opal

OPAL's Playable Almost Indefectibly. OPAL is a rogue-like dungeon crawler.

Log | Files | << Repositories


tree c4bf25763d6dfe587917a166d4898d6fae762c80
parent ca8b98c9493b5998109057204dc25a3f07d2d3db
author esote <esote.net@gmail.com> 1555773991 -0500
committer esote <esote.net@gmail.com> 1555773991 -0500
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQTXAxYDuIzimYoNSPuhTmRAjzzC8gUCXSAqaQAKCRChTmRAjzzC
 8v9GAQC7Uu0qntQHM3utzOWzUNHLnHaScdKaqQmssHu8hbOqBQD+Mrfc/iZ34htY
 MacLYRKy0ijAnGyrNJNl5wtwDAMhDQs=
 =skE5
 -----END PGP SIGNATURE-----

Add example object description file

 README    |   8 +-
 gen.cpp   |  10 +-
 gen.h     |   2 +-
 obj_desc  | 722 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 opal.cpp  |  98 +++------
 parse.cpp |  22 +-
 parse.l   |   8 +-
 7 files changed, 777 insertions(+), 93 deletions(-)

diff --git a/README b/README
index 1bb01ea..c6f1cfe 100644
--- a/README
+++ b/README
@@ -1,10 +1,12 @@
-OPAL's Playable Almost Indefectibly. OPAL is a rogue-like dungeon crawler. Get
-flag info using ./opal.out -h.
+OPAL's Playable Almost Indefectibly. OPAL is a rogue-like dungeon crawler.
+
+OPAL expects NPC and object description files at $HOME/.opal/npc_desc and
+$HOME/.opal/obj_desc respectively. Example files have been provided.
 
 Damage is calculated as the sum of dice rolls for the player's base damage and
 all equipped items.
 
-The monster and object parsing is implemented in Yacc and (F)Lex, so compilation
+The NPC and object parsing is implemented in Yacc and (F)Lex, so compilation
 requires them.
 
 Requires support for C++17 and POSIX threads.
diff --git a/gen.cpp b/gen.cpp
index 291dabe..8536594 100644
--- a/gen.cpp
+++ b/gen.cpp
@@ -35,11 +35,11 @@ static void	init_fresh();
 static void	place_player();
 static int	valid_player(int const, int const);
 
-static char const *const DIRECTORY = "/.rlg327";
+static char const *const DIRECTORY = "/.opal";
 static char const *const FILEPATH = "/dungeon";
 static int constexpr DF_L = 8;
 
-static char const *const MARK = "RLG327-S2019";
+static char const *const MARK = "OPAL-DUNGEON";
 static int constexpr MARK_L = 12;
 
 static int constexpr NEW_ROOM_COUNT = 8;
@@ -57,7 +57,7 @@ static uint16_t stair_dn_count;
 tile tiles[HEIGHT][WIDTH];
 
 std::string
