/*-----------------------------------------------------------------------------
 * PROJECT:       Adventure Castle
 * --------
 *
 * MODULE:        adventure_castle.c
 * -------
 *
 * AUTHOR:        Dave Dunfield
 * -------
 * 
 * This is a text based adventure game which I wrote a number of
 * years ago (on a unix system), and have since ported to the PC
 * using MICRO-C.
 *
 * With thanks to Crowther and Woods who wrote the original FORTRAN
 * adventure game from which this game borrows several ideas.
 *
 * LICENSE:
 * --------
 * Copyright 1983-2001 Dave Dunfield
 * All rights reserved.
 *
 * Permission granted for personal (non-commercial) use only.
 *
 * BUILD:
 * ------
 * make castle
 *
 * LOGS:
 * -----
 * 1983         Dave Dunfield         Release for DOS 2.0 Micro-C compiler
 *
 * 09/07/2026   Dominique Rioual      Port to modern C compiler (gcc) :
 *              
 *              - Fixed usage of fopen, ftell, fseek
 *              - Modification of function declaration (modern style)
 *              - Addition of types t_bool, t_usi and t_si
 * 				- Improvement of code presentation
 *              - Fixed parse() function
 *              - Fixed score calculation in end_game()
 *              - Fixed usage of rand(), seed initialisation with time
 *              - Fixed all warnings (-Wall option for gcc)
 *
 ----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define OBJECTS			41		/* number of objects */
#define LOCS			181		/* number of locations */
#define DARK			16		/* first location where its dark */
#define DOORS			14		/* number of doors */
#define MONSTERS		12		/* number of monsters */
#define CARRY			5		/* carry limit */
#define STARTING_HITS	50		/* starting number of hit points */
#define LAMP_DIM_1		300		/* first warning */
#define LAMP_DIM_2		350		/* second warning */
#define LAMP_DIE		400		/* lamp dies */
#define EXTRA_POINTS	32		/* extra points for not breaking vase */

/* type definition */
typedef enum { FALSE, TRUE } t_bool;
typedef short t_si;
typedef unsigned short t_usi;

/* game status data, all data from 'prop' to start of 'inbuf' (exclusive) */
/* is saved in the file 'castle.sav' when game is suspended */
char prop[OBJECTS] = { 0 };
char door_stat[DOORS]= { 0 };
t_usi location[OBJECTS] = {
	6, 4, 39, 8, 77, 33, 18, -1, 21, 24, 29,
	92, 64, 108, 10, 144, 146, -1, -1, -1,
	31, 67, 115, 65, 105, 79, 141, 41, 42, 117, -1,
	-1, 96, 12, 90, 163, 128, 124, 11, 135, 164 };
t_si lstate = 1;
t_si died = 0, found_chest = 0;
t_usi carry = 0, turns = 0, lturns = 0, current_loc = 1,
	old_loc = 1, deep = 0;
t_si hits = STARTING_HITS;
t_si monster_hits[] = {			/* monsters current hit points */
	10, 20, 25, 30, 40, 55, 60, 60, 75, 50, 100, 100 };
t_usi monster_loc[] = {		/* monsters current location */
	15, 25, 41, 55, 69, 83, 97, 111, 125, 139, 153, 167 };
t_usi date[2] = { 0 };

unsigned char inbuf[80], *inptr;		/* input buffer and pointer */
t_usi places[LOCS];						/* stores pointers to descriptions */
unsigned char vocabulary[] = {			/* known words */
/* verbs */
	'g','o'+128,				1,
	'r','u','n'+128,			1,
	'w','a','l','k'+128,		1,
	't','a','k','e'+128,		2,
	'g','r','a','b'+128,		2,
	'g','e','t'+128,			2,
	'd','r','o','p'+128,		3,
	'd','i','g'+128,			4,
	'l','i','g','h','t'+128,	5,
	'o','n'+128,				5,
	'o','f','f'+128,			6,
	'o','p','e','n'+128,		7,
	'u','n','l','o','c','k'+128,7,
	'm','o','v','e'+128,		7,
	'p','u','s','h'+128,		7,
	'r','e','m','o','v','e'+128,7,
	'c','l','o','s','e'+128,	8,
	'l','o','c','k'+128,		8,
	'l','o','o','k'+128,		9,
	'v','i','e','w'+128,		9,
	'i','n','v','e','n','t','o','r','y'+128,	10,
	'c','a','r','r','y'+128,	10,
	'q','u','i','t'+128,		11,
	's','t','o','p'+128,		11,
	'r','e','a','d'+128,		12,
	'w','a','v','e'+128,		13,
	'r','u','b'+128,			13,
	'o','p','e','r','a','t','e'+128, 13,
	'u','s','e'+128,			13,
	'a','b','r','a','c','a','d','a','b','r','a'+128, 14,
	'h','o','k','u','s'+128,14,
	'p','o','k','u','s'+128,14,
	's','e','s','a','m','e'+128,14,
	's','h','a','z','a','m'+128,14,
	'p','l','u','g','h'+128,	14,
	'x','y','z','z','y'+128,	14,
	'f','a','r','l','e','y'+128,15,
	's','u','s','p','e','n','d'+128,16,
	'r','e','s','u','m','e'+128,17,
	'a','t','t','a','c','k'+128,18,
	's','w','i','n','g'+128,	18,
	's','t','a','b'+128,		18,
	't','h','r','u','s','t'+128,18,
	'k','i','l','l'+128,		18,
	'h','i','t'+128,			18,
	'd','r','i','n','k'+128,	19,
	'e','m','p','t','y'+128,	20,
	'd','u','m','p'+128,		20,
	's','p','i','l','l'+128,	20,
	'r','e','t','r','e','a','t'+128,21,
	'b','a','c','k','u','p'+128,21,
	'l','a','s','t'+128,		21,
	'b','a','c','k'+128,		21,
	'o','u','t'+128,			21,
	'o','i','l'+128,			22,
	'l','u','b','r','i','c','a','t','e'+128,22,
	's','c','o','r','e'+128,	23,
	'p','o','i','n','t','s'+128,23,
	's','h','i','t'+128,		24,
	'f','u','c','k'+128,		24,
	'd','a','m','n'+128,		24,
	'p','i','s','s'+128,		24,
/* nouns */
	'g','a','t','e'+128,		50,
	'g','r','a','t','e'+128,	50,
	'f','e','n','c','e'+128,	50,
	'd','o','o','r'+128,		50,
	's','l','a','b'+128,		50,
	's','i','g','n'+128,		50,
	'b','o','o','k','c','a','s','e'+128,	50,
	's','h','e','l','v','e','s'+128, 50,
	's','h','e','l','f'+128,	50,
	'b','a','r','s'+128,		50,
	'c','e','l','l'+128,		50,
	'p','i','t'+128,			51,
	'h','o','l','e'+128,		51,
	's','t','a','i','r','s'+128,52,
	'l','a','d','d','e','r'+128,52,
	'w','a','l','l'+128,		53,
	'c','a','v','e'+128,		53,
	'c','a','v','e','r','n'+128,53,
	'g','r','o','u','n','d'+128,53,
	'f','l','o','o','r'+128,	53,
/* objects */
	'l','a','m','p'+128,		100,
	'l','a','n','t','e','r','n'+128,100,
	'k','e','y'+128,			101,
	'r','o','p','e'+128,		102,
	's','t','r','i','n','g'+128,102,
	'c','o','r','d'+128,		102,
	'k','n','i','f','e'+128,	103,
	'd','a','g','g','e','r'+128,103,
	'a','x','e'+128,			104,
	'h','a','t','c','h','e','t'+128,104,
	's','w','o','r','d'+128,	105,
	'h','i','l','t'+128,		105,
	's','a','c','k'+128,		106,
	'b','a','g'+128,			106,
	'b','a','t','t','e','r','i','e','s'+128,107,
	'b','a','t','t','e','r','y'+128,107,
	'b','a','r'+128,			108,
	'c','r','o','w','b','a','r'+128,108,
	'r','o','d'+128,			109,
	'w','a','n','d'+128,		109,
	's','c','r','o','l','l'+128,110,
	'p','a','p','e','r'+128,	110,
	'r','o','l','l'+128,		110,
	'p','o','t','i','o','n'+128,111,
	'b','o','t','t','l','e'+128,111,
	'f','l','a','s','k'+128,	112,
	'l','i','q','u','i','d'+128,112,
	'v','e','l','v','e','t'+128,113,
	'p','i','l','l','o','w'+128,113,
	'r','o','c','k'+128,		114,
	's','t','o','n','e'+128,	114,
	'p','u','r','p','l','e'+128,114,
	'c','a','n','n','o','n'+128,115,
	'b','a','l','l'+128,		115,
	'm','e','t','a','l'+128,	116,
	'd','e','t','e','c','t','o','r'+128,116,
/* treasure objects */
	'c','o','i','n','s'+128,	120,
	'm','o','n','e','y'+128,	120,
	'r','i','n','g'+128,		121,
	'b','a','n','d'+128,		121,
	's','t','a','t','u','e'+128,122,
	'c','r','y','s','t','a','l'+128,122,
	'd','r','a','g','o','n'+128,122,
	'i','v','o','r','y'+128,	123,
	'a','m','u','l','e','t'+128,123,
	'e','b','o','n','y'+128,	124,
	'g','l','o','b','e'+128,	124,
	'c','r','o','w','n'+128,	125,
	'd','i','a','m','o','n','d'+128,126,
	'g','o','l','d'+128,		127,
	'n','u','g','g','e','t'+128,127,
	'n','u','g','g','e','t','s'+128,127,
	'p','r','e','c','i','o','u','s'+128,128,
	'j','e','w','e','l','s'+128,128,
	'j','e','w','e','l','r','y'+128,128,
	'p','l','a','t','i','n','u','m'+128,129,
	'p','y','r','a','m','i','d'+128,129,
	'c','h','e','s','t'+128,	130,
	't','r','e','a','s','u','r','e'+128,130,
	'e','m','e','r','a','l','d'+128,131,
	'e','a','r','r','i','n','g'+128,131,
	'r','a','r','e'+128,		132,
	's','p','i','c','e'+128,	132,
	's','p','i','c','e','s'+128,132,
	'p','e','r','s','i','a','n'+128,133,
	'r','u','g'+128,			133,
	'r','u','b','y'+128,		134,
	's','t','a','f','f'+128,	134,
	'm','i','n','g'+128,		135,
	'v','a','s','e'+128,		135,
	's','i','l','v','e','r'+128,136,
	'p','e','n','d','a','n','t'+128,136,
	'p','a','i','n','t','i','n','g'+128,137,
	'a','r','t'+128,			137,
	'b','r','o','n','z','e'+128,138,
	'c','a','n','d','l','e'+128,138,
	'c','a','n','d','l','e','s','t','i','c','k'+128,138,
	's','i','l','k'+128,		139,
	'j','a','c','k','e','t'+128,139,
	's','i','l','k','e','n'+128,139,
	'c','o','a','t'+128,		139,
	'b','o','o','k'+128,		140,
	'b','i','b','l','e'+128,	140,
	'l','e','a','t','h','e','r'+128, 140,
/* directions */
	'n','o','r','t','h'+128,	240,
	'n'+128,					240,
	'e','a','s','t'+128,		241,
	'e'+128,					241,
	's','o','u','t','h'+128,	242,
	's'+128,					242,
	'w','e','s','t'+128,		243,
	'w'+128,					243,
	'u','p'+128,				244,
	'u'+128,					244,
	'd','o','w','n'+128,		245,
	'd'+128,					245,
/* filler words, not actually looked at, but no message given is used */
	's','a','y'+128,			0,
	't','h','e'+128,			0,
	'a'+128,					0,
	'w','i','t','h'+128,		0,
	't','u','r','n'+128,		0,
	'a','t'+128,				0,
	0 };

