#define MAX_OBJECTS     400
#define MAX_WORDS       20
#define STACK_SIZE      10
#define VERSION         3
#define RELEASE         0
#define DEFAULT_CONFIG  "\\games\\jacl\\config"

/*#define POWERC*/

#include <stdio.h>
#include <stdlib.h>

#ifdef POWERC

#include <conio.h>
#include <bios.h>

#endif

#ifdef __STDC__

int scope(int index, char *expected);
long value_of(int index);
long attribute_resolve(int wordno);
struct function_type *function_resolve(char *name);
struct variable_type *variable_resolve(int wordno);

#else

int scope();
long value_of();
long attribute_resolve();
struct function_type *function_resolve();
struct variable_type *variable_resolve();

#endif

#define HELD CURRENT_PLAYER->value+MAX_OBJECTS
#define HERE object[CURRENT_PLAYER->value]->location

#define LIMBO   32765
#define SCENERY 100
#define HEAVY   99

#define CURRENT_PLAYER          variable[0]
#define TOTAL_MOVES             variable[1]
#define TURN_WORKED             variable[2]
#define CURRENT_SCORE           variable[3]
#define INTERNAL_VERSION        variable[4]
#define DISPLAY_MODE            variable[5]

#define FALSE   0
#define TRUE    1

#define SEEK_SET        0
#define SEEK_CURRENT    1
#define SEEK_END        2

char current_function[81];
char function_name[81];
char default_function[81];
char before_function[81];
char after_function[81];
char override[81];
char text_buffer[161];
char write_buffer[240];
char last_command[81];
char current_command[81] = "\0";
char *word[MAX_WORDS + 2];
char punctuation[MAX_WORDS + 2];

int wp;
int destination = 1;
int locations, objects;
int it;
int them;
int her;
int him;
int parent;
int stack = 0;
int current_level;
int execution_level;
int custom_error;
int row = 0;
int column = 0;
int noun[4];
int screen_width = 79;
int screen_depth = 25;
int significance = 8;
int variable_contents;
int oec, location_element_contents;
int *object_element_address, *location_element_address;
int *object_backup_address, *location_backup_address;

short int restarting = FALSE;
short int spaced = FALSE;
short int notify = FALSE;
short int object_expected = FALSE;

FILE *file = NULL;

struct stack_type {
   int execution_level;
   int current_level;
   long address;
   char name[81];
};

struct stack_type backup[STACK_SIZE];

struct object_type {
   char label[21];
   char article[11];
   struct name_type *first_name;
   char inventory[41];
   char described[81];
   char described_backup[81];
   char initial[81];
   int location;
   int location_backup;
   long attributes;
   long attributes_backup;
   int info;
   int info_backup;
   int mass;
   int mass_backup;
};

struct object_type *object[MAX_OBJECTS];

struct location_type {
   char label[21];
   long no_go;
   int north;
   int north_backup;
   long go_north;
   long no_north;
   int northeast;
   int northeast_backup;
   long go_northeast;
   long no_northeast;
   int northwest;
   int northwest_backup;
   long go_northwest;
   long no_northwest;
   int south;
   int south_backup;
   long go_south;
   long no_south;
   int southeast;
   int southeast_backup;
   long go_southeast;
   long no_southeast;
   int southwest;
   int southwest_backup;
   long go_southwest;
   long no_southwest;
   int east;
   int east_backup;
   long go_east;
   long no_east;
   int west;
   int west_backup;
   long go_west;
   long no_west;
   int up;
   int up_backup;
   long go_up;
   long no_up;
   int down;
   int down_backup;
   long go_down;
   long no_down;
   int in;
   int in_backup;
   long go_in;
   long no_in;
   int out;
   int out_backup;
   long go_out;
   long no_out;
   long attributes;
   long attributes_backup;
};

struct location_type *location[MAX_OBJECTS];

struct variable_type {
   char name[21];
   int value;
   int value_backup;
   struct variable_type *next_variable;
};

struct variable_type *variable_table = NULL;
struct variable_type *variable[6];

struct function_type {
   char name[81];
   long position;
   struct function_type *next_function;
};

struct function_type *function_table = NULL;

struct word_type {
   char word[21];
   struct word_type *first_child;
   struct word_type *next_sibling;
};

struct word_type *grammar_table = NULL;

struct synonym_type {
   char original[21];
   char standard[21];
   struct synonym_type *next_synonym;
};

struct synonym_type *synonym_table = NULL;

struct name_type {
   char name[21];
   struct name_type *next_name;
};

struct filter_type {
   char word[21];
   struct filter_type *next_filter;
};

struct filter_type *filter_table = NULL;

main(argc, argv)
int argc;
char *argv[];
{
   int index;

   override[0] = 0;
   if ((file = fopen("config", "r")) != NULL)
       read_config_file();
   else if ((file = fopen(DEFAULT_CONFIG, "r")) != NULL)
       read_config_file();

   if (screen_width < 30)
       screen_width = 30;
   if (screen_depth < 10)
       screen_depth = 10;

   if (argc == 1) {
       clrscrn();
       version_info();
       write_text("you are welcome to redistribute it under certain ");
       write_text("conditions, type ~info~ for details.^^");
       printf("ENTER NAME OF GAME FILE: ");
       fgets(text_buffer, 80, stdin);
       strcat(override, text_buffer);
       row = 0;
   } else
       strcat(override, argv[1]);

   if (text_buffer[0] == 10) {
       write_text("ERROR: No data file specified, can't continue.^");
       terminate(1);
   }
   for (index = 0; index < strlen(override); index++) {
       if (override[index] == 10)
           override[index] = 0;
   }

   if ((file = fopen(override, "r")) == NULL) {
       write_text("ERROR: Unable to open data file ~");
       write_text(override);
       write_text("~, can't continue.^");
       terminate(1);
   }
   read_gamefile();

   if (location[1] == NULL) {
       write_text("ERROR: No locations defined, can't continue.^");
       terminate(1);
   }
   strcpy(function_name, "{global_intro");
   execute(function_name);

   if (object[CURRENT_PLAYER->value] == NULL) {
       write_text("ERROR: Variable CURRENT_PLAYER does not point to an ");
       write_text("object, can't continue.^");
       terminate(1);
   }
   strcpy(function_name, "{global_eachturn");
   execute(function_name);

   do {
       if (TURN_WORKED->value) {
           TOTAL_MOVES->value++;
           strcpy(function_name, "{global_eachturn");
           execute(function_name);
           if (!check_light(HERE)) {
               strcpy(function_name, "{global_grued");
               if (execute(function_name) == FALSE) {
                   unkfunrun("{global_grued");
                   terminate(0);
               }
           }
       }
       TURN_WORKED->value = TRUE;
       custom_error = FALSE;
       row = 0;

       newline();
       printf(">");

       strcpy(last_command, current_command);
       fgets(text_buffer, 80, stdin);

       if (feof(stdin)) {
           write_text("END OF FILE^");
           terminate(0);
       }
       for (index = 0; index < 80; index++)
           text_buffer[index] = tolower(text_buffer[index]);

       strcpy(current_command, text_buffer);
       encapsulate();
       truncate();
       if (word[0] != NULL) {
           if (strcmp(word[0], "undo"))
               save_game_state();
           word_check();
       } else {
           write_text("A fiendishly clever move.^");
           TURN_WORKED->value = FALSE;
       }
   }
   while (TRUE);
}

word_check()
{
   if (!strncmp(word[wp], "quit", significance) || !strncmp(word[wp], "q", significance)) {
       TURN_WORKED->value = FALSE;
       printf("\nAre you sure you want to quit? >");
       fgets(text_buffer, 4, stdin);
       if (text_buffer[0] == 121 || text_buffer[0] == 89) {
           newline();
           strcpy(function_name, "{global_score");
           execute(function_name);
           terminate(0);
       } else
           write_text("Returning to game.^");
   } else if (!strncmp(word[wp], "restart", significance)) {
       TURN_WORKED->value = FALSE;
       printf("\nAre you sure you want to restart? >");
       fgets(text_buffer, 4, stdin);
       if (text_buffer[0] == 121 || text_buffer[0] == 89) {
           write_text("^Restarting...^");
           restart_game();
       } else
           write_text("Returning to game.^");
   } else if (!strncmp(word[wp], "again", significance) || !strncmp(word[wp], "g", significance)) {
       if (last_command[0] == 0) {
           write_text("But you haven't done anything yet!^");
           TURN_WORKED->value = FALSE;
       } else if (last_command[0] == 10) {
           write_text("It wasn't so clever as to be worth repeating.^");
           current_command[0] = 10;
           TURN_WORKED->value = FALSE;
       } else {
           strcpy(text_buffer, last_command);
           strcpy(current_command, last_command);
           encapsulate();
           truncate();
           word_check();
       }
   } else if (!strncmp(word[wp], "save", significance))
       save_game();
   else if (!strncmp(word[wp], "restore", significance))
       restore_game();
   else if (!strncmp(word[wp], "undo", significance)) {
       if (TOTAL_MOVES->value && strncmp(last_command, "undo", 4))
           restore_game_state();
       else {
           write_text("Nothing to undo.^");
           TURN_WORKED->value = FALSE;
       }
   } else if (!strncmp(word[wp], "info", significance)) {
       version_info();
       write_text("you can redistribute it and/or modify it under the ");
       write_text("terms of the GNU General Public License as published by ");
       write_text("the Free Software Foundation; either version 2 of the ");
       write_text("License, or any later version.^^");
       write_text("This program is distributed in the hope that it will be ");
       write_text("useful, but WITHOUT ANY WARRANTY; without even the ");
       write_text("implied warranty of MERCHANTABILITY or FITNESS FOR A ");
       write_text("PARTICULAR PURPOSE. See the GNU General Public License ");
       write_text("for more details.^^");
       write_text("You should have received a copy of the GNU General ");
       write_text("Public License along with this program; if not, write ");
       write_text("to the Free Software Foundation, Inc., 675 Mass Ave, ");
       write_text("Cambridge, MA 02139, USA.^^");
       printf("OBJECTS DEFINED:   %d", objects);
       newline();
       printf("LOCATIONS DEFINED: %d", locations);
       newline();
       TURN_WORKED->value = FALSE;
   } else
       parser();
}

read_config_file()
{
   fgets(text_buffer, 80, file);
   while (!feof(file)) {
       encapsulate();
       if (word[0] == NULL);
       else if (!strcmp(word[0], "columns")) {
           if (word[1] != NULL)
               screen_width = atoi(word[1]);
       } else if (!strcmp(word[0], "lines")) {
           if (word[1] != NULL)
               screen_depth = atoi(word[1]);
       } else if (!strcmp(word[0], "spaced")) {
           spaced = TRUE;
       } else if (!strcmp(word[0], "notify")) {
           notify = TRUE;
       } else if (!strcmp(word[0], "significance")) {
           if (word[1] != NULL)
               significance = atoi(word[1]);
       } else if (!strcmp(word[0], "path")) {
           if (word[1] != NULL)
               strcpy(override, word[1]);
       }
       fgets(text_buffer, 80, file);
   }
   fclose(file);
   file = NULL;
}

version_info()
{
   printf("JACL Engine v%d / r%d ", VERSION, RELEASE);
   printf("/ %d object.", MAX_OBJECTS);
   write_text("^Copyright (C) 1990-1997 Stuart Allen.^^");
   write_text("This program is free software; ");
}

