1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "titanic/true_talk/tt_parser.h"
24 #include "titanic/support/files_manager.h"
25 #include "titanic/true_talk/script_handler.h"
26 #include "titanic/true_talk/true_talk_manager.h"
27 #include "titanic/true_talk/tt_action.h"
28 #include "titanic/true_talk/tt_concept.h"
29 #include "titanic/true_talk/tt_picture.h"
30 #include "titanic/true_talk/tt_sentence.h"
31 #include "titanic/true_talk/tt_word.h"
32 #include "titanic/titanic.h"
33 #include "titanic/translation.h"
34
35 namespace Titanic {
36
TTparser(CScriptHandler * owner)37 TTparser::TTparser(CScriptHandler *owner) : _owner(owner), _sentenceConcept(nullptr),
38 _sentence(nullptr), _fieldC(0), _field10(0), _field14(0),
39 _currentWordP(nullptr), _nodesP(nullptr), _conceptP(nullptr) {
40 loadArrays();
41 }
42
~TTparser()43 TTparser::~TTparser() {
44 clear();
45 }
46
clear()47 void TTparser::clear() {
48 if (_nodesP) {
49 _nodesP->deleteSiblings();
50 delete _nodesP;
51 _nodesP = nullptr;
52 }
53
54 if (_conceptP) {
55 _conceptP->deleteSiblings();
56 delete _conceptP;
57 _conceptP = nullptr;
58 }
59
60 delete _currentWordP;
61 _currentWordP = nullptr;
62 }
63
loadArray(StringArray & arr,const CString & name)64 void TTparser::loadArray(StringArray &arr, const CString &name) {
65 Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name);
66 while (r->pos() < r->size())
67 arr.push_back(readStringFromStream(r));
68 delete r;
69 }
70
loadArrays()71 void TTparser::loadArrays() {
72 loadArray(_replacements1, "TEXT/REPLACEMENTS1");
73 loadArray(_replacements2, "TEXT/REPLACEMENTS2");
74 loadArray(_replacements3, "TEXT/REPLACEMENTS3");
75 if (g_language == Common::DE_DEU)
76 loadArray(_replacements4, "TEXT/REPLACEMENTS4");
77 loadArray(_phrases, "TEXT/PHRASES");
78 loadArray(_pronouns, "TEXT/PRONOUNS");
79
80 Common::SeekableReadStream *r = g_vm->_filesManager->getResource("TEXT/NUMBERS");
81 while (r->pos() < r->size()) {
82 NumberEntry ne;
83 ne._text = readStringFromStream(r);
84 ne._value = r->readSint32LE();
85 ne._flags = r->readUint32LE();
86 _numbers.push_back(ne);
87 }
88 delete r;
89 }
90
preprocess(TTsentence * sentence)91 int TTparser::preprocess(TTsentence *sentence) {
92 _sentence = sentence;
93 if (normalize(sentence))
94 return 0;
95
96 if (g_language == Common::DE_DEU) {
97 preprocessGerman(sentence->_normalizedLine);
98 } else {
99 // Scan for and replace common slang and contractions with verbose versions
100 searchAndReplace(sentence->_normalizedLine, _replacements1);
101 searchAndReplace(sentence->_normalizedLine, _replacements2);
102 }
103
104 // Check entire normalized line against common phrases to replace
105 for (uint idx = 0; idx < _phrases.size(); idx += 2) {
106 if (!_phrases[idx].compareTo(sentence->_normalizedLine))
107 sentence->_normalizedLine = _phrases[idx + 1];
108 }
109
110 if (g_language == Common::DE_DEU) {
111 // Scan for and replace common slang and contractions with verbose versions
112 searchAndReplace(sentence->_normalizedLine, _replacements1);
113 searchAndReplace(sentence->_normalizedLine, _replacements2);
114 }
115
116 // Do a further search and replace of roman numerals to decimal
117 searchAndReplace(sentence->_normalizedLine, _replacements3);
118
119 // Replace any roman numerals, spelled out words, etc. with decimal numbers
120 CTrueTalkManager::_v1 = -1000;
121 int idx = 0;
122 do {
123 idx = replaceNumbers(sentence->_normalizedLine, idx);
124 } while (idx >= 0);
125
126 if (CTrueTalkManager::_v1 == -1000 && !sentence->_normalizedLine.empty()) {
127 // Scan the text for any numeric digits
128 for (const char *strP = sentence->_normalizedLine.c_str(); *strP; ++strP) {
129 if (Common::isDigit(*strP)) {
130 // Found digit, so convert it and any following ones
131 CTrueTalkManager::_v1 = atoi(strP);
132 break;
133 }
134 }
135 }
136
137 return 0;
138 }
139
normalize(TTsentence * sentence)140 int TTparser::normalize(TTsentence *sentence) {
141 TTstring *destLine = new TTstring();
142 const TTstring &srcLine = sentence->_initialLine;
143 int srcSize = srcLine.size();
144 int savedIndex = 0;
145 int counter1 = 0;
146 int commandVal;
147
148 for (int index = 0; index < srcSize; ++index) {
149 char c = srcLine[index];
150 if (Common::isLower(c)) {
151 (*destLine) += c;
152 } else if (Common::isSpace(c)) {
153 if (!destLine->empty() && destLine->lastChar() != ' ')
154 (*destLine) += ' ';
155 } else if (Common::isUpper(c)) {
156 (*destLine) += tolower(c);
157 } else if (Common::isDigit(c)) {
158 if (c == '0' && isEmoticon(srcLine, index)) {
159 sentence->set38(10);
160 } else {
161 // Iterate through all the digits of the number
162 (*destLine) += c;
163 while (Common::isDigit(srcLine[index + 1]))
164 (*destLine) += srcLine[++index];
165 }
166 } else if (Common::isPunct(c)) {
167 bool flag = false;
168 switch (c) {
169 case '!':
170 sentence->set38(3);
171 break;
172
173 case '\'':
174 if (!normalizeContraction(srcLine, index, *destLine))
175 flag = true;
176 break;
177
178 case '.':
179 sentence->set38(1);
180 break;
181
182 case ':':
183 commandVal = isEmoticon(srcLine, index);
184 if (commandVal) {
185 sentence->set38(commandVal);
186 index += 2;
187 } else {
188 flag = true;
189 }
190 break;
191
192 case ';':
193 commandVal = isEmoticon(srcLine, index);
194 if (commandVal == 6) {
195 sentence->set38(7);
196 index += 2;
197 } else if (commandVal != 0) {
198 sentence->set38(commandVal);
199 index += 2;
200 }
201 break;
202
203 case '<':
204 ++index;
205 commandVal = isEmoticon(srcLine, index);
206 if (commandVal == 6) {
207 sentence->set38(12);
208 } else {
209 --index;
210 flag = true;
211 }
212 break;
213
214 case '>':
215 ++index;
216 commandVal = isEmoticon(srcLine, index);
217 if (commandVal == 6 || commandVal == 9) {
218 sentence->set38(11);
219 } else {
220 --index;
221 flag = true;
222 }
223 break;
224
225 case '?':
226 sentence->set38(2);
227 break;
228
229 default:
230 flag = true;
231 break;
232 }
233
234 if (flag && (!savedIndex || (index - savedIndex) == 1))
235 ++counter1;
236
237 savedIndex = index;
238 }
239 }
240
241 if (counter1 >= 4)
242 sentence->set38(4);
243
244 // Remove any trailing spaces
245 while (destLine->hasSuffix(" "))
246 destLine->deleteLastChar();
247
248 // Copy out the normalized line
249 sentence->_normalizedLine = *destLine;
250 delete destLine;
251
252 return 0;
253 }
254
isEmoticon(const TTstring & str,int & index)255 int TTparser::isEmoticon(const TTstring &str, int &index) {
256 if (str[index] != ':' && str[index] != ';')
257 return 0;
258
259 if (str[index + 1] != '-')
260 return 0;
261
262 index += 2;
263 switch (str[index]) {
264 case '(':
265 case '<':
266 return 8;
267
268 case ')':
269 case '>':
270 return 6;
271
272 case 'P':
273 case 'p':
274 return 9;
275
276 default:
277 return 5;
278 }
279 }
280
normalizeContraction(const TTstring & srcLine,int & srcIndex,TTstring & destLine)281 bool TTparser::normalizeContraction(const TTstring &srcLine, int &srcIndex, TTstring &destLine) {
282 int startIndex = srcIndex + 1;
283 switch (srcLine[startIndex]) {
284 case 'd':
285 srcIndex += 2;
286 if (srcLine.compareAt(srcIndex, " a ") || srcLine.compareAt(srcIndex, " the ")) {
287 destLine += " had";
288 } else {
289 destLine += " would";
290 }
291
292 srcIndex = startIndex;
293 break;
294
295 case 'l':
296 if (srcLine[srcIndex + 2] == 'l') {
297 // 'll ending
298 destLine += " will";
299 srcIndex = startIndex;
300 }
301 break;
302
303 case 'm':
304 // 'm ending
305 destLine += " am";
306 srcIndex = startIndex;
307 break;
308
309 case 'r':
310 // 're ending
311 if (srcLine[srcIndex + 2] == 'e') {
312 destLine += " are";
313 srcIndex = startIndex;
314 }
315 break;
316
317 case 's':
318 destLine += "s*";
319 srcIndex = startIndex;
320 break;
321
322 case 't':
323 if (srcLine[srcIndex - 1] == 'n' && srcIndex >= 3) {
324 if (srcLine[srcIndex - 3] == 'c' && srcLine[srcIndex - 2] == 'a' &&
325 (srcIndex == 3 || srcLine[srcIndex - 4])) {
326 // can't -> can not
327 destLine += 'n';
328 } else if (srcLine[srcIndex - 3] == 'w' && srcLine[srcIndex - 2] == 'o' &&
329 (srcIndex == 3 || srcLine[srcIndex - 4])) {
330 // won't -> will not
331 destLine.deleteLastChar();
332 destLine.deleteLastChar();
333 destLine += "ill";
334 } else if (srcLine[srcIndex - 3] == 'a' && srcLine[srcIndex - 2] == 'i' &&
335 (srcIndex == 3 || srcLine[srcIndex - 4])) {
336 // ain't -> am not
337 destLine.deleteLastChar();
338 destLine.deleteLastChar();
339 destLine += "m";
340 } else if (srcLine.hasSuffix(" sha") ||
341 (srcIndex == 4 && srcLine.hasSuffix("sha"))) {
342 // shan't -> shall not
343 destLine.deleteLastChar();
344 destLine += "ll";
345 }
346
347 destLine += " not";
348 }
349 break;
350
351 case 'v':
352 // 've ending
353 if (srcLine[startIndex + 2] == 'e') {
354 destLine += " have";
355 srcIndex = startIndex;
356 }
357 break;
358
359 default:
360 break;
361 }
362
363 return false;
364 }
365
searchAndReplace(TTstring & line,const StringArray & strings)366 void TTparser::searchAndReplace(TTstring &line, const StringArray &strings) {
367 int charIndex = 0;
368 while (charIndex >= 0)
369 charIndex = searchAndReplace(line, charIndex, strings);
370 }
371
searchAndReplace(TTstring & line,int startIndex,const StringArray & strings)372 int TTparser::searchAndReplace(TTstring &line, int startIndex, const StringArray &strings) {
373 int lineSize = line.size();
374 if (startIndex >= lineSize)
375 return -1;
376
377 for (uint idx = 0; idx < strings.size(); idx += 2) {
378 const CString &origStr = strings[idx];
379 const CString &replacementStr = strings[idx + 1];
380
381 if (!strncmp(line.c_str() + startIndex, origStr.c_str(), strings[idx].size())) {
382 // Ensure that that a space follows the match, or the end of string,
383 // so the end of the string doesn't match on parts of larger words
384 char c = line[startIndex + strings[idx].size()];
385 if (c == ' ' || c == '\0') {
386 // Replace the text in the line with it's replacement
387 line = CString(line.c_str(), line.c_str() + startIndex) + replacementStr +
388 CString(line.c_str() + startIndex + origStr.size());
389 lineSize = line.size();
390
391 startIndex += replacementStr.size();
392 break;
393 }
394 }
395 }
396
397 // Skip to the end of the current word
398 while (startIndex < lineSize && line[startIndex] != ' ')
399 ++startIndex;
400 if (startIndex == lineSize)
401 return -1;
402
403 // ..and all spaces following it until the start of the next word
404 while (startIndex < lineSize && line[startIndex] == ' ')
405 ++startIndex;
406 if (startIndex == lineSize)
407 return -1;
408
409 // Return index of the start of the next word
410 return startIndex;
411 }
412
replaceNumbers(TTstring & line,int startIndex)413 int TTparser::replaceNumbers(TTstring &line, int startIndex) {
414 int index = startIndex;
415 const NumberEntry *numEntry = replaceNumbers2(line, &index);
416 if (!numEntry || !(numEntry->_flags & NF_2))
417 return index;
418
419 bool flag1 = false, flag2 = false, flag3 = false;
420 int total = 0, factor = 0, endIndex;
421
422 do {
423 if (!(numEntry->_flags & NF_1)) {
424 flag2 = true;
425 if (numEntry->_flags & NF_8)
426 flag1 = true;
427
428 if (numEntry->_flags & NF_4) {
429 flag3 = true;
430 factor *= numEntry->_value;
431 }
432
433 if (numEntry->_flags & NF_2) {
434 if (flag3) {
435 total += factor;
436 factor = 0;
437 }
438
439 factor += numEntry->_value;
440 }
441 }
442
443 endIndex = index;
444 } while ((numEntry = replaceNumbers2(line, &index)) != nullptr);
445
446 index = endIndex;
447 if (!flag2)
448 return index;
449
450 if (index >= 0) {
451 if (line[index - 1] != ' ')
452 return index;
453 }
454
455 total += factor;
456 CTrueTalkManager::_v1 = total;
457 if (flag1)
458 total = -total;
459
460 CString numStr = CString::format("%d", total);
461 line = CString::format("%s%s%s",
462 CString(line.c_str(), line.c_str() + startIndex).c_str(),
463 numStr.c_str(),
464 (index == -1) ? "" : line.c_str() + index - 1
465 );
466
467 index = startIndex + numStr.size();
468 return index;
469 }
470
replaceNumbers2(TTstring & line,int * startIndex)471 const NumberEntry *TTparser::replaceNumbers2(TTstring &line, int *startIndex) {
472 int lineSize = line.size();
473 int index = *startIndex;
474 if (index < 0 || index >= lineSize) {
475 *startIndex = -1;
476 return nullptr;
477 }
478
479 NumberEntry *numEntry = nullptr;
480
481 for (uint idx = 0; idx < _numbers.size(); ++idx) {
482 NumberEntry &ne = _numbers[idx];
483 if (!strncmp(line.c_str() + index, ne._text.c_str(), ne._text.size())) {
484 if ((ne._flags & NF_10) || (index + (int)ne._text.size()) >= lineSize ||
485 line[index + ne._text.size()] == ' ') {
486 *startIndex += ne._text.size();
487 numEntry = ≠
488 break;
489 }
490 }
491 }
492
493 if (!numEntry || !(numEntry->_flags & NF_10)) {
494 // Skip to end of current word
495 while (*startIndex < lineSize && !Common::isSpace(line[*startIndex]))
496 ++*startIndex;
497 }
498
499 // Skip over following spaces until start of following word is reached
500 while (*startIndex < lineSize && Common::isSpace(line[*startIndex]))
501 ++*startIndex;
502
503 if (*startIndex >= lineSize)
504 *startIndex = -1;
505
506 return numEntry;
507 }
508
findFrames(TTsentence * sentence)509 int TTparser::findFrames(TTsentence *sentence) {
510 _sentenceConcept = &sentence->_sentenceConcept;
511 _sentence = sentence;
512
513 TTstring *line = sentence->_normalizedLine.copy();
514 TTstring wordString;
515 int status = 0;
516 for (int ctr = 1; status <= 1; ++ctr) {
517 // Keep stripping words off the start of the passed input
518 wordString = line->tokenize(" \n");
519 if (wordString.empty())
520 break;
521
522 TTword *srcWord = nullptr;
523 TTword *word = _owner->_vocab->getWord(wordString, &srcWord);
524 sentence->storeVocabHit(srcWord);
525
526 if (!word && ctr == 1) {
527 word = new TTword(wordString, WC_UNKNOWN, 0);
528 }
529
530 for (TTword *currP = word; currP && status <= 1; currP = currP->_nextP)
531 status = processRequests(currP);
532
533 if (word) {
534 word->deleteSiblings();
535 delete word;
536 }
537 }
538
539 if (status <= 1) {
540 status = checkForAction();
541 clear();
542 }
543
544 delete line;
545 return status;
546 }
547
loadRequests(TTword * word)548 int TTparser::loadRequests(TTword *word) {
549 int status = 0;
550
551 if (word->_tag != MKTAG('Z', 'Z', 'Z', '['))
552 addNode(word->_tag);
553
554 switch (word->_wordClass) {
555 case WC_UNKNOWN:
556 break;
557
558 case WC_ACTION:
559 if (word->_id != 0x70 && word->_id != 0x71)
560 addNode(CHECK_COMMAND_FORM);
561 addNode(SET_ACTION);
562
563 switch (word->_id) {
564 case 101:
565 case 110:
566 addNode(SEEK_OBJECT);
567 addNode(SEEK_ACTOR);
568 break;
569
570 case 102:
571 addNode(SEEK_ACTOR);
572 break;
573
574 case 103:
575 case 111:
576 addNode(SEEK_FROM);
577 addNode(SEEK_TO);
578 addNode(SEEK_OBJECT);
579 addNode(SEEK_ACTOR);
580 break;
581
582 case 104:
583 case 107:
584 addNode(SEEK_NEW_FRAME);
585 addNode(SEEK_OBJECT);
586 addNode(SEEK_ACTOR);
587 break;
588
589 case 106:
590 addNode(SEEK_TO);
591 addNode(SEEK_ACTOR);
592 break;
593
594 case 108:
595 addNode(SEEK_OBJECT);
596 addNode(SEEK_ACTOR);
597 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
598 break;
599
600 case 112:
601 case 113:
602 addNode(SEEK_STATE);
603 addNode(SEEK_OBJECT);
604 break;
605
606 default:
607 break;
608 }
609
610 if (_sentenceConcept) {
611 if (_sentenceConcept->get18() == 0 || _sentenceConcept->get18() == 2) {
612 TTaction *action = dynamic_cast<TTaction *>(word);
613 _sentenceConcept->set18(action->getVal());
614 }
615 }
616 break;
617
618 case WC_THING:
619 if (word->checkTag() && _sentence->_field58 > 0)
620 _sentence->_field58--;
621 addNode(SEEK_MODIFIERS);
622 break;
623
624 case WC_ABSTRACT:
625 switch (word->_id) {
626 case 300:
627 addNode(SEEK_MODIFIERS);
628 status = 1;
629 break;
630
631 case 306:
632 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
633 addNode(SEEK_ACTOR);
634 break;
635
636 case 307:
637 case 308:
638 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
639 break;
640
641 default:
642 break;
643 }
644
645 if (status != 1) {
646 addToConceptList(word);
647 addNode(SEEK_STATE);
648 addNode(SEEK_MODIFIERS);
649 }
650 break;
651
652 case WC_ARTICLE:
653 addNode(EXPECT_THING);
654 status = 1;
655 break;
656
657 case WC_CONJUNCTION:
658 if (_sentence->checkCategory()) {
659 _sentenceConcept->_field1C = 1;
660 _sentenceConcept = _sentenceConcept->addSibling();
661 delete this;
662 } else {
663 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
664 }
665 break;
666
667 case WC_PRONOUN:
668 status = fn2(word);
669 break;
670
671 case WC_PREPOSITION:
672 switch (word->_id) {
673 case 700:
674 addNode(SEEK_OBJECT_OVERRIDE);
675 addNode(SEEK_OBJECT);
676 break;
677 case 701:
678 addNode(SEEK_LOCATION);
679 break;
680 case 702:
681 status = 1;
682 break;
683 case 703:
684 addNode(SEEK_TO_OVERRIDE);
685 break;
686 case 704:
687 addNode(SEEK_FROM_OVERRIDE);
688 break;
689 default:
690 break;
691 }
692 break;
693
694 case WC_ADJECTIVE:
695 if (word->_id == 304) {
696 // Nothing
697 } else if (word->_id == 801) {
698 addNode(STATE_IDENTITY);
699 } else {
700 if (word->proc16())
701 _sentence->_field58++;
702 if (word->proc17())
703 _sentence->_field58++;
704 }
705 break;
706
707 case WC_ADVERB:
708 switch (word->_id) {
709 case 900:
710 case 901:
711 case 902:
712 case 904:
713 if (_sentence->_category == 9) {
714 _sentenceConcept->_field1C = 1;
715 _sentenceConcept = _sentenceConcept->addSibling();
716 addNode(CHECK_COMMAND_FORM);
717 }
718 else {
719 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
720 addNode(SEEK_STATE);
721 addNode(CHECK_COMMAND_FORM);
722 }
723 break;
724
725 case 905:
726 case 907:
727 case 908:
728 case 909:
729 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
730 break;
731
732 case 906:
733 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
734 status = 1;
735 break;
736
737 case 910:
738 addNode(SEEK_ACTOR);
739 addNode(COMPLEX_VERB);
740 addNode(WORD_TYPE_IS_SENTENCE_TYPE);
741 addNode(SEEK_MODIFIERS);
742 status = 1;
743 break;
744
745 default:
746 break;
747 }
748
749 if (word->_id == 906) {
750 addNode(SEEK_MODIFIERS);
751 status = 1;
752 }
753 break;
754
755 default:
756 break;
757 }
758
759 return status;
760 }
761
considerRequests(TTword * word)762 int TTparser::considerRequests(TTword *word) {
763 if (!_nodesP || !word)
764 return 0;
765
766 TTconcept *concept = nullptr;
767 int status = 0;
768 bool flag = false;
769 bool modifierFlag = false;
770 int seekVal = 0;
771
772 for (TTparserNode *nodeP = _nodesP; nodeP; ) {
773 switch (nodeP->_tag) {
774 case CHECK_COMMAND_FORM:
775 if (_sentenceConcept->_concept1P && _sentence->_category == 1 &&
776 !_sentenceConcept->_concept0P) {
777 TTconcept *newConcept = new TTconcept(_sentence->_npcScript, ST_NPC_SCRIPT);
778 _sentenceConcept->_concept0P = newConcept;
779 _sentenceConcept->_field18 = 3;
780 }
781
782 flag = true;
783 break;
784
785 case EXPECT_THING:
786 if (!word->_wordClass) {
787 word->_wordClass = WC_THING;
788 addToConceptList(word);
789 addNode(SEEK_MODIFIERS);
790 }
791
792 flag = true;
793 break;
794
795 case OBJECT_IS_TO:
796 flag = resetConcept(&_sentenceConcept->_concept2P, 3);
797 break;
798
799 case SEEK_ACTOR:
800 case MKTAG('S', 'A', 'C', 'T'):
801 if (!_sentenceConcept->_concept0P) {
802 flag = filterConcepts(5, 0);
803 } else if (_sentenceConcept->_concept0P->compareTo("?") &&
804 (_sentenceConcept->_concept1P && isWordId(_sentenceConcept->_concept1P, 113)) &&
805 word->_wordClass == WC_THING) {
806 TTconcept *oldConcept = _sentenceConcept->_concept0P;
807 _sentenceConcept->_concept0P = nullptr;
808 flag = filterConcepts(5, 2);
809 if (flag)
810 delete oldConcept;
811 } else {
812 flag = true;
813 }
814 break;
815
816 case SEEK_OBJECT:
817 if (_sentenceConcept->_concept2P && _sentenceConcept->_concept2P->compareTo(word)) {
818 flag = true;
819 } else if (!_sentenceConcept->_concept2P) {
820 if (filterConcepts(5, 2) && _sentenceConcept->_concept2P->checkWordId1())
821 addNode(SEEK_OBJECT);
822 } else if (word->_wordClass == WC_THING && _sentence->fn2(2, TTstring("?"), _sentenceConcept)) {
823 TTconcept *oldConcept = _sentenceConcept->_concept2P;
824 flag = filterConcepts(5, 2);
825 _sentenceConcept->_concept2P->_field20 = oldConcept->get20();
826 if (flag)
827 delete oldConcept;
828 } else if (!_sentenceConcept->_concept3P &&
829 (!_sentenceConcept->_concept1P || (getWordId(_sentenceConcept->_concept1P) != 113 &&
830 getWordId(_sentenceConcept->_concept1P) != 112)) &&
831 _sentenceConcept->_concept2P->checkWordId1() &&
832 (word->_wordClass == WC_THING || word->_wordClass == WC_PRONOUN)) {
833 _sentenceConcept->changeConcept(0, &_sentenceConcept->_concept2P, 3);
834
835 if (_conceptP && isWordId(_conceptP, word->_id)) {
836 status = _sentenceConcept->replaceConcept(0, 2, _conceptP);
837 removeConcept(_conceptP);
838 } else {
839 status = _sentenceConcept->createConcept(0, 2, word);
840 }
841
842 if (!status && !_sentenceConcept->_concept4P && _sentenceConcept->_concept0P) {
843 TTconcept *oldConcept = _sentenceConcept->_concept2P;
844 flag = filterConcepts(5, 2);
845 _sentenceConcept->_concept2P->_field20 = oldConcept->get20();
846 if (flag)
847 delete oldConcept;
848 } else {
849 flag = true;
850 }
851 }
852 break;
853
854 case SEEK_OBJECT_OVERRIDE:
855 if ((word->_wordClass == WC_THING || word->_wordClass == WC_PRONOUN) &&
856 _sentence->fn2(2, TTstring("thePlayer"), _sentenceConcept) &&
857 !_sentenceConcept->_concept3P) {
858 _sentenceConcept->_concept3P = _sentenceConcept->_concept2P;
859 _sentenceConcept->_concept2P = nullptr;
860
861 flag = filterConcepts(5, 2);
862 if (!flag) {
863 status = _sentenceConcept->createConcept(0, 2, word);
864 }
865 }
866 break;
867
868 case SEEK_TO:
869 if (!_sentenceConcept->_concept3P) {
870 if (!filterConcepts(8, 3))
871 flag = filterConcepts(3, 3);
872 } else {
873 flag = true;
874 }
875 break;
876
877 case SEEK_FROM:
878 if (!_sentenceConcept->_concept4P) {
879 if (!filterConcepts(8, 4))
880 flag = filterConcepts(3, 3);
881 } else {
882 flag = true;
883 }
884 break;
885
886 case SEEK_TO_OVERRIDE:
887 if (word->_wordClass == WC_ACTION) {
888 status = _sentenceConcept->createConcept(0, 1, word);
889 if (!status) {
890 seekVal = _sentenceConcept->_field18;
891 _sentenceConcept->_field18 = 4;
892 flag = true;
893 }
894 } else if (word->_id == 703) {
895 if (_sentenceConcept->_concept2P) {
896 delete _sentenceConcept->_concept2P;
897 _sentenceConcept->_concept2P = nullptr;
898 }
899
900 if (_sentenceConcept->_concept4P || !_sentenceConcept->_concept0P) {
901 addNode(SEEK_TO);
902 } else {
903 _sentenceConcept->changeConcept(1, &_sentenceConcept->_concept0P, 4);
904 concept = nullptr;
905 addNode(SEEK_TO);
906 }
907 } else {
908 flag = true;
909 }
910 break;
911
912 case SEEK_FROM_OVERRIDE:
913 if (_sentenceConcept->_concept4P) {
914 delete _sentenceConcept->_concept4P;
915 _sentenceConcept->_concept4P = nullptr;
916 }
917
918 addNode(SEEK_FROM);
919 flag = true;
920 break;
921
922 case SEEK_LOCATION:
923 addNode(SEEK_OBJECT);
924 _sentenceConcept->createConcept(0, 5, word);
925 flag = true;
926 break;
927
928 case SEEK_OWNERSHIP:
929 if (word->_id == 601) {
930 if (_conceptP->findByWordClass(WC_THING))
931 status = _conceptP->setOwner(word, false);
932
933 flag = true;
934 }
935 break;
936
937 case SEEK_STATE:
938 if (_sentenceConcept->_concept5P) {
939 if (_sentenceConcept->_concept5P->findByWordId(306) ||
940 _sentenceConcept->_concept5P->findByWordId(904)) {
941 TTconcept *oldConcept = _sentenceConcept->_concept5P;
942 _sentenceConcept->_concept5P = nullptr;
943 flag = filterConcepts(9, 5);
944 if (flag)
945 delete oldConcept;
946 } else {
947 flag = true;
948 }
949 } else {
950 flag = filterConcepts(9, 5);
951 if (!flag && word->_wordClass == WC_ADVERB) {
952 status = _sentenceConcept->createConcept(1, 5, word);
953 flag = true;
954 }
955 }
956 break;
957
958 case SEEK_MODIFIERS:
959 if (!modifierFlag) {
960 bool tempFlag = false;
961
962 switch (word->_wordClass) {
963 case WC_ACTION:
964 status = processModifiers(1, word);
965 break;
966 case WC_THING:
967 status = processModifiers(2, word);
968 break;
969 case WC_ABSTRACT:
970 if (word->_id != 300) {
971 status = processModifiers(3, word);
972 } else if (!_conceptP || !_conceptP->findByWordClass(WC_THING)) {
973 status = processModifiers(3, word);
974 } else {
975 word->_id = atoi(word->_text.c_str());
976 }
977 break;
978 case WC_PRONOUN:
979 if (word->_id != 602)
980 addToConceptList(word);
981 break;
982 case WC_ADJECTIVE: {
983 TTconcept *conceptP = _conceptP->findByWordClass(WC_THING);
984 if (conceptP) {
985 conceptP->_string2 += ' ';
986 conceptP->_string2 += word->getText();
987 } else {
988 status = processModifiers(8, word);
989 }
990 break;
991 }
992 case WC_ADVERB:
993 if (word->_id == 906) {
994 for (TTconcept *currP = _conceptP; currP; currP = currP->_nextP) {
995 if (_sentence->isFrameSlotClass(1, WC_ACTION) ||
996 _sentence->isFrameSlotClass(1, WC_THING))
997 currP->_field34 = 1;
998 }
999 } else {
1000 TTconcept *conceptP = _conceptP->findByWordClass(WC_ACTION);
1001
1002 if (conceptP) {
1003 conceptP->_string2 += ' ';
1004 conceptP->_string2 += word->getText();
1005 } else {
1006 tempFlag = true;
1007 }
1008 }
1009 break;
1010 default:
1011 addToConceptList(word);
1012 status = 0;
1013 break;
1014 }
1015
1016 if (tempFlag)
1017 status = _sentenceConcept->createConcept(1, 5, word);
1018
1019 modifierFlag = true;
1020 flag = true;
1021 }
1022 break;
1023
1024 case SEEK_NEW_FRAME:
1025 if (word->_wordClass == WC_ACTION && word->_id != 104 && word->_id != 107) {
1026 if (concept && (_sentenceConcept->_concept5P || _sentenceConcept->_concept2P)) {
1027 TTsentenceConcept *oldNode = _sentenceConcept;
1028 oldNode->_field1C = 2;
1029 _sentenceConcept = oldNode->addSibling();
1030 concept = nullptr;
1031
1032 _sentenceConcept->_concept1P = oldNode->_concept1P;
1033 _sentenceConcept->_concept5P = oldNode->_concept5P;
1034 _sentenceConcept->_concept2P = oldNode->_concept2P;
1035
1036 if (seekVal) {
1037 seekVal = 0;
1038
1039 _sentenceConcept->_field18 = oldNode->_field18;
1040 oldNode->_field18 = seekVal;
1041 }
1042 }
1043
1044 flag = true;
1045 }
1046 break;
1047
1048 case SEEK_STATE_OBJECT:
1049 if (!_sentenceConcept->_concept5P) {
1050 addToConceptList(word);
1051 } else if (_sentenceConcept->concept5WordId() == 113 ||
1052 _sentenceConcept->concept5WordId() == 112) {
1053 _sentenceConcept->createConcept(1, 2, word);
1054 } else {
1055 addToConceptList(word);
1056 }
1057
1058 flag = true;
1059 break;
1060
1061 case SET_ACTION:
1062 if (_sentence->fn4(1, 104, _sentenceConcept) ||
1063 _sentence->fn4(1, 107, _sentenceConcept)) {
1064 concept = _sentenceConcept->_concept1P;
1065 _sentenceConcept->_concept1P = nullptr;
1066 addNode(SEEK_NEW_FRAME);
1067 }
1068
1069 if (_sentence->checkCategory() && word->_id == 113)
1070 addNode(SEEK_ACTOR);
1071
1072 if (word->_wordClass == WC_ACTION)
1073 _sentenceConcept->createConcept(0, 1, word);
1074
1075 flag = true;
1076 break;
1077
1078 case ACTOR_IS_TO:
1079 _sentenceConcept->changeConcept(1, &_sentenceConcept->_concept0P, 3);
1080 flag = true;
1081 break;
1082
1083 case ACTOR_IS_FROM:
1084 _sentenceConcept->changeConcept(1, &_sentenceConcept->_concept0P, 4);
1085 break;
1086
1087 case ACTOR_IS_OBJECT:
1088 flag = resetConcept(&_sentenceConcept->_concept0P, 2);
1089 break;
1090
1091 case WORD_TYPE_IS_SENTENCE_TYPE:
1092 if (_sentence->_category == 1 || _sentence->_category == 10) {
1093 for (TTword *wordP = _currentWordP; wordP; wordP = wordP->_nextP) {
1094 if (wordP->_id == 906) {
1095 _sentence->_category = 12;
1096 flag = true;
1097 break;
1098 }
1099 }
1100
1101 TTpicture *newPictP;
1102 TTconcept *newConceptP;
1103 switch (word->_id) {
1104 case 108:
1105 _sentence->_category = 8;
1106 break;
1107 case 113:
1108 if (!_sentenceConcept->_concept3P)
1109 _sentence->_category = 22;
1110 break;
1111 case 306:
1112 _sentence->_category = 7;
1113 break;
1114 case 307:
1115 _sentence->_category = 24;
1116 break;
1117 case 308:
1118 _sentence->_category = 25;
1119 break;
1120 case 501:
1121 _sentence->_category = 9;
1122 break;
1123 case 900:
1124 _sentence->_category = 5;
1125 break;
1126 case 901:
1127 _sentence->_category = 4;
1128 break;
1129 case 904:
1130 _sentence->_category = 6;
1131 break;
1132 case 905:
1133 _sentence->_category = 11;
1134 break;
1135 case 906:
1136 _sentence->_category = 12;
1137 break;
1138 case 907:
1139 _sentence->_category = 13;
1140 break;
1141 case 908:
1142 _sentence->_category = 2;
1143 if (!_sentenceConcept->_concept0P) {
1144 newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0);
1145 newConceptP = new TTconcept(newPictP);
1146
1147 _sentenceConcept->_concept0P = newConceptP;
1148 delete newPictP;
1149 addNode(SEEK_ACTOR);
1150 }
1151 break;
1152 case 909:
1153 _sentence->_category = 3;
1154 newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0);
1155 newConceptP = new TTconcept(newPictP);
1156
1157 _sentenceConcept->_concept2P = newConceptP;
1158 delete newPictP;
1159 addNode(SEEK_ACTOR);
1160 break;
1161
1162 default:
1163 break;
1164 }
1165 }
1166
1167 flag = true;
1168 break;
1169
1170 case COMPLEX_VERB:
1171 if (word->_wordClass == WC_ACTION) {
1172 flag = true;
1173 } else if (!_sentenceConcept->_concept1P) {
1174 TTstring wordStr = word->getText();
1175 if (wordStr == "do" || wordStr == "doing" || wordStr == "does" || wordStr == "done") {
1176 TTaction *verbP = new TTaction(TTstring("do"), WC_ACTION, 112, 0,
1177 _sentenceConcept->get18());
1178 status = _sentenceConcept->createConcept(1, 1, verbP);
1179 delete verbP;
1180 flag = true;
1181 }
1182 }
1183 break;
1184
1185 case MKTAG('C', 'O', 'M', 'E'):
1186 addNode(SEEK_TO);
1187 addNode(SEEK_OBJECT);
1188 addNode(ACTOR_IS_OBJECT);
1189
1190 if (!_sentence->_category)
1191 _sentence->_category = 15;
1192 break;
1193
1194 case MKTAG('C', 'U', 'R', 'S'):
1195 case MKTAG('S', 'E', 'X', 'X'):
1196 if (_sentence->_field58 > 1)
1197 _sentence->_field58--;
1198 flag = true;
1199 break;
1200
1201 case MKTAG('E', 'X', 'I', 'T'):
1202 addNode(SEEK_FROM);
1203 addNode(SEEK_OBJECT);
1204 addNode(ACTOR_IS_OBJECT);
1205
1206 if (!_sentence->_category)
1207 _sentence->_category = 14;
1208 break;
1209
1210 case MKTAG('F', 'A', 'R', 'R'):
1211 if (_conceptP->findBy20(0))
1212 _conceptP->_field20 = 2;
1213 break;
1214
1215 case MKTAG('F', 'U', 'T', 'R'):
1216 _sentenceConcept->_field18 = 3;
1217 break;
1218
1219 case MKTAG('G', 'O', 'G', 'O'):
1220 addNode(SEEK_TO);
1221 addNode(SEEK_OBJECT);
1222 addNode(ACTOR_IS_OBJECT);
1223
1224 if (_sentence->_category == 1)
1225 _sentence->_category = 14;
1226
1227 flag = true;
1228 break;
1229
1230 case MKTAG('H', 'E', 'L', 'P'):
1231 if (_sentence->_category == 1)
1232 _sentence->_category = 18;
1233
1234 flag = true;
1235 break;
1236
1237 case MKTAG('L', 'O', 'C', 'F'):
1238 status = _sentenceConcept->createConcept(1, 5, word);
1239 if (!status)
1240 _sentenceConcept->_concept5P->_field20 = 2;
1241
1242 flag = true;
1243 break;
1244
1245 case MKTAG('L', 'O', 'C', 'N'):
1246 status = _sentenceConcept->createConcept(1, 5, word);
1247 if (!status)
1248 _sentenceConcept->_concept5P->_field20 = 1;
1249
1250 flag = true;
1251 break;
1252
1253 case MKTAG('N', 'E', 'A', 'R'):
1254 if (_conceptP->findBy20(0)) {
1255 _conceptP->_field20 = 1;
1256 } else {
1257 TTpicture *newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0);
1258 status = addToConceptList(newPictP);
1259 _conceptP->_field20 = 1;
1260 if (!status)
1261 delete newPictP;
1262 }
1263
1264 flag = true;
1265 break;
1266
1267 case MKTAG('P', 'A', 'S', 'T'):
1268 _sentenceConcept->_field18 = 1;
1269 flag = true;
1270 break;
1271
1272 case MKTAG('P', 'L', 'E', 'Z'):
1273 if (_sentence->_field58 < 10)
1274 _sentence->_field58++;
1275 break;
1276
1277 case MKTAG('P', 'R', 'E', 'Z'):
1278 _sentenceConcept->_field18 = 2;
1279 flag = true;
1280 break;
1281
1282 case MKTAG('S', 'A', 'A', 'O'):
1283 addNode(SEEK_OBJECT);
1284 addNode(SEEK_ACTOR);
1285 flag = true;
1286 break;
1287
1288 case MKTAG('S', 'S', 'T', 'A'):
1289 addNode(SEEK_STATE);
1290 addNode(SEEK_OBJECT);
1291 flag = true;
1292 break;
1293
1294 case MKTAG('T', 'E', 'A', 'C'):
1295 if (_sentence->_category == 1)
1296 _sentence->_category = 10;
1297
1298 flag = true;
1299 break;
1300
1301 case MKTAG('V', 'O', 'B', 'J'):
1302 status = _sentenceConcept->createConcept(1, 2, word);
1303 flag = true;
1304 break;
1305
1306 default:
1307 flag = true;
1308 break;
1309 }
1310
1311 TTparserNode *nextP = dynamic_cast<TTparserNode *>(nodeP->_nextP);
1312 if (flag) {
1313 removeNode(nodeP);
1314 flag = false;
1315 }
1316
1317 nodeP = nextP;
1318 }
1319
1320 delete concept;
1321 return status;
1322 }
1323
processRequests(TTword * word)1324 int TTparser::processRequests(TTword *word) {
1325 int status = loadRequests(word);
1326 switch (status) {
1327 case 0:
1328 status = considerRequests(word);
1329
1330 // Iterate through the words
1331 while (_currentWordP) {
1332 considerRequests(_currentWordP);
1333 TTword *nextP = _currentWordP->_nextP;
1334
1335 delete _currentWordP;
1336 _currentWordP = nextP;
1337 }
1338 break;
1339
1340 case 1: {
1341 TTword *newWord = new TTword(word);
1342 newWord->_nextP = nullptr;
1343
1344 // Add word to word chain
1345 if (_currentWordP) {
1346 // Add at end of existing chain
1347 for (word = _currentWordP; word->_nextP; word = word->_nextP)
1348 ;
1349 word->_nextP = newWord;
1350 } else {
1351 // First word, so set as head
1352 _currentWordP = newWord;
1353 }
1354 break;
1355 }
1356
1357 default:
1358 warning("unexpected return from consider requests");
1359 break;
1360 }
1361
1362 return status;
1363 }
1364
addToConceptList(TTword * word)1365 int TTparser::addToConceptList(TTword *word) {
1366 TTconcept *concept = new TTconcept(word, ST_UNKNOWN_SCRIPT);
1367 addConcept(concept);
1368 return 0;
1369 }
1370
addNode(uint tag)1371 void TTparser::addNode(uint tag) {
1372 TTparserNode *newNode = new TTparserNode(tag);
1373 if (_nodesP)
1374 _nodesP->addToHead(newNode);
1375 _nodesP = newNode;
1376 }
1377
addConcept(TTconcept * concept)1378 int TTparser::addConcept(TTconcept *concept) {
1379 if (!concept)
1380 return SS_5;
1381
1382 if (_conceptP)
1383 concept->_nextP = _conceptP;
1384 _conceptP = concept;
1385
1386 return SS_VALID;
1387 }
1388
removeConcept(TTconcept * concept)1389 void TTparser::removeConcept(TTconcept *concept) {
1390 // If no concept passed, exit immediately
1391 if (!concept)
1392 return;
1393
1394 if (_conceptP == concept) {
1395 // Concept specified is the ver ystart of the linked list, so reset head pointer
1396 _conceptP = _conceptP->_nextP;
1397 } else {
1398 // Scan through the linked list, looking for the specific concept
1399 for (TTconcept *currP = _conceptP; currP; currP = currP->_nextP) {
1400 if (currP->_nextP == concept) {
1401 // Found match, so unlink the next link from the chain
1402 currP->_nextP = currP->_nextP->_nextP;
1403 break;
1404 }
1405 }
1406 }
1407
1408 // FInally, delete the concept
1409 concept->_nextP = nullptr;
1410 delete concept;
1411 }
1412
removeNode(TTparserNode * node)1413 void TTparser::removeNode(TTparserNode *node) {
1414 if (!node->_priorP)
1415 // Node is the head of the chain, so reset parser's nodes pointer
1416 _nodesP = dynamic_cast<TTparserNode *>(node->_nextP);
1417
1418 delete node;
1419 }
1420
checkForAction()1421 int TTparser::checkForAction() {
1422 int status = SS_VALID;
1423 bool flag = false;
1424 bool actionFlag = false;
1425
1426 if (_conceptP && _currentWordP) {
1427 // Firstly we need to get the next word to process, and remove it from
1428 // the list pointed to by _currentWordP
1429 TTword *word = _currentWordP;
1430 if (word->_nextP) {
1431 // Chain of words, so we need to find the last word of the chain,
1432 // and set the last-but-one's _nextP to nullptr to detach the last one
1433 TTword *prior = nullptr;
1434 for (; word->_nextP; word = word->_nextP) {
1435 prior = word;
1436 }
1437
1438 if (prior)
1439 prior->_nextP = nullptr;
1440 } else {
1441 // No chain, so singular word can simply be removed
1442 _currentWordP = nullptr;
1443 if (word->_id == 906 && _sentence->_category == 1)
1444 _sentence->_category = 12;
1445 }
1446
1447 if (word->_text == "do" || word->_text == "doing" || word->_text == "does" ||
1448 word->_text == "done") {
1449 TTstring doStr("do");
1450 TTaction *action = new TTaction(doStr, WC_ACTION, 112, 0, _sentenceConcept->get18());
1451
1452 if (!action->isValid()) {
1453 status = SS_4;
1454 } else {
1455 // Have the new action replace the old word instance
1456 delete word;
1457 word = action;
1458 actionFlag = true;
1459 }
1460 }
1461
1462 addToConceptList(word);
1463 delete word;
1464 flag = true;
1465 }
1466
1467 // Handle any remaining words
1468 while (_currentWordP) {
1469 int result = considerRequests(_currentWordP);
1470 if (result > 1) {
1471 status = result;
1472 } else {
1473 // Delete the top of the word chain
1474 TTword *wordP = _currentWordP;
1475 _currentWordP = _currentWordP->_nextP;
1476 delete wordP;
1477 }
1478 }
1479
1480 if (flag && _conceptP) {
1481 if (actionFlag && (!_sentenceConcept->_concept1P || isWordId(_sentenceConcept->_concept1P, 113))) {
1482 _sentenceConcept->replaceConcept(0, 1, _conceptP);
1483 } else if (!_sentenceConcept->_concept5P) {
1484 _sentenceConcept->replaceConcept(1, 5, _conceptP);
1485 } else if (isWordId(_sentenceConcept->_concept5P, 904)) {
1486 _sentenceConcept->replaceConcept(0, 5, _conceptP);
1487 }
1488
1489 removeConcept(_conceptP);
1490 }
1491
1492 if (_sentence->fn2(3, TTstring("thePlayer"), _sentenceConcept) && !flag) {
1493 if (_sentenceConcept->concept1WordId() == 101) {
1494 _sentence->_category = 16;
1495 } else if (_sentence->_category != 18 && _sentenceConcept->concept1WordId() == 102) {
1496 if (_sentence->fn2(0, TTstring("targetNpc"), _sentenceConcept))
1497 _sentence->_category = 15;
1498 }
1499 }
1500
1501 if (_sentence->fn2(2, TTstring("thePlayer"), _sentenceConcept) &&
1502 _sentenceConcept->concept1WordId() == 101 && flag)
1503 _sentence->_category = 17;
1504
1505 if (!_sentenceConcept->_concept0P && !_sentenceConcept->_concept1P &&
1506 !_sentenceConcept->_concept2P && !_sentenceConcept->_concept5P && !flag) {
1507 if (_conceptP)
1508 filterConcepts(5, 2);
1509
1510 if (!_sentenceConcept->_concept2P && _sentence->_category == 1)
1511 _sentence->_category = 0;
1512 }
1513
1514 if (_sentence->_field58 < 5 && _sentence->_category == 1 && !flag)
1515 _sentence->_category = 19;
1516
1517 for (TTconceptNode *nodeP = &_sentence->_sentenceConcept; nodeP; nodeP = nodeP->_nextP) {
1518 if (nodeP->_field18 == 0 && nodeP->_concept1P) {
1519 nodeP->_field18 = _sentence->concept18(nodeP);
1520 } else if (nodeP->_field18 == 4 && !_sentenceConcept->_concept0P) {
1521 if (_sentenceConcept->_concept3P) {
1522 _sentenceConcept->_concept0P = _sentenceConcept->_concept3P;
1523 _sentenceConcept->_concept3P = nullptr;
1524 } else if (_sentenceConcept->_concept2P) {
1525 _sentenceConcept->_concept0P = _sentenceConcept->_concept2P;
1526 _sentenceConcept->_concept2P = nullptr;
1527 }
1528 }
1529 }
1530
1531 if (_sentence->_category == 1 && _sentenceConcept->_concept5P &&
1532 _sentenceConcept->_concept2P) {
1533 if (_sentence->fn4(1, 113, nullptr)) {
1534 if (_sentence->fn2(2, TTstring("targetNpc"), nullptr)) {
1535 _sentence->_category = 20;
1536 } else if (_sentence->fn2(2, TTstring("thePlayer"), nullptr)) {
1537 _sentence->_category = 21;
1538 } else {
1539 _sentence->_category = 22;
1540 }
1541 }
1542 } else if (!_sentenceConcept->_concept0P && !_sentenceConcept->_concept1P &&
1543 !_sentenceConcept->_concept2P && !_sentenceConcept->_concept5P) {
1544 if (_conceptP)
1545 filterConcepts(5, 2);
1546
1547 if (!_sentenceConcept->_concept2P && _sentence->_category == 1)
1548 _sentence->_category = 0;
1549 }
1550
1551 return status;
1552 }
1553
fn2(TTword * word)1554 int TTparser::fn2(TTword *word) {
1555 switch (word->_id) {
1556 case 600:
1557 addNode(SEEK_STATE);
1558 return 0;
1559
1560 case 601:
1561 addNode(SEEK_OWNERSHIP);
1562 return 1;
1563
1564 case 602:
1565 case 607:
1566 return checkReferent(dynamic_cast<TTpronoun *>(word));
1567
1568 case 608:
1569 return 1;
1570
1571 default:
1572 return 0;
1573 }
1574 }
1575
checkReferent(TTpronoun * pronoun)1576 int TTparser::checkReferent(TTpronoun *pronoun) {
1577 TTconcept *concept;
1578
1579 switch (pronoun->getVal()) {
1580 case 0:
1581 return 0;
1582
1583 case 1:
1584 concept = new TTconcept(_owner->_script, ST_ROOM_SCRIPT);
1585 break;
1586
1587 case 2:
1588 concept = new TTconcept(_sentence->_npcScript, ST_NPC_SCRIPT);
1589 break;
1590
1591 default:
1592 concept = new TTconcept(pronoun, (ScriptType)pronoun->getVal());
1593 break;
1594 }
1595
1596 addConcept(concept);
1597 return 0;
1598 }
1599
conceptChanged(TTconcept * newConcept,TTconcept * oldConcept)1600 void TTparser::conceptChanged(TTconcept *newConcept, TTconcept *oldConcept) {
1601 if (!oldConcept && newConcept != _currentConceptP)
1602 _currentConceptP = nullptr;
1603 else if (oldConcept && oldConcept == _currentConceptP)
1604 _currentConceptP = newConcept;
1605 }
1606
checkConcept2(TTconcept * concept,int conceptMode)1607 bool TTparser::checkConcept2(TTconcept *concept, int conceptMode) {
1608 switch (conceptMode) {
1609 case 3:
1610 return concept->checkWordId2();
1611
1612 case 5:
1613 return concept->checkWordClass();
1614
1615 case 8:
1616 return concept->checkWordId1();
1617
1618 case 9:
1619 if (concept->checkWordId3())
1620 return true;
1621
1622 if (_sentenceConcept->_concept2P) {
1623 if (!_sentenceConcept->_concept2P->checkWordId2() || !concept->checkWordId2()) {
1624 return _sentenceConcept->_concept2P->checkWordClass() &&
1625 concept->checkWordClass();
1626 }
1627 }
1628 break;
1629
1630 default:
1631 break;
1632 }
1633
1634 return false;
1635 }
1636
filterConcepts(int conceptMode,int conceptIndex)1637 int TTparser::filterConcepts(int conceptMode, int conceptIndex) {
1638 int result = 0;
1639
1640 for (TTconcept *nextP, *currP = _conceptP; currP && !result; currP = nextP) {
1641 nextP = currP->_nextP;
1642
1643 if (checkConcept2(currP, conceptMode)) {
1644 TTconcept **ptrPP = _sentenceConcept->setConcept(conceptIndex, currP);
1645 TTconcept *newConcept = new TTconcept(*currP);
1646 *ptrPP = newConcept;
1647
1648 if (newConcept->isValid()) {
1649 removeConcept(currP);
1650 (*ptrPP)->_nextP = nullptr;
1651 result = 1;
1652 } else {
1653 result = -2;
1654 }
1655 }
1656 }
1657
1658 return result;
1659 }
1660
resetConcept(TTconcept ** conceptPP,int conceptIndex)1661 bool TTparser::resetConcept(TTconcept **conceptPP, int conceptIndex) {
1662 TTconcept **ptrPP = _sentenceConcept->setConcept(conceptIndex, nullptr);
1663
1664 if (!*ptrPP)
1665 return 0;
1666
1667 int result = _sentenceConcept->changeConcept(1, conceptPP, conceptIndex);
1668 if (*conceptPP)
1669 _sentenceConcept->setConcept(conceptIndex, *conceptPP);
1670
1671 return !result;
1672 }
1673
processModifiers(int modifier,TTword * word)1674 int TTparser::processModifiers(int modifier, TTword *word) {
1675 TTconcept *newConcept = new TTconcept(word, ST_UNKNOWN_SCRIPT);
1676
1677 // Cycles through each word
1678 for (TTword *currP = _currentWordP; currP && currP != word; currP = _currentWordP) {
1679 if ((modifier == 2 && currP->_wordClass == WC_ADJECTIVE) ||
1680 (modifier == 1 && currP->_wordClass == WC_ADVERB)) {
1681 newConcept->_string2 += ' ';
1682 newConcept->_string2 += _currentWordP->getText();
1683 } else if (word->_id == 113 && currP->_wordClass == WC_ADJECTIVE) {
1684 addToConceptList(currP);
1685 addNode(SEEK_STATE);
1686 }
1687
1688 if (modifier == 2 || modifier == 3) {
1689 switch (_currentWordP->_id) {
1690 case 94:
1691 _currentConceptP->setOwner(newConcept);
1692 if (_currentWordP) {
1693 _currentWordP->deleteSiblings();
1694 delete _currentWordP;
1695 _currentWordP = nullptr;
1696 }
1697
1698 delete newConcept;
1699 newConcept = nullptr;
1700 break;
1701
1702 case 204:
1703 newConcept->_field34 = 1;
1704 if (_sentence->_category == 1)
1705 _sentence->_category = 12;
1706 newConcept->_field14 = 1;
1707 break;
1708
1709 case 300:
1710 newConcept->set1C(atoi(_currentWordP->_text.c_str()));
1711 break;
1712
1713 case 400:
1714 newConcept->_field14 = 2;
1715 break;
1716
1717 case 401:
1718 newConcept->_field14 = 1;
1719 break;
1720
1721 case 601:
1722 newConcept->setOwner(_currentWordP, false);
1723 break;
1724
1725 case 608:
1726 if (_currentWordP->comparePronounTo(10)) {
1727 newConcept->_field20 = 1;
1728 } else if (_currentWordP->comparePronounTo(11)) {
1729 newConcept->_field20 = 2;
1730 }
1731
1732 default:
1733 break;
1734 }
1735 }
1736
1737 if (_currentWordP) {
1738 // Detaches word and deletes it
1739 TTword *wordP = _currentWordP;
1740 _currentWordP = wordP->_nextP;
1741
1742 wordP->_nextP = nullptr;
1743 delete wordP;
1744 }
1745 }
1746
1747 if (newConcept) {
1748 newConcept->setFlag(true);
1749 _currentConceptP = newConcept;
1750 addConcept(newConcept);
1751 }
1752
1753 return 0;
1754 }
1755
preprocessGerman(TTstring & line)1756 void TTparser::preprocessGerman(TTstring &line) {
1757 static const char *const SUFFIXES[12] = {
1758 " ", "est ", "em ", "en ", "er ", "es ",
1759 "et ", "st ", "s ", "e ", "n ", "t "
1760 };
1761
1762 for (uint idx = 0; idx < _replacements4.size(); ++idx) {
1763 if (!line.hasSuffix(_replacements4[idx]))
1764 continue;
1765
1766 const char *lineP = line.c_str();
1767 const char *p = strstr(lineP, _replacements4[idx].c_str());
1768 if (!p || p == lineP || *(p - 1) != ' ')
1769 continue;
1770
1771 const char *wordEndP = p + _replacements4[idx].size();
1772
1773 for (int sIdx = 0; sIdx < 12; ++sIdx) {
1774 const char *suffixP = SUFFIXES[sIdx];
1775 if (!strncmp(wordEndP, suffixP, strlen(suffixP))) {
1776 // Form a new line with the replacement word
1777 const char *nextWordP = wordEndP + strlen(suffixP);
1778 line = Common::String::format("%s %s %s",
1779 Common::String(lineP, p).c_str(),
1780 _replacements4[idx + 1].c_str(),
1781 nextWordP
1782 );
1783 return;
1784 }
1785 }
1786 }
1787 }
1788
1789 } // End of namespace Titanic
1790