/* door descriptions: "closed desc|open desc" */
char *doors[] = {
	"The grate is locked|The grate is open",
	"A large bookcase fills the east wall|A large bookcase is standing beside a small exit in the east wall",
	"The door is locked shut|The door is open",
	"A massive stone slab leans against the west wall|A massive stone slab stands beside a passage leading west",
	"The fissure is too wide to cross|A solid stone bridge crosses the fissure",
	"The trapdoor is locked|The trapdoor is open",
	"The sign is firmly fastened to the wall|The sign stands beside a small passage",
	"The door is locked|The door is open",
	"The hinges on the door are rusted solid|The door is open",
	"The door is tightly locked|The door is standing open",
	"The north wall is filled with sturdy shelves|A small passage exits from the north wall, beside a pile of rubble",
	"The bars are locked shut|The bars are open",
	"The bars are locked shut|The bars are open",
	"The bars are locked shut|The bars are open"
	};

/* table of objects required to open the doors */
char door_keys[DOORS] = {
	1, 8, 1, 8, 0, 1, 8, 1, 0, 1, 8, 1, 1, 1 };

/* table of damage done by weapons */
char damage[3] = {
	10, 18, 34 };

/* long object descriptions, Multiple descriptions (Seperated by '|') are */
/* for different prop values of the object (Starting with prop value=0).  */
/* Last desc is given if the prop value is higher than # of descrptions.  */
char *l_descriptions[] = {
	"An unlit brass lantern is here|A brightly glowing brass lantern is here|A dimly glowing brass lantern is here|A very dim brass lantern is here",
	"There is a small key on the ground here",
	"There is a long length of rope here",
	"A small sharp knife is on the ground nearby",
	"A little axe is lying on the ground here",
	"The hilt of a shining sword protrudes from the stone|There is a long shiny sword here",
	"A large brown sack is lying in a heap on the floor",
	"There are fresh batteries here|Some old worn-out batteries are discarded nearby",
	"A long crowbar is lying on the ground here",
	"There is a long black rod with a rusty star on its end here",
	"A tightly rolled paper scroll is here|A blank sheet of paper is here",
	"There is a small bottle here, containing a golden potion|A small empty bottle is discarded nearby",
	"There is a small flask full of a dark liquid here|A small empty flask is discarded nearby",
	"A soft velvet pillow is resting on the floor here",
	"A funny looking purple stone is lying on the ground here",
	"There is a large black cannon ball here",
	"A battered metal detector is lying on the ground here",
/* objects 17 through 19 reserved for future non-valuable items */
	"",
	"",
	"",
/* objects 20 and above represent treasure items. */
	"There is a pile of coins on the ground here",
	"A small golden ring is lying on the ground here",
	"There is a small crystal statue of a dragon here",
	"A small ivory amulet is lying to one side",
	"A small globe of ebony is resting on the floor here",
	"There is a many jeweled crown here",
	"A large sparkling diamond is here",
	"There are nuggets of gold here",
	"There is precious jewelry here",
	"There is a platinum pyramid here, eight inches to a side",
	"The thiefs treasure chest is here",
	"A large emerald earring is lying on the ground here",
	"There are rare spices here",
	"A valuable persion rug is spread out on the floor",
	"A ruby studded staff is lying on the ground here",
	"A precious ming vase is resting delicately here|The floor is littered with worthless shards of pottery",
	"There is a large silver pendant lying here",
	"A beautiful painting is lying against the wall",
	"There is a beautiful bronze candlestick here",
	"There is a lovely silken jacket here",
	"A rare leather bound bible is lying on the ground here"
	} ;

