diff options
author | vanfanel | 2015-11-11 17:56:12 +0100 |
---|---|---|
committer | vanfanel | 2015-11-11 17:56:12 +0100 |
commit | 99739a13fe844c807d3cdd87e67e207e888fd48a (patch) | |
tree | 6afbf4763326277efbf528f0bb9e587bf7a01788 /engines/sherlock/tattoo/tattoo_talk.cpp | |
parent | 37e157a11c3fc731dfdcf6ec6b6a5a448550219b (diff) | |
parent | 7e44493fe8877a3c6a65f83b9ed84a5f59169005 (diff) | |
download | scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.tar.gz scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.tar.bz2 scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.zip |
Merge branch 'master' into dispmanx
Diffstat (limited to 'engines/sherlock/tattoo/tattoo_talk.cpp')
-rw-r--r-- | engines/sherlock/tattoo/tattoo_talk.cpp | 1017 |
1 files changed, 1017 insertions, 0 deletions
diff --git a/engines/sherlock/tattoo/tattoo_talk.cpp b/engines/sherlock/tattoo/tattoo_talk.cpp new file mode 100644 index 0000000000..a5ada7b63e --- /dev/null +++ b/engines/sherlock/tattoo/tattoo_talk.cpp @@ -0,0 +1,1017 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sherlock/tattoo/tattoo_talk.h" +#include "sherlock/tattoo/tattoo_fixed_text.h" +#include "sherlock/tattoo/tattoo_people.h" +#include "sherlock/tattoo/tattoo_scene.h" +#include "sherlock/tattoo/tattoo_user_interface.h" +#include "sherlock/tattoo/tattoo.h" +#include "sherlock/screen.h" + +namespace Sherlock { + +namespace Tattoo { + +static const uint8 DIRECTION_CONVERSION[] = { + WALK_RIGHT, WALK_DOWN, WALK_LEFT, WALK_UP, STOP_RIGHT, STOP_DOWN, STOP_LEFT, STOP_UP, + WALK_UPRIGHT, WALK_DOWNRIGHT, WALK_UPLEFT, WALK_DOWNLEFT, STOP_UPRIGHT, STOP_UPLEFT, + STOP_DOWNRIGHT, STOP_DOWNLEFT +}; + +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_HOlMES_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 + 203, // OP_END_TEXT_WINDOW + 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 + 186, // OP_WALK_NPC_TO_COORDS + 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 + 225 // OP_NULL +}; + +/*----------------------------------------------------------------*/ + +TattooTalk::TattooTalk(SherlockEngine *vm) : Talk(vm), _talkWidget(vm), _passwordWidget(vm) { + static OpcodeMethod OPCODE_METHODS[] = { + (OpcodeMethod)&TattooTalk::cmdSwitchSpeaker, + + (OpcodeMethod)&TattooTalk::cmdRunCAnimation, + (OpcodeMethod)&TattooTalk::cmdCallTalkFile, + (OpcodeMethod)&TattooTalk::cmdPause, + (OpcodeMethod)&TattooTalk::cmdMouseOnOff, + (OpcodeMethod)&TattooTalk::cmdSetWalkControl, + (OpcodeMethod)&TattooTalk::cmdAdjustObjectSequence, + (OpcodeMethod)&TattooTalk::cmdWalkHolmesToCoords, + (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, + (OpcodeMethod)&TattooTalk::cmdEndTextWindow, + (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, + nullptr, + (OpcodeMethod)&TattooTalk::cmdSetNPCVerbTarget, + (OpcodeMethod)&TattooTalk::cmdTurnSoundsOff + }; + + _opcodes = TATTOO_OPCODES; + _opcodeTable = OPCODE_METHODS; +} + +void TattooTalk::talkTo(const Common::String filename) { + Events &events = *_vm->_events; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + // WORKAROUND: Keep wait cursor active until very end of the cutscene of the monkey + // stealing the cap, which is finished by calling the 30cuend script + if (filename == "wilb29a") + events.incWaitCounter(); + + Talk::talkTo(filename); + + if (filename == "wilb29a") + ui._menuMode = TALK_MODE; + if (filename == "30cuend") { + events.decWaitCounter(); + events.setCursor(ARROW); + } +} + +void TattooTalk::talkInterface(const byte *&str) { + TattooEngine &vm = *(TattooEngine *)_vm; + Sound &sound = *_vm->_sound; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + const byte *s = str; + + // Move to past the end of the text string + _wait = 1; + _charCount = 0; + while ((*str < TATTOO_OPCODES[0] || *str == TATTOO_OPCODES[OP_NULL]) && *str) { + ++_charCount; + ++str; + } + + // If speech is on, and text windows (subtitles) are off, then don't show the text window + if (!vm._textWindowsOn && sound._speechOn && _speaker != -1) + return; + + // Display the text window + ui.banishWindow(); + ui._textWidget.load(Common::String((const char *)s, (const char *)str), _speaker); + ui._textWidget.summonWindow(); +} + +void TattooTalk::nothingToSay() { + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + ui.putMessage("%s", FIXED(NothingToSay)); +} + +void TattooTalk::showTalk() { + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + people.setListenSequence(_talkTo, 129); + + _talkWidget.load(); + _talkWidget.summonWindow(); + _talkWidget.refresh(); + + if (ui._menuMode != MESSAGE_MODE) + ui._menuMode = TALK_MODE; +} + +OpcodeReturn TattooTalk::cmdSwitchSpeaker(const byte *&str) { + TattooPeople &people = *(TattooPeople *)_vm->_people; + Screen &screen = *_vm->_screen; + UserInterface &ui = *_vm->_ui; + + if (_talkToAbort) + return RET_EXIT; + + ui.clearWindow(); + + _yp = screen.fontHeight() + 11; + _charCount = _line = 0; + + people.setListenSequence(_speaker, 129); + _speaker = *++str - 1; + ++str; + + people.setTalkSequence(_speaker, 1); + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdMouseOnOff(const byte *&str) { + Events &events = *_vm->_events; + bool mouseOn = *++str == 2; + if (mouseOn) + events.showCursor(); + else + events.hideCursor(); + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdWalkHolmesToCoords(const byte *&str) { + People &people = *_vm->_people; + ++str; + + int xp = (str[0] - 1) * 256 + str[1] - 1; + if (xp > 16384) + // Negative X + xp = -1 * (xp - 16384); + int yp = (str[2] - 1) * 256 + str[3] - 1; + + people[HOLMES].walkToCoords(Point32(xp * FIXED_INT_MULTIPLIER, yp * FIXED_INT_MULTIPLIER), + DIRECTION_CONVERSION[str[4] - 1]); + + if (_talkToAbort) + return RET_EXIT; + + str += 4; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdGotoScene(const byte *&str) { + Map &map = *_vm->_map; + TattooPeople &people = *(TattooPeople *)_vm->_people; + Scene &scene = *_vm->_scene; + scene._goToScene = str[1] - 1; + + if (scene._goToScene != OVERHEAD_MAP) { + // Not going to the map overview + map._oldCharPoint = scene._goToScene; + + // Run a canimation? + if (str[2] > 100) { + people._savedPos = PositionFacing(160, 100, str[2]); + } else { + int posX = (str[3] - 1) * 256 + str[4] - 1; + if (posX > 16384) + posX = -1 * (posX - 16384); + int posY = (str[5] - 1) * 256 + str[6] - 1; + people._savedPos = PositionFacing(posX, posY, str[2] - 1); + } + + _scriptMoreFlag = 1; + } + + str += 7; + if (scene._goToScene != OVERHEAD_MAP) + _scriptSaveIndex = str - _scriptStart; + + _endStr = true; + _wait = 0; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdNextSong(const byte *&str) { + Music &music = *_vm->_music; + + // Get the name of the next song to play + ++str; + music._nextSongName = ""; + for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx) + music._nextSongName += str[idx]; + str += 7; + + // WORKAROUND: Original game set wrong music name at the end of the introduction sequence + if (_scriptName == "prol80p" && music._nextSongName == "default") + music._nextSongName = "01cue90"; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdNPCLabelGoto(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 8; + person._npcPath[person._npcIndex + 1] = str[1]; + person._npcIndex += 2; + str++; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdNPCLabelIfFlagGoto(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 9; + for (int i = 1; i <= 3; i++) + person._npcPath[person._npcIndex + i] = str[i]; + + person._npcIndex += 4; + str += 3; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdNPCLabelSet(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 7; + person._npcPath[person._npcIndex + 1] = str[1]; + person._npcIndex += 2; + str++; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdPassword(const byte *&str) { + _vm->_ui->clearWindow(); + _passwordWidget.show(); + return RET_EXIT; +} + +OpcodeReturn TattooTalk::cmdPlaySong(const byte *&str) { + Music &music = *_vm->_music; + Common::String currentSong = music._currentSongName; + + // Get the name of the song to play + music._currentSongName = ""; + str++; + for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx) + music._currentSongName += str[idx]; + str += 7; + + // Play the song + music.loadSong(music._currentSongName); + + // Copy the old song name to _nextSongName so that when the new song is finished, the old song will restart + music._nextSongName = currentSong; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdRestorePeopleSequence(const byte *&str) { + int npcNum = *++str - 1; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + person._misc = 0; + + if (person._seqTo) { + person._walkSequences[person._sequenceNumber]._sequences[person._frameNumber] = person._seqTo; + person._seqTo = 0; + } + person._sequenceNumber = person._savedNpcSequence; + person._frameNumber = person._savedNpcFrame; + person.checkWalkGraphics(); + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCDescOnOff(const byte *&str) { + int npcNum = *++str; + ++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + Person &person = people[npcNum]; + + // Copy over the NPC examine text until we reach a stop marker, which is + // the same as a start marker, or we reach the end of the file + person._examine = ""; + while (*str && *str != _opcodes[OP_NPC_DESC_ON_OFF]) + person._examine += *str++; + + // Move past any leftover text till we reach a stop marker + while (*str && *str != _opcodes[OP_NPC_DESC_ON_OFF]) + str++; + + if (!*str) + // Reached end of file, so decrement pointer so outer loop will terminate on NULL + --str; + else + // Move past the ending OP_NPC_DEST_ON_OFF opcode + ++str; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCInfoLine(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + person._description = ""; + int len = *++str; + for (int idx = 0; idx < len; ++idx) + person._description += str[idx + 1]; + + str += len; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCOff(const byte *&str) { + TattooPeople &people = *(TattooPeople *)_vm->_people; + int npcNum = *++str; + people[npcNum]._type = REMOVE; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCOn(const byte *&str) { + TattooPeople &people = *(TattooPeople *)_vm->_people; + int npcNum = *++str; + people[npcNum]._type = CHARACTER; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCPathDest(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 1; + for (int i = 1; i <= 4; i++) + person._npcPath[person._npcIndex + i] = str[i]; + person._npcPath[person._npcIndex + 5] = DIRECTION_CONVERSION[str[5] - 1] + 1; + + person._npcIndex += 6; + str += 5; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCPathPause(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 2; + for (int i = 1; i <= 2; i++) + person._npcPath[person._npcIndex + i] = str[i]; + + person._npcIndex += 3; + str += 2; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCPathPauseTakingNotes(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 5; + for (int i = 1; i <= 2; i++) + person._npcPath[person._npcIndex + i] = str[i]; + + person._npcIndex += 3; + str += 2; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCPathPauseLookingHolmes(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = 6; + for (int i = 1; i <= 2; i++) + person._npcPath[person._npcIndex + i] = str[i]; + + person._npcIndex += 3; + str += 2; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCPosition(const byte *&str) { + int npcNum = *++str - 1; + ++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + int posX = (str[0] - 1) * 256 + str[1] - 1; + if (posX > 16384) + posX = -1 * (posX - 16384); + int posY = (str[2] - 1) * 256 + str[3] - 1; + + person._position = Point32(posX * FIXED_INT_MULTIPLIER, posY * FIXED_INT_MULTIPLIER); + if (person._seqTo && person._walkLoaded) { + person._walkSequences[person._sequenceNumber]._sequences[person._frameNumber] = person._seqTo; + person._seqTo = 0; + } + + assert(str[4] - 1 < 16); + person._sequenceNumber = DIRECTION_CONVERSION[str[4] - 1]; + person._frameNumber = 0; + + if (person._walkLoaded) + person.checkWalkGraphics(); + + if (person._walkLoaded && person._type == CHARACTER && + person._sequenceNumber >= STOP_UP && person._sequenceNumber <= STOP_UPLEFT) { + bool done = false; + do { + person.checkSprite(); + for (int x = 0; x < person._frameNumber; x++) { + if (person._walkSequences[person._sequenceNumber]._sequences[x] == 0) { + done = true; + break; + } + } + } while (!done); + } + + str += 4; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCTalkFile(const byte *&str) { + int npcNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + memset(person._npcPath, 0, 100); + } + + person._npcPath[person._npcIndex] = NPCPATH_SET_TALK_FILE; + for (int i = 1; i <= 8; i++) + person._npcPath[person._npcIndex + i] = str[i]; + + person._npcIndex += 9; + str += 8; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCVerb(const byte *&str) { + int npcNum = *++str; + int verbNum = *++str - 1; + TattooPeople &people = *(TattooPeople *)_vm->_people; + Common::String &verb = people[npcNum]._use[verbNum]._verb; + + // Get the verb name + verb = ""; + for (int idx = 0; idx < 12 && str[idx + 1] != '~'; ++idx) + verb += str[idx + 1]; + + // Strip off any trailing whitespace + while (verb.hasSuffix(" ")) + verb.deleteLastChar(); + + str += 12; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCVerbCAnimation(const byte *&str) { + int npcNum = *++str; + int verbNum = *++str - 1; + TattooPeople &people = *(TattooPeople *)_vm->_people; + UseType &useType = people[npcNum]._use[verbNum]; + + useType._cAnimNum = (str[1] - 1) & 127; + useType._cAnimSpeed = 1 + 128 * (str[1] >= 128); + str++; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCVerbScript(const byte *&str) { + int npcNum = *++str; + int verbNum = *++str - 1; + TattooPeople &people = *(TattooPeople *)_vm->_people; + UseType &useType = people[npcNum]._use[verbNum]; + + Common::String &name = useType._names[0]; + name = "*C"; + + for (int idx = 0; idx < 8 && str[idx + 1] != '~'; ++idx) + name += str[idx + 1]; + + useType._cAnimNum = 99; + useType._cAnimSpeed = 1; + str += 8; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCVerbTarget(const byte *&str) { + int npcNum = *++str; + int verbNum = *++str - 1; + TattooPeople &people = *(TattooPeople *)_vm->_people; + Common::String &target = people[npcNum]._use[verbNum]._target; + + target = ""; + for (int idx = 0; idx < 12 && str[idx + 1] != '~'; ++idx) + target += str[idx + 1]; + + while (target.hasSuffix(" ")) + target.deleteLastChar(); + + str += 12; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetNPCWalkGraphics(const byte *&str) { + int npcNum = *++str - 1; + TattooPeople &people = *(TattooPeople *)_vm->_people; + Person &person = people[npcNum]; + + // Build up walk library name for the given NPC + person._walkVGSName = ""; + for (int idx = 0; idx < 8 && str[idx + 1] != '~'; ++idx) + person._walkVGSName += str[idx + 1]; + + person._walkVGSName += ".VGS"; + people._forceWalkReload = true; + str += 8; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetSceneEntryFlag(const byte *&str) { + TattooScene &scene = *(TattooScene *)_vm->_scene; + ++str; + int flag = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1); + + int flag1 = flag & 16383; + if (flag > 16383) + flag1 *= -1; + + str += 2; + + // Make sure that this instance is not already being tracked + bool found = false; + for (uint idx = 0; idx < scene._sceneTripCounters.size() && !found; ++idx) { + SceneTripEntry &entry = scene._sceneTripCounters[idx]; + if (entry._flag == flag1 && entry._sceneNumber == str[0] - 1) + found = true; + } + + // Only add it if it's not being tracked already + if (!found) + scene._sceneTripCounters.push_back(SceneTripEntry(flag1, str[0] - 1, str[1] - 1)); + + ++str; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetTalkSequence(const byte *&str) { + TattooPeople &people = *(TattooPeople *)_vm->_people; + int speaker = str[1] - 1; + int sequenceNumber = str[2]; + + if (sequenceNumber < 128) + people.setTalkSequence(speaker, sequenceNumber); + else + people.setListenSequence(speaker, sequenceNumber); + + str += 2; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdSetWalkControl(const byte *&str) { + TattooPeople &people = *(TattooPeople *)_vm->_people; + ++str; + people._walkControl = str[0] - 1; + + return RET_SUCCESS; +} + +// Dummy opcode +OpcodeReturn TattooTalk::cmdTalkInterruptsDisable(const byte *&str) { error("Dummy opcode cmdTalkInterruptsDisable called"); } + +// Dummy opcode +OpcodeReturn TattooTalk::cmdTalkInterruptsEnable(const byte *&str) { error("Dummy opcode cmdTalkInterruptsEnable called"); } + +OpcodeReturn TattooTalk::cmdTurnSoundsOff(const byte *&str) { error("TODO: script opcode (cmdTurnSoundsOff)"); } + +OpcodeReturn TattooTalk::cmdWalkHolmesAndNPCToCAnimation(const byte *&str) { + int npcNum = *++str; + int cAnimNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + Scene &scene = *_vm->_scene; + CAnim &anim = scene._cAnim[cAnimNum]; + + if (person._pathStack.empty()) + person.pushNPCPath(); + person._npcMoved = true; + + person.walkToCoords(anim._goto[1], anim._goto[1]._facing); + + if (_talkToAbort) + return RET_EXIT; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdWalkNPCToCAnimation(const byte *&str) { + int npcNum = *++str; + int cAnimNum = *++str; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + Scene &scene = *_vm->_scene; + CAnim &anim = scene._cAnim[cAnimNum]; + + if (person._pathStack.empty()) + person.pushNPCPath(); + person._npcMoved = true; + + person.walkToCoords(anim._goto[1], anim._goto[1]._facing); + + if (_talkToAbort) + return RET_EXIT; + + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdWalkNPCToCoords(const byte *&str) { + int npcNum = *++str; + str++; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._pathStack.empty()) + person.pushNPCPath(); + person._npcMoved = true; + + int xp = (str[0] - 1) * 256 + str[1] - 1; + if (xp > 16384) + xp = -1 * (xp - 16384); + int yp = (str[2] - 1) * 256 + str[3] - 1; + + person.walkToCoords(Point32(xp * FIXED_INT_MULTIPLIER, yp * FIXED_INT_MULTIPLIER), + DIRECTION_CONVERSION[str[4] - 1]); + if (_talkToAbort) + return RET_EXIT; + + str += 4; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdWalkHomesAndNPCToCoords(const byte *&str) { + int npcNum = *++str; + str++; + TattooPeople &people = *(TattooPeople *)_vm->_people; + TattooPerson &person = people[npcNum]; + + if (person._pathStack.empty()) + person.pushNPCPath(); + person._npcMoved = true; + + // Get destination position and facing for Holmes + int xp = (str[0] - 1) * 256 + str[1] - 1; + if (xp > 16384) + xp = -1 * (xp - 16384); + int yp = (str[2] - 1) * 256 + str[3] - 1; + PositionFacing holmesDest(xp * FIXED_INT_MULTIPLIER, yp * FIXED_INT_MULTIPLIER, DIRECTION_CONVERSION[str[4] - 1]); + + // Get destination position and facing for specified NPC + xp = (str[5] - 1) * 256 + str[6] - 1; + if (xp > 16384) + xp = -1 * (xp - 16384); + yp = (str[7] - 1) * 256 + str[8] - 1; + PositionFacing npcDest(xp * FIXED_INT_MULTIPLIER, yp * FIXED_INT_MULTIPLIER, DIRECTION_CONVERSION[str[9] - 1]); + + person.walkBothToCoords(holmesDest, npcDest); + + if (_talkToAbort) + return RET_EXIT; + + str += 9; + return RET_SUCCESS; +} + +OpcodeReturn TattooTalk::cmdCallTalkFile(const byte *&str) { + TattooPeople &people = *(TattooPeople *)_vm->_people; + Common::String tempString; + + int npc = *++str; + assert(npc >= 1 && npc < MAX_CHARACTERS); + TattooPerson &person = people[npc]; + + if (person._resetNPCPath) { + person._npcIndex = person._npcPause = 0; + person._resetNPCPath = false; + Common::fill(&person._npcPath[0], &person._npcPath[100], 0); + } + + // Set the path control code and copy the filename + person._npcPath[person._npcIndex] = 4; + for (int idx = 1; idx <= 8; ++idx) + person._npcPath[person._npcIndex + idx] = str[idx]; + + person._npcIndex += 9; + str += 8; + + return RET_SUCCESS; +} + +void TattooTalk::pushSequenceEntry(Object *obj) { + // Check if the shape is already on the stack + for (uint idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) { + if (_sequenceStack[idx]._obj == obj) + return; + } + + // Find a free slot and save the details in it + for (uint idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) { + SequenceEntry &seq = _sequenceStack[idx]; + if (seq._obj == nullptr) { + seq._obj = obj; + seq._frameNumber = obj->_frameNumber; + seq._sequenceNumber = obj->_sequenceNumber; + seq._seqStack = obj->_seqStack; + seq._seqTo = obj->_seqTo; + seq._seqCounter = obj->_seqCounter; + seq._seqCounter2 = obj->_seqCounter2; + return; + } + } + + error("Ran out of talk sequence stack space"); +} + +void TattooTalk::pullSequence(int slot) { + People &people = *_vm->_people; + + for (int idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) { + SequenceEntry &seq = _sequenceStack[idx]; + if (slot != -1 && idx != slot) + continue; + + // Check for an entry in this slot + if (seq._obj) { + Object &o = *seq._obj; + + // See if we're not supposed to restore it until an Allow Talk Interrupt + if (slot == -1 && seq._obj->hasAborts()) { + seq._obj->_gotoSeq = -1; + seq._obj->_restoreSlot = idx; + } else { + // Restore the object's sequence information immediately + o._frameNumber = seq._frameNumber; + o._sequenceNumber = seq._sequenceNumber; + o._seqStack = seq._seqStack; + o._seqTo = seq._seqTo; + o._seqCounter = seq._seqCounter; + o._seqCounter2 = seq._seqCounter2; + o._gotoSeq = 0; + o._talkSeq = 0; + + // Flag the slot as free again + seq._obj = nullptr; + } + } + } + + // Handle restoring any character positioning + for (int idx = 0; idx < MAX_CHARACTERS; ++idx) { + Person &person = people[idx]; + + if (person._type == CHARACTER && !person._walkSequences.empty() && person._sequenceNumber >= TALK_UPRIGHT + && person._sequenceNumber <= LISTEN_UPLEFT) { + person.gotoStand(); + + bool done = false; + do { + person.checkSprite(); + for (int frameNum = 0; frameNum < person._frameNumber; ++frameNum) { + if (person._walkSequences[person._sequenceNumber]._sequences[frameNum] == 0) + done = true; + } + } while (!done); + } + } +} + +bool TattooTalk::isSequencesEmpty() const { + for (int idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) { + if (_sequenceStack[idx]._obj) + return false; + } + + return true; +} + +void TattooTalk::clearSequences() { + for (int idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) { + _sequenceStack[idx]._obj = nullptr; + } +} + +} // End of namespace Tattoo + +} // End of namespace Sherlock |