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 = &ne;
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