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 "sherlock/talk.h"
24 #include "sherlock/sherlock.h"
25 #include "sherlock/screen.h"
26 #include "sherlock/scalpel/scalpel.h"
27 #include "sherlock/scalpel/scalpel_people.h"
28 #include "sherlock/scalpel/scalpel_talk.h"
29 #include "sherlock/scalpel/scalpel_user_interface.h"
30 #include "sherlock/tattoo/tattoo.h"
31 #include "sherlock/tattoo/tattoo_fixed_text.h"
32 #include "sherlock/tattoo/tattoo_people.h"
33 #include "sherlock/tattoo/tattoo_scene.h"
34 #include "sherlock/tattoo/tattoo_talk.h"
35 #include "sherlock/tattoo/tattoo_user_interface.h"
36 
37 namespace Sherlock {
38 
SequenceEntry()39 SequenceEntry::SequenceEntry() {
40 	_objNum = 0;
41 	_obj = nullptr;
42 	_seqStack = 0;
43 	_seqTo = 0;
44 	_sequenceNumber = _frameNumber = 0;
45 	_seqCounter = _seqCounter2 = 0;
46 }
47 
48 /*----------------------------------------------------------------*/
49 
load(Common::SeekableReadStream & s,bool isRoseTattoo)50 void Statement::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
51 	int length;
52 
53 	length = s.readUint16LE();
54 	for (int idx = 0; idx < length - 1; ++idx)
55 		_statement += (char)s.readByte();
56 	s.readByte();	// Null ending
57 
58 	length = s.readUint16LE();
59 	for (int idx = 0; idx < length - 1; ++idx)
60 		_reply += (char)s.readByte();
61 	s.readByte();	// Null ending
62 
63 	length = s.readUint16LE();
64 	for (int idx = 0; idx < length - 1; ++idx)
65 		_linkFile += (char)s.readByte();
66 	s.readByte();	// Null ending
67 
68 	length = s.readUint16LE();
69 	for (int idx = 0; idx < length - 1; ++idx)
70 		_voiceFile += (char)s.readByte();
71 	s.readByte();	// Null ending
72 
73 	_required.resize(s.readByte());
74 	_modified.resize(s.readByte());
75 
76 	// Read in flag required/modified data
77 	for (uint idx = 0; idx < _required.size(); ++idx)
78 		_required[idx] = s.readSint16LE();
79 	for (uint idx = 0; idx < _modified.size(); ++idx)
80 		_modified[idx] = s.readSint16LE();
81 
82 	_portraitSide = s.readByte();
83 	_quotient = s.readUint16LE();
84 	_journal = isRoseTattoo ? s.readByte() : 0;
85 }
86 
87 /*----------------------------------------------------------------*/
88 
TalkHistoryEntry()89 TalkHistoryEntry::TalkHistoryEntry() {
90 	Common::fill(&_data[0], &_data[16], false);
91 }
92 
93 /*----------------------------------------------------------------*/
94 
init(SherlockEngine * vm)95 Talk *Talk::init(SherlockEngine *vm) {
96 	if (vm->getGameID() == GType_SerratedScalpel)
97 		return new Scalpel::ScalpelTalk(vm);
98 	else
99 		return new Tattoo::TattooTalk(vm);
100 }
101 
Talk(SherlockEngine * vm)102 Talk::Talk(SherlockEngine *vm) : _vm(vm) {
103 	_talkCounter = 0;
104 	_talkToAbort = false;
105 	_openTalkWindow = false;
106 	_speaker = 0;
107 	_talkIndex = 0;
108 	_talkTo = 0;
109 	_scriptSelect = 0;
110 	_converseNum = -1;
111 	_talkStealth = 0;
112 	_talkToFlag = -1;
113 	_moreTalkDown = _moreTalkUp = false;
114 	_scriptMoreFlag = 0;
115 	_scriptSaveIndex = -1;
116 	_opcodes = nullptr;
117 	_opcodeTable = nullptr;
118 	_3doSpeechIndex = -1;
119 
120 	_charCount = 0;
121 	_line = 0;
122 	_yp = 0;
123 	_wait = 0;
124 	_pauseFlag = false;
125 	_seqCount = 0;
126 	_scriptStart = _scriptEnd = nullptr;
127 	_endStr = _noTextYet = false;
128 
129 	_talkHistory.resize(IS_ROSE_TATTOO ? 1500 : 500);
130 }
131 
talkTo(const Common::String filename)132 void Talk::talkTo(const Common::String filename) {
133 	Events &events = *_vm->_events;
134 	Inventory &inv = *_vm->_inventory;
135 	Journal &journal = *_vm->_journal;
136 	People &people = *_vm->_people;
137 	Scene &scene = *_vm->_scene;
138 	Screen &screen = *_vm->_screen;
139 	Sound &sound = *_vm->_sound;
140 	UserInterface &ui = *_vm->_ui;
141 	Common::Rect savedBounds = screen.getDisplayBounds();
142 	bool abortFlag = false;
143 
144 	if (filename.empty())
145 		// No filename passed, so exit
146 		return;
147 
148 	// If there any canimations currently running, or a portrait is being cleared,
149 	// save the filename for later executing when the canimation is done
150 	bool ongoingAnim = scene._canimShapes.size() > 0;
151 	if (IS_ROSE_TATTOO) {
152 		ongoingAnim = static_cast<Tattoo::TattooScene *>(_vm->_scene)->_activeCAnim.active();
153 	}
154 	if (ongoingAnim || people._clearingThePortrait) {
155 		// Make sure we're not in the middle of a script
156 		if (!_scriptMoreFlag) {
157 			_scriptName = filename;
158 			_scriptSaveIndex = 0;
159 
160 			// Flag the selection, since we don't yet know which statement yet
161 			_scriptSelect = 100;
162 			_scriptMoreFlag = 3;
163 		}
164 
165 		return;
166 	}
167 
168 	// Save the ui mode temporarily and switch to talk mode
169 	MenuMode savedMode = ui._menuMode;
170 	ui._menuMode = TALK_MODE;
171 
172 	// Turn on the Exit option
173 	ui._endKeyActive = true;
174 
175 	if (people[HOLMES]._walkCount || (!people[HOLMES]._walkTo.empty() &&
176 			(IS_SERRATED_SCALPEL || people._allowWalkAbort))) {
177 		// Only interrupt if trying to do an action, and not just if player is walking around the scene
178 		if (people._allowWalkAbort)
179 			abortFlag = true;
180 
181 		people[HOLMES].gotoStand();
182 	}
183 
184 	if (_talkToAbort)
185 		return;
186 
187 	freeTalkVars();
188 
189 	// If any sequences have changed in the prior talk file, restore them
190 	if (_savedSequences.size() > 0) {
191 		for (uint idx = 0; idx < _savedSequences.size(); ++idx) {
192 			SequenceEntry &ss = _savedSequences[idx];
193 			for (uint idx2 = 0; idx2 < ss._sequences.size(); ++idx2)
194 				scene._bgShapes[ss._objNum]._sequences[idx2] = ss._sequences[idx2];
195 
196 			// Reset the object's frame to the beginning of the sequence
197 			scene._bgShapes[ss._objNum]._frameNumber = 0;
198 		}
199 	}
200 
201 	if (IS_ROSE_TATTOO) {
202 		pullSequence();
203 	} else {
204 		while (!isSequencesEmpty())
205 			pullSequence();
206 	}
207 
208 	if (IS_SERRATED_SCALPEL) {
209 		// Restore any pressed button
210 		if (!ui._windowOpen && savedMode != STD_MODE)
211 			static_cast<Scalpel::ScalpelUserInterface *>(_vm->_ui)->restoreButton((int)(savedMode - 1));
212 	} else {
213 		static_cast<Tattoo::TattooPeople *>(_vm->_people)->pullNPCPaths();
214 	}
215 
216 	// Clear the ui counter so that anything displayed on the info line
217 	// before the window was opened isn't cleared
218 	ui._menuCounter = 0;
219 
220 	// Close any previous window before starting the talk
221 	if (ui._windowOpen) {
222 		switch (savedMode) {
223 		case LOOK_MODE:
224 			events.setCursor(ARROW);
225 
226 			if (ui._invLookFlag) {
227 				screen.resetDisplayBounds();
228 				ui.drawInterface(2);
229 			}
230 
231 			ui.banishWindow();
232 			ui._windowBounds.top = CONTROLS_Y1;
233 			ui._temp = ui._oldTemp = ui._lookHelp = 0;
234 			ui._menuMode = STD_MODE;
235 			events._pressed = events._released = events._oldButtons = 0;
236 			ui._invLookFlag = false;
237 			break;
238 
239 		case TALK_MODE:
240 			if (_speaker < SPEAKER_REMOVE)
241 				people.clearTalking();
242 			if (_talkCounter)
243 				return;
244 
245 			// If we were in inventory mode looking at an object, restore the
246 			// back buffers before closing the window, so we get the ui restored
247 			// rather than the inventory again
248 			if (ui._invLookFlag) {
249 				screen.resetDisplayBounds();
250 				ui.drawInterface(2);
251 				ui._invLookFlag = ui._lookScriptFlag = false;
252 			}
253 
254 			ui.banishWindow();
255 			ui._windowBounds.top = CONTROLS_Y1;
256 			abortFlag = true;
257 			break;
258 
259 		case INV_MODE:
260 		case USE_MODE:
261 		case GIVE_MODE:
262 			inv.freeInv();
263 			if (ui._invLookFlag) {
264 				screen.resetDisplayBounds();
265 				ui.drawInterface(2);
266 				ui._invLookFlag = ui._lookScriptFlag = false;
267 			}
268 
269 			ui._infoFlag = true;
270 			ui.clearInfo();
271 			ui.banishWindow(false);
272 			ui._key = -1;
273 			break;
274 
275 		case FILES_MODE:
276 			ui.banishWindow(true);
277 			ui._windowBounds.top = CONTROLS_Y1;
278 			abortFlag = true;
279 			break;
280 
281 		case SETUP_MODE:
282 			ui.banishWindow(true);
283 			ui._windowBounds.top = CONTROLS_Y1;
284 			ui._temp = ui._oldTemp = ui._lookHelp = ui._invLookFlag = false;
285 			ui._menuMode = STD_MODE;
286 			events._pressed = events._released = events._oldButtons = 0;
287 			abortFlag = true;
288 			break;
289 
290 		default:
291 			break;
292 		}
293 	}
294 
295 	screen.resetDisplayBounds();
296 	events._pressed = events._released = false;
297 	loadTalkFile(filename);
298 	ui._selector = ui._oldSelector = ui._key = ui._oldKey = -1;
299 
300 	// Find the first statement that has the correct flags
301 	int select = -1;
302 	for (uint idx = 0; idx < _statements.size() && select == -1; ++idx) {
303 		if (_statements[idx]._talkMap == 0)
304 			select = _talkIndex = idx;
305 	}
306 
307 	// If there's a pending automatic selection to be made, then use it
308 	if (_scriptMoreFlag && _scriptSelect != 100)
309 		select = _scriptSelect;
310 
311 	if (select == -1) {
312 		if (IS_ROSE_TATTOO) {
313 			static_cast<Tattoo::TattooUserInterface *>(&ui)->putMessage(
314 				"%s", _vm->_fixedText->getText(Tattoo::kFixedText_NoEffect));
315 			return;
316 		}
317 		error("Couldn't find statement to display");
318 	}
319 
320 	// Add the statement into the journal and talk history
321 	if (_talkTo != -1 && !_talkHistory[_converseNum][select])
322 		journal.record(_converseNum, select, true);
323 	_talkHistory[_converseNum][select] = true;
324 
325 	// Check if the talk file is meant to be a non-seen comment
326 	if (filename.size() < 8 || filename[7] != '*') {
327 		// Should we start in stealth mode?
328 		if (_statements[select]._statement.hasPrefix("^")) {
329 			_talkStealth = 2;
330 		} else {
331 			// Not in stealth mode, so bring up the ui window
332 			_talkStealth = 0;
333 			++_talkToFlag;
334 			events.setCursor(WAIT);
335 
336 			ui._windowBounds.top = CONTROLS_Y;
337 			ui._infoFlag = true;
338 			ui.clearInfo();
339 		}
340 
341 		// Handle replies until there's no further linked file,
342 		// or the link file isn't a reply first cnversation
343 		while (!_vm->shouldQuit()) {
344 			clearSequences();
345 			_scriptSelect = select;
346 			_speaker = _talkTo;
347 
348 			// Set up the talk file extension
349 			if (IS_ROSE_TATTOO && sound._speechOn && _scriptMoreFlag != 1)
350 				sound._talkSoundFile += Common::String::format("%02dB", select + 1);
351 
352 			// Make a copy of the statement (in case the script frees the statement list), and then execute it
353 			Statement statement = _statements[select];
354 			doScript(_statements[select]._reply);
355 
356 			if (IS_ROSE_TATTOO) {
357 				for (int idx = 0; idx < MAX_CHARACTERS; ++idx)
358 					people[idx]._misc = 0;
359 			}
360 
361 			if (_talkToAbort)
362 				return;
363 
364 			if (!_talkStealth)
365 				ui.clearWindow();
366 
367 			if (statement._modified.size() > 0) {
368 				for (uint idx = 0; idx < statement._modified.size(); ++idx)
369 					_vm->setFlags(statement._modified[idx]);
370 
371 				setTalkMap();
372 			}
373 
374 			// Check for a linked file
375 			if (!statement._linkFile.empty() && !_scriptMoreFlag) {
376 				Common::String linkFilename = statement._linkFile;
377 				freeTalkVars();
378 				loadTalkFile(linkFilename);
379 
380 				// Scan for the first valid statement in the newly loaded file
381 				select = -1;
382 				for (uint idx = 0; idx < _statements.size(); ++idx) {
383 					if (_statements[idx]._talkMap == 0) {
384 						select = idx;
385 						break;
386 					}
387 				}
388 
389 				if (_talkToFlag == 1)
390 					pullSequence();
391 
392 				// Set the stealth mode for the new talk file
393 				Statement &newStatement = _statements[select];
394 				_talkStealth = newStatement._statement.hasPrefix("^") ? 2 : 0;
395 
396 				// If the new conversion is a reply first, then we don't need
397 				// to display any choices, since the reply needs to be shown
398 				if (!newStatement._statement.hasPrefix("*") && !newStatement._statement.hasPrefix("^")) {
399 					_talkIndex = select;
400 					showTalk();
401 
402 					// Break out of loop now that we're waiting for player input
403 					events.setCursor(ARROW);
404 					break;
405 				} else {
406 					// Add the statement into the journal and talk history
407 					if (_talkTo != -1 && !_talkHistory[_converseNum][select])
408 						journal.record(_converseNum, select, true);
409 					_talkHistory[_converseNum][select] = true;
410 				}
411 
412 				ui._key = ui._oldKey = 'T'; // FIXME: I'm not sure what to do here, I need ScalpelUI->_hotkeyTalk
413 				ui._temp = ui._oldTemp = 0;
414 				ui._menuMode = TALK_MODE;
415 				_talkToFlag = 2;
416 			} else {
417 				freeTalkVars();
418 
419 				if (IS_SERRATED_SCALPEL) {
420 					if (!ui._lookScriptFlag) {
421 						ui.drawInterface(2);
422 						ui._menuMode = STD_MODE;
423 						ui._windowBounds.top = CONTROLS_Y1;
424 					}
425 				} else {
426 					ui._menuMode = static_cast<Tattoo::TattooScene *>(_vm->_scene)->_labTableScene ? LAB_MODE : STD_MODE;
427 				}
428 
429 				ui.banishWindow();
430 				break;
431 			}
432 		}
433 	}
434 
435 	_talkStealth = 0;
436 	events._pressed = events._released = events._oldButtons = 0;
437 	events.clearKeyboard();
438 
439 	if (savedBounds.bottom == SHERLOCK_SCREEN_HEIGHT)
440 		screen.resetDisplayBounds();
441 	else
442 		screen.setDisplayBounds(savedBounds);
443 
444 	_talkToAbort = abortFlag;
445 
446 	// If a script was added to the script stack, restore state so that the
447 	// previous script can continue
448 	popStack();
449 
450 	events.setCursor(ARROW);
451 }
452 
initTalk(int objNum)453 void Talk::initTalk(int objNum) {
454 	Events &events = *_vm->_events;
455 	People &people = *_vm->_people;
456 	Scene &scene = *_vm->_scene;
457 	UserInterface &ui = *_vm->_ui;
458 
459 	ui._windowBounds.top = CONTROLS_Y;
460 	ui._infoFlag = true;
461 	_speaker = SPEAKER_REMOVE;
462 
463 	Common::String talkFilename = (objNum >= 1000) ? people[objNum - 1000]._npcName : scene._bgShapes[objNum]._name;
464 	loadTalkFile(talkFilename);
465 
466 	// Find the first statement with the correct flags
467 	int select = -1;
468 	for (uint idx = 0; idx < _statements.size(); ++idx) {
469 		if (_statements[idx]._talkMap == 0) {
470 			select = idx;
471 			break;
472 		}
473 	}
474 
475 	if (select == -1) {
476 		freeTalkVars();
477 		if (!scumm_strnicmp(talkFilename.c_str(), "PATH", 4))
478 			error("No entries found to execute in path file");
479 
480 		nothingToSay();
481 		return;
482 	}
483 
484 	// See if the statement is a stealth mode reply
485 	Statement &statement = _statements[select];
486 	if (statement._statement.hasPrefix("^")) {
487 		clearSequences();
488 
489 		// Start talk in stealth mode
490 		_talkStealth = 2;
491 
492 		talkTo(talkFilename);
493 	} else if (statement._statement.hasPrefix("*")) {
494 		// Character being spoken to will speak first
495 		if (objNum > 1000) {
496 			(*static_cast<Tattoo::TattooPeople *>(_vm->_people))[objNum - 1000].walkHolmesToNPC();
497 		} else {
498 			Object &obj = scene._bgShapes[objNum];
499 			clearSequences();
500 			pushSequence(_talkTo);
501 			people.setListenSequence(_talkTo, 129);
502 
503 			events.setCursor(WAIT);
504 			if (obj._lookPosition.y != 0)
505 				// Need to walk to character first
506 				people[HOLMES].walkToCoords(obj._lookPosition, obj._lookPosition._facing);
507 			events.setCursor(ARROW);
508 		}
509 
510 		if (!_talkToAbort)
511 			talkTo(talkFilename);
512 	} else {
513 		// Holmes will be speaking first
514 		_talkToFlag = false;
515 
516 		if (objNum > 1000) {
517 			(*static_cast<Tattoo::TattooPeople *>(_vm->_people))[objNum - 1000].walkHolmesToNPC();
518 		} else {
519 			Object &obj = scene._bgShapes[objNum];
520 			clearSequences();
521 			pushSequence(_talkTo);
522 			people.setListenSequence(_talkTo, 129);
523 
524 			events.setCursor(WAIT);
525 			if (obj._lookPosition.y != 0)
526 				// Walk over to person to talk to
527 				people[HOLMES].walkToCoords(obj._lookPosition, obj._lookPosition._facing);
528 			events.setCursor(ARROW);
529 		}
530 
531 		if (!_talkToAbort) {
532 			// See if walking over triggered a conversation
533 			if (_talkToFlag) {
534 				if (_talkToFlag == 1) {
535 					events.setCursor(ARROW);
536 					// _sequenceStack._count = 1;
537 					pullSequence();
538 				}
539 			} else {
540 				_talkIndex = select;
541 				showTalk();
542 
543 				// Break out of loop now that we're waiting for player input
544 				events.setCursor(ARROW);
545 			}
546 
547 			_talkToFlag = -1;
548 		}
549 	}
550 }
551 
freeTalkVars()552 void Talk::freeTalkVars() {
553 	_statements.clear();
554 }
555 
loadTalkFile(const Common::String & filename)556 void Talk::loadTalkFile(const Common::String &filename) {
557 	People &people = *_vm->_people;
558 	Resources &res = *_vm->_res;
559 	Sound &sound = *_vm->_sound;
560 
561 	// Save a copy of the talk filename
562 	_scriptName = filename;
563 
564 	// Check for an existing person being talked to
565 	_talkTo = -1;
566 	for (int idx = 0; idx < (int)people._characters.size(); ++idx) {
567 		if (!scumm_strnicmp(filename.c_str(), people._characters[idx]._portrait, 4)) {
568 			_talkTo = idx;
569 			break;
570 		}
571 	}
572 
573 	const char *chP = strchr(filename.c_str(), '.');
574 	Common::String talkFile = chP ? Common::String(filename.c_str(), chP) + ".tlk" :
575 		Common::String(filename.c_str(), filename.c_str() + 7) + ".tlk";
576 
577 	// Create the base of the sound filename used for talking in Rose Tattoo
578 	if (IS_ROSE_TATTOO && _scriptMoreFlag != 1)
579 		sound._talkSoundFile = Common::String(filename.c_str(), filename.c_str() + 7) + ".";
580 
581 	// Open the talk file for reading
582 	Common::SeekableReadStream *talkStream = res.load(talkFile);
583 	_converseNum = res.resourceIndex();
584 	talkStream->skip(2);	// Skip talk file version num
585 
586 	_statements.clear();
587 	_statements.resize(talkStream->readByte());
588 	for (uint idx = 0; idx < _statements.size(); ++idx)
589 		_statements[idx].load(*talkStream, IS_ROSE_TATTOO);
590 
591 	delete talkStream;
592 
593 	if (!sound._voices)
594 		stripVoiceCommands();
595 	setTalkMap();
596 }
597 
stripVoiceCommands()598 void Talk::stripVoiceCommands() {
599 	for (uint sIdx = 0; sIdx < _statements.size(); ++sIdx) {
600 		Statement &statement = _statements[sIdx];
601 
602 		// Scan for an sound effect byte, which indicates to play a sound
603 		for (uint idx = 0; idx < statement._reply.size(); ++idx) {
604 			if (statement._reply[idx] == (char)_opcodes[OP_SFX_COMMAND]) {
605 				// Replace instruction character with a space, and delete the
606 				// rest of the name following it
607 				statement._reply = Common::String(statement._reply.c_str(),
608 					statement._reply.c_str() + idx) + " " +
609 					Common::String(statement._reply.c_str() + idx + 9);
610 			}
611 		}
612 
613 		// Ensure the last character of the reply is not a space from the prior
614 		// conversion loop, to avoid any issues with the space ever causing a page
615 		// wrap, and ending up displaying another empty page
616 		while (statement._reply.lastChar() == ' ')
617 			statement._reply.deleteLastChar();
618 	}
619 }
620 
setTalkMap()621 void Talk::setTalkMap() {
622 	int statementNum = 0;
623 
624 	for (uint sIdx = 0; sIdx < _statements.size(); ++sIdx) {
625 		Statement &statement = _statements[sIdx];
626 
627 		// Set up talk map entry for the statement
628 		bool valid = true;
629 		for (uint idx = 0; idx < statement._required.size(); ++idx) {
630 			if (!_vm->readFlags(statement._required[idx]))
631 				valid = false;
632 		}
633 
634 		statement._talkMap = valid ? statementNum++ : -1;
635 	}
636 }
637 
pushSequence(int speaker)638 void Talk::pushSequence(int speaker) {
639 	People &people = *_vm->_people;
640 	Scene &scene = *_vm->_scene;
641 
642 	// Only proceed if a speaker is specified
643 	if (speaker != -1) {
644 		int objNum = people.findSpeaker(speaker);
645 		if (objNum != -1)
646 			pushSequenceEntry(&scene._bgShapes[objNum]);
647 	}
648 }
649 
doScript(const Common::String & script)650 void Talk::doScript(const Common::String &script) {
651 	People &people = *_vm->_people;
652 	Scene &scene = *_vm->_scene;
653 	Screen &screen = *_vm->_screen;
654 	UserInterface &ui = *_vm->_ui;
655 
656 	_savedSequences.clear();
657 
658 	_scriptStart = (const byte *)script.c_str();
659 	_scriptEnd = _scriptStart + script.size();
660 	const byte *str = _scriptStart;
661 	_charCount = 0;
662 	_line = 0;
663 	_wait = 0;
664 	_pauseFlag = false;
665 	_seqCount = 0;
666 	_noTextYet = true;
667 	_endStr = false;
668 	_openTalkWindow = false;
669 
670 	if (IS_SERRATED_SCALPEL)
671 		_yp = CONTROLS_Y + 12;
672 	else
673 		_yp = (_talkTo == -1) ? 5 : screen.fontHeight() + 11;
674 
675 	if (IS_ROSE_TATTOO) {
676 		for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
677 			Tattoo::TattooPerson &p = (*(Tattoo::TattooPeople *)_vm->_people)[idx];
678 			p._savedNpcSequence = p._sequenceNumber;
679 			p._savedNpcFrame = p._frameNumber;
680 			p._resetNPCPath = true;
681 		}
682 	}
683 
684 	if (_scriptMoreFlag) {
685 		_scriptMoreFlag = 0;
686 		str = _scriptStart + _scriptSaveIndex;
687 	}
688 
689 	// Check if the script begins with a Stealh Mode Active command
690 	if (str[0] == _opcodes[OP_STEALTH_MODE_ACTIVE] || _talkStealth) {
691 		_talkStealth = 2;
692 		_speaker |= SPEAKER_REMOVE;
693 	} else {
694 		if (IS_SERRATED_SCALPEL)
695 			pushSequence(_speaker);
696 		if (IS_SERRATED_SCALPEL || ui._windowOpen)
697 			ui.clearWindow();
698 
699 		// Need to switch speakers?
700 		if (str[0] == _opcodes[OP_SWITCH_SPEAKER]) {
701 			_speaker = str[1] - 1;
702 
703 			if (IS_SERRATED_SCALPEL) {
704 				str += 2;
705 				pullSequence();
706 				pushSequence(_speaker);
707 			} else {
708 				str += 3;
709 			}
710 
711 			people.setTalkSequence(_speaker);
712 		} else {
713 			people.setTalkSequence(_speaker);
714 		}
715 
716 		if (IS_SERRATED_SCALPEL) {
717 			// Assign portrait location?
718 			if (str[0] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION]) {
719 				switch (str[1] & 15) {
720 				case 1:
721 					people._portraitSide = 20;
722 					break;
723 				case 2:
724 					people._portraitSide = 220;
725 					break;
726 				case 3:
727 					people._portraitSide = 120;
728 					break;
729 				default:
730 					break;
731 
732 				}
733 
734 				if (str[1] > 15)
735 					people._speakerFlip = true;
736 				str += 2;
737 			}
738 
739 			if (IS_SERRATED_SCALPEL) {
740 				// Remove portrait?
741 				if ( str[0] == _opcodes[OP_REMOVE_PORTRAIT]) {
742 					_speaker = -1;
743 				} else {
744 					// Nope, so set the first speaker
745 					((Scalpel::ScalpelPeople *)_vm->_people)->setTalking(_speaker);
746 				}
747 			}
748 		}
749 	}
750 
751 	do {
752 		Common::String tempString;
753 		_wait = 0;
754 
755 		byte c = str[0];
756 		if (!c) {
757 			_endStr = true;
758 		} else if (c == '{') {
759 			// Start of comment, so skip over it
760 			while (*str++ != '}')
761 				;
762 		} else if (isOpcode(c)) {
763 			// the original interpreter checked for c being >= 0x80
764 			// and if that is the case, it tried to process it as opcode, BUT ALSO ALWAYS skipped over it
765 			// This was done inside the Spanish + German interpreters of Serrated Scalpel, not the original
766 			// English interpreter (reverse engineered from the binaries).
767 			//
768 			// This resulted in special characters not getting shown in case they occurred at the start
769 			// of sentences like for example the inverted exclamation mark and the inverted question mark.
770 			// For further study see fonts.cpp
771 			//
772 			// We create an inverted exclamation mark for the Spanish version and we show it.
773 			//
774 			// Us not skipping over those characters may result in an assert() happening inside fonts.cpp
775 			// in case more invalid characters exist.
776 			// More information see bug #6931
777 			//
778 
779 			// Handle control code
780 			switch ((this->*_opcodeTable[c - _opcodes[0]])(str)) {
781 			case RET_EXIT:
782 				return;
783 			case RET_CONTINUE:
784 				continue;
785 			default:
786 				break;
787 			}
788 
789 			++str;
790 		} else {
791 			// Handle drawing the talk interface with the text
792 			talkInterface(str);
793 		}
794 
795 		// Open window if it wasn't already open, and text has already been printed
796 		if ((_openTalkWindow && _wait) || (_openTalkWindow && str[0] >= _opcodes[0] && str[0] != _opcodes[OP_END_TEXT_WINDOW])) {
797 			if (!ui._slideWindows) {
798 				screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
799 			} else {
800 				ui.summonWindow();
801 			}
802 
803 			ui._windowOpen = true;
804 			_openTalkWindow = false;
805 		}
806 
807 		if (_wait)
808 			// Handling pausing
809 			talkWait(str);
810 	} while (!_vm->shouldQuit() && !_endStr);
811 
812 	if (_wait != -1) {
813 		for (int ssIndex = 0; ssIndex < (int)_savedSequences.size(); ++ssIndex) {
814 			SequenceEntry &seq = _savedSequences[ssIndex];
815 			Object &object = scene._bgShapes[seq._objNum];
816 
817 			for (uint idx = 0; idx < seq._sequences.size(); ++idx)
818 				object._sequences[idx] = seq._sequences[idx];
819 			object._frameNumber = seq._frameNumber;
820 			object._seqTo = seq._seqTo;
821 		}
822 
823 		pullSequence();
824 
825 		if (IS_SERRATED_SCALPEL) {
826 			if (_speaker >= 0 && _speaker < SPEAKER_REMOVE)
827 				people.clearTalking();
828 		} else {
829 			static_cast<Tattoo::TattooPeople *>(_vm->_people)->pullNPCPaths();
830 		}
831 	}
832 }
833 
waitForMore(int delay)834 int Talk::waitForMore(int delay) {
835 	Events &events = *_vm->_events;
836 	People &people = *_vm->_people;
837 	Scene &scene = *_vm->_scene;
838 	Sound &sound = *_vm->_sound;
839 	UserInterface &ui = *_vm->_ui;
840 	CursorId oldCursor = events.getCursor();
841 	int key2 = 254;
842 	bool playingSpeech = false;
843 
844 	// Unless we're in stealth mode, show the appropriate cursor
845 	if (!_talkStealth) {
846 		events.setCursor(ui._lookScriptFlag ? MAGNIFY : ARROW);
847 	}
848 
849 	// Handle playing any speech associated with the text being displayed
850 	switchSpeaker();
851 	if (sound._speechOn && IS_ROSE_TATTOO) {
852 		sound.playSpeech(sound._talkSoundFile);
853 		sound._talkSoundFile.setChar(sound._talkSoundFile.lastChar() + 1, sound._talkSoundFile.size() - 1);
854 	}
855 	playingSpeech = sound.isSpeechPlaying();
856 
857 	do {
858 		if (IS_SERRATED_SCALPEL && playingSpeech && !sound.isSpeechPlaying())
859 			people._portrait._frameNumber = -1;
860 
861 		scene.doBgAnim();
862 
863 		// If talkTo call was done via doBgAnim, abort out of talk quietly
864 		if (_talkToAbort) {
865 			key2 = -1;
866 			events._released = true;
867 		} else {
868 			// See if there's been a button press
869 			events.pollEventsAndWait();
870 			events.setButtonState();
871 
872 			if (events.kbHit()) {
873 				Common::KeyState keyState = events.getKey();
874 				if (keyState.keycode == Common::KEYCODE_ESCAPE) {
875 					if (IS_ROSE_TATTOO && static_cast<Tattoo::TattooEngine *>(_vm)->_runningProlog) {
876 						// Skip out of the introduction
877 						_vm->setFlags(-76);
878 						_vm->setFlags(396);
879 						scene._goToScene = 1;
880 					}
881 					break;
882 
883 				} else if (Common::isPrint(keyState.ascii))
884 					key2 = keyState.keycode;
885 			}
886 
887 			if (_talkStealth) {
888 				key2 = 254;
889 				events._released = false;
890 			}
891 		}
892 
893 		// Count down the delay
894 		if ((delay > 0 && !ui._invLookFlag && !ui._lookScriptFlag) || _talkStealth)
895 			--delay;
896 
897 		if (playingSpeech && !sound.isSpeechPlaying())
898 			delay = 0;
899 	} while (!_vm->shouldQuit() && key2 == 254 && (delay || (playingSpeech && sound.isSpeechPlaying()))
900 		&& !events._released && !events._rightReleased);
901 
902 	// If voices was set 2 to indicate a Scalpel voice file was playing, then reset it back to 1
903 	if (sound._voices == 2)
904 		sound._voices = 1;
905 
906 	if (delay > 0 && sound.isSpeechPlaying())
907 		sound.stopSpeech();
908 
909 	// Adjust _talkStealth mode:
910 	// mode 1 - It was by a pause without stealth being on before the pause, so reset back to 0
911 	// mode 3 - It was set by a pause with stealth being on before the pause, to set it to active
912 	// mode 0/2 (Inactive/active) No change
913 	switch (_talkStealth) {
914 	case 1:
915 		_talkStealth = 0;
916 		break;
917 	case 2:
918 		_talkStealth = 2;
919 		break;
920 	default:
921 		break;
922 	}
923 
924 
925 	sound.stopSpeech();
926 	events.setCursor(_talkToAbort ? ARROW : oldCursor);
927 	events._pressed = events._released = false;
928 
929 	return key2;
930 }
931 
isOpcode(byte checkCharacter)932 bool Talk::isOpcode(byte checkCharacter) {
933 	if ((checkCharacter < _opcodes[0]) || (checkCharacter >= (_opcodes[0] + 99)))
934 		return false; // outside of range
935 	if (_opcodeTable[checkCharacter - _opcodes[0]])
936 		return true;
937 	return false;
938 }
939 
popStack()940 void Talk::popStack() {
941 	if (!_scriptStack.empty()) {
942 		ScriptStackEntry scriptEntry = _scriptStack.pop();
943 		_scriptName = scriptEntry._name;
944 		_scriptSaveIndex = scriptEntry._currentIndex;
945 		_scriptSelect = scriptEntry._select;
946 		_scriptMoreFlag = 1;
947 	}
948 }
949 
synchronize(Serializer & s)950 void Talk::synchronize(Serializer &s) {
951 	for (uint idx = 0; idx < _talkHistory.size(); ++idx) {
952 		TalkHistoryEntry &he = _talkHistory[idx];
953 
954 		for (int flag = 0; flag < 16; ++flag)
955 			s.syncAsByte(he._data[flag]);
956 	}
957 }
958 
cmdAddItemToInventory(const byte * & str)959 OpcodeReturn Talk::cmdAddItemToInventory(const byte *&str) {
960 	Inventory &inv = *_vm->_inventory;
961 	Common::String tempString;
962 
963 	++str;
964 	for (int idx = 0; idx < str[0]; ++idx)
965 		tempString += str[idx + 1];
966 	str += str[0];
967 
968 	inv.putNameInInventory(tempString);
969 	return RET_SUCCESS;
970 }
971 
cmdAdjustObjectSequence(const byte * & str)972 OpcodeReturn Talk::cmdAdjustObjectSequence(const byte *&str) {
973 	Scene &scene = *_vm->_scene;
974 	Common::String tempString;
975 
976 	// Get the name of the object to adjust
977 	++str;
978 	for (int idx = 0; idx < (str[0] & 127); ++idx)
979 		tempString += str[idx + 2];
980 
981 	// Scan for object
982 	int objId = -1;
983 	for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
984 		if (tempString.equalsIgnoreCase(scene._bgShapes[idx]._name))
985 			objId = idx;
986 	}
987 	if (objId == -1)
988 		error("Could not find object %s to change", tempString.c_str());
989 
990 	// Should the script be overwritten?
991 	if (str[0] > 0x80) {
992 		// Save the current sequence
993 		_savedSequences.push(SequenceEntry());
994 		SequenceEntry &seqEntry = _savedSequences.top();
995 		seqEntry._objNum = objId;
996 		seqEntry._seqTo = scene._bgShapes[objId]._seqTo;
997 		for (uint idx = 0; idx < scene._bgShapes[objId]._seqSize; ++idx)
998 			seqEntry._sequences.push_back(scene._bgShapes[objId]._sequences[idx]);
999 	}
1000 
1001 	// Get number of bytes to change
1002 	_seqCount = str[1];
1003 	str += (str[0] & 127) + 2;
1004 
1005 	// WORKAROUND: Original German Scalpel crash when moving box at Tobacconists
1006 	if (_vm->getLanguage() == Common::DE_DEU && _scriptName == "Alfr30Z")
1007 		_seqCount = 16;
1008 
1009 	// Copy in the new sequence
1010 	for (int idx = 0; idx < _seqCount; ++idx, ++str)
1011 		scene._bgShapes[objId]._sequences[idx] = str[0] - 1;
1012 
1013 	// Reset object back to beginning of new sequence
1014 	scene._bgShapes[objId]._frameNumber = 0;
1015 
1016 	return RET_CONTINUE;
1017 }
1018 
cmdBanishWindow(const byte * & str)1019 OpcodeReturn Talk::cmdBanishWindow(const byte *&str) {
1020 	People &people = *_vm->_people;
1021 	UserInterface &ui = *_vm->_ui;
1022 
1023 	if (!(_speaker & SPEAKER_REMOVE))
1024 		people.clearTalking();
1025 	pullSequence();
1026 
1027 	if (_talkToAbort)
1028 		return RET_EXIT;
1029 
1030 	_speaker |= SPEAKER_REMOVE;
1031 	ui.banishWindow();
1032 	ui._menuMode = TALK_MODE;
1033 	_noTextYet = true;
1034 
1035 	return RET_SUCCESS;
1036 }
1037 
cmdDisableEndKey(const byte * & str)1038 OpcodeReturn Talk::cmdDisableEndKey(const byte *&str) {
1039 	_vm->_ui->_endKeyActive = false;
1040 	return RET_SUCCESS;
1041 }
1042 
cmdEnableEndKey(const byte * & str)1043 OpcodeReturn Talk::cmdEnableEndKey(const byte *&str) {
1044 	_vm->_ui->_endKeyActive = true;
1045 	return RET_SUCCESS;
1046 }
1047 
cmdEndTextWindow(const byte * & str)1048 OpcodeReturn Talk::cmdEndTextWindow(const byte *&str) {
1049 	return RET_SUCCESS;
1050 }
1051 
cmdHolmesOff(const byte * & str)1052 OpcodeReturn Talk::cmdHolmesOff(const byte *&str) {
1053 	People &people = *_vm->_people;
1054 	people[HOLMES]._type = REMOVE;
1055 	people._holmesOn = false;
1056 
1057 	return RET_SUCCESS;
1058 }
1059 
cmdHolmesOn(const byte * & str)1060 OpcodeReturn Talk::cmdHolmesOn(const byte *&str) {
1061 	People &people = *_vm->_people;
1062 	people[HOLMES]._type = CHARACTER;
1063 	people._holmesOn = true;
1064 
1065 	return RET_SUCCESS;
1066 }
1067 
cmdPause(const byte * & str)1068 OpcodeReturn Talk::cmdPause(const byte *&str) {
1069 	_charCount = *++str;
1070 	_wait = _pauseFlag = true;
1071 
1072 	return RET_SUCCESS;
1073 }
1074 
cmdPauseWithoutControl(const byte * & str)1075 OpcodeReturn Talk::cmdPauseWithoutControl(const byte *&str) {
1076 	Events &events = *_vm->_events;
1077 	Scene &scene = *_vm->_scene;
1078 	++str;
1079 
1080 	events.incWaitCounter();
1081 
1082 	for (int idx = 0; idx < (str[0] - 1); ++idx) {
1083 		scene.doBgAnim();
1084 		if (_talkToAbort)
1085 			return RET_EXIT;
1086 
1087 		// Check for button press
1088 		events.pollEvents();
1089 		events.setButtonState();
1090 	}
1091 
1092 	events.decWaitCounter();
1093 
1094 	_endStr = false;
1095 	return RET_SUCCESS;
1096 }
1097 
cmdRemoveItemFromInventory(const byte * & str)1098 OpcodeReturn Talk::cmdRemoveItemFromInventory(const byte *&str) {
1099 	Inventory &inv = *_vm->_inventory;
1100 	Common::String tempString;
1101 
1102 	++str;
1103 	for (int idx = 0; idx < str[0]; ++idx)
1104 		tempString += str[idx + 1];
1105 	str += str[0];
1106 
1107 	inv.deleteItemFromInventory(tempString);
1108 
1109 	return RET_SUCCESS;
1110 }
1111 
cmdRunCAnimation(const byte * & str)1112 OpcodeReturn Talk::cmdRunCAnimation(const byte *&str) {
1113 	Scene &scene = *_vm->_scene;
1114 
1115 	++str;
1116 	scene.startCAnim((str[0] - 1) & 127, (str[0] & 0x80) ? -1 : 1);
1117 	if (_talkToAbort)
1118 		return RET_EXIT;
1119 
1120 	// Check if next character is changing side or changing portrait
1121 	_wait = 0;
1122 	if (_charCount && (str[1] == _opcodes[OP_SWITCH_SPEAKER] ||
1123 			(IS_SERRATED_SCALPEL && str[1] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION])))
1124 		_wait = 1;
1125 
1126 	return RET_SUCCESS;
1127 }
1128 
cmdSetFlag(const byte * & str)1129 OpcodeReturn Talk::cmdSetFlag(const byte *&str) {
1130 	++str;
1131 	int flag1 = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
1132 	int flag = (flag1 & 0x3fff) * (flag1 >= 0x4000 ? -1 : 1);
1133 	_vm->setFlags(flag);
1134 	++str;
1135 
1136 	return RET_SUCCESS;
1137 }
1138 
cmdSetObject(const byte * & str)1139 OpcodeReturn Talk::cmdSetObject(const byte *&str) {
1140 	Scene &scene = *_vm->_scene;
1141 	Common::String tempString;
1142 
1143 	++str;
1144 	for (int idx = 0; idx < (str[0] & 127); ++idx)
1145 		tempString += str[idx + 1];
1146 
1147 	// Set comparison state according to if we want to hide or unhide
1148 	bool state = (str[0] >= SPEAKER_REMOVE);
1149 	str += str[0] & 127;
1150 
1151 	for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
1152 		Object &object = scene._bgShapes[idx];
1153 		if (tempString.equalsIgnoreCase(object._name)) {
1154 			// Only toggle the object if it's not in the desired state already
1155 			if ((object._type == HIDDEN && state) || (object._type != HIDDEN && !state))
1156 				object.toggleHidden();
1157 		}
1158 	}
1159 
1160 	return RET_SUCCESS;
1161 }
1162 
cmdStealthModeActivate(const byte * & str)1163 OpcodeReturn Talk::cmdStealthModeActivate(const byte *&str) {
1164 	_talkStealth = 2;
1165 	return RET_SUCCESS;
1166 }
1167 
cmdStealthModeDeactivate(const byte * & str)1168 OpcodeReturn Talk::cmdStealthModeDeactivate(const byte *&str) {
1169 	Events &events = *_vm->_events;
1170 
1171 	_talkStealth = 0;
1172 	events.clearKeyboard();
1173 
1174 	return RET_SUCCESS;
1175 }
1176 
cmdToggleObject(const byte * & str)1177 OpcodeReturn Talk::cmdToggleObject(const byte *&str) {
1178 	Scene &scene = *_vm->_scene;
1179 	Common::String tempString;
1180 
1181 	++str;
1182 	for (int idx = 0; idx < str[0]; ++idx)
1183 		tempString += str[idx + 1];
1184 
1185 	scene.toggleObject(tempString);
1186 	str += str[0];
1187 
1188 	return RET_SUCCESS;
1189 }
1190 
cmdWalkToCAnimation(const byte * & str)1191 OpcodeReturn Talk::cmdWalkToCAnimation(const byte *&str) {
1192 	People &people = *_vm->_people;
1193 	Scene &scene = *_vm->_scene;
1194 
1195 	++str;
1196 	CAnim &animation = scene._cAnim[str[0] - 1];
1197 	people[HOLMES].walkToCoords(animation._goto[0], animation._goto[0]._facing);
1198 
1199 	return _talkToAbort ? RET_EXIT : RET_SUCCESS;
1200 }
1201 
talkWait(const byte * & str)1202 void Talk::talkWait(const byte *&str) {
1203 	if (!_pauseFlag && _charCount < 160)
1204 		_charCount = 160;
1205 
1206 	_wait = waitForMore(_charCount);
1207 	if (_wait == -1)
1208 		_endStr = true;
1209 
1210 	// If a key was pressed to finish the window, see if further voice files should be skipped
1211 	if (IS_SERRATED_SCALPEL && _wait >= 0 && _wait < 254) {
1212 		if (str[0] == _opcodes[OP_SFX_COMMAND])
1213 			str += 9;
1214 	}
1215 
1216 	_pauseFlag = false;
1217 }
1218 
1219 } // End of namespace Sherlock
1220