encapsulate()
{
   int index, length;
   int position = 0;
   int new_word = TRUE;

   length = strlen(text_buffer);
   text_buffer[--length] = 0;
   index = 0;

   for (index = 0; index < length; index++) {
       switch (text_buffer[index]) {
       case ',':
           punctuation[position - 1] = text_buffer[index];
           text_buffer[index] = 0;
           new_word = TRUE;
           break;
       case ':':
       case '\t':
       case ' ':
           text_buffer[index] = 0;
           new_word = TRUE;
           break;
       case ';':
           text_buffer[index] = 0;
           index = length;
           break;
       case '"':
           index++;
           if (new_word) {
               word[position] = &text_buffer[index];
               new_word = FALSE;
               if (position < MAX_WORDS) {
                   position++;
                   punctuation[position] = 0;
               }
           }
           for (; index < length; index++) {
               if (text_buffer[index] == 34) {
                   text_buffer[index] = 0;
                   new_word = TRUE;
                   break;
               }
           }
           break;
       default:
           if (new_word) {
               word[position] = &text_buffer[index];
               new_word = FALSE;
               if (position < MAX_WORDS) {
                   position++;
                   punctuation[position] = 0;
               }
           }
           break;
       }

   }
   word[position] = NULL;
   wp = 0;
}

truncate()
{
   int index, counter, match;
   int position = 0;

   struct synonym_type *synonym;
   struct filter_type *filter = filter_table;

   if (filter != NULL) {
       while (word[position] != NULL) {
           match = FALSE;
           do {
               if (!strncmp(word[position], filter->word, significance)) {
                   for (index = position; word[index + 1] != NULL; index++) {
                       word[index] = word[index + 1];
                       punctuation[index] = punctuation[index + 1];
                   }
                   word[index] = NULL;
                   punctuation[index] = 0;
                   match = TRUE;
               }
               filter = filter->next_filter;
           }
           while (filter != NULL && !match);
           filter = filter_table;
           if (!match)
               position++;
       };
   }
   if (synonym_table != NULL) {
       for (counter = 0; word[counter] != NULL; counter++) {
           synonym = synonym_table;
           do {
               if (!strncmp(word[counter], synonym->original, significance)) {
                   word[counter] = synonym->standard;
                   break;
               }
               if (synonym->next_synonym != NULL)
                   synonym = synonym->next_synonym;
               else
                   break;
           }
           while (TRUE);
       }
   }
}

parser()
{
   struct word_type *pointer;

   int index;
   int current_noun = -1;

   noun[0] = FALSE;
   noun[1] = FALSE;

   if (grammar_table != NULL) {
       pointer = grammar_table;
       while (word[wp] != NULL) {
           object_expected = FALSE;
           do {
               if (!strncmp(word[wp], pointer->word, significance)) {
                   if (pointer->first_child == NULL) {
                       write_text("ERROR: Incomplete grammar statement.^");
                       return;
                   } else {
                       pointer = pointer->first_child;
                       wp++;
                       break;
                   }
               } else if (pointer->word[0] == '*' && (index = noun_resolve(pointer))) {
                   noun[++current_noun] = index;
                   if (position(noun[current_noun], pointer->word)) {
                       if (pointer->first_child == NULL) {
                           write_text("ERROR: Incomplete grammar statement.^");
                           return;
                       } else {
                           pointer = pointer->first_child;
                           wp++;
                           break;
                       }
                   } else {
                       TURN_WORKED->value = FALSE;
                       return;
                   }
               } else {
                   if (custom_error == TRUE) {
                       TURN_WORKED->value = FALSE;
                       return;
                   } else if (pointer->next_sibling != NULL)
                       pointer = pointer->next_sibling;
                   else {
                       diagnose();
                       return;
                   }
               }
           }
           while (TRUE);
       };

       do {
           if (!strncmp(pointer->word, "->", 2)) {
               strcpy(function_name, "{");
               strcat(function_name, pointer->word + 2);
               strcat(function_name, "_");
               strcpy(override, function_name);
               strcpy(before_function, "{global_before_");
               strcat(before_function, pointer->word + 2);
               strcpy(after_function, "{global_after_");
               strcat(after_function, pointer->word + 2);
               strcpy(default_function, "{global_default_");
               strcat(default_function, pointer->word + 2);
               if (execute(before_function) != FALSE)
                   return;
               if (noun[0] == FALSE) {
                   strcat(function_name, location[HERE]->label);
                   if (execute(function_name) == FALSE) {
                       strcat(override, "override_");
                       strcat(override, location[HERE]->label);
                       strcpy(function_name, "{global_");
                       strcat(function_name, pointer->word + 2);
                       if (execute(function_name) == FALSE)
                           unkfunrun(function_name);
                   }
               } else if (noun[1] == FALSE) {
                   strcat(function_name, object[noun[0]]->label);
                   if (execute(function_name) == FALSE) {
                       strcat(override, "override_");
                       strcat(override, object[noun[0]]->label);
                       strcpy(function_name, "{global_");
                       strcat(function_name, pointer->word + 2);
                       if (execute(function_name) == FALSE)
                           unkfunrun(function_name);
                   }
               } else {
                   strcat(function_name, object[noun[1]]->label);
                   strcat(function_name, "_");
                   strcat(function_name, object[noun[0]]->label);
                   if (execute(function_name) == FALSE) {
                       strcat(override, object[noun[1]]->label);
                       strcat(override, "_override_");
                       strcat(override, object[noun[0]]->label);
                       strcpy(function_name, "{global_");
                       strcat(function_name, pointer->word + 2);
                       if (execute(function_name) == FALSE)
                           unkfunrun(function_name);
                   }
               }
               execute(after_function);
               return;
           } else {
               if (pointer->next_sibling == NULL)
                   break;
               else
                   pointer = pointer->next_sibling;
           }
       }
       while (TRUE);
       diagnose();
       return;
   }
   diagnose();
}

diagnose()
{
   if (custom_error) {
       TURN_WORKED->value = FALSE;
       return;
   }
   if (word[wp] == NULL) {
       write_text("The sentence you typed was incomplete.^");
   } else if (object_expected && wp != 0) {
       write_text("You can't see any such thing.^");
   } else {
       strcpy(write_buffer, "You can't use the word ~");
       strcat(write_buffer, word[wp]);
       strcat(write_buffer, "~ in that context.^");
       write_text(write_buffer);
   }
   TURN_WORKED->value = FALSE;
}

position(index, expected)
int index;
char expected[];
{
   if (!strcmp(expected, "*held")) {
       if (object[index]->location == HELD)
           return (TRUE);
       else {
           strcpy(write_buffer, "You are not holding ");
           sentence_output(index, 0);
           strcat(write_buffer, ".^");
           write_text(write_buffer);
           return (FALSE);
       }
   } else if (!strcmp(expected, "*here")) {
       if (object[index]->location == HERE)
           return (TRUE);
       else if (object[index]->location == HELD) {
           strcpy(write_buffer, "That will be a bit hard while you're holding ");
           sentence_output(index, 0);
           strcat(write_buffer, ".^");
           write_text(write_buffer);
           return (FALSE);
       } else if (find_parent(index)) {
           if (object[parent]->attributes & 32) {
               strcpy(write_buffer, "You don't see ");
               sentence_output(index, 0);
               strcat(write_buffer, " here.^");
               write_text(write_buffer);
               return (FALSE);
           } else if (object[parent]->attributes & 2097152) {
               write_buffer[0] = 0;
               sentence_output(parent, 1);
               strcat(write_buffer, " currently has ");
               sentence_output(index, 0);
               strcat(write_buffer, ".^");
               write_text(write_buffer);
               return (FALSE);
           } else
               return (TRUE);
       } else {
           strcpy(write_buffer, "You don't see ");
           sentence_output(index, 0);
           strcat(write_buffer, " here.^");
           write_text(write_buffer);
           return (FALSE);
       }
   } else if (!strcmp(expected, "*anywhere"))
       return (TRUE);
   else {
       if (object[index]->location == HERE || object[index]->location == HELD)
           return (TRUE);
       else if (find_parent(index)) {
           if (object[parent]->attributes & 32) {
               strcpy(write_buffer, "You don't see ");
               sentence_output(index, 0);
               strcat(write_buffer, " here.^");
               write_text(write_buffer);
               return (FALSE);
           } else if (object[parent]->attributes & 2097152 && !strcmp(expected, "*present")) {
               write_buffer[0] = 0;
               sentence_output(parent, 1);
               strcat(write_buffer, " currently has ");
               sentence_output(index, 0);
               strcat(write_buffer, ".^");
               write_text(write_buffer);
               return (FALSE);
           } else
               return (TRUE);
       } else {
           strcpy(write_buffer, "You don't see ");
           sentence_output(index, 0);
           strcat(write_buffer, " here.^");
           write_text(write_buffer);
           return (FALSE);
       }
   }
}

scope(index, expected)
int index;
char *expected;
{
   if (!strcmp(expected, "*held")) {
       if (object[index]->location == HELD)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(expected, "*here")) {
       if (object[index]->location == HERE)
           return (TRUE);
       else if (object[index]->location == HELD)
           return (FALSE);
       else if (find_parent(index)) {
           if (object[parent]->attributes & 32)
               return (FALSE);
           return (TRUE);
       } else
           return (FALSE);
   } else if (!strcmp(expected, "*anywhere"))
       return (TRUE);
   else {
       if (object[index]->location == HERE || object[index]->location == HELD)
           return (TRUE);
       else if (find_parent(index)) {
           if (object[parent]->attributes & 32)
               return (FALSE);
           return (TRUE);
       } else
           return (FALSE);
   }
}

save_game()
{
   struct variable_type *current_variable = variable_table;
   FILE *bookmark;
   int index;
   char text_buffer[81];

   if (word[++wp] == NULL)
       strcpy(text_buffer, "bookmark");
   else
       strncpy(text_buffer, word[wp], 80);

   if ((bookmark = fopen(text_buffer, "w")) == NULL) {
       write_text("Error opening save file ~");
       write_text(text_buffer);
       write_text("~.^");
       return;
   }
   do {
       fprintf(bookmark, "%d/", current_variable->value);
       current_variable = current_variable->next_variable;
   }
   while (current_variable != NULL);

   for (index = 1; index <= objects; index++) {
       fprintf(bookmark, "%d/%ld/%d/%d/", object[index]->info, object[index]->attributes, object[index]->location, object[index]->mass);
   }
   for (index = 1; index <= locations; index++) {
       fprintf(bookmark, "%d/%d/%d/%d/%d/", location[index]->in, location[index]->out, location[index]->north, location[index]->south, location[index]->east);
       fprintf(bookmark, "%d/%d/%d/%d/", location[index]->northeast, location[index]->northwest, location[index]->southeast, location[index]->southwest);
       fprintf(bookmark, "%d/%d/%d/%ld/", location[index]->west, location[index]->up, location[index]->down, location[index]->attributes);
   }

   fprintf(bookmark, "\n");

   for (index = 1; index <= objects; index++) {
       strcpy(text_buffer, object[index]->described);
       strcat(text_buffer, "\n");
       fputs(text_buffer, bookmark);
   }

   fclose(bookmark);
   write_text("Game saved.^");
   TURN_WORKED->value = FALSE;
}

