/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "glk/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 MAXOBJLIST) objcount = MAXOBJLIST; } void Hugo::AddPossibleObject(int obj, char type, unsigned int w) { int i; if (pobjcount==MAXPOBJECTS) return; for (i=0; i 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; iwords) return; for (i=a; i 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; strcpy(line, word[2]); for (i=1; i<=(int)strlen(errbuf); i++) { if (!strcmp(Mid(errbuf, i, strlen(oops)), oops)) break; } strcpy(buffer, errbuf); buffer[i-1] = '\0'; strcat(buffer, line); strcat(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; i65535 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) { strcpy(parseerr, ""); ParseError(6, 0); /* "...doesn't make any sense..." */ return 0; } /* No provision made for addressing objects (characters) */ if (flag==0 || speaktoaddr==0) { strcpy(parseerr, ""); 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; strcpy(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: */ strcpy(parseerr, ""); /* "...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 strcpy(parseerr, ""); 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') strcat(parseerr, " "); strcat(parseerr, word[*wordnum]); flag = 0; for (i=0; i=(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) { strcpy(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) { strcpy(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) { strcpy(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 1) { for (i=0; i 1 && Peek(grammaraddr)==ANYTHING_T) { for (i=0; i 1 && findobjectaddr) { for (k=0; k 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 and word[] array */ strcpy(buffer, ""); for (i=1; i<=wtemp; i++) { strcat(buffer, GetWord(wdtemp[i])); strcat(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; i1 && i!=MULTI_T && i!=MULTIHELD_T && i!=MULTINOTHELD_T) { strcpy(parseerr, word[1]); /* "You can't...multiple objects." */ ParseError(3, 0); return false; } /* Check objlist against original pobjlist */ for (i=0; 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 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 */ strcpy(parseerr, word[1]); ParseError(9, 0); /* "Nothing to (verb)..." */ return false; } } } /* Go back for the next object phrase */ pobjcount = 0; mobjs = 0; strcpy(parseerr, ""); 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(itoa(atoi(word[*wordnum]), num, 10), 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 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], itoa(atoi(word[*wordnum]), num, 10))) { 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) { strcpy(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], itoa(atoi(word[*wordnum]), num, 10))) { 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(","); strcpy(parsestr, ""); /* 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) { strcpy(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], itoa(atoi(word[i]), num, 10))) { #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') strcpy(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) { strcpy(parseerr, word[i]); strcpy(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); strcpy(errbuf, ""); for (i=1; i<=words; i++) { strcat(errbuf, word[i]); if (i != words) strcat(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) {strcpy(buffer, ""); words = 0; ParseError(0, 0); return 0;} for (k=words; k>i; k--) { strcpy(tempword, word[k]); word[k] += m; strcpy(word[k], tempword); } } strcpy(word[i], GetWord(wd[i])); i--; break; } case 1: /* removal */ { KillWord(i); i--; break; } case 2: /* compound */ { if (wd[i+1]==PeekWord(synptr+3)) { strcat(word[i], 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")) strcpy(oops, ""); 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: sprintf(line, "You can't use the word \"%s\".", parseerr); AP(line); break; case 2: AP("Better start with a verb."); break; case 3: sprintf(line, "You can't %s multiple objects.", parseerr); AP(line); break; case 4: AP("Can't do that."); break; case 5: sprintf(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: { sprintf(line, "Which %s do you mean, ", !parse_called_twice?parseerr:"exactly"); count = 1; for (k=0; k 2) strcat(line, ","); strcat(line, " or "); } else { if (count != 1) strcat(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")) strcat(line, "the "); else sprintf(line+strlen(line), "%s ", w); */ /* We'll just use "the" */ if (w) strcat(line, "the "); } strcat(line, Name(i)); count++; } } strcat(line, "?"); AP(line); break; } case 9: sprintf(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: sprintf(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 MAXWORDS) words = MAXWORDS; word[words] = buffer + bloc; strcpy(word[words], ""); } if (b[0]=='\"' && inquote==0) { strcpy(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; strcpy(word[words], b); bloc += strlen(b) + 1; if (++words > MAXWORDS) words = MAXWORDS; word[words] = buffer + bloc; strcpy(word[words], ""); } else { strcpy(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) { strcpy(w1, Left(word[i], strcspn(word[i], ":"))); strcpy(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")) strcpy(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, itoa((int)n1, temp, 10)) && !strcmp(w2, itoa((int)n2, temp, 10)) && (n1 > 0 && n1 < 25) && (n2 >= 0 && n2 < 60)) { strcpy(parseerr, word[i]); itoa(n1 * 60 + n2, word[i], 10); } } } } void Hugo::SubtractObj(int obj) { int i, j; for (i=0; i