diff options
author | Paul Gilbert | 2010-08-25 06:42:54 +0000 |
---|---|---|
committer | Paul Gilbert | 2010-08-25 06:42:54 +0000 |
commit | 212479ab79cfabade43222e1295aa402435d55d9 (patch) | |
tree | 9f3425de06fc0c5f91c232db019e3073abf64e69 /engines/m4 | |
parent | 4c215fc9a01008fd93bb6d717bb705462b435de4 (diff) | |
download | scummvm-rg350-212479ab79cfabade43222e1295aa402435d55d9.tar.gz scummvm-rg350-212479ab79cfabade43222e1295aa402435d55d9.tar.bz2 scummvm-rg350-212479ab79cfabade43222e1295aa402435d55d9.zip |
M4: Implementation of script engine
svn-id: r52364
Diffstat (limited to 'engines/m4')
-rw-r--r-- | engines/m4/globals.h | 65 | ||||
-rw-r--r-- | engines/m4/m4.h | 12 | ||||
-rw-r--r-- | engines/m4/mads_logic.cpp | 768 | ||||
-rw-r--r-- | engines/m4/mads_logic.h | 52 | ||||
-rw-r--r-- | engines/m4/mads_scene.cpp | 3 | ||||
-rw-r--r-- | engines/m4/mads_scene.h | 4 | ||||
-rw-r--r-- | engines/m4/mads_views.cpp | 12 | ||||
-rw-r--r-- | engines/m4/scene.cpp | 1 | ||||
-rw-r--r-- | engines/m4/scene.h | 7 |
9 files changed, 743 insertions, 181 deletions
diff --git a/engines/m4/globals.h b/engines/m4/globals.h index 3fc31b4ec2..67dcfc2b94 100644 --- a/engines/m4/globals.h +++ b/engines/m4/globals.h @@ -229,7 +229,68 @@ struct MadsConfigData { #define SET_GLOBAL(x,y) _madsVm->globals()->_globals[x] = y #define SET_GLOBAL32(x,y) { _madsVm->globals()->_globals[x] = (y) & 0xffff; _madsVm->globals()->_globals[(x) + 1] = (y) >> 16; } -typedef Common::HashMap<uint16, uint16> IntStorage; +union DataMapEntry { + bool *boolValue; + uint16 *uint16Value; + int *intValue; +}; + +typedef Common::HashMap<uint16, uint16> DataMapHash; + +enum DataMapType {BOOL, UINT16, INT}; + +class DataMapWrapper { + friend class DataMap; +private: + DataMapEntry _value; + DataMapType _type; +public: + DataMapWrapper(bool *v) { _value.boolValue = v; _type = BOOL; } + DataMapWrapper(uint16 *v) { _value.uint16Value = v; _type = UINT16; } + DataMapWrapper(int16 *v) { _value.uint16Value = (uint16 *)v; _type = UINT16; } + DataMapWrapper(int *v) { _value.intValue = v; _type = INT; } + + uint16 getIntValue() { + if (_type == BOOL) return *_value.boolValue ? 0xffff : 0; + else if (_type == UINT16) return *_value.uint16Value; + else return *_value.intValue; + } + void setIntValue(uint16 v) { + if (_type == BOOL) *_value.boolValue = v != 0; + else if (_type == UINT16) *_value.uint16Value = v; + else *_value.intValue = v; + } +}; + +#define MAP_DATA(V) _madsVm->globals()->_dataMap.addMapping(new DataMapWrapper(V)) + +class DataMap { +private: + DataMapHash _data; + Common::Array<DataMapWrapper *> _mapList; +public: + DataMap() { + _mapList.push_back(NULL); + } + ~DataMap() { + for (uint i = 1; i < _mapList.size(); ++i) + delete _mapList[i]; + } + + void addMapping(DataMapWrapper *v) { _mapList.push_back(v); } + uint16 get(uint16 index) { + if (index < _mapList.size()) + return _mapList[index]->getIntValue(); + + return _data[index]; + } + void set(uint16 index, uint16 v) { + if (index < _mapList.size()) + _mapList[index]->setIntValue(v); + else + _data[index] = v; + } +}; class MadsGlobals : public Globals { private: @@ -259,7 +320,7 @@ public: int previousScene; int16 _nextSceneId; uint16 actionNouns[3]; - IntStorage _dataMap; + DataMap _dataMap; int _difficultyLevel; void loadMadsVocab(); diff --git a/engines/m4/m4.h b/engines/m4/m4.h index 3174c886d5..b68f7248af 100644 --- a/engines/m4/m4.h +++ b/engines/m4/m4.h @@ -96,10 +96,10 @@ class Animation; enum M4GameType { GType_Riddle = 1, - GType_Burger, - GType_RexNebular, - GType_DragonSphere, - GType_Phantom + GType_Burger = 2, + GType_RexNebular = 3, + GType_DragonSphere = 4, + GType_Phantom = 5 }; enum Features { @@ -224,8 +224,10 @@ public: MadsGlobals *globals() { return (MadsGlobals *)_globals; } MadsScene *scene() { return (MadsScene *)_scene; } void startScene(int sceneNum) { - if (!_scene) + if (!_scene) { _scene = new MadsScene(this); + ((MadsScene *)_scene)->initialise(); + } _scene->show(); _scene->loadScene(101); } diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp index e7c20b237d..23edf645b0 100644 --- a/engines/m4/mads_logic.cpp +++ b/engines/m4/mads_logic.cpp @@ -24,9 +24,12 @@ */ #include "m4/m4.h" +#include "m4/dialogs.h" #include "m4/mads_logic.h" #include "m4/scene.h" +#define MAX_CALL_PARAMS 10 + namespace M4 { void MadsGameLogic::initialiseGlobals() { @@ -139,6 +142,52 @@ void MadsGameLogic::initialiseGlobals() { /*--------------------------------------------------------------------------*/ +const char *MadsSceneLogic::subFormatList[] = {"scene%d_enter", "scene%d_step", "scene%d_preaction", "scene%d_actions"}; + +#define OPSIZE8 0x40 ///< when this bit is set - the operand size is 8 bits +#define OPSIZE16 0x80 ///< when this bit is set - the operand size is 16 bits +#define OPMASK 0x3F ///< mask to isolate the opcode + +enum Opcodes { + OP_HALT = 0, OP_IMM = 1, OP_ZERO = 2, OP_ONE = 3, OP_MINUSONE = 4, OP_STR = 5, OP_DLOAD = 6, + OP_DSTORE = 7, OP_PAL = 8, OP_LOAD = 9, OP_GLOAD = 10, OP_STORE = 11, OP_GSTORE = 12, + OP_CALL = 13, OP_LIBCALL = 14, OP_RET = 15, OP_ALLOC = 16, OP_JUMP = 17, OP_JMPFALSE = 18, + OP_JMPTRUE = 19, OP_EQUAL = 20, OP_LESS = 21, OP_LEQUAL = 22, OP_NEQUAL = 23, OP_GEQUAL = 24, + OP_GREAT = 25, OP_PLUS = 26, OP_MINUS = 27, OP_LOR = 28, OP_MULT = 29, OP_DIV = 30, + OP_MOD = 31, OP_AND = 32, OP_OR = 33, OP_EOR = 34, OP_LAND = 35, OP_NOT = 36, OP_COMP = 37, + OP_NEG = 38, OP_DUP = 39, + TOTAL_OPCODES = 40 +}; + +const char *MadsSceneLogic::_opcodeStrings[] = { + "HALT", "IMM", "ZERO", "ONE", "MINUSONE", "STR", "DLOAD", "DSTORE", NULL, "LOAD", "GLOAD", + "STORE", "GSTORE", "CALL", "LIBCALL", "RET", "ALLOC", "JUMP", "JMPFALSE", "JMPTRUE", "EQUAL", + "LESS", "LEQUAL", "NEQUAL", "GEQUAL", "GREAT", "PLUS", "MINUS", "LOR", "MULT", "DIV", + "MOD", "AND", "OR", "EOR", "LAND", "NOT", "COMP", "NEG", "DUP" +}; + +/** + * This method sets up the data map with pointers to all the common game objects. This allows the script engine to + * convert game specific offsets for various fields in the original game's data segment into a generic data index + * that will be common across all the MADS games + */ +void MadsSceneLogic::initialiseDataMap() { + // The unique order of these items must be maintained + MAP_DATA((uint16 *)&_madsVm->scene()->_abortTimersMode2); + MAP_DATA(&_madsVm->scene()->_abortTimers); + MAP_DATA(&_madsVm->_player._stepEnabled); + MAP_DATA(&_madsVm->scene()->_nextScene); + MAP_DATA(&_madsVm->scene()->_previousScene); + MAP_DATA(&_madsVm->_player._playerPos.x); + MAP_DATA(&_madsVm->_player._playerPos.y); + MAP_DATA(&_madsVm->_player._direction); + MAP_DATA(&_madsVm->_player._visible); +} + +DataMap &MadsSceneLogic::dataMap() { + return _madsVm->globals()->_dataMap; +} + const char *MadsSceneLogic::formAnimName(char sepChar, int16 suffixNum) { return MADSResourceManager::getResourceName(sepChar, _sceneNumber, EXTTYPE_NONE, NULL, suffixNum); } @@ -177,10 +226,6 @@ void MadsSceneLogic::getAnimName() { strcpy(_madsVm->scene()->_aaName, newName); } -IntStorage &MadsSceneLogic::dataMap() { - return _madsVm->globals()->_dataMap; -} - /*--------------------------------------------------------------------------*/ uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) { @@ -220,44 +265,6 @@ void MadsSceneLogic::activateHotspot(int idx, bool active) { // TODO: } -void MadsSceneLogic::lowRoomsEntrySound() { - if (!_madsVm->globals()->_config.musicFlag) { - _madsVm->_sound->playSound(2); - } else { - // Play different sounds for each of the rooms - switch (_madsVm->globals()->sceneNumber) { - case 101: - _madsVm->_sound->playSound(11); - break; - case 102: - _madsVm->_sound->playSound(12); - break; - case 103: - _madsVm->_sound->playSound(3); - _madsVm->_sound->playSound(25); - break; - case 104: - _madsVm->_sound->playSound(10); - break; - case 105: - if ((_madsVm->globals()->previousScene < 104) || (_madsVm->globals()->previousScene > 108)) - _madsVm->_sound->playSound(10); - break; - case 106: - _madsVm->_sound->playSound(13); - break; - case 107: - _madsVm->_sound->playSound(3); - break; - case 108: - _madsVm->_sound->playSound(15); - break; - default: - break; - } - } -} - void MadsSceneLogic::getPlayerSpritesPrefix() { _madsVm->_sound->playSound(5); @@ -315,17 +322,107 @@ void MadsSceneLogic::getPlayerSpritesPrefix2() { /*--------------------------------------------------------------------------*/ /** - * FIXME: - * Currently I'm only working at providing manual implementation of the first Rex Nebular scene. - * It will make more sense to convert the remaining game logic from the games into some - * kind of bytecode scripts + * Loads the MADS.DAT file and loads the script data for the correct game/language */ +void MadsSceneLogic::initialiseScripts() { + Common::File f; + if (!f.open("mads.dat")) { + warning("Could not locate mads.dat file"); + return; + } + + // Validate that the file being read is a valid mads.dat file + char header[4]; + f.read(&header[0], 4); + if (strncmp(header, "MADS", 4) != 0) { + warning("Invalid mads.dat file"); + return; + } + + // Get a list of the offsets of game blocks + uint32 v; + Common::Array<uint32> offsets; + while ((v = f.readUint32LE()) != 0) + offsets.push_back(v); + + // Check the header of each block in turn + _scriptsData = NULL; + _scriptsSize = 0; + + for (uint i = 0; i < offsets.size(); ++i) { + // Get the block header + f.seek(offsets[i]); + byte gameId = f.readByte(); + byte language = f.readByte(); + f.readByte(); // Language currently unused + + // If this block isn't for the current game, skip it + if (_madsVm->getGameType() != (gameId + 2)) + continue; + if ((language != 1) || (_madsVm->getLanguage() != Common::EN_ANY)) + continue; + + // Found script block for the given game and language. + _scriptsSize = (i < (offsets.size() - 1)) ? offsets[i + 1] - offsets[i] : f.size() - offsets[i]; + break; + } + + if (!_scriptsSize) { + warning("Could not find appropriate scripts block for game in mads.dat file"); + f.close(); + return; + } + + // Load up the list of subroutines into a hash map + uint32 blockOffset = f.pos() - 3; + uint32 subsStart = 0; + for (;;) { + // Get next entry + Common::String subName; + char c; + while ((c = (char)f.readByte()) != '\0') + subName += c; + if (subName.empty()) + // Reached end of subroutine list + break; + + // Read in the offset of the routine + uint32 offset = f.readUint32LE(); + if (_subroutines.empty()) { + // The first subroutine offset is used to reduce the amount of data to later load in. In essence, + // the subroutine index will not be separately loaded, since it's contents will be in the hash map + subsStart = offset; + _scriptsSize -= offset; + } + + _subroutines[subName] = offset - subsStart; + _subroutineOffsets.push_back(offset - subsStart); + } + + // Read in the remaining data + f.seek(blockOffset + subsStart, SEEK_SET); + _scriptsData = (byte *)malloc(_scriptsSize); + f.read(_scriptsData, _scriptsSize); + + f.close(); +} void MadsSceneLogic::selectScene(int sceneNum) { assert(sceneNum == 101); _sceneNumber = sceneNum; Common::set_to(&_spriteIndexes[0], &_spriteIndexes[50], 0); + + // If debugging is turned on, show a debug warning if any of the scene methods aren't present + if (gDebugLevel > 0) { + for (int i = 0; i < 4; ++i) { + char buffer[20]; + sprintf(buffer, subFormatList[i], sceneNum); + Common::HashMap<Common::String, uint32>::iterator it = _subroutines.find(Common::String(buffer)); + if (it == _subroutines.end()) + debugC(1, kDebugScript, "Scene method %s not found", buffer); + } + } } void MadsSceneLogic::setupScene() { @@ -343,149 +440,492 @@ void MadsSceneLogic::setupScene() { getAnimName(); } -void MadsSceneLogic::enterScene() { - for (int i = 1; i <= 7; ++i) - _spriteIndexes[i - 1] = loadSpriteSet(i, 'x'); - _spriteIndexes[7] = loadSpriteSet(0xFFFF, 'm'); - _spriteIndexes[8] = loadSpriteSet(1, 'b'); - _spriteIndexes[9] = loadSpriteSet(2, 'b'); - _spriteIndexes[10] = loadSpriteSet(0, 'a'); - _spriteIndexes[11] = loadSpriteSet(1, 'a'); - _spriteIndexes[12] = loadSpriteSet(8, 'x'); - _spriteIndexes[13] = loadSpriteSet(0, 'x'); - - _spriteIndexes[15] = startCycledSpriteSequence(_spriteIndexes[0], false, 5, 0, 0, 25); - _spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], false, 4, 0, 1, 0); - _spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], false, 4, 0, 1, 0); - - _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70); - - _spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], false, 10, 0, 0, 60); - _spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], false, 5, 0, 1, 0); - _spriteIndexes[20] = startCycledSpriteSequence(_spriteIndexes[5], false, 10, 0, 2, 0); - _spriteIndexes[21] = startCycledSpriteSequence(_spriteIndexes[6], false, 6, 0, 0, 0); - _spriteIndexes[23] = startCycledSpriteSequence(_spriteIndexes[8], false, 6, 0, 10, 4); - _spriteIndexes[24] = startCycledSpriteSequence(_spriteIndexes[9], false, 6, 0, 32, 47); - - activateHotspot(0x137, false); // SHIELD MODULATOR - // shield_panel_opened = 0; - - if (_madsVm->globals()->previousScene != -1) - _madsVm->globals()->_globals[10] = 0; - if (_madsVm->globals()->previousScene != -2) { - _madsVm->_player._playerPos = Common::Point(100, 152); - } - - if ((_madsVm->globals()->previousScene == 112) || - ((_madsVm->globals()->previousScene != -2) && (_spriteIndexes[29] != 0))) { - // Returning from probe cutscene? - _spriteIndexes[29] = -1; - _madsVm->_player._playerPos = Common::Point(161, 123); - _madsVm->_player._direction = 9; - - // TODO: Extra flags setting - _spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], false, 3, 0, 0, 0); - _madsVm->scene()->_sequenceList.setAnimRange(_spriteIndexes[25], 17, 17); - activateHotspot(0x47, false); // CHAIR - /*timer_unk1 = */_madsVm->scene()->_dynamicHotspots.add(0x47, 0x13F /*SIT_IN*/, -1, - Common::Rect(159, 84, 159+33, 84+36)); +/** + * Handles the logic when a scene is entered + */ +void MadsSceneLogic::doEnterScene() { + char buffer[20]; + sprintf(buffer, subFormatList[SUBFORMAT_ENTER], _sceneNumber); + execute(Common::String(buffer)); +} + +/** + * Handles the script execution which is called regularly every frame + */ +void MadsSceneLogic::doSceneStep() { + char buffer[20]; + sprintf(buffer, subFormatList[SUBFORMAT_STEP], _sceneNumber); + execute(Common::String(buffer)); +} + +/** + * Handles and preactions before an action is started + */ +void MadsSceneLogic::doPreactions() { + char buffer[20]; + sprintf(buffer, subFormatList[SUBFORMAT_PREACTIONS], _sceneNumber); + execute(Common::String(buffer)); +} + +/** + * Handles any action that has been selected + */ +void MadsSceneLogic::doAction() { + char buffer[20]; + sprintf(buffer, subFormatList[SUBFORMAT_ACTIONS], _sceneNumber); + execute(Common::String(buffer)); +} + +/** + * Executes the script with the specified name + */ +void MadsSceneLogic::execute(const Common::String &scriptName) { + Common::HashMap<Common::String, uint32>::iterator it = _subroutines.find(scriptName); + if (it != _subroutines.end()) + execute(it->_value); +} + +#define UNUSED_VAL 0xEAEAEAEA +/** + * Executes the script at the specified offset + */ +void MadsSceneLogic::execute(uint32 subOffset) { + Common::Array<ScriptVar> locals; + Common::Stack<ScriptVar> stack; + char opcodeBuffer[100]; + uint32 scriptOffset = subOffset; + uint32 param; + + debugC(1, kDebugScript, "executing script at %xh", subOffset); + + bool done = false; + while (!done) { + param = UNUSED_VAL; + byte opcode = _scriptsData[scriptOffset++]; + sprintf(opcodeBuffer, "%.4x[%.2d] - %s", scriptOffset - 1, stack.size(), _opcodeStrings[opcode & OPMASK]); + + switch (opcode & OPMASK) { + case OP_HALT: // end of program + case OP_RET: + done = true; + break; + + case OP_IMM: // Loads immediate value onto stack + param = getParam(scriptOffset, opcode); + stack.push(ScriptVar(param)); + break; + + case OP_ZERO: // loads zero onto stack + stack.push(ScriptVar((uint32)0)); + break; + + case OP_ONE: // loads one onto stack + stack.push(ScriptVar(1)); + break; + + case OP_MINUSONE: // loads minus one (0xffff) onto stack + stack.push(ScriptVar(0xffff)); + break; + + case OP_DLOAD: { // Gets data variable + param = getParam(scriptOffset, opcode); + uint16 v = dataMap().get(param); + stack.push(ScriptVar(v)); + break; + } + + case OP_DSTORE: { // Stores data variable + param = getParam(scriptOffset, opcode); + ScriptVar v = stack.pop(); + dataMap().set(param, v.isInt() ? v : 0); + break; + } - //if (_madsVm->globals()->previousScene == 112) - // room101Check(); - } else { - _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], false, 6, 0, 0, 0); - _madsVm->scene()->_sequenceList.setDepth(_spriteIndexes[26], 4); - } + case OP_LOAD: // loads local variable onto stack + param = getParam(scriptOffset, opcode); + stack.push(locals[param]); + break; + + case OP_STORE: // Pops a value and stores it in a local + // Get the local index and expand the locals store if necessary + param = getParam(scriptOffset, opcode); + while (param >= locals.size()) + locals.push_back(ScriptVar()); + + locals[param] = stack.pop(); + break; + + case OP_GLOAD: // loads global variable onto stack + param = getParam(scriptOffset, opcode); + assert(param < TOTAL_NUM_VARIABLES); + stack.push(_madsVm->globals()->_globals[param]); + break; + + case OP_GSTORE: // pops stack and stores in global variable + param = getParam(scriptOffset, opcode); + assert(param < TOTAL_NUM_VARIABLES); + _madsVm->globals()->_globals[param] = stack.pop().get(); + break; + + case OP_CALL: // procedure call + param = getParam(scriptOffset, opcode); + assert(param < _subroutineOffsets.size()); + execute(_subroutineOffsets[param]); + break; + + case OP_LIBCALL: // library procedure or function call + param = getParam(scriptOffset, opcode); + callSubroutine(param, stack); + break; + + case OP_JUMP: // unconditional jump + param = subOffset + getParam(scriptOffset, opcode); + scriptOffset = param; + break; + + case OP_JMPFALSE: // conditional jump + param = subOffset + getParam(scriptOffset, opcode); + if (stack.pop().get() == 0) + // Condition satisfied - do the jump + scriptOffset = param; + break; + + case OP_JMPTRUE: // conditional jump + param = subOffset + getParam(scriptOffset, opcode); + if (stack.pop().get() != 0) + // Condition satisfied - do the jump + scriptOffset = param; + break; + + case OP_EQUAL: // tests top two items on stack for equality + case OP_LESS: // tests top two items on stack + case OP_LEQUAL: // tests top two items on stack + case OP_NEQUAL: // tests top two items on stack + case OP_GEQUAL: // tests top two items on stack + case OP_GREAT: // tests top two items on stack + case OP_LOR: // logical or of top two items on stack and replaces with result + case OP_LAND: // logical ands top two items on stack and replaces with result + { + uint32 param2 = stack.pop().get(); + uint32 param1 = stack.pop().get(); + + // Do the comparison + uint32 tmp = 0; + switch (opcode) { + case OP_EQUAL: tmp = (param1 == param2); break; + case OP_LESS: tmp = (param1 < param2); break; + case OP_LEQUAL: tmp = (param1 <= param2); break; + case OP_NEQUAL: tmp = (param1 != param2); break; + case OP_GEQUAL: tmp = (param1 >= param2); break; + case OP_GREAT: tmp = (param1 > param2); break; + + case OP_LOR: tmp = (param1 || param2); break; + case OP_LAND: tmp = (param1 && param2); break; + } + + stack.push(ScriptVar(tmp)); + } + break; - _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1); + case OP_PLUS: // adds top two items on stack and replaces with result + case OP_MINUS: // subs top two items on stack and replaces with result + case OP_MULT: // multiplies top two items on stack and replaces with result + case OP_DIV: // divides top two items on stack and replaces with result + case OP_MOD: // divides top two items on stack and replaces with modulus + case OP_AND: // bitwise ands top two items on stack and replaces with result + case OP_OR: // bitwise ors top two items on stack and replaces with result + case OP_EOR: // bitwise exclusive ors top two items on stack and replaces with result + { + uint32 param2 = stack.pop().get(); + uint32 param1 = stack.pop().get(); + + // replace other operand with result of operation + switch (opcode) { + case OP_PLUS: param1 += param2; break; + case OP_MINUS: param1 -= param2; break; + case OP_MULT: param1 *= param2; break; + case OP_DIV: param1 /= param2; break; + case OP_MOD: param1 %= param2; break; + case OP_AND: param1 &= param2; break; + case OP_OR: param1 |= param2; break; + case OP_EOR: param1 ^= param2; break; + } + + stack.push(ScriptVar(param1)); + } + break; + + case OP_NOT: // logical nots top item on stack + param = stack.pop().get(); + stack.push(ScriptVar((uint32)!param & 0xffff)); + break; + + case OP_COMP: // complements top item on stack + param = stack.pop().get(); + stack.push(ScriptVar(~param & 0xffff)); + break; + + case OP_NEG: // negates top item on stack + param = stack.pop().get(); + stack.push(ScriptVar(((uint32)-(int32)param) & 0xffff)); + break; + + case OP_DUP: // duplicates top item on stack + stack.push(stack.top()); + break; - if (_madsVm->globals()->_globals[10]) { - const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1); - _madsVm->scene()->loadAnimation(animName, 71); + default: + error("execute() - Unknown opcode"); + } - _madsVm->_player._playerPos = Common::Point(68, 140); - _madsVm->_player._direction = 4; - _madsVm->_player._visible = false; - _madsVm->_player._stepEnabled = false; + // check for stack size + assert(stack.size() < 100); - dataMap()[0x56FC] = 0; - dataMap()[0x5482] = 0; - dataMap()[0x5484] = 30; + if (gDebugLevel > 0) { + if (param != UNUSED_VAL) + sprintf(opcodeBuffer + strlen(opcodeBuffer), "\t%d", param); + debugC(2, kDebugScript, opcodeBuffer); + } } - _madsVm->globals()->_dataMap[0x5486] = 0; - lowRoomsEntrySound(); + debugC(1, kDebugScript, "finished executing script"); + + // make sure stack is unwound + assert(stack.size() == 0); } -void MadsSceneLogic::doPreactions() { - warning("Still to do preactions logic"); +uint32 MadsSceneLogic::getParam(uint32 &scriptOffset, int opcode) { + switch (opcode & (~OPMASK)) { + case OPSIZE8: + return _scriptsData[scriptOffset++]; + case OPSIZE16: { + uint16 v = READ_LE_UINT16(&_scriptsData[scriptOffset]); + scriptOffset += sizeof(uint16); + return v; + } + default: { + uint32 v = READ_LE_UINT32(&_scriptsData[scriptOffset]); + scriptOffset += sizeof(uint32); + return v; + } + } } -void MadsSceneLogic::doAction() { - warning("Still to do actions logic"); +/** + * Support method for extracting the required number of parameters needed for a library routine call + */ +void MadsSceneLogic::getCallParameters(int numParams, Common::Stack<ScriptVar> &stack, ScriptVar *callParams) { + assert(numParams <= MAX_CALL_PARAMS); + for (int i = 0; i < numParams; ++i, ++callParams) + *callParams = stack.pop(); } -void MadsSceneLogic::doSceneStep() { - // TODO: Sound handling - - switch (_madsVm->scene()->_abortTimers) { - case 70: - _madsVm->_sound->playSound(9); +#define EXTRACT_PARAMS(n) getCallParameters(n, stack, p) + +void MadsSceneLogic::callSubroutine(int subIndex, Common::Stack<ScriptVar> &stack) { + ScriptVar p[MAX_CALL_PARAMS]; + + switch (subIndex) { + case 1: { + // dialog_show + EXTRACT_PARAMS(1); + Dialog *dlg = new Dialog(_vm, p[0].getStr(), "TODO: Proper Title"); + _vm->_viewManager->addView(dlg); + _vm->_viewManager->moveToFront(dlg); + break; + } + + case 2: + // SequenceList_remove + EXTRACT_PARAMS(1); + _madsVm->scene()->_sequenceList.remove(p[0]); + break; + + case 3: + case 6: + case 20: { + // 3: start_reversible_sprite_sequence + // 6: start_cycled_sprite_sequence + // 20: start_sprite_sequence3 + EXTRACT_PARAMS(6); + int idx; + if (subIndex == 3) + idx = startReversibleSpriteSequence(p[0], p[1] != 0, p[2], p[3], p[4], p[5]); + else if (subIndex == 6) + idx = startCycledSpriteSequence(p[0], p[1], p[2], p[3], p[4], p[5]); + else + idx = startSpriteSequence3(p[0], p[1] != 0, p[2], p[3], p[4], p[5]); + stack.push(ScriptVar(idx)); break; - case 71: - _madsVm->globals()->_globals[10] = 0; - _madsVm->_player._visible = true; - _madsVm->_player._stepEnabled = true; + } - _madsVm->_player._priorTimer = _madsVm->_currentTimer - _madsVm->_player._ticksAmount; + case 4: + // SequenceList_setAnimRange + EXTRACT_PARAMS(3); + _madsVm->scene()->_sequenceList.setAnimRange(p[0], p[1], p[2]); break; - case 72: - case 73: - // TODO: Method that should be scripted + + case 5: + // SequenceList_addSubEntry + EXTRACT_PARAMS(4); + stack.push(ScriptVar(_madsVm->scene()->_sequenceList.addSubEntry(p[0], (SequenceSubEntryMode)p[1].get(), p[2], p[3]))); break; - default: + case 7: { + // quotes_get_pointer + EXTRACT_PARAMS(1); + const char *quoteStr = _madsVm->globals()->getQuote(p[0]); + stack.push(ScriptVar(quoteStr)); break; } - // Wake up message sequence - Animation *anim = _madsVm->scene()->activeAnimation(); - if (anim) { - if ((anim->getCurrentFrame() == 6) && (dataMap()[0x5482] == 0)) { - dataMap()[0x5482]++; - _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), - 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(49)); - dataMap()[0x5484] += 14; - } + case 8: { + // KernelMessageList_add + EXTRACT_PARAMS(8); + int msgIndex = _madsVm->scene()->_kernelMessages.add(Common::Point(p[0], p[1]), p[2], + p[3], p[4], p[5] | (p[6] << 16), p[7].getStr()); + stack.push(ScriptVar(msgIndex)); + break; + } - if ((anim->getCurrentFrame() == 7) && (dataMap()[0x5482] == 1)) { - dataMap()[0x5482]++; - _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), - 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(54)); - dataMap()[0x5484] += 14; - } + case 9: + // SequenceList_unk3 + EXTRACT_PARAMS(1); + // TODO: Implement unk3 method +// stack.push(ScriptVar(_madsVm->scene()->_sequenceList.unk3(p[0]))); + break; - if ((anim->getCurrentFrame() == 10) && (dataMap()[0x5482] == 2)) { - dataMap()[0x5482]++; - _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), - 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(55)); - dataMap()[0x5484] += 14; - } + case 10: + // start_sound + EXTRACT_PARAMS(1); + _madsVm->_sound->playSound(p[0]); + break; - if ((anim->getCurrentFrame() == 17) && (dataMap()[0x5482] == 3)) { - dataMap()[0x5482]++; - _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), - 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(56)); - dataMap()[0x5484] += 14; - } + case 11: + // SceneLogic_formAnimName + EXTRACT_PARAMS(2); + stack.push(ScriptVar(formAnimName((char)p[0], p[1]))); + break; + + case 12: + // SpriteList_addSprites + EXTRACT_PARAMS(2); + stack.push(ScriptVar(_madsVm->scene()->_spriteSlots.addSprites(p[0].getStr(), false, p[1]))); + break; + + case 13: + // hotspot_activate + EXTRACT_PARAMS(2); + // TODO: Implement setActive version that takes in a hotspot Id +// _madsVm->scene()->getSceneResources().hotspots->setActive(p[0], p[1] != 0); + break; + + case 14: { + // DynamicHotspots_add + EXTRACT_PARAMS(7); + int idx = _madsVm->scene()->_dynamicHotspots.add(p[0], p[1], p[2], + Common::Rect(p[6], p[5], p[6] + p[4], p[5] + p[3])); + stack.push(ScriptVar(idx)); + break; + } + + case 15: + // SequenceList_setDepth + EXTRACT_PARAMS(2); + _madsVm->scene()->_sequenceList.setDepth(p[0], p[1]); + break; - if ((anim->getCurrentFrame() == 20) && (dataMap()[0x5482] == 4)) { - dataMap()[0x5482]++; - _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), - 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(50)); - dataMap()[0x5484] += 14; + case 16: { + // quotes_load + // Quotes loading can take an arbitrary number of quote Ids, terminated by a 0 + int firstId = -1; + int quoteId; + while ((quoteId = stack.pop()) != 0) { + if (firstId == -1) + firstId = quoteId; + _madsVm->globals()->loadQuote(quoteId); } - } -} + if (firstId != -1) + stack.push(ScriptVar(_madsVm->globals()->getQuote(firstId))); + break; + } + + case 17: { + // form_resource_name + EXTRACT_PARAMS(4); + const char *suffix = NULL; + if (p[4].isInt()) { + // If integer provided for suffix, it must be a value of 0 (NULL) + uint32 vTemp = p[4] | stack.pop(); + assert(!vTemp); + } else + suffix = p[4].getStr(); + + stack.push(ScriptVar(MADSResourceManager::getResourceName((char)p[1], p[0], (ExtensionType)p[3].get(), + suffix, (int16)p[2]))); + break; + } + + case 18: + // MadsScene_loadAnimation + EXTRACT_PARAMS(3); + _madsVm->scene()->loadAnimation(p[1].getStr(), p[0]); + break; + + case 19: { + // Action_isAction + int verbId = stack.pop(); + int objectNameId = (verbId == 0) ? 0 : stack.pop(); + int indirectObjectId = (objectNameId == 0) ? 0 : stack.pop(); + + stack.push(ScriptVar(_madsVm->scene()->_action.isAction(verbId, objectNameId, indirectObjectId))); + break; + } + + case 21: + // DynamicHotspots_remove + EXTRACT_PARAMS(1); + _madsVm->scene()->_dynamicHotspots.remove(p[0]); + break; + + case 22: { + // object_is_present + EXTRACT_PARAMS(1); + const MadsObject *obj = _madsVm->globals()->getObject(p[0]); + stack.push(ScriptVar((obj->roomNumber == _madsVm->scene()->_currentScene))); + break; + } + + case 23: + // inventory_add + EXTRACT_PARAMS(1); + _madsVm->scene()->getInterface()->addObjectToInventory(p[0]); + break; + + case 24: { + // dialog_picture_show + EXTRACT_PARAMS(3); + int messageId = p[0] | (p[1] << 16); + int objectNum = p[2]; + warning("TODO: Implement dialog with picture. MessageId=%d, objectNum=%d", messageId, objectNum); + break; + } + + case 25: { + // object_is_in_inventory + EXTRACT_PARAMS(1); + const MadsObject *obj = _madsVm->globals()->getObject(p[0]); + stack.push(ScriptVar(obj->isInInventory())); + break; + } + + default: + error("Unknown subroutine %d called", subIndex); + break; + } } + +#undef EXTRACT_PARAMS + +}
\ No newline at end of file diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h index ec6eff368b..adafe6f93d 100644 --- a/engines/m4/mads_logic.h +++ b/engines/m4/mads_logic.h @@ -29,10 +29,38 @@ #ifndef M4_MADS_LOGIC_H #define M4_MADS_LOGIC_H +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "common/stack.h" #include "m4/mads_views.h" namespace M4 { +union ScriptVarValue { + const char *strValue; + uint32 intValue; +}; + +/** + * Specifies a script variable that either be a 32-bit unsigned integer or a string pointer + */ +class ScriptVar { +private: + ScriptVarValue _value; + bool _isInt; +public: + ScriptVar(uint32 v = 0) { _value.intValue = v; _isInt = true; } + ScriptVar(const char *s) { _value.strValue = s; _isInt = false; } + + void set(uint32 v) { _value.intValue = v; _isInt = true; } + void set(const char *s) { _value.strValue = s; _isInt = false; } + const char *getStr() const { assert(!_isInt); return _value.strValue; } + uint32 get() const { assert(_isInt); return _value.intValue; } + bool isInt() const { return _isInt; } + + operator int() { return get(); } +}; + class MadsSceneLogic { private: // Library interface methods @@ -41,27 +69,45 @@ private: uint16 startCycledSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); uint16 startSpriteSequence3(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); void activateHotspot(int idx, bool active); - void lowRoomsEntrySound(); void getPlayerSpritesPrefix(); void getPlayerSpritesPrefix2(); private: int _sceneNumber; int16 _spriteIndexes[50]; + byte *_scriptsData; + int _scriptsSize; + Common::HashMap<Common::String, uint32> _subroutines; + Common::Array<uint32> _subroutineOffsets; + + enum SubFormatIndex {SUBFORMAT_ENTER, SUBFORMAT_STEP, SUBFORMAT_PREACTIONS, SUBFORMAT_ACTIONS}; + static const char *subFormatList[]; + static const char *_opcodeStrings[]; // Support functions const char *formAnimName(char sepChar, int16 suffixNum); void getSceneSpriteSet(); void getAnimName(); - IntStorage &dataMap(); + DataMap &dataMap(); + void getCallParameters(int numParams, Common::Stack<ScriptVar> &stack, ScriptVar *callParams); public: + MadsSceneLogic() { _scriptsData = NULL; } + ~MadsSceneLogic() { delete _scriptsData; } + + void initialiseScripts(); + void initialiseDataMap(); void selectScene(int sceneNum); void setupScene(); - void enterScene(); + void doEnterScene(); void doPreactions(); void doAction(); void doSceneStep(); + + void execute(const Common::String &scriptName); + void execute(uint32 scriptOffset); + uint32 getParam(uint32 &scriptOffset, int opcode); + void callSubroutine(int subIndex, Common::Stack<ScriptVar> &stack); }; class MadsGameLogic { diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp index d44fa2a753..1ddbf89fed 100644 --- a/engines/m4/mads_scene.cpp +++ b/engines/m4/mads_scene.cpp @@ -68,6 +68,7 @@ MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResour _interfaceSurface = new MadsInterfaceView(vm); _showMousePos = false; _mouseMsgIndex = -1; + _previousScene = -1; } MadsScene::~MadsScene() { @@ -174,7 +175,7 @@ void MadsScene::loadScene(int sceneNumber) { // Do any scene specific setup if (_vm->getGameType() == GType_RexNebular) - _sceneLogic.enterScene(); + _sceneLogic.doEnterScene(); // Miscellaneous player setup _madsVm->_player._destPos = _madsVm->_player._destPos; diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h index 7723058cc7..609000eaa3 100644 --- a/engines/m4/mads_scene.h +++ b/engines/m4/mads_scene.h @@ -111,6 +111,10 @@ public: public: MadsScene(MadsEngine *vm); virtual ~MadsScene(); + void initialise() { + _sceneLogic.initialiseScripts(); + _sceneLogic.initialiseDataMap(); + } // Methods that differ between engines virtual void loadScene(int sceneNumber); diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp index 3e5f0c2ac9..58a9a99211 100644 --- a/engines/m4/mads_views.cpp +++ b/engines/m4/mads_views.cpp @@ -394,14 +394,20 @@ int MadsSpriteSlots::addSprites(const char *resName, bool suppressErrors, int fl return -1; } + // Append on a '.SS' suffix if the resource doesn't already have an extension + char buffer[100]; + strncpy(buffer, resName, 95); + if (!strchr(buffer, '.')) + strcat(buffer, ".SS"); + // Get the sprite set - Common::SeekableReadStream *data = _vm->res()->get(resName); - SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName, false, flags); + Common::SeekableReadStream *data = _vm->res()->get(buffer); + SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), buffer, false, flags); spriteSet->translate(_madsVm->_palette); assert(spriteSet != NULL); _sprites.push_back(spriteSet); - _vm->res()->toss(resName); + _vm->res()->toss(buffer); return _sprites.size() - 1; } diff --git a/engines/m4/scene.cpp b/engines/m4/scene.cpp index 8457f2087a..57d63c153a 100644 --- a/engines/m4/scene.cpp +++ b/engines/m4/scene.cpp @@ -51,6 +51,7 @@ Scene::Scene(MadsM4Engine *vm, SceneResources *res): View(vm, Common::Rect(0, 0, _interfacePal = NULL; _interfaceSurface = NULL; _vm->_rails->setCodeSurface(_walkSurface); + _currentScene = -1; } Scene::~Scene() { diff --git a/engines/m4/scene.h b/engines/m4/scene.h index 9262a7c828..e0b28e6454 100644 --- a/engines/m4/scene.h +++ b/engines/m4/scene.h @@ -77,9 +77,6 @@ class Scene : public View { private: HotSpotList _sceneHotspots; protected: - int _currentScene; - int _previousScene; - int _nextScene; GameInterfaceView *_interfaceSurface; M4Surface *_backgroundSurface; M4Surface *_walkSurface; @@ -87,6 +84,10 @@ protected: RGBList *_interfacePal; SceneResources *_sceneResources; public: + int _currentScene; + int _previousScene; + int _nextScene; +public: Scene(MadsM4Engine *vm, SceneResources *res); virtual ~Scene(); |