save_game_state()
{
   int index;
   struct variable_type *current_variable = variable_table;

   do {
       current_variable->value_backup = current_variable->value;
       current_variable = current_variable->next_variable;
   }
   while (current_variable != NULL);

   for (index = 1; index <= objects; index++) {
       object[index]->info_backup = object[index]->info;
       object[index]->location_backup = object[index]->location;
       object[index]->attributes_backup = object[index]->attributes;
       strcpy(object[index]->described_backup, object[index]->described);
   }
   for (index = 1; index <= locations; index++) {
       location[index]->north_backup = location[index]->north;
       location[index]->northeast_backup = location[index]->northeast;
       location[index]->east_backup = location[index]->east;
       location[index]->southeast_backup = location[index]->southeast;
       location[index]->south_backup = location[index]->south;
       location[index]->southwest_backup = location[index]->southwest;
       location[index]->west_backup = location[index]->west;
       location[index]->northwest_backup = location[index]->northwest;
       location[index]->in_backup = location[index]->in;
       location[index]->out_backup = location[index]->out;
       location[index]->up_backup = location[index]->up;
       location[index]->down_backup = location[index]->down;
       location[index]->attributes_backup = location[index]->attributes;
   }
}

restore_game()
{
   struct variable_type *current_variable = variable_table;
   FILE *bookmark;
   int index, counter;
   char text_buffer[81];

   if (word[++wp] == NULL)
       strcpy(text_buffer, "bookmark");
   else
       strncpy(text_buffer, word[wp], 80);

   if ((bookmark = fopen(text_buffer, "r")) == NULL) {
       write_text("Error opening save file ~");
       write_text(text_buffer);
       write_text("~.^");
       return;
   }
   do {
       fscanf(bookmark, "%d/", &current_variable->value);
       current_variable = current_variable->next_variable;
   }
   while (current_variable != NULL);

   for (index = 1; index <= objects; index++) {
       fscanf(bookmark, "%d/%ld/%d/%d/", &object[index]->info, &object[index]->attributes, &object[index]->location, &object[index]->mass);
   }

   for (index = 1; index <= locations; index++) {
       fscanf(bookmark, "%d/%d/%d/%d/%d/", &location[index]->in, &location[index]->out, &location[index]->north, &location[index]->south, &location[index]->east);
       fscanf(bookmark, "%d/%d/%d/%d/", &location[index]->northeast, &location[index]->northwest, &location[index]->southeast, &location[index]->southwest);
       fscanf(bookmark, "%d/%d/%d/%ld/", &location[index]->west, &location[index]->up, &location[index]->down, &location[index]->attributes);
   }

   fscanf(bookmark, "\n");

   for (index = 1; index <= objects; index++) {
       fgets(object[index]->described, 80, bookmark);
       for (counter = 0; counter < strlen(object[index]->described); counter++) {
           if (object[index]->described[counter] == 10) {
               object[index]->described[counter] = 0;
               break;
           }
       }
   }

   fclose(bookmark);
   write_text("Game restored.^^");
   location[HERE]->attributes &= ~1L;
   display();
   TURN_WORKED->value = FALSE;
}

restore_game_state()
{
   int index;
   struct variable_type *current_variable = variable_table;

   do {
       current_variable->value = current_variable->value_backup;
       current_variable = current_variable->next_variable;
   }
   while (current_variable != NULL);

   for (index = 1; index <= objects; index++) {
       object[index]->info = object[index]->info_backup;
       object[index]->location = object[index]->location_backup;
       object[index]->attributes = object[index]->attributes_backup;
       strcpy(object[index]->described, object[index]->described_backup);
   }

   for (index = 1; index <= locations; index++) {
       location[index]->north = location[index]->north_backup;
       location[index]->northeast = location[index]->northeast_backup;
       location[index]->east = location[index]->east_backup;
       location[index]->southeast = location[index]->southeast_backup;
       location[index]->south = location[index]->south_backup;
       location[index]->southwest = location[index]->southwest_backup;
       location[index]->west = location[index]->west_backup;
       location[index]->northwest = location[index]->northwest_backup;
       location[index]->in = location[index]->in_backup;
       location[index]->out = location[index]->out_backup;
       location[index]->up = location[index]->up_backup;
       location[index]->down = location[index]->down_backup;
       location[index]->attributes = location[index]->attributes_backup;
   }
   write_text("Previous move undone.^^");
   location[HERE]->attributes &= ~1L;
   display();
   TURN_WORKED->value = FALSE;
}

display()
{
   int index;

   strcpy(function_name, "{global_title");
   execute(function_name);

   if (DISPLAY_MODE->value)
       location[HERE]->attributes &= ~1L;

   strcpy(function_name, "{look_");
   strcat(function_name, location[HERE]->label);
   execute(function_name);
   location[HERE]->attributes = location[HERE]->attributes | 1;

   for (index = 1; index <= objects; index++) {
       if (object[index]->location == HERE && object[index]->mass < SCENERY) {
           if (object[index]->attributes & 8388608 || object[index]->initial[0] == 0) {

               if (spaced)
                   newline();
               write_text(object[index]->described);
           } else {
               if (spaced)
                   newline();
               write_text(object[index]->initial);
           }
           newline();
       }
   }
}

check_light(where)
int where;
{
   int index;

   if ((location[where]->attributes & 2) == FALSE)
       return (TRUE);
   else {
       for (index = 1; index <= objects; index++) {
           if ((object[index]->attributes & 64) && scope(index, "*present"))
               return (TRUE);
       }
   }
   return (FALSE);
}

move_player(go_pointer, no_pointer)
long go_pointer;
long no_pointer;
{
   strcpy(function_name, "{global_movement");
   if (execute(function_name) == TRUE) {
       TURN_WORKED->value = FALSE;
       return (FALSE);
   }
   if (destination) {
       if (check_light(destination)) {
           if (go_pointer != 0) {
               fseek(file, go_pointer, SEEK_SET);
               fgets(text_buffer, 160, file);
               encapsulate();
               if (word[1] != NULL) {
                   write_text(word[1]);
                   newline();
                   newline();
               }
           }
           HERE = destination;
           display();
       } else {
           write_text("It's dark ");
           if (location[HERE]->attributes & 32)
               write_text("out");
           else
               write_text("in");
           write_text(" there, you'll need a light.^");
           TURN_WORKED->value = FALSE;
       }
   } else {
       TURN_WORKED->value = FALSE;
       if (no_pointer != 0) {
           fseek(file, no_pointer, SEEK_SET);
           fgets(text_buffer, 160, file);
           encapsulate();
           if (word[1] != NULL) {
               write_text(word[1]);
               newline();
           } else
               write_text("You can't go that way.^");
       } else if (location[HERE]->no_go != 0) {
           fseek(file, location[HERE]->no_go, SEEK_SET);
           fgets(text_buffer, 160, file);
           encapsulate();
           if (word[1] != NULL) {
               write_text(word[1]);
               newline();
           } else
               write_text("You can't go that way.^");
       } else
           write_text("You can't go that way.^");
   }
}

find_parent(index)
int index;
{
   if ((object[index]->location > MAX_OBJECTS) && (object[index]->location < (MAX_OBJECTS * 2))) {
       parent = object[index]->location - MAX_OBJECTS;
       if (object[parent]->location == HELD || object[parent]->location == HERE) {
           if (object[parent]->attributes & 1)
               return (FALSE);
           else
               return (TRUE);
       } else {
           if (object[parent]->attributes & 1)
               return (FALSE);
           else
               return (find_parent(parent));
       }
   } else
       return (FALSE);
}

sentence_output(index, capital)
int index;
int capital;
{
   char text_buffer[52];

   if (!strcmp(object[index]->article, "name")) {
       strcpy(text_buffer, object[index]->inventory);
   } else {
       strcpy(text_buffer, "the");
       strcat(text_buffer, " ");
       strcat(text_buffer, object[index]->inventory);
   }

   if (capital)
       text_buffer[0] = toupper(text_buffer[0]);
   strcat(write_buffer, text_buffer);
}

isnt_output(index)
int index;
{
   if (object[index]->attributes & 16384)
       strcat(write_buffer, " aren't");
   else
       strcat(write_buffer, " isn't");
}

is_output(index)
int index;
{
   if (object[index]->attributes & 16384)
       strcat(write_buffer, " are");
   else
       strcat(write_buffer, " is");
}

doesnt_output(index)
int index;
{
   if (object[index]->attributes & 16384)
       strcat(write_buffer, " don't ");
   else
       strcat(write_buffer, " doesn't ");
}

does_output(index)
int index;
{
   if (object[index]->attributes & 16384)
       strcat(write_buffer, " do ");
   else
       strcat(write_buffer, " does ");
}

list_output(index, capital)
int index;
int capital;
{
   char text_buffer[52];

   if (!strcmp(object[index]->article, "name")) {
       strcpy(text_buffer, object[index]->inventory);
   } else {
       strcpy(text_buffer, object[index]->article);
       strcat(text_buffer, " ");
       strcat(text_buffer, object[index]->inventory);
   }

   if (capital)
       text_buffer[0] = toupper(text_buffer[0]);
   strcat(write_buffer, text_buffer);
}

terminate(code)
int code;
{
   write_text("^---JACL ENGINE TERMINATED---^^^");
   more();
   if (file != NULL)
       fclose(file);
   exit(code);
}

