From 64cf93198fc560d31cc24003d164f656751d2e76 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Fri, 18 Mar 2016 00:30:51 +0100 Subject: ADL: Use functors to implement opcodes --- engines/adl/adl.cpp | 461 +++++++++++++++++++++++++++++++++------------------- engines/adl/adl.h | 55 ++++++- 2 files changed, 342 insertions(+), 174 deletions(-) diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp index bd1cc84f42..ad5f559fe0 100644 --- a/engines/adl/adl.cpp +++ b/engines/adl/adl.cpp @@ -278,6 +278,71 @@ void AdlEngine::checkInput(byte verb, byte noun) { printMessage(_messageIds.dontUnderstand); } +typedef Common::Functor1Mem OpcodeV1; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV1(this, &AdlEngine::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV1(this, 0)) + +void AdlEngine::setupOpcodeTables() { + Common::Array *table = 0; + + SetOpcodeTable(_condOpcodes); + // 0x00 + OpcodeUnImpl(); + OpcodeUnImpl(); + OpcodeUnImpl(); + Opcode(o1_isItemInRoom); + // 0x04 + OpcodeUnImpl(); + Opcode(o1_isMovesGrEq); + Opcode(o1_isVarEq); + OpcodeUnImpl(); + // 0x08 + OpcodeUnImpl(); + Opcode(o1_isCurPicEq); + Opcode(o1_isItemPicEq); + + SetOpcodeTable(_actOpcodes); + // 0x00 + OpcodeUnImpl(); + Opcode(o1_varAdd); + Opcode(o1_varSub); + Opcode(o1_varSet); + // 0x04 + Opcode(o1_listInv); + Opcode(o1_moveItem); + Opcode(o1_setRoom); + Opcode(o1_setCurPic); + // 0x08 + Opcode(o1_setPic); + Opcode(o1_printMsg); + Opcode(o1_setLight); + Opcode(o1_setDark); + // 0x0c + OpcodeUnImpl(); + Opcode(o1_quit); + OpcodeUnImpl(); + Opcode(o1_save); + // 0x10 + Opcode(o1_restore); + Opcode(o1_restart); + Opcode(o1_placeItem); + Opcode(o1_setItemPic); + // 0x14 + Opcode(o1_resetPic); + Opcode(o1_goDirection); + Opcode(o1_goDirection); + Opcode(o1_goDirection); + // 0x18 + Opcode(o1_goDirection); + Opcode(o1_goDirection); + Opcode(o1_goDirection); + Opcode(o1_takeItem); + // 0x1c + Opcode(o1_dropItem); + Opcode(o1_setRoomPic); +} + void AdlEngine::clearScreen() const { _display->setMode(DISPLAY_MODE_MIXED); _display->clear(0x00); @@ -416,6 +481,8 @@ Common::Error AdlEngine::run() { _speaker = new Speaker(); _display = new Display(); + setupOpcodeTables(); + init(); int saveSlot = ConfMan.getInt("save_slot"); @@ -657,8 +724,9 @@ bool AdlEngine::canSaveGameStateCurrently() { // "SAVE GAME". This prevents saving via the GMM in situations where // it wouldn't otherwise be possible to do so. for (cmd = _roomCommands.begin(); cmd != _roomCommands.end(); ++cmd) { + ScriptEnv env(*cmd, _saveVerb, _saveNoun); uint offset; - if (matchCommand(*cmd, _saveVerb, _saveNoun, &offset)) + if (matchCommand(env, &offset)) return cmd->script[offset] == IDO_ACT_SAVE; } @@ -784,198 +852,249 @@ void AdlEngine::getInput(uint &verb, uint &noun) { } } -#define ARG(N) (command.script[offset + (N)]) +typedef Common::Functor1Mem OpcodeV1; -bool AdlEngine::matchCommand(const Command &command, byte verb, byte noun, uint *actions) const { - if (command.room != IDI_NONE && command.room != _state.room) +#define ARG(N) (env.cmd.script[env.ip + (N)]) + +bool AdlEngine::o1_isItemInRoom(ScriptEnv &env) { + if (getItem(ARG(1)).room != ARG(2)) return false; + env.ip += 3; + return true; +} - if (command.verb != IDI_NONE && command.verb != verb) +bool AdlEngine::o1_isMovesGrEq(ScriptEnv &env) { + if (ARG(1) > _state.moves) return false; + env.ip += 2; + return true; +} - if (command.noun != IDI_NONE && command.noun != noun) +bool AdlEngine::o1_isVarEq(ScriptEnv &env) { + if (getVar(ARG(1)) != ARG(2)) return false; + env.ip += 3; + return true; +} - uint offset = 0; - for (uint i = 0; i < command.numCond; ++i) { - switch (ARG(0)) { - case IDO_CND_ITEM_IN_ROOM: - if (getItem(ARG(1)).room != ARG(2)) - return false; - offset += 3; - break; - case IDO_CND_MOVES_GE: - if (ARG(1) > _state.moves) - return false; - offset += 2; - break; - case IDO_CND_VAR_EQ: - if (getVar(ARG(1)) != ARG(2)) - return false; - offset += 3; - break; - case IDO_CND_CUR_PIC_EQ: - if (getCurRoom().curPicture != ARG(1)) - return false; - offset += 2; - break; - case IDO_CND_ITEM_PIC_EQ: - if (getItem(ARG(1)).picture != ARG(2)) - return false; - offset += 3; - break; - default: - error("Invalid condition opcode %02x", command.script[offset]); - } +bool AdlEngine::o1_isCurPicEq(ScriptEnv &env) { + if (getCurRoom().curPicture != ARG(1)) + return false; + env.ip += 2; + return true; +} + +bool AdlEngine::o1_isItemPicEq(ScriptEnv &env) { + if (getItem(ARG(1)).picture != ARG(2)) + return false; + env.ip += 3; + return true; +} + +bool AdlEngine::o1_varAdd(ScriptEnv &env) { + setVar(ARG(2), getVar(ARG(2) + ARG(1))); + env.ip += 3; + return true; +} + +bool AdlEngine::o1_varSub(ScriptEnv &env) { + setVar(ARG(2), getVar(ARG(2)) - ARG(1)); + env.ip += 3; + return true; +} + +bool AdlEngine::o1_varSet(ScriptEnv &env) { + setVar(ARG(1), ARG(2)); + env.ip += 3; + return true; +} + +bool AdlEngine::o1_listInv(ScriptEnv &env) { + Common::Array::const_iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->room == IDI_NONE) + printMessage(item->description); + + ++env.ip; + return true; +} + +bool AdlEngine::o1_moveItem(ScriptEnv &env) { + getItem(ARG(1)).room = ARG(2); + env.ip += 3; + return true; +} + +bool AdlEngine::o1_setRoom(ScriptEnv &env) { + getCurRoom().curPicture = getCurRoom().picture; + _state.room = ARG(1); + env.ip += 2; + return true; +} + +bool AdlEngine::o1_setCurPic(ScriptEnv &env) { + getCurRoom().curPicture = ARG(1); + env.ip += 2; + return true; +} + +bool AdlEngine::o1_setPic(ScriptEnv &env) { + getCurRoom().picture = getCurRoom().curPicture = ARG(1); + env.ip += 2; + return true; +} + +bool AdlEngine::o1_printMsg(ScriptEnv &env) { + printMessage(ARG(1)); + env.ip += 2; + return true; +} + +bool AdlEngine::o1_setLight(ScriptEnv &env) { + _state.isDark = false; + ++env.ip; + return true; +} + +bool AdlEngine::o1_setDark(ScriptEnv &env) { + _state.isDark = true; + ++env.ip; + return true; +} + +bool AdlEngine::o1_save(ScriptEnv &env) { + saveGameState(0, ""); + ++env.ip; + return true; +} + +bool AdlEngine::o1_restore(ScriptEnv &env) { + loadGameState(0); + ++env.ip; + _isRestoring = false; + return true; +} + +bool AdlEngine::o1_restart(ScriptEnv &env) { + _display->printString(_strings.playAgain); + Common::String input = inputString(); + + if (input.size() == 0 || input[0] != APPLECHAR('N')) { + _isRestarting = true; + _display->clear(0x00); + _display->updateHiResScreen(); + restartGame(); + return false; } - if (actions) - *actions = offset; + return o1_quit(env); +} + +bool AdlEngine::o1_quit(ScriptEnv &env) { + printMessage(_messageIds.thanksForPlaying); + quitGame(); + return false; +} +bool AdlEngine::o1_placeItem(ScriptEnv &env) { + getItem(ARG(1)).room = ARG(2); + getItem(ARG(1)).position.x = ARG(3); + getItem(ARG(1)).position.y = ARG(4); + env.ip += 5; return true; } -void AdlEngine::doActions(const Command &command, byte noun, byte offset) { - for (uint i = 0; i < command.numAct; ++i) { - switch (ARG(0)) { - case IDO_ACT_VAR_ADD: - setVar(ARG(2), getVar(ARG(2) + ARG(1))); - offset += 3; - break; - case IDO_ACT_VAR_SUB: - setVar(ARG(2), getVar(ARG(2)) - ARG(1)); - offset += 3; - break; - case IDO_ACT_VAR_SET: - setVar(ARG(1), ARG(2)); - offset += 3; - break; - case IDO_ACT_LIST_ITEMS: { - Common::Array::const_iterator item; +bool AdlEngine::o1_setItemPic(ScriptEnv &env) { + getItem(ARG(2)).picture = ARG(1); + env.ip += 3; + return true; +} - for (item = _state.items.begin(); item != _state.items.end(); ++item) - if (item->room == IDI_NONE) - printMessage(item->description); +bool AdlEngine::o1_resetPic(ScriptEnv &env) { + getCurRoom().curPicture = getCurRoom().picture; + ++env.ip; + return true; +} - ++offset; - break; - } - case IDO_ACT_MOVE_ITEM: - getItem(ARG(1)).room = ARG(2); - offset += 3; - break; - case IDO_ACT_SET_ROOM: - getCurRoom().curPicture = getCurRoom().picture; - _state.room = ARG(1); - offset += 2; - break; - case IDO_ACT_SET_CUR_PIC: - getCurRoom().curPicture = ARG(1); - offset += 2; - break; - case IDO_ACT_SET_PIC: - getCurRoom().picture = getCurRoom().curPicture = ARG(1); - offset += 2; - break; - case IDO_ACT_PRINT_MSG: - printMessage(ARG(1)); - offset += 2; - break; - case IDO_ACT_SET_LIGHT: - _state.isDark = false; - ++offset; - break; - case IDO_ACT_SET_DARK: - _state.isDark = true; - ++offset; - break; - case IDO_ACT_SAVE: - saveGameState(0, ""); - ++offset; - break; - case IDO_ACT_LOAD: - loadGameState(0); - ++offset; - // Original engine does not jump out of the loop, - // so we don't either. - // We reset the restore flag, as the restore game - // process is complete - _isRestoring = false; - break; - case IDO_ACT_RESTART: { - _display->printString(_strings.playAgain); - Common::String input = inputString(); - if (input.size() == 0 || input[0] != APPLECHAR('N')) { - _isRestarting = true; - _display->clear(0x00); - _display->updateHiResScreen(); - restartGame(); - return; - } - // Fall-through - } - case IDO_ACT_QUIT: - printMessage(_messageIds.thanksForPlaying); - quitGame(); - return; - case IDO_ACT_PLACE_ITEM: - getItem(ARG(1)).room = ARG(2); - getItem(ARG(1)).position.x = ARG(3); - getItem(ARG(1)).position.y = ARG(4); - offset += 5; - break; - case IDO_ACT_SET_ITEM_PIC: - getItem(ARG(2)).picture = ARG(1); - offset += 3; - break; - case IDO_ACT_RESET_PIC: - getCurRoom().curPicture = getCurRoom().picture; - ++offset; - break; - case IDO_ACT_GO_NORTH: - case IDO_ACT_GO_SOUTH: - case IDO_ACT_GO_EAST: - case IDO_ACT_GO_WEST: - case IDO_ACT_GO_UP: - case IDO_ACT_GO_DOWN: { - byte room = getCurRoom().connections[ARG(0) - IDO_ACT_GO_NORTH]; - - if (room == 0) { - printMessage(_messageIds.cantGoThere); - return; - } +bool AdlEngine::o1_goDirection(ScriptEnv &env) { + byte room = getCurRoom().connections[ARG(0) - IDO_ACT_GO_NORTH]; - getCurRoom().curPicture = getCurRoom().picture; - _state.room = room; - return; - } - case IDO_ACT_TAKE_ITEM: - takeItem(noun); - ++offset; - break; - case IDO_ACT_DROP_ITEM: - dropItem(noun); - ++offset; - break; - case IDO_ACT_SET_ROOM_PIC: - getRoom(ARG(1)).picture = getRoom(ARG(1)).curPicture = ARG(2); - offset += 3; - break; - default: - error("Invalid action opcode %02x", ARG(0)); - } + if (room == 0) { + printMessage(_messageIds.cantGoThere); + return false; } + + getCurRoom().curPicture = getCurRoom().picture; + _state.room = room; + return false; +} + +bool AdlEngine::o1_takeItem(ScriptEnv &env) { + takeItem(env.noun); + ++env.ip; + return true; +} + +bool AdlEngine::o1_dropItem(ScriptEnv &env) { + dropItem(env.noun); + ++env.ip; + return true; +} + +bool AdlEngine::o1_setRoomPic(ScriptEnv &env) { + getRoom(ARG(1)).picture = getRoom(ARG(1)).curPicture = ARG(2); + env.ip += 3; + return true; } #undef ARG +bool AdlEngine::matchCommand(ScriptEnv &env, uint *actions) const { + if (env.cmd.room != IDI_NONE && env.cmd.room != _state.room) + return false; + + if (env.cmd.verb != IDI_NONE && env.cmd.verb != env.verb) + return false; + + if (env.cmd.noun != IDI_NONE && env.cmd.noun != env.noun) + return false; + + for (uint i = 0; i < env.cmd.numCond; ++i) { + byte op = env.cmd.script[env.ip]; + + if (!_condOpcodes[op] || !_condOpcodes[op]->isValid()) + error("Unimplemented condition opcode %02x", op); + + if (!(*_condOpcodes[op])(env)) + return false; + } + + if (actions) + *actions = env.ip; + + return true; +} + +void AdlEngine::doActions(ScriptEnv &env) { + for (uint i = 0; i < env.cmd.numAct; ++i) { + byte op = env.cmd.script[env.ip]; + + if (!_actOpcodes[op] || !_actOpcodes[op]->isValid()) + error("Unimplemented action opcode %02x", op); + + if (!(*_actOpcodes[op])(env)) + return; + } +} + bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) { Commands::const_iterator cmd; for (cmd = commands.begin(); cmd != commands.end(); ++cmd) { - uint offset = 0; - if (matchCommand(*cmd, verb, noun, &offset)) { - doActions(*cmd, noun, offset); + ScriptEnv env(*cmd, verb, noun); + if (matchCommand(env)) { + doActions(env); return true; } } @@ -987,9 +1106,9 @@ void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) { Commands::const_iterator cmd; for (cmd = commands.begin(); cmd != commands.end(); ++cmd) { - uint offset = 0; - if (matchCommand(*cmd, verb, noun, &offset)) - doActions(*cmd, noun, offset); + ScriptEnv env(*cmd, verb, noun); + if (matchCommand(env)) + doActions(env); } } diff --git a/engines/adl/adl.h b/engines/adl/adl.h index 97077f94f7..e007b15545 100644 --- a/engines/adl/adl.h +++ b/engines/adl/adl.h @@ -28,6 +28,7 @@ #include "common/str.h" #include "common/hashmap.h" #include "common/hash-str.h" +#include "common/func.h" #include "engines/engine.h" @@ -47,6 +48,9 @@ class Display; class GraphicsMan; class Speaker; struct AdlGameDescription; +struct ScriptEnv; + +typedef Common::Functor1 Opcode; // Conditional opcodes #define IDO_CND_ITEM_IN_ROOM 0x03 @@ -101,11 +105,22 @@ struct Picture { uint16 offset; }; +typedef Common::Array Script; + struct Command { byte room; byte verb, noun; byte numCond, numAct; - Common::Array script; + Script script; +}; + +struct ScriptEnv { + ScriptEnv(const Command &cmd_, byte verb_, byte noun_) : + cmd(cmd_), verb(verb_), noun(noun_), ip(0) { } + + const Command &cmd; + byte verb, noun; + byte ip; }; enum { @@ -165,6 +180,38 @@ protected: void readCommands(Common::ReadStream &stream, Commands &commands); virtual void checkInput(byte verb, byte noun); + virtual void setupOpcodeTables(); + + // Opcodes + bool o1_isItemInRoom(ScriptEnv &env); + bool o1_isMovesGrEq(ScriptEnv &env); + bool o1_isVarEq(ScriptEnv &env); + bool o1_isCurPicEq(ScriptEnv &env); + bool o1_isItemPicEq(ScriptEnv &env); + + bool o1_varAdd(ScriptEnv &env); + bool o1_varSub(ScriptEnv &env); + bool o1_varSet(ScriptEnv &env); + bool o1_listInv(ScriptEnv &env); + bool o1_moveItem(ScriptEnv &env); + bool o1_setRoom(ScriptEnv &env); + bool o1_setCurPic(ScriptEnv &env); + bool o1_setPic(ScriptEnv &env); + bool o1_printMsg(ScriptEnv &env); + bool o1_setLight(ScriptEnv &env); + bool o1_setDark(ScriptEnv &env); + bool o1_save(ScriptEnv &env); + bool o1_restore(ScriptEnv &env); + bool o1_restart(ScriptEnv &env); + bool o1_quit(ScriptEnv &env); + bool o1_placeItem(ScriptEnv &env); + bool o1_setItemPic(ScriptEnv &env); + bool o1_resetPic(ScriptEnv &env); + bool o1_goDirection(ScriptEnv &env); + bool o1_takeItem(ScriptEnv &env); + bool o1_dropItem(ScriptEnv &env); + bool o1_setRoomPic(ScriptEnv &env); + // Graphics void clearScreen() const; void drawItems() const; @@ -183,8 +230,8 @@ protected: void setVar(uint i, byte value); void takeItem(byte noun); void dropItem(byte noun); - bool matchCommand(const Command &command, byte verb, byte noun, uint *actions = nullptr) const; - void doActions(const Command &command, byte noun, byte offset); + bool matchCommand(ScriptEnv &env, uint *actions = nullptr) const; + void doActions(ScriptEnv &env); bool doOneCommand(const Commands &commands, byte verb, byte noun); void doAllCommands(const Commands &commands, byte verb, byte noun); @@ -192,6 +239,8 @@ protected: GraphicsMan *_graphics; Speaker *_speaker; + // Opcodes + Common::Array _condOpcodes, _actOpcodes; // Message strings in data file Common::Array _messages; // Picture data -- cgit v1.2.3