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 "prince/script.h"
24 #include "prince/prince.h"
25 #include "prince/flags.h"
26 #include "prince/variatxt.h"
27 #include "prince/font.h"
28 #include "prince/hero.h"
29 #include "prince/resource.h"
30 #include "prince/animation.h"
31
32 #include "common/debug.h"
33 #include "common/debug-channels.h"
34 #include "common/archive.h"
35 #include "common/memstream.h"
36
37 namespace Prince {
38
39 static const uint16 kNumOpcodes = 144;
40
Room()41 Room::Room() {}
42
loadRoom(byte * roomData)43 bool Room::loadRoom(byte *roomData) {
44 int roomSize = 64;
45 Common::MemoryReadStream roomStream(roomData, roomSize);
46
47 _mobs = roomStream.readSint32LE();
48 _backAnim = roomStream.readSint32LE();
49 _obj = roomStream.readSint32LE();
50 _nak = roomStream.readSint32LE();
51 _itemUse = roomStream.readSint32LE();
52 _itemGive = roomStream.readSint32LE();
53 _walkTo = roomStream.readSint32LE();
54 _examine = roomStream.readSint32LE();
55 _pickup = roomStream.readSint32LE();
56 _use = roomStream.readSint32LE();
57 _pushOpen = roomStream.readSint32LE();
58 _pullClose = roomStream.readSint32LE();
59 _talk = roomStream.readSint32LE();
60 _give = roomStream.readSint32LE();
61
62 return true;
63 }
64
getOptionOffset(int option)65 int Room::getOptionOffset(int option) {
66 switch (option) {
67 case 0:
68 return _walkTo;
69 case 1:
70 return _examine;
71 case 2:
72 return _pickup;
73 case 3:
74 return _use;
75 case 4:
76 return _pushOpen;
77 case 5:
78 return _pullClose;
79 case 6:
80 return _talk;
81 case 7:
82 return _give;
83 default:
84 error("Wrong option - nr %d", option);
85 }
86 }
87
Script(PrinceEngine * vm)88 Script::Script(PrinceEngine *vm) : _vm(vm), _data(nullptr), _dataSize(0) {
89 }
90
~Script()91 Script::~Script() {
92 if (_data != nullptr) {
93 free(_data);
94 _dataSize = 0;
95 _data = nullptr;
96 }
97 }
98
loadStream(Common::SeekableReadStream & stream)99 bool Script::loadStream(Common::SeekableReadStream &stream) {
100 _dataSize = stream.size();
101 if (!_dataSize) {
102 return false;
103 }
104
105 _data = (byte *)malloc(_dataSize);
106
107 if (!_data) {
108 return false;
109 }
110
111 stream.read(_data, _dataSize);
112
113 Common::MemoryReadStream scriptDataStream(_data, _dataSize);
114 _scriptInfo.rooms = scriptDataStream.readSint32LE();
115 _scriptInfo.startGame = scriptDataStream.readSint32LE();
116 _scriptInfo.restoreGame = scriptDataStream.readSint32LE();
117 _scriptInfo.stdExamine = scriptDataStream.readSint32LE();
118 _scriptInfo.stdPickup = scriptDataStream.readSint32LE();
119 _scriptInfo.stdUse = scriptDataStream.readSint32LE();
120 _scriptInfo.stdOpen = scriptDataStream.readSint32LE();
121 _scriptInfo.stdClose = scriptDataStream.readSint32LE();
122 _scriptInfo.stdTalk = scriptDataStream.readSint32LE();
123 _scriptInfo.stdGive = scriptDataStream.readSint32LE();
124 _scriptInfo.usdCode = scriptDataStream.readSint32LE();
125 _scriptInfo.invObjExam = scriptDataStream.readSint32LE();
126 _scriptInfo.invObjUse = scriptDataStream.readSint32LE();
127 _scriptInfo.invObjUU = scriptDataStream.readSint32LE();
128 _scriptInfo.stdUseItem = scriptDataStream.readSint32LE();
129 _scriptInfo.lightSources = scriptDataStream.readSint32LE();
130 _scriptInfo.specRout = scriptDataStream.readSint32LE();
131 _scriptInfo.invObjGive = scriptDataStream.readSint32LE();
132 _scriptInfo.stdGiveItem = scriptDataStream.readSint32LE();
133 _scriptInfo.goTester = scriptDataStream.readSint32LE();
134
135 return true;
136 }
137
readScript16(uint32 address)138 uint16 Script::readScript16(uint32 address) {
139 assert((_data + address + sizeof(uint16)) <= (_data + _dataSize));
140 uint16 data = READ_LE_UINT16(&_data[address]);
141 return data;
142 }
143
readScript32(uint32 address)144 uint32 Script::readScript32(uint32 address) {
145 assert((_data + address + sizeof(uint32)) <= (_data + _dataSize));
146 uint32 data = READ_LE_UINT32(&_data[address]);
147 return data;
148 }
149
getLightX(int locationNr)150 int16 Script::getLightX(int locationNr) {
151 return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8]);
152 }
153
getLightY(int locationNr)154 int16 Script::getLightY(int locationNr) {
155 return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8 + 2]);
156 }
157
getShadowScale(int locationNr)158 int32 Script::getShadowScale(int locationNr) {
159 return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8 + 4]);
160 }
161
getStartGameOffset()162 uint32 Script::getStartGameOffset() {
163 return _scriptInfo.startGame;
164 }
165
getLocationInitScript(int initRoomTableOffset,int roomNr)166 uint32 Script::getLocationInitScript(int initRoomTableOffset, int roomNr) {
167 return (uint32)READ_LE_UINT32(&_data[initRoomTableOffset + roomNr * 4]);
168 }
169
getMobVisible(int roomMobOffset,uint16 mob)170 byte Script::getMobVisible(int roomMobOffset, uint16 mob) {
171 return _data[roomMobOffset + mob];
172 }
173
setMobVisible(int roomMobOffset,uint16 mob,byte value)174 void Script::setMobVisible(int roomMobOffset, uint16 mob, byte value) {
175 _data[roomMobOffset + mob] = value;
176 }
177
getRoomOffset(int locationNr)178 uint8 *Script::getRoomOffset(int locationNr) {
179 return &_data[_scriptInfo.rooms + locationNr * 64];
180 }
181
getOptionStandardOffset(int option)182 int32 Script::getOptionStandardOffset(int option) {
183 switch (option) {
184 case 1:
185 return _scriptInfo.stdExamine;
186 case 2:
187 return _scriptInfo.stdPickup;
188 case 3:
189 return _scriptInfo.stdUse;
190 case 4:
191 return _scriptInfo.stdOpen;
192 case 5:
193 return _scriptInfo.stdClose;
194 case 6:
195 return _scriptInfo.stdTalk;
196 case 7:
197 return _scriptInfo.stdGive;
198 default:
199 error("Wrong standard option - nr %d", option);
200 }
201 }
202
getHeroAnimName(int offset)203 uint8 *Script::getHeroAnimName(int offset) {
204 return &_data[offset];
205 }
206
getBackAnimId(int roomBackAnimOffset,int slot)207 uint32 Script::getBackAnimId(int roomBackAnimOffset, int slot) {
208 uint32 animId = READ_LE_UINT32(&_data[roomBackAnimOffset + slot * 4]);
209 return animId;
210 }
211
setBackAnimId(int roomBackAnimOffset,int slot,int animId)212 void Script::setBackAnimId(int roomBackAnimOffset, int slot, int animId) {
213 WRITE_LE_UINT32(&_data[roomBackAnimOffset + slot * 4], animId);
214 }
215
getObjId(int roomObjOffset,int slot)216 byte Script::getObjId(int roomObjOffset, int slot) {
217 return _data[roomObjOffset + slot];
218 }
219
setObjId(int roomObjOffset,int slot,byte objectId)220 void Script::setObjId(int roomObjOffset, int slot, byte objectId) {
221 _data[roomObjOffset + slot] = objectId;
222 }
223
scanMobEvents(int mobMask,int dataEventOffset)224 int Script::scanMobEvents(int mobMask, int dataEventOffset) {
225 debug(3, "scanMobEvents: mobMask: %d", mobMask);
226 int i = 0;
227 int16 mob;
228 int32 code;
229 do {
230 mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 6]);
231 if (mob == mobMask) {
232 code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 6 + 2]);
233 debug(3, "scanMobEvents: code: %d", code);
234 return code;
235 }
236 i++;
237 } while (mob != -1);
238 return -1;
239 }
240
scanMobEventsWithItem(int mobMask,int dataEventOffset,int itemMask)241 int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask) {
242 debug(3, "scanMobEventsWithItem: mobMask: %d", mobMask);
243 int i = 0;
244 int16 mob;
245 int16 item;
246 int32 code;
247 do {
248 mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8]);
249 if (mob == mobMask) {
250 item = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8 + 2]);
251 if (item == itemMask) {
252 code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 8 + 4]);
253 debug(3, "scanMobEventsWithItem: itemMask: %d", item);
254 debug(3, "scanMobEventsWithItem: code: %d", code);
255 return code;
256 }
257 }
258 i++;
259 } while (mob != -1);
260 return -1;
261 }
262
installSingleBackAnim(Common::Array<BackgroundAnim> & backAnimList,int slot,int roomBackAnimOffset)263 void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset) {
264
265 _vm->removeSingleBackAnim(slot); // free slot before loading
266
267 int offset = roomBackAnimOffset + slot * 4; // BackgroundAnim offset for selected slot number
268
269 BackgroundAnim newBackgroundAnim; // BackgroundAnim seq data and its array of Anim
270
271 int animOffset = READ_LE_UINT32(&_data[offset]); // pos of BackgroundAnim data in script
272 int anims = READ_LE_UINT32(&_data[animOffset + 8]); // amount of Anim in BackgroundAnim
273
274 if (anims == 0) {
275 anims = 1; // anims with 0 as amount in game data has just 1 animation
276 }
277
278 if (animOffset != 0) {
279 Common::MemoryReadStream stream(_data, _dataSize); // stream from script data
280 for (int i = 0; i < anims; i++) {
281 Anim newAnim;
282 stream.seek(animOffset + kStructSizeBAS + kStructSizeBASA * i);
283 // Anim BASA data
284 newAnim._basaData._num = stream.readUint16LE();
285 newAnim._basaData._start = stream.readUint16LE();
286 newAnim._basaData._end = stream.readUint16LE();
287
288 // Anim number in game files
289 int animNumber = newAnim._basaData._num;
290 const Common::String animName = Common::String::format("AN%02d", animNumber);
291 const Common::String shadowName = Common::String::format("AN%02dS", animNumber);
292 newAnim._animData = new Animation();
293 newAnim._shadowData = new Animation();
294 Resource::loadResource(newAnim._animData, animName.c_str(), true);
295 if (!Resource::loadResource(newAnim._shadowData, shadowName.c_str(), false)) {
296 delete newAnim._shadowData;
297 newAnim._shadowData = nullptr;
298 }
299
300 newAnim._usage = 0;
301 newAnim._state = 0; // enabled
302 if ((_vm->_animList[animNumber]._flags & 4)) {
303 newAnim._state = 1;
304 newAnim._frame = _vm->_animList[animNumber]._endPhase;
305 newAnim._showFrame = _vm->_animList[animNumber]._endPhase;
306 } else {
307 newAnim._frame = _vm->_animList[animNumber]._startPhase;
308 newAnim._showFrame = _vm->_animList[animNumber]._startPhase;
309 }
310 newAnim._flags = _vm->_animList[animNumber]._flags;
311 newAnim._lastFrame = _vm->_animList[animNumber]._endPhase;
312 newAnim._loopFrame = _vm->_animList[animNumber]._loopPhase;
313 newAnim._loopType = _vm->_animList[animNumber]._loopType;
314 newAnim._nextAnim = _vm->_animList[animNumber]._nextAnim;
315 newAnim._x = _vm->_animList[animNumber]._x;
316 newAnim._y = _vm->_animList[animNumber]._y;
317 newAnim._currFrame = 0;
318 newAnim._currX = _vm->_animList[animNumber]._x;
319 newAnim._currY = _vm->_animList[animNumber]._y;
320 newAnim._currW = 0;
321 newAnim._currH = 0;
322 newAnim._packFlag = 0;
323 newAnim._shadowBack = _vm->_animList[animNumber]._type;
324 newBackgroundAnim.backAnims.push_back(newAnim);
325 }
326
327 // Anim BAS data
328 stream.seek(animOffset);
329 newBackgroundAnim._seq._type = stream.readUint32LE();
330 newBackgroundAnim._seq._data = stream.readUint32LE();
331 newBackgroundAnim._seq._anims = stream.readUint32LE();
332 stream.skip(12);
333 newBackgroundAnim._seq._current = newBackgroundAnim.backAnims[0]._basaData._num;
334 newBackgroundAnim._seq._counter = 0;
335 newBackgroundAnim._seq._currRelative = 0;
336 newBackgroundAnim._seq._data2 = stream.readUint32LE();
337
338 int start = newBackgroundAnim.backAnims[0]._basaData._start; // BASA_Start of first frame
339 int end = newBackgroundAnim.backAnims[0]._basaData._end; // BASA_End of first frame
340
341 if (start != -1) {
342 newBackgroundAnim.backAnims[0]._frame = start;
343 newBackgroundAnim.backAnims[0]._showFrame = start;
344 newBackgroundAnim.backAnims[0]._loopFrame = start;
345 }
346
347 if (end != -1) {
348 newBackgroundAnim.backAnims[0]._lastFrame = end;
349 }
350
351 backAnimList[slot] = newBackgroundAnim;
352 }
353 }
354
installBackAnims(Common::Array<BackgroundAnim> & backAnimList,int roomBackAnimOffset)355 void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset) {
356 for (int i = 0; i < _vm->kMaxBackAnims; i++) {
357 installSingleBackAnim(backAnimList, i, roomBackAnimOffset);
358 }
359 }
360
installObjects(int offset)361 void Script::installObjects(int offset) {
362 for (int i = 0; i < _vm->kMaxObjects; i++) {
363 _vm->_objSlot[i] = _data[offset];
364 offset++;
365 }
366 }
367
loadAllMasks(Common::Array<Mask> & maskList,int offset)368 bool Script::loadAllMasks(Common::Array<Mask> &maskList, int offset) {
369 Mask tempMask;
370 while (1) {
371 Common::MemoryReadStream maskStream(_data, _dataSize);
372 maskStream.seek(offset);
373 tempMask._state = maskStream.readUint16LE();
374 if (tempMask._state == 0xffff) {
375 break;
376 }
377 tempMask._flags = maskStream.readUint16LE();
378 tempMask._x1 = maskStream.readUint16LE();
379 tempMask._y1 = maskStream.readUint16LE();
380 tempMask._x2 = maskStream.readUint16LE();
381 tempMask._y2 = maskStream.readUint16LE();
382 tempMask._z = maskStream.readUint16LE();
383 tempMask._number = maskStream.readUint16LE();
384
385 const Common::String msStreamName = Common::String::format("MS%02d", tempMask._number);
386 Common::SeekableReadStream *msStream = SearchMan.createReadStreamForMember(msStreamName);
387 if (!msStream) {
388 tempMask._width = 0;
389 tempMask._height = 0;
390 tempMask._data = nullptr;
391 warning("loadAllMasks: Can't load %s", msStreamName.c_str());
392 delete msStream;
393 } else {
394 msStream = Resource::getDecompressedStream(msStream);
395
396 int32 dataSize = msStream->size();
397 if (dataSize != -1) {
398 tempMask._data = (byte *)malloc(dataSize);
399 if (msStream->read(tempMask._data, dataSize) != (uint32)dataSize) {
400 free(tempMask._data);
401 delete msStream;
402 return false;
403 }
404 delete msStream;
405
406 tempMask._width = tempMask.getWidth();
407 tempMask._height = tempMask.getHeight();
408 } else {
409 return false;
410 }
411 }
412
413 maskList.push_back(tempMask);
414 offset += 16; // size of Mask (Nak) struct
415 }
416 return true;
417 }
418
InterpreterFlags()419 InterpreterFlags::InterpreterFlags() {
420 resetAllFlags();
421 }
422
resetAllFlags()423 void InterpreterFlags::resetAllFlags() {
424 memset(_flags, 0, sizeof(_flags));
425 }
426
setFlagValue(Flags::Id flagId,int32 value)427 void InterpreterFlags::setFlagValue(Flags::Id flagId, int32 value) {
428 _flags[(uint32)flagId - kFlagMask] = value;
429 }
430
getFlagValue(Flags::Id flagId)431 int32 InterpreterFlags::getFlagValue(Flags::Id flagId) {
432 return _flags[(uint32)flagId - kFlagMask];
433 }
434
Interpreter(PrinceEngine * vm,Script * script,InterpreterFlags * flags)435 Interpreter::Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags) :
436 _vm(vm), _script(script), _flags(flags),
437 _stacktop(0), _opcodeNF(false), _opcodeEnd(false),
438 _waitFlag(0), _result(true) {
439
440 // Initialize the script
441 _mode = "fg";
442 _fgOpcodePC = _script->getStartGameOffset();
443 _bgOpcodePC = 0;
444 }
445
debugInterpreter(const char * s,...)446 void Interpreter::debugInterpreter(const char *s, ...) {
447 char buf[STRINGBUFLEN];
448 va_list va;
449 va_start(va, s);
450 vsnprintf(buf, STRINGBUFLEN, s, va);
451 va_end(va);
452
453 Common::String str = Common::String::format("@0x%08X: ", _lastInstruction);
454 str += Common::String::format("op %04d: ", _lastOpcode);
455 if (!strcmp(_mode, "fg")) {
456 debug(10, "PrinceEngine::Script %s %s", str.c_str(), buf);
457 }
458 }
459
stepBg()460 void Interpreter::stepBg() {
461 if (_bgOpcodePC) {
462 _mode = "bg";
463 _bgOpcodePC = step(_bgOpcodePC);
464 }
465 }
466
stepFg()467 void Interpreter::stepFg() {
468 if (_fgOpcodePC) {
469 _mode = "fg";
470 _fgOpcodePC = step(_fgOpcodePC);
471 }
472 }
473
step(uint32 opcodePC)474 uint32 Interpreter::step(uint32 opcodePC) {
475 _currentInstruction = opcodePC;
476
477 while (!_opcodeNF) {
478 _lastInstruction = _currentInstruction;
479
480 // Get the current opcode
481 _lastOpcode = readScript16();
482
483 if (_lastOpcode >= kNumOpcodes)
484 error(
485 "Trying to execute unknown opcode @0x%04X: %02d",
486 _currentInstruction,
487 _lastOpcode);
488
489 // Execute the current opcode
490 OpcodeFunc op = _opcodes[_lastOpcode];
491 (this->*op)();
492 if (_opcodeNF) {
493 _opcodeNF = 0;
494 break;
495 }
496 }
497
498 if (_opcodeEnd) {
499 _vm->quitGame();
500 }
501
502 return _currentInstruction;
503 }
504
storeNewPC(int opcodePC)505 void Interpreter::storeNewPC(int opcodePC) {
506 if (_flags->getFlagValue(Flags::GETACTION) == 1) {
507 _flags->setFlagValue(Flags::GETACTIONDATA, opcodePC);
508 opcodePC = _flags->getFlagValue(Flags::GETACTIONBACK);
509 }
510 _fgOpcodePC = opcodePC;
511 }
512
getLastOPCode()513 int Interpreter::getLastOPCode() {
514 return _lastOpcode;
515 }
516
getFgOpcodePC()517 int Interpreter::getFgOpcodePC() {
518 return _fgOpcodePC;
519 }
520
getCurrentString()521 uint32 Interpreter::getCurrentString() {
522 return _currentString;
523 }
524
setCurrentString(uint32 value)525 void Interpreter::setCurrentString(uint32 value) {
526 _currentString = value;
527 }
528
getString()529 byte *Interpreter::getString() {
530 return _string;
531 }
532
setString(byte * newString)533 void Interpreter::setString(byte *newString) {
534 _string = newString;
535 }
536
increaseString()537 void Interpreter::increaseString() {
538 while (*_string) {
539 _string++;
540 }
541 _string++;
542 }
543
setResult(byte value)544 void Interpreter::setResult(byte value) {
545 _result = value;
546 }
547
setBgOpcodePC(uint32 value)548 void Interpreter::setBgOpcodePC(uint32 value) {
549 _bgOpcodePC = value;
550 }
551
setFgOpcodePC(uint32 value)552 void Interpreter::setFgOpcodePC(uint32 value) {
553 _fgOpcodePC = value;
554 }
555
readScript16()556 uint16 Interpreter::readScript16() {
557 uint16 data = _script->readScript16(_currentInstruction);
558 _currentInstruction += sizeof(uint16);
559 return data;
560 }
561
readScript32()562 uint32 Interpreter::readScript32() {
563 uint32 data = _script->readScript32(_currentInstruction);
564 _currentInstruction += sizeof(uint32);
565 return data;
566 }
567
readScriptFlagValue()568 int32 Interpreter::readScriptFlagValue() {
569 uint16 value = readScript16();
570 if (value & InterpreterFlags::kFlagMask) {
571 return _flags->getFlagValue((Flags::Id)value);
572 }
573 return value;
574 }
575
readScriptFlagId()576 Flags::Id Interpreter::readScriptFlagId() {
577 return (Flags::Id)readScript16();
578 }
579
O_WAITFOREVER()580 void Interpreter::O_WAITFOREVER() {
581 _vm->changeCursor(_vm->_currentPointerNumber);
582 _opcodeNF = 1;
583 _currentInstruction -= 2;
584 //debugInterpreter("O_WAITFOREVER");
585 }
586
O_BLACKPALETTE()587 void Interpreter::O_BLACKPALETTE() {
588 _vm->blackPalette();
589 debugInterpreter("O_BLACKPALETTE");
590 }
591
O_SETUPPALETTE()592 void Interpreter::O_SETUPPALETTE() {
593 _vm->setPalette(_vm->_roomBmp->getPalette());
594 debugInterpreter("O_SETUPPALETTE");
595 }
596
O_INITROOM()597 void Interpreter::O_INITROOM() {
598 int32 roomId = readScriptFlagValue();
599 _vm->loadLocation(roomId);
600 _opcodeNF = 1;
601 debugInterpreter("O_INITROOM %d", roomId);
602 }
603
O_SETSAMPLE()604 void Interpreter::O_SETSAMPLE() {
605 int32 sampleId = readScriptFlagValue();
606 int32 sampleNameOffset = readScript32();
607 const char *sampleName = _script->getString(_currentInstruction + sampleNameOffset - 4);
608 _vm->loadSample(sampleId, sampleName);
609 debugInterpreter("O_SETSAMPLE %d %s", sampleId, sampleName);
610 }
611
O_FREESAMPLE()612 void Interpreter::O_FREESAMPLE() {
613 int32 sampleId = readScriptFlagValue();
614 _vm->freeSample(sampleId);
615 debugInterpreter("O_FREESAMPLE sampleId: %d", sampleId);
616 }
617
O_PLAYSAMPLE()618 void Interpreter::O_PLAYSAMPLE() {
619 int32 sampleId = readScriptFlagValue();
620 uint16 loopType = readScript16();
621 _vm->playSample(sampleId, loopType);
622 debugInterpreter("O_PLAYSAMPLE sampleId %d loopType %d", sampleId, loopType);
623 }
624
O_PUTOBJECT()625 void Interpreter::O_PUTOBJECT() {
626 int32 roomId = readScriptFlagValue();
627 int32 slot = readScriptFlagValue();
628 int32 objectId = readScriptFlagValue();
629 Room *room = new Room();
630 room->loadRoom(_script->getRoomOffset(roomId));
631 _vm->_script->setObjId(room->_obj, slot, objectId);
632 if (_vm->_locationNr == roomId) {
633 _vm->_objSlot[slot] = objectId;
634 }
635 delete room;
636 debugInterpreter("O_PUTOBJECT roomId %d, slot %d, objectId %d", roomId, slot, objectId);
637 }
638
O_REMOBJECT()639 void Interpreter::O_REMOBJECT() {
640 int32 roomId = readScriptFlagValue();
641 int32 slot = readScriptFlagValue();
642 Room *room = new Room();
643 room->loadRoom(_script->getRoomOffset(roomId));
644 _vm->_script->setObjId(room->_obj, slot, 0xFF);
645 if (_vm->_locationNr == roomId) {
646 _vm->_objSlot[slot] = 0xFF;
647 }
648 delete room;
649 debugInterpreter("O_REMOBJECT roomId %d slot %d", roomId, slot);
650 }
651
O_SHOWANIM()652 void Interpreter::O_SHOWANIM() {
653 int32 slot = readScriptFlagValue();
654 int32 animId = readScriptFlagValue();
655 _vm->freeNormAnim(slot);
656 Anim &anim = _vm->_normAnimList[slot];
657 AnimListItem &animList = _vm->_animList[animId];
658 anim._currFrame = 0;
659 anim._packFlag = 0;
660 anim._state = 0;
661 anim._frame = animList._startPhase;
662 anim._showFrame = animList._startPhase;
663 anim._lastFrame = animList._endPhase;
664 anim._loopFrame = animList._loopPhase;
665 anim._x = animList._x;
666 anim._y = animList._y;
667 anim._loopType = animList._loopType;
668 anim._shadowBack = animList._type;
669 anim._flags = animList._flags;
670 anim._nextAnim = animList._nextAnim;
671 int fileNumber = animList._fileNumber;
672 const Common::String animName = Common::String::format("AN%02d", fileNumber);
673 const Common::String shadowName = Common::String::format("AN%02dS", fileNumber);
674 anim._animData = new Animation();
675 anim._shadowData = new Animation();
676 Resource::loadResource(anim._animData, animName.c_str(), true);
677 if (!Resource::loadResource(anim._shadowData, shadowName.c_str(), false)) {
678 delete anim._shadowData;
679 anim._shadowData = nullptr;
680 }
681
682 // WALKAROUND: fix for turning off bard's wife background animation
683 // in front of bard's house (location 7) after giving her poem (item 33)
684 // in script: GiveLetter (line 11082)
685 if (_currentInstruction == kGiveLetterScriptFix) {
686 _vm->_backAnimList[1].backAnims[0]._state = 1;
687 }
688
689 debugInterpreter("O_SHOWANIM slot %d, animId %d", slot, animId);
690 }
691
O_CHECKANIMEND()692 void Interpreter::O_CHECKANIMEND() {
693 int32 slot = readScriptFlagValue();
694 if (_vm->_normAnimList[slot]._frame != _vm->_normAnimList[slot]._lastFrame - 1) {
695 _currentInstruction -= 4;
696 _opcodeNF = 1;
697 }
698 debugInterpreter("O_CHECKANIMEND slot %d", slot);
699 }
700
O_FREEANIM()701 void Interpreter::O_FREEANIM() {
702 int32 slot = readScriptFlagValue();
703 _vm->freeNormAnim(slot);
704 debugInterpreter("O_FREEANIM slot %d", slot);
705 }
706
O_CHECKANIMFRAME()707 void Interpreter::O_CHECKANIMFRAME() {
708 int32 slot = readScriptFlagValue();
709 int32 frameNumber = readScriptFlagValue();
710 if (_vm->_normAnimList[slot]._frame != frameNumber - 1) {
711 _currentInstruction -= 6;
712 _opcodeNF = 1;
713 }
714 debugInterpreter("O_CHECKANIMFRAME slot %d, frameNumber %d", slot, frameNumber);
715 }
716
O_PUTBACKANIM()717 void Interpreter::O_PUTBACKANIM() {
718 int32 roomId = readScriptFlagValue();
719 int32 slot = readScriptFlagValue();
720 int32 animId = readScript32();
721 Room *room = new Room();
722 room->loadRoom(_script->getRoomOffset(roomId));
723 _vm->_script->setBackAnimId(room->_backAnim, slot, animId);
724 if (_vm->_locationNr == roomId) {
725 _vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, room->_backAnim);
726 }
727 delete room;
728
729 // WALKAROUND: fix for turning on 'walking bird' background animation too soon,
730 // after completing 'throw a rock' mini-game in Silmaniona location.
731 // Second bird shouldn't appear when normal animation is still in use
732 // in script lines 13814 and 13848
733 if (_currentInstruction == kSecondBirdAnimationScriptFix) {
734 if (_vm->_normAnimList[1]._state == 0) {
735 _vm->_backAnimList[0].backAnims[0]._state = 1;
736 }
737 }
738
739 debugInterpreter("O_PUTBACKANIM roomId %d, slot %d, animId %d", roomId, slot, animId);
740 }
741
O_REMBACKANIM()742 void Interpreter::O_REMBACKANIM() {
743 int32 roomId = readScriptFlagValue();
744 int32 slot = readScriptFlagValue();
745 if (_vm->_locationNr == roomId) {
746 _vm->removeSingleBackAnim(slot);
747 }
748 Room *room = new Room();
749 room->loadRoom(_script->getRoomOffset(roomId));
750 _vm->_script->setBackAnimId(room->_backAnim, slot, 0);
751 delete room;
752 debugInterpreter("O_REMBACKANIM roomId %d, slot %d", roomId, slot);
753 }
754
O_CHECKBACKANIMFRAME()755 void Interpreter::O_CHECKBACKANIMFRAME() {
756 int32 slotId = readScriptFlagValue();
757 int32 frameId = readScriptFlagValue();
758 int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
759 if (_vm->_backAnimList[slotId].backAnims[currAnim]._frame != frameId - 1) {
760 _currentInstruction -= 6;
761 _opcodeNF = 1;
762 }
763 debugInterpreter("O_CHECKBACKANIMFRAME slotId %d, frameId %d", slotId, frameId);
764 }
765
766 // Not used in script
O_FREEALLSAMPLES()767 void Interpreter::O_FREEALLSAMPLES() {
768 error("O_FREEALLSAMPLES");
769 }
770
O_SETMUSIC()771 void Interpreter::O_SETMUSIC() {
772 uint16 musicId = readScript16();
773 _vm->loadMusic(musicId);
774 debugInterpreter("O_SETMUSIC musicId %d", musicId);
775 }
776
O_STOPMUSIC()777 void Interpreter::O_STOPMUSIC() {
778 _vm->stopMusic();
779 debugInterpreter("O_STOPMUSIC");
780 }
781
O__WAIT()782 void Interpreter::O__WAIT() {
783 int32 pause = readScriptFlagValue();
784 debugInterpreter("O__WAIT pause %d", pause);
785 if (!_waitFlag) {
786 // set new wait flag value and continue
787 _waitFlag = pause;
788 _opcodeNF = 1;
789 _currentInstruction -= 4;
790 return;
791 }
792 _waitFlag--;
793 if (_waitFlag > 0) {
794 _opcodeNF = 1;
795 _currentInstruction -= 4;
796 return;
797 }
798 }
799
800 // Not used in script
O_UPDATEOFF()801 void Interpreter::O_UPDATEOFF() {
802 error("O_UPDATEOFF");
803 }
804
805 // Not used in script
O_UPDATEON()806 void Interpreter::O_UPDATEON() {
807 error("O_UPDATEON");
808 }
809
810 // Not used in script
O_UPDATE()811 void Interpreter::O_UPDATE () {
812 error("O_UPDATE");
813 }
814
815 // Not used in script
O_CLS()816 void Interpreter::O_CLS() {
817 error("O_CLS");
818 }
819
O__CALL()820 void Interpreter::O__CALL() {
821 int32 address = readScript32();
822 _stack[_stacktop] = _currentInstruction;
823 _stacktop++;
824 _currentInstruction += address - 4;
825 debugInterpreter("O__CALL 0x%04X", _currentInstruction);
826 }
827
O_RETURN()828 void Interpreter::O_RETURN() {
829 if (_stacktop > 0) {
830 _stacktop--;
831 _currentInstruction = _stack[_stacktop];
832 debugInterpreter("O_RETURN 0x%04X", _currentInstruction);
833 } else {
834 error("O_RETURN: Stack is empty");
835 }
836 }
837
O_GO()838 void Interpreter::O_GO() {
839 int32 opPC = readScript32();
840 _currentInstruction += opPC - 4;
841 debugInterpreter("O_GO 0x%04X", opPC);
842 }
843
O_BACKANIMUPDATEOFF()844 void Interpreter::O_BACKANIMUPDATEOFF() {
845 int32 slotId = readScriptFlagValue();
846 int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
847 if (!_vm->_backAnimList[slotId].backAnims.empty()) {
848 _vm->_backAnimList[slotId].backAnims[currAnim]._state = 1;
849 }
850 debugInterpreter("O_BACKANIMUPDATEOFF slotId %d", slotId);
851 }
852
O_BACKANIMUPDATEON()853 void Interpreter::O_BACKANIMUPDATEON() {
854 int32 slotId = readScriptFlagValue();
855 int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
856 if (!_vm->_backAnimList[slotId].backAnims.empty()) {
857 _vm->_backAnimList[slotId].backAnims[currAnim]._state = 0;
858 }
859 debugInterpreter("O_BACKANIMUPDATEON slotId %d", slotId);
860 }
861
O_CHANGECURSOR()862 void Interpreter::O_CHANGECURSOR() {
863 int32 cursorId = readScriptFlagValue();
864 _vm->changeCursor(cursorId);
865 debugInterpreter("O_CHANGECURSOR %x", cursorId);
866 }
867
868 // Not used in script
O_CHANGEANIMTYPE()869 void Interpreter::O_CHANGEANIMTYPE() {
870 error("O_CHANGEANIMTYPE");
871 }
872
O__SETFLAG()873 void Interpreter::O__SETFLAG() {
874 Flags::Id flagId = readScriptFlagId();
875 int32 value = readScriptFlagValue();
876 _flags->setFlagValue((Flags::Id)(flagId), value);
877 debugInterpreter("O__SETFLAG 0x%04X (%s) = %d", flagId, Flags::getFlagName(flagId), value);
878 }
879
O_COMPARE()880 void Interpreter::O_COMPARE() {
881 Flags::Id flagId = readScriptFlagId();
882 int32 value = readScriptFlagValue();
883 _result = _flags->getFlagValue(flagId) != value;
884 debugInterpreter("O_COMPARE flagId 0x%04X (%s), value %d == %d (%d)", flagId, Flags::getFlagName(flagId), value, _flags->getFlagValue(flagId), _result);
885 }
886
O_JUMPZ()887 void Interpreter::O_JUMPZ() {
888 int32 offset = readScript32();
889 if (!_result) {
890 _currentInstruction += offset - 4;
891 }
892 debugInterpreter("O_JUMPZ result = %d, next %08x, offset 0x%08X", _result, _currentInstruction, offset);
893 }
894
O_JUMPNZ()895 void Interpreter::O_JUMPNZ() {
896 int32 offset = readScript32();
897 if (_result) {
898 _currentInstruction += offset - 4;
899 }
900 debugInterpreter("O_JUMPNZ result = %d, next %08x, offset 0x%08X", _result, _currentInstruction, offset);
901 }
902
O_EXIT()903 void Interpreter::O_EXIT() {
904 int32 exitCode = readScriptFlagValue();
905 _opcodeEnd = true;
906 _opcodeNF = 1;
907 if (exitCode == 0x2EAD) {
908 _vm->scrollCredits();
909 }
910 debugInterpreter("O_EXIT exitCode %d", exitCode);
911 }
912
O_ADDFLAG()913 void Interpreter::O_ADDFLAG() {
914 Flags::Id flagId = readScriptFlagId();
915 int32 value = readScriptFlagValue();
916 _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) + value);
917 if (_flags->getFlagValue(flagId)) {
918 _result = 1;
919 } else {
920 _result = 0;
921 }
922 debugInterpreter("O_ADDFLAG flagId %04x (%s), value %d", flagId, Flags::getFlagName(flagId), value);
923 }
924
O_TALKANIM()925 void Interpreter::O_TALKANIM() {
926 int32 animNumber = readScriptFlagValue();
927 int32 slot = readScriptFlagValue();
928 _vm->doTalkAnim(animNumber, slot, kNormalAnimation);
929 debugInterpreter("O_TALKANIM animNumber %d, slot %d", animNumber, slot);
930 }
931
O_SUBFLAG()932 void Interpreter::O_SUBFLAG() {
933 Flags::Id flagId = readScriptFlagId();
934 int32 value = readScriptFlagValue();
935 _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) - value);
936 if (_flags->getFlagValue(flagId)) {
937 _result = 1;
938 } else {
939 _result = 0;
940 }
941 debugInterpreter("O_SUBFLAG flagId %d, value %d", flagId, value);
942 }
943
O_SETSTRING()944 void Interpreter::O_SETSTRING() {
945 int32 offset = readScript32();
946 _currentString = offset;
947 if (offset >= 80000) {
948 _string = _vm->_variaTxt->getString(offset - 80000);
949 debugInterpreter("GetVaria %s", _string);
950 } else if (offset < 2000) {
951 _vm->_dialogData = &_vm->_dialogDat[offset * 4 - 4];
952 uint32 of = READ_LE_UINT32(_vm->_talkTxt + offset * 4);
953 const char *txt = (const char *)&_vm->_talkTxt[of];
954 _string = &_vm->_talkTxt[of];
955 debugInterpreter("TalkTxt %d %s", of, txt);
956 }
957 debugInterpreter("O_SETSTRING %04d", offset);
958 }
959
O_ANDFLAG()960 void Interpreter::O_ANDFLAG() {
961 Flags::Id flagId = readScriptFlagId();
962 int32 value = readScriptFlagValue();
963 _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) & value);
964 if (_flags->getFlagValue(flagId)) {
965 _result = 1;
966 } else {
967 _result = 0;
968 }
969 debugInterpreter("O_ANDFLAG flagId %d, value %d", flagId, value);
970 }
971
O_GETMOBDATA()972 void Interpreter::O_GETMOBDATA() {
973 Flags::Id flagId = readScriptFlagId();
974 int32 mobId = readScriptFlagValue();
975 int32 mobOffset = readScriptFlagValue();
976 int16 value = _vm->_mobList[mobId].getData((Mob::AttrId)mobOffset);
977 _flags->setFlagValue(flagId, value);
978 debugInterpreter("O_GETMOBDATA flagId %d, modId %d, mobOffset %d", flagId, mobId, mobOffset);
979 }
980
O_ORFLAG()981 void Interpreter::O_ORFLAG() {
982 Flags::Id flagId = readScriptFlagId();
983 int32 value = readScriptFlagValue();
984 _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) | value);
985 if (_flags->getFlagValue(flagId)) {
986 _result = 1;
987 } else {
988 _result = 0;
989 }
990 debugInterpreter("O_ORFLAG flagId %d, value %d", flagId, value);
991 }
992
O_SETMOBDATA()993 void Interpreter::O_SETMOBDATA() {
994 int32 mobId = readScriptFlagValue();
995 int32 mobOffset = readScriptFlagValue();
996 int32 value = readScriptFlagValue();
997 _vm->_mobList[mobId].setData((Mob::AttrId)mobOffset, value);
998 debugInterpreter("O_SETMOBDATA mobId %d, mobOffset %d, value %d", mobId, mobOffset, value);
999 }
1000
O_XORFLAG()1001 void Interpreter::O_XORFLAG() {
1002 Flags::Id flagId = readScriptFlagId();
1003 int32 value = readScriptFlagValue();
1004 _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) ^ value);
1005 if (_flags->getFlagValue(flagId)) {
1006 _result = 1;
1007 } else {
1008 _result = 0;
1009 }
1010 debugInterpreter("O_XORFLAG flagId %d, value %d", flagId, value);
1011 }
1012
O_GETMOBTEXT()1013 void Interpreter::O_GETMOBTEXT() {
1014 int32 mob = readScriptFlagValue();
1015 _currentString = _vm->_locationNr * 100 + mob + 60001;
1016 // FIXME: UB?
1017 // This casts away the constness of the pointer returned by c_str() which is
1018 // stored and potentially modified later (for example in printAt()).
1019 // Also, the pointer is only valid as long as _vm->_mobList[mob]
1020 // is around and _vm->_mobList[mob]._examText hasn't been modified by any of its
1021 // non-const member functions which also might or might not be a problem.
1022 _string = (byte *)_vm->_mobList[mob]._examText.c_str();
1023 debugInterpreter("O_GETMOBTEXT mob %d", mob);
1024 }
1025
O_MOVEHERO()1026 void Interpreter::O_MOVEHERO() {
1027 int32 heroId = readScriptFlagValue();
1028 int32 x = readScriptFlagValue();
1029 int32 y = readScriptFlagValue();
1030 int32 dir = readScriptFlagValue();
1031 _vm->moveRunHero(heroId, x, y, dir, false);
1032 debugInterpreter("O_MOVEHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir);
1033 }
1034
O_WALKHERO()1035 void Interpreter::O_WALKHERO() {
1036 int32 heroId = readScriptFlagValue();
1037 Hero *hero = nullptr;
1038 if (!heroId) {
1039 hero = _vm->_mainHero;
1040 } else if (heroId == 1) {
1041 hero = _vm->_secondHero;
1042 }
1043 if (hero != nullptr) {
1044 if (hero->_state != Hero::kHeroStateStay) {
1045 _currentInstruction -= 4;
1046 _opcodeNF = 1;
1047 }
1048 }
1049 debugInterpreter("O_WALKHERO %d", heroId);
1050 }
1051
O_SETHERO()1052 void Interpreter::O_SETHERO() {
1053 int32 heroId = readScriptFlagValue();
1054 int32 x = readScriptFlagValue();
1055 int32 y = readScriptFlagValue();
1056 int32 dir = readScriptFlagValue();
1057 Hero *hero = nullptr;
1058 if (!heroId) {
1059 hero = _vm->_mainHero;
1060 } else if (heroId == 1) {
1061 hero = _vm->_secondHero;
1062 }
1063 if (hero != nullptr) {
1064 hero->setPos(x, y);
1065 hero->_lastDirection = dir;
1066 hero->_visible = 1;
1067 hero->countDrawPosition();
1068 }
1069 debugInterpreter("O_SETHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir);
1070 }
1071
O_HEROOFF()1072 void Interpreter::O_HEROOFF() {
1073 int32 heroId = readScriptFlagValue();
1074 Hero *hero = nullptr;
1075 if (!heroId) {
1076 hero = _vm->_mainHero;
1077 } else if (heroId == 1) {
1078 hero = _vm->_secondHero;
1079 }
1080 if (hero != nullptr) {
1081 hero->setVisible(false);
1082 }
1083 debugInterpreter("O_HEROOFF %d", heroId);
1084 }
1085
O_HEROON()1086 void Interpreter::O_HEROON() {
1087 int32 heroId = readScriptFlagValue();
1088 Hero *hero = nullptr;
1089 if (!heroId) {
1090 hero = _vm->_mainHero;
1091 } else if (heroId == 1) {
1092 hero = _vm->_secondHero;
1093 }
1094 if (hero != nullptr) {
1095 hero->setVisible(true);
1096 }
1097 debugInterpreter("O_HEROON %d", heroId);
1098 }
1099
O_CLSTEXT()1100 void Interpreter::O_CLSTEXT() {
1101 int32 slot = readScriptFlagValue();
1102 _vm->_textSlots[slot]._str = nullptr;
1103 _vm->_textSlots[slot]._time = 0;
1104 debugInterpreter("O_CLSTEXT slot %d", slot);
1105 }
1106
O_CALLTABLE()1107 void Interpreter::O_CALLTABLE() {
1108 Flags::Id flagId = readScriptFlagId();
1109 int roomNr = _flags->getFlagValue(flagId);
1110 int32 tableOffset = readScript32();
1111 int initLocationScript = _script->getLocationInitScript(tableOffset, roomNr);
1112 if (initLocationScript) {
1113 _stack[_stacktop] = _currentInstruction;
1114 _stacktop++;
1115 _currentInstruction = initLocationScript;
1116 }
1117 debugInterpreter("O_CALLTABLE loc %d", roomNr);
1118 }
1119
O_CHANGEMOB()1120 void Interpreter::O_CHANGEMOB() {
1121 int32 mob = readScriptFlagValue();
1122 int32 value = readScriptFlagValue();
1123 value ^= 1;
1124 _vm->_script->setMobVisible(_vm->_room->_mobs, mob, value);
1125 _vm->_mobList[mob]._visible = value;
1126 debugInterpreter("O_CHANGEMOB mob %d, value %d", mob, value);
1127 }
1128
O_ADDINV()1129 void Interpreter::O_ADDINV() {
1130 int32 hero = readScriptFlagValue();
1131 int32 item = readScriptFlagValue();
1132 _vm->addInv(hero, item, false);
1133 debugInterpreter("O_ADDINV hero %d, item %d", hero, item);
1134 }
1135
O_REMINV()1136 void Interpreter::O_REMINV() {
1137 int32 hero = readScriptFlagValue();
1138 int32 item = readScriptFlagValue();
1139 _vm->remInv(hero, item);
1140 debugInterpreter("O_REMINV hero %d, item %d", hero, item);
1141 }
1142
1143 // Not used in script
O_REPINV()1144 void Interpreter::O_REPINV() {
1145 error("O_REPINV");
1146 }
1147
1148 // Not used in script
O_OBSOLETE_GETACTION()1149 void Interpreter::O_OBSOLETE_GETACTION() {
1150 error("O_OBSOLETE_GETACTION");
1151 }
1152
1153 // Not used in script
O_ADDWALKAREA()1154 void Interpreter::O_ADDWALKAREA() {
1155 error("O_ADDWALKAREA");
1156 }
1157
1158 // Not used in script
O_REMWALKAREA()1159 void Interpreter::O_REMWALKAREA() {
1160 error("O_REMWALKAREA");
1161 }
1162
1163 // Not used in script
O_RESTOREWALKAREA()1164 void Interpreter::O_RESTOREWALKAREA() {
1165 error("O_RESTOREWALKAREA");
1166 }
1167
O_WAITFRAME()1168 void Interpreter::O_WAITFRAME() {
1169 _opcodeNF = true;
1170 debugInterpreter("O_WAITFRAME");
1171 }
1172
O_SETFRAME()1173 void Interpreter::O_SETFRAME() {
1174 int32 anim = readScriptFlagValue();
1175 int32 frame = readScriptFlagValue();
1176 _vm->_normAnimList[anim]._frame = frame;
1177 debugInterpreter("O_SETFRAME anim %d, frame %d", anim, frame);
1178 }
1179
1180 // Not used in script
O_RUNACTION()1181 void Interpreter::O_RUNACTION() {
1182 error("O_RUNACTION");
1183 }
1184
O_COMPAREHI()1185 void Interpreter::O_COMPAREHI() {
1186 Flags::Id flag = readScriptFlagId();
1187 int32 value = readScriptFlagValue();
1188 int32 flagValue = _flags->getFlagValue(flag);
1189 if (flagValue > value) {
1190 _result = 0;
1191 } else {
1192 _result = 1;
1193 }
1194 debugInterpreter("O_COMPAREHI flag %04x - (%s), value %d, flagValue %d, result %d", flag, Flags::getFlagName(flag), value, flagValue, _result);
1195 }
1196
O_COMPARELO()1197 void Interpreter::O_COMPARELO() {
1198 Flags::Id flag = readScriptFlagId();
1199 int32 value = readScriptFlagValue();
1200 int32 flagValue = _flags->getFlagValue(flag);
1201 if (flagValue < value) {
1202 _result = 0;
1203 } else {
1204 _result = 1;
1205 }
1206 debugInterpreter("O_COMPARELO flag %04x - (%s), value %d, flagValue %d, result %d", flag, Flags::getFlagName(flag), value, flagValue, _result);
1207 }
1208
1209 // Not used in script
O_PRELOADSET()1210 void Interpreter::O_PRELOADSET() {
1211 error("O_PRELOADSET");
1212 }
1213
1214 // Not used in script
O_FREEPRELOAD()1215 void Interpreter::O_FREEPRELOAD() {
1216 error("O_FREEPRELOAD");
1217 }
1218
1219 // Not used in script
O_CHECKINV()1220 void Interpreter::O_CHECKINV() {
1221 error("O_CHECKINV");
1222 }
1223
O_TALKHERO()1224 void Interpreter::O_TALKHERO() {
1225 int32 hero = readScriptFlagValue();
1226 _vm->talkHero(hero);
1227 debugInterpreter("O_TALKHERO hero %d", hero);
1228 }
1229
O_WAITTEXT()1230 void Interpreter::O_WAITTEXT() {
1231 int32 slot = readScriptFlagValue();
1232 Text &text = _vm->_textSlots[slot];
1233 if (text._time && text._str) {
1234 if (_flags->getFlagValue(Flags::ESCAPED)) {
1235 text._time = 1;
1236 if (!slot) {
1237 _vm->_mainHero->_talkTime = 1;
1238 } else if (slot == 1) {
1239 _vm->_secondHero->_talkTime = 1;
1240 }
1241 } else {
1242 _opcodeNF = 1;
1243 _currentInstruction -= 4;
1244 }
1245 }
1246 //debugInterpreter("O_WAITTEXT slot %d", slot);
1247 }
1248
O_SETHEROANIM()1249 void Interpreter::O_SETHEROANIM() {
1250 int32 heroId = readScriptFlagValue();
1251 int32 offset = readScript32();
1252 Hero *hero = nullptr;
1253 if (!heroId) {
1254 hero = _vm->_mainHero;
1255 } else {
1256 hero = _vm->_secondHero;
1257 }
1258 if (hero != nullptr) {
1259 hero->freeHeroAnim();
1260 if (hero ->_specAnim == nullptr) {
1261 hero->_specAnim = new Animation();
1262 if (offset < 100) {
1263 const Common::String animName = Common::String::format("AN%02d", offset);
1264 Resource::loadResource(hero->_specAnim, animName.c_str(), true);
1265 } else {
1266 const Common::String animName = Common::String((const char *)_script->getHeroAnimName(offset));
1267 Common::String normalizedPath = lastPathComponent(animName, '\\');
1268 Resource::loadResource(hero->_specAnim, normalizedPath.c_str(), true);
1269 }
1270 hero->_phase = 0;
1271 hero->_state = Hero::kHeroStateSpec;
1272 }
1273 }
1274 debugInterpreter("O_SETHEROANIM hero %d, offset %d", hero, offset);
1275 }
1276
O_WAITHEROANIM()1277 void Interpreter::O_WAITHEROANIM() {
1278 int32 heroId = readScriptFlagValue();
1279 Hero *hero = nullptr;
1280 if (!heroId) {
1281 hero = _vm->_mainHero;
1282 } else {
1283 hero = _vm->_secondHero;
1284 }
1285 if (hero != nullptr) {
1286 if (hero->_state == Hero::kHeroStateSpec) {
1287 _currentInstruction -= 4;
1288 _opcodeNF = 1;
1289 }
1290 }
1291 debugInterpreter("O_WAITHEROANIM heroId %d", heroId);
1292 }
1293
O_GETHERODATA()1294 void Interpreter::O_GETHERODATA() {
1295 Flags::Id flagId = readScriptFlagId();
1296 int32 heroId = readScriptFlagValue();
1297 int32 heroOffset = readScriptFlagValue();
1298 Hero *hero = nullptr;
1299 if (!heroId) {
1300 hero = _vm->_mainHero;
1301 } else {
1302 hero = _vm->_secondHero;
1303 }
1304 if (hero != nullptr) {
1305 _flags->setFlagValue(flagId, hero->getData((Hero::AttrId)heroOffset));
1306 }
1307 debugInterpreter("O_GETHERODATA flag %04x - (%s), heroId %d, heroOffset %d", flagId, Flags::getFlagName(flagId), heroId, heroOffset);
1308 }
1309
1310 // No need of implementation here
O_GETMOUSEBUTTON()1311 void Interpreter::O_GETMOUSEBUTTON() {
1312 debugInterpreter("O_GETMOUSEBUTTON");
1313 }
1314
O_CHANGEFRAMES()1315 void Interpreter::O_CHANGEFRAMES() {
1316 int32 anim = readScriptFlagValue();
1317 int32 frame = readScriptFlagValue();
1318 int32 lastFrame = readScriptFlagValue();
1319 int32 loopFrame = readScriptFlagValue();
1320 _vm->_normAnimList[anim]._frame = frame;
1321 _vm->_normAnimList[anim]._lastFrame = lastFrame;
1322 _vm->_normAnimList[anim]._loopFrame = loopFrame;
1323 debugInterpreter("O_CHANGFRAMES anim %d, frame %d, lastFrame %d, loopFrame %d", anim, frame, lastFrame, loopFrame);
1324 }
1325
O_CHANGEBACKFRAMES()1326 void Interpreter::O_CHANGEBACKFRAMES() {
1327 int32 anim = readScriptFlagValue();
1328 int32 frame = readScriptFlagValue();
1329 int32 lastFrame = readScriptFlagValue();
1330 int32 loopFrame = readScriptFlagValue();
1331 int currAnim = _vm->_backAnimList[anim]._seq._currRelative;
1332 Anim &backAnim = _vm->_backAnimList[anim].backAnims[currAnim];
1333 backAnim._frame = frame;
1334 backAnim._lastFrame = lastFrame;
1335 backAnim._loopFrame = loopFrame;
1336 debugInterpreter("O_CHANGEBACKFRAMES anim %d, frame %d, lastFrame %d, loopFrame %d", anim, frame, lastFrame, loopFrame);
1337 }
1338
O_GETBACKANIMDATA()1339 void Interpreter::O_GETBACKANIMDATA() {
1340 Flags::Id flagId = readScriptFlagId();
1341 int32 animNumber = readScriptFlagValue();
1342 int32 animDataOffset = readScriptFlagValue();
1343 int currAnim = _vm->_backAnimList[animNumber]._seq._currRelative;
1344 int16 value = _vm->_backAnimList[animNumber].backAnims[currAnim].getAnimData((Anim::AnimOffsets)(animDataOffset));
1345 _flags->setFlagValue((Flags::Id)(flagId), value);
1346 debugInterpreter("O_GETBACKANIMDATA flag %04X (%s), animNumber %d, animDataOffset %d, value %d", flagId, Flags::getFlagName(flagId), animNumber, animDataOffset, value);
1347 }
1348
O_GETANIMDATA()1349 void Interpreter::O_GETANIMDATA() {
1350 Flags::Id flagId = readScriptFlagId();
1351 int32 anim = readScriptFlagValue();
1352 int32 animOffset = readScriptFlagValue();
1353 if (_vm->_normAnimList[anim]._animData != nullptr) {
1354 _flags->setFlagValue(flagId, _vm->_normAnimList[anim].getAnimData((Anim::AnimOffsets)(animOffset)));
1355 }
1356 debugInterpreter("O_GETANIMDATA flag %04X (%s), anim %d, animOffset %d", flagId, Flags::getFlagName(flagId), anim, animOffset);
1357 }
1358
O_SETBGCODE()1359 void Interpreter::O_SETBGCODE() {
1360 int32 offset = readScript32();
1361 _bgOpcodePC = _currentInstruction + offset - 4;
1362 debugInterpreter("O_SETBGCODE next %08x, offset %08x", _bgOpcodePC, offset);
1363 }
1364
O_SETBACKFRAME()1365 void Interpreter::O_SETBACKFRAME() {
1366 int32 anim = readScriptFlagValue();
1367 int32 frame = readScriptFlagValue();
1368 int currAnim = _vm->_backAnimList[anim]._seq._currRelative;
1369 if (_vm->_backAnimList[anim].backAnims[currAnim]._animData != nullptr) {
1370 _vm->_backAnimList[anim].backAnims[currAnim]._frame = frame;
1371 }
1372 debugInterpreter("O_SETBACKFRAME anim %d, frame %d", anim, frame);
1373 }
1374
O_GETRND()1375 void Interpreter::O_GETRND() {
1376 Flags::Id flag = readScriptFlagId();
1377 uint16 rndSeed = readScript16();
1378 int value = _vm->_randomSource.getRandomNumber(rndSeed - 1);
1379 _flags->setFlagValue(flag, value);
1380 debugInterpreter("O_GETRND flag %d, rndSeed %d, value %d", flag, rndSeed, value);
1381 }
1382
O_TALKBACKANIM()1383 void Interpreter::O_TALKBACKANIM() {
1384 int32 animNumber = readScriptFlagValue();
1385 int32 slot = readScriptFlagValue();
1386 _vm->doTalkAnim(animNumber, slot, kBackgroundAnimation);
1387 debugInterpreter("O_TALKBACKANIM animNumber %d, slot %d", animNumber, slot);
1388 }
1389
1390 // Simplifying, because used only once in Location 20
O_LOADPATH()1391 void Interpreter::O_LOADPATH() {
1392 readScript32();
1393 _vm->loadPath("path2");
1394 debugInterpreter("O_LOADPATH - path2");
1395 }
1396
O_GETCHAR()1397 void Interpreter::O_GETCHAR() {
1398 Flags::Id flagId = readScriptFlagId();
1399 _flags->setFlagValue(flagId, *_string);
1400 _string++;
1401 debugInterpreter("O_GETCHAR %04X (%s) %02x", flagId, Flags::getFlagName(flagId), _flags->getFlagValue(flagId));
1402 }
1403
O_SETDFLAG()1404 void Interpreter::O_SETDFLAG() {
1405 Flags::Id flagId = readScriptFlagId();
1406 int32 address = readScript32();
1407 _flags->setFlagValue((Flags::Id)(flagId), _currentInstruction + address - 4);
1408 debugInterpreter("O_SETDFLAG 0x%04X (%s) = 0x%04X", flagId, Flags::getFlagName(flagId), _currentInstruction + address - 4);
1409 }
1410
O_CALLDFLAG()1411 void Interpreter::O_CALLDFLAG() {
1412 Flags::Id flagId = readScriptFlagId();
1413 _stack[_stacktop] = _currentInstruction;
1414 _stacktop++;
1415 _currentInstruction = _flags->getFlagValue(flagId);
1416 debugInterpreter("O_CALLDFLAG 0x%04X (%s) = 0x%04X", flagId, Flags::getFlagName(flagId), _currentInstruction);
1417 }
1418
O_PRINTAT()1419 void Interpreter::O_PRINTAT() {
1420 int32 slot = readScriptFlagValue();
1421 int32 x = readScriptFlagValue();
1422 int32 y = readScriptFlagValue();
1423 int32 color = _flags->getFlagValue(Flags::KOLOR);
1424 _vm->printAt(slot, color, (char *)_string, x, y);
1425 increaseString();
1426 debugInterpreter("O_PRINTAT slot %d, x %d, y %d", slot, x, y);
1427 }
1428
O_ZOOMIN()1429 void Interpreter::O_ZOOMIN() {
1430 int32 slot = readScriptFlagValue();
1431 _vm->initZoomIn(slot);
1432 debugInterpreter("O_ZOOMIN slot %04d", slot);
1433 }
1434
O_ZOOMOUT()1435 void Interpreter::O_ZOOMOUT() {
1436 int32 slot = readScriptFlagValue();
1437 _vm->initZoomOut(slot);
1438 debugInterpreter("O_ZOOMOUT slot %d", slot);
1439 }
1440
1441 // Not used in script
O_SETSTRINGOFFSET()1442 void Interpreter::O_SETSTRINGOFFSET() {
1443 error("O_SETSTRINGOFFSET");
1444 }
1445
O_GETOBJDATA()1446 void Interpreter::O_GETOBJDATA() {
1447 Flags::Id flag = readScriptFlagId();
1448 int32 slot = readScriptFlagValue();
1449 int32 objOffset = readScriptFlagValue();
1450 int nr = _vm->_objSlot[slot];
1451 if (nr != 0xFF) {
1452 int16 value = _vm->_objList[nr]->getData((Object::AttrId)objOffset);
1453 _flags->setFlagValue(flag, value);
1454 }
1455 debugInterpreter("O_GETOBJDATA flag %d, objSlot %d, objOffset %d", flag, slot, objOffset);
1456 }
1457
O_SETOBJDATA()1458 void Interpreter::O_SETOBJDATA() {
1459 int32 slot = readScriptFlagValue();
1460 int32 objOffset = readScriptFlagValue();
1461 int32 value = readScriptFlagValue();
1462 int nr = _vm->_objSlot[slot];
1463 if (nr != 0xFF) {
1464 _vm->_objList[nr]->setData((Object::AttrId)objOffset, value);
1465 }
1466 debugInterpreter("O_SETOBJDATA objSlot %d, objOffset %d, value %d", slot, objOffset, value);
1467 }
1468
1469 // Not used in script
O_SWAPOBJECTS()1470 void Interpreter::O_SWAPOBJECTS() {
1471 error("O_SWAPOBJECTS");
1472 }
1473
O_CHANGEHEROSET()1474 void Interpreter::O_CHANGEHEROSET() {
1475 int32 heroId = readScriptFlagValue();
1476 int32 heroSet = readScriptFlagValue();
1477 if (!heroId) {
1478 _vm->_mainHero->loadAnimSet(heroSet);
1479 } else if (heroId == 1) {
1480 _vm->_secondHero->loadAnimSet(heroSet);
1481 }
1482 debugInterpreter("O_CHANGEHEROSET hero %d, heroSet %d", heroId, heroSet);
1483 }
1484
1485 // Not used in script
O_ADDSTRING()1486 void Interpreter::O_ADDSTRING() {
1487 error("O_ADDSTRING");
1488 }
1489
O_SUBSTRING()1490 void Interpreter::O_SUBSTRING() {
1491 int32 value = readScriptFlagValue();
1492 _string -= value;
1493 debugInterpreter("O_SUBSTRING value %d", value);
1494 }
1495
checkSeq(byte * string)1496 int Interpreter::checkSeq(byte *string) {
1497 int freeHSlotIncrease = 0;
1498 byte c;
1499 while ((c = string[0]) != 0xFF) {
1500 string++;
1501 if (c < 0xF0) {
1502 freeHSlotIncrease++;
1503 while ((c = string[0])) {
1504 string++;
1505 }
1506 string++;
1507 } else if (c != 0xFE) {
1508 string++;
1509 }
1510 }
1511 return freeHSlotIncrease;
1512 }
1513
O_INITDIALOG()1514 void Interpreter::O_INITDIALOG() {
1515 if (_string[0] == 255) {
1516 byte *stringCurrOff = _string;
1517 byte *string = _string;
1518 stringCurrOff++;
1519 int32 adressOfFirstSequence = (int)READ_LE_UINT16(stringCurrOff);
1520 stringCurrOff += 2;
1521 _string = string + adressOfFirstSequence;
1522
1523 for (int i = 0; i < 32; i++) {
1524 _vm->_dialogBoxAddr[i] = 0;
1525 _vm->_dialogOptAddr[i] = 0;
1526 }
1527
1528 for (int i = 0; i < 4 * 32; i++) {
1529 _vm->_dialogOptLines[i] = 0;
1530 }
1531
1532 int16 off;
1533 byte *line = nullptr;
1534
1535 int dialogBox = 0;
1536 while ((off = (int)READ_LE_UINT16(stringCurrOff)) != -1) {
1537 stringCurrOff += 2;
1538 if (off) {
1539 line = string + off;
1540 }
1541 _vm->_dialogBoxAddr[dialogBox] = line;
1542 dialogBox++;
1543 }
1544 stringCurrOff += 2;
1545
1546 int dialogOpt = 0;
1547 while ((off = (int)READ_LE_UINT16(stringCurrOff)) != -1) {
1548 stringCurrOff += 2;
1549 if (off) {
1550 line = string + off;
1551 }
1552 _vm->_dialogOptAddr[dialogOpt] = line;
1553 dialogOpt++;
1554 }
1555
1556 _flags->setFlagValue(Flags::VOICE_A_LINE, 0);
1557 _flags->setFlagValue(Flags::VOICE_B_LINE, 0); // bx in original?
1558
1559 int freeHSlot = 0;
1560 for (int i = 31; i >= 0; i--) {
1561 if (_vm->_dialogOptAddr[i] != 0) {
1562 i++;
1563 freeHSlot = i;
1564 _flags->setFlagValue(Flags::VOICE_H_LINE, i);
1565 break;
1566 }
1567 }
1568
1569 freeHSlot += checkSeq(_string);
1570
1571 for (int i = 0; i < 32; i++) {
1572 _vm->_dialogOptLines[i * 4] = freeHSlot;
1573 _vm->_dialogOptLines[i * 4 + 1] = freeHSlot;
1574 _vm->_dialogOptLines[i * 4 + 2] = freeHSlot;
1575 if (_vm->_dialogOptAddr[i]) {
1576 freeHSlot += checkSeq(_vm->_dialogOptAddr[i]);
1577 }
1578 }
1579 }
1580 debugInterpreter("O_INITDIALOG");
1581 }
1582
O_ENABLEDIALOGOPT()1583 void Interpreter::O_ENABLEDIALOGOPT() {
1584 int32 opt = readScriptFlagValue();
1585 int dialogDataValue = (int)READ_LE_UINT32(_vm->_dialogData);
1586 dialogDataValue &= ~(1u << opt);
1587 WRITE_LE_UINT32(_vm->_dialogData, dialogDataValue);
1588 debugInterpreter("O_ENABLEDIALOGOPT opt %d", opt);
1589 }
1590
O_DISABLEDIALOGOPT()1591 void Interpreter::O_DISABLEDIALOGOPT() {
1592 int32 opt = readScriptFlagValue();
1593 int dialogDataValue = (int)READ_LE_UINT32(_vm->_dialogData);
1594 dialogDataValue |= (1u << opt);
1595 WRITE_LE_UINT32(_vm->_dialogData, dialogDataValue);
1596 debugInterpreter("O_DISABLEDIALOGOPT opt %d", opt);
1597 }
1598
O_SHOWDIALOGBOX()1599 void Interpreter::O_SHOWDIALOGBOX() {
1600 int32 box = readScriptFlagValue();
1601 uint32 currInstr = _currentInstruction;
1602 _vm->createDialogBox(box);
1603 _flags->setFlagValue(Flags::DIALINES, _vm->_dialogLines);
1604 if (_vm->_dialogLines) {
1605 _vm->changeCursor(1);
1606 _vm->dialogRun();
1607 _vm->changeCursor(0);
1608 }
1609 _currentInstruction = currInstr;
1610 debugInterpreter("O_SHOWDIALOGBOX box %d", box);
1611 }
1612
O_STOPSAMPLE()1613 void Interpreter::O_STOPSAMPLE() {
1614 int32 slot = readScriptFlagValue();
1615 _vm->stopSample(slot);
1616 debugInterpreter("O_STOPSAMPLE slot %d", slot);
1617 }
1618
O_BACKANIMRANGE()1619 void Interpreter::O_BACKANIMRANGE() {
1620 int32 slotId = readScriptFlagValue();
1621 uint16 animId = readScript16();
1622 int32 low = readScriptFlagValue();
1623 int32 high = readScriptFlagValue();
1624 if (animId != 0xFFFF) {
1625 if (animId & InterpreterFlags::kFlagMask) {
1626 animId = _flags->getFlagValue((Flags::Id)animId);
1627 }
1628 }
1629 _result = 1;
1630 if (!_vm->_backAnimList[slotId].backAnims.empty()) {
1631 int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
1632 if (_vm->_backAnimList[slotId].backAnims[currAnim]._animData != nullptr) {
1633 if (animId == 0xFFFF || _vm->_backAnimList[slotId]._seq._current == animId) {
1634 Anim &backAnim = _vm->_backAnimList[slotId].backAnims[currAnim];
1635 if (!backAnim._state) {
1636 if (backAnim._frame >= low) {
1637 if (backAnim._frame <= high) {
1638 _result = 0;
1639 }
1640 }
1641 }
1642 }
1643 }
1644 }
1645 debugInterpreter("O_BACKANIMRANGE slotId %d, animId %d, low %d, high %d, _result %d", slotId, animId, low, high, _result);
1646 }
1647
O_CLEARPATH()1648 void Interpreter::O_CLEARPATH() {
1649 for (uint i = 0; i < _vm->kPathBitmapLen; i++) {
1650 _vm->_roomPathBitmap[i] = 255;
1651 }
1652 debugInterpreter("O_CLEARPATH");
1653 }
1654
O_SETPATH()1655 void Interpreter::O_SETPATH() {
1656 _vm->loadPath("path");
1657 debugInterpreter("O_SETPATH");
1658 }
1659
O_GETHEROX()1660 void Interpreter::O_GETHEROX() {
1661 int32 heroId = readScriptFlagValue();
1662 Flags::Id flagId = readScriptFlagId();
1663 if (!heroId) {
1664 _flags->setFlagValue(flagId, _vm->_mainHero->_middleX);
1665 } else if (heroId == 1) {
1666 _flags->setFlagValue(flagId, _vm->_secondHero->_middleX);
1667 }
1668 debugInterpreter("O_GETHEROX heroId %d, flagId %d", heroId, flagId);
1669 }
1670
O_GETHEROY()1671 void Interpreter::O_GETHEROY() {
1672 int32 heroId = readScriptFlagValue();
1673 Flags::Id flagId = readScriptFlagId();
1674 if (!heroId) {
1675 _flags->setFlagValue(flagId, _vm->_mainHero->_middleY);
1676 } else if (heroId == 1) {
1677 _flags->setFlagValue(flagId, _vm->_secondHero->_middleY);
1678 }
1679 debugInterpreter("O_GETHEROY heroId %d, flagId %d", heroId, flagId);
1680 }
1681
O_GETHEROD()1682 void Interpreter::O_GETHEROD() {
1683 int32 heroId = readScriptFlagValue();
1684 Flags::Id flagId = readScriptFlagId();
1685 if (!heroId) {
1686 _flags->setFlagValue(flagId, _vm->_mainHero->_lastDirection);
1687 } else if (heroId == 1) {
1688 _flags->setFlagValue(flagId, _vm->_secondHero->_lastDirection);
1689 }
1690 debugInterpreter("O_GETHEROD heroId %d, flagId %d", heroId, flagId);
1691 }
1692
O_PUSHSTRING()1693 void Interpreter::O_PUSHSTRING() {
1694 _stringStack.string = _string;
1695 _stringStack.dialogData = _vm->_dialogData;
1696 _stringStack.currentString = _currentString;
1697 debugInterpreter("O_PUSHSTRING");
1698 }
1699
O_POPSTRING()1700 void Interpreter::O_POPSTRING() {
1701 _string = _stringStack.string;
1702 _vm->_dialogData = _stringStack.dialogData;
1703 _currentString = _stringStack.currentString;
1704 debugInterpreter("O_POPSTRING");
1705 }
1706
O_SETFGCODE()1707 void Interpreter::O_SETFGCODE() {
1708 int32 offset = readScript32();
1709 _fgOpcodePC = _currentInstruction + offset - 4;
1710 debugInterpreter("O_SETFGCODE next %08x, offset %08x", _fgOpcodePC, offset);
1711 }
1712
O_STOPHERO()1713 void Interpreter::O_STOPHERO() {
1714 int32 heroId = readScriptFlagValue();
1715 if (!heroId) {
1716 _vm->_mainHero->freeOldMove();
1717 } else if (heroId == 1) {
1718 _vm->_secondHero->freeOldMove();
1719 }
1720 debugInterpreter("O_STOPHERO heroId %d", heroId);
1721 }
1722
O_ANIMUPDATEOFF()1723 void Interpreter::O_ANIMUPDATEOFF() {
1724 int32 slotId = readScriptFlagValue();
1725 _vm->_normAnimList[slotId]._state = 1;
1726 debugInterpreter("O_ANIMUPDATEOFF slotId %d", slotId);
1727 }
1728
O_ANIMUPDATEON()1729 void Interpreter::O_ANIMUPDATEON() {
1730 int32 slotId = readScriptFlagValue();
1731 _vm->_normAnimList[slotId]._state = 0;
1732 debugInterpreter("O_ANIMUPDATEON slotId %d", slotId);
1733 }
1734
O_FREECURSOR()1735 void Interpreter::O_FREECURSOR() {
1736 _vm->changeCursor(0);
1737 _vm->_currentPointerNumber = 1;
1738 // free memory here?
1739 debugInterpreter("O_FREECURSOR");
1740 }
1741
O_ADDINVQUIET()1742 void Interpreter::O_ADDINVQUIET() {
1743 int32 hero = readScriptFlagValue();
1744 int32 item = readScriptFlagValue();
1745 _vm->addInv(hero, item, true);
1746 debugInterpreter("O_ADDINVQUIET hero %d, item %d", hero, item);
1747 }
1748
O_RUNHERO()1749 void Interpreter::O_RUNHERO() {
1750 int32 heroId = readScriptFlagValue();
1751 int32 x = readScriptFlagValue();
1752 int32 y = readScriptFlagValue();
1753 int32 dir = readScriptFlagValue();
1754 _vm->moveRunHero(heroId, x, y, dir, true);
1755 debugInterpreter("O_RUNHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir);
1756 }
1757
O_SETBACKANIMDATA()1758 void Interpreter::O_SETBACKANIMDATA() {
1759 uint16 animNumber = readScript16();
1760 uint16 animDataOffset = readScript16();
1761 Flags::Id flagId = readScriptFlagId();
1762 uint16 value = _flags->getFlagValue((Flags::Id)(flagId));
1763 int currAnim = _vm->_backAnimList[animNumber]._seq._currRelative;
1764 _vm->_backAnimList[animNumber].backAnims[currAnim].setAnimData((Anim::AnimOffsets)(animDataOffset), value);
1765 debugInterpreter("O_SETBACKANIMDATA flag %04X (%s), animNumber %d, animDataOffset %d, value %d", flagId, Flags::getFlagName(flagId), animNumber, animDataOffset, value);
1766 }
1767
O_VIEWFLC()1768 void Interpreter::O_VIEWFLC() {
1769 int32 animNr = readScriptFlagValue();
1770 _vm->_flcFrameSurface = nullptr;
1771 _vm->loadAnim(animNr, false);
1772 debugInterpreter("O_VIEWFLC animNr %d", animNr);
1773 }
1774
O_CHECKFLCFRAME()1775 void Interpreter::O_CHECKFLCFRAME() {
1776 int32 frameNr = readScriptFlagValue();
1777 debugInterpreter("O_CHECKFLCFRAME frame number %d", frameNr);
1778 if (_vm->_flicPlayer.getCurFrame() != frameNr) {
1779 _currentInstruction -= 4;
1780 _opcodeNF = 1;
1781 }
1782 }
1783
O_CHECKFLCEND()1784 void Interpreter::O_CHECKFLCEND() {
1785 const Video::FlicDecoder &flicPlayer = _vm->_flicPlayer;
1786 debugInterpreter("O_CHECKFLCEND frameCount %d, currentFrame %d", flicPlayer.getFrameCount(), flicPlayer.getCurFrame());
1787 if (flicPlayer.getFrameCount() - flicPlayer.getCurFrame() > 1) {
1788 _currentInstruction -= 2;
1789 _opcodeNF = 1;
1790 }
1791 }
1792
O_FREEFLC()1793 void Interpreter::O_FREEFLC() {
1794 _vm->_flcFrameSurface = nullptr;
1795 debugInterpreter("O_FREEFLC");
1796 }
1797
O_TALKHEROSTOP()1798 void Interpreter::O_TALKHEROSTOP() {
1799 int32 heroId = readScriptFlagValue();
1800 if (!heroId) {
1801 _vm->_mainHero->_state = Hero::kHeroStateStay;
1802 } else if (heroId == 1) {
1803 _vm->_secondHero->_state = Hero::kHeroStateStay;
1804 }
1805 debugInterpreter("O_TALKHEROSTOP %d", heroId);
1806 }
1807
O_HEROCOLOR()1808 void Interpreter::O_HEROCOLOR() {
1809 int32 heroId = readScriptFlagValue();
1810 int32 color = readScriptFlagValue();
1811 if (!heroId) {
1812 _vm->_mainHero->_color = color;
1813 } else if (heroId == 1) {
1814 _vm->_secondHero->_color = color;
1815 }
1816 debugInterpreter("O_HEROCOLOR heroId %d, color %d", heroId, color);
1817 }
1818
O_GRABMAPA()1819 void Interpreter::O_GRABMAPA() {
1820 _vm->grabMap();
1821 debugInterpreter("O_GRABMAPA");
1822 }
1823
O_ENABLENAK()1824 void Interpreter::O_ENABLENAK() {
1825 int32 nakId = readScriptFlagValue();
1826 _vm->_maskList[nakId]._flags = 0;
1827 debugInterpreter("O_ENABLENAK nakId %d", nakId);
1828 }
1829
O_DISABLENAK()1830 void Interpreter::O_DISABLENAK() {
1831 int32 nakId = readScriptFlagValue();
1832 _vm->_maskList[nakId]._flags = 1;
1833 debugInterpreter("O_DISABLENAK nakId %d", nakId);
1834 }
1835
O_GETMOBNAME()1836 void Interpreter::O_GETMOBNAME() {
1837 int32 modId = readScriptFlagValue();
1838 // FIXME: UB?
1839 // This casts away the constness of the pointer returned by c_str() which is
1840 // stored and potentially modified later (for example in printAt()).
1841 // Also, the pointer is only valid as long as _vm->_mobList[mobId]
1842 // is around and _vm->_mobList[mobId]._name hasn't been modified by any of its
1843 // non-const member functions which also might or might not be a problem.
1844 _string = (byte *)_vm->_mobList[modId]._name.c_str();
1845 debugInterpreter("O_GETMOBNAME modId %d", modId);
1846 }
1847
O_SWAPINVENTORY()1848 void Interpreter::O_SWAPINVENTORY() {
1849 int32 hero = readScriptFlagValue();
1850 _vm->swapInv(hero);
1851 debugInterpreter("O_SWAPINVENTORY hero %d", hero);
1852 }
1853
O_CLEARINVENTORY()1854 void Interpreter::O_CLEARINVENTORY() {
1855 int32 hero = readScriptFlagValue();
1856 _vm->clearInv(hero);
1857 debugInterpreter("O_CLEARINVENTORY hero %d", hero);
1858 }
1859
O_SKIPTEXT()1860 void Interpreter::O_SKIPTEXT() {
1861 increaseString();
1862 debugInterpreter("O_SKIPTEXT");
1863 }
1864
O_SETVOICEH()1865 void Interpreter::O_SETVOICEH() {
1866 int32 slot = readScriptFlagValue();
1867 static const uint32 VOICE_H_SLOT = 28;
1868 uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
1869 _vm->setVoice(slot, VOICE_H_SLOT, voiceLineH);
1870 }
1871
O_SETVOICEA()1872 void Interpreter::O_SETVOICEA() {
1873 int32 slot = readScriptFlagValue();
1874 static const uint32 VOICE_A_SLOT = 29;
1875 uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
1876 _vm->setVoice(slot, VOICE_A_SLOT, voiceLineH);
1877 }
1878
O_SETVOICEB()1879 void Interpreter::O_SETVOICEB() {
1880 int32 slot = readScriptFlagValue();
1881 static const uint32 VOICE_B_SLOT = 30;
1882 uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
1883 _vm->setVoice(slot, VOICE_B_SLOT, voiceLineH);
1884 }
1885
O_SETVOICEC()1886 void Interpreter::O_SETVOICEC() {
1887 int32 slot = readScriptFlagValue();
1888 static const uint32 VOICE_C_SLOT = 31;
1889 uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
1890 _vm->setVoice(slot, VOICE_C_SLOT, voiceLineH);
1891 }
1892
O_SETVOICED()1893 void Interpreter::O_SETVOICED() {
1894 int32 slot = readScriptFlagValue();
1895 static const uint32 VOICE_D_SLOT = 32;
1896 uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
1897 _vm->setVoice(slot, VOICE_D_SLOT, voiceLineH);
1898 }
1899
O_VIEWFLCLOOP()1900 void Interpreter::O_VIEWFLCLOOP() {
1901 int32 animId = readScriptFlagValue();
1902 _vm->loadAnim(animId, true);
1903 debugInterpreter("O_VIEWFLCLOOP animId %d", animId);
1904 }
1905
1906 // Not used in script
O_FLCSPEED()1907 void Interpreter::O_FLCSPEED() {
1908 int32 speed = readScriptFlagValue();
1909 error("O_FLCSPEED speed %d", speed);
1910 }
1911
O_OPENINVENTORY()1912 void Interpreter::O_OPENINVENTORY() {
1913 _vm->_showInventoryFlag = true;
1914 _opcodeNF = 1;
1915 debugInterpreter("O_OPENINVENTORY");
1916 }
1917
O_KRZYWA()1918 void Interpreter::O_KRZYWA() {
1919 _vm->makeCurve();
1920 debugInterpreter("O_KRZYWA");
1921 }
1922
O_GETKRZYWA()1923 void Interpreter::O_GETKRZYWA() {
1924 _vm->getCurve();
1925 debugInterpreter("O_GETKRZYWA");
1926 }
1927
O_GETMOB()1928 void Interpreter::O_GETMOB() {
1929 Flags::Id flagId = readScriptFlagId();
1930 int32 posX = readScriptFlagValue();
1931 int32 posY = readScriptFlagValue();
1932 int mobNumber = _vm->getMob(_vm->_mobList, true, posX, posY);
1933 _flags->setFlagValue(flagId, mobNumber + 1);
1934 debugInterpreter("O_GETMOB flagId %d, posX %d, posY %d", flagId, posX, posY);
1935 }
1936
1937 // Not used in game
O_INPUTLINE()1938 void Interpreter::O_INPUTLINE() {
1939 error("O_INPUTLINE");
1940 }
1941
1942 // Not used in script
O_BREAK_POINT()1943 void Interpreter::O_BREAK_POINT() {
1944 error("O_BREAK_POINT");
1945 }
1946
1947 Interpreter::OpcodeFunc Interpreter::_opcodes[kNumOpcodes] = {
1948 &Interpreter::O_WAITFOREVER,
1949 &Interpreter::O_BLACKPALETTE,
1950 &Interpreter::O_SETUPPALETTE,
1951 &Interpreter::O_INITROOM,
1952 &Interpreter::O_SETSAMPLE,
1953 &Interpreter::O_FREESAMPLE,
1954 &Interpreter::O_PLAYSAMPLE,
1955 &Interpreter::O_PUTOBJECT,
1956 &Interpreter::O_REMOBJECT,
1957 &Interpreter::O_SHOWANIM,
1958 &Interpreter::O_CHECKANIMEND,
1959 &Interpreter::O_FREEANIM,
1960 &Interpreter::O_CHECKANIMFRAME,
1961 &Interpreter::O_PUTBACKANIM,
1962 &Interpreter::O_REMBACKANIM,
1963 &Interpreter::O_CHECKBACKANIMFRAME,
1964 &Interpreter::O_FREEALLSAMPLES,
1965 &Interpreter::O_SETMUSIC,
1966 &Interpreter::O_STOPMUSIC,
1967 &Interpreter::O__WAIT,
1968 &Interpreter::O_UPDATEOFF,
1969 &Interpreter::O_UPDATEON,
1970 &Interpreter::O_UPDATE ,
1971 &Interpreter::O_CLS,
1972 &Interpreter::O__CALL,
1973 &Interpreter::O_RETURN,
1974 &Interpreter::O_GO,
1975 &Interpreter::O_BACKANIMUPDATEOFF,
1976 &Interpreter::O_BACKANIMUPDATEON,
1977 &Interpreter::O_CHANGECURSOR,
1978 &Interpreter::O_CHANGEANIMTYPE,
1979 &Interpreter::O__SETFLAG,
1980 &Interpreter::O_COMPARE,
1981 &Interpreter::O_JUMPZ,
1982 &Interpreter::O_JUMPNZ,
1983 &Interpreter::O_EXIT,
1984 &Interpreter::O_ADDFLAG,
1985 &Interpreter::O_TALKANIM,
1986 &Interpreter::O_SUBFLAG,
1987 &Interpreter::O_SETSTRING,
1988 &Interpreter::O_ANDFLAG,
1989 &Interpreter::O_GETMOBDATA,
1990 &Interpreter::O_ORFLAG,
1991 &Interpreter::O_SETMOBDATA,
1992 &Interpreter::O_XORFLAG,
1993 &Interpreter::O_GETMOBTEXT,
1994 &Interpreter::O_MOVEHERO,
1995 &Interpreter::O_WALKHERO,
1996 &Interpreter::O_SETHERO,
1997 &Interpreter::O_HEROOFF,
1998 &Interpreter::O_HEROON,
1999 &Interpreter::O_CLSTEXT,
2000 &Interpreter::O_CALLTABLE,
2001 &Interpreter::O_CHANGEMOB,
2002 &Interpreter::O_ADDINV,
2003 &Interpreter::O_REMINV,
2004 &Interpreter::O_REPINV,
2005 &Interpreter::O_OBSOLETE_GETACTION,
2006 &Interpreter::O_ADDWALKAREA,
2007 &Interpreter::O_REMWALKAREA,
2008 &Interpreter::O_RESTOREWALKAREA,
2009 &Interpreter::O_WAITFRAME,
2010 &Interpreter::O_SETFRAME,
2011 &Interpreter::O_RUNACTION,
2012 &Interpreter::O_COMPAREHI,
2013 &Interpreter::O_COMPARELO,
2014 &Interpreter::O_PRELOADSET,
2015 &Interpreter::O_FREEPRELOAD,
2016 &Interpreter::O_CHECKINV,
2017 &Interpreter::O_TALKHERO,
2018 &Interpreter::O_WAITTEXT,
2019 &Interpreter::O_SETHEROANIM,
2020 &Interpreter::O_WAITHEROANIM,
2021 &Interpreter::O_GETHERODATA,
2022 &Interpreter::O_GETMOUSEBUTTON,
2023 &Interpreter::O_CHANGEFRAMES,
2024 &Interpreter::O_CHANGEBACKFRAMES,
2025 &Interpreter::O_GETBACKANIMDATA,
2026 &Interpreter::O_GETANIMDATA,
2027 &Interpreter::O_SETBGCODE,
2028 &Interpreter::O_SETBACKFRAME,
2029 &Interpreter::O_GETRND,
2030 &Interpreter::O_TALKBACKANIM,
2031 &Interpreter::O_LOADPATH,
2032 &Interpreter::O_GETCHAR,
2033 &Interpreter::O_SETDFLAG,
2034 &Interpreter::O_CALLDFLAG,
2035 &Interpreter::O_PRINTAT,
2036 &Interpreter::O_ZOOMIN,
2037 &Interpreter::O_ZOOMOUT,
2038 &Interpreter::O_SETSTRINGOFFSET,
2039 &Interpreter::O_GETOBJDATA,
2040 &Interpreter::O_SETOBJDATA,
2041 &Interpreter::O_SWAPOBJECTS,
2042 &Interpreter::O_CHANGEHEROSET,
2043 &Interpreter::O_ADDSTRING,
2044 &Interpreter::O_SUBSTRING,
2045 &Interpreter::O_INITDIALOG,
2046 &Interpreter::O_ENABLEDIALOGOPT,
2047 &Interpreter::O_DISABLEDIALOGOPT,
2048 &Interpreter::O_SHOWDIALOGBOX,
2049 &Interpreter::O_STOPSAMPLE,
2050 &Interpreter::O_BACKANIMRANGE,
2051 &Interpreter::O_CLEARPATH,
2052 &Interpreter::O_SETPATH,
2053 &Interpreter::O_GETHEROX,
2054 &Interpreter::O_GETHEROY,
2055 &Interpreter::O_GETHEROD,
2056 &Interpreter::O_PUSHSTRING,
2057 &Interpreter::O_POPSTRING,
2058 &Interpreter::O_SETFGCODE,
2059 &Interpreter::O_STOPHERO,
2060 &Interpreter::O_ANIMUPDATEOFF,
2061 &Interpreter::O_ANIMUPDATEON,
2062 &Interpreter::O_FREECURSOR,
2063 &Interpreter::O_ADDINVQUIET,
2064 &Interpreter::O_RUNHERO,
2065 &Interpreter::O_SETBACKANIMDATA,
2066 &Interpreter::O_VIEWFLC,
2067 &Interpreter::O_CHECKFLCFRAME,
2068 &Interpreter::O_CHECKFLCEND,
2069 &Interpreter::O_FREEFLC,
2070 &Interpreter::O_TALKHEROSTOP,
2071 &Interpreter::O_HEROCOLOR,
2072 &Interpreter::O_GRABMAPA,
2073 &Interpreter::O_ENABLENAK,
2074 &Interpreter::O_DISABLENAK,
2075 &Interpreter::O_GETMOBNAME,
2076 &Interpreter::O_SWAPINVENTORY,
2077 &Interpreter::O_CLEARINVENTORY,
2078 &Interpreter::O_SKIPTEXT,
2079 &Interpreter::O_SETVOICEH,
2080 &Interpreter::O_SETVOICEA,
2081 &Interpreter::O_SETVOICEB,
2082 &Interpreter::O_SETVOICEC,
2083 &Interpreter::O_VIEWFLCLOOP,
2084 &Interpreter::O_FLCSPEED,
2085 &Interpreter::O_OPENINVENTORY,
2086 &Interpreter::O_KRZYWA,
2087 &Interpreter::O_GETKRZYWA,
2088 &Interpreter::O_GETMOB,
2089 &Interpreter::O_INPUTLINE,
2090 &Interpreter::O_SETVOICED,
2091 &Interpreter::O_BREAK_POINT,
2092 };
2093
2094 } // End of namespace Prince
2095