execute(funcname)
char *funcname;
{
   char subfunc[81];

   int index, to, from;
   int counter = 0;
   int *container;
   int contents;

   long top_of_loop = FALSE;
   long go_pointer;
   long no_pointer;

   long bit_mask;

   struct function_type *resolved_function;
   struct variable_type *resolved_variable;
   struct name_type *current_name;

   char temporary_buffer[161];
   char variable_buffer[16];

   resolved_function = function_resolve(funcname);

   if (resolved_function == NULL)
       return (FALSE);

   execution_level = 0;
   current_level = 0;
   strcpy(current_function, funcname);

   fseek(file, resolved_function->position, SEEK_SET);
   fgets(text_buffer, 160, file);

   while (text_buffer[0] != 125) {
       encapsulate();
       if (word[0] == NULL);
       else if (!strcmp(word[0], "endif")) {
           current_level--;
           if (current_level < execution_level)
               execution_level = current_level;
       } else if (!strcmp(word[0], "endall")) {
           current_level = 0;
           execution_level = 0;
       } else if (!strcmp(word[0], "else")) {
           if (current_level == execution_level)
               execution_level--;
           else if (current_level == execution_level + 1)
               execution_level++;
       } else if (current_level == execution_level) {
           if (!strcmp(word[0], "more"))
               more();
           else if (!strcmp(word[0], "look")) {
               push_stack(ftell(file));
               location[HERE]->attributes &= ~1L;
               display();
               pop_stack();
           } else if (!strcmp(word[0], "endgame")) {
               while (TRUE) {
                   write_text("^Please type S to start again, Q to quit ");
                   write_text("or U to undo >");
                   row = 0;
                   fgets(text_buffer, 4, stdin);
                   if (text_buffer[0] == 'U' || text_buffer[0] == 'u') {
                       restore_game_state();
                       return (TRUE);
                   } else if (text_buffer[0] == 'Q' || text_buffer[0] == 'q')
                       terminate(0);
                   else if (text_buffer[0] == 'S' || text_buffer[0] == 's') {
                       write_text("^Restarting...^");
                       restart_game();
                       return (TRUE);
                   }
               };
           } else if (!strcmp(word[0], "loop")) {
               top_of_loop = ftell(file);
               noun[2] = 1;
           } else if (!strcmp(word[0], "endloop")) {
               if (top_of_loop == FALSE)
                   printf("ERROR: In function \"%s\", endloop command without loop command.", funcname);
               else {
                   noun[2]++;
                   if (noun[2] > objects) {
                       top_of_loop = FALSE;
                       noun[2] = FALSE;
                   } else
                       fseek(file, top_of_loop, SEEK_SET);
               }
           } else if (!strcmp(word[0], "proxy")) {
               push_stack(ftell(file));
               temporary_buffer[0] = 0;
               for (counter = 1; word[counter] != NULL; counter++) {
                   if (!strcmp(word[counter], "noun1")) {
                       current_name = object[noun[0]]->first_name;
                       while (current_name != NULL) {
                           strcat(temporary_buffer, " ");
                           strcat(temporary_buffer, current_name->name);
                           current_name = current_name->next_name;
                       }
                   } else if (!strcmp(word[counter], "noun2")) {
                       current_name = object[noun[1]]->first_name;
                       while (current_name != NULL) {
                           strcat(temporary_buffer, " ");
                           strcat(temporary_buffer, current_name->name);
                           current_name = current_name->next_name;
                       }
                   } else if (!strcmp(word[counter], "noun3")) {
                       current_name = object[noun[2]]->first_name;
                       while (current_name != NULL) {
                           strcat(temporary_buffer, " ");
                           strcat(temporary_buffer, current_name->name);
                           current_name = current_name->next_name;
                       }
                   } else if (!strcmp(word[counter], "noun4")) {
                       current_name = object[noun[3]]->first_name;
                       while (current_name != NULL) {
                           strcat(temporary_buffer, " ");
                           strcat(temporary_buffer, current_name->name);
                           current_name = current_name->next_name;
                       }
                   } else
                       strcat(temporary_buffer, word[counter]);
               }
               strcat(temporary_buffer, "\n");
               strcpy(text_buffer, temporary_buffer);
               encapsulate();
               truncate();
               word_check();
               pop_stack();
           } else if (!strcmp(word[0], "override")) {
               push_stack(ftell(file));
               if (execute(override) == TRUE) {
                   pop_stack();
                   return (TRUE);
               } else {
                   if (execute(default_function) == TRUE) {
                       pop_stack();
                       return (TRUE);
                   } else {
                       pop_stack();
                   }
               }
           } else if (!strcmp(word[0], "execute")) {
               if (word[1] == NULL)
                   noproprun();
               else {
                   strcpy(subfunc, "{");
                   strcat(subfunc, word[1]);
                   push_stack(ftell(file));
                   if (execute(subfunc) == FALSE) {
                       printf("ERROR: In function \"%s\", attempt to execute undefined function \"%s\".\n", funcname, subfunc);
                   }
                   pop_stack();
               }
           } else if (!strcmp(word[0], "terminate"))
               terminate(0);
           else if (!strcmp(word[0], "points")) {
               if (word[1] != NULL) {
                   CURRENT_SCORE->value = CURRENT_SCORE->value + atoi(word[1]);
                   if (notify) {
                       strcpy(write_buffer, "^[YOUR SCORE JUST WENT UP BY ");
                       strcat(write_buffer, word[1]);
                       if (atoi(word[1]) == 1)
                           strcat(write_buffer, " POINT]^");
                       else
                           strcat(write_buffer, " POINTS]^");
                       write_text(write_buffer);
                   }
               } else
                   noproprun();
           } else if (!strcmp(word[0], "right")) {
               if (word[1] != NULL) {
                   write_right(word[1]);
                   newline();
               } else
                   noproprun();
           } else if (!strcmp(word[0], "centre")) {
               if (word[1] != NULL) {
                   write_centred(word[1]);
                   newline();
               } else
                   noproprun();
           } else if (!strcmp(word[0], "write")) {
               write_buffer[0] = 0;
               for (counter = 1; word[counter] != NULL; counter++) {
                   if (!strcmp(word[counter], "noun1")) {
                       if (noun[0] != 0)
                           sentence_output(noun[0], 0);
                   } else if (!strcmp(word[counter], "noun1(inventory)")) {
                       if (noun[0] != 0)
                           list_output(noun[0], 0);
                   } else if (!strcmp(word[counter], "noun1(does)")) {
                       if (noun[0] != 0)
                           does_output(noun[0]);
                   } else if (!strcmp(word[counter], "noun1(doesnt)")) {
                       if (noun[0] != 0)
                           doesnt_output(noun[0]);
                   } else if (!strcmp(word[counter], "noun1(s)")) {
                       if (noun[0] != 0) {
                           if (!(object[noun[0]]->attributes & 16384))
                               strcat(write_buffer, "s");
                       }
                   } else if (!strcmp(word[counter], "noun1(is)")) {
                       if (noun[0] != 0)
                           is_output(noun[0]);
                   } else if (!strcmp(word[counter], "noun1(isnt)")) {
                       if (noun[0] != 0)
                           isnt_output(noun[0]);
                   } else if (!strcmp(word[counter], "Noun1(inventory)")) {
                       if (noun[0] != 0)
                           list_output(noun[0], 1);
                   } else if (!strcmp(word[counter], "Noun1")) {
                       if (noun[0] != 0)
                           sentence_output(noun[0], 1);
                   } else if (!strcmp(word[counter], "noun2")) {
                       if (noun[1] != 0)
                           sentence_output(noun[1], 0);
                   } else if (!strcmp(word[counter], "noun2(inventory)")) {
                       if (noun[1] != 0)
                           list_output(noun[1], 0);
                   } else if (!strcmp(word[counter], "noun2(does)")) {
                       if (noun[1] != 0)
                           does_output(noun[1]);
                   } else if (!strcmp(word[counter], "noun2(doesnt)")) {
                       if (noun[1] != 0)
                           doesnt_output(noun[1]);
                   } else if (!strcmp(word[counter], "noun2(s)")) {
                       if (noun[1] != 0) {
                           if (!(object[noun[1]]->attributes & 16384))
                               strcat(write_buffer, "s");
                       }
                   } else if (!strcmp(word[counter], "noun2(is)")) {
                       if (noun[1] != 0)
                           is_output(noun[1]);
                   } else if (!strcmp(word[counter], "noun2(isnt)")) {
                       if (noun[1] != 0)
                           isnt_output(noun[1]);
                   } else if (!strcmp(word[counter], "Noun2(inventory)")) {
                       if (noun[1] != 0)
                           list_output(noun[1], 1);
                   } else if (!strcmp(word[counter], "Noun2")) {
                       if (noun[1] != 0)
                           sentence_output(noun[1], 1);
                   } else if (!strcmp(word[counter], "noun3")) {
                       if (noun[2] != 0)
                           sentence_output(noun[2], 0);
                   } else if (!strcmp(word[counter], "noun3(inventory)")) {
                       if (noun[2] != 0)
                           list_output(noun[2], 0);
                   } else if (!strcmp(word[counter], "noun3(does)")) {
                       if (noun[2] != 0)
                           does_output(noun[2]);
                   } else if (!strcmp(word[counter], "noun3(doesnt)")) {
                       if (noun[2] != 0)
                           doesnt_output(noun[2]);
                   } else if (!strcmp(word[counter], "noun3(s)")) {
                       if (noun[2] != 0) {
                           if (!(object[noun[2]]->attributes & 16384))
                               strcat(write_buffer, "s");
                       }
                   } else if (!strcmp(word[counter], "noun3(is)")) {
                       if (noun[2] != 0)
                           is_output(noun[2]);
                   } else if (!strcmp(word[counter], "noun3(isnt)")) {
                       if (noun[2] != 0)
                           isnt_output(noun[2]);
                   } else if (!strcmp(word[counter], "Noun3(inventory)")) {
                       if (noun[2] != 0)
                           list_output(noun[2], 1);
                   } else if (!strcmp(word[counter], "Noun3")) {
                       if (noun[2] != 0)
                           sentence_output(noun[2], 1);
                   } else if (!strcmp(word[counter], "noun4")) {
                       if (noun[3] != 0)
                           sentence_output(noun[3], 0);
                   } else if (!strcmp(word[counter], "noun4(inventory)")) {
                       if (noun[3] != 0)
                           list_output(noun[3], 0);
                   } else if (!strcmp(word[counter], "noun4(does)")) {
                       if (noun[3] != 0)
                           does_output(noun[3]);
                   } else if (!strcmp(word[counter], "noun4(doesnt)")) {
                       if (noun[3] != 0)
                           doesnt_output(noun[3]);
                   } else if (!strcmp(word[counter], "noun4(s)")) {
                       if (noun[3] != 0) {
                           if (!(object[noun[3]]->attributes & 16384))
                               strcat(write_buffer, "s");
                       }
                   } else if (!strcmp(word[counter], "noun4(is)")) {
                       if (noun[3] != 0)
                           is_output(noun[3]);
                   } else if (!strcmp(word[counter], "noun4(isnt)")) {
                       if (noun[3] != 0)
                           isnt_output(noun[3]);
                   } else if (!strcmp(word[counter], "Noun4(inventory)")) {
                       if (noun[3] != 0)
                           list_output(noun[3], 1);
                   } else if (!strcmp(word[counter], "Noun4")) {
                       if (noun[3] != 0)
                           sentence_output(noun[3], 1);
                   } else if ((resolved_variable = variable_resolve(counter)) != NULL) {
                       variable_buffer[0] = 0;
                       sprintf(variable_buffer, "%d", resolved_variable->value);
                       strcat(write_buffer, variable_buffer);
                   } else
                       strcat(write_buffer, word[counter]);
               }
               write_text(write_buffer);
           } else if (!strcmp(word[0], "break")) {
               if (word[1] == NULL)
                   return (TRUE);
               else if (!strcmp(word[1], "false"))
                   return (FALSE);
               else
                   return (TRUE);
           } else if (!strcmp(word[0], "clear"))
               clrscrn();
           else if (!strcmp(word[0], "describe")) {
               if (word[1] == NULL)
                   noproprun();
               else if (index = object_resolve(1)) {
                   if (word[3] == NULL)
                       noproprun();
                   else {
                       strncpy(object[index]->described, word[3], 80);
                       object[index]->described[80] = 0;
                   }
               } else
                   unkobjrun(1);
           } else if (!strcmp(word[0], "set")) {
               if (word[3] == NULL)
                   noproprun();
               else if ((resolved_variable = variable_resolve(1)) != NULL)
                   container = &resolved_variable->value;
               else if (location_element_resolve(1))
                   container = location_element_address;
               else if (object_element_resolve(1))
                   container = object_element_address;
               else if (!strcmp(word[1], "noun1"))
                   container = &noun[0];
               else if (!strcmp(word[1], "noun2"))
                   container = &noun[1];
               else if (!strcmp(word[1], "noun3"))
                   container = &noun[2];
               else if (!strcmp(word[1], "noun4"))
                   container = &noun[3];
               else
                   unkvarrun(1);
               contents = value_of(3);
               if (!strcmp(word[2], "+"))
                   *container += contents;
               else if (!strcmp(word[2], "-"))
                   *container -= contents;
               else if (!strcmp(word[2], "*"))
                   *container = *container * contents;
               else if (!strcmp(word[2], "/"))
                   *container = *container / contents;
               else if (!strcmp(word[2], "="))
                   *container = contents;
               else if (!strcmp(word[2], "parentof")) {
                   if (object[contents]->location > MAX_OBJECTS)
                       *container = object[contents]->location - MAX_OBJECTS;
                   else
                       *container = 0;
               } else
                   printf("ERROR: Illegal operator \"%s\".", word[2]);
           } else if (!strcmp(word[0], "ensure")) {
               if (word[3] == NULL)
                   noproprun();
               else {
                   if (bit_mask = attribute_resolve(3)) {
                       if (index = object_resolve(1)) {
                           if (!strcmp(word[2], "has")) {
                               object[index]->attributes = object[index]->attributes | bit_mask;
                           } else if (!strcmp(word[2], "hasnt")) {
                               bit_mask = ~bit_mask;
                               object[index]->attributes = object[index]->attributes & bit_mask;
                           }
                       } else if (index = location_resolve(1)) {
                           if (!strcmp(word[2], "has")) {
                               location[index]->attributes = location[index]->attributes | bit_mask;
                           } else if (!strcmp(word[2], "hasnt")) {
                               bit_mask = ~bit_mask;
                               location[index]->attributes = location[index]->attributes & bit_mask;
                           }
                       }
                   } else {
                       printf("ERROR: Unknown attribute \"%s\".\n", word[3]);
                   }
               }
           } else if (!strcmp(word[0], "move")) {
               if (word[3] == NULL)
                   noproprun();
               else if (!strcmp(word[1], "player")) {
                   push_stack(ftell(file), funcname);
                   if (!strcmp(word[3], "north")) {
                       destination = location[HERE]->north;
                       go_pointer = location[HERE]->go_north;
                       no_pointer = location[HERE]->no_north;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "northeast")) {
                       destination = location[HERE]->northeast;
                       go_pointer = location[HERE]->go_northeast;
                       no_pointer = location[HERE]->no_northeast;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "northwest")) {
                       destination = location[HERE]->northwest;
                       go_pointer = location[HERE]->go_northwest;
                       no_pointer = location[HERE]->no_northwest;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "south")) {
                       destination = location[HERE]->south;
                       go_pointer = location[HERE]->go_south;
                       no_pointer = location[HERE]->no_south;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "southeast")) {
                       destination = location[HERE]->southeast;
                       go_pointer = location[HERE]->go_southeast;
                       no_pointer = location[HERE]->no_southeast;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "southwest")) {
                       destination = location[HERE]->southwest;
                       go_pointer = location[HERE]->go_southwest;
                       no_pointer = location[HERE]->no_southwest;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "east")) {
                       destination = location[HERE]->east;
                       go_pointer = location[HERE]->go_east;
                       no_pointer = location[HERE]->no_east;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "west")) {
                       destination = location[HERE]->west;
                       go_pointer = location[HERE]->go_west;
                       no_pointer = location[HERE]->no_west;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "up")) {
                       destination = location[HERE]->up;
                       go_pointer = location[HERE]->go_up;
                       no_pointer = location[HERE]->no_up;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "down")) {
                       destination = location[HERE]->down;
                       go_pointer = location[HERE]->go_down;
                       no_pointer = location[HERE]->no_down;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "in")) {
                       destination = location[HERE]->in;
                       go_pointer = location[HERE]->go_in;
                       no_pointer = location[HERE]->no_in;
                       move_player(go_pointer, no_pointer);
                   } else if (!strcmp(word[3], "out")) {
                       destination = location[HERE]->out;
                       go_pointer = location[HERE]->go_out;
                       no_pointer = location[HERE]->no_out;
                       move_player(go_pointer, no_pointer);
                   } else {
                       unklocrun(3);
                   }
                   pop_stack();
               } else if (index = object_resolve(1)) {
                   from = object[index]->location;
                   if (from > MAX_OBJECTS && from != LIMBO) {
                       object[from - MAX_OBJECTS]->info += object[index]->mass;
                   }
                   if (to = location_resolve(3))
                       object[index]->location = to;
                   else if (location_element_resolve(3))
                       object[index]->location = location_element_contents;
                   else if (to = object_resolve(3)) {
                       object[index]->location = to + MAX_OBJECTS;
                       object[to]->info -= object[index]->mass;
                   } else if (!strcmp(word[3], "limbo"))
                       object[index]->location = LIMBO;
                   else
                       unklocrun(3);
               } else
                   unkobjrun(1);
           } else if (!strcmp(word[0], "if")) {
               current_level++;
               if (word[3] == NULL)
                   noproprun(0);
               else if (condition())
                   execution_level++;
           } else
               printf("ERROR: In function \"%s\", unknown command \"%s\".\n", funcname, word[0]);
       } else if (!strcmp(word[wp], "if"))
           current_level++;
       fgets(text_buffer, 160, file);
   };
   return (TRUE);
}

