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