/* short object descriptions, format same as above */
char *s_descriptions[] = {
	"Brass lantern|Brightly shining brass lantern|Dim brass lantern|Very dim brass lantern",
	"Small key",
	"Coil of rope",
	"Small sharp knife",
	"Little axe",
	"Long shiny sword",
	"Large sack",
	"Fresh batteries|Worn-out batteries",
	"Long crowbar",
	"Long black rod",
	"Paper scroll|Blank paper scroll",
	"Bottle containing golden potion|Empty bottle",
	"Flask containing dark liquid|Empty flask",
	"Velvet pillow",
	"Purple stone",
	"Cannon ball",
	"Metal detector",
/* objects 17 through 19 reserved for future non-valuable items */
	"",
	"",
	"",
/* valuable "treasure" objects */
	"Many coins",
	"Small golden ring",
	"Crystal statue",
	"Ivory amulet",
	"Ebony globe",
	"Jeweled crown",
	"Sparkling diamond",
	"Gold nuggets",
	"Precious jewelry",
	"Platinum pyramid",
	"Treasure chest",
	"Emerald earring",
	"Rare spices",
	"Persion rug",
	"Rubby studded staff",
	"Ming vase|Broken vase",
	"Silver pendant",
	"Beautiful painting",
	"Bronze candlestick",
	"Silken jacket",
	"Rare bible"
	} ;

