Fixed opcode to create frame correctly.
[scummvm-innocent.git] / engines / innocent / opcode_handlers.cpp
blobecbb9ce484064ae8b63d9cc5eefb13a7fc37dc72
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(0x41) {
155 // say (protagonist)
156 debugC(3, kDebugLevelScript, "opcode 0x41: say %s", +a[0]);
158 if (Log.protagonist()->isSpeaking()) {
159 Log.protagonist()->callMeWhenSilent(current);
160 return kReturn;
163 if (Log.protagonist()->isMoving()) {
164 Log.protagonist()->callMeWhenStill(current);
165 return kReturn;
168 Log.protagonist()->say(a[0]);
169 return kThxBye;
172 OPCODE(0x54) {
173 debugC(3, kDebugLevelScript, "opcode 0x54: ask about '%s' at %s:%s %sx%s", +a[4], +a[0], +a[1], +a[2], +a[3]);
175 uint16 result;
176 unless ((result = _graphics->ask(a[0], a[1], a[2], a[3], a[4])) == 0xffff)
177 return CodePointer(result, this);
178 return kThxBye;
181 OPCODE(0x55) {
182 // paint text
183 // args: left, top, colour, text
184 debugC(3, kDebugLevelScript, "opcode 0x55: paint '%s' with colour %s at %s:%s", +a[3], +a[2], +a[0], +a[1]);
185 _graphics->paintText(a[0], a[1], a[2], a[3]);
186 return kThxBye;
189 OPCODE(0x56) {
190 // say text
191 debugC(3, kDebugLevelScript, "opcode 0x86: say %s for %s frames", +a[1], +a[0]);
192 Graf.say(a[1], a[1], a[0]);
193 return kThxBye;
196 OPCODE(0x57) {
197 // wait until said
198 debugC(3, kDebugLevelScript, "opcode 0x57: wait until text is said");
199 Graf.runWhenSaid(next);
200 return kReturn;
203 OPCODE(0x60) {
204 // lookup locally
205 // takes a list (1st)
206 // a value (2nd)
207 // a field (as offset from structure start) (3rd)
208 // first word on the list is entry length in words (minus one for index)
209 // then are entries, first word being index
210 // finds entry matching index == value in the list and
211 // saves value of specified field in 4th argument
212 uint16 offset = static_cast<CodePointer &>(a[0]).offset();
214 uint16 value;
215 byte *pos = _base + offset;
216 uint16 width = READ_LE_UINT16(pos);
217 pos += 2;
218 while(true) {
219 uint16 index = READ_LE_UINT16(pos);
220 if (index == 0xffff) {
221 value = index;
222 break;
224 pos += 2;
225 if (index == a[1]) {
226 value = READ_LE_UINT16(pos + a[2]);
227 break;
229 pos += width * 2;
232 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]);
233 a[3] = value;
234 return kThxBye;
237 OPCODE(0x63) {
238 // get actor property
239 Actor *actor = _logic->getActor(a[0]);
240 const char *desc;
242 switch (uint16(a[1]) & 0xff) {
243 case Actor::kOffsetRoom:
244 desc = "Room";
245 a[2] = actor->room();
246 break;
247 /* case Actor::kOffsetOffset:
248 desc = "Offset";
249 a[2] = actor->offset();
250 break;
251 case Actor::kOffsetLeft:
252 desc = "Left";
253 a[2] = actor->left();
254 break;
255 case Actor::kOffsetTop:
256 desc = "Top";
257 a[2] = actor->top();
258 break;
259 case Actor::kOffsetMainSprite:
260 desc = "MainSprite";
261 a[2] = actor->mainSprite();
262 break;
263 case Actor::kOffsetTicksLeft:
264 desc = "TicksLeft";
265 a[2] = actor->ticksLeft();
266 break;
267 case Actor::kOffsetCode:
268 desc = "Code";
269 a[2] = actor->code();
270 break;
271 case Actor::kOffsetInterval:
272 desc = "Interval";
273 a[2] = actor->interval();
274 break;*/
275 default:
276 error("unhandled actor property %s", +a[1]);
279 debugC(3, kDebugLevelScript, "opcode 0x63: %s = get %s of actor %s", +a[2], desc, +a[0]);
280 return kThxBye;
283 OPCODE(0x6d) {
284 // increment
285 debugC(3, kDebugLevelScript, "opcode 0x6d: %s++", +a[0]);
286 a[0]++;
287 return kThxBye;
290 OPCODE(0x6f) {
291 // decrement
292 debugC(3, kDebugLevelScript, "opcode 0x6d: %s--", +a[0]);
293 a[0]--;
294 return kThxBye;
297 OPCODE(0x70) {
298 // assign
299 debugC(3, kDebugLevelScript, "opcode 0x70: %s = %s", +a[0], +a[1]);
300 a[0] = a[1];
301 return kThxBye;
304 OPCODE(0x72) {
305 // assign 1
306 debugC(3, kDebugLevelScript, "opcode 0x72: %s = 1", +a[0]);
307 a[0] = 1;
308 return kThxBye;
311 OPCODE(0x73) {
312 // assign 0
313 debugC(3, kDebugLevelScript, "opcode 0x73: %s = 0", +a[0]);
314 a[0] = 0;
315 return kThxBye;
318 OPCODE(0x77) {
319 // initialize protagonist
320 debugC(3, kDebugLevelScript, "opcode 0x77: go to room %s facing %s", +a[0], +a[1]);
321 _logic->protagonist()->setRoom(a[0], a[1]);
322 _logic->changeRoom(a[0]);
323 return kThxBye;
326 OPCODE(0x79) {
327 // move actor to another room
328 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]);
329 _logic->getActor(a[0])->setRoom(a[1]);
330 return kThxBye;
333 OPCODE(0x7c) {
334 // toggle exit state
335 Exit *exit = _logic->blockProgram()->getExit(a[0]);
336 debugC(3, kDebugLevelScript, "opcode 0x7c: toggling exit %s to %s", +a[0], exit->isEnabled() ? "disabled" : "enabled");
337 exit->setEnabled(!exit->isEnabled());
338 return kThxBye;
341 OPCODE(0x96) {
342 // disallow user interaction
343 debugC(1, kDebugLevelScript, "opcode 0x96: lock control STUB");
344 return kThxBye;
347 OPCODE(0x99) {
348 // wait for protagonist to exit
349 debugC(3, kDebugLevelScript, "opcoe 0x99: wait for protagonist to exit");
351 Actor *ac = _logic->protagonist();
352 if (ac->room() != _logic->currentRoom())
353 return kThxBye;
355 ac->callMe(next);
356 return kReturn;
359 OPCODE(0x9a) {
360 // wait for actor to exit
361 debugC(3, kDebugLevelScript, "opcode 0x9a: wait for actor %s to exit", +a[0]);
363 Actor *ac = _logic->getActor(a[0]);
364 if (ac->room() != _logic->currentRoom())
365 return kThxBye;
367 ac->callMe(next);
368 return kReturn;
371 OPCODE(0x9b) {
372 // delay
373 debugC(3, kDebugLevelScript, "opcode 0x9b: delay %s frames", +a[0]);
374 _logic->runLater(next, a[0]);
375 return kReturn;
378 OPCODE(0x9c) {
379 // wait until another room
380 debugC(3, kDebugLevelScript, "opcode 0x9a: wait until actor %s enters or %s ticks", +a[0], +a[1]);
382 Actor *ac = _logic->getActor(a[0]);
383 if (ac->room() != _logic->currentRoom()) {
384 ac->tellMe(next, a[1]);
385 return kReturn;
387 return kThxBye;
390 OPCODE(0x9d) {
391 // set protagonist
392 debugC(3, kDebugLevelScript, "opcode 0x9d: set protagonist(%s)", +a[0]);
393 _logic->setProtagonist(a[0]);
394 return kThxBye;
397 OPCODE(0xab) {
398 // set protagonist frame
399 debugC(3, kDebugLevelScript, "opcode 0xab: set protagonist frame to %s", +a[0]);
401 Actor *ac = Log.protagonist();
402 if (ac->isFine()) {
403 ac->callMe(current);
404 return kReturn;
407 ac->setFrame(a[0]);
408 return kThxBye;
411 OPCODE(0xad) {
412 // turn actor
413 debugC(3, kDebugLevelScript, "opcode 0xad: move actor %s to frame %s next", +a[0], +a[1]);
415 Actor *ac = _logic->getActor(a[0]);
416 if (ac->isFine()) {
417 ac->callMe(current);
418 return kReturn;
421 // TODO: special handling for protagonist
422 ac->setFrame(a[1]);
424 return kThxBye;
427 OPCODE(0xbc) {
428 // hide actor
429 debugC(3, kDebugLevelScript, "opcode 0xbc: hide actor %s", +a[0]);
430 _logic->getActor(a[0])->hide();
431 return kThxBye;
434 OPCODE(0xbd) {
435 // set protagonist animation
436 debugC(3, kDebugLevelScript, "opcode 0xbd: set protagonist animation to %s", +a[0]);
438 Actor *ac = Log.protagonist();
439 if (ac->isFine()) {
440 ac->callMe(current);
441 return kReturn;
444 CodePointer p(static_cast<CodePointer &>(a[0]).offset(), Log.mainInterpreter());
445 ac->setAnimation(p);
446 return kThxBye;
449 OPCODE(0xc2) {
450 // add animation at cursor
451 debugC(3, kDebugLevelScript, "opcode 0xc2: add animation %s at cursor partial STUB", +a[0]);
452 _logic->addAnimation(new Animation(static_cast<CodePointer &>(a[0]), _graphics->cursorPosition()));
453 return kThxBye;
456 OPCODE(0xc6) {
457 // suspend execution until an animation's ip points to 0xff
458 debugC(3, kDebugLevelScript, "opcode 0xc6: wait on animation %s", +a[0]);
459 _logic->animation(a[0])->runOnNextFrame(next);
460 return kReturn;
463 OPCODE(0xc7) {
464 // play movie
465 debugC(3, kDebugLevelScript, "opcode 0xc7: play movie %s with slowness %s", +a[0], +a[1]);
466 Movie *m = Movie::fromFile(reinterpret_cast<char *>((byte *)(a[0])));
467 m->setFrameDelay(a[1]);
468 m->play();
469 return kThxBye;
472 OPCODE(0xc8) {
473 // set backdrop
474 // (not sure what's the difference to c9)
475 debugC(3, kDebugLevelScript, "opcode 0xc8: set backdrop(%s)", +a[0]);
476 _graphics->setBackdrop(a[0]);
477 return kThxBye;
480 OPCODE(0xc9) {
481 // set backdrop
482 // does it set the default one?
483 debugC(3, kDebugLevelScript, "opcode 0xc9: set backdrop(%s)", +a[0]);
484 _graphics->setBackdrop(a[0]);
485 return kThxBye;
488 OPCODE(0xcc) {
489 // go fullscreen
490 debugC(1, kDebugLevelScript, "opcode 0xcc: go fullscreen STUB");
491 return kThxBye;
494 OPCODE(0xce) {
495 // start cutscene
496 debugC(2, kDebugLevelScript, "opcode 0xce: start cutscene partial STUB");
497 Graf.hideCursor();
498 // hide objects
499 // set game area height to 200
500 return kThxBye;
503 OPCODE(0xcf) {
504 // fade out
505 debugC(1, kDebugLevelScript, "opcode 0xcf: fadeout");
506 _graphics->fadeOut();
507 return kThxBye;
510 OPCODE(0xd0) {
511 debugC(1, kDebugLevelScript, "opcode 0xd0: partial fadeout");
512 Graf.fadeOut(Graphics::kPartialFade);
513 return kThxBye;
516 OPCODE(0xd1) {
517 debugC(3, kDebugLevelScript, "opcode 0xd1: fadein next paint");
518 _graphics->willFadein();
519 return kThxBye;
522 OPCODE(0xd2) {
523 // prepare interface palette
524 debugC(3, kDebugLevelScript, "opcode 0xd2: will fadein partially");
525 Graf.willFadein(Graphics::kPartialFade);
526 return kThxBye;
529 enum {
530 kCopyProtectionRoom = 81,
531 kIntroOffset = 0x33a3
534 OPCODE(0xd6) {
535 // change room
536 debugC(1, kDebugLevelScript, "opcode 0xd6: change room(%s)", +a[0]);
537 uint16 room = a[0];
538 if (room == kCopyProtectionRoom) {
539 if (_engine->_startRoom)
540 room = Eng._startRoom;
542 _logic->changeRoom(room);
543 if (room == kCopyProtectionRoom && !Eng._copyProtection) {
544 Log.runLater(CodePointer(kIntroOffset, Log.blockInterpreter()));
545 return kReturn;
547 return kThxBye;
550 OPCODE(0xdb) {
551 // add active rect
552 debugC(3, kDebugLevelScript, "opcode 0xdb: add rect %s at %s %s %s %s", +a[0], +a[1], +a[2], +a[3], +a[4]);
553 Log.room()->addRect(Room::Rect(a[0].signd(), Common::Rect(a[1].signd(), a[4].signd(), a[2].signd(), a[3].signd())));
554 return kThxBye;
557 OPCODE(0xdf) {
558 // add actor frame
559 std::vector<byte> nexts(8);
560 for (int i = 0; i < 4; i++) {
561 uint16 val = a[i+2];
562 nexts[2*i] = val & 0xff;
563 nexts[2*i+1] = val >> 8;
565 const int16 left = a[0].signd();
566 const int16 top = a[1].signd();
567 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]);
568 Log.room()->addActorFrame(Common::Point(left, top), nexts);
569 return kThxBye;
572 OPCODE(0xe5) {
573 // hide all exits from the map
574 debugC(1, kDebugLevelScript, "opcode 0xe5: hide exits from map STUB");
575 return kThxBye;
578 OPCODE(0xe6) {
579 // set room loop code
580 debugC(3, kDebugLevelScript, "opcode 0xe6: set room loop to %s", +a[0]);
581 assert(a[0].holdsCode());
582 _logic->setRoomLoop(static_cast<CodePointer &>(a[0]));
583 return kThxBye;
586 OPCODE(0xef) {
587 // random
588 uint16 value = _engine->getRandom(a[0]);
589 debugC(3, kDebugLevelScript, "opcode 0xef: %s = %d == random(%s)", +a[1], value, +a[0]);
590 a[1] = value;
591 return kThxBye;
594 OPCODE(0xf0) {
595 // load sfx set
596 debugC(1, kDebugLevelScript, "opcode 0xf0: load sfx set %s STUB", +a[0]);
597 return kThxBye;
600 OPCODE(0xf4) {
601 // play music
602 debugC(1, kDebugLevelScript, "opcode 0xf4: play music script at %s", +a[0]);
603 Music.loadMusic(static_cast<CodePointer &>(a[0]).offset() + Log.mainInterpreter()->_base);
604 return kThxBye;
607 OPCODE(0xf7) {
608 // stop music
609 debugC(1, kDebugLevelScript, "opcode 0xf7: stop music STUB");
610 return kThxBye;
613 OPCODE(0xf9) {
614 // set sound on
616 debugC(1, kDebugLevelScript, "opcode 0xf9: set %s to %s STUB", a[0] == 1 ? "music" : "sfx", +a[1]);
617 return kThxBye;
620 OPCODE(0xfc) {
621 // quit
622 debugC(3, kDebugLevelScript, "opcode 0xfc: quit%s", a[0] == 0 ? "" : " unconditionally");
623 if (a[0] == 0)
624 error("asking for quitting not implemented");
626 _engine->quitGame();
627 return kThxBye;
630 // #define ANIMCODE(n) template<> void Animation::handle<n>()
632 // ANIMCODE(2) {
633 // // set position
634 // uint16 left = READ_LE_UINT16(_code + 2);
635 // uint16 top = READ_LE_UINT16(_code + 4);
636 // setPosition(Common::Point(left, top));
637 // _code += 6;
638 // }
640 // ANIMCODE(7) {
641 // // get sprite id from main variable
642 // const uint16 offset = READ_LE_UINT16(_code + 2);
643 // const byte *var = _resources->getGlobalWordVariable(offset/2);
644 // const uint16 sprite = READ_LE_UINT16(var);
645 // setSprite(sprite);
646 // }
648 // ANIMCODE(26) {
649 // // set z index
650 // setZIndex(_code[1]);
651 // // TODO set field 16 to 0
652 // _code += 2;
653 // }
655 } // End of namespace Innocent