/* 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. * */ #ifndef SHERLOCK_TALK_H #define SHERLOCK_TALK_H #include "common/scummsys.h" #include "common/array.h" #include "common/rect.h" #include "common/stream.h" #include "common/stack.h" #include "sherlock/objects.h" #include "sherlock/saveload.h" namespace Sherlock { #define SPEAKER_REMOVE 0x80 #define MAX_TALK_SEQUENCES 11 #define TALK_SEQUENCE_STACK_SIZE 20 enum { OP_SWITCH_SPEAKER = 0, OP_RUN_CANIMATION = 1, OP_ASSIGN_PORTRAIT_LOCATION = 2, OP_PAUSE = 3, OP_REMOVE_PORTRAIT = 4, OP_CLEAR_WINDOW = 5, OP_ADJUST_OBJ_SEQUENCE = 6, OP_WALK_TO_COORDS = 7, OP_PAUSE_WITHOUT_CONTROL = 8, OP_BANISH_WINDOW = 9, OP_SUMMON_WINDOW = 10, OP_SET_FLAG = 11, OP_SFX_COMMAND = 12, OP_TOGGLE_OBJECT = 13, OP_STEALTH_MODE_ACTIVE = 14, OP_IF_STATEMENT = 15, OP_ELSE_STATEMENT = 16, OP_END_IF_STATEMENT = 17, OP_STEALTH_MODE_DEACTIVATE = 18, OP_TURN_HOLMES_OFF = 19, OP_TURN_HOLMES_ON = 20, OP_GOTO_SCENE = 21, OP_PLAY_PROLOGUE = 22, OP_ADD_ITEM_TO_INVENTORY = 23, OP_SET_OBJECT = 24, OP_CALL_TALK_FILE = 25, OP_MOVE_MOUSE = 26, OP_DISPLAY_INFO_LINE = 27, OP_CLEAR_INFO_LINE = 28, OP_WALK_TO_CANIMATION = 29, OP_REMOVE_ITEM_FROM_INVENTORY = 30, OP_ENABLE_END_KEY = 31, OP_DISABLE_END_KEY = 32, OP_CARRIAGE_RETURN = 33, OP_MOUSE_OFF_ON = 34, OP_SET_WALK_CONTROL = 35, OP_SET_TALK_SEQUENCE = 36, OP_PLAY_SONG = 37, OP_WALK_HOLMES_AND_NPC_TO_CANIM = 38, OP_SET_NPC_PATH_DEST = 39, OP_NEXT_SONG = 40, OP_SET_NPC_PATH_PAUSE = 41, OP_NEED_PASSWORD = 42, OP_SET_SCENE_ENTRY_FLAG = 43, OP_WALK_NPC_TO_CANIM = 44, OP_WALK_NPC_TO_COORDS = 45, OP_WALK_HOLMES_AND_NPC_TO_COORDS = 46, OP_SET_NPC_TALK_FILE = 47, OP_TURN_NPC_OFF = 48, OP_TURN_NPC_ON = 49, OP_NPC_DESC_ON_OFF = 50, OP_NPC_PATH_PAUSE_TAKING_NOTES = 51, OP_NPC_PATH_PAUSE_LOOKING_HOLMES = 52, OP_ENABLE_TALK_INTERRUPTS = 53, OP_DISABLE_TALK_INTERRUPTS = 54, OP_SET_NPC_INFO_LINE = 55, OP_SET_NPC_POSITION = 56, OP_NPC_PATH_LABEL = 57, OP_PATH_GOTO_LABEL = 58, OP_PATH_IF_FLAG_GOTO_LABEL = 59, OP_NPC_WALK_GRAPHICS = 60, OP_NPC_VERB = 61, OP_NPC_VERB_CANIM = 62, OP_NPC_VERB_SCRIPT = 63, OP_RESTORE_PEOPLE_SEQUENCE = 64, OP_NPC_VERB_TARGET = 65, OP_TURN_SOUNDS_OFF = 66, OP_NULL = 67, OP_END_TEXT_WINDOW = 68 }; enum OpcodeReturn { RET_EXIT = -1, RET_SUCCESS = 0, RET_CONTINUE = 1 }; class SherlockEngine; class Talk; namespace Scalpel { class ScalpelUserInterface; } typedef OpcodeReturn(Talk::*OpcodeMethod)(const byte *&str); struct SequenceEntry { int _objNum; Common::Array _sequences; int _frameNumber; int _seqTo; SequenceEntry(); }; struct ScriptStackEntry { Common::String _name; int _currentIndex; int _select; }; struct Statement { Common::String _statement; Common::String _reply; Common::String _linkFile; Common::String _voiceFile; Common::Array _required; Common::Array _modified; int _portraitSide; int _quotient; int _talkMap; Common::Rect _talkPos; int _journal; /** * Load the data for a single statement within a talk file */ void load(Common::SeekableReadStream &s, bool isRoseTattoo); }; struct TalkHistoryEntry { bool _data[16]; TalkHistoryEntry(); bool &operator[](int index) { return _data[index]; } }; struct TalkSequence { Object *_obj; // Pointer to the bgshape that these values go to short _frameNumber; // Frame number in frame sequence to draw short _sequenceNumber; // Start frame of sequences that are repeated int _seqStack; // Allows gosubs to return to calling frame int _seqTo; // Allows 1-5, 8-3 type sequences encoded int _seqCounter; // How many times this sequence has been executed int _seqCounter2; TalkSequence(); }; class Talk { friend class Scalpel::ScalpelUserInterface; private: /** * Remove any voice commands from a loaded statement list */ void stripVoiceCommands(); /** * Form a table of the display indexes for statements */ void setTalkMap(); /** * Display a list of statements in a window at the bottom of the screen that the * player can select from. */ bool displayTalk(bool slamIt); /** * Prints a single conversation option in the interface window */ int talkLine(int lineNum, int stateNum, byte color, int lineY, bool slamIt); /** * Parses a reply for control codes and display text. The found text is printed within * the text window, handles delays, animations, and animating portraits. */ void doScript(const Common::String &script); /** * When the talk window has been displayed, waits a period of time proportional to * the amount of text that's been displayed */ int waitForMore(int delay); protected: SherlockEngine *_vm; OpcodeMethod *_opcodeTable; Common::Stack _savedSequences; Common::Stack _sequenceStack; Common::Stack _scriptStack; Common::Array _statements; Common::Array _talkHistory; int _speaker; int _talkIndex; int _scriptSelect; int _talkStealth; int _talkToFlag; int _scriptSaveIndex; // These fields are used solely by doScript, but are fields because all the script opcodes are // separate methods now, and need access to these fields int _yp; int _charCount; int _line; int _wait; bool _pauseFlag; bool _endStr, _noTextYet; int _seqCount; const byte *_scriptStart, *_scriptEnd; protected: Talk(SherlockEngine *vm); OpcodeReturn cmdAddItemToInventory(const byte *&str); OpcodeReturn cmdAdjustObjectSequence(const byte *&str); OpcodeReturn cmdBanishWindow(const byte *&str); OpcodeReturn cmdCallTalkFile(const byte *&str); OpcodeReturn cmdDisableEndKey(const byte *&str); OpcodeReturn cmdEnableEndKey(const byte *&str); OpcodeReturn cmdHolmesOff(const byte *&str); OpcodeReturn cmdHolmesOn(const byte *&str); OpcodeReturn cmdPause(const byte *&str); OpcodeReturn cmdPauseWithoutControl(const byte *&str); OpcodeReturn cmdRemoveItemFromInventory(const byte *&str); OpcodeReturn cmdRunCAnimation(const byte *&str); OpcodeReturn cmdSetFlag(const byte *&str); OpcodeReturn cmdSetObject(const byte *&str); OpcodeReturn cmdStealthModeActivate(const byte *&str); OpcodeReturn cmdStealthModeDeactivate(const byte *&str); OpcodeReturn cmdToggleObject(const byte *&str); OpcodeReturn cmdWalkToCAnimation(const byte *&str); protected: /** * Checks, if a character is an opcode */ virtual inline bool isOpcode(byte checkCharacter); /** * Display the talk interface window */ virtual void talkInterface(const byte *&str) = 0; /** * Pause when displaying a talk dialog on-screen */ virtual void talkWait(const byte *&str); /** * Trigger to play a 3DO talk dialog movie */ virtual void talk3DOMovieTrigger(int subIndex) {}; public: TalkSequence _talkSequenceStack[TALK_SEQUENCE_STACK_SIZE]; bool _talkToAbort; int _talkCounter; int _talkTo; int _scriptMoreFlag; bool _openTalkWindow; Common::String _scriptName; bool _moreTalkUp, _moreTalkDown; int _converseNum; const byte *_opcodes; public: static Talk *init(SherlockEngine *vm); virtual ~Talk() {} /** * Return a given talk statement */ Statement &operator[](int idx) { return _statements[idx]; } /** * Called whenever a conversation or item script needs to be run. For standard conversations, * it opens up a description window similar to how 'talk' does, but shows a 'reply' directly * instead of waiting for a statement option. * @remarks It seems that at some point, all item scripts were set up to use this as well. * In their case, the conversation display is simply suppressed, and control is passed on to * doScript to implement whatever action is required. */ void talkTo(const Common::String &filename); /** * Main method for handling conversations when a character to talk to has been * selected. It will make Holmes walk to the person to talk to, draws the * interface window for the conversation and passes on control to give the * player a list of options to make a selection from */ void talk(int objNum); /** * Clear loaded talk data */ void freeTalkVars(); /** * Draws the interface for conversation display */ void drawInterface(); /** * Opens the talk file 'talk.tlk' and searches the index for the specified * conversation. If found, the data for that conversation is loaded */ void loadTalkFile(const Common::String &filename); /** * Change the sequence of a background object corresponding to a given speaker. * The new sequence will display the character as "listening" */ void setStillSeq(int speaker); /** * Clears the stack of pending object sequences associated with speakers in the scene */ void clearSequences(); /** * Pulls a background object sequence from the sequence stack and restore's the * object's sequence */ void pullSequence(); /** * Push the sequence of a background object that's an NPC that needs to be * saved onto the sequence stack. */ void pushSequence(int speaker); /** * Push a given shape's sequence data onto the Rose Tattoo talk sequence stack */ void pushTalkSequence(Object *obj); /** * Returns true if the script stack is empty */ bool isSequencesEmpty() const { return _scriptStack.empty(); } /** * Pops an entry off of the script stack */ void popStack(); /** * Synchronize the data for a savegame */ void synchronize(Serializer &s); }; } // End of namespace Sherlock #endif