diff options
-rw-r--r-- | engines/lure/scripts.cpp | 280 | ||||
-rw-r--r-- | engines/lure/scripts.h | 16 |
2 files changed, 263 insertions, 33 deletions
diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp index 9ef29721a6..3fe5785340 100644 --- a/engines/lure/scripts.cpp +++ b/engines/lure/scripts.cpp @@ -23,6 +23,7 @@ #include "lure/scripts.h" #include "lure/res.h" #include "lure/game.h" +#include "lure/room.h" #include "common/stack.h" #include "common/endian.h" @@ -80,6 +81,18 @@ void Script::deactivateHotspotSet(uint16 listIndex, uint16 v2, uint16 v3) { } } +// Resets the pathfinder + +void Script::resetPathfinder(uint16 v1, uint16 v2, uint16 v3) { + // Reset the pathfinding for any characters + Resources &res = Resources::getReference(); + + for (uint16 charId = PLAYER_ID + 1; charId < FIRST_NONCHARACTER_ID; ++charId) { + Hotspot *charHotspot = res.getActiveHotspot(charId); + if (charHotspot) charHotspot->pathFinder().clear(); + } +} + // deactivates the specified hotspot from active animation void Script::deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) { @@ -95,22 +108,18 @@ void Script::deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) { // Sets the offset for the table of action sequence offsets for the given // hotspot -void Script::setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3) { +void Script::setDesc(uint16 hotspotId, uint16 descId, uint16 v3) { Resources &res = Resources::getReference(); HotspotData *hotspot = res.getHotspot(hotspotId); - - if (!res.getHotspotActions(offset)) - warning("Hotspot %xh set to invalid actions offset %d", - hotspotId, offset); - - hotspot->actionsOffset = offset; + assert(hotspot); + hotspot->descId = descId; } // Add a sequence to be executed after a specified delay -void Script::addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3) { +void Script::addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 canClear) { SequenceDelayList &list = Resources::getReference().delayList(); - list.addSequence(delay, seqOffset); + list.add(delay, seqOffset, canClear != 0); } // Checks whether the given character is in the specified room, and stores @@ -157,14 +166,17 @@ void Script::remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3) { player->roomNumber()); } -// Checks the status of the cell door, and starts music depending on it's state +// Starts a character speaking out loud to themselves (ie. no destination character) -void Script::checkCellDoor(uint16 v1, uint16 v2, uint16 v3) { - // In the original game, this method checks to see if the cell door - // is currently open, if it is, starts a music sequence. - // TODO: Implement starting music if cell door is open +void Script::startSpeakingToNoone(uint16 characterId, uint16 stringId, uint16 v3) { + Resources &res = Resources::getReference(); + Hotspot *charHotspot = res.getActiveHotspot(characterId); + assert(charHotspot); + + charHotspot->converse(NOONE_ID, stringId, false); } + void Script::playMusic(uint16 musicNum, uint16 v2, uint16 v3) { // TODO: Play a given music warning("TODO: Play music #%d", musicNum); @@ -208,11 +220,25 @@ void Script::decrInventoryItems(uint16 v1, uint16 v2, uint16 v3) { // inventory items, so don't do anything } -// Sets the current frame number for the given hotspot +void Script::setTalking(uint16 characterId, uint16 destHotspot, uint16 messageId) { + Hotspot *hotspot = Resources::getReference().getActiveHotspot(characterId); + if (hotspot) + hotspot->converse(destHotspot, messageId, false); +} + +// Sets the current action ctr value for the given hotspot + +void Script::setActionCtr(uint16 hotspotId, uint16 value, uint16 v3) { + HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId); + assert(hotspot); + hotspot->actionCtr = value; +} + +// Starts the designated character speaking the given message to a given dest hotspot -void Script::setFrameNumber(uint16 hotspotId, uint16 frameNumber, uint16 v3) { - Hotspot *hotspot = Resources::getReference().getActiveHotspot(hotspotId); - hotspot->setFrameNumber(frameNumber); +void Script::startSpeaking(uint16 characterId, uint16 destHotspot, uint16 messageId) { + Hotspot *hotspot = Resources::getReference().getActiveHotspot(characterId); + hotspot->converse(destHotspot, messageId, false); } // Disables the given hotspot from being highlighted by the cursor @@ -371,6 +397,29 @@ void Script::addActions(uint16 hotspotId, uint16 actions, uint16 v3) { hotspot->actions |= actions; } +// Generates a random number and stores it in the general field + +void Script::randomToGeneral(uint16 maxVal, uint16 minVal, uint16 v3) { + Common::RandomSource rnd; + uint16 v = minVal + rnd.getRandomNumber(maxVal - minVal); + Resources::getReference().fieldList().setField(GENERAL, v); +} + +// Checks the status of the cell door, and starts music depending on it's state + +void Script::checkCellDoor(uint16 v1, uint16 v2, uint16 v3) { + // In the original game, this method checks to see if the cell door + // is currently open, if it is, starts a music sequence. + // TODO: Implement starting music if cell door is open +} + +// Has something to do with music handling + +void Script::method66(uint16 hotspotId, uint16 actions, uint16 v3) { + // For now, simply set the general value field so that the Skorl schedule + // will work properly + Resources::getReference().fieldList().setField(GENERAL, 0); +} typedef void(*SequenceMethodPtr)(uint16, uint16, uint16); @@ -385,20 +434,24 @@ SequenceMethodRecord scriptMethods[] = { {4, Script::clearSequenceDelayList}, {5, Script::deactivateHotspotSet}, {6, Script::deactivateHotspot}, + {7, Script::resetPathfinder}, {8, Script::addDelayedSequence}, {10, Script::characterInRoom}, - {11, Script::setActionsOffset}, + {11, Script::setDesc}, {12, Script::setHotspotName}, {13, Script::playSound}, {16, Script::displayDialog}, {18, Script::remoteRoomViewSetup}, + {19, Script::startSpeakingToNoone}, {20, Script::checkCellDoor}, {21, Script::playMusic}, {22, Script::getDoorBlocked}, {23, Script::isSkorlInCell}, {27, Script::setBlockingHotspotScript}, {28, Script::decrInventoryItems}, - {30, Script::setFrameNumber}, + {29, Script::setTalking}, + {30, Script::setActionCtr}, + {31, Script::startSpeaking}, {32, Script::disableHotspot}, {33, Script::cutSack}, {34, Script::increaseNumGroats}, @@ -417,9 +470,46 @@ SequenceMethodRecord scriptMethods[] = { {57, Script::getNumGroats}, {62, Script::animationLoad}, {63, Script::addActions}, + {64, Script::randomToGeneral}, {65, Script::checkCellDoor}, + {66, Script::method66}, {0xff, NULL}}; + +const char *scriptOpcodes[] = { + "ABORT", "ADD", "SUBTRACT", "MULTIPLY", "DIVIDE", "NOT_EQUALS", "EQUALS", + "GT", "LT", "LT2", "GT2", "AND", "OR", "LOGICAL_AND", "LOGICAL_OR", + "GET_FIELD", "SET_FIELD", "PUSH", "SUBROUTINE", "EXEC", "END", + "COND_JUMP", "JUMP", "ABORT2", "ABORT3", "RANDOM" +}; + +const char *scriptMethodNames[67] = { + "ACTIVATE HOTSPOT", "SET HOTSPOT SCRIPT", NULL, NULL, "CLEAR SEQUENCE DELAY LIST", + "DEACTIVATE HOTSPOT SET", "DEACTIVATE HOTSPOT", "RESET PATHFINDER", + "ADD DELAYED SCRIPT", NULL, + + "IS CHARACTER IN ROOM", "SET HOTSPOT DESC", "SET HOTSPOT NAME", + "PLAY SOUND", NULL, NULL, "DISPLAY DIALOG", NULL, "REMOTE ROOM VIEW SETUP", + "SET CHAR SPEAKING TO ITSELF", + + "CHECK CELL DOOR", "PLAY MUSIC", "IS DOOR BLOCKED", "IS SKORL IN CELL", + NULL, NULL, NULL, "SET BLOCKING HOTSPOT SCRIPT", "DECREMENT # INVENTORY ITEMS", + "SET TALKING", + + "SET ACTION CTR", "START SPEAKING", "DISABLE HOTSPOT", "CUT SACK", + "INCREASE # GROATS", "ENABLE HOTSPOT", NULL, "TRANSFORM PLAYER", + NULL, "ROOM 14 CLOSE DOOR", + + "CHECK DROPPED DESC", NULL, "CLOSE DOOR", NULL, "OPEN DOOR", NULL, NULL, + "DISPLAY MESSAGE", "SET NEW ACTION SUPPORT DATA", "SET ACTION SUPPORT DATA", + "GIVE PLAYER ITEM", "DECREASE # GROATS", NULL, NULL, + "SET VILLAGE SKORL TICK PROC", NULL, NULL, "GET # GROATS", NULL, NULL, + + NULL, "KILL PLAYER", "ANIMATION LOAD", "ADD ACTIONS", "RANDOM TO GENERAL", + "CHECK CELL DOOR", "METHOD 66" +}; + + /*------------------------------------------------------------------------*/ /*- Script Execution -*/ /*- -*/ @@ -433,32 +523,82 @@ uint16 Script::execute(uint16 startOffset) { Common::Stack<uint16> stack; Common::Stack<uint16> methodStack; byte opcode; + bool hasParam; uint16 param, v1, v2; uint16 param1, param2, param3; uint16 fieldNum; uint32 tempVal; SequenceMethodPtr ptr; SequenceMethodRecord *rec; + char debugInfo[MAX_DESC_SIZE]; uint16 offset = startOffset; bool breakFlag = false; + param = 0; fields.setField(SEQUENCE_RESULT, 0); + debugC(ERROR_BASIC, kLureDebugScripts, "Executing script %xh", startOffset); + strcpy(debugInfo, ""); + while (!breakFlag) { if (offset >= scriptData->size()) error("Script failure in script %d - invalid offset %d", startOffset, offset); + if (gDebugLevel >= ERROR_DETAILED) + sprintf(debugInfo, "%xh - ", offset); + + // Get opcode byte and separate into opcode and has parameter bit flag opcode = scripts[offset++]; - if ((opcode & 1) != 0) { + hasParam = (opcode & 1) != 0; + opcode >>= 1; + + if (gDebugLevel >= ERROR_DETAILED) + strcat(debugInfo, (opcode > S_OPCODE_RANDOM) ? "INVALID" : + scriptOpcodes[opcode]); + + if (hasParam) { // Flag to read next two bytes as active parameter if (offset >= scriptData->size()-2) error("Script failure in script %d - invalid offset %d", startOffset, offset); param = READ_LE_UINT16(scripts + offset); offset += 2; + + if (gDebugLevel >= ERROR_DETAILED) + sprintf(debugInfo + strlen(debugInfo), " [%d]", + ((opcode == S_OPCODE_GET_FIELD) || (opcode == S_OPCODE_SET_FIELD)) ? + param >> 1 : param); + } + + if (gDebugLevel >= ERROR_DETAILED) { + switch (opcode) { + case S_OPCODE_ADD: + case S_OPCODE_SUBTRACT: + case S_OPCODE_MULTIPLY: + case S_OPCODE_DIVIDE: + case S_OPCODE_NOT_EQUALS: + case S_OPCODE_EQUALS: + case S_OPCODE_GT: + case S_OPCODE_LT: + case S_OPCODE_LT2: + case S_OPCODE_GT2: + case S_OPCODE_AND: + case S_OPCODE_OR: + case S_OPCODE_LOGICAL_AND: + case S_OPCODE_LOGICAL_OR: + sprintf(debugInfo + strlen(debugInfo), + " %d, %d", stack[stack.size() - 1], stack[stack.size() - 2]); + break; + + case S_OPCODE_SET_FIELD: + sprintf(debugInfo + strlen(debugInfo), " <= ST (%d)", stack[stack.size() - 1]); + break; + + default: + break; + } } - opcode >>= 1; // Discard param bit from opcode byte switch (opcode) { case S_OPCODE_ABORT: @@ -554,15 +694,39 @@ uint16 Script::execute(uint16 startOffset) { break; case S_OPCODE_EXEC: + // Search for the method in the method list + rec = &scriptMethods[0]; + while ((rec->methodIndex != 0xff) && (rec->methodIndex != param)) + ++rec; + + if (gDebugLevel >= ERROR_DETAILED) { + // Set up the debug string for the method call + if (rec->methodIndex == 0xff) strcat(debugInfo, " INVALID INDEX"); + else if (scriptMethodNames[param] == NULL) strcat(debugInfo, " UNKNOWN METHOD"); + else { + strcat(debugInfo, " "); + strcat(debugInfo, scriptMethodNames[param]); + } + + // Any params + if (stack.size() >= 3) + sprintf(debugInfo + strlen(debugInfo), " (%d,%d,%d)", + stack[stack.size()-1], stack[stack.size()-2], stack[stack.size()-3]); + if (stack.size() == 2) + sprintf(debugInfo + strlen(debugInfo), " (%d,%d)", + stack[stack.size()-1], stack[stack.size()-2]); + else if (stack.size() == 1) + sprintf(debugInfo + strlen(debugInfo), " (%d)", stack[stack.size()-1]); + strcat(debugInfo, ")"); + + debugC(ERROR_DETAILED, kLureDebugScripts, debugInfo); + } + param1 = 0; param2 = 0; param3 = 0; if (!stack.empty()) param1 = stack.pop(); if (!stack.empty()) param2 = stack.pop(); if (!stack.empty()) param3 = stack.pop(); - rec = &scriptMethods[0]; - while ((rec->methodIndex != 0xff) && (rec->methodIndex != param)) - ++rec; - if (rec->methodIndex == 0xff) warning("Undefined script method %d", param); else { @@ -596,9 +760,42 @@ uint16 Script::execute(uint16 startOffset) { error("Unknown script opcode %d", opcode); break; } + + if ((gDebugLevel >= ERROR_DETAILED) && (opcode != S_OPCODE_EXEC)) { + switch (opcode) { + case S_OPCODE_ADD: + case S_OPCODE_SUBTRACT: + case S_OPCODE_MULTIPLY: + case S_OPCODE_DIVIDE: + case S_OPCODE_NOT_EQUALS: + case S_OPCODE_EQUALS: + case S_OPCODE_GT: + case S_OPCODE_LT: + case S_OPCODE_LT2: + case S_OPCODE_GT2: + case S_OPCODE_AND: + case S_OPCODE_OR: + case S_OPCODE_LOGICAL_AND: + case S_OPCODE_LOGICAL_OR: + case S_OPCODE_GET_FIELD: + sprintf(debugInfo + strlen(debugInfo), " => ST (%d)", stack[stack.size()-1]); + break; + + case S_OPCODE_PUSH: + strcat(debugInfo, " => ST"); + break; + + default: + break; + } + + debugC(ERROR_DETAILED, kLureDebugScripts, debugInfo); + } } - return fields.getField(SEQUENCE_RESULT); + uint16 result = fields.getField(SEQUENCE_RESULT); + debugC(ERROR_DETAILED, kLureDebugScripts, "Script result = %d", result); + return result; } /*------------------------------------------------------------------------*/ @@ -621,14 +818,20 @@ bool HotspotScript::execute(Hotspot *h) uint16 offset = h->script(); int16 opcode = 0; int16 param1, param2; + uint32 actions; bool breakFlag = false; + debugC(ERROR_BASIC, kLureDebugScripts, "Executing hotspot %xh script pos=%xh", + h->hotspotId(), offset); + while (!breakFlag) { opcode = nextVal(scriptData, offset); switch (opcode) { case S2_OPCODE_TIMEOUT: param1 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "SET TIMEOUT = %d", param1); + h->setTickCtr(param1); h->setScript(offset); breakFlag = true; @@ -637,53 +840,74 @@ bool HotspotScript::execute(Hotspot *h) case S2_OPCODE_POSITION: param1 = nextVal(scriptData, offset); param2 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "SET POSITION = (%d,%d)", + param1 - 0x80, param2 - 0x80); + h->setPosition(param1 - 0x80, param2 - 0x80); break; case S2_OPCODE_CHANGE_POS: param1 = nextVal(scriptData, offset); param2 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "CHANGE POSITION BY = (%d,%d)", + param1, param2); + h->setPosition(h->x() + param1, h->y() + param2); break; case S2_OPCODE_UNLOAD: + debugC(ERROR_DETAILED, kLureDebugScripts, "UNLOAD HOTSPOT"); breakFlag = true; break; case S2_OPCODE_DIMENSIONS: param1 = nextVal(scriptData, offset) << 4; param2 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "SET SIZE = (%d,%d)", + param1, param2); + h->setSize((uint16) param1, (uint16) param2); break; case S2_OPCODE_JUMP: offset = (uint16) nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "JUMP OFFSET = %xh", offset); break; case S2_OPCODE_ANIMATION: param1 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "SET ANIMATION ID = %xh", param1); + h->setAnimation(param1); break; case S2_OPCODE_UNKNOWN_247: param1 = nextVal(scriptData, offset); param2 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "SUB_247(%d,%d)", param1, param2); + // warning("UNKNOWN_247 stub called"); break; case S2_OPCODE_UNKNOWN_258: param1 = nextVal(scriptData, offset); + debugC(ERROR_DETAILED, kLureDebugScripts, "SUB_258()"); // warning("UNKNOWN_258 stub called"); break; case S2_OPCODE_ACTIONS: param1 = nextVal(scriptData, offset) << 4; param2 = nextVal(scriptData, offset); - h->setActions((uint32) param1 | ((uint32) param2 << 16)); + actions = (uint32) param1 | ((uint32) param2 << 16); + + debugC(ERROR_DETAILED, kLureDebugScripts, "SET ACTIONS = %xh", actions); + h->setActions(actions); break; default: // Set the animation frame number + debugC(ERROR_DETAILED, kLureDebugScripts, "SET FRAME NUMBER = %d", opcode); + h->setFrameNumber(opcode); h->setScript(offset); breakFlag = true; diff --git a/engines/lure/scripts.h b/engines/lure/scripts.h index 2f82401b3c..f6c04bdc3c 100644 --- a/engines/lure/scripts.h +++ b/engines/lure/scripts.h @@ -76,24 +76,27 @@ public: static void activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3); static void setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3); + static void method2(uint16 v1, uint16 v2, uint16 v3); static void clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3); static void deactivateHotspotSet(uint16 listIndex, uint16 v2, uint16 v3); - static void method2(uint16 v1, uint16 v2, uint16 v3); static void deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3); - static void setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3); - static void addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3); + static void resetPathfinder(uint16 v1, uint16 v2, uint16 v3); + static void addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 canClear); static void characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3); + static void setDesc(uint16 hotspotId, uint16 descId, uint16 v3); static void setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3); static void playSound(uint16 v1, uint16 v2, uint16 v3); static void displayDialog(uint16 stringId, uint16 v2, uint16 v3); static void remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3); - static void checkCellDoor(uint16 v1, uint16 v2, uint16 v3); + static void startSpeakingToNoone(uint16 characterId, uint16 stringId, uint16 v3); static void playMusic(uint16 musicNum, uint16 v2, uint16 v3); static void getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3); static void isSkorlInCell(uint16 v1, uint16 v2, uint16 v3); static void setBlockingHotspotScript(uint16 charId, uint16 scriptIndex, uint16 v3); static void decrInventoryItems(uint16 v1, uint16 v2, uint16 v3); - static void setFrameNumber(uint16 hotspotId, uint16 offset, uint16 v3); + static void setTalking(uint16 characterId, uint16 destHotspot, uint16 messageId); + static void setActionCtr(uint16 hotspotId, uint16 value, uint16 v3); + static void startSpeaking(uint16 characterId, uint16 destHotspot, uint16 messageId); static void disableHotspot(uint16 hotspotId, uint16 v2, uint16 v3); static void cutSack(uint16 hotspotId, uint16 v2, uint16 v3); static void increaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3); @@ -112,6 +115,9 @@ public: static void getNumGroats(uint16 v1, uint16 v2, uint16 v3); static void animationLoad(uint16 hotspotId, uint16 v2, uint16 v3); static void addActions(uint16 hotspotId, uint16 actions, uint16 v3); + static void randomToGeneral(uint16 maxVal, uint16 minVal, uint16 v3); + static void checkCellDoor(uint16 v1, uint16 v2, uint16 v3); + static void method66(uint16 v1, uint16 v2, uint16 v3); }; class HotspotScript { |