1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef SHERLOCK_TALK_H
24 #define SHERLOCK_TALK_H
25 
26 #include "common/scummsys.h"
27 #include "common/array.h"
28 #include "common/rect.h"
29 #include "common/stream.h"
30 #include "common/stack.h"
31 #include "sherlock/objects.h"
32 #include "sherlock/saveload.h"
33 
34 namespace Sherlock {
35 
36 #define SPEAKER_REMOVE 0x80
37 #define MAX_TALK_SEQUENCES 11
38 
39 enum {
40 	OP_SWITCH_SPEAKER			= 0,
41 	OP_RUN_CANIMATION			= 1,
42 	OP_ASSIGN_PORTRAIT_LOCATION = 2,
43 	OP_PAUSE					= 3,
44 	OP_REMOVE_PORTRAIT			= 4,
45 	OP_CLEAR_WINDOW				= 5,
46 	OP_ADJUST_OBJ_SEQUENCE		= 6,
47 	OP_WALK_TO_COORDS			= 7,
48 	OP_PAUSE_WITHOUT_CONTROL	= 8,
49 	OP_BANISH_WINDOW			= 9,
50 	OP_SUMMON_WINDOW			= 10,
51 	OP_SET_FLAG					= 11,
52 	OP_SFX_COMMAND				= 12,
53 	OP_TOGGLE_OBJECT			= 13,
54 	OP_STEALTH_MODE_ACTIVE		= 14,
55 	OP_IF_STATEMENT				= 15,
56 	OP_ELSE_STATEMENT			= 16,
57 	OP_END_IF_STATEMENT			= 17,
58 	OP_STEALTH_MODE_DEACTIVATE	= 18,
59 	OP_TURN_HOLMES_OFF			= 19,
60 	OP_TURN_HOLMES_ON			= 20,
61 	OP_GOTO_SCENE				= 21,
62 	OP_PLAY_PROLOGUE			= 22,
63 	OP_ADD_ITEM_TO_INVENTORY	= 23,
64 	OP_SET_OBJECT				= 24,
65 	OP_CALL_TALK_FILE			= 25,
66 	OP_MOVE_MOUSE				= 26,
67 	OP_DISPLAY_INFO_LINE		= 27,
68 	OP_CLEAR_INFO_LINE			= 28,
69 	OP_WALK_TO_CANIMATION		= 29,
70 	OP_REMOVE_ITEM_FROM_INVENTORY = 30,
71 	OP_ENABLE_END_KEY			= 31,
72 	OP_DISABLE_END_KEY			= 32,
73 	OP_END_TEXT_WINDOW			= 33,
74 
75 	OP_MOUSE_OFF_ON				= 34,
76 	OP_SET_WALK_CONTROL			= 35,
77 	OP_SET_TALK_SEQUENCE		= 36,
78 	OP_PLAY_SONG				= 37,
79 	OP_WALK_HOLMES_AND_NPC_TO_CANIM = 38,
80 	OP_SET_NPC_PATH_DEST		= 39,
81 	OP_NEXT_SONG				= 40,
82 	OP_SET_NPC_PATH_PAUSE		= 41,
83 	OP_NEED_PASSWORD			= 42,
84 	OP_SET_SCENE_ENTRY_FLAG		= 43,
85 	OP_WALK_NPC_TO_CANIM		= 44,
86 	OP_WALK_NPC_TO_COORDS		= 45,
87 	OP_WALK_HOLMES_AND_NPC_TO_COORDS = 46,
88 	OP_SET_NPC_TALK_FILE		= 47,
89 	OP_TURN_NPC_OFF				= 48,
90 	OP_TURN_NPC_ON				= 49,
91 	OP_NPC_DESC_ON_OFF			= 50,
92 	OP_NPC_PATH_PAUSE_TAKING_NOTES	= 51,
93 	OP_NPC_PATH_PAUSE_LOOKING_HOLMES = 52,
94 	OP_ENABLE_TALK_INTERRUPTS	= 53,
95 	OP_DISABLE_TALK_INTERRUPTS	= 54,
96 	OP_SET_NPC_INFO_LINE		= 55,
97 	OP_SET_NPC_POSITION			= 56,
98 	OP_NPC_PATH_LABEL			= 57,
99 	OP_PATH_GOTO_LABEL			= 58,
100 	OP_PATH_IF_FLAG_GOTO_LABEL	= 59,
101 	OP_NPC_WALK_GRAPHICS		= 60,
102 	OP_NPC_VERB					= 61,
103 	OP_NPC_VERB_CANIM			= 62,
104 	OP_NPC_VERB_SCRIPT			= 63,
105 	OP_RESTORE_PEOPLE_SEQUENCE	= 64,
106 	OP_NPC_VERB_TARGET			= 65,
107 	OP_TURN_SOUNDS_OFF			= 66,
108 	OP_NULL						= 67
109 };
110 
111 enum OpcodeReturn { RET_EXIT = -1, RET_SUCCESS = 0, RET_CONTINUE = 1 };
112 
113 class SherlockEngine;
114 class Talk;
115 namespace Scalpel { class ScalpelUserInterface; }
116 
117 typedef OpcodeReturn(Talk::*OpcodeMethod)(const byte *&str);
118 
119 struct SequenceEntry {
120 	int _objNum;
121 	Common::Array<byte> _sequences;
122 	Object *_obj;			// Pointer to the bgshape that these values go to
123 	short _frameNumber;		// Frame number in frame sequence to draw
124 	short _sequenceNumber;	// Start frame of sequences that are repeated
125 	int _seqStack;			// Allows gosubs to return to calling frame
126 	int _seqTo;				// Allows 1-5, 8-3 type sequences encoded
127 	int _seqCounter;		// How many times this sequence has been executed
128 	int _seqCounter2;
129 
130 	SequenceEntry();
131 };
132 
133 struct ScriptStackEntry {
134 	Common::String _name;
135 	int _currentIndex;
136 	int _select;
137 };
138 
139 struct Statement {
140 	Common::String _statement;
141 	Common::String _reply;
142 	Common::String _linkFile;
143 	Common::String _voiceFile;
144 	Common::Array<int> _required;
145 	Common::Array<int> _modified;
146 	int _portraitSide;
147 	int _quotient;
148 	int _talkMap;
149 	Common::Rect _talkPos;
150 	int _journal;
151 
152 	/**
153 	 * Load the data for a single statement within a talk file
154 	 */
155 	void load(Common::SeekableReadStream &s, bool isRoseTattoo);
156 };
157 
158 struct TalkHistoryEntry {
159 	bool _data[16];
160 
161 	TalkHistoryEntry();
162 	bool &operator[](int index) { return _data[index]; }
163 };
164 
165 class Talk {
166 	friend class Scalpel::ScalpelUserInterface;
167 private:
168 	/**
169 	 * Remove any voice commands from a loaded statement list
170 	 */
171 	void stripVoiceCommands();
172 protected:
173 	SherlockEngine *_vm;
174 	OpcodeMethod *_opcodeTable;
175 	Common::Stack<SequenceEntry> _savedSequences;
176 	Common::Stack<ScriptStackEntry> _scriptStack;
177 	Common::Array<TalkHistoryEntry> _talkHistory;
178 	int _talkIndex;
179 	int _scriptSelect;
180 	int _talkStealth;
181 	int _talkToFlag;
182 	int _scriptSaveIndex;
183 	int _3doSpeechIndex;
184 
185 	// These fields are used solely by doScript, but are fields because all the script opcodes are
186 	// separate methods now, and need access to these fields
187 	int _yp;
188 	int _charCount;
189 	int _line;
190 	int _wait;
191 	bool _pauseFlag;
192 	bool _endStr, _noTextYet;
193 	int _seqCount;
194 	const byte *_scriptStart, *_scriptEnd;
195 protected:
196 	Talk(SherlockEngine *vm);
197 
198 	OpcodeReturn cmdAddItemToInventory(const byte *&str);
199 	OpcodeReturn cmdAdjustObjectSequence(const byte *&str);
200 	OpcodeReturn cmdBanishWindow(const byte *&str);
201 	OpcodeReturn cmdDisableEndKey(const byte *&str);
202 	OpcodeReturn cmdEnableEndKey(const byte *&str);
203 	OpcodeReturn cmdEndTextWindow(const byte *&str);
204 	OpcodeReturn cmdHolmesOff(const byte *&str);
205 	OpcodeReturn cmdHolmesOn(const byte *&str);
206 	OpcodeReturn cmdPause(const byte *&str);
207 	OpcodeReturn cmdPauseWithoutControl(const byte *&str);
208 	OpcodeReturn cmdRemoveItemFromInventory(const byte *&str);
209 	OpcodeReturn cmdRunCAnimation(const byte *&str);
210 	OpcodeReturn cmdSetFlag(const byte *&str);
211 	OpcodeReturn cmdSetObject(const byte *&str);
212 	OpcodeReturn cmdStealthModeActivate(const byte *&str);
213 	OpcodeReturn cmdStealthModeDeactivate(const byte *&str);
214 	OpcodeReturn cmdToggleObject(const byte *&str);
215 	OpcodeReturn cmdWalkToCAnimation(const byte *&str);
216 protected:
217 	/**
218 	 * Checks if a character is an opcode
219 	 */
220 	bool isOpcode(byte checkCharacter);
221 
222 	/**
223 	 * Form a table of the display indexes for statements
224 	 */
225 	void setTalkMap();
226 
227 	/**
228 	 * When the talk window has been displayed, waits a period of time proportional to
229 	 * the amount of text that's been displayed
230 	 */
231 	virtual int waitForMore(int delay);
232 
233 	/**
234 	 * Display the talk interface window
235 	 */
236 	virtual void talkInterface(const byte *&str) = 0;
237 
238 	/**
239 	 * Pause when displaying a talk dialog on-screen
240 	 */
241 	virtual void talkWait(const byte *&str);
242 
243 	/**
244 	 * Show the talk display
245 	 */
246 	virtual void showTalk() = 0;
247 
248 	/**
249 	 * Called when a character being spoken to has no talk options to display
250 	 */
251 	virtual void nothingToSay() = 0;
252 
253 	/**
254 	 * Called when the active speaker is switched
255 	 */
switchSpeaker()256 	virtual void switchSpeaker() {}
257 public:
258 	Common::Array<Statement> _statements;
259 	bool _talkToAbort;
260 	int _talkCounter;
261 	int _talkTo;
262 	int _scriptMoreFlag;
263 	bool _openTalkWindow;
264 	Common::String _scriptName;
265 	bool _moreTalkUp, _moreTalkDown;
266 	int _converseNum;
267 	const byte *_opcodes;
268 	int _speaker;
269 public:
270 	static Talk *init(SherlockEngine *vm);
~Talk()271 	virtual ~Talk() {}
272 
273 	/**
274 	 * Return a given talk statement
275 	 */
276 	Statement &operator[](int idx) { return _statements[idx]; }
277 
278 	/**
279 	 * Called whenever a conversation or item script needs to be run. For standard conversations,
280 	 * it opens up a description window similar to how 'talk' does, but shows a 'reply' directly
281 	 * instead of waiting for a statement option.
282 	 * @remarks		It seems that at some point, all item scripts were set up to use this as well.
283 	 *	In their case, the conversation display is simply suppressed, and control is passed on to
284 	 *	doScript to implement whatever action is required.
285 	 */
286 	virtual void talkTo(const Common::String filename);
287 
288 	/**
289 	 * Parses a reply for control codes and display text. The found text is printed within
290 	 * the text window, handles delays, animations, and animating portraits.
291 	 */
292 	void doScript(const Common::String &script);
293 
294 	/**
295 	 * Main method for handling conversations when a character to talk to has been
296 	 * selected. It will make Holmes walk to the person to talk to, draws the
297 	 * interface window for the conversation and passes on control to give the
298 	 * player a list of options to make a selection from
299 	 */
300 	void initTalk(int objNum);
301 
302 	/**
303 	 * Clear loaded talk data
304 	 */
305 	void freeTalkVars();
306 
307 	/**
308 	 * Opens the talk file 'talk.tlk' and searches the index for the specified
309 	 * conversation. If found, the data for that conversation is loaded
310 	 */
311 	virtual void loadTalkFile(const Common::String &filename);
312 
313 	/**
314 	 * Push the sequence of a background object that's an NPC that needs to be
315 	 * saved onto the sequence stack.
316 	 */
317 	void pushSequence(int speaker);
318 
319 	/**
320 	 * Push the details of a passed object onto the saved sequences stack
321 	 */
322 	virtual void pushSequenceEntry(Object *obj) = 0;
323 
324 	/**
325 	 * Clears the stack of pending object sequences associated with speakers in the scene
326 	 */
327 	virtual void clearSequences() = 0;
328 
329 	/**
330 	 * Pops an entry off of the script stack
331 	 */
332 	void popStack();
333 
334 	/**
335 	 * Synchronize the data for a savegame
336 	 */
337 	void synchronize(Serializer &s);
338 
339 	/**
340 	 * Draws the interface for conversation display
341 	 */
drawInterface()342 	virtual void drawInterface() {}
343 
344 	/**
345 	 * Display a list of statements in a window at the bottom of the screen that the
346 	 * player can select from.
347 	 */
displayTalk(bool slamIt)348 	virtual bool displayTalk(bool slamIt) { return false; }
349 
350 	/**
351 	 * Prints a single conversation option in the interface window
352 	 */
talkLine(int lineNum,int stateNum,byte color,int lineY,bool slamIt)353 	virtual int talkLine(int lineNum, int stateNum, byte color, int lineY, bool slamIt) { return 0; }
354 
355 	/**
356 	 * Pulls a background object sequence from the sequence stack and restore's the
357 	 * object's sequence
358 	 */
359 	virtual void pullSequence(int slot = -1) = 0;
360 
361 	/**
362 	 * Returns true if the script stack is empty
363 	 */
364 	virtual bool isSequencesEmpty() const = 0;
365 };
366 
367 } // End of namespace Sherlock
368 
369 #endif
370