aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/jacl/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/jacl/parser.cpp')
-rw-r--r--engines/glk/jacl/parser.cpp1918
1 files changed, 1918 insertions, 0 deletions
diff --git a/engines/glk/jacl/parser.cpp b/engines/glk/jacl/parser.cpp
new file mode 100644
index 0000000000..b9787c8ac7
--- /dev/null
+++ b/engines/glk/jacl/parser.cpp
@@ -0,0 +1,1918 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/jacl/jacl.h"
+#include "glk/jacl/language.h"
+#include "glk/jacl/types.h"
+#include "glk/jacl/prototypes.h"
+
+namespace Glk {
+namespace JACL {
+
+#define FIRST_LIST noun_number-2
+#define SECOND_LIST noun_number
+
+int object_list[4][MAX_OBJECTS];
+int multiple_resolved[MAX_OBJECTS];
+
+// THIS IS THE NUMBER OF OBJECTS LEFT IN THE LIST
+int list_size[4];
+
+// THIS IS THE INDEX OF THE FINAL OBJECT
+int max_size[4];
+
+/* object_list[] HAS THE FOLLOWING INDEXES:
+ * noun1 : 0
+ * noun2 : 1
+ * noun1 EXCEPTIONS : 2
+ * noun2 EXCEPTIONS : 3
+ */
+
+int selection;
+
+int matches = 0;
+int highest_confidence = 0;
+int prime_suspect = 0;
+int done = 0;
+int backup_pointer = 0;
+int everything = 0;
+
+int confidence[MAX_OBJECTS];
+int possible_objects[MAX_OBJECTS];
+
+int it;
+int them[MAX_OBJECTS];
+int her;
+int him;
+int parent;
+
+int custom_error;
+
+int oops_word;
+int last_exact;
+
+char *expected_scope[3];
+
+// THIS ARRAY DEFINES THE OBJECTS THAT THE CURRENT OBJECTS ARE
+// SUPPOSED TO BE A CHILD OF
+int from_objects[MAX_OBJECTS];
+
+int after_from;
+char *from_word;
+
+int object_expected = FALSE;
+
+char default_function[84];
+char object_name[84];
+
+char base_function[84];
+char before_function[84];
+char after_function[84];
+char local_after_function[84];
+
+extern char text_buffer[];
+extern char function_name[];
+extern char temp_buffer[];
+extern char error_buffer[];
+extern char override[];
+extern char *word[];
+
+extern int quoted[];
+
+extern struct object_type *object[];
+extern int objects;
+
+extern int noun[];
+extern int wp;
+extern int player;
+
+extern struct word_type *grammar_table;
+extern struct function_type *executing_function;
+extern struct object_type *object[];
+extern struct variable_type *variable[];
+
+void parser() {
+ // THIS FUNCTION COMPARES THE WORDS IN THE PLAYER'S COMMAND TO THE
+ // GRAMMAR TREE OF POSSIBLE COMMANDS
+
+ struct word_type *pointer;
+ struct word_type *matched_word = NULL;
+
+ int index;
+ int current_noun = 0;
+
+ // RESET TO START OF PROCESSING
+
+ ////printf("--- in parser\n");
+
+ // THIS IS USED TO STORE THE LAST EXACT WORD MATCH IN THE PLAYERS COMMAND
+ last_exact = -1;
+ after_from = -1;
+ from_objects[0] = 0;
+
+ noun[0] = FALSE;
+ noun[1] = FALSE;
+
+ // RESET BOTH THE LISTS TO BE EMPTY
+ //printf("--- reset lists\n");
+ for (index = 0; index < 4; index++) {
+ list_size[index] = 0;
+ max_size[index] = 0;
+ }
+
+ //printf("--- clear $integer\n");
+ clear_cinteger("$integer");
+ //printf("--- clear $string\n");
+ clear_cstring("$string");
+ //printf("--- clear action\n");
+ clear_cstring("action");
+
+ if (grammar_table == NULL) {
+ // THERE ARE NO USER DEFINED COMMANDS AVAILABLE, SO THE USER'S
+ // COMMAND IS INEVITABLY INVALID
+ //printf("--- no grammar table\n");
+ INTERRUPTED->value = TRUE;
+ diagnose();
+ return;
+ }
+
+ //printf("--- set pointer to grammar table\n");
+ // START AT THE TOP OF THE GRAMMAR TREE
+ pointer = grammar_table;
+
+ while (word[wp] != NULL && pointer != NULL) {
+ //printf("--- wp = %d\n", wp);
+ //printf("--- word[%d] = %s\n", wp, word[wp]);
+ object_expected = FALSE;
+
+ if (!strcmp(cstring_resolve("THEN_WORD")->value, word[wp])) {
+ // CONSIDER THIS THE END OF THIS COMMAND AS 'THEN' IS
+ // TREATED LIKE A FULL STOP
+ break;
+ } else if ((matched_word = exact_match(pointer)) != NULL) {
+ // THIS WORD WAS AN EXACT MATCH FOR ONE OF THE POSSIBLE WORDS
+ // AT THE CURRENT GRAMMAR TREE LEVEL - MOVE ON!
+ pointer = matched_word;
+ pointer = pointer->first_child;
+ } else if ((matched_word = object_match(pointer, current_noun)) != NULL) {
+ // THIS WAS AN OBJECT PLACE HOLDER AT THIS GRAMMAR LEVEL AND
+ // THIS POINT IN THE PLAYER'S COMMAND COULD BE RESOLVED TO
+ // AT LEAST ONE OBJECT
+
+ if (list_size[current_noun] > 1) {
+ // MULTIPLE OBJECTS WERE RETURNED
+ if (matched_word->word[1] != '*') {
+ if (last_exact == -1) {
+ write_text(cstring_resolve("NO_MULTI_START")->value);
+ } else {
+ sprintf(error_buffer, cstring_resolve("NO_MULTI_VERB")->value, word[last_exact]);
+ write_text(error_buffer);
+ }
+
+ INTERRUPTED->value = TRUE;
+ return;
+ }
+ }
+
+ // STORE THE EXPECTED SCOPE FOR LATER CHECKING
+ expected_scope[current_noun] = (char *) &matched_word->word;
+ //printf("--- expected scope for noun%d is %s\n", current_noun, expected_scope[current_noun]);
+
+ // THE NEXT MATCH OR GROUP OF MATCHES SHOULD BE IN THE SECOND
+ // LIST OF OBJECTS
+ current_noun++;
+
+ // PUSH ON FROM THE POINT A MATCH WAS FOUND....
+ pointer = matched_word;
+ pointer = pointer->first_child;
+ } else {
+ // THIS IS AN UNKNOWN WORD
+ if (oops_word == -1 && word[wp] != NULL) {
+ oops_word = wp;
+ }
+
+ if (custom_error == TRUE) {
+ // THERE HAS BEEN SOME CUSTOM ERROR DISPLAYED ALREADY
+ // SO JUST RETURN
+ TIME->value = FALSE;
+ INTERRUPTED->value = TRUE;
+ return;
+ } else {
+ // THERE ARE NO MORE POSIBILITIES, THE WORD CAN'T BE
+ // USED IN THIS CONTEXT
+ INTERRUPTED->value = TRUE;
+ diagnose();
+ return;
+ }
+ }
+ };
+
+ if (pointer == NULL) {
+ // THIS CAN ONLY HAPPEN IF MOVING THE POINTER TO ITS
+ // FIRST CHILD RESULTS IN A NULL - AN INCOMPLETE
+ // GRAMMAR STATEMENT.
+ log_error(INCOMPLETE_GRAMMAR, PLUS_STDOUT);
+ INTERRUPTED->value = TRUE;
+ return;
+ }
+
+ //printf("--- first list has %d objects\n", list_size[0]);
+ //printf("--- first list has %d max\n", max_size[0]);
+ //printf("--- second list has %d objects\n", list_size[1]);
+ //printf("--- second list has %d max\n", max_size[1]);
+
+ /* THE PLAYER'S MOVE HAS NO MORE WORDS, AND A BRANCH OF
+ * THE GRAMMAR TREE HAS BEEN FOUND THAT SO FAR MATCHES
+ * THIS MOVE. SCAN THROUGH ALL THE CHILDREN OF THE FINAL
+ * WORD OF THE PLAYER'S COMMAND AND FIND A FUNCTION BASE.
+ * (FUNCTION BASES BEGIN WITH '>'). IF THIS IS FOUND,
+ * CALL THE APPROPRIATE JACL FUNCTIONS IN THE GAME CODE
+ * IF NO FUNCTION BASE IS FOUND, MORE WORDS WERE EXPECTED
+ * IN ORDER TO CONSTRUCT A COMPLETE COMMAND. SHOW ERROR. */
+ do {
+ if (pointer->word[0] == '>') {
+ //printf("--- found action %s\n", pointer->word);
+ /* CALL ALL THE APPROPRIATE FUNCTIONS FOR EACH OF */
+ /* THE OBJECTS IN THE SET */
+ add_cstring("action", &pointer->word[1]);
+
+ if (list_size[0] > 1) {
+ /* FIRST IS MULTI, PRESUME SECOND IS SINGLE OR ZERO AS YOU
+ * CAN HAVE COMMANDS WITH TWO MULTIPLE OBJECT REFERENCES */
+ noun[1] = first_available(1);
+
+ for (index = 0; index < max_size[0]; index++) {
+ /* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
+ if (object_list[0][index] != 0) {
+ noun[0] = object_list[0][index];
+
+ if (MULTI_PREFIX->value) {
+ // DISPLAY THE OBJECT BEING ACTED ON
+ plain_output(noun[0], FALSE);
+ write_text(temp_buffer);
+ write_text(": ");
+ }
+
+ //printf("--- 1 calling functions for %s\n", object[noun[0]]->label);
+ call_functions(pointer->word);
+
+ /* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
+ * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
+ if (INTERRUPTED->value) {
+ break;
+ }
+ }
+ }
+ } else {
+ // FIRST LIST ISN'T MULTI
+ //printf("--- first list isn't multi\n");
+ if (list_size[1] > 1) {
+ //printf("--- second list is multi\n");
+ // ONLY SECOND IS MULTI
+ noun[0] = first_available(0);
+
+ for (index = 0; index < max_size[1]; index++) {
+ /* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */
+ if (object_list[1][index] != 0) {
+ noun[1] = object_list[1][index];
+
+ if (MULTI_PREFIX->value) {
+ // DISPLAY THE OBJECT BEING ACTED ON
+ plain_output(noun[0], FALSE);
+ write_text(temp_buffer);
+ write_text(": ");
+ }
+
+ //printf("--- 2 calling functions for %s\n", object[noun[1]]->label);
+ call_functions(pointer->word);
+
+ /* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T
+ * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */
+ if (INTERRUPTED->value) {
+ break;
+ }
+ }
+ }
+ } else {
+ //printf("--- second list isn't multi\n");
+ // NEITHER OBJECT REFERENCE IS MULTI
+ if (list_size[0] == 0) {
+ noun[0] = 0;
+ noun[1] = 0;
+
+ /* THIS IS AN OBJECT-LESS COMMAND */
+ call_functions(pointer->word);
+ } else {
+ noun[0] = first_available(0);
+ noun[1] = first_available(1);
+
+ //printf("--- 3 calling functions for %s and %s\n", object[noun[0]]->label, object[noun[1]]->label);
+ call_functions(pointer->word);
+ }
+ }
+ }
+ if (TIME->value == FALSE) {
+ /* IS THIS ENOUGH TO INDICATE THAT THE OTHER COMMANDS
+ * SHOULDN'T BE PERFORMED??? */
+ //INTERRUPTED->value = TRUE;
+ }
+ return;
+ } else {
+ //printf("--- move to next grammar sibling\n");
+ // MOVE THROUGH THE OPTIONS AT THIS LEVEL OF THE GRAMMAR TREE
+ // TO FIND THE ACTION THAT MATCHES THIS COMMAND
+ if (pointer->next_sibling == NULL) {
+ break;
+ } else {
+ pointer = pointer->next_sibling;
+ }
+ }
+ } while (TRUE);
+
+ /* THERE IS NO FUNCTION AS A CHILD OF THE LAST WORD OF THE PLAYER'S
+ * COMMAND SO DISPLAY AN IN-GAME ERROR TO THE PLAYER. THIS IS LIKELY
+ * TO BE AN INCOMPLETE COMMAND */
+ INTERRUPTED->value = TRUE;
+ diagnose();
+ return;
+}
+
+/* THIS FUNCTION RETURNS THE FIRST OBJECT IN THE SPECIFIED LIST
+ * OR 0 IF IT IS EMPTY. THE FIRST OBJECT IN THE LIST IS THE FIRST
+ * ELEMENT THAT IS NON-ZERO BEFORE THE max-size IS REACHED. */
+int first_available(int list_number) {
+ int index;
+
+ if (list_size[list_number] == 0) return (0);
+
+ //printf("--- looking for next object in list\n");
+ for (index = 0; index < max_size[list_number]; index++) {
+ if (object_list[list_number][index] != 0) {
+ //printf("--- returning object %s\n", object[object_list[list_number][index]]->label);
+ return (object_list[list_number][index]);
+ }
+ }
+
+ //printf("--- no objects left in list\n");
+ /* NO OBJECTS LEFT IN THE LIST */
+ return (0);
+}
+
+void call_functions(char *base_name) {
+ /* THIS FUNCTION CALLS ALL THE APPROPRIATE JACL FUNCTIONS TO RESPOND
+ * TO A PLAYER'S COMMAND GIVEN A BASE FUNCTION NAME AND THE CURRENT
+ * VALUE OF noun1 AND noun2 */
+
+ /* THE DEFAULT IS THAT THE COMMAND IS SUCCESSFUL AND THAT TIME SHOULD
+ * PASS. IF THE COMMAND FAILS, 'TIME' WILL BE SET TO FALSE */
+ TIME->value = TRUE;
+
+ strncpy(base_function, base_name + 1, 80);
+ strcat(base_function, "_");
+
+ strncpy(override, base_function, 80);
+
+ strcpy(before_function, "+before_");
+ strcat(before_function, base_name + 1);
+
+ strcpy(after_function, "+after_");
+ strcat(after_function, base_name + 1);
+
+ strcpy(local_after_function, "after_");
+ strcat(local_after_function, base_name + 1);
+ if (noun[1] != FALSE) {
+ strcat(local_after_function, "_");
+ strcat(local_after_function, object[noun[1]]->label);
+ }
+ if (noun[0] != FALSE) {
+ strcat(local_after_function, "_");
+ strcat(local_after_function, object[noun[0]]->label);
+ }
+
+ /* THIS IS CALLED IF AN 'override' COMMAND IS EXECUTED
+ * IN A LIBRARY FUNCTION BUT THE OBJECT-OR-LOCATION-SPECIFIC
+ * OVERRIDE DOES NOT EXIST. IT IS SET TO '+default_func' */
+ strcpy(default_function, "+default_");
+ strcat(default_function, base_name + 1);
+
+ /* EXECUTE THE GLOBAL *DEFAULT* BEFORE FUNCTION
+ * AND RETURN IF IT RETURNS TRUE */
+ if (execute("+before") != FALSE)
+ return;
+
+ /* EXECUTE THE VERB-SPECIFIC BEFORE
+ FUNCTION AND RETURN IF IT RETURNS TRUE */
+ if (execute(before_function) != FALSE)
+ return;
+
+ if (noun[0] == FALSE) { /* USER'S COMMAND HAS NO NOUNS */
+ strcat(base_function, object[HERE]->label);
+ /* EXECUTE THE FUNCTION 'func_here' */
+ if (execute(base_function) == FALSE) {
+ /* THIS LOCATION-SPECIFIC FUNCTION DOES NOT
+ * EXIST OR HAS ISSUED A 'break false' COMMAND.
+ * EXECUTE THE FUNCTION '+func'
+ * WITH THE POSSIBLILITY OF
+ * EXECUTING THE FUNCTION 'func_override_here'
+ * IF AN 'override' COMMAND IS FOUND IN '+func'
+ * IF THIS OVERRIDE FUNCTION ISN'T FOUND
+ * THE DEFAULT FUNCTION WILL BE EXECUTED */
+
+ /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
+ * IS NEEDED */
+ strcat(override, "override_");
+ strcat(override, object[HERE]->label);
+
+ /* CREATE THE FUNCTION NAME '+func' */
+ strcpy(base_function, "+");
+ strcat(base_function, base_name + 1);
+
+ /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
+ if (execute(base_function) == FALSE)
+ unkfunrun(base_function);
+ }
+ } else if (noun[1] == FALSE) { /* USER'S COMMAND HAS ONE NOUN */
+ strcat(base_function, object[noun[0]]->label);
+ /* EXECUTE THE FUNCTION 'func_noun1' */
+ if (execute(base_function) == FALSE) {
+ /* THIS OBJECT-SPECIFIC FUNCTION DOES NOT
+ * EXIST OR HAS ISSUED A 'break false' COMMAND.
+ * EXECUTE THE FUNCTION '+func'
+ * WITH THE POSSIBLILITY OF
+ * EXECUTING THE FUNCTION 'func_override_noun1'
+ * IF AN 'override' COMMAND IS FOUND IN '+func'
+ * IF THIS OVERRIDE FUNCTION ISN'T FOUND
+ * THE DEFAULT FUNCTION WILL BE EXECUTED */
+
+ /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
+ * IS NEEDED */
+ strcat(override, "override_");
+ strcat(override, object[noun[0]]->label);
+
+ /* CREATE THE FUNCTION NAME '+func' */
+ strcpy(base_function, "+");
+ strcat(base_function, base_name + 1);
+
+ /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
+ if (execute(base_function) == FALSE)
+ unkfunrun(base_function);
+ }
+ } else { /* USER'S COMMAND HAS TWO NOUNS */
+ strcat(base_function, object[noun[1]]->label);
+ strcat(base_function, "_");
+ strcat(base_function, object[noun[0]]->label);
+ /* EXECUTE THE FUNCTION 'func_noun2_noun1'
+ * IE give_to_fred THAT IS ASSOCIATED WITH
+ * THE OBJECT flint_stone */
+ if (execute(base_function) == FALSE) {
+ /* THIS OBJECTS-SPECIFIC FUNCTION DOES NOT
+ * EXIST OR HAS ISSUED A 'break false' COMMAND.
+ * EXECUTE THE FUNCTION '+func'
+ * WITH THE POSSIBLILITY OF
+ * EXECUTING THE FUNCTION 'func_override_noun2_noun1'
+ * IF AN 'override' COMMAND IS FOUND IN '+func'
+ * IF THIS OVERRIDE FUNCTION ISN'T FOUND
+ * THE DEFAULT FUNCTION WILL BE EXECUTED */
+
+ /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT
+ * IS NEEDED */
+ strcat(override, object[noun[1]]->label);
+ strcat(override, "_override_");
+ strcat(override, object[noun[0]]->label);
+
+ /* CREATE THE FUNCTION NAME '+func' */
+ strcpy(base_function, "+");
+ strcat(base_function, base_name + 1);
+
+ /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */
+ if (execute(base_function) == FALSE)
+ unkfunrun(base_function);
+ }
+ }
+
+ /* EXECUTE THE LOCAL AFTER FUNCTION
+ * AND RETURN IF IT RETURNS TRUE */
+ if (execute(local_after_function) != FALSE)
+ return;
+
+ /* EXECUTE THE VERB-SPECIFIC AFTER
+ * FUNCTION AND RETURN IF IT RETURNS TRUE */
+ if (execute(after_function) != FALSE)
+ return;
+
+ /* EXECUTE THE GLOBAL *DEFAULT* AFTER FUNCTION
+ * AND RETURN IF IT RETURNS TRUE */
+ if (execute("+after") != FALSE)
+ return;
+
+
+ //sprintf(temp_buffer, "TIME = %d", TIME->value);
+ //log_error(temp_buffer, PLUS_STDERR);
+
+ if (TIME->value) {
+ //printf("--- %s\n", base_function);
+ eachturn();
+ }
+
+ return;
+}
+
+struct word_type *object_match(struct word_type *iterator, int noun_number) {
+ /* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
+ * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY OBJECT PLACE HOLDERS */
+
+ /* STORE WHETHER BY THE END AN OBJECT PLACEHOLDER WAS ENCOUNTERED AND
+ * DISPLAY A MESSAGE IF NO OBJECTS MATCHED */
+ int object_was_option = FALSE;
+
+ do {
+ /* THIS LOOP MEANS THAT CERTAIN ERRORS SUCH AS TAKING FROM A
+ * CLOSED CONTAINER CAN OCCUR MORE THAN ONCE */
+ if ((iterator->word[0] == '*')) {
+ object_was_option = TRUE;
+ if (build_object_list(iterator, noun_number)) {
+ /* RETURN THE POINT IN THE GRAMMAR TREE THAT MATCHED TO
+ * CONTINUE ON FROM */
+ //printf("--- returned TRUE from build_object_list\n");
+ return (iterator);
+ }
+ }
+
+ if (custom_error == TRUE) {
+ /* AN ERROR OCCURED IN THE FIRST OBJECT PLACEHOLDER, DON'T
+ * TRY ANY OTHERS */
+ return (NULL);
+ }
+ } while ((iterator = iterator->next_sibling) != NULL);
+
+ /* THERE WERE NO OBJECT PLACE HOLDERS OR, IF THERE WERE, NO
+ * MATCHING OBJECTS COULD BE RESOLVED */
+ //printf("--- returning null from object_match\n");
+
+ if (object_was_option) {
+ /* NO OBJECT MATCHED, BUT AN OBJECT WAS VALID AT THIS POINT IN THE
+ * PLAYER'S COMMAND */
+ diagnose();
+ custom_error = TRUE;
+ }
+
+ return (NULL);
+}
+
+struct word_type *exact_match(struct word_type *pointer) {
+ /* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL
+ * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY EXACT MATCHES WITH THE
+ * CURRENT WORD IN THE PLAYER'S COMMAND.
+ * AN EXACT MATCH IS ANYTHING THAT ISN'T AN OBJECT PLACE HOLDER. */
+ struct word_type *iterator = pointer;
+
+ do {
+ if (iterator->word[0] == '*') {
+ /* THIS IS AN OBJECT PLACE HOLDER, THEREFORE IGNORE AND
+ * KEEP LOOKING FOR EXACT MATCHES */
+ } else if (!strcmp(iterator->word, "$string")) {
+ add_cstring("$string", word[wp]);
+ last_exact = wp;
+ wp++;
+ return (iterator);
+ } else if (!strcmp(iterator->word, "$integer") &&
+ validate(word[wp])) {
+ add_cinteger("$integer", atoi(word[wp]));
+ last_exact = wp;
+ wp++;
+ return (iterator);
+ } else if (!strcmp(word[wp], iterator->word)) {
+ last_exact = wp;
+ wp++;
+ return (iterator);
+ }
+ } while ((iterator = iterator->next_sibling) != NULL);
+
+ /* THERE WERE NO EXACT MATCHES, SO RETURN FALSE */
+ return (NULL);
+}
+
+int is_terminator(struct word_type *scope_word) {
+ struct word_type *terminator = scope_word->first_child;
+
+ if (terminator != NULL) {
+ /* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
+ * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
+ do {
+ /* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
+ * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
+ * OF THE OBJECT REFERENCE PART OF THE COMMAND */
+ if (!strcmp(word[wp], terminator->word)
+ || (!strcmp(terminator->word, "$integer")
+ && validate(word[wp]))) {
+ return (TRUE);
+ }
+ } while ((terminator = terminator->next_sibling) != NULL);
+ }
+
+ return (FALSE);
+}
+
+int build_object_list(struct word_type *scope_word, int noun_number) {
+ /* THIS FUNCTION BUILDS A LIST OF OBJECTS FROM THE PLAYER'S COMMAND
+ * AND RETURNS THE NUMBER OF OBJECTS IN THAT LIST */
+
+ int index, counter;
+ int resolved_object;
+ char *except_word;
+
+ //printf("--- entering build object list starting at %s with a scope_word of %s\n", word[wp], scope_word->word);
+ /* LOOK AHEAD FOR A FROM CLAUSE AND STORE from_object IF SO */
+ if (get_from_object(scope_word, noun_number) == FALSE) {
+ /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
+ * DISPLAYED */
+ //printf("--- from had an error\n");
+ return (FALSE);
+ }
+
+ while (word[wp] != NULL) {
+ /* LOOP THROUGH WORDS IN THE PLAYER'S INPUT ENDING WHEN EITHER
+ * THERE ARE NO MORE WORDS OR ONE OF THE CHILD NODES OF THE
+ * CURRENT scope_word NODE IS REACHED INDICATING THERE ARE NO
+ * MORE OBJECTS TO RESOLVE */
+
+ if (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value) ||
+ !strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value)) {
+ /* START ADDING ALL FUTURE RESOLVED OBJECTS TO A SECOND LIST
+ * TO REMOVE FROM THE FIRST */
+ except_word = word[wp];
+
+ wp++;
+
+ if (word[wp] != NULL && !strcmp(word[wp], cstring_resolve("FOR_WORD")->value)) {
+ /* SKIP PAST THE WORD 'FOR' */
+ wp++;
+ }
+
+ /* LOOK FORWARD FOR A FROM CLAUSE */
+ if (get_from_object(scope_word, noun_number) == FALSE) {
+ /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
+ * DISPLAYED */
+ return (FALSE);
+ }
+
+ /* MOVE TO THE SECOND LIST THAT WILL ULTIMATELY BE SUBTRACTED
+ * FROM THE FIRST LIST */
+ if (noun_number < 2) {
+ /* CREATE A 'them' SET THAT CAN BE REFERRED TO IN THE
+ * 'except' CLAUSE */
+ set_them(noun_number);
+ /* JUMP TO THE 'EXCEPT' LIST THAT CORRESPONDS TO THIS
+ * RESOLVED LIST */
+ noun_number = noun_number + 2;
+ } else {
+ sprintf(error_buffer, cstring_resolve("DOUBLE_EXCEPT")->value, except_word);
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);
+ }
+ } else if (after_from != -1 && !strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
+ /* SET THE WORD POINTER TO AFTER THE ALREADY-PROCESSED FROM
+ * CLAUSE (IF ONE EXISTED) AND CONTINUE */
+ wp = after_from;
+ //printf("--- hit from in processing moving on to word '%s'\n", word[wp]);
+
+ /* IF NO OBJECTS WERE MATCHED BY THE TIME WE HIT FROM THEN EITHER
+ * PRESUME THE PLAYER MEANT 'ALL FROM' OR PRINT AN ERROR */
+ if (list_size[noun_number] == 0) {
+ //printf("--- adding all due to empty list.\n");
+ add_all(scope_word, noun_number);
+ }
+
+ /* LOOK FOR THE NEXT FROM CLAUSE */
+ if (get_from_object(scope_word, noun_number) == FALSE) {
+ /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN
+ * DISPLAYED */
+ return (FALSE);
+ }
+ } else if (!strcmp("then", word[wp])) {
+ break;
+ } else if (is_terminator(scope_word)) {
+ /* THERE ARE NO MORE OBJECTS TO RESOLVE */
+
+ //printf("--- %s is a build list terminator\n", word[wp]);
+ break;
+ } else if (!strcmp(word[wp], "comma") ||
+ !strcmp(word[wp], cstring_resolve("AND_WORD")->value)) {
+ /* JUST MOVE ONTO THE NEXT WORD AND SEE WHAT COME NEXT */
+ wp++;
+ } else {
+ /* CALL noun_resolve TO FETCH THE FIRST MATCHING OBJECT */
+ /* FALSE INDICATES THAT WE ARE NOT LOOKING FOR A FROM OBJECT */
+ resolved_object = noun_resolve(scope_word, FALSE, noun_number);
+
+ if (resolved_object == -1) {
+ /* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
+ * BEING USED */
+ index = 0;
+
+ while (multiple_resolved[index] != 0) {
+ /* ADD ALL THE RESOLVED OBJECTS TO THE LIST */
+ add_to_list(noun_number, multiple_resolved[index]);
+ index++;
+ }
+ } else if (resolved_object) {
+ /* ADD IT TO THE LIST */
+ add_to_list(noun_number, resolved_object);
+ } else {
+ /* NO OBJECTS COULD BE RESOLVED, THIS MIGHT NOT BE A BAD
+ * THING YET IF THERE ARE OTHER OBJECT PLACEHOLDERS TO
+ * COME THAT ARE LESS RESTRICTIVE */
+ return (FALSE);
+ }
+ }
+ }
+
+ if (noun_number > 1 && list_size[noun_number] != 0) {
+ /* A SECOND EXCEPTION LIST EXISTS, SUBTRACT IT FROM THE FIRST */
+ //printf("--- there are some exceptions.\n");
+
+ //printf("--- first list: %d, second list: %d\n", max_size[FIRST_LIST], max_size[SECOND_LIST]);
+ /* LOOP THROUGH ALL THE ITEMS IN THE SECOND LIST */
+ for (index = 0; index < max_size[SECOND_LIST]; index++) {
+ if (object_list[SECOND_LIST][index] != 0) {
+ /* THIS OBJECT IS A REAL OBJECT SO LOOP THROUGH ALL THE ITEMS
+ * IN THE FIRST LIST */
+ //printf("--- exception object is %s\n", object[object_list[SECOND_LIST][index]]->label);
+ for (counter = 0; counter < max_size[FIRST_LIST]; counter++) {
+ /* LOOP THROUGH ALL THE OBJECTS IN THE FIRST LIST
+ * IF AN OBJECT FROM THE SECOND LIST EXISTS IN THE FIRST
+ * LIST, REMOVE IT */
+ //printf("--- comparing %s = %s\n", object[object_list[FIRST_LIST][counter]]->label, object[object_list[SECOND_LIST][index]]->label);
+ if (object_list[FIRST_LIST][counter] ==
+ object_list[SECOND_LIST][index]) {
+
+ //printf("--- removing object %s\n", object[object_list[FIRST_LIST][counter]]->label);
+ object_list[FIRST_LIST][counter] = 0;
+ list_size[FIRST_LIST]--;
+ }
+ }
+ }
+ }
+ }
+
+ if (noun_number > 1) {
+ /* IF THERE WERE EXCEPTIONS, MOVE BACK TO THE FIRST LIST */
+ noun_number = FIRST_LIST;
+ }
+
+ /* THE RETURN TRUE IF AN OBJECT COULD BE RESOLVED */
+ if (list_size[noun_number] != 0) {
+ /* SET THEM ARRAY */
+ set_them(noun_number);
+
+ return (TRUE);
+ } else {
+ /* THE LIST IS NOW EMPTY, DISPLAY ' I DON'T SEE WHAT YOU
+ * ARE REFERRING TO.' ERROR */
+ if (!strcmp(scope_word->word, "*held") ||
+ !strcmp(scope_word->word, "**held")) {
+ write_text(cstring_resolve("NONE_HELD")->value);
+ } else {
+ write_text(cstring_resolve("NO_OBJECTS")->value);
+ }
+
+ custom_error = TRUE;
+ return (FALSE);
+ }
+}
+
+void set_them(int noun_number) {
+ int index, counter;
+
+ if (list_size[noun_number] == 1) {
+ /* THERE IS ONLY ONE OBJECT LEFT IN THE LIST, FIND IT AND ADD
+ * TO THEM IF REQUIRED */
+ for (index = 0; index < max_size[noun_number]; index++) {
+ if (object_list[noun_number][index] != 0) {
+ if (object[object_list[noun_number][index]]->attributes & PLURAL) {
+ them[0] = object_list[noun_number][index];
+ them[1] = 0;
+ break;
+ }
+ }
+ }
+ } else {
+ /* THERE IS MORE THAN ONE OBJECT IN THE LIST SO COPY IT TO THE
+ * 'THEM' LIST */
+ counter = 0;
+
+ for (index = 0; index < max_size[noun_number]; index++) {
+ if (object_list[noun_number][index] != 0) {
+ them[counter] = object_list[noun_number][index];
+ //printf("--- storing %s in them list\n", object[them[counter]]->label);
+ counter++;
+ }
+ }
+
+ /* NULL TERMINATE THE LIST */
+ them[counter] = 0;
+ }
+}
+
+void add_all(struct word_type *scope_word, int noun_number) {
+ int index, counter;
+
+ //printf("--- trying to add all\n");
+ counter = 0;
+
+ for (index = 1; index <= objects; index++) {
+ if ((object[index]->MASS < HEAVY) &&
+ !(object[index]->attributes & LOCATION)) {
+ if (is_direct_child_of_from(index) &&
+ scope(index, scope_word->word, RESTRICT)) {
+ //printf("--- objects parent is %s\n", object[object[index]->PARENT]->label);
+ add_to_list(noun_number, index);
+ }
+ }
+ }
+}
+
+int is_child_of_from(int child) {
+ /* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
+ * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
+ int index = 0;
+
+ if (from_objects[0] == 0) {
+ /* THERE HAS BEEN NO FROM CLAUSE */
+ return (TRUE);
+ }
+
+ while (from_objects[index] != 0) {
+ //printf("--- in is_child from object is %s, child is %s\n", object[from_objects[index]]->label, object[child]->label);
+ /* THIS OLD WAY OF DOING THINGS ALLOWS SPECIFIC 'take thing from box'
+ * WHEN thing IS INSIDE SOMETHING ELSE INSIDE box. THAT IS KINDA COOL
+ * BUT NOT PARTICULARLY NECESSARY. BY ONLY CHECKING IMMEDIATE CHILDREN
+ * THE NON-RESTRICTIVE VERSION OF parent_of CAN BE USED FROM SCOPE
+ * WHEN SAYING take all from box BECAUSE THIS FROM STOPS OBJECT IN
+ * OBJECTS FROM BEING TAKING FROM SOMETHING, SO scope DOESN'T HAVE
+ * TO. THIS BEHAVIOUR WOULD NOT BE NECESSARY WHEN NOT taking all from */
+
+ //if (parent_of(from_objects[index], child, RESTRICT)) {
+ if (object[child]->PARENT == from_objects[index]) {
+ //printf("--- %s is in %s\n", object[child]->label, object[from_objects[index]]->label);
+ return (TRUE);
+ }
+ index++;
+ }
+
+ return (FALSE);
+}
+
+int is_direct_child_of_from(int child) {
+ /* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF
+ * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */
+ int index = 0;
+
+ if (from_objects[0] == 0) {
+ /* THERE HAS BEEN NO FROM CLAUSE */
+ return (TRUE);
+ }
+
+ while (from_objects[index] != 0) {
+ //printf("--- in is_direct from object is %s\n", object[from_objects[index]]->label);
+ if (object[child]->PARENT == from_objects[index]) {
+ //printf("--- object %s is in the from object\n", object[child]->label);
+ return (TRUE);
+ }
+ index++;
+ }
+
+ return (FALSE);
+}
+
+int get_from_object(struct word_type *scope_word, int noun_number) {
+ /* THIS FUNCTION LOOKS AHEAD TO FIND IF THE CURRENT OBJECT REFERENCE
+ * IS QUALIFIED BY A 'FROM' WORD. IT RETURNS FALSE ON AN ERROR
+ * CONDITION AND TRUE OTHERWISE, REGARDLESS OF WHETHER A FROM OBJECT
+ * IS SPECIFIED */
+
+ int index, counter, from_object;
+
+ /* TAKE A COPY OF THE CURRENT VALUE OF wp */
+ int backup = wp;
+
+ /* SET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
+ struct word_type *terminator = scope_word->first_child;
+
+ /* SEE IF 'FROM' IS ONE OF THE TERMINATORS OF THIS CURRENT OBJECT
+ * PLACEHOLDER. IF SO, DON'T LOOK FOR A FROM OBJECT */
+ if (terminator != NULL) {
+ //printf("--- checking if terminator word (%s) is from\n", terminator->word);
+ if (!strcmp(cstring_resolve("FROM_WORD")->value, terminator->word)) {
+ //printf("--- from is a terminator, don't get a from object\n");
+ return (TRUE);
+ }
+ while ((terminator = terminator->next_sibling) != NULL);
+ }
+
+ /* LOOP FROM THE CURRENT WORD TO THE NEXT TERMINATOR AND LOOK FOR THE
+ * WORD 'FROM' AND STORE THE FOLLOWING OBJECT */
+ while (word[wp] != NULL) {
+ //printf("--- from loop checking %s\n", word[wp]);
+ if (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) {
+ from_word = word[wp];
+ wp++;
+
+ /* scope_word FOR THE CURRENT OBJECT IS PASSED ONLY SO
+ * noun_resolve CAN FIND OUT THE APPROPRIATE TERMINATORS
+ * THE ACCEPTABLE SCOPE FOR THE FROM OBJECT SHOULD BE
+ * AT LEAST *present */
+ /* TRUE INDICATES THAT WE ARE LOOKING FOR A FROM OBJECT */
+ from_object = noun_resolve(scope_word, TRUE, noun_number);
+
+ /* STORE THE wp FROM AFTER THE RESOLVED FROM OBJECT SO
+ * WE CAN JUMP FORWARD TO HERE AGAIN WHEN WE HIT THIS
+ * FROM CLAUSE IN PROCESSING (THIS FUNCTION IS A LOOK-AHEAD */
+ after_from = wp;
+
+ //printf("--- looked forward and found a from object of %s\n", object[from_object]->label);
+
+ if (from_object == -1) {
+ /* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME
+ * BEING USED */
+ index = 0;
+ counter = 0;
+ /* LOOK THROUGH ALL THE OBJECTS RESOLVED AND CHECK THAT
+ * THEY ARE ALL VALID FROM OBJECTS */
+ while (multiple_resolved[index] != 0) {
+ if (verify_from_object(multiple_resolved[index]) == FALSE) {
+ /* AS SOON AS ONE IS BAD, ABORT THE FROM CLAUSE */
+ return (FALSE);
+ } else {
+ from_objects[counter] = multiple_resolved[index];
+ counter++;
+ }
+ index++;
+ }
+
+ /* NULL TERMINATE THE LIST */
+ from_objects[counter] = 0;
+
+ /* OBJECTS HAVE BEEN SET AND ARE VALID, RESTORE THE WORD
+ * POINTER */
+ wp = backup;
+ return (TRUE);
+ } else if (from_object) {
+ if (verify_from_object(from_object) == FALSE) {
+ return (FALSE);
+ } else {
+ /* ADD THIS OBJECT TO THE NULL TERMINATED LIST */
+ from_objects[0] = from_object;
+ from_objects[1] = 0;
+
+ /* OBJECT HAS BEEN SET AND IS VALID, RESTORE THE WORD
+ * POINTER */
+ wp = backup;
+ return (TRUE);
+ }
+ } else {
+ /* FROM UNKNOWN OBJECT, DISPLAY ERROR */
+ diagnose();
+ custom_error = TRUE;
+ return (FALSE);
+ }
+ //printf("--- finished processing from clause, next word is %s\n", word[wp]);
+ } else if (!strcmp(cstring_resolve("EXCEPT_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("BUT_WORD")->value, word[wp])) {
+ /* THIS IS THE LIMIT OF THE EFFECT ANY FROM OBJECT ANYWAY,
+ * SO TREAT IT LIKE A TERMINATOR */
+ //printf("--- %s is a get_from_object except word\n", word[wp]);
+ wp = backup;
+ return (TRUE);
+ } else if (is_terminator(scope_word) || !strcmp("then", word[wp])) {
+ /* THERE ARE NO MORE OBJECTS TO RESOLVE */
+
+ //printf("--- %s is a get_from_object terminator\n", word[wp]);
+ wp = backup;
+ return (TRUE);
+ }
+ wp++;
+ }
+
+ /* HIT THE END OF THE PLAYER'S COMMAND BEFORE A 'FROM' WORD */
+ //printf("--- no from clause specified in this block\n");
+ wp = backup;
+ return (TRUE);
+}
+
+int verify_from_object(int from_object) {
+ //printf("--- from object is %s\n", object[from_object]->label);
+ //if (!(object[from_object]->attributes & CONTAINER) &&
+ // !(object[from_object]->attributes & SURFACE) &&
+ // !(object[from_object]->attributes & ANIMATE)) {
+ // sprintf (error_buffer, FROM_NON_CONTAINER, from_word);
+ // write_text (error_buffer);
+ // custom_error = TRUE;
+ // return (FALSE);
+ //} else if (object[from_object]->attributes & CLOSED) {
+ if (object[from_object]->attributes & CONTAINER && object[from_object]->attributes & CLOSED) {
+ //printf("--- container is concealing\n");
+ if (object[from_object]->attributes & FEMALE) {
+ sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED_FEM")->value, sentence_output(from_object, TRUE));
+ } else {
+ sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED")->value, sentence_output(from_object, TRUE));
+ }
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);
+ /* IF THE PERSON IS CONCEALING, THEN THE OBJECT CAN'T BE REFERRED TO
+ * IF THE PERSON IS POSSESSIVE LET THE LIBRARY HANDLE THE RESPONSE
+ } else if (object[from_object]->attributes & POSSESSIVE) {
+ //printf("--- container is closed\n");
+ sprintf (error_buffer, PERSON_POSSESSIVE, sentence_output(from_object, TRUE));
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);
+ } else if (object[from_object]->attributes & CONCEALING) {
+ //printf("--- container is closed\n");
+ sprintf (error_buffer, PERSON_CONCEALING, sentence_output(from_object, TRUE));
+ write_text(error_buffer);
+ custom_error = TRUE;
+ return (FALSE);*/
+ }
+
+ //printf("--- set from object just fine\n");
+ return (TRUE);
+}
+
+void add_to_list(int noun_number, int resolved_object) {
+ /* ADD THIS OBJECT TO THE OBJECT LIST DEPENDING */
+ /* AND SET IT, THEM, HER AND HIM */
+ if (!(object[resolved_object]->attributes & ANIMATE))
+ it = resolved_object;
+ if (object[resolved_object]->attributes & ANIMATE
+ && object[resolved_object]->attributes & FEMALE)
+ her = resolved_object;
+ if (object[resolved_object]->attributes & ANIMATE
+ && !(object[resolved_object]->attributes & FEMALE))
+ him = resolved_object;
+
+ //printf("--- adding_object %s to list %d at index %d\n", object[resolved_object]->label, noun_number, max_size[noun_number]);
+ object_list[noun_number][max_size[noun_number]] = resolved_object;
+ list_size[noun_number]++;
+ max_size[noun_number]++;
+}
+
+int noun_resolve(struct word_type *scope_word, int finding_from, int noun_number) {
+ /* THIS FUNCTION STARTS LOOKING AT THE PLAYER'S COMMAND FROM wp ONWARDS
+ * AND LOOKS FOR OBJECTS IN THE SCOPE SPECIFIED BY THE GRAMMAR ELEMENT
+ * POINTED TO BY THE PASSED pointer */
+
+ /* THIS IS SET TO TRUE WHEN THE CURRENT WORD MATCHES THE CURRENT OBJECT */
+ int object_matched;
+
+ /* THIS IS SET TO > 0 WHEN THE PLURAL FORM OF AN OBJECT IS USED */
+ int return_limit = 0;
+
+ int index;
+ int counter;
+ int first_word = TRUE;
+
+ struct word_type *terminator = scope_word->first_child;
+ struct name_type *current_name;
+
+ matches = 0;
+ highest_confidence = 0;
+ prime_suspect = 0;
+ done = FALSE;
+ backup_pointer = wp;
+ everything = FALSE;
+
+ if (word[wp] == NULL) {
+ /* NOTHING TO RESOLVE... */
+ return (FALSE);
+ }
+
+ /* SET THE CONFIDENCE FOR EACH OBJECT TO 1 TO INDICATE THAT THEY ARE
+ * ALL EQUALLY POSSIBLE AT THE MOMENT. SET TO ZERO TO DISCOUNT */
+ for (index = 1; index <= objects; index++)
+ confidence[index] = 1;
+
+ /* CLEAR THE OBJECT NAME BEFORE BUILDING IT WAS WE GO ALONG */
+ object_name[0] = 0;
+
+ /* CHECK FOR A QUANTITY QUALIFIER */
+ if (validate(word[wp])) {
+ /* FIRST WORD IS AN INTEGER AND SECOND WORD IS 'OF' SO
+ * TREAT THIS AS A LIMIT QUALIFIER BEFORE STARTING TO
+ * PROCESS THE REST OF THE WORDS */
+ if (word[wp + 1] != NULL && !strcmp(word[wp + 1], cstring_resolve("OF_WORD")->value)) {
+ return_limit = atoi(word[wp]);
+
+ /* MAKE SURE THE RETURN LIMIT IS SOMETHING SENSIBLE */
+ if (return_limit < 1) {
+ return_limit = 1;
+ }
+
+ object_expected = TRUE;
+ strcpy(object_name, word[wp]);
+ strcat(object_name, " ");
+ strcat(object_name, cstring_resolve("OF_WORD")->value);
+
+ /* MOVE THE WORD POINTER TO AFTER THE 'OF' */
+ wp = wp + 2;
+ }
+ /* IF AN INTEGER IS NOT FOLLOWED BY 'OF', PRESUME IT IS AN OBJECT
+ * NAME */
+ }
+
+ // CLEAR THE ERROR BUFFER AND USE IT TO STORE ALL THE WORDS THE PLAYER
+ // HAS USED TO REFER TO THE OBJECT
+ error_buffer[0] = 0;
+
+ while (word[wp] != NULL) {
+ // ADD THE WORDS USED TO error_buffer FOR POSSIBLE USE
+ // IN A DISABMIGUATE EMESSAGE
+ if (first_word == FALSE) {
+ strcat(error_buffer, " ");
+ }
+ strcat(error_buffer, word[wp]);
+ first_word = FALSE;
+
+ /* LOOP THROUGH WORDS IN THE PLAYER'S INPUT */
+
+ /* RESET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */
+ terminator = scope_word->first_child;
+
+ if (terminator != NULL) {
+ /* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND
+ * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */
+ do {
+ /* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE
+ * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END
+ * OF THE OBJECT REFERENCE PART OF THE COMMAND */
+ //printf("--- checking terminator word %s\n", terminator->word);
+ if (!strcmp(word[wp], terminator->word)
+ || (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value))
+ || (!strcmp(word[wp], cstring_resolve("AND_WORD")->value))
+ || (!strcmp(word[wp], "comma"))
+ || (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value))
+ || (!strcmp(word[wp], cstring_resolve("THEN_WORD")->value))
+ || (!strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value))
+ || (!strcmp(terminator->word, "$integer")
+ && validate(word[wp]))) {
+ if (!matches) {
+ /* A TERMINATOR HAS BEEN FOUND BEFORE A
+ * SINGLE MATCHING OBJECT NAME. */
+ return (FALSE);
+ } else {
+ /* A TERMINATOR HAS BEEN FOUND, BUT NOT
+ * BEFORE MATCHING OBJECT NAMES. JUMP FORWARD TO
+ * RESOLVING INTO OBJECT NUMBER(S) */
+ done = TRUE;
+ break;
+ }
+ }
+ } while ((terminator = terminator->next_sibling) != NULL);
+ }
+
+ if (done == TRUE) {
+ /* A TERMINATING WORD HAS BEEN REACHED, SO DON'T TEST THIS
+ * WORD AGAINST THE OBJECT NAMES, JUST MOVE ON TO CHOSING
+ * AN OBJECT. */
+ //printf("--- %s is a terminator\n", word[wp]);
+ break;
+ }
+
+ //puts("--- passed checking for a terminator");
+
+ /* ADD THE CURRENT WORD TO THE NAME OF THE OBJECT THE PLAYER
+ * IS TRYING TO REFER TO FOR USE IN AN ERROR MESSAGE IF
+ * LATER REQUIRED */
+ if (object_name[0] != 0)
+ strcat(object_name, " ");
+
+ strcat(object_name, word[wp]);
+
+ if (!strcmp("everything", word[wp])) {
+ /* ALL THIS NEEDS TO SIGNIFY IS THAT IT IS OKAY TO RETURN MULTIPLE
+ * RESULTS. EVERYTHING ELSE SHOULD TAKE CARE OF ITSELF GIVEN ALL
+ * OBJECTS START OF WITH A CONFIDENCE OF 1, AND ALL OBJECTS WITH
+ * A CONFIDENCE > 0 ARE RETURNED WHEN return_limit > 1 */
+ if (return_limit == FALSE) {
+ return_limit = MAX_OBJECTS;
+ }
+
+ /* THIS IS USED TO ALTER THE BEHAVIOUR OF SCOPE SELECTION LATER */
+ everything = TRUE;
+
+ matches = 0;
+
+ //printf("--- entering for loop\n");
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ matches++;
+ }
+ }
+ //printf("--- exiting for loop\n");
+
+ if (word[wp + 1] != NULL && !strcmp(cstring_resolve("OF_WORD")->value, word[wp + 1])) {
+ /* MOVE PAST THE 'OF' IF IT IS NEXT */
+ wp++;
+ }
+ //printf("--- finished setting matches to %d for all\n", matches);
+ } else {
+ /* THE CURRENT WORD IS NOT ONE OF THE NEXT POSSIBLE WORDS IN THE
+ * PLAYER'S COMMAND OR ANY OF THE SPECIAL MEANING WORDS, THEREFORE
+ * TEST IT AGAINST ALL OF THE NAMES OF ALL OF THE OBJECTS. */
+ for (index = 1; index <= objects; index++) {
+ if (!confidence[index]) {
+ /* SKIP OBJECTS THAT HAVE ALREADY
+ * BEEN EXCLUDED BY A PREVIOUS WORD */
+ continue;
+ }
+
+ /* NEXT WORD IN PLAYERS INPUT IS YET TO
+ * BE TESTED AGAINST ALL THIS OBJECT'S NAMES */
+ object_matched = FALSE;
+
+ if (!strcmp(cstring_resolve("IT_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("ITSELF_WORD")->value, word[wp])) {
+ if (it == FALSE) {
+ no_it();
+ return (FALSE);
+ } else {
+ if (index == it) {
+ object_matched = TRUE;
+ }
+ }
+ } else if (!strcmp(cstring_resolve("HER_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("HERSELF_WORD")->value, word[wp])) {
+ if (her == FALSE) {
+ no_it();
+ return (FALSE);
+ } else {
+ if (index == her) {
+ object_matched = TRUE;
+ }
+ }
+ } else if (!strcmp(cstring_resolve("HIM_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("HIMSELF_WORD")->value, word[wp])) {
+ if (him == FALSE) {
+ no_it();
+ return (FALSE);
+ } else {
+ if (index == him) {
+ object_matched = TRUE;
+ }
+ }
+ } else if (!strcmp(cstring_resolve("THEM_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("THEMSELVES_WORD")->value, word[wp]) ||
+ !strcmp(cstring_resolve("ONES_WORD")->value, word[wp])) {
+ /* THIS NEED ONLY BE THE SIZE OF 'THEM', BUT NO HARM
+ * IN MAKING IT THE FULL SIZE */
+ if (return_limit == FALSE) {
+ return_limit = MAX_OBJECTS;
+ }
+
+ /* LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
+ * SEE IF THIS OBJECT IS PRESENT */
+ counter = 0;
+
+ while (them[counter] != 0) {
+ if (them[counter] == index) {
+ //printf("--- found previous them object %s\n", object[index]->label);
+ object_matched = TRUE;
+ break;
+ }
+ counter++;
+ }
+ /*} else if (!strcmp("1", word[wp])) {
+ return_limit = 1;
+
+ // LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND
+ // SEE IF THIS OBJECT IS PRESENT
+ counter = 0;
+
+ while (them[counter] != 0) {
+ if (them[counter] == index) {
+ //printf("--- found previous them object %s\n", object[index]->label);
+ object_matched = TRUE;
+ break;
+ }
+ counter++;
+ }*/
+ } else {
+ current_name = object[index]->first_name;
+
+ while (current_name != NULL) {
+ /* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
+ if (!strcmp(word[wp], current_name->name)) {
+ /* CURRENT WORD MATCHES THE CURRENT NAME
+ *OF THE CURRENT OBJECT */
+ //printf("--- %s is a name match of object %d\n", word[wp], index);
+ object_matched = TRUE;
+
+ /* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
+ * NOT HAVE THE SAME NAME TWICE */
+ break;
+ }
+ current_name = current_name->next_name;
+ }
+
+ /* NOW LOOP THROUGH ALL THE OJBECTS PLURAL NAMES */
+ current_name = object[index]->first_plural;
+
+ while (current_name != NULL) {
+ /* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */
+ if (!strcmp(word[wp], current_name->name)) {
+ /* CURRENT WORD MATCHES THE CURRENT NAME
+ *OF THE CURRENT OBJECT */
+ //printf("--- %s is a plural name match of object %d\n", word[wp], index);
+ object_matched = TRUE;
+
+ /* IT IS NOW OKAY FOR THIS FUNCTION TO RETURN MORE
+ * THAT ONE MATCHING OBJECT AS THE PLURAL FORM
+ * HAS BEEN USED. THIS IS INDICATED BY RETURNING
+ * -1 TO INDICATED THAT THE OBJECT LIST IS STORED
+ * IN A GLOBALLY ACCESSIBLE ARRAY */
+ if (return_limit == FALSE) {
+ return_limit = MAX_OBJECTS;
+ }
+
+ /* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD
+ * NOT HAVE THE SAME NAME TWICE */
+ break;
+ }
+ current_name = current_name->next_name;
+ }
+ }
+
+ if (object_matched) {
+ /* THE CURRENT WORD MATCHES ONE OF THE NAMES OF THE
+ * CURRENT OBJECT. */
+ if (confidence[index] == 1) {
+ /* OBJECT HAD NOT YET BEEN COUNTED AS MATCH SO INCREMENT
+ * THE NUMBER OF OBJECTS THAT MATCH SO FAR. */
+ matches++;
+ }
+ if (confidence[index] != FALSE) {
+ /* IF OBJECT HAS NOT BEEN EXCLUDED, INCREMENT THE
+ * NUMBER OF NAMES IT HAS MATCHING. */
+ confidence[index]++;
+ }
+ } else {
+ /* THE CURRENT WORD IS NOT ONE OF THE NAMES OF THE
+ * CURRENT OBJECT.
+ * IF THE OBJECT HAD PREVIOUSLY BEEN A CANDIDATE, DECREMENT
+ * THE NUMBER OF MATCHING OBJECTS. */
+ if (confidence[index] > 1)
+ matches--;
+
+ /* AS THE CURRENT WORD DID NOT MATCH ANY OF THE NAMES OF
+ * THE THIS OBJECT, EXCLUDE IT FROM CONTENTION. */
+ confidence[index] = FALSE;
+ }
+ } /* MOVE ON TO NEXT OBJECT FOR IN LOOP */
+ }
+
+ if (!matches) {
+ /* IF THERE ARE NO REMAINING MATCHES DON'T MOVE ON TO THE NEXT
+ * WORD IN THE PLAYER'S INPUT. */
+ //printf("--- %s isnt a name match for any object\n", word[wp]);
+
+ /* THIS WORD IS A LIKELY BE INCORRECT AS IT DIDN'T MATCH
+ * ANY OBJECTS */
+ if (oops_word == -1 && word[wp] != NULL) {
+ oops_word = wp;
+ }
+
+ break;
+ }
+
+ /* MOVE ON TO THE NEXT WORD IN THE PLAYER'S INPUT. */
+ //printf("--- moving on to next word\n");
+ wp++;
+ }
+
+ //printf("--- finished processing words\n");
+
+ /***************************************************
+ * FINISHED LOOKING THROUGH THE PLAYER'S INPUT NOW *
+ * MOVE THE POINTER BACK FOR PARSER TO PROCESS *
+ * THIS SHOULDN'T BE DONE FOR A WORD LIKE 'AND' *
+ ***************************************************/
+
+ if (return_limit == FALSE) {
+ /* THE RETURN LIMIT WAS NEVER SET, SO TREAT THIS AS AN IMPLICIT 1 */
+ return_limit = 1;
+ }
+
+ if (matches == 0) {
+ /* THE PLAYER'S INPUT COULD NOT BE RESOLVED TO A SINGLE OBJECT
+ * BUT NO TERMINATING WORD WAS USED BEFORE A NON-TERMINATING
+ * WORD IN A PLACE WHERE AN OBJECT COULD BE SPECIFIED ---
+ * IN OTHER WORDS, PRESUME THE PLAYER WAS TRYING TO REFER TO
+ * AN OBJECT BUT SOMEHOW GOT IT WRONG */
+
+ //printf("--- matches = 0\n");
+ /* THIS VARIABLE IS USED TO CHANGE THE BEHAVIOR OF diagnose()
+ * SHOULD THIS COMMAND TURN OUT TO BE INVALID */
+ object_expected = TRUE;
+
+ wp = backup_pointer; /* BACK UP THE CURRENT WORD POINTER.
+ * SO THE PARSER CAN INVESTIAGE THE
+ * POSIBILITY THAT THIS ISN'T A
+ * REFERENCE TO AN OBJECT AT ALL */
+
+ return (FALSE); /* RETURN TO PARSER WITH NO MATCHING
+ * OBJECT HAVING BEEN FOUND. */
+ }
+
+ //printf("--- starting with %d matches\n", matches);
+
+ /* LOOP THROUGH ALL OBJECTS AND REMOVE FROM ANY OBJECT THAT IS NOT IN THE
+ * CURRENT LOCATION, PROVIDED THE VERB DOESN'T ALLOW THE OBJECT TO
+ * BE *ANYWHERE. */
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE && strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere") && strcmp(scope_word->word, "*location")) {
+ if (scope(index, "*present", UNRESTRICT) == FALSE) {
+ matches--;
+ confidence[index] = FALSE;
+ //printf("--- removing %s for not being present\n", object[index]->label);
+ }
+ }
+
+ /* IF THE OBJECT IS IMPLICITLY INCLUDED DUE TO A MULTIPLE RETURN
+ * LIMIT, REMOVE IT IF IT IS PART OF THE SCENERY */
+ if (confidence[index] == 1 && return_limit > 1) {
+ if ((object[index]->MASS >= HEAVY) ||
+ (object[index]->attributes & LOCATION)) {
+ matches--;
+ confidence[index] = 0;
+ //printf("--- removing %s for being scenery\n", object[index]->label);
+ }
+ }
+
+ /* FOR ALL CONTENDERS, CALCULATE THE CONFIDENCE AS A PERCENTAGE,
+ * SO LONG AS WE ARE STILL LOOKING FOR A SINGLE OBJECT */
+ if (confidence[index] != FALSE && return_limit == 1) {
+ 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;
+ }
+ }
+
+ // CALCULATE THE HIGHEST CONFIDENCE OF ALL THE POSSIBLE OBJECTS
+ highest_confidence = 0;
+
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] > highest_confidence) {
+ highest_confidence = confidence[index];
+ }
+ }
+
+ /* REMOVE ANY OBJECTS THAT ARE NOT IN THEIR VERB'S SPECIFIED SCOPE */
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ if (scope(index, "*present", UNRESTRICT) != FALSE) {
+ // TAKE SPECIAL NOT OF AN OBJECT THAT IS PRESENT
+ // AND AT LEAST EQUAL FOR THE HIGHEST CONFIDENCE
+ // IN CASE NO OBJECT ARE LEFT AFTER SPECIFIED SCOPE
+ // IS USED TO FILTER THE LIST */
+ if (confidence[index] == highest_confidence) {
+ prime_suspect = index;
+ }
+ //printf("--- storing %s as prime_suspect\n", object[index]->label);
+ }
+
+ if (finding_from) {
+ if (strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere")) {
+ if (scope(index, "*present") == FALSE) {
+ matches--;
+ confidence[index] = FALSE;
+ continue;
+ }
+ }
+ } else if (scope(index, scope_word->word, (everything && !from_objects[0])) == FALSE) {
+ /* IF everything IS TRUE, scope IS RESTRICTED */
+ //printf("--- removing %s due to regular scope\n", object[index]->label);
+ matches--;
+ confidence[index] = FALSE;
+ continue;
+ }
+
+ /* CHECK IF THIS OBJECT IS IN ACCORDANCE WITH ANY FROM CLAUSE
+ * THAT MAY OR MAY NOT HAVE USED */
+ if (is_child_of_from(index) == FALSE) {
+ matches--;
+ confidence[index] = FALSE;
+ //printf("--- removing %s due to from clause\n", object[index]->label);
+ }
+ }
+ }
+
+ //printf("--- there are %d matches left\n", matches);
+
+ /* THIS LOOP REMOVES ANY OBJECT THAT ARE NOT EQUAL TO THE HIGHEST
+ * CONFIDENCE UNLESS A PLURAL NAME WAS USED. IN THAT CASE, ONLY
+ * EXCLUDE OBJECTS THAT DO NOT HAVE ONE OF THE NAMES SUPPLIED */
+ if (matches > 1 && return_limit == 1) {
+ // CALCULATE THE HIGHEST CONFIDENCE NOW THAT OBJECTS
+ // NOT IN SCOPE HAVE BEEN REMOVED
+ highest_confidence = 0;
+
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] > highest_confidence) {
+ highest_confidence = confidence[index];
+ }
+ }
+
+ //printf("--- removing lower confidence objects\n");
+ for (index = 1; index <= objects; index++) {
+ /* REMOVE ANY OBJECTS THAT ARE NOT EQUAL
+ TO THE HIGHEST CONFIDENCE. */
+ if (confidence[index] != FALSE) {
+ if (confidence[index] < highest_confidence) {
+ //printf("--- removing %s due to confidence of %d being under %d\n", object[index]->label, confidence[index], highest_confidence);
+ confidence[index] = FALSE;
+ matches--;
+ }
+ }
+ }
+ }
+
+ if (matches == 0) {
+ /* IF THIS LEAVES NO OBJECT, RETURN THE OBJECT THAT WAS PRESENT
+ * AND SET THE APPROPRIATE POINTERS. */
+ if (prime_suspect != FALSE) {
+ return (prime_suspect);
+ } else {
+ object_expected = TRUE;
+ wp = backup_pointer;
+ return (FALSE);
+ }
+ }
+
+ if (matches == 1) {
+ /* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
+ * APPROPRIATE POINTERS. */
+ //printf("--- only one object left\n");
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ return (index);
+ }
+ }
+ }
+
+ if (return_limit > 1) {
+ /* THE PLURAL NAME OF AN OBJECT WAS USED OR A QUANTITY QUALIFIER */
+
+ counter = 0;
+
+ //printf("--- return_limit == TRUE\n");
+
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ /* ADD EACH OBJECT TO multiple_resolved UNTIL
+ * THE return_limit IS REACHED */
+ multiple_resolved[counter] = index;
+ return_limit--;
+ //printf("--- adding %s to multiple_resolved\n", object[index]->label);
+ counter++;
+
+ if (return_limit == 0) {
+ break;
+ }
+ }
+ }
+
+ /* NULL TERMINATE THE LIST */
+ multiple_resolved[counter] = 0;
+
+ //printf("--- returning multiple objects\n");
+ /* RETURN -1 TO INDICATED THERE ARE MULTIPLE OBJECTS */
+ return (-1);
+ }
+
+ /* AN AMBIGUOUS REFERENCE WAS MADE. ATTEMPT TO CALL ALL THE disambiguate
+ * FUNCTIONS TO SEE IF ANY OF THE OBJECT WANTS TO TAKE PREFERENCE IN
+ * THIS CIRCUMSTANCE */
+ int situation = noun_number;
+
+ if (finding_from) {
+ situation += 4;
+ }
+
+ /*
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ strcpy(function_name, "disambiguate");
+ strcat(function_name, "_");
+ strcat(function_name, object[index]->label);
+ strcat(function_name, "<");
+ sprintf(temp_buffer, "%d", situation);
+ strcat(function_name, temp_buffer);
+
+ // CALL THE DISAMBIGUATION FUNCTION ATTACHED TO EACH OF THE
+ // POSSIBLE OBJECTS
+ int return_code = execute (function_name);
+
+ if (return_code == 1) {
+ // THIS OBJECT CAN CLAIMED OWNERSHIP OF THIS COMMAND
+ return (index);
+ } else if (return_code == -1) {
+ // THIS OBJECT HAS REJECTED OWNERSHIP OF THIS COMMAND
+ confidence[index] = FALSE;
+ matches--;
+ }
+ }
+ }
+ */
+
+ // CHECK IF ALL THE OBJECTS WERE REJECTED
+ if (matches == 0) {
+ return (prime_suspect);
+ }
+
+ // CHECK IF ONLY ONE OBJECT NOW REMAINS
+ if (matches == 1) {
+ /* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE
+ * APPROPRIATE POINTERS. */
+ //printf("--- only one object left\n");
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ return (index);
+ }
+ }
+ }
+
+#if defined GLK || defined __NDS__
+ /* NO OBJECT HAS CLAIMED OWNERSHIP, PROMPT THE PLAYER TO SPECIFY
+ * WHICH ONE THEY REQUIRE. */
+ counter = 1;
+ write_text(cstring_resolve("BY")->value);
+ write_text(error_buffer);
+ write_text(cstring_resolve("REFERRING_TO")->value);
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ possible_objects[counter] = index;
+ sprintf(text_buffer, " [%d] ", counter);
+ write_text(text_buffer);
+ sentence_output(index, 0);
+ write_text(temp_buffer);
+ matches--;
+ if (counter < 9)
+ counter++;
+ write_text("^");
+ }
+ }
+
+ /* GET A NUMBER: don't insist, low = 1, high = counter */
+ selection = get_number(FALSE, 1, counter - 1);
+
+ if (selection == -1) {
+ write_text(cstring_resolve("INVALID_SELECTION")->value);
+ custom_error = TRUE;
+ return (FALSE);
+ } else {
+ write_text("^");
+ return (possible_objects[selection]);
+ }
+#else
+ /* IF MORE THAT ONE OBJECT STILL REMAINS, PROMPT THE PLAYER TO SPECIFY
+ * WHICH ONE THEY REQUIRE. */
+ write_text(cstring_resolve("WHEN_YOU_SAY")->value);
+ write_text(error_buffer);
+ write_text(cstring_resolve("MUST_SPECIFY")->value);
+ for (index = 1; index <= objects; index++) {
+ if (confidence[index] != FALSE) {
+ sentence_output(index, 0);
+ write_text(temp_buffer);
+ matches--;
+ if (matches == 0) {
+ write_text(".");
+ break;
+ } else if (matches == 1) {
+ write_text(cstring_resolve("OR_WORD")->value);
+ } else
+ write_text(", ");
+ }
+ }
+ write_text("^");
+
+ custom_error = TRUE;
+ return (FALSE);
+#endif
+}
+
+void diagnose() {
+ if (custom_error) {
+ TIME->value = FALSE;
+ return;
+ }
+ if (word[wp] == NULL)
+ write_text(cstring_resolve("INCOMPLETE_SENTENCE")->value);
+ else if (object_expected && wp != 0) {
+ write_text(cstring_resolve("UNKNOWN_OBJECT")->value);
+ write_text(object_name);
+ write_text(cstring_resolve("UNKNOWN_OBJECT_END")->value);
+ } else {
+ write_text(cstring_resolve("CANT_USE_WORD")->value);
+ write_text(word[wp]);
+ write_text(cstring_resolve("IN_CONTEXT")->value);
+ }
+ TIME->value = FALSE;
+}
+
+int scope(int index, char *expected, int restricted) {
+ /* THIS FUNCTION DETERMINES IF THE SPECIFIED OBJECT IS IN THE SPECIFIED
+ * SCOPE - IT RETURNS TRUE IF SO, FALSE IF NOT. */
+
+ int temp = 0;
+
+ /* WHEN THE ARGUMENT restricted IS TRUE IT HAS A MORE LIMITED
+ * SENSE OF WHAT IS ACCEPTABLE */
+
+ if (!strcmp(expected, "*held") || !strcmp(expected, "**held")) {
+ if (object[index]->PARENT == HELD) {
+ return (TRUE);
+ } else if (object[index]->MASS >= HEAVY) {
+ /* ALLOW AND OBJECT TO BE CONSIDERED HELD IF IT HAS A
+ * MASS OF HEAVY OR GREATER AND ITS DIRECT PARENT IS
+ * BEING HELD. IE, A LABEL ON A JAR CAN BE SHOWN BECAUSE
+ * THE JAR IS BEING HELD. */
+ temp = object[index]->PARENT;
+ if (temp > 0 && temp < objects) {
+ if (object[temp]->PARENT == HELD) {
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+ } else {
+ return (FALSE);
+ }
+ } else if (!strcmp(expected, "*location")) {
+ if (object[index]->attributes & LOCATION) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ } else if (!strcmp(expected, "*here") || !strcmp(expected, "**here")) {
+ if (object[index]->PARENT == HERE || index == HERE) {
+ /* THE OBJECT IN QUESTION IS IN THE PLAYER'S CURRENT LOCATION
+ * OR IS THE PLAYER'S CURRENT LOCATION. */
+ return (TRUE);
+ } else if (object[index]->PARENT == HELD) {
+ /* IT IS ONLY A PROBLEM FOR THE OBJECT TO BE AN IMMEDIATE CHILD
+ * OF THE PLAYER. THE PLAYER CAN STILL TAKE AN OBJECT THAT IS IN
+ * SOMETHING THEY ARE CARRYING */
+ return (FALSE);
+ } else {
+ /* IS THE OBJECT A CHILD OF THE CURRENT LOCATION SOMEHOW */
+ return (parent_of(HERE, index, restricted));
+ }
+ } else if (!strcmp(expected, "*anywhere") || !strcmp(expected, "**anywhere")) {
+ return (TRUE);
+ } else if (!strcmp(expected, "*inside") || !strcmp(expected, "**inside")) {
+ if (object_list[0][0] > 0 && object_list[0][0] < objects) {
+ return (parent_of(object_list[0][0], index, restricted));
+ } else {
+ // THERE IS NO PREVIOUS OBJECT SO TREAT THIS LIKE A *here
+ return (parent_of(HERE, index, restricted));
+ }
+ } else if (!strcmp(expected, "*present") || !strcmp(expected, "**present")) {
+ if (index == HERE) {
+ return (TRUE);
+ } else {
+ if (find_parent(index)) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ }
+ } else {
+ unkscorun(expected);
+ return (FALSE);
+ }
+}
+
+int find_parent(int index) {
+ /* THIS FUNCTION WILL SET THE GLOBAL VARIABLE parent TO
+ * THE OBJECT THAT IS AT THE TOP OF THE POSSESSION TREE.
+ * IT WILL RETURN TRUE IF THE OBJECT IS VISIBLE TO THE
+ * PLAYER */
+ //printf("--- find parent of %s\n", object[index]->label);
+
+ if (!(object[index]->attributes & LOCATION) &&
+ object[index]->PARENT != NOWHERE) {
+
+ parent = object[index]->PARENT;
+ //printf("--- parent is %s\n", object[parent]->label);
+
+ if (index == parent) {
+ /* THIS OBJECT HAS ITS PARENT SET TO ITSELF */
+ sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
+ log_error(error_buffer, PLUS_STDOUT);
+ return (FALSE);
+ } else if (!(object[parent]->attributes & LOCATION)
+ && ((object[parent]->attributes & CLOSED && object[parent]->attributes & CONTAINER)
+ || object[parent]->attributes & CONCEALING)) {
+ //printf("--- %s is closed, so return FALSE\n", object[parent]->label);
+ return (FALSE);
+ } else if (parent == HERE || parent == HELD) {
+ /* THE OBJECT IS THE PLAYER'S CURRENT LOCATION OR BEING HELD */
+ return (TRUE);
+ } else {
+ if (object[parent]->attributes & LOCATION) {
+ //printf("--- %s is a location, so dont recuse\n", object[parent]->label);
+ return (FALSE);
+ } else {
+ //printf("--- %s isnt a location, so recuse\n", object[parent]->label);
+ return (find_parent(parent));
+ }
+ }
+ } else {
+ if (index == HERE)
+ /* THE OBJECT IS THE PLAYER'S CURRENT LOCATION. */
+ return (TRUE);
+ else
+ return (FALSE);
+ }
+}
+
+int parent_of(int parent, int child, int restricted) {
+ /* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL
+ * 'PARENT' IS REACHED (RETURN TRUE), OR THE TOP OF THE TREE OR A CLOSED
+ * OR CONCEALING OBJECT IS REACHED (RETURN FALSE). */
+
+ /* restricted ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN AN
+ * OBJECT WITH A mass OF heavy OR LESS THAT IS NOT THE SUPPLIED
+ * PARENT ie. DON'T ACCEPT OBJECTS IN SUB OBJECTS */
+
+ int index;
+
+ //printf("--- parent is %s, child is %s\n", object[parent]->label, object[child]->label);
+ if (child == parent) {
+ return (TRUE);
+ } else if (!(object[child]->attributes & LOCATION) &&
+ object[child]->PARENT != NOWHERE) {
+ /* STORE THE CHILDS PARENT OBJECT */
+ index = object[child]->PARENT;
+ //printf("--- %s is the parent of %s\n", object[index]->label, object[child]->label);
+
+ if (index == child) {
+ /* THIS CHILD HAS IT'S PARENT SET TO ITSELF */
+ sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label);
+ log_error(error_buffer, PLUS_STDOUT);
+ //printf("--- self parent.\n");
+ return (FALSE);
+ } else if (!(object[index]->attributes & LOCATION)
+ && ((object[index]->attributes & CLOSED && object[index]->attributes & CONTAINER)
+ || object[index]->attributes & CONCEALING)) {
+ /* THE CHILDS PARENT IS CLOSED OR CONCEALING - CANT BE SEEN */
+ //printf("--- parent %s is closed\n", object[index]->label);
+ return (FALSE);
+ } else if (restricted && object[index]->MASS < HEAVY && index != parent) {
+ //printf("--- scenery object.\n");
+ return (FALSE);
+ } else {
+ //printf("--- comparing %s with %s\n", object[index]->label, object[parent]->label);
+ if (index == parent) {
+ /* YES, IS PARENT OF CHILD */
+ return (TRUE);
+ } else if (object[index]->attributes & LOCATION) {
+ return (FALSE);
+ } else {
+ /* KEEP LOOKING UP THE TREE TILL THE CHILD HAS NO MORE
+ * PARENTS */
+ return (parent_of(parent, index, restricted));
+ }
+ }
+ } else {
+ /* THE SPECIFIED OBJECT HAS NO PARENT */
+ return (FALSE);
+ }
+}
+
+} // End of namespace JACL
+} // End of namespace Glk