-rlg_path()
+opal_path()
 {
 	char *home;
 #ifdef _GNU_SOURCE
@@ -84,7 +84,7 @@ save_dungeon()
 	FILE *f;
 	bool ret;
 
-	std::string path = rlg_path();
+	std::string path = opal_path();
 
 	if (stat(path.c_str(), &st) == -1) {
 		if (errno == ENOENT) {
@@ -116,7 +116,7 @@ load_dungeon()
 {
 	FILE *f;
 	bool ret;
-	std::string path = rlg_path() + FILEPATH;
+	std::string path = opal_path() + FILEPATH;
 
 	if (!(f = fopen(path.c_str(), "r"))) {
 		err(1, "load fopen");
diff --git a/gen.h b/gen.h
index 1bea4d9..b160d89 100644
--- a/gen.h
+++ b/gen.h
@@ -20,7 +20,7 @@
 
 #include <string>
 
-std::string	rlg_path();
+std::string	opal_path();
 
 /* io */
 bool	save_dungeon();
diff --git a/obj_desc b/obj_desc
new file mode 100644
index 0000000..0238dde
--- /dev/null
+++ b/obj_desc
@@ -0,0 +1,722 @@
+OPAL OBJ DESCRIPTION 1
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 1+0d1
+DEF 4+3d2
+DESC
+Given to Sir Pelleas after assisting an old woman across a river. Enchanted
+so that its wearer would be unfathomably loved.
+
+It's true name has been lost.
+.
+DODGE 91+3d9
+HIT 1+0d1
+NAME Necklace of the Lady of the Lake
+RRTY 95
+SPEED 51+4d7
+TYPE AMULET
+VAL 999+0d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLUE
+DAM 0+0d1
+DEF 1+0d1
+DESC
+An enchanted amulet signifying one of the Nine Devines, Kynareth. Occasionally
+found near shrines. Increases stamina.
+.
+DODGE 10+3d1
+HIT 0+0d1
+NAME Amulet of Kynareth
+RRTY 20
+SPEED 10+3d1
+TYPE AMULET
+VAL 20+2d1
+WEIGHT 1+2d3
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR WHITE
+DAM 2+3d5
+DEF 1+2d1
+DESC
+The tooth is a little dull. Found during Blood Moons.
+.
+DODGE 2+1d2
+HIT 0+0d1
+NAME Shark Tooth Necklace
+RRTY 25
+SPEED 1+2d1
+TYPE AMULET
+VAL 1+3d6
+WEIGHT 1+2d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR WHITE
+DAM 5+1d6
+DEF 93+6d7
+DESC
+Originally belonging to king Evalach, the shield is shimmering white and
+painted by Josephus of Arimethea with a cross of his own blood. The owner is
+granted heavenly protection.
+
+While previously used by Sir Galahad, he must have lost it at the Castle
+Anthrax.
+.
+DODGE 6+1d7
+HIT 0+0d1
+NAME Shield of Evalach
+RRTY 95
+SPEED 1+2d1
+TYPE ARMOR
+VAL 999+0d1
+WEIGHT 1+3d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR WHITE
+DAM 0+0d1
+DEF 16+3d1
+DESC
+Armor consisting of small metal rings linked together to form a mesh. While
+useful at blocking slashing blows from edged weapons, it is less useful
+against spears and pointed weapons.
+.
+DODGE 1+2d6
+HIT 0+0d1
+NAME Chain mail
+RRTY 30
+SPEED 1+1d3
+TYPE ARMOR
+VAL 15+3d1
+WEIGHT 15+3d5
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 0+0d1
+DEF 15+3d5
+DESC
+Forged of the finest gold and enchanted with a weak spell of protection. Lined
+on the inside with shimmering samite.
+.
+DODGE 5+0d1
+HIT 0+0d1
+NAME Golden Armor
+RRTY 45
+SPEED 5+6d2
+TYPE ARMOR
+VAL 99+3d1
+WEIGHT 6+3d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 3+3d2
+DEF 15+0d1
+DESC
+The winged sandals of the messenger god Hermes. Made by the god Hepheastus of
+imperishable gold and make their wearer as swift as a bird. The shoe strap
+was a bit cheap though.
+.
+DODGE 96+3d1
+HIT 0+0d1
+NAME Talaria
+RRTY 95
+SPEED 94+3d7
+TYPE BOOTS
+VAL 999+0d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR MAGENTA
+DAM 0+0d1
+DEF 3+5d2
+DESC
+Shoes designed for sports and now more widely used for everyday wear. The
+rubber is a bit torn, but it gets the job done. If you die the strings loose
+enough, they can also be used as slippers.
+.
+DODGE 6+3d2
+HIT 5+0d1
+NAME Sneakers
+RRTY 20
+SPEED 3+5d2
+TYPE BOOTS
+VAL 15+1d5
+WEIGHT 2+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR RED
+DAM 2+3d1
+DEF 5+5d2
+DESC
+Very useful when hiking or climbing mountains, yet you always seem to trip
+over yourself when wearing them to the mall.
+.
+DODGE 2+0d1
+HIT 0+0d1
+NAME Mountain Boots
+RRTY 30
+SPEED 0+3d5
+TYPE BOOTS
+VAL 30+3d3
+WEIGHT 15+1d6
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR BLACK
+DAM 2+0d1
+DEF 0+0d1
+DESC
+Robe made from the feathers of a swan maiden. Our scientists haven't figured
+out what it does.
+.
+DODGE 999+0d1
+HIT 0+0d1
+NAME Swan Cloak
+RRTY 90
+SPEED 3+0d1
+TYPE CLOAK
+VAL 999+0d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLUE
+DAM 0+0d1
+DEF 5+0d3
+DESC
+A cloak that's useful on cold or windy occasions. Some say it's fashionable,
+but only when you use it to bow or hold it up a little bit and gallivant
+around.
+.
+DODGE 6+0d1
+HIT 0+0d1
+NAME Evening Cloak
+RRTY 30
+SPEED 1+0d1
+TYPE CLOAK
+VAL 15+3d5
+WEIGHT 0+5d6
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR GREEN
+DAM 0+0d1
+DEF 5+3d1
+DESC
+Woven by Galadriel and her maidens. Each cloak fits to its wearer and is
+fashioned with a green broach. The color shifts to match natural surroundings.
+.
+DODGE 35+3d1
+HIT 0+0d1
+NAME Elven Cloak
+RRTY 45
+SPEED 6+3d5
+TYPE CLOAK
+VAL 51+3d5
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR CYAN
+DAM 1+3d1
+DEF 3+3d5
+DESC
+Iron gloves of the god Thor. Used to wield the hammer Mjolnir.
+.
+DODGE 3+0d1
+HIT 0+0d1
+NAME Jarngreipr
+RRTY 95
+SPEED 0+0d1
+TYPE GLOVES
+VAL 999+0d1
+WEIGHT 41+3d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLUE
+DAM 0+0d1
+DEF 5+3d1
+DESC
+An early virtual reality glove. While imprecise, it sure does look cool.
+.
+DODGE 5+6d1
+HIT 0+0d1
+NAME Power Glove
+RRTY 45
+SPEED 0+0d1
+TYPE GLOVES
+VAL 94+3d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLACK
+DAM 6+10d5
+DEF 2+0d1
+DESC
+Gloves with razors build into them. Makes it really hard to prepare your
+cereal in the morning.
+.
+DODGE 0+0d1
+HIT 15+6d4
+NAME Freddy Krueger Gloves
+RRTY 50
+SPEED 3+0d1
+TYPE GLOVES
+VAL 94+1d3
+WEIGHT 5+0d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR CYAN
+DAM 3+5d5
+DEF 60+7d8
+DESC
+The helmet of King Arthur, passed down to him from Uther Pendragon.
+.
+DODGE 6+3d1
+HIT 0+0d1
+NAME Goswhit
+RRTY 95
+SPEED 6+3d1
+TYPE HELMET
+VAL 999+0d1
+WEIGHT 9+3d4
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR GREEN
+DAM 0+0d1
+DEF 6+7d1
+DESC
+The helmet worn by Master Chief. Contains various heads-up display
+information, such as health and the count of sticky grenades.
+.
+DODGE 6+0d1
+HIT 0+0d1
+NAME Master Chief Helmet
+RRTY 56
+SPEED 9+3d1
+TYPE HELMET
+VAL 56+3d7
+WEIGHT 6+7d3
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR RED
+DAM 0+0d1
+DEF 6+0d1
+DESC
+A clump of soil with a plant growing from it. The plant is useful for cooking
+and making Dwarven wine.
+.
+DODGE 6+0d1
+HIT 0+0d1
+NAME Plump Helmet
+RRTY 31
+SPEED 0+0d1
+TYPE HELMET
+VAL 0+0d1
+WEIGHT 6+1d3
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR GREEN
+DAM 96+0d1
+DEF 3+0d1
+DESC
+A trident-shaped staff which emits a bright light in the darkness, and grants
+wisdom and insight. Used by the Japanese mountain god Koya-no-Myojin.
+.
+DODGE 0+0d1
+HIT 0+0d1
+NAME Kongo
+RRTY 95
+SPEED 6+7d8
+TYPE LIGHT
+VAL 999+0d1
+WEIGHT 5+1d5
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 0+0d1
+DEF 5+0d1
+DESC
+A portable light that burns using a wick in oil. Make sure to always hold it
+upright, otherwise you'll spill hot oil on yourself.
+.
+DODGE 5+0d1
+HIT 0+0d1
+NAME Lantern
+RRTY 20
+SPEED 4+0d1
+TYPE LIGHT
+VAL 5+5d2
+WEIGHT 6+2d3
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 10+3d2
+DEF 0+0d1
+DESC
+Large wooden stick with very flammable cloth at one end.
+.
+DODGE 1+0d3
+HIT 0+0d1
+NAME Torch
+RRTY 25
+SPEED 0+0d1
+TYPE LIGHT
+VAL 1+0d1
+WEIGHT 2+0d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR CYAN
+DAM 35+3d2
+DEF 0+0d1
+DESC
+The spear used by St. George to kill a dragon in Beirut, saving a princess.
+.
+DODGE 0+0d1
+HIT 5+31d3
+NAME Ascalon
+RRTY 95
+SPEED 5+6d1
+TYPE OFFHAND
+VAL 999+0d1
+WEIGHT 5+3d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR WHITE
+DAM 5+6d2
+DEF 0+0d1
+DESC
+Small, sharp knife. Can be easily concealed.
+.
+DODGE 5+6d1
+HIT 0+0d1
+NAME Dagger
+RRTY 30
+SPEED 5+0d1
+TYPE OFFHAND
+VAL 5+1d6
+WEIGHT 3+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR RED
+DAM 5+6d1
+DEF 0+0d1
+DESC
+Long wooden pole with a sharp iron spike fastened to the end of the shaft.
+.
+DODGE 0+0d1
+HIT 5+6d7
+NAME Spear
+RRTY 35
+SPEED 4+0d1
+TYPE OFFHAND
+VAL 4+5d3
+WEIGHT 0+3d4
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR RED
+DAM 35+6d7
+DEF 4+6d1
+DESC
+Apollo's bow was crafted from sun rays.
+.
+DODGE 0+3d6
+HIT 35+60d7
+NAME Apollo's Bow
+RRTY 90
+SPEED 0+0d1
+TYPE RANGED
+VAL 999+0d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR MAGENTA
+DAM 3+5d3
+DEF 0+0d1
+DESC
+As tall as the height of the player, this bow allows you to draw back an arrow
+far enough to reach an innocent bystander.
+.
+DODGE 3+0d1
+HIT 5+6d3
+NAME Longbow
+RRTY 41
+SPEED 0+0d1
+TYPE RANGED
+VAL 6+3d1
+WEIGHT 4+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLUE
+DAM 7+8d2
+DEF 3+0d1
+DESC
+An elastic ranged weapon allowing you to shoot arrows at a significant rate.
+.
+DODGE 6+3d1
+HIT 6+6d1
+NAME Crossbow
+RRTY 50
+SPEED 5+6d3
+TYPE RANGED
+VAL 9+6d7
+WEIGHT 5+3d2
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 0+0d1
+DEF 99+99d99
+DESC
+Forged by the Dark Lord Sauron in the Second Age. Warps and corrupts the mind
+of its wearer towards the goal of returning to the hand of Sauron.
+
+Causes the wearer to become invisible, except to the eyes of the Witch-king
+and his kind.
+.
+DODGE 15+6d7
+HIT 0+0d1
+NAME One Ring
+RRTY 99
+SPEED 15+3d5
+TYPE RING
+VAL 2999+0d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR BLACK
+DAM 0+0d1
+DEF 56+6d3
+DESC
+Adil's prized ring.
+.
+DODGE 5+6d7
+HIT 67+8d9
+NAME Sviagris
+RRTY 95
+SPEED 5+6d7
+TYPE RING
+VAL 999+0d1
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLACK
+DAM 5+3d1
+DEF 0+0d1
+DESC
+While it looks authentic, we things it's just made of old chocolate.
+.
+DODGE 0+0d1
+HIT 5+6d2
+NAME Black Diamond Ring
+RRTY 50
+SPEED 9+0d1
+TYPE RING
+VAL 0+1d99
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR RED
+DAM 9+9d9
+DEF 0+0d1
+DESC
+Ring which increases the damage dealt by its wearer.
+.
+DODGE 0+0d1
+HIT 3+5d6
+NAME Ring of Damage
+RRTY 35
+SPEED 0+0d1
+TYPE RING
+VAL 15+3d5
+WEIGHT 3+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLUE
+DAM 0+0d1
+DEF 15+3d4
+DESC
+Ring which increases the defense of its wearer.
+.
+DODGE 3+9d2
+HIT 0+0d1
+NAME Ring of Defense
+RRTY 40
+SPEED 0+3d1
+TYPE RING
+VAL 15+3d5
+WEIGHT 3+0d1
+END
+
+BEGIN OBJ
+ART TRUE
+ATTR 0+0d1
+COLOR YELLOW
+DAM 67+3d90
+DEF 5+6d7
+DESC
+Originally given to Sigmund during the wedding feast of his sister. Enchanted
+by Odin as a gift to anyone who could pull it from the Barnstokkr tree.
+Broken during a battle with Odin and reforged by Sigmund's son Sigurd.
+
+Used to slay the dragon Fafnir.
+.
+DODGE 3+5d6
+HIT 5+0d1
+NAME Gram
+RRTY 99
+SPEED 5+6d9
+TYPE WEAPON
+VAL 2999+0d1
+WEIGHT 0+1d99
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR RED
+DAM 3+5d1
+DEF 0+1d3
+DESC
+A solid wooden club with three nails poking out of it. The nails are for
+decoration, it's the splinters that get you.
+.
+DODGE 3+5d1
+HIT 5+3d1
+NAME Club
+RRTY 20
+SPEED 2+1d3
+TYPE WEAPON
+VAL 0+0d1
+WEIGHT 5+3d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR BLACK
+DAM 6+3d5
+DEF 2+3d1
+DESC
+The sword wielded by the samurai. Distinguished by its curved, single-edge
+blade.
+.
+DODGE 0+0d1
+HIT 0+0d1
+NAME Katana
+RRTY 50
+SPEED 5+6d7
+TYPE WEAPON
+VAL 99+3d5
+WEIGHT 0+0d1
+END
+
+BEGIN OBJ
+ART FALSE
+ATTR 0+0d1
+COLOR GREEN
+DAM 3+4d5
+DEF 3+0d1
+DESC
+The trustworthy battle axe. Always gets the job done, if just a little slower.
+.
+DODGE 0+0d1
+HIT 5+6d7
+NAME Battle axe
+RRTY 40
+SPEED 0+0d1
+TYPE WEAPON
+VAL 9+7d8
+WEIGHT 50+3d1
+END
diff --git a/opal.cpp b/opal.cpp
index 5f21216..7418239 100644
--- a/opal.cpp
+++ b/opal.cpp
@@ -29,8 +29,6 @@
 #include "parse.h"
 #include "turn.h"
 
-static void	usage(int const, std::string const &);
-
 static bool	colors();
 
 static void	print_deathscreen(WINDOW *const);
@@ -39,19 +37,6 @@ static void	print_winscreen2(WINDOW *const);
 
 static bool	is_number(std::string const &);
 
-static char const *const PROGRAM_NAME = "opal";
-
-static struct option const long_opts[] = {
-	{"nodescs", no_argument, NULL, 'd'},
-	{"help", no_argument, NULL, 'h'},
-	{"load", no_argument, NULL, 'l'},
-	{"numnpcs", required_argument, NULL, 'n'},
-	{"numobjs", required_argument, NULL, 'o'},
-	{"save", no_argument, NULL, 's'},
-	{"seed", required_argument, NULL, 'z'},
-	{NULL, 0, NULL, 0}
-};
-
 npc player;
 
 int
@@ -59,38 +44,42 @@ main(int const argc, char *const argv[])
 {
 	WINDOW *win;
 	char *end;
-	int ch;
-	bool load = false;
-	bool save = false;
-	bool no_descs = false;
-	unsigned int numnpcs = std::numeric_limits<unsigned int>::max();
-	unsigned int numobjs = std::numeric_limits<unsigned int>::max();
-	std::string const name = (argc == 0) ? PROGRAM_NAME : argv[0];
-
-	while ((ch = getopt_long(argc, argv, "dhln:o:sz:", long_opts, NULL)) != -1) {
-		switch(ch) {
-		case 'd':
-			no_descs = true;
-			break;
-		case 'h':
-			usage(EXIT_SUCCESS, name);
-			break;
+	char const *const usage = "usage: opal [-ls] [-n count] [-o count] [-z seed]";
+	int opt;
+	unsigned int numnpcs;
+	unsigned int numobjs;
+	bool load;
+	bool save;
+
+	numnpcs = std::numeric_limits<unsigned int>::max();
+	numobjs = std::numeric_limits<unsigned int>::max();
+	load = false;
+	save = false;
+
+	while ((opt = getopt(argc, argv, "ln:o:sz:")) != -1) {
+		switch(opt) {
 		case 'l':
 			load = true;
 			break;
 		case 'n':
 			numnpcs = (unsigned int)strtoul(optarg, &end, 10);
 
-			if (optarg == end || errno == EINVAL || errno == ERANGE) {
+			if (errno == EINVAL || errno == ERANGE) {
 				err(1, "numnpcs invalid");
+			} else if (optarg == end) {
+				errx(1, "numnpcs invalid");
 			}
+
 			break;
 		case 'o':
 			numobjs = (unsigned int)strtoul(optarg, &end, 10);
 
-			if (optarg == end || errno == EINVAL || errno == ERANGE) {
+			if (errno == EINVAL || errno == ERANGE) {
 				err(1, "numobjs invalid");
+			} else if (optarg == end) {
+				errx(1, "numobjs invalid");
 			}
+
 			break;
 		case 's':
 			save = true;
@@ -99,19 +88,26 @@ main(int const argc, char *const argv[])
 			if (is_number(optarg)) {
 				rr = ranged_random(strtoul(optarg, &end, 10));
 
-				if (optarg == end || errno == EINVAL
-					|| errno == ERANGE) {
+				if (errno == EINVAL || errno == ERANGE) {
 					err(1, "seed %s invalid", optarg);
+				} else if (optarg == end) {
+					errx(1, "seed %s invalid", optarg);
 				}
 			} else {
 				rr = ranged_random(optarg);
 			}
 			break;
 		default:
-			usage(EXIT_FAILURE, name);
+			errx(1, usage);
 		}
 	}
 
+	if (optind < argc) {
+		fprintf(stderr, "%s: invalid option -- '%s'\n", argv[0],
+			argv[optind]);
+		errx(1, usage);
+	}
+
 	if (numnpcs == std::numeric_limits<unsigned int>::max()) {
 		numnpcs = rr.rrand<unsigned int>(3, 5);
 	}
@@ -127,10 +123,8 @@ main(int const argc, char *const argv[])
 	}
 
 	/* requires colors initialized */
-	if (!no_descs) {
-		parse_npc_file();
-		parse_obj_file();
-	}
+	parse_npc_file();
+	parse_obj_file();
 
 	if (refresh() == ERR) {
 		errx(1, "refresh from initscr");
@@ -234,30 +228,6 @@ main(int const argc, char *const argv[])
 	return EXIT_SUCCESS;
 }
 
-static void
-usage(int const status, std::string const &name)
-{
-	std::cout << "Usage: " << name << " [OPTION]... \n\n";
-
-	if (status != EXIT_SUCCESS) {
-		std::cerr << "Try '" << name <<
-			" --help' for more information.\n";
-	} else {
-		std::cout << "OPAL's Playable Almost Indefectibly.\n\n"
-			<< "Traverse a generated dungeon.\n\n"
-			<< "Options:\n\
-  -d, --nodescs         don't parse description files\n\
-  -h, --help            display this help text and exit\n\
-  -l, --load            load dungeon file\n\
-  -n, --numnpcs=[NUM]   number of npcs per floor\n\
-  -o, --numobjs=[NUM]   number of objs per floor\n\
-  -s, --save            save dungeon file\n\
-  -z, --seed=[SEED]     set rand seed, takes integer or string\n";
-	}
-
-	exit(status);
-}
-
 static bool
 colors()
 {
diff --git a/parse.cpp b/parse.cpp
index 95e17fd..cf887e9 100644
--- a/parse.cpp
+++ b/parse.cpp
@@ -24,8 +24,8 @@
 #include "globs.h"
 #include "y.tab.h"
 
-static char const *const NPC_FILE = "/monster_desc.txt";
-static char const *const OBJ_FILE = "/object_desc.txt";
+static char const *const NPC_FILE = "/npc_desc";
+static char const *const OBJ_FILE = "/obj_desc";
 
 static char const *const color_map_r[] = {
 	"BLACK",
@@ -72,15 +72,10 @@ void
 parse_npc_file()
 {
 	struct stat st;
-	std::string const path = rlg_path() + NPC_FILE;
+	std::string const path = opal_path() + NPC_FILE;
 
 	if (stat(path.c_str(), &st) == -1) {
-		if (errno == ENOENT) {
-			err(1, "no npc file, run with '-m' to skip "
-				"npc file parsing");
-		} else {
-			err(1, "stat npc file");
-		}
+		err(1, "npc description file");
 	}
 
 	yyin = fopen(path.c_str(), "r");
@@ -102,15 +97,10 @@ void
 parse_obj_file()
 {
 	struct stat st;
-	std::string const path = rlg_path() + OBJ_FILE;
+	std::string const path = opal_path() + OBJ_FILE;
 
 	if (stat(path.c_str(), &st) == -1) {
-		if (errno == ENOENT) {
-			err(1, "no object file, run with '-o' to skip object "
-				"parsing");
-		} else {
-			err(1, "stat object file");
-		}
+		err(1, "object description file");
 	}
 
 	yyin = fopen(path.c_str(), "r");
diff --git a/parse.l b/parse.l
index 2956b23..1b7bce0 100644
--- a/parse.l
+++ b/parse.l
@@ -42,19 +42,19 @@ BOOLEAN	TRUE|FALSE
 %Start desc
 %%
 
-^"RLG327 MONSTER DESCRIPTION 1"$	{
+^"OPAL NPC DESCRIPTION 1"$	{
 						in_n = true;
 						in_o = false;
 						return BEGIN_NPC_FILE;
 					}
-^"RLG327 OBJECT DESCRIPTION 1"$		{
+^"OPAL OBJ DESCRIPTION 1"$		{
 						in_o = true;
 						in_n = false;
 						return BEGIN_OBJ_FILE;
 					}
 
-^"BEGIN MONSTER"$	{ c_npc = {}; return BEGIN_NPC; }
-^"BEGIN OBJECT"$	{ c_obj = {}; return BEGIN_OBJ; }
+^"BEGIN NPC"$	{ c_npc = {}; return BEGIN_NPC; }
+^"BEGIN OBJ"$	{ c_obj = {}; return BEGIN_OBJ; }
 
 ^"END"$	{
 		if (in_n) npcs_parsed.push_back(c_npc);