/* Travel table, each entry corresponds to a location in the game,	*/
/* and has six words, each representing a direction (North, East,	*/
/* South, West, Up, Down). Bits 0-7 of each word represents a new	*/
/* location that can be reached by that direction. Bits 8-12 give	*/
/* a number 'N' which is used in conditional movement calculations	*/
/* Bits 13-15 give the type of conditional movement as follows:		*/
/* 0 - Unconditional, character moves directly to new location.		*/
/* 1 - Door 'N' must be open, otherwise describe the door.			*/
/* 2 - He must be carrying object 'N'.								*/
/* 3 - He must not be carrying object 'N'							*/
/* 4 - Object 'N' must have prop value = 0.							*/
/* 5 - Object 'N' must have prop value = 1.							*/
/* 6 - Special condition 'N', (See "special_condition")				*/
/* 7 - Special action 'N' occurs when passed (See "special_action") */
t_usi travel[LOCS][6] = {		/* N, E, S, W, U, D */
	{ 0, 0, 0, 0, 0, 0 },
	{ 5, 0, 2, 6, 0, 0 },
	{ 1, 2, 2, 3, 0, 0 },
	{ 0, 2, 0, 0, 0, 4 },
	{ 0, 0, 0, 0, 3, 0 },
	{ 7, 8, 1, 6, 0, 0 },			/* 5 */
	{ 7, 5, 1, 7, 0, 0 },
	{ 7, 7, 5, 7, 0, 0 },
	{ 7, 7, 9, 5, 0, 0 },
	{ 8, 7, 7, 7, 0, 0x200f },
	{ 105, 0, 0, 0xc068, 0, 0 },	/* 10 */
	{ 0, 0, 126, 0, 0, 0 },
	{ 164, 0, 163, 165, 0, 0 },
	{ 0, 0, 0, 0, 0, 0 },
	{ 0, 0, 0, 0, 0, 0 },
	{ 26, 16, 17, 25, 0x2009, 0 },	/* 15 */
	{ 0, 0x211e, 0, 15, 0, 0 },
	{ 15, 19, 0, 18, 0, 0 },
	{ 0, 17, 0, 0, 0, 0 },
	{ 0, 0, 20, 17, 0, 0 },
	{ 19, 0, 21, 22, 0, 0 },		/* 20 */
	{ 20, 0, 0, 0, 0, 0 },
	{ 0, 20, 0, 23, 0, 0 },
	{ 24, 22, 0, 0, 0, 0 },
	{ 25, 0, 23, 0, 0, 0 },
	{ 31, 15, 24, 0, 0, 0 },		/* 25 */
	{ 27, 0, 15, 0, 0, 0 },
	{ 0x221c, 0, 26, 0, 0, 0 },
	{ 0, 29, 0x221b, 0, 0, 0 },
	{ 0, 0, 0, 28, 0, 0 },
	{ 0, 0, 0, 16, 0, 32 },			/* 30 */
	{ 0, 0, 25, 0, 0, 0 },
	{ 33, 0, 0, 0, 30, 0 },
	{ 34, 0, 44, 32, 0, 0 },
	{ 38, 40, 33, 0x2323, 0, 0 },
	{ 36, 34, 0, 0, 0, 0 },			/* 35 */
	{ 0, 37, 35, 0, 0, 0 },
	{ 0, 0, 0, 36, 0, 61 },
	{ 0, 39, 34, 0, 0, 0 },
	{ 0, 0, 0, 38, 0, 0 },
	{ 0, 42, 0x6629, 34, 0x6629, 0 },	/* 40 */
	{ 40, 0, 0, 0, 0, 0 },
	{ 0, 43, 0, 40, 0, 0 },
	{ 0, 42, 0, 0, 0, 0 },
	{ 0, 45, 0, 33, 0, 0 },
	{ 0, 46, 48+0xe000, 44, 0, 0 },	/* 45 */
	{ 0, 47, 0, 45, 0, 0 },
	{ 0, 0, 0, 46, 0, 0 },
	{ 56+0xe000, 55+0xe000, 53+0xe000, 49+0xe000, 45, 52+0xe000 },
	{ 50+0xe000, 49+0xe000, 48+0xe000, 49+0xe000, 57+0xe000, 51+0xe000 },
	{ 51+0xe000, 54+0xe000, 53+0xe000, 56+0xe000, 58+0xe000, 50+0xe000 },		/* 50 */
	{ 49+0xe000, 55+0xe000, 54+0xe000, 50+0xe000, 57+0xe000, 57+0xe000 },
	{ 48+0xe000, 56+0xe000, 55+0xe000, 49+0xe000, 54+0xe000, 52+0xe000 },
	{ 50+0xe000, 57+0xe000, 54+0xe000, 59+0xe000, 58+0xe000, 48+0xe000 },
	{ 57+0xe000, 54+0xe000, 49+0xe000, 52+0xe000, 57+0xe000, 56+0xe000 },
	{ 54+0xe000, 56+0xe000, 59+0xe000, 52+0xe000, 53+0xe000, 51+0xe000 },		/* 55 */
	{ 48+0xe000, 57+0xe000, 52+0xe000, 53+0xe000, 58+0xe000, 50+0xe000 },
	{ 56+0xe000, 49+0xe000, 53+0xe000, 51+0xe000, 54+0xe000, 52+0xe000 },
	{ 54+0xe000, 56+0xe000, 48+0xe000, 55+0xe000, 55+0xe000, 50+0xe000 },
	{ 58+0xe000, 55+0xe000, 57+0xe000, 52+0xe000, 60, 53+0xe000 },
	{ 0, 0, 0, 0, 0, 59+0xe000 },	/* 60 */
	{ 0, 0, 62, 0, 37, 0 },
	{ 61, 63, 0, 71, 0, 0 },
	{ 64, 66, 0xe243, 62, 0, 0 },
	{ 0, 0, 63, 0, 0x6641, 0 },
	{ 0, 0, 0, 0, 0, 64 },			/* 65 */
	{ 0, 0, 68, 63, 0, 0 },
	{ 0xe23f, 68, 0, 0, 0, 0 },
	{ 66, 0, 69, 67, 0, 0 },
	{ 0, 68, 0x5749, 70, 0, 0 },
	{ 71, 0, 69, 80+0xe000, 0, 0 },		/* 70 */
	{ 72, 62, 70, 0, 0, 0 },
	{ 0, 0, 71, 0, 0, 0xc258 },
	{ 69, 0, 0x574a, 0, 0, 0 },
	{ 0x5749, 0, 0x574b, 0x574c, 0, 0 },
	{ 0x574a, 0x574e, 0, 0x574c, 0, 0 },	/* 75 */
	{ 0x574a, 0x574b, 0x574d, 0, 0, 0 },
	{ 0x574c, 0x574e, 0x584f, 0, 0, 0 },
	{ 0x574b, 0, 0x584f, 0x574d, 0, 0 },
	{ 0, 78, 0, 77, 0, 0 },
	{ 81+0xe00, 81+0xe00, 83+0xe00, 85+0xe00, 84+0xe00, 70 },		/* 80 */
	{ 83+0xe00, 82+0xe00, 80+0xe00, 86+0xe00, 85+0xe00, 81+0xe00 },
	{ 82+0xe00, 85+0xe00, 80+0xe00, 81+0xe00, 86+0xe00, 84+0xe00 },
	{ 85+0xe00, 83+0xe00, 82+0xe00, 84+0xe00, 83+0xe00, 83+0xe00 },
	{ 83+0xe00, 84+0xe00, 84+0xe00, 82+0xe00, 83+0xe00, 84+0xe00 },
	{ 85+0xe00, 81+0xe00, 82+0xe00, 86+0xe00, 82+0xe00, 81+0xe00 },		/* 85 */
	{ 86+0xe00, 82+0xe00, 84+0xe00, 83+0xe00, 83+0xe00, 87 },
	{ 0, 0, 0, 0, 86+0xe00, 0 },
	{ 0, 0, 89, 0x245f, 72, 0 },
	{ 88, 90, 91, 0, 0, 0 },
	{ 0, 0, 0, 89, 0, 0 },			/* 90 */
	{ 89, 92, 93, 0, 0, 0 },
	{ 0, 0, 0, 91, 0, 0 },
	{ 91, 94, 0, 0, 0, 0 },
	{ 0, 0, 0, 93, 0, 0 },
	{ 0, 0x2458, 97, 96, 0, 0 },	/* 95 */
	{ 0, 95, 0, 0, 0, 0 },
	{ 95, 0, 98, 0, 0, 0 },
	{ 97, 0, 100, 99, 0, 0 },
	{ 0, 98, 0, 0, 0, 0x2565 }, 
	{ 98, 0, 0, 0, 0, 0 },			/* 100 */
	{ 0, 102, 0, 0, 0x2563, 0 },
	{ 112, 0, 103, 101, 0, 0 },
	{ 102, 104, 0, 106, 0, 0 },
	{ 0, 0xc00a, 0, 103, 0, 0 },
	{ 0, 0, 10, 0, 0, 0 }, 			/* 105 */
	{ 0, 103, 107, 0, 0, 0 },
	{ 106, 0, 0xe26c, 109, 0, 0 },
	{ 0, 0xe26b, 0, 109, 0, 0 },
	{ 110, 107, 108, 0, 0, 0 },
	{ 116, 111, 109, 120, 0, 0 },	/* 110 */
	{ 118, 112, 0, 110, 0, 0 },
	{ 0, 113, 102, 111, 0, 0 },
	{ 114, 0, 0, 112, 0, 0 },
	{ 0, 115, 113, 116, 0, 0 },
	{ 0, 0, 0, 114, 0, 0 },			/* 115 */
	{ 0x2675, 114, 110, 0, 0, 0 },
	{ 0, 0, 116, 0, 0, 0 },
	{ 0xe077, 0, 111, 0, 0, 0 },
	{ 0xe177, 0xe177, 0xc176, 0xe177, 0xe177, 0xe177 },
	{ 121, 110, 0, 0, 0, 0 },		/* 120 */
	{ 0, 0, 120, 0, 0, 122 },
	{ 0, 123, 131, 0, 121, 0 },
	{ 0, 125, 0x287c, 122, 0, 0 },
	{ 123, 0, 0, 0, 0, 0 },
	{ 126, 0, 132, 123, 0, 0 },		/* 125 */
	{ 11, 0, 125, 127, 0, 0 },
	{ 128, 126, 0, 129, 0, 0 },
	{ 0, 0, 127, 0, 0, 0 },
	{ 127, 0, 131, 130, 0, 0 },
	{ 129, 0, 133, 0, 0, 0 },		/* 130 */
	{ 122, 0x2787, 0xe285, 129, 0, 0 },
	{ 125, 0, 133, 0, 0, 0 },
	{ 0xe283, 132, 134, 130, 0, 0 },
	{ 133, 0, 0, 0, 0, 136 },
	{ 0, 0, 0, 0x2783, 0, 0 },		/* 135 */
	{ 137, 0, 0, 0, 134, 0 },
	{ 138, 0, 136, 0, 0, 0 },
	{ 0, 139, 137, 144, 0, 0 },
	{ 140, 0, 0, 138, 0, 0 },
	{ 142, 0, 139, 141, 0, 0 },		/* 140 */
	{ 0, 140, 0, 0, 0, 0 },
	{ 0, 0, 140, 143, 0, 0 },
	{ 0x2994, 142, 0, 147, 0, 0 },
	{ 145, 138, 0, 0, 0, 0 },
	{ 147, 146, 144, 0, 0, 0 },		/* 145 */
	{ 0x2a95, 0, 0, 145, 0, 0 },
	{ 0, 143, 145, 0, 0, 0 },
	{ 0, 0, 0x298f, 0, 0, 0 },
	{ 0, 0, 146, 0, 0, 150 },
	{ 0, 0, 151, 0, 149, 0 },		/* 150 */
	{ 150, 152, 0, 160, 0, 0 },
	{ 151, 0x2b9b, 153, 0, 0, 0 }, 
	{ 152, 0x2c9c, 154, 0, 0, 0 },
	{ 153, 0x2d9d, 158, 0, 0, 0 },
	{ 0, 0, 0, 0x2b98, 0, 0 },		/* 155 */
	{ 0, 0, 0, 0x2c99, 0, 0 },
	{ 0, 0, 0, 0x2d9a, 0, 0 },
	{ 0, 154, 159, 160, 0, 0 },
	{ 158, 0, 0, 0, 0, 0 },
	{ 151, 0, 158, 161, 0, 0 },		/* 160 */
	{ 162, 160, 168, 163, 0, 0 },
	{ 0, 0, 161, 0, 0, 0 },
	{ 12, 161, 0, 166, 0, 0 },
	{ 0, 0, 12, 0, 0, 0 },
	{ 0, 12, 166, 0, 0, 0 },		/* 165 */
	{ 165, 163, 167, 169, 0, 0 },
	{ 166, 168, 0, 0, 0, 0 },
	{ 161, 0, 0, 167, 0, 0 },
	{ 170, 166, 173, 171, 0, 0 },
	{ 0, 0, 169, 177, 0, 0 },		/* 170 */
	{ 0, 169, 0, 172, 0, 0 },
	{ 178, 171, 175, 0, 0, 0 },
	{ 169, 0, 0, 174, 0, 0 },
	{ 0, 173, 0, 175, 0, 0 },
	{ 172, 174, 0xe3b0, 0, 0, 0 },	/* 175 */
	{ 175, 0, 0, 0, 0, 0 },
	{ 0, 170, 0, 178, 0, 0 },
	{ 0, 177, 172, 179, 0, 0 },
	{ 0, 178, 0, 180, 0, 0 },
	{0, 179, 0, 0, 0, 0 }			/* 180 */
	} ;
	
/* death messages, issued for the three times he is allowed to die */
char *diemsgs[] = {
	"Oh dear.. You seem to have gotten yourself killed!\nI might be of some assistance although I have never actually done this before.\nWould you like me to try and re-incarnate you",
	"Clumsy oaf, you've done it again...\nI don't know how long I can keep this up!\nWould you like me to try again to re-incarnate you",
	"Now you've REALLY done it!!!\nIm all out of orange smoke!!!\nYou don't expect me to do a decent re-incarnation\nwithout orange smoke do you" } ;

/* snide comments as we resurrect him. */
char *resmsgs[] = {
	"All right, but don't blame me if something goes wrong...\n((( POOF!!! )))\nYou are engulfed in a cloud of bright orange smoke...\nYou emerge to find...",
	"Ok... Now where did I put my orange smoke...\n((( POOF!!! )))\nEverything disappears in a dense cloud of orange smoke",
	"If you're so smart.. do it yourself... Im leaving." } ;

