diff options
Diffstat (limited to 'engines/lure/scripts.cpp')
-rw-r--r-- | engines/lure/scripts.cpp | 576 |
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 |