aboutsummaryrefslogtreecommitdiff
path: root/engines/lure/scripts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/lure/scripts.cpp')
-rw-r--r--engines/lure/scripts.cpp576
1 files changed, 576 insertions, 0 deletions
diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp
new file mode 100644
index 0000000000..073753a93f
--- /dev/null
+++ b/engines/lure/scripts.cpp
@@ -0,0 +1,576 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/scripts.h"
+#include "lure/res.h"
+#include "lure/game.h"
+#include "common/stack.h"
+
+namespace Lure {
+
+/*------------------------------------------------------------------------*/
+/*- Script Method List -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+// activateHotspot
+// Activates a hotspot entry for active use
+
+void Script::activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources::getReference().activateHotspot(hotspotId);
+}
+
+// setHotspotScript
+// Sets a hotspot's animation script offset from a master table of offsets
+
+void Script::setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3) {
+ Resources &r = Resources::getReference();
+ uint16 offset = r.getHotspotScript(scriptIndex);
+ HotspotData *rsc = r.getHotspot(hotspotId);
+ rsc->sequenceOffset = offset;
+}
+
+// Clears the sequence delay list
+
+void Script::clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3) {
+ Resources::getReference().delayList().clear();
+}
+
+// deactivates the specified hotspot from active animation
+
+void Script::deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &rsc = Resources::getReference();
+ if (hotspotId < START_NONVISUAL_HOTSPOT_ID)
+ rsc.deactivateHotspot(hotspotId);
+ HotspotData *hs = rsc.getHotspot(hotspotId);
+ hs->flags |= 0x20;
+ if (hotspotId < START_NONVISUAL_HOTSPOT_ID)
+ hs->layer = 0xff;
+}
+
+// Sets the offset for the table of action sequence offsets for the given
+// hotspot
+
+void Script::setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3) {
+ Resources &res = Resources::getReference();
+ HotspotData *hotspot = res.getHotspot(hotspotId);
+
+ if (!res.getHotspotActions(offset))
+ warning("Hotspot %d set to invalid actions offset %d",
+ hotspotId, offset);
+
+ hotspot->actionsOffset = offset;
+}
+
+// Add a sequence to be executed after a specified delay
+
+void Script::addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3) {
+ SequenceDelayList &list = Resources::getReference().delayList();
+ list.addSequence(delay, seqOffset);
+}
+
+// Checks whether the given character is in the specified room, and stores
+// the result in the general value field
+
+void Script::characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3) {
+ Resources &res = Resources::getReference();
+ uint16 result = 0;
+ if (characterId >= PLAYER_ID) {
+ HotspotData *hotspot = res.getHotspot(characterId);
+ if (hotspot->roomNumber == roomNumber)
+ result = 1;
+ }
+
+ res.fieldList().setField(GENERAL, result);
+}
+
+// Changes the given hotspot's name to a new name
+
+void Script::setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->nameId = nameId;
+}
+
+// Displays the given string resource Id in a dialog
+
+void Script::displayDialog(uint16 stringId, uint16 v2, uint16 v3) {
+ Dialog::show(stringId);
+}
+
+// Flags for remotely viewing a room
+
+void Script::remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3) {
+ Hotspot *player = Resources::getReference().getActiveHotspot(PLAYER_ID);
+ player->setTickProc(0); // disable player actions
+ Game::getReference().setRemoteView();
+}
+
+// Gets the current blocked state for the given door and stores it in the
+// general value field
+
+void Script::getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec = res.getExitJoin(hotspotId);
+ res.fieldList().setField(GENERAL, joinRec->blocked);
+}
+
+// Decrements the number of inventory itemst he player has
+
+void Script::decrInventoryItems(uint16 v1, uint16 v2, uint16 v3) {
+ // module currently doesn't use a static counter for the number of
+ // inventory items, so don't do anything
+}
+
+// Sets the current frame number for the given hotspot
+
+void Script::setFrameNumber(uint16 hotspotId, uint16 frameNumber, uint16 v3) {
+ Hotspot *hotspot = Resources::getReference().getActiveHotspot(hotspotId);
+ hotspot->setFrameNumber(frameNumber);
+}
+
+// Disables the given hotspot from being highlighted by the cursor
+
+void Script::disableHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->flags |= 0x20;
+}
+
+// Increase the player's number by the specified amount
+
+void Script::increaseNumGroats(uint16 v1, uint16 numGroats, uint16 v3) {
+ ValueTableData &fields = Resources::getReference().fieldList();
+ fields.numGroats() += numGroats;
+}
+
+// Enables the flags for the given hotspot for it to be actively highlighted
+
+void Script::enableHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ // Clear flag 0x20 and add flag 0x80
+ hotspot->flags = (hotspot->flags & 0xdf) | 0x80;
+}
+
+// Marks the door in room 14 for closing
+
+void Script::room14DoorClose(uint16 v1, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(0x2719);
+ joinRec->blocked = 1;
+}
+
+// Sets the sequence result to 1 if the given secondary description for a
+// hotspot is empty (for inventory items, this gives the description before
+// the item is initially picked up)
+
+void Script::checkDroppedDesc(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &res = Resources::getReference();
+ HotspotData *hotspot = res.getHotspot(hotspotId);
+ uint16 seqResult = (hotspot->descId2 == 0) ? 1 : 0;
+ res.fieldList().setField(SEQUENCE_RESULT, seqResult);
+}
+
+// Marks the given door hotspot for closing
+
+void Script::doorClose(uint16 hotspotId, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(hotspotId);
+ if (!joinRec) error("Tried to close a non-door");
+ joinRec->blocked = 1;
+}
+
+// Marks the given door hotspot for opening
+
+void Script::doorOpen(uint16 hotspotId, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(hotspotId);
+ if (!joinRec) error("Tried to close a non-door");
+ joinRec->blocked = 0;
+}
+
+// Lookup the given message Id for the specified character and display in a dialog
+
+void Script::displayMessage(uint16 messageId, uint16 characterId, uint16 unknownVal) {
+ Dialog::showMessage(messageId, characterId);
+}
+
+// Assign the given hotspot item to the player's inventory
+
+void Script::givePlayerItem(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->roomNumber = PLAYER_ID;
+ hotspot->flags |= 0x80;
+}
+
+// Decrease the number of graots the player has
+
+void Script::decreaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3) {
+ ValueTableData &fields = Resources::getReference().fieldList();
+ fields.numGroats() -= numGroats;
+}
+
+// Sets the tick handler for the village Skorl to an alternate handler
+
+void Script::setVillageSkorlTickProc(uint16 v1, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(0x3F1);
+ hotspot->tickProcOffset = 0x7efa;
+}
+
+// Stores the current number of groats in the general field
+
+void Script::getNumGroats(uint16 v1, uint16 v2, uint16 v3) {
+ ValueTableData fields = Resources::getReference().fieldList();
+ fields.setField(GENERAL, fields.numGroats());
+}
+
+// Loads the specified animation, completely bypassing the standard process
+// of checking for a load proc/sequence
+
+void Script::animationLoad(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources::getReference().addHotspot(hotspotId);
+}
+
+// Adds the passed actions to the available actions for the given hotspot
+
+void Script::addActions(uint16 hotspotId, uint16 actions, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->actions |= actions;
+}
+
+// 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. I'll
+ // implement this method properly when I get around to implementing
+ // the in-game music
+}
+
+typedef void(*SequenceMethodPtr)(uint16, uint16, uint16);
+
+struct SequenceMethodRecord {
+ uint8 methodIndex;
+ SequenceMethodPtr proc;
+};
+
+SequenceMethodRecord scriptMethods[] = {
+ {0, Script::activateHotspot},
+ {1, Script::setHotspotScript},
+ {4, Script::clearSequenceDelayList},
+ {6, Script::deactivateHotspot},
+ {8, Script::addDelayedSequence},
+ {10, Script::characterInRoom},
+ {11, Script::setActionsOffset},
+ {12, Script::setHotspotName},
+ {16, Script::displayDialog},
+ {18, Script::remoteRoomViewSetup},
+ {20, Script::checkCellDoor},
+ {22, Script::getDoorBlocked},
+ {28, Script::decrInventoryItems},
+ {30, Script::setFrameNumber},
+ {32, Script::disableHotspot},
+ {34, Script::increaseNumGroats},
+ {35, Script::enableHotspot},
+ {39, Script::room14DoorClose},
+ {40, Script::checkDroppedDesc},
+ {42, Script::doorClose},
+ {44, Script::doorOpen},
+ {47, Script::displayMessage},
+ {50, Script::givePlayerItem},
+ {51, Script::decreaseNumGroats},
+ {54, Script::setVillageSkorlTickProc},
+ {57, Script::getNumGroats},
+ {62, Script::animationLoad},
+ {63, Script::addActions},
+ {65, Script::checkCellDoor},
+ {0xff, NULL}};
+
+/*------------------------------------------------------------------------*/
+/*- Script Execution -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+uint16 Script::execute(uint16 startOffset) {
+ Resources &r = Resources::getReference();
+ ValueTableData &fields = r.fieldList();
+ MemoryBlock *scriptData = r.scriptData();
+ byte *scripts = scriptData->data();
+ Common::Stack<uint16> stack;
+ Common::Stack<uint16> methodStack;
+ byte opcode;
+ uint16 param, v1, v2;
+ uint16 param1, param2, param3;
+ uint16 fieldNum;
+ uint32 tempVal;
+ SequenceMethodPtr ptr;
+ SequenceMethodRecord *rec;
+
+ uint16 offset = startOffset;
+ bool breakFlag = false;
+ param = 0;
+ fields.setField(SEQUENCE_RESULT, 0);
+
+ while (!breakFlag) {
+ if (offset >= scriptData->size())
+ error("Script failure in script %d - invalid offset %d", startOffset, offset);
+
+ opcode = scripts[offset++];
+ if ((opcode & 1) != 0) {
+ // 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;
+ }
+ opcode >>= 1; // Discard param bit from opcode byte
+
+ switch (opcode) {
+ case S_OPCODE_ABORT:
+ case S_OPCODE_ABORT2:
+ case S_OPCODE_ABORT3:
+ methodStack.clear();
+ break;
+
+ case S_OPCODE_ADD:
+ stack.push(stack.pop() + stack.pop());
+ break;
+
+ case S_OPCODE_SUBTRACT:
+ v1 = stack.pop();
+ v2 = stack.pop();
+ stack.push(v2 - v1);
+ break;
+
+ case S_OPCODE_MULTIPLY:
+ tempVal = stack.pop() * stack.pop();
+ stack.push(tempVal & 0xffff);
+ param = (uint16) (tempVal >> 16);
+ break;
+
+ case S_OPCODE_DIVIDE:
+ v1 = stack.pop();
+ v2 = stack.pop();
+ stack.push(v2 / v1);
+ param = v2 % v1; // remainder
+ break;
+
+ case S_OPCODE_NOT_EQUALS:
+ stack.push((stack.pop() != stack.pop()) ? 0 : 1);
+ break;
+
+ case S_OPCODE_EQUALS:
+ stack.push((stack.pop() == stack.pop()) ? 0 : 1);
+ break;
+
+ case S_OPCODE_GT:
+ stack.push((stack.pop() > stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LT:
+ stack.push((stack.pop() < stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LT2:
+ stack.push((stack.pop() < stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_GT2:
+ stack.push((stack.pop() > stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_AND:
+ stack.push(stack.pop() & stack.pop());
+ break;
+
+ case S_OPCODE_OR:
+ stack.push(stack.pop() | stack.pop());
+ break;
+
+ case S_OPCODE_LOGICAL_AND:
+ stack.push(((stack.pop() != 0) && (stack.pop() != 0)) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LOGICAL_OR:
+ stack.push(((stack.pop() != 0) || (stack.pop() != 0)) ? 1 : 0);
+ break;
+
+ case S_OPCODE_GET_FIELD:
+ // Opcode not yet fully implemented
+ fieldNum = param >> 1;
+ v1 = fields.getField(fieldNum);
+ stack.push(v1);
+ break;
+
+ case S_OPCODE_SET_FIELD:
+ // Opcode not yet fully implemented
+ fieldNum = param >> 1;
+ v1 = stack.pop();
+ fields.setField(fieldNum, v1);
+ break;
+
+ case S_OPCODE_PUSH:
+ stack.push(param);
+ break;
+
+ case S_OPCODE_SUBROUTINE:
+ methodStack.push(offset);
+ offset = param;
+ break;
+
+ case S_OPCODE_EXEC:
+ 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 {
+ ptr = rec->proc;
+ ptr(param1, param2, param3);
+ }
+ break;
+
+ case S_OPCODE_COND_JUMP:
+ v1 = stack.pop();
+ if (v1 == 0) offset += (int16) param;
+ break;
+
+ case S_OPCODE_JUMP:
+ offset += (int16) param;
+ break;
+
+ case S_OPCODE_RANDOM:
+ param = r.random() >> 8; // make number between 0 to 255
+ break;
+
+ case S_OPCODE_END:
+ // Signal to end the execution
+ if (!methodStack.empty())
+ offset = methodStack.pop();
+ else
+ breakFlag = true;
+ break;
+
+ default:
+ error("Unknown script opcode %d", opcode);
+ break;
+ }
+ }
+
+ return fields.getField(SEQUENCE_RESULT);
+}
+
+/*------------------------------------------------------------------------*/
+/*- Hotspot Script Handler -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+int16 HotspotScript::nextVal(MemoryBlock *data, uint16 &offset) {
+ if (offset >= data->size() - 1)
+ error("Script failure - invalid offset");
+ int16 value = READ_LE_UINT16(data->data() + offset);
+ offset += 2;
+ return value;
+}
+
+bool HotspotScript::execute(Hotspot *h)
+{
+ Resources &r = Resources::getReference();
+ MemoryBlock *scriptData = r.hotspotScriptData();
+ uint16 offset = h->script();
+ int16 opcode = 0;
+ int16 param1, param2;
+ bool breakFlag = false;
+
+ while (!breakFlag) {
+ opcode = nextVal(scriptData, offset);
+ switch (opcode) {
+ case S2_OPCODE_TIMEOUT:
+ param1 = nextVal(scriptData, offset);
+ h->setTickCtr(param1);
+ h->setScript(offset);
+ breakFlag = true;
+ break;
+
+ case S2_OPCODE_POSITION:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+ h->setPosition(param1 - 0x80, param2 - 0x80);
+ break;
+
+ case S2_OPCODE_CHANGE_POS:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+ h->setPosition(h->x() + param1, h->y() + param2);
+ break;
+
+ case S2_OPCODE_UNLOAD:
+ breakFlag = true;
+ break;
+
+ case S2_OPCODE_DIMENSIONS:
+ param1 = nextVal(scriptData, offset) << 4;
+ param2 = nextVal(scriptData, offset);
+ h->setSize((uint16) param1, (uint16) param2);
+ break;
+
+ case S2_OPCODE_JUMP:
+ offset = (uint16) nextVal(scriptData, offset);
+ break;
+
+ case S2_OPCODE_ANIMATION:
+ param1 = nextVal(scriptData, offset);
+ h->setAnimation(param1);
+ break;
+
+ case S2_OPCODE_UNKNOWN_247:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+// warning("UNKNOWN_247 stub called");
+ break;
+
+ case S2_OPCODE_UNKNOWN_258:
+ param1 = nextVal(scriptData, offset);
+// 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));
+ break;
+
+ default:
+ // Set the animation frame number
+ h->setFrameNumber(opcode);
+ h->setScript(offset);
+ breakFlag = true;
+ break;
+ }
+ }
+
+ return (opcode == S2_OPCODE_UNLOAD);
+}
+
+} // end of namespace Lure