diff options
Diffstat (limited to 'engines/titanic/true_talk')
78 files changed, 19178 insertions, 0 deletions
diff --git a/engines/titanic/true_talk/barbot_script.cpp b/engines/titanic/true_talk/barbot_script.cpp new file mode 100644 index 0000000000..b327c3647e --- /dev/null +++ b/engines/titanic/true_talk/barbot_script.cpp @@ -0,0 +1,1200 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/barbot_script.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/titanic.h" + +namespace Titanic { + +static const int STATE_ARRAY[7] = { + 0xCAB0, 0xCAB2, 0xCAB3, 0xCAB4, 0xCAB5, 0xCAB6, 0xCAB7 +}; + +static const uint ARRAY1[] = { + 0, 50033, 50044, 50045, 50046, 50047, 50048, 50049, + 50050, 50051, 50034, 50035, 50036, 50037, 50038, 50039, + 50040, 50041, 50042, 50043, 50411, 0 +}; + +static const uint ARRAY2[] = { + 51899, 51900, 51901, 51902, 51903, 51904, 51905, 51906, 51907, 0 +}; + +BarbotScript::BarbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, v4, v5, v6, v7) { + _state = 0; + _arrIndex = 0; + + loadRanges("Ranges/Barbot"); + loadResponses("Responses/Barbot"); + setupSentences(); + _tagMappings.load("TagMap/Barbot"); + _quotes.load("Quotes/Barbot"); + _states.load("States/Barbot"); + _preResponses.load("PreResponses/Barbot"); +} + +void BarbotScript::setupSentences() { + for (int idx = 28; idx < 35; ++idx) + CTrueTalkManager::setFlags(idx, 0); + setupDials(100, 100, 100); + + if (!_currentDialNum) + _currentDialNum = 2; + + _mappings.load("Mappings/Barbot", 8); + _entries.load("Sentences/Barbot"); + _entries2.load("Sentences/Barbot2"); + _words.load("Words/Barbot"); +} + +int BarbotScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + if (tag == MKTAG('D', 'N', 'A', '1') || tag == MKTAG('H', 'H', 'G', 'Q') || + tag == MKTAG('A', 'N', 'S', 'W') || tag == MKTAG('S', 'U', 'M', 'S')) { + if (_state < 7) { + addResponse(STATE_ARRAY[_state++]); + } else { + selectResponse(51896); + setState(1); + _state = 0; + } + + applyResponse(); + return 2; + + } else if (tag == MKTAG('S', 'W', 'E', 'R')) { + adjustDial(0, -18); + adjustDial(1, -5); + + if (getRandomNumber(100) > 50) { + addResponse(getDialogueId(getDialRegion(0) == 0 ? 250200 : 250062)); + applyResponse(); + return 2; + } + + } else if (tag == MKTAG('B', 'A', 'R', 'K') && getRandomNumber(100) > 50) { + selectResponse(250025); + switch (getDialsBitset()) { + case 4: + case 6: + addResponse(getDialogueId(250125)); + break; + default: + break; + } + + applyResponse(); + return 2; + + } else if (tag == MKTAG('B', 'A', 'R', 'U') && getRandomNumber(100) > 50) { + selectResponse(250025); + switch (getDialsBitset()) { + case 4: + case 6: + addResponse(getDialogueId(250112)); + break; + default: + break; + } + + applyResponse(); + return 2; + } + + if (tag == MKTAG('T', 'H', 'R', 'T') || tag == MKTAG('S', 'L', 'O', 'W') || + tag == MKTAG('S', 'E', 'X', '1') || tag == MKTAG('P', 'K', 'U', 'P')) { + adjustDial(0, -7); + adjustDial(1, -3); + } + + return TTnpcScript::chooseResponse(roomScript, sentence, tag); +} + +int BarbotScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + int dialogueId = 0; + + if (roomScript->_scriptId != 112) + return 2; + + checkItems(roomScript, sentence); + if (isState9()) { + if (sentence->localWord("visioncenter") || sentence->localWord("brain") || + sentence->contains("vision") || sentence->contains("visual") || + sentence->contains("brain") || sentence->contains("crystal")) { + if (CTrueTalkManager::getStateValue(2)) { + addResponse(getDialogueId(251003)); + applyResponse(); + CTrueTalkManager::triggerAction(6, 0); + return 2; + } + } + + if (sentence->contains("goldfish")) { + addResponse(getDialogueId(250184)); + applyResponse(); + return 2; + } + + dialogueId = ARRAY1[getRandomNumber(20)]; + if (!ARRAY2[_arrIndex]) + _arrIndex = 0; + + if (_arrIndex) { + dialogueId = ARRAY2[_arrIndex++]; + } else if (getRandomNumber(100) > 35) { + dialogueId = ARRAY2[0]; + _arrIndex = 1; + } else if (getRandomNumber(100) > 60) { + switch (sentence->_field2C) { + case 2: + dialogueId = 51914; + break; + case 3: + dialogueId = 51911; + break; + case 4: + dialogueId = 51913; + break; + case 5: + dialogueId = 51912; + break; + case 6: + dialogueId = 51915; + break; + case 7: + dialogueId = 51909; + break; + default: + break; + } + } + + addResponse(dialogueId); + if (getRandomNumber(100) > 65) + addResponse(getDialogueId(251250)); + applyResponse(); + return 2; + } + + CTrueTalkManager::setFlags(29, getValue(29) - 1); + CTrueTalkManager::setFlags(30, getValue(30) - 1); + CTrueTalkManager::setFlags(31, getValue(31) - 1); + CTrueTalkManager::setFlags(32, getValue(32) - 1); + CTrueTalkManager::setFlags(33, getValue(33) - 1); + CTrueTalkManager::setFlags(34, getValue(34) - 1); + + TTtreeResult treeResult; + int val34 = getState(); + setState(0); + + int val2C = sentence->_field2C; + bool flag = val2C == 11 || val2C == 13; + bool flag2 = val2C == 12; + + if (!val34) { + goto done; + } else if (val34 > 50357) { + goto done; + } else if (val34 == 50357) { + return applySentenceIds(50358, -1); + } + + switch (val34) { + case 1: + if (flag) + return applySentenceIds(51898, 2); + if (flag2) + return applySentenceIds(51897); + break; + case 2: + if (flag) + return applySentenceIds(51897); + break; + case 3: + if (sentence->localWord("useless") || sentence->contains("useless")) + return applySentenceIds(50824); + break; + case 4: + if (flag) + return applySentenceIds(getRandomBit() ? 50512 : 51642); + else if (flag2) + return applySentenceIds(getRandomBit() ? 50511 : 51643); + break; + case 5: + if (flag) + return applySentenceIds(50829, 6); + if (flag2) + return applySentenceIds(50828); + break; + case 6: + if (flag) + return applySentenceIds(50831); + if (flag2) + return applySentenceIds(50830); + break; + case 7: + if (flag2 || sentence->contains("never")) + return applySentenceIds(51553); + if (flag || sentence->contains("nicest")) + return applySentenceIds(51554); + break; + case 8: + if (flag) + return applySentenceIds(50961); + if (flag2) + return applySentenceIds(50960); + break; + case 9: + if (flag) + return applySentenceIds(getDialogueId(251858)); + break; + case 10: + if (flag) + return applySentenceIds(getDialogueId(251014)); + else if (flag2) + return applySentenceIds(getDialogueId(251013)); + break; + case 11: + if (flag) + return applySentenceIds(getDialogueId(251008)); + else if (flag2) + return applySentenceIds(getDialogueId(251007)); + break; + case 12: + if (flag) + return applySentenceIds(getDialogueId(250656)); + else if (flag2) + return applySentenceIds(getDialogueId(250655)); + break; + case 13: + if (flag) + return applySentenceIds(getDialogueId(250614)); + else if (flag2) + return applySentenceIds(getDialogueId(250613)); + break; + case 14: + if (val2C == 6) + return applySentenceIds(getDialogueId(250946)); + break; + case 15: + if (flag || sentence->contains("or")) { + return applySentenceIds(getDialogueId(250526), 16); + } else { + if (g_vm->_trueTalkManager->_quotesTree.search( + sentence->_normalizedLine.c_str(), TREE_3, &treeResult, 0, nullptr) != -1) { + uint newId = getDialogueId(250526); + return applySentenceIds(newId, 16); + } + } + break; + case 17: + if (flag) { + return applySentenceIds(50382); + } else if (flag2) { + return applySentenceIds(51423); + } + // Deliberate fall-through + + case 16: + if (val2C == 7 || val2C == 10) + return applySentenceIds(getDialogueId(250525)); + break; + case 18: + return applySentenceIds(getDialogueId(250589)); + case 19: + return applySentenceIds(getDialogueId(250565), 20); + case 20: + if (flag) + return applySentenceIds(50307); + if (flag2) + return applySentenceIds(50306); + break; + case 21: + if (flag) + return applySentenceIds(50359); + if (flag2) + return applySentenceIds(50357); + break; + case 23: + if (val2C == 6 || val2C == 10) + return applySentenceIds(getDialogueId(250551)); + break; + case 24: + if (sentence->contains("do not know") + || sentence->contains("no idea") + || sentence->contains("a clue")) { + return applySentenceIds(getDialogueId(250553)); + } else { + return applySentenceIds(getDialogueId(250552)); + } + break; + case 25: + if (flag || val2C == 10) + applySentenceIds(getDialogueId(251899), 26); + else if (flag2) + return applySentenceIds(50215); + break; + case 26: + if (g_vm->_trueTalkManager->_quotesTree.search( + sentence->_normalizedLine.c_str(), TREE_3, &treeResult, 0, nullptr) != -1) + return applySentenceIds(getDialogueId(251899), 26); + break; + + case 27: + if (flag) + return applySentenceIds(getDialogueId(250766)); + else if (flag2) + return applySentenceIds(getDialogueId(250764)); + break; + case 28: + return applySentenceIds(getDialogueId(250765)); + case 29: + return applySentenceIds(getDialogueId(250652)); + case 30: + return applySentenceIds(getDialogueId(250653)); + case 31: + if (flag) + return applySentenceIds(getDialogueId(250664)); + else if (flag2) + return applySentenceIds(getDialogueId(250663)); + break; + case 32: + if (flag) + return applySentenceIds(getDialogueId(250643)); + else if (flag2) + return applySentenceIds(getDialogueId(250642)); + break; + case 33: + return applySentenceIds(50763); + case 34: + if (flag) + return applySentenceIds(getDialogueId(251622)); + else if (flag2) + return applySentenceIds(getDialogueId(251624)); + break; + case 35: + if (val2C == 6 || val2C == 10) + return applySentenceIds(getDialogueId(251623)); + break; + case 36: + if (flag) + return applySentenceIds(50335); + if (flag2) + return applySentenceIds(50334); + break; + case 37: + if (flag) + return applySentenceIds(50217); + if (flag2) + return applySentenceIds(50153); + break; + case 38: + return applySentenceIds(getDialogueId(250637)); + case 39: + return applySentenceIds(getDialogueId(250638)); + case 40: + return applySentenceIds(getDialogueId(250639)); + case 41: + return applySentenceIds(getDialogueId(250640)); + case 42: + if (flag) + return applySentenceIds(getDialogueId(250676)); + else if (flag2) + return applySentenceIds(getDialogueId(250673)); + break; + case 43: + if (flag) + return applySentenceIds(50416, -1); + if (flag2) + return applySentenceIds(50415, -1); + break; + case 44: + if (flag) + return applySentenceIds(getDialogueId(250468)); + else if (flag2) + return applySentenceIds(getDialogueId(250413)); + + if (val2C == 6 || val2C == 10) + return applySentenceIds(getDialogueId(251649)); + break; + case 45: + if (sentence->localWord("summer") + || sentence->contains("summer") + || sentence->localWord("autumn") + || sentence->contains("autumn")) { + return applySentenceIds(50743); + } else if (sentence->localWord("winter") || sentence->contains("winter")) { + return applySentenceIds(50696); + } else { + return applySentenceIds(50225); + } + break; + case 46: + if (val2C == 7 || val2C == 10) + return applySentenceIds(50698); + break; + case 47: + if (flag || flag2 || val2C == 6) + return applySentenceIds(50717); + break; + case 48: + if (flag) + return applySentenceIds(50710); + if (flag2) + return applySentenceIds(50225); + break; + case 49: + if (sentence->localWord("scraliontis") || sentence->contains("scraliontis")) + return applySentenceIds(50711); + if (sentence->localWord("brobostigon") || sentence->contains("brobostigon")) + return applySentenceIds(50712); + break; + case 50: + return applySentenceIds(50713); + case 51: + if (flag) + return applySentenceIds(50715); + if (flag2) + return applySentenceIds(50714); + break; + case 52: + if (sentence->localWord("note") || sentence->contains("note")) + return applySentenceIds(50716); + return applySentenceIds(50210); + case 53: + return applySentenceIds(50210); + case 54: + if (getDialRegion(0) != 0) { + if (val2C == 12) + return applySentenceIds(50174); + else + return applySentenceIds(50300); + } else if (val2C == 7 || val2C == 10) { + return applySentenceIds(50871); + } + break; + case 55: + if (flag) + return applySentenceIds(50302); + if (flag2) + return applySentenceIds(50301); + break; + case 56: + if (flag) + return applySentenceIds(50304); + if (flag2) + return applySentenceIds(50303); + break; + case 57: + if (sentence->localWord("mustard") + || sentence->contains("mustard") + || sentence->localWord("tomato") + || sentence->contains("tomato")) + return applySentenceIds(50320); + if (sentence->localWord("sauce") + || sentence->localWord("puree") + || sentence->contains("sauce") + || sentence->contains("puree") + || sentence->contains("bird") + || sentence->contains("starling")) { + applySentenceIds(50321); + CTrueTalkManager::triggerAction(30, 0); + return 2; + } + + return applySentenceIds(50320); + case 58: + if (val2C == 6 || val2C == 10) + return applySentenceIds(50880); + break; + case 59: + if (flag) { + if (addRandomResponse(true)) { + setState(59); + return 2; + } + } else if (flag2) { + return applySentenceIds(getDialogueId(251754)); + } + break; + case 60: + if (flag && addRandomResponse(true)) { + setState(59); + return 2; + } else if (flag2 || val2C == 7 || val2C == 10) { + return applySentenceIds(getDialogueId(251712)); + } + break; + case 61: + if (val2C == 3) { + if (sentence->localWord("loop")) + return applySentenceIds(getDialogueId(250269)); + else if (sentence->localWord("do")) + return applySentenceIds(getDialogueId(250270)); + } else if (val2C == 7) { + return applySentenceIds(getDialogueId(250270)); + } else if (flag) { + return applySentenceIds(getDialogueId(250270)); + } + + return applySentenceIds(getDialogueId(250272)); + case 62: + if (flag + || (val2C == 3 && sentence->localWord("do")) + || val2C == 7 + || sentence->localWord("help")) + return applySentenceIds(getDialogueId(250270)); + + return applySentenceIds(getDialogueId(2570272)); + case 63: + if (flag + || (val2C == 3 || sentence->localWord("do")) + || val2C == 7 + || sentence->localWord("help")) + return applySentenceIds(getDialogueId(250271)); + + return applySentenceIds(getDialogueId(250272)); + case 64: + if (flag || val2C == 3 || val2C == 8) + return applySentenceIds(getDialogueId(250631)); + break; + case 65: + if (sentence->localWord("now") || sentence->localWord("soonh")) + return applySentenceIds(getDialogueId(250424)); + return applySentenceIds(getDialogueId(250506)); + case 66: + if (flag || sentence->localWord("good") || sentence->localWord("well")) + return applySentenceIds(getDialogueId(251027)); + return applySentenceIds(getDialogueId(251021)); + case 67: + if (flag || val2C == 6 || val2C == 10) { + setDial(0, getDialLevel(0, false) - 8); + return applySentenceIds(getDialogueId(251589)); + } + break; + case 68: + if (flag || val2C == 6 || val2C == 10) { + setDial(0, getDialLevel(0, false) - 12); + return applySentenceIds(getDialogueId(251590)); + } + break; + case 69: + if (flag || val2C == 6 || val2C == 10) { + setDial(0, getDialLevel(0, false) - 25); + return applySentenceIds(getDialogueId(251591)); + } + break; + default: + break; + } + +done: + // Adjust primary dial + setState(0); + if (sentence->get58() != 5) { + adjustDial(0, sentence->get58() * 4 - 20); + } else if (getDialLevel(0, false) > 65) { + adjustDial(0, -2 - getRandomNumber(7)); + } else if (getDialLevel(0, false) < 35) { + adjustDial(0, 2 + getRandomNumber(7)); + } + + updateCurrentDial(true); + + if (sentence->contains("goldfish")) { + addResponse(250184); + } else if ((sentence->localWord("puree") || sentence->localWord("pureed")) + && sentence->localWord("parrot")) { + addResponse(250021); + } else if (sentence->localWord("starling")) { + addResponse(250024); + } else { + if (getRandomNumber(100) > 95 && getDialRegion(2) == 0) { + addResponse(getDialogueId(250210)); + } + + if (processEntries(&_entries, _entryCount, roomScript, sentence) == 2) + return 2; + if (processEntries(_defaultEntries, 0, roomScript, sentence) != 2 + && !defaultProcess(roomScript, sentence)) { + int dval = 0; + flag = getRandomNumber(100) > 50; + int val; + + switch (_field2C) { + case 2: + val = getValue(29); + if (val < 16) + val += 4; + if (val < 9) { + val = val / 2; + dval = 250081 + (flag ? 0 : 267); + } + CTrueTalkManager::setFlags(29, val); + break; + + case 3: + val = getValue(30); + if (val < 16) + val += 4; + if (val < 9) { + val = val / 2; + dval = 250081 + (flag ? 0 : 243); + } + CTrueTalkManager::setFlags(30, val); + break; + + case 4: + val = getValue(31); + if (val < 16) + val += 4; + if (val < 9) { + val = val / 2; + dval = 250081 + (flag ? 0 : 256); + } + CTrueTalkManager::setFlags(31, val); + break; + + case 5: + val = getValue(32); + if (val < 16) + val += 4; + if (val < 9) { + val = val / 2; + dval = 250081 + (flag ? 0 : 251); + } + CTrueTalkManager::setFlags(32, val); + break; + + case 6: + val = getValue(33); + if (val < 16) + val += 4; + if (val < 9) { + val = val / 2; + dval = 250081 + (flag ? 0 : 273); + } + CTrueTalkManager::setFlags(33, val); + break; + + case 7: + val = getValue(34); + if (val < 16) + val += 4; + if (val < 9) { + val = val / 2; + dval = 250081 + (flag ? 0 : 236); + } + CTrueTalkManager::setFlags(34, val); + break; + + + case 11: + addResponse(getDialogueId(250463)); + applyResponse(); + return 2; + + case 12: + addResponse(getDialogueId(250455)); + applyResponse(); + return 2; + + case 13: + addResponse(getDialogueId(250447)); + applyResponse(); + return 2; + + case 19: + return applySentenceIds(getDialogueId(getDialRegion(0) ? 250062 : 250200)); + + default: + break; + } + + if (dval) { + adjustDial(0, -9); + adjustDial(1, -2); + + if (dval != 250081) { + selectResponse(250286); + selectResponse(250296); + selectResponse(250307); + applyResponse(); + return 2; + } + } else if (processEntries(&_entries2, 0, roomScript, sentence) == 2) { + return 2; + } + + addResponse(getDialogueId(250082 + getRandomNumber(100) <= 89 ? 128 : 0)); + } + } + + applyResponse(); + return 2; +} + +ScriptChangedResult BarbotScript::scriptChanged(const TTroomScript *roomScript, uint id) { + switch (id) { + case 1: + case 100: + if (!isState9()) { + selectResponse(250210); + applyResponse(); + } + + adjustDial(0, getRandomBit() ? getRandomNumber(5) * 4 : + -(int)getRandomNumber(5) * 4); + break; + + case 3: + if (isState9()) { + selectResponse(250244); + applyResponse(); + resetFlags(); + } else { + if (!getValue(28) || !fn10(true)) { + addResponse(getDialogueId(251627 + getValue(28) ? -1034 : 0)); + applyResponse(); + } + + CTrueTalkManager::setFlags(28, 1); + resetFlags(); + } + break; + + case 4: + selectResponse(isState9() ? 250141 : 250140); + applyResponse(); + adjustDial(2, getDialLevel(2, false) < 50 ? -15 - getRandomNumber(30) : + 15 + getRandomNumber(30)); + + if (getDialRegion(1) != 0 && getRandomNumber(100) > 75) + adjustDial(1, -35); + break; + + case 143: + addResponse(getDialogueId(isState9() ? 250577 : 250576)); + break; + + case 144: + addResponse(getDialogueId(isState9() ? 250577 : 250584)); + break; + + case 145: + if (isState9()) { + addResponse(getDialogueId(250577)); + applyResponse(); + } else { + setState(57); + } + break; + + case 146: + addResponse(getDialogueId(isState9() ? 250577 : 250574)); + break; + + case 147: + addResponse(getDialogueId(250579)); + break; + + } + + if (id >= 250000 && id <= 251900) { + if (id > 250571) { + if (id != 250575 && (id == 250586 || id == 251858 || !isState9())) { + addResponse(getDialogueId(id)); + applyResponse(); + } + } else if (id == 250571 || (id != 250244 && !isState9()) || isState9()) { + addResponse(getDialogueId(id)); + applyResponse(); + } else { + addResponse(getDialogueId(251018)); + applyResponse(); + } + } + + return SCR_2; +} + +int BarbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + switch (tagId) { + case MKTAG('A', 'D', 'V', 'T'): + case MKTAG('A', 'R', 'T', 'I'): + case MKTAG('A', 'R', 'T', 'Y'): + case MKTAG('B', 'R', 'N', 'D'): + case MKTAG('C', 'O', 'M', 'D'): + case MKTAG('D', 'N', 'C', 'E'): + case MKTAG('H', 'B', 'B', 'Y'): + case MKTAG('M', 'A', 'G', 'S'): + case MKTAG('L', 'I', 'T', 'R'): + case MKTAG('M', 'C', 'P', 'Y'): + case MKTAG('M', 'I', 'N', 'S'): + case MKTAG('M', 'U', 'S', 'I'): + case MKTAG('N', 'I', 'K', 'E'): + case MKTAG('S', 'F', 'S', 'F'): + case MKTAG('S', 'O', 'A', 'P'): + case MKTAG('S', 'O', 'N', 'G'): + case MKTAG('S', 'P', 'R', 'T'): + case MKTAG('T', 'E', 'A', 'M'): + case MKTAG('T', 'U', 'S', 'H'): + case MKTAG('W', 'W', 'E', 'B'): + tagId = MKTAG('E', 'N', 'T', 'N'); + break; + case MKTAG('A', 'U', 'T', 'H'): + case MKTAG('B', 'A', 'R', 'K'): + case MKTAG('B', 'L', 'R', '1'): + case MKTAG('B', 'L', 'P', '1'): + case MKTAG('B', 'L', 'P', '2'): + case MKTAG('B', 'L', 'P', '3'): + case MKTAG('B', 'L', 'P', '4'): + case MKTAG('B', 'L', 'T', '1'): + case MKTAG('B', 'L', 'T', '2'): + case MKTAG('B', 'L', 'T', '3'): + case MKTAG('B', 'L', 'T', '4'): + case MKTAG('B', 'L', 'T', '5'): + case MKTAG('B', 'O', 'Y', 'S'): + case MKTAG('C', 'O', 'P', 'S'): + case MKTAG('D', 'C', 'T', 'R'): + case MKTAG('F', 'A', 'M', 'E'): + case MKTAG('F', 'A', 'S', 'H'): + case MKTAG('G', 'I', 'R', 'L'): + case MKTAG('H', 'E', 'R', 'O'): + case MKTAG('H', 'O', 'S', 'T'): + case MKTAG('K', 'N', 'O', 'B'): + case MKTAG('N', 'H', 'R', 'O'): + case MKTAG('R', 'A', 'C', 'E'): + case MKTAG('S', 'C', 'I', 'T'): + case MKTAG('T', 'D', 'V', 'P'): + case MKTAG('T', 'W', 'A', 'T'): + case MKTAG('W', 'E', 'A', 'T'): + tagId = MKTAG('P', 'R', 'S', 'N'); + break; + case MKTAG('C', 'H', 'S', 'E'): + case MKTAG('C', 'M', 'N', 'T'): + case MKTAG('F', 'I', 'L', 'M'): + case MKTAG('J', 'F', 'O', 'D'): + case MKTAG('L', 'I', 'Q', 'D'): + tagId = MKTAG('F', 'O', 'O', 'D'); + break; + case MKTAG('C', 'R', 'M', 'N'): + case MKTAG('C', 'S', 'P', 'Y'): + case MKTAG('U', 'B', 'A', 'D'): + tagId = MKTAG('V', 'B', 'A', 'D'); + break; + case MKTAG('E', 'A', 'R', 'T'): + case MKTAG('H', 'O', 'M', 'E'): + case MKTAG('N', 'P', 'L', 'C'): + case MKTAG('P', 'L', 'A', 'C'): + case MKTAG('P', 'L', 'A', 'N'): + tagId = MKTAG('P', 'L', 'A', 'C'); + break; + case MKTAG('F', 'A', 'U', 'N'): + case MKTAG('F', 'I', 'S', 'H'): + case MKTAG('F', 'L', 'O', 'R'): + tagId = MKTAG('N', 'A', 'T', 'R'); + break; + case MKTAG('H', 'H', 'L', 'D'): + case MKTAG('T', 'O', 'Y', 'S'): + case MKTAG('W', 'E', 'A', 'P'): + tagId = MKTAG('M', 'A', 'C', 'H'); + break; + case MKTAG('M', 'L', 'T', 'Y'): + case MKTAG('P', 'G', 'R', 'P'): + case MKTAG('P', 'T', 'I', 'C'): + tagId = MKTAG('G', 'R', 'U', 'P'); + break; + case MKTAG('P', 'K', 'U', 'P'): + case MKTAG('S', 'E', 'X', '1'): + case MKTAG('S', 'W', 'E', 'R'): + tagId = MKTAG('R', 'U', 'D', 'E'); + break; + case MKTAG('P', 'H', 'I', 'L'): + case MKTAG('R', 'C', 'K', 'T'): + case MKTAG('S', 'C', 'I', 'E'): + tagId = MKTAG('S', 'C', 'I', 'E'); + break; + case MKTAG('T', 'R', 'A', '2'): + case MKTAG('T', 'R', 'A', '3'): + tagId = MKTAG('T', 'R', 'A', 'V'); + break; + default: + break; + } + + if (val == 36) { + switch (getValue(1)) { + case 1: + return setResponse(getDialogueId(220837), -1); + break; + case 2: + return setResponse(getDialogueId(220849), -1); + default: + return setResponse(getDialogueId(220858), -1); + } + } else if (val == 61 && getValue(1) > 2) { + return setResponse(getDialogueId(222301), -1); + } + + return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); +} + +int BarbotScript::updateState(uint oldId, uint newId, int index) { + if (newId == 250538) { + CTrueTalkManager::triggerAction(28, 0); + return 250538; + } + if (newId == 251704) { + return 251701 + (_field7C ? 3 : 0); + } + + for (uint idx = 0; idx < _states.size(); ++idx) { + const TTupdateState &us = _states[idx]; + if (us._newId == newId) { + if ((us._dialBits & 1) && !getDialRegion(0)) + continue; + if ((us._dialBits & 2) && getDialRegion(0)) + continue; + if ((us._dialBits & 4) && !getDialRegion(1)) + continue; + if ((us._dialBits & 8) && getDialRegion(1)) + continue; + if ((us._dialBits & 0x10) && !getDialRegion(2)) + continue; + if ((us._dialBits & 0x20) && getDialRegion(2)) + continue; + + setState(us._newValue); + break; + } + } + + return newId; +} + +int BarbotScript::preResponse(uint id) { + if (getDialRegion(0) == 0 && getRandomNumber(100) > 80) + return 251250; + + return _preResponses.find(id); +} + +uint BarbotScript::getDialsBitset() const { + uint bits = 0; + if (!getDialRegion(0)) + bits = 1; + if (!getDialRegion(1)) + bits |= 2; + if (!getDialRegion(2)) + bits |= 4; + + return bits; +} + +int BarbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + int v34 = getState(); + uint id = 0; + + if (v34 > 0x200) { + switch (v34 - 0x201) { + case 0: + if (getValue(4) != 2) + id = 250738; + break; + case 1: + if (getValue(4) != 3) + id = 250738; + case 2: + if (getValue(4) != 0) + id = 250738; + break; + default: + break; + } + } else if (v34 == 0x200) { + if (getValue(4) != 1) + id = 250738; + } else { + switch (v34) { + case 2: + if (getValue(1) != 1) + return 1; + break; + case 3: + if (getValue(1) != 2) + return 1; + break; + case 4: + if (getValue(1) != 3) + return 1; + break; + case 5: + if (getValue(1) == 3) + return 1; + break; + case 6: + if (sentence->contains("do not") || sentence->contains("have no") || + sentence->contains("got no")) + return 1; + break; + case 7: + if (!sentence->contains("do not") && !sentence->contains("have no") && + !sentence->contains("got no")) + return 1; + break; + case 8: + if (sentence->_field38 == 2) + return 1; + break; + case 9: { + uint val = CTrueTalkManager::getStateValue(3); + bool bit0 = (val & 1) != 0; + bool bit2 = (val & 4) != 0; + bool bit3 = (val & 8) != 0; + + if (bit2) { + if (!bit0) { + id = 250085 - (bit3 ? 0 : 199715); + break; + } else if (!bit3) { + id = 250627; + } + } else { + if (!bit0) { + id = 50365 + (bit3 ? 0 : 2); + } else if (!bit3) { + id = 50370; + } + } + + if (id) { + addResponse(getDialogueId(id)); + applyResponse(); + return 2; + } + break; + } + + case 10: { + uint val = CTrueTalkManager::getStateValue(3); + bool bit0 = (val & 1) != 0; + bool bit2 = (val & 4) != 0; + bool bit3 = (val & 8) != 0; + + if (bit0 && bit2 && bit3) { + addResponse(getDialogueId(251027)); + applyResponse(); + CTrueTalkManager::triggerAction(7, 0); + return 2; + } else { + if (getDialRegion(1) == 1) { + if (*srcIdP != 251650) + id = 251651; + } else { + addResponse(getDialRegion(0) != 0 ? 51444 : 51530); + applyResponse(); + return 2; + } + } + break; + } + + case 11: + if (CTrueTalkManager::getStateValue(2) != 0) { + CTrueTalkManager::triggerAction(6, 0); + id = 251003; + } + break; + + case 12: + if (getDialRegion(1) == 0) { + addResponse(getDialogueId(251871)); + applyResponse(); + return 2; + } else if (getRandomNumber(100) > 25 && addRandomResponse(false)) { + return 2; + } + + default: + break; + } + } + + if (id) { + addResponse(getDialogueId(id)); + applyResponse(); + } + + return 2; +} + +void BarbotScript::setDialRegion(int dialNum, int region) { + TTnpcScript::setDialRegion(dialNum, region); + selectResponse(250365); + applyResponse(); +} + +void BarbotScript::adjustDial(int dialNum, int amount) { + int level = CLIP(getDialLevel(dialNum) + amount, 0, 100); + setDial(dialNum, level); +} + +bool BarbotScript::isState9() const { + return CTrueTalkManager::getStateValue(9) != 0; +} + +int BarbotScript::applySentenceIds(int dialogueId, int v34) { + addResponse(dialogueId); + applyResponse(); + + if (v34 != -1) { + setState(v34); + } else { + for (uint idx = 0; idx < _mappings.size(); ++idx) { + const TTscriptMapping &m = _mappings[idx]; + for (int vidx = 0; vidx < _mappings._valuesPerMapping; ++idx) { + if (m._values[vidx] == (uint)dialogueId) { + updateState(m._id, m._id, vidx); + break; + } + } + } + } + + return -2; +} + +int BarbotScript::setResponse(int dialogueId, int state) { + addResponse(dialogueId); + applyResponse(); + + if (state != -1) + setState(state); + return 2; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/barbot_script.h b/engines/titanic/true_talk/barbot_script.h new file mode 100644 index 0000000000..1820d77216 --- /dev/null +++ b/engines/titanic/true_talk/barbot_script.h @@ -0,0 +1,106 @@ +/* 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. + * + */ + +#ifndef TITANIC_BARBOT_SCRIPT_H +#define TITANIC_BARBOT_SCRIPT_H + +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class BarbotScript : public TTnpcScript { +private: + int _state; + int _arrIndex; + TTsentenceEntries _entries2; + TTupdateStateArray _states; + TTmapEntryArray _preResponses; +private: + /** + * Adjust a given dial number by a given delta amount + */ + void adjustDial(int dialNum, int amount); + + /** + * Setup sentence data + */ + void setupSentences(); + + bool isState9() const; + + int applySentenceIds(int dialogueId, int v34 = -1); + + /** + * Add a response and optionally set the state + */ + int setResponse(int dialogueId, int state = -1); +public: + BarbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); + + /** + * Returns a bitset of the first three dialgs being on or not + */ + virtual uint getDialsBitset() const; + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Sets a given dial to be pointing in a specified region (0 to 2) + */ + virtual void setDialRegion(int dialNum, int region); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_BARBOT_SCRIPT_H */ diff --git a/engines/titanic/true_talk/bellbot_script.cpp b/engines/titanic/true_talk/bellbot_script.cpp new file mode 100644 index 0000000000..7da2ab6201 --- /dev/null +++ b/engines/titanic/true_talk/bellbot_script.cpp @@ -0,0 +1,1905 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/bellbot_script.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/pet_control/pet_control.h" +#include "titanic/core/node_item.h" +#include "titanic/titanic.h" + +namespace Titanic { + +uint BellbotScript::_oldId; + +static const RoomDialogueId ROOM_DIALOGUE_IDS[] = { + { 100, 201442 },{ 101, 201417 },{ 107, 201491 },{ 108, 201421 }, + { 109, 201437 },{ 110, 201431 },{ 111, 201457 },{ 112, 201411 }, + { 113, 201424 },{ 114, 201464 },{ 115, 201407 },{ 116, 201468 }, + { 117, 201447 },{ 122, 201491 },{ 123, 201299 },{ 124, 201479 }, + { 125, 201480 },{ 126, 201476 },{ 127, 201483 },{ 128, 201399 }, + { 129, 201400 },{ 130, 201387 },{ 131, 201395 },{ 132, 201388 }, + { 0, 0 } +}; + +BellbotScript::BellbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, -1, -1, -1, 0), + _field2D0(0), _field2D4(0), _field2D8(0), _field2DC(0), + _room107First(false) { + CTrueTalkManager::setFlags(25, 0); + CTrueTalkManager::setFlags(24, 0); + CTrueTalkManager::setFlags(40, 0); + CTrueTalkManager::setFlags(26, 0); + + setupDials(0, 0, 0); + _array[0] = 100; + _array[1] = 0; + + loadRanges("Ranges/Bellbot"); + loadResponses("Responses/Bellbot", 4); + setupSentences(); + _tagMappings.load("TagMap/Bellbot"); + _words.load("Words/Bellbot"); + _quotes.load("Quotes/Bellbot"); + _states.load("States/Bellbot"); + _preResponses.load("PreResponses/Bellbot"); + _phrases.load("Phrases/Bellbot"); +} + +void BellbotScript::setupSentences() { + _mappings.load("Mappings/Bellbot", 1); + _entries.load("Sentences/Bellbot"); + for (int idx = 1; idx < 20; ++idx) + _sentences[idx].load(CString::format("Sentences/Bellbot/%d", idx)); + + _field2DC = 0; + _field68 = 0; + _entryCount = 0; +} + +int BellbotScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + int val24 = getValue(24); + CTrueTalkManager::setFlags(24, 0); + + int result = preprocess(roomScript, sentence); + if (result != 1) + return 1; + + CTrueTalkManager::setFlags(23, 0); + setState(0); + if (getValue(1) <= 2) + updateCurrentDial(1); + + // Handle room specific sentences + switch (roomScript->_scriptId) { + case 101: + if (getValue(2) == 1) { + result = processEntries(&_sentences[11], 0, roomScript, sentence); + } + break; + + case 107: + result = processEntries(&_sentences[5], 0, roomScript, sentence); + break; + + case 108: + result = processEntries(&_sentences[7], 0, roomScript, sentence); + break; + + case 109: + result = processEntries(&_sentences[13], 0, roomScript, sentence); + break; + + case 110: + result = processEntries(&_sentences[16], 0, roomScript, sentence); + break; + + case 111: + result = processEntries(&_sentences[10], 0, roomScript, sentence); + break; + + case 112: + result = processEntries(&_sentences[15], 0, roomScript, sentence); + break; + + case 113: + result = processEntries(&_sentences[9], 0, roomScript, sentence); + break; + + case 114: + result = processEntries(&_sentences[18], 0, roomScript, sentence); + break; + + case 115: + result = processEntries(&_sentences[12], 0, roomScript, sentence); + break; + + case 116: + result = processEntries(&_sentences[8], 0, roomScript, sentence); + break; + + case 117: + result = processEntries(&_sentences[6], 0, roomScript, sentence); + break; + + case 123: + result = processEntries(&_sentences[17], 0, roomScript, sentence); + break; + + case 125: + result = processEntries(&_sentences[14], 0, roomScript, sentence); + break; + + case 131: + if (getValue(26) == 0) { + result = processEntries(&_sentences[getValue(6) ? 5 : 4], 0, roomScript, sentence); + } + break; + } + + if (result == 2) + return 2; + if (sentence->contains("pretend you summoned yourself") || + sentence->contains("pretend you just summoned yourself")) { + if (scriptChanged(roomScript, 157) == 2) + return 2; + } + + if (sentence->localWord("television") || roomScript->_scriptId == 111) { + if (sentence->localWord("drop") || sentence->localWord("throw") + || sentence->localWord("smash") || sentence->localWord("destroy") + || sentence->localWord("toss") || sentence->localWord("put") + || sentence->localWord("pitch") || sentence->localWord("heft")) { + if (getValue(40) == 1) { + addResponse(getDialogueId(201687)); + applyResponse(); + return 2; + } + else if (roomScript->_scriptId == 111) { + addResponse(getDialogueId(201687)); + applyResponse(); + CTrueTalkManager::triggerAction(17, 0); + CTrueTalkManager::setFlags(40, 1); + return 2; + } + else { + addResponse(getDialogueId(200710)); + addResponse(getDialogueId(201334)); + applyResponse(); + return 2; + } + } + } + + if (sentence->contains("what should i do here") + || sentence->contains("what do i do here") + || sentence->contains("what shall i do in here") + || sentence->contains("what shall i do in this room") + || sentence->contains("what should i do in this room") + || sentence->contains("what am i supposed to do in here") + || sentence->contains("what should i do in here") + || sentence->contains("what do i do in this room")) { + if (addRoomDescription(roomScript)) { + applyResponse(); + return 2; + } + } + + if (sentence->contains("help") + || sentence->contains("what now") + || sentence->contains("what next") + || sentence->contains("give me a hint") + || sentence->contains("i need a hint") + || sentence->contains("what should i be doing") + || sentence->contains("what do you reckon i should do now") + || sentence->contains("what shall i do") + || sentence->contains("what would you do") + || sentence->contains("what should i do") + || sentence->contains("what do i do")) { + if (getDialRegion(0) == 1) { + randomResponse4(roomScript, getValue(1)); + applyResponse(); + return 2; + } else { + randomResponse3(roomScript, getValue(1)); + } + } + + if (sentence->get58() > 6 && sentence->contains("please")) { + addResponse(getDialogueId(200432)); + applyResponse(); + return 2; + } + + if (checkCommonSentences(roomScript, sentence) == 2) + return 2; + + // WORKAROUND: Skip processEntries call on unassigned sentence array + + // Standard sentence list + if (processEntries(&_entries, _entryCount, roomScript, sentence) == 2) + return 2; + + if ((sentence->_field2C == 4 && sentence->localWord("am") && sentence->localWord("i")) + || (sentence->localWord("are") && sentence->localWord("we")) + || (sentence->_field2C == 3 && sentence->localWord("room") + && sentence->localWord("we") && sentence->localWord("in")) + || (sentence->_field2C == 3 && sentence->localWord("rom") + && sentence->localWord("is") && sentence->localWord("this")) + ) { + uint id = getRangeValue(getRoomDialogueId(roomScript)); + addResponse(getDialogueId(id ? id : 201384)); + applyResponse(); + return 2; + } + + if (getValue(1) >= 3) { + result = processEntries(&_sentences[1], 0, roomScript, sentence); + } else if (getValue(1) == 2) { + result = processEntries(&_sentences[2], 0, roomScript, sentence); + } else if (getValue(1) == 1) { + result = processEntries(&_sentences[3], 0, roomScript, sentence); + + if (sentence->contains("shrinkbot")) { + addResponse(getDialogueId(200583)); + applyResponse(); + return 2; + } + } + + if (sentence->localWord("television") || sentence->localWord("tv") + || sentence->localWord("crush") || sentence->localWord("crushed")) { + if (roomScript->_scriptId == 111 || getRandomBit()) { + addResponse(getDialogueId(getRandomBit() ? 200912 : 200913)); + } else { + addResponse(getDialogueId(200710)); + addResponse(getDialogueId(201334)); + } + + applyResponse(); + return 2; + } + + if (checkCommonWords(roomScript, sentence)) { + applyResponse(); + setState(0); + return 2; + } + + if (sentence->contains("my") && (sentence->contains("where can i find") + || sentence->contains("where is") + || sentence->contains("wheres") + || sentence->contains("help me find") + || sentence->contains("what have you done with") + || sentence->contains("have you got") + || sentence->contains("id like") + || sentence->contains("i would like") + || sentence->contains("have you seen") + )) { + addResponse(getDialogueId(200799)); + applyResponse(); + return 2; + } + + setupSentences(); + uint tagId = g_vm->_trueTalkManager->_quotes.find(sentence->_normalizedLine); + if (tagId && chooseResponse(roomScript, sentence, tagId) == 2) + return 2; + if (defaultProcess(roomScript, sentence)) + return 2; + if (!processEntries(&_sentences[19], 0, roomScript, sentence)) + return 2; + if (!processEntries(_defaultEntries, 0, roomScript, sentence)) + return 2; + + if (sentence->contains("42")) { + addResponse(getDialogueId(200515)); + applyResponse(); + return 2; + } + + CTrueTalkManager::setFlags(24, val24 + 1); + if (getValue(24) > 3) { + addResponse(getDialogueId(200200)); + applyResponse(); + return 2; + } + + if (sentence->localWord("get")) { + addResponse(getDialogueId(200475)); + applyResponse(); + return 2; + } + + if (getRandomNumber(100) <= 75) { + addResponse(getDialogueId(200060)); + applyResponse(); + return 2; + } + + addResponse(getDialogueId(200140)); + addResponse(getDialogueId(getRandomBit() ? 200192 : 200157)); + addResponse(getDialogueId(200176)); + applyResponse(); + return 2; +} + +ScriptChangedResult BellbotScript::scriptChanged(const TTroomScript *roomScript, uint id) { + if (!roomScript) + return SCR_2; + + switch (id) { + case 104: + addResponse(getDialogueId(200617)); + applyResponse(); + break; + + case 105: + addResponse(getDialogueId(200732)); + applyResponse(); + break; + + case 106: + addResponse(getDialogueId(200733)); + applyResponse(); + break; + + case 107: + addResponse(getDialogueId(200731)); + applyResponse(); + break; + + case 157: + _field2DC = 1; + break; + + case 158: + CTrueTalkManager::setFlags(26, 1); + break; + + case 3: + if (_field2DC) { + if (randomResponse0(roomScript, id)) + return SCR_2; + } else { + addResponse(getDialogueId(201693)); + applyResponse(); + } + + _field2DC = 0; + CTrueTalkManager::_v9 = 0; + // Deliberate fall-through + default: + if (roomScript->_scriptId == 115 && id == 103) { + switch (getValue(4)) { + case 0: + addResponse(getDialogueId(200014)); + applyResponse(); + break; + case 1: + case 2: + addResponse(getDialogueId(200011)); + applyResponse(); + break; + case 3: + addResponse(getDialogueId(200007)); + applyResponse(); + break; + default: + break; + } + } + break; + } + + return SCR_2; +} + +int BellbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + switch (tagId) { + case MKTAG('A', 'D', 'V', 'T'): + case MKTAG('A', 'R', 'T', 'I'): + case MKTAG('A', 'R', 'T', 'Y'): + case MKTAG('B', 'R', 'N', 'D'): + case MKTAG('C', 'O', 'M', 'D'): + case MKTAG('D', 'N', 'C', 'E'): + case MKTAG('H', 'B', 'B', 'Y'): + case MKTAG('L', 'I', 'T', 'R'): + case MKTAG('M', 'A', 'G', 'S'): + case MKTAG('M', 'C', 'P', 'Y'): + case MKTAG('M', 'I', 'N', 'S'): + case MKTAG('M', 'U', 'S', 'I'): + case MKTAG('N', 'I', 'K', 'E'): + case MKTAG('S', 'F', 'S', 'F'): + case MKTAG('S', 'O', 'A', 'P'): + case MKTAG('S', 'O', 'N', 'G'): + case MKTAG('S', 'P', 'R', 'T'): + case MKTAG('T', 'E', 'A', 'M'): + case MKTAG('T', 'V', 'S', 'H'): + case MKTAG('W', 'W', 'E', 'B'): + tagId = MKTAG('E', 'N', 'T', 'N'); + break; + case MKTAG('A', 'C', 'T', 'R'): + case MKTAG('A', 'C', 'T', 'S'): + case MKTAG('A', 'U', 'T', 'H'): + case MKTAG('B', 'A', 'R', 'K'): + case MKTAG('B', 'A', 'R', 'U'): + case MKTAG('B', 'L', 'F', '1'): + case MKTAG('B', 'L', 'F', '2'): + case MKTAG('B', 'L', 'P', '1'): + case MKTAG('B', 'L', 'P', '2'): + case MKTAG('B', 'L', 'P', '3'): + case MKTAG('B', 'L', 'P', '4'): + case MKTAG('B', 'L', 'R', '1'): + case MKTAG('B', 'L', 'R', '2'): + case MKTAG('B', 'L', 'T', '1'): + case MKTAG('B', 'L', 'T', '2'): + case MKTAG('B', 'L', 'T', '3'): + case MKTAG('B', 'L', 'T', '4'): + case MKTAG('B', 'L', 'T', '5'): + case MKTAG('C', 'O', 'P', 'S'): + case MKTAG('D', 'C', 'T', 'R'): + case MKTAG('F', 'A', 'S', 'H'): + case MKTAG('F', 'A', 'M', 'E'): + case MKTAG('H', 'E', 'R', 'D'): + case MKTAG('H', 'O', 'S', 'T'): + case MKTAG('K', 'N', 'O', 'B'): + case MKTAG('N', 'H', 'R', 'O'): + case MKTAG('R', 'A', 'C', 'E'): + case MKTAG('S', 'C', 'I', 'T'): + case MKTAG('T', 'O', 'U', 'P'): + case MKTAG('T', 'W', 'A', 'T'): + case MKTAG('W', 'E', 'A', 'T'): + tagId = MKTAG('P', 'R', 'S', 'N'); + break; + case MKTAG('C', 'H', 'S', 'E'): + case MKTAG('C', 'M', 'N', 't'): + case MKTAG('F', 'I', 'L', 'M'): + case MKTAG('J', 'F', 'O', 'D'): + case MKTAG('L', 'I', 'Q', 'D'): + tagId = MKTAG('F', 'O', 'O', 'D'); + break; + case MKTAG('C', 'R', 'I', 'M'): + case MKTAG('C', 'S', 'P', 'Y'): + case MKTAG('D', 'R', 'U', 'G'): + tagId = MKTAG('V', 'B', 'A', 'D'); + break; + case MKTAG('E', 'A', 'R', 'T'): + case MKTAG('H', 'O', 'M', 'E'): + case MKTAG('N', 'P', 'L', 'C'): + case MKTAG('P', 'L', 'A', 'N'): + tagId = MKTAG('P', 'L', 'A', 'C'); + break; + case MKTAG('F', 'A', 'U', 'N'): + case MKTAG('F', 'I', 'S', 'H'): + case MKTAG('F', 'L', 'O', 'R'): + tagId = MKTAG('N', 'A', 'T', 'R'); + break; + case MKTAG('H', 'H', 'L', 'D'): + case MKTAG('T', 'O', 'Y', 'S'): + case MKTAG('W', 'E', 'A', 'P'): + tagId = MKTAG('M', 'A', 'C', 'H'); + break; + case MKTAG('M', 'L', 'T', 'Y'): + case MKTAG('P', 'G', 'R', 'P'): + case MKTAG('P', 'T', 'I', 'C'): + tagId = MKTAG('G', 'R', 'U', 'P'); + break; + case MKTAG('P', 'K', 'U', 'P'): + case MKTAG('S', 'E', 'X', '1'): + case MKTAG('S', 'W', 'E', 'R'): + tagId = MKTAG('R', 'U', 'D', 'E'); + break; + case MKTAG('P', 'H', 'I', 'L'): + case MKTAG('R', 'C', 'K', 'T'): + tagId = MKTAG('S', 'C', 'I', 'E'); + break; + case MKTAG('T', 'R', 'A', '2'): + case MKTAG('T', 'R', 'A', '3'): + tagId = MKTAG('T', 'R', 'A', 'V'); + break; + default: + break; + } + + return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); +} + +int BellbotScript::updateState(uint oldId, uint newId, int index) { + if (!getValue(25)) { + newId = 202043 - getValue(1) <= 2 ? 994 : 0; + CTrueTalkManager::setFlags(25, 1); + } + + if (oldId == _oldId && _rangeResetCtr >= 3) { + TTscriptRange *range = findRange(oldId); + if (range) + range->_priorIndex = 0; + + _rangeResetCtr = 0; + return getRangeValue(200370); + } + + if (oldId != _oldId) { + _oldId = oldId; + _rangeResetCtr = 0; + } + + if (oldId >= 201709 && oldId <= 201754) { + addResponse(getDialogueId(201705)); + addResponse(getDialogueId(201706)); + newId = getRangeValue(201707); + } + + if (newId == 202276) + newId = addLocation(); + if (newId == 202275) + newId = getStateDialogueId(); + + if (getValue(1) >= 2) { + if (newId == 200840 || newId == 200845 || newId == 200846 || newId == 200851) { + if (getValue(1) == 2) { + newId = 202047; + } else { + newId = getRangeValue(202848); + } + } + } + + if (getValue(1) >= 3) { + if (newId == 200841 || newId == 200842 || newId == 200843 || + newId == 200847 || newId == 200848 || newId == 200854) { + newId = getRangeValue(202038); + } + } + + if (newId == 200264 && getValue(1) == 1) + newId = 200267; + if (newId == 202231 && getValue(1) == 1) + newId = 200848; + + int v4 = getValue(4); + if (newId == 200187 && v4) { + return 200188; + } else if (newId == 200188 && !v4) { + return 200187; + } else if (newId == 200014 && (v4 == 1 || v4 == 2)) { + return 200011; + } else if (newId == 200011 && !v4) { + return 200014; + } + + if (oldId == 200612) { + CTrueTalkManager::setFlags(25, 2); + CTrueTalkManager::setFlags(5, 1); + } + + if (newId == 200423 || newId == 200424 || newId == 200425) { + if (getValue(5)) { + CTrueTalkManager::triggerAction(16, 0); + } else { + newId = 200611; + } + } + + if (oldId == 200261 && getRandomNumber(10) == 1) { + if (getValue(1) >= 3) + newId = getRangeValue(200283); + else if (getValue(1) == 2) + newId = getRangeValue(200279); + } + + if (oldId == 200962) { + if (getValue(1) == 2) + return 200963; + if (getValue(1) == 1) + return 200964; + } + if (oldId == 200989 && getValue(1) <= 2) + return 200990; + + if (oldId == 201760) { + CGameManager *gameManager = g_vm->_trueTalkManager->getGameManager(); + CPetControl *pet = getPetControl(gameManager); + + if (pet) { + bool canSummon = pet->canSummonBot("DoorBot"); + if (canSummon) { + CTrueTalkManager::_v9 = 101; + CTrueTalkManager::triggerAction(5, 0); + } else { + newId = 201857; + } + } + } + + setValue23(newId); + return newId; +} + +int BellbotScript::preResponse(uint id) { + int newId = _preResponses.find(id); + + if (newId == 202277) { + applyResponse(); + CTrueTalkManager::triggerAction(1, 0); + } + if (newId == 200769) { + applyResponse(); + CTrueTalkManager::triggerAction(18, 0); + } + + if (id == 21790) + CTrueTalkManager::triggerAction(13, 0); + + return newId; +} + +int BellbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + switch (val1) { + case 1: + addResponse(getDialogueId(*srcIdP)); + applyResponse(); + return 2; + + case 2: + addResponse(getDialogueId(*srcIdP)); + addResponse(getDialogueId(getRandomNumber(2) == 1 ? 200192 : 200157)); + addResponse(getDialogueId(200176)); + applyResponse(); + return 2; + + case 21: + if (CTrueTalkManager::getStateValue(7) == 0) { + selectResponse(21372); + applyResponse(); + return 2; + } + + if (!sentence->localWord("broken") && !sentence->contains("broken") && + CTrueTalkManager::_currentNPC) { + CNodeItem *node = CTrueTalkManager::_currentNPC->getNode(); + if (node) { + CString nodeName = node->getName(); + if (nodeName == "5" || nodeName == "6" || nodeName == "7") { + CTrueTalkManager::triggerAction(29, 2); + selectResponse(201571); + applyResponse(); + return 2; + } + } + } + + CTrueTalkManager::triggerAction(29, 1); + selectResponse(201771); + applyResponse(); + return 2; + + case 22: + if (CTrueTalkManager::getStateValue(7) == 0) { + selectResponse(21372); + applyResponse(); + return 2; + } + + if (!sentence->localWord("broken") && !sentence->contains("broken") && + CTrueTalkManager::_currentNPC) { + CNodeItem *node = CTrueTalkManager::_currentNPC->getNode(); + if (node) { + CString nodeName = node->getName(); + if (nodeName == "5" || nodeName == "6" || nodeName != "7") { + CTrueTalkManager::triggerAction(29, 2); + selectResponse(201571); + applyResponse(); + return 2; + } + } + } + + CTrueTalkManager::triggerAction(29, 1); + selectResponse(201771); + applyResponse(); + return 2; + + case 23: + case 24: + if (CTrueTalkManager::getStateValue(7) == 0) { + selectResponse(21372); + applyResponse(); + return 2; + } + + CTrueTalkManager::triggerAction(29, val1 == 23 ? 3 : 4); + break; + + default: + break; + } + + return 0; +} + +bool BellbotScript::randomResponse(uint index) { + if (getRandomNumber(100) > 10 || getRandomNumber(10) <= index) + return 0; + + if (getRandomNumber(100) > 95) { + deleteResponses(); + addResponse(getDialogueId(201695)); + applyResponse(); + } else { + setResponseFromArray(index, 201696); + } + + return true; +} + +int BellbotScript::addLocation() { + addResponse(getDialogueId(202228)); + int roomNum, floorNum, elevatorNum; + getAssignedRoom(&roomNum, &floorNum, &elevatorNum); + + addResponse(getDialogueId(202071 + roomNum)); + addResponse(getDialogueId(201933 + floorNum)); + addResponse(getDialogueId(201916 + elevatorNum)); + + return 200858; +} + +int BellbotScript::getStateDialogueId() const { + switch (getValue(1)) { + case 1: + return 201253; + case 2: + return 200282; + default: + return 201246; + } +} + +void BellbotScript::setValue23(uint id) { + uint val = 0; + for (uint idx = 0; idx < _states.size() && !val; ++idx) { + TTmapEntry &us = _states[idx]; + if (us._src == id) + val = us._dest; + } + + CTrueTalkManager::setFlags(23, val); +} + +int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence *sentence) { + if (!roomScript || !sentence) + return true; + + bool applyFlag = false, stateFlag = true; + switch (getValue(23)) { + case 1: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200818)); + applyFlag = true; + } + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200817)); + applyFlag = true; + } + break; + + case 2: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200835)); + addResponse(getDialogueId(200830)); + applyFlag = true; + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(200834)); + addResponse(getDialogueId(200830)); + applyFlag = true; + } + break; + + case 3: + if (sentence->_field2C >= 11 && sentence->_field2C <= 13) { + addResponse(getDialogueId(200831)); + addResponse(getDialogueId(200833)); + applyFlag = true; + } + break; + + case 4: + if (sentence->_field2C == 11) { + addResponse(getDialogueId(200872)); + applyFlag = true; + } + if (sentence->_field2C == 12 || sentence->_field2C == 13) { + addResponse(getDialogueId(200873)); + applyFlag = true; + } + break; + + case 5: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200492)); + applyFlag = true; + } + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200491)); + applyFlag = true; + } + break; + + case 6: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200496)); + applyResponse(); + setState(0); + CTrueTalkManager::setFlags(23, 7); + return 2; + } + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200127)); + applyFlag = true; + } + break; + + case 7: + addResponse(getDialogueId(200504)); + addResponse(getDialogueId(200496)); + applyFlag = true; + stateFlag = false; + break; + + case 8: + addResponse(getDialogueId(200494)); + applyFlag = true; + stateFlag = false; + break; + + case 9: + addResponse(getDialogueId(sentence->localWord("guess") ? 200495 : 200493)); + applyFlag = true; + break; + + case 10: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200317)); + applyResponse(); + setState(0); + CTrueTalkManager::setFlags(23, 11); + return 2; + } + + addResponse(getDialogueId(sentence->_field2C == 12 ? 200316 : 200315)); + applyFlag = true; + break; + + case 11: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200055)); + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(200318)); + } else { + addResponse(getDialogueId(200315)); + } + + applyFlag = true; + break; + + case 12: + if (sentence->_field2C == 6) { + addResponse(getDialogueId(200259)); + applyFlag = true; + } + break; + + case 13: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200207)); + applyFlag = true; + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(200206)); + applyFlag = true; + } + break; + + case 14: + if (sentence->_field2C == 6) { + addResponse(getDialogueId(200349)); + applyFlag = true; + } + + case 15: + if (sentence->_field2C == 6) { + addResponse(getDialogueId(200130)); + applyResponse(); + setState(0); + CTrueTalkManager::setFlags(23, 16); + return 2; + } + break; + + case 16: + if (sentence->localWord("invented")) { + addResponse(getDialogueId(200131)); + applyFlag = true; + } + break; + + case 17: + if ((sentence->_field2C == 3 && sentence->localWord("code")) + || (sentence->localWord("which") && sentence->localWord("is")) + || sentence->localWord("remember") + || sentence->localWord("know") + ) { + addResponse(getDialogueId(200044)); + applyFlag = true; + stateFlag = false; + } + break; + + case 19: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200223)); + applyFlag = true; + } + break; + + case 20: + addResponse(getDialogueId(200254)); + applyFlag = true; + break; + + case 21: + if (sentence->contains("hiker") || sentence->contains("hug")) { + addResponse(getDialogueId(200379)); + applyFlag = true; + } + break; + + case 22: + if (sentence->localWord("get") || sentence->localWord("it")) { + addResponse(getDialogueId(200474)); + applyFlag = true; + } + break; + + case 23: + addResponse(getDialogueId(sentence->localWord("long") ? 200870 : 200871)); + applyFlag = true; + break; + + case 24: + addResponse(getDialogueId(200793)); + applyFlag = true; + stateFlag = false; + break; + + case 25: + if (sentence->localWord("parrot")) { + addResponse(getDialogueId(200255)); + applyFlag = true; + stateFlag = false; + } + break; + + case 26: + if (sentence->localWord("cage")) { + addResponse(getDialogueId(200380)); + applyFlag = true; + stateFlag = false; + } + break; + + case 27: + addResponse(getDialogueId(200347)); + applyFlag = true; + stateFlag = false; + break; + + case 28: + if (sentence->localWord("perch")) { + addResponse(getDialogueId(200242)); + applyFlag = true; + stateFlag = false; + } + break; + + case 29: + if (sentence->localWord("brain") || sentence->localWord("titania")) { + addResponse(getDialogueId(200392)); + applyFlag = true; + stateFlag = false; + } + break; + + case 30: + if ((sentence->localWord("did") || sentence->localWord("not")) + || (sentence->localWord("would") || sentence->localWord("not")) + || (sentence->localWord("could") || sentence->localWord("not")) + || sentence->localWord("tried")) { + addResponse(getDialogueId(200416)); + applyFlag = true; + } + break; + + case 31: + addResponse(getDialogueId(sentence->_field2C == 11 ? 200810 : 200811)); + applyFlag = true; + break; + + case 32: + addResponse(getDialogueId(sentence->_field2C == 11 ? 200810 : 200812)); + applyFlag = true; + break; + + case 33: + addResponse(getDialogueId(200822)); + applyFlag = true; + break; + + case 34: + addResponse(getDialogueId(200824)); + applyFlag = true; + break; + + case 35: + if (sentence->_field2C == 3 && sentence->localWord("it") + && (sentence->localWord("for") || sentence->localWord("do"))) { + addResponse(getDialogueId(200768)); + applyFlag = true; + } + break; + + case 36: + if (sentence->_field2C == 11) { + CTrueTalkManager::triggerAction(14, 0); + addResponse(getDialogueId(200761)); + applyFlag = true; + } + break; + + case 37: + addResponse(getDialogueId(200630)); + applyFlag = true; + break; + + case 38: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200631)); + applyFlag = true; + } + break; + + case 39: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200632)); + stateFlag = false; + } else { + addResponse(getDialogueId(200633)); + } + applyFlag = true; + break; + + case 40: + addResponse(getDialogueId(200633)); + applyFlag = true; + break; + + case 41: + addResponse(getDialogueId(sentence->contains("42") ? 200139 : 200627)); + applyFlag = true; + break; + + case 42: + if ((sentence->localWord("carry") && sentence->localWord("on")) + || (sentence->localWord("go") && sentence->localWord("on")) + || sentence->localWord("more") + || sentence->localWord("going") + || sentence->localWord("elaborate") + || sentence->localWord("suspicious") + || sentence->localWord("they")) { + addResponse(getDialogueId(200642)); + applyFlag = true; + stateFlag = false; + } + break; + + case 43: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200643)); + applyFlag = true; + } + break; + + case 44: +case44: + if (better(sentence, 200615, 200613)) { + applyFlag = true; + stateFlag = false; + } + break; + + case 45: + if (sentence->contains("surprise")) { + addResponse(getDialogueId(200614)); + applyFlag = true; + stateFlag = false; + break; + } + goto case44; + + case 46: + if (sentence->contains("good")) { + addResponse(getDialogueId(200616)); + applyFlag = true; + stateFlag = false; + break; + } + goto case44; + + case 47: + if (sentence->_field2C == 12) + addResponse(getDialogueId(200368)); + addResponse(getDialogueId(200366)); + applyFlag = true; + stateFlag = false; + break; + + case 48: + if ((sentence->localWord("carry") && sentence->localWord("on")) + || sentence->localWord("more") + || (sentence->localWord("go") && sentence->localWord("on")) + || sentence->localWord("going") + || sentence->localWord("yes") + || sentence->localWord("really")) { + addResponse(getDialogueId(200367)); + applyFlag = true; + } + break; + + case 49: + if (sentence->_field2C >= 11 && sentence->_field2C <= 13) { + addResponse(getDialogueId(200407)); + applyFlag = true; + stateFlag = false; + } + break; + + case 50: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200408)); + stateFlag = false; + } else { + addResponse(getDialogueId(200409)); + } + applyFlag = true; + break; + + case 51: + if (sentence->localWord("no") || sentence->localWord("it") + || sentence->localWord("is") || sentence->localWord("not") + || sentence->contains("yeah right")) { + addResponse(getDialogueId(200636)); + applyFlag = true; + } + break; + + case 52: + if (sentence->_field2C >= 11 && sentence->_field2C <= 13) { + addResponse(getDialogueId(200872)); + applyFlag = true; + } + break; + + case 53: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200525)); + applyFlag = true; + } else if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200526)); + applyFlag = true; + } + break; + + case 54: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200095)); + applyFlag = true; + stateFlag = false; + } + break; + + case 55: + if (sentence->_field2C == 6) { + addResponse(getDialogueId(200112)); + applyFlag = true; + } + break; + + case 56: + if (sentence->localWord("sure") + || (sentence->localWord("nothing") && sentence->localWord("else"))) { + addResponse(getDialogueId(200649)); + applyFlag = true; + stateFlag = false; + } + break; + + case 57: + if (sentence->localWord("bad") + || (sentence->localWord("not") && sentence->localWord("good"))) { + addResponse(getDialogueId(200654)); + } else { + addResponse(getDialogueId(200655)); + stateFlag = false; + } + applyFlag = true; + break; + + case 58: + if (sentence->localWord("more") + || (sentence->localWord("go") && sentence->localWord("on")) + || (sentence->_field2C == 11 && sentence->localWord("really"))) { + addResponse(getDialogueId(200650)); + applyFlag = true; + stateFlag = false; + } + break; + + case 59: + if (!sentence->localWord("shutup")) { + addResponse(getDialogueId(200651)); + applyFlag = true; + stateFlag = false; + } + break; + + case 60: + if (sentence->_field2C == 3 && sentence->localWord("they") && sentence->localWord("do")) { + addResponse(getDialogueId(200652)); + applyFlag = true; +stateFlag = false; + } + break; + + case 61: + if ((sentence->localWord("that") && sentence->localWord("all")) + || (sentence->localWord("anything") && sentence->localWord("else"))) { + addResponse(getDialogueId(200653)); + applyFlag = true; + } + break; + + case 62: + if (sentence->localWord("meant") || sentence->localWord("woman")) { + addResponse(getDialogueId(200743)); + applyFlag = true; + } + break; + + case 63: + addResponse(getDialogueId(200208)); + applyFlag = true; + break; + + case 64: + if (sentence->localWord("rowboat")) { + addResponse(getDialogueId(200052)); + applyFlag = true; + } + break; + + case 65: + if (sentence->localWord("sorry")) { + addResponse(getDialogueId(200056)); + applyFlag = true; + stateFlag = false; + } + break; + + case 66: + if (sentence->localWord("sorry")) { + addResponse(getDialogueId(200057)); + applyFlag = true; + stateFlag = false; + } + break; + + case 67: + if (sentence->localWord("sorry")) { + addResponse(getDialogueId(200055)); + applyFlag = true; + stateFlag = false; + } + break; + + case 68: + if ((sentence->localWord("i") && sentence->localWord("care")) + || sentence->localWord("do") + || sentence->localWord("me")) { + addResponse(getDialogueId(201006)); + applyFlag = true; + } + break; + + case 69: + if ((sentence->localWord("what") && sentence->localWord("happen")) + || sentence->localWord("filigon")) { + addResponse(getDialogueId(201011)); + applyFlag = true; + stateFlag = false; + } + break; + + case 70: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(201012)); + applyFlag = true; + stateFlag = false; + } + break; + + case 71: + if (sentence->localWord("why")) { + addResponse(getDialogueId(201013)); + applyFlag = true; + } + break; + + case 72: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(200921)); + applyFlag = true; + } else if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(200920)); + applyFlag = true; + } + break; + + case 73: + if (sentence->localWord("mood") && (charId() == 7 || charId() == 5)) { + addResponse(getDialogueId(201021)); + applyFlag = true; + stateFlag = false; + } + break; + + case 74: + if (sentence->_field2C == 6) { + addResponse(getDialogueId(201022)); + applyFlag = true; + stateFlag = false; + } + break; + + case 75: + if (sentence->_field2C == 3) { + if (sentence->localWord("that") || sentence->localWord("worb")) { + addResponse(getDialogueId(201802)); + applyFlag = true; + } + } + break; + + case 76: + if (sentence->_field2C == 2 && (sentence->localWord("that") || sentence->localWord("gat"))) { + addResponse(getDialogueId(201034)); + applyFlag = true; + stateFlag = false; + } + break; + + case 77: + if (sentence->_field2C == 4 || sentence->_field2C == 3) { + if (sentence->localWord("that") || sentence->localWord("blerontis")) { + addResponse(getDialogueId(201035)); + applyFlag = true; + } + } + break; + + case 78: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(201034)); + applyFlag = true; + stateFlag = false; + } else if (sentence->_field2C == 11) { + addResponse(getDialogueId(201040)); + applyFlag = true; + } else if ((sentence->localWord("not") && sentence->localWord("remember")) + || sentence->localWord("forgot")) { + addResponse(getDialogueId(201041)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why")) { + addResponse(getDialogueId(201042)); + applyFlag = true; + stateFlag = false; + } + break; + + case 79: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(201052)); + CTrueTalkManager::triggerAction(14, 0); + applyFlag = true; + stateFlag = false; + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(202119)); + addResponse(getDialogueId(200256)); + applyFlag = true; + } + break; + + case 80: + if ((!sentence->localWord("what") && sentence->localWord("how")) + || sentence->localWord("about") + || sentence->localWord("you")) { + if (sentence->_field2C != 3 && sentence->_field2C != 4 && sentence->_field2C != 7) { + addResponse(getDialogueId(201694)); + applyFlag = true; + stateFlag = false; + } + } else { + addResponse(getDialogueId(201135)); + applyFlag = true; + } + break; + + case 81: + if ((!sentence->localWord("what") && !sentence->localWord("how")) + || !sentence->localWord("about") + || !sentence->localWord("you")) { + if (!sentence->localWord("and") || !sentence->localWord("yourself")) + break; + } + addResponse(getDialogueId(201135)); + applyFlag = true; + break; + + case 82: + if ((sentence->_field2C == 3 && sentence->localWord("mean")) + || sentence->localWord("surf") + || (sentence->localWord("what") && sentence->localWord("talk") + && sentence->localWord("about"))) { + addResponse(getDialogueId(201694)); + applyFlag = true; + stateFlag = false; + } + break; + + case 83: + if (sentence->_field2C != 3 && sentence->_field2C != 4 && sentence->_field2C != 7) { + addResponse(getDialogueId(201083)); + applyFlag = true; + } + break; + + case 84: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(202119)); + + switch (getValue(1)) { + case 1: + addResponse(getDialogueId(202024)); + applyFlag = true; + break; + case 2: + addResponse(getDialogueId(201812)); + applyFlag = true; + stateFlag = false; + break; + default: + break; + } + } else if (sentence->_field2C == 11) { + addResponse(getDialogueId(201060)); + addResponse(getDialogueId(201079)); + applyFlag = true; + stateFlag = false; + } + break; + + case 85: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(201814)); + applyFlag = true; + } + if (sentence->_field2C == 12) { + addResponse(getDialogueId(201813)); + applyFlag = true; + } + break; + + case 86: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(202109)); + applyFlag = true; + } + if (sentence->_field2C == 12) { + addResponse(getDialogueId(202108)); + applyFlag = true; + } + break; + + case 87: + if (better(sentence, 201993, 200720)) { + applyFlag = true; + } + break; + + case 88: + if (sentence->_field2C == 6 || sentence->contains("upside down")) { + addResponse(getDialogueId(202142)); + applyFlag = true; + } + break; + + case 89: + if (sentence->_field2C == 2) { + addResponse(getDialogueId(200739)); + applyFlag = true; + stateFlag = false; + } + break; + + case 90: + if (sentence->contains("like") && (sentence->contains("slug") || sentence->contains("snail"))) { + addResponse(getDialogueId(201029)); + applyFlag = true; + stateFlag = false; + } else if (sentence->contains("slime") || sentence->localWord("what")) { + addResponse(getDialogueId(201220)); + applyFlag = true; + stateFlag = false; + } + + default: + break; + } + + if (applyFlag) + applyResponse(); + if (stateFlag) { + setState(0); + CTrueTalkManager::setFlags(23, 0); + } + + return applyFlag ? 2 : 1; +} + +bool BellbotScript::better(const TTsentence *sentence, uint id1, uint id2) { + if (sentence->contains("good") || sentence->localWord("better")) { + addResponse(getDialogueId(id1)); + } else if (sentence->localWord("bad")) { + addResponse(getDialogueId(id2)); + } else { + return false; + } + + return true; +} + +bool BellbotScript::randomResponse0(const TTroomScript *roomScript, uint id) { + bool dr0 = getDialRegion(0) == 1; + uint newId = getValue(1); + + if (getValue(25) == 0) { + CTrueTalkManager::setFlags(25, 1); + if (getValue(1) > 2) { + addResponse(getDialogueId(202043)); + applyResponse(); + return true; + } + } + + bool result = dr0 ? randomResponse1(roomScript, newId) : + randomResponse2(roomScript, newId); + if (result) + CTrueTalkManager::triggerAction(1, 0); + + return true; +} + +bool BellbotScript::randomResponse1(const TTroomScript *roomScript, uint id) { + if (getRandomNumber(100) < 10) { + addResponse(getDialogueId(201978)); + applyResponse(); + } else { + if (getRandomNumber(100) < 50) + addResponse(getDialogueId(202259)); + + randomResponse3(roomScript, id); + applyResponse(); + } + + return false; +} + +bool BellbotScript::randomResponse2(const TTroomScript *roomScript, uint id) { + if (getRandomNumber(100) < 5) { + addResponse(getDialogueId(202262)); + applyResponse(); + } else { + if (getRandomNumber(100) < 40) + addResponse(getDialogueId(202258)); + + randomResponse4(roomScript, id); + applyResponse(); + } + + return false; +} + +void BellbotScript::randomResponse3(const TTroomScript *roomScript, uint id) { + bool result = false; + if (roomScript && getRandomNumber(100) < 50) + result = addRoomDescription(roomScript); + + if (result) + return; + if (getRandomNumber(100) >= 50) { + addResponse(getDialogueId(202262)); + return; + } + + if (id <= 2) { + if (getRandomNumber(100) < 50) { + addResponse(getDialogueId(202266)); + return; + } else if (id == 2) { + addResponse(getDialogueId(202264)); + return; + } + } + + addResponse(getDialogueId(id == 1 ? 202265 : 202263)); +} + +void BellbotScript::randomResponse4(const TTroomScript *roomScript, uint id) { + if (getRandomNumber(100) < 4 && id <= 2) { + addResponse(getDialogueId(202268)); + } else { + addResponse(getDialogueId(202267)); + } +} + +int BellbotScript::checkCommonSentences(const TTroomScript *roomScript, const TTsentence *sentence) { + if (!roomScript || !sentence) + return 1; + + uint val1 = getValue(1); + for (uint idx = 0; idx < _phrases.size(); ++idx) { + TTcommonPhrase &cp = _phrases[idx]; + + if (cp._roomNum != 0 && cp._roomNum != roomScript->_scriptId) + continue; + if (cp._val1 != 0 && cp._val1 != val1 && (cp._val1 == 3 || val1 != 4)) + continue; + if (!sentence->contains(cp._str.c_str())) + continue; + + addResponse(getDialogueId(cp._dialogueId)); + applyResponse(); + return 2; + } + + return 0; +} + +bool BellbotScript::checkCommonWords(const TTroomScript *roomScript, const TTsentence *sentence) { + if (!roomScript || !sentence) + return 0; + CTrueTalkManager::setFlags(23, 0); + if (sentence->_field2C != 4) + return 0; + + if (sentence->localWord("garage")) { + addResponse(getDialogueId(200874)); + } else if (sentence->localWord("parrotfoodshop")) { + addResponse(getDialogueId(200821)); + } else if (sentence->localWord("sgt") && sentence->localWord("restaurant")) { + addResponse(getDialogueId(200857)); + } else if (sentence->localWord("firstclass") && sentence->localWord("restaurant")) { + addResponse(getDialogueId(200839)); + } else if (sentence->localWord("restaurant")) { + addResponse(getDialogueId(getValue(1) == 1 ? 200839 : 200857)); + } else if (getValue(1) == 1 && sentence->localWord("canal") && sentence->localWord("firstclass")) { + addResponse(getDialogueId(200846)); + } else if (getValue(1) == 2 && sentence->localWord("canal") && sentence->localWord("secondclass")) { + addResponse(getDialogueId(200847)); + } else if (sentence->localWord("canal")) { + addResponse(getDialogueId(getValue(1) == 1 ? 200846 : 200847)); + } else if (sentence->localWord("firstclass") && + (sentence->localWord("stateroom") || sentence->localWord("room"))) { + addResponse(getDialogueId(getValue(1) == 1 ? 200840 : 200306)); + } else if (sentence->localWord("secondclass") && sentence->localWord("stateroom") && sentence->localWord("room")) { + addResponse(getDialogueId(getValue(1) < 3 ? 202231 : 200306)); + } else if (sentence->localWord("stateroom") || sentence->contains("my room")) { + addResponse(getDialogueId(202231)); + } else if (sentence->localWord("firstclass")) { + addResponse(getDialogueId(200840)); + } else if (sentence->localWord("secondclass")) { + addResponse(getDialogueId(200841)); + } else if (sentence->localWord("thirdclass")) { + addResponse(getDialogueId(202231)); + } else if (sentence->localWord("arboretum")) { + addResponse(getDialogueId(200842)); + } else if (sentence->localWord("bar")) { + addResponse(getDialogueId(200843)); + } else if (sentence->localWord("bottomofwell")) { + addResponse(getDialogueId(200860)); + } else if (sentence->localWord("topwell") || sentence->localWord("well")) { + addResponse(getDialogueId(200861)); + } else if (sentence->localWord("bridge")) { + addResponse(getDialogueId(202213)); + } else if (sentence->localWord("creatorroom")) { + addResponse(getDialogueId(200848)); + } else if (sentence->localWord("servicelift")) { + addResponse(getDialogueId(200855)); + } else if (sentence->localWord("lift")) { + addResponse(getDialogueId(202256)); + } else if (sentence->localWord("bilgeroom")) { + addResponse(getDialogueId(202255)); + } else if (sentence->localWord("musicroom")) { + addResponse(getDialogueId(200851)); + } else if (sentence->localWord("parrotlobby")) { + addResponse(getDialogueId(200852)); + } else if (sentence->localWord("parrot") && + (sentence->localWord("room") || sentence->localWord("lobby"))) { + addResponse(getDialogueId(200852)); + } else if (sentence->localWord("promenade")) { + addResponse(getDialogueId(200853)); + } else if (sentence->localWord("sculpture") || sentence->localWord("sculptureroom") + || sentence->localWord("statue")) { + addResponse(getDialogueId(200854)); + } else if (sentence->localWord("lounge")) { + addResponse(getDialogueId(200856)); + } else if (sentence->localWord("titania")) { + if (sentence->localWord("room")) { + addResponse(getDialogueId(200859)); + } else if (sentence->localWord("nose")) { + addResponse(getDialogueId(200703)); + } else if (sentence->localWord("mouth")) { + addResponse(getDialogueId(200702)); + } else if (sentence->localWord("eyes")) { + addResponse(getDialogueId(200701)); + } else if (sentence->localWord("ear")) { + addResponse(getDialogueId(200698)); + } else if (sentence->localWord("brain")) { + addResponse(getDialogueId(200693)); + } else { + addResponse(getDialogueId(200686)); + } + } else if (sentence->localWord("embarklobby") + || sentence->localWord("lobby")) { + addResponse(getDialogueId(200850)); + } else if (sentence->localWord("pellerator")) { + addResponse(getDialogueId(200862)); + } else if (sentence->localWord("servicelift") + || (sentence->localWord("service") && sentence->localWord("elevator"))) { + addResponse(getDialogueId(200855)); + } else if (sentence->localWord("elevator")) { + addResponse(getDialogueId(202256)); + } else if (sentence->localWord("now")) { + addResponse(getDialogueId(200788)); + } else if (sentence->localWord("room")) { + addResponse(getDialogueId(200311)); + } else { + return false; + } + + return true; +} + +uint BellbotScript::getRoomDialogueId(const TTroomScript *roomScript) { + if (!roomScript) + return 0; + + for (int idx = 0; ROOM_DIALOGUE_IDS[idx]._roomNum; ++idx) { + if (ROOM_DIALOGUE_IDS[idx]._roomNum == roomScript->_scriptId) + return ROOM_DIALOGUE_IDS[idx]._dialogueId; + } + + return 0; +} + +bool BellbotScript::addRoomDescription(const TTroomScript *roomScript) { + if (!roomScript) + return false; + + switch (roomScript->_scriptId) { + case 101: + addResponse(getDialogueId(getValue(2) == 1 ? 20185 : 201832)); + break; + case 107: + if (_room107First) { + addResponse(getDialogueId(202162)); + } else { + addResponse(getDialogueId(202162)); + _room107First = true; + } + break; + case 108: + addResponse(getDialogueId(201844)); + break; + case 109: + addResponse(getDialogueId(200303)); + break; + case 110: + addResponse(getDialogueId(202257)); + break; + case 111: + addResponse(getDialogueId(202056)); + break; + case 112: + addResponse(getDialogueId(201828)); + break; + case 113: + addResponse(getDialogueId(201859)); + break; + case 114: + addResponse(getDialogueId(202052)); + break; + case 115: + addResponse(getDialogueId(202004)); + break; + case 116: + addResponse(getDialogueId(202092)); + break; + case 117: + addResponse(getDialogueId(202027)); + break; + case 124: + addResponse(getDialogueId(202110)); + break; + case 125: + addResponse(getDialogueId(202103)); + break; + case 126: + addResponse(getDialogueId(202116)); + break; + case 127: + addResponse(getDialogueId(202111)); + break; + case 128: + addResponse(getDialogueId(201815)); + break; + case 129: + addResponse(getDialogueId(201816)); + break; + case 131: + addResponse(getDialogueId(201930)); + break; + case 132: + addResponse(getDialogueId(201924)); + break; + default: + return false; + } + + return true; +} + + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/bellbot_script.h b/engines/titanic/true_talk/bellbot_script.h new file mode 100644 index 0000000000..3080b56902 --- /dev/null +++ b/engines/titanic/true_talk/bellbot_script.h @@ -0,0 +1,129 @@ +/* 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. + * + */ + +#ifndef TITANIC_BELLBOT_SCRIPT_H +#define TITANIC_BELLBOT_SCRIPT_H + +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class BellbotScript : public TTnpcScript { +private: + static uint _oldId; + TTmapEntryArray _states; + TTmapEntryArray _preResponses; + TTsentenceEntries _sentences[20]; + TTcommonPhraseArray _phrases; + int _array[150]; + int _field2D0; + int _field2D4; + int _field2D8; + int _field2DC; + bool _room107First; +private: + /** + * Setup sentence data + */ + void setupSentences(); + + /** + * Add the current location to the response + */ + int addLocation(); + + /** + * Get a dialogue Id based on the state + */ + int getStateDialogueId() const; + + /** + * Sets the state value 25 based on the passed Id + */ + void setValue23(uint id); + + /** + * Does preprocessing for the sentence + */ + int preprocess(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Checks for good, better, or bad in the sentence + */ + bool better(const TTsentence *sentence, uint id1, uint id2); + + bool randomResponse0(const TTroomScript *roomScript, uint id); + bool randomResponse1(const TTroomScript *roomScript, uint id); + bool randomResponse2(const TTroomScript *roomScript, uint id); + void randomResponse3(const TTroomScript *roomScript, uint id); + void randomResponse4(const TTroomScript *roomScript, uint id); + + int checkCommonSentences(const TTroomScript *roomScript, const TTsentence *sentence); + bool checkCommonWords(const TTroomScript *roomScript, const TTsentence *sentence); + + uint getRoomDialogueId(const TTroomScript *roomScript); + + /** + * Adds a description of the room to the conversation response + */ + bool addRoomDescription(const TTroomScript *roomScript); +public: + BellbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Handles a randomzied response + */ + virtual bool randomResponse(uint index); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_BELLBOT_SCRIPT_H */ diff --git a/engines/titanic/true_talk/deskbot_script.cpp b/engines/titanic/true_talk/deskbot_script.cpp new file mode 100644 index 0000000000..f3a997e218 --- /dev/null +++ b/engines/titanic/true_talk/deskbot_script.cpp @@ -0,0 +1,1502 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/deskbot_script.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/titanic.h" + +namespace Titanic { + +int DeskbotScript::_oldId; + +DeskbotScript::DeskbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, -1, -1, -1, 0) { + CTrueTalkManager::setFlags(18, 0); + CTrueTalkManager::setFlags(19, 0); + CTrueTalkManager::setFlags(20, 0); + CTrueTalkManager::setFlags(21, 0); + CTrueTalkManager::setFlags(22, 0); + + setupDials(0, 0, 0); + _data[0] = 100; + if (_currentDialNum == 1) + _currentDialNum = 0; + + loadRanges("Ranges/Deskbot"); + loadResponses("Responses/Deskbot", 4); + setupSentences(); + _tagMappings.load("TagMap/Deskbot"); + _words.load("Words/Deskbot"); + _quotes.load("Quotes/Deskbot"); + _states.load("States/Deskbot"); +} + +void DeskbotScript::setupSentences() { + _mappings.load("Mappings/Deskbot", 4); + _entries.load("Sentences/Deskbot"); + _entries2.load("Sentences/Deskbot/2"); + _entries3.load("Sentences/Deskbot/3"); + _dialValues[0] = _dialValues[1] = 0; + _field68 = 0; + _entryCount = 0; +} + +int DeskbotScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + if (roomScript->_scriptId != 110) + return 2; + + bool flag20 = getValue(20) != 0; + CTrueTalkManager::setFlags(20, 0); + checkItems(nullptr, nullptr); + + if (preprocess(roomScript, sentence) != 1) + return 1; + + CTrueTalkManager::setFlags(17, 0); + setState(0); + updateCurrentDial(false); + + if (getValue(1) == 3) { + if (sentence->localWord("competition") || sentence->contains("competition") + || sentence->localWord("won") || sentence->contains("won") + || sentence->localWord("winning") || sentence->contains("winning") + || sentence->localWord("winner") || sentence->contains("winner") + || sentence->contains("35279") || sentence->contains("3 5 2 7 9") + ) { + addResponse(getDialogueId(41773)); + applyResponse(); + return 2; + } else if (sentence->localWord("magazine") || sentence->contains("magazine")) { + addResponse(getDialogueId(41771)); + applyResponse(); + return 2; + } else if (sentence->localWord("upgrade") || sentence->contains("upgrade")) { + if (CTrueTalkManager::_currentNPC) { + CGameObject *obj; + if (CTrueTalkManager::_currentNPC->find("Magazine", &obj, FIND_PET)) { + addResponse(getDialogueId(41773)); + applyResponse(); + return 2; + } + } + } + } + + if (processEntries(&_entries, _entryCount, roomScript, sentence) != 2 + && processEntries(&_entries2, 0, roomScript, sentence) != 2) { + if (sentence->localWord("sauce") || sentence->localWord("pureed")) { + addResponse(getDialogueId(240398)); + applyResponse(); + } else if (sentence->contains("cherries")) { + addResponse(getDialogueId(240358)); + applyResponse(); + } else if (sentence->contains("42")) { + addResponse(getDialogueId(240453)); + applyResponse(); + } else if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(241778)); + applyResponse(); + } else { + if (sentence->contains("98129812")) + setDialRegion(1, 1); + + if (!defaultProcess(roomScript, sentence) + && processEntries(&_entries3, 0, roomScript, sentence) != 2 + && processEntries(_defaultEntries, 0, roomScript, sentence) != 2) { + if (flag20) + CTrueTalkManager::setFlags(20, 1); + addResponse(getDialogueId(240569)); + applyResponse(); + } + } + } + + return 2; +} + +ScriptChangedResult DeskbotScript::scriptChanged(const TTroomScript *roomScript, uint id) { + switch (id) { + case 3: + case 100: + case 108: + CTrueTalkManager::setFlags(21, getValue(21) + 1); + addResponse(getDialogueId(getValue(22) ? 240577 : 241261)); + applyResponse(); + break; + + case 109: + addResponse(getDialogueId(241627)); + applyResponse(); + break; + + case 140: + if (getValue(1) == 3) + addAssignedRoomDialogue3(); + break; + + case 148: + CTrueTalkManager::setFlags(3, 1); + break; + + case 150: + CTrueTalkManager::setFlags(2, 1); + break; + } + + return SCR_2; +} + +int DeskbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + switch (tagId) { + case MKTAG('A', 'D', 'V', 'T'): + case MKTAG('A', 'R', 'T', 'I'): + case MKTAG('A', 'R', 'T', 'Y'): + case MKTAG('B', 'R', 'N', 'D'): + case MKTAG('C', 'O', 'M', 'D'): + case MKTAG('D', 'N', 'C', 'E'): + case MKTAG('H', 'B', 'B', 'Y'): + case MKTAG('L', 'I', 'T', 'R'): + case MKTAG('M', 'A', 'G', 'S'): + case MKTAG('M', 'C', 'P', 'Y'): + case MKTAG('M', 'I', 'N', 'S'): + case MKTAG('M', 'U', 'S', 'I'): + case MKTAG('N', 'I', 'K', 'E'): + case MKTAG('S', 'F', 'S', 'F'): + case MKTAG('S', 'O', 'A', 'P'): + case MKTAG('S', 'O', 'N', 'G'): + case MKTAG('S', 'P', 'R', 'T'): + case MKTAG('T', 'E', 'A', 'M'): + case MKTAG('T', 'V', 'S', 'H'): + tagId = MKTAG('E', 'N', 'T', 'N'); + break; + case MKTAG('A', 'C', 'T', 'R'): + case MKTAG('A', 'C', 'T', 'S'): + case MKTAG('A', 'U', 'T', 'H'): + case MKTAG('B', 'A', 'R', 'K'): + case MKTAG('B', 'A', 'R', 'U'): + case MKTAG('B', 'L', 'F', '1'): + case MKTAG('B', 'L', 'F', '2'): + case MKTAG('B', 'L', 'P', '1'): + case MKTAG('B', 'L', 'P', '2'): + case MKTAG('B', 'L', 'P', '3'): + case MKTAG('B', 'L', 'P', '4'): + case MKTAG('B', 'L', 'R', '1'): + case MKTAG('B', 'L', 'R', '2'): + case MKTAG('B', 'L', 'T', '1'): + case MKTAG('B', 'L', 'T', '2'): + case MKTAG('B', 'L', 'T', '3'): + case MKTAG('B', 'L', 'T', '4'): + case MKTAG('B', 'L', 'T', '5'): + case MKTAG('B', 'O', 'Y', 'S'): + case MKTAG('C', 'O', 'P', 'S'): + case MKTAG('D', 'C', 'T', 'R'): + case MKTAG('F', 'A', 'M', 'E'): + case MKTAG('F', 'A', 'S', 'H'): + case MKTAG('G', 'I', 'R', 'L'): + case MKTAG('H', 'E', 'R', 'O'): + case MKTAG('H', 'O', 'S', 'T'): + case MKTAG('K', 'N', 'O', 'B'): + case MKTAG('N', 'H', 'R', 'O'): + case MKTAG('R', 'A', 'C', 'E'): + case MKTAG('S', 'C', 'I', 'T'): + case MKTAG('T', 'D', 'V', 'P'): + case MKTAG('T', 'W', 'A', 'T'): + case MKTAG('W', 'E', 'A', 'T'): + case MKTAG('W', 'W', 'E', 'B'): + tagId = MKTAG('P', 'R', 'S', 'N'); + break; + case MKTAG('C', 'H', 'S', 'E'): + case MKTAG('C', 'M', 'N', 'T'): + case MKTAG('F', 'I', 'L', 'M'): + case MKTAG('J', 'F', 'O', 'D'): + case MKTAG('L', 'I', 'Q', 'D'): + tagId = MKTAG('F', 'O', 'O', 'D'); + break; + case MKTAG('C', 'R', 'I', 'M'): + case MKTAG('C', 'S', 'P', 'Y'): + case MKTAG('D', 'R', 'U', 'G'): + tagId = MKTAG('V', 'B', 'A', 'D'); + break; + case MKTAG('E', 'A', 'R', 'T'): + case MKTAG('H', 'O', 'M', 'E'): + case MKTAG('N', 'P', 'L', 'C'): + case MKTAG('P', 'L', 'A', 'N'): + tagId = MKTAG('P', 'L', 'A', 'C'); + break; + case MKTAG('F', 'A', 'U', 'N'): + case MKTAG('F', 'I', 'S', 'H'): + case MKTAG('F', 'L', 'O', 'R'): + tagId = MKTAG('N', 'A', 'T', 'R'); + break; + case MKTAG('H', 'H', 'L', 'D'): + case MKTAG('T', 'O', 'Y', 'S'): + case MKTAG('W', 'E', 'A', 'P'): + tagId = MKTAG('M', 'A', 'C', 'H'); + break; + case MKTAG('M', 'L', 'T', 'Y'): + case MKTAG('P', 'G', 'R', 'P'): + case MKTAG('P', 'T', 'I', 'C'): + tagId = MKTAG('G', 'R', 'U', 'P'); + break; + case MKTAG('P', 'K', 'U', 'P'): + case MKTAG('S', 'E', 'X', '1'): + case MKTAG('S', 'W', 'E', 'R'): + tagId = MKTAG('R', 'U', 'D', 'E'); + break; + case MKTAG('P', 'H', 'I', 'L'): + case MKTAG('R', 'C', 'K', 'T'): + tagId = MKTAG('S', 'C', 'I', 'E'); + break; + case MKTAG('T', 'R', 'A', '2'): + case MKTAG('T', 'R', 'A', '3'): + tagId = MKTAG('T', 'R', 'A', 'V'); + break; + default: + break; + } + + return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); + +} + +int DeskbotScript::updateState(uint oldId, uint newId, int index) { + if (isDial1Medium() || getValue(1) < 4) + CTrueTalkManager::setFlags(22, 1); + + if (newId == 240420 || newId == 240947 || newId == 241261) { + if (getValue(22) && (newId == 240947 || newId == 241261)) + newId = getRangeValue(241184); + } + + if (newId == 240832) + setDialRegion(1, 0); + + if (oldId == 241183) { + if (getValue(1) == 2) + newId = getRangeValue(241182); + else if (getValue(1) == 1) + newId = getRangeValue(241181); + } + + if (newId == 240931 && getValue(1) <= 2) { + newId = 240924; + } else if (newId == 240924 && getValue(1) > 2) { + newId = 240931; + } + + if (newId == 240830 && getValue(1) == 1) { + newId = 240801; + } else if (newId == 240801 && getValue(1) > 1) { + newId = 240830; + } + + if (oldId >= 241217 && oldId <= 241259) { + addResponse(getDialogueId(241202)); + addResponse(getDialogueId(241200)); + newId = getRangeValue(241199); + } + + if (newId == 241354) + newId = addAssignedRoomDialogue2(); + if (newId == 241353) + newId = getStateDialogueId(); + if (newId == 240464 && getValue(1) != 1) + newId = 240462; + + if (newId == 241635 && isDial1Medium()) { + addResponse(getDialogueId(241556)); + newId = getRangeValue(241632); + } + + if (getValue(20) && (oldId == 240569 || oldId == 240576)) + newId = 240460; + if (!getValue(20)) { + if (newId != 240460) + goto exit; + CTrueTalkManager::setFlags(20, 1); + } + if (newId == 240460 && _oldId != 240569) { + CTrueTalkManager::setFlags(20, 0); + newId = 240455; + } + +exit: + _oldId = oldId; + setFlags17(newId, index); + + return newId; +} + +int DeskbotScript::preResponse(uint id) { + int newId = 0; + if (getValue(1) >= 3 && (id == 41176 || id == 41738 || id == 41413 || id == 41740)) + newId = 241601; + + if (id == 42114) + CTrueTalkManager::triggerAction(20, 0); + + return newId; +} + +uint DeskbotScript::getDialsBitset() const { + if (getDialRegion(1)) + return getDialRegion(0) ? 2 : 3; + else + return getDialRegion(0) ? 0 : 1; +} + +int DeskbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + uint id; + + switch (val1) { + case 1: + id = *srcIdP; + if (id == 240431 || id == 240432) { + switch (getValue(1)) { + case 1: + id = 240336; + break; + case 2: + id = addAssignedRoomDialogue(); + break; + case 3: + if (getValue(3) == 1) { + if (id == 240431) + id = 240432; + } + else { + if (id == 240432) + id = 240431; + } + default: + break; + } + + addResponse(getDialogueId(id)); + applyResponse(); + return 2; + } + break; + + case 2: + if (getValue(1) == 1) + return true; + break; + + default: + break; + } + + return 0; +} + +bool DeskbotScript::randomResponse(uint index) { + if (getValue(1) == 1 || getRandomNumber(100) > 10 || getRandomNumber(2) <= index) + return 0; + + if (getRandomNumber(100) > 95) { + deleteResponses(); + addResponse(getDialogueId(241195)); + applyResponse(); + } else { + setResponseFromArray(index, 241193); + } + + return true; +} + +bool DeskbotScript::isDial0Medium() const { + return getDialRegion(0) == 1; +} + +bool DeskbotScript::isDial0Low() const { + return getDialRegion(0) == 0; +} + +bool DeskbotScript::isDial1Medium() const { + return getDialRegion(1) == 1; +} + +bool DeskbotScript::isDial1Low() const { + return getDialRegion(1) == 0; +} + +uint DeskbotScript::addAssignedRoomDialogue() { + if (isDial1Medium()) { + addResponse(getDialogueId(240407)); + addResponse(getDialogueId(241510)); + CTrueTalkManager::setFlags(1, 1); + CTrueTalkManager::triggerAction(19, 1); + + int roomNum, floorNum, elevatorNum; + getAssignedRoom(&roomNum, &floorNum, &elevatorNum); + + addResponse(getDialogueId(241317 + roomNum)); + addResponse(getDialogueId(241271 + floorNum)); + addResponse(getDialogueId(241511)); + addResponse(getDialogueId(241313 + elevatorNum)); + + return 241512; + } else { + return 240567; + } +} + +uint DeskbotScript::addAssignedRoomDialogue2() { + addResponse(getDialogueId(241355)); + int roomNum = 0, floorNum = 0, elevatorNum = 0; + getAssignedRoom(&roomNum, &floorNum, &elevatorNum); + + addResponse(getDialogueId(241317 + roomNum)); + addResponse(getDialogueId(241271 + floorNum)); + addResponse(getDialogueId(241356)); + addResponse(getDialogueId(241313 + elevatorNum)); + + return 241357; +} + +void DeskbotScript::addAssignedRoomDialogue3() { + addResponse(getDialogueId(241513)); + addResponse(getDialogueId(241510)); + + CTrueTalkManager::setFlags(1, 2); + setDialRegion(0, 0); + setDialRegion(1, 0); + CTrueTalkManager::triggerAction(19, 2); + CTrueTalkManager::setFlags(3, 0); + + int roomNum = 1, floorNum = 1, elevatorNum = 1; + getAssignedRoom(&roomNum, &floorNum, &elevatorNum); + + addResponse(getDialogueId(241317 + roomNum)); + addResponse(getDialogueId(241271 + floorNum)); + addResponse(getDialogueId(241511)); + addResponse(getDialogueId(241313 + elevatorNum)); + addResponse(getDialogueId(241512)); + applyResponse(); +} + +uint DeskbotScript::getStateDialogueId() const { + switch (getValue(1)) { + case 1: + return 241503; + case 2: + return 241504; + default: + return 241505; + } +} + +void DeskbotScript::setFlags17(uint newId, uint index) { + int newValue = getValue(17); + + for (uint idx = 0; idx < _states.size(); ++idx) { + const TTupdateState &us = _states[idx]; + if (newId == (idx == 0 ? 0 : us._newId)) { + uint bits = us._dialBits; + + if (!bits + || (index == 1 && (bits & 1) && (bits & 4)) + || (index == 0 && (bits & 2) && (bits & 4)) + || (index == 3 && (bits & 1) && (bits & 8)) + || (index == 2 && (bits & 2) && (bits & 8))) { + newValue = us._newValue; + break; + } + } + } + + CTrueTalkManager::setFlags(17, newValue); +} + +int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence *sentence) { + if (!roomScript || !sentence) + return 1; + + bool stateFlag = true, applyFlag = false; + switch (getValue(17)) { + case 1: + if (sentence->_field2C != 3 && sentence->_field2C != 4 + && sentence->_field2C != 6 && sentence->_field2C != 10 + && sentence->_field2C != 2) { + addResponse(getDialogueId(240423)); + applyFlag = true; + } + break; + + case 2: + if (sentence->localWord("gobbledygook")) { + addResponse(getDialogueId(240427)); + applyFlag = true; + } + break; + + case 3: + if (sentence->_field2C == 11 || sentence->_field2C == 13 + || sentence->_field2C == 3 || sentence->localWord("upgrade")) { + addResponse(getDialogueId(240433)); + applyFlag = true; + } + break; + + case 4: + addResponse(getDialogueId(sentence->_field2C == 11 || sentence->_field2C == 13 + ? 240495 : 240494)); + applyFlag = true; + break; + + case 5: + if (isDial1Low() && getValue(1) == 4) { + if (sentence->localWord("name") || sentence->localWord("called") + || sentence->localWord("passenger")) { + addResponse(getDialogueId(240867)); + addResponse(getDialogueId(240745)); + } else { + addResponse(getDialogueId(240446)); + } + + applyFlag = true; + stateFlag = false; + } + break; + + case 6: + if (isDial1Low() && getValue(1) == 4) { + if (sentence->localWord("passenger")) { + addResponse(getDialogueId(240449)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("home") + || sentence->localWord("destroy")) { + addResponse(getDialogueId(240574)); + stateFlag = true; + } else if (sentence->localWord("name") + || sentence->localWord("called") + || sentence->_field2C == 11) { + addResponse(getDialogueId(240448)); + stateFlag = true; + } else { + addResponse(getDialogueId(240489)); + stateFlag = true; + } + } + break; + + case 7: + if (sentence->_field2C == 11 || sentence->_field2C == 13 + || (sentence->localWord("what") && sentence->localWord("wrong")) + || sentence->localWord("clothes")) { + addResponse(getDialogueId(240489)); + applyFlag = true; + stateFlag = false; + } + break; + + case 8: + if (isDial1Low() && getValue(1) == 4) { + if (sentence->_field2C == 12 || sentence->_field2C == 13 + || sentence->contains("do not")) { + + addResponse(getDialogueId(240447)); + setDialRegion(0, 0); + setDialRegion(1, 0); + CTrueTalkManager::setFlags(1, 3); + CTrueTalkManager::triggerAction(19, 3); + CTrueTalkManager::setFlags(22, 1); + applyFlag = true; + } else if (sentence->_field2C == 11) { + addResponse(getDialogueId(240746)); + applyFlag = true; + stateFlag = false; + } else { + addResponse(getDialogueId(240448)); + applyFlag = true; + stateFlag = false; + } + } + break; + + case 9: + if (searchQuotes(roomScript, sentence)) { + if (isDial0Medium()) { + addResponse(getDialogueId(240382)); + applyFlag = true; + stateFlag = false; + } else { + addResponse(getDialogueId(240548)); + applyFlag = true; + } + } + break; + + case 10: + if (isDial1Medium() && searchQuotes(roomScript, sentence)) { + if (isDial0Medium()) { + addResponse(getDialogueId(240405)); + applyFlag = true; + stateFlag = false; + } else { + addResponse(getDialogueId(240548)); + applyFlag = true; + } + } + break; + + case 11: + if (isDial0Medium() && isDial1Medium() + && searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240403)); + applyFlag = true; + stateFlag = false; + } + break; + + case 12: + if (isDial0Medium() && isDial1Medium() + && searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240548)); + applyFlag = true; + stateFlag = false; + } + break; + + case 13: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240348)); + applyFlag = true; + stateFlag = false; + } + break; + + case 14: + if (isDial1Medium()) { + addResponse(getDialogueId(240568)); + applyFlag = true; + stateFlag = false; + } + break; + + case 15: + if (sentence->localWord("magazine")) { + addAssignedRoomDialogue3(); + stateFlag = true; + } + break; + + case 17: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240401)); + applyFlag = true; + stateFlag = false; + } + break; + + case 18: + if (!isDial0Low() || !isDial1Low()) { + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240402)); + applyFlag = true; + stateFlag = false; + } + } + break; + + case 19: + if (!isDial0Low() && searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240403)); + applyFlag = true; + stateFlag = false; + } + break; + + case 20: + if (!isDial1Medium() && searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240548)); + applyFlag = true; + } + break; + + case 21: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240382)); + applyFlag = true; + stateFlag = false; + } + break; + + case 22: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240383)); + applyFlag = true; + stateFlag = false; + } + break; + + case 23: + if (isDial0Medium() && searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240384)); + applyFlag = true; + stateFlag = false; + } + break; + + case 24: + setDialRegion(0, 0); + + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240548)); + applyFlag = true; + } + break; + + case 25: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240349)); + applyFlag = true; + stateFlag = false; + } + break; + + case 26: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240350)); + applyFlag = true; + stateFlag = false; + } + break; + + case 27: + case 30: + case 33: + case 36: + case 40: + case 43: + case 46: + case 50: + case 55: + case 58: + case 61: + case 68: + case 73: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240548)); + applyFlag = true; + } + break; + + case 28: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240373)); + applyFlag = true; + stateFlag = false; + } + break; + + case 29: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240374)); + applyFlag = true; + stateFlag = false; + } + break; + + case 31: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240346)); + applyFlag = true; + stateFlag = false; + } + break; + + case 32: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240347)); + applyFlag = true; + stateFlag = false; + } + break; + + case 34: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240376)); + applyFlag = true; + stateFlag = false; + } + break; + + case 35: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240377)); + applyFlag = true; + stateFlag = false; + } + break; + + case 37: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240369)); + applyFlag = true; + stateFlag = false; + } + break; + + case 38: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240370)); + applyFlag = true; + stateFlag = false; + } + break; + + case 39: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240371)); + applyFlag = true; + stateFlag = false; + } + break; + + case 41: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240352)); + applyFlag = true; + stateFlag = false; + } + break; + + case 42: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240353)); + applyFlag = true; + stateFlag = false; + } + break; + + case 44: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240389)); + applyFlag = true; + stateFlag = false; + } + break; + + case 45: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240390)); + applyFlag = true; + stateFlag = false; + } + break; + + case 47: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240395)); + applyFlag = true; + stateFlag = false; + } + break; + + case 48: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240396)); + applyFlag = true; + stateFlag = false; + } + break; + + case 49: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240397)); + applyFlag = true; + stateFlag = false; + } + break; + + case 51: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240363)); + applyFlag = true; + stateFlag = false; + } + break; + + case 52: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240364)); + applyFlag = true; + stateFlag = false; + } + break; + + case 53: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240365)); + applyFlag = true; + stateFlag = false; + } + break; + + case 54: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240366)); + applyFlag = true; + stateFlag = false; + } + break; + + case 56: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240392)); + applyFlag = true; + stateFlag = false; + } + break; + + case 57: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240393)); + applyFlag = true; + stateFlag = false; + } + break; + + case 59: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240386)); + applyFlag = true; + stateFlag = false; + } + break; + + case 60: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240387)); + applyFlag = true; + stateFlag = false; + } + break; + + case 62: + if (isDial1Medium() && getValue(1) == 1) { + if (isDial0Medium()) { + if (sentence->localWord("down")) { + addResponse(getDialogueId(240413)); + applyFlag = true; + } + } else if (sentence->contains("left") || sentence->contains("right")) { + addResponse(getDialogueId(240415)); + CTrueTalkManager::triggerAction(sentence->localWord("down") ? 22 : 23, 0); + } else { + addResponse(getDialogueId(240414)); + applyFlag = true; + } + } + break; + + case 63: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240836)); + applyFlag = true; + stateFlag = false; + } + break; + + case 64: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240837)); + applyFlag = true; + stateFlag = false; + } + break; + + case 65: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240838)); + applyFlag = true; + stateFlag = false; + } + break; + + case 66: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240839)); + applyFlag = true; + stateFlag = false; + } + break; + + case 67: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(240840)); + applyFlag = true; + stateFlag = false; + } + break; + + case 69: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(241104)); + applyFlag = true; + stateFlag = false; + } + break; + + case 70: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(241105)); + applyFlag = true; + stateFlag = false; + } + break; + + case 71: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(241106)); + applyFlag = true; + stateFlag = false; + } + break; + + case 72: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(241107)); + applyFlag = true; + stateFlag = false; + } + break; + + case 74: + case 75: + if (sentence->_field2C == 24) { + addResponse(getDialogueId(240972)); + applyFlag = true; + } else if (sentence->localWord("good") || sentence->localWord("yes") + || sentence->localWord("well") || sentence->localWord("ill") + || sentence->localWord("sad")) { + addResponse(getDialogueId(240805)); + applyFlag = true; + } + break; + + case 76: + if (sentence->_field2C == 6) { + addResponse(getDialogueId(240767)); + applyFlag = true; + stateFlag = false; + } + break; + + case 77: + if (sentence->_field2C == 3) { + addResponse(getDialogueId(241109)); + applyFlag = true; + stateFlag = false; + } + + case 78: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(241262)); + } else if (sentence->_field2C == 12 || sentence->contains("do not")) { + setDialRegion(0, 0); + setDialRegion(1, 0); + addResponse(getDialogueId(241268)); + add241716(); + } else { + addResponse(getDialogueId(240745)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 79: + switch (checkCommonWords(sentence)) { + case 1: + addResponse(getDialogueId(241263)); + break; + case 2: + addResponse(getDialogueId(241266)); + break; + case 3: + addAssignedRoom(); + setState(0); + CTrueTalkManager::setFlags(17, 0); + return 2; + default: + addResponse(getDialogueId(241267)); + break; + } + + add241716(); + applyFlag = true; + stateFlag = false; + break; + + case 81: + addResponse(getDialogueId(sentence->_field2C == 12 ? 240602 : 241337)); + applyResponse(); + setState(0); + CTrueTalkManager::setFlags(17, 0); + return 2; + + case 82: + if (sentence->_field2C == 2) { + addResponse(getDialogueId(241339)); + applyFlag = true; + } + break; + + case 83: + if ((isDial1Medium() && isDial0Low()) || + (isDial1Low() && isDial0Medium())) { + if (sentence->localWord("yes") || sentence->localWord("but")) { + if (sentence->localWord("will") || sentence->localWord("do")) { + addResponse(getDialogueId(241366)); + applyFlag = true; + } + } + } + break; + + case 84: + if (sentence->_field2C == 12 || sentence->contains("vegetarian") + || sentence->contains("vegitarian")) { + addResponse(getDialogueId(241718)); + addResponse(getDialogueId(241709)); + applyFlag = true; + stateFlag = false; + } else if (sentence->contains("continental") + || sentence->contains("full") + || sentence->contains("porky") + || sentence->contains("the 1") + || sentence->contains("the 2") + || sentence->contains("former") + || sentence->contains("latter")) { + addResponse(getDialogueId(241717)); + addResponse(getDialogueId(241709)); + applyFlag = true; + stateFlag = false; + } else { + if (sentence2C(sentence)) + addResponse(getDialogueId(241707)); + addResponse(getDialogueId(241719)); + applyFlag = true; + stateFlag = false; + } + break; + + case 85: + if (sentence->_field2C == 12 || sentence->contains("bugle") + || sentence->contains("buggle") || sentence->contains("trumpet") + || sentence->contains("saxophone") || sentence->contains("kazoo") + || sentence->contains("blerontin 1") || sentence->contains("the 1") + || sentence->contains("the 2") || sentence->contains("the 3") + || sentence->contains("the 4") || sentence->contains("all of them") + || sentence->contains("the lot")) { + addResponse(getDialogueId(241710)); + addResponse(getDialogueId(241713)); + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + + addResponse(getDialogueId(241711)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 86: + if (sentence->_field2C == 12 || sentence->_field2C == 11 || sentence->contains("view")) { + addResponse(getDialogueId(241714)); + addResponse(getDialogueId(241699)); + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + + if (getRandomNumber(100) < 50) { + addResponse(getDialogueId(241715)); + } else { + addResponse(getDialogueId(241712)); + addResponse(getDialogueId(241713)); + } + + } + + applyFlag = true; + stateFlag = false; + break; + + case 87: + if (sentence->contains("corner") || sentence->contains("on the end") + || sentence->contains("balcony") || sentence->contains("neither") + || sentence->contains("the 1") || sentence->contains("the 2") + || sentence->contains("former") || sentence->contains("latter") + || sentence->contains("either")) { + addResponse(getDialogueId(241700)); + addResponse(getDialogueId(241687)); + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + + addResponse(getDialogueId(241701)); + addResponse(getDialogueId(241699)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 88: + if (sentence->contains("imperial") || sentence->contains("the 1")) { + addResponse(getDialogueId(241700)); + addResponse(getDialogueId(241739)); + } else if (sentence->contains("royal") || sentence->contains("the 2")) { + addResponse(getDialogueId(241690)); + } else if (sentence->contains("despotic") || sentence->contains("the last") + || sentence->contains("latter")) { + addResponse(getDialogueId(241688)); + } else if (sentence->contains("president") || sentence->contains("presidential") + || sentence->contains("the 3")) { + addResponse(getDialogueId(241689)); + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + + addResponse(getDialogueId(241692)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 89: + if (sentence->contains("king")) { + addResponse(getDialogueId(241691)); + } else if (sentence->contains("queen") || sentence->contains("prince") + || sentence->contains("princess") || sentence->contains("small") + || sentence->contains("the 1") || sentence->contains("the 2") + || sentence->contains("the 3") || sentence->contains("the 4") + || sentence->contains("big") || sentence->contains("large")) { + addResponse(getDialogueId(241700)); + addResponse(getDialogueId(241739)); + } else { + if (getRandomNumber(100) < 100 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + + addResponse(getDialogueId(241690)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 90: + if (sentence->contains("constitutional") || sentence->contains("const") + || sentence->contains("absolute") || sentence->contains("small") + || sentence->contains("the 1") || sentence->contains("the 2") + || sentence->contains("big") || sentence->contains("large")) { + addResponse(getDialogueId(241700)); + addResponse(getDialogueId(241739)); + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241708)); + + addResponse(getDialogueId(241691)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 91: + if (sentence->contains("benev") || sentence->contains("dict") + || sentence->contains("small") || sentence->contains("the 1") + || sentence->contains("the 2") || sentence->contains("big") + || sentence->contains("large") || sentence->contains("former") + || sentence->contains("latter")) { + addResponse(getDialogueId(241700)); + addResponse(getDialogueId(241739)); + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241708)); + + addResponse(getDialogueId(241688)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 92: + case 93: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(241077)); + addResponse(getDialogueId(241706)); + } else if (sentence->_field2C == 12) { + addAssignedRoom(); + setState(0); + CTrueTalkManager::setFlags(17, 0); + return 2; + } else if (g_vm->_trueTalkManager->_quotes.find(sentence->_normalizedLine.c_str()) + == MKTAG('F', 'I', 'S', 'H')) { + addResponse(getDialogueId(240877)); + addResponse(getDialogueId(241706)); + }else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + + addResponse(getDialogueId(241705)); + if (getRandomNumber(100) < 80) + addResponse(getDialogueId(241739)); + } + + applyFlag = true; + stateFlag = false; + break; + + case 94: + if (sentence->contains("seperate") || sentence->contains("separate") + || sentence->contains("detached") || sentence->contains("outside") + || sentence->contains("onsweet") || sentence->contains("ensuite") + || sentence->contains("suite") || sentence->contains("next door") + || sentence->contains("the 1") || sentence->contains("the 2") + || sentence->contains("former") || sentence->contains("latter") + || sentence->contains("same room")) { + addAssignedRoom(); + setState(0); + CTrueTalkManager::setFlags(17, 0); + return 2; + } else { + if (getRandomNumber(100) < 80 && sentence2C(sentence)) + addResponse(getDialogueId(241707)); + addResponse(getDialogueId(241706)); + applyFlag = true; + stateFlag = false; + } + break; + + case 95: + if (isDial1Medium()) { + if ((sentence->localWord("i") && sentence->localWord("am")) + || sentence->localWord("me")) { + addResponse(getDialogueId(240632)); + applyFlag = true; + stateFlag = false; + } + } + break; + + case 96: + if (sentence->_field2C == 2) { + addResponse(getDialogueId(241350)); + applyFlag = true; + stateFlag = false; + } + break; + + case 97: + if (searchQuotes(roomScript, sentence)) { + addResponse(getDialogueId(241351)); + applyFlag = true; + stateFlag = false; + } + break; + + default: + break; + } + + if (applyFlag) + applyResponse(); + if (stateFlag) { + setState(0); + CTrueTalkManager::setFlags(17, 0); + } + + return applyFlag ? 2 : 1; +} + +int DeskbotScript::searchQuotes(const TTroomScript *roomScript, const TTsentence *sentence) { + TTtreeResult treeResult; + return g_vm->_trueTalkManager->_quotesTree.search(sentence->_normalizedLine.c_str(), + TREE_2, &treeResult, 0, 0) != -1; +} + +int DeskbotScript::checkCommonWords(const TTsentence *sentence) { + if (sentence->contains("xyzzy")) + return 3; + + const TTquotes "es = g_vm->_trueTalkManager->_quotes; + if (quotes._loaded) { + uint tagId = quotes.find(sentence->_normalizedLine.c_str()); + if (tagId == MKTAG('F', 'U', 'L', 'N') + || tagId == MKTAG('T', 'D', 'V', 'P') + || tagId == MKTAG('H', 'E', 'R', 'O') + || sentence->contains("douglas adam")) + return 1; + else if (tagId == MKTAG('J', 'N', 'A', 'M') + || tagId == MKTAG('N', 'I', 'K', 'N') + || tagId == MKTAG('B', 'O', 'Y', 'S') + || tagId == MKTAG('G', 'I', 'R', 'L')) + return 2; + } else { + if (sentence->contains("douglas adams") + || sentence->contains("shaikh") + || sentence->contains("millican") + || sentence->contains("williams") + || sentence->contains("henkes") + || sentence->contains("kenny")) + return 1; + else if (sentence->contains("richard") + || sentence->contains("jason") + || sentence->contains("mike") + || sentence->contains("renata")) + return 2; + } + + return 0; +} + +void DeskbotScript::add241716() { + addResponse(getDialogueId(241716)); +} + +void DeskbotScript::addAssignedRoom() { + addResponse(getDialogueId(241696)); + addResponse(getDialogueId(241697)); + CTrueTalkManager::setFlags(1, 3); + CTrueTalkManager::triggerAction(19, 3); + CTrueTalkManager::setFlags(22, 1); + + int roomNum = 1, floorNum = 1, elevatorNum = 1; + getAssignedRoom(&roomNum, &floorNum, &elevatorNum); + addResponse(getDialogueId(241313 + elevatorNum)); + addResponse(getDialogueId(241271 + floorNum)); + addResponse(getDialogueId(241317 + roomNum)); + addResponse(getDialogueId(241698)); +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/deskbot_script.h b/engines/titanic/true_talk/deskbot_script.h new file mode 100644 index 0000000000..f5978553ce --- /dev/null +++ b/engines/titanic/true_talk/deskbot_script.h @@ -0,0 +1,157 @@ +/* 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. + * + */ + +#ifndef TITANIC_DESKBOT_SCRIPT_H +#define TITANIC_DESKBOT_SCRIPT_H + +#include "common/array.h" +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class DeskbotScript : public TTnpcScript { +private: + static int _oldId; + TTupdateStateArray _states; + TTsentenceEntries _entries2; + TTsentenceEntries _entries3; +private: + /** + * Setup sentence data + */ + void setupSentences(); + + /** + * Adds dialogue for the player's assigned room + */ + uint addAssignedRoomDialogue(); + + /** + * Adds dialogue for the player's assigned room + */ + uint addAssignedRoomDialogue2(); + + /** + * Adds dialogue for the player's assigned room + */ + void addAssignedRoomDialogue3(); + + /** + * Gets a dialogue Id based on the NPC's state + */ + uint getStateDialogueId() const; + + /** + * Sets state data in flags 17 + */ + void setFlags17(uint newId, uint index); + + /** + * Does preprocessing for the sentence + */ + int preprocess(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Scans the quotes tree + */ + int searchQuotes(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Checks for common words + */ + int checkCommonWords(const TTsentence *sentence); + + /** + * Adds response dialogue 241716 + */ + void add241716(); + + /** + * Adds a dialogue description for the player's assigned room + */ + void addAssignedRoom(); +public: + DeskbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); + + /** + * Returns a bitset of the first three dialgs being on or not + */ + virtual uint getDialsBitset() const; + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Handles a randomzied response + */ + virtual bool randomResponse(uint index); + + /** + * Returns true if dial 1 is the medium (1) region + */ + virtual bool isDial0Medium() const; + + /** + * Returns true if dial 0 is the low end region + */ + virtual bool isDial0Low() const; + + /** + * Returns true if dial 1 is the medium (1) region + */ + bool isDial1Medium() const; + + /** + * Returns true if dial 1 is the low end region + */ + virtual bool isDial1Low() const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_DESKBOT_SCRIPT_H */ diff --git a/engines/titanic/true_talk/dialogue_file.cpp b/engines/titanic/true_talk/dialogue_file.cpp new file mode 100644 index 0000000000..34eb164779 --- /dev/null +++ b/engines/titanic/true_talk/dialogue_file.cpp @@ -0,0 +1,109 @@ +/* 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/dialogue_file.h" + +namespace Titanic { + +void DialogueIndexEntry::load(Common::SeekableReadStream &s) { + _v1 = s.readUint32LE(); + _offset = s.readUint32LE(); +} + +/*------------------------------------------------------------------------*/ + +CDialogueFile::CDialogueFile(const CString &filename, uint count) { + if (!_file.open(filename)) + error("Could not locate dialogue file - %s", filename.c_str()); + + _cache.resize(count); + + _file.readUint32LE(); // Skip over file Id + _index.resize(_file.readUint32LE()); + + // Read in the entries + for (uint idx = 0; idx < _index.size(); ++idx) + _index[idx].load(_file); +} + +CDialogueFile::~CDialogueFile() { + clear(); +} + +void CDialogueFile::clear() { + _file.close(); +} + +DialogueResource *CDialogueFile::addToCache(int index) { + if (_index.size() == 0 || index < 0 || index >= (int)_index.size() + || _cache.empty()) + return nullptr; + + // Scan cache for a free slot + uint cacheIndex = 0; + while (cacheIndex < _cache.size() && !_cache[cacheIndex]._active) + ++cacheIndex; + if (cacheIndex == _cache.size()) + return nullptr; + + DialogueIndexEntry &indexEntry = _index[index]; + DialogueResource &res = _cache[cacheIndex]; + + res._active = true; + res._offset = indexEntry._offset; + res._bytesRead = 0; + res._entryPtr = &indexEntry; + + // Figure out the size of the entry + if (index == ((int)_index.size() - 1)) { + res._size = _file.size() - indexEntry._offset; + } else { + res._size = _index[index + 1]._offset - indexEntry._offset; + } + + // Return a pointer to the loaded entry + return &res; +} + +bool CDialogueFile::closeEntry(DialogueResource *cacheEntry) { + if (!cacheEntry || !cacheEntry->_active) + return false; + + cacheEntry->_active = false; + return true; +} + +bool CDialogueFile::read(DialogueResource *cacheEntry, byte *buffer, size_t bytesToRead) { + // Sanity checks that a valid record is passed, and the size can be read + if (!cacheEntry || !cacheEntry->_active || !bytesToRead + || (cacheEntry->_bytesRead + bytesToRead) > cacheEntry->_size) + return false; + + // Move to the correct position in the file + _file.seek(cacheEntry->_offset + cacheEntry->_bytesRead); + bool result = _file.read(buffer, bytesToRead) == bytesToRead; + cacheEntry->_bytesRead += bytesToRead; + + return result; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/dialogue_file.h b/engines/titanic/true_talk/dialogue_file.h new file mode 100644 index 0000000000..19e94cf9b9 --- /dev/null +++ b/engines/titanic/true_talk/dialogue_file.h @@ -0,0 +1,100 @@ +/* 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. + * + */ + +#ifndef TITANIC_DIALOGUE_FILE_H +#define TITANIC_DIALOGUE_FILE_H + +#include "titanic/support/simple_file.h" +#include "titanic/support/string.h" + +namespace Titanic { + +struct DialogueIndexEntry { + uint _v1, _offset; + + DialogueIndexEntry() : _v1(0), _offset(0) {} + void load(Common::SeekableReadStream &s); +}; + +struct DialogueResource { + bool _active; + uint _offset, _bytesRead, _size; + DialogueIndexEntry *_entryPtr; + + DialogueResource() : _active(false), _offset(0), + _bytesRead(0), _size(0), _entryPtr(nullptr) {} + + /** + * Return the size of a cache entry + */ + size_t size() const { return _active ? _size : 0; } +}; + +class CDialogueFile { +private: + File _file; + Common::Array<DialogueIndexEntry> _index; + Common::Array<DialogueResource> _cache; +private: + /** + * Add a dialogue file entry to the active cache + */ + DialogueResource *addToCache(int index); +public: + CDialogueFile(const CString &filename, uint count); + ~CDialogueFile(); + + /** + * Clear the loaded data + */ + void clear(); + + File *getFile() { return &_file; } + + /** + * Sets up a text entry within the dialogue file for access + */ + DialogueResource *openTextEntry(int index) { + return addToCache(index * 2); + } + + /** + * Sets up a wave (sound) entry within the dialogue file for access + */ + DialogueResource *openWaveEntry(int index) { + return addToCache(index * 2 + 1); + } + + /** + * Removes an entry from the cache + */ + bool closeEntry(DialogueResource *cacheEntry); + + /** + * Read data for a resource + */ + bool read(DialogueResource *cacheEntry, byte *buffer, size_t bytesToRead); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TITLE_ENGINE_H */ diff --git a/engines/titanic/true_talk/doorbot_script.cpp b/engines/titanic/true_talk/doorbot_script.cpp new file mode 100644 index 0000000000..1ca1ab13e5 --- /dev/null +++ b/engines/titanic/true_talk/doorbot_script.cpp @@ -0,0 +1,1013 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/doorbot_script.h" +#include "titanic/true_talk/tt_room_script.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/titanic.h" + +namespace Titanic { + +static const int STATE_ARRAY[9] = { + 0x2E2A, 0x2E2B, 0x2E2C, 0x2E2D, 0x2E2E, 0x2E2F, 0x2E30, 0x2E31, 0x2E32 +}; + +static const RoomDialogueId ROOM_DIALOGUES1[] = { + { 100, 10523 }, { 101, 10499 }, { 107, 10516 }, { 108, 10500 }, + { 109, 10490 }, { 110, 10504 }, { 111, 10506 }, { 112, 10498 }, + { 113, 10502 }, { 114, 10507 }, { 115, 10497 }, { 116, 10508 }, + { 117, 10505 }, { 118, 10505 }, { 122, 10516 }, { 123, 10383 }, + { 124, 10510 }, { 125, 10511 }, { 126, 10513 }, { 127, 10512 }, + { 128, 10495 }, { 129, 10496 }, { 130, 10491 }, { 131, 10493 }, + { 132, 10492 }, { 0, 0 } +}; +static const RoomDialogueId ROOM_DIALOGUES2[] = { + { 102, 221981 }, { 110, 221948 }, { 111, 221968 }, { 107, 222000 }, + { 101, 221935 }, { 112, 221924 }, { 113, 221942 }, { 116, 221977 }, + { 124, 221987 }, { 125, 221984 }, { 127, 221991 }, { 128, 221916 }, + { 129, 221919 }, { 131, 221912 }, { 132, 221908 }, { 0, 0 } +}; + +DoorbotScript::DoorbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, v4, v5, v6, v7) { + loadRanges("Ranges/Doorbot"); + loadResponses("Responses/Doorbot"); + setupSentences(); + _tagMappings.load("TagMap/Doorbot"); + _words.load("Words/Doorbot"); + _quotes.load("Quotes/Doorbot"); + _states.load("States/Doorbot"); +} + +void DoorbotScript::setupSentences() { + for (int idx = 35; idx < 40; ++idx) + CTrueTalkManager::setFlags(idx, 0); + _doorbotState = 1; + _field68 = 0; + _entryCount = 0; + _dialValues[0] = _dialValues[1] = 100; + + _mappings.load("Mappings/Doorbot", 4); + _entries.load("Sentences/Doorbot"); + + static const int SENTENCE_NUMS[11] = { + 2, 100, 101, 102, 107, 110, 111, 124, 129, 131, 132 + }; + for (int idx = 0; idx < 11; ++idx) { + _sentences[idx] = TTsentenceEntries(); + _sentences[idx].load(CString::format("Sentences/Doorbot/%d", SENTENCE_NUMS[idx])); + } +} + +int DoorbotScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + if (tag == MKTAG('D', 'N', 'A', '1') || tag == MKTAG('H', 'H', 'G', 'Q') || + tag == MKTAG('A', 'N', 'S', 'W') || tag == MKTAG('S', 'U', 'M', 'S')) { + if (_stateIndex > 9) + _stateIndex = 0; + addResponse(STATE_ARRAY[_stateIndex]); + applyResponse(); + + if (STATE_ARRAY[_stateIndex] == 11826) + setState(1); + ++_stateIndex; + return 2; + } + + if (tag == MKTAG('C', 'H', 'S', 'E') || tag == MKTAG('C', 'M', 'N', 'T') || + tag == MKTAG('J', 'F', 'O', 'D')) + tag = MKTAG('F', 'O', 'O', 'D'); + + if (tag == MKTAG('F', 'O', 'O', 'D') && roomScript->_scriptId == 132) { + return setResponse(getDialogueId(220818)); + } + + if (tag == MKTAG('T', 'R', 'A', 'V')) { + return setResponse(11858 - getRandomBit()); + } else if (tag == MKTAG('C', 'S', 'P', 'Y')) { + return setResponse(10405, 3); + } else if (tag == MKTAG('S', 'C', 'I', 'T')) { + return setResponse(10410, 14); + } else if (tag == MKTAG('L', 'I', 'T', 'E')) { + return setResponse(10296, 17); + } else if (tag == MKTAG('D', 'O', 'R', '1')) { + return setResponse(getDialogueId(222034)); + } else if (tag == MKTAG('W', 'T', 'H', 'R')) { + return setResponse(getDialogueId(222126)); + } else if (tag == MKTAG('N', 'A', 'U', 'T')) { + return setResponse(getDialogueId(222259)); + } else if (tag == MKTAG('T', 'R', 'A', '2')) { + return setResponse(getRandomBit() ? 11860 : 11859); + } else if (tag == MKTAG('T', 'R', 'A', '3')) { + return setResponse(getRandomBit() ? 11859 : 11858); + } else if (tag == MKTAG('B', 'R', 'N', 'D')) { + switch (getRandomNumber(3)) { + case 1: + tag = MKTAG('B', 'R', 'N', '2'); + break; + case 2: + tag = MKTAG('B', 'R', 'N', '3'); + break; + default: + break; + } + } + + return TTnpcScript::chooseResponse(roomScript, sentence, tag); +} + +int DoorbotScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + int currState; + + switch (roomScript->_scriptId) { + case 100: + case 101: + case 102: + case 103: + case 104: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 113: + case 116: + case 117: + case 118: + case 122: + case 123: + case 124: + case 125: + case 126: + case 127: + case 128: + case 129: + case 130: + case 131: + case 132: + break; + + default: + return 2; + } + + checkItems(nullptr, nullptr); + if (getState() == 0) { + if (CTrueTalkManager::_v2 > getValue(36)) { + if (getDialRegion(1) == 1 && getRandomBit()) { + setDialRegion(0, getDialRegion(1) ? 0 : 1); + } else { + setDialRegion(1, getDialRegion(1) ? 0 : 1); + } + CTrueTalkManager::setFlags(36, CTrueTalkManager::_v2 + 3 + getRandomNumber(5)); + + if (getValue(37)) { + CTrueTalkManager::setFlags(37, 0); + setState(0); + return setResponse(getDialogueId(221140)); + } + } + } + + if (getValue(35) == 0 && roomScript->_scriptId != 100 && sentence->localWord("parrot")) { + CTrueTalkManager::setFlags(35, 1); + setState(0); + return setResponse(getDialogueId(220113)); + } + + if (sentence->_field2C == 6 && sentence->contains("why not")) { + return setResponse(11871, 8); + } + + currState = getState(); + if (currState) { + int sentMode = sentence->_field2C; + bool flag1 = sentMode == 11 || sentMode == 13; + bool flag2 = sentMode == 12; + + switch (currState) { + case 1: + if (flag1) + return setResponse(11828, 2); + if (flag2) + return setResponse(11827, 0); + break; + + case 2: + if (flag1) + return setResponse(11827, 0); + break; + + case 3: + if (sentMode == 3) + return setResponse(10406, 0); + break; + + case 4: + if (flag1) + return setResponse(11332, 0); + if (flag2) + return setResponse(11331, 0); + break; + + case 5: + return setResponse(11868, 0); + + case 6: + return setResponse(11872, 0); + + case 7: + return setResponse(11869, 0); + + case 8: + return setResponse(11870, 0); + + case 12: + if (flag1) + return setResponse(11894, 13); + if (flag2) + return setResponse(11893, 13); + break; + + case 13: + return setResponse(11895, 12); + + case 15: + if (sentMode == 3 || sentMode == 6) + return setResponse(10257, 0); + break; + + case 16: { + TTtreeResult treeResult; + if (g_vm->_trueTalkManager->_quotesTree.search(sentence->_normalizedLine.c_str(), + TREE_3, &treeResult, 0, nullptr) != -1) + return setResponse(getDialogueId(221380), 0); + break; + } + + case 17: + return setResponse(getDialogueId(221126), 0); + + case 18: + if (flag1) + return setResponse(getDialogueId(221135), 0); + if (flag2) + return setResponse(getDialogueId(221134), 0); + break; + + case 19: + if (flag1) { + if (addRandomResponse(true)) { + setState(10); + return 2; + } + } + if (flag2) + return setResponse(getDialogueId(221966), 0); + break; + + case 20: + if (flag1) { + if (addRandomResponse(true)) { + setState(19); + return 2; + } + } + if (flag2 || sentMode == 7 || sentMode == 10) { + return setResponse(getDialogueId(221879), 0); + } + break; + + case 21: + if (flag2) + return setResponse(10935, 0); + break; + + case 22: + if (flag1) { + if (getRandomBit()) { + return setResponse(11211, 23); + } else { + return setResponse(10127, 0); + } + } + if (flag2) + return setResponse(10136, 0); + break; + + case 23: + return setResponse(10212, 0); + + case 24: + if (flag1) + return setResponse(11151, 0); + if (flag2) + return setResponse(11150, 0); + break; + + case 25: + case 26: + if (flag2) { + if (getRandomBit()) { + return setResponse(11211, 23); + } else { + return setResponse(10127, 0); + } + } + if (flag1) + return setResponse(10136, 0); + break; + + case 27: + if (flag1 || sentence->localWord("did") || sentence->contains("did")) + return setResponse(221175, 28); + break; + + case 28: + if (flag1 || sentence->localWord("did") || sentence->contains("did")) + return setResponse(getDialogueId(221176), 29); + break; + + case 29: + if (flag1 || sentence->localWord("did") || sentence->contains("did")) + return setResponse(getDialogueId(221177), 30); + break; + + case 30: + return setResponse(getDialogueId(221178), 31); + + case 31: + if (sentMode == 3 || sentMode == 10) + return setResponse(10350, 0); + break; + + case 32: + return setResponse(10110, 0); + + case 33: + if (sentence->contains("sieve") || sentence->contains("colander") + || sentence->contains("vegetable") || sentence->contains("ground") + || sentence->contains("earth") || sentence->contains("garden") + || sentence->contains("cheese") || sentence->contains("strainer")) { + return setResponse(getDialogueId(221375), 0); + } else if (getRandomNumber(100) > 30) { + return setResponse(getDialogueId(221376), 33); + } else { + return setResponse(getDialogueId(221376), 0); + } + break; + + case 34: + if (sentence->localWord("bellbot")) + return setResponse(10094, 0); + if (sentence->localWord("bellbot")) + return setResponse(10349, 0); + if (sentence->localWord("deskbot") || sentence->localWord("titania")) + return setResponse(10148, 0); + if (sentence->localWord("barbot") || sentence->localWord("rowbot") + || sentence->localWord("liftbot") || sentence->localWord("maitredbot")) + return setResponse(10147, 0); + break; + + case 35: + return setResponse(10811, 36); + + case 36: + if (flag1) + return setResponse(10813, 37); + if (flag2) + return setResponse(10812, 37); + break; + + case 37: + if (flag1) + return setResponse(10815, 37); + if (flag2) + return setResponse(10814, 37); + break; + + case 38: + return setResponse(10848, 39); + + case 39: + return setResponse(10823, 40); + + case 40: + return setResponse(10832, 41); + + case 41: + addResponse(10833); + return setResponse(10835, 0); + + case 42: + if (sentence->localWord("please")) + return setResponse(10840, 43); + return setResponse(10844, 0); + + case 43: + case 45: + return setResponse(10844, 0); + + case 44: + if (sentence->localWord("thanks")) + return setResponse(10843, 45); + return setResponse(10844, 0); + + case 46: + if (flag1) + return setResponse(getDialogueId(222251), 0); + if (flag2) + return setResponse(10713, 0); + break; + + } + } + + if (currState != 14) + setState(0); + + if (getDialRegion(1) != 1 && getRandomNumber(100) > 92) + return setResponse(getDialogueId(221043), 0); + + int result = 0; + switch (roomScript->_scriptId) { + case 100: + case 101: + case 102: + case 107: + case 110: + case 111: + case 124: + case 129: + case 131: + case 132: + result = processEntries(&_sentences[roomScript->_scriptId], 0, roomScript, sentence); + break; + default: + break; + } + if (result == 2) + return 2; + + if (processEntries(&_entries, _entryCount, roomScript, sentence) == 2 + || processEntries(_defaultEntries, 0, roomScript, sentence) == 2 + || defaultProcess(roomScript, sentence)) + return 2; + + switch (sentence->_field2C) { + case 11: + if (getRandomNumber(100) > 90) + return setResponse(10839, 42); + return setResponse(222415, 0); + + case 12: + if (getRandomNumber(100) > 90) + return setResponse(10841, 44); + return setResponse(getDialogueId(222416), 0); + + case 13: + return setResponse(getDialogueId(222415), 0); + + default: + if (getRandomNumber(100) > 75 && getStateValue()) + return setResponse(getDialogueId(221095)); + + if (processEntries(&_sentences[2], 0, roomScript, sentence) != 2) + return setResponse(getDialogueId(220000)); + break; + } + + return 2; +} + +ScriptChangedResult DoorbotScript::scriptChanged(const TTroomScript *roomScript, uint id) { + if (id == 3) { + if (roomScript != nullptr && roomScript->_scriptId != 100) { + if (CTrueTalkManager::_v9 == 101) { + addResponse(getDialogueId(220873)); + applyResponse(); + } else { + bool flag = false; + if (CTrueTalkManager::_currentNPC) { + CGameObject *obj; + if (CTrueTalkManager::_currentNPC->find("Magazine", &obj, FIND_PET)) { + setResponse(getDialogueId(222248), 46); + flag = true; + } + } + + if (!flag) { + if (getRandomNumber(100) > 80 && getStateValue()) { + addResponse(getDialogueId(221095)); + applyResponse(); + flag = true; + } + + if (!flag && (_doorbotState || !fn10(true))) { + addResponse(getDialogueId(220074)); + applyResponse(); + } + } + } + } + + _doorbotState = 0; + resetFlags(); + CTrueTalkManager::_v9 = 0; + } else if (id == 4) { + setState(0); + if (getValue(38) == 0) { + addResponse(getDialogueId(220883)); + applyResponse(); + } + + CTrueTalkManager::setFlags(38, 0); + CTrueTalkManager::setFlags(39, 0); + } + + if (id >= 220000 && id <= 222418) { + addResponse(getDialogueId(id)); + applyResponse(); + } else if (id >= 10000 && id <= 11986) { + addResponse(id); + applyResponse(); + } + + return SCR_2; +} + +int DoorbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + switch (tagId) { + case MKTAG('A', 'D', 'V', 'T'): + case MKTAG('A', 'R', 'T', 'I'): + case MKTAG('A', 'R', 'T', 'Y'): + case MKTAG('B', 'R', 'N', 'D'): + case MKTAG('C', 'O', 'M', 'D'): + case MKTAG('D', 'N', 'C', 'E'): + case MKTAG('H', 'B', 'B', 'Y'): + case MKTAG('L', 'I', 'T', 'R'): + case MKTAG('M', 'A', 'G', 'S'): + case MKTAG('M', 'C', 'P', 'Y'): + case MKTAG('M', 'I', 'N', 'S'): + case MKTAG('M', 'U', 'S', 'I'): + case MKTAG('N', 'I', 'K', 'E'): + case MKTAG('S', 'F', 'S', 'F'): + case MKTAG('S', 'O', 'A', 'P'): + case MKTAG('S', 'O', 'N', 'G'): + case MKTAG('S', 'P', 'R', 'T'): + case MKTAG('T', 'E', 'A', 'M'): + case MKTAG('T', 'V', 'S', 'H'): + case MKTAG('W', 'W', 'E', 'B'): + tagId = MKTAG('E', 'N', 'T', 'N'); + break; + case MKTAG('A', 'C', 'T', 'R'): + case MKTAG('A', 'C', 'T', 'S'): + case MKTAG('A', 'U', 'T', 'H'): + case MKTAG('B', 'A', 'R', 'K'): + case MKTAG('B', 'A', 'R', 'U'): + case MKTAG('B', 'L', 'F', '1'): + case MKTAG('B', 'L', 'F', '2'): + case MKTAG('B', 'L', 'P', '1'): + case MKTAG('B', 'L', 'P', '2'): + case MKTAG('B', 'L', 'P', '3'): + case MKTAG('B', 'L', 'P', '4'): + case MKTAG('B', 'L', 'T', '1'): + case MKTAG('B', 'L', 'T', '2'): + case MKTAG('B', 'L', 'T', '3'): + case MKTAG('B', 'L', 'T', '4'): + case MKTAG('B', 'L', 'T', '5'): + case MKTAG('B', 'O', 'Y', 'S'): + case MKTAG('D', 'C', 'T', 'R'): + case MKTAG('F', 'A', 'M', 'E'): + case MKTAG('F', 'A', 'S', 'H'): + case MKTAG('G', 'I', 'R', 'L'): + case MKTAG('H', 'E', 'R', 'O'): + case MKTAG('H', 'O', 'S', 'T'): + case MKTAG('K', 'N', 'O', 'B'): + case MKTAG('N', 'H', 'R', 'O'): + case MKTAG('R', 'A', 'C', 'E'): + case MKTAG('S', 'C', 'I', 'T'): + case MKTAG('T', 'D', 'V', 'P'): + case MKTAG('T', 'W', 'A', 'T'): + case MKTAG('W', 'E', 'A', 'T'): + tagId = MKTAG('P', 'R', 'S', 'N'); + break; + case MKTAG('C', 'H', 'S', 'E'): + case MKTAG('C', 'M', 'N', 'T'): + case MKTAG('F', 'I', 'L', 'M'): + case MKTAG('J', 'F', 'O', 'D'): + case MKTAG('L', 'I', 'Q', 'D'): + tagId = MKTAG('F', 'O', 'O', 'D'); + break; + case MKTAG('C', 'R', 'I', 'M'): + case MKTAG('C', 'S', 'P', 'Y'): + case MKTAG('D', 'R', 'U', 'G'): + tagId = MKTAG('V', 'B', 'A', 'D'); + break; + case MKTAG('E', 'A', 'R', 'T'): + case MKTAG('H', 'O', 'M', 'E'): + case MKTAG('N', 'P', 'L', 'C'): + case MKTAG('P', 'L', 'A', 'N'): + tagId = MKTAG('P', 'L', 'A', 'C'); + break; + case MKTAG('F', 'A', 'U', 'N'): + case MKTAG('F', 'I', 'S', 'H'): + case MKTAG('F', 'L', 'O', 'R'): + tagId = MKTAG('N', 'A', 'T', 'R'); + break; + case MKTAG('H', 'H', 'L', 'D'): + case MKTAG('T', 'O', 'Y', 'S'): + case MKTAG('W', 'E', 'A', 'P'): + tagId = MKTAG('M', 'A', 'C', 'H'); + break; + case MKTAG('M', 'L', 'T', 'Y'): + case MKTAG('P', 'G', 'R', 'P'): + case MKTAG('P', 'T', 'I', 'C'): + tagId = MKTAG('G', 'R', 'U', 'P'); + break; + case MKTAG('P', 'K', 'U', 'P'): + case MKTAG('S', 'E', 'X', '1'): + case MKTAG('S', 'W', 'E', 'R'): + tagId = MKTAG('R', 'U', 'D', 'E'); + break; + case MKTAG('P', 'H', 'I', 'L'): + case MKTAG('R', 'C', 'K', 'T'): + tagId = MKTAG('S', 'C', 'I', 'E'); + break; + case MKTAG('T', 'R', 'A', '2'): + case MKTAG('T', 'R', 'A', '3'): + tagId = MKTAG('T', 'R', 'A', 'V'); + break; + default: + break; + } + + return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); +} + +int DoorbotScript::updateState(uint oldId, uint newId, int index) { + getValue(38); + bool flag39 = getValue(39) != 0; + CTrueTalkManager::setFlags(38, 0); + CTrueTalkManager::setFlags(39, 0); + + if (newId > 220890) { + switch (newId) { + case 221064: + return getValue(1) == 2 ? newId : 221062; + case 221080: + return getValue(1) >= 2 ? newId : 221066; + case 221078: + case 221079: + return getValue(1) >= 3 ? newId : 221065; + case 221081: + return getValue(7) == 0 ? newId : 221070; + case 221251: + CTrueTalkManager::triggerAction(28, 0); + break; + default: + break; + } + } else if (newId >= 220883) { + CTrueTalkManager::setFlags(38, 1); + CTrueTalkManager::triggerAction(28, 0); + } else if (newId >= 220076) { + switch (newId) { + case 220078: + case 220080: + case 220081: + case 220082: + case 220083: + case 220084: + if (flag39) + return getRangeValue(221381); + break; + default: + break; + } + + CTrueTalkManager::setFlags(39, 1); + } else if (newId == 220075) { + if (flag39) + return getRangeValue(221381); + CTrueTalkManager::setFlags(39, 1); + } else if (newId == 220038) { + return 220038; + } + + for (uint idx = 0; idx < _states.size(); ++idx) { + TTupdateState &us = _states[idx]; + if (us._newId == newId) { + uint bits = us._dialBits; + + if (!bits + || (index == 0 && (bits == 5 || bits == 1)) + || (index == 1 && (bits == 6 || bits == 2)) + || (index == 2 && (bits == 9 || bits == 1)) + || (index == 3 && (bits == 10 || bits == 2))) { + setState(us._newValue); + break; + } + } + } + + return newId; +} + +int DoorbotScript::preResponse(uint id) { + uint newId = 0; + if (getDialRegion(0) != 1 && getRandomNumber(100) > 60) { + addResponse(11195); + newId = 222193; + } + + return newId; +} + +uint DoorbotScript::getDialsBitset() const { + uint bits = 0; + if (!getDialRegion(1)) + bits = 1; + if (!getDialRegion(0)) + bits |= 2; + + return bits; +} + +int DoorbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + int id2, id = 0; + + switch (val1) { + case 2: + if (getValue(1) != 1) + return 1; + break; + case 3: + if (getValue(1) != 2) + return 1; + break; + case 4: + if (getValue(1) != 3) + return 1; + break; + case 5: + if (getValue(1) == 3) + return 1; + case 6: + if (getRoom54(132)) + return 1; + break; + case 9: + if (sentence->localWord("my") || sentence->contains("my")) + return true; + id2 = getRoomDialogueId1(roomScript); + if (id2) { + addResponse(id2); + applyResponse(); + return 2; + } + break; + case 11: + switch (getValue(1)) { + case 1: + id = 220837; + break; + case 2: + id = 220849; + break; + default: + id = 220858; + break; + } + break; + case 12: + if (getValue(4) != 1) + id = 221157; + break; + case 13: + if (getValue(4) != 2) + id = 221157; + break; + case 14: + if (getValue(4) != 3) + id = 221157; + break; + case 15: + if (getValue(4) != 0) + id = 221157; + break; + case 16: + if (!sentence->localWord("weather")) + return true; + switch (getRandomNumber(4)) { + case 1: + if (getValue(4) != 0) + id = 221354 - getRandomNumber(2) ? -489 : 0; + break; + case 2: + switch (getValue(4)) { + case 0: + id = 220851; + break; + case 1: + id = 221268; + break; + case 2: + id = 221270; + break; + default: + id = 220865; + } + break; + case 3: + id = 221280; + break; + default: + break; + } + break; + case 17: + if (getState()) + return 1; + setState(0); + break; + case 18: + if (roomScript->_scriptId == 100) { + CTrueTalkManager::triggerAction(3, 0); + return 2; + } + break; + case 19: + CTrueTalkManager::_v9 = 104; + CTrueTalkManager::triggerAction(4, 0); + break; + case 20: + CTrueTalkManager::triggerAction(28, 0); + break; + case 22: + CTrueTalkManager::triggerAction(29, 1); + break; + case 23: + CTrueTalkManager::triggerAction(29, 2); + break; + case 24: + CTrueTalkManager::triggerAction(29, 3); + break; + case 25: + CTrueTalkManager::triggerAction(29, 4); + break; + case 26: + if (!sentence->localWord("my") && !sentence->contains("my")) + return 1; + break; + case 27: + if (!sentence->localWord("earth") && !sentence->contains("earth")) + return 1; + break; + case 28: + id2 = getRoomDialogueId2(roomScript); + if (id2) { + addResponse(id2); + applyResponse(); + return 2; + } + break; + case 29: + if (sentence->localWord("another") || sentence->localWord("more") || + sentence->localWord("additional") || sentence->contains("another") || + sentence->contains("more") || sentence->contains("additional")) { + addResponse(getDialogueId(220058)); + applyResponse(); + return 2; + } + break; + case 30: + if (!sentence->localWord("because") && !sentence->contains("because")) + return 1; + break; + case 0x200: + if (getValue(4) != 1) + id = 221157; + break; + case 0x201: + if (getValue(4) != 2) + id = 221157; + break; + case 0x202: + if (getValue(4) != 3) + id = 221157; + break; + case 0x203: + if (getValue(4) != 0) + id = 221157; + break; + default: + break; + } + + if (id) { + addResponse(getDialogueId(id)); + applyResponse(); + return 2; + } else { + return 0; + } +} + +void DoorbotScript::setDialRegion(int dialNum, int region) { + TTnpcScript::setDialRegion(dialNum, region); + if (dialNum == 1 && region != 1) { + CTrueTalkManager::setFlags(37, dialNum); + } else { + addResponse(getDialogueId(221777)); + applyResponse(); + } +} + +bool DoorbotScript::randomResponse(uint index) { + static const int DIALOGUE_IDS[] = { + 220133, 220074, 220000, 220008, 220009, 220010, 220011, + 220012, 220013, 220014, 220015, 220016, 221053, 221054, + 221055, 221056, 221057, 221058, 221059, 221060, 221061, + 221173, 221174, 221175, 221176, 221177, 222415, 222416, + 221157, 221165, 221166, 221167, 221168, 221169, 221170, + 221171, 221172, 221158, 221159, 221356, 221364, 221365, + 221366, 221367, 221368, 221369, 221370, 221371, 221357, + 221358, 221359, 221360, 221252, 221019, 221355, 220952, + 220996, 220916, 220924, 220926, 220931, 220948, 220956, + 220965, 220967, 220968, 220980, 220981, 220982, 220983, + 220984, 220988, 220903, 221095, 222202, 222239, 221758, + 221759, 221762, 221763, 221766, 221767, 221768, 0 + }; + + int *dataP = _data.getSlot(index); + bool flag = false; + for (const int *idP = DIALOGUE_IDS; *idP && !flag; ++idP) { + flag = *idP == *dataP; + } + + if (flag || (getDialRegion(1) != 1 && getRandomNumber(100) > 33) + || getRandomNumber(8) <= index) + return false; + + if (getRandomNumber(100) > 40) { + deleteResponses(); + addResponse(getDialogueId(221242)); + applyResponse(); + } else { + setResponseFromArray(index, 221245); + } + + return true; +} + +int DoorbotScript::setResponse(int dialogueId, int v34) { + addResponse(dialogueId); + applyResponse(); + + if (v34 != -1) + setState(v34); + return 2; +} + +int DoorbotScript::getRoomDialogueId1(const TTroomScript *roomScript) { + for (const RoomDialogueId *r = ROOM_DIALOGUES1; r->_roomNum; ++r) { + if (r->_roomNum == roomScript->_scriptId) + return getDialogueId(r->_dialogueId); + } + + return 0; +} + +int DoorbotScript::getRoomDialogueId2(const TTroomScript *roomScript) { + for (const RoomDialogueId *r = ROOM_DIALOGUES2; r->_roomNum; ++r) { + if (r->_roomNum == roomScript->_scriptId) + return getDialogueId(r->_dialogueId); + } + + return 0; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/doorbot_script.h b/engines/titanic/true_talk/doorbot_script.h new file mode 100644 index 0000000000..78ebcbfd68 --- /dev/null +++ b/engines/titanic/true_talk/doorbot_script.h @@ -0,0 +1,113 @@ +/* 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. + * + */ + +#ifndef TITANIC_DOORBOT_SCRIPT_H +#define TITANIC_DOORBOT_SCRIPT_H + +#include "common/hashmap.h" +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class DoorbotScript : public TTnpcScript { + typedef Common::HashMap<uint, TTsentenceEntries> SentenceEntriesMap; +private: + TTupdateStateArray _states; + SentenceEntriesMap _sentences; + int _stateIndex; + int _doorbotState; +private: + /** + * Setup sentence data + */ + void setupSentences(); + + /** + * Sets a response + */ + int setResponse(int dialogueId, int v34 = -1); + + /** + * Gets the dialogue Id for a given room + */ + int getRoomDialogueId1(const TTroomScript *roomScript); + + /** + * Gets the dialogue Id for a given room + */ + int getRoomDialogueId2(const TTroomScript *roomScript); +public: + DoorbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); + + /** + * Returns a bitset of the dials being off or not + */ + virtual uint getDialsBitset() const; + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Sets a given dial to be pointing in a specified region (0 to 2) + */ + virtual void setDialRegion(int dialNum, int region); + + /** + * Handles a randomzied response + */ + virtual bool randomResponse(uint index); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_DOORBOT_SCRIPT_H */ diff --git a/engines/titanic/true_talk/liftbot_script.cpp b/engines/titanic/true_talk/liftbot_script.cpp new file mode 100644 index 0000000000..ab995b71b9 --- /dev/null +++ b/engines/titanic/true_talk/liftbot_script.cpp @@ -0,0 +1,695 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/liftbot_script.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/titanic.h" + +namespace Titanic { + +int LiftbotScript::_stateIndex; + +static const int STATE_ARRAY[7] = { + 0x78BE, 0x78C0, 0x78C1, 0x78C2, 0x78C3, 0x78C4, 0x78C5 +}; + +LiftbotScript::LiftbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, v4, v5, v6, v7) { + _stateIndex = 0; + + loadRanges("Ranges/Liftbot"); + loadResponses("Responses/Liftbot"); + setupSentences(); + _tagMappings.load("TagMap/Liftbot"); + _words.load("Words/Liftbot"); + _quotes.load("Quotes/Liftbot"); + _states.load("States/Liftbot"); +} + +void LiftbotScript::setupSentences() { + CTrueTalkManager::setFlags(27, 0); + setupDials(getRandomNumber(40) + 60, getRandomNumber(40) + 60, 0); + + _mappings.load("Mappings/Liftbot", 4); + _entries.load("Sentences/Liftbot"); + _field68 = 0; + _entryCount = 0; +} + +int LiftbotScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + switch (tag) { + case MKTAG('D', 'N', 'A', '1'): + case MKTAG('H', 'H', 'G', 'Q'): + case MKTAG('A', 'N', 'S', 'W'): + if (_stateIndex >= 7) { + selectResponse(30918); + setState(2); + _stateIndex = 0; + } else { + addResponse(STATE_ARRAY[_stateIndex++]); + } + + applyResponse(); + return 2; + + case MKTAG('O', 'R', 'D', '8'): + addResponse(30475); + addResponse(30467); + addResponse(30466); + addResponse(30474); + applyResponse(); + return SS_2; + + default: + return TTnpcScript::chooseResponse(roomScript, sentence, tag); + } +} + +int LiftbotScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + if (roomScript->_scriptId != 103) + return 2; + + checkItems(roomScript, sentence); + int currState = getState(); + int sentMode = sentence->_field2C; + TTtreeResult treeResult; + + if (currState) { + setState(0); + bool flag1 = sentMode == 11 || sentMode == 13; + bool flag2 = sentMode == 12; + + switch (currState) { + case 2: + if (flag1) + return addDialogueAndState(30920, 3); + if (flag2) + return addDialogueAndState(30919, 1); + break; + + case 3: + if (flag1) + return addDialogueAndState(30919, 1); + break; + + case 4: + return addDialogueAndState(210391, 1); + + case 5: + if (sentence->contains("reborzo") || sentence->contains("is that")) + return addDialogueAndState(30515, 1); + break; + + case 6: + if (sentMode == 6) + return addDialogueAndState(getDialogueId(210771), 1); + break; + + case 7: + case 8: + if (sentMode == 6 || sentMode == 10) + return addDialogueAndState(getDialogueId(210099), 1); + break; + + case 9: + if (sentMode == 10 || g_vm->_trueTalkManager->_quotesTree.search( + sentence->_normalizedLine.c_str(), TREE_2, &treeResult, 0, 0) != -1) + return addDialogueAndState(getDialogueId(210970), 9); + break; + + default: + break; + } + } + + updateCurrentDial(true); + if (processEntries(&_entries, _entryCount, roomScript, sentence) == 2) + return 2; + + if (sentence->localWord("injury") || sentence->localWord("illness")) { + addResponse(getDialogueId(210059)); + applyResponse(); + } else if (processEntries(_defaultEntries, 0, roomScript, sentence) != 2 + && !defaultProcess(roomScript, sentence) + && !sentence1(sentence)) { + if (getDialRegion(1) != 0 && getRandomNumber(100) <= 20) { + addResponse(getDialogueId(210906)); + addResponse(getDialogueId(210901)); + } else { + addResponse(getDialogueId(210590)); + } + applyResponse(); + } + + return 2; +} + +ScriptChangedResult LiftbotScript::scriptChanged(uint id) { + return scriptChanged(nullptr, id); +} + +ScriptChangedResult LiftbotScript::scriptChanged(const TTroomScript *roomScript, uint id) { + switch (id) { + case 3: + if (getValue(27) == 0) { + addResponse(getDialogueId(210018)); + } else if (getStateValue()) { + addResponse(getDialogueId(210682)); + } else { + addResponse(getDialogueId(210033)); + } + CTrueTalkManager::setFlags(27, 1); + break; + + case 155: + selectResponse(30446); + applyResponse(); + break; + + case 156: + if (getCurrentFloor() == 1) { + addResponse(getDialogueId(210614)); + } else { + selectResponse(30270); + } + applyResponse(); + break; + + default: + break; + } + + if (id >= 210000 && id <= 211001) { + addResponse(getDialogueId(id)); + applyResponse(); + } + + return SCR_2; +} + +int LiftbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + switch (tagId) { + case MKTAG('A', 'D', 'V', 'T'): + case MKTAG('A', 'R', 'T', 'I'): + case MKTAG('A', 'R', 'T', 'Y'): + case MKTAG('B', 'R', 'N', 'D'): + case MKTAG('C', 'O', 'M', 'D'): + case MKTAG('D', 'N', 'C', 'E'): + case MKTAG('H', 'B', 'B', 'Y'): + case MKTAG('L', 'I', 'T', 'R'): + case MKTAG('M', 'A', 'G', 'S'): + case MKTAG('M', 'C', 'P', 'Y'): + case MKTAG('M', 'I', 'N', 'S'): + case MKTAG('M', 'U', 'S', 'I'): + case MKTAG('N', 'I', 'K', 'E'): + case MKTAG('S', 'F', 'S', 'F'): + case MKTAG('S', 'O', 'A', 'P'): + case MKTAG('S', 'O', 'N', 'G'): + case MKTAG('S', 'P', 'R', 'T'): + case MKTAG('T', 'E', 'A', 'M'): + case MKTAG('T', 'V', 'S', 'H'): + case MKTAG('W', 'W', 'E', 'B'): + tagId = MKTAG('E', 'N', 'T', 'N'); + break; + case MKTAG('A', 'C', 'T', 'R'): + case MKTAG('A', 'C', 'T', 'S'): + case MKTAG('A', 'U', 'T', 'H'): + case MKTAG('B', 'A', 'R', 'K'): + case MKTAG('B', 'A', 'R', 'U'): + case MKTAG('B', 'L', 'F', '1'): + case MKTAG('B', 'L', 'F', '2'): + case MKTAG('B', 'L', 'R', '1'): + case MKTAG('B', 'L', 'R', '2'): + case MKTAG('B', 'L', 'P', '1'): + case MKTAG('B', 'L', 'P', '2'): + case MKTAG('B', 'L', 'P', '3'): + case MKTAG('B', 'L', 'P', '4'): + case MKTAG('B', 'L', 'T', '1'): + case MKTAG('B', 'L', 'T', '2'): + case MKTAG('B', 'L', 'T', '3'): + case MKTAG('B', 'L', 'T', '4'): + case MKTAG('B', 'L', 'T', '5'): + case MKTAG('B', 'O', 'Y', 'S'): + case MKTAG('C', 'O', 'P', 'S'): + case MKTAG('D', 'C', 'T', 'R'): + case MKTAG('F', 'A', 'M', 'E'): + case MKTAG('F', 'A', 'S', 'H'): + case MKTAG('G', 'I', 'R', 'L'): + case MKTAG('H', 'E', 'R', 'O'): + case MKTAG('H', 'O', 'S', 'T'): + case MKTAG('K', 'N', 'O', 'B'): + case MKTAG('N', 'H', 'R', 'O'): + case MKTAG('R', 'A', 'C', 'E'): + case MKTAG('S', 'C', 'I', 'T'): + case MKTAG('T', 'D', 'V', 'P'): + case MKTAG('T', 'W', 'A', 'T'): + case MKTAG('W', 'E', 'A', 'T'): + tagId = MKTAG('P', 'R', 'S', 'N'); + break; + case MKTAG('C', 'H', 'S', 'E'): + case MKTAG('C', 'M', 'N', 'T'): + case MKTAG('F', 'I', 'L', 'M'): + case MKTAG('J', 'F', 'O', 'D'): + case MKTAG('L', 'I', 'Q', 'D'): + tagId = MKTAG('F', 'O', 'O', 'D'); + break; + case MKTAG('C', 'R', 'I', 'M'): + case MKTAG('C', 'S', 'P', 'Y'): + case MKTAG('D', 'R', 'U', 'G'): + tagId = MKTAG('V', 'B', 'A', 'D'); + break; + case MKTAG('E', 'A', 'R', 'T'): + case MKTAG('H', 'O', 'M', 'E'): + case MKTAG('N', 'P', 'L', 'C'): + case MKTAG('P', 'L', 'A', 'N'): + tagId = MKTAG('P', 'L', 'A', 'C'); + break; + case MKTAG('F', 'A', 'U', 'N'): + case MKTAG('F', 'I', 'S', 'H'): + case MKTAG('F', 'L', 'O', 'R'): + tagId = MKTAG('N', 'A', 'T', 'R'); + break; + case MKTAG('H', 'H', 'L', 'D'): + case MKTAG('T', 'O', 'Y', 'S'): + case MKTAG('W', 'E', 'A', 'P'): + tagId = MKTAG('M', 'A', 'C', 'H'); + break; + case MKTAG('M', 'L', 'T', 'Y'): + case MKTAG('P', 'G', 'R', 'P'): + case MKTAG('P', 'T', 'I', 'C'): + tagId = MKTAG('G', 'R', 'U', 'P'); + break; + case MKTAG('P', 'K', 'U', 'P'): + case MKTAG('S', 'E', 'X', '1'): + case MKTAG('S', 'W', 'E', 'R'): + tagId = MKTAG('R', 'U', 'D', 'E'); + break; + case MKTAG('P', 'H', 'I', 'L'): + case MKTAG('R', 'C', 'K', 'T'): + tagId = MKTAG('S', 'C', 'I', 'E'); + break; + case MKTAG('T', 'R', 'A', '2'): + case MKTAG('T', 'R', 'A', '3'): + tagId = MKTAG('T', 'R', 'A', 'V'); + break; + } + + return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); +} + +int LiftbotScript::updateState(uint oldId, uint newId, int index) { + for (uint idx = 0; idx < _states.size(); ++idx) { + TTmapEntry &us = _states[idx]; + if (us._src == newId) { + setState(us._dest); + break; + } + } + + return newId; +} + +int LiftbotScript::preResponse(uint id) { + if (id == 30565 || id == 30566 || id == 30567 || id == 30568 + || id == 30569 || id == 30570 || id == 30571) + return 210901; + + if (getDialRegion(0) == 0 && getRandomNumber(100) > 60) + return 210830; + + return 0; +} + +uint LiftbotScript::getDialsBitset() const { + uint bits = 0; + if (!getDialRegion(1)) + bits = 1; + if (!getDialRegion(0)) + bits |= 2; + if (bits > 1) + bits ^= 1; + + return bits; +} + + +int LiftbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + static const int ARRAY13[] = { + 210724, 210735, 210746, 210757, 210758, 210759, 210760, + 210761, 210762, 210725, 210726, 210727, 210728, 210729, + 210730, 210731, 210732, 210733, 210734, 210736, 210737, + 210738, 210739, 210740, 210741, 210742, 210743, 210744, + 210745, 210747, 210748, 210749, 210750, 210751, 210752, + 210753, 210754, 210755, 210756 + }; + static const int ARRAY14[] = { + 0, 210849, 210850, 210851, 210852, 210838, 210839, 210840, 210841, 0 + }; + + getState(); + int stateVal; + + switch (val1) { + case 1: + if (getValue(1) != 1) + return 1; + break; + case 2: + if (getValue(1) != 2) + return 1; + break; + case 3: + if (getValue(1) != 3) + return 1; + break; + case 4: + case 5: + return !sentence1(sentence); + case 6: + if (sentence->localWord("big") || sentence->localWord("small")) { + addResponse(getDialogueId(210215)); + applyResponse(); + } else if (sentence->localWord("my") || sentence->contains("my") || + sentence->contains("bedroom") || sentence->contains("state")) { + addResponse1(CTrueTalkManager::getStateValue(4), true, 0); + } else { + selectResponse(210763); + applyResponse(); + } + return 2; + case 7: + if (!sentence->localWord("ill") && !sentence->localWord("well")) + return 1; + break; + case 8: + if (!sentence->localWord("long")) + return 1; + break; + case 9: + if (addResponse1(1, false, 0)) + return 2; + break; + case 10: + if (addResponse1(39, false, 0)) + return 2; + break; + case 11: + if (getState6() == 2 || getState6() == 4) + return 1; + break; + case 12: + if (getState6() == 1 || getState6() == 3) + return 1; + break; + case 13: + selectResponse(ARRAY13[getCurrentFloor()]); + applyResponse(); + return 2; + case 14: + stateVal = getState6(); + if (sentence->contains("elevator") || + (!sentence->contains("lift") && getRandomNumber(100) > 60)) + stateVal += 4; + selectResponse(ARRAY14[stateVal]); + applyResponse(); + return 2; + case 15: + if (getRandomNumber(100) > 60) { + addResponse(getDialogueId(210440)); + } else { + addResponse(getDialogueId(210906)); + addResponse(getDialogueId(210901)); + } + applyResponse(); + return 2; + case 16: + if (sentence->contains("elevator") || sentence->contains("elavator")) + addResponse(30579); + else + addResponse(30580); + applyResponse(); + return 2; + case 17: + if (sentence->localWord("restaurant") || sentence->contains("restaurant")) + return 1; + break; + default: + break; + } + + return 0; +} + +void LiftbotScript::setDialRegion(int dialNum, int region) { + TTnpcScript::setDialRegion(dialNum, region); + addResponse(getDialogueId(210688)); + applyResponse(); +} + +int LiftbotScript::getCurrentFloor() const { + int val = CTrueTalkManager::getStateValue(5); + return CLIP(val, 1, 39); +} + +int LiftbotScript::getState6() const { + int val = CTrueTalkManager::getStateValue(6); + return (val < 1 || val > 4) ? 1 : val; +} + +int LiftbotScript::addDialogueAndState(int id, int state) { + addResponse(id); + applyResponse(); + + if (state != 1) + setState(state); + return 2; +} + +int LiftbotScript::addResponse1(int index, bool flag, int id) { + static const int DIALOGUE_IDS[37] = { + 210735, 210746, 210757, 210758, 210759, 210760, 210761, 210762, + 210725, 210726, 210727, 210728, 210729, 210730, 210731, 210732, + 210733, 210734, 210736, 210737, 210738, 210739, 210740, 210741, + 210742, 210743, 210744, 210745, 210747, 210748, 210749, 210750, + 210751, 210752, 210753, 210754, 210755 + }; + + int stateVal = getState6(); + int maxIndex = (stateVal == 2 || stateVal == 4) ? 27 : 39; + + if (index < 1 || index > maxIndex) { + addResponse(getDialogueId(maxIndex == 27 ? 210587 : 210586)); + applyResponse(); + return 1; + } else if (index == getCurrentFloor()) { + if (index == 1) { + addResponse(30558 - getRandomBit() ? 290 : 0); + addResponse(getDialogueId(210589)); + } else { + if (index == 39) + addResponse(30346); + addResponse(getDialogueId(210589)); + } + + applyResponse(); + return 2; + } + + stateVal = getValue(1); + if (index >= 2 && index <= 19 && stateVal > 1) { + addResponse(getDialogueId(210203)); + applyResponse(); + setState(7); + return true; + } + + if (index >= 20 && index <= 27 && stateVal > 2) { + addResponse(getDialogueId(210210)); + applyResponse(); + setState(8); + return true; + } + + if (flag) { + if (index == 1) { + selectResponse(30558 - getRandomBit() ? 290 : 0); + } else if (index == 39) { + addResponse(30346); + } else { + if (getRandomNumber(100) > 35 && index >= 2 && index <= 38) { + addResponse(getDialogueId(DIALOGUE_IDS[index - 2])); + } + + addResponse(getDialogueId(210588)); + } + + if (id) { + if (id == 210717 || id == 210716 || id == 210719 || id == 210718) { + addResponse(getDialogueId(210720)); + addResponse(getDialogueId(id)); + addResponse(getDialogueId(210715)); + } else { + addResponse(getDialogueId(id)); + } + } + + applyResponse(); + } + + CTrueTalkManager::triggerAction(2, index); + return flag; +} + +int LiftbotScript::sentence1(const TTsentence *sentence) { + if (CTrueTalkManager::_v1 >= 0) { + if (sentence->localWord("room")) { + addResponse1(getStateValue(), true, 0); + } else if (CTrueTalkManager::_v1 >= 1 && CTrueTalkManager::_v1 <= 39) { + if (CTrueTalkManager::_v1 != 1 || !sentence->localWord("floor")) { + addResponse1(CTrueTalkManager::_v1, true, 0); + } else if (sentence->localWord("up") || sentence->localWord("above")) { + addResponse1(getCurrentFloor() - 1, true, 0); + } else if (sentence->localWord("down") || sentence->localWord("below")) { + addResponse1(getCurrentFloor() + 1, true, 0); + } else { + addResponse1(CTrueTalkManager::_v1, true, 0); + } + } + return 1; + } + + int classNum = 1; + bool classSet = true; + if (sentence->localWord("firstclass")) + classNum = 1; + else if (sentence->localWord("secondclass")) + classNum = 2; + else if (sentence->localWord("thirdclass")) + classNum = 3; + else + classSet = false; + + uint newId = 0; + int diff = 1; + if (sentence->localWord("promenade")) { + newId = 210718; + } else if (sentence->localWord("bar")) { + newId = 210894 - (getRandomBit() ? 178 : 0); + } else if (sentence->localWord("musicroom")) { + newId = 210897 - (getRandomBit() ? 180 : 0); + } else if (sentence->localWord("creatorroom")) { + newId = 210713; + } else if (sentence->localWord("sculpture") || sentence->localWord("sculptureroom")) { + newId = 210722; + } else if (sentence->localWord("embarklobby")) { + newId = 210714; + } else if (sentence->localWord("parrotlobby")) { + newId = 210721; + } else if (sentence->localWord("arboretum")) { + newId = 210711; + } else if (sentence->localWord("canal")) { + newId = 210896; + } else if (sentence->localWord("bar")) { + newId = 210894; + } else if (sentence->localWord("bilgeroom")) { + newId = 210895; + } else if (sentence->localWord("titaniaroom")) { + newId = 210723; + } else if (sentence->localWord("restaurant")) { + if (classNum == 1) { + newId = 210719; + diff = 1; + } else { + newId = 210898; + diff = -98; + } + } else if (sentence->localWord("topwell") || sentence->localWord("servicelift") + || sentence->localWord("bridge") || sentence->localWord("dome") + || sentence->localWord("pellerator") || sentence->localWord("top")) { + diff = 1; + } else { + diff = -100; + } + + if (sentence->localWord("lobby")) + diff = (getValue(1) == 0 ? 1 : 0) - 99; + if (sentence->localWord("bottomofwell") || sentence->contains("bottom")) + diff = 39; + + if (diff == -99 || (diff == -100 && classSet)) { + if (classNum == 1) + addResponse(getDialogueId(210235)); + else if (classNum == 2) + addResponse(getDialogueId(210241)); + else + addResponse(getDialogueId(210242)); + applyResponse(); + + return 1; + } + + if (sentence->_field2C == 4 || sentence->localWord("find") + || sentence->contains("get to")) { + if (getCurrentFloor() != diff) { + selectResponse(diff == 1 ? 210769 : 210764); + applyResponse(); + } else if (!newId) { + selectResponse(210764); + applyResponse(); + } else if (newId >= 210715 && newId <= 210719) { + selectResponse(newId); + applyResponse(); + } else { + addResponse(getDialogueId(210720)); + selectResponse(210715); + applyResponse(); + } + + return 1; + } + + if (diff == -98) { + addResponse1(getStateValue(), true, newId); + return 1; + } else if (diff >= 0) { + addResponse1(diff, true, newId); + return 1; + } else if (sentence->localWord("up") || sentence->localWord("ascend")) { + selectResponse(210128); + applyResponse(); + return 1; + } else if (sentence->localWord("down") || sentence->localWord("descend")) { + selectResponse(210138); + applyResponse(); + return 1; + } else if (diff >= 0) { + addResponse1(diff, true, newId); + return 1; + } else { + return 0; + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/liftbot_script.h b/engines/titanic/true_talk/liftbot_script.h new file mode 100644 index 0000000000..0a9cdfd6f0 --- /dev/null +++ b/engines/titanic/true_talk/liftbot_script.h @@ -0,0 +1,109 @@ +/* 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. + * + */ + +#ifndef TITANIC_LIFTBOT_SCRIPT_H +#define TITANIC_LIFTBOT_SCRIPT_H + +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class LiftbotScript : public TTnpcScript { +private: + TTmapEntryArray _states; + static int _stateIndex; +private: + /** + * Setup sentence data + */ + void setupSentences(); + + int addResponse1(int mode, bool flag, int id); + int sentence1(const TTsentence *sentence); + + /** + * Gets the current floor + */ + int getCurrentFloor() const; + + int getState6() const; + + /** + * Adds a dialogue response and sets the state + */ + int addDialogueAndState(int id, int state); +public: + LiftbotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(uint id); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); + + /** + * Returns a bitset of the dials being off or not + */ + virtual uint getDialsBitset() const; + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Sets a given dial to be pointing in a specified region (0 to 2) + */ + virtual void setDialRegion(int dialNum, int region); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_LIFTBOT_SCRIPT_H */ diff --git a/engines/titanic/true_talk/maitred_script.cpp b/engines/titanic/true_talk/maitred_script.cpp new file mode 100644 index 0000000000..e0636d045f --- /dev/null +++ b/engines/titanic/true_talk/maitred_script.cpp @@ -0,0 +1,1057 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/maitred_script.h" +#include "titanic/true_talk/true_talk_manager.h" + +namespace Titanic { + +MaitreDScript::MaitreDScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, -1, -1, -1, 0) { + _answerCtr = 0; + + CTrueTalkManager::setFlags(9, 1); + CTrueTalkManager::setFlags(10, 0); + CTrueTalkManager::setFlags(11, 0); + CTrueTalkManager::setFlags(12, 0); + CTrueTalkManager::setFlags(13, 0); + CTrueTalkManager::setFlags(14, 0); + CTrueTalkManager::setFlags(15, 0); + CTrueTalkManager::setFlags(16, 0); + + loadRanges("Ranges/MaitreD"); + loadResponses("Responses/MaitreD"); + setupSentences(); + _tagMappings.load("TagMap/MaitreD"); + _quotes.load("Quotes/MaitreD"); + _states.load("States/MaitreD"); +} + +void MaitreDScript::setupSentences() { + _mappings.load("Mappings/MaitreD", 1); + _entries.load("Sentences/MaitreD"); + _sentences1.load("Sentences/MaitreD/1"); + _field68 = 0; + _entryCount = 0; +} + +int MaitreDScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + if (tag == MKTAG('F', 'O', 'O', 'D') || tag == MKTAG('F', 'I', 'S', 'H') || + tag == MKTAG('C', 'H', 'S', 'E')) { + addResponse(getDialogueId(260388)); + addResponse(getDialogueId(260659)); + applyResponse(); + return 2; + } + + return TTnpcScript::chooseResponse(roomScript, sentence, tag); +} + +int MaitreDScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + if (roomScript->_scriptId != 132) + return 2; + if (preprocess(roomScript, sentence) == 1) + return 1; + + CTrueTalkManager::setFlags(10, 0); + setState(0); + + if (getValue(12) == 0) { + trigger12(false); + _answerCtr = 0; + + if (sentence->contains("restaurant at the end of the universe") + || sentence->contains("milliway") + || sentence->contains("big bang burger bar")) { + addResponse(getDialogueId(260975)); + applyResponse(); + } else if (processEntries(&_entries, _entryCount, roomScript, sentence) == 2) { + // Do nothing further + } else if (sentence->localWord("menu")) { + addResponse(getDialogueId(260683)); + applyResponse(); + } else if (sentence->localWord("table") && sentence->localWord("other")) { + addResponse(getDialogueId(260091)); + applyResponse(); + } else if ((sentence->localWord("not") && sentence->localWord("busy")) + || (sentence->localWord("no") && sentence->localWord("people")) + || sentence->localWord("empty")) { + addResponse(getDialogueId(260087)); + applyResponse(); + } else if (!defaultProcess(roomScript, sentence) + && processEntries(&_sentences1, 0, roomScript, sentence) != 2 + && processEntries(_defaultEntries, 0, roomScript, sentence) != 2) { + addResponse(getDialogueId(260975)); + applyResponse(); + } + + return 2; + } + + if (++_answerCtr > 50 || sentence->localWord("stop") || sentence->localWord("enough") + || sentence->contains("i give up") || sentence->contains("i give in") + || sentence->contains("i surrender") || sentence->contains("i submit")) { + _answerCtr = 0; + trigger12(false); + addResponse(getDialogueId(260063)); + } else if (sentence->localWord("not") && sentence->localWord("fight") && + (sentence->localWord("feel") || sentence->localWord("want") + || sentence->localWord("do") || sentence->localWord("will"))) { + _answerCtr = 0; + trigger12(false); + addResponse(getDialogueId(260678)); + } else if (sentence->contains("touche") || sentence->contains("toushe")) { + addResponse(getDialogueId(260098)); + } else if (sentence->contains("have at you")) { + addResponse(getDialogueId(260047)); + } else if (sentence->contains("en garde") || sentence->contains("on guard")) { + addResponse(getDialogueId(260008)); + } else if ((sentence->localWord("surrender") && !sentence->contains("i surrender")) + || (sentence->contains("give up") && !sentence->contains("i give up")) + || (sentence->contains("give in") && !sentence->contains("i give in")) + || (sentence->contains("submit") && !sentence->contains("i submit"))) { + addResponse(getDialogueId(260086)); + } else { + addResponse(getDialogueId(260031)); + } + + applyResponse(); + return 2; +} + +ScriptChangedResult MaitreDScript::scriptChanged(const TTroomScript *roomScript, uint id) { + resetFlags(); + bool flag1 = false, flag2 = false; + + switch (id) { + case 3: + if (getValue(4)) + addResponse(getDialogueId(260655)); + else if (getValue(12)) + addResponse(getDialogueId(260622)); + else if (getValue(9) && getValue(16)) + addResponse(getDialogueId(getValue(16))); + else if (getValue(15)) + addResponse(getDialogueId(260649)); + else + addResponse(getDialogueId(260112)); + + CTrueTalkManager::setFlags(16, 0); + CTrueTalkManager::setFlags(15, 1); + applyResponse(); + break; + + case 110: + addResponse(getDialogueId(260118)); + applyResponse(); + trigger12(true); + CTrueTalkManager::setFlags(8, 1); + CTrueTalkManager::setFlags(9, 1); + break; + + case 111: + CTrueTalkManager::setFlags(16, 260680); + CTrueTalkManager::setFlags(8, 0); + CTrueTalkManager::setFlags(9, 1); + break; + + case 112: + addResponse(getDialogueId(getValue(8) ? 260095 : 260127)); + applyResponse(); + break; + + case 113: + CTrueTalkManager::setFlags(16, 260266); + CTrueTalkManager::setFlags(8, 0); + CTrueTalkManager::setFlags(9, 1); + break; + + case 114: + CTrueTalkManager::setFlags(16, 260267); + CTrueTalkManager::setFlags(8, 0); + CTrueTalkManager::setFlags(9, 1); + break; + + case 115: + CTrueTalkManager::setFlags(16, 260268); + CTrueTalkManager::setFlags(8, 0); + CTrueTalkManager::setFlags(9, 1); + break; + + case 116: + CTrueTalkManager::setFlags(8, 0); + CTrueTalkManager::setFlags(9, 1); + break; + + case 117: + CTrueTalkManager::setFlags(8, 0); + CTrueTalkManager::setFlags(9, 0); + setFlags12(); + break; + + case 132: + addResponse(getDialogueId(260655)); + applyResponse(); + break; + + default: + flag1 = true; + break; + } + + if (!getValue(8)) { + switch (id - 118) { + case 0: + addResponse(getDialogueId(260676)); + applyResponse(); + break; + case 1: + addResponse(getDialogueId(260677)); + applyResponse(); + break; + case 2: + addResponse(getDialogueId(260189)); + applyResponse(); + break; + case 3: + case 4: + case 5: + case 6: + case 7: + CTrueTalkManager::setFlags(13, id - 120); + break; + case 8: + CTrueTalkManager::setFlags(13, 0); + break; + case 9: + if (!getValue(12)) { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260120)); + applyResponse(); + } else if (getRandomNumber(4) == 1) { + addResponse(getDialogueId(260067)); + applyResponse(); + } else { + addResponse(getDialogueId(260131)); + applyResponse(); + } + break; + + case 10: + if (getValue(12) == 0) { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } else if (getRandomNumber(4) == 1) { + addResponse(getDialogueId(260077)); + applyResponse(); + } else { + addResponse(getDialogueId(260131)); + applyResponse(); + } + break; + + case 11: + if (getValue(12)) { + addResponse(getDialogueId(260121)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + case 12: + if (getValue(12)) { + addResponse(getDialogueId(260131)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + case 13: + setFlags12(); + addResponse(getDialogueId(260131)); + applyResponse(); + break; + + case 15: + CTrueTalkManager::setFlags(13, 1); + if (getValue(12)) { + addResponse(getDialogueId(260122)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + case 16: + CTrueTalkManager::setFlags(13, 2); + if (getValue(12)) { + addResponse(getDialogueId(260123)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + case 17: + CTrueTalkManager::setFlags(13, 3); + if (getValue(12)) { + addResponse(getDialogueId(260124)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + case 18: + CTrueTalkManager::setFlags(13, 4); + if (getValue(12)) { + addResponse(getDialogueId(260125)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + case 19: + CTrueTalkManager::setFlags(13, 5); + if (getValue(12)) { + addResponse(getDialogueId(260126)); + applyResponse(); + } else { + addResponse(getDialogueId(getValue(14) == 1 ? 260063 : 260119)); + applyResponse(); + } + break; + + default: + flag2 = true; + break; + } + } + + return !flag1 || !flag2 ? SCR_2 : SCR_0; +} + +int MaitreDScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + switch (tagId) { + case MKTAG('A', 'D', 'V', 'T'): + case MKTAG('A', 'R', 'T', 'I'): + case MKTAG('A', 'R', 'T', 'Y'): + case MKTAG('B', 'R', 'N', 'D'): + case MKTAG('C', 'O', 'M', 'D'): + case MKTAG('D', 'N', 'C', 'E'): + case MKTAG('H', 'B', 'B', 'Y'): + case MKTAG('L', 'I', 'T', 'R'): + case MKTAG('M', 'A', 'G', 'S'): + case MKTAG('M', 'C', 'P', 'Y'): + case MKTAG('M', 'I', 'N', 'S'): + case MKTAG('M', 'U', 'S', 'I'): + case MKTAG('N', 'I', 'K', 'E'): + case MKTAG('S', 'F', 'S', 'F'): + case MKTAG('S', 'O', 'A', 'P'): + case MKTAG('S', 'P', 'R', 'T'): + case MKTAG('S', 'O', 'N', 'G'): + case MKTAG('T', 'E', 'A', 'M'): + case MKTAG('T', 'V', 'S', 'H'): + case MKTAG('W', 'W', 'E', 'B'): + tagId = MKTAG('E', 'N', 'T', 'N'); + break; + case MKTAG('A', 'C', 'T', 'R'): + case MKTAG('A', 'C', 'T', 'S'): + case MKTAG('A', 'U', 'T', 'H'): + case MKTAG('B', 'A', 'R', 'K'): + case MKTAG('B', 'A', 'R', 'U'): + case MKTAG('B', 'L', 'F', '1'): + case MKTAG('B', 'L', 'F', '2'): + case MKTAG('B', 'L', 'P', '1'): + case MKTAG('B', 'L', 'P', '2'): + case MKTAG('B', 'L', 'P', '3'): + case MKTAG('B', 'L', 'P', '4'): + case MKTAG('B', 'L', 'R', '1'): + case MKTAG('B', 'L', 'R', '2'): + case MKTAG('B', 'L', 'T', '1'): + case MKTAG('B', 'L', 'T', '2'): + case MKTAG('B', 'L', 'T', '3'): + case MKTAG('B', 'L', 'T', '4'): + case MKTAG('B', 'L', 'T', '5'): + case MKTAG('C', 'O', 'P', 'S'): + case MKTAG('D', 'C', 'T', 'R'): + case MKTAG('F', 'A', 'M', 'E'): + case MKTAG('F', 'A', 'S', 'H'): + case MKTAG('G', 'I', 'R', 'L'): + case MKTAG('H', 'E', 'R', 'O'): + case MKTAG('H', 'O', 'S', 'T'): + case MKTAG('K', 'N', 'O', 'B'): + case MKTAG('N', 'H', 'R', 'O'): + case MKTAG('R', 'A', 'C', 'E'): + case MKTAG('S', 'C', 'I', 'T'): + case MKTAG('T', 'D', 'V', 'P'): + case MKTAG('T', 'W', 'A', 'T'): + case MKTAG('W', 'E', 'A', 'T'): + tagId = MKTAG('P', 'R', 'S', 'N'); + break; + case MKTAG('C', 'H', 'S', 'E'): + case MKTAG('C', 'M', 'N', 'T'): + case MKTAG('F', 'I', 'L', 'M'): + case MKTAG('L', 'I', 'Q', 'D'): + tagId = MKTAG('F', 'O', 'O', 'D'); + break; + case MKTAG('C', 'R', 'I', 'M'): + case MKTAG('C', 'S', 'P', 'Y'): + case MKTAG('D', 'R', 'U', 'G'): + tagId = MKTAG('V', 'B', 'A', 'D'); + break; + case MKTAG('E', 'A', 'R', 'T'): + case MKTAG('H', 'O', 'M', 'E'): + case MKTAG('N', 'P', 'L', 'C'): + case MKTAG('P', 'L', 'A', 'N'): + tagId = MKTAG('P', 'L', 'A', 'C'); + break; + case MKTAG('F', 'A', 'U', 'N'): + case MKTAG('F', 'I', 'S', 'H'): + case MKTAG('F', 'L', 'O', 'R'): + tagId = MKTAG('N', 'A', 'T', 'R'); + break; + case MKTAG('H', 'H', 'L', 'D'): + case MKTAG('T', 'O', 'Y', 'S'): + case MKTAG('W', 'E', 'A', 'P'): + tagId = MKTAG('M', 'A', 'C', 'H'); + break; + case MKTAG('M', 'L', 'T', 'Y'): + case MKTAG('P', 'G', 'R', 'P'): + case MKTAG('P', 'T', 'I', 'C'): + tagId = MKTAG('G', 'R', 'U', 'P'); + break; + case MKTAG('P', 'K', 'U', 'P'): + case MKTAG('S', 'E', 'X', '1'): + case MKTAG('S', 'W', 'E', 'R'): + tagId = MKTAG('R', 'U', 'D', 'E'); + break; + case MKTAG('P', 'H', 'I', 'L'): + case MKTAG('R', 'C', 'K', 'T'): + tagId = MKTAG('S', 'C', 'I', 'E'); + break; + case MKTAG('T', 'R', 'A', '2'): + case MKTAG('T', 'R', 'A', '3'): + tagId = MKTAG('T', 'R', 'A', 'V'); + break; + + } + + return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); +} + +int MaitreDScript::updateState(uint oldId, uint newId, int index) { + if (getValue(8)) { + if (oldId == 260112) + return getRangeValue(260654); + if (oldId != 260655 && oldId != 260654) + return getRangeValue(260655); + } + + newId = getStateDialogueId(oldId, newId); + + if (newId == 260023) { + switch (getValue(13)) { + case 1: + newId = 260023; break; + case 2: + newId = 260024; break; + case 3: + newId = 260025; break; + case 4: + newId = 260026; break; + case 5: + newId = 260027; break; + default: + newId = 260016; break; + } + } + + if (newId == 260034) { + switch (getValue(13)) { + case 1: + newId = 260034; break; + case 2: + newId = 260035; break; + case 3: + newId = 260036; break; + case 4: + newId = 260037; break; + case 5: + newId = 260038; break; + default: + newId = 260045; break; + } + } + + if (newId == 260070) { + switch (getValue(13)) { + case 1: + newId = 260070; break; + case 2: + newId = 260071; break; + case 3: + newId = 260072; break; + case 4: + newId = 260073; break; + case 5: + newId = 260074; break; + default: + newId = 260110; break; + } + } + + if (newId == 260076 || newId == 260181 || newId == 261010) { + CTrueTalkManager::setFlags(14, 1); + trigger12(true); + setFlags10(newId, index); + return newId; + } + + if (!getValue(12)) { + static const uint FLAG_IDS[] = { + 260080, 260066, 260067, 260062, 260050, 260087, 260090, 260171, 260173, + 260184, 260193, 260202, 260205, 260220, 260221, 260223, 260231, 260232, + 260365, 260373, 260374, 260387, 260421, 260622, 260695, 0 + }; + + for (uint idx = 0; FLAG_IDS[idx]; ++idx) { + if (FLAG_IDS[idx] == newId) { + setFlags12(); + break; + } + } + } + + if (newId == 261018) { + if (getValue(8) == 0 && getValue(9) == 0) { + newId = getRangeValue(260961); + setFlags10(newId, index); + return newId; + } + + if (getValue(8) == 1 && getValue(9) == 1) { + newId = getRangeValue(260655); + setFlags10(newId, index); + return newId; + } + + if (getValue(9) && getValue(16)) { + newId = getValue(16); + setFlags10(newId, index); + return index; + } + + newId = 260989; + } + + setFlags10(newId, index); + return newId; +} + +int MaitreDScript::preResponse(uint id) { + if (id == 60911) + return 260101; + + return 0; +} + +uint MaitreDScript::getStateDialogueId(uint oldId, uint newId) { + if (getValue(8) || getValue(9)) + return newId; + + switch (newId) { + case 260009: + case 260010: + case 260011: + case 260012: + return getRangeValue(260961); + case 260052: + return 260094; + case 260203: + return 260204; + case 260211: + case 260212: + case 260761: + case 260986: + case 260987: + case 260989: + return getRangeValue(260961); + case 260263: + case 260264: + return 260265; + case 260411: + return 260457; + case 260427: + case 260053: + case 260054: + case 260055: + case 260056: + case 260057: + case 260058: + case 260059: + case 260060: + return 260135; + case 260698: + case 260895: + case 260896: + return 260457; + case 260799: + return 260214; + + default: + return newId; + } +} + + +void MaitreDScript::setFlags12() { + int val = getValue(12); + CTrueTalkManager::setFlags(12, 1); + + if (!val) { + CTrueTalkManager::triggerAction(8, 0); + resetRange(260121); + resetRange(260122); + resetRange(260123); + resetRange(260124); + resetRange(260125); + resetRange(260126); + } +} + +void MaitreDScript::setFlags10(uint newId, uint index) { + int val = 28; + for (uint idx = 0; idx < _states.size(); ++idx) { + TTmapEntry &us = _states[idx]; + if (us._src == newId) { + val = us._dest; + break; + } + } + + CTrueTalkManager::setFlags(10, val); +} + +void MaitreDScript::trigger12(bool flag) { + int val = getValue(12); + CTrueTalkManager::setFlags(12, 0); + + if (val) { + CTrueTalkManager::triggerAction(flag ? 10 : 9, 0); + } +} + +int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence *sentence) { + if (!roomScript || !sentence || getValue(8)) + return 1; + + bool stateFlag = true, applyFlag = false; + switch (getValue(10)) { + case 1: + if (!getValue(11) && !getValue(8)) { + addResponse(getDialogueId(260052)); + applyFlag = true; + stateFlag = false; + } + break; + + case 2: + if (sentence->localWord("change") || sentence->localWord("music")) { + addResponse(getDialogueId(200684)); + applyFlag = true; + } + break; + + case 3: + if (sentence->localWord("chance") && (sentence->localWord("another") + || sentence->localWord("other") || sentence->localWord("more"))) { + addResponse(getDialogueId(260106)); + } else { + addResponse(getDialogueId(260107)); + } + applyFlag = true; + break; + + case 4: + if (sentence->contains("unless what")) { + addResponse(getDialogueId(260099)); + } else { + addResponse(getDialogueId(260131)); + applyFlag = true; + stateFlag = false; + } + break; + + case 5: + addResponse(getDialogueId(260096)); + applyFlag = true; + stateFlag = false; + break; + + case 6: + addResponse(getDialogueId(260097)); + applyFlag = true; + stateFlag = false; + break; + + case 7: + if (sentence->_field2C == 12) { + addResponse(getDialogueId(260089)); + applyFlag = true; + stateFlag = false; + } else { + addResponse(getDialogueId(260094)); + applyFlag = true; + CTrueTalkManager::setFlags(11, 1); + } + break; + + case 8: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + trigger12(false); + addResponse(getDialogueId(260094)); + CTrueTalkManager::setFlags(11, 1); + } else { + setFlags12(); + addResponse(getDialogueId(260131)); + } + applyFlag = true; + break; + + case 9: + setFlags12(); + break; + + case 11: + if ((sentence->localWord("say") || sentence->localWord("talk")) || + sentence->localWord("you")) { + addResponse(getDialogueId(260216)); + applyFlag = true; + } + break; + + case 12: + if (sentence->localWord("why") && sentence->localWord("naughty")) { + addResponse(getDialogueId(260196)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("what") && sentence->localWord("his") + && sentence->localWord("name")) { + addResponse(getDialogueId(260197)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("i") && sentence->localWord("meet")) { + addResponse(getDialogueId(260198)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("i") && sentence->localWord("speak")) { + addResponse(getDialogueId(260206)); + applyFlag = true; + stateFlag = false; + } + break; + + case 13: + if (sentence->localWord("why") || sentence->localWord("please") + || sentence->contains("go on") || sentence->localWord("need") + || sentence->contains("got to") || sentence->localWord("must")) { + addResponse(getDialogueId(260199)); + applyFlag = true; + stateFlag = false; + } + break; + + case 14: + if (sentence->localWord("what") || sentence->localWord("why") + || sentence->localWord("kill")) { + addResponse(getDialogueId(260200)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("you") && sentence->localWord("kill")) { + addResponse(getDialogueId(260574)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("how") && sentence->localWord("kill")) { + addResponse(getDialogueId(260557)); + applyFlag = true; + stateFlag = false; + } + break; + + case 15: + if ((sentence->localWord("what") && sentence->localWord("way")) + || sentence->localWord("how")) { + addResponse(getDialogueId(260201)); + applyFlag = true; + stateFlag = false; + } + break; + + case 16: + addResponse(getDialogueId(sentence->_field2C == 11 ? 260209 : 260210)); + applyFlag = true; + stateFlag = false; + break; + + case 17: + if (sentence->localWord("what") && sentence->localWord("mean")) { + addResponse(getDialogueId(260222)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("laugh") && sentence->localWord("with") + && sentence->localWord("you")) { + addResponse(getDialogueId(260221)); + applyFlag = true; + stateFlag = false; + } else { + setFlags12(); + addResponse(getDialogueId(260221)); + applyFlag = true; + stateFlag = false; + } + break; + + case 18: + if (sentence->_field2C == 11) { + addResponse(getDialogueId(260232)); + applyFlag = true; + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(260231)); + applyFlag = true; + } else if (sentence->_field2C == 13) { + addResponse(getDialogueId(260444)); + addResponse(getDialogueId(260233)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("what") && sentence->localWord("happen")) { + addResponse(getDialogueId(260233)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why") && sentence->localWord("stressed")) { + addResponse(getDialogueId(260245)); + addResponse(getDialogueId(260233)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why")) { + addResponse(getDialogueId(260453)); + addResponse(getDialogueId(260233)); + applyFlag = true; + stateFlag = false; + } + break; + + case 19: + if ((sentence->localWord("what") && sentence->localWord("scral")) + || (sentence->localWord("what") && sentence->localWord("happen")) + || sentence->contains("go on") || sentence->contains("and then")) { + addResponse(getDialogueId(260234)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why") && sentence->localWord("stressed")) { + addResponse(getDialogueId(260245)); + addResponse(getDialogueId(260234)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why")) { + addResponse(getDialogueId(260276)); + addResponse(getDialogueId(260237)); + addResponse(getDialogueId(260234)); + applyFlag = true; + stateFlag = false; + } + break; + + case 20: + if ((sentence->localWord("what") && sentence->localWord("leovinus")) + || (sentence->localWord("what") && sentence->localWord("happen"))) { + addResponse(getDialogueId(260235)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("where") && sentence->localWord("leovinus")) { + addResponse(getDialogueId(260236)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why") && sentence->localWord("stressed")) { + addResponse(getDialogueId(260245)); + addResponse(getDialogueId(260235)); + applyFlag = true; + stateFlag = false; + } else { + addResponse(getDialogueId(260237)); + applyFlag = true; + stateFlag = false; + } + break; + + case 21: + case 22: + if (sentence->contains("cooking") + || (sentence->localWord("what") && sentence->localWord("mean"))) { + addResponse(getDialogueId(260238)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("where") && sentence->localWord("now")) { + addResponse(getDialogueId(260236)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why") && sentence->localWord("stressed")) { + addResponse(getDialogueId(260245)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why")) { + addResponse(getDialogueId(260239)); + applyFlag = true; + stateFlag = false; + } + break; + + case 23: + if (sentence->_field2C == 11) { + addResponse(getDialogueId(260237)); + applyFlag = true; + stateFlag = false; + } + break; + + case 24: + if ((sentence->localWord("can") && sentence->localWord("i") + && sentence->localWord("have")) + || (sentence->localWord("give") && sentence->localWord("me")) + || (sentence->localWord("i") && sentence->localWord("want")) + || (sentence->localWord("i") && sentence->localWord("need")) + ) { + addResponse(getDialogueId(260251)); + applyFlag = true; + } + + case 25: + if ((sentence->localWord("open") && sentence->localWord("it")) + || (sentence->localWord("how") && sentence->localWord("open")) + || (sentence->localWord("how") && sentence->localWord("get") && sentence->localWord("in")) + || (sentence->localWord("how") && sentence->localWord("change") && sentence->localWord("music")) + ) { + addResponse(getDialogueId(260253)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("where") && (sentence->localWord("it") + || sentence->localWord("that"))) { + addResponse(getDialogueId(260252)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("where") && sentence->localWord("key")) { + addResponse(getDialogueId(260254)); + applyFlag = true; + stateFlag = false; + } else if ((sentence->localWord("how") && sentence->localWord("work")) + || (sentence->localWord("what") && sentence->localWord("i") && sentence->localWord("do"))) { + addResponse(getDialogueId(260259)); + applyFlag = true; + stateFlag = false; + } + break; + + case 26: + if (sentence->localWord("where") && (sentence->localWord("key") + || sentence->localWord("it"))) { + addResponse(getDialogueId(260254)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("where") && (sentence->localWord("hand") + || sentence->localWord("that"))) { + addResponse(getDialogueId(260256)); + applyFlag = true; + stateFlag = false; + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(260255)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("why") && sentence->localWord("need")) { + addResponse(getDialogueId(260685)); + applyFlag = true; + stateFlag = false; + } + break; + + case 27: + if (sentence->localWord("where") && (sentence->localWord("that") + || sentence->localWord("it"))) { + addResponse(getDialogueId(260262)); + applyFlag = true; + } + break; + + case 28: + if (sentence->localWord("why")) { + addResponse(getDialogueId(260386)); + applyFlag = true; + stateFlag = false; + } else if (sentence->localWord("insist")) { + addResponse(getDialogueId(260387)); + applyFlag = true; + stateFlag = false; + } + break; + + case 29: + if (sentence->_field2C == 11) { + setFlags12(); + addResponse(getDialogueId(260131)); + } else { + addResponse(getDialogueId(260966)); + } + applyFlag = true; + break; + + case 30: + if (sentence->_field2C == 11 || sentence->_field2C == 13) { + addResponse(getDialogueId(260695)); + applyFlag = true; + } else if (sentence->_field2C == 12) { + addResponse(getDialogueId(260696)); + applyFlag = true; + } + break; + } + + if (applyFlag) + applyResponse(); + if (stateFlag) { + setState(0); + CTrueTalkManager::setFlags(10, 0); + } + + return applyFlag ? 2 : 1; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/maitred_script.h b/engines/titanic/true_talk/maitred_script.h new file mode 100644 index 0000000000..0472050d20 --- /dev/null +++ b/engines/titanic/true_talk/maitred_script.h @@ -0,0 +1,101 @@ +/* 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. + * + */ + +#ifndef TITANIC_MAITRED_SCRIPT_H +#define TITANIC_MAITRED_SCRIPT_H + +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class MaitreDScript : public TTnpcScript { +private: + TTmapEntryArray _states; + TTsentenceEntries _sentences1; + int _answerCtr; +private: + /** + * Setup sentence data + */ + void setupSentences(); + + /** + * Alter dialogue Id based on current NPC state + */ + uint getStateDialogueId(uint oldId, uint newId); + + /** + * Sets flags 12 and resets some ranges + */ + void setFlags12(); + + /** + * Sets flags 10 to different values based on the passed + * dialogue Id + */ + void setFlags10(uint newId, uint index); + + /** + * Trigers 12 + */ + void trigger12(bool flag); + + /** + * Does preprocessing for the sentence + */ + int preprocess(const TTroomScript *roomScript, const TTsentence *sentence); +public: + MaitreDScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_MAITRED_SCRIPT_H */ diff --git a/engines/titanic/true_talk/parrot_script.cpp b/engines/titanic/true_talk/parrot_script.cpp new file mode 100644 index 0000000000..b09e74505c --- /dev/null +++ b/engines/titanic/true_talk/parrot_script.cpp @@ -0,0 +1,110 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/parrot_script.h" +#include "titanic/titanic.h" + +namespace Titanic { + +ParrotScript::ParrotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, v4, v5, v6, v7) { + + loadRanges("Ranges/Parrot"); + setupSentences(); +} + +void ParrotScript::setupSentences() { + _mappings.load("Mappings/Parrot", 1); + _entries.load("Sentences/Parrot"); + _field68 = 0; + _entryCount = 0; +} + +int ParrotScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + if (tag == MKTAG('B', 'Y', 'Z', 'A')) { + addResponse(getDialogueId(280246)); + applyResponse(); + return 2; + } else { + return 1; + } +} + +int ParrotScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + if (processEntries(roomScript, sentence) == 2) { + int tagId = g_vm->_trueTalkManager->_quotes.find(sentence->_normalizedLine); + if (!tagId || chooseResponse(roomScript, sentence, tagId) != 2) { + addResponse(getDialogueId(sentence->check2C() ? 280248 : 280235)); + applyResponse(); + } + } + + return 2; +} + +ScriptChangedResult ParrotScript::scriptChanged(const TTroomScript *roomScript, uint id) { + if (id >= 280000 && id <= 280276) { + if (id == 280258) { + if (CTrueTalkManager::_currentNPC) { + CGameObject *chicken; + if (CTrueTalkManager::_currentNPC->find("Chicken", &chicken, FIND_PET)) + id = 280147 - getRandomBit(); + } + + id = getDialogueId(id); + } else { + if ((id == 280146 || id == 280147) && CTrueTalkManager::_currentNPC) { + CGameObject *chicken; + if (CTrueTalkManager::_currentNPC->find("Chicken", &chicken, FIND_PET)) + id = 280142; + } + + addResponse(getDialogueId(id)); + if (id == 280192) + addResponse(getDialogueId(280222)); + applyResponse(); + } + } + + if (id >= 80000 && id <= 80244) { + if ((id == 80155 || id == 80156) && CTrueTalkManager::_currentNPC) { + CGameObject *chicken; + if (CTrueTalkManager::_currentNPC->find("Chicken", &chicken, FIND_PET)) + id = 80151; + } + + addResponse(id); + if (id == 80201) + addResponse(getDialogueId(280222)); + applyResponse(); + } + + return (id == 3) ? SCR_2 : SCR_1; +} + +int ParrotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + return 0; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/parrot_script.h b/engines/titanic/true_talk/parrot_script.h new file mode 100644 index 0000000000..ec7bec7629 --- /dev/null +++ b/engines/titanic/true_talk/parrot_script.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +#ifndef TITANIC_PARROT_SCRIPT_H +#define TITANIC_PARROT_SCRIPT_H + +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class ParrotScript : public TTnpcScript { +private: + /** + * Setup sentence data + */ + void setupSentences(); +public: + ParrotScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_PARROT_SCRIPT_H */ diff --git a/engines/titanic/true_talk/script_handler.cpp b/engines/titanic/true_talk/script_handler.cpp new file mode 100644 index 0000000000..64e789a4b9 --- /dev/null +++ b/engines/titanic/true_talk/script_handler.cpp @@ -0,0 +1,148 @@ +/* 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/script_handler.h" +#include "titanic/true_talk/tt_concept.h" +#include "titanic/true_talk/tt_sentence.h" +#include "titanic/true_talk/tt_parser.h" +#include "titanic/true_talk/tt_word.h" +#include "titanic/titanic.h" + +namespace Titanic { + +/*------------------------------------------------------------------------*/ + +CScriptHandler::CScriptHandler(CTitleEngine *owner, int val1, int val2) : + _owner(owner), _script(owner->_script), _resources(g_vm->_exeResources), + _parser(this), _field10(0), _inputCtr(0), + _concept1P(nullptr), _concept2P(nullptr), _concept3P(nullptr), + _concept4P(nullptr), _field30(0) { + g_vm->_scriptHandler = this; + g_vm->_script = _script; + g_vm->_exeResources.reset(this, val1, val2); + _vocab = new TTvocab(val2); +} + +CScriptHandler::~CScriptHandler() { + delete _vocab; + delete _concept1P; + delete _concept2P; + delete _concept3P; + delete _concept4P; +} + +ScriptChangedResult CScriptHandler::scriptChanged(TTroomScript *roomScript, TTnpcScript *npcScript, uint dialogueId) { + if (!npcScript || !roomScript) { + ++_inputCtr; + return SCR_5; + } + + ScriptChangedResult result = roomScript->notifyScript(npcScript, dialogueId); + if (result == SCR_1) + result = npcScript->notifyScript(roomScript, dialogueId); + + if (result != SCR_3 && result != SCR_4) + return result; + + ++_inputCtr; + delete _concept1P; + delete _concept2P; + delete _concept3P; + delete _concept4P; + _concept1P = nullptr; + _concept2P = nullptr; + _concept3P = nullptr; + _concept4P = nullptr; + + return result; +} + +int CScriptHandler::processInput(TTroomScript *roomScript, TTnpcScript *npcScript, + const TTstring &line) { + if (!roomScript || !line.isValid()) + return SS_5; + + TTsentence *sentence = new TTsentence(_inputCtr++, line, this, roomScript, npcScript); + int result = _parser.preprocess(sentence); + roomScript->scriptPreprocess(sentence); + npcScript->scriptPreprocess(sentence); + + int canProcess = 0; + if (result) { + sentence->setState(result); + if (roomScript->canRespond(npcScript, sentence, result)) { + canProcess = npcScript->chooseResponse(roomScript, sentence, result); + } + } + + if (canProcess == 0 || canProcess == 1) { + if (!_parser.findFrames(sentence)) { + if (roomScript->canProcess(npcScript, sentence) && npcScript) { + npcScript->process(roomScript, sentence); + } + } + } + + delete sentence; + return SS_VALID; +} + +SimpleFile *CScriptHandler::openResource(const CString &name) { + return _owner->open(name); +} + +void CScriptHandler::setParserConcept(TTconcept *newConcept, TTconcept *oldConcept) { + _parser.conceptChanged(newConcept, oldConcept); +} + +int CScriptHandler::setResponse(TTscriptBase *script, TTresponse *response) { + return _owner->setResponse(script, response); +} + +void CScriptHandler::handleWord(const TTstring *str) { + handleWord1(str); + handleWord2(str); +} + +void CScriptHandler::handleWord1(const TTstring *str) { + if (_concept2P) + delete _concept2P; + _concept2P = nullptr; + + if (str) { + TTword word(*str, WC_UNKNOWN, 0); + _concept2P = new TTconcept(&word); + } +} + +void CScriptHandler::handleWord2(const TTstring *str) { + if (_concept1P) + delete _concept1P; + _concept1P = nullptr; + + if (str) { + TTword word(*str, WC_UNKNOWN, 0); + _concept1P = new TTconcept(&word); + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/script_handler.h b/engines/titanic/true_talk/script_handler.h new file mode 100644 index 0000000000..193c60f719 --- /dev/null +++ b/engines/titanic/true_talk/script_handler.h @@ -0,0 +1,89 @@ +/* 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. + * + */ + +#ifndef TITANIC_SCRIPT_HANDLER_H +#define TITANIC_SCRIPT_HANDLER_H + +#include "titanic/true_talk/tt_npc_script.h" +#include "titanic/true_talk/tt_parser.h" +#include "titanic/true_talk/tt_room_script.h" +#include "titanic/true_talk/tt_string.h" +#include "titanic/true_talk/tt_vocab.h" +#include "titanic/support/exe_resources.h" + +namespace Titanic { + +class CTitleEngine; +class CScriptHandler; + +class CScriptHandler { +private: + CTitleEngine *_owner; + CExeResources &_resources; + int _field10; + int _inputCtr; + int _field30; +private: + void handleWord1(const TTstring *str); + void handleWord2(const TTstring *str); +public: + TTparser _parser; + TTvocab *_vocab; + TTscriptBase *_script; + TTconcept *_concept1P; + TTconcept *_concept2P; + TTconcept *_concept3P; + TTconcept *_concept4P; +public: + CScriptHandler(CTitleEngine *owner, int val1, int val2); + ~CScriptHandler(); + + /** + * Set the character and room + */ + ScriptChangedResult scriptChanged(TTroomScript *roomScript, + TTnpcScript *npcScript, uint dialogueId); + + int processInput(TTroomScript *roomScript, TTnpcScript *npcScript, + const TTstring &line); + + /** + * Open a resource for access + */ + SimpleFile *openResource(const CString &name); + + /** + * Called when concept data is copied from one to another + */ + void setParserConcept(TTconcept *newConcept, TTconcept *oldConcept); + + /** + * Sets a conversation reponse + */ + int setResponse(TTscriptBase *script, TTresponse *response); + + void handleWord(const TTstring *str); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_SCRIPT_HANDLER_H */ diff --git a/engines/titanic/true_talk/script_support.cpp b/engines/titanic/true_talk/script_support.cpp new file mode 100644 index 0000000000..c24e275827 --- /dev/null +++ b/engines/titanic/true_talk/script_support.cpp @@ -0,0 +1,226 @@ +/* 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/script_support.h" +#include "titanic/titanic.h" + +namespace Titanic { + +int TTnpcScriptResponse::size() const { + for (int idx = 0; idx < 4; ++idx) { + if (_values[idx] == 0) + return idx; + } + + return 4; +} + +/*------------------------------------------------------------------------*/ + +TTscriptRange::TTscriptRange(uint id, const Common::Array<uint> &values, + bool isRandom, bool isSequential) : + _id(id), _nextP(nullptr) { + _mode = SF_NONE; + if (isRandom) + _mode = SF_RANDOM; + if (isSequential) + _mode = SF_SEQUENTIAL; + + for (uint idx = 0; idx < values.size(); ++idx) + _values.push_back(values[idx]); +} + +/*------------------------------------------------------------------------*/ + + +bool TTsentenceEntry::load(Common::SeekableReadStream *s) { + if (s->pos() >= s->size()) + return false; + + _field0 = s->readUint32LE(); + _field4 = s->readUint32LE(); + _string8 = readStringFromStream(s); + _fieldC = s->readUint32LE(); + _string10 = readStringFromStream(s); + _string14 = readStringFromStream(s); + _string18 = readStringFromStream(s); + _string1C = readStringFromStream(s); + _field20 = s->readUint32LE(); + _string24 = readStringFromStream(s); + _field28 = s->readUint32LE(); + _field2C = s->readUint32LE(); + _field30 = s->readUint32LE(); + + return true; +} + +/*------------------------------------------------------------------------*/ + +void TTsentenceEntries::load(const CString &resName) { + TTsentenceEntry entry; + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(resName); + + while (entry.load(r)) + push_back(entry); + + delete r; +} + +/*------------------------------------------------------------------------*/ + +TTscriptMapping::TTscriptMapping() : _id(0) { + Common::fill(&_values[0], &_values[8], 0); +} + +/*------------------------------------------------------------------------*/ + +void TTscriptMappings::load(const char *name, int valuesPerMapping) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + _valuesPerMapping = valuesPerMapping; + + while (r->pos() < r->size()) { + resize(size() + 1); + TTscriptMapping &m = (*this)[size() - 1]; + + m._id = r->readUint32LE(); + for (int idx = 0; idx < valuesPerMapping; ++idx) + m._values[idx] = r->readUint32LE(); + } + + delete r; +} + +/*------------------------------------------------------------------------*/ + +void TTtagMappings::load(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + uint src = r->readUint32LE(); + uint dest = r->readUint32LE(); + + push_back(TTtagMapping(src, dest)); + } + + delete r; +} + +/*------------------------------------------------------------------------*/ + +void TTwordEntries::load(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + TTwordEntry we; + we._id = r->readUint32LE(); + we._text = readStringFromStream(r); + + push_back(we); + } + + delete r; +} + +/*------------------------------------------------------------------------*/ + +void TThandleQuoteEntries::load(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + _tag1 = r->readUint32LE(); + _tag2 = r->readUint32LE(); + _rangeStart = r->readUint32LE(); + _rangeEnd = r->readUint32LE(); + + while (r->pos() < r->size()) { + TThandleQuoteEntry qe; + qe._index = r->readUint32LE(); + qe._tagId = r->readUint32LE(); + qe._dialogueId = r->readUint32LE(); + + push_back(qe); + } + + delete r; +} + +/*------------------------------------------------------------------------*/ + +void TTmapEntryArray::load(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + TTmapEntry us; + us._src = r->readUint32LE(); + us._dest = r->readUint32LE(); + + push_back(us); + } + + delete r; +} + +int TTmapEntryArray::find(uint id) const { + for (uint idx = 0; idx < size(); ++idx) { + const TTmapEntry &me = (*this)[idx]; + if (me._src == id) + return me._dest; + } + + return 0; +} + +/*------------------------------------------------------------------------*/ + +void TTupdateStateArray::load(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + TTupdateState us; + us._newId = r->readUint32LE(); + us._newValue = r->readUint32LE(); + us._dialBits = r->readUint32LE(); + + push_back(us); + } + + delete r; +} + +/*------------------------------------------------------------------------*/ + +void TTcommonPhraseArray::load(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + TTcommonPhrase cp; + cp._str = readStringFromStream(r); + cp._dialogueId = r->readUint32LE(); + cp._roomNum = r->readUint32LE(); + cp._val1 = r->readUint32LE(); + + push_back(cp); + } + + delete r; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/script_support.h b/engines/titanic/true_talk/script_support.h new file mode 100644 index 0000000000..a41673bd5c --- /dev/null +++ b/engines/titanic/true_talk/script_support.h @@ -0,0 +1,194 @@ +/* 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. + * + */ + +#ifndef TITANIC_SCRIPT_SUPPORT_H +#define TITANIC_SCRIPT_SUPPORT_H + +#include "titanic/support/simple_file.h" + +namespace Titanic { + +#define DIALS_ARRAY_COUNT 10 + +enum ScriptArrayFlag { SF_NONE = 0, SF_RANDOM = 1, SF_SEQUENTIAL = 2 }; + +struct RoomDialogueId { + uint _roomNum; + uint _dialogueId; +}; + +struct TTnpcScriptResponse { + uint _tag; + uint _values[4]; + + /** + * Returns the size of the values list plus 1 + */ + int size() const; +}; + +struct TTscriptRange { + uint _id; + Common::Array<uint> _values; + TTscriptRange *_nextP; + uint _priorIndex; + ScriptArrayFlag _mode; + + TTscriptRange() : _id(0), _nextP(nullptr), + _priorIndex(0), _mode(SF_NONE) {} + TTscriptRange(uint id, const Common::Array<uint> &values, bool isRandom, + bool isSequential); +}; + + +struct TTsentenceEntry { + int _field0; + int _field4; + CString _string8; + int _fieldC; + CString _string10; + CString _string14; + CString _string18; + CString _string1C; + int _field20; + CString _string24; + int _field28; + int _field2C; + int _field30; + + TTsentenceEntry() : _field0(0), _field4(0), _fieldC(0), + _field20(0), _field28(0), _field2C(0), _field30(0) {} + + /** + * Load an entry from the passed stream, and returns true + * if an entry was successfully loaded + */ + bool load(Common::SeekableReadStream *s); +}; + +class TTsentenceEntries : public Common::Array<TTsentenceEntry> { +public: + /** + * Load a list of entries from the specified resource + */ + void load(const CString &resName); +}; + +struct TTscriptMapping { + uint _id; + uint _values[8]; + + TTscriptMapping(); +}; + +class TTscriptMappings : public Common::Array<TTscriptMapping> { +public: + int _valuesPerMapping; + + void load(const char *name, int valuesPerMapping); +}; + +struct TTtagMapping { + uint _src, _dest; + TTtagMapping() : _src(0), _dest(0) {} + TTtagMapping(uint src, uint dest) : _src(src), _dest(dest) {} +}; + +class TTtagMappings : public Common::Array<TTtagMapping> { +public: + void load(const char *name); +}; + +struct TTwordEntry { + uint _id; + CString _text; + + TTwordEntry() : _id(0) {} +}; + +class TTwordEntries : public Common::Array<TTwordEntry> { +public: + void load(const char *name); +}; + +struct TThandleQuoteEntry { + uint _index; + uint _tagId; + uint _dialogueId; + + TThandleQuoteEntry() : _index(0), _tagId(0), _dialogueId(0) {} +}; + +class TThandleQuoteEntries : public Common::Array<TThandleQuoteEntry> { +public: + uint _tag1, _tag2; + uint _rangeStart, _rangeEnd; +public: + TThandleQuoteEntries() : _tag1(0), _tag2(0), _rangeStart(0), _rangeEnd(0) {} + void load(const char *name); +}; + +struct TTmapEntry { + uint _src; + uint _dest; + + TTmapEntry() : _src(0), _dest(0) {} +}; + +class TTmapEntryArray : public Common::Array<TTmapEntry> { +public: + void load(const char *name); + + /** + * Finds a record by Id, and returns it's associated value + */ + int find(uint id) const; +}; + +struct TTupdateState { + uint _newId; + uint _newValue; + uint _dialBits; + + TTupdateState() : _newId(0), _newValue(0), _dialBits(0) {} +}; + +class TTupdateStateArray : public Common::Array<TTupdateState> { +public: + void load(const char *name); +}; + +struct TTcommonPhrase { + CString _str; + uint _dialogueId; + uint _roomNum; + uint _val1; +}; + +class TTcommonPhraseArray : public Common::Array<TTcommonPhrase> { +public: + void load(const char *name); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_NPC_SCRIPT_H */ diff --git a/engines/titanic/true_talk/succubus_script.cpp b/engines/titanic/true_talk/succubus_script.cpp new file mode 100644 index 0000000000..db537c6470 --- /dev/null +++ b/engines/titanic/true_talk/succubus_script.cpp @@ -0,0 +1,237 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/succubus_script.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/titanic.h" + +namespace Titanic { + +SuccUBusScript::SuccUBusScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTnpcScript(val1, charClass, v2, charName, v3, val2, v4, v5, v6, v7), + _isRoom101(false) { + + loadRanges("Ranges/SuccUBus"); + setupSentences(); +} + +void SuccUBusScript::setupSentences() { + _mappings.load("Mappings/SuccUBus", 1); + _entries.load("Sentences/SuccUBus"); + _field68 = 0; + _entryCount = 0; +} + +int SuccUBusScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + uint dialogueId = tag; + + switch (tag) { + case MKTAG('S', 'L', 'O', 'W'): + case MKTAG('T', 'H', 'R', 'T'): + dialogueId = 70021; + + case MKTAG('S', 'U', 'C', '1'): + dialogueId = getDialogueId(230009); + break; + + case MKTAG('S', 'U', 'C', '2'): + dialogueId = 70117; + break; + + case MKTAG('S', 'W', 'E', 'R'): + dialogueId = getRandomNumber(100) > 40 ? 70103 : getDialogueId(230030); + break; + + default: + break; + } + + if (dialogueId) { + addResponse(dialogueId); + applyResponse(); + return 2; + } else { + return 1; + } +} + +int SuccUBusScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + if (!CTrueTalkManager::getStateValue(1)) + return 2; + + if (roomScript && roomScript->_scriptId == 101) + _isRoom101 = true; + + int currState = getState(); + if (currState) { + int currMode = sentence->_field2C; + bool modeFlag1 = currMode == 11 || currMode == 13; + bool modeFlag2 = currMode == 12; + setState(0); + + switch (currState) { + case 1: + if (currMode == 3 || currMode == 10) + return setResponse(70050, 0); + break; + + case 2: + if (modeFlag1 || modeFlag2) + return setResponse(70070 + (getRandomBit() ? 254 : 0), 0); + break; + + case 3: + if (currMode == 3 || currMode == 10) + return setResponse(70074, 0); + break; + + case 4: + if (currMode == 4) + return setResponse(70077, 0); + if (currMode == 3) + return setResponse(getDialogueId(230117), 0); + break; + + case 5: + if (currMode == 3 || currMode == 10) + return setResponse(70089, 0); + break; + + case 6: + if (modeFlag1) + return setResponse(70103, 0); + if (modeFlag2) + return setResponse(70102, 0); + break; + + case 7: + if (modeFlag1) + return setResponse(getDialogueId(230157), 0); + break; + + case 8: + if (modeFlag1) + return setResponse(getDialogueId(230159), 0); + break; + + case 9: + if (modeFlag1) + return setResponse(getDialogueId(230160), 0); + break; + + case 10: + if (modeFlag1) + return setResponse(getDialogueId(230161), 0); + break; + + case 11: + if (modeFlag1) + return setResponse(getDialogueId(230142), 0); + break; + + case 12: + return setResponse(70030, 0); + + default: + break; + } + } + + if (processEntries(&_entries, _entryCount, roomScript, sentence) != 2) { + uint tagId = g_vm->_trueTalkManager->_quotes.find(sentence->_normalizedLine.c_str()); + if (tagId && chooseResponse(roomScript, sentence, tagId) != 2) { + addResponse(getDialogueId(230030)); + applyResponse(); + } + } + + return 2; +} + +ScriptChangedResult SuccUBusScript::scriptChanged(const TTroomScript *roomScript, uint id) { + if (id == 148) + CTrueTalkManager::setFlags(3, 1); + else if (id == 150) + CTrueTalkManager::setFlags(2, 1); + + if (id >= 230000 && id <= 230245) { + addResponse(getDialogueId(id)); + applyResponse(); + } + else if (id >= 70000 && id <= 70243) { + addResponse(id); + applyResponse(); + } + + return SCR_2; +} + +int SuccUBusScript::updateState(uint oldId, uint newId, int index) { + if (newId == 230199) { + return _isRoom101 ? 230148 : newId; + } else if (newId >= 230208 && newId <= 230235) { + addResponse(70158 - getRandomBit()); + return newId; + } else if (newId >= 230061 && newId <= 230063) { + if (getValue(2)) + return 230125; + } + + static const uint UPDATE_STATES[][2] = { + { 230078, 1 }, { 230106, 2 }, { 230112, 3 }, { 230115, 4 }, + { 230127, 5 }, { 230140, 6 }, { 230156, 7 }, { 230157, 8 }, + { 230159, 9 }, { 230160, 10 }, { 230161, 11 }, { 230072, 12 } + }; + + for (int idx = 0; idx < 12; ++idx) { + if (UPDATE_STATES[idx][0] == newId) { + setState(UPDATE_STATES[idx][1]); + break; + } + } + + return newId; +} + +int SuccUBusScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + if (val1 == 1 && roomScript && roomScript->_scriptId == 101) { + addResponse(getDialogueId(230239)); + applyResponse(); + return 2; + } + + return 0; +} + +int SuccUBusScript::setResponse(int dialogueId, int state) { + addResponse(dialogueId); + applyResponse(); + + if (state != -1) + setState(state); + + return 2; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/succubus_script.h b/engines/titanic/true_talk/succubus_script.h new file mode 100644 index 0000000000..d5cea7e66f --- /dev/null +++ b/engines/titanic/true_talk/succubus_script.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +#ifndef TITANIC_SUCCUBUS_SCRIPT_H +#define TITANIC_SUCCUBUS_SCRIPT_H + +#include "titanic/true_talk/tt_npc_script.h" + +namespace Titanic { + +class SuccUBusScript : public TTnpcScript { +private: + bool _isRoom101; +private: + /** + * Setup sentence data + */ + void setupSentences(); + + /** + * Add a response and optionally set the state + */ + int setResponse(int dialogueId, int state = -1); +public: + SuccUBusScript(int val1, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Process a sentence fragment entry + */ + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_SUCCUBUS_SCRIPT_H */ diff --git a/engines/titanic/true_talk/title_engine.cpp b/engines/titanic/true_talk/title_engine.cpp new file mode 100644 index 0000000000..4dd45ba335 --- /dev/null +++ b/engines/titanic/true_talk/title_engine.cpp @@ -0,0 +1,83 @@ +/* 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/title_engine.h" +#include "titanic/titanic.h" + +namespace Titanic { + +CTitleEngine::CTitleEngine() : _script(nullptr), _scriptHandler(nullptr) { +} + +CTitleEngine::~CTitleEngine() { + delete _script; + delete _scriptHandler; +} + +void CTitleEngine::setup(int val1, int val2) { + _script = new TTTitleScript(); + _scriptHandler = new CScriptHandler(this, val1, val2); +} + +/*------------------------------------------------------------------------*/ + +STtitleEngine::STtitleEngine(): CTitleEngine(), + _responseP(nullptr), _field58(0) { +} + +STtitleEngine::~STtitleEngine() { + delete _stream; +} + +void STtitleEngine::reset() { + _field58 = 0; + _indexes.clear(); +} + +void STtitleEngine::setup(int val1, int val2) { + CTitleEngine::setup(val1, 3); +} + +int STtitleEngine::setResponse(TTscriptBase *script, TTresponse *response) { + _indexes.clear(); + for (TTresponse *respP = response; respP; respP = respP->getNext()) { + _indexes.push_back(respP->getDialogueId()); + } + + return 0; +} + +void STtitleEngine::dump(int val1, int val2) { + // TODO +} + +SimpleFile *STtitleEngine::open(const CString &name) { + Common::SeekableReadStream *stream = g_vm->_filesManager->getResource( + CString::format("TEXT/%s", name.c_str())); + assert(stream); + + SimpleFile *file = new SimpleFile(); + file->open(stream); + return file; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/title_engine.h b/engines/titanic/true_talk/title_engine.h new file mode 100644 index 0000000000..afd2d3b92f --- /dev/null +++ b/engines/titanic/true_talk/title_engine.h @@ -0,0 +1,113 @@ +/* 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. + * + */ + +#ifndef TITANIC_TITLE_ENGINE_H +#define TITANIC_TITLE_ENGINE_H + +#include "common/stream.h" +#include "common/winexe_pe.h" +#include "titanic/support/string.h" +#include "titanic/true_talk/script_handler.h" +#include "titanic/true_talk/tt_response.h" +#include "titanic/true_talk/tt_script_base.h" +#include "titanic/true_talk/tt_title_script.h" + +namespace Titanic { + +class CTitleEngine; + +class CTitleStream : public SimpleFile { +public: + CTitleStream() : SimpleFile() {} +}; + +class CTitleEngine { +public: + CScriptHandler *_scriptHandler; + TTscriptBase *_script; +public: + CTitleEngine(); + virtual ~CTitleEngine(); + + /** + * Setup the engine + */ + virtual void setup(int val1, int val2 = 0); + + /** + * Sets a conversation reponse + */ + virtual int setResponse(TTscriptBase *script, TTresponse *response) { return SS_4; } + + virtual int proc4(int unused) const = 0; + virtual int proc5(int64 unused) const = 0; + virtual int proc6(int64 unused) const = 0; + virtual int proc7(int64 unused) const = 0; + virtual int proc8() const = 0; + + /** + * Open a designated file + */ + virtual SimpleFile *open(const CString &name) = 0; +}; + +class STtitleEngine : public CTitleEngine { +private: + Common::SeekableReadStream *_stream; + TTresponse *_responseP; + int _field58; +public: + Common::Array<uint> _indexes; + Common::Array<byte> _data; +public: + STtitleEngine(); + virtual ~STtitleEngine(); + + void reset(); + + /** + * Setup the engine + */ + virtual void setup(int val1, int val2 = 0); + + /** + * Sets a conversation reponse + */ + virtual int setResponse(TTscriptBase *script, TTresponse *response); + + virtual void dump(int val1, int val2); + + virtual int proc4(int unused) const { return 0; } + virtual int proc5(int64 unused) const { return 0; } + virtual int proc6(int64 unused) const { return 0; } + virtual int proc7(int64 unused) const { return 0; } + virtual int proc8() const { return 0; } + + /** + * Open a designated file + */ + virtual SimpleFile *open(const CString &name); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TITLE_ENGINE_H */ diff --git a/engines/titanic/true_talk/true_talk_manager.cpp b/engines/titanic/true_talk/true_talk_manager.cpp new file mode 100644 index 0000000000..19beee9796 --- /dev/null +++ b/engines/titanic/true_talk/true_talk_manager.cpp @@ -0,0 +1,597 @@ +/* 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/true_talk_manager.h" +#include "titanic/core/tree_item.h" +#include "titanic/npcs/true_talk_npc.h" +#include "titanic/game_manager.h" +#include "titanic/titanic.h" + +#define MKTAG_BE(a3,a2,a1,a0) ((uint32)((a3) | ((a2) << 8) | ((a1) << 16) | ((a0) << 24))) + +namespace Titanic { + +int CTrueTalkManager::_v1; +int CTrueTalkManager::_v2; +int CTrueTalkManager::_v3; +bool CTrueTalkManager::_v4; +bool CTrueTalkManager::_v5; +int CTrueTalkManager::_v6; +int CTrueTalkManager::_v7; +bool CTrueTalkManager::_v8; +int CTrueTalkManager::_v9; +bool CTrueTalkManager::_v10; +int CTrueTalkManager::_v11[41]; +CTrueTalkNPC *CTrueTalkManager::_currentNPC; + +/*------------------------------------------------------------------------*/ + +CTrueTalkManager::CTrueTalkManager(CGameManager *owner) : + _gameManager(owner), _scripts(&_titleEngine), _currentCharId(0), + _dialogueFile(nullptr), _dialogueId(0) { + _titleEngine.setup(3, 3); + _quotes.load(); + _quotesTree.load(); + + _currentNPC = nullptr; + g_vm->_trueTalkManager = this; +} + +CTrueTalkManager::~CTrueTalkManager() { + clear(); + g_vm->_trueTalkManager = nullptr; +} + +void CTrueTalkManager::save(SimpleFile *file) const { + saveStatics(file); + + saveNPC(file, 101); + saveNPC(file, 103); + saveNPC(file, 104); + saveNPC(file, 105); + saveNPC(file, 111); + saveNPC(file, 100); + saveNPC(file, 112); + saveNPC(file, 107); + file->writeNumber(0); +} + +void CTrueTalkManager::load(SimpleFile *file) { + loadStatics(file); + + // Iterate through loading characters + int charId = file->readNumber(); + while (charId) { + loadNPC(file, charId); + + int ident1 = file->readNumber(); + int ident2 = file->readNumber(); + + if (ident1 != MKTAG_BE('U', 'R', 'A', 'H')) { + while (ident2 != MKTAG_BE('A', 'K', 'E', 'R')) { + ident1 = ident2; + ident2 = file->readNumber(); + + if (!ident1) + break; + } + } + + // Get start of next character + charId = file->readNumber(); + } +} + +void CTrueTalkManager::loadStatics(SimpleFile *file) { + int count = file->readNumber(); + _v1 = file->readNumber(); + _v2 = file->readNumber(); + _v3 = file->readNumber(); + _v4 = file->readNumber() != 0; + _v5 = file->readNumber() != 0; + _v6 = file->readNumber(); + _v7 = file->readNumber(); + _v8 = file->readNumber() != 0; + _v9 = file->readNumber(); + _v10 = file->readNumber() != 0; + + for (int idx = count; idx > 10; --idx) + file->readNumber(); + + int count2 = file->readNumber(); + for (int idx = 0; idx < count2; ++idx) { + int v = file->readNumber(); + if (idx < 41) + _v11[idx] = v; + } +} + +void CTrueTalkManager::saveStatics(SimpleFile *file) { + file->writeNumber(10); + file->writeNumber(_v1); + file->writeNumber(_v2); + file->writeNumber(_v3); + file->writeNumber(_v4 ? 1 : 0); + file->writeNumber(_v5 ? 1 : 0); + file->writeNumber(_v6); + file->writeNumber(_v7); + file->writeNumber(_v8 ? 1 : 0); + file->writeNumber(_v9); + file->writeNumber(_v10 ? 1 : 0); + + file->writeNumber(41); + for (int idx = 0; idx < 41; ++idx) + file->writeNumber(_v11[idx]); +} + +void CTrueTalkManager::clear() { + delete _dialogueFile; + _dialogueFile = nullptr; + _currentCharId = 0; +} + +void CTrueTalkManager::setFlags(int index, int val) { + switch (index) { + case 1: + if (val >= 1 && val <= 3) + _v3 = val; + break; + + case 2: + _v4 = !val; + break; + + case 3: + _v5 = val != 0; + break; + + case 4: + if (val >= 0 && val <= 3) + _v6 = val; + break; + + case 5: + _v7 = val; + break; + + case 6: + _v8 = val != 0; + break; + + default: + if (index < 41) + _v11[index] = val; + break; + } +} + +void CTrueTalkManager::loadNPC(SimpleFile *file, int charId) { + TTnpcScript *script = _scripts.getNpcScript(charId); + if (script) + script->load(file); +} + +void CTrueTalkManager::saveNPC(SimpleFile *file, int charId) const { + TTnpcScript *script = _scripts.getNpcScript(charId); + if (script) { + script->save(file); + file->writeNumber(MKTAG_BE('U', 'R', 'A', 'H')); + file->writeNumber(MKTAG_BE('A', 'K', 'E', 'R')); + } +} + +void CTrueTalkManager::preLoad() { + // Delete any previous talkers + for (TTtalkerList::iterator i = _talkers.begin(); i != _talkers.end(); ++i) + delete *i; + _talkers.clear(); +} + +void CTrueTalkManager::removeCompleted() { + for (TTtalkerList::iterator i = _talkers.begin(); i != _talkers.end(); ) { + TTtalker *talker = *i; + + if (talker->_done) { + i = _talkers.erase(i); + delete talker; + } else { + ++i; + } + } +} + +void CTrueTalkManager::update2() { + //warning("CTrueTalkManager::update2"); +} + +void CTrueTalkManager::start(CTrueTalkNPC *npc, uint id, CViewItem *view) { + TTnpcScript *npcScript = getNpcScript(npc); + TTroomScript *roomScript = getRoomScript(); + + _titleEngine.reset(); + uint charId = npcScript->charId(); + loadAssets(npc, charId); + + _currentNPC = npc; + _titleEngine._scriptHandler->scriptChanged(roomScript, npcScript, id); + _currentNPC = nullptr; + + setDialogue(npc, roomScript, view); +} + +void CTrueTalkManager::start3(CTrueTalkNPC *npc, CViewItem *view) { + start(npc, 3, view); +} + +void CTrueTalkManager::start4(CTrueTalkNPC *npc, CViewItem *view) { + start(npc, 4, view); +} + +TTnpcScript *CTrueTalkManager::getTalker(const CString &name) const { + if (name.contains("Doorbot")) + return _scripts.getNpcScript(104); + else if (name.contains("DeskBot")) + return _scripts.getNpcScript(103); + else if (name.contains("LiftBot")) + return _scripts.getNpcScript(105); + else if (name.contains("Parrot")) + return _scripts.getNpcScript(107); + else if (name.contains("BarBot")) + return _scripts.getNpcScript(100); + else if (name.contains("ChatterBot")) + return _scripts.getNpcScript(102); + else if (name.contains("BellBot")) + return _scripts.getNpcScript(101); + else if (name.contains("MaitreD")) + return _scripts.getNpcScript(112); + else if (name.contains("Succubus") || name.contains("Sub")) + return _scripts.getNpcScript(111); + + return nullptr; +} + +TTnpcScript *CTrueTalkManager::getNpcScript(CTrueTalkNPC *npc) const { + CString npcName = npc->getName(); + TTnpcScript *script = getTalker(npcName); + + if (!script) { + // Fall back on the default NPC script + script = _scripts.getNpcScript(101); + } + + return script; +} + +TTroomScript *CTrueTalkManager::getRoomScript() const { + CRoomItem *room = _gameManager->getRoom(); + TTroomScript *script = nullptr; + if (room) { + int scriptId = room->getScriptId(); + if (scriptId) + script = _scripts.getRoomScript(scriptId); + } + + if (!script) { + // Fall back on the default Room script + script = _scripts.getRoomScript(110); + } + + return script; +} + +TTroomScript *CTrueTalkManager::getRoomScript(int roomId) const { + TTroomScript *script = nullptr; + if (roomId) + script = _scripts.getRoomScript(roomId); + + if (!script) + // Fall back on the default Room script + script = _scripts.getRoomScript(110); + + return script; +} + +void CTrueTalkManager::loadAssets(CTrueTalkNPC *npc, int charId) { + // If assets for the character are already loaded, simply exit + if (_currentCharId == charId) + return; + + // Clear any previously loaded data + clear(); + + // Signal the NPC to get the asset details + CTrueTalkGetAssetDetailsMsg detailsMsg; + detailsMsg.execute(npc); + + if (!detailsMsg._filename.empty()) { + _dialogueFile = new CDialogueFile(detailsMsg._filename, 20); + _dialogueId = detailsMsg._numValue + 1; + } +} + +void CTrueTalkManager::processInput(CTrueTalkNPC *npc, CTextInputMsg *msg, CViewItem *view) { + TTnpcScript *npcScript = getNpcScript(npc); + TTroomScript *roomScript = getRoomScript(); + _titleEngine.reset(); + + if (npcScript && roomScript) { + _currentNPC = npc; + _titleEngine._scriptHandler->processInput(roomScript, npcScript, TTstring(msg->_input)); + _currentNPC = nullptr; + + loadAssets(npc, npcScript->charId()); + setDialogue(npc, roomScript, view); + } + + _currentNPC = nullptr; +} + +void CTrueTalkManager::setDialogue(CTrueTalkNPC *npc, TTroomScript *roomScript, CViewItem *view) { + // Get the dialog text + CString dialogueStr = readDialogueString(); + if (dialogueStr.empty()) + return; + + int soundId = readDialogSound(); + TTtalker *talker = new TTtalker(this, npc); + _talkers.push_back(talker); + + bool isParrot = npc->getName().contains("parrot"); + triggerNPC(npc); + playSpeech(talker, roomScript, view, isParrot); + talker->speechStarted(dialogueStr, _titleEngine._indexes[0], soundId); +} + +#define STRING_BUFFER_SIZE 2048 + +CString CTrueTalkManager::readDialogueString() { + byte buffer[STRING_BUFFER_SIZE]; + CString result; + + for (uint idx = 0; idx < _titleEngine._indexes.size(); ++idx) { + if (idx != 0) + result += " "; + + // Open a text entry from the dialogue file for access + DialogueResource *textRes = _dialogueFile->openTextEntry( + _titleEngine._indexes[idx] - _dialogueId); + if (!textRes) + continue; + + size_t entrySize = textRes->size(); + byte *tempBuffer = (entrySize < STRING_BUFFER_SIZE) ? buffer : + new byte[entrySize + 1]; + + _dialogueFile->read(textRes, tempBuffer, entrySize); + buffer[entrySize] = '\0'; + + // Close the resource + _dialogueFile->closeEntry(textRes); + + // Strip off any non-printable characters + for (byte *p = buffer; *p != '\0'; ++p) { + if (*p < 32 || *p > 127) + *p = ' '; + } + + // Add string to result + result += CString((const char *)buffer); + + // Free buffer if one was allocated + if (entrySize >= STRING_BUFFER_SIZE) + delete[] tempBuffer; + } + + return result; +} + +int CTrueTalkManager::readDialogSound() { + _field18 = 0; + + for (uint idx = 0; idx < _titleEngine._indexes.size(); ++idx) { + CWaveFile *waveFile = _gameManager->_sound.getTrueTalkSound( + _dialogueFile, _titleEngine._indexes[idx] - _dialogueId); + if (waveFile) { + _field18 = waveFile->fn1(); + } + } + + return _field18; +} + +void CTrueTalkManager::triggerNPC(CTrueTalkNPC *npc) { + CTrueTalkSelfQueueAnimSetMsg queueSetMsg; + if (queueSetMsg.execute(npc)) { + if (_field18 > 300) { + CTrueTalkQueueUpAnimSetMsg upMsg(_field18); + upMsg.execute(npc); + } + } else { + CTrueTalkGetAnimSetMsg getAnimMsg; + if (_field18 > 300) { + do { + getAnimMsg.execute(npc); + if (!getAnimMsg._endFrame) + break; + + npc->playMovie(getAnimMsg._startFrame, getAnimMsg._endFrame, 0); + getAnimMsg._endFrame = 0; + + uint numFrames = getAnimMsg._endFrame - getAnimMsg._startFrame; + int64 val = (numFrames * 1000) * 0x88888889; + uint diff = (val >> (32 + 5)) - 500; + _field18 += diff; + + getAnimMsg._index++; + } while (_field18 > 0); + } + } +} + +void CTrueTalkManager::playSpeech(TTtalker *talker, TTroomScript *roomScript, CViewItem *view, bool isParrot) { + uint milli, index; + switch (roomScript->_scriptId) { + case 101: + milli = 300; + index = 16; + break; + case 106: + case 107: + case 110: + case 114: + case 115: + case 122: + milli = 130; + index = 10; + break; + case 108: + case 109: + milli = 200; + index = 10; + break; + case 111: + case 116: + case 121: + milli = 80; + index = 12; + break; + case 112: + case 124: + case 128: + case 130: + milli = 80; + index = 4; + break; + case 132: + milli = 60; + index = 4; + break; + default: + milli = 0; + index = 4; + break; + } + + // Setup proximities + CProximity p1, p2, p3; + if (isParrot) { + p1._channel = 3; + p2._channel = 5; + p3._channel = 4; + } else { + p1._channel = 0; + p2._channel = 1; + p3._channel = 2; + } + + if (milli > 0) { + p3._channelVolume = (index * 3) / 2; + p3._positioningMode = POSMODE_POLAR; + p3._azimuth = -135.0; + p3._range = 1.0; + p3._elevation = 0; + + p2._channelVolume = (index * 3) / 4; + p2._positioningMode = POSMODE_NONE; + p2._azimuth = 135.0; + p2._range = 1.0; + p2._elevation = 0; + } + + _gameManager->_sound.stopChannel(p1._channel); + if (view) { + p1._positioningMode = POSMODE_VECTOR; + view->getPosition(p1._posX, p1._posY, p1._posZ); + } + + // Loop through adding each of the speech portions in. We use the + // _priorSoundHandle of CProximity to chain each successive speech + // to start when the prior one finishes + for (uint idx = 0; idx < _titleEngine._indexes.size(); ++idx) { + uint id = _titleEngine._indexes[idx]; + if (id > 100000) + continue; + + if (idx == (_titleEngine._indexes.size() - 1)) { + // Final iteration of speech segments to play + p1._endTalkerFn = &talkerEnd; + p1._talker = talker; + } + + // Start the speech + p1._priorSoundHandle = _gameManager->_sound.playSpeech(_dialogueFile, id - _dialogueId, p1); + if (!milli) + continue; + + if (idx == 0) + g_vm->_events->sleep(milli); + + p3._priorSoundHandle = _gameManager->_sound.playSpeech(_dialogueFile, id - _dialogueId, p3); + if (idx == 0) + g_vm->_events->sleep(milli); + + p2._priorSoundHandle = _gameManager->_sound.playSpeech(_dialogueFile, id - _dialogueId, p2); + } +} + +int CTrueTalkManager::getStateValue(int stateNum) { + if (!_currentNPC) + return -1000; + + CTrueTalkGetStateValueMsg msg(stateNum, -1000); + msg.execute(_currentNPC); + return msg._stateVal; +} + +bool CTrueTalkManager::triggerAction(int action, int param) { + if (!_currentNPC) + return false; + + CTrueTalkTriggerActionMsg msg(action, param, 0); + msg.execute(_currentNPC); + return true; +} + +void CTrueTalkManager::talkerEnd(TTtalker *talker) { + if (talker) + talker->endSpeech(0); +} + +CGameManager *CTrueTalkManager::getGameManager() const { + return _gameManager; +} + +CGameState *CTrueTalkManager::getGameState() const { + return _gameManager ? &_gameManager->_gameState : nullptr; +} + +int CTrueTalkManager::getPassengerClass() const { + CGameState *gameState = getGameState(); + return gameState ? gameState->_passengerClass : 4; +} + +int CTrueTalkManager::getState14() const { + CGameState *gameState = getGameState(); + return gameState ? gameState->_field14 : 0; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/true_talk_manager.h b/engines/titanic/true_talk/true_talk_manager.h new file mode 100644 index 0000000000..8a8895917a --- /dev/null +++ b/engines/titanic/true_talk/true_talk_manager.h @@ -0,0 +1,245 @@ +/* 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. + * + */ + +#ifndef TITANIC_TRUE_TALK_MANAGER_H +#define TITANIC_TRUE_TALK_MANAGER_H + +#include "titanic/messages/messages.h" +#include "titanic/support/simple_file.h" +#include "titanic/true_talk/dialogue_file.h" +#include "titanic/true_talk/title_engine.h" +#include "titanic/true_talk/tt_quotes.h" +#include "titanic/true_talk/tt_quotes_tree.h" +#include "titanic/true_talk/tt_scripts.h" +#include "titanic/true_talk/tt_talker.h" + +namespace Titanic { + +class CGameManager; +class CGameState; +class CTreeItem; +class CViewItem; +class CTrueTalkManager; +class CTrueTalkNPC; + +class CTrueTalkManager { +private: + CGameManager *_gameManager; + STtitleEngine _titleEngine; + TTscripts _scripts; + int _currentCharId; + CDialogueFile *_dialogueFile; + int _dialogueId; + int _field18; + TTtalkerList _talkers; +private: + /** + * Loads the statics for the class + */ + static void loadStatics(SimpleFile *file); + + /** + * Saves the statics associated with the class + */ + static void saveStatics(SimpleFile *file); + + /** + * Loads an NPC from file + */ + void loadNPC(SimpleFile *file, int charId); + + /** + * Saves the specified NPC to file + */ + void saveNPC(SimpleFile *file, int charId) const; + + /** + * Gets the script associated with an NPC game object + */ + TTnpcScript *getNpcScript(CTrueTalkNPC *npc) const; + + /** + * Gets the script associated with the current room + */ + TTroomScript *getRoomScript() const; + + /** + * Loads assets for the current character, if it's changed + */ + void loadAssets(CTrueTalkNPC *npc, int charId); + + void setDialogue(CTrueTalkNPC *npc, TTroomScript *roomScript, CViewItem *view); + + /** + * Read in text from the dialogue file + */ + CString readDialogueString(); + + /** + * Read in the sound from the dialogue file + */ + int readDialogSound(); + + /** + * Triggers animation for the NPC + */ + void triggerNPC(CTrueTalkNPC *npc); + + /** + * Plays speech specified by the manager's indexes array + */ + void playSpeech(TTtalker *talker, TTroomScript *roomScript, CViewItem *view, bool isParrot); + + /** + * Called when a talker finishes + */ + static void talkerEnd(TTtalker *talker); + + /** + * Return the game state + */ + CGameState *getGameState() const; +public: + static int _v1; + static int _v2; + static int _v3; + static bool _v4; + static bool _v5; + static int _v6; + static int _v7; + static bool _v8; + static int _v9; + static bool _v10; + static int _v11[41]; + static CTrueTalkNPC *_currentNPC; + + static void setFlags(int index, int val); +public: + TTquotes _quotes; + TTquotesTree _quotesTree; +public: + /** + * Get a specified state value from the currently set NPC + */ + static int getStateValue(int stateNum); + + /** + * Trigger an NPC action + */ + static bool triggerAction(int action, int param); +public: + CTrueTalkManager(CGameManager *owner); + ~CTrueTalkManager(); + + /** + * Save the data for the class to file + */ + void save(SimpleFile *file) const; + + /** + * Load the data for the class from file + */ + void load(SimpleFile *file); + + /** + * Clear the manager + */ + void clear(); + + /** + * Called when a game is about to be loaded + */ + void preLoad(); + + /** + * Called when loading a game is complete + */ + void postLoad() {} + + /** + * Called when a game is about to be saved + */ + void preSave() {} + + /** + * Called when a game has finished being saved + */ + void postSave() {} + + /** + * Returns the scripts for the manager + */ + TTscripts &getScripts() { return _scripts; } + + /** + * Remove any completed talkers + */ + void removeCompleted(); + + /** + * Return the game manager + */ + CGameManager *getGameManager() const; + + void update2(); + + /** + * Start a TrueTalk conversation + */ + void start(CTrueTalkNPC *npc, uint id, CViewItem *view); + + /** + * Start a TrueTalk conversation + */ + void start3(CTrueTalkNPC *npc, CViewItem *view); + + /** + * Start a TrueTalk conversation + */ + void start4(CTrueTalkNPC *npc, CViewItem *view); + + /** + * Return a TrueTalk talker/script + */ + TTnpcScript *getTalker(const CString &name) const; + + /** + * Process player's input + */ + void processInput(CTrueTalkNPC *npc, CTextInputMsg *msg, CViewItem *view); + + /** + * Gets the script associated with a specific room + */ + TTroomScript *getRoomScript(int roomId) const; + + /** + * Get the player's passenger class + */ + int getPassengerClass() const; + + int getState14() const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TRUE_TALK_MANAGER_H */ diff --git a/engines/titanic/true_talk/tt_action.cpp b/engines/titanic/true_talk/tt_action.cpp new file mode 100644 index 0000000000..5bf91c71df --- /dev/null +++ b/engines/titanic/true_talk/tt_action.cpp @@ -0,0 +1,69 @@ +/* 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_action.h" + +namespace Titanic { + +bool TTaction::_staticFlag; + +TTaction::TTaction(const TTstring &str, WordClass wordClass, int val2, int val3, int val4) : + TTmajorWord(str, wordClass, val2, val3), _field30(val4) { +} + +TTaction::TTaction(const TTaction *src) : TTmajorWord(src) { + if (src->getStatus()) { + _field30 = 0; + _status = SS_5; + } else { + _field30 = src->_field30; + } +} + +int TTaction::load(SimpleFile *file) { + int val; + + if (!TTword::load(file, WC_ACTION) && file->scanf("%d", &val)) { + _field30 = val; + return 0; + } else { + return 8; + } +} + +TTword *TTaction::copy() const { + TTaction *returnWordP = new TTaction(this); + returnWordP->_status = _status; + if (!_status) { + _staticFlag = false; + return returnWordP; + } else if (_status == SS_13 && !_staticFlag) { + _staticFlag = true; + delete returnWordP; + return copy(); + } else { + delete returnWordP; + return nullptr; + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_action.h b/engines/titanic/true_talk/tt_action.h new file mode 100644 index 0000000000..29e2bc4e4a --- /dev/null +++ b/engines/titanic/true_talk/tt_action.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_ACTION_H +#define TITANIC_TT_ACTION_H + +#include "titanic/true_talk/tt_major_word.h" + +namespace Titanic { + +class TTaction : public TTmajorWord { +private: + static bool _staticFlag; +protected: + int _field30; +public: + TTaction(const TTstring &str, WordClass wordClass, int val2, int val3, int val4); + TTaction(const TTaction *src); + + /** + * Load the word + */ + int load(SimpleFile *file); + + void setVal(int val) { _field30 = val; } + int getVal() const { return _field30; } + + /** + * Creates a copy of the word + */ + virtual TTword *copy() const; + + virtual bool proc12(int val) const { return _field30 == val; } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_ACTION_H */ diff --git a/engines/titanic/true_talk/tt_adj.cpp b/engines/titanic/true_talk/tt_adj.cpp new file mode 100644 index 0000000000..a14784798f --- /dev/null +++ b/engines/titanic/true_talk/tt_adj.cpp @@ -0,0 +1,84 @@ +/* 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_adj.h" + +namespace Titanic { + +bool TTadj::_staticFlag; + +TTadj::TTadj(TTstring &str, WordClass wordClass, int val2, int val3, int val4) : + TTmajorWord(str, wordClass, val2, val3) { + if (val4 >= 0 && val4 <= 9) { + _val = val4; + } else { + _val = 0; + _status = SS_5; + } +} + +TTadj::TTadj(const TTadj *src) : TTmajorWord(src) { + if (src->getStatus()) { + _val = 0; + _status = SS_5; + } else { + _val = src->_val; + } +} + +int TTadj::load(SimpleFile *file) { + int val; + + if (!TTword::load(file, WC_ADJECTIVE) && file->scanf("%d", &val)) { + _val = val; + return 0; + } else { + return 8; + } +} + +int TTadj::adjFn1(int val) { + if (_val < 0 || _val > 9) { + return SS_4; + } else { + _val = val; + return SS_VALID; + } +} + +TTword *TTadj::copy() const { + TTadj *returnWordP = new TTadj(this); + returnWordP->_status = _status; + if (!_status) { + _staticFlag = false; + return returnWordP; + } else if (_status == SS_13 && !_staticFlag) { + _staticFlag = true; + delete returnWordP; + return copy(); + } else { + delete returnWordP; + return nullptr; + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_adj.h b/engines/titanic/true_talk/tt_adj.h new file mode 100644 index 0000000000..1dec8a77d2 --- /dev/null +++ b/engines/titanic/true_talk/tt_adj.h @@ -0,0 +1,67 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_ADJ_H +#define TITANIC_TT_ADJ_H + +#include "titanic/true_talk/tt_major_word.h" + +namespace Titanic { + +class TTadj : public TTmajorWord { +private: + static bool _staticFlag; +protected: + int _val; +public: + TTadj(TTstring &str, WordClass wordClass, int val2, int val3, int val4); + TTadj(const TTadj *src); + + /** + * Load the word + */ + int load(SimpleFile *file); + + int adjFn1(int val); + + /** + * Creates a copy of the word + */ + virtual TTword *copy() const; + + virtual bool proc14(int val) const { return _val == val; } + virtual int proc15() const { return _val; } + virtual bool proc16() const { return _val >= 7; } + virtual bool proc17() const { return _val <= 3; } + virtual bool proc18() const { return _val > 3 && _val < 7; } + + /** + * Dumps data associated with the word to file + */ + virtual int save(SimpleFile *file) const { + return saveData(file, _val); + } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_ADJ_H */ diff --git a/engines/titanic/true_talk/tt_concept.cpp b/engines/titanic/true_talk/tt_concept.cpp new file mode 100644 index 0000000000..c614e14dae --- /dev/null +++ b/engines/titanic/true_talk/tt_concept.cpp @@ -0,0 +1,308 @@ +/* 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_concept.h" +#include "titanic/true_talk/tt_script_base.h" +#include "titanic/true_talk/tt_word.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTconcept::TTconcept() : _string1(" "), _string2(" "), + _scriptP(nullptr), _wordP(nullptr) { + if (setStatus()) + setScriptType(ST_UNKNOWN_SCRIPT); + else + reset(); +} + +TTconcept::TTconcept(TTscriptBase *script, ScriptType scriptType) : + _string1(" "), _string2(" "), _wordP(nullptr), _scriptP(nullptr) { + if (!script->getStatus()) { + setScriptType(scriptType); + _scriptP = script; + + if (scriptType == ST_UNKNOWN_SCRIPT && script->_id == 1) + _scriptType = ST_ROOM_SCRIPT; + } + + if (_status) + reset(); +} + +TTconcept::TTconcept(TTword *word, ScriptType scriptType) : + _string1(" "), _string2(" "), _wordP(nullptr), _scriptP(nullptr) { + + if (!word || !setStatus() || word->getStatus()) { + _status = SS_5; + } else { + _status = initializeWordRef(word); + if (!_status) + setScriptType(scriptType); + } + + if (_status) + reset(); +} + +TTconcept::TTconcept(TTconcept &src) : + _string1(src._string1), _string2(src._string2), + _wordP(nullptr), _scriptP(nullptr) { + + if (src.getStatus()) { + _status = SS_5; + } else { + if (setStatus()) { + _status = SS_VALID; + _scriptP = src._scriptP; + + if (src._wordP) { + _status = initializeWordRef(src._wordP); + initialize(src); + } + } + } + + if (_status) + reset(); +} + +TTconcept::~TTconcept() { + if (_word2P) { + _word2P->deleteSiblings(); + delete _word2P; + } + delete _wordP; + + if (_flag) + g_vm->_exeResources._owner->setParserConcept(this, nullptr); +} + +void TTconcept::deleteSiblings() { + for (TTconcept *currP = _nextP, *nextP; currP; currP = nextP) { + nextP = currP->_nextP; + delete currP; + } + + _nextP = nullptr; +} + +bool TTconcept::setStatus() { + if (_string1.isValid() && _string2.isValid()) { + _status = SS_VALID; + return true; + } else { + _status = SS_11; + return false; + } +} + +void TTconcept::setScriptType(ScriptType scriptType) { + _nextP = nullptr; + _field14 = 0; + _scriptType = scriptType; + _field1C = -1; + _field20 = 0; + _word2P = nullptr; + _field30 = 0; + _field34 = 0; + _flag = false; + _status = 0; +} + +int TTconcept::initializeWordRef(TTword *word) { + delete _wordP; + _wordP = word; + return 0; +} + +void TTconcept::reset() { + delete _wordP; + _wordP = nullptr; + _scriptP = nullptr; + + int oldStatus = _status; + setScriptType(ST_UNKNOWN_SCRIPT); + _status = oldStatus; +} + +bool TTconcept::compareTo(const char *str) const { + return this != nullptr && _wordP != nullptr && + _wordP->compareTo(str); +} + +bool TTconcept::compareTo(TTword *word) const { + if (_wordP && _wordP->compareTo(word->_text)) + return true; + + if (_scriptP && _scriptP->getId() == 1 && word->comparePronounTo(1)) + return true; + + return false; +} + +void TTconcept::initialize(TTconcept &src) { + _nextP = src._nextP; + _field14 = src._field14; + _scriptType = src._scriptType; + _field1C = src._field1C; + _field20 = src._field20; + + if (src._word2P) { + _word2P = src._word2P->copyWords(); + if (src._word2P->getChainStatus()) + _status = 11; + } else { + _word2P = nullptr; + } + + _field30 = src._field30; + _field34 = src._field34; + + if (src._flag) { + g_vm->_exeResources._owner->setParserConcept(this, &src); + src.setFlag(true); + _flag = true; + } + + _status = src._status; +} + +void TTconcept::copyFrom(TTconcept *src) { + if (this != src) { + if (src->getStatus()) { + _status = SS_5; + } else { + _string1 = src->_string1; + _string2 = src->_string2; + + if (setStatus()) { + _scriptP = src->_scriptP; + if (src->_wordP) { + _status = initializeWordRef(src->_wordP); + initialize(*src); + } else { + _wordP = nullptr; + initialize(*src); + } + } + } + } + + if (_status) + reset(); +} + +int TTconcept::setOwner(TTconcept *src) { + if (this) { + if (src->_wordP) { + TTword *newWord = src->_wordP->copy(); + return setOwner(newWord, 1); + } + } + + return 0; +} + +int TTconcept::setOwner(TTword *src, bool dontDup) { + TTword *word = dontDup ? src : src->copy(); + + if (word) { + if (!_word2P) { + _word2P = word; + } else { + // Add word to end of word list + TTword *tailP = _word2P; + while (tailP->_nextP) + tailP = tailP->_nextP; + + tailP->_nextP = word; + } + } + + return 0; +} + +bool TTconcept::checkWordId1() const { + return (_wordP && (_wordP->_id == 200 || _wordP->_id == 201 || + _wordP->_id == 602 || _wordP->_id == 607)) || + (_scriptP && _scriptP->_id <= 2); +} + +bool TTconcept::checkWordId2() const { + return (_wordP && _wordP->_id == 204) || (_scriptP && _scriptP->getId() == 3); +} + +bool TTconcept::checkWordId3() const { + return isWordClass(WC_ABSTRACT) || isWordClass(WC_ADJECTIVE) || + (isWordClass(WC_ADVERB) && getWordId() != 910); +} + +bool TTconcept::checkWordClass() const { + return !_scriptP && _wordP && (_wordP->_wordClass == WC_THING || _wordP->_wordClass == WC_PRONOUN); +} + +const TTstring TTconcept::getText() { + if (_scriptP) + return _scriptP->getText(); + else if (_wordP) + return _wordP->getText(); + else + return TTstring(); +} + +TTconcept *TTconcept::findByWordId(int id) { + for (TTconcept *conceptP = this; conceptP; conceptP = conceptP->_nextP) { + if (conceptP->_wordP && conceptP->_wordP->_id == id) + return conceptP; + } + + return nullptr; +} + +TTconcept *TTconcept::findByWordClass(WordClass wordClass) { + for (TTconcept *conceptP = this; conceptP; conceptP = conceptP->_nextP) { + if (conceptP->_wordP && conceptP->_wordP->_wordClass == wordClass) + return conceptP; + } + + return nullptr; +} + +TTconcept *TTconcept::findBy20(int val) { + for (TTconcept *conceptP = this; conceptP; conceptP = conceptP->_nextP) { + if (conceptP->_field20 == val) + return conceptP; + } + + return nullptr; +} + +bool TTconcept::isWordId(int id) const { + return this && _wordP && _wordP->_id == id; +} + +int TTconcept::getWordId() const { + return this && _wordP ? _wordP->_id : 0; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_concept.h b/engines/titanic/true_talk/tt_concept.h new file mode 100644 index 0000000000..88afb6f28b --- /dev/null +++ b/engines/titanic/true_talk/tt_concept.h @@ -0,0 +1,172 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_CONCEPT_H +#define TITANIC_TT_CONCEPT_H + +#include "titanic/true_talk/tt_string.h" +#include "titanic/true_talk/tt_word.h" + +namespace Titanic { + +enum ScriptType { ST_UNKNOWN_SCRIPT = 0, ST_ROOM_SCRIPT = 1, ST_NPC_SCRIPT = 2 }; + +class TTscriptBase; +class TTword; + +class TTconcept { +private: + TTstring _string1; + int _field1C; + TTword *_word2P; + int _field30; + bool _flag; + int _status; +private: + /** + * Sets the status of the concept + */ + bool setStatus(); + + /** + * Sets the script type and resets other fields + */ + void setScriptType(ScriptType scriptType); + + /** + * Sets up the concept for a word reference + */ + int initializeWordRef(TTword *word); + + /** + * Resets the concept + */ + void reset(); + + /** + * Initialize inner data for the concept from a given source concept + */ + void initialize(TTconcept &src); +public: + TTconcept *_nextP; + TTscriptBase *_scriptP; + TTword *_wordP; + int _scriptType; + int _field14; + int _field20; + int _field34; + TTstring _string2; +public: + TTconcept(); + TTconcept(TTscriptBase *script, ScriptType scriptType); + TTconcept(TTword *word, ScriptType scriptType = ST_UNKNOWN_SCRIPT); + TTconcept(TTconcept &src); + ~TTconcept(); + + /** + * Destroys any attached sibling concepts to the given concept + */ + void deleteSiblings(); + + /** + * Copies data from a source concept + */ + void copyFrom(TTconcept *src); + + /** + * Compares the name of the associated word, if any, to the passed string + */ + bool compareTo(const char *str) const; + + /** + * Compares the concept to the specified word + */ + bool compareTo(TTword *word) const; + + /** + * Set an owner for the concept + */ + int setOwner(TTconcept *src); + + /** + * Set an owner for the concept + */ + int setOwner(TTword *src, bool dontDup); + + /** + * Return the status of the concept + */ + int getStatus() const { return _status; } + + /** + * True true if the concept is valid + */ + bool isValid() const { return _status == SS_VALID; } + + /** + * Returns true if the word is of the specified class + */ + bool isWordClass(WordClass wordClass) const { + return _wordP && _wordP->isClass(wordClass); + } + + void setFlag(bool val) { _flag = val; } + void set1C(int val) { _field1C = val; } + int get20() const { return _field20; } + int getState() const { return _field34; } + + bool checkWordId1() const; + bool checkWordId2() const; + bool checkWordId3() const; + bool checkWordClass() const; + + /** + * Return text assocaited with the concept's word or script + */ + const TTstring getText(); + + /** + * Find a word by Id + */ + TTconcept *findByWordId(int id); + + /** + * Find a word by it's class + */ + TTconcept *findByWordClass(WordClass wordClass); + + TTconcept *findBy20(int val); + + /** + * Returns true if the concept has a word with a given Id + */ + bool isWordId(int id) const; + + /** + * If a word is associated, return it's Id + */ + int getWordId() const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_CONCEPT_H */ diff --git a/engines/titanic/true_talk/tt_concept_node.cpp b/engines/titanic/true_talk/tt_concept_node.cpp new file mode 100644 index 0000000000..454ca59971 --- /dev/null +++ b/engines/titanic/true_talk/tt_concept_node.cpp @@ -0,0 +1,165 @@ +/* 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_concept_node.h" +#include "titanic/true_talk/script_handler.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTconceptNode::TTconceptNode() : _concept0P(_concepts[0]), _concept1P(_concepts[1]), + _concept2P(_concepts[2]), _concept3P(_concepts[3]), _concept4P(_concepts[4]), + _concept5P(_concepts[5]), _field18(0), _field1C(0), _nextP(nullptr), _status(0) { +} + +TTconceptNode::TTconceptNode(const TTconceptNode &src) : _concept0P(_concepts[0]), _concept1P(_concepts[1]), + _concept2P(_concepts[2]), _concept3P(_concepts[3]), _concept4P(_concepts[4]), + _concept5P(_concepts[5]), _field18(0), _field1C(0), _nextP(nullptr), _status(0) { + if (src._status) { + _status = SS_5; + } else { + for (int idx = 0; idx < 6; ++idx) { + if (src._concepts[idx]) { + _concepts[idx] = new TTconcept(*src._concepts[idx]); + if (!_concepts[idx]->isValid()) + _status = SS_11; + } + } + + _field18 = src._field18; + _field1C = src._field1C; + _nextP = src._nextP; + } +} + +void TTconceptNode::deleteSiblings() { + // Iterate through the linked chain of nodes, deleting each in turn + for (TTconceptNode *curP = _nextP, *nextP = nullptr; nextP; curP = nextP) { + nextP = curP->_nextP; + delete curP; + } + + _nextP = nullptr; +} + +TTconcept **TTconceptNode::setConcept(int conceptIndex, TTconcept *src) { + TTconcept **conceptPP = nullptr; + switch (conceptIndex) { + case 1: + conceptPP = &_concept1P; + break; + case 2: + conceptPP = &_concept2P; + break; + case 3: + conceptPP = &_concept3P; + break; + case 4: + conceptPP = &_concept4P; + break; + case 5: + conceptPP = &_concept5P; + break; + default: + break; + } + + bool isPronoun = false; + StringArray &pronouns = g_vm->_scriptHandler->_parser._pronouns; + for (uint idx = 0; idx < pronouns.size() && !isPronoun; ++idx) { + isPronoun = pronouns[idx] == src->getText(); + } + + CScriptHandler &scrHandler = *g_vm->_exeResources._owner; + if (!isPronoun) { + switch (conceptIndex) { + case 0: + delete scrHandler._concept2P; + scrHandler._concept2P = new TTconcept(*src); + break; + + case 1: + delete scrHandler._concept4P; + scrHandler._concept4P = new TTconcept(*src); + break; + + case 2: + delete scrHandler._concept1P; + scrHandler._concept1P = new TTconcept(*src); + break; + + default: + break; + } + } + + return conceptPP; +} + +int TTconceptNode::replaceConcept(int mode, int conceptIndex, TTconcept *concept) { + TTconcept **conceptPP = setConcept(conceptIndex, concept); + + if (mode == 0 || (mode == 1 && !*conceptPP)) { + if (!concept || !concept->isValid()) + return SS_5; + + if (mode == 0 && *conceptPP) { + delete *conceptPP; + } + + *conceptPP = new TTconcept(*concept); + return (*conceptPP)->isValid() ? SS_VALID : SS_11; + } else { + return SS_1; + } +} + +int TTconceptNode::changeConcept(int mode, TTconcept **conceptPP, int conceptIndex) { + TTconcept **newConceptPP = setConcept(conceptIndex, *conceptPP); + + if (mode == 0 || (mode == 1 && !*newConceptPP)) { + if (!*conceptPP) + return SS_5; + + delete *newConceptPP; + *newConceptPP = new TTconcept(**conceptPP); + return SS_VALID; + } else { + return SS_1; + } +} + +bool TTconceptNode::createConcept(int mode, int conceptIndex, TTword *word) { + TTconcept *newConcept = new TTconcept(word, ST_UNKNOWN_SCRIPT); + TTconcept **conceptPP = setConcept(conceptIndex, newConcept); + + if (mode == 0 || (mode == 1 && !*conceptPP)) { + delete *conceptPP; + *conceptPP = newConcept; + return false; + } else { + delete newConcept; + return true; + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_concept_node.h b/engines/titanic/true_talk/tt_concept_node.h new file mode 100644 index 0000000000..9a1c3a9912 --- /dev/null +++ b/engines/titanic/true_talk/tt_concept_node.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_CONCEPT_NODE_H +#define TITANIC_TT_CONCEPT_NODE_H + +#include "titanic/true_talk/tt_concept.h" + +namespace Titanic { + +class TTconceptNode { +public: + TTconcept *_concepts[6]; + TTconcept *&_concept0P; + TTconcept *&_concept1P; + TTconcept *&_concept2P; + TTconcept *&_concept3P; + TTconcept *&_concept4P; + TTconcept *&_concept5P; + int _field18; + int _field1C; + TTconceptNode *_nextP; + int _status; +public: + TTconceptNode(); + TTconceptNode(const TTconceptNode &src); + + /** + * Delete any sibling chain attached to this node + */ + void deleteSiblings(); + + void set18(int val) { _field18 = val; } + int get18() const { return _field18; } + + /** + * Returns true if the node is valid + */ + bool isValid() const { return _status == SS_VALID; } + + TTconcept **setConcept(int conceptIndex, TTconcept *src); + int replaceConcept(int mode, int conceptIndex, TTconcept *concept); + int changeConcept(int mode, TTconcept **conceptPP, int conceptIndex); + bool createConcept(int mode, int conceptIndex, TTword *word); + + int concept1WordId() const { + return _concept1P ? _concept1P->getWordId() : 0; + } + int concept5WordId() const { + return _concept5P ? _concept5P->getWordId() : 0; + } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_CONCEPT_NODE_H */ diff --git a/engines/titanic/true_talk/tt_hist.cpp b/engines/titanic/true_talk/tt_hist.cpp new file mode 100644 index 0000000000..fae9ae6286 --- /dev/null +++ b/engines/titanic/true_talk/tt_hist.cpp @@ -0,0 +1,36 @@ +/* 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_hist.h" +#include "titanic/true_talk/tt_sentence.h" + +namespace Titanic { + +TThist::TThist(TTsentence *sentence) : _status(0) { + _sentence = new TTsentence(sentence); +} + +TThist::~TThist() { + delete _sentence; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_hist.h b/engines/titanic/true_talk/tt_hist.h new file mode 100644 index 0000000000..f67a0387c5 --- /dev/null +++ b/engines/titanic/true_talk/tt_hist.h @@ -0,0 +1,47 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_HIST_H +#define TITANIC_TT_HIST_H + +namespace Titanic { + +class TTsentence; + +class TThist { +protected: + int _field0; + TTsentence *_sentence; + int _status; +public: + TThist(TTsentence *sentence); + virtual ~TThist(); +}; + +class TTscriptHist : public TThist { +public: + TTscriptHist(TTsentence *sentence) : TThist(sentence) {} +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_HIST_H */ diff --git a/engines/titanic/true_talk/tt_major_word.cpp b/engines/titanic/true_talk/tt_major_word.cpp new file mode 100644 index 0000000000..18a56a40be --- /dev/null +++ b/engines/titanic/true_talk/tt_major_word.cpp @@ -0,0 +1,70 @@ +/* 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_major_word.h" + +namespace Titanic { + +bool TTmajorWord::_staticFlag; + +TTmajorWord::TTmajorWord(const TTstring &str, WordClass wordClass, int val2, int val3) : + TTword(str, wordClass, val2), _field2C(val3) { +} + +TTmajorWord::TTmajorWord(const TTmajorWord *src) : TTword(src) { + if (src->getStatus()) { + _field2C = 0; + _status = SS_5; + } else { + _field2C = src->_field2C; + } +} + +int TTmajorWord::saveData(SimpleFile *file, int val) const { + int result = TTword::save(file); + if (!result) { + file->writeFormat("%1.0d", val); + file->writeFormat("%c", '\n'); + if (_synP) + result = _synP->save(file); + } + + return result; +} + +TTword *TTmajorWord::copy() const { + TTmajorWord *returnWordP = new TTmajorWord(this); + returnWordP->_status = _status; + if (!_status) { + _staticFlag = false; + return returnWordP; + } else if (_status == SS_13 && !_staticFlag) { + _staticFlag = true; + delete returnWordP; + return copy(); + } else { + delete returnWordP; + return nullptr; + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_major_word.h b/engines/titanic/true_talk/tt_major_word.h new file mode 100644 index 0000000000..c9a708abfb --- /dev/null +++ b/engines/titanic/true_talk/tt_major_word.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_MAJOR_WORD_H +#define TITANIC_TT_MAJOR_WORD_H + +#include "titanic/true_talk/tt_word.h" + +namespace Titanic { + +class TTmajorWord : public TTword { +private: + static bool _staticFlag; +protected: + int _field2C; +protected: + /** + * Dumps data for the word to a file + */ + int saveData(SimpleFile *file, int val) const; +public: + TTmajorWord(const TTstring &str, WordClass wordClass, int val2, int val3); + TTmajorWord(const TTmajorWord *src); + + /** + * Creates a copy of the word + */ + virtual TTword *copy() const; + + virtual bool proc2(int val) const { return _field2C == val; } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_MAJOR_WORD_H */ diff --git a/engines/titanic/true_talk/tt_node.cpp b/engines/titanic/true_talk/tt_node.cpp new file mode 100644 index 0000000000..cbbb1dd756 --- /dev/null +++ b/engines/titanic/true_talk/tt_node.cpp @@ -0,0 +1,89 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/tt_node.h" + +namespace Titanic { + +TTnode::TTnode() : _priorP(nullptr), _nextP(nullptr) { +} + +TTnode::~TTnode() { + detach(); +} + +void TTnode::addToTail(TTnode *newNode) { + TTnode *tail = getTail(); + tail->_nextP = newNode; + newNode->_priorP = this; +} + +void TTnode::addToHead(TTnode *newNode) { + TTnode *head = getHead(); + head->_priorP = newNode; + newNode->_nextP = head; +} + +void TTnode::detach() { + if (_priorP) + _priorP->_nextP = _nextP; + + if (_nextP) + _nextP->_priorP = _priorP; +} + +void TTnode::deleteSiblings() { + // Detach current node from prior one, if there is one + if (_priorP) + _priorP->_nextP = nullptr; + + // Iterate through the linked chain of nodes, deleting each in turn + for (TTnode *curP = _nextP, *nextP = nullptr; nextP; curP = nextP) { + nextP = curP->_nextP; + delete curP; + } +} + +TTnode *TTnode::getHead() { + if (_priorP == nullptr) + return this; + + TTnode *node = _priorP; + while (node->_priorP) + node = node->_priorP; + + return node; +} + +TTnode *TTnode::getTail() { + if (_nextP == nullptr) + return this; + + TTnode *node = _nextP; + while (node->_nextP) + node = node->_nextP; + + return node; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_node.h b/engines/titanic/true_talk/tt_node.h new file mode 100644 index 0000000000..36d44288fd --- /dev/null +++ b/engines/titanic/true_talk/tt_node.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_NODE_H +#define TITANIC_TT_NODE_H + +namespace Titanic { + +class TTnode { +public: + TTnode *_priorP; + TTnode *_nextP; +public: + TTnode(); + virtual ~TTnode(); + + /** + * Adds a new node at the beginning of the linked list + */ + void addToHead(TTnode *newNode); + + /** + * Links the passed node to this node as a linked list + */ + void addToTail(TTnode *newNode); + + /** + * Detaches a node from any predecessor and/or successor + */ + void detach(); + + /** + * Delete any sibling chain attached to this node + */ + void deleteSiblings(); + + /** + * Returns the first node at the beginning of a linked list of nodes + */ + TTnode *getHead(); + + /** + * Returns the final node at the end of the linked list of nodes + */ + TTnode *getTail(); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_NODE_H */ diff --git a/engines/titanic/true_talk/tt_npc_script.cpp b/engines/titanic/true_talk/tt_npc_script.cpp new file mode 100644 index 0000000000..61c3b0e00c --- /dev/null +++ b/engines/titanic/true_talk/tt_npc_script.cpp @@ -0,0 +1,1009 @@ +/* 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 "common/algorithm.h" +#include "common/textconsole.h" +#include "titanic/messages/messages.h" +#include "titanic/pet_control/pet_control.h" +#include "titanic/true_talk/tt_npc_script.h" +#include "titanic/true_talk/tt_sentence.h" +#include "titanic/true_talk/true_talk_manager.h" +#include "titanic/game_manager.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTsentenceEntries *TTnpcScript::_defaultEntries; + +static const char *const ITEMS[] = { + "chicken", "napkin", "parrot", "moth", "fuse", "eye", "nose", "ear", "mouth", + "auditorycenter", "visioncenter", "olfactorycenter", "speechcenter", "stick", + "longstick", "bomb", "lemon", "puree", "television", "hammer", nullptr +}; + +struct ItemRec { + const char *const _name; + uint _id; +}; +static const ItemRec ARRAY1[] = { + { ITEMS[0], 290138 }, + { ITEMS[1], 290139 }, + { ITEMS[2], 290141 }, + { ITEMS[3], 290142 }, + { ITEMS[4], 290153 }, + { ITEMS[5], 290158 }, + { ITEMS[6], 290159 }, + { ITEMS[7], 290160 }, + { ITEMS[8], 290161 }, + { ITEMS[9], 290162 }, + { ITEMS[10], 290163 }, + { ITEMS[11], 290164 }, + { ITEMS[12], 290165 }, + { ITEMS[13], 290166 }, + { ITEMS[14], 290166 }, + { ITEMS[15], 290178 }, + { ITEMS[16], 290174 }, + { ITEMS[17], 290175 }, + { ITEMS[18], 290176 }, + { ITEMS[19], 290179 }, + { nullptr, 0 } +}; +static const uint ARRAY2[] = { + 290167, 290178, 290183, 290144, 290148, 290151, 290154, 290156, 290158, 290159, 290160, 290161, + 290162, 290163, 290164, 290165, 290177, 290181, 0 +}; +static const uint RANDOM1[] = { + 290184, 290185, 290187, 290188, 290190, 290191, 290193, 290195, 290196, 290197, 290198, 290199, + 290202, 290205, 0 +}; +static const uint RANDOM2[] = { + 290186, 290187, 290188, 290190, 290191, 290193, 290194, 290195, 290196, 290197, 290198, 290199, + 290200, 290201, 290202, 290204, 290205, 0 +}; +static const uint RANDOM3[] = { + 290188, 290190, 290192, 290194, 290197, 290200, 290201, 290202, 290204, 290205, 0 +}; +static const uint RANDOM4[] = { + 290206, 290207, 290209, 290210, 290211, 290212, 290216, 290217, 290218, 290219, 290222, 290223, 0 +}; +static const uint RANDOM5[] = { + 290208, 290209, 290210, 290211, 290212, 290214, 290215, 290216, 290217, 290218, 290219, 290221, + 290222, 290223, 0 +}; +static const uint RANDOM6[] = { + 290210, 290211, 290213, 290214, 290215, 290220, 290221, 290222, 290223, 0 +}; +static const uint RANDOM7[] = { + 290225, 290226, 290228, 290229, 290230, 290232, 290231, 290235, 290236, 290237, 290238, 290241, 0 +}; +static const uint RANDOM8[] = { + 290227, 290228, 290229, 290230, 290231, 290232, 290233, 290234, 290235, 290236, 290237, 290238, + 290240, 290241, 0 +}; +static const uint RANDOM9[] = { + 290228, 290229, 290230, 290232, 290233, 290234, 290239, 290240, 290241, 0 +}; + +/*------------------------------------------------------------------------*/ + +TTnpcData::TTnpcData() { + Common::fill(&_array[0], &_array[136], 0); +} + +void TTnpcData::resetFlags() { + Common::fill(&_array[20], &_array[136], 0); +} + +/*------------------------------------------------------------------------*/ + +TTnpcScriptBase::TTnpcScriptBase(int charId_, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTscriptBase(0, charClass, v2, charName, v3, v4, v5, v6, v7), + _charId(charId_), _field54(0), _val2(val2) { +} + +/*------------------------------------------------------------------------*/ + +void TTnpcScript::init() { + _defaultEntries = new TTsentenceEntries(); + _defaultEntries->load("Sentences/Default"); +} + +void TTnpcScript::deinit() { + delete _defaultEntries; + _defaultEntries = nullptr; +} + +TTnpcScript::TTnpcScript(int charId_, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, int v5, int v6, int v7) : + TTnpcScriptBase(charId_, charClass, v2, charName, v3, val2, v4, v5, v6, v7), + _entryCount(0), _field68(0), _field6C(0), _rangeResetCtr(0), + _currentDialNum(0), _dialDelta(0), _field7C(0), _itemStringP(nullptr), _field2CC(false) { + CTrueTalkManager::_v2 = 0; + Common::fill(&_dialValues[0], &_dialValues[DIALS_ARRAY_COUNT], 0); + + if (!CTrueTalkManager::_v10) { + Common::fill(&CTrueTalkManager::_v11[0], &CTrueTalkManager::_v11[41], 0); + CTrueTalkManager::_v10 = true; + } + + resetFlags(); +} + +void TTnpcScript::loadResponses(const char *name, int valuesPerResponse) { + _valuesPerResponse = valuesPerResponse; + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + TTnpcScriptResponse sr; + sr._tag = r->readUint32LE(); + for (int idx = 0; idx < valuesPerResponse; ++idx) + sr._values[idx] = r->readUint32LE(); + + _responses.push_back(sr); + } + + delete r; +} + +void TTnpcScript::loadRanges(const char *name) { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); + + while (r->pos() < r->size()) { + Common::Array<uint> values; + uint id = r->readUint32LE(); + bool isRandom = r->readByte(); + bool isSequential = r->readByte(); + + uint v; + do { + v = r->readUint32LE(); + values.push_back(v); + } while (v); + + addRange(id, values, isRandom, isSequential); + } + + delete r; +} + +void TTnpcScript::resetFlags() { + _data.resetFlags(); + _field2CC = false; +} + +void TTnpcScript::setupDials(int dial1, int dial2, int dial3) { + _dialValues[0] = dial1; + _dialValues[1] = dial2; + _dialValues[2] = dial3; + _currentDialNum = getRandomNumber(3) - 1; + _dialDelta = getRandomNumber(5) + 6; + + if (_dialValues[0] > 70) + _dialDelta = -_dialDelta; +} + +void TTnpcScript::addResponse(int id) { + if (id > 200000) + id = getDialogueId(id); + + handleWord(id); + TTscriptBase::addResponse(id); +} + +int TTnpcScript::chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) { + for (uint idx = 0; idx < _responses.size(); ++idx) { + const TTnpcScriptResponse &response = _responses[idx]; + + if (response._tag == tag) { + if (_valuesPerResponse == 1) { + selectResponse(response._values[0]); + } else { + int valIndex = getRandomNumber(response.size()) - 1; + uint diagId = getDialogueId(response._values[valIndex]); + addResponse(diagId); + } + + applyResponse(); + return 2; + } + } + + return 1; +} + +int TTnpcScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { + return processEntries(&_entries, _entryCount, roomScript, sentence); +} + +int TTnpcScript::proc8() const { + return 0; +} + +int TTnpcScript::proc11() const { + return 2; +} + +int TTnpcScript::proc12() const { + return 1; +} + +void TTnpcScript::selectResponse(int id) { + if (id >= 200000 && id <= 290264) + id = getDialogueId(id); + + addResponse(id); +} + +bool TTnpcScript::handleWord(uint id) const { + if (_words.empty()) + return false; + + for (uint idx = 0; idx < _words.size(); ++idx) { + const TTwordEntry &we = _words[idx]; + if (we._id == id) { + TTstring str(we._text); + g_vm->_scriptHandler->handleWord(&str); + return true; + } + } + + g_vm->_scriptHandler->handleWord(nullptr); + return true; +} + +int TTnpcScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder) { + if (_quotes.empty()) + return 1; + + + int loopCounter = 0; + for (uint idx = 0; idx < _quotes.size() && loopCounter < 2; ++idx) { + const TThandleQuoteEntry *qe = &_quotes[idx]; + + if (!qe->_index) { + // End of list; start at beginning again + ++loopCounter; + idx = 0; + qe = &_quotes[0]; + } + + if (qe->_index == val && ( + (tagId == 0 && loopCounter == 2) || + (qe->_tagId < MKTAG('A', 'A', 'A', 'A')) || + (qe->_tagId == tagId) + )) { + uint foundTagId = qe->_tagId; + if (foundTagId > 0 && foundTagId < 100) { + if (!tagId) + foundTagId >>= 1; + if (getRandomNumber(100) < foundTagId) + return 1; + } + + uint dialogueId = qe->_dialogueId; + if (dialogueId >= _quotes._rangeStart && dialogueId <= _quotes._rangeEnd) { + dialogueId -= _quotes._rangeStart; + if (dialogueId > 3) + error("Invalid dialogue index in BarbotScript"); + + const int RANDOM_LIMITS[4] = { 30, 50, 70, 60 }; + int rangeLimit = RANDOM_LIMITS[dialogueId]; + int dialRegion = getDialRegion(0); + + if (dialRegion != 1) { + rangeLimit = MAX(rangeLimit - 20, 20); + } + + dialogueId = (((int)remainder + 25) % 100) >= rangeLimit + ? _quotes._tag1 : _quotes._tag2; + } + + addResponse(getDialogueId(dialogueId)); + applyResponse(); + return 2; + } + } + + return 1; + +} + +uint TTnpcScript::getRangeValue(uint id) { + TTscriptRange *range = findRange(id); + if (!range) + return 0; + + switch (range->_mode) { + case SF_RANDOM: { + uint count = range->_values.size(); + + uint index = getRandomNumber(count) - 1; + if (count > 1 && range->_values[index] == range->_priorIndex) { + for (int retry = 0; retry < 8 && index != range->_priorIndex; ++retry) + index = getRandomNumber(count) - 1; + } + + range->_priorIndex = index; + return range->_values[index]; + } + + case SF_SEQUENTIAL: { + // Get the next value from the array sequentially + int val = range->_values[range->_priorIndex]; + if (!val) { + // Reached end of array, so reset back to start + range->_priorIndex = 1; + val = range->_values[1]; + } + + ++range->_priorIndex; + return val; + } + + default: + if (range->_values[range->_priorIndex]) + return range->_values[range->_priorIndex++]; + + range->_priorIndex = 1; + ++_rangeResetCtr; + return range->_values[0]; + } +} + +void TTnpcScript::resetRange(int id) { + TTscriptRange *range = findRange(id); + if (range && range->_mode != SF_RANDOM) + range->_priorIndex = 0; +} + +int TTnpcScript::updateState(uint oldId, uint newId, int index) { + return newId; +} + +int TTnpcScript::preResponse(uint id) { + return 0; +} + +const TTscriptMapping *TTnpcScript::getMapping(int index) { + if (index >= 0 && index < (int)_mappings.size()) + return &_mappings[index]; + return nullptr; +} + +int TTnpcScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { + return 0; +} + +void TTnpcScript::save(SimpleFile *file) { + file->writeNumber(charId()); + saveBody(file); + + file->writeNumber(4); + file->writeNumber(_rangeResetCtr); + file->writeNumber(_currentDialNum); + file->writeNumber(_dialDelta); + file->writeNumber(_field7C); + + file->writeNumber(10); + for (int idx = 0; idx < 10; ++idx) + file->writeNumber(_data[idx]); +} + +void TTnpcScript::load(SimpleFile *file) { + loadBody(file); + + int count = file->readNumber(); + _rangeResetCtr = file->readNumber(); + _currentDialNum = file->readNumber(); + _dialDelta = file->readNumber(); + _field7C = file->readNumber(); + + for (int idx = count; idx > 4; --idx) + file->readNumber(); + + count = file->readNumber(); + for (int idx = 0; idx < count; ++idx) { + int v = file->readNumber(); + if (idx < 10) + _data[idx] = v; + } +} + +void TTnpcScript::saveBody(SimpleFile *file) { + int count = proc31(); + file->writeNumber(count); + + if (count > 0) { + for (uint idx = 0; idx < _ranges.size(); ++idx) { + const TTscriptRange &item = _ranges[idx]; + if (item._mode == SF_RANDOM && item._priorIndex) { + file->writeNumber(item._id); + file->writeNumber(item._priorIndex); + } + } + } +} + +void TTnpcScript::loadBody(SimpleFile *file) { + int count = file->readNumber(); + preLoad(); + + for (int index = 0; index < count; index += 2) { + int id = file->readNumber(); + int val = file->readNumber(); + + for (uint idx = 0; idx < _ranges.size(); ++idx) { + TTscriptRange &item = _ranges[idx]; + if (item._id == (uint)id) { + item._priorIndex = val; + break; + } + } + } +} + +int TTnpcScript::proc31() const { + int count = 0; + for (uint idx = 0; idx < _ranges.size(); ++idx) { + const TTscriptRange &item = _ranges[idx]; + if (item._mode != SF_RANDOM && item._priorIndex) + ++count; + } + + return count * 2; +} + +void TTnpcScript::setDialRegion(int dialNum, int region) { + if (dialNum < DIALS_ARRAY_COUNT) + _dialValues[dialNum] = region * 100; + + if (g_vm->_trueTalkManager) { + CPetControl *petControl = getPetControl(g_vm->_trueTalkManager->getGameManager()); + if (petControl) + petControl->playSound(1); + } +} + +void TTnpcScript::setDial(int dialNum, int value) { + if (dialNum < DIALS_ARRAY_COUNT) { + int oldRegion = getDialRegion(dialNum); + + int newRegion = 1; + if (value < 50) + newRegion = 0; + else if (value > 150) + newRegion = 2; + + if (oldRegion == newRegion) + setDialRegion(dialNum, newRegion); + + _dialValues[dialNum] = value; + } + + if (g_vm->_trueTalkManager) { + CPetControl *petControl = getPetControl(g_vm->_trueTalkManager->getGameManager()); + if (petControl) + petControl->convResetDials(); + } +} + +int TTnpcScript::getDialRegion(int dialNum) const { + if (dialNum < DIALS_ARRAY_COUNT) { + int value = _dialValues[dialNum]; + if (value < 50) + return 0; + else if (value > 150) + return 2; + else + return 1; + } else { + return 0; + } +} + +int TTnpcScript::getDialLevel(uint dialNum, bool randomizeFlag) { + int result = _dialValues[dialNum]; + if (randomizeFlag) { + bool lowFlag = result <= 50; + result = CLIP(result + (int)getRandomNumber(18) - 9, 0, 100); + + if (lowFlag) { + result = MIN(result, 46); + } else { + result = MAX(result, 54); + } + } + + return result; +} + +bool TTnpcScript::randomResponse(uint index) { + return false; +} + +uint TTnpcScript::translateId(uint id) const { + for (uint idx = 0; idx < _tagMappings.size(); ++idx) { + if (_tagMappings[idx]._src == id) + return _tagMappings[idx]._dest; + } + + return 0; +} + +void TTnpcScript::preLoad() { + for (uint idx = 0; idx < _ranges.size(); ++idx) + _ranges[idx]._priorIndex = 0; +} + +int TTnpcScript::getRoom54(int roomId) { + TTroomScript *room = g_vm->_trueTalkManager->getRoomScript(roomId); + return room ? room->_field54 : 0; +} + +int TTnpcScript::getValue(int testNum) const { + switch (testNum) { + case 0: + return CTrueTalkManager::_v2; + + case 1: + if (g_vm->_trueTalkManager) + CTrueTalkManager::_v3 = g_vm->_trueTalkManager->getPassengerClass(); + return CTrueTalkManager::_v3; + + case 2: + return CTrueTalkManager::_v4; + + case 3: + return CTrueTalkManager::_v5 != 0; + + case 4: + if (g_vm->_trueTalkManager) { + switch (g_vm->_trueTalkManager->getState14()) { + case 1: + CTrueTalkManager::_v6 = 3; + break; + case 2: + CTrueTalkManager::_v6 = 0; + break; + case 3: + CTrueTalkManager::_v6 = 1; + break; + default: + CTrueTalkManager::_v6 = 2; + break; + } + } + return CTrueTalkManager::_v6; + + case 5: + return CTrueTalkManager::_v7; + + case 6: + return CTrueTalkManager::_v8 != 0; + + case 7: + return !!getRoom54(123); + + default: + return CTrueTalkManager::_v11[testNum]; + } +} + +uint TTnpcScript::getRandomNumber(int max) const { + return 1 + g_vm->getRandomNumber(max - 1); +} + +uint TTnpcScript::getDialogueId(uint tagId) { + if (tagId < 200000) + return tagId; + + // Perform any script specific translation + uint origId = tagId; + if (tagId >= 290000 && tagId <= 290263) + tagId = translateId(tagId); + if (!tagId) + return 0; + + if (!_field2CC) { + _field2CC = true; + int val = translateByArray(tagId); + if (val > 0) { + if (randomResponse(val)) + return 4; + } + } + + uint oldTagId = tagId; + tagId = getRangeValue(tagId); + if (tagId != oldTagId) + tagId = getRangeValue(tagId); + + oldTagId = getDialsBitset(); + uint newId = updateState(origId, tagId, oldTagId); + if (!newId) + return 0; + + int idx = 0; + const TTscriptMapping *tableP; + for (;;) { + tableP = getMapping(idx++); + if (!tableP) + return 0; + + if (tableP->_id == newId) + break; + } + uint newVal = tableP->_values[oldTagId]; + + // First slot dialogue Ids + idx = 0; + int *arrP = _data.getSlot(0); + while (idx < 4 && arrP[idx]) + ++idx; + + if (idx == 4) + return newVal; + arrP[idx] = origId; + + // Second slot dialogue Ids + idx = 0; + arrP = _data.getSlot(1); + while (idx < 4 && arrP[idx]) + ++idx; + + if (idx == 4) + return newVal; + arrP[idx] = newVal; + + return newVal; +} + +int TTnpcScript::translateByArray(int id) { + for (uint idx = 1, arrIndex = 35; idx < 15; ++idx, arrIndex += 8) { + if (_data[idx - 1] == id && _data[idx] == 0) + return idx; + } + + return -1; +} + +CPetControl *TTnpcScript::getPetControl(CGameManager *gameManager) { + if (gameManager && gameManager->_project) + return gameManager->_project->getPetControl(); + return nullptr; +} + +int TTnpcScript::processEntries(const TTsentenceEntries *entries, uint entryCount, const TTroomScript *roomScript, const TTsentence *sentence) { + if (!entries) + return SS_1; + if (!entryCount) + // No count specified, so use entire list + entryCount = entries->size(); + int entryId = _field2C; + + for (uint loopCtr = 0; loopCtr < 2; ++loopCtr) { + for (uint entryCtr = 0; entryCtr < entryCount; ++entryCtr) { + const TTsentenceEntry &entry = (*entries)[entryCtr]; + if (entry._field4 != entryId && (loopCtr == 0 || entry._field4)) + continue; + + bool flag; + if (entry._fieldC || entry._string10.empty()) { + flag = sentence->fn1(entry._string8, entry._fieldC, + entry._string14, entry._string18, entry._string1C, + entry._field20, entry._field28, 0, nullptr); + } else { + flag = sentence->fn3(entry._string8, entry._string10, + entry._string14, entry._string18, entry._string1C, + entry._string24, entry._field28, 0, nullptr); + } + + if (flag) { + if (entry._field2C) { + bool flag2 = true; + if (entry._field2C & 0x1000000) + flag2 = sentence->isConcept34(1); + + if (entry._field2C & 0x2000000) + flag2 = sentence->isConcept34(0) || sentence->isConcept34(4); + + if (!flag2) { + flag = false; + } else { + int result = doSentenceEntry(entry._field2C & 0xFFFFFF, &entry._field0, + roomScript, sentence); + if (result == 2) + return 2; + flag = !result; + } + } + + if (flag) { + int dialogueId = getDialogueId(entry._field0); + int id; + if (!dialogueId) + return 1; + else if (dialogueId == 4) + return 2; + addResponse(dialogueId); + + id = preResponse(dialogueId); + if (id) + addResponse(getDialogueId(id)); + applyResponse(); + + if (entry._field30) + postResponse(entry._field30, &entry, roomScript, sentence); + + return 2; + } + } + } + } + + return 1; +} + +bool TTnpcScript::defaultProcess(const TTroomScript *roomScript, const TTsentence *sentence) { + uint remainder; + TTtreeResult results[32]; + const TTstring &line = sentence->_normalizedLine; + + uint tagId = g_vm->_trueTalkManager->_quotes.find(line.c_str()); + int val = g_vm->_trueTalkManager->_quotesTree.search(line.c_str(), TREE_1, results, tagId, &remainder); + + if (val > 0) { + if (!handleQuote(roomScript, sentence, val, tagId, remainder)) + return true; + } + + if (tagId) { + if (chooseResponse(roomScript, sentence, tagId) == 2) + return true; + } + + return false; +} + +void TTnpcScript::addRange(uint id, const Common::Array<uint> &values, bool isRandom, bool isSequential) { + _ranges.push_back(TTscriptRange(id, values, isRandom, isSequential)); +} + +TTscriptRange *TTnpcScript::findRange(uint id) { + for (uint idx = 0; idx < _ranges.size(); ++idx) { + if (_ranges[idx]._id == id) + return &_ranges[idx]; + } + + return nullptr; +} + +void TTnpcScript::checkItems(const TTroomScript *roomScript, const TTsentence *sentence) { + _field2CC = 0; + ++CTrueTalkManager::_v2; + + if (sentence) { + if (!_itemStringP || getRandomNumber(100) > 80) { + for (const char *const *strP = &ITEMS[0]; *strP; ++strP) { + if (sentence->localWord(*strP)) { + _itemStringP = *strP; + break; + } + } + } + + if (sentence->localWord("bomb")) + _field7C = 1; + } +} + +bool TTnpcScript::addRandomResponse(bool flag) { + if (getValue(1) > 3) + return false; + + const uint *data; + if (flag) { + if (getValue(1) == 2) + data = RANDOM8; + else if (getValue(1) == 1) + data = RANDOM7; + else + data = RANDOM9; + } else if (getRandomBit()) { + if (getValue(1) == 2) + data = RANDOM2; + else if (getValue(1) == 1) + data = RANDOM1; + else + data = RANDOM3; + } else { + if (getValue(1) == 2) + data = RANDOM5; + else if (getValue(1) == 1) + data = RANDOM4; + else + data = RANDOM6; + } + + // Pick a random entry + uint count = 0; + while (data[count]) + ++count; + uint id = data[getRandomNumber(count - 1)]; + + if (id == 290188 && getRoom54(101)) + id = 290189; + else if (id == 290202 && getRoom54(123)) + id = 290203; + + if (!id) + return false; + id = getDialogueId(id); + if (id == 4) + return true; + if (!id) + return false; + + if (flag) + addResponse(getDialogueId(290224)); + + addResponse(id); + applyResponse(); + return true; +} + +void TTnpcScript::updateCurrentDial(bool changeDial) { + int dialLevel = CLIP(getDialLevel(_currentDialNum) + _dialDelta, 0, 100); + setDial(_currentDialNum, dialLevel); + + bool edgeFlag = false; + if (_dialDelta < 0) { + if (dialLevel < 10 || getRandomNumber(100) > 93) + edgeFlag = true; + } else { + if (dialLevel > 90 || getRandomNumber(100) > 93) + edgeFlag = true; + } + + if (edgeFlag) { + if (changeDial) + _currentDialNum = getRandomNumber(3); + + _dialDelta = getRandomNumber(12) + 3; + dialLevel = getDialLevel(_currentDialNum, false); + if (dialLevel > 50) + _dialDelta = -_dialDelta; + } +} + +bool TTnpcScript::fn10(bool flag) { + if (_itemStringP) { + for (const ItemRec *ir = ARRAY1; ir->_id; ++ir) { + if (!strcmp(ir->_name, _itemStringP)) { + _itemStringP = nullptr; + uint id = getDialogueId(ir->_id); + if (id == 4) { + return true; + } else if (id != 0) { + addResponse(id); + applyResponse(); + return true; + } + break; + } + } + + _itemStringP = nullptr; + } + + if (flag && getRandomNumber(100) > 60) { + int val = getRandomNumber(18) - 1; + + if (val == 0 && !getRoom54(101) && !getRoom54(132)) + val = -1; + else if ((val == 1 && !_field7C) || val == 2) + val = -1; + + if (val >= 0) { + val = getDialogueId(ARRAY2[val]); + if (val == 4) { + return true; + } else { + addResponse(val); + applyResponse(); + return true; + } + } + } + + return false; +} + +bool TTnpcScript::getStateValue() const { + if (!CTrueTalkManager::_currentNPC) + return false; + + CGameObject *bomb; + if (CTrueTalkManager::_currentNPC->find("Bomb", &bomb, FIND_GLOBAL) && bomb) { + CTrueTalkGetStateValueMsg stateMsg(10, -1000); + stateMsg.execute(bomb); + if (stateMsg._stateVal) + return true; + } + + return false; +} + +bool TTnpcScript::sentence2C(const TTsentence *sentence) { + return sentence->_field2C >= 2 && sentence->_field2C <= 7; +} + +void TTnpcScript::getAssignedRoom(int *roomNum, int *floorNum, int *elevatorNum) const { + if (roomNum) + *roomNum = 5; + if (floorNum) + *floorNum = 40; + if (elevatorNum) + *elevatorNum = 3; + + CGameManager *gameManager = g_vm->_trueTalkManager->getGameManager(); + CPetControl *petControl = getPetControl(gameManager); + if (petControl) { + if (roomNum) + *roomNum = petControl->getAssignedRoomNum(); + if (floorNum) + *floorNum = petControl->getAssignedFloorNum(); + if (elevatorNum) + *elevatorNum = petControl->getAssignedElevatorNum(); + } + + if (floorNum) + *floorNum = CLIP(*floorNum, 1, 42); + if (roomNum) + *roomNum = CLIP(*roomNum, 1, 18); + if (elevatorNum) + *elevatorNum = CLIP(*elevatorNum, 1, 4); +} + +void TTnpcScript::setResponseFromArray(int index, int id) { + if (index >= 0 && index <= 15) { + deleteResponses(); + if (id) + addResponse(getDialogueId(id)); + + // Add any loaded responses + int *vals = _data.getSlot(index + 1); + for (int idx = 0; idx < 4; ++idx) { + if (vals[idx]) + addResponse(vals[idx]); + } + applyResponse(); + + // Clear out the values used + if (index) + Common::fill(vals, vals + 4, 0); + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_npc_script.h b/engines/titanic/true_talk/tt_npc_script.h new file mode 100644 index 0000000000..b9afdfad8a --- /dev/null +++ b/engines/titanic/true_talk/tt_npc_script.h @@ -0,0 +1,355 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_NPC_SCRIPT_H +#define TITANIC_TT_NPC_SCRIPT_H + +#include "titanic/support/simple_file.h" +#include "titanic/true_talk/tt_script_base.h" +#include "titanic/true_talk/script_support.h" + +namespace Titanic { + +#define DIALS_ARRAY_COUNT 10 + +class CGameManager; +class CPetControl; +class TTroomScript; + +struct TTnpcData { +private: + int _array[136]; +public: + TTnpcData(); + int &operator[](int idx) { return _array[idx]; } + int *getSlot(int idx) { return &_array[16 + idx * 4]; } + void resetFlags(); +}; + +class TTnpcScriptBase : public TTscriptBase { +protected: + int _field54; + int _val2; +public: + int _charId; +public: + TTnpcScriptBase(int charId, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, + int v5, int v6, int v7); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag) = 0; + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence) = 0; + + virtual int proc8() const = 0; + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(uint id) = 0; + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id) = 0; + + virtual int proc11() const = 0; + virtual int proc12() const = 0; + + int charId() const { return _charId; } +}; + +class TTnpcScript : public TTnpcScriptBase { +private: + int translateByArray(int id); +protected: + static TTsentenceEntries *_defaultEntries; +protected: + Common::Array<TTnpcScriptResponse> _responses; + int _valuesPerResponse; + Common::Array<TTscriptRange> _ranges; + TTscriptMappings _mappings; + TTsentenceEntries _entries; + TTtagMappings _tagMappings; + TTwordEntries _words; + TThandleQuoteEntries _quotes; + int _entryCount; + int _field68; + int _field6C; + int _rangeResetCtr; + int _currentDialNum; + int _dialDelta; + int _field7C; + const char *_itemStringP; + int _dialValues[DIALS_ARRAY_COUNT]; + TTnpcData _data; + bool _field2CC; +protected: + /** + * Loads response data for the NPC from the given resource + */ + void loadResponses(const char *name, int valuesPerResponse = 1); + + /** + * Load ranges data for the NPC from the given resource + */ + void loadRanges(const char *name); + + /** + * Reset script flags + */ + void resetFlags(); + + /** + * Setup dials + */ + void setupDials(int dial1, int dial2, int dial3); + + static int getRoom54(int roomId); + + /** + * Perform test on various state values + */ + int getValue(int testNum) const; + + /** + * Gets a random number between 1 and a given max + */ + uint getRandomNumber(int max) const; + + /** + * Gets a random number of 0 or 1 + */ + uint getRandomBit() const { + return getRandomNumber(2) - 1; + } + + /** + * Returns a dialogue Id by script tag value Id + */ + uint getDialogueId(uint tagId); + + /** + * Returns a pointer to the PET control + */ + static CPetControl *getPetControl(CGameManager *gameManager); + + /** + * Adds a new item to the list of number ranges + */ + void addRange(uint id, const Common::Array<uint> &values, bool isRandom, bool isSequential); + + /** + * Finds an entry in the list of prevoiusly registered number ranges + */ + TTscriptRange *findRange(uint id); + + /** + * Scans through a list of sentence entries for a matching standardized response + */ + int processEntries(const TTsentenceEntries *entries, uint entryCount, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Scans through a list of sentence entries for a matching standardized response + */ + int processEntries(const TTroomScript *roomScript, const TTsentence *sentence) { + return processEntries(&_entries, _entryCount, roomScript, sentence); + } + + bool defaultProcess(const TTroomScript *roomScript, const TTsentence *sentence); + + void checkItems(const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Adds a random conversation response + */ + bool addRandomResponse(bool flag); + + /** + * Updates the current dial with the given delta + */ + void updateCurrentDial(bool changeDial); + + bool fn10(bool flag); + + static bool sentence2C(const TTsentence *sentence); + + /** + * Gets the True Talk state value + */ + bool getStateValue() const; + + /** + * Gets the assigned room's room, floor, and elevator number + */ + void getAssignedRoom(int *roomNum, int *floorNum, int *elevatorNum) const; + + /** + * Uses a porition of the state _array to set up a new response + */ + void setResponseFromArray(int index, int id); +public: + static void init(); + static void deinit(); +public: + TTnpcScript(int charId, const char *charClass, int v2, + const char *charName, int v3, int val2, int v4, + int v5, int v6, int v7); + + virtual void addResponse(int id); + + /** + * Chooses and adds a conversation response based on a specified tag Id. + * This default implementation does a lookup into a list of known tags, + * and chooses a random dialogue Id from the available ones for that tag + */ + virtual int chooseResponse(const TTroomScript *roomScript, const TTsentence *sentence, uint tag); + + /** + * Does NPC specific processing of the parsed sentence + */ + virtual int process(const TTroomScript *roomScript, const TTsentence *sentence); + + virtual int proc8() const; + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(uint id) { + return SCR_2; + } + + /** + * Called when the script/id changes + */ + virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id) { + return SCR_2; + } + + virtual int proc11() const; + virtual int proc12() const; + + /** + * Translate a passed Id to a dialogue Id if necessary, + * and adds it to the response + */ + virtual void selectResponse(int id); + + /** + * Handles scanning the word list for a given Id, and if + * found adds it to the sentence concept list + */ + virtual bool handleWord(uint id) const; + + virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, + uint val, uint tagId, uint remainder); + + /** + * Given an Id for a previously registered set of random number values, + * picks one of the array values and returns it.. depending on flags, + * either a random value, or each value in turn + */ + virtual uint getRangeValue(uint id); + + /** + * Resets the prior used index for the specified range + */ + virtual void resetRange(int id); + + /** + * Handles updating NPC state based on specified dialogue Ids and dial positions + */ + virtual int updateState(uint oldId, uint newId, int index); + + /** + * Handles getting a pre-response + */ + virtual int preResponse(uint id); + + /** + * Returns a bitset of the dials being off or not + */ + virtual uint getDialsBitset() const { return 0; } + + virtual const TTscriptMapping *getMapping(int index); + virtual int doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence); + + /** + * Handles any post-response NPC processing + */ + virtual void postResponse(int v1, const TTsentenceEntry *entry, const TTroomScript *roomScript, const TTsentence *sentence) {} + + virtual void save(SimpleFile *file); + virtual void load(SimpleFile *file); + virtual void saveBody(SimpleFile *file); + virtual void loadBody(SimpleFile *file); + virtual int proc31() const; + + /** + * Sets a given dial to be pointing in a specified region (0 to 2) + */ + virtual void setDialRegion(int dialNum, int region); + + /** + * Sets the value for an NPC's dial + */ + virtual void setDial(int dialNum, int value); + + /** + * Returns a dial's region number + */ + virtual int getDialRegion(int dialNum) const; + + /** + * Gets the value for a dial + * @param dialNum Dial number + * @param randomizeFlag If set, introduces a slight random variance so that + * the displayed dial will oscillate randomly around it's real level + */ + virtual int getDialLevel(uint dialNum, bool randomizeFlag = true); + + /** + * Handles a randomzied response + */ + virtual bool randomResponse(uint index); + + virtual uint translateId(uint id) const; + + void preLoad(); + + /** + * Called with the script and id changes + */ + ScriptChangedResult notifyScript(TTroomScript *roomScript, int id) { + return scriptChanged(roomScript, id); + } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_NPC_SCRIPT_H */ 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 diff --git a/engines/titanic/true_talk/tt_parser.h b/engines/titanic/true_talk/tt_parser.h new file mode 100644 index 0000000000..201de7eb0e --- /dev/null +++ b/engines/titanic/true_talk/tt_parser.h @@ -0,0 +1,208 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_PARSER_H +#define TITANIC_TT_PARSER_H + +#include "titanic/true_talk/tt_node.h" +#include "titanic/true_talk/tt_pronoun.h" +#include "titanic/true_talk/tt_sentence.h" +#include "titanic/true_talk/tt_string.h" + +namespace Titanic { + +enum NumberFlag { NF_1 = 1, NF_2 = 2, NF_4 = 4, NF_8 = 8, NF_10 = 0x10 }; + +enum ParserAction { + NO_ACTION = 0, CHECK_COMMAND_FORM, EXPECT_THING, OBJECT_IS_TO, + SEEK_ACTOR, SEEK_OBJECT, SEEK_OBJECT_OVERRIDE, SEEK_TO, + SEEK_FROM, SEEK_TO_OVERRIDE, SEEK_FROM_OVERRIDE, SEEK_LOCATION, + SEEK_OWNERSHIP, SEEK_STATE, SEEK_MODIFIERS, SEEK_NEW_FRAME, + SEEK_STATE_OBJECT, SET_ACTION, SET_COLOR, ACTOR_IS_TO, + ACTOR_IS_FROM, ACTOR_IS_OBJECT, STATE_IDENTITY, + WORD_TYPE_IS_SENTENCE_TYPE, COMPLEX_VERB +}; + +class CScriptHandler; +class TTconcept; + +struct NumberEntry { + CString _text; + int _value; + int _flags; + + NumberEntry() : _value(0), _flags(0) {} + NumberEntry(const CString &text, int value, int flags) : + _text(text), _value(value), _flags(flags) {} +}; +typedef Common::Array<NumberEntry> NumberArray; + +class TTparserNode : public TTnode { +public: + uint _tag; +public: + TTparserNode() : TTnode(), _tag(0) {} + TTparserNode(uint tag) : TTnode(), _tag(tag) {} +}; + +class TTparser { +private: + StringArray _replacements1; + StringArray _replacements2; + StringArray _replacements3; + StringArray _phrases; + NumberArray _numbers; + TTparserNode *_nodesP; + TTconcept *_conceptP; + TTconcept *_currentConceptP; +private: + /** + * Load the data for a given array resource + */ + void loadArray(StringArray &arr, const CString &name); + + /** + * Loads the various replacement string data arrays + */ + void loadArrays(); + + /** + * Normalizes a passed input, taking care of things like removing extra + * spaces and lowercasing everything + */ + int normalize(TTsentence *sentence); + + /** + * Submethod called by normalize to handle expanding contacted word pairs + * like can't, should've, and so on. + */ + bool normalizeContraction(const TTstring &srcLine, int srcIndex, TTstring &destLine); + + /** + * Checks for what is likely special developer cheat codes + */ + static int isEmoticon(const TTstring &str, int &index); + + /** + * Checks if any word within a passed line has an entry in the list of replacements, + * and if found, replaces it with it's equivalent replacement string + * @param line Line to check + * @param strings List of strings to check for. Strings come in pairs, with the + * first being the string to match, and the second the replacement + */ + static void searchAndReplace(TTstring &line, const StringArray &strings); + + /** + * Checks the string starting at a given index for any word in the passed string array, + * and if found, replaces it in the line with it's replacement + * @param line Line to check + * @param startIndex Starting index in the start to check + * @param strings List of strings to check for. Strings come in pairs, with the + * first being the string to match, and the second the replacement + * @returns Index of the start of the following word + */ + static int searchAndReplace(TTstring &line, int startIndex, const StringArray &strings); + + /** + * Checks the string starting at a given index for a number representation + * such as roman numericals, spelled out numbers, etc. and replaces it with + * a plain decimal representation. + * @param line Line to check + * @param startIndex Starting index in the start to check + * @returns Index of the start of the following word, or -1 if at end of line + */ + int replaceNumbers(TTstring &line, int startIndex); + + /** + * Checks the string starting at a given index for a number representation + * such as roman numericals, spelled out numbers, etc. and replaces it with + * a plain decimal representation. + * @param line Line to check + * @param startIndex Starting index in the start to check + * @returns Pointer to matching number entry, if match occurred + */ + const NumberEntry *replaceNumbers2(TTstring &line, int *startIndex); + + int loadRequests(TTword *word); + int considerRequests(TTword *word); + int processRequests(TTword *word); + + int addToConceptList(TTword *word); + int checkReferent(TTpronoun *pronoun); + + /** + * Creates a new parser node, and adds it to the parser's list + */ + void addNode(uint tag); + + /** + * Add a concept node + */ + int addConcept(TTconcept *concept); + + /** + * Detaches a concept from the main concept list if prseent, then deletes it + */ + void removeConcept(TTconcept *concept); + + /** + * Detaches a node from the main node list + */ + void removeNode(TTparserNode *node); + + int processModifiers(int modifier, TTword *word); + + int checkForAction(); + int fn2(TTword *word); + bool checkConcept2(TTconcept *concept, int conceptMode); + int filterConcepts(int conceptMode, int conceptIndex); + bool resetConcept(TTconcept **conceptPP, int conceptIndex); +public: + CScriptHandler *_owner; + TTsentenceConcept *_sentenceConcept; + TTsentence *_sentence; + int _fieldC; + int _field10; + int _field14; + TTword *_currentWordP; + StringArray _pronouns; +public: + TTparser(CScriptHandler *owner); + ~TTparser(); + + /** + * Preprocesses the passed input text, to handle things like lowercasing + * all the words, and replcaing common slang with their full equivalents + */ + int preprocess(TTsentence *sentence); + + int findFrames(TTsentence *sentence); + + /** + * Called when a concept is copied from one to another + */ + void conceptChanged(TTconcept *newConcept, TTconcept *oldConcept); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_PARSER_H */ diff --git a/engines/titanic/true_talk/tt_picture.cpp b/engines/titanic/true_talk/tt_picture.cpp new file mode 100644 index 0000000000..4b04b8825b --- /dev/null +++ b/engines/titanic/true_talk/tt_picture.cpp @@ -0,0 +1,102 @@ +/* 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_picture.h" + +namespace Titanic { + +bool TTpicture::_staticFlag; + +TTpicture::TTpicture(const TTstring &str, WordClass wordClass, int val2, int val3, int val4, int val5, int val6) : + TTmajorWord(str, wordClass, val2, val4), _tag(val3), _field30(val5), _field3C(val6), + _field38(0) { +} + +TTpicture::TTpicture(const TTpicture *src) : TTmajorWord(src) { + if (getStatus()) { + _tag = 0; + _field30 = 0; + _field38 = 0; + _field3C = 0; + _status = SS_5; + } else { + _tag = src->_tag; + _field30 = src->_field30; + _field38 = src->_field38; + _field3C = src->_field3C; + } +} + +int TTpicture::load(SimpleFile *file) { + CString str; + int val1, val2; + + if (!TTword::load(file, WC_THING) && file->scanf("%s %d %d", &str, &val1, &val2)) { + _tag = readNumber(str.c_str()); + _field30 = val1; + _field3C = val2; + return 0; + } else { + return 3; + } +} + +TTword *TTpicture::copy() const { + TTpicture *returnWordP = new TTpicture(this); + returnWordP->_status = _status; + if (!_status) { + _staticFlag = false; + return returnWordP; + } else if (_status == SS_13 && !_staticFlag) { + _staticFlag = true; + delete returnWordP; + return copy(); + } else { + delete returnWordP; + return nullptr; + } +} + +bool TTpicture::checkTag() const { + return _tag == MKTAG('S', 'E', 'X', 'X') || + _tag == MKTAG('E', 'X', 'C', 'R') || + _tag == MKTAG('P', 'P', 'R', 'T') || + _tag == MKTAG('B', 'L', 'A', 'S'); +} + +bool TTpicture::compareTagTo(uint tag) const { + return _tag == tag; +} + +uint TTpicture::getTag() const { + return _tag; +} + +bool TTpicture::proc9(int val) const { + return _field3C == val; +} + +int TTpicture::proc10() const { + return _field3C; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_picture.h b/engines/titanic/true_talk/tt_picture.h new file mode 100644 index 0000000000..18cb88278f --- /dev/null +++ b/engines/titanic/true_talk/tt_picture.h @@ -0,0 +1,74 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_PICTURE_H +#define TITANIC_TT_PICTURE_H + +#include "titanic/true_talk/tt_major_word.h" + +namespace Titanic { + +class TTpicture : public TTmajorWord { +private: + static bool _staticFlag; +protected: + int _field30; + uint _tag; + int _field38; + int _field3C; +public: + TTpicture(const TTstring &str, WordClass wordClass, int val2, int val3, int val4, int val5, int val6); + TTpicture(const TTpicture *src); + + /** + * Load the word + */ + int load(SimpleFile *file); + + /** + * Creates a copy of the word + */ + virtual TTword *copy() const; + + /** + * Checks whether the word's tag is a known type + */ + virtual bool checkTag() const; + + /** + * Compare the word's tag to a given tag value + */ + virtual bool compareTagTo(uint tag) const; + + /** + * Return the tag associated with the word + */ + virtual uint getTag() const; + + virtual bool proc9(int val) const; + virtual int proc10() const; + +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_PICTURE_H */ diff --git a/engines/titanic/true_talk/tt_pronoun.cpp b/engines/titanic/true_talk/tt_pronoun.cpp new file mode 100644 index 0000000000..3ef48314e6 --- /dev/null +++ b/engines/titanic/true_talk/tt_pronoun.cpp @@ -0,0 +1,73 @@ +/* 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_pronoun.h" + +namespace Titanic { + +bool TTpronoun::_staticFlag; + +TTpronoun::TTpronoun(TTstring &str, WordClass wordClass, int val2, int val3, int val4) : + TTmajorWord(str, wordClass, val2, val3), _field30(val4) { +} + +TTpronoun::TTpronoun(const TTpronoun *src) : TTmajorWord(src) { + if (src->getStatus()) { + _field30 = 0; + _status = SS_5; + } else { + _field30 = src->_field30; + } +} + +int TTpronoun::load(SimpleFile *file) { + int val; + + if (!TTword::load(file, WC_PRONOUN) && file->scanf("%d", &val)) { + if (val >= 0 && val <= 12) { + _field30 = val; + return 0; + } else { + return 5; + } + } else { + return 8; + } +} + +TTword *TTpronoun::copy() const { + TTpronoun *returnWordP = new TTpronoun(this); + returnWordP->_status = _status; + if (!_status) { + _staticFlag = false; + return returnWordP; + } else if (_status == SS_13 && !_staticFlag) { + _staticFlag = true; + delete returnWordP; + return copy(); + } else { + delete returnWordP; + return nullptr; + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_pronoun.h b/engines/titanic/true_talk/tt_pronoun.h new file mode 100644 index 0000000000..ccc077152c --- /dev/null +++ b/engines/titanic/true_talk/tt_pronoun.h @@ -0,0 +1,65 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_PRONOUN_H +#define TITANIC_TT_PRONOUN_H + +#include "titanic/true_talk/tt_major_word.h" + +namespace Titanic { + +class TTpronoun : public TTmajorWord { +private: + static bool _staticFlag; +protected: + int _field30; +public: + TTpronoun(TTstring &str, WordClass wordClass, int val2, int val3, int val4); + TTpronoun(const TTpronoun *src); + + /** + * Load the word + */ + int load(SimpleFile *file); + + int getVal() const { return _field30; } + + /** + * Creates a copy of the word + */ + virtual TTword *copy() const; + + virtual bool comparePronounTo(int val) const { + return _field30 == val; + } + + /** + * Dumps data associated with the word to file + */ + virtual int save(SimpleFile *file) const { + return saveData(file, _field30); + } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_WORD_H */ diff --git a/engines/titanic/true_talk/tt_quotes.cpp b/engines/titanic/true_talk/tt_quotes.cpp new file mode 100644 index 0000000000..c690ac8780 --- /dev/null +++ b/engines/titanic/true_talk/tt_quotes.cpp @@ -0,0 +1,145 @@ +/* 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 "common/algorithm.h" +#include "titanic/true_talk/tt_quotes.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTquotes::TTquotes() : _loaded(false), _dataP(nullptr), _dataSize(0), + _field544(0) { + Common::fill(&_tags[0], &_tags[256], 0); +} + +TTquotes::~TTquotes() { + delete[] _dataP; +} + +void TTquotes::load() { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource("TEXT/JRQUOTES.TXT"); + size_t size = r->readUint32LE(); + _loaded = true; + + _dataSize = _field544 = size; + _dataP = new char[size + 0x10]; + + for (int idx = 0; idx < 256; ++idx) + _tags[idx] = r->readUint32LE(); + + for (int charIdx = 0; charIdx < 26; ++charIdx) { + TTquotesLetter &letter = _alphabet[charIdx]; + int count = r->readUint32LE(); + + // Load the list of entries for the given letter + letter._entries.resize(count); + for (int idx = 0; idx < count; ++idx) { + letter._entries[idx]._tagIndex = r->readByte(); + letter._entries[idx]._maxSize = r->readByte(); + letter._entries[idx]._strP = _dataP + r->readUint32LE(); + } + } + + // Read in buffer and then decode it + r->read((byte *)_dataP, _dataSize); + for (size_t idx = 0; idx < _dataSize; idx += 4) + WRITE_LE_UINT32((byte *)_dataP + idx, READ_LE_UINT32((const byte *)_dataP + idx) ^ 0xA55A5AA5); + + delete r; +} + +int TTquotes::find(const char *str) const { + if (!str || !*str) + return 0; + + // Find start and end of string + const char *startP = str, *endP = str; + while (*endP) + ++endP; + + do { + int tagId = find(startP, endP); + if (tagId) + return tagId; + + // Move to next following space or end of string + while (*startP && *startP != ' ') + ++startP; + // If it's a space, then move past it to start of next word + while (*startP && *startP == ' ') + ++startP; + + } while (*startP); + + // No match + return 0; +} + +int TTquotes::find(const char *startP, const char *endP) const { + int size = endP - startP; + if (size < 3) + return 0; + + uint index = MIN((uint)(*startP - 'a'), (uint)25); + const TTquotesLetter &letter = _alphabet[index]; + if (letter._entries.empty()) + // No entries for the letter, so exit immediately + return 0; + + int maxSize = size + 4; + + for (uint idx = 0; idx < letter._entries.size(); ++idx) { + const TTquotesEntry &entry = letter._entries[idx]; + if (entry._maxSize > maxSize) + continue; + + const char *srcP = startP; + const char *destP = entry._strP; + int srcIndex = index != 25 ? 1 : 0, destIndex = 0; + if (*destP) { + do { + if (!srcP[srcIndex]) { + break; + } else if (srcP[srcIndex] == '*') { + ++srcIndex; + } else if (destP[destIndex] == '-') { + ++destIndex; + if (srcP[srcIndex] == ' ') + ++srcIndex; + } else if (srcP[srcIndex] != destP[destIndex]) { + break; + } else { + ++destIndex; + ++srcIndex; + } + } while (destP[destIndex]); + + if (!destP[destIndex] && (srcP[srcIndex] <= '*' || + (srcP[srcIndex] == 's' && srcP[srcIndex + 1] <= '*'))) + return _tags[entry._tagIndex]; + } + } + + return 0; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_quotes.h b/engines/titanic/true_talk/tt_quotes.h new file mode 100644 index 0000000000..5958c9ebcf --- /dev/null +++ b/engines/titanic/true_talk/tt_quotes.h @@ -0,0 +1,77 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_QUOTES_H +#define TITANIC_TT_QUOTES_H + +#include "common/scummsys.h" +#include "common/stream.h" +#include "titanic/support/string.h" + +namespace Titanic { + +class TTquotes { + struct TTquotesEntry { + byte _tagIndex, _maxSize; + const char *_strP; + TTquotesEntry() : _tagIndex(0), _maxSize(0), _strP(nullptr) {} + }; + struct TTquotesLetter { + Common::Array<TTquotesEntry> _entries; + int _field4; + int _field8; + + TTquotesLetter() : _field4(0), _field8(0) {} + }; +private: + TTquotesLetter _alphabet[26]; + uint _tags[256]; + char *_dataP; + size_t _dataSize; + int _field544; +private: + /** + * Test whether a substring contains one of the quotes, + * and if so, returns the 4-character tag Id associated with it + */ + int find(const char *startP, const char *endP) const; +public: + bool _loaded; +public: + TTquotes(); + ~TTquotes(); + + /** + * Load quotes data resource + */ + void load(); + + /** + * Test whether a passed string contains one of the quotes, + * and if so, returns the 4-character tag Id associated with it + */ + int find(const char *str) const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_QUOTES_H */ diff --git a/engines/titanic/true_talk/tt_quotes_tree.cpp b/engines/titanic/true_talk/tt_quotes_tree.cpp new file mode 100644 index 0000000000..1f073b83f2 --- /dev/null +++ b/engines/titanic/true_talk/tt_quotes_tree.cpp @@ -0,0 +1,214 @@ +/* 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 "common/algorithm.h" +#include "titanic/true_talk/tt_quotes_tree.h" +#include "titanic/titanic.h" + +namespace Titanic { + +/** + * Specifies the starting index for each of the three main trees + */ +static uint TABLE_INDEXES[3] = { 922, 1015, 1018 }; + +void TTquotesTree::load() { + Common::SeekableReadStream *r = g_vm->_filesManager->getResource("TEXT/TREE"); + + for (int idx = 0; idx < QUOTES_TREE_COUNT; ++idx) { + TTquotesTreeEntry &rec = _entries[idx]; + assert(r->pos() < r->size()); + + rec._id = r->readUint32LE(); + if (rec._id == 0) { + // Nothing needed + } else { + byte type = r->readByte(); + if (type == 0) { + // Index to sub-table + rec._subTable = &_entries[0] + r->readUint32LE(); + } else { + // Read in string for entry + char c; + while ((c = r->readByte()) != '\0') + rec._string += c; + } + } + } + + assert(r->pos() == r->size()); + delete r; +} + +int TTquotesTree::search(const char *str, QuoteTreeNum treeNum, + TTtreeResult *buffer, uint tagId, uint *remainder) { + const TTquotesTreeEntry *bTree = &_entries[TABLE_INDEXES[treeNum]]; + if (!search1(&str, bTree, buffer, tagId) || !buffer->_treeItemP) + return -1; + + if (remainder) { + while (*str) { + if (*str >= 'a' && *str != 's') + *remainder += *str; + } + } + + return buffer->_treeItemP->_id & 0xffffff; +} + +bool TTquotesTree::search1(const char **str, const TTquotesTreeEntry *bTree, + TTtreeResult *buffer, uint tagId) { + buffer->_treeItemP = nullptr; + (buffer + 1)->_treeItemP = nullptr; + + const char *strP = *str; + bool flag = false; + + for (uint mode = bTree->_id >> 24; mode != 0; + ++bTree, mode = bTree->_id >> 24) { + + switch (mode) { + case 1: + if (compareWord(str, bTree->_string.c_str())) + flag = true; + break; + + case 2: + compareWord(str, bTree->_string.c_str()); + break; + + case 5: + if (READ_LE_UINT32(bTree->_string.c_str()) == tagId) + flag = true; + break; + + case 7: + if (search1(str, bTree->_subTable, buffer + 1, tagId)) + flag = true; + break; + + case 8: + if (search2(str, bTree->_subTable, buffer + 1, tagId)) + flag = true; + break; + + default: + break; + } + + if (flag) { + buffer->_treeItemP = bTree; + return true; + } + } + + *str = strP; + return false; +} + +bool TTquotesTree::search2(const char **str, const TTquotesTreeEntry *bTree, + TTtreeResult *buffer, uint tagId) { + buffer->_treeItemP = bTree; + (buffer + 1)->_treeItemP = nullptr; + + const char *strP = *str; + bool flag = false; + for (uint mode = bTree->_id >> 24; mode != 0; + ++bTree, mode = bTree->_id >> 24) { + switch (mode) { + case 0: + return true; + + case 1: + if (compareWord(str, bTree->_string.c_str())) + flag = true; + break; + + case 2: + compareWord(str, bTree->_string.c_str()); + break; + + case 5: + if (READ_LE_UINT32(bTree->_string.c_str()) == tagId) + flag = true; + break; + + case 7: + if (search1(str, bTree->_subTable, buffer + 1, tagId)) + flag = true; + break; + + case 8: + if (search2(str, bTree->_subTable, buffer + 1, tagId)) + flag = true; + break; + + default: + break; + } + + if (flag) { + buffer->_treeItemP = nullptr; + *str = strP; + return false; + } + } + + return true; +} + +bool TTquotesTree::compareWord(const char **str, const char *refStr) { + // Skip over any spaces + const char *strP = *str; + while (*strP && *strP == ' ') + ++strP; + *str = strP; + + // Compare against the reference string + while (*strP && *refStr && *refStr != '*') { + if (*refStr == '-') { + if (*strP == ' ') + ++strP; + } else if (*strP == *refStr) { + ++strP; + } else { + return false; + } + } + + if (*refStr && *refStr != '*') + return false; + if (!*refStr && *strP && *strP != ' ') + return false; + + if (*refStr == '*') { + // Skip over to the end of the word + while (*strP && *strP != ' ') + ++strP; + } + + // Pass out the new updated string position + *str = strP; + return true; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_quotes_tree.h b/engines/titanic/true_talk/tt_quotes_tree.h new file mode 100644 index 0000000000..d7ca798ae8 --- /dev/null +++ b/engines/titanic/true_talk/tt_quotes_tree.h @@ -0,0 +1,84 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_QUOTES_TREE_H +#define TITANIC_TT_QUOTES_TREE_H + +#include "common/scummsys.h" +#include "common/stream.h" +#include "titanic/support/string.h" + +namespace Titanic { + +#define QUOTES_TREE_COUNT 1022 + +enum QuoteTreeNum { TREE_1 = 0, TREE_2 = 1, TREE_3 = 2 }; + +struct TTquotesTreeEntry { + uint _id; + TTquotesTreeEntry *_subTable; + CString _string; + + TTquotesTreeEntry() : _id(0), _subTable(nullptr) {} +}; + +class TTtreeResult { +public: + int _id; + const TTquotesTreeEntry *_treeItemP; +public: + TTtreeResult() : _id(0), _treeItemP(nullptr) {} +}; + +class TTquotesTree { +private: + TTquotesTreeEntry _entries[QUOTES_TREE_COUNT]; +private: + /** + * First inner search method + */ + bool search1(const char **str, const TTquotesTreeEntry *bTree, + TTtreeResult *buffer, uint tagId); + + /** + * Second inner search method + */ + bool search2(const char **str, const TTquotesTreeEntry *bTree, + TTtreeResult *buffer, uint tagId); + + /** + * Compare the current word in the string against a specified word + */ + bool compareWord(const char **str, const char *refStr); +public: + /** + * Load data for the quotes tree + */ + void load(); + + int search(const char *str, QuoteTreeNum treeNum, + TTtreeResult *buffer, uint tagId, uint *remainder); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_QUOTES_TREE_H */ diff --git a/engines/titanic/true_talk/tt_response.cpp b/engines/titanic/true_talk/tt_response.cpp new file mode 100644 index 0000000000..f007f98f97 --- /dev/null +++ b/engines/titanic/true_talk/tt_response.cpp @@ -0,0 +1,71 @@ +/* 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_response.h" + +namespace Titanic { + +TTresponse::TTresponse(const TTstring &src) : _field0(0), _text(src), + _dialogueId(0), _nextP(nullptr), _linkP(nullptr) { +} + +TTresponse::TTresponse(int dialogueId, int val2) : _field0(val2), _text(" "), + _dialogueId(dialogueId), _nextP(nullptr), _linkP(nullptr) { +} + +TTresponse::TTresponse(const TTresponse *src) : _field0(src->_field0), + _text(src->_text), _dialogueId(src->_dialogueId), _nextP(src->_nextP), + _linkP(src->_linkP) { +} + +TTresponse::~TTresponse() { + // Iterate through destroying any successive linked response items + TTresponse *nextP; + for (TTresponse *currP = _nextP; currP; currP = nextP) { + // Get the following response and detach it from the current one, + // so that when the current is destroyed, it will only destroy itself + nextP = currP->_nextP; + currP->_nextP = nullptr; + delete currP; + } +} + +TTresponse *TTresponse::copyChain() const { + TTresponse *returnResponseP = new TTresponse(this); + + for (TTresponse *srcP = _nextP, *destP = returnResponseP; + srcP; srcP = srcP->_nextP, destP = destP->_nextP) { + destP->_nextP = new TTresponse(*srcP); + } + + return returnResponseP; +} + +void TTresponse::addLink(TTresponse *item) { + TTresponse *currP = this; + while (currP->_linkP) + currP = currP->_linkP; + + currP->_linkP = item; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_response.h b/engines/titanic/true_talk/tt_response.h new file mode 100644 index 0000000000..d39d18c193 --- /dev/null +++ b/engines/titanic/true_talk/tt_response.h @@ -0,0 +1,66 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_RESPONSE_H +#define TITANIC_TT_RESPONSE_H + +#include "titanic/true_talk/tt_string.h" +namespace Titanic { + +class TTsentence; + +class TTresponse { +private: + int _field0; + TTstring _text; + int _dialogueId; + TTresponse *_nextP; + TTresponse *_linkP; +public: + TTresponse(const TTstring &src); + TTresponse(int val1, int val2); + TTresponse(const TTresponse *src); + virtual ~TTresponse(); + + /** + * Makes a copy of the chain of responses + */ + TTresponse *copyChain() const; + + TTresponse *getLink() const { return _linkP; } + + void addLink(TTresponse *item); + + /** + * Get the dialogue Id for the response + */ + int getDialogueId() const { return _dialogueId; } + + /** + * Return the next response item, if present + */ + TTresponse *getNext() const { return _nextP; } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_RESPONSE_H */ diff --git a/engines/titanic/true_talk/tt_room_script.cpp b/engines/titanic/true_talk/tt_room_script.cpp new file mode 100644 index 0000000000..b8fbca7d39 --- /dev/null +++ b/engines/titanic/true_talk/tt_room_script.cpp @@ -0,0 +1,60 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/tt_room_script.h" + +namespace Titanic { + +TTroomScriptBase::TTroomScriptBase(int scriptId, + const char *charClass, const char *charName, + int v3, int v4, int v5, int v6, int v2, int v7) : _scriptId(scriptId), + TTscriptBase(3, charClass, v2, charName, v3, v4, v5, v6, v7) { +} + +/*------------------------------------------------------------------------*/ + +TTroomScript::TTroomScript(int scriptId) : + TTroomScriptBase(scriptId, "", "", 0, -1, -1, -1, 0, 0) { +} + +bool TTroomScript::proc8() const { + return false; +} + +void TTroomScript::proc9(int v) { + if (v == 1) + _field54 = 1; +} + +ScriptChangedResult TTroomScript::scriptChanged(TTscriptBase *npcScript, int id) { + if (id == 1) + _field54 = 1; + + return SCR_1; +} + +bool TTroomScript::proc11() const { + return true; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_room_script.h b/engines/titanic/true_talk/tt_room_script.h new file mode 100644 index 0000000000..39a50ac30d --- /dev/null +++ b/engines/titanic/true_talk/tt_room_script.h @@ -0,0 +1,103 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_ROOM_SCRIPT_H +#define TITANIC_TT_ROOM_SCRIPT_H + +#include "titanic/true_talk/tt_script_base.h" + +namespace Titanic { + +class TTnpcScript; +class TTsentence; + +class TTroomScriptBase : public TTscriptBase { +public: + uint _scriptId; +public: + TTroomScriptBase(int scriptId, const char *charClass, const char *charName, + int v3, int v4, int v5, int v6, int v2, int v7); + + /** + * Returns true if a response can be made + */ + virtual bool canRespond(TTnpcScript *npcScript, TTsentence *sentence, int val) const = 0; + + /** + * Returns true if further sentence processing is allowed + */ + virtual bool canProcess(TTnpcScript *npcScript, TTsentence *sentence) const = 0; + + virtual bool proc8() const = 0; + virtual void proc9(int v) = 0; + + /** + * Called when the script changes + */ + virtual ScriptChangedResult scriptChanged(TTscriptBase *npcScript, int id) = 0; + + virtual bool proc11() const = 0; +}; + + +class TTroomScript : public TTroomScriptBase { +public: + int _field54; +public: + TTroomScript(int scriptId); + + /** + * Returns true if a response can be made + */ + virtual bool canRespond(TTnpcScript *npcScript, TTsentence *sentence, int val) const { + return true; + } + + /** + * Returns true if further sentence processing is allowed + */ + virtual bool canProcess(TTnpcScript *npcScript, TTsentence *sentence) const { + return true; + } + + virtual bool proc8() const; + + virtual void proc9(int v); + + /** + * Called when the script changes + */ + virtual ScriptChangedResult scriptChanged(TTscriptBase *npcScript, int id); + + virtual bool proc11() const; + + /** + * Called with the new script and id + */ + ScriptChangedResult notifyScript(TTscriptBase *npcScript, int id) { + return scriptChanged(npcScript, id); + } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_ROOM_SCRIPT_H */ diff --git a/engines/titanic/true_talk/tt_script_base.cpp b/engines/titanic/true_talk/tt_script_base.cpp new file mode 100644 index 0000000000..4109134501 --- /dev/null +++ b/engines/titanic/true_talk/tt_script_base.cpp @@ -0,0 +1,156 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/tt_script_base.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTscriptBase::TTscriptBase(int scriptId, const char *charClass, int state, + const char *charName, int v3, int v4, int v5, int v6, int v7) : + _charName(charName), _charClass(charClass), _status(0), + _nodesP(nullptr), _id(0), _hist1P(nullptr), + _field20(0), _field24(0), _field28(0), _field2C(0), + _field30(0), _state(0), _hist2P(nullptr), _field3C(0), + _respHeadP(nullptr), _respTailP(nullptr), _oldResponseP(nullptr) { + if (!isValid()) { + if (!v7 || !getStatus()) { + _id = scriptId; + _field20 = v3; + _field24 = v4; + _field28 = v5; + _field2C = v6; + _field30 = v7; + _state = state; + } else { + _status = 5; + } + } + + if (_status) + reset(); +} + +TTscriptBase::~TTscriptBase() { + deleteResponses(); + delete _oldResponseP; + + delete _hist1P; + delete _hist2P; + + if (_nodesP) { + _nodesP->deleteSiblings(); + delete _nodesP; + } +} + +bool TTscriptBase::isValid() { + bool result = !_charName.isValid() && !_charClass.isValid(); + _status = result ? 0 : 11; + return result; +} + +void TTscriptBase::reset() { + _nodesP = nullptr; + _id = 4; + _hist1P = nullptr; + _field20 = 0; + _field24 = -1; + _field28 = -1; + _field2C = -1; + _field30 = 0; + _state = 0; + _hist2P = nullptr; + _field3C = 0; + _respHeadP = nullptr; + _respTailP = nullptr; + _oldResponseP = nullptr; +} + +int TTscriptBase::scriptPreprocess(TTsentence *sentence) { + delete _hist1P; + _hist1P = new TTscriptHist(sentence); + + return _hist1P ? SS_VALID : SS_7; +} + +void TTscriptBase::addResponse(const TTstring &str) { + appendResponse2(-1, nullptr, str); +} + +void TTscriptBase::addResponse(int id) { + appendResponse(-1, nullptr, id); +} + +void TTscriptBase::applyResponse() { + delete _oldResponseP; + _oldResponseP = nullptr; + + if (_respHeadP) { + g_vm->_scriptHandler->setResponse(this, _respHeadP); + _oldResponseP = _respHeadP->copyChain(); + TTresponse *oldRespP = _respHeadP; + _respHeadP = _respHeadP->getLink(); + _respTailP = nullptr; + + delete oldRespP; + } +} + +void TTscriptBase::deleteResponses() { + while (_respTailP) { + _respHeadP = _respTailP; + _respTailP = _respHeadP->getLink(); + delete _respHeadP; + } +} + +void TTscriptBase::appendResponse(int val1, int *val2, int val3) { + if (!val2 || val1 <= *val2) { + if (_respHeadP) { + _respHeadP = new TTresponse(_respHeadP); + } else { + _respHeadP = new TTresponse(val3, 3); + if (_respTailP) + _respTailP->addLink(_respHeadP); + else + _respTailP = _respHeadP; + } + } +} + +void TTscriptBase::appendResponse(int val1, int *val2, const TTstring &str) { + if (!val2 || val1 <= *val2) { + if (_respHeadP) { + _respHeadP = new TTresponse(str); + } else { + _respHeadP = new TTresponse(str); + if (_respTailP) + _respTailP->addLink(_respHeadP); + else + _respTailP = _respHeadP; + } + } +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_script_base.h b/engines/titanic/true_talk/tt_script_base.h new file mode 100644 index 0000000000..c489dcb0a7 --- /dev/null +++ b/engines/titanic/true_talk/tt_script_base.h @@ -0,0 +1,131 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_SCRIPT_BASE_H +#define TITANIC_TT_SCRIPT_BASE_H + +#include "titanic/true_talk/tt_string.h" +#include "titanic/true_talk/tt_hist.h" +#include "titanic/true_talk/tt_node.h" +#include "titanic/true_talk/tt_response.h" + +namespace Titanic { + +enum ScriptChangedResult { + SCR_0 = 0, SCR_1 = 1, SCR_2 = 2, SCR_3 = 3, SCR_4 = 4, SCR_5 = 5 +}; + +class TTsentence; + +class TTscriptBase { +private: + void reset(); +protected: + TTnode *_nodesP; + TThist *_hist1P; + TTstring _charName, _charClass; + int _field20; + int _field24; + int _field28; + int _field2C; + int _field30; + int _state; + TThist *_hist2P; + int _field3C; + TTresponse *_respHeadP; + TTresponse *_respTailP; + TTresponse *_oldResponseP; + int _status; +protected: + /** + * Delete any responses set up for the script + */ + void deleteResponses(); + + /** + * Creates and appends a new response to the script + */ + void appendResponse(int val1, int *val2, int val3); + + void appendResponse(int val1, int *val2, const TTstring &str); + + void appendResponse2(int val1, int *val2, const TTstring &str) { + appendResponse(val1, val2, str); + } + + /** + * Set the script state + */ + void setState(int state) { _state = state; } + + /** + * Get the current state + */ + int getState() const { return _state; } +public: + int _id; +public: + TTscriptBase(int scriptId, const char *charClass, int v2, const char *charName, + int v3, int v4, int v5, int v6, int v7); + virtual ~TTscriptBase(); + + virtual void addResponse(const TTstring &str); + + virtual void addResponse(int id); + + /** + * Passes on the list of dialogue Ids stored in the response(s) + * to the title engine for later display in the PET + */ + virtual void applyResponse(); + + /** + * Returns true if the script is in a valid state + */ + bool isValid(); + + /** + * Return the Id of the script + */ + int getId() const { return _id; } + + /** + * Return the status + */ + int getStatus() const { return _status; } + + /** + * Return the script text + */ + const TTstring getText() { return _charClass; } + + /** + * Gets passed a newly created input wrapper during conversation text processing + */ + int scriptPreprocess(TTsentence *sentence); + +}; + + +} // End of namespace Titanic + +#endif /* TITANIC_TT_SCRIPT_BASE_H */ diff --git a/engines/titanic/true_talk/tt_scripts.cpp b/engines/titanic/true_talk/tt_scripts.cpp new file mode 100644 index 0000000000..f8ea65a475 --- /dev/null +++ b/engines/titanic/true_talk/tt_scripts.cpp @@ -0,0 +1,97 @@ +/* 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_scripts.h" +#include "titanic/true_talk/title_engine.h" +#include "titanic/true_talk/barbot_script.h" +#include "titanic/true_talk/bellbot_script.h" +#include "titanic/true_talk/deskbot_script.h" +#include "titanic/true_talk/doorbot_script.h" +#include "titanic/true_talk/liftbot_script.h" +#include "titanic/true_talk/maitred_script.h" +#include "titanic/true_talk/parrot_script.h" +#include "titanic/true_talk/succubus_script.h" + +namespace Titanic { + +TTnpcScript *TTnpcScriptList::findById(int charId) const { + for (TTnpcScriptList::const_iterator i = begin(); i != end(); ++i) { + const TTnpcScriptListItem *item = *i; + if (item->_npcScript->_charId == charId) + return item->_npcScript; + } + + return nullptr; +} + +/*------------------------------------------------------------------------*/ + +TTroomScript *TTroomScriptList::findById(uint scriptId) const { + for (TTroomScriptList::const_iterator i = begin(); i != end(); ++i) { + const TTroomScriptListItem *item = *i; + if (item->_item->_scriptId == scriptId) + return item->_item; + } + + return nullptr; +} + +/*------------------------------------------------------------------------*/ + +TTscripts::TTscripts(CTitleEngine *titleEngine) : + _titleEngine(titleEngine), _field24(0), _field28(0) { + // Load room scripts + for (int scriptNum = 100; scriptNum < 133; ++scriptNum) + addScript(new TTroomScript(scriptNum)); + + // Load npc scripts + addScript(new DoorbotScript(104, "Doorbot", 0, "Fentible", 11, 1, -1, -1, -1, 0), 100); + addScript(new BellbotScript(101, "Bellbot", 0, "Krage", 8, 1), 110); + addScript(new LiftbotScript(105, "LiftBot", 0, "Nobby", 11, 1, -1, -1, -1, 0), 103); + addScript(new DeskbotScript(103, "DeskBot", 0, "Marsinta", 11, 2), 110); + addScript(new BarbotScript(100, "Barbot", 0, "Fortillian", 9, 1, -1, -1, -1, 0), 112); + addScript(new ParrotScript(107, "Parrot", 0, "The Parrot", 5, 1, -1, -1, -1, 0), 111); + addScript(new MaitreDScript(112, "MaitreDBot", 0, "Dastrogaaar", 8, 1), 132); + addScript(new SuccUBusScript(111, "Succubus", 0, "Shorbert", 9, 1, -1, -1, -1, 0), 110); +} + +void TTscripts::addScript(TTnpcScript *script, int scriptId) { + // Find the room script this is associated with + TTroomScript *roomScript = getRoomScript(scriptId); + assert(roomScript); + + _npcScripts.push_back(new TTnpcScriptListItem(script, roomScript)); +} + +void TTscripts::addScript(TTroomScript *script) { + _roomScripts.push_back(new TTroomScriptListItem(script)); +} + +TTroomScript *TTscripts::getRoomScript(int scriptId) const { + return _roomScripts.findById(scriptId); +} + +TTnpcScript *TTscripts::getNpcScript(int charId) const { + return _npcScripts.findById(charId); +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_scripts.h b/engines/titanic/true_talk/tt_scripts.h new file mode 100644 index 0000000000..734d86256f --- /dev/null +++ b/engines/titanic/true_talk/tt_scripts.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_SCRIPTS_H +#define TITANIC_TT_SCRIPTS_H + +#include "titanic/core/list.h" +#include "titanic/true_talk/tt_npc_script.h" +#include "titanic/true_talk/tt_room_script.h" + +namespace Titanic { + +class CTitleEngine; + +class TTnpcScriptListItem : public ListItem { +public: + TTnpcScript *_npcScript; + TTroomScript *_roomScript; +public: + TTnpcScriptListItem() : _npcScript(nullptr), _roomScript(nullptr) {} + TTnpcScriptListItem(TTnpcScript *script, TTroomScript *roomScript) : + _npcScript(script), _roomScript(roomScript) {} + virtual ~TTnpcScriptListItem() { delete _npcScript; } +}; + +PTR_LIST_ITEM(TTroomScript); + +class TTnpcScriptList : public List<TTnpcScriptListItem> { +public: + TTnpcScript *findById(int charId) const; +}; + +class TTroomScriptList : public List<TTroomScriptListItem> { +public: + TTroomScript *findById(uint scriptId) const; +}; + +class TTscripts { +private: + TTnpcScriptList _npcScripts; + TTroomScriptList _roomScripts; + CTitleEngine *_titleEngine; + int _field24; + int _field28; +private: + /** + * Add a named script to the named scripts list + */ + void addScript(TTnpcScript *script, int charId); + + /** + * Add an unnamed script to the unnamed scripts list + */ + void addScript(TTroomScript *script); +public: + TTscripts(CTitleEngine *titleEngine); + + /** + * Return a pointer to the specified room script + */ + TTroomScript *getRoomScript(int scriptId) const; + + /** + * Return a pointer to the specified character script + */ + TTnpcScript *getNpcScript(int charId) const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_CHARACTERS_H */ diff --git a/engines/titanic/true_talk/tt_sentence.cpp b/engines/titanic/true_talk/tt_sentence.cpp new file mode 100644 index 0000000000..9588ee021e --- /dev/null +++ b/engines/titanic/true_talk/tt_sentence.cpp @@ -0,0 +1,347 @@ +/* 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_sentence.h" +#include "titanic/true_talk/tt_concept.h" +#include "titanic/true_talk/script_handler.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTsentenceConcept *TTsentenceConcept::addSibling() { + if (this == nullptr || _nextP != nullptr) + // This should never happen + return nullptr; + + TTsentenceConcept *nextP = new TTsentenceConcept(); + _nextP = nextP; + return nextP; +} + +/*------------------------------------------------------------------------*/ + +TTsentence::TTsentence(int inputCtr, const TTstring &line, CScriptHandler *owner, + TTroomScript *roomScript, TTnpcScript *npcScript) : + _owner(owner), _field2C(1), _inputCtr(inputCtr), _field34(0), + _field38(0), _initialLine(line), _nodesP(nullptr), _roomScript(roomScript), + _npcScript(npcScript), _field58(0), _field5C(0) { + _status = _initialLine.isValid() && _normalizedLine.isValid() ? SS_11: SS_VALID; +} + +TTsentence::TTsentence(const TTsentence *src) : _sentenceConcept(src->_sentenceConcept), + _initialLine(src->_initialLine), _normalizedLine(src->_normalizedLine) { + copyFrom(*src); +} + +TTsentence::~TTsentence() { + _sentenceConcept.deleteSiblings(); + + if (_nodesP) { + _nodesP->deleteSiblings(); + delete _nodesP; + } +} + +void TTsentence::copyFrom(const TTsentence &src) { + if (!src.getStatus()) + _status = SS_5; + else if (!src._initialLine.isValid() || !src._normalizedLine.isValid()) + _status = SS_11; + else + _status = SS_VALID; + + _inputCtr = src._inputCtr; + _owner = src._owner; + _roomScript = src._roomScript; + _npcScript = src._npcScript; + _field58 = src._field58; + _field5C = src._field5C; + _field34 = src._field34; + _field38 = src._field38; + _field2C = src._field2C; + _nodesP = nullptr; + + if (src._nodesP) { + // Source has processed nodes, so duplicate them + for (TTsentenceNode *node = src._nodesP; node; + node = static_cast<TTsentenceNode *>(node->_nextP)) { + TTsentenceNode *newNode = new TTsentenceNode(node->_wordP); + if (_nodesP) + _nodesP->addToTail(newNode); + else + _nodesP = newNode; + } + } +} + +int TTsentence::storeVocabHit(TTword *word) { + if (!word) + return 0; + + TTsentenceNode *node = new TTsentenceNode(word); + if (_nodesP) { + _nodesP->addToTail(node); + } else { + _nodesP = node; + } + + return 0; +} + +bool TTsentence::fn1(const CString &str, int wordId1, const CString &str1, const CString &str2, + const CString &str3, int wordId2, int val1, int val2, const TTconceptNode *node) const { + if (node) + node = &_sentenceConcept; + + if (!node && !node) + return false; + if (val1 && !is18(val1, node)) + return false; + if (!str.empty() && !fn2(0, str, node)) + return false; + if (wordId1 && !fn4(1, wordId1, node)) + return false; + if (!str1.empty() && !fn2(2, str1, node)) + return false; + if (!str2.empty() && !fn2(3, str2, node)) + return false; + if (!str3.empty() && !fn2(4, str3, node)) + return false; + if (wordId2 && !fn4(5, wordId2, node)) + return false; + if (val2 && !is1C(val2, node)) + return false; + + return true; +} + +bool TTsentence::fn3(const CString &str1, const CString &str2, const CString &str3, + const CString &str4, const CString &str5, const CString &str6, + int val1, int val2, const TTconceptNode *node) const { + if (!node) + node = &_sentenceConcept; + + if (val1 && !is18(val1, node)) + return false; + if (!str1.empty() && !fn2(0, str1, node)) + return false; + if (!str2.empty() && !fn2(1, str2, node)) + return false; + if (!str3.empty() && !fn2(2, str3, node)) + return false; + if (!str4.empty() && !fn2(3, str4, node)) + return false; + if (!str5.empty() && !fn2(4, str5, node)) + return false; + if (!str6.empty() && !fn2(5, str6, node)) + return false; + if (!val2 && !is1C(val2, node)) + return false; + + return true; +} + +bool TTsentence::fn2(int slotIndex, const TTstring &str, const TTconceptNode *node) const { + if (!node) + node = &_sentenceConcept; + TTconcept *concept = getFrameSlot(slotIndex, node); + + if (!concept) + return str == "isEmpty"; + + bool abortFlag = false; + switch (concept->_scriptType) { + case 1: + if (str == "thePlayer") + abortFlag = 1; + break; + + case 2: + if (str == "targetNpc") + abortFlag = 1; + break; + + case 3: + if (str == "otherNpc") + abortFlag = 1; + break; + + default: + break; + } + + TTstring conceptText = concept->getText(); + if (abortFlag || str == conceptText || concept->compareTo(str)) { + delete concept; + return true; + } + + if (slotIndex == 1 && g_vm->_exeResources._owner->_concept4P) { + if (str == g_vm->_exeResources._owner->_concept4P->getText() && + conceptText == "do") + goto exit; + } + + if (g_vm->_exeResources._owner->_concept2P && (slotIndex == 0 || + slotIndex == 3 || slotIndex == 4)) { + if (str == g_vm->_exeResources._owner->_concept2P->getText() && + (conceptText == "it" || conceptText == "he" || conceptText == "she" || + conceptText == "him" || conceptText == "her" || conceptText == "them" || + conceptText == "they")) + goto exit; + } + + if (g_vm->_exeResources._owner->_concept1P && (slotIndex == 0 || + slotIndex == 2 || slotIndex == 3 || slotIndex == 4 || slotIndex == 5)) { + if (str == g_vm->_exeResources._owner->_concept2P->getText() && + (conceptText == "it" || conceptText == "that" || conceptText == "he" || + conceptText == "she" || conceptText == "him" || conceptText == "her" || + conceptText == "them" || conceptText == "they" || conceptText == "those" || + conceptText == "1" || conceptText == "thing")) + goto exit; + } + + if (g_vm->_exeResources._owner->_concept1P && (slotIndex == 0 || slotIndex == 2)) { + if (conceptText == "?" && str == g_vm->_exeResources._owner->_concept2P->getText()) { + delete concept; + concept = getFrameSlot(5, node); + conceptText = concept->getText(); + + if (conceptText == "it" || conceptText == "that" || conceptText == "he" || + conceptText == "she" || conceptText == "him" || conceptText == "her" || + conceptText == "them" || conceptText == "they" || conceptText == "those" || + conceptText == "1" || conceptText == "thing") + abortFlag = true; + } + } + +exit: + delete concept; + return abortFlag; +} + +bool TTsentence::fn4(int mode, int wordId, const TTconceptNode *node) const { + if (!node) + node = &_sentenceConcept; + + switch (mode) { + case 1: + return node->_concept1P && node->_concept1P->getWordId() == wordId; + + case 5: + return node->_concept5P && node->_concept5P->getWordId() == wordId; + + default: + return false; + } +} + +TTconcept *TTsentence::getFrameEntry(int slotIndex, const TTconceptNode *conceptNode) const { + if (!conceptNode) + conceptNode = &_sentenceConcept; + + return conceptNode->_concepts[slotIndex]; +} + +TTconcept *TTsentence::getFrameSlot(int slotIndex, const TTconceptNode *conceptNode) const { + TTconcept *newConcept = new TTconcept(); + TTconcept *concept = getFrameEntry(slotIndex, conceptNode); + newConcept->copyFrom(concept); + + if (!newConcept->isValid()) { + delete newConcept; + newConcept = nullptr; + } + + return newConcept; +} + +bool TTsentence::isFrameSlotClass(int slotIndex, WordClass wordClass, const TTconceptNode *conceptNode) const { + TTconcept *concept = getFrameEntry(slotIndex, conceptNode); + if (concept && concept->_wordP) { + return concept->_wordP->isClass(wordClass); + } else { + return false; + } +} + +int TTsentence::is18(int val, const TTconceptNode *node) const { + if (!node) + node = &_sentenceConcept; + return node->_field18 == val; +} + +int TTsentence::is1C(int val, const TTconceptNode *node) const { + if (!node) + node = &_sentenceConcept; + return node->_field1C == val; +} + +bool TTsentence::isConcept34(int slotIndex, const TTconceptNode *node) const { + TTconcept *concept = getFrameEntry(slotIndex, node); + return concept && concept->getState(); +} + +bool TTsentence::localWord(const char *str) const { + CScriptHandler &scriptHandler = *g_vm->_exeResources._owner; + bool foundMatch = false; + + if (scriptHandler._concept1P) { + TTstring s = scriptHandler._concept1P->getText(); + if (s == str) + foundMatch = true; + } else if (scriptHandler._concept2P) { + TTstring s = scriptHandler._concept2P->getText(); + if (s == str) + foundMatch = true; + } + + int val = g_vm->_exeResources.get18(); + bool result = false; + + for (TTsentenceNode *nodeP = _nodesP; nodeP && !result; + nodeP = static_cast<TTsentenceNode *>(nodeP->_nextP)) { + TTsynonym syn; + if (!nodeP->_wordP) + continue; + + const TTstring wordStr = nodeP->_wordP->_text; + if (val == 3 && wordStr == str) { + result = true; + } else if (nodeP->_wordP->findSynByName(str, &syn, val)) { + result = true; + } else if (foundMatch) { + result = wordStr == "it" || wordStr == "that" || wordStr == "he" + || wordStr == "she" || wordStr == "him" || wordStr == "her" + || wordStr == "them" || wordStr == "they" || wordStr == "those" + || wordStr == "1" || wordStr == "thing"; + } + } + + return result; +} + +bool TTsentence::contains(const char *str) const { + return _initialLine.contains(str) || _normalizedLine.contains(str); +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_sentence.h b/engines/titanic/true_talk/tt_sentence.h new file mode 100644 index 0000000000..7b2c6400c5 --- /dev/null +++ b/engines/titanic/true_talk/tt_sentence.h @@ -0,0 +1,132 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_SENTENCE_H +#define TITANIC_TT_SENTENCE_H + +#include "common/array.h" +#include "titanic/true_talk/tt_concept_node.h" +#include "titanic/true_talk/tt_npc_script.h" +#include "titanic/true_talk/tt_room_script.h" +#include "titanic/true_talk/tt_sentence_node.h" +#include "titanic/true_talk/tt_string.h" + +namespace Titanic { + +class CScriptHandler; +class TTword; + +class TTsentenceConcept : public TTconceptNode { +public: + TTsentenceConcept() : TTconceptNode() {} + TTsentenceConcept(const TTsentenceConcept &src) : TTconceptNode(src) {} + + /** + * Adds a new sibling instance + */ + TTsentenceConcept *addSibling(); +}; + +class TTsentence { +private: + CScriptHandler *_owner; + int _inputCtr; + int _field34; + TTsentenceNode *_nodesP; + int _field5C; + int _status; +private: + /** + * Copy sentence data from a given source + */ + void copyFrom(const TTsentence &src); +public: + TTsentenceConcept _sentenceConcept; + TTstring _initialLine; + TTstring _normalizedLine; + int _field38; + int _field58; + TTroomScript *_roomScript; + TTnpcScript *_npcScript; + int _field2C; +public: + TTsentence(int inputCtr, const TTstring &line, CScriptHandler *owner, + TTroomScript *roomScript, TTnpcScript *npcScript); + TTsentence(const TTsentence *src); + ~TTsentence(); + + void setState(int v) { _field34 = v; } + void set38(int v) { _field38 = v; } + bool check2C() const { return _field2C > 1 && _field2C <= 10; } + int concept18(TTconceptNode *conceptNode) { + return conceptNode ? conceptNode->get18() : 0; + } + int get58() const { return _field58; } + int is18(int val, const TTconceptNode *node) const; + int is1C(int val, const TTconceptNode *node) const; + + int getStatus() const { return _status; } + + /** + * Gets a concept slot + */ + TTconcept *getFrameEntry(int slotIndex, const TTconceptNode *conceptNode = nullptr) const; + + /** + * Gets a conecpt slot and returns a duplicate of it + */ + TTconcept *getFrameSlot(int slotIndex, const TTconceptNode *conceptNode = nullptr) const; + + /** + * Returns true if the specified slot has an attached word with a given class + */ + bool isFrameSlotClass(int slotIndex, WordClass wordClass, const TTconceptNode *conceptNode = nullptr) const; + + /** + * Adds a found vocab word to the list of words representing + * the player's input + * @param word Word to node + */ + int storeVocabHit(TTword *word); + + bool fn1(const CString &str, int wordId1, const CString &str1, const CString &str2, + const CString &str3, int wordId2, int val1, int val2, const TTconceptNode *node) const; + bool fn3(const CString &str1, const CString &str2, const CString &str3, + const CString &str4, const CString &str5, const CString &str6, + int val1, int val2, const TTconceptNode *node) const; + bool fn2(int slotIndex, const TTstring &str, const TTconceptNode *node = nullptr) const; + bool fn4(int mode, int wordId, const TTconceptNode *node = nullptr) const; + + bool isConcept34(int slotIndex, const TTconceptNode *node = nullptr) const; + + bool localWord(const char *str) const; + + /** + * Returns true if the sentence (either the original or normalized lines) + * contain the specified substring + */ + bool contains(const char *str) const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_SENTENCE_H */ diff --git a/engines/titanic/true_talk/tt_sentence_node.cpp b/engines/titanic/true_talk/tt_sentence_node.cpp new file mode 100644 index 0000000000..33d7501c55 --- /dev/null +++ b/engines/titanic/true_talk/tt_sentence_node.cpp @@ -0,0 +1,34 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/tt_sentence_node.h" + +namespace Titanic { + +TTsentenceNode::TTsentenceNode() : TTnode(), _wordP(nullptr) { +} + +TTsentenceNode::TTsentenceNode(TTword *word) : TTnode(), _wordP(word) { +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_sentence_node.h b/engines/titanic/true_talk/tt_sentence_node.h new file mode 100644 index 0000000000..18fa56fd57 --- /dev/null +++ b/engines/titanic/true_talk/tt_sentence_node.h @@ -0,0 +1,41 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_SENTENCE_NODE_H +#define TITANIC_TT_SENTENCE_NODE_H + +#include "titanic/true_talk/tt_node.h" +#include "titanic/true_talk/tt_word.h" + +namespace Titanic { + +class TTsentenceNode : public TTnode { +public: + TTword *_wordP; +public: + TTsentenceNode(); + TTsentenceNode(TTword *word); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_SENTENCE_NODE_H */ diff --git a/engines/titanic/true_talk/tt_string.cpp b/engines/titanic/true_talk/tt_string.cpp new file mode 100644 index 0000000000..198a8c2e80 --- /dev/null +++ b/engines/titanic/true_talk/tt_string.cpp @@ -0,0 +1,164 @@ +/* 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_string.h" +#include "titanic/support/simple_file.h" + +namespace Titanic { + +TTstring::TTstring() : _status(SS_VALID) { + _data = new TTstringData(); +} + +TTstring::TTstring(const char *str) : _status(SS_VALID) { + _data = new TTstringData(str); +} + +TTstring::TTstring(const CString &str) { + _status = SS_VALID; + _data = new TTstringData(str); +} + +TTstring::TTstring(const TTstring &str) { + if (str._status != SS_VALID) { + _status = SS_5; + _data = nullptr; + } else { + _status = SS_VALID; + _data = str._data; + _data->_referenceCount++; + } +} + +TTstring::~TTstring() { + if (_data && --_data->_referenceCount == 0) + delete _data; +} + +void TTstring::operator=(const TTstring &str) { + // Delete old string reference, if any + if (_data && --_data->_referenceCount == 0) + delete _data; + + // Copy source string data + _status = str._status; + _data = str._data; + if (_data) + _data->_referenceCount++; +} + +void TTstring::operator=(const CString &str) { + operator=(str.c_str()); +} + +void TTstring::operator=(const char *str) { + // Delete old string reference, if any + if (_data && --_data->_referenceCount == 0) + delete _data; + + // Create new string data + _data = new TTstringData(str); + _status = SS_VALID; +} + +TTstring &TTstring::operator+=(const char *str) { + _data->_string += str; + return *this; +} + +TTstring &TTstring::operator+=(const TTstring &str) { + _data->_string += str; + return *this; +} + +TTstring &TTstring::operator+=(char c) { + _data->_string += c; + return *this; +} + +bool TTstring::operator==(const TTstring &str) const { + return _data && str._data && _data->_string == str._data->_string; +} + +bool TTstring::operator==(const char *str) const { + return _data && _data->_string == str; +} + +void TTstring::save(SimpleFile *file) const { + file->writeFormat("%s", c_str()); +} + +TTstring TTstring::tokenize(const char *delim) { + const char *strP = _data->_string.c_str(); + const char *splitP = nullptr, *chP; + + for (const char *d = delim; d; ++d) { + chP = strchr(strP, *d); + if (chP && (splitP == nullptr || chP < splitP)) + splitP = chP; + } + + if (splitP) { + TTstring result(CString(strP, splitP)); + _data->_string = CString(splitP + 1); + return result; + } else { + return TTstring(); + } +} + +int TTstring::deletePrefix(int count) { + int strSize = size(); + if (count > strSize) + count = strSize; + + if (_data->_referenceCount == 1) { + // No other references to this string, so we can just directly modify it + _data->_string = CString(_data->_string.c_str() + count); + } else { + // Detach string from current shared data, and create a new one with the substring + _data->_referenceCount--; + _data = new TTstringData(_data->_string.c_str() + count); + } + + return 1; +} + +int TTstring::deleteSuffix(int count) { + int strSize = size(); + if (count > strSize) + count = strSize; + + CString newStr(_data->_string.c_str(), _data->_string.c_str() + strSize - count); + if (_data->_referenceCount == 1) { + // No other references to this string, so we can just directly modify it + _data->_string = newStr; + } else { + // Detach string from current shared data, and create a new one with the substring + _data->_referenceCount--; + _data = new TTstringData(newStr); + } + + return 1; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_string.h b/engines/titanic/true_talk/tt_string.h new file mode 100644 index 0000000000..5b12d93418 --- /dev/null +++ b/engines/titanic/true_talk/tt_string.h @@ -0,0 +1,173 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_STRING_H +#define TITANIC_TT_STRING_H + +#include "titanic/support/string.h" + +namespace Titanic { + +class SimpleFile; + +struct TTstringData { + CString _string; + int _referenceCount; + + TTstringData() : _referenceCount(1) {} + TTstringData(const char *str) : _string(str), _referenceCount(1) {} + TTstringData(const CString &str) : _string(str), _referenceCount(1) {} +}; + +enum TTstringStatus { + SS_VALID = 0, SS_1 = 1, SS_2 = 2, SS_3 = 3, SS_4 = 4, + SS_5 = 5, SS_7 = 7, SS_8 = 8, SS_11 = 11, SS_13 = 13 +}; + +class TTstring { +private: + TTstringData *_data; + TTstringStatus _status; +public: + TTstring(); + TTstring(const char *str); + TTstring(const CString &str); + TTstring(const TTstring &str); + virtual ~TTstring(); + + void operator=(const TTstring &str); + void operator=(const CString &str); + void operator=(const char *str); + TTstring &operator+=(const char *str); + TTstring &operator+=(const TTstring &str); + TTstring &operator+=(char c); + bool operator==(const TTstring &str) const; + bool operator==(const char *str) const; + + const char &operator[](int index) { + return *(c_str() + index); + } + + bool empty() const { + return _data->_string.empty(); + } + + char firstChar() const { + return _data->_string.firstChar(); + } + + char lastChar() const { + return _data->_string.lastChar(); + } + + int size() const { + return _data->_string.size(); + } + + void deleteLastChar() { + _data->_string.deleteLastChar(); + } + + bool hasPrefix(const CString &str) const { + return _data->_string.hasPrefix(str); + } + bool hasPrefix(const char *str) const { + return _data->_string.hasPrefix(str); + } + bool hasSuffix(const CString &str) const { + return _data->_string.hasSuffix(str); + } + bool hasSuffix(const char *str) const { + return _data->_string.hasSuffix(str); + } + + bool contains(const char *s) const { + return _data->_string.contains(s); + } + + /** + * Create a new copy of the string + */ + TTstring *copy() const { + return new TTstring(c_str()); + } + + /** + * Returns true if the string is valid + */ + bool isValid() const { + return _status == SS_VALID; + } + + /** + * Get the status of the string + */ + TTstringStatus getStatus() const { return _status; } + + /** + * Get a char * pointer to the string data + */ + const char *c_str() const { return _data->_string.c_str(); } + + /** + * Automatic operator to convert to a const char * + */ + operator const char *() const { return c_str(); } + + /** + * Get a character at a specified index + */ + char charAt(int index) const { return *(c_str() + index); } + + /** + * Save the sring to a passed file + */ + void save(SimpleFile *file) const; + + /** + * Compare a substring within the string at the specified index + */ + bool compareAt(int index, const char *str) const { + return !strncmp(c_str() + index, str, strlen(str)); + } + + /** + * Split off everything in the string until the first occurance + * of any specified delimiter character + */ + TTstring tokenize(const char *delim); + + /** + * Delets a specififed number of characters from the start of the string + */ + int deletePrefix(int count); + + /** + * Delets a specififed number of characters from the end of the string + */ + int deleteSuffix(int count); + +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_STRING_H */ diff --git a/engines/titanic/true_talk/tt_string_node.cpp b/engines/titanic/true_talk/tt_string_node.cpp new file mode 100644 index 0000000000..2bb0c5a74b --- /dev/null +++ b/engines/titanic/true_talk/tt_string_node.cpp @@ -0,0 +1,68 @@ +/* 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 "common/textconsole.h" +#include "titanic/true_talk/tt_string_node.h" + +namespace Titanic { + +TTstringNode::TTstringNode() : TTnode() { +} + +void TTstringNode::initialize(int mode) { + _mode = mode; + _file = HANDLE_STDIN; + + if (_string.isValid()) { + _field1C = 0; + } else { + _field1C = 11; + warning("TTstringNode::initialize has bad subobj"); + } +} + +void TTstringNode::initialize(TTstringNode *oldNode) { + _mode = oldNode->_mode; + _file = oldNode->_file; + + if (_string.isValid()) { + _field1C = 0; + } else { + _field1C = 11; + warning("TTstringNode::initialize has bad subobj"); + } + + delete oldNode; +} + +TTstringNode *TTstringNode::findByName(const TTstring &str, int mode) { + for (TTstringNode *nodeP = this; nodeP; nodeP = static_cast<TTstringNode *>(nodeP->_nextP)) { + if (nodeP->_mode == mode || (mode == 3 && nodeP->_mode < 3)) { + if (nodeP->_string == str) + return nodeP; + } + } + + return nullptr; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_string_node.h b/engines/titanic/true_talk/tt_string_node.h new file mode 100644 index 0000000000..ced162b439 --- /dev/null +++ b/engines/titanic/true_talk/tt_string_node.h @@ -0,0 +1,59 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_STRING_NODE_H +#define TITANIC_TT_STRING_NODE_H + +#include "titanic/true_talk/tt_node.h" +#include "titanic/true_talk/tt_string.h" +#include "titanic/support/exe_resources.h" + +namespace Titanic { + +class TTstringNode : public TTnode { +protected: + /** + * Initializes state for the node + */ + void initialize(int mode); + + /** + * Initializes state for the node + */ + void initialize(TTstringNode *oldNode); +public: + TTstring _string; + FileHandle _file; + int _mode; + int _field1C; +public: + TTstringNode(); + + /** + * Find a string node in the linked chain by name + */ + TTstringNode *findByName(const TTstring &str, int mode); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_STRING_NODE_H */ diff --git a/engines/titanic/true_talk/tt_synonym.cpp b/engines/titanic/true_talk/tt_synonym.cpp new file mode 100644 index 0000000000..0f56c5cb22 --- /dev/null +++ b/engines/titanic/true_talk/tt_synonym.cpp @@ -0,0 +1,87 @@ +/* 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_synonym.h" + +namespace Titanic { + +TTsynonym::TTsynonym() : TTstringNode(), _file(HANDLE_STDIN), + _mode(0), _field1C(0) { +} + +TTsynonym::TTsynonym(const TTsynonym *src) : TTstringNode(), + _mode(0), _field1C(0) { + _string = src->_string; + initialize(src->_mode); + _file = src->_file; +} + +TTsynonym::TTsynonym(int mode, const char *str, FileHandle file) : + TTstringNode(), _mode(0), _field1C(0) { + _string = str; + initialize(mode); + _file = file; +} + +TTsynonym::TTsynonym(int mode, TTstring *str) : TTstringNode() { + _string = *str; + initialize(mode); +} + +TTsynonym *TTsynonym::copyFrom(const TTsynonym *src) { + if (src->_field1C) { + _field1C = 5; + } else { + _field1C = 0; + if (src != this) + _string = src->_string; + } + + return this; +} + +int TTsynonym::save(SimpleFile *file) { + for (TTstringNode *synP = this; synP; synP = static_cast<TTstringNode *>(synP->_nextP)) { + file->writeFormat("%s", " 0 "); + synP->_string.save(file); + file->writeFormat("%c", ' '); + + if (synP->_mode) { + file->writeFormat("%1.0d", synP->_mode); + } else { + file->writeFormat("%c", '0'); + } + + file->writeFormat("%c", ' '); + + if (synP->_file) { + file->writeFormat("%2.0d", synP->_file); + } else { + file->writeFormat("%c", ' '); + } + file->writeFormat("%c", '\n'); + } + + return 0; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_synonym.h b/engines/titanic/true_talk/tt_synonym.h new file mode 100644 index 0000000000..d5dc2be09e --- /dev/null +++ b/engines/titanic/true_talk/tt_synonym.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_SYNONYM_H +#define TITANIC_TT_SYNONYM_H + +#include "titanic/true_talk/tt_string_node.h" +#include "titanic/support/simple_file.h" + +namespace Titanic { + +class TTsynonym : public TTstringNode { +public: + TTstring _string; + FileHandle _file; + int _mode; + int _field1C; +public: + TTsynonym(); + TTsynonym(const TTsynonym *src); + TTsynonym(int mode, const char *str, FileHandle file); + TTsynonym(int mode, TTstring *str); + + /** + * Copies data from one synonym to another + */ + TTsynonym *copyFrom(const TTsynonym *src); + + /** + * Save data for the synonym to file + */ + int save(SimpleFile *file); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_SYNONYM_H */ diff --git a/engines/titanic/true_talk/tt_talker.cpp b/engines/titanic/true_talk/tt_talker.cpp new file mode 100644 index 0000000000..61443a4835 --- /dev/null +++ b/engines/titanic/true_talk/tt_talker.cpp @@ -0,0 +1,52 @@ +/* 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_talker.h" +#include "titanic/messages/messages.h" +#include "titanic/pet_control/pet_control.h" + +namespace Titanic { + +void TTtalker::speechStarted(const CString &dialogueStr, uint dialogueId, uint soundId) { + _dialogueId = dialogueId; + + CTrueTalkNotifySpeechStartedMsg msg(soundId, dialogueId, 0); + msg.execute(_npc, nullptr, MSGFLAG_BREAK_IF_HANDLED); +} + +TTtalker::~TTtalker() { + CPetControl *petControl = _npc->getPetControl(); + if (petControl) + // Add in final line + petControl->convAddLine(_line); + + // Notify the end of the speech + CTrueTalkNotifySpeechEndedMsg endedMsg(_field24, _dialogueId); + endedMsg.execute(_npc, nullptr, MSGFLAG_BREAK_IF_HANDLED); +} + +void TTtalker::endSpeech(int val) { + _done = true; + _field24 = val; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_talker.h b/engines/titanic/true_talk/tt_talker.h new file mode 100644 index 0000000000..636eb0c022 --- /dev/null +++ b/engines/titanic/true_talk/tt_talker.h @@ -0,0 +1,65 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_TALKER_H +#define TITANIC_TT_TALKER_H + +#include "titanic/core/list.h" +#include "titanic/npcs/true_talk_npc.h" +#include "titanic/support/string.h" + +namespace Titanic { + +class CTrueTalkManager; + +class TTtalker : public ListItem { +public: + CTrueTalkManager *_owner; + CTrueTalkNPC *_npc; + CString _line; + int _dialogueId; + int _field24; + int _done; +public: + TTtalker() : _owner(nullptr), _npc(nullptr), + _dialogueId(0), _field24(0), _done(0) {} + TTtalker(CTrueTalkManager *owner, CTrueTalkNPC *npc) : + _owner(owner), _npc(npc), _dialogueId(0), _field24(0), _done(0) {} + ~TTtalker(); + + /** + * Start a new speech + */ + void speechStarted(const CString &dialogueStr, uint dialogueId, uint soundId); + + /** + * End the speech + */ + void endSpeech(int val); +}; + +class TTtalkerList : public List<TTtalker> { +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_TALKER_H */ diff --git a/engines/titanic/true_talk/tt_title_script.cpp b/engines/titanic/true_talk/tt_title_script.cpp new file mode 100644 index 0000000000..85b56d0e1e --- /dev/null +++ b/engines/titanic/true_talk/tt_title_script.cpp @@ -0,0 +1,31 @@ +/* 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_title_script.h" + +namespace Titanic { + +TTTitleScript::TTTitleScript() : TTscriptBase(1, "", 0, "", 0, -1, -1, -1, 0), + _field50(0), _field5C(-1), _field60(0) { +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_title_script.h b/engines/titanic/true_talk/tt_title_script.h new file mode 100644 index 0000000000..f02e591c54 --- /dev/null +++ b/engines/titanic/true_talk/tt_title_script.h @@ -0,0 +1,43 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_TITLE_SCRIPT_H +#define TITANIC_TT_TITLE_SCRIPT_H + +#include "titanic/true_talk/tt_script_base.h" +#include "titanic/true_talk/tt_string.h" + +namespace Titanic { + +class TTTitleScript : public TTscriptBase { +private: + int _field50; + TTstring _string1; + int _field5C; + int _field60; +public: + TTTitleScript(); +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_TITLE_SCRIPT_H */ diff --git a/engines/titanic/true_talk/tt_vocab.cpp b/engines/titanic/true_talk/tt_vocab.cpp new file mode 100644 index 0000000000..08d6e9e1a7 --- /dev/null +++ b/engines/titanic/true_talk/tt_vocab.cpp @@ -0,0 +1,557 @@ +/* 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 "common/file.h"
+#include "titanic/true_talk/tt_vocab.h"
+#include "titanic/true_talk/tt_adj.h"
+#include "titanic/true_talk/tt_action.h"
+#include "titanic/true_talk/tt_adj.h"
+#include "titanic/true_talk/tt_major_word.h"
+#include "titanic/true_talk/tt_picture.h"
+#include "titanic/true_talk/tt_pronoun.h"
+#include "titanic/titanic.h"
+
+namespace Titanic {
+
+TTvocab::TTvocab(int val): _headP(nullptr), _tailP(nullptr), _word(nullptr),
+ _fieldC(0), _field10(0), _vocabMode(val) {
+ load("STVOCAB.TXT");
+}
+
+TTvocab::~TTvocab() {
+ if (_headP) {
+ _headP->deleteSiblings();
+ delete _headP;
+ _headP = _tailP = nullptr;
+ }
+}
+
+int TTvocab::load(const CString &name) {
+ SimpleFile *file = g_vm->_exeResources._owner->openResource(name);
+ int result = 0;
+ bool skipFlag;
+
+ while (!result && !file->eos()) {
+ skipFlag = false;
+ WordClass wordClass = (WordClass)file->readNumber();
+ TTstring space(" ");
+
+ switch (wordClass) {
+ case WC_UNKNOWN: {
+ if (_word)
+ result = _word->readSyn(file);
+ skipFlag = true;
+ break;
+ }
+
+ case WC_ACTION: {
+ TTaction *word = new TTaction(space, WC_UNKNOWN, 0, 0, 0);
+ result = word->load(file);
+ _word = word;
+ break;
+ }
+
+ case WC_THING: {
+ TTpicture *word = new TTpicture(space, WC_UNKNOWN, 0, 0, 0, 0, 0);
+ result = word->load(file);
+ _word = word;
+ break;
+ }
+
+ case WC_ABSTRACT:
+ case WC_ADVERB: {
+ TTmajorWord *word = new TTmajorWord(space, WC_UNKNOWN, 0, 0);
+ result = word->load(file, wordClass);
+ _word = word;
+ break;
+ }
+
+ case WC_ARTICLE:
+ case WC_CONJUNCTION:
+ case WC_PREPOSITION: {
+ TTword *word = new TTword(space, WC_UNKNOWN, 0);
+ result = word->load(file, wordClass);
+ _word = word;
+ break;
+ }
+
+ case WC_ADJECTIVE: {
+ TTadj *word = new TTadj(space, WC_UNKNOWN, 0, 0, 0);
+ result = word->load(file);
+ _word = word;
+ break;
+ }
+
+ case WC_PRONOUN: {
+ TTpronoun *word = new TTpronoun(space, WC_UNKNOWN, 0, 0, 0);
+ result = word->load(file);
+ _word = word;
+ break;
+ }
+
+ default:
+ result = 4;
+ break;
+ }
+
+ if (!skipFlag && _word) {
+ if (result) {
+ // Something wrong occurred, so delete word
+ delete _word;
+ _word = nullptr;
+ } else {
+ // Add the word to the master vocab list
+ addWord(_word);
+ }
+ }
+ }
+
+ // Close resource and return result
+ delete file;
+ return result;
+}
+
+void TTvocab::addWord(TTword *word) {
+ TTword *existingWord = findWord(word->_text);
+
+ if (existingWord) {
+ if (word->_synP) {
+ // Move over the synonym
+ existingWord->appendNode(word->_synP);
+ word->_synP = nullptr;
+ }
+
+ _word = nullptr;
+ if (word)
+ delete word;
+ } else if (_tailP) {
+ _tailP->_nextP = word;
+ _tailP = word;
+ } else {
+ if (!_headP)
+ _headP = word;
+
+ _tailP = word;
+ }
+}
+
+TTword *TTvocab::findWord(const TTstring &str) {
+ TTsynonym *tempNode = new TTsynonym();
+ bool flag = false;
+ TTword *word = _headP;
+
+ while (word && !flag) {
+ if (_vocabMode != 3 || strcmp(word->c_str(), str)) {
+ if (word->findSynByName(str, tempNode, _vocabMode))
+ flag = true;
+ else
+ word = word->_nextP;
+ } else {
+ flag = true;
+ }
+ }
+
+ delete tempNode;
+ return word;
+}
+
+TTword *TTvocab::getWord(TTstring &str, TTword **srcWord) const {
+ TTword *word = getPrimeWord(str, srcWord);
+
+ if (!word) {
+ TTstring tempStr(str);
+ if (tempStr.size() > 2) {
+ word = getSuffixedWord(tempStr);
+
+ if (!word)
+ word = getPrefixedWord(tempStr);
+ }
+ }
+
+ return word;
+}
+
+TTword *TTvocab::getPrimeWord(TTstring &str, TTword **srcWord) const {
+ TTsynonym tempSyn;
+ char c = str.charAt(0);
+ TTword *newWord = nullptr;
+ TTword *vocabP;
+
+ if (!Common::isDigit(c)) {
+ vocabP = _headP;
+ newWord = new TTword(str, WC_ABSTRACT, 300);
+ } else {
+ for (vocabP = _headP; vocabP && !newWord; vocabP = vocabP->_nextP) {
+ if (_vocabMode == 3 && !strcmp(str.c_str(), vocabP->c_str())) {
+ newWord = vocabP->copy();
+ newWord->_nextP = nullptr;
+ newWord->setSyn(nullptr);
+ } else if (vocabP->findSynByName(str, &tempSyn, _vocabMode)) {
+ // Create a copy of the word and the found synonym
+ TTsynonym *newSyn = new TTsynonym(tempSyn);
+ newSyn->_nextP = newSyn->_priorP = nullptr;
+ newWord = vocabP->copy();
+ newWord->_nextP = nullptr;
+ newWord->setSyn(newSyn);
+ }
+ }
+ }
+
+ if (srcWord)
+ // Pass out the pointer to the original word
+ *srcWord = vocabP;
+
+ // Return the new copy of the word
+ return newWord;
+}
+
+TTword *TTvocab::getSuffixedWord(TTstring &str) const {
+ TTstring tempStr(str);
+ TTword *word = nullptr;
+
+ if (tempStr.hasSuffix("s")) {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (!word) {
+ if (!tempStr.hasSuffix("e")) {
+ tempStr = str;
+ } else {
+ tempStr.deleteLastChar();
+ word = getPrimeWord(tempStr);
+ }
+ }
+
+ } else if (tempStr.hasSuffix("ing")) {
+ tempStr.deleteSuffix(3);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass == 1) {
+ delete word;
+ word = nullptr;
+ } else {
+ delete word;
+ word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0);
+ }
+ } else {
+ tempStr += "e";
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass != 1) {
+ delete word;
+ word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0);
+ }
+ } else {
+ tempStr.deleteSuffix(2);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass != 1) {
+ delete word;
+ word = new TTadj(str, WC_ADJECTIVE, 0, 0, 0);
+ }
+ } else {
+ tempStr = str;
+ }
+ }
+ }
+
+ } else if (tempStr.hasSuffix("ed")) {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (!word) {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+ }
+
+ if (word) {
+ if (word->_wordClass == WC_ACTION) {
+ static_cast<TTaction *>(word)->setVal(1);
+ }
+ } else {
+ tempStr = str;
+ }
+
+ } else if (tempStr.hasSuffix("ly")) {
+ tempStr.deleteSuffix(2);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ delete word;
+ word = new TTword(str, WC_ADVERB, 0);
+ } else {
+ tempStr = str;
+ }
+
+ } else if (tempStr.hasSuffix("er")) {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass == WC_ADJECTIVE) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0) {
+ adj->adjFn1(val1);
+ }
+ } else {
+ if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+ } else {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass == WC_ADJECTIVE) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0) {
+ adj->adjFn1(val1);
+ }
+ } else {
+ if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+ } else {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (word && word->_wordClass == WC_ADJECTIVE) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0) {
+ adj->adjFn1(val1);
+ }
+ } else {
+ if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+ }
+ }
+
+ } else if (tempStr.hasSuffix("est")) {
+ tempStr.deleteSuffix(2);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass == WC_ADJECTIVE) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0) {
+ adj->adjFn1(val1);
+ }
+ } else {
+ if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+ } else {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass == WC_ADJECTIVE) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0) {
+ adj->adjFn1(val1);
+ }
+ } else {
+ if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+ } else {
+ tempStr.deleteSuffix(1);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0) {
+ adj->adjFn1(val1);
+ }
+ } else {
+ if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+ }
+ }
+
+ } else if (tempStr.hasSuffix("s*")) {
+ tempStr.deleteSuffix(2);
+ word = getPrimeWord(tempStr);
+
+ if (word) {
+ if (word->_wordClass == WC_PRONOUN || word->_wordClass == WC_ADVERB) {
+ delete word;
+ TTstring isStr("is");
+ word = getPrimeWord(isStr);
+ } else {
+ switch (word->_id) {
+ case 200:
+ if (word->proc10() == 2) {
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5);
+ } else if (word->proc10() == 1) {
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 4);
+ }
+ break;
+
+ case 201:
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5);
+ break;
+
+ case 202:
+ case 203:
+ if (word->proc10() == 2) {
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 5);
+ } else {
+ int val = word->proc10() == 1 ? 0 : 4;
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, val);
+ }
+ break;
+
+ case 204:
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 6);
+ break;
+
+ default:
+ delete word;
+ word = new TTpronoun(tempStr, WC_PRONOUN, 601, 0, 0);
+ break;
+ }
+ }
+ }
+ }
+
+ if (word)
+ word->setSynStr(str);
+
+ return word;
+}
+
+TTword *TTvocab::getPrefixedWord(TTstring &str) const {
+ TTstring tempStr(str);
+ TTword *word = nullptr;
+ int prefixLen = 0;
+
+ if (tempStr.hasPrefix("pre")) {
+ prefixLen = 3;
+ } else if (tempStr.hasPrefix("re") || tempStr.hasPrefix("co")) {
+ prefixLen = 2;
+ } else if (tempStr.hasPrefix("inter") || tempStr.hasPrefix("multi")) {
+ prefixLen = 5;
+ } else if (tempStr.hasPrefix("over") || tempStr.hasPrefix("post") || tempStr.hasPrefix("self")) {
+ prefixLen = 4;
+ }
+
+ if (prefixLen) {
+ // Known prefix found, so scan for word without prefix
+ tempStr.deletePrefix(prefixLen);
+ word = getPrimeWord(tempStr);
+ if (word)
+ tempStr = str;
+
+ } else if (tempStr.hasPrefix("anti") || tempStr.hasPrefix("counter")) {
+ prefixLen = tempStr[0] == 'a' ? 4 : 7;
+
+ tempStr.deletePrefix(prefixLen);
+ word = getPrimeWord(tempStr);
+ if (!word)
+ tempStr = str;
+ else if (word->_wordClass == 8) {
+ delete word;
+ word = nullptr;
+ }
+
+ } else if (tempStr.hasPrefix("hyper") || tempStr.hasPrefix("super") ||
+ tempStr.hasPrefix("ultra")) {
+ tempStr.deletePrefix(5);
+ word = getPrimeWord(tempStr);
+
+ if (!word)
+ tempStr = str;
+ else if (word->_wordClass == WC_ADJECTIVE) {
+ TTadj *adj = static_cast<TTadj *>(word);
+ int val1 = word->proc15();
+ int val2 = word->proc15();
+
+ if (val2 < 5) {
+ if (--val1 > 0)
+ adj->adjFn1(val1);
+ } else if (++val1 < 11) {
+ adj->adjFn1(val1);
+ }
+ }
+ }
+
+ if (word) {
+ // Set the original word on either the found word or synonym
+ if (word->hasSynonyms())
+ word->setSynStr(str);
+ else
+ word->_text = str;
+ }
+
+ delete tempStr;
+ return word;
+}
+
+} // End of namespace Titanic
diff --git a/engines/titanic/true_talk/tt_vocab.h b/engines/titanic/true_talk/tt_vocab.h new file mode 100644 index 0000000000..fc7ee2e102 --- /dev/null +++ b/engines/titanic/true_talk/tt_vocab.h @@ -0,0 +1,96 @@ +/* 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. + * + */ + +#ifndef TITANIC_ST_VOCAB_H +#define TITANIC_ST_VOCAB_H + +#include "titanic/support/string.h" +#include "titanic/true_talk/tt_string.h" +#include "titanic/true_talk/tt_word.h" + +namespace Titanic { + +class TTvocab { +private: + TTword *_headP; + TTword *_tailP; + TTword *_word; + int _fieldC; + int _field10; + int _vocabMode; +private: + /** + * Load the vocab data + */ + int load(const CString &name); + + /** + * Adds a specified word to the vocab list + */ + void addWord(TTword *word); + + /** + * Scans the vocab list for an existing word match + */ + TTword *findWord(const TTstring &str); + + /** + * Scans the vocab list for a word with a synonym matching the passed string. + * If found, creates a new word instance that only has the matching synonym + * linked to it. + * @param str Word text to scan for + * @param srcWord Optional pointer to store the original word match was found on + * @returns A new word instance if a match if found, or null if not + */ + TTword *getPrimeWord(TTstring &str, TTword **srcWord = nullptr) const; + + /** + * Checks the passed word for common suffixes, like 's', 'ing', etc. and, if found, + * checks for a word match for the base word without the suffix. + * @param str Word to check + * @returns New word instance for found match, or nullptr otherwise + */ + TTword *getSuffixedWord(TTstring &str) const; + + /** + * Checks the passed word for common prefixes, and checks for a word + * match for the word without the given prefix + * @param str Word to check + * @returns New word instance for found match, or nullptr otherwise + */ + TTword *getPrefixedWord(TTstring &str) const; +public: + TTvocab(int val); + ~TTvocab(); + + /** + * Gets a matching word from the vocab list given a passed string + * @param str Word text to scan for + * @param srcWord Optional pointer to store the original word match was found on + * @returns A new word instance if a match if found, or null if not + */ + TTword *getWord(TTstring &str, TTword **srcWord = nullptr) const; +}; + +} // End of namespace Titanic + +#endif /* TITANIC_ST_VOCAB_H */ diff --git a/engines/titanic/true_talk/tt_word.cpp b/engines/titanic/true_talk/tt_word.cpp new file mode 100644 index 0000000000..df6ee5c7bc --- /dev/null +++ b/engines/titanic/true_talk/tt_word.cpp @@ -0,0 +1,226 @@ +/* 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_word.h" +#include "titanic/true_talk/tt_string_node.h" +#include "titanic/titanic.h" + +namespace Titanic { + +TTword::TTword(const TTstring &str, WordClass wordClass, int id) : _text(str), + _wordClass(wordClass), _id(id), _tag(0), _field24(0), + _field28(0), _synP(nullptr), _nextP(nullptr) { + _status = str.getStatus() == SS_VALID ? SS_VALID : SS_5; +} + +TTword::TTword(const TTword *src) { + if (src->getStatus() != SS_VALID) { + _status = SS_5; + return; + } + + _text = src->_text; + _wordClass = src->_wordClass; + _id = src->_id; + _tag = src->_tag; + _synP = nullptr; + + TTsynonym *priorSyn = nullptr; + for (TTsynonym *synP = _synP; synP && !_status;) { + TTsynonym *newSyn = new TTsynonym(synP); + if (!newSyn) { + _status = SS_7; + } else { + newSyn->_priorP = priorSyn; + newSyn->_nextP = nullptr; + + if (priorSyn) { + priorSyn->_nextP = newSyn; + } else { + _synP = newSyn; + } + + priorSyn = newSyn; + } + } + + _nextP = src->_nextP; + _field24 = src->_field24; + _field28 = src->_field28; +} + +TTword::~TTword() { + if (_synP) { + _synP->deleteSiblings(); + delete _synP; + } +} + +void TTword::deleteSiblings() { + while (_nextP) { + TTword *next = _nextP; + _nextP = next->_nextP; + delete next; + } +} + +int TTword::readSyn(SimpleFile *file) { + CString str; + int mode, val1; + + if (!file->scanf("%s %d %d", &str, &mode, &val1)) + return 8; + if (!testFileHandle(file)) + return 5; + + // Create new synanym node + TTsynonym *synNode = new TTsynonym(mode, str.c_str(), (FileHandle)val1); + + if (_synP) { + // A synonym already exists, so add new one as a tail + // at the end of the linked list of synonyms + _synP->addToTail(synNode); + } else { + // Very first synonym, so set it + _synP = synNode; + } + + return 0; +} + +void TTword::setSyn(TTsynonym *synP) { + if (_synP) { + _synP->deleteSiblings(); + delete _synP; + } + + _synP = synP; +} + +int TTword::setSynStr(TTstring &str) { + if (str.empty()) + return 4; + + TTstring *newStr = new TTstring(str); + TTsynonym *newSyn = new TTsynonym(4, newStr); + setSyn(newSyn); + return 0; +} + +void TTword::appendNode(TTsynonym *node) { + if (_synP) + _synP->addToTail(node); + else + _synP = node; +} + +int TTword::load(SimpleFile *file, WordClass wordClass) { + CString str1, str2; + int id; + + if (file->scanf("%d %s %s", &id, &str1, &str2)) { + _text = str1; + _id = id; + _tag = readNumber(str2.c_str()); + _wordClass = wordClass; + return 0; + } else { + return 3; + } +} + +uint TTword::readNumber(const char *str) { + uint numValue = *str; + if (*str == '0') { + numValue = MKTAG('Z', 'Z', 'Z', '['); + } else { + ++str; + for (int idx = 0; idx < 3; ++idx, ++str) + numValue = (numValue << 8) + *str; + } + + return numValue; +} + +bool TTword::testFileHandle(FileHandle file) const { + if (g_vm->_exeResources.is18Equals(3)) + return true; + + // TODO: Figure out why original compares passed file handle against specific values + return true; +} + +bool TTword::findSynByName(const TTstring &str, TTsynonym *dest, int mode) const { + if (!_synP) + return false; + + const TTsynonym *synP = static_cast<const TTsynonym *>(_synP->findByName(str, mode)); + if (synP) { + dest->copyFrom(synP); + dest->_priorP = nullptr; + dest->_nextP = nullptr; + + return true; + } else { + return false; + } +} + +bool TTword::compareTo(const char *str) const { + return _text == str; +} + +TTword *TTword::copy() const { + return new TTword(this); +} + +FileHandle TTword::getSynFile() const { + return _synP ? _synP->_file : HANDLE_STDIN; +} + +bool TTword::checkSynFile(FileHandle file) const { + return _synP && _synP->_file == file; +} + +void TTword::setSynFile(FileHandle file) { + if (_synP && testFileHandle(file)) + _synP->_file = file; +} + +TTstringStatus TTword::getChainStatus() const { + for (const TTword *word = this; word; word = word->_nextP) { + if (word->getStatus()) + return word->getStatus(); + } + + return SS_VALID; +} + +TTword *TTword::copyWords() { + TTword *result = copy(); + for (TTword *word = result; word; word = word->_nextP) + word->_nextP = word->_nextP->copy(); + + return result; +} + +} // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_word.h b/engines/titanic/true_talk/tt_word.h new file mode 100644 index 0000000000..7fd61c38ac --- /dev/null +++ b/engines/titanic/true_talk/tt_word.h @@ -0,0 +1,216 @@ +/* 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. + * + */ + +#ifndef TITANIC_TT_WORD_H +#define TITANIC_TT_WORD_H + +#include "titanic/support/simple_file.h" +#include "titanic/true_talk/tt_string.h" +#include "titanic/true_talk/tt_synonym.h" + +namespace Titanic { + +/** + * Types of words + */ +enum WordClass { + WC_UNKNOWN = 0, WC_ACTION = 1, WC_THING = 2, WC_ABSTRACT = 3, + WC_ARTICLE = 4, WC_CONJUNCTION = 5, WC_PRONOUN = 6, + WC_PREPOSITION = 7, WC_ADJECTIVE = 8, WC_ADVERB = 9 +}; + +class TTword { +protected: + TTstringStatus _status; + int _field24; + int _field28; +protected: + /** + * Read in a number + */ + uint readNumber(const char *str); + + bool testFileHandle(SimpleFile *file) const { return true; } + bool testFileHandle(FileHandle resHandle) const; +public: + TTword *_nextP; + TTsynonym *_synP; + TTstring _text; + WordClass _wordClass; + int _id; + uint _tag; +public: + TTword(const TTstring &str, WordClass wordClass, int val2); + TTword(const TTword *src); + virtual ~TTword(); + + /** + * Delete any following words chained to the word + */ + void deleteSiblings(); + + /** + * Read in a synonym for the given word + */ + int readSyn(SimpleFile *file); + + /** + * Set a new synonym for the word + */ + void setSyn(TTsynonym *synP); + + /** + * Set a new synonym string + */ + int setSynStr(TTstring &str); + + /** + * Returns true if synonyms have been set for the word + */ + bool hasSynonyms() const { return _synP != nullptr; } + + /** + * Either sets the first synonym for a word, or adds it to an existing one + */ + void appendNode(TTsynonym *node); + + /** + * Load the word + */ + int load(SimpleFile *file, WordClass wordClass); + + /** + * Finds a synonym in the word by name, if one exists + * @param str Name to search for + * @param dest Destination synonym instance to copy match into + * @returns Returns true if a match was found + */ + bool findSynByName(const TTstring &str, TTsynonym *dest, int mode) const; + + const char *c_str() const { return _text.c_str(); } + operator const char *() const { return c_str(); } + + /** + * Return the text of the word + */ + const TTstring getText() { return _text; } + + /** + * Compares the word's text to a passed string + */ + bool compareTo(const char *str) const; + + /** + * Compares the word's text to a passed string + */ + bool compareTo(TTstring *str) const { + return compareTo(str->c_str()); + } + + /** + * Return the status of the word + */ + TTstringStatus getStatus() const { return _status; } + + /** + * Returns true if the word is in a valid state + */ + bool isValid() const { return _status == SS_VALID; } + + /** + * Return the status of the entire word chain + */ + TTstringStatus getChainStatus() const; + + /** + * Returns true if the word is of the specified class + */ + bool isClass(WordClass wordClass) const { return _wordClass == wordClass; } + + /** + * Copy the word and any attached to it + */ + TTword *copyWords(); + + /** + * Creates a copy of the word + */ + virtual TTword *copy() const; + + virtual bool proc2(int val) const { return false; } + virtual int proc3() const { return -1; } + virtual void proc4() {} + virtual void proc5() {} + + /** + * Checks whether the word's tag is a known type + */ + virtual bool checkTag() const { return false; } + + /** + * Compare the word's tag to a given tag value + */ + virtual bool compareTagTo(uint tag) const { return false; } + + /** + * Return the tag associated with the word + */ + virtual uint getTag() const { return 0; } + + virtual bool proc9(int val) const { return false; } + virtual int proc10() const { return 0; } + virtual void proc11() {} + virtual bool proc12(int val) const { return false; } + virtual int proc13() const { return 0; } + virtual bool proc14(int val) const { return false; } + virtual int proc15() const { return -1; } + virtual bool proc16() const { return false; } + virtual bool proc17() const { return false; } + virtual bool proc18() const { return false; } + virtual bool comparePronounTo(int val) const { return false; } + virtual int proc20() const { return 0; } + + /** + * Returns the file associated with the word's first synonym + */ + virtual FileHandle getSynFile() const; + + /** + * Checks whether the file associated with the word's first + * synonym matches the specified file + */ + virtual bool checkSynFile(FileHandle file) const; + + /** + * Sets the file associated with a synonym + */ + virtual void setSynFile(FileHandle file); + + /** + * Dumps data associated with the word to file + */ + virtual int save(SimpleFile *file) const { return 0; } +}; + +} // End of namespace Titanic + +#endif /* TITANIC_TT_WORD_H */ |