/* 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. * */ #ifdef ENABLE_EOB #include "kyra/eobcommon.h" #include "kyra/screen_eob.h" #include "kyra/script_eob.h" #include "kyra/resource.h" #include "kyra/sound.h" #include "common/system.h" namespace Kyra { void EoBCoreEngine::runLevelScript(int block, int flags) { _inf->run(block, flags); } void EoBCoreEngine::setScriptFlags(uint32 flags) { _inf->setFlags(flags); } void EoBCoreEngine::clearScriptFlags(uint32 flags) { _inf->clearFlags(flags); } bool EoBCoreEngine::checkScriptFlags(uint32 flags) { return _inf->checkFlags(flags); } const uint8 *EoBCoreEngine::initScriptTimers(const uint8 *pos) { _scriptTimersCount = 0; while (((int16)READ_LE_UINT16(pos)) != -1) { _scriptTimers[_scriptTimersCount].func = READ_LE_UINT16(pos); pos += 2; uint16 ticks = READ_LE_UINT16(pos) * 18; _scriptTimers[_scriptTimersCount].ticks = ticks; pos += 2; _scriptTimers[_scriptTimersCount++].next = _system->getMillis() + ticks * _tickLength; } return pos; } void EoBCoreEngine::updateScriptTimers() { bool timerUpdate = false; if ((_scriptTimersMode & 2) && _stepsUntilScriptCall && _stepCounter > _stepsUntilScriptCall) { _inf->run(0, 0x20); _stepCounter = 0; timerUpdate = true; } if (_scriptTimersMode & 1) { for (int i = 0; i < _scriptTimersCount; i++) { if (_scriptTimers[i].next < _system->getMillis()) { _inf->run(_scriptTimers[i].func, _flags.gameID == GI_EOB1 ? 0x20 : 0x80); _scriptTimers[i].next = _system->getMillis() + _scriptTimers[i].ticks * _tickLength; _sceneUpdateRequired = true; timerUpdate = true; } } } if (timerUpdate) updateScriptTimersExtra(); } EoBInfProcessor::EoBInfProcessor(EoBCoreEngine *engine, Screen_EoB *screen) : _vm(engine), _screen(screen), _commandMin(engine->game() == GI_EOB1 ? -27 : -31) { #define Opcode(x) _opcodes.push_back(new InfOpcode(new InfProc(this, &EoBInfProcessor::x), #x)) #define OpcodeAlt(x) if (_vm->game() == GI_EOB1) { Opcode(x##_v1); } else { Opcode(x##_v2); } Opcode(oeob_setWallType); Opcode(oeob_toggleWallState); Opcode(oeob_openDoor); Opcode(oeob_closeDoor); Opcode(oeob_replaceMonster); Opcode(oeob_movePartyOrObject); Opcode(oeob_moveInventoryItemToBlock); OpcodeAlt(oeob_printMessage); Opcode(oeob_setFlags); Opcode(oeob_playSoundEffect); Opcode(oeob_removeFlags); Opcode(oeob_modifyCharacterHitPoints); Opcode(oeob_calcAndInflictCharacterDamage); Opcode(oeob_jump); Opcode(oeob_end); Opcode(oeob_returnFromSubroutine); Opcode(oeob_callSubroutine); OpcodeAlt(oeob_eval); Opcode(oeob_deleteItem); Opcode(oeob_loadNewLevelOrMonsters); Opcode(oeob_increasePartyExperience); OpcodeAlt(oeob_createItem); Opcode(oeob_launchObject); Opcode(oeob_changeDirection); Opcode(oeob_identifyItems); Opcode(oeob_sequence); Opcode(oeob_delay); Opcode(oeob_drawScene); Opcode(oeob_dialogue); Opcode(oeob_specialEvent); #undef Opcode #undef OpcodeAlt _scriptData = 0; _scriptSize = 0; _abortScript = 0; _abortAfterSubroutine = 0; _dlgResult = 0; _preventRest = 0; _lastScriptFunc = 0; _lastScriptFlags = 0; _subroutineStack = new int8*[10]; memset(_subroutineStack, 0, 10 * sizeof(int8 *)); _subroutineStackPos = 0; _flagTable = new uint32[18]; memset(_flagTable, 0, 18 * sizeof(uint32)); _stack = new int16[30]; memset(_stack, 0, 30 * sizeof(int16)); _stackIndex = 0; _activeCharacter = -1; } EoBInfProcessor::~EoBInfProcessor() { delete[] _subroutineStack; delete[] _flagTable; delete[] _stack; delete[] _scriptData; for (Common::Array::const_iterator a = _opcodes.begin(); a != _opcodes.end(); ++a) delete *a; _opcodes.clear(); } void EoBInfProcessor::loadData(const uint8 *data, uint32 dataSize) { delete[] _scriptData; _scriptSize = dataSize; _scriptData = new int8[_scriptSize]; memcpy(_scriptData, data, _scriptSize); } void EoBInfProcessor::run(int func, int flags) { int o = _vm->_levelBlockProperties[func].assignedObjects; if (!o) return; uint16 f = _vm->_levelBlockProperties[func].flags; uint16 subFlags = ((f & 0xFFF8) >> 3) | 0xE0; if (!(flags & subFlags)) return; _abortScript = 0; _abortAfterSubroutine = 0; _dlgResult = 0; _activeCharacter = -1; _lastScriptFunc = func; _lastScriptFlags = flags; int8 *pos = (int8 *)(_scriptData + o); do { int8 cmd = *pos++; if (cmd <= _commandMin || cmd >= 0) continue; debugC(3, kDebugLevelScript, "[0x%.04X] EoBInfProcessor::%s()", (uint32)(pos - _scriptData), _opcodes[-(cmd + 1)]->desc.c_str()); pos += (*_opcodes[-(cmd + 1)]->proc)(pos); } while (!_abortScript && !_abortAfterSubroutine); } void EoBInfProcessor::setFlags(uint32 flags) { _flagTable[17] |= flags; } void EoBInfProcessor::clearFlags(uint32 flags) { _flagTable[17] &= ~flags; } bool EoBInfProcessor::checkFlags(uint32 flags) const { return ((_flagTable[17] & flags) == flags) ? true : false; } bool EoBInfProcessor::preventRest() const { return _preventRest ? true : false; } void EoBInfProcessor::loadState(Common::SeekableSubReadStreamEndian &in, bool origFile) { _preventRest = (_vm->game() == GI_EOB1 && origFile) ? 0 : in.readByte(); int numFlags = (_vm->game() == GI_EOB1 && origFile) ? 12 : 18; for (int i = 0; i < numFlags; i++) _flagTable[i] = in.readUint32(); if (_vm->game() == GI_EOB1 && origFile) setFlags(in.readUint32()); } void EoBInfProcessor::saveState(Common::OutSaveFile *out, bool origFile) { if (_vm->game() == GI_EOB2 || !origFile) out->writeByte(_preventRest); int numFlags = (_vm->game() == GI_EOB1 && origFile) ? 12 : 18; for (int i = 0; i < numFlags; i++) { if (origFile) out->writeUint32LE(_flagTable[i]); else out->writeUint32BE(_flagTable[i]); } if (_vm->game() == GI_EOB1 && origFile) out->writeUint32LE(_flagTable[17]); } void EoBInfProcessor::reset() { _preventRest = 0; memset(_flagTable, 0, 18 * sizeof(uint32)); } const char *EoBInfProcessor::getString(uint16 index) { if (index == 0xFFFF) return 0; int8 *res = _scriptData + READ_LE_UINT16(_scriptData); while (index) { if (*res++) continue; index--; } return (const char *)res; } int EoBInfProcessor::oeob_setWallType(int8 *data) { int8 *pos = data; uint16 block = 0; int8 dir = 0; switch (*pos++) { case -23: block = READ_LE_UINT16(pos); pos += 2; dir = *pos++; _vm->_levelBlockProperties[block].walls[dir] = *pos++; _vm->checkSceneUpdateNeed(block); break; case -19: _vm->_currentDirection = *pos++; break; case -9: block = READ_LE_UINT16(pos); pos += 2; dir = *pos++; memset(_vm->_levelBlockProperties[block].walls, dir, 4 * sizeof(uint8)); _vm->checkSceneUpdateNeed(block); break; default: break; } return pos - data; } int EoBInfProcessor::oeob_toggleWallState(int8 *data) { int8 *pos = data; uint16 block = 0; int8 dir = 0; uint8 a = 0; uint8 b = 0; switch (*pos++) { case -23: block = READ_LE_UINT16(pos); pos += 2; dir = *pos++; a = (uint8)*pos++; b = (uint8)*pos++; a = (_vm->_levelBlockProperties[block].walls[dir] == a) ? b : a; _vm->_levelBlockProperties[block].walls[dir] = a; _vm->checkSceneUpdateNeed(block); break; case -22: _vm->processDoorSwitch(READ_LE_UINT16(pos), 0); pos += 2; break; case -9: block = READ_LE_UINT16(pos); pos += 2; a = (uint8)*pos++; b = (uint8)*pos++; a = (_vm->_levelBlockProperties[block].walls[dir] == a) ? b : a; memset(_vm->_levelBlockProperties[block].walls, a, 4 * sizeof(uint8)); _vm->checkSceneUpdateNeed(block); break; default: break; } return pos - data; } int EoBInfProcessor::oeob_openDoor(int8 *data) { int8 *pos = data; _vm->openDoor(READ_LE_UINT16(pos)); pos += 2; return pos - data; } int EoBInfProcessor::oeob_closeDoor(int8 *data) { int8 *pos = data; _vm->closeDoor(READ_LE_UINT16(pos)); pos += 2; return pos - data; } int EoBInfProcessor::oeob_replaceMonster(int8 *data) { int8 *pos = data; _vm->replaceMonster(pos[1], READ_LE_UINT16(pos + 2), pos[4], pos[5], pos[6], pos[7], pos[8], pos[9], READ_LE_UINT16(pos + 10), READ_LE_UINT16(pos + 12)); pos += 14; return pos - data; } int EoBInfProcessor::oeob_movePartyOrObject(int8 *data) { int8 *pos = data; int8 a = *pos++; uint16 b = 0xFFFF; uint16 c = 0; uint16 d = 0; if (_vm->game() == GI_EOB2 && a == -31) { b = READ_LE_UINT16(pos); pos += 2; } if (_vm->game() == GI_EOB1) { if (a != -15) { c = READ_LE_UINT16(pos); pos += 2; } d = READ_LE_UINT16(pos); pos += 2; } if (_vm->game() == GI_EOB2 && a != -31 && a != -11) { c = READ_LE_UINT16(pos); pos += 2; d = READ_LE_UINT16(pos); pos += 2; } if (a == -13) { // move monster from block c to block d for (int i = 0; i < 30; i++) { if (_vm->_monsters[i].block != c) continue; _vm->placeMonster(&_vm->_monsters[i], d, _vm->_monsters[i].pos); } debugC(5, kDebugLevelScript, " - move monsters on block '0x%.04X' to block '0x%.04X'", c, d); } else if (a == -24) { // move party to block d int ba = _dlgResult; int bb = _lastScriptFunc; int bc = _lastScriptFlags; int bd = _abortScript; int be = _activeCharacter; int bf = _subroutineStackPos; _vm->moveParty(d); debugC(5, kDebugLevelScript, " - move party to block '0x%.04X'", d); _dlgResult = ba; _lastScriptFunc = bb; _lastScriptFlags = bc; _abortScript = bd; _activeCharacter = be; if (!_abortAfterSubroutine) _subroutineStackPos = bf; _vm->_sceneDefaultUpdate = 0; } else if ((a == -31 && _vm->game() == GI_EOB2) || a == -11) { // move item int8 e = _vm->_currentLevel; int8 f = _vm->_currentLevel; if (_vm->game() == GI_EOB2) { e = (*pos++ == -21) ? _vm->_currentLevel : *pos++; c = READ_LE_UINT16(pos); pos += 2; f = (*pos++ == -21) ? _vm->_currentLevel : *pos++; d = READ_LE_UINT16(pos); pos += 2; } if (e == _vm->_currentLevel) { int i = _vm->countQueuedItems(_vm->_levelBlockProperties[c].drawObjects, -1, (int16)b, 0, 1); while (i) { int p = _vm->_items[i].pos; _vm->getQueuedItem((Item *)&_vm->_levelBlockProperties[c].drawObjects, 0, i); if (_vm->_currentLevel == f) { _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[d].drawObjects, d, i, p); } else { _vm->_items[i].level = f; _vm->_items[i].block = d; if (p < 8) _vm->_items[i].pos = p & 3; } i = _vm->countQueuedItems(_vm->_levelBlockProperties[c].drawObjects, -1, (int16)b, 0, 1); } for (i = 0; i < 10; i++) { if (_vm->_flyingObjects[i].enable != 1 || _vm->_flyingObjects[i].curBlock != c) continue; if (f == _vm->_currentLevel || _vm->game() == GI_EOB1) _vm->_flyingObjects[i].curBlock = d; else _vm->_flyingObjects[i].enable = 0; } } else { for (int i = 0; i < 600; i++) { if (_vm->_items[i].level != e || _vm->_items[i].block != c) continue; _vm->_items[i].level = f; _vm->_items[i].block = d; } } debugC(5, kDebugLevelScript, " - move items from level '%d', block '0x%.04X' to level '%d', block '0x%.04X'", c, e, d, f); } _vm->_sceneUpdateRequired = true; return pos - data; } int EoBInfProcessor::oeob_moveInventoryItemToBlock(int8 *data) { int8 *pos = data; int8 c = *pos++; uint16 block = READ_LE_UINT16(pos); pos += 2; int8 p = *pos++; if (c == -1) c = _vm->rollDice(1, 6, -1); while (!(_vm->_characters[c].flags & 1)) { if (++c == 5) c = 0; } if (_vm->_currentControlMode && (_vm->_updateCharNum == c)) return pos - data; int slot = _vm->rollDice(1, 27, 0); int itm = 0; int i = 0; for (; i < 27; i++) { if ((!_vm->_currentControlMode && slot > 1) || slot == 16) continue; itm = _vm->_characters[c].inventory[slot]; if (!itm) continue; if (_vm->_dscItemShapeMap[_vm->_items[itm].icon] >= 15) break; if (++slot == 27) slot = 0; } if (i < 27 && itm) { _vm->_characters[c].inventory[slot] = 0; _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block].drawObjects, block, itm, p); } return pos - data; } int EoBInfProcessor::oeob_printMessage_v1(int8 *data) { static const char colorConfig[] = "\x6\x21\x2\x21"; char col[5]; int8 *pos = data; strcpy(col, colorConfig); const char *str = (const char *)pos; pos += (strlen(str) + 1); col[1] = *pos++; col[3] = *pos++; _vm->txt()->printMessage(col); _vm->txt()->printMessage(str); col[1] = _screen->_curDim->unk8; col[3] = _screen->_curDim->unkA; _vm->txt()->printMessage(col); _vm->txt()->printMessage("\r"); return pos - data; } int EoBInfProcessor::oeob_printMessage_v2(int8 *data) { int8 *pos = data; uint16 str = READ_LE_UINT16(pos); pos += 2; uint8 col = (uint8)*pos; pos += 2; int c = 0; if (_activeCharacter == -1) { c = _vm->rollDice(1, 6, -1); while (!_vm->testCharacter(c, 3)) c = (c + 1) % 6; } else { c = _activeCharacter; } _vm->txt()->printMessage(getString(str), col, _vm->_characters[c].name); _vm->txt()->printMessage("\r"); return pos - data; } int EoBInfProcessor::oeob_setFlags(int8 *data) { int8 *pos = data; int8 b = 0; switch (*pos++) { case -47: _preventRest = 0; debugC(5, kDebugLevelScript, " - set preventRest to 0"); break; case -28: _dlgResult = 1; debugC(5, kDebugLevelScript, " - set dlgResult to 1"); break; case -17: _flagTable[_vm->_currentLevel] |= (1 << (*pos++)); debugC(5, kDebugLevelScript, " - set level flag '%d' for current level (current level = '%d')", *(pos - 1), _vm->_currentLevel); break; case -16: _flagTable[17] |= (1 << (*pos++)); debugC(5, kDebugLevelScript, " - set global flag '%d'", *(pos - 1)); break; case -13: b = *pos++; _vm->_monsters[b].flags |= (1 << (*pos++)); _vm->_monsters[b].mode = 0; debugC(5, kDebugLevelScript, " - set monster flag '%d' for monster '%d'", *(pos - 1), b); break; default: break; } return pos - data; } int EoBInfProcessor::oeob_playSoundEffect(int8 *data) { int8 *pos = data; uint16 block = READ_LE_UINT16(pos + 1); if (block) { _vm->snd_processEnvironmentalSoundEffect(pos[0], block); } else { _vm->snd_playSoundEffect(pos[0]); } pos += 3; return pos - data; } int EoBInfProcessor::oeob_removeFlags(int8 *data) { int8 *pos = data; int8 a = *pos++; switch (a) { case -47: _preventRest = 1; debugC(5, kDebugLevelScript, " - set preventRest to 1"); break; case -28: _dlgResult = 0; debugC(5, kDebugLevelScript, " - set dlgResult to 0"); break; case -17: _flagTable[_vm->_currentLevel] &= ~(1 << (*pos++)); debugC(5, kDebugLevelScript, " - clear level flag '%d' for current level (current level = '%d')", *(pos - 1), _vm->_currentLevel); break; case -16: _flagTable[17] &= ~(1 << (*pos++)); debugC(5, kDebugLevelScript, " - clear global flag '%d'", *(pos - 1)); break; default: break; } return pos - data; } int EoBInfProcessor::oeob_modifyCharacterHitPoints(int8 *data) { int8 *pos = data; int8 c = *pos++; int8 p = *pos++; if (c == -1) { for (c = 0; c < 6; c++) _vm->modifyCharacterHitpoints(c, p); } else { _vm->modifyCharacterHitpoints(c, p); } return pos - data; } int EoBInfProcessor::oeob_calcAndInflictCharacterDamage(int8 *data) { int8 *pos = data; int charIndex = *pos++; int times = *pos++; int itemOrPips = *pos++; int useStrModifierOrBase = *pos++; int flg = (charIndex == -1) ? 4 : 0; int savingThrowType = 5; int savingThrowEffect = 1; if (_vm->game() == GI_EOB2) { flg = *pos++; savingThrowType = *pos++; savingThrowEffect = *pos++; } else if (!itemOrPips) { useStrModifierOrBase = times; times = 0; } if (charIndex == -1) { for (int i = 0; i < 6; i++) _vm->calcAndInflictCharacterDamage(i, times, itemOrPips, useStrModifierOrBase, flg, savingThrowType, savingThrowEffect); } else { _vm->calcAndInflictCharacterDamage(charIndex, times, itemOrPips, useStrModifierOrBase, flg, savingThrowType, savingThrowEffect); } return pos - data; } int EoBInfProcessor::oeob_jump(int8 *data) { int8 *pos = data; pos = _scriptData + READ_LE_UINT16(pos); return pos - data; } int EoBInfProcessor::oeob_end(int8 *data) { _abortScript = 1; _subroutineStackPos = 0; return 0; } int EoBInfProcessor::oeob_returnFromSubroutine(int8 *data) { int8 *pos = data; if (_subroutineStackPos) pos = _subroutineStack[--_subroutineStackPos]; else _abortScript = 1; return pos - data; } int EoBInfProcessor::oeob_callSubroutine(int8 *data) { int8 *pos = data; uint16 offs = READ_LE_UINT16(pos); assert(offs < _scriptSize); pos += 2; if (_subroutineStackPos < 10) { _subroutineStack[_subroutineStackPos++] = pos; pos = _scriptData + offs; } return pos - data; } int EoBInfProcessor::oeob_eval_v1(int8 *data) { int8 *pos = data; int8 cmd = *pos++; int a = 0; int b = 0; int i = 0; EoBItem *itm = &_vm->_items[_vm->_itemInHand]; Common::String tempString1; Common::String tempString2; while (cmd != -18) { switch (cmd + 38) { case 0: a = 1; for (i = 0; i < 6; i++) { if (!(_vm->_characters[i].flags & 1)) continue; if (_vm->_characters[i].effectFlags & 0x40) continue; a = 0; break; } _stack[_stackIndex++] = a; debugC(5, kDebugLevelScript, " - check if whole party is invisible - PUSH result: '%d'", a); break; case 1: _stack[_stackIndex++] = _vm->rollDice(pos[0], pos[1], pos[2]); debugC(9, kDebugLevelScript, " - throw dice(s): num = '%d', pips = '%d', offset = '%d' - PUSH result: '%d'", pos[0], pos[1], pos[2], _stack[_stackIndex - 1]); pos += 3; break; case 2: cmd = *pos++; b = 0; for (i = 0; i < 6; i++) { if (!(_vm->_characters[i].flags & 1)) continue; if (_vm->_classModifierFlags[_vm->_characters[i].cClass] & cmd) { b = 1; break; } } _stack[_stackIndex++] = b; debugC(5, kDebugLevelScript, " - check if character with class flags '0x%.02X' is present - PUSH result: '%d'", cmd, b); break; case 3: cmd = *pos++; b = 0; for (i = 0; i < 6; i++) { if (!(_vm->_characters[i].flags & 1)) continue; if ((_vm->_characters[i].raceSex >> 1) == cmd) { b = 1; break; } } _stack[_stackIndex++] = b; debugC(5, kDebugLevelScript, " - check if character with race '%d' is present - PUSH result: '%d'", cmd, b); break; case 6: _stack[_stackIndex++] = _lastScriptFlags; debugC(5, kDebugLevelScript, " - get script execution flags - PUSH result: '%d'", _lastScriptFlags); break; case 13: itm = &_vm->_items[_vm->_itemInHand]; switch (*pos++) { case -31: _stack[_stackIndex++] = itm->type; debugC(5, kDebugLevelScript, " - get hand item type (hand item number = '%d') - PUSH result: '%d'", _vm->_itemInHand, itm->type); break; case -11: _stack[_stackIndex++] = _vm->_itemInHand; debugC(5, kDebugLevelScript, " - get hand item number - PUSH result: '%d'", _vm->_itemInHand); break; default: _stack[_stackIndex++] = itm->value; debugC(5, kDebugLevelScript, " - get hand item value (hand item number = '%d') - PUSH result: '%d'", _vm->_itemInHand, itm->value); break; } break; case 15: _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos + 1)].walls[pos[0]]; debugC(5, kDebugLevelScript, " - get wall index for block '0x%.04X', direction '%d' - PUSH result: '%d'", READ_LE_UINT16(pos + 1), pos[0], _stack[_stackIndex - 1]); pos += 3; break; case 19: _stack[_stackIndex++] = _vm->_currentDirection; debugC(5, kDebugLevelScript, " - get current direction - PUSH result: '%d'", _vm->_currentDirection); break; case 21: _stack[_stackIndex++] = (_flagTable[_vm->_currentLevel] & (1 << (*pos++))) ? 1 : 0; debugC(5, kDebugLevelScript, " - test level flag '%d' (current level = '%d') - PUSH result: '%d'", *(pos - 1), _vm->_currentLevel, _stack[_stackIndex - 1]); break; case 22: _stack[_stackIndex++] = (_flagTable[17] & (1 << (*pos++))) ? 1 : 0; debugC(5, kDebugLevelScript, " - test global flag '%d' - PUSH result: '%d'", *(pos - 1), _stack[_stackIndex - 1]); break; case 23: _stack[_stackIndex++] = (_vm->_currentBlock == READ_LE_UINT16(pos)) ? 1 : 0; debugC(5, kDebugLevelScript, " - compare current block with block '0x%.04X' (current block = '0x%.04X') - PUSH result: '%d'", _vm->_currentBlock, READ_LE_UINT16(pos), _stack[_stackIndex - 1]); pos += 2; break; case 24: a = (int16)READ_LE_UINT16(pos); pos += 2; b = READ_LE_UINT16(pos); pos += 2; _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[b].drawObjects, a, -1, 0, 1); debugC(5, kDebugLevelScript, " - find item number '%d' on block '0x%.04X' - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 25: _stack[_stackIndex++] = (_vm->_levelBlockProperties[READ_LE_UINT16(pos)].flags & 1) ? 1 : 0; debugC(5, kDebugLevelScript, " - test block flag '1' for block '0x%.04X' - PUSH result: '%d'", READ_LE_UINT16(pos), _stack[_stackIndex - 1]); pos += 2; break; case 27: b = *pos++; i = READ_LE_UINT16(pos); pos += 2; _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[i].drawObjects, -1, b, 1, 1); debugC(5, kDebugLevelScript, " - count items of type '%d' on block '0x%.04X' - PUSH result: '%d'", b, i, _stack[_stackIndex - 1]); break; case 29: _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].walls[0]; debugC(5, kDebugLevelScript, " - get wall index 0 for block '0x%.04X' - PUSH result: '%d'", READ_LE_UINT16(pos), _stack[_stackIndex - 1]); pos += 2; break; case 30: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a || b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') || POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 31: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a && b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') && POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 32: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a <= b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') <= POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 33: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a < b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') < POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 34: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a >= b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') >= POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 35: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a > b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') > POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 36: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a != b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') != POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; case 37: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a == b) ? 1 : 0; debugC(5, kDebugLevelScript, " - evaluate: POP('%d') == POP('%d') - PUSH result: '%d'", a, b, _stack[_stackIndex - 1]); break; default: a = cmd; if (a >= 0 && a < 128) _stack[_stackIndex++] = a; debugC(5, kDebugLevelScript, " - PUSH value: '%d'", a); break; } cmd = *pos++; } cmd = _stack[--_stackIndex]; if (cmd) pos += 2; else pos = _scriptData + READ_LE_UINT16(pos); debugC(5, kDebugLevelScript, " - conditional jump depending on POP('%d')", cmd); return pos - data; } int EoBInfProcessor::oeob_eval_v2(int8 *data) { int8 *pos = data; int8 cmd = *pos++; int a = 0; int b = 0; int i = 0; EoBItem *itm = (_vm->_itemInHand != -1) ? &_vm->_items[_vm->_itemInHand] : 0; Common::String tempString1; Common::String tempString2; while (cmd != -18) { switch (cmd + 50) { case 0: a = 0; b = *pos++; for (i = 0; i < 6; i++) { if (!_vm->testCharacter(i, 5)) continue; if (_vm->_characters[i].portrait != b) { a = 1; _activeCharacter = i; break; } } _stack[_stackIndex++] = a; break; case 4: _stack[_stackIndex++] = (int16)READ_LE_UINT16(pos); pos += 2; break; case 9: switch (*pos++) { case -36: _stack[_stackIndex++] = _vm->_itemTypes[_vm->_items[_vm->_lastUsedItem].type].extraProperties & 0x7F; break; case -31: _stack[_stackIndex++] = _vm->_items[_vm->_lastUsedItem].type; break; case -11: _stack[_stackIndex++] = _vm->_lastUsedItem; break; case -10: _stack[_stackIndex++] = _vm->_items[_vm->_lastUsedItem].value; break; default: break; } break; case 12: a = 1; for (i = 0; i < 6; i++) { if (!(_vm->_characters[i].flags & 1)) continue; if (_vm->_characters[i].effectFlags & 0x40) continue; a = 0; break; } _stack[_stackIndex++] = a; break; case 13: _stack[_stackIndex++] = _vm->rollDice(pos[0], pos[1], pos[2]); pos += 3; break; case 14: cmd = *pos++; a = _vm->rollDice(1, 6); b = 0; for (i = 0; i < 6 && b == 0; i++) { if (++a > 5) a = 0; if (_vm->testCharacter(a, 5)) { if (_vm->_classModifierFlags[_vm->_characters[a].cClass] & cmd) { _activeCharacter = a; b = 1; } } } _stack[_stackIndex++] = b; break; case 15: cmd = *pos++; a = _vm->rollDice(1, 6); b = 0; for (i = 0; i < 6; i++) { if (++a > 5) a = 0; if (_vm->testCharacter(a, 5)) { if ((_vm->_characters[a].raceSex >> 1) == cmd) { _activeCharacter = a; b = 1; } } } _stack[_stackIndex++] = b; break; case 17: _stack[_stackIndex++] = _vm->_activeSpell; break; case 18: _stack[_stackIndex++] = _lastScriptFlags; break; case 22: _stack[_stackIndex++] = _dlgResult; break; case 25: itm = &_vm->_items[_vm->_itemInHand]; switch (*pos++) { case -49: a = *pos++; tempString1 = _vm->_itemNames[itm->nameId]; tempString1.toUppercase(); tempString2 = (const char *)pos; tempString2.toUppercase(); pos += a; _stack[_stackIndex++] = tempString1.contains(tempString2) ? 1 : 0; break; case -48: a = *pos++; tempString1 = _vm->_itemNames[itm->nameUnid]; tempString1.toUppercase(); tempString2 = (const char *)pos; tempString2.toUppercase(); pos += a; _stack[_stackIndex++] = tempString1.contains(tempString2) ? 1 : 0; break; case -31: _stack[_stackIndex++] = itm->type; break; case -11: _stack[_stackIndex++] = _vm->_itemInHand; break; case -10: _stack[_stackIndex++] = itm->value; break; default: break; } break; case 26: a = 0; for (i = 0; i < 6; i++) { if (_vm->testCharacter(i, 0x0F)) a++; } _stack[_stackIndex++] = a; break; case 27: _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos + 1)].walls[pos[0]]; pos += 3; break; case 31: _stack[_stackIndex++] = _vm->_currentDirection; break; case 33: _stack[_stackIndex++] = (_flagTable[_vm->_currentLevel] & (1 << (*pos++))) ? 1 : 0; break; case 34: _stack[_stackIndex++] = (_flagTable[17] & (1 << (*pos++))) ? 1 : 0; break; case 35: if (*pos++ == -11) { a = (int16)READ_LE_UINT16(pos); pos += 2; b = (int16)READ_LE_UINT16(pos); pos += 2; _stack[_stackIndex++] = _vm->countCharactersWithSpecificItems(a, b); } else { _stack[_stackIndex++] = (_vm->_currentBlock == READ_LE_UINT16(pos)) ? 1 : 0; pos += 2; } break; case 36: a = (int16)READ_LE_UINT16(pos); pos += 2; b = READ_LE_UINT16(pos); pos += 2; _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[b].drawObjects, a, -1, 0, 0); break; case 37: if (*pos++ == -1) { _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].flags & 7; pos += 2; } else { do { a += _vm->countSpecificMonsters(*pos++); } while (*pos != -1); pos++; _stack[_stackIndex++] = a; } break; case 39: a = *pos++; b = *pos++; i = READ_LE_UINT16(pos); pos += 2; _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[i].drawObjects, -1, b, 1, a); break; case 41: _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].walls[0]; pos += 2; break; case 42: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a || b) ? 1 : 0; break; case 43: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a && b) ? 1 : 0; break; case 44: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a <= b) ? 1 : 0; break; case 45: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a < b) ? 1 : 0; break; case 46: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a >= b) ? 1 : 0; break; case 47: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a > b) ? 1 : 0; break; case 48: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a != b) ? 1 : 0; break; case 49: a = _stack[--_stackIndex]; b = _stack[--_stackIndex]; _stack[_stackIndex++] = (a == b) ? 1 : 0; break; default: break; } cmd = *pos++; } cmd = _stack[--_stackIndex]; if (cmd) pos += 2; else pos = _scriptData + READ_LE_UINT16(pos); return pos - data; } int EoBInfProcessor::oeob_deleteItem(int8 *data) { int8 *pos = data; int8 c = *pos++; if (c == -1) { _vm->deleteInventoryItem(0, -1); debugC(5, kDebugLevelScript, " - delete hand item"); } else { _vm->deleteBlockItem(READ_LE_UINT16(pos), (c == -2) ? -1 : c); debugC(5, kDebugLevelScript, " - delete item(s) of type '%d' on block '0x%.04X'", (c == -2) ? -1 : c, READ_LE_UINT16(pos)); pos += 2; } return pos - data; } int EoBInfProcessor::oeob_loadNewLevelOrMonsters(int8 *data) { int8 *pos = data; _vm->gui_updateControls(); int8 cmd = *pos++; int8 index = *pos++; int res = 0; if (cmd == -27 || _vm->game() == GI_EOB1) { cmd = _vm->game() == GI_EOB2 ? *pos++ : 0; _vm->_currentBlock = READ_LE_UINT16(pos); pos += 2; uint8 dir = (uint8)*pos++; if (dir != 0xFF) _vm->_currentDirection = dir; for (int i = 0; i < 30; i++) _vm->_monsters[i].curAttackFrame = 0; for (int i = 0; i < 10; i++) { EoBFlyingObject *fo = &_vm->_flyingObjects[i]; if (fo->enable == 1) { _vm->_items[fo->item].pos &= 3; run(_vm->_items[fo->item].block, 4); } fo->enable = 0; } _vm->completeDoorOperations(); _vm->generateTempData(); _vm->txt()->removePageBreakFlag(); _screen->setScreenDim(7); _vm->loadLevel(index, cmd); debugC(5, kDebugLevelScript, " - entering level '%d', sub level '%d', start block '0x%.04X', start direction '%d'", index, cmd, _vm->_currentBlock, _vm->_currentDirection); if (_vm->_dialogueField) _vm->restoreAfterDialogueSequence(); _vm->moveParty(_vm->_currentBlock); _abortScript = 1; _abortAfterSubroutine = 1; _vm->_sceneUpdateRequired = true; _vm->gui_drawAllCharPortraitsWithStats(); _subroutineStackPos = 0; } else { cmd = *pos++; _vm->releaseMonsterShapes(cmd * 18, 18); _vm->loadMonsterShapes((const char *)pos, cmd * 18, true, index * 18); debugC(5, kDebugLevelScript, " - loading monster shapes '%s', monster number '%d', encode type '%d'", (const char *)pos, cmd, index); pos += 13; _vm->gui_restorePlayField(); res = pos - data; } return res; } int EoBInfProcessor::oeob_increasePartyExperience(int8 *data) { int8 *pos = data; if (*pos++ == -30) { _vm->increasePartyExperience((int16)READ_LE_UINT16(pos)); debugC(5, kDebugLevelScript, " - award '%d' experience points", READ_LE_UINT16(pos)); pos += 2; } return pos - data; } int EoBInfProcessor::oeob_createItem_v1(int8 *data) { int8 *pos = data; uint16 itm = _vm->duplicateItem(READ_LE_UINT16(pos)); pos += 2; uint16 block = READ_LE_UINT16(pos); pos += 2; uint8 itmPos = *pos++; if (itm) { if (block == 0xFFFF && !_vm->_itemInHand) { _vm->setHandItem(itm); debugC(5, kDebugLevelScript, " - create hand item '%d'", itm); } else if (block != 0xFFFF) { _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block & 0x3FF].drawObjects, block, itm, itmPos); debugC(5, kDebugLevelScript, " - create item '%d' on block '0x%.04X', position '%d'", itm, block, itmPos); } } return pos - data; } int EoBInfProcessor::oeob_createItem_v2(int8 *data) { static const uint8 _itemPos[] = { 0, 1, 2, 3, 1, 3, 0, 2, 3, 2, 1, 0, 2, 0, 3, 1 }; int8 *pos = data; uint16 itm = _vm->duplicateItem(READ_LE_UINT16(pos)); pos += 2; uint16 block = READ_LE_UINT16(pos); pos += 2; uint8 itmPos = *pos++; uint8 flg = *pos++; if (flg & 1) _vm->_items[itm].value = *pos++; if (flg & 2) _vm->_items[itm].flags = *pos++; if (flg & 4) _vm->_items[itm].icon = *pos++; if (!itm) return pos - data; if (block == 0xFFFF) { if (!_vm->_itemInHand) { _vm->setHandItem(itm); debugC(5, kDebugLevelScript, " - create hand item '%d' (value '%d', flags '0x%X', icon number '%d')", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon); } else { _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3FF].drawObjects, _vm->_currentBlock, itm, _itemPos[_vm->rollDice(1, 2, -1)]); debugC(5, kDebugLevelScript, " - create item '%d' (value '%d', flags '0x%X', icon number '%d') on current block", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon); } } else if (block == 0xFFFE) { _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3FF].drawObjects, _vm->_currentBlock, itm, _itemPos[(_vm->_currentDirection << 2) + _vm->rollDice(1, 2, -1)]); debugC(5, kDebugLevelScript, " - create item '%d' (value '%d', flags '0x%X', icon number '%d') on current block", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon); } else { _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block & 0x3FF].drawObjects, block, itm, itmPos); debugC(5, kDebugLevelScript, " - create item '%d' (value '%d', flags '0x%X', icon number '%d') on block '0x%.04X', position '%d'", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon, block, itmPos); } return pos - data; } int EoBInfProcessor::oeob_launchObject(int8 *data) { static const uint8 startPos[] = { 2, 3, 0, 2, 1, 0, 3, 1 }; int8 *pos = data; bool m = (*pos++ == -33); int i = READ_LE_UINT16(pos); pos += 2; uint16 block = READ_LE_UINT16(pos); pos += 2; int dir = *pos++; int dirOffs = *pos++; if (m) { uint8 openBookType = _vm->_openBookType; _vm->_openBookType = 0; _vm->launchMagicObject(-1, i, block, startPos[dir * 2 + dirOffs], dir); _vm->_openBookType = openBookType; } else { Item itm = _vm->duplicateItem(i); if (itm) { if (!_vm->launchObject(-1, itm, block, startPos[dir * 2 + dirOffs], dir, _vm->_items[itm].type)) _vm->_items[itm].block = -1; } } return pos - data; } int EoBInfProcessor::oeob_changeDirection(int8 *data) { int8 *pos = data; int8 cmd = *pos++; int8 dir = *pos++; if (cmd == -15) { _vm->_currentDirection = (_vm->_currentDirection + dir) & 3; //_vm->_keybControlUnk = -1; _vm->_sceneUpdateRequired = true; } else if (cmd == -11) { for (int i = 0; i < 10; i++) { if (_vm->_flyingObjects[i].enable) _vm->_flyingObjects[i].direction = (_vm->_flyingObjects[i].direction + dir) & 3; } } return pos - data; } int EoBInfProcessor::oeob_identifyItems(int8 *data) { int8 *pos = data; uint16 block = READ_LE_UINT16(pos); if (block == _vm->_currentBlock) { for (int i = 0; i < 6; i++) { if (!(_vm->_characters[i].flags & 1)) continue; for (int ii = 0; ii < 27; ii++) { int inv = _vm->_characters[i].inventory[ii]; if (inv) _vm->_items[inv].flags |= 0x40; } _vm->identifyQueuedItems(_vm->_characters[i].inventory[16]); } } _vm->identifyQueuedItems(_vm->_levelBlockProperties[block].drawObjects); return pos - data; } int EoBInfProcessor::oeob_sequence(int8 *data) { int8 *pos = data; _vm->_npcSequenceSub = -1; _vm->txt()->setWaitButtonMode(0); _vm->gui_updateControls(); _vm->drawScene(1); int cmd = *pos++; if (_vm->game() == GI_EOB1) { if (cmd == 10) cmd = -1; else if (cmd == 9) cmd = -3; else if (cmd == 8) cmd = -2; } switch (cmd) { case -3: _vm->seq_xdeath(); _vm->_runFlag = false; _vm->_playFinale = true; _abortScript = 1; return 0; case -2: _vm->seq_portal(); break; case -1: _vm->_runFlag = _vm->checkPassword(); break; default: _vm->npcSequence(cmd); break; } _vm->screen()->setScreenDim(7); return pos - data; } int EoBInfProcessor::oeob_delay(int8 *data) { int8 *pos = data; _vm->delay(READ_LE_UINT16(pos) * _vm->tickLength()); pos += 2; return pos - data; } int EoBInfProcessor::oeob_drawScene(int8 *data) { _vm->drawScene(1); return 0; } int EoBInfProcessor::oeob_dialogue(int8 *data) { int8 *pos = data; switch (*pos++) { case -45: _vm->drawSequenceBitmap((const char *)pos, pos[13], READ_LE_UINT16(pos + 14), READ_LE_UINT16(pos + 16), READ_LE_UINT16(pos + 18)); pos += 20; break; case -44: _vm->restoreAfterDialogueSequence(); break; case -43: _vm->initDialogueSequence(); break; case -42: _vm->gui_drawDialogueBox(); break; case -40: _dlgResult = _vm->runDialogue(READ_LE_UINT16(pos), READ_LE_UINT16(pos + 6) == 0xFFFF ? 2 : 3, getString(READ_LE_UINT16(pos + 2)), getString(READ_LE_UINT16(pos + 4)), getString(READ_LE_UINT16(pos + 6))); pos += 8; break; case -8: _vm->txt()->printDialogueText(READ_LE_UINT16(pos), getString(READ_LE_UINT16(pos + 2))); pos += 4; break; default: break; } return pos - data; } int EoBInfProcessor::oeob_specialEvent(int8 *data) { int8 *pos = data; uint16 cmd = READ_LE_UINT16(pos); pos += 2; uint32 endTime = 0; int i = 0; switch (cmd) { case 0: _vm->drawScene(1); _screen->_curPage = 2; _screen->copyRegion(72, 0, 0, 0, 32, 120, 2, 12, Screen::CR_NO_P_CHECK); for (; i < 4; i++) { endTime = _vm->_system->getMillis() + _vm->_tickLength; _vm->drawLightningColumn(); _screen->copyRegion(72, 0, 72, 0, 32, 120, 2, 0, Screen::CR_NO_P_CHECK); _screen->updateScreen(); _screen->copyRegion(0, 0, 72, 0, 32, 120, 12, 2, Screen::CR_NO_P_CHECK); _vm->delayUntil(endTime); } _screen->_curPage = 0; _vm->_sceneUpdateRequired = true; break; case 1: _dlgResult = _vm->charSelectDialogue(); break; case 2: _vm->characterLevelGain(_dlgResult); break; case 3: _dlgResult = _vm->resurrectionSelectDialogue(); break; case 4: if (_vm->prepareForNewPartyMember(33, 5)) _vm->initNpc(4); break; case 5: _vm->deletePartyItems(46, 5); _vm->deletePartyItems(46, 6); break; case 6: _vm->loadVcnData(0, 0); break; default: break; } return pos - data; } } // End of namespace Kyra #endif // ENABLE_EOB