/* 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/res.h" #include "lure/disk.h" #include "lure/scripts.h" #include "lure/system.h" #include "common/endian.h" namespace Lure { const char *actionList[] = {NULL, "Get", NULL, "Push", "Pull", "Operate", "Open", "Close", "Lock", "Unlock", "Use", "Give", "Talk to", "Tell", "Buy", "Look", "Look at", "Look through", "Ask", NULL, "Drink", "Status", "Go to", "Return", "Bribe", "Examine"}; // Room data holding class RoomData::RoomData(RoomResource *rec, MemoryBlock *pathData) { roomNumber = READ_LE_UINT16(&rec->roomNumber); descId = READ_LE_UINT16(&rec->descId); sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset); numLayers = READ_LE_UINT16(&rec->numLayers); paths.load(pathData->data() + (roomNumber - 1) * ROOM_PATHS_SIZE); for (int ctr = 0; ctr < 4; ++ctr) layers[ctr] = READ_LE_UINT16(&rec->layers[ctr]); clippingXStart = READ_LE_UINT16(&rec->clippingXStart); clippingXEnd = READ_LE_UINT16(&rec->clippingXEnd); } // Room exit hotspot area holding class RoomExitHotspotData::RoomExitHotspotData(RoomExitHotspotResource *rec) { hotspotId = READ_LE_UINT16(&rec->hotspotId); xs = READ_LE_INT16(&rec->xs); ys = READ_LE_INT16(&rec->ys); xe = READ_LE_INT16(&rec->xe); ye = READ_LE_INT16(&rec->ye); cursorNum = rec->cursorNum; destRoomNumber = READ_LE_UINT16(&rec->destRoomNumber); } // Room exit class RoomExitData::RoomExitData(RoomExitResource *rec) { xs = rec->xs; ys = rec->ys; xe = rec->xe; ye = rec->ye; sequenceOffset = rec->sequenceOffset; roomNumber = rec->newRoom; x = rec->newRoomX; y = rec->newRoomY; switch (rec->direction) { case 0x80: direction = UP; break; case 0x40: direction = DOWN; break; case 0x20: direction = LEFT; break; case 0x10: direction = RIGHT; break; default: direction = NO_DIRECTION; break; } } bool RoomExitData::insideRect(int16 xp, int16 yp) { return ((xp >= xs) && (xp < xe) && (yp >= ys) && (yp < ye)); } RoomExitData *RoomExitList::checkExits(int16 xp, int16 yp) { iterator i; for (i = begin(); i != end(); i++) { RoomExitData *rec = *i; if (rec->insideRect(xp, yp)) { return rec; } } return NULL; } // Room paths bool RoomPathsData::isOccupied(int x, int y) { if ((x < 0) || (y < 0) || (x >= ROOM_PATHS_WIDTH) || (y >= ROOM_PATHS_HEIGHT)) // Off screen, so flag as not occupied return false; return (_data[y * 5 + (x >> 3)] & (0x80 >> (x % 8))) != 0; } void RoomPathsData::setOccupied(int x, int y, int width) { if ((x < 0) || (y < 0) || (x >= ROOM_PATHS_WIDTH) || (y >= ROOM_PATHS_HEIGHT)) return; byte *p = &_data[y * 5 + (x / 8)]; byte bitMask = 0x80 >> (x % 8); for (int bitCtr = 0; bitCtr < width; ++bitCtr) { *p |= bitMask; bitMask >>= 1; if (bitMask == 0) { ++p; bitMask = 0x80; } } } void RoomPathsData::clearOccupied(int x, int y, int width) { if ((x < 0) || (y < 0) || (x >= ROOM_PATHS_WIDTH) || (y >= ROOM_PATHS_HEIGHT)) return; byte *p = &_data[y * 5 + (x / 8)]; byte bitMask = 0x80 >> (x % 8); for (int bitCtr = 0; bitCtr < width; ++bitCtr) { *p &= ~bitMask; bitMask >>= 1; if (bitMask == 0) { ++p; bitMask = 0x80; } } } // decompresses the bit-packed data for which parts of a room are occupied // into a byte array. It also adds a column and row of padding around the // edges of the screen, and extends occupied areas to adjust for the width // of the chracter void RoomPathsData::decompress(RoomPathsDecompressedData &dataOut, int characterWidth) { byte *pIn = &_data[ROOM_PATHS_SIZE - 1]; uint16 *pOut = &dataOut[DECODED_PATHS_WIDTH * DECODED_PATHS_HEIGHT - 1]; byte v; int paddingCtr; int charWidth = (characterWidth - 1) >> 3; int charCtr = 0; bool charState = false; // Handle padding for last row, including left/right edge padding, as // well as the right column of the second row for (paddingCtr = 0; paddingCtr < (DECODED_PATHS_WIDTH + 1); ++paddingCtr) *pOut-- = 0; for (int y = 0; y < ROOM_PATHS_HEIGHT; ++y) { for (int x = 0; x < (ROOM_PATHS_WIDTH / 8); ++x) { // Get next byte, which containing bits for 8 blocks v = *pIn--; for (int bitCtr = 0; bitCtr < 8; ++bitCtr) { bool isSet = (v & 1) != 0; v >>= 1; if (charState) { // Handling occupied characters adjusted for character width if (isSet) // Reset character counter charCtr = charWidth; *pOut-- = 0xffff; charState = (--charCtr != 0); } else { // Normal decompression if (!isSet) { // Flag block is available for walking on *pOut-- = 0; } else { // Flag block is occupied *pOut-- = 0xffff; // Handling for adjusting for character widths charCtr = charWidth - 1; charState = charCtr >= 0; } } } } // Store 2 words to allow off-screen row-start/prior row end *pOut-- = 0; *pOut-- = 0; charState = false; } // Handle padding for final top row - no need for end column, as end of prior // row provided padding for it for (paddingCtr = 0; paddingCtr < (ROOM_PATHS_WIDTH + 1); ++paddingCtr) *pOut-- = 0; } // Room exit joins class RoomExitJoinData::RoomExitJoinData(RoomExitJoinResource *rec) { hotspot1Id = READ_LE_UINT16(&rec->hotspot1Id); h1CurrentFrame = rec->h1CurrentFrame; h1DestFrame = rec->h1DestFrame; h1OpenSound = rec->h1OpenSound; h1CloseSound = rec->h1CloseSound; hotspot2Id = READ_LE_UINT16(&rec->hotspot2Id); h2CurrentFrame = rec->h2CurrentFrame; h2DestFrame = rec->h2DestFrame; h2OpenSound = rec->h2OpenSound; h2CloseSound = rec->h2CloseSound; blocked = rec->blocked; } // Hotspot action record HotspotActionData::HotspotActionData(HotspotActionResource *rec) { action = (Action) rec->action; sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset); } uint16 HotspotActionList::getActionOffset(Action action) { iterator i; for (i = begin(); i != end(); ++i) { HotspotActionData *rec = *i; if (rec->action == action) return rec->sequenceOffset; } return 0; } // Hotspot data HotspotData::HotspotData(HotspotResource *rec) { hotspotId = READ_LE_UINT16(&rec->hotspotId); nameId = READ_LE_UINT16(&rec->nameId); descId = READ_LE_UINT16(&rec->descId); descId2 = READ_LE_UINT16(&rec->descId2); actions = READ_LE_UINT32(&rec->actions); actionsOffset = READ_LE_UINT16(&rec->actionsOffset); flags = (byte) (actions >> 24) & 0xf0; actions &= 0xfffffff; roomNumber = READ_LE_UINT16(&rec->roomNumber); layer = rec->layer; scriptLoadFlag = rec->scriptLoadFlag; loadOffset = READ_LE_UINT16(&rec->loadOffset); startX = READ_LE_INT16(&rec->startX); startY = READ_LE_INT16(&rec->startY); width = READ_LE_UINT16(&rec->width); height = READ_LE_UINT16(&rec->height); widthCopy = READ_LE_UINT16(&rec->widthCopy); heightCopy = READ_LE_UINT16(&rec->heightCopy); yCorrection = READ_LE_UINT16(&rec->yCorrection); walkX = READ_LE_INT16(&rec->walkX); walkY = READ_LE_UINT16(&rec->walkY); talkX = rec->talkX; talkY = rec->talkY; colourOffset = READ_LE_UINT16(&rec->colourOffset); animRecordId = READ_LE_UINT16(&rec->animRecordId); sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset); tickProcOffset = READ_LE_UINT16(&rec->tickProcOffset); tickTimeout = READ_LE_UINT16(&rec->tickTimeout); } // Hotspot override data HotspotOverrideData::HotspotOverrideData(HotspotOverrideResource *rec) { hotspotId = READ_LE_UINT16(&rec->hotspotId); xs = READ_LE_INT16(&rec->xs); ys = READ_LE_INT16(&rec->ys); xe = READ_LE_INT16(&rec->xe); ye = READ_LE_INT16(&rec->ye); } // Hotspot animation movement frame MovementData::MovementData(MovementResource *rec) { frameNumber = READ_LE_UINT16(&rec->frameNumber); xChange = READ_LE_INT16(&rec->xChange); yChange = READ_LE_INT16(&rec->yChange); } // List of movement frames bool MovementDataList::getFrame(uint16 currentFrame, int16 &xChange, int16 &yChange, uint16 &nextFrame) { if (empty()) return false; bool foundFlag = false; iterator i; for (i = begin(); i != end(); ++i) { MovementData *rec = *i; if (foundFlag || (i == begin())) { xChange = rec->xChange; yChange = rec->yChange; nextFrame = rec->frameNumber; if (foundFlag) return true; } if (rec->frameNumber == currentFrame) foundFlag = true; } return true; } // Hotspot animation data HotspotAnimData::HotspotAnimData(HotspotAnimResource *rec) { animRecordId = READ_LE_UINT16(&rec->animRecordId); animId = READ_LE_UINT16(&rec->animId); flags = READ_LE_UINT16(&rec->flags); upFrame = rec->upFrame; downFrame = rec->downFrame; leftFrame = rec->leftFrame; rightFrame = rec->rightFrame; } // Hotspot action lists HotspotActionList::HotspotActionList(uint16 id, byte *data) { recordId = id; uint16 numItems = READ_LE_UINT16(data); data += 2; HotspotActionResource *actionRec = (HotspotActionResource *) data; for (int actionCtr = 0; actionCtr < numItems; ++actionCtr, ++actionRec) { HotspotActionData *actionEntry = new HotspotActionData(actionRec); push_back(actionEntry); } } HotspotActionList *HotspotActionSet::getActions(uint16 recordId) { HotspotActionSet::iterator i; for (i = begin(); i != end(); ++i) { HotspotActionList *list = *i; if (list->recordId == recordId) return list; } return NULL; } // The following class holds the set of offsets for a character's talk set TalkHeaderData::TalkHeaderData(uint16 charId, uint16 *entries) { uint16 *src, *dest; characterId = charId; // Get number of entries _numEntries = 0; src = entries; while (READ_LE_UINT16(src) != 0xffff) { ++src; ++_numEntries; } // Duplicate the list _data = (uint16 *) Memory::alloc(_numEntries * sizeof(uint16)); src = entries; dest = _data; for (int ctr = 0; ctr < _numEntries; ++ctr, ++src, ++dest) *dest = READ_LE_UINT16(src); } TalkHeaderData::~TalkHeaderData() { free(_data); } uint16 TalkHeaderData::getEntry(int index) { if (index >= _numEntries) error("Invalid talk index %d specified for hotspot %xh", _numEntries, characterId); return _data[index]; } // The following class holds a single talking entry TalkEntryData::TalkEntryData(TalkDataResource *rec) { preSequenceId = FROM_LE_16(rec->preSequenceId); descId = FROM_LE_16(rec->descId); postSequenceId = FROM_LE_16(rec->postSequenceId); } // The following class acts as a container for all the talk entries and // responses for a single record Id TalkData::TalkData(uint16 id) { recordId = id; } TalkData::~TalkData() { entries.clear(); responses.clear(); } TalkEntryData *TalkData::getResponse(int index) { TalkEntryList::iterator i = responses.begin(); int v = index; while (v-- > 0) { if (i == responses.end()) error("Invalid talk response index %d specified", index); ++i; } return *i; } // The following class handles a set of coordinates a character should walk to // if they're to exit a room to a designated secondary room RoomExitCoordinates::RoomExitCoordinates(RoomExitCoordinateEntryResource *rec) { int ctr; for (ctr = 0; ctr < ROOM_EXIT_COORDINATES_NUM_ENTRIES; ++ctr) { _entries[ctr].x = FROM_LE_16(rec->entries[ctr].x); _entries[ctr].y = FROM_LE_16(rec->entries[ctr].y); uint16 v = FROM_LE_16(rec->entries[ctr].roomNumber); _entries[ctr].roomNumber = v & 0xfff; _entries[ctr].unknown = v >> 12; } for (ctr = 0; ctr < ROOM_EXIT_COORDINATES_NUM_ROOMS; ++ctr) _roomIndex[ctr] = rec->roomIndex[ctr]; } RoomExitCoordinates &RoomExitCoordinatesList::getEntry(uint16 roomNumber) { RoomExitCoordinatesList::iterator i = begin(); while (--roomNumber > 0) ++i; return **i; } RoomExitCoordinateData &RoomExitCoordinates::getData(uint16 destRoomNumber) { return _entries[_roomIndex[destRoomNumber - 1]]; } // The following classes hold any sequence offsets that are being delayed SequenceDelayData::SequenceDelayData(uint16 delay, uint16 seqOffset) { OSystem &system = System::getReference(); _timeoutCtr = system.getMillis() + delay; _sequenceOffset = seqOffset; } void SequenceDelayList::addSequence(uint16 delay, uint16 seqOffset) { SequenceDelayData *entry = new SequenceDelayData(delay, seqOffset); push_back(entry); } void SequenceDelayList::tick() { uint32 currTime = System::getReference().getMillis(); SequenceDelayList::iterator i; for (i = begin(); i != end(); i++) { SequenceDelayData *entry = *i; if (entry->_timeoutCtr >= currTime) { uint16 seqOffset = entry->_sequenceOffset; erase(i); Script::execute(seqOffset); return; } } } // Field list and miscellaneous variables ValueTableData::ValueTableData() { _numGroats = 0; _playerNewPos.roomNumber = 0; _playerNewPos.position.x = 0; _playerNewPos.position.y = 0; for (uint16 index = 0; index < NUM_VALUE_FIELDS; ++index) _fieldList[index] = 0; } bool ValueTableData::isKnownField(uint16 fieldIndex) { return ((fieldIndex <= 10) && (fieldIndex != 6)) || (fieldIndex == 15) || ((fieldIndex >= 18) && (fieldIndex <= 20)); } uint16 ValueTableData::getField(uint16 fieldIndex) { if (fieldIndex > NUM_VALUE_FIELDS) error("Invalid field index specified %d", fieldIndex); if (!isKnownField(fieldIndex)) warning("Unknown field index %d in GET_FIELD opcode", fieldIndex); return _fieldList[fieldIndex]; } uint16 ValueTableData::getField(FieldName fieldName) { return getField((uint16) fieldName); } void ValueTableData::setField(uint16 fieldIndex, uint16 value) { if (fieldIndex > NUM_VALUE_FIELDS) error("Invalid field index specified %d", fieldIndex); _fieldList[fieldIndex] = value; if (!isKnownField(fieldIndex)) warning("Unknown field index %d in SET_FIELD opcode", fieldIndex); } void ValueTableData::setField(FieldName fieldName, uint16 value) { setField((uint16) fieldName, value); } } // end of namespace Lure