diff options
Diffstat (limited to 'engines/kyra/script')
-rw-r--r-- | engines/kyra/script/script.cpp | 445 | ||||
-rw-r--r-- | engines/kyra/script/script.h | 159 | ||||
-rw-r--r-- | engines/kyra/script/script_eob.cpp | 1622 | ||||
-rw-r--r-- | engines/kyra/script/script_eob.h | 131 | ||||
-rw-r--r-- | engines/kyra/script/script_hof.cpp | 1727 | ||||
-rw-r--r-- | engines/kyra/script/script_lok.cpp | 1962 | ||||
-rw-r--r-- | engines/kyra/script/script_lol.cpp | 3051 | ||||
-rw-r--r-- | engines/kyra/script/script_mr.cpp | 1377 | ||||
-rw-r--r-- | engines/kyra/script/script_tim.cpp | 1101 | ||||
-rw-r--r-- | engines/kyra/script/script_tim.h | 306 | ||||
-rw-r--r-- | engines/kyra/script/script_v1.cpp | 125 | ||||
-rw-r--r-- | engines/kyra/script/script_v2.cpp | 342 |
12 files changed, 12348 insertions, 0 deletions
diff --git a/engines/kyra/script/script.cpp b/engines/kyra/script/script.cpp new file mode 100644 index 0000000000..27c9643034 --- /dev/null +++ b/engines/kyra/script/script.cpp @@ -0,0 +1,445 @@ +/* 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 "kyra/script/script.h" +#include "kyra/kyra_v1.h" +#include "kyra/resource/resource.h" + +#include "common/endian.h" + +namespace Kyra { +EMCInterpreter::EMCInterpreter(KyraEngine_v1 *vm) : _vm(vm), _scriptData(0), _filename(0) { +#define OPCODE(x) { &EMCInterpreter::x, #x } + static const OpcodeEntry opcodes[] = { + // 0x00 + OPCODE(op_jmp), + OPCODE(op_setRetValue), + OPCODE(op_pushRetOrPos), + OPCODE(op_push), + // 0x04 + OPCODE(op_push), + OPCODE(op_pushReg), + OPCODE(op_pushBPNeg), + OPCODE(op_pushBPAdd), + // 0x08 + OPCODE(op_popRetOrPos), + OPCODE(op_popReg), + OPCODE(op_popBPNeg), + OPCODE(op_popBPAdd), + // 0x0C + OPCODE(op_addSP), + OPCODE(op_subSP), + OPCODE(op_sysCall), + OPCODE(op_ifNotJmp), + // 0x10 + OPCODE(op_negate), + OPCODE(op_eval), + OPCODE(op_setRetAndJmp) + }; + _opcodes = opcodes; +#undef OPCODE +} + +bool EMCInterpreter::callback(Common::IFFChunk &chunk) { + switch (chunk._type) { + case MKTAG('T','E','X','T'): + _scriptData->text = new byte[chunk._size]; + assert(_scriptData->text); + if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size) + error("Couldn't read TEXT chunk from file '%s'", _filename); + break; + + case MKTAG('O','R','D','R'): + _scriptData->ordr = new uint16[chunk._size >> 1]; + assert(_scriptData->ordr); + if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size) + error("Couldn't read ORDR chunk from file '%s'", _filename); + + for (int i = (chunk._size >> 1) - 1; i >= 0; --i) + _scriptData->ordr[i] = READ_BE_UINT16(&_scriptData->ordr[i]); + break; + + case MKTAG('D','A','T','A'): + _scriptData->data = new uint16[chunk._size >> 1]; + assert(_scriptData->data); + if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size) + error("Couldn't read DATA chunk from file '%s'", _filename); + + for (int i = (chunk._size >> 1) - 1; i >= 0; --i) + _scriptData->data[i] = READ_BE_UINT16(&_scriptData->data[i]); + break; + + default: + warning("Unexpected chunk '%s' of size %d found in file '%s'", tag2str(chunk._type), chunk._size, _filename); + } + + return false; +} + +bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const Opcode *> *opcodes) { + Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename); + if (!stream) { + error("Couldn't open script file '%s'", filename); + return false; // for compilers that don't support NORETURN + } + + memset(scriptData, 0, sizeof(EMCData)); + + _scriptData = scriptData; + _filename = filename; + + IFFParser iff(*stream); + Common::Functor1Mem< Common::IFFChunk &, bool, EMCInterpreter > c(this, &EMCInterpreter::callback); + iff.parse(c); + + if (!_scriptData->ordr) + error("No ORDR chunk found in file: '%s'", filename); + + if (!_scriptData->data) + error("No DATA chunk found in file: '%s'", filename); + + if (stream->err()) + error("Read error while parsing file '%s'", filename); + + delete stream; + + _scriptData->sysFuncs = opcodes; + + Common::strlcpy(_scriptData->filename, filename, 13); + + _scriptData = 0; + _filename = 0; + + return true; +} + +void EMCInterpreter::unload(EMCData *data) { + if (!data) + return; + + delete[] data->text; + delete[] data->ordr; + delete[] data->data; + + data->text = 0; + data->ordr = data->data = 0; +} + +void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) { + scriptStat->dataPtr = data; + scriptStat->ip = 0; + scriptStat->stack[EMCState::kStackLastEntry] = 0; + scriptStat->bp = EMCState::kStackSize+1; + scriptStat->sp = EMCState::kStackLastEntry; +} + +bool EMCInterpreter::start(EMCState *script, int function) { + if (!script->dataPtr) + return false; + + uint16 functionOffset = script->dataPtr->ordr[function]; + if (functionOffset == 0xFFFF) + return false; + + if (_vm->game() == GI_KYRA1) { + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + script->ip = &script->dataPtr->data[functionOffset+1]; + else + script->ip = &script->dataPtr->data[functionOffset]; + } else { + script->ip = &script->dataPtr->data[functionOffset+1]; + } + + return true; +} + +bool EMCInterpreter::isValid(EMCState *script) { + if (!script->ip || !script->dataPtr || _vm->shouldQuit()) + return false; + return true; +} + +bool EMCInterpreter::run(EMCState *script) { + _parameter = 0; + + if (!script->ip) + return false; + + // Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original + // would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway. + const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data); + int16 code = *script->ip++; + int16 opcode = (code >> 8) & 0x1F; + + if (code & 0x8000) { + opcode = 0; + _parameter = code & 0x7FFF; + } else if (code & 0x4000) { + _parameter = (int8)(code); + } else if (code & 0x2000) { + _parameter = *script->ip++; + } else { + _parameter = 0; + } + + if (opcode > 18) { + error("Unknown script opcode: %d in file '%s' at offset 0x%.08X", opcode, script->dataPtr->filename, instOffset); + } else { + debugC(5, kDebugLevelScript, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter); + (this->*(_opcodes[opcode].proc))(script); + } + + return (script->ip != 0); +} + +#pragma mark - +#pragma mark - Command implementations +#pragma mark - + +void EMCInterpreter::op_jmp(EMCState *script) { + script->ip = script->dataPtr->data + _parameter; +} + +void EMCInterpreter::op_setRetValue(EMCState *script) { + script->retValue = _parameter; +} + +void EMCInterpreter::op_pushRetOrPos(EMCState *script) { + switch (_parameter) { + case 0: + script->stack[--script->sp] = script->retValue; + break; + + case 1: + script->stack[--script->sp] = script->ip - script->dataPtr->data + 1; + script->stack[--script->sp] = script->bp; + script->bp = script->sp + 2; + break; + + default: + script->ip = 0; + } +} + +void EMCInterpreter::op_push(EMCState *script) { + script->stack[--script->sp] = _parameter; +} + +void EMCInterpreter::op_pushReg(EMCState *script) { + script->stack[--script->sp] = script->regs[_parameter]; +} + +void EMCInterpreter::op_pushBPNeg(EMCState *script) { + script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp]; +} + +void EMCInterpreter::op_pushBPAdd(EMCState *script) { + script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp]; +} + +void EMCInterpreter::op_popRetOrPos(EMCState *script) { + switch (_parameter) { + case 0: + script->retValue = script->stack[script->sp++]; + break; + + case 1: + if (script->sp >= EMCState::kStackLastEntry) { + script->ip = 0; + } else { + script->bp = script->stack[script->sp++]; + script->ip = script->dataPtr->data + script->stack[script->sp++]; + } + break; + + default: + script->ip = 0; + } +} + +void EMCInterpreter::op_popReg(EMCState *script) { + script->regs[_parameter] = script->stack[script->sp++]; +} + +void EMCInterpreter::op_popBPNeg(EMCState *script) { + script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++]; +} + +void EMCInterpreter::op_popBPAdd(EMCState *script) { + script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++]; +} + +void EMCInterpreter::op_addSP(EMCState *script) { + script->sp += _parameter; +} + +void EMCInterpreter::op_subSP(EMCState *script) { + script->sp -= _parameter; +} + +void EMCInterpreter::op_sysCall(EMCState *script) { + const uint8 id = _parameter; + + assert(script->dataPtr->sysFuncs); + assert(id < script->dataPtr->sysFuncs->size()); + + if ((*script->dataPtr->sysFuncs)[id] && ((*script->dataPtr->sysFuncs)[id])->isValid()) { + script->retValue = (*(*script->dataPtr->sysFuncs)[id])(script); + } else { + script->retValue = 0; + warning("Unimplemented system call 0x%.02X/%d used in file '%s'", id, id, script->dataPtr->filename); + } +} + +void EMCInterpreter::op_ifNotJmp(EMCState *script) { + if (!script->stack[script->sp++]) { + _parameter &= 0x7FFF; + script->ip = script->dataPtr->data + _parameter; + } +} + +void EMCInterpreter::op_negate(EMCState *script) { + int16 value = script->stack[script->sp]; + switch (_parameter) { + case 0: + if (!value) + script->stack[script->sp] = 1; + else + script->stack[script->sp] = 0; + break; + + case 1: + script->stack[script->sp] = -value; + break; + + case 2: + script->stack[script->sp] = ~value; + break; + + default: + warning("Unknown negation func: %d", _parameter); + script->ip = 0; + } +} + +void EMCInterpreter::op_eval(EMCState *script) { + int16 ret = 0; + bool error = false; + + int16 val1 = script->stack[script->sp++]; + int16 val2 = script->stack[script->sp++]; + + switch (_parameter) { + case 0: + ret = (val2 && val1) ? 1 : 0; + break; + + case 1: + ret = (val2 || val1) ? 1 : 0; + break; + + case 2: + ret = (val1 == val2) ? 1 : 0; + break; + + case 3: + ret = (val1 != val2) ? 1 : 0; + break; + + case 4: + ret = (val1 > val2) ? 1 : 0; + break; + + case 5: + ret = (val1 >= val2) ? 1 : 0; + break; + + case 6: + ret = (val1 < val2) ? 1 : 0; + break; + + case 7: + ret = (val1 <= val2) ? 1 : 0; + break; + + case 8: + ret = val1 + val2; + break; + + case 9: + ret = val2 - val1; + break; + + case 10: + ret = val1 * val2; + break; + + case 11: + ret = val2 / val1; + break; + + case 12: + ret = val2 >> val1; + break; + + case 13: + ret = val2 << val1; + break; + + case 14: + ret = val1 & val2; + break; + + case 15: + ret = val1 | val2; + break; + + case 16: + ret = val2 % val1; + break; + + case 17: + ret = val1 ^ val2; + break; + + default: + warning("Unknown evaluate func: %d", _parameter); + error = true; + } + + if (error) + script->ip = 0; + else + script->stack[--script->sp] = ret; +} + +void EMCInterpreter::op_setRetAndJmp(EMCState *script) { + if (script->sp >= EMCState::kStackLastEntry) { + script->ip = 0; + } else { + script->retValue = script->stack[script->sp++]; + uint16 temp = script->stack[script->sp++]; + script->stack[EMCState::kStackLastEntry] = 0; + script->ip = &script->dataPtr->data[temp]; + } +} +} // End of namespace Kyra diff --git a/engines/kyra/script/script.h b/engines/kyra/script/script.h new file mode 100644 index 0000000000..12a44b0a03 --- /dev/null +++ b/engines/kyra/script/script.h @@ -0,0 +1,159 @@ +/* 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. + * + */ + +#ifndef KYRA_SCRIPT_H +#define KYRA_SCRIPT_H + +#include "common/stream.h" +#include "common/array.h" +#include "common/func.h" +#include "common/iff_container.h" + +namespace Kyra { + +struct EMCState; +typedef Common::Functor1<EMCState *, int> Opcode; + +struct EMCData { + char filename[13]; + + byte *text; + uint16 *data; + uint16 *ordr; + uint16 dataSize; + + const Common::Array<const Opcode *> *sysFuncs; +}; + +struct EMCState { + enum { + kStackSize = 100, + kStackLastEntry = kStackSize - 1 + }; + + const uint16 *ip; + const EMCData *dataPtr; + int16 retValue; + uint16 bp; + uint16 sp; + int16 regs[30]; // VM registers + int16 stack[kStackSize]; // VM stack +}; + +#define stackPos(x) (script->stack[script->sp+x]) +#define stackPosString(x) ((const char *)&script->dataPtr->text[READ_BE_UINT16(&script->dataPtr->text[stackPos(x)<<1])]) + +class Resource; +class KyraEngine_v1; + +class IFFParser : public Common::IFFParser { +public: + IFFParser(Common::ReadStream &input) : Common::IFFParser(&input) { + // It seems Westwood missunderstood the 'size' field of the FORM chunk. + // + // For EMC scripts (type EMC2) it's filesize instead of filesize - 8, + // means accidently including the 8 bytes used by the chunk header for the FORM + // chunk. + // + // For TIM scripts (type AVFS) it's filesize - 12 instead of filesize - 8, + // means it will not include the size of the 'type' field in the FORM chunk, + // instead of only not including the chunk header size. + // + // Both lead to some problems in our IFF parser, either reading after the end + // of file or producing a "Chunk overread" error message. To work around this + // we need to adjust the size field properly. + + // Fix for certain Russian fan translations: + // Westwood's original code completely ignores the FORM chunk and its size + // setting. After opening a TIM or EMC file they just check whether the FORM + // chunk exists (as a kind of file type verification) and then immediately seek + // behind the FORM chunk. + // This means that their parser is immune to weird fan translation scripts + // where the file size doesn't match the form chunk size. In our implemetation + // this would produce "Chunk overread" errors. + // Westwood also always pads all chunk sizes to 2 byte alignment after reading + // them from the file (not with FORM though, since they completely ignore it). + // This seems to do the trick for our FORM chunk size issue with the Russian + // fan translations. Another method which I have tried and which seems to work + // well would be simply setting _formChunk.size to the file size (-12 for TIM). + + _formChunk.size = (_formChunk.size + 1) & ~1; + + if (_formType == MKTAG('E','M','C','2')) + _formChunk.size -= 8; + else if (_formType == MKTAG('A','V','F','S')) + _formChunk.size += 4; + } +}; + +class EMCInterpreter { +public: + EMCInterpreter(KyraEngine_v1 *vm); + + bool load(const char *filename, EMCData *data, const Common::Array<const Opcode *> *opcodes); + void unload(EMCData *data); + + void init(EMCState *scriptState, const EMCData *data); + bool start(EMCState *script, int function); + + bool isValid(EMCState *script); + + bool run(EMCState *script); +protected: + KyraEngine_v1 *_vm; + int16 _parameter; + + const char *_filename; + EMCData *_scriptData; + + bool callback(Common::IFFChunk &chunk); + + typedef void (EMCInterpreter::*OpcodeProc)(EMCState *); + struct OpcodeEntry { + OpcodeProc proc; + const char *desc; + }; + + const OpcodeEntry *_opcodes; +private: + void op_jmp(EMCState *); + void op_setRetValue(EMCState *); + void op_pushRetOrPos(EMCState *); + void op_push(EMCState *); + void op_pushReg(EMCState *); + void op_pushBPNeg(EMCState *); + void op_pushBPAdd(EMCState *); + void op_popRetOrPos(EMCState *); + void op_popReg(EMCState *); + void op_popBPNeg(EMCState *); + void op_popBPAdd(EMCState *); + void op_addSP(EMCState *); + void op_subSP(EMCState *); + void op_sysCall(EMCState *); + void op_ifNotJmp(EMCState *); + void op_negate(EMCState *); + void op_eval(EMCState *); + void op_setRetAndJmp(EMCState *); +}; +} // End of namespace Kyra + +#endif diff --git a/engines/kyra/script/script_eob.cpp b/engines/kyra/script/script_eob.cpp new file mode 100644 index 0000000000..709921deb9 --- /dev/null +++ b/engines/kyra/script/script_eob.cpp @@ -0,0 +1,1622 @@ +/* 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/engine/eobcommon.h" +#include "kyra/graphics/screen_eob.h" +#include "kyra/script/script_eob.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/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 InfOpcode *>::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: + if (_vm->gameFlags().platform == Common::kPlatformDOS) + _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 diff --git a/engines/kyra/script/script_eob.h b/engines/kyra/script/script_eob.h new file mode 100644 index 0000000000..b996e314d5 --- /dev/null +++ b/engines/kyra/script/script_eob.h @@ -0,0 +1,131 @@ +/* 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 + +#ifndef KYRA_SCRIPT_EOB_H +#define KYRA_SCRIPT_EOB_H + +#include "common/func.h" +#include "common/substream.h" +#include "common/savefile.h" + +namespace Kyra { + +class KyraRpgEngine; + +class EoBInfProcessor { +public: + EoBInfProcessor(EoBCoreEngine *engine, Screen_EoB *_screen); + ~EoBInfProcessor(); + + void loadData(const uint8 *data, uint32 dataSize); + void run(int func, int flags); + + void setFlags(uint32 flags); + void clearFlags(uint32 flags); + bool checkFlags(uint32 flags) const; + bool preventRest() const; + + void loadState(Common::SeekableSubReadStreamEndian &in, bool origFile = false); + void saveState(Common::OutSaveFile *out, bool origFile = false); + void reset(); + +private: + const char *getString(uint16 index); + + int oeob_setWallType(int8 *data); + int oeob_toggleWallState(int8 *data); + int oeob_openDoor(int8 *data); + int oeob_closeDoor(int8 *data); + int oeob_replaceMonster(int8 *data); + int oeob_movePartyOrObject(int8 *data); + int oeob_moveInventoryItemToBlock(int8 *data); + int oeob_printMessage_v1(int8 *data); + int oeob_printMessage_v2(int8 *data); + int oeob_setFlags(int8 *data); + int oeob_playSoundEffect(int8 *data); + int oeob_removeFlags(int8 *data); + int oeob_modifyCharacterHitPoints(int8 *data); + int oeob_calcAndInflictCharacterDamage(int8 *data); + int oeob_jump(int8 *data); + int oeob_end(int8 *data); + int oeob_returnFromSubroutine(int8 *data); + int oeob_callSubroutine(int8 *data); + int oeob_eval_v1(int8 *data); + int oeob_eval_v2(int8 *data); + int oeob_deleteItem(int8 *data); + int oeob_loadNewLevelOrMonsters(int8 *data); + int oeob_increasePartyExperience(int8 *data); + int oeob_createItem_v1(int8 *data); + int oeob_createItem_v2(int8 *data); + int oeob_launchObject(int8 *data); + int oeob_changeDirection(int8 *data); + int oeob_identifyItems(int8 *data); + int oeob_sequence(int8 *data); + int oeob_delay(int8 *data); + int oeob_drawScene(int8 *data); + int oeob_dialogue(int8 *data); + int oeob_specialEvent(int8 *data); + + EoBCoreEngine *_vm; + Screen_EoB *_screen; + + typedef Common::Functor1Mem<int8 *, int, EoBInfProcessor> InfProc; + struct InfOpcode : private Common::NonCopyable { + InfOpcode(InfProc *p, const char *d) : proc(p), desc(d) {} + ~InfOpcode() { delete proc; } + + InfProc *proc; + Common::String desc; + }; + Common::Array<const InfOpcode *> _opcodes; + + int8 *_scriptData; + uint16 _scriptSize; + + uint8 _abortScript; + uint16 _abortAfterSubroutine; + int _dlgResult; + uint8 _preventRest; + + uint16 _lastScriptFunc; + uint16 _lastScriptFlags; + + int8 **_subroutineStack; + int _subroutineStackPos; + + uint32 *_flagTable; + + int16 *_stack; + int _stackIndex; + + int8 _activeCharacter; + + const int _commandMin; +}; + +} // End of namespace Kyra + +#endif + +#endif // ENABLE_EOB diff --git a/engines/kyra/script/script_hof.cpp b/engines/kyra/script/script_hof.cpp new file mode 100644 index 0000000000..94d3a82cc4 --- /dev/null +++ b/engines/kyra/script/script_hof.cpp @@ -0,0 +1,1727 @@ +/* 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 "kyra/engine/kyra_hof.h" +#include "kyra/engine/timer.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" + +namespace Kyra { + +int KyraEngine_HoF::o2_setCharacterFacingRefresh(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setCharacterFacingRefresh(%p) (-, %d, %d)", (const void *)script, stackPos(1), stackPos(2)); + int animFrame = stackPos(2); + if (animFrame >= 0) + _mainCharacter.animFrame = animFrame; + _mainCharacter.facing = stackPos(1); + updateCharacterAnim(0); + refreshAnimObjectsIfNeed(); + return 0; +} + +int KyraEngine_HoF::o2_setCharacterPos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setCharacterFacingRefresh(%p) (-, %d, %d)", (const void *)script, stackPos(1), stackPos(2)); + int x = stackPos(1); + int y = stackPos(2); + + if (x != -1 && y != -1) { + x &= ~3; + y &= ~1; + } + + restorePage3(); + _mainCharacter.x2 = _mainCharacter.x1 = x; + _mainCharacter.y2 = _mainCharacter.y1 = y; + return 0; +} + +int KyraEngine_HoF::o2_defineObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_defineObject(%p) (%d, '%s', %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + TalkObject *object = &_talkObjectList[stackPos(0)]; + strcpy(object->filename, stackPosString(1)); + object->scriptId = stackPos(2); + object->x = stackPos(3); + object->y = stackPos(4); + object->color = stackPos(5); + return 0; +} + +int KyraEngine_HoF::o2_refreshCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_refreshCharacter(%p) (-, %d, %d, %d)", (const void *)script, stackPos(1), stackPos(2), stackPos(3)); + int unk = stackPos(1); + int facing = stackPos(2); + int refresh = stackPos(3); + if (facing >= 0) + _mainCharacter.facing = facing; + if (unk >= 0 && unk != 32) + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + updateCharacterAnim(0); + if (refresh) + refreshAnimObjectsIfNeed(); + return 0; +} + +int KyraEngine_HoF::o2_setSceneComment(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setSceneComment(%p) ('%s')", (const void *)script, stackPosString(0)); + _sceneCommentString = stackPosString(0); + return 0; +} + +int KyraEngine_HoF::o2_setCharacterAnimFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setCharacterAnimFrame(%p) (-, %d, %d)", (const void *)script, stackPos(1), stackPos(2)); + int animFrame = stackPos(1); + int updateAnim = stackPos(2); + + _mainCharacter.animFrame = animFrame; + if (updateAnim) + updateCharacterAnim(0); + + return 0; +} + +int KyraEngine_HoF::o2_customCharacterChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_customCharacterChat(%p) ('%s', %d, %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + playVoice(_vocHigh, stackPos(4)); + _text->printCustomCharacterText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), 0, 2); + return 0; +} + +int KyraEngine_HoF::o2_soundFadeOut(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_soundFadeOut(%p) ()", (const void *)script); + _sound->beginFadeOut(); + return 0; +} + +int KyraEngine_HoF::o2_showChapterMessage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_showChapterMessage(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + showChapterMessage(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_restoreTalkTextMessageBkgd(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_restoreTalkTextMessageBkgd(%p) ()", (const void *)script); + _text->restoreTalkTextMessageBkgd(2, 0); + return 0; +} + +int KyraEngine_HoF::o2_wsaClose(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_wsaClose(%p) (%d)", (const void *)script, stackPos(0)); + assert(stackPos(0) >= 0 && stackPos(0) < ARRAYSIZE(_wsaSlots)); + _wsaSlots[stackPos(0)]->close(); + return 0; +} + +int KyraEngine_HoF::o2_meanWhileScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_meanWhileScene(%p) (%d)", (const void *)script, stackPos(0)); + static const uint8 jpSubtitle[] = { 0x88, 0xEA, 0x95, 0xFB, 0x81, 0x45, 0x81, 0x45, 0x81, 0x45 }; + const char *cpsfile = stackPosString(0); + const char *palfile = stackPosString(1); + + _screen->loadBitmap(cpsfile, 3, 3, 0); + _screen->copyPalette(2, 0); + _screen->loadPalette(palfile, _screen->getPalette(2)); + _screen->fillRect(0, 0, 319, 199, 207); + _screen->setScreenPalette(_screen->getPalette(2)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + if (!scumm_stricmp(cpsfile, "_MEANWIL.CPS") && _flags.lang == Common::JA_JPN) { + Screen::FontId o = _screen->setFont(Screen::FID_SJIS_FNT); + _screen->printText((const char *)jpSubtitle, 140, 176, 255, 132); + _screen->setFont(o); + } + _screen->updateScreen(); + return 0; +} + +int KyraEngine_HoF::o2_backUpScreen(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_backUpScreen(%p) (%d)", (const void *)script, stackPos(0)); + _screen->copyRegionToBuffer(stackPos(0), 0, 0, 320, 144, _screenBuffer); + return 0; +} + +int KyraEngine_HoF::o2_restoreScreen(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_restoreScreen(%p) (%d)", (const void *)script, stackPos(0)); + _screen->copyBlockToPage(stackPos(0), 0, 0, 320, 144, _screenBuffer); + return 0; +} + +int KyraEngine_HoF::o2_displayWsaFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_displayWsaFrame(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8)); + int frame = stackPos(0); + int x = stackPos(1); + int y = stackPos(2); + int waitTime = stackPos(3); + int slot = stackPos(4); + int copyParam = stackPos(5); + int doUpdate = stackPos(6); + int dstPage = stackPos(7); + int backUp = stackPos(8); + + _screen->hideMouse(); + const uint32 endTime = _system->getMillis() + waitTime * _tickLength; + _wsaSlots[slot]->displayFrame(frame, dstPage, x, y, copyParam | 0xC000, 0, 0); + _screen->updateScreen(); + + if (backUp) + memcpy(_gamePlayBuffer, _screen->getCPagePtr(3), 46080); + + delayUntil(endTime, false, doUpdate != 0); + _screen->showMouse(); + return 0; +} + +int KyraEngine_HoF::o2_displayWsaSequentialFramesLooping(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_displayWsaSequentialFramesLooping(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + int startFrame = stackPos(0); + int endFrame = stackPos(1); + int x = stackPos(2); + int y = stackPos(3); + int waitTime = stackPos(4); + int slot = stackPos(5); + int maxTimes = stackPos(6); + int copyFlags = stackPos(7); + + if (maxTimes > 1) + maxTimes = 1; + + _screen->hideMouse(); + int curTime = 0; + while (curTime < maxTimes) { + if (startFrame < endFrame) { + for (int i = startFrame; i <= endFrame; ++i) { + const uint32 endTime = _system->getMillis() + waitTime * _tickLength; + _wsaSlots[slot]->displayFrame(i, 0, x, y, 0xC000 | copyFlags, 0, 0); + + if (!skipFlag()) { + _screen->updateScreen(); + delayUntil(endTime, false, true); + } + } + } else { + for (int i = startFrame; i >= endFrame; --i) { + const uint32 endTime = _system->getMillis() + waitTime * _tickLength; + _wsaSlots[slot]->displayFrame(i, 0, x, y, 0xC000 | copyFlags, 0, 0); + + if (!skipFlag()) { + _screen->updateScreen(); + delayUntil(endTime, false, true); + } + } + } + + ++curTime; + } + resetSkipFlag(); + _screen->showMouse(); + return 0; +} + +int KyraEngine_HoF::o2_wsaOpen(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_wsaOpen(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + assert(stackPos(1) >= 0 && stackPos(1) < ARRAYSIZE(_wsaSlots)); + _wsaSlots[stackPos(1)]->open(stackPosString(0), 1, 0); + return 0; +} + +int KyraEngine_HoF::o2_displayWsaSequentialFrames(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_displayWsaSequentialFrames(%p) (%d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6)); + + uint16 frameDelay = stackPos(2) * _tickLength; + uint16 currentFrame = stackPos(3); + uint16 lastFrame = stackPos(4); + uint16 index = stackPos(5); + uint16 copyParam = stackPos(6) | 0xC000; + + _screen->hideMouse(); + + while (currentFrame <= lastFrame) { + const uint32 endTime = _system->getMillis() + frameDelay; + _wsaSlots[index]->displayFrame(currentFrame++, 0, stackPos(0), stackPos(1), copyParam, 0, 0); + if (!skipFlag()) { + _screen->updateScreen(); + delayUntil(endTime); + } + } + + resetSkipFlag(); + _screen->showMouse(); + + return 0; +} + +int KyraEngine_HoF::o2_displayWsaSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_displayWsaSequence(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + + const int frameDelay = stackPos(2) * _tickLength; + const int index = stackPos(3); + const bool doUpdate = (stackPos(4) != 0); + const uint16 copyParam = stackPos(5) | 0xC000; + + _screen->hideMouse(); + + int currentFrame = 0; + const int lastFrame = _wsaSlots[index]->frames(); + + while (currentFrame <= lastFrame) { + const uint32 endTime = _system->getMillis() + frameDelay; + _wsaSlots[index]->displayFrame(currentFrame++, 0, stackPos(0), stackPos(1), copyParam, 0, 0); + if (!skipFlag()) { + if (doUpdate) + update(); + _screen->updateScreen(); + delayUntil(endTime); + } + } + + resetSkipFlag(); + _screen->showMouse(); + + return 0; +} + +int KyraEngine_HoF::o2_addItemToInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_addItemToInventory(%p) (%d, -, %d)", (const void *)script, stackPos(0), stackPos(2)); + int slot = findFreeVisibleInventorySlot(); + if (slot != -1) { + _mainCharacter.inventory[slot] = stackPos(0); + if (stackPos(2)) + redrawInventory(0); + } + return slot; +} + +int KyraEngine_HoF::o2_drawShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_drawShape(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + + uint8 *shp = getShapePtr(stackPos(0) + 64); + int x = stackPos(1); + int y = stackPos(2); + uint8 dsFlag = stackPos(3) & 0xFF; + uint8 modeFlag = stackPos(4) & 0xFF; + + if (modeFlag) { + _screen->drawShape(2, shp, x, y, 2, dsFlag ? 1 : 0); + } else { + restorePage3(); + _screen->drawShape(2, shp, x, y, 2, dsFlag ? 1 : 0); + memcpy(_gamePlayBuffer, _screen->getCPagePtr(3), 46080); + _screen->drawShape(0, shp, x, y, 2, dsFlag ? 1 : 0); + + flagAnimObjsForRefresh(); + flagAnimObjsSpecialRefresh(); + refreshAnimObjectsIfNeed(); + } + + return 0; +} + +int KyraEngine_HoF::o2_addItemToCurScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_addItemToCurScene(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + const int16 id = stackPos(0); + int x = stackPos(1); + int y = stackPos(2); + + int freeItem = findFreeItem(); + x = MAX(14, x); + x = MIN(304, x); + y = MAX(14, y); + y = MIN(136, y); + if (freeItem >= 0) { + _itemList[freeItem].id = id; + _itemList[freeItem].x = x; + _itemList[freeItem].y = y; + _itemList[freeItem].sceneId = _mainCharacter.sceneId; + addItemToAnimList(freeItem); + refreshAnimObjectsIfNeed(); + } + return 0; +} + +int KyraEngine_HoF::o2_loadSoundFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_loadSoundFile(%p) (%d)", (const void *)script, stackPos(0)); + if (_flags.platform == Common::kPlatformDOS) + snd_loadSoundFile(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_removeSlotFromInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_removeSlotFromInventory(%p) (%d)", (const void *)script, stackPos(0)); + removeSlotFromInventory(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_removeItemFromInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_removeItemFromInventory(%p) (%d)", (const void *)script, stackPos(0)); + uint16 item = stackPos(0); + int slot = -1; + while ((slot = getInventoryItemSlot(item)) != -1) + removeSlotFromInventory(slot); + return 0; +} + +int KyraEngine_HoF::o2_countItemInInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemInInventory(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + uint16 item = stackPos(1); + int count = 0; + + for (int i = 0; i < 20; ++i) { + if (_mainCharacter.inventory[i] == item) + ++count; + } + + if ((stackPos(0) == 0) && _itemInHand == int16(item)) + ++count; + + return count; +} + +int KyraEngine_HoF::o2_countItemsInScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemsInScene(%p) (%d)", (const void *)script, stackPos(0)); + int count = 0; + for (int i = 0; i < 30; ++i) { + if (_itemList[i].sceneId == stackPos(0) && _itemList[i].id != kItemNone) + ++count; + } + return count; +} + +int KyraEngine_HoF::o2_wipeDownMouseItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_wipeDownMouseItem(%p) (-, %d, %d)", (const void *)script, stackPos(1), stackPos(2)); + _screen->hideMouse(); + const int x = stackPos(1) - 8; + const int y = stackPos(2) - 15; + + if (_itemInHand >= 0) { + backUpGfxRect32x32(x, y); + uint8 *shape = getShapePtr(_itemInHand+64); + for (int curY = y, height = 16; height > 0; height -= 2, curY += 2) { + restoreGfxRect32x32(x, y); + _screen->setNewShapeHeight(shape, height); + uint32 waitTime = _system->getMillis() + _tickLength; + _screen->drawShape(0, shape, x, curY, 0, 0); + _screen->updateScreen(); + delayUntil(waitTime); + } + restoreGfxRect32x32(x, y); + _screen->resetShapeHeight(shape); + } + + _screen->showMouse(); + removeHandItem(); + + return 0; +} + +int KyraEngine_HoF::o2_getElapsedSecs(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getElapsedSecs(%p) ()", (const void *)script); + return _system->getMillis() / 1000; +} + +int KyraEngine_HoF::o2_getTimerDelay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getTimerDelay(%p) (%d)", (const void *)script, stackPos(0)); + return _timer->getDelay(stackPos(0)); +} + +int KyraEngine_HoF::o2_delaySecs(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_delaySecs(%p) (%d)", (const void *)script, stackPos(0)); + delay(stackPos(0) * 1000, true); + return 0; +} + +int KyraEngine_HoF::o2_setTimerDelay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setTimerDelay(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _timer->setDelay(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_setScaleTableItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setScaleTableItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + setScaleTableItem(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_setDrawLayerTableItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setDrawLayerTableItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + setDrawLayerTableEntry(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_setCharPalEntry(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setCharPalEntry(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + setCharPalEntry(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_loadZShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_loadZShapes(%p) (%d)", (const void *)script, stackPos(0)); + loadCharacterShapes(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_drawSceneShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_drawSceneShape(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), + stackPos(2), stackPos(3)); + + int shape = stackPos(0); + int x = stackPos(1); + int y = stackPos(2); + int flag = (stackPos(3) != 0) ? 1 : 0; + + restorePage3(); + + _screen->drawShape(2, _sceneShapeTable[shape], x, y, 2, flag); + + memcpy(_gamePlayBuffer, _screen->getCPagePtr(3), 46080); + + _screen->drawShape(0, _sceneShapeTable[shape], x, y, 2, flag); + + flagAnimObjsSpecialRefresh(); + flagAnimObjsForRefresh(); + refreshAnimObjectsIfNeed(); + return 0; +} + +int KyraEngine_HoF::o2_drawSceneShapeOnPage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_drawSceneShapeOnPage(%p) (%d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + int shape = stackPos(0); + int x = stackPos(1); + int y = stackPos(2); + int flag = stackPos(3); + int drawPage = stackPos(4); + + _screen->drawShape(drawPage, _sceneShapeTable[shape], x, y, 2, flag ? 1 : 0); + return 0; +} + +int KyraEngine_HoF::o2_disableAnimObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_disableAnimObject(%p) (%d)", (const void *)script, stackPos(0)); + _animObjects[stackPos(0)+1].enabled = false; + return 0; +} + +int KyraEngine_HoF::o2_enableAnimObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_enableAnimObject(%p) (%d)", (const void *)script, stackPos(0)); + _animObjects[stackPos(0)+1].enabled = true; + return 0; +} + +int KyraEngine_HoF::o2_loadPalette384(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_loadPalette384(%p) ('%s')", (const void *)script, stackPosString(0)); + _screen->copyPalette(1, 0); + _res->loadFileToBuf(stackPosString(0), _screen->getPalette(1).getData(), 384); + return 0; +} + +int KyraEngine_HoF::o2_setPalette384(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setPalette384(%p) ()", (const void *)script); + _screen->getPalette(0).copy(_screen->getPalette(1), 0, 128); + _screen->setScreenPalette(_screen->getPalette(0)); + return 0; +} + +int KyraEngine_HoF::o2_restoreBackBuffer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_restoreBackBuffer(%p) (%d)", (const void *)script, stackPos(0)); + int disable = stackPos(0); + int oldState = 0; + + if (disable) { + oldState = _animObjects[0].enabled; + _animObjects[0].enabled = 0; + } + + restorePage3(); + + if (disable) + _animObjects[0].enabled = (oldState != 0); + + return 0; +} + +int KyraEngine_HoF::o2_backUpInventoryGfx(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_backUpInventoryGfx(%p) ()", (const void *)script); + _screen->copyRegionToBuffer(1, 0, 144, 320, 56, _screenBuffer); + _inventorySaved = true; + return 0; +} + +int KyraEngine_HoF::o2_disableSceneAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_disableSceneAnim(%p) (%d)", (const void *)script, stackPos(0)); + _sceneAnims[stackPos(0)].flags &= ~1; + return 0; +} + +int KyraEngine_HoF::o2_enableSceneAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_enableSceneAnim(%p) (%d)", (const void *)script, stackPos(0)); + _sceneAnims[stackPos(0)].flags |= 1; + return 0; +} + +int KyraEngine_HoF::o2_restoreInventoryGfx(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_restoreInventoryGfx(%p) ()", (const void *)script); + _screen->copyBlockToPage(1, 0, 144, 320, 56, _screenBuffer); + _inventorySaved = false; + return 0; +} + +int KyraEngine_HoF::o2_setSceneAnimPos2(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setSceneAnimPos2(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + _sceneAnims[stackPos(0)].x2 = stackPos(1); + _sceneAnims[stackPos(0)].y2 = stackPos(2); + return 0; +} + +int KyraEngine_HoF::o2_fadeScenePal(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_fadeScenePal(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + fadeScenePal(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_enterNewScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_enterNewScene(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), + stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + + int skipNpcScript = stackPos(3); + enterNewScene(stackPos(0), stackPos(1), stackPos(2), skipNpcScript, stackPos(4)); + + if (!skipNpcScript) + runSceneScript4(0); + + _unk5 = 1; + + if (_mainCharX == -1 || _mainCharY == -1) { + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + updateCharacterAnim(0); + } + + return 0; +} + +int KyraEngine_HoF::o2_switchScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_switchScene(%p) (%d)", (const void *)script, stackPos(0)); + setGameFlag(0x1EF); + _mainCharX = _mainCharacter.x1; + _mainCharY = _mainCharacter.y1; + _noScriptEnter = false; + enterNewScene(stackPos(0), _mainCharacter.facing, 0, 0, 0); + _noScriptEnter = true; + return 0; +} + +int KyraEngine_HoF::o2_setPathfinderFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setPathfinderFlag(%p) (%d)", (const void *)script, stackPos(0)); + _pathfinderFlag = stackPos(0); + return 0; +} + +int KyraEngine_HoF::o2_getSceneExitToFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getSceneExitToFacing(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int scene = stackPos(0); + const int facing = stackPos(1); + + if (facing == 0) + return (int16)_sceneList[scene].exit1; + else if (facing == 2) + return (int16)_sceneList[scene].exit2; + else if (facing == 4) + return (int16)_sceneList[scene].exit3; + else if (facing == 6) + return (int16)_sceneList[scene].exit4; + return -1; +} + +int KyraEngine_HoF::o2_setLayerFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setLayerFlag(%p) (%d)", (const void *)script, stackPos(0)); + int layer = stackPos(0); + if (layer >= 1 && layer <= 16) + _layerFlagTable[layer] = 1; + return 0; +} + +int KyraEngine_HoF::o2_setZanthiaPos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setZanthiaPos(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _mainCharX = stackPos(0); + _mainCharY = stackPos(1); + + if (_mainCharX == -1 && _mainCharY == -1) + _mainCharacter.animFrame = 32; + else + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + + return 0; +} + +int KyraEngine_HoF::o2_loadMusicTrack(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_loadMusicTrack(%p) (%d)", (const void *)script, stackPos(0)); + snd_loadSoundFile(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_setSceneAnimPos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setSceneAnimPos(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + _sceneAnims[stackPos(0)].x = stackPos(1); + _sceneAnims[stackPos(0)].y = stackPos(2); + return 0; +} + +int KyraEngine_HoF::o2_setCauldronState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setCauldronState(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + setCauldronState(stackPos(0), stackPos(1) != 0); + clearCauldronTable(); + return 0; +} + +int KyraEngine_HoF::o2_showItemString(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_showItemString(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int item = stackPos(0); + + int string = 0; + if (stackPos(1) == 1) { + if (_lang == 1) + string = getItemCommandStringPickUp(item); + else + string = 7; + } else { + if (_lang == 1) + string = getItemCommandStringInv(item); + else + string = 8; + } + + updateCommandLineEx(item+54, string, 0xD6); + return 0; +} + +int KyraEngine_HoF::o2_isAnySoundPlaying(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_isAnySoundPlaying(%p) ()", (const void *)script); + + // WORKAROUND + // + // The input script function in the skull scene does busy wait + // for the sound effect, which is played after completing the + // song, to finish. To avoid too much CPU use, we add some slight + // delay here. + // + // Also the Nintendo DS backend seems only to update the sound, when + // either OSystem::updateScreen or OSystem::delayMillis is called. + // So we have to call delay here, since otherwise the game would hang. +#ifndef __DS__ + if (_currentScene == 16 && _currentChapter == 1) +#endif + delay(_tickLength); + + return _sound->voiceIsPlaying() ? 1 : 0; +} + +int KyraEngine_HoF::o2_setDrawNoShapeFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setDrawNoShapeFlag(%p) (%d)", (const void *)script, stackPos(0)); + _drawNoShapeFlag = (stackPos(0) != 0); + return 0; +} + +int KyraEngine_HoF::o2_setRunFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setRunFlag(%p) (%d)", (const void *)script, stackPos(0)); + // this is usually just _runFlag, but since this is just used when the game should play the credits + // we handle it a bit different :-) + _showOutro = true; + _runFlag = false; + return 0; +} + +int KyraEngine_HoF::o2_showLetter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_showLetter(%p) (%d)", (const void *)script, stackPos(0)); + const int letter = stackPos(0); + char filename[16]; + + _screen->hideMouse(); + + showMessage(0, 0xCF); + displayInvWsaLastFrame(); + backUpPage0(); + + _screen->copyPalette(2, 0); + + _screen->clearPage(3); + _screen->loadBitmap("_NOTE.CPS", 3, 3, 0); + + sprintf(filename, "_NTEPAL%.1d.COL", letter+1); + _screen->loadPalette(filename, _screen->getPalette(0)); + + _screen->fadeToBlack(0x14); + + sprintf(filename, "LETTER%.1d.%s", letter, _languageExtension[_lang]); + uint8 *letterBuffer = _res->fileData(filename, 0); + if (!letterBuffer) { + // some floppy versions use a TXT extension + sprintf(filename, "LETTER%.1d.TXT", letter); + letterBuffer = _res->fileData(filename, 0); + } + + if (letterBuffer) { + bookDecodeText(letterBuffer); + bookPrintText(2, letterBuffer, 0xC, 0xA, 0x20); + } + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->fadePalette(_screen->getPalette(0), 0x14); + _screen->setMouseCursor(0, 0, getShapePtr(0)); + setMousePos(280, 160); + + _screen->showMouse(); + + bool running = true; + while (running) { + int inputFlag = checkInput(0); + removeInputTop(); + + if (inputFlag == 198 || inputFlag == 199) + running = false; + + _screen->updateScreen(); + _system->delayMillis(10); + } + + _screen->hideMouse(); + _screen->fadeToBlack(0x14); + restorePage0(); + _screen->copyPalette(0, 2); + _screen->fadePalette(_screen->getPalette(0), 0x14); + setHandItem(_itemInHand); + _screen->showMouse(); + + return 0; +} + +int KyraEngine_HoF::o2_playFireflyScore(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_playFireflyScore(%p) ()", (const void *)script); + if (_sound->getSfxType() == Sound::kAdLib || _sound->getSfxType() == Sound::kPCSpkr || + _sound->getSfxType() == Sound::kMidiMT32 || _sound->getSfxType() == Sound::kMidiGM) { + snd_playWanderScoreViaMap(86, 1); + return 1; + } else { + return 0; + } +} + +int KyraEngine_HoF::o2_encodeShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_encodeShape(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), + stackPos(2), stackPos(3), stackPos(4)); + _sceneShapeTable[stackPos(0)] = _screen->encodeShape(stackPos(1), stackPos(2), stackPos(3), stackPos(4), 2); + return 0; +} + +int KyraEngine_HoF::o2_defineSceneAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_defineSceneAnim(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, '%s')", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), + stackPos(9), stackPos(10), stackPos(11), stackPosString(12)); + int animId = stackPos(0); + SceneAnim &anim = _sceneAnims[animId]; + anim.flags = stackPos(1); + anim.x = stackPos(2); + anim.y = stackPos(3); + anim.x2 = stackPos(4); + anim.y2 = stackPos(5); + anim.width = stackPos(6); + anim.height = stackPos(7); + anim.specialSize = stackPos(9); + anim.shapeIndex = stackPos(11); + if (stackPosString(12) != 0) + strcpy(anim.filename, stackPosString(12)); + + if (anim.flags & 0x40) { + if (!_sceneAnimMovie[animId]->open(anim.filename, 1, 0)) + error("couldn't load '%s'", anim.filename); + + if (_sceneAnimMovie[animId]->xAdd() || _sceneAnimMovie[animId]->yAdd()) + anim.wsaFlag = 1; + else + anim.wsaFlag = 0; + } + + return 0; +} + +int KyraEngine_HoF::o2_updateSceneAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_updateSceneAnim(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + updateSceneAnim(stackPos(0), stackPos(1)); + + // HACK: Some animations are really too fast because of missing delay times. + // Notice that the delay time is purely subjective set here, it could look + // slower or maybe faster in the original, but at least this looks OK for + // Raziel^. + // + // We know currently of some different animations where this happens. + // - Where Marco is dangling from the flesh-eating plant (see bug + // #1923638 "HoF: Marco missing animation frames"). + // - After giving the ticket to the captain. He would move very fast + // (barely noticeable) onto the ship without this delay. + // - The scene after giving the sandwitch to the guards in the city. + // (see bug #1926838 "HoF: Animation plays too fast") + // This scene script calls o2_delay though, but since this updates + // the scene animation scripts again there is no delay for the + // animation. + // - When the sheriff enters the jail, either to lock you up or to throw + // away the key. (see bug #1926838 "HoF: Animation plays too fast"). + + if ((stackPos(0) == 2 && _mainCharacter.sceneId == 3) || + (stackPos(0) == 3 && _mainCharacter.sceneId == 33) || + ((stackPos(0) == 1 || stackPos(0) == 2) && _mainCharacter.sceneId == 19) || + ((stackPos(0) == 1 || stackPos(0) == 2) && _mainCharacter.sceneId == 27)) + _sceneSpecialScriptsTimer[_lastProcessedSceneScript] = _system->getMillis() + _tickLength * 6; + + _specialSceneScriptRunFlag = false; + return 0; +} + +int KyraEngine_HoF::o2_addToSceneAnimPosAndUpdate(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_addToSceneAnimPosAndUpdate(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + const int anim = stackPos(0); + _sceneAnims[anim].x2 += stackPos(1); + _sceneAnims[anim].y2 += stackPos(2); + if (_sceneAnims[anim].flags & 2) { + _sceneAnims[anim].x += stackPos(1); + _sceneAnims[anim].y += stackPos(2); + } + updateSceneAnim(anim, stackPos(3)); + _specialSceneScriptRunFlag = false; + return 0; +} + +int KyraEngine_HoF::o2_useItemOnMainChar(EMCState *script) { + EMCState tmpScript; + _emc->init(&tmpScript, &_npcScriptData); + _emc->start(&tmpScript, 0); + tmpScript.regs[4] = _itemInHand; + tmpScript.regs[0] = _mainCharacter.sceneId; + + int oldVocH = _vocHigh; + _vocHigh = 0x5A; + + while (_emc->isValid(&tmpScript)) + _emc->run(&tmpScript); + + _vocHigh = oldVocH; + + return 0; +} + +int KyraEngine_HoF::o2_startDialogue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_startDialogue(%p) (%d)", (const void *)script, stackPos(0)); + startDialogue(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_addCauldronStateTableEntry(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_addCauldronStateTableEntry(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return addToCauldronStateTable(stackPos(0), stackPos(1)) ? 1 : 0; +} + +int KyraEngine_HoF::o2_setCountDown(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setCountDown(%p) (%d)", (const void *)script, stackPos(0)); + _scriptCountDown = _system->getMillis() + stackPos(0) * _tickLength; + return 0; +} + +int KyraEngine_HoF::o2_getCountDown(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getCountDown(%p)", (const void *)script); + uint32 time = _system->getMillis(); + return (time > _scriptCountDown) ? 0 : (_scriptCountDown - time) / _tickLength; +} + +int KyraEngine_HoF::o2_pressColorKey(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_pressColorKey(%p) (%d)", (const void *)script, stackPos(0)); + for (int i = 6; i; i--) + _inputColorCode[i] = _inputColorCode[i - 1]; + _inputColorCode[0] = stackPos(0) & 0xFF; + for (int i = 0; i < 7; i++) { + if (_presetColorCode[i] != _inputColorCode[6 - i]) + return _dbgPass; + } + return 1; +} + +int KyraEngine_HoF::o2_objectChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_objectChat(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + if (_flags.isTalkie) + warning("Unexpected call: o2_objectChat(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + else + objectChat(stackPosString(0), stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_changeChapter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_changeChapter(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int chapter = stackPos(0); + const int scene = stackPos(1); + + resetItemList(); + + _newChapterFile = chapter; + runStartScript(chapter, 0); + + _mainCharacter.dlgIndex = 0; + memset(_newSceneDlgState, 0, 32); + + static const int zShapeList[] = { 1, 2, 2, 2, 4 }; + assert(chapter > 1 && chapter <= ARRAYSIZE(zShapeList)); + loadCharacterShapes(zShapeList[chapter-1]); + + enterNewScene(scene, (chapter == 2) ? 2 : 0, 0, 0, 0); + + return 0; +} + +int KyraEngine_HoF::o2_getColorCodeFlag1(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getColorCodeFlag1(%p) ()", (const void *)script); + return _colorCodeFlag1; +} + +int KyraEngine_HoF::o2_setColorCodeFlag1(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getColorCodeFlag1(%p) (%d)", (const void *)script, stackPos(0)); + _colorCodeFlag1 = stackPos(0); + return 0; +} + +int KyraEngine_HoF::o2_getColorCodeFlag2(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getColorCodeFlag2(%p) ()", (const void *)script); + return _colorCodeFlag2; +} + +int KyraEngine_HoF::o2_setColorCodeFlag2(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getColorCodeFlag2(%p) (%d)", (const void *)script, stackPos(0)); + _colorCodeFlag2 = stackPos(0); + return 0; +} + +int KyraEngine_HoF::o2_getColorCodeValue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getColorCodeValue(%p) (%d)", (const void *)script, stackPos(0)); + return _presetColorCode[stackPos(0)]; +} + +int KyraEngine_HoF::o2_setColorCodeValue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setColorCodeValue(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _presetColorCode[stackPos(0)] = stackPos(1) & 0xFF; + return stackPos(1) & 0xFF; +} + +int KyraEngine_HoF::o2_countItemInstances(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemInstances(%p) (%d)", (const void *)script, stackPos(0)); + Item item = stackPos(0); + + int count = 0; + for (int i = 0; i < 20; ++i) { + if (_mainCharacter.inventory[i] == item) + ++count; + } + + if (_itemInHand == item) + ++count; + + for (int i = 0; i < 30; ++i) { + if (_itemList[i].id == item) + ++count; + } + + if (_hiddenItems[0] == item && _newChapterFile == 1) + ++count; + if (_hiddenItems[1] == item && _newChapterFile == 1) + ++count; + if (_hiddenItems[2] == item && _newChapterFile == 2) + ++count; + if (_hiddenItems[3] == item && _newChapterFile == 2) + ++count; + if (_hiddenItems[4] == item && _newChapterFile == 1) + ++count; + + return count; +} + +int KyraEngine_HoF::o2_removeItemFromScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_removeItemFromScene(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int scene = stackPos(0); + const uint16 item = stackPos(1); + for (int i = 0; i < 30; ++i) { + if (_itemList[i].sceneId == scene && _itemList[i].id == item) + _itemList[i].id = kItemNone; + } + return 0; +} + +int KyraEngine_HoF::o2_initObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_initObject(%p) (%d)", (const void *)script, stackPos(0)); + initTalkObject(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_npcChat(EMCState *script) { + if (_flags.isTalkie) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_npcChat(%p) ('%s', %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), _vocHigh, stackPos(2)); + npcChatSequence(stackPosString(0), stackPos(1), _vocHigh, stackPos(2)); + } else { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_npcChat(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + npcChatSequence(stackPosString(0), stackPos(1)); + } + return 0; +} + +int KyraEngine_HoF::o2_deinitObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_deinitObject(%p) (%d)", (const void *)script, stackPos(0)); + deinitTalkObject(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_playTimSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_playTimSequence(%p) ('%s')", (const void *)script, stackPosString(0)); + playTim(stackPosString(0)); + return 0; +} + +int KyraEngine_HoF::o2_makeBookOrCauldronAppear(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_makeBookOrCauldronAppear(%p) (%d)", (const void *)script, stackPos(0)); + seq_makeBookOrCauldronAppear(stackPos(0)); + return 0; +} + +int KyraEngine_HoF::o2_resetInputColorCode(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_resetInputColorCode(%p)", (const void *)script); + memset(_inputColorCode, 255, 7); + return 0; +} + +int KyraEngine_HoF::o2_mushroomEffect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_mushroomEffect(%p)", (const void *)script); + _screen->copyPalette(2, 0); + + for (int i = 1; i < 768; i += 3) + _screen->getPalette(0)[i] = 0; + snd_playSoundEffect(106); + _screen->fadePalette(_screen->getPalette(0), 90, &_updateFunctor); + _screen->copyPalette(0, 2); + + for (int i = 0; i < 768; i += 3) { + _screen->getPalette(0)[i] = _screen->getPalette(0)[i + 1] = 0; + _screen->getPalette(0)[i + 2] += (((int8)_screen->getPalette(0)[i + 2]) >> 1); + if (_screen->getPalette(0)[i + 2] > 63) + _screen->getPalette(0)[i + 2] = 63; + } + snd_playSoundEffect(106); + _screen->fadePalette(_screen->getPalette(0), 90, &_updateFunctor); + + _screen->copyPalette(0, 2); + _screen->fadePalette(_screen->getPalette(0), 30, &_updateFunctor); + + return 0; +} + +int KyraEngine_HoF::o2_customChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_customChat(%p) ('%s', %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2)); + strcpy((char *)_unkBuf500Bytes, stackPosString(0)); + _chatText = (char *)_unkBuf500Bytes; + _chatObject = stackPos(1); + + _chatVocHigh = _chatVocLow = -1; + objectChatInit(_chatText, _chatObject, _vocHigh, stackPos(2)); + playVoice(_vocHigh, stackPos(2)); + return 0; +} + +int KyraEngine_HoF::o2_customChatFinish(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_customChatFinish(%p) ()", (const void *)script); + _text->restoreScreen(); + _chatText = 0; + _chatObject = -1; + return 0; +} + +int KyraEngine_HoF::o2_setupSceneAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setupSceneAnimation(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, '%s')", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9), stackPos(10), stackPos(11), stackPosString(12)); + const int index = stackPos(0); + const uint16 flags = stackPos(1); + + restorePage3(); + + SceneAnim &anim = _sceneAnims[index]; + anim.flags = flags; + anim.x = stackPos(2); + anim.y = stackPos(3); + anim.x2 = stackPos(4); + anim.y2 = stackPos(5); + anim.width = stackPos(6); + anim.height = stackPos(7); + anim.specialSize = stackPos(9); + anim.shapeIndex = stackPos(11); + if (stackPosString(12)) + strcpy(anim.filename, stackPosString(12)); + + if (flags & 0x40) { + _sceneAnimMovie[index]->open(stackPosString(12), 0, 0); + if (_sceneAnimMovie[index]->xAdd() || _sceneAnimMovie[index]->yAdd()) + anim.wsaFlag = 1; + else + anim.wsaFlag = 0; + } + + AnimObj *obj = &_animObjects[1+index]; + obj->enabled = 1; + obj->needRefresh = 1; + obj->specialRefresh = 1; + obj->animFlags = anim.flags & 8; + + if (anim.flags & 2) + obj->flags = 0x800; + else + obj->flags = 0; + + if (anim.flags & 4) + obj->flags |= 1; + + obj->xPos1 = anim.x; + obj->yPos1 = anim.y; + + if ((anim.flags & 0x20) && anim.shapeIndex >= 0) + obj->shapePtr = _sceneShapeTable[anim.shapeIndex]; + else + obj->shapePtr = 0; + + if (anim.flags & 0x40) { + obj->shapeIndex3 = anim.shapeIndex; + obj->animNum = index; + } else { + obj->shapeIndex3 = 0xFFFF; + obj->animNum = 0xFFFF; + } + + obj->shapeIndex2 = 0xFFFF; + obj->xPos2 = obj->xPos3 = anim.x2; + obj->yPos2 = obj->yPos3 = anim.y2; + obj->width = anim.width; + obj->height = anim.height; + obj->width2 = obj->height2 = anim.specialSize; + + _animList = addToAnimListSorted(_animList, obj); + obj->needRefresh = 1; + obj->specialRefresh = 1; + return 0; +} + +int KyraEngine_HoF::o2_stopSceneAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_stopSceneAnimation(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int index = stackPos(0); + AnimObj &obj = _animObjects[1+index]; + restorePage3(); + obj.shapeIndex3 = 0xFFFF; + obj.animNum = 0xFFFF; + obj.needRefresh = 1; + obj.specialRefresh = 1; + if (stackPos(1)) + refreshAnimObjectsIfNeed(); + obj.enabled = 0; + _animList = deleteAnimListEntry(_animList, &_animObjects[1+index]); + + if (_sceneAnimMovie[index]->opened()) + _sceneAnimMovie[index]->close(); + + return 0; +} + +int KyraEngine_HoF::o2_processPaletteIndex(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_processPaletteIndex(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + Palette &palette = _screen->getPalette(0); + + const int index = stackPos(0); + const bool updatePalette = (stackPos(4) != 0); + const int delayTime = stackPos(5); + + palette[index*3+0] = (stackPos(1) * 0x3F) / 100; + palette[index*3+1] = (stackPos(2) * 0x3F) / 100; + palette[index*3+2] = (stackPos(3) * 0x3F) / 100; + + if (updatePalette) { + if (delayTime > 0) + _screen->fadePalette(palette, delayTime, &_updateFunctor); + else + _screen->setScreenPalette(palette); + } + + return 0; +} + +int KyraEngine_HoF::o2_updateTwoSceneAnims(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_updateTwoSceneAnims(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + updateSceneAnim(stackPos(0), stackPos(1)); + updateSceneAnim(stackPos(2), stackPos(3)); + _specialSceneScriptRunFlag = false; + return 0; +} + +int KyraEngine_HoF::o2_getRainbowRoomData(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getRainbowRoomData(%p) (%d)", (const void *)script, stackPos(0)); + return _rainbowRoomData[stackPos(0)]; +} + +int KyraEngine_HoF::o2_drawSceneShapeEx(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_drawSceneShapeEx(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + const int itemShape = stackPos(0) + 64; + const int x = stackPos(1); + const int y = stackPos(2); + const bool skipFronUpdate = (stackPos(3) != 0); + + _screen->drawShape(2, _sceneShapeTable[6], x, y, 2, 0); + _screen->drawShape(2, getShapePtr(itemShape), x+2, y+2, 2, 0); + + if (!skipFronUpdate) { + _screen->copyRegion(x, y, x, y, 0x15, 0x14, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } + + return 0; +} + +int KyraEngine_HoF::o2_midiSoundFadeout(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_midiSoundFadeout(%p) ()", (const void *)script); + if (!stackPos(0)) { + if ((_sound->getMusicType() == Sound::kMidiMT32 || _sound->getMusicType() == Sound::kMidiGM) && + (_sound->getSfxType() == Sound::kMidiMT32 || _sound->getSfxType() == Sound::kMidiGM)) { + _sound->beginFadeOut(); + delay(2000, true); + _lastMusicCommand = -1; + } else { + return 0; + } + } + + return 1; +} + +int KyraEngine_HoF::o2_getSfxDriver(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getSfxDriver(%p) ()", (const void *)script); + if (_sound->getSfxType() == Sound::kAdLib) + return 1; + else if (_sound->getSfxType() == Sound::kPCSpkr) + return 4; + else if (_sound->getSfxType() == Sound::kMidiMT32) + return 6; + else if (_sound->getSfxType() == Sound::kMidiGM) + return 7; + // TODO: find nice default value + return 0; +} + +int KyraEngine_HoF::o2_getVocSupport(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getVocSupport(%p) ()", (const void *)script); + // we always support VOC file playback + return 1; +} + +int KyraEngine_HoF::o2_getMusicDriver(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getMusicDriver(%p) ()", (const void *)script); + if (_sound->getMusicType() == Sound::kAdLib) + return 1; + else if (_sound->getMusicType() == Sound::kPCSpkr) + return 4; + else if (_sound->getMusicType() == Sound::kMidiMT32) + return 6; + else if (_sound->getMusicType() == Sound::kMidiGM) + return 7; + // TODO: find nice default value + return 0; +} + +int KyraEngine_HoF::o2_zanthiaChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_zanthiaChat(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + objectChat(stackPosString(0), 0, _vocHigh, stackPos(1)); + return 0; +} + +int KyraEngine_HoF::o2_isVoiceEnabled(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_isVoiceEnabled(%p) ()", (const void *)script); + return speechEnabled() ? 1 : 0; +} + +int KyraEngine_HoF::o2_isVoicePlaying(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_isVoicePlaying(%p) ()", (const void *)script); + return (snd_voiceIsPlaying() && !skipFlag()) ? 1 : 0; +} + +int KyraEngine_HoF::o2_stopVoicePlaying(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_stopVoicePlaying(%p) ()", (const void *)script); + snd_stopVoice(); + return 0; +} + +int KyraEngine_HoF::o2_getGameLanguage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_getGameLanguage(%p) ()", (const void *)script); + return _lang; +} + +int KyraEngine_HoF::o2_demoFinale(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_demoFinale(%p) ()", (const void *)script); + if (!_flags.isDemo) + return 0; + + int tmpSize; + const char *const *strings = _staticres->loadStrings(k2IngameTlkDemoStrings, tmpSize); + assert(strings); + + _screen->clearPage(0); + _screen->loadPalette("THANKS.COL", _screen->getPalette(0)); + _screen->loadBitmap("THANKS.CPS", 3, 3, 0); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + + _screen->_curPage = 0; + int y = _lang == 1 ? 70 : 65; + for (int i = 0; i < 6; i++) + _text->printText(strings[i], _text->getCenterStringX(strings[i], 1, 319), y + i * 10, 255, 207, 0); + + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->updateScreen(); + + _eventList.clear(); + while (!skipFlag() && !shouldQuit()) + delay(10); + + _sound->beginFadeOut(); + _screen->fadeToBlack(); + + _runFlag = 0; + return 0; +} + +int KyraEngine_HoF::o2_dummy(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_dummy(%p) ()", (const void *)script); + return 0; +} + +#pragma mark - + +int KyraEngine_HoF::o2a_setCharacterFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2a_setCharacterFrame(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _animNewFrame = stackPos(0); + _animDelayTime = stackPos(1); + _animNeedUpdate = true; + return 0; +} + +#pragma mark - + +int KyraEngine_HoF::t2_initChat(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::t2_initChat(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + _chatText = (const char *)tim->text + READ_LE_UINT16(tim->text + (param[0] << 1)); + _chatObject = param[1]; + + if (_flags.lang == Common::JA_JPN) { + for (int i = 0; i < _ingameTimJpStrSize; i += 2) { + if (!scumm_stricmp(_chatText, _ingameTimJpStr[i])) + _chatText = _ingameTimJpStr[i + 1]; + } + } + + objectChatInit(_chatText, _chatObject); + return 0; +} + +int KyraEngine_HoF::t2_updateSceneAnim(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::t2_updateSceneAnim(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], param[1]); + updateSceneAnim(param[1], param[0]); + return 0; +} + +int KyraEngine_HoF::t2_resetChat(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::t2_resetChat(%p, %p) ()", (const void *)tim, (const void *)param); + _text->restoreScreen(); + _chatText = 0; + _chatObject = -1; + return 0; +} + +int KyraEngine_HoF::t2_playSoundEffect(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::t2_playSoundEffect(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + snd_playSoundEffect(*param); + return 0; +} + +#pragma mark - + +typedef Common::Functor1Mem<EMCState *, int, KyraEngine_HoF> OpcodeV2; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV2(this, &KyraEngine_HoF::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV2(this, 0)) + +typedef Common::Functor2Mem<const TIM *, const uint16 *, int, KyraEngine_HoF> TIMOpcodeV2; +#define OpcodeTim(x) _timOpcodes.push_back(new TIMOpcodeV2(this, &KyraEngine_HoF::x)) +#define OpcodeTimUnImpl() _timOpcodes.push_back(new TIMOpcodeV2(this, 0)) + +void KyraEngine_HoF::setupOpcodeTable() { + Common::Array<const Opcode *> *table = 0; + + _opcodes.reserve(176); + SetOpcodeTable(_opcodes); + // 0x00 + Opcode(o2_setCharacterFacingRefresh); + Opcode(o2_setCharacterPos); + Opcode(o2_defineObject); + Opcode(o2_refreshCharacter); + // 0x04 + Opcode(o2_getCharacterX); + Opcode(o2_getCharacterY); + Opcode(o2_getCharacterFacing); + Opcode(o2_getCharacterScene); + // 0x08 + Opcode(o2_setSceneComment); + OpcodeUnImpl(); + OpcodeUnImpl(); + Opcode(o2_setCharacterAnimFrame); + // 0x0C + Opcode(o2_setCharacterFacingOverwrite); + Opcode(o2_trySceneChange); + Opcode(o2_moveCharacter); + Opcode(o2_customCharacterChat); + // 0x10 + Opcode(o2_soundFadeOut); + Opcode(o2_showChapterMessage); + Opcode(o2_restoreTalkTextMessageBkgd); + OpcodeUnImpl(); + // 0x14 + Opcode(o2_wsaClose); + Opcode(o2_backUpScreen); + Opcode(o2_restoreScreen); + Opcode(o2_displayWsaFrame); + // 0x18 + Opcode(o2_displayWsaSequentialFramesLooping); + Opcode(o2_wsaOpen); + Opcode(o2_displayWsaSequentialFrames); + Opcode(o2_displayWsaSequence); + // 0x1C + Opcode(o2_addItemToInventory); + Opcode(o2_drawShape); + Opcode(o2_addItemToCurScene); + Opcode(o2_dummy); // the original used this opcode to limit the mouse range temporary, + // since that is of no use and not really important we just use a dummy here + // 0x20 + Opcode(o2_checkForItem); + Opcode(o2_loadSoundFile); + Opcode(o2_removeSlotFromInventory); + Opcode(o2_defineItem); + // 0x24 + Opcode(o2_removeItemFromInventory); + Opcode(o2_countItemInInventory); + Opcode(o2_countItemsInScene); + Opcode(o1_queryGameFlag); + // 0x28 + Opcode(o1_resetGameFlag); + Opcode(o1_setGameFlag); + Opcode(o1_setHandItem); + Opcode(o1_removeHandItem); + // 0x2C + Opcode(o1_getMouseState); + Opcode(o1_hideMouse); + Opcode(o2_addSpecialExit); + Opcode(o1_setMousePos); + // 0x30 + Opcode(o1_showMouse); + OpcodeUnImpl(); + Opcode(o2_wipeDownMouseItem); + Opcode(o2_getElapsedSecs); + // 0x34 + Opcode(o2_getTimerDelay); + Opcode(o1_playSoundEffect); + Opcode(o2_delaySecs); + Opcode(o2_delay); + // 0x38 + Opcode(o2_dummy); + Opcode(o2_setTimerDelay); + Opcode(o2_setScaleTableItem); + Opcode(o2_setDrawLayerTableItem); + // 0x3C + Opcode(o2_setCharPalEntry); + Opcode(o2_loadZShapes); + Opcode(o2_drawSceneShape); + Opcode(o2_drawSceneShapeOnPage); + // 0x40 + Opcode(o2_disableAnimObject); + Opcode(o2_enableAnimObject); + Opcode(o2_dummy); + Opcode(o2_loadPalette384); + // 0x44 + Opcode(o2_setPalette384); + Opcode(o2_restoreBackBuffer); + Opcode(o2_backUpInventoryGfx); + Opcode(o2_disableSceneAnim); + // 0x48 + Opcode(o2_enableSceneAnim); + Opcode(o2_restoreInventoryGfx); + Opcode(o2_setSceneAnimPos2); + Opcode(o2_update); + // 0x4C + OpcodeUnImpl(); + Opcode(o2_fadeScenePal); + Opcode(o2_dummy); + Opcode(o2_dummy); + // 0x50 + Opcode(o2_enterNewScene); + Opcode(o2_switchScene); + Opcode(o2_getShapeFlag1); + Opcode(o2_setPathfinderFlag); + // 0x54 + Opcode(o2_getSceneExitToFacing); + Opcode(o2_setLayerFlag); + Opcode(o2_setZanthiaPos); + Opcode(o2_loadMusicTrack); + // 0x58 + Opcode(o1_playWanderScoreViaMap); + Opcode(o1_playSoundEffect); + Opcode(o2_setSceneAnimPos); + Opcode(o1_blockInWalkableRegion); + // 0x5C + Opcode(o1_blockOutWalkableRegion); + OpcodeUnImpl(); + Opcode(o2_setCauldronState); + Opcode(o2_showItemString); + // 0x60 + Opcode(o1_getRand); + Opcode(o2_isAnySoundPlaying); + Opcode(o1_setDeathHandler); + Opcode(o2_setDrawNoShapeFlag); + // 0x64 + Opcode(o2_setRunFlag); + Opcode(o2_showLetter); + OpcodeUnImpl(); + Opcode(o1_fillRect); + // 0x68 + OpcodeUnImpl(); + OpcodeUnImpl(); + Opcode(o2_playFireflyScore); + Opcode(o2_waitForConfirmationClick); + // 0x6C + Opcode(o2_encodeShape); + Opcode(o2_defineRoomEntrance); + Opcode(o2_runAnimationScript); + Opcode(o2_setSpecialSceneScriptRunTime); + // 0x70 + Opcode(o2_defineSceneAnim); + Opcode(o2_updateSceneAnim); + Opcode(o2_updateSceneAnim); + Opcode(o2_addToSceneAnimPosAndUpdate); + // 0x74 + Opcode(o2_useItemOnMainChar); + Opcode(o2_startDialogue); + Opcode(o2_randomSceneChat); + Opcode(o2_setDlgIndex); + // 0x78 + Opcode(o2_getDlgIndex); + Opcode(o2_defineScene); + Opcode(o2_addCauldronStateTableEntry); + Opcode(o2_setCountDown); + // 0x7C + Opcode(o2_getCountDown); + Opcode(o2_dummy); + Opcode(o2_dummy); + Opcode(o2_pressColorKey); + // 0x80 + Opcode(o2_objectChat); + Opcode(o2_changeChapter); + Opcode(o2_getColorCodeFlag1); + Opcode(o2_setColorCodeFlag1); + // 0x84 + Opcode(o2_getColorCodeFlag2); + Opcode(o2_setColorCodeFlag2); + Opcode(o2_getColorCodeValue); + Opcode(o2_setColorCodeValue); + // 0x88 + Opcode(o2_countItemInstances); + Opcode(o2_removeItemFromScene); + Opcode(o2_initObject); + Opcode(o2_npcChat); + // 0x8C + Opcode(o2_deinitObject); + Opcode(o2_playTimSequence); + Opcode(o2_makeBookOrCauldronAppear); + Opcode(o2_setSpecialSceneScriptState); + // 0x90 + Opcode(o2_clearSpecialSceneScriptState); + Opcode(o2_querySpecialSceneScriptState); + Opcode(o2_resetInputColorCode); + Opcode(o2_setHiddenItemsEntry); + // 0x94 + Opcode(o2_getHiddenItemsEntry); + Opcode(o2_mushroomEffect); + Opcode(o2_wsaClose); + Opcode(o2_meanWhileScene); + // 0x98 + Opcode(o2_customChat); + Opcode(o2_customChatFinish); + Opcode(o2_setupSceneAnimation); + Opcode(o2_stopSceneAnimation); + // 0x9C + Opcode(o2_disableTimer); + Opcode(o2_enableTimer); + Opcode(o2_setTimerCountdown); + Opcode(o2_processPaletteIndex); + // 0xA0 + Opcode(o2_updateTwoSceneAnims); + Opcode(o2_getRainbowRoomData); + Opcode(o2_drawSceneShapeEx); + Opcode(o2_midiSoundFadeout); + // 0xA4 + Opcode(o2_getSfxDriver); + Opcode(o2_getVocSupport); + Opcode(o2_getMusicDriver); + Opcode(o2_setVocHigh); + // 0xA8 + Opcode(o2_getVocHigh); + Opcode(o2_zanthiaChat); + Opcode(o2_isVoiceEnabled); + Opcode(o2_isVoicePlaying); + // 0xAC + Opcode(o2_stopVoicePlaying); + Opcode(o2_getGameLanguage); + Opcode(o2_demoFinale); + Opcode(o2_dummy); + + _opcodesAnimation.reserve(6); + SetOpcodeTable(_opcodesAnimation); + + // 0x00 + Opcode(o2a_setAnimationShapes); + Opcode(o2a_setCharacterFrame); + Opcode(o1_playSoundEffect); + Opcode(o2_fadeScenePal); + // 0x04 + _flags.isTalkie ? Opcode(o2a_setResetFrame) : Opcode(o2_dummy); + Opcode(o2_dummy); + + // ---- TIM opcodes + + _timOpcodes.reserve(4); + // 0x00 + OpcodeTim(t2_initChat); + OpcodeTim(t2_updateSceneAnim); + OpcodeTim(t2_resetChat); + OpcodeTim(t2_playSoundEffect); +} + +} // End of namespace Kyra diff --git a/engines/kyra/script/script_lok.cpp b/engines/kyra/script/script_lok.cpp new file mode 100644 index 0000000000..325ee67c9e --- /dev/null +++ b/engines/kyra/script/script_lok.cpp @@ -0,0 +1,1962 @@ +/* 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 "kyra/engine/kyra_lok.h" +#include "kyra/engine/sprites.h" +#include "kyra/graphics/wsamovie.h" +#include "kyra/graphics/animator_lok.h" +#include "kyra/text/text.h" +#include "kyra/engine/timer.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" + +namespace Kyra { + +int KyraEngine_LoK::o1_magicInMouseItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_magicInMouseItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + magicInMouseItem(stackPos(0), stackPos(1), -1); + return 0; +} + +int KyraEngine_LoK::o1_characterSays(EMCState *script) { + resetSkipFlag(); + if (_flags.isTalkie) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_characterSays(%p) (%d, '%s', %d, %d)", (const void *)script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3)); + characterSays(stackPos(0), stackPosString(1), stackPos(2), stackPos(3)); + } else { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_characterSays(%p) ('%s', %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2)); + const char *string = stackPosString(0); + + if ((_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) && _flags.lang == Common::JA_JPN) { + static const uint8 townsString1[] = { + 0x83, 0x75, 0x83, 0x89, 0x83, 0x93, 0x83, 0x83, 0x93, 0x81, + 0x41, 0x82, 0xDC, 0x82, 0xBD, 0x97, 0x88, 0x82, 0xBD, 0x82, + 0xCC, 0x82, 0xA9, 0x81, 0x48, 0x00, 0x00, 0x00 + }; + static const uint8 townsString2[] = { + 0x83, 0x75, 0x83, 0x89, 0x83, 0x93, 0x83, 0x5C, 0x83, 0x93, + 0x81, 0x41, 0x82, 0xDC, 0x82, 0xBD, 0x97, 0x88, 0x82, 0xBD, + 0x82, 0xCC, 0x82, 0xA9, 0x81, 0x48, 0x00, 0x00 + }; + + if (strncmp((const char *)townsString1, string, sizeof(townsString1)) == 0) + string = (const char *)townsString2; + } + + characterSays(-1, string, stackPos(1), stackPos(2)); + } + + return 0; +} + +int KyraEngine_LoK::o1_delay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_delay(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (stackPos(1)) { + warning("STUB: special o1_delay"); + // delete this after correct implementing + delayWithTicks(stackPos(0)); + } else { + delayWithTicks(stackPos(0)); + } + return 0; +} + +int KyraEngine_LoK::o1_drawSceneAnimShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_drawSceneAnimShape(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + _screen->drawShape(stackPos(4), _sprites->_sceneShapes[stackPos(0)], stackPos(1), stackPos(2), 0, (stackPos(3) != 0) ? 1 : 0); + return 0; +} + +int KyraEngine_LoK::o1_runNPCScript(EMCState *script) { + warning("STUB: o1_runNPCScript"); + return 0; +} + +int KyraEngine_LoK::o1_setSpecialExitList(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setSpecialExitList(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9)); + + for (int i = 0; i < 10; ++i) + _exitList[i] = stackPos(i); + _exitListPtr = _exitList; + + return 0; +} + +int KyraEngine_LoK::o1_walkPlayerToPoint(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_walkPlayerToPoint(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + + int normalTimers = stackPos(2); + if (!normalTimers) { + _timer->disable(19); + _timer->disable(14); + _timer->disable(18); + } + + int reinitScript = handleSceneChange(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + + if (!normalTimers) { + _timer->enable(19); + _timer->enable(14); + _timer->enable(18); + } + + if (reinitScript) + _emc->init(script, script->dataPtr); + + if (_sceneChangeState) { + _sceneChangeState = 0; + return 1; + } + return 0; +} + +int KyraEngine_LoK::o1_dropItemInScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_dropItemInScene(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int item = stackPos(0); + int xpos = stackPos(1); + int ypos = stackPos(2); + + byte freeItem = findFreeItemInScene(_currentCharacter->sceneId); + if (freeItem != 0xFF) { + int sceneId = _currentCharacter->sceneId; + Room *room = &_roomTable[sceneId]; + room->itemsXPos[freeItem] = xpos; + room->itemsYPos[freeItem] = ypos; + room->itemsTable[freeItem] = item; + + _animator->animAddGameItem(freeItem, sceneId); + _animator->updateAllObjectShapes(); + } else { + if (item == 43) + placeItemInGenericMapScene(item, 0); + else + placeItemInGenericMapScene(item, 1); + } + return 0; +} + +int KyraEngine_LoK::o1_drawAnimShapeIntoScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_drawAnimShapeIntoScene(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + _animator->restoreAllObjectBackgrounds(); + int shape = stackPos(0); + int xpos = stackPos(1); + int ypos = stackPos(2); + int flags = (stackPos(3) != 0) ? 1 : 0; + _screen->drawShape(2, _sprites->_sceneShapes[shape], xpos, ypos, 0, flags); + _screen->drawShape(0, _sprites->_sceneShapes[shape], xpos, ypos, 0, flags); + _animator->flagAllObjectsForBkgdChange(); + _animator->preserveAnyChangedBackgrounds(); + _animator->flagAllObjectsForRefresh(); + _animator->updateAllObjectShapes(); + return 0; +} + +int KyraEngine_LoK::o1_savePageToDisk(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_savePageToDisk(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + _screen->savePageToDisk(stackPosString(0), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_sceneAnimOn(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_sceneAnimOn(%p) (%d)", (const void *)script, stackPos(0)); + _sprites->_anims[stackPos(0)].play = true; + return 0; +} + +int KyraEngine_LoK::o1_sceneAnimOff(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_sceneAnimOff(%p) (%d)", (const void *)script, stackPos(0)); + _sprites->_anims[stackPos(0)].play = false; + return 0; +} + +int KyraEngine_LoK::o1_getElapsedSeconds(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getElapsedSeconds(%p) ()", (const void *)script); + return _system->getMillis() / 1000; +} + +int KyraEngine_LoK::o1_mouseIsPointer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_mouseIsPointer(%p) ()", (const void *)script); + return (_itemInHand == kItemNone); +} + +int KyraEngine_LoK::o1_runSceneAnimUntilDone(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_runSceneAnimUntilDone(%p) (%d)", (const void *)script, stackPos(0)); + _screen->hideMouse(); + _animator->restoreAllObjectBackgrounds(); + _sprites->_anims[stackPos(0)].play = true; + _animator->sprites()[stackPos(0)].active = 1; + _animator->flagAllObjectsForBkgdChange(); + _animator->preserveAnyChangedBackgrounds(); + while (_sprites->_anims[stackPos(0)].play) { + _sprites->updateSceneAnims(); + _animator->updateAllObjectShapes(); + delay(10); + } + _animator->restoreAllObjectBackgrounds(); + _screen->showMouse(); + return 0; +} + +int KyraEngine_LoK::o1_fadeSpecialPalette(EMCState *script) { + if (_flags.platform == Common::kPlatformAmiga) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_fadeSpecialPalette(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (_currentCharacter->sceneId != 45) { + if (stackPos(0) == 13) { + _screen->copyPalette(0, 12); + _screen->setScreenPalette(_screen->getPalette(0)); + } + } else { + setupZanthiaPalette(stackPos(0)); + _screen->getPalette(0).copy(_screen->getPalette(4), 12, 1); + _screen->fadePalette(_screen->getPalette(0), 2); + } + } else { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_fadeSpecialPalette(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + _screen->fadeSpecialPalette(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + } + return 0; +} + +int KyraEngine_LoK::o1_phaseInSameScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_phaseInSameScene(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + transcendScenes(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_setScenePhasingFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setScenePhasingFlag(%p) ()", (const void *)script); + _scenePhasingFlag = 1; + return 1; +} + +int KyraEngine_LoK::o1_resetScenePhasingFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_resetScenePhasingFlag(%p) ()", (const void *)script); + _scenePhasingFlag = 0; + return 0; +} + +int KyraEngine_LoK::o1_queryScenePhasingFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_queryScenePhasingFlag(%p) ()", (const void *)script); + return _scenePhasingFlag; +} + +int KyraEngine_LoK::o1_sceneToDirection(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_sceneToDirection(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + assert(stackPos(0) < _roomTableSize); + Room *curRoom = &_roomTable[stackPos(0)]; + uint16 returnValue = 0xFFFF; + switch (stackPos(1)) { + case 0: + returnValue = curRoom->northExit; + break; + + case 2: + returnValue = curRoom->eastExit; + break; + + case 4: + returnValue = curRoom->southExit; + break; + + case 6: + returnValue = curRoom->westExit; + break; + + default: + break; + } + if (returnValue == 0xFFFF) + return -1; + return returnValue; +} + +int KyraEngine_LoK::o1_setBirthstoneGem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setBirthstoneGem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int index = stackPos(0); + if (index < 4 && index >= 0) { + _birthstoneGemTable[index] = stackPos(1); + return 1; + } + return 0; +} + +int KyraEngine_LoK::o1_placeItemInGenericMapScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_placeItemInGenericMapScene(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + placeItemInGenericMapScene(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_setBrandonStatusBit(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setBrandonStatusBit(%p) (%d)", (const void *)script, stackPos(0)); + _brandonStatusBit |= stackPos(0); + return 0; +} + +int KyraEngine_LoK::o1_delaySecs(EMCState *script) { + if (_flags.isTalkie && speechEnabled()) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_voiceDelay(%p) (%d)", (const void *)script, stackPos(0)); + if (stackPos(0) == 0) { + snd_voiceWaitForFinish(true); + } else if (stackPos(0) < 0) { + uint32 time = ABS(stackPos(0)) * _tickLength; + delay(time, true); + } + } else { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_delaySecs(%p) (%d)", (const void *)script, stackPos(0)); + if (stackPos(0) >= 0 && !skipFlag()) + delay(stackPos(0) * 1000, true); + } + + resetSkipFlag(); + return 0; +} + +int KyraEngine_LoK::o1_getCharacterScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getCharacterScene(%p) (%d)", (const void *)script, stackPos(0)); + return _characterList[stackPos(0)].sceneId; +} + +int KyraEngine_LoK::o1_runNPCSubscript(EMCState *script) { + warning("STUB: o1_runNPCSubscript"); + return 0; +} + +int KyraEngine_LoK::o1_magicOutMouseItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_magicOutMouseItem(%p) (%d)", (const void *)script, stackPos(0)); + magicOutMouseItem(stackPos(0), -1); + return 0; +} + +int KyraEngine_LoK::o1_internalAnimOn(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_internalAnimOn(%p) (%d)", (const void *)script, stackPos(0)); + _animator->sprites()[stackPos(0)].active = 1; + return 0; +} + +int KyraEngine_LoK::o1_forceBrandonToNormal(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_forceBrandonToNormal(%p) ()", (const void *)script); + checkAmuletAnimFlags(); + return 0; +} + +int KyraEngine_LoK::o1_poisonDeathNow(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_poisonDeathNow(%p) ()", (const void *)script); + seq_poisonDeathNow(1); + return 0; +} + +int KyraEngine_LoK::o1_setScaleMode(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setScaleMode(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int len = stackPos(0); + int setValue1 = stackPos(1); + int start2 = stackPos(2); + int setValue2 = stackPos(3); + for (int i = 0; i < len; ++i) + _scaleTable[i] = setValue1; + int temp = setValue2 - setValue1; + int temp2 = start2 - len; + for (int i = len, offset = 0; i < start2; ++i, ++offset) + _scaleTable[i] = (offset * temp) / temp2 + setValue1; + for (int i = start2; i < 145; ++i) + _scaleTable[i] = setValue2; + _scaleMode = 1; + return _scaleMode; +} + +int KyraEngine_LoK::o1_openWSAFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_openWSAFile(%p) ('%s', %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3)); + + const char *filename = stackPosString(0); + int wsaIndex = stackPos(1); + + _movieObjects[wsaIndex]->open(filename, (stackPos(3) != 0) ? 1 : 0, 0); + assert(_movieObjects[wsaIndex]->opened()); + + return 0; +} + +int KyraEngine_LoK::o1_closeWSAFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_closeWSAFile(%p) (%d)", (const void *)script, stackPos(0)); + + int wsaIndex = stackPos(0); + if (_movieObjects[wsaIndex]) + _movieObjects[wsaIndex]->close(); + + return 0; +} + +int KyraEngine_LoK::o1_runWSAFromBeginningToEnd(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_runWSAFromBeginningToEnd(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + + _screen->hideMouse(); + + bool running = true; + + int xpos = stackPos(0); + int ypos = stackPos(1); + int waitTime = stackPos(2); + int wsaIndex = stackPos(3); + int worldUpdate = stackPos(4); + int wsaFrame = 0; + + while (running) { + const uint32 continueTime = waitTime * _tickLength + _system->getMillis(); + + _movieObjects[wsaIndex]->displayFrame(wsaFrame++, 0, xpos, ypos, 0, 0, 0); + if (wsaFrame >= _movieObjects[wsaIndex]->frames()) + running = false; + + delayUntil(continueTime, false, worldUpdate != 0); + } + + _screen->showMouse(); + + return 0; +} + +int KyraEngine_LoK::o1_displayWSAFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_displayWSAFrame(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + int frame = stackPos(0); + int xpos = stackPos(1); + int ypos = stackPos(2); + int waitTime = stackPos(3); + int wsaIndex = stackPos(4); + _screen->hideMouse(); + const uint32 continueTime = waitTime * _tickLength + _system->getMillis(); + _movieObjects[wsaIndex]->displayFrame(frame, 0, xpos, ypos, 0, 0, 0); + delayUntil(continueTime, false, true); + _screen->showMouse(); + return 0; +} + +int KyraEngine_LoK::o1_enterNewScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_enterNewScene(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + enterNewScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + return 0; +} + +int KyraEngine_LoK::o1_setSpecialEnterXAndY(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setSpecialEnterXAndY(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _brandonPosX = stackPos(0); + _brandonPosY = stackPos(1); + if (_brandonPosX + 1 == 0 && _brandonPosY + 1 == 0) + _currentCharacter->currentAnimFrame = 88; + return 0; +} + +int KyraEngine_LoK::o1_runWSAFrames(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_runWSAFrames(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + int xpos = stackPos(0); + int ypos = stackPos(1); + int delayTime = stackPos(2); + int startFrame = stackPos(3); + int endFrame = stackPos(4); + int wsaIndex = stackPos(5); + _screen->hideMouse(); + for (; startFrame <= endFrame; ++startFrame) { + const uint32 nextRun = _system->getMillis() + delayTime * _tickLength; + _movieObjects[wsaIndex]->displayFrame(startFrame, 0, xpos, ypos, 0, 0, 0); + delayUntil(nextRun, false, true); + } + _screen->showMouse(); + return 0; +} + +int KyraEngine_LoK::o1_popBrandonIntoScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_popBrandonIntoScene(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int changeScaleMode = stackPos(3); + int xpos = (int16)(stackPos(0) & 0xFFFC); + int ypos = (int16)(stackPos(1) & 0xFFFE); + int facing = stackPos(2); + _currentCharacter->x1 = _currentCharacter->x2 = xpos; + _currentCharacter->y1 = _currentCharacter->y2 = ypos; + _currentCharacter->facing = facing; + _currentCharacter->currentAnimFrame = 7; + int xOffset = _defaultShapeTable[0].xOffset; + int yOffset = _defaultShapeTable[0].yOffset; + int width = _defaultShapeTable[0].w << 3; + int height = _defaultShapeTable[0].h; + Animator_LoK::AnimObject *curAnim = _animator->actors(); + + if (changeScaleMode) { + curAnim->x1 = _currentCharacter->x1; + curAnim->y1 = _currentCharacter->y1; + _animator->_brandonScaleY = _scaleTable[_currentCharacter->y1]; + _animator->_brandonScaleX = _animator->_brandonScaleY; + + int animWidth = _animator->fetchAnimWidth(curAnim->sceneAnimPtr, _animator->_brandonScaleX) >> 1; + int animHeight = _animator->fetchAnimHeight(curAnim->sceneAnimPtr, _animator->_brandonScaleY); + + animWidth = (xOffset * animWidth) / width; + animHeight = (yOffset * animHeight) / height; + + curAnim->x2 = curAnim->x1 += animWidth; + curAnim->y2 = curAnim->y1 += animHeight; + } else { + curAnim->x2 = curAnim->x1 = _currentCharacter->x1 + xOffset; + curAnim->y2 = curAnim->y1 = _currentCharacter->y1 + yOffset; + } + + int scaleModeBackup = _scaleMode; + if (changeScaleMode) + _scaleMode = 1; + + _animator->animRefreshNPC(0); + _animator->preserveAllBackgrounds(); + _animator->prepDrawAllObjects(); + _animator->copyChangedObjectsForward(0); + + _scaleMode = scaleModeBackup; + + return 0; +} + +int KyraEngine_LoK::o1_restoreAllObjectBackgrounds(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_restoreAllObjectBackgrounds(%p) (%d)", (const void *)script, stackPos(0)); + int disable = stackPos(0); + int activeBackup = 0; + if (disable) { + activeBackup = _animator->actors()->active; + _animator->actors()->active = 0; + } + _animator->restoreAllObjectBackgrounds(); + if (disable) + _animator->actors()->active = activeBackup; + return 0; +} + +int KyraEngine_LoK::o1_setCustomPaletteRange(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCustomPaletteRange(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (_flags.platform == Common::kPlatformAmiga) { + if (_currentCharacter->sceneId == 45) { + setupZanthiaPalette(stackPos(0)); + } else if (stackPos(0) == 29) { + _screen->copyPalette(0, 11); + } else if (stackPos(0) == 13) { + _screen->copyPalette(0, 12); + } + } else { + if (!_specialPalettes[stackPos(0)]) + warning("KyraEngine_LoK::o1_setCustomPaletteRange(): Trying to use missing special palette %d", stackPos(0)); + else + _screen->getPalette(1).copy(_specialPalettes[stackPos(0)], 0, stackPos(2), stackPos(1)); + } + return 0; +} + +int KyraEngine_LoK::o1_loadPageFromDisk(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_loadPageFromDisk(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + _screen->loadPageFromDisk(stackPosString(0), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_customPrintTalkString(EMCState *script) { + if (_flags.isTalkie) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_customPrintTalkString(%p) (%d, '%s', %d, %d, %d)", (const void *)script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4) & 0xFF); + + if (speechEnabled()) { + snd_voiceWaitForFinish(); + snd_playVoiceFile(stackPos(0)); + } + + resetSkipFlag(); + if (textEnabled()) + _text->printTalkTextMessage(stackPosString(1), stackPos(2), stackPos(3), stackPos(4) & 0xFF, 0, 2); + } else { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_customPrintTalkString(%p) ('%s', %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF); + resetSkipFlag(); + _text->printTalkTextMessage(stackPosString(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF, 0, 2); + } + _screen->updateScreen(); + return 0; +} + +int KyraEngine_LoK::o1_restoreCustomPrintBackground(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_restoreCustomPrintBackground(%p) ()", (const void *)script); + _text->restoreTalkTextMessageBkgd(2, 0); + return 0; +} + +int KyraEngine_LoK::o1_getCharacterX(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getCharacterX(%p) (%d)", (const void *)script, stackPos(0)); + return _characterList[stackPos(0)].x1; +} + +int KyraEngine_LoK::o1_getCharacterY(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getCharacterY(%p) (%d)", (const void *)script, stackPos(0)); + return _characterList[stackPos(0)].y1; +} + +int KyraEngine_LoK::o1_setCharacterFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCharacterFacing(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int character = stackPos(0); + int facing = stackPos(1); + int newAnimFrame = stackPos(2); + + _animator->restoreAllObjectBackgrounds(); + if (newAnimFrame != -1) + _characterList[character].currentAnimFrame = newAnimFrame; + _characterList[character].facing = facing; + _animator->animRefreshNPC(character); + _animator->preserveAllBackgrounds(); + _animator->prepDrawAllObjects(); + _animator->copyChangedObjectsForward(0); + + return 0; +} + +int KyraEngine_LoK::o1_copyWSARegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_copyWSARegion(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + int xpos = stackPos(0); + int ypos = stackPos(1); + int width = stackPos(2); + int height = stackPos(3); + int srcPage = stackPos(4); + int dstPage = stackPos(5); + _screen->copyRegion(xpos, ypos, xpos, ypos, width, height, srcPage, dstPage); + return 0; +} + +int KyraEngine_LoK::o1_printText(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_printText(%p) ('%s', %d, %d, 0x%X, 0x%X)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + if (_flags.lang == Common::JA_JPN && stackPos(3) == 7) + _screen->printText(stackPosString(0), stackPos(1), stackPos(2), 0, 0x80); + else + _screen->printText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + _screen->updateScreen(); + return 0; +} + +int KyraEngine_LoK::o1_loadSoundFile(EMCState *script) { + warning("STUB: o1_loadSoundFile"); + return 0; +} + +int KyraEngine_LoK::o1_displayWSAFrameOnHidPage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_displayWSAFrameOnHidPage(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + int frame = stackPos(0); + int xpos = stackPos(1); + int ypos = stackPos(2); + int waitTime = stackPos(3); + int wsaIndex = stackPos(4); + + _screen->hideMouse(); + const uint32 continueTime = waitTime * _tickLength + _system->getMillis(); + _movieObjects[wsaIndex]->displayFrame(frame, 2, xpos, ypos, 0, 0, 0); + delayUntil(continueTime, false, true); + _screen->showMouse(); + + return 0; +} + +int KyraEngine_LoK::o1_displayWSASequentialFrames(EMCState *script) { + if (_flags.isTalkie) + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_displayWSASequentialFrames(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + else + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_displayWSASequentialFrames(%p) (%d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6)); + int startFrame = stackPos(0); + int endFrame = stackPos(1); + int xpos = stackPos(2); + int ypos = stackPos(3); + int waitTime = stackPos(4); + int wsaIndex = stackPos(5); + int maxTime = stackPos(6); + + if (_flags.isTalkie) { + int specialTime = stackPos(7); + if (specialTime) { + uint32 voiceTime = snd_getVoicePlayTime(); + if (voiceTime) { + int displayFrames = ABS(endFrame - startFrame) + 1; + displayFrames *= maxTime; + assert(displayFrames != 0); + + bool voiceSync = false; + + if (specialTime < 0) { + voiceSync = true; + specialTime = ABS(specialTime); + } + + voiceTime *= specialTime; + voiceTime /= 100; + + if (voiceSync) { + uint32 voicePlayedTime = _sound->voicePlayedTime(_speechHandle); + if (voicePlayedTime >= voiceTime) + voiceTime = 0; + else + voiceTime -= voicePlayedTime; + } + + waitTime = voiceTime / displayFrames; + waitTime /= _tickLength; + } + } + } + + if (maxTime - 1 <= 0) + maxTime = 1; + + // WORKAROUND for bug #1498221 "KYRA1: Glitches when meeting Zanthia". + // The original did not do a forced screen update after displaying a WSA + // frame while we have to do it, which makes Brandon disappear for a short + // moment. That is not supposed to happen. So we're not updating the + // screen for this special case. + // This is only an issue for the CD version, but since the floppy version + // does not use the specified paramaeters like these, it is safe to enable + // it for all versions. + if (startFrame == 18 && endFrame == 18 && waitTime == 10 && wsaIndex == 0 && _currentRoom == 45) { + _movieObjects[wsaIndex]->displayFrame(18, 0, xpos, ypos, 0, 0, 0); + // We call delayMillis manually here to avoid the screen getting + // updated. + _system->delayMillis(waitTime * _tickLength); + return 0; + } + + int curTime = 0; + _screen->hideMouse(); + while (curTime < maxTime) { + if (endFrame >= startFrame) { + int frame = startFrame; + while (endFrame >= frame) { + const uint32 continueTime = waitTime * _tickLength + _system->getMillis(); + _movieObjects[wsaIndex]->displayFrame(frame, 0, xpos, ypos, 0, 0, 0); + delayUntil(continueTime, false, true); + ++frame; + } + } else { + int frame = startFrame; + while (endFrame <= frame) { + const uint32 continueTime = waitTime * _tickLength + _system->getMillis(); + _movieObjects[wsaIndex]->displayFrame(frame, 0, xpos, ypos, 0, 0, 0); + delayUntil(continueTime, false, true); + --frame; + } + } + + if (skipFlag()) + break; + else + ++curTime; + } + _screen->showMouse(); + + return 0; +} + +int KyraEngine_LoK::o1_refreshCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_refreshCharacter(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int character = stackPos(0); + int animFrame = stackPos(1); + int newFacing = stackPos(2); + int updateShapes = stackPos(3); + _characterList[character].currentAnimFrame = animFrame; + if (newFacing != -1) + _characterList[character].facing = newFacing; + _animator->animRefreshNPC(character); + if (updateShapes) + _animator->updateAllObjectShapes(); + return 0; +} + +int KyraEngine_LoK::o1_internalAnimOff(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_internalAnimOff(%p) (%d)", (const void *)script, stackPos(0)); + _animator->sprites()[stackPos(0)].active = 0; + return 0; +} + +int KyraEngine_LoK::o1_changeCharactersXAndY(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_changeCharactersXAndY(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + Character *ch = &_characterList[stackPos(0)]; + int16 x = stackPos(1); + int16 y = stackPos(2); + if (x != -1 && y != -1) { + x &= 0xFFFC; + y &= 0xFFFE; + } + _animator->restoreAllObjectBackgrounds(); + ch->x1 = ch->x2 = x; + ch->y1 = ch->y2 = y; + _animator->preserveAllBackgrounds(); + return 0; +} + +int KyraEngine_LoK::o1_clearSceneAnimatorBeacon(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_clearSceneAnimatorBeacon(%p) ()", (const void *)script); + _sprites->_sceneAnimatorBeaconFlag = 0; + return 0; +} + +int KyraEngine_LoK::o1_querySceneAnimatorBeacon(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_querySceneAnimatorBeacon(%p) ()", (const void *)script); + return _sprites->_sceneAnimatorBeaconFlag; +} + +int KyraEngine_LoK::o1_refreshSceneAnimator(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_refreshSceneAnimator(%p) ()", (const void *)script); + _sprites->updateSceneAnims(); + _animator->updateAllObjectShapes(); + return 0; +} + +int KyraEngine_LoK::o1_placeItemInOffScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_placeItemInOffScene(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int item = stackPos(0); + int xpos = stackPos(1); + int ypos = stackPos(2); + int sceneId = stackPos(3); + + byte freeItem = findFreeItemInScene(sceneId); + if (freeItem != 0xFF) { + assert(sceneId < _roomTableSize); + Room *room = &_roomTable[sceneId]; + + room->itemsTable[freeItem] = item; + room->itemsXPos[freeItem] = xpos; + room->itemsYPos[freeItem] = ypos; + } + return 0; +} + +int KyraEngine_LoK::o1_wipeDownMouseItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_wipeDownMouseItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + _screen->hideMouse(); + wipeDownMouseItem(stackPos(1), stackPos(2)); + removeHandItem(); + _screen->showMouse(); + return 0; +} + +int KyraEngine_LoK::o1_placeCharacterInOtherScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_placeCharacterInOtherScene(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + int id = stackPos(0); + int sceneId = stackPos(1); + int xpos = (int16)(stackPos(2) & 0xFFFC); + int ypos = (int16)(stackPos(3) & 0xFFFE); + int facing = stackPos(4); + int animFrame = stackPos(5); + + _characterList[id].sceneId = sceneId; + _characterList[id].x1 = _characterList[id].x2 = xpos; + _characterList[id].y1 = _characterList[id].y2 = ypos; + _characterList[id].facing = facing; + _characterList[id].currentAnimFrame = animFrame; + return 0; +} + +int KyraEngine_LoK::o1_getKey(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getKey(%p) ()", (const void *)script); + + while (true) { + delay(10); + + if (skipFlag()) + break; + } + + resetSkipFlag(); + return 0; +} + +int KyraEngine_LoK::o1_specificItemInInventory(EMCState *script) { + warning("STUB: o1_specificItemInInventory"); + return 0; +} + +int KyraEngine_LoK::o1_popMobileNPCIntoScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_popMobileNPCIntoScene(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), (int16)(stackPos(4) & 0xFFFC), (int8)(stackPos(5) & 0xFE)); + int character = stackPos(0); + int sceneId = stackPos(1); + int animFrame = stackPos(2); + int facing = stackPos(3); + int16 xpos = (int16)(stackPos(4) & 0xFFFC); + int8 ypos = (int16)(stackPos(5) & 0xFFFE); + Character *curChar = &_characterList[character]; + + curChar->sceneId = sceneId; + curChar->currentAnimFrame = animFrame; + curChar->facing = facing; + curChar->x1 = curChar->x2 = xpos; + curChar->y1 = curChar->y2 = ypos; + + _animator->animAddNPC(character); + _animator->updateAllObjectShapes(); + return 0; +} + +int KyraEngine_LoK::o1_mobileCharacterInScene(EMCState *script) { + warning("STUB: o1_mobileCharacterInScene"); + return 0; +} + +int KyraEngine_LoK::o1_hideMobileCharacter(EMCState *script) { + warning("STUB: o1_hideMobileCharacter"); + return 0; +} + +int KyraEngine_LoK::o1_unhideMobileCharacter(EMCState *script) { + warning("STUB: o1_unhideMobileCharacter"); + return 0; +} + +int KyraEngine_LoK::o1_setCharacterLocation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCharacterLocation(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + Character *ch = &_characterList[stackPos(0)]; + Animator_LoK::AnimObject *animObj = &_animator->actors()[stackPos(0)]; + int newScene = stackPos(1); + if (_currentCharacter->sceneId == ch->sceneId) { + if (_currentCharacter->sceneId != newScene) + animObj->active = 0; + } else if (_currentCharacter->sceneId == newScene) { + if (_currentCharacter->sceneId != ch->sceneId) + animObj->active = 1; + } + + ch->sceneId = stackPos(1); + return 0; +} + +int KyraEngine_LoK::o1_walkCharacterToPoint(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_walkCharacterToPoint(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int character = stackPos(0); + int toX = stackPos(1); + int toY = stackPos(2); + _pathfinderFlag2 = 1; + uint32 nextFrame; + int findWayReturn = findWay(_characterList[character].x1, _characterList[character].y1, toX, toY, _movFacingTable, 150); + _pathfinderFlag2 = 0; + + if (_lastFindWayRet < findWayReturn) + _lastFindWayRet = findWayReturn; + if (findWayReturn == 0x7D00 || findWayReturn == 0) + return 0; + + int *curPos = _movFacingTable; + bool running = true; + while (running) { + bool forceContinue = false; + switch (*curPos) { + case 0: + _characterList[character].facing = 2; + break; + + case 1: + _characterList[character].facing = 1; + break; + + case 2: + _characterList[character].facing = 0; + break; + + case 3: + _characterList[character].facing = 7; + break; + + case 4: + _characterList[character].facing = 6; + break; + + case 5: + _characterList[character].facing = 5; + break; + + case 6: + _characterList[character].facing = 4; + break; + + case 7: + _characterList[character].facing = 3; + break; + + case 8: + running = 0; + break; + + default: + ++curPos; + forceContinue = true; + } + + if (forceContinue || !running) + continue; + + setCharacterPosition(character, 0); + ++curPos; + + delayUntil(nextFrame = _timer->getDelay(5 + character) * _tickLength + _system->getMillis(), true, true); + } + return 0; +} + +int KyraEngine_LoK::o1_specialEventDisplayBrynnsNote(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_specialEventDisplayBrynnsNote(%p) ()", (const void *)script); + _screen->hideMouse(); + _screen->savePageToDisk("HIDPAGE.TMP", 2); + _screen->savePageToDisk("SEENPAGE.TMP", 0); + if (_flags.isTalkie) { + if (_flags.lang == Common::EN_ANY || _flags.lang == Common::IT_ITA) + _screen->loadBitmap("NOTEENG.CPS", 3, 3, 0); + else if (_flags.lang == Common::FR_FRA) + _screen->loadBitmap("NOTEFRE.CPS", 3, 3, 0); + else if (_flags.lang == Common::DE_DEU) + _screen->loadBitmap("NOTEGER.CPS", 3, 3, 0); + } else { + _screen->loadBitmap("NOTE.CPS", 3, 3, 0); + } + _screen->copyRegion(63, 8, 63, 8, 194, 128, 2, 0); + _screen->updateScreen(); + _screen->showMouse(); + + if (_flags.platform != Common::kPlatformAmiga && !_flags.isTalkie && _flags.lang != Common::JA_JPN) + _screen->setFont(Screen::FID_6_FNT); + return 0; +} + +int KyraEngine_LoK::o1_specialEventRemoveBrynnsNote(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_specialEventRemoveBrynnsNote(%p) ()", (const void *)script); + _screen->hideMouse(); + _screen->loadPageFromDisk("SEENPAGE.TMP", 0); + _screen->loadPageFromDisk("HIDPAGE.TMP", 2); + _screen->updateScreen(); + _screen->showMouse(); + + if (_flags.platform != Common::kPlatformAmiga && !_flags.isTalkie && _flags.lang != Common::JA_JPN) + _screen->setFont(Screen::FID_8_FNT); + return 0; +} + +int KyraEngine_LoK::o1_setLogicPage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setLogicPage(%p) (%d)", (const void *)script, stackPos(0)); + _screen->_curPage = stackPos(0); + return stackPos(0); +} + +int KyraEngine_LoK::o1_fatPrint(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_fatPrint(%p) ('%s', %d, %d, %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + + // Workaround for bug #1582672 ("KYRA1: Text crippled and drawn wrong") + // I'm not sure how the original handles this, since it seems to call + // printText also, maybe it fails somewhere inside... + // TODO: fix the reason for this workaround + if (_currentRoom == 117) + return 0; + _text->printText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + return 0; +} + +int KyraEngine_LoK::o1_preserveAllObjectBackgrounds(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_preserveAllObjectBackgrounds(%p) ()", (const void *)script); + _animator->preserveAllBackgrounds(); + return 0; +} + +int KyraEngine_LoK::o1_updateSceneAnimations(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_updateSceneAnimations(%p) (%d)", (const void *)script, stackPos(0)); + int times = stackPos(0); + while (times--) { + _sprites->updateSceneAnims(); + _animator->updateAllObjectShapes(); + } + return 0; +} + +int KyraEngine_LoK::o1_sceneAnimationActive(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_sceneAnimationActive(%p) (%d)", (const void *)script, stackPos(0)); + return _sprites->_anims[stackPos(0)].play; +} + +int KyraEngine_LoK::o1_setCharacterMovementDelay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCharacterMovementDelay(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _timer->setDelay(stackPos(0) + 5, stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_getCharacterFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getCharacterFacing(%p) (%d)", (const void *)script, stackPos(0)); + return _characterList[stackPos(0)].facing; +} + +int KyraEngine_LoK::o1_bkgdScrollSceneAndMasksRight(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_bkgdScrollSceneAndMasksRight(%p) (%d)", (const void *)script, stackPos(0)); + _screen->copyBackgroundBlock(stackPos(0), 2, 0); + _screen->copyBackgroundBlock2(stackPos(0)); + // update the whole screen + _screen->copyRegion(7, 7, 7, 7, 305, 129, 3, 0); + // Don't do a screen update here, see bug #1910180 "KYRA1: Screen 'flash'" + // it would cause to draw the screen with a wrong palette and thus look + // strange for the user. Since this opcode should be just called on scene + // initialization anyway, there should be no problem with not updating the + // screen right now. + return 0; +} + +int KyraEngine_LoK::o1_dispelMagicAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_dispelMagicAnimation(%p) ()", (const void *)script); + seq_dispelMagicAnimation(); + return 0; +} + +int KyraEngine_LoK::o1_findBrightestFireberry(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_findBrightestFireberry(%p) ()", (const void *)script); + if (_currentCharacter->sceneId >= 187 && _currentCharacter->sceneId <= 198) + return 29; + + // The following rooms are only a "A fireberry bush" scene in the CD version + // of Kyrandia 1. In all other versions they are a usual dark cave, thus we do only + // return a glow value of "29" over here, when we are running a CD version. + if (_flags.isTalkie) { + if (_currentCharacter->sceneId == 133 || _currentCharacter->sceneId == 137 || + _currentCharacter->sceneId == 165 || _currentCharacter->sceneId == 173) + return 29; + } + + if (_itemInHand == 28) + return 28; + + int brightestFireberry = 107; + if (_itemInHand >= 29 && _itemInHand <= 33) + brightestFireberry = _itemInHand; + for (int i = 0; i < 10; ++i) { + uint8 item = _currentCharacter->inventoryItems[i]; + if (item == 0xFF) + continue; + if (item == 28) + return 28; + if (item >= 29 && item <= 33) { + if (item < brightestFireberry) + brightestFireberry = item; + } + } + assert(_currentCharacter->sceneId < _roomTableSize); + Room *curRoom = &_roomTable[_currentCharacter->sceneId]; + for (int i = 0; i < 12; ++i) { + uint8 item = curRoom->itemsTable[i]; + if (item == 0xFF) + continue; + if (item == 28) + return 28; + if (item >= 29 && item <= 33) { + if (item < brightestFireberry) + brightestFireberry = item; + } + } + if (brightestFireberry == 107) + return -1; + return brightestFireberry; +} + +int KyraEngine_LoK::o1_setFireberryGlowPalette(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setFireberryGlowPalette(%p) (%d)", (const void *)script, stackPos(0)); + + if (_flags.platform == Common::kPlatformAmiga) { + int palIndex = 0; + + switch (stackPos(0)) { + case -1: + // The original seemed to draw some lines on page 2 here, which looks strange... + //if (!(_brandonStatusBit & 2)) + // warning("Unimplemented case for o1_setFireberryGlowPalette"); + palIndex = 9; + break; + + case 30: + palIndex = 7; + break; + + case 31: + palIndex = 8; + break; + + case 32: + case 33: + palIndex = 9; + break; + + case 28: case 29: default: + palIndex = 6; + } + + if (_brandonStatusBit & 2) { + if (_currentCharacter->sceneId < 187 || _currentCharacter->sceneId > 198) + palIndex = 10; + } + + _screen->copyPalette(0, palIndex); + } else { + int palIndex = 0; + + switch (stackPos(0)) { + case 0x1E: + palIndex = 9; + break; + + case 0x1F: + palIndex = 10; + break; + + case 0x20: + palIndex = 11; + break; + + case 0x21: + case -1: + palIndex = 12; + break; + + default: + palIndex = 8; + } + + if (_brandonStatusBit & 2) { + if (_currentCharacter->sceneId != 133 && _currentCharacter->sceneId != 137 && + _currentCharacter->sceneId != 165 && _currentCharacter->sceneId != 173 && + (_currentCharacter->sceneId < 187 || _currentCharacter->sceneId > 198)) { + palIndex = 14; + } + } + + _screen->getPalette(1).copy(_specialPalettes[palIndex], 0, 15, 228); + } + + return 0; +} + +int KyraEngine_LoK::o1_drinkPotionAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_drinkPotionAnimation(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + seq_playDrinkPotionAnim(stackPos(0), stackPos(1), stackPos(2)); + return 0; +} + +int KyraEngine_LoK::o1_makeAmuletAppear(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_makeAmuletAppear(%p) ()", (const void *)script); + Movie *amulet = createWSAMovie(); + assert(amulet); + amulet->open("AMULET.WSA", 1, 0); + + if (amulet->opened()) { + assert(_amuleteAnim); + _screen->hideMouse(); + snd_playSoundEffect(0x70); + for (int i = 0; _amuleteAnim[i] != 0xFF; ++i) { + const uint32 nextTime = _system->getMillis() + 5 * _tickLength; + + uint8 code = _amuleteAnim[i]; + if (code == 3 || code == 7) + snd_playSoundEffect(0x71); + + if (code == 5) + snd_playSoundEffect(0x72); + + if (code == 14) + snd_playSoundEffect(0x73); + + amulet->displayFrame(code, 0, 224, 152, 0, 0, 0); + delayUntil(nextTime, false, true); + } + _screen->showMouse(); + } + + delete amulet; + setGameFlag(0x2D); + return 0; +} + +int KyraEngine_LoK::o1_drawItemShapeIntoScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_drawItemShapeIntoScene(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + int item = stackPos(0); + int x = stackPos(1); + int y = stackPos(2); + int flags = stackPos(3); + int onlyHidPage = stackPos(4); + + if (flags) + flags = 1; + + if (onlyHidPage) { + _screen->drawShape(2, _shapes[216 + item], x, y, 0, flags); + } else { + _animator->restoreAllObjectBackgrounds(); + _screen->drawShape(2, _shapes[216 + item], x, y, 0, flags); + _screen->drawShape(0, _shapes[216 + item], x, y, 0, flags); + _animator->flagAllObjectsForBkgdChange(); + _animator->preserveAnyChangedBackgrounds(); + _animator->flagAllObjectsForRefresh(); + _animator->updateAllObjectShapes(); + } + return 0; +} + +int KyraEngine_LoK::o1_setCharacterCurrentFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCharacterCurrentFrame(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _characterList[stackPos(0)].currentAnimFrame = stackPos(1); + return 0; +} + +int KyraEngine_LoK::o1_waitForConfirmationMouseClick(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_waitForConfirmationMouseClick(%p) ()", (const void *)script); + + _eventList.clear(); + while (true) { + updateMousePointer(); + _sprites->updateSceneAnims(); + _animator->updateAllObjectShapes(); + + updateInput(); + + int input = checkInput(0, false) & 0xFF; + removeInputTop(); + if (input == 200) + break; + + delay(10); + } + + script->regs[1] = _mouseX; + script->regs[2] = _mouseY; + + return 0; +} + +int KyraEngine_LoK::o1_pageFlip(EMCState *script) { + warning("STUB: o1_pageFlip"); + return 0; +} + +int KyraEngine_LoK::o1_setSceneFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setSceneFile(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + setSceneFile(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_getItemInMarbleVase(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getItemInMarbleVase(%p) ()", (const void *)script); + return _marbleVaseItem; +} + +int KyraEngine_LoK::o1_setItemInMarbleVase(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setItemInMarbleVase(%p) (%d)", (const void *)script, stackPos(0)); + _marbleVaseItem = stackPos(0); + return 0; +} + +int KyraEngine_LoK::o1_addItemToInventory(EMCState *script) { + warning("STUB: o1_addItemToInventory"); + return 0; +} + +int KyraEngine_LoK::o1_intPrint(EMCState *script) { + warning("STUB: o1_intPrint"); + return 0; +} + +int KyraEngine_LoK::o1_shakeScreen(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_shakeScreen(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int waitTicks = stackPos(1); + int times = stackPos(0); + + for (int i = 0; i < times; ++i) { + _screen->shakeScreen(1); + delay(waitTicks * _tickLength); + } + + return 0; +} + +int KyraEngine_LoK::o1_createAmuletJewel(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_createAmuletJewel(%p) (%d)", (const void *)script, stackPos(0)); + seq_createAmuletJewel(stackPos(0), 0, 0, 0); + return 0; +} + +int KyraEngine_LoK::o1_setSceneAnimCurrXY(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setSceneAnimCurrXY(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + _sprites->_anims[stackPos(0)].x = stackPos(1); + _sprites->_anims[stackPos(0)].y = stackPos(2); + return 0; +} + +int KyraEngine_LoK::o1_poisonBrandonAndRemaps(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_poisonBrandonAndRemaps(%p) ()", (const void *)script); + setBrandonPoisonFlags(1); + return 0; +} + +int KyraEngine_LoK::o1_fillFlaskWithWater(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_fillFlaskWithWater(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + seq_fillFlaskWithWater(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_getCharacterMovementDelay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getCharacterMovementDelay(%p) (%d)", (const void *)script, stackPos(0)); + return _timer->getDelay(stackPos(0) + 5); +} + +int KyraEngine_LoK::o1_getBirthstoneGem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getBirthstoneGem(%p) (%d)", (const void *)script, stackPos(0)); + if (stackPos(0) < 4) + return _birthstoneGemTable[stackPos(0)]; + return 0; +} + +int KyraEngine_LoK::o1_queryBrandonStatusBit(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_queryBrandonStatusBit(%p) (%d)", (const void *)script, stackPos(0)); + if (_brandonStatusBit & stackPos(0)) + return 1; + return 0; +} + +int KyraEngine_LoK::o1_playFluteAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_playFluteAnimation(%p) ()", (const void *)script); + seq_playFluteAnimation(); + return 0; +} + +int KyraEngine_LoK::o1_playWinterScrollSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_playWinterScrollSequence(%p) (%d)", (const void *)script, stackPos(0)); + if (!stackPos(0)) + seq_winterScroll2(); + else + seq_winterScroll1(); + return 0; +} + +int KyraEngine_LoK::o1_getIdolGem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getIdolGem(%p) (%d)", (const void *)script, stackPos(0)); + return _idolGemsTable[stackPos(0)]; +} + +int KyraEngine_LoK::o1_setIdolGem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setIdolGem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _idolGemsTable[stackPos(0)] = stackPos(1); + return 0; +} + +int KyraEngine_LoK::o1_totalItemsInScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_totalItemsInScene(%p) (%d)", (const void *)script, stackPos(0)); + return countItemsInScene(stackPos(0)); +} + +int KyraEngine_LoK::o1_restoreBrandonsMovementDelay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_restoreBrandonsMovementDelay(%p) ()", (const void *)script); + setWalkspeed(_configWalkspeed); + return 0; +} + +int KyraEngine_LoK::o1_setEntranceMouseCursorTrack(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setEntranceMouseCursorTrack(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + _entranceMouseCursorTracks[0] = stackPos(0); + _entranceMouseCursorTracks[1] = stackPos(1); + _entranceMouseCursorTracks[2] = stackPos(0) + stackPos(2) - 1; + _entranceMouseCursorTracks[3] = stackPos(1) + stackPos(3) - 1; + _entranceMouseCursorTracks[4] = stackPos(4); + return 0; +} + +int KyraEngine_LoK::o1_itemAppearsOnGround(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_itemAppearsOnGround(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + processItemDrop(_currentCharacter->sceneId, stackPos(0), stackPos(1), stackPos(2), 2, 0); + return 0; +} + +int KyraEngine_LoK::o1_setNoDrawShapesFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setNoDrawShapesFlag(%p) (%d)", (const void *)script, stackPos(0)); + _animator->_noDrawShapesFlag = stackPos(0); + return 0; +} + +int KyraEngine_LoK::o1_fadeEntirePalette(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_fadeEntirePalette(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int cmd = stackPos(0); + + int fadePal = 0; + + if (_flags.platform == Common::kPlatformAmiga) { + if (cmd == 0) { + _screen->getPalette(2).clear(); + fadePal = 2; + _screen->copyPalette(4, 0); + } else if (cmd == 1) { + fadePal = 0; + _screen->copyPalette(0, 4); + } else if (cmd == 2) { + fadePal = 0; + _screen->getPalette(2).clear(); + } + } else { + if (cmd == 0) { + fadePal = 2; + _screen->getPalette(2).clear(); + _screen->copyPalette(3, 0); + } else if (cmd == 1) { + //fadePal = 3; + warning("unimplemented o1_fadeEntirePalette function"); + return 0; + } else if (cmd == 2) { + _screen->getPalette(2).clear(); + _screen->copyPalette(0, 1); + fadePal = 0; + } + } + + _screen->fadePalette(_screen->getPalette(fadePal), stackPos(1)); + return 0; +} + +int KyraEngine_LoK::o1_itemOnGroundHere(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_itemOnGroundHere(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + assert(stackPos(0) < _roomTableSize); + Room *curRoom = &_roomTable[stackPos(0)]; + for (int i = 0; i < 12; ++i) { + if (curRoom->itemsTable[i] == stackPos(1)) + return 1; + } + return 0; +} + +int KyraEngine_LoK::o1_queryCauldronState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_queryCauldronState(%p) ()", (const void *)script); + return _cauldronState; +} + +int KyraEngine_LoK::o1_setCauldronState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCauldronState(%p) (%d)", (const void *)script, stackPos(0)); + _cauldronState = stackPos(0); + return _cauldronState; +} + +int KyraEngine_LoK::o1_queryCrystalState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_queryCrystalState(%p) (%d)", (const void *)script, stackPos(0)); + if (!stackPos(0)) + return _crystalState[0]; + else if (stackPos(0) == 1) + return _crystalState[1]; + return -1; +} + +int KyraEngine_LoK::o1_setCrystalState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setCrystalState(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (!stackPos(0)) + _crystalState[0] = stackPos(1); + else if (stackPos(0) == 1) + _crystalState[1] = stackPos(1); + return stackPos(1); +} + +int KyraEngine_LoK::o1_setPaletteRange(EMCState *script) { + warning("STUB: o1_setPaletteRange"); + return 0; +} + +int KyraEngine_LoK::o1_shrinkBrandonDown(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_shrinkBrandonDown(%p) (%d)", (const void *)script, stackPos(0)); + int delayTime = stackPos(0); + checkAmuletAnimFlags(); + int scaleValue = _scaleTable[_currentCharacter->y1]; + int scale = 0; + + if (_scaleMode) + scale = scaleValue; + else + scale = 256; + + int scaleModeBackUp = _scaleMode; + _scaleMode = 1; + int scaleEnd = scale >> 1; + for (; scaleEnd <= scale; --scale) { + _scaleTable[_currentCharacter->y1] = scale; + _animator->animRefreshNPC(0); + delayWithTicks(1); + } + delayWithTicks(delayTime); // XXX + _scaleTable[_currentCharacter->y1] = scaleValue; + _scaleMode = scaleModeBackUp; + return 0; +} + +int KyraEngine_LoK::o1_growBrandonUp(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_growBrandonUp(%p) ()", (const void *)script); + int scaleValue = _scaleTable[_currentCharacter->y1]; + int scale = 0; + if (_scaleMode) + scale = scaleValue; + else + scale = 256; + + int scaleModeBackUp = _scaleMode; + _scaleMode = 1; + for (int curScale = scale >> 1; curScale <= scale; ++curScale) { + _scaleTable[_currentCharacter->y1] = curScale; + _animator->animRefreshNPC(0); + delayWithTicks(1); + } + _scaleTable[_currentCharacter->y1] = scaleValue; + _scaleMode = scaleModeBackUp; + return 0; +} + +int KyraEngine_LoK::o1_setBrandonScaleXAndY(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setBrandonScaleXAndY(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _animator->_brandonScaleX = stackPos(0); + _animator->_brandonScaleY = stackPos(1); + return 0; +} + +int KyraEngine_LoK::o1_resetScaleMode(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_resetScaleMode(%p) ()", (const void *)script); + _scaleMode = 0; + return 0; +} + +int KyraEngine_LoK::o1_getScaleDepthTableValue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getScaleDepthTableValue(%p) (%d)", (const void *)script, stackPos(0)); + assert(stackPos(0) < ARRAYSIZE(_scaleTable)); + return _scaleTable[stackPos(0)]; +} + +int KyraEngine_LoK::o1_setScaleDepthTableValue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setScaleDepthTableValue(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + assert(stackPos(0) < ARRAYSIZE(_scaleTable)); + _scaleTable[stackPos(0)] = stackPos(1); + return stackPos(1); +} + +int KyraEngine_LoK::o1_message(EMCState *script) { + if (_flags.isTalkie) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_message(%p) (%d, '%s', %d)", (const void *)script, stackPos(0), stackPosString(1), stackPos(2)); + drawSentenceCommand(stackPosString(1), stackPos(2)); + } else { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_message(%p) ('%s', %d)", (const void *)script, stackPosString(0), stackPos(1)); + drawSentenceCommand(stackPosString(0), stackPos(1)); + } + + return 0; +} + +int KyraEngine_LoK::o1_checkClickOnNPC(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_checkClickOnNPC(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return checkForNPCScriptRun(stackPos(0), stackPos(1)); +} + +int KyraEngine_LoK::o1_getFoyerItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_getFoyerItem(%p) (%d)", (const void *)script, stackPos(0)); + assert(stackPos(0) < ARRAYSIZE(_foyerItemTable)); + return _foyerItemTable[stackPos(0)]; +} + +int KyraEngine_LoK::o1_setFoyerItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setFoyerItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + assert(stackPos(0) < ARRAYSIZE(_foyerItemTable)); + _foyerItemTable[stackPos(0)] = stackPos(1); + return stackPos(1); +} + +int KyraEngine_LoK::o1_setNoItemDropRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setNoItemDropRegion(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + addToNoDropRects(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 0; +} + +int KyraEngine_LoK::o1_walkMalcolmOn(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_walkMalcolmOn(%p) ()", (const void *)script); + if (!_malcolmFlag) + _malcolmFlag = 1; + return 0; +} + +int KyraEngine_LoK::o1_passiveProtection(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_passiveProtection(%p) ()", (const void *)script); + return 1; +} + +int KyraEngine_LoK::o1_setPlayingLoop(EMCState *script) { + warning("STUB: o1_setPlayingLoop"); + return 0; +} + +int KyraEngine_LoK::o1_brandonToStoneSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_brandonToStoneSequence(%p) ()", (const void *)script); + seq_brandonToStone(); + return 0; +} + +int KyraEngine_LoK::o1_brandonHealingSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_brandonHealingSequence(%p) ()", (const void *)script); + seq_brandonHealing(); + return 0; +} + +int KyraEngine_LoK::o1_protectCommandLine(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_protectCommandLine(%p) (%d)", (const void *)script, stackPos(0)); + return stackPos(0); +} + +int KyraEngine_LoK::o1_pauseMusicSeconds(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_pauseMusicSeconds(%p) ()", (const void *)script); + // if music disabled + // return + delay(stackPos(0) * 1000, true); + return 0; +} + +int KyraEngine_LoK::o1_resetMaskRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_resetMaskRegion(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + _screen->fillRect(stackPos(1), stackPos(2), stackPos(1) + stackPos(3), stackPos(2) + stackPos(4), 0, 5); + return 0; +} + +int KyraEngine_LoK::o1_setPaletteChangeFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_setPaletteChangeFlag(%p) (%d)", (const void *)script, stackPos(0)); + _paletteChanged = stackPos(0); + return _paletteChanged; +} + +int KyraEngine_LoK::o1_vocUnload(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_vocUnload(%p) ()", (const void *)script); + // this should unload all voc files (not needed) + return 0; +} + +int KyraEngine_LoK::o1_vocLoad(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_vocLoad(%p) (%d)", (const void *)script, stackPos(0)); + // this should load the specified voc file (not needed) + return 0; +} + +int KyraEngine_LoK::o1_dummy(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_dummy(%p) ()", (const void *)script); + return 0; +} + +#pragma mark - + +typedef Common::Functor1Mem<EMCState *, int, KyraEngine_LoK> OpcodeV1; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV1(this, &KyraEngine_LoK::x)) +void KyraEngine_LoK::setupOpcodeTable() { + Common::Array<const Opcode *> *table = 0; + + _opcodes.reserve(157); + SetOpcodeTable(_opcodes); + // 0x00 + Opcode(o1_magicInMouseItem); + Opcode(o1_characterSays); + Opcode(o1_delay); + Opcode(o1_drawSceneAnimShape); + // 0x04 + Opcode(o1_queryGameFlag); + Opcode(o1_setGameFlag); + Opcode(o1_resetGameFlag); + Opcode(o1_runNPCScript); + // 0x08 + Opcode(o1_setSpecialExitList); + Opcode(o1_blockInWalkableRegion); + Opcode(o1_blockOutWalkableRegion); + Opcode(o1_walkPlayerToPoint); + // 0x0C + Opcode(o1_dropItemInScene); + Opcode(o1_drawAnimShapeIntoScene); + Opcode(o1_setHandItem); + Opcode(o1_savePageToDisk); + // 0x10 + Opcode(o1_sceneAnimOn); + Opcode(o1_sceneAnimOff); + Opcode(o1_getElapsedSeconds); + Opcode(o1_mouseIsPointer); + // 0x14 + Opcode(o1_removeHandItem); + Opcode(o1_runSceneAnimUntilDone); + Opcode(o1_fadeSpecialPalette); + Opcode(o1_playSoundEffect); + // 0x18 + Opcode(o1_playWanderScoreViaMap); + Opcode(o1_phaseInSameScene); + Opcode(o1_setScenePhasingFlag); + Opcode(o1_resetScenePhasingFlag); + // 0x1C + Opcode(o1_queryScenePhasingFlag); + Opcode(o1_sceneToDirection); + Opcode(o1_setBirthstoneGem); + Opcode(o1_placeItemInGenericMapScene); + // 0x20 + Opcode(o1_setBrandonStatusBit); + Opcode(o1_delaySecs); + Opcode(o1_getCharacterScene); + Opcode(o1_runNPCSubscript); + // 0x24 + Opcode(o1_magicOutMouseItem); + Opcode(o1_internalAnimOn); + Opcode(o1_forceBrandonToNormal); + Opcode(o1_poisonDeathNow); + // 0x28 + Opcode(o1_setScaleMode); + Opcode(o1_openWSAFile); + Opcode(o1_closeWSAFile); + Opcode(o1_runWSAFromBeginningToEnd); + // 0x2C + Opcode(o1_displayWSAFrame); + Opcode(o1_enterNewScene); + Opcode(o1_setSpecialEnterXAndY); + Opcode(o1_runWSAFrames); + // 0x30 + Opcode(o1_popBrandonIntoScene); + Opcode(o1_restoreAllObjectBackgrounds); + Opcode(o1_setCustomPaletteRange); + Opcode(o1_loadPageFromDisk); + // 0x34 + Opcode(o1_customPrintTalkString); + Opcode(o1_restoreCustomPrintBackground); + Opcode(o1_hideMouse); + Opcode(o1_showMouse); + // 0x38 + Opcode(o1_getCharacterX); + Opcode(o1_getCharacterY); + Opcode(o1_setCharacterFacing); + Opcode(o1_copyWSARegion); + // 0x3C + Opcode(o1_printText); + Opcode(o1_getRand); + Opcode(o1_loadSoundFile); + Opcode(o1_displayWSAFrameOnHidPage); + // 0x40 + Opcode(o1_displayWSASequentialFrames); + Opcode(o1_refreshCharacter); + Opcode(o1_internalAnimOff); + Opcode(o1_changeCharactersXAndY); + // 0x44 + Opcode(o1_clearSceneAnimatorBeacon); + Opcode(o1_querySceneAnimatorBeacon); + Opcode(o1_refreshSceneAnimator); + Opcode(o1_placeItemInOffScene); + // 0x48 + Opcode(o1_wipeDownMouseItem); + Opcode(o1_placeCharacterInOtherScene); + Opcode(o1_getKey); + Opcode(o1_specificItemInInventory); + // 0x4C + Opcode(o1_popMobileNPCIntoScene); + Opcode(o1_mobileCharacterInScene); + Opcode(o1_hideMobileCharacter); + Opcode(o1_unhideMobileCharacter); + // 0x50 + Opcode(o1_setCharacterLocation); + Opcode(o1_walkCharacterToPoint); + Opcode(o1_specialEventDisplayBrynnsNote); + Opcode(o1_specialEventRemoveBrynnsNote); + // 0x54 + Opcode(o1_setLogicPage); + Opcode(o1_fatPrint); + Opcode(o1_preserveAllObjectBackgrounds); + Opcode(o1_updateSceneAnimations); + // 0x58 + Opcode(o1_sceneAnimationActive); + Opcode(o1_setCharacterMovementDelay); + Opcode(o1_getCharacterFacing); + Opcode(o1_bkgdScrollSceneAndMasksRight); + // 0x5C + Opcode(o1_dispelMagicAnimation); + Opcode(o1_findBrightestFireberry); + Opcode(o1_setFireberryGlowPalette); + Opcode(o1_setDeathHandler); + // 0x60 + Opcode(o1_drinkPotionAnimation); + Opcode(o1_makeAmuletAppear); + Opcode(o1_drawItemShapeIntoScene); + Opcode(o1_setCharacterCurrentFrame); + // 0x64 + Opcode(o1_waitForConfirmationMouseClick); + Opcode(o1_pageFlip); + Opcode(o1_setSceneFile); + Opcode(o1_getItemInMarbleVase); + // 0x68 + Opcode(o1_setItemInMarbleVase); + Opcode(o1_addItemToInventory); + Opcode(o1_intPrint); + Opcode(o1_shakeScreen); + // 0x6C + Opcode(o1_createAmuletJewel); + Opcode(o1_setSceneAnimCurrXY); + Opcode(o1_poisonBrandonAndRemaps); + Opcode(o1_fillFlaskWithWater); + // 0x70 + Opcode(o1_getCharacterMovementDelay); + Opcode(o1_getBirthstoneGem); + Opcode(o1_queryBrandonStatusBit); + Opcode(o1_playFluteAnimation); + // 0x74 + Opcode(o1_playWinterScrollSequence); + Opcode(o1_getIdolGem); + Opcode(o1_setIdolGem); + Opcode(o1_totalItemsInScene); + // 0x78 + Opcode(o1_restoreBrandonsMovementDelay); + Opcode(o1_setMousePos); + Opcode(o1_getMouseState); + Opcode(o1_setEntranceMouseCursorTrack); + // 0x7C + Opcode(o1_itemAppearsOnGround); + Opcode(o1_setNoDrawShapesFlag); + Opcode(o1_fadeEntirePalette); + Opcode(o1_itemOnGroundHere); + // 0x80 + Opcode(o1_queryCauldronState); + Opcode(o1_setCauldronState); + Opcode(o1_queryCrystalState); + Opcode(o1_setCrystalState); + // 0x84 + Opcode(o1_setPaletteRange); + Opcode(o1_shrinkBrandonDown); + Opcode(o1_growBrandonUp); + Opcode(o1_setBrandonScaleXAndY); + // 0x88 + Opcode(o1_resetScaleMode); + Opcode(o1_getScaleDepthTableValue); + Opcode(o1_setScaleDepthTableValue); + Opcode(o1_message); + // 0x8C + Opcode(o1_checkClickOnNPC); + Opcode(o1_getFoyerItem); + Opcode(o1_setFoyerItem); + Opcode(o1_setNoItemDropRegion); + // 0x90 + Opcode(o1_walkMalcolmOn); + Opcode(o1_passiveProtection); + Opcode(o1_setPlayingLoop); + Opcode(o1_brandonToStoneSequence); + // 0x94 + Opcode(o1_brandonHealingSequence); + Opcode(o1_protectCommandLine); + Opcode(o1_pauseMusicSeconds); + Opcode(o1_resetMaskRegion); + // 0x98 + Opcode(o1_setPaletteChangeFlag); + Opcode(o1_fillRect); + Opcode(o1_vocUnload); + Opcode(o1_vocLoad); + // 0x9C + Opcode(o1_dummy); +} +#undef Opcode + +} // End of namespace Kyra diff --git a/engines/kyra/script/script_lol.cpp b/engines/kyra/script/script_lol.cpp new file mode 100644 index 0000000000..65d0f6c1e0 --- /dev/null +++ b/engines/kyra/script/script_lol.cpp @@ -0,0 +1,3051 @@ +/* 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_LOL + +#include "kyra/engine/lol.h" +#include "kyra/graphics/screen_lol.h" +#include "kyra/engine/timer.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" + +namespace Kyra { + +void LoLEngine::runInitScript(const char *filename, int optionalFunc) { + _suspendScript = true; + EMCData scriptData; + EMCState scriptState; + memset(&scriptData, 0, sizeof(EMCData)); + _emc->unload(&_scriptData); + _emc->load(filename, &scriptData, &_opcodes); + + _emc->init(&scriptState, &scriptData); + _emc->start(&scriptState, 0); + while (_emc->isValid(&scriptState)) + _emc->run(&scriptState); + + if (optionalFunc) { + _emc->init(&scriptState, &scriptData); + _emc->start(&scriptState, optionalFunc); + while (_emc->isValid(&scriptState)) + _emc->run(&scriptState); + } + + _emc->unload(&scriptData); + _suspendScript = false; +} + +void LoLEngine::runInfScript(const char *filename) { + _emc->unload(&_scriptData); + _emc->load(filename, &_scriptData, &_opcodes); + runLevelScript(0x400, -1); +} + +void LoLEngine::runLevelScript(int block, int flags) { + runLevelScriptCustom(block, flags, -1, 0, 0, 0); +} + +void LoLEngine::runLevelScriptCustom(int block, int flags, int charNum, int item, int reg3, int reg4) { + EMCState scriptState; + memset(&scriptState, 0, sizeof(EMCState)); + + if (!_suspendScript) { + _emc->init(&scriptState, &_scriptData); + _emc->start(&scriptState, block); + + scriptState.regs[0] = flags; + scriptState.regs[1] = charNum; + scriptState.regs[2] = item; + scriptState.regs[3] = reg3; + scriptState.regs[4] = reg4; + scriptState.regs[5] = block; + scriptState.regs[6] = _scriptDirection; + + if (_emc->isValid(&scriptState)) { + if (*(scriptState.ip - 1) & flags) { + while (_emc->isValid(&scriptState)) + _emc->run(&scriptState); + } + } + } + + checkSceneUpdateNeed(block); +} + +int LoLEngine::olol_setWallType(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setWallType(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (stackPos(2) != -1) { + if (_wllWallFlags[stackPos(2)] & 4) + deleteMonstersFromBlock(stackPos(0)); + } + setWallType(stackPos(0), stackPos(1), stackPos(2)); + return 1; +} + +int LoLEngine::olol_getWallType(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getWallType(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return (int8)_levelBlockProperties[stackPos(0)].walls[stackPos(1) & 3]; +} + +int LoLEngine::olol_drawScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_drawScene(%p) (%d)", (const void *)script, stackPos(0)); + drawScene(stackPos(0)); + return 1; +} + +int LoLEngine::olol_rollDice(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_rollDice(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return rollDice(stackPos(0), stackPos(1)); +} + +int LoLEngine::olol_moveParty(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_moveParty(%p) (%d)", (const void *)script, stackPos(0)); + int mode = stackPos(0); + if (mode > 5 && mode < 10) + mode = (mode - 6 - _currentDirection) & 3; + + Button b; + b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE; + b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01; + + switch (mode) { + case 0: + clickedUpArrow(&b); + break; + + case 1: + clickedRightArrow(&b); + break; + + case 2: + clickedDownArrow(&b); + break; + + case 3: + clickedLeftArrow(&b); + break; + + case 4: + clickedTurnLeftArrow(&b); + break; + + case 5: + clickedTurnRightArrow(&b); + break; + + case 10: + case 11: + case 12: + case 13: + mode = ABS(mode - 10 - _currentDirection); + if (mode > 2) + mode = (mode ^ 2) * -1; + + while (mode) { + if (mode > 0) { + clickedTurnRightArrow(&b); + mode--; + } else { + clickedTurnLeftArrow(&b); + mode++; + } + } + break; + + default: + break; + } + + return 1; +} + + +int LoLEngine::olol_delay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_delay(%p) (%d)", (const void *)script, stackPos(0)); + delay(stackPos(0) * _tickLength, true); + return 1; +} + +int LoLEngine::olol_setGameFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setGameFlag(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + + if (stackPos(1)) + setGameFlag(stackPos(0)); + else + resetGameFlag(stackPos(0)); + + return 1; +} + +int LoLEngine::olol_testGameFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_testGameFlag(%p) (%d)", (const void *)script, stackPos(0)); + if (stackPos(0) < 0) + return 0; + + return queryGameFlag(stackPos(0)); +} + +int LoLEngine::olol_loadLevelGraphics(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadLevelGraphics(%p) (%s, %d, %d, %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + loadLevelGraphics(stackPosString(0), stackPos(1), stackPos(2), stackPos(3) == -1 ? -1 : (uint16)stackPos(3), stackPos(4) == -1 ? -1 : (uint16)stackPos(4), (stackPos(5) == -1) ? 0 : stackPosString(5)); + return 1; +} + +int LoLEngine::olol_loadBlockProperties(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadBlockProperties(%p) (%s)", (const void *)script, stackPosString(0)); + loadBlockProperties(stackPosString(0)); + return 1; +} + +int LoLEngine::olol_loadMonsterShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadMonsterShapes(%p) (%s, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2)); + loadMonsterShapes(stackPosString(0), stackPos(1), stackPos(2)); + return 1; +} + +int LoLEngine::olol_deleteHandItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_deleteHandItem(%p) ()", (const void *)script); + int r = _itemInHand; + deleteItem(_itemInHand); + setHandItem(0); + return r; +} + +int LoLEngine::olol_allocItemPropertiesBuffer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_allocItemPropertiesBuffer(%p) (%d)", (const void *)script, stackPos(0)); + delete[] _itemProperties; + _itemProperties = new ItemProperty[stackPos(0)]; + return 1; +} + +int LoLEngine::olol_setItemProperty(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setItemProperty(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9)); + ItemProperty *tmp = &_itemProperties[stackPos(0)]; + + tmp->nameStringId = stackPos(1); + tmp->shpIndex = stackPos(2); + tmp->type = stackPos(3); + + // WORKAROUND for unpatched early floppy versions. + // The Vaelan's cube should not be able to be equipped in a weapon slot. + if (stackPos(0) == 264 && tmp->type == 5) + tmp->type = 0; + + tmp->itemScriptFunc = stackPos(4); + tmp->might = stackPos(5); + tmp->skill = stackPos(6); + tmp->protection = stackPos(7); + tmp->flags = stackPos(8); + tmp->unkB = stackPos(9); + return 1; +} + +int LoLEngine::olol_makeItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_makeItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + return makeItem(stackPos(0), stackPos(1), stackPos(2)); +} + +int LoLEngine::olol_placeMoveLevelItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_placeMoveLevelItem(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + placeMoveLevelItem(stackPos(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF, stackPos(4) & 0xFF, stackPos(5)); + return 1; +} + +int LoLEngine::olol_createLevelItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_createLevelItem(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + int item = makeItem(stackPos(0), stackPos(1), stackPos(2)); + if (item == -1) + return item; + placeMoveLevelItem(item, stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + return item; +} + +int LoLEngine::olol_getItemPara(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getItemPara(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (!stackPos(0)) + return 0; + + LoLItem *i = &_itemsInPlay[stackPos(0)]; + ItemProperty *p = &_itemProperties[i->itemPropertyIndex]; + + switch (stackPos(1)) { + case 0: + return i->block; + case 1: + return i->x; + case 2: + return i->y; + case 3: + return i->level; + case 4: + return i->itemPropertyIndex; + case 5: + return i->shpCurFrame_flg; + case 6: + return p->nameStringId; + case 7: + break; + case 8: + return p->shpIndex; + case 9: + return p->type; + case 10: + return p->itemScriptFunc; + case 11: + return p->might; + case 12: + return p->skill; + case 13: + return p->protection; + case 14: + return p->unkB; + case 15: + return i->shpCurFrame_flg & 0x1FFF; + case 16: + return p->flags; + case 17: + return (p->skill << 8) | ((uint8)p->might); + default: + break; + } + + return -1; +} + +int LoLEngine::olol_getCharacterStat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getCharacterStat(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + LoLCharacter *c = &_characters[stackPos(0)]; + int d = stackPos(2); + + switch (stackPos(1)) { + case 0: + return c->flags; + + case 1: + return c->raceClassSex; + + case 5: + return c->hitPointsCur; + + case 6: + return c->hitPointsMax; + + case 7: + return c->magicPointsCur; + + case 8: + return c->magicPointsMax; + + case 9: + return c->itemProtection; + + case 10: + return c->items[d]; + + case 11: + return c->skillLevels[d] + c->skillModifiers[d]; + + case 12: + return c->protectionAgainstItems[d]; + + case 13: + return (d & 0x80) ? c->itemsMight[7] : c->itemsMight[d]; + + case 14: + return c->skillModifiers[d]; + + case 15: + return c->id; + + default: + break; + } + + return 0; +} + +int LoLEngine::olol_setCharacterStat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setCharacterStat(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + LoLCharacter *c = &_characters[stackPos(0)]; + int d = stackPos(2); + int e = stackPos(3); + + switch (stackPos(1)) { + case 0: + c->flags = e; + break; + + case 1: + c->raceClassSex = e & 0x0F; + break; + + case 5: + setCharacterMagicOrHitPoints(stackPos(0), 0, e, 0); + break; + + case 6: + c->hitPointsMax = e; + break; + + case 7: + setCharacterMagicOrHitPoints(stackPos(0), 1, e, 0); + break; + + case 8: + c->magicPointsMax = e; + break; + + case 9: + c->itemProtection = e; + break; + + case 10: + c->items[d] = 0; + break; + + case 11: + c->skillLevels[d] = e; + break; + + case 12: + c->protectionAgainstItems[d] = e; + break; + + case 13: + if (d & 0x80) + c->itemsMight[7] = e; + else + c->itemsMight[d] = e; + break; + + case 14: + c->skillModifiers[d] = e; + break; + + default: + break; + } + + return 0; +} + +int LoLEngine::olol_loadLevelShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadLevelShapes(%p) (%s, %s)", (const void *)script, stackPosString(0), stackPosString(1)); + loadLevelShpDat(stackPosString(0), stackPosString(1), true); + return 1; +} + +int LoLEngine::olol_closeLevelShapeFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_closeLevelShapeFile(%p) ()", (const void *)script); + delete _lvlShpFileHandle; + _lvlShpFileHandle = 0; + return 1; +} + +int LoLEngine::olol_loadDoorShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadDoorShapes(%p) (%s, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2)); + _screen->loadBitmap(stackPosString(0), 3, 3, 0); + const uint8 *p = _screen->getCPagePtr(2); + if (_doorShapes[0]) + delete[] _doorShapes[0]; + _doorShapes[0] = _screen->makeShapeCopy(p, stackPos(1)); + if (_doorShapes[1]) + delete[] _doorShapes[1]; + _doorShapes[1] = _screen->makeShapeCopy(p, stackPos(2)); + + for (int i = 0; i < 20; i++) { + _wllWallFlags[i + 3] |= 7; + int t = i % 5; + if (t == 4) + _wllWallFlags[i + 3] &= 0xF8; + if (t == 3) + _wllWallFlags[i + 3] &= 0xFD; + } + + if (stackPos(3)) { + for (int i = 3; i < 13; i++) + _wllWallFlags[i] &= 0xFD; + } + + if (stackPos(4)) { + for (int i = 13; i < 23; i++) + _wllWallFlags[i] &= 0xFD; + } + + return 1; +} + +int LoLEngine::olol_initAnimStruct(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_initAnimStruct(%p) (%s, %d, %d, %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + if (_tim->initAnimStruct(stackPos(1), stackPosString(0), stackPos(2), stackPos(3), stackPos(4), 0, stackPos(5))) + return 1; + return 0; +} + +int LoLEngine::olol_playAnimationPart(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playAnimationPart(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + _tim->animator()->playPart(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 1; +} + +int LoLEngine::olol_freeAnimStruct(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_freeAnimStruct(%p) (%d)", (const void *)script, stackPos(0)); + if (_tim->freeAnimStruct(stackPos(0))) + return 1; + return 0; +} + +int LoLEngine::olol_getDirection(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getDirection(%p)", (const void *)script); + return _currentDirection; +} + +int LoLEngine::olol_characterSurpriseFeedback(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_characterSurpriseFeedback(%p)", (const void *)script); + for (int i = 0; i < 4; i++) { + if (!(_characters[i].flags & 1) || _characters[i].id >= 0) + continue; + int s = -_characters[i].id; + int sfx = (s == 1) ? 136 : ((s == 5) ? 50 : ((s == 8) ? 49 : ((s == 9) ? 48 : 0))); + if (sfx) + snd_playSoundEffect(sfx, -1); + return 1; + } + return 1; +} + +int LoLEngine::olol_setMusicTrack(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setMusicTrack(%p) (%d)", (const void *)script, stackPos(0)); + _curMusicTheme = stackPos(0); + return 1; +} + +int LoLEngine::olol_setSequenceButtons(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setSequenceButtons(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + setSequenceButtons(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + return 1; +} + +int LoLEngine::olol_setDefaultButtonState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setDefaultButtonState(%p)", (const void *)script); + setDefaultButtonState(); + return 1; +} + +int LoLEngine::olol_checkRectForMousePointer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkRectForMousePointer(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return posWithinRect(_mouseX, _mouseY, stackPos(0), stackPos(1), stackPos(2), stackPos(3)) ? 1 : 0; +} + +int LoLEngine::olol_clearDialogueField(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_clearDialogueField(%p) (%d)", (const void *)script, stackPos(0)); + if (_currentControlMode && (!textEnabled())) + return 1; + + _screen->setScreenDim(5); + const ScreenDim *d = _screen->getScreenDim(5); + _screen->fillRect(d->sx, d->sy, d->sx + d->w - (_flags.use16ColorMode ? 3 : 2), d->sy + d->h - 2, d->unkA); + _txt->clearDim(4); + _txt->resetDimTextPositions(4); + + return 1; +} + +int LoLEngine::olol_setupBackgroundAnimationPart(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setupBackgroundAnimationPart(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9)); + _tim->animator()->setupPart(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9)); + return 0; +} + +int LoLEngine::olol_startBackgroundAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_startBackgroundAnimation(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _tim->animator()->start(stackPos(0), stackPos(1)); + return 1; +} + +int LoLEngine::olol_fadeToBlack(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_fadeToBlack(%p) (%d)", (const void *)script, stackPos(0)); + _screen->fadeToBlack(10); + return 1; +} + +int LoLEngine::olol_fadePalette(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_fadePalette(%p)", (const void *)script); + if (_flags.use16ColorMode) + setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect); + else + _screen->fadePalette(_screen->getPalette(3), 10); + _screen->_fadeFlag = 0; + return 1; +} + +int LoLEngine::olol_loadBitmap(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadBitmap(%p) (%s, %d)", (const void *)script, stackPosString(0), stackPos(1)); + _screen->loadBitmap(stackPosString(0), 3, 3, &_screen->getPalette(3)); + if (stackPos(1) != 2) + _screen->copyPage(3, stackPos(1)); + return 1; +} + +int LoLEngine::olol_stopBackgroundAnimation(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_stopBackgroundAnimation(%p) (%d)", (const void *)script, stackPos(0)); + _tim->animator()->stop(stackPos(0)); + return 1; +} + +int LoLEngine::olol_getGlobalScriptVar(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getGlobalScriptVar(%p) (%d)", (const void *)script, stackPos(0)); + assert(stackPos(0) < 24); + return _globalScriptVars[stackPos(0)]; +} + +int LoLEngine::olol_setGlobalScriptVar(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setGlobalScriptVar(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + assert(stackPos(0) < 24); + _globalScriptVars[stackPos(0)] = stackPos(1); + return 1; +} + +int LoLEngine::olol_getGlobalVar(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getGlobalVar(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + + switch (stackPos(0)) { + case 0: + return _currentBlock; + case 1: + return _currentDirection; + case 2: + return _currentLevel; + case 3: + return _itemInHand; + case 4: + return _brightness; + case 5: + return _credits; + case 6: + return _globalScriptVars2[stackPos(1)]; + case 8: + return _updateFlags; + case 9: + return _lampOilStatus; + case 10: + return _sceneDefaultUpdate; + case 11: + return _compassBroken; + case 12: + return _drainMagic; + case 13: + return getVolume(kVolumeSpeech) - 2; + case 14: + return _tim->_abortFlag; + default: + break; + } + + return 0; +} + +int LoLEngine::olol_setGlobalVar(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setGlobalVar(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + uint16 a = stackPos(1); + uint16 b = stackPos(2); + + switch (stackPos(0)) { + case 0: + _currentBlock = b; + calcCoordinates(_partyPosX, _partyPosY, _currentBlock, 0x80, 0x80); + updateAutoMap(_currentBlock); + break; + + case 1: + _currentDirection = b; + break; + + case 2: + _currentLevel = b & 0xFF; + break; + + case 3: + setHandItem(b); + break; + + case 4: + _brightness = b & 0xFF; + break; + + case 5: + _credits = b; + break; + + case 6: + _globalScriptVars2[a] = b; + break; + + case 7: + break; + + case 8: + _updateFlags = b; + if (b == 1) { + if (!textEnabled() || (!(_currentControlMode & 2))) + timerUpdatePortraitAnimations(1); + disableSysTimer(2); + } else { + enableSysTimer(2); + } + break; + + case 9: + _lampOilStatus = b & 0xFF; + break; + + case 10: + _sceneDefaultUpdate = b & 0xFF; + gui_toggleButtonDisplayMode(0, 0); + break; + + case 11: + _compassBroken = a & 0xFF; + break; + + case 12: + _drainMagic = a & 0xFF; + break; + + default: + break; + } + + return 1; +} + +int LoLEngine::olol_triggerDoorSwitch(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_triggerDoorSwitch(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + processDoorSwitch(stackPos(0)/*, (_wllWallFlags[_levelBlockProperties[stackPos(0)].walls[0]] & 8) ? 0 : 1*/, stackPos(1)); + return 1; +} + +int LoLEngine::olol_checkEquippedItemScriptFlags(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkEquippedItemScriptFlags(%p)", (const void *)script); + for (int i = 0; i < 4; i++) { + if (!(_characters[i].flags & 1)) + continue; + for (int ii = 0; ii < 4; ii++) { + uint8 f = _itemProperties[_itemsInPlay[_characters[i].items[ii]].itemPropertyIndex].itemScriptFunc; + if (f == 0 || f == 2) + return 1; + } + } + return 0; +} + +int LoLEngine::olol_setDoorState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setDoorState(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (stackPos(1)) + _levelBlockProperties[stackPos(0)].flags = (_levelBlockProperties[stackPos(0)].flags & 0xEF) | 0x20; + else + _levelBlockProperties[stackPos(0)].flags &= 0xDF; + return 1; +} + +int LoLEngine::olol_updateBlockAnimations(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_updateBlockAnimations(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int block = stackPos(0); + int wall = stackPos(1); + setWallType(block, wall, _levelBlockProperties[block].walls[(wall == -1) ? 0 : wall] == stackPos(2) ? stackPos(3) : stackPos(2)); + return 0; +} + +int LoLEngine::olol_assignLevelDecorationShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_assignLevelDecorationShape(%p) (%d)", (const void *)script, stackPos(0)); + return assignLevelDecorationShapes(stackPos(0)); +} + +int LoLEngine::olol_resetBlockShapeAssignment(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_resetBlockShapeAssignment(%p) (%d)", (const void *)script, stackPos(0)); + uint8 v = stackPos(0) & 0xFF; + memset(_wllShapeMap + 3, v, 5); + memset(_wllShapeMap + 13, v, 5); + return 1; +} + +int LoLEngine::olol_copyRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_copyRegion(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", + (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + _screen->copyRegion(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), Screen::CR_NO_P_CHECK); + if (!stackPos(7)) + _screen->updateScreen(); + return 1; +} + +int LoLEngine::olol_initMonster(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_initMonster(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9), stackPos(10)); + uint16 x = 0; + uint16 y = 0; + calcCoordinates(x, y, stackPos(0), stackPos(1), stackPos(2)); + uint16 w = _monsterProperties[stackPos(4)].maxWidth; + + if (checkBlockBeforeObjectPlacement(x, y, w, 7, 7)) + return -1; + + for (uint8 i = 0; i < 30; i++) { + LoLMonster *l = &_monsters[i]; + if (l->hitPoints || l->mode == 13) + continue; + + memset(l, 0, sizeof(LoLMonster)); + l->id = i; + l->x = x; + l->y = y; + l->facing = stackPos(3); + l->type = stackPos(4); + l->properties = &_monsterProperties[l->type]; + l->direction = l->facing << 1; + l->hitPoints = (l->properties->hitPoints * _monsterModifiers1[_monsterDifficulty]) >> 8; + + if (_currentLevel != 12 || l->type != 2) + l->hitPoints = (l->hitPoints * (rollDice(1, 128) + 192)) >> 8; + + l->numDistAttacks = l->properties->numDistAttacks; + l->distAttackTick = rollDice(1, calcMonsterSkillLevel(l->id | 0x8000, 8)) - 1; + l->flyingHeight = 2; + l->flags = stackPos(5); + l->assignedItems = 0; + + setMonsterMode(l, stackPos(6)); + placeMonster(l, l->x, l->y); + + l->destX = l->x; + l->destY = l->y; + l->destDirection = l->direction; + + for (int ii = 0; ii < 4; ii++) + l->equipmentShapes[ii] = stackPos(7 + ii) & 0xFF; + + checkSceneUpdateNeed(l->block); + return i; + } + + return -1; +} + +int LoLEngine::olol_fadeClearSceneWindow(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_fadeClearSceneWindow(%p)", (const void *)script); + _screen->fadeClearSceneWindow(10); + return 1; +} + +int LoLEngine::olol_fadeSequencePalette(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_fadeSequencePalette(%p)", (const void *)script); + if (_flags.use16ColorMode) { + setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect); + } else { + _screen->getPalette(3).copy(_screen->getPalette(0), 128); + _screen->loadSpecialColors(_screen->getPalette(3)); + _screen->fadePalette(_screen->getPalette(3), 10); + } + _screen->_fadeFlag = 0; + return 1; +} + +int LoLEngine::olol_redrawPlayfield(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_redrawPlayfield(%p)", (const void *)script); + if (_screen->_fadeFlag != 2) + _screen->fadeClearSceneWindow(10); + gui_drawPlayField(); + setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect); + _screen->_fadeFlag = 0; + return 1; +} + +int LoLEngine::olol_loadNewLevel(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadNewLevel(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + _screen->fadeClearSceneWindow(10); + _screen->fillRect(112, 0, 288, 120, 0); + disableSysTimer(2); + + for (int i = 0; i < 8; i++) { + if (!_flyingObjects[i].enable || _flyingObjects[i].objectType) + continue; + endObjectFlight(&_flyingObjects[i], _flyingObjects[i].x, _flyingObjects[i].y, 1); + } + + completeDoorOperations(); + + generateTempData(); + + _currentBlock = stackPos(1); + _currentDirection = stackPos(2); + calcCoordinates(_partyPosX, _partyPosY, _currentBlock, 0x80, 0x80); + + loadLevel(stackPos(0)); + + enableSysTimer(2); + + script->ip = 0; + return 1; +} + +int LoLEngine::olol_getNearestMonsterFromCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getNearestMonsterFromCharacter(%p) (%d)", (const void *)script, stackPos(0)); + return getNearestMonsterFromCharacter(stackPos(0)); +} + +int LoLEngine::olol_dummy0(EMCState *script) { + return 0; +} + +int LoLEngine::olol_loadMonsterProperties(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadMonsterProperties(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", + (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), + stackPos(6), stackPos(7), stackPos(8), stackPos(9), stackPos(10), stackPos(11), stackPos(12), stackPos(13), + stackPos(14), stackPos(15), stackPos(16), stackPos(17), stackPos(18), stackPos(19), stackPos(20), + stackPos(21), stackPos(22), stackPos(23), stackPos(24), stackPos(25), stackPos(26), stackPos(27), + stackPos(28), stackPos(29), stackPos(30), stackPos(31), stackPos(32), stackPos(33), stackPos(34), + stackPos(35), stackPos(36), stackPos(37), stackPos(38), stackPos(39), stackPos(40), stackPos(41)); + + LoLMonsterProperty *l = &_monsterProperties[stackPos(0)]; + l->shapeIndex = stackPos(1) & 0xFF; + + int shpWidthMax = 0; + + for (int i = 0; i < 16; i++) { + uint8 m = _monsterShapes[(l->shapeIndex << 4) + i][3]; + if (m > shpWidthMax) + shpWidthMax = m; + } + + l->maxWidth = shpWidthMax; + + l->fightingStats[0] = (stackPos(2) << 8) / 100; // hit chance + l->fightingStats[1] = 256; // + l->fightingStats[2] = (stackPos(3) << 8) / 100; // protection + l->fightingStats[3] = stackPos(4); // evade chance + l->fightingStats[4] = (stackPos(5) << 8) / 100; // speed + l->fightingStats[5] = (stackPos(6) << 8) / 100; // + l->fightingStats[6] = (stackPos(7) << 8) / 100; // + l->fightingStats[7] = (stackPos(8) << 8) / 100; // + l->fightingStats[8] = 0; + + for (int i = 0; i < 8; i++) { + l->itemsMight[i] = stackPos(9 + i); + l->protectionAgainstItems[i] = (stackPos(17 + i) << 8) / 100; + } + + l->itemProtection = stackPos(25); + l->hitPoints = stackPos(26); + l->speedTotalWaitTicks = 1; + l->flags = stackPos(27); + // This is what the original does here (setting the value first to stackPos(28) and then to stackPos(29): + //l->unk5 = stackPos(28); + l->unk5 = stackPos(29); + + l->numDistAttacks = stackPos(30); + l->numDistWeapons = stackPos(31); + for (int i = 0; i < 3; i++) + l->distWeapons[i] = stackPos(32 + i); + + l->attackSkillChance = stackPos(35); + l->attackSkillType = stackPos(36); + l->defenseSkillChance = stackPos(37); + l->defenseSkillType = stackPos(38); + + for (int i = 0; i < 3; i++) + l->sounds[i] = stackPos(39 + i); + + return 1; +} + +int LoLEngine::olol_battleHitSkillTest(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_battleHitSkillTest(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + return battleHitSkillTest(stackPos(0), stackPos(1), stackPos(2)); +} + +int LoLEngine::olol_inflictDamage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_inflictDamage(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + if (stackPos(0) == -1) { + for (int i = 0; i < 4; i++) + inflictDamage(i, stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + } else { + inflictDamage(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + } + + return 1; +} + +int LoLEngine::olol_moveMonster(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_moveMonster(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + LoLMonster *m = &_monsters[stackPos(0)]; + + if (m->mode == 1 || m->mode == 2) { + calcCoordinates(m->destX, m->destY, stackPos(1), stackPos(2), stackPos(3)); + m->destDirection = stackPos(4) << 1; + if (m->x != m->destX || m->y != m->destY) + setMonsterDirection(m, calcMonsterDirection(m->x, m->y, m->destX, m->destY)); + } + + return 1; +} + +int LoLEngine::olol_setupDialogueButtons(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setupDialogueButtons(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + setupDialogueButtons(stackPos(0), getLangString(stackPos(1)), getLangString(stackPos(2)), getLangString(stackPos(3))); + return 1; +} + +int LoLEngine::olol_giveTakeMoney(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_giveTakeMoney(%p) (%d)", (const void *)script, stackPos(0)); + int c = stackPos(0); + if (c >= 0) + giveCredits(c, 1); + else + takeCredits(-c, 1); + + return 1; +} + +int LoLEngine::olol_checkMoney(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkMoney(%p) (%d)", (const void *)script, stackPos(0)); + return (stackPos(0) > _credits) ? 0 : 1; +} + +int LoLEngine::olol_setScriptTimer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setScriptTimer(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + uint8 id = 0x50 + stackPos(0); + + if (stackPos(1)) { + _timer->enable(id); + _timer->setCountdown(id, stackPos(1)); + + } else { + _timer->disable(id); + } + + return 1; +} + +int LoLEngine::olol_createHandItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_createHandItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (_itemInHand) + return 0; + + setHandItem(makeItem(stackPos(0), stackPos(1), stackPos(2))); + return 1; +} + +int LoLEngine::olol_playAttackSound(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playAttackSound(%p) (%d)", (const void *)script, stackPos(0)); + + static const uint8 sounds[] = { 12, 62, 63 }; + int d = stackPos(0); + + if ((d < 70 || d > 74) && (d < 81 || d > 89) && (d < 93 || d > 97) && (d < 102 || d > 106)) + snd_playSoundEffect(sounds[_itemProperties[d].skill & 3], -1); + else + snd_playSoundEffect(12, -1); + + return 1; +} + +int LoLEngine::olol_addRemoveCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_addRemoveCharacter(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + + int16 id = stackPos(0); + if (id < 0) { + id = -id; + for (int i = 0; i < 4; i++) { + if (!(_characters[i].flags & 1) || _characters[i].id != id) + continue; + + _characters[i].flags &= 0xFFFE; + calcCharPortraitXpos(); + + if (_selectedCharacter == i) + _selectedCharacter = 0; + break; + } + } else { + addCharacter(id); + } + + if (!_updateFlags) { + gui_enableDefaultPlayfieldButtons(); + gui_drawPlayField(); + } + + return 1; +} +int LoLEngine::olol_giveItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_giveItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int item = makeItem(stackPos(0), stackPos(1), stackPos(2)); + if (addItemToInventory(item)) + return 1; + + deleteItem(item); + return 0; +} + +int LoLEngine::olol_loadTimScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadTimScript(%p) (%d, %s)", (const void *)script, stackPos(0), stackPosString(1)); + if (_activeTim[stackPos(0)]) + return 1; + Common::String file = Common::String::format("%s.TIM", stackPosString(1)); + _activeTim[stackPos(0)] = _tim->load(file.c_str(), &_timIngameOpcodes); + return 1; +} + +int LoLEngine::olol_runTimScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_runTimScript(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return _tim->exec(_activeTim[stackPos(0)], stackPos(1)); +} + +int LoLEngine::olol_releaseTimScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_releaseTimScript(%p) (%d)", (const void *)script, stackPos(0)); + _tim->unload(_activeTim[stackPos(0)]); + return 1; +} + +int LoLEngine::olol_initSceneWindowDialogue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_initSceneWindowDialogue(%p) (%d)", (const void *)script, stackPos(0)); + initSceneWindowDialogue(stackPos(0)); + return 1; +} + +int LoLEngine::olol_restoreAfterSceneWindowDialogue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restoreAfterSceneWindowDialogue(%p) (%d)", (const void *)script, stackPos(0)); + restoreAfterSceneWindowDialogue(stackPos(0)); + return 1; +} + +int LoLEngine::olol_getItemInHand(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getItemInHand(%p))", (const void *)script); + return _itemInHand; +} + +int LoLEngine::olol_checkMagic(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkMagic(%p )(%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + return checkMagic(stackPos(0), stackPos(1), stackPos(2)); +} + +int LoLEngine::olol_giveItemToMonster(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_giveItemToMonster(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (stackPos(0) == -1) + return 0; + giveItemToMonster(&_monsters[stackPos(0)], stackPos(1)); + return 1; +} + +int LoLEngine::olol_loadLangFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadLangFile(%p) (%s)", (const void *)script, stackPosString(0)); + Common::String filename = Common::String::format("%s.%s", stackPosString(0), _languageExt[_lang]); + delete[] _levelLangFile; + _levelLangFile = _res->fileData(filename.c_str(), 0); + return 1; +} + +int LoLEngine::olol_playSoundEffect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playSoundEffect(%p) (%d)", (const void *)script, stackPos(0)); + snd_playSoundEffect(stackPos(0), -1); + return 1; +} + +int LoLEngine::olol_processDialogue(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_processDialogue(%p)", (const void *)script); + return processDialogue(); +} + +int LoLEngine::olol_stopTimScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_stopTimScript(%p) (%d)", (const void *)script, stackPos(0)); + _tim->stopAllFuncs(_activeTim[stackPos(0)]); + return 1; +} + +int LoLEngine::olol_getWallFlags(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getWallFlags(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return _wllWallFlags[_levelBlockProperties[stackPos(0)].walls[stackPos(1) & 3]]; +} + +int LoLEngine::olol_changeMonsterStat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_changeMonsterStat(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (stackPos(0) == -1) + return 1; + + LoLMonster *m = &_monsters[stackPos(0) & 0x7FFF]; + + int16 d = stackPos(2); + uint16 x = 0; + uint16 y = 0; + + switch (stackPos(1)) { + case 0: + setMonsterMode(m, d); + break; + + case 1: + m->hitPoints = d; + break; + + case 2: + calcCoordinates(x, y, d, m->x & 0xFF, m->y & 0xFF); + if (!walkMonsterCheckDest(x, y, m, 7)) + placeMonster(m, x, y); + break; + + case 3: + setMonsterDirection(m, d << 1); + break; + + case 6: + m->flags |= d; + break; + + default: + break; + } + + return 1; +} + +int LoLEngine::olol_getMonsterStat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getMonsterStat(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (stackPos(0) == -1) + return 0; + + LoLMonster *m = &_monsters[stackPos(0) & 0x7FFF]; + int d = stackPos(1); + + switch (d) { + case 0: + return m->mode; + case 1: + return m->hitPoints; + case 2: + return m->block; + case 3: + return m->facing; + case 4: + return m->type; + case 5: + return m->properties->hitPoints; + case 6: + return m->flags; + case 7: + return m->properties->flags; + case 8: + return _monsterAnimType[m->properties->shapeIndex]; + default: + break; + } + + return 0; +} + +int LoLEngine::olol_releaseMonsterShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_releaseMonsterShapes(%p)", (const void *)script); + for (int i = 0; i < 3; i++) + releaseMonsterShapes(i); + return 0; +} + +int LoLEngine::olol_playCharacterScriptChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playCharacterScriptChat(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (_flags.isTalkie) { + snd_stopSpeech(1); + stopPortraitSpeechAnim(); + } + return playCharacterScriptChat(stackPos(0), stackPos(1), 1, getLangString(stackPos(2)), script, 0, 3); +} + +int LoLEngine::olol_playEnvironmentalSfx(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playEnvironmentalSfx(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + uint16 block = (stackPos(1) == -1) ? _currentBlock : stackPos(1); + snd_processEnvironmentalSoundEffect(stackPos(0), block); + return 1; +} + +int LoLEngine::olol_update(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_update(%p)", (const void *)script); + update(); + return 1; +} + +int LoLEngine::olol_healCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_healCharacter(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + if (stackPos(3)) { + processMagicHeal(stackPos(0), stackPos(1)); + } else { + increaseCharacterHitpoints(stackPos(0), stackPos(1), true); + if (stackPos(2)) + gui_drawCharPortraitWithStats(stackPos(0)); + } + return 1; +} + +int LoLEngine::olol_drawExitButton(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_drawExitButton(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + + static const uint8 printPara[] = { 0x90, 0x78, 0x0C, 0x9F, 0x80, 0x1E }; + + int cp = _screen->setCurPage(0); + Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT); + int x = printPara[3 * stackPos(0)] << 1; + int y = printPara[3 * stackPos(0) + 1]; + int offs = printPara[3 * stackPos(0) + 2]; + + char *str = getLangString(0x4033); + int w = _screen->getTextWidth(str); + + if (_flags.use16ColorMode) { + gui_drawBox(x - offs - w, y - 9, w + offs, 9, 0xEE, 0xCC, 0x11); + _screen->printText(str, x - (offs >> 1) - w, y - 7, 0xBB, 0); + } else { + gui_drawBox(x - offs - w, y - 9, w + offs, 9, 136, 251, 252); + _screen->printText(str, x - (offs >> 1) - w, y - 7, 144, 0); + } + + if (stackPos(1)) + _screen->drawGridBox(x - offs - w + 1, y - 8, w + offs - 2, 7, 1); + + _screen->setFont(cf); + _screen->setCurPage(cp); + return 1; +} + +int LoLEngine::olol_loadSoundFile(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_loadSoundFile(%p) (%d)", (const void *)script, stackPos(0)); + snd_loadSoundFile(stackPos(0)); + return 1; +} + +int LoLEngine::olol_playMusicTrack(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playMusicTrack(%p) (%d)", (const void *)script, stackPos(0)); + return snd_playTrack(stackPos(0)); +} + +int LoLEngine::olol_deleteMonstersFromBlock(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_deleteMonstersFromBlock(%p) (%d)", (const void *)script, stackPos(0)); + deleteMonstersFromBlock(stackPos(0)); + return 1; +} + +int LoLEngine::olol_countBlockItems(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_countBlockItems(%p) (%d)", (const void *)script, stackPos(0)); + uint16 o = _levelBlockProperties[stackPos(0)].assignedObjects; + int res = 0; + + while (o) { + if (!(o & 0x8000)) + res++; + o = findObject(o)->nextAssignedObject; + } + + return res; +} + +int LoLEngine::olol_characterSkillTest(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_characterSkillTest(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int skill = stackPos(0); + int n = countActiveCharacters(); + int m = 0; + int c = 0; + + for (int i = 0; i < n; i++) { + int v = _characters[i].skillModifiers[skill] + _characters[i].skillLevels[skill] + 25; + if (v > m) { + m = v; + c = i; + } + } + + return (rollDice(1, 100) > m) ? -1 : c; +} + +int LoLEngine::olol_countAllMonsters(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_countAllMonsters(%p)", (const void *)script); + int res = 0; + + for (int i = 0; i < 30; i++) { + if (_monsters[i].hitPoints > 0 && _monsters[i].mode != 13) + res++; + } + + return res; +} + +int LoLEngine::olol_playEndSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playEndSequence(%p)", (const void *)script); + + int c = 0; + if (_characters[0].id == -9) + c = 1; + else if (_characters[0].id == -5) + c = 3; + else if (_characters[0].id == -1) + c = 2; + + while (snd_updateCharacterSpeech()) + delay(_tickLength); + + _eventList.clear(); + _screen->hideMouse(); + _screen->getPalette(1).clear(); + + showOutro(c, (_monsterDifficulty == 2)); + // Don't call quitGame() on a RTL request (because this would + // make the next game launched from the launcher quit instantly. + if (!shouldQuit()) + quitGame(); + + return 0; +} + +int LoLEngine::olol_stopPortraitSpeechAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_stopPortraitSpeechAnim(%p)", (const void *)script); + if (_flags.isTalkie) + snd_stopSpeech(1); + stopPortraitSpeechAnim(); + return 1; +} + +int LoLEngine::olol_setPaletteBrightness(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setPaletteBrightness(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + uint16 old = _brightness; + _brightness = stackPos(0); + if (stackPos(1) == 1) + setPaletteBrightness(_screen->getPalette(0), stackPos(0), _lampEffect); + return old; +} + +int LoLEngine::olol_calcInflictableDamage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_calcInflictableDamage(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + return calcInflictableDamage(stackPos(0), stackPos(1), stackPos(2)); +} + +int LoLEngine::olol_getInflictedDamage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getInflictedDamage(%p) (%d)", (const void *)script, stackPos(0)); + int mx = stackPos(0); + return rollDice(2, mx); +} + +int LoLEngine::olol_checkForCertainPartyMember(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkForCertainPartyMember(%p) (%d)", (const void *)script, stackPos(0)); + for (int i = 0; i < 4; i++) { + if (_characters[i].flags & 9 && _characters[i].id == stackPos(0)) + return 1; + } + return 0; +} + +int LoLEngine::olol_printMessage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_printMessage(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9)); + int snd = stackPos(2); + _txt->printMessage(stackPos(0), getLangString(stackPos(1)), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9)); + + if (snd >= 0) + snd_playSoundEffect(snd, -1); + + return 1; +} + +int LoLEngine::olol_deleteLevelItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_deleteLevelItem(%p) (%d)", (const void *)script, stackPos(0)); + if (_itemsInPlay[stackPos(0)].block) + removeLevelItem(stackPos(0), _itemsInPlay[stackPos(0)].block); + + deleteItem(stackPos(0)); + + return 1; +} + +int LoLEngine::olol_calcInflictableDamagePerItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_calcInflictableDamagePerItem(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + return calcInflictableDamagePerItem(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); +} + +int LoLEngine::olol_distanceAttack(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_distanceAttack(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8)); + + uint16 fX = stackPos(3); + uint16 fY = stackPos(4); + + if (!(stackPos(8) & 0x8000)) + fX = fY = 0x80; + + uint16 x = 0; + uint16 y = 0; + calcCoordinates(x, y, stackPos(2), fX, fY); + + if (launchObject(stackPos(0), stackPos(1), x, y, stackPos(5), stackPos(6) << 1, stackPos(7), stackPos(8), 0x3F)) + return 1; + + deleteItem(stackPos(1)); + return 0; +} + +int LoLEngine::olol_removeCharacterEffects(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_removeCharacterEffects(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + removeCharacterEffects(&_characters[stackPos(0)], stackPos(1), stackPos(2)); + return 1; +} + +int LoLEngine::olol_checkInventoryFull(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkInventoryFull(%p)", (const void *)script); + for (int i = 0; i < 48; i++) { + if (_inventory[i]) + return 0; + } + return 1; +} + +int LoLEngine::olol_moveBlockObjects(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_moveBlockObjects(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + int o = _levelBlockProperties[stackPos(0)].assignedObjects; + int res = 0; + int level = stackPos(2); + int destBlock = stackPos(1); + int runScript = stackPos(4); + int includeMonsters = stackPos(3); + int includeItems = stackPos(5); + + // WORKAROUND for script bug + // Items would vanish when thrown towards the stairs + // in white tower level 3. + if (_currentLevel == 21 && level == 21 && destBlock == 0x3E0) { + level = 20; + destBlock = 0x0247; + } + + while (o) { + int l = o; + o = findObject(o)->nextAssignedObject; + if (l & 0x8000) { + if (!includeMonsters) + continue; + + l &= 0x7FFF; + + LoLMonster *m = &_monsters[l]; + + setMonsterMode(m, 14); + checkSceneUpdateNeed(m->block); + placeMonster(m, 0, 0); + + res = 1; + + } else { + if (!(_itemsInPlay[l].shpCurFrame_flg & 0x4000) || !includeItems) + continue; + + placeMoveLevelItem(l, level, destBlock, _itemsInPlay[l].x & 0xFF, _itemsInPlay[l].y & 0xFF, _itemsInPlay[l].flyingHeight); + res = 1; + + if (!runScript || level != _currentLevel) + continue; + + runLevelScriptCustom(destBlock, 0x80, -1, l, 0, 0); + } + } + + return res; +} + +int LoLEngine::olol_addSpellToScroll(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_addSpellToScroll(%p) (%d)", (const void *)script, stackPos(0)); + addSpellToScroll(stackPos(0), stackPos(1)); + return 1; +} + +int LoLEngine::olol_playDialogueText(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playDialogueText(%p) (%d)", (const void *)script, stackPos(0)); + _txt->printDialogueText(3, getLangString(stackPos(0)), script, 0, 1); + return 1; +} + +int LoLEngine::olol_playDialogueTalkText(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_playDialogueTalkText(%p) (%d)", (const void *)script, stackPos(0)); + int track = stackPos(0); + + if (!snd_playCharacterSpeech(track, 0, 0) || textEnabled()) { + char *s = getLangString(track); + _txt->printDialogueText(4, s, script, 0, 1); + } + + return 1; +} + +int LoLEngine::olol_checkMonsterTypeHostility(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkMonsterTypeHostility(%p) (%d)", (const void *)script, stackPos(0)); + for (int i = 0; i < 30; i++) { + if (stackPos(0) != _monsters[i].type && stackPos(0) != -1) + continue; + return (_monsters[i].mode == 1) ? 0 : 1; + } + return 1; +} + +int LoLEngine::olol_setNextFunc(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setNextFunc(%p) (%d)", (const void *)script, stackPos(0)); + _nextScriptFunc = stackPos(0); + return 1; +} + +int LoLEngine::olol_dummy1(EMCState *script) { + return 1; +} + +int LoLEngine::olol_suspendMonster(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_suspendMonster(%p) (%d)", (const void *)script, stackPos(0)); + LoLMonster *m = &_monsters[stackPos(0) & 0x7FFF]; + setMonsterMode(m, 14); + checkSceneUpdateNeed(m->block); + placeMonster(m, 0, 0); + return 1; +} + +int LoLEngine::olol_setScriptTextParameter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setScriptTextParameter(%p) (%d)", (const void *)script, stackPos(0)); + _txt->_scriptTextParameter = stackPos(0); + return 1; +} + +int LoLEngine::olol_triggerEventOnMouseButtonClick(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_triggerEventOnMouseButtonClick(%p) (%d)", (const void *)script, stackPos(0)); + gui_notifyButtonListChanged(); + snd_updateCharacterSpeech(); + + int f = checkInput(0); + removeInputTop(); + if (f == 0 || (f & 0x800)) + return 0; + + int evt = stackPos(0); + if (evt) { + gui_triggerEvent(evt); + _seqTrigger = 1; + } else { + removeInputTop(); + } + + return 1; +} + +int LoLEngine::olol_printWindowText(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_printWindowText(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int dim = stackPos(0); + int flg = stackPos(1); + _screen->setScreenDim(dim); + if (flg & 1) + _txt->clearCurDim(); + if (flg & 3) + _txt->resetDimTextPositions(dim); + _txt->printDialogueText(dim, getLangString(stackPos(2)), script, 0, 3); + return 1; +} + +int LoLEngine::olol_countSpecificMonsters(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_countSpecificMonsters(%p) (%d, ...)", (const void *)script, stackPos(0)); + uint16 types = 0; + int res = 0; + int cnt = 0; + + while (stackPos(cnt) != -1) + types |= (1 << stackPos(cnt++)); + + for (int i = 0; i < 30; i++) { + if (((1 << _monsters[i].type) & types) && _monsters[i].mode < 14) + res++; + } + + return res; +} + +int LoLEngine::olol_updateBlockAnimations2(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_updateBlockAnimations2(%p) (%d, %d, %d, %d, ...)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int numFrames = stackPos(3); + assert(numFrames <= 97); + int curFrame = stackPos(2) % numFrames; + setWallType(stackPos(0), stackPos(1), stackPos(4 + curFrame)); + return 0; +} + +int LoLEngine::olol_checkPartyForItemType(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkPartyForItemType(%p) (%d, %d, %d))", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int p = stackPos(1); + + if (!stackPos(2)) { + for (int i = 0; i < 48; i++) { + if (!_inventory[i] || _itemsInPlay[_inventory[i]].itemPropertyIndex != p) + continue; + return 1; + } + + if (_itemsInPlay[_itemInHand].itemPropertyIndex == p) + return 1; + } + + int last = (stackPos(0) == -1) ? 3 : stackPos(0); + int first = (stackPos(0) == -1) ? 0 : stackPos(0); + + for (int i = first; i <= last; i++) { + if (itemEquipped(i, p)) + return 1; + } + + return 0; +} + +int LoLEngine::olol_blockDoor(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_blockDoor(%p) (%d)", (const void *)script, stackPos(0)); + _blockDoor = stackPos(0); + return _blockDoor; +} + +int LoLEngine::olol_resetTimDialogueState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_resetTimDialogueState(%p) (%d)", (const void *)script, stackPos(0)); + _tim->resetDialogueState(_activeTim[stackPos(0)]); + return 1; +} + +int LoLEngine::olol_getItemOnPos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getItemOnPos(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int pX = stackPos(1); + if (pX != -1) + pX &= 0xFF; + + int pY = stackPos(2); + if (pY != -1) + pY &= 0xFF; + + int o = (stackPos(3) || _emcLastItem == -1) ? stackPos(0) : _emcLastItem; + + _emcLastItem = _levelBlockProperties[o].assignedObjects; + + while (_emcLastItem) { + if (_emcLastItem & 0x8000) { + o = _emcLastItem & 0x7FFF; + _emcLastItem = _levelBlockProperties[o].assignedObjects; + continue; + } + + if (pX != -1 && (_itemsInPlay[_emcLastItem].x & 0xFF) != pX) { + o = _emcLastItem & 0x7FFF; + _emcLastItem = _levelBlockProperties[o].assignedObjects; + continue; + } + + if (pY != -1 && (_itemsInPlay[_emcLastItem].y & 0xFF) != pY) { + o = _emcLastItem & 0x7FFF; + _emcLastItem = _levelBlockProperties[o].assignedObjects; + continue; + } + + return _emcLastItem; + } + + return 0; +} + +int LoLEngine::olol_removeLevelItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_removeLevelItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + removeLevelItem(stackPos(0), stackPos(1)); + return 1; +} + +int LoLEngine::olol_savePage5(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_savePage5(%p)", (const void *)script); + // Not implemented: The original code uses this to back up and restore page 5 which is used + // to load WSA files. Since our WSA player provides its own memory buffers we don't + // need to use page 5. + return 1; +} + +int LoLEngine::olol_restorePage5(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restorePage5(%p)", (const void *)script); + // Not implemented: The original code uses this to back up and restore page 5 which is used + // to load WSA files. Since our WSA player provides its own memory buffers we don't + // need to use page 5. + for (int i = 0; i < 6; i++) + _tim->freeAnimStruct(i); + return 1; +} + +int LoLEngine::olol_initDialogueSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_initDialogueSequence(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + initDialogueSequence(stackPos(0), stackPos(1)); + return 1; +} + +int LoLEngine::olol_restoreAfterDialogueSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restoreAfterDialogueSequence(%p) (%d)", (const void *)script, stackPos(0)); + restoreAfterDialogueSequence(stackPos(0)); + return 1; +} + +int LoLEngine::olol_setSpecialSceneButtons(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setSpecialSceneButtons(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + setSpecialSceneButtons(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + return 1; +} + +int LoLEngine::olol_restoreButtonsAfterSpecialScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restoreButtonsAfterSpecialScene(%p)", (const void *)script); + gui_specialSceneRestoreButtons(); + return 1; +} + +int LoLEngine::olol_prepareSpecialScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_prepareSpecialScene(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + prepareSpecialScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + return 1; +} + +int LoLEngine::olol_restoreAfterSpecialScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restoreAfterSpecialScene(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return restoreAfterSpecialScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); +} + +int LoLEngine::olol_assignCustomSfx(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_assignCustomSfx(%p) (%s, %d)", (const void *)script, stackPosString(0), stackPos(1)); + const char *c = stackPosString(0); + int i = stackPos(1); + + if (!c || i > 250) + return 0; + + uint16 t = READ_LE_UINT16(&_ingameSoundIndex[i << 1]); + if (t == 0xFFFF) + return 0; + + strcpy(_ingameSoundList[t], c); + + return 0; +} + +int LoLEngine::olol_findAssignedMonster(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_findAssignedMonster(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + uint16 o = stackPos(1) == -1 ? _levelBlockProperties[stackPos(0)].assignedObjects : findObject(stackPos(1))->nextAssignedObject; + while (o) { + if (o & 0x8000) + return o & 0x7FFF; + o = findObject(o)->nextAssignedObject; + } + return -1; +} + +int LoLEngine::olol_checkBlockForMonster(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkBlockForMonster(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + uint16 block = stackPos(0); + uint16 id = stackPos(1) | 0x8000; + + uint16 o = _levelBlockProperties[block].assignedObjects; + while (o & 0x8000) { + if (id == 0xFFFF || id == o) + return o & 0x7FFF; + o = findObject(o)->nextAssignedObject; + } + return -1; +} + +int LoLEngine::olol_crossFadeRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_crossFadeRegion(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + _screen->crossFadeRegion(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + return 1; +} + +int LoLEngine::olol_calcCoordinatesAddDirectionOffset(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_calcCoordinatesAddDirectionOffset(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + uint16 x = stackPos(0); + uint16 y = stackPos(1); + calcCoordinatesAddDirectionOffset(x, y, stackPos(2)); + return stackPos(3) ? x : y; +} + +int LoLEngine::olol_resetPortraitsAndDisableSysTimer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_resetPortraitsAndDisableSysTimer(%p)", (const void *)script); + resetPortraitsAndDisableSysTimer(); + return 1; +} + +int LoLEngine::olol_enableSysTimer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_enableSysTimer(%p)", (const void *)script); + _needSceneRestore = 0; + enableSysTimer(2); + return 1; +} + +int LoLEngine::olol_checkNeedSceneRestore(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_checkNeedSceneRestore(%p)", (const void *)script); + return _needSceneRestore; +} + +int LoLEngine::olol_getNextActiveCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getNextActiveCharacter(%p) (%d)", (const void *)script, stackPos(0)); + if (stackPos(0)) + _scriptCharacterCycle = 0; + else + _scriptCharacterCycle++; + + while (_scriptCharacterCycle < 4) { + if (_characters[_scriptCharacterCycle].flags & 1) + return _scriptCharacterCycle; + _scriptCharacterCycle++; + } + return -1; +} + +int LoLEngine::olol_paralyzePoisonCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_paralyzePoisonCharacter(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + return paralyzePoisonCharacter(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); +} + +int LoLEngine::olol_drawCharPortrait(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_drawCharPortrait(%p) (%d)", (const void *)script, stackPos(0)); + int charNum = stackPos(0); + if (charNum == -1) + gui_drawAllCharPortraitsWithStats(); + else + gui_drawCharPortraitWithStats(charNum); + return 1; +} + +int LoLEngine::olol_removeInventoryItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_removeInventoryItem(%p) (%d)", (const void *)script, stackPos(0)); + int itemType = stackPos(0); + for (int i = 0; i < 48; i++) { + if (!_inventory[i] || _itemsInPlay[_inventory[i]].itemPropertyIndex != itemType) + continue; + _inventory[i] = 0; + gui_drawInventory(); + return 1; + } + + for (int i = 0; i < 4; i++) { + if (!(_characters[i].flags & 1)) + continue; + + for (int ii = 0; ii < 11; ii++) { + if (!_characters[i].items[ii] || _itemsInPlay[_characters[i].items[ii]].itemPropertyIndex != itemType) + continue; + _characters[i].items[ii] = 0; + return 1; + } + } + return 0; +} + +int LoLEngine::olol_getAnimationLastPart(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getAnimationLastPart(%p) (%d)", (const void *)script, stackPos(0)); + return _tim->animator()->resetLastPart(stackPos(0)); +} + +int LoLEngine::olol_assignSpecialGuiShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_assignSpecialGuiShape(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + if (stackPos(0)) { + _specialGuiShape = _levelDecorationShapes[_levelDecorationProperties[_wllShapeMap[stackPos(0)]].shapeIndex[stackPos(1)]]; + _specialGuiShapeX = stackPos(2); + _specialGuiShapeY = stackPos(3); + _specialGuiShapeMirrorFlag = stackPos(4); + + } else { + _specialGuiShape = 0; + _specialGuiShapeX = _specialGuiShapeY = _specialGuiShapeMirrorFlag = 0; + } + return 1; +} + +int LoLEngine::olol_findInventoryItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_findInventoryItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (stackPos(0) == 0) { + for (int i = 0; i < 48; i++) { + if (!_inventory[i]) + continue; + if (_itemsInPlay[_inventory[i]].itemPropertyIndex == stackPos(2)) + return 0; + } + } + int cur = stackPos(1); + int last = cur; + if (stackPos(1) == -1) { + cur = 0; + last = 4; + } + for (; cur < last; cur++) { + if (!(_characters[cur].flags & 1)) + continue; + for (int i = 0; i < 11; i++) { + if (!_characters[cur].items[i]) + continue; + if (_itemsInPlay[_characters[cur].items[i]].itemPropertyIndex == stackPos(2)) + return cur; + } + } + return -1; +} + +int LoLEngine::olol_restoreFadePalette(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restoreFadePalette(%p)", (const void *)script); + _screen->getPalette(0).copy(_screen->getPalette(1), 0, _flags.use16ColorMode ? 16 : 128); + _screen->fadePalette(_screen->getPalette(0), 10); + _screen->_fadeFlag = 0; + return 1; +} + +int LoLEngine::olol_getSelectedCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getSelectedCharacter(%p)", (const void *)script); + return _selectedCharacter; +} + +int LoLEngine::olol_setHandItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setHandItem(%p) (%d)", (const void *)script, stackPos(0)); + setHandItem(stackPos(0)); + return 1; +} + +int LoLEngine::olol_drinkBezelCup(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_drinkBezelCup(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + drinkBezelCup(3 - stackPos(0), stackPos(1)); + return 1; +} + +int LoLEngine::olol_changeItemTypeOrFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_changeItemTypeOrFlag(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + if (stackPos(0) < 1) + return 0; + + LoLItem *i = &_itemsInPlay[stackPos(0)]; + int16 val = stackPos(2); + + if (stackPos(1) == 4) + i->itemPropertyIndex = val; + else if (stackPos(1) == 15) + i->shpCurFrame_flg = (i->shpCurFrame_flg & 0xE000) | (val & 0x1FFF); + else + val = -1; + + return val; +} + +int LoLEngine::olol_placeInventoryItemInHand(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_placeInventoryItemInHand(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int itemType = stackPos(0); + int i = 0; + for (; i < 48; i++) { + if (!_inventory[i]) + continue; + if (_itemsInPlay[_inventory[i]].itemPropertyIndex == itemType) + break; + } + + if (i == 48) + return -1; + + _inventoryCurItem = i; + int r = _itemInHand; + setHandItem(_inventory[i]); + _inventory[i] = r; + + if (stackPos(1)) + gui_drawInventory(); + + return r; +} + +int LoLEngine::olol_castSpell(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_castSpell(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + return castSpell(stackPos(0), stackPos(1), stackPos(2)); +} + +int LoLEngine::olol_pitDrop(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_pitDrop(%p) (%d)", (const void *)script, stackPos(0)); + int m = stackPos(0); + _screen->updateScreen(); + if (m) { + gui_drawScene(2); + pitDropScroll(9); + snd_playSoundEffect(-1, -1); + shakeScene(30, 4, 0, 1); + + } else { + int t = -1; + for (int i = 0; i < 4; i++) { + if (!(_characters[i].flags & 1) || (_characters[i].id >= 0)) + continue; + if (_characters[i].id == -1) + t = 54; + else if (_characters[i].id == -5) + t = 53; + else if (_characters[i].id == -8) + t = 52; + else if (_characters[i].id == -9) + t = 51; + } + + _screen->fillRect(112, 0, 288, 120, 0, 2); + snd_playSoundEffect(t, -1); + pitDropScroll(12); + } + + return 1; +} + +int LoLEngine::olol_increaseSkill(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_increaseSkill(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + LoLCharacter *c = &_characters[stackPos(0)]; + int s = stackPos(1); + int l = c->skillLevels[s]; + increaseExperience(stackPos(0), s, _expRequirements[l] - c->experiencePts[s]); + return c->skillLevels[s] - l; +} + +int LoLEngine::olol_paletteFlash(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_paletteFlash(%p) (%d)", (const void *)script, stackPos(0)); + Palette &p1 = _screen->getPalette(1); + + if (_flags.use16ColorMode) { + Palette p2(16); + p2.copy(p1); + uint8 *d = p2.getData(); + + for (int i = 0; i < 16; i++) + d[i * 3] = 0x3F; + + _screen->setScreenPalette(p2); + _screen->updateScreen(); + + delay(4 * _tickLength); + + _screen->setScreenPalette(p1); + if (_smoothScrollModeNormal) + _screen->copyRegion(112, 0, 112, 0, 176, 120, 2, 0); + + _screen->updateScreen(); + + } else { + Palette &p2 = _screen->getPalette(3); + + uint8 ovl[256]; + generateFlashPalette(p1, p2, stackPos(0)); + _screen->loadSpecialColors(p1); + _screen->loadSpecialColors(p2); + + if (_smoothScrollModeNormal) { + for (int i = 0; i < 256; i++) + ovl[i] = i; + ovl[1] = 6; + + _screen->copyRegion(112, 0, 112, 0, 176, 120, 0, 2); + _screen->applyOverlay(112, 0, 176, 120, 0, ovl); + } + + _screen->setScreenPalette(p2); + _screen->updateScreen(); + + delay(2 * _tickLength); + + _screen->setScreenPalette(p1); + if (_smoothScrollModeNormal) + _screen->copyRegion(112, 0, 112, 0, 176, 120, 2, 0); + + _screen->updateScreen(); + } + + return 0; +} + +int LoLEngine::olol_restoreMagicShroud(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_restoreMagicShroud(%p)", (const void *)script); + + WSAMovie_v2 *mov = new WSAMovie_v2(this); + mov->open("DARKLITE.WSA", 2, 0); + if (!mov->opened()) { + delete mov; + warning("LoLEngine::olol_restoreMagicShroud: Could not open file: \"DARKLITE.WSA\""); + return 1; + } + + _screen->hideMouse(); + + Palette *fadeTab[28]; + for (int i = 0; i < 28; i++) + fadeTab[i] = new Palette(_flags.use16ColorMode ? 16 : 256); + + Palette **tpal1 = &fadeTab[0]; + Palette **tpal2 = &fadeTab[1]; + Palette **tpal3 = &fadeTab[2]; + Palette **tpal4 = 0; + + int len = _flags.use16ColorMode ? 48 : 768; + _res->loadFileToBuf("LITEPAL1.COL", (*tpal1)->getData(), len); + tpal2 = _screen->generateFadeTable(tpal3, 0, *tpal1, 21); + + _res->loadFileToBuf("LITEPAL2.COL", (*tpal2)->getData(), len); + tpal4 = tpal2++; + + _res->loadFileToBuf("LITEPAL3.COL", (*tpal1)->getData(), len); + _screen->generateFadeTable(tpal2, *tpal4, *tpal1, 4); + + for (int i = 0; i < 21; i++) { + uint32 etime = _system->getMillis() + 20 * _tickLength; + mov->displayFrame(i, 0, 0, 0, 0, 0, 0); + _screen->setScreenPalette(**tpal3++); + _screen->updateScreen(); + + if (i == 2 || i == 5 || i == 8 || i == 11 || i == 13 || i == 15 || i == 17 || i == 19) + snd_playSoundEffect(95, -1); + + delayUntil(etime); + } + + snd_playSoundEffect(91, -1); + _screen->fadePalette(**tpal3++, 300); + + for (int i = 22; i < 38; i++) { + uint32 etime = _system->getMillis() + 12 * _tickLength; + mov->displayFrame(i, 0, 0, 0, 0, 0, 0); + if (i == 22 || i == 24 || i == 28 || i == 32) { + snd_playSoundEffect(131, -1); + _screen->setScreenPalette(**tpal3++); + } + _screen->updateScreen(); + delayUntil(etime); + } + + mov->close(); + delete mov; + + for (int i = 0; i < 28; i++) + delete fadeTab[i]; + + _screen->showMouse(); + + return 1; +} + +int LoLEngine::olol_disableControls(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_disableControls(%p) (%d)", (const void *)script, stackPos(0)); + return gui_disableControls(stackPos(0)); +} + +int LoLEngine::olol_enableControls(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_enableControls(%p)", (const void *)script); + return gui_enableControls(); +} + +int LoLEngine::olol_shakeScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_shakeScene(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + shakeScene(stackPos(0), stackPos(1), stackPos(2), 1); + return 1; +} + +int LoLEngine::olol_gasExplosion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_gasExplosion(%p) (%d)", (const void *)script, stackPos(0)); + processGasExplosion(stackPos(0)); + return 1; +} + +int LoLEngine::olol_calcNewBlockPosition(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_calcNewBlockPosition(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return calcNewBlockPosition(stackPos(0), stackPos(1)); +} + +int LoLEngine::olol_crossFadeScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_crossFadeScene(%p)", (const void *)script); + gui_drawScene(2); + _screen->crossFadeRegion(112, 0, 112, 0, 176, 120, 2, 0); + updateDrawPage2(); + return 1; +} + +int LoLEngine::olol_updateDrawPage2(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_updateDrawPage2(%p)", (const void *)script); + updateDrawPage2(); + return 1; +} + +int LoLEngine::olol_setMouseCursor(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setMouseCursor(%p) (%d)", (const void *)script, stackPos(0)); + if (stackPos(0) == 1) + setMouseCursorToIcon(133); + else + setMouseCursorToItemInHand(); + return 1; +} + +int LoLEngine::olol_characterSays(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_characterSays(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + + if (!_flags.isTalkie) + return 0; + + if (stackPos(0) == -1) { + snd_stopSpeech(true); + return 1; + } + + if (stackPos(0) != -2) + return characterSays(stackPos(0), stackPos(1), stackPos(2)); + else + return snd_updateCharacterSpeech(); +} + +int LoLEngine::olol_queueSpeech(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_queueSpeech(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (stackPos(0) && stackPos(1)) { + _nextSpeechId = stackPos(0) + 1000; + _nextSpeaker = stackPos(1); + } + return 1; +} + +int LoLEngine::olol_getItemPrice(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getItemPrice(%p) (%d)", (const void *)script, stackPos(0)); + int c = stackPos(0); + if (c < 0) { + c = -c; + if (c < 50) + return 50; + c = (c + 99) / 100; + return c * 100; + + } else { + for (int i = 0; i < 46; i++) { + if (_itemCost[i] >= c) + return _itemCost[i]; + } + } + + return 0; +} + +int LoLEngine::olol_getLanguage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getLanguage(%p)", (const void *)script); + return _lang; +} + +#pragma mark - + +int LoLEngine::tlol_setupPaletteFade(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_setupPaletteFade(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + _screen->getFadeParams(_screen->getPalette(0), param[0], _tim->_palDelayInc, _tim->_palDiff); + _tim->_palDelayAcc = 0; + return 1; +} + +int LoLEngine::tlol_loadPalette(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_loadPalette(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + const char *palFile = (const char *)(tim->text + READ_LE_UINT16(tim->text + (param[0] << 1))); + _screen->loadPalette(palFile, _screen->getPalette(0)); + return 1; +} + +int LoLEngine::tlol_setupPaletteFadeEx(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_setupPaletteFadeEx(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + _screen->copyPalette(0, 1); + + _screen->getFadeParams(_screen->getPalette(0), param[0], _tim->_palDelayInc, _tim->_palDiff); + _tim->_palDelayAcc = 0; + return 1; +} + +int LoLEngine::tlol_processWsaFrame(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_processWsaFrame(%p, %p) (%d, %d, %d, %d, %d)", + (const void *)tim, (const void *)param, param[0], param[1], param[2], param[3], param[4]); + + const int animIndex = tim->wsa[param[0]].anim - 1; + const int frame = param[1]; + const int x2 = param[2]; + const int y2 = param[3]; + const int factor = MAX<int>(0, (int16)param[4]); + + const int x1 = _tim->animator()->getAnimX(animIndex); + const int y1 = _tim->animator()->getAnimY(animIndex); + const Movie *wsa = _tim->animator()->getWsaCPtr(animIndex); + + int w1 = wsa->width(); + int h1 = wsa->height(); + int w2 = (w1 * factor) / 100; + int h2 = (h1 * factor) / 100; + + _tim->animator()->displayFrame(animIndex, 2, frame); + _screen->wsaFrameAnimationStep(x1, y1, x2, y2, w1, h1, w2, h2, 2, _flags.isDemo && _flags.platform != Common::kPlatformPC98 ? 0 : 8, 0); + if (!_flags.isDemo && _flags.platform != Common::kPlatformPC98) + _screen->checkedPageUpdate(8, 4); + _screen->updateScreen(); + + return 1; +} + +int LoLEngine::tlol_displayText(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_displayText(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], (int16)param[1]); + if (tim->isLoLOutro) + _tim->displayText(param[0], param[1], param[2]); + else + _tim->displayText(param[0], param[1]); + return 1; +} + +int LoLEngine::tlol_initSceneWindowDialogue(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_initSceneWindowDialogue(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + initSceneWindowDialogue(param[0]); + return 1; +} + +int LoLEngine::tlol_restoreAfterSceneWindowDialogue(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_restoreAfterSceneWindowDialogue(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + restoreAfterSceneWindowDialogue(param[0]); + return 1; +} + +int LoLEngine::tlol_giveItem(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_giveItem(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + int item = makeItem(param[0], param[1], param[2]); + if (addItemToInventory(item)) + return 1; + + deleteItem(item); + return 0; +} + +int LoLEngine::tlol_setPartyPosition(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_setPartyPosition(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], param[1]); + if (param[0] == 1) { + _currentDirection = param[1]; + } else if (param[0] == 0) { + _currentBlock = param[1]; + calcCoordinates(_partyPosX, _partyPosY, _currentBlock, 0x80, 0x80); + } + + return 1; +} + +int LoLEngine::tlol_fadeClearWindow(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_fadeClearWindow(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + + switch (param[0]) { + case 0: + _screen->fadeClearSceneWindow(10); + break; + + case 1: + if (_flags.use16ColorMode) { + _screen->fadePalette(_screen->getPalette(1), 10); + } else { + _screen->getPalette(3).copy(_screen->getPalette(0), 128); + _screen->loadSpecialColors(_screen->getPalette(3)); + _screen->fadePalette(_screen->getPalette(3), 10); + } + _screen->_fadeFlag = 0; + break; + + case 2: + _screen->fadeToBlack(10); + break; + + case 3: + _screen->loadSpecialColors(_screen->getPalette(3)); + _screen->fadePalette(_screen->getPalette(_flags.use16ColorMode ? 1 : 3), 10); + _screen->_fadeFlag = 0; + break; + + case 4: + if (_screen->_fadeFlag != 2) + _screen->fadeClearSceneWindow(10); + gui_drawPlayField(); + setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect); + _screen->_fadeFlag = 0; + break; + + case 5: + _screen->loadSpecialColors(_screen->getPalette(3)); + _screen->fadePalette(_screen->getPalette(1), 10); + _screen->_fadeFlag = 0; + break; + + default: + break; + } + + return 1; +} + +int LoLEngine::tlol_copyRegion(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_copyRegion(%p, %p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)tim, (const void *)param, param[0], param[1], param[2], param[3], param[4], param[5], param[6], param[7]); + _screen->copyRegion(param[0], param[1], param[2], param[3], param[4], param[5], param[6], param[7], Screen::CR_NO_P_CHECK); + if (!param[7]) + _screen->updateScreen(); + return 1; +} + +int LoLEngine::tlol_characterChat(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_characterChat(%p, %p) (%d, %d, %d)", (const void *)tim, (const void *)param, param[0], param[1], param[2]); + playCharacterScriptChat(param[0], param[1], 1, getLangString(param[2]), 0, param, 3); + return 1; +} + +int LoLEngine::tlol_drawScene(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_drawScene(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + gui_drawScene(param[0]); + //if (_sceneDrawPage2 != 2 && param[0] == 2) + // _screen->copyRegion(112 << 3, 0, 112 << 3, 0, 176 << 3, 120, _sceneDrawPage2, 2, Screen::CR_NO_P_CHECK); + return 1; +} + +int LoLEngine::tlol_update(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_update(%p, %p)", (const void *)tim, (const void *)param); + update(); + return 1; +} + +int LoLEngine::tlol_clearTextField(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_clearTextField(%p, %p)", (const void *)tim, (const void *)param); + if (_currentControlMode && !textEnabled()) + return 1; + _screen->setScreenDim(5); + const ScreenDim *d = _screen->_curDim; + _screen->fillRect(d->sx, d->sy, d->sx + d->w - (_flags.use16ColorMode ? 3 : 2), d->sy + d->h - 2, d->unkA); + _txt->clearDim(4); + _txt->resetDimTextPositions(4); + return 1; +} + +int LoLEngine::tlol_loadSoundFile(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_loadSoundFile(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + snd_loadSoundFile(param[0]); + return 1; +} + +int LoLEngine::tlol_playMusicTrack(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_playMusicTrack(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + snd_playTrack(param[0]); + return 1; +} + +int LoLEngine::tlol_playDialogueTalkText(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_playDialogueTalkText(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + if (!snd_playCharacterSpeech(param[0], 0, 0) || textEnabled()) + _txt->printDialogueText(4, getLangString(param[0]), 0, param, 1); + return 1; +} + +int LoLEngine::tlol_playSoundEffect(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_playSoundEffect(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + snd_playSoundEffect(param[0], -1); + return 1; +} + +int LoLEngine::tlol_startBackgroundAnimation(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_startBackgroundAnimation(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], param[1]); + _tim->animator()->start(param[0], param[1]); + return 1; +} + +int LoLEngine::tlol_stopBackgroundAnimation(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_stopBackgroundAnimation(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + _tim->animator()->stop(param[0]); + return 1; +} + +int LoLEngine::tlol_fadeInScene(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_fadeInScene(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], param[1]); + const char *sceneFile = (const char *)(tim->text + READ_LE_UINT16(tim->text + (param[0] << 1))); + const char *overlayFile = (const char *)(tim->text + READ_LE_UINT16(tim->text + (param[1] << 1))); + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 0, 2, Screen::CR_NO_P_CHECK); + + char filename[32]; + strcpy(filename, sceneFile); + strcat(filename, ".CPS"); + + _screen->loadBitmap(filename, 7, 5, &_screen->getPalette(0)); + + uint8 *overlay = 0; + if (!_flags.use16ColorMode) { + filename[0] = 0; + + if (_flags.isTalkie) { + strcpy(filename, _languageExt[_lang]); + strcat(filename, "/"); + } + + strcat(filename, overlayFile); + overlay = _res->fileData(filename, 0); + + for (int i = 0; i < 3; ++i) { + uint32 endTime = _system->getMillis() + 10 * _tickLength; + _screen->copyBlockAndApplyOverlayOutro(4, 2, overlay); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + delayUntil(endTime); + } + } + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 4, 0, Screen::CR_NO_P_CHECK); + + if (_flags.use16ColorMode) { + _screen->fadePalette(_screen->getPalette(0), 5); + } else { + _screen->updateScreen(); + delete[] overlay; + } + + return 1; +} + +int LoLEngine::tlol_unusedResourceFunc(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_unusedResourceFunc(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + // The original used 0x6 / 0x7 for some resource caching, we don't need this. + return 1; +} + +int LoLEngine::tlol_fadeInPalette(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_fadeInPalette(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], param[1]); + const char *bitmap = (const char *)(tim->text + READ_LE_UINT16(tim->text + (param[0] << 1))); + + Palette pal(_screen->getPalette(0).getNumColors()); + _screen->loadBitmap(bitmap, 3, 3, &pal); + + if (_flags.use16ColorMode) { + _screen->getPalette(0).clear(); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->copyPage(2, 0); + } + + _screen->fadePalette(pal, param[1]); + + return 1; +} + +int LoLEngine::tlol_fadeOutSound(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_fadeOutSound(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + _sound->beginFadeOut(); + return 1; +} + +int LoLEngine::tlol_displayAnimFrame(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_displayAnimFrame(%p, %p) (%d, %d)", (const void *)tim, (const void *)param, param[0], param[1]); + + const int animIndex = tim->wsa[param[0]].anim - 1; + const Movie *wsa = _tim->animator()->getWsaCPtr(animIndex); + + if (param[1] == 0xFFFF) { + _screen->copyRegion(0, 0, 0, 0, 320, 200, 0, 2, Screen::CR_NO_P_CHECK); + } else { + _tim->animator()->displayFrame(animIndex, 2, param[1], 0); + _screen->copyRegion(wsa->xAdd(), wsa->yAdd(), wsa->xAdd(), wsa->yAdd(), wsa->width(), wsa->height(), 2, 0); + } + + return 1; +} + +int LoLEngine::tlol_delayForChat(const TIM *tim, const uint16 *param) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::tlol_delayForChat(%p, %p) (%d)", (const void *)tim, (const void *)param, param[0]); + if (!speechEnabled()) + delay(param[0]); + return 1; +} + +#pragma mark - + +typedef Common::Functor1Mem<EMCState *, int, LoLEngine> OpcodeV2; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV2(this, &LoLEngine::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV2(this, 0)) + +typedef Common::Functor2Mem<const TIM *, const uint16 *, int, LoLEngine> TIMOpcodeLoL; +#define SetTimOpcodeTable(x) timTable = &x; +#define OpcodeTim(x) timTable->push_back(new TIMOpcodeLoL(this, &LoLEngine::x)) +#define OpcodeTimUnImpl() timTable->push_back(new TIMOpcodeLoL(this, 0)) + +void LoLEngine::setupOpcodeTable() { + Common::Array<const Opcode *> *table = 0; + + _opcodes.reserve(192); + SetOpcodeTable(_opcodes); + // 0x00 + Opcode(olol_setWallType); + Opcode(olol_getWallType); + Opcode(olol_drawScene); + Opcode(olol_rollDice); + + // 0x04 + Opcode(olol_moveParty); + OpcodeUnImpl(); + Opcode(olol_delay); + Opcode(olol_setGameFlag); + + // 0x08 + Opcode(olol_testGameFlag); + Opcode(olol_loadLevelGraphics); + Opcode(olol_loadBlockProperties); + Opcode(olol_loadMonsterShapes); + + // 0x0C + Opcode(olol_deleteHandItem); + Opcode(olol_allocItemPropertiesBuffer); + Opcode(olol_setItemProperty); + Opcode(olol_makeItem); + + // 0x10 + Opcode(olol_placeMoveLevelItem); + Opcode(olol_createLevelItem); + Opcode(olol_getItemPara); + Opcode(olol_getCharacterStat); + + // 0x14 + Opcode(olol_setCharacterStat); + Opcode(olol_loadLevelShapes); + Opcode(olol_closeLevelShapeFile); + OpcodeUnImpl(); + + // 0x18 + Opcode(olol_loadDoorShapes); + Opcode(olol_initAnimStruct); + Opcode(olol_playAnimationPart); + Opcode(olol_freeAnimStruct); + + // 0x1C + Opcode(olol_getDirection); + Opcode(olol_characterSurpriseFeedback); + Opcode(olol_setMusicTrack); + Opcode(olol_setSequenceButtons); + + // 0x20 + Opcode(olol_setDefaultButtonState); + Opcode(olol_checkRectForMousePointer); + Opcode(olol_clearDialogueField); + Opcode(olol_setupBackgroundAnimationPart); + + // 0x24 + Opcode(olol_startBackgroundAnimation); + Opcode(o1_hideMouse); + Opcode(o1_showMouse); + Opcode(olol_fadeToBlack); + + // 0x28 + Opcode(olol_fadePalette); + Opcode(olol_loadBitmap); + Opcode(olol_stopBackgroundAnimation); + OpcodeUnImpl(); + + // 0x2C + OpcodeUnImpl(); + Opcode(olol_getGlobalScriptVar); + Opcode(olol_setGlobalScriptVar); + Opcode(olol_getGlobalVar); + + // 0x30 + Opcode(olol_setGlobalVar); + Opcode(olol_triggerDoorSwitch); + Opcode(olol_checkEquippedItemScriptFlags); + Opcode(olol_setDoorState); + + // 0x34 + Opcode(olol_updateBlockAnimations); + Opcode(olol_assignLevelDecorationShape); + Opcode(olol_resetBlockShapeAssignment); + Opcode(olol_copyRegion); + + // 0x38 + Opcode(olol_initMonster); + Opcode(olol_fadeClearSceneWindow); + Opcode(olol_fadeSequencePalette); + Opcode(olol_redrawPlayfield); + + // 0x3C + Opcode(olol_loadNewLevel); + Opcode(olol_getNearestMonsterFromCharacter); + Opcode(olol_dummy0); + Opcode(olol_loadMonsterProperties); + + // 0x40 + Opcode(olol_battleHitSkillTest); + Opcode(olol_inflictDamage); + OpcodeUnImpl(); + OpcodeUnImpl(); + + // 0x44 + Opcode(olol_moveMonster); + Opcode(olol_setupDialogueButtons); + Opcode(olol_giveTakeMoney); + Opcode(olol_checkMoney); + + // 0x48 + Opcode(olol_setScriptTimer); + Opcode(olol_createHandItem); + Opcode(olol_playAttackSound); + Opcode(olol_addRemoveCharacter); + + // 0x4C + Opcode(olol_giveItem); + OpcodeUnImpl(); + Opcode(olol_loadTimScript); + Opcode(olol_runTimScript); + + // 0x50 + Opcode(olol_releaseTimScript); + Opcode(olol_initSceneWindowDialogue); + Opcode(olol_restoreAfterSceneWindowDialogue); + Opcode(olol_getItemInHand); + + // 0x54 + Opcode(olol_checkMagic); + Opcode(olol_giveItemToMonster); + Opcode(olol_loadLangFile); + Opcode(olol_playSoundEffect); + + // 0x58 + Opcode(olol_processDialogue); + Opcode(olol_stopTimScript); + Opcode(olol_getWallFlags); + Opcode(olol_changeMonsterStat); + + // 0x5C + Opcode(olol_getMonsterStat); + Opcode(olol_releaseMonsterShapes); + Opcode(olol_playCharacterScriptChat); + Opcode(olol_update); + + // 0x60 + Opcode(olol_playEnvironmentalSfx); + Opcode(olol_healCharacter); + Opcode(olol_drawExitButton); + Opcode(olol_loadSoundFile); + + // 0x64 + Opcode(olol_playMusicTrack); + Opcode(olol_deleteMonstersFromBlock); + Opcode(olol_countBlockItems); + Opcode(olol_characterSkillTest); + + // 0x68 + Opcode(olol_countAllMonsters); + Opcode(olol_playEndSequence); + Opcode(olol_stopPortraitSpeechAnim); + Opcode(olol_setPaletteBrightness); + + // 0x6C + Opcode(olol_calcInflictableDamage); + Opcode(olol_getInflictedDamage); + Opcode(olol_checkForCertainPartyMember); + Opcode(olol_printMessage); + + // 0x70 + Opcode(olol_deleteLevelItem); + Opcode(olol_calcInflictableDamagePerItem); + Opcode(olol_distanceAttack); + Opcode(olol_removeCharacterEffects); + + // 0x74 + Opcode(olol_checkInventoryFull); + Opcode(olol_moveBlockObjects); + OpcodeUnImpl(); + OpcodeUnImpl(); + + // 0x78 + Opcode(olol_addSpellToScroll); + Opcode(olol_playDialogueText); + Opcode(olol_playDialogueTalkText); + Opcode(olol_checkMonsterTypeHostility); + + // 0x7C + Opcode(olol_setNextFunc); + Opcode(olol_dummy1); + OpcodeUnImpl(); + Opcode(olol_suspendMonster); + + // 0x80 + Opcode(olol_setScriptTextParameter); + Opcode(olol_triggerEventOnMouseButtonClick); + Opcode(olol_printWindowText); + Opcode(olol_countSpecificMonsters); + + // 0x84 + Opcode(olol_updateBlockAnimations2); + Opcode(olol_checkPartyForItemType); + Opcode(olol_blockDoor); + Opcode(olol_resetTimDialogueState); + + // 0x88 + Opcode(olol_getItemOnPos); + Opcode(olol_removeLevelItem); + Opcode(olol_savePage5); + Opcode(olol_restorePage5); + + // 0x8C + Opcode(olol_initDialogueSequence); + Opcode(olol_restoreAfterDialogueSequence); + Opcode(olol_setSpecialSceneButtons); + Opcode(olol_restoreButtonsAfterSpecialScene); + + // 0x90 + OpcodeUnImpl(); + OpcodeUnImpl(); + Opcode(olol_prepareSpecialScene); + Opcode(olol_restoreAfterSpecialScene); + + // 0x94 + Opcode(olol_assignCustomSfx); + OpcodeUnImpl(); + Opcode(olol_findAssignedMonster); + Opcode(olol_checkBlockForMonster); + + // 0x98 + Opcode(olol_crossFadeRegion); + Opcode(olol_calcCoordinatesAddDirectionOffset); + Opcode(olol_resetPortraitsAndDisableSysTimer); + Opcode(olol_enableSysTimer); + + // 0x9C + Opcode(olol_checkNeedSceneRestore); + Opcode(olol_getNextActiveCharacter); + Opcode(olol_paralyzePoisonCharacter); + Opcode(olol_drawCharPortrait); + + // 0xA0 + Opcode(olol_removeInventoryItem); + OpcodeUnImpl(); + OpcodeUnImpl(); + Opcode(olol_getAnimationLastPart); + + // 0xA4 + Opcode(olol_assignSpecialGuiShape); + Opcode(olol_findInventoryItem); + Opcode(olol_restoreFadePalette); + Opcode(olol_calcNewBlockPosition); + + // 0xA8 + Opcode(olol_getSelectedCharacter); + Opcode(olol_setHandItem); + Opcode(olol_drinkBezelCup); + Opcode(olol_changeItemTypeOrFlag); + + // 0xAC + Opcode(olol_placeInventoryItemInHand); + Opcode(olol_castSpell); + Opcode(olol_pitDrop); + Opcode(olol_increaseSkill); + + // 0xB0 + Opcode(olol_paletteFlash); + Opcode(olol_restoreMagicShroud); + Opcode(olol_dummy1); // anim buffer select? + Opcode(olol_disableControls); + + // 0xB4 + Opcode(olol_enableControls); + Opcode(olol_shakeScene); + Opcode(olol_gasExplosion); + Opcode(olol_calcNewBlockPosition); + + // 0xB8 + Opcode(olol_crossFadeScene); + Opcode(olol_updateDrawPage2); + Opcode(olol_setMouseCursor); + Opcode(olol_characterSays); + + // 0xBC + Opcode(olol_queueSpeech); + Opcode(olol_getItemPrice); + Opcode(olol_getLanguage); + Opcode(olol_dummy0); + + Common::Array<const TIMOpcode *> *timTable = 0; + + _timIntroOpcodes.reserve(8); + SetTimOpcodeTable(_timIntroOpcodes); + + // 0x00 + OpcodeTim(tlol_setupPaletteFade); + OpcodeTimUnImpl(); + OpcodeTim(tlol_loadPalette); + OpcodeTim(tlol_setupPaletteFadeEx); + + // 0x04 + OpcodeTim(tlol_processWsaFrame); + OpcodeTim(tlol_displayText); + OpcodeTimUnImpl(); + OpcodeTimUnImpl(); + + _timOutroOpcodes.reserve(16); + SetTimOpcodeTable(_timOutroOpcodes); + + // 0x00 + OpcodeTim(tlol_setupPaletteFade); + OpcodeTimUnImpl(); + OpcodeTim(tlol_loadPalette); + OpcodeTim(tlol_setupPaletteFadeEx); + + // 0x04 + OpcodeTimUnImpl(); + OpcodeTim(tlol_fadeInScene); + OpcodeTim(tlol_unusedResourceFunc); + OpcodeTim(tlol_unusedResourceFunc); + + // 0x08 + OpcodeTim(tlol_fadeInPalette); + OpcodeTimUnImpl(); + OpcodeTimUnImpl(); + OpcodeTim(tlol_fadeOutSound); + + // 0x0C + OpcodeTim(tlol_displayAnimFrame); + OpcodeTim(tlol_delayForChat); + OpcodeTim(tlol_displayText); + OpcodeTimUnImpl(); + + _timIngameOpcodes.reserve(17); + SetTimOpcodeTable(_timIngameOpcodes); + + // 0x00 + OpcodeTim(tlol_initSceneWindowDialogue); + OpcodeTim(tlol_restoreAfterSceneWindowDialogue); + OpcodeTimUnImpl(); + OpcodeTim(tlol_giveItem); + + // 0x04 + OpcodeTim(tlol_setPartyPosition); + OpcodeTim(tlol_fadeClearWindow); + OpcodeTim(tlol_copyRegion); + OpcodeTim(tlol_characterChat); + + // 0x08 + OpcodeTim(tlol_drawScene); + OpcodeTim(tlol_update); + OpcodeTim(tlol_clearTextField); + OpcodeTim(tlol_loadSoundFile); + + // 0x0C + OpcodeTim(tlol_playMusicTrack); + OpcodeTim(tlol_playDialogueTalkText); + OpcodeTim(tlol_playSoundEffect); + OpcodeTim(tlol_startBackgroundAnimation); + + // 0x10 + OpcodeTim(tlol_stopBackgroundAnimation); +} + +} // End of namespace Kyra + +#endif // ENABLE_LOL diff --git a/engines/kyra/script/script_mr.cpp b/engines/kyra/script/script_mr.cpp new file mode 100644 index 0000000000..1051fccbc6 --- /dev/null +++ b/engines/kyra/script/script_mr.cpp @@ -0,0 +1,1377 @@ +/* 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 "kyra/engine/kyra_mr.h" +#include "kyra/text/text_mr.h" +#include "kyra/resource/resource.h" + +#include "common/system.h" + +namespace Kyra { + +int KyraEngine_MR::o3_getMalcolmShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_getMaloclmShapes(%p) ()", (const void *)script); + return _characterShapeFile; +} + +int KyraEngine_MR::o3_setCharacterPos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setCharacterPos(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int x = stackPos(0); + int y = stackPos(1); + + if (x != -1 && y != -1) { + x &= ~3; + y &= ~1; + } + + _mainCharacter.x1 = _mainCharacter.x2 = x; + _mainCharacter.y1 = _mainCharacter.y2 = y; + + return 0; +} + +int KyraEngine_MR::o3_defineObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_defineObject(%p) (%d, '%s', %d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + TalkObject &obj = _talkObjectList[stackPos(0)]; + strcpy(obj.filename, stackPosString(1)); + obj.sceneAnim = stackPos(2); + obj.sceneScript = stackPos(3); + obj.x = stackPos(4); + obj.y = stackPos(5); + obj.color = stackPos(6); + obj.sceneId = stackPos(7); + return 0; +} + +int KyraEngine_MR::o3_refreshCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_refreshCharacter(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + const int frame = stackPos(0); + const int facing = stackPos(1); + const bool updateNeed = stackPos(2) != 0; + + if (facing >= 0) + _mainCharacter.facing = facing; + + if (frame >= 0 && frame != 87) + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + else + _mainCharacter.animFrame = 87; + + updateCharacterAnim(0); + + if (updateNeed) + refreshAnimObjectsIfNeed(); + return 0; +} + +int KyraEngine_MR::o3_getMalcolmsMood(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_getMalcolmsMood(%p) ()", (const void *)script); + return _malcolmsMood; +} + +int KyraEngine_MR::o3_getCharacterFrameFromFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_getCharacterFrameFromFacing(%p) ()", (const void *)script); + return _characterFrameTable[_mainCharacter.facing]; +} + +int KyraEngine_MR::o3_setCharacterFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setCharacterFacing(%p) (%d)", (const void *)script, stackPos(0)); + _mainCharacter.facing = stackPos(0); + return 0; +} + +int KyraEngine_MR::o3_showSceneFileMessage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_showSceneFileMessage(%p) (%d)", (const void *)script, stackPos(0)); + showMessage((const char *)getTableEntry(_scenesFile, stackPos(0)), 0xFF, 0xF0); + return 0; +} + +int KyraEngine_MR::o3_setCharacterAnimFrameFromFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setCharacterAnimFrameFromFacing(%p) ()", (const void *)script); + updateCharPal(0); + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + updateCharacterAnim(0); + refreshAnimObjectsIfNeed(); + return 0; +} + +int KyraEngine_MR::o3_showBadConscience(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_showBadConscience(%p) ()", (const void *)script); + showBadConscience(); + return 0; +} + +int KyraEngine_MR::o3_hideBadConscience(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_hideBadConscience(%p) ()", (const void *)script); + hideBadConscience(); + return 0; +} + +int KyraEngine_MR::o3_showAlbum(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_showAlbum(%p) ()", (const void *)script); + showAlbum(); + return 0; +} + +int KyraEngine_MR::o3_setInventorySlot(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setInventorySlot(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int slot = MAX<int16>(0, MIN<int16>(10, stackPos(0))); + return (_mainCharacter.inventory[slot] = stackPos(1)); +} + +int KyraEngine_MR::o3_getInventorySlot(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_getInventorySlot(%p) (%d)", (const void *)script, stackPos(0)); + return _mainCharacter.inventory[stackPos(0)]; +} + +int KyraEngine_MR::o3_addItemToInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_addItemToInventory(%p) (%d)", (const void *)script, stackPos(0)); + int slot = findFreeInventorySlot(); + if (slot >= 0) { + _mainCharacter.inventory[slot] = stackPos(0); + if (_inventoryState) { + redrawInventory(0); + } + } + return slot; +} + +int KyraEngine_MR::o3_addItemToCurScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_addItemToCurScene(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + const uint16 item = stackPos(0); + int x = stackPos(1); + int y = stackPos(2); + int itemSlot = findFreeItem(); + + if (x < 20) + x = 20; + else if (x > 299) + x = 299; + + if (y < 18) + y = 18; + else if (y > 187) + y = 187; + + if (itemSlot >= 0) { + _itemList[itemSlot].x = x; + _itemList[itemSlot].y = y; + _itemList[itemSlot].id = item; + _itemList[itemSlot].sceneId = _mainCharacter.sceneId; + addItemToAnimList(itemSlot); + refreshAnimObjectsIfNeed(); + } + + return itemSlot; +} + +int KyraEngine_MR::o3_objectChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_objectChat(%p) (%d)", (const void *)script, stackPos(0)); + int id = stackPos(0); + const char *str = (const char *)getTableEntry(_useActorBuffer ? _actorFile : _sceneStrings, id); + if (str) { + objectChat(str, 0, _vocHigh, id); + playStudioSFX(str); + } + return 0; +} + +int KyraEngine_MR::o3_resetInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_resetInventory(%p) ()", (const void *)script); + memset(_mainCharacter.inventory, -1, sizeof(_mainCharacter.inventory)); + return 0; +} + +int KyraEngine_MR::o3_removeInventoryItemInstances(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeInventoryItemInstances(%p) (%d)", (const void *)script, stackPos(0)); + const int item = stackPos(0); + for (int i = 0; i < 10; ++i) { + if (_mainCharacter.inventory[i] == item) + _mainCharacter.inventory[i] = kItemNone; + } + return 0; +} + +int KyraEngine_MR::o3_countInventoryItemInstances(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_countInventoryItemInstances(%p) (%d)", (const void *)script, stackPos(0)); + const int item = stackPos(0); + int count = 0; + + for (int i = 0; i < 10; ++i) { + if (_mainCharacter.inventory[i] == item) + ++count; + } + + if (_itemInHand == item) + ++count; + + return count; +} + +int KyraEngine_MR::o3_npcChatSequence(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_npcChatSequence(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int id = stackPos(0); + const char *str = (const char *)getTableEntry(_sceneStrings, id); + if (str) + npcChatSequence(str, stackPos(1), _vocHigh, id); + return 0; +} + +int KyraEngine_MR::o3_badConscienceChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_badConscienceChat(%p) (%d)", (const void *)script, stackPos(0)); + int id = stackPos(0); + const char *str = (const char *)getTableEntry(_useActorBuffer ? _actorFile : _sceneStrings, id); + badConscienceChat(str, _vocHigh, id); + return 0; +} + +int KyraEngine_MR::o3_wipeDownMouseItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o3_wipeDownMouseItem(%p) (-, %d, %d)", (const void *)script, stackPos(1), stackPos(2)); + _screen->hideMouse(); + const int x = stackPos(1) - 12; + const int y = stackPos(2) - 19; + + if (_itemInHand >= 0) { + backUpGfxRect32x32(x, y); + uint8 *shape = getShapePtr(_itemInHand+248); + for (int curY = y, height = 20; height > 0; height -= 2, curY += 2) { + restoreGfxRect32x32(x, y); + _screen->setNewShapeHeight(shape, height); + const uint32 waitTime = _system->getMillis() + _tickLength; + _screen->drawShape(0, shape, x, curY, 0, 0); + _screen->updateScreen(); + delayUntil(waitTime); + } + restoreGfxRect32x32(x, y); + _screen->resetShapeHeight(shape); + } + + _screen->showMouse(); + removeHandItem(); + + return 0; +} + +int KyraEngine_MR::o3_setMalcolmsMood(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setMalcolmsMood(%p) (%d)", (const void *)script, stackPos(0)); + return (_malcolmsMood = stackPos(0)); +} + +int KyraEngine_MR::o3_updateScore(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_updateScore(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return updateScore(stackPos(0), stackPos(1)) ? 1 : 0; +} + +int KyraEngine_MR::o3_makeSecondChanceSave(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_makeSecondChanceSave(%p) ()", (const void *)script); + saveGameStateIntern(999, "Autosave", 0); + return 0; +} + +int KyraEngine_MR::o3_setSceneFilename(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setSceneFilename(%p) (%d, '%s')", (const void *)script, stackPos(0), stackPosString(1)); + strcpy(_sceneList[stackPos(0)].filename1, stackPosString(1)); + _sceneList[stackPos(0)].filename1[9] = 0; + return 0; +} + +int KyraEngine_MR::o3_removeItemsFromScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeItemsFromScene(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + const uint16 itemId = stackPos(0); + const uint16 sceneId = stackPos(1); + const bool allItems = (stackPos(2) != 0); + + int retValue = 0; + + for (int i = 0; i < 50; ++i) { + if (_itemList[i].sceneId == sceneId && _itemList[i].id == itemId) { + resetItem(i); + retValue = 1; + if (!allItems) + return 1; + } + } + + return retValue; +} + +int KyraEngine_MR::o3_disguiseMalcolm(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o3_disguiseMalcolm(%p) (%d)", (const void *)script, stackPos(0)); + loadCharacterShapes(stackPos(0)); + updateDlgIndex(); + return 0; +} + +int KyraEngine_MR::o3_drawSceneShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o3_drawSceneShape(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + + int shape = stackPos(0); + int flag = (stackPos(1) != 0) ? 1 : 0; + + restorePage3(); + + const int x = _sceneShapeDescs[shape].drawX; + const int y = _sceneShapeDescs[shape].drawY; + + _screen->drawShape(2, _sceneShapes[shape], x, y, 2, flag); + + _screen->copyRegionToBuffer(3, 0, 0, 320, 200, _gamePlayBuffer); + + _screen->drawShape(0, _sceneShapes[shape], x, y, 2, flag); + + flagAnimObjsForRefresh(); + refreshAnimObjectsIfNeed(); + return 0; +} + +int KyraEngine_MR::o3_drawSceneShapeOnPage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_drawSceneShapeOnPage(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + const int shape = stackPos(0); + + int x = _sceneShapeDescs[shape].drawX; + int y = _sceneShapeDescs[shape].drawY; + _screen->drawShape(stackPos(2), _sceneShapes[shape], x, y, 2, (stackPos(1) != 0) ? 1 : 0); + return 0; +} + +int KyraEngine_MR::o3_checkInRect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_checkInRect(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + const int x1 = stackPos(0); + const int y1 = stackPos(1); + const int x2 = stackPos(2); + const int y2 = stackPos(3); + int x = stackPos(4), y = stackPos(5); + if (_itemInHand >= 0) { + const int8 *desc = &_itemBuffer2[_itemInHand*2]; + x -= 12; + x += desc[0]; + y -= 19; + y += desc[1]; + } + + if (x >= x1 && x <= x2 && y >= y1 && y <= y2) + return 1; + else + return 0; +} + +int KyraEngine_MR::o3_updateConversations(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_updateConversations(%p) (%d)", (const void *)script, stackPos(0)); + int dlgIndex = stackPos(0); + switch (_currentChapter-2) { + case 0: + dlgIndex -= 34; + break; + + case 1: + dlgIndex -= 54; + break; + + case 2: + dlgIndex -= 55; + break; + + case 3: + dlgIndex -= 70; + break; + + default: + break; + } + + int convs[4]; + Common::fill(convs, convs+4, -1); + + if (_currentChapter == 1) { + switch (_mainCharacter.dlgIndex) { + case 0: + convs[0] = 6; + convs[1] = 12; + break; + + case 2: + convs[0] = 8; + convs[1] = 14; + break; + + case 3: + convs[0] = 9; + convs[1] = 15; + break; + + case 4: + convs[0] = 10; + convs[1] = 16; + break; + + case 5: + convs[0] = 11; + convs[1] = 17; + break; + + case 6: + convs[0] = 0; + convs[1] = 12; + break; + + case 8: + convs[0] = 2; + convs[1] = 14; + break; + + case 9: + convs[0] = 3; + convs[1] = 15; + break; + + case 10: + convs[0] = 4; + convs[1] = 16; + break; + + case 11: + convs[0] = 5; + convs[1] = 17; + break; + + case 12: + convs[0] = 0; + convs[1] = 6; + break; + + case 14: + convs[0] = 2; + convs[1] = 8; + break; + + case 15: + convs[0] = 3; + convs[1] = 9; + break; + + case 16: + convs[0] = 4; + convs[1] = 10; + break; + + case 17: + convs[0] = 5; + convs[1] = 11; + break; + + default: + break; + } + } else if (_currentChapter == 2) { + switch (_mainCharacter.dlgIndex) { + case 0: + convs[0] = 4; + convs[1] = 8; + convs[2] = 5; + convs[3] = 9; + break; + + case 1: + convs[0] = 4; + convs[1] = 8; + convs[2] = 0; + convs[3] = 5; + break; + + case 2: + convs[0] = 6; + convs[2] = 11; + break; + + case 3: + convs[0] = 7; + convs[2] = 12; + break; + + case 4: + convs[0] = 0; + convs[1] = 8; + convs[2] = 1; + convs[3] = 9; + break; + + case 5: + convs[0] = 0; + convs[1] = 8; + convs[2] = 4; + convs[3] = 1; + break; + + case 6: + convs[0] = 2; + convs[1] = 10; + break; + + case 7: + convs[0] = 3; + convs[1] = 11; + break; + + case 8: + convs[0] = 0; + convs[1] = 4; + convs[2] = 1; + break; + + case 9: + convs[0] = 0; + convs[1] = 4; + convs[2] = 0; + convs[3] = 1; + break; + + case 10: + convs[0] = 2; + convs[1] = 6; + break; + + case 11: + convs[0] = 3; + convs[1] = 7; + break; + + default: + break; + } + } else if (_currentChapter == 4) { + if (_malcolmsMood == 0) { + convs[0] = _mainCharacter.dlgIndex - 10; + convs[1] = _mainCharacter.dlgIndex - 5; + } else if (_malcolmsMood == 1) { + convs[0] = _mainCharacter.dlgIndex + 5; + convs[1] = _mainCharacter.dlgIndex + 10; + } else if (_malcolmsMood == 2) { + convs[0] = _mainCharacter.dlgIndex - 5; + convs[1] = _mainCharacter.dlgIndex + 5; + } + } + + for (int i = 0; i < 4; ++i) { + if (convs[i] != -1) + _conversationState[dlgIndex][convs[i]] = 0; + } + + return 1; +} + +int KyraEngine_MR::o3_removeItemSlot(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeItemSlot(%p) (%d)", (const void *)script, stackPos(0)); + deleteItemAnimEntry(stackPos(0)); + _itemList[stackPos(0)].id = kItemNone; + return 1; +} + +int KyraEngine_MR::o3_setSceneDim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setSceneDim(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _sceneMinX = stackPos(0); + _sceneMaxX = stackPos(1); + return 0; +} + +int KyraEngine_MR::o3_setSceneAnimPosAndFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setSceneAnimPosAndFrame(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + SceneAnim &anim = _sceneAnims[stackPos(0)]; + const int newX2 = stackPos(1); + const int newY2 = stackPos(2); + const int newX = stackPos(3); + const int newY = stackPos(4); + + if (newX2 >= 0) + anim.x2 = newX2; + if (newY2 >= 0) + anim.y2 = newY2; + + if (newX >= 0) + anim.x = newX; + else + anim.x = anim.x2 + (anim.width >> 1); + + if (newY >= 0) + anim.y = newY; + else + anim.y = anim.y2 + anim.height - 1; + + updateSceneAnim(stackPos(0), stackPos(5)); + _specialSceneScriptRunFlag = false; + return 0; +} + +int KyraEngine_MR::o3_removeItemInstances(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeItemInstances(%p) (%d)", (const void *)script, stackPos(0)); + const int16 item = stackPos(0); + + int deleted = 0; + + for (int i = 0; i < 10; ++i) { + if (_mainCharacter.inventory[i] == item) { + _mainCharacter.inventory[i] = kItemNone; + ++deleted; + } + } + + if (_itemInHand == item) { + removeHandItem(); + ++deleted; + } + + for (int i = 0; i < 50; ++i) { + if (_itemList[i].id == item) { + _itemList[i].id = kItemNone; + ++deleted; + } + } + + return deleted; +} + +int KyraEngine_MR::o3_disableInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_disableInventory(%p) ()", (const void *)script); + _enableInventory = false; + return 0; +} + +int KyraEngine_MR::o3_enableInventory(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_enableInventory(%p) ()", (const void *)script); + _enableInventory = true; + return 1; +} + +int KyraEngine_MR::o3_enterNewScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_enterNewScene(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), + stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + + _screen->hideMouse(); + enterNewScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + + _unk5 = 1; + + if (_mainCharX == -1 || _mainCharY == -1) { + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + updateCharacterAnim(0); + } + _screen->showMouse(); + + return 0; +} + +int KyraEngine_MR::o3_switchScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_switchScene(%p) (%d)", (const void *)script, stackPos(0)); + setGameFlag(1); + _mainCharX = _mainCharacter.x1; + _mainCharY = _mainCharacter.y1; + _noScriptEnter = false; + enterNewScene(stackPos(0), _mainCharacter.facing, 0, 0, 0); + _noScriptEnter = true; + return 0; +} + +int KyraEngine_MR::o3_setMalcolmPos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setMalcolmPos(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _mainCharX = stackPos(0); + _mainCharY = stackPos(1); + + if (_mainCharX == -1 && _mainCharY == -1) + _mainCharacter.animFrame = 87; + else + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + + return 0; +} + +int KyraEngine_MR::o3_stopMusic(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_stopMusic(%p) ()", (const void *)script); + stopMusicTrack(); + return 0; +} + +int KyraEngine_MR::o3_playSoundEffect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_playSoundEffect(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + snd_playSoundEffect(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_MR::o3_getScore(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_getScore(%p) ()", (const void *)script); + return _score; +} + +int KyraEngine_MR::o3_daggerWarning(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_daggerWarning(%p) ()", (const void *)script); + int selection = 1; + + _screen->hideMouse(); + _screen->copyRegionToBuffer(1, 0, 0, 320, 200, _screenBuffer); + int curPageBackUp = _screen->_curPage; + _screen->_curPage = 2; + + _screen->drawFilledBox(0, 0, 0x13F, 0xC7, 0xB4, 0xB3, 0xB6); + _screen->drawFilledBox(0xF, 0xAA, 0x68, 0xBA, 0xB4, 0xB3, 0xB6); + _screen->drawFilledBox(0x73, 0xAA, 0xCC, 0xBA, 0xB4, 0xB3, 0xB6); + _screen->drawFilledBox(0xD6, 0xAA, 0x12F, 0xBA, 0xB4, 0xB3, 0xB6); + + int y = 15; + for (int i = 100; i <= 107; ++i) { + const char *str = (const char *)getTableEntry(_cCodeFile, i); + int x = _text->getCenterStringX(str, 0, 0x13F); + _text->printText(str, x, y, 0xFF, 0xF0, 0x00); + y += 10; + } + y += 15; + for (int i = 110; i <= 113; ++i) { + const char *str = (const char *)getTableEntry(_cCodeFile, i); + int x = _text->getCenterStringX(str, 0, 0x13F); + _text->printText(str, x, y, 0xFF, 0xF0, 0x00); + y += 10; + } + + const char *str = 0; + int x = 0; + + str = (const char *)getTableEntry(_cCodeFile, 120); + x = _text->getCenterStringX(str, 0xF, 0x68); + _text->printText(str, x, 174, 0xFF, 0xF0, 0x00); + + str = (const char *)getTableEntry(_cCodeFile, 121); + x = _text->getCenterStringX(str, 0x73, 0xCC); + _text->printText(str, x, 174, 0xFF, 0xF0, 0x00); + + str = (const char *)getTableEntry(_cCodeFile, 122); + x = _text->getCenterStringX(str, 0xD6, 0x12F); + _text->printText(str, x, 174, 0xFF, 0xF0, 0x00); + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + + _screen->_curPage = curPageBackUp; + _screen->showMouse(); + + while (!shouldQuit()) { + int keys = checkInput(0); + removeInputTop(); + + if (keys == 198 || keys == 199) { + if (_mouseX >= 15 && _mouseX <= 104 && _mouseY >= 170 && _mouseY <= 186) { + selection = 1; + break; + } else if (_mouseX >= 115 && _mouseX <= 204 && _mouseY >= 170 && _mouseY <= 186) { + selection = 2; + break; + } else if (_mouseX >= 214 && _mouseX <= 303 && _mouseY >= 170 && _mouseY <= 186) { + selection = 3; + break; + } + } + + delay(10); + } + + restorePage3(); + _screen->copyBlockToPage(1, 0, 0, 320, 200, _screenBuffer); + return selection; +} + +int KyraEngine_MR::o3_blockOutWalkableRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_blockOutWalkableRegion(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + const int x1 = stackPos(0); + int y1 = stackPos(1); + const int x2 = stackPos(2); + int y2 = stackPos(3); + + if (y1 < _maskPageMinY) + y1 = _maskPageMinY; + if (y2 > _maskPageMaxY) + y2 = _maskPageMaxY; + + _screen->blockOutRegion(x1, y1, x2-x1+1, y2-y1+1); + return 0; +} + +int KyraEngine_MR::o3_showSceneStringsMessage(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_showSceneStringsMessage(%p) (%d)", (const void *)script, stackPos(0)); + showMessage((const char *)getTableEntry(_sceneStrings, stackPos(0)), 0xFF, 0xF0); + return 0; +} + +int KyraEngine_MR::o3_showGoodConscience(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_showGoodConscience(%p) ()", (const void *)script); + showGoodConscience(); + return 0; +} + +int KyraEngine_MR::o3_goodConscienceChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_goodConscienceChat(%p) (%d)", (const void *)script, stackPos(0)); + int id = stackPos(0); + const char *str = (const char *)getTableEntry(_useActorBuffer ? _actorFile : _sceneStrings, id); + goodConscienceChat(str, _vocHigh, id); + return 0; +} + +int KyraEngine_MR::o3_hideGoodConscience(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_hideGoodConscience(%p) ()", (const void *)script); + hideGoodConscience(); + return 0; +} + +int KyraEngine_MR::o3_defineSceneAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_defineSceneAnim(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, '%s')", + (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), + stackPos(8), stackPos(9), stackPos(10), stackPos(11), stackPosString(12)); + const int animId = stackPos(0); + SceneAnim &anim = _sceneAnims[animId]; + + uint16 flags = anim.flags = stackPos(1); + int x = anim.x = stackPos(2); + int y = anim.y = stackPos(3); + int x2 = anim.x2 = stackPos(4); + int y2 = anim.y2 = stackPos(5); + int w = anim.width = stackPos(6); + int h = anim.height = stackPos(7); + anim.specialSize = stackPos(9); + anim.shapeIndex = stackPos(11); + const char *filename = stackPosString(12); + + if (filename) + strcpy(anim.filename, filename); + + if (flags & 8) { + _sceneAnimMovie[animId]->open(filename, 1, 0); + if (_sceneAnimMovie[animId]->opened()) { + anim.wsaFlag = 1; + if (x2 == -1) + x2 = _sceneAnimMovie[animId]->xAdd(); + if (y2 == -1) + y2 = _sceneAnimMovie[animId]->yAdd(); + if (w == -1) + w = _sceneAnimMovie[animId]->width(); + if (h == -1) + h = _sceneAnimMovie[animId]->height(); + if (x == -1) + x = (w >> 1) + x2; + if (y == -1) + y = y2 + h - 1; + + anim.x = x; + anim.y = y; + anim.x2 = x2; + anim.y2 = y2; + anim.width = w; + anim.height = h; + } + } + + return 9; +} + +int KyraEngine_MR::o3_updateSceneAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_updateSceneAnim(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + updateSceneAnim(stackPos(0), stackPos(1)); + _specialSceneScriptRunFlag = false; + return 0; +} + +int KyraEngine_MR::o3_runActorScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_runActorScript(%p) ()", (const void *)script); + EMCData data; + EMCState state; + memset(&data, 0, sizeof(data)); + memset(&state, 0, sizeof(state)); + + _res->exists("_ACTOR.EMC", true); + _emc->load("_ACTOR.EMC", &data, &_opcodes); + _emc->init(&state, &data); + _emc->start(&state, 0); + + state.regs[4] = _itemInHand; + state.regs[0] = _mainCharacter.sceneId; + + int vocHigh = _vocHigh; + _vocHigh = 200; + _useActorBuffer = true; + + while (_emc->isValid(&state)) + _emc->run(&state); + + _useActorBuffer = false; + _vocHigh = vocHigh; + _emc->unload(&data); + + if (queryGameFlag(0x218)) { + resetGameFlag(0x218); + enterNewScene(78, -1, 0, 0, 0); + } + + return 0; +} + +int KyraEngine_MR::o3_doDialog(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_doDialog(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + doDialog(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_MR::o3_setConversationState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setConversationState(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + int id = stackPos(0); + const int dlgIndex = stackPos(1); + const int value = stackPos(2); + + switch (_currentChapter-2) { + case 0: + id -= 34; + break; + + case 1: + id -= 54; + break; + + case 2: + id -= 55; + break; + + case 3: + id -= 70; + break; + + default: + break; + } + + return (_conversationState[id][dlgIndex] = value); +} + +int KyraEngine_MR::o3_getConversationState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_getConversationState(%p) (%d)", (const void *)script, stackPos(0)); + int id = stackPos(0); + const int dlgIndex = _mainCharacter.dlgIndex; + + switch (_currentChapter-2) { + case 0: + id -= 34; + break; + + case 1: + id -= 54; + break; + + case 2: + id -= 55; + break; + + case 3: + id -= 70; + break; + + default: + break; + } + + return _conversationState[id][dlgIndex]; +} + +int KyraEngine_MR::o3_changeChapter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_changeChapter(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + changeChapter(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 0; +} + +int KyraEngine_MR::o3_countItemInstances(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_countItemInstances(%p) (%d)", (const void *)script, stackPos(0)); + int count = 0; + const int16 item = stackPos(0); + + for (int i = 0; i < 10; ++i) { + if (_mainCharacter.inventory[i] == item) + ++count; + } + + if (_itemInHand == item) + ++count; + + for (int i = 0; i < 50; ++i) { + if (_itemList[i].id == item) + ++count; + } + + return count; +} + +int KyraEngine_MR::o3_dialogStartScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_dialogStartScript(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + dialogStartScript(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_MR::o3_dialogEndScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_dialogEndScript(%p) (%d)", (const void *)script, stackPos(0)); + dialogEndScript(stackPos(0)); + return 0; +} + +int KyraEngine_MR::o3_customChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_customChat(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + const int id = stackPos(0); + const int object = stackPos(1); + const char *str = (const char *)getTableEntry(_sceneStrings, id); + + if (!str) + return 0; + + strcpy(_stringBuffer, str); + _chatText = _stringBuffer; + _chatObject = object; + _chatVocHigh = _chatVocLow = -1; + objectChatInit(_stringBuffer, object, _vocHigh, id); + playVoice(_vocHigh, id); + return 0; +} + +int KyraEngine_MR::o3_customChatFinish(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_customChatFinish(%p) ()", (const void *)script); + _text->restoreScreen(); + _chatText = 0; + _chatObject = -1; + return 0; +} + +int KyraEngine_MR::o3_setupSceneAnimObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_setupSceneAnimObject(%p) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9), + stackPos(10), stackPos(11), stackPosString(12)); + setupSceneAnimObject(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), + stackPos(9), stackPos(10), stackPos(11), stackPosString(12)); + return 0; +} + +int KyraEngine_MR::o3_removeSceneAnimObject(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeSceneAnimObject(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + removeSceneAnimObject(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_MR::o3_dummy(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_dummy(%p) ()", (const void *)script); + return 0; +} + +#pragma mark - + +int KyraEngine_MR::o3a_setCharacterFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3a_setCharacterFrame(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + static const uint8 frameTable[] = { + 0x58, 0xD8, 0xD8, 0x98, 0x78, 0x78, 0xB8, 0xB8 + }; + + _animNewFrame = stackPos(0); + if (_useFrameTable) + _animNewFrame += frameTable[_mainCharacter.facing]; + + _animDelayTime = stackPos(1); + _animNeedUpdate = true; + return 0; +} + +int KyraEngine_MR::o3a_playSoundEffect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3a_playSoundEffect(%p) (%d)", (const void *)script, stackPos(0)); + snd_playSoundEffect(stackPos(0), 200); + return 0; +} + +#pragma mark - + +int KyraEngine_MR::o3d_updateAnim(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3d_updateAnim(%p) (%d)", (const void *)script, stackPos(0)); + if (_dialogSceneAnim >= 0) + updateSceneAnim(_dialogSceneAnim, stackPos(0)); + return 0; +} + +int KyraEngine_MR::o3d_delay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3d_delay(%p) (%d)", (const void *)script, stackPos(0)); + delayUntil(_system->getMillis() + stackPos(0) * _tickLength, false, true); + return 0; +} + +#pragma mark - + +typedef Common::Functor1Mem<EMCState *, int, KyraEngine_MR> OpcodeV3; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV3(this, &KyraEngine_MR::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV3(this, 0)) +void KyraEngine_MR::setupOpcodeTable() { + Common::Array<const Opcode *> *table = 0; + + _opcodes.reserve(176); + SetOpcodeTable(_opcodes); + // 0x00 + Opcode(o3_getMalcolmShapes); + Opcode(o3_setCharacterPos); + Opcode(o3_defineObject); + Opcode(o3_refreshCharacter); + // 0x04 + Opcode(o2_getCharacterX); + Opcode(o2_getCharacterY); + Opcode(o2_getCharacterFacing); + Opcode(o2_getCharacterScene); + // 0x08 + Opcode(o3_getMalcolmsMood); + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_getCharacterFrameFromFacing); + // 0x0C + Opcode(o2_setCharacterFacingOverwrite); + Opcode(o2_trySceneChange); + Opcode(o2_moveCharacter); + Opcode(o3_setCharacterFacing); + // 0x10 + OpcodeUnImpl(); + Opcode(o3_showSceneFileMessage); + Opcode(o3_dummy); + Opcode(o3_dummy); + // 0x14 + Opcode(o3_setCharacterAnimFrameFromFacing); + Opcode(o3_showBadConscience); + Opcode(o3_dummy); + Opcode(o3_hideBadConscience); + // 0x18 + OpcodeUnImpl(); + Opcode(o3_showAlbum); + Opcode(o3_setInventorySlot); + Opcode(o3_getInventorySlot); + // 0x1C + Opcode(o3_addItemToInventory); + OpcodeUnImpl(); + Opcode(o3_addItemToCurScene); + Opcode(o3_objectChat); + // 0x20 + Opcode(o2_checkForItem); + Opcode(o3_dummy); + Opcode(o3_resetInventory); + Opcode(o2_defineItem); + // 0x24 + Opcode(o3_removeInventoryItemInstances); + Opcode(o3_countInventoryItemInstances); + Opcode(o3_npcChatSequence); + Opcode(o1_queryGameFlag); + // 0x28 + Opcode(o1_resetGameFlag); + Opcode(o1_setGameFlag); + Opcode(o1_setHandItem); + Opcode(o1_removeHandItem); + // 0x2C + Opcode(o1_getMouseState); + Opcode(o1_hideMouse); + Opcode(o2_addSpecialExit); + Opcode(o1_setMousePos); + // 0x30 + Opcode(o1_showMouse); + Opcode(o3_badConscienceChat); + Opcode(o3_wipeDownMouseItem); + Opcode(o3_dummy); + // 0x34 + Opcode(o3_setMalcolmsMood); + Opcode(o3_playSoundEffect); + Opcode(o3_dummy); + Opcode(o2_delay); + // 0x38 + Opcode(o3_updateScore); + Opcode(o3_makeSecondChanceSave); + Opcode(o3_setSceneFilename); + OpcodeUnImpl(); + // 0x3C + Opcode(o3_removeItemsFromScene); + Opcode(o3_disguiseMalcolm); + Opcode(o3_drawSceneShape); + Opcode(o3_drawSceneShapeOnPage); + // 0x40 + Opcode(o3_checkInRect); + Opcode(o3_updateConversations); + Opcode(o3_removeItemSlot); + Opcode(o3_dummy); + // 0x44 + Opcode(o3_dummy); + Opcode(o3_setSceneDim); + OpcodeUnImpl(); + Opcode(o3_dummy); + // 0x48 + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_setSceneAnimPosAndFrame); + Opcode(o2_update); + // 0x4C + Opcode(o3_removeItemInstances); + Opcode(o3_dummy); + Opcode(o3_disableInventory); + Opcode(o3_enableInventory); + // 0x50 + Opcode(o3_enterNewScene); + Opcode(o3_switchScene); + Opcode(o2_getShapeFlag1); + Opcode(o3_dummy); + // 0x54 + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_setMalcolmPos); + Opcode(o3_stopMusic); + // 0x58 + Opcode(o1_playWanderScoreViaMap); + Opcode(o3_playSoundEffect); + Opcode(o3_getScore); + Opcode(o3_daggerWarning); + // 0x5C + Opcode(o3_blockOutWalkableRegion); + Opcode(o3_dummy); + Opcode(o3_showSceneStringsMessage); + OpcodeUnImpl(); + // 0x60 + Opcode(o1_getRand); + Opcode(o3_dummy); + Opcode(o1_setDeathHandler); + Opcode(o3_showGoodConscience); + // 0x64 + Opcode(o3_goodConscienceChat); + Opcode(o3_hideGoodConscience); + Opcode(o3_dummy); + Opcode(o3_dummy); + // 0x68 + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o2_waitForConfirmationClick); + // 0x6C + Opcode(o3_dummy); + Opcode(o2_defineRoomEntrance); + Opcode(o2_runAnimationScript); + Opcode(o2_setSpecialSceneScriptRunTime); + // 0x70 + Opcode(o3_defineSceneAnim); + Opcode(o3_dummy); + Opcode(o3_updateSceneAnim); + Opcode(o3_dummy); + // 0x74 + Opcode(o3_runActorScript); + Opcode(o3_doDialog); + Opcode(o2_randomSceneChat); + Opcode(o2_setDlgIndex); + // 0x78 + Opcode(o2_getDlgIndex); + Opcode(o2_defineScene); + Opcode(o3_setConversationState); + OpcodeUnImpl(); + // 0x7C + OpcodeUnImpl(); + Opcode(o3_getConversationState); + Opcode(o3_dummy); + Opcode(o3_dummy); + // 0x80 + Opcode(o3_dummy); + Opcode(o3_changeChapter); + Opcode(o3_dummy); + Opcode(o3_dummy); + // 0x84 + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_dummy); + // 0x88 + Opcode(o3_countItemInstances); + Opcode(o3_dummy); + Opcode(o3_dialogStartScript); + Opcode(o3_dummy); + // 0x8C + Opcode(o3_dialogEndScript); + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o2_setSpecialSceneScriptState); + // 0x90 + Opcode(o2_clearSpecialSceneScriptState); + Opcode(o2_querySpecialSceneScriptState); + Opcode(o3_dummy); + Opcode(o2_setHiddenItemsEntry); + // 0x94 + Opcode(o2_getHiddenItemsEntry); + Opcode(o3_dummy); + Opcode(o3_dummy); + OpcodeUnImpl(); + // 0x98 + Opcode(o3_customChat); + Opcode(o3_customChatFinish); + Opcode(o3_setupSceneAnimObject); + Opcode(o3_removeSceneAnimObject); + // 0x9C + Opcode(o2_disableTimer); + Opcode(o2_enableTimer); + Opcode(o2_setTimerCountdown); + OpcodeUnImpl(); + // 0xA0 + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_dummy); + Opcode(o3_dummy); + // 0xA4 + OpcodeUnImpl(); + OpcodeUnImpl(); + OpcodeUnImpl(); + Opcode(o2_setVocHigh); + // 0xA8 + Opcode(o2_getVocHigh); + OpcodeUnImpl(); + OpcodeUnImpl(); + OpcodeUnImpl(); + // 0xAC + OpcodeUnImpl(); + Opcode(o3_dummy); + OpcodeUnImpl(); + Opcode(o3_dummy); + + _opcodesAnimation.reserve(8); + SetOpcodeTable(_opcodesAnimation); + // 0x00 + Opcode(o2a_setAnimationShapes); + Opcode(o3a_setCharacterFrame); + Opcode(o3a_playSoundEffect); + Opcode(o3_dummy); + // 0x04 + Opcode(o2a_setResetFrame); + Opcode(o1_getRand); + Opcode(o3_getMalcolmShapes); + Opcode(o3_dummy); + + _opcodesDialog.reserve(5); + SetOpcodeTable(_opcodesDialog); + // 0x00 + Opcode(o3d_updateAnim); + Opcode(o3d_delay); + Opcode(o1_getRand); + Opcode(o1_queryGameFlag); + // 0x04 + Opcode(o3_dummy); +} + +} // End of namespace Kyra diff --git a/engines/kyra/script/script_tim.cpp b/engines/kyra/script/script_tim.cpp new file mode 100644 index 0000000000..7afa35c339 --- /dev/null +++ b/engines/kyra/script/script_tim.cpp @@ -0,0 +1,1101 @@ +/* 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 "kyra/script/script_tim.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#ifdef ENABLE_LOL +#include "kyra/engine/lol.h" +#include "kyra/graphics/screen_lol.h" +#endif // ENABLE_LOL + +#include "common/iff_container.h" +#include "common/system.h" + +namespace Kyra { + +TIMInterpreter::TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system) : _vm(engine), _screen(screen_v2), _system(system), _currentTim(0) { +#define COMMAND(x) { &TIMInterpreter::x, #x } +#define COMMAND_UNIMPL() { 0, 0 } +#define cmd_return(n) cmd_return_##n + static const CommandEntry commandProcs[] = { + // 0x00 + COMMAND(cmd_initFunc0), + COMMAND(cmd_stopCurFunc), + COMMAND(cmd_initWSA), + COMMAND(cmd_uninitWSA), + // 0x04 + COMMAND(cmd_initFunc), + COMMAND(cmd_stopFunc), + COMMAND(cmd_wsaDisplayFrame), + COMMAND(cmd_displayText), + // 0x08 + COMMAND(cmd_loadVocFile), + COMMAND(cmd_unloadVocFile), + COMMAND(cmd_playVocFile), + COMMAND_UNIMPL(), + // 0x0C + COMMAND(cmd_loadSoundFile), + COMMAND(cmd_return(1)), + COMMAND(cmd_playMusicTrack), + COMMAND_UNIMPL(), + // 0x10 + COMMAND(cmd_return(1)), + COMMAND(cmd_return(1)), + COMMAND_UNIMPL(), + COMMAND_UNIMPL(), + // 0x14 + COMMAND(cmd_setLoopIp), + COMMAND(cmd_continueLoop), + COMMAND(cmd_resetLoopIp), + COMMAND(cmd_resetAllRuntimes), + // 0x18 + COMMAND(cmd_return(1)), + COMMAND(cmd_execOpcode), + COMMAND(cmd_initFuncNow), + COMMAND(cmd_stopFuncNow), + // 0x1C + COMMAND(cmd_return(1)), + COMMAND(cmd_return(1)), + COMMAND(cmd_return(n1)) + }; +#undef cmd_return +#undef COMMAND_UNIMPL +#undef COMMAND + + _commands = commandProcs; + _commandsSize = ARRAYSIZE(commandProcs); + + _langData = 0; + _textDisplayed = false; + _textAreaBuffer = new uint8[320*40]; + assert(_textAreaBuffer); + if ((_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().isDemo) && _vm->game() == GI_LOL) + _drawPage2 = 0; + else + _drawPage2 = 8; + + _animator = new TimAnimator(0, screen_v2, 0, false); + + _palDelayInc = _palDiff = _palDelayAcc = 0; + _abortFlag = 0; + _tim = 0; +} + +TIMInterpreter::~TIMInterpreter() { + delete[] _langData; + delete[] _textAreaBuffer; + delete _animator; +} + +bool TIMInterpreter::callback(Common::IFFChunk &chunk) { + switch (chunk._type) { + case MKTAG('T','E','X','T'): + _tim->text = new byte[chunk._size]; + assert(_tim->text); + if (chunk._stream->read(_tim->text, chunk._size) != chunk._size) + error("Couldn't read TEXT chunk from file '%s'", _filename); + break; + + case MKTAG('A','V','T','L'): + _avtlChunkSize = chunk._size >> 1; + _tim->avtl = new uint16[_avtlChunkSize]; + assert(_tim->avtl); + if (chunk._stream->read(_tim->avtl, chunk._size) != chunk._size) + error("Couldn't read AVTL chunk from file '%s'", _filename); + + for (int i = _avtlChunkSize - 1; i >= 0; --i) + _tim->avtl[i] = READ_LE_UINT16(&_tim->avtl[i]); + break; + + default: + warning("Unexpected chunk '%s' of size %d found in file '%s'", tag2str(chunk._type), chunk._size, _filename); + } + + return false; +} + +TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes) { + if (!_vm->resource()->exists(filename)) + return 0; + + Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename); + if (!stream) + error("Couldn't open TIM file '%s'", filename); + + _avtlChunkSize = 0; + _filename = filename; + + _tim = new TIM; + assert(_tim); + memset(_tim, 0, sizeof(TIM)); + + _tim->procFunc = -1; + _tim->opcodes = opcodes; + + IFFParser iff(*stream); + Common::Functor1Mem<Common::IFFChunk &, bool, TIMInterpreter> c(this, &TIMInterpreter::callback); + iff.parse(c); + + if (!_tim->avtl) + error("No AVTL chunk found in file: '%s'", filename); + + if (stream->err()) + error("Read error while parsing file '%s'", filename); + + delete stream; + + const int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs; + for (int i = 0; i < num; ++i) + _tim->func[i].avtl = _tim->avtl + _tim->avtl[i]; + + Common::strlcpy(_tim->filename, filename, 13); + + _tim->isLoLOutro = (_vm->game() == GI_LOL) && !scumm_stricmp(filename, "LOLFINAL.TIM"); + _tim->lolCharacter = 0; + + TIM *r = _tim; + _tim = 0; + return r; +} + +void TIMInterpreter::unload(TIM *&tim) const { + if (!tim) + return; + + delete[] tim->text; + delete[] tim->avtl; + delete tim; + tim = 0; +} + +void TIMInterpreter::setLangData(const char *filename) { + delete[] _langData; + _langData = _vm->resource()->fileData(filename, 0); +} + +int TIMInterpreter::exec(TIM *tim, bool loop) { + if (!tim) + return 0; + + _currentTim = tim; + if (!_currentTim->func[0].ip) { + _currentTim->func[0].ip = _currentTim->func[0].avtl; + _currentTim->func[0].nextTime = _currentTim->func[0].lastTime = _system->getMillis(); + } + + do { + update(); + + for (_currentFunc = 0; _currentFunc < TIM::kCountFuncs; ++_currentFunc) { + TIM::Function &cur = _currentTim->func[_currentFunc]; + + if (_currentTim->procFunc != -1) + execCommand(28, &_currentTim->procParam); + + update(); + checkSpeechProgress(); + + bool running = true; + int cnt = 0; + while (cur.ip && cur.nextTime <= _system->getMillis() && running) { + if (cnt++ > 0) { + if (_currentTim->procFunc != -1) + execCommand(28, &_currentTim->procParam); + update(); + } + + int8 opcode = int8(cur.ip[2] & 0xFF); + + switch (execCommand(opcode, cur.ip + 3)) { + case -1: + loop = false; + running = false; + _currentFunc = 11; + break; + + case -2: + running = false; + break; + + case -3: + _currentTim->procFunc = _currentFunc; + _currentTim->dlgFunc = -1; + break; + + case 22: + cur.loopIp = 0; + break; + + default: + break; + } + + if (cur.ip) { + cur.ip += cur.ip[0]; + cur.lastTime = cur.nextTime; + cur.nextTime += cur.ip[1] * _vm->tickLength(); + } + } + } + } while (loop && !_vm->shouldQuit()); + + return _currentTim->clickedButton; +} + +void TIMInterpreter::refreshTimersAfterPause(uint32 elapsedTime) { + if (!_currentTim) + return; + + for (int i = 0; i < TIM::kCountFuncs; i++) { + if (_currentTim->func[i].lastTime) + _currentTim->func[i].lastTime += elapsedTime; + if (_currentTim->func[i].nextTime) + _currentTim->func[i].nextTime += elapsedTime; + } +} + +void TIMInterpreter::displayText(uint16 textId, int16 flags) { + char *text = getTableEntry(textId); + + if (_textDisplayed) { + _screen->copyBlockToPage(0, 0, 160, 320, 40, _textAreaBuffer); + _textDisplayed = false; + } + + if (!text) + return; + if (!text[0]) + return; + + char filename[16]; + memset(filename, 0, sizeof(filename)); + + if (text[0] == '$') { + const char *end = strchr(text+1, '$'); + if (end) + memcpy(filename, text+1, end-1-text); + } + + const bool sjisMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode); + if (filename[0] && (_vm->speechEnabled() || !_vm->gameFlags().isTalkie)) + _vm->sound()->voicePlay(filename, 0, 255, 255, !_vm->gameFlags().isTalkie); + + if (text[0] == '$') + text = strchr(text + 1, '$') + 1; + + if (!_vm->gameFlags().use16ColorMode) + setupTextPalette((flags < 0) ? 1 : flags, 0); + + if (flags < 0) { + static const uint8 colorMap[] = { 0x00, 0xF0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); + _screen->setTextColorMap(colorMap); + _screen->_charWidth = -2; + } + + _screen->_charOffset = -4; + _screen->copyRegionToBuffer(0, 0, 160, 320, 40, _textAreaBuffer); + _textDisplayed = true; + + char backupChar = 0; + char *str = text; + int heightAdd = 0; + + while (str[0] && _vm->textEnabled()) { + char *nextLine = strchr(str, '\r'); + + backupChar = 0; + if (nextLine) { + backupChar = nextLine[0]; + nextLine[0] = '\0'; + } + + int width = _screen->getTextWidth(str); + + if (flags >= 0) { + if (_vm->gameFlags().use16ColorMode) { + static const uint8 colorMap[] = { 0xE1, 0xE1, 0xC1, 0xA1, 0x81, 0x61 }; + _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, colorMap[flags], 0x00); + } else { + _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, 0xF0, 0x00); + } + } else { + _screen->printText(str, (320 - width) >> 1, 188, 0xF0, 0x00); + } + + heightAdd += _screen->getFontHeight(); + str += strlen(str); + + if (backupChar) { + nextLine[0] = backupChar; + ++str; + } + } + + _screen->_charOffset = 0; + + if (flags < 0) { + static const uint8 colorMap[] = { 0x00, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00 }; + + _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT); + _screen->setTextColorMap(colorMap); + _screen->_charWidth = 0; + } +} + +void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) { + if (!_vm->textEnabled() && !(textId & 0x8000)) + return; + + char *text = getTableEntry(textId & 0x7FFF); + + if (flags > 0) + _screen->copyBlockToPage(0, 0, 0, 320, 40, _textAreaBuffer); + + if (flags == 255) + return; + + _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT); + + static const uint8 colorMap[] = { 0x00, 0xA0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + _screen->setTextColorMap(colorMap); + _screen->_charWidth = 0; + if (!_vm->gameFlags().use16ColorMode) + _screen->_charOffset = -4; + + if (!flags) + _screen->copyRegionToBuffer(0, 0, 0, 320, 40, _textAreaBuffer); + + char backupChar = 0; + char *str = text; + int y = 0; + + if (_vm->gameFlags().use16ColorMode) { + if (color == 0xDA) + color = 0xA1; + else if (color == 0xF2) + color = 0xE1; + else if (flags < 0) + color = 0xE1; + else + color = 0xC1; + } + + while (str[0]) { + char *nextLine = strchr(str, '\r'); + + backupChar = 0; + if (nextLine) { + backupChar = nextLine[0]; + nextLine[0] = '\0'; + } + + int width = _screen->getTextWidth(str); + + if (flags >= 0) + _screen->printText(str, (320 - width) >> 1, y, color, 0x00); + else + _screen->printText(str, 0, y, color, 0x00); + + y += (_vm->gameFlags().use16ColorMode ? 16 : (_screen->getFontHeight() - 4)); + str += strlen(str); + + if (backupChar) { + nextLine[0] = backupChar; + ++str; + } + } +} + +void TIMInterpreter::setupTextPalette(uint index, int fadePalette) { + static const uint16 palTable[] = { + 0x00, 0x00, 0x00, + 0x64, 0x64, 0x64, + 0x61, 0x51, 0x30, + 0x29, 0x48, 0x64, + 0x00, 0x4B, 0x3B, + 0x64, 0x1E, 0x1E, + }; + + for (int i = 0; i < 15; ++i) { + uint8 *palette = _screen->getPalette(0).getData() + (240 + i) * 3; + + uint8 c1 = (((15 - i) << 2) * palTable[index*3+0]) / 100; + uint8 c2 = (((15 - i) << 2) * palTable[index*3+1]) / 100; + uint8 c3 = (((15 - i) << 2) * palTable[index*3+2]) / 100; + + palette[0] = c1; + palette[1] = c2; + palette[2] = c3; + } + + if (!fadePalette && !_palDiff) { + _screen->setScreenPalette(_screen->getPalette(0)); + } else { + _screen->getFadeParams(_screen->getPalette(0), fadePalette, _palDelayInc, _palDiff); + _palDelayAcc = 0; + } +} + +int TIMInterpreter::initAnimStruct(int index, const char *filename, int x, int y, int, int offscreenBuffer, uint16 wsaFlags) { + Movie *wsa = 0; + + const bool isLoLDemo = _vm->gameFlags().isDemo && _vm->game() == GI_LOL; + + if (isLoLDemo || _vm->gameFlags().platform == Common::kPlatformPC98 || _currentTim->isLoLOutro) + _drawPage2 = 0; + else + _drawPage2 = 8; + + uint16 wsaOpenFlags = 0; + if (isLoLDemo) { + if (!(wsaFlags & 0x10)) + wsaOpenFlags |= 1; + } else { + if (wsaFlags & 0x10) + wsaOpenFlags |= 2; + wsaOpenFlags |= 1; + + if (offscreenBuffer == 2) + wsaOpenFlags = 1; + } + + Common::String file = Common::String::format("%s.WSA", filename); + + if (_vm->resource()->exists(file.c_str())) { + if (isLoLDemo) + wsa = new WSAMovie_v1(_vm); + else + wsa = new WSAMovie_v2(_vm); + assert(wsa); + + wsa->open(file.c_str(), wsaOpenFlags, (index == 1) ? &_screen->getPalette(0) : 0); + } + + if (wsa && wsa->opened()) { + if (isLoLDemo) { + if (x == -1) { + int16 t = int8(320 - wsa->width()); + uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8); + v >>= 1; + x = v; + } + + if (y == -1) { + int16 t = int8(200 - wsa->height()); + uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8); + v >>= 1; + y = v; + } + } else { + if (x == -1) + x = 0; + if (y == -1) + y = 0; + } + + if (wsaFlags & 2) { + _screen->fadePalette(_screen->getPalette(1), 15, 0); + _screen->clearPage(_drawPage2); + if (_drawPage2) + _screen->checkedPageUpdate(8, 4); + _screen->updateScreen(); + } + + if (wsaFlags & 4) { + file = Common::String::format("%s.CPS", filename); + + if (_vm->resource()->exists(file.c_str())) { + _screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK); + if (_drawPage2) + _screen->checkedPageUpdate(8, 4); + _screen->updateScreen(); + } + + wsa->displayFrame(0, 0, x, y, 0, 0, 0); + } + + if (wsaFlags & 2) + _screen->fadePalette(_screen->getPalette(0), 30, 0); + } else { + if (wsaFlags & 2) { + _screen->fadePalette(_screen->getPalette(1), 15, 0); + _screen->clearPage(_drawPage2); + if (_drawPage2) + _screen->checkedPageUpdate(8, 4); + _screen->updateScreen(); + } + + file = Common::String::format("%s.CPS", filename); + + if (_vm->resource()->exists(file.c_str())) { + _screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK); + if (_drawPage2) + _screen->checkedPageUpdate(8, 4); + _screen->updateScreen(); + } + + if (wsaFlags & 2) + _screen->fadePalette(_screen->getPalette(0), 30, 0); + } + + _animator->init(index, wsa, x, y, wsaFlags, 0); + + return index + 1; +} + +int TIMInterpreter::freeAnimStruct(int index) { + _animator->reset(index, true); + return 1; +} + +char *TIMInterpreter::getTableEntry(uint idx) { + if (!_langData) + return 0; + else + return (char *)(_langData + READ_LE_UINT16(_langData + (idx<<1))); +} + +const char *TIMInterpreter::getCTableEntry(uint idx) const { + if (!_langData) + return 0; + else + return (const char *)(_langData + READ_LE_UINT16(_langData + (idx<<1))); +} + +int TIMInterpreter::execCommand(int cmd, const uint16 *param) { + if (cmd < 0 || cmd >= _commandsSize) { + warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename); + return 0; + } + + if (_commands[cmd].proc == 0) { + warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename); + return 0; + } + + debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param); + return (this->*_commands[cmd].proc)(param); +} + +int TIMInterpreter::cmd_initFunc0(const uint16 *param) { + for (int i = 0; i < TIM::kWSASlots; ++i) + memset(&_currentTim->wsa[i], 0, sizeof(TIM::WSASlot)); + + _currentTim->func[0].ip = _currentTim->func[0].avtl; + _currentTim->func[0].lastTime = _system->getMillis(); + return 1; +} + +int TIMInterpreter::cmd_stopCurFunc(const uint16 *param) { + if (_currentFunc < TIM::kCountFuncs) + _currentTim->func[_currentFunc].ip = 0; + if (!_currentFunc) + _finished = true; + return -2; +} + +void TIMInterpreter::stopAllFuncs(TIM *tim) { + for (int i = 0; i < TIM::kCountFuncs; ++i) + tim->func[i].ip = 0; +} + +int TIMInterpreter::cmd_initWSA(const uint16 *param) { + const int index = param[0]; + + TIM::WSASlot &slot = _currentTim->wsa[index]; + + slot.x = int16(param[2]); + slot.y = int16(param[3]); + slot.offscreen = param[4]; + slot.wsaFlags = param[5]; + const char *filename = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[1]<<1))); + + slot.anim = initAnimStruct(index, filename, slot.x, slot.y, 10, slot.offscreen, slot.wsaFlags); + return 1; +} + +int TIMInterpreter::cmd_uninitWSA(const uint16 *param) { + const int index = param[0]; + + TIM::WSASlot &slot = _currentTim->wsa[index]; + + if (!slot.anim) + return 0; + + if (slot.offscreen) { + _animator->reset(index, false); + slot.anim = 0; + } else { + //XXX + _animator->reset(index, true); + memset(&slot, 0, sizeof(TIM::WSASlot)); + } + + return 1; +} + +int TIMInterpreter::cmd_initFunc(const uint16 *param) { + uint16 func = *param; + assert(func < TIM::kCountFuncs); + if (_currentTim->func[func].avtl) + _currentTim->func[func].ip = _currentTim->func[func].avtl; + else + _currentTim->func[func].avtl = _currentTim->func[func].ip = _currentTim->avtl + _currentTim->avtl[func]; + return 1; +} + +int TIMInterpreter::cmd_stopFunc(const uint16 *param) { + uint16 func = *param; + assert(func < TIM::kCountFuncs); + _currentTim->func[func].ip = 0; + return 1; +} + +int TIMInterpreter::cmd_wsaDisplayFrame(const uint16 *param) { + _animator->displayFrame(param[0], _drawPage2, param[1]); + return 1; +} + +int TIMInterpreter::cmd_displayText(const uint16 *param) { + if (_currentTim->isLoLOutro) + displayText(param[0], param[1], 0xF2); + else + displayText(param[0], param[1]); + return 1; +} + +int TIMInterpreter::cmd_loadVocFile(const uint16 *param) { + const int stringId = param[0]; + const int index = param[1]; + + _vocFiles[index] = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (stringId << 1))); + + if (index == 2 && _currentTim->isLoLOutro && _vm->gameFlags().isTalkie) { + _vocFiles[index] = "CONGRATA.VOC"; + + switch (_currentTim->lolCharacter) { + case 0: + _vocFiles[index].setChar('K', 7); + break; + + case 1: + _vocFiles[index].setChar('A', 7); + break; + + case 2: + _vocFiles[index].setChar('M', 7); + break; + + case 3: + _vocFiles[index].setChar('C', 7); + break; + + default: + break; + } + } + + for (int i = 0; i < 4; ++i) + _vocFiles[index].deleteLastChar(); + return 1; +} + +int TIMInterpreter::cmd_unloadVocFile(const uint16 *param) { + const int index = param[0]; + _vocFiles[index].clear(); + return 1; +} + +int TIMInterpreter::cmd_playVocFile(const uint16 *param) { + const int index = param[0]; + const int volume = (param[1] * 255) / 100; + + if (index < ARRAYSIZE(_vocFiles) && !_vocFiles[index].empty()) + _vm->sound()->voicePlay(_vocFiles[index].c_str(), 0, volume, 255, true); + else if (index == 7 && !_vm->gameFlags().isTalkie) + _vm->sound()->playTrack(index); + else + _vm->sound()->playSoundEffect(index); + + return 1; +} + +int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) { + const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1))); + + _vm->sound()->loadSoundFile(file); + if (_vm->game() == GI_LOL) + _vm->sound()->loadSfxFile(file); + + return 1; +} + +int TIMInterpreter::cmd_playMusicTrack(const uint16 *param) { + _vm->sound()->playTrack(param[0]); + return 1; +} + +int TIMInterpreter::cmd_setLoopIp(const uint16 *param) { + _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip; + return 1; +} + +int TIMInterpreter::cmd_continueLoop(const uint16 *param) { + TIM::Function &func = _currentTim->func[_currentFunc]; + + if (!func.loopIp) + return -2; + + func.ip = func.loopIp; + + uint16 factor = param[0]; + if (factor) { + const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000); + uint32 waitTime = (random * factor) / 0x8000; + func.nextTime += waitTime * _vm->tickLength(); + } + + return -2; +} + +int TIMInterpreter::cmd_resetLoopIp(const uint16 *param) { + _currentTim->func[_currentFunc].loopIp = 0; + return 1; +} + +int TIMInterpreter::cmd_resetAllRuntimes(const uint16 *param) { + for (int i = 0; i < TIM::kCountFuncs; ++i) { + if (_currentTim->func[i].ip) + _currentTim->func[i].nextTime = _system->getMillis(); + } + return 1; +} + +int TIMInterpreter::cmd_execOpcode(const uint16 *param) { + const uint16 opcode = *param++; + + if (!_currentTim->opcodes) { + warning("Trying to execute TIM opcode %d without opcode list (file '%s')", opcode, _currentTim->filename); + return 0; + } + + if (opcode > _currentTim->opcodes->size()) { + warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename); + return 0; + } + + if (!(*_currentTim->opcodes)[opcode]->isValid()) { + warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename); + return 0; + } + + return (*(*_currentTim->opcodes)[opcode])(_currentTim, param); +} + +int TIMInterpreter::cmd_initFuncNow(const uint16 *param) { + uint16 func = *param; + assert(func < TIM::kCountFuncs); + _currentTim->func[func].ip = _currentTim->func[func].avtl; + _currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis(); + return 1; +} + +int TIMInterpreter::cmd_stopFuncNow(const uint16 *param) { + uint16 func = *param; + assert(func < TIM::kCountFuncs); + _currentTim->func[func].ip = 0; + _currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis(); + return 1; +} + +// TODO: Consider moving to another file + +#ifdef ENABLE_LOL +// LOL version of the TIM interpreter + +TIMInterpreter_LoL::TIMInterpreter_LoL(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system) : + TIMInterpreter(engine, screen_v2, system), _vm(engine) { + #define COMMAND(x) { &TIMInterpreter_LoL::x, #x } + #define COMMAND_UNIMPL() { 0, 0 } + #define cmd_return(n) cmd_return_##n + static const CommandEntry commandProcs[] = { + // 0x00 + COMMAND(cmd_initFunc0), + COMMAND(cmd_stopAllFuncs), + COMMAND(cmd_initWSA), + COMMAND(cmd_uninitWSA), + // 0x04 + COMMAND(cmd_initFunc), + COMMAND(cmd_stopFunc), + COMMAND(cmd_wsaDisplayFrame), + COMMAND_UNIMPL(), + // 0x08 + COMMAND(cmd_loadVocFile), + COMMAND(cmd_unloadVocFile), + COMMAND(cmd_playVocFile), + COMMAND_UNIMPL(), + // 0x0C + COMMAND(cmd_loadSoundFile), + COMMAND(cmd_return(1)), + COMMAND(cmd_playMusicTrack), + COMMAND_UNIMPL(), + // 0x10 + COMMAND(cmd_return(1)), + COMMAND(cmd_return(1)), + COMMAND_UNIMPL(), + COMMAND_UNIMPL(), + // 0x14 + COMMAND(cmd_setLoopIp), + COMMAND(cmd_continueLoop), + COMMAND(cmd_resetLoopIp), + COMMAND(cmd_resetAllRuntimes), + // 0x18 + COMMAND(cmd_return(1)), + COMMAND(cmd_execOpcode), + COMMAND(cmd_initFuncNow), + COMMAND(cmd_stopFuncNow), + // 0x1C + COMMAND(cmd_processDialogue), + COMMAND(cmd_dialogueBox), + COMMAND(cmd_return(n1)) + }; + #undef cmd_return + #undef COMMAND_UNIMPL + #undef COMMAND + + _commands = commandProcs; + _commandsSize = ARRAYSIZE(commandProcs); + + _screen = engine->_screen; + + delete _animator; + _animator = new TimAnimator(engine, screen_v2, system, true); + + _drawPage2 = 0; +} + +int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaFlags) { + Movie *wsa = 0; + uint16 wsaOpenFlags = 0; + if (wsaFlags & 0x10) + wsaOpenFlags |= 2; + if (wsaFlags & 8) + wsaOpenFlags |= 1; + + Common::String file = Common::String::format("%s.WSA", filename); + + if (_vm->resource()->exists(file.c_str())) { + wsa = new WSAMovie_v2(_vm); + assert(wsa); + wsa->open(file.c_str(), wsaOpenFlags, &_screen->getPalette(3)); + } + + if (!_vm->_flags.use16ColorMode) { + if (wsaFlags & 1) { + if (_screen->_fadeFlag != 1) + _screen->fadeClearSceneWindow(10); + _screen->getPalette(3).copy(_screen->getPalette(0), 128, 128); + } else if (wsaFlags & 2) { + _screen->fadeToBlack(10); + } + } + + if (wsa && (wsaFlags & 7)) + wsa->displayFrame(0, 0, x, y, 0, 0, 0); + + if (wsaFlags & 3) { + if (_vm->_flags.use16ColorMode) { + _vm->setPaletteBrightness(_screen->getPalette(0), _vm->_brightness, _vm->_lampEffect); + } else { + _screen->loadSpecialColors(_screen->getPalette(3)); + _screen->fadePalette(_screen->getPalette(3), 10); + } + _screen->_fadeFlag = 0; + } + + _animator->init(index, wsa, x, y, wsaFlags, frameDelay); + + return index + 1; +} + +int TIMInterpreter_LoL::freeAnimStruct(int index) { + _animator->reset(index, true); + return 1; +} + +void TIMInterpreter_LoL::advanceToOpcode(int opcode) { + TIM::Function *f = &_currentTim->func[_currentTim->dlgFunc]; + uint16 len = f->ip[0]; + + while ((f->ip[2] & 0xFF) != opcode) { + if ((f->ip[2] & 0xFF) == 1) { + f->ip[0] = len; + break; + } + len = f->ip[0]; + f->ip += len; + } + + f->nextTime = _system->getMillis(); +} + +void TIMInterpreter_LoL::resetDialogueState(TIM *tim) { + if (!tim) + return; + + tim->procFunc = 0; + tim->procParam = _vm->_dialogueNumButtons ? _vm->_dialogueNumButtons : 1; + tim->clickedButton = 0; + tim->dlgFunc = -1; +} + +void TIMInterpreter_LoL::update() { + _vm->update(); +} + +void TIMInterpreter_LoL::checkSpeechProgress() { + if (_vm->speechEnabled() && _currentTim->procParam > 1 && _currentTim->func[_currentFunc].loopIp) { + if (_vm->snd_updateCharacterSpeech() != 2) { + _currentTim->func[_currentFunc].loopIp = 0; + _currentTim->dlgFunc = _currentFunc; + advanceToOpcode(21); + _currentTim->dlgFunc = -1; + _animator->reset(5, false); + } + } +} + +char *TIMInterpreter_LoL::getTableString(int id) { + return _vm->getLangString(id); +} + +int TIMInterpreter_LoL::execCommand(int cmd, const uint16 *param) { + if (cmd < 0 || cmd >= _commandsSize) { + warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename); + return 0; + } + + if (_commands[cmd].proc == 0) { + warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename); + return 0; + } + + debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param); + return (this->*_commands[cmd].proc)(param); +} + +int TIMInterpreter_LoL::cmd_stopAllFuncs(const uint16 *param) { + while (_currentTim->dlgFunc == -1 && _currentTim->clickedButton == 0 && !_vm->shouldQuit()) { + update(); + _currentTim->clickedButton = _vm->processDialogue(); + } + + for (int i = 0; i < TIM::kCountFuncs; ++i) + _currentTim->func[i].ip = 0; + + return -1; +} + +int TIMInterpreter_LoL::cmd_setLoopIp(const uint16 *param) { + if (_vm->speechEnabled()) { + if (_vm->snd_updateCharacterSpeech() == 2) + _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip; + else + advanceToOpcode(21); + } else { + _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip; + } + return 1; +} + +int TIMInterpreter_LoL::cmd_continueLoop(const uint16 *param) { + TIM::Function &func = _currentTim->func[_currentFunc]; + + if (!func.loopIp) + return -2; + + func.ip = func.loopIp; + + if (_vm->snd_updateCharacterSpeech() != 2) { + uint16 factor = param[0]; + if (factor) { + const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000); + uint32 waitTime = (random * factor) / 0x8000; + func.nextTime += waitTime * _vm->tickLength(); + } + } + + return -2; +} + +int TIMInterpreter_LoL::cmd_processDialogue(const uint16 *param) { + int res = _vm->processDialogue(); + if (!res || !_currentTim->procParam) + return res; + + _vm->snd_stopSpeech(false); + + _currentTim->func[_currentTim->procFunc].loopIp = 0; + _currentTim->dlgFunc = _currentTim->procFunc; + _currentTim->procFunc = -1; + _currentTim->clickedButton = res; + + _animator->reset(5, false); + + if (_currentTim->procParam) + advanceToOpcode(21); + + return res; +} + +int TIMInterpreter_LoL::cmd_dialogueBox(const uint16 *param) { + uint16 func = *param; + assert(func < TIM::kCountFuncs); + _currentTim->procParam = func; + _currentTim->clickedButton = 0; + + const char *tmpStr[3]; + int cnt = 0; + + for (int i = 1; i < 4; i++) { + if (param[i] != 0xFFFF) { + tmpStr[i-1] = getTableString(param[i]); + cnt++; + } else { + tmpStr[i-1] = 0; + } + } + + _vm->setupDialogueButtons(cnt, tmpStr[0], tmpStr[1], tmpStr[2]); + _vm->gui_notifyButtonListChanged(); + + return -3; +} +#endif // ENABLE_LOL + +} // End of namespace Kyra diff --git a/engines/kyra/script/script_tim.h b/engines/kyra/script/script_tim.h new file mode 100644 index 0000000000..bd9c2645e5 --- /dev/null +++ b/engines/kyra/script/script_tim.h @@ -0,0 +1,306 @@ +/* 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. + * + */ + +#ifndef KYRA_SCRIPT_TIM_H +#define KYRA_SCRIPT_TIM_H + +#include "kyra/kyra_v1.h" + +#include "common/array.h" +#include "common/func.h" +#include "common/str.h" + +namespace Kyra { + +class WSAMovie_v2; +class Screen_v2; +class Movie; +class LoLEngine; + +class TimAnimator { +public: + struct AnimPart { + uint16 firstFrame; + uint16 lastFrame; + uint16 cycles; + int16 nextPart; + int16 partDelay; + uint16 field_A; + int16 sfxIndex; + uint16 sfxFrame; + }; + + struct Animation { + Movie *wsa; + int16 x, y; + uint32 nextFrame; + uint8 enable; + uint8 field_D; + uint8 frameDelay; + int8 curPart; + uint8 curFrame; + uint8 cyclesCompleted; + uint16 wsaCopyParams; + int8 lastPart; + AnimPart *parts; + }; + +#ifdef ENABLE_LOL + TimAnimator(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system, bool useParts); +#else + TimAnimator(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system, bool useParts); +#endif + ~TimAnimator(); + + void init(int animIndex, Movie *wsa, int x, int y, int wsaCopyParams, int frameDelay); + void reset(int animIndex, bool clearStruct); + + void displayFrame(int animIndex, int page, int frame, int flags = -1); + + const Movie *getWsaCPtr(int animIndex) { return (animIndex >= 0 && animIndex < 6) ? _animations[animIndex].wsa : 0; } + int getAnimX(int animIndex) { return (animIndex >= 0 && animIndex < 6) ? _animations[animIndex].x : 0; } + int getAnimY(int animIndex) { return (animIndex >= 0 && animIndex < 6) ? _animations[animIndex].y : 0; } + +#ifdef ENABLE_LOL + void setupPart(int animIndex, int part, int firstFrame, int lastFrame, int cycles, int nextPart, int partDelay, int f, int sfxIndex, int sfxFrame); + void start(int animIndex, int part); + void stop(int animIndex); + void update(int animIndex); + void playPart(int animIndex, int firstFrame, int lastFrame, int delay); + int resetLastPart(int animIndex); +#endif + +private: +#ifdef ENABLE_LOL + LoLEngine *_vm; +#else + KyraEngine_v1 *_vm; +#endif + Screen_v2 *_screen; + OSystem *_system; + + Animation *_animations; + + const bool _useParts; +}; + +struct TIM; +typedef Common::Functor2<const TIM *, const uint16 *, int> TIMOpcode; + +struct TIM { + char filename[13]; + + uint16 clickedButton; + int16 dlgFunc; + + int16 procFunc; + uint16 procParam; + + enum { + kCountFuncs = 10 + }; + + struct Function { + uint16 *ip; + + uint32 lastTime; + uint32 nextTime; + + uint16 *loopIp; + + uint16 *avtl; + } func[kCountFuncs]; + + enum { + kWSASlots = 6, + kAnimParts = 10 + }; + + struct WSASlot { + int anim; + + int16 x, y; + uint16 wsaFlags; + uint16 offscreen; + } wsa[kWSASlots]; + + uint16 *avtl; + uint8 *text; + + const Common::Array<const TIMOpcode *> *opcodes; + + // TODO: Get rid of this ugly HACK to allow the + // Lands of Lore outro to be working properly. + bool isLoLOutro; + uint8 lolCharacter; +}; + +class TIMInterpreter { +public: + TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system); + virtual ~TIMInterpreter(); + + TIM *load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes); + void unload(TIM *&tim) const; + + bool callback(Common::IFFChunk &chunk); + + virtual int initAnimStruct(int index, const char *filename, int x, int y, int, int offscreenBuffer, uint16 wsaFlags); + virtual int freeAnimStruct(int index); + TimAnimator *animator() { return _animator; } + + void setLangData(const char *filename); + void clearLangData() { delete[] _langData; _langData = 0; } + + const char *getCTableEntry(uint idx) const; + + void resetFinishedFlag() { _finished = false; } + bool finished() const { return _finished; } + + int exec(TIM *tim, bool loop); + void stopCurFunc() { if (_currentTim) cmd_stopCurFunc(0); } + void stopAllFuncs(TIM *tim); + + void refreshTimersAfterPause(uint32 elapsedTime); + + void displayText(uint16 textId, int16 flags); + void displayText(uint16 textId, int16 flags, uint8 color); + void setupTextPalette(uint index, int fadePalette); + + virtual void resetDialogueState(TIM *tim) {} + + int _drawPage2; + + int _palDelayInc, _palDiff, _palDelayAcc; + int _abortFlag; + +protected: + KyraEngine_v1 *_vm; + Screen_v2 *_screen; + OSystem *_system; + + TIM *_currentTim; + int _currentFunc; + + TimAnimator *_animator; + + bool _finished; + + // used when loading + int _avtlChunkSize; + const char *_filename; + TIM *_tim; + + Common::String _vocFiles[120]; + + virtual void update() {} + virtual void checkSpeechProgress() {} + + char _audioFilename[32]; + + uint8 *_langData; + char *getTableEntry(uint idx); + bool _textDisplayed; + uint8 *_textAreaBuffer; + + virtual int execCommand(int cmd, const uint16 *param); + + typedef int (TIMInterpreter::*CommandProc)(const uint16 *); + struct CommandEntry { + CommandProc proc; + const char *desc; + }; + + const CommandEntry *_commands; + int _commandsSize; + + int cmd_initFunc0(const uint16 *param); + int cmd_stopCurFunc(const uint16 *param); + int cmd_initWSA(const uint16 *param); + int cmd_uninitWSA(const uint16 *param); + int cmd_initFunc(const uint16 *param); + int cmd_stopFunc(const uint16 *param); + int cmd_wsaDisplayFrame(const uint16 *param); + int cmd_displayText(const uint16 *param); + int cmd_loadVocFile(const uint16 *param); + int cmd_unloadVocFile(const uint16 *param); + int cmd_playVocFile(const uint16 *param); + int cmd_loadSoundFile(const uint16 *param); + int cmd_playMusicTrack(const uint16 *param); + virtual int cmd_setLoopIp(const uint16 *param); + virtual int cmd_continueLoop(const uint16 *param); + int cmd_resetLoopIp(const uint16 *param); + int cmd_resetAllRuntimes(const uint16 *param); + int cmd_execOpcode(const uint16 *param); + int cmd_initFuncNow(const uint16 *param); + int cmd_stopFuncNow(const uint16 *param); +#define cmd_return(n, v) \ + int cmd_return_##n(const uint16 *){ return v; } + cmd_return( 1, 1) + cmd_return(n1, -1) +#undef cmd_return +}; + +#ifdef ENABLE_LOL +class LoLEngine; +class Screen_LoL; +class TIMInterpreter_LoL : public TIMInterpreter { +public: + TIMInterpreter_LoL(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system); + + int initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaCopyParams); + int freeAnimStruct(int index); + + void resetDialogueState(TIM *tim); + +private: + void update(); + void checkSpeechProgress(); + + char *getTableString(int id); + void advanceToOpcode(int opcode); + + LoLEngine *_vm; + Screen_LoL *_screen; + + virtual int execCommand(int cmd, const uint16 *param); + + typedef int (TIMInterpreter_LoL::*CommandProc)(const uint16 *); + struct CommandEntry { + CommandProc proc; + const char *desc; + }; + + const CommandEntry *_commands; + int _commandsSize; + + int cmd_stopAllFuncs(const uint16 *param); + int cmd_setLoopIp(const uint16 *param); + int cmd_continueLoop(const uint16 *param); + int cmd_processDialogue(const uint16 *param); + int cmd_dialogueBox(const uint16 *param); +}; +#endif // ENABLE_LOL + +} // End of namespace Kyra + +#endif diff --git a/engines/kyra/script/script_v1.cpp b/engines/kyra/script/script_v1.cpp new file mode 100644 index 0000000000..2fbd2f22f4 --- /dev/null +++ b/engines/kyra/script/script_v1.cpp @@ -0,0 +1,125 @@ +/* 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 "kyra/kyra_v1.h" +#include "kyra/graphics/screen.h" + +#include "common/system.h" + +namespace Kyra { + +int KyraEngine_v1::o1_queryGameFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_queryGameFlag(%p) (0x%X)", (const void *)script, stackPos(0)); + return queryGameFlag(stackPos(0)); +} + +int KyraEngine_v1::o1_setGameFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_setGameFlag(%p) (0x%X)", (const void *)script, stackPos(0)); + return setGameFlag(stackPos(0)); +} + +int KyraEngine_v1::o1_resetGameFlag(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_resetGameFlag(%p) (0x%X)", (const void *)script, stackPos(0)); + return resetGameFlag(stackPos(0)); +} + +int KyraEngine_v1::o1_getRand(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_getRand(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + int min = stackPos(0); + int max = stackPos(1); + if (max < min) + SWAP(min, max); + return _rnd.getRandomNumberRng(min, max); +} + +int KyraEngine_v1::o1_hideMouse(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_hideMouse(%p) ()", (const void *)script); + screen()->hideMouse(); + return 0; +} + +int KyraEngine_v1::o1_showMouse(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_showMouse(%p) ()", (const void *)script); + screen()->showMouse(); + return 0; +} + +int KyraEngine_v1::o1_setMousePos(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_setMousePos(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _system->warpMouse(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_v1::o1_setHandItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_setHandItem(%p) (%d)", (const void *)script, stackPos(0)); + setHandItem(stackPos(0)); + return 0; +} + +int KyraEngine_v1::o1_removeHandItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_removeHandItem(%p) ()", (const void *)script); + removeHandItem(); + return 0; +} + +int KyraEngine_v1::o1_getMouseState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_getMouseState(%p) ()", (const void *)script); + return _mouseState; +} + +int KyraEngine_v1::o1_setDeathHandler(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_setDeathHandler(%p) (%d)", (const void *)script, stackPos(0)); + _deathHandler = stackPos(0); + return 0; +} + +int KyraEngine_v1::o1_playWanderScoreViaMap(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_playWanderScoreViaMap(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + snd_playWanderScoreViaMap(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_v1::o1_fillRect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_fillRect(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + screen()->fillRect(stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(0)); + return 0; +} + +int KyraEngine_v1::o1_blockInWalkableRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_blockInWalkableRegion(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + screen()->blockInRegion(stackPos(0), stackPos(1), stackPos(2) - stackPos(0) + 1, stackPos(3) - stackPos(1) + 1); + return 0; +} + +int KyraEngine_v1::o1_blockOutWalkableRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_blockOutWalkableRegion(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + screen()->blockOutRegion(stackPos(0), stackPos(1), stackPos(2) - stackPos(0) + 1, stackPos(3) - stackPos(1) + 1); + return 0; +} + +int KyraEngine_v1::o1_playSoundEffect(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_playSoundEffect(%p) (%d)", (const void *)script, stackPos(0)); + snd_playSoundEffect(stackPos(0)); + return 0; +} + +} // End of namespace Kyra diff --git a/engines/kyra/script/script_v2.cpp b/engines/kyra/script/script_v2.cpp new file mode 100644 index 0000000000..c8168a3353 --- /dev/null +++ b/engines/kyra/script/script_v2.cpp @@ -0,0 +1,342 @@ +/* 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 "kyra/engine/kyra_v2.h" +#include "kyra/graphics/screen_v2.h" +#include "kyra/engine/timer.h" + +#include "common/system.h" + +namespace Kyra { + +int KyraEngine_v2::o2_getCharacterX(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getCharacterX(%p) ()", (const void *)script); + return _mainCharacter.x1; +} + +int KyraEngine_v2::o2_getCharacterY(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getCharacterY(%p) ()", (const void *)script); + return _mainCharacter.y1; +} + +int KyraEngine_v2::o2_getCharacterFacing(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getCharacterFacing(%p) ()", (const void *)script); + return _mainCharacter.facing; +} + +int KyraEngine_v2::o2_getCharacterScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getCharacterScene(%p) ()", (const void *)script); + return _mainCharacter.sceneId; +} + +int KyraEngine_v2::o2_setCharacterFacingOverwrite(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setCharacterFacingOverwrite(%p) (%d)", (const void *)script, stackPos(0)); + _mainCharacter.facing = stackPos(0); + _overwriteSceneFacing = true; + return 0; +} + +int KyraEngine_v2::o2_trySceneChange(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_trySceneChange(%p) (%d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + + _unkHandleSceneChangeFlag = 1; + int success = inputSceneChange(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + _unkHandleSceneChangeFlag = 0; + + if (success) { + _emc->init(script, script->dataPtr); + _unk4 = 0; + _savedMouseState = -1; + _unk5 = 1; + return 0; + } else { + return (_unk4 != 0) ? 1 : 0; + } +} + +int KyraEngine_v2::o2_moveCharacter(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_moveCharacter(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + moveCharacter(stackPos(0), stackPos(1), stackPos(2)); + return 0; +} + +int KyraEngine_v2::o2_checkForItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_checkForItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return findItem(stackPos(0), stackPos(1)) == -1 ? 0 : 1; +} + +int KyraEngine_v2::o2_defineItem(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_defineItem(%p) (%d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + int freeItem = findFreeItem(); + + if (freeItem >= 0) { + _itemList[freeItem].id = stackPos(0); + _itemList[freeItem].x = stackPos(1); + _itemList[freeItem].y = stackPos(2); + _itemList[freeItem].sceneId = stackPos(3); + } + + return freeItem; +} + +int KyraEngine_v2::o2_addSpecialExit(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_addSpecialExit(%p) (%d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + if (_specialExitCount < 5) { + _specialExitTable[_specialExitCount + 0] = stackPos(0); + _specialExitTable[_specialExitCount + 5] = stackPos(1); + _specialExitTable[_specialExitCount + 10] = stackPos(2) + stackPos(0) - 1; + _specialExitTable[_specialExitCount + 15] = stackPos(3) + stackPos(1) - 1; + _specialExitTable[_specialExitCount + 20] = stackPos(4); + ++_specialExitCount; + } + return 0; +} + +int KyraEngine_v2::o2_delay(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_delay(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + if (stackPos(1)) { + uint32 maxWaitTime = _system->getMillis() + stackPos(0) * _tickLength; + while (_system->getMillis() < maxWaitTime) { + int inputFlag = checkInput(0); + removeInputTop(); + + if (inputFlag == 198 || inputFlag == 199) + return 1; + + if (_chatText) + updateWithText(); + else + update(); + _system->delayMillis(10); + } + } else { + delay(stackPos(0) * _tickLength, true); + } + return 0; +} + +int KyraEngine_v2::o2_update(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_update(%p) (%d)", (const void *)script, stackPos(0)); + for (int times = stackPos(0); times != 0; --times) { + if (_chatText) + updateWithText(); + else + update(); + } + return 0; +} + +int KyraEngine_v2::o2_getShapeFlag1(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getShapeFlag1(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return screen()->getShapeFlag1(stackPos(0), stackPos(1)); +} + +int KyraEngine_v2::o2_waitForConfirmationClick(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_waitForConfirmationClick(%p) (%d)", (const void *)script, stackPos(0)); + resetSkipFlag(); + uint32 maxWaitTime = _system->getMillis() + stackPos(0) * _tickLength; + + while (_system->getMillis() < maxWaitTime) { + int inputFlag = checkInput(0); + removeInputTop(); + + if (inputFlag == 198 || inputFlag == 199) { + _sceneScriptState.regs[1] = _mouseX; + _sceneScriptState.regs[2] = _mouseY; + return 0; + } + + update(); + _system->delayMillis(10); + } + + _sceneScriptState.regs[1] = _mouseX; + _sceneScriptState.regs[2] = _mouseY; + return 1; +} + +int KyraEngine_v2::o2_randomSceneChat(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_randomSceneChat(%p)", (const void *)script); + randomSceneChat(); + return 0; +} + +int KyraEngine_v2::o2_setDlgIndex(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setDlgIndex(%p) (%d)", (const void *)script, stackPos(0)); + setDlgIndex(stackPos(0)); + return 0; +} + +int KyraEngine_v2::o2_getDlgIndex(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getDlgIndex(%p) ()", (const void *)script); + return _mainCharacter.dlgIndex; +} + + +int KyraEngine_v2::o2_defineRoomEntrance(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_defineRoomEntrance(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); + switch (stackPos(0)) { + case 0: + _sceneEnterX1 = stackPos(1); + _sceneEnterY1 = stackPos(2); + break; + + case 1: + _sceneEnterX2 = stackPos(1); + _sceneEnterY2 = stackPos(2); + break; + + case 2: + _sceneEnterX3 = stackPos(1); + _sceneEnterY3 = stackPos(2); + break; + + case 3: + _sceneEnterX4 = stackPos(1); + _sceneEnterY4 = stackPos(2); + break; + + default: + break; + } + return 0; +} + +int KyraEngine_v2::o2_runAnimationScript(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_runAnimationScript(%p) ('%s', %d, %d, %d)", (const void *)script, stackPosString(0), stackPos(1), + stackPos(2), stackPos(3)); + + runAnimationScript(stackPosString(0), stackPos(3), stackPos(2) ? 1 : 0, stackPos(1), stackPos(2)); + return 0; +} + +int KyraEngine_v2::o2_setSpecialSceneScriptRunTime(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setSpecialSceneScriptRunTime(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + assert(stackPos(0) >= 0 && stackPos(0) < 10); + _sceneSpecialScriptsTimer[stackPos(0)] = _system->getMillis() + stackPos(1) * _tickLength; + return 0; +} + +int KyraEngine_v2::o2_defineScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_defineScene(%p) (%d, '%s', %d, %d, %d, %d, %d, %d)", + (const void *)script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + const int scene = stackPos(0); + strcpy(_sceneList[scene].filename1, stackPosString(1)); + strcpy(_sceneList[scene].filename2, stackPosString(1)); + + _sceneList[scene].exit1 = stackPos(2); + _sceneList[scene].exit2 = stackPos(3); + _sceneList[scene].exit3 = stackPos(4); + _sceneList[scene].exit4 = stackPos(5); + _sceneList[scene].flags = stackPos(6); + _sceneList[scene].sound = stackPos(7); + + if (_mainCharacter.sceneId == scene) { + _sceneExit1 = _sceneList[scene].exit1; + _sceneExit2 = _sceneList[scene].exit2; + _sceneExit3 = _sceneList[scene].exit3; + _sceneExit4 = _sceneList[scene].exit4; + } + + return 0; +} + +int KyraEngine_v2::o2_setSpecialSceneScriptState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setSpecialSceneScriptState(%p) (%d)", (const void *)script, stackPos(0)); + _specialSceneScriptState[stackPos(0)] = 1; + return 1; +} + +int KyraEngine_v2::o2_clearSpecialSceneScriptState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_clearSpecialSceneScriptState(%p) (%d)", (const void *)script, stackPos(0)); + _specialSceneScriptState[stackPos(0)] = 0; + return 0; +} + +int KyraEngine_v2::o2_querySpecialSceneScriptState(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_querySpecialSceneScriptState(%p) (%d)", (const void *)script, stackPos(0)); + return _specialSceneScriptState[stackPos(0)]; +} + +int KyraEngine_v2::o2_setHiddenItemsEntry(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setHiddenItemsEntry(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + return (_hiddenItems[stackPos(0)] = stackPos(1)); +} + +int KyraEngine_v2::o2_getHiddenItemsEntry(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getHiddenItemsEntry(%p) (%d)", (const void *)script, stackPos(0)); + return (int16)_hiddenItems[stackPos(0)]; +} + +int KyraEngine_v2::o2_disableTimer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_disableTimer(%p) (%d)", (const void *)script, stackPos(0)); + _timer->disable(stackPos(0)); + return 0; +} + +int KyraEngine_v2::o2_enableTimer(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_enableTimer(%p) (%d)", (const void *)script, stackPos(0)); + _timer->enable(stackPos(0)); + return 0; +} + +int KyraEngine_v2::o2_setTimerCountdown(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setTimerCountdown(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + _timer->setCountdown(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine_v2::o2_setVocHigh(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_setVocHigh(%p) (%d)", (const void *)script, stackPos(0)); + _vocHigh = stackPos(0); + return _vocHigh; +} + +int KyraEngine_v2::o2_getVocHigh(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2_getVocHigh(%p) ()", (const void *)script); + return _vocHigh; +} + +#pragma mark - + +int KyraEngine_v2::o2a_setAnimationShapes(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v2::o2a_setAnimationShapes(%p) ('%s', %d, %d, %d, %d, %d)", (const void *)script, + stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + strcpy(_animShapeFilename, stackPosString(0)); + _animShapeLastEntry = stackPos(1); + _animShapeWidth = stackPos(2); + _animShapeHeight = stackPos(3); + _animShapeXAdd = stackPos(4); + _animShapeYAdd = stackPos(5); + return 0; +} + +int KyraEngine_v2::o2a_setResetFrame(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3t_setResetFrame(%p) (%d)", (const void *)script, stackPos(0)); + _animResetFrame = stackPos(0); + return 0; +} + +} // End of namespace Kyra |