/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/system.h" #include "touche/touche.h" namespace Touche { void ToucheEngine::setupOpcodes() { static const OpcodeProc opcodesTable[] = { /* 0x00 */ &ToucheEngine::op_nop, &ToucheEngine::op_jnz, &ToucheEngine::op_jz, &ToucheEngine::op_jmp, /* 0x04 */ &ToucheEngine::op_true, &ToucheEngine::op_false, &ToucheEngine::op_push, &ToucheEngine::op_not, /* 0x08 */ &ToucheEngine::op_add, &ToucheEngine::op_sub, &ToucheEngine::op_mul, &ToucheEngine::op_div, /* 0x0C */ &ToucheEngine::op_mod, &ToucheEngine::op_and, &ToucheEngine::op_or, &ToucheEngine::op_neg, /* 0x10 */ &ToucheEngine::op_testGreater, &ToucheEngine::op_testEquals, &ToucheEngine::op_testLower, &ToucheEngine::op_fetchScriptWord, /* 0x14 */ 0, 0, 0, 0, /* 0x18 */ &ToucheEngine::op_testGreaterOrEquals, &ToucheEngine::op_testLowerOrEquals, &ToucheEngine::op_testNotEquals, &ToucheEngine::op_endConversation, /* 0x1C */ &ToucheEngine::op_stopScript, &ToucheEngine::op_getFlag, &ToucheEngine::op_setFlag, 0, /* 0x20 */ 0, 0, 0, &ToucheEngine::op_fetchScriptByte, /* 0x24 */ 0, 0, 0, 0, /* 0x28 */ 0, 0, 0, 0, /* 0x2C */ 0, 0, &ToucheEngine::op_getKeyCharWalkBox, &ToucheEngine::op_startSound, /* 0x30 */ &ToucheEngine::op_moveKeyCharToPos, 0, 0, 0, /* 0x34 */ &ToucheEngine::op_loadRoom, &ToucheEngine::op_updateRoom, &ToucheEngine::op_startTalk, &ToucheEngine::op_setKeyCharBox, /* 0x38 */ &ToucheEngine::op_initKeyCharScript, &ToucheEngine::op_loadSprite, &ToucheEngine::op_loadSequence, &ToucheEngine::op_setKeyCharFrame, /* 0x3C */ &ToucheEngine::op_setKeyCharDirection, &ToucheEngine::op_clearConversationChoices, &ToucheEngine::op_addConversationChoice, &ToucheEngine::op_removeConversationChoice, /* 0x40 */ &ToucheEngine::op_getInventoryItem, &ToucheEngine::op_setInventoryItem, &ToucheEngine::op_startEpisode, &ToucheEngine::op_setConversationNum, /* 0x44 */ 0, &ToucheEngine::op_enableInput, &ToucheEngine::op_disableInput, &ToucheEngine::op_faceKeyChar, /* 0x48 */ &ToucheEngine::op_getKeyCharCurrentAnim, &ToucheEngine::op_getCurrentKeyChar, &ToucheEngine::op_isKeyCharActive, &ToucheEngine::op_setPalette, /* 0x4C */ &ToucheEngine::op_changeWalkPath, &ToucheEngine::op_lockWalkPath, &ToucheEngine::op_initializeKeyChar, &ToucheEngine::op_setupWaitingKeyChars, /* 0x50 */ &ToucheEngine::op_updateRoomAreas, &ToucheEngine::op_unlockWalkPath, 0, &ToucheEngine::op_addItemToInventoryAndRedraw, /* 0x54 */ &ToucheEngine::op_giveItemTo, &ToucheEngine::op_setHitBoxText, &ToucheEngine::op_fadePalette, 0, /* 0x58 */ 0, 0, 0, 0, /* 0x5C */ 0, 0, 0, 0, /* 0x60 */ 0, &ToucheEngine::op_getInventoryItemFlags, &ToucheEngine::op_drawInventory, &ToucheEngine::op_stopKeyCharScript, /* 0x64 */ &ToucheEngine::op_restartKeyCharScript, &ToucheEngine::op_getKeyCharCurrentWalkBox, &ToucheEngine::op_getKeyCharPointsDataNum, &ToucheEngine::op_setupFollowingKeyChar, /* 0x68 */ &ToucheEngine::op_startAnimation, &ToucheEngine::op_setKeyCharTextColor, 0, 0, /* 0x6C */ 0, 0, 0, 0, /* 0x70 */ &ToucheEngine::op_startMusic, 0, &ToucheEngine::op_sleep, 0, /* 0x74 */ &ToucheEngine::op_setKeyCharDelay, &ToucheEngine::op_lockHitBox, &ToucheEngine::op_removeItemFromInventory, &ToucheEngine::op_unlockHitBox, /* 0x78 */ &ToucheEngine::op_addRoomArea, &ToucheEngine::op_setKeyCharFlags, 0, 0, /* 0x7C */ 0, 0, 0, 0, /* 0x80 */ &ToucheEngine::op_unsetKeyCharFlags, &ToucheEngine::op_drawSpriteOnBackdrop, &ToucheEngine::op_loadSpeechSegment, 0, /* 0x84 */ &ToucheEngine::op_startPaletteFadeIn, &ToucheEngine::op_startPaletteFadeOut, &ToucheEngine::op_setRoomAreaState }; _opcodesTable = opcodesTable; _numOpcodes = ARRAYSIZE(opcodesTable); } void ToucheEngine::op_nop() { debugC(9, kDebugOpcodes, "ToucheEngine::op_nop()"); } void ToucheEngine::op_jnz() { debugC(9, kDebugOpcodes, "ToucheEngine::op_jnz()"); if (*_script.stackDataPtr != 0) { _script.dataOffset = _script.readNextWord(); } else { _script.dataOffset += 2; } } void ToucheEngine::op_jz() { debugC(9, kDebugOpcodes, "ToucheEngine::op_jz()"); if (*_script.stackDataPtr == 0) { _script.dataOffset = _script.readNextWord(); } else { _script.dataOffset += 2; } } void ToucheEngine::op_jmp() { debugC(9, kDebugOpcodes, "ToucheEngine::op_jmp()"); _script.dataOffset = _script.readNextWord(); } void ToucheEngine::op_true() { debugC(9, kDebugOpcodes, "ToucheEngine::op_true()"); *_script.stackDataPtr = -1; } void ToucheEngine::op_false() { debugC(9, kDebugOpcodes, "ToucheEngine::op_false()"); *_script.stackDataPtr = 0; } void ToucheEngine::op_push() { debugC(9, kDebugOpcodes, "ToucheEngine::op_push()"); --_script.stackDataPtr; *_script.stackDataPtr = 0; } void ToucheEngine::op_not() { debugC(9, kDebugOpcodes, "ToucheEngine::op_not()"); if (*_script.stackDataPtr == 0) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_add() { debugC(9, kDebugOpcodes, "ToucheEngine::op_add()"); int16 val = *_script.stackDataPtr++; *_script.stackDataPtr += val; } void ToucheEngine::op_sub() { debugC(9, kDebugOpcodes, "ToucheEngine::op_sub()"); int16 val = *_script.stackDataPtr++; *_script.stackDataPtr -= val; } void ToucheEngine::op_mul() { debugC(9, kDebugOpcodes, "ToucheEngine::op_mul()"); int16 val = *_script.stackDataPtr++; *_script.stackDataPtr *= val; } void ToucheEngine::op_div() { debugC(9, kDebugOpcodes, "ToucheEngine::op_div()"); int16 val = *_script.stackDataPtr++; if (val != 0) { *_script.stackDataPtr /= val; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_mod() { debugC(9, kDebugOpcodes, "ToucheEngine::op_mod()"); int16 val = *_script.stackDataPtr++; if (val != 0) { *_script.stackDataPtr %= val; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_and() { debugC(9, kDebugOpcodes, "ToucheEngine::op_and()"); uint16 val = *_script.stackDataPtr++; *_script.stackDataPtr &= val; } void ToucheEngine::op_or() { debugC(9, kDebugOpcodes, "ToucheEngine::op_or()"); uint16 val = *_script.stackDataPtr++; *_script.stackDataPtr |= val; } void ToucheEngine::op_neg() { debugC(9, kDebugOpcodes, "ToucheEngine::op_neg()"); uint16 val = *_script.stackDataPtr; *_script.stackDataPtr = ~val; } void ToucheEngine::op_testGreater() { debugC(9, kDebugOpcodes, "ToucheEngine::op_testGreater()"); int16 val = *_script.stackDataPtr++; if (val > *_script.stackDataPtr) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_testEquals() { debugC(9, kDebugOpcodes, "ToucheEngine::op_testEquals()"); int16 val = *_script.stackDataPtr++; if (val == *_script.stackDataPtr) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_testLower() { debugC(9, kDebugOpcodes, "ToucheEngine::op_testLower()"); int16 val = *_script.stackDataPtr++; if (val < *_script.stackDataPtr) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_fetchScriptWord() { debugC(9, kDebugOpcodes, "ToucheEngine::op_fetchScriptWord()"); int16 val = _script.readNextWord(); *_script.stackDataPtr = val; } void ToucheEngine::op_testGreaterOrEquals() { debugC(9, kDebugOpcodes, "ToucheEngine::op_testGreaterOrEquals()"); int16 val = *_script.stackDataPtr++; if (val >= *_script.stackDataPtr) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_testLowerOrEquals() { debugC(9, kDebugOpcodes, "ToucheEngine::op_testLowerOrEquals()"); int16 val = *_script.stackDataPtr++; if (val <= *_script.stackDataPtr) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_testNotEquals() { debugC(9, kDebugOpcodes, "ToucheEngine::op_testNotEquals()"); int16 val = *_script.stackDataPtr++; if (val != *_script.stackDataPtr) { *_script.stackDataPtr = -1; } else { *_script.stackDataPtr = 0; } } void ToucheEngine::op_endConversation() { debugC(9, kDebugOpcodes, "ToucheEngine::op_endConversation()"); _script.quitFlag = 1; _conversationEnded = true; _disabledInputCounter = 0; } void ToucheEngine::op_stopScript() { debugC(9, kDebugOpcodes, "ToucheEngine::op_stopScript()"); _script.quitFlag = 1; } void ToucheEngine::op_getFlag() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getFlag()"); uint16 fl = _script.readNextWord(); *_script.stackDataPtr = _flagsTable[fl]; } void ToucheEngine::op_setFlag() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setFlag()"); uint16 flag = _script.readNextWord(); int16 val = *_script.stackDataPtr; _flagsTable[flag] = val; switch (flag) { case 104: _currentKeyCharNum = val; break; case 611: if (val != 0) quitGame(); break; case 612: _flagsTable[613] = getRandomNumber(val); break; case 614: case 615: _fullRedrawCounter = 1; break; case 618: showCursor(val == 0); break; case 619: debug(0, "Unknown music flag %d", val); break; } } void ToucheEngine::op_fetchScriptByte() { debugC(9, kDebugOpcodes, "ToucheEngine::op_fetchScriptByte()"); int16 val = _script.readNextByte(); *_script.stackDataPtr = val; } void ToucheEngine::op_getKeyCharWalkBox() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharWalkBox()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } *_script.stackDataPtr = _keyCharsTable[keyChar].walkDataNum; } void ToucheEngine::op_startSound() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startSound()"); _newSoundNum = _script.readNextWord(); _newSoundDelay = _script.readNextWord(); _newSoundPriority = 1; } void ToucheEngine::op_moveKeyCharToPos() { debugC(9, kDebugOpcodes, "ToucheEngine::op_moveKeyCharToPos()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } int16 num = _script.readNextWord(); if (num == -1) { num = _script.readNextWord(); num = _keyCharsTable[num].pointsDataNum; } sortPointsData(-1, num); buildWalkPointsList(keyChar); _keyCharsTable[keyChar].flags &= ~0x10; if (_script.keyCharNum == keyChar) { removeFromTalkTable(_script.keyCharNum); _keyCharsTable[keyChar].waitingKeyCharPosTable[0] = -1; _keyCharsTable[keyChar].waitingKeyCharPosTable[2] = -1; _keyCharsTable[keyChar].waitingKeyChar = _script.keyCharNum; _keyCharsTable[keyChar].waitingKeyCharPosTable[1] = num; _script.quitFlag = 3; } } void ToucheEngine::op_loadRoom() { debugC(9, kDebugOpcodes, "ToucheEngine::op_loadRoom()"); int16 num = _script.readNextWord(); res_loadRoom(num); } void ToucheEngine::op_updateRoom() { debugC(9, kDebugOpcodes, "ToucheEngine::op_updateRoom()"); int16 area = _script.readNextWord(); updateRoomAreas(area, 0); // Workaround for bug #1618700. Beggar sign (area 25) should be displayed even // if Henri isn't present in the room. // // [00B3] (1D) ST[0] = FLAGS[2] // [00B6] (06) PUSH // [00B7] (13) ST[0] = 0 // [00BA] (11) ST[0] = ST[1] == ST[0] // [00BB] (02) JZ 0xF6 // [xxxx] ... // [0192] (35) UPDATE_ROOM(16, 0) // [0195] (35) UPDATE_ROOM(19, 0) if (_currentEpisodeNum == 91 && area == 19 && _flagsTable[2] != 0) { debug(0, "Workaround beggar sign disappearing bug"); updateRoomAreas(25, 0); } } void ToucheEngine::op_startTalk() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startTalk()"); int16 keyChar = _script.readNextWord(); int16 num = _script.readNextWord(); if (num == 750) { return; } if (keyChar == 256) { keyChar = _currentKeyCharNum; num += _currentKeyCharNum & 1; } addToTalkTable(keyChar, num, _script.keyCharNum); _script.quitFlag = 3; } void ToucheEngine::op_loadSprite() { debugC(9, kDebugOpcodes, "ToucheEngine::op_loadSprite()"); int16 index = _script.readNextWord(); int16 num = _script.readNextWord(); res_loadSprite(num, index); } void ToucheEngine::op_loadSequence() { debugC(9, kDebugOpcodes, "ToucheEngine::op_loadSequence()"); int16 index = _script.readNextWord(); int16 num = _script.readNextWord(); res_loadSequence(num, index); } void ToucheEngine::op_setKeyCharBox() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharBox()"); int16 keyChar = _script.readNextWord(); int16 num = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } setKeyCharBox(keyChar, num); } void ToucheEngine::op_initKeyCharScript() { debugC(9, kDebugOpcodes, "ToucheEngine::op_initKeyCharScript()"); int16 keyChar = _script.readNextWord(); int16 color = _script.readNextWord(); int16 f1 = _script.readNextWord(); int16 f2 = _script.readNextWord(); int16 f3 = _script.readNextWord(); setKeyCharTextColor(keyChar, color); initKeyCharScript(keyChar, f1, f2, f3); // Workaround for bug #1622114. KeyChar 3 script must be running in order to complete the // rope+torch puzzle. // // FLAG[500] : 1 if Cardinal cutscene has already been played // FLAG[501] : 1 if cathedral is lightened (by the two torches) // // [00D3] (38) INIT_KEY_CHAR_SCRIPT(keychar=1, 254, 1, 1, 0) if (_currentEpisodeNum == 109 && keyChar == 1 && _flagsTable[500] == 1 && _flagsTable[501] == 1 && _keyCharsTable[3].scriptDataOffset == 0) { debug(0, "Workaround disappearing rope bug"); initKeyCharScript(3, 3, 3, 0); } } void ToucheEngine::op_setKeyCharFrame() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharFrame()"); int16 keyChar = _script.readNextWord(); int16 val1 = _script.readNextWord(); int16 val2 = _script.readNextWord(); int16 val3 = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } setKeyCharFrame(keyChar, val1, val2, val3); } void ToucheEngine::op_setKeyCharDirection() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharDirection()"); int16 keyChar = _script.readNextWord(); int16 dir = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } setKeyCharFacingDirection(keyChar, dir); } void ToucheEngine::op_clearConversationChoices() { debugC(9, kDebugOpcodes, "ToucheEngine::op_clearConversationChoices()"); clearConversationChoices(); } void ToucheEngine::op_addConversationChoice() { debugC(9, kDebugOpcodes, "ToucheEngine::op_addConversationChoice()"); int16 num = _script.readNextWord(); addConversationChoice(num); } void ToucheEngine::op_removeConversationChoice() { debugC(9, kDebugOpcodes, "ToucheEngine::op_removeConversationChoice()"); int16 num = _script.readNextWord(); removeConversationChoice(num); } void ToucheEngine::op_getInventoryItem() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getInventoryItem()"); int16 keyChar = _script.readNextWord(); uint16 item = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); if (item == 4) { // item 4 is the 'money' field *_script.stackDataPtr = _keyCharsTable[keyChar].money; } else { assert(item < ARRAYSIZE(_keyCharsTable[keyChar].inventoryItems)); *_script.stackDataPtr = _keyCharsTable[keyChar].inventoryItems[item]; } } void ToucheEngine::op_setInventoryItem() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setInventoryItem()"); int16 keyChar = _script.readNextWord(); uint16 item = _script.readNextWord(); if (item == 4) { setKeyCharMoney(); } if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); if (item == 4) { // item 4 is the 'money' field _keyCharsTable[keyChar].money = *_script.stackDataPtr; } else { assert(item < ARRAYSIZE(_keyCharsTable[keyChar].inventoryItems)); _keyCharsTable[keyChar].inventoryItems[item] = *_script.stackDataPtr; } if (item == 4 && !_hideInventoryTexts) { drawAmountOfMoneyInInventory(); } } void ToucheEngine::op_startEpisode() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startEpisode()"); _newEpisodeNum = _script.readNextWord(); _flagsTable[0] = _script.readNextWord(); _disabledInputCounter = 1; _script.quitFlag = 1; } void ToucheEngine::op_setConversationNum() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setConversationNum()"); _conversationNum = _script.readNextWord(); } void ToucheEngine::op_enableInput() { debugC(9, kDebugOpcodes, "ToucheEngine::op_enableInput()"); ++_disabledInputCounter; } void ToucheEngine::op_disableInput() { debugC(9, kDebugOpcodes, "ToucheEngine::op_disableInput()"); if (_disabledInputCounter != 0) { --_disabledInputCounter; } } void ToucheEngine::op_faceKeyChar() { debugC(9, kDebugOpcodes, "ToucheEngine::op_faceKeyChar()"); int16 keyChar1 = _script.readNextWord(); int16 keyChar2 = _script.readNextWord(); if (keyChar1 == 256) { keyChar1 = _currentKeyCharNum; } if (_keyCharsTable[keyChar1].xPos <= _keyCharsTable[keyChar2].xPos) { _keyCharsTable[keyChar2].facingDirection = 3; } else { _keyCharsTable[keyChar2].facingDirection = 0; } } void ToucheEngine::op_getKeyCharCurrentAnim() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharCurrentAnim()"); int16 keyChar = _script.readNextWord(); assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); *_script.stackDataPtr = _keyCharsTable[keyChar].currentAnim; } void ToucheEngine::op_getCurrentKeyChar() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getCurrentKeyChar()"); *_script.stackDataPtr = _currentKeyCharNum; } void ToucheEngine::op_isKeyCharActive() { debugC(9, kDebugOpcodes, "ToucheEngine::op_isKeyCharActive()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); *_script.stackDataPtr = _keyCharsTable[keyChar].num != 0 ? 1 : 0; } void ToucheEngine::op_setPalette() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setPalette()"); int16 r = _script.readNextWord(); int16 g = _script.readNextWord(); int16 b = _script.readNextWord(); setPalette(0, 240, r, g, b); } void ToucheEngine::op_changeWalkPath() { debugC(9, kDebugOpcodes, "ToucheEngine::op_changeWalkPath()"); int16 num1 = _script.readNextWord(); int16 num2 = _script.readNextWord(); int16 val = _script.readNextWord(); changeWalkPath(num1, num2, val); } void ToucheEngine::op_lockWalkPath() { debugC(9, kDebugOpcodes, "ToucheEngine::op_lockWalkPath()"); int16 num1 = _script.readNextWord(); int16 num2 = _script.readNextWord(); lockWalkPath(num1, num2); } void ToucheEngine::op_initializeKeyChar() { debugC(9, kDebugOpcodes, "ToucheEngine::op_initializeKeyChar()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } initKeyChars(keyChar); } void ToucheEngine::op_setupWaitingKeyChars() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setupWaitingKeyChars()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } int16 val1 = _script.readNextWord(); int16 val2 = _script.readNextWord(); if (val1 == -1) { _waitingSetKeyCharNum2 = keyChar; _waitingSetKeyCharNum1 = val2; _waitingSetKeyCharNum3 = _script.keyCharNum; _script.quitFlag = 3; } else { _keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[0] = -1; _keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[1] = -1; _keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[2] = -1; _keyCharsTable[_script.keyCharNum].waitingKeyChar = keyChar; assert(val1 >= 0 && val1 < 3); _keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[val1] = val2; _script.quitFlag = 3; } } void ToucheEngine::op_updateRoomAreas() { debugC(9, kDebugOpcodes, "ToucheEngine::op_updateRoomAreas()"); int16 area = _script.readNextWord(); updateRoomAreas(area, 1); } void ToucheEngine::op_unlockWalkPath() { debugC(9, kDebugOpcodes, "ToucheEngine::op_unlockWalkPath()"); int16 num1 = _script.readNextWord(); int16 num2 = _script.readNextWord(); unlockWalkPath(num1, num2); } void ToucheEngine::op_addItemToInventoryAndRedraw() { debugC(9, kDebugOpcodes, "ToucheEngine::op_addItemToInventoryAndRedraw()"); int16 keyChar = _script.readNextWord(); int16 item = *_script.stackDataPtr; if (keyChar == 256) { keyChar = _currentKeyCharNum; } // Workaround for bug #1623356. The original script allows you to either use the // "waxy knife" (object 72) or the dagger (object 7) on the rope. But in both // situations, only the dagger is put back in the inventory. // // [1A35] (1D) ST[0] = FLAGS[119] // [1A38] (06) PUSH // [1A39] (13) ST[0] = 7 // [1A3C] (11) ST[0] = ST[1] == ST[0] // [1A3D] (06) PUSH // [1A3E] (1D) ST[0] = FLAGS[119] // [1A41] (06) PUSH // [1A42] (13) ST[0] = 72 // [1A45] (11) ST[0] = ST[1] == ST[0] // [1A46] (0E) OR // [1A47] (02) JZ 0x1B1B // [xxxx] ... // [1B05] (13) ST[0] = 7 // [1B08] (53) ADD_ITEM_TO_INVENTORY_AND_REDRAW(keychar=1) if (_currentEpisodeNum == 92 && keyChar == 1 && item == 7) { if (_flagsTable[119] == 72) { debug(0, "Workaround waxy knife not re-appearing in the inventory"); item = 72; } } addItemToInventory(keyChar, item); if (_currentKeyCharNum == keyChar && !_hideInventoryTexts) { drawInventory(_currentKeyCharNum, 1); } } void ToucheEngine::op_giveItemTo() { debugC(9, kDebugOpcodes, "ToucheEngine::op_giveItemTo()"); _giveItemToCounter = 1; _giveItemToObjectNum = _script.readNextWord(); _giveItemToKeyCharNum = _script.keyCharNum; _script.quitFlag = 3; } void ToucheEngine::op_setHitBoxText() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setHitBoxText()"); int16 num = _script.readNextWord(); if (num & 0x4000) { num &= 0xFF; _keyCharsTable[num].strNum = 1; } else { for (uint i = 0; i < _programHitBoxTable.size(); ++i) { if (_programHitBoxTable[i].item == num) { _programHitBoxTable[i].str = _programHitBoxTable[i].defaultStr; break; } } } } void ToucheEngine::op_fadePalette() { debugC(9, kDebugOpcodes, "ToucheEngine::op_fadePalette()"); int16 fadeOut = _script.readNextWord(); int colorsCount = 240; // Workaround for bug #1751149. Script triggers a palette fading, but some // of the room graphics use palette colors >= 240. if (_currentEpisodeNum == 104 && _currentRoomNum == 68) { colorsCount = 256; } if (fadeOut) { fadePalette(0, colorsCount, 255, -2, 128); } else { fadePalette(0, colorsCount, 0, 2, 128); } } void ToucheEngine::op_getInventoryItemFlags() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getInventoryItemFlags()"); int16 item = _script.readNextWord(); int16 flags = _inventoryItemsInfoTable[item]; if (flags & 0x10) { flags &= 0xF; } else { flags &= ~0xF; } *_script.stackDataPtr = flags; } void ToucheEngine::op_drawInventory() { debugC(9, kDebugOpcodes, "ToucheEngine::op_drawInventory()"); int16 num = _script.readNextWord(); drawInventory(num, 1); } void ToucheEngine::op_stopKeyCharScript() { debugC(9, kDebugOpcodes, "ToucheEngine::op_stopKeyCharScript()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); _keyCharsTable[keyChar].flags |= kScriptStopped; } void ToucheEngine::op_restartKeyCharScript() { debugC(9, kDebugOpcodes, "ToucheEngine::op_restartKeyCharScript()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); KeyChar *key = &_keyCharsTable[keyChar]; key->flags &= ~(kScriptStopped | kScriptPaused); key->scriptDataOffset = key->scriptDataStartOffset; key->scriptStackPtr = &key->scriptStackTable[39]; } void ToucheEngine::op_getKeyCharCurrentWalkBox() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharCurrentWalkBox()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); *_script.stackDataPtr = _keyCharsTable[keyChar].currentWalkBox; } void ToucheEngine::op_getKeyCharPointsDataNum() { debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharPointsDataNum()"); int16 keyChar = _script.readNextWord(); if (keyChar == 256) { keyChar = _currentKeyCharNum; } assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); *_script.stackDataPtr = _keyCharsTable[keyChar].pointsDataNum; } void ToucheEngine::op_setupFollowingKeyChar() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setupFollowingKeyChar()"); int16 val = _script.readNextWord(); int16 keyChar = _script.readNextWord(); assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); _keyCharsTable[keyChar].followingKeyCharNum = val; _keyCharsTable[keyChar].flags |= 0x10; _keyCharsTable[keyChar].followingKeyCharPos = -1; } void ToucheEngine::op_startAnimation() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startAnimation()"); int16 keyChar = _script.readNextWord(); int16 pos = _script.readNextWord(); int16 num = *_script.stackDataPtr; addToAnimationTable(num, pos, keyChar, 3); } void ToucheEngine::op_setKeyCharTextColor() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharTextColor()"); int16 keyChar = _script.readNextWord(); uint16 color = _script.readNextWord(); setKeyCharTextColor(keyChar, color); } void ToucheEngine::op_startMusic() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startMusic()"); _newMusicNum = _script.readNextWord(); } void ToucheEngine::op_sleep() { debugC(9, kDebugOpcodes, "ToucheEngine::op_sleep()"); // this should probably be turned into a no-op/debug-op... int cycles = _script.readNextWord() * 2; if (!_fastMode) { for (; cycles > 0; --cycles) { _system->delayMillis(kCycleDelay); _system->updateScreen(); } } } void ToucheEngine::op_setKeyCharDelay() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharDelay()"); int16 delay = _script.readNextWord(); _keyCharsTable[_script.keyCharNum].delay = delay; _script.quitFlag = 3; } void ToucheEngine::op_lockHitBox() { debugC(9, kDebugOpcodes, "ToucheEngine::op_lockHitBox()"); int16 num = _script.readNextWord(); lockUnlockHitBox(num, 1); } void ToucheEngine::op_removeItemFromInventory() { debugC(9, kDebugOpcodes, "ToucheEngine::op_removeItemFromInventory()"); int16 keyChar = _script.readNextWord(); int16 item = *_script.stackDataPtr; if (keyChar == 256) { keyChar = _currentKeyCharNum; } removeItemFromInventory(keyChar, item); if (keyChar == _currentKeyCharNum && !_hideInventoryTexts) { drawInventory(_currentKeyCharNum, 1); } } void ToucheEngine::op_unlockHitBox() { debugC(9, kDebugOpcodes, "ToucheEngine::op_unlockHitBox()"); int16 num = _script.readNextWord(); lockUnlockHitBox(num, 0); } void ToucheEngine::op_addRoomArea() { debugC(9, kDebugOpcodes, "ToucheEngine::op_addRoomArea()"); int16 num = _script.readNextWord(); uint16 flag = _script.readNextWord(); addRoomArea(num, flag); } void ToucheEngine::op_setKeyCharFlags() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharFlags()"); int16 keyChar = _script.readNextWord(); uint16 flags = _script.readNextWord(); flags &= 0xFF00; _keyCharsTable[keyChar].flags |= flags; } void ToucheEngine::op_unsetKeyCharFlags() { debugC(9, kDebugOpcodes, "ToucheEngine::op_unsetKeyCharFlags()"); int16 keyChar = _script.readNextWord(); uint16 flags = _script.readNextWord(); flags &= 0xFF00; _keyCharsTable[keyChar].flags &= ~flags; } void ToucheEngine::op_loadSpeechSegment() { debugC(9, kDebugOpcodes, "ToucheEngine::op_loadSpeechSegment()"); int16 num = _script.readNextWord(); res_loadSpeech(num); } void ToucheEngine::op_drawSpriteOnBackdrop() { debugC(9, kDebugOpcodes, "ToucheEngine::op_drawSpriteOnBackdrop()"); int16 num = _script.readNextWord(); int16 x = _script.readNextWord(); int16 y = _script.readNextWord(); drawSpriteOnBackdrop(num, x, y); } void ToucheEngine::op_startPaletteFadeIn() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startPaletteFadeIn()"); _flagsTable[290] = 0; _flagsTable[605] = 0; _flagsTable[607] = 0; _flagsTable[608] = 0xFF; _flagsTable[609] = 0xFF; _flagsTable[610] = 0; _flagsTable[603] = _script.readNextWord(); } void ToucheEngine::op_startPaletteFadeOut() { debugC(9, kDebugOpcodes, "ToucheEngine::op_startPaletteFadeOut()"); _flagsTable[290] = 0; _flagsTable[605] = 0xFF; _flagsTable[607] = 0; _flagsTable[608] = 0xFF; _flagsTable[609] = 0xFF; _flagsTable[610] = 0; _flagsTable[603] = -_script.readNextWord(); } void ToucheEngine::op_setRoomAreaState() { debugC(9, kDebugOpcodes, "ToucheEngine::op_setRoomAreaState()"); int16 num = _script.readNextWord(); int16 val = _script.readNextWord(); setRoomAreaState(num, val); } } // namespace Touche