pop_stack()
{
   stack--;
   strcpy(current_function, backup[stack].name);
   current_level = backup[stack].current_level;
   execution_level = backup[stack].execution_level;
   fseek(file, backup[stack].address, SEEK_SET);
}

push_stack(file_pointer)
long file_pointer;
{

   if (stack == STACK_SIZE) {
       puts("ERROR: Stack overflow.");
       terminate(1);
   } else {
       strcpy(backup[stack].name, current_function);
       backup[stack].address = file_pointer;
       backup[stack].current_level = current_level;
       backup[stack].execution_level = execution_level;
   }
   stack++;
}

newline()
{
   printf("\n");
   scroll();
}

write_right(string)
char string[];
{
   int index;

   if (strlen(string) > screen_width)
       puts(string);
   else {
       index = (screen_width - strlen(string));
       index -= column;
       if (index < 0)
           index = 0;
       for (; index != 0; index--)
           printf(" ");
       printf("%s", string);
   }
}

write_centred(string)
char string[];
{
   int index;

   if (strlen(string) > screen_width)
       puts(string);
   else {
       index = ((screen_width - strlen(string)) / 2);
       for (; index != 0; index--)
           printf(" ");
       printf("%s", string);
   }
}

write_text(tout_buffer)
char tout_buffer[];
{
   char chunk_buffer[512];
   int counter = 0;
   int index;

   for (index = 0; index < strlen(tout_buffer); index++) {
       if (tout_buffer[index] != '^') {
           chunk_buffer[counter] = tout_buffer[index];
           counter++;
       } else {
           chunk_buffer[counter] = 0;
           if (chunk_buffer[0] != 0)
               output_text(chunk_buffer);
           newline();
           counter = 0;
       }
   }
   chunk_buffer[counter] = 0;
   output_text(chunk_buffer);
}

output_text(string)
char string[];
{
   int index;
   int done = FALSE;
   char text_buffer[512];
   char temporary_buffer[512];
   char *pointer;
   strcpy(text_buffer, string);

   if (strlen(text_buffer) + column < screen_width) {
       if (text_buffer[0] == 32 && column == 0) {
           pointer = &text_buffer[1];
           printj(pointer);
       } else
           printj(text_buffer);
   } else {
       for (index = screen_width - column; index > 0; index--) {
           if (text_buffer[index] == 32) {
               done = TRUE;
               text_buffer[index] = 0;
               if (text_buffer[0] == 32 && column == 0) {
                   pointer = &text_buffer[1];
                   printj(pointer);
               } else
                   printj(text_buffer);
               newline();
               index++;
               pointer = &text_buffer[index];
               write_text(pointer);
               break;
           }
       }
       if (done == FALSE) {
           if (column == 0) {
               strncpy(temporary_buffer, text_buffer, screen_width);
               printj(temporary_buffer);
               newline();
               pointer = &text_buffer[screen_width];
               write_text(pointer);
           } else {
               newline();
               write_text(text_buffer);
           }
       }
   }
}

printj(output_string)
char output_string[];
{
   int index;

   for (index = 0; output_string[index] != 0; index++) {
       if (output_string[index] == '~') {
           printf("\"");
           column++;
       } else {
           printf("%c", output_string[index]);
           column++;
       }
   }
}

scroll()
{
   column = 0;
   row++;

   if (row == screen_depth - 2)
       more();
}

condition()
{
   long compare = 0;
   long index = 0;
   int type = 0;

   struct variable_type *resolved_variable;

   if (word[3] == NULL) {
       noproprun();
       return (FALSE);
   } else
       compare = value_of(3);

   if (index = object_resolve(1))
       type = 1;
   else if (index = location_resolve(1))
       type = 2;
   else if ((resolved_variable = variable_resolve(1)) != NULL)
       index = resolved_variable->value;
   else if (object_element_resolve(1))
       index = oec;
   else if (location_element_resolve(1))
       index = location_element_contents;

   if (!strcmp(word[2], "=")) {
       if (index == compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], ">")) {
       if (index > compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "<")) {
       if (index < compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "is")) {
       return (scope(index, word[3]));
   } else if (!strcmp(word[2], "isnt")) {
       return (!scope(index, word[3]));
   } else if (!strcmp(word[2], "has")) {
       if (type == 1)
           index = object[index]->attributes;
       else if (type == 2)
           index = location[index]->attributes;
       if (index & compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "hasnt")) {
       if (type == 1)
           index = object[index]->attributes;
       else if (type == 2)
           index = location[index]->attributes;
       if (index & compare)
           return (FALSE);
       else
           return (TRUE);
   } else if (!strcmp(word[2], "!=")) {
       if (index != compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], ">=") || !strcmp(word[2], "=>")) {
       if (index >= compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "<=") || !strcmp(word[2], "=<")) {
       if (index <= compare)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "childof")) {
       if (object[index]->location == compare + MAX_OBJECTS)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "!childof")) {
       if (object[index]->location != compare + MAX_OBJECTS)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "grandof")) {
       find_parent(compare);
       if (parent == index)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "!grandof")) {
       find_parent(compare);
       if (parent != index)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "parentof")) {
       if (object[index]->location == compare - MAX_OBJECTS)
           return (TRUE);
       else
           return (FALSE);
   } else if (!strcmp(word[2], "!parentof")) {
       if (object[index]->location != compare - MAX_OBJECTS)
           return (TRUE);
       else
           return (FALSE);
   } else {
       printf("ERROR: In function \"%s\", illegal operand \"%s\".\n", function_name, word[2]);
       return (FALSE);
   }
}

long value_of(index)
int index;
{
   long compare = 0;

   struct variable_type *resolved_variable;

   if (!strcmp(word[index], "objects"))
       return (MAX_OBJECTS);
   else if (!strcmp(word[index], "limbo"))
       return (LIMBO);
   else if (!strcmp(word[index], "scenery"))
       return (SCENERY);
   else if (!strcmp(word[index], "heavy"))
       return (HEAVY);
   else if (!strcmp(word[index], "true"))
       return (TRUE);
   else if (!strcmp(word[index], "false"))
       return (FALSE);
   else if (!strcmp(word[index], "nowhere"))
       return (FALSE);
   else if ((resolved_variable = variable_resolve(index)) != NULL)
       return (resolved_variable->value);
   else if (object_element_resolve(index))
       return (oec);
   else if (location_element_resolve(index))
       return (location_element_contents);
   else if (compare = attribute_resolve(index))
       return (compare);
   else if (compare = object_resolve(index))
       return (compare);
   else if (compare = location_resolve(index))
       return (compare);
   else
       return (atoi(word[index]));
}

struct variable_type *
variable_resolve(wordno)
int wordno;
{
   struct variable_type *pointer = variable_table;

   do {
       if (!strcmp(word[wordno], pointer->name))
           return (pointer);
       else
           pointer = pointer->next_variable;
   }
   while (pointer != NULL);

   return (NULL);
}

struct function_type *
function_resolve(name)
char *name;
{
   struct function_type *pointer = function_table;

   if (function_table == NULL)
       return (NULL);

   do {
       if (!strcmp(name, pointer->name))
           return (pointer);
       else
           pointer = pointer->next_function;
   }
   while (pointer != NULL);

   return (NULL);
}

