1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "lastexpress/game/entities.h"
24
25 // Data
26 #include "lastexpress/data/scene.h"
27 #include "lastexpress/data/sequence.h"
28
29 // Entities
30 #include "lastexpress/entities/abbot.h"
31 #include "lastexpress/entities/alexei.h"
32 #include "lastexpress/entities/alouan.h"
33 #include "lastexpress/entities/anna.h"
34 #include "lastexpress/entities/august.h"
35 #include "lastexpress/entities/boutarel.h"
36 #include "lastexpress/entities/chapters.h"
37 #include "lastexpress/entities/cooks.h"
38 #include "lastexpress/entities/coudert.h"
39 #include "lastexpress/entities/entity39.h"
40 #include "lastexpress/entities/francois.h"
41 #include "lastexpress/entities/gendarmes.h"
42 #include "lastexpress/entities/hadija.h"
43 #include "lastexpress/entities/ivo.h"
44 #include "lastexpress/entities/kahina.h"
45 #include "lastexpress/entities/kronos.h"
46 #include "lastexpress/entities/mahmud.h"
47 #include "lastexpress/entities/max.h"
48 #include "lastexpress/entities/mertens.h"
49 #include "lastexpress/entities/milos.h"
50 #include "lastexpress/entities/mmeboutarel.h"
51 #include "lastexpress/entities/pascale.h"
52 #include "lastexpress/entities/rebecca.h"
53 #include "lastexpress/entities/salko.h"
54 #include "lastexpress/entities/sophie.h"
55 #include "lastexpress/entities/tables.h"
56 #include "lastexpress/entities/tatiana.h"
57 #include "lastexpress/entities/train.h"
58 #include "lastexpress/entities/vassili.h"
59 #include "lastexpress/entities/verges.h"
60 #include "lastexpress/entities/vesna.h"
61 #include "lastexpress/entities/waiter1.h"
62 #include "lastexpress/entities/waiter2.h"
63 #include "lastexpress/entities/yasmin.h"
64
65 // Game
66 #include "lastexpress/game/logic.h"
67 #include "lastexpress/game/savepoint.h"
68 #include "lastexpress/game/scenes.h"
69 #include "lastexpress/game/state.h"
70
71 #include "lastexpress/sound/queue.h"
72
73 #include "lastexpress/graphics.h"
74 #include "lastexpress/lastexpress.h"
75 #include "lastexpress/resource.h"
76
77 namespace LastExpress {
78
79 #define STORE_VALUE(data) ((uint)1 << (uint)data)
80
81 static const EntityPosition objectsPosition[8] = {kPosition_8200, kPosition_7500,
82 kPosition_6470, kPosition_5790,
83 kPosition_4840, kPosition_4070,
84 kPosition_3050, kPosition_2740};
85
86 static const EntityPosition entityPositions[41] = {kPositionNone, kPosition_851, kPosition_1430, kPosition_2110, kPositionNone,
87 kPosition_2410, kPosition_2980, kPosition_3450, kPosition_3760, kPosition_4100,
88 kPosition_4680, kPosition_5140, kPosition_5440, kPosition_5810, kPosition_6410,
89 kPosition_6850, kPosition_7160, kPosition_7510, kPosition_8514, kPositionNone,
90 kPositionNone, kPositionNone, kPosition_2086, kPosition_2690, kPositionNone,
91 kPosition_3110, kPosition_3390, kPosition_3890, kPosition_4460, kPosition_4770,
92 kPosition_5090, kPosition_5610, kPosition_6160, kPosition_6460, kPosition_6800,
93 kPosition_7320, kPosition_7870, kPosition_8160, kPosition_8500, kPosition_9020,
94 kPosition_9269};
95
96 #define ADD_ENTITY(class) \
97 _entities.push_back(new class(engine));
98
99 #define COMPUTE_SEQUENCE_NAME(sequenceTo, sequenceFrom) { \
100 sequenceTo = sequenceFrom; \
101 for (int seqIdx = 0; seqIdx < 7; seqIdx++) \
102 sequenceTo.deleteLastChar(); \
103 if (isInsideTrainCar(entityIndex, kCarGreenSleeping) || isInsideTrainCar(entityIndex, kCarRedSleeping)) { \
104 if (data->car < getData(kEntityPlayer)->car || (data->car == getData(kEntityPlayer)->car && data->entityPosition < getData(kEntityPlayer)->entityPosition)) \
105 sequenceTo += "R.SEQ"; \
106 else \
107 sequenceTo += "F.SEQ"; \
108 } else { \
109 sequenceTo += ".SEQ"; \
110 } \
111 }
112
113 #define TRY_LOAD_SEQUENCE(sequence, name, name1, name2) { \
114 if (data->car == getData(kEntityPlayer)->car) \
115 sequence = loadSequence1(name1, field30); \
116 if (sequence) { \
117 name = name1; \
118 } else { \
119 if (name2 != "") \
120 sequence = loadSequence1(name2, field30); \
121 name = (sequence ? name2 : ""); \
122 } \
123 }
124
125 //////////////////////////////////////////////////////////////////////////
126 // Entities
127 //////////////////////////////////////////////////////////////////////////
Entities(LastExpressEngine * engine)128 Entities::Entities(LastExpressEngine *engine) : _engine(engine) {
129 _header = new EntityData();
130
131 _entities.push_back(NULL); // Header
132 ADD_ENTITY(Anna);
133 ADD_ENTITY(August);
134 ADD_ENTITY(Mertens);
135 ADD_ENTITY(Coudert);
136 ADD_ENTITY(Pascale);
137 ADD_ENTITY(Waiter1);
138 ADD_ENTITY(Waiter2);
139 ADD_ENTITY(Cooks);
140 ADD_ENTITY(Verges);
141 ADD_ENTITY(Tatiana);
142 ADD_ENTITY(Vassili);
143 ADD_ENTITY(Alexei);
144 ADD_ENTITY(Abbot);
145 ADD_ENTITY(Milos);
146 ADD_ENTITY(Vesna);
147 ADD_ENTITY(Ivo);
148 ADD_ENTITY(Salko);
149 ADD_ENTITY(Kronos);
150 ADD_ENTITY(Kahina);
151 ADD_ENTITY(Francois);
152 ADD_ENTITY(MmeBoutarel);
153 ADD_ENTITY(Boutarel);
154 ADD_ENTITY(Rebecca);
155 ADD_ENTITY(Sophie);
156 ADD_ENTITY(Mahmud);
157 ADD_ENTITY(Yasmin);
158 ADD_ENTITY(Hadija);
159 ADD_ENTITY(Alouan);
160 ADD_ENTITY(Gendarmes);
161 ADD_ENTITY(Max);
162 ADD_ENTITY(Chapters);
163 ADD_ENTITY(Train);
164
165 // Special case for tables
166 _entities.push_back(new Tables(engine, kEntityTables0));
167 _entities.push_back(new Tables(engine, kEntityTables1));
168 _entities.push_back(new Tables(engine, kEntityTables2));
169 _entities.push_back(new Tables(engine, kEntityTables3));
170 _entities.push_back(new Tables(engine, kEntityTables4));
171 _entities.push_back(new Tables(engine, kEntityTables5));
172
173 ADD_ENTITY(Entity39);
174
175 // Init compartments & positions
176 memset(&_compartments, 0, sizeof(_compartments));
177 memset(&_compartments1, 0, sizeof(_compartments1));
178 memset(&_positions, 0, sizeof(_positions));
179 }
180
~Entities()181 Entities::~Entities() {
182 SAFE_DELETE(_header);
183
184 for (uint i = 0; i < _entities.size(); i++)
185 SAFE_DELETE(_entities[i]);
186
187 _entities.clear();
188
189 // Zero passed pointers
190 _engine = NULL;
191 }
192
193 //////////////////////////////////////////////////////////////////////////
194 // Accessors
195 //////////////////////////////////////////////////////////////////////////
get(EntityIndex entity)196 Entity *Entities::get(EntityIndex entity) {
197 assert((uint)entity < _entities.size());
198
199 if (entity == kEntityPlayer)
200 error("[Entities::get] Cannot get entity for kEntityPlayer");
201
202 return _entities[entity];
203 }
204
getData(EntityIndex entity) const205 EntityData::EntityCallData *Entities::getData(EntityIndex entity) const {
206 assert((uint)entity < _entities.size());
207
208 if (entity == kEntityPlayer)
209 return _header->getCallData();
210
211 return _entities[entity]->getData();
212 }
213
getPosition(CarIndex car,Position position) const214 int Entities::getPosition(CarIndex car, Position position) const {
215 int index = 100 * car + position;
216
217 if (car > 10)
218 error("[Entities::getPosition] Trying to access an invalid car (was: %d, valid:0-9)", car);
219
220 if (position > 100)
221 error("[Entities::getPosition] Trying to access an invalid position (was: %d, valid:0-100)", position);
222
223 return _positions[index];
224 }
225
getCompartments(int index) const226 int Entities::getCompartments(int index) const {
227 if (index >= _compartmentsCount)
228 error("[Entities::getCompartments] Trying to access an invalid compartment (was: %d, valid:0-15)", index);
229
230 return _compartments[index];
231 }
232
getCompartments1(int index) const233 int Entities::getCompartments1(int index) const {
234 if (index >= _compartmentsCount)
235 error("[Entities::getCompartments] Trying to access an invalid compartment (was: %d, valid:0-15)", index);
236
237 return _compartments1[index];
238 }
239
240 //////////////////////////////////////////////////////////////////////////
241 // Savegame
242 //////////////////////////////////////////////////////////////////////////
saveLoadWithSerializer(Common::Serializer & s)243 void Entities::saveLoadWithSerializer(Common::Serializer &s) {
244 _header->saveLoadWithSerializer(s, NULL);
245 for (uint i = 1; i < _entities.size(); i++)
246 _entities[i]->saveLoadWithSerializer(s);
247 }
248
savePositions(Common::Serializer & s)249 void Entities::savePositions(Common::Serializer &s) {
250 for (uint i = 0; i < (uint)_positionsCount; i++)
251 s.syncAsUint32LE(_positions[i]);
252 }
253
saveCompartments(Common::Serializer & s)254 void Entities::saveCompartments(Common::Serializer &s) {
255 for (uint i = 0; i < (uint)_compartmentsCount; i++)
256 s.syncAsUint32LE(_compartments[i]);
257
258 for (uint i = 0; i < (uint)_compartmentsCount; i++)
259 s.syncAsUint32LE(_compartments1[i]);
260 }
261
262 //////////////////////////////////////////////////////////////////////////
263 // Setup
264 //////////////////////////////////////////////////////////////////////////
setup(bool isFirstChapter,EntityIndex entityIndex)265 void Entities::setup(bool isFirstChapter, EntityIndex entityIndex) {
266 setupChapter(isFirstChapter ? kChapter1 : kChapterAll);
267
268 bool flag_4 = false;
269
270 if (!isFirstChapter) {
271 getFlags()->flag_4 = false;
272
273 if (entityIndex) {
274 getSavePoints()->call(kEntityPlayer, entityIndex, kActionNone);
275 flag_4 = getFlags()->flag_4;
276 }
277 }
278
279 getFlags()->flag_4 = flag_4;
280 if (!getFlags()->flag_4)
281 getScenes()->loadScene(getState()->scene);
282 }
283
setupChapter(ChapterIndex chapter)284 void Entities::setupChapter(ChapterIndex chapter) {
285 if (chapter) {
286 // Reset current call, inventory item & draw sequences
287 for (uint i = 1; i < _entities.size(); i++) {
288 getData((EntityIndex)i)->currentCall = 0;
289 getData((EntityIndex)i)->inventoryItem = kItemNone;
290
291 clearSequences((EntityIndex)i);
292 }
293
294 // Init compartments & positions
295 memset(&_compartments, 0, sizeof(_compartments));
296 memset(&_compartments1, 0, sizeof(_compartments1));
297 memset(&_positions, 0, sizeof(_positions));
298
299 getSoundQueue()->stopAllExcept(kSoundTagMenu);
300 }
301
302 // we skip the header when doing entity setup
303 for (uint i = 1; i < _entities.size(); i++) {
304 // Special case of chapters (prevents infinite loop as we will be called from Chapters functions when changing chapters)
305 if (i == kEntityChapters && chapter >= 2)
306 continue;
307
308 _entities[i]->setup(chapter);
309 }
310 }
311
reset()312 void Entities::reset() {
313 // Reset header
314 delete _header;
315 _header = new EntityData();
316
317 for (uint i = 1; i < _entities.size(); i++)
318 resetSequences((EntityIndex)i);
319
320 getScenes()->resetDoorsAndClock();
321 }
322
323 //////////////////////////////////////////////////////////////////////////
324 // State & Sequences
325 //////////////////////////////////////////////////////////////////////////
326
canInteractWith(const Common::Point & point) const327 EntityIndex Entities::canInteractWith(const Common::Point &point) const {
328 if (!getFlags()->isGameRunning)
329 return kEntityPlayer;
330
331 EntityIndex index = kEntityPlayer;
332 int location = 10000;
333
334 // Check if there is an entity we can interact with
335 for (uint i = 0; i < _entities.size(); i++) {
336
337 // Skip entities with no current frame
338 if (!getData((EntityIndex)i)->frame)
339 continue;
340
341 FrameInfo *info = getData((EntityIndex)i)->frame->getInfo();
342
343 // Check the hotspot
344 if (info->hotspot.contains(point)) {
345
346 // If closer to us, update with its values
347 if (location > info->location) {
348 location = info->location;
349 index = (EntityIndex)i;
350 }
351 }
352 }
353
354 // Check if we found an entity
355 if (!index)
356 return kEntityPlayer;
357
358 // Check that there is an item to interact with
359 if (!getData(index)->inventoryItem)
360 return kEntityPlayer;
361
362 return index;
363 }
364
resetState(EntityIndex entityIndex)365 void Entities::resetState(EntityIndex entityIndex) {
366 getData(entityIndex)->currentCall = 0;
367 getData(entityIndex)->inventoryItem = kItemNone;
368
369 if (getSoundQueue()->isBuffered(entityIndex))
370 getSoundQueue()->stop(entityIndex);
371
372 clearSequences(entityIndex);
373
374 if (entityIndex == kEntity39)
375 entityIndex = kEntityPlayer;
376
377 if (entityIndex > kEntityChapters)
378 return;
379
380 // reset compartments and positions for this entity
381 for (int i = 0; i < _positionsCount; i++)
382 _positions[i] &= ~STORE_VALUE(entityIndex);
383
384 for (int i = 0; i < _compartmentsCount; i++) {
385 _compartments[i] &= ~STORE_VALUE(entityIndex);
386 _compartments1[i] &= ~STORE_VALUE(entityIndex);
387 }
388
389 getLogic()->updateCursor();
390 }
391
updateFields() const392 void Entities::updateFields() const {
393 if (!getFlags()->isGameRunning)
394 return;
395
396 for (int i = 0; i < (int)_entities.size(); i++) {
397
398 if (!getSavePoints()->getCallback((EntityIndex)i))
399 continue;
400
401 EntityData::EntityCallData *data = getData((EntityIndex)i);
402 int positionDelta = data->field_4A3 * 10;
403 switch (data->direction) {
404 default:
405 break;
406
407 case kDirectionUp:
408 if (data->entityPosition >= 10000 - positionDelta)
409 data->entityPosition = (EntityPosition)(data->entityPosition + positionDelta);
410 break;
411
412 case kDirectionDown:
413 if (data->entityPosition > positionDelta)
414 data->entityPosition = (EntityPosition)(data->entityPosition - positionDelta);
415 break;
416
417 case kDirectionLeft:
418 data->currentFrame++;
419 break;
420
421 case kDirectionRight:
422 data->field_4A1 += 9;
423 break;
424
425 case kDirectionSwitch:
426 if (data->directionSwitch == kDirectionRight)
427 data->field_4A1 += 9;
428 break;
429
430 }
431 }
432 }
433
updateFrame(EntityIndex entityIndex) const434 void Entities::updateFrame(EntityIndex entityIndex) const {
435 Sequence *sequence = NULL;
436 int16 *currentFrame = NULL;
437 bool found = false;
438
439 if (getData(entityIndex)->direction == kDirectionSwitch) {
440 sequence = getData(entityIndex)->sequence2;
441 currentFrame = &getData(entityIndex)->currentFrame2;
442 } else {
443 sequence = getData(entityIndex)->sequence;
444 currentFrame = &getData(entityIndex)->currentFrame;
445 }
446
447 if (!sequence)
448 return;
449
450 // Save current values
451 int16 oldFrame = *currentFrame;
452 int16 field_4A1 = getData(entityIndex)->field_4A1;
453
454 do {
455 // Check we do not get past the end
456 if (*currentFrame >= (int)sequence->count() - 1)
457 break;
458
459 // Get the proper frame
460 FrameInfo *info = sequence->getFrameInfo((uint16)*currentFrame);
461
462 if (info->field_33 & 8) {
463 found = true;
464 } else {
465 if (info->soundAction == 35)
466 found = true;
467
468 getData(entityIndex)->field_4A1 += info->field_30;
469
470 // Progress to the next frame
471 ++*currentFrame;
472 }
473 } while (!found);
474
475 // Restore old values
476 if (!found) {
477 *currentFrame = oldFrame;
478 getData(entityIndex)->field_4A1 = field_4A1;
479 }
480 }
481
updateSequences() const482 void Entities::updateSequences() const {
483 if (!getFlags()->isGameRunning)
484 return;
485
486 // Update the train clock & doors
487 getScenes()->updateDoorsAndClock();
488
489 //////////////////////////////////////////////////////////////////////////
490 // First pass: Drawing
491 //////////////////////////////////////////////////////////////////////////
492 for (uint i = 1; i < _entities.size(); i++) {
493 EntityIndex entityIndex = (EntityIndex)i;
494
495 if (!getSavePoints()->getCallback(entityIndex))
496 continue;
497
498 EntityData::EntityCallData *data = getData(entityIndex);
499
500 if (data->frame) {
501 getScenes()->removeFromQueue(data->frame);
502 SAFE_DELETE(data->frame);
503 }
504
505 if (data->frame1) {
506 getScenes()->removeFromQueue(data->frame1);
507 SAFE_DELETE(data->frame1);
508 }
509
510 if (data->direction == kDirectionSwitch) {
511
512 // Clear sequence 2
513 if (data->sequence)
514 SAFE_DELETE(data->sequence);
515
516 // Replace by sequence 3 if available
517 if (data->sequence2) {
518 data->sequence = data->sequence2;
519 data->sequenceName = data->sequenceName2;
520
521 data->sequence2 = NULL;
522 data->sequenceName2 = "";
523 }
524
525 data->direction = data->directionSwitch;
526 data->currentFrame = -1;
527 data->field_49B = 0;
528 }
529
530 // Draw sequences
531 drawSequences(entityIndex, data->direction, false);
532 }
533
534 //////////////////////////////////////////////////////////////////////////
535 // Second pass: Load sequences for next pass
536 //////////////////////////////////////////////////////////////////////////
537 for (uint i = 1; i < _entities.size(); i++) {
538 EntityIndex entityIndex = (EntityIndex)i;
539
540 if (!getSavePoints()->getCallback(entityIndex))
541 continue;
542
543 EntityData::EntityCallData *data = getData(entityIndex);
544 byte field30 = (data->direction == kDirectionLeft ? entityIndex + 35 : 15);
545
546 if (data->sequenceName != "" && !data->sequence) {
547 data->sequence = loadSequence1(data->sequenceName, field30);
548
549 // If sequence 2 was loaded correctly, remove the copied name
550 // otherwise, compute new name
551 if (data->sequence) {
552 data->sequenceNameCopy = "";
553 } else {
554 Common::String sequenceName;
555
556 // Left and down directions
557 if (data->direction == kDirectionLeft || data->direction == kDirectionRight) {
558 COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName);
559
560 // Try loading the sequence
561 data->sequence = loadSequence1(sequenceName, field30);
562 }
563
564 // Update sequence names
565 data->sequenceNameCopy = (data->sequence ? "" : data->sequenceName);
566 data->sequenceName = (data->sequence ? sequenceName : "");
567 }
568 }
569
570 // Update sequence 3
571 if (data->sequenceName2 != "" && !data->sequence2) {
572
573 if (data->car == getData(kEntityPlayer)->car)
574 data->sequence2 = loadSequence1(data->sequenceName2, field30);
575
576 if (!data->sequence2) {
577 Common::String sequenceName;
578
579 // Left and down directions
580 if (data->directionSwitch == kDirectionLeft || data->directionSwitch == kDirectionRight) {
581 COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName2);
582
583 // Try loading the sequence
584 data->sequence2 = loadSequence1(sequenceName, field30);
585 }
586
587 // Update sequence names
588 data->sequenceName2 = (data->sequence2 ? sequenceName : "");
589 }
590 }
591 }
592 }
593
resetSequences(EntityIndex entityIndex) const594 void Entities::resetSequences(EntityIndex entityIndex) const {
595
596 // Reset direction
597 if (getData(entityIndex)->direction == kDirectionSwitch) {
598 getData(entityIndex)->direction = getData(entityIndex)->directionSwitch;
599 getData(entityIndex)->field_49B = 0;
600 getData(entityIndex)->currentFrame = -1;
601 }
602
603 SAFE_DELETE(getData(entityIndex)->frame);
604 SAFE_DELETE(getData(entityIndex)->frame1);
605
606 SAFE_DELETE(getData(entityIndex)->sequence);
607 SAFE_DELETE(getData(entityIndex)->sequence2);
608 SAFE_DELETE(getData(entityIndex)->sequence3);
609
610 getData(entityIndex)->field_4A9 = false;
611 getData(entityIndex)->field_4AA = false;
612
613 strcpy((char *)&getData(entityIndex)->sequenceNameCopy, "");
614 strcpy((char *)&getData(entityIndex)->sequenceName, "");
615 strcpy((char *)&getData(entityIndex)->sequenceName2, "");
616
617 getScenes()->resetQueue();
618 }
619
620 //////////////////////////////////////////////////////////////////////////
621 // Callbacks
622 //////////////////////////////////////////////////////////////////////////
updateCallbacks()623 void Entities::updateCallbacks() {
624 if (!getFlags()->isGameRunning)
625 return;
626
627 getFlags()->flag_entities_0 = false;
628
629 if (getFlags()->flag_entities_1) {
630 executeCallbacks();
631 getFlags()->flag_entities_0 = true;
632 } else {
633 getFlags()->flag_entities_1 = true;
634 executeCallbacks();
635 getFlags()->flag_entities_1 = false;
636 }
637 }
638
executeCallbacks()639 void Entities::executeCallbacks() {
640 for (uint i = 1; i < _entities.size(); i++) {
641 if (getFlags()->flag_entities_0)
642 break;
643
644 if (getSavePoints()->getCallback((EntityIndex)i))
645 processEntity((EntityIndex)i);
646 }
647
648 if (getFlags()->flag_entities_0)
649 return;
650
651 bool processed = true;
652 do {
653 processed = true;
654 for (int i = 1; i < (int)_entities.size(); i++) {
655 if (getFlags()->flag_entities_0)
656 break;
657
658 if (getSavePoints()->getCallback((EntityIndex)i)) {
659 if (getData((EntityIndex)i)->doProcessEntity) {
660 processed = false;
661 processEntity((EntityIndex)i);
662 }
663 }
664 }
665 } while (!processed);
666 }
667
668 //////////////////////////////////////////////////////////////////////////
669 // Processing
670 //////////////////////////////////////////////////////////////////////////
incrementDirectionCounter(EntityData::EntityCallData * data) const671 void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) const {
672 data->doProcessEntity = false;
673
674 if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight))
675 ++data->field_4A1;
676 }
677
processEntity(EntityIndex entityIndex)678 void Entities::processEntity(EntityIndex entityIndex) {
679 EntityData::EntityCallData *data = getData(entityIndex);
680 bool keepPreviousFrame = false;
681
682 data->doProcessEntity = false;
683
684 if (getData(kEntityPlayer)->car != data->car && data->direction != kDirectionRight && data->direction != kDirectionSwitch) {
685
686 if (data->position) {
687 updatePositionExit(entityIndex, data->car2, data->position);
688 data->car2 = kCarNone;
689 data->position = 0;
690 }
691
692 getScenes()->removeAndRedraw(&data->frame, false);
693 getScenes()->removeAndRedraw(&data->frame1, false);
694
695 incrementDirectionCounter(data);
696 return;
697 }
698
699 if (data->frame1) {
700 getScenes()->removeAndRedraw(&data->frame1, false);
701
702 if (data->frame && data->frame->getInfo()->subType != kFrameType3) {
703 data->frame->getInfo()->subType = kFrameTypeNone;
704 getScenes()->setFlagDrawSequences();
705 }
706 }
707
708 SAFE_DELETE(data->sequence3);
709
710 if (!data->frame || !data->direction) {
711 if (!data->sequence)
712 label_nosequence:
713 drawSequences(entityIndex, data->direction, true);
714
715 data->doProcessEntity = false;
716 computeCurrentFrame(entityIndex);
717
718 if (getFlags()->flag_entities_0 || data->doProcessEntity)
719 return;
720
721 if (data->sequence && data->currentFrame != -1 && data->currentFrame <= (int16)(data->sequence->count() - 1)) {
722 processFrame(entityIndex, false, true);
723
724 if (!getFlags()->flag_entities_0 && !data->doProcessEntity) {
725 incrementDirectionCounter(data);
726 return;
727 }
728 } else {
729 if (data->direction == kDirectionRight && data->field_4A1 > 100) {
730 getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
731 getSavePoints()->process();
732
733 if (getFlags()->flag_entities_0 || data->doProcessEntity)
734 return;
735 }
736
737 if (data->position) {
738 updatePositionExit(entityIndex, data->car2, data->position);
739 data->car2 = kCarNone;
740 data->position = 0;
741 }
742
743 incrementDirectionCounter(data);
744 }
745 return;
746 }
747
748 if (!data->sequence)
749 goto label_nosequence;
750
751 if (data->frame->getInfo()->field_30 > (data->field_49B + 1) || (data->direction == kDirectionLeft && data->sequence->count() == 1)) {
752 ++data->field_49B;
753 } else if (data->frame->getInfo()->field_30 <= data->field_49B || data->frame->getInfo()->keepPreviousFrame) {
754 if (data->frame->getInfo()->keepPreviousFrame == 1)
755 keepPreviousFrame = true;
756
757 // Increment current frame
758 ++data->currentFrame;
759
760 if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) {
761
762 if (data->direction == kDirectionLeft) {
763 data->currentFrame = 0;
764 } else {
765 keepPreviousFrame = true;
766 drawNextSequence(entityIndex);
767
768 if (getFlags()->flag_entities_0 || data->doProcessEntity)
769 return;
770
771 if (!data->sequence2) {
772 updateEntityPosition(entityIndex);
773 data->doProcessEntity = false;
774 return;
775 }
776
777 copySequenceData(entityIndex);
778 }
779
780 }
781
782 processFrame(entityIndex, keepPreviousFrame, false);
783
784 if (getFlags()->flag_entities_0 || data->doProcessEntity)
785 return;
786 } else {
787 ++data->field_49B;
788 }
789
790 incrementDirectionCounter(data);
791 }
792
computeCurrentFrame(EntityIndex entityIndex) const793 void Entities::computeCurrentFrame(EntityIndex entityIndex) const {
794 EntityData::EntityCallData *data = getData(entityIndex);
795 int16 originalCurrentFrame = data->currentFrame;
796
797 if (!data->sequence) {
798 data->currentFrame = -1;
799 return;
800 }
801
802 switch (data->direction) {
803 default:
804 break;
805
806 case kDirectionNone:
807 case kDirectionSwitch:
808 data->currentFrame = -1;
809 break;
810
811 case kDirectionUp:
812 case kDirectionDown: {
813 Scene *scene = getScenes()->get(getState()->scene);
814
815 if (scene->position > 40)
816 break;
817
818 switch (scene->position) {
819 default:
820 case 4:
821 case 19:
822 case 20:
823 case 21:
824 case 24:
825 break;
826
827 case 1:
828 case 18:
829 case 22:
830 case 40:
831 data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
832 break;
833
834 case 2:
835 case 3:
836 case 5:
837 case 6:
838 case 7:
839 case 8:
840 case 9:
841 case 10:
842 case 11:
843 case 12:
844 case 13:
845 case 14:
846 case 15:
847 case 16:
848 case 17:
849 if (data->field_4A9) {
850 if (getData(kEntityPlayer)->entityPosition >= data->entityPosition) {
851 data->currentFrame = -1;
852 } else {
853 data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
854
855 if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
856 if (data->currentFrame < (int)(data->sequence->count() - 2))
857 data->currentFrame += 2;
858 }
859 } else {
860 data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
861 }
862 break;
863
864 case 23:
865 case 25:
866 case 26:
867 case 27:
868 case 28:
869 case 29:
870 case 30:
871 case 31:
872 case 32:
873 case 33:
874 case 34:
875 case 35:
876 case 36:
877 case 37:
878 case 38:
879 case 39:
880 if (data->field_4A9) {
881 if (getData(kEntityPlayer)->entityPosition <= data->entityPosition) {
882 data->currentFrame = -1;
883 } else {
884 data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
885
886 if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
887 if (data->currentFrame < (int)(data->sequence->count() - 2))
888 data->currentFrame += 2;
889 }
890 } else {
891 data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
892 }
893 break;
894 }
895
896 }
897 break;
898
899 case kDirectionLeft:
900 if (data->currentFrame == -1 || data->currentFrame >= (int32)data->sequence->count()) {
901 data->currentFrame = 0;
902 data->field_49B = 0;
903 }
904 break;
905
906 case kDirectionRight:
907 bool found = false;
908 bool flag = false;
909 uint16 frameIndex = 0;
910 byte field30 = 0;
911
912 int16 currentFrameCopy = (!data->currentFrame && !data->field_4A1) ? -1 : data->currentFrame;
913
914 // Process frames
915 do {
916 if (frameIndex >= data->sequence->count())
917 break;
918
919 FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
920
921 if (field30 + info->field_30 >= data->field_4A1) {
922 found = true;
923 break;
924 }
925
926 if (field30 > data->field_4A1 - 10) {
927 if (info->soundAction)
928 getSound()->playSoundEvent(entityIndex, info->soundAction, (field30 <= data->field_4A1 - info->field_31) ? 0 : (byte)(field30 + info->field_31 - data->field_4A1));
929 }
930
931 field30 += info->field_30;
932
933 if (info->field_33 & 4)
934 flag = true;
935
936 if (info->field_33 & 2) {
937 flag = false;
938
939 getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
940 getSavePoints()->process();
941
942 if (getFlags()->flag_entities_0 || data->doProcessEntity)
943 return;
944 }
945
946 if (info->field_33 & 16) {
947 getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
948 getSavePoints()->process();
949
950 if (getFlags()->flag_entities_0 || data->doProcessEntity)
951 return;
952 }
953
954 frameIndex++;
955
956 } while (!found);
957
958 if (found) {
959
960 if (flag) {
961 bool found2 = false;
962
963 do {
964 if (frameIndex >= data->sequence->count())
965 break;
966
967 FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
968 if (info->field_33 & 2) {
969 found2 = true;
970
971 getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
972 getSavePoints()->process();
973
974 if (getFlags()->flag_entities_0 || data->doProcessEntity)
975 return;
976
977 } else {
978 data->field_4A1 += info->field_30;
979
980 byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
981 if (soundAction)
982 getSound()->playSoundEvent(entityIndex, soundAction);
983
984 ++frameIndex;
985 }
986
987 } while (!found2);
988
989 if (found2) {
990 data->currentFrame = frameIndex;
991 data->field_49B = 0;
992
993 byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
994 byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
995 if (soundAction && data->currentFrame != currentFrameCopy)
996 getSound()->playSoundEvent(entityIndex, soundAction, field31);
997
998 } else {
999 data->currentFrame = (int16)(data->sequence->count() - 1);
1000 data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
1001 }
1002
1003 } else {
1004
1005 data->currentFrame = frameIndex;
1006 data->field_49B = data->field_4A1 - field30;
1007
1008 byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
1009 byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
1010 if (soundAction && data->currentFrame != currentFrameCopy)
1011 getSound()->playSoundEvent(entityIndex, soundAction, field31 <= data->field_49B ? 0 : (byte)(field31 - data->field_49B));
1012 }
1013 } else {
1014 data->currentFrame = (int16)(data->sequence->count() - 1);
1015 data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
1016
1017 getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
1018 getSavePoints()->process();
1019 }
1020 break;
1021 }
1022 }
1023
getCurrentFrame(EntityIndex entity,Sequence * sequence,EntityPosition position,bool doProcessing) const1024 int16 Entities::getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const {
1025 EntityData::EntityCallData *data = getData(entity);
1026
1027 EntityPosition firstFramePosition = sequence->getFrameInfo(0)->entityPosition;
1028 EntityPosition lastFramePosition = sequence->getFrameInfo(sequence->count() - 1)->entityPosition;
1029
1030 bool isGoingForward = (firstFramePosition < lastFramePosition);
1031
1032 if (!doProcessing) {
1033 if (!isGoingForward) {
1034 if (data->field_4A3 + firstFramePosition < data->entityPosition || lastFramePosition - data->field_4A3 > data->entityPosition)
1035 return -1;
1036 } else {
1037 if (firstFramePosition - data->field_4A3 > data->entityPosition || lastFramePosition + data->field_4A3 < data->entityPosition)
1038 return -1;
1039 }
1040 }
1041
1042 if (sequence->count() == 0)
1043 return 0;
1044
1045 // Search for the correct frame
1046 // TODO: looks slightly like some sort of binary search
1047 uint16 frame = 0;
1048 uint16 numFrames = sequence->count() - 1;
1049
1050 for (;;) {
1051 uint16 currentFrame = (frame + numFrames) / 2;
1052
1053 if (position + sequence->getFrameInfo(currentFrame)->entityPosition <= data->entityPosition) {
1054 if (!isGoingForward)
1055 numFrames = (frame + numFrames) / 2;
1056 else
1057 frame = (frame + numFrames) / 2;
1058 } else {
1059 if (isGoingForward)
1060 numFrames = (frame + numFrames) / 2;
1061 else
1062 frame = (frame + numFrames) / 2;
1063 }
1064
1065 if (numFrames - frame == 1) {
1066 uint16 lastFramePos = ABS(position - (sequence->getFrameInfo(numFrames)->entityPosition + data->entityPosition));
1067 uint16 framePosition = ABS(position - (sequence->getFrameInfo(frame)->entityPosition + data->entityPosition));
1068
1069 return (framePosition > lastFramePos) ? numFrames : frame;
1070 }
1071
1072 if (numFrames <= frame)
1073 return currentFrame;
1074 }
1075 }
1076
processFrame(EntityIndex entityIndex,bool keepPreviousFrame,bool dontPlaySound)1077 void Entities::processFrame(EntityIndex entityIndex, bool keepPreviousFrame, bool dontPlaySound) {
1078 EntityData::EntityCallData *data = getData(entityIndex);
1079
1080 // Set frame to be drawn again
1081 if (data->frame && keepPreviousFrame) {
1082 if (data->frame->getInfo()->subType != kFrameType3)
1083 data->frame->getInfo()->subType = kFrameType2;
1084
1085 getScenes()->setFlagDrawSequences();
1086 }
1087
1088 // Remove old frame from queue
1089 if (data->frame && !keepPreviousFrame)
1090 getScenes()->removeFromQueue(data->frame);
1091
1092 // Stop if nothing else to draw
1093 if (data->currentFrame < 0)
1094 return;
1095
1096 if (data->currentFrame > (int)data->sequence->count())
1097 return;
1098
1099 // Get new frame info
1100 FrameInfo *info = data->sequence->getFrameInfo((uint16)data->currentFrame);
1101
1102 if (data->frame && data->frame->getInfo()->subType != kFrameType3 && (!info->field_2E || keepPreviousFrame))
1103 getScenes()->setCoordinates(data->frame);
1104
1105 // Update position
1106 if (info->entityPosition) {
1107 data->entityPosition = info->entityPosition;
1108 if (data->field_4A9)
1109 data->entityPosition = (EntityPosition)(data->entityPosition + getEntityPositionFromCurrentPosition());
1110 }
1111
1112 info->location = entityIndex + ABS(getData(entityIndex)->entityPosition - getData(kEntityPlayer)->entityPosition);
1113
1114 if (info->subType != kFrameType3) {
1115 info->subType = kFrameType1;
1116
1117 if (!keepPreviousFrame)
1118 info->subType = kFrameTypeNone;
1119 }
1120
1121 if (info->field_33 & 1)
1122 getSavePoints()->push(kEntityPlayer, entityIndex, kActionExcuseMeCath);
1123
1124 if (info->field_33 & 2) {
1125 getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
1126 getSavePoints()->process();
1127
1128 if (getFlags()->flag_entities_0 || data->doProcessEntity)
1129 return;
1130 }
1131
1132 if (info->field_33 & 16) {
1133 getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
1134 getSavePoints()->process();
1135
1136 if (getFlags()->flag_entities_0 || data->doProcessEntity)
1137 return;
1138 }
1139
1140 if (data->position) {
1141 updatePositionExit(entityIndex, data->car2, data->position);
1142 data->car2 = kCarNone;
1143 data->position = 0;
1144 }
1145
1146 if (info->position) {
1147 data->car2 = data->car;
1148 data->position = info->position;
1149 updatePositionEnter(entityIndex, data->car2, data->position);
1150
1151 if (getFlags()->flag_entities_0 || data->doProcessEntity)
1152 return;
1153 }
1154
1155 if (info->soundAction && !dontPlaySound)
1156 getSound()->playSoundEvent(entityIndex, info->soundAction, info->field_31);
1157
1158 // Add the new frame to the queue
1159 SequenceFrame *frame = new SequenceFrame(data->sequence, (uint16)data->currentFrame);
1160 getScenes()->addToQueue(frame);
1161
1162 // Keep previous frame if needed and store the new frame
1163 if (keepPreviousFrame) {
1164 SAFE_DELETE(data->frame1);
1165 data->frame1 = data->frame;
1166 } else {
1167 SAFE_DELETE(data->frame);
1168 }
1169
1170 data->frame = frame;
1171
1172 if (!dontPlaySound)
1173 data->field_49B = keepPreviousFrame ? 0 : 1;
1174 }
1175
drawNextSequence(EntityIndex entityIndex) const1176 void Entities::drawNextSequence(EntityIndex entityIndex) const {
1177 EntityData::EntityCallData *data = getData(entityIndex);
1178
1179 if (data->direction == kDirectionRight) {
1180 getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
1181 getSavePoints()->process();
1182
1183 if (getFlags()->flag_entities_0 || data->doProcessEntity)
1184 return;
1185 }
1186
1187 if (!isDirectionUpOrDown(entityIndex))
1188 return;
1189
1190 if (data->sequence2)
1191 return;
1192
1193 if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingAtDoors))
1194 return;
1195
1196 if (getData(kEntityPlayer)->car != data->car)
1197 return;
1198
1199 if (!data->field_4A9 || isWalkingOppositeToPlayer(entityIndex)) {
1200 if (!data->field_4A9 && isWalkingOppositeToPlayer(entityIndex)) {
1201 data->entityPosition = kPosition_2088;
1202
1203 if (data->direction != kDirectionUp)
1204 data->entityPosition = kPosition_8512;
1205
1206 drawSequences(entityIndex, data->direction, true);
1207 }
1208 } else {
1209 data->entityPosition = kPosition_8514;
1210
1211 if (data->direction != kDirectionUp)
1212 data->entityPosition = kPosition_2086;
1213
1214 drawSequences(entityIndex, data->direction, true);
1215 }
1216 }
1217
updateEntityPosition(EntityIndex entityIndex) const1218 void Entities::updateEntityPosition(EntityIndex entityIndex) const {
1219 EntityData::EntityCallData *data = getData(entityIndex);
1220
1221 getScenes()->removeAndRedraw(&data->frame, false);
1222
1223 SAFE_DELETE(data->frame1);
1224 data->field_49B = 0;
1225
1226 if (isDirectionUpOrDown(entityIndex)
1227 && (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
1228 && data->car == getData(kEntityPlayer)->car) {
1229
1230 if (isWalkingOppositeToPlayer(entityIndex)) {
1231 data->entityPosition = getData(kEntityPlayer)->entityPosition;
1232 } else if (data->field_4A9) {
1233 data->entityPosition = (data->direction == kDirectionUp) ? kPosition_8514 : kPosition_2086;
1234 } else {
1235 if (isPlayerPosition(kCarGreenSleeping, 1) || isPlayerPosition(kCarGreenSleeping, 40)
1236 || isPlayerPosition(kCarRedSleeping, 1) || isPlayerPosition(kCarRedSleeping, 40)) {
1237 data->entityPosition = (data->direction == kDirectionUp) ? kPosition_2588 : kPosition_8012;
1238 } else {
1239 data->entityPosition = (data->direction == kDirectionUp) ? kPosition_9271 : kPosition_849;
1240 }
1241 }
1242 }
1243
1244 SAFE_DELETE(data->sequence);
1245 data->sequenceName = "";
1246 data->field_4A9 = false;
1247
1248 if (data->directionSwitch)
1249 data->direction = data->directionSwitch;
1250 }
1251
copySequenceData(EntityIndex entityIndex) const1252 void Entities::copySequenceData(EntityIndex entityIndex) const {
1253 EntityData::EntityCallData *data = getData(entityIndex);
1254
1255 if (data->sequence)
1256 data->sequence3 = data->sequence;
1257
1258 data->sequence = data->sequence2;
1259 data->sequenceName = data->sequenceName2;
1260 data->field_4A9 = data->field_4AA;
1261
1262 if (data->directionSwitch)
1263 data->direction = data->directionSwitch;
1264
1265 // Clear sequence 3
1266 data->sequence2 = NULL;
1267 data->sequenceName2 = "";
1268 data->field_4AA = false;
1269 data->directionSwitch = kDirectionNone;
1270
1271 if (data->field_4A9) {
1272 computeCurrentFrame(entityIndex);
1273
1274 if (data->currentFrame == -1)
1275 data->currentFrame = 0;
1276 } else {
1277 data->currentFrame = data->currentFrame2;
1278 data->currentFrame2 = 0;
1279
1280 if (data->currentFrame == -1)
1281 data->currentFrame = 0;
1282 }
1283 }
1284
1285 //////////////////////////////////////////////////////////////////////////
1286 // Drawing
1287 //////////////////////////////////////////////////////////////////////////
drawSequenceLeft(EntityIndex index,const char * sequence) const1288 void Entities::drawSequenceLeft(EntityIndex index, const char *sequence) const {
1289 drawSequence(index, sequence, kDirectionLeft);
1290 }
1291
drawSequenceRight(EntityIndex index,const char * sequence) const1292 void Entities::drawSequenceRight(EntityIndex index, const char *sequence) const {
1293 drawSequence(index, sequence, kDirectionRight);
1294 }
1295
clearSequences(EntityIndex entityIndex) const1296 void Entities::clearSequences(EntityIndex entityIndex) const {
1297 debugC(8, kLastExpressDebugLogic, "Clear sequences for entity %s", ENTITY_NAME(entityIndex));
1298
1299 EntityData::EntityCallData *data = getData(entityIndex);
1300
1301 getScenes()->removeAndRedraw(&data->frame, false);
1302 getScenes()->removeAndRedraw(&data->frame1, false);
1303
1304 if (data->sequence2) {
1305 SAFE_DELETE(data->sequence2);
1306 data->sequenceName2 = "";
1307 data->field_4AA = false;
1308 data->directionSwitch = kDirectionNone;
1309 }
1310
1311 if (data->sequence) {
1312 SAFE_DELETE(data->sequence);
1313 data->sequenceName = "";
1314 data->field_4A9 = false;
1315 data->currentFrame = -1;
1316 }
1317
1318 data->sequenceNamePrefix = "";
1319 data->direction = kDirectionNone;
1320 data->doProcessEntity = true;
1321 }
1322
drawSequence(EntityIndex index,const char * sequence,EntityDirection direction) const1323 void Entities::drawSequence(EntityIndex index, const char *sequence, EntityDirection direction) const {
1324 debugC(8, kLastExpressDebugLogic, "Drawing sequence %s for entity %s with direction %s", sequence, ENTITY_NAME(index), DIRECTION_NAME(direction));
1325
1326 // Copy sequence name
1327 getData(index)->sequenceNamePrefix = sequence;
1328 getData(index)->sequenceNamePrefix.toUppercase();
1329 getData(index)->sequenceNamePrefix += "-";
1330
1331 // Reset fields
1332 getData(index)->field_49B = 0;
1333 getData(index)->currentFrame = 0;
1334 getData(index)->field_4A1 = 0;
1335
1336 drawSequences(index, direction, true);
1337 }
1338
drawSequences(EntityIndex entityIndex,EntityDirection direction,bool loadSequence) const1339 void Entities::drawSequences(EntityIndex entityIndex, EntityDirection direction, bool loadSequence) const {
1340 EntityData::EntityCallData *data = getData(entityIndex);
1341
1342 // Compute value for loading sequence depending on direction
1343 byte field30 = (direction == kDirectionLeft ? entityIndex + 35 : 15);
1344
1345 data->doProcessEntity = true;
1346 bool field4A9 = data->field_4A9;
1347
1348 // First case: different car and not going right: cleanup and return
1349 if (data->car != getData(kEntityPlayer)->car && direction != kDirectionRight) {
1350 clearEntitySequenceData(data, direction);
1351 return;
1352 }
1353
1354 data->directionSwitch = kDirectionNone;
1355
1356 // Process sequence names
1357 Common::String sequenceName;
1358 Common::String sequenceName1;
1359 Common::String sequenceName2;
1360 Common::String sequenceName3;
1361
1362 getSequenceName(entityIndex, direction, sequenceName1, sequenceName2);
1363
1364 // No sequence 1: cleanup and return
1365 if (sequenceName1 == "") {
1366 clearEntitySequenceData(data, direction);
1367 return;
1368 }
1369
1370 if (sequenceName1 == data->sequenceNameCopy) {
1371 data->direction = direction;
1372 return;
1373 }
1374
1375 if (direction == kDirectionLeft || direction == kDirectionRight) {
1376 COMPUTE_SEQUENCE_NAME(sequenceName, sequenceName1);
1377
1378 if (sequenceName3 != "")
1379 COMPUTE_SEQUENCE_NAME(sequenceName3, sequenceName2);
1380 }
1381
1382 if (!data->frame) {
1383 data->direction = direction;
1384
1385 if (sequenceName1 == data->sequenceName) {
1386 if (sequenceName2 == "")
1387 return;
1388
1389 loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
1390 return;
1391 }
1392
1393 SAFE_DELETE(data->sequence);
1394
1395 if (sequenceName1 != data->sequenceName2) {
1396
1397 if (loadSequence) {
1398
1399 if (data->car == getData(kEntityPlayer)->car)
1400 data->sequence = loadSequence1(sequenceName1, field30);
1401
1402 if (data->sequence) {
1403 data->sequenceName = sequenceName1;
1404 data->sequenceNameCopy = "";
1405 } else {
1406 if (sequenceName != "")
1407 data->sequence = loadSequence1(sequenceName, field30);
1408
1409 data->sequenceName = (data->sequence ? sequenceName : "");
1410 data->sequenceNameCopy = (data->sequence ? "" : sequenceName1);
1411 }
1412 } else {
1413 data->sequenceName = sequenceName1;
1414 }
1415
1416 if (sequenceName2 != "") {
1417 loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
1418 return;
1419 }
1420
1421 if (!data->sequence2) {
1422 if (sequenceName2 == "")
1423 return;
1424
1425 loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
1426 return;
1427 }
1428
1429 SAFE_DELETE(data->sequence2);
1430 } else {
1431 data->sequence = data->sequence2;
1432 data->sequenceName = data->sequenceName2;
1433 data->sequence2 = NULL;
1434 }
1435
1436 data->sequenceName2 = "";
1437
1438 if (sequenceName2 == "")
1439 return;
1440
1441 loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
1442 return;
1443 }
1444
1445 if (data->sequenceName != sequenceName1) {
1446
1447 if (data->sequenceName2 != sequenceName1) {
1448 SAFE_DELETE(data->sequence2);
1449 TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName1, sequenceName);
1450 }
1451
1452 data->field_4AA = data->field_4A9;
1453 if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
1454 data->currentFrame2 = 0;
1455 } else {
1456 data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
1457
1458 if (data->currentFrame2 == -1) {
1459 clearSequences(entityIndex);
1460 return;
1461 }
1462 }
1463
1464 data->field_4A9 = field4A9;
1465 data->field_49B = data->frame->getInfo()->field_30;
1466 data->currentFrame = (int16)(data->sequence->count() - 1);
1467 data->direction = kDirectionSwitch;
1468 data->directionSwitch = direction;
1469 } else {
1470 SAFE_DELETE(data->sequence2);
1471
1472 data->sequence2 = loadSequence1(data->sequence->getName(), data->sequence->getField30());
1473
1474 data->sequenceName2 = data->sequenceName;
1475 data->field_4AA = data->field_4A9;
1476 data->field_49B = data->frame->getInfo()->field_30;
1477 data->currentFrame = (int16)(data->sequence->count() - 1);
1478 data->direction = kDirectionSwitch;
1479 data->directionSwitch = direction;
1480
1481 if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
1482 data->currentFrame2 = 0;
1483 } else {
1484 data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
1485
1486 if (data->currentFrame2 == -1)
1487 clearSequences(entityIndex);
1488 }
1489 }
1490 }
1491
loadSequence2(EntityIndex entityIndex,Common::String sequenceName,Common::String sequenceName2,byte field30,bool reloadSequence) const1492 void Entities::loadSequence2(EntityIndex entityIndex, Common::String sequenceName, Common::String sequenceName2, byte field30, bool reloadSequence) const {
1493 EntityData::EntityCallData *data = getData(entityIndex);
1494
1495 if (data->sequenceName2 == sequenceName)
1496 return;
1497
1498 if (data->sequence2)
1499 SAFE_DELETE(data->sequence2);
1500
1501 if (reloadSequence) {
1502 TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName, sequenceName2);
1503 } else {
1504 data->sequenceName2 = sequenceName;
1505 }
1506 }
1507
getSequenceName(EntityIndex index,EntityDirection direction,Common::String & sequence1,Common::String & sequence2) const1508 void Entities::getSequenceName(EntityIndex index, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const {
1509 EntityData::EntityCallData *data = getData(index);
1510 Position position = getScenes()->get(getState()->scene)->position;
1511
1512 // reset fields
1513 data->field_4A9 = false;
1514 data->field_4AA = false;
1515
1516 switch (direction) {
1517 default:
1518 break;
1519
1520 case kDirectionUp:
1521 switch (position) {
1522 default:
1523 break;
1524
1525 case 1:
1526 if (data->entityPosition < kPosition_2587)
1527 sequence1 = Common::String::format("%02d%01d-01u.seq", index, data->clothes);
1528 break;
1529
1530 case 2:
1531 case 3:
1532 case 5:
1533 case 6:
1534 case 7:
1535 case 8:
1536 case 9:
1537 case 10:
1538 case 11:
1539 case 12:
1540 case 13:
1541 case 14:
1542 case 15:
1543 case 16:
1544 case 17:
1545 if (data->entityPosition >= kPosition_9270)
1546 break;
1547
1548 if (data->entityPosition >= kPosition_8513) {
1549 sequence1 = Common::String::format("%02d%01d-%02deu.seq", index, data->clothes, position);
1550 } else {
1551 sequence1 = Common::String::format("%02d%01d-03u.seq", index, data->clothes);
1552 sequence2 = Common::String::format("%02d%01d-%02deu.seq", index, data->clothes, position);
1553 data->field_4A9 = true;
1554 }
1555 break;
1556
1557 case 18:
1558 if (data->entityPosition < kPosition_9270)
1559 sequence1 = Common::String::format("%02d%01d-18u.seq", index, data->clothes);
1560 break;
1561
1562 case 22:
1563 if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
1564 sequence1 = Common::String::format("%02d%01d-22u.seq", index, data->clothes);
1565 break;
1566
1567 case 23:
1568 case 25:
1569 case 26:
1570 case 27:
1571 case 28:
1572 case 29:
1573 case 30:
1574 case 31:
1575 case 32:
1576 case 33:
1577 case 34:
1578 case 35:
1579 case 36:
1580 case 37:
1581 case 38:
1582 case 39:
1583 if (getData(kEntityPlayer)->entityPosition <= data->entityPosition)
1584 break;
1585
1586 if (data->entityPosition >= kPosition_2087) {
1587 sequence1 = Common::String::format("%02d%01d-38u.seq", index, data->clothes);
1588 data->field_4A9 = true;
1589 } else {
1590 sequence1 = Common::String::format("%02d%01d-%02deu.seq", index, data->clothes, position);
1591 sequence2 = Common::String::format("%02d%01d-38u.seq", index, data->clothes);
1592 data->field_4AA = true;
1593 }
1594 break;
1595
1596 case 40:
1597 if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
1598 sequence1 = Common::String::format("%02d%01d-40u.seq", index, data->clothes);
1599 break;
1600 }
1601 break;
1602
1603 case kDirectionDown:
1604 switch (position) {
1605 default:
1606 break;
1607
1608 case 1:
1609 if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
1610 sequence1 = Common::String::format("%02d%01d-01d.seq", index, data->clothes);
1611 break;
1612
1613 case 2:
1614 case 3:
1615 case 5:
1616 case 6:
1617 case 7:
1618 case 8:
1619 case 9:
1620 case 10:
1621 case 11:
1622 case 12:
1623 case 13:
1624 case 14:
1625 case 15:
1626 case 16:
1627 case 17:
1628 if (getData(kEntityPlayer)->entityPosition >= data->entityPosition)
1629 break;
1630
1631 if (data->entityPosition <= kPosition_8513) {
1632 sequence1 = Common::String::format("%02d%01d-03d.seq", index, data->clothes);
1633 data->field_4A9 = true;
1634 } else {
1635 sequence1 = Common::String::format("%02d%01d-%02ded.seq", index, data->clothes, position);
1636 sequence2 = Common::String::format("%02d%01d-03d.seq", index, data->clothes);
1637 data->field_4AA = true;
1638 }
1639 break;
1640
1641 case 18:
1642 if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
1643 sequence1 = Common::String::format("%02d%01d-18d.seq", index, data->clothes);
1644 break;
1645
1646 case 22:
1647 if (data->entityPosition > kPosition_850)
1648 sequence1 = Common::String::format("%02d%01d-22d.seq", index, data->clothes);
1649 break;
1650
1651 case 23:
1652 case 25:
1653 case 26:
1654 case 27:
1655 case 28:
1656 case 29:
1657 case 30:
1658 case 31:
1659 case 32:
1660 case 33:
1661 case 34:
1662 case 35:
1663 case 36:
1664 case 37:
1665 case 38:
1666 case 39:
1667 if (data->entityPosition <= kPosition_850)
1668 break;
1669
1670 if (data->entityPosition <= kPosition_2087) {
1671 sequence1 = Common::String::format("%02d%01d-%02ded.seq", index, data->clothes, position);
1672 } else {
1673 sequence1 = Common::String::format("%02d%01d-38d.seq", index, data->clothes);
1674 sequence2 = Common::String::format("%02d%01d-%02ded.seq", index, data->clothes, position);
1675 data->field_4A9 = true;
1676 }
1677 break;
1678
1679 case 40:
1680 if (getData(kEntityPlayer)->entityPosition > kPosition_8013)
1681 sequence1 = Common::String::format("%02d%01d-40d.seq", index, data->clothes);
1682 break;
1683 }
1684 break;
1685
1686 // First part of sequence is already set
1687 case kDirectionLeft:
1688 case kDirectionRight:
1689 sequence1 = Common::String::format("%s%02d.seq", data->sequenceNamePrefix.c_str(), position);
1690 break;
1691 }
1692 }
1693
1694 //////////////////////////////////////////////////////////////////////////
1695 /// Compartments
1696 //////////////////////////////////////////////////////////////////////////
enterCompartment(EntityIndex entity,ObjectIndex compartment,bool useCompartment1)1697 void Entities::enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
1698 if (entity > kEntityChapters)
1699 return;
1700
1701 switch (compartment) {
1702 default:
1703 // Return here so we do not update the compartments
1704 return;
1705
1706 case kObjectCompartment1:
1707 updatePositionsEnter(entity, kCarGreenSleeping, 41, 51, 17, 38);
1708 break;
1709
1710 case kObjectCompartment2:
1711 updatePositionsEnter(entity, kCarGreenSleeping, 42, 52, 15, 36);
1712 break;
1713
1714 case kObjectCompartment3:
1715 updatePositionsEnter(entity, kCarGreenSleeping, 43, 53, 13, 34);
1716 break;
1717
1718 case kObjectCompartment4:
1719 updatePositionsEnter(entity, kCarGreenSleeping, 44, 54, 11, 32);
1720 break;
1721
1722 case kObjectCompartment5:
1723 updatePositionsEnter(entity, kCarGreenSleeping, 45, 55, 9, 30);
1724 break;
1725
1726 case kObjectCompartment6:
1727 updatePositionsEnter(entity, kCarGreenSleeping, 46, 56, 7, 28);
1728 break;
1729
1730 case kObjectCompartment7:
1731 updatePositionsEnter(entity, kCarGreenSleeping, 47, 57, 5, 26);
1732 break;
1733
1734 case kObjectCompartment8:
1735 updatePositionsEnter(entity, kCarGreenSleeping, 48, 58, 3, 25);
1736 break;
1737
1738 case kObjectCompartmentA:
1739 updatePositionsEnter(entity, kCarRedSleeping, 41, 51, 17, 38);
1740 break;
1741
1742 case kObjectCompartmentB:
1743 updatePositionsEnter(entity, kCarRedSleeping, 42, 52, 15, 36);
1744 break;
1745
1746 case kObjectCompartmentC:
1747 updatePositionsEnter(entity, kCarRedSleeping, 43, 53, 13, 34);
1748 break;
1749
1750 case kObjectCompartmentD:
1751 updatePositionsEnter(entity, kCarRedSleeping, 44, 54, 11, 32);
1752 break;
1753
1754 case kObjectCompartmentE:
1755 updatePositionsEnter(entity, kCarRedSleeping, 45, 55, 9, 30);
1756 break;
1757
1758 case kObjectCompartmentF:
1759 updatePositionsEnter(entity, kCarRedSleeping, 46, 56, 7, 28);
1760 break;
1761
1762 case kObjectCompartmentG:
1763 updatePositionsEnter(entity, kCarRedSleeping, 47, 57, 5, 26);
1764 break;
1765
1766 case kObjectCompartmentH:
1767 updatePositionsEnter(entity, kCarRedSleeping, 48, 58, 3, 25);
1768 break;
1769 }
1770
1771 // Update compartments
1772 int index = (compartment < 32 ? compartment - 1 : compartment - 24);
1773 assert(index < 16);
1774
1775 if (useCompartment1)
1776 _compartments1[index] |= STORE_VALUE(entity);
1777 else
1778 _compartments[index] |= STORE_VALUE(entity);
1779 }
1780
exitCompartment(EntityIndex entity,ObjectIndex compartment,bool useCompartment1)1781 void Entities::exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
1782 if (entity > kEntityChapters)
1783 return;
1784
1785 // TODO factorize in one line
1786 switch (compartment) {
1787 default:
1788 // Return here so we do not update the compartments
1789 return;
1790
1791 case kObjectCompartment1:
1792 updatePositionsExit(entity, kCarGreenSleeping, 41, 51);
1793 break;
1794
1795 case kObjectCompartment2:
1796 updatePositionsExit(entity, kCarGreenSleeping, 42, 52);
1797 break;
1798
1799 case kObjectCompartment3:
1800 updatePositionsExit(entity, kCarGreenSleeping, 43, 53);
1801 break;
1802
1803 case kObjectCompartment4:
1804 updatePositionsExit(entity, kCarGreenSleeping, 44, 54);
1805 break;
1806
1807 case kObjectCompartment5:
1808 updatePositionsExit(entity, kCarGreenSleeping, 45, 55);
1809 break;
1810
1811 case kObjectCompartment6:
1812 updatePositionsExit(entity, kCarGreenSleeping, 46, 56);
1813 break;
1814
1815 case kObjectCompartment7:
1816 updatePositionsExit(entity, kCarGreenSleeping, 47, 57);
1817 break;
1818
1819 case kObjectCompartment8:
1820 updatePositionsExit(entity, kCarGreenSleeping, 48, 58);
1821 break;
1822
1823 case kObjectCompartmentA:
1824 updatePositionsExit(entity, kCarRedSleeping, 41, 51);
1825 break;
1826
1827 case kObjectCompartmentB:
1828 updatePositionsExit(entity, kCarRedSleeping, 42, 52);
1829 break;
1830
1831 case kObjectCompartmentC:
1832 updatePositionsExit(entity, kCarRedSleeping, 43, 53);
1833 break;
1834
1835 case kObjectCompartmentD:
1836 updatePositionsExit(entity, kCarRedSleeping, 44, 54);
1837 break;
1838
1839 case kObjectCompartmentE:
1840 updatePositionsExit(entity, kCarRedSleeping, 45, 55);
1841 break;
1842
1843 case kObjectCompartmentF:
1844 updatePositionsExit(entity, kCarRedSleeping, 46, 56);
1845 break;
1846
1847 case kObjectCompartmentG:
1848 updatePositionsExit(entity, kCarRedSleeping, 47, 57);
1849 break;
1850
1851 case kObjectCompartmentH:
1852 updatePositionsExit(entity, kCarRedSleeping, 48, 58);
1853 break;
1854 }
1855
1856 // Update compartments
1857 int index = (compartment < 32 ? compartment - 1 : compartment - 24);
1858 assert(index < 16);
1859
1860 if (useCompartment1)
1861 _compartments1[index] &= ~STORE_VALUE(entity);
1862 else
1863 _compartments[index] &= ~STORE_VALUE(entity);
1864 }
1865
updatePositionEnter(EntityIndex entity,CarIndex car,Position position)1866 void Entities::updatePositionEnter(EntityIndex entity, CarIndex car, Position position) {
1867 if (entity == kEntity39)
1868 entity = kEntityPlayer;
1869
1870 if (entity > kEntityChapters)
1871 return;
1872
1873 _positions[100 * car + position] |= STORE_VALUE(entity);
1874
1875 if (isPlayerPosition(car, position) || (car == kCarRestaurant && position == 57 && isPlayerPosition(kCarRestaurant, 50))) {
1876 getSound()->excuseMe(entity);
1877 getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
1878 getSound()->playSound(kEntityPlayer, "CAT1127A");
1879 } else {
1880 getLogic()->updateCursor();
1881 }
1882 }
1883
updatePositionExit(EntityIndex entity,CarIndex car,Position position)1884 void Entities::updatePositionExit(EntityIndex entity, CarIndex car, Position position) {
1885 if (entity == kEntity39)
1886 entity = kEntityPlayer;
1887
1888 if (entity > kEntityChapters)
1889 return;
1890
1891 _positions[100 * car + position] &= ~STORE_VALUE(entity);
1892
1893 getLogic()->updateCursor();
1894 }
1895
updatePositionsEnter(EntityIndex entity,CarIndex car,Position position1,Position position2,Position position3,Position position4)1896 void Entities::updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4) {
1897 if (entity == kEntity39)
1898 entity = kEntityPlayer;
1899
1900 if (entity > kEntityChapters)
1901 return;
1902
1903 _positions[100 * car + position1] |= STORE_VALUE(entity);
1904 _positions[100 * car + position2] |= STORE_VALUE(entity);
1905
1906 // FIXME: also checking two DWORD values that do not seem to updated anywhere...
1907 if (isPlayerPosition(car, position1) || isPlayerPosition(car, position2) || isPlayerPosition(car, position3) || isPlayerPosition(car, position4)) {
1908 getSound()->excuseMe(entity);
1909 getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
1910 getSound()->playSound(kEntityPlayer, "CAT1127A");
1911 } else {
1912 getLogic()->updateCursor();
1913 }
1914 }
1915
updatePositionsExit(EntityIndex entity,CarIndex car,Position position1,Position position2)1916 void Entities::updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2) {
1917 if (entity == kEntity39)
1918 entity = kEntityPlayer;
1919
1920 if (entity > kEntityChapters)
1921 return;
1922
1923 _positions[100 * car + position1] &= ~STORE_VALUE(entity);
1924 _positions[100 * car + position2] &= ~STORE_VALUE(entity);
1925
1926 getLogic()->updateCursor();
1927 }
1928
loadSceneFromEntityPosition(CarIndex car,EntityPosition entityPosition,bool alternate) const1929 void Entities::loadSceneFromEntityPosition(CarIndex car, EntityPosition entityPosition, bool alternate) const {
1930
1931 // Determine position
1932 Position position = (alternate ? 1 : 40);
1933 do {
1934 if (alternate ? entityPosition < entityPositions[position] : entityPosition > entityPositions[position]) {
1935 if (alternate)
1936 break;
1937
1938 // For default value, we ignore position 24
1939 if (position != 24)
1940 break;
1941 }
1942
1943 alternate ? ++position : --position;
1944
1945 } while (alternate ? position <= 18 : position >= 22);
1946
1947 // For position outside bounds, use minimal value
1948 if ((alternate && position > 18) || (!alternate && position < 22)) {
1949 getScenes()->loadSceneFromPosition(car, alternate ? 18 : 22);
1950 return;
1951 }
1952
1953 // Load scene from position
1954 switch (position) {
1955 default:
1956 getScenes()->loadSceneFromPosition(car, (Position)(position + (alternate ? - 1 : 1)));
1957 break;
1958
1959 // Alternate
1960 case 1:
1961 if (alternate) getScenes()->loadSceneFromPosition(car, 1);
1962 break;
1963
1964 case 5:
1965 if (alternate) getScenes()->loadSceneFromPosition(car, 3);
1966 break;
1967
1968 // Default
1969 case 23:
1970 if (!alternate) getScenes()->loadSceneFromPosition(car, 25);
1971 break;
1972
1973 case 40:
1974 if (!alternate) getScenes()->loadSceneFromPosition(car, 40);
1975 break;
1976 }
1977 }
1978
1979 //////////////////////////////////////////////////////////////////////////
1980 // Checks
1981 //////////////////////////////////////////////////////////////////////////
hasValidFrame(EntityIndex entity) const1982 bool Entities::hasValidFrame(EntityIndex entity) const {
1983 return (getData(entity)->frame && (getData(entity)->frame->getInfo()->subType != kFrameType3));
1984 }
1985
compare(EntityIndex entity1,EntityIndex entity2) const1986 bool Entities::compare(EntityIndex entity1, EntityIndex entity2) const {
1987 EntityData::EntityCallData *data1 = getData(entity1);
1988 EntityData::EntityCallData *data2 = getData(entity2);
1989
1990 if (data2->car != data1->car
1991 || data1->car < kCarGreenSleeping
1992 || data1->car > kCarRedSleeping)
1993 return false;
1994
1995 EntityPosition position1 = (data1->entityPosition >= data2->entityPosition) ? data1->entityPosition : data2->entityPosition;
1996 EntityPosition position2 = (data1->entityPosition >= data2->entityPosition) ? data2->entityPosition : data1->entityPosition;
1997
1998 // Compute position
1999 int index1 = 7;
2000 do {
2001 if (objectsPosition[index1] >= position2)
2002 break;
2003
2004 --index1;
2005 } while (index1 > -1);
2006
2007 int index2 = 0;
2008 do {
2009 if (objectsPosition[index2] <= position2)
2010 break;
2011
2012 ++index2;
2013 } while (index2 < 8);
2014
2015 if (index1 > -1 && index2 < 8 && index2 <= index1) {
2016 while (index2 <= index1) {
2017 if (getCompartments(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
2018 return true;
2019
2020 if (getCompartments1(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
2021 return true;
2022
2023 ++index2;
2024 }
2025 }
2026
2027 for (EntityIndex entity = kEntityAnna; entity <= kEntity39; entity = (EntityIndex)(entity + 1)) {
2028
2029 if (entity1 == entity || entity2 == entity)
2030 continue;
2031
2032 if (!isDirectionUpOrDown(entity))
2033 continue;
2034
2035 if (data1->car == getEntityData(entity)->car
2036 && getEntityData(entity)->entityPosition > position2
2037 && getEntityData(entity)->entityPosition < position1)
2038 return true;
2039 }
2040
2041 return false;
2042 }
2043
updateEntity(EntityIndex entity,CarIndex car,EntityPosition position) const2044 bool Entities::updateEntity(EntityIndex entity, CarIndex car, EntityPosition position) const {
2045 EntityData::EntityCallData *data = getData(entity);
2046 EntityDirection direction = kDirectionNone;
2047 int delta = 0;
2048 bool flag1 = false;
2049 bool flag2 = false;
2050 bool flag3 = false;
2051
2052 if (position == kPosition_2000
2053 && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
2054 && !isPlayerPosition(kCarGreenSleeping, 1)
2055 && !isPlayerPosition(kCarRedSleeping, 2))
2056 position = kPosition_1500;
2057
2058 if (data->direction != kDirectionUp && data->direction != kDirectionDown)
2059 data->field_497 = 0;
2060
2061 if (data->field_497) {
2062 data->field_497--;
2063
2064 if (data->field_497 == 128)
2065 data->field_497 = 0;
2066
2067 if ((data->field_497 & 127) != 8) {
2068 data->field_49B = 0;
2069 return false;
2070 }
2071
2072 flag1 = true;
2073
2074 if (data->field_497 & 128)
2075 flag2 = true;
2076 }
2077
2078 if (data->car != car)
2079 goto label_process_entity;
2080
2081 // Calculate delta
2082 delta = ABS(data->entityPosition - position);
2083 if (delta < 100 || (position > kPosition_850 && position < kPosition_9270 && delta < 300))
2084 flag3 = true;
2085
2086 if (!flag3) {
2087 if ((getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && data->direction == kDirectionUp)
2088 || (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && data->direction == kDirectionDown)) {
2089 if (!checkPosition(position) && isDistanceBetweenEntities(entity, kEntityPlayer, 250))
2090 flag3 = true;
2091 }
2092
2093 if (!flag3)
2094 goto label_process_entity;
2095 }
2096
2097 if (getEntities()->hasValidFrame(entity)
2098 && getEntities()->isWalkingOppositeToPlayer(entity)
2099 && !getEntities()->checkPosition(position)) {
2100 flag3 = false;
2101 position = (EntityPosition)(getData(kEntityPlayer)->entityPosition + 250 * (data->direction == kDirectionUp ? 1 : -1));
2102 }
2103
2104 if (!flag3) {
2105 label_process_entity:
2106
2107 // Calculate direction
2108 if (data->car < car)
2109 direction = kDirectionUp;
2110 else if (data->car > car)
2111 direction = kDirectionDown;
2112 else // same car
2113 direction = (data->entityPosition < position) ? kDirectionUp : kDirectionDown;
2114
2115 if (data->direction == direction) {
2116 if (!flag1) {
2117
2118 if (checkDistanceFromPosition(entity, kPosition_1500, 750) && entity != kEntityFrancois) {
2119
2120 if (data->entity == kEntityPlayer) {
2121 if (data->direction != kDirectionUp || (position <= kPosition_2000 && data->car == car)) {
2122 if (data->direction == kDirectionDown && (position < kPosition_1500 || data->car != car)) {
2123 if (data->entityPosition > kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
2124 data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
2125 getSavePoints()->push(entity, data->entity, kAction11);
2126 }
2127 }
2128 } else {
2129 if (data->entityPosition < kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
2130 data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
2131 getSavePoints()->push(entity, data->entity, kAction11, 1);
2132 }
2133 }
2134 }
2135
2136 } else if (data->entity) {
2137 getSavePoints()->push(entity, data->entity, kAction16);
2138 data->entity = kEntityPlayer;
2139 }
2140
2141 if (hasValidFrame(entity)) {
2142
2143 if (!data->field_4A9)
2144 return false;
2145
2146 int compartmentIndex = 0;
2147 if (data->car == kCarGreenSleeping)
2148 compartmentIndex = 0;
2149 else if (data->car == kCarRedSleeping)
2150 compartmentIndex = 8;
2151
2152 for (int i = 0; i < 8; i++) {
2153 if (getCompartments(compartmentIndex) || getCompartments1(compartmentIndex)) {
2154 if (checkDistanceFromPosition(entity, objectsPosition[i], 750)) {
2155 if (checkPosition(objectsPosition[i])) {
2156
2157 if ((data->direction == kDirectionUp && data->entityPosition < objectsPosition[i] && (data->car != car || position > objectsPosition[i]))
2158 || (data->direction == kDirectionDown && data->entityPosition > objectsPosition[i] && (data->car != car || position < objectsPosition[i]))) {
2159
2160 getSound()->excuseMe(entity, (EntityIndex)(State::getPowerOfTwo((uint32)(getCompartments(compartmentIndex) ? getCompartments(compartmentIndex) : getCompartments1(compartmentIndex)))));
2161
2162 data->field_497 = 144;
2163
2164 break;
2165 }
2166 }
2167 }
2168 }
2169
2170 compartmentIndex++;
2171 }
2172
2173 for (EntityIndex entityIndex = kEntityAnna; entityIndex <= kEntity39; entityIndex = (EntityIndex)(entityIndex + 1)) {
2174 if (getSavePoints()->getCallback(entityIndex)
2175 && hasValidFrame(entityIndex)
2176 && entityIndex != entity
2177 && isDistanceBetweenEntities(entity, entityIndex, 750)
2178 && isDirectionUpOrDown(entityIndex)
2179 && (entity != kEntityRebecca || entityIndex != kEntitySophie)
2180 && (entity != kEntitySophie || entityIndex != kEntityRebecca)
2181 && (entity != kEntityIvo || entityIndex != kEntitySalko)
2182 && (entity != kEntitySalko || entityIndex != kEntityIvo)
2183 && (entity != kEntityMilos || entityIndex != kEntityVesna)
2184 && (entity != kEntityVesna || entityIndex != kEntityMilos)) {
2185
2186 EntityData::EntityCallData *data2 = getData(entityIndex);
2187
2188 if (data->direction != data2->direction) {
2189
2190 if ((data->direction != kDirectionUp || data2->entityPosition <= data->entityPosition)
2191 && (data->direction != kDirectionDown || data2->entityPosition >= data->entityPosition))
2192 continue;
2193
2194 data->field_49B = 0;
2195 data2->field_49B = 0;
2196
2197 data->field_497 = 16;
2198 data2->field_497 = 16;
2199
2200 getSound()->excuseMe(entity, entityIndex);
2201 getSound()->excuseMe(entityIndex, entity);
2202
2203 if (entityIndex > entity)
2204 ++data2->field_497;
2205
2206 break;
2207 }
2208
2209 if (ABS(data2->entityPosition - getData(kEntityPlayer)->entityPosition) < ABS(data->entityPosition - getData(kEntityPlayer)->entityPosition)) {
2210
2211 if (!isWalkingOppositeToPlayer(entity)) {
2212
2213 if (direction == kDirectionUp) {
2214 if (data->entityPosition < kPosition_9500)
2215 data->entityPosition = (EntityPosition)(data->entityPosition + 500);
2216 } else {
2217 if (data->entityPosition > kPosition_500)
2218 data->entityPosition = (EntityPosition)(data->entityPosition - 500);
2219 }
2220
2221 drawSequences(entity, direction, true);
2222
2223 return false;
2224 }
2225 data->field_49B = 0;
2226
2227 break;
2228 }
2229 }
2230 }
2231
2232 return false;
2233 }
2234
2235 if (data->direction == kDirectionUp) {
2236 if (data->entityPosition + data->field_4A3 < 10000)
2237 data->entityPosition = (EntityPosition)(data->entityPosition + data->field_4A3);
2238 } else {
2239 if (data->entityPosition > data->field_4A3)
2240 data->entityPosition = (EntityPosition)(data->entityPosition - data->field_4A3);
2241 }
2242
2243 if (data->entityPosition <= kPosition_9270 || data->direction != kDirectionUp) {
2244 if (data->entityPosition < kPosition_850 && data->direction == kDirectionDown) {
2245 if (changeCar(data, entity, car, position, false, kPosition_9269, kCarKronos))
2246 return true;
2247 }
2248 } else {
2249 if (changeCar(data, entity, car, position, true, kPosition_851, kCarGreenSleeping))
2250 return true;
2251 }
2252
2253 if (getData(kEntityPlayer)->car == data->car && data->location == kLocationOutsideCompartment) {
2254 if (data->direction == kDirectionUp) {
2255
2256 if (getData(kEntityPlayer)->entityPosition > data->entityPosition
2257 && getData(kEntityPlayer)->entityPosition - data->entityPosition >= 500
2258 && data->field_4A3 + 500 > getData(kEntityPlayer)->entityPosition - data->entityPosition) {
2259
2260 if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkCurrentPosition(false)) {
2261 getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
2262
2263 if (getScenes()->checkCurrentPosition(false))
2264 getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1, true);
2265
2266 } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)) {
2267 getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
2268 }
2269 }
2270 } else {
2271 if (getData(kEntityPlayer)->entityPosition < data->entityPosition
2272 && data->entityPosition - getData(kEntityPlayer)->entityPosition >= 500
2273 && data->field_4A3 + 500 > data->entityPosition - getData(kEntityPlayer)->entityPosition) {
2274
2275 if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) {
2276 getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
2277 } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)) {
2278 getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
2279
2280 if (getScenes()->checkCurrentPosition(false))
2281 getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1);
2282 }
2283 }
2284 }
2285 }
2286 return false;
2287 }
2288 } else if (!flag1) {
2289 drawSequences(entity, direction, true);
2290 return false;
2291 }
2292
2293 //////////////////////////////////////////////////////////////////////////
2294 // Adjust positions
2295
2296 // Direction Up
2297 if (direction == kDirectionUp) {
2298 if (data->entityPosition < (flag2 ? kPosition_8800 : kPosition_9250))
2299 data->entityPosition = (EntityPosition)(data->entityPosition + (flag2 ? kPosition_1200 : kPosition_750));
2300
2301 if (data->car == car && data->entityPosition >= position) {
2302 data->entityPosition = position;
2303 data->direction = kDirectionNone;
2304 data->entity = kEntityPlayer;
2305 return true;
2306 }
2307
2308 drawSequences(entity, direction, true);
2309 return false;
2310 }
2311
2312 // Direction Down
2313 if (data->entityPosition > (flag2 ? kPosition_1200 : kPosition_750))
2314 data->entityPosition = (EntityPosition)(data->entityPosition - (flag2 ? kPosition_1200 : kPosition_750));
2315
2316 if (data->car == car && data->entityPosition <= position) {
2317 data->entityPosition = position;
2318 data->direction = kDirectionNone;
2319 data->entity = kEntityPlayer;
2320 return true;
2321 }
2322
2323 drawSequences(entity, direction, true);
2324 return false;
2325 }
2326
2327 data->entityPosition = position;
2328 if (data->direction == kDirectionUp || data->direction == kDirectionDown)
2329 data->direction = kDirectionNone;
2330 data->entity = kEntityPlayer;
2331
2332 return true;
2333 }
2334
changeCar(EntityData::EntityCallData * data,EntityIndex entity,CarIndex car,EntityPosition position,bool increment,EntityPosition newPosition,CarIndex newCar) const2335 bool Entities::changeCar(EntityData::EntityCallData *data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const {
2336 if (getData(kEntityPlayer)->car == data->car) {
2337 getSound()->playSoundEvent(entity, 36);
2338 getSound()->playSoundEvent(entity, 37, 30);
2339 }
2340
2341 data->car = (CarIndex)(increment ? data->car + 1 : data->car - 1);
2342 data->entityPosition = newPosition;
2343
2344 if (data->car == newCar) {
2345 if (isInGreenCarEntrance(kEntityPlayer)) {
2346 getSound()->playSoundEvent(kEntityPlayer, 14);
2347 getSound()->excuseMe(entity, kEntityPlayer, kVolumeFull);
2348 getScenes()->loadSceneFromPosition(kCarGreenSleeping, 1);
2349 getSound()->playSound(kEntityPlayer, "CAT1127A");
2350 getSound()->playSoundEvent(kEntityPlayer, 15);
2351 }
2352 }
2353
2354 if ((increment ? data->car > car : data->car < car) || (data->car == car && (increment ? data->entityPosition >= position : data->entityPosition <= position))) {
2355 data->car = car;
2356 data->entityPosition = position;
2357 data->direction = kDirectionNone;
2358 data->entity = kEntityPlayer;
2359
2360 return true;
2361 }
2362
2363 if (data->car == newCar) {
2364 if (isInKronosCarEntrance(kEntityPlayer)) {
2365 getSound()->playSoundEvent(kEntityPlayer, 14);
2366 getSound()->excuseMe(entity, kEntityPlayer, kVolumeFull);
2367 getScenes()->loadSceneFromPosition(kCarGreenSleeping, 62);
2368 getSound()->playSound(kEntityPlayer, "CAT1127A");
2369 getSound()->playSoundEvent(kEntityPlayer, 15);
2370 }
2371 }
2372
2373 if (data->car == getData(kEntityPlayer)->car) {
2374 getSound()->playSoundEvent(entity, 36);
2375 getSound()->playSoundEvent(entity, 37, 30);
2376 }
2377
2378 return false;
2379 }
2380
2381 //////////////////////////////////////////////////////////////////////////
2382 // CHECKS
2383 //////////////////////////////////////////////////////////////////////////
isInsideCompartment(EntityIndex entity,CarIndex car,EntityPosition position) const2384 bool Entities::isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const {
2385 return (getData(entity)->entityPosition == position
2386 && getData(entity)->location == kLocationInsideCompartment
2387 && getData(entity)->car == car);
2388 }
2389
checkFields2(ObjectIndex object) const2390 bool Entities::checkFields2(ObjectIndex object) const {
2391
2392 EntityPosition position = kPositionNone;
2393 CarIndex car = kCarNone;
2394
2395 switch (object) {
2396 default:
2397 return false;
2398
2399 case kObjectCompartment1:
2400 case kObjectCompartment2:
2401 case kObjectCompartment3:
2402 case kObjectCompartment4:
2403 case kObjectCompartment5:
2404 case kObjectCompartment6:
2405 case kObjectCompartment7:
2406 case kObjectCompartment8:
2407 position = objectsPosition[object - 1];
2408 car = kCarGreenSleeping;
2409 if (isInsideCompartment(kEntityPlayer, car, position))
2410 return false;
2411 break;
2412
2413 case kObjectHandleBathroom:
2414 case kObjectHandleInsideBathroom:
2415 case kObjectKitchen:
2416 case kObject20:
2417 case kObject21:
2418 case kObject22:
2419 position = objectsPosition[object-17];
2420 car = kCarGreenSleeping;
2421 break;
2422
2423 case kObjectCompartmentA:
2424 case kObjectCompartmentB:
2425 case kObjectCompartmentC:
2426 case kObjectCompartmentD:
2427 case kObjectCompartmentE:
2428 case kObjectCompartmentF:
2429 case kObjectCompartmentG:
2430 case kObjectCompartmentH:
2431 position = objectsPosition[object-32];
2432 car = kCarRedSleeping;
2433 if (isInsideCompartment(kEntityPlayer, car, position))
2434 return false;
2435 break;
2436
2437 case kObject48:
2438 case kObject49:
2439 case kObject50:
2440 case kObject51:
2441 case kObject52:
2442 case kObject53:
2443 position = objectsPosition[object-48];
2444 car = kCarRedSleeping;
2445 break;
2446
2447 }
2448
2449 uint index = 1;
2450 while (!isInsideCompartment((EntityIndex)index, car, position) || index == kEntityVassili) {
2451 index++;
2452 if (index >= 40)
2453 return false;
2454 }
2455
2456 return true;
2457 }
2458
isInsideCompartments(EntityIndex entity) const2459 bool Entities::isInsideCompartments(EntityIndex entity) const {
2460 return (getData(entity)->car == kCarGreenSleeping
2461 || getData(entity)->car == kCarRedSleeping)
2462 && getData(entity)->location == kLocationInsideCompartment;
2463 }
2464
isPlayerPosition(CarIndex car,Position position) const2465 bool Entities::isPlayerPosition(CarIndex car, Position position) const {
2466 return getData(kEntityPlayer)->car == car && getScenes()->get(getState()->scene)->position == position;
2467 }
2468
isInsideTrainCar(EntityIndex entity,CarIndex car) const2469 bool Entities::isInsideTrainCar(EntityIndex entity, CarIndex car) const {
2470 return getData(entity)->car == car && getData(entity)->location <= kLocationInsideCompartment;
2471 }
2472
isInGreenCarEntrance(EntityIndex entity) const2473 bool Entities::isInGreenCarEntrance(EntityIndex entity) const {
2474 return isInsideTrainCar(entity, kCarGreenSleeping) && getData(entity)->entityPosition < kPosition_850;
2475 }
2476
isPlayerInCar(CarIndex car) const2477 bool Entities::isPlayerInCar(CarIndex car) const {
2478 return isInsideTrainCar(kEntityPlayer, car) && getData(kEntityPlayer)->location && !isInGreenCarEntrance(kEntityPlayer);
2479 }
2480
isDirectionUpOrDown(EntityIndex entity) const2481 bool Entities::isDirectionUpOrDown(EntityIndex entity) const {
2482 return getData(entity)->direction == kDirectionUp || getData(entity)->direction == kDirectionDown;
2483 }
2484
isDistanceBetweenEntities(EntityIndex entity1,EntityIndex entity2,uint distance) const2485 bool Entities::isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const {
2486 return getData(entity1)->car == getData(entity2)->car
2487 && (uint)ABS(getData(entity1)->entityPosition - getData(entity2)->entityPosition) <= distance
2488 && (getData(entity1)->location != kLocationOutsideTrain || getData(entity2)->location != kLocationOutsideTrain);
2489 }
2490
checkFields10(EntityIndex entity) const2491 bool Entities::checkFields10(EntityIndex entity) const {
2492 return getData(entity)->location <= kLocationOutsideTrain;
2493 }
2494
isSomebodyInsideRestaurantOrSalon() const2495 bool Entities::isSomebodyInsideRestaurantOrSalon() const {
2496 for (uint i = 1; i < _entities.size(); i++) {
2497 EntityIndex index = (EntityIndex)i;
2498
2499 if (getData(index)->location == kLocationOutsideCompartment && (isInSalon(index) || isInRestaurant(index)))
2500 return false;
2501 }
2502
2503 return true;
2504 }
2505
isInSalon(EntityIndex entity) const2506 bool Entities::isInSalon(EntityIndex entity) const {
2507 return isInsideTrainCar(entity, kCarRestaurant)
2508 && getData(entity)->entityPosition >= kPosition_1540
2509 && getData(entity)->entityPosition <= kPosition_3650;
2510 }
2511
isInRestaurant(EntityIndex entity) const2512 bool Entities::isInRestaurant(EntityIndex entity) const {
2513 return isInsideTrainCar(entity, kCarRestaurant)
2514 && getData(entity)->entityPosition >= kPosition_3650
2515 && getData(entity)->entityPosition <= kPosition_5800;
2516 }
2517
isInKronosSalon(EntityIndex entity) const2518 bool Entities::isInKronosSalon(EntityIndex entity) const {
2519 return isInsideTrainCar(entity, kCarKronos)
2520 && getData(entity)->entityPosition >= kPosition_5500
2521 && getData(entity)->entityPosition <= kPosition_7500;
2522 }
2523
isOutsideAlexeiWindow() const2524 bool Entities::isOutsideAlexeiWindow() const {
2525 return (getData(kEntityPlayer)->entityPosition == kPosition_7500 || getData(kEntityPlayer)->entityPosition == kPosition_8200)
2526 && getData(kEntityPlayer)->location == kLocationOutsideTrain
2527 && getData(kEntityPlayer)->car == kCarGreenSleeping;
2528 }
2529
isOutsideAnnaWindow() const2530 bool Entities::isOutsideAnnaWindow() const {
2531 return (getData(kEntityPlayer)->entityPosition == kPosition_4070 || getData(kEntityPlayer)->entityPosition == kPosition_4840)
2532 && getData(kEntityPlayer)->location == kLocationOutsideTrain
2533 && getData(kEntityPlayer)->car == kCarRedSleeping;
2534 }
2535
isInKitchen(EntityIndex entity) const2536 bool Entities::isInKitchen(EntityIndex entity) const {
2537 return isInsideTrainCar(entity, kCarRestaurant) && getData(entity)->entityPosition > kPosition_5800;
2538 }
2539
isNobodyInCompartment(CarIndex car,EntityPosition position) const2540 bool Entities::isNobodyInCompartment(CarIndex car, EntityPosition position) const {
2541 for (uint i = 1; i < _entities.size(); i++) {
2542 if (isInsideCompartment((EntityIndex)i, car, position))
2543 return false;
2544 }
2545 return true;
2546 }
2547
checkFields19(EntityIndex entity,CarIndex car,EntityPosition position) const2548 bool Entities::checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const {
2549
2550 if (getData(entity)->car != car || getData(entity)->location != kLocationInsideCompartment)
2551 return false;
2552
2553 EntityPosition entityPosition = getData(entity)->entityPosition;
2554
2555 // Test values
2556 if (position == kPosition_4455) {
2557 if (entityPosition == kPosition_4070 || entityPosition == kPosition_4455 || entityPosition == kPosition_4840)
2558 return true;
2559
2560 return false;
2561 }
2562
2563 if (position == kPosition_6130) {
2564 if (entityPosition == kPosition_5790 || entityPosition == kPosition_6130 || entityPosition == kPosition_6470)
2565 return true;
2566
2567 return false;
2568 }
2569
2570 if (position != kPosition_7850
2571 || (entityPosition != kPosition_7500 && entityPosition != kPosition_7850 && entityPosition != kPosition_8200))
2572 return false;
2573
2574 return true;
2575 }
2576
isInBaggageCarEntrance(EntityIndex entity) const2577 bool Entities::isInBaggageCarEntrance(EntityIndex entity) const {
2578 return isInsideTrainCar(entity, kCarBaggage)
2579 && getData(entity)->entityPosition >= kPosition_4500
2580 && getData(entity)->entityPosition <= kPosition_5500;
2581 }
2582
isInBaggageCar(EntityIndex entity) const2583 bool Entities::isInBaggageCar(EntityIndex entity) const {
2584 return isInsideTrainCar(entity, kCarBaggage) && getData(entity)->entityPosition < kPosition_4500;
2585 }
2586
isInKronosSanctum(EntityIndex entity) const2587 bool Entities::isInKronosSanctum(EntityIndex entity) const {
2588 return isInsideTrainCar(entity, kCarKronos)
2589 && getData(entity)->entityPosition >= kPosition_3500
2590 && getData(entity)->entityPosition <= kPosition_5500;
2591 }
2592
isInKronosCarEntrance(EntityIndex entity) const2593 bool Entities::isInKronosCarEntrance(EntityIndex entity) const {
2594 return isInsideTrainCar(entity, kCarKronos) && getData(entity)->entityPosition > kPosition_7900;
2595 }
2596
checkDistanceFromPosition(EntityIndex entity,EntityPosition position,int distance) const2597 bool Entities::checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const {
2598 return distance >= ABS(getData(entity)->entityPosition - position);
2599 }
2600
isWalkingOppositeToPlayer(EntityIndex entity) const2601 bool Entities::isWalkingOppositeToPlayer(EntityIndex entity) const {
2602 if (getData(entity)->direction == kDirectionUp && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
2603 return true;
2604
2605 return (getData(entity)->direction == kDirectionDown && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp));
2606 }
2607
isFemale(EntityIndex entity)2608 bool Entities::isFemale(EntityIndex entity) {
2609 return (entity == kEntityAnna
2610 || entity == kEntityTatiana
2611 || entity == kEntityVesna
2612 || entity == kEntityKahina
2613 || entity == kEntityMmeBoutarel
2614 || entity == kEntityRebecca
2615 || entity == kEntitySophie
2616 || entity == kEntityYasmin
2617 || entity == kEntityHadija
2618 || entity == kEntityAlouan);
2619 }
2620
isMarried(EntityIndex entity)2621 bool Entities::isMarried(EntityIndex entity) {
2622 return (entity != kEntityTatiana
2623 && entity != kEntityRebecca
2624 && entity != kEntitySophie);
2625 }
2626
checkPosition(EntityPosition position) const2627 bool Entities::checkPosition(EntityPosition position) const {
2628 Position position1 = 0;
2629 Position position2 = 0;
2630
2631 switch (position) {
2632 default:
2633 return true;
2634
2635 case kPosition_1500:
2636 position1 = 1;
2637 position2 = 23;
2638 break;
2639
2640 case kPosition_2740:
2641 position1 = 3;
2642 position2 = 25;
2643 break;
2644
2645 case kPosition_3050:
2646 position1 = 5;
2647 position2 = 26;
2648 break;
2649
2650 case kPosition_4070:
2651 position1 = 7;
2652 position2 = 28;
2653 break;
2654
2655 case kPosition_4840:
2656 position1 = 9;
2657 position2 = 30;
2658 break;
2659
2660 case kPosition_5790:
2661 position1 = 11;
2662 position2 = 32;
2663 break;
2664
2665 case kPosition_6470:
2666 position1 = 13;
2667 position2 = 34;
2668 break;
2669
2670 case kPosition_7500:
2671 position1 = 15;
2672 position2 = 36;
2673 break;
2674
2675 case kPosition_8200:
2676 position1 = 17;
2677 position2 = 38;
2678 break;
2679 }
2680
2681 if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && entityPositions[position1] >= getEntityData(kEntityPlayer)->entityPosition)
2682 return true;
2683 else
2684 return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && entityPositions[position2] <= getEntityData(kEntityPlayer)->entityPosition);
2685 }
2686
checkSequenceFromPosition(EntityIndex entity) const2687 bool Entities::checkSequenceFromPosition(EntityIndex entity) const {
2688 FrameInfo *info = getEntityData(entity)->sequence->getFrameInfo((uint16)getEntityData(entity)->currentFrame);
2689
2690 if (getEntityData(entity)->direction == kDirectionUp)
2691 return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
2692 && info->entityPosition + getEntityPositionFromCurrentPosition() > kPosition_8513);
2693
2694 if (getEntityData(entity)->direction == kDirectionDown)
2695 return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)
2696 && info->entityPosition + getEntityPositionFromCurrentPosition() < kPosition_2087);
2697
2698 return false;
2699 }
2700
getEntityPositionFromCurrentPosition() const2701 EntityPosition Entities::getEntityPositionFromCurrentPosition() const {
2702 // Get the scene position first
2703 Position position = getScenes()->get(getState()->scene)->position;
2704
2705 if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
2706 return (EntityPosition)(entityPositions[position] - kPosition_1430);
2707
2708 if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
2709 return (EntityPosition)(entityPositions[position] - kPosition_9020);
2710
2711 return kPositionNone;
2712 }
2713
clearEntitySequenceData(EntityData::EntityCallData * data,EntityDirection direction) const2714 void Entities::clearEntitySequenceData(EntityData::EntityCallData *data, EntityDirection direction) const {
2715 getScenes()->removeAndRedraw(&data->frame, false);
2716 getScenes()->removeAndRedraw(&data->frame1, false);
2717
2718 SAFE_DELETE(data->sequence);
2719 SAFE_DELETE(data->sequence2);
2720
2721 data->sequenceName = "";
2722 data->sequenceName2 = "";
2723
2724 data->field_4A9 = false;
2725 data->field_4AA = false;
2726 data->directionSwitch = kDirectionNone;
2727
2728 data->currentFrame = -1;
2729 data->currentFrame2 = 0;
2730
2731 data->direction = direction;
2732 }
2733
2734 } // End of namespace LastExpress
2735