#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
