From 879425385e07e9c8712908066102c0c102e3e40c Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 18 Jan 2010 00:13:37 +0200 Subject: WAGE: Started implementing script execution. Signed-off-by: Eugene Sandulenko --- engines/wage/entities.h | 3 + engines/wage/script.cpp | 269 ++++++++++++++++++++++++++++++++++++++++++++++++ engines/wage/script.h | 85 ++++++++++++++- engines/wage/wage.cpp | 2 + engines/wage/wage.h | 29 +++++- engines/wage/world.cpp | 4 +- engines/wage/world.h | 10 +- 7 files changed, 390 insertions(+), 12 deletions(-) (limited to 'engines/wage') diff --git a/engines/wage/entities.h b/engines/wage/entities.h index 4ac63c5adb..f99d6d189a 100644 --- a/engines/wage/entities.h +++ b/engines/wage/entities.h @@ -59,6 +59,7 @@ class Scene; class Script; class Context { +public: enum StatVariables { /** The base physical accuracy of the player. */ PHYS_ACC_BAS = 0, @@ -118,6 +119,8 @@ public: } void setDesignBounds(Common::Rect *bounds); + + String toString() { return _name; } }; class Chr : public Designed { diff --git a/engines/wage/script.cpp b/engines/wage/script.cpp index 47e00c1261..a478b8bfed 100644 --- a/engines/wage/script.cpp +++ b/engines/wage/script.cpp @@ -49,9 +49,278 @@ */ #include "wage/wage.h" +#include "wage/entities.h" #include "wage/script.h" +#include "wage/world.h" namespace Wage { +bool Script::execute(World *world, int loopCount, String *inputText, Designed *inputClick, WageEngine *callbacks) { + _world = world; + _loopCount = loopCount; + _inputText = inputText; + _inputClick = inputClick; + _callbacks = callbacks; + _handled = false; + + _index = 12; + while (_index < _dataSize) { + switch(_data[_index]) { + case 0x80: // IF + _index++; + processIf(); + break; + case 0x87: // EXIT + debug(0, "exit at offset %d", _index - 1); + + return true; + case 0x89: // MOVE + { + _index++; + Scene *currentScene = _world->_player->_currentScene; + processMove(); + if (_world->_player->_currentScene != currentScene) + return true; + break; + } + case 0x8B: // PRINT + { + _index++; + Operand *op = readOperand(); + // TODO check op type is string or number, or something good... + appendText(op->_str); + // TODO check data[_index] == 0xFD + _index++; + break; + } + case 0x8C: // SOUND + { + _index++; + Operand *op = readOperand(); + // TODO check op type is string. + _handled = true; + callbacks->playSound(op->_str); + // TODO check data[_index] == 0xFD + _index++; + break; + } + case 0x8E: // LET + _index++; + processLet(); + break; + case 0x95: // MENU + { + _index++; + Operand *op = readStringOperand(); // allows empty menu + // TODO check op type is string. + _callbacks->setMenu(op->_str); + // TODO check data[_index] == 0xFD + _index++; + } + case 0x88: // END + _index++; + break; + default: + debug(0, "Unknown opcode: %d", _index); + } + } + + + if (_world->_globalScript != this) { + debug(0, "Executing global script..."); + bool globalHandled = _world->_globalScript->execute(_world, _loopCount, _inputText, _inputClick, _callbacks); + if (globalHandled) + _handled = true; + } + +#if 0 + else if (inputText != null) { + String input = inputText.toLowerCase(); + if (input.equals("n") || input.contains("north")) { + handleMoveCommand(Scene.NORTH, "north"); + } else if (input.equals("e") || input.contains("east")) { + handleMoveCommand(Scene.EAST, "east"); + } else if (input.equals("s") || input.contains("south")) { + handleMoveCommand(Scene.SOUTH, "south"); + } else if (input.equals("w") || input.contains("west")) { + handleMoveCommand(Scene.WEST, "west"); + } else if (input.startsWith("take ")) { + handleTakeCommand(input.substring(5)); + } else if (input.startsWith("get ")) { + handleTakeCommand(input.substring(4)); + } else if (input.startsWith("pick up ")) { + handleTakeCommand(input.substring(8)); + } else if (input.startsWith("drop ")) { + handleDropCommand(input.substring(5)); + } else if (input.startsWith("aim ")) { + handleAimCommand(input.substring(4)); + } else if (input.startsWith("wear ")) { + handleWearCommand(input.substring(5)); + } else if (input.startsWith("put on ")) { + handleWearCommand(input.substring(7)); + } else if (input.startsWith("offer ")) { + handleOfferCommand(input.substring(6)); + } else if (input.contains("look")) { + handleLookCommand(); + } else if (input.contains("inventory")) { + handleInventoryCommand(); + } else if (input.contains("status")) { + handleStatusCommand(); + } else if (input.contains("rest") || input.equals("wait")) { + handleRestCommand(); + } else if (callbacks.getOffer() != null && input.contains("accept")) { + handleAcceptCommand(); + } else { + Chr player = world.getPlayer(); + for (Weapon weapon : player.getWeapons()) { + if (tryAttack(weapon, input)) { + handleAttack(weapon); + break; + } + } + } + // TODO: weapons, offer, etc... + } else if (inputClick instanceof Obj) { + Obj obj = (Obj) inputClick; + if (obj.getType() != Obj.IMMOBILE_OBJECT) { + takeObj(obj); + } else { + appendText(obj.getClickMessage()); + } + } +#endif + + return _handled; +} + +Script::Operand *Script::readOperand() { + switch (_data[_index++]) { + case 0xA0: // TEXT$ + return new Operand(_inputText, Operand::TEXT_INPUT); + case 0xA1: + return new Operand(_inputClick, Operand::CLICK_INPUT); + case 0xC0: // STORAGE@ + return new Operand(&_world->_storageScene, Operand::SCENE); + case 0xC1: // SCENE@ + return new Operand(_world->_player->_currentScene, Operand::SCENE); + case 0xC2: // PLAYER@ + return new Operand(_world->_player, Operand::CHR); + case 0xC3: // MONSTER@ + return new Operand(_callbacks->_monster, Operand::CHR); + case 0xC4: // RANDOMSCN@ + return new Operand(_world->_orderedScenes[_callbacks->_rnd.getRandomNumber(_world->_orderedScenes.size())], Operand::SCENE); + case 0xC5: // RANDOMCHR@ + return new Operand(_world->_orderedChrs[_callbacks->_rnd.getRandomNumber(_world->_orderedChrs.size())], Operand::CHR); + case 0xC6: // RANDOMOBJ@ + return new Operand(_world->_orderedObjs[_callbacks->_rnd.getRandomNumber(_world->_orderedObjs.size())], Operand::OBJ); + case 0xB0: // VISITS# + return new Operand(_world->_player->_context._visits, Operand::NUMBER); + case 0xB1: // RANDOM# for Star Trek, but VISITS# for some other games? + return new Operand(1 + _callbacks->_rnd.getRandomNumber(100), Operand::NUMBER); + case 0xB5: // RANDOM# // A random number between 1 and 100. + return new Operand(1 + _callbacks->_rnd.getRandomNumber(100), Operand::NUMBER); + case 0xB2: // LOOP# + return new Operand(_loopCount, Operand::NUMBER); + case 0xB3: // VICTORY# + return new Operand(_world->_player->_context._kills, Operand::NUMBER); + case 0xB4: // BADCOPY# + return new Operand(0, Operand::NUMBER); // ???? + case 0xFF: + { + // user variable + int value = _data[_index++]; + if (value < 0) + value += 256; + + // TODO: Verify that we're using the right index. + return new Operand(_world->_player->_context._userVariables[value], Operand::NUMBER); + } + case 0xD0: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_STR_BAS], Operand::NUMBER); + case 0xD1: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_HIT_BAS], Operand::NUMBER); + case 0xD2: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_ARM_BAS], Operand::NUMBER); + case 0xD3: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_ACC_BAS], Operand::NUMBER); + case 0xD4: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_STR_BAS], Operand::NUMBER); + case 0xD5: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_HIT_BAS], Operand::NUMBER); + case 0xD6: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_ARM_BAS], Operand::NUMBER); + case 0xD7: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_ACC_BAS], Operand::NUMBER); + case 0xD8: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_SPE_BAS], Operand::NUMBER); + case 0xE0: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_STR_CUR], Operand::NUMBER); + case 0xE1: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_HIT_CUR], Operand::NUMBER); + case 0xE2: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_ARM_CUR], Operand::NUMBER); + case 0xE3: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_ACC_CUR], Operand::NUMBER); + case 0xE4: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_STR_CUR], Operand::NUMBER); + case 0xE5: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_HIT_CUR], Operand::NUMBER); + case 0xE6: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_ARM_CUR], Operand::NUMBER); + case 0xE7: + return new Operand(_world->_player->_context._statVariables[Context::SPIR_ACC_CUR], Operand::NUMBER); + case 0xE8: + return new Operand(_world->_player->_context._statVariables[Context::PHYS_SPE_CUR], Operand::NUMBER); + default: + _index--; + if (_data[_index] >= 0x20 && _data[_index] < 0x80) { + return readStringOperand(); + } else { + debug("Dunno what %x is (index=%d)!\n", _data[_index], _index); + } + _index++; + return NULL; + } +} + +Script::Operand *Script::readStringOperand() { + String *sb; + bool allDigits = true; + + sb = new String(); + + while (_data[_index] >= 0x20 && _data[_index] < 0x80) { + char c = _data[_index++]; + if (c < '0' || c > '9') + allDigits = false; + *sb += c; + } + + if (allDigits && sb->size() > 0) { + debug(0, "Read number %s", sb->c_str()); + int r = atol(sb->c_str()); + delete sb; + + return new Operand(r, Operand::NUMBER); + } else { + // TODO: This string could be a room name or something like that. + debug(0, "Read string %s", sb->c_str()); + return new Operand(sb, Operand::STRING); + } +} + +void Script::processIf() { +} + +void Script::processMove() { +} + +void Script::processLet() { +} + +void Script::appendText(String str) { + _handled = true; + _callbacks->appendText(str); +} } // End of namespace Wage diff --git a/engines/wage/script.h b/engines/wage/script.h index dcbf4087e7..a5288d66da 100644 --- a/engines/wage/script.h +++ b/engines/wage/script.h @@ -55,11 +55,94 @@ namespace Wage { class Script { public: - Script(byte *data) : _data(data) {} + Script(byte *data, int dataSize) : _data(data), _dataSize(dataSize) {} ~Script(); private: byte *_data; + int _dataSize; + + WageEngine *_callbacks; + World *_world; + int _loopCount; + String *_inputText; + Designed *_inputClick; + int _index; + bool _evalResult; + bool _handled; + + class Operand { + public: + enum OperandTypes { + OBJ = 0, + CHR = 1, + SCENE = 2, + NUMBER = 3, + STRING = 4, + CLICK_INPUT = 5, + TEXT_INPUT = 6 + }; + + union { + Obj *obj; + Chr *chr; + Scene *scene; + int16 number; + String *string; + Designed *inputClick; + } _value; + OperandTypes _type; + String _str; + + Operand(Obj *value, OperandTypes type) { + _value.obj = value; + _str = value->toString(); + _type = type; + } + + Operand(Chr *value, OperandTypes type) { + _value.chr = value; + _str = value->toString(); + _type = type; + } + + Operand(Scene *value, OperandTypes type) { + _value.scene = value; + _str = value->toString(); + _type = type; + } + + Operand(int value, OperandTypes type) { + char buf[30]; + _value.number = value; + snprintf(buf, 30, "%d", value); + _str = value; + _type = type; + } + + Operand(String *value, OperandTypes type) { + _value.string = value; + _str = *value; + _type = type; + } + + Operand(Designed *value, OperandTypes type) { + _value.inputClick = value; + _str = value->toString(); + _type = type; + } + }; + + + bool execute(World *world, int loopCount, String *inputText, Designed *inputClick, WageEngine *callbacks); + Operand *readOperand(); + Operand *readStringOperand(); + void processIf(); + void processMove(); + void processLet(); + + void appendText(String str); + }; } // End of namespace Wage diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp index a681fc69bb..2be43e7880 100644 --- a/engines/wage/wage.cpp +++ b/engines/wage/wage.cpp @@ -64,6 +64,8 @@ namespace Wage { WageEngine::WageEngine(OSystem *syst, const ADGameDescription *desc) : Engine(syst), _gameDescription(desc) { // Don't forget to register your random source g_eventRec.registerRandomSource(_rnd, "wage"); + + _aim = -1; printf("WageEngine::WageEngine\n"); } diff --git a/engines/wage/wage.h b/engines/wage/wage.h index 04a505c4ab..5c56d74eb8 100644 --- a/engines/wage/wage.h +++ b/engines/wage/wage.h @@ -64,6 +64,9 @@ namespace Wage { class Console; class World; +class Scene; +class Obj; +class Chr; using Common::String; @@ -95,17 +98,35 @@ public: private: bool loadWorld(Common::MacResManager *resMan); -private: - Console *_console; - +public: // We need random numbers Common::RandomSource _rnd; + World *_world; + + Scene *_lastScene; + //PrintStream out; + int _loopCount; + int _turn; + //Callbacks callbacks; + Chr *_monster; + Obj *_offer; + bool _commandWasQuick; + int _aim; + bool _temporarilyHidden; + + void playSound(String soundName) {} + void setMenu(String soundName) {} + void appendText(String str) {} + + +private: + Console *_console; + const ADGameDescription *_gameDescription; Common::MacResManager *_resManager; - World *_world; }; // Example console class diff --git a/engines/wage/world.cpp b/engines/wage/world.cpp index a2cca7cce1..c8ed238be0 100644 --- a/engines/wage/world.cpp +++ b/engines/wage/world.cpp @@ -75,7 +75,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { // Load global script res = resMan->getResource("GCOD", resArray[0], &resSize); - _globalScript = new Script(res); + _globalScript = new Script(res, resSize); // Load main configuration if ((resArray = resMan->getResIDArray("VERS")).size() == 0) @@ -112,7 +112,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { res = resMan->getResource("ACOD", *iter, &resSize); if (res != NULL) - scene->_script = new Script(res); + scene->_script = new Script(res, resSize); res = resMan->getResource("ATXT", *iter, &resSize); if (res != NULL) { diff --git a/engines/wage/world.h b/engines/wage/world.h index 3ffd607eca..30832a603d 100644 --- a/engines/wage/world.h +++ b/engines/wage/world.h @@ -74,11 +74,11 @@ public: Common::HashMap _objs; Common::HashMap _chrs; Common::HashMap _sounds; - Common::List _orderedScenes; - Common::List _orderedObjs; - Common::List _orderedChrs; - Common::List _orderedSounds; - Common::List _patterns; + Common::Array _orderedScenes; + Common::Array _orderedObjs; + Common::Array _orderedChrs; + Common::Array _orderedSounds; + Common::Array _patterns; Scene _storageScene; Chr *_player; //List moveListeners; -- cgit v1.2.3