aboutsummaryrefslogtreecommitdiff
path: root/engines/titanic/true_talk
diff options
context:
space:
mode:
Diffstat (limited to 'engines/titanic/true_talk')
-rw-r--r--engines/titanic/true_talk/barbot_script.cpp1200
-rw-r--r--engines/titanic/true_talk/barbot_script.h106
-rw-r--r--engines/titanic/true_talk/bellbot_script.cpp1905
-rw-r--r--engines/titanic/true_talk/bellbot_script.h129
-rw-r--r--engines/titanic/true_talk/deskbot_script.cpp1502
-rw-r--r--engines/titanic/true_talk/deskbot_script.h157
-rw-r--r--engines/titanic/true_talk/dialogue_file.cpp109
-rw-r--r--engines/titanic/true_talk/dialogue_file.h100
-rw-r--r--engines/titanic/true_talk/doorbot_script.cpp1013
-rw-r--r--engines/titanic/true_talk/doorbot_script.h113
-rw-r--r--engines/titanic/true_talk/liftbot_script.cpp695
-rw-r--r--engines/titanic/true_talk/liftbot_script.h109
-rw-r--r--engines/titanic/true_talk/maitred_script.cpp1057
-rw-r--r--engines/titanic/true_talk/maitred_script.h101
-rw-r--r--engines/titanic/true_talk/parrot_script.cpp110
-rw-r--r--engines/titanic/true_talk/parrot_script.h63
-rw-r--r--engines/titanic/true_talk/script_handler.cpp148
-rw-r--r--engines/titanic/true_talk/script_handler.h89
-rw-r--r--engines/titanic/true_talk/script_support.cpp226
-rw-r--r--engines/titanic/true_talk/script_support.h194
-rw-r--r--engines/titanic/true_talk/succubus_script.cpp237
-rw-r--r--engines/titanic/true_talk/succubus_script.h75
-rw-r--r--engines/titanic/true_talk/title_engine.cpp83
-rw-r--r--engines/titanic/true_talk/title_engine.h113
-rw-r--r--engines/titanic/true_talk/true_talk_manager.cpp597
-rw-r--r--engines/titanic/true_talk/true_talk_manager.h245
-rw-r--r--engines/titanic/true_talk/tt_action.cpp69
-rw-r--r--engines/titanic/true_talk/tt_action.h57
-rw-r--r--engines/titanic/true_talk/tt_adj.cpp84
-rw-r--r--engines/titanic/true_talk/tt_adj.h67
-rw-r--r--engines/titanic/true_talk/tt_concept.cpp308
-rw-r--r--engines/titanic/true_talk/tt_concept.h172
-rw-r--r--engines/titanic/true_talk/tt_concept_node.cpp165
-rw-r--r--engines/titanic/true_talk/tt_concept_node.h75
-rw-r--r--engines/titanic/true_talk/tt_hist.cpp36
-rw-r--r--engines/titanic/true_talk/tt_hist.h47
-rw-r--r--engines/titanic/true_talk/tt_major_word.cpp70
-rw-r--r--engines/titanic/true_talk/tt_major_word.h54
-rw-r--r--engines/titanic/true_talk/tt_node.cpp89
-rw-r--r--engines/titanic/true_talk/tt_node.h69
-rw-r--r--engines/titanic/true_talk/tt_npc_script.cpp1009
-rw-r--r--engines/titanic/true_talk/tt_npc_script.h355
-rw-r--r--engines/titanic/true_talk/tt_parser.cpp1713
-rw-r--r--engines/titanic/true_talk/tt_parser.h208
-rw-r--r--engines/titanic/true_talk/tt_picture.cpp102
-rw-r--r--engines/titanic/true_talk/tt_picture.h74
-rw-r--r--engines/titanic/true_talk/tt_pronoun.cpp73
-rw-r--r--engines/titanic/true_talk/tt_pronoun.h65
-rw-r--r--engines/titanic/true_talk/tt_quotes.cpp145
-rw-r--r--engines/titanic/true_talk/tt_quotes.h77
-rw-r--r--engines/titanic/true_talk/tt_quotes_tree.cpp214
-rw-r--r--engines/titanic/true_talk/tt_quotes_tree.h84
-rw-r--r--engines/titanic/true_talk/tt_response.cpp71
-rw-r--r--engines/titanic/true_talk/tt_response.h66
-rw-r--r--engines/titanic/true_talk/tt_room_script.cpp60
-rw-r--r--engines/titanic/true_talk/tt_room_script.h103
-rw-r--r--engines/titanic/true_talk/tt_script_base.cpp156
-rw-r--r--engines/titanic/true_talk/tt_script_base.h131
-rw-r--r--engines/titanic/true_talk/tt_scripts.cpp97
-rw-r--r--engines/titanic/true_talk/tt_scripts.h90
-rw-r--r--engines/titanic/true_talk/tt_sentence.cpp347
-rw-r--r--engines/titanic/true_talk/tt_sentence.h132
-rw-r--r--engines/titanic/true_talk/tt_sentence_node.cpp34
-rw-r--r--engines/titanic/true_talk/tt_sentence_node.h41
-rw-r--r--engines/titanic/true_talk/tt_string.cpp164
-rw-r--r--engines/titanic/true_talk/tt_string.h173
-rw-r--r--engines/titanic/true_talk/tt_string_node.cpp68
-rw-r--r--engines/titanic/true_talk/tt_string_node.h59
-rw-r--r--engines/titanic/true_talk/tt_synonym.cpp87
-rw-r--r--engines/titanic/true_talk/tt_synonym.h56
-rw-r--r--engines/titanic/true_talk/tt_talker.cpp52
-rw-r--r--engines/titanic/true_talk/tt_talker.h65
-rw-r--r--engines/titanic/true_talk/tt_title_script.cpp31
-rw-r--r--engines/titanic/true_talk/tt_title_script.h43
-rw-r--r--engines/titanic/true_talk/tt_vocab.cpp557
-rw-r--r--engines/titanic/true_talk/tt_vocab.h96
-rw-r--r--engines/titanic/true_talk/tt_word.cpp226
-rw-r--r--engines/titanic/true_talk/tt_word.h216
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 &quotes = 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 = &ne;
+ 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 */