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 #include "mohawk/cursors.h"
24 #include "mohawk/riven.h"
25 #include "mohawk/riven_card.h"
26 #include "mohawk/riven_graphics.h"
27 #include "mohawk/riven_scripts.h"
28 #include "mohawk/riven_sound.h"
29 #include "mohawk/riven_stack.h"
30 #include "mohawk/riven_stacks/aspit.h"
31 #include "mohawk/riven_video.h"
32 #include "common/memstream.h"
33
34 #include "common/debug-channels.h"
35 #include "common/stream.h"
36 #include "common/system.h"
37
38 namespace Mohawk {
39
printTabs(byte tabs)40 static void printTabs(byte tabs) {
41 for (byte i = 0; i < tabs; i++)
42 debugN("\t");
43 }
44
RivenScriptManager(MohawkEngine_Riven * vm)45 RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) :
46 _vm(vm),
47 _runningQueuedScripts(false),
48 _stoppingAllScripts(false) {
49
50 _storedMovieOpcode.time = 0;
51 _storedMovieOpcode.slot = 0;
52 }
53
~RivenScriptManager()54 RivenScriptManager::~RivenScriptManager() {
55 clearStoredMovieOpcode();
56 }
57
readScript(Common::ReadStream * stream)58 RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream) {
59 RivenScriptPtr script = RivenScriptPtr(new RivenScript());
60
61 uint16 commandCount = stream->readUint16BE();
62
63 for (uint16 i = 0; i < commandCount; i++) {
64 RivenCommandPtr command = readCommand(stream);
65 script->addCommand(command);
66 }
67
68 return script;
69 }
70
readCommand(Common::ReadStream * stream)71 RivenCommandPtr RivenScriptManager::readCommand(Common::ReadStream *stream) {
72 RivenCommandType type = (RivenCommandType) stream->readUint16BE();
73
74 switch (type) {
75 case kRivenCommandSwitch:
76 return RivenCommandPtr(RivenSwitchCommand::createFromStream(_vm, stream));
77 case kRivenCommandChangeStack:
78 return RivenCommandPtr(RivenStackChangeCommand::createFromStream(_vm, stream));
79 default:
80 return RivenCommandPtr(RivenSimpleCommand::createFromStream(_vm, type, stream));
81 }
82 }
83
readScripts(Common::ReadStream * stream)84 RivenScriptList RivenScriptManager::readScripts(Common::ReadStream *stream) {
85 RivenScriptList scriptList;
86
87 uint16 scriptCount = stream->readUint16BE();
88 for (uint16 i = 0; i < scriptCount; i++) {
89 RivenTypedScript script;
90 script.type = stream->readUint16BE();
91 script.script = readScript(stream);
92 scriptList.push_back(script);
93 }
94
95 return scriptList;
96 }
97
stopAllScripts()98 void RivenScriptManager::stopAllScripts() {
99 _stoppingAllScripts = true;
100 }
101
setStoredMovieOpcode(const StoredMovieOpcode & op)102 void RivenScriptManager::setStoredMovieOpcode(const StoredMovieOpcode &op) {
103 clearStoredMovieOpcode();
104 _storedMovieOpcode.script = op.script;
105 _storedMovieOpcode.slot = op.slot;
106 _storedMovieOpcode.time = op.time;
107 }
108
runStoredMovieOpcode()109 void RivenScriptManager::runStoredMovieOpcode() {
110 if (_storedMovieOpcode.script) {
111 runScript(_storedMovieOpcode.script, false);
112 clearStoredMovieOpcode();
113 }
114 }
115
clearStoredMovieOpcode()116 void RivenScriptManager::clearStoredMovieOpcode() {
117 _storedMovieOpcode.script = RivenScriptPtr();
118 _storedMovieOpcode.time = 0;
119 _storedMovieOpcode.slot = 0;
120 }
121
runScript(const RivenScriptPtr & script,bool queue)122 void RivenScriptManager::runScript(const RivenScriptPtr &script, bool queue) {
123 if (!script || script->empty()) {
124 return;
125 }
126
127 if (!queue) {
128 script->run(this);
129 } else {
130 _queue.push_back(script);
131 }
132 }
133
hasQueuedScripts() const134 bool RivenScriptManager::hasQueuedScripts() const {
135 return !_queue.empty();
136 }
137
runQueuedScripts()138 void RivenScriptManager::runQueuedScripts() {
139 _runningQueuedScripts = true;
140
141 for (uint i = 0; i < _queue.size(); i++) {
142 _queue[i]->run(this);
143 }
144
145 _queue.clear();
146
147 _stoppingAllScripts = false; // Once the queue is empty, all scripts have been stopped
148 _runningQueuedScripts = false;
149 }
150
createScriptFromData(uint commandCount,...)151 RivenScriptPtr RivenScriptManager::createScriptFromData(uint commandCount, ...) {
152 va_list args;
153 va_start(args, commandCount);
154
155 // Build a script from the variadic arguments
156 Common::MemoryWriteStreamDynamic writeStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
157 writeStream.writeUint16BE((uint16)commandCount);
158
159 for (uint i = 0; i < commandCount; i++) {
160 uint16 command = va_arg(args, int);
161 writeStream.writeUint16BE(command);
162
163 if (command == kRivenCommandSwitch) {
164 // The switch command has a different format that is not implemented
165 error("Cannot create a Switch command from data");
166 }
167
168 uint16 argumentCount = va_arg(args, int);
169 writeStream.writeUint16BE(argumentCount);
170
171 for (uint j = 0; j < commandCount; j++) {
172 uint16 argument = va_arg(args, int);
173 writeStream.writeUint16BE(argument);
174 }
175 }
176
177 va_end(args);
178
179 Common::MemoryReadStream readStream = Common::MemoryReadStream(writeStream.getData(), writeStream.size());
180 return readScript(&readStream);
181 }
182
readScriptFromData(uint16 * data,uint16 size)183 RivenScriptPtr RivenScriptManager::readScriptFromData(uint16 *data, uint16 size) {
184 // Script data is expected to be in big endian
185 for (uint i = 0; i < size; i++) {
186 data[i] = TO_BE_16(data[i]);
187 }
188
189 Common::MemoryReadStream patchStream((const byte *)(data), size * sizeof(uint16));
190 return _vm->_scriptMan->readScript(&patchStream);
191 }
192
createScriptWithCommand(RivenCommand * command)193 RivenScriptPtr RivenScriptManager::createScriptWithCommand(RivenCommand *command) {
194 assert(command);
195
196 RivenScriptPtr script = RivenScriptPtr(new RivenScript());
197 script->addCommand(RivenCommandPtr(command));
198 return script;
199 }
200
runningQueuedScripts() const201 bool RivenScriptManager::runningQueuedScripts() const {
202 return _runningQueuedScripts;
203 }
204
stoppingAllScripts() const205 bool RivenScriptManager::stoppingAllScripts() const {
206 return _stoppingAllScripts;
207 }
208
RivenScript()209 RivenScript::RivenScript() {
210 }
211
~RivenScript()212 RivenScript::~RivenScript() {
213 }
214
dumpScript(byte tabs)215 void RivenScript::dumpScript(byte tabs) {
216 for (uint16 i = 0; i < _commands.size(); i++) {
217 _commands[i]->dump(tabs);
218 }
219 }
220
run(RivenScriptManager * scriptManager)221 void RivenScript::run(RivenScriptManager *scriptManager) {
222 for (uint i = 0; i < _commands.size(); i++) {
223 if (scriptManager->stoppingAllScripts()) {
224 return;
225 }
226
227 _commands[i]->execute();
228 }
229 }
230
addCommand(RivenCommandPtr command)231 void RivenScript::addCommand(RivenCommandPtr command) {
232 _commands.push_back(command);
233 }
234
empty() const235 bool RivenScript::empty() const {
236 return _commands.empty();
237 }
238
operator +=(const RivenScript & other)239 RivenScript &RivenScript::operator+=(const RivenScript &other) {
240 _commands.push_back(other._commands);
241 return *this;
242 }
243
getTypeName(uint16 type)244 const char *RivenScript::getTypeName(uint16 type) {
245 static const char *names[] = {
246 "MouseDown",
247 "MouseDrag",
248 "MouseUp",
249 "MouseEnter",
250 "MouseInside",
251 "MouseLeave",
252 "CardLoad",
253 "CardLeave",
254 "CardUnknown",
255 "CardEnter",
256 "CardUpdate"
257 };
258
259 assert(type < ARRAYSIZE(names));
260 return names[type];
261 }
262
applyCardPatches(MohawkEngine_Riven * vm,uint32 cardGlobalId,uint16 scriptType,uint16 hotspotId)263 void RivenScript::applyCardPatches(MohawkEngine_Riven *vm, uint32 cardGlobalId, uint16 scriptType, uint16 hotspotId) {
264 bool shouldApplyPatches = false;
265
266 // On Prison Island when pressing the dome viewer switch to close the dome,
267 // the game schedules an ambient sound change using kRivenCommandStoreMovieOpcode
268 // but does not play the associated video in a blocking way. The stored opcode
269 // is not immediately used, stays in memory and may be triggered by some
270 // other action. (Bug #9958)
271 // We replace kRivenCommandStoreMovieOpcode by kRivenCommandActivateSLST
272 // to make the ambient sound change happen immediately.
273 //
274 // Script before patch:
275 // playMovieBlocking(3); // Dome closing
276 // playMovie(4); // Dome spinning up
277 // activatePLST(2); // Dome closed
278 // playMovieBlocking(4); // Dome spinning up
279 // storeMovieOpcode(1, 0, 0, 40, 2); // Schedule ambient sound change to "dome spinning"
280 // after movie 1 finishes blocking playback
281 // playMovie(1); // Dome spinning
282 //
283 // Script after patch:
284 // playMovieBlocking(3); // Dome closing
285 // playMovie(4); // Dome spinning up
286 // activatePLST(2); // Dome closed
287 // playMovieBlocking(4); // Dome spinning up
288 // activateSLST(2); // Ambient sound change to "dome spinning"
289 // playMovie(1); // Dome spinning
290 if (cardGlobalId == 0x1AC1 && scriptType == kCardEnterScript) {
291 shouldApplyPatches = true;
292 for (uint i = 0; i < _commands.size(); i++) {
293 if (_commands[i]->getType() == kRivenCommandStoreMovieOpcode) {
294 RivenSimpleCommand::ArgumentArray arguments;
295 arguments.push_back(2);
296 _commands[i] = RivenCommandPtr(new RivenSimpleCommand(vm, kRivenCommandActivateSLST, arguments));
297 debugC(kRivenDebugPatches, "Applied immediate ambient sound patch to card %x", cardGlobalId);
298 break;
299 }
300 }
301 }
302
303 // On Jungle Island when entering the submarine from the dock beside the main walkway,
304 // the sound of the hatch closing does not play (Bug #9972).
305 // This happens only in the CD version of the game.
306 //
307 // Script before patch:
308 // transition(16);
309 // switchCard(534);
310 //
311 // Script after patch:
312 // transition(16);
313 // switchCard(534);
314 // playSound(112, 256, 0);
315 if (cardGlobalId == 0x2E900 && scriptType == kMouseDownScript && hotspotId == 3
316 && !(vm->getFeatures() & GF_DVD)) {
317 shouldApplyPatches = true;
318 RivenSimpleCommand::ArgumentArray arguments;
319 arguments.push_back(112);
320 arguments.push_back(256);
321 arguments.push_back(0);
322 _commands.push_back(RivenCommandPtr(new RivenSimpleCommand(vm, kRivenCommandPlaySound, arguments)));
323 debugC(kRivenDebugPatches, "Applied missing closing sound patch to card %x", cardGlobalId);
324 }
325
326 // Second part of the patch to fix the invalid card change when entering Gehn's office
327 // The first part is in the card patches.
328 if (cardGlobalId == 0x2E76 && scriptType == kCardUpdateScript && !(vm->getFeatures() & GF_DVD)) {
329 shouldApplyPatches = true;
330
331 for (uint i = 0; i < _commands.size(); i++) {
332 int transitionIndex = -1;
333 if (_commands[i]->getType() == kRivenCommandTransition) {
334 transitionIndex = i;
335 }
336 if (transitionIndex >= 0) {
337 _commands.remove_at(transitionIndex + 1);
338 _commands.remove_at(transitionIndex);
339
340 RivenSimpleCommand::ArgumentArray arguments;
341 arguments.push_back(6);
342 _commands.push_back(RivenCommandPtr(new RivenSimpleCommand(vm, kRivenCommandActivatePLST, arguments)));
343 }
344 }
345
346 debugC(kRivenDebugPatches, "Applied invalid card change during screen update (2/2) to card %x", cardGlobalId);
347 }
348
349 // First part of the patch to fix the invalid steam sounds
350 // when looking at the Boiler island bridge from Temple island.
351 // The second part is in the card patches.
352 if (cardGlobalId == 0x22118 && scriptType == kCardLoadScript) {
353 shouldApplyPatches = true;
354
355 // Remove all the activateSLST calls.
356 // Fixed calls will be added back in the second part of the patch.
357 for (uint i = 0; i < _commands.size(); i++) {
358 if (_commands[i]->getType() == kRivenCommandActivateSLST) {
359 _commands.remove_at(i);
360 break;
361 }
362 }
363
364 debugC(kRivenDebugPatches, "Applied incorrect steam sounds (1/2) to card %x", cardGlobalId);
365 }
366
367 // Override the main menu new game script to call an external command.
368 // This way we can reset all the state when starting a new game while a game is already started.
369 if (cardGlobalId == 0xE2E && scriptType == kMouseDownScript && hotspotId == 16
370 && (vm->getFeatures() & GF_25TH)) {
371 shouldApplyPatches = true;
372 _commands.clear();
373
374 RivenSimpleCommand::ArgumentArray arguments;
375 arguments.push_back(RivenStacks::ASpit::kExternalNewGame);
376 arguments.push_back(0);
377 _commands.push_back(RivenCommandPtr(new RivenSimpleCommand(vm, kRivenCommandRunExternal, arguments)));
378 debugC(kRivenDebugPatches, "Applied override new game script patch to card %x", cardGlobalId);
379 }
380
381 if (shouldApplyPatches) {
382 for (uint i = 0; i < _commands.size(); i++) {
383 _commands[i]->applyCardPatches(cardGlobalId, scriptType, hotspotId);
384 }
385 }
386 }
387
operator +=(RivenScriptPtr & lhs,const RivenScriptPtr & rhs)388 RivenScriptPtr &operator+=(RivenScriptPtr &lhs, const RivenScriptPtr &rhs) {
389 if (rhs) {
390 *lhs += *rhs;
391 }
392 return lhs;
393 }
394
RivenCommand(MohawkEngine_Riven * vm)395 RivenCommand::RivenCommand(MohawkEngine_Riven *vm) :
396 _vm(vm) {
397
398 }
399
~RivenCommand()400 RivenCommand::~RivenCommand() {
401
402 }
403
RivenSimpleCommand(MohawkEngine_Riven * vm,RivenCommandType type,const ArgumentArray & arguments)404 RivenSimpleCommand::RivenSimpleCommand(MohawkEngine_Riven *vm, RivenCommandType type, const ArgumentArray &arguments) :
405 RivenCommand(vm),
406 _type(type),
407 _arguments(arguments) {
408 setupOpcodes();
409 }
410
~RivenSimpleCommand()411 RivenSimpleCommand::~RivenSimpleCommand() {
412 }
413
createFromStream(MohawkEngine_Riven * vm,RivenCommandType type,Common::ReadStream * stream)414 RivenSimpleCommand *RivenSimpleCommand::createFromStream(MohawkEngine_Riven *vm, RivenCommandType type, Common::ReadStream *stream) {
415 uint16 argc = stream->readUint16BE();
416
417 Common::Array<uint16> arguments;
418 arguments.resize(argc);
419
420 for (uint16 i = 0; i < argc; i++) {
421 arguments[i] = stream->readUint16BE();
422 }
423
424 return new RivenSimpleCommand(vm, type, arguments);
425 }
426
427 #define OPCODE(x) { &RivenSimpleCommand::x, #x }
428
setupOpcodes()429 void RivenSimpleCommand::setupOpcodes() {
430 static const RivenOpcode riven_opcodes[] = {
431 // 0x00 (0 decimal)
432 OPCODE(empty),
433 OPCODE(drawBitmap),
434 OPCODE(switchCard),
435 OPCODE(playScriptSLST),
436 // 0x04 (4 decimal)
437 OPCODE(playSound),
438 OPCODE(empty), // Empty
439 OPCODE(empty), // Complex animation (not used)
440 OPCODE(setVariable),
441 // 0x08 (8 decimal)
442 OPCODE(empty), // Not a SimpleCommand
443 OPCODE(enableHotspot),
444 OPCODE(disableHotspot),
445 OPCODE(empty), // Empty
446 // 0x0C (12 decimal)
447 OPCODE(stopSound),
448 OPCODE(changeCursor),
449 OPCODE(delay),
450 OPCODE(empty), // Empty
451 // 0x10 (16 decimal)
452 OPCODE(empty), // Empty
453 OPCODE(runExternalCommand),
454 OPCODE(transition),
455 OPCODE(refreshCard),
456 // 0x14 (20 decimal)
457 OPCODE(beginScreenUpdate),
458 OPCODE(applyScreenUpdate),
459 OPCODE(empty), // Empty
460 OPCODE(empty), // Empty
461 // 0x18 (24 decimal)
462 OPCODE(incrementVariable),
463 OPCODE(empty), // Empty
464 OPCODE(empty), // Empty
465 OPCODE(empty), // Not a SimpleCommand
466 // 0x1C (28 decimal)
467 OPCODE(disableMovie),
468 OPCODE(disableAllMovies),
469 OPCODE(empty), // Set movie rate (not used)
470 OPCODE(enableMovie),
471 // 0x20 (32 decimal)
472 OPCODE(playMovieBlocking),
473 OPCODE(playMovie),
474 OPCODE(stopMovie),
475 OPCODE(empty), // Start a water effect (not used)
476 // 0x24 (36 decimal)
477 OPCODE(unk_36), // Unknown
478 OPCODE(fadeAmbientSounds),
479 OPCODE(storeMovieOpcode),
480 OPCODE(activatePLST),
481 // 0x28 (40 decimal)
482 OPCODE(activateSLST),
483 OPCODE(activateMLSTAndPlay),
484 OPCODE(empty), // Empty
485 OPCODE(activateBLST),
486 // 0x2C (44 decimal)
487 OPCODE(activateFLST),
488 OPCODE(zipMode),
489 OPCODE(activateMLST),
490 OPCODE(empty) // Activate an SLST with a volume parameter (not used)
491 };
492
493 _opcodes = riven_opcodes;
494 }
495
496 ////////////////////////////////
497 // Opcodes
498 ////////////////////////////////
499
500 // Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3)
drawBitmap(uint16 op,const ArgumentArray & args)501 void RivenSimpleCommand::drawBitmap(uint16 op, const ArgumentArray &args) {
502 if (args.size() < 5) // Copy the image to the whole screen, ignoring the rest of the parameters
503 _vm->_gfx->copyImageToScreen(args[0], 0, 0, 608, 392);
504 else // Copy the image to a certain part of the screen
505 _vm->_gfx->copyImageToScreen(args[0], args[1], args[2], args[3], args[4]);
506 }
507
508 // Command 2: go to card (card id)
switchCard(uint16 op,const ArgumentArray & args)509 void RivenSimpleCommand::switchCard(uint16 op, const ArgumentArray &args) {
510 _vm->changeToCard(args[0]);
511 }
512
513 // Command 3: play an SLST from the script
playScriptSLST(uint16 op,const ArgumentArray & args)514 void RivenSimpleCommand::playScriptSLST(uint16 op, const ArgumentArray &args) {
515 int offset = 0, j = 0;
516 uint16 soundCount = args[offset++];
517
518 SLSTRecord slstRecord;
519 slstRecord.index = 0; // not set by the scripts, so we set it to 0
520 slstRecord.soundIds.resize(soundCount);
521
522 for (j = 0; j < soundCount; j++)
523 slstRecord.soundIds[j] = args[offset++];
524 slstRecord.fadeFlags = args[offset++];
525 slstRecord.loop = args[offset++];
526 slstRecord.globalVolume = args[offset++];
527 slstRecord.u0 = args[offset++];
528 slstRecord.suspend = args[offset++];
529
530 slstRecord.volumes.resize(soundCount);
531 slstRecord.balances.resize(soundCount);
532 slstRecord.u2.resize(soundCount);
533
534 for (j = 0; j < soundCount; j++)
535 slstRecord.volumes[j] = args[offset++];
536
537 for (j = 0; j < soundCount; j++)
538 slstRecord.balances[j] = args[offset++]; // negative = left, 0 = center, positive = right
539
540 for (j = 0; j < soundCount; j++)
541 slstRecord.u2[j] = args[offset++]; // Unknown
542
543 // Play the requested sound list
544 _vm->_sound->playSLST(slstRecord);
545 }
546
547 // Command 4: play local tWAV resource (twav_id, volume, block)
playSound(uint16 op,const ArgumentArray & args)548 void RivenSimpleCommand::playSound(uint16 op, const ArgumentArray &args) {
549 uint16 volume = args[1];
550 bool playOnDraw = args[2] == 1;
551
552 _vm->_sound->playSound(args[0], volume, playOnDraw);
553 }
554
555 // Command 7: set variable value (variable, value)
setVariable(uint16 op,const ArgumentArray & args)556 void RivenSimpleCommand::setVariable(uint16 op, const ArgumentArray &args) {
557 _vm->getStackVar(args[0]) = args[1];
558 }
559
560 // Command 9: enable hotspot (blst_id)
enableHotspot(uint16 op,const ArgumentArray & args)561 void RivenSimpleCommand::enableHotspot(uint16 op, const ArgumentArray &args) {
562 RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(args[0]);
563 if (hotspot) {
564 hotspot->enable(true);
565 }
566 }
567
568 // Command 10: disable hotspot (blst_id)
disableHotspot(uint16 op,const ArgumentArray & args)569 void RivenSimpleCommand::disableHotspot(uint16 op, const ArgumentArray &args) {
570 RivenHotspot *hotspot = _vm->getCard()->getHotspotByBlstId(args[0]);
571 if (hotspot) {
572 hotspot->enable(false);
573 }
574 }
575
576 // Command 12: stop sounds (flags)
stopSound(uint16 op,const ArgumentArray & args)577 void RivenSimpleCommand::stopSound(uint16 op, const ArgumentArray &args) {
578 // WORKAROUND: The Play Riven/Visit Riven/Start New Game buttons
579 // in the main menu call this function to stop ambient sounds
580 // after the change stack call to Temple Island. However, this
581 // would cause all ambient sounds not to play. An alternative
582 // fix would be to stop all scripts on a stack change, but this
583 // does fine for now.
584 if (_vm->getStack()->getId() == kStackTspit && (_vm->getStack()->getCurrentCardGlobalId() == 0x6e9a ||
585 _vm->getStack()->getCurrentCardGlobalId() == 0xfeeb))
586 return;
587
588 // The argument is a bitflag for the setting.
589 // bit 0 is normal sound stopping
590 // bit 1 is ambient sound stopping
591 // Having no flags set means clear all
592 if (args[0] & 2 || args[0] == 0)
593 _vm->_sound->stopAllSLST();
594
595 if (args[0] & 1 || args[0] == 0)
596 _vm->_sound->stopSound();
597 }
598
599 // Command 13: set mouse cursor (cursor_id)
changeCursor(uint16 op,const ArgumentArray & args)600 void RivenSimpleCommand::changeCursor(uint16 op, const ArgumentArray &args) {
601 _vm->_cursor->setCursor(args[0]);
602 }
603
604 // Command 14: pause script execution (delay in ms, u1)
delay(uint16 op,const ArgumentArray & args)605 void RivenSimpleCommand::delay(uint16 op, const ArgumentArray &args) {
606 if (args[0] > 0)
607 _vm->delay(args[0]);
608 }
609
610 // Command 17: call external command
runExternalCommand(uint16 op,const ArgumentArray & args)611 void RivenSimpleCommand::runExternalCommand(uint16 op, const ArgumentArray &args) {
612 uint16 commandNameid = args[0];
613 uint16 argumentCount = args[1];
614
615 Common::Array<uint16> commandArgs(argumentCount ? &args[2] : nullptr, argumentCount);
616
617 _vm->getStack()->runCommand(commandNameid, commandArgs);
618 }
619
620 // Command 18: transition
621 // Note that this opcode has 1 or 5 parameters, depending on args.size()
622 // Parameter 0: transition type
623 // Parameters 1-4: transition rectangle
transition(uint16 op,const ArgumentArray & args)624 void RivenSimpleCommand::transition(uint16 op, const ArgumentArray &args) {
625 if (args.size() == 1)
626 _vm->_gfx->scheduleTransition((RivenTransition) args[0]);
627 else
628 _vm->_gfx->scheduleTransition((RivenTransition) args[0], Common::Rect(args[1], args[2], args[3], args[4]));
629 }
630
631 // Command 19: reload card
refreshCard(uint16 op,const ArgumentArray & args)632 void RivenSimpleCommand::refreshCard(uint16 op, const ArgumentArray &args) {
633 _vm->getCard()->enter(false);
634 }
635
636 // Command 20: begin screen update
beginScreenUpdate(uint16 op,const ArgumentArray & args)637 void RivenSimpleCommand::beginScreenUpdate(uint16 op, const ArgumentArray &args) {
638 _vm->_gfx->beginScreenUpdate();
639 }
640
641 // Command 21: apply screen update
applyScreenUpdate(uint16 op,const ArgumentArray & args)642 void RivenSimpleCommand::applyScreenUpdate(uint16 op, const ArgumentArray &args) {
643 _vm->_gfx->applyScreenUpdate();
644 }
645
646 // Command 24: increment variable (variable, value)
incrementVariable(uint16 op,const ArgumentArray & args)647 void RivenSimpleCommand::incrementVariable(uint16 op, const ArgumentArray &args) {
648 _vm->getStackVar(args[0]) += args[1];
649 }
650
651 // Command 28: disable a movie
disableMovie(uint16 op,const ArgumentArray & args)652 void RivenSimpleCommand::disableMovie(uint16 op, const ArgumentArray &args) {
653 RivenVideo *video = _vm->_video->openSlot(args[0]);
654 if (video)
655 video->disable();
656 }
657
658 // Command 29: disable all movies
disableAllMovies(uint16 op,const ArgumentArray & args)659 void RivenSimpleCommand::disableAllMovies(uint16 op, const ArgumentArray &args) {
660 _vm->_video->disableAllMovies();
661 }
662
663 // Command 31: enable a movie
enableMovie(uint16 op,const ArgumentArray & args)664 void RivenSimpleCommand::enableMovie(uint16 op, const ArgumentArray &args) {
665 RivenVideo *video = _vm->_video->openSlot(args[0]);
666 video->enable();
667 }
668
669 // Command 32: play foreground movie - blocking (movie_id)
playMovieBlocking(uint16 op,const ArgumentArray & args)670 void RivenSimpleCommand::playMovieBlocking(uint16 op, const ArgumentArray &args) {
671 RivenVideo *video = _vm->_video->openSlot(args[0]);
672 video->setLooping(false);
673 video->enable();
674 video->playBlocking();
675 }
676
677 // Command 33: play background movie - nonblocking (movie_id)
playMovie(uint16 op,const ArgumentArray & args)678 void RivenSimpleCommand::playMovie(uint16 op, const ArgumentArray &args) {
679 RivenVideo *video = _vm->_video->openSlot(args[0]);
680 video->enable();
681 video->play();
682 }
683
684 // Command 34: stop a movie
stopMovie(uint16 op,const ArgumentArray & args)685 void RivenSimpleCommand::stopMovie(uint16 op, const ArgumentArray &args) {
686 RivenVideo *video = _vm->_video->openSlot(args[0]);
687 video->stop();
688 }
689
690 // Command 36: unknown
unk_36(uint16 op,const ArgumentArray & args)691 void RivenSimpleCommand::unk_36(uint16 op, const ArgumentArray &args) {
692 }
693
694 // Command 37: fade ambient sounds
fadeAmbientSounds(uint16 op,const ArgumentArray & args)695 void RivenSimpleCommand::fadeAmbientSounds(uint16 op, const ArgumentArray &args) {
696 // Similar to stopSound(), but does fading
697 _vm->_sound->stopAllSLST(true);
698 }
699
700 // Command 38: Store an opcode for use when playing a movie (movie id, time high, time low, opcode, arguments...)
storeMovieOpcode(uint16 op,const ArgumentArray & args)701 void RivenSimpleCommand::storeMovieOpcode(uint16 op, const ArgumentArray &args) {
702 // This opcode is used to delay an opcode's usage based on the elapsed
703 // time of a specified movie. However, every use in the game is for
704 // delaying an activateSLST opcode.
705
706 uint32 delayTime = (args[1] << 16) + args[2];
707
708 // Store the script
709 RivenScriptManager::StoredMovieOpcode storedOp;
710 storedOp.script = _vm->_scriptMan->createScriptFromData(1, args[3], 1, args[4]);
711 storedOp.time = delayTime;
712 storedOp.slot = args[0];
713
714 // Store the opcode for later
715 _vm->_scriptMan->setStoredMovieOpcode(storedOp);
716 }
717
718 // Command 39: activate PLST record (card picture lists)
activatePLST(uint16 op,const ArgumentArray & args)719 void RivenSimpleCommand::activatePLST(uint16 op, const ArgumentArray &args) {
720 _vm->_activatedPLST = true;
721
722 RivenCard::Picture picture = _vm->getCard()->getPicture(args[0]);
723 _vm->_gfx->copyImageToScreen(picture.id, picture.rect.left, picture.rect.top, picture.rect.right, picture.rect.bottom);
724 }
725
726 // Command 40: activate SLST record (card ambient sound lists)
activateSLST(uint16 op,const ArgumentArray & args)727 void RivenSimpleCommand::activateSLST(uint16 op, const ArgumentArray &args) {
728 _vm->_activatedSLST = true;
729
730 SLSTRecord slstRecord = _vm->getCard()->getSound(args[0]);
731 _vm->_sound->playSLST(slstRecord);
732 }
733
734 // Command 41: activate MLST record and play
activateMLSTAndPlay(uint16 op,const ArgumentArray & args)735 void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, const ArgumentArray &args) {
736 MLSTRecord mlstRecord = _vm->getCard()->getMovie(args[0]);
737 activateMLST(mlstRecord);
738
739 RivenVideo *video = _vm->_video->openSlot(mlstRecord.playbackSlot);
740 video->enable();
741 video->play();
742 }
743
744 // Command 43: activate BLST record (card hotspot enabling lists)
activateBLST(uint16 op,const ArgumentArray & args)745 void RivenSimpleCommand::activateBLST(uint16 op, const ArgumentArray &args) {
746 _vm->getCard()->activateHotspotEnableRecord(args[0]);
747 }
748
749 // Command 44: activate FLST record (information on which SFXE resource this card should use)
activateFLST(uint16 op,const ArgumentArray & args)750 void RivenSimpleCommand::activateFLST(uint16 op, const ArgumentArray &args) {
751 _vm->getCard()->activateWaterEffect(args[0]);
752 }
753
754 // Command 45: do zip mode
zipMode(uint16 op,const ArgumentArray & args)755 void RivenSimpleCommand::zipMode(uint16 op, const ArgumentArray &args) {
756 assert(_vm->getCard() && _vm->getCard()->getCurHotspot());
757
758 // Check the ZIPS records to see if we have a match to the hotspot name
759 Common::String hotspotName = _vm->getCard()->getCurHotspot()->getName();
760
761 for (uint16 i = 0; i < _vm->_zipModeData.size(); i++)
762 if (_vm->_zipModeData[i].name == hotspotName) {
763 _vm->changeToCard(_vm->_zipModeData[i].id);
764 return;
765 }
766 }
767
768 // Command 46: activate MLST record (movie lists)
activateMLST(uint16 op,const ArgumentArray & args)769 void RivenSimpleCommand::activateMLST(uint16 op, const ArgumentArray &args) {
770 MLSTRecord mlstRecord = _vm->getCard()->getMovie(args[0]);
771 activateMLST(mlstRecord);
772 }
773
activateMLST(const MLSTRecord & mlstRecord) const774 void RivenSimpleCommand::activateMLST(const MLSTRecord &mlstRecord) const {
775 RivenVideo *ptr = _vm->_video->openSlot(mlstRecord.playbackSlot);
776 ptr->load(mlstRecord.movieID);
777 ptr->moveTo(mlstRecord.left, mlstRecord.top);
778 ptr->setLooping(mlstRecord.loop != 0);
779 ptr->setVolume(mlstRecord.volume);
780 }
781
describe() const782 Common::String RivenSimpleCommand::describe() const {
783 Common::String desc;
784
785 if (_type == kRivenCommandSwitch) { // Use the variable name
786 Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
787 desc = Common::String::format("%s = %d", varName.c_str(), _arguments[1]);
788 } else if (_type == kRivenCommandRunExternal) { // Use the external command name
789 Common::String externalCommandName = _vm->getStack()->getName(kExternalCommandNames, _arguments[0]);
790 desc = Common::String::format("%s(", externalCommandName.c_str());
791 uint16 varCount = _arguments[1];
792 for (uint16 j = 0; j < varCount; j++) {
793 desc += Common::String::format("%d", _arguments[2 + j]);
794 if (j != varCount - 1)
795 desc += ", ";
796 }
797 desc += ")";
798 } else if (_type == kRivenCommandIncrementVariable) { // Use the variable name
799 Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
800 desc = Common::String::format("%s += %d", varName.c_str(), _arguments[1]);
801 } else if (_type == kRivenCommandSetVariable) { // Use the variable name
802 Common::String varName = _vm->getStack()->getName(kVariableNames, _arguments[0]);
803 desc = Common::String::format("%s = %d", varName.c_str(), _arguments[1]);
804 } else {
805 desc = Common::String::format("%s(", _opcodes[_type].desc);
806 for (uint16 j = 0; j < _arguments.size(); j++) {
807 desc += Common::String::format("%d", _arguments[j]);
808 if (j != _arguments.size() - 1)
809 desc += ", ";
810 }
811 desc += ")";
812 }
813
814 return desc;
815 }
816
dump(byte tabs)817 void RivenSimpleCommand::dump(byte tabs) {
818 printTabs(tabs);
819 debugN("%s;\n", describe().c_str());
820 }
821
execute()822 void RivenSimpleCommand::execute() {
823 if (DebugMan.isDebugChannelEnabled(kRivenDebugScript)) {
824 debugC(kRivenDebugScript, "Running opcode: %s", describe().c_str());
825 }
826
827 (this->*(_opcodes[_type].proc)) (_type, _arguments);
828 }
829
getType() const830 RivenCommandType RivenSimpleCommand::getType() const {
831 return _type;
832 }
833
RivenSwitchCommand(MohawkEngine_Riven * vm)834 RivenSwitchCommand::RivenSwitchCommand(MohawkEngine_Riven *vm) :
835 RivenCommand(vm),
836 _variableId(0) {
837
838 }
839
~RivenSwitchCommand()840 RivenSwitchCommand::~RivenSwitchCommand() {
841
842 }
843
createFromStream(MohawkEngine_Riven * vm,Common::ReadStream * stream)844 RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm, Common::ReadStream *stream) {
845 RivenSwitchCommand *command = new RivenSwitchCommand(vm);
846
847 if (stream->readUint16BE() != 2) {
848 // This value is not used in the original engine
849 warning("if-then-else unknown value is not 2");
850 }
851
852 // variable to check against
853 command->_variableId = stream->readUint16BE();
854
855 // number of logic blocks
856 uint16 logicBlockCount = stream->readUint16BE();
857 command->_branches.resize(logicBlockCount);
858
859 for (uint16 i = 0; i < logicBlockCount; i++) {
860 Branch &branch = command->_branches[i];
861
862 // Value for this logic block
863 branch.value = stream->readUint16BE();
864 branch.script = vm->_scriptMan->readScript(stream);
865 }
866
867 return command;
868 }
869
dump(byte tabs)870 void RivenSwitchCommand::dump(byte tabs) {
871 Common::String varName = _vm->getStack()->getName(kVariableNames, _variableId);
872 printTabs(tabs); debugN("switch (%s) {\n", varName.c_str());
873 for (uint16 j = 0; j < _branches.size(); j++) {
874 printTabs(tabs + 1);
875 if (_branches[j].value == 0xFFFF)
876 debugN("default:\n");
877 else
878 debugN("case %d:\n", _branches[j].value);
879 _branches[j].script->dumpScript(tabs + 2);
880 printTabs(tabs + 2); debugN("break;\n");
881 }
882 printTabs(tabs); debugN("}\n");
883 }
884
execute()885 void RivenSwitchCommand::execute() {
886 if (DebugMan.isDebugChannelEnabled(kRivenDebugScript)) {
887 Common::String varName = _vm->getStack()->getName(kVariableNames, _variableId);
888 debugC(kRivenDebugScript, "Running opcode: switch(%s)", varName.c_str());
889 }
890
891 // Get the switch variable value
892 uint32 value = _vm->getStackVar(_variableId);
893
894 // Look for a case matching the value
895 for (uint i = 0; i < _branches.size(); i++) {
896 if (_branches[i].value == value) {
897 _vm->_scriptMan->runScript(_branches[i].script, false);
898 return;
899 }
900 }
901
902 // Look for the default case if any
903 for (uint i = 0; i < _branches.size(); i++) {
904 if (_branches[i].value == 0Xffff) {
905 _vm->_scriptMan->runScript(_branches[i].script, false);
906 return;
907 }
908 }
909 }
910
getType() const911 RivenCommandType RivenSwitchCommand::getType() const {
912 return kRivenCommandSwitch;
913 }
914
applyCardPatches(uint32 globalId,int scriptType,uint16 hotspotId)915 void RivenSwitchCommand::applyCardPatches(uint32 globalId, int scriptType, uint16 hotspotId) {
916 for (uint i = 0; i < _branches.size(); i++) {
917 _branches[i].script->applyCardPatches(_vm, globalId, scriptType, hotspotId);
918 }
919 }
920
RivenStackChangeCommand(MohawkEngine_Riven * vm,uint16 stackId,uint32 globalCardId,bool byStackId,bool byStackCardId)921 RivenStackChangeCommand::RivenStackChangeCommand(MohawkEngine_Riven *vm, uint16 stackId, uint32 globalCardId,
922 bool byStackId, bool byStackCardId) :
923 RivenCommand(vm),
924 _stackId(stackId),
925 _cardId(globalCardId),
926 _byStackId(byStackId),
927 _byStackCardId(byStackCardId) {
928
929 }
930
~RivenStackChangeCommand()931 RivenStackChangeCommand::~RivenStackChangeCommand() {
932
933 }
934
createFromStream(MohawkEngine_Riven * vm,Common::ReadStream * stream)935 RivenStackChangeCommand *RivenStackChangeCommand::createFromStream(MohawkEngine_Riven *vm, Common::ReadStream *stream) {
936 /* argumentsSize = */ stream->readUint16BE();
937 uint16 stackId = stream->readUint16BE();
938 uint32 globalCardId = stream->readUint32BE();
939
940 return new RivenStackChangeCommand(vm, stackId, globalCardId, false, false);
941 }
942
execute()943 void RivenStackChangeCommand::execute() {
944 debugC(kRivenDebugScript, "Running opcode: changeStack(%d, %d)", _stackId, _cardId);
945
946 uint16 stackID;
947 if (_byStackId) {
948 stackID = _stackId;
949 } else {
950 Common::String stackName = _vm->getStack()->getName(kStackNames, _stackId);
951
952 stackID = RivenStacks::getId(stackName.c_str());
953 if (stackID == kStackUnknown) {
954 error ("'%s' is not a stack name!", stackName.c_str());
955 }
956 }
957
958 _vm->changeToStack(stackID);
959
960 uint16 cardID;
961 if (_byStackCardId) {
962 cardID = _cardId;
963 } else {
964 cardID = _vm->getStack()->getCardStackId(_cardId);
965 }
966
967 _vm->changeToCard(cardID);
968 }
969
dump(byte tabs)970 void RivenStackChangeCommand::dump(byte tabs) {
971 printTabs(tabs);
972 debugN("changeStack(%d, %d);\n", _stackId, _cardId);
973 }
974
getType() const975 RivenCommandType RivenStackChangeCommand::getType() const {
976 return kRivenCommandChangeStack;
977 }
978
RivenTimerCommand(MohawkEngine_Riven * vm,const Common::SharedPtr<RivenStack::TimerProc> & timerProc)979 RivenTimerCommand::RivenTimerCommand(MohawkEngine_Riven *vm, const Common::SharedPtr<RivenStack::TimerProc> &timerProc) :
980 RivenCommand(vm),
981 _timerProc(timerProc) {
982
983 }
984
execute()985 void RivenTimerCommand::execute() {
986 (*_timerProc)();
987 }
988
dump(byte tabs)989 void RivenTimerCommand::dump(byte tabs) {
990 printTabs(tabs);
991 debugN("doTimer();\n");
992 }
993
getType() const994 RivenCommandType RivenTimerCommand::getType() const {
995 return kRivenCommandTimer;
996 }
997
998 } // End of namespace Mohawk
999