mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 09:49:11 +00:00
1502 lines
44 KiB
C++
1502 lines
44 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/disk.h"
|
|
#include "lure/lure.h"
|
|
#include "lure/res.h"
|
|
#include "lure/res_struct.h"
|
|
#include "lure/scripts.h"
|
|
#include "common/endian.h"
|
|
|
|
namespace Lure {
|
|
|
|
const int actionNumParams[NPC_JUMP_ADDRESS+1] = {0,
|
|
1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1,
|
|
0, 1, 1, 1, 1, 0, 0, 2, 1, 1, 0, 0, 1, 1, 2, 2, 5, 2, 2, 1};
|
|
|
|
// Barman related frame lists
|
|
|
|
static const uint16 basicPolish[] = {8+13,8+14,8+15,8+16,8+17,8+18,8+17,8+16,8+15,8+14,
|
|
8+15,8+16,8+17,8+18,8+17,8+16,8+15,8+14,8+13,0};
|
|
|
|
static const uint16 sidsFetch[] = {12+1,12+2,12+3,12+4,12+5,12+6,12+5,12+6,12+5,12+4,12+3,12+7,12+8,0};
|
|
|
|
static const uint16 nelliesScratch[] = {11+1,11+2,11+3,11+4,11+5,11+4,11+5,11+4,11+5,11+4,11+3,11+2,11+1,0};
|
|
|
|
static const uint16 nelliesFetch[] = {1,2,3,4,5,4,5,4,3,2,6,7,0};
|
|
|
|
static const uint16 ewansFetch[] = {13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,0};
|
|
|
|
static const uint16 ewanExtraGraphic1[]= {
|
|
28,29,30,31,32,33,34,35,36,37,
|
|
38,39,40,41,42,43,44,45,46,47,
|
|
48,
|
|
40,39,38,37,36,35,34,33,32,31,30,29,28,
|
|
0};
|
|
|
|
static const uint16 ewanExtraGraphic2[] = {
|
|
1,2,3,4,5,6,7,8,9,
|
|
10,11,12,13,14,15,16,17,18,19,
|
|
20,21,22,23,24,0};
|
|
|
|
static const BarEntry default_barList[3] = {
|
|
{29, SID_ID, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {&basicPolish[0], &sidsFetch[0], NULL, NULL}, 13, NULL},
|
|
{32, NELLIE_ID, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {&nelliesScratch[0], &nelliesFetch[0], NULL, NULL}, 14, NULL},
|
|
{35, EWAN_ID, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {&ewansFetch[0], &ewansFetch[0],
|
|
&ewanExtraGraphic1[0], &ewanExtraGraphic2[0]}, 16, NULL}
|
|
};
|
|
|
|
const RoomTranslationRecord roomTranslations[] = {
|
|
{0x1E, 0x13}, {0x07, 0x08}, {0x1C, 0x12}, {0x26, 0x0F},
|
|
{0x27, 0x0F}, {0x28, 0x0F}, {0x29, 0x0F}, {0x22, 0x0A},
|
|
{0x23, 0x13}, {0x24, 0x14}, {0x31, 0x2C}, {0x2F, 0x2C},
|
|
{0, 0}};
|
|
|
|
// Room data holding class
|
|
|
|
RoomData::RoomData(RoomResource *rec, MemoryBlock *pathData) {
|
|
roomNumber = READ_LE_UINT16(&rec->roomNumber);
|
|
hdrFlags = rec->hdrFlags;
|
|
|
|
actions = READ_LE_UINT32(&rec->actions) & 0xfffffff;
|
|
flags = (READ_LE_UINT32(&rec->actions) >> 24) & 0xf0;
|
|
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);
|
|
exitTime = READ_LE_UINT32(&rec->exitTime);
|
|
areaFlag = rec->areaFlag;
|
|
walkBounds.left = READ_LE_UINT16(&rec->walkBounds.xs);
|
|
walkBounds.right = READ_LE_UINT16(&rec->walkBounds.xe);
|
|
walkBounds.top = READ_LE_UINT16(&rec->walkBounds.ys);
|
|
walkBounds.bottom = READ_LE_UINT16(&rec->walkBounds.ye);
|
|
}
|
|
|
|
// Room exit hotspot area holding class
|
|
|
|
RoomExitHotspotData::RoomExitHotspotData(RoomExitHotspotResource *rec) {
|
|
hotspotId = READ_LE_UINT16(&rec->hotspotId);
|
|
xs = READ_LE_UINT16(&rec->xs);
|
|
ys = READ_LE_UINT16(&rec->ys);
|
|
xe = READ_LE_UINT16(&rec->xe);
|
|
ye = READ_LE_UINT16(&rec->ye);
|
|
cursorNum = rec->cursorNum;
|
|
destRoomNumber = READ_LE_UINT16(&rec->destRoomNumber);
|
|
}
|
|
|
|
// Room exit class
|
|
|
|
RoomExitData::RoomExitData(RoomExitResource *rec) {
|
|
xs = READ_LE_INT16(&rec->xs);
|
|
ys = READ_LE_INT16(&rec->ys);
|
|
xe = READ_LE_INT16(&rec->xe);
|
|
ye = READ_LE_INT16(&rec->ye);
|
|
sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
|
|
roomNumber = rec->newRoom;
|
|
x = READ_LE_INT16(&rec->newRoomX);
|
|
y = READ_LE_INT16(&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).get();
|
|
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;
|
|
}
|
|
|
|
bool RoomPathsData::isOccupied(int x, int y, int width) {
|
|
for (int blockCtr = 0; blockCtr < width; ++blockCtr) {
|
|
if (isOccupied(x + 8 * blockCtr, y))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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 >> 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) {
|
|
charState = false;
|
|
|
|
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 data class
|
|
|
|
void RoomDataList::saveToStream(Common::WriteStream *stream) {
|
|
RoomDataList::iterator i;
|
|
|
|
for (i = begin(); i != end(); ++i) {
|
|
RoomData *rec = (*i).get();
|
|
stream->writeByte(rec->flags);
|
|
const byte *pathData = rec->paths.data();
|
|
stream->write(pathData, ROOM_PATHS_HEIGHT * ROOM_PATHS_WIDTH);
|
|
}
|
|
}
|
|
|
|
void RoomDataList::loadFromStream(Common::ReadStream *stream) {
|
|
RoomDataList::iterator i;
|
|
byte data[ROOM_PATHS_HEIGHT * ROOM_PATHS_WIDTH];
|
|
|
|
for (i = begin(); i != end(); ++i) {
|
|
RoomData *rec = (*i).get();
|
|
rec->flags = stream->readByte();
|
|
stream->read(data, ROOM_PATHS_HEIGHT * ROOM_PATHS_WIDTH);
|
|
rec->paths.load(data);
|
|
}
|
|
}
|
|
|
|
// Room exit joins class
|
|
|
|
RoomExitJoinData::RoomExitJoinData(RoomExitJoinResource *rec) {
|
|
hotspots[0].hotspotId = READ_LE_UINT16(&rec->hotspot1Id);
|
|
hotspots[0].currentFrame = rec->h1CurrentFrame;
|
|
hotspots[0].destFrame = rec->h1DestFrame;
|
|
hotspots[0].openSound = rec->h1OpenSound;
|
|
hotspots[0].closeSound = rec->h1CloseSound;
|
|
hotspots[1].hotspotId = READ_LE_UINT16(&rec->hotspot2Id);
|
|
hotspots[1].currentFrame = rec->h2CurrentFrame;
|
|
hotspots[1].destFrame = rec->h2DestFrame;
|
|
hotspots[1].openSound = rec->h2OpenSound;
|
|
hotspots[1].closeSound = rec->h2CloseSound;
|
|
blocked = rec->blocked;
|
|
}
|
|
|
|
void RoomExitJoinList::saveToStream(Common::WriteStream *stream) {
|
|
for (RoomExitJoinList::iterator i = begin(); i != end(); ++i) {
|
|
RoomExitJoinData *rec = (*i).get();
|
|
|
|
stream->writeUint16LE(rec->hotspots[0].hotspotId);
|
|
stream->writeUint16LE(rec->hotspots[1].hotspotId);
|
|
stream->writeByte(rec->hotspots[0].currentFrame);
|
|
stream->writeByte(rec->hotspots[0].destFrame);
|
|
stream->writeByte(rec->hotspots[1].currentFrame);
|
|
stream->writeByte(rec->hotspots[1].destFrame);
|
|
stream->writeByte(rec->blocked);
|
|
}
|
|
|
|
// Write end of list marker
|
|
stream->writeUint16LE(0xffff);
|
|
}
|
|
|
|
void RoomExitJoinList::loadFromStream(Common::ReadStream *stream) {
|
|
for (RoomExitJoinList::iterator i = begin(); i != end(); ++i) {
|
|
RoomExitJoinData *rec = (*i).get();
|
|
|
|
uint16 hotspot1Id = stream->readUint16LE();
|
|
if (hotspot1Id == 0xffff) error("Invalid room exit join list");
|
|
uint16 hotspot2Id = stream->readUint16LE();
|
|
|
|
if ((rec->hotspots[0].hotspotId != hotspot1Id) ||
|
|
(rec->hotspots[1].hotspotId != hotspot2Id))
|
|
break;
|
|
|
|
rec->hotspots[0].currentFrame = stream->readByte();
|
|
rec->hotspots[0].destFrame = stream->readByte();
|
|
rec->hotspots[1].currentFrame = stream->readByte();
|
|
rec->hotspots[1].destFrame = stream->readByte();
|
|
rec->blocked = stream->readByte();
|
|
}
|
|
|
|
// Read final end of list marker
|
|
stream->readUint16LE();
|
|
}
|
|
|
|
// Hotspot action record
|
|
|
|
HotspotActionData::HotspotActionData(HotspotActionResource *rec) {
|
|
action = (Action) rec->action;
|
|
// FIXME: some compilers may add padding to properly align the second member
|
|
sequenceOffset = READ_LE_UINT16(((byte *)rec) + 1);
|
|
}
|
|
|
|
uint16 HotspotActionList::getActionOffset(Action action) {
|
|
iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
HotspotActionData *rec = (*i).get();
|
|
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_UINT16(&rec->startX);
|
|
startY = READ_LE_UINT16(&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_UINT16(&rec->walkX);
|
|
walkY = READ_LE_UINT16(&rec->walkY);
|
|
talkX = rec->talkX;
|
|
talkY = rec->talkY;
|
|
colorOffset = READ_LE_UINT16(&rec->colorOffset);
|
|
animRecordId = READ_LE_UINT16(&rec->animRecordId);
|
|
hotspotScriptOffset = READ_LE_UINT16(&rec->hotspotScriptOffset);
|
|
talkScriptOffset = READ_LE_UINT16(&rec->talkScriptOffset);
|
|
tickProcId = READ_LE_UINT16(&rec->tickProcId);
|
|
tickTimeout = READ_LE_UINT16(&rec->tickTimeout);
|
|
tickScriptOffset = READ_LE_UINT16(&rec->tickScriptOffset);
|
|
characterMode = (CharacterMode) READ_LE_UINT16(&rec->characterMode);
|
|
delayCtr = READ_LE_UINT16(&rec->delayCtr);
|
|
flags2 = READ_LE_UINT16(&rec->flags2);
|
|
headerFlags = READ_LE_UINT16(&rec->hdrFlags);
|
|
|
|
// Initialize runtime fields
|
|
actionCtr = 0;
|
|
blockedState = BS_NONE;
|
|
blockedFlag = false;
|
|
coveredFlag = VB_INITIAL;
|
|
talkMessageId = 0;
|
|
talkerId = 0;
|
|
talkDestCharacterId = 0;
|
|
talkCountdown = 0;
|
|
useHotspotId = 0;
|
|
pauseCtr = 0;
|
|
actionHotspotId = 0;
|
|
talkOverride = 0;
|
|
talkGate = 0;
|
|
scriptHotspotId = 0;
|
|
|
|
// Get the NPC schedule, if any
|
|
npcScheduleId = READ_LE_UINT16(&rec->npcSchedule);
|
|
}
|
|
|
|
void HotspotData::saveToStream(Common::WriteStream *stream) {
|
|
// Write out the basic fields
|
|
stream->writeUint16LE(nameId);
|
|
stream->writeUint16LE(descId);
|
|
stream->writeUint16LE(descId2);
|
|
stream->writeUint32LE(actions);
|
|
stream->writeByte(flags);
|
|
stream->writeByte(flags2);
|
|
stream->writeByte(headerFlags);
|
|
stream->writeSint16LE(startX);
|
|
stream->writeSint16LE(startY);
|
|
stream->writeUint16LE(roomNumber);
|
|
stream->writeByte(layer);
|
|
stream->writeUint16LE(walkX);
|
|
stream->writeUint16LE(walkY);
|
|
|
|
stream->writeUint16LE(width);
|
|
stream->writeUint16LE(height);
|
|
stream->writeUint16LE(widthCopy);
|
|
stream->writeUint16LE(heightCopy);
|
|
stream->writeUint16LE(yCorrection);
|
|
stream->writeUint16LE(hotspotScriptOffset);
|
|
stream->writeUint16LE(tickProcId);
|
|
stream->writeUint16LE(tickTimeout);
|
|
stream->writeUint16LE(tickScriptOffset);
|
|
stream->writeUint16LE(characterMode);
|
|
stream->writeUint16LE(delayCtr);
|
|
stream->writeUint16LE(animRecordId);
|
|
|
|
// Write out the runtime fields
|
|
stream->writeUint16LE(actionCtr);
|
|
stream->writeUint16LE(blockedState);
|
|
stream->writeByte((byte)blockedFlag);
|
|
stream->writeByte((byte)coveredFlag);
|
|
stream->writeUint16LE(talkMessageId);
|
|
stream->writeUint16LE(talkerId);
|
|
stream->writeUint16LE(talkDestCharacterId);
|
|
stream->writeUint16LE(talkCountdown);
|
|
stream->writeUint16LE(pauseCtr);
|
|
stream->writeUint16LE(useHotspotId);
|
|
stream->writeUint16LE(scriptHotspotId);
|
|
stream->writeUint16LE(talkGate);
|
|
stream->writeUint16LE(actionHotspotId);
|
|
stream->writeUint16LE(talkOverride);
|
|
}
|
|
|
|
void HotspotData::loadFromStream(Common::ReadStream *stream) {
|
|
// Read in the basic fields
|
|
nameId = stream->readUint16LE();
|
|
descId = stream->readUint16LE();
|
|
descId2 = stream->readUint16LE();
|
|
actions = stream->readUint32LE();
|
|
flags = stream->readByte();
|
|
flags2 = stream->readByte();
|
|
headerFlags = stream->readByte();
|
|
startX = stream->readSint16LE();
|
|
startY = stream->readSint16LE();
|
|
roomNumber = stream->readUint16LE();
|
|
|
|
uint8 saveVersion = LureEngine::getReference().saveVersion();
|
|
if (saveVersion >= 29)
|
|
layer = stream->readByte();
|
|
if (saveVersion >= 33) {
|
|
walkX = stream->readUint16LE();
|
|
walkY = stream->readUint16LE();
|
|
}
|
|
|
|
width = stream->readUint16LE();
|
|
height = stream->readUint16LE();
|
|
widthCopy = stream->readUint16LE();
|
|
heightCopy = stream->readUint16LE();
|
|
yCorrection = stream->readUint16LE();
|
|
hotspotScriptOffset = stream->readUint16LE();
|
|
tickProcId = stream->readUint16LE();
|
|
tickTimeout = stream->readUint16LE();
|
|
tickScriptOffset = stream->readUint16LE();
|
|
characterMode = (CharacterMode) stream->readUint16LE();
|
|
delayCtr = stream->readUint16LE();
|
|
animRecordId = stream->readUint16LE();
|
|
|
|
// Read in the runtime fields
|
|
actionCtr = stream->readUint16LE();
|
|
blockedState = (BlockedState)stream->readUint16LE();
|
|
blockedFlag = stream->readByte() != 0;
|
|
coveredFlag = (VariantBool)stream->readByte();
|
|
talkMessageId = stream->readUint16LE();
|
|
talkerId = stream->readUint16LE();
|
|
talkDestCharacterId = stream->readUint16LE();
|
|
talkCountdown = stream->readUint16LE();
|
|
pauseCtr = stream->readUint16LE();
|
|
useHotspotId = stream->readUint16LE();
|
|
scriptHotspotId = stream->readUint16LE();
|
|
talkGate = stream->readUint16LE();
|
|
actionHotspotId = stream->readUint16LE();
|
|
talkOverride = stream->readUint16LE();
|
|
}
|
|
|
|
// Hotspot data list
|
|
|
|
void HotspotDataList::saveToStream(Common::WriteStream *stream) {
|
|
iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
HotspotData *hotspot = (*i).get();
|
|
stream->writeUint16LE(hotspot->hotspotId);
|
|
hotspot->saveToStream(stream);
|
|
}
|
|
stream->writeUint16LE(0);
|
|
}
|
|
|
|
void HotspotDataList::loadFromStream(Common::ReadStream *stream) {
|
|
Resources &res = Resources::getReference();
|
|
uint16 hotspotId = stream->readUint16LE();
|
|
while (hotspotId != 0) {
|
|
HotspotData *hotspot = res.getHotspot(hotspotId);
|
|
assert(hotspot);
|
|
hotspot->loadFromStream(stream);
|
|
hotspotId = stream->readUint16LE();
|
|
}
|
|
}
|
|
|
|
// Hotspot override data
|
|
|
|
HotspotOverrideData::HotspotOverrideData(HotspotOverrideResource *rec) {
|
|
hotspotId = READ_LE_UINT16(&rec->hotspotId);
|
|
xs = READ_LE_UINT16(&rec->xs);
|
|
ys = READ_LE_UINT16(&rec->ys);
|
|
xe = READ_LE_UINT16(&rec->xe);
|
|
ye = READ_LE_UINT16(&rec->ye);
|
|
}
|
|
|
|
// Hotspot animation movement frame
|
|
|
|
MovementData::MovementData(MovementResource *rec) {
|
|
frameNumber = READ_LE_UINT16(&rec->frameNumber);
|
|
xChange = READ_LE_UINT16(&rec->xChange);
|
|
yChange = READ_LE_UINT16(&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).get();
|
|
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,
|
|
GET_NEXT(actionRec, HotspotActionResource)) {
|
|
|
|
HotspotActionData *actionEntry = new HotspotActionData(actionRec);
|
|
push_back(HotspotActionList::value_type(actionEntry));
|
|
}
|
|
}
|
|
|
|
HotspotActionList *HotspotActionSet::getActions(uint16 recordId) {
|
|
HotspotActionSet::iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
HotspotActionList *list = (*i).get();
|
|
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 = READ_LE_UINT16(&rec->preSequenceId);
|
|
descId = READ_LE_UINT16(&rec->descId);
|
|
postSequenceId = READ_LE_UINT16(&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).get();
|
|
}
|
|
|
|
// The following class acts as a container for all the NPC conversations
|
|
|
|
void TalkDataList::saveToStream(Common::WriteStream *stream) {
|
|
TalkDataList::iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
TalkData *rec = (*i).get();
|
|
TalkEntryList::iterator i2;
|
|
|
|
for (i2 = rec->entries.begin(); i2 != rec->entries.end(); ++i2) {
|
|
TalkEntryData *entry = (*i2).get();
|
|
stream->writeUint16LE(entry->descId);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TalkDataList::loadFromStream(Common::ReadStream *stream) {
|
|
TalkDataList::iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
TalkData *rec = (*i).get();
|
|
TalkEntryList::iterator i2;
|
|
|
|
for (i2 = rec->entries.begin(); i2 != rec->entries.end(); ++i2) {
|
|
TalkEntryData *entry = (*i2).get();
|
|
entry->descId = stream->readUint16LE();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
uint16 tempY = READ_LE_UINT16(&rec->entries[ctr].y);
|
|
_entries[ctr].x = READ_LE_UINT16(&rec->entries[ctr].x);
|
|
_entries[ctr].y = tempY & 0xfff;
|
|
_entries[ctr].roomNumber = READ_LE_UINT16(&rec->entries[ctr].roomNumber);
|
|
_entries[ctr].hotspotIndexId = (tempY >> 12) << 4;
|
|
}
|
|
|
|
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, bool canClearFlag) {
|
|
// The delay is in number of seconds - convert it to remaining milliseconds
|
|
timeoutCtr = delay * 1000;
|
|
sequenceOffset = seqOffset;
|
|
canClear = canClearFlag;
|
|
}
|
|
|
|
SequenceDelayData *SequenceDelayData::load(uint32 delay, uint16 seqOffset, bool canClearFlag) {
|
|
SequenceDelayData *result = new SequenceDelayData();
|
|
result->sequenceOffset = seqOffset;
|
|
result->timeoutCtr = delay;
|
|
result->canClear = canClearFlag;
|
|
return result;
|
|
}
|
|
|
|
void SequenceDelayList::add(uint16 delay, uint16 seqOffset, bool canClear) {
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Delay List add sequence=%xh delay=%d canClear=%d",
|
|
seqOffset, delay, (int)canClear);
|
|
SequenceDelayData *entry = new SequenceDelayData(delay, seqOffset, canClear);
|
|
push_front(SequenceDelayList::value_type(entry));
|
|
}
|
|
|
|
void SequenceDelayList::tick() {
|
|
SequenceDelayList::iterator i;
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Delay List check start at time %d",
|
|
g_system->getMillis());
|
|
|
|
for (i = begin(); i != end(); ++i) {
|
|
SequenceDelayData *entry = (*i).get();
|
|
debugC(ERROR_DETAILED, kLureDebugScripts, "Delay List check %xh at time %d", entry->sequenceOffset, entry->timeoutCtr);
|
|
|
|
if (entry->timeoutCtr <= GAME_FRAME_DELAY) {
|
|
// Timeout reached - delete entry from list and execute the sequence
|
|
uint16 seqOffset = entry->sequenceOffset;
|
|
erase(i);
|
|
Script::execute(seqOffset);
|
|
return;
|
|
} else {
|
|
entry->timeoutCtr -= GAME_FRAME_DELAY;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SequenceDelayList::clear(bool forceClear) {
|
|
SequenceDelayList::iterator i = begin();
|
|
|
|
while (i != end()) {
|
|
SequenceDelayData *entry = (*i).get();
|
|
if (entry->canClear || forceClear)
|
|
i = erase(i);
|
|
else
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void SequenceDelayList::saveToStream(Common::WriteStream *stream) {
|
|
SequenceDelayList::iterator i;
|
|
|
|
for (i = begin(); i != end(); ++i) {
|
|
SequenceDelayData *entry = (*i).get();
|
|
stream->writeUint16LE(entry->sequenceOffset);
|
|
stream->writeUint32LE(entry->timeoutCtr);
|
|
stream->writeByte(entry->canClear);
|
|
}
|
|
|
|
stream->writeUint16LE(0);
|
|
}
|
|
|
|
void SequenceDelayList::loadFromStream(Common::ReadStream *stream) {
|
|
clear(true);
|
|
uint16 seqOffset;
|
|
|
|
while ((seqOffset = stream->readUint16LE()) != 0) {
|
|
uint32 delay = stream->readUint32LE();
|
|
bool canClear = stream->readByte() != 0;
|
|
push_back(SequenceDelayList::value_type(SequenceDelayData::load(delay, seqOffset, canClear)));
|
|
}
|
|
}
|
|
|
|
// The following classes hold the NPC schedules
|
|
|
|
CharacterScheduleEntry::CharacterScheduleEntry(Action theAction, ...) {
|
|
_parent = NULL;
|
|
_action = theAction;
|
|
|
|
va_list u_Arg;
|
|
va_start(u_Arg, theAction);
|
|
|
|
for (int paramCtr = 0; paramCtr < actionNumParams[_action]; ++paramCtr)
|
|
_params[paramCtr] = (uint16) va_arg(u_Arg, int);
|
|
|
|
va_end(u_Arg);
|
|
_numParams = actionNumParams[_action];
|
|
}
|
|
|
|
CharacterScheduleEntry::CharacterScheduleEntry(CharacterScheduleSet *parentSet,
|
|
CharacterScheduleResource *&rec) {
|
|
_parent = parentSet;
|
|
|
|
if ((rec->action == 0) || (READ_LE_UINT16(&rec->action) > NPC_JUMP_ADDRESS))
|
|
error("Invalid action encountered reading NPC schedule");
|
|
|
|
_action = (Action) READ_LE_UINT16(&rec->action);
|
|
_numParams = actionNumParams[_action];
|
|
for (int index = 0; index < _numParams; ++index)
|
|
_params[index] = READ_LE_UINT16(&rec->params[index]);
|
|
|
|
rec = (CharacterScheduleResource *) ((byte *) rec +
|
|
(_numParams + 1) * sizeof(uint16));
|
|
}
|
|
|
|
CharacterScheduleEntry::CharacterScheduleEntry(CharacterScheduleEntry *src) {
|
|
_parent = src->_parent;
|
|
_action = src->_action;
|
|
_numParams = src->_numParams;
|
|
Common::copy(src->_params, src->_params + MAX_TELL_COMMANDS * 3, _params);
|
|
}
|
|
|
|
uint16 CharacterScheduleEntry::param(int index) {
|
|
if ((index < 0) || (index >= numParams()))
|
|
error("Invalid parameter index %d on handling action %d", index, _action);
|
|
return _params[index];
|
|
}
|
|
|
|
void CharacterScheduleEntry::setDetails(Action theAction, ...) {
|
|
_action = theAction;
|
|
_numParams = actionNumParams[_action];
|
|
|
|
va_list list;
|
|
va_start(list, theAction);
|
|
|
|
for (int paramCtr = 0; paramCtr < actionNumParams[_action]; ++paramCtr)
|
|
_params[paramCtr] = (uint16) va_arg(list, int);
|
|
|
|
va_end(list);
|
|
}
|
|
|
|
void CharacterScheduleEntry::setDetails2(Action theAction, int numParamEntries, uint16 *paramList) {
|
|
_action = theAction;
|
|
_numParams = numParamEntries;
|
|
|
|
assert((numParamEntries >= 0) && (numParamEntries < (MAX_TELL_COMMANDS * 3)));
|
|
for (int paramCtr = 0; paramCtr < numParamEntries; ++paramCtr)
|
|
_params[paramCtr] = paramList[paramCtr];
|
|
}
|
|
|
|
CharacterScheduleEntry *CharacterScheduleEntry::next() {
|
|
if (_parent) {
|
|
CharacterScheduleSet::iterator i;
|
|
for (i = _parent->begin(); i != _parent->end(); ++i) {
|
|
if ((*i).get() == this) {
|
|
++i;
|
|
CharacterScheduleEntry *result = (i == _parent->end()) ? NULL : (*i).get();
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
uint16 CharacterScheduleEntry::id() {
|
|
return (_parent == NULL) ? 0 : _parent->getId(this);
|
|
}
|
|
|
|
CharacterScheduleSet::CharacterScheduleSet(CharacterScheduleResource *rec, uint16 setId) {
|
|
// Load up all the entries in the schedule
|
|
|
|
while (rec->action != 0) {
|
|
CharacterScheduleEntry *r = new CharacterScheduleEntry(this, rec);
|
|
push_back(CharacterScheduleSet::value_type(r));
|
|
}
|
|
|
|
_id = setId;
|
|
}
|
|
|
|
// Given a support data entry identifier, locates that entry in the list of data sets
|
|
|
|
CharacterScheduleEntry *CharacterScheduleList::getEntry(uint16 id, CharacterScheduleSet *currentSet) {
|
|
// Respond to the special no entry with no record
|
|
if (id == 0xffff) return NULL;
|
|
|
|
// Handle jumps within a current set versus external jumps
|
|
if ((id >> 10) == 0) {
|
|
// Jump within current set
|
|
if (currentSet == NULL)
|
|
error("Local support data jump encountered outside of a support data sequence");
|
|
} else {
|
|
// Inter-set jump - locate the appropriate set
|
|
int index = (id >> 10) - 1;
|
|
|
|
iterator i = begin();
|
|
while ((i != end()) && (index > 0)) {
|
|
++i;
|
|
--index;
|
|
}
|
|
|
|
if (i == end())
|
|
error("Invalid index %d specified for support data set", id >> 8);
|
|
currentSet = (*i).get();
|
|
}
|
|
|
|
// Get the indexed instruction in the specified set
|
|
int instructionIndex = id & 0x3ff;
|
|
CharacterScheduleSet::iterator i = currentSet->begin();
|
|
while ((i != currentSet->end()) && (instructionIndex > 0)) {
|
|
++i;
|
|
--instructionIndex;
|
|
}
|
|
if (i == currentSet->end())
|
|
error("Invalid index %d specified within support data set", id & 0x3ff);
|
|
|
|
return (*i).get();
|
|
}
|
|
|
|
uint16 CharacterScheduleSet::getId(CharacterScheduleEntry *rec) {
|
|
// Return an Id for the entry based on the id of the set combined with the
|
|
// index of the specific entry
|
|
uint16 result = _id << 10;
|
|
|
|
iterator i;
|
|
for (i = begin(); i != end(); ++i, ++result)
|
|
if ((*i).get() == rec) break;
|
|
if (i == end())
|
|
error("Parent child relationship missing in character schedule set");
|
|
return result;
|
|
}
|
|
|
|
// This classes is used to store a list of random action sets - one set per room
|
|
|
|
RandomActionSet::RandomActionSet(uint16 *&offset) {
|
|
_roomNumber = READ_LE_UINT16(offset++);
|
|
uint16 actionDetails = READ_LE_UINT16(offset++);
|
|
_numActions = (actionDetails & 0xff);
|
|
assert(_numActions <= 8);
|
|
_types = new RandomActionType[_numActions];
|
|
_ids = new uint16[_numActions];
|
|
|
|
for (int actionIndex = 0; actionIndex < _numActions; ++actionIndex) {
|
|
_ids[actionIndex] = READ_LE_UINT16(offset++);
|
|
_types[actionIndex] = (actionDetails & (0x100 << actionIndex)) != 0 ? REPEATABLE : REPEAT_ONCE;
|
|
}
|
|
}
|
|
|
|
RandomActionSet::~RandomActionSet() {
|
|
delete[] _types;
|
|
delete[] _ids;
|
|
}
|
|
|
|
RandomActionSet *RandomActionList::getRoom(uint16 roomNumber) {
|
|
iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
RandomActionSet *v = (*i).get();
|
|
if (v->roomNumber() == roomNumber)
|
|
return v;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void RandomActionSet::saveToStream(Common::WriteStream *stream) {
|
|
stream->writeByte(numActions());
|
|
for (int actionIndex = 0; actionIndex < _numActions; ++actionIndex)
|
|
stream->writeByte((byte)_types[actionIndex]);
|
|
}
|
|
|
|
void RandomActionSet::loadFromStream(Common::ReadStream *stream) {
|
|
int amount = stream->readByte();
|
|
assert(amount == _numActions);
|
|
for (int actionIndex = 0; actionIndex < _numActions; ++actionIndex)
|
|
_types[actionIndex] = (RandomActionType)stream->readByte();
|
|
}
|
|
|
|
|
|
void RandomActionList::saveToStream(Common::WriteStream *stream) {
|
|
for (iterator i = begin(); i != end(); ++i)
|
|
(*i)->saveToStream(stream);
|
|
}
|
|
|
|
void RandomActionList::loadFromStream(Common::ReadStream *stream) {
|
|
for (iterator i = begin(); i != end(); ++i)
|
|
(*i)->loadFromStream(stream);
|
|
}
|
|
|
|
// This class handles an indexed hotspot entry - which is used by the NPC code to
|
|
// determine whether exiting a room to another given room has an exit hotspot or not
|
|
|
|
RoomExitIndexedHotspotData::RoomExitIndexedHotspotData(RoomExitIndexedHotspotResource *rec) {
|
|
roomNumber = rec->roomNumber;
|
|
hotspotIndex = rec->hotspotIndex;
|
|
hotspotId = READ_LE_UINT16(&rec->hotspotId);
|
|
}
|
|
|
|
uint16 RoomExitIndexedHotspotList::getHotspot(uint16 roomNumber, uint8 hotspotIndexId) {
|
|
iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
RoomExitIndexedHotspotData *entry = (*i).get();
|
|
if ((entry->roomNumber == roomNumber) && (entry->hotspotIndex == hotspotIndexId))
|
|
return entry->hotspotId;
|
|
}
|
|
|
|
// No hotspot
|
|
return 0xffff;
|
|
}
|
|
|
|
// Paused character list methods
|
|
|
|
PausedCharacter::PausedCharacter(uint16 SrcCharId, uint16 DestCharId) {
|
|
srcCharId = SrcCharId;
|
|
destCharId = DestCharId;
|
|
counter = IDLE_COUNTDOWN_SIZE;
|
|
charHotspot = Resources::getReference().getHotspot(DestCharId);
|
|
assert(charHotspot);
|
|
}
|
|
|
|
void PausedCharacterList::reset(uint16 hotspotId) {
|
|
iterator i;
|
|
for (i = begin(); i != end(); ++i) {
|
|
PausedCharacter *rec = (*i).get();
|
|
|
|
if (rec->srcCharId == hotspotId) {
|
|
rec->counter = 1;
|
|
if (rec->destCharId < START_EXIT_ID)
|
|
rec->charHotspot->pauseCtr = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PausedCharacterList::countdown() {
|
|
iterator i = begin();
|
|
|
|
while (i != end()) {
|
|
PausedCharacter *rec = (*i).get();
|
|
--rec->counter;
|
|
|
|
// Handle reflecting counter to hotspot
|
|
if (rec->destCharId < START_EXIT_ID)
|
|
rec->charHotspot->pauseCtr = rec->counter + 1;
|
|
|
|
// If counter has reached zero, remove entry from list
|
|
if (rec->counter == 0)
|
|
i = erase(i);
|
|
else
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void PausedCharacterList::scan(Hotspot &h) {
|
|
iterator i;
|
|
|
|
if (h.blockedState() != BS_NONE) {
|
|
|
|
for (i = begin(); i != end(); ++i) {
|
|
PausedCharacter *rec = (*i).get();
|
|
|
|
if (rec->srcCharId == h.hotspotId()) {
|
|
rec->counter = IDLE_COUNTDOWN_SIZE;
|
|
|
|
if (rec->destCharId < START_EXIT_ID)
|
|
rec->charHotspot->pauseCtr = IDLE_COUNTDOWN_SIZE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int PausedCharacterList::check(uint16 charId, int numImpinging, uint16 *impingingList) {
|
|
Resources &res = Resources::getReference();
|
|
PausedCharacterList::iterator i;
|
|
int result = 0;
|
|
Hotspot *charHotspot = res.getActiveHotspot(charId);
|
|
assert(charHotspot);
|
|
|
|
for (int index = 0; index < numImpinging; ++index) {
|
|
Hotspot *hotspot = res.getActiveHotspot(impingingList[index]);
|
|
if ((!hotspot) || (!hotspot->currentActions().isEmpty() &&
|
|
(hotspot->currentActions().top().action() == EXEC_HOTSPOT_SCRIPT)))
|
|
// Entry is skipped if hotspot not present or is executing hotspot script
|
|
continue;
|
|
|
|
// Scan through the pause list to see if there's a record for the
|
|
// calling character and the impinging list entry
|
|
bool foundEntry = false;
|
|
for (i = res.pausedList().begin(); !foundEntry && (i != res.pausedList().end()); ++i) {
|
|
PausedCharacter *rec = (*i).get();
|
|
foundEntry = (rec->srcCharId == charId) &&
|
|
(rec->destCharId == hotspot->hotspotId());
|
|
}
|
|
|
|
if (foundEntry)
|
|
// There was, so move to next impinging character entry
|
|
continue;
|
|
|
|
if ((hotspot->hotspotId() == PLAYER_ID) && !hotspot->coveredFlag()) {
|
|
hotspot->updateMovement();
|
|
return 1;
|
|
}
|
|
|
|
// Add a new paused character entry
|
|
PausedCharacter *entry = new PausedCharacter(charId, hotspot->hotspotId());
|
|
res.pausedList().push_back(PausedCharacterList::value_type(entry));
|
|
charHotspot->setBlockedState(BS_INITIAL);
|
|
|
|
if (hotspot->hotspotId() < START_EXIT_ID) {
|
|
if ((charHotspot->characterMode() == CHARMODE_PAUSED) ||
|
|
((charHotspot->pauseCtr() == 0) &&
|
|
(charHotspot->characterMode() == CHARMODE_NONE))) {
|
|
if (hotspot->characterMode() != CHARMODE_WAIT_FOR_INTERACT)
|
|
hotspot->resource()->scriptHotspotId = charId;
|
|
}
|
|
|
|
hotspot->setPauseCtr(IDLE_COUNTDOWN_SIZE);
|
|
}
|
|
|
|
if (result == 0)
|
|
charHotspot->setRandomDest();
|
|
result = 2;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Wrapper class for the barman lists
|
|
|
|
BarmanLists::BarmanLists() {
|
|
for (int index = 0; index < 3; ++index)
|
|
_barList[index] = default_barList[index];
|
|
}
|
|
|
|
void BarmanLists::reset() {
|
|
for (int index = 0; index < 3; ++index)
|
|
_barList[index] = default_barList[index];
|
|
}
|
|
|
|
BarEntry &BarmanLists::getDetails(uint16 roomNumber) {
|
|
for (int index = 0; index < 3; ++index)
|
|
if (_barList[index].roomNumber == roomNumber)
|
|
return _barList[index];
|
|
error("Invalid room %d specified for barman details retrieval", roomNumber);
|
|
}
|
|
|
|
void BarmanLists::saveToStream(Common::WriteStream *stream) {
|
|
for (int index = 0; index < 3; ++index) {
|
|
uint16 value = (_barList[index].currentCustomer == NULL) ? 0 :
|
|
(_barList[index].currentCustomer - &_barList[index].customers[0]) / sizeof(BarEntry) + 1;
|
|
stream->writeUint16LE(value);
|
|
for (int ctr = 0; ctr < NUM_SERVE_CUSTOMERS; ++ctr) {
|
|
stream->writeUint16LE(_barList[index].customers[ctr].hotspotId);
|
|
stream->writeByte(_barList[index].customers[ctr].serveFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BarmanLists::loadFromStream(Common::ReadStream *stream) {
|
|
uint8 saveVersion = LureEngine::getReference().saveVersion();
|
|
int numEntries = (saveVersion >= 30) ? 3 : 2;
|
|
|
|
reset();
|
|
for (int index = 0; index < numEntries; ++index) {
|
|
int16 value = stream->readUint16LE();
|
|
_barList[index].currentCustomer = ((value < 1) || (value > NUM_SERVE_CUSTOMERS)) ? NULL :
|
|
&_barList[index].customers[value - 1];
|
|
|
|
for (int ctr = 0; ctr < NUM_SERVE_CUSTOMERS; ++ctr) {
|
|
_barList[index].customers[ctr].hotspotId = stream->readUint16LE();
|
|
_barList[index].customers[ctr].serveFlags = stream->readByte();
|
|
}
|
|
}
|
|
}
|
|
|
|
// String list resource class
|
|
|
|
void StringList::load(MemoryBlock *data) {
|
|
_data = Memory::allocate(data->size());
|
|
_data->copyFrom(data);
|
|
|
|
_numEntries = READ_LE_UINT16(_data->data());
|
|
char *p = (char *) _data->data() + sizeof(uint16);
|
|
|
|
_entries = (char **) Memory::alloc(_numEntries * sizeof(char *));
|
|
|
|
for (int index = 0; index < _numEntries; ++index) {
|
|
_entries[index] = p;
|
|
p += strlen(p) + 1;
|
|
}
|
|
}
|
|
|
|
void StringList::clear() {
|
|
if (_numEntries != 0) {
|
|
Memory::dealloc(_entries);
|
|
delete _data;
|
|
_numEntries = 0;
|
|
}
|
|
}
|
|
|
|
// Field list and miscellaneous variables
|
|
|
|
ValueTableData::ValueTableData() {
|
|
reset();
|
|
}
|
|
|
|
void ValueTableData::reset() {
|
|
_numGroats = 0;
|
|
_playerNewPos.roomNumber = 0;
|
|
_playerNewPos.position.x = 0;
|
|
_playerNewPos.position.y = 0;
|
|
_hdrFlagMask = 1;
|
|
|
|
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);
|
|
}
|
|
|
|
void ValueTableData::saveToStream(Common::WriteStream *stream) {
|
|
// Write out the special fields
|
|
stream->writeUint16LE(_numGroats);
|
|
stream->writeSint16LE(_playerNewPos.position.x);
|
|
stream->writeSint16LE(_playerNewPos.position.y);
|
|
stream->writeUint16LE(_playerNewPos.roomNumber);
|
|
stream->writeByte(_hdrFlagMask);
|
|
|
|
// Write out the special fields
|
|
for (int index = 0; index < NUM_VALUE_FIELDS; ++index)
|
|
stream->writeUint16LE(_fieldList[index]);
|
|
}
|
|
|
|
void ValueTableData::loadFromStream(Common::ReadStream *stream) {
|
|
// Load special fields
|
|
_numGroats = stream->readUint16LE();
|
|
_playerNewPos.position.x = stream->readSint16LE();
|
|
_playerNewPos.position.y = stream->readSint16LE();
|
|
_playerNewPos.roomNumber = stream->readUint16LE();
|
|
_hdrFlagMask = stream->readByte();
|
|
|
|
// Read in the field list
|
|
for (int index = 0; index < NUM_VALUE_FIELDS; ++index)
|
|
_fieldList[index] = stream->readUint16LE();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
// Current action entry class methods
|
|
|
|
CurrentActionEntry::CurrentActionEntry(CurrentAction newAction, uint16 roomNum) {
|
|
_action = newAction;
|
|
_supportData = NULL;
|
|
_dynamicSupportData = false;
|
|
_roomNumber = roomNum;
|
|
}
|
|
|
|
CurrentActionEntry::CurrentActionEntry(CurrentAction newAction, CharacterScheduleEntry *data, uint16 roomNum) {
|
|
assert(data->parent() != NULL);
|
|
_action = newAction;
|
|
_supportData = data;
|
|
_dynamicSupportData = false;
|
|
_roomNumber = roomNum;
|
|
}
|
|
|
|
CurrentActionEntry::CurrentActionEntry(Action newAction, uint16 roomNum, uint16 param1, uint16 param2) {
|
|
_action = DISPATCH_ACTION;
|
|
_dynamicSupportData = true;
|
|
_supportData = new CharacterScheduleEntry();
|
|
uint16 params[2] = {param1, param2};
|
|
_supportData->setDetails2(newAction, 2, params);
|
|
_roomNumber = roomNum;
|
|
}
|
|
|
|
CurrentActionEntry::CurrentActionEntry(CurrentActionEntry *src) {
|
|
_action = src->_action;
|
|
_dynamicSupportData = src->_dynamicSupportData;
|
|
_roomNumber = src->_roomNumber;
|
|
if (!_dynamicSupportData)
|
|
_supportData = src->_supportData;
|
|
else if (src->_supportData == NULL)
|
|
_supportData = NULL;
|
|
else {
|
|
_supportData = new CharacterScheduleEntry(src->_supportData);
|
|
}
|
|
}
|
|
|
|
void CurrentActionEntry::setSupportData(uint16 entryId) {
|
|
CharacterScheduleEntry &entry = supportData();
|
|
|
|
CharacterScheduleEntry *newEntry = Resources::getReference().
|
|
charSchedules().getEntry(entryId, entry.parent());
|
|
setSupportData(newEntry);
|
|
}
|
|
|
|
void CurrentActionEntry::saveToStream(Common::WriteStream *stream) {
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "Saving hotspot action entry dyn=%d id=%d",
|
|
hasSupportData(), hasSupportData() ? supportData().id() : 0);
|
|
stream->writeByte((uint8) _action);
|
|
stream->writeUint16LE(_roomNumber);
|
|
stream->writeByte(hasSupportData());
|
|
if (hasSupportData()) {
|
|
// Handle the support data
|
|
stream->writeByte(_dynamicSupportData);
|
|
if (_dynamicSupportData) {
|
|
// Write out the dynamic data
|
|
stream->writeByte(supportData().action());
|
|
stream->writeSint16LE(supportData().numParams());
|
|
for (int index = 0; index < supportData().numParams(); ++index)
|
|
stream->writeUint16LE(supportData().param(index));
|
|
} else {
|
|
// Write out the Id for the static entry
|
|
stream->writeUint16LE(supportData().id());
|
|
}
|
|
}
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "Finished saving hotspot action entry");
|
|
}
|
|
|
|
CurrentActionEntry *CurrentActionEntry::loadFromStream(Common::ReadStream *stream) {
|
|
Resources &res = Resources::getReference();
|
|
uint8 actionNum = stream->readByte();
|
|
if (actionNum == 0xff) return NULL;
|
|
CurrentActionEntry *result;
|
|
|
|
uint16 roomNumber = stream->readUint16LE();
|
|
bool hasSupportData = stream->readByte() != 0;
|
|
|
|
if (!hasSupportData) {
|
|
// An entry that doesn't have support data
|
|
result = new CurrentActionEntry(
|
|
(CurrentAction) actionNum, roomNumber);
|
|
} else {
|
|
// Handle support data for the entry
|
|
bool dynamicData = stream->readByte() != 0;
|
|
if (dynamicData) {
|
|
// Load action entry that has dynamic data
|
|
result = new CurrentActionEntry(
|
|
(CurrentAction) actionNum, roomNumber);
|
|
result->_supportData = new CharacterScheduleEntry();
|
|
Action action = (Action) stream->readByte();
|
|
int numParams = stream->readSint16LE();
|
|
uint16 *paramList = new uint16[numParams];
|
|
for (int index = 0; index < numParams; ++index)
|
|
paramList[index] = stream->readUint16LE();
|
|
|
|
result->_supportData->setDetails2(action, numParams, paramList);
|
|
delete[] paramList;
|
|
result->_dynamicSupportData = true;
|
|
} else {
|
|
// Load action entry with an NPC schedule entry
|
|
uint16 entryId = stream->readUint16LE();
|
|
CharacterScheduleEntry *entry = res.charSchedules().getEntry(entryId);
|
|
result = new CurrentActionEntry((CurrentAction) actionNum, roomNumber);
|
|
result->setSupportData(entry);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Common::String CurrentActionStack::getDebugInfo() const {
|
|
Common::String buffer;
|
|
ActionsList::const_iterator i;
|
|
|
|
buffer += Common::String::format("CurrentActionStack::list num_actions=%d\n", size());
|
|
|
|
for (i = _actions.begin(); i != _actions.end(); ++i) {
|
|
CurrentActionEntry *entry = (*i).get();
|
|
buffer += Common::String::format("style=%d room#=%d", entry->action(), entry->roomNumber());
|
|
|
|
if (entry->hasSupportData()) {
|
|
CharacterScheduleEntry &rec = entry->supportData();
|
|
|
|
buffer += Common::String::format(", action=%d params=", rec.action());
|
|
|
|
if (rec.numParams() == 0)
|
|
buffer += "none";
|
|
else {
|
|
buffer += Common::String::format("%d", rec.param(0));
|
|
for (int ctr = 1; ctr < rec.numParams(); ++ctr) {
|
|
buffer += Common::String::format(", %d", rec.param(ctr));
|
|
}
|
|
}
|
|
}
|
|
buffer += "\n";
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
void CurrentActionStack::saveToStream(Common::WriteStream *stream) {
|
|
ActionsList::iterator i;
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "Saving hotspot action stack");
|
|
Common::String buffer = getDebugInfo();
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "%s", buffer.c_str());
|
|
|
|
for (i = _actions.begin(); i != _actions.end(); ++i) {
|
|
CurrentActionEntry *rec = (*i).get();
|
|
rec->saveToStream(stream);
|
|
}
|
|
stream->writeByte(0xff); // End of list marker
|
|
debugC(ERROR_DETAILED, kLureDebugAnimations, "Finished saving hotspot action stack");
|
|
}
|
|
|
|
void CurrentActionStack::loadFromStream(Common::ReadStream *stream) {
|
|
CurrentActionEntry *rec;
|
|
|
|
_actions.clear();
|
|
while ((rec = CurrentActionEntry::loadFromStream(stream)) != NULL)
|
|
_actions.push_back(ActionsList::value_type(rec));
|
|
}
|
|
|
|
void CurrentActionStack::copyFrom(CurrentActionStack &stack) {
|
|
ActionsList::iterator i;
|
|
|
|
for (i = stack._actions.begin(); i != stack._actions.end(); ++i) {
|
|
CurrentActionEntry *rec = (*i).get();
|
|
_actions.push_back(ActionsList::value_type(new CurrentActionEntry(rec)));
|
|
}
|
|
}
|
|
|
|
} // End of namespace Lure
|