object_element_resolve(wordno)
int wordno;
{
   int index;
   int delimiter = 0;
   int match = 0;

   char wordtemp[161];
   strcpy(wordtemp, word[wordno]);

   if (!strncmp(wordtemp, "noun1(", 6)) {
       if (noun[0] != FALSE) {
           index = noun[0];
           delimiter = 5;
           match = TRUE;
       }
   } else if (!strncmp(wordtemp, "noun2(", 6)) {
       index = noun[1];
       delimiter = 5;
       match = TRUE;
   } else if (!strncmp(wordtemp, "noun3(", 6)) {
       index = noun[2];
       delimiter = 5;
       match = TRUE;
   } else if (!strncmp(wordtemp, "noun4(", 6)) {
       index = noun[3];
       delimiter = 5;
       match = TRUE;
   } else if (!strncmp(wordtemp, "player(", 7)) {
       index = CURRENT_PLAYER->value;
       delimiter = 6;
       match = TRUE;
   } else {
       for (index = 0; index < strlen(wordtemp); index++) {
           if (wordtemp[index] == 40) {
               delimiter = index;
               wordtemp[index] = 0;
               break;
           }
       }

       if (delimiter) {
           for (index = 1; index <= objects; index++) {
               if (!strcmp(wordtemp, object[index]->label)) {
                   match = TRUE;
                   break;
               }
           }
       }
   }

   if (match) {
       delimiter++;
       if (!strcmp(&wordtemp[delimiter], "mass)")) {
           oec = object[index]->mass;
           object_element_address = &object[index]->mass;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "info)")) {
           oec = object[index]->info;
           object_element_address = &object[index]->info;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "location)")) {
           object_element_address = &object[index]->location;
           oec = object[index]->location;
           return (TRUE);
       }
   }
   return (FALSE);
}

location_element_resolve(wordno)
int wordno;
{
   int index;
   int delimiter = 0;
   int match = 0;

   char wordtemp[161];
   strcpy(wordtemp, word[wordno]);

   if (!strncmp(wordtemp, "here(", 5)) {
       index = HERE;
       delimiter = 4;
       match = TRUE;
   } else {
       for (index = 0; index < strlen(wordtemp); index++) {
           if (wordtemp[index] == 40) {
               delimiter = index;
               wordtemp[index] = 0;
               break;
           }
       }

       if (delimiter) {
           for (index = 1; index <= locations; index++) {
               if (!strcmp(wordtemp, location[index]->label)) {
                   match = TRUE;
                   break;
               }
           }
       }
   }

   if (match) {
       delimiter++;
       if (!strcmp(&wordtemp[delimiter], "north)")) {
           location_element_contents = location[index]->north;
           location_element_address = &location[index]->north;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "northeast)")) {
           location_element_contents = location[index]->northeast;
           location_element_address = &location[index]->northeast;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "northwest)")) {
           location_element_contents = location[index]->northwest;
           location_element_address = &location[index]->northwest;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "south)")) {
           location_element_contents = location[index]->south;
           location_element_address = &location[index]->south;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "southeast)")) {
           location_element_contents = location[index]->southeast;
           location_element_address = &location[index]->southeast;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "southwest)")) {
           location_element_contents = location[index]->southwest;
           location_element_address = &location[index]->southwest;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "east)")) {
           location_element_contents = location[index]->east;
           location_element_address = &location[index]->east;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "west)")) {
           location_element_contents = location[index]->west;
           location_element_address = &location[index]->west;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "up)")) {
           location_element_contents = location[index]->up;
           location_element_address = &location[index]->up;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "down)")) {
           location_element_contents = location[index]->down;
           location_element_address = &location[index]->down;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "in)")) {
           location_element_contents = location[index]->in;
           location_element_address = &location[index]->in;
           return (TRUE);
       } else if (!strcmp(&wordtemp[delimiter], "out)")) {
           location_element_contents = location[index]->out;
           location_element_address = &location[index]->out;
           return (TRUE);
       }
   }
   return (FALSE);
}

object_resolve(wordno)
int wordno;
{
   int index;

   if (!strcmp(word[wordno], "noun1"))
       return (noun[0]);
   else if (!strcmp(word[wordno], "noun2"))
       return (noun[1]);
   else if (!strcmp(word[wordno], "noun3"))
       return (noun[2]);
   else if (!strcmp(word[wordno], "noun4"))
       return (noun[3]);
   else if (!strcmp(word[wordno], "player"))
       return (CURRENT_PLAYER->value);
   else {
       for (index = 1; index <= objects; index++) {
           if (!strcmp(word[wordno], object[index]->label))
               return (index);
       }
   }

   return (FALSE);
}

location_resolve(wordno)
int wordno;
{
   int index;

   if (!strcmp(word[wordno], "here"))
       return (HERE);

   if (!strcmp(word[wordno], "destination"))
       return (destination);

   for (index = 1; index <= locations; index++) {
       if (!strcmp(word[wordno], location[index]->label))
           return (index);
   }

   return (FALSE);
}

restart_game()
{
   int index;

   struct variable_type *current_variable;
   struct variable_type *previous_variable;
   struct name_type *current_name;
   struct name_type *next_name;

   restarting = TRUE;

   for (index = 1; index <= objects; index++) {
       current_name = object[index]->first_name;
       while (current_name->next_name != NULL) {
           next_name = current_name->next_name;
           free(current_name);
           current_name = next_name;
       }
       free(current_name);
       free(object[index]);
   }
   for (index = 1; index <= locations; index++)
       free(location[index]);

   do {
       current_variable = variable_table;
       previous_variable = variable_table;
       while (current_variable->next_variable != NULL) {
           previous_variable = current_variable;
           current_variable = current_variable->next_variable;
       }
       free(current_variable);
       previous_variable->next_variable = NULL;
   }
   while (previous_variable != variable_table);

   free(variable_table);
   variable_table = NULL;

   read_gamefile();

   row = 0;

   strcpy(function_name, "{global_intro");
   execute(function_name);
}

