/* ScummVM - Scumm Interpreter * Copyright (C) 2002 The ScummVM project * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ #include "stdafx.h" #include "scumm.h" #include "actor.h" #include "charset.h" #include "intern.h" #include "sound.h" #include "verbs.h" #include "smush/player.h" #include "smush/scumm_renderer.h" /* * NO, we do NOT support CMI yet :-) This file is mostly a placeholder and a place * to grow real support in. For now, only a few opcodes are implemented, and they * might even be wrong... so don't hold your breath. */ #define OPCODE(x) { &Scumm_v8::x, #x } void Scumm_v8::setupOpcodes() { // TODO: any of the o6_ entries are potentially wrong and pure guesses :-) static const OpcodeEntryV8 opcodes[256] = { /* 00 */ OPCODE(o6_invalid), OPCODE(o6_pushWord), OPCODE(o6_pushWordVar), OPCODE(o6_wordArrayRead), /* 04 */ OPCODE(o6_wordArrayIndexedRead), OPCODE(o6_dup), OPCODE(o6_pop), OPCODE(o6_not), /* 08 */ OPCODE(o6_eq), OPCODE(o6_neq), OPCODE(o6_gt), OPCODE(o6_lt), /* 0C */ OPCODE(o6_le), OPCODE(o6_ge), OPCODE(o6_add), OPCODE(o6_sub), /* 10 */ OPCODE(o6_mul), OPCODE(o6_div), OPCODE(o6_land), OPCODE(o6_lor), /* 14 */ OPCODE(o6_band), OPCODE(o6_bor), OPCODE(o8_mod), OPCODE(o6_invalid), /* 18 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 1C */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 20 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 24 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 28 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 2C */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 30 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 34 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 38 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 3C */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 40 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 44 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 48 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 4C */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 50 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 54 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 58 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 5C */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 60 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 64 */ OPCODE(o6_jumpTrue), OPCODE(o6_jumpFalse), OPCODE(o6_jump), OPCODE(o6_breakHere), /* 68 */ OPCODE(o6_delayFrames), OPCODE(o8_wait), OPCODE(o6_delay), // FIXME - is the delay period right? OPCODE(o6_delayLonger), // FIXME - is the delay period right? /* 6C */ OPCODE(o6_delayVeryLong), // FIXME - is the delay period right? OPCODE(o6_writeWordVar), OPCODE(o6_wordVarInc), OPCODE(o6_wordVarDec), /* 70 */ OPCODE(o8_dim), OPCODE(o6_wordArrayWrite), OPCODE(o6_wordArrayInc), OPCODE(o6_wordArrayDec), /* 74 */ OPCODE(o8_dim2), OPCODE(o6_wordArrayIndexedWrite), OPCODE(o8_arrayOps), OPCODE(o6_invalid), /* 78 */ OPCODE(o6_invalid), OPCODE(o6_startScript), OPCODE(o6_startScriptQuick), OPCODE(o6_stopObjectCode), /* 7C */ OPCODE(o6_stopScript), OPCODE(o6_jumpToScript), // FIXME - is this right? "O_CHAIN_SCRIPT" OPCODE(o6_dummy), OPCODE(o6_startObject), /* 80 */ OPCODE(o6_stopObjectScript), // FIXME - is this right? OPCODE(o6_cutscene), OPCODE(o6_endCutscene), OPCODE(o6_freezeUnfreeze), /* 84 */ OPCODE(o6_beginOverride), OPCODE(o6_endOverride), OPCODE(o6_stopSentence), OPCODE(o6_invalid), /* 88 */ OPCODE(o6_invalid), OPCODE(o6_setClass), OPCODE(o6_setState), OPCODE(o6_setOwner), /* 8C */ OPCODE(o6_panCameraTo), OPCODE(o6_actorFollowCamera), OPCODE(o6_setCameraAt), OPCODE(o8_talkActor), /* 90 */ OPCODE(o6_invalid), OPCODE(o8_talkActorSimple), OPCODE(o6_invalid), OPCODE(o8_printLine), /* 94 */ OPCODE(o8_printCursor), OPCODE(o8_printDebug), OPCODE(o8_printSystem), OPCODE(o8_blastText), /* 98 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* 9C */ OPCODE(o8_cursorCommand), OPCODE(o6_loadRoom), OPCODE(o6_loadRoomWithEgo), // FIXME - this is a pure guess OPCODE(o6_walkActorToObj), /* A0 */ OPCODE(o6_walkActorTo), OPCODE(o6_putActorInRoom), // FIXME - this seems to be wrong? at least we get actor indices from 31 to 79 here... OPCODE(o6_putActorAtObject), OPCODE(o6_faceActor), /* A4 */ OPCODE(o6_animateActor), OPCODE(o6_doSentence), OPCODE(o6_pickupObject), OPCODE(o6_setBoxFlags), /* A8 */ OPCODE(o6_createBoxMatrix), // fixme? OPCODE(o6_invalid), OPCODE(o8_resourceRoutines), OPCODE(o8_roomOps), /* AC */ OPCODE(o8_actorOps), OPCODE(o8_cameraOps), OPCODE(o8_verbOps), OPCODE(o6_startSound), /* B0 */ OPCODE(o6_startMusic), OPCODE(o6_stopSound), OPCODE(o8_soundKludge), OPCODE(o8_system), /* B4 */ OPCODE(o6_saveRestoreVerbs), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_drawBox), /* B8 */ OPCODE(o6_invalid), OPCODE(o8_startVideo), OPCODE(o6_kernelSetFunctions), OPCODE(o6_invalid), /* BC */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* C0 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* C4 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* C8 */ OPCODE(o6_startScript), OPCODE(o6_startObject), OPCODE(o6_pickOneOf), OPCODE(o6_pickOneOfDefault), /* CC */ OPCODE(o6_invalid), OPCODE(o6_isAnyOf), OPCODE(o6_getRandomNumber), OPCODE(o6_getRandomNumberRange), /* D0 */ OPCODE(o6_ifClassOfIs), // FIXME - this is a guess OPCODE(o6_getState), OPCODE(o6_getOwner), OPCODE(o6_isScriptRunning), /* D4 */ OPCODE(o6_invalid), OPCODE(o6_isSoundRunning), OPCODE(o6_abs), OPCODE(o6_invalid), /* D8 */ OPCODE(o6_kernelGetFunctions), OPCODE(o6_isActorInBox), OPCODE(o6_getVerbEntrypoint), OPCODE(o6_getActorFromXY), /* DC */ OPCODE(o6_findObject), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_findInventory), /* E0 */ OPCODE(o6_getInventoryCount), OPCODE(o6_getAnimateVariable), OPCODE(o6_getActorRoom), OPCODE(o6_getActorWalkBox), /* E4 */ OPCODE(o6_getActorMoving), OPCODE(o6_getActorCostume), OPCODE(o6_getActorScaleX), OPCODE(o6_getActorLayer), /* E8 */ OPCODE(o6_getActorElevation), OPCODE(o6_getActorWidth), OPCODE(o6_getObjectNewDir), // FIXME: is this right? OPCODE(o6_getObjectX), /* EC */ OPCODE(o6_getObjectY), OPCODE(o6_getActorAnimCounter1), OPCODE(o6_distObjectObject), OPCODE(o6_distPtPt), /* F0 */ OPCODE(o8_getObjectImageX), OPCODE(o8_getObjectImageY), OPCODE(o8_getObjectImageWidth), OPCODE(o8_getObjectImageHeight), /* F4 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* F8 */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), /* FC */ OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), OPCODE(o6_invalid), }; _opcodesV8 = opcodes; } void Scumm_v8::executeOpcode(int i) { OpcodeProcV8 op = _opcodesV8[i].proc; (this->*op) (); } const char *Scumm_v8::getOpcodeDesc(int i) { return _opcodesV8[i].desc; } // In V8, the word size is 4 byte, not 2 bytes as in V6/V7 games uint Scumm_v8::fetchScriptWord() { int a; if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) { uint32 oldoffs = _scriptPointer - _scriptOrgPointer; getScriptBaseAddress(); _scriptPointer = _scriptOrgPointer + oldoffs; } a = READ_LE_UINT32(_scriptPointer); _scriptPointer += 4; return a; } int Scumm_v8::fetchScriptWordSigned() { return (int32)fetchScriptWord(); } int Scumm_v8::readVar(uint var) { debug(9, "readvar(%d)", var); if (!(var & 0xF0000000)) { checkRange(_numVariables - 1, 0, var, "Variable %d out of range(r)"); return _vars[var]; } if (var & 0x80000000) { var &= 0x7FFFFFFF; checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(r)"); return (_bitVars[var >> 3] & (1 << (var & 7))) ? 1 : 0; } if (var & 0x40000000) { var &= 0xFFFFFFF; checkRange(0x10, 0, var, "Local variable %d out of range(r)"); return vm.localvar[_currentScript][var]; } error("Illegal varbits (r)"); return -1; } void Scumm_v8::writeVar(uint var, int value) { debug(9, "writeVar(%d, %d)", var, value); if (!(var & 0xF0000000)) { checkRange(_numVariables - 1, 0, var, "Variable %d out of range(w)"); _vars[var] = value; if ((_varwatch == (int)var) || (_varwatch == 0)) { if (vm.slot[_currentScript].number < 100) debug(1, "vars[%d] = %d (via script-%d)", var, value, vm.slot[_currentScript].number); else debug(1, "vars[%d] = %d (via room-%d-%d)", var, value, _currentRoom, vm.slot[_currentScript].number); } return; } if (var & 0x80000000) { var &= 0x7FFFFFFF; checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(w)"); if (value) _bitVars[var >> 3] |= (1 << (var & 7)); else _bitVars[var >> 3] &= ~(1 << (var & 7)); return; } if (var & 0x40000000) { var &= 0xFFFFFFF; checkRange(0x10, 0, var, "Local variable %d out of range(w)"); vm.localvar[_currentScript][var] = value; return; } error("Illegal varbits (w)"); } void Scumm_v8::decodeParseString(int m, int n) { byte b; b = fetchScriptByte(); switch (b) { case 0xC8: setStringVars(m); if (n) _actorToPrintStrFor = pop(); return; case 0xC9: _string[m].t_xpos = _string[m].xpos; _string[m].t_ypos = _string[m].ypos; _string[m].t_center = _string[m].center; _string[m].t_overhead = _string[m].overhead; _string[m].t_no_talk_anim = _string[m].no_talk_anim; _string[m].t_right = _string[m].right; _string[m].t_color = _string[m].color; _string[m].t_charset = _string[m].charset; return; case 0xCA: _string[m].ypos = pop(); _string[m].xpos = pop(); _string[m].overhead = false; break; case 0xCB: _string[m].color = pop(); break; case 0xCC: _string[m].center = true; _string[m].overhead = false; break; case 0xCD: { // SO_PRINT_CHARSET Set print character set // FIXME - TODO int charset = pop(); _string[m].charset = charset; } break; case 0xCE: _string[m].center = false; _string[m].overhead = false; break; case 0xCF: _string[m].overhead = true; _string[m].no_talk_anim = false; break; case 0xD0: // SO_PRINT_MUMBLE error("decodeParseString: SO_PRINT_MUMBLE"); break; case 0xD1: { #if 1 _messagePtr = _scriptPointer; if (_messagePtr[0] == '/') { char pointer[20]; int i, j; _scriptPointer += resStrLen((char*)_scriptPointer)+ 1; translateText(_messagePtr, _transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; // Stop any talking that's still going on if (_sound->_talkChannel > -1) _mixer->stop(_sound->_talkChannel); // _sound->_talkChannel = _sound->playBundleSound(pointer); _messagePtr = _transText; switch (m) { case 0: actorTalk(); break; case 1: drawString(1); break; case 2: unkMessage1(); break; case 3: unkMessage2(); break; } return; } else { switch (m) { case 0: actorTalk(); break; case 1: drawString(1); break; case 2: unkMessage1(); break; case 3: unkMessage2(); break; } _scriptPointer = _messagePtr; return; } #else char buffer[1024]; _messagePtr = _scriptPointer; if (_messagePtr[0] == '/') { char pointer[20]; int i, j; // Skip over the string _scriptPointer += resStrLen((char*)_scriptPointer) + 1; translateText(_messagePtr, _transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; // Stop any talking that's still going on if (_sound->_talkChannel > -1) _mixer->stop(_sound->_talkChannel); // FIXME: no 'digvoice.bun' in COMI // _sound->_talkChannel = _sound->playBundleSound(pointer); _messagePtr = _transText; _msgPtrToAdd = (byte *)buffer; _messagePtr = addMessageToStack(_messagePtr); } else { _msgPtrToAdd = (byte *)buffer; _scriptPointer = _messagePtr = addMessageToStack(_messagePtr); } if (_fr[_string[m].charset] == NULL) // FIXME: Put this elsewhere? loadCharset(_string[m].charset); if (_fr[_string[m].charset] != NULL) { int x = _string[m].xpos; // HACK - center mode. I call this a hack because we really should // not duplicate all the string code from string.cpp. Rather, maybe // abstract away the code in string.cpp from using the 'charset' // fonts, and allow it to work both with old and new fonts. To this // end, we should seperate the parts of Charset/_charset which are // for bookkeeping (like x_pos, center mode etc.) and those that are // for rendering. Then let the old (think printCharOld), medium (printChar), // new (NUT) style renderers be subclasses of a common base class (which // defines the API). This will clean up the code, and allow us to reuse // the string logic in string.cpp. if (_string[m].center) x -= _fr[_string[m].charset]->getStringWidth(buffer) / 2; _fr[_string[m].charset]->drawString(buffer, x, _string[m].ypos, _string[m].color, 0); } #endif break; } case 0xD2: // SO_PRINT_WRAP Set print wordwrap error("decodeParseString: SO_PRINT_MUMBLE"); break; default: error("decodeParseString: default case"); } } void Scumm_v8::o8_mod() { int a = pop(); push(pop() % a); } void Scumm_v8::o8_wait() { // TODO byte subOp = fetchScriptByte(); switch (subOp) { case 0x1E: { // SO_WAIT_FOR_ACTOR Wait for actor (to finish current action?) int offs = fetchScriptWordSigned(); if (derefActorSafe(pop(), "o8_wait:SO_WAIT_FOR_ACTOR")->moving) { _scriptPointer += offs; o6_breakHere(); } return; } case 0x1F: // SO_WAIT_FOR_MESSAGE Wait for message if (_vars[VAR_HAVE_MSG]) break; return; case 0x20: // SO_WAIT_FOR_CAMERA Wait for camera (to finish current action?) if (camera._dest != camera._cur) break; return; case 0x21: // SO_WAIT_FOR_SENTENCE if (_sentenceNum) { if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(_vars[VAR_SENTENCE_SCRIPT])) return; break; } if (!isScriptInUse(_vars[VAR_SENTENCE_SCRIPT])) return; break; case 0x22: { // SO_WAIT_FOR_ANIMATION int actnum = pop(); Actor *a = derefActorSafe(actnum, "o8_wait:SO_WAIT_FOR_ANIMATION"); int offs = fetchScriptWordSigned(); if (a && a->isInCurrentRoom() && a->needRedraw) { _scriptPointer += offs; o6_breakHere(); } return; } case 0x23: { // SO_WAIT_FOR_TURN int actnum = pop(); Actor *a = derefActorSafe(actnum, "o8_wait:SO_WAIT_FOR_TURN"); int offs = fetchScriptWordSigned(); if (a && a->isInCurrentRoom() && a->moving & MF_TURN) { _scriptPointer += offs; o6_breakHere(); } return; } default: error("o8_wait: default case %d", subOp); } _scriptPointer -= 2; o6_breakHere(); } void Scumm_v8::o8_dim() { byte subOp = fetchScriptByte(); int array = fetchScriptWord(); switch (subOp) { case 0x0A: // SO_ARRAY_SCUMMVAR defineArray(array, 5, 0, pop()); break; case 0x0B: // SO_ARRAY_STRING defineArray(array, 4, 0, pop()); break; case 0x0C: // SO_ARRAY_UNDIM nukeArray(array); break; default: error("o8_dim: default case %d", subOp); } } void Scumm_v8::o8_dim2() { byte subOp = fetchScriptByte(); int array = fetchScriptWord(), a, b; switch (subOp) { case 0x0A: // SO_ARRAY_SCUMMVAR b = pop(); a = pop(); defineArray(array, 5, a, b); break; case 0x0B: // SO_ARRAY_STRING b = pop(); a = pop(); defineArray(array, 4, a, b); break; case 0x0C: // SO_ARRAY_UNDIM nukeArray(array); break; default: error("o8_dim2: default case %d", subOp); } } void Scumm_v8::o8_arrayOps() { byte subOp = fetchScriptByte(); int array = fetchScriptWord(); int b, c, d, len; int list[128]; switch (subOp) { case 0x14: // SO_ASSIGN_STRING { int idx = pop(); ArrayHeader *ah; int r; len = getStringLen(NULL); r = defineArray(array, 4, 0, len); ah = (ArrayHeader *)getResourceAddress(rtString, r); copyString(ah->data + idx, NULL, len); } break; case 0x15: // SO_ASSIGN_SCUMMVAR_LIST // TODO / FIXME: is this right? b = pop(); c = pop(); d = readVar(array); if (d == 0) { defineArray(array, 5, 0, b + c); } while (c--) { writeArray(array, 0, b + c, pop()); } break; case 0x16: // SO_ASSIGN_2DIM_LIST // TODO / FIXME: is this right? b = pop(); len = getStackList(list, sizeof(list) / sizeof(list[0])); d = readVar(array); if (d == 0) error("Must DIM a two dimensional array before assigning"); c = pop(); while (--len >= 0) { writeArray(array, c, b + len, list[len]); } break; default: error("o8_arrayOps: default case %d (array %d)", subOp, array); } } void Scumm_v8::o8_talkActor() { int _actorToPrintStrFor = pop(); _messagePtr = _scriptPointer; if (_messagePtr[0] == '/') { char pointer[20]; int i, j; _scriptPointer += resStrLen((char*)_scriptPointer)+ 1; translateText(_messagePtr, _transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; _messagePtr = _transText; //setStringVars(0); //actorTalk(); // FIXME - This crashes } else { //setStringVars(0); //actorTalk(); // FIXME - This crashes _scriptPointer = _messagePtr; } printf("o8_talkActor(%d, %s)\n", _actorToPrintStrFor, _messagePtr); } void Scumm_v8::o8_talkActorSimple() { int _actorToPrintStrFor = pop(); _messagePtr = _scriptPointer; if (_messagePtr[0] == '/') { char pointer[20]; int i, j; _scriptPointer += resStrLen((char*)_scriptPointer)+ 1; translateText(_messagePtr, _transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; _messagePtr = _transText; //setStringVars(0); //actorTalk(); // FIXME - This crashes } else { //setStringVars(0); //actorTalk(); // FIXME - This crashes _scriptPointer = _messagePtr; } printf("o8_talkActorSimple(%d, %s)\n", _actorToPrintStrFor, _messagePtr); } void Scumm_v8::o8_printLine() { // FIXME decodeParseString(0, 0); } void Scumm_v8::o8_printCursor() { // FIXME decodeParseString(1, 0); } void Scumm_v8::o8_printDebug() { // FIXME decodeParseString(2, 0); } void Scumm_v8::o8_printSystem() { // FIXME decodeParseString(3, 0); } void Scumm_v8::o8_blastText() { // FIXME decodeParseString(1, 0); } void Scumm_v8::o8_cursorCommand() { // TODO byte subOp = fetchScriptByte(); int a, i; int args[16]; switch (subOp) { case 0xDC: // SO_CURSOR_ON Turn cursor on _cursor.state = 1; verbMouseOver(0); break; case 0xDD: // SO_CURSOR_OFF Turn cursor off _cursor.state = 0; verbMouseOver(0); break; case 0xDE: // SO_CURSOR_SOFT_ON Turn soft cursor on _cursor.state++; if (_cursor.state > 1) error("Cursor state greater than 1 in script"); verbMouseOver(0); break; case 0xDF: // SO_CURSOR_SOFT_OFF Turn soft cursor off _cursor.state--; verbMouseOver(0); break; case 0xE0: // SO_USERPUT_ON _userPut = 1; break; case 0xE1: // SO_USERPUT_OFF _userPut = 0; break; case 0xE2: // SO_USERPUT_SOFT_ON _userPut++; break; case 0xE3: // SO_USERPUT_SOFT_OFF _userPut--; break; case 0xE4: // SO_CURSOR_IMAGE Set cursor image { int idx = pop(); int room, obj; obj = popRoomAndObj(&room); setCursorImg(obj, room, idx); } break; case 0xE5: // SO_CURSOR_HOTSPOT Set cursor hotspot a = pop(); setCursorHotspot2(pop(), a); break; case 0xE6: // SO_CURSOR_TRANSPARENT Set cursor transparent color makeCursorColorTransparent(pop()); break; case 0xE7: { // SO_CHARSET_SET int charset = pop(); warning("Set userface charset to %d\n", charset); // loadCharset(charset); break; } case 0xE8: // SO_CHARSET_COLOR getStackList(args, sizeof(args) / sizeof(args[0])); for (i = 0; i < 16; i++) _charsetColorMap[i] = _charsetData[_string[1].t_charset][i] = (unsigned char)args[i]; break; case 0xE9: // SO_CURSOR_PUT default: error("o8_cursorCommand: default case %d", subOp); } _vars[VAR_CURSORSTATE] = _cursor.state; _vars[VAR_USERPUT] = _userPut; } void Scumm_v8::o8_resourceRoutines() { // TODO byte subOp = fetchScriptByte(); int resid = pop(); switch (subOp) { case 0x3C: // SO_HEAP_LOAD_CHARSET Load character set to heap ensureResourceLoaded(rtCharset, resid); // FIXME - is this correct? break; case 0x3D: // SO_HEAP_LOAD_COSTUME Load costume to heap ensureResourceLoaded(rtCostume, resid); break; case 0x3E: // SO_HEAP_LOAD_OBJECT Load object to heap // TODO - is 'object' in COMI the same as FlObject in Sam&Max ?!? break; case 0x3F: // SO_HEAP_LOAD_ROOM Load room to heap ensureResourceLoaded(rtRoom, resid); break; case 0x40: // SO_HEAP_LOAD_SCRIPT Load script to heap ensureResourceLoaded(rtScript, resid); break; case 0x41: // SO_HEAP_LOAD_SOUND Load sound to heap ensureResourceLoaded(rtSound, resid); break; case 0x42: // SO_HEAP_LOCK_COSTUME Lock costume in heap lock(rtCostume, resid); break; case 0x43: // SO_HEAP_LOCK_ROOM Lock room in heap lock(rtRoom, resid); break; case 0x44: // SO_HEAP_LOCK_SCRIPT Lock script in heap lock(rtScript, resid); break; case 0x45: // SO_HEAP_LOCK_SOUND Lock sound in heap lock(rtSound, resid); break; case 0x46: // SO_HEAP_UNLOCK_COSTUME Unlock costume unlock(rtCostume, resid); break; case 0x47: // SO_HEAP_UNLOCK_ROOM Unlock room unlock(rtRoom, resid); break; case 0x48: // SO_HEAP_UNLOCK_SCRIPT Unlock script unlock(rtScript, resid); break; case 0x49: // SO_HEAP_UNLOCK_SOUND Unlock sound unlock(rtSound, resid); break; case 0x4A: // SO_HEAP_NUKE_COSTUME Remove costume from heap setResourceCounter(rtCostume, resid, 0x7F); break; case 0x4B: // SO_HEAP_NUKE_ROOM Remove room from heap setResourceCounter(rtRoom, resid, 0x7F); break; case 0x4C: // SO_HEAP_NUKE_SCRIPT Remove script from heap setResourceCounter(rtScript, resid, 0x7F); break; case 0x4D: // SO_HEAP_NUKE_SOUND Remove sound from heap setResourceCounter(rtSound, resid, 0x7F); break; default: error("o8_resourceRoutines: default case %d", subOp); } } void Scumm_v8::o8_roomOps() { // TODO byte subOp = fetchScriptByte(); int a, b, c, d, e; switch (subOp) { case 0x52: // SO_ROOM_PALETTE Set room palette error("o8_roomOps: default case %d", subOp); break; case 0x55: // SO_ROOM_INTENSITY Set room intensity // Not used in CMI??? c = pop(); b = pop(); a = pop(); darkenPalette(a, a, a, b, c); break; case 0x57: // SO_ROOM_FADE Fade room a = pop(); if (a) { _switchRoomEffect = (byte)(a); _switchRoomEffect2 = (byte)(a >> 8); } else { fadeIn(_newEffect); } break; case 0x58: // SO_ROOM_RGB_INTENSITY Set room color intensity e = pop(); d = pop(); c = pop(); b = pop(); a = pop(); darkenPalette(a, b, c, d, e); break; case 0x59: // SO_ROOM_TRANSFORM Transform room case 0x5A: // SO_ROOM_CYCLE_SPEED Set palette cycling speed case 0x5B: // SO_ROOM_COPY_PALETTE Copy palette case 0x5C: // SO_ROOM_NEW_PALETTE Create new palette error("o8_roomOps: unimplemented case %d", subOp); break; case 0x5D: // SO_ROOM_SAVE_GAME Save game warning("V8 Save game opcode not implemented"); break; case 0x5E: // SO_ROOM_LOAD_GAME Load game warning("V8 Load game opcode not implemented"); break; case 0x5F: // SO_ROOM_SATURATION Set saturation of room colors default: error("o8_roomOps: default case %d", subOp); } } void Scumm_v8::o8_actorOps() { // TODO byte subOp = fetchScriptByte(); Actor *a; int i, j; if (subOp == 0x7A) { _curActor = pop(); //printf("Setting current actor to %d\n", _curActor); return; } a = derefActorSafe(_curActor, "o8_actorOps"); if (!a) return; switch (subOp) { case 0x64: // SO_ACTOR_COSTUME Set actor costume a->setActorCostume(pop()); break; case 0x65: // SO_ACTOR_STEP_DIST Set actor width of steps j = pop(); i = pop(); a->setActorWalkSpeed(i, j); break; case 0x67: // SO_ACTOR_ANIMATION_DEFAULT Set actor animation to default a->initFrame = 1; a->walkFrame = 2; a->standFrame = 3; a->talkFrame1 = 4; a->talkFrame2 = 5; break; case 0x68: // SO_ACTOR_ANIMATION_INIT Initialize animation a->initFrame = pop(); break; case 0x69: // SO_ACTOR_ANIMATION_TALK Set actor animation to talk animation a->talkFrame2 = pop(); a->talkFrame1 = pop(); break; case 0x6A: // SO_ACTOR_ANIMATION_WALK Set actor animation to walk animation a->walkFrame = pop(); break; case 0x6B: // SO_ACTOR_ANIMATION_STAND Set actor animation to standing animation a->standFrame = pop(); break; case 0x6C: // SO_ACTOR_ANIMATION_SPEED Set speed of animation a->animSpeed = pop(); a->animProgress = 0; break; case 0x6D: // SO_ACTOR_DEFAULT // FIXME - is this right? Or maybe a->initActor(2) ? a->initActor(0); break; case 0x6E: // SO_ACTOR_ELEVATION a->elevation = pop(); a->needRedraw = true; a->needBgReset = true; break; case 0x6F: // SO_ACTOR_PALETTE Set actor palette j = pop(); i = pop(); checkRange(31, 0, i, "Illegal palet slot %d"); a->palette[i] = j; a->needRedraw = true; break; case 0x70: // SO_ACTOR_TALK_COLOR Set actor talk color a->talkColor = pop(); break; case 0x71: // SO_ACTOR_NAME Set name of actor loadPtrToResource(rtActorName, a->number, NULL); break; case 0x72: // SO_ACTOR_WIDTH Set width of actor a->width = pop(); break; case 0x73: // SO_ACTOR_SCALE Set scaling of actor a->scalex = a->scaley = pop(); a->needRedraw = true; a->needBgReset = true; break; case 0x74: // SO_ACTOR_NEVER_ZCLIP ? a->forceClip = 0; break; case 0x75: // SO_ACTOR_ALWAYS_ZCLIP ? a->forceClip = pop(); break; case 0x76: // SO_ACTOR_IGNORE_BOXES Make actor ignore boxes a->ignoreBoxes = true; a->forceClip = 100; if (a->isInCurrentRoom()) a->putActor(a->x, a->y, a->room); break; case 0x77: // SO_ACTOR_FOLLOW_BOXES Make actor follow boxes a->ignoreBoxes = false; a->forceClip = 100; if (a->isInCurrentRoom()) a->putActor(a->x, a->y, a->room); break; case 0x78: // SO_ACTOR_SPECIAL_DRAW // TODO - implement this! i = pop(); warning("o8_actorOps: specialDraw(%d) not implemented", i); break; case 0x79: // SO_ACTOR_TEXT_OFFSET Set text offset relative to actor a->talkPosX = pop(); a->talkPosY = pop(); break; // case 0x7A: // SO_ACTOR_INIT Initialize actor case 0x7B: // SO_ACTOR_VARIABLE Set actor variable // FIXME - is this right?? i = pop(); a->setAnimVar(pop(), i); break; case 0x7C: // SO_ACTOR_IGNORE_TURNS_ON Make actor ignore turns a->ignoreTurns = true; break; case 0x7D: // SO_ACTOR_IGNORE_TURNS_OFF Make actor follow turns a->ignoreTurns = false; break; case 0x7E: // SO_ACTOR_NEW New actor // FIXME - is this right? Or maybe a->initActor(0) ? a->initActor(2); break; case 0x7F: // SO_ACTOR_DEPTH Set actor Z position a->layer = pop(); break; case 0x80: // SO_ACTOR_STOP a->stopActorMoving(); a->startAnimActor(a->standFrame); break; case 0x81: // SO_ACTOR_FACE Make actor face angle a->moving &= ~MF_TURN; a->setDirection(pop()); break; case 0x82: // SO_ACTOR_TURN Turn actor a->turnToDirection(pop()); break; case 0x83: // SO_ACTOR_WALK_SCRIPT Set walk script for actor? a->walk_script = pop(); break; case 0x84: // SO_ACTOR_TALK_SCRIPT Set talk script for actor? a->talk_script = pop(); break; case 0x85: // SO_ACTOR_WALK_PAUSE a->moving |= 0x80; break; case 0x86: // SO_ACTOR_WALK_RESUME a->moving &= ~0x7f; break; case 0x87: // SO_ACTOR_VOLUME Set volume of actor speech // TODO - implement this! i = pop(); warning("o8_actorOps: setActorVolume(%d) not implemented", i); break; case 0x88: // SO_ACTOR_FREQUENCY Set frequency of actor speech // TODO - implement this! i = pop(); warning("o8_actorOps: setActorFrequency(%d) not implemented", i); break; case 0x89: // SO_ACTOR_PAN // TODO - implement this! i = pop(); warning("o8_actorOps: setActorPan(%d) not implemented", i); break; default: error("o8_actorOps: default case %d", subOp); } } void Scumm_v8::o8_cameraOps() { // TODO byte subOp = fetchScriptByte(); switch (subOp) { case 0x32: // SO_CAMERA_PAUSE warning("freezeCamera NYI"); break; case 0x33: // SO_CAMERA_RESUME warning("unfreezeCamera NYI"); break; default: error("o8_cameraOps: default case %d", subOp); } } void Scumm_v8::o8_verbOps() { // TODO byte subOp = fetchScriptByte(); VerbSlot *vs = NULL; if (0 <= _curVerbSlot && _curVerbSlot < _maxVerbs) vs = &_verbs[_curVerbSlot]; switch (subOp) { case 0x96: // SO_VERB_INIT Choose verb number for editing _curVerb = pop(); _curVerbSlot = getVerbSlot(_curVerb, 0); checkRange(_maxVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d"); break; case 0x97: // SO_VERB_NEW New verb case 0x98: // SO_VERB_DELETE Delete verb case 0x99: // SO_VERB_NAME Set verb name case 0x9A: // SO_VERB_AT Set verb (X,Y) placement error("o8_verbops: default case %d", subOp); break; case 0x9B: // SO_VERB_ON Turn verb on vs->curmode = 1; break; case 0x9C: // SO_VERB_OFF Turn verb off vs->curmode = 0; break; case 0x9D: // SO_VERB_COLOR Set verb color vs->color = pop(); break; case 0x9E: // SO_VERB_HICOLOR Set verb highlighted color vs->hicolor = pop(); break; case 0xA0: // SO_VERB_DIMCOLOR Set verb dimmed (disabled) color vs->dimcolor = pop(); break; case 0xA1: // SO_VERB_DIM vs->curmode = 2; break; case 0xA2: // SO_VERB_KEY Set keypress to associate with verb vs->key = pop(); break; case 0xA3: // SO_VERB_IMAGE Set verb image case 0xA4: // SO_VERB_NAME_STR Set verb name error("o8_verbops: default case %d", subOp); break; case 0xA5: // SO_VERB_CENTER Center verb vs->center = 1; break; case 0xA6: // SO_VERB_CHARSET Choose charset for verb // FIXME - TODO vs->charset_nr = pop(); //printf("Set to charset %d\n", vs->charset_nr); break; case 0xA7: // SO_VERB_LINE_SPACING Choose linespacing for verb // FIXME - TODO pop(); break; default: error("o8_verbops: default case %d", subOp); } } void Scumm_v8::o8_soundKludge() { int args[16]; int num = getStackList(args, sizeof(args) / sizeof(args[0])); _sound->soundKludge(args, num); } void Scumm_v8::o8_system() { // TODO byte subOp = fetchScriptByte(); // switch (subOp) { // default: error("o8_system: default case %d", subOp); // } } void Scumm_v8::o8_startVideo() { char dirName[255]; int len = resStrLen((char*)_scriptPointer); sprintf(dirName, "%s/resource/", getGameDataPath()); warning("o8_startVideo(%s/%s)\n", dirName, (char*)_scriptPointer); ScummRenderer * sr = new ScummRenderer(this, 1000/14); SmushPlayer * sp = new SmushPlayer(sr); //sp->play((char*)_scriptPointer, dirName); _scriptPointer += len + 1; } void Scumm_v8::o6_kernelSetFunctions() { // TODO int args[30]; int len = getStackList(args, sizeof(args) / sizeof(args[0])); switch (args[0]) { case 11: // lockObject warning("o6_kernelSetFunctions: lockObject(%d)", args[1]); // getObjectIndex(args[1]); // if (ObjData.field28 != 0) { // ObjData.field32 = 1; // } break; case 12: // unlockObject warning("o6_kernelSetFunctions: unlockObject(%d)", args[1]); // getObjectIndex(args[1]); // if (ObjData.field28 != 0) { // ObjData.field32 = 0; // } break; case 13: // remapCostume derefActorSafe(args[1], "o6_kernelSetFunctions:remapCostume")->remapActorPalette(args[2], args[3], args[4], -1); break; case 14: // remapCostumeInsert derefActorSafe(args[1], "o6_kernelSetFunctions:remapCostumeInsert")->remapActorPalette(args[2], args[3], args[4], args[5]); break; case 15: // setVideoFrameRate // not used anymore (was smush frame rate) break; case 20: // setBoxSlot setBoxScale(args[1], args[2]); break; case 21: // setScaleSlot warning("o6_kernelSetFunctions: setScaleSlot(%d, %d, %d, %d, %d, %d, %d)", args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break; case 22: // setBannerColors // warning("o6_kernelSetFunctions: setBannerColors(%d, %d, %d, %d)", args[1], args[2], args[3], args[4]); break; case 23: // setActorChoreLimbFrame warning("o6_kernelSetFunctions: setActorChoreLimbFrame(%d, %d, %d, %d)", args[1], args[2], args[3], args[4]); break; case 24: // clearTextQueue warning("o6_kernelSetFunctions: clearTextQueue()"); break; case 25: // saveGameWrite warning("o6_kernelSetFunctions: saveGameWrite(%d, %d)", args[1], args[2]); break; case 26: // saveGameRead warning("o6_kernelSetFunctions: saveGameRead(%d, %d)", args[1], args[2]); break; case 27: // saveGameReadName warning("o6_kernelSetFunctions: saveGameReadName(%d)", args[1]); break; case 28: // saveGameStampScreenshot warning("o6_kernelSetFunctions: saveGameStampScreenshot(%d, %d, %d, %d, %d, %d)", args[1], args[2], args[3], args[4], args[5], args[6]); break; case 29: // setKeyScript warning("o6_kernelSetFunctions: setKeyScript(%d, %d)", args[1], args[2]); break; case 30: // killAllScriptsButMe warning("o6_kernelSetFunctions: killAllScriptsButMe()"); killAllScriptsExceptCurrent(); break; case 31: // stopAllVideo warning("o6_kernelSetFunctions: stopAllVideo()"); break; case 32: // writeRegistryValue warning("o6_kernelSetFunctions: writeRegistryValue(%d, %d)", args[1], args[2]); break; case 33: // paletteSetIntensity warning("o6_kernelSetFunctions: paletteSetIntensity(%d, %d)", args[1], args[2]); break; case 34: // queryQuit warning("o6_kernelSetFunctions: queryQuit()"); break; case 108: setupShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]); break; case 109: setupShadowPalette(0, args[1], args[2], args[3], args[4], args[5]); break; case 115: // getWalkBoxAt // TODO break; case 116: // isPointInBox // TODO break; case 118: enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3); break; case 119: enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0); break; default: warning("o6_kernelSetFunctions: default case (len = %d)", len); } } void Scumm_v8::o6_kernelGetFunctions() { // TODO int args[30]; int len = getStackList(args, sizeof(args) / sizeof(args[0])); switch (args[0]) { case 0xCE: // getRGBSlot case 0xD3: // getKeyState case 0xD7: // getBox case 0xD8: // findBlastObject case 0xD9: // actorHit case 0xDA: // lipSyncWidth case 0xDB: // lipSyncHeight case 0xDC: // actorTalkAnimation // FIXME - hack! push(0); break; case 0xDD: // getMasterSFXVol push(_sound->_sound_volume_sfx / 2); break; case 0xDE: // getMasterVoiceVol push(_sound->_sound_volume_sfx / 2); break; case 0xDF: // getMasterMusicVol push(_sound->_sound_volume_music / 2); break; case 0xE0: // readRegistryValue { printf("readRegistryValue(%d)\n", args[1]); int array = args[1]; // FIXME - hack: for some reasons the wrong variable ID arrives here, compared to the // scripts. Probably a wrong push/pop somewhere. For now override to correct value. array = 658; ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array)); if (!strcmp((char *)ah->data, "Saveload Page")) push(1); else push(0); } break; default: error("o6_kernelGetFunctions: default case (len = %d)", len); } } void Scumm_v8::o8_getObjectImageX() { int i = getObjectIndex(pop()); push(_objs[i].x_pos); } void Scumm_v8::o8_getObjectImageY() { int i = getObjectIndex(pop()); push(_objs[i].y_pos); } void Scumm_v8::o8_getObjectImageWidth() { int i = getObjectIndex(pop()); push(_objs[i].width); } void Scumm_v8::o8_getObjectImageHeight() { int i = getObjectIndex(pop()); push(_objs[i].height); } /* From http://scummrev.mixnmojo.com/specs/CMIOpcodes.shtml 000 O_0 001 O_PUSH_NUMBER Push number onto stack. 002 O_PUSH_VARIABLE Push variable value onto stack. 003 O_PUSH_ARRAY_VALUE Push array value onto stack. 004 O_PUSH_ARRAY2_VALUE ? 005 O_DUP Duplicate stack value 006 O_POP Pop value from stack. 007 O_NOT NOT (!) 008 O_EQ Equals (==) 009 O_NEQ Does not equal (!=) 00A O_GT Greater than (>) 00B O_LT Less than (<) 00C O_LEQ Less than or equal (<=) 00D O_GEQ Greater than or equal (>=) 00E O_ADD Add (+) 00F O_SUB Subtract (-) 010 O_MUL Multiply (*) 011 O_DIV Divide (/) 012 O_LAND Logical AND 013 O_LOR Logical OR 014 O_BAND Binary AND 015 O_BOR Binary OR 016 O_MOD Modulus (%) 017 O_23 018 O_24 019 O_25 01A O_26 01B O_27 01C O_28 01D O_29 01E O_30 01F O_31 020 O_32 021 O_33 022 O_34 023 O_35 024 O_36 025 O_37 026 O_38 027 O_39 028 O_40 029 O_41 02A O_42 02B O_43 02C O_44 02D O_45 02E O_46 02F O_47 030 O_48 031 O_49 032 O_50 033 O_51 034 O_52 035 O_53 036 O_54 037 O_55 038 O_56 039 O_57 03A O_58 03B O_59 03C O_60 03D O_61 03E O_62 03F O_63 040 O_64 041 O_65 042 O_66 043 O_67 044 O_68 045 O_69 046 O_70 047 O_71 048 O_72 049 O_73 04A O_74 04B O_75 04C O_76 04D O_77 04E O_78 04F O_79 050 O_80 051 O_81 052 O_82 053 O_83 054 O_84 055 O_85 056 O_86 057 O_87 058 O_88 059 O_89 05A O_90 05B O_91 05C O_92 05D O_93 05E O_94 05F O_95 060 O_96 061 O_97 062 O_98 063 O_99 064 O_IF if () 065 O_IF_NOT if not () 066 O_JUMP jump/goto 067 O_BREAK_HERE Break out of script 068 O_BREAK_HERE_VAR 069 O_WAIT_FOR_STUFF Wait Sub opcodes 06A O_SLEEP_JIFFIES Sleep for jiffies (10ths of a second) 06B O_SLEEP_SECONDS Sleep for seconds 06C O_SLEEP_MINUTES Sleep for minutes 06D O_STORE_VARIABLE Assign value to variable 06E O_INC_VARIABLE Increase variable value (++) 06F O_DEC_VARIABLE Decrease variable value (--) 070 O_ARRAY_DIM Set dimension of array 071 O_STORE_ARRAY Assign value to array 072 O_INC_ARRAY Increase array value 073 O_DEC_ARRAY Decrease array value 074 O_ARRAY_DIM2 Set dimensions of 2 dimensional array 075 O_STORE_ARRAY2 Assign value to 2 dimensional array 076 O_ASSIGN_ARRAY ? 077 O_ARRAY_SHUFFLE Shuffle array 078 O_ARRAY_LOCALIZE ? 079 O_START_SCRIPT Start script 07A O_START_SCRIPT_QUICK ? 07B O_END_SCRIPT End script 07C O_STOP_SCRIPT Stop script from running 07D O_CHAIN_SCRIPT ? 07E O_RETURN Return 07F O_START_OBJECT ? 080 O_STOP_OBJECT ? 081 O_CUT_SCENE Start of cutscene (interface off) 082 O_END_CUT_SCENE End of cutscene (interface on) 083 O_FREEZE_SCRIPTS ? 084 O_OVERRIDE ? 085 O_OVERRIDE_OFF ? 086 O_STOP_SENTENCE ? 087 O_DEBUG Set debug mode 088 O_DEBUG_WINDEX Set debug mode with output to external window 089 O_CLASS_OF Set class of script 08A O_STATE_OF ? 08B O_OWNER_OF Set owner of object 08C O_CAMERA_PAN_TO Pan camera to (X,Y) 08D O_CAMERA_FOLLOW Make camera follow character/object 08E O_CAMERA_AT Place camera at specific (X,Y) 08F O_SAY_LINE Talk 090 O_SAY_LINE_DEFAULT Talk using default actor 091 O_SAY_LINE_SIMPLE Talk with less arguments 092 O_SAY_LINE_SIMPLE_DEFAULT Talk with less arguments using default actor 093 O_PRINT_LINE Print a line on screen 094 O_PRINT_CURSOR 095 O_PRINT_DEBUG 096 O_PRINT_SYSTEM 097 O_BLAST_TEXT Text to output to screen 098 O_DRAW_OBJECT Draw object 099 O_153 09A O_BLAST_OBJECT 09B O_155 09C O_USERFACE ? 09D O_CURRENT_ROOM Set current room 09E O_COME_OUT_DOOR 09F O_WALK_ACTOR_TO_OBJECT Walk to object 0A0 O_WALK_ACTOR_TO_XY Walk to coordinate 0A1 O_PUT_ACTOR_AT_XY Put at coordinate 0A2 O_PUT_ACTOR_AT_OBJECT Put at object 0A3 O_FACE_TOWARDS Change facing 0A4 O_DO_ANIMATION Animate 0A5 O_DO_SENTENCE 0A6 O_PICK_UP_OBJECT Pick up object 0A7 O_SET_BOX 0A8 O_SET_BOX_PATH 0A9 O_SET_BOX_SET 0AA O_HEAP_STUFF Heap sub opcodes 0AB O_ROOM_STUFF Room sub opcodes 0AC O_ACTOR_STUFF Actor sub opcodes 0AD O_CAMERA_STUFF Camera sub opcodes 0AE O_VERB_STUFF Verb sub opcodes 0AF O_START_SFX Start sound effect 0B0 O_START_MUSIC Start music 0B1 O_STOP_SOUND Stop sound (effect or music) 0B2 O_SOUND_KLUDGE ? 0B3 O_SYSTEM System sub opcodes 0B4 O_VERB_SETS 0B5 O_NEW_NAME_OF Set new name of object 0B6 O_GET_TIME_DATE Get time and/or date 0B7 O_DRAW_BOX 0B8 O_ACTOBJ_STAMP 0B9 O_START_VIDEO Start cutscene video 0BA O_KLUDGE 0BB O_187 0BC O_188 0BD O_189 0BE O_190 0BF O_191 0C0 O_192 0C1 O_193 0C2 O_194 0C3 O_195 0C4 O_196 0C5 O_197 0C6 O_198 0C7 O_199 0C8 F_START_SCRIPT Start script 0C9 F_START_OBJECT Start object script 0CA F_PICK 0CB F_PICK_DEFAULT 0CC F_PICK_RANDOM 0CD F_IN_SET 0CE F_RANDOM Get random number 0CF F_RANDOM_BETWEEN Get random number between two values 0D0 F_CLASS_OF Get class of script 0D1 F_STATE_OF 0D2 F_OWNER_OF Get owner of object 0D3 F_SCRIPT_RUNNING Test if script is running 0D4 F_OBJECT_RUNNING Test if object is running 0D5 F_SOUND_RUNNING Test if sound is running 0D6 F_ABS Get absolute value 0D7 F_PIXEL 0D8 F_KLUDGE 0D9 F_IN_BOX Test if object is in box 0DA F_VALID_VERB 0DB F_FIND_ACTOR 0DC F_FIND_OBJECT 0DD F_FIND_VERB 0DE F_FIND_ALL_OBJECTS 0DF F_ACTOR_INVENTORY 0E0 F_ACTOR_INVENTORY_COUNT Get number of items in inventory 0E1 F_ACTOR_VARIABLE Get actor variable (property) 0E2 F_ACTOR_ROOM Get current room for actor 0E3 F_ACTOR_BOX Get current box for actor 0E4 F_ACTOR_MOVING Test if actor is moving 0E5 F_ACTOR_COSTUME Get current costume for actor 0E6 F_ACTOR_SCALE Get current scale of actor 0E7 F_ACTOR_DEPTH Get current Z position of actor 0E8 F_ACTOR_ELEVATION Get current actor elevation 0E9 F_ACTOR_WIDTH Get current actor width 0EA F_ACTOBJ_FACING Get current actor/object facing 0EB F_ACTOBJ_X Get X position of actor/object 0EC F_ACTOBJ_Y Get Y position of actor/object 0ED F_ACTOR_CHORE 0EE F_PROXIMITY_2ACTOBJS Get distance between 2 actors/objects 0EF F_PROXIMITY_2POINTS Get distance between 2 points 0F0 F_OBJECT_IMAGE_X Get X position of object image 0F1 F_OBJECT_IMAGE_Y Get Y position of object image 0F2 F_OBJECT_IMAGE_WIDTH Get width of object image 0F3 F_OBJECT_IMAGE_HEIGHT Get height of object image 0F4 F_VERB_X Get X position of verb 0F5 F_VERB_Y Get Y position of verb 0F6 F_STRING_WIDTH 0F7 F_ACTOR_ZPLANE 0F8 O_248 0F9 O_249 0FA O_250 0FB O_251 0FC O_252 0FD O_253 0FE O_254 0FF O_255 The following are subopcodes - just strip the leading 1 100 SO_256 101 SO_257 102 SO_258 103 SO_259 104 SO_260 105 SO_261 106 SO_262 107 SO_263 108 SO_264 109 SO_265 10A SO_ARRAY_SCUMMVAR 10B SO_ARRAY_STRING 10C SO_ARRAY_UNDIM 10D SO_269 10E SO_270 10F SO_271 110 SO_272 111 SO_273 112 SO_274 113 SO_275 114 SO_ASSIGN_STRING 115 SO_ASSIGN_SCUMMVAR_LIST 116 SO_ASSIGN_2DIM_LIST 117 SO_279 118 SO_280 119 SO_281 11A SO_282 11B SO_283 11C SO_284 11D SO_285 11E SO_WAIT_FOR_ACTOR Wait for actor (to finish current action?) 11F SO_WAIT_FOR_MESSAGE Wait for message 120 SO_WAIT_FOR_CAMERA Wait for camera (to finish current action?) 121 SO_WAIT_FOR_SENTENCE 122 SO_WAIT_FOR_ANIMATION 123 SO_WAIT_FOR_TURN 124 SO_292 125 SO_293 126 SO_294 127 SO_295 128 SO_SYSTEM_RESTART Restart game 129 SO_SYSTEM_QUIT Quit game 12A SO_298 12B SO_299 12C SO_300 12D SO_301 12E SO_302 12F SO_303 130 SO_304 131 SO_305 132 SO_CAMERA_PAUSE 133 SO_CAMERA_RESUME 134 SO_308 135 SO_309 136 SO_310 137 SO_311 138 SO_312 139 SO_313 13A SO_314 13B SO_315 13C SO_HEAP_LOAD_CHARSET Load character set to heap 13D SO_HEAP_LOAD_COSTUME Load costume to heap 13E SO_HEAP_LOAD_OBJECT Load object to heap 13F SO_HEAP_LOAD_ROOM Load room to heap 140 SO_HEAP_LOAD_SCRIPT Load script to heap 141 SO_HEAP_LOAD_SOUND Load sound to heap 142 SO_HEAP_LOCK_COSTUME Lock costume in heap 143 SO_HEAP_LOCK_ROOM Lock room in heap 144 SO_HEAP_LOCK_SCRIPT Lock script in heap 145 SO_HEAP_LOCK_SOUND Lock sound in heap 146 SO_HEAP_UNLOCK_COSTUME Unlock costume 147 SO_HEAP_UNLOCK_ROOM Unlock room 148 SO_HEAP_UNLOCK_SCRIPT Unlock script 149 SO_HEAP_UNLOCK_SOUND Unlock sound 14A SO_HEAP_NUKE_COSTUME Remove costume from heap 14B SO_HEAP_NUKE_ROOM Remove room from heap 14C SO_HEAP_NUKE_SCRIPT Remove script from heap 14D SO_HEAP_NUKE_SOUND Remove sound from heap 14E SO_334 14F SO_335 150 SO_336 151 SO_337 152 SO_ROOM_PALETTE Set room palette 153 SO_339 154 SO_340 155 SO_ROOM_INTENSITY Set room intensity 156 SO_342 157 SO_ROOM_FADE Fade room 158 SO_ROOM_RGB_INTENSITY Set room color intensity 159 SO_ROOM_TRANSFORM Transform room 15A SO_ROOM_CYCLE_SPEED Set palette cycling speed 15B SO_ROOM_COPY_PALETTE Copy palette 15C SO_ROOM_NEW_PALETTE Create new palette 15D SO_ROOM_SAVE_GAME Save game 15E SO_ROOM_LOAD_GAME Load game 15F SO_ROOM_SATURATION Set saturation of room colors 160 SO_352 161 SO_353 162 SO_354 163 SO_355 164 SO_ACTOR_COSTUME Set actor costume 165 SO_ACTOR_STEP_DIST Set actor width of steps 166 SO_358 167 SO_ACTOR_ANIMATION_DEFAULT Set actor animation to default 168 SO_ACTOR_ANIMATION_INIT Initialize animation 169 SO_ACTOR_ANIMATION_TALK Set actor animation to talk animation 16A SO_ACTOR_ANIMATION_WALK Set actor animation to walk animation 16B SO_ACTOR_ANIMATION_STAND Set actor animation to standing animation 16C SO_ACTOR_ANIMATION_SPEED Set speed of animation 16D SO_ACTOR_DEFAULT 16E SO_ACTOR_ELEVATION 16F SO_ACTOR_PALETTE Set actor palette 170 SO_ACTOR_TALK_COLOR Set actor talk color 171 SO_ACTOR_NAME Set name of actor 172 SO_ACTOR_WIDTH Set width of actor 173 SO_ACTOR_SCALE Set scaling of actor 174 SO_ACTOR_NEVER_ZCLIP ? 175 SO_ACTOR_ALWAYS_ZCLIP ? 176 SO_ACTOR_IGNORE_BOXES Make actor ignore boxes 177 SO_ACTOR_FOLLOW_BOXES Make actor follow boxes 178 SO_ACTOR_SPECIAL_DRAW 179 SO_ACTOR_TEXT_OFFSET Set text offset relative to actor 17A SO_ACTOR_INIT Initialize actor 17B SO_ACTOR_VARIABLE Set actor variable 17C SO_ACTOR_IGNORE_TURNS_ON Make actor ignore turns 17D SO_ACTOR_IGNORE_TURNS_OFF Make actor follow turns 17E SO_ACTOR_NEW New actor 17F SO_ACTOR_DEPTH Set actor Z position 180 SO_ACTOR_STOP 181 SO_ACTOR_FACE Make actor face angle 182 SO_ACTOR_TURN Turn actor 183 SO_ACTOR_WALK_SCRIPT Set walk script for actor? 184 SO_ACTOR_TALK_SCRIPT Set talk script for actor? 185 SO_ACTOR_WALK_PAUSE 186 SO_ACTOR_WALK_RESUME 187 SO_ACTOR_VOLUME Set volume of actor speech 188 SO_ACTOR_FREQUENCY Set frequency of actor speech 189 SO_ACTOR_PAN 18A SO_394 18B SO_395 18C SO_396 18D SO_397 18E SO_398 18F SO_399 190 SO_400 191 SO_401 192 SO_402 193 SO_403 194 SO_404 195 SO_405 196 SO_VERB_INIT Choose verb number for editing 197 SO_VERB_NEW New verb 198 SO_VERB_DELETE Delete verb 199 SO_VERB_NAME Set verb name 19A SO_VERB_AT Set verb (X,Y) placement 19B SO_VERB_ON Turn verb on 19C SO_VERB_OFF Turn verb off 19D SO_VERB_COLOR Set verb color 19E SO_VERB_HICOLOR Set verb highlighted color 19F SO_415 1A0 SO_VERB_DIMCOLOR Set verb dimmed (disabled) color 1A1 SO_VERB_DIM 1A2 SO_VERB_KEY Set keypress to associate with verb 1A3 SO_VERB_IMAGE Set verb image 1A4 SO_VERB_NAME_STR Set verb name 1A5 SO_VERB_CENTER Center verb 1A6 SO_VERB_CHARSET Choose charset for verb 1A7 SO_VERB_LINE_SPACING Choose linespacing for verb 1A8 SO_424 1A9 SO_425 1AA SO_426 1AB SO_427 1AC SO_428 1AD SO_429 1AE SO_430 1AF SO_431 1B0 SO_432 1B1 SO_433 1B2 SO_434 1B3 SO_435 1B4 SO_VERBS_SAVE 1B5 SO_VERBS_RESTORE 1B6 SO_VERBS_DELETE 1B7 SO_439 1B8 SO_440 1B9 SO_441 1BA SO_442 1BB SO_443 1BC SO_444 1BD SO_445 1BE SO_446 1BF SO_447 1C0 SO_448 1C1 SO_449 1C2 SO_450 1C3 SO_451 1C4 SO_452 1C5 SO_453 1C6 SO_454 1C7 SO_455 1C8 SO_PRINT_BASEOP 1C9 SO_PRINT_END 1CA SO_PRINT_AT Print at coordinates (x,y) 1CB SO_PRINT_COLOR Print color 1CC SO_PRINT_CENTER Center output 1CD SO_PRINT_CHARSET Set print character set 1CE SO_PRINT_LEFT Left justify output 1CF SO_PRINT_OVERHEAD 1D0 SO_PRINT_MUMBLE 1D1 SO_PRINT_STRING Set string to print 1D2 SO_PRINT_WRAP Set print wordwrap 1D3 SO_467 1D4 SO_468 1D5 SO_469 1D6 SO_470 1D7 SO_471 1D8 SO_472 1D9 SO_473 1DA SO_474 1DB SO_475 1DC SO_CURSOR_ON Turn cursor on 1DD SO_CURSOR_OFF Turn cursor off 1DE SO_CURSOR_SOFT_ON Turn soft cursor on 1DF SO_CURSOR_SOFT_OFF Turn soft cursor off 1E0 SO_USERPUT_ON 1E1 SO_USERPUT_OFF 1E2 SO_USERPUT_SOFT_ON 1E3 SO_USERPUT_SOFT_OFF 1E4 SO_CURSOR_IMAGE Set cursor image 1E5 SO_CURSOR_HOTSPOT Set cursor hotspot 1E6 SO_CURSOR_TRANSPARENT Set cursor transparent color 1E7 SO_CHARSET_SET 1E8 SO_CHARSET_COLOR 1E9 SO_CURSOR_PUT 1EA SO_490 1EB SO_491 1EC SO_492 1ED SO_493 1EE SO_494 1EF SO_495 1F0 SO_496 1F1 SO_497 1F2 SO_498 1F3 SO_499 1F4 SO_500 1F5 SO_501 1F6 SO_502 1F7 SO_503 1F8 SO_504 1F9 SO_505 1FA SO_506 1FB SO_507 1FC SO_508 1FD SO_509 1FE SO_510 1FF SO_511 */