mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 22:28:10 +00:00
831 lines
24 KiB
C++
831 lines
24 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "lure/res.h"
|
|
#include "lure/disk.h"
|
|
#include "lure/scripts.h"
|
|
#include "lure/screen.h"
|
|
#include "lure/lure.h"
|
|
#include "common/endian.h"
|
|
#include "common/events.h"
|
|
|
|
namespace Lure {
|
|
|
|
static Resources *int_resources = nullptr;
|
|
|
|
Resources &Resources::getReference() {
|
|
return *int_resources;
|
|
}
|
|
|
|
Resources::Resources() : _rnd(LureEngine::getReference().rnd()) {
|
|
int_resources = this;
|
|
reloadData();
|
|
|
|
// Load the string list
|
|
MemoryBlock *mb = Disk::getReference().getEntry(STRING_LIST_RESOURCE_ID);
|
|
_stringList.load(mb);
|
|
delete mb;
|
|
|
|
// WORKAROUND: In Spanish the look "Obsevar" should be "Observar"
|
|
if (!Common::String(_stringList.getString(LOOK)).compareTo("Obsevar"))
|
|
_stringList.setString(LOOK, "Observar");
|
|
}
|
|
|
|
Resources::~Resources() {
|
|
// Free up any loaded data
|
|
freeData();
|
|
|
|
// Free up constant data
|
|
_stringList.clear();
|
|
}
|
|
|
|
void Resources::freeData() {
|
|
_activeHotspots.clear();
|
|
_roomData.clear();
|
|
_hotspotData.clear();
|
|
_hotspotOverrides.clear();
|
|
_animData.clear();
|
|
_exitJoins.clear();
|
|
_delayList.clear();
|
|
_charSchedules.clear();
|
|
_randomActions.clear();
|
|
_indexedRoomExitHospots.clear();
|
|
_pausedList.clear();
|
|
_actionsList.clear();
|
|
_coordinateList.clear();
|
|
_talkHeaders.clear();
|
|
_talkData.clear();
|
|
_giveTalkIds.clear();
|
|
|
|
free(_hotspotScriptData);
|
|
delete _paletteSubset;
|
|
delete _scriptData;
|
|
delete _script2Data;
|
|
delete _talkDialogData;
|
|
delete _messagesData;
|
|
delete _cursors;
|
|
delete[] _charOffsets;
|
|
}
|
|
|
|
struct AnimRecordTemp {
|
|
uint16 offset;
|
|
MovementDataList *list;
|
|
};
|
|
|
|
void Resources::reset() {
|
|
freeData();
|
|
|
|
_fieldList.reset();
|
|
_barmanLists.reset();
|
|
_talkState = TALK_NONE;
|
|
_activeTalkData = nullptr;
|
|
|
|
reloadData();
|
|
}
|
|
|
|
void Resources::reloadData() {
|
|
Disk &d = Disk::getReference();
|
|
bool isEGA = LureEngine::getReference().isEGA();
|
|
MemoryBlock *mb, *paths;
|
|
uint16 *offset, offsetVal;
|
|
uint16 recordId, startOffset;
|
|
int ctr;
|
|
uint16 *v;
|
|
|
|
// Get the palette subset data
|
|
_paletteSubset = isEGA ? nullptr : new Palette(ALT_PALETTE_RESOURCE_ID);
|
|
|
|
// Load room data
|
|
mb = d.getEntry(ROOM_DATA_RESOURCE_ID);
|
|
paths = d.getEntry(ROOM_PATHS_RESOURCE_ID);
|
|
|
|
offset = (uint16 *) mb->data();
|
|
while ((offsetVal = READ_LE_UINT16(offset++)) != 0xffff) {
|
|
if (offsetVal != 0) {
|
|
// Get room resource
|
|
RoomResource *rec = (RoomResource *) (mb->data() + offsetVal);
|
|
|
|
RoomData *newEntry = new RoomData(rec, paths);
|
|
_roomData.push_back(RoomDataList::value_type(newEntry));
|
|
|
|
uint8 numExits = rec->numExits;
|
|
if (numExits > 0) {
|
|
RoomExitResource *exitRes = (RoomExitResource *)
|
|
(mb->data() + offsetVal + sizeof(RoomResource));
|
|
|
|
for (uint16 exitCtr = 0; exitCtr < numExits; ++exitCtr, ++exitRes) {
|
|
RoomExitData *exit = new RoomExitData(exitRes);
|
|
newEntry->exits.push_back(RoomExitList::value_type(exit));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete mb;
|
|
delete paths;
|
|
|
|
// Load room exits
|
|
mb = d.getEntry(ROOM_EXITS_RESOURCE_ID);
|
|
ctr = 0;
|
|
for (;;) {
|
|
offsetVal = READ_LE_UINT16(mb->data() + (ctr * 2));
|
|
if (offsetVal == 0xffff) break;
|
|
|
|
if (offsetVal != 0) {
|
|
RoomData *room = getRoom(ctr);
|
|
if (room) {
|
|
RoomExitHotspotResource *re = (RoomExitHotspotResource *)
|
|
(mb->data() + offsetVal);
|
|
while (READ_LE_UINT16(&re->hotspotId) != 0xffff) {
|
|
RoomExitHotspotData *newEntry = new RoomExitHotspotData(re);
|
|
room->exitHotspots.push_back(RoomExitHotspotList::value_type(newEntry));
|
|
++re;
|
|
}
|
|
}
|
|
}
|
|
++ctr;
|
|
}
|
|
delete mb;
|
|
|
|
// Load room joins
|
|
mb = d.getEntry(ROOM_EXIT_JOINS_RESOURCE_ID);
|
|
RoomExitJoinResource *joinRec = (RoomExitJoinResource *) mb->data();
|
|
while (READ_LE_UINT16(&joinRec->hotspot1Id) != 0xffff) {
|
|
RoomExitJoinData *newEntry = new RoomExitJoinData(joinRec);
|
|
_exitJoins.push_back(RoomExitJoinList::value_type(newEntry));
|
|
|
|
GET_NEXT(joinRec, RoomExitJoinResource);
|
|
}
|
|
delete mb;
|
|
|
|
// Load the set of NPC schedules
|
|
mb = d.getEntry(NPC_SCHEDULES_RESOURCE_ID);
|
|
|
|
// Load the lookup list of support data indexes used in the script engine
|
|
numCharOffsets = 0;
|
|
offset = (uint16 *) mb->data();
|
|
while (READ_LE_UINT16(offset++) != 0xffff) ++numCharOffsets;
|
|
_charOffsets = new uint16[numCharOffsets];
|
|
offset = (uint16 *) mb->data();
|
|
for (ctr = 0; ctr < numCharOffsets; ++ctr, ++offset)
|
|
_charOffsets[ctr] = READ_LE_UINT16(offset);
|
|
|
|
// Next load up the list of random actions your follower can do in each room
|
|
|
|
++offset;
|
|
while (READ_LE_UINT16(offset) != 0xffff) {
|
|
RandomActionSet *actionSet = new RandomActionSet(offset);
|
|
_randomActions.push_back(RandomActionList::value_type(actionSet));
|
|
}
|
|
|
|
// Loop through loading the schedules
|
|
ctr = 0;
|
|
while ((startOffset = READ_LE_UINT16(++offset)) != 0xffff) {
|
|
CharacterScheduleResource *res = (CharacterScheduleResource *) (mb->data() + startOffset);
|
|
CharacterScheduleSet *newEntry = new CharacterScheduleSet(res, ++ctr);
|
|
_charSchedules.push_back(CharacterScheduleList::value_type(newEntry));
|
|
}
|
|
delete mb;
|
|
|
|
// Load the hotspot list
|
|
mb = d.getEntry(HOTSPOT_DATA_RESOURCE_ID);
|
|
HotspotResource *hsRec = (HotspotResource *) mb->data();
|
|
while (READ_LE_UINT16(&hsRec->hotspotId) != 0xffff) {
|
|
HotspotData *newEntry = new HotspotData(hsRec);
|
|
_hotspotData.push_back(HotspotDataList::value_type(newEntry));
|
|
|
|
GET_NEXT(hsRec, HotspotResource);
|
|
}
|
|
delete mb;
|
|
|
|
// Load the hotspot overrides
|
|
mb = d.getEntry(HOTSPOT_OVERRIDE_DATA_RESOURCE_ID);
|
|
HotspotOverrideResource *hsoRec = (HotspotOverrideResource *) mb->data();
|
|
while (READ_LE_UINT16(&hsoRec->hotspotId) != 0xffff) {
|
|
HotspotOverrideData *newEntry = new HotspotOverrideData(hsoRec);
|
|
_hotspotOverrides.push_back(HotspotOverrideList::value_type(newEntry));
|
|
++hsoRec;
|
|
}
|
|
delete mb;
|
|
|
|
// Load the animation list
|
|
mb = d.getEntry(ANIM_DATA_RESOURCE_ID);
|
|
HotspotAnimResource *animRec = (HotspotAnimResource *) mb->data();
|
|
while (READ_LE_UINT16(&animRec->animRecordId) != 0xffff) {
|
|
HotspotAnimData *newEntry = new HotspotAnimData(animRec);
|
|
_animData.push_back(HotspotAnimList::value_type(newEntry));
|
|
|
|
// Handle any direction frames
|
|
AnimRecordTemp dirEntries[4] = {
|
|
{FROM_LE_16(animRec->leftOffset), &newEntry->leftFrames},
|
|
{FROM_LE_16(animRec->rightOffset), &newEntry->rightFrames},
|
|
{FROM_LE_16(animRec->upOffset), &newEntry->upFrames},
|
|
{FROM_LE_16(animRec->downOffset), &newEntry->downFrames}};
|
|
for (int dirCtr = 0; dirCtr < 4; ++dirCtr) {
|
|
offsetVal = dirEntries[dirCtr].offset;
|
|
if (offsetVal != 0) {
|
|
MovementResource *moveRec = (MovementResource *)
|
|
(mb->data() + offsetVal);
|
|
while (READ_LE_UINT16(&moveRec->frameNumber) != 0xffff) {
|
|
MovementData *newMove = new MovementData(moveRec);
|
|
dirEntries[dirCtr].list->push_back(MovementDataList::value_type(newMove));
|
|
++moveRec;
|
|
}
|
|
}
|
|
}
|
|
|
|
++animRec;
|
|
}
|
|
delete mb;
|
|
|
|
// Hotspot scripts
|
|
mb = d.getEntry(HOTSPOT_SCRIPT_LIST_RESOURCE_ID);
|
|
uint16 numEntries = mb->size() / 2;
|
|
uint16 *srcVal = (uint16 *) mb->data();
|
|
uint16 *destVal = _hotspotScriptData = (uint16 *)
|
|
Memory::alloc(numEntries * sizeof(uint16));
|
|
for (ctr = 0; ctr < numEntries; ++ctr, ++srcVal, ++destVal) {
|
|
*destVal = READ_LE_UINT16(srcVal);
|
|
}
|
|
delete mb;
|
|
|
|
// Handle the hotspot action lists
|
|
mb = d.getEntry(ACTION_LIST_RESOURCE_ID);
|
|
v = (uint16 *) mb->data();
|
|
while ((recordId = READ_LE_UINT16(v)) != 0xffff) {
|
|
++v;
|
|
offsetVal = READ_LE_UINT16(v);
|
|
++v;
|
|
|
|
HotspotActionList *list = new HotspotActionList(
|
|
recordId, mb->data() + offsetVal);
|
|
_actionsList.push_back(HotspotActionSet::value_type(list));
|
|
}
|
|
delete mb;
|
|
|
|
// Read in the talk data header
|
|
mb = d.getEntry(TALK_HEADER_RESOURCE_ID);
|
|
TalkHeaderResource *thHeader = (TalkHeaderResource *) mb->data();
|
|
uint16 hotspotId;
|
|
while ((hotspotId = READ_LE_UINT16(&thHeader->hotspotId)) != 0xffff) {
|
|
uint16 *offsets = (uint16 *) (mb->data() + READ_LE_UINT16(&thHeader->offset));
|
|
TalkHeaderData *newEntry = new TalkHeaderData(hotspotId, offsets);
|
|
|
|
_talkHeaders.push_back(TalkHeaderList::value_type(newEntry));
|
|
++thHeader;
|
|
}
|
|
delete mb;
|
|
|
|
// Read in the talk data entries
|
|
mb = d.getEntry(TALK_DATA_RESOURCE_ID);
|
|
|
|
// First get the list of give talk Ids
|
|
v = (uint16 *) mb->data();
|
|
|
|
for (int talkIndex = 0; talkIndex < NUM_GIVE_TALK_IDS; ++talkIndex)
|
|
_giveTalkIds.push_back(READ_LE_UINT16(v++));
|
|
|
|
// Get the following talk ata
|
|
|
|
byte *dataStart = (byte *) v;
|
|
TalkDataHeaderResource *tdHeader = (TalkDataHeaderResource *) dataStart;
|
|
|
|
while ((recordId = READ_LE_UINT16(&tdHeader->recordId)) != 0xffff) {
|
|
TalkData *data = new TalkData(recordId);
|
|
|
|
TalkDataResource *entry = (TalkDataResource *) (dataStart + READ_LE_UINT16(&tdHeader->listOffset));
|
|
while (READ_LE_UINT16(&entry->preSequenceId) != 0xffff) {
|
|
TalkEntryData *newEntry = new TalkEntryData(entry);
|
|
data->entries.push_back(TalkEntryList::value_type(newEntry));
|
|
++entry;
|
|
}
|
|
|
|
entry = (TalkDataResource *) (dataStart + READ_LE_UINT16(&tdHeader->responsesOffset));
|
|
while (READ_LE_UINT16(&entry->preSequenceId) != 0xffff) {
|
|
TalkEntryData *newEntry = new TalkEntryData(entry);
|
|
data->responses.push_back(TalkEntryList::value_type(newEntry));
|
|
++entry;
|
|
}
|
|
|
|
_talkData.push_back(TalkDataList::value_type(data));
|
|
++tdHeader;
|
|
}
|
|
delete mb;
|
|
|
|
// Load in the list of room exit coordinates
|
|
mb = d.getEntry(EXIT_COORDINATES_RESOURCE_ID);
|
|
RoomExitCoordinateEntryResource *coordRec = (RoomExitCoordinateEntryResource *) mb->data();
|
|
while (READ_LE_UINT16(coordRec) != 0xffff) {
|
|
RoomExitCoordinates *newEntry = new RoomExitCoordinates(coordRec);
|
|
_coordinateList.push_back(RoomExitCoordinatesList::value_type(newEntry));
|
|
++coordRec;
|
|
}
|
|
delete mb;
|
|
|
|
// Load the list of room exit hotspot Ids
|
|
mb = d.getEntry(EXIT_HOTSPOT_ID_LIST);
|
|
RoomExitIndexedHotspotResource *indexedRec = (RoomExitIndexedHotspotResource *) mb->data();
|
|
while (READ_LE_UINT16(indexedRec) != 0xffff) {
|
|
_indexedRoomExitHospots.push_back(RoomExitIndexedHotspotList::value_type(new RoomExitIndexedHotspotData(indexedRec)));
|
|
indexedRec++;
|
|
}
|
|
delete mb;
|
|
|
|
// Initialize delay list
|
|
_delayList.clear(true);
|
|
|
|
// Load miscellaneous data
|
|
_cursors = d.getEntry(CURSOR_RESOURCE_ID);
|
|
_scriptData = d.getEntry(SCRIPT_DATA_RESOURCE_ID);
|
|
_script2Data = d.getEntry(SCRIPT2_DATA_RESOURCE_ID);
|
|
_messagesData = d.getEntry(MESSAGES_LIST_RESOURCE_ID);
|
|
_talkDialogData = d.getEntry(TALK_DIALOG_RESOURCE_ID);
|
|
|
|
_activeTalkData = nullptr;
|
|
_currentAction = NONE;
|
|
_talkState = TALK_NONE;
|
|
_talkSelection = 0;
|
|
_talkStartEntry = 0;
|
|
_talkDetails.active = false;
|
|
_talkingCharacter = 0;
|
|
}
|
|
|
|
RoomExitJoinData *Resources::getExitJoin(uint16 hotspotId) {
|
|
RoomExitJoinList::iterator i;
|
|
|
|
for (i = _exitJoins.begin(); i != _exitJoins.end(); ++i) {
|
|
RoomExitJoinData *rec = (*i).get();
|
|
if ((rec->hotspots[0].hotspotId == hotspotId) || (rec->hotspots[1].hotspotId == hotspotId))
|
|
return rec;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
uint16 Resources::getHotspotScript(uint16 index) {
|
|
return _hotspotScriptData[index];
|
|
}
|
|
|
|
RoomData *Resources::getRoom(uint16 roomNumber) {
|
|
RoomDataList::iterator i;
|
|
|
|
for (i = _roomData.begin(); i != _roomData.end(); ++i) {
|
|
RoomData *rec = (*i).get();
|
|
if (rec->roomNumber == roomNumber) return rec;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool Resources::checkHotspotExtent(HotspotData *hotspot) {
|
|
uint16 roomNum = hotspot->roomNumber;
|
|
RoomData *room = getRoom(roomNum);
|
|
return (hotspot->startX >= room->clippingXStart) && ((room->clippingXEnd == 0) ||
|
|
(hotspot->startX + 32 < room->clippingXEnd));
|
|
}
|
|
|
|
void Resources::insertPaletteSubset(Palette &p) {
|
|
p.palette()->copyFrom(_paletteSubset->palette(), 0, 129*4, 60*4);
|
|
p.palette()->copyFrom(_paletteSubset->palette(), 60*4, 220*4, 8*4);
|
|
}
|
|
|
|
byte *Resources::getCursor(uint8 cursorNum) {
|
|
if (!LureEngine::getReference().isEGA())
|
|
return _cursors->data() + (cursorNum * CURSOR_SIZE);
|
|
|
|
Common::fill(&_cursor[0], &_cursor[0] + CURSOR_SIZE, 0);
|
|
byte *pSrc = _cursors->data() + (cursorNum * 64);
|
|
byte *pDest = &_cursor[0];
|
|
|
|
for (int y = 0; y < 16; ++y) {
|
|
for (int x = 0; x < 2; ++x) {
|
|
for (int planeNum = 0; planeNum < 2; ++planeNum, ++pSrc) {
|
|
|
|
byte v = *pSrc;
|
|
for (int bitCtr = 0; bitCtr < 8; ++bitCtr, v <<= 1) {
|
|
if ((v & 0x80) != 0)
|
|
*(pDest + bitCtr) |= 1 << planeNum;
|
|
else
|
|
*(pDest + bitCtr) &= ~(1 << planeNum);
|
|
}
|
|
}
|
|
|
|
pDest += 8;
|
|
}
|
|
}
|
|
|
|
// Post-process the cells to adjust the color
|
|
for (int index = 0; index < CURSOR_SIZE; ++index) {
|
|
if (_cursor[index] == 3) _cursor[index] = 15;
|
|
}
|
|
|
|
return &_cursor[0];
|
|
}
|
|
|
|
HotspotData *Resources::getHotspot(uint16 hotspotId) {
|
|
HotspotDataList::iterator i;
|
|
|
|
for (i = _hotspotData.begin(); i != _hotspotData.end(); ++i) {
|
|
HotspotData *rec = (*i).get();
|
|
if (rec->hotspotId == hotspotId) return rec;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Hotspot *Resources::getActiveHotspot(uint16 hotspotId) {
|
|
HotspotList::iterator i;
|
|
|
|
for (i = _activeHotspots.begin(); i != _activeHotspots.end(); ++i) {
|
|
Hotspot *rec = (*i).get();
|
|
if (rec->hotspotId() == hotspotId) return rec;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
HotspotOverrideData *Resources::getHotspotOverride(uint16 hotspotId) {
|
|
HotspotOverrideList::iterator i;
|
|
|
|
for (i = _hotspotOverrides.begin(); i != _hotspotOverrides.end(); ++i) {
|
|
HotspotOverrideData *rec = (*i).get();
|
|
if (rec->hotspotId == hotspotId) return rec;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
HotspotAnimData *Resources::getAnimation(uint16 animRecordId) {
|
|
HotspotAnimList::iterator i;
|
|
|
|
for (i = _animData.begin(); i != _animData.end(); ++i) {
|
|
HotspotAnimData *rec = (*i).get();
|
|
if (rec->animRecordId == animRecordId) return rec;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int Resources::getAnimationIndex(HotspotAnimData *animData) {
|
|
HotspotAnimList::iterator i;
|
|
int index = 0;
|
|
|
|
for (i = _animData.begin(); i != _animData.end(); ++i, ++index) {
|
|
HotspotAnimData *rec = (*i).get();
|
|
if (rec == animData)
|
|
return index;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
uint16 Resources::getHotspotAction(uint16 actionsOffset, Action action) {
|
|
HotspotActionList *list = _actionsList.getActions(actionsOffset);
|
|
uint16 offset = (!list) ? 0 : list->getActionOffset(action);
|
|
debugC(ERROR_DETAILED, kLureDebugHotspots,
|
|
"Resources::getHotspotAction actionsOffset=%xh result=%xh", actionsOffset, offset);
|
|
return offset;
|
|
}
|
|
|
|
TalkHeaderData *Resources::getTalkHeader(uint16 hotspotId) {
|
|
TalkHeaderList::iterator i;
|
|
for (i = _talkHeaders.begin(); i != _talkHeaders.end(); ++i) {
|
|
TalkHeaderData *rec = (*i).get();
|
|
if (rec->characterId == hotspotId) return rec;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
HotspotActionList *Resources::getHotspotActions(uint16 actionsOffset) {
|
|
return _actionsList.getActions(actionsOffset);
|
|
}
|
|
|
|
void Resources::setTalkingCharacter(uint16 id) {
|
|
Resources &res = Resources::getReference();
|
|
|
|
if (_talkingCharacter != 0) {
|
|
deactivateHotspot(_talkingCharacter, true);
|
|
HotspotData *charHotspot = res.getHotspot(_talkingCharacter);
|
|
assert(charHotspot);
|
|
charHotspot->talkDestCharacterId = 0;
|
|
|
|
if (_talkingCharacter != id)
|
|
charHotspot->talkCountdown = 0;
|
|
}
|
|
|
|
_talkingCharacter = id;
|
|
|
|
if (_talkingCharacter != 0) {
|
|
Hotspot *character = getActiveHotspot(id);
|
|
assert(character);
|
|
|
|
// Add the special "voice" animation above the character
|
|
Hotspot *hotspot = new Hotspot(character, VOICE_ANIM_IDX);
|
|
addHotspot(hotspot);
|
|
}
|
|
}
|
|
uint16 englishLoadOffsets[] = {0x3afe, 0x41BD, 0x7167, 0x7172, 0x8617, 0x88ac, 0};
|
|
|
|
Hotspot *Resources::activateHotspot(uint16 hotspotId) {
|
|
Resources &resources = Resources::getReference();
|
|
HotspotData *res = getHotspot(hotspotId);
|
|
if (!res) return nullptr;
|
|
res->roomNumber &= 0x7fff; // clear any suppression bit in room #
|
|
|
|
// Make sure that the hotspot isn't already active
|
|
Hotspot *h = getActiveHotspot(hotspotId);
|
|
if (h != nullptr)
|
|
return h;
|
|
|
|
// If it's NPC with a schedule, then activate the schedule
|
|
if ((res->npcScheduleId != 0) && (res->npcSchedule.isEmpty())) {
|
|
CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId);
|
|
res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber);
|
|
}
|
|
|
|
// Check the script load flag
|
|
if (res->scriptLoadFlag) {
|
|
// Execute a script rather than doing a standard load
|
|
Script::execute(res->loadOffset);
|
|
} else {
|
|
// Standard load
|
|
bool loadFlag = true;
|
|
uint16 talkIndex;
|
|
|
|
switch (res->loadOffset) {
|
|
case 1:
|
|
// Copy protection check - since the game is freeware now, ignore it
|
|
loadFlag = false;
|
|
break;
|
|
|
|
case 2:
|
|
// Empty handler used to prevent loading hotspots that
|
|
// are yet to be active (such as the straw fire)
|
|
loadFlag = false;
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
// Standard animation load
|
|
break;
|
|
|
|
case 5:
|
|
// Custom loader used by the notice hotspot 42ah in room #20
|
|
talkIndex = _fieldList.getField(TALK_INDEX);
|
|
if ((talkIndex < 8) || (talkIndex >= 14))
|
|
// Don't load hotspot
|
|
loadFlag = false;
|
|
else
|
|
// Make the notice be on-screen
|
|
res->startY = 85;
|
|
break;
|
|
|
|
case 6:
|
|
// Torch in room #1
|
|
loadFlag = _fieldList.getField(TORCH_HIDE) == 0;
|
|
break;
|
|
|
|
default:
|
|
// All others simply activate the hotspot
|
|
warning("Hotspot %d uses unknown load offset index %d",
|
|
res->hotspotId, res->loadOffset);
|
|
}
|
|
|
|
if (loadFlag) {
|
|
Hotspot *hotspot = addHotspot(hotspotId);
|
|
assert(hotspot);
|
|
|
|
// Special post-load handling
|
|
if (res->loadOffset == 3) hotspot->setPersistant(true);
|
|
if (res->loadOffset == 5) hotspot->handleTalkDialog();
|
|
if (hotspotId == CASTLE_SKORL_ID) {
|
|
// The Castle skorl has a default room #99, so it needs to be adjusted dynamically
|
|
res->npcSchedule.clear();
|
|
CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId);
|
|
res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber);
|
|
}
|
|
if ((hotspotId == GOEWIN_ID) && (hotspot->roomNumber() == 39)) {
|
|
// WORKAROUND: When you re-join Goewin in the caves, clear her schedule. This may prevent a
|
|
// situation where you could close the left door, and she'd be permanently stuck trying to go
|
|
// the next room on the left, since her old schedule still had her following your old path
|
|
hotspot->currentActions().clear();
|
|
|
|
// Since she's no longer a follower, clear her start room field
|
|
hotspot->setStartRoomNumber(0);
|
|
}
|
|
|
|
// TODO: Figure out why there's a room set in the animation decode for a range of characters,
|
|
// particularly since it doesn't seem to match what happens in-game
|
|
/*
|
|
if ((hotspot->hotspotId() >= RATPOUCH_ID) &&
|
|
(hotspot->hotspotId() < FIRST_NONCHARACTER_ID) &&
|
|
(hotspot->roomNumber() < 42)) {
|
|
// Start wandering characters off in room 24
|
|
hotspot->setRoomNumber(24);
|
|
hotspot->setPosition(64, 116);
|
|
_fieldList.wanderingCharsLoaded() = true;
|
|
}
|
|
*/
|
|
|
|
return hotspot;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Hotspot *Resources::addHotspot(uint16 hotspotId) {
|
|
HotspotData *hData = getHotspot(hotspotId);
|
|
assert(hData);
|
|
Hotspot *hotspot = new Hotspot(hData);
|
|
_activeHotspots.push_back(HotspotList::value_type(hotspot));
|
|
|
|
if (hotspotId < FIRST_NONCHARACTER_ID) {
|
|
// Default characters to facing upwards until they start moving
|
|
hotspot->setDirection(UP);
|
|
hotspot->setCharRectY(0);
|
|
|
|
// When reactivating an NPC, ensure that their previous state wasn't PROCESSING_PATH, since
|
|
// the pause has destroyed the previously decided destination position
|
|
if (!hData->npcSchedule.isEmpty() && (hData->npcSchedule.top().action() == PROCESSING_PATH))
|
|
hData->npcSchedule.top().setAction(DISPATCH_ACTION);
|
|
}
|
|
|
|
return hotspot;
|
|
}
|
|
|
|
void Resources::addHotspot(Hotspot *hotspot) {
|
|
_activeHotspots.push_back(HotspotList::value_type(hotspot));
|
|
}
|
|
|
|
void Resources::deactivateHotspot(uint16 hotspotId, bool isDestId) {
|
|
HotspotList::iterator i = _activeHotspots.begin();
|
|
|
|
while (i != _activeHotspots.end()) {
|
|
Hotspot const &h = **i;
|
|
if ((!isDestId && (h.hotspotId() == hotspotId)) ||
|
|
(isDestId && (h.destHotspotId() == hotspotId) && (h.hotspotId() == 0xffff))) {
|
|
_activeHotspots.erase(i);
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void Resources::deactivateHotspot(Hotspot *hotspot) {
|
|
HotspotList::iterator i = _activeHotspots.begin();
|
|
|
|
while (i != _activeHotspots.end()) {
|
|
Hotspot *h = (*i).get();
|
|
if (h == hotspot) {
|
|
_activeHotspots.erase(i);
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
uint16 Resources::numInventoryItems() {
|
|
uint16 numItems = 0;
|
|
HotspotDataList &list = _hotspotData;
|
|
HotspotDataList::iterator i;
|
|
for (i = list.begin(); i != list.end(); ++i) {
|
|
if ((*i)->roomNumber == PLAYER_ID) ++numItems;
|
|
}
|
|
|
|
return numItems;
|
|
}
|
|
|
|
void Resources::copyCursorTo(Surface *s, uint8 cursorNum, int16 x, int16 y) {
|
|
byte *pSrc = getCursor(cursorNum);
|
|
byte *pDest = s->data().data() + (y * FULL_SCREEN_WIDTH) + x;
|
|
|
|
for (int yP = 0; yP < CURSOR_HEIGHT; ++yP) {
|
|
for (int xP = 0; xP < CURSOR_WIDTH; ++xP) {
|
|
if (*pSrc != 0) *pDest = *pSrc;
|
|
++pSrc;
|
|
++pDest;
|
|
}
|
|
pDest += FULL_SCREEN_WIDTH - CURSOR_WIDTH;
|
|
}
|
|
}
|
|
|
|
void Resources::setTalkData(uint16 offset) {
|
|
if (offset == 0) {
|
|
_activeTalkData = nullptr;
|
|
return;
|
|
}
|
|
|
|
TalkDataList::iterator i;
|
|
for (i = _talkData.begin(); i != _talkData.end(); ++i) {
|
|
TalkData *rec = (*i).get();
|
|
if (rec->recordId == offset) {
|
|
_activeTalkData = rec;
|
|
return;
|
|
}
|
|
}
|
|
|
|
error("Unknown talk entry offset %d requested", offset);
|
|
}
|
|
|
|
void Resources::saveToStream(Common::WriteStream *stream) {
|
|
// Save basic fields
|
|
stream->writeUint16LE(_talkingCharacter);
|
|
|
|
// Save out the schedule for any non-active NPCs
|
|
HotspotDataList::iterator i;
|
|
for (i = _hotspotData.begin(); i != _hotspotData.end(); ++i) {
|
|
HotspotData const &rec = **i;
|
|
if (!rec.npcSchedule.isEmpty()) {
|
|
Hotspot *h = getActiveHotspot(rec.hotspotId);
|
|
if (h == nullptr) {
|
|
stream->writeUint16LE(rec.hotspotId);
|
|
rec.npcSchedule.saveToStream(stream);
|
|
}
|
|
}
|
|
}
|
|
stream->writeUint16LE(0xffff);
|
|
|
|
// Save sublist data
|
|
_hotspotData.saveToStream(stream);
|
|
_activeHotspots.saveToStream(stream);
|
|
_fieldList.saveToStream(stream);
|
|
_randomActions.saveToStream(stream);
|
|
_barmanLists.saveToStream(stream);
|
|
_exitJoins.saveToStream(stream);
|
|
_roomData.saveToStream(stream);
|
|
_delayList.saveToStream(stream);
|
|
_talkData.saveToStream(stream);
|
|
}
|
|
|
|
void Resources::loadFromStream(Common::ReadStream *stream) {
|
|
uint8 saveVersion = LureEngine::getReference().saveVersion();
|
|
|
|
if (saveVersion >= 26) {
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading resource data");
|
|
_talkingCharacter = stream->readUint16LE();
|
|
} else {
|
|
_talkingCharacter = 0;
|
|
}
|
|
|
|
_talkState = TALK_NONE;
|
|
_activeTalkData = nullptr;
|
|
|
|
if (saveVersion >= 31) {
|
|
// Load in any schedules for non-active NPCS
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading NPC schedules");
|
|
uint16 hotspotId;
|
|
while ((hotspotId = stream->readUint16LE()) != 0xffff) {
|
|
HotspotData *hotspot = getHotspot(hotspotId);
|
|
assert(hotspot);
|
|
hotspot->npcSchedule.loadFromStream(stream);
|
|
}
|
|
}
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading hotspot data");
|
|
_hotspotData.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading active hotspots");
|
|
_activeHotspots.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading fields");
|
|
_fieldList.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading random actions");
|
|
_randomActions.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading barman lists");
|
|
_barmanLists.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading room exit joins");
|
|
_exitJoins.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading walkable paths");
|
|
_roomData.loadFromStream(stream);
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading delay list");
|
|
_delayList.loadFromStream(stream);
|
|
|
|
if (saveVersion >= 32) {
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading talk data");
|
|
_talkData.loadFromStream(stream);
|
|
}
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Finished loading");
|
|
}
|
|
|
|
} // End of namespace Lure
|