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 // Item script opcodes for Simon1/Simon2
24
25 #include "common/debug-channels.h"
26 #include "common/endian.h"
27 #include "common/system.h"
28 #include "common/textconsole.h"
29
30 #include "agos/agos.h"
31 #include "agos/intern.h"
32
33 namespace AGOS {
34
setupOpcodes()35 void AGOSEngine::setupOpcodes() {
36 error("setupOpcodes: Unknown game");
37 }
38
setScriptCondition(bool cond)39 void AGOSEngine::setScriptCondition(bool cond) {
40 _runScriptCondition[_recursionDepth] = cond;
41 }
42
getScriptCondition()43 bool AGOSEngine::getScriptCondition() {
44 return _runScriptCondition[_recursionDepth];
45 }
46
setScriptReturn(int ret)47 void AGOSEngine::setScriptReturn(int ret) {
48 _runScriptReturn[_recursionDepth] = ret;
49 }
50
getScriptReturn()51 int AGOSEngine::getScriptReturn() {
52 return _runScriptReturn[_recursionDepth];
53 }
54
55 // -----------------------------------------------------------------------
56 // Common Opcodes
57 // -----------------------------------------------------------------------
58
o_invalid()59 void AGOSEngine::o_invalid() {
60 error("Invalid opcode %d", _opcode);
61 }
62
o_at()63 void AGOSEngine::o_at() {
64 // 1: ptrA parent is
65 setScriptCondition(me()->parent == getNextItemID());
66 }
67
o_notAt()68 void AGOSEngine::o_notAt() {
69 // 2: ptrA parent is not
70 setScriptCondition(me()->parent != getNextItemID());
71 }
72
o_carried()73 void AGOSEngine::o_carried() {
74 // 5: parent is 1
75 setScriptCondition(getNextItemPtr()->parent == getItem1ID());
76 }
77
o_notCarried()78 void AGOSEngine::o_notCarried() {
79 // 6: parent isnot 1
80 setScriptCondition(getNextItemPtr()->parent != getItem1ID());
81 }
82
o_isAt()83 void AGOSEngine::o_isAt() {
84 // 7: parent is
85 Item *item = getNextItemPtr();
86 setScriptCondition(item->parent == getNextItemID());
87 }
88
o_zero()89 void AGOSEngine::o_zero() {
90 // 11: is zero
91 setScriptCondition(getNextVarContents() == 0);
92 }
93
o_notZero()94 void AGOSEngine::o_notZero() {
95 // 12: isnot zero
96 setScriptCondition(getNextVarContents() != 0);
97 }
98
o_eq()99 void AGOSEngine::o_eq() {
100 // 13: equal
101 uint tmp = getNextVarContents();
102 uint tmp2 = getVarOrWord();
103
104 #ifdef __DS__
105 // HACK: Skip attempt to read Calypso's letter manually,
106 // due to speech segment been too large to fit into memory
107 if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE) &&
108 getPlatform() == Common::kPlatformWindows && _currentTable) {
109 if (_currentTable->id == 71 && tmp == 1 && tmp2 == 1) {
110 setScriptCondition(false);
111 return;
112 }
113 }
114 #endif
115 setScriptCondition(tmp == tmp2);
116 }
117
o_notEq()118 void AGOSEngine::o_notEq() {
119 // 14: not equal
120 uint tmp = getNextVarContents();
121 setScriptCondition(tmp != getVarOrWord());
122 }
123
o_gt()124 void AGOSEngine::o_gt() {
125 // 15: is greater
126 int16 tmp1 = getNextVarContents();
127 int16 tmp2 = getVarOrWord();
128 setScriptCondition(tmp1 > tmp2);
129 }
130
o_lt()131 void AGOSEngine::o_lt() {
132 // 16: is less
133 int16 tmp1 = getNextVarContents();
134 int16 tmp2 = getVarOrWord();
135 setScriptCondition(tmp1 < tmp2);
136 }
137
o_eqf()138 void AGOSEngine::o_eqf() {
139 // 17: is eq f
140 uint tmp = getNextVarContents();
141 setScriptCondition(tmp == getNextVarContents());
142 }
143
o_notEqf()144 void AGOSEngine::o_notEqf() {
145 // 18: is not equal f
146 uint tmp = getNextVarContents();
147 setScriptCondition(tmp != getNextVarContents());
148 }
149
o_ltf()150 void AGOSEngine::o_ltf() {
151 // 19: is greater f
152 int16 tmp1 = getNextVarContents();
153 int16 tmp2 = getNextVarContents();
154 setScriptCondition(tmp1 < tmp2);
155 }
156
o_gtf()157 void AGOSEngine::o_gtf() {
158 // 20: is less f
159 int16 tmp1 = getNextVarContents();
160 int16 tmp2 = getNextVarContents();
161 setScriptCondition(tmp1 > tmp2);
162 }
163
o_chance()164 void AGOSEngine::o_chance() {
165 // 23: chance
166 int16 a = getVarOrWord();
167
168 if (a == 0) {
169 setScriptCondition(false);
170 return;
171 }
172
173 if (a == 100) {
174 setScriptCondition(true);
175 return;
176 }
177
178 a += _chanceModifier;
179
180 if (a <= 0) {
181 _chanceModifier = 0;
182 setScriptCondition(false);
183 } else if ((int16)_rnd.getRandomNumber(99) < a) {
184 if (_chanceModifier <= 0)
185 _chanceModifier -= 5;
186 else
187 _chanceModifier = 0;
188 setScriptCondition(true);
189 } else {
190 if (_chanceModifier >= 0)
191 _chanceModifier += 5;
192 else
193 _chanceModifier = 0;
194 setScriptCondition(false);
195 }
196 }
197
o_isRoom()198 void AGOSEngine::o_isRoom() {
199 // 25: is room
200 setScriptCondition(isRoom(getNextItemPtr()));
201 }
202
o_isObject()203 void AGOSEngine::o_isObject() {
204 // 26: is object
205 setScriptCondition(isObject(getNextItemPtr()));
206 }
207
o_state()208 void AGOSEngine::o_state() {
209 // 27: item state is
210 Item *item = getNextItemPtr();
211 setScriptCondition((uint) item->state == getVarOrWord());
212 }
213
o_oflag()214 void AGOSEngine::o_oflag() {
215 // 28: item has prop
216 SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), kObjectType);
217 uint num = getVarOrByte();
218 setScriptCondition(subObject != NULL && (subObject->objectFlags & (1 << num)) != 0);
219 }
220
o_destroy()221 void AGOSEngine::o_destroy() {
222 // 31: set no parent
223 setItemParent(getNextItemPtr(), NULL);
224 }
225
o_place()226 void AGOSEngine::o_place() {
227 // 33: set item parent
228 Item *item = getNextItemPtr();
229 setItemParent(item, getNextItemPtr());
230 }
231
o_copyff()232 void AGOSEngine::o_copyff() {
233 // 36: copy var
234 uint value = getNextVarContents();
235 writeNextVarContents(value);
236 }
237
o_clear()238 void AGOSEngine::o_clear() {
239 // 41: zero var
240 writeNextVarContents(0);
241 }
242
o_let()243 void AGOSEngine::o_let() {
244 // 42: set var
245 uint var = getVarWrapper();
246 uint value = getVarOrWord();
247
248 if (getGameType() == GType_FF && _currentTable) {
249 // WORKAROUND: When the repair man comes to fix the car, the game doesn't
250 // wait long enough for the screen to completely scroll to the left side.
251 if (_currentTable->id == 20438 && var == 103 && value == 60) {
252 value = 71;
253 }
254 }
255
256 writeVariable(var, value);
257 }
258
o_add()259 void AGOSEngine::o_add() {
260 // 43: add
261 uint var = getVarWrapper();
262 writeVariable(var, readVariable(var) + getVarOrWord());
263
264 // WORKAROUND: The conversation of the male in Vid-Phone Booth at Dave's Space Bar
265 // is based on variable 116, but stops due to a missing option (37).
266 if (getGameType() == GType_FF && _currentTable->id == 10538 && readVariable(116) == 37)
267 writeVariable(116, 38);
268 }
269
o_sub()270 void AGOSEngine::o_sub() {
271 // 44: sub
272 uint var = getVarWrapper();
273 writeVariable(var, readVariable(var) - getVarOrWord());
274 }
275
o_addf()276 void AGOSEngine::o_addf() {
277 // 45: add f
278 uint var = getVarWrapper();
279 writeVariable(var, readVariable(var) + getNextVarContents());
280 }
281
o_subf()282 void AGOSEngine::o_subf() {
283 // 46: sub f
284 uint var = getVarWrapper();
285 writeVariable(var, readVariable(var) - getNextVarContents());
286 }
287
o_mul()288 void AGOSEngine::o_mul() {
289 // 47: mul
290 uint var = getVarWrapper();
291 writeVariable(var, readVariable(var) * getVarOrWord());
292 }
293
o_div()294 void AGOSEngine::o_div() {
295 // 48: div
296 uint var = getVarWrapper();
297 int value = getVarOrWord();
298 if (value == 0)
299 error("o_div: Division by zero");
300 writeVariable(var, readVariable(var) / value);
301 }
302
o_mulf()303 void AGOSEngine::o_mulf() {
304 // 49: mul f
305 uint var = getVarWrapper();
306 writeVariable(var, readVariable(var) * getNextVarContents());
307 }
308
o_divf()309 void AGOSEngine::o_divf() {
310 // 50: div f
311 uint var = getVarWrapper();
312 int value = getNextVarContents();
313 if (value == 0)
314 error("o_divf: Division by zero");
315 writeVariable(var, readVariable(var) / value);
316 }
317
o_mod()318 void AGOSEngine::o_mod() {
319 // 51: mod
320 uint var = getVarWrapper();
321 int value = getVarOrWord();
322 if (value == 0)
323 error("o_mod: Division by zero");
324 writeVariable(var, readVariable(var) % value);
325 }
326
o_modf()327 void AGOSEngine::o_modf() {
328 // 52: mod f
329 uint var = getVarWrapper();
330 int value = getNextVarContents();
331 if (value == 0)
332 error("o_modf: Division by zero");
333 writeVariable(var, readVariable(var) % value);
334 }
335
o_random()336 void AGOSEngine::o_random() {
337 // 53: random
338 uint var = getVarWrapper();
339 uint value = (uint16)getVarOrWord();
340 writeVariable(var, _rnd.getRandomNumber(value - 1));
341 }
342
o_goto()343 void AGOSEngine::o_goto() {
344 // 55: set itemA parent
345 uint item = getNextItemID();
346 setItemParent(me(), _itemArrayPtr[item]);
347 }
348
o_oset()349 void AGOSEngine::o_oset() {
350 // 56: set child2 fr bit
351 SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), kObjectType);
352 int value = getVarOrByte();
353 if (subObject != NULL && value >= 16)
354 subObject->objectFlags |= (1 << value);
355 }
356
o_oclear()357 void AGOSEngine::o_oclear() {
358 // 57: clear child2 fr bit
359 SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), kObjectType);
360 int value = getVarOrByte();
361 if (subObject != NULL && value >= 16)
362 subObject->objectFlags &= ~(1 << value);
363 }
364
o_putBy()365 void AGOSEngine::o_putBy() {
366 // 58: make siblings
367 Item *item = getNextItemPtr();
368 setItemParent(item, derefItem(getNextItemPtr()->parent));
369 }
370
o_inc()371 void AGOSEngine::o_inc() {
372 // 59: item inc state
373 Item *item = getNextItemPtr();
374 if (item->state <= 30000) {
375 setItemState(item, item->state + 1);
376 synchChain(item);
377 }
378 }
379
o_dec()380 void AGOSEngine::o_dec() {
381 // 60: item dec state
382 Item *item = getNextItemPtr();
383 if (item->state >= 0) {
384 setItemState(item, item->state - 1);
385 synchChain(item);
386 }
387 }
388
o_setState()389 void AGOSEngine::o_setState() {
390 // 61: item set state
391 Item *item = getNextItemPtr();
392 int value = getVarOrWord();
393 if (value < 0)
394 value = 0;
395 if (value > 30000)
396 value = 30000;
397 setItemState(item, value);
398 synchChain(item);
399 }
400
o_print()401 void AGOSEngine::o_print() {
402 // 62: show int
403 showMessageFormat("%d", getNextVarContents());
404 }
405
o_message()406 void AGOSEngine::o_message() {
407 // 63: show string nl
408 showMessageFormat("%s\n", getStringPtrByID(getNextStringID()));
409 }
410
o_msg()411 void AGOSEngine::o_msg() {
412 // 64: show string
413 showMessageFormat("%s", getStringPtrByID(getNextStringID()));
414 }
415
o_end()416 void AGOSEngine::o_end() {
417 // 68: exit interpreter
418 quitGame();
419 // Make sure the quit event is processed immediately.
420 delay(0);
421 }
422
o_done()423 void AGOSEngine::o_done() {
424 // 69: return 1
425 setScriptReturn(1);
426 }
427
o_process()428 void AGOSEngine::o_process() {
429 // 71: start subroutine
430 uint16 id = getVarOrWord();
431
432 if (!_copyProtection && getGameType() == GType_WW && id == 71) {
433 // Copy protection was disabled in Good Old Games release
434 return;
435 }
436
437 Subroutine *sub = getSubroutineByID(id);
438 if (sub != NULL) {
439 #ifdef __DS__
440 // HACK: Skip scene of Simon reading letter from Calypso
441 // due to speech segment been too large to fit into memory
442 if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE) &&
443 getPlatform() == Common::kPlatformWindows && sub->id == 2922) {
444 // set parent special
445 _noParentNotify = true;
446 setItemParent(derefItem(16), me());
447 _noParentNotify = false;
448
449 // set parent special
450 _noParentNotify = true;
451 setItemParent(derefItem(14), me());
452 _noParentNotify = false;
453
454 // set item parent
455 setItemParent(derefItem(12), me());
456
457 return;
458 }
459 #endif
460 startSubroutine(sub);
461 }
462 }
463
o_when()464 void AGOSEngine::o_when() {
465 // 76: add timeout
466 uint16 timeout = getVarOrWord();
467 addTimeEvent(timeout, getVarOrWord());
468 }
469
o_if1()470 void AGOSEngine::o_if1() {
471 // 77: has item minus 1
472 setScriptCondition(_subjectItem != NULL);
473 }
474
o_if2()475 void AGOSEngine::o_if2() {
476 // 78: has item minus 3
477 setScriptCondition(_objectItem != NULL);
478 }
479
o_isCalled()480 void AGOSEngine::o_isCalled() {
481 // 79: childstruct fr2 is
482 SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), kObjectType);
483 uint stringId = getNextStringID();
484 setScriptCondition((subObject != NULL) && subObject->objectName == stringId);
485 }
486
o_is()487 void AGOSEngine::o_is() {
488 // 80: item equal
489 setScriptCondition(getNextItemPtr() == getNextItemPtr());
490 }
491
o_debug()492 void AGOSEngine::o_debug() {
493 // 82: debug opcode
494 getVarOrByte();
495 }
496
o_comment()497 void AGOSEngine::o_comment() {
498 // 87: comment
499 getNextStringID();
500 }
501
o_haltAnimation()502 void AGOSEngine::o_haltAnimation() {
503 // 88: stop animation
504 _videoLockOut |= 0x10;
505
506 if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
507 VgaTimerEntry *vte = _vgaTimerList;
508 while (vte->delay) {
509 if (vte->type == ANIMATE_EVENT)
510 vte->delay += 10;
511 vte++;
512 }
513
514 _scrollCount = 0;
515 _scrollFlag = 0;
516 }
517 }
518
o_restartAnimation()519 void AGOSEngine::o_restartAnimation() {
520 // 89: restart animation
521 _videoLockOut &= ~0x10;
522 }
523
o_getParent()524 void AGOSEngine::o_getParent() {
525 // 90: set minusitem to parent
526 Item *i = getNextItemPtr();
527 if (getVarOrByte() == 1)
528 _subjectItem = derefItem(i->parent);
529 else
530 _objectItem = derefItem(i->parent);
531 }
532
o_getNext()533 void AGOSEngine::o_getNext() {
534 // 91: set minusitem to next
535 Item *i = getNextItemPtr();
536 if (getVarOrByte() == 1)
537 _subjectItem = derefItem(i->next);
538 else
539 _objectItem = derefItem(i->next);
540 }
541
o_getChildren()542 void AGOSEngine::o_getChildren() {
543 // 92: set minusitem to child
544 Item *i = getNextItemPtr();
545 if (getVarOrByte() == 1)
546 _subjectItem = derefItem(i->child);
547 else
548 _objectItem = derefItem(i->child);
549 }
550
o_picture()551 void AGOSEngine::o_picture() {
552 // 96
553 uint vga_res = getVarOrWord();
554 uint mode = getVarOrByte();
555
556 // WORKAROUND: For a script bug in the Amiga AGA/CD32 versions
557 // When selecting locations on the magical map, the script looks
558 // for vga_res 12701, but only vga_res 12700 exists.
559 if (getGameType() == GType_SIMON1 && getPlatform() == Common::kPlatformAmiga &&
560 vga_res == 12701) {
561 return;
562 }
563
564 if (getGameType() == GType_PP && getGameId() != GID_DIMP) {
565 if (vga_res == 8700 && getBitFlag(107)) {
566 _vgaPeriod = 30;
567 }
568
569 _picture8600 = (vga_res == 8600);
570 }
571
572 setWindowImageEx(mode, vga_res);
573 }
574
o_loadZone()575 void AGOSEngine::o_loadZone() {
576 // 97: load zone
577 uint vga_res = getVarOrWord();
578
579 _videoLockOut |= 0x80;
580
581 if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 ||
582 getGameType() == GType_WW) {
583 vc27_resetSprite();
584 vc29_stopAllSounds();
585 }
586
587 loadZone(vga_res);
588
589 if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 ||
590 getGameType() == GType_WW) {
591 _copyScnFlag = 0;
592 _vgaSpriteChanged = 0;
593 }
594
595 _videoLockOut &= ~0x80;
596 }
597
o_killAnimate()598 void AGOSEngine::o_killAnimate() {
599 // 100: kill animations
600 _videoLockOut |= 0x8000;
601 vc27_resetSprite();
602 _videoLockOut &= ~0x8000;
603 }
604
o_defWindow()605 void AGOSEngine::o_defWindow() {
606 // 101: define window
607 uint num = getVarOrByte();
608 uint x = getVarOrWord();
609 uint y = getVarOrWord();
610 uint w = getVarOrWord();
611 uint h = getVarOrWord();
612 uint flags = getVarOrWord();
613 uint color = getVarOrWord();
614
615 uint fillColor, textColor;
616 if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 ||
617 getGameType() == GType_WW) {
618 fillColor = color % 100;
619 textColor = color / 100;
620 } else {
621 fillColor = color;
622 textColor = 0;
623 }
624
625 num &= 7;
626
627 if (_windowArray[num])
628 closeWindow(num);
629
630 _windowArray[num] = openWindow(x, y, w, h, flags, fillColor, textColor);
631
632 if (num == _curWindow) {
633 _textWindow = _windowArray[num];
634 justifyStart();
635 }
636 }
637
o_window()638 void AGOSEngine::o_window() {
639 // 102
640 changeWindow(getVarOrByte() & 7);
641 }
642
o_cls()643 void AGOSEngine::o_cls() {
644 // 103
645 mouseOff();
646 removeIconArray(_curWindow);
647 showMessageFormat("\x0C");
648 _oracleMaxScrollY = 0;
649 _noOracleScroll = 0;
650 mouseOn();
651 }
652
o_closeWindow()653 void AGOSEngine::o_closeWindow() {
654 // 104
655 closeWindow(getVarOrByte() & 7);
656 }
657
o_addBox()658 void AGOSEngine::o_addBox() {
659 // 107: add item box
660 uint flags = 0;
661 uint id = getVarOrWord();
662 uint params = id / 1000;
663 uint x, y, w, h, verb;
664 Item *item;
665
666 id = id % 1000;
667
668 if (params & 1)
669 flags |= kBFInvertTouch;
670 if (params & 2)
671 flags |= kBFNoTouchName;
672 if (params & 4)
673 flags |= kBFBoxItem;
674 if (params & 8)
675 flags |= kBFTextBox;
676 if (params & 16)
677 flags |= kBFDragBox;
678
679 x = getVarOrWord();
680 y = getVarOrWord();
681 w = getVarOrWord();
682 h = getVarOrWord();
683 item = getNextItemPtrStrange();
684 verb = getVarOrWord();
685 if (x >= 1000) {
686 verb += 0x4000;
687 x -= 1000;
688 }
689 defineBox(id, x, y, w, h, flags, verb, item);
690 }
691
o_delBox()692 void AGOSEngine::o_delBox() {
693 // 108: delete box
694 undefineBox(getVarOrWord());
695 }
696
o_enableBox()697 void AGOSEngine::o_enableBox() {
698 // 109: enable box
699 enableBox(getVarOrWord());
700 }
701
o_disableBox()702 void AGOSEngine::o_disableBox() {
703 // 110: set hitarea bit 0x40
704 disableBox(getVarOrWord());
705 }
706
o_moveBox()707 void AGOSEngine::o_moveBox() {
708 // 111: set hitarea xy
709 uint hitarea_id = getVarOrWord();
710 uint x = getVarOrWord();
711 uint y = getVarOrWord();
712 moveBox(hitarea_id, x, y);
713 }
714
o_doIcons()715 void AGOSEngine::o_doIcons() {
716 // 114
717 Item *item = getNextItemPtr();
718 uint num = getVarOrByte();
719 mouseOff();
720 drawIconArray(num, item, 0, 0);
721 mouseOn();
722 }
723
o_isClass()724 void AGOSEngine::o_isClass() {
725 // 115: item has flag
726 Item *item = getNextItemPtr();
727 setScriptCondition((item->classFlags & (1 << getVarOrByte())) != 0);
728 }
729
o_setClass()730 void AGOSEngine::o_setClass() {
731 // 116: item set flag
732 Item *item = getNextItemPtr();
733 item->classFlags |= (1 << getVarOrByte());
734 }
735
o_unsetClass()736 void AGOSEngine::o_unsetClass() {
737 // 117: item clear flag
738 Item *item = getNextItemPtr();
739 item->classFlags &= ~(1 << getVarOrByte());
740 }
741
o_waitSync()742 void AGOSEngine::o_waitSync() {
743 // 119: wait vga
744 uint var = getVarOrWord();
745 _scriptVar2 = (var == 200);
746
747 if (var != 200 || !_skipVgaWait)
748 waitForSync(var);
749 _skipVgaWait = false;
750 }
751
o_sync()752 void AGOSEngine::o_sync() {
753 // 120: sync
754 sendSync(getVarOrWord());
755 }
756
o_defObj()757 void AGOSEngine::o_defObj() {
758 // 121: set vga item
759 uint slot = getVarOrByte();
760 _objectArray[slot] = getNextItemPtr();
761 }
762
o_here()763 void AGOSEngine::o_here() {
764 // 125: item is sibling with item 1
765 Item *item = getNextItemPtr();
766 setScriptCondition(me()->parent == item->parent);
767 }
768
o_doClassIcons()769 void AGOSEngine::o_doClassIcons() {
770 // 126: do class icons
771 Item *item = getNextItemPtr();
772 uint num = getVarOrByte();
773 uint a = getVarOrByte();
774
775 mouseOff();
776 if (getGameType() == GType_ELVIRA1)
777 drawIconArray(num, item, 0, a);
778 else
779 drawIconArray(num, item, 0, 1 << a);
780 mouseOn();
781 }
782
o_playTune()783 void AGOSEngine::o_playTune() {
784 // 127: play tune
785 uint16 music = getVarOrWord();
786 uint16 track = getVarOrWord();
787
788 if (music != _lastMusicPlayed) {
789 _lastMusicPlayed = music;
790 playMusic(music, track);
791 }
792 }
793
o_setAdjNoun()794 void AGOSEngine::o_setAdjNoun() {
795 // 130: set adj noun
796 uint var = getVarOrByte();
797 if (var == 1) {
798 _scriptAdj1 = getNextWord();
799 _scriptNoun1 = getNextWord();
800 } else {
801 _scriptAdj2 = getNextWord();
802 _scriptNoun2 = getNextWord();
803 }
804 }
805
o_saveUserGame()806 void AGOSEngine::o_saveUserGame() {
807 // 132: save user game
808 if (getGameId() == GID_SIMON1CD32) {
809 // The Amiga CD32 version of Simon the Sorcerer 1 uses a single slot
810 if (!saveGame(0, "Default Saved Game")) {
811 vc33_setMouseOn();
812 fileError(_windowArray[5], true);
813 }
814 } else {
815 _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
816 userGame(false);
817 _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
818 }
819 }
820
o_loadUserGame()821 void AGOSEngine::o_loadUserGame() {
822 // 133: load user game
823 if (getGameId() == GID_SIMON1CD32) {
824 // The Amiga CD32 version of Simon the Sorcerer 1 uses a single slot
825 if (!loadGame(genSaveName(0))) {
826 vc33_setMouseOn();
827 fileError(_windowArray[5], false);
828 }
829 } else {
830 _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
831 userGame(true);
832 _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
833 }
834 }
835
o_copysf()836 void AGOSEngine::o_copysf() {
837 // 136: set var to item unk3
838 Item *item = getNextItemPtr();
839 writeNextVarContents(item->state);
840 }
841
o_restoreIcons()842 void AGOSEngine::o_restoreIcons() {
843 // 137
844 uint num = getVarOrByte();
845 WindowBlock *window = _windowArray[num & 7];
846 if (window->iconPtr)
847 drawIconArray(num, window->iconPtr->itemRef, window->iconPtr->line, window->iconPtr->classMask);
848 }
849
o_freezeZones()850 void AGOSEngine::o_freezeZones() {
851 // 138: freeze zones
852 freezeBottom();
853
854 if (!_copyProtection && !(getFeatures() & GF_TALKIE) && _currentTable) {
855 if ((getGameType() == GType_SIMON1 && _currentTable->id == 2924) ||
856 (getGameType() == GType_SIMON2 && _currentTable->id == 1322)) {
857 _variableArray[134] = 3;
858 _variableArray[135] = 3;
859 setBitFlag(135, 1);
860 setScriptCondition(0);
861 }
862 }
863 }
864
o_placeNoIcons()865 void AGOSEngine::o_placeNoIcons() {
866 // 139: set parent special
867 Item *item = getNextItemPtr();
868 _noParentNotify = true;
869 setItemParent(item, getNextItemPtr());
870 _noParentNotify = false;
871 }
872
o_clearTimers()873 void AGOSEngine::o_clearTimers() {
874 // 140: clear timers
875 killAllTimers();
876
877 if (getGameType() == GType_SIMON1)
878 addTimeEvent(3, 160);
879 }
880
o_setDollar()881 void AGOSEngine::o_setDollar() {
882 // 141: set m1 to m3
883 uint which = getVarOrByte();
884 Item *item = getNextItemPtr();
885 if (which == 1) {
886 _subjectItem = item;
887 } else {
888 _objectItem = item;
889 }
890 }
891
o_isBox()892 void AGOSEngine::o_isBox() {
893 // 142: is box dead
894 setScriptCondition(isBoxDead(getVarOrWord()));
895 }
896
897 // -----------------------------------------------------------------------
898
getByte()899 byte AGOSEngine::getByte() {
900 return *_codePtr++;
901 }
902
getNextWord()903 int AGOSEngine::getNextWord() {
904 int16 a = (int16)READ_BE_UINT16(_codePtr);
905 _codePtr += 2;
906 return a;
907 }
908
getNextStringID()909 uint AGOSEngine::getNextStringID() {
910 return (uint16)getNextWord();
911 }
912
getVarOrByte()913 uint AGOSEngine::getVarOrByte() {
914 if (getGameType() == GType_ELVIRA1) {
915 return getVarOrWord();
916 } else {
917 uint a = *_codePtr++;
918 if (a != 255)
919 return a;
920 return readVariable(*_codePtr++);
921 }
922 }
923
getVarOrWord()924 uint AGOSEngine::getVarOrWord() {
925 uint a = READ_BE_UINT16(_codePtr);
926 _codePtr += 2;
927 if (getGameType() == GType_PP) {
928 if (a >= 60000 && a < 62048) {
929 return readVariable(a - 60000);
930 }
931 } else {
932 if (a >= 30000 && a < 30512) {
933 return readVariable(a - 30000);
934 }
935 }
936 return a;
937 }
938
getVarWrapper()939 uint AGOSEngine::getVarWrapper() {
940 if (getGameType() == GType_ELVIRA1 || getGameType() == GType_PP)
941 return getVarOrWord();
942 else
943 return getVarOrByte();
944 }
945
getNextVarContents()946 uint AGOSEngine::getNextVarContents() {
947 return (uint16)readVariable(getVarWrapper());
948 }
949
readVariable(uint16 variable)950 uint AGOSEngine::readVariable(uint16 variable) {
951 if (variable >= _numVars)
952 error("readVariable: Variable %d out of range", variable);
953
954 if (getGameType() == GType_PP) {
955 return (uint16)_variableArray[variable];
956 } else if (getGameType() == GType_FF) {
957 if (getBitFlag(83))
958 return (uint16)_variableArray2[variable];
959 else
960 return (uint16)_variableArray[variable];
961 } else {
962 return _variableArray[variable];
963 }
964 }
965
writeNextVarContents(uint16 contents)966 void AGOSEngine::writeNextVarContents(uint16 contents) {
967 writeVariable(getVarWrapper(), contents);
968 }
969
writeVariable(uint16 variable,uint16 contents)970 void AGOSEngine::writeVariable(uint16 variable, uint16 contents) {
971 if (variable >= _numVars)
972 error("writeVariable: Variable %d out of range", variable);
973
974 if (getGameType() == GType_FF && getBitFlag(83))
975 _variableArray2[variable] = contents;
976 else
977 _variableArray[variable] = contents;
978 }
979
runScript()980 int AGOSEngine::runScript() {
981 bool flag;
982
983 if (shouldQuit())
984 return 1;
985
986 do {
987 if (DebugMan.isDebugChannelEnabled(kDebugOpcode))
988 dumpOpcode(_codePtr);
989
990 if (getGameType() == GType_ELVIRA1) {
991 _opcode = getVarOrWord();
992 if (_opcode == 10000)
993 return 0;
994 } else {
995 _opcode = getByte();
996 if (_opcode == 0xFF)
997 return 0;
998 }
999
1000 if (_runScriptReturn1)
1001 return 1;
1002
1003 /* Invert condition? */
1004 flag = false;
1005 if (getGameType() == GType_ELVIRA1) {
1006 if (_opcode == 203) {
1007 flag = true;
1008 _opcode = getVarOrWord();
1009 if (_opcode == 10000)
1010 return 0;
1011 }
1012 } else {
1013 if (_opcode == 0) {
1014 flag = true;
1015 _opcode = getByte();
1016 if (_opcode == 0xFF)
1017 return 0;
1018 }
1019 }
1020
1021 setScriptCondition(true);
1022 setScriptReturn(0);
1023
1024 if (_opcode > _numOpcodes)
1025 error("Invalid opcode '%d' encountered", _opcode);
1026
1027 executeOpcode(_opcode);
1028 } while (getScriptCondition() != flag && !getScriptReturn() && !shouldQuit());
1029
1030 return (shouldQuit()) ? 1 : getScriptReturn();
1031 }
1032
nextSub(Child * sub,int16 key)1033 Child *nextSub(Child *sub, int16 key) {
1034 Child *a = sub->next;
1035 while (a) {
1036 if (a->type == key)
1037 return a;
1038 a = a->next;
1039 }
1040 return NULL;
1041 }
1042
synchChain(Item * i)1043 void AGOSEngine::synchChain(Item *i) {
1044 SubChain *c = (SubChain *)findChildOfType(i, kChainType);
1045 while (c) {
1046 setItemState(derefItem(c->chChained), i->state);
1047 c = (SubChain *)nextSub((Child *)c, kChainType);
1048 }
1049 }
1050
sendSync(uint a)1051 void AGOSEngine::sendSync(uint a) {
1052 uint16 id = to16Wrapper(a);
1053 _videoLockOut |= 0x8000;
1054 _vcPtr = (byte *)&id;
1055 vc15_sync();
1056 _videoLockOut &= ~0x8000;
1057 }
1058
stopAnimate(uint16 a)1059 void AGOSEngine::stopAnimate(uint16 a) {
1060 uint16 b = to16Wrapper(a);
1061 _videoLockOut |= 0x8000;
1062 _vcPtr = (byte *)&b;
1063 vc60_stopAnimation();
1064 _videoLockOut &= ~0x8000;
1065 }
1066
waitForSync(uint a)1067 void AGOSEngine::waitForSync(uint a) {
1068 const uint maxCount = (getGameType() == GType_SIMON1) ? 1000 : 2500;
1069
1070 if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE)) {
1071 if (a != 200) {
1072 uint16 tmp = _lastVgaWaitFor;
1073 _lastVgaWaitFor = 0;
1074 if (tmp == a)
1075 return;
1076 }
1077 }
1078
1079 _vgaWaitFor = a;
1080 _syncCount = 0;
1081 _exitCutscene = false;
1082 _rightButtonDown = false;
1083
1084 while (_vgaWaitFor != 0 && !shouldQuit()) {
1085 if (_rightButtonDown) {
1086 if (_vgaWaitFor == 200 && (getGameType() == GType_FF || !getBitFlag(14))) {
1087 skipSpeech();
1088 break;
1089 }
1090 }
1091 if (_exitCutscene) {
1092 if (getGameType() == GType_ELVIRA1) {
1093 if (_variableArray[105] == 0) {
1094 _variableArray[105] = 255;
1095 break;
1096 }
1097 } else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
1098 if (_vgaWaitFor == 51) {
1099 setBitFlag(244, 1);
1100 break;
1101 }
1102 } else {
1103 if (getBitFlag(9)) {
1104 endCutscene();
1105 break;
1106 }
1107 }
1108 }
1109 processSpecialKeys();
1110
1111 if (_syncCount >= maxCount) {
1112 warning("waitForSync: wait timed out");
1113 break;
1114 }
1115
1116 delay(1);
1117 }
1118 }
1119
1120 } // End of namespace AGOS
1121