read_gamefile()
{
   int index, reference, errors;
   int object_count, location_count;
   int last = 0;
   int line = 0;

   long start_of_file = 0;
   long current_file_position = 0;
   long bit_mask;

   struct filter_type *current_filter;
   struct filter_type *new_filter;
   struct synonym_type *current_synonym;
   struct synonym_type *new_synonym;
   struct function_type *current_function;
   struct variable_type *current_variable;
   struct name_type *current_name;

   char function_name[81];

   if ((variable_table = (struct variable_type *) malloc(sizeof(struct variable_type))) == NULL)
       outofmem();
   CURRENT_PLAYER = variable_table;

   for (index = 1; index < 6; index++) {
       if ((variable[index] = (struct variable_type *) malloc(sizeof(struct variable_type))) == NULL)
           outofmem();
       variable[index - 1]->next_variable = variable[index];
   }

   current_variable = DISPLAY_MODE;
   errors = FALSE;
   objects = 0;
   locations = 0;
   fseek(file, start_of_file, SEEK_SET);

   fgets(text_buffer, 160, file);
   line++;

   while (!feof(file)) {
       encapsulate();
       if (word[0] == NULL);
       else if (text_buffer[0] == 123 && restarting == FALSE) {
           while (word[wp] != NULL) {
               if (!strncmp(text_buffer, "{global_", 8)) {
                   strncpy(function_name, word[wp], 80);
                   function_name[80] = 0;
               } else if (last == 0) {
                   printf("ERROR: In line %d, non-global function before object or location.\n", line);
                   errors++;
               } else if (last == 1) {
                   if (word[wp][0] == 123)
                       function_name[0] = 0;
                   else
                       strcpy(function_name, "{");
                   strncat(function_name, word[wp], 39);
                   strcat(function_name, "_");
                   strcat(function_name, location[locations]->label);
               } else if (last == 2) {
                   if (word[wp][0] == 123)
                       function_name[0] = 0;
                   else
                       strcpy(function_name, "{");
                   strncat(function_name, word[wp], 59);
                   strcat(function_name, "_");
                   strcat(function_name, object[objects]->label);
               }
               if (function_table == NULL) {
                   if ((function_table = (struct function_type *) malloc(sizeof(struct function_type))) == NULL)
                       outofmem();
                   else {
                       current_function = function_table;
                       strcpy(current_function->name, function_name);
                       current_function->position = ftell(file);
                       current_function->next_function = NULL;
                   }
               } else {
                   if ((current_function->next_function = (struct function_type *) malloc(sizeof(struct function_type))) == NULL)
                       outofmem();
                   else {
                       current_function = current_function->next_function;
                       strcpy(current_function->name, function_name);
                       current_function->position = ftell(file);
                       current_function->next_function = NULL;
                   }
               }
               wp++;
           }
           while (!feof(file)) {
               fgets(text_buffer, 160, file);
               line++;
               if (text_buffer[0] == 125)
                   break;
           }
       } else {
           if (!strcmp(word[0], "grammar") && restarting == FALSE) {
               if (word[++wp] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   if (grammar_table == NULL) {
                       if ((grammar_table = (struct word_type *) malloc(sizeof(struct word_type))) == NULL)
                           outofmem();
                       else {
                           strncpy(grammar_table->word, word[wp], 20);
                           grammar_table->word[20] = 0;
                           grammar_table->next_sibling = NULL;
                           grammar_table->first_child = NULL;
                           build_grammar_table(grammar_table);
                       }
                   } else
                       build_grammar_table(grammar_table);
               }
           } else if (!strcmp(word[0], "location")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   locations++;
                   if (locations == MAX_OBJECTS) {
                       printf("ERROR: Maximum number of locations exceeded, can't continue.\n");
                   } else {
                       if ((location[locations] = (struct location_type *) malloc(sizeof(struct location_type))) == NULL)
                           outofmem();
                       strncpy(location[locations]->label, word[1], 20);
                       location[locations]->label[20] = 0;
                       last = 1;
                   }
               }
           } else if (!strcmp(word[0], "object")) {
               if (word[2] == NULL) {
                   noproperr(line);
                   terminate(1);
               } else {
                   objects++;
                   if (objects == MAX_OBJECTS) {
                       printf("ERROR: Maximum number of objects exceeded, can't continue.\n");
                       terminate(1);
                   } else {
                       if ((object[objects] = (struct object_type *) malloc(sizeof(struct object_type))) == NULL)
                           outofmem();
                       strncpy(object[objects]->label, word[1], 20);
                       object[objects]->label[20] = 0;
                       if (locations == 0)
                           object[objects]->location = 1;
                       else
                           object[objects]->location = locations;
                       last = 2;
                   }
               }
           } else if (!strcmp(word[0], "synonym") && restarting == FALSE) {
               if (word[++wp] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   if ((new_synonym = (struct synonym_type *) malloc(sizeof(struct synonym_type))) == NULL)
                       outofmem();
                   else {
                       if (synonym_table == NULL) {
                           synonym_table = new_synonym;
                       } else {
                           current_synonym->next_synonym = new_synonym;
                       }
                   }
                   current_synonym = new_synonym;
                   strncpy(current_synonym->original, word[wp], 20);
                   current_synonym->original[20] = 0;
                   if (word[++wp] == NULL) {
                       noproperr(line);
                       errors++;
                   } else {
                       strncpy(current_synonym->standard, word[wp], 20);
                       current_synonym->standard[20] = 0;
                   }
                   current_synonym->next_synonym = NULL;
               }
           } else if (!strcmp(word[0], "filter") && restarting == FALSE) {
               if (word[++wp] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   if ((new_filter = (struct filter_type *) malloc(sizeof(struct filter_type))) == NULL)
                       outofmem();
                   else {
                       if (filter_table == NULL) {
                           filter_table = new_filter;
                       } else {
                           current_filter->next_filter = new_filter;
                       }
                       current_filter = new_filter;
                       strncpy(current_filter->word, word[wp], 20);
                       current_filter->word[20] = 0;
                       current_filter->next_filter = NULL;

                   }
               }
           } else if (!strcmp(word[0], "variable")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   if ((current_variable->next_variable = (struct variable_type *) malloc(sizeof(struct variable_type))) == NULL)
                       outofmem();
                   else {
                       current_variable = current_variable->next_variable;
                       strncpy(current_variable->name, word[1], 20);
                       current_variable->name[20] = 0;
                       current_variable->next_variable = NULL;
                   }
                   if (word[2] != NULL) {
                       if (!strcmp(word[2], "true"))
                           current_variable->value = TRUE;
                       else if (!strcmp(word[2], "false"))
                           current_variable->value = FALSE;
                       else
                           current_variable->value = atoi(word[2]);
                   } else
                       current_variable->value = FALSE;
               }
           }
       }
       fgets(text_buffer, 160, file);
       line++;
   }

   setdefaults();

   fseek(file, start_of_file, SEEK_SET);

   object_count = 0;
   location_count = 0;
   line = 0;

   fgets(text_buffer, 160, file);
   line++;

   while (!feof(file)) {
       if (text_buffer[0] == 123) {
           while (!feof(file)) {
               fgets(text_buffer, 160, file);
               line++;
               if (text_buffer[0] == 125)
                   break;
           }
       } else {
           encapsulate();
           if (word[0] == NULL);
           else if (!strcmp(word[0], "variable"));
           else if (!strcmp(word[0], "synonym"));
           else if (!strcmp(word[0], "grammar"));
           else if (!strcmp(word[0], "filter"));
           else if (!strcmp(word[0], "has")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   for (index = 1; word[index] != NULL; index++) {
                       if (bit_mask = attribute_resolve(index)) {
                           if (last == 0);
                           else if (last == 1)
                               location[location_count]->attributes += bit_mask;
                           else if (last == 2)
                               object[object_count]->attributes += bit_mask;
                       } else {
                           unkkeyerr(line, index);
                           errors++;
                       }
                   }
               }
           } else if (!strcmp(word[0], "location")) {
               location_count++;
               last = 1;
           } else if (!strcmp(word[0], "object")) {
               object_count++;
               last = 2;
               if ((object[object_count]->first_name = (struct name_type *) malloc(sizeof(struct name_type))) == NULL)
                   outofmem();
               else {
                   current_name = object[object_count]->first_name;
                   strncpy(current_name->name, word[2], 20);
                   current_name->name[20] = 0;
                   current_name->next_name = NULL;
               }
               wp = 3;
               while (word[wp] != NULL) {
                   if ((current_name->next_name = (struct name_type *) malloc(sizeof(struct name_type))) == NULL)
                       outofmem();
                   else {
                       current_name = current_name->next_name;
                       strncpy(current_name->name, word[wp], 20);
                       current_name->name[20] = 0;
                       current_name->next_name = NULL;
                   }
                   wp++;
               }
           } else if (!strcmp(word[0], "no_go")) {
               location[location_count]->no_go = current_file_position;
           } else if (!strcmp(word[0], "northeast")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->northeast = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_northeast")) {
               location[location_count]->no_northeast = current_file_position;
           } else if (!strcmp(word[0], "go_northeast")) {
               location[location_count]->go_northeast = current_file_position;
           } else if (!strcmp(word[0], "northwest")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->northwest = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_northwest")) {
               location[location_count]->no_northwest = current_file_position;
           } else if (!strcmp(word[0], "go_northwest")) {
               location[location_count]->go_northwest = current_file_position;
           } else if (!strcmp(word[0], "southeast")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->southeast = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_southeast")) {
               location[location_count]->no_southeast = current_file_position;
           } else if (!strcmp(word[0], "go_southeast")) {
               location[location_count]->go_southeast = current_file_position;
           } else if (!strcmp(word[0], "southwest")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->southwest = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_southwest")) {
               location[location_count]->no_southwest = current_file_position;
           } else if (!strcmp(word[0], "go_southwest")) {
               location[location_count]->go_southwest = current_file_position;
           } else if (!strcmp(word[0], "in")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->in = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_in")) {
               location[location_count]->no_in = current_file_position;
           } else if (!strcmp(word[0], "go_in")) {
               location[location_count]->go_in = current_file_position;
           } else if (!strcmp(word[0], "out")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->out = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_out")) {
               location[location_count]->no_out = current_file_position;
           } else if (!strcmp(word[0], "go_out")) {
               location[location_count]->go_out = current_file_position;
           } else if (!strcmp(word[0], "up")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->up = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_up")) {
               location[location_count]->no_up = current_file_position;
           } else if (!strcmp(word[0], "go_up")) {
               location[location_count]->go_up = current_file_position;
           } else if (!strcmp(word[0], "down")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->down = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_down")) {
               location[location_count]->no_down = current_file_position;
           } else if (!strcmp(word[0], "go_down")) {
               location[location_count]->go_down = current_file_position;
           } else if (!strcmp(word[0], "north")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->north = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_north")) {
               location[location_count]->no_north = current_file_position;
           } else if (!strcmp(word[0], "go_north")) {
               location[location_count]->go_north = current_file_position;
           } else if (!strcmp(word[0], "south")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->south = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_south")) {
               location[location_count]->no_south = current_file_position;
           } else if (!strcmp(word[0], "go_south")) {
               location[location_count]->go_south = current_file_position;
           } else if (!strcmp(word[0], "east")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->east = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_east")) {
               location[location_count]->no_east = current_file_position;
           } else if (!strcmp(word[0], "go_east")) {
               location[location_count]->go_east = current_file_position;
           } else if (!strcmp(word[0], "west")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "nowhere"));
               else {
                   if (reference = location_resolve(1)) {
                       location[location_count]->west = reference;
                   } else {
                       unklocerr(line, 1);
                       errors++;
                   }
               }
           } else if (!strcmp(word[0], "no_west")) {
               location[location_count]->no_west = current_file_position;
           } else if (!strcmp(word[0], "go_west")) {
               location[location_count]->go_west = current_file_position;
           } else if (!strcmp(word[0], "inventory")) {
               if (word[2] == NULL) {
                   noproperr(line);
                   errors++;
               } else {
                   strncpy(object[object_count]->article, word[1], 10);
                   object[object_count]->article[10] = 0;
                   strncpy(object[object_count]->inventory, word[2], 40);
                   object[object_count]->inventory[40] = 0;
               }
           } else if (!strcmp(word[0], "described")) {
               strncpy(object[object_count]->described, word[1], 80);
               object[object_count]->described[80] = 0;
           } else if (!strcmp(word[0], "initial")) {
               strncpy(object[object_count]->initial, word[1], 80);
               object[object_count]->initial[80] = 0;
           } else if (!strcmp(word[0], "starts")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "childof")) {
                   if (word[2] == NULL) {
                       noproperr(line);
                       errors++;
                   } else if (reference = object_resolve(2)) {
                       object[object_count]->location = reference + MAX_OBJECTS;
                   } else {
                       unkobjerr(line, 2);
                       errors++;
                   }
               } else {
                   if (!strcmp(word[1], "limbo"))
                       object[object_count]->location = LIMBO;
                   else if (!strcmp(word[1], "here"))
                       object[object_count]->location = location_count;
                   else {
                       if (reference = location_resolve(1)) {
                           object[object_count]->location = reference;
                       } else {
                           unklocerr(line, 1);
                           errors++;
                       }
                   }
               }
           } else if (!strcmp(word[0], "quantity")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else
                   object[object_count]->info = atoi(word[1]);
           } else if (!strcmp(word[0], "mass")) {
               if (word[1] == NULL) {
                   noproperr(line);
                   errors++;
               } else if (!strcmp(word[1], "scenery"))
                   object[object_count]->mass = SCENERY;
               else if (!strcmp(word[1], "heavy"))
                   object[object_count]->mass = HEAVY;
               else
                   object[object_count]->mass = atoi(word[1]);
           } else {
               unkkeyerr(line, 0);
               errors++;
           }
       }
       current_file_position = ftell(file);
       fgets(text_buffer, 160, file);
       line++;
   }

   if (errors) {
       newline();
       if (errors == 1)
           printf("1 error detected.\n");
       else
           printf("%d errors detected.\n", errors);
       terminate(1);
   }
}

build_grammar_table(pointer)
struct word_type *pointer;
{
   do {
       if (!strcmp(word[wp], pointer->word)) {
           if (pointer->first_child == NULL && word[wp + 1] != NULL) {
               if ((pointer->first_child = (struct word_type *) malloc(sizeof(struct word_type))) == NULL)
                   outofmem();
               else {
                   pointer = pointer->first_child;
                   strncpy(pointer->word, word[++wp], 20);
                   pointer->word[20] = 0;
                   pointer->next_sibling = NULL;
                   pointer->first_child = NULL;
               }
           } else {
               pointer = pointer->first_child;
               wp++;
           }
       } else {
           if (pointer->next_sibling == NULL) {
               if ((pointer->next_sibling = (struct word_type *) malloc(sizeof(struct word_type))) == NULL)
                   outofmem();
               else {
                   pointer = pointer->next_sibling;
                   strncpy(pointer->word, word[wp], 20);
                   pointer->word[20] = 0;
                   pointer->next_sibling = NULL;
                   pointer->first_child = NULL;
               }
           } else
               pointer = pointer->next_sibling;
       }
   }
   while (word[wp] != NULL);
}

unkfunrun(name)
char *name;
{
   printf("ERROR: Attempt to execute unknown function \"%s\".\n", name);
}

unkkeyerr(line, wordno)
int line;
int wordno;
{
   printf("ERROR: In line %d, unknown keyword \"%s\".\n", line, word[wordno]);
}

noproprun()
{
   printf("ERROR: In function \"%s\", \"%s\" command with insufficient arguments.\n", current_function, word[0]);
}

noproperr(line)
int line;
{
   printf("ERROR: In line %d, \"%s\" keyword with insufficient arguments.\n", line, word[0]);
}

unklocerr(line, wordno)
int line;
int wordno;
{
   printf("ERROR: In line %d, reference to undefined location \"%s\".\n", line, word[wordno]);
}

unklocrun(wordno)
int wordno;
{
   printf("ERROR: In function \"%s\", reference to undefined location \"%s\".\n", current_function, word[wordno]);
}

unkobjerr(line, wordno)
int line;
int wordno;
{
   printf("ERROR: In line %d, reference to undefined object \"%s\".\n", line, word[wordno]);
}

unkobjrun(wordno)
int wordno;
{
   printf("ERROR: In function \"%s\", reference to undefined object \"%s\".\n", current_function, word[wordno]);
}

unkvarrun(wordno)
int wordno;
{
   printf("ERROR: In function \"%s\", reference to undefined variable \"%s\".\n", current_function, word[wordno]);
}

outofmem()
{
   printf("ERROR: Out of memory, can't continue.\n");
   terminate(1);
}