/* descriptions of live (or at least moveing) monsters */
char *monsters[] = {
	"There is a nasty looking skeleton attacking you!",
	"There is a nasty big orc in the room with you!",
	"A wicked little goblin is in the room with you!",
	"There is a fearsome bugbear in the room with you!",
	"A huge lizard-man is here in the room with you!",
	"There is a fearsome looking ogre in the room with you!",
	"There is a wretched looking mummy attacking you!",
	"A large suit of armor is attacking you",
	"A huge big giant is trying to kill you!",
	"Near the edge of your vision, a slight movement attracts your attention!",
	"A awful *DRAGON* is here in the room, he dosn't look friendly",
	"A little man in a long flowing robe is looking at you"
	};

/* description of monsters attacking */
char *monster_attack[] = {
	"The skeleton lunges at you!",
	"The orc swings at you with a nasty long sword!",
	"The goblin thrusts at you with a sharp spear!",
	"The bugbear claws and bites at you!",
	"The lizard-man snaps at you!",
	"The ogre bashes at you with a giant club!",
	"The mummy howls, and lunges at you!",
	"The suit of armour swings a fearsome sword!",
	"The giant clobbers at you with its huge fists!",
	"You hear the swoosh of a dagger behind your back!",
	"The *DRAGON* breathes fire.... Wow is it getting warm in here!!!",
	"The wizard waves his hands, and pronounces a spell!"
	};

/* descriptions of deceased monsters */
char *dead_monster[] = {
	"A shattered pile of bones is lying to one side.",
	"A dead orc is lying here.",
	"A dead goblin is lying to one side.",
	"The body of a dead bugbear is peacefully resting here.",
	"The body of a lizard-man is lying here.",
	"A huge ugly nasty horrible dead ogre is lying on the ground here.",
	"A pile of cloth and bones is lying to one side.",
	"Pieces of broken armor litter the floor.",
	"The body of a huge giant is partially blocking the passage.",
	"A dead small elvish humanoid is here, wearing a small black mask.",
	"The body of a huge dead dragon is nearly blocking the passage.",
	"A small pile of ashes and a wizard's hat are lying here."
	};

/* names of monsters */
char *monster_short[] = {
	"skeleton", "orc", "goblin", "bugbear", "lizard-man", "ogre",
	"mummy", "suit of armor", "giant", "thief", "dragon", "wizard" };

t_usi monster_start[] = {		/* highest place monster can go */
	15, 32, 32, 61, 61, 88, 101, 122, 122, 32, 137, 150 };

/* rank messages */
char *ranks[] = {
	"You are obviously a rank amateur. Better luck next time.",
	"Your score qualifies you as a novice class adventurer.",
	"You have acheived the rateing 'Experienced Adventurer'.",
	"You may now consider yourself a 'Seasoned Adventurer'.",
	"You have reached 'Junior Master' status.",
	"You now rate as a master adventurer class D.",
	"You now rate as a master adventurer class C.",
	"You now rate as a master adventurer class B.",
	"You now rate as a master adventurer class A.",
	"All adventuredom gives tribute to you, 'Adventurer Grandmaster'." };

/* misc. common messages */
char can_not_move[]		= "You can not move in that direction." ;
char too_big[]			= "Something your carrying won't fit through the passage." ;

FILE *fp, *fp1;

/* Function declaration */
char skip_blank();
t_bool isterm(unsigned char);
t_usi parse();
void get_line(char *);
void describe(t_usi , char *table[]);
void read_data_file();
t_bool isdark(t_usi);
t_bool have_object(unsigned char); 
t_bool ishere(unsigned char); 
void look(t_usi);
void describe_door(t_usi);
t_bool move(unsigned char);
t_bool special_condition(t_usi);
t_bool special_action(t_usi);
t_si isobject(t_usi);
void message(char *);
void ok();
void not_here();
t_bool test_range(t_usi, t_usi, t_usi);
void die();
t_si calculate_score();
void end_game();


