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 "common/config-manager.h"
24 #include "common/system.h"
25
26 #include "scumm/actor.h"
27 #include "scumm/charset.h"
28 #include "scumm/file.h"
29 #include "scumm/imuse/imuse.h"
30 #include "scumm/imuse_digi/dimuse.h"
31 #include "scumm/insane/insane.h"
32 #include "scumm/object.h"
33 #include "scumm/resource.h"
34 #include "scumm/scumm.h"
35 #include "scumm/scumm_v6.h"
36 #include "scumm/scumm_v7.h"
37 #include "scumm/smush/smush_player.h"
38 #include "scumm/sound.h"
39 #include "scumm/util.h"
40 #include "scumm/verbs.h"
41
42 namespace Scumm {
43
44 #define OPCODE(i, x) _opcodes[i]._OPCODE(ScummEngine_v6, x)
45
setupOpcodes()46 void ScummEngine_v6::setupOpcodes() {
47 /* 00 */
48 OPCODE(0x00, o6_pushByte);
49 OPCODE(0x01, o6_pushWord);
50 OPCODE(0x02, o6_pushByteVar);
51 OPCODE(0x03, o6_pushWordVar);
52 /* 04 */
53 OPCODE(0x06, o6_byteArrayRead);
54 OPCODE(0x07, o6_wordArrayRead);
55 /* 08 */
56 OPCODE(0x0a, o6_byteArrayIndexedRead);
57 OPCODE(0x0b, o6_wordArrayIndexedRead);
58 /* 0C */
59 OPCODE(0x0c, o6_dup);
60 OPCODE(0x0d, o6_not);
61 OPCODE(0x0e, o6_eq);
62 OPCODE(0x0f, o6_neq);
63 /* 10 */
64 OPCODE(0x10, o6_gt);
65 OPCODE(0x11, o6_lt);
66 OPCODE(0x12, o6_le);
67 OPCODE(0x13, o6_ge);
68 /* 14 */
69 OPCODE(0x14, o6_add);
70 OPCODE(0x15, o6_sub);
71 OPCODE(0x16, o6_mul);
72 OPCODE(0x17, o6_div);
73 /* 18 */
74 OPCODE(0x18, o6_land);
75 OPCODE(0x19, o6_lor);
76 OPCODE(0x1a, o6_pop);
77 /* 1C */
78 /* 20 */
79 /* 24 */
80 /* 28 */
81 /* 2C */
82 /* 30 */
83 /* 34 */
84 /* 38 */
85 /* 3C */
86 /* 40 */
87 OPCODE(0x42, o6_writeByteVar);
88 OPCODE(0x43, o6_writeWordVar);
89 /* 44 */
90 OPCODE(0x46, o6_byteArrayWrite);
91 OPCODE(0x47, o6_wordArrayWrite);
92 /* 48 */
93 OPCODE(0x4a, o6_byteArrayIndexedWrite);
94 OPCODE(0x4b, o6_wordArrayIndexedWrite);
95 /* 4C */
96 OPCODE(0x4e, o6_byteVarInc);
97 OPCODE(0x4f, o6_wordVarInc);
98 /* 50 */
99 OPCODE(0x52, o6_byteArrayInc);
100 OPCODE(0x53, o6_wordArrayInc);
101 /* 54 */
102 OPCODE(0x56, o6_byteVarDec);
103 OPCODE(0x57, o6_wordVarDec);
104 /* 58 */
105 OPCODE(0x5a, o6_byteArrayDec);
106 OPCODE(0x5b, o6_wordArrayDec);
107 /* 5C */
108 OPCODE(0x5c, o6_if);
109 OPCODE(0x5d, o6_ifNot);
110 OPCODE(0x5e, o6_startScript);
111 OPCODE(0x5f, o6_startScriptQuick);
112 /* 60 */
113 OPCODE(0x60, o6_startObject);
114 OPCODE(0x61, o6_drawObject);
115 OPCODE(0x62, o6_drawObjectAt);
116 OPCODE(0x63, o6_drawBlastObject);
117 /* 64 */
118 OPCODE(0x64, o6_setBlastObjectWindow);
119 OPCODE(0x65, o6_stopObjectCode);
120 OPCODE(0x66, o6_stopObjectCode);
121 OPCODE(0x67, o6_endCutscene);
122 /* 68 */
123 OPCODE(0x68, o6_cutscene);
124 OPCODE(0x69, o6_stopMusic);
125 OPCODE(0x6a, o6_freezeUnfreeze);
126 OPCODE(0x6b, o6_cursorCommand);
127 /* 6C */
128 OPCODE(0x6c, o6_breakHere);
129 OPCODE(0x6d, o6_ifClassOfIs);
130 OPCODE(0x6e, o6_setClass);
131 OPCODE(0x6f, o6_getState);
132 /* 70 */
133 OPCODE(0x70, o6_setState);
134 OPCODE(0x71, o6_setOwner);
135 OPCODE(0x72, o6_getOwner);
136 OPCODE(0x73, o6_jump);
137 /* 74 */
138 OPCODE(0x74, o6_startSound);
139 OPCODE(0x75, o6_stopSound);
140 OPCODE(0x76, o6_startMusic);
141 OPCODE(0x77, o6_stopObjectScript);
142 /* 78 */
143 OPCODE(0x78, o6_panCameraTo);
144 OPCODE(0x79, o6_actorFollowCamera);
145 OPCODE(0x7a, o6_setCameraAt);
146 OPCODE(0x7b, o6_loadRoom);
147 /* 7C */
148 OPCODE(0x7c, o6_stopScript);
149 OPCODE(0x7d, o6_walkActorToObj);
150 OPCODE(0x7e, o6_walkActorTo);
151 OPCODE(0x7f, o6_putActorAtXY);
152 /* 80 */
153 OPCODE(0x80, o6_putActorAtObject);
154 OPCODE(0x81, o6_faceActor);
155 OPCODE(0x82, o6_animateActor);
156 OPCODE(0x83, o6_doSentence);
157 /* 84 */
158 OPCODE(0x84, o6_pickupObject);
159 OPCODE(0x85, o6_loadRoomWithEgo);
160 OPCODE(0x87, o6_getRandomNumber);
161 /* 88 */
162 OPCODE(0x88, o6_getRandomNumberRange);
163 OPCODE(0x8a, o6_getActorMoving);
164 OPCODE(0x8b, o6_isScriptRunning);
165 /* 8C */
166 OPCODE(0x8c, o6_getActorRoom);
167 OPCODE(0x8d, o6_getObjectX);
168 OPCODE(0x8e, o6_getObjectY);
169 OPCODE(0x8f, o6_getObjectOldDir);
170 /* 90 */
171 OPCODE(0x90, o6_getActorWalkBox);
172 OPCODE(0x91, o6_getActorCostume);
173 OPCODE(0x92, o6_findInventory);
174 OPCODE(0x93, o6_getInventoryCount);
175 /* 94 */
176 OPCODE(0x94, o6_getVerbFromXY);
177 OPCODE(0x95, o6_beginOverride);
178 OPCODE(0x96, o6_endOverride);
179 OPCODE(0x97, o6_setObjectName);
180 /* 98 */
181 OPCODE(0x98, o6_isSoundRunning);
182 OPCODE(0x99, o6_setBoxFlags);
183 OPCODE(0x9a, o6_createBoxMatrix);
184 OPCODE(0x9b, o6_resourceRoutines);
185 /* 9C */
186 OPCODE(0x9c, o6_roomOps);
187 OPCODE(0x9d, o6_actorOps);
188 OPCODE(0x9e, o6_verbOps);
189 OPCODE(0x9f, o6_getActorFromXY);
190 /* A0 */
191 OPCODE(0xa0, o6_findObject);
192 OPCODE(0xa1, o6_pseudoRoom);
193 OPCODE(0xa2, o6_getActorElevation);
194 OPCODE(0xa3, o6_getVerbEntrypoint);
195 /* A4 */
196 OPCODE(0xa4, o6_arrayOps);
197 OPCODE(0xa5, o6_saveRestoreVerbs);
198 OPCODE(0xa6, o6_drawBox);
199 OPCODE(0xa7, o6_pop);
200 /* A8 */
201 OPCODE(0xa8, o6_getActorWidth);
202 OPCODE(0xa9, o6_wait);
203 OPCODE(0xaa, o6_getActorScaleX);
204 OPCODE(0xab, o6_getActorAnimCounter);
205 /* AC */
206 OPCODE(0xac, o6_soundKludge);
207 OPCODE(0xad, o6_isAnyOf);
208 OPCODE(0xae, o6_systemOps);
209 OPCODE(0xaf, o6_isActorInBox);
210 /* B0 */
211 OPCODE(0xb0, o6_delay);
212 OPCODE(0xb1, o6_delaySeconds);
213 OPCODE(0xb2, o6_delayMinutes);
214 OPCODE(0xb3, o6_stopSentence);
215 /* B4 */
216 OPCODE(0xb4, o6_printLine);
217 OPCODE(0xb5, o6_printText);
218 OPCODE(0xb6, o6_printDebug);
219 OPCODE(0xb7, o6_printSystem);
220 /* B8 */
221 OPCODE(0xb8, o6_printActor);
222 OPCODE(0xb9, o6_printEgo);
223 OPCODE(0xba, o6_talkActor);
224 OPCODE(0xbb, o6_talkEgo);
225 /* BC */
226 OPCODE(0xbc, o6_dimArray);
227 OPCODE(0xbd, o6_dummy);
228 OPCODE(0xbe, o6_startObjectQuick);
229 OPCODE(0xbf, o6_startScriptQuick2);
230 /* C0 */
231 OPCODE(0xc0, o6_dim2dimArray);
232 /* C4 */
233 OPCODE(0xc4, o6_abs);
234 OPCODE(0xc5, o6_distObjectObject);
235 OPCODE(0xc6, o6_distObjectPt);
236 OPCODE(0xc7, o6_distPtPt);
237 /* C8 */
238 OPCODE(0xc8, o6_kernelGetFunctions);
239 OPCODE(0xc9, o6_kernelSetFunctions);
240 OPCODE(0xca, o6_delayFrames);
241 OPCODE(0xcb, o6_pickOneOf);
242 /* CC */
243 OPCODE(0xcc, o6_pickOneOfDefault);
244 OPCODE(0xcd, o6_stampObject);
245 /* D0 */
246 OPCODE(0xd0, o6_getDateTime);
247 OPCODE(0xd1, o6_stopTalking);
248 OPCODE(0xd2, o6_getAnimateVariable);
249 /* D4 */
250 OPCODE(0xd4, o6_shuffle);
251 OPCODE(0xd5, o6_jumpToScript);
252 OPCODE(0xd6, o6_band);
253 OPCODE(0xd7, o6_bor);
254 /* D8 */
255 OPCODE(0xd8, o6_isRoomScriptRunning);
256 /* DC */
257 OPCODE(0xdd, o6_findAllObjects);
258 /* E0 */
259 OPCODE(0xe1, o6_getPixel);
260 OPCODE(0xe3, o6_pickVarRandom);
261 /* E4 */
262 OPCODE(0xe4, o6_setBoxSet);
263 /* E8 */
264 /* EC */
265 OPCODE(0xec, o6_getActorLayer);
266 OPCODE(0xed, o6_getObjectNewDir);
267 }
268
popRoomAndObj(int * room)269 int ScummEngine_v6::popRoomAndObj(int *room) {
270 int obj;
271
272 if (_game.version >= 7) {
273 obj = pop();
274 *room = getObjectRoom(obj);
275 } else {
276 *room = pop();
277 obj = pop();
278 }
279
280 return obj;
281 }
282
defineArray(int array,int type,int dim2,int dim1)283 byte *ScummEngine_v6::defineArray(int array, int type, int dim2, int dim1) {
284 int id;
285 int size;
286 ArrayHeader *ah;
287
288 assert(0 <= type && type <= 5);
289
290
291 if (_game.heversion >= 61) {
292 if (type == kBitArray || type == kNibbleArray)
293 type = kByteArray;
294 } else {
295 // NOTE: The following code turns all arrays except string arrays into
296 // integer arrays. There seems to be no reason for this, and it wastes
297 // space. However, we can't just remove this either, as that would
298 // break savegame compatibility. So do not touch this unless you are
299 // also adding code which updates old savegames, too. And of course
300 // readArray() and writeArray() would have to be updated, too...
301 if (type != kStringArray)
302 type = kIntArray;
303 }
304
305 nukeArray(array);
306
307 id = findFreeArrayId();
308
309 if (_game.version == 8) {
310 if (array & 0x40000000) {
311 }
312
313 if (array & 0x80000000) {
314 error("Can't define bit variable as array pointer");
315 }
316
317 size = (type == kIntArray) ? 4 : 1;
318 } else {
319 if (array & 0x4000) {
320 }
321
322 if (array & 0x8000) {
323 error("Can't define bit variable as array pointer");
324 }
325
326 size = (type == kIntArray) ? 2 : 1;
327 }
328
329 writeVar(array, id);
330
331 size *= dim2 + 1;
332 size *= dim1 + 1;
333
334 ah = (ArrayHeader *)_res->createResource(rtString, id, size + sizeof(ArrayHeader));
335
336 ah->type = TO_LE_16(type);
337 ah->dim1 = TO_LE_16(dim1 + 1);
338 ah->dim2 = TO_LE_16(dim2 + 1);
339
340 return ah->data;
341 }
342
nukeArray(int a)343 void ScummEngine_v6::nukeArray(int a) {
344 int data;
345
346 data = readVar(a);
347
348 if (_game.heversion >= 80)
349 data &= ~0x33539000;
350
351 if (data)
352 _res->nukeResource(rtString, data);
353 if (_game.heversion >= 60)
354 _arraySlot[data] = 0;
355
356 writeVar(a, 0);
357 }
358
findFreeArrayId()359 int ScummEngine_v6::findFreeArrayId() {
360 const ResourceManager::ResTypeData &rtd = _res->_types[rtString];
361 int i;
362
363 for (i = 1; i < _numArray; i++) {
364 if (!rtd[i]._address)
365 return i;
366 }
367 error("Out of array pointers, %d max", _numArray);
368 return -1;
369 }
370
371 #define SWAP16(x) x = SWAP_BYTES_16(x)
372
getArray(int array)373 ScummEngine_v6::ArrayHeader *ScummEngine_v6::getArray(int array) {
374 ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
375 if (!ah)
376 return 0;
377
378 if (_game.heversion == 0) {
379 // Workaround for a long standing bug where we saved array headers in native
380 // endianness, instead of a fixed endianness. We now always store the
381 // dimensions in little endian byte order. But to stay compatible with older
382 // savegames, we try to detect savegames which were created on a big endian
383 // system and convert them to the proper little endian format on the fly.
384 if ((FROM_LE_16(ah->dim1) & 0xF000) || (FROM_LE_16(ah->dim2) & 0xF000) || (FROM_LE_16(ah->type) & 0xFF00)) {
385 SWAP16(ah->dim1);
386 SWAP16(ah->dim2);
387 SWAP16(ah->type);
388 }
389 }
390
391 return ah;
392 }
393
readArray(int array,int idx,int base)394 int ScummEngine_v6::readArray(int array, int idx, int base) {
395 ArrayHeader *ah = getArray(array);
396
397 if (!ah)
398 error("readArray: invalid array %d (%d)", array, readVar(array));
399
400 // WORKAROUND bug #600. This is clearly a script bug, as this script
401 // excerpt shows nicely:
402 // ...
403 // [03A7] (5D) if (isAnyOf(array-447[localvar13][localvar14],[0,4])) {
404 // [03BD] (5D) if ((localvar13 != -1) && (localvar14 != -1)) {
405 // [03CF] (B6) printDebug.begin()
406 // ...
407 // So it checks for invalid array indices only *after* using them to access
408 // the array. Ouch.
409 if (_game.id == GID_FT && array == 447 && _currentRoom == 95 && vm.slot[_currentScript].number == 2010 && idx == -1 && base == -1) {
410 return 0;
411 }
412
413 const int offset = base + idx * FROM_LE_16(ah->dim1);
414
415 if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
416 error("readArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
417 array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
418 }
419
420 int val;
421 if (FROM_LE_16(ah->type) != kIntArray) {
422 val = ah->data[offset];
423 } else if (_game.version == 8) {
424 val = (int32)READ_LE_UINT32(ah->data + offset * 4);
425 } else {
426 val = (int16)READ_LE_UINT16(ah->data + offset * 2);
427 }
428 return val;
429 }
430
writeArray(int array,int idx,int base,int value)431 void ScummEngine_v6::writeArray(int array, int idx, int base, int value) {
432 ArrayHeader *ah = getArray(array);
433 if (!ah)
434 return;
435
436 const int offset = base + idx * FROM_LE_16(ah->dim1);
437
438 if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
439 error("writeArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
440 array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
441 }
442
443 if (FROM_LE_16(ah->type) != kIntArray) {
444 ah->data[offset] = value;
445 } else if (_game.version == 8) {
446 WRITE_LE_UINT32(ah->data + offset * 4, value);
447 } else {
448 WRITE_LE_UINT16(ah->data + offset * 2, value);
449 }
450 }
451
readArrayFromIndexFile()452 void ScummEngine_v6::readArrayFromIndexFile() {
453 int num;
454 int a, b, c;
455
456 while ((num = _fileHandle->readUint16LE()) != 0) {
457 a = _fileHandle->readUint16LE();
458 b = _fileHandle->readUint16LE();
459 c = _fileHandle->readUint16LE();
460 if (c == kBitArray)
461 defineArray(num, kBitArray, a, b);
462 else
463 defineArray(num, kIntArray, a, b);
464 }
465 }
466
getStackList(int * args,uint maxnum)467 int ScummEngine_v6::getStackList(int *args, uint maxnum) {
468 uint num, i;
469
470 for (i = 0; i < maxnum; i++)
471 args[i] = 0;
472
473 num = pop();
474
475 if (num > maxnum)
476 error("Too many items %d in stack list, max %d", num, maxnum);
477
478 i = num;
479 while (i--) {
480 args[i] = pop();
481 }
482
483 return num;
484 }
485
o6_pushByte()486 void ScummEngine_v6::o6_pushByte() {
487 push(fetchScriptByte());
488 }
489
o6_pushWord()490 void ScummEngine_v6::o6_pushWord() {
491 push(fetchScriptWordSigned());
492 }
493
o6_pushByteVar()494 void ScummEngine_v6::o6_pushByteVar() {
495 push(readVar(fetchScriptByte()));
496 }
497
o6_pushWordVar()498 void ScummEngine_v6::o6_pushWordVar() {
499 push(readVar(fetchScriptWord()));
500 }
501
o6_byteArrayRead()502 void ScummEngine_v6::o6_byteArrayRead() {
503 int base = pop();
504 push(readArray(fetchScriptByte(), 0, base));
505 }
506
o6_wordArrayRead()507 void ScummEngine_v6::o6_wordArrayRead() {
508 int base = pop();
509 push(readArray(fetchScriptWord(), 0, base));
510 }
511
o6_byteArrayIndexedRead()512 void ScummEngine_v6::o6_byteArrayIndexedRead() {
513 int base = pop();
514 int idx = pop();
515 push(readArray(fetchScriptByte(), idx, base));
516 }
517
o6_wordArrayIndexedRead()518 void ScummEngine_v6::o6_wordArrayIndexedRead() {
519 int base = pop();
520 int idx = pop();
521 push(readArray(fetchScriptWord(), idx, base));
522 }
523
o6_dup()524 void ScummEngine_v6::o6_dup() {
525 int a = pop();
526 push(a);
527 push(a);
528 }
529
o6_not()530 void ScummEngine_v6::o6_not() {
531 push(pop() == 0);
532 }
533
o6_eq()534 void ScummEngine_v6::o6_eq() {
535 int a = pop();
536 int b = pop();
537
538 // WORKAROUND: Forces the game version string set via script 1 to be used in both Macintosh and Windows versions,
539 // when checking for save game compatibility. Allows saved games to be shared between Macintosh and Windows versions.
540 // The scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
541 if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) &&
542 a == 2 && b == 1) {
543 push(1);
544 } else {
545 push(a == b);
546 }
547 }
548
o6_neq()549 void ScummEngine_v6::o6_neq() {
550 push(pop() != pop());
551 }
552
o6_gt()553 void ScummEngine_v6::o6_gt() {
554 int a = pop();
555 push(pop() > a);
556 }
557
o6_lt()558 void ScummEngine_v6::o6_lt() {
559 int a = pop();
560 push(pop() < a);
561 }
562
o6_le()563 void ScummEngine_v6::o6_le() {
564 int a = pop();
565 push(pop() <= a);
566 }
567
o6_ge()568 void ScummEngine_v6::o6_ge() {
569 int a = pop();
570 push(pop() >= a);
571 }
572
o6_add()573 void ScummEngine_v6::o6_add() {
574 int a = pop();
575 push(pop() + a);
576 }
577
o6_sub()578 void ScummEngine_v6::o6_sub() {
579 int a = pop();
580 push(pop() - a);
581 }
582
o6_mul()583 void ScummEngine_v6::o6_mul() {
584 int a = pop();
585 push(pop() * a);
586 }
587
o6_div()588 void ScummEngine_v6::o6_div() {
589 int a = pop();
590 if (a == 0)
591 error("division by zero");
592 push(pop() / a);
593 }
594
o6_land()595 void ScummEngine_v6::o6_land() {
596 int a = pop();
597 push(pop() && a);
598 }
599
o6_lor()600 void ScummEngine_v6::o6_lor() {
601 int a = pop();
602 push(pop() || a);
603 }
604
o6_bor()605 void ScummEngine_v6::o6_bor() {
606 int a = pop();
607 push(pop() | a);
608 }
609
o6_band()610 void ScummEngine_v6::o6_band() {
611 int a = pop();
612 push(pop() & a);
613 }
614
o6_pop()615 void ScummEngine_v6::o6_pop() {
616 pop();
617 }
618
o6_writeByteVar()619 void ScummEngine_v6::o6_writeByteVar() {
620 writeVar(fetchScriptByte(), pop());
621 }
622
o6_writeWordVar()623 void ScummEngine_v6::o6_writeWordVar() {
624 writeVar(fetchScriptWord(), pop());
625 }
626
o6_byteArrayWrite()627 void ScummEngine_v6::o6_byteArrayWrite() {
628 int a = pop();
629 writeArray(fetchScriptByte(), 0, pop(), a);
630 }
631
o6_wordArrayWrite()632 void ScummEngine_v6::o6_wordArrayWrite() {
633 int a = pop();
634 writeArray(fetchScriptWord(), 0, pop(), a);
635 }
636
o6_byteArrayIndexedWrite()637 void ScummEngine_v6::o6_byteArrayIndexedWrite() {
638 int val = pop();
639 int base = pop();
640 writeArray(fetchScriptByte(), pop(), base, val);
641 }
642
o6_wordArrayIndexedWrite()643 void ScummEngine_v6::o6_wordArrayIndexedWrite() {
644 int val = pop();
645 int base = pop();
646 writeArray(fetchScriptWord(), pop(), base, val);
647 }
648
o6_byteVarInc()649 void ScummEngine_v6::o6_byteVarInc() {
650 int var = fetchScriptByte();
651 writeVar(var, readVar(var) + 1);
652 }
653
o6_wordVarInc()654 void ScummEngine_v6::o6_wordVarInc() {
655 int var = fetchScriptWord();
656 writeVar(var, readVar(var) + 1);
657 }
658
o6_byteArrayInc()659 void ScummEngine_v6::o6_byteArrayInc() {
660 int var = fetchScriptByte();
661 int base = pop();
662 writeArray(var, 0, base, readArray(var, 0, base) + 1);
663 }
664
o6_wordArrayInc()665 void ScummEngine_v6::o6_wordArrayInc() {
666 int var = fetchScriptWord();
667 int base = pop();
668 writeArray(var, 0, base, readArray(var, 0, base) + 1);
669 }
670
o6_byteVarDec()671 void ScummEngine_v6::o6_byteVarDec() {
672 int var = fetchScriptByte();
673 writeVar(var, readVar(var) - 1);
674 }
675
o6_wordVarDec()676 void ScummEngine_v6::o6_wordVarDec() {
677 int var = fetchScriptWord();
678 writeVar(var, readVar(var) - 1);
679 }
680
o6_byteArrayDec()681 void ScummEngine_v6::o6_byteArrayDec() {
682 int var = fetchScriptByte();
683 int base = pop();
684 writeArray(var, 0, base, readArray(var, 0, base) - 1);
685 }
686
o6_wordArrayDec()687 void ScummEngine_v6::o6_wordArrayDec() {
688 int var = fetchScriptWord();
689 int base = pop();
690 writeArray(var, 0, base, readArray(var, 0, base) - 1);
691 }
692
o6_if()693 void ScummEngine_v6::o6_if() {
694 if (pop())
695 o6_jump();
696 else
697 fetchScriptWord();
698 }
699
o6_ifNot()700 void ScummEngine_v6::o6_ifNot() {
701 if (!pop())
702 o6_jump();
703 else
704 fetchScriptWord();
705 }
706
o6_jump()707 void ScummEngine_v6::o6_jump() {
708 int offset = fetchScriptWordSigned();
709
710 // WORKAROUND bug #6097: Pressing escape at the lake side entrance of
711 // the cave while Putt Putt is not on solid ground and still talking
712 // will cause the raft to disappear. This is a script bug in the
713 // original game and affects several versions.
714 if (_game.id == GID_PUTTZOO) {
715 if (_game.heversion == 73 && vm.slot[_currentScript].number == 206 && offset == 176 && !isScriptRunning(202))
716 _scummVars[244] = 35;
717 if (_game.features & GF_HE_985 && vm.slot[_currentScript].number == 2054 && offset == 178 && !isScriptRunning(2050))
718 _scummVars[202] = 35;
719 }
720
721 // WORKAROUND bug #4464: Talking to the guard at the bigfoot party, after
722 // he's let you inside, will cause the game to hang, if you end the conversation.
723 // This is a script bug, due to a missing jump in one segment of the script.
724 if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 101 && readVar(0x8000 + 97) == 1 && offset == 1) {
725 offset = -18;
726 }
727
728 _scriptPointer += offset;
729 }
730
o6_startScript()731 void ScummEngine_v6::o6_startScript() {
732 int args[25];
733 int script, flags;
734
735 getStackList(args, ARRAYSIZE(args));
736 script = pop();
737 flags = pop();
738
739 // WORKAROUND for a bug also present in the original EXE: After greasing (or oiling?)
740 // the cannonballs in the Plunder Town Theater, during the juggling show, the game
741 // cuts from room 18 (backstage) to room 19 (stage).
742 //
743 // Usually, when loading a room script 29 handles the change of background music,
744 // based on which room we've just loaded.
745 // Unfortunately, during this particular cutscene, script 29 is not executing,
746 // therefore the music is unchanged from room 18 to 19 (the muffled backstage
747 // version is played), and is not coherent with the drums fill played afterwards
748 // (sequence 2225), which is unmuffled.
749 //
750 // This fix checks for this situation happening (and only this one), and makes a call
751 // to a soundKludge operation like script 29 would have done.
752 if (_game.id == GID_CMI && _currentRoom == 19 &&
753 vm.slot[_currentScript].number == 168 && script == 118) {
754 int list[16] = { 4096, 1278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
755 _sound->soundKludge(list, 2);
756 }
757
758 // WORKAROUND bug #269: At Dino Bungee National Memorial, the buttons for
759 // the Wally and Rex dinosaurs will always restart their speech, instead of
760 // stopping and starting their speech. This was a script bug in the original
761 // game.
762 if (_game.id == GID_SAMNMAX && _roomResource == 59 &&
763 vm.slot[_currentScript].number == 201 && script == 48) {
764 o6_breakHere();
765 }
766
767 // WORKAROUND bug #1493: In Puerto Pollo, if you have Guybrush examine
768 // the church clock, he'll read out the current time. Nice touch, only that
769 // it sounds crap in the german version (and maybe others, too). It seems
770 // the original engine of the german version played just a simple fixed
771 // text in this spot, for the above reason. Since the data files are
772 // unchanged, it must have been an engine hack job. No idea how they did
773 // it exactly, but this here is how we do it :-)
774 if (_game.id == GID_CMI && script == 204 &&
775 _currentRoom == 15 && vm.slot[_currentScript].number == 421 &&
776 _language == Common::DE_DEU) {
777
778 _actorToPrintStrFor = 1;
779 _string[0].loadDefault();
780 actorTalk((const byte *)"/VDSO325/Whoa! Look at the time. Gotta scoot.");
781
782 return;
783 }
784
785 // WORKAROUND bug #3591: When turning pages in the recipe book
786 // (found on Blood Island), there is a brief moment where it displays
787 // text from two different pages at the same time.
788 //
789 // The content of the books is drawing (in an endless loop) by local
790 // script 2007. Changing the page is handled by script 2006, which
791 // first stops script 2007; then switches the page; then restarts
792 // script 2007. But it fails to clear the blast texts beforehand.
793 // Hence, the next time blast text is drawn, both the old one (from
794 // the old instance of script 2007) and the new text (from the new
795 // instance) are briefly drawn simultaneously.
796 //
797 // This looks like a script bug to me (a missing call to clearTextQueue).
798 // But this could also hint at a subtle bug in ScummVM; we should check
799 // whether this bug occurs with the original engine or not.
800 if (_game.id == GID_CMI && script == 2007 &&
801 _currentRoom == 62 && vm.slot[_currentScript].number == 2006) {
802
803 removeBlastTexts();
804 }
805
806 runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
807 }
808
o6_jumpToScript()809 void ScummEngine_v6::o6_jumpToScript() {
810 int args[25];
811 int script, flags;
812
813 getStackList(args, ARRAYSIZE(args));
814 script = pop();
815 flags = pop();
816 stopObjectCode();
817 runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
818 }
819
o6_startScriptQuick()820 void ScummEngine_v6::o6_startScriptQuick() {
821 int args[25];
822 int script;
823 getStackList(args, ARRAYSIZE(args));
824 script = pop();
825 runScript(script, 0, 0, args);
826 }
827
o6_startScriptQuick2()828 void ScummEngine_v6::o6_startScriptQuick2() {
829 int args[25];
830 int script;
831 getStackList(args, ARRAYSIZE(args));
832 script = pop();
833 runScript(script, 0, 1, args);
834 }
835
o6_startObject()836 void ScummEngine_v6::o6_startObject() {
837 int args[25];
838 int script, entryp;
839 int flags;
840 getStackList(args, ARRAYSIZE(args));
841 entryp = pop();
842 script = pop();
843 flags = pop();
844 runObjectScript(script, entryp, (flags & 1) != 0, (flags & 2) != 0, args);
845 }
846
o6_startObjectQuick()847 void ScummEngine_v6::o6_startObjectQuick() {
848 int args[25];
849 int script, entryp;
850 getStackList(args, ARRAYSIZE(args));
851 entryp = pop();
852 script = pop();
853 runObjectScript(script, entryp, 0, 1, args);
854 }
855
o6_drawObject()856 void ScummEngine_v6::o6_drawObject() {
857 int state = pop();
858 int obj = pop();
859
860 // This is based on disassembly
861 if (state == 0)
862 state = 1;
863
864 setObjectState(obj, state, -1, -1);
865 }
866
o6_drawObjectAt()867 void ScummEngine_v6::o6_drawObjectAt() {
868 int y = pop();
869 int x = pop();
870 int obj = pop();
871
872 // WORKAROUND bug #3487 : Adjust x and y position of
873 // objects in credits sequence, to match other ports
874 if (_game.id == GID_PUTTMOON && _game.platform == Common::kPlatform3DO &&
875 _roomResource == 38 && vm.slot[_currentScript].number == 206) {
876 x = y = -1;
877 }
878
879 setObjectState(obj, 1, x, y);
880 }
881
o6_stopObjectCode()882 void ScummEngine_v6::o6_stopObjectCode() {
883 stopObjectCode();
884 }
885
o6_endCutscene()886 void ScummEngine_v6::o6_endCutscene() {
887 endCutscene();
888 }
889
o6_cutscene()890 void ScummEngine_v6::o6_cutscene() {
891 int args[25];
892 getStackList(args, ARRAYSIZE(args));
893 beginCutscene(args);
894 }
895
o6_stopMusic()896 void ScummEngine_v6::o6_stopMusic() {
897 _sound->stopAllSounds();
898 }
899
o6_freezeUnfreeze()900 void ScummEngine_v6::o6_freezeUnfreeze() {
901 int a = pop();
902
903 if (a)
904 freezeScripts(a);
905 else
906 unfreezeScripts();
907 }
908
o6_cursorCommand()909 void ScummEngine_v6::o6_cursorCommand() {
910 int a, i;
911 int args[16];
912
913 byte subOp = fetchScriptByte();
914
915 switch (subOp) {
916 case 0x90: // SO_CURSOR_ON Turn cursor on
917 _cursor.state = 1;
918 verbMouseOver(0);
919 break;
920 case 0x91: // SO_CURSOR_OFF Turn cursor off
921 _cursor.state = 0;
922 verbMouseOver(0);
923 break;
924 case 0x92: // SO_USERPUT_ON
925 _userPut = 1;
926 break;
927 case 0x93: // SO_USERPUT_OFF
928 _userPut = 0;
929 break;
930 case 0x94: // SO_CURSOR_SOFT_ON Turn soft cursor on
931 _cursor.state++;
932 if (_cursor.state > 1)
933 error("Cursor state greater than 1 in script");
934 verbMouseOver(0);
935 break;
936 case 0x95: // SO_CURSOR_SOFT_OFF Turn soft cursor off
937 _cursor.state--;
938 verbMouseOver(0);
939 break;
940 case 0x96: // SO_USERPUT_SOFT_ON
941 _userPut++;
942 break;
943 case 0x97: // SO_USERPUT_SOFT_OFF
944 _userPut--;
945 break;
946 case 0x99: // SO_CURSOR_IMAGE Set cursor image
947 {
948 int room, obj;
949 if (_game.heversion >= 70) {
950 obj = pop();
951 room = getObjectRoom(obj);
952 } else {
953 obj = popRoomAndObj(&room);
954 }
955 setCursorFromImg(obj, room, 1);
956 break;
957 }
958 case 0x9A: // SO_CURSOR_HOTSPOT Set cursor hotspot
959 a = pop();
960 setCursorHotspot(pop(), a);
961 updateCursor();
962 break;
963 case 0x9C: // SO_CHARSET_SET
964 initCharset(pop());
965 break;
966 case 0x9D: // SO_CHARSET_COLOR
967 getStackList(args, ARRAYSIZE(args));
968 for (i = 0; i < 16; i++)
969 _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
970 break;
971 case 0xD6: // SO_CURSOR_TRANSPARENT Set cursor transparent color
972 setCursorTransparency(pop());
973 break;
974 default:
975 error("o6_cursorCommand: default case %x", subOp);
976 }
977
978 VAR(VAR_CURSORSTATE) = _cursor.state;
979 VAR(VAR_USERPUT) = _userPut;
980 }
981
o6_breakHere()982 void ScummEngine_v6::o6_breakHere() {
983 updateScriptPtr();
984 _currentScript = 0xFF;
985 }
986
o6_ifClassOfIs()987 void ScummEngine_v6::o6_ifClassOfIs() {
988 int args[16];
989 int num, obj, cls;
990 bool b;
991 int cond = 1;
992
993 num = getStackList(args, ARRAYSIZE(args));
994 obj = pop();
995
996 if (_game.heversion >= 80 && num == 0) {
997 push(_classData[obj]);
998 return;
999 }
1000
1001 while (--num >= 0) {
1002 cls = args[num];
1003 b = getClass(obj, cls);
1004 if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
1005 cond = 0;
1006 }
1007 push(cond);
1008 }
1009
o6_setClass()1010 void ScummEngine_v6::o6_setClass() {
1011 int args[16];
1012 int num, obj, cls;
1013
1014 num = getStackList(args, ARRAYSIZE(args));
1015 obj = pop();
1016
1017 while (--num >= 0) {
1018 cls = args[num];
1019 if (cls == 0)
1020 _classData[num] = 0;
1021 else if (cls & 0x80)
1022 putClass(obj, cls, 1);
1023 else
1024 putClass(obj, cls, 0);
1025 }
1026 }
1027
o6_getState()1028 void ScummEngine_v6::o6_getState() {
1029 push(getState(pop()));
1030 }
1031
o6_setState()1032 void ScummEngine_v6::o6_setState() {
1033 int state = pop();
1034 int obj = pop();
1035
1036 putState(obj, state);
1037 markObjectRectAsDirty(obj);
1038 if (_bgNeedsRedraw)
1039 clearDrawObjectQueue();
1040 }
1041
o6_setOwner()1042 void ScummEngine_v6::o6_setOwner() {
1043 int owner = pop();
1044 int obj = pop();
1045 setOwnerOf(obj, owner);
1046 }
1047
o6_getOwner()1048 void ScummEngine_v6::o6_getOwner() {
1049 push(getOwner(pop()));
1050 }
1051
o6_startSound()1052 void ScummEngine_v6::o6_startSound() {
1053 int offset = 0;
1054
1055 // In Fatty Bear's Birthday Surprise the piano uses offsets 1 - 23 to
1056 // indicate which note to play, but only when using the standard piano
1057 // sound. See also o60_soundOps()
1058 if (_game.heversion >= 60 && (_game.id != GID_PUTTDEMO))
1059 offset = pop();
1060
1061 #ifdef ENABLE_SCUMM_7_8
1062 if (_game.version >= 7)
1063 _imuseDigital->startSfx(pop(), 64);
1064 else
1065 #endif
1066 _sound->addSoundToQueue(pop(), offset);
1067 }
1068
o6_stopSound()1069 void ScummEngine_v6::o6_stopSound() {
1070 _sound->stopSound(pop());
1071 }
1072
o6_startMusic()1073 void ScummEngine_v6::o6_startMusic() {
1074 if (_game.version >= 7)
1075 error("o6_startMusic() It shouldn't be called here for imuse digital");
1076
1077 _sound->addSoundToQueue(pop());
1078 }
1079
o6_stopObjectScript()1080 void ScummEngine_v6::o6_stopObjectScript() {
1081 stopObjectScript(pop());
1082 }
1083
o6_panCameraTo()1084 void ScummEngine_v6::o6_panCameraTo() {
1085 if (_game.version >= 7) {
1086 int y = pop();
1087 int x = pop();
1088 panCameraTo(x, y);
1089 } else {
1090 panCameraTo(pop(), 0);
1091 }
1092 }
1093
o6_actorFollowCamera()1094 void ScummEngine_v6::o6_actorFollowCamera() {
1095 if (_game.version >= 7)
1096 setCameraFollows(derefActor(pop(), "actorFollowCamera"));
1097 else
1098 actorFollowCamera(pop());
1099 }
1100
o6_setCameraAt()1101 void ScummEngine_v6::o6_setCameraAt() {
1102 if (_game.version >= 7) {
1103 int x, y;
1104
1105 camera._follows = 0;
1106 VAR(VAR_CAMERA_FOLLOWED_ACTOR) = 0;
1107
1108 y = pop();
1109 x = pop();
1110
1111 setCameraAt(x, y);
1112 } else {
1113 setCameraAtEx(pop());
1114 }
1115 }
1116
o6_loadRoom()1117 void ScummEngine_v6::o6_loadRoom() {
1118 int room = pop();
1119 startScene(room, 0, 0);
1120 if (_game.heversion >= 61) {
1121 setCameraAt(camera._cur.x, 0);
1122 }
1123 _fullRedraw = true;
1124 }
1125
o6_stopScript()1126 void ScummEngine_v6::o6_stopScript() {
1127 int script = pop();
1128 if (script == 0)
1129 stopObjectCode();
1130 else
1131 stopScript(script);
1132 }
1133
o6_walkActorToObj()1134 void ScummEngine_v6::o6_walkActorToObj() {
1135 int act, obj, dist;
1136 Actor *a, *a2;
1137 int x, y;
1138
1139 dist = pop();
1140 obj = pop();
1141 act = pop();
1142 a = derefActor(act, "o6_walkActorToObj");
1143
1144 if (obj >= _numActors) {
1145 int wio = whereIsObject(obj);
1146
1147 if (wio != WIO_FLOBJECT && wio != WIO_ROOM)
1148 return;
1149
1150 int dir;
1151 getObjectXYPos(obj, x, y, dir);
1152 a->startWalkActor(x, y, dir);
1153 } else {
1154 a2 = derefActorSafe(obj, "o6_walkActorToObj(2)");
1155 if (_game.id == GID_SAMNMAX && a2 == 0) {
1156 // WORKAROUND bug #801 SAM: Fish Farm. Note quite sure why it
1157 // happens, whether it's normal or due to a bug in the ScummVM code.
1158 debug(0, "o6_walkActorToObj: invalid actor %d", obj);
1159 return;
1160 }
1161 if (!a->isInCurrentRoom() || !a2->isInCurrentRoom())
1162 return;
1163 if (dist == 0) {
1164 dist = a2->_scalex * a2->_width / 0xFF;
1165 dist += dist / 2;
1166 }
1167 x = a2->getPos().x;
1168 y = a2->getPos().y;
1169 if (x < a->getPos().x)
1170 x += dist;
1171 else
1172 x -= dist;
1173 a->startWalkActor(x, y, -1);
1174 }
1175 }
1176
o6_walkActorTo()1177 void ScummEngine_v6::o6_walkActorTo() {
1178 int x, y;
1179 y = pop();
1180 x = pop();
1181 Actor *a = derefActor(pop(), "o6_walkActorTo");
1182 a->startWalkActor(x, y, -1);
1183 }
1184
o6_putActorAtXY()1185 void ScummEngine_v6::o6_putActorAtXY() {
1186 int room, x, y, act;
1187 Actor *a;
1188
1189 room = pop();
1190 y = pop();
1191 x = pop();
1192 act = pop();
1193 a = derefActor(act, "o6_putActorAtXY");
1194
1195 if (room == 0xFF || room == 0x7FFFFFFF) {
1196 room = a->_room;
1197 } else {
1198 if (a->_visible && _currentRoom != room && getTalkingActor() == a->_number) {
1199 stopTalk();
1200 }
1201 if (room != 0)
1202 a->_room = room;
1203 }
1204 a->putActor(x, y, room);
1205 }
1206
1207
o6_putActorAtObject()1208 void ScummEngine_v6::o6_putActorAtObject() {
1209 int room, obj, x, y;
1210 Actor *a;
1211
1212 obj = popRoomAndObj(&room);
1213
1214 a = derefActor(pop(), "o6_putActorAtObject");
1215 if (whereIsObject(obj) != WIO_NOT_FOUND) {
1216 getObjectXYPos(obj, x, y);
1217 } else {
1218 x = 160;
1219 y = 120;
1220 }
1221 if (room == 0xFF)
1222 room = a->_room;
1223 a->putActor(x, y, room);
1224 }
1225
o6_faceActor()1226 void ScummEngine_v6::o6_faceActor() {
1227 int obj = pop();
1228 Actor *a = derefActor(pop(), "o6_faceActor");
1229 a->faceToObject(obj);
1230 }
1231
o6_animateActor()1232 void ScummEngine_v6::o6_animateActor() {
1233 int anim = pop();
1234 int act = pop();
1235 if (_game.id == GID_TENTACLE && _roomResource == 57 &&
1236 vm.slot[_currentScript].number == 19 && act == 593) {
1237 // WORKAROUND bug #813: This very odd case (animateActor(593,250))
1238 // occurs in DOTT, in the cutscene after George cuts down the "cherry
1239 // tree" and the tree Laverne is trapped in vanishes...
1240 // Not sure if this means animateActor somehow also must work for objects
1241 // (593 is the time machine in room 57), or if this is simply a script bug.
1242 act = 6;
1243 }
1244 if (_game.id == GID_SAMNMAX && _roomResource == 35 &&
1245 vm.slot[_currentScript].number == 202 && act == 4 && anim == 14) {
1246 // WORKAROUND bug #2068 (Animation glitch at World of Fish).
1247 // Before starting animation 14 of the fisherman, make sure he isn't
1248 // talking anymore. This appears to be a bug in the original game as well.
1249 if (getTalkingActor() == 4) {
1250 stopTalk();
1251 }
1252 }
1253 Actor *a = derefActor(act, "o6_animateActor");
1254 a->animateActor(anim);
1255 }
1256
o6_doSentence()1257 void ScummEngine_v6::o6_doSentence() {
1258 int verb, objectA, objectB;
1259
1260 objectB = pop();
1261 if (_game.version < 8)
1262 pop(); // dummy pop (in Sam&Max, seems to be always 0 or 130)
1263 objectA = pop();
1264 verb = pop();
1265
1266 doSentence(verb, objectA, objectB);
1267 }
1268
o6_pickupObject()1269 void ScummEngine_v6::o6_pickupObject() {
1270 int obj, room;
1271 int i;
1272
1273 obj = popRoomAndObj(&room);
1274 if (room == 0)
1275 room = _roomResource;
1276
1277 for (i = 0; i < _numInventory; i++) {
1278 if (_inventory[i] == (uint16)obj) {
1279 putOwner(obj, VAR(VAR_EGO));
1280 runInventoryScript(obj);
1281 return;
1282 }
1283 }
1284
1285 addObjectToInventory(obj, room);
1286 putOwner(obj, VAR(VAR_EGO));
1287 putClass(obj, kObjectClassUntouchable, 1);
1288 putState(obj, 1);
1289 markObjectRectAsDirty(obj);
1290 clearDrawObjectQueue();
1291 runInventoryScript(obj);
1292 }
1293
o6_loadRoomWithEgo()1294 void ScummEngine_v6::o6_loadRoomWithEgo() {
1295 Actor *a;
1296 int obj, room, x, y;
1297
1298 y = pop();
1299 x = pop();
1300
1301 obj = popRoomAndObj(&room);
1302
1303 a = derefActor(VAR(VAR_EGO), "o6_loadRoomWithEgo");
1304 a->putActor(0, 0, room);
1305 _egoPositioned = false;
1306
1307 VAR(VAR_WALKTO_OBJ) = obj;
1308 startScene(a->_room, a, obj);
1309 VAR(VAR_WALKTO_OBJ) = 0;
1310
1311 if (_game.version == 6) {
1312 camera._cur.x = camera._dest.x = a->getPos().x;
1313 setCameraFollows(a, (_game.heversion >= 60));
1314 }
1315
1316 _fullRedraw = true;
1317
1318 if (x != -1 && x != 0x7FFFFFFF) {
1319 a->startWalkActor(x, y, -1);
1320 }
1321 }
1322
o6_getRandomNumber()1323 void ScummEngine_v6::o6_getRandomNumber() {
1324 int rnd;
1325 rnd = _rnd.getRandomNumber(ABS(pop()));
1326 if (VAR_RANDOM_NR != 0xFF)
1327 VAR(VAR_RANDOM_NR) = rnd;
1328 push(rnd);
1329 }
1330
o6_getRandomNumberRange()1331 void ScummEngine_v6::o6_getRandomNumberRange() {
1332 int max = pop();
1333 int min = pop();
1334 int rnd = _rnd.getRandomNumberRng(min, max);
1335 if (VAR_RANDOM_NR != 0xFF)
1336 VAR(VAR_RANDOM_NR) = rnd;
1337 push(rnd);
1338 }
1339
o6_isScriptRunning()1340 void ScummEngine_v6::o6_isScriptRunning() {
1341 push(isScriptRunning(pop()));
1342 }
1343
o6_isRoomScriptRunning()1344 void ScummEngine_v6::o6_isRoomScriptRunning() {
1345 push(isRoomScriptRunning(pop()));
1346 }
1347
o6_getActorMoving()1348 void ScummEngine_v6::o6_getActorMoving() {
1349 Actor *a = derefActor(pop(), "o6_getActorMoving");
1350 push(a->_moving);
1351 }
1352
o6_getActorRoom()1353 void ScummEngine_v6::o6_getActorRoom() {
1354 int act = pop();
1355
1356 if (act == 0) {
1357 // This case occurs at the very least in COMI. That's because in COMI's script 28,
1358 // there is a check which looks as follows:
1359 // if (((VAR_TALK_ACTOR != 0) && (VAR_HAVE_MSG == 1)) &&
1360 // (getActorRoom(VAR_TALK_ACTOR) == VAR_ROOM))
1361 // Due to the way this is represented in bytecode, the engine cannot
1362 // short circuit. Hence, even though this would be perfectly fine code
1363 // in C/C++, here it can (and does) lead to getActorRoom(0) being
1364 // invoked. We silently ignore this.
1365 push(0);
1366 return;
1367 }
1368
1369 if (act == 255) {
1370 // This case also occurs in COMI...
1371 push(0);
1372 return;
1373 }
1374
1375 Actor *a = derefActor(act, "o6_getActorRoom");
1376 push(a->_room);
1377 }
1378
o6_getActorWalkBox()1379 void ScummEngine_v6::o6_getActorWalkBox() {
1380 Actor *a = derefActor(pop(), "o6_getActorWalkBox");
1381 push(a->_ignoreBoxes ? 0 : a->_walkbox);
1382 }
1383
o6_getActorCostume()1384 void ScummEngine_v6::o6_getActorCostume() {
1385 Actor *a = derefActor(pop(), "o6_getActorCostume");
1386 push(a->_costume);
1387 }
1388
o6_getActorElevation()1389 void ScummEngine_v6::o6_getActorElevation() {
1390 Actor *a = derefActor(pop(), "o6_getActorElevation");
1391 push(a->getElevation());
1392 }
1393
o6_getActorWidth()1394 void ScummEngine_v6::o6_getActorWidth() {
1395 Actor *a = derefActor(pop(), "o6_getActorWidth");
1396 push(a->_width);
1397 }
1398
o6_getActorScaleX()1399 void ScummEngine_v6::o6_getActorScaleX() {
1400 Actor *a = derefActor(pop(), "o6_getActorScaleX");
1401 push(a->_scalex);
1402 }
1403
o6_getActorAnimCounter()1404 void ScummEngine_v6::o6_getActorAnimCounter() {
1405 Actor *a = derefActor(pop(), "o6_getActorAnimCounter");
1406 push(a->_cost.animCounter);
1407 }
1408
o6_getAnimateVariable()1409 void ScummEngine_v6::o6_getAnimateVariable() {
1410 int var = pop();
1411 Actor *a = derefActor(pop(), "o6_getAnimateVariable");
1412
1413 // WORKAROUND: In Backyard Baseball 2001 and 2003,
1414 // bunting a foul ball as Pete Wheeler may softlock the game
1415 // with an animation loop if the ball goes way into
1416 // the left or right field line.
1417 //
1418 // This is a script bug because Pete's actor variable never
1419 // sets to 1 in this condition and script room-4-2105
1420 // (or room-3-2105 in 2003) will always break.
1421 // We fix that by forcing Pete to play the return animation
1422 // regardless if the ball's foul or not.
1423 if ((_game.id == GID_BASEBALL2001 || _game.id == GID_BASEBALL2003) && \
1424 _currentRoom == ((_game.id == GID_BASEBALL2001) ? 4 : 3) && \
1425 vm.slot[_currentScript].number == 2105 && \
1426 a->_costume == ((_game.id == GID_BASEBALL2001) ? 107 : 99) && \
1427 // Room variable 5 to ensure this workaround executes only once at
1428 // the beginning of the script and room variable 22 to check if we
1429 // are bunting.
1430 readVar(0x8000 + 5) != 0 && readVar(0x8000 + 22) == 4)
1431 push(1);
1432 else
1433 push(a->getAnimVar(var));
1434 }
1435
o6_isActorInBox()1436 void ScummEngine_v6::o6_isActorInBox() {
1437 int box = pop();
1438 Actor *a = derefActor(pop(), "o6_isActorInBox");
1439 push(checkXYInBoxBounds(box, a->getPos().x, a->getPos().y));
1440 }
1441
o6_getActorLayer()1442 void ScummEngine_v6::o6_getActorLayer() {
1443 Actor *a = derefActor(pop(), "getActorLayer");
1444 push(a->_layer);
1445 }
1446
o6_getObjectX()1447 void ScummEngine_v6::o6_getObjectX() {
1448 push(getObjX(pop()));
1449 }
1450
o6_getObjectY()1451 void ScummEngine_v6::o6_getObjectY() {
1452 push(getObjY(pop()));
1453 }
1454
o6_getObjectOldDir()1455 void ScummEngine_v6::o6_getObjectOldDir() {
1456 push(getObjOldDir(pop()));
1457 }
1458
o6_getObjectNewDir()1459 void ScummEngine_v6::o6_getObjectNewDir() {
1460 push(getObjNewDir(pop()));
1461 }
1462
o6_findInventory()1463 void ScummEngine_v6::o6_findInventory() {
1464 int idx = pop();
1465 int owner = pop();
1466 push(findInventory(owner, idx));
1467 }
1468
o6_getInventoryCount()1469 void ScummEngine_v6::o6_getInventoryCount() {
1470 push(getInventoryCount(pop()));
1471 }
1472
o6_getVerbFromXY()1473 void ScummEngine_v6::o6_getVerbFromXY() {
1474 int y = pop();
1475 int x = pop();
1476 int over = findVerbAtPos(x, y);
1477 if (over)
1478 over = _verbs[over].verbid;
1479 push(over);
1480 }
1481
o6_beginOverride()1482 void ScummEngine_v6::o6_beginOverride() {
1483 beginOverride();
1484 _skipVideo = 0;
1485 }
1486
o6_endOverride()1487 void ScummEngine_v6::o6_endOverride() {
1488 endOverride();
1489 }
1490
o6_setObjectName()1491 void ScummEngine_v6::o6_setObjectName() {
1492 int obj = pop();
1493 setObjectName(obj);
1494 }
1495
o6_isSoundRunning()1496 void ScummEngine_v6::o6_isSoundRunning() {
1497 int snd = pop();
1498
1499 if (snd)
1500 snd = _sound->isSoundRunning(snd);
1501
1502 push(snd);
1503 }
1504
o6_setBoxFlags()1505 void ScummEngine_v6::o6_setBoxFlags() {
1506 int table[65];
1507 int num, value;
1508
1509 value = pop();
1510 num = getStackList(table, ARRAYSIZE(table));
1511
1512 while (--num >= 0) {
1513 setBoxFlags(table[num], value);
1514 }
1515 }
1516
o6_createBoxMatrix()1517 void ScummEngine_v6::o6_createBoxMatrix() {
1518 createBoxMatrix();
1519
1520 if ((_game.id == GID_DIG) || (_game.id == GID_CMI))
1521 putActors();
1522 }
1523
o6_resourceRoutines()1524 void ScummEngine_v6::o6_resourceRoutines() {
1525 int resid;
1526
1527 byte subOp = fetchScriptByte();
1528
1529 switch (subOp) {
1530 case 100: // SO_LOAD_SCRIPT
1531 resid = pop();
1532 if (_game.version >= 7)
1533 if (resid >= _numGlobalScripts)
1534 break;
1535 ensureResourceLoaded(rtScript, resid);
1536 break;
1537 case 101: // SO_LOAD_SOUND
1538 resid = pop();
1539 ensureResourceLoaded(rtSound, resid);
1540 break;
1541 case 102: // SO_LOAD_COSTUME
1542 resid = pop();
1543 ensureResourceLoaded(rtCostume, resid);
1544 break;
1545 case 103: // SO_LOAD_ROOM
1546 resid = pop();
1547 ensureResourceLoaded(rtRoom, resid);
1548 break;
1549 case 104: // SO_NUKE_SCRIPT
1550 resid = pop();
1551 if (_game.version >= 7)
1552 if (resid >= _numGlobalScripts)
1553 break;
1554 _res->setResourceCounter(rtScript, resid, 0x7F);
1555 break;
1556 case 105: // SO_NUKE_SOUND
1557 resid = pop();
1558 _res->setResourceCounter(rtSound, resid, 0x7F);
1559 break;
1560 case 106: // SO_NUKE_COSTUME
1561 resid = pop();
1562 _res->setResourceCounter(rtCostume, resid, 0x7F);
1563 break;
1564 case 107: // SO_NUKE_ROOM
1565 resid = pop();
1566 _res->setResourceCounter(rtRoom, resid, 0x7F);
1567 break;
1568 case 108: // SO_LOCK_SCRIPT
1569 resid = pop();
1570 if (resid >= _numGlobalScripts)
1571 break;
1572 _res->lock(rtScript, resid);
1573 break;
1574 case 109: // SO_LOCK_SOUND
1575 resid = pop();
1576 _res->lock(rtSound, resid);
1577 break;
1578 case 110: // SO_LOCK_COSTUME
1579 resid = pop();
1580 _res->lock(rtCostume, resid);
1581 break;
1582 case 111: // SO_LOCK_ROOM
1583 resid = pop();
1584 if (resid > 0x7F)
1585 resid = _resourceMapper[resid & 0x7F];
1586 _res->lock(rtRoom, resid);
1587 break;
1588 case 112: // SO_UNLOCK_SCRIPT
1589 resid = pop();
1590 if (resid >= _numGlobalScripts)
1591 break;
1592 _res->unlock(rtScript, resid);
1593 break;
1594 case 113: // SO_UNLOCK_SOUND
1595 resid = pop();
1596 _res->unlock(rtSound, resid);
1597 break;
1598 case 114: // SO_UNLOCK_COSTUME
1599 resid = pop();
1600 _res->unlock(rtCostume, resid);
1601 break;
1602 case 115: // SO_UNLOCK_ROOM
1603 resid = pop();
1604 if (resid > 0x7F)
1605 resid = _resourceMapper[resid & 0x7F];
1606 _res->unlock(rtRoom, resid);
1607 break;
1608 case 116: // SO_CLEAR_HEAP
1609 /* this is actually a scumm message */
1610 error("clear heap not working yet");
1611 break;
1612 case 117: // SO_LOAD_CHARSET
1613 resid = pop();
1614 loadCharset(resid);
1615 break;
1616 case 118: // SO_NUKE_CHARSET
1617 resid = pop();
1618 nukeCharset(resid);
1619 break;
1620 case 119: // SO_LOAD_OBJECT
1621 {
1622 int room, obj = popRoomAndObj(&room);
1623 loadFlObject(obj, room);
1624 break;
1625 }
1626 default:
1627 error("o6_resourceRoutines: default case %d", subOp);
1628 }
1629 }
1630
1631
o6_roomOps()1632 void ScummEngine_v6::o6_roomOps() {
1633 int a, b, c, d, e;
1634
1635 byte subOp = fetchScriptByte();
1636
1637 switch (subOp) {
1638 case 172: // SO_ROOM_SCROLL
1639 b = pop();
1640 a = pop();
1641 if (a < (_screenWidth / 2))
1642 a = (_screenWidth / 2);
1643 if (b < (_screenWidth / 2))
1644 b = (_screenWidth / 2);
1645 if (a > _roomWidth - (_screenWidth / 2))
1646 a = _roomWidth - (_screenWidth / 2);
1647 if (b > _roomWidth - (_screenWidth / 2))
1648 b = _roomWidth - (_screenWidth / 2);
1649 VAR(VAR_CAMERA_MIN_X) = a;
1650 VAR(VAR_CAMERA_MAX_X) = b;
1651 break;
1652
1653 case 174: // SO_ROOM_SCREEN
1654 b = pop();
1655 a = pop();
1656 initScreens(a, b);
1657 break;
1658
1659 case 175: // SO_ROOM_PALETTE
1660 d = pop();
1661 c = pop();
1662 b = pop();
1663 a = pop();
1664 setPalColor(d, a, b, c);
1665 break;
1666
1667 case 176: // SO_ROOM_SHAKE_ON
1668 setShake(1);
1669 break;
1670
1671 case 177: // SO_ROOM_SHAKE_OFF
1672 setShake(0);
1673 break;
1674
1675 case 179: // SO_ROOM_INTENSITY
1676 c = pop();
1677 b = pop();
1678 a = pop();
1679 // Prevent assert() error with corner case, fixes bug #9871
1680 if (_game.id == GID_FT && _roomResource == 0)
1681 break;
1682 darkenPalette(a, a, a, b, c);
1683 break;
1684
1685 case 180: // SO_ROOM_SAVEGAME
1686 _saveTemporaryState = true;
1687 _saveLoadSlot = pop();
1688 _saveLoadFlag = pop();
1689 if (_game.id == GID_TENTACLE)
1690 _saveSound = (_saveLoadSlot != 0);
1691 break;
1692
1693 case 181: // SO_ROOM_FADE
1694 a = pop();
1695 if (a) {
1696 _switchRoomEffect = (byte)(a & 0xFF);
1697 _switchRoomEffect2 = (byte)(a >> 8);
1698 } else {
1699 fadeIn(_newEffect);
1700 }
1701 break;
1702
1703 case 182: // SO_RGB_ROOM_INTENSITY
1704 e = pop();
1705 d = pop();
1706 c = pop();
1707 b = pop();
1708 a = pop();
1709 darkenPalette(a, b, c, d, e);
1710 break;
1711
1712 case 183: // SO_ROOM_SHADOW
1713 e = pop();
1714 d = pop();
1715 c = pop();
1716 b = pop();
1717 a = pop();
1718 setShadowPalette(a, b, c, d, e, 0, 256);
1719 break;
1720
1721 case 184: // SO_SAVE_STRING
1722 error("save string not implemented");
1723 break;
1724
1725 case 185: // SO_LOAD_STRING
1726 error("load string not implemented");
1727 break;
1728
1729 case 186: // SO_ROOM_TRANSFORM
1730 d = pop();
1731 c = pop();
1732 b = pop();
1733 a = pop();
1734 palManipulateInit(a, b, c, d);
1735 break;
1736
1737 case 187: // SO_CYCLE_SPEED
1738 b = pop();
1739 a = pop();
1740 assertRange(1, a, 16, "o6_roomOps: 187: color cycle");
1741 _colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0;
1742 break;
1743
1744 case 213: // SO_ROOM_NEW_PALETTE
1745 a = pop();
1746
1747 // This opcode is used when turning off noir mode in Sam & Max,
1748 // but since our implementation of this feature doesn't change
1749 // the original palette there's no need to reload it. Doing it
1750 // this way, we avoid some graphics glitches that the original
1751 // interpreter had.
1752
1753 if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 64)
1754 setDirtyColors(0, 255);
1755 else
1756 setCurrentPalette(a);
1757 break;
1758 default:
1759 error("o6_roomOps: default case %d", subOp);
1760 }
1761 }
1762
o6_actorOps()1763 void ScummEngine_v6::o6_actorOps() {
1764 Actor *a;
1765 int i, j, k;
1766 int args[8];
1767
1768 byte subOp = fetchScriptByte();
1769 if (subOp == 197) {
1770 _curActor = pop();
1771 return;
1772 }
1773
1774 a = derefActorSafe(_curActor, "o6_actorOps");
1775 if (!a)
1776 return;
1777
1778 switch (subOp) {
1779 case 76: // SO_COSTUME
1780 a->setActorCostume(pop());
1781 break;
1782 case 77: // SO_STEP_DIST
1783 j = pop();
1784 i = pop();
1785 a->setActorWalkSpeed(i, j);
1786 break;
1787 case 78: // SO_SOUND
1788 k = getStackList(args, ARRAYSIZE(args));
1789 for (i = 0; i < k; i++)
1790 a->_sound[i] = args[i];
1791 break;
1792 case 79: // SO_WALK_ANIMATION
1793 a->_walkFrame = pop();
1794 break;
1795 case 80: // SO_TALK_ANIMATION
1796 a->_talkStopFrame = pop();
1797 a->_talkStartFrame = pop();
1798 break;
1799 case 81: // SO_STAND_ANIMATION
1800 a->_standFrame = pop();
1801 break;
1802 case 82: // SO_ANIMATION
1803 // dummy case in scumm6
1804 pop();
1805 pop();
1806 pop();
1807 break;
1808 case 83: // SO_DEFAULT
1809 a->initActor(0);
1810 break;
1811 case 84: // SO_ELEVATION
1812 a->setElevation(pop());
1813 break;
1814 case 85: // SO_ANIMATION_DEFAULT
1815 a->_initFrame = 1;
1816 a->_walkFrame = 2;
1817 a->_standFrame = 3;
1818 a->_talkStartFrame = 4;
1819 a->_talkStopFrame = 5;
1820 break;
1821 case 86: // SO_PALETTE
1822 j = pop();
1823 i = pop();
1824 assertRange(0, i, 255, "o6_actorOps: palette slot");
1825 a->setPalette(i, j);
1826 break;
1827 case 87: // SO_TALK_COLOR
1828 a->_talkColor = pop();
1829 break;
1830 case 88: // SO_ACTOR_NAME
1831 loadPtrToResource(rtActorName, a->_number, NULL);
1832 break;
1833 case 89: // SO_INIT_ANIMATION
1834 a->_initFrame = pop();
1835 break;
1836 case 91: // SO_ACTOR_WIDTH
1837 a->_width = pop();
1838 break;
1839 case 92: // SO_SCALE
1840 i = pop();
1841 a->setScale(i, i);
1842 break;
1843 case 93: // SO_NEVER_ZCLIP
1844 a->_forceClip = 0;
1845 break;
1846 case 225: // SO_ALWAYS_ZCLIP
1847 case 94: // SO_ALWAYS_ZCLIP
1848 a->_forceClip = pop();
1849 break;
1850 case 95: // SO_IGNORE_BOXES
1851 a->_ignoreBoxes = 1;
1852 a->_forceClip = (_game.version >= 7) ? 100 : 0;
1853 if (a->isInCurrentRoom())
1854 a->putActor();
1855 break;
1856 case 96: // SO_FOLLOW_BOXES
1857 a->_ignoreBoxes = 0;
1858 a->_forceClip = (_game.version >= 7) ? 100 : 0;
1859 if (a->isInCurrentRoom())
1860 a->putActor();
1861 break;
1862 case 97: // SO_ANIMATION_SPEED
1863 a->setAnimSpeed(pop());
1864 break;
1865 case 98: // SO_SHADOW
1866 a->_shadowMode = pop();
1867 break;
1868 case 99: // SO_TEXT_OFFSET
1869 a->_talkPosY = pop();
1870 a->_talkPosX = pop();
1871 break;
1872 case 198: // SO_ACTOR_VARIABLE
1873 i = pop();
1874 a->setAnimVar(pop(), i);
1875 break;
1876 case 215: // SO_ACTOR_IGNORE_TURNS_ON
1877 a->_ignoreTurns = true;
1878 break;
1879 case 216: // SO_ACTOR_IGNORE_TURNS_OFF
1880 a->_ignoreTurns = false;
1881 break;
1882 case 217: // SO_ACTOR_NEW
1883 a->initActor(2);
1884 break;
1885 case 227: // SO_ACTOR_DEPTH
1886 a->_layer = pop();
1887 break;
1888 case 228: // SO_ACTOR_WALK_SCRIPT
1889 a->_walkScript = pop();
1890 break;
1891 case 229: // SO_ACTOR_STOP
1892 a->stopActorMoving();
1893 a->startAnimActor(a->_standFrame);
1894 break;
1895 case 230: /* set direction */
1896 a->_moving &= ~MF_TURN;
1897 a->setDirection(pop());
1898 break;
1899 case 231: /* turn to direction */
1900 a->turnToDirection(pop());
1901 break;
1902 case 233: // SO_ACTOR_WALK_PAUSE
1903 a->_moving |= MF_FROZEN;
1904 break;
1905 case 234: // SO_ACTOR_WALK_RESUME
1906 a->_moving &= ~MF_FROZEN;
1907 break;
1908 case 235: // SO_ACTOR_TALK_SCRIPT
1909 a->_talkScript = pop();
1910 break;
1911 default:
1912 error("o6_actorOps: default case %d", subOp);
1913 }
1914 }
1915
o6_verbOps()1916 void ScummEngine_v6::o6_verbOps() {
1917 int slot, a, b;
1918 VerbSlot *vs;
1919
1920 byte subOp = fetchScriptByte();
1921 if (subOp == 196) {
1922 _curVerb = pop();
1923 _curVerbSlot = getVerbSlot(_curVerb, 0);
1924 assertRange(0, _curVerbSlot, _numVerbs - 1, "new verb slot");
1925 return;
1926 }
1927 vs = &_verbs[_curVerbSlot];
1928 slot = _curVerbSlot;
1929 switch (subOp) {
1930 case 124: // SO_VERB_IMAGE
1931 a = pop();
1932 if (_curVerbSlot) {
1933 setVerbObject(_roomResource, a, slot);
1934 vs->type = kImageVerbType;
1935 if (_game.heversion >= 61)
1936 vs->imgindex = a;
1937 }
1938 break;
1939 case 125: // SO_VERB_NAME
1940 loadPtrToResource(rtVerb, slot, NULL);
1941 vs->type = kTextVerbType;
1942 vs->imgindex = 0;
1943 break;
1944 case 126: // SO_VERB_COLOR
1945 vs->color = pop();
1946 break;
1947 case 127: // SO_VERB_HICOLOR
1948 vs->hicolor = pop();
1949 break;
1950 case 128: // SO_VERB_AT
1951 vs->curRect.top = pop();
1952 vs->curRect.left = vs->origLeft = pop();
1953 break;
1954 case 129: // SO_VERB_ON
1955 vs->curmode = 1;
1956 break;
1957 case 130: // SO_VERB_OFF
1958 vs->curmode = 0;
1959 break;
1960 case 131: // SO_VERB_DELETE
1961 if (_game.heversion >= 60) {
1962 slot = getVerbSlot(pop(), 0);
1963 }
1964 killVerb(slot);
1965 break;
1966 case 132: // SO_VERB_NEW
1967 slot = getVerbSlot(_curVerb, 0);
1968 if (slot == 0) {
1969 for (slot = 1; slot < _numVerbs; slot++) {
1970 if (_verbs[slot].verbid == 0)
1971 break;
1972 }
1973 if (slot == _numVerbs)
1974 error("Too many verbs");
1975 _curVerbSlot = slot;
1976 }
1977 vs = &_verbs[slot];
1978 vs->verbid = _curVerb;
1979 vs->color = 2;
1980 vs->hicolor = 0;
1981 vs->dimcolor = 8;
1982 vs->type = kTextVerbType;
1983 vs->charset_nr = _string[0]._default.charset;
1984 vs->curmode = 0;
1985 vs->saveid = 0;
1986 vs->key = 0;
1987 vs->center = 0;
1988 vs->imgindex = 0;
1989 break;
1990 case 133: // SO_VERB_DIMCOLOR
1991 vs->dimcolor = pop();
1992 break;
1993 case 134: // SO_VERB_DIM
1994 vs->curmode = 2;
1995 break;
1996 case 135: // SO_VERB_KEY
1997 vs->key = pop();
1998 break;
1999 case 136: // SO_VERB_CENTER
2000 vs->center = 1;
2001 break;
2002 case 137: // SO_VERB_NAME_STR
2003 a = pop();
2004 if (a == 0) {
2005 loadPtrToResource(rtVerb, slot, (const byte *)"");
2006 } else {
2007 loadPtrToResource(rtVerb, slot, getStringAddress(a));
2008 }
2009 vs->type = kTextVerbType;
2010 vs->imgindex = 0;
2011 break;
2012 case 139: // SO_VERB_IMAGE_IN_ROOM
2013 b = pop();
2014 a = pop();
2015
2016 if (slot && a != vs->imgindex) {
2017 setVerbObject(b, a, slot);
2018 vs->type = kImageVerbType;
2019 vs->imgindex = a;
2020 }
2021 break;
2022 case 140: // SO_VERB_BAKCOLOR
2023 vs->bkcolor = pop();
2024 break;
2025 case 255:
2026 drawVerb(slot, 0);
2027 verbMouseOver(0);
2028 break;
2029 default:
2030 error("o6_verbops: default case %d", subOp);
2031 }
2032 }
2033
o6_getActorFromXY()2034 void ScummEngine_v6::o6_getActorFromXY() {
2035 int y = pop();
2036 int x = pop();
2037 int r = getActorFromPos(x, y);
2038 push(r);
2039 }
2040
o6_findObject()2041 void ScummEngine_v6::o6_findObject() {
2042 int y = pop();
2043 int x = pop();
2044 int r = findObject(x, y);
2045 push(r);
2046 }
2047
o6_pseudoRoom()2048 void ScummEngine_v6::o6_pseudoRoom() {
2049 int list[100];
2050 int num, a, value;
2051
2052 num = getStackList(list, ARRAYSIZE(list));
2053 value = pop();
2054
2055 while (--num >= 0) {
2056 a = list[num];
2057 if (a > 0x7F)
2058 _resourceMapper[a & 0x7F] = value;
2059 }
2060 }
2061
o6_getVerbEntrypoint()2062 void ScummEngine_v6::o6_getVerbEntrypoint() {
2063 int e = pop();
2064 int v = pop();
2065 push(getVerbEntrypoint(v, e));
2066 }
2067
o6_arrayOps()2068 void ScummEngine_v6::o6_arrayOps() {
2069 byte subOp = fetchScriptByte();
2070 int array = fetchScriptWord();
2071 int b, c, d, len;
2072 byte *data;
2073 int list[128];
2074
2075 switch (subOp) {
2076 case 205: // SO_ASSIGN_STRING
2077 b = pop();
2078 len = resStrLen(_scriptPointer);
2079 data = defineArray(array, kStringArray, 0, len + 1);
2080 copyScriptString(data + b);
2081 break;
2082 case 208: // SO_ASSIGN_INT_LIST
2083 b = pop();
2084 c = pop();
2085 d = readVar(array);
2086 if (d == 0) {
2087 defineArray(array, kIntArray, 0, b + c);
2088 }
2089 while (c--) {
2090 writeArray(array, 0, b + c, pop());
2091 }
2092 break;
2093 case 212: // SO_ASSIGN_2DIM_LIST
2094 b = pop();
2095 len = getStackList(list, ARRAYSIZE(list));
2096 d = readVar(array);
2097 if (d == 0)
2098 error("Must DIM a two dimensional array before assigning");
2099 c = pop();
2100 while (--len >= 0) {
2101 writeArray(array, c, b + len, list[len]);
2102 }
2103 break;
2104 default:
2105 error("o6_arrayOps: default case %d (array %d)", subOp, array);
2106 }
2107 }
2108
o6_saveRestoreVerbs()2109 void ScummEngine_v6::o6_saveRestoreVerbs() {
2110 int a, b, c;
2111 int slot, slot2;
2112
2113 c = pop();
2114 b = pop();
2115 a = pop();
2116
2117 byte subOp = fetchScriptByte();
2118 if (_game.version == 8) {
2119 subOp = (subOp - 141) + 0xB4;
2120 }
2121
2122 switch (subOp) {
2123 case 141: // SO_SAVE_VERBS
2124 while (a <= b) {
2125 slot = getVerbSlot(a, 0);
2126 if (slot && _verbs[slot].saveid == 0) {
2127 _verbs[slot].saveid = c;
2128 drawVerb(slot, 0);
2129 verbMouseOver(0);
2130 }
2131 a++;
2132 }
2133 break;
2134 case 142: // SO_RESTORE_VERBS
2135 while (a <= b) {
2136 slot = getVerbSlot(a, c);
2137 if (slot) {
2138 slot2 = getVerbSlot(a, 0);
2139 if (slot2)
2140 killVerb(slot2);
2141 slot = getVerbSlot(a, c);
2142 _verbs[slot].saveid = 0;
2143 drawVerb(slot, 0);
2144 verbMouseOver(0);
2145 }
2146 a++;
2147 }
2148 break;
2149 case 143: // SO_DELETE_VERBS
2150 while (a <= b) {
2151 slot = getVerbSlot(a, c);
2152 if (slot)
2153 killVerb(slot);
2154 a++;
2155 }
2156 break;
2157 default:
2158 error("o6_saveRestoreVerbs: default case");
2159 }
2160 }
2161
o6_drawBox()2162 void ScummEngine_v6::o6_drawBox() {
2163 int x, y, x2, y2, color;
2164 color = pop();
2165 y2 = pop();
2166 x2 = pop();
2167 y = pop();
2168 x = pop();
2169 drawBox(x, y, x2, y2, color);
2170 }
2171
o6_wait()2172 void ScummEngine_v6::o6_wait() {
2173 int actnum;
2174 int offs = -2;
2175 Actor *a;
2176
2177 byte subOp = fetchScriptByte();
2178
2179 switch (subOp) {
2180 case 168: // SO_WAIT_FOR_ACTOR Wait for actor
2181 offs = fetchScriptWordSigned();
2182 actnum = pop();
2183 a = derefActor(actnum, "o6_wait:168");
2184 if (_game.version >= 7) {
2185 if (a->isInCurrentRoom() && a->_moving)
2186 break;
2187 } else {
2188 if (a->_moving)
2189 break;
2190 }
2191 return;
2192 case 169: // SO_WAIT_FOR_MESSAGE Wait for message
2193 if (VAR(VAR_HAVE_MSG))
2194 break;
2195 return;
2196 case 170: // SO_WAIT_FOR_CAMERA Wait for camera
2197 if (_game.version >= 7) {
2198 if (camera._dest != camera._cur)
2199 break;
2200 } else {
2201 if (camera._cur.x / 8 != camera._dest.x / 8)
2202 break;
2203 }
2204
2205 return;
2206 case 171: // SO_WAIT_FOR_SENTENCE
2207 if (_sentenceNum) {
2208 if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
2209 return;
2210 break;
2211 }
2212 if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
2213 return;
2214 break;
2215 case 226: // SO_WAIT_FOR_ANIMATION
2216 offs = fetchScriptWordSigned();
2217 actnum = pop();
2218 a = derefActor(actnum, "o6_wait:226");
2219 if (a->isInCurrentRoom() && a->_needRedraw)
2220 break;
2221 return;
2222 case 232: // SO_WAIT_FOR_TURN
2223 // WORKAROUND for bug #819: An angle will often be received as the
2224 // actor number due to script bugs in The Dig. In all cases where this
2225 // occurs, _curActor is set just before it, so we can use it instead.
2226 //
2227 // For now, if the value passed in is divisible by 45, assume it is an
2228 // angle, and use _curActor as the actor to wait for.
2229 offs = fetchScriptWordSigned();
2230 actnum = pop();
2231 if (actnum % 45 == 0) {
2232 actnum = _curActor;
2233 }
2234 a = derefActor(actnum, "o6_wait:232b");
2235 if (a->isInCurrentRoom() && a->_moving & MF_TURN)
2236 break;
2237 return;
2238 default:
2239 error("o6_wait: default case 0x%x", subOp);
2240 }
2241
2242 _scriptPointer += offs;
2243 o6_breakHere();
2244 }
2245
o6_soundKludge()2246 void ScummEngine_v6::o6_soundKludge() {
2247 int list[16];
2248 int num = getStackList(list, ARRAYSIZE(list));
2249
2250 _sound->soundKludge(list, num);
2251
2252 // WORKAROUND for bug #2438: The room-11-2016 script contains a
2253 // slight bug causing it to busy-wait for a sound to finish. Even under
2254 // the best of circumstances, this will cause the game to hang briefly.
2255 // On platforms where threading is cooperative, it will cause the game
2256 // to hang indefinitely. We identify the buggy part of the script by
2257 // looking for a soundKludge() opcode immediately followed by a jump.
2258
2259 if (_game.id == GID_CMI && _roomResource == 11 && vm.slot[_currentScript].number == 2016 && *_scriptPointer == 0x66) {
2260 debug(3, "Working around script bug in room-11-2016");
2261 o6_breakHere();
2262 }
2263 }
2264
o6_isAnyOf()2265 void ScummEngine_v6::o6_isAnyOf() {
2266 int list[100];
2267 int num;
2268 int32 val;
2269
2270 num = getStackList(list, ARRAYSIZE(list));
2271 val = pop();
2272
2273 while (--num >= 0) {
2274 if (list[num] == val) {
2275 push(1);
2276 return;
2277 }
2278 }
2279
2280 push(0);
2281 }
2282
o6_systemOps()2283 void ScummEngine_v6::o6_systemOps() {
2284 byte subOp = fetchScriptByte();
2285
2286 switch (subOp) {
2287 case 158: // SO_RESTART
2288 restart();
2289 break;
2290 case 159: // SO_PAUSE
2291 pauseGame();
2292 break;
2293 case 160: // SO_QUIT
2294 quitGame();
2295 break;
2296 default:
2297 error("o6_systemOps invalid case %d", subOp);
2298 }
2299 }
2300
o6_delay()2301 void ScummEngine_v6::o6_delay() {
2302 uint32 delay = (uint16)pop();
2303 vm.slot[_currentScript].delay = delay;
2304 vm.slot[_currentScript].status = ssPaused;
2305 o6_breakHere();
2306 }
2307
o6_delaySeconds()2308 void ScummEngine_v6::o6_delaySeconds() {
2309 uint32 delay = (uint32)pop();
2310 delay = delay * 60;
2311 vm.slot[_currentScript].delay = delay;
2312 vm.slot[_currentScript].status = ssPaused;
2313 o6_breakHere();
2314 }
2315
o6_delayMinutes()2316 void ScummEngine_v6::o6_delayMinutes() {
2317 uint32 delay = (uint16)pop() * 3600;
2318 vm.slot[_currentScript].delay = delay;
2319 vm.slot[_currentScript].status = ssPaused;
2320 o6_breakHere();
2321 }
2322
o6_stopSentence()2323 void ScummEngine_v6::o6_stopSentence() {
2324 _sentenceNum = 0;
2325 stopScript(VAR(VAR_SENTENCE_SCRIPT));
2326 clearClickedStatus();
2327 }
2328
o6_printLine()2329 void ScummEngine_v6::o6_printLine() {
2330 _actorToPrintStrFor = 0xFF;
2331 decodeParseString(0, 0);
2332 }
2333
o6_printText()2334 void ScummEngine_v6::o6_printText() {
2335 decodeParseString(1, 0);
2336 }
2337
o6_printDebug()2338 void ScummEngine_v6::o6_printDebug() {
2339 decodeParseString(2, 0);
2340 }
2341
o6_printSystem()2342 void ScummEngine_v6::o6_printSystem() {
2343 decodeParseString(3, 0);
2344 }
2345
o6_printActor()2346 void ScummEngine_v6::o6_printActor() {
2347 decodeParseString(0, 1);
2348 }
2349
o6_printEgo()2350 void ScummEngine_v6::o6_printEgo() {
2351 push(VAR(VAR_EGO));
2352 decodeParseString(0, 1);
2353 }
2354
o6_talkActor()2355 void ScummEngine_v6::o6_talkActor() {
2356 int offset = _scriptPointer - _scriptOrgPointer;
2357
2358 // WORKAROUND for bug #1452: see below for detailed description
2359 if (_forcedWaitForMessage) {
2360 if (VAR(VAR_HAVE_MSG)) {
2361 _scriptPointer--;
2362 o6_breakHere();
2363 return;
2364 }
2365
2366 _forcedWaitForMessage = false;
2367 _scriptPointer += resStrLen(_scriptPointer) + 1;
2368
2369 return;
2370 }
2371
2372 _actorToPrintStrFor = pop();
2373
2374 // WORKAROUND for bug #3803: "DOTT: Bernard impersonating LaVerne"
2375 // Original script did not check for VAR_EGO == 2 before executing
2376 // a talkActor opcode.
2377 if (_game.id == GID_TENTACLE && vm.slot[_currentScript].number == 307
2378 && VAR(VAR_EGO) != 2 && _actorToPrintStrFor == 2) {
2379 _scriptPointer += resStrLen(_scriptPointer) + 1;
2380 return;
2381 }
2382
2383 _string[0].loadDefault();
2384 actorTalk(_scriptPointer);
2385
2386 // WORKAROUND for bug #1452: "DIG: Missing subtitles when talking to Brink"
2387 // Original script does not have wait.waitForMessage() after several messages:
2388 //
2389 // [011A] (5D) if (getActorCostume(VAR_EGO) == 1) {
2390 // [0126] (BA) talkActor("/STOP.008/Low out.",3)
2391 // [013D] (A9) wait.waitForMessage()
2392 // [013F] (5D) } else if (var227 == 0) {
2393 // [014C] (BA) talkActor("/STOP.009/Never mind.",3)
2394 // [0166] (73) } else {
2395 //
2396 // Here we simulate that opcode.
2397 if (_game.id == GID_DIG && vm.slot[_currentScript].number == 88) {
2398 if (offset == 0x158 || offset == 0x214 || offset == 0x231 || offset == 0x278) {
2399 _forcedWaitForMessage = true;
2400 _scriptPointer--;
2401
2402 return;
2403 }
2404 }
2405 _scriptPointer += resStrLen(_scriptPointer) + 1;
2406 }
2407
o6_talkEgo()2408 void ScummEngine_v6::o6_talkEgo() {
2409 push(VAR(VAR_EGO));
2410 o6_talkActor();
2411 }
2412
o6_dimArray()2413 void ScummEngine_v6::o6_dimArray() {
2414 int data;
2415
2416 byte subOp = fetchScriptByte();
2417
2418 switch (subOp) {
2419 case 199: // SO_INT_ARRAY
2420 data = kIntArray;
2421 break;
2422 case 200: // SO_BIT_ARRAY
2423 data = kBitArray;
2424 break;
2425 case 201: // SO_NIBBLE_ARRAY
2426 data = kNibbleArray;
2427 break;
2428 case 202: // SO_BYTE_ARRAY
2429 data = kByteArray;
2430 break;
2431 case 203: // SO_STRING_ARRAY
2432 data = kStringArray;
2433 break;
2434 case 204: // SO_UNDIM_ARRAY
2435 nukeArray(fetchScriptWord());
2436 return;
2437 default:
2438 error("o6_dimArray: default case %d", subOp);
2439 }
2440
2441 defineArray(fetchScriptWord(), data, 0, pop());
2442 }
2443
o6_dummy()2444 void ScummEngine_v6::o6_dummy() {
2445 }
2446
o6_dim2dimArray()2447 void ScummEngine_v6::o6_dim2dimArray() {
2448 int a, b, data;
2449
2450 byte subOp = fetchScriptByte();
2451
2452 switch (subOp) {
2453 case 199: // SO_INT_ARRAY
2454 data = kIntArray;
2455 break;
2456 case 200: // SO_BIT_ARRAY
2457 data = kBitArray;
2458 break;
2459 case 201: // SO_NIBBLE_ARRAY
2460 data = kNibbleArray;
2461 break;
2462 case 202: // SO_BYTE_ARRAY
2463 data = kByteArray;
2464 break;
2465 case 203: // SO_STRING_ARRAY
2466 data = kStringArray;
2467 break;
2468 default:
2469 error("o6_dim2dimArray: default case %d", subOp);
2470 }
2471
2472 b = pop();
2473 a = pop();
2474 defineArray(fetchScriptWord(), data, a, b);
2475 }
2476
o6_abs()2477 void ScummEngine_v6::o6_abs() {
2478 int a = pop();
2479 push(ABS(a));
2480 }
2481
o6_distObjectObject()2482 void ScummEngine_v6::o6_distObjectObject() {
2483 int a, b;
2484 b = pop();
2485 a = pop();
2486 push(getDistanceBetween(true, a, 0, true, b, 0));
2487 }
2488
o6_distObjectPt()2489 void ScummEngine_v6::o6_distObjectPt() {
2490 int a, b, c;
2491 c = pop();
2492 b = pop();
2493 a = pop();
2494 push(getDistanceBetween(true, a, 0, false, b, c));
2495 }
2496
o6_distPtPt()2497 void ScummEngine_v6::o6_distPtPt() {
2498 int a, b, c, d;
2499 d = pop();
2500 c = pop();
2501 b = pop();
2502 a = pop();
2503 push(getDistanceBetween(false, a, b, false, c, d));
2504 }
2505
o6_drawBlastObject()2506 void ScummEngine_v6::o6_drawBlastObject() {
2507 int args[16];
2508 int a, b, c, d, e;
2509
2510 getStackList(args, ARRAYSIZE(args));
2511 e = pop();
2512 d = pop();
2513 c = pop();
2514 b = pop();
2515 a = pop();
2516 enqueueObject(a, b, c, d, e, 0xFF, 0xFF, 1, 0);
2517 }
2518
2519 // Set BOMP processing window
o6_setBlastObjectWindow()2520 void ScummEngine_v6::o6_setBlastObjectWindow() {
2521 pop();
2522 pop();
2523 pop();
2524 pop();
2525
2526 // None of the scripts of The Dig and Full Throttle use this opcode.
2527 // Sam & Max only uses it at the beginning of the highway subgame. In
2528 // the original interpreter pop'ed arguments are just ignored and the
2529 // clipping blastObject window is defined with (0, 0, 320, 200)...
2530 // which matches the screen dimensions and thus, doesn't require
2531 // another clipping operation.
2532 // So, we just handle this as no-op opcode.
2533 }
2534
2535 #ifdef ENABLE_SCUMM_7_8
o6_kernelSetFunctions()2536 void ScummEngine_v7::o6_kernelSetFunctions() {
2537 int args[30];
2538 int num;
2539 Actor *a;
2540
2541 num = getStackList(args, ARRAYSIZE(args));
2542
2543 switch (args[0]) {
2544 case 4:
2545 grabCursor(args[1], args[2], args[3], args[4]);
2546 break;
2547 case 6: {
2548 // SMUSH movie playback
2549 if (args[1] == 0 && !_skipVideo) {
2550 const char *videoname = (const char *)getStringAddressVar(VAR_VIDEONAME);
2551 assert(videoname);
2552
2553 // Correct incorrect smush filename in Macintosh FT demo
2554 if ((_game.id == GID_FT) && (_game.features & GF_DEMO) && (_game.platform == Common::kPlatformMacintosh) &&
2555 (!strcmp(videoname, "jumpgorge.san")))
2556 _splayer->play("jumpgorg.san", _smushFrameRate);
2557 // WORKAROUND: A faster frame rate is required, to keep audio/video in sync in this video
2558 else if (_game.id == GID_DIG && !strcmp(videoname, "sq3.san"))
2559 _splayer->play(videoname, 14);
2560 else
2561 _splayer->play(videoname, _smushFrameRate);
2562
2563 if (_game.id == GID_DIG) {
2564 _disableFadeInEffect = true;
2565 }
2566 } else if (_game.id == GID_FT && !_skipVideo) {
2567 const int insaneVarNum = ((_game.features & GF_DEMO) && (_game.platform == Common::kPlatformDOS))
2568 ? 232 : 233;
2569
2570 _insane->setSmushParams(_smushFrameRate);
2571 _insane->runScene(insaneVarNum);
2572 }
2573 }
2574 break;
2575 case 12:
2576 setCursorFromImg(args[1], (uint) - 1, args[2]);
2577 break;
2578 case 13:
2579 derefActor(args[1], "o6_kernelSetFunctions:13")->remapActorPalette(args[2], args[3], args[4], -1);
2580 break;
2581 case 14:
2582 derefActor(args[1], "o6_kernelSetFunctions:14")->remapActorPalette(args[2], args[3], args[4], args[5]);
2583 break;
2584 case 15:
2585 _smushFrameRate = args[1];
2586 break;
2587 case 16:
2588 case 17:
2589 enqueueText(getStringAddressVar(VAR_STRING2DRAW), args[3], args[4], args[2], args[1], (args[0] == 16));
2590 break;
2591 case 20:
2592 _imuseDigital->setRadioChatterSFX(args[1]);
2593 break;
2594 case 107:
2595 a = derefActor(args[1], "o6_kernelSetFunctions: 107");
2596 a->setScale((unsigned char)args[2], -1);
2597 break;
2598 case 108:
2599 setShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]);
2600 break;
2601 case 109:
2602 setShadowPalette(0, args[1], args[2], args[3], args[4], args[5]);
2603 break;
2604 case 114:
2605 error("o6_kernelSetFunctions: stub114()");
2606 break;
2607 case 117:
2608 freezeScripts(2);
2609 break;
2610 case 118:
2611 enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3);
2612 break;
2613 case 119:
2614 enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
2615 break;
2616 case 124:
2617 _saveSound = args[1];
2618 break;
2619 case 215:
2620 ConfMan.setBool("subtitles", args[1] != 0);
2621 break;
2622 default:
2623 error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
2624 break;
2625 }
2626 }
2627 #endif
2628
o6_kernelSetFunctions()2629 void ScummEngine_v6::o6_kernelSetFunctions() {
2630 int args[30];
2631 int num;
2632 Actor *a;
2633
2634 num = getStackList(args, ARRAYSIZE(args));
2635
2636 switch (args[0]) {
2637 case 3:
2638 // Dummy case
2639 break;
2640 case 4:
2641 grabCursor(args[1], args[2], args[3], args[4]);
2642 break;
2643 case 5:
2644 fadeOut(args[1]);
2645 break;
2646 case 6:
2647 _fullRedraw = true;
2648 redrawBGAreas();
2649 setActorRedrawFlags();
2650 processActors();
2651 fadeIn(args[1]);
2652 break;
2653 case 8:
2654 if (startManiac()) {
2655 // This is so that the surprised exclamation happens
2656 // after we return to the game again, not before.
2657 o6_breakHere();
2658 }
2659 break;
2660 case 9:
2661 killAllScriptsExceptCurrent();
2662 break;
2663 case 104: /* samnmax */
2664 nukeFlObjects(args[2], args[3]);
2665 break;
2666 case 107: /* set actor scale */
2667 a = derefActor(args[1], "o6_kernelSetFunctions: 107");
2668 a->setScale((unsigned char)args[2], -1);
2669 break;
2670 case 108: /* create proc_special_palette */
2671 case 109:
2672 // Case 108 and 109 share the same function
2673 if (num != 6)
2674 error("o6_kernelSetFunctions sub op %d: expected 6 params but got %d", args[0], num);
2675 setShadowPalette(args[3], args[4], args[5], args[1], args[2], 0, 256);
2676 break;
2677 case 110:
2678 clearCharsetMask();
2679 break;
2680 case 111:
2681 a = derefActor(args[1], "o6_kernelSetFunctions: 111");
2682 a->_shadowMode = args[2] + args[3];
2683 break;
2684 case 112: /* palette shift? */
2685 setShadowPalette(args[3], args[4], args[5], args[1], args[2], args[6], args[7]);
2686 break;
2687 case 114:
2688 // Sam & Max film noir mode
2689 if (_game.id == GID_SAMNMAX) {
2690 // At this point ScummVM will already have set
2691 // variable 0x8000 to indicate that the game is
2692 // in film noir mode. All we have to do here is
2693 // to mark the palette as "dirty", because
2694 // updatePalette() will desaturate the colors
2695 // as they are uploaded to the backend.
2696 //
2697 // This actually works better than the original
2698 // interpreter, where actors would sometimes
2699 // still be drawn in color.
2700 setDirtyColors(0, 255);
2701 } else
2702 error("stub o6_kernelSetFunctions_114()");
2703 break;
2704 case 117:
2705 // Sam & Max uses this opcode in script-43, right
2706 // before a screensaver is selected.
2707 //
2708 // Sam & Max uses variable 132 to specify the number of
2709 // minutes of inactivity (no mouse movements) before
2710 // starting the screensaver, so setting it to 0 will
2711 // help in debugging.
2712 freezeScripts(0x80);
2713 break;
2714 case 119:
2715 enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
2716 break;
2717 case 120:
2718 swapPalColors(args[1], args[2]);
2719 break;
2720 case 122:
2721 VAR(VAR_SOUNDRESULT) =
2722 (short)_imuse->doCommand (num - 1, &args[1]);
2723 break;
2724 case 123:
2725 copyPalColor(args[2], args[1]);
2726 break;
2727 case 124:
2728 _saveSound = args[1];
2729 break;
2730 default:
2731 error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
2732 break;
2733 }
2734 }
2735
o6_kernelGetFunctions()2736 void ScummEngine_v6::o6_kernelGetFunctions() {
2737 int args[30];
2738 int i;
2739 int slot;
2740 Actor *a;
2741 VirtScreen *vs = &_virtscr[kMainVirtScreen];
2742
2743 getStackList(args, ARRAYSIZE(args));
2744
2745 switch (args[0]) {
2746 case 113:
2747 // WORKAROUND for bug #1465: The scripts used for screen savers
2748 // in Sam & Max use hard coded values for the maximum height and width.
2749 // This causes problems in rooms (ie. Credits) where their values are
2750 // lower, so we set result to zero if out of bounds.
2751 if (args[1] >= 0 && args[1] <= vs->w && args[2] >= 0 && args[2] <= vs->h) {
2752 byte pixel = *vs->getPixels(args[1], args[2]);
2753 push(pixel);
2754 } else {
2755 push(0);
2756 }
2757 break;
2758 case 115:
2759 push(getSpecialBox(args[1], args[2]));
2760 break;
2761 case 116:
2762 push(checkXYInBoxBounds(args[3], args[1], args[2]));
2763 break;
2764 case 206:
2765 push(remapPaletteColor(args[1], args[2], args[3], -1));
2766 break;
2767 case 207:
2768 i = getObjectIndex(args[1]);
2769 assert(i);
2770 push(_objs[i].x_pos);
2771 break;
2772 case 208:
2773 i = getObjectIndex(args[1]);
2774 assert(i);
2775 push(_objs[i].y_pos);
2776 break;
2777 case 209:
2778 i = getObjectIndex(args[1]);
2779 assert(i);
2780 push(_objs[i].width);
2781 break;
2782 case 210:
2783 i = getObjectIndex(args[1]);
2784 assert(i);
2785 push(_objs[i].height);
2786 break;
2787 case 211:
2788 /*
2789 13 = thrust
2790 336 = thrust
2791 328 = thrust
2792 27 = abort
2793 97 = left
2794 331 = left
2795 115 = right
2796 333 = right
2797 */
2798
2799 push(getKeyState(args[1]));
2800 break;
2801 case 212:
2802 a = derefActor(args[1], "o6_kernelGetFunctions:212");
2803 // This is used by walk scripts
2804 push(a->_frame);
2805 break;
2806 case 213:
2807 slot = getVerbSlot(args[1], 0);
2808 push(_verbs[slot].curRect.left);
2809 break;
2810 case 214:
2811 slot = getVerbSlot(args[1], 0);
2812 push(_verbs[slot].curRect.top);
2813 break;
2814 case 215:
2815 if ((_extraBoxFlags[args[1]] & 0x00FF) == 0x00C0) {
2816 push(_extraBoxFlags[args[1]]);
2817 } else {
2818 push(getBoxFlags(args[1]));
2819 }
2820 break;
2821 default:
2822 error("o6_kernelGetFunctions: default case %d", args[0]);
2823 }
2824 }
2825
getKeyState(int key)2826 int ScummEngine::getKeyState(int key) {
2827 switch (key) {
2828 case 0x147: // Home
2829 // FIXME: There seems to be a mistake in the code here ("insert" vs. "home")
2830 return (_keyDownMap[Common::KEYCODE_KP7] ||
2831 _keyDownMap[Common::KEYCODE_INSERT]) ? 1 : 0;
2832 case 0x148: // Up
2833 return (_keyDownMap[Common::KEYCODE_KP8] ||
2834 _keyDownMap[Common::KEYCODE_UP] ||
2835 _keyDownMap[Common::KEYCODE_8]) ? 1 : 0;
2836 case 0x149: // PgUp
2837 return (_keyDownMap[Common::KEYCODE_KP9] ||
2838 _keyDownMap[Common::KEYCODE_PAGEUP]) ? 1 : 0;
2839 case 0x14B: // Left
2840 return (_keyDownMap[Common::KEYCODE_KP4] ||
2841 _keyDownMap[Common::KEYCODE_LEFT] ||
2842 _keyDownMap[Common::KEYCODE_4]) ? 1 : 0;
2843 case 0x14D: // Right
2844 return (_keyDownMap[Common::KEYCODE_KP6] ||
2845 _keyDownMap[Common::KEYCODE_RIGHT] ||
2846 _keyDownMap[Common::KEYCODE_6]) ? 1 : 0;
2847 case 0x14F: // End
2848 return (_keyDownMap[Common::KEYCODE_KP1] ||
2849 _keyDownMap[Common::KEYCODE_END]) ? 1 : 0;
2850 case 0x150: // Down
2851 return (_keyDownMap[Common::KEYCODE_KP2] ||
2852 _keyDownMap[Common::KEYCODE_DOWN] ||
2853 _keyDownMap[Common::KEYCODE_2]) ? 1 : 0;
2854 case 0x151: // PgDn
2855 return (_keyDownMap[Common::KEYCODE_KP3] ||
2856 _keyDownMap[Common::KEYCODE_PAGEDOWN]) ? 1 : 0;
2857 default:
2858 return (_keyDownMap[key]) ? 1 : 0;
2859 }
2860 }
2861
o6_delayFrames()2862 void ScummEngine_v6::o6_delayFrames() {
2863 ScriptSlot *ss = &vm.slot[_currentScript];
2864 if (ss->delayFrameCount == 0) {
2865 ss->delayFrameCount = pop();
2866 } else {
2867 ss->delayFrameCount--;
2868 }
2869 if (ss->delayFrameCount) {
2870 _scriptPointer--;
2871 o6_breakHere();
2872 }
2873 }
2874
o6_pickOneOf()2875 void ScummEngine_v6::o6_pickOneOf() {
2876 int args[100];
2877 int i, num;
2878
2879 num = getStackList(args, ARRAYSIZE(args));
2880 i = pop();
2881 if (i < 0 || i > num)
2882 error("o6_pickOneOf: %d out of range (0, %d)", i, num - 1);
2883 push(args[i]);
2884 }
2885
o6_pickOneOfDefault()2886 void ScummEngine_v6::o6_pickOneOfDefault() {
2887 int args[100];
2888 int i, num, def;
2889
2890 def = pop();
2891 num = getStackList(args, ARRAYSIZE(args));
2892 i = pop();
2893 if (i < 0 || i >= num)
2894 i = def;
2895 else
2896 i = args[i];
2897 push(i);
2898 }
2899
o6_stampObject()2900 void ScummEngine_v6::o6_stampObject() {
2901 int object, x, y, state;
2902
2903 state = pop();
2904 y = pop();
2905 x = pop();
2906 object = pop();
2907 if (_game.version >= 7 && object < 30) {
2908 if (state == 0)
2909 state = 255;
2910
2911 Actor *a = derefActor(object, "o6_stampObject");
2912 a->_scalex = state;
2913 a->_scaley = state;
2914 a->putActor(x, y, _currentRoom);
2915 a->_drawToBackBuf = true;
2916 a->drawActorCostume();
2917 a->_drawToBackBuf = false;
2918 a->drawActorCostume();
2919 return;
2920 }
2921
2922 if (state == 0)
2923 state = 1;
2924
2925 int objnum = getObjectIndex(object);
2926 if (objnum == -1)
2927 return;
2928
2929 if (x != -1) {
2930 _objs[objnum].x_pos = x * 8;
2931 _objs[objnum].y_pos = y * 8;
2932 }
2933
2934 putState(object, state);
2935 drawObject(objnum, 0);
2936 }
2937
o6_stopTalking()2938 void ScummEngine_v6::o6_stopTalking() {
2939 stopTalk();
2940 }
2941
o6_findAllObjects()2942 void ScummEngine_v6::o6_findAllObjects() {
2943 int room = pop();
2944 int i = 1;
2945
2946 if (room != _currentRoom)
2947 error("o6_findAllObjects: current room is not %d", room);
2948 writeVar(0, 0);
2949 defineArray(0, kIntArray, 0, _numLocalObjects + 1);
2950 writeArray(0, 0, 0, _numLocalObjects);
2951
2952 while (i < _numLocalObjects) {
2953 writeArray(0, 0, i, _objs[i].obj_nr);
2954 i++;
2955 }
2956
2957 push(readVar(0));
2958 }
2959
shuffleArray(int num,int minIdx,int maxIdx)2960 void ScummEngine_v6::shuffleArray(int num, int minIdx, int maxIdx) {
2961 int range = maxIdx - minIdx;
2962 int count = range * 2;
2963
2964 // Shuffle the array 'num'
2965 while (count--) {
2966 // Determine two random elements...
2967 int rand1 = _rnd.getRandomNumber(range) + minIdx;
2968 int rand2 = _rnd.getRandomNumber(range) + minIdx;
2969
2970 // ...and swap them
2971 int val1 = readArray(num, 0, rand1);
2972 int val2 = readArray(num, 0, rand2);
2973 writeArray(num, 0, rand1, val2);
2974 writeArray(num, 0, rand2, val1);
2975 }
2976 }
2977
o6_shuffle()2978 void ScummEngine_v6::o6_shuffle() {
2979 int b = pop();
2980 int a = pop();
2981 shuffleArray(fetchScriptWord(), a, b);
2982 }
2983
o6_pickVarRandom()2984 void ScummEngine_v6::o6_pickVarRandom() {
2985 int num;
2986 int args[100];
2987 int dim1;
2988
2989 num = getStackList(args, ARRAYSIZE(args));
2990 int value = fetchScriptWord();
2991
2992 if (readVar(value) == 0) {
2993 defineArray(value, kIntArray, 0, num);
2994 if (num > 0) {
2995 int16 counter = 0;
2996 do {
2997 writeArray(value, 0, counter + 1, args[counter]);
2998 } while (++counter < num);
2999 }
3000
3001 shuffleArray(value, 1, num);
3002 writeArray(value, 0, 0, 2);
3003 push(readArray(value, 0, 1));
3004 return;
3005 }
3006
3007 num = readArray(value, 0, 0);
3008
3009 ArrayHeader *ah = getArray(value);
3010 dim1 = FROM_LE_16(ah->dim1) - 1;
3011
3012 if (dim1 < num) {
3013 int16 var_2 = readArray(value, 0, num - 1);
3014 shuffleArray(value, 1, dim1);
3015 if (readArray(value, 0, 1) == var_2) {
3016 num = 2;
3017 } else {
3018 num = 1;
3019 }
3020 }
3021
3022 writeArray(value, 0, 0, num + 1);
3023 push(readArray(value, 0, num));
3024 }
3025
o6_getDateTime()3026 void ScummEngine_v6::o6_getDateTime() {
3027 TimeDate t;
3028 _system->getTimeAndDate(t);
3029
3030 VAR(VAR_TIMEDATE_YEAR) = t.tm_year;
3031 VAR(VAR_TIMEDATE_MONTH) = t.tm_mon;
3032 VAR(VAR_TIMEDATE_DAY) = t.tm_mday;
3033 VAR(VAR_TIMEDATE_HOUR) = t.tm_hour;
3034 VAR(VAR_TIMEDATE_MINUTE) = t.tm_min;
3035
3036 if (_game.version == 8)
3037 VAR(VAR_TIMEDATE_SECOND) = t.tm_sec;
3038 }
3039
o6_getPixel()3040 void ScummEngine_v6::o6_getPixel() {
3041 int x, y;
3042
3043 if (_game.heversion >= 61 && _game.heversion <= 62) {
3044 x = pop();
3045 y = pop();
3046 } else {
3047 y = pop();
3048 x = pop();
3049 }
3050
3051 VirtScreen *vs = findVirtScreen(y);
3052
3053 if (vs == NULL || x > _screenWidth - 1 || x < 0) {
3054 push(-1);
3055 return;
3056 }
3057
3058 byte pixel = *vs->getPixels(x, y - vs->topline);
3059 push(pixel);
3060 }
3061
o6_setBoxSet()3062 void ScummEngine_v6::o6_setBoxSet() {
3063 int arg = pop() - 1;
3064
3065 const byte *room = getResourceAddress(rtRoom, _roomResource);
3066 const byte *boxd = NULL, *boxm = NULL;
3067 int32 dboxSize, mboxSize;
3068 int i;
3069
3070 ResourceIterator boxds(room, false);
3071 for (i = 0; i < arg; i++)
3072 boxd = boxds.findNext(MKTAG('B','O','X','D'));
3073
3074 if (!boxd)
3075 error("ScummEngine_v6::o6_setBoxSet: Can't find dboxes for set %d", arg);
3076
3077 dboxSize = READ_BE_UINT32(boxd + 4) - 8;
3078 byte *matrix = _res->createResource(rtMatrix, 2, dboxSize);
3079
3080 assert(matrix);
3081 memcpy(matrix, boxd + 8, dboxSize);
3082
3083 ResourceIterator boxms(room, false);
3084 for (i = 0; i < arg; i++)
3085 boxm = boxms.findNext(MKTAG('B','O','X','M'));
3086
3087 if (!boxm)
3088 error("ScummEngine_v6::o6_setBoxSet: Can't find mboxes for set %d", arg);
3089
3090 mboxSize = READ_BE_UINT32(boxm + 4) - 8;
3091 matrix = _res->createResource(rtMatrix, 1, mboxSize);
3092
3093 assert(matrix);
3094 memcpy(matrix, boxm + 8, mboxSize);
3095
3096 if (_game.version == 7)
3097 putActors();
3098 }
3099
decodeParseString(int m,int n)3100 void ScummEngine_v6::decodeParseString(int m, int n) {
3101 byte b = fetchScriptByte();
3102
3103 switch (b) {
3104 case 65: // SO_AT
3105 _string[m].ypos = pop();
3106 _string[m].xpos = pop();
3107 _string[m].overhead = false;
3108 break;
3109 case 66: // SO_COLOR
3110 _string[m].color = pop();
3111 break;
3112 case 67: // SO_CLIPPED
3113 _string[m].right = pop();
3114 break;
3115 case 69: // SO_CENTER
3116 _string[m].center = true;
3117 _string[m].overhead = false;
3118 break;
3119 case 71: // SO_LEFT
3120 _string[m].center = false;
3121 _string[m].overhead = false;
3122 break;
3123 case 72: // SO_OVERHEAD
3124 _string[m].overhead = true;
3125 _string[m].no_talk_anim = false;
3126 break;
3127 case 74: // SO_MUMBLE
3128 _string[m].no_talk_anim = true;
3129 break;
3130 case 75: // SO_TEXTSTRING
3131 printString(m, _scriptPointer);
3132 _scriptPointer += resStrLen(_scriptPointer) + 1;
3133 break;
3134 case 0xFE:
3135 _string[m].loadDefault();
3136 if (n)
3137 _actorToPrintStrFor = pop();
3138 break;
3139 case 0xFF:
3140 _string[m].saveDefault();
3141 break;
3142 default:
3143 error("decodeParseString: default case 0x%x", b);
3144 }
3145 }
3146
3147 } // End of namespace Scumm
3148