scummvm/engines/sherlock/talk.cpp

2081 lines
58 KiB
C++
Raw Normal View History

2015-03-15 16:52:55 -04:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
2015-03-15 16:52:55 -04:00
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
2015-03-15 16:52:55 -04:00
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "sherlock/talk.h"
2015-03-21 20:25:15 -04:00
#include "sherlock/sherlock.h"
2015-03-31 08:28:51 -04:00
#include "sherlock/screen.h"
2015-03-15 16:52:55 -04:00
namespace Sherlock {
#define SPEAKER_REMOVE 0x80
const byte SCALPEL_OPCODES[] = {
128, // OP_SWITCH_SPEAKER
129, // OP_RUN_CANIMATION
130, // OP_ASSIGN_PORTRAIT_LOCATION
131, // OP_PAUSE
132, // OP_REMOVE_PORTRAIT
133, // OP_CLEAR_WINDOW
134, // OP_ADJUST_OBJ_SEQUENCE
135, // OP_WALK_TO_COORDS
136, // OP_PAUSE_WITHOUT_CONTROL
137, // OP_BANISH_WINDOW
138, // OP_SUMMON_WINDOW
139, // OP_SET_FLAG
140, // OP_SFX_COMMAND
141, // OP_TOGGLE_OBJECT
142, // OP_STEALTH_MODE_ACTIVE
143, // OP_IF_STATEMENT
144, // OP_ELSE_STATEMENT
145, // OP_END_IF_STATEMENT
146, // OP_STEALTH_MODE_DEACTIVATE
147, // OP_TURN_HOLMES_OFF
148, // OP_TURN_HOLMES_ON
149, // OP_GOTO_SCENE
150, // OP_PLAY_PROLOGUE
151, // OP_ADD_ITEM_TO_INVENTORY
152, // OP_SET_OBJECT
153, // OP_CALL_TALK_FILE
143, // OP_MOVE_MOUSE
155, // OP_DISPLAY_INFO_LINE
156, // OP_CLEAR_INFO_LINE
157, // OP_WALK_TO_CANIMATION
158, // OP_REMOVE_ITEM_FROM_INVENTORY
159, // OP_ENABLE_END_KEY
160, // OP_DISABLE_END_KEY
161, // OP_CARRIAGE_RETURN
0, // OP_MOUSE_ON_OFF
0, // OP_SET_WALK_CONTROL
0, // OP_SET_TALK_SEQUENCE
0, // OP_PLAY_SONG
0, // OP_WALK_HOLMES_AND_NPC_TO_CANIM
0, // OP_SET_NPC_PATH_DEST
0, // OP_NEXT_SONG
0, // OP_SET_NPC_PATH_PAUSE
0, // OP_PASSWORD
0, // OP_SET_SCENE_ENTRY_FLAG
0, // OP_WALK_NPC_TO_CANIM
0, // OP_WALK_HOLMES_AND_NPC_TO_COORDS
0, // OP_WALK_HOLMES_AND_NPC_TO_COORDS
0, // OP_SET_NPC_TALK_FILE
0, // OP_TURN_NPC_OFF
0, // OP_TURN_NPC_ON
0, // OP_NPC_DESC_ON_OFF
0, // OP_NPC_PATH_PAUSE_TAKING_NOTES
0, // OP_NPC_PATH_PAUSE_LOOKING_HOLMES
0, // OP_ENABLE_TALK_INTERRUPTS
0, // OP_DISABLE_TALK_INTERRUPTS
0, // OP_SET_NPC_INFO_LINE
0, // OP_SET_NPC_POSITION
0, // OP_NPC_PATH_LABEL
0, // OP_PATH_GOTO_LABEL
0, // OP_PATH_IF_FLAG_GOTO_LABEL
0, // OP_NPC_WALK_GRAPHICS
0, // OP_NPC_VERB
0, // OP_NPC_VERB_CANIM
0, // OP_NPC_VERB_SCRIPT
0, // OP_RESTORE_PEOPLE_SEQUENCE
0, // OP_NPC_VERB_TARGET
0 // OP_TURN_SOUNDS_OFF
};
const byte TATTOO_OPCODES[] = {
170, // OP_SWITCH_SPEAKER
171, // OP_RUN_CANIMATION
0, // OP_ASSIGN_PORTRAIT_LOCATION
173, // OP_PAUSE
0, // OP_REMOVE_PORTRAIT
0, // OP_CLEAR_WINDOW
176, // OP_ADJUST_OBJ_SEQUENCE
177, // OP_WALK_TO_COORDS
178, // OP_PAUSE_WITHOUT_CONTROL
179, // OP_BANISH_WINDOW
0, // OP_SUMMON_WINDOW
181, // OP_SET_FLAG
0, // OP_SFX_COMMAND
183, // OP_TOGGLE_OBJECT
184, // OP_STEALTH_MODE_ACTIVE
0, // OP_IF_STATEMENT
0, // OP_ELSE_STATEMENT
0, // OP_END_IF_STATEMENT
188, // OP_STEALTH_MODE_DEACTIVATE
189, // OP_TURN_HOLMES_OFF
190, // OP_TURN_HOLMES_ON
191, // OP_GOTO_SCENE
0, // OP_PLAY_PROLOGUE
193, // OP_ADD_ITEM_TO_INVENTORY
194, // OP_SET_OBJECT
172, // OP_CALL_TALK_FILE
0, // OP_MOVE_MOUSE
0, // OP_DISPLAY_INFO_LINE
0, // OP_CLEAR_INFO_LINE
199, // OP_WALK_TO_CANIMATION
200, // OP_REMOVE_ITEM_FROM_INVENTORY
201, // OP_ENABLE_END_KEY
202, // OP_DISABLE_END_KEY
0, // OP_CARRIAGE_RETURN
174, // OP_MOUSE_ON_OFF
175, // OP_SET_WALK_CONTROL
180, // OP_SET_TALK_SEQUENCE
182, // OP_PLAY_SONG
187, // OP_WALK_HOLMES_AND_NPC_TO_CANIM
192, // OP_SET_NPC_PATH_DEST
195, // OP_NEXT_SONG
196, // OP_SET_NPC_PATH_PAUSE
197, // OP_PASSWORD
198, // OP_SET_SCENE_ENTRY_FLAG
185, // OP_WALK_NPC_TO_CANIM
204, // OP_WALK_HOLMES_AND_NPC_TO_COORDS
205, // OP_SET_NPC_TALK_FILE
206, // OP_TURN_NPC_OFF
207, // OP_TURN_NPC_ON
208, // OP_NPC_DESC_ON_OFF
209, // OP_NPC_PATH_PAUSE_TAKING_NOTES
210, // OP_NPC_PATH_PAUSE_LOOKING_HOLMES
211, // OP_ENABLE_TALK_INTERRUPTS
212, // OP_DISABLE_TALK_INTERRUPTS
213, // OP_SET_NPC_INFO_LINE
214, // OP_SET_NPC_POSITION
215, // OP_NPC_PATH_LABEL
216, // OP_PATH_GOTO_LABEL
217, // OP_PATH_IF_FLAG_GOTO_LABEL
218, // OP_NPC_WALK_GRAPHICS
220, // OP_NPC_VERB
221, // OP_NPC_VERB_CANIM
222, // OP_NPC_VERB_SCRIPT
224, // OP_RESTORE_PEOPLE_SEQUENCE
226, // OP_NPC_VERB_TARGET
227 // OP_TURN_SOUNDS_OFF
};
/*----------------------------------------------------------------*/
2015-05-03 18:17:50 -10:00
SequenceEntry::SequenceEntry() {
_objNum = 0;
_frameNumber = 0;
_seqTo = 0;
}
/*----------------------------------------------------------------*/
2015-03-31 07:55:54 -04:00
void Statement::synchronize(Common::SeekableReadStream &s) {
int length;
length = s.readUint16LE();
for (int idx = 0; idx < length - 1; ++idx)
_statement += (char)s.readByte();
s.readByte(); // Null ending
length = s.readUint16LE();
for (int idx = 0; idx < length - 1; ++idx)
_reply += (char)s.readByte();
s.readByte(); // Null ending
length = s.readUint16LE();
for (int idx = 0; idx < length - 1; ++idx)
_linkFile += (char)s.readByte();
s.readByte(); // Null ending
length = s.readUint16LE();
for (int idx = 0; idx < length - 1; ++idx)
_voiceFile += (char)s.readByte();
s.readByte(); // Null ending
_required.resize(s.readByte());
_modified.resize(s.readByte());
// Read in flag required/modified data
for (uint idx = 0; idx < _required.size(); ++idx)
_required[idx] = s.readSint16LE();
for (uint idx = 0; idx < _modified.size(); ++idx)
_modified[idx] = s.readSint16LE();
_portraitSide = s.readByte();
_quotient = s.readUint16LE();
}
/*----------------------------------------------------------------*/
2015-03-31 07:55:54 -04:00
TalkHistoryEntry::TalkHistoryEntry() {
Common::fill(&_data[0], &_data[16], false);
}
/*----------------------------------------------------------------*/
TalkSequences::TalkSequences(const byte *data) {
Common::copy(data, data + MAX_TALK_SEQUENCES, _data);
}
void TalkSequences::clear() {
2015-05-07 19:33:44 +02:00
Common::fill(&_data[0], &_data[MAX_TALK_SEQUENCES], 0);
}
/*----------------------------------------------------------------*/
Talk *Talk::init(SherlockEngine *vm) {
if (vm->getGameID() == GType_SerratedScalpel)
return new ScalpelTalk(vm);
else
return new TattooTalk(vm);
}
2015-05-18 18:12:58 -04:00
Talk::Talk(SherlockEngine *vm) : _vm(vm) {
2015-03-21 20:25:15 -04:00
_talkCounter = 0;
_talkToAbort = false;
_speaker = 0;
_talkIndex = 0;
_talkTo = 0;
2015-03-31 07:55:54 -04:00
_scriptSelect = 0;
_converseNum = -1;
_talkStealth = 0;
_talkToFlag = -1;
2015-03-31 08:28:51 -04:00
_moreTalkDown = _moreTalkUp = false;
_scriptMoreFlag = 0;
_scriptSaveIndex = -1;
_opcodes = IS_SERRATED_SCALPEL ? SCALPEL_OPCODES : TATTOO_OPCODES;
_charCount = 0;
_line = 0;
_yp = 0;
_wait = 0;
_pauseFlag = false;
_seqCount = 0;
_scriptStart = _scriptEnd = nullptr;
2015-03-21 20:25:15 -04:00
}
void Talk::talkTo(const Common::String &filename) {
Events &events = *_vm->_events;
Inventory &inv = *_vm->_inventory;
2015-03-31 07:55:54 -04:00
Journal &journal = *_vm->_journal;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Screen &screen = *_vm->_screen;
UserInterface &ui = *_vm->_ui;
Common::Rect savedBounds = screen.getDisplayBounds();
2015-03-31 07:55:54 -04:00
bool abortFlag = false;
if (filename.empty())
// No filename passed, so exit
return;
// If there any canimations currently running, or a portrait is being cleared,
// save the filename for later executing when the canimation is done
if (scene._canimShapes.size() > 0 || people._clearingThePortrait) {
// Make sure we're not in the middle of a script
if (!_scriptMoreFlag) {
_scriptName = filename;
_scriptSaveIndex = 0;
// Flag the selection, since we don't yet know which statement yet
_scriptSelect = 100;
_scriptMoreFlag = 3;
}
return;
}
// Save the ui mode temporarily and switch to talk mode
MenuMode savedMode = ui._menuMode;
ui._menuMode = TALK_MODE;
// Turn on the Exit option
ui._endKeyActive = true;
if (people[AL]._walkCount || people._walkTo.size() > 0) {
// Only interrupt if an action if trying to do an action, and not just
// if the player is walking around the scene
if (people._allowWalkAbort)
2015-03-31 07:55:54 -04:00
abortFlag = true;
people.gotoStand(people._player);
}
if (_talkToAbort)
return;
freeTalkVars();
// If any sequences have changed in the prior talk file, restore them
if (_savedSequences.size() > 0) {
for (uint idx = 0; idx < _savedSequences.size(); ++idx) {
SequenceEntry &ss = _savedSequences[idx];
for (uint idx2 = 0; idx2 < ss._sequences.size(); ++idx2)
scene._bgShapes[ss._objNum]._sequences[idx2] = ss._sequences[idx2];
2015-05-07 19:33:44 +02:00
// Reset the object's frame to the beginning of the sequence
scene._bgShapes[ss._objNum]._frameNumber = 0;
}
}
while (!_sequenceStack.empty())
pullSequence();
if (IS_SERRATED_SCALPEL) {
// Restore any pressed button
if (!ui._windowOpen && savedMode != STD_MODE)
2015-05-22 22:31:21 -04:00
((ScalpelUserInterface *)_vm->_ui)->restoreButton((int)(savedMode - 1));
}
// Clear the ui counter so that anything displayed on the info line
// before the window was opened isn't cleared
ui._menuCounter = 0;
// Close any previous window before starting the talk
if (ui._windowOpen) {
switch (savedMode) {
case LOOK_MODE:
events.setCursor(ARROW);
if (ui._invLookFlag) {
screen.resetDisplayBounds();
ui.drawInterface(2);
}
ui.banishWindow();
ui._windowBounds.top = CONTROLS_Y1;
ui._temp = ui._oldTemp = ui._lookHelp = 0;
ui._menuMode = STD_MODE;
events._pressed = events._released = events._oldButtons = 0;
ui._invLookFlag = false;
break;
case TALK_MODE:
if (_speaker < SPEAKER_REMOVE)
people.clearTalking();
if (_talkCounter)
return;
// If we were in inventory mode looking at an object, restore the
// back buffers before closing the window, so we get the ui restored
// rather than the inventory again
if (ui._invLookFlag) {
screen.resetDisplayBounds();
ui.drawInterface(2);
ui._invLookFlag = ui._lookScriptFlag = false;
}
ui.banishWindow();
ui._windowBounds.top = CONTROLS_Y1;
2015-03-31 07:55:54 -04:00
abortFlag = true;
break;
case INV_MODE:
case USE_MODE:
case GIVE_MODE:
inv.freeInv();
if (ui._invLookFlag) {
screen.resetDisplayBounds();
ui.drawInterface(2);
ui._invLookFlag = ui._lookScriptFlag = false;
}
ui._infoFlag = true;
ui.clearInfo();
ui.banishWindow(false);
ui._key = -1;
break;
case FILES_MODE:
ui.banishWindow(true);
ui._windowBounds.top = CONTROLS_Y1;
2015-03-31 07:55:54 -04:00
abortFlag = true;
break;
case SETUP_MODE:
ui.banishWindow(true);
ui._windowBounds.top = CONTROLS_Y1;
ui._temp = ui._oldTemp = ui._lookHelp = ui._invLookFlag = false;
ui._menuMode = STD_MODE;
events._pressed = events._released = events._oldButtons = 0;
2015-03-31 07:55:54 -04:00
abortFlag = true;
break;
default:
break;
}
}
screen.resetDisplayBounds();
events._pressed = events._released = false;
loadTalkFile(filename);
ui._selector = ui._oldSelector = ui._key = ui._oldKey = -1;
// Find the first statement that has the correct flags
int select = -1;
for (uint idx = 0; idx < _statements.size() && select == -1; ++idx) {
2015-03-31 07:55:54 -04:00
if (_statements[idx]._talkMap == 0)
select = _talkIndex = idx;
}
// If there's a pending automatic selection to be made, then use it
if (_scriptMoreFlag && _scriptSelect != 100)
2015-03-31 07:55:54 -04:00
select = _scriptSelect;
if (select == -1)
error("Couldn't find statement to display");
// Add the statement into the journal and talk history
if (_talkTo != -1 && !_talkHistory[_converseNum][select])
journal.record(_converseNum, select, true);
2015-03-31 07:55:54 -04:00
_talkHistory[_converseNum][select] = true;
// Check if the talk file is meant to be a non-seen comment
if (filename.size() < 8 || filename[7] != '*') {
2015-03-31 07:55:54 -04:00
// Should we start in stealth mode?
if (_statements[select]._statement.hasPrefix("^")) {
_talkStealth = 2;
} else {
// Not in stealth mode, so bring up the ui window
_talkStealth = 0;
++_talkToFlag;
events.setCursor(WAIT);
ui._windowBounds.top = CONTROLS_Y;
ui._infoFlag = true;
ui.clearInfo();
}
2015-05-07 19:33:44 +02:00
// Handle replies until there's no further linked file,
2015-03-31 07:55:54 -04:00
// or the link file isn't a reply first cnversation
while (!_vm->shouldQuit()) {
clearSequences();
2015-03-31 07:55:54 -04:00
_scriptSelect = select;
_speaker = _talkTo;
Statement &statement = _statements[select];
doScript(_statements[select]._reply);
2015-03-31 07:55:54 -04:00
if (_talkToAbort)
return;
if (!_talkStealth)
ui.clearWindow();
if (statement._modified.size() > 0) {
for (uint idx = 0; idx < statement._modified.size(); ++idx)
_vm->setFlags(statement._modified[idx]);
setTalkMap();
}
2015-05-07 19:33:44 +02:00
2015-03-31 07:55:54 -04:00
// Check for a linked file
if (!statement._linkFile.empty() && !_scriptMoreFlag) {
Common::String linkFilename = statement._linkFile;
2015-03-31 07:55:54 -04:00
freeTalkVars();
loadTalkFile(linkFilename);
2015-03-31 07:55:54 -04:00
// Scan for the first valid statement in the newly loaded file
select = -1;
for (uint idx = 0; idx < _statements.size(); ++idx) {
if (_statements[idx]._talkMap == 0) {
select = idx;
break;
}
}
if (_talkToFlag == 1)
pullSequence();
2015-03-31 07:55:54 -04:00
// Set the stealth mode for the new talk file
Statement &newStatement = _statements[select];
_talkStealth = newStatement._statement.hasPrefix("^") ? 2 : 0;
// If the new conversion is a reply first, then we don't need
// to display any choices, since the reply needs to be shown
if (!newStatement._statement.hasPrefix("*") &&
!newStatement._statement.hasPrefix("^")) {
clearSequences();
pushSequence(_talkTo);
setStillSeq(_talkTo);
2015-03-31 07:55:54 -04:00
_talkIndex = select;
ui._selector = ui._oldSelector = -1;
if (!ui._windowOpen) {
// Draw the talk interface on the back buffer
drawInterface();
displayTalk(false);
} else {
displayTalk(true);
}
byte color = ui._endKeyActive ? COMMAND_FOREGROUND : COMMAND_NULL;
// If the window is already open, simply draw. Otherwise, do it
2015-03-31 07:55:54 -04:00
// to the back buffer and then summon the window
if (ui._windowOpen) {
screen.buttonPrint(Common::Point(119, CONTROLS_Y), color, true, "Exit");
} else {
screen.buttonPrint(Common::Point(119, CONTROLS_Y), color, false, "Exit");
2015-05-07 19:33:44 +02:00
if (!ui._slideWindows) {
2015-03-31 07:55:54 -04:00
screen.slamRect(Common::Rect(0, CONTROLS_Y,
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
} else {
ui.summonWindow();
}
ui._windowOpen = true;
}
// Break out of loop now that we're waiting for player input
events.setCursor(ARROW);
break;
} else {
// Add the statement into the journal and talk history
if (_talkTo != -1 && !_talkHistory[_converseNum][select])
journal.record(_converseNum, select, true);
2015-03-31 07:55:54 -04:00
_talkHistory[_converseNum][select] = true;
}
ui._key = ui._oldKey = COMMANDS[TALK_MODE - 1];
ui._temp = ui._oldTemp = 0;
ui._menuMode = TALK_MODE;
_talkToFlag = 2;
} else {
freeTalkVars();
if (!ui._lookScriptFlag) {
ui.banishWindow();
ui._windowBounds.top = CONTROLS_Y1;
ui._menuMode = STD_MODE;
}
break;
}
}
}
_talkStealth = 0;
events._pressed = events._released = events._oldButtons = 0;
events.clearKeyboard();
if (savedBounds.bottom == SHERLOCK_SCREEN_HEIGHT)
screen.resetDisplayBounds();
else
screen.setDisplayBounds(savedBounds);
2015-03-31 07:55:54 -04:00
_talkToAbort = abortFlag;
// If a script was added to the script stack, restore state so that the
// previous script can continue
2015-04-15 07:49:10 -05:00
popStack();
2015-03-31 07:55:54 -04:00
if (_vm->getGameID() == GType_SerratedScalpel && filename == "Tube59c") {
// WORKAROUND: Original game bug causes the results of testing the powdery substance
// to disappear too quickly. Introduce a delay to allow it to be properly displayed
ui._menuCounter = 30;
}
2015-03-31 07:55:54 -04:00
events.setCursor(ARROW);
2015-03-15 16:52:55 -04:00
}
void Talk::talk(int objNum) {
2015-03-31 20:37:59 -04:00
Events &events = *_vm->_events;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Screen &screen = *_vm->_screen;
UserInterface &ui = *_vm->_ui;
Object &obj = scene._bgShapes[objNum];
2015-03-31 08:28:51 -04:00
2015-03-31 20:37:59 -04:00
ui._windowBounds.top = CONTROLS_Y;
ui._infoFlag = true;
_speaker = SPEAKER_REMOVE;
2015-03-31 20:37:59 -04:00
loadTalkFile(scene._bgShapes[objNum]._name);
// Find the first statement with the correct flags
int select = -1;
for (uint idx = 0; idx < _statements.size(); ++idx) {
if (_statements[idx]._talkMap == 0) {
select = idx;
break;
}
}
if (select == -1)
error("No entry matched all required flags");
// See if the statement is a stealth mode reply
Statement &statement = _statements[select];
if (statement._statement.hasPrefix("^")) {
clearSequences();
2015-03-31 20:37:59 -04:00
// Start talk in stealth mode
_talkStealth = 2;
talkTo(obj._name);
} else if (statement._statement.hasPrefix("*")) {
// Character being spoken to will speak first
clearSequences();
pushSequence(_talkTo);
setStillSeq(_talkTo);
2015-03-31 20:37:59 -04:00
events.setCursor(WAIT);
if (obj._lookPosition.y != 0)
// Need to walk to character first
2015-05-07 19:33:44 +02:00
people.walkToCoords(Common::Point(obj._lookPosition.x, obj._lookPosition.y * 100),
2015-03-31 20:37:59 -04:00
obj._lookFacing);
events.setCursor(ARROW);
if (!_talkToAbort)
2015-03-31 20:37:59 -04:00
talkTo(obj._name);
} else {
// Holmes will be speaking first
clearSequences();
pushSequence(_talkTo);
setStillSeq(_talkTo);
2015-03-31 20:37:59 -04:00
_talkToFlag = false;
events.setCursor(WAIT);
if (obj._lookPosition.y != 0)
// Walk over to person to talk to
people.walkToCoords(Common::Point(obj._lookPosition.x, obj._lookPosition.y * 100),
obj._lookFacing);
events.setCursor(ARROW);
if (!_talkToAbort) {
// See if walking over triggered a conversation
if (_talkToFlag) {
if (_talkToFlag == 1) {
events.setCursor(ARROW);
// _sequenceStack._count = 1;
pullSequence();
2015-03-31 20:37:59 -04:00
}
} else {
drawInterface();
events._pressed = events._released = false;
_talkIndex = select;
displayTalk(false);
ui._selector = ui._oldSelector = -1;
if (!ui._slideWindows) {
2015-03-31 20:37:59 -04:00
screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
SHERLOCK_SCREEN_HEIGHT));
} else {
ui.summonWindow();
}
ui._windowOpen = true;
}
_talkToFlag = -1;
}
}
}
void Talk::freeTalkVars() {
_statements.clear();
}
void Talk::loadTalkFile(const Common::String &filename) {
People &people = *_vm->_people;
Resources &res = *_vm->_res;
Sound &sound = *_vm->_sound;
// Save a copy of the talk filename
_scriptName = filename;
// Check for an existing person being talked to
_talkTo = -1;
for (int idx = 0; idx < (int)people._characters.size(); ++idx) {
if (!scumm_strnicmp(filename.c_str(), people._characters[idx]._portrait, 4)) {
_talkTo = idx;
break;
}
}
const char *chP = strchr(filename.c_str(), '.');
Common::String talkFile = chP ? Common::String(filename.c_str(), chP) + ".tlk" :
Common::String(filename.c_str(), filename.c_str() + 7) + ".tlk";
// Open the talk file for reading
Common::SeekableReadStream *talkStream = res.load(talkFile);
_converseNum = res.resourceIndex();
talkStream->skip(2); // Skip talk file version num
_statements.resize(talkStream->readByte());
for (uint idx = 0; idx < _statements.size(); ++idx)
2015-03-31 07:55:54 -04:00
_statements[idx].synchronize(*talkStream);
2015-05-07 19:33:44 +02:00
delete talkStream;
2015-03-31 07:55:54 -04:00
if (!sound._voices)
2015-03-31 07:55:54 -04:00
stripVoiceCommands();
setTalkMap();
}
2015-03-31 07:55:54 -04:00
void Talk::stripVoiceCommands() {
for (uint sIdx = 0; sIdx < _statements.size(); ++sIdx) {
Statement &statement = _statements[sIdx];
// Scan for an sound effect byte, which indicates to play a sound
for (uint idx = 0; idx < statement._reply.size(); ++idx) {
if (statement._reply[idx] == (char)_opcodes[OP_SFX_COMMAND]) {
2015-03-31 07:55:54 -04:00
// Replace instruction character with a space, and delete the
// rest of the name following it
2015-05-07 19:33:44 +02:00
statement._reply = Common::String(statement._reply.c_str(),
2015-03-31 07:55:54 -04:00
statement._reply.c_str() + idx) + " " +
Common::String(statement._reply.c_str() + 9);
}
}
// Ensure the last character of the reply is not a space from the prior
// conversion loop, to avoid any issues with the space ever causing a page
// wrap, and ending up displaying another empty page
while (statement._reply.lastChar() == ' ')
statement._reply.deleteLastChar();
}
}
void Talk::setTalkMap() {
int statementNum = 0;
for (uint sIdx = 0; sIdx < _statements.size(); ++sIdx) {
Statement &statement = _statements[sIdx];
// Set up talk map entry for the statement
bool valid = true;
for (uint idx = 0; idx < statement._required.size(); ++idx) {
if (!_vm->readFlags(statement._required[idx]))
valid = false;
}
statement._talkMap = valid ? statementNum++ : -1;
}
}
2015-03-31 07:55:54 -04:00
void Talk::drawInterface() {
Screen &screen = *_vm->_screen;
Surface &bb = *screen._backBuffer;
bb.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y1 + 10), BORDER_COLOR);
bb.fillRect(Common::Rect(0, CONTROLS_Y + 10, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
bb.fillRect(Common::Rect(SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y + 10,
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
bb.fillRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 1, SHERLOCK_SCREEN_WIDTH - 2,
SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
bb.fillRect(Common::Rect(2, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH - 2,
SHERLOCK_SCREEN_HEIGHT - 2), INV_BACKGROUND);
if (_talkTo != -1) {
screen.makeButton(Common::Rect(99, CONTROLS_Y, 139, CONTROLS_Y + 10),
119 - screen.stringWidth("Exit") / 2, "Exit");
screen.makeButton(Common::Rect(140, CONTROLS_Y, 180, CONTROLS_Y + 10),
159 - screen.stringWidth("Up") / 2, "Up");
screen.makeButton(Common::Rect(181, CONTROLS_Y, 221, CONTROLS_Y + 10),
200 - screen.stringWidth("Down") / 2, "Down");
} else {
int strWidth = screen.stringWidth(PRESS_KEY_TO_CONTINUE);
screen.makeButton(Common::Rect(46, CONTROLS_Y, 273, CONTROLS_Y + 10),
160 - strWidth / 2, PRESS_KEY_TO_CONTINUE);
2015-04-11 17:50:07 -05:00
screen.gPrint(Common::Point(160 - strWidth / 2, CONTROLS_Y), COMMAND_FOREGROUND, "P");
}
2015-03-31 07:55:54 -04:00
}
2015-03-31 08:28:51 -04:00
bool Talk::displayTalk(bool slamIt) {
Screen &screen = *_vm->_screen;
int yp = CONTROLS_Y + 14;
int lineY = -1;
_moreTalkDown = _moreTalkUp = false;
2015-05-07 19:33:44 +02:00
2015-03-31 08:28:51 -04:00
for (uint idx = 0; idx < _statements.size(); ++idx) {
_statements[idx]._talkPos.top = _statements[idx]._talkPos.bottom = -1;
}
if (_talkIndex) {
2015-04-24 20:59:30 -05:00
for (int idx = 0; idx < _talkIndex && !_moreTalkUp; ++idx) {
2015-03-31 08:28:51 -04:00
if (_statements[idx]._talkMap != -1)
_moreTalkUp = true;
}
}
2015-04-24 20:59:30 -05:00
// Display the up arrow and enable Up button if the first option is scrolled off-screen
2015-03-31 08:28:51 -04:00
if (_moreTalkUp) {
if (slamIt) {
screen.print(Common::Point(5, CONTROLS_Y + 13), INV_FOREGROUND, "~");
screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_FOREGROUND, true, "Up");
} else {
screen.gPrint(Common::Point(5, CONTROLS_Y + 12), INV_FOREGROUND, "~");
screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_FOREGROUND, false, "Up");
}
} else {
if (slamIt) {
screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, true, "Up");
screen.vgaBar(Common::Rect(5, CONTROLS_Y + 11, 15, CONTROLS_Y + 22), INV_BACKGROUND);
} else {
screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, false, "Up");
2015-05-07 19:33:44 +02:00
screen._backBuffer1.fillRect(Common::Rect(5, CONTROLS_Y + 11,
2015-03-31 08:28:51 -04:00
15, CONTROLS_Y + 22), INV_BACKGROUND);
}
}
// Loop through the statements
bool done = false;
for (uint idx = _talkIndex; idx < _statements.size() && !done; ++idx) {
Statement &statement = _statements[idx];
if (statement._talkMap != -1) {
bool flag = _talkHistory[_converseNum][idx];
2015-05-07 19:33:44 +02:00
lineY = talkLine(idx, statement._talkMap, flag ? TALK_NULL : INV_FOREGROUND,
2015-03-31 08:28:51 -04:00
yp, slamIt);
2015-05-07 19:33:44 +02:00
2015-03-31 08:28:51 -04:00
if (lineY != -1) {
statement._talkPos.top = yp;
yp = lineY;
statement._talkPos.bottom = yp;
if (yp == SHERLOCK_SCREEN_HEIGHT)
done = true;
} else {
done = true;
}
}
}
2015-04-24 20:59:30 -05:00
// Display the down arrow and enable down button if there are more statements available down off-screen
2015-03-31 08:28:51 -04:00
if (lineY == -1 || lineY == SHERLOCK_SCREEN_HEIGHT) {
_moreTalkDown = true;
if (slamIt) {
screen.print(Common::Point(5, 190), INV_FOREGROUND, "|");
screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_FOREGROUND, true, "Down");
} else {
screen.gPrint(Common::Point(5, 189), INV_FOREGROUND, "|");
screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_FOREGROUND, false, "Down");
}
} else {
if (slamIt) {
screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, true, "Down");
screen.vgaBar(Common::Rect(5, 189, 16, 199), INV_BACKGROUND);
} else {
2015-04-24 20:59:30 -05:00
screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, false, "Down");
2015-03-31 08:28:51 -04:00
screen._backBuffer1.fillRect(Common::Rect(5, 189, 16, 199), INV_BACKGROUND);
}
}
return done;
}
int Talk::talkLine(int lineNum, int stateNum, byte color, int lineY, bool slamIt) {
2015-03-31 20:16:34 -04:00
Screen &screen = *_vm->_screen;
int idx = lineNum;
Common::String msg, number;
bool numberFlag = false;
// Get the statement to display as well as optional number prefix
if (idx < SPEAKER_REMOVE) {
2015-03-31 20:16:34 -04:00
number = Common::String::format("%d.", stateNum + 1);
numberFlag = true;
} else {
idx -= SPEAKER_REMOVE;
2015-03-31 20:16:34 -04:00
}
msg = _statements[idx]._statement;
// Handle potentially multiple lines needed to display entire statement
const char *lineStartP = msg.c_str();
int maxWidth = 298 - (numberFlag ? 18 : 0);
2015-03-31 20:16:34 -04:00
for (;;) {
// Get as much of the statement as possible will fit on the
Common::String sLine;
const char *lineEndP = lineStartP;
int width = 0;
do {
width += screen.charWidth(*lineEndP);
} while (*++lineEndP && width < maxWidth);
// Check if we need to wrap the line
if (width >= maxWidth) {
// Work backwards to the prior word's end
while (*--lineEndP != ' ')
;
sLine = Common::String(lineStartP, lineEndP++);
} else {
// Can display remainder of the statement on the current line
sLine = Common::String(lineStartP);
}
if (lineY <= (SHERLOCK_SCREEN_HEIGHT - 10)) {
// Need to directly display on-screen?
if (slamIt) {
// See if a numer prefix is needed or not
if (numberFlag) {
// Are we drawing the first line?
if (lineStartP == msg.c_str()) {
// We are, so print the number and then the text
screen.print(Common::Point(16, lineY), color, "%s", number.c_str());
2015-03-31 20:16:34 -04:00
}
// Draw the line with an indent
screen.print(Common::Point(30, lineY), color, "%s", sLine.c_str());
2015-03-31 20:16:34 -04:00
} else {
screen.print(Common::Point(16, lineY), color, "%s", sLine.c_str());
2015-03-31 20:16:34 -04:00
}
} else {
if (numberFlag) {
if (lineStartP == msg.c_str()) {
screen.gPrint(Common::Point(16, lineY - 1), color, "%s", number.c_str());
2015-03-31 20:16:34 -04:00
}
screen.gPrint(Common::Point(30, lineY - 1), color, "%s", sLine.c_str());
2015-03-31 20:16:34 -04:00
} else {
screen.gPrint(Common::Point(16, lineY - 1), color, "%s", sLine.c_str());
2015-03-31 20:16:34 -04:00
}
}
2015-03-31 20:16:34 -04:00
// Move to next line, if any
lineY += 9;
lineStartP = lineEndP;
2015-05-07 19:33:44 +02:00
2015-03-31 20:16:34 -04:00
if (!*lineEndP)
break;
} else {
// We're close to the bottom of the screen, so stop display
lineY = -1;
break;
}
}
if (lineY == -1 && lineStartP != msg.c_str())
lineY = SHERLOCK_SCREEN_HEIGHT;
// Return the Y position of the next line to follow this one
return lineY;
}
2015-03-31 08:28:51 -04:00
void Talk::clearSequences() {
_sequenceStack.clear();
}
void Talk::pullSequence() {
Scene &scene = *_vm->_scene;
if (_sequenceStack.empty())
2015-04-11 17:50:07 -05:00
return;
SequenceEntry seq = _sequenceStack.pop();
if (seq._objNum != -1) {
Object &obj = scene._bgShapes[seq._objNum];
if (obj._seqSize < MAX_TALK_SEQUENCES) {
warning("Tried to restore too few frames");
} else {
for (int idx = 0; idx < MAX_TALK_SEQUENCES; ++idx)
obj._sequences[idx] = seq._sequences[idx];
obj._frameNumber = seq._frameNumber;
obj._seqTo = seq._seqTo;
}
}
}
void Talk::pushSequence(int speaker) {
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
// Only proceed if a speaker is specified
if (speaker == -1)
return;
SequenceEntry seqEntry;
if (!speaker) {
seqEntry._objNum = -1;
} else {
seqEntry._objNum = people.findSpeaker(speaker);
if (seqEntry._objNum != -1) {
Object &obj = scene._bgShapes[seqEntry._objNum];
for (uint idx = 0; idx < MAX_TALK_SEQUENCES; ++idx)
seqEntry._sequences.push_back(obj._sequences[idx]);
seqEntry._frameNumber = obj._frameNumber;
seqEntry._seqTo = obj._seqTo;
}
}
2015-05-07 19:33:44 +02:00
_sequenceStack.push(seqEntry);
if (_scriptStack.size() >= 5)
error("script stack overflow");
}
void Talk::setSequence(int speaker) {
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
2015-05-07 19:33:44 +02:00
// If no speaker is specified, then nothing needs to be done
if (speaker == -1)
return;
if (speaker) {
int objNum = people.findSpeaker(speaker);
if (objNum != -1) {
Object &obj = scene._bgShapes[objNum];
if (obj._seqSize < MAX_TALK_SEQUENCES) {
warning("Tried to copy too many talk frames");
} else {
for (int idx = 0; idx < MAX_TALK_SEQUENCES; ++idx) {
obj._sequences[idx] = people._characters[speaker]._talkSequences[idx];
if (idx > 0 && !obj._sequences[idx] && !obj._sequences[idx - 1])
return;
obj._frameNumber = 0;
obj._sequenceNumber = 0;
}
}
}
}
}
void Talk::setStillSeq(int speaker) {
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
// Don't bother doing anything if no specific speaker is specified
if (speaker == -1)
return;
if (speaker) {
int objNum = people.findSpeaker(speaker);
if (objNum != -1) {
Object &obj = scene._bgShapes[objNum];
2015-05-07 19:33:44 +02:00
if (obj._seqSize < MAX_TALK_SEQUENCES) {
warning("Tried to copy too few still frames");
} else {
for (uint idx = 0; idx < MAX_TALK_SEQUENCES; ++idx) {
obj._sequences[idx] = people._characters[speaker]._stillSequences[idx];
if (idx > 0 && !people._characters[speaker]._talkSequences[idx] &&
!people._characters[speaker]._talkSequences[idx - 1])
break;
}
obj._frameNumber = 0;
obj._seqTo = 0;
}
}
}
}
void Talk::doScript(const Common::String &script) {
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Screen &screen = *_vm->_screen;
UserInterface &ui = *_vm->_ui;
bool openTalkWindow = false;
_savedSequences.clear();
_scriptStart = (const byte *)script.c_str();
_scriptEnd = _scriptStart + script.size();
const byte *str = _scriptStart;
_yp = CONTROLS_Y + 12;
_charCount = 0;
_line = 0;
_wait = 0;
_pauseFlag = false;
_seqCount = 0;
_noTextYet = true;
_endStr = false;
if (_scriptMoreFlag) {
_scriptMoreFlag = 0;
str = _scriptStart + _scriptSaveIndex;
}
// Check if the script begins with a Stealh Mode Active command
if (str[0] == _opcodes[OP_STEALTH_MODE_ACTIVE] || _talkStealth) {
_talkStealth = 2;
_speaker |= SPEAKER_REMOVE;
} else {
pushSequence(_speaker);
ui.clearWindow();
// Need to switch speakers?
if (str[0] == _opcodes[OP_SWITCH_SPEAKER]) {
_speaker = str[1] - 1;
str += 2;
pullSequence();
pushSequence(_speaker);
setSequence(_speaker);
2015-04-24 17:27:23 -05:00
} else {
setSequence(_speaker);
}
// Assign portrait location?
if (str[0] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION]) {
switch (str[1] & 15) {
case 1:
people._portraitSide = 20;
break;
case 2:
people._portraitSide = 220;
break;
case 3:
people._portraitSide = 120;
break;
default:
break;
}
if (str[1] > 15)
people._speakerFlip = true;
str += 2;
}
// Remove portrait?
if (str[0] == _opcodes[OP_REMOVE_PORTRAIT]) {
_speaker = 255;
2015-04-24 17:27:23 -05:00
} else {
// Nope, so set the first speaker
people.setTalking(_speaker);
}
}
do {
Common::String tempString;
_wait = 0;
byte c = str[0];
if (!c) {
_endStr = true;
} else if (c == '{') {
// Start of comment, so skip over it
while (*str++ != '}')
;
} else if (_opcodeTable[c - 128]) {
// Handle control code
switch ((this->*_opcodeTable[c - 128])(str)) {
case RET_EXIT:
return;
case RET_CONTINUE:
continue;
default:
break;
}
++str;
} else {
// If the window isn't yet open, draw the window before printing starts
if (!ui._windowOpen && _noTextYet) {
_noTextYet = false;
drawInterface();
if (_talkTo != -1) {
screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, false, "Exit");
screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, false, "Up");
screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, false, "Down");
}
}
// If it's the first line, display the speaker
if (!_line && _speaker >= 0 && _speaker < (int)people._characters.size()) {
// If the window is open, display the name directly on-screen.
// Otherwise, simply draw it on the back buffer
if (ui._windowOpen) {
screen.print(Common::Point(16, _yp), TALK_FOREGROUND, "%s",
people._characters[_speaker & 127]._name);
} else {
screen.gPrint(Common::Point(16, _yp - 1), TALK_FOREGROUND, "%s",
people._characters[_speaker & 127]._name);
openTalkWindow = true;
}
_yp += 9;
}
2015-05-21 19:00:22 -04:00
// Find amount of text that will fit on the line
int width = 0, idx = 0;
do {
width += screen.charWidth(str[idx]);
2015-04-11 17:50:07 -05:00
++idx;
++_charCount;
} while (width < 298 && str[idx] && str[idx] != '{' && str[idx] < _opcodes[0]);
if (str[idx] || width >= 298) {
if (str[idx] < _opcodes[0] && str[idx] != '{') {
--idx;
--_charCount;
}
} else {
_endStr = true;
}
// If word wrap is needed, find the start of the current word
if (width >= 298) {
while (str[idx] != ' ') {
--idx;
--_charCount;
}
}
// Print the line
Common::String lineStr((const char *)str, (const char *)str + idx);
// If the speaker indicates a description file, print it in yellow
if (_speaker != -1) {
if (ui._windowOpen) {
screen.print(Common::Point(16, _yp), COMMAND_FOREGROUND, "%s", lineStr.c_str());
} else {
screen.gPrint(Common::Point(16, _yp - 1), COMMAND_FOREGROUND, "%s", lineStr.c_str());
openTalkWindow = true;
}
} else {
if (ui._windowOpen) {
screen.print(Common::Point(16, _yp), COMMAND_FOREGROUND, "%s", lineStr.c_str());
} else {
screen.gPrint(Common::Point(16, _yp - 1), COMMAND_FOREGROUND, "%s", lineStr.c_str());
openTalkWindow = true;
}
}
// Move to end of displayed line
str += idx;
// If line wrap occurred, then move to after the separating space between the words
if (str[0] < _opcodes[0] && str[0] != '{')
++str;
_yp += 9;
++_line;
// Certain different conditions require a wait
if ((_line == 4 && str < _scriptEnd && str[0] != _opcodes[OP_SFX_COMMAND] && str[0] != _opcodes[OP_PAUSE] && _speaker != -1) ||
(_line == 5 && str < _scriptEnd && str[0] != _opcodes[OP_PAUSE] && _speaker == -1) ||
_endStr) {
_wait = 1;
}
byte v = (str >= _scriptEnd ? 0 : str[0]);
_wait = v == _opcodes[OP_SWITCH_SPEAKER] || v == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION] ||
v == _opcodes[OP_BANISH_WINDOW] || _opcodes[OP_IF_STATEMENT] || v == _opcodes[OP_ELSE_STATEMENT] ||
v == _opcodes[OP_END_IF_STATEMENT] || v == _opcodes[OP_GOTO_SCENE] || v == _opcodes[OP_CALL_TALK_FILE];
}
// Open window if it wasn't already open, and text has already been printed
if ((openTalkWindow && _wait) || (openTalkWindow && str[0] >= _opcodes[0] && str[0] != _opcodes[OP_CARRIAGE_RETURN])) {
if (!ui._slideWindows) {
screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
} else {
ui.summonWindow();
}
ui._windowOpen = true;
openTalkWindow = false;
}
if (_wait) {
// Handling pausing
if (!_pauseFlag && _charCount < 160)
_charCount = 160;
_wait = waitForMore(_charCount);
if (_wait == -1)
_endStr = true;
// If a key was pressed to finish the window, see if further voice files should be skipped
if (_wait >= 0 && _wait < 254) {
if (str[0] == _opcodes[OP_SFX_COMMAND])
str += 9;
}
// Clear the window unless the wait was due to a PAUSE command
if (!_pauseFlag && _wait != -1 && str < _scriptEnd && str[0] != _opcodes[OP_SFX_COMMAND]) {
if (!_talkStealth)
ui.clearWindow();
_yp = CONTROLS_Y + 12;
_charCount = _line = 0;
}
_pauseFlag = false;
}
} while (!_vm->shouldQuit() && !_endStr);
if (_wait != -1) {
for (int ssIndex = 0; ssIndex < (int)_savedSequences.size(); ++ssIndex) {
SequenceEntry &seq = _savedSequences[ssIndex];
2015-05-09 13:08:36 +02:00
Object &object = scene._bgShapes[seq._objNum];
for (uint idx = 0; idx < seq._sequences.size(); ++idx)
2015-05-09 13:08:36 +02:00
object._sequences[idx] = seq._sequences[idx];
object._frameNumber = seq._frameNumber;
object._seqTo = seq._seqTo;
}
pullSequence();
if (_speaker >= 0 && _speaker < SPEAKER_REMOVE)
people.clearTalking();
}
}
int Talk::waitForMore(int delay) {
Events &events = *_vm->_events;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Sound &sound = *_vm->_sound;
UserInterface &ui = *_vm->_ui;
CursorId oldCursor = events.getCursor();
int key2 = 254;
// Unless we're in stealth mode, show the appropriate cursor
if (!_talkStealth) {
events.setCursor(ui._lookScriptFlag ? MAGNIFY : ARROW);
}
do {
if (sound._speechOn && !*sound._soundIsOn)
people._portrait._frameNumber = -1;
scene.doBgAnim();
// If talkTo call was done via doBgAnim, abort out of talk quietly
if (_talkToAbort) {
key2 = -1;
events._released = true;
} else {
// See if there's been a button press
events.setButtonState();
if (events.kbHit()) {
Common::KeyState keyState = events.getKey();
2015-05-18 23:44:59 -04:00
if (Common::isPrint(keyState.ascii))
key2 = keyState.keycode;
}
if (_talkStealth) {
key2 = 254;
events._released = false;
}
}
// Count down the delay
if ((delay > 0 && !ui._invLookFlag && !ui._lookScriptFlag) || _talkStealth)
--delay;
// If there are voices playing, reset delay so that they keep playing
if (sound._voices == 2 && *sound._soundIsOn)
delay = 0;
} while (!_vm->shouldQuit() && key2 == 254 && (delay || (sound._voices == 2 && *sound._soundIsOn))
&& !events._released && !events._rightReleased);
// If voices was set 2 to indicate a voice file was place, then reset it back to 1
if (sound._voices == 2)
sound._voices = 1;
if (delay > 0 && sound._diskSoundPlaying)
sound.stopSndFuncPtr(0, 0);
// Adjust _talkStealth mode:
// mode 1 - It was by a pause without stealth being on before the pause, so reset back to 0
// mode 3 - It was set by a pause with stealth being on before the pause, to set it to active
// mode 0/2 (Inactive/active) No change
switch (_talkStealth) {
case 1:
_talkStealth = 0;
break;
case 2:
_talkStealth = 2;
break;
default:
break;
}
sound._speechOn = false;
events.setCursor(_talkToAbort ? ARROW : oldCursor);
events._pressed = events._released = false;
return key2;
}
2015-04-15 07:49:10 -05:00
void Talk::popStack() {
if (!_scriptStack.empty()) {
ScriptStackEntry scriptEntry = _scriptStack.pop();
_scriptName = scriptEntry._name;
_scriptSaveIndex = scriptEntry._currentIndex;
_scriptSelect = scriptEntry._select;
_scriptMoreFlag = 1;
2015-04-15 07:49:10 -05:00
}
}
void Talk::synchronize(Common::Serializer &s) {
for (int idx = 0; idx < MAX_TALK_FILES; ++idx) {
TalkHistoryEntry &he = _talkHistory[idx];
2015-05-07 19:33:44 +02:00
for (int flag = 0; flag < 16; ++flag)
s.syncAsByte(he._data[flag]);
}
}
OpcodeReturn Talk::cmdAddItemToInventory(const byte *&str) {
Inventory &inv = *_vm->_inventory;
Common::String tempString;
++str;
for (int idx = 0; idx < str[0]; ++idx)
tempString += str[idx + 1];
str += str[0];
inv.putNameInInventory(tempString);
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdAdjustObjectSequence(const byte *&str) {
Scene &scene = *_vm->_scene;
Common::String tempString;
// Get the name of the object to adjust
++str;
for (int idx = 0; idx < (str[0] & 127); ++idx)
tempString += str[idx + 2];
// Scan for object
int objId = -1;
for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
if (tempString.equalsIgnoreCase(scene._bgShapes[idx]._name))
objId = idx;
}
if (objId == -1)
error("Could not find object %s to change", tempString.c_str());
// Should the script be overwritten?
if (str[0] > 0x80) {
// Save the current sequence
_savedSequences.push(SequenceEntry());
SequenceEntry &seqEntry = _savedSequences.top();
seqEntry._objNum = objId;
seqEntry._seqTo = scene._bgShapes[objId]._seqTo;
for (uint idx = 0; idx < scene._bgShapes[objId]._seqSize; ++idx)
seqEntry._sequences.push_back(scene._bgShapes[objId]._sequences[idx]);
}
// Get number of bytes to change
_seqCount = str[1];
str += (str[0] & 127) + 2;
// Copy in the new sequence
for (int idx = 0; idx < _seqCount; ++idx, ++str)
scene._bgShapes[objId]._sequences[idx] = str[0] - 1;
// Reset object back to beginning of new sequence
scene._bgShapes[objId]._frameNumber = 0;
return RET_CONTINUE;
}
OpcodeReturn Talk::cmdBanishWindow(const byte *&str) {
People &people = *_vm->_people;
UserInterface &ui = *_vm->_ui;
if (!(_speaker & SPEAKER_REMOVE))
people.clearTalking();
pullSequence();
if (_talkToAbort)
return RET_EXIT;
_speaker |= SPEAKER_REMOVE;
ui.banishWindow();
ui._menuMode = TALK_MODE;
_noTextYet = true;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdCallTalkFile(const byte *&str) {
Common::String tempString;
++str;
for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
tempString += str[idx];
str += 8;
int scriptCurrentIndex = str - _scriptStart;
// Save the current script position and new talk file
if (_scriptStack.size() < 9) {
ScriptStackEntry rec1;
rec1._name = _scriptName;
rec1._currentIndex = scriptCurrentIndex;
rec1._select = _scriptSelect;
_scriptStack.push(rec1);
// Push the new talk file onto the stack
ScriptStackEntry rec2;
rec2._name = tempString;
rec2._currentIndex = 0;
rec2._select = 100;
_scriptStack.push(rec2);
}
else {
error("Script stack overflow");
}
_scriptMoreFlag = 1;
_endStr = true;
_wait = 0;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdDisableEndKey(const byte *&str) {
_vm->_ui->_endKeyActive = false;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdEnableEndKey(const byte *&str) {
_vm->_ui->_endKeyActive = true;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdGotoScene(const byte *&str) {
Map &map = *_vm->_map;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
scene._goToScene = str[1] - 1;
if (scene._goToScene != 100) {
// Not going to the map overview
map._oldCharPoint = scene._goToScene;
map._overPos.x = map[scene._goToScene].x * 100 - 600;
map._overPos.y = map[scene._goToScene].y * 100 + 900;
// Run a canimation?
if (str[2] > 100) {
people._hSavedFacing = str[2];
people._hSavedPos = Common::Point(160, 100);
}
}
str += 6;
_scriptMoreFlag = (scene._goToScene == 100) ? 2 : 1;
_scriptSaveIndex = str - _scriptStart;
_endStr = true;
_wait = 0;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdHolmesOff(const byte *&str) {
_vm->_people->_holmesOn = false;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdHolmesOn(const byte *&str) {
_vm->_people->_holmesOn = true;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdPause(const byte *&str) {
_charCount = *++str;
_wait = _pauseFlag = true;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdPauseWithoutControl(const byte *&str) {
Events &events = *_vm->_events;
Scene &scene = *_vm->_scene;
++str;
for (int idx = 0; idx < (str[0] - 1); ++idx) {
scene.doBgAnim();
if (_talkToAbort)
return RET_EXIT;
// Check for button press
events.pollEvents();
events.setButtonState();
}
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdRemoveItemFromInventory(const byte *&str) {
Inventory &inv = *_vm->_inventory;
Common::String tempString;
++str;
for (int idx = 0; idx < str[0]; ++idx)
tempString += str[idx + 1];
str += str[0];
inv.deleteItemFromInventory(tempString);
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdRunCAnimation(const byte *&str) {
Scene &scene = *_vm->_scene;
++str;
scene.startCAnim((str[0] - 1) & 127, (str[0] & 0x80) ? -1 : 1);
if (_talkToAbort)
return RET_EXIT;
// Check if next character is changing side or changing portrait
if (_charCount && (str[1] == _opcodes[OP_SWITCH_SPEAKER] || str[1] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION]))
_wait = 1;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdSetFlag(const byte *&str) {
++str;
int flag1 = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
int flag = (flag1 & 0x3fff) * (flag1 >= 0x4000 ? -1 : 1);
_vm->setFlags(flag);
++str;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdSetObject(const byte *&str) {
Scene &scene = *_vm->_scene;
Common::String tempString;
++str;
for (int idx = 0; idx < (str[0] & 127); ++idx)
tempString += str[idx + 1];
// Set comparison state according to if we want to hide or unhide
bool state = (str[0] >= SPEAKER_REMOVE);
str += str[0] & 127;
for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
Object &object = scene._bgShapes[idx];
if (tempString.equalsIgnoreCase(object._name)) {
// Only toggle the object if it's not in the desired state already
if ((object._type == HIDDEN && state) || (object._type != HIDDEN && !state))
object.toggleHidden();
}
}
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdStealthModeActivate(const byte *&str) {
_talkStealth = 2;
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdStealthModeDeactivate(const byte *&str) {
Events &events = *_vm->_events;
_talkStealth = 0;
events.clearKeyboard();
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdSwitchSpeaker(const byte *&str) {
People &people = *_vm->_people;
UserInterface &ui = *_vm->_ui;
if (!(_speaker & SPEAKER_REMOVE))
people.clearTalking();
if (_talkToAbort)
return RET_EXIT;
ui.clearWindow();
_yp = CONTROLS_Y + 12;
_charCount = _line = 0;
_speaker = *++str - 1;
people.setTalking(_speaker);
pullSequence();
pushSequence(_speaker);
setSequence(_speaker);
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdToggleObject(const byte *&str) {
Scene &scene = *_vm->_scene;
Common::String tempString;
++str;
for (int idx = 0; idx < str[0]; ++idx)
tempString += str[idx + 1];
scene.toggleObject(tempString);
str += str[0];
return RET_SUCCESS;
}
OpcodeReturn Talk::cmdWalkToCAnimation(const byte *&str) {
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
++str;
CAnim &animation = scene._cAnim[str[0] - 1];
people.walkToCoords(animation._goto, animation._gotoDir);
return _talkToAbort ? RET_EXIT : RET_SUCCESS;
}
OpcodeReturn Talk::cmdWalkToCoords(const byte *&str) {
People &people = *_vm->_people;
++str;
people.walkToCoords(Common::Point(((str[0] - 1) * 256 + str[1] - 1) * 100,
str[2] * 100), str[3] - 1);
if (_talkToAbort)
return RET_EXIT;
str += 3;
return RET_SUCCESS;
}
/*----------------------------------------------------------------*/
ScalpelTalk::ScalpelTalk(SherlockEngine *vm) : Talk(vm) {
static OpcodeMethod OPCODE_METHODS[] = {
(OpcodeMethod)&ScalpelTalk::cmdSwitchSpeaker,
(OpcodeMethod)&ScalpelTalk::cmdRunCAnimation,
(OpcodeMethod)&ScalpelTalk::cmdAssignPortraitLocation,
(OpcodeMethod)&ScalpelTalk::cmdPause,
(OpcodeMethod)&ScalpelTalk::cmdRemovePortrait,
(OpcodeMethod)&ScalpelTalk::cmdClearWindow,
(OpcodeMethod)&ScalpelTalk::cmdAdjustObjectSequence,
(OpcodeMethod)&ScalpelTalk::cmdWalkToCoords,
(OpcodeMethod)&ScalpelTalk::cmdPauseWithoutControl,
(OpcodeMethod)&ScalpelTalk::cmdBanishWindow,
(OpcodeMethod)&ScalpelTalk::cmdSummonWindow,
(OpcodeMethod)&ScalpelTalk::cmdSetFlag,
(OpcodeMethod)&ScalpelTalk::cmdSfxCommand,
(OpcodeMethod)&ScalpelTalk::cmdToggleObject,
(OpcodeMethod)&ScalpelTalk::cmdStealthModeActivate,
(OpcodeMethod)&ScalpelTalk::cmdIf,
(OpcodeMethod)&ScalpelTalk::cmdElse,
nullptr,
(OpcodeMethod)&ScalpelTalk::cmdStealthModeDeactivate,
(OpcodeMethod)&ScalpelTalk::cmdHolmesOff,
(OpcodeMethod)&ScalpelTalk::cmdHolmesOn,
(OpcodeMethod)&ScalpelTalk::cmdGotoScene,
(OpcodeMethod)&ScalpelTalk::cmdPlayPrologue,
(OpcodeMethod)&ScalpelTalk::cmdAddItemToInventory,
(OpcodeMethod)&ScalpelTalk::cmdSetObject,
(OpcodeMethod)&ScalpelTalk::cmdCallTalkFile,
(OpcodeMethod)&ScalpelTalk::cmdMoveMouse,
(OpcodeMethod)&ScalpelTalk::cmdDisplayInfoLine,
(OpcodeMethod)&ScalpelTalk::cmdClearInfoLine,
(OpcodeMethod)&ScalpelTalk::cmdWalkToCAnimation,
(OpcodeMethod)&ScalpelTalk::cmdRemoveItemFromInventory,
(OpcodeMethod)&ScalpelTalk::cmdEnableEndKey,
(OpcodeMethod)&ScalpelTalk::cmdDisableEndKey,
(OpcodeMethod)&ScalpelTalk::cmdCarriageReturn
};
_opcodeTable = OPCODE_METHODS;
}
OpcodeReturn ScalpelTalk::cmdAssignPortraitLocation(const byte *&str) {
People &people = *_vm->_people;
++str;
switch (str[0] & 15) {
case 1:
people._portraitSide = 20;
break;
case 2:
people._portraitSide = 220;
break;
case 3:
people._portraitSide = 120;
break;
default:
break;
}
if (str[0] > 15)
people._speakerFlip = true;
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdClearInfoLine(const byte *&str) {
UserInterface &ui = *_vm->_ui;
ui._infoFlag = true;
ui.clearInfo();
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdClearWindow(const byte *&str) {
UserInterface &ui = *_vm->_ui;
ui.clearWindow();
_yp = CONTROLS_Y + 12;
_charCount = _line = 0;
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdDisplayInfoLine(const byte *&str) {
Screen &screen = *_vm->_screen;
UserInterface &ui = *_vm->_ui;
Common::String tempString;
++str;
for (int idx = 0; idx < str[0]; ++idx)
tempString += str[idx + 1];
str += str[0];
screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", tempString.c_str());
ui._menuCounter = 30;
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdElse(const byte *&str) {
// If this is encountered here, it means that a preceeding IF statement was found,
// and evaluated to true. Now all the statements for the true block are finished,
// so skip over the block of code that would have executed if the result was false
_wait = 0;
do {
++str;
} while (str[0] && str[0] != _opcodes[OP_END_IF_STATEMENT]);
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdIf(const byte *&str) {
++str;
int flag = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
++str;
_wait = 0;
bool result = flag < 0x8000;
if (_vm->readFlags(flag & 0x7fff) != result) {
do {
++str;
} while (str[0] && str[0] != _opcodes[OP_ELSE_STATEMENT] && str[0] != _opcodes[OP_END_IF_STATEMENT]);
if (!str[0])
_endStr = true;
}
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdMoveMouse(const byte *&str) {
Events &events = *_vm->_events;
++str;
events.moveMouse(Common::Point((str[0] - 1) * 256 + str[1] - 1, str[2]));
if (_talkToAbort)
return RET_EXIT;
str += 3;
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdPlayPrologue(const byte *&str) {
Animation &anim = *_vm->_animation;
Common::String tempString;
++str;
for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
tempString += str[idx];
anim.play(tempString, 1, 3, true, 4);
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdRemovePortrait(const byte *&str) {
People &people = *_vm->_people;
if (_speaker >= 0 && _speaker < SPEAKER_REMOVE)
people.clearTalking();
pullSequence();
if (_talkToAbort)
return RET_EXIT;
_speaker |= SPEAKER_REMOVE;
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdSfxCommand(const byte *&str) {
Sound &sound = *_vm->_sound;
Common::String tempString;
++str;
if (sound._voices) {
for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
tempString += str[idx];
sound.playSound(tempString, WAIT_RETURN_IMMEDIATELY);
// Set voices to wait for more
sound._voices = 2;
sound._speechOn = (*sound._soundIsOn);
}
_wait = 1;
str += 7;
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdSummonWindow(const byte *&str) {
Events &events = *_vm->_events;
Screen &screen = *_vm->_screen;
drawInterface();
events._pressed = events._released = false;
events.clearKeyboard();
_noTextYet = false;
if (_speaker != -1) {
screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, false, "Exit");
screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, false, "Up");
screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, false, "Down");
}
return RET_SUCCESS;
}
OpcodeReturn ScalpelTalk::cmdCarriageReturn(const byte *&str) {
return RET_SUCCESS;
}
/*----------------------------------------------------------------*/
TattooTalk::TattooTalk(SherlockEngine *vm) : Talk(vm) {
static OpcodeMethod OPCODE_METHODS[] = {
nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
(OpcodeMethod)&TattooTalk::cmdSwitchSpeaker,
(OpcodeMethod)&TattooTalk::cmdRunCAnimation,
(OpcodeMethod)&TattooTalk::cmdCallTalkFile,
(OpcodeMethod)&TattooTalk::cmdPause,
(OpcodeMethod)&TattooTalk::cmdMouseOnOff,
(OpcodeMethod)&TattooTalk::cmdSetWalkControl,
(OpcodeMethod)&TattooTalk::cmdAdjustObjectSequence,
(OpcodeMethod)&TattooTalk::cmdWalkToCoords,
(OpcodeMethod)&TattooTalk::cmdPauseWithoutControl,
(OpcodeMethod)&TattooTalk::cmdBanishWindow,
(OpcodeMethod)&TattooTalk::cmdSetTalkSequence,
(OpcodeMethod)&TattooTalk::cmdSetFlag,
(OpcodeMethod)&TattooTalk::cmdPlaySong,
(OpcodeMethod)&TattooTalk::cmdToggleObject,
(OpcodeMethod)&TattooTalk::cmdStealthModeActivate,
(OpcodeMethod)&TattooTalk::cmdWalkNPCToCAnimation,
(OpcodeMethod)&TattooTalk::cmdWalkNPCToCoords,
(OpcodeMethod)&TattooTalk::cmdWalkHomesAndNPCToCoords,
(OpcodeMethod)&TattooTalk::cmdStealthModeDeactivate,
(OpcodeMethod)&TattooTalk::cmdHolmesOff,
(OpcodeMethod)&TattooTalk::cmdHolmesOn,
(OpcodeMethod)&TattooTalk::cmdGotoScene,
(OpcodeMethod)&TattooTalk::cmdSetNPCPathDest,
(OpcodeMethod)&TattooTalk::cmdAddItemToInventory,
(OpcodeMethod)&TattooTalk::cmdSetObject,
(OpcodeMethod)&TattooTalk::cmdNextSong,
(OpcodeMethod)&TattooTalk::cmdSetNPCPathPause,
(OpcodeMethod)&TattooTalk::cmdPassword,
(OpcodeMethod)&TattooTalk::cmdSetSceneEntryFlag,
(OpcodeMethod)&TattooTalk::cmdWalkToCAnimation,
(OpcodeMethod)&TattooTalk::cmdRemoveItemFromInventory,
(OpcodeMethod)&TattooTalk::cmdEnableEndKey,
(OpcodeMethod)&TattooTalk::cmdDisableEndKey,
nullptr,
(OpcodeMethod)&TattooTalk::cmdWalkHomesAndNPCToCoords,
(OpcodeMethod)&TattooTalk::cmdSetNPCTalkFile,
(OpcodeMethod)&TattooTalk::cmdSetNPCOff,
(OpcodeMethod)&TattooTalk::cmdSetNPCOn,
(OpcodeMethod)&TattooTalk::cmdSetNPCDescOnOff,
(OpcodeMethod)&TattooTalk::cmdSetNPCPathPauseTakingNotes,
(OpcodeMethod)&TattooTalk::cmdSetNPCPathPauseLookingHolmes,
(OpcodeMethod)&TattooTalk::cmdTalkInterruptsEnable,
(OpcodeMethod)&TattooTalk::cmdTalkInterruptsDisable,
(OpcodeMethod)&TattooTalk::cmdSetNPCInfoLine,
(OpcodeMethod)&TattooTalk::cmdSetNPCPosition,
(OpcodeMethod)&TattooTalk::cmdNPCLabelSet,
(OpcodeMethod)&TattooTalk::cmdNPCLabelGoto,
(OpcodeMethod)&TattooTalk::cmdNPCLabelIfFlagGoto,
(OpcodeMethod)&TattooTalk::cmdSetNPCWalkGraphics,
nullptr,
(OpcodeMethod)&TattooTalk::cmdSetNPCVerb,
(OpcodeMethod)&TattooTalk::cmdSetNPCVerbCAnimation,
(OpcodeMethod)&TattooTalk::cmdSetNPCVerbScript,
nullptr,
(OpcodeMethod)&TattooTalk::cmdRestorePeopleSequence,
(OpcodeMethod)&TattooTalk::cmdSetNPCVerbTarget,
(OpcodeMethod)&TattooTalk::cmdTurnSoundsOff
};
_opcodeTable = OPCODE_METHODS;
}
OpcodeReturn TattooTalk::cmdMouseOnOff(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdNextSong(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdNPCLabelGoto(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdNPCLabelIfFlagGoto(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdNPCLabelSet(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdPassword(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdPlaySong(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdRestorePeopleSequence(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCDescOnOff(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCInfoLine(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCOff(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCOn(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCPathDest(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCPathPause(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCPathPauseTakingNotes(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCPathPauseLookingHolmes(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCPosition(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCTalkFile(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCVerb(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCVerbCAnimation(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCVerbScript(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCVerbTarget(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetNPCWalkGraphics(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetSceneEntryFlag(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetTalkSequence(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdSetWalkControl(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdTalkInterruptsDisable(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdTalkInterruptsEnable(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdTurnSoundsOff(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdWalkHolmesAndNPCToCAnimation(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdWalkNPCToCAnimation(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdWalkNPCToCoords(const byte *&str) { error("TODO: script opcode"); }
OpcodeReturn TattooTalk::cmdWalkHomesAndNPCToCoords(const byte *&str) { error("TODO: script opcode"); }
2015-03-15 16:52:55 -04:00
} // End of namespace Sherlock