/*---------------------------------------------------------------------------*/
/* main function                                                             */
/*---------------------------------------------------------------------------*/
int main()
{
	t_si words[10], i, j, verb, noun, door, temp;


/* initialize random number seed */
	srand( time( NULL ) );

	message("ADVENTURE CASTLE by Dave Dunfield\n");

/* get help if any */
	get_line("Welcome adventurer, would you like instruction?");
	
	if (*inptr == 'y') {
		t_si ch;
		fp1 = fopen("castle.hlp","rb+");
		while((ch = getc(fp1)) != EOF)
			putc(ch, stdout);
		fclose(fp1);
	}

/* initializations */
	message("One moment please...");
	read_data_file();
	putc('\n',stdout);
	look(current_loc);

	for (;;) {

		if(current_loc > deep) deep = current_loc;
		get_line(">");
		temp=0;
		while (*inptr)
			if ( (words[temp] = parse() ) )
				++temp;

		words[temp] = 0;
		verb = noun = 0;

		for (i = 0 ; i< temp ; ++i) {
			if ((words[i] < 50) && (words[i] > verb))
				verb = words[i];
			if (words[i] > 49)
				noun = words[i];
		}
		
		if ((!verb) && (noun > 239))		/* assume GO */
			verb = 1;

		door = -1;
		
		for (i=0 ; i<6 ; ++i)
			if ((travel[current_loc][i] >> 13) == 1)
				door = (travel[current_loc][i] >> 8) & 0x1f;
	
	/* figure out what he wants */
		switch(verb) {
		case 1 :		/* movement */
			if (noun < 240)
				message("Give me a direction.");
			else
				move(noun - 240);
			break;

		case 2 :		/* take an object */
			if ((noun < 100) && (noun > 49))
				message("You can't move it.");
			else if ((noun = isobject(noun)) != -1) {
				if (!location[noun])
					message("Your already carrying it!");
				else if (location[noun] != current_loc)
					message("I don't see it here.");
				else if (carry >= (CARRY + (5*(location[6] == 0))))
					message("You can't carry anything more, you will have to drop something first.");
				else if ((noun == 5) && (!prop[noun]))
					message("The sword is stuck quite firmly, you can't budge it!");
				else {
					location[noun] = 0;
					++carry;
					if(noun == 30)
						found_chest = -1;
					ok(); } }
			break;

		case 3 :		/* drop an object */
			if ((noun = isobject(noun)) != -1) {
				if (location[noun])
					message("You are not carrying it!");
				else if ((noun == 6) && (carry > CARRY))
					message("You need it to carry some of the other stuff!");
				else {
					location[noun] = current_loc;
					--carry;
					ok();
					if ((current_loc == 60) && (noun == 20)) {
						location[noun] = 148;		/* remove coins to supply room */
						if(!location[7])			/* he's carrying old batteries */
							--carry;
						location[7] = current_loc;	/* give him batteries */
						prop[7] = 0;				/* insure they are fresh */
						message("There are fresh batteries here!"); }
					if ((noun == 35) && (location[13] != current_loc)) {
						prop[35] = 1;
						message("The vase drops with a delicate crash!");
					}
				}
			}
			break;
			
		case 4 :		/* dig */
			message("Digging without a shovel is impractical, progress seems unlikely.");
			break;

		case 5 :		/* light lamp */
			if (test_range(noun,100,100)) {
				if (have_object(0)) {
					if (prop[0])
						message("Its already on.");
					else {
						if ((!lstate) && (!prop[7]) && (ishere(7))) {
							lstate = prop[7] = 1;
							lturns = 0;
							message("I'm taking the liberty of changing the batteries.");
						}
						if ( (prop[0] = lstate) )  {
							ok();
							if (current_loc >= DARK) look(current_loc);
						}
						else
							message("The batteries are dead.");
					}
				}
			}
			break;

		case 6 :		/* turn off lamp */
			if (test_range(noun,100,100)) {
				if (have_object(0)) {
					if (!prop[0])
						message("Its already off.");
					else {
						prop[0] = 0;
						ok();
						if (current_loc >= DARK) look(current_loc);
					}
				}
			}
			break;

		case 7 :			/* open a door */
			if (test_range(noun, 50, 50)) {
				if (door == -1)
					not_here();
				else {
					if ((temp=door_keys[door]) && (!location[temp])) {
						door_stat[door] = 1;
						ok();
					}
					else
						message("You can't, I think you will need something to do that.");
				}
			}
			break;

		case 8 :			/* close a door */
			if (test_range(noun, 50, 50)) {
				if (door == -1)
					not_here();
				else {
					if ((temp=door_keys[door]) && (!location[temp])) {
						door_stat[door] = 0;
						ok();
					}
					else
						message("You can't, I think you will need something to do that.");
				}
			}
			break;

		case 9 :			/* look around */
			message("I will repeat the description of your location:");
			look(current_loc);
			break;

		case 10 :			/* inventory */
			message("You are currently carrying the following:");
			for (i=0; i<OBJECTS; ++i)
				if(!location[i])
					describe(i,s_descriptions);
			break;

		case 11 :			/* quit */
			get_line("Do you really want to quit now?");
			if ('y' == *inptr)
				end_game();
			ok();
			break;

		case 12 :			/* read */
			if (noun == 110) {		/* scroll */
				if (have_object(10)) {
					if (prop[10])
						message("Im afraid the paper is blank");
					else if(ishere(21) && (current_loc == 33)) {
						prop[10] = prop[5] = 1;
						message("As you pronounce the archane words, the sword jumps as if alive,\nand falls at your feet.");
					}
					else {
						prop[10] = 1;
						message("As you attempt to pronounce the strange words on the scroll,\nThe ink disappears in a wisp of orange smoke.");
					}
				}
			}
			else
				message("I don't know how to read that!");
			break;

		case 13 :		/* wave, rub, etc */
			if ((noun = isobject(noun)) != -1) {
				if (have_object(noun)) {
					if ((noun == 9) && ((current_loc == 88) || (current_loc == 95))) {
						if (door_stat[4]) {
							door_stat[4] = 0;
							message("The stone bridge has vanished!");
						}
						else {
							door_stat[4] = 1;
							message("A stone bridge now spans the fissure!");
						}
					}
					else if(noun == 16) {
						message("Crackle... Buzz... Humm....");
						if ((current_loc == 6) && (location[31] == -1)) {
							message("BEEP BEEP BEEP: Detected something");
							location[31] = current_loc;
						}
						else
							message("No metal here");
					}
					else
						message("Nothing exciting happens.");
				}
			}
			break;

		case 14 :
			message("Good try.... but thats on old worn-out magic word.");
			break;

		case 15 :		/* magic word */
			old_loc = 0;
			if ((current_loc == 16) || (current_loc == 133))
				look(current_loc = 4);
			else if (current_loc == 4)
				look(current_loc = 16);
			else if (current_loc == 102)
				look(current_loc = 10);
			else if (current_loc == 10)
				look(current_loc = 102);
			else
				message("Nothing unusual happens.");
			break;
		
		case 16 :		/* suspend game */
			if ( (fp1 = fopen("castle.sav","wb")) ) {

				fwrite(prop,sizeof(char),OBJECTS,fp1);
				fwrite(door_stat,sizeof(char),DOORS,fp1);
				fwrite(location,sizeof(t_usi),OBJECTS,fp1);
				fwrite(&lstate,sizeof(t_si),1,fp1);
				fwrite(&died,sizeof(t_si),1,fp1);
				fwrite(&found_chest,sizeof(t_si),1,fp1);
				fwrite(&carry,sizeof(t_usi),1,fp1);
				fwrite(&turns,sizeof(t_usi),1,fp1);
				fwrite(&lturns,sizeof(t_usi),1,fp1);
				fwrite(&current_loc,sizeof(t_usi),1,fp1);
				fwrite(&old_loc,sizeof(t_usi),1,fp1);
				fwrite(&deep,sizeof(t_usi),1,fp1);
				fwrite(&hits,sizeof(t_si),1,fp1);
				fwrite(monster_hits,sizeof(t_si),MONSTERS,fp1);
				fwrite(monster_loc,sizeof(t_usi),MONSTERS,fp1);
				fclose(fp1);

				message("Game saved...");
				end_game();
			}
			else
				message("Impossible to save game");
			break;

		case 17 :		/* resume game */
			if (!turns) {
				if ( (fp1 = fopen("castle.sav","rb")) ) {
					//fget(prop, (date - prop) + 4, fp1);

					fread(prop,sizeof(char),OBJECTS,fp1);
					fread(door_stat,sizeof(char),DOORS,fp1);
					fread(location,sizeof(t_usi),OBJECTS,fp1);
					fread(&lstate,sizeof(t_si),1,fp1);
					fread(&died,sizeof(t_si),1,fp1);
					fread(&found_chest,sizeof(t_si),1,fp1);
					fread(&carry,sizeof(t_usi),1,fp1);
					fread(&turns,sizeof(t_usi),1,fp1);
					fread(&lturns,sizeof(t_usi),1,fp1);
					fread(&current_loc,sizeof(t_usi),1,fp1);
					fread(&old_loc,sizeof(t_usi),1,fp1);
					fread(&deep,sizeof(t_usi),1,fp1);
					fread(&hits,sizeof(t_si),1,fp1);
					fread(monster_hits,sizeof(t_si),MONSTERS,fp1);
					fread(monster_loc,sizeof(t_usi),MONSTERS,fp1);

					fclose(fp1);
				}
				look(current_loc);
			}
			else
				message("You can only resume at the start of a game");
			break;

		case 18 :			/* attack */
			if (test_range(noun,103,105)) {
				if (!location[noun - 100]) {
					noun -= 103;
					temp = 0;
					message("You take a wild swing...");
					for (i=0; i < MONSTERS; ++i) {
						if (monster_loc[i] == -1) {
							++temp;
							if (rand() > (8000 - (noun*1500))) {
								printf("and hit the %s!\n",monster_short[i]);
								if ((monster_hits[i] -= damage[noun]) <= 0) {
									monster_loc[i] = current_loc;
									message("You killed it!!!");
								}
							}
						}
					}
					if (!temp)
						message("but your blade meets only air, be carefull not to hurt yourself!"); }
				else
					message("You don't have that weapon."); }
			break;

		case 19 :		/* drink */
			if (test_range(noun,111,112) && have_object(noun-100)) {
				if (noun == 111) {
					if (!prop[11]) {		/* bottle */
						++prop[11];
						hits = STARTING_HITS;
						message("That was quite refreshing, you feel stronger.");
					}
					else
						message("The bottle is empty.");
				}
				else {
					if (!prop[12]) {		/* flask */
						++prop[12];
						message("That was awful... I don't feel well!");
						if((hits -= 15) <= 0)
							die();
						}
					else
						message("The flask is empty.");
				}
			}
			break;

		case 20 :		/* empty */
			if (test_range(noun,111,112) && have_object(noun = noun-100)) {
				if (!prop[noun]) {
					++prop[noun];
					ok();
				}
				else
					message("It's already empty.");
			}
			break;

		case 21 :		/* backup */
			if ((!old_loc) || (old_loc == current_loc))
				message("I can't quite seem to remember how it was we got here!");
			else
				look(current_loc = old_loc);
			break;

		case 22 :		/* lubricate */
			if (test_range(noun,50,50)) {
				if (!ishere(12))
					message("You have no oil!");
				else if(prop[12])
					message("The oil flask is empty!");
				else if(door == -1)
					not_here();
				else {
					++prop[12];
					if(current_loc == 123) {	/* castle door */
						++door_stat[8];
						message("As you lubricate the hinges, the door slowly creaks open!");
					}
				}
			}
			break;

		case 23 :			/* score, points etc. */
			printf( "If you quit now, you will have a score of %u points.\n", calculate_score());
			break;

		case 24 :			/* bad words */
			message("Watch your language!!!");
#ifdef DEBUG
			printf("[%u], ", current_loc);
			get_line("New?");
			look(current_loc = atoi(inptr));
#endif
			break;

		default :
			message("I don't quite understand you.");
		}

		++turns;

/* check the status of the lamp */
		if (prop[0]) {		/* lamp is on */
			if ((LAMP_DIM_1 <= ++lturns) && (!location[7]) && (ishere(0)) && (!prop[7])) {	/* time to change batteries */
				prop[7] = prop[0] = lstate = 1;	/* take away new batteries, brighten lamp */
				lturns = 0;						/* insure we don't go dim again */
				message("You lamp is going dim, Im takeing the liberty of changing the batteries.");
			}
			else if (lturns == LAMP_DIM_1) {
				prop[0] = lstate = 2;
				if (ishere(0)) {
				message("Your lamp is growing dim... You should wrap this up soon, unless you");
				message("can find some fresh batteries. I seem to recall there is a vending");
				message("machine in the maze, bring some coins with you");
				}
			}
			else if(lturns == LAMP_DIM_2) {
				prop[0] = lstate = 3;
				if (ishere(0))
					message("You lamp is getting dimmer... You'd best get those batteries soon!");
			}
			else if(lturns == LAMP_DIE) {
				prop[0] = lstate = 0;
				if (ishere(0))
					message("Your lamp has run out of power.");
			}
		}

		if ((isdark(current_loc)) && (8000 > rand()) && (verb == 1)) {
			message("You fell into a pit... OUCH!!! ... That hurt!");
			if((hits -= 10) <= 0) {
				die();
			}
		}

/* check for wandering monsters */
		for (i=0; i < MONSTERS; ++i) {
			if ((monster_loc[i] == current_loc) && (monster_hits[i] > 0))
				monster_loc[i] = -1;
		
			if (monster_loc[i] == -1) {
				if (current_loc >= DARK-1) {
					message(monsters[i]);
					temp = rand();
					if (temp < 1000) {			/* monster runs off */
						monster_loc[i] = current_loc + 1;
						message("It ran off!");
					}
					else if(temp < 23000) {		/* monster attacks */
						j = OBJECTS;
						if ((i == 9) && (!found_chest)) {	/* thief */
							while(--j > 19) {
								if (ishere(j)) {
									monster_loc[9] = location[30] = location[j] = 87;
									--carry;
									j = 1;				/* only take one */
									temp = 32767;		/* insure no attack */
									message("A thief stole a treasure and ran off!");
								}
							}
						}
						if (j) 
							message(monster_attack[i]);
						if (temp < 12000) {		/* monster hits */
							message("It hits you...");
							if ((hits -= (t_si)(i+2)) <= 0) {
								i = MONSTERS;	/* don't attack if DIE returns */
								die();
							}
							else
								message("OUCH!!! ... That hurt!!!");
						}
					}
				}
			}
			else {					/* monster is not in room */
				if (monster_hits[i] > 0)
					if (LOCS <= ++monster_loc[i])
						monster_loc[i] = monster_start[i];
			}
		}
	}
}