long attribute_resolve(wordno)
int wordno;
{
   if (!strcmp(word[wordno], "VISITED"))
       return (1);
   else if (!strcmp(word[wordno], "DARK"))
       return (2);
   else if (!strcmp(word[wordno], "ON_WATER"))
       return (4);
   else if (!strcmp(word[wordno], "UNDER_WATER"))
       return (8);
   else if (!strcmp(word[wordno], "WITHOUT_AIR"))
       return (16);
   else if (!strcmp(word[wordno], "OUTDOORS"))
       return (32);
   else if (!strcmp(word[wordno], "MID_AIR"))
       return (64);
   else if (!strcmp(word[wordno], "TIGHT_ROPE"))
       return (128);
   else if (!strcmp(word[wordno], "POLLUTED"))
       return (256);
   else if (!strcmp(word[wordno], "SOLVED"))
       return (512);
   else if (!strcmp(word[wordno], "MID_WATER"))
       return (1024);
   else if (!strcmp(word[wordno], "CLOSED"))
       return (1);
   else if (!strcmp(word[wordno], "LOCKED"))
       return (2);
   else if (!strcmp(word[wordno], "DEAD"))
       return (4);
   else if (!strcmp(word[wordno], "EDIBLE"))
       return (8);
   else if (!strcmp(word[wordno], "WORN"))
       return (16);
   else if (!strcmp(word[wordno], "CONCEALING"))
       return (32);
   else if (!strcmp(word[wordno], "LUMINOUS"))
       return (64);
   else if (!strcmp(word[wordno], "WEARABLE"))
       return (128);
   else if (!strcmp(word[wordno], "CLOSABLE"))
       return (256);
   else if (!strcmp(word[wordno], "LOCKABLE"))
       return (512);
   else if (!strcmp(word[wordno], "ANIMATE"))
       return (1024);
   else if (!strcmp(word[wordno], "LIQUID"))
       return (2048);
   else if (!strcmp(word[wordno], "CONTAINER"))
       return (4096);
   else if (!strcmp(word[wordno], "SURFACE"))
       return (8192);
   else if (!strcmp(word[wordno], "PLURAL"))
       return (16384);
   else if (!strcmp(word[wordno], "FLAMMABLE"))
       return (32768);
   else if (!strcmp(word[wordno], "BURNING"))
       return (65536);
   else if (!strcmp(word[wordno], "SWITCHABLE"))
       return (131072);
   else if (!strcmp(word[wordno], "ON"))
       return (262144);
   else if (!strcmp(word[wordno], "DAMAGED"))
       return (524288);
   else if (!strcmp(word[wordno], "FEMALE"))
       return (1048576);
   else if (!strcmp(word[wordno], "POSSESSIVE"))
       return (2097152);
   else if (!strcmp(word[wordno], "OUT_OF_REACH"))
       return (4194304);
   else if (!strcmp(word[wordno], "TOUCHED"))
       return (8388608);
   else if (!strcmp(word[wordno], "SCORED"))
       return (16777216);
   else if (!strcmp(word[wordno], "SITTING"))
       return (33554432);
   else if (!strcmp(word[wordno], "CUSTOM1"))
       return (67108864);
   else if (!strcmp(word[wordno], "CUSTOM2"))
       return (134217728);
   else if (!strcmp(word[wordno], "CUSTOM3"))
       return (268435456);
   else if (!strcmp(word[wordno], "CUSTOM4"))
       return (536870912);
   else if (!strcmp(word[wordno], "CUSTOM5"))
       return (1073741824);
   else
       return (0);
}

setdefaults()
{
   int index;

   strcpy(CURRENT_PLAYER->name, "CURRENT_PLAYER");
   CURRENT_PLAYER->value = 0;
   strcpy(TOTAL_MOVES->name, "TOTAL_MOVES");
   TOTAL_MOVES->value = 0;
   strcpy(TURN_WORKED->name, "TURN_WORKED");
   TURN_WORKED->value = FALSE;
   strcpy(CURRENT_SCORE->name, "CURRENT_SCORE");
   CURRENT_SCORE->value = 0;
   strcpy(DISPLAY_MODE->name, "DISPLAY_MODE");
   DISPLAY_MODE->value = 0;
   strcpy(INTERNAL_VERSION->name, "INTERNAL_VERSION");
   INTERNAL_VERSION->value = VERSION;

   for (index = 1; index <= objects; index++) {
       strcpy(object[index]->described, object[index]->label);
       object[index]->initial[0] = 0;
       strcpy(object[index]->inventory, object[index]->label);
       object[index]->attributes = FALSE;
       object[index]->info = 0;
       object[index]->mass = SCENERY;
   }

   for (index = 1; index <= locations; index++) {
       location[index]->in = FALSE;
       location[index]->out = FALSE;
       location[index]->north = FALSE;
       location[index]->northeast = FALSE;
       location[index]->northwest = FALSE;
       location[index]->south = FALSE;
       location[index]->southeast = FALSE;
       location[index]->southwest = FALSE;
       location[index]->east = FALSE;
       location[index]->west = FALSE;
       location[index]->up = FALSE;
       location[index]->down = FALSE;
       location[index]->go_north = FALSE;
       location[index]->go_northeast = FALSE;
       location[index]->go_northwest = FALSE;
       location[index]->go_south = FALSE;
       location[index]->go_southeast = FALSE;
       location[index]->go_southwest = FALSE;
       location[index]->go_east = FALSE;
       location[index]->go_west = FALSE;
       location[index]->go_up = FALSE;
       location[index]->go_down = FALSE;
       location[index]->go_in = FALSE;
       location[index]->go_out = FALSE;
       location[index]->no_north = FALSE;
       location[index]->no_northeast = FALSE;
       location[index]->no_northwest = FALSE;
       location[index]->no_south = FALSE;
       location[index]->no_southeast = FALSE;
       location[index]->no_southwest = FALSE;
       location[index]->no_east = FALSE;
       location[index]->no_west = FALSE;
       location[index]->no_up = FALSE;
       location[index]->no_down = FALSE;
       location[index]->no_in = FALSE;
       location[index]->no_out = FALSE;
       location[index]->no_go = FALSE;
       location[index]->attributes = FALSE;
   }
}

no_it()
{
   char buffer[200];

   strcpy(buffer, "You must have referred to an appropriate noun previously to use the word \"");
   strcat(buffer, word[wp]);
   strcat(buffer, "\".");
   write_text(buffer);
   newline();
   custom_error = TRUE;
}

noun_resolve(pointer)
struct word_type *pointer;
{
   int word_found, index, counter;
   int matches = 0;
   int confidence[MAX_OBJECTS];
   int highest_confidence = 0;
   int prime_suspect = 0;
   int name_found = FALSE;
   int done = FALSE;
   int backup_pointer = wp;
   short int last_word = FALSE;

   struct word_type *options = pointer;
   struct word_type *terminator = options->first_child;
   struct name_type *current_name;

   char buffer[200];

   if (!strncmp("it", word[wp], significance)) {
       if (it)
           return (it);
       else {
           no_it();
           return (FALSE);
       }
   } else if (!strncmp("them", word[wp], significance)) {
       if (them)
           return (them);
       else {
           no_it();
           return (FALSE);
       }
   } else if (!strncmp("him", word[wp], significance)) {
       if (him)
           return (him);
       else {
           no_it();
           return (FALSE);
       }
   } else if (!strncmp("her", word[wp], significance)) {
       if (her)
           return (her);
       else {
           no_it();
           return (FALSE);
       }
   }
   if (!strncmp("itself", word[wp], significance) || !strncmp("themselves", word[wp], significance) || !strncmp("himself", word[wp], significance) || !strncmp("herself", word[wp], significance) || !strncmp("yourself", word[wp], significance)) {
       if (noun[0] == FALSE) {
           strcpy(buffer, "You must have referred to a noun previously in the same sentence to use the word \"");
           strcat(buffer, word[wp]);
           strcat(buffer, "\".");
           write_text(buffer);
           newline();
           custom_error = TRUE;
           return (FALSE);
       } else
           return (noun[0]);
   }
   for (index = 1; index <= objects; index++)
       confidence[index] = 1;

   while (word[wp] != NULL && last_word != TRUE) {
       if (punctuation[wp] != 0) {
           last_word = TRUE;
       } else if (options != NULL) {
           do {
               if (!strncmp(word[wp], options->word, significance)) {
                   if (!matches)
                       return (FALSE);
                   else {
                       done = TRUE;
                       break;
                   }
               }
           }
           while ((options = options->next_sibling) != NULL);
       }
       if (done == TRUE) {
           break;
       }
       for (index = 1; index <= objects; index++) {
           if (!confidence[index])
               continue;
           word_found = FALSE;
           current_name = object[index]->first_name;
           while (current_name != NULL) {
               if (!strncmp(word[wp], current_name->name, significance)) {
                   word_found = TRUE;
                   name_found = TRUE;
                   options = terminator;
                   break;
               }
               current_name = current_name->next_name;
           }
           if (word_found) {
               if (confidence[index] == 1)
                   matches++;
               if (confidence[index] != FALSE)
                   confidence[index]++;
           } else {
               if (confidence[index] > 1)
                   matches--;
               confidence[index] = FALSE;
           }
       }
       if (!name_found && !matches)
           break;
       wp++;
       name_found = FALSE;
   }

   wp--;

   if (matches == 0) {
       object_expected = TRUE;
       wp = backup_pointer;
       return (FALSE);
   }
   for (index = 1; index <= objects; index++) {
       if (confidence[index] != FALSE && strcmp(pointer->word, "*anywhere")) {
           if (scope(index, "*present") == FALSE) {
               matches--;
               confidence[index] = FALSE;
           }
       }
       if (confidence[index] != FALSE) {
           current_name = object[index]->first_name;
           counter = 0;
           while (current_name != NULL) {
               counter++;
               current_name = current_name->next_name;
           }
           confidence[index] = ((confidence[index] - 1) * 100) / counter;
           if (confidence[index] > highest_confidence)
               highest_confidence = confidence[index];
       }
   }
   if (matches > 1) {
       for (index = 1; index <= objects; index++) {
           if (confidence[index] != FALSE) {
               if (confidence[index] < highest_confidence) {
                   confidence[index] = FALSE;
                   matches--;
               }
           }
       }
   }
   for (index = 1; index <= objects; index++) {
       if (confidence[index] != FALSE) {
           if (scope(index, "*present") != FALSE)
               prime_suspect = index;
           if (scope(index, pointer->word) == FALSE) {
               matches--;
               confidence[index] = FALSE;
           }
       }
   }
   if (matches == 0) {
       if (prime_suspect != FALSE) {
           if (!(object[prime_suspect]->attributes & 1024))
               it = prime_suspect;
           if (object[prime_suspect]->attributes & 16384)
               them = prime_suspect;
           if (object[prime_suspect]->attributes & 1024 && object[prime_suspect]->attributes & 1048576)
               her = prime_suspect;
           if (object[prime_suspect]->attributes & 1024 && !(object[prime_suspect]->attributes & 1048576))
               him = prime_suspect;
           return (prime_suspect);
       } else {
           object_expected = TRUE;
           wp = backup_pointer;
           return (FALSE);
       }
   }
   if (matches == 1) {
       for (index = 1; index <= objects; index++) {
           if (confidence[index] != FALSE) {
               if (!(object[index]->attributes & 1024))
                   it = index;
               if (object[index]->attributes & 16384)
                   them = index;
               if (object[index]->attributes & 1024 && object[index]->attributes & 1048576)
                   her = index;
               if (object[index]->attributes & 1024 && !(object[index]->attributes & 1048576))
                   him = index;
               return (index);
           }
       }
   }
   strcpy(write_buffer, "Are you referring to ");
   for (index = 1; index <= objects; index++) {
       if (confidence[index] != FALSE) {
           sentence_output(index, 0);
           matches--;
           if (matches == 1)
               strcat(write_buffer, " or ");
           else if (matches > 1)
               strcat(write_buffer, ", ");
       }
   }
   strcat(write_buffer, "?^");
   write_text(write_buffer);
   custom_error = TRUE;
   return (FALSE);
}


#ifndef POWERC

clrscrn()
{
   int y = screen_depth;

   for (; y >= 0; y--)
       printf("\n");
}

more()
{
   write_centred("---[PRESS ENTER]---");

   getchar();

   row = 0;
}

#else

more()
{
   int index;
   int y;

   y = cursrow();

   write_centred("---[MORE]---");

   kbhit();
   getch();

   poscurs(y, 0);
   for (index = 0; index < screen_width; index++)
       printf(" ");
   poscurs(y, 0);

   row = 0;
}

#endif