0x9a
[scummvm-innocent.git] / engines / innocent / opcode_handlers.cpp
blob4232f3b85dc9924ba95d0985bb27c30a9a5c294e
1 #include "innocent/inter.h"
3 #include <vector>
5 #include "innocent/actor.h"
6 #include "innocent/animation.h"
7 #include "innocent/exit.h"
8 #include "innocent/graphics.h"
9 #include "innocent/innocent.h"
10 #include "innocent/logic.h"
11 #include "innocent/movie.h"
12 #include "innocent/musicparser.h"
13 #include "innocent/room.h"
14 #include "innocent/util.h"
16 #include "common/events.h"
17 #include "common/util.h"
19 namespace Innocent {
20 #define OPCODE(num) template<> Interpreter::OpResult Interpreter::opcodeHandler<num>(ValueVector a, CodePointer current, CodePointer next)
22 OPCODE(0x00) {
23 // nop
24 debugC(3, kDebugLevelScript, "opcode 0x00: nop");
25 return kThxBye;
28 OPCODE(0x01) {
29 // exit
30 // (some peculiarities in conj. with op 0x38, needs research TODO)
31 debugC(3, kDebugLevelScript, "opcode 0x01: exit");
32 return kReturn;
35 OPCODE(0x02) {
36 // check equality
37 debugC(3, kDebugLevelScript, "opcode 0x02: if %s == %s", +a[0], +a[1]);
38 unless (a[0] == a[1])
39 return kFail;
40 return kThxBye;
43 OPCODE(0x03) {
44 // check inequality
45 debugC(3, kDebugLevelScript, "opcode 0x03: if %s != %s", +a[0], +a[1]);
46 if (a[0] == a[1])
47 return kFail;
48 return kThxBye;
51 OPCODE(0x04) {
52 // less than
53 debugC(3, kDebugLevelScript, "opcode 0x04: if %s < %s", +a[0], +a[1]);
54 unless (a[0] < a[1])
55 return kFail;
56 return kThxBye;
59 OPCODE(0x05) {
60 // greater than
61 debugC(3, kDebugLevelScript, "opcode 0x05: if %s > %s", +a[0], +a[1]);
62 unless (a[0] > a[1])
63 return kFail;
64 return kThxBye;
67 OPCODE(0x0f) {
68 // check room
69 debugC(3, kDebugLevelScript, "opcode 0x0f: if current room == %s then", +a[0]);
70 unless (a[0] == _logic->currentRoom())
71 return kFail;
72 return kThxBye;
75 OPCODE(0x12) {
76 // if sound is on then
77 // (argument is a set of flags, 1 - adlib, 2 - sb, 4 - roland)
78 debugC(2, kDebugLevelScript, "opcode 0x12: if sound is on then partial STUB");
79 // just say roland+sb for now
80 unless (a[0] & 6)
81 return kFail;
82 return kThxBye;
85 OPCODE(0x13) {
86 // if left button is up
87 // and current mode isn't null
88 debugC(1, kDebugLevelScript, "opcode 0x13: if 1st button is up and mode isn't null partial STUB");
89 if (_engine->_eventMan->getButtonState() & Common::EventManager::LBUTTON)
90 return kFail;
91 return kThxBye;
94 /*OPCODE(0x1f) {
95 // if actor in current room then whatever
96 debugC(1, kDebugLevelScript, "opcode 0x1f: if actor %s in current room and STUB then", +a[1]);
97 if (_logic->getActor(a[1])->room() == _logic->currentRoom())
98 error("case with condition true unhandled");
99 else
100 return kFail;
101 return kThxBye;
104 OPCODE(0x24) {
105 // check nonzeroness
106 debugC(3, kDebugLevelScript, "opcode 0x24: if (%s)", +a[0]);
107 if (a[0] == 0)
108 return kFail;
109 return kThxBye;
112 OPCODE(0x2c) {
113 // else
114 debugC(3, kDebugLevelScript, "opcode 0x2c: else");
116 return kElse;
119 OPCODE(0x2d) {
120 // end if
121 debugC(3, kDebugLevelScript, "opcode 0x2d: end if");
122 return kEndIf;
125 OPCODE(0x35) {
126 // jump
127 debugC(3, kDebugLevelScript, "opcode 0x35: jump to %s", +a[0]);
128 return static_cast<CodePointer &>(a[0]);
131 OPCODE(0x36) {
132 // call
133 debugC(3, kDebugLevelScript, ">>>opcode 0x36: call procedure %s", +a[0]);
134 CodePointer &p = static_cast<CodePointer &>(a[0]);
135 p.run();
136 debugC(3, kDebugLevelScript, "<<<opcode 0x36: called procedure %s", +a[0]);
137 return kReturn;
140 OPCODE(0x39) {
141 // run later
142 debugC(3, kDebugLevelScript, "opcode 0x39: execute main %s later", +a[0]);
143 _logic->runLater(CodePointer(static_cast<CodePointer &>(a[0]).offset(), _logic->mainInterpreter()));
144 return kThxBye;
147 OPCODE(0x3d) {
148 // save first arg -- instruction pointer -- for after skipping animation
149 debugC(3, kDebugLevelScript, "opcode 0x3d: store position to continue if animation skipped to %s", +a[0]);
150 Log.setSkipPoint(static_cast<CodePointer &>(a[0]));
151 return kThxBye;
154 OPCODE(0x54) {
155 debugC(3, kDebugLevelScript, "opcode 0x54: ask about '%s' at %s:%s %sx%s", +a[4], +a[0], +a[1], +a[2], +a[3]);
157 uint16 result;
158 unless ((result = _graphics->ask(a[0], a[1], a[2], a[3], a[4])) == 0xffff)
159 return CodePointer(result, this);
160 return kThxBye;
163 OPCODE(0x55) {
164 // paint text
165 // args: left, top, colour, text
166 debugC(3, kDebugLevelScript, "opcode 0x55: paint '%s' with colour %s at %s:%s", +a[3], +a[2], +a[0], +a[1]);
167 _graphics->paintText(a[0], a[1], a[2], a[3]);
168 return kThxBye;
171 OPCODE(0x56) {
172 // say text
173 debugC(3, kDebugLevelScript, "opcode 0x86: say %s for %s frames", +a[1], +a[0]);
174 Graf.say(a[1], a[1], a[0]);
175 return kThxBye;
178 OPCODE(0x57) {
179 // wait until said
180 debugC(3, kDebugLevelScript, "opcode 0x57: wait until text is said");
181 Graf.runWhenSaid(next);
182 return kReturn;
185 OPCODE(0x60) {
186 // lookup locally
187 // takes a list (1st)
188 // a value (2nd)
189 // a field (as offset from structure start) (3rd)
190 // first word on the list is entry length in words (minus one for index)
191 // then are entries, first word being index
192 // finds entry matching index == value in the list and
193 // saves value of specified field in 4th argument
194 uint16 offset = static_cast<CodePointer &>(a[0]).offset();
196 uint16 value;
197 byte *pos = _base + offset;
198 uint16 width = READ_LE_UINT16(pos);
199 pos += 2;
200 while(true) {
201 uint16 index = READ_LE_UINT16(pos);
202 if (index == 0xffff) {
203 value = index;
204 break;
206 pos += 2;
207 if (index == a[1]) {
208 value = READ_LE_UINT16(pos + a[2]);
209 break;
211 pos += width * 2;
214 debugC(3, kDebugLevelScript, "opcode 0x60: %s = %d == search list %s for %s and return field %s", +a[3], value, +a[0], +a[1], +a[2]);
215 a[3] = value;
216 return kThxBye;
219 OPCODE(0x63) {
220 // get actor property
221 Actor *actor = _logic->getActor(a[0]);
222 const char *desc;
224 switch (uint16(a[1]) & 0xff) {
225 case Actor::kOffsetRoom:
226 desc = "Room";
227 a[2] = actor->room();
228 break;
229 /* case Actor::kOffsetOffset:
230 desc = "Offset";
231 a[2] = actor->offset();
232 break;
233 case Actor::kOffsetLeft:
234 desc = "Left";
235 a[2] = actor->left();
236 break;
237 case Actor::kOffsetTop:
238 desc = "Top";
239 a[2] = actor->top();
240 break;
241 case Actor::kOffsetMainSprite:
242 desc = "MainSprite";
243 a[2] = actor->mainSprite();
244 break;
245 case Actor::kOffsetTicksLeft:
246 desc = "TicksLeft";
247 a[2] = actor->ticksLeft();
248 break;
249 case Actor::kOffsetCode:
250 desc = "Code";
251 a[2] = actor->code();
252 break;
253 case Actor::kOffsetInterval:
254 desc = "Interval";
255 a[2] = actor->interval();
256 break;*/
257 default:
258 error("unhandled actor property %s", +a[1]);
261 debugC(3, kDebugLevelScript, "opcode 0x63: %s = get %s of actor %s", +a[2], desc, +a[0]);
262 return kThxBye;
265 OPCODE(0x6d) {
266 // increment
267 debugC(3, kDebugLevelScript, "opcode 0x6d: %s++", +a[0]);
268 a[0]++;
269 return kThxBye;
272 OPCODE(0x6f) {
273 // decrement
274 debugC(3, kDebugLevelScript, "opcode 0x6d: %s--", +a[0]);
275 a[0]--;
276 return kThxBye;
279 OPCODE(0x70) {
280 // assign
281 debugC(3, kDebugLevelScript, "opcode 0x70: %s = %s", +a[0], +a[1]);
282 a[0] = a[1];
283 return kThxBye;
286 OPCODE(0x72) {
287 // assign 1
288 debugC(3, kDebugLevelScript, "opcode 0x72: %s = 1", +a[0]);
289 a[0] = 1;
290 return kThxBye;
293 OPCODE(0x73) {
294 // assign 0
295 debugC(3, kDebugLevelScript, "opcode 0x73: %s = 0", +a[0]);
296 a[0] = 0;
297 return kThxBye;
300 OPCODE(0x77) {
301 // initialize protagonist
302 debugC(3, kDebugLevelScript, "opcode 0x77: go to room %s facing %s", +a[0], +a[1]);
303 _logic->protagonist()->setRoom(a[0], a[1]);
304 _logic->changeRoom(a[0]);
305 return kThxBye;
308 OPCODE(0x79) {
309 // move actor to another room
310 debugC(1, kDebugLevelScript, "opcode 0x79: move actor %s to room %s (and set current animation frame to %s STUB)", +a[0], +a[1], +a[2]);
311 _logic->getActor(a[0])->setRoom(a[1]);
312 return kThxBye;
315 OPCODE(0x7c) {
316 // toggle exit state
317 Exit *exit = _logic->blockProgram()->getExit(a[0]);
318 debugC(3, kDebugLevelScript, "opcode 0x7c: toggling exit %s to %s", +a[0], exit->isEnabled() ? "disabled" : "enabled");
319 exit->setEnabled(!exit->isEnabled());
320 return kThxBye;
323 OPCODE(0x96) {
324 // disallow user interaction
325 debugC(1, kDebugLevelScript, "opcode 0x96: lock control STUB");
326 return kThxBye;
329 OPCODE(0x9a) {
330 // wait for actor to exit
331 debugC(3, kDebugLevelScript, "opcode 0x9a: wait for actor %s to exit", +a[0]);
333 Actor *ac = _logic->getActor(a[0]);
334 if (ac->room() != _logic->currentRoom())
335 return kThxBye;
337 ac->callMe(next);
338 return kReturn;
341 OPCODE(0x9b) {
342 // delay
343 debugC(3, kDebugLevelScript, "opcode 0x9b: delay %s frames", +a[0]);
344 _logic->runLater(next, a[0]);
345 return kReturn;
348 OPCODE(0x9c) {
349 // wait until another room
350 debugC(3, kDebugLevelScript, "opcode 0x9a: wait until actor %s enters or %s ticks", +a[0], +a[1]);
352 Actor *ac = _logic->getActor(a[0]);
353 if (ac->room() != _logic->currentRoom()) {
354 ac->tellMe(next, a[1]);
355 return kReturn;
357 return kThxBye;
360 OPCODE(0x9d) {
361 // set protagonist
362 debugC(3, kDebugLevelScript, "opcode 0x9d: set protagonist(%s)", +a[0]);
363 _logic->setProtagonist(a[0]);
364 return kThxBye;
367 // OPCODE(0xab) {
368 // // set protagonist frame
369 // debugC(3, kDebugLevelScript, "opcode 0xab: set protagonist frame to %s", +a[0]);
371 // Actor *ac = Log.protagonist();
372 // if (!ac->isVisible()) {
373 // ac->whenYouHideUpCall(current);
374 // return kReturn;
375 // }
377 // ac->setFrame(a[0]);
378 // return kThxBye;
379 // }
381 OPCODE(0xad) {
382 // turn actor
383 debugC(3, kDebugLevelScript, "opcode 0xad: move actor %s to frame %s next", +a[0], +a[1]);
385 Actor *ac = _logic->getActor(a[0]);
386 if (ac->isFine()) {
387 ac->callMe(current);
388 return kReturn;
391 // TODO: special handling for protagonist
392 ac->setFrame(a[1]);
394 return kThxBye;
397 // OPCODE(0xbc) {
398 // // hide actor
399 // debugC(3, kDebugLevelScript, "opcode 0xbc: hide actor %s", +a[0]);
400 // _logic->getActor(a[0])->hide();
401 // return kThxBye;
402 // }
404 // OPCODE(0xbd) {
405 // // set protagonist animation
406 // debugC(3, kDebugLevelScript, "opcode 0xbd: set protagonist animation to %s", +a[0]);
408 // Actor *ac = Log.protagonist();
409 // if (!ac->isVisible()) {
410 // ac->whenYouHideUpCall(current);
411 // return kReturn;
412 // }
414 // CodePointer p(static_cast<CodePointer &>(a[0]).offset(), Log.mainInterpreter());
415 // ac->setAnimation(p);
416 // return kThxBye;
417 // }
419 OPCODE(0xc2) {
420 // add animation at cursor
421 debugC(3, kDebugLevelScript, "opcode 0xc2: add animation %s at cursor partial STUB", +a[0]);
422 _logic->addAnimation(new Animation(static_cast<CodePointer &>(a[0]), _graphics->cursorPosition()));
423 return kThxBye;
426 OPCODE(0xc6) {
427 // suspend execution until an animation's ip points to 0xff
428 debugC(3, kDebugLevelScript, "opcode 0xc6: wait on animation %s", +a[0]);
429 _logic->animation(a[0])->runOnNextFrame(next);
430 return kReturn;
433 OPCODE(0xc7) {
434 // play movie
435 debugC(3, kDebugLevelScript, "opcode 0xc7: play movie %s with slowness %s", +a[0], +a[1]);
436 Movie *m = Movie::fromFile(reinterpret_cast<char *>((byte *)(a[0])));
437 m->setFrameDelay(a[1]);
438 m->play();
439 return kThxBye;
442 OPCODE(0xc8) {
443 // set backdrop
444 // (not sure what's the difference to c9)
445 debugC(3, kDebugLevelScript, "opcode 0xc8: set backdrop(%s)", +a[0]);
446 _graphics->setBackdrop(a[0]);
447 return kThxBye;
450 OPCODE(0xc9) {
451 // set backdrop
452 // does it set the default one?
453 debugC(3, kDebugLevelScript, "opcode 0xc9: set backdrop(%s)", +a[0]);
454 _graphics->setBackdrop(a[0]);
455 return kThxBye;
458 OPCODE(0xcc) {
459 // go fullscreen
460 debugC(1, kDebugLevelScript, "opcode 0xcc: go fullscreen STUB");
461 return kThxBye;
464 OPCODE(0xce) {
465 // start cutscene
466 debugC(2, kDebugLevelScript, "opcode 0xce: start cutscene partial STUB");
467 Graf.hideCursor();
468 // hide objects
469 // set game area height to 200
470 return kThxBye;
473 OPCODE(0xcf) {
474 // fade out
475 debugC(1, kDebugLevelScript, "opcode 0xcf: fadeout");
476 _graphics->fadeOut();
477 return kThxBye;
480 OPCODE(0xd0) {
481 debugC(1, kDebugLevelScript, "opcode 0xd0: partial fadeout");
482 Graf.fadeOut(Graphics::kPartialFade);
483 return kThxBye;
486 OPCODE(0xd1) {
487 debugC(3, kDebugLevelScript, "opcode 0xd1: fadein next paint");
488 _graphics->willFadein();
489 return kThxBye;
492 OPCODE(0xd2) {
493 // prepare interface palette
494 debugC(3, kDebugLevelScript, "opcode 0xd2: will fadein partially");
495 Graf.willFadein(Graphics::kPartialFade);
496 return kThxBye;
499 enum {
500 kCopyProtectionRoom = 81,
501 kIntroOffset = 0x33a3
504 OPCODE(0xd6) {
505 // change room
506 debugC(1, kDebugLevelScript, "opcode 0xd6: change room(%s)", +a[0]);
507 uint16 room = a[0];
508 if (room == kCopyProtectionRoom) {
509 if (_engine->_startRoom)
510 room = Eng._startRoom;
512 _logic->changeRoom(room);
513 if (room == kCopyProtectionRoom && !Eng._copyProtection) {
514 Log.runLater(CodePointer(kIntroOffset, Log.blockInterpreter()));
515 return kReturn;
517 return kThxBye;
520 OPCODE(0xdb) {
521 // add active rect
522 debugC(3, kDebugLevelScript, "opcode 0xdb: add rect %s at %s %s %s %s", +a[0], +a[1], +a[2], +a[3], +a[4]);
523 Log.room()->addRect(Room::Rect(a[0].signd(), Common::Rect(a[1].signd(), a[4].signd(), a[2].signd(), a[3].signd())));
524 return kThxBye;
527 OPCODE(0xdf) {
528 // add actor frame
529 std::vector<byte> nexts(8);
530 for (int i = 0; i < 4; i++) {
531 uint16 val = a[i+2];
532 nexts[2*i] = val & 0xff;
533 nexts[2*i+1] = val >> 8;
535 const int16 left = a[0].signd();
536 const int16 top = a[1].signd();
537 debugC(3, kDebugLevelScript, "opcode 0xdf: add actor frame %d %d %d %d %d %d %d %d %d %d", left, top, nexts[0], nexts[1], nexts[2], nexts[3], nexts[4], nexts[5], nexts[6], nexts[7]);
538 Log.room()->addActorFrame(Actor::Frame(left, top, nexts));
539 return kThxBye;
542 OPCODE(0xe5) {
543 // hide all exits from the map
544 debugC(1, kDebugLevelScript, "opcode 0xe5: hide exits from map STUB");
545 return kThxBye;
548 OPCODE(0xe6) {
549 // set room loop code
550 debugC(3, kDebugLevelScript, "opcode 0xe6: set room loop to %s", +a[0]);
551 assert(a[0].holdsCode());
552 _logic->setRoomLoop(static_cast<CodePointer &>(a[0]));
553 return kThxBye;
556 OPCODE(0xef) {
557 // random
558 uint16 value = _engine->getRandom(a[0]);
559 debugC(3, kDebugLevelScript, "opcode 0xef: %s = %d == random(%s)", +a[1], value, +a[0]);
560 a[1] = value;
561 return kThxBye;
564 OPCODE(0xf0) {
565 // load sfx set
566 debugC(1, kDebugLevelScript, "opcode 0xf0: load sfx set %s STUB", +a[0]);
567 return kThxBye;
570 OPCODE(0xf4) {
571 // play music
572 debugC(1, kDebugLevelScript, "opcode 0xf4: play music script at %s", +a[0]);
573 Music.loadMusic(static_cast<CodePointer &>(a[0]).offset() + Log.mainInterpreter()->_base);
574 return kThxBye;
577 OPCODE(0xf7) {
578 // stop music
579 debugC(1, kDebugLevelScript, "opcode 0xf7: stop music STUB");
580 return kThxBye;
583 OPCODE(0xf9) {
584 // set sound on
586 debugC(1, kDebugLevelScript, "opcode 0xf9: set %s to %s STUB", a[0] == 1 ? "music" : "sfx", +a[1]);
587 return kThxBye;
590 OPCODE(0xfc) {
591 // quit
592 debugC(3, kDebugLevelScript, "opcode 0xfc: quit%s", a[0] == 0 ? "" : " unconditionally");
593 if (a[0] == 0)
594 error("asking for quitting not implemented");
596 _engine->quitGame();
597 return kThxBye;
600 // #define ANIMCODE(n) template<> void Animation::handle<n>()
602 // ANIMCODE(2) {
603 // // set position
604 // uint16 left = READ_LE_UINT16(_code + 2);
605 // uint16 top = READ_LE_UINT16(_code + 4);
606 // setPosition(Common::Point(left, top));
607 // _code += 6;
608 // }
610 // ANIMCODE(7) {
611 // // get sprite id from main variable
612 // const uint16 offset = READ_LE_UINT16(_code + 2);
613 // const byte *var = _resources->getGlobalWordVariable(offset/2);
614 // const uint16 sprite = READ_LE_UINT16(var);
615 // setSprite(sprite);
616 // }
618 // ANIMCODE(26) {
619 // // set z index
620 // setZIndex(_code[1]);
621 // // TODO set field 16 to 0
622 // _code += 2;
623 // }
625 } // End of namespace Innocent