mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1917 lines
62 KiB
C++
1917 lines
62 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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;
|
|
const 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 const 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 = nullptr;
|
|
|
|
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 == nullptr) {
|
|
// 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] != nullptr && pointer != nullptr) {
|
|
//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)) != nullptr) {
|
|
// 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)) != nullptr) {
|
|
// 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 {
|
|
Common::sprintf_s(error_buffer, 1024, 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] != nullptr) {
|
|
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 == nullptr) {
|
|
// 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 == nullptr) {
|
|
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(const 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;
|
|
|
|
Common::strlcpy(base_function, base_name + 1, 81);
|
|
Common::strcat_s(base_function, "_");
|
|
|
|
Common::strlcpy(override_, base_function, 81);
|
|
|
|
Common::strcpy_s(before_function, "+before_");
|
|
Common::strcat_s(before_function, base_name + 1);
|
|
|
|
Common::strcpy_s(after_function, "+after_");
|
|
Common::strcat_s(after_function, base_name + 1);
|
|
|
|
Common::strcpy_s(local_after_function, "after_");
|
|
Common::strcat_s(local_after_function, base_name + 1);
|
|
if (noun[1] != FALSE) {
|
|
Common::strcat_s(local_after_function, "_");
|
|
Common::strcat_s(local_after_function, object[noun[1]]->label);
|
|
}
|
|
if (noun[0] != FALSE) {
|
|
Common::strcat_s(local_after_function, "_");
|
|
Common::strcat_s(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' */
|
|
Common::strcpy_s(default_function, "+default_");
|
|
Common::strcat_s(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 */
|
|
Common::strcat_s(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 POSSIBILITY 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 */
|
|
Common::strcat_s(override_, 81, "override_");
|
|
Common::strcat_s(override_, 81, object[HERE]->label);
|
|
|
|
/* CREATE THE FUNCTION NAME '+func' */
|
|
Common::strcpy_s(base_function, "+");
|
|
Common::strcat_s(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 */
|
|
Common::strcat_s(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 POSSIBILITY 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 */
|
|
Common::strcat_s(override_, 81, "override_");
|
|
Common::strcat_s(override_, 81, object[noun[0]]->label);
|
|
|
|
/* CREATE THE FUNCTION NAME '+func' */
|
|
Common::strcpy_s(base_function, "+");
|
|
Common::strcat_s(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 */
|
|
Common::strcat_s(base_function, object[noun[1]]->label);
|
|
Common::strcat_s(base_function, "_");
|
|
Common::strcat_s(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 POSSIBILITY 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 */
|
|
Common::strcat_s(override_, 81, object[noun[1]]->label);
|
|
Common::strcat_s(override_, 81, "_override_");
|
|
Common::strcat_s(override_, 81, object[noun[0]]->label);
|
|
|
|
/* CREATE THE FUNCTION NAME '+func' */
|
|
Common::strcpy_s(base_function, "+");
|
|
Common::strcat_s(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 OCCURRED IN THE FIRST OBJECT PLACEHOLDER, DON'T
|
|
* TRY ANY OTHERS */
|
|
return (nullptr);
|
|
}
|
|
} while ((iterator = iterator->next_sibling) != nullptr);
|
|
|
|
/* 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 (nullptr);
|
|
}
|
|
|
|
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) != nullptr);
|
|
|
|
/* THERE WERE NO EXACT MATCHES, SO RETURN FALSE */
|
|
return (nullptr);
|
|
}
|
|
|
|
int is_terminator(struct word_type *scope_word) {
|
|
struct word_type *terminator = scope_word->first_child;
|
|
|
|
if (terminator != nullptr) {
|
|
/* 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) != nullptr);
|
|
}
|
|
|
|
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;
|
|
const 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] != nullptr) {
|
|
/* 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] != nullptr && !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 {
|
|
Common::sprintf_s(error_buffer, 1024, 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;
|
|
|
|
//printf("--- trying to add all\n");
|
|
|
|
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 != nullptr) {
|
|
//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) != nullptr);
|
|
}
|
|
|
|
/* LOOP FROM THE CURRENT WORD TO THE NEXT TERMINATOR AND LOOK FOR THE
|
|
* WORD 'FROM' AND STORE THE FOLLOWING OBJECT */
|
|
while (word[wp] != nullptr) {
|
|
//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) {
|
|
Common::sprintf_s(error_buffer, 1024, cstring_resolve("CONTAINER_CLOSED_FEM")->value, sentence_output(from_object, TRUE));
|
|
} else {
|
|
Common::sprintf_s(error_buffer, 1024, 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] == nullptr) {
|
|
/* 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] != nullptr && !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;
|
|
Common::strcpy_s(object_name, word[wp]);
|
|
Common::strcat_s(object_name, " ");
|
|
Common::strcat_s(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] != nullptr) {
|
|
// ADD THE WORDS USED TO error_buffer FOR POSSIBLE USE
|
|
// IN A DISABMIGUATE EMESSAGE
|
|
if (first_word == FALSE) {
|
|
Common::strcat_s(error_buffer, 1024, " ");
|
|
}
|
|
Common::strcat_s(error_buffer, 1024, 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 != nullptr) {
|
|
/* 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) != nullptr);
|
|
}
|
|
|
|
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)
|
|
Common::strcat_s(object_name, " ");
|
|
|
|
Common::strcat_s(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] != nullptr && !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 != nullptr) {
|
|
/* 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 != nullptr) {
|
|
/* 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 isn't 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] != nullptr) {
|
|
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 != nullptr) {
|
|
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;
|
|
Common::sprintf_s(text_buffer, 1024, " [%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] == nullptr)
|
|
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, const 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 */
|
|
Common::sprintf_s(error_buffer, 1024, 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 isn't 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 */
|
|
Common::sprintf_s(error_buffer, 1024, 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 - CAN'T 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
|