diff options
Diffstat (limited to 'engines/titanic/true_talk/tt_parser.cpp')
-rw-r--r-- | engines/titanic/true_talk/tt_parser.cpp | 1713 |
1 files changed, 1713 insertions, 0 deletions
diff --git a/engines/titanic/true_talk/tt_parser.cpp b/engines/titanic/true_talk/tt_parser.cpp new file mode 100644 index 0000000000..1d9c199054 --- /dev/null +++ b/engines/titanic/true_talk/tt_parser.cpp @@ -0,0 +1,1713 @@ +/* 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 "titanic/true_talk/tt_parser.h" +#include "titanic/true_talk/script_handler.h" +#include "titanic/true_talk/tt_action.h" +#include "titanic/true_talk/tt_concept.h" +#include "titanic/true_talk/tt_picture.h" +#include "titanic/true_talk/tt_sentence.h" +#include "titanic/true_talk/tt_word.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTparser::TTparser(CScriptHandler *owner) : _owner(owner), _sentenceConcept(nullptr), + _sentence(nullptr), _fieldC(0), _field10(0), _field14(0), + _currentWordP(nullptr), _nodesP(nullptr), _conceptP(nullptr) { + loadArrays(); +} + +TTparser::~TTparser() { + if (_nodesP) { + _nodesP->deleteSiblings(); + delete _nodesP; + } + + if (_conceptP) { + _conceptP->deleteSiblings(); + delete _conceptP; + } + + delete _currentWordP; +} + +void TTparser::loadArray(StringArray &arr, const CString &name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + while (r->pos() < r->size()) + arr.push_back(readStringFromStream(r)); + delete r; +} + +void TTparser::loadArrays() { + loadArray(_replacements1, "TEXT/REPLACEMENTS1"); + loadArray(_replacements2, "TEXT/REPLACEMENTS2"); + loadArray(_replacements3, "TEXT/REPLACEMENTS3"); + loadArray(_phrases, "TEXT/PHRASES"); + loadArray(_pronouns, "TEXT/PRONOUNS"); + + Common::SeekableReadStream *r = g_vm->_filesManager->getResource("TEXT/NUMBERS"); + while (r->pos() < r->size()) { + NumberEntry ne; + ne._text = readStringFromStream(r); + ne._value = r->readSint32LE(); + ne._flags = r->readUint32LE(); + _numbers.push_back(ne); + } + delete r; +} + +int TTparser::preprocess(TTsentence *sentence) { + _sentence = sentence; + if (normalize(sentence)) + return 0; + + // Scan for and replace common slang and contractions with verbose versions + searchAndReplace(sentence->_normalizedLine, _replacements1); + searchAndReplace(sentence->_normalizedLine, _replacements2); + + // Check entire normalized line against common phrases to replace + for (uint idx = 0; idx < _phrases.size(); idx += 2) { + if (!_phrases[idx].compareTo(sentence->_normalizedLine)) + sentence->_normalizedLine = _phrases[idx + 1]; + } + + // Do a further search and replace of roman numerals to decimal + searchAndReplace(sentence->_normalizedLine, _replacements3); + + // Replace any roman numerals, spelled out words, etc. with decimal numbers + CTrueTalkManager::_v1 = -1000; + int idx = 0; + do { + idx = replaceNumbers(sentence->_normalizedLine, idx); + } while (idx >= 0); + + if (CTrueTalkManager::_v1 == -1000 && !sentence->_normalizedLine.empty()) { + // Scan the text for any numeric digits + for (const char *strP = sentence->_normalizedLine.c_str(); *strP; ++strP) { + if (Common::isDigit(*strP)) { + // Found digit, so convert it and any following ones + CTrueTalkManager::_v1 = atoi(strP); + break; + } + } + } + + return 0; +} + +int TTparser::normalize(TTsentence *sentence) { + TTstring *destLine = new TTstring(); + const TTstring &srcLine = sentence->_initialLine; + int srcSize = srcLine.size(); + int savedIndex = 0; + int counter1 = 0; + int commandVal; + + for (int index = 0; index < srcSize; ++index) { + char c = srcLine[index]; + if (Common::isLower(c)) { + (*destLine) += c; + } else if (Common::isSpace(c)) { + if (!destLine->empty() && destLine->lastChar() != ' ') + (*destLine) += ' '; + } else if (Common::isUpper(c)) { + (*destLine) += toupper(c); + } else if (Common::isDigit(c)) { + if (c == '0' && isEmoticon(srcLine, index)) { + sentence->set38(10); + } else { + // Iterate through all the digits of the number + (*destLine) += c; + while (Common::isDigit(srcLine[index + 1])) + (*destLine) += srcLine[++index]; + } + } else if (Common::isPunct(c)) { + bool flag = false; + switch (c) { + case '!': + sentence->set38(3); + break; + + case '\'': + if (!normalizeContraction(srcLine, index, *destLine)) + flag = true; + break; + + case '.': + sentence->set38(1); + break; + + case ':': + commandVal = isEmoticon(srcLine, index); + if (commandVal) { + sentence->set38(commandVal); + index += 2; + } else { + flag = true; + } + break; + + case ';': + commandVal = isEmoticon(srcLine, index); + if (commandVal == 6) { + sentence->set38(7); + index += 2; + } else if (commandVal != 0) { + sentence->set38(commandVal); + index += 2; + } + break; + + case '<': + ++index; + commandVal = isEmoticon(srcLine, index); + if (commandVal == 6) { + sentence->set38(12); + } else { + --index; + flag = true; + } + break; + + case '>': + ++index; + commandVal = isEmoticon(srcLine, index); + if (commandVal == 6 || commandVal == 9) { + sentence->set38(11); + } else { + --index; + flag = true; + } + break; + + case '?': + sentence->set38(2); + break; + + default: + flag = true; + break; + } + + if (flag && (!savedIndex || (index - savedIndex) == 1)) + ++counter1; + + savedIndex = index; + } + } + + if (counter1 >= 4) + sentence->set38(4); + + // Remove any trailing spaces + while (destLine->hasSuffix(" ")) + destLine->deleteLastChar(); + + // Copy out the normalized line + sentence->_normalizedLine = *destLine; + delete destLine; + + return 0; +} + +int TTparser::isEmoticon(const TTstring &str, int &index) { + if (str[index] != ':' && str[index] != ';') + return 0; + + if (str[index + 1] != '-') + return 0; + + index += 2; + switch (str[index]) { + case '(': + case '<': + return 8; + + case ')': + case '>': + return 6; + + case 'P': + case 'p': + return 9; + + default: + return 5; + } +} + +bool TTparser::normalizeContraction(const TTstring &srcLine, int srcIndex, TTstring &destLine) { + int startIndex = srcIndex + 1; + switch (srcLine[startIndex]) { + case 'd': + srcIndex += 2; + if (srcLine.compareAt(srcIndex, " a ") || srcLine.compareAt(srcIndex, " the ")) { + destLine += " had"; + } else { + destLine += " would"; + } + + srcIndex = startIndex; + break; + + case 'l': + if (srcLine[srcIndex + 2] == 'l') { + // 'll ending + destLine += " will"; + srcIndex = startIndex; + } + break; + + case 'm': + // 'm ending + destLine += " am"; + srcIndex = startIndex; + break; + + case 'r': + // 're ending + if (srcLine[srcIndex + 2] == 'e') { + destLine += " are"; + srcIndex = startIndex; + } + break; + + case 's': + destLine += "s*"; + srcIndex = startIndex; + break; + + case 't': + if (srcLine[srcIndex - 1] == 'n' && srcIndex >= 3) { + if (srcLine[srcIndex - 3] == 'c' && srcLine[srcIndex - 2] == 'a' && + (srcIndex == 3 || srcLine[srcIndex - 4])) { + // can't -> can not + destLine += 'n'; + } else if (srcLine[srcIndex - 3] == 'w' && srcLine[srcIndex - 2] == 'o' && + (srcIndex == 3 || srcLine[srcIndex - 4])) { + // won't -> will not + destLine.deleteLastChar(); + destLine.deleteLastChar(); + destLine += "ill"; + } else if (srcLine[srcIndex - 3] == 'a' && srcLine[srcIndex - 2] == 'i' && + (srcIndex == 3 || srcLine[srcIndex - 4])) { + // ain't -> am not + destLine.deleteLastChar(); + destLine.deleteLastChar(); + destLine += "m"; + } else if (srcLine.hasSuffix(" sha") || + (srcIndex == 4 && srcLine.hasSuffix("sha"))) { + // shan't -> shall not + destLine.deleteLastChar(); + destLine += "ll"; + } + + destLine += " not"; + } + break; + + case 'v': + // 've ending + if (srcLine[startIndex + 2] == 'e') { + destLine += " have"; + srcIndex = startIndex; + } + break; + + default: + break; + } + + return false; +} + +void TTparser::searchAndReplace(TTstring &line, const StringArray &strings) { + int charIndex = 0; + while (charIndex >= 0) + charIndex = searchAndReplace(line, charIndex, strings); +} + +int TTparser::searchAndReplace(TTstring &line, int startIndex, const StringArray &strings) { + int lineSize = line.size(); + if (startIndex >= lineSize) + return -1; + + for (uint idx = 0; idx < strings.size(); idx += 2) { + const CString &origStr = strings[idx]; + const CString &replacementStr = strings[idx + 1]; + + if (!strncmp(line.c_str() + startIndex, origStr.c_str(), strings[idx].size())) { + // Ensure that that a space follows the match, or the end of string, + // so the end of the string doesn't match on parts of larger words + char c = line[startIndex + strings[idx].size()]; + if (c == ' ' || c == '\0') { + // Replace the text in the line with it's replacement + line = CString(line.c_str(), line.c_str() + startIndex) + replacementStr + + CString(line.c_str() + startIndex + origStr.size()); + + startIndex += replacementStr.size(); + break; + } + } + } + + // Skip to the end of the current word + while (startIndex < lineSize && line[startIndex] != ' ') + ++startIndex; + if (startIndex == lineSize) + return -1; + + // ..and all spaces following it until the start of the next word + while (startIndex < lineSize && line[startIndex] == ' ') + ++startIndex; + if (startIndex == lineSize) + return -1; + + // Return index of the start of the next word + return startIndex; +} + +int TTparser::replaceNumbers(TTstring &line, int startIndex) { + int index = startIndex; + const NumberEntry *numEntry = replaceNumbers2(line, &index); + if (!numEntry || !(numEntry->_flags & NF_2)) + return index; + + bool flag1 = false, flag2 = false, flag3 = false; + int total = 0, factor = 0; + + do { + if (numEntry->_flags & NF_1) { + flag2 = true; + if (numEntry->_flags & NF_8) + flag1 = true; + + if (numEntry->_flags & NF_4) { + flag3 = true; + factor *= numEntry->_value; + } + + if (numEntry->_flags & NF_2) { + if (flag3) { + total += factor; + factor = 0; + } + + factor += numEntry->_value; + } + } + } while (replaceNumbers2(line, &index)); + + if (!flag2) + return index; + + if (index >= 0) { + if (line[index - 1] != ' ') + return index; + } + + total += factor; + CTrueTalkManager::_v1 = total; + if (flag1) + total = -total; + + CString numStr = CString::format("%d", total); + line = CString(line.c_str(), line.c_str() + startIndex) + numStr + + CString(line.c_str() + index); + return index; +} + +const NumberEntry *TTparser::replaceNumbers2(TTstring &line, int *startIndex) { + int lineSize = line.size(); + int index = *startIndex; + if (index < 0 || index >= lineSize) { + *startIndex = -1; + return nullptr; + } + + NumberEntry *numEntry = nullptr; + + for (uint idx = 0; idx < _numbers.size(); ++idx) { + NumberEntry &ne = _numbers[idx]; + if (!strncmp(line.c_str() + index, ne._text.c_str(), ne._text.size())) { + if ((ne._flags & NF_10) || (index + (int)ne._text.size()) >= lineSize || + line[index + ne._text.size()] == ' ') { + *startIndex += ne._text.size(); + numEntry = ≠ + break; + } + } + } + + if (!numEntry || !(numEntry->_flags & NF_10)) { + // Skip to end of current word + while (*startIndex < lineSize && !Common::isSpace(line[*startIndex])) + ++*startIndex; + } + + // Skip over following spaces until start of following word is reached + while (*startIndex < lineSize && Common::isSpace(line[*startIndex])) + ++*startIndex; + + if (*startIndex >= lineSize) + *startIndex = -1; + + return numEntry; +} + +int TTparser::findFrames(TTsentence *sentence) { + _sentenceConcept = &sentence->_sentenceConcept; + _sentence = sentence; + + TTstring *line = sentence->_normalizedLine.copy(); + TTstring wordString; + int status = 0; + for (int ctr = 1; !status; ++ctr) { + // Keep stripping words off the start of the passed input + wordString = line->tokenize(" \n"); + if (wordString.empty()) + break; + + TTword *srcWord = nullptr; + TTword *word = _owner->_vocab->getWord(wordString, &word); + sentence->storeVocabHit(srcWord); + + if (!word && ctr == 1) { + word = new TTword(wordString, WC_UNKNOWN, 0); + } + + for (TTword *currP = word; currP && !status; currP = currP->_nextP) + status = processRequests(currP); + + word->deleteSiblings(); + delete word; + } + + if (!status) { + status = checkForAction(); + } + + delete line; + return status; +} + +int TTparser::loadRequests(TTword *word) { + int status = 0; + + if (word->_tag != MKTAG('Z', 'Z', 'Z', 'T')) + addNode(word->_tag); + + switch (word->_wordClass) { + case WC_UNKNOWN: + break; + + case WC_ACTION: + if (word->_id != 0x70 && word->_id != 0x71) + addNode(1); + addNode(17); + + switch (word->_id) { + case 101: + case 110: + addNode(5); + addNode(4); + break; + + case 102: + addNode(4); + break; + + case 103: + case 111: + addNode(8); + addNode(7); + addNode(5); + addNode(4); + break; + + case 104: + case 107: + addNode(15); + addNode(5); + addNode(4); + break; + + case 106: + addNode(7); + addNode(4); + break; + + case 108: + addNode(5); + addNode(4); + addNode(23); + break; + + case 112: + case 113: + addNode(13); + addNode(5); + break; + + default: + break; + } + + if (_sentenceConcept) { + if (_sentenceConcept->get18() == 0 || _sentenceConcept->get18() == 2) { + TTaction *action = static_cast<TTaction *>(word); + _sentenceConcept->set18(action->getVal()); + } + } + break; + + case WC_THING: + if (word->checkTag() && _sentence->_field58 > 0) + _sentence->_field58--; + addNode(14); + break; + + case WC_ABSTRACT: + switch (word->_id) { + case 300: + addNode(14); + status = 1; + break; + + case 306: + addNode(23); + addNode(4); + break; + + case 307: + case 308: + addNode(23); + break; + + default: + break; + } + + if (status != 1) { + addToConceptList(word); + addNode(14); + } + break; + + case WC_ARTICLE: + addNode(2); + status = 1; + break; + + case WC_CONJUNCTION: + if (_sentence->check2C()) { + _sentenceConcept->_field1C = 1; + _sentenceConcept = _sentenceConcept->addSibling(); + delete this; + } else { + addNode(23); + } + break; + + case WC_PRONOUN: + status = fn2(word); + break; + + case WC_PREPOSITION: + switch (word->_id) { + case 700: + addNode(6); + addNode(5); + break; + case 701: + addNode(11); + break; + case 702: + status = 1; + break; + case 703: + addNode(9); + break; + case 704: + addNode(10); + break; + default: + break; + } + + case WC_ADJECTIVE: + if (word->_id == 304) { + // Nothing + } else if (word->_id == 801) { + addNode(22); + } else { + if (word->proc16()) + _sentence->_field58++; + if (word->proc17()) + _sentence->_field58++; + } + break; + + case WC_ADVERB: + switch (word->_id) { + case 900: + case 901: + case 902: + case 904: + if (_sentence->_field2C == 9) { + _sentenceConcept->_field1C = 1; + _sentenceConcept = _sentenceConcept->addSibling(); + addNode(1); + } + else { + addNode(23); + addNode(13); + addNode(1); + } + break; + + case 905: + case 907: + case 908: + case 909: + addNode(23); + break; + + case 906: + addNode(23); + status = 1; + break; + + case 910: + addNode(4); + addNode(24); + addNode(23); + addNode(14); + status = 1; + break; + + default: + break; + } + + if (word->_id == 906) { + addNode(14); + status = 1; + } + break; + + default: + break; + } + + return status; +} + +int TTparser::considerRequests(TTword *word) { + if (!_nodesP || !word) + return 0; + + TTconcept *concept = nullptr; + int status = 0; + bool flag = false; + bool modifierFlag = false; + int seekVal = 0; + + for (TTparserNode *nodeP = _nodesP; nodeP; ) { + switch (nodeP->_tag) { + case CHECK_COMMAND_FORM: + if (_sentenceConcept->_concept1P && _sentence->_field2C == 1 && + !_sentenceConcept->_concept0P) { + concept = new TTconcept(_sentence->_npcScript, ST_NPC_SCRIPT); + _sentenceConcept->_concept0P = concept; + _sentenceConcept->_field18 = 3; + } + + flag = true; + break; + + case EXPECT_THING: + if (!word->_wordClass) { + word->_wordClass = WC_THING; + addToConceptList(word); + addNode(14); + } + + flag = true; + break; + + case OBJECT_IS_TO: + flag = resetConcept(&_sentenceConcept->_concept2P, 3); + break; + + case SEEK_ACTOR: + case MKTAG('S', 'A', 'C', 'T'): + if (!_sentenceConcept->_concept0P) { + flag = filterConcepts(5, 0); + } else if (_sentenceConcept->_concept0P->compareTo("?") && + _sentenceConcept->_concept1P->isWordId(113) && + word->_wordClass == WC_THING) { + TTconcept *oldConcept = _sentenceConcept->_concept0P; + _sentenceConcept->_concept0P = nullptr; + flag = filterConcepts(5, 2); + if (flag) + delete oldConcept; + } else { + flag = true; + } + break; + + case SEEK_OBJECT: + if (_sentenceConcept->_concept2P && _sentenceConcept->_concept2P->compareTo(word)) { + flag = true; + } else if (!_sentenceConcept->_concept2P) { + if (filterConcepts(5, 2) && _sentenceConcept->_concept2P->checkWordId1()) + addNode(5); + } else if (word->_wordClass == WC_THING && _sentence->fn2(2, TTstring("?"), _sentenceConcept)) { + TTconcept *oldConcept = _sentenceConcept->_concept2P; + flag = filterConcepts(5, 2); + _sentenceConcept->_concept2P->_field20 = oldConcept->get20(); + if (flag) + delete oldConcept; + } else if (!_sentenceConcept->_concept3P && + (!_sentenceConcept->_concept1P || (_sentenceConcept->_concept1P->getWordId() && + _sentenceConcept->_concept1P->getWordId() == 112)) && + _sentenceConcept->_concept2P->checkWordId1() && + (word->_wordClass == WC_THING || word->_wordClass == WC_PRONOUN)) { + _sentenceConcept->changeConcept(0, &_sentenceConcept->_concept2P, 3); + + if (_conceptP && _conceptP->isWordId(word->_id)) { + status = _sentenceConcept->replaceConcept(0, 2, _conceptP); + removeConcept(_conceptP); + } else { + status = _sentenceConcept->createConcept(0, 2, word); + } + + if (!status && !_sentenceConcept->_concept4P && _sentenceConcept->_concept0P) { + TTconcept *oldConcept = _sentenceConcept->_concept2P; + flag = filterConcepts(5, 2); + _sentenceConcept->_concept2P->_field20 = oldConcept->get20(); + if (flag) + delete oldConcept; + } else { + flag = true; + } + } + break; + + case SEEK_OBJECT_OVERRIDE: + if ((word->_wordClass == WC_THING || word->_wordClass == WC_PRONOUN) && + _sentence->fn2(2, TTstring("thePlayer"), _sentenceConcept) && + !_sentenceConcept->_concept3P) { + _sentenceConcept->_concept3P = _sentenceConcept->_concept2P; + _sentenceConcept->_concept2P = nullptr; + + flag = filterConcepts(5, 2); + if (!flag) { + status = _sentenceConcept->createConcept(0, 2, word); + } + } + break; + + case SEEK_TO: + if (!_sentenceConcept->_concept3P) { + if (!filterConcepts(8, 3)) + flag = filterConcepts(3, 3); + } else { + flag = true; + } + break; + + case SEEK_FROM: + if (!_sentenceConcept->_concept4P) { + if (!filterConcepts(8, 4)) + flag = filterConcepts(3, 3); + } else { + flag = true; + } + break; + + case SEEK_TO_OVERRIDE: + if (word->_wordClass == WC_ACTION) { + status = _sentenceConcept->createConcept(0, 1, word); + if (!status) { + seekVal = _sentenceConcept->_field18; + _sentenceConcept->_field18 = 4; + flag = true; + } + } else if (word->_id == 703) { + if (_sentenceConcept->_concept2P) { + delete _sentenceConcept->_concept2P; + _sentenceConcept->_concept2P = nullptr; + } + + if (_sentenceConcept->_concept4P || !_sentenceConcept->_concept0P) { + addNode(7); + } else { + _sentenceConcept->changeConcept(1, &_sentenceConcept->_concept0P, 4); + concept = nullptr; + addNode(7); + } + } else { + flag = true; + } + break; + + case SEEK_FROM_OVERRIDE: + if (_sentenceConcept->_concept4P) { + delete _sentenceConcept->_concept4P; + _sentenceConcept->_concept4P = nullptr; + } + + addNode(8); + flag = true; + break; + + case SEEK_LOCATION: + addNode(5); + _sentenceConcept->createConcept(0, 5, word); + flag = true; + break; + + case SEEK_OWNERSHIP: + if (word->_id == 601) { + if (_conceptP->findByWordClass(WC_THING)) + status = _conceptP->setOwner(word, false); + + flag = true; + } + break; + + case SEEK_STATE: + if (_sentenceConcept->_concept5P) { + if (_sentenceConcept->_concept5P->findByWordId(306) || + _sentenceConcept->_concept5P->findByWordId(904)) { + TTconcept *oldConcept = _sentenceConcept->_concept5P; + _sentenceConcept->_concept5P = nullptr; + flag = filterConcepts(9, 5); + if (flag) + delete oldConcept; + } else { + flag = true; + } + } else { + flag = filterConcepts(9, 5); + if (!flag && word->_wordClass == WC_ADVERB) { + status = _sentenceConcept->createConcept(1, 5, word); + flag = true; + } + } + break; + + case SEEK_MODIFIERS: + if (!modifierFlag) { + bool tempFlag = false; + + switch (word->_wordClass) { + case WC_ACTION: + status = processModifiers(1, word); + break; + case WC_THING: + status = processModifiers(2, word); + break; + case WC_ABSTRACT: + if (word->_id != 300) { + status = processModifiers(3, word); + } else if (!_conceptP->findByWordClass(WC_THING)) { + status = processModifiers(3, word); + } else { + word->_id = atoi(word->_text.c_str()); + } + break; + case WC_PRONOUN: + if (word->_id != 602) + addToConceptList(word); + break; + case WC_ADJECTIVE: { + TTconcept *conceptP = _conceptP->findByWordClass(WC_THING); + if (conceptP) { + conceptP->_string2 += ' '; + conceptP->_string2 += word->getText(); + } else { + status = processModifiers(8, word); + } + break; + } + case WC_ADVERB: + if (word->_id == 906) { + for (TTconcept *currP = _conceptP; currP; currP = currP->_nextP) { + if (_sentence->isFrameSlotClass(1, WC_ACTION) || + _sentence->isFrameSlotClass(1, WC_THING)) + currP->_field34 = 1; + } + } else { + TTconcept *conceptP = _conceptP->findByWordClass(WC_ACTION); + + if (conceptP) { + conceptP->_string2 += ' '; + conceptP->_string2 += word->getText(); + } else { + tempFlag = true; + } + } + break; + default: + addToConceptList(word); + status = 0; + break; + } + + if (tempFlag) + status = _sentenceConcept->createConcept(1, 5, word); + + modifierFlag = true; + flag = true; + } + break; + + case SEEK_NEW_FRAME: + if (word->_wordClass == WC_ACTION && word->_id != 104 && word->_id != 107) { + if (concept && (_sentenceConcept->_concept5P || _sentenceConcept->_concept2P)) { + TTsentenceConcept *oldNode = _sentenceConcept; + oldNode->_field1C = 2; + _sentenceConcept = oldNode->addSibling(); + concept = nullptr; + + _sentenceConcept->_concept1P = oldNode->_concept1P; + _sentenceConcept->_concept5P = oldNode->_concept5P; + _sentenceConcept->_concept2P = oldNode->_concept2P; + + if (seekVal) { + seekVal = 0; + + _sentenceConcept->_field18 = oldNode->_field18; + oldNode->_field18 = seekVal; + } + } + + flag = true; + } + break; + + case SEEK_STATE_OBJECT: + if (!_sentenceConcept->_concept5P) { + addToConceptList(word); + } else if (_sentenceConcept->concept5WordId() == 113 || + _sentenceConcept->concept5WordId() == 112) { + _sentenceConcept->createConcept(1, 2, word); + } else { + addToConceptList(word); + } + + flag = true; + break; + + case SET_ACTION: + if (_sentence->fn4(1, 104, _sentenceConcept) || + _sentence->fn4(1, 107, _sentenceConcept)) { + concept = _sentenceConcept->_concept1P; + _sentenceConcept->_concept1P = nullptr; + addNode(15); + } + + if (_sentence->check2C() && word->_id == 113) + addNode(4); + + if (word->_wordClass == WC_ACTION) + _sentenceConcept->createConcept(0, 1, word); + + flag = true; + break; + + case ACTOR_IS_TO: + _sentenceConcept->changeConcept(1, &_sentenceConcept->_concept0P, 3); + flag = true; + break; + + case ACTOR_IS_FROM: + _sentenceConcept->changeConcept(1, &_sentenceConcept->_concept0P, 4); + break; + + case ACTOR_IS_OBJECT: + flag = resetConcept(&_sentenceConcept->_concept0P, 2); + break; + + case WORD_TYPE_IS_SENTENCE_TYPE: + if (_sentence->_field2C == 1 || _sentence->_field2C == 10) { + for (TTword *wordP = _currentWordP; wordP; wordP = wordP->_nextP) { + if (wordP->_id == 906) { + _sentence->_field2C = 12; + flag = true; + break; + } + } + + TTpicture *newPictP; + TTconcept *newConceptP; + switch (word->_id) { + case 108: + _sentence->_field2C = 8; + break; + case 113: + if (!_sentenceConcept->_concept3P) + _sentence->_field2C = 22; + break; + case 304: + _sentence->_field2C = 25; + break; + case 305: + _sentence->_field2C = 24; + break; + case 306: + _sentence->_field2C = 7; + break; + case 501: + _sentence->_field2C = 9; + break; + case 900: + _sentence->_field2C = 5; + break; + case 901: + _sentence->_field2C = 4; + break; + case 904: + _sentence->_field2C = 6; + break; + case 905: + _sentence->_field2C = 11; + break; + case 906: + _sentence->_field2C = 12; + break; + case 907: + _sentence->_field2C = 13; + break; + case 908: + _sentence->_field2C = 2; + if (!_sentenceConcept->_concept0P) { + newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0); + newConceptP = new TTconcept(newPictP); + + _sentenceConcept->_concept0P = newConceptP; + delete newPictP; + addNode(4); + } + break; + case 909: + _sentence->_field2C = 3; + newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0); + newConceptP = new TTconcept(newPictP); + + _sentenceConcept->_concept2P = newConceptP; + delete newPictP; + addNode(4); + break; + + default: + break; + } + } + + flag = true; + break; + + case COMPLEX_VERB: + if (word->_wordClass == WC_ACTION) { + flag = true; + } else if (!_sentenceConcept->_concept1P) { + TTstring wordStr = word->getText(); + if (wordStr == "do" || wordStr == "doing" || wordStr == "does" || wordStr == "done") { + TTaction *verbP = new TTaction(TTstring("do"), WC_ACTION, 112, 0, + _sentenceConcept->get18()); + status = _sentenceConcept->createConcept(1, 1, verbP); + delete verbP; + } + + flag = true; + } + break; + + case MKTAG('C', 'O', 'M', 'E'): + addNode(7); + addNode(5); + addNode(21); + + if (!_sentence->_field2C) + _sentence->_field2C = 15; + break; + + case MKTAG('C', 'U', 'R', 'S'): + case MKTAG('S', 'E', 'X', 'X'): + if (_sentence->_field58 > 1) + _sentence->_field58--; + flag = true; + break; + + case MKTAG('E', 'X', 'I', 'T'): + addNode(8); + addNode(5); + addNode(21); + + if (!_sentence->_field2C) + _sentence->_field2C = 14; + break; + + case MKTAG('F', 'A', 'R', 'R'): + if (_conceptP->findBy20(0)) + _conceptP->_field20 = 2; + break; + + case MKTAG('F', 'U', 'T', 'R'): + _sentenceConcept->_field18 = 3; + break; + + case MKTAG('G', 'O', 'G', 'O'): + addNode(7); + addNode(5); + addNode(21); + + if (_sentence->_field2C == 1) + _sentence->_field2C = 14; + + flag = true; + break; + + case MKTAG('H', 'E', 'L', 'P'): + if (_sentence->_field2C == 1) + _sentence->_field2C = 18; + + flag = true; + break; + + case MKTAG('L', 'O', 'C', 'F'): + status = _sentenceConcept->createConcept(1, 5, word); + if (!status) + _sentenceConcept->_concept5P->_field20 = 2; + + flag = true; + break; + + case MKTAG('L', 'O', 'C', 'N'): + status = _sentenceConcept->createConcept(1, 5, word); + if (!status) + _sentenceConcept->_concept5P->_field20 = 1; + + flag = true; + break; + + case MKTAG('N', 'E', 'A', 'R'): + if (_conceptP->findBy20(0)) { + _conceptP->_field20 = 1; + } else { + TTpicture *newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0); + status = addToConceptList(newPictP); + _conceptP->_field20 = 1; + if (!status) + delete newPictP; + } + + flag = true; + break; + + case MKTAG('P', 'A', 'S', 'T'): + _sentenceConcept->_field18 = 1; + flag = true; + break; + + case MKTAG('P', 'L', 'E', 'Z'): + if (_sentence->_field58 < 10) + _sentence->_field58++; + break; + + case MKTAG('P', 'R', 'E', 'Z'): + _sentenceConcept->_field18 = 2; + flag = true; + break; + + case MKTAG('S', 'A', 'A', 'O'): + addNode(5); + addNode(4); + flag = true; + break; + + case MKTAG('S', 'S', 'T', 'A'): + addNode(13); + addNode(5); + flag = true; + break; + + case MKTAG('T', 'E', 'A', 'C'): + if (_sentence->_field2C == 1) + _sentence->_field2C = 10; + + flag = true; + break; + + case MKTAG('V', 'O', 'B', 'J'): + status = _sentenceConcept->createConcept(1, 2, word); + flag = true; + break; + + default: + flag = true; + break; + } + + TTparserNode *nextP = static_cast<TTparserNode *>(nodeP->_nextP); + if (flag) + delete nodeP; + nodeP = nextP; + } + + delete concept; + return status; +} + +int TTparser::processRequests(TTword *word) { + int status = loadRequests(word); + switch (status) { + case 0: + status = considerRequests(word); + + // Iterate through the words + while (_currentWordP) { + considerRequests(_currentWordP); + TTword *nextP = _currentWordP->_nextP; + + delete _currentWordP; + _currentWordP = nextP; + } + break; + + case 1: { + TTword *newWord = new TTword(word); + newWord->_nextP = nullptr; + + // Add word to word chain + if (_currentWordP) { + // Add at end of existing chain + for (word = _currentWordP; word->_nextP; word = word->_nextP) + ; + word->_nextP = newWord; + } else { + // First word, so set as head + _currentWordP = newWord; + } + break; + } + + default: + warning("unexpected return from consider requests"); + break; + } + + return status; +} + +int TTparser::addToConceptList(TTword *word) { + TTconcept *concept = new TTconcept(word, ST_UNKNOWN_SCRIPT); + addConcept(concept); + return 0; +} + +void TTparser::addNode(uint tag) { + TTparserNode *newNode = new TTparserNode(tag); + if (_nodesP) + _nodesP->addToHead(newNode); + _nodesP = newNode; +} + +int TTparser::addConcept(TTconcept *concept) { + if (!concept) + return SS_5; + + if (_conceptP) + concept->_nextP = _conceptP; + _conceptP = concept; + + return SS_VALID; +} + +void TTparser::removeConcept(TTconcept *concept) { + // If no concept passed, exit immediately + if (!concept) + return; + + if (_conceptP == concept) { + // Concept specified is the ver ystart of the linked list, so reset head pointer + _conceptP = _conceptP->_nextP; + } else { + // Scan through the linked list, looking for the specific concept + for (TTconcept *currP = _conceptP; currP; currP = currP->_nextP) { + if (currP->_nextP == concept) { + // Found match, so unlink the next link from the chain + currP->_nextP = currP->_nextP->_nextP; + break; + } + } + } + + // FInally, delete the concept + concept->_nextP = nullptr; + delete concept; +} + +void TTparser::removeNode(TTparserNode *node) { + if (!node->_priorP) + // Node is the head of the chain, so reset parser's nodes pointer + _nodesP = static_cast<TTparserNode *>(node->_nextP); + + delete node; +} + +int TTparser::checkForAction() { + int status = SS_VALID; + bool flag = false; + bool actionFlag = false; + + if (_conceptP && _currentWordP) { + // Firstly we need to get the next word to process, and remove it from + // the list pointed to by _currentWordP + TTword *word = _currentWordP; + if (word->_nextP) { + // Chain of words, so we need to find the last word of the chain, + // and set the last-but-one's _nextP to nullptr to detach the last one + TTword *prior = nullptr; + for (word = word->_nextP; word->_nextP; word = word->_nextP) { + prior = word; + } + + if (prior) + prior->_nextP = nullptr; + } else { + // No chain, so singular word can simply be removed + _currentWordP = nullptr; + if (word->_id == 906 && _sentence->_field2C == 1) + _sentence->_field2C = 12; + } + + if (word->_text == "do" || word->_text == "doing" || word->_text == "does" || + word->_text == "done") { + TTstring doStr("do"); + TTaction *action = new TTaction(doStr, WC_ACTION, 112, 0, _sentenceConcept->get18()); + + if (!action->isValid()) { + status = SS_4; + } else { + // Have the new action replace the old word instance + delete word; + word = action; + actionFlag = true; + } + } + + addToConceptList(word); + delete word; + flag = true; + } + + // Handle any remaining words + while (_currentWordP) { + int result = considerRequests(_currentWordP); + if (result > 1) { + status = result; + } else { + // Delete the top of the word chain + TTword *wordP = _currentWordP; + _currentWordP = _currentWordP->_nextP; + delete wordP; + } + } + + if (flag && _conceptP) { + if (actionFlag && (!_sentenceConcept->_concept1P || _sentenceConcept->_concept1P->isWordId(113))) { + _sentenceConcept->replaceConcept(0, 1, _conceptP); + } else if (!_sentenceConcept->_concept5P) { + _sentenceConcept->replaceConcept(1, 5, _conceptP); + } else if (_sentenceConcept->_concept5P->isWordId(904)) { + _sentenceConcept->replaceConcept(0, 5, _conceptP); + } + + removeConcept(_conceptP); + } + + if (_sentence->fn2(3, TTstring("thePlayer"), _sentenceConcept) && !flag) { + if (_sentenceConcept->concept1WordId() == 101) { + _sentence->_field2C = 16; + } else if (_sentence->_field2C != 18 && _sentenceConcept->concept1WordId() == 102) { + if (_sentence->fn2(0, TTstring("targetNpc"), _sentenceConcept)) + _sentence->_field2C = 15; + } + } + + if (_sentence->fn2(2, TTstring("thePlayer"), _sentenceConcept) && + _sentenceConcept->concept1WordId() == 101 && flag) + _sentence->_field2C = 17; + + if (!_sentenceConcept->_concept0P && !_sentenceConcept->_concept1P && + !_sentenceConcept->_concept2P && !_sentenceConcept->_concept5P && !flag) { + if (_conceptP) + filterConcepts(5, 2); + + if (!_sentenceConcept->_concept2P && _sentence->_field2C == 1) + _sentence->_field2C = 0; + } + + if (_sentence->_field58 < 5 && _sentence->_field2C == 1 && !flag) + _sentence->_field2C = 19; + + for (TTconceptNode *nodeP = &_sentence->_sentenceConcept; nodeP; nodeP = nodeP->_nextP) { + if (nodeP->_field18 == 0 && nodeP->_concept1P) { + nodeP->_field18 = _sentence->concept18(nodeP); + } else if (nodeP->_field18 == 4 && !_sentenceConcept->_concept0P) { + if (_sentenceConcept->_concept3P) { + _sentenceConcept->_concept0P = _sentenceConcept->_concept3P; + _sentenceConcept->_concept3P = nullptr; + } else if (_sentenceConcept->_concept2P) { + _sentenceConcept->_concept0P = _sentenceConcept->_concept2P; + _sentenceConcept->_concept2P = nullptr; + } + } + } + + if (_sentence->_field2C == 1 && _sentenceConcept->_concept5P && + _sentenceConcept->_concept2P) { + if (_sentence->fn4(1, 113, nullptr)) { + if (_sentence->fn2(2, TTstring("targetNpc"), nullptr)) { + _sentence->_field2C = 20; + } else if (_sentence->fn2(2, TTstring("thePlayer"), nullptr)) { + _sentence->_field2C = 21; + } else { + _sentence->_field2C = 22; + } + } + } else if (!_sentenceConcept->_concept0P && !_sentenceConcept->_concept1P && + !_sentenceConcept->_concept2P && !_sentenceConcept->_concept5P) { + if (_conceptP) + filterConcepts(5, 2); + + if (!_sentenceConcept->_concept2P && _sentence->_field2C == 1) + _sentence->_field2C = 0; + } + + return status; +} + +int TTparser::fn2(TTword *word) { + switch (word->_id) { + case 600: + addNode(13); + return 0; + + case 601: + addNode(12); + return 1; + + case 602: + case 607: + return checkReferent(static_cast<TTpronoun *>(word)); + + case 608: + return 1; + + default: + return 0; + } +} + +int TTparser::checkReferent(TTpronoun *pronoun) { + TTconcept *concept; + + switch (pronoun->getVal()) { + case 0: + return 0; + + case 1: + concept = new TTconcept(_owner->_script, ST_ROOM_SCRIPT); + break; + + case 2: + concept = new TTconcept(_sentence->_npcScript, ST_NPC_SCRIPT); + break; + + default: + concept = new TTconcept(pronoun, (ScriptType)pronoun->getVal()); + break; + } + + addConcept(concept); + return 0; +} + +void TTparser::conceptChanged(TTconcept *newConcept, TTconcept *oldConcept) { + if (!oldConcept && newConcept != _currentConceptP) + _currentConceptP = nullptr; + else if (oldConcept && oldConcept == _currentConceptP) + _currentConceptP = newConcept; +} + +bool TTparser::checkConcept2(TTconcept *concept, int conceptMode) { + switch (conceptMode) { + case 3: + return concept->checkWordId2(); + + case 5: + return concept->checkWordClass(); + + case 8: + return concept->checkWordId1(); + + case 9: + if (!concept->checkWordId3() && _sentenceConcept->_concept2P) { + if (!_sentenceConcept->_concept2P->checkWordId2() || !concept->checkWordId2()) { + return _sentenceConcept->_concept2P->checkWordClass() && + concept->checkWordClass(); + } + } + break; + + default: + break; + } + + return false; +} + +int TTparser::filterConcepts(int conceptMode, int conceptIndex) { + int result = 0; + + for (TTconcept *currP = _conceptP; currP && !result; currP = currP->_nextP) { + if (checkConcept2(currP, conceptMode)) { + TTconcept **ptrPP = _sentenceConcept->setConcept(conceptIndex, currP); + TTconcept *newConcept = new TTconcept(*currP); + *ptrPP = newConcept; + + if (newConcept->isValid()) { + removeConcept(currP); + (*ptrPP)->_nextP = nullptr; + result = 1; + } else { + result = -2; + } + } + } + + return result; +} + +bool TTparser::resetConcept(TTconcept **conceptPP, int conceptIndex) { + TTconcept **ptrPP = _sentenceConcept->setConcept(conceptIndex, nullptr); + + if (!*ptrPP) + return 0; + + int result = _sentenceConcept->changeConcept(1, conceptPP, conceptIndex); + if (*conceptPP) + _sentenceConcept->setConcept(conceptIndex, *conceptPP); + + return !result; +} + +int TTparser::processModifiers(int modifier, TTword *word) { + TTconcept *newConcept = new TTconcept(word, ST_UNKNOWN_SCRIPT); + + // Cycles through each word + for (TTword *currP = _currentWordP; currP != word; currP = _currentWordP) { + if ((modifier == 2 && currP->_wordClass == WC_ADJECTIVE) || + (modifier == 1 && currP->_wordClass == WC_ADVERB)) { + newConcept->_string2 += ' '; + newConcept->_string2 += _currentWordP->getText(); + } else if (word->_id == 113 && currP->_wordClass == WC_ADJECTIVE) { + addToConceptList(currP); + addNode(13); + } + + if (modifier == 2 || modifier == 3) { + switch (_currentWordP->_id) { + case 94: + _currentConceptP->setOwner(newConcept); + if (_currentWordP) { + _currentWordP->deleteSiblings(); + delete _currentWordP; + _currentWordP = nullptr; + } + + delete newConcept; + newConcept = nullptr; + break; + + case 204: + newConcept->_field34 = 1; + if (_sentence->_field2C == 1) + _sentence->_field2C = 12; + newConcept->_field14 = 1; + break; + + case 300: + newConcept->set1C(atoi(_currentWordP->_text.c_str())); + break; + + case 400: + newConcept->_field14 = 2; + break; + + case 401: + newConcept->_field14 = 1; + break; + + case 601: + newConcept->setOwner(_currentWordP, false); + break; + + case 608: + if (_currentWordP->comparePronounTo(10)) { + newConcept->_field20 = 1; + } else if (_currentWordP->comparePronounTo(11)) { + newConcept->_field20 = 2; + } + + default: + break; + } + } + + if (_currentWordP) { + // Detaches word and deletes it + TTword *wordP = _currentWordP; + _currentWordP = wordP->_nextP; + + wordP->_nextP = nullptr; + delete wordP; + } + } + + if (newConcept) { + newConcept->setFlag(true); + _currentConceptP = newConcept; + addConcept(newConcept); + } + + return 0; +} + +} // End of namespace Titanic |