/*---------------------------------------------------------------------------*/
/* skip ahead to next non-blank                                              */
/*---------------------------------------------------------------------------*/
char skip_blank() {

	while (*inptr == ' ')
		++inptr;
	return *inptr;
}

/*---------------------------------------------------------------------------*/
/* test for terminator character                                             */
/*---------------------------------------------------------------------------*/
t_bool isterm(unsigned char chr) {
	return (chr == ' ') || (chr == 9) || (chr == 0);
}

/*---------------------------------------------------------------------------*/
/* pick off next word from input line                                        */
/*---------------------------------------------------------------------------*/
t_usi parse() {

	unsigned char *tmptr, *tabptr;

	tmptr = inptr;

    for (tabptr = vocabulary ; *tabptr; ) {
		for (inptr = tmptr; *inptr == *tabptr; ++inptr) {
			++tabptr;
		}
		
		if ( *tabptr == (128 + *inptr++ ) && isterm(*inptr)  ) {
			skip_blank();
			return *++tabptr;
		}
		else {
			inptr = tmptr;
			while(!(*tabptr++ & 0x80));
			++tabptr;
		}
	}

	fputs("I don't know the word '",stdout);
	while (!isterm(*inptr))
		putc(*inptr++, stdout);
	message("'.");
	skip_blank();
	return 0;
}

/*---------------------------------------------------------------------------*/
/* prompt for and get a line from the terminal                               */
/*---------------------------------------------------------------------------*/
void get_line(char *string) {

	t_si chr;
	unsigned char *ptr;

	do {
		fputs(string, stdout);
		inptr = ptr = inbuf;
		while ((chr = getc(stdin)) != '\n')
			*ptr++ = (unsigned char) chr;
		*ptr = 0;
	}
	while(!skip_blank());
}

/*---------------------------------------------------------------------------*/
/* give description of an object                                             */
/* type=0 specifies inventory type description                               */
/*---------------------------------------------------------------------------*/
void describe(t_usi object, char *table[]) {

	char *ptr, *ptr1;
	register t_usi pvalue;

	ptr = table[object];
	pvalue = prop[object] + 1;

	while ((--pvalue) && (*ptr)) {
		ptr1 = ptr;
		while((*ptr) && ('|' != *ptr++));
	}

	if (!*ptr) ptr = ptr1;			/* no special description */
	
	while ((*ptr) && ('|'!=*ptr)) putc(*ptr++, stdout);
	
	putc('.', stdout);
	putc('\n', stdout);
}

/*---------------------------------------------------------------------------*/
/* read in data file                                                         */
/*---------------------------------------------------------------------------*/
void read_data_file() {
	t_usi i;
	t_si chr;

	fp = fopen("castle.dat","rb");
	i = 0;

	while ((chr =  getc(fp)) >= 0) {
		if (chr == 0) {
			places[i] = ftell(fp);
			++i;
		}
	}
}

/*---------------------------------------------------------------------------*/
/* determine if a location is dark                                           */
/*---------------------------------------------------------------------------*/
t_bool isdark(t_usi locn) {

	if (locn < DARK) return FALSE;
	if (!prop[0]) return TRUE;
	if (location[0] == locn) return FALSE;
	if ((!location[0]) && (locn == current_loc)) return FALSE;

	return TRUE;
}

/*---------------------------------------------------------------------------*/
/* determine if object is here, complain if not                              */
/*---------------------------------------------------------------------------*/
t_bool have_object(unsigned char obj) {

	if (!ishere(obj)) {
		message("I see no such object here.");
		return FALSE;
	}
	else
		return TRUE;
}

/*---------------------------------------------------------------------------*/
/* determine if an object is here                                            */
/*---------------------------------------------------------------------------*/
t_bool ishere(unsigned char obj) {

	return (location[obj] == 0) || (location[obj] == current_loc);
}

/*---------------------------------------------------------------------------*/
/* look at a location, and describe it                                       */
/*---------------------------------------------------------------------------*/
void look(t_usi locn) {

	t_usi i;
	t_si chr;
	char *eptr1, *eptr2;
	static char code1[] = { 54, 126, 99, 72, 12, 213, 99, 0 },
		code2[] = { 77, 118, 41, 123, 18, 0 };

	if (isdark(locn))		/* he can't see */
		message("It is now pitch dark - if you procede, you will likely fall into a pit!");
	else {
		fseek(fp, places[locn], SEEK_SET);
		eptr1 = eptr2 = "";
		while((chr = getc(fp)) > 0) {
			if(!*eptr1)
				eptr1 = code1;
			if(!*eptr2)
				eptr2 = code2;
			chr ^= *eptr1++ ^ *eptr2++ ^ locn;
			putc(chr & 0x7f, stdout);
		}

	/* decribe doors if any */
		for(i=0; i<6; ++i)
			if((travel[locn][i] >> 13) == 1)
				describe_door((travel[locn][i] >> 8) & 0x1f);

	/* describe dead monsters if any */
		for(i=0; i < MONSTERS; ++i)
			if((monster_loc[i] == current_loc) && (monster_hits[i] <= 0))
				message(dead_monster[i]);

	/* describe objects present */
		for(i=0; i < OBJECTS; ++i)
			if(location[i] == locn)
				describe(i,l_descriptions);		/* give long description */
	}
}

