scummvm/engines/lure/res.cpp
Colin Snover b0eb5caa51 LURE: Stop taking address of unaligned pointer
While usage of these pointers was technically safe because they
were read through an alignment-aware API, taking the address of an
unaligned pointer was generating warnings in Clang, and is not
strictly necessary here. This change solves the warning and also
protects this code from any future change that might cause it to
start reading unsafely.
2017-11-07 22:57:33 -06:00

832 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 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.
*
*/
#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 = NULL;
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 = NULL;
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 ? NULL : 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 = NULL;
_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 NULL;
}
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 NULL;
}
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 NULL;
}
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 NULL;
}
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 NULL;
}
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 NULL;
}
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 NULL;
}
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 NULL;
res->roomNumber &= 0x7fff; // clear any suppression bit in room #
// Make sure that the hotspot isn't already active
Hotspot *h = getActiveHotspot(hotspotId);
if (h != NULL)
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 NULL;
}
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 = NULL;
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 == NULL) {
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 = NULL;
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