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 "parallaction/exec.h"
24 #include "parallaction/input.h"
25 #include "parallaction/parallaction.h"
26 
27 #include "common/textconsole.h"
28 
29 namespace Parallaction {
30 
31 #define INST_ON			1
32 #define INST_OFF		2
33 #define INST_X			3
34 #define INST_Y			4
35 #define INST_Z			5
36 #define INST_F			6
37 #define INST_LOOP		7
38 #define INST_ENDLOOP	8
39 #define INST_SHOW		9
40 #define INST_INC		10
41 #define INST_DEC		11
42 #define INST_SET		12
43 #define INST_PUT		13
44 #define INST_CALL		14
45 #define INST_WAIT		15
46 #define INST_START		16
47 #define INST_PROCESS	17
48 #define INST_MOVE		18
49 #define INST_COLOR		19
50 #define INST_SOUND		20
51 #define INST_MASK		21
52 #define INST_PRINT		22
53 #define INST_TEXT		23
54 #define INST_MUL		24
55 #define INST_DIV		25
56 #define INST_IFEQ		26
57 #define INST_IFLT		27
58 #define INST_IFGT		28
59 #define INST_ENDIF		29
60 #define INST_STOP		30
61 #define INST_ENDSCRIPT	31
62 
63 #define SetOpcodeTable(x) table = &x;
64 
65 typedef Common::Functor1Mem<CommandContext&, void, CommandExec_br> OpcodeV1;
66 #define COMMAND_OPCODE(op) table->push_back(new OpcodeV1(this, &CommandExec_br::cmdOp_##op))
67 #define DECLARE_COMMAND_OPCODE(op) void CommandExec_br::cmdOp_##op(CommandContext &ctxt)
68 
69 typedef Common::Functor1Mem<ProgramContext&, void, ProgramExec_br> OpcodeV2;
70 #define INSTRUCTION_OPCODE(op) table->push_back(new OpcodeV2(this, &ProgramExec_br::instOp_##op))
71 #define DECLARE_INSTRUCTION_OPCODE(op) void ProgramExec_br::instOp_##op(ProgramContext &ctxt)
72 
73 extern const char *_instructionNamesRes_br[];
74 
setupSubtitles(const char * s,const char * s2,int y)75 void Parallaction_br::setupSubtitles(const char *s, const char *s2, int y) {
76 	debugC(5, kDebugExec, "setupSubtitles(%s, %s, %i)", s, s2, y);
77 
78 	clearSubtitles();
79 
80 	if (!scumm_stricmp("clear", s)) {
81 		return;
82 	}
83 
84 	if (y != -1) {
85 		_subtitleY = y;
86 	}
87 
88 	// FIXME: render subtitles using the right color (10 instead of 0).
89 	// The original game features a nasty hack, having the font rendering routine
90 	// replacing color 12 of font RUSSIA with 10 when preparing subtitles.
91 	uint8 color = (getPlatform() == Common::kPlatformAmiga) ? 11 : 0;
92 	_subtitle[0] = _gfx->createLabel(_labelFont, s, color);
93 	_gfx->showLabel(_subtitle[0], CENTER_LABEL_HORIZONTAL, _subtitleY);
94 	if (s2) {
95 		_subtitle[1] = _gfx->createLabel(_labelFont, s2, color);
96 		_gfx->showLabel(_subtitle[1], CENTER_LABEL_HORIZONTAL, _subtitleY + 5 + _labelFont->height());
97 	} else {
98 		_subtitle[1] = 0;
99 	}
100 #if 0	// disabled because no references to lip sync has been found in the scripts
101 	_subtitleLipSync = 0;
102 #endif
103 }
104 
clearSubtitles()105 void Parallaction_br::clearSubtitles() {
106 	if (_subtitle[0]) {
107 		_gfx->hideLabel(_subtitle[0]);
108 	}
109 	delete _subtitle[0];
110 	_subtitle[0] = 0;
111 
112 	if (_subtitle[1]) {
113 		_gfx->hideLabel(_subtitle[1]);
114 	}
115 	delete _subtitle[1];
116 	_subtitle[1] = 0;
117 }
118 
119 
DECLARE_COMMAND_OPCODE(location)120 DECLARE_COMMAND_OPCODE(location) {
121 	_vm->_location._startPosition = ctxt._cmd->_startPos;
122 	_vm->_location._startFrame = 0;
123 	_vm->_location._followerStartPosition = ctxt._cmd->_startPos2;
124 	_vm->_location._followerStartFrame = 0;
125 
126 	_vm->scheduleLocationSwitch(ctxt._cmd->_string.c_str());
127 }
128 
129 
DECLARE_COMMAND_OPCODE(open)130 DECLARE_COMMAND_OPCODE(open) {
131 	_vm->updateDoor(ctxt._cmd->_zone, false);
132 }
133 
134 
DECLARE_COMMAND_OPCODE(close)135 DECLARE_COMMAND_OPCODE(close) {
136 	_vm->updateDoor(ctxt._cmd->_zone, true);
137 }
138 
139 
DECLARE_COMMAND_OPCODE(on)140 DECLARE_COMMAND_OPCODE(on) {
141 	_vm->showZone(ctxt._cmd->_zone, true);
142 }
143 
144 
DECLARE_COMMAND_OPCODE(off)145 DECLARE_COMMAND_OPCODE(off) {
146 	_vm->showZone(ctxt._cmd->_zone, false);
147 }
148 
149 
DECLARE_COMMAND_OPCODE(call)150 DECLARE_COMMAND_OPCODE(call) {
151 	_vm->callFunction(ctxt._cmd->_callable, &ctxt._z);
152 }
153 
154 
DECLARE_COMMAND_OPCODE(drop)155 DECLARE_COMMAND_OPCODE(drop) {
156 	_vm->dropItem(ctxt._cmd->_object);
157 }
158 
159 
DECLARE_COMMAND_OPCODE(move)160 DECLARE_COMMAND_OPCODE(move) {
161 	_vm->scheduleWalk(ctxt._cmd->_move.x, ctxt._cmd->_move.y, false);
162 	suspend();
163 }
164 
DECLARE_COMMAND_OPCODE(start)165 DECLARE_COMMAND_OPCODE(start) {
166 	ctxt._cmd->_zone->_flags |= kFlagsActing;
167 }
168 
DECLARE_COMMAND_OPCODE(stop)169 DECLARE_COMMAND_OPCODE(stop) {
170 	ctxt._cmd->_zone->_flags &= ~kFlagsActing;
171 }
172 
173 
DECLARE_COMMAND_OPCODE(character)174 DECLARE_COMMAND_OPCODE(character) {
175 	debugC(9, kDebugExec, "Parallaction_br::cmdOp_character(%s)", ctxt._cmd->_string.c_str());
176 	_vm->changeCharacter(ctxt._cmd->_string.c_str());
177 }
178 
179 
DECLARE_COMMAND_OPCODE(followme)180 DECLARE_COMMAND_OPCODE(followme) {
181 	Common::String s(ctxt._cmd->_string);
182 	if (!s.compareToIgnoreCase("NULL")) {
183 		s.clear();
184 	}
185 	_vm->setFollower(s);
186 }
187 
188 
DECLARE_COMMAND_OPCODE(onmouse)189 DECLARE_COMMAND_OPCODE(onmouse) {
190 	_vm->_input->setMouseState(MOUSE_ENABLED_SHOW);
191 }
192 
193 
DECLARE_COMMAND_OPCODE(offmouse)194 DECLARE_COMMAND_OPCODE(offmouse) {
195 	_vm->_input->setMouseState(MOUSE_DISABLED);
196 }
197 
198 
DECLARE_COMMAND_OPCODE(add)199 DECLARE_COMMAND_OPCODE(add) {
200 	_vm->addInventoryItem(ctxt._cmd->_object);
201 }
202 
203 
DECLARE_COMMAND_OPCODE(leave)204 DECLARE_COMMAND_OPCODE(leave) {
205 	ZonePtr z = ctxt._cmd->_zone;
206 	_vm->dropItem(z->u._getIcon);
207 	_vm->showZone(z, true);
208 }
209 
210 
DECLARE_COMMAND_OPCODE(inc)211 DECLARE_COMMAND_OPCODE(inc) {
212 	int v = _vm->getCounterValue(ctxt._cmd->_counterName);
213 	_vm->setCounterValue(ctxt._cmd->_counterName, v + ctxt._cmd->_counterValue);
214 }
215 
216 
DECLARE_COMMAND_OPCODE(dec)217 DECLARE_COMMAND_OPCODE(dec) {
218 	int v = _vm->getCounterValue(ctxt._cmd->_counterName);
219 	_vm->setCounterValue(ctxt._cmd->_counterName, v - ctxt._cmd->_counterValue);
220 }
221 
222 // these definitions must match those in parser_br.cpp
223 #define CMD_TEST		25
224 #define CMD_TEST_GT		26
225 #define CMD_TEST_LT		27
226 
DECLARE_COMMAND_OPCODE(ifeq)227 DECLARE_COMMAND_OPCODE(ifeq) {
228 	_vm->testCounterCondition(ctxt._cmd->_counterName, CMD_TEST, ctxt._cmd->_counterValue);
229 }
230 
DECLARE_COMMAND_OPCODE(iflt)231 DECLARE_COMMAND_OPCODE(iflt) {
232 	_vm->testCounterCondition(ctxt._cmd->_counterName, CMD_TEST_LT, ctxt._cmd->_counterValue);
233 }
234 
DECLARE_COMMAND_OPCODE(ifgt)235 DECLARE_COMMAND_OPCODE(ifgt) {
236 	_vm->testCounterCondition(ctxt._cmd->_counterName, CMD_TEST_GT, ctxt._cmd->_counterValue);
237 }
238 
239 
DECLARE_COMMAND_OPCODE(let)240 DECLARE_COMMAND_OPCODE(let) {
241 	_vm->setCounterValue(ctxt._cmd->_counterName, ctxt._cmd->_counterValue);
242 }
243 
244 
DECLARE_COMMAND_OPCODE(music)245 DECLARE_COMMAND_OPCODE(music) {
246 	warning("Parallaction_br::cmdOp_music not yet implemented");
247 }
248 
249 
DECLARE_COMMAND_OPCODE(fix)250 DECLARE_COMMAND_OPCODE(fix) {
251 	ctxt._cmd->_zone->_flags |= kFlagsFixed;
252 }
253 
254 
DECLARE_COMMAND_OPCODE(unfix)255 DECLARE_COMMAND_OPCODE(unfix) {
256 	ctxt._cmd->_zone->_flags &= ~kFlagsFixed;
257 }
258 
259 
DECLARE_COMMAND_OPCODE(zeta)260 DECLARE_COMMAND_OPCODE(zeta) {
261 	_vm->_location._zeta0 = ctxt._cmd->_zeta0;
262 	_vm->_location._zeta1 = ctxt._cmd->_zeta1;
263 	_vm->_location._zeta2 = ctxt._cmd->_zeta2;
264 }
265 
266 
DECLARE_COMMAND_OPCODE(scroll)267 DECLARE_COMMAND_OPCODE(scroll) {
268 	Common::Point p;
269 	_vm->_gfx->getScrollPos(p);
270 	_vm->_gfx->initiateScroll(ctxt._cmd->_counterValue - p.x, 0);
271 }
272 
273 
DECLARE_COMMAND_OPCODE(swap)274 DECLARE_COMMAND_OPCODE(swap) {
275 	warning("Parallaction_br::cmdOp_swap does not handle a follower yet");
276 
277 	/*
278 		TODO:
279 		- fixup follower
280 		- change mouse pointer
281 	*/
282 
283 	const char *newCharacterName = ctxt._cmd->_string.c_str();
284 	AnimationPtr newCharacterAnimation = _vm->_location.findAnimation(newCharacterName);
285 	AnimationPtr oldCharaterAnimation = _vm->_char._ani;
286 
287 	Common::strlcpy(oldCharaterAnimation->_name, _vm->_char.getName(), ZONENAME_LENGTH);
288 	_vm->_char.setName(newCharacterName);
289 
290 	_vm->_char._ani = newCharacterAnimation;
291 	_vm->_char._talk = _vm->_disk->loadTalk(newCharacterName);
292 	Common::strlcpy(_vm->_char._ani->_name, "yourself", ZONENAME_LENGTH);
293 
294 	_vm->linkUnlinkedZoneAnimations();
295 
296 	_vm->_inventory = _vm->findInventory(newCharacterName);
297 	_vm->_inventoryRenderer->setInventory(_vm->_inventory);
298 
299 	_vm->_input->setCharacterPointer(newCharacterName);
300 }
301 
302 
DECLARE_COMMAND_OPCODE(give)303 DECLARE_COMMAND_OPCODE(give) {
304 	int item = ctxt._cmd->_object;
305 	Inventory *targetInventory = _vm->findInventory(ctxt._cmd->_characterName.c_str());
306 
307 	if (targetInventory) {
308 		targetInventory->addItem(item);
309 	}
310 
311 	_vm->_inventory->removeItem(item);
312 }
313 
314 
DECLARE_COMMAND_OPCODE(text)315 DECLARE_COMMAND_OPCODE(text) {
316 	_vm->setupSubtitles(ctxt._cmd->_string.c_str(), ctxt._cmd->_string2.c_str(), ctxt._cmd->_zeta0);
317 }
318 
319 
DECLARE_COMMAND_OPCODE(part)320 DECLARE_COMMAND_OPCODE(part) {
321 	_vm->_nextPart = ctxt._cmd->_counterValue;
322 }
323 
324 
DECLARE_COMMAND_OPCODE(testsfx)325 DECLARE_COMMAND_OPCODE(testsfx) {
326 	warning("Parallaction_br::cmdOp_testsfx not completely implemented");
327 	_vm->clearLocationFlags(kFlagsTestTrue);	// should test if sfx are enabled
328 }
329 
330 
DECLARE_COMMAND_OPCODE(ret)331 DECLARE_COMMAND_OPCODE(ret) {
332 	g_engineFlags |= kEngineReturn;
333 }
334 
335 
DECLARE_COMMAND_OPCODE(onsave)336 DECLARE_COMMAND_OPCODE(onsave) {
337 	warning("Parallaction_br::cmdOp_onsave not yet implemented");
338 }
339 
340 
DECLARE_COMMAND_OPCODE(offsave)341 DECLARE_COMMAND_OPCODE(offsave) {
342 	warning("Parallaction_br::cmdOp_offsave not yet implemented");
343 }
344 
DECLARE_INSTRUCTION_OPCODE(invalid)345 DECLARE_INSTRUCTION_OPCODE(invalid) {
346 	error("Can't execute invalid opcode %i", ctxt._inst->_index);
347 }
348 
DECLARE_COMMAND_OPCODE(clear)349 DECLARE_COMMAND_OPCODE(clear) {
350 	if (ctxt._cmd->_flags & kFlagsGlobal) {
351 		ctxt._cmd->_flags &= ~kFlagsGlobal;
352 		g_globalFlags &= ~ctxt._cmd->_flags;
353 	} else {
354 		_vm->clearLocationFlags(ctxt._cmd->_flags);
355 	}
356 }
357 
DECLARE_COMMAND_OPCODE(speak)358 DECLARE_COMMAND_OPCODE(speak) {
359 	// WORKAROUND: this avoids crashing when the zone is not parsed, like in the case
360 	// of script bug in ticket #4251.
361 	if (!ctxt._cmd->_zone) {
362 		return;
363 	}
364 
365 	if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak && ctxt._cmd->_zone->u._speakDialogue) {
366 		_vm->enterDialogueMode(ctxt._cmd->_zone);
367 	} else {
368 		_vm->_activeZone = ctxt._cmd->_zone;
369 	}
370 }
371 
372 
DECLARE_COMMAND_OPCODE(get)373 DECLARE_COMMAND_OPCODE(get) {
374 	ctxt._cmd->_zone->_flags &= ~kFlagsFixed;
375 	_vm->runZone(ctxt._cmd->_zone);
376 }
377 
DECLARE_COMMAND_OPCODE(toggle)378 DECLARE_COMMAND_OPCODE(toggle) {
379 	if (ctxt._cmd->_flags & kFlagsGlobal) {
380 		ctxt._cmd->_flags &= ~kFlagsGlobal;
381 		g_globalFlags ^= ctxt._cmd->_flags;
382 	} else {
383 		_vm->toggleLocationFlags(ctxt._cmd->_flags);
384 	}
385 }
386 
DECLARE_COMMAND_OPCODE(quit)387 DECLARE_COMMAND_OPCODE(quit) {
388 	_vm->quitGame();
389 }
390 
DECLARE_COMMAND_OPCODE(invalid)391 DECLARE_COMMAND_OPCODE(invalid) {
392 	error("Can't execute invalid command '%i'", ctxt._cmd->_id);
393 }
394 
DECLARE_COMMAND_OPCODE(set)395 DECLARE_COMMAND_OPCODE(set) {
396 	if (ctxt._cmd->_flags & kFlagsGlobal) {
397 		ctxt._cmd->_flags &= ~kFlagsGlobal;
398 		g_globalFlags |= ctxt._cmd->_flags;
399 	} else {
400 		_vm->setLocationFlags(ctxt._cmd->_flags);
401 	}
402 }
403 
404 
405 
DECLARE_INSTRUCTION_OPCODE(on)406 DECLARE_INSTRUCTION_OPCODE(on) {
407 	_vm->showZone(ctxt._inst->_z, true);
408 }
409 
410 
DECLARE_INSTRUCTION_OPCODE(off)411 DECLARE_INSTRUCTION_OPCODE(off) {
412 	_vm->showZone(ctxt._inst->_z, false);
413 }
414 
415 
DECLARE_INSTRUCTION_OPCODE(set)416 DECLARE_INSTRUCTION_OPCODE(set) {
417 	ctxt._inst->_opA.setValue(ctxt._inst->_opB.getValue());
418 }
419 
420 
421 
DECLARE_INSTRUCTION_OPCODE(inc)422 DECLARE_INSTRUCTION_OPCODE(inc) {
423 	InstructionPtr inst = ctxt._inst;
424 
425 	int16 rvalue = inst->_opB.getValue();
426 
427 	if (inst->_flags & kInstMod) {	// mod
428 		int16 _bx = (rvalue > 0 ? rvalue : -rvalue);
429 		if (ctxt._modCounter % _bx != 0) return;
430 
431 		rvalue = (rvalue > 0 ?  1 : -1);
432 	}
433 
434 	int16 lvalue = inst->_opA.getValue();
435 
436 	switch (inst->_index) {
437 	case INST_INC:
438 		lvalue += rvalue;
439 		break;
440 
441 	case INST_DEC:
442 		lvalue -= rvalue;
443 		break;
444 
445 	case INST_MUL:
446 		lvalue *= rvalue;
447 		break;
448 
449 	case INST_DIV:
450 		lvalue /= rvalue;
451 		break;
452 
453 	default:
454 		error("This should never happen. Report immediately");
455 	}
456 
457 	inst->_opA.setValue(lvalue);
458 
459 }
460 
461 
DECLARE_INSTRUCTION_OPCODE(put)462 DECLARE_INSTRUCTION_OPCODE(put) {
463 	// NOTE: there is not a single occurrence of PUT in the scripts
464 	warning("PUT instruction is not implemented");
465 }
466 
467 
468 
DECLARE_INSTRUCTION_OPCODE(wait)469 DECLARE_INSTRUCTION_OPCODE(wait) {
470 	// NOTE: there is not a single occurrence of WAIT in the scripts
471 	warning("WAIT instruction is not implemented");
472 }
473 
474 
DECLARE_INSTRUCTION_OPCODE(start)475 DECLARE_INSTRUCTION_OPCODE(start) {
476 	ctxt._inst->_z->_flags |= kFlagsActing;
477 }
478 
479 
DECLARE_INSTRUCTION_OPCODE(process)480 DECLARE_INSTRUCTION_OPCODE(process) {
481 	_vm->_activeZone2 = ctxt._inst->_z;
482 }
483 
484 
DECLARE_INSTRUCTION_OPCODE(move)485 DECLARE_INSTRUCTION_OPCODE(move) {
486 	// NOTE: I couldn't find evidence of scripts containing this instruction being used
487 	InstructionPtr inst = ctxt._inst;
488 	_vm->scheduleWalk(inst->_opA.getValue(), inst->_opB.getValue(), false);
489 	ctxt._suspend = true;
490 }
491 
492 
DECLARE_INSTRUCTION_OPCODE(color)493 DECLARE_INSTRUCTION_OPCODE(color) {
494 	InstructionPtr inst = ctxt._inst;
495 	_vm->_gfx->_palette.setEntry(inst->_opB.getValue(), inst->_colors[0], inst->_colors[1], inst->_colors[2]);
496 	_vm->_gfx->setPalette(_vm->_gfx->_palette);
497 }
498 
499 
DECLARE_INSTRUCTION_OPCODE(mask)500 DECLARE_INSTRUCTION_OPCODE(mask) {
501 #if 0
502 	Instruction *inst = *ctxt._inst;
503 	_gfx->_bgLayers[0] = inst->_opA.getRValue();
504 	_gfx->_bgLayers[1] = inst->_opB.getRValue();
505 	_gfx->_bgLayers[2] = inst->_opC.getRValue();
506 #endif
507 }
508 
509 
DECLARE_INSTRUCTION_OPCODE(print)510 DECLARE_INSTRUCTION_OPCODE(print) {
511 	// NOTE: there is not a single occurrence of PRINT in the scripts
512 	// I suppose it was used for debugging
513 	warning("PRINT instruction is not implemented");
514 }
515 
DECLARE_INSTRUCTION_OPCODE(text)516 DECLARE_INSTRUCTION_OPCODE(text) {
517 	InstructionPtr inst = ctxt._inst;
518 	_vm->setupSubtitles(inst->_text.c_str(), inst->_text2.c_str(), inst->_y);
519 }
520 
521 
DECLARE_INSTRUCTION_OPCODE(ifeq)522 DECLARE_INSTRUCTION_OPCODE(ifeq) {
523 	InstructionPtr inst = ctxt._inst;
524 	bool cond = inst->_opA.getValue() == inst->_opB.getValue();
525 	if (!cond) {
526 		ctxt._ip = inst->_endif;
527 	}
528 }
529 
530 
DECLARE_INSTRUCTION_OPCODE(iflt)531 DECLARE_INSTRUCTION_OPCODE(iflt) {
532 	InstructionPtr inst = ctxt._inst;
533 	bool cond = inst->_opA.getValue() < inst->_opB.getValue();
534 	if (!cond) {
535 		ctxt._ip = inst->_endif;
536 	}
537 }
538 
539 
DECLARE_INSTRUCTION_OPCODE(ifgt)540 DECLARE_INSTRUCTION_OPCODE(ifgt) {
541 	InstructionPtr inst = ctxt._inst;
542 	bool cond = inst->_opA.getValue() > inst->_opB.getValue();
543 	if (!cond) {
544 		ctxt._ip = inst->_endif;
545 	}
546 }
547 
548 
DECLARE_INSTRUCTION_OPCODE(endif)549 DECLARE_INSTRUCTION_OPCODE(endif) {
550 	// nothing to do here
551 }
552 
553 
DECLARE_INSTRUCTION_OPCODE(stop)554 DECLARE_INSTRUCTION_OPCODE(stop) {
555 	ZonePtr z = ctxt._inst->_z;
556 
557 	// Prevent execution if zone is missing. The known case is "PART2/insegui.scr", which has
558 	// "STOP insegui", which doesn't exist (see ticket #9193 for the gory details)
559 	if (!z) return;
560 
561 	if (ACTIONTYPE(z) == kZoneHear) {
562 		warning("Parallaction_br::instOp_stop not yet implemented for HEAR zones");
563 		// TODO: stop music or sound effects generated by a zone.
564 	} else {
565 		z->_flags &= ~kFlagsActing;
566 	}
567 }
568 
DECLARE_INSTRUCTION_OPCODE(loop)569 DECLARE_INSTRUCTION_OPCODE(loop) {
570 	InstructionPtr inst = ctxt._inst;
571 
572 	ctxt._program->_loopCounter = inst->_opB.getValue();
573 	ctxt._program->_loopStart = ctxt._ip;
574 }
575 
576 
DECLARE_INSTRUCTION_OPCODE(endloop)577 DECLARE_INSTRUCTION_OPCODE(endloop) {
578 	if (--ctxt._program->_loopCounter > 0) {
579 		ctxt._ip = ctxt._program->_loopStart;
580 	}
581 }
582 
DECLARE_INSTRUCTION_OPCODE(show)583 DECLARE_INSTRUCTION_OPCODE(show) {
584 	ctxt._suspend = true;
585 }
586 
DECLARE_INSTRUCTION_OPCODE(call)587 DECLARE_INSTRUCTION_OPCODE(call) {
588 	_vm->callFunction(ctxt._inst->_immediate, 0);
589 }
590 
591 
DECLARE_INSTRUCTION_OPCODE(endscript)592 DECLARE_INSTRUCTION_OPCODE(endscript) {
593 	if ((ctxt._anim->_flags & kFlagsLooping) == 0) {
594 		ctxt._anim->_flags &= ~kFlagsActing;
595 		_vm->_cmdExec->run(ctxt._anim->_commands, ctxt._anim);
596 		ctxt._program->_status = kProgramDone;
597 	}
598 
599 	ctxt._ip = 0;
600 	ctxt._suspend = true;
601 }
602 
603 
CommandExec_br(Parallaction_br * vm)604 CommandExec_br::CommandExec_br(Parallaction_br* vm) : CommandExec(vm), _vm(vm) {
605 	CommandOpcodeSet *table = 0;
606 
607 	SetOpcodeTable(_opcodes);
608 	COMMAND_OPCODE(invalid);
609 	COMMAND_OPCODE(set);
610 	COMMAND_OPCODE(clear);
611 	COMMAND_OPCODE(start);
612 	COMMAND_OPCODE(speak);
613 	COMMAND_OPCODE(get);
614 	COMMAND_OPCODE(location);
615 	COMMAND_OPCODE(open);
616 	COMMAND_OPCODE(close);
617 	COMMAND_OPCODE(on);
618 	COMMAND_OPCODE(off);
619 	COMMAND_OPCODE(call);
620 	COMMAND_OPCODE(toggle);
621 	COMMAND_OPCODE(drop);
622 	COMMAND_OPCODE(quit);
623 	COMMAND_OPCODE(move);
624 	COMMAND_OPCODE(stop);
625 	COMMAND_OPCODE(character);
626 	COMMAND_OPCODE(followme);
627 	COMMAND_OPCODE(onmouse);
628 	COMMAND_OPCODE(offmouse);
629 	COMMAND_OPCODE(add);
630 	COMMAND_OPCODE(leave);
631 	COMMAND_OPCODE(inc);
632 	COMMAND_OPCODE(dec);
633 	COMMAND_OPCODE(ifeq);
634 	COMMAND_OPCODE(iflt);
635 	COMMAND_OPCODE(ifgt);
636 	COMMAND_OPCODE(let);
637 	COMMAND_OPCODE(music);
638 	COMMAND_OPCODE(fix);
639 	COMMAND_OPCODE(unfix);
640 	COMMAND_OPCODE(zeta);
641 	COMMAND_OPCODE(scroll);
642 	COMMAND_OPCODE(swap);
643 	COMMAND_OPCODE(give);
644 	COMMAND_OPCODE(text);
645 	COMMAND_OPCODE(part);
646 	COMMAND_OPCODE(testsfx);
647 	COMMAND_OPCODE(ret);
648 	COMMAND_OPCODE(onsave);
649 	COMMAND_OPCODE(offsave);
650 }
651 
652 
ProgramExec_br(Parallaction_br * vm)653 ProgramExec_br::ProgramExec_br(Parallaction_br *vm) : _vm(vm) {
654 	_instructionNames = _instructionNamesRes_br;
655 
656 	ProgramOpcodeSet *table = 0;
657 
658 	SetOpcodeTable(_opcodes);
659 	INSTRUCTION_OPCODE(invalid);
660 	INSTRUCTION_OPCODE(on);
661 	INSTRUCTION_OPCODE(off);
662 	INSTRUCTION_OPCODE(set);		// x
663 	INSTRUCTION_OPCODE(set);		// y
664 	INSTRUCTION_OPCODE(set);		// z
665 	INSTRUCTION_OPCODE(set);		// f
666 	INSTRUCTION_OPCODE(loop);
667 	INSTRUCTION_OPCODE(endloop);
668 	INSTRUCTION_OPCODE(show);		// show
669 	INSTRUCTION_OPCODE(inc);
670 	INSTRUCTION_OPCODE(inc);		// dec
671 	INSTRUCTION_OPCODE(set);
672 	INSTRUCTION_OPCODE(put);
673 	INSTRUCTION_OPCODE(call);
674 	INSTRUCTION_OPCODE(wait);
675 	INSTRUCTION_OPCODE(start);
676 	INSTRUCTION_OPCODE(process);
677 	INSTRUCTION_OPCODE(move);
678 	INSTRUCTION_OPCODE(color);
679 	INSTRUCTION_OPCODE(process);	// sound
680 	INSTRUCTION_OPCODE(mask);
681 	INSTRUCTION_OPCODE(print);
682 	INSTRUCTION_OPCODE(text);
683 	INSTRUCTION_OPCODE(inc);		// mul
684 	INSTRUCTION_OPCODE(inc);		// div
685 	INSTRUCTION_OPCODE(ifeq);
686 	INSTRUCTION_OPCODE(iflt);
687 	INSTRUCTION_OPCODE(ifgt);
688 	INSTRUCTION_OPCODE(endif);
689 	INSTRUCTION_OPCODE(stop);
690 	INSTRUCTION_OPCODE(endscript);
691 }
692 
693 
694 
695 } // namespace Parallaction
696