scummvm/engines/lastexpress/game/entities.cpp
2010-10-27 19:19:22 +00:00

2744 lines
78 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.
*
* $URL$
* $Id$
*
*/
#include "lastexpress/game/entities.h"
// Data
#include "lastexpress/data/scene.h"
#include "lastexpress/data/sequence.h"
// Entities
#include "lastexpress/entities/entity.h"
#include "lastexpress/entities/abbot.h"
#include "lastexpress/entities/alexei.h"
#include "lastexpress/entities/alouan.h"
#include "lastexpress/entities/anna.h"
#include "lastexpress/entities/august.h"
#include "lastexpress/entities/boutarel.h"
#include "lastexpress/entities/chapters.h"
#include "lastexpress/entities/cooks.h"
#include "lastexpress/entities/coudert.h"
#include "lastexpress/entities/entity39.h"
#include "lastexpress/entities/francois.h"
#include "lastexpress/entities/gendarmes.h"
#include "lastexpress/entities/hadija.h"
#include "lastexpress/entities/ivo.h"
#include "lastexpress/entities/kahina.h"
#include "lastexpress/entities/kronos.h"
#include "lastexpress/entities/mahmud.h"
#include "lastexpress/entities/max.h"
#include "lastexpress/entities/mertens.h"
#include "lastexpress/entities/milos.h"
#include "lastexpress/entities/mmeboutarel.h"
#include "lastexpress/entities/pascale.h"
#include "lastexpress/entities/rebecca.h"
#include "lastexpress/entities/salko.h"
#include "lastexpress/entities/servers0.h"
#include "lastexpress/entities/servers1.h"
#include "lastexpress/entities/sophie.h"
#include "lastexpress/entities/tables.h"
#include "lastexpress/entities/tatiana.h"
#include "lastexpress/entities/train.h"
#include "lastexpress/entities/vassili.h"
#include "lastexpress/entities/verges.h"
#include "lastexpress/entities/vesna.h"
#include "lastexpress/entities/yasmin.h"
// Game
#include "lastexpress/game/logic.h"
#include "lastexpress/game/savepoint.h"
#include "lastexpress/game/scenes.h"
#include "lastexpress/game/sound.h"
#include "lastexpress/game/state.h"
#include "lastexpress/graphics.h"
#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
#include "lastexpress/resource.h"
namespace LastExpress {
#define STORE_VALUE(data) ((uint)1 << (uint)data)
static const EntityPosition objectsPosition[8] = {kPosition_8200, kPosition_7500,
kPosition_6470, kPosition_5790,
kPosition_4840, kPosition_4070,
kPosition_3050, kPosition_2740};
static const EntityPosition entityPositions[41] = {
kPositionNone, kPosition_851, kPosition_1430, kPosition_2110, kPositionNone,
kPosition_2410, kPosition_2980, kPosition_3450, kPosition_3760, kPosition_4100,
kPosition_4680, kPosition_5140, kPosition_5440, kPosition_5810, kPosition_6410,
kPosition_6850, kPosition_7160, kPosition_7510, kPosition_8514, kPositionNone,
kPositionNone, kPositionNone, kPosition_2086, kPosition_2690, kPositionNone,
kPosition_3110, kPosition_3390, kPosition_3890, kPosition_4460, kPosition_4770,
kPosition_5090, kPosition_5610, kPosition_6160, kPosition_6460, kPosition_6800,
kPosition_7320, kPosition_7870, kPosition_8160, kPosition_8500, kPosition_9020,
kPosition_9269};
#define ADD_ENTITY(class) \
_entities.push_back(new class(engine));
#define COMPUTE_SEQUENCE_NAME(sequenceTo, sequenceFrom) { \
sequenceTo = sequenceFrom; \
for (int seqIdx = 0; seqIdx < 7; seqIdx++) \
sequenceTo.deleteLastChar(); \
if (isInsideTrainCar(entityIndex, kCarGreenSleeping) || isInsideTrainCar(entityIndex, kCarGreenSleeping)) { \
if (data->car < getData(kEntityPlayer)->car || (data->car == getData(kEntityPlayer)->car && data->entityPosition < getData(kEntityPlayer)->entityPosition)) \
sequenceTo += "R.SEQ"; \
else \
sequenceTo += "F.SEQ"; \
} else { \
sequenceTo += ".SEQ"; \
} \
}
#define TRY_LOAD_SEQUENCE(sequence, name, name1, name2) { \
if (data->car == getData(kEntityPlayer)->car) \
sequence = loadSequence1(name1, field30); \
if (sequence) { \
name = name1; \
} else { \
if (name2 != "") \
sequence = loadSequence1(name2, field30); \
name = (sequence ? name2 : ""); \
} \
}
//////////////////////////////////////////////////////////////////////////
// Entities
//////////////////////////////////////////////////////////////////////////
Entities::Entities(LastExpressEngine *engine) : _engine(engine) {
_header = new EntityData();
_entities.push_back(NULL); // Header
ADD_ENTITY(Anna);
ADD_ENTITY(August);
ADD_ENTITY(Mertens);
ADD_ENTITY(Coudert);
ADD_ENTITY(Pascale);
ADD_ENTITY(Servers0);
ADD_ENTITY(Servers1);
ADD_ENTITY(Cooks);
ADD_ENTITY(Verges);
ADD_ENTITY(Tatiana);
ADD_ENTITY(Vassili);
ADD_ENTITY(Alexei);
ADD_ENTITY(Abbot);
ADD_ENTITY(Milos);
ADD_ENTITY(Vesna);
ADD_ENTITY(Ivo);
ADD_ENTITY(Salko);
ADD_ENTITY(Kronos);
ADD_ENTITY(Kahina);
ADD_ENTITY(Francois);
ADD_ENTITY(MmeBoutarel);
ADD_ENTITY(Boutarel);
ADD_ENTITY(Rebecca);
ADD_ENTITY(Sophie);
ADD_ENTITY(Mahmud);
ADD_ENTITY(Yasmin);
ADD_ENTITY(Hadija);
ADD_ENTITY(Alouan);
ADD_ENTITY(Gendarmes);
ADD_ENTITY(Max);
ADD_ENTITY(Chapters);
ADD_ENTITY(Train);
// Special case for tables
_entities.push_back(new Tables(engine, kEntityTables0));
_entities.push_back(new Tables(engine, kEntityTables1));
_entities.push_back(new Tables(engine, kEntityTables2));
_entities.push_back(new Tables(engine, kEntityTables3));
_entities.push_back(new Tables(engine, kEntityTables4));
_entities.push_back(new Tables(engine, kEntityTables5));
ADD_ENTITY(Entity39);
// Init compartments & positions
memset(&_compartments, 0, sizeof(_compartments));
memset(&_compartments1, 0, sizeof(_compartments1));
memset(&_positions, 0, sizeof(_positions));
}
Entities::~Entities() {
delete _header;
for (int i = 0; i < (int)_entities.size(); i++)
delete _entities[i];
// Zero passed pointers
_engine = NULL;
}
//////////////////////////////////////////////////////////////////////////
// Accessors
//////////////////////////////////////////////////////////////////////////
Entity *Entities::get(EntityIndex entity) {
assert((uint)entity < _entities.size());
if (entity == kEntityPlayer)
error("Cannot get entity for index == 0!");
return _entities[entity];
}
EntityData::EntityCallData *Entities::getData(EntityIndex entity) const {
assert((uint)entity < _entities.size());
if (entity == kEntityPlayer)
return _header->getCallData();
return _entities[entity]->getData();
}
int Entities::getPosition(CarIndex car, Position position) const {
int index = 100 * car + position;
if (car > 10)
error("Entities::getPosition: trying to access an invalid car (was: %d, valid:0-9)", car);
if (position > 100)
error("Entities::getPosition: trying to access an invalid position (was: %d, valid:0-100)", position);
return _positions[index];
}
int Entities::getCompartments(int index) const {
if (index >= _compartmentsCount)
error("Entities::getCompartments: trying to access an invalid compartment (was: %d, valid:0-15)", index);
return _compartments[index];
}
int Entities::getCompartments1(int index) const {
if (index >= _compartmentsCount)
error("Entities::getCompartments: trying to access an invalid compartment (was: %d, valid:0-15)", index);
return _compartments1[index];
}
//////////////////////////////////////////////////////////////////////////
// Savegame
//////////////////////////////////////////////////////////////////////////
void Entities::saveLoadWithSerializer(Common::Serializer &s) {
_header->saveLoadWithSerializer(s);
for (uint i = 1; i < _entities.size(); i++)
_entities[i]->saveLoadWithSerializer(s);
}
void Entities::savePositions(Common::Serializer &s) {
for (uint i = 0; i < (uint)_positionsCount; i++)
s.syncAsUint32LE(_positions[i]);
}
void Entities::saveCompartments(Common::Serializer &s) {
for (uint i = 0; i < (uint)_compartmentsCount; i++)
s.syncAsUint32LE(_compartments[i]);
for (uint i = 0; i < (uint)_compartmentsCount; i++)
s.syncAsUint32LE(_compartments1[i]);
}
//////////////////////////////////////////////////////////////////////////
// Setup
//////////////////////////////////////////////////////////////////////////
void Entities::setup(bool isFirstChapter, EntityIndex entityIndex) {
setupChapter(isFirstChapter ? kChapter1 : kChapterAll);
bool flag_4 = false;
if (!isFirstChapter) {
getFlags()->flag_4 = false;
if (entityIndex) {
getSavePoints()->call(kEntityPlayer, entityIndex, kActionNone);
flag_4 = getFlags()->flag_4;
}
}
getFlags()->flag_4 = flag_4;
if (!getFlags()->flag_4)
getScenes()->loadScene(getState()->scene);
}
void Entities::setupChapter(ChapterIndex chapter) {
if (chapter) {
// Reset current call, inventory item & draw sequences
for (uint i = 1; i < _entities.size(); i++) {
getData((EntityIndex)i)->currentCall = 0;
getData((EntityIndex)i)->inventoryItem = kItemNone;
clearSequences((EntityIndex)i);
}
// Init compartments & positions
memset(&_compartments, 0, sizeof(_compartments));
memset(&_compartments1, 0, sizeof(_compartments1));
memset(&_positions, 0, sizeof(_positions));
getSound()->resetQueue(SoundManager::kSoundType13);
}
// we skip the header when doing entity setup
for (uint i = 1; i < _entities.size(); i++) {
// Special case of chapters (prevents infinite loop as we will be called from Chapters functions when changing chapters)
if (i == kEntityChapters && chapter >= 2)
continue;
_entities[i]->setup(chapter);
}
}
void Entities::reset() {
// Reset header
delete _header;
_header = new EntityData();
for (uint i = 1; i < _entities.size(); i++)
resetSequences((EntityIndex)i);
getScenes()->resetDoorsAndClock();
}
//////////////////////////////////////////////////////////////////////////
// State & Sequences
//////////////////////////////////////////////////////////////////////////
EntityIndex Entities::canInteractWith(const Common::Point &point) const {
if (!getFlags()->isGameRunning)
return kEntityPlayer;
EntityIndex index = kEntityPlayer;
int location = 10000;
// Check if there is an entity we can interact with
for (uint i = 0; i < _entities.size(); i++) {
// Skip entities with no current frame
if (!getData((EntityIndex)i)->frame)
continue;
FrameInfo *info = getData((EntityIndex)i)->frame->getInfo();
// Check the hotspot
if (info->hotspot.contains(point)) {
// If closer to us, update with its values
if (location > info->location) {
location = info->location;
index = (EntityIndex)i;
}
}
}
// Check if we found an entity
if (!index)
return kEntityPlayer;
// Check that there is an item to interact with
if (!getData(index)->inventoryItem)
return kEntityPlayer;
return index;
}
void Entities::resetState(EntityIndex entityIndex) {
getData(entityIndex)->currentCall = 0;
getData(entityIndex)->inventoryItem = kItemNone;
if (getSound()->isBuffered(entityIndex))
getSound()->removeFromQueue(entityIndex);
clearSequences(entityIndex);
if (entityIndex == kEntity39)
entityIndex = kEntityPlayer;
if (entityIndex > kEntityChapters)
return;
// reset compartments and positions for this entity
for (int i = 0; i < _positionsCount; i++)
_positions[i] &= ~STORE_VALUE(entityIndex);
for (int i = 0; i < _compartmentsCount; i++) {
_compartments[i] &= ~STORE_VALUE(entityIndex);
_compartments1[i] &= ~STORE_VALUE(entityIndex);
}
getLogic()->updateCursor();
}
void Entities::updateFields() const {
if (!getFlags()->isGameRunning)
return;
for (int i = 0; i < (int)_entities.size(); i++) {
if (!getSavePoints()->getCallback((EntityIndex)i))
continue;
EntityData::EntityCallData *data = getData((EntityIndex)i);
int positionDelta = data->field_4A3 * 10;
switch (data->direction) {
default:
break;
case kDirectionUp:
if (data->entityPosition >= 10000 - positionDelta)
data->entityPosition = (EntityPosition)(data->entityPosition + positionDelta);
break;
case kDirectionDown:
if (data->entityPosition > positionDelta)
data->entityPosition = (EntityPosition)(data->entityPosition - positionDelta);
break;
case kDirectionLeft:
data->currentFrame++;
break;
case kDirectionRight:
data->field_4A1 += 9;
break;
case kDirectionSwitch:
if (data->directionSwitch == kDirectionRight)
data->field_4A1 += 9;
break;
}
}
}
void Entities::updateFrame(EntityIndex entityIndex) const {
Sequence *sequence = NULL;
int16 *currentFrame = NULL;
bool found = false;
if (getData(entityIndex)->direction == kDirectionSwitch) {
sequence = getData(entityIndex)->sequence2;
currentFrame = &getData(entityIndex)->currentFrame2;
} else {
sequence = getData(entityIndex)->sequence;
currentFrame = &getData(entityIndex)->currentFrame;
}
if (!sequence)
return;
// Save current values
int16 oldFrame = *currentFrame;
int16 field_4A1 = getData(entityIndex)->field_4A1;
do {
// Check we do not get past the end
if (*currentFrame >= (int)sequence->count() - 1)
break;
// Get the proper frame
FrameInfo *info = sequence->getFrameInfo((uint16)*currentFrame);
if (info->field_33 & 8) {
found = true;
} else {
if (info->soundAction == 35)
found = true;
getData(entityIndex)->field_4A1 += info->field_30;
// Progress to the next frame
++*currentFrame;
}
} while (!found);
// Restore old values
if (!found) {
*currentFrame = oldFrame;
getData(entityIndex)->field_4A1 = field_4A1;
}
}
void Entities::updateSequences() const {
if (!getFlags()->isGameRunning)
return;
// Update the train clock & doors
getScenes()->updateDoorsAndClock();
//////////////////////////////////////////////////////////////////////////
// First pass: Drawing
//////////////////////////////////////////////////////////////////////////
for (uint i = 1; i < _entities.size(); i++) {
EntityIndex entityIndex = (EntityIndex)i;
if (!getSavePoints()->getCallback(entityIndex))
continue;
EntityData::EntityCallData *data = getData(entityIndex);
if (data->frame) {
getScenes()->removeFromQueue(data->frame);
SAFE_DELETE(data->frame);
}
if (data->frame1) {
getScenes()->removeFromQueue(data->frame1);
SAFE_DELETE(data->frame1);
}
if (data->direction == kDirectionSwitch) {
// Clear sequence 2
if (data->sequence)
SAFE_DELETE(data->sequence);
// Replace by sequence 3 if available
if (data->sequence2) {
data->sequence = data->sequence2;
data->sequenceName = data->sequenceName2;
data->sequence2 = NULL;
data->sequenceName2 = "";
}
data->direction = data->directionSwitch;
data->currentFrame = -1;
data->field_49B = 0;
}
// Draw sequences
drawSequences(entityIndex, data->direction, false);
}
//////////////////////////////////////////////////////////////////////////
// Second pass: Load sequences for next pass
//////////////////////////////////////////////////////////////////////////
for (uint i = 1; i < _entities.size(); i++) {
EntityIndex entityIndex = (EntityIndex)i;
if (!getSavePoints()->getCallback(entityIndex))
continue;
EntityData::EntityCallData *data = getData(entityIndex);
byte field30 = (data->direction == kDirectionLeft ? entityIndex + 35 : 15);
if (data->sequenceName != "" && !data->sequence) {
data->sequence = loadSequence1(data->sequenceName, field30);
// If sequence 2 was loaded correctly, remove the copied name
// otherwise, compute new name
if (data->sequence) {
data->sequenceNameCopy = "";
} else {
Common::String sequenceName;
// Left and down directions
if (data->direction == kDirectionLeft || data->direction == kDirectionRight) {
COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName);
// Try loading the sequence
data->sequence = loadSequence1(sequenceName, field30);
}
// Update sequence names
data->sequenceNameCopy = (data->sequence ? "" : data->sequenceName);
data->sequenceName = (data->sequence ? sequenceName : "");
}
}
// Update sequence 3
if (data->sequenceName2 != "" && !data->sequence2) {
if (data->car == getData(kEntityPlayer)->car)
data->sequence2 = loadSequence1(data->sequenceName2, field30);
if (!data->sequence2) {
Common::String sequenceName;
// Left and down directions
if (data->directionSwitch == kDirectionLeft || data->directionSwitch == kDirectionRight) {
COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName2);
// Try loading the sequence
data->sequence2 = loadSequence1(sequenceName, field30);
}
// Update sequence names
data->sequenceName2 = (data->sequence2 ? sequenceName : "");
}
}
}
}
void Entities::resetSequences(EntityIndex entityIndex) const {
// Reset direction
if (getData(entityIndex)->direction == kDirectionSwitch) {
getData(entityIndex)->direction = getData(entityIndex)->directionSwitch;
getData(entityIndex)->field_49B = 0;
getData(entityIndex)->currentFrame = -1;
}
// FIXME: in the original engine, the sequence pointers might just be copies,
// make sure we free the associated memory at some point
getData(entityIndex)->frame = NULL;
getData(entityIndex)->frame1 = NULL;
SAFE_DELETE(getData(entityIndex)->sequence);
SAFE_DELETE(getData(entityIndex)->sequence2);
SAFE_DELETE(getData(entityIndex)->sequence3);
getData(entityIndex)->field_4A9 = false;
getData(entityIndex)->field_4AA = false;
strcpy((char*)&getData(entityIndex)->sequenceNameCopy, "");
strcpy((char*)&getData(entityIndex)->sequenceName, "");
strcpy((char*)&getData(entityIndex)->sequenceName2, "");
// Original engine resets flag to decompress data on the fly (we don't need to do that)
}
//////////////////////////////////////////////////////////////////////////
// Callbacks
//////////////////////////////////////////////////////////////////////////
void Entities::updateCallbacks() {
if (!getFlags()->isGameRunning)
return;
getFlags()->flag_entities_0 = false;
if (getFlags()->flag_entities_1) {
executeCallbacks();
getFlags()->flag_entities_0 = true;
} else {
getFlags()->flag_entities_1 = true;
executeCallbacks();
getFlags()->flag_entities_1 = false;
}
}
void Entities::executeCallbacks() {
for (uint i = 1; i < _entities.size(); i++) {
if (getFlags()->flag_entities_0)
break;
if (getSavePoints()->getCallback((EntityIndex)i))
processEntity((EntityIndex)i);
}
if (getFlags()->flag_entities_0)
return;
bool processed = true;
do {
processed = true;
for (int i = 1; i < (int)_entities.size(); i++) {
if (getFlags()->flag_entities_0)
break;
if (getSavePoints()->getCallback((EntityIndex)i)) {
if (getData((EntityIndex)i)->doProcessEntity) {
processed = false;
processEntity((EntityIndex)i);
}
}
}
} while (!processed);
}
//////////////////////////////////////////////////////////////////////////
// Processing
//////////////////////////////////////////////////////////////////////////
#define INCREMENT_DIRECTION_COUNTER() { \
data->doProcessEntity = false; \
if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) \
++data->field_4A1; \
}
void Entities::processEntity(EntityIndex entityIndex) {
EntityData::EntityCallData *data = getData(entityIndex);
bool keepPreviousFrame = false;
data->doProcessEntity = false;
if (getData(kEntityPlayer)->car != data->car && data->direction != kDirectionRight && data->direction != kDirectionSwitch) {
if (data->position) {
updatePositionExit(entityIndex, data->car2, data->position);
data->car2 = kCarNone;
data->position = 0;
}
getScenes()->removeAndRedraw(&data->frame, false);
getScenes()->removeAndRedraw(&data->frame1, false);
INCREMENT_DIRECTION_COUNTER();
return;
}
if (data->frame1) {
getScenes()->removeAndRedraw(&data->frame1, false);
if (data->frame && data->frame->getInfo()->subType != kFrameType3) {
data->frame->getInfo()->subType = kFrameTypeNone;
getScenes()->setFlagDrawSequences();
}
}
SAFE_DELETE(data->sequence3);
if (!data->frame || !data->direction) {
if (!data->sequence)
label_nosequence:
drawSequences(entityIndex, data->direction, true);
data->doProcessEntity = false;
computeCurrentFrame(entityIndex);
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
if (data->sequence && data->currentFrame != -1 && data->currentFrame <= (int16)(data->sequence->count() - 1)) {
processFrame(entityIndex, false, true);
if (!getFlags()->flag_entities_0 && !data->doProcessEntity) {
INCREMENT_DIRECTION_COUNTER();
return;
}
} else {
if (data->direction == kDirectionRight && data->field_4A1 > 100) {
getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
if (data->position) {
updatePositionExit(entityIndex, data->car2, data->position);
data->car2 = kCarNone;
data->position = 0;
}
INCREMENT_DIRECTION_COUNTER();
}
return;
}
if (!data->sequence)
goto label_nosequence;
if (data->frame->getInfo()->field_30 > data->field_49B + 1 || (data->direction == kDirectionLeft && data->sequence->count() == 1)) {
++data->field_49B;
INCREMENT_DIRECTION_COUNTER();
return;
}
if (data->frame->getInfo()->field_30 > data->field_49B && !data->frame->getInfo()->keepPreviousFrame) {
++data->field_49B;
INCREMENT_DIRECTION_COUNTER();
return;
}
if (data->frame->getInfo()->keepPreviousFrame == 1)
keepPreviousFrame = true;
// Increment current frame
++data->currentFrame;
if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) {
if (data->direction == kDirectionLeft) {
data->currentFrame = 0;
} else {
keepPreviousFrame = true;
drawNextSequence(entityIndex);
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
if (!data->sequence2) {
updateEntityPosition(entityIndex);
data->doProcessEntity = false;
return;
}
copySequenceData(entityIndex);
}
}
processFrame(entityIndex, keepPreviousFrame, false);
if (!getFlags()->flag_entities_0 && !data->doProcessEntity)
INCREMENT_DIRECTION_COUNTER();
}
void Entities::computeCurrentFrame(EntityIndex entityIndex) const {
EntityData::EntityCallData *data = getData(entityIndex);
int16 originalCurrentFrame = data->currentFrame;
if (!data->sequence) {
data->currentFrame = -1;
return;
}
switch (data->direction) {
default:
break;
case kDirectionNone:
case kDirectionSwitch:
data->currentFrame = -1;
break;
case kDirectionUp:
case kDirectionDown: {
Scene *scene = getScenes()->get(getState()->scene);
if (scene->position > 40)
break;
switch (scene->position) {
default:
case 4:
case 19:
case 20:
case 21:
case 24:
break;
case 1:
case 18:
case 22:
case 40:
data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
break;
case 2:
case 3:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
if (data->field_4A9) {
if (getData(kEntityPlayer)->entityPosition >= data->entityPosition) {
data->currentFrame = -1;
} else {
data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
if (data->currentFrame < (int)(data->sequence->count() - 2))
data->currentFrame += 2;
}
} else {
data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
}
break;
case 23:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
if (data->field_4A9) {
if (getData(kEntityPlayer)->entityPosition <= data->entityPosition) {
data->currentFrame = -1;
} else {
data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
if (data->currentFrame < (int)(data->sequence->count() - 2))
data->currentFrame += 2;
}
} else {
data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
}
break;
}
}
break;
case kDirectionLeft:
if (data->currentFrame == -1 || data->currentFrame >= (int32)data->sequence->count()) {
data->currentFrame = 0;
data->field_49B = 0;
}
break;
case kDirectionRight:
bool found = false;
bool flag = false;
uint16 frameIndex = 0;
byte field30 = 0;
int16 currentFrameCopy = (!data->currentFrame && !data->field_4A1) ? -1 : data->currentFrame;
// Process frames
do {
if (frameIndex >= data->sequence->count())
break;
FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
if (field30 + info->field_30 >= data->field_4A1) {
found = true;
break;
}
if (field30 > data->field_4A1 - 10) {
if (info->soundAction)
getSound()->playSoundEvent(entityIndex, info->soundAction, (field30 <= data->field_4A1 - info->field_31) ? 0 : (byte)(field30 + info->field_31 - data->field_4A1));
}
field30 += info->field_30;
if (info->field_33 & 4)
flag = true;
if (info->field_33 & 2) {
flag = false;
getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
if (info->field_33 & 16) {
getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
frameIndex++;
} while (!found);
if (found) {
if (flag) {
bool found2 = false;
do {
if (frameIndex >= data->sequence->count())
break;
FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
if (info->field_33 & 2) {
found2 = true;
getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
} else {
data->field_4A1 += info->field_30;
byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
if (soundAction)
getSound()->playSoundEvent(entityIndex, soundAction);
++frameIndex;
}
} while (!found2);
if (found2) {
data->currentFrame = frameIndex;
data->field_49B = 0;
byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
if (soundAction && data->currentFrame != currentFrameCopy)
getSound()->playSoundEvent(entityIndex, soundAction, field31);
} else {
data->currentFrame = (int16)(data->sequence->count() - 1);
data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
}
} else {
data->currentFrame = frameIndex;
data->field_49B = data->field_4A1 - field30;
byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
if (soundAction && data->currentFrame != currentFrameCopy)
getSound()->playSoundEvent(entityIndex, soundAction, field31 <= data->field_49B ? 0 : (byte)(field31 - data->field_49B));
}
} else {
data->currentFrame = (int16)(data->sequence->count() - 1);
data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
getSavePoints()->process();
}
break;
}
}
int16 Entities::getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const {
EntityData::EntityCallData *data = getData(entity);
EntityPosition firstFramePosition = sequence->getFrameInfo(0)->entityPosition;
EntityPosition lastFramePosition = sequence->getFrameInfo(sequence->count() - 1)->entityPosition;
bool isGoingForward = (firstFramePosition < lastFramePosition);
if (!doProcessing) {
if (!isGoingForward) {
if (data->field_4A3 + firstFramePosition < data->entityPosition || lastFramePosition - data->field_4A3 > data->entityPosition)
return -1;
} else {
if (firstFramePosition - data->field_4A3 > data->entityPosition || lastFramePosition + data->field_4A3 < data->entityPosition)
return -1;
}
}
if (sequence->count() == 0)
return 0;
// Search for the correct frame
// TODO: looks slightly like some sort of binary search
uint16 frame = 0;
uint16 numFrames = sequence->count() - 1;
for (;;) {
uint16 currentFrame = (frame + numFrames) / 2;
if (position + sequence->getFrameInfo(currentFrame)->entityPosition <= data->entityPosition) {
if (!isGoingForward)
numFrames = (frame + numFrames) / 2;
else
frame = (frame + numFrames) / 2;
} else {
if (isGoingForward)
numFrames = (frame + numFrames) / 2;
else
frame = (frame + numFrames) / 2;
}
if (numFrames - frame == 1) {
uint16 lastFramePos = ABS(position - (sequence->getFrameInfo(numFrames)->entityPosition + data->entityPosition));
uint16 framePosition = ABS(position - (sequence->getFrameInfo(frame)->entityPosition + data->entityPosition));
return (framePosition > lastFramePos) ? numFrames : frame;
}
if (numFrames <= frame)
return currentFrame;
}
}
void Entities::processFrame(EntityIndex entityIndex, bool keepPreviousFrame, bool dontPlaySound) {
EntityData::EntityCallData *data = getData(entityIndex);
// Set frame to be drawn again
if (data->frame && keepPreviousFrame) {
if (data->frame->getInfo()->subType != kFrameType3)
data->frame->getInfo()->subType = kFrameType2;
getScenes()->setFlagDrawSequences();
}
// Remove old frame from queue
if (data->frame && !keepPreviousFrame)
getScenes()->removeFromQueue(data->frame);
// Stop if nothing else to draw
if (data->currentFrame < 0)
return;
if (data->currentFrame > (int)data->sequence->count())
return;
// Get new frame info
FrameInfo *info = data->sequence->getFrameInfo((uint16)data->currentFrame);
if (data->frame && data->frame->getInfo()->subType != kFrameType3)
if (!info->field_2E || keepPreviousFrame)
getScenes()->setCoordinates(data->frame);
// Update position
if (info->entityPosition) {
data->entityPosition = info->entityPosition;
if (data->field_4A9)
data->entityPosition = (EntityPosition)(data->entityPosition + getEntityPositionFromCurrentPosition());
}
info->location = entityIndex + ABS(getData(entityIndex)->entityPosition - getData(kEntityPlayer)->entityPosition);
if (info->subType != kFrameType3) {
info->subType = kFrameType1;
if (!keepPreviousFrame)
info->subType = kFrameTypeNone;
}
if (info->field_33 & 1)
getSavePoints()->push(kEntityPlayer, entityIndex, kActionExcuseMeCath);
if (info->field_33 & 2) {
getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
if (info->field_33 & 16) {
getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
if (data->position) {
updatePositionExit(entityIndex, data->car2, data->position);
data->car2 = kCarNone;
data->position = 0;
}
if (info->position) {
data->car2 = data->car;
data->position = info->position;
updatePositionEnter(entityIndex, data->car2, data->position);
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
if (info->soundAction && !dontPlaySound)
getSound()->playSoundEvent(entityIndex, info->soundAction, info->field_31);
// Add the new frame to the queue
SequenceFrame *frame = new SequenceFrame(data->sequence, (uint16)data->currentFrame);
getScenes()->addToQueue(frame);
// Keep previous frame if needed and store the new frame
if (keepPreviousFrame)
data->frame1 = data->frame;
data->frame = frame;
if (!dontPlaySound)
data->field_49B = keepPreviousFrame ? 0 : 1;
}
void Entities::drawNextSequence(EntityIndex entityIndex) const {
EntityData::EntityCallData *data = getData(entityIndex);
if (data->direction == kDirectionRight) {
getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
getSavePoints()->process();
if (getFlags()->flag_entities_0 || data->doProcessEntity)
return;
}
if (!isDirectionUpOrDown(entityIndex))
return;
if (data->sequence2)
return;
if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingAtDoors))
return;
if (getData(kEntityPlayer)->car != data->car)
return;
if (!data->field_4A9 || isWalkingOppositeToPlayer(entityIndex)) {
if (!data->field_4A9 && isWalkingOppositeToPlayer(entityIndex)) {
data->entityPosition = kPosition_2088;
if (data->direction != kDirectionUp)
data->entityPosition = kPosition_8512;
drawSequences(entityIndex, data->direction, true);
}
} else {
data->entityPosition = kPosition_8514;
if (data->direction != kDirectionUp)
data->entityPosition = kPosition_2086;
drawSequences(entityIndex, data->direction, true);
}
}
void Entities::updateEntityPosition(EntityIndex entityIndex) const {
EntityData::EntityCallData *data = getData(entityIndex);
getScenes()->removeAndRedraw(&data->frame, false);
SAFE_DELETE(data->frame1);
data->field_49B = 0;
if (isDirectionUpOrDown(entityIndex)
&& (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
&& data->car == getData(kEntityPlayer)->car) {
if (isWalkingOppositeToPlayer(entityIndex)) {
data->entityPosition = getData(kEntityPlayer)->entityPosition;
} else if (data->field_4A9) {
data->entityPosition = (data->direction == kDirectionUp) ? kPosition_8514 : kPosition_2086;
} else {
if (isPlayerPosition(kCarGreenSleeping, 1) || isPlayerPosition(kCarGreenSleeping, 40)
|| isPlayerPosition(kCarRedSleeping, 1) || isPlayerPosition(kCarRedSleeping, 40)) {
data->entityPosition = (data->direction == kDirectionUp) ? kPosition_2588 : kPosition_8012;
} else {
data->entityPosition = (data->direction == kDirectionUp) ? kPosition_9271 : kPosition_849;
}
}
}
SAFE_DELETE(data->sequence);
data->sequenceName = "";
data->field_4A9 = false;
if (data->directionSwitch)
data->direction = data->directionSwitch;
}
void Entities::copySequenceData(EntityIndex entityIndex) const {
EntityData::EntityCallData *data = getData(entityIndex);
if (data->sequence)
data->sequence3 = data->sequence;
data->sequence = data->sequence2;
data->sequenceName = data->sequenceName2;
data->field_4A9 = data->field_4AA;
if (data->directionSwitch)
data->direction = data->directionSwitch;
// Clear sequence 3
data->sequence2 = NULL;
data->sequenceName2 = "";
data->field_4AA = false;
data->directionSwitch = kDirectionNone;
if (data->field_4A9) {
computeCurrentFrame(entityIndex);
if (data->currentFrame == -1)
data->currentFrame = 0;
} else {
data->currentFrame = data->currentFrame2;
data->currentFrame2 = 0;
if (data->currentFrame == -1)
data->currentFrame = 0;
}
}
//////////////////////////////////////////////////////////////////////////
// Drawing
//////////////////////////////////////////////////////////////////////////
void Entities::drawSequenceLeft(EntityIndex index, const char *sequence) const {
drawSequence(index, sequence, kDirectionLeft);
}
void Entities::drawSequenceRight(EntityIndex index, const char *sequence) const {
drawSequence(index, sequence, kDirectionRight);
}
void Entities::clearSequences(EntityIndex entityIndex) const {
debugC(8, kLastExpressDebugLogic, "Clear sequences for entity %s", ENTITY_NAME(entityIndex));
EntityData::EntityCallData *data = getData(entityIndex);
getScenes()->removeAndRedraw(&data->frame, false);
getScenes()->removeAndRedraw(&data->frame1, false);
if (data->sequence2) {
SAFE_DELETE(data->sequence2);
data->sequenceName2 = "";
data->field_4AA = false;
data->directionSwitch = kDirectionNone;
}
if (data->sequence) {
SAFE_DELETE(data->sequence);
data->sequenceName = "";
data->field_4A9 = false;
data->currentFrame = -1;
}
data->sequenceNamePrefix = "";
data->direction = kDirectionNone;
data->doProcessEntity = true;
}
void Entities::drawSequence(EntityIndex index, const char *sequence, EntityDirection direction) const {
debugC(8, kLastExpressDebugLogic, "Drawing sequence %s for entity %s with direction %s", sequence, ENTITY_NAME(index), DIRECTION_NAME(direction));
// Copy sequence name
getData(index)->sequenceNamePrefix = sequence;
getData(index)->sequenceNamePrefix.toUppercase();
getData(index)->sequenceNamePrefix += "-";
// Reset fields
getData(index)->field_49B = 0;
getData(index)->currentFrame = 0;
getData(index)->field_4A1 = 0;
drawSequences(index, direction, true);
}
void Entities::drawSequences(EntityIndex entityIndex, EntityDirection direction, bool loadSequence) const {
EntityData::EntityCallData *data = getData(entityIndex);
// Compute value for loading sequence depending on direction
byte field30 = (direction == kDirectionLeft ? entityIndex + 35 : 15);
data->doProcessEntity = true;
bool field4A9 = data->field_4A9;
// First case: different car and not going right: cleanup and return
if (data->car != getData(kEntityPlayer)->car && direction != kDirectionRight) {
clearEntitySequenceData(data, direction);
return;
}
data->directionSwitch = kDirectionNone;
// Process sequence names
Common::String sequenceName;
Common::String sequenceName1;
Common::String sequenceName2;
Common::String sequenceName3;
getSequenceName(entityIndex, direction, sequenceName1, sequenceName2);
// No sequence 1: cleanup and return
if (sequenceName1 == "") {
clearEntitySequenceData(data, direction);
return;
}
if (sequenceName1 == data->sequenceNameCopy) {
data->direction = direction;
return;
}
if (direction == kDirectionLeft || direction == kDirectionRight) {
COMPUTE_SEQUENCE_NAME(sequenceName, sequenceName1);
if (sequenceName3 != "")
COMPUTE_SEQUENCE_NAME(sequenceName3, sequenceName2);
}
if (!data->frame) {
data->direction = direction;
if (sequenceName1 == data->sequenceName) {
if (sequenceName2 == "")
return;
loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
return;
}
SAFE_DELETE(data->sequence);
if (sequenceName1 != data->sequenceName2) {
if (loadSequence) {
if (data->car == getData(kEntityPlayer)->car)
data->sequence = loadSequence1(sequenceName1, field30);
if (data->sequence) {
data->sequenceName = sequenceName1;
data->sequenceNameCopy = "";
} else {
if (sequenceName != "")
data->sequence = loadSequence1(sequenceName, field30);
data->sequenceName = (data->sequence ? sequenceName : "");
data->sequenceNameCopy = (data->sequence ? "" : sequenceName1);
}
} else {
data->sequenceName = sequenceName1;
}
if (sequenceName2 != "") {
loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
return;
}
if (!data->sequence2) {
if (sequenceName2 == "")
return;
loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
return;
}
SAFE_DELETE(data->sequence2);
} else {
data->sequence = data->sequence2;
data->sequenceName = data->sequenceName2;
data->sequence2 = NULL;
}
data->sequenceName2 = "";
if (sequenceName2 == "")
return;
loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
return;
}
if (data->sequenceName != sequenceName1) {
if (data->sequenceName2 != sequenceName1) {
SAFE_DELETE(data->sequence2);
TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName1, sequenceName);
}
data->field_4AA = data->field_4A9;
if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
data->currentFrame2 = 0;
} else {
data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
if (data->currentFrame2 == -1) {
clearSequences(entityIndex);
return;
}
}
data->field_4A9 = field4A9;
data->field_49B = data->frame->getInfo()->field_30;
data->currentFrame = (int16)(data->sequence->count() - 1);
data->direction = kDirectionSwitch;
data->directionSwitch = direction;
} else {
SAFE_DELETE(data->sequence2);
data->sequence2 = loadSequence1(data->sequence->getName(), data->sequence->getField30());
data->sequenceName2 = data->sequenceName;
data->field_4AA = data->field_4A9;
data->field_49B = data->frame->getInfo()->field_30;
data->currentFrame = (int16)(data->sequence->count() - 1);
data->direction = kDirectionSwitch;
data->directionSwitch = direction;
if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
data->currentFrame2 = 0;
} else {
data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
if (data->currentFrame2 == -1)
clearSequences(entityIndex);
}
}
}
void Entities::loadSequence2(EntityIndex entityIndex, Common::String sequenceName, Common::String sequenceName2, byte field30, bool reloadSequence) const {
EntityData::EntityCallData *data = getData(entityIndex);
if (data->sequenceName2 == sequenceName)
return;
if (data->sequence2)
SAFE_DELETE(data->sequence2);
if (reloadSequence) {
TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName, sequenceName2);
} else {
data->sequenceName2 = sequenceName;
}
}
void Entities::getSequenceName(EntityIndex index, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const {
EntityData::EntityCallData *data = getData(index);
Position position = getScenes()->get(getState()->scene)->position;
// reset fields
data->field_4A9 = false;
data->field_4AA = false;
switch (direction) {
default:
break;
case kDirectionUp:
switch (position) {
default:
break;
case 1:
if (data->entityPosition < kPosition_2587)
sequence1 = Common::String::printf("%02d%01d-01u.seq", index, data->clothes);
break;
case 2:
case 3:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
if (data->entityPosition >= kPosition_9270)
break;
if (data->entityPosition >= kPosition_8513) {
sequence1 = Common::String::printf("%02d%01d-%02deu.seq", index, data->clothes, position);
} else {
sequence1 = Common::String::printf("%02d%01d-03u.seq", index, data->clothes);
sequence2 = Common::String::printf("%02d%01d-%02deu.seq", index, data->clothes, position);
data->field_4A9 = true;
}
break;
case 18:
if (data->entityPosition < kPosition_9270)
sequence1 = Common::String::printf("%02d%01d-18u.seq", index, data->clothes);
break;
case 22:
if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
sequence1 = Common::String::printf("%02d%01d-22u.seq", index, data->clothes);
break;
case 23:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
if (getData(kEntityPlayer)->entityPosition <= data->entityPosition)
break;
if (data->entityPosition >= kPosition_2087) {
sequence1 = Common::String::printf("%02d%01d-38u.seq", index, data->clothes);
data->field_4A9 = true;
} else {
sequence1 = Common::String::printf("%02d%01d-%02deu.seq", index, data->clothes, position);
sequence2 = Common::String::printf("%02d%01d-38u.seq", index, data->clothes);
data->field_4AA = true;
}
break;
case 40:
if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
sequence1 = Common::String::printf("%02d%01d-40u.seq", index, data->clothes);
break;
}
break;
case kDirectionDown:
switch (position) {
default:
break;
case 1:
if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
sequence1 = Common::String::printf("%02d%01d-01d.seq", index, data->clothes);
break;
case 2:
case 3:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
if (getData(kEntityPlayer)->entityPosition >= data->entityPosition)
break;
if (data->entityPosition <= kPosition_8513) {
sequence1 = Common::String::printf("%02d%01d-03d.seq", index, data->clothes);
data->field_4A9 = true;
} else {
sequence1 = Common::String::printf("%02d%01d-%02ded.seq", index, data->clothes, position);
sequence2 = Common::String::printf("%02d%01d-03d.seq", index, data->clothes);
data->field_4AA = true;
}
break;
case 18:
if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
sequence1 = Common::String::printf("%02d%01d-18d.seq", index, data->clothes);
break;
case 22:
if (data->entityPosition > kPosition_850)
sequence1 = Common::String::printf("%02d%01d-22d.seq", index, data->clothes);
break;
case 23:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
if (data->entityPosition <= kPosition_850)
break;
if (data->entityPosition <= kPosition_2087) {
sequence1 = Common::String::printf("%02d%01d-%02ded.seq", index, data->clothes, position);
} else {
sequence1 = Common::String::printf("%02d%01d-38d.seq", index, data->clothes);
sequence2 = Common::String::printf("%02d%01d-%02ded.seq", index, data->clothes, position);
data->field_4A9 = true;
}
break;
case 40:
if (getData(kEntityPlayer)->entityPosition > kPosition_8013)
sequence1 = Common::String::printf("%02d%01d-40d.seq", index, data->clothes);
break;
}
break;
// First part of sequence is already set
case kDirectionLeft:
case kDirectionRight:
sequence1 = Common::String::printf("%s%02d.seq", data->sequenceNamePrefix.c_str(), position);
break;
}
}
//////////////////////////////////////////////////////////////////////////
/// Compartments
//////////////////////////////////////////////////////////////////////////
void Entities::enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
if (entity > kEntityChapters)
return;
switch (compartment) {
default:
// Return here so we do not update the compartments
return;
case kObjectCompartment1:
updatePositionsEnter(entity, kCarGreenSleeping, 41, 51, 17, 38);
break;
case kObjectCompartment2:
updatePositionsEnter(entity, kCarGreenSleeping, 42, 52, 15, 36);
break;
case kObjectCompartment3:
updatePositionsEnter(entity, kCarGreenSleeping, 43, 53, 13, 34);
break;
case kObjectCompartment4:
updatePositionsEnter(entity, kCarGreenSleeping, 44, 54, 11, 32);
break;
case kObjectCompartment5:
updatePositionsEnter(entity, kCarGreenSleeping, 45, 55, 9, 30);
break;
case kObjectCompartment6:
updatePositionsEnter(entity, kCarGreenSleeping, 46, 56, 7, 28);
break;
case kObjectCompartment7:
updatePositionsEnter(entity, kCarGreenSleeping, 47, 57, 5, 26);
break;
case kObjectCompartment8:
updatePositionsEnter(entity, kCarGreenSleeping, 48, 58, 3, 25);
break;
case kObjectCompartmentA:
updatePositionsEnter(entity, kCarRedSleeping, 41, 51, 17, 38);
break;
case kObjectCompartmentB:
updatePositionsEnter(entity, kCarRedSleeping, 42, 52, 15, 36);
break;
case kObjectCompartmentC:
updatePositionsEnter(entity, kCarRedSleeping, 43, 53, 13, 34);
break;
case kObjectCompartmentD:
updatePositionsEnter(entity, kCarRedSleeping, 44, 54, 11, 32);
break;
case kObjectCompartmentE:
updatePositionsEnter(entity, kCarRedSleeping, 45, 55, 9, 30);
break;
case kObjectCompartmentF:
updatePositionsEnter(entity, kCarRedSleeping, 46, 56, 7, 28);
break;
case kObjectCompartmentG:
updatePositionsEnter(entity, kCarRedSleeping, 47, 57, 5, 26);
break;
case kObjectCompartmentH:
updatePositionsEnter(entity, kCarRedSleeping, 48, 58, 3, 25);
break;
}
// Update compartments
int index = (compartment < 32 ? compartment - 1 : compartment - 24);
if (index >= 16)
error("Entities::exitCompartment: invalid compartment index!");
if (useCompartment1)
_compartments1[index] |= STORE_VALUE(entity);
else
_compartments[index] |= STORE_VALUE(entity);
}
void Entities::exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
if (entity > kEntityChapters)
return;
// TODO factorize in one line
switch (compartment) {
default:
// Return here so we do not update the compartments
return;
case kObjectCompartment1:
updatePositionsExit(entity, kCarGreenSleeping, 41, 51);
break;
case kObjectCompartment2:
updatePositionsExit(entity, kCarGreenSleeping, 42, 52);
break;
case kObjectCompartment3:
updatePositionsExit(entity, kCarGreenSleeping, 43, 53);
break;
case kObjectCompartment4:
updatePositionsExit(entity, kCarGreenSleeping, 44, 54);
break;
case kObjectCompartment5:
updatePositionsExit(entity, kCarGreenSleeping, 45, 55);
break;
case kObjectCompartment6:
updatePositionsExit(entity, kCarGreenSleeping, 46, 56);
break;
case kObjectCompartment7:
updatePositionsExit(entity, kCarGreenSleeping, 47, 57);
break;
case kObjectCompartment8:
updatePositionsExit(entity, kCarGreenSleeping, 48, 58);
break;
case kObjectCompartmentA:
updatePositionsExit(entity, kCarRedSleeping, 41, 51);
break;
case kObjectCompartmentB:
updatePositionsExit(entity, kCarRedSleeping, 42, 52);
break;
case kObjectCompartmentC:
updatePositionsExit(entity, kCarRedSleeping, 43, 53);
break;
case kObjectCompartmentD:
updatePositionsExit(entity, kCarRedSleeping, 44, 54);
break;
case kObjectCompartmentE:
updatePositionsExit(entity, kCarRedSleeping, 45, 55);
break;
case kObjectCompartmentF:
updatePositionsExit(entity, kCarRedSleeping, 46, 56);
break;
case kObjectCompartmentG:
updatePositionsExit(entity, kCarRedSleeping, 47, 57);
break;
case kObjectCompartmentH:
updatePositionsExit(entity, kCarRedSleeping, 48, 58);
break;
}
// Update compartments
int index = (compartment < 32 ? compartment - 1 : compartment - 24);
if (index >= 16)
error("Entities::exitCompartment: invalid compartment index!");
if (useCompartment1)
_compartments1[index] &= ~STORE_VALUE(entity);
else
_compartments[index] &= ~STORE_VALUE(entity);
}
void Entities::updatePositionEnter(EntityIndex entity, CarIndex car, Position position) {
if (entity == kEntity39)
entity = kEntityPlayer;
if (entity > kEntityChapters)
return;
_positions[100 * car + position] |= STORE_VALUE(entity);
if (isPlayerPosition(car, position) || (car == kCarRestaurant && position == 57 && isPlayerPosition(kCarRestaurant, 50))) {
getSound()->excuseMe(entity);
getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
getSound()->playSound(kEntityPlayer, "CAT1127A");
} else {
getLogic()->updateCursor();
}
}
void Entities::updatePositionExit(EntityIndex entity, CarIndex car, Position position) {
if (entity == kEntity39)
entity = kEntityPlayer;
if (entity > kEntityChapters)
return;
_positions[100 * car + position] &= ~STORE_VALUE(entity);
getLogic()->updateCursor();
}
void Entities::updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4) {
if (entity == kEntity39)
entity = kEntityPlayer;
if (entity > kEntityChapters)
return;
_positions[100 * car + position1] |= STORE_VALUE(entity);
_positions[100 * car + position2] |= STORE_VALUE(entity);
// FIXME: also checking two DWORD values that do not seem to updated anywhere...
if (isPlayerPosition(car, position1) || isPlayerPosition(car, position2) || isPlayerPosition(car, position3) || isPlayerPosition(car, position4)) {
getSound()->excuseMe(entity);
getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
getSound()->playSound(kEntityPlayer, "CAT1127A");
} else {
getLogic()->updateCursor();
}
}
void Entities::updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2) {
if (entity == kEntity39)
entity = kEntityPlayer;
if (entity > kEntityChapters)
return;
_positions[100 * car + position1] &= ~STORE_VALUE(entity);
_positions[100 * car + position2] &= ~STORE_VALUE(entity);
getLogic()->updateCursor();
}
void Entities::loadSceneFromEntityPosition(CarIndex car, EntityPosition entityPosition, bool alternate) const {
// Determine position
Position position = (alternate ? 1 : 40);
do {
if (entityPosition > entityPositions[position]) {
if (alternate)
break;
// For default value, we ignore position 24
if (position != 24)
break;
}
alternate ? ++position : --position;
} while (alternate ? position <= 18 : position >= 22);
// For position outside bounds, use minimal value
if ((alternate && position > 18) || (alternate && position < 22)) {
getScenes()->loadSceneFromPosition(car, alternate ? 18 : 22);
return;
}
// Load scene from position
switch (position) {
default:
getScenes()->loadSceneFromPosition(car, (Position)(position + (alternate ? - 1 : 1)));
break;
// Alternate
case 1:
if (alternate) getScenes()->loadSceneFromPosition(car, 1);
break;
case 5:
if (alternate) getScenes()->loadSceneFromPosition(car, 3);
break;
// Default
case 23:
if (!alternate) getScenes()->loadSceneFromPosition(car, 25);
break;
case 40:
if (!alternate) getScenes()->loadSceneFromPosition(car, 40);
break;
}
}
//////////////////////////////////////////////////////////////////////////
// Checks
//////////////////////////////////////////////////////////////////////////
bool Entities::hasValidFrame(EntityIndex entity) const {
return (getData(entity)->frame && (getData(entity)->frame->getInfo()->subType != kFrameType3));
}
bool Entities::compare(EntityIndex entity1, EntityIndex entity2) const {
EntityData::EntityCallData *data1 = getData(entity1);
EntityData::EntityCallData *data2 = getData(entity2);
if (data2->car != data1->car
|| data1->car < kCarGreenSleeping
|| data1->car > kCarRedSleeping)
return false;
EntityPosition position1 = (data1->entityPosition >= data2->entityPosition) ? data1->entityPosition : data2->entityPosition;
EntityPosition position2 = (data1->entityPosition >= data2->entityPosition) ? data2->entityPosition : data1->entityPosition;
// Compute position
int index1 = 7;
do {
if (objectsPosition[index1] >= position2)
break;
--index1;
} while (index1 > -1);
int index2 = 0;
do {
if (objectsPosition[index2] <= position2)
break;
++index2;
} while (index2 < 8);
if (index1 > -1 && index2 < 8 && index2 <= index1) {
while (index2 <= index1) {
if (getCompartments(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
return true;
if (getCompartments1(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
return true;
++index2;
}
}
for (EntityIndex entity = kEntityAnna; entity <= kEntity39; entity = (EntityIndex)(entity + 1)) {
if (entity1 == entity || entity2 == entity)
continue;
if (!isDirectionUpOrDown(entity))
continue;
if (data1->car == getEntityData(entity)->car
&& getEntityData(entity)->entityPosition > position2
&& getEntityData(entity)->entityPosition < position1)
return true;
}
return false;
}
bool Entities::updateEntity(EntityIndex entity, CarIndex car, EntityPosition position) const {
EntityData::EntityCallData *data = getData(entity);
EntityDirection direction = kDirectionNone;
int delta = 0;
bool flag1 = false;
bool flag2 = false;
bool flag3 = false;
if (position == kPosition_2000
&& getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
&& !isPlayerPosition(kCarGreenSleeping, 1)
&& !isPlayerPosition(kCarRedSleeping, 2))
position = kPosition_1500;
if (data->direction != kDirectionUp && data->direction != kDirectionDown)
data->field_497 = 0;
if (data->field_497) {
data->field_497--;
if (data->field_497 == 128)
data->field_497 = 0;
if ((data->field_497 & 127) != 8) {
data->field_49B = 0;
return false;
}
flag1 = true;
if (data->field_497 & 128)
flag2 = true;
}
if (data->car != car)
goto label_process_entity;
// Calculate delta
delta = ABS(data->entityPosition - position);
if (delta < 100 || (position > kPosition_850 && position < kPosition_9270 && delta < 300))
flag3 = true;
if (!flag3) {
if ((getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && data->direction == kDirectionUp)
|| (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && data->direction == kDirectionDown)) {
if (!checkPosition(position) && isDistanceBetweenEntities(entity, kEntityPlayer, 250))
flag3 = true;
}
if (!flag3)
goto label_process_entity;
}
if (getEntities()->hasValidFrame(entity)
&& getEntities()->isWalkingOppositeToPlayer(entity)
&& !getEntities()->checkPosition(position)) {
flag3 = false;
position = (EntityPosition)(getData(kEntityPlayer)->entityPosition + 250 * (data->direction == kDirectionUp ? 1 : -1));
}
if (!flag3) {
label_process_entity:
// Calculate direction
if (data->car < car)
direction = kDirectionUp;
else if (data->car > car)
direction = kDirectionDown;
else // same car
direction = (data->entityPosition < position) ? kDirectionUp : kDirectionDown;
if (data->direction == direction) {
if (!flag1) {
if (checkDistanceFromPosition(entity, kPosition_1500, 750) && entity != kEntityFrancois) {
if (data->entity != kEntityPlayer) {
if (data->direction != kDirectionUp || (position <= kPosition_2000 && data->car == car)) {
if (data->direction == kDirectionDown && (position < kPosition_1500 || data->car != car)) {
if (data->entityPosition > kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
getSavePoints()->push(entity, data->entity, kAction11);
}
}
} else {
if (data->entityPosition < kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
getSavePoints()->push(entity, data->entity, kAction11, 1);
}
}
}
} else if (data->entity) {
getSavePoints()->push(entity, data->entity, kAction16);
data->entity = kEntityPlayer;
}
if (hasValidFrame(entity)) {
if (!data->field_4A9)
return false;
int compartmentIndex = 0;
if (data->car == kCarGreenSleeping)
compartmentIndex = 0;
else if (data->car == kCarRedSleeping)
compartmentIndex = 8;
for (int i = 0; i < 8; i++) {
if (getCompartments(compartmentIndex) || getCompartments1(compartmentIndex)) {
if (checkDistanceFromPosition(entity, objectsPosition[i], 750)) {
if (checkPosition(objectsPosition[i])) {
if ((data->direction == kDirectionUp && data->entityPosition < objectsPosition[i] && (data->car != car || position > objectsPosition[i]))
|| (data->direction == kDirectionDown && data->entityPosition > objectsPosition[i] && (data->car != car || position < objectsPosition[i]))) {
getSound()->excuseMe(entity, (EntityIndex)(State::getPowerOfTwo((uint32)(getCompartments(compartmentIndex) ? getCompartments(compartmentIndex) : getCompartments1(compartmentIndex)))));
data->field_497 = 144;
break;
}
}
}
}
compartmentIndex++;
}
for (EntityIndex entityIndex = kEntityAnna; entityIndex <= kEntity39; entityIndex = (EntityIndex)(entityIndex + 1)) {
if (getSavePoints()->getCallback(entityIndex)
&& hasValidFrame(entityIndex)
&& entityIndex != entity
&& isDistanceBetweenEntities(entity, entityIndex, 750)
&& isDirectionUpOrDown(entityIndex)
&& (entity != kEntityRebecca || entityIndex != kEntitySophie)
&& (entity != kEntitySophie || entityIndex != kEntityRebecca)
&& (entity != kEntityIvo || entityIndex != kEntitySalko)
&& (entity != kEntitySalko || entityIndex != kEntityIvo)
&& (entity != kEntityMilos || entityIndex != kEntityVesna)
&& (entity != kEntityVesna || entityIndex != kEntityMilos)) {
EntityData::EntityCallData *data2 = getData(entityIndex);
if (data->direction != data2->direction) {
if ((data->direction != kDirectionUp || data2->entityPosition <= data->entityPosition)
&& (data->direction != kDirectionDown || data2->entityPosition >= data->entityPosition))
continue;
data->field_49B = 0;
data2->field_49B = 0;
data->field_497 = 16;
data2->field_497 = 16;
getSound()->excuseMe(entity, entityIndex);
getSound()->excuseMe(entityIndex, entity);
if (entityIndex > entity)
++data2->field_497;
break;
}
if (ABS(data2->entityPosition - getData(kEntityPlayer)->entityPosition) < ABS(data->entityPosition - getData(kEntityPlayer)->entityPosition)) {
if (!isWalkingOppositeToPlayer(entity)) {
if (direction == kDirectionUp) {
if (data->entityPosition < kPosition_9500)
data->entityPosition = (EntityPosition)(data->entityPosition + 500);
} else {
if (data->entityPosition > kPosition_500)
data->entityPosition = (EntityPosition)(data->entityPosition - 500);
}
drawSequences(entity, direction, true);
return false;
}
data->field_49B = 0;
break;
}
}
}
return false;
}
if (data->direction == kDirectionUp) {
if (data->entityPosition + data->field_4A3 < 10000)
data->entityPosition = (EntityPosition)(data->entityPosition + data->field_4A3);
} else {
if (data->entityPosition > data->field_4A3)
data->entityPosition = (EntityPosition)(data->entityPosition - data->field_4A3);
}
if (data->entityPosition <= kPosition_9270 || data->direction != kDirectionUp) {
if (data->entityPosition < kPosition_850 && data->direction == kDirectionDown) {
if (changeCar(data, entity, car, position, false, kPosition_9269, kCarKronos))
return true;
}
} else {
if (changeCar(data, entity, car, position, true, kPosition_851, kCarGreenSleeping))
return true;
}
if (getData(kEntityPlayer)->car == data->car && data->location == kLocationOutsideCompartment) {
if (data->direction == kDirectionUp) {
if (getData(kEntityPlayer)->entityPosition > data->entityPosition
&& getData(kEntityPlayer)->entityPosition - data->entityPosition >= 500
&& data->field_4A3 + 500 > getData(kEntityPlayer)->entityPosition - data->entityPosition) {
if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkCurrentPosition(false)) {
getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
if (getScenes()->checkCurrentPosition(false))
getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1, true);
} else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)) {
getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
}
}
} else {
if (getData(kEntityPlayer)->entityPosition < data->entityPosition
&& data->entityPosition - getData(kEntityPlayer)->entityPosition >= 500
&& data->field_4A3 + 500 > data->entityPosition - getData(kEntityPlayer)->entityPosition) {
if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) {
getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
} else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)){
getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
if (getScenes()->checkCurrentPosition(false))
getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1);
}
}
}
return false;
}
}
} else if (!flag1) {
drawSequences(entity, direction, true);
return false;
}
//////////////////////////////////////////////////////////////////////////
// Adjust positions
// Direction Up
if (direction == kDirectionUp) {
if (data->entityPosition < (flag2 ? kPosition_8800 : kPosition_9250))
data->entityPosition = (EntityPosition)(data->entityPosition + (flag2 ? kPosition_1200 : kPosition_750));
if (data->car == car && data->entityPosition >= position) {
data->entityPosition = position;
data->direction = kDirectionNone;
data->entity = kEntityPlayer;
return true;
}
drawSequences(entity, direction, true);
return false;
}
// Direction Down
if (data->entityPosition > (flag2 ? kPosition_1200 : kPosition_750))
data->entityPosition = (EntityPosition)(data->entityPosition - (flag2 ? kPosition_1200 : kPosition_750));
if (data->car == car && data->entityPosition <= position) {
data->entityPosition = position;
data->direction = kDirectionNone;
data->entity = kEntityPlayer;
return true;
}
drawSequences(entity, direction, true);
return false;
}
data->entityPosition = position;
if (data->direction == kDirectionUp || data->direction == kDirectionDown)
data->direction = kDirectionNone;
data->entity = kEntityPlayer;
return true;
}
bool Entities::changeCar(EntityData::EntityCallData *data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const {
if (getData(kEntityPlayer)->car == data->car) {
getSound()->playSoundEvent(entity, 36);
getSound()->playSoundEvent(entity, 37, 30);
}
data->car = (CarIndex)(increment ? data->car + 1 : data->car - 1);
data->entityPosition = newPosition;
if (data->car == newCar) {
if (isInGreenCarEntrance(kEntityPlayer)) {
getSound()->playSoundEvent(kEntityPlayer, 14);
getSound()->excuseMe(entity, kEntityPlayer, SoundManager::kFlagDefault);
getScenes()->loadSceneFromPosition(kCarGreenSleeping, 1);
getSound()->playSound(kEntityPlayer, "CAT1127A");
getSound()->playSoundEvent(kEntityPlayer, 15);
}
}
if ((increment ? data->car > car : data->car < car) || (data->car == car && (increment ? data->entityPosition >= position : data->entityPosition <= position))) {
data->car = car;
data->entityPosition = position;
data->direction = kDirectionNone;
data->entity = kEntityPlayer;
return true;
}
if (data->car == newCar) {
if (isInKronosCarEntrance(kEntityPlayer)) {
getSound()->playSoundEvent(kEntityPlayer, 14);
getSound()->excuseMe(entity, kEntityPlayer, SoundManager::kFlagDefault);
getScenes()->loadSceneFromPosition(kCarGreenSleeping, 62);
getSound()->playSound(kEntityPlayer, "CAT1127A");
getSound()->playSoundEvent(kEntityPlayer, 15);
}
}
if (data->car == getData(kEntityPlayer)->car) {
getSound()->playSoundEvent(entity, 36);
getSound()->playSoundEvent(entity, 37, 30);
}
return false;
}
//////////////////////////////////////////////////////////////////////////
// CHECKS
//////////////////////////////////////////////////////////////////////////
bool Entities::isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const {
return (getData(entity)->entityPosition == position
&& getData(entity)->location == kLocationInsideCompartment
&& getData(entity)->car == car);
}
bool Entities::checkFields2(ObjectIndex object) const {
EntityPosition position = kPositionNone;
CarIndex car = kCarNone;
switch (object) {
default:
return false;
case kObjectCompartment1:
case kObjectCompartment2:
case kObjectCompartment3:
case kObjectCompartment4:
case kObjectCompartment5:
case kObjectCompartment6:
case kObjectCompartment7:
case kObjectCompartment8:
position = objectsPosition[object - 1];
car = kCarGreenSleeping;
if (isInsideCompartment(kEntityPlayer, car, position))
return false;
break;
case kObjectHandleBathroom:
case kObjectHandleInsideBathroom:
case kObjectKitchen:
case kObject20:
case kObject21:
case kObject22:
position = objectsPosition[object-17];
car = kCarGreenSleeping;
break;
case kObjectCompartmentA:
case kObjectCompartmentB:
case kObjectCompartmentC:
case kObjectCompartmentD:
case kObjectCompartmentE:
case kObjectCompartmentF:
case kObjectCompartmentG:
case kObjectCompartmentH:
position = objectsPosition[object-32];
car = kCarRedSleeping;
if (isInsideCompartment(kEntityPlayer, car, position))
return false;
break;
case kObject48:
case kObject49:
case kObject50:
case kObject51:
case kObject52:
case kObject53:
position = objectsPosition[object-48];
car = kCarRedSleeping;
break;
}
uint index = 1;
while (!isInsideCompartment((EntityIndex)index, car, position) || index == kEntityVassili) {
index++;
if (index >= 40)
return false;
}
return true;
}
bool Entities::isInsideCompartments(EntityIndex entity) const {
return (getData(entity)->car == kCarGreenSleeping
|| getData(entity)->car == kCarRedSleeping)
&& getData(entity)->location == kLocationInsideCompartment;
}
bool Entities::isPlayerPosition(CarIndex car, Position position) const {
return getData(kEntityPlayer)->car == car && getScenes()->get(getState()->scene)->position == position;
}
bool Entities::isInsideTrainCar(EntityIndex entity, CarIndex car) const {
return getData(entity)->car == car && getData(entity)->location <= kLocationInsideCompartment;
}
bool Entities::isInGreenCarEntrance(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarGreenSleeping) && getData(entity)->entityPosition < kPosition_850;
}
bool Entities::isPlayerInCar(CarIndex car) const {
return isInsideTrainCar(kEntityPlayer, car) && getData(kEntityPlayer)->location && !isInGreenCarEntrance(kEntityPlayer);
}
bool Entities::isDirectionUpOrDown(EntityIndex entity) const {
return getData(entity)->direction == kDirectionUp || getData(entity)->direction == kDirectionDown;
}
bool Entities::isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const {
return getData(entity1)->car == getData(entity2)->car
&& (uint)ABS(getData(entity1)->entityPosition - getData(entity2)->entityPosition) <= distance
&& (getData(entity1)->location != kLocationOutsideTrain || getData(entity2)->location != kLocationOutsideTrain);
}
bool Entities::checkFields10(EntityIndex entity) const {
return getData(entity)->location <= kLocationOutsideTrain;
}
bool Entities::isSomebodyInsideRestaurantOrSalon() const {
for (uint i = 1; i < _entities.size(); i++) {
EntityIndex index = (EntityIndex)i;
if (getData(index)->location == kLocationOutsideCompartment && (isInSalon(index) || isInRestaurant(index)))
return false;
}
return true;
}
bool Entities::isInSalon(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarRestaurant)
&& getData(entity)->entityPosition >= kPosition_1540
&& getData(entity)->entityPosition <= kPosition_3650;
}
bool Entities::isInRestaurant(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarRestaurant)
&& getData(entity)->entityPosition >= kPosition_3650
&& getData(entity)->entityPosition <= kPosition_5800;
}
bool Entities::isInKronosSalon(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarKronos)
&& getData(entity)->entityPosition >= kPosition_5500
&& getData(entity)->entityPosition <= kPosition_7500;
}
bool Entities::isOutsideAlexeiWindow() const {
return (getData(kEntityPlayer)->entityPosition == kPosition_7500 || getData(kEntityPlayer)->entityPosition == kPosition_8200)
&& getData(kEntityPlayer)->location == kLocationOutsideTrain
&& getData(kEntityPlayer)->car == kCarGreenSleeping;
}
bool Entities::isOutsideAnnaWindow() const {
return (getData(kEntityPlayer)->entityPosition == kPosition_4070 || getData(kEntityPlayer)->entityPosition == kPosition_4840)
&& getData(kEntityPlayer)->location == kLocationOutsideTrain
&& getData(kEntityPlayer)->car == kCarRedSleeping;
}
bool Entities::isInKitchen(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarRestaurant) && getData(entity)->entityPosition > kPosition_5800;
}
bool Entities::isNobodyInCompartment(CarIndex car, EntityPosition position) const {
for (uint i = 1; i < _entities.size(); i++) {
if (isInsideCompartment((EntityIndex)i, car, position))
return false;
}
return true;
}
bool Entities::checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const {
if (getData(entity)->car != car || getData(entity)->location != kLocationInsideCompartment)
return false;
EntityPosition entityPosition = getData(entity)->entityPosition;
// Test values
if (position == kPosition_4455) {
if (entityPosition == kPosition_4070 || entityPosition == kPosition_4455 || entityPosition == kPosition_4840)
return true;
return false;
}
if (position == kPosition_6130) {
if (entityPosition == kPosition_5790 || entityPosition == kPosition_6130 || entityPosition == kPosition_6470)
return true;
return false;
}
if (position != kPosition_7850
|| (entityPosition != kPosition_7500 && entityPosition != kPosition_7850 && entityPosition != kPosition_8200))
return false;
return true;
}
bool Entities::isInBaggageCarEntrance(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarBaggage)
&& getData(entity)->entityPosition >= kPosition_4500
&& getData(entity)->entityPosition <= kPosition_5500;
}
bool Entities::isInBaggageCar(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarBaggage) && getData(entity)->entityPosition < kPosition_4500;
}
bool Entities::isInKronosSanctum(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarKronos)
&& getData(entity)->entityPosition >= kPosition_3500
&& getData(entity)->entityPosition <= kPosition_5500;
}
bool Entities::isInKronosCarEntrance(EntityIndex entity) const {
return isInsideTrainCar(entity, kCarKronos) && getData(entity)->entityPosition > kPosition_7900;
}
bool Entities::checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const {
return distance >= ABS(getData(entity)->entityPosition - position);
}
bool Entities::isWalkingOppositeToPlayer(EntityIndex entity) const {
if (getData(entity)->direction == kDirectionUp && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
return true;
return (getData(entity)->direction == kDirectionDown && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp));
}
bool Entities::isFemale(EntityIndex entity) {
return (entity == kEntityAnna
|| entity == kEntityTatiana
|| entity == kEntityVesna
|| entity == kEntityKahina
|| entity == kEntityMmeBoutarel
|| entity == kEntityRebecca
|| entity == kEntitySophie
|| entity == kEntityYasmin
|| entity == kEntityHadija
|| entity == kEntityAlouan);
}
bool Entities::isMarried(EntityIndex entity) {
return (entity != kEntityTatiana
&& entity != kEntityRebecca
&& entity != kEntitySophie);
}
bool Entities::checkPosition(EntityPosition position) const {
Position position1 = 0;
Position position2 = 0;
switch (position) {
default:
return true;
case kPosition_1500:
position1 = 1;
position2 = 23;
break;
case kPosition_2740:
position1 = 3;
position2 = 25;
break;
case kPosition_3050:
position1 = 5;
position2 = 26;
break;
case kPosition_4070:
position1 = 7;
position2 = 28;
break;
case kPosition_4840:
position1 = 9;
position2 = 30;
break;
case kPosition_5790:
position1 = 11;
position2 = 32;
break;
case kPosition_6470:
position1 = 13;
position2 = 34;
break;
case kPosition_7500:
position1 = 15;
position2 = 36;
break;
case kPosition_8200:
position1 = 17;
position2 = 38;
break;
}
if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && entityPositions[position1] >= getEntityData(kEntityPlayer)->entityPosition)
return true;
else
return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && entityPositions[position2] <= getEntityData(kEntityPlayer)->entityPosition);
}
bool Entities::checkSequenceFromPosition(EntityIndex entity) const {
FrameInfo *info = getEntityData(entity)->sequence->getFrameInfo((uint16)getEntityData(entity)->currentFrame);
if (getEntityData(entity)->direction == kDirectionUp)
return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
&& info->entityPosition + getEntityPositionFromCurrentPosition() > kPosition_8513);
if (getEntityData(entity)->direction == kDirectionDown)
return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)
&& info->entityPosition + getEntityPositionFromCurrentPosition() < kPosition_2087);
return false;
}
EntityPosition Entities::getEntityPositionFromCurrentPosition() const {
// Get the scene position first
Position position = getScenes()->get(getState()->scene)->position;
if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
return (EntityPosition)(entityPositions[position] - kPosition_1430);
if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
return (EntityPosition)(entityPositions[position] - kPosition_9020);
return kPositionNone;
}
void Entities::clearEntitySequenceData(EntityData::EntityCallData *data, EntityDirection direction) const {
getScenes()->removeAndRedraw(&data->frame, false);
getScenes()->removeAndRedraw(&data->frame1, false);
SAFE_DELETE(data->sequence);
SAFE_DELETE(data->sequence2);
data->sequenceName = "";
data->sequenceName2 = "";
data->field_4A9 = false;
data->field_4AA = false;
data->directionSwitch = kDirectionNone;
data->currentFrame = -1;
data->currentFrame2 = 0;
data->direction = direction;
}
} // End of namespace LastExpress