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 MADS_CONVERSATIONS_H
24 #define MADS_CONVERSATIONS_H
25 
26 #include "common/scummsys.h"
27 #include "common/array.h"
28 #include "common/str-array.h"
29 #include "mads/screen.h"
30 #include "mads/dialogs.h"
31 
32 namespace MADS {
33 
34 #define MAX_CONVERSATIONS 5
35 #define MAX_SPEAKERS 5
36 
37 enum ConversationMode {
38 	CONVMODE_NONE = -1,
39 	CONVMODE_0 = 0,
40 	CONVMODE_1 = 1,
41 	CONVMODE_2 = 2,
42 	CONVMODE_3 = 3,
43 	CONVMODE_4 = 4,
44 	CONVMODE_5 = 5,
45 	CONVMODE_6 = 6,
46 	CONVMODE_7 = 7,
47 	CONVMODE_8 = 8,
48 	CONVMODE_9 = 9,
49 	CONVMODE_STOP = 10
50 };
51 
52 enum DialogCommand {
53 	CMD_END = 0,
54 	CMD_1 = 1,
55 	CMD_HIDE = 2,
56 	CMD_UNHIDE = 3,
57 	CMD_MESSAGE1 = 4,
58 	CMD_MESSAGE2 = 5,
59 	CMD_ERROR = 6,
60 	CMD_NODE = 7,
61 	CMD_GOTO = 8,
62 	CMD_ASSIGN = 9,
63 	CMD_DIALOG_END = 255
64 };
65 
66 enum ConvEntryFlag {
67 	ENTRYFLAG_2 = 2,
68 	ENTRYFLAG_4000 = 0x4000,
69 	ENTRYFLAG_8000 = 0x8000
70 };
71 
72 enum ConditionalOperation {
73 	CONDOP_NONE = 0xff,
74 	CONDOP_VALUE = 0,
75 	CONDOP_ADD = 1,
76 	CONDOP_SUBTRACT = 2,
77 	CONDOP_MULTIPLY = 3,
78 	CONDOP_DIVIDE = 4,
79 	CONDOP_MODULUS = 5,
80 	CONDOP_LTEQ = 6,
81 	CONDOP_GTEQ = 7,
82 	CONDOP_LT = 8,
83 	CONDOP_GT = 9,
84 	CONDOP_NEQ = 10,
85 	CONDOP_EQ = 11,
86 	CONDOP_AND = 12,
87 	CONDOP_OR = 13,
88 	CONDOP_ABORT = 0xff
89 };
90 
91 
92 struct ConversationVar {
93 	bool _isPtr;
94 	int _val;
95 	int *_valPtr;
96 
97 	/**
98 	 * Constructor
99 	 */
ConversationVarConversationVar100 	ConversationVar() : _isPtr(false), _val(0), _valPtr(nullptr) {}
101 
102 	/**
103 	 * Sets a numeric value
104 	 */
105 	void setValue(int val);
106 
107 	/**
108 	 * Sets a pointer value
109 	 */
110 	void setValue(int *val);
111 
112 	/**
113 	 * Return either the variable's pointer, or a pointer to it's direct value
114 	 */
getValueConversationVar115 	int *getValue() { return _isPtr ? _valPtr : &_val; }
116 
117 	/**
118 	 * Returns true if variable is a pointer
119 	 */
isPtrConversationVar120 	bool isPtr() const { return _isPtr; }
121 
122 	/**
123 	 * Returns true if variable is numeric
124 	 */
isNumericConversationVar125 	bool isNumeric() const { return !_isPtr; }
126 };
127 
128 struct ScriptEntry {
129 	struct Conditional {
130 		struct CondtionalParamEntry {
131 			bool _isVariable;
132 			int _val;
133 
134 			/**
135 			 * Constructor
136 			 */
CondtionalParamEntryScriptEntry::Conditional::CondtionalParamEntry137 			CondtionalParamEntry() : _isVariable(false), _val(0) {}
138 		};
139 
140 		static Common::Array<ConversationVar> *_vars;
141 		ConditionalOperation _operation;
142 		CondtionalParamEntry _param1;
143 		CondtionalParamEntry _param2;
144 
145 		/**
146 		 * Constructor
147 		 */
ConditionalScriptEntry::Conditional148 		Conditional() : _operation(CONDOP_NONE) {}
149 
150 		/**
151 		 * Loads data from a passed stream into the parameters structure
152 		 */
153 		void load(Common::SeekableReadStream &s);
154 
155 		/**
156 		 * Gets the value
157 		 */
158 		int get(int paramNum) const;
159 
160 		/**
161 		 * Evaluates the conditional
162 		 */
163 		int evaluate() const;
164 	};
165 
166 	struct MessageEntry {
167 		int _size;
168 		int _v2;
169 
MessageEntryScriptEntry::MessageEntry170 		MessageEntry() : _size(0), _v2(0) {}
171 	};
172 
173 	DialogCommand _command;
174 	Conditional _conditionals[3];
175 
176 	// Extra parameters for different opcodes
177 	int _index;
178 	Common::Array<int> _entries;
179 	Common::Array<MessageEntry> _entries2;
180 
181 	/**
182 	 * Constructor
183 	 */
ScriptEntryScriptEntry184 	ScriptEntry() : _command(CMD_END), _index(0) {}
185 
186 	/**
187 	 * Loads data from a passed stream into the parameters structure
188 	 */
189 	void load(Common::SeekableReadStream &s);
190 };
191 
192 /**
193  * Representation of scripts associated with a dialog
194  */
195 class DialogScript : public Common::Array<ScriptEntry> {
196 public:
197 	/**
198 	 * Loads a script from the passed stream
199 	 */
200 	void load(Common::SeekableReadStream &s, uint startingOffset);
201 };
202 
203 /**
204  * Reperesents the data for a dialog to be displayed in a conversation
205  */
206 struct ConvDialog {
207 	struct ScriptEntry {
208 		DialogCommand _command;
209 	};
210 
211 	int16 _textLineIndex;	// 0-based
212 	int16 _speechIndex;		// 1-based
213 	uint16 _scriptOffset;	// offset of script entry
214 	uint16 _scriptSize;		// size of script entry
215 
216 	DialogScript _script;
217 };
218 
219 /**
220  * Represents a node within the conversation control logic
221  */
222 struct ConvNode {
223 	uint16 _index;
224 	uint16 _dialogCount;
225 	int16 _unk1;
226 	bool _active;
227 	int16 _unk3;
228 
229 	Common::Array<ConvDialog> _dialogs;
230 };
231 
232 /**
233  * Represents a message entry
234  */
235 struct ConvMessage {
236 	uint _stringIndex;
237 	uint _count;
238 
ConvMessageConvMessage239 	ConvMessage() : _stringIndex(0), _count(0) {}
240 };
241 
242 /**
243  * Represents the static, non-changing data for a conversation
244  */
245 struct ConversationData {
246 	uint16 _nodeCount;		// conversation nodes, each one containing several dialog options and messages
247 	uint16 _dialogCount;		// messages (non-selectable) + texts (selectable)
248 	uint16 _messageCount;	// messages (non-selectable)
249 	uint16 _textLineCount;
250 	uint16 _unk2;
251 	uint16 _maxImports;
252 	uint16 _speakerCount;
253 	int _textSize;
254 	int _commandsSize;
255 
256 	Common::String _portraits[MAX_SPEAKERS];
257 	int _speakerFrame[MAX_SPEAKERS];
258 	Common::String _speechFile;
259 	Common::Array<ConvMessage> _messages;
260 	Common::StringArray _textLines;
261 	Common::Array<ConvNode> _nodes;
262 	Common::Array<ConvDialog> _dialogs;
263 
264 	/**
265 	 * Load the specified conversation resource file
266 	 */
267 	void load(const Common::String &filename);
268 };
269 
270 /**
271  * Conditional (i.e. changeable) data for the conversation
272  */
273 struct ConversationConditionals {
274 	Common::Array<uint> _importVariables;
275 	Common::Array<uint> _entryFlags;
276 	Common::Array<ConversationVar> _vars;
277 	int _numImports;
278 
279 	int _currentNode;
280 	Common::Array<int> _messageList1;
281 	Common::Array<int> _messageList2;
282 	Common::Array<int> _messageList3;
283 	Common::Array<int> _messageList4;
284 
285 	/**
286 	 * Constructor
287 	 */
288 	ConversationConditionals();
289 
290 	/**
291 	 * Load the specified conversation conditionals resource file
292 	 */
293 	void load(const Common::String &filename);
294 };
295 
296 /**
297  * Represents all the data needed for a particular loaded conversation
298  */
299 struct ConversationEntry {
300 	int _convId;
301 	ConversationData _data;
302 	ConversationConditionals _cnd;
303 };
304 
305 class MADSEngine;
306 
307 /**
308  * Manager for loading and running conversations
309  */
310 class GameConversations {
311 private:
312 	MADSEngine *_vm;
313 	ConversationEntry _conversations[MAX_CONVERSATIONS];
314 	bool _speakerActive[MAX_SPEAKERS];
315 	int _speakerSeries[MAX_SPEAKERS];
316 	int _speakerFrame[MAX_SPEAKERS];
317 	int _popupX[MAX_SPEAKERS];
318 	int _popupY[MAX_SPEAKERS];
319 	int _popupMaxLen[MAX_SPEAKERS];
320 	InputMode _inputMode;
321 	bool _popupVisible;
322 	ConversationMode _currentMode;
323 	ConversationMode _priorMode;
324 	int _verbId;
325 	int _speakerVal;
326 	int _heroTrigger;
327 	TriggerMode _heroTriggerMode;
328 	int _interlocutorTrigger;
329 	TriggerMode _interlocutorTriggerMode;
330 	ConversationEntry *_runningConv;
331 	int _restoreRunning;
332 	bool _playerEnabled;
333 	uint32 _startFrameNumber;
334 	ConversationVar *_vars;
335 	ConversationVar *_nextStartNode;
336 	int _currentNode;
337 	int _dialogNodeOffset, _dialogNodeSize;
338 	int _personSpeaking;
339 	TextDialog *_dialog;
340 	bool _dialogAltFlag;
341 
342 	/**
343 	 * Returns the record for the specified conversation, if it's loaded
344 	 */
345 	ConversationEntry *getConv(int convId);
346 
347 	/**
348 	 * Start a specified conversation slot
349 	 */
350 	void start();
351 
352 	/**
353 	 * Remove any currently active dialog window
354 	 */
355 	void removeActiveWindow();
356 
357 	/**
358 	 * Flags a conversation option/entry
359 	 */
360 	void flagEntry(DialogCommand mode, int entryIndex);
361 
362 	/**
363 	 * Generate a menu
364 	 */
365 	ConversationMode generateMenu();
366 
367 	/**
368 	 * Generate text
369 	 */
370 	void generateText(int textLineIndex, Common::Array<int> &messages);
371 
372 	/**
373 	 * Generate message
374 	 */
375 	void generateMessage(Common::Array<int> &messageList, Common::Array<int> &voiecList);
376 
377 	/**
378 	 * Gets the next node
379 	 */
380 	bool nextNode();
381 
382 	/**
383 	 * Executes a conversation entry
384 	 */
385 	int executeEntry(int index);
386 
387 	/**
388 	 * Handle messages
389 	 */
390 	void scriptMessage(ScriptEntry &scrEntry);
391 
392 	/**
393 	 * Handle node changes
394 	 */
395 	bool scriptNode(ScriptEntry &scrEntry);
396 public:
397 	/**
398 	 * Constructor
399 	 */
400 	GameConversations(MADSEngine *vm);
401 
402 	/**
403 	 * Destructor
404 	 */
405 	virtual ~GameConversations();
406 
407 	/**
408 	 * Gets the specified conversation and loads into into a free slot
409 	 * in the conversation list
410 	 */
411 	void load(int id);
412 
413 	/**
414 	 * Run a specified conversation number. The conversation must have
415 	 * previously been loaded by calling the load method
416 	 */
417 	void run(int id);
418 
419 	/**
420 	 * Sets a variable to a numeric value
421 	 */
422 	void setVariable(uint idx, int val);
423 
424 	/**
425 	 * Sets a variable to a pointer value
426 	 */
427 	void setVariable(uint idx, int *val);
428 
429 	/**
430 	 * Sets the starting node index
431 	 */
432 	void setStartNode(uint nodeIndex);
433 
434 	/**
435 	 * Set the hero trigger
436 	 */
437 	void setHeroTrigger(int val);
438 
439 	/**
440 	 * Set the interlocutor trigger
441 	 */
442 	void setInterlocutorTrigger(int val);
443 
444 	/**
445 	 * Returns either the pointer value of a variable, or if the variable
446 	 * contains a numeric value directly, returns a pointer to it
447 	 */
448 	int *getVariable(int idx);
449 
450 	/**
451 	 * Hold the current mode value
452 	 */
453 	void hold();
454 
455 	/**
456 	 * Release the prevoiusly held mode value
457 	 */
458 	void release();
459 
460 	/**
461 	 * Stop any currently running conversation
462 	 */
463 	void stop();
464 
465 	/**
466 	 * Adds the passed pointer into the list of import variables for the given conversation
467 	 */
468 	void exportPointer(int *ptr);
469 
470 	/**
471 	 * Adds the passed value into the list of import variables for the given conversation
472 	 */
473 	void exportValue(int val);
474 
475 	void reset(int id);
476 
477 	/**
478 	 * Handles updating the conversation display
479 	 */
480 	void update(bool flag);
481 
482 	/**
483 	 * Returns true if any conversation is currently atcive
484 	 */
active()485 	bool active() const { return _runningConv != nullptr; }
486 
487 	/**
488 	 * Returns the currently active conversation Id
489 	 */
activeConvId()490 	int activeConvId() const { return !active() ? -1 : _runningConv->_convId; }
491 
492 	/**
493 	 * Returns _restoreRunning value
494 	 */
restoreRunning()495 	int restoreRunning() const { return _restoreRunning; }
496 
497 	/**
498 	 * Returns the current conversation mode
499 	 */
currentMode()500 	ConversationMode currentMode() const { return _currentMode; }
501 };
502 
503 } // End of namespace MADS
504 
505 #endif /* MADS_CONVERSATIONS_H */
506