/*---------------------------------------------------------------------------*/
/* describe a door                                                           */
/*---------------------------------------------------------------------------*/
void describe_door(t_usi d) {

	register char chr, *ptr;

	ptr = doors[d];

	if (door_stat[d])
		while('|' != *ptr++);

	while ((chr=*ptr++) && ('|' != chr))
		putc(chr,stdout);
	
	putc('.', stdout);
	putc('\n',stdout);
}

/*---------------------------------------------------------------------------*/
/* move the poor sap, passed is direction (0-5)                              */
/*---------------------------------------------------------------------------*/
t_bool move(unsigned char dir) {

	t_usi new_loc, value;

	if (!(new_loc = travel[current_loc][dir])) {
		message(can_not_move);
		return FALSE; }

	old_loc = current_loc;

	if (new_loc & 0xff00) {		/* conditions on move */

		value = (new_loc >> 8) & 0x1f;

		switch(new_loc >> 13) {
			case 1 :			/* door */
				if (!door_stat[value]) {
					message(can_not_move);
					describe_door(value);
					return FALSE; }
				break;

			case 2 :			/* has to have an object */
				if (location[value]) {
					message("Something is preventing you from entering the passage.");
					return FALSE; }
				break;

			case 3 :			/* has to not have an object */
				if (!location[value]) {
					message(too_big);
					return FALSE; }
				break;

			case 4 :			/* object must have prop 0 */
				if (prop[value]) {
					message("Not implemented yet");
					return FALSE; }
				break;

			case 5 :			/* object must have prop 1 */
				if (prop[value] != 1) {
					message("Not implemented yet");
					return FALSE; }
				break;

			case 6 :			/* special condition code */
				if (!special_condition(value))
					return FALSE;
				break;

			case 7 :			/* special action as he moves */
				current_loc = new_loc & 0xff;	 /* move him first incase he dies */
				if (!special_action(value))
					look(current_loc);
				return TRUE;
		}
	}

	/* all ok, move him now */
	look(current_loc = new_loc & 0xff);
	return TRUE;
}

/*---------------------------------------------------------------------------*/
/* evaluate special conditions                                               */
/*---------------------------------------------------------------------------*/
t_bool special_condition(t_usi cond) {
	switch(cond) {
		case 0 :	/* must be empty handed */
			if (carry) {
				message(too_big);
				return FALSE;
			}
			break;
			
		case 1 :	/* 10 percent probable */
			if (4000 < rand()) {
				special_action(1);	/* lost message */
				return FALSE;
			}
			break;

		case 2 :	/* needs rope at current loc */
			if (location[2] != current_loc) {
				message("The passage looks unsafe, you would surely fall.");
				return FALSE;
			}
			break;

		default :
			message("Bad special condition");
	}

	return TRUE;
}

/*---------------------------------------------------------------------------*/
/* perform special actions as he moves return true indicates                 */
/* that no description of the new location should be given                   */
/*---------------------------------------------------------------------------*/
t_bool special_action(t_usi act) {

	t_usi temp;

	switch(act) {
		case 0 :	/* forgets where he was */
			old_loc = 0;
			break;

		case 1 :	/* display "lost" message */
			message("You have crawled around the rocks, and found yourself back in the main passage.");
			return TRUE;

		case 2 :	/* arrow trap */
			if (18000 > (temp = rand())) {
				message("As you walk down the passage, tiny arrows fire at you from");
				printf("little holes in the wall. %u of them get you - OUCH.\n", temp = (temp & 15) + 1);
				if ((hits -= (t_si)temp) <= 0) {
					die();
					return TRUE;
				}
			}
			break;

		case 3 :	/* enters wizards room */
			monster_loc[11] = 174;		/* wizard will enter shortly */
			break;

		default :
			message("bad special action");
	}

	return FALSE;
}

/*---------------------------------------------------------------------------*/
/* determine if noun is an object                                            */
/*---------------------------------------------------------------------------*/
t_si isobject(t_usi noun) {

	if ((noun < 100) || (noun > 199)) {
		message("I don't know of such a thing.");
		return -1;
	}
	else
		return noun - 100;
}

/*---------------------------------------------------------------------------*/
/* display message                                                           */
/*---------------------------------------------------------------------------*/
void message(char *string) {
	fputs(string,stdout);
	putc('\n',stdout);
}

/*---------------------------------------------------------------------------*/
/* display ok message                                                        */
/*---------------------------------------------------------------------------*/
void ok() {
	message("Ok");
}

/*---------------------------------------------------------------------------*/
/* display not here message                                                  */
/*---------------------------------------------------------------------------*/
void not_here() {
	message("I see nothing to do that to here.");
}

/*---------------------------------------------------------------------------*/
/* verify that word is in a given range                                      */
/*---------------------------------------------------------------------------*/
t_bool test_range(t_usi word, t_usi r1, t_usi r2) {

	if (!word) {
		message("Please specifiy what you want to do it to.");
		return FALSE;
	}
	if ((word < r1) || (word > r2)) {
		message("I don't know how to do that to the object you specified.");
		return FALSE;
	}

	return TRUE;
}

/*---------------------------------------------------------------------------*/
/* kill the poor sucker                                                      */
/*---------------------------------------------------------------------------*/
void die() {

	t_usi i;

	for (i=0; i<OBJECTS; ++i)		/* first, drop things when dead */
		if (!location[i])
			location[i] = current_loc;
	carry = 0;

	for (i=0; i < MONSTERS; ++i)		/* release monsters */
		if (monster_loc[i] == -1)
			monster_loc[i] = monster_start[i];

	hits = STARTING_HITS;

	fputs(diemsgs[died], stdout);
	get_line("?");

	if (*inptr == 'y') {
		message(resmsgs[died]);
		if (3 == ++died)		/* force end of game */
			end_game();
		look(current_loc);
	}
	else
		end_game();
}

/*---------------------------------------------------------------------------*/
/* calculate score                                                           */
/*---------------------------------------------------------------------------*/
t_si calculate_score() {

	t_usi i, score;

	score = 0;

	for (i=20; i < OBJECTS; ++i)		/* first give points for safely got treasure */
		if (location[i] == 4)
			score += 10;

	for (i=0; i < MONSTERS; ++i)		/* now give points for killing monsters */
		if (monster_hits[i] <= 0)
			score += (i + 1);

	score += deep;					/* add amount of cave explored */
	score -= 10 * died;				/* dock him if he died */
	
	if ((location[35] == 4) && (!prop[35]))		/* give him more for not busting vase */
		score += EXTRA_POINTS;

	return score;
}

/*---------------------------------------------------------------------------*/
/* finish off game, and quit                                                 */
/*---------------------------------------------------------------------------*/
void end_game() {

	t_si score, max_score, i;

	max_score = ((10 * (OBJECTS - 20)) + (LOCS-1)) + EXTRA_POINTS;
	
	for (i=0; i < MONSTERS; ++i)
		max_score += (i+1);

	fclose(fp);

	score = calculate_score();
	if (score < 0)
		score = 0;

	printf("You have ended your game with a score of %d out of\n",score = calculate_score());
	printf("a possible total of %u points, using %u turns.\n",max_score,turns);
	message(ranks[(score * 9) / max_score]);
	exit(0);
}


