mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
These functions are often present, but not reliably portable. They are not defined in ANSI C and while they are likely present in the stdlib.h, the exact behaviour especialy for invalid inputs can vary. Replacing usage of these functions in engines with Common::String::format() is recommended.
2583 lines
50 KiB
C++
2583 lines
50 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/hugo/hugo.h"
|
|
|
|
namespace Glk {
|
|
namespace Hugo {
|
|
|
|
#define STARTS_AS_NUMBER(a) (((a[0]>='0' && a[0]<='9') || a[0]=='-')?1:0)
|
|
|
|
static char EMPTY[1] = { 0 };
|
|
|
|
void Hugo::AddAllObjects(int loc) {
|
|
int i;
|
|
|
|
if (loc==var[player] && domain!=loc)
|
|
return;
|
|
|
|
/* Try to add everything in the specified domain
|
|
to objlist[]
|
|
*/
|
|
for (i=Child(loc); i!=0; i=Sibling(i))
|
|
{
|
|
if (i==var[xobject]) continue;
|
|
|
|
TryObj(i);
|
|
if (domain==0)
|
|
{
|
|
if (Child(i)) AddAllObjects(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hugo::AddObj(int obj) {
|
|
int i;
|
|
|
|
for (i=0; i<objcount; i++)
|
|
{
|
|
if (objlist[i]==obj)
|
|
return;
|
|
}
|
|
|
|
objlist[(int)objcount] = obj;
|
|
if (++objcount> MAXOBJLIST) objcount = MAXOBJLIST;
|
|
}
|
|
|
|
void Hugo::AddPossibleObject(int obj, char type, unsigned int w) {
|
|
int i;
|
|
|
|
if (pobjcount==MAXPOBJECTS)
|
|
return;
|
|
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
/* If it is already in the list */
|
|
if (pobjlist[i].obj==obj)
|
|
{
|
|
/* Being referred to with a noun outweighs being
|
|
referred to previously with an adjective
|
|
*/
|
|
if (type==(char)noun || ObjWordType(obj, w, noun))
|
|
pobjlist[i].type = (char)noun;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Getting this to point is presuming that we're adding an object
|
|
referred to with an adjective, but check just to be sure it isn't
|
|
also a noun for that same object
|
|
*/
|
|
if (ObjWordType(obj, w, noun)) type = (char)noun;
|
|
|
|
pobjlist[pobjcount].obj = obj;
|
|
pobjlist[pobjcount].type = type;
|
|
|
|
pobjcount++;
|
|
#ifdef DEBUG_PARSER
|
|
{
|
|
char buf[100];
|
|
Common::sprintf_s(buf, "AddPossibleObject(%d:\"%s\")", obj, Name(obj));
|
|
Printout(buf);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Hugo::AdvanceGrammar() {
|
|
int a;
|
|
|
|
defseg = gameseg;
|
|
|
|
switch (a = Peek(grammaraddr))
|
|
{
|
|
case FORWARD_SLASH_T:
|
|
case HELD_T:
|
|
case MULTI_T:
|
|
case MULTIHELD_T:
|
|
case ANYTHING_T:
|
|
case NUMBER_T:
|
|
case PARENT_T:
|
|
case NOTHELD_T:
|
|
case MULTINOTHELD_T:
|
|
case WORD_T:
|
|
case OBJECT_T:
|
|
case XOBJECT_T:
|
|
case STRING_T:
|
|
grammaraddr++;
|
|
break;
|
|
|
|
case ASTERISK_T:
|
|
case ATTR_T:
|
|
grammaraddr += 2;
|
|
break;
|
|
|
|
case DICTENTRY_T:
|
|
case ROUTINE_T:
|
|
case OBJECTNUM_T:
|
|
grammaraddr += 3;
|
|
break;
|
|
|
|
case OPEN_BRACKET_T:
|
|
grammaraddr +=5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int Hugo::AnyObjWord(int wn) {
|
|
int i;
|
|
|
|
if (objword_cache[wn])
|
|
return objword_cache[wn];
|
|
|
|
for (i=0; i<objects; i++)
|
|
{
|
|
if (ObjWord(i, wd[wn]))
|
|
{
|
|
return (objword_cache[wn] = 1);
|
|
}
|
|
}
|
|
|
|
return (objword_cache[wn] = -1);
|
|
}
|
|
|
|
int Hugo::Available(int obj, char non_grammar) {
|
|
int temp_stack_depth;
|
|
|
|
if (findobjectaddr)
|
|
{
|
|
passlocal[0] = obj;
|
|
|
|
/* if anything or (Routine) grammar */
|
|
if ((Peek(grammaraddr)==ANYTHING_T
|
|
|| (Peek(grammaraddr)==OPEN_BRACKET_T && Peek(grammaraddr+1)==ROUTINE_T))
|
|
&& non_grammar==0)
|
|
{
|
|
passlocal[1] = 0;
|
|
}
|
|
else
|
|
{
|
|
if (domain > 0)
|
|
passlocal[1] = domain;
|
|
else if (speaking && non_grammar==0)
|
|
passlocal[1] = GrandParent(speaking);
|
|
/* domain of -1 is an explicit 'parent' */
|
|
/*
|
|
else if (domain==-1)
|
|
passlocal[1] = parse_location;
|
|
*/
|
|
else
|
|
passlocal[1] = parse_location;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
PassLocals(2);
|
|
temp_stack_depth = stack_depth;
|
|
|
|
SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
|
|
|
|
#if defined (DEBUGGER)
|
|
DebugRunRoutine((long)findobjectaddr*address_scale);
|
|
#else
|
|
RunRoutine((long)findobjectaddr*address_scale);
|
|
#endif
|
|
retflag = 0;
|
|
stack_depth = temp_stack_depth;
|
|
return ret;
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void Hugo::CallLibraryParse() {
|
|
if (parseaddr)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
Printout("CallLibraryParse()");
|
|
#endif
|
|
parse_called_twice = false;
|
|
|
|
SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
|
|
|
|
ret = 0;
|
|
PassLocals(0);
|
|
#if defined (DEBUGGER)
|
|
DebugRunRoutine((long)parseaddr*address_scale);
|
|
#else
|
|
RunRoutine((long)parseaddr*address_scale);
|
|
#endif
|
|
retflag = 0;
|
|
|
|
/* Returning non-zero return calls the
|
|
engine's Parse routine again.
|
|
*/
|
|
if (ret)
|
|
{
|
|
parse_called_twice = true;
|
|
Parse();
|
|
}
|
|
#ifdef DEBUG_PARSER
|
|
if (ret)
|
|
Printout("CallLibraryParse() returned true");
|
|
else
|
|
Printout("CallLibraryParse() returned false");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int Hugo::DomainObj(int obj) {
|
|
int yes = false;
|
|
|
|
if (obj != var[actor])
|
|
{
|
|
switch (domain)
|
|
{
|
|
case 0:
|
|
case -1:
|
|
{
|
|
if (Parent(obj)==parse_location)
|
|
yes = true;
|
|
else if ((parse_allflag) && GrandParent(obj)==parse_location)
|
|
yes = true;
|
|
else
|
|
{
|
|
if (Parent(obj)==parse_location && !InList(Parent(obj)))
|
|
yes = true;
|
|
}
|
|
|
|
if (Peek(grammaraddr)==MULTINOTHELD_T)
|
|
{
|
|
if (Parent(obj)==var[actor])
|
|
yes = false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if (Parent(obj)==domain)
|
|
yes = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return yes;
|
|
}
|
|
|
|
unsigned int Hugo::FindWord(const char *a) {
|
|
unsigned int ptr = 0;
|
|
int i, p, alen;
|
|
|
|
if (a[0]=='\0')
|
|
return 0;
|
|
|
|
alen = strlen(a);
|
|
|
|
defseg = dicttable;
|
|
|
|
for (i=1; i<=dictcount; i++)
|
|
{
|
|
if (alen==(p = Peek(ptr+2)) && (unsigned char)(MEM(dicttable*16L+ptr+3)-CHAR_TRANSLATION)==(unsigned char)a[0])
|
|
{
|
|
if (!strcmp(GetString(ptr + 2), a))
|
|
{
|
|
defseg = gameseg;
|
|
return ptr;
|
|
}
|
|
}
|
|
|
|
ptr += (p + 1);
|
|
}
|
|
|
|
/* As a last resort, see if the first 6 characters of the word (if it
|
|
has at least six characters) match a dictionary word:
|
|
*/
|
|
if (alen >= 6)
|
|
{
|
|
unsigned int possible = 0;
|
|
int posscount = 0;
|
|
|
|
ptr = 0;
|
|
|
|
for (i=1; i<=dictcount; i++)
|
|
{
|
|
if (alen<=(p = Peek(ptr+2)) && (unsigned char)MEM(dicttable*16L+ptr+3)-CHAR_TRANSLATION==a[0])
|
|
{
|
|
if (!strncmp(GetString(ptr + 2), a, alen))
|
|
{
|
|
/* As long as the dictionary word
|
|
doesn't contain a space */
|
|
if (!strrchr(GetString(ptr+2), ' '))
|
|
{
|
|
possible = ptr;
|
|
posscount++;
|
|
}
|
|
}
|
|
}
|
|
ptr += (p + 1);
|
|
}
|
|
|
|
if (posscount==1)
|
|
return possible;
|
|
}
|
|
|
|
defseg = gameseg;
|
|
|
|
return UNKNOWN_WORD; /* not found */
|
|
}
|
|
|
|
int Hugo::InList(int obj) {
|
|
int i;
|
|
|
|
for (i=0; i<objcount; i++)
|
|
{
|
|
if (objlist[i]==obj)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Hugo::KillWord(int a) {
|
|
int i;
|
|
|
|
if (a>words)
|
|
return;
|
|
|
|
for (i=a; i<words; i++)
|
|
word[i] = word[i+1];
|
|
word[words] = EMPTY;
|
|
|
|
RemoveWord(a);
|
|
words--;
|
|
}
|
|
|
|
int Hugo::MatchCommand() {
|
|
int i, j, flag, a, mw = 0, gotspeaker = 0;
|
|
int wordnum = 0;
|
|
int numverbs = 0;
|
|
bool nextverb = false;
|
|
unsigned int ptr, verbptr, nextgrammar;
|
|
unsigned int obj, propaddr;
|
|
|
|
#ifdef DEBUG_PARSER
|
|
Printout("Entering MatchCommand()");
|
|
#endif
|
|
|
|
odomain = 0;
|
|
|
|
/* Reset these for command-matching */
|
|
if (!speaking)
|
|
{
|
|
var[actor] = var[player];
|
|
parse_location = var[location];
|
|
}
|
|
else
|
|
{
|
|
var[actor] = speaking;
|
|
parse_location = GrandParent(speaking);
|
|
}
|
|
|
|
if (!strcmp(word[1], "~oops"))
|
|
{
|
|
parseerr[0] = '\0';
|
|
|
|
/* "oops" on its own */
|
|
if (words==1 || !strcmp(oops, ""))
|
|
{
|
|
ParseError(16, 0); /* "You'll have to make a mistake..." */
|
|
return 0;
|
|
}
|
|
|
|
/* trying to correct more than one word */
|
|
if (words > 2 || oopscount)
|
|
{
|
|
ParseError(17, 0); /* "...one word at a time..." */
|
|
return 0;
|
|
}
|
|
|
|
/* trying to correct a correction */
|
|
if (!strcmp(Left(errbuf, 5), "~oops"))
|
|
{
|
|
ParseError(13, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* Rebuild the corrected buffer */
|
|
oopscount = 1;
|
|
Common::strcpy_s(line, word[2]);
|
|
for (i=1; i<=(int)strlen(errbuf); i++)
|
|
{
|
|
if (!strcmp(Mid(errbuf, i, strlen(oops)), oops))
|
|
break;
|
|
}
|
|
|
|
Common::strcpy_s(buffer, errbuf);
|
|
buffer[i-1] = '\0';
|
|
Common::strcat_s(buffer, line);
|
|
|
|
Common::strcat_s(buffer, Right(errbuf, strlen(errbuf) - i - strlen(oops) + 1));
|
|
|
|
SeparateWords();
|
|
if (!Parse())
|
|
return 0;
|
|
|
|
CallLibraryParse();
|
|
}
|
|
|
|
if (word[1][0]=='.') KillWord(1);
|
|
|
|
|
|
/*
|
|
* STEP 1: Match verb
|
|
*
|
|
*/
|
|
|
|
ptr = 64;
|
|
|
|
MatchVerb:
|
|
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchCommand(): Step 1");
|
|
#endif
|
|
if (words==0)
|
|
return 0;
|
|
|
|
defseg = gameseg;
|
|
|
|
grammaraddr = 0;
|
|
domain = 0;
|
|
obj_match_state = 0;
|
|
xverb = 0;
|
|
starts_with_verb = 0;
|
|
objcount = 0;
|
|
parse_allflag = false;
|
|
objstart = 0;
|
|
object_is_number = false;
|
|
|
|
for (i=1; i<MAXWORDS; i++)
|
|
objword_cache[i] = 0;
|
|
|
|
var[object] = 0;
|
|
var[xobject] = 0;
|
|
var[self] = 0;
|
|
var[verbroutine] = 0;
|
|
|
|
while ((a = Peek(ptr)) != 255)
|
|
{
|
|
defseg = gameseg;
|
|
|
|
/* verb or xverb header */
|
|
if (a==VERB_T || a==XVERB_T)
|
|
{
|
|
/* Skim through 1 or more verb words */
|
|
numverbs = Peek(ptr + 1);
|
|
verbptr = ptr + 2;
|
|
for (i=1; i<=numverbs; i++)
|
|
{
|
|
/* 0xffff signals something other than a
|
|
dictionary word--see BuildVerb() in hcbuild.c.
|
|
This will be the case, like, 1% of the time.
|
|
*/
|
|
|
|
/* If it is a dictionary word... */
|
|
if (PeekWord(verbptr)!=0xffff)
|
|
{
|
|
/* If one of the verb words matches the first
|
|
word in the input line
|
|
*/
|
|
if (wd[1]==PeekWord(verbptr))
|
|
{
|
|
grammaraddr = ptr;
|
|
goto GotVerb;
|
|
}
|
|
|
|
verbptr += 2;
|
|
}
|
|
|
|
/* ...otherwise assume it's an object (value) */
|
|
else
|
|
{
|
|
codeptr = verbptr + 1; /* skip 0xffff */
|
|
|
|
/* GetVal(), not GetValue(), since it's
|
|
always a simple value
|
|
*/
|
|
obj = GetVal();
|
|
|
|
/* codeptr can't be >65535 on a 16-bit
|
|
compiler
|
|
*/
|
|
verbptr = (unsigned int)codeptr;
|
|
propaddr = PropAddr(obj, noun, 0);
|
|
if (propaddr)
|
|
{
|
|
defseg = proptable;
|
|
a = Peek(propaddr+1); /* obj.#prop */
|
|
defseg = gameseg;
|
|
|
|
for (j=1; j<=a; j++)
|
|
{
|
|
if (wd[1]==(unsigned)GetProp(obj, noun, j, 0))
|
|
{
|
|
grammaraddr = ptr;
|
|
goto GotVerb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Otherwise skip over this verb header */
|
|
ptr += 2 + numverbs * 2;
|
|
}
|
|
|
|
/* anything else */
|
|
else
|
|
ptr += Peek(ptr + 1) + 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* STEP 2: Match object/character (if no verb match)
|
|
*
|
|
*/
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchCommand(): Step 2");
|
|
#endif
|
|
/* If we hit the end of the grammar without finding a verb match: */
|
|
if (Peek(ptr)==255)
|
|
{
|
|
/* If we already tried this once */
|
|
if (gotspeaker)
|
|
{
|
|
if (!starts_with_verb)
|
|
/* "Better start with a verb..." */
|
|
ParseError(2, 0);
|
|
else
|
|
/* "That doesn't make any sense..." */
|
|
ParseError(6, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* See if the command begins with an object
|
|
(character) name:
|
|
*/
|
|
flag = 0;
|
|
if (AnyObjWord(1)==1)
|
|
flag = 1;
|
|
|
|
/* No match, ergo an invalid command */
|
|
if (flag==0 && nextverb==true)
|
|
{
|
|
parseerr[0] = '\0';
|
|
ParseError(6, 0); /* "...doesn't make any sense..." */
|
|
return 0;
|
|
}
|
|
|
|
/* No provision made for addressing objects (characters) */
|
|
if (flag==0 || speaktoaddr==0)
|
|
{
|
|
parseerr[0] = '\0';
|
|
ParseError(2, 0); /* "Better start with a verb..." */
|
|
return 0;
|
|
}
|
|
|
|
/* Count how many object words there are */
|
|
for (i=2; i<=words; i++)
|
|
{
|
|
if (AnyObjWord(i)!=1)
|
|
break;
|
|
}
|
|
|
|
/* Try to match the first word to a valid object */
|
|
objfinish = i - 1;
|
|
obj_match_state = 5;
|
|
i = 1;
|
|
recursive_call = 0;
|
|
|
|
if (MatchObject(&i) != true)
|
|
return 0; /* unsuccessful */
|
|
|
|
speaking = pobj; /* successful */
|
|
gotspeaker = true;
|
|
|
|
/* So erase the object name from the start of the line */
|
|
for (i=1; i<=objfinish; i++)
|
|
KillWord(1);
|
|
if (word[1][0]=='~') KillWord(1);
|
|
|
|
/* If it's a name and that's all...*/
|
|
if (words==0)
|
|
return true;
|
|
|
|
/* ...or else proceed as usual */
|
|
ptr = 64;
|
|
goto MatchVerb;
|
|
}
|
|
else if (!gotspeaker)
|
|
speaking = 0;
|
|
|
|
GotVerb:
|
|
|
|
if (!speaking)
|
|
{
|
|
var[actor] = var[player];
|
|
parse_location = var[location];
|
|
}
|
|
else
|
|
{
|
|
var[actor] = speaking;
|
|
parse_location = GrandParent(speaking);
|
|
}
|
|
|
|
obj_match_state = 0;
|
|
starts_with_verb = 1;
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
|
|
if (Peek(grammaraddr)==XVERB_T) xverb = true;
|
|
grammaraddr += 2 + numverbs * 2;
|
|
|
|
/*
|
|
* STEP 3: Match proper grammar structure (syntax)
|
|
*
|
|
*/
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchCommand(): Step 3");
|
|
#endif
|
|
|
|
/*
|
|
* (STEP 4: We'll be matching xobject, if any, before object)
|
|
*
|
|
*/
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchCommand(): Step 4");
|
|
#endif
|
|
|
|
/* Loop until end of grammar table, or next verb:
|
|
*/
|
|
while (Peek(grammaraddr)!=255 &&
|
|
Peek(grammaraddr)!=VERB_T && Peek(grammaraddr)!=XVERB_T)
|
|
{
|
|
wordnum = 1;
|
|
|
|
nextgrammar = grammaraddr + Peek(grammaraddr + 1) + 1;
|
|
|
|
/* Loop until end of table or next verb: */
|
|
while (Peek(grammaraddr) != 255 && Peek(grammaraddr) != VERB_T && Peek(grammaraddr) != XVERB_T)
|
|
{
|
|
mw = MatchWord(&wordnum);
|
|
|
|
if (mw==1)
|
|
{
|
|
/* end of both input and grammar */
|
|
if (wd[wordnum]==0 && Peek(grammaraddr)==ROUTINE_T)
|
|
{
|
|
full_buffer = (byte)wordnum;
|
|
break;
|
|
}
|
|
|
|
/* end of grammar, not input */
|
|
if (Peek(grammaraddr)==ROUTINE_T)
|
|
{
|
|
mw = false;
|
|
goto NextStructure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If error already signalled */
|
|
if (mw==2)
|
|
return 0;
|
|
|
|
/* No match, so try next structure */
|
|
else
|
|
{
|
|
NextStructure:
|
|
grammaraddr = nextgrammar;
|
|
var[object] = 0;
|
|
var[xobject] = 0;
|
|
var[verbroutine] = 0;
|
|
domain = 0;
|
|
odomain = 0;
|
|
obj_match_state = 0;
|
|
xverb = 0;
|
|
objcount = 0;
|
|
objstart = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Matched the complete syntax of a verb */
|
|
if (mw==1)
|
|
{
|
|
var[verbroutine] = PeekWord(grammaraddr + 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mw != 1)
|
|
{
|
|
if (mw==0) /* mw = 2 if error already printed */
|
|
{
|
|
/* If there's more grammar to check... */
|
|
if (Peek(grammaraddr) != 255)
|
|
{
|
|
ptr = grammaraddr;
|
|
nextverb = true;
|
|
goto MatchVerb;
|
|
}
|
|
|
|
/* ...or if we reached the end without a sensible
|
|
syntax matched:
|
|
*/
|
|
parseerr[0] = '\0';
|
|
|
|
/* "...doesn't make any sense..." */
|
|
ParseError(6, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* STEP 5: Match remaining object(s), if any
|
|
*
|
|
*/
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchCommand(): Step 5");
|
|
#endif
|
|
|
|
/* If there are objects waiting to be loaded into objlist[] */
|
|
if (objstart)
|
|
{
|
|
ResetFindObject();
|
|
|
|
obj_match_state = 2;
|
|
recursive_call = false;
|
|
if (odomain) domain = odomain;
|
|
|
|
grammaraddr = objgrammar;
|
|
i = objstart;
|
|
|
|
/* If there was a problem matching them */
|
|
if (MatchObject(&i)==false)
|
|
return 0;
|
|
}
|
|
|
|
/* Got a successfully matched verb with all the requisite
|
|
parameters (if any).
|
|
*/
|
|
|
|
if (words < wordnum)
|
|
remaining = 0;
|
|
else
|
|
remaining = (char)(words - wordnum);
|
|
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchCommand(): Leaving");
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool Hugo::MatchObject(int *wordnum) {
|
|
char found_noun = false;
|
|
int i, j, k, m, flag;
|
|
int mobjs; unsigned int mobj[MAX_MOBJ];
|
|
bool allmatch;
|
|
int roomloc;
|
|
int bestobj = 0;
|
|
char bestavail = 0;
|
|
|
|
/* If this is a recursive call, we're not adding new objects */
|
|
if (!recursive_call) addflag = true;
|
|
|
|
stack_depth = 0;
|
|
|
|
pobj = 0; /* possible object */
|
|
objcount = 0; /* of objlist[] */
|
|
pobjcount = 0; /* of pobjlist[] */
|
|
mobjs = 0; /* # of previous words in phrase */
|
|
bestavail = 0; /* adjective or noun */
|
|
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchObject(): Entering");
|
|
#endif
|
|
parseerr[0] = '\0';
|
|
|
|
do /* starting at word #a */
|
|
{
|
|
/* Check first to make sure it's not a housekeeping word
|
|
such as "~and" or "~all".
|
|
*/
|
|
if (word[*wordnum][0]!='~' && word[*wordnum][0]!='\0')
|
|
{
|
|
if (parseerr[0]!='\0') Common::strcat_s(parseerr, " ");
|
|
Common::strcat_s(parseerr, word[*wordnum]);
|
|
|
|
flag = 0;
|
|
for (i=0; i<objects; i++)
|
|
{
|
|
if (wd[*wordnum]==0)
|
|
break;
|
|
|
|
/* Might be this object if wd[*wordnum] is an
|
|
adjective or noun of object i
|
|
*/
|
|
m = ObjWord(i, wd[*wordnum]);
|
|
|
|
if (m)
|
|
{
|
|
flag = true;
|
|
allmatch = true;
|
|
|
|
/* check previously matched words */
|
|
for (j=1; j<=mobjs; j++)
|
|
{
|
|
if (!ObjWord(i, mobj[j])) /* || wd[*wordnum]==mobj[j]) */
|
|
allmatch = false;
|
|
}
|
|
|
|
/* matches all previous words */
|
|
if (allmatch==true)
|
|
{
|
|
AddPossibleObject(i, (char)m, wd[*wordnum]);
|
|
pobj = i;
|
|
if (bestavail==0 || bestavail>=(char)m)
|
|
{
|
|
if (!bestobj)
|
|
bestobj = i;
|
|
bestavail = (char)m;
|
|
}
|
|
}
|
|
|
|
/* doesn't match previous words */
|
|
else
|
|
SubtractPossibleObject(i);
|
|
}
|
|
|
|
/* definitely not this object */
|
|
else
|
|
SubtractPossibleObject(i);
|
|
}
|
|
|
|
|
|
/* If checking the start of an input line, i.e. for
|
|
a command addressed to an object (character):
|
|
*/
|
|
if (obj_match_state==5 && !flag) goto Clarify;
|
|
}
|
|
|
|
else if (!strcmp(word[*wordnum], "~any"))
|
|
goto NextLoop;
|
|
|
|
/* "~and", "~all",... */
|
|
else
|
|
goto Clarify;
|
|
|
|
/* Didn't get any suspects */
|
|
if (pobjcount==0)
|
|
{
|
|
/* If "~and", "~all",... */
|
|
if (word[*wordnum][0]=='~')
|
|
{
|
|
/* If checking the xobject */
|
|
if (obj_match_state==1)
|
|
{
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
/* "...can't use multiple objects..."
|
|
(as indirect objects) */
|
|
ParseError(7, 0);
|
|
return false;
|
|
}
|
|
goto Clarify;
|
|
}
|
|
|
|
/* Got an unmatchable sequence of words */
|
|
else
|
|
{
|
|
if (obj_match_state==5)
|
|
{
|
|
if (!starts_with_verb)
|
|
/* "Better start with a verb..." */
|
|
ParseError(2, 0);
|
|
else
|
|
/* "That doesn't make any sense..." */
|
|
ParseError(6, 0);
|
|
}
|
|
else
|
|
/* "(no such thing)..." */
|
|
ParseError(5, 0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (word[*wordnum][0]!='~')
|
|
{
|
|
/* Go back for next word in this object phrase */
|
|
|
|
mobjs++;
|
|
if (mobjs==MAX_MOBJ)
|
|
{
|
|
/* "(no such thing)..." */
|
|
ParseError(5, 0);
|
|
return false;
|
|
}
|
|
mobj[mobjs] = wd[*wordnum];
|
|
}
|
|
else
|
|
{
|
|
/* Since hitting "~and" or "~all", we've obviously
|
|
finished an object phrase
|
|
*/
|
|
(*wordnum)++;
|
|
goto Clarify;
|
|
}
|
|
|
|
NextLoop:
|
|
(*wordnum)++; /* next word */
|
|
if ((*wordnum > words || word[*wordnum][0]=='\0')
|
|
|| *wordnum > objfinish)
|
|
goto Clarify;
|
|
}
|
|
while (true); /* endless loop */
|
|
|
|
|
|
Clarify:
|
|
|
|
#ifdef DEBUG_PARSER
|
|
Printout("MatchObject(): Clarify");
|
|
#endif
|
|
/* If "~and", "~all",... */
|
|
if (word[*wordnum][0]=='~')
|
|
{
|
|
/* If checking the xobject or addressing a command */
|
|
if (obj_match_state==1 || speaking)
|
|
{
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
/* "...can't use multiple objects..."
|
|
(as indirect objects) */
|
|
ParseError(7, 0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(word[*wordnum], "~all")) /* if "~all" is specified */
|
|
{
|
|
parse_allflag = true;
|
|
|
|
/* If one or more words were already matched, however... */
|
|
|
|
if (mobjs > 0)
|
|
{
|
|
ParseError(6, 0); /* "...doesn't make any sense..." */
|
|
return false;
|
|
}
|
|
|
|
if (!domain) /* no particular domain object specified */
|
|
roomloc = parse_location;
|
|
else
|
|
roomloc = domain;
|
|
|
|
AddAllObjects(roomloc);
|
|
|
|
(*wordnum)++;
|
|
|
|
|
|
/* Done processing the object phrase yet? */
|
|
|
|
/* only >GET ALL EXCEPT... if we're not done the object phrase */
|
|
if (*wordnum<=objfinish && strcmp(word[*wordnum], "~except"))
|
|
{
|
|
ParseError(6, 0); /* "Doesn't make any sense..." */
|
|
return false;
|
|
}
|
|
|
|
if ((*wordnum > words || word[*wordnum][0]=='\0')
|
|
|| (obj_match_state != 1 && *wordnum >= objfinish))
|
|
{
|
|
if (!objcount && !speaking)
|
|
{
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
ParseError(9, 0); /* "Nothing to (verb)..." */
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Go back for the next piece of the phrase */
|
|
pobjcount = 0;
|
|
(*wordnum)--;
|
|
goto NextLoop;
|
|
}
|
|
|
|
|
|
/* If we have a possible object or set of objects, go through the
|
|
disqualification process, either to sort out any confusion, or
|
|
even if there's only one possible object, to make sure it's
|
|
available
|
|
*/
|
|
if (pobjcount >= 1)
|
|
{
|
|
bestavail = 0;
|
|
|
|
for (k=0; k<pobjcount; k++) /* disqualify if unavailable */
|
|
{
|
|
i = pobjlist[k].obj;
|
|
|
|
/* held or multiheld */
|
|
if ((domain) && domain==var[actor])
|
|
{
|
|
if (Parent(i) != var[actor])
|
|
{
|
|
SubtractPossibleObject(i);
|
|
k--;
|
|
}
|
|
else
|
|
pobj = i;
|
|
}
|
|
|
|
else if ((Peek(grammaraddr)==NOTHELD_T || Peek(grammaraddr)==MULTINOTHELD_T)
|
|
&& Parent(i)==var[actor])
|
|
{
|
|
SubtractPossibleObject(i);
|
|
k--;
|
|
|
|
/* if this was the last suspect */
|
|
if (pobjcount<1) /* i.e., 0 */
|
|
{
|
|
ParseError(11, i); /* "You don't see that..." */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* otherwise */
|
|
else if (Available(i, 0)==false) /* and obj_match_state!=5) */
|
|
{
|
|
SubtractPossibleObject(i);
|
|
k--;
|
|
}
|
|
else
|
|
pobj = i;
|
|
|
|
/* Try to determine the best available object */
|
|
if ((!bestavail) || (bestavail==(char)adjective && pobjlist[k].type==(char)noun))
|
|
{
|
|
m = domain;
|
|
/* Temporary parent domain */
|
|
domain = -1;
|
|
if (Available(i, 0))
|
|
{
|
|
bestavail = pobjlist[k].type;
|
|
bestobj = i;
|
|
}
|
|
domain = m;
|
|
}
|
|
|
|
/* Pick a default (poor) best match */
|
|
if (!bestobj)
|
|
bestobj = i;
|
|
}
|
|
|
|
|
|
/* Disqualify if an object is less exact--i.e., if the
|
|
word is a noun for one object but only an adjective for
|
|
another, disqualify the one for which it is an
|
|
adjective.
|
|
*/
|
|
if (pobjcount > 1)
|
|
{
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
if (pobjlist[i].type==(char)noun)
|
|
found_noun = true;
|
|
}
|
|
|
|
if (found_noun)
|
|
{
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
/* Use ObjWord()'s test to see if this word
|
|
should get upgraded from an adjective to a noun
|
|
*/
|
|
if (pobjlist[i].type==(char)adjective)
|
|
{
|
|
if (ObjWordType(pobjlist[i].obj, wd[*wordnum-1], noun))
|
|
pobjlist[i].type = (char)noun;
|
|
}
|
|
|
|
if (pobjlist[i].type==(char)adjective)
|
|
{
|
|
SubtractPossibleObject(pobjlist[i].obj);
|
|
i--;
|
|
}
|
|
|
|
if (pobjcount==1) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* If we're dealing with an 'anything' object, prefer an
|
|
object that's currently available to one that isn't
|
|
*/
|
|
if (pobjcount > 1 && Peek(grammaraddr)==ANYTHING_T)
|
|
{
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
if (Available(pobjlist[i].obj, true))
|
|
{
|
|
for (j=0; j<pobjcount; j++)
|
|
{
|
|
if (j!=i)
|
|
{
|
|
if (!Available(pobjlist[j].obj, true))
|
|
{
|
|
SubtractPossibleObject(pobjlist[j].obj);
|
|
j--; /* don't skip one */
|
|
if (pobjcount<=1) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (pobjcount<=1) break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Use whatever disambiguation the FindObject routine might provide */
|
|
if (pobjcount > 1 && findobjectaddr)
|
|
{
|
|
for (k=0; k<pobjcount; k++)
|
|
{
|
|
i = pobjlist[k].obj;
|
|
ret = 0;
|
|
passlocal[0] = i;
|
|
passlocal[1] = 0;
|
|
PassLocals(2);
|
|
|
|
SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
|
|
#if defined (DEBUGGER)
|
|
DebugRunRoutine((long)findobjectaddr*address_scale);
|
|
#else
|
|
RunRoutine((long)findobjectaddr*address_scale);
|
|
#endif
|
|
retflag = 0;
|
|
|
|
if (ret==0) /* returned false */
|
|
{
|
|
SubtractPossibleObject(i);
|
|
k--; /* so we don't skip one */
|
|
if (pobjcount<=1) break;
|
|
}
|
|
else
|
|
{
|
|
bestobj = i;
|
|
pobj = i;
|
|
}
|
|
|
|
if (pobjcount==1) break;
|
|
}
|
|
}
|
|
|
|
|
|
/* If this is a clarification-required call to MatchObject(),
|
|
return true if more than one possible object is found.
|
|
*/
|
|
if (pobjcount > 1 && recursive_call)
|
|
return true;
|
|
|
|
|
|
/* On the other hand, if we've managed to disqualify
|
|
everything:
|
|
*/
|
|
if (pobjcount==0 && !speaking)
|
|
{
|
|
if (obj_match_state==5)
|
|
{
|
|
if (!starts_with_verb)
|
|
/* "Better start with a verb..." */
|
|
ParseError(2, 0);
|
|
else
|
|
/* "That doesn't make any sense..." */
|
|
ParseError(6, 0);
|
|
}
|
|
else
|
|
{
|
|
if ((!domain) || domain != var[actor])
|
|
{
|
|
if (Peek(grammaraddr)==ANYTHING_T)
|
|
/* "...haven't seen..." */
|
|
ParseError(10, bestobj);
|
|
else if (domain)
|
|
/* "...don't see that there..." */
|
|
ParseError(14, bestobj);
|
|
else
|
|
/* "...don't see any..." */
|
|
ParseError(11, bestobj);
|
|
}
|
|
else
|
|
ParseError(15, bestobj); /* "...don't have any..." */
|
|
}
|
|
return false;
|
|
}
|
|
else if (pobjcount==0)
|
|
pobj = bestobj;
|
|
|
|
|
|
/* If, after all this, there still exists some confusion
|
|
about what object is meant:
|
|
*/
|
|
if (pobjcount > 1)
|
|
{
|
|
int wtemp;
|
|
unsigned int wdtemp[MAXWORDS+1];
|
|
int tempobjlist[MAXWORDS+1], tempobjcount;
|
|
struct pobject_structure temppobjlist[MAXPOBJECTS];
|
|
int tempobjfinish;
|
|
int temppobjcount;
|
|
bool tempaddflag;
|
|
|
|
char tempxverb = xverb;
|
|
ParseError(8, 0); /* "Which...do you mean..." */
|
|
xverb = tempxverb;
|
|
|
|
for (i=1; i<=words; i++) /* save word arrays */
|
|
wdtemp[i] = wd[i];
|
|
wtemp = words;
|
|
tempobjfinish = objfinish;
|
|
|
|
for (i=0; i<=objcount; i++) /* save object arrays */
|
|
tempobjlist[i] = objlist[i];
|
|
for (i=0; i<=pobjcount; i++)
|
|
temppobjlist[i] = pobjlist[i];
|
|
|
|
tempobjcount = objcount;
|
|
temppobjcount = pobjcount;
|
|
tempaddflag = addflag;
|
|
|
|
|
|
/* Get a new input and properly parse it; after
|
|
all, it may be returned to MatchCommand() as
|
|
a potentially valid command.
|
|
*/
|
|
GetCommand();
|
|
SeparateWords();
|
|
if (words==0)
|
|
{
|
|
ParseError(0, 1);
|
|
return false;
|
|
}
|
|
if (!Parse())
|
|
return false;
|
|
|
|
objfinish = words;
|
|
|
|
/* Do we not care? i.e. is "any", "either", etc. given? */
|
|
if (!strcmp(word[1], "~any") && words==1)
|
|
{
|
|
for (k=0; k<pobjcount; k++)
|
|
{
|
|
i = pobjlist[k].obj;
|
|
|
|
if (strcmp(Name(i), ""))
|
|
{
|
|
pobj = i;
|
|
Common::sprintf_s(line, "(%s)", Name(i));
|
|
AP(line);
|
|
goto RestoreTempArrays;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check to see if any object is disqualified by
|
|
any one of the words.
|
|
*/
|
|
for (i=1; i<=words; i++)
|
|
{
|
|
if (word[i][0]!='~')
|
|
{
|
|
for (j=0; j<pobjcount; j++)
|
|
{
|
|
if (!ObjWord(pobjlist[j].obj, wd[i]))
|
|
{
|
|
SubtractPossibleObject(pobjlist[j].obj);
|
|
j--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (pobjcount==1)
|
|
{
|
|
pobj = pobjlist[0].obj;
|
|
full_buffer = 0;
|
|
goto RestoreTempArrays;
|
|
}
|
|
|
|
/* Check to see if the first word is a noun
|
|
or adjective for any of the possible suspect objects.
|
|
*/
|
|
flag = 0;
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
if (ObjWord(pobjlist[i].obj, wd[1]))
|
|
{
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!strcmp(word[1], "~all"))
|
|
flag = 1;
|
|
|
|
/* If not, tell MatchCommand() that there's a
|
|
new command coming down the hopper.
|
|
*/
|
|
if (flag==0)
|
|
{
|
|
full_buffer = 1;
|
|
return false;
|
|
}
|
|
|
|
/* Here, tell MatchObject()--this function--
|
|
that this isn't a virgin call.
|
|
*/
|
|
recursive_call = true;
|
|
addflag = true;
|
|
i = 1;
|
|
j = MatchObject(&i);
|
|
if (j==false) /* parsing error */
|
|
{
|
|
full_buffer = 0;
|
|
return false;
|
|
}
|
|
else if (j==-1) /* multiple matches found */
|
|
{
|
|
/* See if a single entered word matches
|
|
exactly the name of only one object
|
|
*/
|
|
if (words==1)
|
|
{
|
|
flag = 0;
|
|
for (j=0; j<temppobjcount; j++)
|
|
{
|
|
if (wd[1]==(unsigned int)GetProp(temppobjlist[j].obj, 0, 1, 0))
|
|
{
|
|
pobj = temppobjlist[j].obj;
|
|
flag++;
|
|
}
|
|
}
|
|
if (flag==1)
|
|
{
|
|
full_buffer = 0;
|
|
goto RestoreTempArrays;
|
|
}
|
|
}
|
|
|
|
/* Now, even if we weren't able to find a
|
|
match, check to see if the last word
|
|
belongs to only one of the possible
|
|
suspects, especially if it's a noun.
|
|
*/
|
|
flag = 0;
|
|
bestobj = 0;
|
|
i = words;
|
|
while (wd[i]==0) i--;
|
|
for (j=0; j<temppobjcount; j++)
|
|
{
|
|
if ((m = ObjWord(temppobjlist[j].obj, wd[i])) != 0)
|
|
{
|
|
if (!bestobj || m<=bestobj)
|
|
{
|
|
flag++;
|
|
bestobj = m;
|
|
pobj = temppobjlist[j].obj;
|
|
}
|
|
}
|
|
}
|
|
if (flag != 1)
|
|
{
|
|
/* "You'll have to be more specific..." */
|
|
ParseError(13, 0);
|
|
full_buffer = 0;
|
|
return false;
|
|
}
|
|
full_buffer = 0;
|
|
}
|
|
|
|
RestoreTempArrays:
|
|
/* Rebuild <buffer> and word[] array */
|
|
buffer[0] = '\0';
|
|
for (i=1; i<=wtemp; i++)
|
|
{
|
|
Common::strcat_s(buffer, GetWord(wdtemp[i]));
|
|
Common::strcat_s(buffer, " ");
|
|
}
|
|
SeparateWords();
|
|
|
|
/* Restore wd[] array after SeparateWords() */
|
|
for (i=1; i<=wtemp; i++)
|
|
wd[i] = wdtemp[i];
|
|
words = wtemp;
|
|
objfinish = tempobjfinish;
|
|
|
|
/* Restore object lists */
|
|
for (i=0; i<temppobjcount; i++)
|
|
pobjlist[i] = temppobjlist[i];
|
|
pobjcount = temppobjcount;
|
|
|
|
/* Multiple disambig. results for a non-multi verb? */
|
|
i = Peek(grammaraddr);
|
|
if (objcount>1 && i!=MULTI_T && i!=MULTIHELD_T && i!=MULTINOTHELD_T)
|
|
{
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
/* "You can't...multiple objects." */
|
|
ParseError(3, 0);
|
|
return false;
|
|
}
|
|
|
|
/* Check objlist against original pobjlist */
|
|
for (i=0; i<objcount; i++)
|
|
{
|
|
flag = 0;
|
|
for (j=0; j<pobjcount; j++)
|
|
{
|
|
if (pobjlist[j].obj==objlist[i])
|
|
flag = true;
|
|
}
|
|
if (!flag)
|
|
{
|
|
/* "You'll have to be more specific..." */
|
|
ParseError(13, 0);
|
|
full_buffer = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Check pobjlist against disambiguation objlist */
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
flag = 0;
|
|
for (j=0; j<objcount; j++)
|
|
{
|
|
if (pobjlist[i].obj==objlist[j])
|
|
flag = true;
|
|
}
|
|
if (!flag)
|
|
{
|
|
SubtractPossibleObject(pobjlist[i].obj);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<=tempobjcount; i++)
|
|
objlist[i] = tempobjlist[i];
|
|
objcount = (char)tempobjcount;
|
|
addflag = (char)tempaddflag;
|
|
|
|
if (word[*wordnum][0]=='~') (*wordnum)--;
|
|
}
|
|
}
|
|
|
|
/* Rule out "noun noun" and "noun adjective" combinations */
|
|
|
|
i = 0; /* count nouns */
|
|
k = 10; /* best (i.e., lowest) match */
|
|
for (j=1; j<=mobjs; j++)
|
|
{
|
|
if ((m = ObjWord(pobj, mobj[j]))==noun)
|
|
{
|
|
k = noun;
|
|
i++;
|
|
}
|
|
else if (k==noun) i = 2;
|
|
}
|
|
|
|
/* Can't use more than one noun for a given object */
|
|
if (i > 1)
|
|
{
|
|
/* "...no such thing" */
|
|
ParseError(5, 0);
|
|
return false;
|
|
}
|
|
|
|
/* Check finally to make sure it's valid */
|
|
ResetFindObject();
|
|
if (!ValidObj(pobj))
|
|
return false;
|
|
|
|
/* Finally--now add it or subtract it from the list, as
|
|
appropriate:
|
|
*/
|
|
if (addflag==true)
|
|
{
|
|
if (pobjcount)
|
|
{
|
|
for (i=0; i<pobjcount; i++)
|
|
AddObj(pobjlist[i].obj);
|
|
}
|
|
|
|
/* backup */
|
|
else if (pobj != 0)
|
|
AddObj(pobj);
|
|
}
|
|
else
|
|
SubtractObj(pobj);
|
|
|
|
/* If the object wasn't where it was specifically claimed to be,
|
|
applies to second pass through object phrase(s) after xobject
|
|
is found (since <obj_match_state> is 2):
|
|
*/
|
|
if (obj_match_state==2 && domain != 0 &&
|
|
Parent(pobj) != domain && pobj != 0 && !speaking)
|
|
{
|
|
if (domain==var[player])
|
|
ParseError(15, pobj); /* "You don't have any..." */
|
|
else
|
|
ParseError(14, pobj); /* "You don't see any...there..." */
|
|
return false;
|
|
}
|
|
|
|
if (!strcmp(word[*wordnum], "~except"))
|
|
{
|
|
if (obj_match_state==1)
|
|
{
|
|
ParseError(7, 0); /* can't have multiple xobjects */
|
|
return false;
|
|
}
|
|
addflag = false;
|
|
}
|
|
|
|
if ((strcmp(word[*wordnum], "~and") && strcmp(word[*wordnum], "~except"))
|
|
|| obj_match_state==5)
|
|
{
|
|
/* At the end yet? */
|
|
if ((*wordnum > words || word[*wordnum][0]=='\0')
|
|
|| *wordnum > objfinish)
|
|
{
|
|
if ((objcount > 0 && pobj != 0) || recursive_call || speaking)
|
|
return true;
|
|
else
|
|
{
|
|
/* No objects found */
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
ParseError(9, 0); /* "Nothing to (verb)..." */
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Go back for the next object phrase */
|
|
pobjcount = 0;
|
|
mobjs = 0;
|
|
parseerr[0] = '\0';
|
|
|
|
goto NextLoop;
|
|
}
|
|
|
|
int Hugo::MatchWord(int *wordnum) {
|
|
char num[18];
|
|
int i, p, t, flag, finish;
|
|
unsigned int thissyntax, nextsyntax;
|
|
|
|
if (wd[*wordnum]==0)
|
|
return 0;
|
|
|
|
switch ((t = Peek(grammaraddr)))
|
|
{
|
|
/* the verb ("*") */
|
|
case ASTERISK_T:
|
|
(*wordnum)++;
|
|
AdvanceGrammar();
|
|
return 1;
|
|
|
|
/* a non-specific dictionary word */
|
|
case WORD_T:
|
|
if (obj_match_state==1)
|
|
var[xobject] = wd[*wordnum];
|
|
else
|
|
{
|
|
var[object] = wd[*wordnum];
|
|
obj_match_state = 1;
|
|
}
|
|
object_is_number = true;
|
|
(*wordnum)++;
|
|
AdvanceGrammar();
|
|
return 1;
|
|
|
|
/* a specific dictionary entry */
|
|
case DICTENTRY_T:
|
|
CheckWord:
|
|
/* matches word */
|
|
if (wd[*wordnum]==PeekWord(grammaraddr + 1))
|
|
{
|
|
(*wordnum)++;
|
|
AdvanceGrammar();
|
|
while (Peek(grammaraddr)==9)
|
|
grammaraddr += 4;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* if next word is a "/" */
|
|
if (Peek(grammaraddr + 3)==FORWARD_SLASH_T)
|
|
{
|
|
AdvanceGrammar(); /* this word */
|
|
AdvanceGrammar(); /* "/" */
|
|
goto CheckWord;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* alternative dictionary words */
|
|
case FORWARD_SLASH_T:
|
|
grammaraddr++;
|
|
return 1;
|
|
|
|
/* a number */
|
|
case NUMBER_T:
|
|
if ((STARTS_AS_NUMBER(word[*wordnum])) &&
|
|
!strcmp(hugo_itoa(atoi(word[*wordnum]), num, 10, sizeof(num)), word[*wordnum]))
|
|
{
|
|
if (obj_match_state==1)
|
|
var[xobject] = atoi(word[*wordnum]);
|
|
else
|
|
{
|
|
var[object] = atoi(word[*wordnum]);
|
|
obj_match_state = 1;
|
|
}
|
|
object_is_number = true;
|
|
AdvanceGrammar();
|
|
(*wordnum)++;
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
/* a string enclosed in quotes */
|
|
case STRING_T:
|
|
if (parsestr[0]=='\"')
|
|
{
|
|
AdvanceGrammar();
|
|
(*wordnum)++;
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
default:
|
|
{
|
|
|
|
/* Some manifestation of an object (or objects) before domain is
|
|
found, since <obj_match_state> is initially set to 0:
|
|
*/
|
|
if (obj_match_state==0)
|
|
{
|
|
if (Peek(grammaraddr)==HELD_T || Peek(grammaraddr)==MULTIHELD_T)
|
|
odomain = var[actor];
|
|
|
|
obj_match_state = 1; /* since next set of object words
|
|
must be the xobject */
|
|
objstart = *wordnum;
|
|
objgrammar = grammaraddr;
|
|
|
|
while (wd[*wordnum] != 0)
|
|
{
|
|
finish = *wordnum;
|
|
|
|
/* Check what's coming up in case it's a dictionary
|
|
word--which would override an object phrase.
|
|
*/
|
|
thissyntax = grammaraddr;
|
|
AdvanceGrammar();
|
|
nextsyntax = grammaraddr;
|
|
grammaraddr = thissyntax;
|
|
|
|
/* dictionary word or string */
|
|
CheckWordorString:
|
|
p = Peek(nextsyntax);
|
|
if (p==DICTENTRY_T || p==STRING_T)
|
|
{
|
|
if ((PeekWord(nextsyntax + 1)==wd[*wordnum]) ||
|
|
(p==STRING_T && wd[*wordnum]==UNKNOWN_WORD))
|
|
{
|
|
grammaraddr = nextsyntax;
|
|
if (*wordnum != objstart)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
else if (Peek(nextsyntax + 3)==FORWARD_SLASH_T)
|
|
{
|
|
thissyntax = grammaraddr;
|
|
grammaraddr = nextsyntax + 3;
|
|
AdvanceGrammar();
|
|
nextsyntax = grammaraddr;
|
|
grammaraddr = thissyntax;
|
|
|
|
goto CheckWordorString;
|
|
}
|
|
}
|
|
|
|
/* or a number */
|
|
else if ((p==NUMBER_T && STARTS_AS_NUMBER(word[*wordnum])) &&
|
|
!strcmp(word[*wordnum], hugo_itoa(atoi(word[*wordnum]), num, 10, sizeof(num))))
|
|
{
|
|
grammaraddr = nextsyntax;
|
|
if (*wordnum != objstart)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Pass over any object words--they'll be matched
|
|
specifically later in MatchCommand().
|
|
*/
|
|
flag = 0;
|
|
if (AnyObjWord(*wordnum)==1)
|
|
{
|
|
(*wordnum)++;
|
|
flag = 1;
|
|
}
|
|
|
|
/* if "~and", "~all",... */
|
|
if (word[*wordnum][0]=='~')
|
|
{
|
|
int multicheck = Peek(grammaraddr);
|
|
|
|
(*wordnum)++;
|
|
flag = 1;
|
|
|
|
/* multi or multi(something) */
|
|
if (multicheck != MULTI_T &&
|
|
multicheck != MULTIHELD_T &&
|
|
multicheck != MULTINOTHELD_T)
|
|
{
|
|
Common::strcpy_s(parseerr, word[1]);
|
|
/* "You can't...multiple objects." */
|
|
ParseError(3, 0);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
objfinish = finish;
|
|
|
|
if (flag==0)
|
|
return 0;
|
|
}
|
|
|
|
AdvanceGrammar();
|
|
return 1;
|
|
}
|
|
|
|
/* hitting xobject */
|
|
|
|
else if (obj_match_state==1)
|
|
{
|
|
int temp_objfinish = objfinish;
|
|
|
|
/* If we don't know the verbroutine, try to figure it out */
|
|
if (var[verbroutine]==0)
|
|
{
|
|
thissyntax = grammaraddr;
|
|
AdvanceGrammar();
|
|
nextsyntax = grammaraddr;
|
|
grammaraddr = thissyntax;
|
|
if (Peek(nextsyntax)==ROUTINE_T)
|
|
{
|
|
var[verbroutine] = PeekWord(nextsyntax+1);
|
|
}
|
|
}
|
|
|
|
p = Peek(grammaraddr);
|
|
|
|
/* If the xobject is specifically a parent of the
|
|
object(s) to be matched later:
|
|
*/
|
|
if (p==PARENT_T) domain = -1;
|
|
|
|
/* Also deal with held xobjects */
|
|
t = domain;
|
|
if (p==HELD_T || p==MULTIHELD_T)
|
|
domain = var[actor];
|
|
|
|
/* Figure out where this xobject must end, as per grammar */
|
|
objfinish = -1;
|
|
if (*wordnum < words)
|
|
{
|
|
thissyntax = grammaraddr;
|
|
AdvanceGrammar();
|
|
nextsyntax = grammaraddr;
|
|
grammaraddr = thissyntax;
|
|
|
|
CheckXobjectFinish:
|
|
p = Peek(nextsyntax);
|
|
for (i=*wordnum+1; i<=words; i++)
|
|
{
|
|
if (p==DICTENTRY_T || p==STRING_T)
|
|
{
|
|
if ((PeekWord(nextsyntax + 1)==wd[i]) ||
|
|
(p==STRING_T && wd[i]==UNKNOWN_WORD))
|
|
{
|
|
objfinish = i-1;
|
|
break;
|
|
}
|
|
}
|
|
else if (Peek(nextsyntax + 3)==FORWARD_SLASH_T)
|
|
{
|
|
thissyntax = grammaraddr;
|
|
grammaraddr = nextsyntax + 3;
|
|
AdvanceGrammar();
|
|
nextsyntax = grammaraddr;
|
|
grammaraddr = thissyntax;
|
|
|
|
goto CheckXobjectFinish;
|
|
}
|
|
else if ((p==NUMBER_T && STARTS_AS_NUMBER(word[*wordnum])) &&
|
|
!strcmp(word[*wordnum], hugo_itoa(atoi(word[*wordnum]), num, 10, sizeof(num))))
|
|
{
|
|
objfinish = i;
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (objfinish==-1) objfinish = words;
|
|
|
|
/* Regardless, try to match the xobject */
|
|
recursive_call = false;
|
|
if (MatchObject(&(*wordnum))==0)
|
|
{
|
|
objfinish = temp_objfinish;
|
|
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
objfinish = temp_objfinish;
|
|
|
|
domain = 0;
|
|
if (ValidObj(pobj)==false)
|
|
return 2;
|
|
domain = t;
|
|
|
|
if (objcount==1)
|
|
var[xobject] = objlist[0];
|
|
else
|
|
var[xobject] = pobj;
|
|
if (domain==-1) domain = var[xobject]; /* parent */
|
|
obj_match_state = 2;
|
|
|
|
AdvanceGrammar();
|
|
|
|
/* Can't have multiple xobjects */
|
|
if (objcount > 1)
|
|
{
|
|
ParseError(7, 0);
|
|
return 2;
|
|
}
|
|
objcount = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Hugo::ObjWordType(int obj, unsigned int w, int type) {
|
|
int j, num;
|
|
unsigned int pa;
|
|
|
|
pa = PropAddr(obj, type, 0);
|
|
if (pa)
|
|
{
|
|
defseg = proptable;
|
|
num = Peek(pa + 1);
|
|
|
|
if (num==PROP_ROUTINE)
|
|
{
|
|
if ((unsigned int)GetProp(obj, type, 1, false)==w)
|
|
{
|
|
defseg = gameseg;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j=1; j<=num; j++)
|
|
{
|
|
if (PeekWord(pa + j * 2)==w)
|
|
{
|
|
defseg = gameseg;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
defseg = gameseg;
|
|
|
|
return false;
|
|
}
|
|
|
|
int Hugo::ObjWord(int obj, unsigned int w) {
|
|
if ((obj_parselist) && !(obj_parselist[obj/8]&1<<(obj%8)))
|
|
return 0;
|
|
|
|
if (ObjWordType(obj, w, adjective))
|
|
return adjective;
|
|
|
|
if (ObjWordType(obj, w, noun))
|
|
return noun;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Hugo::Parse() {
|
|
char foundstring = 0; /* allow one unknown word/phrase */
|
|
int notfound_word = 0;
|
|
int i, j, k, m;
|
|
char num[33];
|
|
char tempword[81];
|
|
unsigned int period, comma;
|
|
unsigned int synptr;
|
|
|
|
period = FindWord(".");
|
|
comma = FindWord(",");
|
|
|
|
parsestr[0] = '\0'; /* for storing any unknown string */
|
|
parsed_number = 0; /* " " " parsed number */
|
|
|
|
for (i=1; i<=words; i++) /* find dictionary addresses */
|
|
{
|
|
if (word[i][0]=='\"' && foundstring==0)
|
|
{
|
|
Common::strcpy_s(parsestr, word[i]);
|
|
foundstring = 1;
|
|
wd[i] = UNKNOWN_WORD;
|
|
}
|
|
else
|
|
{
|
|
wd[i] = FindWord(word[i]);
|
|
|
|
/* Numbers -32768 to 32767 are valid...*/
|
|
if (!strcmp(word[i], hugo_itoa(atoi(word[i]), num, 10, sizeof(num))))
|
|
{
|
|
#if !defined (MATH_16BIT)
|
|
if (atoi(word[i]) > 32767 || atoi(word[i]) < -32768)
|
|
goto NotinDictionary;
|
|
#endif
|
|
parsed_number = atoi(word[i]);
|
|
if (parseerr[0]=='\0')
|
|
Common::strcpy_s(parseerr, word[i]);
|
|
}
|
|
|
|
/* Otherwise it must be a dictionary entry */
|
|
else
|
|
{
|
|
/* If it's not in the dictionary */
|
|
if (wd[i]==UNKNOWN_WORD)
|
|
{
|
|
NotinDictionary:
|
|
if (!notfound_word)
|
|
{
|
|
Common::strcpy_s(parseerr, word[i]);
|
|
Common::strcpy_s(oops, word[i]);
|
|
|
|
notfound_word = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return here instead of immediately, so that we have a chance
|
|
to load the rest of the recognized words into the word array
|
|
*/
|
|
if (notfound_word)
|
|
{
|
|
i = notfound_word;
|
|
|
|
/* "...can't use the word..." */
|
|
ParseError(1, 0);
|
|
errbuf[0] = '\0';
|
|
for (i=1; i<=words; i++)
|
|
{
|
|
Common::strcat_s(errbuf, word[i]);
|
|
if (i != words) Common::strcat_s(errbuf, " ");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
wd[words+1] = 0;
|
|
oopscount = 0;
|
|
|
|
|
|
/* Do synonyms, removals, compounds, punctuation */
|
|
|
|
for (i=1; i<=words; i++) /* Look through words... */
|
|
{
|
|
synptr = 2;
|
|
|
|
for (j=1; j<=syncount; j++) /* ...and alterations */
|
|
{
|
|
defseg = syntable;
|
|
if (wd[i]==PeekWord(synptr + 1))
|
|
{
|
|
switch (Peek(synptr))
|
|
{
|
|
case 0: /* synonym */
|
|
{
|
|
defseg = syntable;
|
|
wd[i] = PeekWord(synptr + 3);
|
|
m = strlen(GetWord(wd[i])) - strlen(word[i]);
|
|
if (m)
|
|
{
|
|
if (m + (int)strlen(buffer) > 81)
|
|
{buffer[0] = '\0';
|
|
words = 0;
|
|
ParseError(0, 0);
|
|
return 0;}
|
|
|
|
for (k=words; k>i; k--)
|
|
{
|
|
Common::strcpy_s(tempword, word[k]);
|
|
word[k] += m;
|
|
Common::strcpy_s(word[k], sizeof(buffer) - (word[k] - buffer), tempword);
|
|
}
|
|
}
|
|
Common::strcpy_s(word[i], sizeof(buffer) - (word[i] - buffer), GetWord(wd[i]));
|
|
i--;
|
|
break;
|
|
}
|
|
|
|
case 1: /* removal */
|
|
{
|
|
KillWord(i);
|
|
i--;
|
|
break;
|
|
}
|
|
|
|
case 2: /* compound */
|
|
{
|
|
if (wd[i+1]==PeekWord(synptr+3))
|
|
{
|
|
Common::strcat_s(word[i], sizeof(buffer) - (word[i] - buffer), word[i+1]);
|
|
wd[i] = FindWord(word[i]);
|
|
KillWord(i+1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
goto NextSyn;
|
|
}
|
|
NextSyn:
|
|
synptr += 5;
|
|
}
|
|
|
|
if (wd[i]==comma)
|
|
{
|
|
if (strcmp(word[i+1], "~and"))
|
|
{
|
|
static char AND[5] = "~and";
|
|
word[i] = AND;
|
|
wd[i] = FindWord("~and");
|
|
}
|
|
else
|
|
KillWord(i);
|
|
}
|
|
|
|
if (wd[i]==period)
|
|
{
|
|
wd[i] = 0;
|
|
word[i] = EMPTY;
|
|
}
|
|
}
|
|
|
|
defseg = gameseg;
|
|
|
|
if (strcmp(word[1], "~oops")) oops[0] = '\0';
|
|
|
|
if (words==0)
|
|
{
|
|
ParseError(0,0); /* What? */
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Hugo::ParseError(int e, int a) {
|
|
int i, k, count;
|
|
|
|
remaining = 0;
|
|
xverb = true;
|
|
|
|
if (e==5 && !strcmp(parseerr, "")) e = 6;
|
|
|
|
if (parseerroraddr)
|
|
{
|
|
ret = 0;
|
|
passlocal[0] = e;
|
|
passlocal[1] = a;
|
|
PassLocals(2);
|
|
|
|
SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
|
|
|
|
#if defined (DEBUGGER)
|
|
DebugRunRoutine((long)parseerroraddr*address_scale);
|
|
#else
|
|
RunRoutine((long)parseerroraddr*address_scale);
|
|
#endif
|
|
stack_depth = 0;
|
|
retflag = 0;
|
|
if (ret)
|
|
{
|
|
if (ret==2) reparse_everything = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (e)
|
|
{
|
|
case 0:
|
|
AP("What?");
|
|
break;
|
|
|
|
case 1:
|
|
Common::sprintf_s(line, "You can't use the word \"%s\".", parseerr);
|
|
AP(line);
|
|
break;
|
|
|
|
case 2:
|
|
AP("Better start with a verb.");
|
|
break;
|
|
|
|
case 3:
|
|
Common::sprintf_s(line, "You can't %s multiple objects.", parseerr);
|
|
AP(line);
|
|
break;
|
|
|
|
case 4:
|
|
AP("Can't do that.");
|
|
break;
|
|
|
|
case 5:
|
|
Common::sprintf_s(line, "You haven't seen any \"%s\", nor are you likely to in the near future even if such a thing exists.", parseerr);
|
|
AP(line);
|
|
break;
|
|
|
|
case 6:
|
|
AP("That doesn't make any sense.");
|
|
break;
|
|
|
|
case 7:
|
|
AP("You can't use multiple objects like that.");
|
|
break;
|
|
|
|
case 8:
|
|
{
|
|
Common::sprintf_s(line, "Which %s do you mean, ", !parse_called_twice?parseerr:"exactly");
|
|
count = 1;
|
|
for (k=0; k<pobjcount; k++)
|
|
{
|
|
i = pobjlist[k].obj;
|
|
|
|
if (strcmp(Name(i), ""))
|
|
{
|
|
if (count==pobjcount)
|
|
{
|
|
if (count > 2) Common::strcat_s(line, ",");
|
|
Common::strcat_s(line, " or ");
|
|
}
|
|
else
|
|
{
|
|
if (count != 1)
|
|
Common::strcat_s(line, ", ");
|
|
}
|
|
if (GetProp(i, article, 1, 0))
|
|
{
|
|
const char *w = GetWord(GetProp(i, article, 1, 0));
|
|
/* Don't use "a" or "an" in listing */
|
|
/*
|
|
if (!strcmp(w, "a") || !strcmp(w, "an"))
|
|
Common::strcat_s(line, "the ");
|
|
else
|
|
Common::sprintf_s(line+strlen(line), "%s ", w);
|
|
*/
|
|
/* We'll just use "the" */
|
|
if (w) Common::strcat_s(line, "the ");
|
|
}
|
|
Common::strcat_s(line, Name(i));
|
|
count++;
|
|
}
|
|
}
|
|
Common::strcat_s(line, "?");
|
|
AP(line);
|
|
break;
|
|
}
|
|
|
|
case 9:
|
|
Common::sprintf_s(line, "Nothing to %s.", parseerr);
|
|
AP(line);
|
|
break;
|
|
|
|
case 10:
|
|
AP("You haven't seen anything like that.");
|
|
break;
|
|
|
|
case 11:
|
|
AP("You don't see that.");
|
|
break;
|
|
|
|
case 12:
|
|
Common::sprintf_s(line, "You can't do that with the %s.", Name(a));
|
|
AP(line);
|
|
break;
|
|
|
|
case 13:
|
|
AP("You'll have to be a little more specific.");
|
|
break;
|
|
|
|
case 14:
|
|
AP("You don't see that there.");
|
|
break;
|
|
|
|
case 15:
|
|
AP("You don't have that.");
|
|
break;
|
|
|
|
case 16:
|
|
AP("You'll have to make a mistake first.");
|
|
break;
|
|
|
|
case 17:
|
|
AP("You can only correct one word at a time.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Hugo::RemoveWord(int a) {
|
|
if (a > words)
|
|
return;
|
|
|
|
for (; a<words; a++)
|
|
{
|
|
wd[a] = wd[a + 1];
|
|
objword_cache[a] = objword_cache[a + 1];
|
|
}
|
|
wd[words] = 0;
|
|
objword_cache[words] = 0;
|
|
}
|
|
|
|
void Hugo::ResetFindObject() {
|
|
if (findobjectaddr)
|
|
{
|
|
SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
|
|
PassLocals(0);
|
|
#if defined (DEBUGGER)
|
|
DebugRunRoutine((long)findobjectaddr*address_scale);
|
|
#else
|
|
RunRoutine((long)findobjectaddr*address_scale);
|
|
#endif
|
|
retflag = 0;
|
|
}
|
|
}
|
|
|
|
void Hugo::SeparateWords() {
|
|
char inquote = 0;
|
|
char a[1025];
|
|
char b[2];
|
|
char w1[17], w2[17]; /* for time conversions */
|
|
char temp[17];
|
|
short n1, n2; /* must be 16 bits */
|
|
int bloc = 0; /* buffer location */
|
|
int i;
|
|
|
|
|
|
/* First filter the line of any user-specified punctuation */
|
|
do
|
|
{
|
|
i = strcspn(buffer, punc_string);
|
|
if (buffer[i]) buffer[i] = ' ';
|
|
} while (buffer[i]);
|
|
|
|
|
|
/* Begin the word-splitting proper: */
|
|
|
|
words = 1; /* Setup a blank string */
|
|
|
|
for (i=0; i<MAXWORDS+1; i++)
|
|
{
|
|
word[i] = EMPTY;
|
|
wd[i] = 0;
|
|
}
|
|
word[1] = buffer;
|
|
|
|
Common::strcpy_s(a, buffer);
|
|
buffer[0] = '\0';
|
|
|
|
for (i=1; i<=(int)strlen(a); i++)
|
|
{
|
|
if (inquote!=1 && Common::isAscii(a[i-1]))
|
|
b[0] = (char)tolower(a[i-1]);
|
|
else b[0] = a[i-1];
|
|
b[1] = '\0';
|
|
|
|
if (b[0]=='\"' && inquote==1)
|
|
{
|
|
Common::strcpy_s(buffer+bloc, sizeof(buffer)-bloc, b);
|
|
bloc++;
|
|
inquote++;
|
|
}
|
|
|
|
if (b[0]=='\"' || ((b[0]==' ' || b[0]=='!' || b[0]=='?') && inquote!=1))
|
|
{
|
|
if (word[words][0]!='\0')
|
|
{
|
|
bloc++;
|
|
if (++words > MAXWORDS) words = MAXWORDS;
|
|
word[words] = buffer + bloc;
|
|
word[words][0] = '\0';
|
|
}
|
|
|
|
if (b[0]=='\"' && inquote==0)
|
|
{
|
|
Common::strcpy_s(buffer+bloc, sizeof(buffer)-bloc, b);
|
|
bloc++;
|
|
inquote = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((b[0]=='.' || b[0]==',') && inquote!=1)
|
|
{
|
|
if (word[words][0]!='\0')
|
|
{
|
|
bloc++;
|
|
if (++words > MAXWORDS) words = MAXWORDS;
|
|
}
|
|
word[words] = buffer + bloc;
|
|
Common::strcpy_s(word[words], sizeof(buffer)-bloc, b);
|
|
bloc += strlen(b) + 1;
|
|
if (++words > MAXWORDS) words = MAXWORDS;
|
|
word[words] = buffer + bloc;
|
|
word[words][0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
Common::strcpy_s(buffer+bloc, sizeof(buffer)-bloc, b);
|
|
bloc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!strcmp(word[words], "")) words--;
|
|
|
|
for (i=1; i<=words; i++)
|
|
{
|
|
/* Convert hours:minutes time to minutes only */
|
|
if (strcspn(word[i], ":")!=strlen(word[i]) && strlen(word[i])<=5)
|
|
{
|
|
Common::strcpy_s(w1, Left(word[i], strcspn(word[i], ":")));
|
|
Common::strcpy_s(w2, Right(word[i], strlen(word[i]) - strcspn(word[i], ":") - 1));
|
|
n1 = (short)atoi(w1);
|
|
n2 = (short)atoi(w2);
|
|
|
|
if (!strcmp(Left(w2, 1), "0"))
|
|
Common::strcpy_s(w2, Right(w2, strlen(w2) - 1));
|
|
|
|
/* If this is indeed a hh:mm time, write it back
|
|
as the modified word, storing the original hh:mm
|
|
in parse$:
|
|
*/
|
|
if (!strcmp(w1, hugo_itoa((int)n1, temp, 10, sizeof(temp))) &&
|
|
!strcmp(w2, hugo_itoa((int)n2, temp, 10, sizeof(temp))) &&
|
|
(n1 > 0 && n1 < 25) && (n2 >= 0 && n2 < 60))
|
|
{
|
|
Common::strcpy_s(parseerr, word[i]);
|
|
hugo_itoa(n1 * 60 + n2, word[i], 10, strlen(word[i]) + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hugo::SubtractObj(int obj) {
|
|
int i, j;
|
|
|
|
for (i=0; i<objcount; i++)
|
|
{
|
|
if (objlist[i]==obj)
|
|
{
|
|
for (j=i; j<objcount; j++)
|
|
objlist[j] = objlist[j+1];
|
|
objcount--;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hugo::SubtractPossibleObject(int obj) {
|
|
int i, j, last = 0;
|
|
|
|
for (i=0; i<pobjcount; i++)
|
|
{
|
|
if (pobjlist[i].obj==obj)
|
|
{
|
|
if (pobjlist[i].obj==pobj && last!=0) pobj = last;
|
|
|
|
for (j=i; j+1<pobjcount; j++)
|
|
{
|
|
pobjlist[j] = pobjlist[j+1];
|
|
}
|
|
pobjcount--;
|
|
|
|
#ifdef DEBUG_PARSER
|
|
{
|
|
char buf[100];
|
|
Common::sprintf_s(buf, "SubtractPossibleObject(%d:\"%s\")", obj, Name(obj));
|
|
Printout(buf);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
else last = pobjlist[i].obj;
|
|
}
|
|
}
|
|
|
|
void Hugo::TryObj(int obj) {
|
|
unsigned int tempdomain;
|
|
|
|
if ((obj_parselist) && !(obj_parselist[obj/8]&1<<(obj%8)))
|
|
return;
|
|
|
|
if (DomainObj(obj))
|
|
{
|
|
tempdomain = domain;
|
|
domain = 0;
|
|
|
|
if (Available(obj, 0) && !InList(Parent(obj)))
|
|
{
|
|
AddObj(obj);
|
|
}
|
|
else
|
|
{
|
|
SubtractObj(obj);
|
|
}
|
|
|
|
domain = tempdomain;
|
|
}
|
|
}
|
|
|
|
int Hugo::ValidObj(int obj) {
|
|
int attr, nattr = 0;
|
|
unsigned int addr;
|
|
|
|
defseg = gameseg;
|
|
|
|
if (!Available(obj, 0) && !speaking &&
|
|
(Peek(grammaraddr)!=OPEN_BRACKET_T ||
|
|
Peek(grammaraddr+1)!=ROUTINE_T))
|
|
{
|
|
if (Peek(grammaraddr)==ANYTHING_T)
|
|
ParseError(10, obj); /* "...haven't seen..." */
|
|
else
|
|
ParseError(11, obj); /* "...don't see any..." */
|
|
return 0;
|
|
}
|
|
|
|
switch (Peek(grammaraddr))
|
|
{
|
|
case OPEN_BRACKET_T:
|
|
{
|
|
if (Peek(grammaraddr+1)==ROUTINE_T)
|
|
{
|
|
addr = PeekWord(grammaraddr+2);
|
|
ret = 0;
|
|
passlocal[0] = obj;
|
|
PassLocals(1);
|
|
|
|
SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
|
|
|
|
#if defined (DEBUGGER)
|
|
DebugRunRoutine((long)addr*address_scale);
|
|
#else
|
|
RunRoutine((long)addr*address_scale);
|
|
#endif
|
|
retflag = 0;
|
|
|
|
/* If the routine doesn't return true,
|
|
the object doesn't qualify.
|
|
*/
|
|
if (!ret)
|
|
return (0);
|
|
}
|
|
else if (Peek(grammaraddr+1)==OBJECTNUM_T)
|
|
{
|
|
if (obj != (int)PeekWord(grammaraddr+2))
|
|
{
|
|
parseerr[0] = '\0';
|
|
if (GetProp(obj, article, 1, 0))
|
|
Common::strcpy_s(parseerr, "the ");
|
|
Common::strcat_s(parseerr, Name(obj));
|
|
|
|
/* "...can't do that with..." */
|
|
ParseError(12, obj);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ATTR_T:
|
|
case NOT_T:
|
|
{
|
|
if (Peek(grammaraddr)==NOT_T) nattr = 1;
|
|
attr = Peek(grammaraddr + 1 + nattr);
|
|
|
|
/* If the attribute match is not made,
|
|
the object doesn't qualify.
|
|
*/
|
|
if (!TestAttribute(obj, attr, nattr))
|
|
{
|
|
parseerr[0] = '\0';
|
|
if (GetProp(obj, article, 1, 0))
|
|
Common::strcpy_s(parseerr, "the ");
|
|
Common::strcat_s(parseerr, Name(obj));
|
|
|
|
/* "...can't do that with..." */
|
|
ParseError(12, obj);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // End of namespace Hugo
|
|
} // End of namespace Glk
|