aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/lastexpress/data/animation.cpp300
-rw-r--r--engines/lastexpress/data/animation.h114
-rw-r--r--engines/lastexpress/data/archive.cpp115
-rw-r--r--engines/lastexpress/data/archive.h75
-rw-r--r--engines/lastexpress/data/background.cpp141
-rw-r--r--engines/lastexpress/data/background.h81
-rw-r--r--engines/lastexpress/data/cursor.cpp144
-rw-r--r--engines/lastexpress/data/cursor.h93
-rw-r--r--engines/lastexpress/data/font.cpp205
-rw-r--r--engines/lastexpress/data/font.h82
-rw-r--r--engines/lastexpress/data/scene.cpp292
-rw-r--r--engines/lastexpress/data/scene.h245
-rw-r--r--engines/lastexpress/data/sequence.cpp475
-rw-r--r--engines/lastexpress/data/sequence.h205
-rw-r--r--engines/lastexpress/data/snd.cpp141
-rw-r--r--engines/lastexpress/data/snd.h97
-rw-r--r--engines/lastexpress/data/subtitle.cpp243
-rw-r--r--engines/lastexpress/data/subtitle.h79
-rw-r--r--engines/lastexpress/debug.cpp1091
-rw-r--r--engines/lastexpress/debug.h104
-rw-r--r--engines/lastexpress/detection.cpp171
-rw-r--r--engines/lastexpress/drawable.h42
-rw-r--r--engines/lastexpress/entities/abbot.cpp1910
-rw-r--r--engines/lastexpress/entities/abbot.h225
-rw-r--r--engines/lastexpress/entities/alexei.cpp1897
-rw-r--r--engines/lastexpress/entities/alexei.h213
-rw-r--r--engines/lastexpress/entities/alouan.cpp504
-rw-r--r--engines/lastexpress/entities/alouan.h139
-rw-r--r--engines/lastexpress/entities/anna.cpp3400
-rw-r--r--engines/lastexpress/entities/anna.h252
-rw-r--r--engines/lastexpress/entities/august.cpp2804
-rw-r--r--engines/lastexpress/entities/august.h275
-rw-r--r--engines/lastexpress/entities/boutarel.cpp1260
-rw-r--r--engines/lastexpress/entities/boutarel.h188
-rw-r--r--engines/lastexpress/entities/chapters.cpp1810
-rw-r--r--engines/lastexpress/entities/chapters.h166
-rw-r--r--engines/lastexpress/entities/cooks.cpp571
-rw-r--r--engines/lastexpress/entities/cooks.h109
-rw-r--r--engines/lastexpress/entities/coudert.cpp3611
-rw-r--r--engines/lastexpress/entities/coudert.h229
-rw-r--r--engines/lastexpress/entities/entity.cpp434
-rw-r--r--engines/lastexpress/entities/entity.h681
-rw-r--r--engines/lastexpress/entities/entity39.cpp103
-rw-r--r--engines/lastexpress/entities/entity39.h78
-rw-r--r--engines/lastexpress/entities/entity_intern.h577
-rw-r--r--engines/lastexpress/entities/francois.cpp1043
-rw-r--r--engines/lastexpress/entities/francois.h170
-rw-r--r--engines/lastexpress/entities/gendarmes.cpp491
-rw-r--r--engines/lastexpress/entities/gendarmes.h99
-rw-r--r--engines/lastexpress/entities/hadija.cpp529
-rw-r--r--engines/lastexpress/entities/hadija.h144
-rw-r--r--engines/lastexpress/entities/ivo.cpp829
-rw-r--r--engines/lastexpress/entities/ivo.h177
-rw-r--r--engines/lastexpress/entities/kahina.cpp944
-rw-r--r--engines/lastexpress/entities/kahina.h166
-rw-r--r--engines/lastexpress/entities/kronos.cpp666
-rw-r--r--engines/lastexpress/entities/kronos.h138
-rw-r--r--engines/lastexpress/entities/mahmud.cpp839
-rw-r--r--engines/lastexpress/entities/mahmud.h153
-rw-r--r--engines/lastexpress/entities/max.cpp628
-rw-r--r--engines/lastexpress/entities/max.h129
-rw-r--r--engines/lastexpress/entities/mertens.cpp4113
-rw-r--r--engines/lastexpress/entities/mertens.h220
-rw-r--r--engines/lastexpress/entities/milos.cpp1077
-rw-r--r--engines/lastexpress/entities/milos.h175
-rw-r--r--engines/lastexpress/entities/mmeboutarel.cpp939
-rw-r--r--engines/lastexpress/entities/mmeboutarel.h164
-rw-r--r--engines/lastexpress/entities/pascale.cpp1232
-rw-r--r--engines/lastexpress/entities/pascale.h166
-rw-r--r--engines/lastexpress/entities/rebecca.cpp1732
-rw-r--r--engines/lastexpress/entities/rebecca.h230
-rw-r--r--engines/lastexpress/entities/salko.cpp642
-rw-r--r--engines/lastexpress/entities/salko.h149
-rw-r--r--engines/lastexpress/entities/servers0.cpp1039
-rw-r--r--engines/lastexpress/entities/servers0.h173
-rw-r--r--engines/lastexpress/entities/servers1.cpp787
-rw-r--r--engines/lastexpress/entities/servers1.h167
-rw-r--r--engines/lastexpress/entities/sophie.cpp279
-rw-r--r--engines/lastexpress/entities/sophie.h98
-rw-r--r--engines/lastexpress/entities/tables.cpp221
-rw-r--r--engines/lastexpress/entities/tables.h77
-rw-r--r--engines/lastexpress/entities/tatiana.cpp1711
-rw-r--r--engines/lastexpress/entities/tatiana.h235
-rw-r--r--engines/lastexpress/entities/train.cpp575
-rw-r--r--engines/lastexpress/entities/train.h95
-rw-r--r--engines/lastexpress/entities/vassili.cpp589
-rw-r--r--engines/lastexpress/entities/vassili.h110
-rw-r--r--engines/lastexpress/entities/verges.cpp1898
-rw-r--r--engines/lastexpress/entities/verges.h182
-rw-r--r--engines/lastexpress/entities/vesna.cpp1161
-rw-r--r--engines/lastexpress/entities/vesna.h176
-rw-r--r--engines/lastexpress/entities/yasmin.cpp490
-rw-r--r--engines/lastexpress/entities/yasmin.h142
-rw-r--r--engines/lastexpress/eventhandler.h53
-rw-r--r--engines/lastexpress/game/action.cpp1968
-rw-r--r--engines/lastexpress/game/action.h135
-rw-r--r--engines/lastexpress/game/beetle.cpp517
-rw-r--r--engines/lastexpress/game/beetle.h118
-rw-r--r--engines/lastexpress/game/entities.cpp2731
-rw-r--r--engines/lastexpress/game/entities.h378
-rw-r--r--engines/lastexpress/game/fight.cpp1587
-rw-r--r--engines/lastexpress/game/fight.h269
-rw-r--r--engines/lastexpress/game/inventory.cpp594
-rw-r--r--engines/lastexpress/game/inventory.h169
-rw-r--r--engines/lastexpress/game/logic.cpp577
-rw-r--r--engines/lastexpress/game/logic.h91
-rw-r--r--engines/lastexpress/game/menu.cpp1564
-rw-r--r--engines/lastexpress/game/menu.h211
-rw-r--r--engines/lastexpress/game/object.cpp108
-rw-r--r--engines/lastexpress/game/object.h83
-rw-r--r--engines/lastexpress/game/savegame.cpp310
-rw-r--r--engines/lastexpress/game/savegame.h173
-rw-r--r--engines/lastexpress/game/savepoint.cpp296
-rw-r--r--engines/lastexpress/game/savepoint.h149
-rw-r--r--engines/lastexpress/game/scenes.cpp1195
-rw-r--r--engines/lastexpress/game/scenes.h120
-rw-r--r--engines/lastexpress/game/sound.cpp1676
-rw-r--r--engines/lastexpress/game/sound.h333
-rw-r--r--engines/lastexpress/game/state.cpp74
-rw-r--r--engines/lastexpress/game/state.h593
-rw-r--r--engines/lastexpress/graphics.cpp166
-rw-r--r--engines/lastexpress/graphics.h76
-rw-r--r--engines/lastexpress/helpers.h102
-rw-r--r--engines/lastexpress/lastexpress.cpp310
-rw-r--r--engines/lastexpress/lastexpress.h155
-rw-r--r--engines/lastexpress/module.mk73
-rw-r--r--engines/lastexpress/resource.cpp248
-rw-r--r--engines/lastexpress/resource.h70
-rw-r--r--engines/lastexpress/shared.h1670
129 files changed, 73102 insertions, 0 deletions
diff --git a/engines/lastexpress/data/animation.cpp b/engines/lastexpress/data/animation.cpp
new file mode 100644
index 0000000000..88973c4b0b
--- /dev/null
+++ b/engines/lastexpress/data/animation.cpp
@@ -0,0 +1,300 @@
+/* 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$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/animation.h"
+
+#include "lastexpress/data/sequence.h"
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/events.h"
+#include "engines/engine.h"
+
+namespace LastExpress {
+
+Animation::Animation() : _stream(NULL), _currentChunk(NULL), _overlay(NULL), _background1(NULL), _background2(NULL), _backgroundCurrent(0), _audio(NULL), _startTime(0), _changed(false), _flag(0) {
+}
+
+Animation::~Animation() {
+ reset();
+}
+
+void Animation::reset() {
+ delete _overlay;
+ _overlay = NULL;
+ delete _background1;
+ _background1 = NULL;
+ delete _background2;
+ _background2 = NULL;
+ delete _audio;
+ _audio = NULL;
+
+ _backgroundCurrent = 0;
+ _chunks.clear();
+
+ _currentChunk = NULL;
+
+ delete _stream;
+}
+
+bool Animation::load(Common::SeekableReadStream *stream, int flag) {
+ if (!stream)
+ return false;
+
+ reset();
+
+ // Keep stream for later decoding of animation
+ _stream = stream;
+
+ // Read header to get the number of chunks
+ uint32 numChunks = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of chunks in NIS file: %d", numChunks);
+
+ // Check if there is enough data
+ if (_stream->size() - _stream->pos() < (signed)(numChunks * sizeof(Chunk))) {
+ debugC(2, kLastExpressDebugGraphics, "NIS file seems to be corrupted!");
+ return false;
+ }
+
+ // Read all the chunks
+ for (uint32 i = 0; i < numChunks; ++i) {
+ Chunk chunk;
+ chunk.type = (ChunkType)_stream->readUint16LE();
+ chunk.frame = _stream->readUint16LE();
+ chunk.size = _stream->readUint32LE();
+
+ _chunks.push_back(chunk);
+
+ debugC(9, kLastExpressDebugGraphics, "Chunk Entry: type 0x%.4x, frame=%d, size=%d", chunk.type, chunk.frame, chunk.size);
+ }
+ _currentChunk = _chunks.begin();
+ _changed = false;
+ _startTime = g_engine->_system->getMillis();
+
+ return true;
+}
+
+bool Animation::process() {
+ if (!_currentChunk)
+ error("Animation::process - internal error: the current chunk iterator is invalid!");
+
+ if (_stream == NULL || _chunks.size() == 0)
+ error("Trying to show an animation before loading data");
+
+ // TODO: substract the time paused by the GUI
+ uint32 currentFrame = (uint32)(((float)(g_engine->_system->getMillis() - _startTime)) / 33.33f);
+
+ // Process all chunks until the current frame
+ while (!_changed && currentFrame > _currentChunk->frame && !hasEnded()) {
+ switch(_currentChunk->type) {
+ //TODO: some info chunks are probably subtitle/sync related
+ case kChunkTypeUnknown1:
+ case kChunkTypeUnknown2:
+ case kChunkTypeUnknown5:
+ debugC(9, kLastExpressDebugGraphics | kLastExpressDebugUnknown, " info chunk: type 0x%.4x (size %d)", _currentChunk->type, _currentChunk->size);
+ assert (_currentChunk->frame == 0);
+ //TODO: _currentChunk->size?
+ break;
+
+ case kChunkTypeAudioInfo:
+ debugC(9, kLastExpressDebugGraphics, " audio info: %d blocks", _currentChunk->size);
+ assert (_currentChunk->frame == 0);
+ //TODO: save the size?
+ _audio = new AppendableSound();
+ break;
+
+ case kChunkTypeUnknown4:
+ debugC(9, kLastExpressDebugGraphics | kLastExpressDebugUnknown, " info block 4");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ //TODO unknown type of chunk
+ break;
+
+ case kChunkTypeBackground1:
+ debugC(9, kLastExpressDebugGraphics, " background frame 1 (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _background1;
+ _background1 = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeSelectBackground1:
+ debugC(9, kLastExpressDebugGraphics, " select background 1");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ _backgroundCurrent = 1;
+ break;
+
+ case kChunkTypeBackground2:
+ debugC(9, kLastExpressDebugGraphics, " background frame 2 (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _background2;
+ _background2 = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeSelectBackground2:
+ debugC(9, kLastExpressDebugGraphics, " select background 2");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ _backgroundCurrent = 2;
+ break;
+
+ case kChunkTypeOverlay:
+ debugC(9, kLastExpressDebugGraphics, " overlay frame (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _overlay;
+ _overlay = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeUpdate:
+ case kChunkTypeUpdateTransition:
+ debugC(9, kLastExpressDebugGraphics, " update%s: frame %d", _currentChunk->type == 15 ? "" : " with transition", _currentChunk->frame);
+ assert (_currentChunk->size == 0);
+ _changed = true;
+ break;
+
+ case kChunkTypeAudioData:
+ debugC(9, kLastExpressDebugGraphics, " audio (%d blocks, %d bytes, frame %d)", _currentChunk->size / _soundBlockSize, _currentChunk->size, _currentChunk->frame);
+ processChunkAudio(_stream, *_currentChunk);
+
+ // Synchronize the audio by resetting the start time
+ if (_currentChunk->frame == 0)
+ _startTime = g_engine->_system->getMillis();
+ break;
+
+ case kChunkTypeAudioEnd:
+ debugC(9, kLastExpressDebugGraphics, " audio end: %d blocks", _currentChunk->frame);
+ assert (_currentChunk->size == 0);
+ _audio->finish();
+ //TODO: we need to start the linked sound (.LNK) after the audio from the animation ends
+ break;
+
+ default:
+ error(" UNKNOWN chunk type=%x frame=%d size=%d", _currentChunk->type, _currentChunk->frame, _currentChunk->size);
+ break;
+ }
+ _currentChunk++;
+ }
+
+ return true;
+}
+
+bool Animation::hasEnded() {
+ return _currentChunk == _chunks.end();
+}
+
+Common::Rect Animation::draw(Graphics::Surface *surface) {
+ if (!_overlay)
+ error("Animation::draw - internal error: the current overlay animation frame is invalid!");
+
+ // Paint the background
+ if (_backgroundCurrent == 1 && _background1)
+ _background1->draw(surface);
+ else if (_backgroundCurrent == 2 && _background2)
+ _background2->draw(surface);
+
+ // Paint the overlay
+ _overlay->draw(surface);
+
+ //TODO
+ return Common::Rect();
+}
+
+AnimFrame *Animation::processChunkFrame(Common::SeekableReadStream *in, const Chunk &c) const {
+ assert (c.frame == 0);
+
+ // Create a temporary chunk buffer
+ Common::MemoryReadStream *str = in->readStream(c.size);
+
+ // Read the frame information
+ FrameInfo i;
+ i.read(str, false);
+
+ // Decode the frame
+ AnimFrame *f = new AnimFrame(str, i);
+
+ // Delete the temporary chunk buffer
+ delete str;
+
+ return f;
+}
+
+void Animation::processChunkAudio(Common::SeekableReadStream *in, const Chunk &c) {
+ if (!_audio)
+ error("Animation::processChunkAudio - internal error: the audio stream is invalid!");
+
+ // Skip the Snd header, to queue just the audio blocks
+ uint32 size = c.size;
+ if ((c.size % 739) != 0) {
+ // Read Snd header
+ uint32 header1 = in->readUint32LE();
+ uint16 header2 = in->readUint16LE();
+ warning("Start ADPCM: %d, %d", header1, header2);
+ size -= 6;
+ }
+
+ // Append the current chunk to the Snd
+ _audio->queueBuffer(in->readStream(size));
+}
+
+// TODO: this method will probably go away and be integrated in the main loop
+void Animation::play() {
+ while (!hasEnded() && !g_engine->getEventManager()->shouldQuit() && !g_engine->getEventManager()->shouldRTL()) {
+ process();
+
+ if (_changed) {
+ // Create a temporary surface to merge the overlay with the background
+ Graphics::Surface *s = new Graphics::Surface;
+ s->create(640, 480, 2);
+
+ draw(s);
+
+ // XXX: Update the screen
+ g_system->copyRectToScreen((byte *)s->pixels, s->pitch, 0, 0, s->w, s->h);
+
+ // Free the temporary surface
+ s->free();
+ delete s;
+
+ _changed = false;
+ }
+
+ g_system->updateScreen();
+
+ //FIXME: implement subtitles
+ g_engine->_system->delayMillis(20);
+
+ // Handle right-click to interrupt animations
+ Common::Event ev;
+ while (g_engine->getEventManager()->pollEvent(ev)) {
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ // Stop audio
+ if (_audio)
+ _audio->finish();
+
+ // TODO start LNK file sound?
+ return;
+ }
+ }
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/animation.h b/engines/lastexpress/data/animation.h
new file mode 100644
index 0000000000..ca1f7c6fa0
--- /dev/null
+++ b/engines/lastexpress/data/animation.h
@@ -0,0 +1,114 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ANIMATION_H
+#define LASTEXPRESS_ANIMATION_H
+
+/*
+ Animation format (.NIS)
+
+ uint32 {4} - Number of chunks
+
+ // for each chunk
+ uint16 {2} - Type
+ uint16 {2} - Tag
+ uint32 {4} - Size of chunk
+ byte {x} - Data (for "data" chunks: backgrounds, overlay & audio data)
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class AnimFrame;
+class AppendableSound;
+
+class Animation : public Drawable {
+public:
+ enum FlagType {
+ kFlagDefault = 16384,
+ kFlagProcess = 49152
+ };
+
+ Animation();
+ ~Animation();
+
+ bool load(Common::SeekableReadStream *stream, int flag = kFlagDefault);
+ bool process();
+ bool hasEnded();
+ Common::Rect draw(Graphics::Surface *surface);
+ void play();
+
+private:
+ static const uint32 _soundBlockSize = 739;
+
+ // despite their size field, info chunks don't have a payload
+ enum ChunkType {
+ kChunkTypeUnknown1 = 1,
+ kChunkTypeUnknown2 = 2,
+ kChunkTypeAudioInfo = 3,
+ kChunkTypeUnknown4 = 4,
+ kChunkTypeUnknown5 = 5,
+ kChunkTypeBackground1 = 10,
+ kChunkTypeSelectBackground1 = 11,
+ kChunkTypeBackground2 = 12,
+ kChunkTypeSelectBackground2 = 13,
+ kChunkTypeOverlay = 20,
+ kChunkTypeUpdate = 21,
+ kChunkTypeUpdateTransition = 22,
+ kChunkTypeSound1 = 30,
+ kChunkTypeSound2 = 31,
+ kChunkTypeAudioData = 32,
+ kChunkTypeAudioEnd = 99
+ };
+
+ struct Chunk {
+ ChunkType type;
+ uint16 frame;
+ uint32 size;
+ };
+
+ void reset();
+ AnimFrame *processChunkFrame(Common::SeekableReadStream *in, const Chunk &c) const;
+ void processChunkAudio(Common::SeekableReadStream *in, const Chunk &c);
+
+ Common::SeekableReadStream *_stream;
+ Common::Array<Chunk> _chunks;
+ Common::Array<Chunk>::iterator _currentChunk;
+ AnimFrame *_overlay, *_background1, *_background2;
+ byte _backgroundCurrent;
+ AppendableSound *_audio;
+
+ uint32 _startTime;
+ bool _changed;
+ int _flag;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ANIMATION_H
diff --git a/engines/lastexpress/data/archive.cpp b/engines/lastexpress/data/archive.cpp
new file mode 100644
index 0000000000..1a5b6905a3
--- /dev/null
+++ b/engines/lastexpress/data/archive.cpp
@@ -0,0 +1,115 @@
+/* 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$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SND
+
+#include "lastexpress/data/archive.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+
+namespace LastExpress {
+
+HPFArchive::HPFArchive(const Common::String &path) {
+ _filename = path;
+
+ // Open a stream to the archive
+ Common::SeekableReadStream *archive = SearchMan.createReadStreamForMember(_filename);
+ if (!archive) {
+ debugC(2, kLastExpressDebugResource, "Error opening file: %s", path.c_str());
+ return;
+ }
+
+ debugC(2, kLastExpressDebugResource, "Opened archive: %s", path.c_str());
+
+ // Read header to get the number of files
+ uint32 numFiles = archive->readUint32LE();
+ debugC(3, kLastExpressDebugResource, "Number of files in archive: %d", numFiles);
+
+ // Read the list of files
+ for (unsigned int i = 0; i < numFiles; ++i) {
+ char name[13];
+ HPFEntry entry;
+
+ archive->read(&name, sizeof(char) * _archiveNameSize);
+ entry.offset = archive->readUint32LE();
+ entry.size = archive->readUint32LE();
+ entry.isOnHD = archive->readUint16LE();
+
+ // Terminate string
+ name[12] = '\0';
+
+ Common::String filename(name);
+ filename.toLowercase();
+
+ _files[filename] = entry;
+
+ //debugC(9, kLastExpressDebugResource, "File entry: %s (offset:%d - Size: %d - HD: %u)", &name, entry.offset, entry.size, entry.isOnHD);
+ }
+
+ // Close stream
+ delete archive;
+}
+
+bool HPFArchive::hasFile(const Common::String &name) {
+ return (_files.find(name) != _files.end());
+}
+
+int HPFArchive::listMembers(Common::ArchiveMemberList &list) {
+ int numMembers = 0;
+
+ for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(i->_key, this)));
+ numMembers++;
+ }
+
+ return numMembers;
+}
+
+Common::ArchiveMemberPtr HPFArchive::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *HPFArchive::createReadStreamForMember(const Common::String &name) const {
+ FileMap::const_iterator fDesc = _files.find(name);
+ if (fDesc == _files.end())
+ return NULL;
+
+ Common::File *archive = new Common::File();
+ if (!archive->open(_filename)) {
+ delete archive;
+ return NULL;
+ }
+
+ return new Common::SeekableSubReadStream(archive, fDesc->_value.offset * _archiveSectorSize, fDesc->_value.offset * _archiveSectorSize + fDesc->_value.size * _archiveSectorSize, DisposeAfterUse::YES);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/archive.h b/engines/lastexpress/data/archive.h
new file mode 100644
index 0000000000..3860245bc5
--- /dev/null
+++ b/engines/lastexpress/data/archive.h
@@ -0,0 +1,75 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_HPFARCHIVE_H
+#define LASTEXPRESS_HPFARCHIVE_H
+
+/*
+ Archive file format (.HPF)
+
+ uint32 {4} - number of files
+
+ // for each file
+ char {12} - name (zero-terminated)
+ uint32 {4} - offset (expressed in sectors of 2048 bytes)
+ uint32 {4} - size (expressed in sectors of 2048 bytes)
+ byte {2} - file status: 1 = on disk (ie. in HD.HPF), 0 = on CD
+*/
+
+#include "common/archive.h"
+
+namespace LastExpress {
+
+class HPFArchive : public Common::Archive {
+public:
+ HPFArchive(const Common::String &path);
+
+ bool hasFile(const Common::String &name);
+ int listMembers(Common::ArchiveMemberList &list);
+ Common::ArchiveMemberPtr getMember(const Common::String &name);
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+ int count() { return _files.size(); }
+
+private:
+ static const unsigned int _archiveNameSize = 12;
+ static const unsigned int _archiveSectorSize = 2048;
+
+ // File entry
+ struct HPFEntry {
+ uint32 offset; ///< Offset (in sectors of 2048 bytes)
+ uint32 size; ///< Size (in sectors of 2048 bytes)
+ uint16 isOnHD; ///< File location (1: on HD; 0: on CD)
+ };
+
+ typedef Common::HashMap<Common::String, HPFEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+
+ FileMap _files; ///< List of files
+ Common::String _filename; ///< Filename of the archive
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_HPFARCHIVE_H
diff --git a/engines/lastexpress/data/background.cpp b/engines/lastexpress/data/background.cpp
new file mode 100644
index 0000000000..94d7fb16c3
--- /dev/null
+++ b/engines/lastexpress/data/background.cpp
@@ -0,0 +1,141 @@
+/* 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$
+ *
+ */
+
+// Based on Deniz Oezmen's code and Xentax Wiki documentation
+// http://oezmen.eu/
+// http://wiki.xentax.com/index.php/The_Last_Express_BG
+
+#include "lastexpress/data/background.h"
+
+#include "lastexpress/debug.h"
+
+namespace LastExpress {
+
+Background::Background() : _data(NULL) {
+ memset(&_header, 0, sizeof(BackgroundHeader));
+}
+
+Background::~Background() {
+ delete[] _data;
+}
+
+bool Background::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ delete[] _data;
+
+ // Load Background header
+ _header.posX = stream->readUint32LE();
+ _header.posY = stream->readUint32LE();
+ _header.width = stream->readUint32LE();
+ _header.height = stream->readUint32LE();
+ _header.redSize = stream->readUint32LE();
+ _header.blueSize = stream->readUint32LE();
+ _header.greenSize = stream->readUint32LE();
+
+ debugC(3, kLastExpressDebugGraphics, "Background Info: (%d, %d) - (%d x %d) - (%d, %d, %d)",
+ _header.posX, _header.posY, _header.width, _header.height,
+ _header.redSize, _header.blueSize, _header.greenSize);
+
+ // Load and decompress Background channel data
+ uint32 numPix = _header.width * _header.height;
+ byte *dataR = decodeComponent(stream, _header.redSize, numPix);
+ byte *dataB = decodeComponent(stream, _header.blueSize, numPix);
+ byte *dataG = decodeComponent(stream, _header.greenSize, numPix);
+
+ // Save to pixel buffer
+ // FIXME handle big-endian case
+ _data = new uint16[_header.width * _header.height];
+ for (uint i = 0; i < _header.width * _header.height; i++)
+ _data[i] = (uint16)((dataR[i] << 10) + (dataG[i] << 5) + dataB[i]);
+
+ // Cleanup buffers
+ delete[] dataR;
+ delete[] dataG;
+ delete[] dataB;
+
+ delete stream;
+
+ return true;
+}
+
+Common::Rect Background::draw(Graphics::Surface *surface) {
+ if (!_data) {
+ debugC(2, kLastExpressDebugGraphics, "Trying to show a background before loading data!");
+ return Common::Rect();
+ }
+
+ int i = 0;
+ for (uint16 y = 0; y < _header.height; y++) {
+ for (uint16 x = 0; x < _header.width; x++) {
+ surface->fillRect(Common::Rect((int16)(_header.posX + x), (int16)(_header.posY + y), (int16)(_header.posX + x + 1), (int16)(_header.posY + y + 1)), _data[i]);
+ i ++;
+ }
+ }
+
+ return Common::Rect((int16)_header.posX, (int16)_header.posY, (int16)(_header.posX + _header.width), (int16)(_header.posY + _header.height));
+}
+
+byte *Background::decodeComponent(Common::SeekableReadStream *in, uint32 inSize, uint32 outSize) const {
+ // Create the destination array
+ byte *out = new byte[outSize];
+ if (!out)
+ return NULL;
+
+ // Initialize the decoding
+ uint32 inPos = 0;
+ uint32 outPos = 0;
+
+ // Decode
+ while (inPos < inSize) {
+ byte inByte = in->readByte();
+ inPos++;
+
+ if (inByte < 0x80) {
+ // Direct decompression (RLE)
+ byte len = (inByte >> 5) + 1;
+ byte data = inByte & 0x1f;
+ for (int i = 0; i < len && outPos < outSize; i++)
+ out[outPos++] = data;
+ } else {
+ // Buffer back reference, 4096 byte window
+ // Take inByte and the following value as a big endian
+ // OfsLen while zeroing the first bit
+ uint16 ofsLen = ((inByte & 0x7F) << 8) | in->readByte();
+ inPos++;
+
+ int32 len = (ofsLen >> 12) + 3;
+ int32 hisPos = (int32)(outPos + (ofsLen & 0x0FFF) - 4096);
+ for (int i = 0; i < len && outPos < outSize; i++)
+ out[outPos++] = out[hisPos++];
+ }
+ }
+
+ return out;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/background.h b/engines/lastexpress/data/background.h
new file mode 100644
index 0000000000..fc1cc26fa4
--- /dev/null
+++ b/engines/lastexpress/data/background.h
@@ -0,0 +1,81 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_BACKGROUND_H
+#define LASTEXPRESS_BACKGROUND_H
+
+/*
+ Background file format (.BG)
+
+ header:
+ uint32 {4} - position X on screen
+ uint32 {4} - position Y on screen
+ uint32 {4} - image width
+ uint32 {4} - image height
+ uint32 {4} - red colour channel data size
+ uint32 {4} - blue colour channel data size
+ uint32 {4} - green colour channel data size
+
+ data:
+ byte {x} - red colour channel data
+ byte {x} - blue colour channel data
+ byte {x} - green colour channel data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Background : public Drawable {
+public:
+ Background();
+ ~Background();
+
+ bool load(Common::SeekableReadStream *stream);
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ struct BackgroundHeader {
+ uint32 posX; ///< position X on screen
+ uint32 posY; ///< position Y on screen
+ uint32 width; ///< image width
+ uint32 height; ///< image height
+ uint32 redSize; ///< red color channel data size
+ uint32 blueSize; ///< blue color channel data size
+ uint32 greenSize; ///< green color channel data size
+ };
+
+ BackgroundHeader _header;
+ uint16 *_data; ///< decoded background data
+
+ byte *decodeComponent(Common::SeekableReadStream *in, uint32 inSize, uint32 outSize) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BACKGROUND_H
diff --git a/engines/lastexpress/data/cursor.cpp b/engines/lastexpress/data/cursor.cpp
new file mode 100644
index 0000000000..4e7003578a
--- /dev/null
+++ b/engines/lastexpress/data/cursor.cpp
@@ -0,0 +1,144 @@
+/* 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/data/cursor.h"
+
+#include "lastexpress/lastexpress.h"
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+
+namespace LastExpress {
+
+Cursor::Cursor() : _current(kCursorMAX) {
+ memset(&_cursors, 0, sizeof(_cursors));
+}
+
+bool Cursor::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Load the whole file to memory
+ Common::MemoryReadStream *data = stream->readStream((uint32) stream->size());
+ delete stream;
+ if (!data)
+ return false;
+
+ // Read the hotspot data
+ for (int i = 0; i < kCursorMAX; i++) {
+ _cursors[i].hotspotX = data->readUint16LE();
+ _cursors[i].hotspotY = data->readUint16LE();
+ debugC(15, kLastExpressDebugCursor | kLastExpressDebugAll,
+ "Cursor %d hotspot x: %d, hotspot y: %d",
+ i, _cursors[i].hotspotX, _cursors[i].hotspotY);
+ }
+
+ // Read the pixel data
+ for (int i = 0; i < kCursorMAX; i++)
+ for (int pix = 0; pix < 32 * 32; pix++)
+ _cursors[i].image[pix] = data->readUint16LE();
+
+ delete data;
+ return true;
+}
+
+void Cursor::show(bool visible) const {
+ CursorMan.showMouse(visible);
+}
+
+bool Cursor::checkStyle(CursorStyle style) const {
+ if (style >= kCursorMAX) {
+ debugC(2, kLastExpressDebugGraphics, "Trying to use an invalid cursor style: was %d, max %d", (int)style, kCursorMAX);
+ return false;
+ }
+
+ return true;
+}
+
+void Cursor::setStyle(CursorStyle style) {
+ if (!checkStyle(style))
+ return;
+
+ if (style == _current)
+ return;
+
+ debugC(10, kLastExpressDebugCursor | kLastExpressDebugAll, "Cursor: setting style: %d", style);
+
+ // Save the new cursor
+ _current = style;
+
+ // Reuse the screen pixel format
+ Graphics::PixelFormat pf = g_system->getScreenFormat();
+ CursorMan.replaceCursor((const byte *)getCursorImage(style),
+ 32, 32, _cursors[style].hotspotX, _cursors[style].hotspotY,
+ 0, 1, &pf);
+}
+
+const uint16 *Cursor::getCursorImage(CursorStyle style) const {
+ if (!checkStyle(style))
+ return NULL;
+
+ return _cursors[style].image;
+}
+
+
+Icon::Icon(CursorStyle style) : _style(style), _x(0), _y(0), _brightness(100) {}
+
+void Icon::setPosition(int16 x, int16 y) {
+ _x = x;
+ _y = y;
+}
+
+void Icon::setBrightness(uint brightness) {
+ assert(brightness <= 100);
+
+ _brightness = (uint8)brightness;
+}
+
+Common::Rect Icon::draw(Graphics::Surface *surface) {
+ const uint16 *image = ((LastExpressEngine *)g_engine)->getCursor()->getCursorImage((CursorStyle)_style);
+ if (!image)
+ return Common::Rect();
+
+ // TODO adjust brightness. The original game seems to be using a table for that (at least in the highlighting case)
+ for (int j = 0; j < 32; j++) {
+ uint16 *s = (uint16 *)surface->getBasePtr(_x, _y + j);
+ for (int i = 0; i < 32; i++) {
+ if (_brightness == 100)
+ *s = *image;
+ else
+ // HACK change color to show highlight
+ *s = (*image & 0x739C) >> 1;
+
+ // Update the image and surface pointers
+ image++;
+ s++;
+ }
+ }
+
+ return Common::Rect(_x, _y, _x + 32, _y + 32);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/cursor.h b/engines/lastexpress/data/cursor.h
new file mode 100644
index 0000000000..992266569f
--- /dev/null
+++ b/engines/lastexpress/data/cursor.h
@@ -0,0 +1,93 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_CURSOR_H
+#define LASTEXPRESS_CURSOR_H
+
+/*
+ Cursor format (CURSORS.TBM)
+
+ style table:
+ (for each cursor)
+ uint16 {2} - hotspot X
+ uint16 {2} - hotspot Y
+
+ data:
+ (for each cursor)
+ uint16 {32*32} - cursor data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Icon : public Drawable {
+public:
+ Icon(CursorStyle style);
+
+ void setPosition(int16 x, int16 y);
+ void setBrightness(uint brightness);
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ CursorStyle _style;
+ int16 _x, _y;
+ uint8 _brightness;
+};
+
+class Cursor {
+public:
+ Cursor();
+
+ bool load(Common::SeekableReadStream *stream);
+ void show(bool visible) const;
+
+ void setStyle(CursorStyle style);
+ CursorStyle getStyle() const { return _current; }
+
+private:
+ // Style
+ CursorStyle _current;
+
+ // Cursors data
+ struct {
+ uint16 image[32 * 32];
+ uint16 hotspotX, hotspotY;
+ } _cursors[kCursorMAX];
+
+ bool checkStyle(CursorStyle style) const;
+ const uint16 *getCursorImage(CursorStyle style) const;
+
+ // Only allow full access for drawing (needed for getCursorImage)
+ friend Common::Rect Icon::draw(Graphics::Surface *surface);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_CURSOR_H
diff --git a/engines/lastexpress/data/font.cpp b/engines/lastexpress/data/font.cpp
new file mode 100644
index 0000000000..5f4b3b40b8
--- /dev/null
+++ b/engines/lastexpress/data/font.cpp
@@ -0,0 +1,205 @@
+/* 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/data/font.h"
+
+#include "common/system.h"
+
+namespace LastExpress {
+
+Font::Font() : _numGlyphs(0), _glyphs(NULL), _glyphWidths(0) {
+ memset(&_palette, 0, sizeof(_palette));
+ memset(&_charMap, 0, sizeof(_charMap));
+}
+
+Font::~Font() {
+ reset();
+}
+
+void Font::reset() {
+ delete[] _glyphs;
+ delete[] _glyphWidths;
+}
+
+bool Font::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ // Read the palette
+ for (uint i = 0; i < _paletteSize; i++) {
+ _palette[i] = stream->readUint16LE();
+ }
+
+ // Read the character map
+ stream->read(_charMap, _charMapSize);
+
+ // Read the glyphs
+ _numGlyphs = stream->readUint16LE();
+ _glyphs = new byte[_numGlyphs * 18 * 8];
+ stream->read(_glyphs, _numGlyphs * 18 * 8);
+
+ // TODO: Read something else?
+ //uint16 unknown = fontFile->readByte();
+ //warning("unknown = %d", unknown);
+ //warning("pos = %d", fontFile->pos());
+ //warning("left = %d", fontFile->size() - fontFile->pos());
+
+ //while (!fontFile->eos()) {
+ //unknown = fontFile->readByte();
+ //warning("val = %d", unknown);
+ //}
+
+ // Precalculate glyph widths
+ _glyphWidths = new byte[_numGlyphs];
+ for (uint16 i = 0; i < _numGlyphs; i++) {
+ _glyphWidths[i] = getGlyphWidth(i);
+ }
+
+ delete stream;
+
+ return true;
+}
+
+
+uint16 Font::getCharGlyph(uint16 c) const {
+ //warning("%c", c);
+ if (c >= 0x200)
+ error("Express::Font: Invalid character %d", c);
+
+ return _charMap[c];
+}
+
+byte *Font::getGlyphImg(uint16 g) {
+ if (!_glyphs)
+ error("Express::getGlyphImg: Invalid glyphs!");
+
+ if (g >= _numGlyphs)
+ error("Express::getGlyphImg: Invalid glyph %d (%d available)", g, _numGlyphs);
+
+ return _glyphs + g * 18 * 8;
+}
+
+uint8 Font::getGlyphWidth(uint16 g) {
+ byte *p = getGlyphImg(g);
+
+ uint8 maxLineWidth = 0;
+ for (int j = 0; j < 18; j++) {
+ uint8 currentLineWidth = 0;
+ for (uint8 i = 0; i < 16; i++) {
+ byte index;
+ if (i % 2)
+ index = *p & 0xf;
+ else
+ index = *p >> 4;
+ uint16 color = _palette[index];
+ if (color != 0x1f)
+ currentLineWidth = i;
+ if (i % 2)
+ p++;
+ }
+ if (currentLineWidth > maxLineWidth)
+ maxLineWidth = currentLineWidth;
+ }
+
+ return maxLineWidth;
+}
+
+byte *Font::getCharImg(uint16 c) {
+ return getGlyphImg(getCharGlyph(c));
+}
+
+uint8 Font::getCharWidth(uint16 c) const{
+ if (c == 0x20) {
+ // Space is a special case
+ // TODO: this is an arbitrary value
+ return 10;
+ } else {
+ if (!_glyphWidths)
+ error("Express::getCharWidth: Invalid glyphs widths!");
+
+ return _glyphWidths[getCharGlyph(c)];
+ }
+}
+
+uint16 Font::getStringWidth(Common::String str) const {
+ uint16 width = 0;
+ for (uint i = 0; i < str.size(); i++)
+ width += getCharWidth((unsigned) (int)str[i]);
+
+ return width;
+}
+
+uint16 Font::getStringWidth(const uint16 *str, uint16 length) const {
+ uint16 width = 0;
+ for (uint i = 0; i < length; i++)
+ width += getCharWidth(str[i]);
+
+ return width;
+}
+
+void Font::drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+ byte *p = getCharImg(c);
+
+ for (int16 j = 0; j < 18; j++) {
+ for (int16 i = 0; i < 16; i++) {
+ byte index;
+ if (i % 2)
+ index = *p & 0xf;
+ else
+ index = *p >> 4;
+ uint16 color = _palette[index];
+ if (color != 0x1f) {
+ surface->fillRect(Common::Rect(x+i, y+j, x+i+1, y+j+1), color);
+ }
+ if (i % 2)
+ p++;
+ }
+ }
+}
+
+Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str) {
+ int16 currentX = x;
+ for (uint i = 0; i < str.size(); i++) {
+ drawChar(surface, currentX, y, (unsigned) (int)str[i]);
+ currentX += getCharWidth((unsigned) (int)str[i]);
+ }
+
+ return Common::Rect(x, y, x + currentX, y + (int16)_charHeight);
+}
+
+Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, const uint16 *str, uint16 length) {
+ int16 currentX = x;
+ for (uint i = 0; i < length; i++) {
+ drawChar(surface, currentX, y, str[i]);
+ currentX += getCharWidth(str[i]);
+ }
+
+ return Common::Rect(x, y, x + currentX, y + (int16)_charHeight);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/font.h b/engines/lastexpress/data/font.h
new file mode 100644
index 0000000000..457c0c3432
--- /dev/null
+++ b/engines/lastexpress/data/font.h
@@ -0,0 +1,82 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_FONT_H
+#define LASTEXPRESS_FONT_H
+
+/*
+ Font format (FONT.DAT)
+
+ uint16 {40} - Palette data
+ byte {200} - Character map
+ uint16 {2} - Number of glyphs
+
+ // For each glyph
+ byte {18*8} - Glyph data
+
+ byte {x} - Unknown data (probably just garbage)
+*/
+
+#include "common/stream.h"
+#include "graphics/surface.h"
+
+namespace LastExpress {
+
+class Font {
+public:
+ Font();
+ ~Font();
+
+ bool load(Common::SeekableReadStream *stream);
+ Common::Rect drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str);
+ Common::Rect drawString(Graphics::Surface *surface, int16 x, int16 y, const uint16 *str, uint16 length);
+
+private:
+ static const uint32 _paletteSize = 0x10;
+ static const uint32 _charMapSize = 0x200;
+ static const uint32 _charHeight = 16;
+
+ void reset();
+
+ uint16 getCharGlyph(uint16 c) const;
+ byte *getGlyphImg(uint16 g);
+ uint8 getGlyphWidth(uint16 g);
+ byte *getCharImg(uint16 c);
+ uint8 getCharWidth(uint16 c) const;
+ uint16 getStringWidth(Common::String str) const;
+ uint16 getStringWidth(const uint16 *str, uint16 length) const;
+ void drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+
+ // Font data
+ uint16 _palette[_paletteSize];
+ uint8 _charMap[_charMapSize];
+ uint16 _numGlyphs;
+ byte *_glyphs;
+ uint8 *_glyphWidths;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FONT_H
diff --git a/engines/lastexpress/data/scene.cpp b/engines/lastexpress/data/scene.cpp
new file mode 100644
index 0000000000..e1be515bbc
--- /dev/null
+++ b/engines/lastexpress/data/scene.cpp
@@ -0,0 +1,292 @@
+/* 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/data/scene.h"
+
+#include "lastexpress/data/background.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+SceneHotspot *SceneHotspot::load(Common::SeekableReadStream *stream) {
+ SceneHotspot *hs = new SceneHotspot();
+
+ // Rect
+ hs->rect.left = (int16)stream->readUint16LE();
+ hs->rect.right = (int16)stream->readUint16LE();
+ hs->rect.top = (int16)stream->readUint16LE();
+ hs->rect.bottom = (int16)stream->readUint16LE();
+
+ hs->coordsOffset = stream->readUint32LE();
+ hs->scene = (SceneIndex)stream->readUint16LE();
+ hs->location = stream->readByte();
+ hs->action = (Action)stream->readByte();
+ hs->param1 = stream->readByte();
+ hs->param2 = stream->readByte();
+ hs->param3 = stream->readByte();
+ hs->cursor = stream->readByte();
+ hs->next = stream->readUint32LE();
+
+ debugC(10, kLastExpressDebugScenes, "\thotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d,%d)",
+ hs->scene, hs->location, hs->action, hs->param1, hs->param2, hs->param3, hs->cursor, hs->rect.left, hs->rect.top, hs->rect.right, hs->rect.bottom);
+ debugC(10, kLastExpressDebugScenes, "\t coords=%d next=%d ", hs->coordsOffset, hs->next);
+
+ // Read all coords data
+ uint32 offset = hs->coordsOffset;
+ while (offset != 0) {
+
+ SceneCoord *sceneCoord = new SceneCoord;
+
+ stream->seek(offset, SEEK_SET);
+
+ sceneCoord->field_0 = stream->readSint32LE();
+ sceneCoord->field_4 = stream->readSint32LE();
+ sceneCoord->field_8 = stream->readByte();
+ sceneCoord->next = stream->readUint32LE();
+
+ hs->_coords.push_back(sceneCoord);
+
+ offset = sceneCoord->next;
+ }
+
+ return hs;
+}
+
+Common::String SceneHotspot::toString() const {
+ Common::String output = "";
+
+ output += Common::String::printf(" hotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d,%d)",
+ scene, location, action, param1, param2, param3, cursor, rect.left, rect.top, rect.right, rect.bottom);
+
+ return output;
+}
+
+bool SceneHotspot::isInside(const Common::Point &point) {
+
+ bool contains = rect.contains(point);
+
+ if (_coords.empty() || !contains)
+ return contains;
+
+ // Checks extended coordinates
+ for (uint i = 0; i < _coords.size(); i++) {
+
+ SceneCoord *sCoord = _coords[i];
+
+ bool cont;
+ if (sCoord->field_8)
+ cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) >= 0;
+ else
+ cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) <= 0;
+
+ if (!cont)
+ return false;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene
+Scene::~Scene() {
+ // Free the hotspots
+ for (int i = 0; i < (int)_hotspots.size(); i++)
+ delete _hotspots[i];
+}
+
+Scene *Scene::load(Common::SeekableReadStream *stream) {
+ Scene *scene = new Scene();
+
+ stream->read(&scene->_name, sizeof(scene->_name));
+ scene->_sig = stream->readByte();
+ scene->entityPosition = (EntityPosition)stream->readUint16LE();;
+ scene->location = (Location)stream->readUint16LE();
+ scene->car = (CarIndex)stream->readUint16LE();
+ scene->position = stream->readByte();
+ scene->type = (Type)stream->readByte();
+ scene->param1 = stream->readByte();
+ scene->param2 = stream->readByte();
+ scene->param3 = stream->readByte();
+ scene->_hotspot = stream->readUint32LE();
+
+ return scene;
+}
+
+void Scene::loadHotspots(Common::SeekableReadStream *stream) {
+ if (!_hotspots.empty())
+ return;
+
+ debugC(10, kLastExpressDebugScenes, "Scene: name=%s, sig=%02d, entityPosition=%d, location=%d", _name, _sig, entityPosition, location);
+ debugC(10, kLastExpressDebugScenes, "\tcar=%02d, position=%02d, type=%02d, param1=%02d", car, position, type, param1);
+ debugC(10, kLastExpressDebugScenes, "\tparam2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);
+
+ // Read all hotspots
+ if (_hotspot != 0) {
+ stream->seek((int32)_hotspot, SEEK_SET);
+ SceneHotspot *hotspot = SceneHotspot::load(stream);
+ while (hotspot) {
+ _hotspots.push_back(hotspot);
+
+ if (hotspot->next == 0)
+ break;
+
+ stream->seek((int32)hotspot->next, SEEK_SET);
+ hotspot = SceneHotspot::load(stream);
+ }
+ }
+}
+
+bool Scene::checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot) {
+ bool found = false;
+ int _location = 0;
+
+ for (int i = 0; i < (int)_hotspots.size(); i++) {
+ if (_hotspots[i]->isInside(coord)) {
+ if (_location <= _hotspots[i]->location) {
+ _location = _hotspots[i]->location;
+ *hotspot = _hotspots[i];
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
+SceneHotspot *Scene::getHotspot(uint index) {
+ if (_hotspots.empty())
+ error("Scene::getHotspot: scene does not have any hotspots!");
+
+ if (index >= _hotspots.size())
+ error("Scene::getHotspot: invalid index (was: %d, max: %d)", index, _hotspots.size() - 1);
+
+ return _hotspots[index];
+}
+
+Common::Rect Scene::draw(Graphics::Surface *surface) {
+ // Safety checks
+ Common::Rect rect;
+
+ Common::String sceneName(_name);
+ sceneName.trim();
+ if (sceneName.empty())
+ error("Scene::draw: This scene is not a valid drawing scene!");
+
+ // Load background
+ Background *background = ((LastExpressEngine *)g_engine)->getResourceManager()->loadBackground(sceneName);
+ if (background) {
+ rect = background->draw(surface);
+ delete background;
+ }
+
+ return rect;
+}
+
+Common::String Scene::toString() {
+ Common::String output = "";
+
+ output += Common::String::printf("Scene: name=%s, sig=%02d, entityPosition=%d, location=%d\n", _name, _sig, entityPosition, location);
+ output += Common::String::printf(" car=%02d, position=%02d, type=%02d, param1=%02d\n", car, position, type, param1);
+ output += Common::String::printf(" param2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);
+
+ // Hotspots
+ if (_hotspots.size() != 0) {
+ output += "\nHotspots:\n";
+ for (int i = 0; i < (int)_hotspots.size(); i++)
+ output += _hotspots[i]->toString() + "\n";
+ }
+
+ return output;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SceneLoader
+SceneLoader::SceneLoader() : _stream(NULL) {}
+
+SceneLoader::~SceneLoader() {
+ clear();
+}
+
+void SceneLoader::clear() {
+ // Remove all scenes
+ for (int i = 0; i < (int)_scenes.size(); i++)
+ delete _scenes[i];
+
+ _scenes.clear();
+
+ delete _stream;
+}
+
+bool SceneLoader::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ clear();
+
+ _stream = stream;
+
+ // Read the default scene to get the total number of scenes
+ Scene *header = Scene::load(_stream);
+ if (!header)
+ error("SceneLoader::load: Invalid data file!");
+
+ debugC(2, kLastExpressDebugScenes, " found %d entries", header->entityPosition); /* Header entityPosition is the scene count */
+
+ if (header->entityPosition > 2500) {
+ delete header;
+
+ return false;
+ }
+
+ _scenes.push_back(header);
+
+ // Read all the chunks
+ for (uint i = 0; i < (uint)header->entityPosition; ++i) {
+ Scene *scene = Scene::load(_stream);
+ if (!scene)
+ break;
+
+ _scenes.push_back(scene);
+ }
+
+ return true;
+}
+
+Scene *SceneLoader::get(SceneIndex index) {
+ if (_scenes.empty())
+ return NULL;
+
+ if (index > _scenes.size())
+ return NULL;
+
+ // Load the hotspots if needed
+ _scenes[(int)index]->loadHotspots(_stream);
+
+ return _scenes[(int)index];
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/scene.h b/engines/lastexpress/data/scene.h
new file mode 100644
index 0000000000..ae2f3d2d28
--- /dev/null
+++ b/engines/lastexpress/data/scene.h
@@ -0,0 +1,245 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SCENE_H
+#define LASTEXPRESS_SCENE_H
+
+/*
+ Scene format (CDTRAIN.DAT)
+
+ (text:00484750)
+ header (24 bytes)
+ char {8} - entry name (null terminated)
+ byte {1} - 0xCD
+ uint16 {2} - number of scenes (for first entry - always 0 after?)
+ uint16 {2} - 11 ??
+ uint16 {2} - car
+ byte {1} - camera position (used to get the proper sequences to show)
+ byte {1} - type
+ byte {1} - param1
+ byte {1} - param2
+ byte {1} - param3
+ uint32 {4} - Offset to hotspot info struct
+
+ probably contains cursor type too / scene index : 0 - 2500 (max)
+
+ hotspot info (24 bytes)
+ uint16 {2} - left
+ uint16 {2} - right
+ uint16 {2} - top
+ uint16 {2} - bottom
+ uint32 {4} - scene coords data
+ uint16 {2} - scene
+ byte {1} - location;
+ byte {1} - action;
+ byte {1} - param1;
+ byte {1} - param2;
+ byte {1} - param3
+ byte {1} - cursor
+ uint32{4} - offset to next hotpost
+
+ coords data (9 bytes)
+ uint32 {4} - ??
+ uint32 {4} - ??
+ byte {1} - ??
+ uint32 {4} - offset to next coords data structure
+
+*/
+
+#include "lastexpress/drawable.h"
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Scene;
+
+class SceneHotspot {
+public:
+ enum Action {
+ kActionInventory = 1,
+ kActionSavePoint = 2,
+ kActionPlaySound = 3,
+ kActionPlayMusic = 4,
+ kActionKnockOnDoor = 5,
+ kActionCompartment = 6,
+ kActionPlaySounds = 7,
+ kActionPlayAnimation = 8,
+ kActionOpenCloseObject = 9,
+ kActionObjectUpdateLocation2 = 10,
+ kActionSetItemLocation = 11,
+ kAction12 = 12,
+ kActionPickItem = 13,
+ kActionDropItem = 14,
+ kAction15 = 15,
+ kActionEnterCompartment = 16,
+ kActionGetOutsideTrain = 18,
+ kActionSlip = 19,
+ kActionGetInsideTrain = 20,
+ kActionClimbUpTrain = 21,
+ kActionClimbDownTrain = 22,
+ kActionJumpUpDownTrain = 23,
+ kActionUnbound = 24,
+ kAction25 = 25,
+ kAction26 = 26,
+ kAction27 = 27,
+ kActionConcertSitCough = 28,
+ kAction29 = 29,
+ kActionCatchBeetle = 30,
+ kActionExitCompartment = 31,
+ kAction32 = 32,
+ KActionUseWhistle = 33,
+ kActionOpenMatchBox = 34,
+ kActionOpenBed = 35,
+ kActionDialog = 37,
+ kActionEggBox = 38,
+ kAction39 = 39,
+ kActionBed = 40,
+ kAction41 = 41,
+ kAction42 = 42,
+ kActionSwitchChapter = 43,
+ kAction44 = 44
+ };
+
+ struct SceneCoord {
+ int32 field_0;
+ int32 field_4;
+ byte field_8;
+ uint32 next;
+
+ SceneCoord() {
+ field_0 = 0;
+ field_4 = 0;
+ field_8 = 0;
+ next = 0;
+ }
+ };
+
+ Common::Rect rect;
+ uint32 coordsOffset;
+ SceneIndex scene;
+ byte location;
+ Action action;
+ byte param1;
+ byte param2;
+ byte param3;
+ byte cursor;
+ uint32 next;
+
+ SceneHotspot() {}
+ static SceneHotspot *load(Common::SeekableReadStream *stream);
+
+ bool isInside(const Common::Point &point);
+
+ Common::String toString() const;
+
+private:
+ Common::Array<SceneCoord *> _coords;
+};
+
+class SceneLoader {
+public:
+ SceneLoader();
+ ~SceneLoader();
+
+ bool load(Common::SeekableReadStream *stream);
+ Scene *get(SceneIndex index);
+
+ uint32 count() const { return _scenes.size() - 1; };
+
+private:
+ Common::SeekableReadStream *_stream;
+ Common::Array<Scene *> _scenes;
+
+ void clear();
+};
+
+class Scene : public Drawable {
+public:
+ // Enumerations
+ enum Type {
+ // PreProcess
+ kTypeObject = 1,
+ kTypeItem = 2,
+ kTypeItem2 = 3,
+ kTypeObjectItem = 4,
+ kTypeItem3 = 5,
+ kTypeObjectLocation2 = 6,
+ kTypeCompartments = 7,
+ kTypeCompartmentsItem = 8,
+
+ // PostProcess
+ kTypeList = 128,
+ kTypeSavePointChapter = 129,
+ kTypeLoadBeetleSequences = 130,
+ kTypeGameOver = 131,
+ kTypeReadText = 132,
+ kType133 = 133
+ };
+
+ // Data
+ EntityPosition entityPosition;
+ Location location;
+ CarIndex car;
+ Position position;
+ Type type;
+ byte param1;
+ byte param2;
+ byte param3;
+
+ ~Scene();
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+ // Hotspots
+ Common::Array<SceneHotspot *> *getHotspots() { return &_hotspots; }
+ bool checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot);
+ SceneHotspot *getHotspot(uint index = 0);
+
+ Common::String toString();
+
+private:
+ char _name[8];
+ byte _sig;
+ uint32 _hotspot;
+
+ Scene() {}
+ Common::Array<SceneHotspot *> _hotspots;
+
+ static Scene *load(Common::SeekableReadStream *stream);
+ void loadHotspots(Common::SeekableReadStream *stream);
+
+ void clear();
+
+ // Only allow full access for loading the scene and the hotspots
+ friend bool SceneLoader::load(Common::SeekableReadStream *stream);
+ friend Scene *SceneLoader::get(SceneIndex index);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SCENE_H
diff --git a/engines/lastexpress/data/sequence.cpp b/engines/lastexpress/data/sequence.cpp
new file mode 100644
index 0000000000..be8b14fc27
--- /dev/null
+++ b/engines/lastexpress/data/sequence.cpp
@@ -0,0 +1,475 @@
+/* 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$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/debug.h"
+
+namespace LastExpress {
+
+void FrameInfo::read(Common::SeekableReadStream *in, bool isSequence) {
+ // Save the current position
+ int32 basePos = in->pos();
+
+ dataOffset = in->readUint32LE();
+ unknown = in->readUint32LE();
+ paletteOffset = in->readUint32LE();
+ xPos1 = in->readUint32LE();
+ yPos1 = in->readUint32LE();
+ xPos2 = in->readUint32LE();
+ yPos2 = in->readUint32LE();
+ initialSkip = in->readUint32LE();
+ decompressedEndOffset = in->readUint32LE();
+
+ // Read the compression type for NIS files
+ if (!isSequence) {
+ in->seek(basePos + 0x124);
+ } else {
+ hotspot.left = (int16)in->readUint16LE();
+ hotspot.right = (int16)in->readUint16LE();
+ hotspot.top = (int16)in->readUint16LE();
+ hotspot.bottom = (int16)in->readUint16LE();
+ }
+
+ compressionType = in->readByte();
+ subType = (FrameSubType)in->readByte();
+
+ // Sequence information
+ field_2E = in->readByte();
+ keepPreviousFrame = in->readByte();
+ field_30 = in->readByte();
+ field_31 = in->readByte();
+ soundAction = in->readByte();
+ field_33 = in->readByte();
+ position = in->readByte();
+ field_35 = in->readByte();
+ field_36 = in->readUint16LE();
+ field_38 = in->readUint32LE();
+ entityPosition = (EntityPosition)in->readUint16LE();
+ location = in->readUint16LE();
+ next = in->readUint32LE();
+}
+
+
+// AnimFrame
+
+AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f) : _palette(NULL) {
+ _palSize = 1;
+ // TODO: use just the needed rectangle
+ _image.create(640, 480, 1);
+
+ //debugC(6, kLastExpressDebugGraphics, " Offsets: data=%d, unknown=%d, palette=%d", f.dataOffset, f.unknown, f.paletteOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Position: (%d, %d) - (%d, %d)", f.xPos1, f.yPos1, f.xPos2, f.yPos2);
+ //debugC(6, kLastExpressDebugGraphics, " Initial Skip: %d", f.initialSkip);
+ //debugC(6, kLastExpressDebugGraphics, " Decompressed end offset: %d", f.decompressedEndOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Hotspot: (%d, %d) x (%d, %d)\n", f.hotspot.left, f.hotspot.top, f.hotspot.right, f.hotspot.bottom);
+ //debugC(6, kLastExpressDebugGraphics, " Compression type: %u / %u", f.compressionType, f.subType);
+ //debugC(6, kLastExpressDebugGraphics, " Unknown: %u - %u - %u - %u - %u - %u - %u - %d", f.field_2E, f.field_2F, f.field_30, f.field_31, f.field_33, f.field_35, f.field_36, f.field_38);
+ //debugC(6, kLastExpressDebugGraphics, " Sound action: %u", f.soundAction);
+ //debugC(6, kLastExpressDebugGraphics, " Position: %d", f.position);
+ //debugC(6, kLastExpressDebugGraphics, " Entity Position: %d", f.entityPosition);
+ //debugC(6, kLastExpressDebugGraphics, " Location: %d", f.location);
+ //debugC(6, kLastExpressDebugGraphics, " next: %d", f.next);
+
+ switch (f.compressionType) {
+ case 0:
+ // Empty frame
+ break;
+ case 3:
+ decomp3(in, f);
+ break;
+ case 4:
+ decomp4(in, f);
+ break;
+ case 5:
+ decomp5(in, f);
+ break;
+ case 7:
+ decomp7(in, f);
+ break;
+ case 255:
+ decompFF(in, f);
+ break;
+ default:
+ error("Unknown frame compression: %d", f.compressionType);
+ }
+
+ readPalette(in, f);
+ _rect = Common::Rect((int16)f.xPos1, (int16)f.yPos1, (int16)f.xPos2, (int16)f.yPos2);
+ //_rect.debugPrint(0, "Frame rect:");
+}
+
+AnimFrame::~AnimFrame() {
+ _image.free();
+ delete[] _palette;
+}
+
+Common::Rect AnimFrame::draw(Graphics::Surface *s) {
+ byte *inp = (byte *)_image.pixels;
+ uint16 *outp = (uint16 *)s->pixels;
+ for (int i = 0; i < 640 * 480; i++, inp++, outp++) {
+ if (*inp)
+ *outp = _palette[*inp];
+ }
+ return _rect;
+}
+
+void AnimFrame::readPalette(Common::SeekableReadStream *in, const FrameInfo &f) {
+ // Read the palette
+ in->seek((int)f.paletteOffset);
+ _palette = new uint16[_palSize];
+ for (uint32 i = 0; i < _palSize; i++) {
+ _palette[i] = in->readUint16LE();
+ }
+}
+
+void AnimFrame::decomp3(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0x7, 3);
+}
+
+void AnimFrame::decomp4(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0xf, 4);
+}
+
+void AnimFrame::decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ opcode &= 0x3f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x3f;
+ if (opcode & 0x20) {
+ opcode = ((opcode & 0x1f) << 8) + in->readByte();
+ if (opcode & 0x1000) {
+ out += opcode & 0xfff;
+ continue;
+ }
+ }
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & mask;
+ opcode >>= shift;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp5(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (!(opcode & 0x1f)) {
+ opcode = (uint16)((opcode << 3) + in->readByte());
+ if (opcode & 0x400) {
+ // skip these 10 bits
+ out += (opcode & 0x3ff);
+ } else {
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & 0x1f;
+ opcode >>= 5;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp7(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ if (opcode & 0x20) {
+ opcode &= 0x1f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x1f;
+ if (opcode & 0x10) {
+ opcode = ((opcode & 0xf) << 8) + in->readByte();
+ if (opcode & 0x800) {
+ // skip these 11 bits
+ out += (opcode & 0x7ff);
+ continue;
+ }
+ }
+
+ // skip these 4 bits
+ out += opcode + 2;
+ }
+ } else {
+ opcode &= 0x3f;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ }
+ }
+}
+
+void AnimFrame::decompFF(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode < 0x80) {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ } else {
+ if (opcode < 0xf0) {
+ if (opcode < 0xe0) {
+ // copy old part
+ uint32 old = out + ((opcode & 0x7) << 8) + in->readByte() - 2048;
+ opcode = ((opcode >> 3) & 0xf) + 3;
+ for (int i = 0; i < opcode; i++, out++, old++) {
+ p[out] = p[old];
+ }
+ } else {
+ opcode = (opcode & 0xf) + 1;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ out += ((opcode & 0xf) << 8) + in->readByte();
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SEQUENCE
+//////////////////////////////////////////////////////////////////////////
+
+Sequence::~Sequence() {
+ reset();
+}
+
+void Sequence::reset() {
+ _frames.clear();
+ delete _stream;
+ _stream = NULL;
+}
+
+Sequence *Sequence::load(Common::String name, Common::SeekableReadStream *stream, byte field30) {
+ Sequence *sequence = new Sequence(name);
+
+ if (!sequence->load(stream, field30)) {
+ delete sequence;
+ return NULL;
+ }
+
+ return sequence;
+}
+
+bool Sequence::load(Common::SeekableReadStream *stream, byte field30) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ _field30 = field30;
+
+ // Keep stream for later decoding of sequence
+ _stream = stream;
+
+ // Read header to get the number of frames
+ _stream->seek(0);
+ uint32 numframes = _stream->readUint32LE();
+ uint32 unknown = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of frames in sequence: %d / unknown=0x%x", numframes, unknown);
+
+ // Store frames information
+ for (uint i = 0; i < numframes; i++) {
+
+ // Move stream to start of frame
+ _stream->seek((int32)(_sequenceHeaderSize + i * _sequenceFrameSize), SEEK_SET);
+ if (_stream->eos())
+ error("Couldn't seek to the current frame data");
+
+ // Check if there is enough data
+ if ((unsigned)(_stream->size() - _stream->pos()) < _sequenceFrameSize)
+ error("The sequence frame does not have a valid header");
+
+ FrameInfo info;
+ info.read(_stream, true);
+ _frames.push_back(info);
+ }
+
+ _isLoaded = true;
+
+ return true;
+}
+
+FrameInfo *Sequence::getFrameInfo(uint16 index) {
+ if (_frames.size() == 0)
+ error("Trying to decode a sequence before loading its data");
+
+ if (index > _frames.size() - 1)
+ error("Invalid sequence frame requested: %d, max %d", index, _frames.size() - 1);
+
+ return &_frames[index];
+}
+
+AnimFrame *Sequence::getFrame(uint16 index) {
+
+ FrameInfo *frame = getFrameInfo(index);
+
+ if (!frame)
+ return NULL;
+
+ // Skip "invalid" frames
+ if (frame->compressionType == 0)
+ return NULL;
+
+ debugC(9, kLastExpressDebugGraphics, "Decoding sequence %s: frame %d / %d", _name.c_str(), index, _frames.size() - 1);
+
+ return new AnimFrame(_stream, *frame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SequenceFrame
+SequenceFrame::~SequenceFrame() {
+ if (_dispose && _sequence) {
+ delete _sequence;
+ }
+
+ _sequence = NULL;
+}
+
+Common::Rect SequenceFrame::draw(Graphics::Surface *surface) {
+ if (!_sequence || _frame >= _sequence->count())
+ return Common::Rect();
+
+ AnimFrame *f = _sequence->getFrame(_frame);
+ if (!f)
+ return Common::Rect();
+
+ Common::Rect rect = f->draw(surface);
+
+ delete f;
+
+ return rect;
+}
+
+bool SequenceFrame::setFrame(uint16 frame) {
+ if (!_sequence)
+ return false;
+
+ if (frame < _sequence->count()) {
+ _frame = frame;
+ return true;
+ } else
+ return false;
+}
+
+bool SequenceFrame::nextFrame() {
+ return setFrame(_frame + 1);
+}
+
+FrameInfo *SequenceFrame::getInfo() {
+ if (!_sequence)
+ error("SequenceFrame::getFrameInfo: Invalid sequence!");
+
+ return _sequence->getFrameInfo(_frame);
+}
+
+bool SequenceFrame::equal(const SequenceFrame *other) const {
+ return _sequence->getName() == other->_sequence->getName() && _frame == other->_frame;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/sequence.h b/engines/lastexpress/data/sequence.h
new file mode 100644
index 0000000000..eadc2962f8
--- /dev/null
+++ b/engines/lastexpress/data/sequence.h
@@ -0,0 +1,205 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SEQUENCE_H
+#define LASTEXPRESS_SEQUENCE_H
+
+/*
+ Sequence format (.SEQ / .NIS (frame header & data only))
+
+ uint32 {4} - Number of frames in sequence
+ uint32 {4} - Unknown
+
+ frames headers (68 bytes):
+ // for each frame
+ uint32 {4} - Data offset (from beginning of file)
+ uint32 {4} - Unknown
+ uint32 {4} - Palette offset (from beginning of file)
+ uint32 {4} - Top-left X coordinate
+ uint32 {4} - Top-left Y coordinate
+ uint32 {4} - Bottom-right X coordinate
+ uint32 {4} - Bottom-right Y coordinate
+ uint32 {4} - Initial offset of decompressed data (doubled, since each pixel occupies one color word)
+ uint32 {4} - End of data after decompression
+
+ (for SEQ files only)
+ uint16 {2} - Hotspot left
+ uint16 {2} - Hotspot right
+ uint16 {2} - Hotspot top
+ uint16 {2} - Hotspot bottom
+ byte {1} - Compression type
+ byte {1} - Subtype (determines which set of decompression functions will be called) => 0, 1, 2, 3
+ byte {1} - Unknown
+ byte {1} - Unknown
+ uint16 {2} - Unknown
+ byte {1} - Sound action
+ byte {1} - Unknown
+ uint32 {4} - positionId
+ uint32 {4} - Unknown
+ uint16 {2} - Entity Position
+ uint16 {2} - Location (~z-order)
+ uint32 {4} - Next sequence in the linked list
+
+ (for NIS files: found at 0x124)
+ byte {1} - Compression type
+
+ palette data:
+ uint16 {x} - palette data (max size: 256)
+
+ data
+ byte {x} - compressed image data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+enum FrameSubType {
+ kFrameTypeNone = 0,
+ kFrameType1 = 1,
+ kFrameType2 = 2,
+ kFrameType3 = 3
+};
+
+struct FrameInfo {
+ void read(Common::SeekableReadStream *in, bool isSequence);
+
+ uint32 dataOffset; ///< Data offset (from beginning of file)
+ uint32 unknown; ///< FIXME: unknown data
+ uint32 paletteOffset; ///< Palette offset (from beginning of file)
+ uint32 xPos1; ///< Top-left X coordinate
+ uint32 yPos1; ///< Top-left Y coordinate
+ uint32 xPos2; ///< Bottom-right X coordinate
+ uint32 yPos2; ///< Bottom-right Y coordinate
+ uint32 initialSkip; ///< Initial on-screen offset of decompressed data (doubled, since each pixel occupies one color word)
+ uint32 decompressedEndOffset; ///< End of data after decompression
+
+ // NIS frame headers end here. SEQ frame headers have additional 32 bytes of
+ // data, notably the compression type at the position outlined above in
+ // CompPos_SEQ
+
+ Common::Rect hotspot;
+
+ byte compressionType; ///< Type of frame compression (0x03, 0x04, 0x05, 0x07, 0xFF)
+ FrameSubType subType; ///< Subtype (byte)
+
+ byte field_2E;
+ byte keepPreviousFrame;
+ byte field_30;
+ byte field_31;
+ byte soundAction;
+ byte field_33;
+ Position position;
+ byte field_35;
+ int16 field_36;
+ uint32 field_38;
+ EntityPosition entityPosition;
+ uint16 location;
+ uint32 next;
+};
+
+class AnimFrame : public Drawable {
+public:
+ AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f);
+ ~AnimFrame();
+ Common::Rect draw(Graphics::Surface *s);
+
+private:
+ void decomp3(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp4(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift);
+ void decomp5(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp7(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decompFF(Common::SeekableReadStream *in, const FrameInfo &f);
+ void readPalette(Common::SeekableReadStream *in, const FrameInfo &f);
+
+ Graphics::Surface _image;
+ uint16 _palSize;
+ uint16 *_palette;
+ Common::Rect _rect;
+};
+
+class Sequence {
+public:
+ Sequence(Common::String name) : _stream(NULL), _isLoaded(false), _name(name), _field30(15) {}
+ ~Sequence();
+
+ static Sequence *load(Common::String name, Common::SeekableReadStream *stream = NULL, byte field30 = 15);
+
+ bool load(Common::SeekableReadStream *stream, byte field30 = 15);
+
+ uint16 count() const { return (uint16)_frames.size(); };
+ AnimFrame *getFrame(uint16 index = 0);
+ FrameInfo *getFrameInfo(uint16 index = 0);
+
+ Common::String getName() { return _name; }
+ byte getField30() { return _field30; }
+
+ bool isLoaded() { return _isLoaded; }
+
+private:
+ static const uint32 _sequenceHeaderSize = 8;
+ static const uint32 _sequenceFrameSize = 68;
+
+ void reset();
+
+ Common::Array<FrameInfo> _frames;
+ Common::SeekableReadStream *_stream;
+ bool _isLoaded;
+
+ Common::String _name;
+ byte _field30; // used when copying sequences
+};
+
+class SequenceFrame : public Drawable {
+public:
+ SequenceFrame(Sequence *sequence, uint16 frame = 0, bool dispose = false) : _sequence(sequence), _frame(frame), _dispose(dispose) {}
+ ~SequenceFrame();
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+ bool setFrame(uint16 frame);
+ uint32 getFrame() { return _frame; }
+ bool nextFrame();
+
+ Common::String getName() { return _sequence->getName(); }
+ FrameInfo *getInfo();
+
+ bool equal(const SequenceFrame *other) const;
+
+private:
+ Sequence *_sequence;
+ uint16 _frame;
+ bool _dispose;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SEQUENCE_H
diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
new file mode 100644
index 0000000000..496bd58772
--- /dev/null
+++ b/engines/lastexpress/data/snd.cpp
@@ -0,0 +1,141 @@
+/* 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$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SND
+
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/debug.h"
+
+#include "sound/decoders/adpcm.h"
+#include "sound/audiostream.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Sound
+//////////////////////////////////////////////////////////////////////////
+SimpleSound::SimpleSound() : _size(0), _blocks(0), _blockSize(0) {}
+
+SimpleSound::~SimpleSound() {
+ stop();
+}
+
+// Stop the sound
+void SimpleSound::stop() const {
+ g_system->getMixer()->stopHandle(_handle);
+}
+
+void SimpleSound::loadHeader(Common::SeekableReadStream *in) {
+ _size = in->readUint32LE();
+ _blocks = in->readUint16LE();
+ debugC(5, kLastExpressDebugSound, " sound header data: size=\"%d\", %d blocks", _size, _blocks);
+
+ assert (_size % _blocks == 0);
+ _blockSize = _size / _blocks;
+}
+
+Audio::AudioStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size) const {
+ return Audio::makeADPCMStream(in, DisposeAfterUse::YES, size, Audio::kADPCMMSImaLastExpress, 44100, 1, _blockSize);
+}
+
+void SimpleSound::play(Audio::AudioStream *as) {
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, as);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// StreamedSound
+//////////////////////////////////////////////////////////////////////////
+StreamedSound::StreamedSound() {}
+StreamedSound::~StreamedSound() {}
+
+bool StreamedSound::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ g_system->getMixer()->stopHandle(_handle);
+
+ loadHeader(stream);
+
+ // Start decoding the input stream
+ Audio::AudioStream *as = makeDecoder(stream, _size);
+
+ // Start playing the decoded audio stream
+ play(as);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// StreamedSound
+//////////////////////////////////////////////////////////////////////////
+AppendableSound::AppendableSound() : SimpleSound() {
+ // Create an audio stream where the decoded chunks will be appended
+ _as = Audio::makeQueuingAudioStream(44100, false);
+ _finished = false;
+
+ // Start playing the decoded audio stream
+ play(_as);
+
+ // Initialize the block size
+ // TODO: get it as an argument?
+ _blockSize = 739;
+}
+
+AppendableSound::~AppendableSound() {
+ finish();
+
+ _as = NULL;
+}
+
+void AppendableSound::queueBuffer(const byte *data, uint32 size) {
+ Common::MemoryReadStream *buffer = new Common::MemoryReadStream(data, size);
+ queueBuffer(buffer);
+}
+
+void AppendableSound::queueBuffer(Common::SeekableReadStream *bufferIn) {
+ if (!_as)
+ error("AppendableSound::queueBuffer - internal error: the audio stream is invalid!");
+
+ // Setup the ADPCM decoder
+ uint32 sizeIn = (uint32)bufferIn->size();
+ Audio::AudioStream *adpcm = makeDecoder(bufferIn, sizeIn);
+
+ // Queue the stream
+ _as->queueAudioStream(adpcm);
+}
+
+void AppendableSound::finish() {
+ if (!_as)
+ error("AppendableSound::queueBuffer - internal error: the audio stream is invalid!");
+
+ if (!_finished)
+ _as->finish();
+
+ _finished = true;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/snd.h b/engines/lastexpress/data/snd.h
new file mode 100644
index 0000000000..2e0bc8c1b0
--- /dev/null
+++ b/engines/lastexpress/data/snd.h
@@ -0,0 +1,97 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SND_H
+#define LASTEXPRESS_SND_H
+
+/*
+ Sound format (.SND / .LNK)
+
+ uint32 {4} - data size
+ uint16 {2} - number of blocks
+
+ // for each block
+ int16 {2} - initial sample
+ byte {1} - initial index
+ byte {1} - unused (00)
+ byte {x} - IMA ADPCM sample codes
+*/
+
+#include "common/stream.h"
+#include "sound/mixer.h"
+
+namespace Audio {
+ class AudioStream;
+ class QueuingAudioStream;
+}
+
+namespace LastExpress {
+
+class SimpleSound {
+public:
+ SimpleSound();
+ virtual ~SimpleSound();
+
+ void stop() const;
+
+protected:
+ void loadHeader(Common::SeekableReadStream *in);
+ Audio::AudioStream *makeDecoder(Common::SeekableReadStream *in, uint32 size) const;
+ void play(Audio::AudioStream *as);
+
+ uint32 _size; ///< data size
+ ///< - NIS: size of all blocks, including those located in the matching LNK file
+ ///< - LNK: size of the LNK file itself, including the header
+ ///< - SND: size of all blocks
+ uint16 _blocks; ///< number of blocks
+ uint32 _blockSize;
+ Audio::SoundHandle _handle;
+};
+
+class StreamedSound : public SimpleSound {
+public:
+ StreamedSound();
+ ~StreamedSound();
+
+ bool load(Common::SeekableReadStream *stream);
+};
+
+class AppendableSound : public SimpleSound {
+public:
+ AppendableSound();
+ ~AppendableSound();
+
+ void queueBuffer(const byte *data, uint32 size);
+ void queueBuffer(Common::SeekableReadStream *bufferIn);
+ void finish();
+
+private:
+ Audio::QueuingAudioStream *_as;
+ bool _finished;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SND_H
diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp
new file mode 100644
index 0000000000..fb7d4ec6fa
--- /dev/null
+++ b/engines/lastexpress/data/subtitle.cpp
@@ -0,0 +1,243 @@
+/* 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$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SBE
+
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Subtitle
+//////////////////////////////////////////////////////////////////////////
+class Subtitle {
+public:
+ Subtitle() : _timeStart(0), _timeStop(0), _topLength(0), _topText(NULL),
+ _bottomLength(0), _bottomText(NULL) {}
+ ~Subtitle() { reset(); }
+
+ bool load(Common::SeekableReadStream *in);
+ Common::Rect draw(Graphics::Surface *surface, Font *font);
+
+ uint16 getTimeStart() const { return _timeStart; }
+ uint16 getTimeStop() const { return _timeStop; }
+
+private:
+ uint16 _timeStart; ///< display start time
+ uint16 _timeStop; ///< display stop time
+
+ uint16 _topLength; ///< top line length
+ uint16 *_topText; ///< bottom line length
+
+ uint16 _bottomLength; ///< top line (UTF-16 string)
+ uint16 *_bottomText; ///< bottom line (UTF-16 string)
+
+ void reset();
+};
+
+void Subtitle::reset() {
+ delete[] _topText;
+ delete[] _bottomText;
+ _topText = NULL;
+ _bottomText = NULL;
+}
+
+template<typename T>
+T *newArray(size_t n)
+{
+ if (n <= (size_t)-1 / sizeof(T))
+ return new T[n];
+
+ // n is too large
+ return NULL;
+}
+
+bool Subtitle::load(Common::SeekableReadStream *in) {
+ reset();
+
+ if (!in)
+ return false;
+
+ // Read the display times
+ _timeStart = in->readUint16LE();
+ _timeStop = in->readUint16LE();
+
+ // Read the text lengths
+ _topLength = in->readUint16LE();
+ _bottomLength = in->readUint16LE();
+
+ // Create the buffers
+ if (_topLength) {
+ _topText = newArray<uint16>(_topLength);
+ if (!_topText)
+ return false;
+ }
+ if (_bottomLength) {
+ _bottomText = newArray<uint16>(_bottomLength);
+ if (!_bottomText)
+ return false;
+ }
+
+ // Read the texts
+ for (int i = 0; i < _topLength; i++)
+ _topText[i] = in->readUint16LE();
+ for (int i = 0; i < _bottomLength; i++)
+ _bottomText[i] = in->readUint16LE();
+
+ debugC(9, kLastExpressDebugSubtitle, " %d -> %d:", _timeStart, _timeStop);
+ if (_topLength)
+ debugC(9, kLastExpressDebugSubtitle, "\t%ls", (wchar_t *)_topText);
+ if (_bottomLength)
+ debugC(9, kLastExpressDebugSubtitle, "\t%ls", (wchar_t *)_bottomText);
+
+ return true;
+}
+
+Common::Rect Subtitle::draw(Graphics::Surface *surface, Font *font) {
+ Common::Rect rectTop, rectBottom;
+
+ //FIXME find out proper subtitles coordinates (and hope it's hardcoded and not stored in the sequence or animation)
+ rectTop = font->drawString(surface, 100, 100, _topText, _topLength);
+ rectBottom = font->drawString(surface, 100, 300, _bottomText, _bottomLength);
+
+ rectTop.extend(rectBottom);
+
+ return rectTop;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SubtitleManager
+//////////////////////////////////////////////////////////////////////////
+SubtitleManager::SubtitleManager(Font *font) : _font(font), _maxTime(0), _currentIndex(-1), _lastIndex(-1) {}
+
+SubtitleManager::~SubtitleManager() {
+ reset();
+
+ _font = NULL;
+}
+
+void SubtitleManager::reset() {
+ for (int i = 0; i < (int)_subtitles.size(); i++)
+ delete _subtitles[i];
+
+ _subtitles.clear();
+ _currentIndex = -1;
+ _lastIndex = -1;
+
+ // Zero passed pointers
+ _font = NULL;
+}
+
+bool SubtitleManager::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ reset();
+
+ // Read header to get the number of subtitles
+ uint32 numSubtitles = stream->readUint16LE();
+ if (stream->eos())
+ error("Cannot read from subtitle file");
+
+ debugC(3, kLastExpressDebugSubtitle, "Number of subtitles in file: %d", numSubtitles);
+
+ // TODO: Check that stream contain enough data
+ //if (stream->size() < (signed)(numSubtitles * sizeof(SubtitleData))) {
+ //debugC(2, kLastExpressDebugSubtitle, "Subtitle file does not contain valid data!");
+ //return false;
+ //}
+
+ // Read the list of subtitles
+ _maxTime = 0;
+ for (uint i = 0; i < numSubtitles; i++) {
+ Subtitle *subtitle = new Subtitle();
+ if (!subtitle->load(stream)) {
+ // Failed to read this line
+ reset();
+
+ delete subtitle;
+
+ return false;
+ }
+
+ // Update the max time
+ if (subtitle->getTimeStop() > _maxTime)
+ _maxTime = subtitle->getTimeStop();
+
+ _subtitles.push_back(subtitle);
+ }
+
+ delete stream;
+
+ return true;
+}
+
+uint16 SubtitleManager::getMaxTime() const {
+ return _maxTime;
+}
+
+void SubtitleManager::setTime(uint16 time) {
+ _currentIndex = -1;
+
+ // Find the appropriate line to show
+ for (int16 i = 0; i < (int16)_subtitles.size(); i++) {
+ if ((time >= _subtitles[i]->getTimeStart()) && (time <= _subtitles[i]->getTimeStop())) {
+ // Keep the index of the line to show
+ _currentIndex = i;
+ return;
+ }
+ }
+}
+
+bool SubtitleManager::hasChanged() const {
+ // TODO: mark the old line rect as dirty
+ if (_currentIndex != _lastIndex)
+ return true;
+ else
+ return false;
+}
+
+Common::Rect SubtitleManager::draw(Graphics::Surface *surface) {
+ // Update the last drawn index
+ _lastIndex = _currentIndex;
+
+ // Return if we don't have to draw any line
+ if (_currentIndex == -1)
+ return Common::Rect();
+
+ // Draw the current line
+ assert(_currentIndex >= 0 && _currentIndex < (int16)_subtitles.size());
+ return _subtitles[_currentIndex]->draw(surface, _font);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/subtitle.h b/engines/lastexpress/data/subtitle.h
new file mode 100644
index 0000000000..9acb7068f1
--- /dev/null
+++ b/engines/lastexpress/data/subtitle.h
@@ -0,0 +1,79 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SUBTITLE_H
+#define LASTEXPRESS_SUBTITLE_H
+
+/*
+ Subtitle format (.SBE)
+
+ uint16 {2} - number of subtitles
+
+ // for each subtitle
+ uint16 {2} - display start time
+ uint16 {2} - display stop time
+ uint16 {2} - top line length
+ uint16 {2} - bottom line length
+ byte {x} - top line (UTF-16 string)
+ byte {x} - bottom line (UTF-16 string)
+
+ Subtitles seem to be drawn on screen at (80, 420) x (560, 458)
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Font;
+class Subtitle;
+
+class SubtitleManager : public Drawable {
+public:
+ SubtitleManager(Font *font);
+ ~SubtitleManager();
+
+ bool load(Common::SeekableReadStream *stream);
+ uint16 getMaxTime() const;
+ void setTime(uint16 time);
+ bool hasChanged() const;
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ Common::Array<Subtitle *> _subtitles;
+ Font *_font;
+ uint16 _maxTime;
+
+ int16 _currentIndex;
+ int16 _lastIndex;
+
+ void reset();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SUBTITLE_H
diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp
new file mode 100644
index 0000000000..cf81e162ae
--- /dev/null
+++ b/engines/lastexpress/debug.cpp
@@ -0,0 +1,1091 @@
+/* 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/debug.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/background.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.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"
+
+#include "common/debug-channels.h"
+#include "common/events.h"
+
+namespace LastExpress {
+
+Debugger::Debugger(LastExpressEngine *engine) : _engine(engine), _command(NULL), _numParams(0), _commandParams(NULL) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Register the debugger commands
+
+ // General
+ DCmd_Register("help", WRAP_METHOD(Debugger, cmdHelp));
+
+ // Data
+ DCmd_Register("ls", WRAP_METHOD(Debugger, cmdListFiles));
+
+ DCmd_Register("showframe", WRAP_METHOD(Debugger, cmdShowFrame));
+ DCmd_Register("showbg", WRAP_METHOD(Debugger, cmdShowBg));
+ DCmd_Register("playseq", WRAP_METHOD(Debugger, cmdPlaySeq));
+ DCmd_Register("playsnd", WRAP_METHOD(Debugger, cmdPlaySnd));
+ DCmd_Register("playsbe", WRAP_METHOD(Debugger, cmdPlaySbe));
+ DCmd_Register("playnis", WRAP_METHOD(Debugger, cmdPlayNis));
+
+ // Scene & interaction
+ DCmd_Register("loadscene", WRAP_METHOD(Debugger, cmdLoadScene));
+ DCmd_Register("fight", WRAP_METHOD(Debugger, cmdFight));
+ DCmd_Register("beetle", WRAP_METHOD(Debugger, cmdBeetle));
+
+ // Game
+ DCmd_Register("delta", WRAP_METHOD(Debugger, cmdTimeDelta));
+ DCmd_Register("dump", WRAP_METHOD(Debugger, cmdDump));
+ DCmd_Register("entity", WRAP_METHOD(Debugger, cmdEntity));
+
+ // Misc
+ DCmd_Register("loadgame", WRAP_METHOD(Debugger, cmdLoadGame));
+ DCmd_Register("chapter", WRAP_METHOD(Debugger, cmdSwitchChapter));
+ DCmd_Register("clear", WRAP_METHOD(Debugger, cmdClear));
+
+ resetCommand();
+
+ _soundStream = new StreamedSound();
+}
+
+Debugger::~Debugger() {
+ DebugMan.clearAllDebugChannels();
+
+ delete _soundStream;
+
+ // Zero passed pointers
+ _engine = NULL;
+ _command = NULL;
+ _commandParams = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helper functions
+//////////////////////////////////////////////////////////////////////////
+bool Debugger::hasCommand() const {
+ return (_numParams != 0);
+}
+
+void Debugger::resetCommand() {
+ _command = NULL;
+ _commandParams = NULL;
+ _numParams = 0;
+}
+
+int Debugger::getNumber(const char *arg) const {
+ return strtol(arg, (char **)NULL, 0);
+}
+
+void Debugger::copyCommand(int argc, const char **argv) {
+ _commandParams = (char **)malloc((uint)argc);
+ if (!_commandParams)
+ return;
+
+ _numParams = argc;
+
+ for (int i = 0; i < _numParams; i++) {
+ _commandParams[i] = (char *)malloc(strlen(argv[i]));
+ strcpy(_commandParams[i], "");
+ strcpy(_commandParams[i], argv[i]);
+ }
+
+ // Exit the debugger!
+ Cmd_Exit(0, 0);
+}
+
+void Debugger::callCommand() {
+ if (_command)
+ (*_command)(_numParams, const_cast<const char **>(_commandParams));
+}
+
+void Debugger::loadArchive(ArchiveIndex index) const {
+ _engine->getResourceManager()->loadArchive(index);
+ getScenes()->loadSceneDataFile(index);
+}
+
+// Restore loaded archive
+void Debugger::restoreArchive() const {
+
+ ArchiveIndex index = kArchiveCd1;
+
+ switch (getProgress().chapter) {
+ default:
+ case kChapter1:
+ index = kArchiveCd1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ index = kArchiveCd2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ index = kArchiveCd3;
+ break;
+ }
+
+ _engine->getResourceManager()->loadArchive(index);
+ getScenes()->loadSceneDataFile(index);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Debugger commands
+//////////////////////////////////////////////////////////////////////////
+bool Debugger::cmdHelp(int, const char **) {
+ DebugPrintf("Debug flags\n");
+ DebugPrintf("-----------\n");
+ DebugPrintf(" debugflag_list - Lists the available debug flags and their status\n");
+ DebugPrintf(" debugflag_enable - Enables a debug flag\n");
+ DebugPrintf(" debugflag_disable - Disables a debug flag\n");
+ DebugPrintf("\n");
+ DebugPrintf("Commands\n");
+ DebugPrintf("--------\n");
+ DebugPrintf(" ls - list files in the archive\n");
+ DebugPrintf("\n");
+ DebugPrintf(" showframe - show a frame from a sequence\n");
+ DebugPrintf(" showbg - show a background\n");
+ DebugPrintf(" playseq - play a sequence\n");
+ DebugPrintf(" playsnd - play a sound\n");
+ DebugPrintf(" playsbe - play a subtitle\n");
+ DebugPrintf(" playnis - play an animation\n");
+ DebugPrintf("\n");
+ DebugPrintf(" loadscene - load a scene\n");
+ DebugPrintf(" fight - start a fight\n");
+ DebugPrintf(" beetle - start the beetle game\n");
+ DebugPrintf("\n");
+ DebugPrintf(" delta - Adjust the time delta\n");
+ DebugPrintf(" dump - Dump game data\n");
+ DebugPrintf(" entity - Dump entity data\n");
+ DebugPrintf("\n");
+ DebugPrintf(" loadgame - load a saved game\n");
+ DebugPrintf(" chapter - switch to a specific chapter\n");
+ DebugPrintf(" clear - clear the screen\n");
+ DebugPrintf("\n");
+ return true;
+}
+
+/**
+ * Command: list files in archive
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdListFiles(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filter(const_cast<char *>(argv[1]));
+
+ // Load the proper archive
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ Common::ArchiveMemberList list;
+ int count = _engine->getResourceManager()->listMatchingMembers(list, filter);
+
+ DebugPrintf("Number of matches: %d\n", count);
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it)
+ DebugPrintf(" %s\n", (*it)->getName().c_str());
+
+ // Restore archive
+ if (argc == 3)
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: ls <filter> (use * for all)\n (<cd number>)");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Shows a frame
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShowFrame(int argc, const char **argv) {
+ if (argc == 3 || argc == 4) {
+ Common::String filename(const_cast<char *>(argv[1]));
+ filename += ".seq";
+
+ if (argc == 4)
+ loadArchive((ArchiveIndex)getNumber(argv[3]));
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdShowFrame);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ Sequence sequence(filename);
+ if (sequence.load(getArchive(filename))) {
+ _engine->getCursor()->show(false);
+ clearBg(GraphicsManager::kBackgroundOverlay);
+
+ AnimFrame *frame = sequence.getFrame((uint16)getNumber(argv[2]));
+ if (!frame) {
+ DebugPrintf("Invalid frame index: %i\n", filename.c_str());
+ resetCommand();
+ return true;
+ }
+
+ _engine->getGraphicsManager()->draw(frame, GraphicsManager::kBackgroundOverlay);
+ delete frame;
+
+ askForRedraw();
+ redrawScreen();
+
+ _engine->_system->delayMillis(1000);
+ _engine->getCursor()->show(true);
+ }
+
+ resetCommand();
+
+ if (argc == 4)
+ restoreArchive();
+ }
+ } else {
+ DebugPrintf("Syntax: cmd_showframe <seqname> <index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: shows a background
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShowBg(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (!_engine->getResourceManager()->hasFile(filename + ".BG")) {
+ DebugPrintf("Cannot find file: %s\n", (filename + ".BG").c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdShowBg);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ clearBg(GraphicsManager::kBackgroundC);
+
+ Background *background = _engine->getResourceManager()->loadBackground(filename);
+ if (background) {
+ _engine->getGraphicsManager()->draw(background, GraphicsManager::kBackgroundC);
+ delete background;
+ askForRedraw();
+ }
+
+ redrawScreen();
+
+ if (argc == 3)
+ restoreArchive();
+
+ // Pause for a second to be able to see the background
+ _engine->_system->delayMillis(1000);
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: showbg <bgname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a sequence.
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySeq(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+ filename += ".seq";
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlaySeq);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ Sequence *sequence = new Sequence(filename);
+ if (sequence->load(getArchive(filename))) {
+
+ // Check that we have at least a frame to show
+ if (sequence->count() == 0) {
+ delete sequence;
+ return false;
+ }
+
+ _engine->getCursor()->show(false);
+
+ SequenceFrame player(sequence, 0, true);
+ do {
+ // Clear screen
+ clearBg(GraphicsManager::kBackgroundA);
+
+ _engine->getGraphicsManager()->draw(&player, GraphicsManager::kBackgroundA);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Handle right-click to interrupt sequence
+ Common::Event ev;
+ _engine->getEventManager()->pollEvent(ev);
+ if (ev.type == Common::EVENT_RBUTTONUP)
+ break;
+
+ _engine->_system->delayMillis(175);
+
+ // go to the next frame
+ } while (player.nextFrame());
+ _engine->getCursor()->show(true);
+ } else {
+ // Sequence player is deleting his reference to the sequence, but we need to take care of it if the
+ // sequence could not be loaded
+ delete sequence;
+ }
+
+ resetCommand();
+
+ if (argc == 3)
+ restoreArchive();
+ }
+ } else {
+ DebugPrintf("Syntax: playseq <seqname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a sound
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySnd(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ // Add .SND at the end of the filename if needed
+ Common::String name(const_cast<char *>(argv[1]));
+ if (!name.contains('.'))
+ name += ".SND";
+
+ if (!_engine->getResourceManager()->hasFile(name)) {
+ DebugPrintf("Cannot find file: %s\n", name.c_str());
+ return true;
+ }
+
+ _engine->_system->getMixer()->stopAll();
+
+ _soundStream->load(getArchive(name));
+
+ if (argc == 3)
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: playsnd <sndname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays subtitles
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySbe(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ filename += ".sbe";
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlaySbe);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ SubtitleManager subtitle(_engine->getFont());
+ if (subtitle.load(getArchive(filename))) {
+ _engine->getCursor()->show(false);
+ for (uint16 i = 0; i < subtitle.getMaxTime(); i += 25) {
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ subtitle.setTime(i);
+ _engine->getGraphicsManager()->draw(&subtitle, GraphicsManager::kBackgroundOverlay);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Handle right-click to interrupt sequence
+ Common::Event ev;
+ _engine->getEventManager()->pollEvent(ev);
+ if (ev.type == Common::EVENT_RBUTTONUP)
+ break;
+
+ _engine->_system->delayMillis(500);
+ }
+ _engine->getCursor()->show(true);
+ }
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: playsbe <sbename> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a NIS animation sequence.
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlayNis(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String name(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ // If we got a nis filename, check that the file exists
+ if (name.contains('.') && _engine->getResourceManager()->hasFile(name)) {
+ DebugPrintf("Cannot find file: %s\n", name.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlayNis);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ // Make sure we are not called in a loop
+ _numParams = 0;
+
+
+ // Check if we got a nis filename or an animation index
+ if (name.contains('.')) {
+ Animation animation;
+ if (animation.load(getArchive(name))) {
+ _engine->getCursor()->show(false);
+ animation.play();
+ _engine->getCursor()->show(true);
+ }
+ } else {
+ getAction()->playAnimation((EventIndex)atoi(name.c_str()), true);
+ }
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: playnis <nisname.nis or animation index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: loads a scene
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdLoadScene(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ int cd = 1;
+ SceneIndex index = (SceneIndex)getNumber(argv[1]);;
+
+ // Check args
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (index > 2500) {
+ DebugPrintf("Error: invalid index value (0-2500)");
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdLoadScene);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ /************ DEBUG *************************/
+ // Use to find scenes with certain values
+
+ //for (int i = index; i < 2500; i++) {
+ // loadSceneObject(scene, i);
+
+ // if (scene.getHeader() && scene.getHeader()->car == 5 && scene.getHeader()->position == 81) {
+ // DebugPrintf("Found scene: %d", i);
+
+ // // Draw scene found
+ // _engine->getGraphicsManager()->draw(&scene, GraphicsManager::kBackgroundC);
+
+ // askForRedraw();
+ // redrawScreen();
+ // _engine->_system->delayMillis(500);
+
+ // break;
+ // }
+ //}
+
+ //delete _sceneLoader;
+ //resetCommand();
+ //return true;
+
+ /*********************************************/
+ Scene *scene = getScenes()->get(index);
+ if (!scene) {
+ DebugPrintf("Cannot load scene %i from CD %i", index, cd);
+ resetCommand();
+
+ return true;
+ }
+
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Pause for a second to be able to see the scene
+ _engine->_system->delayMillis(500);
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: loadscene <scene index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: starts a fight sequence
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdFight(int argc, const char **argv) {
+ if (argc == 2) {
+ FightType type = (FightType)getNumber(argv[1]);
+
+ // Load proper data file
+ ArchiveIndex index = kArchiveCd1;
+ switch (type) {
+ default:
+ goto error;
+
+ case kFightMilos:
+ index = kArchiveCd1;
+ break;
+
+ case kFightAnna:
+ index = kArchiveCd2;
+ break;
+
+ case kFightIvo:
+ case kFightSalko:
+ case kFightVesna:
+ index = kArchiveCd3;
+ break;
+ }
+
+ loadArchive(index);
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdFight);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ // Make sure we are not called in a loop
+ _numParams = 0;
+
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+
+ SceneIndex lastScene = getState()->scene;
+
+ getFight()->setup(type) ? DebugPrintf("Lost fight!\n") : DebugPrintf("Won fight!\n");
+
+ // Pause for a second to be able to see the final scene
+ _engine->_system->delayMillis(1000);
+
+ // Restore loaded archive
+ restoreArchive();
+
+ // Stop audio and restore scene
+ getSound()->stopAllSound();
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ Scene *scene = getScenes()->get(lastScene);
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ resetCommand();
+ }
+ } else {
+error:
+ DebugPrintf("Syntax: fight <id> (id=2001-2005)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: starts the beetle sequence
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdBeetle(int argc, const char **argv) {
+ if (argc == 1) {
+ // Load proper data file (beetle game in in Cd2)
+ loadArchive(kArchiveCd2);
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdBeetle);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+
+ // Save current state
+ SceneIndex previousScene = getState()->scene;
+ ObjectLocation previousLocation = getInventory()->get(kItemBeetle)->location;
+ ChapterIndex previousChapter = (ChapterIndex)getProgress().chapter;
+
+ // Setup scene & inventory
+ getProgress().chapter = kChapter2;
+ Scene *scene = getScenes()->get(kSceneBeetle);
+ getInventory()->get(kItemBeetle)->location = kObjectLocation3;
+
+ askForRedraw();
+ redrawScreen();
+
+ // Load the beetle game
+ Action *action = NULL;
+ Beetle *beetle = new Beetle(_engine);
+ if (!beetle->isLoaded())
+ beetle->load();
+
+ // Play the game
+ Common::Event ev;
+ bool playgame = true;
+ while (playgame) {
+ // Update beetle
+ beetle->update();
+
+ askForRedraw();
+ redrawScreen();
+
+ while (g_engine->getEventManager()->pollEvent(ev)) {
+
+ switch (ev.type) {
+ default:
+ break;
+
+ case Common::EVENT_KEYDOWN:
+ // Exit beetle game on escape
+ if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+ playgame = false;
+
+ break;
+
+ case Common::EVENT_MOUSEMOVE: {
+ // Update cursor
+ CursorStyle style = kCursorNormal;
+ SceneHotspot *hotspot = NULL;
+ if (scene->checkHotSpot(ev.mouse, &hotspot)) {
+ if (!action)
+ action = new Action(_engine);
+
+ style = action->getCursor(*hotspot);
+ }
+
+ _engine->getCursor()->setStyle(style);
+ break;
+ }
+
+
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ // Update coordinates
+ getLogic()->getGameState()->setCoordinates(ev.mouse);
+
+ if (beetle->catchBeetle())
+ playgame = false;
+ break;
+ }
+
+ _engine->_system->delayMillis(10);
+ }
+ }
+
+ // Cleanup
+ beetle->unload();
+ delete beetle;
+ SAFE_DELETE(action);
+
+ // Pause for a second to be able to see the final scene
+ _engine->_system->delayMillis(1000);
+
+ // Restore state
+ getProgress().chapter = previousChapter;
+ getInventory()->get(kItemBeetle)->location = previousLocation;
+
+ // Restore loaded archive
+ restoreArchive();
+
+ // Stop audio and restore scene
+ getSound()->stopAllSound();
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ Scene *oldScene = getScenes()->get(previousScene);
+ _engine->getGraphicsManager()->draw(oldScene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: beetle\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: adjusts the time delta
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdTimeDelta(int argc, const char **argv) {
+ if (argc == 2) {
+ int delta = getNumber(argv[1]);
+
+ if (delta <= 0 || delta > 500)
+ goto label_error;
+
+ getState()->timeDelta = (uint)delta;
+ } else {
+label_error:
+ DebugPrintf("Syntax: delta <time delta> (delta=1-500)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: dumps game logic data
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdDump(int argc, const char **argv) {
+#define OUTPUT_DUMP(name, text) \
+ DebugPrintf(#name "\n"); \
+ DebugPrintf("--------------------------------------------------------------------\n\n"); \
+ DebugPrintf(text); \
+ DebugPrintf("\n");
+
+ if (argc == 2) {
+ switch (getNumber(argv[1])) {
+ default:
+ goto label_error;
+
+ // GameState
+ case 1:
+ OUTPUT_DUMP("Game state", getState()->toString().c_str());
+ break;
+
+ // Inventory
+ case 2:
+ OUTPUT_DUMP("Inventory", getInventory()->toString().c_str());
+ break;
+
+ // Objects
+ case 3:
+ OUTPUT_DUMP("Objects", getObjects()->toString().c_str());
+ break;
+
+ // SavePoints
+ case 4:
+ OUTPUT_DUMP("SavePoints", getSavePoints()->toString().c_str());
+ break;
+
+ case 5:
+ OUTPUT_DUMP("Current scene", getScenes()->get(getState()->scene)->toString().c_str());
+ }
+ } else {
+label_error:
+ DebugPrintf("Syntax: state <option>\n");
+ DebugPrintf(" 1 : Game state\n");
+ DebugPrintf(" 2 : Inventory\n");
+ DebugPrintf(" 3 : Objects\n");
+ DebugPrintf(" 4 : SavePoints\n");
+ DebugPrintf(" 5 : Current scene\n");
+ }
+
+ return true;
+
+#undef OUTPUT_DUMP
+}
+
+/**
+ * Command: shows entity data
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdEntity(int argc, const char **argv) {
+ if (argc == 2) {
+ EntityIndex index = (EntityIndex)getNumber(argv[1]);
+
+ if (index > 39)
+ goto label_error;
+
+ DebugPrintf("Entity %s\n", ENTITY_NAME(index));
+ DebugPrintf("--------------------------------------------------------------------\n\n");
+ DebugPrintf(getEntities()->getData(index)->toString().c_str());
+
+ // The Player entity does not have any callback data
+ if (index != kEntityPlayer) {
+ EntityData *data = getEntities()->get(index)->getParamData();
+ for (uint callback = 0; callback < 9; callback++) {
+ DebugPrintf("Call parameters %d:\n", callback);
+ for (byte ix = 0; ix < 4; ix++)
+ DebugPrintf(" %s", data->getParameters(callback, ix)->toString().c_str());
+ }
+ }
+
+ DebugPrintf("\n");
+ } else {
+label_error:
+ DebugPrintf("Syntax: entity <index>\n");
+ for (int i = 0; i < 40; i += 4)
+ DebugPrintf(" %s - %d %s - %d %s - %d %s - %d\n", ENTITY_NAME(i), i, ENTITY_NAME(i+1), i+1, ENTITY_NAME(i+2), i+2, ENTITY_NAME(i+3), i+3);
+ }
+
+ return true;
+}
+
+/**
+ * Command: loads a game
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdLoadGame(int argc, const char **argv) {
+ if (argc == 2) {
+ int id = getNumber(argv[1]);
+
+ if (id == 0 || id > 6)
+ goto error;
+
+ if (!getSaveLoad()->loadGame((GameId)(id - 1)))
+ DebugPrintf("Error loading game with id=%d", id);
+
+ } else {
+error:
+ DebugPrintf("Syntax: loadgame <id> (id=1-6)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: switch to a specific chapter
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdSwitchChapter(int argc, const char **argv) {
+ if (argc == 2) {
+ int id = getNumber(argv[1]);
+
+ if (id <= 1 || id > 6)
+ goto error;
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdSwitchChapter);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ // Sets the current chapter and then call Logic::switchChapter to proceed to the next chapter
+ getState()->progress.chapter = (ChapterIndex)(id - 1);
+
+ getLogic()->switchChapter();
+
+ resetCommand();
+ }
+ } else {
+error:
+ DebugPrintf("Syntax: chapter <id> (id=2-6)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: clears the screen
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdClear(int argc, const char **) {
+ if (argc == 1) {
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+ } else {
+ DebugPrintf("Syntax: clear - clear the screen\n");
+ }
+
+ return true;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/debug.h b/engines/lastexpress/debug.h
new file mode 100644
index 0000000000..bb13c76bd0
--- /dev/null
+++ b/engines/lastexpress/debug.h
@@ -0,0 +1,104 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_DEBUG_H
+#define LASTEXPRESS_DEBUG_H
+
+#include "gui/debugger.h"
+
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/shared.h"
+
+namespace LastExpress {
+
+enum {
+ kLastExpressDebugAll = 1 << 0,
+ kLastExpressDebugGraphics = 1 << 1,
+ kLastExpressDebugResource = 1 << 2,
+ kLastExpressDebugCursor = 1 << 3,
+ kLastExpressDebugSound = 1 << 4,
+ kLastExpressDebugSubtitle = 1 << 5,
+ kLastExpressDebugSavegame = 1 << 6,
+ kLastExpressDebugLogic = 1 << 7,
+ kLastExpressDebugScenes = 1 << 8,
+ kLastExpressDebugUnknown = 1 << 9
+ // the current limitation is 32 debug levels (1 << 31 is the last one)
+};
+
+class LastExpressEngine;
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger(LastExpressEngine *engine);
+ ~Debugger();
+
+ bool hasCommand() const;
+ void callCommand();
+
+private:
+ LastExpressEngine *_engine;
+
+ bool cmdHelp(int argc, const char **argv);
+
+ bool cmdListFiles(int argc, const char **argv);
+
+ bool cmdShowFrame(int argc, const char **argv);
+ bool cmdShowBg(int argc, const char **argv);
+ bool cmdPlaySeq(int argc, const char **argv);
+ bool cmdPlaySnd(int argc, const char **argv);
+ bool cmdPlaySbe(int argc, const char **argv);
+ bool cmdPlayNis(int argc, const char **argv);
+
+ bool cmdLoadScene(int argc, const char **argv);
+ bool cmdFight(int argc, const char **argv);
+ bool cmdBeetle(int argc, const char **argv);
+
+ bool cmdTimeDelta(int argc, const char **argv);
+ bool cmdDump(int argc, const char **argv);
+ bool cmdEntity(int argc, const char **argv);
+
+ bool cmdLoadGame(int argc, const char **argv);
+ bool cmdSwitchChapter(int argc, const char **argv);
+ bool cmdClear(int argc, const char **argv);
+
+ void resetCommand();
+ void copyCommand(int argc, const char **argv);
+ int getNumber(const char *arg) const;
+
+ void loadArchive(ArchiveIndex index) const;
+ void restoreArchive() const;
+
+ Debuglet *_command;
+ int _numParams;
+ char **_commandParams;
+
+ // Special sound stream for playing sounds
+ StreamedSound *_soundStream;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_DEBUG_H
diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp
new file mode 100644
index 0000000000..1a0bebc881
--- /dev/null
+++ b/engines/lastexpress/detection.cpp
@@ -0,0 +1,171 @@
+/* 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/lastexpress.h"
+
+namespace LastExpress {
+
+static const PlainGameDescriptor lastExpressGames[] = {
+ // Games
+ {"lastexpress", "The Last Express"},
+ {0, 0}
+};
+
+static const ADGameDescription gameDescriptions[] = {
+
+ // The Last Express (English)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "2d331459e0e68cf277ef4e4043750413", 29865984},
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944},
+ {"CD2.HPF", 0, "58aa26e782d10ec5d2231e539d2fe6a2", 669581312},
+ {"CD3.HPF", 0, "00554fbf78a2ad391d98578fbbbe1c48", 641128448},
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (English) - Interplay Release
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "bcc32d977f92bb52c060a0b4e8589cac", 30715904},
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944},
+ {"CD2.HPF", 0, "58aa26e782d10ec5d2231e539d2fe6a2", 669581312},
+ {"CD3.HPF", 0, "00554fbf78a2ad391d98578fbbbe1c48", 641128448},
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (French)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "c14c6d685d9bf8705d9f659062e6c5c2", 29505536},
+ {"CD1.HPF", 0, "b4277b22bc5cd6ad3b00c2ec04d4645d", 522924032},
+ {"CD2.HPF", 0, "8c9610aa4cb707ab51f61c30feb22c1a", 665710592},
+ {"CD3.HPF", 0, "411c1bab57b3e8da4fb359c5b40ef5d7", 640884736},
+ },
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (Spanish)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "46bed8832f06cf7160883a2aae2d667f", 29657088},
+ {"CD1.HPF", 0, "367a3a8581f6f88ddc51af7cde105ba9", 519927808},
+ {"CD2.HPF", 0, "af5566df3000472852ec182c9ec57797", 662210560},
+ {"CD3.HPF", 0, "0d1901662f4d063a5c250c9fbf64b771", 639504384},
+ },
+ Common::ES_ESP,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (Demo)
+ {
+ "lastexpress",
+ "Demo",
+ {
+ {"Demo.HPF", 0, "baf3b1f64155d34872896e61c3d3cb78", 58191872},
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ AD_TABLE_END_MARKER
+};
+
+static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)gameDescriptions,
+ // Size of that superset structure
+ sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ lastExpressGames,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ "lastexpress",
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0,
+ // Additional GUI options (for every game}
+ Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
+};
+
+
+class LastExpressMetaEngine : public AdvancedMetaEngine {
+public:
+ LastExpressMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ const char *getName() const {
+ return "LastExpress Engine";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "LastExpress Engine (C) 1997 Smoking Car Productions";
+ }
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+};
+
+bool LastExpressMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (gd) {
+ *engine = new LastExpressEngine(syst, (const ADGameDescription *)gd);
+ }
+ return gd != 0;
+}
+
+} // End of namespace LastExpress
+
+#if PLUGIN_ENABLED_DYNAMIC(LASTEXPRESS)
+ REGISTER_PLUGIN_DYNAMIC(LASTEXPRESS, PLUGIN_TYPE_ENGINE, LastExpress::LastExpressMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(LASTEXPRESS, PLUGIN_TYPE_ENGINE, LastExpress::LastExpressMetaEngine);
+#endif
diff --git a/engines/lastexpress/drawable.h b/engines/lastexpress/drawable.h
new file mode 100644
index 0000000000..e273876362
--- /dev/null
+++ b/engines/lastexpress/drawable.h
@@ -0,0 +1,42 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_DRAWABLE_H
+#define LASTEXPRESS_DRAWABLE_H
+
+#include "graphics/surface.h"
+
+namespace LastExpress {
+
+class Drawable {
+public:
+ virtual ~Drawable() {}
+
+ virtual Common::Rect draw(Graphics::Surface *surface) = 0;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_DRAWABLE_H
diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp
new file mode 100644
index 0000000000..d46bb325be
--- /dev/null
+++ b/engines/lastexpress/entities/abbot.cpp
@@ -0,0 +1,1910 @@
+/* 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/entities/abbot.h"
+
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Abbot::Abbot(LastExpressEngine *engine) : Entity(engine, kEntityAbbot) {
+ ADD_CALLBACK_FUNCTION(Abbot, reset);
+ ADD_CALLBACK_FUNCTION(Abbot, draw);
+ ADD_CALLBACK_FUNCTION(Abbot, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Abbot, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Abbot, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Abbot, draw2);
+ ADD_CALLBACK_FUNCTION(Abbot, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Abbot, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Abbot, playSound);
+ ADD_CALLBACK_FUNCTION(Abbot, savegame);
+ ADD_CALLBACK_FUNCTION(Abbot, updateEntity);
+ ADD_CALLBACK_FUNCTION(Abbot, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Abbot, updatePosition);
+ ADD_CALLBACK_FUNCTION(Abbot, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter1);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter2);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter3);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function19);
+ ADD_CALLBACK_FUNCTION(Abbot, function20);
+ ADD_CALLBACK_FUNCTION(Abbot, function21);
+ ADD_CALLBACK_FUNCTION(Abbot, function22);
+ ADD_CALLBACK_FUNCTION(Abbot, function23);
+ ADD_CALLBACK_FUNCTION(Abbot, function24);
+ ADD_CALLBACK_FUNCTION(Abbot, function25);
+ ADD_CALLBACK_FUNCTION(Abbot, function26);
+ ADD_CALLBACK_FUNCTION(Abbot, function27);
+ ADD_CALLBACK_FUNCTION(Abbot, function28);
+ ADD_CALLBACK_FUNCTION(Abbot, function29);
+ ADD_CALLBACK_FUNCTION(Abbot, function30);
+ ADD_CALLBACK_FUNCTION(Abbot, function31);
+ ADD_CALLBACK_FUNCTION(Abbot, function32);
+ ADD_CALLBACK_FUNCTION(Abbot, function33);
+ ADD_CALLBACK_FUNCTION(Abbot, function34);
+ ADD_CALLBACK_FUNCTION(Abbot, function35);
+ ADD_CALLBACK_FUNCTION(Abbot, function36);
+ ADD_CALLBACK_FUNCTION(Abbot, function37);
+ ADD_CALLBACK_FUNCTION(Abbot, function38);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter4);
+ ADD_CALLBACK_FUNCTION(Abbot, function40);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function42);
+ ADD_CALLBACK_FUNCTION(Abbot, function43);
+ ADD_CALLBACK_FUNCTION(Abbot, function44);
+ ADD_CALLBACK_FUNCTION(Abbot, function45);
+ ADD_CALLBACK_FUNCTION(Abbot, function46);
+ ADD_CALLBACK_FUNCTION(Abbot, drinkAfterDefuse);
+ ADD_CALLBACK_FUNCTION(Abbot, function48);
+ ADD_CALLBACK_FUNCTION(Abbot, pickBomb);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter5);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function52);
+ ADD_CALLBACK_FUNCTION(Abbot, function53);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Abbot, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Abbot, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Abbot, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Abbot, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarRedSleeping, kObjectCompartmentC, true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Abbot, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(6, Abbot, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(7, Abbot, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(8, Abbot, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(9, Abbot, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Abbot, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(11, Abbot, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 18) || getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ getSound()->excuseMe(kEntityAbbot);
+ } else {
+ if (getEvent(kEventAbbotIntroduction))
+ getSound()->playSound(kEntityPlayer, "CAT1013");
+ else
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(12, Abbot, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(13, Abbot, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Abbot, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Abbot, chapter1)
+ if (savepoint.action == kActionDefault)
+ getSavePoints()->addData(kEntityAbbot, kAction203073664, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Abbot, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityAbbot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Abbot, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ getData()->clothes = kClothesDefault;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("804DD");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAbbot, kEntityCooks, kAction236976550);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(5);
+ setup_enterExitCompartment("617AC", kObjectCompartmentC);
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction192054567:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Abbot, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1953000) {
+ if (!params->param1) {
+ params->param1 = 1;
+ setCallback(3);
+ setup_playSound("MrB3010");
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setCallback(1);
+ setup_playSound("Abb3010");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateFromTime(900);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ setup_function20();
+ break;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Abbot, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1966500 && getEntities()->isInRestaurant(kEntityBoutarel))
+ setup_function21();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "509A");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Abbot, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("509B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Mc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("804US");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceRight(kEntityAbbot, "029J");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029H");
+ getSavePoints()->push(kEntityAbbot, kEntityPascale, kAction207769280);
+ break;
+
+ case 7:
+ setup_function22();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getSavePoints()->push(kEntityAbbot, kEntityTables4, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(7);
+ setup_draw("029B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Abbot, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752);
+
+ if (getState()->time > kTime1989000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function23();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotIntroduction);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ if (!getEvent(kEventAbbotIntroduction))
+ getData()->inventoryItem = (InventoryItem)kCursorProcess;
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getAction()->playAnimation(kEventAbbotIntroduction);
+ getSound()->playSound(kEntityPlayer, "LIB036");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Abbot, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_callSavepoint("029F", kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction238936000);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Abbot, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 900);
+
+ setup_function25();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.action == kActionKnock) {
+ setCallback(1);
+ setup_playSound("LIB012");
+ } else {
+ setCallback(2);
+ setup_playSound("LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("Abb3001");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Abbot, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("617Dc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Abbot, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon())
+ setup_function27();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityKronos, kAction157159392);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ break;
+
+ case kAction101169422:
+ params->param1 = 1;
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Abbot, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("115C", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Abbot, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2052000, params->param1, 1, setup_function29);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+
+ setCallback(1);
+ setup_playSound("abb3013");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case kAction222609266:
+ setup_function30();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Abbot, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTicks(450);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(225);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(7);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Abbot, function30)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("Abb3030");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function31();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Abbot, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+ if (getState()->time < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = getState()->time + 450;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+ }
+ }
+
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param5, getState()->time, 450);
+
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDefault:
+ params->param2 = getState()->time + 4500;
+ params->param3 = getState()->time + 18000;
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("115E", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122358304);
+ getSound()->playSound(kEntityAbbot, "Abb3020");
+
+ setCallback(3);
+ setup_updatePosition("125A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "125B");
+
+ setCallback(4);
+ setup_playSound("Abb3021");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAbbot, "Abb3023");
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 52);
+
+ setCallback(5);
+ setup_draw2("125C1", "125C2", kEntityAlexei);
+ break;
+
+ case 5:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 52);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "125D");
+ getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122288808);
+ params->param1 = 1;
+
+ UPDATE_PARAM(params->param5, getState()->time, 450);
+
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updatePosition("125E", kCarRestaurant, 52);
+ break;
+
+ case 7:
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Abbot, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setup_function33();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Abbot, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid && getState()->time > kTime2115000) {
+ if (getState()->time <= kTime2124000) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 2000) || !params->param1)
+ params->param1 = getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb3014");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case kAction123712592:
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Abbot, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("Abb3031");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ getData()->location = kLocationInsideCompartment;
+ setup_function35();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Abbot, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param2)
+ params->param2 = getState()->time + 450;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityAbbot, kEntityAugust, kAction136196244);
+
+ setCallback(1);
+ setup_updateFromTime(0);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ params->param1 = getState()->time + 9000;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityAbbot, "Abb3040", SoundManager::kFlagInvalid, 45);
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 57);
+
+ setCallback(3);
+ setup_callSavepoint("121A", kEntityAugust, kAction122358304, "BOGUS");
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 57);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function36();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Abbot, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ switch (params->param2) {
+ default:
+ break;
+
+ case 1:
+ if (params->param3 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = getState()->time + 675;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ getSound()->playSound(kEntityAbbot, "Abb3041");
+ break;
+
+ case 2:
+ UPDATE_PARAM(params->param4, getState()->time, 900);
+
+ getSound()->playSound(kEntityAbbot, "Abb3042");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAbbot, "Abb3043");
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 57);
+
+ setCallback(1);
+ setup_callSavepoint("121D", kEntityAugust, kAction122288808, "BOGUS");
+ break;
+ }
+ break;
+
+ case kActionEndSound:
+ ++params->param2;
+ break;
+
+ case kActionDefault:
+ params->param1 = getState()->time + 4500;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "121B");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 57);
+ setup_function37();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Abbot, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Abbot, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+
+ setCallback(1);
+ setup_playSound("Abb3014A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Abbot, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (!getEvent(kEventAbbotInvitationDrink)
+ && getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotInvitationDrink);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAbbotInvitationDrink);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Abbot, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129);
+
+ if (getState()->time > kTime2389500 && getEntities()->isSomebodyInsideRestaurantOrSalon())
+ setup_function42();
+
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityTables4, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Abbot, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_callSavepoint("029F", kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Abbot, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+ if (getState()->time < kTime2452500) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb4002");
+ break;
+ } else {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000) || getSound()->isBuffered(kEntityBoutarel) || !params->param4)
+ params->param4 = getState()->time + 450;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb4002");
+ break;
+ }
+ }
+ }
+
+label_callback_1:
+ TIME_CHECK(kTime2466000, params->param5, setup_function44);
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 75);
+
+ params->param2 = 1;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param6 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param3) {
+ setCallback(savepoint.param.intValue == 50 ? 5 : 6);
+ setup_playSound(savepoint.param.intValue == 50 ? getSound()->justAMinuteCath() : getSound()->wrongDoorCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_playSound("Abb3001");
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorTalk, kCursorNormal);
+
+ params->param3 = 1;
+ break;
+
+ case 5:
+ case 6:
+ params->param2 = 1;
+ params->param3 = 0;
+ break;
+ }
+ break;
+
+ case kAction101687594:
+ params->param1 = 1;
+ break;
+
+ case kAction159003408:
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Abbot, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction104060776:
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Abbot, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6471;
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationOutsideCompartment;
+
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function38);
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "617Ec");
+ getEntities()->enterCompartment(kEntityAbbot, kObjectCompartmentC, true);
+
+ setCallback(1);
+ setup_playSound("Abb4010");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("617Kc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAbbot, kObjectCompartmentC, true);
+ getSavePoints()->push(kEntityAbbot, kEntityVerges, kAction125233040);
+
+ setup_function46();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Abbot, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6471;
+
+ setCallback(1);
+ setup_function40(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_drinkAfterDefuse();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Abbot, drinkAfterDefuse)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotDrinkGiveDetonator);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("126A");
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ getData()->inventoryItem = kItemBomb;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAbbotDrinkGiveDetonator);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Abbot, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1))
+ getData()->inventoryItem = kItemInvalid;
+
+ UPDATE_PARAM_PROC(params->param1, getState()->time, 1800)
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_updatePosition("126C", kCarRedSleeping, 52);
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotDrinkDefuse);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ getSavePoints()->push(kEntityAbbot, kEntityVerges, kAction125233040);
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("126A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ break;
+
+ case 4:
+ if (!getEvent(kEventAbbotDrinkDefuse) && ENTITY_PARAM(0, 1))
+ getData()->inventoryItem = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ params->param1 = 0;
+
+ TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_updatePosition("126D", kCarRestaurant, 52);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+
+ setup_function44();
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventAbbotDrinkDefuse);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction100969180);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 58);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Abbot, pickBomb)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 150);
+
+ getSavePoints()->push(kEntityAbbot, kEntityAbbot, kAction157489665);
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ case kAction157489665:
+ getSavePoints()->push(kEntityAbbot, kEntityTatiana, kAction238790488);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSavePoints()->call(kEntityAbbot, kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction125039808);
+ getObjects()->update(kObjectCompartment2, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(getObjects()->get(kObjectCompartment2).location2 < kObjectLocation2 ? kEventAbbotWrongCompartmentBed : kEventAbbotWrongCompartment);
+ getEntities()->updateEntity(kEntityAbbot, kCarRedSleeping, kPosition_6470);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromObject(kObjectCompartment2, true);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Abbot, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Abbot, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function52();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Abbot, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+ break;
+
+ case kAction135600432:
+ setup_function53();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Abbot, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getInventory()->setLocationAndProcess(kItem25, kObjectLocation1);
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction158480160);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventLocomotiveAbbotGetSomeRest);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventLocomotiveAbbotShoveling);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+
+ case kAction168646401:
+ if (!getEvent(kEventLocomotiveAbbotGetSomeRest)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAbbotGetSomeRest);
+ break;
+ }
+
+ if (!getEvent(kEventLocomotiveAbbotShoveling)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAbbotShoveling);
+ break;
+ }
+
+ getAction()->playAnimation(kEventLocomotiveAbbotShoveling);
+ getScenes()->processScene();
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h
new file mode 100644
index 0000000000..4637fbd817
--- /dev/null
+++ b/engines/lastexpress/entities/abbot.h
@@ -0,0 +1,225 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ABBOT_H
+#define LASTEXPRESS_ABBOT_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Abbot : public Entity {
+public:
+ Abbot(LastExpressEngine *engine);
+ ~Abbot() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char* sequence1, const char* sequence2, EntityIndex entity)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * ???
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function40, CarIndex car, EntityPosition position)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(function43)
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(drinkAfterDefuse)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(pickBomb)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ABBOT_H
diff --git a/engines/lastexpress/entities/alexei.cpp b/engines/lastexpress/entities/alexei.cpp
new file mode 100644
index 0000000000..30e1c4384b
--- /dev/null
+++ b/engines/lastexpress/entities/alexei.cpp
@@ -0,0 +1,1897 @@
+/* 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/entities/alexei.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Alexei::Alexei(LastExpressEngine *engine) : Entity(engine, kEntityAlexei) {
+ ADD_CALLBACK_FUNCTION(Alexei, reset);
+ ADD_CALLBACK_FUNCTION(Alexei, playSound);
+ ADD_CALLBACK_FUNCTION(Alexei, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Alexei, draw);
+ ADD_CALLBACK_FUNCTION(Alexei, updatePosition);
+ ADD_CALLBACK_FUNCTION(Alexei, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Alexei, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Alexei, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Alexei, savegame);
+ ADD_CALLBACK_FUNCTION(Alexei, updateEntity);
+ ADD_CALLBACK_FUNCTION(Alexei, draw2);
+ ADD_CALLBACK_FUNCTION(Alexei, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Alexei, function13);
+ ADD_CALLBACK_FUNCTION(Alexei, function14);
+ ADD_CALLBACK_FUNCTION(Alexei, function15);
+ ADD_CALLBACK_FUNCTION(Alexei, function16);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter1);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function19);
+ ADD_CALLBACK_FUNCTION(Alexei, function20);
+ ADD_CALLBACK_FUNCTION(Alexei, function21);
+ ADD_CALLBACK_FUNCTION(Alexei, function22);
+ ADD_CALLBACK_FUNCTION(Alexei, function23);
+ ADD_CALLBACK_FUNCTION(Alexei, function24);
+ ADD_CALLBACK_FUNCTION(Alexei, function25);
+ ADD_CALLBACK_FUNCTION(Alexei, function26);
+ ADD_CALLBACK_FUNCTION(Alexei, function27);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter2);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function30);
+ ADD_CALLBACK_FUNCTION(Alexei, function31);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter3);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function34);
+ ADD_CALLBACK_FUNCTION(Alexei, function35);
+ ADD_CALLBACK_FUNCTION(Alexei, function36);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter4);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function39);
+ ADD_CALLBACK_FUNCTION(Alexei, function40);
+ ADD_CALLBACK_FUNCTION(Alexei, function41);
+ ADD_CALLBACK_FUNCTION(Alexei, function42);
+ ADD_CALLBACK_FUNCTION(Alexei, function43);
+ ADD_CALLBACK_FUNCTION(Alexei, function44);
+ ADD_CALLBACK_FUNCTION(Alexei, function45);
+ ADD_CALLBACK_FUNCTION(Alexei, function46);
+ ADD_CALLBACK_FUNCTION(Alexei, function47);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Alexei, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Alexei, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(3, Alexei, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Alexei, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(5, Alexei, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Alexei, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Alexei, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(8, Alexei, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Alexei, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Alexei, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 18) || getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ getSound()->excuseMe(kEntityAlexei);
+ } else {
+ if (getEvent(kEventAlexeiSalonVassili) || (getEvent(kEventTatianaAskMatchSpeakRussian) && getInventory()->hasItem(kItemPassengerList))) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1012" : "CAT1012A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ }
+ // Stop execution here
+ return;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(11, Alexei, draw2)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Alexei, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Alexei, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction302614416);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602DB");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment2, true);
+ }
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_7500;
+ getEntities()->clearSequences(kEntityAlexei);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction135664192:
+ setCallback(2);
+ setup_enterExitCompartment("602Eb", kObjectCompartment2);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Alexei, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("602Fb", kObjectCompartment2);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction302614416);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602DB");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+ }
+ break;
+
+ case kAction135664192:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Alexei, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("103D", kCarRestaurant, 52);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(60) + 90);
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("103C", kCarRestaurant, 52);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103E");
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(16, Alexei, function16, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param7 && params->param1 < getState()->time && !params->param8) {
+ params->param8 = 1;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(CURRENT_PARAMS(1, 1), getState()->timeTicks, 75);
+
+ params->param5 = 0;
+ params->param6 = 1;
+
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ CURRENT_PARAMS(1, 1) = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param5) {
+ if (savepoint.param.intValue == 18) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : "CAT1503");
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAlexei, (char*)&params->seq);
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param6 || params->param5) {
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param5 = 0;
+ params->param6 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("ALX1134A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param5 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param5 = 0;
+ params->param6 = 1;
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTicks(300);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("602Gb", kObjectCompartment2);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602Hb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+ break;
+
+ case 10:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_7500;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, (char *)&params->seq);
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param7 = 0;
+ break;
+ }
+ break;
+
+ case kAction124697504:
+ setCallback(10);
+ setup_enterExitCompartment("602Ib", kObjectCompartment2);
+ break;
+
+ case kAction221617184:
+ params->param7 = 1;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction100906246);
+
+ setCallback(7);
+ setup_playSound("CON1024");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Alexei, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler)
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Alexei, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ params->param2 = kItemNone;
+
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+ getInventory()->setLocationAndProcess(kItem17, kObjectLocation1);
+
+ setCallback(1);
+ setup_callSavepoint("005D", kEntityTables1, kActionDrawTablesWithChairs, "005E");
+ break;
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ } else {
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction1:
+ params->param2 = kItemNone;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiDiner);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "005B");
+
+ params->param2 = kItemInvalid;
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 63) ? 1 : 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ setup_function19();
+ break;
+
+ case 2:
+ getAction()->playAnimation(getProgress().jacket == kJacketGreen ? kEventAlexeiDiner : kEventAlexeiDinerOriginalJacket);
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kActionDrawTablesWithChairs, "005E");
+
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityAlexei);
+ getInventory()->get(kItem17)->location = kObjectLocation1;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 63);
+
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOBYTE(params->param2);
+ break;
+
+ case kAction225182640:
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19 ,Alexei, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("811DS");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("811US");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_draw("933");
+ break;
+
+ case 6:
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+ getScenes()->loadSceneFromItemPosition(kItem17);
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+
+ setCallback(7);
+ setup_callSavepoint("005F", kEntityTables1, kActionDrawTablesWithChairs, "005G");
+ break;
+
+ case 7:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityServers1, kAction302996448);
+
+ setCallback(8);
+ setup_draw("934");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_draw("811DS");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function13();
+ break;
+
+ case 10:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(11);
+ setup_function16(kTime1098000, "411");
+ break;
+
+ case 11:
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Alexei, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function14();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Alexei, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1)
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updatePosition("103C", kCarRestaurant, 52);
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ params->param1 = 225 * (4 * rnd(3) + 4);
+
+ if (!getEvent(kEventAlexeiSalonPoem))
+ getData()->inventoryItem = kItemParchemin;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ setup_function22();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "103D");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 52);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 52);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Alexei, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, params->param2)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updatePosition("103D", kCarRestaurant, 52);
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+
+ if (params->param3 == kTimeInvalid || getState()->time <= kTime1111500)
+ break;
+
+ if (getState()->time > kTime1138500) {
+ params->param3 = kTimeInvalid;
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = getState()->time;
+
+ if (params->param3 >= getState()->time)
+ break;
+
+ params->param3 = kTimeInvalid;
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ setup_function23();
+ break;
+
+ case kAction1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(4) + 8);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103E");
+ if (!getEvent(kEventAlexeiSalonPoem))
+ getData()->inventoryItem = kItemParchemin;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ setup_function21();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "103D");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 52);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 52);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Alexei, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->inventoryItem = (!getEntities()->isInRestaurant(kEntityAlexei) || getEvent(kEventAlexeiSalonPoem)) ? kItemNone : kItemParchemin;
+ break;
+
+ case kAction1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction124973510);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAlexeiSalonVassili);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103F");
+ getScenes()->processScene();
+
+ setup_function24();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+
+ getData()->inventoryItem = kItemNone;
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ break;
+ }
+ break;
+
+ case kAction157159392:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonVassili);
+ } else {
+ setup_function24();
+ }
+ break;
+
+ case kAction188784532:
+ setup_function24();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Alexei, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonCath);
+ break;
+
+ case kActionDefault:
+ if (getEvent(kEventAlexeiSalonVassili))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAlexeiSalonCath);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_9460;
+ getEntities()->clearSequences(kEntityAlexei);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ setup_function25();
+ break;
+
+ case 2:
+ setup_function25();
+ break;
+ }
+ break;
+
+ case kAction135854208:
+ getData()->inventoryItem = kItemNone;
+ setCallback(2);
+ setup_draw("103G");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Alexei, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(2);
+ setup_function16(kTime1179000, "411");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(kTime1323000, "412");
+ break;
+
+ case 3:
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Alexei, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1512000, params->param1, setup_function27)
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->car = kCarGreenSleeping;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 66);
+
+ getEntities()->clearSequences(kEntityAlexei);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Alexei, function27)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "412");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Alexei, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Alexei, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime1791000, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("811US");
+ break;
+
+ case 5:
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ setCallback(6);
+ setup_callSavepoint("018B", kEntityTables1, kAction136455232, "BOGUS");
+ break;
+
+ case 6:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction290869168);
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Alexei, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getData()->car = kCarRestaurant;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "018C");
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction156444784);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "018E");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getProgress().field_68 = 1;
+
+ setCallback(2);
+ setup_playSound("TAT2116");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAlexei, "TAt2116A");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ setCallback(3);
+ setup_callSavepoint("018F", kEntityTatiana, kAction123857088, "BOGUS");
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ setup_function31();
+ break;
+ }
+ break;
+
+ case kAction236053296:
+ getEntities()->drawSequenceRight(kEntityAlexei, "018D1");
+ getEntities()->drawSequenceRight(kEntityTatiana, "018D2");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ if (savepoint.param.intValue)
+ getScenes()->loadSceneFromPosition(kCarRestaurant, (Position)savepoint.param.intValue);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Alexei, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityAlexei, "811DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAlexei);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function13();
+ break;
+
+ case 2:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(3);
+ setup_function16(kTimeEnd, "411");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Alexei, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Alexei, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function34();
+ break;
+
+ case kAction122288808:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Alexei, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(1);
+ setup_function16(kTime2083500, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function35();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function13();
+ break;
+
+ case 7:
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 66);
+
+ setCallback(8);
+ setup_function16(kTime2124000, "NONE");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function14();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function36();
+ break;
+
+ case 10:
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(11);
+ setup_function16(kTime16451100, "411");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Alexei, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 2700)
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ UPDATE_PARAM_PROC_END
+ } else {
+ params->param2 = 0;
+ }
+
+ UPDATE_PARAM_PROC(params->param3, getState()->time, params->param1)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setCallback(3);
+ setup_function15();
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+
+label_callback_3:
+ UPDATE_PARAM(params->param4, getState()->time, 9000);
+
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDefault:
+ params->param1 = 15 * rnd(120);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback((byte)(getCallback() + 1));
+ setup_updatePosition("124C", kCarRestaurant, 52);
+ break;
+
+ case 2:
+ case 5:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ params->param1 = 15 * rnd(120);
+ params->param3 = 0;
+ goto label_callback_3;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Alexei, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 || params->param2)
+ break;
+
+ UPDATE_PARAM(params->param4, getState()->timeTicks, params->param1);
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "124B");
+
+ params->param2 = 1;
+ params->param4 = 0;
+ break;
+
+ case kActionExitCompartment:
+ if (params->param2) {
+ getEntities()->drawSequenceLeft(kEntityAlexei, "124A");
+ params->param1 = 5 * (3 * rnd(15) + 15);
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(15) + 15);
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAlexei, kEntityAbbot, kAction222609266);
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "124A");
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ setCallback(4);
+ setup_function13();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "BLANK");
+ params->param3 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Alexei, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Alexei, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2354400, "411");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function39();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Alexei, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2)
+ break;
+
+ if (!params->param4) {
+ params->param3 = getState()->time + 4500;
+ params->param4 = getState()->time + 9000;
+ }
+
+ if (params->param5 != kTimeInvalid && params->param3 < getState()->time) {
+
+ if (params->param4 >= getState()->time) {
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) || !params->param5)
+ params->param5 = getState()->time;
+
+ if (params->param5 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ setup_function40();
+ }
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param2 && !params->param2)
+ getEntities()->drawSequenceLeft(kEntityAlexei, "306F");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("602FB", kObjectCompartment2);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62)) {
+ if (params->param1) {
+ if (!params->param2)
+ break;
+ } else if (!params->param2) {
+ getEntities()->drawSequenceRight(kEntityAlexei, "306A");
+ break;
+ }
+
+ setup_function40();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+ break;
+ }
+ break;
+
+ case kAction123536024:
+ params->param2 = 1;
+ break;
+
+ case kAction123712592:
+ getEntities()->clearSequences(kEntityAlexei);
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Alexei, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityAlexei, "602Eb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment2);
+ }
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2);
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAlexei);
+
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Alexei, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(1);
+ setup_function16(kTime2403000, "411");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function42();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Alexei, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function14();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction191198209);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Alexei, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time < kTime1806300 && params->param2 < getState()->time) {
+ if (!params->param2)
+ params->param2 = getState()->time + params->param1;
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setCallback(1);
+ setup_function15();
+ break;
+ }
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2457000 && !params->param3) {
+ params->param3 = 1;
+
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(120) + 180);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 5 * (3 * rnd(120) + 180);
+ params->param2 = 0;
+ goto label_callback_1;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("124C", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ setup_function44();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Alexei, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2457000 && !params->param1) {
+ params->param1 = 1;
+
+ getEntities()->updatePositionExit(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityAlexei, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+
+ setup_function45();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_9460;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62)) {
+ setCallback(2);
+ setup_draw("306A");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "306F");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Alexei, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ if (getInventory()->hasItem(kItemBomb)) {
+ setup_function46();
+ } else {
+ setCallback(2);
+ setup_function16(kTimeEnd, "412");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Alexei, function46)
+ error("Alexei: callback function 46 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Alexei, function47)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Alexei, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityAlexei);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/alexei.h b/engines/lastexpress/entities/alexei.h
new file mode 100644
index 0000000000..dc1a136143
--- /dev/null
+++ b/engines/lastexpress/entities/alexei.h
@@ -0,0 +1,213 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ALEXEI_H
+#define LASTEXPRESS_ALEXEI_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Alexei : public Entity {
+public:
+ Alexei(LastExpressEngine *engine);
+ ~Alexei() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The sequence to draw for the second entity
+ * - The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_NOSETUP(draw2)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * ???
+ *
+ * @param timeValue The time value
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_2(function16, TimeValue timeValue, const char* sequence)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+ DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(function43)
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ALEXEI_H
diff --git a/engines/lastexpress/entities/alouan.cpp b/engines/lastexpress/entities/alouan.cpp
new file mode 100644
index 0000000000..0a66004a49
--- /dev/null
+++ b/engines/lastexpress/entities/alouan.cpp
@@ -0,0 +1,504 @@
+/* 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/entities/alouan.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Alouan::Alouan(LastExpressEngine *engine) : Entity(engine, kEntityAlouan) {
+ ADD_CALLBACK_FUNCTION(Alouan, reset);
+ ADD_CALLBACK_FUNCTION(Alouan, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Alouan, playSound);
+ ADD_CALLBACK_FUNCTION(Alouan, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Alouan, updateEntity);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment6);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment8);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment6to8);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment8to6);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter1);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function12);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter2);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter3);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter4);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function19);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter5);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function22);
+ ADD_CALLBACK_FUNCTION(Alouan, function23);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Alouan, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Alouan, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Alouan, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Alouan, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Alouan, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Alouan, compartment6)
+ COMPARTMENT_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Cf", "621Df");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Alouan, compartment8)
+ COMPARTMENT_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Alouan, compartment6to8)
+ COMPARTMENT_FROM_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Alouan, compartment8to6)
+ COMPARTMENT_FROM_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Alouan, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Alouan, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+
+ TIME_CHECK_CALLBACK(kTime1096200, params->param1, 1, setup_compartment8to6);
+
+label_callback1:
+ if (getState()->time > kTime1162800 && !params->param2) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4070);
+ getData()->entityPosition = kPosition_4070;
+ }
+
+ if (getState()->time > kTime1179000 && !params->param3) {
+ params->param3 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+
+ setCallback(2);
+ setup_compartment6to8();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4840;
+ goto label_callback1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Alouan, function12)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAlouan);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Alouan, chapter2)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Alouan, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime1777500) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2)
+ params->param2 = getState()->time + 75;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(params->param1 ? 1 : 2);
+ if (params->param1)
+ setup_compartment8();
+ else
+ setup_compartment6();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ params->param1 = 1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 3:
+ params->param1 = 0;
+ setCallback(4);
+ setup_playSound("Har2011");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(900);
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityAlouan, kEntityFrancois, kAction190219584);
+ break;
+ }
+ break;
+
+ case kAction189489753:
+ setCallback(3);
+ setup_compartment8to6();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Alouan, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Alouan, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTimeCitySalzbourg, params->param1, 1, setup_compartment8to6);
+
+label_callback1:
+ if (params->param2 != kTimeInvalid && getState()->time > kTime1989000)
+ TIME_CHECK_CAR(kTime2119500, params->param5, 5, setup_compartment8);
+
+label_callback2:
+ TIME_CHECK_PLAYSOUND(kTime2052000, params->param3, 3, "Har1005");
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime2133000, params->param4, 4, setup_compartment6to8);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime2151000)
+ TIME_CHECK_CAR(kTime2241000, params->param5, 5, setup_compartment8);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4840;
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Alouan, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Alouan, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid)
+ TIME_CHECK_CAR(kTime2443500, params->param1, 1, setup_compartment8);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2455200, params->param2, 2, setup_compartment8to6);
+
+label_callback2:
+ if (getState()->time > kTime2475000 && !params->param3) {
+ params->param3 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+
+ setCallback(3);
+ setup_compartment6to8();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4070);
+ goto label_callback2;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Alouan, function19)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAlouan);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Alouan, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Alouan, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function22();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Alouan, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ setup_function23();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Alouan, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("619AF", kObjectCompartment5);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment6, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(24, Alouan)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/alouan.h b/engines/lastexpress/entities/alouan.h
new file mode 100644
index 0000000000..282117fcea
--- /dev/null
+++ b/engines/lastexpress/entities/alouan.h
@@ -0,0 +1,139 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ALOUAN_H
+#define LASTEXPRESS_ALOUAN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Alouan : public Entity {
+public:
+ Alouan(LastExpressEngine *engine);
+ ~Alouan() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(compartment6)
+ DECLARE_FUNCTION(compartment8)
+ DECLARE_FUNCTION(compartment6to8)
+ DECLARE_FUNCTION(compartment8to6)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ALOUAN_H
diff --git a/engines/lastexpress/entities/anna.cpp b/engines/lastexpress/entities/anna.cpp
new file mode 100644
index 0000000000..f81d95754e
--- /dev/null
+++ b/engines/lastexpress/entities/anna.cpp
@@ -0,0 +1,3400 @@
+/* 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/entities/anna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) {
+ ADD_CALLBACK_FUNCTION(Anna, reset);
+ ADD_CALLBACK_FUNCTION(Anna, draw);
+ ADD_CALLBACK_FUNCTION(Anna, updatePosition);
+ ADD_CALLBACK_FUNCTION(Anna, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Anna, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Anna, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Anna, playSound);
+ ADD_CALLBACK_FUNCTION(Anna, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Anna, savegame);
+ ADD_CALLBACK_FUNCTION(Anna, updateEntity);
+ ADD_CALLBACK_FUNCTION(Anna, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Anna, function12);
+ ADD_CALLBACK_FUNCTION(Anna, draw2);
+ ADD_CALLBACK_FUNCTION(Anna, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Anna, function15);
+ ADD_CALLBACK_FUNCTION(Anna, chapter1);
+ ADD_CALLBACK_FUNCTION(Anna, function17);
+ ADD_CALLBACK_FUNCTION(Anna, function18);
+ ADD_CALLBACK_FUNCTION(Anna, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function20);
+ ADD_CALLBACK_FUNCTION(Anna, function21);
+ ADD_CALLBACK_FUNCTION(Anna, function22);
+ ADD_CALLBACK_FUNCTION(Anna, function23);
+ ADD_CALLBACK_FUNCTION(Anna, function24);
+ ADD_CALLBACK_FUNCTION(Anna, function25);
+ ADD_CALLBACK_FUNCTION(Anna, function26);
+ ADD_CALLBACK_FUNCTION(Anna, function27);
+ ADD_CALLBACK_FUNCTION(Anna, function28);
+ ADD_CALLBACK_FUNCTION(Anna, function29);
+ ADD_CALLBACK_FUNCTION(Anna, function30);
+ ADD_CALLBACK_FUNCTION(Anna, function31);
+ ADD_CALLBACK_FUNCTION(Anna, function32);
+ ADD_CALLBACK_FUNCTION(Anna, function33);
+ ADD_CALLBACK_FUNCTION(Anna, function34);
+ ADD_CALLBACK_FUNCTION(Anna, function35);
+ ADD_CALLBACK_FUNCTION(Anna, function36);
+ ADD_CALLBACK_FUNCTION(Anna, function37);
+ ADD_CALLBACK_FUNCTION(Anna, function38);
+ ADD_CALLBACK_FUNCTION(Anna, function39);
+ ADD_CALLBACK_FUNCTION(Anna, function40);
+ ADD_CALLBACK_FUNCTION(Anna, function41);
+ ADD_CALLBACK_FUNCTION(Anna, chapter2);
+ ADD_CALLBACK_FUNCTION(Anna, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Anna, chapter3);
+ ADD_CALLBACK_FUNCTION(Anna, function45);
+ ADD_CALLBACK_FUNCTION(Anna, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function47);
+ ADD_CALLBACK_FUNCTION(Anna, function48);
+ ADD_CALLBACK_FUNCTION(Anna, leaveTableWithAugust);
+ ADD_CALLBACK_FUNCTION(Anna, function50);
+ ADD_CALLBACK_FUNCTION(Anna, function51);
+ ADD_CALLBACK_FUNCTION(Anna, function52);
+ ADD_CALLBACK_FUNCTION(Anna, function53);
+ ADD_CALLBACK_FUNCTION(Anna, function54);
+ ADD_CALLBACK_FUNCTION(Anna, function55);
+ ADD_CALLBACK_FUNCTION(Anna, function56);
+ ADD_CALLBACK_FUNCTION(Anna, function57);
+ ADD_CALLBACK_FUNCTION(Anna, function58);
+ ADD_CALLBACK_FUNCTION(Anna, function59);
+ ADD_CALLBACK_FUNCTION(Anna, function60);
+ ADD_CALLBACK_FUNCTION(Anna, function61);
+ ADD_CALLBACK_FUNCTION(Anna, function62);
+ ADD_CALLBACK_FUNCTION(Anna, function63);
+ ADD_CALLBACK_FUNCTION(Anna, baggage);
+ ADD_CALLBACK_FUNCTION(Anna, function65);
+ ADD_CALLBACK_FUNCTION(Anna, chapter4);
+ ADD_CALLBACK_FUNCTION(Anna, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function68);
+ ADD_CALLBACK_FUNCTION(Anna, function69);
+ ADD_CALLBACK_FUNCTION(Anna, function70);
+ ADD_CALLBACK_FUNCTION(Anna, function71);
+ ADD_CALLBACK_FUNCTION(Anna, function72);
+ ADD_CALLBACK_FUNCTION(Anna, function73);
+ ADD_CALLBACK_FUNCTION(Anna, chapter5);
+ ADD_CALLBACK_FUNCTION(Anna, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function76);
+ ADD_CALLBACK_FUNCTION(Anna, function77);
+ ADD_CALLBACK_FUNCTION(Anna, function78);
+ ADD_CALLBACK_FUNCTION(Anna, function79);
+ ADD_CALLBACK_FUNCTION(Anna, function80);
+ ADD_CALLBACK_FUNCTION(Anna, finalSequence);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Anna, reset)
+ Entity::reset(savepoint, true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Anna, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(3, Anna, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Anna, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Anna, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(6, Anna, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Anna, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Anna, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Anna, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Anna, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction) || getProgress().chapter >= kChapter2)
+ getSound()->playSound(kEntityPlayer, "CAT1001");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Anna, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Anna, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2 && ENTITY_PARAM(0, 1))
+ params->param2 = 1;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, 75)
+ getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound);
+
+ params->param6 = 0;
+ params->param7 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param4) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param4 = 0;
+ params->param5 = 1;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ --params->param1;
+
+ getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionEndSound:
+ if (params->param2) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ ++params->param1;
+
+ switch (params->param1) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityAnna, "ANN2135A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAnna, "ANN2135B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAnna, "ANN2135C");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAnna, "ANN2135C");
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityAnna, "ANN2135L");
+ break;
+
+ case 6:
+ getSound()->playSound(kEntityAnna, "ANN2135K");
+ break;
+
+ case 7:
+ getSound()->playSound(kEntityAnna, "ANN2135H");
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityAnna, "ANN2135K");
+ break;
+
+ case 9:
+ getSound()->playSound(kEntityAnna, "ANN2135I");
+ break;
+
+ case 10:
+ getSound()->playSound(kEntityAnna, "ANN2135J");
+ break;
+
+ case 11:
+ getSound()->playSound(kEntityAnna, "ANN2135M");
+ break;
+
+ case 12:
+ getSound()->playSound(kEntityAnna, "ANN2135L");
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param4) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ if (savepoint.param.intValue == 53) {
+ getSound()->playSound(kEntityPlayer, getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ if (rnd(2)) {
+ getSound()->playSound(kEntityPlayer, getSound()->wrongDoorCath());
+ } else {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1506A" : "CAT1506");
+ }
+ } else {
+ getSound()->playSound(kEntityPlayer, getSound()->wrongDoorCath());
+ }
+
+ params->param4 = 0;
+ params->param5 = 0;
+ } else {
+ getSound()->removeFromQueue(kEntityAnna);
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case kActionOpenDoor:
+ getSound()->removeFromQueue(kEntityAnna);
+ setCallback(3);
+ setup_playSound("LIB013");
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 49))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "418C");
+
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getSound()->playSound(kEntityAnna, "ANN2135A");
+ break;
+
+ case kActionDrawScene:
+ if (params->param5 || params->param4) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60)) {
+ ++params->param3;
+ if (params->param3 == 2) {
+ setCallback(2);
+ setup_draw("418B");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Ann1016");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorHand);
+ params->param4 = 1;
+ break;
+
+ case 3:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(4);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ --params->param1;
+ params->param6 = 1;
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAnna, "418A");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(13, Anna, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Anna, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Anna, function15, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param5 = 0;
+ params->param6 = 1;
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next action
+
+ case kActionKnock:
+ if (params->param5) {
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ if (savepoint.param.intValue == kObject53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ } else {
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ }
+ } else {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, (char *)&params->seq);
+ break;
+
+ case kActionDrawScene:
+ if (params->param6 || params->param5) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param5 = 0;
+ params->param6 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param5 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ params->param5 = 0;
+ params->param6 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Anna, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityAnna, kAction291662081, 0);
+ getSavePoints()->addData(kEntityAnna, kAction238936000, 1);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->inventoryItem = (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityAnna, kEntityPlayer, 2000)) ? (InventoryItem)LOBYTE(params->param3) : kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ if (savepoint.param.intValue == 8) {
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+ params->param3 &= 0xFFFFFFF7;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaGiveScarf);
+ } else {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventGotALight);
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction) || getProgress().chapter >= kChapter2)
+ getSound()->playSound(kEntityPlayer, "CAT1001");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityAnna);
+ break;
+
+ case kActionDefault:
+ if (getProgress().jacket == kJacketGreen) {
+ if (!getEvent(kEventGotALight) && !getEvent(kEventGotALightD) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ params->param3 = kItemInvalid;
+
+ if (!params->param3 && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param3 |= 8;
+ }
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf)
+ || getEvent(kEventAnnaGiveScarfDiner)
+ || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram)
+ || getEvent(kEventAnnaGiveScarfDinerMonogram)
+ || getEvent(kEventAnnaGiveScarfSalonMonogram))
+ getAction()->playAnimation(kEventAnnaGiveScarfAsk);
+ else if (getEvent(kEventAugustPresentAnna)
+ || getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ getAction()->playAnimation(kEventAnnaGiveScarfMonogram);
+ else
+ getAction()->playAnimation(kEventAnnaGiveScarf);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->direction == kDirectionUp ? kEventGotALightD : kEventGotALight);
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh);
+ params->param3 &= 0xFFFFFF7F;
+
+ if (getProgress().jacket == kJacketGreen && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param3 |= 8;
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && params->param1 < getState()->time && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5 && !params->param4) {
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 900)
+ params->param2 |= kItemScarf;
+ params->param5 = 0;
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ } else {
+ params->param7 = 0;
+ }
+ break;
+
+ case kAction1:
+ setCallback(savepoint.param.intValue == 8 ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, savepoint.param.intValue == 8 ? kEventAnnaGiveScarf : kEventDinerMindJoin);
+ break;
+
+ case kActionDefault:
+ if (getProgress().jacket == kJacketGreen) {
+ if (!getEvent(kEventDinerMindJoin) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ params->param2 |= kItemInvalid;
+
+ if (!params->param2 && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param2 |= 8;
+ }
+
+ getData()->inventoryItem = (InventoryItem)LOBYTE(params->param2);
+ break;
+
+ case kActionDrawScene:
+ params->param3 = getEntities()->isPlayerPosition(kCarRestaurant, 62);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram)) {
+ getAction()->playAnimation(kEventAnnaGiveScarfDinerAsk);
+ } else {
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaGiveScarfDinerMonogram : kEventAnnaGiveScarfDiner);
+ params->param5 = 1;
+ }
+
+ params->param2 &= 0xFFFFFFF7;
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventDinerMindJoin);
+
+ params->param2 &= 0xFFFFFFF7;
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk)) {
+ params->param2 |= 8;
+ }
+
+ getData()->inventoryItem = (InventoryItem)LOBYTE(params->param2);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ params->param4 = 1;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOBYTE(params->param2);
+ params->param4 = 0;
+ break;
+
+ case kAction170016384:
+ case kAction259136835:
+ case kAction268773672:
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Anna, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("618Ca", kObjectCompartment1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_8514;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Anna, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function15(kTime1093500, "NONE");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("618Bf", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Anna, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_draw("801US");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceRight(kEntityAnna, "001B");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 4:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Anna, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001A");
+ getSavePoints()->push(kEntityAnna, kEntityPascale, kAction223262556);
+ break;
+
+ case kAction157370960:
+ getData()->location = kLocationInsideCompartment;
+ setup_function23();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Anna, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001D");
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction270410280);
+ getSavePoints()->push(kEntityAnna, kEntityTables0, kAction136455232);
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001E");
+ setCallback(2);
+ setup_playSound("ANN1048");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_draw("001F");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction203859488);
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Anna, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001G");
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001H");
+ setCallback(2);
+ setup_playSound("ANN1049");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction136702400);
+ setup_function25();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Anna, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001J");
+ getProgress().field_28 = 1;
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 2:
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ setup_function26();
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+
+ case kAction201437056:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001J");
+ setCallback(2);
+ setup_function18(kTime1138500);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Anna, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62);
+
+ setCallback(1);
+ setup_callSavepoint("001L", kEntityTables0, kActionDrawTablesWithChairs, "001H");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62);
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction237485916);
+ getEntities()->drawSequenceRight(kEntityAnna, "801DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function17(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Anna, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ setCallback(1);
+ setup_function15(kTime1156500, "NONE");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (getProgress().field_14 == 29) {
+ params->param1 = getState()->time + 900;
+ setCallback(2);
+ setup_function15((TimeValue)params->param1, "NONE");
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("618Bf", kObjectCompartmentF);
+ }
+ break;
+
+ case 3:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Anna, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_1540;
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ setCallback(3);
+ setup_updatePosition("104A", kCarRestaurant, 56);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ setup_function29();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Anna, function29)
+ error("Anna: callback function 29 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Anna, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 != kTimeInvalid && getState()->time) {
+ if (getState()->time > kTime1188000) {
+ params->param3 = kTimeInvalid;
+ getSound()->playSound(kEntityAnna, "AUG1004");
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = getState()->time + 450;
+
+ if (params->param3 < getState()->time) {
+ params->param3 = kTimeInvalid;
+ getSound()->playSound(kEntityAnna, "AUG1004");
+ }
+ }
+ }
+
+ if (params->param2 && params->param4 != kTimeInvalid && getState()->time > kTime1179000) {
+
+ if (getState()->time > kTime1192500) {
+ params->param4 = kTimeInvalid;
+ setup_function30();
+ break;
+ }
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = getState()->time + 150;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ setup_function30();
+ break;
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ params->param2 = 1;
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAnna, "106B");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 56);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Anna, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityAnna, "AUG1005");
+
+ setCallback(2);
+ setup_updateFromTicks(150);
+ break;
+
+ case 2:
+ getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 56);
+
+ setCallback(3);
+ setup_draw2("106C1", "106C2", kEntityAugust);
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 56);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction159332865);
+
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Anna, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function33();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Anna, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+
+ params->param1 = getState()->time + 4500;
+ setCallback(1);
+ setup_function15((TimeValue)params->param1, "NONE");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getObjects()->updateLocation2(kObjectCompartmentF, kObjectLocation1);
+ setup_function34();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Anna, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntities()->isPlayerPosition(kCarRedSleeping, 60)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 150)
+ setCallback(1);
+ setup_draw("419B");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_1:
+ TIME_CHECK(kTime1489500, params->param3, setup_function35);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "419A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ params->param1 = 1;
+ goto label_callback_1;
+
+ case 2:
+ case 3:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(4);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Anna, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ switch (params->param2) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityAnna, "ANN2135E");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityAnna, "ANN2135F");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAnna, "ANN2135G");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAnna, "ANN2135D");
+ break;
+ }
+
+ params->param1 = 0;
+ params->param3 = 0;
+ break;
+
+ case kActionEndSound:
+ ++params->param2;
+
+ if (params->param2 > 3)
+ params->param2 = 0;
+
+ params->param1 = 1;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaVisitToCompartmentGun);
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes1;
+ params->param1 = 1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaVisitToCompartmentGun);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_4840;
+
+ getEntities()->updateEntity(kEntityAnna, kCarRedSleeping, kPosition_8200);
+ getScenes()->loadSceneFromObject(kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityVassili, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityVerges, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+
+ setup_function36();
+ break;
+
+ case 2:
+ setup_function36();
+ break;
+ }
+ break;
+
+ case kAction226031488:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ break;
+
+ case kAction238358920:
+ setCallback(2);
+ setup_enterExitCompartment("608Cf", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Anna, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("608Aa", kObjectCompartmentA);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Anna, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction191477936:
+ setup_function38();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Anna, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+
+ setCallback(1);
+ setup_playSound("ANN1010");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSound()->playSound(kEntityPlayer, "MUS043");
+ setup_function40();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaGoodNight);
+ break;
+
+ case kActionExcuseMe:
+ getSound()->playSound(kEntityAnna, "ANN1107A");
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ if (!getEvent(kEventAnnaGoodNight) && !getEvent(kEventAnnaGoodNightInverse))
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(getData()->direction == kDirectionNone ? kEventAnnaGoodNight : kEventAnnaGoodNightInverse);
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Anna, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("608Cb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_function39(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("608Bf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(4);
+ setup_updateFromTime(150);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("608Cf", kObjectCompartmentF);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_function39(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("608Bb", kObjectCompartmentB);
+ break;
+
+ case 7:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(8);
+ setup_updateFromTime(150);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("608Cb", kObjectCompartmentB);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_function39(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("608Bf", kObjectCompartmentF);
+ break;
+
+ case 11:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4070;
+
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Anna, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 2700);
+
+ params->param5++;
+ switch (params->param5) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419A");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ params->param1 = 0;
+ break;
+ }
+
+ params->param2 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(3);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Anna, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43 ,Anna, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function15(kTime1786500, "418C");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function12();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function15(kTime1818000, "418C");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function12();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function15(kTimeEnd, "418C");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Anna, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(45, Anna, function45, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("625Bf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, params->param1 ? kAction185737168 : kAction185671840);
+ getSound()->playSound(kEntityAnna, "Ann3147");
+ getEntities()->drawSequenceLeft(kEntityAnna, "625EF");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction157894320:
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Anna, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function47();
+ } else {
+ setCallback(2);
+ setup_function15((TimeValue)(getState()->time + 4500), "418C");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Anna, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("688Bf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("801VS");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAnna, getEvent(kEventAugustLunch) ? "Ann3136" : "Ann3136A", SoundManager::kFlagInvalid, 30);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122358304);
+
+ setCallback(5);
+ setup_draw2("026B1", "026B2", kEntityAugust);
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ setup_function48();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Anna, function48)
+ error("Anna: callback function 48 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Anna, leaveTableWithAugust)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityAnna, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getEntities()->clearSequences(kEntityAugust);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables3, "026J3");
+ getEntities()->drawSequenceRight(kEntityAugust, "026J2");
+ getEntities()->drawSequenceRight(kEntityAnna, "026J1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Anna, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("ann3141");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(3);
+ setup_leaveTableWithAugust();
+ break;
+
+ case 3:
+ setup_function51();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Anna, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getSound()->playSound(kEntityAnna, "Aug3008");
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw2("112E1", "112E2", kEntityAugust);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityAnna, "Aug3142", SoundManager::kFlagInvalid, 30);
+ getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 57);
+ getEntities()->drawSequenceRight(kEntityAnna, "112A");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAnna, "112B");
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 57);
+ getSavePoints()->push(kEntityAnna, kEntityServers1, kAction219377792);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+
+ setup_function52();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "112D");
+
+ if (getState()->time >= kTimeEnterAttnangPuchheim) {
+ params->param1 = 1;
+ } else {
+ setCallback(4);
+ setup_playSound("Ann3142A");
+ }
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(1800);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound("Aug3007");
+ break;
+
+ case 6:
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101169422:
+ if (getEvent(kEventKronosVisit)) {
+ setCallback(3);
+ setup_updatePosition("112J", kCarRestaurant, 57);
+ break;
+ }
+
+ if (getState()->time >= kTimeEnterAttnangPuchheim) {
+ params->param1 = 1;
+ } else {
+ setCallback(4);
+ setup_playSound("Ann3142A");
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "112D");
+ getSavePoints()->push(kEntityAnna, kEntityKronos, kAction157159392);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Anna, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityAnna);
+
+ setup_function53();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceRight(kEntityAnna, "688Af");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070) || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentF);
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Anna, function53)
+ error("Anna: callback function 53 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Anna, function54)
+ error("Anna: callback function 54 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Anna, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getInventory()->setLocationAndProcess(kItemKey, kObjectLocation1);
+
+ setCallback(1);
+ setup_function45(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9270);
+ break;
+
+ case 2:
+ setup_function56();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, Anna, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function57();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, Anna, function57)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction191668032);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction205033696);
+ getEntities()->drawSequenceLeft(kEntityAnna, "625Ef");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "625Gf");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction169032608);
+ break;
+
+ case 4:
+ if (getSound()->isBuffered(kEntityAugust)) {
+ setCallback(4);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(5);
+ setup_playSound("Aug3009");
+ }
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityAnna, "Aug3009A");
+
+ setCallback(6);
+ setup_enterExitCompartment("628Bf", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+
+ setup_function59();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityAnna, "628Af");
+
+ if (getSound()->isBuffered(kEntityAugust)) {
+ setCallback(4);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(5);
+ setup_playSound("Aug3009");
+ }
+ break;
+
+ case kAction192063264:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ setup_function58();
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("625Ff", kObjectCompartmentF);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, Anna, function58)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaSearchingCompartment);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaSearchingCompartment);
+ getEntities()->clearSequences(kEntityAnna);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+ getSound()->playSound(kEntityAnna, "lib015");
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+ setup_function59();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, Anna, function59)
+ error("Anna: callback function 59 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, Anna, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction122358304);
+ getSound()->playSound(kEntityAnna, rnd(2) ? "Ann3126" : "Ann3127");
+
+ setCallback(1);
+ setup_enterExitCompartment("630Cf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("630Df", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAnna);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction189026624);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction156049968:
+ setCallback(3);
+ setup_enterExitCompartment("629EF", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, Anna, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getState()->timeDelta = 3;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeIndex, 0);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_function45(false);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("802US");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceRight(kEntityAnna, "802UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getEntities()->clearSequences(kEntityAnna);
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, Anna, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2259000 && !params->param2) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityAnna, kEntityVesna, kAction189299008);
+ setup_function63();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getProgress().field_54 = 1;
+ break;
+
+ case kAction235856512:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(63, Anna, function63)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction171843264);
+ break;
+
+ // Game over with Anna killed!
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaKilled);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2250000, kSceneGameOverAnnaDied, true);
+ }
+ break;
+
+ // Anna will get killed...
+ case kAction272177921:
+ if (getSound()->isBuffered("MUS012"))
+ getSound()->processEntry("MUS012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaKilled);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(64, Anna, baggage)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBaggageArgument);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaBaggageArgument);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, (EventIndex)kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightAnna);
+
+ if (params->param1)
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param1 == Fight::kFightEndLost);
+ else {
+ getState()->time += 1800;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBagagePart2);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAnnaBagagePart2);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 96);
+
+ getProgress().field_54 = 0;
+ getState()->time = kTime2266200;
+
+ setup_function65();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(65, Anna, function65)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_function15(kTimeEnd, "NONE");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(66, Anna, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(67, Anna, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 46)) {
+ UPDATE_PARAM_GOTO(params->param4, getState()->timeTicks, 30, label_next);
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+ }
+
+ params->param4 = 0;
+
+label_next:
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaConversation_34);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, "511B");
+ break;
+
+ case kActionDrawScene:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaConversation_34);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+
+ setup_function68();
+ break;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_playSound("ANN1016");
+ break;
+
+ case 4:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 5:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction191001984:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->inventoryItem = kItemNone;
+
+ setup_function69();
+ break;
+
+ case kAction219971920:
+ params->param3 = 1;
+ getData()->inventoryItem = kItemInvalid;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(68, Anna, function68)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ setCallback(1);
+ setup_function15(kTime2511900, "NONE");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kAction191001984:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ setup_function69();
+ break;
+
+ case kAction201431954:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(69, Anna, function69)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+
+ setup_function70();
+ break;
+ }
+
+ TIME_CHECK_CALLBACK(kTime2535300, params->param3, 4, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)) {
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+
+ setup_function70();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("127A", kCarRestaurant, 56);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAnna, "127B");
+ getSavePoints()->push(kEntityAnna, kEntityServers1, kAction258136010);
+ break;
+
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("127G", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ setup_function70();
+ break;
+ }
+ break;
+
+ case kAction100969180:
+ getEntities()->clearSequences(kEntityAnna);
+ params->param1 = 1;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "127E");
+ getSavePoints()->push(kEntityAnna, kEntityAbbot, kAction203073664);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(70, Anna, function70)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function72(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function71();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+ setup_function73();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(71, Anna, function71)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->entityPosition = kPosition_4070;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityAnna, "625Af");
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 7)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 28)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 56))
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentF, true);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!getEvent(kEventAnnaTiredKiss)
+ && getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityAnna, 2000)
+ && getEntities()->hasValidFrame(kEntityAnna)
+ && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaTiredKiss);
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaTiredKiss);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 29);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventAnnaTired) || getEntities()->isWalkingOppositeToPlayer(kEntityAnna))
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaTired);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (!getEvent(kEventAnnaTired))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaTired);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(73, Anna, function73)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 == kTimeInvalid || params->param1 >= getState()->time)
+ break;
+
+ if (params->param2 >= getState()->time) {
+ if (!((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) && params->param3))
+ params->param3 = getState()->time;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTrainHijacked);
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound("LIB012");
+ break;
+
+ case kActionOpenDoor:
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaKissTrainHijacked);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getState()->timeDelta = 1;
+
+ params->param1 = getState()->time + 4500;
+ params->param2 = getState()->time + 9000;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventTrainHijacked);
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction139254416);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("Ann4200");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventAnnaKissTrainHijacked);
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction139254416);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(74, Anna, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggageRear;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(75, Anna, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getProgress().field_C)
+ getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies2 : kEventAnnaBaggageTies);
+ else
+ getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies3 : kEventAnnaBaggageTies4);
+
+ getScenes()->loadSceneFromPosition(kCarBaggage, 8);
+ setup_function76();
+ }
+ break;
+
+ case kAction272177921:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBaggageTies);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(76, Anna, function76)
+ if (savepoint.action == kAction158480160)
+ setup_function77();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(77, Anna, function77)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime3645000 && !params->param2) {
+ params->param2 = 1;
+ getState()->timeDelta = 0;
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, savepoint.action == kActionKnock ? "LIB012" : "LIB014");
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaDialogGoToJerusalem);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject106, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 1;
+ break;
+
+ case 2:
+ getObjects()->update(kObject106, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getAction()->playAnimation(kEventAnnaDialogGoToJerusalem);
+
+ getState()->time = kTimeCityConstantinople;
+ getState()->timeDelta = 0;
+
+ getSavePoints()->push(kEntityAnna, kEntityTatiana, kAction236060709);
+
+ getScenes()->loadSceneFromPosition(kCarBaggage, 97, 1);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 3:
+ setup_function78();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(78, Anna, function78)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDrawScene:
+ if ((getEntities()->isInRestaurant(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer)) && getInventory()->hasItem(kItemFirebird)) {
+ setup_function80();
+ break;
+ }
+
+ getState()->time = kTimeInvalid2;
+
+ setCallback(getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? 2 : 1);
+ setup_savegame(kSavegameTypeEvent, getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? kEventKronosHostageAnna : kEventKronosHostageAnnaNoFirebird);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKronosHostageAnnaNoFirebird);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAugustUnhookCarsBetrayal, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosHostageAnna);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ getSound()->playSound(kEntityAnna, "Mus024", SoundManager::kFlagDefault);
+ setup_function79();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(79, Anna, function79)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getState()->time = kTime5933;
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInRestaurant(kEntityPlayer) && getInventory()->hasItem(kItemFirebird)) {
+ setup_function80();
+ break;
+ }
+
+ if (getEntities()->isInSalon(kEntityPlayer) && !getEvent(kEventKahinaPunch)) {
+ getState()->time = kTime5933;
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchSalon);
+ else if (getEntities()->isInRestaurant(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchRestaurant);
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchKitchen);
+ else if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchBaggageCarEntrance);
+ else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage))
+ getAction()->playAnimation(kEventKahinaPunchBaggageCar);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKahinaPunchSalon);
+ break;
+ }
+
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(80, Anna, function80)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 450);
+
+ getSound()->playSound(kEntityPlayer, "Kro5001", SoundManager::kFlagDefault);
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityPlayer, "Kro5002", SoundManager::kFlagDefault);
+ getState()->time = kTime4923000;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringFirebird);
+ break;
+
+ case kActionDefault:
+ getState()->time = kTime4929300;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getAction()->playAnimation(kEventKronosBringFirebird);
+ getScenes()->loadSceneFromItem(kItemFirebird);
+ getSound()->playSound(kEntityAnna, "Mus025", SoundManager::kFlagDefault);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKahinaPunch);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 3:
+ getProgress().isEggOpen = true;
+
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getAction()->playAnimation(kEventKronosOpenFirebird);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 3);
+
+ setup_finalSequence();
+ break;
+ }
+ break;
+
+ case kAction205294778:
+ getState()->time = kTime4929300;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosOpenFirebird);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(81, Anna, finalSequence)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 180);
+
+ getSound()->playSound(kEntityTrain, "LIB069");
+ getLogic()->gameOver(kSavegameTypeIndex, 2, kSceneNone, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCathCloseEggNoBackground);
+ getAction()->playAnimation(kEventKronosGiveFirebird);
+
+ if (getInventory()->hasItem(kItemWhistle))
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverTrainExplosion, true);
+ else if (getInventory()->get(kItemWhistle)->location == kObjectLocation1)
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAnnaDialogGoToJerusalem, kSceneNone, true);
+ else
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAugustUnhookCarsBetrayal, kSceneGameOverTrainExplosion2, true);
+ break;
+
+ case 2:
+ getInventory()->removeItem(kItemWhistle);
+ getLogic()->playFinalSequence();
+ break;
+ }
+ break;
+
+ case kAction224309120:
+ getProgress().isEggOpen = false;
+ getState()->time = kTimeCityConstantinople;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosGiveFirebird);
+ break;
+
+ case kActionUseWhistle:
+ getProgress().isEggOpen = false;
+ setGlobalTimer(0);
+ getState()->time = kTimeCityConstantinople;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventFinalSequence);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/anna.h b/engines/lastexpress/entities/anna.h
new file mode 100644
index 0000000000..4712f4b262
--- /dev/null
+++ b/engines/lastexpress/entities/anna.h
@@ -0,0 +1,252 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ANNA_H
+#define LASTEXPRESS_ANNA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Anna : public Entity {
+public:
+ Anna(LastExpressEngine *engine);
+ ~Anna() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char* sequence1, const char* sequence2, EntityIndex entity)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ DECLARE_FUNCTION_2(function15, TimeValue timeValue, const char *sequence)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_2(function17, uint32, uint32)
+
+ DECLARE_FUNCTION_1(function18, TimeValue timeValue)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION_2(function39, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+ DECLARE_FUNCTION_1(function45, bool useAction1)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(leaveTableWithAugust)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+ DECLARE_FUNCTION(function57)
+ DECLARE_FUNCTION(function58)
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+ DECLARE_FUNCTION(function63)
+ DECLARE_FUNCTION(baggage)
+ DECLARE_FUNCTION(function65)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function68)
+ DECLARE_FUNCTION(function69)
+ DECLARE_FUNCTION(function70)
+ DECLARE_FUNCTION(function71)
+ DECLARE_FUNCTION_2(function72, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION(function73)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function76)
+ DECLARE_FUNCTION(function77)
+ DECLARE_FUNCTION(function78)
+ DECLARE_FUNCTION(function79)
+ DECLARE_FUNCTION(function80)
+ DECLARE_FUNCTION(finalSequence)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ANNA_H
diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp
new file mode 100644
index 0000000000..419d716ced
--- /dev/null
+++ b/engines/lastexpress/entities/august.cpp
@@ -0,0 +1,2804 @@
+/* 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/entities/august.h"
+
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+August::August(LastExpressEngine *engine) : Entity(engine, kEntityAugust) {
+ ADD_CALLBACK_FUNCTION(August, reset);
+ ADD_CALLBACK_FUNCTION(August, updateFromTime);
+ ADD_CALLBACK_FUNCTION(August, draw);
+ ADD_CALLBACK_FUNCTION(August, updatePosition);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(August, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(August, callSavepoint);
+ ADD_CALLBACK_FUNCTION(August, callSavepointNoDrawing);
+ ADD_CALLBACK_FUNCTION(August, draw2);
+ ADD_CALLBACK_FUNCTION(August, playSound);
+ ADD_CALLBACK_FUNCTION(August, playSound16);
+ ADD_CALLBACK_FUNCTION(August, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(August, savegame);
+ ADD_CALLBACK_FUNCTION(August, updateEntity);
+ ADD_CALLBACK_FUNCTION(August, function17);
+ ADD_CALLBACK_FUNCTION(August, updateEntity2);
+ ADD_CALLBACK_FUNCTION(August, function19);
+ ADD_CALLBACK_FUNCTION(August, function20);
+ ADD_CALLBACK_FUNCTION(August, function21);
+ ADD_CALLBACK_FUNCTION(August, chapter1);
+ ADD_CALLBACK_FUNCTION(August, function23);
+ ADD_CALLBACK_FUNCTION(August, dinner);
+ ADD_CALLBACK_FUNCTION(August, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(August, function26);
+ ADD_CALLBACK_FUNCTION(August, function27);
+ ADD_CALLBACK_FUNCTION(August, function28);
+ ADD_CALLBACK_FUNCTION(August, function29);
+ ADD_CALLBACK_FUNCTION(August, restaurant);
+ ADD_CALLBACK_FUNCTION(August, function31);
+ ADD_CALLBACK_FUNCTION(August, function32);
+ ADD_CALLBACK_FUNCTION(August, function33);
+ ADD_CALLBACK_FUNCTION(August, function34);
+ ADD_CALLBACK_FUNCTION(August, chapter2);
+ ADD_CALLBACK_FUNCTION(August, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(August, function37);
+ ADD_CALLBACK_FUNCTION(August, function38);
+ ADD_CALLBACK_FUNCTION(August, function39);
+ ADD_CALLBACK_FUNCTION(August, chapter3);
+ ADD_CALLBACK_FUNCTION(August, function41);
+ ADD_CALLBACK_FUNCTION(August, function42);
+ ADD_CALLBACK_FUNCTION(August, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(August, function44);
+ ADD_CALLBACK_FUNCTION(August, function45);
+ ADD_CALLBACK_FUNCTION(August, function46);
+ ADD_CALLBACK_FUNCTION(August, function47);
+ ADD_CALLBACK_FUNCTION(August, function48);
+ ADD_CALLBACK_FUNCTION(August, function49);
+ ADD_CALLBACK_FUNCTION(August, function50);
+ ADD_CALLBACK_FUNCTION(August, function51);
+ ADD_CALLBACK_FUNCTION(August, function52);
+ ADD_CALLBACK_FUNCTION(August, function53);
+ ADD_CALLBACK_FUNCTION(August, function54);
+ ADD_CALLBACK_FUNCTION(August, function55);
+ ADD_CALLBACK_FUNCTION(August, function56);
+ ADD_CALLBACK_FUNCTION(August, chapter4);
+ ADD_CALLBACK_FUNCTION(August, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(August, function59);
+ ADD_CALLBACK_FUNCTION(August, function60);
+ ADD_CALLBACK_FUNCTION(August, function61);
+ ADD_CALLBACK_FUNCTION(August, function62);
+ ADD_CALLBACK_FUNCTION(August, function63);
+ ADD_CALLBACK_FUNCTION(August, function64);
+ ADD_CALLBACK_FUNCTION(August, function65);
+ ADD_CALLBACK_FUNCTION(August, chapter5);
+ ADD_CALLBACK_FUNCTION(August, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(August, function68);
+ ADD_CALLBACK_FUNCTION(August, unhookCars);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, August, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, August, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, August, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(4, August, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, August, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, August, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarGreenSleeping, kObjectCompartment3, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, August, enterExitCompartment3, ObjectIndex)
+ if (savepoint.action == kAction4) {
+ getEntities()->exitCompartment(kEntityAugust, (ObjectIndex)params->param4);
+ CALLBACK_ACTION();
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, August, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(9, August, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(10, August, callSavepointNoDrawing, EntityIndex, ActionIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param6)
+ getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)&params->seq);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction10:
+ if (!params->param6) {
+ getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)&params->seq);
+ params->param6 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(11, August, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(12, August, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(13, August, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, August, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(15, August, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(16, August, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getProgress().eventMetAugust ? getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1002A" : "CAT1002") : getSound()->excuseMeCath();
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ params->param2 = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_updateEntity2(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_updateEntity2(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 1) = 0;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ ENTITY_PARAM(0, 1) = 1;
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(19, August, function19, bool, bool)
+ error("August: callback function 19 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(20, August, function20, bool)
+ // Expose parameters as ISSI and ignore the default exposed parameters
+ EntityData::EntityParametersISSI *parameters = (EntityData::EntityParametersISSI*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ strcpy((char *)&parameters->seq1, "626");
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (getData()->clothes != kClothes2) {
+ strcpy((char *)&parameters->seq1, "666");
+ break;
+ }
+ // Fallback to next case
+
+ case kChapter4:
+ case kChapter5:
+ strcpy((char *)&parameters->seq1, "696");
+ break;
+ }
+
+ if (params->param1) {
+ strcpy((char *)&parameters->seq2, Common::String::printf("%s%s", (char *)&parameters->seq1, "Gc").c_str());
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ } else {
+ strcpy((char *)&parameters->seq2, Common::String::printf("%s%s", (char *)&parameters->seq1, "Ec").c_str());
+ }
+
+ setCallback(1);
+ setup_enterExitCompartment((char *)&parameters->seq2, kObjectCompartment3);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1: {
+ getData()->location = kLocationOutsideCompartment;
+
+ Common::String sequence2 = Common::String::printf("%s%s", (char *)&parameters->seq2, "Pc");
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, sequence2.c_str());
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ if (getProgress().chapter != kChapter3 || getState()->time >= kTime1998000) {
+ setCallback(3);
+ setup_playSound("AUG2095");
+ } else {
+ setCallback(2);
+ setup_playSound("AUG2094");
+ }
+ }
+ break;
+
+ case 2:
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction269436673);
+ strcpy((char *)&parameters->seq2, Common::String::printf("%s%s", (char *)&parameters->seq1, "Qc").c_str());
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ break;
+ }
+ break;
+
+ case kAction69239528:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(21, August, function21, TimeValue)
+ error("August: callback function 21 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, August, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject11, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+
+ getProgress().eventMetAugust = false;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29 || getProgress().field_14 == 3) {
+ if (params->param3) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_enterExitCompartment("626Ea", kObjectCompartment1);
+ } else {
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (!params->param2) {
+
+ if (!CURRENT_PARAMS(1, 3))
+ CURRENT_PARAMS(1, 3) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAMS(1, 3) >= getState()->timeTicks)
+ break;
+
+ if (!params->param5) {
+ setCallback(8);
+ setup_playSound("AUG1002B");
+ break;
+ }
+
+label_callback_8:
+ UPDATE_PARAM_PROC(CURRENT_PARAMS(1, 4), getState()->timeTicks, 75)
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true);
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(9);
+ setup_enterExitCompartment("626Da", kObjectCompartment1);
+ } else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(10);
+ setup_enterExitCompartment3("626Da", kObjectCompartment1);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ break;
+ UPDATE_PARAM_PROC_END
+
+label_callback_9:
+ if (params->param3 && params->param1 < getState()->time && !CURRENT_PARAMS(1, 5)) {
+ CURRENT_PARAMS(1, 5) = 1;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(12);
+ setup_enterExitCompartment("626Ea", kObjectCompartment1);
+ }
+ break;
+ }
+
+ if (!CURRENT_PARAMS(1, 1))
+ CURRENT_PARAMS(1, 1) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAMS(1, 1) >= getState()->timeTicks)
+ break;
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ UPDATE_PARAM(CURRENT_PARAMS(1, 2), getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ params->param6++;
+
+ switch (params->param6) {
+ default:
+ break;
+
+ case 1:
+ setCallback(5);
+ setup_playSound("LIB013");
+ return;
+
+ case 2:
+ setCallback(7);
+ setup_playSound("LIB012");
+ return;
+
+ case 3:
+ params->param8++;
+
+ if (params->param8 >= 3) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ params->param6 = 0;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, params->param4 ? kCursorNormal : kCursorTalk, kCursorHand);
+ CURRENT_PARAMS(1, 2) = 0;
+ } else {
+
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? 8 : 7;
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustTylerCompartment);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param3) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(15);
+ setup_playSound("LIB012");
+ } else if (!params->param4) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(17);
+ setup_playSound("AUG1002A");
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ if (params->param3) {
+ getData()->location = kLocationInsideCompartment;
+
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMeetAugustHisCompartmentBed : kEventMeetAugustHisCompartment;
+ } else {
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMeetAugustTylerCompartmentBed : kEventMeetAugustTylerCompartment;
+ }
+
+ setCallback(14);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustTylerCompartment);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(13);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+
+ params->param2 = 1;
+ } else {
+ setCallback(1);
+ setup_enterExitCompartment("626Aa", kObjectCompartment1);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAugust, "626Ba");
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment1, true);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventAugustFindCorpse);
+ if (getEvent(kEventDinerAugustOriginalJacket))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventDinerAugustOriginalJacket, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ else if (getProgress().eventCorpseMovedFromFloor)
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ else
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getEntities()->clearSequences(kEntityAugust);
+ getData()->location = kLocationInsideCompartment;
+
+ getAction()->playAnimation((EventIndex)params->param7);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getProgress().eventMetAugust = true;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound16("AUG1002B");
+ break;
+
+ case 6:
+ case 7:
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, params->param4 ? kCursorNormal : kCursorTalk, kCursorHand);
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+
+ case 8:
+ params->param5 = 1;
+ goto label_callback_8;
+
+ case 9:
+ params->param3 = 1;
+ getEntities()->clearSequences(kEntityAugust);
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ goto label_callback_9;
+
+ case 10:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ break;
+
+ case 11:
+ getAction()->playAnimation(kEventAugustFindCorpse);
+
+ getLogic()->gameOver(getEvent(kEventDinerAugustOriginalJacket) ? kSavegameTypeEvent2 : kSavegameTypeIndex,
+ getEvent(kEventDinerAugustOriginalJacket) ? kEventDinerAugustOriginalJacket : 1,
+ getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice,
+ true);
+ break;
+
+ case 12:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+
+ case 13:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getAction()->playAnimation(kEventAugustFindCorpse);
+
+ if (getEvent(kEventDinerAugustOriginalJacket))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventDinerAugustOriginalJacket, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ else if (getProgress().eventCorpseMovedFromFloor)
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ else
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 14:
+ if (!params->param2)
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getAction()->playAnimation((EventIndex)params->param7);
+ getProgress().eventMetAugust = true;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_playSound("AUG1128A");
+ break;
+
+ case 16:
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 17:
+ params->param4 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, August, dinner)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventDinerAugust);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+
+ getAction()->playAnimation(getEntities()->isInRestaurant(kEntityAlexei) ? kEventDinerAugustAlexeiBackground : kEventDinerAugust);
+ getProgress().eventMetAugust = true;
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, August, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getProgress().eventCorpseFound) {
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction239072064);
+ params->param1 = 1;
+ }
+
+ if (getState()->time > kTime1080000 && !params->param3) {
+ params->param3 = 1;
+
+ if (!params->param1) {
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction239072064);
+ params->param1 = 1;
+ }
+ }
+
+ if (getState()->time > kTime1093500 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_callSavepoint("010J", kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ }
+ break;
+
+ case kAction1:
+ params->param2 = 0;
+ getData()->inventoryItem = kItemNone;
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction191604416);
+
+ if (getProgress().jacket == kJacketGreen) {
+ setCallback(3);
+ setup_dinner();
+ } else {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventDinerAugustOriginalJacket);
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAugust, kEntityTables3, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+
+ if (!getProgress().eventMetAugust)
+ params->param2 = kItemInvalid;
+
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction204704037);
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setup_function26();
+ break;
+
+ case 3:
+ setup_function28();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityAugust, kEntityAlexei, kAction225182640);
+ getAction()->playAnimation(kEventDinerAugustOriginalJacket);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityAugust, kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ getEntities()->drawSequenceRight(kEntityAugust, "010P");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction204704037);
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getProgress().field_14 = 2;
+
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function23(kTimeNone);
+ break;
+
+ case 8:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, August, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().eventMetAugust || getProgress().field_14) {
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ } else {
+ getProgress().field_14 = 2;
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function23((TimeValue)(getState()->time + 13500));
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(false, false);
+ break;
+
+ case 4:
+ if (getProgress().field_14 == 2)
+ getProgress().field_14 = 0;
+
+ setCallback(7);
+ setup_function21((TimeValue)(getState()->time + 900));
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function19(false, false);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function21((TimeValue)(getState()->time + 900));
+ break;
+
+ case 7:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, August, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, August, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ params->param1 = 0;
+
+ setCallback(3);
+ setup_dinner();
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventMetAugust && getProgress().jacket == kJacketGreen)
+ params->param1 = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction304061224);
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction203859488);
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ setup_function29();
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ break;
+
+ case kAction170016384:
+ getData()->inventoryItem = kItemNone;
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityAugust, "010G");
+
+ setCallback(2);
+ setup_playSound("AUG1053");
+ break;
+
+ case kAction268773672:
+ getData()->inventoryItem = kItemNone;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010D");
+
+ setCallback(1);
+ setup_playSound("AUG1052");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, August, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().field_28 || (params->param2 && params->param3 == kTimeInvalid))
+ break;
+
+ if (getState()->time < kTime1134000) {
+
+ if (!getEntities()->isInRestaurant(kEntityPlayer)
+ || getSound()->isBuffered("MRB1076") || getSound()->isBuffered("MRB1078") || getSound()->isBuffered("MRB1078A"))
+ params->param3 = getState()->time + 225;
+
+ if (params->param3 > getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+ getData()->inventoryItem = kItemNone;
+ getProgress().field_28 = 0;
+
+ setup_restaurant();
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ params->param1 = kItemNone;
+
+ setCallback(1);
+ setup_dinner();
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventMetAugust && getProgress().jacket == kJacketGreen)
+ params->param1 = kItemInvalid;
+
+ getData()->inventoryItem = (InventoryItem)LOBYTE(params->param1);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "010H");
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOBYTE(params->param1);
+ break;
+
+ case kAction189426612:
+ params->param2 = 1;
+ break;
+
+ case kAction235257824:
+ params->param2 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, August, restaurant)
+ error("August: callback function 30 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, August, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function19(false, false);
+ break;
+
+ case 2:
+ setCallback(2);
+ setup_function21(kTime1161000);
+ break;
+
+ case 3:
+ case 4:
+ if (getProgress().field_14 == 29) {
+ setCallback(4);
+ setup_function21((TimeValue)(getState()->time + 900));
+ } else {
+ setup_function32();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, August, function32)
+ error("August: callback function 32 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, August, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getProgress().eventMetAugust ? 1 : 2);
+ setup_function21(getProgress().eventMetAugust ? (TimeValue)(getState()->time + 9000) : kTimeBedTime);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2)
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, August, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityAugust) && getProgress().field_18 != 4)
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, August, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_3970;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject11, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, August, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704);
+
+ if (getState()->time > kTime1773000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 62);
+
+ setCallback(2);
+ setup_callSavepoint("016C", kEntityTables0, kActionDrawTablesWithChairs, "016D");
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustGoodMorning);
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAugustGoodMorning))
+ getData()->inventoryItem = kItemInvalid;
+
+ getSavePoints()->push(kEntityAugust, kEntityTables0, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAugust, "016B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAugustGoodMorning);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 62);
+ getEntities()->drawSequenceRight(kEntityAugust, "803ES");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction286534136);
+
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(true, false);
+ break;
+
+ case 5:
+ setup_function37();
+ break;
+
+ case 6:
+ if (!getEvent(kEventAugustGoodMorning))
+ getData()->inventoryItem = kItemInvalid;
+
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction219522616);
+ getEntities()->drawSequenceLeft(kEntityAugust, "016B");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityAugust, "016A");
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_playSound("AUG2113");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, August, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1791000, params->param2, 5, setup_function20, true);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "506A2");
+ break;
+
+ case kActionDrawScene:
+ if (getState()->time > kTime1786500 && getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ if (params->param1) {
+ setCallback(2);
+ setup_draw("506C2");
+ } else {
+ params->param1 = 1;
+
+ setCallback(1);
+ setup_draw("506B2");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 16);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function20(true);
+ break;
+
+ case 3:
+ case 5:
+ setCallback(getCallback() == 3 ? 4 : 6);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 4:
+ case 6:
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, August, function38)
+ error("August: callback function 38 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, August, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!ENTITY_PARAM(0, 1))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustArrivalInMunich);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAugustArrivalInMunich);
+ getSavePoints()->push(kEntityAugust, kEntityChapters, kActionChapter3);
+ getEntities()->clearSequences(kEntityAugust);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, August, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!getEvent(kEventAugustMerchandise)
+ && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarGreenSleeping) {
+ getAction()->playAnimation(kEventAugustMerchandise);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ }
+ break;
+
+ case kAction1:
+ params->param3 = kItemNone;
+ getData()->inventoryItem = kItemNone;
+
+ getAction()->playAnimation((getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) ? kEventAugustTalkGoldDay : kEventAugustTalkGold);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getProgress().eventMetAugust)
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1002" : "CAT1002A");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityAugust);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEvent(kEventAugustMerchandise) && !getEvent(kEventAugustTalkGold) && !getEvent(kEventAugustTalkGoldDay))
+ params->param3 = kItemInvalid;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ params->param4 = 0;
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, getEvent(kEventAugustBringBriefcase) ? "AUG3103" : "AUG3100", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionExcuseMe:
+ if (!getSound()->isBuffered(kEntityAugust))
+ getSound()->excuseMe(kEntityAugust);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param3) {
+ params->param4 = 128;
+
+ if (!getEvent(kEventAugustBringBriefcase))
+ params->param4 = 147;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, August, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081);
+
+ // Set as same position as Anna
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityAnna)->entityPosition;
+ getData()->car = getEntityData(kEntityAnna)->car;
+ }
+
+ if (getState()->time > kTime2016000 && !params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function44();
+ }
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventAugustLunch);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function41(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803VS");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A2");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B2");
+
+ if (!getEvent(kEventAugustLunch))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventAugustLunch);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ params->param1 = 0;
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "112G");
+ break;
+
+ case kAction122358304:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, August, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("122H", kCarRestaurant, 57);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAugustMerchandise)) {
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ } else {
+ setCallback(2);
+ setup_function17(kTime2043000);
+ }
+ break;
+
+ case 2:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ } else {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustMerchandise);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustMerchandise);
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityAugust, kPosition_6470, 500))
+ getData()->entityPosition = kPosition_5970;
+
+ getEntities()->updateEntity(kEntityAugust, kCarGreenSleeping, kPosition_6470);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car,
+ (EntityPosition)(getData()->entityPosition + 750 * (getData()->direction == kDirectionUp ? -1 : 1)),
+ getData()->direction == kDirectionUp);
+
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(true, false);
+ break;
+
+ case 5:
+ setup_function45();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, August, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2061000 && !params->param1) {
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+
+ setup_function46();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, "AUG3102", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "506A2");
+ getData()->inventoryItem = kItem146; // TODO which item is that?
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, August, function46)
+ switch (savepoint.action) {
+ default:
+ TIME_CHECK_CALLBACK(kTime2088000, params->param1, 1, setup_function47);
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ setCallback(2);
+ setup_draw("507B2");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setup_function48();
+ break;
+
+ case 2:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 43))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 34);
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, August, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function41(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAugust);
+ setCallback(3);
+ setup_updateFromTime(2700);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(false, false);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, August, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeCityLinz, params->param1, setup_function49);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getEvent(kEventAugustTalkCompartmentDoor) && !getEvent(kEventAugustTalkCompartmentDoorBlueRedingote)
+ && !getEvent(kEventAugustBringEgg) && !getEvent(kEventAugustBringBriefcase)) {
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustTalkCompartmentDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getData()->clothes = kClothes2;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAugustTalkCompartmentDoor);
+ getScenes()->processScene();
+
+ setCallback(2);
+ setup_function21(kTimeCityLinz);
+ break;
+
+ case 2:
+ setup_function49();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, August, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 2:
+ setup_function50();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, August, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function51();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, August, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function42(kCarGreenSleeping, kPosition_5790, false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityTatiana, kAction191668032);
+
+ setCallback(2);
+ setup_function42(kCarRedSleeping, kPosition_540, true);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592);
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ setup_function52();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction169032608:
+ setCallback(3);
+ setup_function42(kCarRedSleeping, kPosition_3820, true);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, August, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getInventory()->hasItem(kItemBriefcase)) {
+ getData()->location = kLocationInsideCompartment;
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringBriefcase);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird) && !getEvent(kEventAugustBringEgg)) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringEgg);
+ break;
+ }
+
+ if (!getEvent(kEventAugustTalkCompartmentDoorBlueRedingote) && !getEvent(kEventAugustBringEgg) && !getEvent(kEventAugustBringBriefcase)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringEgg);
+ break;
+ }
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 6 : 7);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function42(kCarGreenSleeping, kPosition_6470, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function19(false, true);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityAugust, kEntityKahina, kAction134611040);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustBringBriefcase);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function17);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 13);
+
+ setup_function53();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventAugustBringEgg);
+ getScenes()->processScene();
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventAugustTalkCompartmentDoorBlueRedingote);
+ getScenes()->processScene();
+ break;
+
+ case 6:
+ case 7:
+ setCallback(8);
+ setup_playSound("AUG1128F");
+ break;
+
+ case 8:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, August, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(2700);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function20(false);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setup_function54();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, August, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4 || params->param2 || getProgress().field_44)
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (!params->param2 && params->param1) {
+ UPDATE_PARAM(params->param5, getState()->time, params->param1);
+
+ getData()->inventoryItem = kItemNone;
+ setup_function55();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustTalkCigar);
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 57);
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B3");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isPlayerPosition(kCarRestaurant, 60) || params->param3) {
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ } else if (!params->param2) {
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 57);
+ getEntities()->drawSequenceRight(kEntityAugust, "105C3");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("105A3", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAugust, kEntityAbbot, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B3");
+ params->param4 = 1;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustTalkCigar);
+ getEntities()->drawSequenceLeft(kEntityAugust, params->param3 ? "122B" : "105B3");
+ getScenes()->processScene();
+
+ params->param1 = 9000;
+ params->param4 = 0;
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ params->param2 = 0;
+
+ if (getEvent(kEventAugustTalkCigar))
+ params->param1 = 9000;
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ params->param2 = 1;
+ params->param3 = 1;
+ break;
+
+ case kAction136196244:
+ params->param2 = 1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, August, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("105D3", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(true, false);
+ break;
+
+ case 4:
+ setup_function56();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, August, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "507A3");
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ setCallback(1);
+ setup_draw("507B3");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityAugust, "507A3");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, August, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, August, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803WS");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A3");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function59();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, August, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction190605184);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction123793792:
+ setup_function60();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, August, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ bool pushSavepoint = false;
+ if (!params->param2) {
+ pushSavepoint = true;
+ params->param2 = getState()->time + 450;
+ }
+
+ if (params->param2 < getState()->time) {
+ pushSavepoint = true;
+ params->param2 = kTimeInvalid;
+ }
+
+ if (pushSavepoint)
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction207330561);
+
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param3, getState()->time, 9000);
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_callSavepoint("010J3", kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction286403504);
+ setup_function61();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction201964801:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010H3");
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, August, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->drawSequenceRight(kEntityAugust, "803FS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function19(false, false);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function21((TimeValue)(getState()->time + 4500));
+ break;
+
+ case 4:
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, August, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 900);
+
+ getSound()->playSound(kEntityAugust, "Aug4003A");
+
+ setCallback(5);
+ setup_updatePosition("122C", kCarRestaurant, 57);
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("696Ec", kObjectCompartment3);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("122A", kCarRestaurant, 57);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ getSavePoints()->push(kEntityAugust, kEntityServers1, kAction291721418);
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction125826561:
+ setup_function63();
+ break;
+
+ case kAction134486752:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(63, August, function63)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 1800)
+ getData()->inventoryItem = kItemInvalid;
+ UPDATE_PARAM_PROC_END
+
+ if (getState()->time > kTime2488500 && !params->param4) {
+ params->param4 = 1;
+ getData()->inventoryItem = kItemNone;
+ setup_function64();
+ break;
+ }
+
+ UPDATE_PARAM(params->param5, getState()->timeTicks, params->param1);
+
+ params->param2 = (params->param6 < 1 ? 1 : 0);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, params->param2 ? "122H" : "122F");
+
+ params->param1 = 5 * (3 * rnd(20) + 15);
+ params->param5 = 0;
+ break;
+
+ case kAction1:
+ if (getEntities()->isInSalon(kEntityAlexei))
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function44);
+
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustDrink);
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(20) + 15);
+ getEntities()->drawSequenceLeft(kEntityAugust, "122F");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAugustDrink);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+
+ setup_function64();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(64, August, function64)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ params->param1 = getState()->time + 1800;
+
+ if (params->param1 >= getState()->time)
+ break;
+
+ if (getState()->time > kTime2430000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("122J", kCarRestaurant, 57);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122H");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment2("696Dc", kObjectCompartment3);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAugust);
+ setup_function65();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(65, August, function65)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAugust);
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (!getSound()->isBuffered(kEntityAugust))
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(66, August, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(67, August, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function68();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(68, August, function68)
+ error("August: callback function 68 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(69, August, unhookCars)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getSavePoints()->pushAll(kEntityAugust, kAction135800432);
+ setup_nullfunction();
+ break;
+
+ case kActionDefault:
+ getSound()->processEntries();
+ if (getSound()->isBuffered("ARRIVE"))
+ getSound()->removeFromQueue("ARRIVE");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustUnhookCarsBetrayal);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(getProgress().field_C ? kEventAugustUnhookCarsBetrayal : kEventAugustUnhookCars);
+ getEntities()->clearSequences(kEntityAugust);
+ getSound()->resetState();
+ getSound()->playSound(kEntityPlayer, "MUS050");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 85, 1);
+ getSavePoints()->pushAll(kEntityAugust, kActionProceedChapter5);
+
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function42)
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(70, August)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/august.h b/engines/lastexpress/entities/august.h
new file mode 100644
index 0000000000..e684fc0361
--- /dev/null
+++ b/engines/lastexpress/entities/august.h
@@ -0,0 +1,275 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_AUGUST_H
+#define LASTEXPRESS_AUGUST_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class August : public Entity {
+public:
+ August(LastExpressEngine *engine);
+ ~August() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment3, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Call a savepoint
+ *
+ * @param param1 The entity
+ * @param param2 The action
+ * @param seq The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_3(callSavepointNoDrawing, EntityIndex entity, ActionIndex action, const char* sequence)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char* sequence1, const char* sequence2, EntityIndex entity)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char* filename)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function17, TimeValue timeValue)
+
+ /**
+ * Updates the entity
+ *
+ * @param param1 The car
+ * @param param2 The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_2(function19, bool, bool)
+
+ DECLARE_FUNCTION_1(function20, bool)
+ DECLARE_FUNCTION_1(function21, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_1(function23, TimeValue timeValue)
+ DECLARE_FUNCTION(dinner)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(restaurant)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION_2(function41, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_3(function42, CarIndex car, EntityPosition entityPosition, bool)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+ DECLARE_FUNCTION(function63)
+ DECLARE_FUNCTION(function64)
+ DECLARE_FUNCTION(function65)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function68)
+ DECLARE_FUNCTION(unhookCars)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_AUGUST_H
diff --git a/engines/lastexpress/entities/boutarel.cpp b/engines/lastexpress/entities/boutarel.cpp
new file mode 100644
index 0000000000..411456c5c8
--- /dev/null
+++ b/engines/lastexpress/entities/boutarel.cpp
@@ -0,0 +1,1260 @@
+/* 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/entities/boutarel.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Boutarel::Boutarel(LastExpressEngine *engine) : Entity(engine, kEntityBoutarel) {
+ ADD_CALLBACK_FUNCTION(Boutarel, reset);
+ ADD_CALLBACK_FUNCTION(Boutarel, playSound);
+ ADD_CALLBACK_FUNCTION(Boutarel, draw);
+ ADD_CALLBACK_FUNCTION(Boutarel, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Boutarel, updatePosition);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Boutarel, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Boutarel, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Boutarel, updateEntity);
+ ADD_CALLBACK_FUNCTION(Boutarel, function11);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterTableWithMmeBoutarel);
+ ADD_CALLBACK_FUNCTION(Boutarel, leaveTableWithMmeBoutarel);
+ ADD_CALLBACK_FUNCTION(Boutarel, function14);
+ ADD_CALLBACK_FUNCTION(Boutarel, function15);
+ ADD_CALLBACK_FUNCTION(Boutarel, function16);
+ ADD_CALLBACK_FUNCTION(Boutarel, function17);
+ ADD_CALLBACK_FUNCTION(Boutarel, function18);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter1);
+ ADD_CALLBACK_FUNCTION(Boutarel, function20);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function22);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter2);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function25);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter3);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function28);
+ ADD_CALLBACK_FUNCTION(Boutarel, function29);
+ ADD_CALLBACK_FUNCTION(Boutarel, function30);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter4);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function33);
+ ADD_CALLBACK_FUNCTION(Boutarel, function34);
+ ADD_CALLBACK_FUNCTION(Boutarel, function35);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter5);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function38);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Boutarel, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Boutarel, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Boutarel, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Boutarel, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(5, Boutarel, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Boutarel, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, Boutarel, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarRedSleeping, kObjectCompartmentC, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Boutarel, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Boutarel, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Boutarel, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+
+ if (getInventory()->hasItem(kItemPassengerList) && getState()->time > kTime1089000)
+ getSound()->playSound(kEntityPlayer, "CAT1022");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Boutarel, function11, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1) && ENTITY_PARAM(0, 2)) {
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 1) = 0;
+
+ setCallback(5);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ if (params->param1) {
+ if (getProgress().chapter == kChapter4) {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("607Hc", kObjectCompartmentC);
+ } else {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("607Dc", kObjectCompartmentC);
+ }
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("607Bc", kObjectCompartmentC);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 2:
+ case 3:
+ if (getCallback() == 2)
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ else
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ // Fallback to next case
+
+ case 1:
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityBoutarel, kEntityFrancois, kAction101107728);
+
+ setCallback(4);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityBoutarel);
+ break;
+
+ case 5:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_draw("812US");
+ break;
+
+ case 6:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getSound()->playSound(kEntityBoutarel, "MRB1075", SoundManager::kFlagInvalid, 60);
+ break;
+
+ case kChapter3:
+ getSound()->playSound(kEntityBoutarel, "MRB3101");
+ break;
+ }
+
+ setCallback(7);
+ setup_enterTableWithMmeBoutarel();
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Boutarel, enterTableWithMmeBoutarel)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getSavePoints()->push(kEntityBoutarel, kEntityTables2, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables2, "008A3");
+ getEntities()->drawSequenceRight(kEntityMmeBoutarel, "008A2");
+ getEntities()->drawSequenceRight(kEntityBoutarel, "008A1");
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->updateFrame(kEntityBoutarel);
+ getEntityData(kEntityMmeBoutarel)->location = getData()->location;
+ getEntityData(kEntityTables2)->location = getData()->location;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Boutarel, leaveTableWithMmeBoutarel)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityBoutarel, kEntityTables2, kActionDrawTablesWithChairs, "008F");
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables2, "008E3");
+ getEntities()->drawSequenceRight(kEntityMmeBoutarel, "008E2");
+ getEntities()->drawSequenceRight(kEntityBoutarel, "008E1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Boutarel, function14, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityBoutarel, "MRB1079");
+
+ setCallback(2);
+ setup_leaveTableWithMmeBoutarel();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction326144276);
+ getEntities()->drawSequenceRight(kEntityBoutarel, "812DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityBoutarel);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntityData(kEntityFrancois)->entityPosition = kPosition_540;
+ getEntityData(kEntityMmeBoutarel)->entityPosition = kPosition_540;
+ getData()->entityPosition = kPosition_540;
+
+ getEntityData(kEntityFrancois)->location = kLocationOutsideCompartment;
+ getEntityData(kEntityMmeBoutarel)->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityBoutarel);
+ getSavePoints()->push(kEntityBoutarel, kEntityMmeBoutarel, kAction100901266);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityBoutarel, kEntityFrancois, kAction100901266);
+
+ setCallback(5);
+ setup_updateFromTime(450);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ setCallback(params->param1 ? 7: 8);
+ setup_enterExitCompartment2(params->param1 ? "607Gc" : "607Ac", kObjectCompartmentC);
+ break;
+
+ case 7:
+ case 8:
+ getEntities()->clearSequences(kEntityBoutarel);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Boutarel, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (params->param1)
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(params->param1 ? 1 : 2);
+ setup_enterExitCompartment(params->param1 ? "607Dc" : "607Bc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition(params->seq, kCarRestaurant, 52);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters:
+// bool
+// const char *
+IMPLEMENT_FUNCTION_IS(16, Boutarel, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition((const char *)&params->seq, kCarRestaurant, 52);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(params->param1 ? 4 : 5);
+ setup_enterExitCompartment2(params->param1 ? "607Gc" : "607Ac", kObjectCompartmentC);
+ break;
+
+ case 4:
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(17, Boutarel, function17, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_ACTION(params->param1, params->param6);
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 90)
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ } else {
+ params->param7 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, (char *)&params->seq);
+ break;
+
+ case kActionDrawScene:
+ params->param5 = (getEntities()->isPlayerPosition(kCarRestaurant, 52) ? 1 : 0);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(18, Boutarel, function18, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param4) {
+ params->param4 = 1;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (savepoint.param.intValue == 50) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1511" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "MRB1001" : "MRB1001A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+
+ params->param2 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction123199584);
+ break;
+
+ }
+ break;
+
+ case kAction122865568:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction88652208);
+ break;
+
+ case kAction221683008:
+ setCallback(7);
+ setup_playSound("MRB1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Boutarel, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityBoutarel, kAction203520448, 0);
+ getSavePoints()->addData(kEntityBoutarel, kAction237889408, 1);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject42, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_1750;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Boutarel, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (!params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 4500)
+ setCallback(3);
+ setup_playSound("MRB1078A");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+
+ setCallback(2);
+ setup_playSound("MRB1076");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ break;
+
+ case 3:
+ TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityBoutarel, kEntityCooks, kAction224849280);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction134466544:
+ params->param2 = 0;
+ break;
+
+ case kAction135854206:
+ params->param2 = 1;
+ break;
+
+ case kAction168717392:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+
+ if (!params->param2) {
+ setCallback(5);
+ setup_playSound("MRB1078");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Boutarel, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kTime1071000, "101A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16(false, "101B");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18(kTime1102500);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 54) || getEntities()->isPlayerPosition(kCarRedSleeping, 44))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 10);
+
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 54);
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 44);
+
+ setCallback(4);
+ setup_playSound("MRB1074");
+ break;
+
+ case 4:
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 54);
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 44);
+
+ setCallback(5);
+ setup_function20();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function18(kTimeEnterChalons);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function15(false, "102A");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function17(kTime1183500, "102B");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function16(false, "102C");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function18(kTime1215000);
+ break;
+
+ case 10:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Boutarel, function22)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityBoutarel);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Boutarel, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Boutarel, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1759500, params->param2, 1, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInRestaurant(kEntityPlayer) && !params->param1) {
+ getSound()->playSound(kEntityBoutarel, "MRB2001");
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function25();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Boutarel, function25)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Boutarel, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Boutarel, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122288808:
+ setup_function28();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Boutarel, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(true);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function29();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Boutarel, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 450)
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ UPDATE_PARAM_PROC_END
+
+ if (!params->param1)
+ break;
+
+ if (getEntities()->isInRestaurant(kEntityAnna)
+ && getEntities()->isInRestaurant(kEntityAugust)
+ && !getSound()->isBuffered(kEntityBoutarel)
+ && params->param3 != kTimeInvalid) {
+
+ if (getState()->time <= kTime1998000)
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param3)
+ params->param3 = getState()->time + 450;
+
+ if (params->param3 < getState()->time || getState()->time > kTime1998000) {
+ params->param3 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("MRB3102");
+ break;
+ }
+ }
+
+ TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true);
+ break;
+
+ case 2:
+ setup_function30();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ params->param1 = 1;
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Boutarel, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Boutarel, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Boutarel, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2367000, params->param1, setup_function33);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Boutarel, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1)
+ TIME_CHECK_CALLBACK_1(kTime2389500, params->param2, 3, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+
+ setCallback(2);
+ setup_updateFromTime(450);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ break;
+
+ case 3:
+ setup_function34();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Boutarel, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2470500, params->param1, setup_function35);
+
+ if (getState()->time > kTime2457000 && getEvent(kEventAugustDrink)) {
+ getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction159003408);
+
+ setCallback(1);
+ setup_function15(false, "102A");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction101687594);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(kTime2479500, "102B");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(false, "102C");
+ break;
+
+ case 3:
+ case 7:
+ setup_function35();
+ break;
+
+ case 4:
+ case 8:
+ if (getState()->time >= kTime2470500) {
+ setup_function35();
+ break;
+ }
+
+ if (getEvent(kEventAugustDrink)) {
+ setCallback(5);
+ setup_function15(false, "102A");
+ } else {
+ setCallback(8);
+ setup_function18((TimeValue)(getState()->time + 900));
+ }
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17(kTime2479500, "102B");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function16(false, "102C");
+ break;
+
+ case 9:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction123199584);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction88652208);
+ break;
+
+ case kAction125039808:
+ setCallback(4);
+ setup_function18(kTime2457000);
+ break;
+
+ case kAction221683008:
+ setCallback(9);
+ setup_playSound("Mrb1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Boutarel, function35)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Boutarel, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Boutarel, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function38();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Boutarel, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(39, Boutarel)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/boutarel.h b/engines/lastexpress/entities/boutarel.h
new file mode 100644
index 0000000000..22f8e62b62
--- /dev/null
+++ b/engines/lastexpress/entities/boutarel.h
@@ -0,0 +1,188 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_BOUTAREL_H
+#define LASTEXPRESS_BOUTAREL_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Boutarel : public Entity {
+public:
+ Boutarel(LastExpressEngine *engine);
+ ~Boutarel() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function11, bool)
+ DECLARE_FUNCTION(enterTableWithMmeBoutarel)
+ DECLARE_FUNCTION(leaveTableWithMmeBoutarel)
+ DECLARE_FUNCTION_1(function14, bool)
+ DECLARE_FUNCTION_2(function15, bool, const char *sequence)
+ DECLARE_FUNCTION_2(function16, bool, const char *sequence)
+ DECLARE_FUNCTION_2(function17, TimeValue timeValue, const char *sequence)
+ DECLARE_FUNCTION_1(function18, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function38)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BOUTAREL_H
diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp
new file mode 100644
index 0000000000..1bf3ee3f71
--- /dev/null
+++ b/engines/lastexpress/entities/chapters.cpp
@@ -0,0 +1,1810 @@
+/* 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/entities/chapters.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/coudert.h"
+#include "lastexpress/entities/cooks.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/tatiana.h"
+#include "lastexpress/entities/vassili.h"
+#include "lastexpress/entities/verges.h"
+#include "lastexpress/entities/vesna.h"
+#include "lastexpress/entities/yasmin.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+Chapters::Chapters(LastExpressEngine *engine) : Entity(engine, kEntityChapters) {
+ ADD_CALLBACK_FUNCTION(Chapters, savegame);
+ ADD_CALLBACK_FUNCTION(Chapters, enterStation);
+ ADD_CALLBACK_FUNCTION(Chapters, exitStation);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1);
+ ADD_CALLBACK_FUNCTION(Chapters, resetMainEntities);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1End);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Next);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, viennaEvents);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter5);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Handler);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(1, Chapters, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Chapters, enterStation, CityIndex)
+ enterExitStation(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Chapters, exitStation)
+ enterExitStation(savepoint, false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Chapters, chapter1)
+ if (savepoint.action == kActionDefault) {
+ getSavePoints()->addData(kEntityChapters, kAction171843264, 0);
+ setup_chapter1Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Chapters, resetMainEntities)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_reset);
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_reset);
+ RESET_ENTITY_STATE(kEntityAlouan, Alouan, setup_reset);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_reset);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_reset);
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_reset);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_reset);
+ RESET_ENTITY_STATE(kEntityFrancois, Francois, setup_reset);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_reset);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_reset);
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_reset);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_reset);
+ RESET_ENTITY_STATE(kEntityMahmud, Mahmud, setup_reset);
+ RESET_ENTITY_STATE(kEntityMax, Max, setup_reset);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_reset);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_reset);
+ RESET_ENTITY_STATE(kEntityGendarmes, Gendarmes, setup_reset);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_reset);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_reset);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_reset);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_reset);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_reset);
+ RESET_ENTITY_STATE(kEntityVassili, Vassili, setup_reset);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_reset);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_reset);
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6,Chapters, chapter1End)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityChapters, "MUS0009", SoundManager::kFlagDefault);
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ if (params->param1) {
+ getEntities()->clearSequences(kEntityChapters);
+ getSound()->processEntry(kEntityChapters);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getSound()->resetState();
+
+ ENTITY_PARAM(0, 4) = 7;
+
+ getSound()->playSteam(kCityPolice);
+
+ getAction()->playAnimation(kEventCathDream);
+
+ getState()->timeDelta = 3;
+ getProgress().field_18 = 1;
+ getProgress().field_84 = 0;
+
+ getObjects()->update(kObject63, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ } else {
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getSound()->playSound(kEntityPlayer, "LIB015", SoundManager::kFlagDefault, 15);
+
+ if (!getSound()->isBuffered(kEntityChapters))
+ getSound()->playSound(kEntityChapters, "MUS009", SoundManager::kFlagDefault);
+
+ getScenes()->loadSceneFromPosition(kCarLocomotive, 38);
+
+ getObjects()->update(kObject63, kEntityChapters, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ RESET_ENTITY_STATE(kEntityPascale, Pascale, setup_function19);
+ RESET_ENTITY_STATE(kEntityServers0, Servers0, setup_function22);
+ RESET_ENTITY_STATE(kEntityServers1, Servers1, setup_function16);
+ RESET_ENTITY_STATE(kEntityCooks, Cooks, setup_function7);
+
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_function42);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_chapter1Handler);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_chapter1Handler);
+
+ getSavePoints()->push(kEntityChapters, kEntityMertens, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ RESET_ENTITY_STATE(kEntityKronos, Kronos, setup_function10);
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function13);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function34);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function34);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function24);
+ RESET_ENTITY_STATE(kEntityVassili, Vassili, setup_function7);
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function26);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_function18);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_function15);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_function17);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function11);
+ RESET_ENTITY_STATE(kEntityFrancois, Francois, setup_function20);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_function16);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_function22);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function27);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_function5);
+ RESET_ENTITY_STATE(kEntityMahmud, Mahmud, setup_resetChapter);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_function10);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_function12);
+ RESET_ENTITY_STATE(kEntityHadija, Alouan, setup_function12);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->processEntries();
+
+ if (getSound()->isBuffered("CON1505"))
+ getSound()->processEntry("CON1505");
+
+ if (getSound()->isBuffered("AUG1057"))
+ getSound()->processEntry("AUG1057");
+
+ if (getSound()->isBuffered("ZFX1005"))
+ getSound()->processEntry("ZFX1005");
+
+ if (getSound()->isBuffered("ZFX1006"))
+ getSound()->processEntry("ZFX1006");
+
+ if (getSound()->isBuffered("ZFX1007"))
+ getSound()->processEntry("ZFX1007");
+
+ if (getSound()->isBuffered("ZFX1007A"))
+ getSound()->processEntry("ZFX1007A");
+
+ if (getSound()->isBuffered("ZFX1007B"))
+ getSound()->processEntry("ZFX1007B");
+
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ // FIXME add event pump ?
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ getProgress().field_84 = true;
+
+ getScenes()->loadSceneFromPosition(kCarLocomotive, 75);
+ getInventory()->show();
+
+ getState()->time = kTime1492200;
+ getProgress().field_18 = 4;
+ getState()->timeDelta = 0;
+
+ getObjects()->update(kObject63, kEntityChapters, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ getProgress().isTrainRunning = false;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kAction225358684:
+ params->param2++;
+
+ if (params->param2 >= 3) {
+
+ if (!getSound()->isBuffered("LIB031", true))
+ getSound()->playSound(kEntityPlayer, "LIB031");
+
+ if (params->param2 == 3) {
+ getData()->car = kCarGreenSleeping;
+ getEntities()->drawSequenceLeft(kEntityChapters, "JUGL");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Chapters, chapter1Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getProgress().chapter = kChapter1;
+ getSound()->resetState();
+
+ getState()->time = kTimeChapter1;
+ getState()->timeDelta = 0;
+ getProgress().isTrainRunning = true;
+ getProgress().portrait = kPortraitOriginal;
+ getProgress().field_18 = 1;
+
+ // Setup inventory & items location
+ getInventory()->addItem(kItemTelegram);
+ getInventory()->addItem(kItemArticle);
+
+ getInventory()->setLocationAndProcess(kItemScarf, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemParchemin, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemGreenJacket, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemCorpse, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemPassengerList, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem22, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemPaper, kObjectLocation1);
+
+ getProgress().field_7C = 1;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ for (uint i = kObjectCompartment1; i <= kObjectCompartment8; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ for (uint i = kObjectCompartmentA; i <= kObjectCompartmentH; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ params->param1 = 40;
+
+ getObjects()->updateLocation2(kObject25, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectTrainTimeTable, kObjectLocation1);
+ getObjects()->updateLocation2(kObject98, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation1);
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectRedSleepingCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject28, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject56, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject54, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject59, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject66, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject64, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject69, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject98, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHandKnock);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHandKnock);
+ getObjects()->update(kObject101, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_chapter1Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+#define PLAY_STEAM() { \
+ getSound()->resetState(); \
+ getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); \
+ ENTITY_PARAM(0, 2) = 0; \
+ }
+
+IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().isTrainRunning || getState()->time >= kTime1458000)
+ goto label_processStations;
+
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param2)
+ // Play sound FX
+ getSound()->playLocomotiveSound();
+
+ params->param2 = 225 * (4 * rnd(5) + 20);
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+
+label_processStations:
+ // Process stations
+ TIME_CHECK_SAVEGAME(kTime1039500, params->param7, 1, kSavegameTypeTime, kTimeNone);
+
+label_enter_epernay:
+ // Entering Epernay station
+ TIME_CHECK_ENTERSTATION(kTimeEnterEpernay, params->param8, 1, "Epernay", kCityEpernay);
+
+label_exit_epernay:
+ // Exiting Epernay station
+ TIME_CHECK_EXITSTATION_2(kTimeExitEpernay, CURRENT_PARAMS(1, 1), params->param4, 3, "Epernay");
+
+label_epernay_police:
+ TIME_CHECK_EXITSTATION_0(params->param5, ENTITY_PARAM(0, 2), 4, "Unschedu");
+
+label_enter_chalons:
+ if (getState()->time > kTimeEnterChalons && !CURRENT_PARAMS(1, 2)) {
+ CURRENT_PARAMS(1, 2) = 1;
+ getProgress().field_18 = 2;
+ }
+
+ // Skip to callback 18 checks
+ if (params->param1)
+ goto label_exit_strasbourg;
+
+ // Entering Chalons station
+ TIME_CHECK_ENTERSTATION(kTimeEnterChalons, CURRENT_PARAMS(1, 3), 5, "Chalons", kCityChalons);
+
+label_exit_chalons:
+ // Exiting Chalons station
+ TIME_CHECK_EXITSTATION(kTimeExitChalons, CURRENT_PARAMS(1, 4), 6, "Chalons");
+
+label_enter_barleduc:
+ // Entering Bar-Le-Duc station
+ TIME_CHECK_ENTERSTATION(kTimeCityBarLeDuc, CURRENT_PARAMS(1, 5), 7, "BarLeDuc", kCityBarleduc);
+
+label_exit_barleduc:
+ // Exiting Bar-Le-Duc station
+ TIME_CHECK_EXITSTATION(kTimeExitBarLeDuc, CURRENT_PARAMS(1, 6), 8, "BarLeDuc");
+
+label_enter_nancy:
+ if (getState()->time > kTime1260000 && !CURRENT_PARAMS(1, 7)) {
+ CURRENT_PARAMS(1, 7) = 1;
+ getState()->timeDelta = 1;
+ }
+
+ // Entering Nancy station
+ TIME_CHECK_ENTERSTATION(kTimeCityNancy, CURRENT_PARAMS(1, 8), 9, "Nancy", kCityNancy);
+
+label_exit_nancy:
+ // Exiting Nancy station
+ TIME_CHECK_EXITSTATION(kTimeExitNancy, CURRENT_PARAMS(2, 1), 10, "Nancy");
+
+label_enter_luneville:
+ // Entering Luneville station
+ TIME_CHECK_ENTERSTATION(kTimeCityLuneville, CURRENT_PARAMS(2, 2), 11, "Luneville", kCityLuneville);
+
+label_exit_luneville:
+ // Exiting Luneville station
+ TIME_CHECK_EXITSTATION(kTimeExitLuneville, CURRENT_PARAMS(2, 3), 12, "Luneville");
+
+label_enter_avricourt:
+ // Entering Avricourt station
+ TIME_CHECK_ENTERSTATION(kTimeCityAvricourt, CURRENT_PARAMS(2, 4), 13, "Avricourt", kCityAvricourt);
+
+label_exit_avricourt:
+ // Exiting Avricourt station
+ TIME_CHECK_EXITSTATION(kTimeExitAvricourt, CURRENT_PARAMS(2, 5), 14, "Avricourt");
+
+label_enter_deutschavricourt:
+ // Entering Deutsch-Avricourt station
+ TIME_CHECK_ENTERSTATION(kTimeCityDeutschAvricourt, CURRENT_PARAMS(2, 6), 15, "DeutschA", kCityDeutschAvricourt);
+
+label_exit_deutschavricourt:
+ // Exiting Avricourt station
+ TIME_CHECK_EXITSTATION(kTimeExitDeutschAvricourt, CURRENT_PARAMS(2, 7), 16, "DeutschA");
+
+label_enter_strasbourg:
+ TIME_CHECK_SAVEGAME(kTimeCityStrasbourg, CURRENT_PARAMS(2, 8), 17, kSavegameTypeTime, kTimeNone);
+
+label_exit_strasbourg:
+ // Exiting Strasbourg station
+ TIME_CHECK_EXITSTATION(kTimeExitStrasbourg, CURRENT_PARAMS(3, 1), 19, "Strasbou");
+
+label_enter_badenoos:
+ // Entering Baden Oos station
+ TIME_CHECK_ENTERSTATION(kTimeCityBadenOos, CURRENT_PARAMS(3, 2), 20, "BadenOos", kCityBadenOos);
+
+label_exit_badenoos:
+ // Exiting Baden Oos station
+ TIME_CHECK_EXITSTATION(kTimeExitBadenOos, CURRENT_PARAMS(3, 3), 21, "BadenOos");
+
+label_chapter1_next:
+ if (getState()->time > kTimeChapter1End3 && ! CURRENT_PARAMS(3, 4)) {
+ CURRENT_PARAMS(3, 4) = 1;
+ setup_chapter1Next();
+ }
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car < kCarBaggageRear || car > kCarGreenSleeping) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+
+ if (params->param4) {
+ getSavePoints()->push(kEntityChapters, getProgress().field_24 ? kEntityVerges : kEntityMertens, getProgress().field_24 ? kAction168187490 : kAction224122407);
+ params->param4 = 0;
+ }
+ }
+ break;
+
+ case kActionDefault:
+ params->param2 = 225 * (4 * rnd(5) + 20);
+ break;
+
+ case kActionDrawScene:
+ if (!params->param3) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ getState()->time = kTimeChapter1;
+ getState()->timeDelta = 3;
+ params->param3 = 1;
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_enter_epernay;
+
+ case 2:
+ goto label_exit_epernay;
+
+ case 3:
+ goto label_epernay_police;
+
+ case 4:
+ params->param5 = 0;
+ goto label_enter_chalons;
+
+ case 5:
+ goto label_exit_chalons;
+
+ case 6:
+ goto label_enter_barleduc;
+
+ case 7:
+ goto label_exit_barleduc;
+
+ case 8:
+ goto label_enter_nancy;
+
+ case 9:
+ goto label_exit_nancy;
+
+ case 10:
+ goto label_enter_luneville;
+
+ case 11:
+ goto label_exit_luneville;
+
+ case 12:
+ goto label_enter_avricourt;
+
+ case 13:
+ goto label_exit_avricourt;
+
+ case 14:
+ goto label_enter_deutschavricourt;
+
+ case 15:
+ goto label_exit_deutschavricourt;
+
+ case 16:
+ getState()->time = kTimeEnterStrasbourg;
+ goto label_enter_strasbourg;
+
+ case 17:
+ getProgress().field_18 = 1;
+ setCallback(18);
+ setup_enterStation("Strasbou", kCityStrasbourg);
+ break;
+
+ case 18:
+ goto label_exit_strasbourg;
+
+ case 19:
+ getState()->timeDelta = 1;
+ goto label_enter_badenoos;
+
+ case 20:
+ goto label_exit_badenoos;
+
+ case 21:
+ goto label_chapter1_next;
+
+ case 22:
+ params->param5 = 1;
+ break;
+
+ case 23:
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction169629818:
+ setCallback(22);
+ setup_enterStation("Unschedu", kCityPolice);
+ break;
+
+ case kActionEndChapter:
+ getProgress().field_18 = 3;
+
+ if (getState()->time >= kTimeChapter1End) {
+ setup_chapter1Next();
+ } else {
+ setCallback(23);
+ setup_chapter1End();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Chapters, chapter1Next)
+ if (savepoint.action == kActionDefault) {
+ // Reset sound cache
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ setup_chapter2();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Chapters, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Setup for chapter 2 in case it hasn't been done before
+ if (getProgress().chapter != kChapter2) {
+ getProgress().chapter = kChapter2;
+ getEntities()->setupChapter(kChapter2);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter2;
+ getState()->timeDelta = 5;
+
+ // Save game
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!_engine->getResourceManager()->loadArchive(kArchiveCd2)) {
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+ return;
+ }
+
+ // Load scene data
+ getScenes()->loadSceneDataFile(kArchiveCd2);
+ setup_chapter2Init();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Chapters, chapter2Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getProgress().eventCorpseMovedFromFloor = true;
+ getProgress().field_18 = 1;
+ getProgress().isTrainRunning = true;
+ getProgress().eventCorpseFound = true;
+
+ // Switch to green jacket/portrait
+ getProgress().jacket = kJacketGreen;
+ getProgress().portrait = kPortraitGreen;
+
+ // Setup inventory & items location
+ getInventory()->addItem(kItemGreenJacket);
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getInventory()->setLocationAndProcess(kItemBeetle, kObjectLocation3);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ for (uint i = 1; i < 9; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ for (uint i = 33; i < 40; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ params->param1 = 40;
+
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ // Reset sound cache
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getAction()->playAnimation(kEventTrainPassing);
+
+ if (getInventory()->hasItem(kItemScarf))
+ getScenes()->loadScene(kScene41);
+ else
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 79);
+
+ setup_chapter2Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Chapters, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().isTrainRunning)
+ break;
+
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1);
+
+ getSound()->playLocomotiveSound();
+
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ break;
+
+ case kActionChapter3:
+ setup_chapter3();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Chapters, chapter3)
+ if (savepoint.action == kActionDefault) {
+ // Setup for chapter 3 in case it hasn't been done before
+ if (getProgress().chapter != kChapter3) {
+ getProgress().chapter = kChapter3;
+ getEntities()->setupChapter(kChapter3);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter3;
+ getState()->timeDelta = 5;
+
+ setup_chapter3Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Chapters, chapter3Init)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables5, kActionDrawTablesWithChairs);
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment1, kObjectLocation2);
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation3, kCursorKeepValue, kCursorKeepValue);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 60);
+ getInventory()->show();
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter3Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().isTrainRunning) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, params->param1)
+ getSound()->playLocomotiveSound();
+
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param4 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, params->param2)
+ switch (rnd(2)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityPlayer, "ZFX1008", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "ZFX1009", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+ }
+
+ params->param2 = 225 * (4 * rnd(6) + 8);
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_ENTERSTATION(kTimeEnterSalzbourg, params->param6, 1, "Salzburg", kCitySalzbourg);
+
+label_callback_1:
+ TIME_CHECK_EXITSTATION(kTimeExitSalzbourg, params->param7, 2, "Salzburg");
+
+label_callback_2:
+ TIME_CHECK_ENTERSTATION(kTimeEnterAttnangPuchheim, params->param8, 3, "Attnang", kCityAttnangPuchheim);
+
+label_callback_3:
+ TIME_CHECK_EXITSTATION(kTimeExitAttnangPuchheim, CURRENT_PARAMS(1, 1), 4, "Attnang");
+
+label_callback_4:
+ TIME_CHECK_ENTERSTATION(kTimeEnterWels, CURRENT_PARAMS(1, 2), 5, "Wels", kCityWels);
+
+label_callback_5:
+ TIME_CHECK_EXITSTATION(kTimeEnterWels, CURRENT_PARAMS(1, 3), 6, "Wels");
+
+label_callback_6:
+ TIME_CHECK_ENTERSTATION(kTimeEnterLinz, CURRENT_PARAMS(1, 4), 7, "Linz", kCityLinz);
+
+label_callback_7:
+ TIME_CHECK_EXITSTATION(kTimeCityLinz, CURRENT_PARAMS(1, 5), 8, "Linz");
+
+label_callback_8:
+ if (getState()->time > kTime2187000 && !CURRENT_PARAMS(1, 6)) {
+ CURRENT_PARAMS(1, 6) = 1;
+ getState()->timeDelta = 5;
+ }
+
+ TIME_CHECK_ENTERSTATION(kTimeCityVienna, CURRENT_PARAMS(1, 7), 9, "Vienna", kCityVienna);
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location == kLocationOutsideTrain) {
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ } else if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ } else {
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car >= kCarBaggageRear && car <= kCarGreenSleeping) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ }
+ }
+ } else {
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ }
+ }
+ }
+
+ getSound()->resetState();
+ getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4));
+
+ ENTITY_PARAM(0, 2) = 0;
+ if (params->param1)
+ setup_viennaEvents();
+
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param2 = 225 * (4 * rnd(6) + 8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ params->param3 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Chapters, viennaEvents)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventViennaAugustUnloadGuns);
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ getLogic()->gameOver(kSavegameTypeTime, kTime2187000, kSceneNone, true);
+ else if (getEvent(kEventCathJumpDownCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent, kEventCathJumpDownCeiling, kSceneNone, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventViennaKronosFirebird);
+ if (getEvent(kEventKronosBringEggCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventKronosBringEggCeiling, kSceneGameOverVienna1, true);
+ else if (getEvent(kEventKronosBringEgg)) {
+ if (getEvent(kEventKronosBringEggCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventKronosBringEggCeiling, kSceneGameOverVienna1, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna1, true);
+ } else {
+ if (getProgress().field_C0) {
+ if (getEvent(kEventKronosReturnBriefcase))
+ getLogic()->gameOver(kSavegameTypeTime, getProgress().field_C0, kSceneGameOverVienna2, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna2, true);
+ } else {
+ if (getEvent(kEventKronosReturnBriefcase))
+ getLogic()->gameOver(kSavegameTypeEvent, kEventKronosReturnBriefcase, kSceneGameOverVienna, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna, true);
+ }
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventVergesAnnaDead);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2250000, kSceneGameOverAnnaDied, true);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventViennaContinueGame);
+ setup_chapter4();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Chapters, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Setup for chapter 4 in case it hasn't been done before
+ if (getProgress().chapter != kChapter4) {
+ getProgress().chapter = kChapter4;
+ getEntities()->setupChapter(kChapter4);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter4;
+ getState()->timeDelta = 5;
+
+ // Save game
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!_engine->getResourceManager()->loadArchive(kArchiveCd3)) {
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+ return;
+ }
+
+ // Load scene data
+ getScenes()->loadSceneDataFile(kArchiveCd3);
+ setup_chapter4Init();
+ }
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Chapters, chapter4Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getSound()->processEntries();
+ getSound()->resetState();
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStartRunning);
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables5, kActionDrawTablesWithChairs);
+
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ getInventory()->setLocationAndProcess(kItemBomb, kObjectLocation1);
+
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3)
+ getScenes()->loadSceneFromItemPosition(kItemBeetle);
+
+ getObjects()->updateLocation2(kObject25, kObjectLocation2);
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation3, kCursorKeepValue, kCursorKeepValue);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 76);
+ else
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+
+ getInventory()->show();
+ setup_chapter4Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().isTrainRunning) {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param4);
+ getSound()->playLocomotiveSound();
+
+ params->param4 = 225 * (4 * rnd(5) + 20);
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, params->param5)
+ switch (rnd(2)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityPlayer, "ZFX1008", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "ZFX1009", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+ }
+
+ params->param5 = 225 * (4 * rnd(6) + 8);
+ params->param7 = 0;
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_ENTERSTATION(kTimeEnterPoszony, params->param8, 1, "Pozsony", kCityPoszony);
+
+label_exitPozsony:
+ TIME_CHECK_EXITSTATION(kTimeExitPoszony, CURRENT_PARAMS(1, 1), 2, "Pozsony");
+
+label_enterGalanta:
+ if (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) {
+ if (getState()->time > kTime2403000 && !CURRENT_PARAMS(1, 2)) {
+ CURRENT_PARAMS(1, 2) = 1;
+ getProgress().field_18 = 2;
+ }
+ }
+
+ if (params->param1)
+ goto label_callback_4;
+
+ TIME_CHECK_ENTERSTATION(kTimeEnterGalanta, CURRENT_PARAMS(1, 3), 3, "Galanta", kCityGalanta);
+
+label_exitGalanta:
+ TIME_CHECK_EXITSTATION(kTimeExitGalanta, CURRENT_PARAMS(1, 4), 4, "Galanta");
+
+label_callback_4:
+ if (getState()->time > kTime2470500 && !CURRENT_PARAMS(1, 5)) {
+ CURRENT_PARAMS(1, 5) = 1;
+
+ if (getProgress().field_18 == 2)
+ getState()->timeDelta = 1;
+ }
+
+ if (getState()->time > kTime2506500 && !CURRENT_PARAMS(1, 6)) {
+ CURRENT_PARAMS(1, 6) = 1;
+
+ if (getProgress().field_18 == 2)
+ getProgress().field_18 = 1;
+ }
+
+ if (getState()->time > kTime2520000 && !CURRENT_PARAMS(1, 7)) {
+ CURRENT_PARAMS(1, 7) = 1;
+
+ if (!params->param2 && !params->param3) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventTrainExplosionBridge);
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car < kCarBaggageRear || car > kCarGreenSleeping) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+ } else if (!params->param2 && !params->param3) {
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ }
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntityChapters);
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionDefault:
+ params->param4 = 225 * (4 * rnd(5) + 20);
+ params->param5 = 225 * (4 * rnd(6) + 8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_exitPozsony;
+
+ case 2:
+ goto label_enterGalanta;
+
+ case 3:
+ goto label_exitGalanta;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getAction()->playAnimation(kEventTrainExplosionBridge);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 6:
+ getSound()->processEntries();
+ getAction()->playAnimation(kEventTylerCastleDream);
+ getSound()->resetState();
+
+ getProgress().field_18 = 1;
+
+ getScenes()->loadScene(kScene41);
+ getSavePoints()->push(kEntityChapters, kEntityTatiana, kAction169360385);
+
+ getState()->timeDelta = 1;
+ getState()->time = kTime2511900;
+
+ getInventory()->setLocationAndProcess(kItem2, kObjectLocation1);
+ getScenes()->loadSceneFromItemPosition(kItem22);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventTrainExplosionBridge);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2430000, kSceneNone, true);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityPlayer, "MUS022");
+
+ if (getState()->time < kTime2517300)
+ getState()->time = kTime2517300;
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventCathDefusingBomb);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 73);
+ break;
+
+ case 10:
+ getAction()->playAnimation(kEventDefuseBomb);
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_function48);
+ getSavePoints()->push(kEntityChapters, kEntityAnna, kAction191001984);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction191001984);
+ getScenes()->loadSceneFromItemPosition(kItem2);
+
+ getInventory()->get(kItem2)->location = kObjectLocationNone;
+ params->param2 = 1;
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 2);
+ break;
+
+ case 11:
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 74);
+ getSound()->playSound(kEntityTrain, "ZFX4001", SoundManager::kFlagDefault);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+ break;
+
+ case kActionChapter5:
+ setup_chapter5();
+ break;
+
+ case kAction156435676:
+ getSavePoints()->push(kEntityChapters, kEntityTatiana, kAction169360385);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ getState()->timeDelta = 1;
+ getState()->time = kTime2511900;
+
+ getInventory()->setLocationAndProcess(kItem2, kObjectLocation1);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ break;
+
+ case kAction158610240:
+ setCallback(8);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kAction169300225:
+ if (getState()->time < kTime2519100)
+ getState()->time = kTime2519100;
+
+ params->param3 = 1;
+
+ getEntities()->drawSequenceRight(kEntityChapters, "BOMB");
+ break;
+
+ case kAction190346110:
+ getProgress().field_18 = 3;
+
+ params->param1 = 1;
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ if (getInventory()->hasItem(kItemBomb)) {
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function47);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function68);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function65);
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_function48);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function53);
+ RESET_ENTITY_STATE(kEntityServers0, Servers0, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityServers1, Servers1, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityPascale, Pascale, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function49);
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_function44);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_function32);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_function27);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_function29);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function22);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_function25);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_function35);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function45);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_function9);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_function17);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_function19);
+ RESET_ENTITY_STATE(kEntityAlouan, Alouan, setup_function19);
+ RESET_ENTITY_STATE(kEntityMax, Max, setup_chapter4Handler);
+ getSavePoints()->push(kEntityChapters, kEntityAnna, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityMertens, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityServers0, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityServers1, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityPascale, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventTylerCastleDream);
+ } else {
+ getState()->time = kTime2520000;
+
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventTrainExplosionBridge);
+ }
+ break;
+
+ case kAction191001984:
+ getState()->time = kTime2520000;
+
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getEntities()->clearSequences(kEntityChapters);
+ getInventory()->removeItem(kItemTelegram);
+
+ getState()->timeDelta = 5;
+
+ setCallback(10);
+ setup_savegame(kSavegameTypeEvent, kEventDefuseBomb);
+ break;
+
+ case kAction201959744:
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getSound()->playSound(kEntityTrain, "ZFX4001", SoundManager::kFlagDefault);
+
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ break;
+
+ case kAction225367984:
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventCathDefusingBomb);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Chapters, chapter5)
+ if (savepoint.action == kActionDefault) {
+ // Setup for chapter 5 in case it hasn't been done before
+ if (getProgress().chapter != kChapter5) {
+ getProgress().chapter = kChapter5;
+ getEntities()->setupChapter(kChapter5);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter5;
+ getState()->timeDelta = 2;
+
+ setup_chapter5Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Chapters, chapter5Init)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTables0);
+ getEntities()->clearSequences(kEntityTables1);
+ getEntities()->clearSequences(kEntityTables2);
+ getEntities()->clearSequences(kEntityTables3);
+ getEntities()->clearSequences(kEntityTables4);
+ getEntities()->clearSequences(kEntityTables5);
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectKitchen, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject20, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject21, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject22, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_18 = 1;
+ getProgress().field_84 = 1;
+ getProgress().portrait = kCursorPortraitYellow;
+
+ getInventory()->unselectItem();
+
+ getInventory()->removeItem(kItemKey);
+ getInventory()->removeItem(kItemBomb);
+ getInventory()->removeItem(kItemMatch);
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->setLocationAndProcess(kItemFirebird, kObjectLocation3);
+
+ if (getInventory()->hasItem(kItemWhistle)) {
+ getInventory()->removeItem(kItemWhistle);
+ getInventory()->setLocationAndProcess(kItemWhistle, kObjectLocation3);
+ }
+ }
+
+ getObjects()->update(kObject93, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject94, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject101, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getObjects()->updateLocation2(kObject98, kObjectLocation2);
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation2);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 95);
+ getInventory()->show();
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter5Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Chapters, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2907000 && !params->param2) {
+ params->param2 = 1;
+
+ if (!getProgress().isNightTime) {
+ getSound()->playSound(kEntityChapters, "ARRIVE", SoundManager::kFlag8);
+ getSound()->processEntries();
+ }
+ }
+
+ if (getState()->time > kTimeTrainStopped2 && !params->param3) {
+ params->param3 = 1;
+
+ if (!getEvent(kEventLocomotiveMilos) && !getEvent(kEventLocomotiveMilosNight)) {
+ getSound()->playSound(kEntityChapters, "ARRIVE", SoundManager::kFlag8);
+ getSound()->processEntries();
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ if (getState()->time <= kTimeTrainStopped2) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTrainStopped);
+ } else {
+ getLogic()->gameOver(kSavegameTypeTime, kTimeTrainStopped2, kSceneGameOverTrainStopped, true);
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(10) + 20);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventTrainStopped);
+ getLogic()->gameOver(kSavegameTypeTime, kTimeTrainStopped, kSceneGameOverTrainStopped, true);
+ }
+ break;
+
+ case kAction135800432:
+ getProgress().isNightTime = true;
+ getState()->time = kTime2916000;
+
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Chapters::enterExitStation(const SavePoint &savepoint, bool isEnteringStation) {
+ if (savepoint.action == kActionDefault) {
+ if (!ENTITY_PARAM(0, 2) && !ENTITY_PARAM(0, 3)) {
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getSound()->removeFromQueue(kEntityChapters);
+
+ if (!ENTITY_PARAM(0, 2)) {
+ if (ENTITY_PARAM(0, 3))
+ ENTITY_PARAM(0, 3) = 0;
+
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Green sleeping car
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Red sleeping car
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Other cars
+ if (getEntityData(kEntityPlayer)->car < kCarRedSleeping || getEntityData(kEntityPlayer)->car > kCarCoalTender) {
+
+ if (getEntityData(kEntityPlayer)->car < kCarBaggageRear || getEntityData(kEntityPlayer)->car > kCarGreenSleeping) {
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Chapters::enterExitHelper(bool isEnteringStation) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ getSound()->playSound(kEntityChapters, isEnteringStation ? "ARRIVE" : "DEPART", SoundManager::kFlag8);
+ getSound()->processEntries();
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, isEnteringStation ? kCursorNormal : kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, isEnteringStation ? kCursorNormal : kCursorHand);
+
+ getProgress().isTrainRunning = isEnteringStation ? false : true;
+
+ if (isEnteringStation) {
+ ENTITY_PARAM(0, 2) = 1;
+ ENTITY_PARAM(0, 4) = params->param4;
+ } else {
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStartRunning);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ CALLBACK_ACTION();
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h
new file mode 100644
index 0000000000..845fe8b727
--- /dev/null
+++ b/engines/lastexpress/entities/chapters.h
@@ -0,0 +1,166 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_CHAPTERS_H
+#define LASTEXPRESS_CHAPTERS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Chapters : public Entity {
+public:
+ Chapters(LastExpressEngine *engine);
+ ~Chapters() {};
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Exit a train station
+ *
+ * @param stationName The name of the train station
+ * @param index The index of the train station
+ */
+ DECLARE_FUNCTION_2(enterStation, const char *stationName, CityIndex index)
+
+ /**
+ * Exit a train station
+ *
+ * @param stationName The name of the train station
+ */
+ DECLARE_FUNCTION_1(exitStation, const char *stationName)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Reset main entities
+ */
+ DECLARE_FUNCTION(resetMainEntities)
+
+ /**
+ * Handle end of Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1End)
+
+ /**
+ * Init Chapter 1 data
+ */
+ DECLARE_FUNCTION(chapter1Init)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ /**
+ * Handle switching to Chapter 2 after the end of Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1Next)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Init Chapter 2 data
+ */
+ DECLARE_FUNCTION(chapter2Init)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Init Chapter 3 data
+ */
+ DECLARE_FUNCTION(chapter3Init)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Handle Vienna events
+ */
+ DECLARE_FUNCTION(viennaEvents)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Init Chapter 4 data
+ */
+ DECLARE_FUNCTION(chapter4Init)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Init Chapter 5 data
+ */
+ DECLARE_FUNCTION(chapter5Init)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+private:
+ void enterExitStation(const SavePoint &savepoint, bool isEnteringStation);
+ void enterExitHelper(bool isEnteringStation);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_CHAPTERS_H
diff --git a/engines/lastexpress/entities/cooks.cpp b/engines/lastexpress/entities/cooks.cpp
new file mode 100644
index 0000000000..2bc787b495
--- /dev/null
+++ b/engines/lastexpress/entities/cooks.cpp
@@ -0,0 +1,571 @@
+/* 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/entities/cooks.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Cooks::Cooks(LastExpressEngine *engine) : Entity(engine, kEntityCooks) {
+ ADD_CALLBACK_FUNCTION(Cooks, draw);
+ ADD_CALLBACK_FUNCTION(Cooks, playSound);
+ ADD_CALLBACK_FUNCTION(Cooks, function3);
+ ADD_CALLBACK_FUNCTION(Cooks, function4);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter1);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, function7);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter2);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter3);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter4);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(1, Cooks, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Cooks, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Cooks, function3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308A");
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+
+ switch (getProgress().chapter) {
+ default:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case kChapter1:
+ setCallback(1);
+ setup_playSound("KIT1010");
+ break;
+
+ case kChapter3:
+ setCallback(2);
+ setup_playSound("KIT1012");
+ break;
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer)) {
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 46)) {
+ getEntities()->drawSequenceLeft(kEntityCooks, "308D");
+
+ if (!getSound()->isBuffered(kEntityCooks)) {
+ if (params->param1) {
+ if (!getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ // Kitchen apprentice getting a lesson :D
+ getSound()->playSound(kEntityCooks, "KIT1011A");
+ params->param1 = 1;
+ }
+ }
+
+ if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308C");
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 78);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Cooks, function4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308A");
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ break;
+
+ case kChapter3:
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ break;
+ }
+
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer)) {
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 80)) {
+ getEntities()->drawSequenceLeft(kEntityCooks, "308D");
+
+ if (!getSound()->isBuffered(kEntityCooks)) {
+ if (params->param1) {
+ if (!getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ // Kitchen apprentice getting a lesson :D
+ getSound()->playSound(kEntityCooks, "KIT1011A");
+ params->param1 = 1;
+ }
+ }
+
+ if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308C");
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Cooks, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getProgress().field_4C = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Cooks, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param4, getState()->time, params->param2);
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ params->param4 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ if (params->param1) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 73)) {
+ setCallback(1);
+ setup_function3();
+ }
+ } else {
+ if (params->param3) {
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(3);
+ setup_playSound("ZFX1012");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ break;
+
+ case 2:
+ case 3:
+ params->param3 = !params->param3;
+ break;
+ }
+ break;
+
+ case kAction101632192:
+ setup_function7();
+ break;
+
+ case kAction224849280:
+ getProgress().field_4C = 1;
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Cooks, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Snoring...
+ setCallback(1);
+ setup_playSound("WAT1200");
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityCooks);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Cooks, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 1;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Cooks, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param1);
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2) {
+ setCallback(1);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(2);
+ setup_playSound("ZFX1012");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2)
+ params->param2 = !params->param2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Cooks, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Cooks, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param4, getState()->time, params->param2)
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ params->param4 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (getState()->time > kTime2079000 && !params->param5) {
+ params->param1 = 0;
+ params->param5 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ if (params->param1) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 80)) {
+ setCallback(1);
+ setup_function4();
+ }
+ } else {
+ if (params->param3) {
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(3);
+ setup_playSound("ZFX1012");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ break;
+
+ case 2:
+ case 3:
+ params->param3 = !params->param3;
+ break;
+ }
+ break;
+
+ case kAction236976550:
+ getProgress().field_4C = 1;
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Cooks, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 1;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Cooks, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param1)
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ // Kitchen background sound
+ if (params->param2) {
+ setCallback(1);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(2);
+ setup_playSound("ZFX1012");
+ }
+ break;
+
+
+ case kActionCallback:
+ // Play the next part of background sound
+ if (getCallback() == 1 || getCallback() == 2) {
+ params->param2 = !params->param2;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Cooks, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityCooks);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/cooks.h b/engines/lastexpress/entities/cooks.h
new file mode 100644
index 0000000000..7f1a70fb8a
--- /dev/null
+++ b/engines/lastexpress/entities/cooks.h
@@ -0,0 +1,109 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_COOKS_H
+#define LASTEXPRESS_COOKS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Cooks : public Entity {
+public:
+ Cooks(LastExpressEngine *engine);
+ ~Cooks() {};
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ DECLARE_FUNCTION(function3)
+
+ DECLARE_FUNCTION(function4)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_COOKS_H
diff --git a/engines/lastexpress/entities/coudert.cpp b/engines/lastexpress/entities/coudert.cpp
new file mode 100644
index 0000000000..6249ea5487
--- /dev/null
+++ b/engines/lastexpress/entities/coudert.cpp
@@ -0,0 +1,3611 @@
+/* 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/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+#define SAVEGAME_BLOOD_JACKET() \
+ if (getProgress().jacket == kJacketBlood \
+ && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000) \
+ && !getEntities()->isInsideCompartments(kEntityPlayer) \
+ && !getEntities()->checkFields10(kEntityPlayer)) { \
+ setCallback(1); \
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \
+ }
+
+Coudert::Coudert(LastExpressEngine *engine) : Entity(engine, kEntityCoudert) {
+ ADD_CALLBACK_FUNCTION(Coudert, reset);
+ ADD_CALLBACK_FUNCTION(Coudert, bloodJacket);
+ ADD_CALLBACK_FUNCTION(Coudert, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Coudert, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Coudert, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Coudert, playSound);
+ ADD_CALLBACK_FUNCTION(Coudert, playSound16);
+ ADD_CALLBACK_FUNCTION(Coudert, savegame);
+ ADD_CALLBACK_FUNCTION(Coudert, updateEntity);
+ ADD_CALLBACK_FUNCTION(Coudert, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Coudert, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Coudert, excuseMe);
+ ADD_CALLBACK_FUNCTION(Coudert, function13);
+ ADD_CALLBACK_FUNCTION(Coudert, function14);
+ ADD_CALLBACK_FUNCTION(Coudert, function15);
+ ADD_CALLBACK_FUNCTION(Coudert, function16);
+ ADD_CALLBACK_FUNCTION(Coudert, function17);
+ ADD_CALLBACK_FUNCTION(Coudert, function18);
+ ADD_CALLBACK_FUNCTION(Coudert, function19);
+ ADD_CALLBACK_FUNCTION(Coudert, function20);
+ ADD_CALLBACK_FUNCTION(Coudert, function21);
+ ADD_CALLBACK_FUNCTION(Coudert, function22);
+ ADD_CALLBACK_FUNCTION(Coudert, function23);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentF);
+ ADD_CALLBACK_FUNCTION(Coudert, function25);
+ ADD_CALLBACK_FUNCTION(Coudert, function26);
+ ADD_CALLBACK_FUNCTION(Coudert, function27);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentB);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentA);
+ ADD_CALLBACK_FUNCTION(Coudert, function30);
+ ADD_CALLBACK_FUNCTION(Coudert, function31);
+ ADD_CALLBACK_FUNCTION(Coudert, function32);
+ ADD_CALLBACK_FUNCTION(Coudert, function33);
+ ADD_CALLBACK_FUNCTION(Coudert, function34);
+ ADD_CALLBACK_FUNCTION(Coudert, function35);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter1);
+ ADD_CALLBACK_FUNCTION(Coudert, function37);
+ ADD_CALLBACK_FUNCTION(Coudert, function38);
+ ADD_CALLBACK_FUNCTION(Coudert, function39);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Coudert, function41);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter2);
+ ADD_CALLBACK_FUNCTION(Coudert, function43);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter3);
+ ADD_CALLBACK_FUNCTION(Coudert, function45);
+ ADD_CALLBACK_FUNCTION(Coudert, function46);
+ ADD_CALLBACK_FUNCTION(Coudert, function47);
+ ADD_CALLBACK_FUNCTION(Coudert, function48);
+ ADD_CALLBACK_FUNCTION(Coudert, function49);
+ ADD_CALLBACK_FUNCTION(Coudert, function50);
+ ADD_CALLBACK_FUNCTION(Coudert, function51);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter4);
+ ADD_CALLBACK_FUNCTION(Coudert, function53);
+ ADD_CALLBACK_FUNCTION(Coudert, function54);
+ ADD_CALLBACK_FUNCTION(Coudert, function55);
+ ADD_CALLBACK_FUNCTION(Coudert, function56);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter5);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Coudert, function59);
+ ADD_CALLBACK_FUNCTION(Coudert, function60);
+ ADD_CALLBACK_FUNCTION(Coudert, function61);
+ ADD_CALLBACK_FUNCTION(Coudert, function62);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Coudert, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Coudert, bloodJacket)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Coudert, enterExitCompartment, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Coudert, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(5, Coudert, enterExitCompartment2, ObjectIndex, EntityPosition, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint, (EntityPosition)params->param5, (EntityPosition)params->param6, kCarRedSleeping, (ObjectIndex)params->param4, false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Coudert, playSound)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Coudert, playSound16)
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityCoudert, (char *)&params->seq1, SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Coudert, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getProgress().jacket != kJacketBlood
+ || !getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000)
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields10(kEntityPlayer)) {
+ if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+
+ case kAction1:
+ params->param3 = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getData()->clothes == kClothes1)
+ getSound()->playSound(kEntityPlayer, "ZFX1003", getSound()->getSoundFlag(kEntityCoudert));
+ else if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityPlayer, "JAC1112", getSound()->getSoundFlag(kEntityCoudert));
+ break;
+
+ case kActionExcuseMe:
+ if (getData()->clothes == kClothes1)
+ getSound()->playSound(kEntityPlayer, "ZFX1003", getSound()->getSoundFlag(kEntityCoudert));
+ else
+ getSound()->excuseMe(kEntityCoudert);
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventCorpseFound && !getEvent(kEventCoudertAskTylerCompartment))
+ params->param3 = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+
+ if (getData()->direction != kDirectionUp)
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+ else
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(10, Coudert, updateFromTime, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->time, params->param1);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Coudert, updateFromTicks, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters
+// - EntityIndex
+IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ if (isNight()) {
+ if (Entities::isFemale((EntityIndex)params->param1)) {
+ getSound()->playSound(kEntityCoudert, Entities::isMarried((EntityIndex)params->param1) ? "JAC1112C" : "JAC1112F");
+ } else {
+ if (!params->param1 && getProgress().field_18 == 2) {
+ switch (rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityCoudert, "JAC1113");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityCoudert, "JAC1113A");
+ break;
+ }
+ } else {
+ getSound()->playSound(kEntityCoudert, "JAC1112D");
+ }
+ }
+ } else {
+ if (Entities::isFemale((EntityIndex)params->param1))
+ getSound()->playSound(kEntityCoudert, Entities::isMarried((EntityIndex)params->param1) ? "JAC1112B" : "JAC1112G");
+ else
+ getSound()->playSound(kEntityCoudert, "JAC1112E");
+ }
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ if (!params->param2 && !params->param3) {
+
+ if (!params->param4) {
+ params->param4 = getState()->timeTicks + 75;
+
+ if (!params->param4) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(4);
+ setup_function19(true);
+ break;
+ }
+ }
+
+ if (params->param4 < getState()->timeTicks) {
+ params->param4 = kTimeInvalid;
+
+ getData()->inventoryItem = kItemNone;
+ setCallback(4);
+ setup_function19(true);
+ break;
+ }
+ }
+
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 225);
+
+ getData()->inventoryItem = kItemNone;
+ setCallback(5);
+ setup_function19(true);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kAction11:
+ ++params->param3;
+
+ setCallback(8);
+ setup_excuseMe(savepoint.entity2);
+ break;
+
+ case kActionDefault:
+ if (params->param2)
+ params->param3 = 1;
+
+ setCallback(1);
+ setup_excuseMe((EntityIndex)params->param2);
+ break;
+
+ case kAction16:
+ --params->param3;
+
+ if (params->param2 && !params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_function19(true);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(6);
+ setup_function19(true);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(true);
+ break;
+
+ case 2:
+ if (getProgress().chapter == kChapter1 && !getProgress().eventCorpseFound && !getEvent(kEventCoudertAskTylerCompartment))
+ getData()->inventoryItem = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, params->param1 ? "667I" : "667H");
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ // BUG: the original game continues executing code here
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ CALLBACK_ACTION();
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+ break;
+ }
+ break;
+
+ case kAction201439712:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ } else {
+ setCallback(1);
+ setup_updateFromTime(15);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction202558662);
+
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction202558662);
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction125499160:
+ switch (params->param1) {
+ default:
+ break;
+
+ case kEntityVerges:
+ ENTITY_PARAM(0, 3) = 0;
+ break;
+
+ case kEntityMmeBoutarel:
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+
+ case kEntityMertens:
+ ENTITY_PARAM(0, 5) = 0;
+ break;
+ }
+
+ setCallback(5);
+ setup_function19(false);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(15, Coudert, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 8) = 0;
+ ENTITY_PARAM(1, 1) = 0;
+
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ if (params->param1)
+ getSound()->playSound(kEntityCoudert, "Tat3163");
+ else
+ getSound()->playSound(kEntityCoudert, (getProgress().chapter != kChapter3 || getState()->time > kTime1449000) ? "Tat3162A" : "Tat3161A");
+
+ setCallback(3);
+ setup_enterExitCompartment("627Xb", kObjectCompartmentB);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityTatiana, kAction69239528);
+ getData()->entityPosition = kPosition_7250;
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Coudert, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(ENTITY_PARAM(0, 2) ? 1 : 2);
+ setup_bloodJacket(ENTITY_PARAM(0, 2) ? "627C" : "627F");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param1) {
+ setCallback(1);
+ setup_bloodJacket("627H");
+ break;
+ }
+
+ if (params->param2) {
+ setCallback(2);
+ setup_bloodJacket("627C");
+ break;
+ }
+
+ setCallback(3);
+ setup_bloodJacket("627F");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Coudert, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 8)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7) || ENTITY_PARAM(1, 8)
+ || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) {
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 68)) {
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, "JAC1111");
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+ }
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityCoudert);
+ ENTITY_PARAM(2, 1) = 1;
+
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+
+ case 2:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ ENTITY_PARAM(0, 1) = 0;
+ getSavePoints()->push(kEntityCoudert, kEntityCoudert, kActionDrawScene);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 8)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7) || ENTITY_PARAM(1, 8)
+ || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) {
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) {
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ ENTITY_PARAM(2, 1) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param1)
+ getEntities()->drawSequenceRight(kEntityCoudert, "697H");
+ else
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(CURRENT_PARAMS(1, 3), getState()->time, 300)
+ getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityCoudert));
+ UPDATE_PARAM_PROC_END
+
+ UPDATE_PARAM(CURRENT_PARAMS(1, 4), getState()->time, 900);
+
+ getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1);
+
+ if (params->param4 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, (EntityIndex)params->param3, (ObjectLocation)params->param4, (CursorStyle)params->param5, (CursorStyle)params->param6);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param7, (ObjectLocation)params->param8, (CursorStyle)CURRENT_PARAMS(1, 1), (CursorStyle)CURRENT_PARAMS(1, 2));
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param3 = getObjects()->get((ObjectIndex)params->param1).entity;
+ params->param4 = getObjects()->get((ObjectIndex)params->param1).location;
+ params->param5 = getObjects()->get((ObjectIndex)params->param1).cursor;
+ params->param6 = getObjects()->get((ObjectIndex)params->param1).cursor2;
+
+ if (params->param2) {
+ params->param7 = getObjects()->get((ObjectIndex)params->param2).entity;
+ params->param8 = getObjects()->get((ObjectIndex)params->param2).location;
+ CURRENT_PARAMS(1, 1) = getObjects()->get((ObjectIndex)params->param2).cursor;
+ CURRENT_PARAMS(1, 2) = getObjects()->get((ObjectIndex)params->param2).cursor2;
+
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ if (params->param4 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (params->param1 == kObjectCompartmentA || params->param1 == kObjectCompartmentC
+ || params->param1 == kObjectCompartmentG || params->param1 == kObjectCompartmentH) {
+ setCallback(3);
+ setup_playSound("Jac1001B");
+ } else {
+ setCallback(4);
+ setup_playSound("Jac1001A");
+ }
+ break;
+
+ case 3:
+ case 4:
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Coudert, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment("627Zh", kObjectCompartmentH);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vh", kObjectCompartmentH);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wh");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentH, kObjectNone);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("697Ah", kObjectCompartmentH);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentH, kObjectNone);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(9);
+ setup_enterExitCompartment("667Uh", kObjectCompartmentH);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment("667Th", kObjectCompartmentH);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Coudert, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment("627Rg", kObjectCompartmentG);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Mg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ng");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentG, kObjectNone);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Sg", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentG, kObjectNone);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(9);
+ setup_enterExitCompartment("627Ug", kObjectCompartmentG);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment("627Tg", kObjectCompartmentG);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1030");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Coudert, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vf", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kAction158007856);
+
+ setCallback(3);
+ setup_updateFromTime(150);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Coudert, visitCompartmentF)
+ visitCompartment(savepoint, kPosition_4070, "627Vf", kObjectCompartmentF, "627Wf", "627Zf", kPosition_4455, kObject53, "697Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Coudert, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Me", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_updateFromTime(45);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction254915200);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("627Re", kObjectCompartmentE, kPosition_4840, kPosition_4455);
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_function20(kObjectCompartmentE, kObject52);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Coudert, function26)
+ error("Coudert: callback function 26 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Coudert, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Mc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Nc");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentC, kObject50);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Sc", kObjectCompartmentC);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentC, kObject50);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+
+ setCallback(9);
+ setup_enterExitCompartment("627Uc", kObjectCompartmentC);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Coudert, visitCompartmentB)
+ visitCompartment(savepoint, kPosition_7500, "627Vb", kObjectCompartmentB, "627Wb", "627Zb", kPosition_7850, kObject49, "697Ab");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Coudert, visitCompartmentA)
+ visitCompartment(savepoint, kPosition_8200, "627Ma", kObjectCompartmentA, "627Na", "627Ra", kPosition_7850, kObject48, "627Sa");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex)
+ // Expose parameters as IIIIIS and ignore the default exposed parameters
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+ EntityData::EntityParametersSIIS *parameters1 = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (parameters->param1) {
+ default:
+ CALLBACK_ACTION();
+ // Stop processing here
+ return;
+
+ case kObjectCompartmentA:
+ parameters->param2 = kPosition_8200;
+ parameters->param3 = kPosition_7850;
+ strcpy((char *)&parameters->seq, "627Ma");
+ strcpy((char *)&parameters1->seq1, "627Na");
+ break;
+
+ case kObjectCompartmentB:
+ parameters->param2 = kPosition_7500;
+ parameters->param3 = kPosition_7850;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vb");
+ strcpy((char *)&parameters1->seq1, "627Wb");
+ break;
+
+ case kObjectCompartmentC:
+ parameters->param2 = kPosition_6470;
+ parameters->param3 = kPosition_6130;
+ strcpy((char *)&parameters->seq, "627Mc");
+ strcpy((char *)&parameters1->seq1, "627Nc");
+ break;
+
+ case kObjectCompartmentD:
+ parameters->param2 = kPosition_5790;
+ parameters->param3 = kPosition_6130;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vd");
+ strcpy((char *)&parameters1->seq1, "627Wd");
+ break;
+
+ case kObjectCompartmentE:
+ parameters->param2 = kPosition_4840;
+ parameters->param3 = kPosition_4455;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Me");
+ strcpy((char *)&parameters1->seq1, "627Ne");
+ break;
+
+ case kObjectCompartmentF:
+ parameters->param2 = kPosition_4070;
+ parameters->param3 = kPosition_4455;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vf");
+ strcpy((char *)&parameters1->seq1, "627Wf");
+ break;
+ }
+
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, (EntityPosition)parameters->param2);
+ break;
+
+ case 2:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, (EntityPosition)parameters->param3)
+ || ((parameters->param1 == kObjectCompartmentE || parameters->param1 == kObjectCompartmentF) && getEntities()->isOutsideAnnaWindow())) {
+ getObjects()->update((ObjectIndex)parameters->param1, kEntityPlayer, getObjects()->get((ObjectIndex)parameters->param1).location, kCursorNormal, kCursorNormal);
+ parameters->param5 = true;
+ }
+
+ setCallback(3);
+ setup_enterExitCompartment((char *)&parameters->seq, (ObjectIndex)parameters->param1);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, (char *)&parameters1->seq1);
+ getEntities()->enterCompartment(kEntityCoudert, (ObjectIndex)parameters->param1, true);
+
+ setCallback(4);
+ setup_playSound(parameters->param4 ? "JAC3020" : "JAC3021");
+ break;
+
+ case 4:
+ if (parameters->param5)
+ getObjects()->update((ObjectIndex)parameters->param1, kEntityPlayer, getObjects()->get((ObjectIndex)parameters->param1).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityCoudert, (ObjectIndex)parameters->param1, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function18();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(31, Coudert, function31, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setCallback(3);
+ setup_function19(true);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_bloodJacket("627G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ } else {
+ setCallback(2);
+ setup_function19(true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Coudert, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityCoudert);
+ setCallback(3);
+ setup_updateFromTime(900);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Coudert, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 4) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)
+ || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(2, 6) = 1;
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 4) || ENTITY_PARAM(0, 5)) {
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ } else {
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ }
+ } else {
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(2, 1) = 1;
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(2);
+ setup_function14(kEntityVerges);
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(3);
+ setup_function14(kEntityMertens);
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ ENTITY_PARAM(2, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_updateFromTime(75);
+ break;
+
+ case 6:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) {
+ setCallback(7);
+ setup_function37();
+ break;
+ }
+ // Fallback to next case
+
+ case 7:
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(8);
+ setup_function39();
+ break;
+ }
+ // Fallback to next case
+
+ case 8:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(9);
+ setup_function55();
+ break;
+ }
+ // Fallback to next case
+
+ case 9:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(10);
+ setup_function34(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 10:
+ ENTITY_PARAM(2, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(34, Coudert, function34, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ if (!params->param1) {
+ getSound()->playSound(kEntityCoudert, "Ann3124");
+
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+
+ setCallback(7);
+ setup_function35((bool)params->param1);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Vf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("Jac1001");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityCoudert, "Ann3125");
+
+ setCallback(5);
+ setup_enterExitCompartment("629Bf", kObjectCompartmentF);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("629Ff", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+
+ setCallback(7);
+ setup_function35((bool)params->param1);
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage)) {
+ getAction()->playAnimation(kEventCoudertBaggageCar);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ UPDATE_PARAM(params->param2, getState()->time, 2700);
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage);
+
+ getData()->clothes = kClothesDefault;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionDefault:
+ if (params->param1)
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction156049968);
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kAction122358304);
+
+ getData()->clothes = kClothes1;
+ getData()->entityPosition = kPosition_4370;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, "Ann3124");
+
+ if (params->param1)
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction123733488);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityCoudert);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function18();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Coudert, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTimeChapter1, params->param1, 1, setup_chapter1Handler)
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityCoudert, kAction292048641, 7);
+ getSavePoints()->addData(kEntityCoudert, kAction326348944, 8);
+ getSavePoints()->addData(kEntityCoudert, kAction171394341, 2);
+ getSavePoints()->addData(kEntityCoudert, kAction154005632, 4);
+ getSavePoints()->addData(kEntityCoudert, kAction169557824, 3);
+ getSavePoints()->addData(kEntityCoudert, kAction226031488, 5);
+ getSavePoints()->addData(kEntityCoudert, kAction339669520, 6);
+ getSavePoints()->addData(kEntityCoudert, kAction189750912, 10);
+ getSavePoints()->addData(kEntityCoudert, kAction185737168, 12);
+ getSavePoints()->addData(kEntityCoudert, kAction185671840, 13);
+ getSavePoints()->addData(kEntityCoudert, kAction205033696, 15);
+ getSavePoints()->addData(kEntityCoudert, kAction157026693, 14);
+ getSavePoints()->addData(kEntityCoudert, kAction189026624, 11);
+ getSavePoints()->addData(kEntityCoudert, kAction168254872, 17);
+ getSavePoints()->addData(kEntityCoudert, kAction201431954, 18);
+ getSavePoints()->addData(kEntityCoudert, kAction188570113, 19);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 1;
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter1Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Coudert, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getSound()->isBuffered(kEntityCoudert))
+ getSound()->processEntry(kEntityCoudert);
+
+ if (ENTITY_PARAM(0, 7)) {
+ getData()->entityPosition = kPosition_8200;
+
+ setCallback(4);
+ setup_enterExitCompartment2("698Ha", kObjectCompartmentA, kPosition_8200, kPosition_7850);
+ } else {
+ setCallback(1);
+ setup_function16();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction238358920);
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("698Ha", kObjectCompartmentA, kPosition_8200, kPosition_7850);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Coudert, function38)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ setup_chapter1Handler();
+ break;
+ }
+ break;
+
+ case kAction191477936:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Coudert, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("LIB070");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("627Vd", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wd");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentD, true);
+
+ setCallback(5);
+ setup_playSound("MME1151A");
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentD, true);
+
+ setCallback(6);
+ setup_enterExitCompartment("627Zd", kObjectCompartmentD);
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(7);
+ setup_playSound("MME1151");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("MME1151", kObjectCompartmentD);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction223068211);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function18();
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction167854368);
+ ENTITY_PARAM(2, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Coudert, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(0, 1) = 1;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 2) = 0;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param1 = 1;
+ params->param2 = 1;
+
+ ENTITY_PARAM(2, 3) = 0;
+ }
+
+ getData()->inventoryItem = (getProgress().eventCorpseFound || getEvent(kEventCoudertAskTylerCompartment)) ? kItemNone : kItemInvalid;
+
+ if (ENTITY_PARAM(0, 8)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(1, 1)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(5);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setup_function37();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_6:
+ if (ENTITY_PARAM(0, 5)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(7);
+ setup_function14(kEntityMertens);
+ break;
+ }
+
+label_callback_7:
+ if (ENTITY_PARAM(0, 4)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(8);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+
+label_callback_8:
+ if (ENTITY_PARAM(2, 2)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_function39();
+ break;
+ }
+
+label_callback_9:
+ if (ENTITY_PARAM(0, 1) && !getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "JAC1065" : "JAC1065A");
+
+ if (getState()->time > kTime1107000 && !ENTITY_PARAM(0, 1) && !getEvent(kEventVassiliSeizure)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(10);
+ setup_function41();
+ break;
+ }
+
+label_callback_10:
+ if (getState()->time > kTime1189800 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 2700);
+ ENTITY_PARAM(0, 2) = 1;
+ ENTITY_PARAM(0, 1) = 1;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!ENTITY_PARAM(0, 2))
+ break;
+
+ TIME_CHECK_OBJECT(kTime1107000, params->param4, kObject111, kObjectLocation2);
+ TIME_CHECK_OBJECT(kTime1161000, params->param5, kObject111, kObjectLocation3);
+ TIME_CHECK_OBJECT(kTime1206000, params->param6, kObject111, kObjectLocation4);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(13);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 1) && !getEntities()->isPlayerPosition(kCarRedSleeping, 23))
+ break;
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket);
+ } else {
+ setCallback(getEntities()->isPlayerPosition(kCarRedSleeping, 1) ? 2 : 3);
+ setup_function13(true, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ params->param1 = 1;
+ goto label_callback_10;
+
+ case 11:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+
+ setCallback(12);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 12:
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction168253822:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityCoudert, "JAC1120");
+
+ setCallback(14);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(16);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityCoudert, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(17);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Coudert, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_visitCompartmentA();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_visitCompartmentB();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function27();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction285528346);
+
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function26();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function33();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function25();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function33();
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_function23();
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function33();
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_function22();
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function33();
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_function21();
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 17:
+ setCallback(18);
+ setup_function18();
+ break;
+
+ case 18:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction208228224);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Coudert, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation5);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function43();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Coudert, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function15(true);
+ break;
+ }
+
+label_callback1:
+ if (!ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_function15(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function14(kEntityVerges);
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(4);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(5);
+ setup_function13(true, kEntityPlayer);
+
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(6);
+ setup_function13(false, kEntityPlayer);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 7:
+ setCallback(8);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(7);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(10);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Coudert, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+ ENTITY_PARAM(2, 5) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation6);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Coudert, function45)
+ error("Coudert: callback function 45 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Coudert, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Vf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction253868128);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+
+ setCallback(4);
+ setup_playSound("Ann1016A");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("Ann4150");
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityCoudert, "Ann3121");
+
+ setCallback(6);
+ setup_enterExitCompartment("629Bf", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "629Cf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ // Fallback to next case
+
+ case 7:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ setCallback(7);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(8);
+ setup_playSound("Ann3122");
+ }
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "Ann3123");
+
+ setCallback(9);
+ setup_updateFromTicks(75);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_enterExitCompartment("629Ff", kObjectCompartmentF);
+ break;
+
+ case 10:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ ENTITY_PARAM(1, 3) = 0;
+
+ setCallback(11);
+ setup_function35(true);
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(47, Coudert, function47, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("627Xf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF);
+ // Fallback to next case
+
+ case 4:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ setCallback(4);
+ setup_updateFromTime(225);
+ } else {
+ setCallback(5);
+ setup_playSound(params->param1 ? "Ann3149" : "Ann3147a");
+ }
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction157894320);
+
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+
+ setCallback(7);
+ setup_function18();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Coudert, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityCoudert, "Ann3148A");
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Ann3148B" : "Ann3148");
+ setCallback(3);
+ setup_enterExitCompartment("627Xf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction192063264);
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ ENTITY_PARAM(1, 8) = 0;
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Coudert, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("627Vb", kObjectCompartmentB);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityTatiana, kCarRedSleeping, kPosition_7500)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+
+ setCallback(4);
+ setup_playSound("Jac3006");
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(8);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(5);
+ setup_enterExitCompartment("627Zb", kObjectCompartmentB);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_playSound("Jac3006A");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("697Ab", kObjectCompartmentB);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateFromTime(150);
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction242526416);
+ ENTITY_PARAM(2, 4) = 0;
+ ENTITY_PARAM(2, 5) = 1;
+
+ setCallback(11);
+ setup_function18();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Coudert, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Me");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ if (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840)) {
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(8);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+
+ setCallback(4);
+ setup_playSound("Jac3005");
+ }
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Re", kObjectCompartmentE);
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_playSound("Jac3005A");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ ENTITY_PARAM(2, 5) = 0;
+
+ setCallback(9);
+ setup_function18();
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Coudert, function51)
+ error("Coudert: callback function 51 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Coudert, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 3) = 0;
+ ENTITY_PARAM(2, 4) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation10);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ ENTITY_PARAM(1, 2) = 1;
+ setup_function53();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Coudert, function53)
+ error("Coudert: callback function 53 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Coudert, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->hasValidFrame(kEntityCoudert)) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ } else {
+ getData()->car = kCarLocomotive;
+ getData()->entityPosition = kPosition_540;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityCoudert);
+ getData()->car = kCarLocomotive;
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction191001984:
+ getData()->car = kCarRedSleeping;
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Coudert, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("LIB070");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ setCallback(4);
+ setup_playSound("Ann4150A");
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction219971920);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction101824388);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityCoudert);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction136059947);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(7);
+ setup_function18();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, Coudert, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function21();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function22();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_visitCompartmentF();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function25();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function33();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function26();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function33();
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_function27();
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function33();
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_visitCompartmentB();
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_function18();
+ break;
+
+ case 16:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, Coudert, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, Coudert, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function59();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, Coudert, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getSound()->playSound(kEntityCoudert, "Jac5010"); // Situation is under control, please remain in your compartment
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ setup_function60();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, Coudert, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function61();
+ break;
+
+ case kAction155991520:
+ setCallback(1);
+ setup_updateFromTime(225);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, Coudert, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Me", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_updateFromTime(75);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(4);
+ setup_enterExitCompartment("627Re", kObjectCompartmentE);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_playSound("Reb5010");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction155604840);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("627Zh", kObjectCompartmentH);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction169750080);
+
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, Coudert, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ ++params->param3;
+
+ if (params->param3 == 1 || params->param2) {
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ setCallback(params->param3 == 1 ? 4 : 5);
+ setup_playSound(params->param3 == 1 ? "Jac5002" : "Jac5002A");
+ }
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 5:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(63, Coudert)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Coudert::visitCompartment(const SavePoint &savepoint, EntityPosition position, const char* seq1, ObjectIndex compartment, const char* seq2, const char* seq3, EntityPosition sittingPosition, ObjectIndex object, const char* seq4) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, position);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment(seq1, compartment);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, seq2);
+ getEntities()->enterCompartment(kEntityCoudert, compartment, true);
+
+ setCallback(3);
+ setup_updateFromTime(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2(seq3, compartment, position, sittingPosition);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, compartment, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(5);
+ setup_function20(compartment, object);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment(seq4, compartment);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/coudert.h b/engines/lastexpress/entities/coudert.h
new file mode 100644
index 0000000000..e86e519385
--- /dev/null
+++ b/engines/lastexpress/entities/coudert.h
@@ -0,0 +1,229 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_COUDERT_H
+#define LASTEXPRESS_COUDERT_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Coudert : public Entity {
+public:
+ Coudert(LastExpressEngine *engine);
+ ~Coudert() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handle meeting Coudert with the blooded jacket
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(bloodJacket, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param entityPosition1 The entity position 1
+ * @param entityPosition2 The entity position 2
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment2, const char* sequence, ObjectIndex compartment, EntityPosition entityPosition1, EntityPosition entityPosition2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound16)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ DECLARE_FUNCTION_1(excuseMe, EntityIndex entity)
+ DECLARE_FUNCTION_2(function13, bool, EntityIndex entity)
+ DECLARE_FUNCTION_1(function14, EntityIndex entity)
+ DECLARE_FUNCTION_1(function15, bool)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION_1(function17, bool)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION_1(function19, bool)
+
+ /**
+ * ???
+ *
+ * @param object1 The first object index
+ * @param object2 The second object index
+ */
+ DECLARE_FUNCTION_2(function20, ObjectIndex object1, ObjectIndex object2)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(visitCompartmentF)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(visitCompartmentB)
+ DECLARE_FUNCTION(visitCompartmentA)
+
+ /**
+ * ???
+ *
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_1(function30, ObjectIndex compartment)
+
+ DECLARE_FUNCTION_1(function31, uint32)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION_1(function34, bool)
+ DECLARE_FUNCTION_1(function35, bool)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function43)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION_1(function47, bool)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void visitCompartment(const SavePoint &savepoint, EntityPosition position, const char* seq1, ObjectIndex compartment, const char* seq2, const char* seq3, EntityPosition sittingPosition, ObjectIndex object, const char* seq4);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_COUDERT_H
diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp
new file mode 100644
index 0000000000..8400c38737
--- /dev/null
+++ b/engines/lastexpress/entities/entity.cpp
@@ -0,0 +1,434 @@
+/* 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/entities/entity.h"
+
+#include "lastexpress/entities/entity_intern.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// EntityData
+//////////////////////////////////////////////////////////////////////////
+EntityData::EntityParameters *EntityData::getParameters(uint callback, byte index) const {
+ if (callback >= 9)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 9)", callback);
+
+ if (index >= 4)
+ error("EntityData::getParameters: invalid index value (was: %d, max: 4)", index);
+
+ return _parameters[callback].parameters[index];
+}
+
+int EntityData::getCallback(uint callback) const {
+ if (callback >= 16)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 16)", callback);
+
+ return _data.callbacks[callback];
+}
+
+void EntityData::setCallback(uint callback, byte index) {
+ if (callback >= 16)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 16)", callback);
+
+ _data.callbacks[callback] = index;
+}
+
+void EntityData::updateParameters(uint32 index) const {
+ if (index < 8)
+ getParameters(8, 0)->update(index);
+ else if (index < 16)
+ getParameters(8, 1)->update(index - 8);
+ else if (index < 24)
+ getParameters(8, 2)->update(index - 16);
+ else if (index < 32)
+ getParameters(8, 3)->update(index - 24);
+ else
+ error("EntityData::updateParameters: invalid param index to update (was:%d, max:32)!", index);
+}
+
+void EntityData::saveLoadWithSerializer(Common::Serializer &) {
+ error("EntityData::saveLoadWithSerializer: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entity
+//////////////////////////////////////////////////////////////////////////
+Entity::Entity(LastExpressEngine *engine, EntityIndex index) : _engine(engine), _entityIndex(index) {
+ _data = new EntityData();
+
+ // Add first empty entry to callbacks array
+ _callbacks.push_back(NULL);
+}
+
+Entity::~Entity() {
+ for (uint i = 0; i < _callbacks.size(); i++)
+ delete _callbacks[i];
+
+ delete _data;
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+void Entity::setup(ChapterIndex index) {
+ switch(index) {
+ case kChapterAll:
+ getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]);
+ break;
+
+ case kChapter1:
+ setup_chapter1();
+ break;
+
+ case kChapter2:
+ setup_chapter2();
+ break;
+
+ case kChapter3:
+ setup_chapter3();
+ break;
+
+ case kChapter4:
+ setup_chapter4();
+ break;
+
+ case kChapter5:
+ setup_chapter5();
+ break;
+
+ default:
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Shared functions
+//////////////////////////////////////////////////////////////////////////
+
+void Entity::reset(const SavePoint &savepoint, bool resetClothes, bool resetItem) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ if (resetClothes) {
+ // Select next available clothes
+ getData()->clothes = (ClothesIndex)(getData()->clothes + 1);
+ if (getData()->clothes > kClothes3)
+ getData()->clothes = kClothesDefault;
+ }
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, kCarGreenSleeping, (EntityPosition)params->param1))
+ params->param1 = (params->param1 == 10000) ? 0 : 10000;
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ if (resetItem)
+ getData()->inventoryItem = kItemInvalid;
+
+ params->param1 = 10000;
+ break;
+ }
+}
+
+void Entity::savegame(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSaveLoad()->saveGame((SavegameType)params->param1, _entityIndex, (EventIndex)params->param2);
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundManager::FlagType flag) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (resetItem)
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(_entityIndex, (char *)&params->seq1, flag);
+ break;
+ }
+}
+
+void Entity::draw(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !params->param4) {
+ getSound()->excuseMe(_entityIndex);
+ params->param4 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ break;
+ }
+}
+
+void Entity::draw2(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ getEntities()->drawSequenceRight((EntityIndex)params->param7, (char *)&params->seq2);
+ break;
+ }
+}
+
+void Entity::updateFromTicks(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::updateFromTime(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callbackActionOnDirection(const SavePoint &savepoint) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callbackActionRestaurantOrSalon(const SavePoint &savepoint) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon())
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::updateEntity(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe)
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ if (handleExcuseMe)
+ getSound()->excuseMe(_entityIndex);
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callSavepoint(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ if (!CURRENT_PARAMS(1, 1))
+ getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)&params->seq2);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !CURRENT_PARAMS(1, 2)) {
+ getSound()->excuseMe(_entityIndex);
+ CURRENT_PARAMS(1, 2) = 1;
+ }
+ break;
+
+ case kAction10:
+ if (!CURRENT_PARAMS(1, 1)) {
+ getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)&params->seq2);
+ CURRENT_PARAMS(1, 1) = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ break;
+ }
+}
+
+void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition position1, EntityPosition position2, CarIndex car, ObjectIndex compartment, bool alternate, bool updateLocation) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4);
+ if (position1)
+ getData()->entityPosition = position1;
+
+ if (updateLocation)
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ getEntities()->enterCompartment(_entityIndex, (ObjectIndex)params->param4);
+
+ if (position1) {
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, car, position1) || getEntities()->isInsideCompartment(kEntityPlayer, car, position2)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(compartment, alternate);
+ }
+ }
+ break;
+ }
+}
+
+void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(_entityIndex, (CarIndex)params->param4, (Position)params->param5);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !params->param6) {
+ getSound()->excuseMe(_entityIndex);
+ params->param6 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq);
+ getEntities()->updatePositionEnter(_entityIndex, (CarIndex)params->param4, (Position)params->param5);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h
new file mode 100644
index 0000000000..b0a1ab68ea
--- /dev/null
+++ b/engines/lastexpress/entities/entity.h
@@ -0,0 +1,681 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY_H
+#define LASTEXPRESS_ENTITY_H
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/game/sound.h"
+
+#include "common/array.h"
+#include "common/func.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+class SequenceFrame;
+struct SavePoint;
+
+class EntityData : Common::Serializable {
+public:
+
+ struct EntityParameters {
+ virtual ~EntityParameters() {}
+ virtual Common::String toString() = 0;
+
+ virtual void update(uint32 index) = 0;
+ };
+
+ struct EntityParametersIIII : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ uint param4;
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersIIII() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ param4 = 0;
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ bool hasNonNullParameter() {
+ return param1 || param2 || param3 || param4 || param5 || param6 || param7 || param8;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IIII: %d %d %d %d %d %d %d %d\n", param1, param2, param3, param4, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIIII::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 2: param3 = 1; break;
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersSIII : EntityParameters {
+ char seq[12];
+ uint param4;
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersSIII() {
+ memset(&seq, 0, 12);
+ param4 = 0;
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SIII: %s %d %d %d %d %d\n", seq, param4, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSIII::update: invalid index (was: %d)", index);
+
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersSIIS : EntityParameters {
+ char seq1[12];
+ uint param4;
+ uint param5;
+ char seq2[12];
+
+ EntityParametersSIIS() {
+ memset(&seq1, 0, 12);
+ param4 = 0;
+ param5 = 0;
+ memset(&seq2, 0, 12);
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SIIS: %s %d %d %s\n", seq1, param4, param5, seq2);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSIIS::update: invalid index (was: %d)", index);
+
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersISSI : EntityParameters {
+ uint param1;
+ char seq1[12];
+ char seq2[12];
+ uint param8;
+
+ EntityParametersISSI() {
+ param1 = 0;
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("ISSI: %d %s %s %d\n", param1, seq1, seq2, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersISSI::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersISII : EntityParameters {
+ uint param1;
+ char seq[12];
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersISII() {
+ param1 = 0;
+ memset(&seq, 0, 12);
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("ISII: %d %s %d %d %d %d\n", param1, seq, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersISII::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersSSII : EntityParameters {
+ char seq1[12];
+ char seq2[12];
+ uint param7;
+ uint param8;
+
+ EntityParametersSSII() {
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SSII: %s %s %d %d\n", seq1, seq2, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSSII::update: invalid index (was: %d)", index);
+
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersIISS : EntityParameters {
+ uint param1;
+ uint param2;
+ char seq1[12];
+ char seq2[12];
+
+ EntityParametersIISS() {
+ param1 = 0;
+ param2 = 0;
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IISS: %d %d %s %s\n", param1, param2, seq1, seq2);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIISS::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersIISI : EntityParameters {
+ uint param1;
+ uint param2;
+ char seq[12];
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersIISI() {
+ param1 = 0;
+ param2 = 0;
+ memset(&seq, 0, 12);
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IISI: %d %d %s %d %d %d\n", param1, param2, seq, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIISI::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersIIIS : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ char seq[12];
+ uint param7;
+ uint param8;
+
+ EntityParametersIIIS() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ memset(&seq, 0, 12);
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IIIS: %d %d %d %s %d %d\n", param1, param2, param3, seq, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIIIS::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 2: param3 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+ };
+
+ struct EntityParametersI5S : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ uint param4;
+ uint param5;
+ char seq[12];
+
+ EntityParametersI5S() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ param4 = 0;
+ param5 = 0;
+ memset(&seq, 0, 12);
+ }
+ };
+
+ struct EntityCallParameters {
+ EntityParameters* parameters[4];
+
+ EntityCallParameters() {
+ // We default to int parameters
+ create<EntityParametersIIII>();
+ }
+
+ ~EntityCallParameters() {
+ clear();
+ }
+
+ template <class parameter>
+ void create() {
+ for (int i = 0; i < 4; i++)
+ parameters[i] = new parameter();
+ }
+
+ void clear() {
+ for (int i = 0; i < 4; i++) {
+ if (parameters[i])
+ delete parameters[i];
+ parameters[i] = NULL;
+ }
+ }
+ };
+
+ struct EntityCallData {
+ byte callbacks[16];
+ byte currentCall;
+ EntityPosition entityPosition; // word
+ Location location; // word
+ CarIndex car; // word
+ byte field_497;
+ EntityIndex entity; // byte
+ InventoryItem inventoryItem; // byte
+ EntityDirection direction; // byte
+ int16 field_49B;
+ int16 currentFrame;
+ int16 currentFrame2;
+ int16 field_4A1;
+ int16 field_4A3;
+ ClothesIndex clothes; // byte
+ Position position;
+ CarIndex car2; // byte
+ bool doProcessEntity; // byte
+ bool field_4A9; // byte
+ bool field_4AA; // byte
+ EntityDirection directionSwitch;
+ Common::String sequenceName; // char[13]
+ Common::String sequenceName2; // char[13]
+ Common::String sequenceNamePrefix; // char[7]
+ Common::String sequenceNameCopy; // char[13]
+ SequenceFrame *frame;
+ SequenceFrame *frame1;
+ Sequence *sequence;
+ Sequence *sequence2;
+ Sequence *sequence3;
+
+ /**
+ * Default constructor.
+ */
+ EntityCallData() {
+ memset(&callbacks, 0, 16 * sizeof(byte));
+ currentCall = 0;
+ entityPosition = kPositionNone;
+ location = kLocationOutsideCompartment;
+ car = kCarNone;
+ field_497 = 0;
+ entity = kEntityPlayer;
+ inventoryItem = kItemNone;
+ direction = kDirectionNone;
+ field_49B = 0;
+ currentFrame = 0;
+ currentFrame2 = 0;
+ field_4A1 = 0;
+ field_4A3 = 30;
+ clothes = kClothesDefault;
+ position = 0;
+ car2 = kCarNone;
+ doProcessEntity = false;
+ field_4A9 = false;
+ field_4AA = false;
+ directionSwitch = kDirectionNone;
+ frame = NULL;
+ frame1 = NULL;
+ sequence = NULL;
+ sequence2 = NULL;
+ sequence3 = NULL;
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String str = "";
+
+ str += Common::String::printf("Entity position: %d - Location: %d - Car: %d\n", entityPosition, location, car);
+ str += Common::String::printf("Entity: %d - Item: %d - Direction: %d\n", entity, inventoryItem, direction);
+ str += Common::String::printf("Clothes: %d - Position: %d - Direction switch: %d\n", clothes, position, directionSwitch);
+ str += "\n";
+ str += Common::String::printf("field_497: %02d - field_49B: %i - field_4A1: %i\n", field_497, field_49B, field_4A1);
+ str += Common::String::printf("field_4A9: %02d - field_4AA: %i - Car 2: %d\n", field_4A9, field_4AA, car2);
+ str += "\n";
+ str += "Sequence: " + sequenceName + " - Sequence 2: " + sequenceName2 + "\n";
+ str += "Sequence prefix: " + sequenceNamePrefix + " - Sequence copy: " + sequenceNameCopy + "\n";
+ str += Common::String::printf("Current frame: %i - Current frame 2: %i - Process entity: %d\n", currentFrame, currentFrame2, doProcessEntity);
+ str += "\n";
+ str += Common::String::printf("Current call: %d\n", currentCall);
+ str += Common::String::printf("Functions: %d %d %d %d %d %d %d %d\n", callbacks[0], callbacks[1], callbacks[2], callbacks[3], callbacks[4], callbacks[5], callbacks[6], callbacks[7]);
+ str += Common::String::printf("Callbacks: %d %d %d %d %d %d %d %d\n", callbacks[8], callbacks[9], callbacks[10], callbacks[11], callbacks[12], callbacks[13], callbacks[14], callbacks[15]);
+
+ return str;
+ }
+ };
+
+ EntityData() {}
+
+ template <class parameter>
+ void resetCurrentParameters() {
+ _parameters[_data.currentCall].clear();
+ _parameters[_data.currentCall].create<parameter>();
+ }
+
+ EntityCallData *getCallData() { return &_data; }
+
+ EntityParameters *getParameters(uint callback, byte index) const;
+ EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); }
+
+ int getCallback(uint callback) const;
+ int getCurrentCallback() { return getCallback(_data.currentCall); }
+ void setCallback(uint callback, byte index);
+ void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); }
+
+ void updateParameters(uint32 index) const;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+private:
+
+ EntityCallData _data;
+ EntityCallParameters _parameters[9];
+};
+
+class Entity : Common::Serializable {
+public:
+
+ typedef Common::Functor1<const SavePoint&, void> Callback;
+
+ Entity(LastExpressEngine *engine, EntityIndex index);
+ virtual ~Entity();
+
+ // Accessors
+ EntityData *getParamData() { return _data; }
+ EntityData::EntityCallData *getData() { return _data->getCallData(); }
+
+ // Callbacks
+ int getCallback() { return _data->getCallback(_data->getCallData()->currentCall + 8); }
+ void setCallback(byte index) { _data->setCallback(_data->getCallData()->currentCall + 8, index); getData()->currentCall++; }
+
+ // Setup
+ void setup(ChapterIndex index);
+
+ virtual void setup_chapter1() = 0;
+ virtual void setup_chapter2() = 0;
+ virtual void setup_chapter3() = 0;
+ virtual void setup_chapter4() = 0;
+ virtual void setup_chapter5() = 0;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser) { _data->saveLoadWithSerializer(ser); }
+
+ void nullfunction(const SavePoint &savepoint) {}
+
+protected:
+ LastExpressEngine* _engine;
+
+ EntityIndex _entityIndex;
+ EntityData *_data;
+ Common::Array<Callback *> _callbacks;
+
+ /**
+ * Saves the game
+ *
+ * @param savepoint The savepoint
+ * - SavegameType
+ * - EventIndex
+ */
+ void savegame(const SavePoint &savepoint);
+
+ /**
+ * Play sound
+ *
+ * @param savepoint The savepoint
+ * - Sound filename
+ * @param resetItem true to reset item.
+ * @param flag sound flag
+ */
+ void playSound(const SavePoint &savepoint, bool resetItem = false, SoundManager::FlagType flag = SoundManager::kFlagInvalid);
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - Sequence
+ * - ExcuseMe flag
+ * @param handleExcuseMe true to handle excuseMeCath action
+ */
+ void draw(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint.
+ * - Sequence 1
+ * - Sequence 2
+ * - EntityIndex
+ */
+ void draw2(const SavePoint &savepoint);
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - Number of ticks to add
+ */
+ void updateFromTicks(const SavePoint &savepoint);
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint.
+ * - Time to add
+ */
+ void updateFromTime(const SavePoint &savepoint);
+
+ /**
+ * Resets an entity
+ *
+ * @param savepoint The savepoint.
+ * @param resetClothes true to reset clothes.
+ * @param resetItem true to reset inventoryItem to kItemInvalid
+ */
+ void reset(const SavePoint &savepoint, bool resetClothes = false, bool resetItem = false);
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ *
+ * @param savepoint The savepoint.
+ */
+ void callbackActionOnDirection(const SavePoint &savepoint);
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ *
+ * @param savepoint The savepoint.
+ */
+ void callbackActionRestaurantOrSalon(const SavePoint &savepoint);
+
+ /**
+ * Updates the entity
+ *
+ * @param savepoint The savepoint.
+ * - CarIndex
+ * - EntityPosition
+ * @param handleExcuseMe true to handle the kActionExcuseMe/kActionExcuseMeCath actions.
+ */
+ void updateEntity(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Call a specific savepoint (or draw sequence in default case)
+ *
+ * @param savepoint The savepoint.
+ * - Sequence to draw in default case
+ * - EntityIndex
+ * - ActionIndex
+ * - Sequence for the savepoint
+ * @param handleExcuseMe true to handle excuse me.
+ */
+ void callSavepoint(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param savepoint The savepoint.
+ * @param position1 The first position.
+ * @param position2 The second position.
+ * @param car The car.
+ * @param compartment The compartment.
+ * @param alternate true to use the alternate version of SceneManager::loadSceneFromObject()
+ */
+ void enterExitCompartment(const SavePoint &savepoint, EntityPosition position1 = kPositionNone, EntityPosition position2 = kPositionNone, CarIndex car = kCarNone, ObjectIndex compartment = kObjectNone, bool alternate = false, bool updateLocation = false);
+
+ /**
+ * Updates the position
+ *
+ * @param savepoint The savepoint
+ * - Sequence name
+ * - CarIndex
+ * - Position
+ * @param handleExcuseMe true to handle excuseMe actions
+ */
+ void updatePosition(const SavePoint &savepoint, bool handleExcuseMe = false);
+};
+
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITY_H
diff --git a/engines/lastexpress/entities/entity39.cpp b/engines/lastexpress/entities/entity39.cpp
new file mode 100644
index 0000000000..6d139094c7
--- /dev/null
+++ b/engines/lastexpress/entities/entity39.cpp
@@ -0,0 +1,103 @@
+/* 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/entities/entity39.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Entity39::Entity39(LastExpressEngine *engine) : Entity(engine, kEntity39) {
+ ADD_CALLBACK_FUNCTION(Entity39, chapter1);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter2);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter3);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter4);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter5);
+ ADD_CALLBACK_FUNCTION(Entity39, process);
+
+ memset(&_sequence, 0, 12);
+ _counter = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Entity39, chapter1)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Entity39, chapter2)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Entity39, chapter3)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Entity39, chapter4)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Entity39, chapter5)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Entity39, process)
+// TODO: _sequence & counter do not seem to be touched anywhere else in the code :(
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->drawSequenceRight(kEntity39, (char *)&_sequence);
+ break;
+
+ case kActionNone:
+ getData()->car = getEntityData(kEntityPlayer)->car;
+
+ if (*_sequence && !_counter) {
+ _counter++;
+ getEntities()->drawSequenceRight(kEntity39, (char *)&_sequence);
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity39.h b/engines/lastexpress/entities/entity39.h
new file mode 100644
index 0000000000..652dbd139e
--- /dev/null
+++ b/engines/lastexpress/entities/entity39.h
@@ -0,0 +1,78 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY39_H
+#define LASTEXPRESS_ENTITY39_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Entity39 : public Entity {
+public:
+ Entity39(LastExpressEngine *engine);
+ ~Entity39() {};
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Process function
+ */
+ DECLARE_FUNCTION(process)
+
+private:
+ char _sequence[12];
+ int _counter;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_##define##_H
diff --git a/engines/lastexpress/entities/entity_intern.h b/engines/lastexpress/entities/entity_intern.h
new file mode 100644
index 0000000000..6442f76b3c
--- /dev/null
+++ b/engines/lastexpress/entities/entity_intern.h
@@ -0,0 +1,577 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY_INTERN_H
+#define LASTEXPRESS_ENTITY_INTERN_H
+
+namespace LastExpress {
+
+#define LOBYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff))
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+#define ENTITY_CALLBACK(class, name, pointer) \
+ Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name)
+
+#define ADD_CALLBACK_FUNCTION(class, name) \
+ _callbacks.push_back(new ENTITY_CALLBACK(class, name, this));
+
+#define ADD_NULL_FUNCTION() \
+ _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this));
+
+//////////////////////////////////////////////////////////////////////////
+// Declaration
+//////////////////////////////////////////////////////////////////////////
+
+#define DECLARE_FUNCTION(name) \
+ void setup_##name(); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_1(name, param1) \
+ void setup_##name(param1); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_2(name, param1, param2) \
+ void setup_##name(param1, param2); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_3(name, param1, param2, param3) \
+ void setup_##name(param1, param2, param3); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \
+ void setup_##name(param1, param2, param3, param4); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_NOSETUP(name) \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_NULL_FUNCTION() \
+ void setup_nullfunction();
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+
+#define IMPLEMENT_SETUP(class, callback_class, name, index) \
+void class::setup_##name() { \
+ BEGIN_SETUP(callback_class, name, index, EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::setup_" #name "()"); \
+ END_SETUP() \
+}
+
+#define BEGIN_SETUP(class, name, index, type) \
+ _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, new ENTITY_CALLBACK(class, name, this)); \
+ _data->setCurrentCallback(index); \
+ _data->resetCurrentParameters<type>();
+
+#define END_SETUP() \
+ _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault);
+
+
+//////////////////////////////////////////////////////////////////////////
+// Implementation
+//////////////////////////////////////////////////////////////////////////
+
+// Expose parameters and check validity
+#define EXPOSE_PARAMS(type) \
+ type *params = (type*)_data->getCurrentParameters(); \
+ if (!params) \
+ error("Trying to call an entity function with invalid parameters!"); \
+
+
+// function signature without setup (we keep the index for consistency but never use it)
+#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \
+ void class::name(const SavePoint &savepoint) { \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")");
+
+// simple setup with no parameters
+#define IMPLEMENT_FUNCTION(index, class, name) \
+ IMPLEMENT_SETUP(class, class, name, index) \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action));
+
+// nullfunction call
+#define IMPLEMENT_NULL_FUNCTION(index, class) \
+ IMPLEMENT_SETUP(class, Entity, nullfunction, index)
+
+// setup with one uint parameter
+#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \
+ void class::setup_##name(paramType param1) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = (unsigned int)param1; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action));
+
+// setup with two uint parameters
+#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action));
+
+// setup with three uint parameters
+#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ params->param3 = param3; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action));
+
+// setup with on char* parameter
+#define IMPLEMENT_FUNCTION_S(index, class, name) \
+ void class::setup_##name(const char* seq1) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)&params->seq1, ACTION_NAME(savepoint.action));
+
+// setup with on char* parameter and one uint
+#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \
+ void class::setup_##name(const char* seq1, paramType2 param4) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)&params->seq1, params->param4, ACTION_NAME(savepoint.action));
+
+// setup with on char* parameter and two uints
+#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \
+ void class::setup_##name(const char* seq1, paramType2 param4, paramType3 param5) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)&params->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action));
+
+// setup with on char* parameter and three uints
+#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \
+ void class::setup_##name(const char* seq, paramType2 param4, paramType3 param5, paramType4 param6) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIII) \
+ EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq, seq, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ params->param6 = param6; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)&params->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \
+ void class::setup_##name(const char* seq1, paramType2 param4, paramType3 param5, const char* seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)&params->seq1, params->param4, params->param5, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SS(index, class, name) \
+ void class::setup_##name(const char* seq1, const char* seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \
+ EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \
+ void class::setup_##name(const char* seq1, const char* seq2, paramType3 param7) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \
+ EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ params->param7 = param7; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)&params->seq1, (char *)&params->seq2, params->param7, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \
+ void class::setup_##name(paramType param1, const char* seq) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersISII) \
+ EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII*)_data->getCurrentParameters(); \
+ params->param1 = (unsigned int)param1; \
+ strncpy((char *)&params->seq, seq, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersISII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)&params->seq, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \
+ void class::setup_##name(paramType param1, const char* seq1, const char* seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersISSI) \
+ EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersISSI) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, const char* seq) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISI) \
+ EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ strncpy((char *)&params->seq, seq, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIISI) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)&params->seq, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, const char* seq1, const char* seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISS) \
+ EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIISS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+#define RESET_ENTITY_STATE(entity, class, function) \
+ getEntities()->resetState(entity); \
+ ((class*)getEntities()->get(entity))->function();
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters macros (for default IIII parameters)
+//////////////////////////////////////////////////////////////////////////
+#define CURRENT_PARAMS(index, id) \
+ ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id
+
+#define ENTITY_PARAM(index, id) \
+ ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id
+
+//////////////////////////////////////////////////////////////////////////
+// Time check macros
+//////////////////////////////////////////////////////////////////////////
+#define TIME_CHECK_CHAPTER1(function) \
+ TIME_CHECK(kTimeChapter1, params->param1, function)
+
+#define TIME_CHECK(timeValue, parameter, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_SAVEPOINT(timeValue, parameter, entity1, entity2, action) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getSavePoints()->push(entity1, entity2, action); \
+ }
+
+#define TIME_CHECK_CALLBACK(timeValue, parameter, callback, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_1(timeValue, parameter, callback, function, param1) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_2(timeValue, parameter, callback, function, param1, param2) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1, param2); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_3(timeValue, parameter, callback, function, param1, param2, param3) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1, param2, param3); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_INVENTORY(timeValue, parameter, callback, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->inventoryItem = kItemNone; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_ACTION(timeValue, parameter) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ CALLBACK_ACTION(); \
+ break; \
+ }
+
+#define TIME_CHECK_SAVEGAME(timeValue, parameter, callback, type, event) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ setup_savegame(type, event); \
+ break; \
+ }
+
+#define TIME_CHECK_ENTERSTATION(timeValue, parameter, callback, name, param2) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ setup_enterStation(name, param2); \
+ break; \
+ }
+
+#define TIME_CHECK_EXITSTATION(timeValue, parameter, callback, name) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ setup_exitStation(name); \
+ break; \
+ }
+
+#define TIME_CHECK_EXITSTATION_2(timeValue, parameter1, parameter2, callback, name) \
+ if (getState()->time > timeValue && !parameter1) { \
+ parameter1 = 1; \
+ parameter2 = 1; \
+ setCallback(callback); \
+ setup_exitStation(name); \
+ break; \
+ }
+
+#define TIME_CHECK_EXITSTATION_0(parameter1, parameter2, callback, name) \
+ if (parameter1 && !parameter2) { \
+ setCallback(callback); \
+ setup_exitStation(name); \
+ break; \
+ }
+
+#define TIME_CHECK_PLAYSOUND(timeValue, parameter, callback, sound) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ setup_playSound(sound); \
+ break; \
+ }
+
+#define TIME_CHECK_PLAYSOUND_UPDATEPOSITION(timeValue, parameter, callback, sound, position) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->entityPosition = position; \
+ setCallback(callback); \
+ setup_playSound(sound); \
+ break; \
+ }
+
+#define TIME_CHECK_OBJECT(timeValue, parameter, object, location) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getObjects()->updateLocation2(object, location); \
+ }
+
+#define TIME_CHECK_POSITION(timeValue, parameter, position) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->entityPosition = position; \
+ }
+
+#define TIME_CHECK_CAR(timeValue, parameter, callback, function) {\
+ if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) \
+ parameter = getState()->time + 75; \
+ if (getState()->time > timeValue || parameter < getState()->time) { \
+ parameter = kTimeInvalid; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ } \
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callback action
+//////////////////////////////////////////////////////////////////////////
+#define CALLBACK_ACTION() { \
+ if (getData()->currentCall == 0) \
+ error("CALLBACK_ACTION: currentCall is already 0, cannot proceed!"); \
+ getData()->currentCall--; \
+ getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); \
+ getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); \
+ }
+
+//////////////////////////////////////////////////////////////////////////
+// Param update
+//////////////////////////////////////////////////////////////////////////
+#define UPDATE_PARAM(parameter, type, value) { \
+ if (!parameter) \
+ parameter = type + value; \
+ if (parameter >= type) \
+ break; \
+ parameter = kTimeInvalid; \
+}
+
+// Todo: replace with UPDATE_PARAM_PROC as appropriate
+#define UPDATE_PARAM_GOTO(parameter, type, value, label) { \
+ if (!parameter) \
+ parameter = type + value; \
+ if (parameter >= type) \
+ goto label; \
+ parameter = kTimeInvalid; \
+}
+
+// Updating parameter with code inside the check
+#define UPDATE_PARAM_PROC(parameter, type, value) \
+ if (!parameter) \
+ parameter = type + value; \
+ if (parameter < type) { \
+ parameter = kTimeInvalid;
+
+#define UPDATE_PARAM_PROC_END }
+
+// Updating parameter with an added check (and code inside the check)
+#define UPDATE_PARAM_CHECK(parameter, type, value) \
+ if (!parameter || parameter < type) { \
+ if (!parameter) \
+ parameter = type + value;
+
+//////////////////////////////////////////////////////////////////////////
+// Compartments
+//////////////////////////////////////////////////////////////////////////
+// Go from one compartment to another (or the same one if no optional args are passed
+#define COMPARTMENT_TO(class, compartmentFrom, positionFrom, sequenceFrom, sequenceTo) \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionDefault: \
+ getData()->entityPosition = positionFrom; \
+ setCallback(1); \
+ setup_enterExitCompartment(sequenceFrom, compartmentFrom); \
+ break; \
+ case kActionCallback: \
+ switch (getCallback()) { \
+ default: \
+ break; \
+ case 1: \
+ setCallback(2); \
+ setup_enterExitCompartment(sequenceTo, compartmentFrom); \
+ break; \
+ case 2: \
+ getData()->entityPosition = positionFrom; \
+ getEntities()->clearSequences(_entityIndex); \
+ CALLBACK_ACTION(); \
+ } \
+ break; \
+ }
+
+#define COMPARTMENT_FROM_TO(class, compartmentFrom, positionFrom, sequenceFrom, compartmentTo, positionTo, sequenceTo) \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionDefault: \
+ getData()->entityPosition = positionFrom; \
+ getData()->location = kLocationOutsideCompartment; \
+ setCallback(1); \
+ setup_enterExitCompartment(sequenceFrom, compartmentFrom); \
+ break; \
+ case kActionCallback: \
+ switch (getCallback()) { \
+ default: \
+ break; \
+ case 1: \
+ setCallback(2); \
+ setup_updateEntity(kCarGreenSleeping, positionTo); \
+ break; \
+ case 2: \
+ setCallback(3); \
+ setup_enterExitCompartment(sequenceTo, compartmentTo); \
+ break; \
+ case 3: \
+ getData()->location = kLocationInsideCompartment; \
+ getEntities()->clearSequences(_entityIndex); \
+ CALLBACK_ACTION(); \
+ break; \
+ } \
+ break; \
+ }
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITY_INTERN_H
diff --git a/engines/lastexpress/entities/francois.cpp b/engines/lastexpress/entities/francois.cpp
new file mode 100644
index 0000000000..fd1237f8a3
--- /dev/null
+++ b/engines/lastexpress/entities/francois.cpp
@@ -0,0 +1,1043 @@
+/* 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/entities/francois.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Francois::Francois(LastExpressEngine *engine) : Entity(engine, kEntityFrancois) {
+ ADD_CALLBACK_FUNCTION(Francois, reset);
+ ADD_CALLBACK_FUNCTION(Francois, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Francois, draw);
+ ADD_CALLBACK_FUNCTION(Francois, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Francois, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Francois, playSound);
+ ADD_CALLBACK_FUNCTION(Francois, savegame);
+ ADD_CALLBACK_FUNCTION(Francois, updateEntity);
+ ADD_CALLBACK_FUNCTION(Francois, function9);
+ ADD_CALLBACK_FUNCTION(Francois, function10);
+ ADD_CALLBACK_FUNCTION(Francois, function11);
+ ADD_CALLBACK_FUNCTION(Francois, function12);
+ ADD_CALLBACK_FUNCTION(Francois, function13);
+ ADD_CALLBACK_FUNCTION(Francois, function14);
+ ADD_CALLBACK_FUNCTION(Francois, function15);
+ ADD_CALLBACK_FUNCTION(Francois, function16);
+ ADD_CALLBACK_FUNCTION(Francois, chapter1);
+ ADD_CALLBACK_FUNCTION(Francois, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function19);
+ ADD_CALLBACK_FUNCTION(Francois, function20);
+ ADD_CALLBACK_FUNCTION(Francois, chapter2);
+ ADD_CALLBACK_FUNCTION(Francois, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function23);
+ ADD_CALLBACK_FUNCTION(Francois, chapter3);
+ ADD_CALLBACK_FUNCTION(Francois, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Francois, chapter4);
+ ADD_CALLBACK_FUNCTION(Francois, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Francois, chapter5);
+ ADD_CALLBACK_FUNCTION(Francois, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function30);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Francois, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, Francois, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Francois, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Francois, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, Francois, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_5790, kPosition_6130, kCarRedSleeping, kObjectCompartmentD, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Francois, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Francois, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition)
+ error("Francois: callback function 8 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Francois, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getObjects()->get(kObjectCompartmentD).location == kObjectLocation2) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction134289824);
+ setCallback(1);
+ setup_enterExitCompartment("605Cd", kObjectCompartmentD);
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("605Ed", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Francois, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getObjects()->get(kObjectCompartmentD).location == kObjectLocation2) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(1);
+ setup_enterExitCompartment("605Bd", kObjectCompartmentD);
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("605Dd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction102484312);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityFrancois)) {
+
+ UPDATE_PARAM_PROC(CURRENT_PARAMS(1, 1), getState()->timeTicks, params->param6)
+ switch (rnd(7)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityFrancois, "Fra1002A");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityFrancois, "Fra1002B");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityFrancois, "Fra1002C");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityFrancois, "Fra1002D");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityFrancois, "Fra1002E");
+ break;
+
+ case 5:
+ case 6:
+ getSound()->playSound(kEntityFrancois, "Fra1002F");
+ break;
+ }
+
+ params->param6 = 15 * rnd(7);
+ CURRENT_PARAMS(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->hasValidFrame(kEntityFrancois) || !getEntities()->isWalkingOppositeToPlayer(kEntityFrancois))
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityFrancois, (CarIndex)params->param2, (EntityPosition)params->param3)) {
+ params->param5 = 0;
+
+ if (params->param3 == kPosition_540) {
+ params->param2 = (getProgress().chapter == kChapter1) ? kCarRedSleeping : kCarGreenSleeping;
+ params->param3 = kPosition_9460;
+ } else {
+ params->param2 = kCarGreenSleeping;
+ params->param3 = kPosition_540;
+ params->param7 = 0;
+ params->param8 = 0;
+
+ getSavePoints()->push(kEntityFrancois, kEntityCoudert, kAction225932896);
+ getSavePoints()->push(kEntityFrancois, kEntityMertens, kAction225932896);
+ }
+ }
+
+ if (getEntities()->checkDistanceFromPosition(kEntityFrancois, kPosition_2000, 500) && getData()->direction == kDirectionDown) {
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarRedSleeping) && params->param8) {
+ setCallback(2);
+ setup_draw("605A");
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarGreenSleeping) && params->param7) {
+ setCallback(3);
+ setup_draw("605A");
+ break;
+ }
+ }
+
+label_callback:
+ if (getProgress().chapter == kChapter1) {
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarRedSleeping)
+ && (getEntities()->hasValidFrame(kEntityFrancois) || params->param1 < getState()->time || params->param4)
+ && !params->param5
+ && getData()->entityPosition < getEntityData(kEntityMmeBoutarel)->entityPosition) {
+
+ if (getData()->direction == kDirectionDown) {
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction202221040);
+ params->param4 = 1;
+ params->param5 = 1;
+ } else if (params->param4 && getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityMmeBoutarel, 1000)) {
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction168986720);
+ params->param5 = 1;
+ }
+ }
+ } else if (params->param1 < getState()->time) {
+ getData()->clothes = kClothesDefault;
+ getData()->field_4A3 = 30;
+ getData()->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(kEntityFrancois))
+ getSound()->processEntry(kEntityFrancois);
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(kEntityFrancois))
+ getSound()->processEntry(kEntityFrancois);
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisWhistle);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventFrancoisWhistle)
+ && !getEvent(kEventFrancoisWhistleD)
+ && !getEvent(kEventFrancoisWhistleNight)
+ && !getEvent(kEventFrancoisWhistleNightD))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->clothes = kClothes1;
+ getData()->field_4A3 = 100;
+ getData()->inventoryItem = kItemNone;
+
+ params->param2 = kCarGreenSleeping;
+ params->param3 = kPosition_540;
+
+ getEntities()->updateEntity(kEntityFrancois, kCarGreenSleeping, kPosition_540);
+
+ params->param6 = 15 * rnd(7);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityFrancois, kEntityCoudert, kAction168253822);
+ // Fallback to next case
+
+ case 3:
+ params->param2 = kCarRedSleeping;
+ params->param3 = kPosition_9460;
+ params->param5 = 0;
+
+ getData()->entityPosition = kPosition_2088;
+
+ getEntities()->updateEntity(kEntityFrancois, kCarRedSleeping, kPosition_9460);
+ goto label_callback;
+
+ case 4:
+ setCallback(5);
+ setup_function10();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ if (getProgress().jacket == kJacketGreen) {
+ if (isNight())
+ getAction()->playAnimation(getData()->entityPosition <= getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisWhistleNightD : kEventFrancoisWhistleNight);
+ else
+ getAction()->playAnimation(getData()->entityPosition <= getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisWhistleD : kEventFrancoisWhistleD);
+ }
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750 * (getData()->direction == kDirectionUp ? -1 : 1)), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+
+ case kAction102752636:
+ getEntities()->clearSequences(kEntityFrancois);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ getData()->clothes = kClothesDefault;
+ getData()->field_4A3 = 30;
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction205346192:
+ if (savepoint.entity2 == kEntityCoudert)
+ params->param8 = 1;
+ else if (savepoint.entity2 == kEntityMertens)
+ params->param7 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Francois, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(675);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(675);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function10();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Francois, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("605Df", kObjectCompartment6);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+
+ setCallback(5);
+ setup_playSound("Har2010");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityFrancois, kEntityAlouan, kAction189489753);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4840);
+ break;
+
+ case 7:
+ if (getInventory()->hasItem(kItemWhistle) || getInventory()->get(kItemWhistle)->location == kObjectLocation3) {
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityFrancois, "605He");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateFromTime(450);
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityFrancois, kObjectCompartmentE, true);
+
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function10();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction190219584:
+ setCallback(6);
+ setup_enterExitCompartment("605Ef", kObjectCompartment6);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(14, Francois, function14, ObjectIndex, EntityPosition)
+ error("Francois: callback function 14 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Francois, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getData()->entityPosition >= getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ } else {
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ }
+ break;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function10();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ if (!getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound("Fra2012");
+ break;
+
+ case 8:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Francois, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityBoutarel)->entityPosition;
+ getData()->location = getEntityData(kEntityBoutarel)->location;
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_enterExitCompartment("605Cd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_5890;
+
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction101107728);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityFrancois);
+ getSavePoints()->push(kEntityFrancois, kEntityBoutarel, kAction237889408);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("605Id", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction100957716);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Francois, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Francois, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTimeParisEpernay, params->param1, 1, setup_function11, kTime1093500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function19();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Francois, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1161000, params->param1, 2, setup_function12);
+ break;
+
+ case kAction101107728:
+ setCallback(1);
+ setup_function16();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Francois, function20)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityFrancois);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21 ,Francois, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Francois, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("605Id", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction100957716);
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+ setup_function23();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Francois, function23)
+ error("Francois: callback function 23 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Francois, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Francois, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventFrancoisShowBeetle) || getEvent(kEventFrancoisShowBeetleD))
+ if (!getEvent(kEventFrancoisTradeWhistle) && !getEvent(kEventFrancoisTradeWhistleD))
+ ENTITY_PARAM(0, 1) = 1;
+
+ if (params->param2 && getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790) && !params->param1) {
+
+ if (ENTITY_PARAM(0, 1) && getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ setCallback(2);
+ setup_function15();
+ break;
+ }
+
+label_callback_2:
+ TIME_CHECK_CALLBACK(kTime2025000, params->param3, 3, setup_function12);
+
+label_callback_3:
+ TIME_CHECK_CALLBACK(kTime2052000, params->param4, 4, setup_function12);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK(kTime2079000, params->param5, 5, setup_function12);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2092500, params->param6, 6, setup_function12);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK(kTime2173500, params->param7, 7, setup_function12);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2182500, params->param8, 8, setup_function12);
+
+label_callback_8:
+ TIME_CHECK_CALLBACK(kTime2241000, CURRENT_PARAMS(1, 1), 9, setup_function12);
+
+label_callback_9:
+ if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ TIME_CHECK_CALLBACK_1(kTime2011500, CURRENT_PARAMS(1, 2), 10, setup_function11, kTime2016000);
+
+label_callback_10:
+ TIME_CHECK_CALLBACK_1(kTime2115000, CURRENT_PARAMS(1, 3), 11, setup_function11, kTime2119500);
+ }
+
+label_callback_11:
+ if (getInventory()->get(kItemWhistle)->location == kObjectLocation3) {
+ if (getState()->time <= kTimeEnd)
+ if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000) || !params->param4)
+ params->param4 = getState()->time + 75;
+
+ if (params->param4 < getState()->time || getState()->time > kTimeEnd) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(12);
+ setup_playSound("Fra2010");
+ break;
+ }
+
+label_callback_12:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAMS(1, 5), 13, setup_function14, kObjectCompartmentE, kPosition_4840, "e");
+
+label_callback_13:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAMS(1, 6), 14, setup_function14, kObjectCompartmentF, kPosition_4070, "f");
+
+label_callback_14:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAMS(1, 7), 15, setup_function14, kObjectCompartmentB, kPosition_7500, "b");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param2 = 1;
+ break;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ getProgress().field_9C = 1;
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kAction189872836:
+ params->param1 = 1;
+ break;
+ case kAction190390860:
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Francois, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Francois, chapter4Handler)
+ if (savepoint.action == kAction101107728) {
+ setCallback(1);
+ setup_function16();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Francois, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Francois, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5) {
+ if (!getInventory()->hasItem(kItemWhistle)
+ && getInventory()->get(kItemWhistle)->location != kObjectLocation3)
+ getInventory()->setLocationAndProcess(kItemWhistle, kObjectLocation1);
+
+ setup_function30();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Francois, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(31, Francois)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/francois.h b/engines/lastexpress/entities/francois.h
new file mode 100644
index 0000000000..894a60bc5b
--- /dev/null
+++ b/engines/lastexpress/entities/francois.h
@@ -0,0 +1,170 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_FRANCOIS_H
+#define LASTEXPRESS_FRANCOIS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Francois : public Entity {
+public:
+ Francois(LastExpressEngine *engine);
+ ~Francois() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION_1(function11, TimeValue timeValue)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION_3(function14, ObjectIndex compartment, EntityPosition entityPosition, const char *str)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function30)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FRANCOIS_H
diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp
new file mode 100644
index 0000000000..3af85ffdb9
--- /dev/null
+++ b/engines/lastexpress/entities/gendarmes.cpp
@@ -0,0 +1,491 @@
+/* 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/entities/gendarmes.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Gendarmes::Gendarmes(LastExpressEngine *engine) : Entity(engine, kEntityGendarmes) {
+ ADD_CALLBACK_FUNCTION(Gendarmes, reset);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter1);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestDraw);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestPlaysound);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestPlaysound16);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestCallback);
+ ADD_CALLBACK_FUNCTION(Gendarmes, savegame);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestUpdateEntity);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function9);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function10);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function12);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function13);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter2);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter3);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter4);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Gendarmes, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Gendarmes, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Gendarmes, arrestDraw)
+ arrest(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Gendarmes, arrestPlaysound)
+ arrest(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(5, Gendarmes, arrestPlaysound16)
+ arrest(savepoint, true, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Gendarmes, arrestCallback, TimeValue)
+ arrest(savepoint, true, SoundManager::kFlagInvalid, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Gendarmes, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Gendarmes, arrestUpdateEntity, CarIndex, EntityPosition)
+ arrest(savepoint, true, SoundManager::kFlagInvalid, false, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition)
+ error("Gendarmes: callback function 9 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param5 || getState()->timeTicks > (uint32)params->param5) {
+ if (!params->param5)
+ params->param5 = getState()->timeTicks + 75;
+
+ if (!getEntities()->isOutsideAlexeiWindow() && getObjects()->get((ObjectIndex)params->param3).location != kObjectLocation1) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ break;
+ }
+ }
+
+ if (!params->param6)
+ params->param6 = getState()->timeTicks + 150;
+
+ if (params->param6 == 0 || getState()->timeTicks > (uint32)params->param6) {
+ params->param6 = kTimeInvalid;
+
+ getSound()->playSound(kEntityGendarmes, "POL1046A", SoundManager::kFlagDefault);
+ }
+
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 300);
+
+ if (!params->param4 && getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ } else {
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityGendarmes, "LIB017", SoundManager::kFlagDefault);
+
+ setCallback(getProgress().jacket == kJacketBlood ? 3 : 4);
+ setup_savegame(kSavegameTypeEvent, getProgress().jacket == kJacketBlood ? kEventMertensBloodJacket : kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionKnock:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_arrestPlaysound16("POL1046B");
+ break;
+
+ case kActionOpenDoor:
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ break;
+
+ case kActionDefault:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_arrestPlaysound16("POL1046");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorTalk, kCursorNormal);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityGendarmes, "LIB014", SoundManager::kFlagDefault);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 3:
+ getAction()->playAnimation((params->param1 < kCarRedSleeping) ? kEventMertensBloodJacket : kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorHand);
+ break;
+
+ case 6:
+ getSound()->playSound(kEntityGendarmes, "LIB014", SoundManager::kFlagDefault);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Gendarmes, chapter1Handler)
+ if (savepoint.action == kAction169499649) {
+ getSavePoints()->push(kEntityGendarmes, kEntityMertens, kAction190082817);
+ setup_function12();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Gendarmes, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getProgress().field_14 = 29;
+
+ setCallback(1);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_5540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function9(kCarGreenSleeping, kPosition_5790, "d", "A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_6220);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function9(kCarGreenSleeping, kPosition_6470, "c", "B");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_7250);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function9(kCarGreenSleeping, kPosition_7500, "b", "C");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_7950);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function9(kCarGreenSleeping, kPosition_8200, "a", "NODIALOG");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 9:
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping) {
+ getProgress().field_14 = 0;
+ getEntities()->clearSequences(kEntityGendarmes);
+ getSavePoints()->push(kEntityGendarmes, kEntityVerges, kAction168710784);
+ setup_function13();
+ break;
+ }
+
+ setCallback(10);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_2490);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function9(kCarRedSleeping, kPosition_2740, "h", "NODIALOG");
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_3820);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function9(kCarRedSleeping, kPosition_4070, "f", "E");
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_4590);
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function9(kCarRedSleeping, kPosition_4840, "e", "F");
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_5540);
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_function9(kCarRedSleeping, kPosition_5790, "d", "G");
+ break;
+
+ case 17:
+ setCallback(18);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_6220);
+ break;
+
+ case 18:
+ setCallback(19);
+ setup_function9(kCarRedSleeping, kPosition_6470, "c", "H");
+ break;
+
+ case 19:
+ setCallback(20);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_7250);
+ break;
+
+ case 20:
+ setCallback(21);
+ setup_function9(kCarRedSleeping, kPosition_7500, "b", "J");
+ break;
+
+ case 21:
+ setCallback(22);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_7950);
+ break;
+
+ case 22:
+ setCallback(23);
+ setup_function9(kCarRedSleeping, kPosition_8200, "a", "NODIALOG");
+ break;
+
+ case 23:
+ setCallback(24);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 24:
+ getProgress().field_14 = 0;
+ getEntities()->clearSequences(kEntityGendarmes);
+ getSavePoints()->push(kEntityGendarmes, kEntityVerges, kAction168710784);
+ setup_function13();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Gendarmes, function13)
+ if (savepoint.action == kActionDefault)
+ getData()->car = kCarNone;
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Gendarmes, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Gendarmes, chapter3)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Gendarmes, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Gendarmes, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundManager::FlagType flag, bool checkCallback, bool shouldUpdateEntity) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (checkCallback) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ TIME_CHECK_CALLBACK_ACTION(params->param1, params->param2);
+ }
+
+ if (shouldUpdateEntity) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ // Fallback to next action
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(0, 1) && getEntities()->hasValidFrame(kEntityGendarmes)) {
+ getSound()->playSound(kEntityPlayer, "MUS007");
+ ENTITY_PARAM(0, 1) = 1;
+ }
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityGendarmes, kEntityPlayer, 1000) && !getEntityData(kEntityPlayer)->location) {
+
+ if (shouldUpdateEntity)
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 22) && !getEntities()->isDistanceBetweenEntities(kEntityGendarmes, kEntityPlayer, 250))
+ break;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ // Only handle when passing SIIS params
+ if (!checkCallback) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ if (!shouldPlaySound)
+ getEntities()->drawSequenceRight(kEntityGendarmes, (char *)&params->seq1);
+ else
+ getSound()->playSound(kEntityGendarmes, (char *)&params->seq1, flag);
+ }
+
+ if (shouldUpdateEntity) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/gendarmes.h b/engines/lastexpress/entities/gendarmes.h
new file mode 100644
index 0000000000..b0aec28bfb
--- /dev/null
+++ b/engines/lastexpress/entities/gendarmes.h
@@ -0,0 +1,99 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_GENDARMES_H
+#define LASTEXPRESS_GENDARMES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+#include "lastexpress/game/sound.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Gendarmes : public Entity {
+public:
+ Gendarmes(LastExpressEngine *engine);
+ ~Gendarmes() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_1(arrestDraw, const char *sequence)
+ DECLARE_FUNCTION_1(arrestPlaysound, const char *soundName)
+ DECLARE_FUNCTION_1(arrestPlaysound16, const char *soundName)
+ DECLARE_FUNCTION_1(arrestCallback, TimeValue timeValue)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION_2(arrestUpdateEntity, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_4(function9, CarIndex car, EntityPosition entityPosition, const char *sequence1, const char *sequence2)
+ DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, ObjectIndex object)
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+private:
+ void arrest(const SavePoint &savepoint, bool playSound = false, SoundManager::FlagType flag = SoundManager::kFlagInvalid, bool checkCallback = false, bool shouldUpdateEntity = false);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_GENDARMES_H
diff --git a/engines/lastexpress/entities/hadija.cpp b/engines/lastexpress/entities/hadija.cpp
new file mode 100644
index 0000000000..564ac942ca
--- /dev/null
+++ b/engines/lastexpress/entities/hadija.cpp
@@ -0,0 +1,529 @@
+/* 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/entities/hadija.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Hadija::Hadija(LastExpressEngine *engine) : Entity(engine, kEntityHadija) {
+ ADD_CALLBACK_FUNCTION(Hadija, reset);
+ ADD_CALLBACK_FUNCTION(Hadija, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Hadija, playSound);
+ ADD_CALLBACK_FUNCTION(Hadija, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Hadija, updateEntity);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment6);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment8);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment6to8);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment8to6);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter1);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function12);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter2);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter3);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter4);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function19);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter5);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function22);
+ ADD_CALLBACK_FUNCTION(Hadija, function23);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Hadija, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Hadija, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Hadija, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Hadija, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Hadija, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Hadija, compartment6)
+ COMPARTMENT_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Cf", "619Df");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Hadija, compartment8)
+ COMPARTMENT_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Hadija, compartment6to8)
+ COMPARTMENT_FROM_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Hadija, compartment8to6)
+ COMPARTMENT_FROM_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Hadija, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Hadija, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime1084500, params->param2, 2, setup_compartment6to8);
+
+label_callback2:
+ if (params->param3 != kTimeInvalid && getState()->time > kTime1093500) {
+
+ if (getState()->time <= kTime1134000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !getEntities()->isInsideCompartment(kEntityMahmud, kCarGreenSleeping, kPosition_5790) || !params->param3) {
+ params->param3 = getState()->time + 75;
+
+ if (!params->param3) {
+ setCallback(3);
+ setup_compartment8();
+ return;
+ }
+ }
+
+ if (params->param3 >= getState()->time)
+ return;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ setCallback(3);
+ setup_compartment8();
+ }
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime1156500, params->param4, 4, setup_compartment8to6);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime1165500) {
+ if (getState()->time <= kTime1188000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !getEntities()->isInsideCompartment(kEntityMahmud, kCarGreenSleeping, kPosition_5790) || !params->param5) {
+ params->param5 = getState()->time + 75;
+
+ if (!params->param5) {
+ setCallback(5);
+ setup_compartment6();
+ return;
+ }
+ }
+
+ if (params->param5 >= getState()->time)
+ return;
+ }
+
+ params->param5 = kTimeInvalid;
+
+ setCallback(5);
+ setup_compartment6();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Hadija, function12)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityHadija);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Hadija, chapter2)
+ if (savepoint.action == kActionDefault) {
+
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_POSITION(kTime1782000, params->param1, kPosition_2740);
+
+ if (params->param2 == kTimeInvalid || getState()->time <= kTime1786500) {
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+ }
+
+ if (getState()->time <= kTime1818000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2)
+ params->param2 = getState()->time + 75;
+
+ if (params->param2 >= getState()->time) {
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+ }
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_compartment8();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("Har2012");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Hadija, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Hadija, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1998000, params->param1, 1, setup_compartment6to8);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2020500, params->param2, 2, setup_compartment8to6);
+
+label_callback2:
+ TIME_CHECK_CALLBACK(kTime2079000, params->param3, 3, setup_compartment6to8);
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime2187000, params->param4, 4, setup_compartment8to6);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime2196000)
+ TIME_CHECK_CAR(kTime2254500, params->param5, 5, setup_compartment6);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Hadija, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Hadija, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid)
+ TIME_CHECK_CAR(kTime1714500, params->param1, 1, setup_compartment6);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2367000, params->param2, 2, setup_compartment6to8);
+
+label_callback2:
+ TIME_CHECK_CALLBACK(kTime2421000, params->param3, 3, setup_compartment8to6);
+
+label_callback3:
+ if (params->param4 != kTimeInvalid && getState()->time > kTime2425500)
+ TIME_CHECK_CAR(kTime2484000, params->param4, 4, setup_compartment6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Hadija, function19)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityHadija);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Hadija, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Hadija, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function22();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Hadija, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setup_function23();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Hadija, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("619AF", kObjectCompartment6);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(24, Hadija)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/hadija.h b/engines/lastexpress/entities/hadija.h
new file mode 100644
index 0000000000..4792f97cad
--- /dev/null
+++ b/engines/lastexpress/entities/hadija.h
@@ -0,0 +1,144 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_HADIJA_H
+#define LASTEXPRESS_HADIJA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Hadija : public Entity {
+public:
+ Hadija(LastExpressEngine *engine);
+ ~Hadija() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(compartment6)
+ DECLARE_FUNCTION(compartment8)
+ DECLARE_FUNCTION(compartment6to8)
+ DECLARE_FUNCTION(compartment8to6)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_HADIJA_H
diff --git a/engines/lastexpress/entities/ivo.cpp b/engines/lastexpress/entities/ivo.cpp
new file mode 100644
index 0000000000..d56c184c15
--- /dev/null
+++ b/engines/lastexpress/entities/ivo.cpp
@@ -0,0 +1,829 @@
+/* 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/entities/ivo.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Ivo::Ivo(LastExpressEngine *engine) : Entity(engine, kEntityIvo) {
+ ADD_CALLBACK_FUNCTION(Ivo, reset);
+ ADD_CALLBACK_FUNCTION(Ivo, draw);
+ ADD_CALLBACK_FUNCTION(Ivo, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Ivo, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Ivo, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Ivo, updateEntity);
+ ADD_CALLBACK_FUNCTION(Ivo, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Ivo, playSound);
+ ADD_CALLBACK_FUNCTION(Ivo, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Ivo, savegame);
+ ADD_CALLBACK_FUNCTION(Ivo, function11);
+ ADD_CALLBACK_FUNCTION(Ivo, sitAtTableWithSalko);
+ ADD_CALLBACK_FUNCTION(Ivo, leaveTableWithSalko);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter1);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, function16);
+ ADD_CALLBACK_FUNCTION(Ivo, function17);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter2);
+ ADD_CALLBACK_FUNCTION(Ivo, function19);
+ ADD_CALLBACK_FUNCTION(Ivo, function20);
+ ADD_CALLBACK_FUNCTION(Ivo, function21);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter3);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter4);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, function26);
+ ADD_CALLBACK_FUNCTION(Ivo, function27);
+ ADD_CALLBACK_FUNCTION(Ivo, function28);
+ ADD_CALLBACK_FUNCTION(Ivo, function29);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter5);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, fight);
+ ADD_CALLBACK_FUNCTION(Ivo, function33);
+ ADD_CALLBACK_FUNCTION(Ivo, function34);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Ivo, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Ivo, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Ivo, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Ivo, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(5, Ivo, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(6, Ivo, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath || savepoint.action == kActionExcuseMe) {
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ return;
+ }
+
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Ivo, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(8, Ivo, playSound)
+ Entity::playSound(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Ivo, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Ivo, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Ivo, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityIvo, kEntitySalko, 750) || getEntities()->checkDistanceFromPosition(kEntitySalko, kPosition_2740, 500)) {
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction123668192);
+
+ setCallback(4);
+ setup_enterExitCompartment("613Ah", kObjectCompartmentH);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "809DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityIvo);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ if (getEntities()->isDistanceBetweenEntities(kEntityIvo, kEntitySalko, 750) || getEntities()->checkDistanceFromPosition(kEntitySalko, kPosition_2740, 500)) {
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction123668192);
+
+ setCallback(3);
+ setup_enterExitCompartment("613Ah", kObjectCompartmentH);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Hh");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH, true);
+ }
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Ivo, sitAtTableWithSalko)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntitySalko);
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "023A1");
+ getEntities()->drawSequenceRight(kEntitySalko, "023A2");
+ getEntities()->drawSequenceRight(kEntityTables2, "023A3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Ivo, leaveTableWithSalko)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kActionDrawTablesWithChairs, "009E");
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "023D1");
+ getEntities()->drawSequenceRight(kEntitySalko, "023D2");
+ getEntities()->drawSequenceRight(kEntityTables2, "023D3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Ivo, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject47, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Ivo, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityMilos)->entityPosition;
+ getData()->location = getEntityData(kEntityMilos)->location;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function11();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityIvo, kEntityMilos, kAction135024800);
+ setup_function16();
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_updateFromTicks(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Ivo, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityIvo);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Ch");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH);
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction88652208);
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("613Bh", kObjectCompartmentH);
+ break;
+
+ case kAction123852928:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+
+ setCallback(2);
+ setup_enterExitCompartment("613Dh", kObjectCompartmentH);
+ break;
+
+ case kAction221683008:
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Ivo, function17)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Ivo, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1777500, params->param1, setup_function19);
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject47, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Ivo, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("613FH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction136184016);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("809US");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_sitAtTableWithSalko();
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function20();
+ break;
+ }
+ break;
+
+ case kAction102675536:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Ivo, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1809000 && params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_leaveTableWithSalko();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction189688608);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction101106391);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ params->param1 = 1;
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction236237423);
+ setup_function21();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityIvo, "023C2");
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Ivo, function21)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Ivo, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Ivo, chapter3Handler)
+ if (savepoint.action == kActionDefault)
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Ivo, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Ivo, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2361600 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ setup_function26();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Ivo, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_leaveTableWithSalko();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function11();
+ break;
+
+ case 2:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Ivo, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityIvo);
+ setup_function28();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Ch");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction88652208);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ setCallback(1);
+ setup_enterExitCompartment("613FH", kObjectCompartmentH);
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_enterExitCompartment("613Bh", kObjectCompartmentH);
+ break;
+
+ case kAction123852928:
+ setCallback(4);
+ setup_enterExitCompartment("613Dh", kObjectCompartmentH);
+ break;
+
+ case kAction221683008:
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Ivo, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2425500 && !params->param1) {
+ params->param1 = 1;
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("613EH", kObjectCompartmentH);
+ break;
+
+ case 2:
+ setup_function29();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Ivo, function29)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Ivo, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggageRear;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Ivo, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_fight();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Ivo, fight)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_540;
+ getData()->car = kCarBaggageRear;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathIvoFight);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "LIB090");
+ getAction()->playAnimation(kEventCathIvoFight);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightIvo);
+ if (params->param1) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 96);
+ setup_function33();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Ivo, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getState()->time += 1800;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getObjects()->update(kObject94, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+
+ case kAction135800432:
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Ivo, function34)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityIvo);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/ivo.h b/engines/lastexpress/entities/ivo.h
new file mode 100644
index 0000000000..65bf9de165
--- /dev/null
+++ b/engines/lastexpress/entities/ivo.h
@@ -0,0 +1,177 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_IVO_H
+#define LASTEXPRESS_IVO_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Ivo : public Entity {
+public:
+ Ivo(LastExpressEngine *engine);
+ ~Ivo() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(sitAtTableWithSalko)
+ DECLARE_FUNCTION(leaveTableWithSalko)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(fight)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_IVO_H
diff --git a/engines/lastexpress/entities/kahina.cpp b/engines/lastexpress/entities/kahina.cpp
new file mode 100644
index 0000000000..b10b60476b
--- /dev/null
+++ b/engines/lastexpress/entities/kahina.cpp
@@ -0,0 +1,944 @@
+/* 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/entities/kahina.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Kahina::Kahina(LastExpressEngine *engine) : Entity(engine, kEntityKahina) {
+ ADD_CALLBACK_FUNCTION(Kahina, reset);
+ ADD_CALLBACK_FUNCTION(Kahina, playSound);
+ ADD_CALLBACK_FUNCTION(Kahina, savegame);
+ ADD_CALLBACK_FUNCTION(Kahina, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Kahina, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Kahina, function6);
+ ADD_CALLBACK_FUNCTION(Kahina, updateEntity2);
+ ADD_CALLBACK_FUNCTION(Kahina, updateEntity);
+ ADD_CALLBACK_FUNCTION(Kahina, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter1);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, function12);
+ ADD_CALLBACK_FUNCTION(Kahina, function13);
+ ADD_CALLBACK_FUNCTION(Kahina, function14);
+ ADD_CALLBACK_FUNCTION(Kahina, function15);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter2);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter3);
+ ADD_CALLBACK_FUNCTION(Kahina, function19);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, function21);
+ ADD_CALLBACK_FUNCTION(Kahina, function22);
+ ADD_CALLBACK_FUNCTION(Kahina, function23);
+ ADD_CALLBACK_FUNCTION(Kahina, function24);
+ ADD_CALLBACK_FUNCTION(Kahina, function25);
+ ADD_CALLBACK_FUNCTION(Kahina, function26);
+ ADD_CALLBACK_FUNCTION(Kahina, function27);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter4);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Kahina, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Kahina, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Kahina, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Kahina, updateFromTime, uint32)
+ if (savepoint.action == kAction137503360) {
+ ENTITY_PARAM(0, 2) = 1;
+ CALLBACK_ACTION();
+ }
+
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Kahina, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue)
+ error("Kahina: callback function 6 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7 ,Kahina, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ ENTITY_PARAM(0, 1) = 1;
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kAction137503360:
+ ENTITY_PARAM(0, 2) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Kahina, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1019" : "CAT1019A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(9, Kahina, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Kahina, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Kahina, chapter1Handler)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (getProgress().jacket != kJacketOriginal)
+ TIME_CHECK_SAVEPOINT(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837);
+
+ if (getProgress().eventMertensKronosInvitation)
+ setup_function12();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Kahina, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1485000, params->param2, setup_function13);
+ break;
+
+ case kActionKnock:
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ // Fallback to next action
+
+ case kActionOpenDoor:
+ if (!getEvent(kEventKronosGoingToInvitation)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosGoingToInvitation);
+ break;
+ }
+
+ if (savepoint.action == kActionOpenDoor)
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 80);
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction171849314);
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosGoingToInvitation);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80);
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction171849314);
+ params->param1 = 1;
+ }
+ break;
+
+ case kAction137685712:
+ setup_function13();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Kahina, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 || getState()->time >= kTime1201500 || params->param2 == kTimeInvalid || params->param1 >= getState()->time)
+ break;
+
+ if (getState()->time <= kTime1197000) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2) {
+ params->param2 = getState()->time;
+
+ if (!getState()->time)
+ goto label_callback;
+ }
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+label_callback:
+ setCallback(1);
+ setup_function15();
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = getState()->time + 1800;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Kahina, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF);
+ CALLBACK_ACTION();
+ break;
+
+ case kAction4:
+ getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityKahina, "616Cf");
+ getEntities()->enterCompartment(kEntityKahina, kObjectCompartmentF);
+ getSavePoints()->push(kEntityKahina, kEntityMax, kAction158007856);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Kahina, function15)
+ error("Kahina: callback function 15 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Kahina, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Kahina, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 9000)
+ params->param1 = 1;
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEvent(kEventKahinaAskSpeakFirebird) && getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 900)
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_3:
+ if (getState()->time > kTime1845000 && getEvent(kEventKronosConversationFirebird) && getEntities()->isInKronosSalon(kEntityPlayer)) {
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getEvent(kEventKronosConversationFirebird))
+ break;
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)) {
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getEvent(kEventTatianaGivePoem) || getEvent(kEventTatianaBreakfastGivePoem)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaAskSpeakFirebird);
+ break;
+ }
+
+ if (params->param1) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getAction()->playAnimation(kEventKahinaAskSpeak);
+ getScenes()->processScene();
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(8);
+ setup_playSound("KRO3003");
+ } else {
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 9 : 10);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(getCallback() == 1 ? 2 : 5);
+ setup_updateFromTime(900);
+ break;
+
+ case 2:
+ case 5:
+ setCallback(getCallback() == 2 ? 3 : 6);
+ setup_playSound("KRO3005");
+ break;
+
+ case 3:
+ goto label_callback_3;
+
+ case 7:
+ getAction()->playAnimation(kEventKahinaAskSpeakFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityKahina, "KRO3004");
+ break;
+
+ case 8:
+ case 9:
+ case 10:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ if (getCallback() == 8)
+ params->param1 = 0;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Kahina, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventAnnaBaggageArgument))
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function22);
+
+ if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1019" : "CAT1019A");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityKahina);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Kahina, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventKronosVisit))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4);
+ break;
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2079000 && !params->param2) {
+ params->param2 = 1;
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)
+ && !getEvent(kEventKronosConversationFirebird)
+ && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+label_callback_2:
+ if (getEntities()->isInKronosSalon(kEntityPlayer))
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+
+ setup_function21();
+ break;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 9000)
+ params->param1 = 1;
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)
+ && !getEvent(kEventKronosConversationFirebird)
+ && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ UPDATE_PARAM(params->param4, getState()->time, 900);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getEvent(kEventKronosConversationFirebird)) {
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getEvent(kEventTatianaGivePoem) || getEvent(kEventTatianaBreakfastGivePoem)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaAskSpeakFirebird);
+ break;
+ }
+
+ if (params->param1) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getAction()->playAnimation(kEventKahinaAskSpeak);
+ getScenes()->processScene();
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(10);
+ setup_playSound("KRO3003");
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 11 : 12);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ if (getEvent(kEventKronosConversationFirebird)) {
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ } else {
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, false);
+ goto label_callback_1;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ goto label_callback_2;
+
+ case 3:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(4);
+ setup_updateFromTime(900);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("KRO3005");
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_playSound("KRO3005");
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventKahinaAskSpeakFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityKahina, "KRO3004");
+ break;
+
+ case 10:
+ params->param1 = 0;
+ // Fallback to next case
+
+ case 11:
+ case 12:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Kahina, function21)
+ error("Kahina: callback function 21 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Kahina, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+
+ if (ENTITY_PARAM(0, 3) || location == kObjectLocation3 || location == kObjectLocation7) {
+ setCallback(1);
+ setup_function25();
+ } else if (location == kObjectLocation2 || location == kObjectLocation1) {
+ setCallback(2);
+ setup_function26();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+
+ case kActionDrawScene:
+ if (getData()->car > kCarGreenSleeping || (getData()->car == kCarGreenSleeping && getData()->entityPosition > kPosition_2740))
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Kahina, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina));
+ getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina), 15);
+
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_540;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_4455) || getEntities()->isOutsideAnnaWindow()) {
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("616Cf", kObjectCompartmentF);
+ }
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, getObjects()->get(kObjectCompartmentF).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityPlayer, getObjects()->get(kObject53).location, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_updateFromTime(900);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, getObjects()->get(kObjectCompartmentF).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, getObjects()->get(kObject53).location, kCursorHandKnock, kCursorHand);
+
+ setCallback(4);
+ setup_enterExitCompartment("616Df", kObjectCompartmentF);
+ break;
+
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 7:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Kahina, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param2, (EntityPosition)params->param3)) {
+ getEntities()->clearSequences(kEntityKahina);
+ params->param1 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos))
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kActionOpenDoor);
+ else
+ setup_function27();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function6(kTime2241000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 2)) {
+ getEntities()->clearSequences(kEntityKahina);
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ getProgress().field_44 = 0;
+
+ setup_function22();
+ } else if (ENTITY_PARAM(0, 1)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaGunYellow);
+ } else {
+ setup_function27();
+ }
+ break;
+
+ case 2:
+ if (getEntityData(kEntityPlayer)->entityPosition >= getData()->entityPosition)
+ getAction()->playAnimation(getData()->car < kCarRedSleeping ? kEventKahinaGunYellow : kEventKahinaGunBlue);
+ else
+ getAction()->playAnimation(kEventKahinaGun);
+
+ getEntities()->updateEntity(kEntityKahina, kCarKronos, kPosition_9270);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction235599361);
+ getSound()->playSound(kEntityKahina, "MUS016", SoundManager::kFlagDefault);
+ getProgress().field_44 = 1;
+
+ params->param1 = true;
+ params->param2 = kCarKronos;
+ params->param3 = kPosition_9270;
+ break;
+ }
+ break;
+
+ case kAction137503360:
+ getEntities()->clearSequences(kEntityKahina);
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ getProgress().field_44 = 0;
+
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Kahina, function25)
+ error("Kahina: callback function 25 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Kahina, function26)
+ error("Kahina: callback function 26 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Kahina, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer))
+ params->param1 = kEventKahinaPunchCar;
+ else if (getEntities()->isPlayerInCar(kCarGreenSleeping))
+ params->param1 = kEventKahinaPunchBlue;
+ else if (getEntities()->isPlayerInCar(kCarRedSleeping))
+ params->param1 = kEventKahinaPunchYellow;
+ else if (getEntities()->isInSalon(kEntityPlayer))
+ params->param1 = kEventKahinaPunchSalon;
+ else if (getEntities()->isInRestaurant(kEntityPlayer))
+ params->param1 = kEventKahinaPunchRestaurant;
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ params->param1 = kEventKahinaPunchKitchen;
+ else if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ params->param1 = kEventKahinaPunchBaggageCarEntrance;
+ else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage))
+ params->param1 = kEventKahinaPunchBaggageCar;
+
+ if (params->param1) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kSceneGameOverAlarm2);
+ }
+ break;
+
+ case kActionDefault:
+ getState()->timeDelta = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation((EventIndex)params->param1);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Kahina, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKahina);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Kahina, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKahina);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/kahina.h b/engines/lastexpress/entities/kahina.h
new file mode 100644
index 0000000000..1eba9c62d9
--- /dev/null
+++ b/engines/lastexpress/entities/kahina.h
@@ -0,0 +1,166 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_KAHINA_H
+#define LASTEXPRESS_KAHINA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Kahina : public Entity {
+public:
+ Kahina(LastExpressEngine *engine);
+ ~Kahina() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ DECLARE_FUNCTION_1(function6, TimeValue timeValue)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Update the entity, handling excuse me events and resetting the entity state after the argument with Anna in the baggage car
+ *
+ * @param car The car index
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function19, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_KAHINA_H
diff --git a/engines/lastexpress/entities/kronos.cpp b/engines/lastexpress/entities/kronos.cpp
new file mode 100644
index 0000000000..f45f18bbd2
--- /dev/null
+++ b/engines/lastexpress/entities/kronos.cpp
@@ -0,0 +1,666 @@
+/* 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/entities/kronos.h"
+
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tatiana.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Kronos::Kronos(LastExpressEngine *engine) : Entity(engine, kEntityKronos) {
+ ADD_CALLBACK_FUNCTION(Kronos, reset);
+ ADD_CALLBACK_FUNCTION(Kronos, savegame);
+ ADD_CALLBACK_FUNCTION(Kronos, updateEntity);
+ ADD_CALLBACK_FUNCTION(Kronos, playSound);
+ ADD_CALLBACK_FUNCTION(Kronos, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Kronos, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter1);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Kronos, function9);
+ ADD_CALLBACK_FUNCTION(Kronos, function10);
+ ADD_CALLBACK_FUNCTION(Kronos, function11);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter2);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter3);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Kronos, function15);
+ ADD_CALLBACK_FUNCTION(Kronos, function16);
+ ADD_CALLBACK_FUNCTION(Kronos, function17);
+ ADD_CALLBACK_FUNCTION(Kronos, function18);
+ ADD_CALLBACK_FUNCTION(Kronos, function19);
+ ADD_CALLBACK_FUNCTION(Kronos, function20);
+ ADD_CALLBACK_FUNCTION(Kronos, function21);
+ ADD_CALLBACK_FUNCTION(Kronos, function22);
+ ADD_CALLBACK_FUNCTION(Kronos, function23);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter4);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Kronos, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(2, Kronos, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Kronos, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Kronos, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Kronos, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(6, Kronos, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Kronos, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Kronos, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1489500, params->param2, setup_function11);
+ break;
+
+ case kAction171849314:
+ params->param1 = 1;
+ break;
+
+ case kAction202621266:
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Kronos, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversation);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosConversation);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction137685712);
+ setup_function10();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Kronos, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1489500, params->param1, setup_function11);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ getEntities()->clearSequences(kEntityKronos);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Kronos, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ params->param1++;
+ getSound()->playSound(kEntityKronos, (params->param1 & 1) ? "KRO1001" : "KRO1002");
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7000;
+
+ if (!getSound()->isBuffered(kEntityKronos))
+ getSound()->playSound(kEntityKronos, "KRO1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Kronos, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Kronos, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKronos);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Kronos, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1993500 && !params->param1 && !params->param2 && !params->param3)
+ setup_function15();
+ break;
+
+ case kAction157159392:
+ switch (savepoint.entity2) {
+ case kEntityAnna:
+ params->param1 = 1;
+ break;
+
+ case kEntityTatiana:
+ params->param2 = 1;
+ break;
+
+ case kEntityAbbot:
+ params->param3 = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Kronos, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && !getEntities()->isInSalon(kEntityBoutarel)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 75)
+ setup_function16();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3 != kTimeInvalid && getState()->time > kTime2002500) {
+ if (getState()->time <= kTime2052000) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = getState()->time + 900;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setup_function16();
+ } else {
+ getSavePoints()->push(kEntityKronos, kEntityAnna, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityTatiana, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityAbbot, kAction101169422);
+
+ setup_function18();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 60)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 59)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 83)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 81)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 87))
+ params->param1 = 1;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isPlayerPosition(kCarRestaurant, 51) && !getEntities()->isInSalon(kEntityBoutarel))
+ setup_function16();
+ else
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 60)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 59)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 83)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 81)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 87);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16 ,Kronos, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosVisit);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosVisit);
+ getSavePoints()->push(kEntityKronos, kEntityAnna, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityTatiana, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityAbbot, kAction101169422);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 60);
+
+ setup_function17();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Kronos, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9270);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function18();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Kronos, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2079000 && !params->param2) {
+ getObjects()->updateLocation2(kObjectCompartmentKronos, kObjectLocation3);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param1 = 1;
+ params->param2 = 1;
+ }
+
+ TIME_CHECK(kTime2106000, params->param3, setup_function19)
+ else {
+ if (params->param1 && getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->car = kCarKronos;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Kronos, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+
+ case kActionDrawScene:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventConcertStart);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityKronos);
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function39);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_chaptersHandler);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function50);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function56);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function35);
+
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Kronos, function20)
+ error("Kronos: callback function 20 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Kronos, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getProgress().field_40 = 0;
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ getSavePoints()->push(kEntityKronos, kEntityRebecca, kAction191668032);
+ if (!getEvent(kEventConcertLeaveWithBriefcase))
+ setup_function22();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ }
+ break;
+
+ case kAction235599361:
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Kronos, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_44) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchBaggageCarEntrance);
+ } else {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getSound()->isBuffered(savepoint.action == kActionKnock ? "LIB012" : "LIB013", true))
+ getSound()->playSound(kEntityPlayer, savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ getSavePoints()->call(kEntityKronos, kEntityKahina, kAction137503360);
+
+ if (getInventory()->hasItem(kItemBriefcase)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosReturnBriefcase);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird) && getEvent(kEventConcertLeaveWithBriefcase)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringEggCeiling);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringEggCeiling);
+ break;
+ }
+
+ if (getEvent(kEventConcertLeaveWithBriefcase)) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringNothing);
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKronos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKronosReturnBriefcase);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->removeItem(kItemScarf);
+
+ setup_function23();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosBringEggCeiling);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+
+ setup_function23();
+ break;
+
+ case 3:
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getAction()->playAnimation(kEventKronosBringEgg);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->addItem(kItemBriefcase);
+ setup_function23();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventKronosBringNothing);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ break;
+ }
+ break;
+
+ case kAction138085344:
+ setup_function23();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Kronos, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Kronos, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Kronos, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/kronos.h b/engines/lastexpress/entities/kronos.h
new file mode 100644
index 0000000000..5d8efbd4c2
--- /dev/null
+++ b/engines/lastexpress/entities/kronos.h
@@ -0,0 +1,138 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_KRONOS_H
+#define LASTEXPRESS_KRONOS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Kronos : public Entity {
+public:
+ Kronos(LastExpressEngine *engine);
+ ~Kronos() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_KRONOS_H
diff --git a/engines/lastexpress/entities/mahmud.cpp b/engines/lastexpress/entities/mahmud.cpp
new file mode 100644
index 0000000000..0200f0b554
--- /dev/null
+++ b/engines/lastexpress/entities/mahmud.cpp
@@ -0,0 +1,839 @@
+/* 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/data/scene.h"
+
+#include "lastexpress/entities/mahmud.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Mahmud::Mahmud(LastExpressEngine *engine) : Entity(engine, kEntityMahmud) {
+ ADD_CALLBACK_FUNCTION(Mahmud, reset);
+ ADD_CALLBACK_FUNCTION(Mahmud, draw);
+ ADD_CALLBACK_FUNCTION(Mahmud, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Mahmud, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Mahmud, playSound);
+ ADD_CALLBACK_FUNCTION(Mahmud, playSoundMertens);
+ ADD_CALLBACK_FUNCTION(Mahmud, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Mahmud, savegame);
+ ADD_CALLBACK_FUNCTION(Mahmud, updateEntity);
+ ADD_CALLBACK_FUNCTION(Mahmud, function10);
+ ADD_CALLBACK_FUNCTION(Mahmud, function11);
+ ADD_CALLBACK_FUNCTION(Mahmud, function12);
+ ADD_CALLBACK_FUNCTION(Mahmud, function13);
+ ADD_CALLBACK_FUNCTION(Mahmud, chaptersHandler);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter1);
+ ADD_CALLBACK_FUNCTION(Mahmud, resetChapter);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter2);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter3);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter4);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Mahmud, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(2, Mahmud, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Mahmud, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param7, getState()->timeTicks, params->param5);
+
+ if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
+ getScenes()->loadSceneFromObject((ObjectIndex)params->param6, true);
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityMahmud, (ObjectIndex)params->param4);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityMahmud, (char *)&params->seq);
+ getEntities()->enterCompartment(kEntityMahmud, (ObjectIndex)params->param4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(5, Mahmud, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Mahmud, playSoundMertens)
+ Entity::playSound(savepoint, false, getSound()->getSoundFlag(kEntityMertens));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Mahmud, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Mahmud, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Mahmud, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getInventory()->hasItem(kItemPassengerList))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1025" : "CAT1025Q");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param6, getState()->time, 13500);
+
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterExitCompartment("614Ed", kObjectCompartment4);
+ break;
+
+ case kActionEndSound:
+ case kActionDrawScene:
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ EntityPosition position = getEntityData(kEntityPlayer)->entityPosition;
+ if (position < kPosition_1500 || position >= kPosition_5790 || (position > kPosition_4455 && params->param5 != 5)) {
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_enterExitCompartment("614Ed", kObjectCompartment4);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getSound()->isBuffered((savepoint.action == kActionKnock) ? "LIB012" : "LIB013", true))
+ getSound()->playSound(kEntityPlayer, (savepoint.action == kActionKnock) ? "LIB012" : "LIB013");
+
+ params->param5 = savepoint.param.intValue;
+
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ params->param3++;
+
+ switch(params->param3) {
+ default:
+ params->param4 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMahmud, "MAH1174");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMahmud, "MAH1173B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMahmud, params->param2 ? "MAH1170E" : "MAH1173A");
+ break;
+ }
+ }
+
+ if (params->param4) {
+ if (getState()->time >= kTimeCityGalanta) {
+ params->param3 = 0;
+ } else {
+ getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, (getProgress().chapter == kChapter1) ? kSceneGameOverPolice1 : kSceneGameOverPolice2, true);
+ }
+ break;
+ }
+
+ getAction()->handleOtherCompartment((ObjectIndex)savepoint.param.intValue, false, false);
+
+ switch (getScenes()->get(getState()->scene)->position) {
+ default:
+ break;
+
+ case 55:
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ break;
+
+ case 56:
+ getScenes()->loadSceneFromObject(kObjectCompartment6, true);
+ break;
+
+ case 57:
+ getScenes()->loadSceneFromObject(kObjectCompartment7, true);
+ break;
+
+ case 58:
+ getScenes()->loadSceneFromObject(kObjectCompartment8, true);
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMahmud, params->param2 ? "MAH1170A" : "MAH1173", SoundManager::kFlagInvalid, 45);
+ getProgress().field_C4 = 1;
+
+ setCallback(1);
+ setup_enterExitCompartment2("614Dd", kObjectCompartment4, 30, (ObjectIndex)params->param1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment5, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Md");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+ break;
+
+ case 2:
+ case 3:
+ getEntities()->exitCompartment(kEntityMahmud, kObjectCompartment4, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Mahmud, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor: {
+ getSound()->playSound(kEntityPlayer, (savepoint.action == kActionKnock ? "LIB012" : "LIB013"));
+
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ params->param1++;
+
+ getSound()->playSound(kEntityMahmud, (params->param1 == 1 ? "MAH1170E" : (params->param1 == 2 ? "MAH1173B" : "MAH1174")));
+ }
+
+ switch (getScenes()->get(getState()->scene)->position) {
+ default:
+ break;
+
+ case 55:
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ break;
+
+ case 56:
+ getScenes()->loadSceneFromObject(kObjectCompartment6, true);
+ break;
+
+ case 57:
+ getScenes()->loadSceneFromObject(kObjectCompartment7, true);
+ break;
+
+ case 58:
+ getScenes()->loadSceneFromObject(kObjectCompartment8, true);
+ break;
+ }
+ break;
+ }
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction102227384);
+ setCallback(1);
+ setup_enterExitCompartment("614Ad", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Kd");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+
+ setCallback(2);
+ setup_playSound("MAH1170A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSoundMertens("MAH1170B");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("MAH1170C");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSoundMertens("MAH1170D");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound("MAH1170E");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSoundMertens("MAH1170F");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("614Ld", kObjectCompartment4);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Bd");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+
+ setCallback(9);
+ setup_playSound("MAH1170G");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_playSoundMertens("MAH1170H");
+ break;
+
+ case 10:
+ getObjects()->update(kObjectCompartment5, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 11:
+ getEntities()->exitCompartment(kEntityMahmud, kObjectCompartment4, true);
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityMahmud);
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction123852928:
+ if (getSound()->isBuffered(kEntityMahmud))
+ getSound()->processEntry(kEntityMahmud);
+
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(11);
+ setup_enterExitCompartment("614Cd", kObjectCompartment4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// TODO: factorize code between function12 & function13
+IMPLEMENT_FUNCTION(12, Mahmud, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("614Gd", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("614Ff", kObjectCompartment6);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ setCallback(4);
+ setup_playSound("Har1105");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("614Gf", kObjectCompartment6);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("614Fd", kObjectCompartment4);
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Mahmud, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("614Gd", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("614Fh", kObjectCompartment8);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ setCallback(4);
+ setup_playSound("Har1107");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("614Gh", kObjectCompartment8);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("614Fd", kObjectCompartment4);
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1)) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction204379649);
+ ENTITY_PARAM(0, 1) = 0;
+ }
+
+ if (!params->param2 && getProgress().chapter == kChapter1) {
+
+ TIME_CHECK_CALLBACK(kTime1098000, params->param6, 1, setup_function13);
+
+ if (!getSound()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) {
+ params->param7 = 1;
+
+ setCallback(2);
+ setup_function12();
+ break;
+ }
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param4 = 1;
+ params->param5 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorNormal, kCursorNormal);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param5) {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (getProgress().jacket == kJacketBlood || getEvent(kEventMahmudWrongDoor) || getEvent(kEventMahmudWrongDoorOriginalJacket) || getEvent(kEventMahmudWrongDoorDay)) {
+ // Check if we have the passenger list
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(6);
+ setup_playSound(rnd(2) == 0 ? "CAT1501" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(7);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 8 : 9);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ } else {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityMahmud);
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param4 || params->param5) {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ return;
+
+ case 1:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+
+ if (!getSound()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) {
+ params->param7 = 1;
+ setCallback(2);
+ setup_function12();
+ break;
+ }
+
+ params->param8 = 0;
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ params->param8 = 0;
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("MAH1175");
+ break;
+
+ case 5: {
+ CursorStyle cursor = kCursorHand;
+ CursorStyle cursor2 = kCursorHandKnock;
+
+ if (getProgress().jacket == kJacketBlood
+ || getEvent(kEventMahmudWrongDoor)
+ || getEvent(kEventMahmudWrongDoorOriginalJacket)
+ || getEvent(kEventMahmudWrongDoorDay)) {
+ cursor = kCursorNormal;
+ cursor2 = kCursorTalk;
+ }
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation1, cursor, cursor2);
+ params->param5 = 1;
+ break;
+ }
+
+ case 6:
+ case 7:
+ params->param4 = 1;
+ break;
+
+ case 8:
+ case 9:
+ setCallback(10);
+ setup_savegame(kSavegameTypeEvent, kEventMahmudWrongDoor);
+ return;
+
+ case 10:
+ getAction()->playAnimation((getProgress().jacket == kJacketGreen) ? (isNight() ? kEventMahmudWrongDoor : kEventMahmudWrongDoorDay) : kEventMahmudWrongDoorOriginalJacket);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->processScene();
+
+ params->param4 = 1;
+ break;
+
+ case 11:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ break;
+
+ case 12:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param2 = 0;
+ params->param4 = 0;
+ params->param5 = 0;
+ break;
+ }
+ break;
+
+ case kAction225563840:
+ setCallback(12);
+ setup_function11();
+ break;
+
+ case kAction290410610:
+ params->param3 = (params->param3 < 1) ? 1 : 0;
+ setCallback(11);
+ setup_function10((ObjectIndex)savepoint.param.intValue, (bool)params->param3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Mahmud, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chaptersHandler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMahmud, kAction170483072, 0);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject20, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Mahmud, resetChapter)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityMahmud);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Mahmud, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Mahmud, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Mahmud, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Mahmud, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityMahmud);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mahmud.h b/engines/lastexpress/entities/mahmud.h
new file mode 100644
index 0000000000..0b65a8bcff
--- /dev/null
+++ b/engines/lastexpress/entities/mahmud.h
@@ -0,0 +1,153 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MAHMUD_H
+#define LASTEXPRESS_MAHMUD_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Mahmud : public Entity {
+public:
+ Mahmud(LastExpressEngine *engine);
+ ~Mahmud() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param ticks The time ticks
+ * @param object The object for loading the scene
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment2, const char* sequence, ObjectIndex compartment, uint32 ticks, ObjectIndex object)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSoundMertens, const char* filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_2(function10, ObjectIndex, bool)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Handle chapters events
+ */
+ DECLARE_FUNCTION(chaptersHandler)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Reset chapter data
+ */
+ DECLARE_FUNCTION(resetChapter)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MAHMUD_H
diff --git a/engines/lastexpress/entities/max.cpp b/engines/lastexpress/entities/max.cpp
new file mode 100644
index 0000000000..5bc1c1f357
--- /dev/null
+++ b/engines/lastexpress/entities/max.cpp
@@ -0,0 +1,628 @@
+/* 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/entities/max.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Max::Max(LastExpressEngine *engine) : Entity(engine, kEntityMax) {
+ ADD_CALLBACK_FUNCTION(Max, reset);
+ ADD_CALLBACK_FUNCTION(Max, playSound);
+ ADD_CALLBACK_FUNCTION(Max, draw);
+ ADD_CALLBACK_FUNCTION(Max, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Max, savegame);
+ ADD_CALLBACK_FUNCTION(Max, chapter12_handler);
+ ADD_CALLBACK_FUNCTION(Max, function7);
+ ADD_CALLBACK_FUNCTION(Max, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Max, function9);
+ ADD_CALLBACK_FUNCTION(Max, chapter1);
+ ADD_CALLBACK_FUNCTION(Max, chapter2);
+ ADD_CALLBACK_FUNCTION(Max, chapter3);
+ ADD_CALLBACK_FUNCTION(Max, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Max, freeFromCage);
+ ADD_CALLBACK_FUNCTION(Max, function15);
+ ADD_CALLBACK_FUNCTION(Max, chapter4);
+ ADD_CALLBACK_FUNCTION(Max, function17);
+ ADD_CALLBACK_FUNCTION(Max, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Max, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Max, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(3, Max, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Max, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Max, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Max, chapter12_handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ break;
+
+ case kAction71277948:
+ setCallback(1);
+ setup_function7();
+ break;
+
+ case kAction158007856:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Max, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param2 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ setCallback((savepoint.action == kActionKnock) ? 1 : 2);
+ setup_playSound((savepoint.action == kActionKnock) ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 56) || getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getSound()->playSound(kEntityMax, "Max1120");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ case 0:
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("Max1122");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction101687594:
+ getEntities()->clearSequences(kEntityMax);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction122358304:
+ case kActionMaxFreeFromCage:
+ getSavePoints()->push(kEntityMax, kEntityMax, kActionMaxFreeFromCage);
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction158007856:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Max, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param2);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3101");
+
+ params->param2 = 255 * (4 * rnd(20) + 40);
+ params->param3 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (params->param1) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxLickHand);
+ break;
+ }
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getAction()->playAnimation(kEventCathMaxLickHand);
+ getScenes()->processScene();
+
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ params->param2 = 255 * (4 * rnd(20) + 40);
+
+ getObjects()->update(kObjectCageMax, kEntityMax, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3101");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getSound()->playSound(kEntityPlayer, "LIB026");
+ getAction()->playAnimation(kEventCathMaxFree);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 92);
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Max, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid || !getState()->time)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->hasValidFrame(kEntityMax) || !params->param2) {
+
+ params->param2 = getState()->time;
+ if (!params->param2)
+ goto setup_functions;
+ }
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+setup_functions:
+ if (getProgress().chapter == kChapter3)
+ setup_function15();
+
+ if (getProgress().chapter == kChapter4)
+ setup_function17();
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw Max outside of cage
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getEntities()->enterCompartment(kEntityMax, kObjectCompartmentF, true);
+
+ params->param1 = getState()->time + 2700;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Max, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter12_handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Max, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter12_handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Max, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Max, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ break;
+ }
+
+ UPDATE_PARAM(params->param3, getState()->time, params->param1);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction71277948:
+ setCallback(1);
+ setup_function7();
+ break;
+
+ case kAction122358304:
+ params->param2 = 1;
+ break;
+
+ case kActionMaxFreeFromCage:
+ setup_freeFromCage();
+ break;
+
+ case kAction158007856:
+ if (params->param2)
+ break;
+
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Max, freeFromCage)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityMax, "Max1122");
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Save game after freeing Max from his cage
+ case kActionOpenDoor:
+ if (getEvent(kEventCathMaxCage)) {
+ if (getEvent(kEventCathMaxFree)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxFree);
+ }
+
+ } else {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxCage);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCageMax, kEntityMax, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Play animation for Max in the cage and after opening it
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->removeFromQueue(kEntityMax);
+
+ getAction()->playAnimation(kEventCathMaxCage);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityMax);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getSound()->playSound(kEntityPlayer, "LIB026");
+ getAction()->playAnimation(kEventCathMaxFree);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 92);
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ setup_function9();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Max, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ getData()->car = getEntityData(kEntityCoudert)->car;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM(params->param3, getState()->time, 900);
+
+ getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3010");
+
+ setCallback(1);
+ setup_enterExitCompartment("630Bf", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getEntities()->enterCompartment(kEntityMax, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityMax, kEntityAnna, kAction156622016);
+ }
+ break;
+
+ case kAction122358304:
+ (savepoint.entity2 == kEntityAnna) ? (params->param1 = 1) : (params->param2 = 1);
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ getEntities()->drawSequenceLeft(kEntityMax, "BLANK");
+ break;
+
+ case kActionMaxFreeFromCage:
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ setup_chapter4Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Max, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Max, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ getData()->car = getEntityData(kEntityCoudert)->car;
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693);
+ break;
+
+ case kAction122358304:
+ params->param1 = 1;
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ getEntities()->drawSequenceLeft(kEntityMax, "BLANK");
+ break;
+
+ case kActionMaxFreeFromCage:
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ setup_chapter4Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Max, chapter5)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/max.h b/engines/lastexpress/entities/max.h
new file mode 100644
index 0000000000..404ee42f5f
--- /dev/null
+++ b/engines/lastexpress/entities/max.h
@@ -0,0 +1,129 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MAX_H
+#define LASTEXPRESS_MAX_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Max : public Entity {
+public:
+ Max(LastExpressEngine *engine);
+ ~Max() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Handle Chapter 1 & 2 events
+ */
+ DECLARE_FUNCTION(chapter12_handler)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(freeFromCage)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MAX_H
diff --git a/engines/lastexpress/entities/mertens.cpp b/engines/lastexpress/entities/mertens.cpp
new file mode 100644
index 0000000000..a59a8eebe8
--- /dev/null
+++ b/engines/lastexpress/entities/mertens.cpp
@@ -0,0 +1,4113 @@
+/* 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/entities/mertens.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+#define SAVEGAME_BLOOD_JACKET() \
+ if (getProgress().jacket == kJacketBlood \
+ && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000) \
+ && !getEntities()->isInsideCompartments(kEntityPlayer) \
+ && !getEntities()->checkFields10(kEntityPlayer)) { \
+ setCallback(1); \
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \
+ }
+
+Mertens::Mertens(LastExpressEngine *engine) : Entity(engine, kEntityMertens) {
+ ADD_CALLBACK_FUNCTION(Mertens, reset);
+ ADD_CALLBACK_FUNCTION(Mertens, bloodJacket);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(Mertens, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Mertens, playSound);
+ ADD_CALLBACK_FUNCTION(Mertens, playSound16);
+ ADD_CALLBACK_FUNCTION(Mertens, savegame);
+ ADD_CALLBACK_FUNCTION(Mertens, updateEntity);
+ ADD_CALLBACK_FUNCTION(Mertens, function11);
+ ADD_CALLBACK_FUNCTION(Mertens, bonsoir);
+ ADD_CALLBACK_FUNCTION(Mertens, function13);
+ ADD_CALLBACK_FUNCTION(Mertens, function14);
+ ADD_CALLBACK_FUNCTION(Mertens, function15);
+ ADD_CALLBACK_FUNCTION(Mertens, function16);
+ ADD_CALLBACK_FUNCTION(Mertens, function17);
+ ADD_CALLBACK_FUNCTION(Mertens, function18);
+ ADD_CALLBACK_FUNCTION(Mertens, function19);
+ ADD_CALLBACK_FUNCTION(Mertens, function20);
+ ADD_CALLBACK_FUNCTION(Mertens, function21);
+ ADD_CALLBACK_FUNCTION(Mertens, function22);
+ ADD_CALLBACK_FUNCTION(Mertens, function23);
+ ADD_CALLBACK_FUNCTION(Mertens, function24);
+ ADD_CALLBACK_FUNCTION(Mertens, function25);
+ ADD_CALLBACK_FUNCTION(Mertens, function26);
+ ADD_CALLBACK_FUNCTION(Mertens, tylerCompartment);
+ ADD_CALLBACK_FUNCTION(Mertens, function28);
+ ADD_CALLBACK_FUNCTION(Mertens, function29);
+ ADD_CALLBACK_FUNCTION(Mertens, function30);
+ ADD_CALLBACK_FUNCTION(Mertens, function31);
+ ADD_CALLBACK_FUNCTION(Mertens, function32);
+ ADD_CALLBACK_FUNCTION(Mertens, function33);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter1);
+ ADD_CALLBACK_FUNCTION(Mertens, function35);
+ ADD_CALLBACK_FUNCTION(Mertens, function36);
+ ADD_CALLBACK_FUNCTION(Mertens, function37);
+ ADD_CALLBACK_FUNCTION(Mertens, function38);
+ ADD_CALLBACK_FUNCTION(Mertens, function39);
+ ADD_CALLBACK_FUNCTION(Mertens, function40);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Mertens, function42);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter2);
+ ADD_CALLBACK_FUNCTION(Mertens, function44);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter3);
+ ADD_CALLBACK_FUNCTION(Mertens, function46);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter4);
+ ADD_CALLBACK_FUNCTION(Mertens, function48);
+ ADD_CALLBACK_FUNCTION(Mertens, function49);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter5);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Mertens, function52);
+ ADD_CALLBACK_FUNCTION(Mertens, function53);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Mertens, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Mertens, bloodJacket)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityMertens, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Mertens, enterExitCompartment, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Mertens, enterExitCompartment2, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kAction4:
+ getEntities()->exitCompartment(kEntityMertens, (ObjectIndex)params->param4);
+ CALLBACK_ACTION();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(5, Mertens, enterExitCompartment3, ObjectIndex, EntityPosition, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4);
+ getData()->entityPosition = (EntityPosition)params->param5;
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq);
+ getEntities()->enterCompartment(_entityIndex, (ObjectIndex)params->param4);
+ getData()->entityPosition = (EntityPosition)params->param5;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, (EntityPosition)params->param5) || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, (EntityPosition)params->param6)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject((ObjectIndex)params->param4);
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Mertens, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Mertens, playSound)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(8, Mertens, playSound16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1, SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Mertens, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition)
+
+#define LOADSCENE_FROM_POSITION() \
+ if (getData()->direction != kDirectionUp) { \
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); \
+ } else { \
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); \
+ }
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 2000))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemInvalid);
+ else
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh);
+
+ if (!getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000)
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields10(kEntityPlayer)) {
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if ((ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) && (!getEvent(kEventKronosConversation) && getProgress().jacket == kJacketGreen)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2) && getProgress().jacket == kJacketGreen && !getProgress().eventMetAugust) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaiting);
+ break;
+ }
+
+ if (ENTITY_PARAM(2, 4) && getState()->time < kTime2133000) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosConcertInvitation);
+ break;
+ }
+
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ params->param3 = 0;
+ if (getProgress().eventCorpseFound || getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD)) {
+ if (ENTITY_PARAM(0, 4) && getProgress().jacket == kJacketGreen && !getEvent(kEventMertensDontMakeBed) && !getProgress().eventCorpseThrown) {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventMertensDontMakeBed);
+ }
+ } else {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartment);
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->playSound(kEntityMertens, "CON1110B");
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityMertens);
+ break;
+
+ case kActionDefault:
+ if ((!getProgress().eventCorpseFound && !getEvent(kEventMertensAskTylerCompartment) && !getEvent(kEventMertensAskTylerCompartment))
+ || (ENTITY_PARAM(0, 4) && getProgress().jacket == kJacketGreen && !getEvent(kEventMertensDontMakeBed) && !getProgress().eventCorpseThrown))
+ params->param3 = 1;
+
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensKronosInvitation : kEventMertensKronosInvitationClosedWindows);
+ getProgress().eventMertensKronosInvitation = true;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (params->param1 != 3 || (params->param2 != kPosition_8200 && params->param2 != kPosition_9510)) {
+ LOADSCENE_FROM_POSITION();
+ break;
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityMertens, kPosition_2000, 500))
+ getData()->entityPosition = kPosition_2500;
+
+ getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventMertensAugustWaiting);
+ getProgress().eventMertensAugustWaiting = true;
+
+ ENTITY_PARAM(1, 2) = 0;
+
+ if (params->param1 == 3 && params->param2 == kPosition_8200) {
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityMertens, kPosition_2000, 500))
+ getData()->entityPosition = kPosition_2500;
+
+ getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensKronosConcertInvitation);
+ ENTITY_PARAM(2, 4) = 0;
+
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 5:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensAskTylerCompartmentD : kEventMertensAskTylerCompartment);
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventMertensDontMakeBed);
+ LOADSCENE_FROM_POSITION();
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+ break;
+ }
+
+#undef LOADSCENE_FROM_POSITION
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Mertens, function11, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex)
+ EntityIndex entity = (EntityIndex)params->param1;
+
+ if (savepoint.action == kActionDefault)
+ return;
+
+ if (getSound()->isBuffered(kEntityMertens)) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ if (isNight()) {
+ if (Entities::isFemale(entity)) {
+ getSound()->playSound(kEntityMertens, rnd(2) ? "CON1112" : "CON1112A");
+ } else {
+ if (entity || getProgress().field_18 != 2) {
+ getSound()->playSound(kEntityMertens, "CON1112F");
+ } else {
+ switch (rnd(3)) {
+ default:
+ case 0:
+ getSound()->playSound(kEntityMertens, "CON1061");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "CON1110G");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, "CON1110H");
+ break;
+ }
+ }
+ }
+ } else {
+ if (Entities::isFemale(entity))
+ getSound()->playSound(kEntityMertens, rnd(2) ? "CON1112B" : "CON1112C");
+ else
+ getSound()->playSound(kEntityMertens, "CON1112G");
+ }
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ if (!params->param2 && !params->param3) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 75)
+ getData()->inventoryItem = kItemNone;
+ setCallback(5);
+ setup_function18();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 225)
+ getData()->inventoryItem = kItemNone;
+ setCallback(6);
+ setup_function18();
+ break;
+ UPDATE_PARAM_PROC_END
+
+ getData()->inventoryItem = (getProgress().chapter == kChapter1
+ && !ENTITY_PARAM(2, 1)
+ && !getProgress().eventCorpseFound
+ && !getEvent(kEventMertensAskTylerCompartment)
+ && !getEvent(kEventMertensAskTylerCompartmentD)) ? kItemMatchBox : kItemNone;
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartmentD);
+ break;
+
+ case kAction11:
+ params->param3++;
+ setCallback(11);
+ setup_bonsoir(savepoint.entity2);
+ break;
+
+ case kActionDefault:
+ if (params->param2)
+ params->param3 = 1;
+
+ if (!getSound()->isBuffered(kEntityMertens)) {
+
+ }
+
+ setCallback(3);
+ setup_function20();
+ break;
+
+ case kAction16:
+ params->param3--;
+
+ if (params->param2 && !params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function18();
+ }
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && ENTITY_PARAM(0, 7) && !getEvent(kEventKronosConversation)) {
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_function20();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityMertens, params->param1 ? "601I" : "601H");
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ case 6:
+ case 9:
+ case 10:
+ CALLBACK_ACTION();
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventMertensAskTylerCompartmentD);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventMertensKronosInvitation);
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ getScenes()->processScene();
+
+ if (!params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function18();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_1500);
+ } else {
+ setCallback(1);
+ setup_function11(15);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction202558662);
+
+ setCallback(2);
+ setup_function20();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction202558662);
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction125499160:
+ if (params->param1 == kEntityVerges)
+ ENTITY_PARAM(0, 8) = 0;
+
+ setCallback(5);
+ setup_function18();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(15, Mertens, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, params->param1 ? "CON1059A" : "CON1059");
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Xb", kObjectCompartment2);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction135664192);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Mertens, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ switch (rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityMertens, "AUG2095A");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "AUG2096A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, "AUG2094B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, "AUG2094C");
+ break;
+ }
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, params->param1 ? "AUG2097" : "AUG2098");
+
+ setCallback(4);
+ setup_enterExitCompartment("601Xc", kObjectCompartment3);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction69239528);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Mertens, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // FIXME: Check that we are using the correct parameter struct
+ if (ENTITY_PARAM(0, 6) || ((EntityData::EntityParametersIIII*)_data->getParameters(8, 1))->hasNonNullParameter()) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ // Mertens sits on his chair at the back of the train
+ if (!getInventory()->hasItem(kItemPassengerList) || ENTITY_PARAM(0, 2)) {
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ } else {
+ // Got the passenger list, Mertens is looking for it before sitting
+ ENTITY_PARAM(0, 2) = 1;
+ getSound()->playSound(kEntityMertens, "CON1058", SoundManager::kFlagInvalid, 75);
+ getEntities()->drawSequenceRight(kEntityMertens, "601D");
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 68)) {
+ getSound()->playSound(kEntityPlayer, "CON1110");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+ }
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityMertens);
+ ENTITY_PARAM(2, 1) = 1;
+ setCallback(2);
+ setup_function11(75);
+ break;
+
+ case 2:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ if (!ENTITY_PARAM(0, 3)
+ && !getInventory()->hasItem(kItemPassengerList)
+ && ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityMertens, kEntityVerges, kAction158617345);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ getSavePoints()->push(kEntityMertens, kEntityMertens, kActionDrawScene);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Mertens, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6)
+ || ENTITY_PARAM(1, 1)
+ || ENTITY_PARAM(1, 2)
+ || ENTITY_PARAM(1, 3)
+ || ENTITY_PARAM(1, 4)
+ || ENTITY_PARAM(1, 5)
+ || ENTITY_PARAM(1, 6)
+ || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(1, 8)) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!getInventory()->hasItem(kItemPassengerList) || ENTITY_PARAM(0, 2)) {
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ } else {
+ ENTITY_PARAM(0, 2) = 1;
+ getSound()->playSound(kEntityMertens, "CON1058", SoundManager::kFlagInvalid, 75);
+ getEntities()->drawSequenceRight(kEntityMertens, "601D");
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!ENTITY_PARAM(0, 3)
+ && !getInventory()->hasItem(kItemPassengerList)
+ && ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityMertens, kEntityVerges, kAction158617345);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Mertens, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 0;
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_bloodJacket("601C");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Mertens, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_bloodJacket("601C");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(CURRENT_PARAMS(1, 4), getState()->time, 300)
+ getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityMertens));
+ UPDATE_PARAM_PROC_END
+
+ UPDATE_PARAM(CURRENT_PARAMS(1, 5), getState()->time, 900);
+
+ // Update objects
+ getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1);
+ if (params->param5 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, (EntityIndex)params->param4, (ObjectLocation)params->param5, (CursorStyle)params->param6, (CursorStyle)params->param7);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param8, (ObjectLocation)CURRENT_PARAMS(1, 1), (CursorStyle)CURRENT_PARAMS(1, 2), (CursorStyle)CURRENT_PARAMS(1, 3));
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param3 = 1;
+ params->param4 = getObjects()->get((ObjectIndex)params->param1).entity;
+ params->param5 = getObjects()->get((ObjectIndex)params->param1).location;
+ params->param6 = getObjects()->get((ObjectIndex)params->param1).cursor;
+ params->param7 = getObjects()->get((ObjectIndex)params->param1).cursor2;
+
+ if (params->param2) {
+ params->param8 = getObjects()->get((ObjectIndex)params->param2).entity;
+ CURRENT_PARAMS(1, 1) = getObjects()->get((ObjectIndex)params->param2).location;
+ CURRENT_PARAMS(1, 2) = getObjects()->get((ObjectIndex)params->param2).cursor;
+ CURRENT_PARAMS(1, 3) = getObjects()->get((ObjectIndex)params->param2).cursor2;
+
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ if (params->param5 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(params->param3 ? 3 : 4);
+ setup_playSound(params->param3 ? "Con1017" : "Con1017A");
+ break;
+
+ case 3:
+ case 4:
+ params->param3 = 0;
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Mertens, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Mh", kObjectCompartment8);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nh");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment8, true);
+
+ setCallback(3);
+ setup_function11(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Mh", kObjectCompartment8);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nh");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment8);
+ getSavePoints()->push(kEntityMertens, kEntityMahmud, kAction225563840);
+ break;
+
+ case 5:
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, "MAH1170I");
+
+ setCallback(6);
+ setup_enterExitCompartment("601Zd", kObjectCompartment4);
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, "MAH1172", SoundManager::kFlagInvalid, 225);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment4, kObject20);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("671Ad", kObjectCompartment4);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityMahmud, kAction123852928);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction102227384:
+ getEntities()->drawSequenceLeft(kEntityMertens, "671Dh");
+ break;
+
+ case kAction156567128:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment8, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Mertens, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Vd", kObjectCompartment4);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wd");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment4, true);
+
+ setCallback(3);
+ setup_function11(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Zd", kObjectCompartment4);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment4);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(5);
+ setup_function21(kObjectCompartment4, kObject20);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("671Ad", kObjectCompartment4);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Mertens, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment3("601Rc", kObjectCompartment3, kPosition_6470, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Mc", kObjectCompartment3);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction221617184);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nc");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment3, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(4);
+ setup_function21(kObjectCompartment3, kObjectKitchen);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("601Sc", kObjectCompartment3);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3, true);
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment3, kObjectKitchen);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(8);
+ setup_enterExitCompartment("601Uc", kObjectCompartment3);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction124697504);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction192849856);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Qc");
+ break;
+
+ case kAction102675536:
+ params->param1 = 1;
+ break;
+
+ case kAction156567128:
+ setCallback(6);
+ setup_enterExitCompartment("601Tc", kObjectCompartment3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Mertens, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment3("601Zb", kObjectCompartment2, kPosition_7500, kPositionNone);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Vb", kObjectCompartment2);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction221617184);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wb");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment2, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (getProgress().chapter == kChapter1 && ENTITY_PARAM(0, 4))
+ if (getProgress().field_14 != 29)
+ getProgress().field_14 = 3;
+
+ setCallback(4);
+ setup_function21(kObjectCompartment2, kObjectHandleInsideBathroom);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("671Ab", kObjectCompartment2);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2, true);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (getProgress().chapter == kChapter1 && ENTITY_PARAM(0, 4))
+ if (getProgress().field_14 != 29)
+ getProgress().field_14 = 3;
+
+ setCallback(7);
+ setup_function21(kObjectCompartment2, kObjectHandleInsideBathroom);
+ break;
+
+ case 7:
+ getSound()->playSound(kEntityMertens, "CON1024A");
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(8);
+ setup_enterExitCompartment("641Ub", kObjectCompartment2);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction124697504);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ params->param1 = 1;
+ break;
+
+ case kAction156567128:
+ setCallback(6);
+ setup_enterExitCompartment("641Tb", kObjectCompartment2);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().eventCorpseThrown
+ || !params->param1
+ || getProgress().chapter != kChapter1
+ || getProgress().jacket != kJacketGreen) {
+
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_playSound16("ZNU1001");
+ } else {
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound16("CON1062");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ } else if (getProgress().eventCorpseMovedFromFloor) {
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment1);
+ getEntities()->drawSequenceRight(kEntityMertens, "601Ra");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 16);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ } else {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment1);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment1, kObjectHandleBathroom);
+ break;
+
+ case 7:
+ if (getProgress().eventCorpseThrown || getProgress().chapter != kChapter1) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000)) {
+ if (!getEntities()->checkFields10(kEntityPlayer))
+ getSound()->playSound(kEntityMertens, "CON1061");
+ }
+
+ setCallback(9);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseBed);
+ }
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(27, Mertens, tylerCompartment, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 150)
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(10);
+ setup_playSound16("CON1018A");
+ break;
+ UPDATE_PARAM_PROC_END
+
+label_callback10:
+ if (!params->param3)
+ params->param3 = getState()->timeTicks + 300;
+
+ if (params->param3 >= getState()->timeTicks) {
+label_callback11:
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 375);
+
+ getSound()->playSound(kEntityPlayer, "LIB033");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(18);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(20);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(21);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(26);
+ setup_function26(false);
+ }
+
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(17);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ } else {
+ params->param3 = kTimeInvalid;
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(11);
+ setup_playSound16("CON1018B");
+ break;
+ }
+
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(13);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(16);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(14);
+ setup_function26(false);
+ }
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(12);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ switch (params->param1) {
+ default:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 1:
+ setCallback(23);
+ setup_playSound16("CON1018D");
+ break;
+
+ case 2:
+ setCallback(24);
+ setup_playSound16("CON1018E");
+ break;
+
+ case 3:
+ setCallback(25);
+ setup_playSound16("CON1025");
+ break;
+ }
+
+ } else {
+ setCallback(22);
+ setup_function26(true);
+ }
+ break;
+
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB012" : "LIB014");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(27);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(29);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(30);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(28);
+ setup_function26(false);
+ }
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(26);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(params->param1 ? 9 : 8);
+ setup_playSound16(params->param1 ? "CON1018" : "CON1060");
+ } else {
+ getSound()->playSound(kEntityMertens, "CON1019");
+
+ setCallback(1);
+ setup_enterExitCompartment("601Ma", kObjectCompartment1);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(4);
+ setup_enterExitCompartment("601Ra", kObjectCompartment1);
+ } else {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_enterExitCompartment("601Ra", kObjectCompartment1);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ break;
+
+ case 3:
+ case 12:
+ case 17:
+ case 26:
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (params->param1) {
+ setCallback(7);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ break;
+ }
+
+ if (getProgress().eventCorpseThrown || getProgress().chapter != kChapter1) {
+ setCallback(6);
+ setup_function21(kObjectCompartment1, kObjectHandleBathroom);
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseBed);
+ }
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 8:
+ case 9:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ break;
+
+ case 10:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ goto label_callback10;
+
+ case 11:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ goto label_callback11;
+
+ case 13:
+ case 18:
+ case 27:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 14:
+ case 19:
+ case 22:
+ case 28:
+ CALLBACK_ACTION();
+ break;
+
+ case 15:
+ case 20:
+ case 29:
+ getAction()->playAnimation(kEventMertensAugustWaitingCompartment);
+ getProgress().eventMertensAugustWaiting = true;
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 16:
+ case 21:
+ case 30:
+ getAction()->playAnimation(kEventMertensKronosInvitationCompartment);
+ getProgress().eventMertensKronosInvitation = true;
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 23:
+ getProgress().eventMertensAugustWaiting = true;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 24:
+ getProgress().eventMertensKronosInvitation = true;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 25:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(28, Mertens, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && params->param5) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param4 = 1;
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param5 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SS(29, Mertens, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param7 > 1 && params->param8) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param7++;
+ if (params->param7 == 1)
+ getSound()->playSound(kEntityMertens, (char *)&params->seq2);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param8 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (params->param1) {
+ default:
+ CALLBACK_ACTION();
+ return;
+
+ case 1:
+ params->param2 = kPosition_8200;
+
+ if (getProgress().field_14) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ getProgress().field_14 = 3;
+ break;
+
+ case 2:
+ params->param2 = kPosition_7500;
+ break;
+
+ case 3:
+ params->param2 = kPosition_6470;
+ break;
+ }
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, (EntityPosition)params->param2);
+ break;
+
+ case 2:
+ switch (params->param1) {
+ default:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 1:
+ if (getProgress().chapter == kChapter4)
+ getSavePoints()->push(kEntityMertens, kEntityTatiana, kAction238790488);
+
+ setCallback(3);
+ setup_tylerCompartment(kMertensAction3);
+ break;
+
+ case 2:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, getObjects()->get(kObjectCompartment2).location, kCursorNormal, kCursorNormal);
+ params->param3 = 1;
+ }
+
+ setCallback(4);
+ setup_enterExitCompartment("601Vb", kObjectCompartment2);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_6470)) {
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, getObjects()->get(kObjectCompartment3).location, kCursorNormal, kCursorNormal);
+ params->param3 = 1;
+ }
+
+ setCallback(6);
+ setup_enterExitCompartment("601Mc", kObjectCompartment3);
+ break;
+ }
+ break;
+
+ case 3:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wb");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment2, true);
+
+ setCallback(5);
+ setup_playSound("CON3020");
+ break;
+
+ case 5:
+ if (params->param3)
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, getObjects()->get(kObjectCompartment2).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2);
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nc");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment3, true);
+
+ setCallback(7);
+ setup_playSound("CON3020");
+ break;
+
+ case 7:
+ if (params->param3)
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, getObjects()->get(kObjectCompartment3).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3);
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function17();
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(31, Mertens, function31, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setCallback(3);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_bloodJacket("601G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityMertens)) {
+ getEntities()->drawSequenceLeft(kEntityMertens, "601J");
+ } else {
+ setCallback(2);
+ setup_function17();
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Mertens, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9510);
+ break;
+
+ case 2:
+ if (getData()->entityPosition >= kPosition_9460) {
+ getEntities()->clearSequences(kEntityMertens);
+ setCallback(3);
+ setup_function11(900);
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function17();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Mertens, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 8) || ENTITY_PARAM(0, 6)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 4) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(1, 8) = 1;
+
+ setCallback(ENTITY_PARAM(0, 8) ? 1 : 3);
+ setup_updateEntity(kCarGreenSleeping, ENTITY_PARAM(0, 8) ? kPosition_1500 : kPosition_540);
+ } else {
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(2, 1) = 1;
+
+ setCallback(2);
+ setup_function14(kEntityVerges);
+ break;
+
+ case 2:
+ ENTITY_PARAM(1, 8) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(4);
+ setup_function11(75);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(5);
+ setup_function16(true);
+ break;
+ }
+ // Fallback to next case
+
+ case 5:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(6);
+ setup_function16(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 6:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(7);
+ setup_function15(true);
+ break;
+ }
+ // Fallback to next case
+
+ case 7:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(8);
+ setup_function15(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 8:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(9);
+ setup_function35();
+ break;
+ }
+ // Fallback to next case
+
+ case 9:
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(10);
+ setup_function36();
+ break;
+ }
+ // Fallback to next case
+
+ case 10:
+ if (ENTITY_PARAM(1, 3)) {
+ setCallback(11);
+ setup_function40();
+ break;
+ }
+ // Fallback to next case
+
+ case 11:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(12);
+ setup_function28("CON1200");
+ break;
+ }
+
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(13);
+ setup_function37();
+ break;
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case 12:
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction168254872);
+ ENTITY_PARAM(1, 1) = 0;
+
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(13);
+ setup_function37();
+ break;
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case 13:
+ ENTITY_PARAM(2, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Mertens, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMertens, kAction171394341, 7);
+ getSavePoints()->addData(kEntityMertens, kAction169633856, 9);
+ getSavePoints()->addData(kEntityMertens, kAction238732837, 10);
+ getSavePoints()->addData(kEntityMertens, kAction269624833, 12);
+ getSavePoints()->addData(kEntityMertens, kAction302614416, 11);
+ getSavePoints()->addData(kEntityMertens, kAction190082817, 8);
+ getSavePoints()->addData(kEntityMertens, kAction269436673, 13);
+ getSavePoints()->addData(kEntityMertens, kAction303343617, 14);
+ getSavePoints()->addData(kEntityMertens, kAction224122407, 17);
+ getSavePoints()->addData(kEntityMertens, kAction201431954, 18);
+ getSavePoints()->addData(kEntityMertens, kAction188635520, 19);
+ getSavePoints()->addData(kEntityMertens, kAction204379649, 4);
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Mertens, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ break;
+ } else {
+ getProgress().field_14 = 3;
+
+ setCallback(1);
+ setup_function19();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ if (!ENTITY_PARAM(1, 2) || getProgress().eventMetAugust) {
+ ENTITY_PARAM(1, 2) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ } else {
+ setCallback(5);
+ setup_tylerCompartment(kMertensAction1);
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ if (getProgress().eventMertensAugustWaiting)
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(7);
+ setup_function17();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Mertens, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ } else {
+ getProgress().field_14 = 3;
+
+ setCallback(1);
+ setup_function19();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ if (ENTITY_PARAM(0, 6)) {
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ } else {
+ setCallback(7);
+ setup_tylerCompartment(kMertensAction2);
+ }
+ } else {
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(7);
+ setup_tylerCompartment(kMertensAction2);
+ } else {
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 7:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ if (!getProgress().eventMertensKronosInvitation)
+ ENTITY_PARAM(0, 7) = 1;
+
+ ENTITY_PARAM(0, 6) = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function17();
+ break;
+
+ case 6:
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Mertens, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 >= 2 && params->param2) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ ++params->param6;
+
+ if (params->param6 == 1)
+ getSound()->playSound(kEntityMertens, getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 2000) ? "CON1152" : "CON1151");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param2 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, "CON1150");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Mertens, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!ENTITY_PARAM(0, 4)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!ENTITY_PARAM(0, 4)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(2);
+ setup_tylerCompartment(kMertensActionNone);
+ break;
+
+ case 2:
+ ENTITY_PARAM(0, 4) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Mertens, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 4) = 1;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function22();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function24();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function25();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function38();
+ break;
+
+ case 8:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function17();
+ break;
+
+ case 10:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Mertens, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 3) = 0;
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_1500);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function17();
+ break;
+
+ case 5:
+ ENTITY_PARAM(0, 6) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Mertens, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17();
+ break;
+
+ case 2:
+ setup_function42();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Mertens, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(0, 1) = 1;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 1) = 0; // BUG: is set twice. Maybe a bug?
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+
+ params->param1 = 1;
+ params->param2 = 1;
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+ }
+
+ if (ENTITY_PARAM(2, 1) || getProgress().eventCorpseFound || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAskTylerCompartment))
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (!params->param2) {
+ TIME_CHECK_SAVEPOINT(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072);
+
+ if (params->param4 != kTimeInvalid && getState()->time > kTimeCityChalons) {
+
+ if (getState()->time <= kTime1188000) {
+ if ((!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getSound()->isBuffered("REB1205")
+ || !getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)
+ || !params->param4) {
+ params->param4 = getState()->time;
+ }
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ ENTITY_PARAM(0, 4) = kTimeInvalid;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(8);
+ setup_function29("CON1210", "CON1210A");
+ break;
+ }
+ }
+
+label_callback_8:
+ if (getState()->time > kTime1215000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM_PROC(params->param5, getState()->time, 2700)
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+ ENTITY_PARAM(0, 1) = 1;
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(9);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+ if (getProgress().field_14 == 29)
+ goto label_callback_13;
+
+label_callback_9:
+ if (ENTITY_PARAM(1, 6)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_10:
+ if (ENTITY_PARAM(1, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(11);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_11:
+ if (ENTITY_PARAM(1, 5)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(12);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_12:
+ if (ENTITY_PARAM(1, 4)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(13);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_13:
+ if (ENTITY_PARAM(1, 2)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(14);
+ setup_function35();
+ break;
+ }
+
+label_callback_14:
+ if (ENTITY_PARAM(0, 6)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(15);
+ setup_function36();
+ break;
+ }
+
+label_callback_15:
+ if (ENTITY_PARAM(1, 3)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(16);
+ setup_function40();
+ break;
+ }
+
+label_callback_16:
+ if (ENTITY_PARAM(1, 1)) {
+ ENTITY_PARAM(1, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+ setCallback(17);
+ setup_function28("CON1200");
+ break;
+ }
+
+label_callback_17:
+ if (ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(2, 2) = 0;
+ getData()->inventoryItem = kItemNone;
+ setCallback(18);
+ setup_function37();
+ break;
+ }
+
+label_callback_18:
+ if (!params->param1 && ENTITY_PARAM(0, 5)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(19);
+ setup_function39();
+ break;
+ }
+
+label_callback_19:
+ if (ENTITY_PARAM(0, 1) && !getSound()->isBuffered(kEntityMertens)) {
+ if (getProgress().field_18 != 4)
+ getSound()->playSound(kEntityMertens, "CON1505");
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(21);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartmentD);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(20);
+ setup_function13((bool)savepoint.param.intValue, (bool)savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && ENTITY_PARAM(0, 7) && !getEvent(kEventKronosConversation)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && !getProgress().eventMertensKronosInvitation && !getEvent(kEventMertensLastCar) && !getEvent(kEventMertensLastCarOriginalJacket)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensLastCar);
+ break;
+ }
+
+label_callback_2_4:
+ if ((getEntities()->isPlayerPosition(kCarGreenSleeping, 1) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(getEntities()->isPlayerPosition(kCarGreenSleeping, 1) ? 5 : 6);
+ setup_function13(getEntities()->isPlayerPosition(kCarGreenSleeping, 1), false);
+ break;
+ }
+
+label_callback_5_6:
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (getProgress().jacket == kJacketOriginal || ENTITY_PARAM(0, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_function32();
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventMertensKronosInvitation);
+ getProgress().eventMertensKronosInvitation = true;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ goto label_callback_2_4;
+
+ case 3:
+ getAction()->playAnimation(getProgress().jacket == kJacketOriginal ? kEventMertensLastCarOriginalJacket : kEventMertensLastCar);
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 6);
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ case 6:
+ goto label_callback_5_6;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+
+ case 15:
+ goto label_callback_15;
+
+ case 16:
+ goto label_callback_16;
+
+ case 17:
+ goto label_callback_17;
+
+ case 18:
+ goto label_callback_18;
+
+ case 19:
+ params->param1 = 1;
+ goto label_callback_19;
+
+ case 21:
+ getAction()->playAnimation(kEventMertensAskTylerCompartmentD);
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getInventory()->get(kItem7)->location = kObjectLocationNone;
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+
+ setCallback(22);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 22:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(23);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(24);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Mertens, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function44();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Mertens, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(5);
+ setup_function13((bool)savepoint.param.intValue, (bool)savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(6);
+ setup_function13(true, false);
+
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(7);
+ setup_function13(false, false);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(8);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(10);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Mertens, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 3) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function46();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Mertens, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(5);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(2, 4)
+ && (getEvent(kEventKronosVisit) || getState()->time > kTime2052000)
+ && getState()->time < kTime2133000
+ && getEntities()->isPlayerInCar(kCarGreenSleeping)) {
+ setCallback(6);
+ setup_function32();
+ break;
+ }
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 7, setup_function28, "CON3012");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2117700, params->param2, 8, setup_function32);
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2124000, params->param3, 9, setup_function28, "CON2010");
+
+label_callback_9:
+ TIME_CHECK_CALLBACK(kTime2146500, params->param4, 10, setup_function32);
+
+label_callback_10:
+ TIME_CHECK_CALLBACK(kTime2169000, params->param5, 11, setup_function32);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(12);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2 != kEntityPlayer);
+ }
+ break;
+
+ case kActionDefault:
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1)) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(13);
+ setup_function13(true, false);
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(14);
+ setup_function13(false, false);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(16);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(15);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(17);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Mertens, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function48();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Mertens, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ params->param1 = 1;
+
+ getObjects()->updateLocation2(kObjectCompartment2, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment3, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment4, kObjectLocation1);
+
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+
+ ENTITY_PARAM(2, 3) = 0;
+ }
+
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_4:
+ if (!params->param1) {
+ TIME_CHECK_CALLBACK(kTime2403000, params->param2, 5, setup_function49);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2430000, params->param3, 6, setup_function32);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK(kTime2439000, params->param4, 7, setup_function32);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2448000, params->param5, 8, setup_function32);
+ }
+
+label_callback_8:
+ if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM(params->param6, getState()->time, 2700);
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+
+ ENTITY_PARAM(0, 1) = 1;
+ params->param6 = 0;
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2 != kEntityPlayer);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(10);
+ setup_function13(true, false);
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(11);
+ setup_function13(false, false);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(13);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(12);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(14);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Mertens, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_tylerCompartment(kMertensActionNone);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function33();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function25();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function33();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function24();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function33();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function23();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function17();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Mertens, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Mertens, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function52();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Mertens, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param2)
+ params->param2 = getState()->time;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Mme5010");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ params->param1 = getState()->time + 4500;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("671Ad", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityMmeBoutarel, kAction155604840);
+ setup_function53();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Mertens, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(3);
+ setup_playSound(getSound()->justCheckingCath());
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 4 : 5);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param1) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601ZD", kObjectCompartment4);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMertens);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ // Fallback to next case
+
+ case 3:
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 4:
+ case 5:
+ params->param3++;
+
+ if (params->param3 == 1) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("Con5002");
+
+ } else if (params->param3 == 2) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_playSound("Con5002A");
+ }
+ break;
+
+ case 6:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 7:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(54, Mertens)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mertens.h b/engines/lastexpress/entities/mertens.h
new file mode 100644
index 0000000000..c6b800f1ff
--- /dev/null
+++ b/engines/lastexpress/entities/mertens.h
@@ -0,0 +1,220 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MERTENS_H
+#define LASTEXPRESS_MERTENS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Mertens : public Entity {
+private:
+ // The type of action when entering Tyler compartment
+ enum MertensActionType {
+ kMertensActionNone = 0,
+ kMertensAction1 = 1,
+ kMertensAction2 = 2,
+ kMertensAction3 = 3
+ };
+
+public:
+ Mertens(LastExpressEngine *engine);
+ ~Mertens() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handle meeting Coudert with the blooded jacket
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(bloodJacket, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param entityPosition1 The entity position
+ * @param entityPosition1 The entity position to check
+ *
+ * @note We are not using the shared function due to too many differences
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment3, const char* sequence, ObjectIndex compartment, EntityPosition entityPosition1, EntityPosition entityPosition2)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char* filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function11, uint32 time)
+
+ /**
+ * Says "Bonsoir" to another character
+ *
+ * @param entity The entity
+ */
+ DECLARE_FUNCTION_1(bonsoir, EntityIndex entity)
+ DECLARE_FUNCTION_2(function13, bool, bool)
+ DECLARE_FUNCTION_1(function14, EntityIndex entity)
+ DECLARE_FUNCTION_1(function15, bool)
+ DECLARE_FUNCTION_1(function16, bool)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * ???
+ *
+ * @param object1 First object index
+ * @param object2 Second object index
+ */
+ DECLARE_FUNCTION_2(function21, ObjectIndex object1, ObjectIndex object2)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION_1(function26, bool)
+ DECLARE_FUNCTION_1(tylerCompartment, MertensActionType action)
+ DECLARE_FUNCTION_1(function28, const char *soundName)
+ DECLARE_FUNCTION_2(function29, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION_1(function30, MertensActionType action)
+ DECLARE_FUNCTION_1(function31, MertensActionType action)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function42)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function44)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function46)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MERTENS_H
diff --git a/engines/lastexpress/entities/milos.cpp b/engines/lastexpress/entities/milos.cpp
new file mode 100644
index 0000000000..30ed546106
--- /dev/null
+++ b/engines/lastexpress/entities/milos.cpp
@@ -0,0 +1,1077 @@
+/* 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/entities/milos.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Milos::Milos(LastExpressEngine *engine) : Entity(engine, kEntityMilos) {
+ ADD_CALLBACK_FUNCTION(Milos, reset);
+ ADD_CALLBACK_FUNCTION(Milos, draw);
+ ADD_CALLBACK_FUNCTION(Milos, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Milos, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Milos, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Milos, playSound);
+ ADD_CALLBACK_FUNCTION(Milos, playSound16);
+ ADD_CALLBACK_FUNCTION(Milos, savegame);
+ ADD_CALLBACK_FUNCTION(Milos, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Milos, enterCompartmentDialog);
+ ADD_CALLBACK_FUNCTION(Milos, function11);
+ ADD_CALLBACK_FUNCTION(Milos, chapter1);
+ ADD_CALLBACK_FUNCTION(Milos, function13);
+ ADD_CALLBACK_FUNCTION(Milos, function14);
+ ADD_CALLBACK_FUNCTION(Milos, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function16);
+ ADD_CALLBACK_FUNCTION(Milos, function17);
+ ADD_CALLBACK_FUNCTION(Milos, function18);
+ ADD_CALLBACK_FUNCTION(Milos, chapter2);
+ ADD_CALLBACK_FUNCTION(Milos, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function21);
+ ADD_CALLBACK_FUNCTION(Milos, chapter3);
+ ADD_CALLBACK_FUNCTION(Milos, function23);
+ ADD_CALLBACK_FUNCTION(Milos, function24);
+ ADD_CALLBACK_FUNCTION(Milos, function25);
+ ADD_CALLBACK_FUNCTION(Milos, function26);
+ ADD_CALLBACK_FUNCTION(Milos, function27);
+ ADD_CALLBACK_FUNCTION(Milos, chapter4);
+ ADD_CALLBACK_FUNCTION(Milos, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function30);
+ ADD_CALLBACK_FUNCTION(Milos, function31);
+ ADD_CALLBACK_FUNCTION(Milos, function32);
+ ADD_CALLBACK_FUNCTION(Milos, chapter5);
+ ADD_CALLBACK_FUNCTION(Milos, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function35);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Milos, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Milos, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Milos, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Milos, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Milos, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Milos, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Milos, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Milos, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(9, Milos, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Milos, enterCompartmentDialog, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ if (getEvent(kEventMilosTylerCompartmentDefeat)) {
+ // Robert saying: "Milos"
+ switch(rnd(3)) {
+ default:
+ case 0:
+ getSound()->playSound(kEntityPlayer, "CAT1014");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "CAT1014A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityPlayer, "CAT1014B");
+ break;
+ }
+ } else {
+ getSound()->excuseMeCath();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ error("Milos: callback function 11 not implemented!");
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(10);
+ setup_playSound((rnd(2) ? "CAT1504" : getSound()->wrongDoorCath()));
+ } else {
+ setCallback(11);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ if (savepoint.action == kActionKnock) {
+ setCallback(7);
+ setup_playSound("LIB012");
+ } else {
+ setCallback(8);
+ setup_playSound("LIB013");
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param3 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch(getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(2);
+ setup_enterCompartmentDialog(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function14();
+ break;
+
+ case 3:
+ if (getProgress().field_14 == 14)
+ getProgress().field_14 = 0;
+
+ params->param6 = 1;
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 7:
+ case 8:
+ setCallback(9);
+ // Milos asking: "Yeah? Who is it?"
+ setup_playSound("MIL1117A");
+ break;
+
+ case 9:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 10:
+ case 11:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 12:
+ getEntities()->drawSequenceLeft(kEntityMilos, "611Cg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction88652208);
+ break;
+
+ case 13:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param5 = 0;
+ break;
+
+ }
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(12);
+ setup_enterExitCompartment("611Bg", kObjectCompartmentG);
+ break;
+
+ case kAction123852928:
+ params->param1 = 13;
+ setup_enterExitCompartment("611Dg", kObjectCompartmentG);
+ break;
+
+ case kAction221683008:
+ params->param5 = 1;
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Milos, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject46, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getSavePoints()->addData(kEntityMilos, kAction157691176, 0);
+ getSavePoints()->addData(kEntityMilos, kAction208228224, 2);
+ getSavePoints()->addData(kEntityMilos, kAction259125998, 3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Milos, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityMilos, kEntityTables2, kActionDrawTablesWithChairs, "009E");
+ getEntities()->clearSequences(kEntityVesna);
+ getEntities()->clearSequences(kEntityIvo);
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntitySalko, "009D5");
+ getEntities()->drawSequenceRight(kEntityTables2, "009D4");
+ getEntities()->drawSequenceRight(kEntityIvo, "009D3");
+ getEntities()->drawSequenceRight(kEntityVesna, "009D2");
+ getEntities()->drawSequenceRight(kEntityMilos, "009D1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Milos, function14)
+ error("Milos: callback function 14 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Milos, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560);
+
+ if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setup_function16();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 61) && !params->param1) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 45)
+ setCallback(1);
+ setup_draw("009C");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 70) && !params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 45);
+
+ setCallback(2);
+ setup_draw("009C");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityMilos, kEntityTables2, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ params->param1 = 1;
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Milos, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityVesna, 750)
+ || getEntities()->checkDistanceFromPosition(kEntityVesna, kPosition_3050, 500)) {
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction123668192);
+
+ setCallback(5);
+ setup_enterExitCompartment("611Ag", kObjectCompartmentG);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMilos, kEntityServers1, kAction269485588);
+ getSavePoints()->push(kEntityMilos, kEntityIvo, kAction125242096);
+ getEntities()->drawSequenceRight(kEntityMilos, "807DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityMilos);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+
+ case 3:
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityVesna, 750)
+ || getEntities()->checkDistanceFromPosition(kEntityVesna, kPosition_3050, 500)) {
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction123668192);
+
+ setCallback(4);
+ setup_enterExitCompartment("611Ag", kObjectCompartmentG);
+ } else {
+ params->param1 = 1;
+
+ getEntities()->drawSequenceLeft(kEntityMilos, "609Dg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ }
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+
+ setup_function17();
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+
+ setup_function17();
+ break;
+ }
+ break;
+
+ case kAction135024800:
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction204832737);
+
+ setCallback(3);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Milos, function17)
+ if (savepoint.action == kActionDefault) {
+ setCallback(1);
+ setup_function11(kTimeBedTime);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Milos, function18)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Milos, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject46, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Milos, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction137165825);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerInCar(kCarRedSleeping) && !getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(1);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Milos, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ params->param1 = 1;
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB012");
+ break;
+
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitAugust);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!getEvent(kEventMilosCompartmentVisitAugust)
+ && !getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)
+ && params->param1)
+ setup_chapter2Handler();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Mil1118");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventMilosCompartmentVisitAugust);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 5);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction135024800);
+
+ setCallback(4);
+ setup_function11(kTimeEnd);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Milos, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->events[kEventMilosCompartmentVisitAugust])
+ setup_function24();
+ else
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Milos, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2106000 && !params->param1) {
+ params->param1 = 1;
+
+ setCallback(1);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction137165825);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerInCar(kCarRedSleeping)
+ && !getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(3);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ case 4:
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+
+ setup_function24();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Milos, function24)
+ error("Milos: callback function 24 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Milos, function25)
+ error("Milos: callback function 25 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_function27(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_function27(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 2) = 0;
+
+ setCallback(1);
+ setup_function27(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function27(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ if (getData()->car == kCarRedSleeping || getData()->car == kCarGreenSleeping) {
+ ENTITY_PARAM(0, 2) = 1;
+
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Milos, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Milos, chapter4Handler)
+#define TIME_CHECK_PLAYSOUND_MILOS(timeValue, parameter, sound) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getSound()->playSound(kEntityMilos, sound); \
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityPlayer, 2000)) \
+ getProgress().field_94 = 1; \
+ break; \
+ }
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1)
+ break;
+
+ if (params->param2) {
+ setup_function30();
+ break;
+ }
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2356200, params->param3, "Mil4013");
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2360700, params->param4, "Mil4014");
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2370600, params->param5, "Mil4015");
+
+ TIME_CHECK_SAVEPOINT(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityMilos, "611Cg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction88652208);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_3050;
+
+ getEntities()->clearSequences(kEntityMilos);
+
+ params->param1 = 0;
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ setCallback(1);
+ setup_enterExitCompartment("611Bg", kObjectCompartmentG);
+ break;
+
+ case kAction123852928:
+ setCallback(2);
+ setup_enterExitCompartment("611Dg", kObjectCompartmentG);
+ break;
+
+ case kAction135600432:
+ params->param2 = 1;
+ break;
+
+ case kAction221683008:
+ if (getSound()->isBuffered(kEntityMilos))
+ getSound()->processEntry(kEntityMilos);
+
+ params->param1 = 1;
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction123199584);
+ break;
+ }
+
+#undef TIME_CHECK_PLAYSOUND_MILOS
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Milos, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function11(kTime2410200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMilos, kEntityIvo, kAction55996766);
+
+ setCallback(2);
+ setup_function11(kTime2412000);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMilos, kEntitySalko, kAction55996766);
+
+ setCallback(3);
+ setup_function11(kTime2415600);
+ break;
+
+ case 3:
+ setup_function31();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Milos, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_enterExitCompartment("609CG", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterCompartmentDialog(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Milos, function32)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarCoalTender;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Milos, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarCoalTender;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Milos, chapter5Handler)
+ error("Milos: callback function 34 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Milos, function35)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityMilos);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/milos.h b/engines/lastexpress/entities/milos.h
new file mode 100644
index 0000000000..4bbafaaaa7
--- /dev/null
+++ b/engines/lastexpress/entities/milos.h
@@ -0,0 +1,175 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MILOS_H
+#define LASTEXPRESS_MILOS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Milos : public Entity {
+public:
+ Milos(LastExpressEngine *engine);
+ ~Milos() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char* filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION_2(enterCompartmentDialog, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_1(function11, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION_1(function26, TimeValue timeValue)
+ DECLARE_FUNCTION_2(function27, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function35)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MILOS_H
diff --git a/engines/lastexpress/entities/mmeboutarel.cpp b/engines/lastexpress/entities/mmeboutarel.cpp
new file mode 100644
index 0000000000..ce9ebf3abd
--- /dev/null
+++ b/engines/lastexpress/entities/mmeboutarel.cpp
@@ -0,0 +1,939 @@
+/* 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/entities/mmeboutarel.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+MmeBoutarel::MmeBoutarel(LastExpressEngine *engine) : Entity(engine, kEntityMmeBoutarel) {
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, reset);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, playSound);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, draw);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, updateFromTime);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, updateEntity);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function8);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function9);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter1);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function11);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function13);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function14);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function15);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function16);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter2);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function19);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter3);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter4);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function24);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function25);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter5);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function28);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, MmeBoutarel, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, MmeBoutarel, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, MmeBoutarel, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, MmeBoutarel, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, MmeBoutarel, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, MmeBoutarel, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_5790, kPosition_6130, kCarRedSleeping, kObjectCompartmentD, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, MmeBoutarel, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getInventory()->hasItem(kItemPassengerList) ? getSound()->playSound(kEntityPlayer, "CAT1021") : getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(8, MmeBoutarel, function8)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && params->param5) {
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ params->param5 = 1;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606U");
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction169557824);
+ break;
+
+ case kAction155853632:
+ params->param4 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606L");
+ getSound()->playSound(kEntityMmeBoutarel, (char *)&params->seq1);
+
+ if (getEntities()->hasValidFrame(kEntityMmeBoutarel) || getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000)) {
+ if (getProgress().chapter == kChapter1)
+ getProgress().field_A8 = 1;
+ else if (getProgress().chapter == kChapter3)
+ getProgress().field_A4 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, MmeBoutarel, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ getData()->entityPosition = getEntityData(kEntityBoutarel)->entityPosition;
+ getData()->location = getEntityData(kEntityBoutarel)->location;
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityBoutarel, kAction203520448);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityFrancois, kCarRedSleeping, kPosition_5790)) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ } else {
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Md");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ }
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kAction100957716:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, MmeBoutarel, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMmeBoutarel, kAction242526416, 0);
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, MmeBoutarel, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 1000) || !params->param2)
+ params->param2 = getState()->time + 150;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("MME1040");
+ break;
+
+ case kActionDefault:
+ params->param1 = getState()->time + 1800;
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("MME1040A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("MME1041");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateFromTime(900);
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, MmeBoutarel, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 44)) {
+ setCallback(1);
+ setup_draw("502B");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Qd");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ params->param1 = 1;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ setup_function13();
+ break;
+ }
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param1 = 0;
+ break;
+
+ case kAction168986720:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction102752636);
+ getSound()->playSound(kEntityMmeBoutarel, "MME1036");
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+
+ setCallback(3);
+ setup_enterExitCompartment("606Fd", kObjectCompartmentD);
+ break;
+
+ case kAction202221040:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ getSound()->playSound(kEntityMmeBoutarel, "MME1035A");
+
+ if (getEntities()->hasValidFrame(kEntityMmeBoutarel) || getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000) )
+ getProgress().field_AC = 1;
+
+ setCallback(2);
+ setup_enterExitCompartment("606Ed", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, MmeBoutarel, function13)
+ error("MmeBoutarel: callback function 13 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, MmeBoutarel, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("606Dd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "503");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "503");
+
+ setCallback(3);
+ setup_playSound("MRB1080");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ setup_function15();
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, MmeBoutarel, function15)
+ error("MmeBoutarel: callback function 15 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, MmeBoutarel, function16)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, MmeBoutarel, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject43, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, MmeBoutarel, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isInsideCompartment(kEntityFrancois, kCarRedSleeping, kPosition_5790)) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Md");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kAction100957716:
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, MmeBoutarel, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 44) && !params->param2) {
+ if (params->param1) {
+ setCallback(1);
+ setup_draw("502B");
+ } else {
+ params->param1 = 1;
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping , 44))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 11);
+ }
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param2 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param2 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, MmeBoutarel, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, MmeBoutarel, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1) && params->param2 != kTimeInvalid) {
+
+ if (getState()->time <= kTime2038500) {
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping)
+ || !params->param1
+ || getSound()->isBuffered("FRA2012")
+ || getSound()->isBuffered("FRA2010")
+ ||!params->param2)
+ params->param2 = getState()->time;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction189872836);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject43, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function8("MME3001");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment2("606Td", kObjectCompartmentD);
+ break;
+
+ case 6:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(7);
+ setup_updateFromTime(150);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("606Dd", kObjectCompartmentD);
+ break;
+
+ case 8:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction190390860);
+ break;
+
+ case 9:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(9);
+ setup_function9();
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, MmeBoutarel, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, MmeBoutarel, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->time, 900);
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ setup_function24();
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, MmeBoutarel, function24)
+ error("MmeBoutarel: callback function 24 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, MmeBoutarel, function25)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, MmeBoutarel, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, MmeBoutarel, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function28();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, MmeBoutarel, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param3 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound("Mme5001");
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction155604840:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(29, MmeBoutarel)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mmeboutarel.h b/engines/lastexpress/entities/mmeboutarel.h
new file mode 100644
index 0000000000..548a0635cb
--- /dev/null
+++ b/engines/lastexpress/entities/mmeboutarel.h
@@ -0,0 +1,164 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MMEBOUTAREL_H
+#define LASTEXPRESS_MMEBOUTAREL_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class MmeBoutarel : public Entity {
+public:
+ MmeBoutarel(LastExpressEngine *engine);
+ ~MmeBoutarel() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function8, const char *soundName)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function28)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MMEBOUTAREL_H
diff --git a/engines/lastexpress/entities/pascale.cpp b/engines/lastexpress/entities/pascale.cpp
new file mode 100644
index 0000000000..dfb0854eda
--- /dev/null
+++ b/engines/lastexpress/entities/pascale.cpp
@@ -0,0 +1,1232 @@
+/* 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/entities/pascale.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Pascale::Pascale(LastExpressEngine *engine) : Entity(engine, kEntityPascale) {
+ ADD_CALLBACK_FUNCTION(Pascale, draw);
+ ADD_CALLBACK_FUNCTION(Pascale, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Pascale, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Pascale, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Pascale, updatePosition);
+ ADD_CALLBACK_FUNCTION(Pascale, playSound);
+ ADD_CALLBACK_FUNCTION(Pascale, draw2);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeSophieAndRebecca);
+ ADD_CALLBACK_FUNCTION(Pascale, sitSophieAndRebecca);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeCath);
+ ADD_CALLBACK_FUNCTION(Pascale, function11);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter1);
+ ADD_CALLBACK_FUNCTION(Pascale, getMessageFromAugustToTyler);
+ ADD_CALLBACK_FUNCTION(Pascale, sitAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, serveTatianaVassili);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function18);
+ ADD_CALLBACK_FUNCTION(Pascale, function19);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter2);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter3);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function23);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeAbbot);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter4);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function27);
+ ADD_CALLBACK_FUNCTION(Pascale, messageFromAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, function29);
+ ADD_CALLBACK_FUNCTION(Pascale, function30);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter5);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function33);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(1, Pascale, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Pascale, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Pascale, callbackActionOnDirection)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityPascale);
+ params->param1 = 1;
+ }
+
+ return;
+ }
+
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Pascale, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Pascale, updatePosition)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Pascale, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Pascale, draw2)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Pascale, welcomeSophieAndRebecca)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("901");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getSound()->playSound(kEntityPascale, "REB1198", SoundManager::kFlagInvalid, 30);
+ break;
+
+ case kChapter3:
+ getSound()->playSound(kEntityPascale, "REB3001", SoundManager::kFlagInvalid, 30);
+ break;
+
+ case kChapter4:
+ getSound()->playSound(kEntityPascale, "REB4001", SoundManager::kFlagInvalid, 30);
+ break;
+ }
+
+ setCallback(2);
+ setup_sitSophieAndRebecca();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityRebecca, kAction157370960);
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Pascale, sitSophieAndRebecca)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityPascale, "012C1");
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012C2");
+ getEntities()->drawSequenceLeft(kEntityTables3, "012C3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Pascale, welcomeCath)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && !getSound()->isBuffered(kEntityPascale))
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 64);
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param2) {
+ params->param2 = 1;
+
+ getSound()->playSound(kEntityPascale, "HED1001A");
+ getSound()->playSound(kEntityPlayer, "LIB004");
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction4:
+ if (!params->param1) {
+ params->param1 = 1;
+ getSound()->playSound(kEntityPascale, "HED1001");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 64);
+ getEntities()->drawSequenceRight(kEntityPascale, "035A");
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isPlayerPosition(kCarRestaurant, 64)) {
+ getSound()->playSound(kEntityPascale, "HED1001A");
+ getSound()->playSound(kEntityPlayer, "LIB004");
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Pascale, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction168046720);
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction168046720);
+ getSavePoints()->push(kEntityPascale, kEntityAlexei, kAction168046720);
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 55);
+
+ setCallback(1);
+ setup_welcomeCath();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction168627977);
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction168627977);
+ getSavePoints()->push(kEntityPascale, kEntityAlexei, kAction168627977);
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 55);
+
+ setCallback(2);
+ setup_draw("905");
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Pascale, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityPascale, kAction239072064, 0);
+ getSavePoints()->addData(kEntityPascale, kAction257489762, 2);
+ getSavePoints()->addData(kEntityPascale, kAction207769280, 6);
+ getSavePoints()->addData(kEntityPascale, kAction101824388, 7);
+ getSavePoints()->addData(kEntityPascale, kAction136059947, 8);
+ getSavePoints()->addData(kEntityPascale, kAction223262556, 1);
+ getSavePoints()->addData(kEntityPascale, kAction269479296, 3);
+ getSavePoints()->addData(kEntityPascale, kAction352703104, 4);
+ getSavePoints()->addData(kEntityPascale, kAction352768896, 5);
+ getSavePoints()->addData(kEntityPascale, kAction191604416, 10);
+ getSavePoints()->addData(kEntityPascale, kAction190605184, 11);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Pascale, getMessageFromAugustToTyler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("902");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!ENTITY_PARAM(1, 3)) {
+ getEntities()->drawSequenceLeft(kEntityPascale, "010E");
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+
+ setCallback(2);
+ setup_playSound("AUG1001");
+ break;
+ }
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityPascale, "010B");
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityPascale);
+ getSavePoints()->push(kEntityPascale, kEntityVerges, kActionDeliverMessageToTyler);
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Pascale, sitAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 62);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables0, "001C3");
+ getEntities()->drawSequenceRight(kEntityAnna, "001C2");
+ getEntities()->drawSequenceRight(kEntityPascale, "001C1");
+
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 62);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Pascale, welcomeAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("901");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPascale, "ANN1047");
+
+ setCallback(2);
+ setup_sitAnna();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction157370960);
+
+ setCallback(3);
+ setup_draw("904");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Pascale, serveTatianaVassili)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("903");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityTatiana, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityPascale, "014B");
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 67);
+
+ if (getSound()->isBuffered("TAT1069A"))
+ getSound()->processEntry("TAT1069A");
+ else if (getSound()->isBuffered("TAT1069B"))
+ getSound()->processEntry("TAT1069B");
+
+ setCallback(2);
+ setup_playSound("TAT1066");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityPascale, kEntityTatiana, kAction122288808);
+
+ setCallback(3);
+ setup_draw("906");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Pascale, chapter1Handler)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61))
+ params->param1 = 1;
+ }
+
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (ENTITY_PARAM(0, 5) && ENTITY_PARAM(0, 6)) {
+ setup_function18();
+ break;
+ }
+
+ if (!getEntities()->isSomebodyInsideRestaurantOrSalon())
+ goto label_callback3;
+
+ if (params->param1 && !params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61)) {
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(0, 1) && !ENTITY_PARAM(1, 3)) {
+ setCallback(2);
+ setup_getMessageFromAugustToTyler();
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_serveTatianaVassili();
+ break;
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(4);
+ setup_welcomeAnna();
+ break;
+ }
+
+label_callback4:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(5);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Pascale, function18)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (getState()->time > kTime1242000 && !params->param1) {
+ params->param1 = 1;
+
+ getSavePoints()->push(kEntityPascale, kEntityServers0, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityServers1, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityCooks, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityVerges, kAction101632192);
+
+ setup_function19();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Pascale, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntityData(kEntityPlayer)->entityPosition < kPosition_3650) {
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityPascale, kEntityTables0, kActionDrawTablesWithChairs, "001P");
+ getSavePoints()->push(kEntityPascale, kEntityTables1, kActionDrawTablesWithChairs, "005J");
+ getSavePoints()->push(kEntityPascale, kEntityTables2, kActionDrawTablesWithChairs, "009G");
+ getSavePoints()->push(kEntityPascale, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kActionDrawTablesWithChairs, "014F");
+ getSavePoints()->push(kEntityPascale, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityPascale);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Pascale, chapter2)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Pascale, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Pascale, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(1);
+ setup_function23();
+ break;
+ }
+
+label_callback:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(2);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ goto label_callback;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Pascale, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_welcomeAbbot();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityPascale, kEntityAbbot, kAction122288808);
+
+ setCallback(2);
+ setup_draw("906");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 7) = 0;
+ getEntities()->clearSequences(kEntityPascale);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Pascale, welcomeAbbot)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ getSound()->playSound(kEntityPascale, "ABB3015A");
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kAction10:
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kAction136455232);
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityPascale, "ABB3015", SoundManager::kFlagInvalid, 105);
+ getEntities()->drawSequenceRight(kEntityPascale, "029A1");
+ getEntities()->drawSequenceRight(kEntityAbbot, "029A2");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Pascale, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Pascale, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2511000 && !params->param4) {
+ params->param2 = 1;
+ params->param4 = 1;
+ }
+
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function27();
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(1, 2) && ENTITY_PARAM(1, 4)) {
+ if (!params->param3)
+ params->param3 = getState()->time + 9000;
+
+ if (params->param5 != kTimeInvalid) {
+
+ if (params->param3 < getState()->time) {
+ params->param5 = kTimeInvalid;
+ setCallback(2);
+ setup_messageFromAnna();
+ break;
+ }
+
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param5)
+ params->param5 = getState()->time;
+
+ if (params->param5 < getState()->time) {
+ params->param5 = kTimeInvalid;
+ setCallback(2);
+ setup_messageFromAnna();
+ break;
+ }
+ }
+ }
+
+label_callback2:
+ if (params->param1 && !params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61)) {
+ setCallback(3);
+ setup_function11();
+ break;
+ }
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+ break;
+
+ case kActionDrawScene:
+ if (!params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61))
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback3;
+ }
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ getSavePoints()->push(kEntityPascale, kEntityTables0, kActionDrawTablesWithChairs, "001P");
+ getSavePoints()->push(kEntityPascale, kEntityTables1, kActionDrawTablesWithChairs, "005J");
+ getSavePoints()->push(kEntityPascale, kEntityTables2, kActionDrawTablesWithChairs, "009G");
+ getSavePoints()->push(kEntityPascale, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kActionDrawTablesWithChairs, "014F");
+ getSavePoints()->push(kEntityPascale, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Pascale, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_updateFromTime(450);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function29();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityPascale);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityCoudert, kAction123712592);
+
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function30();
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 8) = 0;
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Pascale, messageFromAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("902");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityPascale, "010E2");
+
+ setCallback(2);
+ setup_playSound("Aug4001");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction123793792);
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(1, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Pascale, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("817DD");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityPascale, "817DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityPascale);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_850;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Pascale, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("817US");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityPascale, "817UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityPascale);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Pascale, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Pascale, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function33();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Pascale, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4) {
+ UPDATE_PARAM_PROC(params->param5, getState()->time, 4500)
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("Wat5010");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback1:
+ if (params->param1) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 2;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param6 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(2);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param1) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback1;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ params->param3++;
+
+ if (params->param3 == 1 || params->param3 == 2) {
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ setCallback(params->param3 == 1 ? 5 : 6);
+ setup_playSound(params->param3 == 1 ? "Wat5001" : "Wat5002");
+ }
+ break;
+
+ case 5:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 6:
+ params->param2 = 1;
+ break;
+
+ case 7:
+ params->param4 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction169750080:
+ if (getSound()->isBuffered(kEntityPascale)) {
+ params->param4 = 1;
+ } else {
+ setCallback(7);
+ setup_playSound("Wat5002");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(34, Pascale)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/pascale.h b/engines/lastexpress/entities/pascale.h
new file mode 100644
index 0000000000..d899037c0b
--- /dev/null
+++ b/engines/lastexpress/entities/pascale.h
@@ -0,0 +1,166 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_PASCALE_H
+#define LASTEXPRESS_PASCALE_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Pascale : public Entity {
+public:
+ Pascale(LastExpressEngine *engine);
+ ~Pascale() {};
+
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the position
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The car
+ * - The position
+ */
+ DECLARE_FUNCTION_NOSETUP(updatePosition)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The sequence to draw for the second entity
+ * - The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_NOSETUP(draw2)
+
+ DECLARE_FUNCTION(welcomeSophieAndRebecca)
+ DECLARE_FUNCTION(sitSophieAndRebecca)
+ DECLARE_FUNCTION(welcomeCath)
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(getMessageFromAugustToTyler)
+ DECLARE_FUNCTION(sitAnna)
+ DECLARE_FUNCTION(welcomeAnna)
+ DECLARE_FUNCTION(serveTatianaVassili)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(welcomeAbbot)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(messageFromAnna)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function33)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_PASCALE_H
diff --git a/engines/lastexpress/entities/rebecca.cpp b/engines/lastexpress/entities/rebecca.cpp
new file mode 100644
index 0000000000..b9c3f23401
--- /dev/null
+++ b/engines/lastexpress/entities/rebecca.cpp
@@ -0,0 +1,1732 @@
+/* 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/entities/rebecca.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Rebecca::Rebecca(LastExpressEngine *engine) : Entity(engine, kEntityRebecca) {
+ ADD_CALLBACK_FUNCTION(Rebecca, reset);
+ ADD_CALLBACK_FUNCTION(Rebecca, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Rebecca, playSound);
+ ADD_CALLBACK_FUNCTION(Rebecca, playSound16);
+ ADD_CALLBACK_FUNCTION(Rebecca, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Rebecca, draw);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(Rebecca, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Rebecca, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Rebecca, updateEntity);
+ ADD_CALLBACK_FUNCTION(Rebecca, updatePosition);
+ ADD_CALLBACK_FUNCTION(Rebecca, draw2);
+ ADD_CALLBACK_FUNCTION(Rebecca, function15);
+ ADD_CALLBACK_FUNCTION(Rebecca, function16);
+ ADD_CALLBACK_FUNCTION(Rebecca, function17);
+ ADD_CALLBACK_FUNCTION(Rebecca, function18);
+ ADD_CALLBACK_FUNCTION(Rebecca, function19);
+ ADD_CALLBACK_FUNCTION(Rebecca, function20);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter1);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function23);
+ ADD_CALLBACK_FUNCTION(Rebecca, function24);
+ ADD_CALLBACK_FUNCTION(Rebecca, function25);
+ ADD_CALLBACK_FUNCTION(Rebecca, function26);
+ ADD_CALLBACK_FUNCTION(Rebecca, function27);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter2);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function30);
+ ADD_CALLBACK_FUNCTION(Rebecca, function31);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter3);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function34);
+ ADD_CALLBACK_FUNCTION(Rebecca, function35);
+ ADD_CALLBACK_FUNCTION(Rebecca, function36);
+ ADD_CALLBACK_FUNCTION(Rebecca, function37);
+ ADD_CALLBACK_FUNCTION(Rebecca, function38);
+ ADD_CALLBACK_FUNCTION(Rebecca, function39);
+ ADD_CALLBACK_FUNCTION(Rebecca, function40);
+ ADD_CALLBACK_FUNCTION(Rebecca, function41);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter4);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function44);
+ ADD_CALLBACK_FUNCTION(Rebecca, function45);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter5);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function48);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Rebecca, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, Rebecca, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Rebecca, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Rebecca, playSound16)
+ Entity::playSound(savepoint, false, getSound()->getSoundFlag(kEntityCoudert));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(5, Rebecca, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Rebecca, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, Rebecca, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(8, Rebecca, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_4840, kPosition_4455, kCarRedSleeping, kObjectCompartmentE, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(9, Rebecca, enterExitCompartment3, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Rebecca, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Rebecca, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(12, Rebecca, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(13, Rebecca, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(14, Rebecca, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Rebecca, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isOutsideAnnaWindow())
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ setCallback(1);
+ setup_enterExitCompartment2("624Ae", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getObjects()->update(kObjectOutsideBetweenCompartments, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityRebecca);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Rebecca, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)) {
+ if (!getEntities()->hasValidFrame(kEntitySophie)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ }
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment(params->param1 ? "624Be" : "623Ee", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityRebecca);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("810US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityRebecca, "012B");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityRebecca);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012A");
+ if (getProgress().chapter == kChapter3)
+ getSound()->playSound(kEntityRebecca, "REB3000");
+
+ getSavePoints()->push(kEntityRebecca, kEntityPascale, kAction269479296);
+
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction157370960:
+ getSavePoints()->push(kEntityRebecca, kEntityTables3, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ && !getEntities()->hasValidFrame(kEntitySophie)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(3);
+ setup_updateFromTime(0);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("624Be", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getProgress().chapter == kChapter3)
+ getSound()->playSound(kEntityRebecca, "Reb3005", SoundManager::kFlagInvalid, 75);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_updatePosition("118A", kCarRestaurant, 52);
+ } else {
+ getEntities()->updatePositionEnter(kEntityRebecca, kCarRestaurant, 57);
+
+ setCallback(6);
+ setup_draw2("107A1", "107A2", kEntitySophie);
+ }
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->updatePositionExit(kEntityRebecca, kCarRestaurant, 57);
+ getEntities()->clearSequences(kEntitySophie);
+
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Rebecca, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ getEntities()->exitCompartment(kEntityRebecca, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_function15();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction136654208);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(2);
+ setup_function15();
+ } else {
+ getEntities()->drawSequenceLeft(kEntityRebecca, "623Ge");
+ getEntities()->enterCompartment(kEntityRebecca, kObjectCompartmentE, true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Rebecca, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ getEntities()->exitCompartment(kEntityRebecca, kObjectCompartmentE, true);
+
+ setCallback(6);
+ setup_function15();
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_callSavepoint("012H", kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction337548856);
+ getEntities()->drawSequenceRight(kEntityRebecca, "810DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityRebecca);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction136654208);
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 4:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(5);
+ setup_function15();
+ } else {
+ getEntities()->drawSequenceLeft(kEntityRebecca, "623Ge");
+ getEntities()->enterCompartment(kEntityRebecca, kObjectCompartmentE, true);
+ }
+ break;
+
+ case 5:
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(20, Rebecca, function20, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param5) {
+ params->param5 = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!params->param2) {
+ params->param6 = 0;
+ } else {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75)
+ params->param2 = 0;
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getProgress().chapter == kChapter1 && !ENTITY_PARAM(0, 3)) {
+ if (params->param7 != kTimeInvalid && getState()->time > kTime1174500) {
+ if (getState()->time <= kTime1183500) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 2000) || getSound()->isBuffered("CON1210") || !params->param7)
+ params->param7 = getState()->time;
+
+ if (params->param7 >= getState()->time)
+ goto label_callback;
+ }
+
+ params->param7 = kTimeInvalid;
+ ENTITY_PARAM(0, 3) = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("REB1205");
+ break;
+ }
+ goto label_callback;
+ }
+
+ if (getProgress().chapter == kChapter3 && !ENTITY_PARAM(0, 4) && params->param8 != kTimeInvalid && getState()->time > kTime2097000) {
+ if (getState()->time <= kTime2106000) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 1000) || !params->param8)
+ params->param8 = getState()->time;
+
+ if (params->param8 >= getState()->time)
+ goto label_callback;
+ }
+
+ params->param8 = kTimeInvalid;
+ ENTITY_PARAM(0, 4) = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound("REB3010");
+ break;
+ }
+
+label_callback:
+ if (ENTITY_PARAM(0, 2) && getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 1000)) {
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_playSound("REB1040");
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getCallback() != 2)
+ ENTITY_PARAM(0, 2) = 0;
+
+ if (getCallback() != 3)
+ goto label_callback;
+ break;
+
+ case 4:
+ case 5:
+ if (rnd(2)) {
+ setCallback(6);
+ setup_playSound("REB1039");
+ } else {
+ setCallback(7);
+ setup_playSound(rnd(2) ? "SOP1039" : "SOP1039A");
+ }
+ break;
+
+ case 6:
+ case 7:
+ params->param4 = (getCallback() == 6 ? 0 : 1);
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_playSound16("JAC1012B");
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction254915200:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(12);
+ setup_playSound("REB1039A");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Rebecca, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityRebecca, kAction224253538, 0);
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideBetweenCompartments, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation1);
+
+ getData()->entityPosition = kPosition_2830;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_PLAYSOUND(kTime1084500, params->param3, 1, "REB1015");
+
+ if (params->param4 == kTimeInvalid)
+ goto label_callback_4;
+
+ if (getState()->time > kTime1080000)
+ goto label_playConversation;
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = getState()->time + 150;
+
+ if (params->param4 >= getState()->time) {
+label_callback_4:
+ if (params->param1) {
+ UPDATE_PARAM_CHECK(params->param5, getState()->time, 900)
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setCallback(5);
+ setup_playSound("REB1013");
+ break;
+ }
+ }
+ }
+
+label_callback_5:
+ if (params->param2) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param6 = 0;
+ }
+ } else {
+label_playConversation:
+ params->param4 = kTimeInvalid;
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getProgress().field_B8 = 1;
+
+ setCallback(4);
+ setup_playSound("REB1012");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "107B");
+ break;
+
+ case kActionDrawScene:
+ params->param2 = (getEntities()->isPlayerPosition(kCarRestaurant, 57) ? 1 : 0);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updatePosition("107C", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ setup_function23();
+ break;
+
+ case 4:
+ params->param1 = 1;
+ goto label_callback_4;
+
+ case 5:
+ getProgress().field_B4 = 1;
+ params->param1 = 0;
+ goto label_callback_5;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Rebecca, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_2(kTime1111500, params->param2, 3, setup_enterExitCompartment, "623De", kObjectCompartmentE);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(900);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("623Ce", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "504");
+ break;
+
+ case 3:
+ case 6:
+ getEntities()->clearSequences(kEntityRebecca);
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback((byte)(getCallback() + 1));
+ setup_function20(kTime1120500);
+ break;
+
+ case 4:
+ case 5:
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function24();
+ } else {
+ setCallback(5);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ break;
+
+ case 7:
+ case 8:
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function24();
+ } else {
+ setCallback(8);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ break;
+ }
+ break;
+
+ case kAction285528346:
+ setCallback(6);
+ setup_enterExitCompartment("623De", kObjectCompartmentE);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Rebecca, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416);
+
+ if (!params->param1)
+ break;
+
+ TIME_CHECK_CALLBACK(kTime1165500, params->param3, 6, setup_function19);
+
+ if (params->param4 != kTimeInvalid) {
+ if (getState()->time <= kTime1161000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = getState()->time + 150;
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setCallback(7);
+ setup_playSound("REB1200A");
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+
+ setCallback(2);
+ setup_playSound("REB1199");
+ break;
+
+ case 2:
+ if (getEntities()->isInRestaurant(kEntityPlayer)) {
+ setCallback(3);
+ setup_playSound("REB1199A");
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ if (getCallback() == 3)
+ getProgress().field_BC = 1;
+
+ if (getEntities()->isInRestaurant(kEntityAnna)) {
+ setCallback(4);
+ setup_playSound("REB1199B");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ setCallback(5);
+ setup_playSound("REB1199C");
+ break;
+
+ case 6:
+ setup_function25();
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012E");
+
+ setCallback(8);
+ setup_playSound("REB1200");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Rebecca, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime1184400);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Rebecca, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_3(kTime1224000, params->param2, 1, setup_updatePosition, "118H", kCarRestaurant, 52);
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 52);
+ params->param3 = 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Rebecca, function27)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityRebecca);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Rebecca, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation2);
+
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Rebecca, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime1764000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Rebecca, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && params->param4 != kTimeInvalid) {
+
+ if (getState()->time <= kTimeEnd)
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = getState()->time + 450;
+
+ if (params->param4 < getState()->time || getState()->time > kTimeEnd) {
+ params->param4 = kTimeInvalid;
+
+ getSound()->playSound(kEntityRebecca, "Reb2001");
+ getProgress().field_B0 = 1;
+ params->param2 = 1;
+ }
+ }
+
+ if (!params->param3 && !params->param2 && params->param5 != kTimeInvalid) {
+
+ if (getState()->time <= kTime10881000) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param5)
+ params->param5 = getState()->time + 450;
+
+ if (params->param5 >= getState()->time)
+ break;
+ }
+
+ params->param5 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityAugust, kAction169358379);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "107B");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function31();
+ break;
+
+ case kAction125496184:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kAction155465152:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "BLANK");
+ break;
+
+ case kAction155980128:
+ params->param1 = 1;
+ params->param3 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Rebecca, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(900);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("623CE", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "504");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Rebecca, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Rebecca, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2016000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Rebecca, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid) {
+ if (getState()->time <= kTime1386000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param2)
+ params->param2 = getState()->time;
+
+ if (params->param2 >= getState()->time) {
+ TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19);
+ break;
+ }
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416);
+ }
+
+ TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19);
+ break;
+
+ case kActionEndSound:
+ setCallback(5);
+ setup_playSound("Reb3004");
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(2);
+ setup_playSound("Reb3002");
+ break;
+
+ case 3:
+ setup_function35();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getSound()->playSound(kEntityRebecca, "Reb3003");
+
+ setCallback(4);
+ setup_draw("012E");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Rebecca, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2070000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function36();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Rebecca, function36)
+ error("Rebecca: callback function 36 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Rebecca, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2110500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function38();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Rebecca, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment3("624Be", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction259921280);
+
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ setup_function39();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Rebecca, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function40();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Rebecca, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_9270;
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction292775040);
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntityAnna, kAction191668032);
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ setCallback(4);
+ setup_function15();
+ break;
+
+ case 4:
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Rebecca, function41)
+ if (savepoint.action == kActionDefault) {
+ ENTITY_PARAM(0, 2) = 1;
+
+ setCallback(1);
+ setup_function20(kTimeEnd);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Rebecca, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation3);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Rebecca, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2385000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function44();
+ } else {
+ setCallback(2);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Rebecca, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 != kTimeInvalid) {
+ if (getState()->time <= kTime2412000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param3)
+ params->param3 = getState()->time;
+
+ if (params->param3 >= getState()->time)
+ goto label_next;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416);
+ }
+
+label_next:
+ if (params->param1 && params->param4 != kTimeInvalid) {
+ if (getState()->time <= kTime2430000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = getState()->time + 150;
+
+ if (params->param4 >= getState()->time)
+ goto label_callback_2;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setCallback(2);
+ setup_playSound("Reb4004");
+ break;
+ }
+
+label_callback_2:
+ if (params->param2)
+ TIME_CHECK_CALLBACK(kTime2443500, params->param5, 3, setup_function19);
+ break;
+
+ case kActionEndSound:
+ if (getEntities()->isInRestaurant(kEntityPlayer)) {
+ setCallback(5);
+ setup_playSound("Reb4004");
+ break;
+ }
+
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ setup_function45();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "BLANK");
+ getSound()->playSound(kEntityRebecca, "Reb4003");
+
+ setCallback(4);
+ setup_draw("012E");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Rebecca, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityRebecca);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 1;
+ break;
+
+ case kAction205034665:
+ if (!params->param1 && getState()->time < kTime2511000) {
+ setCallback(1);
+ setup_playSound("Reb6969");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Rebecca, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Rebecca, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function48();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Rebecca, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param3 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ params->param1 = 0;
+
+ setCallback(2);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_enterExitCompartment("624AE", kObjectCompartmentE);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityRebecca);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4840;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("Reb5001");
+ break;
+
+ case 5:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction155604840:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(49, Rebecca)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/rebecca.h b/engines/lastexpress/entities/rebecca.h
new file mode 100644
index 0000000000..bf836b1fa3
--- /dev/null
+++ b/engines/lastexpress/entities/rebecca.h
@@ -0,0 +1,230 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_REBECCA_H
+#define LASTEXPRESS_REBECCA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Rebecca : public Entity {
+public:
+ Rebecca(LastExpressEngine *engine);
+ ~Rebecca() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char* filename)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment3, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char* sequence1, const char* sequence2, EntityIndex entity)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION_1(function16, bool)
+ DECLARE_FUNCTION_1(function17, bool)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION_1(function20, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function48)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_REBECCA_H
diff --git a/engines/lastexpress/entities/salko.cpp b/engines/lastexpress/entities/salko.cpp
new file mode 100644
index 0000000000..688208f30a
--- /dev/null
+++ b/engines/lastexpress/entities/salko.cpp
@@ -0,0 +1,642 @@
+/* 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/entities/salko.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Salko::Salko(LastExpressEngine *engine) : Entity(engine, kEntitySalko) {
+ ADD_CALLBACK_FUNCTION(Salko, reset);
+ ADD_CALLBACK_FUNCTION(Salko, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Salko, draw);
+ ADD_CALLBACK_FUNCTION(Salko, updateEntity);
+ ADD_CALLBACK_FUNCTION(Salko, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Salko, savegame);
+ ADD_CALLBACK_FUNCTION(Salko, function7);
+ ADD_CALLBACK_FUNCTION(Salko, function8);
+ ADD_CALLBACK_FUNCTION(Salko, chapter1);
+ ADD_CALLBACK_FUNCTION(Salko, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function11);
+ ADD_CALLBACK_FUNCTION(Salko, chapter2);
+ ADD_CALLBACK_FUNCTION(Salko, function13);
+ ADD_CALLBACK_FUNCTION(Salko, chapter3);
+ ADD_CALLBACK_FUNCTION(Salko, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function16);
+ ADD_CALLBACK_FUNCTION(Salko, function17);
+ ADD_CALLBACK_FUNCTION(Salko, chapter4);
+ ADD_CALLBACK_FUNCTION(Salko, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function20);
+ ADD_CALLBACK_FUNCTION(Salko, function21);
+ ADD_CALLBACK_FUNCTION(Salko, function22);
+ ADD_CALLBACK_FUNCTION(Salko, chapter5);
+ ADD_CALLBACK_FUNCTION(Salko, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Salko, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Salko, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(3, Salko, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(4, Salko, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(5, Salko, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(6, Salko, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Salko, function7, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ params->param3 = 0;
+
+ EntityDirection direction = getData()->direction;
+ CarIndex carSalko = getData()->car;
+ CarIndex carIvo = getEntityData(kEntityIvo)->car;
+ EntityPosition positionSalko = getData()->entityPosition;
+ EntityPosition positionIvo = getEntityData(kEntityIvo)->entityPosition;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntitySalko, kEntityIvo, 500)
+ || (direction == kDirectionUp && (carSalko > carIvo || (carSalko == carIvo && positionSalko > positionIvo)))
+ || (direction == kDirectionDown && (carSalko < carIvo || (carSalko == carIvo && positionSalko < positionIvo)))) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntitySalko, (CarIndex)params->param1, (EntityPosition)params->param2);
+
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ getSound()->playSound(kEntityPlayer, "ZFX1002", getSound()->getSoundFlag(kEntitySalko));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntitySalko, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Salko, function8)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Salko, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Salko, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityIvo)->entityPosition;
+ getData()->location = getEntityData(kEntityIvo)->location;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->clearSequences(kEntitySalko);
+ setup_function8();
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Salko, function11)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntitySalko);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Salko, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction136184016:
+ setCallback(1);
+ setup_function13();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Salko, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("612DH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntitySalko, kEntityIvo, kAction102675536);
+ getEntities()->clearSequences(kEntitySalko);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntitySalko, "BLANK");
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function8();
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(3);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Salko, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Salko, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time < kTime2200500) {
+ UPDATE_PARAM(params->param1, getState()->time, 81000);
+
+ setCallback(1);
+ setup_function16();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Salko, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->hasValidFrame(kEntitySalko) && getEntities()->isDistanceBetweenEntities(kEntitySalko, kEntityPlayer, 5000)) {
+ getSavePoints()->push(kEntitySalko, kEntityMax, kAction158007856);
+
+ setCallback(3);
+ setup_updateFromTime(75);
+ break;
+ }
+
+label_callback3:
+ UPDATE_PARAM(params->param1, getState()->time, 4500);
+
+ getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("612DH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464);
+ goto label_callback3;
+
+ case 4:
+ getEntities()->exitCompartment(kEntitySalko, kObjectCompartmentF, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_2740;
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction101169464:
+ setCallback(4);
+ setup_enterExitCompartment("612Bf", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Salko, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySalko);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntitySalko, kEntityMilos, kAction157691176);
+
+ setup_chapter3Handler();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Salko, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_5420;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Salko, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntitySalko, "BLANK");
+
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function20();
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Salko, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySalko);
+ setup_function21();
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ setCallback(1);
+ setup_enterExitCompartment("612Dh", kObjectCompartmentH);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Salko, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2422800 && !params->param1) {
+ params->param1 = 1;
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 2:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Salko, function22)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntitySalko);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Salko, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Salko, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ getAction()->playAnimation(kEventCathSalkoTrainTopFight);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightSalko);
+
+ if (params->param1 == Fight::kFightEndWin) {
+ getState()->time += 1800;
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventCathSalkoTrainTopWin);
+ } else {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param1 == Fight::kFightEndLost);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathSalkoTrainTopWin);
+ getSavePoints()->push(kEntitySalko, kEntityVesna, kAction134427424);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 10);
+ setup_nullfunction();
+ break;
+ }
+ break;
+
+ case kAction167992577:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathSalkoTrainTopFight);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(25, Salko)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/salko.h b/engines/lastexpress/entities/salko.h
new file mode 100644
index 0000000000..49059c4b40
--- /dev/null
+++ b/engines/lastexpress/entities/salko.h
@@ -0,0 +1,149 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SALKO_H
+#define LASTEXPRESS_SALKO_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Salko : public Entity {
+public:
+ Salko(LastExpressEngine *engine);
+ ~Salko() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION_2(function7, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function8)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SALKO_H
diff --git a/engines/lastexpress/entities/servers0.cpp b/engines/lastexpress/entities/servers0.cpp
new file mode 100644
index 0000000000..33f2943c43
--- /dev/null
+++ b/engines/lastexpress/entities/servers0.cpp
@@ -0,0 +1,1039 @@
+/* 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/entities/servers0.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+#define HANDLE_TABLE(index, param, callback, function) \
+ if (ENTITY_PARAM(index, param)) { \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+Servers0::Servers0(LastExpressEngine *engine) : Entity(engine, kEntityServers0) {
+ ADD_CALLBACK_FUNCTION(Servers0, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Servers0, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Servers0, draw);
+ ADD_CALLBACK_FUNCTION(Servers0, updatePosition);
+ ADD_CALLBACK_FUNCTION(Servers0, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Servers0, playSound);
+ ADD_CALLBACK_FUNCTION(Servers0, function7);
+ ADD_CALLBACK_FUNCTION(Servers0, function8);
+ ADD_CALLBACK_FUNCTION(Servers0, function9);
+ ADD_CALLBACK_FUNCTION(Servers0, function10);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter1);
+ ADD_CALLBACK_FUNCTION(Servers0, function12);
+ ADD_CALLBACK_FUNCTION(Servers0, function13);
+ ADD_CALLBACK_FUNCTION(Servers0, function14);
+ ADD_CALLBACK_FUNCTION(Servers0, function15);
+ ADD_CALLBACK_FUNCTION(Servers0, function16);
+ ADD_CALLBACK_FUNCTION(Servers0, function17);
+ ADD_CALLBACK_FUNCTION(Servers0, function18);
+ ADD_CALLBACK_FUNCTION(Servers0, function19);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, function21);
+ ADD_CALLBACK_FUNCTION(Servers0, function22);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter2);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, function25);
+ ADD_CALLBACK_FUNCTION(Servers0, function26);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter3);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, augustAnnaDateOrder);
+ ADD_CALLBACK_FUNCTION(Servers0, function30);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter4);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, augustOrderSteak);
+ ADD_CALLBACK_FUNCTION(Servers0, augustServeDuck);
+ ADD_CALLBACK_FUNCTION(Servers0, function35);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter5);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(1, Servers0, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(2, Servers0, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Servers0, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Servers0, updatePosition)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Servers0, callbackActionOnDirection)
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityServers0);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Servers0, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Servers0, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ ENTITY_PARAM(0, 3) = 0;
+
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityServers0);
+ getSavePoints()->push(kEntityServers0, kEntityRebecca, kAction123712592);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction136702400:
+ setCallback(2);
+ setup_draw("913");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Servers0, function8)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "913", &ENTITY_PARAM(1, 2));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Servers0, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("915");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "029D");
+
+ setCallback(2);
+ setup_playSound(getProgress().chapter == kChapter3 ? "Abb3016" : "Abb4001");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122288808);
+
+ setCallback(3);
+ setup_draw("917");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Servers0, function10)
+ serveTable(savepoint, "916", kEntityTables4, "014E", "014F", "918", &ENTITY_PARAM(2, 3), false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Servers0, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityServers0, kAction270410280, 0);
+ getSavePoints()->addData(kEntityServers0, kAction304061224, 1);
+ getSavePoints()->addData(kEntityServers0, kAction252568704, 10);
+ getSavePoints()->addData(kEntityServers0, kAction286534136, 11);
+ getSavePoints()->addData(kEntityServers0, kAction218983616, 12);
+ getSavePoints()->addData(kEntityServers0, kAction218586752, 13);
+ getSavePoints()->addData(kEntityServers0, kAction207330561, 14);
+ getSavePoints()->addData(kEntityServers0, kAction286403504, 16);
+ getSavePoints()->addData(kEntityServers0, kAction218128129, 17);
+ getSavePoints()->addData(kEntityServers0, kAction270068760, 18);
+ getSavePoints()->addData(kEntityServers0, kAction223712416, 2);
+ getSavePoints()->addData(kEntityServers0, kAction237485916, 5);
+ getSavePoints()->addData(kEntityServers0, kAction188893625, 8);
+ getSavePoints()->addData(kEntityServers0, kAction204704037, 6);
+ getSavePoints()->addData(kEntityServers0, kAction292758554, 7);
+ getSavePoints()->addData(kEntityServers0, kAction337548856, 9);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Servers0, function12)
+ handleServer(savepoint, "907", kEntityAnna, kAction268773672, &ENTITY_PARAM(0, 1));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Servers0, function13)
+ handleServer(savepoint, "911", kEntityAugust, kAction268773672, &ENTITY_PARAM(0, 2), "010F");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Servers0, function14)
+ handleServer(savepoint, "908", kEntityAnna, kAction170016384, &ENTITY_PARAM(0, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Servers0, function15)
+ handleServer(savepoint, "912", kEntityAugust, kAction170016384, &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Servers0, function16)
+ serveTable(savepoint, "907", kEntityTables0, "001N", "001P", "909", &ENTITY_PARAM(0, 6));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Servers0, function17)
+ serveTable(savepoint, "915", kEntityTables4, "014E", "014F", "917", &ENTITY_PARAM(1, 1), true, false, 67);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Servers0, function18)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010H", "913", &ENTITY_PARAM(0, 7));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Servers0, function19)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "913", &ENTITY_PARAM(0, 8), true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Servers0, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 2700);
+ ENTITY_PARAM(0, 4) = 1;
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM_PROC(params->param4, getState()->time, 4500)
+ ENTITY_PARAM(0, 5) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->isInKitchen(kEntityServers0) && !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ HANDLE_TABLE(0, 1, 1, setup_function12);
+ HANDLE_TABLE(0, 2, 2, setup_function13);
+ HANDLE_TABLE(0, 3, 3, setup_function7);
+ HANDLE_TABLE(0, 4, 4, setup_function14);
+ HANDLE_TABLE(0, 5, 5, setup_function15);
+ HANDLE_TABLE(0, 6, 6, setup_function16);
+ HANDLE_TABLE(1, 1, 7, setup_function17);
+ HANDLE_TABLE(0, 7, 8, setup_function18);
+ HANDLE_TABLE(0, 8, 9, setup_function19);
+ HANDLE_TABLE(1, 2, 10, setup_function8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityServers0, kEntityPascale, kAction352703104);
+ setup_function21();
+ break;
+
+ case 11:
+ case 12:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+
+ if (getCallback() == 11)
+ params->param2 = 1;
+ else
+ params->param1 = 1;
+ break;
+
+ case 13:
+ case 14:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+ break;
+ }
+ break;
+
+ case kAction136702400:
+ setCallback(savepoint.entity2 == kEntityAnna ? 13 : 14);
+ setup_draw(savepoint.entity2 == kEntityAnna ? "909" : "913");
+ break;
+
+ case kAction203859488:
+ setCallback(savepoint.entity2 == kEntityAnna ? 11 : 12);
+ setup_draw(savepoint.entity2 == kEntityAnna ? "910" : "913");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Servers0, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ break;
+
+ case kAction101632192:
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Servers0, function22)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityServers0);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Servers0, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Servers0, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers0) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ HANDLE_TABLE(1, 3, 1, setup_function25);
+ HANDLE_TABLE(1, 4, 2, setup_function26);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ HANDLE_TABLE(1, 4, 2, setup_function26);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Servers0, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("957");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction219522616:
+ setCallback(2);
+ setup_draw("959");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Servers0, function26)
+ serveTable(savepoint, "957", kEntityTables0, "016E", "016D", "959", &ENTITY_PARAM(1, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Servers0, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ ENTITY_PARAM(2, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Servers0, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(1);
+ setup_augustAnnaDateOrder();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(2, 4)) {
+ setCallback(3);
+ setup_function30();
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(2, 3)) {
+ setCallback(4);
+ setup_function10();
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(5);
+ setup_function7();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(6);
+ setup_function8();
+ break;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Servers0, augustAnnaDateOrder)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAnna, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "026D");
+
+ setCallback(2);
+ setup_playSound("Ann3138");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAnna, kAction122288808);
+
+ setCallback(3);
+ setup_draw("913");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 5) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Servers0, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("916");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "029D");
+
+ setCallback(2);
+ setup_playSound("Abb3016a");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122288808);
+
+ setCallback(3);
+ setup_draw("918");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(2, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Servers0, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Servers0, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 3600)
+ ENTITY_PARAM(1, 8) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(1);
+ setup_augustOrderSteak();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 8)) {
+ setCallback(2);
+ setup_augustServeDuck();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(2, 1)) {
+ setCallback(3);
+ setup_function35();
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(4);
+ setup_function9();
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(2, 3)) {
+ setCallback(5);
+ setup_function10();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(6);
+ setup_function7();
+ break;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 1;
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+ }
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ params->param1 = 0;
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Servers0, augustOrderSteak)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityServers0, "010F3");
+ getEntities()->drawSequenceLeft(kEntityAugust, "010D3");
+
+ setCallback(2);
+ setup_playSound("AUG4002");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction122288808);
+
+ setCallback(3);
+ setup_draw("913");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 7) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Servers0, augustServeDuck)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("912");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction122358304);
+ getSound()->playSound(kEntityServers0, "AUG1053");
+
+ setCallback(2);
+ setup_draw("010G3");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction201964801);
+
+ setCallback(3);
+ setup_draw("914");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 8) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Servers0, function35)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "914", &ENTITY_PARAM(2, 1), false, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Servers0, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Servers0, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(38, Servers0)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Servers0::handleServer(const SavePoint &savepoint, const char* name, EntityIndex entity, ActionIndex action, uint *parameter, const char* name2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw(name);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ // Prepare or draw sequences depending of value of string
+ if (strcmp(name2, ""))
+ getEntities()->clearSequences(kEntityServers0);
+ else
+ getEntities()->drawSequenceLeft(kEntityServers0, name2);
+
+ getSavePoints()->push(kEntityServers0, entity, action);
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Servers0::serveTable(const SavePoint &savepoint, const char* seq1, EntityIndex entity, const char* seq2, const char* seq3, const char* seq4, uint *parameter, bool shouldUpdatePosition, bool pushSavepoint, Position position) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (shouldUpdatePosition) {
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ }
+
+ setCallback(1);
+ setup_draw(seq1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (position)
+ getEntities()->updatePositionEnter(kEntityServers0, kCarRestaurant, position);
+
+ getSavePoints()->push(kEntityServers0, entity, kAction136455232);
+
+ setCallback(2);
+ setup_callSavepoint(seq2, entity, kActionDrawTablesWithChairs, seq3);
+ break;
+
+ case 2:
+ if (position)
+ getEntities()->updatePositionExit(kEntityServers0, kCarRestaurant, position);
+
+ setCallback(3);
+ setup_draw(seq4);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+
+ // Special case for functions 19 & 35
+ if (pushSavepoint)
+ getSavePoints()->push(kEntityServers0, kEntityRebecca, kAction224253538);
+
+ getEntities()->clearSequences(kEntityServers0);
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/servers0.h b/engines/lastexpress/entities/servers0.h
new file mode 100644
index 0000000000..61a060f0c2
--- /dev/null
+++ b/engines/lastexpress/entities/servers0.h
@@ -0,0 +1,173 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SERVERS0_H
+#define LASTEXPRESS_SERVERS0_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Servers0 : public Entity {
+public:
+ Servers0(LastExpressEngine *engine);
+ ~Servers0() {};
+
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION_NOSETUP(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ DECLARE_FUNCTION(function7)
+ DECLARE_FUNCTION(function8)
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(augustAnnaDateOrder)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(augustOrderSteak)
+ DECLARE_FUNCTION(augustServeDuck)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void handleServer(const SavePoint &savepoint, const char* name, EntityIndex entity, ActionIndex action, uint *parameter, const char* name2 = "");
+ void serveTable(const SavePoint &savepoint, const char* seq1, EntityIndex entity, const char* seq2, const char* seq3, const char* seq4, uint *parameter, bool shouldUpdatePosition = true, bool pushSavepoint = false, Position position = 0);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SERVERS0_H
diff --git a/engines/lastexpress/entities/servers1.cpp b/engines/lastexpress/entities/servers1.cpp
new file mode 100644
index 0000000000..ea383dbacb
--- /dev/null
+++ b/engines/lastexpress/entities/servers1.cpp
@@ -0,0 +1,787 @@
+/* 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/entities/servers1.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Servers1::Servers1(LastExpressEngine *engine) : Entity(engine, kEntityServers1) {
+ ADD_CALLBACK_FUNCTION(Servers1, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Servers1, draw);
+ ADD_CALLBACK_FUNCTION(Servers1, updatePosition);
+ ADD_CALLBACK_FUNCTION(Servers1, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Servers1, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Servers1, playSound);
+ ADD_CALLBACK_FUNCTION(Servers1, function7);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter1);
+ ADD_CALLBACK_FUNCTION(Servers1, function9);
+ ADD_CALLBACK_FUNCTION(Servers1, function10);
+ ADD_CALLBACK_FUNCTION(Servers1, function11);
+ ADD_CALLBACK_FUNCTION(Servers1, function12);
+ ADD_CALLBACK_FUNCTION(Servers1, function13);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function15);
+ ADD_CALLBACK_FUNCTION(Servers1, function16);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter2);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function19);
+ ADD_CALLBACK_FUNCTION(Servers1, function20);
+ ADD_CALLBACK_FUNCTION(Servers1, function21);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter3);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function24);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter4);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function27);
+ ADD_CALLBACK_FUNCTION(Servers1, function28);
+ ADD_CALLBACK_FUNCTION(Servers1, function29);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter5);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter5Handler);
+ ADD_NULL_FUNCTION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(1, Servers1, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Servers1, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(3, Servers1, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Servers1, callbackActionOnDirection)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityServers1);
+ params->param1 = 1;
+ }
+ }
+
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(5, Servers1, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Servers1, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Servers1, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction122358304);
+ setCallback(2);
+ setup_draw("008C");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction122288808);
+ setCallback(2);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(1, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Servers1, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityServers1, kAction223002560, 0);
+ getSavePoints()->addData(kEntityServers1, kAction302996448, 2);
+ getSavePoints()->addData(kEntityServers1, kAction269485588, 3);
+ getSavePoints()->addData(kEntityServers1, kAction326144276, 4);
+ getSavePoints()->addData(kEntityServers1, kAction302203328, 5);
+ getSavePoints()->addData(kEntityServers1, kAction189688608, 6);
+ getSavePoints()->addData(kEntityServers1, kAction236237423, 7);
+ getSavePoints()->addData(kEntityServers1, kAction219377792, 8);
+ getSavePoints()->addData(kEntityServers1, kAction256200848, 9);
+ getSavePoints()->addData(kEntityServers1, kAction291721418, 10);
+ getSavePoints()->addData(kEntityServers1, kAction258136010, 11);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Servers1, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMilos, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityServers1, "009B");
+
+ setCallback(2);
+ setup_playSound("WAT1001");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+
+ setCallback(3);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Servers1, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityServers1, "008C");
+
+ setCallback(2);
+ setup_playSound("MRB1077");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction168717392);
+
+ setCallback(3);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Servers1, function11)
+ serveTable(savepoint, "919", kEntityTables1, "005H", "005J", "921", &ENTITY_PARAM(0, 3), 63);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Servers1, function12)
+ serveTable(savepoint, "923", kEntityTables2, "009F", "009G", "926", &ENTITY_PARAM(0, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Servers1, function13)
+ serveTable(savepoint, "923", kEntityTables2, "009F", "009G", "926", &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Servers1, chapter1Handler)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(0, 1)) {
+ setCallback(1);
+ setup_function9();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(2);
+ setup_function10();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function11();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_function12();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(5);
+ setup_function13();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 5) {
+ getSavePoints()->push(kEntityServers1, kEntityPascale, kAction352768896);
+ setup_function15();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Servers1, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ break;
+
+ case kAction101632192:
+ setup_function16();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Servers1, function16)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityServers1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Servers1, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Servers1, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(1);
+ setup_function19();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(2);
+ setup_function20();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 8) || ENTITY_PARAM(0, 5)) {
+ setCallback(3);
+ setup_function21();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 4:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ break;
+ }
+ break;
+
+ case kAction101106391:
+ setCallback(4);
+ setup_draw("975");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Servers1, function19)
+ serveTable(savepoint, "969", kEntityTables1, "005H2", "018A", "971", &ENTITY_PARAM(0, 6), 63);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Servers1, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("973");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSavePoints()->push(kEntityServers1, kEntityIvo, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityServers1, "BLANK");
+ ENTITY_PARAM(0, 7) = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Servers1, function21)
+ serveTable(savepoint, "974", kEntityTables2, "009F2", "009G", "976", &ENTITY_PARAM(0, 8), 0, true, &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Servers1, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Servers1, chapter3Handler)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ return;
+
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(1);
+ setup_function24();
+ return;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(2);
+ setup_function7();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Servers1, function24)
+ serveSalon(savepoint, "927", "Ann3143A", kEntityAnna, "Ann31444", "112C", kAction122288808, "928", &ENTITY_PARAM(1, 1));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Servers1, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->clearSequences(kEntityServers1);
+
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Servers1, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 900)
+ ENTITY_PARAM(1, 5) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(2);
+ setup_function28();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(3);
+ setup_function29();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(4);
+ setup_function7();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 1;
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Servers1, function27)
+ serveSalon(savepoint, "929", "", kEntityAugust, "Aug4003", "122D", kAction134486752, "930", &ENTITY_PARAM(1, 3));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Servers1, function28)
+ serveSalon(savepoint, "931", "", kEntityAugust, "Aug4004", "122E", kAction125826561, "930", &ENTITY_PARAM(1, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Servers1, function29)
+ serveSalon(savepoint, "932", "", kEntityAnna, "Ann4151", "127D", kAction122288808, "930", &ENTITY_PARAM(1, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Servers1, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Servers1, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(32, Servers1)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Servers1::serveTable(const SavePoint &savepoint, const char* seq1, EntityIndex entity, const char* seq2, const char* seq3, const char* seq4, uint *parameter, Position position, bool shouldUpdatePosition, uint* parameter2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (shouldUpdatePosition) {
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ }
+
+ setCallback(1);
+ setup_draw(seq1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (position)
+ getEntities()->updatePositionEnter(kEntityServers1, kCarRestaurant, position);
+
+ getSavePoints()->push(kEntityServers1, entity, kAction136455232);
+
+ setCallback(2);
+ setup_callSavepoint(seq2, entity, kActionDrawTablesWithChairs, seq3);
+ break;
+
+ case 2:
+ if (position)
+ getEntities()->updatePositionExit(kEntityServers1, kCarRestaurant, position);
+
+ setCallback(3);
+ setup_draw(seq4);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers1);
+ *parameter = 0;
+
+ if (parameter2 != NULL)
+ *parameter2 = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Servers1::serveSalon(const SavePoint &savepoint, const char* seq1, const char* snd1, EntityIndex entity, const char* snd2, const char* seq2, ActionIndex action, const char* seq3, uint *parameter) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("816DD");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityServers1, seq1);
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityServers1);
+
+ if (!strcmp(snd1, ""))
+ getSound()->playSound(kEntityServers1, snd1);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, entity, kAction122358304);
+
+ getSound()->playSound(kEntityServers1, snd2);
+
+ setCallback(3);
+ setup_updatePosition(seq2, kCarRestaurant, 57);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityServers1, entity, action);
+
+ setCallback(4);
+ setup_draw(seq3);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityServers1, "816UD");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityServers1);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/servers1.h b/engines/lastexpress/entities/servers1.h
new file mode 100644
index 0000000000..f05d994d21
--- /dev/null
+++ b/engines/lastexpress/entities/servers1.h
@@ -0,0 +1,167 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SERVERS1_H
+#define LASTEXPRESS_SERVERS1_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Servers1 : public Entity {
+public:
+ Servers1(LastExpressEngine *engine);
+ ~Servers1() {};
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence, CarIndex car, Position position)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function24)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void serveTable(const SavePoint &savepoint, const char* seq1, EntityIndex entity, const char* seq2, const char* seq3, const char* seq4, uint *parameter, Position position = 0, bool updatePosition = true, uint *parameter2 = NULL);
+ void serveSalon(const SavePoint &savepoint, const char* seq1, const char* snd1, EntityIndex entity, const char* snd2, const char* seq2, ActionIndex action, const char* seq3, uint *parameter);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SERVERS1_H
diff --git a/engines/lastexpress/entities/sophie.cpp b/engines/lastexpress/entities/sophie.cpp
new file mode 100644
index 0000000000..bab74ad1f2
--- /dev/null
+++ b/engines/lastexpress/entities/sophie.cpp
@@ -0,0 +1,279 @@
+/* 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/entities/sophie.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+#define CHAPTER_IMPLEMENTATION() \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionNone: \
+ setup_chaptersHandler(); \
+ break; \
+ case kActionDefault: \
+ getEntities()->clearSequences(kEntitySophie); \
+ getData()->entityPosition = kPosition_4840; \
+ getData()->location = kLocationInsideCompartment; \
+ getData()->car = kCarRedSleeping; \
+ getData()->clothes = kClothesDefault; \
+ getData()->inventoryItem = kItemNone; \
+ break; \
+ }
+
+#define DEFAULT_ACTION_IMPLEMENTATION() \
+ if (savepoint.action == kActionDefault) { \
+ getData()->entityPosition = kPosition_4840; \
+ getData()->location = kLocationInsideCompartment; \
+ getData()->car = kCarRedSleeping; \
+ getEntities()->clearSequences(kEntitySophie); \
+ }
+
+Sophie::Sophie(LastExpressEngine *engine) : Entity(engine, kEntitySophie) {
+ ADD_CALLBACK_FUNCTION(Sophie, reset);
+ ADD_CALLBACK_FUNCTION(Sophie, updateEntity);
+ ADD_CALLBACK_FUNCTION(Sophie, chaptersHandler);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter1);
+ ADD_CALLBACK_FUNCTION(Sophie, function5);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter2);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter3);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter4);
+ ADD_CALLBACK_FUNCTION(Sophie, function9);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter5);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Sophie, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(2, Sophie, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ params->param3 = 0;
+
+ // Sophie
+ byte direction = getData()->direction;
+ EntityPosition position = getData()->entityPosition;
+ CarIndex car = getData()->car;
+
+ // Rebecca
+ EntityPosition rebecca_position = getEntityData(kEntityRebecca)->entityPosition;
+ CarIndex rebeccaCar = getEntityData(kEntityRebecca)->car;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntitySophie, kEntityRebecca, 500)
+ || (direction == kDirectionUp && car >= rebeccaCar && position > rebecca_position)
+ || (direction == kDirectionDown && car <= rebeccaCar && position < rebecca_position)) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntitySophie, (CarIndex)params->param1, (EntityPosition)params->param2);
+
+ break;
+ }
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntitySophie);
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntitySophie, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Sophie, chaptersHandler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityRebecca)->entityPosition;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntitySophie, "BLANK");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntitySophie, "BLANK");
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition - 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kAction136654208:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition + 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kAction259921280:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition + 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(3);
+ setup_updateEntity(kCarKronos, kPosition_9460);
+ break;
+
+ case kAction292775040:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Sophie, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chaptersHandler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Sophie, function5)
+ DEFAULT_ACTION_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Sophie, chapter2)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Sophie, chapter3)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Sophie, chapter4)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Sophie, function9)
+ DEFAULT_ACTION_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Sophie, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySophie);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Sophie, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(12, Sophie)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/sophie.h b/engines/lastexpress/entities/sophie.h
new file mode 100644
index 0000000000..51d3d084b6
--- /dev/null
+++ b/engines/lastexpress/entities/sophie.h
@@ -0,0 +1,98 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SOPHIE_H
+#define LASTEXPRESS_SOPHIE_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Sophie : public Entity {
+public:
+ Sophie(LastExpressEngine *engine);
+ ~Sophie() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handle chapters events
+ */
+ DECLARE_FUNCTION(chaptersHandler)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function5)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SOPHIE_H
diff --git a/engines/lastexpress/entities/tables.cpp b/engines/lastexpress/entities/tables.cpp
new file mode 100644
index 0000000000..eca60a536b
--- /dev/null
+++ b/engines/lastexpress/entities/tables.cpp
@@ -0,0 +1,221 @@
+/* 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/entities/tables.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Tables::Tables(LastExpressEngine *engine, EntityIndex id) : Entity(engine, id) {
+ _id = id;
+
+ ADD_CALLBACK_FUNCTION(Tables, chapter1);
+ ADD_CALLBACK_FUNCTION(Tables, chapter2);
+ ADD_CALLBACK_FUNCTION(Tables, chapter3);
+ ADD_CALLBACK_FUNCTION(Tables, chapter4);
+ ADD_CALLBACK_FUNCTION(Tables, chapter5);
+ ADD_CALLBACK_FUNCTION(Tables, draw);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Tables, chapter1)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Tables, chapter2)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Tables, chapter3)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Tables, chapter4)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Tables, chapter5)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2 && getSound()->isBuffered(kEntityTables2))
+ getSound()->processEntry(kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Tables, draw)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Only applicable to Tables2 entity
+ if (_id != kEntityTables2)
+ break;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ if (getState()->time > kTime1165500 && !params->param1) {
+ params->param1 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ case kChapter3:
+ if (getState()->time > kTime2052000 && !params->param2) {
+ params->param2 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ case kChapter4:
+ if (getState()->time > kTime2488500 && !params->param3) {
+ params->param3 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ }
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ switch(_id) {
+ default:
+ break;
+
+ case kEntityTables0:
+ getData()->entityPosition = kPosition_3970;
+ getEntities()->drawSequenceLeft(_id, "001P");
+ break;
+
+ case kEntityTables1:
+ getData()->entityPosition = kPosition_3970;
+ getEntities()->drawSequenceLeft(_id, "005J");
+ break;
+
+ case kEntityTables2:
+ getData()->entityPosition = kPosition_4690;
+ getEntities()->drawSequenceLeft(_id, "009G");
+ break;
+
+ case kEntityTables3:
+ getData()->entityPosition = kPosition_4690;
+ getEntities()->drawSequenceLeft(_id, "010M");
+ break;
+
+ case kEntityTables4:
+ getData()->entityPosition = kPosition_5420;
+ getEntities()->drawSequenceLeft(_id, "014F");
+ break;
+
+ case kEntityTables5:
+ getData()->entityPosition = kPosition_5420;
+ getEntities()->drawSequenceLeft(_id, "024D");
+ break;
+ }
+
+ break;
+
+ case kActionDrawTablesWithChairs:
+ if (!strcmp(savepoint.param.charValue, "")) {
+ getEntities()->drawSequenceLeft(_id, savepoint.param.charValue);
+ } else {
+ switch(_id) {
+ default:
+ break;
+
+ case kEntityTables0:
+ getEntities()->drawSequenceLeft(_id, "001P");
+ break;
+
+ case kEntityTables1:
+ getEntities()->drawSequenceLeft(_id, "005J");
+ break;
+
+ case kEntityTables2:
+ getEntities()->drawSequenceLeft(_id, "009G");
+ break;
+
+ case kEntityTables3:
+ getEntities()->drawSequenceLeft(_id, "010M");
+ break;
+
+ case kEntityTables4:
+ getEntities()->drawSequenceLeft(_id, "014F");
+ break;
+
+ case kEntityTables5:
+ getEntities()->drawSequenceLeft(_id, "024D");
+ break;
+ }
+ }
+ break;
+
+ case kAction136455232:
+ getEntities()->drawSequenceLeft(_id, "BLANK");
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/tables.h b/engines/lastexpress/entities/tables.h
new file mode 100644
index 0000000000..e7f51da66a
--- /dev/null
+++ b/engines/lastexpress/entities/tables.h
@@ -0,0 +1,77 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_TABLES_H
+#define LASTEXPRESS_TABLES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Tables : public Entity {
+public:
+ Tables(LastExpressEngine *engine, EntityIndex id);
+ ~Tables() {};
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Draws tables
+ */
+ DECLARE_FUNCTION(draw)
+
+private:
+ EntityIndex _id; ///< Table entity id
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TABLES_H
diff --git a/engines/lastexpress/entities/tatiana.cpp b/engines/lastexpress/entities/tatiana.cpp
new file mode 100644
index 0000000000..14567f8afa
--- /dev/null
+++ b/engines/lastexpress/entities/tatiana.cpp
@@ -0,0 +1,1711 @@
+/* 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/entities/tatiana.h"
+
+#include "lastexpress/entities/alexei.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Tatiana::Tatiana(LastExpressEngine *engine) : Entity(engine, kEntityTatiana) {
+ ADD_CALLBACK_FUNCTION(Tatiana, reset);
+ ADD_CALLBACK_FUNCTION(Tatiana, playSound);
+ ADD_CALLBACK_FUNCTION(Tatiana, draw);
+ ADD_CALLBACK_FUNCTION(Tatiana, updatePosition);
+ ADD_CALLBACK_FUNCTION(Tatiana, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Tatiana, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Tatiana, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Tatiana, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Tatiana, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Tatiana, savegame);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateEntity);
+ ADD_CALLBACK_FUNCTION(Tatiana, function14);
+ ADD_CALLBACK_FUNCTION(Tatiana, function15);
+ ADD_CALLBACK_FUNCTION(Tatiana, function16);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter1);
+ ADD_CALLBACK_FUNCTION(Tatiana, function18);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function20);
+ ADD_CALLBACK_FUNCTION(Tatiana, function21);
+ ADD_CALLBACK_FUNCTION(Tatiana, function22);
+ ADD_CALLBACK_FUNCTION(Tatiana, function23);
+ ADD_CALLBACK_FUNCTION(Tatiana, function24);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter2);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function27);
+ ADD_CALLBACK_FUNCTION(Tatiana, function28);
+ ADD_CALLBACK_FUNCTION(Tatiana, function29);
+ ADD_CALLBACK_FUNCTION(Tatiana, function30);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter3);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function33);
+ ADD_CALLBACK_FUNCTION(Tatiana, function34);
+ ADD_CALLBACK_FUNCTION(Tatiana, function35);
+ ADD_CALLBACK_FUNCTION(Tatiana, function36);
+ ADD_CALLBACK_FUNCTION(Tatiana, function37);
+ ADD_CALLBACK_FUNCTION(Tatiana, function38);
+ ADD_CALLBACK_FUNCTION(Tatiana, function39);
+ ADD_CALLBACK_FUNCTION(Tatiana, function40);
+ ADD_CALLBACK_FUNCTION(Tatiana, function41);
+ ADD_CALLBACK_FUNCTION(Tatiana, function42);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter4);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function45);
+ ADD_CALLBACK_FUNCTION(Tatiana, function46);
+ ADD_CALLBACK_FUNCTION(Tatiana, function47);
+ ADD_CALLBACK_FUNCTION(Tatiana, function48);
+ ADD_CALLBACK_FUNCTION(Tatiana, function49);
+ ADD_CALLBACK_FUNCTION(Tatiana, function50);
+ ADD_CALLBACK_FUNCTION(Tatiana, function51);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter5);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function54);
+ ADD_CALLBACK_FUNCTION(Tatiana, function55);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Tatiana, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Tatiana, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Tatiana, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(4, Tatiana, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, Tatiana, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Tatiana, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_7500, kPosition_7850, kCarRedSleeping, kObjectCompartmentB);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(7, Tatiana, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Tatiana, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(9, Tatiana, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(10, Tatiana, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Tatiana, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(12, Tatiana, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Tatiana, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventTatianaAskMatchSpeakRussian) || getEvent(kEventTatianaAskMatch) || getEvent(kEventVassiliSeizure)) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1010" : "CAT1010A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Tatiana, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction326348944);
+ getEntities()->drawSequenceLeft(kEntityTatiana, getProgress().chapter == kChapter1 ? "603Fb" : "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction69239528:
+ setCallback(getProgress().chapter == kChapter1 ? 1 : 2);
+ setup_enterExitCompartment2(getProgress().chapter == kChapter1 ? "603Db" : "673Db", kObjectCompartmentB);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Tatiana, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getProgress().chapter == kChapter1 ? 1 : 2);
+ setup_enterExitCompartment2(getProgress().chapter == kChapter1 ? "603Bb" : "673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction292048641);
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, getProgress().chapter == kChapter1 ? "603Fb" : "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ }
+ break;
+
+ case kAction69239528:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Tatiana, function16, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param4) {
+ params->param4 = 1;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param2) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.param.intValue == 49) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1512" : getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ } else {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "TAT1133A" : "TAT1133B");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Tatiana, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityTatiana, kAction191198209, 0);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_5419;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Tatiana, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+
+ if (getState()->time > kTime1143000 && !params->param2) {
+ params->param2 = 1;
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 4500)
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (getData()->entityPosition <= kPosition_2330) {
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction157159392);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction188784532);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ } else {
+ getEntities()->clearSequences(kEntityTatiana);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ getEntities()->updateFrame(kEntityTatiana);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Tatiana, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getSound()->isBuffered(kEntityTatiana) || !params->param4 || params->param3 == 2 || getSound()->isBuffered("TAT1066"))
+ goto label_tatiana_chapter1_2;
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 450)
+ getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A");
+ getProgress().field_64 = 1;
+ params->param3++;
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 71)) {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75)
+ getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A");
+ getProgress().field_64 = 1;
+ params->param3++;
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_tatiana_chapter1_2:
+ TIME_CHECK_SAVEPOINT(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762);
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ } else {
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityTables4, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityTatiana, "014A");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 67) ? 1 : 0;
+ params->param4 = getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71);
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "014A");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "BLANK");
+ break;
+
+ case kAction124973510:
+ setup_function20();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Tatiana, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityTatiana, kEntityAugust, kAction223183000);
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarRestaurant, 67);
+ getSound()->playSound(kEntityTatiana, "TAT1070");
+
+ setCallback(2);
+ setup_callSavepoint("014C", kEntityTables4, kActionDrawTablesWithChairs, "014D");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityTatiana, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityTatiana, kEntityServers0, kAction188893625);
+
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityTatiana, kEntityAugust, kAction268620864);
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Tatiana, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes1;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8513);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->clothes = kClothesDefault;
+
+ getSound()->playSound(kEntityTatiana, "TAT1071");
+ getEntities()->drawSequenceRight(kEntityTatiana, "604Aa");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentA);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentA, true);
+ }
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentA);
+
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityTatiana);
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction135854208);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ // Fallback to next case
+
+ case 3:
+ if (getSound()->isBuffered(kEntityTatiana)) {
+ setCallback(3);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(4);
+ setup_playSound("TAT1071A");
+ }
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_7500;
+
+ getSavePoints()->push(kEntityTatiana, kEntityVassili, kAction168459827);
+
+ setCallback(5);
+ setup_function16(kTime1156500);
+ break;
+
+ case 5:
+ case 6:
+ if (getProgress().field_14 == 29) {
+ setCallback(6);
+ setup_function16(getState()->time + 900);
+ } else {
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_function22();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Tatiana, function22)
+ error("Tatiana: callback function 22 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Tatiana, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Tatiana, function24)
+ if (savepoint.action == kActionDefault) {
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 71);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Tatiana, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_5420;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Tatiana, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1800000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function28();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setup_function28();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "024A");
+ getSavePoints()->push(kEntityTatiana, kEntityTables5, kAction136455232);
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 64) || getEntities()->isPlayerPosition(kCarRestaurant, 65)) {
+ getData()->inventoryItem = kItemNone;
+ setup_function27();
+ }
+ break;
+
+ case kAction290869168:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Tatiana, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getEvent(kEventTatianaGivePoem) ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, getEvent(kEventTatianaGivePoem) ? kEventTatianaBreakfastAlexei : kEventTatianaBreakfast);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function30);
+ getAction()->playAnimation(kEventTatianaBreakfastAlexei);
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->setLocationAndProcess(kItem11, kObjectLocation1);
+ setup_function28();
+ break;
+
+ case 2:
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function30);
+ getAction()->playAnimation(kEventTatianaBreakfast);
+ if (getInventory()->hasItem(kItemParchemin)) {
+ getAction()->playAnimation(kEventTatianaBreakfastGivePoem);
+ getInventory()->removeItem(kItemParchemin);
+ } else {
+ getAction()->playAnimation(kEventTatianaAlexei);
+ }
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Tatiana, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityTatiana, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction236053296, (getEvent(kEventTatianaBreakfastAlexei) || getEvent(kEventTatianaBreakfast)) ? 69 : 0);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function29();
+ break;
+
+ case kAction123857088:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "018G");
+
+ setCallback(1);
+ setup_updateFromTime(1800);
+ break;
+
+ case kAction156444784:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Tatiana, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarRestaurant, 63);
+
+ setCallback(2);
+ setup_callSavepoint("018H", kEntityTables1, kActionDrawTablesWithChairs, "018A");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityTatiana, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityTatiana, kEntityServers1, kAction302203328);
+ getEntities()->drawSequenceRight(kEntityTatiana, "805DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityTatiana);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Tatiana, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(kTimeEnd);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Tatiana, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_1750;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ // Update inventory
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+
+ if (getEvent(kEventTatianaBreakfastGivePoem) || (getEvent(kEventTatianaGivePoem) && !getEvent(kEventTatianaBreakfastAlexei)))
+ getInventory()->get(kItemParchemin)->location = kObjectLocation2;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Tatiana, chapter3Handler)
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+ EntityData::EntityParametersSIII *parameters1 = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!parameters->param2 && !parameters->param5) {
+ parameters->param1 -= getState()->timeDelta;
+
+ if (getState()->timeDelta > parameters->param1) {
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, (char *)&parameters1->seq);
+ getSound()->playSound(kEntityTatiana, (char *)&parameters->seq);
+
+ if (parameters->param3 == 4 && getEntities()->isInSalon(kEntityPlayer))
+ getProgress().field_90 = 1;
+
+ parameters->param2 = 1;
+ }
+ }
+
+ if (parameters->param4 && parameters->param5) {
+ UPDATE_PARAM_CHECK(parameters->param4, getState()->time, 6300)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("110E", kCarRestaurant, 52);
+ }
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ parameters->param2 = 0;
+ ++parameters->param3;
+
+ switch (parameters->param3) {
+ default:
+ parameters->param5 = 1;
+ break;
+
+ case 1:
+ parameters->param1 = 900;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110A");
+ strcpy((char *)&parameters->seq, "Tat3160B");
+ strcpy((char *)&parameters1->seq, "110A");
+ break;
+
+ case 2:
+ parameters->param1 = 9000;
+ strcpy((char *)&parameters->seq, "Tat3160C");
+ strcpy((char *)&parameters1->seq, "110C");
+ break;
+
+ case 3:
+ parameters->param1 = 13500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160D");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 4:
+ parameters->param1 = 9000;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160E");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 5:
+ parameters->param1 = 4500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160G");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 6:
+ parameters->param1 = 4500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160B");
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction122358304);
+ getSavePoints()->push(kEntityTatiana, kEntityKronos, kAction157159392);
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110C");
+ getSound()->playSound(kEntityTatiana, "Tat3160A");
+
+ parameters->param2 = 1;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction122288808);
+ setup_function33();
+ }
+ break;
+
+ case kAction101169422:
+ parameters->param4 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Tatiana, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+ setCallback(1);
+ setup_updateFromTime(75);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function14();
+ break;
+
+ case 3:
+ setup_function34();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Tatiana, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2097000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation1;
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850))
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ setup_function15();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 3:
+ setup_function35();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Tatiana, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1
+ && getInventory()->hasItem(kItemFirebird)
+ && getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && (getState()->time < kTime2133000 || getProgress().field_40)) {
+ setCallback(1);
+ setup_function41();
+ break;
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2133000) {
+ if (getData()->car >= kCarRedSleeping || (getData()->car == kCarGreenSleeping && getData()->entityPosition > kPosition_5790))
+ setup_function36();
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ params->param1 = 1;
+ goto label_callback_1;
+ }
+ break;
+
+ case kAction191668032:
+ setup_function36();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Tatiana, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850) || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ setCallback(2);
+ setup_function14();
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ break;
+
+ case 2:
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Tatiana, function37)
+ error("Tatiana: callback function 37 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Tatiana, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 450);
+
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true);
+
+ setCallback(4);
+ setup_function42(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes3;
+
+ setCallback(1);
+ setup_enterExitCompartment("673Jb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_function42(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Gf");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentF, true);
+
+ setCallback(3);
+ setup_playSound("Tat3164");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityTatiana, kEntityAnna, kAction236241630);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function39();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true);
+ getEntities()->clearSequences(kEntityTatiana);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(7);
+ setup_playSound("ANN3011");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(900);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("673Jf", kObjectCompartmentF);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_function42(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityTatiana, kEntityAnna, kAction236517970);
+
+ setCallback(11);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 11:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function39();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ setCallback(6);
+ setup_enterExitCompartment("673Df", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Tatiana, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntities()->isDistanceBetweenEntities(kEntityTatiana, kEntityPlayer, 1000)) {
+ params->param1 = 1;
+ getSound()->playSound(kEntityTatiana, "Tat3164"); // Tatiana weeping
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Tatiana, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)
+ || getData()->car != getEntityData(kEntityPlayer)->car
+ || getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMe:
+ if (getEvent(kEventTatianaAskMatchSpeakRussian) || getEvent(kEventTatianaAskMatch) || getEvent(kEventVassiliSeizure))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1001A" : "CAT1010");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Tatiana, function41)
+ error("Tatiana: callback function 41 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(42, Tatiana, function42, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath || savepoint.action == kActionExcuseMe) {
+ getSound()->playSound(kEntityPlayer, "Tat3124", getSound()->getSoundFlag(kEntityTatiana));
+ return;
+ }
+
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Tatiana, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 1) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Tatiana, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2362500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Tatiana, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction123712592);
+ setup_function46();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Tatiana, function46)
+ error("Tatiana: callback function 46 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Tatiana, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setCallback(3);
+ setup_function16(kTime2407500);
+ break;
+
+ case 3:
+ case 4:
+ if (ENTITY_PARAM(0, 1) && getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) {
+ setup_function48();
+ } else {
+ setCallback(4);
+ setup_function16(900);
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Tatiana, function48)
+ error("Tatiana: callback function 48 not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Tatiana, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction169360385:
+ setup_function50();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Tatiana, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2520000 && !params->param1) {
+ params->param1 = 1;
+ setup_function51();
+ }
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityTatiana, "Tat4166");
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliDeadAlexei);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityTatiana, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentA, kEntityTatiana, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ if (!getSound()->isBuffered(kEntityTatiana))
+ getSound()->playSound(kEntityTatiana, "Tat4166");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getSound()->isBuffered("MUS013"))
+ getSound()->processEntry("MUS013");
+
+ getAction()->playAnimation(kEventVassiliDeadAlexei);
+ getSavePoints()->push(kEntityTatiana, kEntityAbbot, kAction104060776);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 38);
+
+ setup_function51();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Tatiana, function51)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Tatiana, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Tatiana, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function54();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Tatiana, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ switch (params->param1) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityTatiana, "Tat5167A");
+ params->param2 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTatiana, "Tat5167B");
+ params->param2 = 1;
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTatiana, "Tat5167C");
+ params->param2 = 1;
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTatiana, "Tat5167D");
+ params->param2 = 1;
+ break;
+ }
+ }
+
+ if (params->param1 > 3) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 225);
+
+ params->param1 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaVassiliTalk);
+ break;
+
+ case kActionEndSound:
+ ++params->param1;
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "033A");
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ if (getSound()->isBuffered(kEntityTatiana))
+ getSound()->processEntry(kEntityTatiana);
+
+ getAction()->playAnimation(isNight() ? kEventTatianaVassiliTalkNight : kEventTatianaVassiliTalk);
+ getScenes()->processScene();
+
+ params->param1 = 4;
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction203078272:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "033E");
+ break;
+
+ case kAction236060709:
+ getData()->inventoryItem = kItemNone;
+ setup_function55();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Tatiana, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+ // fall back to next action
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 72))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 86);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/tatiana.h b/engines/lastexpress/entities/tatiana.h
new file mode 100644
index 0000000000..8dc5de9b35
--- /dev/null
+++ b/engines/lastexpress/entities/tatiana.h
@@ -0,0 +1,235 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_TATIANA_H
+#define LASTEXPRESS_TATIANA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Tatiana : public Entity {
+public:
+ Tatiana(LastExpressEngine *engine);
+ ~Tatiana() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char* sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char* sequence1, EntityIndex entity, ActionIndex action, const char* sequence2)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION_1(function16, uint32)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * ???
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function42, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TATIANA_H
diff --git a/engines/lastexpress/entities/train.cpp b/engines/lastexpress/entities/train.cpp
new file mode 100644
index 0000000000..53f19b1a30
--- /dev/null
+++ b/engines/lastexpress/entities/train.cpp
@@ -0,0 +1,575 @@
+/* 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/entities/train.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Train::Train(LastExpressEngine *engine) : Entity(engine, kEntityTrain) {
+ ADD_CALLBACK_FUNCTION(Train, savegame);
+ ADD_CALLBACK_FUNCTION(Train, chapter1);
+ ADD_CALLBACK_FUNCTION(Train, chapter2);
+ ADD_CALLBACK_FUNCTION(Train, chapter3);
+ ADD_CALLBACK_FUNCTION(Train, chapter4);
+ ADD_CALLBACK_FUNCTION(Train, chapter5);
+ ADD_CALLBACK_FUNCTION(Train, harem);
+ ADD_CALLBACK_FUNCTION(Train, process);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(1, Train, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Train, chapter1)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Train, chapter2)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Train, chapter3)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Train, chapter4)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Train, chapter5)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Train, harem, ObjectIndex, uint32)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ switch (params->param1) {
+ default:
+ error("Train::harem: Invalid value for parameter 1: %d", params->param1);
+ break;
+
+ case kObjectCompartment5:
+ params->param3 = kPosition_4840;
+ break;
+
+ case kObjectCompartment6:
+ params->param3 = kPosition_4070;
+ break;
+
+ case kObjectCompartment7:
+ params->param3 = kPosition_3050;
+ break;
+
+ case kObjectCompartment8:
+ params->param3 = kPosition_2740;
+ break;
+ }
+
+ params->param4 = getEntities()->isInsideCompartment(kEntityAlouan, kCarGreenSleeping, (EntityPosition)params->param3);
+ params->param5 = (ENTITY_PARAM(0, 7) - params->param3) < 1 ? true : false;
+ params->param6 = getEntities()->isInsideCompartment(kEntityYasmin, kCarGreenSleeping, (EntityPosition)params->param3);
+ params->param7 = getEntities()->isInsideCompartment(kEntityHadija, kCarGreenSleeping, (EntityPosition)params->param3);
+
+ getObjects()->update((ObjectIndex)params->param1, kEntityTrain, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ // Knock / closed door sound
+ getSound()->playSound(kEntityTables5, (params->param2 == 8) ? "LIB012" : "LIB013", SoundManager::kFlagDefault);
+
+ if (params->param4 && params->param5) {
+
+ ENTITY_PARAM(0, 5)++;
+
+ switch (ENTITY_PARAM(0, 5)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ getSound()->playSound(kEntityTables5, "Har1016", SoundManager::kFlagDefault, 150);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTables5, "Har1015A", SoundManager::kFlagDefault, 15);
+ getSound()->playSound(kEntityTables5, "Har1015", SoundManager::kFlagDefault, 150);
+ break;
+ }
+
+ // Update progress
+ getProgress().field_DC = 1;
+ getProgress().field_E0 = 1;
+
+ handleCompartmentAction();
+
+ // Done with it!
+ return;
+ }
+
+ if (params->param6 && params->param7) {
+
+ ENTITY_PARAM(0, 6)++;
+
+ switch(ENTITY_PARAM(0, 6)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTables5, "Har1013A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ handleCompartmentAction();
+ return;
+ }
+
+ if (!params->param5) {
+
+ if (params->param6) {
+ ENTITY_PARAM(0, 3)++;
+
+ switch(ENTITY_PARAM(0, 3)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1012", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1012A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ handleCompartmentAction();
+ return;
+ } else {
+
+ if (params->param4) {
+ ENTITY_PARAM(0, 1)++;
+
+ if (ENTITY_PARAM(0, 1) <= 1)
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ else
+ params->param8 = 1;
+
+ getProgress().field_DC = 1;
+
+ handleCompartmentAction();
+ return;
+ }
+
+ if (params->param7) {
+ ENTITY_PARAM(0, 4)++;
+
+ if (ENTITY_PARAM(0, 4) <= 1) {
+ getSound()->playSound(kEntityTables5, "Har1011", SoundManager::kFlagDefault, 15);
+ handleCompartmentAction();
+ return;
+ }
+ }
+ }
+
+ params->param8 = 1;
+ handleCompartmentAction();
+ return;
+ }
+
+ ENTITY_PARAM(0, 2) += 1;
+
+ switch (ENTITY_PARAM(0, 2)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ getProgress().field_E0 = 1;
+
+ handleCompartmentAction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Train, process)
+ EntityData::EntityParametersIIIS *params1 = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Play smoke animation
+ if ((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && params->param4 && !params->param5) {
+
+ params->param4 -= 1;
+
+ if (!params->param4 && getProgress().jacket == kJacketGreen) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+ params->param5 = 1;
+ getScenes()->processScene();
+ }
+ }
+
+ if (params->param6) {
+ UPDATE_PARAM_GOTO(params1->param7, getState()->time, 900, label_process);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 58);
+ }
+
+ params1->param7 = 0;
+
+label_process:
+ if (params->param7) {
+ if (!params1->param8) {
+ params1->param8 = getState()->time + 4500;
+
+ if (!params1->param8)
+ params->param7 = 0;
+ }
+
+ if (params1->param8 && params1->param8 < getState()->time) {
+ params->param7 = 0;
+ params1->param8 = 0;
+ }
+ }
+
+ // Update object
+ if (ENTITY_PARAM(0, 8) && !getSound()->isBuffered(kEntityTables5)) {
+ getObjects()->update((ObjectIndex)ENTITY_PARAM(0, 8), getObjects()->get((ObjectIndex)ENTITY_PARAM(0, 8)).entity, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ ENTITY_PARAM(0, 8) = 0;
+ }
+
+ // Play clock sound
+ if (params->param6 && !getSound()->isBuffered("ZFX1001", true))
+ getSound()->playSound(kEntityPlayer, "ZFX1001");
+
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor: {
+ // Handle opening harem compartments
+ ObjectIndex compartment = (ObjectIndex)savepoint.param.intValue;
+ if (compartment == kObjectCompartment5 || compartment == kObjectCompartment6 || compartment == kObjectCompartment7 || compartment == kObjectCompartment8) {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_harem(compartment, savepoint.action);
+ }
+ break;
+ }
+
+ case kActionDefault:
+ params->param3 = 1;
+ if (getProgress().chapter < kChapter5) {
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+ getData()->entityPosition = kPosition_30000;
+ break;
+
+ case kActionDrawScene:
+ getData()->car = getEntityData(kEntityPlayer)->car;
+
+ // Play clock sound
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 81)) {
+ params->param6 = 1;
+ if (!getSound()->isBuffered("ZFX1001"))
+ getSound()->playSound(kEntityPlayer, "ZFX1001");
+ } else {
+ params->param6 = 0;
+ if (getSound()->isBuffered("ZFX1001", true))
+ getSound()->removeFromQueue("ZFX1001");
+ }
+
+ // Draw moving background behind windows
+ if (params->param3) {
+ if (getEntityData(kEntityPlayer)->car != (CarIndex)params->param1 || isNight() != (bool)(params->param2)) {
+ switch (getEntityData(kEntityPlayer)->car) {
+ default:
+ getEntities()->clearSequences(kEntityTrain);
+ break;
+
+ case kCarBaggageRear:
+ case kCarBaggage:
+ if (getProgress().isNightTime)
+ getEntities()->drawSequenceLeft(kEntityTrain, "B1WNM");
+ else
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "B1WNN" : "B1WND");
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ if (getProgress().isNightTime)
+ getEntities()->drawSequenceLeft(kEntityTrain, "S1WNM");
+ else
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "S1WNN" : "S1WND");
+ break;
+
+ case kCarRestaurant:
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "RCWNN" : "RCWND");
+ break;
+ }
+
+ // Set parameters so we do not get called twice
+ params->param1 = getEntityData(kEntityPlayer)->car;
+ params->param2 = isNight();
+ }
+ }
+
+ if (!params->param5) {
+ params->param4 = 2700; // this is the sound file name
+ params->param5 = 0;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 22)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+ }
+
+ resetParam8();
+ break;
+
+
+ case kActionCallback: {
+ int action = getCallback();
+ switch(action) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getAction()->playAnimation(action == 1 ? kEventCoudertBloodJacket : kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ resetParam8();
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventLocomotiveConductorsDiscovered);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice2, true);
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventCathBreakCeiling);
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getScenes()->processScene();
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventCathJumpDownCeiling);
+ getScenes()->loadSceneFromPosition(kCarKronos, 89);
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventCloseMatchbox);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+ }
+ break;
+ }
+
+ case kAction191070912:
+ ENTITY_PARAM(0, 7) = savepoint.param.intValue;
+ break;
+
+ case kActionTrainStopRunning:
+ params->param3 = 0;
+ getEntities()->clearSequences(kEntityTrain);
+ break;
+
+ case kActionCatchBeetle:
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventCloseMatchbox);
+ break;
+
+ case kAction203339360:
+ if (params->param7) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveConductorsDiscovered);
+ } else {
+ params->param7 = 1;
+ getAction()->playAnimation(kEventLocomotiveConductorsLook);
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 2);
+ }
+ break;
+
+ case kActionTrainStartRunning:
+ if (!params->param3) {
+ params->param1 = 0;
+ params->param3 = 1;
+ getSavePoints()->push(kEntityTrain, kEntityTrain, kActionDrawScene);
+ }
+ break;
+
+ case kAction203863200:
+ if (!strcmp(savepoint.param.charValue, "")) {
+ params->param8 = 1;
+ strcpy((char *)&params1->seq, savepoint.param.charValue); // this is the sound file name
+ }
+ break;
+
+ case kAction222746496:
+ switch(savepoint.param.intValue) {
+ default:
+ break;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartmentA) ? kPosition_8200 : kPosition_7500;
+ params1->param3 = kPosition_7850;
+ break;
+
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment3 || savepoint.param.intValue == kObjectCompartmentC) ? kPosition_6470 : kPosition_5790;
+ params1->param3 = kPosition_6130;
+ break;
+
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment5 || savepoint.param.intValue == kObjectCompartmentE) ? kPosition_4840 : kPosition_4070;
+ params1->param3 = kPosition_4455;
+ break;
+
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ case kObjectCompartmentG:
+ case kObjectCompartmentH:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment7 || savepoint.param.intValue == kObjectCompartmentG) ? kPosition_3050 : kPosition_2740;
+ params1->param3 = kPositionNone;
+ break;
+ }
+ break;
+
+ case kActionBreakCeiling:
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventCathBreakCeiling);
+ break;
+
+ case kActionJumpDownCeiling:
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventCathJumpDownCeiling);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Train::handleCompartmentAction() {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ if (params->param8)
+ getSavePoints()->push(kEntityTrain, kEntityMahmud, kAction290410610, params->param1);
+
+ getAction()->handleOtherCompartment((ObjectIndex)params->param1, false, !params->param8);
+
+ ENTITY_PARAM(0, 8) = params->param1;
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Train::resetParam8() {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+ EntityData::EntityParametersIIIS *params1 = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters(1);
+
+ if (params->param8
+ && !getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params1->param1, (EntityPosition)params1->param2)
+ && !getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params1->param1, (EntityPosition)params1->param3)) {
+
+ if (getSound()->isBuffered((const char *)&params1->seq))
+ getSound()->processEntry((const char *)&params1->seq);
+
+ params->param8 = 0;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/train.h b/engines/lastexpress/entities/train.h
new file mode 100644
index 0000000000..4c6e2989ff
--- /dev/null
+++ b/engines/lastexpress/entities/train.h
@@ -0,0 +1,95 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_TRAIN_H
+#define LASTEXPRESS_TRAIN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Train : public Entity {
+public:
+ Train(LastExpressEngine *engine);
+ ~Train() {};
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Harem events
+ *
+ * @param compartment The compartment to handle
+ * @param counter ??? (checked to decide which sound to make when knocking)
+ */
+ DECLARE_FUNCTION_2(harem, ObjectIndex compartment, uint32 counter)
+
+ /**
+ * Handles Train events
+ */
+ DECLARE_FUNCTION(process)
+
+private:
+ // Helper methods
+ void resetParam8();
+ void handleCompartmentAction();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TRAIN_H
diff --git a/engines/lastexpress/entities/vassili.cpp b/engines/lastexpress/entities/vassili.cpp
new file mode 100644
index 0000000000..951c323f91
--- /dev/null
+++ b/engines/lastexpress/entities/vassili.cpp
@@ -0,0 +1,589 @@
+/* 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/entities/vassili.h"
+
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Vassili::Vassili(LastExpressEngine *engine) : Entity(engine, kEntityVassili) {
+ ADD_CALLBACK_FUNCTION(Vassili, reset);
+ ADD_CALLBACK_FUNCTION(Vassili, draw);
+ ADD_CALLBACK_FUNCTION(Vassili, savegame);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter1);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Vassili, function6);
+ ADD_CALLBACK_FUNCTION(Vassili, function7);
+ ADD_CALLBACK_FUNCTION(Vassili, function8);
+ ADD_CALLBACK_FUNCTION(Vassili, function9);
+ ADD_CALLBACK_FUNCTION(Vassili, seizure);
+ ADD_CALLBACK_FUNCTION(Vassili, drawInBed);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter2);
+ ADD_CALLBACK_FUNCTION(Vassili, sleeping);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter3);
+ ADD_CALLBACK_FUNCTION(Vassili, stealEgg);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter4);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Vassili, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Vassili, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Vassili, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Vassili, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject40, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Vassili, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityTatiana)->entityPosition;
+ getData()->location = getEntityData(kEntityTatiana)->location;
+ } else {
+ if (params->param3 && params->param3 >= getState()->time) {
+ break;
+ }else {
+ params->param3 = getState()->time + 450;
+ if (params->param3 == 0)
+ break;
+ }
+
+ if (!params->param2 && getObjects()->get(kObjectCompartmentA).location == kObjectLocation1) {
+ params->param2 = 1;
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ }
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ break;
+
+ case kAction122732000:
+ setup_function6();
+ break;
+
+ case kAction168459827:
+ params->param1 = 0;
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Vassili, function6)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM_GOTO(params->param3, getState()->timeTicks, params->param1, label_function7);
+
+ setCallback(1);
+ setup_draw("303B");
+ break;
+ }
+
+ params->param3 = 0;
+
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+
+label_function7:
+ if (params->param4 != kTimeInvalid && getState()->time > kTime1489500) {
+
+ if (getState()->time <= kTime1503000) {
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200) || !params->param4) {
+
+ params->param4 = getState()->time;
+ if (!params->param4) {
+ setup_function7();
+ break;
+ }
+ }
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+ setup_function7();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 5 * (3 * rnd(25) + 15);
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+
+ // Shared part with kActionNone
+ goto label_function7;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Vassili, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid && getState()->time > kTime1503000) {
+
+ if (getState()->time <= kTime1512000) {
+ if (getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param1) {
+ params->param1 = getState()->time + 150;
+ if (params->param1) {
+ setup_function8();
+ break;
+ }
+ }
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+ setup_function8();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityVassili);
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200))
+ getScenes()->loadSceneFromObject(kObjectCompartmentA);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction339669520:
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Vassili, function8)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setup_function9();
+ break;
+
+ case kActionDefault:
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, (getEntityData(kEntityPlayer)->car <= kCarRedSleeping) ? 1 : 40);
+ }
+
+ getSavePoints()->push(kEntityVassili, kEntityAnna, kAction226031488);
+ getSavePoints()->push(kEntityVassili, kEntityVerges, kAction226031488);
+ getSavePoints()->push(kEntityVassili, kEntityCoudert, kAction226031488);
+ getSound()->playSound(kEntityVassili, "VAS1027", SoundManager::kFlagDefault);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Vassili, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ if (!getEntities()->isDistanceBetweenEntities(kEntityVassili, kEntityPlayer, 2500))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setup_seizure();
+ break;
+
+ case kActionDefault:
+ case kActionDrawScene:
+ if ((getObjects()->get(kObjectCompartmentA).location == kObjectLocation2 && getEntities()->isPlayerPosition(kCarRedSleeping, 17))
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 18)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 37)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 38)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 41)) {
+
+ if (savepoint.action == kActionDrawScene)
+ getSound()->processEntry(kEntityVassili);
+
+ setup_seizure();
+ } else {
+ if (savepoint.action == kActionDefault)
+ getSound()->playSound(kEntityVassili, "VAS1028", SoundManager::kFlagDefault);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Vassili, seizure)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Check that we have removed the body from the train and changed jacket
+ if (!getProgress().eventCorpseMovedFromFloor) {
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ if (!getProgress().eventCorpseThrown) {
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ // Setup Anna & Coudert
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function37);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function38);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliSeizure);
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getData()->location = kLocationInsideCompartment;
+ getAction()->playAnimation(kEventVassiliSeizure);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getProgress().field_18 = 2;
+
+ getSavePoints()->push(kEntityVassili, kEntityAnna, kAction191477936);
+ getSavePoints()->push(kEntityVassili, kEntityVerges, kAction191477936);
+ getSavePoints()->push(kEntityVassili, kEntityCoudert, kAction191477936);
+ getScenes()->loadSceneFromObject(kObjectCompartmentA);
+
+ setup_drawInBed();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Vassili, drawInBed)
+ if (savepoint.action == kActionDefault)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Vassili, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_sleeping();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Vassili, sleeping)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Vassili, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_stealEgg();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Vassili, stealEgg)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionOpenDoor:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliCompartmentStealEgg);
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && getInventory()->hasItem(kItemFirebird)
+ && !getEvent(kEventVassiliCompartmentStealEgg))
+ getObjects()->update(kObject48, kEntityVassili, kObjectLocationNone, kCursorNormal, kCursorHand);
+ else
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventVassiliCompartmentStealEgg);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 67);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Vassili, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Looks identical to sleeping (#13)
+IMPLEMENT_FUNCTION(17, Vassili, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Vassili, chapter5)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/vassili.h b/engines/lastexpress/entities/vassili.h
new file mode 100644
index 0000000000..113bc1d958
--- /dev/null
+++ b/engines/lastexpress/entities/vassili.h
@@ -0,0 +1,110 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_VASSILI_H
+#define LASTEXPRESS_VASSILI_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Vassili : public Entity {
+public:
+ Vassili(LastExpressEngine *engine);
+ ~Vassili() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function6)
+ DECLARE_FUNCTION(function7)
+ DECLARE_FUNCTION(function8)
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(seizure)
+ DECLARE_FUNCTION(drawInBed)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(sleeping)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(stealEgg)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VASSILI_H
diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp
new file mode 100644
index 0000000000..20979ff9f9
--- /dev/null
+++ b/engines/lastexpress/entities/verges.cpp
@@ -0,0 +1,1898 @@
+/* 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/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Verges::Verges(LastExpressEngine *engine) : Entity(engine, kEntityVerges) {
+ ADD_CALLBACK_FUNCTION(Verges, reset);
+ ADD_CALLBACK_FUNCTION(Verges, draw);
+ ADD_CALLBACK_FUNCTION(Verges, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Verges, playSound);
+ ADD_CALLBACK_FUNCTION(Verges, playSound16);
+ ADD_CALLBACK_FUNCTION(Verges, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Verges, savegame);
+ ADD_CALLBACK_FUNCTION(Verges, updateEntity);
+ ADD_CALLBACK_FUNCTION(Verges, function9);
+ ADD_CALLBACK_FUNCTION(Verges, function10);
+ ADD_CALLBACK_FUNCTION(Verges, function11);
+ ADD_CALLBACK_FUNCTION(Verges, function12);
+ ADD_CALLBACK_FUNCTION(Verges, function13);
+ ADD_CALLBACK_FUNCTION(Verges, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Verges, function15);
+ ADD_CALLBACK_FUNCTION(Verges, function16);
+ ADD_CALLBACK_FUNCTION(Verges, function17);
+ ADD_CALLBACK_FUNCTION(Verges, chapter1);
+ ADD_CALLBACK_FUNCTION(Verges, talkHarem);
+ ADD_CALLBACK_FUNCTION(Verges, talkPassengerList);
+ ADD_CALLBACK_FUNCTION(Verges, talkGendarmes);
+ ADD_CALLBACK_FUNCTION(Verges, function22);
+ ADD_CALLBACK_FUNCTION(Verges, function23);
+ ADD_CALLBACK_FUNCTION(Verges, policeGettingOffTrain);
+ ADD_CALLBACK_FUNCTION(Verges, function25);
+ ADD_CALLBACK_FUNCTION(Verges, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Verges, chapter2);
+ ADD_CALLBACK_FUNCTION(Verges, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Verges, chapter3);
+ ADD_CALLBACK_FUNCTION(Verges, function30);
+ ADD_CALLBACK_FUNCTION(Verges, function31);
+ ADD_CALLBACK_FUNCTION(Verges, function32);
+ ADD_CALLBACK_FUNCTION(Verges, function33);
+ ADD_CALLBACK_FUNCTION(Verges, function34);
+ ADD_CALLBACK_FUNCTION(Verges, function35);
+ ADD_CALLBACK_FUNCTION(Verges, chapter4);
+ ADD_CALLBACK_FUNCTION(Verges, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Verges, function38);
+ ADD_CALLBACK_FUNCTION(Verges, chapter5);
+ ADD_CALLBACK_FUNCTION(Verges, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Verges, function41);
+ ADD_CALLBACK_FUNCTION(Verges, function42);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Verges, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Verges, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Verges, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityVerges);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Verges, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Verges, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Verges, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Verges, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Verges, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityPlayer, "TRA1113", getSound()->getSoundFlag(kEntityVerges));
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(9, Verges, function9)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function10(kCarGreenSleeping, kPosition_540, (char *)&params->seq1);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityVerges);
+
+ setCallback(5);
+ setup_updateFromTime(225);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param7) {
+ if (!getSound()->isBuffered(kEntityVerges)) {
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ params->param7 = 1;
+ }
+ }
+
+ if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param6) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+
+ params->param6 = 0;
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ params->param6 = 1;
+ break;
+
+ case kActionDefault:
+ if (!getSound()->isBuffered(kEntityVerges)) {
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ params->param7 = 1;
+ }
+
+ if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Verges, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_draw("813US");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceRight(kEntityVerges, "813UD");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 4: {
+ getEntities()->clearSequences(kEntityVerges);
+
+ bool loadscene = true;
+
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesEscortToDiningCar);
+ else if (getEntities()->isInBaggageCar(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesBaggageCarOffLimits);
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesCanIHelpYou);
+ else
+ loadscene = false;
+
+ if (loadscene) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Verges, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_850;
+ getEntities()->clearSequences(kEntityVerges);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(13, Verges, function13, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVergesSuitcase);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEvent(kEventVergesSuitcase) || getEvent(kEventVergesSuitcaseNight) || getEvent(kEventVergesSuitcaseOtherEntry) || getEvent(kEventVergesSuitcaseNightOtherEntry))
+ params->param2 = 1;
+
+ if (isNight() && getProgress().chapter != kChapter1)
+ params->param2 = 1;
+
+ if (params->param1) {
+ if (isNight())
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseNightOtherEntryStart : kEventVergesSuitcaseNightOtherEntry);
+ else
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseOtherEntryStart : kEventVergesSuitcaseOtherEntry);
+ } else {
+ if (isNight())
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseNightStart : kEventVergesSuitcaseNight);
+ else
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseStart : kEventVergesSuitcase);
+ }
+
+ getEntities()->clearSequences(kEntityVerges);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 91);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Verges, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param5 && params->param6) {
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ params->param5 = 1;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620F");
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction171394341);
+ break;
+
+ case kAction155853632:
+ params->param6 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (CURRENT_PARAMS(1, 1) && params->param8) {
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ CURRENT_PARAMS(1, 1)++;
+
+ if (CURRENT_PARAMS(1, 1) == 1)
+ getSound()->playSound(kEntityVerges, (char *)&params->seq2);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620F");
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction171394341);
+ break;
+
+ case kAction155853632:
+ params->param8 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Verges, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityMertens, "TRA1291");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ ENTITY_PARAM(0, 3) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Verges, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityVerges, kActionDeliverMessageToTyler, 0);
+ getSavePoints()->addData(kEntityVerges, kAction226031488, 1);
+ getSavePoints()->addData(kEntityVerges, kAction339669520, 1);
+ getSavePoints()->addData(kEntityVerges, kAction167854368, 4);
+ getSavePoints()->addData(kEntityVerges, kAction158617345, 2);
+ getSavePoints()->addData(kEntityVerges, kAction168255788, 3);
+ getSavePoints()->addData(kEntityVerges, kAction201431954, 5);
+ getSavePoints()->addData(kEntityVerges, kAction168187490, 6);
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(19, Verges, talkHarem)
+ talk(savepoint, "TRA1202", "TRA1201");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Verges, talkPassengerList)
+ talk(savepoint, "TRA1205", "TRA1206");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Verges, talkGendarmes)
+ talk(savepoint, "TRA1250", "TRA1251");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Verges, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ if (getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAugustWaiting)) {
+ setCallback(3);
+ setup_function16(kEntityMertens, "TRA1200", "TRA1201");
+ } else {
+ setCallback(4);
+ setup_function16(kEntityMertens, "TRA1200A", "TRA1201");
+ }
+ break;
+
+ case 3:
+ case 4:
+ getSavePoints()->push(kEntityVerges, kEntityMertens, kAction169633856);
+
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Verges, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction191477936:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityVerges, kEntityPlayer, 1000) && getEntityData(kEntityPlayer)->location == kLocationOutsideCompartment) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityVerges, "POL1101", SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSound()->processEntry(kEntityVerges);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Verges, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ if (!getEntities()->isInKronosSalon(kEntityPlayer)) {
+
+ if (getEntityData(kEntityPlayer)->car > kCarRedSleeping
+ || (getEntityData(kEntityPlayer)->car == kCarRedSleeping && getEntityData(kEntityPlayer)->entityPosition > kPosition_9270)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ } else {
+ if (getEntityData(kEntityPlayer)->car > kCarGreenSleeping
+ || (getEntityData(kEntityPlayer)->car == kCarGreenSleeping && getEntityData(kEntityPlayer)->entityPosition < kPosition_4840)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ }
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ }
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isOutsideAnnaWindow())
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4840)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentE, true);
+ }
+
+ getSavePoints()->push(kEntityVerges, kEntityGendarmes, kAction169499649);
+
+ getProgress().field_3C = 1;
+ getState()->timeDelta = 1;
+
+ if (getData()->car == kCarRedSleeping) {
+ setCallback(6);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA1005");
+ } else {
+ setCallback(7);
+ setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ }
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ if (getEvent(kEventKronosConversation)) {
+ getProgress().field_3C = 1;
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+
+ getState()->timeDelta = 3;
+ getSavePoints()->push(kEntityVerges, kEntityChapters, kAction169629818);
+
+ setCallback(3);
+ setup_policeGettingOffTrain();
+ } else {
+ setCallback(2);
+ setup_updateFromTime(150);
+ }
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
+
+ setCallback(4);
+ setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case 5:
+ case 11:
+ ENTITY_PARAM(0, 7) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ case 7:
+ getEntities()->clearSequences(kEntityVerges);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityVerges, kEntityChapters, kAction169629818);
+
+ setCallback(9);
+ setup_policeGettingOffTrain();
+ break;
+
+ case 9:
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
+
+ setCallback(10);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA1006");
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function11();
+ break;
+ }
+ break;
+
+ case kAction168710784:
+ getData()->car = kCarGreenSleeping;
+
+ if (!(getEntityData(kEntityPlayer)->car == kCarGreenSleeping))
+ getData()->car = kCarRedSleeping;
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+
+ getState()->timeDelta = 3;
+
+ setCallback(8);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Verges, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 6)) {
+ params->param1 = 1;
+ params->param2 = 1;
+ params->param3 = 1;
+ params->param4 = 1;
+ params->param5 = 1;
+ params->param6 = 1;
+
+ ENTITY_PARAM(0, 6) = 0;
+ }
+
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(1);
+ setup_function23();
+ break;
+ }
+
+label_callback1:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(2);
+ setup_function13(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(3);
+ setup_function25();
+ break;
+ }
+
+label_callback3:
+ if (params->param6)
+ goto label_callback12;
+
+ TIME_CHECK_CALLBACK_1(kTimeChapter1, params->param7, 4, setup_function9, "TRA1001");
+
+label_callback4:
+ TIME_CHECK_CALLBACK(kTime1089000, params->param8, 5, setup_function12);
+
+ params->param8 = 1;
+
+ if (!params->param5) {
+ setCallback(5);
+ setup_function12();
+ break;
+ }
+
+label_callback8:
+ TIME_CHECK_CALLBACK_1(kTime1107000, CURRENT_PARAMS(1, 1), 9, setup_function9, "TRA1001A");
+
+label_callback9:
+ TIME_CHECK_CALLBACK_1(kTime1134000, CURRENT_PARAMS(1, 2), 10, setup_function9, "TRA1002");
+
+label_callback10:
+ TIME_CHECK_CALLBACK_1(kTime1165500, CURRENT_PARAMS(1, 3), 11, setup_function9, "TRA1003");
+
+label_callback11:
+ TIME_CHECK_CALLBACK_1(kTime1225800, CURRENT_PARAMS(1, 4), 12, setup_function9, "TRA1004");
+
+label_callback12:
+ if (ENTITY_PARAM(0, 5) && !params->param2) {
+ setCallback(13);
+ setup_talkGendarmes();
+ break;
+ }
+
+label_callback13:
+ if (getInventory()->hasItem(kItemPassengerList) && !params->param3 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
+ setCallback(14);
+ setup_talkPassengerList();
+ break;
+ }
+
+label_callback14:
+ if (ENTITY_PARAM(0, 3) && !params->param4 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
+ setCallback(15);
+ setup_function17();
+ break;
+ }
+
+label_callback15:
+ if (ENTITY_PARAM(0, 1) && !params->param5) {
+ if (getState()->time < kTime1134000 || getState()->time > kTime1156500) {
+ setCallback(16);
+ setup_function22();
+ }
+ }
+ break;
+
+ case kActionOpenDoor:
+ setCallback(17);
+ setup_function13(savepoint.param.intValue < 106 ? true : false);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityVerges);
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function15(kEntityMertens, "TRA1202");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function11();
+ break;
+
+ case 8:
+ goto label_callback8;
+
+ case 9:
+ goto label_callback9;
+
+ case 10:
+ goto label_callback10;
+
+ case 11:
+ goto label_callback11;
+
+ case 12:
+ goto label_callback12;
+
+ case 13:
+ params->param2 = 1;
+ goto label_callback13;
+
+ case 14:
+ params->param3 = 1;
+ goto label_callback14;
+
+ case 15:
+ params->param4 = 1;
+ goto label_callback15;
+
+ case 16:
+ params->param5 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Verges, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Verges, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ }
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTime1818900, params->param1, 2, setup_function9, "Tra2177");
+
+label_callback_2:
+ if (params->param2 == kTimeInvalid || !getState()->time)
+ goto label_callback_6;
+
+ if (getState()->time > kTime1836000) {
+ params->param2 = kTimeInvalid;
+ setCallback(3);
+ setup_function12();
+ break;
+ }
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param2) {
+ params->param2 = getState()->time;
+
+ if (!params->param2) {
+ setCallback(3);
+ setup_function12();
+ break;
+ }
+ }
+
+ if (params->param2 >= getState()->time) {
+label_callback_6:
+
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(7);
+ setup_function17();
+ }
+
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+ setCallback(3);
+ setup_function12();
+ break;
+
+ case kActionOpenDoor:
+ setCallback(8);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionDefault:
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityCoudert, "TRA2100");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ goto label_callback_6;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Verges, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(30, Verges, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Verges, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, "TRA3015");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ getProgress().field_48 = 1;
+ ENTITY_PARAM(0, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Verges, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_3(kTime2263500, params->param1, 5, setup_function10, kCarRedSleeping, kPosition_9460, "TRA3006");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_8500;
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityVerges, "TRA3004");
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, "TRA3004");
+
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA3004");
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityVerges);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Verges, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("813US");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVerges, "813UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityVerges);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5799;
+
+ setCallback(getProgress().field_3C ? 4 : 5);
+ setup_playSound(getProgress().field_3C ? "ABB3035A" : "ABB3035");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("ABB3035");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityVerges, kEntityAbbot, kAction192054567);
+
+ setCallback(6);
+ setup_function9("Tra3010");
+ break;
+
+ case 6:
+ setup_function34();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Verges, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(2);
+ setup_function31();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function17();
+ break;
+ }
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 4, setup_function9, "Tra3001");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime1998000, params->param2, 5, setup_function9, "Tra3010a");
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2016000, params->param3, 6, setup_function35);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime2070000, params->param4, 7, setup_function9, "Tra3002");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTime2142000, params->param5, 8, setup_function9, "Tra3003");
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2173500, params->param6, 9, setup_function30, "Tra3012");
+
+label_callback_9:
+ TIME_CHECK_CALLBACK(kTime2218500, params->param7, 10, setup_function32);
+ break;
+
+ case kActionOpenDoor:
+ setCallback(11);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Verges, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityMertens, "Tra3011A");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction188570113);
+
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityMertens, "Tra3011");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityVerges, kEntityMertens, kAction188635520);
+
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Verges, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Verges, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 6)) {
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(2);
+ setup_function17();
+ break;
+ }
+
+label_callback_2:
+ TIME_CHECK_CALLBACK_1(kTime2349000, params->param1, 3, setup_function9, "Tra1001");
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTime2378700, params->param2, 4, setup_function9, "Tra4001");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime2403000, params->param3, 5, setup_function9, "Tra1001A");
+
+label_callback_5:
+ TIME_CHECK_CALLBACK_1(kTime2414700, params->param4, 6, setup_function9, "Tra4002");
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime2484000, params->param5, 7, setup_function9, "Tra4003");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTime2511000, params->param6, 8, setup_function9, "Tra4004");
+ }
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2538000, params->param7, 9, setup_function9, "Tra4005");
+
+ break;
+
+ case kActionOpenDoor:
+ setCallback(10);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Verges, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_6469;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityVerges);
+ setCallback(2);
+ setup_updateFromTime(1800);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11();
+ break;
+
+ case 3:
+ setup_chapter4Handler();
+ break;
+ }
+ break;
+
+ case kAction125233040:
+ getData()->entityPosition = kPosition_5790;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Verges, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Verges, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInSalon(kEntityPlayer) && !getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, "WAT5000");
+ break;
+
+ case kActionOpenDoor:
+ if (getSound()->isBuffered(kEntityVerges))
+ getSound()->processEntry(kEntityVerges);
+
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathFreePassengers);
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getObjects()->update(kObject65, kEntityVerges, kObjectLocation1, kCursorNormal, kCursorForward);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCathFreePassengers);
+ getSavePoints()->pushAll(kEntityVerges, kActionProceedChapter5);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
+ setup_function41();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Verges, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation3);
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_function10(kCarRedSleeping, kPosition_2000, "Tra5001");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ // Fallback to next case
+
+ case 2:
+ if (getSound()->isBuffered(kEntityVerges)) {
+ setCallback(2);
+ setup_updateFromTime(225);
+ } else {
+ setCallback(3);
+ setup_playSound("Con5001");
+ }
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction155991520);
+
+ setCallback(4);
+ setup_updateEntity(kCarBaggageRear, kPosition_9460);
+ break;
+
+ case 4:
+ setup_function42();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Verges, function42)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityVerges);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *sound2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, sound1);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityMertens, sound2);
+ break;
+
+ case 5:
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h
new file mode 100644
index 0000000000..3162d4e0ea
--- /dev/null
+++ b/engines/lastexpress/entities/verges.h
@@ -0,0 +1,182 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_VERGES_H
+#define LASTEXPRESS_VERGES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Verges : public Entity {
+public:
+ Verges(LastExpressEngine *engine);
+ ~Verges() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound16)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function9, const char *soundName)
+ DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, const char *soundName)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION_1(function13, bool)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION_2(function15, EntityIndex entity, const char *soundName)
+ DECLARE_FUNCTION_3(function16, EntityIndex entityIndex, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_NOSETUP(talkHarem)
+ DECLARE_FUNCTION(talkPassengerList)
+ DECLARE_FUNCTION(talkGendarmes)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(policeGettingOffTrain)
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION_1(function30, const char *soundName)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function38)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function41)
+ DECLARE_FUNCTION(function42)
+
+private:
+ void talk(const SavePoint &savepoint, const char* sound1, const char* sound2);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VERGES_H
diff --git a/engines/lastexpress/entities/vesna.cpp b/engines/lastexpress/entities/vesna.cpp
new file mode 100644
index 0000000000..9e0ef09a08
--- /dev/null
+++ b/engines/lastexpress/entities/vesna.cpp
@@ -0,0 +1,1161 @@
+/* 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/entities/vesna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Vesna::Vesna(LastExpressEngine *engine) : Entity(engine, kEntityVesna) {
+ ADD_CALLBACK_FUNCTION(Vesna, reset);
+ ADD_CALLBACK_FUNCTION(Vesna, playSound);
+ ADD_CALLBACK_FUNCTION(Vesna, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Vesna, draw);
+ ADD_CALLBACK_FUNCTION(Vesna, updateEntity);
+ ADD_CALLBACK_FUNCTION(Vesna, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Vesna, updateEntity2);
+ ADD_CALLBACK_FUNCTION(Vesna, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Vesna, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Vesna, savegame);
+ ADD_CALLBACK_FUNCTION(Vesna, function11);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter1);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function14);
+ ADD_CALLBACK_FUNCTION(Vesna, function15);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter2);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function18);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter3);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function21);
+ ADD_CALLBACK_FUNCTION(Vesna, function22);
+ ADD_CALLBACK_FUNCTION(Vesna, function23);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter4);
+ ADD_CALLBACK_FUNCTION(Vesna, function25);
+ ADD_CALLBACK_FUNCTION(Vesna, function26);
+ ADD_CALLBACK_FUNCTION(Vesna, function27);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter5);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function30);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Vesna, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Vesna, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Vesna, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Vesna, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Vesna, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT10150" : "CAT1015A");
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Vesna, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Vesna, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ params->param3 = 0;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityVesna, kEntityMilos, 500)
+ || (((getData()->direction == kDirectionUp && (getData()->car > getEntityData(kEntityMilos)->car)) || (getData()->car == getEntityData(kEntityMilos)->car && getData()->entityPosition > getEntityData(kEntityMilos)->entityPosition)))
+ || (((getData()->direction == kDirectionDown && (getData()->car < getEntityData(kEntityMilos)->car)) || (getData()->car == getEntityData(kEntityMilos)->car && getData()->entityPosition < getEntityData(kEntityMilos)->entityPosition)))) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntityVesna, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntityVesna, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Vesna, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Vesna, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Vesna, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Vesna, function11)
+ // Expose parameters as IIIS and ignore the default exposed parameters
+ EntityData::EntityParametersIIIS *parameters = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (parameters->param3) {
+ UPDATE_PARAM(parameters->param7, getState()->timeTicks, 75);
+
+ parameters->param2 = 1;
+ parameters->param3 = 0;
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ parameters->param7 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (parameters->param3) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ parameters->param1++;
+ switch (parameters->param1) {
+ default:
+ strcpy((char *)&parameters->seq, "VES1015C");
+ parameters->param1 = 0;
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq, "VES1015A");
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq, "VES1015B");
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (parameters->param2 || parameters->param3) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ parameters->param2 = 0;
+ parameters->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound((char *)&parameters->seq);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorTalk, kCursorNormal);
+ parameters->param3 = 1;
+ break;
+
+ case 4:
+ parameters->param2 = 1;
+ parameters->param3 = 0;
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ case kAction101687594:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Vesna, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityVesna, kAction124190740, 0);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Vesna, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityMilos)->entityPosition;
+ getData()->location = getEntityData(kEntityMilos)->location;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->clearSequences(kEntityVesna);
+ setup_function14();
+ }
+ break;
+
+ case kAction204832737:
+ setCallback(1);
+ setup_updateEntity2(kCarRedSleeping, kPosition_3050);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Vesna, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction190412928:
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Vesna, function15)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityVesna);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Vesna, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Vesna, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction135024800:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case kAction137165825:
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Vesna, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 7:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_draw("808DD");
+ break;
+
+ case 8:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(9);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG);
+ break;
+
+ case 11:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Vesna, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Vesna, chapter3Handler)
+ EntityData::EntityParametersIIIS *parameters = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_54 && parameters->param7 != kTimeInvalid) {
+ if (getState()->time > kTime2250000) {
+ parameters->param7 = kTimeInvalid;
+ setup_function22();
+ break;
+ }
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !parameters->param7)
+ parameters->param7 = getState()->time;
+
+ if (parameters->param7 < getState()->time) {
+ parameters->param7 = kTimeInvalid;
+ setup_function22();
+ break;
+ }
+ }
+
+ if (parameters->param2) {
+ UPDATE_PARAM(parameters->param8, getState()->timeTicks, 75);
+
+ parameters->param1 = 1;
+ parameters->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ parameters->param8 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (parameters->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ ++parameters->param3;
+
+ switch (parameters->param3) {
+ default:
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq, "VES1015A");
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq, "VES1015B");
+ break;
+
+ case 3:
+ strcpy((char *)&parameters->seq, "VES1015C");
+ parameters->param3 = 0;
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->clearSequences(kEntityVesna);
+ break;
+
+ case kActionDrawScene:
+ if (parameters->param1 || parameters->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ parameters->param1 = 0;
+ parameters->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound((char *)&parameters->seq);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorTalk, kCursorNormal);
+ parameters->param2 = 1;
+ break;
+
+ case 4:
+ parameters->param1 = 1;
+ parameters->param2 = 0;
+ break;
+ }
+ break;
+
+ case kAction137165825:
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case kAction155913424:
+ setCallback(6);
+ setup_function21();
+ break;
+
+ case kAction203663744:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Vesna, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 7:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_draw("808DD");
+ break;
+
+ case 8:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(9);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG); /* BUG the original engine passes 3050 here */
+ break;
+
+ case 11:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Vesna, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityVesna, kEntityMilos, kAction259125998);
+
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->car = kCarBaggage;
+ getSavePoints()->push(kEntityVesna, kEntityAnna, kAction235856512);
+ break;
+
+ case 6:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_draw("808DD");
+ break;
+
+ case 7:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(8);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG); /* BUG the original engine passes 3050 here */
+ break;
+
+ case 10:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ setup_function23();
+ break;
+ }
+ break;
+
+ case kAction189299008:
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Vesna, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("VES1015A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction203663744:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Vesna, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function11();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function25();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Vesna, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2428200 && !params->param1) {
+ params->param1 = 1;
+ setup_function26();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityVesna, kEntityMilos, kAction135600432);
+
+ setCallback(1);
+ setup_enterExitCompartment("610BG", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+
+ // Original game calls clearSequences a second time
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Vesna, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("808DD");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("610AG", kObjectCompartmentG);
+ break;
+
+ case 5:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Vesna, function27)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityVesna);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Vesna, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Vesna, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionOpenDoor:
+ setCallback(1);
+
+ getData()->currentCall++;
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaRestaurantKilled);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject64, kEntityVesna, kObjectLocationNone, kCursorNormal, kCursorForward);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCathVesnaRestaurantKilled);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ }
+ break;
+
+ case kAction134427424:
+ getObjects()->update(kObject64, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ setup_function30();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Vesna, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->timeTicks, 120)
+ getSound()->playSound(kEntityVesna, "Ves50001", SoundManager::kFlagDefault);
+ params->param1 = 1;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 180);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getAction()->playAnimation(kEventCathVesnaTrainTopKilled);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathVesnaTrainTopFight);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 4:
+ params->param2 = getFight()->setup(kFightVesna);
+
+ if (params->param2) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param2 != Fight::kFightEndExit);
+ } else {
+ getSound()->playSound(kEntityPlayer, "TUNNEL");
+
+ getState()->time += 1800;
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopWin);
+ }
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventCathVesnaTrainTopWin);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 11);
+
+ setup_nullfunction();
+ break;
+ }
+ break;
+
+ case kAction167992577:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopFight);
+ break;
+
+ case kAction202884544:
+ if (params->param1) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled);
+ } else {
+ getSound()->playSound(kEntityVesna, "Ves5001", SoundManager::kFlagDefault);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(31, Vesna)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/vesna.h b/engines/lastexpress/entities/vesna.h
new file mode 100644
index 0000000000..4a0a8550b2
--- /dev/null
+++ b/engines/lastexpress/entities/vesna.h
@@ -0,0 +1,176 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_VESNA_H
+#define LASTEXPRESS_VESNA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Vesna : public Entity {
+public:
+ Vesna(LastExpressEngine *engine);
+ ~Vesna() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char* sequence)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VESNA_H
diff --git a/engines/lastexpress/entities/yasmin.cpp b/engines/lastexpress/entities/yasmin.cpp
new file mode 100644
index 0000000000..fe7c7fb2bf
--- /dev/null
+++ b/engines/lastexpress/entities/yasmin.cpp
@@ -0,0 +1,490 @@
+/* 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/entities/yasmin.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Yasmin::Yasmin(LastExpressEngine *engine) : Entity(engine, kEntityYasmin) {
+ ADD_CALLBACK_FUNCTION(Yasmin, reset);
+ ADD_CALLBACK_FUNCTION(Yasmin, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Yasmin, playSound);
+ ADD_CALLBACK_FUNCTION(Yasmin, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Yasmin, updateEntity);
+ ADD_CALLBACK_FUNCTION(Yasmin, function6);
+ ADD_CALLBACK_FUNCTION(Yasmin, function7);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter1);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function10);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter2);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter3);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter4);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function17);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter5);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function20);
+ ADD_CALLBACK_FUNCTION(Yasmin, function21);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Yasmin, reset)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityYasmin);
+ break;
+ }
+
+ // Process default actions
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Yasmin, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Yasmin, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Yasmin, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Yasmin, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Yasmin, function6)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("615Be", kObjectCompartment5);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_3050);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("615Ag", kObjectCompartment7);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityYasmin);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Yasmin, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("615Bg", kObjectCompartment7);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4840);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("615Ae", kObjectCompartment5);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityYasmin);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Yasmin, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CHAPTER1(setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1093500, params->param1, 1, setup_function6);
+ TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7);
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070);
+ TIME_CHECK_PLAYSOUND(kTime1165500, params->param4, 5, "Har1104");
+ TIME_CHECK_PLAYSOUND(kTime1174500, params->param5, 6, "Har1106");
+ TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_2740;
+ setCallback(2);
+ setup_playSound("Har1102");
+ break;
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7);
+ // Fallback to case 3
+
+ case 3:
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070);
+ // Fallback to case 4
+
+ case 4:
+ TIME_CHECK_PLAYSOUND(kTime1165500, params->param4, 5, "Har1104");
+ // Fallback to case 5
+
+ case 5:
+ TIME_CHECK_PLAYSOUND(kTime1174500, params->param5, 6, "Har1106");
+ // Fallback to case 6
+
+ case 6:
+ TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Yasmin, function10)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityYasmin);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Yasmin, chapter2)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Yasmin, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1759500, params->param1, 1, setup_function7);
+
+ if (getState()->time > kTime1800000 && !params->param2) {
+ params->param2 = 1;
+ getData()->entityPosition = kPosition_4070;
+
+ getSavePoints()->push(kEntityYasmin, kEntityTrain, kAction191070912, kPosition_4070);
+ }
+ break;
+
+ case kActionCallback:
+
+ if (getCallback() != 1)
+ break;
+
+ if (getState()->time > kTime1800000 && !params->param2) {
+ params->param2 = 1;
+ getData()->entityPosition = kPosition_4070;
+
+ getSavePoints()->push(kEntityYasmin, kEntityTrain, kAction191070912, kPosition_4070);
+ }
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Yasmin, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2062800, params->param1, 1, setup_function6);
+ TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7);
+ TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7);
+ // Fallback to case 2
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Yasmin, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2457000, params->param1, 1, setup_function7);
+ TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4070;
+ setCallback(2);
+ setup_playSound("Har1110");
+ break;
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Yasmin, function17)
+ // Same as existing function 10 ?
+ function10(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Yasmin, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Yasmin, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function20();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Yasmin, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function21();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setup_function21();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Yasmin, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityYasmin, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityYasmin);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/yasmin.h b/engines/lastexpress/entities/yasmin.h
new file mode 100644
index 0000000000..125b11d566
--- /dev/null
+++ b/engines/lastexpress/entities/yasmin.h
@@ -0,0 +1,142 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_YASMIN_H
+#define LASTEXPRESS_YASMIN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Yasmin : public Entity {
+public:
+ Yasmin(LastExpressEngine *engine);
+ ~Yasmin() {};
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char* sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char* filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function6)
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function10)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_YASMIN_H
diff --git a/engines/lastexpress/eventhandler.h b/engines/lastexpress/eventhandler.h
new file mode 100644
index 0000000000..8536bba7e7
--- /dev/null
+++ b/engines/lastexpress/eventhandler.h
@@ -0,0 +1,53 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_EVENTHANDLER_H
+#define LASTEXPRESS_EVENTHANDLER_H
+
+#include "common/EventRecorder.h"
+#include "common/func.h"
+
+namespace LastExpress {
+
+#define SET_EVENT_HANDLERS(class, inst) \
+ _engine->setEventHandlers(new EVENT_HANDLER(class, eventMouse, inst), new EVENT_HANDLER(class, eventTick, inst));
+
+#define EVENT_HANDLER(class, name, inst) \
+ Common::Functor1Mem<const Common::Event&, void, class>(inst, &class::name)
+
+class EventHandler {
+public:
+ virtual ~EventHandler() {}
+
+ // Function pointer for event handler
+ typedef Common::Functor1<const Common::Event&, void> EventFunction;
+
+ virtual void eventMouse(const Common::Event &ev) {}; // Event type 1
+ virtual void eventTick(const Common::Event &ev) {}; // Event type 3
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_EVENTHANDLER_H
diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp
new file mode 100644
index 0000000000..b2c7fdef1f
--- /dev/null
+++ b/engines/lastexpress/game/action.cpp
@@ -0,0 +1,1968 @@
+/* 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/action.h"
+
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+static const int _animationListSize = 273;
+
+// List of animations
+static const struct {
+ const char *filename;
+ uint16 time;
+} _animationList[_animationListSize] = {
+ {"", 0},
+ {"1002", 255},
+ {"1002D", 255},
+ {"1003", 0},
+ {"1005", 195},
+ {"1006", 750}, // 5
+ {"1006A", 750},
+ {"1008", 765},
+ {"1008N", 765},
+ {"1008A", 750},
+ {"1008AN", 750}, // 10
+ {"1009", 0},
+ {"1011", 1005},
+ {"1011A", 780},
+ {"1012", 300},
+ {"1013", 285},
+ {"1017", 870}, // 15
+ {"1017A", 0}, // Not in the data files?
+ {"1019", 120},
+ {"1019D", 120},
+ {"1020", 120}, // 20
+ {"1022", 525},
+ {"1022A", 180},
+ {"1022AD", 210},
+ {"1022B", 210},
+ {"1022C", 210}, // 25
+ {"1023", 135},
+ {"1025", 945},
+ {"1028", 300},
+ {"1030", 390},
+ {"1031", 375}, // 30
+ {"1032", 1050},
+ {"1033", 945},
+ {"1034", 495},
+ {"1035", 1230},
+ {"1037", 1425}, // 35
+ {"1038", 195},
+ {"1038A", 405},
+ {"1039", 600},
+ {"1040", 945},
+ {"1041", 510}, // 40
+ {"1042", 540},
+ {"1043", 855},
+ {"1044", 645},
+ {"1046", 0},
+ {"1047", 0}, // 45
+ {"1047A", 0},
+ {"1059", 1005},
+ {"1060", 255},
+ {"1063", 0},
+ {"1101", 255}, // 50
+ {"1102", 1320},
+ {"1103", 210},
+ {"1104", 120},
+ {"1105", 1350},
+ {"1106", 315}, // 55
+ {"1106A", 315},
+ {"1106D", 315},
+ {"1107", 1},
+ {"1107A", 660},
+ {"1108", 300}, // 60
+ {"1109", 1305},
+ {"1110", 300},
+ {"1112", 0},
+ {"1115", 0},
+ {"1115A", 0}, // 65
+ {"1115B", 0},
+ {"1115C", 0},
+ {"1115D", 0},
+ {"1115E", 0},
+ {"1115F", 0}, // 70
+ {"1115G", 0},
+ {"1115H", 0},
+ {"1116", 0},
+ {"1117", 0},
+ {"1118", 105}, // 75
+ {"1202", 510},
+ {"1202A", 510},
+ {"1203", 720},
+ {"1204", 120},
+ {"1205", 465}, // 80
+ {"1206", 690},
+ {"1206A", 450},
+ {"1208", 465},
+ {"1210", 1020},
+ {"1211", 600}, // 85
+ {"1212", 435},
+ {"1213", 525},
+ {"1213A", 150},
+ {"1215", 390},
+ {"1216", 0}, // 90
+ {"1219", 240},
+ {"1222", 1095},
+ {"1223", 0},
+ {"1224", 720},
+ {"1225", 1005}, // 95
+ {"1227", 840},
+ {"1227A", 840},
+ {"1303", 450},
+ {"1303N", 450},
+ {"1304", 450}, // 100
+ {"1304N", 450},
+ {"1305", 630},
+ {"1309", 0},
+ {"1311", 1710},
+ {"1312", 240}, // 105
+ {"1312D", 240},
+ {"1313", 930},
+ {"1315", 1035},
+ {"1315A", 1035},
+ {"1401", 540}, // 110
+ {"1402", 150},
+ {"1402B", 150},
+ {"1403", 90},
+ {"1404", 885},
+ {"1404A", 0}, // 115
+ {"1405", 135},
+ {"1406", 1665},
+ {"1501", 285},
+ {"1501A", 285},
+ {"1502", 165}, // 120
+ {"1502A", 165},
+ {"1502D", 165},
+ {"1503", 0},
+ {"1504", 0},
+ {"1505", 0}, // 125
+ {"1505A", 0},
+ {"1506", 300},
+ {"1506A", 180},
+ {"1508", 0},
+ {"1509", 450}, // 130
+ {"1509S", 450},
+ {"1509A", 450},
+ {"1509AS", 450},
+ {"1509N", 450},
+ {"1509SN", 450}, // 135
+ {"1509AN", 450},
+ {"1509BN", 450},
+ {"1511", 150},
+ {"1511A", 150},
+ {"1511B", 90}, // 140
+ {"1511BA", 90},
+ {"1511C", 135},
+ {"1511D", 105},
+ {"1930", 0},
+ {"1511E", 150}, // 145
+ {"1512", 165},
+ {"1513", 180},
+ {"1517", 0},
+ {"1517A", 165},
+ {"1518", 165}, // 150
+ {"1518A", 165},
+ {"1518B", 165},
+ {"1591", 450},
+ {"1592", 450},
+ {"1593", 450}, // 155
+ {"1594", 450},
+ {"1595", 450},
+ {"1596", 450},
+ {"1601", 0},
+ {"1603", 0}, // 160
+ {"1606B", 315},
+ {"1607A", 0},
+ {"1610", 0},
+ {"1611", 0},
+ {"1612", 0}, // 165
+ {"1615", 0},
+ {"1619", 0},
+ {"1620", 120},
+ {"1621", 105},
+ {"1622", 105}, // 170
+ {"1629", 450},
+ {"1630", 450},
+ {"1631", 525},
+ {"1632", 0},
+ {"1633", 615}, // 175
+ {"1634", 180},
+ {"1702", 180},
+ {"1702DD", 180},
+ {"1702NU", 180},
+ {"1702ND", 180}, // 180
+ {"1704", 300},
+ {"1704D", 300},
+ {"1705", 195},
+ {"1705D", 195},
+ {"1706", 195}, // 185
+ {"1706DD", 195},
+ {"1706ND", 195},
+ {"1706NU", 195},
+ {"1901", 135},
+ {"1902", 1410}, // 190
+ {"1903", 0},
+ {"1904", 1920},
+ {"1908", 600},
+ {"1908A", 195},
+ {"1908B", 105}, // 195
+ {"1908C", 165},
+ {"1908CD", 0},
+ {"1909A", 150},
+ {"1909B", 150},
+ {"1909C", 150}, // 200
+ {"1910A", 180},
+ {"1910B", 180},
+ {"1910C", 180},
+ {"1911A", 90},
+ {"1911B", 90}, // 205
+ {"1911C", 90},
+ {"1912", 0},
+ {"1913", 0},
+ {"1917", 0},
+ {"1918", 390}, // 210
+ {"1919", 360},
+ {"1919A", 105},
+ {"1920", 75},
+ {"1922", 75},
+ {"1923", 150}, // 215
+ {"8001", 120},
+ {"8001A", 120},
+ {"8002", 120},
+ {"8002A", 120},
+ {"8002B", 120}, // 220
+ {"8003", 105},
+ {"8003A", 105},
+ {"8004", 105},
+ {"8004A", 105},
+ {"8005", 270}, // 225
+ {"8005B", 270},
+ {"8010", 270},
+ {"8013", 120},
+ {"8013A", 120},
+ {"8014", 165}, // 230
+ {"8014A", 165},
+ {"8014R", 165},
+ {"8014AR", 165},
+ {"8015", 150},
+ {"8015A", 150}, // 235
+ {"8015R", 150},
+ {"8015AR", 150},
+ {"8017", 120},
+ {"8017A", 120},
+ {"8017R", 120}, // 240
+ {"8017AR", 120},
+ {"8017N", 90},
+ {"8023", 135},
+ {"8023A", 135},
+ {"8023M", 135}, // 245
+ {"8024", 150},
+ {"8024A", 180},
+ {"8024M", 180},
+ {"8025", 150},
+ {"8025A", 150}, // 250
+ {"8025M", 150},
+ {"8027", 75},
+ {"8028", 75},
+ {"8029", 120},
+ {"8029A", 120}, // 255
+ {"8031", 375},
+ {"8032", 0},
+ {"8032A", 0},
+ {"8033", 105},
+ {"8035", 195}, // 260
+ {"8035A", 120},
+ {"8035B", 180},
+ {"8035C", 135},
+ {"8036", 105},
+ {"8037", 195}, // 265
+ {"8037A", 195},
+ {"8040", 240},
+ {"8040A", 240},
+ {"8041", 195},
+ {"8041A", 195}, // 270
+ {"8042", 600},
+ {"8042A", 600}
+};
+
+Action::Action(LastExpressEngine *engine) : _engine(engine) {
+ ADD_ACTION(dummy);
+ ADD_ACTION(inventory);
+ ADD_ACTION(savePoint);
+ ADD_ACTION(playSound);
+ ADD_ACTION(playMusic);
+ ADD_ACTION(knock);
+ ADD_ACTION(compartment);
+ ADD_ACTION(playSounds);
+ ADD_ACTION(playAnimation);
+ ADD_ACTION(openCloseObject);
+ ADD_ACTION(updateObjetLocation2);
+ ADD_ACTION(setItemLocation);
+ ADD_ACTION(knockNoSound);
+ ADD_ACTION(pickItem);
+ ADD_ACTION(dropItem);
+ ADD_ACTION(dummy);
+ ADD_ACTION(enterCompartment);
+ ADD_ACTION(dummy);
+ ADD_ACTION(getOutsideTrain);
+ ADD_ACTION(slip);
+ ADD_ACTION(getInsideTrain);
+ ADD_ACTION(climbUpTrain);
+ ADD_ACTION(climbDownTrain);
+ ADD_ACTION(jumpUpDownTrain);
+ ADD_ACTION(unbound);
+ ADD_ACTION(25);
+ ADD_ACTION(26);
+ ADD_ACTION(27);
+ ADD_ACTION(concertSitCough);
+ ADD_ACTION(29);
+ ADD_ACTION(catchBeetle);
+ ADD_ACTION(exitCompartment);
+ ADD_ACTION(32);
+ ADD_ACTION(useWhistle);
+ ADD_ACTION(openMatchBox);
+ ADD_ACTION(openBed);
+ ADD_ACTION(dummy);
+ ADD_ACTION(dialog);
+ ADD_ACTION(eggBox);
+ ADD_ACTION(39);
+ ADD_ACTION(bed);
+ ADD_ACTION(playMusicChapter);
+ ADD_ACTION(playMusicChapterSetupTrain);
+ ADD_ACTION(switchChapter);
+ ADD_ACTION(44);
+}
+
+Action::~Action() {
+ for (int i = 0; i < (int)_actions.size(); i++)
+ delete _actions[i];
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Processing hotspot
+//////////////////////////////////////////////////////////////////////////
+SceneIndex Action::processHotspot(const SceneHotspot &hotspot) {
+ if (!hotspot.action || hotspot.action >= (int)_actions.size())
+ return kSceneInvalid;
+
+ return (*_actions[hotspot.action])(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Actions
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+// Action 0
+IMPLEMENT_ACTION(dummy)
+ error("Action::action_dummy: Function should never be called (hotspot action: %d)!", hotspot.action);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 1
+IMPLEMENT_ACTION(inventory)
+ if (!getState()->sceneUseBackup)
+ return kSceneInvalid;
+
+ SceneIndex index = kSceneNone;
+ if (getState()->sceneBackup2) {
+ index = getState()->sceneBackup2;
+ getState()->sceneBackup2 = kSceneNone;
+ } else {
+ getState()->sceneUseBackup = false;
+ index = getState()->sceneBackup;
+
+ Scene *backup = getScenes()->get(getState()->sceneBackup);
+ if (getEntities()->getPosition(backup->car, backup->position))
+ index = getScenes()->processIndex(getState()->sceneBackup);
+ }
+
+ getScenes()->loadScene(index);
+
+ if (!getInventory()->getSelectedItem())
+ return kSceneInvalid;
+
+ if (!getInventory()->getSelectedEntry()->isSelectable || (!getState()->sceneBackup2 && getInventory()->getFirstExaminableItem()))
+ getInventory()->selectItem(getInventory()->getFirstExaminableItem());
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 2
+IMPLEMENT_ACTION(savePoint)
+ getSavePoints()->push(kEntityPlayer, (EntityIndex)hotspot.param1, (ActionIndex)hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 3
+IMPLEMENT_ACTION(playSound)
+
+ // Check that the file is not already buffered
+ if (hotspot.param2 || !getSound()->isBuffered(Common::String::printf("LIB%03d", hotspot.param1), true))
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 4
+IMPLEMENT_ACTION(playMusic)
+ // Check that the file is not already buffered
+ Common::String filename = Common::String::printf("MUS%03d", hotspot.param1);
+
+ if (!getSound()->isBuffered(filename) && (hotspot.param1 != 50 || getProgress().chapter == kChapter5))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 5
+IMPLEMENT_ACTION(knock)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(object).entity) {
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
+ } else {
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSoundEvent(kEntityPlayer, 12);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 6
+IMPLEMENT_ACTION(compartment)
+ ObjectIndex compartment = (ObjectIndex)hotspot.param1;
+
+ if (compartment >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(compartment).entity) {
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(compartment).entity, kActionOpenDoor, compartment);
+
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ if (handleOtherCompartment(compartment, true, true)) {
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ ObjectLocation location = getObjects()->get(compartment).location;
+ if (location == kObjectLocation1 || location == kObjectLocation3 || getEntities()->checkFields2(compartment)) {
+
+ if (location != kObjectLocation1 || getEntities()->checkFields2(compartment)
+ || (getInventory()->getSelectedItem() != kItemKey
+ && (compartment != kObjectCompartment1
+ || !getInventory()->hasItem(kItemKey)
+ || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))) {
+ if (!getSound()->isBuffered("LIB13"))
+ getSound()->playSoundEvent(kEntityPlayer, 13);
+
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ getSound()->playSoundEvent(kEntityPlayer, 32);
+
+ if ((compartment >= kObjectCompartment1 && compartment <= kObjectCompartment3) || (compartment >= kObjectCompartmentA && compartment <= kObjectCompartmentF))
+ getObjects()->update(compartment, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+ getInventory()->unselectItem();
+
+ return kSceneInvalid;
+ }
+
+ if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->getSelectedItem() != kItemKey) {
+ if (compartment == kObjectCageMax) {
+ getSound()->playSoundEvent(kEntityPlayer, 26);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+ }
+ return kSceneInvalid;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getSound()->playSoundEvent(kEntityPlayer, 16);
+ getInventory()->unselectItem();
+
+ // Stop processing further
+ return kSceneNone;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 7
+IMPLEMENT_ACTION(playSounds)
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1);
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param3, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 8
+IMPLEMENT_ACTION(playAnimation)
+ if (getEvent(hotspot.param1))
+ return kSceneInvalid;
+
+ playAnimation((EventIndex)hotspot.param1);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 9
+IMPLEMENT_ACTION(openCloseObject)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ getObjects()->update(object, getObjects()->get(object).entity, location, kCursorKeepValue, kCursorKeepValue);
+
+ bool isNotWindow = ((object <= kObjectCompartment8 || object >= kObjectHandleBathroom) && (object <= kObjectCompartmentH || object >= kObject48));
+
+ switch (location) {
+ default:
+ break;
+
+ case kObjectLocation1:
+ if (isNotWindow)
+ getSound()->playSoundEvent(kEntityPlayer, 24);
+ else
+ getSound()->playSoundEvent(kEntityPlayer, 21);
+ break;
+
+ case kObjectLocation2:
+ if (isNotWindow)
+ getSound()->playSoundEvent(kEntityPlayer, 36);
+ else
+ getSound()->playSoundEvent(kEntityPlayer, 20);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 10
+IMPLEMENT_ACTION(updateObjetLocation2)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ getObjects()->updateLocation2(object, location);
+
+ if (object != kObject112 || getSound()->isBuffered("LIB096")) {
+ if (object == 1)
+ getSound()->playSoundEvent(kEntityPlayer, 73);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 96);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 11
+IMPLEMENT_ACTION(setItemLocation)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ Inventory::InventoryEntry* entry = getInventory()->get(item);
+ if (!entry->isPresent)
+ return kSceneInvalid;
+
+ entry->location = (ObjectLocation)hotspot.param2;
+
+ if (item == kItemCorpse) {
+ ObjectLocation corpseLocation = getInventory()->get(kItemCorpse)->location;
+
+ if (corpseLocation == kObjectLocation3 || corpseLocation == kObjectLocation4)
+ getProgress().eventCorpseMovedFromFloor = true;
+ else
+ getProgress().eventCorpseMovedFromFloor = false;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 12
+IMPLEMENT_ACTION(knockNoSound)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(object).entity)
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 13
+IMPLEMENT_ACTION(pickItem)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+ bool process = (hotspot.scene == 0);
+ SceneIndex sceneIndex = kSceneInvalid;
+
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ Inventory::InventoryEntry* entry = getInventory()->get(item);
+ if (!entry->location)
+ return kSceneInvalid;
+
+ // Special case for corpse
+ if (item == kItemCorpse) {
+ pickCorpse(location, process);
+ return kSceneInvalid;
+ }
+
+ // Add and process items
+ getInventory()->addItem(item);
+
+ switch (item) {
+ default:
+ break;
+
+ case kItemGreenJacket:
+ pickGreenJacket(process);
+ break;
+
+ case kItemScarf:
+ pickScarf(process);
+
+ // stop processing
+ return kSceneInvalid;
+
+ case kItemParchemin:
+ if (location != kObjectLocation2)
+ break;
+
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getSound()->playSoundEvent(kEntityPlayer, 9);
+ break;
+
+ case kItemBomb:
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_pickBomb);
+ break;
+
+ case kItemBriefcase:
+ getSound()->playSoundEvent(kEntityPlayer, 83);
+ break;
+ }
+
+ // Load item scene
+ if (getInventory()->get(item)->scene) {
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneUseBackup = true;
+ getState()->sceneBackup = (hotspot.scene ? hotspot.scene : getState()->scene);
+ }
+
+ getScenes()->loadScene(getInventory()->get(item)->scene);
+
+ // do not process further
+ sceneIndex = kSceneNone;
+ }
+
+ // Select item
+ if (getInventory()->get(item)->isSelectable) {
+ getInventory()->selectItem(item);
+ _engine->getCursor()->setStyle(getInventory()->get(item)->cursor);
+ }
+
+ return sceneIndex;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 14
+IMPLEMENT_ACTION(dropItem)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+ bool process = (hotspot.scene == kSceneNone);
+
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ if (!getInventory()->hasItem(item))
+ return kSceneInvalid;
+
+ if (location < kObjectLocation1)
+ return kSceneInvalid;
+
+ // Handle actions
+ if (item == kItemBriefcase) {
+ getSound()->playSoundEvent(kEntityPlayer, 82);
+
+ if (location == kObjectLocation2) {
+ if (!getProgress().field_58) {
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getProgress().field_58 = 1;
+ }
+
+ if (getInventory()->get(kItemParchemin)->location == kObjectLocation2) {
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getSound()->playSoundEvent(kEntityPlayer, 9);
+ }
+ }
+ }
+
+ // Update item location
+ getInventory()->removeItem(item, location);
+
+ if (item == kItemCorpse)
+ dropCorpse(process);
+
+ // Unselect item
+ getInventory()->unselectItem();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 15: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 16
+IMPLEMENT_ACTION(enterCompartment)
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1 || getObjects()->get(kObjectCompartment1).location == kObjectLocation3 || getInventory()->getSelectedItem() == kItemKey)
+ return action_compartment(hotspot);
+
+ if (getProgress().eventCorpseFound) {
+ if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->get(kItemBriefcase)->location != kObjectLocation2)
+ return action_compartment(hotspot);
+
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+
+ if (getProgress().field_78 && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_78 = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 77);
+
+ return kSceneNone;
+ }
+
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ playAnimation(kEventCathFindCorpse);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getProgress().eventCorpseFound = true;
+
+ return kSceneCompartmentCorpse;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 17: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 18
+IMPLEMENT_ACTION(getOutsideTrain)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+
+ if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1)
+ && getProgress().isTrainRunning
+ && (object != kObjectOutsideAnnaCompartment || (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == kObjectLocation2))
+ && getInventory()->getSelectedItem() != kItemFirebird
+ && getInventory()->getSelectedItem() != kItemBriefcase) {
+
+ switch (object) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGoOutsideTylerCompartmentNight : kEventCathGoOutsideTylerCompartmentDay);
+ getProgress().field_C8 = 1;
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGoOutsideNight : kEventCathGoOutsideDay);
+ getProgress().field_C8 = 1;
+ break;
+
+ case kObjectOutsideAnnaCompartment:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
+ if (!hotspot.scene)
+ getScenes()->processScene();
+ break;
+ }
+ } else {
+ if (object == kObjectOutsideTylerCompartment || object == kObjectOutsideBetweenCompartments || object == kObjectOutsideAnnaCompartment) {
+ playAnimation(isNight() ? kEventCathLookOutsideWindowNight : kEventCathLookOutsideWindowDay);
+ getScenes()->processScene();
+ return kSceneNone;
+ }
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 19
+IMPLEMENT_ACTION(slip)
+ switch((ObjectIndex)hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ playAnimation(isNight() ? kEventCathSlipTylerCompartmentNight : kEventCathSlipTylerCompartmentDay);
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ playAnimation(isNight() ? kEventCathSlipNight : kEventCathSlipDay);
+ break;
+ }
+
+ getProgress().field_C8 = 0;
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 20
+IMPLEMENT_ACTION(getInsideTrain)
+ switch ((ObjectIndex)hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ playAnimation(isNight() ? kEventCathGetInsideTylerCompartmentNight : kEventCathGetInsideTylerCompartmentDay);
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
+ break;
+
+ case kObjectOutsideAnnaCompartment:
+ playAnimation(kEventCathGettingInsideAnnaCompartment);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 21
+IMPLEMENT_ACTION(climbUpTrain)
+ byte action = hotspot.param1;
+
+ if (action != 1 && action != 2)
+ return kSceneInvalid;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (action == 2)
+ playAnimation(kEventCathClimbUpTrainGreenJacket);
+
+ playAnimation(kEventCathTopTrainGreenJacket);
+ break;
+
+ case kChapter5:
+ if (action == 2)
+ playAnimation(getProgress().isNightTime ? kEventCathClimbUpTrainNoJacketNight : kEventCathClimbUpTrainNoJacketDay);
+
+ playAnimation(getProgress().isNightTime ? kEventCathTopTrainNoJacketNight : kEventCathTopTrainNoJacketDay);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 22
+IMPLEMENT_ACTION(climbDownTrain)
+ EventIndex evt = kEventNone;
+ switch (getProgress().chapter) {
+ default:
+ return kSceneInvalid;
+
+ case kChapter2:
+ case kChapter3:
+ evt = kEventCathClimbDownTrainGreenJacket;
+ break;
+
+ case kChapter5:
+ evt = (getProgress().isNightTime ? kEventCathClimbDownTrainNoJacketNight : kEventCathClimbDownTrainNoJacketDay);
+ break;
+ }
+
+ playAnimation(evt);
+ if (evt == kEventCathClimbDownTrainNoJacketDay)
+ getSound()->playSoundEvent(kEntityPlayer, 37);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 23
+IMPLEMENT_ACTION(jumpUpDownTrain)
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionBreakCeiling);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionJumpDownCeiling);
+ break;
+
+ case 3:
+ if (getInventory()->getSelectedItem() == kItemBriefcase) {
+ getInventory()->removeItem(kItemBriefcase, kObjectLocation3);
+ getSound()->playSoundEvent(kEntityPlayer, 82);
+ getInventory()->unselectItem();
+ }
+
+ // Show animation with or without briefcase
+ playAnimation((getInventory()->get(kItemBriefcase)->location - 3) ? kEventCathJumpUpCeilingBriefcase : kEventCathJumpUpCeiling);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ break;
+
+ case 4:
+ if (getProgress().chapter == kChapter1)
+ getSavePoints()->push(kEntityPlayer, kEntityKronos, kAction202621266);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 24
+IMPLEMENT_ACTION(unbound)
+ byte action = hotspot.param1;
+
+ switch (action) {
+ default:
+ break;
+
+ case 1:
+ playAnimation(kEventCathStruggleWithBonds);
+ if (hotspot.scene)
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ playAnimation(kEventCathBurnRope);
+ if (hotspot.scene)
+ getScenes()->processScene();
+ break;
+
+ case 3:
+ if (getEvent(kEventCathBurnRope)) {
+ playAnimation(kEventCathRemoveBonds);
+ getProgress().field_84 = 0;
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 89);
+ return kSceneNone;
+ }
+ break;
+
+ case 4:
+ if (!getEvent(kEventCathStruggleWithBonds2)) {
+ playAnimation(kEventCathStruggleWithBonds2);
+ getSound()->playSoundEvent(kEntityPlayer, 101);
+ getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation2);
+ if (!hotspot.scene)
+ getScenes()->processScene();
+ }
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityIvo, kAction192637492);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 25
+IMPLEMENT_ACTION(25)
+ switch(hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction272177921);
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered("MUS021"))
+ getSound()->playSound(kEntityPlayer, "MUS021", SoundManager::kFlagDefault);
+ break;
+
+ case 3:
+ getSound()->playSoundEvent(kEntityPlayer, 43);
+ if (!getInventory()->hasItem(kItemKey) && !getEvent(kEventAnnaBaggageArgument)) {
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_baggage);
+ return kSceneNone;
+ }
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 26
+IMPLEMENT_ACTION(26)
+ switch(hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction158610240);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225367984);
+ getInventory()->unselectItem();
+ return kSceneNone;
+
+ case 3:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction191001984);
+ return kSceneNone;
+
+ case 4:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction201959744);
+ return kSceneNone;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction169300225);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 27
+IMPLEMENT_ACTION(27)
+ if (!getSound()->isBuffered("LIB031", true))
+ getSound()->playSoundEvent(kEntityPlayer, 31);
+
+ switch (getEntityData(kEntityPlayer)->car) {
+ default:
+ break;
+
+ case kCarGreenSleeping:
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction225358684, hotspot.param1);
+ break;
+
+ case kCarRedSleeping:
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction225358684, hotspot.param1);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 28
+IMPLEMENT_ACTION(concertSitCough)
+ switch(hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case 1:
+ playAnimation(kEventConcertSit);
+ break;
+
+ case 2:
+ playAnimation(kEventConcertCough);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 29
+IMPLEMENT_ACTION(29)
+ getProgress().field_C = 1;
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
+
+ Common::String filename = Common::String::printf("MUS%03d", hotspot.param3);
+ if (!getSound()->isBuffered(filename))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 30
+IMPLEMENT_ACTION(catchBeetle)
+ if (!getBeetle()->isLoaded())
+ return kSceneInvalid;
+
+ if (getBeetle()->catchBeetle()) {
+ getBeetle()->unload();
+ getInventory()->get(kItemBeetle)->location = kObjectLocation1;
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionCatchBeetle);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 31
+IMPLEMENT_ACTION(exitCompartment)
+ if (!getProgress().field_30 && getProgress().jacket != kJacketOriginal) {
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getProgress().field_30 = 1;
+ }
+
+ getObjects()->updateLocation2(kObjectCompartment1, (ObjectLocation)hotspot.param2);
+
+ // fall to case enterCompartment action
+ return action_enterCompartment(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 32
+IMPLEMENT_ACTION(32)
+ switch(hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntitySalko, kAction167992577);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction202884544);
+ break;
+
+ case 3:
+ if (getProgress().chapter == kChapter5) {
+ getSavePoints()->push(kEntityPlayer, kEntityAbbot, kAction168646401);
+ getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction168646401);
+ } else {
+ getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction203339360);
+ }
+ // Stop processing further scenes
+ return kSceneNone;
+
+ case 4:
+ getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction169773228);
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction167992577);
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityPlayer, kEntityAugust, kAction203078272);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 33
+IMPLEMENT_ACTION(useWhistle)
+ EventIndex evt = kEventNone;
+ SceneIndex sceneIndex = kSceneInvalid;
+
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction205294778);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) {
+ evt = kEventCathOpenEgg;
+
+ Scene *scene = getScenes()->get(hotspot.scene);
+ if (scene->getHotspot())
+ sceneIndex = scene->getHotspot()->scene;
+
+ } else {
+ evt = kEventCathOpenEggNoBackground;
+ }
+ getProgress().isEggOpen = true;
+ break;
+
+ case 2:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction224309120);
+ break;
+ }
+
+ evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathCloseEgg : kEventCathCloseEggNoBackground;
+ getProgress().isEggOpen = false;
+ break;
+
+ case 3:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kActionUseWhistle);
+ break;
+ }
+
+ evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathUseWhistleOpenEgg : kEventCathUseWhistleOpenEggNoBackground;
+ break;
+
+ }
+
+ if (evt != kEventNone) {
+ playAnimation(evt);
+ if (sceneIndex == kSceneNone || !hotspot.scene)
+ getScenes()->processScene();
+ }
+
+ return sceneIndex;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 34
+IMPLEMENT_ACTION(openMatchBox)
+ // If the match is already in the inventory, do nothing
+ if (!getInventory()->get(kItemMatch)->location
+ || getInventory()->get(kItemMatch)->isPresent)
+ return kSceneInvalid;
+
+ getInventory()->addItem(kItemMatch);
+ getSound()->playSoundEvent(kEntityPlayer, 102);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 35
+IMPLEMENT_ACTION(openBed)
+ getSound()->playSoundEvent(kEntityPlayer, 59);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 36: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 37
+IMPLEMENT_ACTION(dialog)
+ getSound()->playDialog(kEntityTables4, (EntityIndex)hotspot.param1, SoundManager::kFlagDefault, 0);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 38
+IMPLEMENT_ACTION(eggBox)
+ getSound()->playSoundEvent(kEntityPlayer, 43);
+ if (getProgress().field_7C && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_7C = 0;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 39
+IMPLEMENT_ACTION(39)
+ getSound()->playSoundEvent(kEntityPlayer, 24);
+ if (getProgress().field_80 && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_80 = 0;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 40
+IMPLEMENT_ACTION(bed)
+ getSound()->playSoundEvent(kEntityPlayer, 85);
+ // falls to case knockNoSound
+ return action_knockNoSound(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 41
+IMPLEMENT_ACTION(playMusicChapter)
+ byte id = 0;
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ id = hotspot.param1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ id = hotspot.param2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ id = hotspot.param3;
+ break;
+ }
+
+ if (id) {
+ Common::String filename = Common::String::printf("MUS%03d", id);
+
+ if (!getSound()->isBuffered(filename))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 42
+IMPLEMENT_ACTION(playMusicChapterSetupTrain)
+ int id = 0;
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ id = 1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ id = 2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ id = 4;
+ break;
+ }
+
+ Common::String filename = Common::String::printf("MUS%03d", hotspot.param1);
+
+ if (!getSound()->isBuffered(filename) && hotspot.param3 & id) {
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+
+ getSavePoints()->call(kEntityPlayer, kEntityTrain, kAction203863200, filename.c_str());
+ getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction222746496, hotspot.param2);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// // Action 43
+IMPLEMENT_ACTION(switchChapter)
+ // Nothing to do here as an hotspot action
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 44
+IMPLEMENT_ACTION(44)
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityRebecca, kAction205034665);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225358684);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helper functions
+//////////////////////////////////////////////////////////////////////////
+void Action::pickGreenJacket(bool process) const {
+ getProgress().jacket = kJacketGreen;
+ getInventory()->addItem(kItemMatchBox);
+
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ playAnimation(kEventPickGreenJacket);
+
+ getInventory()->setPortrait(kPortraitGreen);
+
+ if (process)
+ getScenes()->processScene();
+}
+
+void Action::pickScarf(bool process) const {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventPickScarfGreen : kEventPickScarfOriginal);
+
+ if (process)
+ getScenes()->processScene();
+}
+
+void Action::pickCorpse(ObjectLocation bedPosition, bool process) const {
+
+ if (getProgress().jacket == kJacketOriginal)
+ getProgress().jacket = kJacketBlood;
+
+ switch(getInventory()->get(kItemCorpse)->location) {
+ // No way to pick the corpse
+ default:
+ break;
+
+ // Floor
+ case kObjectLocation1:
+ if (bedPosition != 4) {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickFloorGreen : kEventCorpsePickFloorOriginal);
+ break;
+ }
+
+ if (getProgress().jacket)
+ playAnimation(kEventCorpsePickFloorOpenedBedOriginal);
+
+ getInventory()->get(kItemCorpse)->location = kObjectLocation5;
+ break;
+
+ // Bed
+ case kObjectLocation2:
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickFloorGreen : kEventCorpsePickBedOriginal);
+ break;
+ }
+
+ if (process)
+ getScenes()->processScene();
+
+ // Add corpse to inventory
+ if (bedPosition != 4) { // bed position
+ getInventory()->addItem(kItemCorpse);
+ getInventory()->selectItem(kItemCorpse);
+ _engine->getCursor()->setStyle(kCursorCorpse);
+ }
+}
+
+void Action::dropCorpse(bool process) const {
+ switch(getInventory()->get(kItemCorpse)->location) {
+ default:
+ break;
+
+ case kObjectLocation1: // Floor
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropFloorGreen : kEventCorpseDropFloorOriginal);
+ break;
+
+ case kObjectLocation2: // Bed
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropBedGreen : kEventCorpseDropBedOriginal);
+ break;
+
+ case kObjectLocation4: // Window
+ // Say goodbye to an old friend
+ getInventory()->get(kItemCorpse)->location = kObjectLocationNone;
+ getProgress().eventCorpseThrown = true;
+
+ if (getState()->time <= kTime1138500) {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropWindowGreen : kEventCorpseDropWindowOriginal);
+
+ getProgress().field_24 = true;
+ } else {
+ playAnimation(kEventCorpseDropBridge);
+ }
+
+ getProgress().eventCorpseMovedFromFloor = true;
+ break;
+ }
+
+ if (process)
+ getScenes()->processScene();
+}
+
+bool Action::handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const {
+#define ENTITY_PARAMS(entity, index, id) \
+ ((EntityData::EntityParametersIIII*)getEntities()->get(entity)->getParamData()->getParameters(8, index))->param##id
+
+ // Only handle compartments
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideCompartment
+ || ((object < kObjectCompartment2 || object > kObjectCompartment8) && (object < kObjectCompartmentA || object > kObjectCompartmentH)))
+ return false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Gendarmes
+ if (getEntityData(kEntityPlayer)->car == getEntityData(kEntityGendarmes)->car
+ && getEntityData(kEntityGendarmes)->location == kLocationOutsideCompartment
+ && !getEntities()->compare(kEntityPlayer, kEntityGendarmes)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Mertens
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping
+ && getEntityData(kEntityMertens)->car == kCarGreenSleeping
+ && !getEntityData(kEntityMertens)->location
+ && !ENTITY_PARAMS(kEntityMertens, 0, 1)) {
+
+ if (!getEntities()->compare(kEntityPlayer, kEntityMertens)) {
+
+ if (getEntityData(kEntityMertens)->entityPosition < kPosition_2740
+ && getEntityData(kEntityMertens)->entityPosition > kPosition_850
+ && (getEntityData(kEntityCoudert)->car != kCarGreenSleeping || getEntityData(kEntityCoudert)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityVerges)->car != kCarGreenSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playWarningCompartment(kEntityMertens, object);
+
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction305159806);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ if (getEntityData(kEntityMertens)->direction == kDirectionUp
+ && getEntityData(kEntityMertens)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+ }
+
+ if (getEntityData(kEntityMertens)->direction == kDirectionDown
+ && getEntityData(kEntityMertens)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object, true);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Coudert
+ if (getEntityData(kEntityPlayer)->car != kCarRedSleeping
+ || !getEntityData(kEntityCoudert)->car
+ || getEntityData(kEntityCoudert)->location != kLocationOutsideCompartment
+ || ENTITY_PARAMS(kEntityCoudert, 0, 1))
+ return false;
+
+ if (!getEntities()->compare(kEntityPlayer, kEntityCoudert)) {
+
+ if (getEntityData(kEntityCoudert)->entityPosition < kPosition_2740
+ && getEntityData(kEntityCoudert)->entityPosition > kPosition_850
+ && (getEntityData(kEntityMertens)->car != kCarRedSleeping || getEntityData(kEntityMertens)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityVerges)->car != kCarRedSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityMmeBoutarel)->car != kCarRedSleeping || getEntityData(kEntityMmeBoutarel)->entityPosition > kPosition_2740)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playWarningCompartment(kEntityCoudert, object);
+
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction305159806);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ // Direction = Up
+ if (getEntityData(kEntityCoudert)->direction == kDirectionUp
+ && getEntityData(kEntityCoudert)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ // Direction = down
+ if (getEntityData(kEntityCoudert)->direction == kDirectionDown
+ && getEntityData(kEntityCoudert)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object, true);
+ }
+ }
+
+ return false;
+}
+
+void Action::playCompartmentSoundEvents(ObjectIndex object) const {
+ if (getObjects()->get(object).location == kObjectLocation1 || getObjects()->get(object).location == kObjectLocation3 || getEntities()->checkFields2(object)) {
+ getSound()->playSoundEvent(kEntityPlayer, 13);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 3);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Cursors
+//////////////////////////////////////////////////////////////////////////
+CursorStyle Action::getCursor(const SceneHotspot &hotspot) const {
+ // Simple cursor style
+ if (hotspot.cursor != kCursorProcess)
+ return (CursorStyle)hotspot.cursor;
+
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+
+ switch (hotspot.action) {
+ default:
+ return kCursorNormal;
+
+ case SceneHotspot::kActionInventory:
+ if (!getState()->sceneBackup2 && (getEvent(kEventKronosBringFirebird) || getProgress().isEggOpen))
+ return kCursorNormal;
+ else
+ return kCursorBackward;
+
+ case SceneHotspot::kActionKnockOnDoor:
+ warning("================================= DOOR %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+ else
+ return (CursorStyle)getObjects()->get(object).cursor;
+
+ case SceneHotspot::kAction12:
+ warning("================================= OBJECT %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getObjects()->get(object).entity)
+ return (CursorStyle)getObjects()->get(object).cursor;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionPickItem:
+ warning("================================= ITEM %03d =================================", object);
+ if (object >= kObjectCompartmentA)
+ return kCursorNormal;
+
+ if ((!getInventory()->getSelectedItem() || getInventory()->getSelectedEntry()->manualSelect)
+ && (object != kObject21 || getProgress().eventCorpseMovedFromFloor == 1))
+ return kCursorHand;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionDropItem:
+ warning("================================= ITEM %03d =================================", object);
+ if (object >= kObjectCompartmentA)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() != (InventoryItem)object)
+ return kCursorNormal;
+
+ if (object == kObject20 && hotspot.param2 == 4 && !getProgress().isTrainRunning)
+ return kCursorNormal;
+
+ if (object == kObjectHandleInsideBathroom && hotspot.param2 == 1 && getProgress().field_5C)
+ return kCursorNormal;
+
+ return (CursorStyle)getInventory()->getSelectedEntry()->cursor;
+
+ case SceneHotspot::kAction15:
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getProgress().isEqual(object, hotspot.param2))
+ return (CursorStyle)hotspot.param3;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionEnterCompartment:
+ if ((getInventory()->getSelectedItem() != kItemKey || getObjects()->get(kObjectCompartment1).location)
+ && (getObjects()->get(kObjectCompartment1).location != 1 || !getInventory()->hasItem(kItemKey)
+ || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))
+ goto LABEL_KEY;
+
+ return (CursorStyle)getInventory()->get(kItemKey)->cursor; // TODO is that always the same as kCursorKey ?
+
+ case SceneHotspot::kActionGetOutsideTrain:
+ if (getProgress().jacket != kJacketGreen)
+ return kCursorNormal;
+
+ if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowDay) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1)
+ && getProgress().isTrainRunning
+ && (object != kObjectOutsideAnnaCompartment || (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == 2))
+ && getInventory()->getSelectedItem() != kItemBriefcase && getInventory()->getSelectedItem() != kItemFirebird)
+ return kCursorForward;
+
+ return (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation2) ? kCursorNormal : kCursorMagnifier;
+
+ case SceneHotspot::kActionSlip:
+ return (getProgress().field_C8 < 1) ? kCursorNormal : kCursorLeft;
+
+ case SceneHotspot::kActionClimbUpTrain:
+ if (getProgress().isTrainRunning
+ && (getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3 || getProgress().chapter == kChapter5)
+ && getInventory()->getSelectedItem() != kItemFirebird
+ && getInventory()->getSelectedItem() != kItemBriefcase)
+ return kCursorUp;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionJumpUpDownTrain:
+ if (object != kObjectCompartment1)
+ return kCursorNormal;
+
+ return (getObjects()->get(kObjectCeiling).location < kObjectLocation1) ? kCursorHand : kCursorNormal;
+
+ case SceneHotspot::kActionUnbound:
+ if (hotspot.param2 != 2)
+ return kCursorNormal;
+
+ if (getEvent(kEventCathBurnRope) || !getEvent(kEventCathStruggleWithBonds2))
+ return kCursorNormal;
+
+ return kCursorHand;
+
+ case SceneHotspot::kActionCatchBeetle:
+ if (!getBeetle()->isLoaded())
+ return kCursorNormal;
+
+ if (!getBeetle()->isCatchable())
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() == kItemMatchBox && getInventory()->hasItem(kItemMatch))
+ return (CursorStyle)getInventory()->get(kItemMatchBox)->cursor;
+
+ return kCursorHandPointer;
+
+ case SceneHotspot::KActionUseWhistle:
+ if (object != kObjectCompartment3)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() == kItemWhistle)
+ return kCursorWhistle;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionOpenBed:
+ if (getProgress().chapter < kChapter2)
+ return kCursorHand;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionDialog:
+ if (getSound()->getDialogName((EntityIndex)object))
+ return kCursorHandPointer;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionBed:
+ if (getProgress().field_18 == 2 && !getProgress().field_E4
+ && (getState()->time > kTimeBedTime
+ || (getProgress().eventMetAugust && getProgress().field_CC
+ && (!getProgress().field_24 || getProgress().field_3C))))
+ return kCursorSleep;
+
+ return kCursorNormal;
+
+LABEL_KEY:
+ // Handle compartment action
+ case SceneHotspot::kActionCompartment:
+ case SceneHotspot::kActionExitCompartment:
+ warning("================================= DOOR %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() != kItemKey
+ || getObjects()->get(object).entity
+ || getObjects()->get(object).location != 1
+ || !getObjects()->get(object).cursor2
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields2(object))
+ return (CursorStyle)getObjects()->get(object).cursor2;
+ else
+ return (CursorStyle)getInventory()->get(kItemKey)->cursor;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Animation
+//////////////////////////////////////////////////////////////////////////
+
+// Play an animation and add delta time to global game time
+void Action::playAnimation(EventIndex index, bool debugMode) const {
+ if (index >= _animationListSize)
+ error("Action::playAnimation: invalid event index (value=%i, max=%i)", index, _animationListSize);
+
+ // In debug mode, just show the animation
+ if (debugMode) {
+ Animation animation;
+ if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis")))
+ animation.play();
+ return;
+ }
+
+ getFlags()->flag_3 = true;
+
+ // Hide cursor
+ _engine->getCursor()->show(false);
+
+ // Show inventory & hourglass
+ getInventory()->show();
+ getInventory()->showHourGlass();
+
+ if (!getFlags()->mouseRightClick) {
+
+ if (getGlobalTimer()) {
+ if (getSound()->isBuffered("TIMER")) {
+ getSound()->processEntry("TIMER");
+ setGlobalTimer(105);
+ }
+ }
+
+ bool processSound = false;
+ if (index >= kEventCorpseDropFloorOriginal
+ || index == kEventCathWakingUp
+ || index == kEventConcertCough
+ || index == kEventConcertSit
+ || index == kEventConcertLeaveWithBriefcase)
+ processSound = true;
+
+ Animation animation;
+ if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis") , processSound ? Animation::kFlagDefault : Animation::kFlagProcess))
+ animation.play();
+
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+ }
+
+ // Show cursor
+ _engine->getCursor()->show(true);
+
+ getEvent(index) = 1;
+
+ // Adjust game time
+ getState()->timeTicks += _animationList[index].time;
+ getState()->time += _animationList[index].time * getState()->timeDelta;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/action.h b/engines/lastexpress/game/action.h
new file mode 100644
index 0000000000..d8f81abfde
--- /dev/null
+++ b/engines/lastexpress/game/action.h
@@ -0,0 +1,135 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ACTION_H
+#define LASTEXPRESS_ACTION_H
+
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/func.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+#define DECLARE_ACTION(name) \
+ SceneIndex action_##name(const SceneHotspot &hotspot) const
+
+#define ADD_ACTION(name) \
+ _actions.push_back(new Functor1MemConst<const SceneHotspot &, SceneIndex, Action>(this, &Action::action_##name));
+
+#define IMPLEMENT_ACTION(name) \
+ SceneIndex Action::action_##name(const SceneHotspot &hotspot) const { \
+ debugC(6, kLastExpressDebugLogic, "Hotspot action: " #name "%s", hotspot.toString().c_str());
+
+class LastExpressEngine;
+class SceneHotspot;
+
+class Action {
+public:
+ Action(LastExpressEngine *engine);
+ ~Action();
+
+ // Hotspot action
+ SceneIndex processHotspot(const SceneHotspot &hotspot);
+
+ // Cursor
+ CursorStyle getCursor(const SceneHotspot &hotspot) const;
+
+ // Animation
+ void playAnimation(EventIndex index, bool debugMode = false) const;
+
+ // Compartment action
+ bool handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const;
+
+private:
+ typedef Common::Functor1<const SceneHotspot &, SceneIndex> ActionFunctor;
+
+ LastExpressEngine* _engine;
+ Common::Array<ActionFunctor *> _actions;
+
+ // Each action is of the form action_<name>(SceneHotspot *hotspot)
+ // - a pointer to each action is added to the _actions array
+ // - processHotspot simply calls the proper function given by the hotspot->action value
+ //
+ // Note: even though there are 44 actions, only 41 are used in processHotspot
+
+ DECLARE_ACTION(inventory);
+ DECLARE_ACTION(savePoint);
+ DECLARE_ACTION(playSound);
+ DECLARE_ACTION(playMusic);
+ DECLARE_ACTION(knock);
+ DECLARE_ACTION(compartment);
+ DECLARE_ACTION(playSounds);
+ DECLARE_ACTION(playAnimation);
+ DECLARE_ACTION(openCloseObject);
+ DECLARE_ACTION(updateObjetLocation2);
+ DECLARE_ACTION(setItemLocation);
+ DECLARE_ACTION(knockNoSound);
+ DECLARE_ACTION(pickItem);
+ DECLARE_ACTION(dropItem);
+ DECLARE_ACTION(enterCompartment);
+ DECLARE_ACTION(getOutsideTrain);
+ DECLARE_ACTION(slip);
+ DECLARE_ACTION(getInsideTrain);
+ DECLARE_ACTION(climbUpTrain);
+ DECLARE_ACTION(climbDownTrain);
+ DECLARE_ACTION(jumpUpDownTrain);
+ DECLARE_ACTION(unbound);
+ DECLARE_ACTION(25);
+ DECLARE_ACTION(26);
+ DECLARE_ACTION(27);
+ DECLARE_ACTION(concertSitCough);
+ DECLARE_ACTION(29);
+ DECLARE_ACTION(catchBeetle);
+ DECLARE_ACTION(exitCompartment);
+ DECLARE_ACTION(32);
+ DECLARE_ACTION(useWhistle);
+ DECLARE_ACTION(openMatchBox);
+ DECLARE_ACTION(openBed);
+ DECLARE_ACTION(dialog);
+ DECLARE_ACTION(eggBox);
+ DECLARE_ACTION(39);
+ DECLARE_ACTION(bed);
+ DECLARE_ACTION(playMusicChapter);
+ DECLARE_ACTION(playMusicChapterSetupTrain);
+ DECLARE_ACTION(switchChapter);
+ DECLARE_ACTION(44);
+
+ // Special dummy function
+ DECLARE_ACTION(dummy);
+
+ // Helpers
+ void pickGreenJacket(bool process) const;
+ void pickScarf(bool process) const;
+ void pickCorpse(ObjectLocation bedPosition, bool process) const;
+ void dropCorpse(bool process) const;
+
+ void playCompartmentSoundEvents(ObjectIndex object) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ACTION_H
diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp
new file mode 100644
index 0000000000..665edb79a5
--- /dev/null
+++ b/engines/lastexpress/game/beetle.cpp
@@ -0,0 +1,517 @@
+/* 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/beetle.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+Beetle::Beetle(LastExpressEngine *engine) : _engine(engine), _data(NULL) {}
+
+Beetle::~Beetle() {
+ SAFE_DELETE(_data);
+
+ // Free passed pointers
+ _engine = NULL;
+}
+
+void Beetle::load() {
+ // Only load in chapter 2 & 3
+ if (getProgress().chapter != kChapter2 && getProgress().chapter != kChapter3)
+ return;
+
+ // Already loaded
+ if (_data)
+ return;
+
+ // Do not load if beetle is in the wrong location
+ if (getInventory()->get(kItemBeetle)->location != kObjectLocation3)
+ return;
+
+ ///////////////////////
+ // Load Beetle data
+ _data = new BeetleData();
+
+ // Load sequences
+ _data->sequences.push_back(loadSequence("BW000.seq")); // 0
+ _data->sequences.push_back(loadSequence("BT000045.seq"));
+ _data->sequences.push_back(loadSequence("BT045000.seq"));
+ _data->sequences.push_back(loadSequence("BW045.seq"));
+ _data->sequences.push_back(loadSequence("BT045090.seq"));
+ _data->sequences.push_back(loadSequence("BT090045.seq")); // 5
+ _data->sequences.push_back(loadSequence("BW090.seq"));
+ _data->sequences.push_back(loadSequence("BT090135.seq"));
+ _data->sequences.push_back(loadSequence("BT135090.seq"));
+ _data->sequences.push_back(loadSequence("BW135.seq"));
+ _data->sequences.push_back(loadSequence("BT135180.seq")); // 10
+ _data->sequences.push_back(loadSequence("BT180135.seq"));
+ _data->sequences.push_back(loadSequence("BW180.seq"));
+ _data->sequences.push_back(loadSequence("BT180225.seq"));
+ _data->sequences.push_back(loadSequence("BT225180.seq"));
+ _data->sequences.push_back(loadSequence("BW225.seq")); // 15
+ _data->sequences.push_back(loadSequence("BT225270.seq"));
+ _data->sequences.push_back(loadSequence("BT270225.seq"));
+ _data->sequences.push_back(loadSequence("BW270.seq"));
+ _data->sequences.push_back(loadSequence("BT270315.seq"));
+ _data->sequences.push_back(loadSequence("BT315270.seq")); // 20
+ _data->sequences.push_back(loadSequence("BW315.seq"));
+ _data->sequences.push_back(loadSequence("BT315000.seq"));
+ _data->sequences.push_back(loadSequence("BT000315.seq"));
+ _data->sequences.push_back(loadSequence("BA135.seq"));
+ _data->sequences.push_back(loadSequence("BL045.seq")); // 25
+ _data->sequences.push_back(loadSequence("BL000.seq"));
+ _data->sequences.push_back(loadSequence("BL315.seq"));
+ _data->sequences.push_back(loadSequence("BL180.seq"));
+
+ // Init fields
+ _data->field_74 = 0;
+
+ // Check that all sequences are loaded properly
+ _data->isLoaded = true;
+ for (int i = 0; i < (int)_data->sequences.size(); i++) {
+ if (!_data->sequences[i]->isLoaded()) {
+ _data->isLoaded = false;
+ break;
+ }
+ }
+
+ _data->field_D9 = 10;
+ _data->coordOffset = 5;
+ _data->coordY = 178;
+ _data->currentSequence = 0;
+ _data->offset = 0;
+ _data->frame = NULL;
+ _data->field_D5 = 0;
+ _data->indexes[0] = 29;
+ _data->field_DD = 0;
+}
+
+void Beetle::unload() {
+ // Remove sequences from display list
+ if (_data)
+ getScenes()->removeFromQueue(_data->frame);
+
+ // Delete all loaded sequences
+ SAFE_DELETE(_data);
+}
+
+bool Beetle::isLoaded() const {
+ if (!_data)
+ return false;
+
+ return _data->isLoaded;
+}
+
+bool Beetle::catchBeetle() {
+ if (!_data)
+ error("Beetle::catchBeetle: sequences have not been loaded!");
+
+ if (getInventory()->getSelectedItem() == kItemMatchBox
+ && getInventory()->hasItem(kItemMatch)
+ && ABS((int16)(getCoords().x - _data->coordX)) < 10
+ && ABS((int16)(getCoords().y - _data->coordY)) < 10) {
+ return true;
+ }
+
+ _data->field_D5 = 0;
+ move();
+
+ return false;
+}
+
+bool Beetle::isCatchable() const {
+ if (!_data)
+ error("Beetle::isCatchable: sequences have not been loaded!");
+
+ return (_data->indexes[_data->offset] >= 30);
+}
+
+void Beetle::update() {
+ if (!_data)
+ error("Beetle::update: sequences have not been loaded!");
+
+ if (!_data->isLoaded)
+ return;
+
+ move();
+
+ if (_data->field_D5)
+ _data->field_D5--;
+
+ if (_data->currentSequence && _data->indexes[_data->offset] != 29) {
+ drawUpdate();
+ return;
+ }
+
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ if ((!_data->field_DD && rnd(10) < 1)
+ || (_data->field_DD && rnd(30) < 1)
+ || rnd(100) < 1) {
+
+ _data->field_DD++;
+ if (_data->field_DD > 3)
+ _data->field_DD = 0;
+
+ updateData(24);
+
+ _data->coordX = (int16)(rnd(250) + 190);
+ _data->coordOffset = (int16)(rnd(5) + 5);
+
+ if (_data->field_D9 > 1)
+ _data->field_D9--;
+
+ drawUpdate();
+ }
+ }
+}
+
+void Beetle::drawUpdate() {
+ if (!_data)
+ error("Beetle::drawUpdate: sequences have not been loaded!");
+
+ if (_data->frame != NULL) {
+ getScenes()->setCoordinates(_data->frame);
+ getScenes()->removeFromQueue(_data->frame);
+ }
+
+ // Update current frame
+ switch (_data->indexes[_data->offset]) {
+ default:
+ _data->currentFrame += 10;
+ break;
+
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ _data->currentFrame++;
+ break;
+ }
+
+ // Update current sequence
+ if (_data->currentSequence->count() <= _data->currentFrame) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ _data->offset++;
+ _data->currentSequence = _data->sequences[_data->indexes[_data->offset]];
+ break;
+
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ break;
+ }
+
+ _data->currentFrame = 0;
+ if (_data->indexes[_data->offset] == 29) {
+ SAFE_DELETE(_data->frame);
+ _data->currentSequence = NULL; // pointer to existing sequence
+ return;
+ }
+ }
+
+ // Update coordinates
+ switch (_data->indexes[_data->offset]) {
+ default:
+ break;
+
+ case 0:
+ _data->coordY -= _data->coordOffset;
+ break;
+
+ case 3:
+ _data->coordX += _data->coordOffset;
+ _data->coordY -= _data->coordOffset;
+ break;
+
+ case 6:
+ _data->coordX += _data->coordOffset;
+ break;
+
+ case 9:
+ _data->coordX += _data->coordOffset;
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 12:
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 15:
+ _data->coordX -= _data->coordOffset;
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 18:
+ _data->coordX -= _data->coordOffset;
+ break;
+
+ case 21:
+ _data->coordX -= _data->coordOffset;
+ _data->coordY -= _data->coordOffset;
+ break;
+ }
+
+ // Update beetle data
+ int rnd = rnd(100);
+ if (_data->coordX < 165 || _data->coordX > 465) {
+ uint index = 0;
+
+ if (rnd >= 30) {
+ if (rnd >= 70)
+ index = (_data->coordX < 165) ? 9 : 15;
+ else
+ index = (_data->coordX < 165) ? 6 : 18;
+ } else {
+ index = (_data->coordX < 165) ? 3 : 21;
+ }
+
+ updateData(index);
+ }
+
+ if (_data->coordY < 178) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ updateData(26);
+ break;
+
+ case 3:
+ updateData(25);
+ break;
+
+ case 21:
+ updateData(27);
+ break;
+ }
+ }
+
+ if (_data->coordY > 354) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ break;
+
+ case 9:
+ case 12:
+ case 15:
+ updateData(28);
+ break;
+ }
+ }
+
+#define INVERT_Y() \
+ switch (_data->indexes[_data->offset]) { \
+ default: \
+ break; \
+ case 24: \
+ case 25: \
+ case 26: \
+ case 27: \
+ case 28: \
+ _data->coordY = -_data->coordY; \
+ break; \
+ }
+
+ // Invert direction
+ INVERT_Y();
+
+ SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame);
+ updateFrame(frame);
+
+ INVERT_Y();
+
+ getScenes()->addToQueue(frame);
+
+ SAFE_DELETE(_data->frame);
+ _data->frame = frame;
+}
+
+void Beetle::move() {
+ if (!_data)
+ error("Beetle::move: sequences have not been loaded!");
+
+ if (_data->indexes[_data->offset] >= 24 && _data->indexes[_data->offset] <= 29)
+ return;
+
+ if (_data->field_D5)
+ return;
+
+ if (ABS((int)(getCoords().x - _data->coordX)) > 35)
+ return;
+
+ if (ABS((int)(getCoords().y - _data->coordY)) > 35)
+ return;
+
+ int32 deltaX = getCoords().x - _data->coordX;
+ int32 deltaY = -getCoords().y - _data->coordY;
+ uint32 index = 0;
+
+ // FIXME: check code path
+ if (deltaX >= 0) {
+ if (deltaY > 0) {
+ if (100 * deltaY - 241 * deltaX <= 0) {
+ if (100 * deltaY - 41 * deltaX <= 0)
+ index = 18;
+ else
+ index = 15;
+ } else {
+ index = 12;
+ }
+
+ goto update_data;
+ }
+ }
+
+ if (deltaX < 0) {
+
+ if (deltaY > 0) {
+ if (100 * deltaY + 241 * deltaX <= 0) {
+ if (100 * deltaY + 41 * deltaX <= 0)
+ index = 6;
+ else
+ index = 9;
+ } else {
+ index = 12;
+ }
+
+ goto update_data;
+ }
+
+ if (deltaY <= 0) {
+ if (100 * deltaY - 41 * deltaX <= 0) {
+ if (100 * deltaY - 241 * deltaX <= 0)
+ index = 0;
+ else
+ index = 3;
+ } else {
+ index = 6;
+ }
+
+ goto update_data;
+ }
+ }
+
+update_data:
+ updateData(index);
+
+ if (_data->coordOffset >= 15) {
+ _data->field_D5 = 0;
+ return;
+ }
+
+ _data->coordOffset = _data->coordOffset + (int16)(4 * rnd(100)/100 + _data->field_D9);
+ _data->field_D5 = 0;
+}
+
+// Update the beetle sequence to show the correct frames in the correct place
+void Beetle::updateFrame(SequenceFrame *frame) const {
+ if (!_data)
+ error("Beetle::updateSequence: sequences have not been loaded!");
+
+ if (!frame)
+ return;
+
+ // Update coordinates
+ if (_data->coordX > 0)
+ frame->getInfo()->xPos1 = (uint16)_data->coordX;
+
+ if (_data->coordY > 0)
+ frame->getInfo()->yPos1 = (uint16)_data->coordY;
+}
+
+void Beetle::updateData(uint32 index) {
+ if (!_data)
+ error("Beetle::updateData: sequences have not been loaded!");
+
+ if (!_data->isLoaded)
+ return;
+
+ if (index == 25 || index == 26 || index == 27 || index == 28) {
+ _data->indexes[0] = index;
+ _data->indexes[1] = 29;
+ _data->offset = 0;
+
+ _data->currentSequence = _data->sequences[index];
+ _data->currentFrame = 0;
+ _data->index = index;
+ } else {
+ if (!_data->sequences[index])
+ return;
+
+ if (_data->index == index)
+ return;
+
+ _data->offset = 0;
+
+ // Special case for sequence 24
+ if (index == 24) {
+ _data->indexes[0] = index;
+ _data->coordY = 178;
+ _data->index = _data->indexes[1];
+ _data->indexes[1] = (_data->coordX >= 265) ? 15 : 9;
+ _data->currentFrame = 0;
+ _data->currentSequence = _data->sequences[index];
+ } else {
+ if (index <= _data->index) {
+ for (uint32 i = _data->index - 1; i > index; ++_data->offset) {
+ _data->indexes[_data->offset] = i;
+ i -= 3;
+ }
+ } else {
+ for (uint32 i = _data->index + 1; i < index; ++_data->offset) {
+ _data->indexes[_data->offset] = i;
+ i += 3;
+ }
+ }
+
+ _data->index = index;
+ _data->indexes[_data->offset] = index;
+ _data->currentFrame = 0;
+ _data->offset = 0;
+ _data->currentSequence = _data->sequences[_data->indexes[0]];
+ }
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/beetle.h b/engines/lastexpress/game/beetle.h
new file mode 100644
index 0000000000..ece5866b85
--- /dev/null
+++ b/engines/lastexpress/game/beetle.h
@@ -0,0 +1,118 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_BEETLE_H
+#define LASTEXPRESS_BEETLE_H
+
+#include "lastexpress/data/sequence.h"
+
+#include "common/array.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Beetle {
+public:
+
+ Beetle(LastExpressEngine *engine);
+ ~Beetle();
+
+ void update();
+
+ void load();
+ void unload();
+
+ bool isLoaded() const;
+
+ bool catchBeetle();
+ bool isCatchable() const;
+
+private:
+ struct BeetleData {
+ Common::Array<Sequence *> sequences;
+
+ uint32 field_74;
+ Sequence *currentSequence;
+ uint32 currentFrame;
+ uint32 index;
+ int16 coordOffset;
+ int16 field_86;
+
+ int16 coordX;
+ int16 coordY;
+
+ uint32 indexes[16];
+
+ uint32 offset;
+ SequenceFrame* frame;
+ bool isLoaded;
+ uint32 field_D5;
+ uint32 field_D9;
+ uint32 field_DD;
+
+ BeetleData() {
+ field_74 = 0;
+ currentSequence = NULL;
+ currentFrame = 0;
+ index = 0;
+ coordOffset = 0;
+
+ field_86 = 0;
+
+ coordX = 0;
+ coordY = 0;
+
+ memset(indexes, 0, sizeof(indexes));
+ offset = 0;
+
+ frame = NULL;
+ isLoaded = false;
+ field_D5 = 0;
+ field_D9 = 0;
+ field_DD = 0;
+ }
+
+ ~BeetleData() {
+ for (int i = 0; i < (int)sequences.size(); i++)
+ if (sequences[i])
+ delete sequences[i];
+ }
+ };
+
+ LastExpressEngine* _engine;
+
+ BeetleData *_data;
+
+ void move();
+ void updateFrame(SequenceFrame *frame) const;
+ void updateData(uint32 index);
+ void drawUpdate();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BEETLE_H
diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp
new file mode 100644
index 0000000000..ef21fee0ae
--- /dev/null
+++ b/engines/lastexpress/game/entities.cpp
@@ -0,0 +1,2731 @@
+/* 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) {
+ int index = 100 * car + position;
+
+ if (car < 0 || car > 10)
+ error("Entities::getPosition: trying to access an invalid car (was: %d, valid:0-9)", car);
+
+ if (position < 0 || 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) {
+ 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) {
+ 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 &ser) {
+ _header->saveLoadWithSerializer(ser);
+ for (uint i = 1; i < _entities.size(); i++)
+ _entities[i]->saveLoadWithSerializer(ser);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 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) {
+ 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) {
+ 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
diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h
new file mode 100644
index 0000000000..40d7025b85
--- /dev/null
+++ b/engines/lastexpress/game/entities.h
@@ -0,0 +1,378 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITIES_H
+#define LASTEXPRESS_ENTITIES_H
+
+/*
+ Entities
+ --------
+
+ The entities structure contains 40 Entity_t structures for each entity
+
+*/
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/rect.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+
+class Entities : Common::Serializable {
+public:
+ Entities(LastExpressEngine *engine);
+ ~Entities();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+ void setup(bool isFirstChapter, EntityIndex entity);
+ void setupChapter(ChapterIndex chapter);
+ void reset();
+
+ // Update & drawing
+
+ /**
+ * Reset an entity state
+ *
+ * @param entity entity index
+ * @note remember to call the function pointer (we do not pass it our implementation)
+ */
+ void resetState(EntityIndex entity);
+ void updateFields() const;
+ void updateSequences() const;
+ void updateCallbacks();
+
+ EntityIndex canInteractWith(const Common::Point &point) const;
+ bool compare(EntityIndex entity1, EntityIndex entity2);
+
+ /**
+ * Update an entity current sequence frame (and related fields)
+ *
+ * @param entity entity index
+ */
+ void updateFrame(EntityIndex entity) const;
+ void updatePositionEnter(EntityIndex entity, CarIndex car, Position position);
+ void updatePositionExit(EntityIndex entity, CarIndex car, Position position);
+ void enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1 = false);
+ void exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1 = false);
+
+ // Sequences
+ void drawSequenceLeft(EntityIndex index, const char* sequence) const;
+ void drawSequenceRight(EntityIndex index, const char* sequence) const;
+ void clearSequences(EntityIndex index) const;
+
+ bool updateEntity(EntityIndex entity, CarIndex car, EntityPosition position);
+ bool hasValidFrame(EntityIndex entity) const;
+
+ // Accessors
+ Entity *get(EntityIndex entity);
+ EntityData::EntityCallData *getData(EntityIndex entity) const;
+ int getPosition(CarIndex car, Position position);
+ int getCompartments(int index);
+ int getCompartments1(int index);
+
+ // Scene
+ void loadSceneFromEntityPosition(CarIndex car, EntityPosition position, bool alternate = false) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Checks
+ //////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Query if 'entity' is inside a compartment
+ *
+ * @param entity The entity.
+ * @param car The car.
+ * @param position The position.
+ *
+ * @return true if inside the compartment, false if not.
+ */
+ bool isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const;
+
+ bool checkFields2(ObjectIndex object) const;
+
+ /**
+ * Query if 'entity' is in compartment cars.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in compartment cars, false if not.
+ */
+ bool isInsideCompartments(EntityIndex entity) const;
+
+ /**
+ * Query if the player is in the specified position
+ *
+ * @param car The car.
+ * @param position The position.
+ * @return true if player is in that position, false if not.
+ */
+ bool isPlayerPosition(CarIndex car, Position position) const;
+
+ /**
+ * Query if 'entity' is inside a train car
+ *
+ * @param entity The entity.
+ * @param car The car.
+ *
+ * @return true if inside a train car, false if not.
+ */
+ bool isInsideTrainCar(EntityIndex entity, CarIndex car) const;
+
+ /**
+ * Query if 'entity' is in green car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the green car entrance, false if not.
+ */
+ bool isInGreenCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Query if the player is in a specific car
+ *
+ * @param car The car.
+ *
+ * @return true if player is in the car, false if not.
+ */
+ bool isPlayerInCar(CarIndex car) const;
+
+ /**
+ * Query if 'entity' is going in the up or down direction.
+ *
+ * @param entity The entity.
+ *
+ * @return true if direction is up or down, false if not.
+ */
+ bool isDirectionUpOrDown(EntityIndex entity) const;
+
+ /**
+ * Query if the distance between the two entities is less 'distance'
+ *
+ * @param entity1 The first entity.
+ * @param entity2 The second entity.
+ * @param distance The distance.
+ *
+ * @return true if the distance between entities is less than 'distance', false if not.
+ */
+ bool isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const;
+
+ bool checkFields10(EntityIndex entity) const;
+
+ /**
+ * Query if there is somebody in the restaurant or salon.
+ *
+ * @return true if somebody is in the restaurant or salon, false if not.
+ */
+ bool isSomebodyInsideRestaurantOrSalon() const;
+
+ /**
+ * Query if 'entity' is in the salon.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the salon, false if not.
+ */
+ bool isInSalon(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in the restaurant.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the restaurant, false if not.
+ */
+ bool isInRestaurant(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos salon.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos salon, false if not.
+ */
+ bool isInKronosSalon(EntityIndex entity) const;
+
+ /**
+ * Query if the player is outside Alexei window.
+ *
+ * @return true if outside alexei window, false if not.
+ */
+ bool isOutsideAlexeiWindow() const;
+
+ /**
+ * Query if the player is outside Anna window.
+ *
+ * @return true if outside anna window, false if not.
+ */
+ bool isOutsideAnnaWindow() const;
+
+ /**
+ * Query if 'entity' is in the kitchen.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the kitchen, false if not.
+ */
+ bool isInKitchen(EntityIndex entity) const;
+
+ /**
+ * Query if nobody is in a compartment at that position.
+ *
+ * @param car The car.
+ * @param position The position.
+ *
+ * @return true if nobody is in a compartment, false if not.
+ */
+ bool isNobodyInCompartment(CarIndex car, EntityPosition position) const;
+
+ bool checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const;
+
+ /**
+ * Query if 'entity' is in the baggage car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the baggage car entrance, false if not.
+ */
+ bool isInBaggageCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in the baggage car.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the baggage car, false if not.
+ */
+ bool isInBaggageCar(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos sanctum.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos sanctum, false if not.
+ */
+ bool isInKronosSanctum(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos car entrance, false if not.
+ */
+ bool isInKronosCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Check distance from position.
+ *
+ * @param entity The entity.
+ * @param position The position.
+ * @param distance The distance.
+ *
+ * @return true if distance is bigger, false otherwise.
+ */
+ bool checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const;
+
+ /**
+ * Query if 'entity' is walking opposite to player.
+ *
+ * @param entity The entity.
+ *
+ * @return true if walking opposite to player, false if not.
+ */
+ bool isWalkingOppositeToPlayer(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is female.
+ *
+ * @param entity The entity.
+ *
+ * @return true if female, false if not.
+ */
+ static bool isFemale(EntityIndex entity);
+
+ /**
+ * Query if 'entity' is married.
+ *
+ * @param entity The entity.
+ *
+ * @return true if married, false if not.
+ */
+ static bool isMarried(EntityIndex entity);
+
+private:
+ static const int _compartmentsCount = 16;
+ static const int _positionsCount = 100 * 10; // 100 positions per train car
+
+ LastExpressEngine *_engine;
+ EntityData *_header;
+ Common::Array<Entity *> _entities;
+
+ // Compartments & positions
+ uint _compartments[_compartmentsCount];
+ uint _compartments1[_compartmentsCount];
+ uint _positions[_positionsCount];
+
+ void executeCallbacks();
+ void processEntity(EntityIndex entity);
+
+ void drawSequence(EntityIndex entity, const char* sequence, EntityDirection direction) const;
+ void drawSequences(EntityIndex entity, EntityDirection direction, bool loadSequence) const;
+ void loadSequence2(EntityIndex entity, Common::String sequenceName, Common::String sequenceName2, byte field30, bool loadSequence) const;
+
+ void clearEntitySequenceData(EntityData::EntityCallData * data, EntityDirection direction) const;
+ void computeCurrentFrame(EntityIndex entity) const;
+ int16 getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const;
+ void processFrame(EntityIndex entity, bool keepPreviousFrame, bool dontPlaySound);
+ void drawNextSequence(EntityIndex entity) const;
+ void updateEntityPosition(EntityIndex entity) const;
+ void copySequenceData(EntityIndex entity) const;
+
+ bool changeCar(EntityData::EntityCallData * data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const;
+
+ void getSequenceName(EntityIndex entity, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const;
+
+ void updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4);
+ void updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2);
+
+ void resetSequences(EntityIndex entity) const;
+
+ bool checkPosition(EntityPosition position) const;
+ bool checkSequenceFromPosition(EntityIndex entity) const;
+ EntityPosition getEntityPositionFromCurrentPosition() const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITIES_H
diff --git a/engines/lastexpress/game/fight.cpp b/engines/lastexpress/game/fight.cpp
new file mode 100644
index 0000000000..8fa711df1c
--- /dev/null
+++ b/engines/lastexpress/game/fight.cpp
@@ -0,0 +1,1587 @@
+/* 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/fight.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.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"
+
+#include "common/func.h"
+
+namespace LastExpress {
+
+#define CALL_FUNCTION0(fighter, name) \
+ (*fighter->name)(fighter)
+
+#define CALL_FUNCTION1(fighter, name, a) \
+ (*fighter->name)(fighter, a)
+
+#define REGISTER_PLAYER_FUNCTIONS(name) \
+ if (!_data) \
+ error("Fight::load##namePlayer - invalid data!"); \
+ _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction##name); \
+ _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update##name); \
+ _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract##name);
+
+#define REGISTER_OPPONENT_FUNCTIONS(name) \
+ if (!_data) \
+ error("Fight::load##nameOpponent - invalid data!"); \
+ _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleOpponentAction##name); \
+ _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponent##name); \
+ _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+#define CHECK_SEQUENCE2(fighter, value) \
+ (fighter->frame->getInfo()->field_33 & value)
+
+Fight::Fight(LastExpressEngine *engine) : _engine(engine), _data(NULL), _endType(kFightEndLost), _state(0), _handleTimer(false) {}
+
+Fight::~Fight() {
+ clearData();
+ _data = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Events
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::eventMouse(const Common::Event &ev) {
+ if (!_data || _data->index)
+ return;
+
+ // TODO move all the egg handling to inventory functions
+
+ getFlags()->mouseLeftClick = false;
+ getFlags()->shouldRedraw = false;
+ getFlags()->mouseRightClick = false;
+
+ if (ev.mouse.x < 608 || ev.mouse.y < 448 || ev.mouse.x >= 640 || ev.mouse.x >= 480) {
+
+ // Handle right button click
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ getSound()->removeFromQueue(kEntityTables0);
+ setStopped();
+
+ getGlobalTimer() ? _state = 0 : ++_state;
+
+ getFlags()->mouseRightClick = true;
+ }
+
+ if (_handleTimer) {
+ // Timer expired => show with full brightness
+ if (!getGlobalTimer())
+ getInventory()->drawEgg();
+
+ _handleTimer = false;
+ }
+
+ // Check hotspots
+ Scene *scene = getScenes()->get(getState()->scene);
+ SceneHotspot *hotspot = NULL;
+
+ if (!scene->checkHotSpot(ev.mouse, &hotspot)) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ } else {
+ _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor);
+
+ // Call player function
+ if (CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) {
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ CALL_FUNCTION1(_data->player, handleAction, (FightAction)hotspot->action);
+ } else {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ }
+ }
+ } else {
+ // Handle clicks on menu icon
+
+ if (!_handleTimer) {
+ // Timer expired => show with full brightness
+ if (!getGlobalTimer())
+ getInventory()->drawEgg();
+
+ _handleTimer = true;
+ }
+
+ // Stop fight if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ _handleTimer = false;
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndExit);
+ }
+
+ // Reset timer on right click
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ if (getGlobalTimer()) {
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+
+ setGlobalTimer(900);
+ }
+ }
+ }
+
+ getFlags()->shouldRedraw = true;
+}
+
+void Fight::eventTick(const Common::Event &ev) {
+ handleTick(ev, true);
+}
+
+void Fight::handleTick(const Common::Event &ev, bool isProcessing) {
+ // TODO move all the egg handling to inventory functions
+
+ // Blink egg
+ if (getGlobalTimer()) {
+ warning("Fight::handleMouseMove - egg blinking not implemented!");
+ }
+
+ if (!_data || _data->index)
+ return;
+
+ SceneHotspot *hotspot = NULL;
+ if (!getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot) || !CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ } else {
+ _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor);
+ }
+
+ CALL_FUNCTION0(_data->player, update);
+ CALL_FUNCTION0(_data->opponent, update);
+
+ // Draw sequences
+ if (!_data->isRunning)
+ return;
+
+ if (isProcessing)
+ getScenes()->drawFrames(true);
+
+ if (_data->index) {
+ // Set next sequence name index
+ _data->index--;
+ _data->sequences[_data->index] = loadSequence(_data->names[_data->index]);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+
+Fight::FightEndType Fight::setup(FightType type) {
+ if (_data)
+ error("Fight::setup - calling fight setup again while a fight is already in progress!");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Prepare UI & state
+ if (_state >= 5 && (type == kFightSalko || type == kFightVesna)) {
+ _state = 0;
+ return kFightEndWin;
+ }
+
+ getInventory()->showHourGlass();
+ // TODO events function
+ getFlags()->flag_0 = false;
+ getFlags()->mouseRightClick = false;
+ getEntities()->reset();
+
+ // Compute scene to use
+ SceneIndex sceneIndex;
+ switch(type) {
+ default:
+ sceneIndex = kSceneFightDefault;
+ break;
+
+ case kFightMilos:
+ sceneIndex = (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation3) ? kSceneFightMilos : kSceneFightMilosBedOpened;
+ break;
+
+ case kFightAnna:
+ sceneIndex = kSceneFightAnna;
+ break;
+
+ case kFightIvo:
+ sceneIndex = kSceneFightIvo;
+ break;
+
+ case kFightSalko:
+ sceneIndex = kSceneFightSalko;
+ break;
+
+ case kFightVesna:
+ sceneIndex = kSceneFightVesna;
+ break;
+ }
+
+ if (getFlags()->shouldRedraw) {
+ getFlags()->shouldRedraw = false;
+ askForRedraw();
+ //redrawScreen();
+ }
+
+ // Load the scene object
+ Scene *scene = getScenes()->get(sceneIndex);
+
+ // Update game entities and state
+ getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition;
+ getEntityData(kEntityPlayer)->location = scene->location;
+
+ getState()->scene = sceneIndex;
+
+ getFlags()->flag_3 = true;
+
+ // Draw the scene
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+ // FIXME move to start of fight?
+ askForRedraw();
+ redrawScreen();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Setup the fight
+ _data = new FightData;
+ loadData(type);
+
+ // Show opponents & egg button
+ Common::Event emptyEvent;
+ handleTick(emptyEvent, false);
+ getInventory()->drawEgg();
+
+ // Start fight
+ _endType = kFightEndLost;
+ while (_data->isRunning) {
+ if (_engine->handleEvents())
+ continue;
+
+ getSound()->updateQueue();
+ }
+
+ // Cleanup after fight is over
+ clearData();
+
+ return _endType;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Status
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::setStopped() {
+ if (_data)
+ _data->isRunning = false;
+}
+
+void Fight::bailout(FightEndType type) {
+ _state = 0;
+ _endType = type;
+ setStopped();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Cleanup
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::clearData() {
+ if (!_data)
+ return;
+
+ // Clear data
+ clearSequences(_data->player);
+ clearSequences(_data->opponent);
+
+ delete _data->player;
+ delete _data->opponent;
+
+ delete _data;
+ _data = NULL;
+
+ _engine->restoreEventHandlers();
+}
+
+void Fight::clearSequences(Fighter *combatant) const {
+ if (!combatant)
+ return;
+
+ // The original game resets the function pointers to default values, just before deleting the struct
+ getScenes()->removeAndRedraw(&combatant->frame, false);
+
+ // Free sequences
+ for (int i = 0; i < (int)combatant->sequences.size(); i++)
+ delete combatant->sequences[i];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::setSequenceAndDraw(Fighter *combatant, uint32 sequenceIndex, FightSequenceType type) const {
+ if (combatant->sequences.size() < sequenceIndex)
+ return;
+
+ switch (type) {
+ default:
+ break;
+
+ case kFightSequenceType0:
+ if (combatant->sequenceIndex)
+ return;
+
+ combatant->sequence = combatant->sequences[sequenceIndex];
+ combatant->sequenceIndex = sequenceIndex;
+ draw(combatant);
+ break;
+
+ case kFightSequenceType1:
+ combatant->sequence = combatant->sequences[sequenceIndex];
+ combatant->sequenceIndex = sequenceIndex;
+ combatant->sequenceIndex2 = 0;
+ draw(combatant);
+ break;
+
+ case kFightSequenceType2:
+ combatant->sequenceIndex2 = sequenceIndex;
+ break;
+ }
+}
+
+void Fight::draw(Fighter *combatant) const {
+ getScenes()->removeAndRedraw(&combatant->frame, false);
+
+ combatant->frameIndex = 0;
+ combatant->field_24 = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Loading
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadData(FightType type) {
+ if (!_data)
+ error("Fight::loadData - invalid data!");
+
+ switch (type) {
+ default:
+ break;
+
+ case kFightMilos:
+ loadMilosPlayer();
+ loadMilosOpponent();
+ break;
+
+ case kFightAnna:
+ loadAnnaPlayer();
+ loadAnnaOpponent();
+ break;
+
+ case kFightIvo:
+ loadIvoPlayer();
+ loadIvoOpponent();
+ break;
+
+ case kFightSalko:
+ loadSalkoPlayer();
+ loadSalkoOpponent();
+ break;
+
+ case kFightVesna:
+ loadVesnaPlayer();
+ loadVesnaOpponent();
+ break;
+ }
+
+ if (!_data->player || !_data->opponent)
+ error("Fight::loadData - error loading fight data (type=%d)", type);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Start running the fight
+ _data->isRunning = true;
+
+ if (_state < 5) {
+ setSequenceAndDraw(_data->player, 0, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0);
+ goto end_load;
+ }
+
+ switch(type) {
+ default:
+ break;
+
+ case kFightMilos:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 4, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0);
+ break;
+
+ case kFightIvo:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 3, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 6, kFightSequenceType0);
+ break;
+
+ case kFightVesna:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 0, kFightSequenceType0);
+ setSequenceAndDraw(_data->player, 3, kFightSequenceType2);
+ setSequenceAndDraw(_data->opponent, 5, kFightSequenceType0);
+ break;
+ }
+
+end_load:
+ // Setup event handlers
+ _engine->backupEventHandlers();
+ SET_EVENT_HANDLERS(Fight, this);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Shared
+//////////////////////////////////////////////////////////////////////////
+void Fight::processFighter(Fighter *fighter) {
+ if (!_data)
+ error("Fight::processFighter - invalid data!");
+
+ if (!fighter->sequence) {
+ if (fighter->frame) {
+ getScenes()->removeFromQueue(fighter->frame);
+ getScenes()->setCoordinates(fighter->frame);
+ }
+ SAFE_DELETE(fighter->frame);
+ return;
+ }
+
+ if (fighter->sequence->count() <= fighter->frameIndex) {
+ switch(fighter->action) {
+ default:
+ break;
+
+ case kFightAction101:
+ setSequenceAndDraw(fighter, fighter->sequenceIndex2, kFightSequenceType1);
+ fighter->sequenceIndex2 = 0;
+ break;
+
+ case kFightActionResetFrame:
+ fighter->frameIndex = 0;
+ break;
+
+ case kFightAction103:
+ setSequenceAndDraw(fighter, 0, kFightSequenceType1);
+ CALL_FUNCTION1(fighter, handleAction, kFightAction101);
+ setSequenceAndDraw(fighter->opponent, 0, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction101);
+ CALL_FUNCTION0(fighter->opponent, update);
+ break;
+
+ case kFightActionWin:
+ bailout(kFightEndWin);
+ break;
+
+ case kFightActionLost:
+ bailout(kFightEndLost);
+ break;
+ }
+ }
+
+ if (_data->isRunning) {
+
+ // Get the current sequence frame
+ SequenceFrame *frame = new SequenceFrame(fighter->sequence, (uint16)fighter->frameIndex);
+ frame->getInfo()->location = 1;
+
+ if (fighter->frame == frame) {
+ delete frame;
+ return;
+ }
+
+ getSound()->playFightSound(frame->getInfo()->soundAction, frame->getInfo()->field_31);
+
+ // Add current frame to queue and advance
+ getScenes()->addToQueue(frame);
+ fighter->frameIndex++;
+
+ if (fighter->frame) {
+ getScenes()->removeFromQueue(fighter->frame);
+
+ if (!frame->getInfo()->field_2E)
+ getScenes()->setCoordinates(fighter->frame);
+ }
+
+ // Replace by new frame
+ delete fighter->frame;
+ fighter->frame = frame;
+ }
+}
+
+void Fight::handleAction(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ return;
+
+ case kFightAction101:
+ break;
+
+ case kFightActionResetFrame:
+ fighter->countdown--;
+ break;
+
+ case kFightAction103:
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+
+ case kFightActionWin:
+ _endType = kFightEndWin;
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+
+ case kFightActionLost:
+ _endType = kFightEndLost;
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+ }
+
+ // Update action
+ fighter->action = action;
+}
+
+bool Fight::canInteract(Fighter const *fighter, FightAction /*= (FightAction)0*/ ) {
+ return (fighter->action == kFightAction101 && !fighter->sequenceIndex);
+}
+
+void Fight::update(Fighter *fighter) {
+
+ processFighter(fighter);
+
+ if (fighter->frame)
+ fighter->frame->getInfo()->location = (fighter->action == kFightActionResetFrame ? 2 : 0);
+}
+
+void Fight::updateOpponent(Fighter *fighter) {
+
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ processFighter(opponent);
+
+ if (opponent->field_38 && !opponent->sequenceIndex)
+ opponent->field_38--;
+
+ if (fighter->frame)
+ fighter->frame->getInfo()->location = 1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Milos
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadMilosPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Milos)
+
+ _data->player->sequences.push_back(loadSequence("2001cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdl.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2001csgr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001csgl.seq"));
+ _data->player->sequences.push_back(loadSequence("2001dbk.seq"));
+}
+
+void Fight::loadMilosOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Milos)
+
+ _data->opponent->sequences.push_back(loadSequence("2001or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001okl.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001okm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001dbk.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001wbk.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS027", SoundManager::kFlagDefault);
+
+ _data->opponent->field_38 = 35;
+}
+
+void Fight::handleActionMilos(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 6, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 3, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 6, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction128:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4) || fighter->opponent->sequenceIndex != 1) {
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ setSequenceAndDraw(fighter, rnd(3) + 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+ }
+ } else {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+ }
+}
+
+void Fight::updateMilos(Fighter *fighter) {
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ // Draw sequences
+ if (fighter->opponent->countdown <= 0) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1);
+
+ getSound()->removeFromQueue(kEntityTables0);
+ getSound()->playSound(kEntityTrain, "MUS029", SoundManager::kFlagDefault);
+
+ CALL_FUNCTION1(fighter, handleAction, kFightActionWin);
+ }
+
+ if (fighter->sequenceIndex == 4) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction4);
+ _endType = kFightEndLost;
+ }
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractMilos(Fighter const *fighter, FightAction action) {
+ if (!_data)
+ error("Fight::canInteractMilos - invalid data!");
+
+ if (action != kFightAction128
+ || _data->player->sequenceIndex != 1
+ || !fighter->frame
+ || CHECK_SEQUENCE2(fighter, 4)
+ || fighter->opponent->sequenceIndex != 1) {
+ return canInteract(fighter);
+ }
+
+ _engine->getCursor()->setStyle(kCursorHand);
+
+ return true;
+}
+
+void Fight::handleOpponentActionMilos(Fighter *fighter, FightAction action) {
+ if (action == kFightAction4) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ } else {
+ if (action != kFightAction131)
+ handleAction(fighter, action);
+ }
+}
+
+void Fight::updateOpponentMilos(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType1);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ } else {
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ }
+
+ // Update field_38
+ if (opponent->opponent->field_34 < 5)
+ opponent->field_38 = 6 * (5 - opponent->opponent->field_34);
+ else
+ opponent->field_38 = 0;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Anna
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadAnnaPlayer() {
+ if (!_data)
+ error("Fight::loadAnnaPlayer - invalid data!");
+
+ // Special case: we are using some shared functions directly
+ _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleActionAnna);
+ _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update);
+ _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+ _data->player->sequences.push_back(loadSequence("2002cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdl.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2002lbk.seq"));
+}
+
+void Fight::loadAnnaOpponent() {
+ if (!_data)
+ error("Fight::loadAnnaOpponent - invalid data!");
+
+ // Special case: we are using some shared functions directly
+ _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction);
+ _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponentAnna);
+ _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+ _data->opponent->sequences.push_back(loadSequence("2002or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okml.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okm.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS030", SoundManager::kFlagDefault);
+
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionAnna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if ((fighter->sequenceIndex != 1 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction3:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 1) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction128:
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+ break;
+ }
+
+ if (fighter->field_34 > 4) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+ }
+}
+
+void Fight::updateOpponentAnna(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(6)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = (int32)rnd(15);
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 3)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Ivo
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadIvoPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Ivo)
+
+ _data->player->sequences.push_back(loadSequence("2003cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003car.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cal.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2003chr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003chl.seq"));
+ _data->player->sequences.push_back(loadSequence("2003ckr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003lbk.seq"));
+ _data->player->sequences.push_back(loadSequence("2003fbk.seq"));
+
+ _data->player->countdown = 5;
+}
+
+void Fight::loadIvoOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Ivo)
+
+ _data->opponent->sequences.push_back(loadSequence("2003or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003odm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003okl.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003okj.seq"));
+ _data->opponent->sequences.push_back(loadSequence("blank.seq"));
+ _data->opponent->sequences.push_back(loadSequence("csdr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003l.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS032", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 5;
+ _data->opponent->field_38 = 15;
+}
+
+void Fight::handleActionIvo(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+ break;
+
+ case kFightAction129:
+ setSequenceAndDraw(fighter, (fighter->opponent->countdown > 1) ? 4 : 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0);
+ break;
+
+ case kFightAction130:
+ setSequenceAndDraw(fighter, 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0);
+ break;
+ }
+}
+
+void Fight::updateIvo(Fighter *fighter) {
+
+ if ((fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4) && !fighter->frameIndex)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction131);
+
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ // Draw sequences
+ if (fighter->opponent->countdown <= 0) {
+ setSequenceAndDraw(fighter, 9, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 8, kFightSequenceType1);
+ getSound()->removeFromQueue(kEntityTables0);
+
+ CALL_FUNCTION1(fighter, handleAction, kFightActionWin);
+ return;
+ }
+
+ if (fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4)
+ CALL_FUNCTION1(fighter->opponent, handleAction, (FightAction)fighter->sequenceIndex);
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractIvo(Fighter const *fighter, FightAction action) {
+ if (action == kFightAction129 || action == kFightAction130)
+ return (fighter->sequenceIndex >= 8);
+
+ return canInteract(fighter);
+}
+
+void Fight::handleOpponentActionIvo(Fighter *fighter, FightAction action) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ break;
+
+ case kFightAction3:
+ if ((opponent->sequenceIndex != 1 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) {
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 6, kFightSequenceType1);
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103);
+ }
+ break;
+
+ case kFightAction4:
+ if ((opponent->sequenceIndex != 2 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) {
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 5, kFightSequenceType1);
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103);
+ }
+ break;
+
+ case kFightAction131:
+ if (opponent->sequenceIndex)
+ break;
+
+ if (rnd(100) <= (unsigned int)(opponent->countdown > 2 ? 60 : 75)) {
+ setSequenceAndDraw(opponent, 3 , kFightSequenceType1);
+ if (opponent->opponent->sequenceIndex == 4)
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ }
+ break;
+ }
+}
+
+void Fight::updateOpponentIvo(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 0, kFightSequenceType2);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 0, kFightSequenceType1);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = 3 * opponent->countdown + (int32)rnd(10);
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+
+ if (opponent->opponent->countdown <= 0) {
+ setSequenceAndDraw(opponent, 7, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 8, kFightSequenceType1);
+ getSound()->removeFromQueue(kEntityTables0);
+
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightActionWin);
+
+ return;
+ }
+
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Salko
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadSalkoPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Salko)
+
+ _data->player->sequences.push_back(loadSequence("2004cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2004cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2004chj.seq"));
+ _data->player->sequences.push_back(loadSequence("2004bk.seq"));
+
+ _data->player->countdown = 2;
+}
+
+void Fight::loadSalkoOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Salko)
+
+ _data->opponent->sequences.push_back(loadSequence("2004or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004ohm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("blank.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS035", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 3;
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionSalko(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ case kFightAction2:
+ if (fighter->sequenceIndex != 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ fighter->field_34 = 0;
+
+ setSequenceAndDraw(fighter, 3, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, (action == kFightAction1 ? 3 : 4), kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+
+ if (action == kFightAction2)
+ fighter->countdown= 0;
+
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction5:
+ if (fighter->sequenceIndex != 3) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ fighter->field_34 = 0;
+ break;
+
+ case kFightAction131:
+ setSequenceAndDraw(fighter, 2, (fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0));
+ break;
+ }
+}
+
+void Fight::updateSalko(Fighter *fighter) {
+ update(fighter);
+
+ // The original doesn't check for currentSequence2 != NULL (might not happen when everything is working properly, but crashes with our current implementation)
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ if (fighter->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+
+ return;
+ }
+
+ if (fighter->sequenceIndex == 2)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction2);
+ }
+}
+
+bool Fight::canInteractSalko(Fighter const *fighter, FightAction action) {
+ if (action == kFightAction131) {
+ if (fighter->sequenceIndex == 1) {
+ if (fighter->opponent->countdown <= 0)
+ _engine->getCursor()->setStyle(kCursorHand);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ return canInteract(fighter);
+}
+
+void Fight::handleOpponentActionSalko(Fighter *fighter, FightAction action) {
+ if (action == kFightAction2) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ } else {
+ handleAction(fighter, action);
+ }
+}
+
+void Fight::updateOpponentSalko(Fighter *fighter) {
+ // This is an opponent struct
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+
+ // Update field_38
+ opponent->field_38 = 4 * opponent->countdown;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndLost);
+
+ // Stop processing
+ return;
+ }
+
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Vesna
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadVesnaPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Vesna)
+
+ _data->player->sequences.push_back(loadSequence("2005cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cbr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005bk.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cdm1.seq"));
+ _data->player->sequences.push_back(loadSequence("2005chl.seq"));
+}
+
+void Fight::loadVesnaOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Vesna)
+
+ _data->opponent->sequences.push_back(loadSequence("2005or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005okml.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005odm1.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005csbm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oam4.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS038", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 4;
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionVesna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if (fighter->sequenceIndex != 2) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction5:
+ if (fighter->sequenceIndex != 3) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ if (fighter->sequenceIndex == 1 && fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ } else {
+ setSequenceAndDraw(fighter, (fighter->opponent->sequenceIndex == 5) ? 3 : 1, kFightSequenceType0);
+ }
+ break;
+
+ case kFightAction132:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+
+ if (fighter->field_34 > 10) {
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType2);
+ fighter->opponent->countdown = 1;
+ fighter->field_34 = 0;
+ }
+}
+
+void Fight::updateVesna(Fighter *fighter) {
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ if (fighter->sequenceIndex == 3)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction3);
+
+ if (fighter->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+ return;
+ }
+
+ if (fighter->sequenceIndex == 5)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction5);
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractVesna(Fighter const *fighter, FightAction action) {
+ if (action != kFightAction128)
+ return canInteract(fighter);
+
+ if (fighter->sequenceIndex != 1) {
+
+ if (fighter->opponent->sequenceIndex == 5) {
+ _engine->getCursor()->setStyle(kCursorDown);
+ return true;
+ }
+
+ return canInteract(fighter);
+ }
+
+ if (fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ _engine->getCursor()->setStyle(kCursorPunchLeft);
+ return true;
+ }
+
+ return false;
+}
+
+void Fight::handleOpponentActionVesna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ break;
+
+ case kFightAction3:
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ break;
+
+ case kFightAction5:
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ if (fighter->countdown <= 1)
+ fighter->countdown = 1;
+ break;
+
+ case kFightAction131:
+ break;
+ }
+}
+
+void Fight::updateOpponentVesna(Fighter *fighter) {
+ // This is an opponent struct
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 == 1) {
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ } else {
+ switch (rnd(6)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = 4 * opponent->countdown;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 5)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+
+ switch (opponent->sequenceIndex) {
+ default:
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType1);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 4, kFightSequenceType1);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ break;
+ }
+
+ setSequenceAndDraw(opponent->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ CALL_FUNCTION0(opponent->opponent, update);
+ CALL_FUNCTION0(opponent, update);
+
+ getSound()->removeFromQueue(kEntityTables0);
+
+ // Stop processing
+ return;
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/fight.h b/engines/lastexpress/game/fight.h
new file mode 100644
index 0000000000..32d734d57c
--- /dev/null
+++ b/engines/lastexpress/game/fight.h
@@ -0,0 +1,269 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_FIGHT_H
+#define LASTEXPRESS_FIGHT_H
+
+/*
+ Fight structure
+ ---------------
+ uint32 {4} - player struct
+ uint32 {4} - opponent struct
+ uint32 {4} - hasLost flag
+
+ byte {1} - isRunning
+
+ Fight participant structure
+ ---------------------------
+ uint32 {4} - function pointer
+ uint32 {4} - pointer to fight structure
+ uint32 {4} - pointer to opponent (fight participant structure)
+ uint32 {4} - array of sequences
+ uint32 {4} - number of sequences
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint16 {2} - ??
+ uint16 {2} - ?? - only for opponent structure
+ uint32 {4} - ?? - only for opponent structure
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/array.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+class SequenceFrame;
+
+//////////////////////////////////////////////////////////////////////////
+// TODO : objectify!
+class Fight : public EventHandler {
+public:
+ enum FightEndType {
+ kFightEndWin = 0,
+ kFightEndLost = 1,
+ kFightEndExit = 2
+ };
+
+ Fight(LastExpressEngine *engine);
+ ~Fight();
+
+ FightEndType setup(FightType type);
+
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ void setStopped();
+ void resetState() { _state = 0; }
+
+private:
+ enum FightSequenceType {
+ kFightSequenceType0 = 0,
+ kFightSequenceType1 = 1,
+ kFightSequenceType2 = 2
+ };
+
+ enum FightAction {
+ kFightAction1 = 1,
+ kFightAction2 = 2,
+ kFightAction3 = 3,
+ kFightAction4 = 4,
+ kFightAction5 = 5,
+ kFightAction101 = 101,
+ kFightActionResetFrame = 102,
+ kFightAction103 = 103,
+ kFightActionWin = 104,
+ kFightActionLost = 105,
+ kFightAction128 = 128,
+ kFightAction129 = 129,
+ kFightAction130 = 130,
+ kFightAction131 = 131,
+ kFightAction132 = 132
+ };
+
+ struct Fighter {
+ Common::Functor2<Fighter *, FightAction, void> *handleAction;
+ Common::Functor1<Fighter *, void> *update;
+ Common::Functor2<Fighter const *, FightAction, bool> *canInteract;
+ Fighter* opponent;
+ Common::Array<Sequence *> sequences;
+ uint32 sequenceIndex;
+ Sequence *sequence;
+ SequenceFrame *frame;
+ uint32 frameIndex;
+ uint32 field_24;
+ FightAction action;
+ uint32 sequenceIndex2;
+ int32 countdown; // countdown before loosing ?
+ uint32 field_34;
+
+ Fighter() {
+ handleAction = NULL;
+ update = NULL;
+ canInteract = NULL;
+
+ opponent = NULL;
+
+ sequenceIndex = 0;
+ sequence = NULL;
+ frame = NULL;
+ frameIndex = 0;
+
+ field_24 = 0;
+
+ action = kFightAction101;
+ sequenceIndex2 = 0;
+
+ countdown = 1;
+
+ field_34 = 0;
+ }
+ };
+
+ // Opponent struct
+ struct Opponent : Fighter {
+ int32 field_38;
+
+ Opponent() : Fighter() {
+ field_38 = 0;
+ }
+ };
+
+ struct FightData {
+ Fighter *player;
+ Opponent *opponent;
+ int32 index;
+
+ Sequence *sequences[20];
+ Common::String names[20];
+
+ bool isRunning;
+
+ FightData() {
+ player = new Fighter();
+ opponent = new Opponent();
+
+ // Set opponents
+ player->opponent = opponent;
+ opponent->opponent = player;
+
+ index = 0;
+
+ isRunning = false;
+ }
+ };
+
+ LastExpressEngine* _engine;
+ FightData *_data;
+ FightEndType _endType;
+ int _state;
+
+ bool _handleTimer;
+
+ // Events
+ void handleTick(const Common::Event &ev, bool unknown);
+
+ // State
+ void bailout(FightEndType type);
+
+
+ // Drawing
+ void setSequenceAndDraw(Fighter *fighter, uint32 sequenceIndex, FightSequenceType type) const;
+ void draw(Fighter *fighter) const;
+
+ // Cleanup
+ void clearData();
+ void clearSequences(Fighter *fighter) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Loading
+ void loadData(FightType type);
+
+ // Shared
+ void processFighter(Fighter *fighter);
+
+ // Default functions
+ void handleAction(Fighter *fighter, FightAction action);
+ void update(Fighter *fighter);
+ bool canInteract(Fighter const *fighter, FightAction = (FightAction)0);
+ void updateOpponent(Fighter *fighter);
+
+ // Milos
+ void loadMilosPlayer();
+ void loadMilosOpponent();
+ void handleActionMilos(Fighter *fighter, FightAction action);
+ void updateMilos(Fighter *fighter);
+ bool canInteractMilos(Fighter const *fighter, FightAction action);
+ void handleOpponentActionMilos(Fighter *fighter, FightAction action);
+ void updateOpponentMilos(Fighter *fighter);
+
+ // Anna
+ void loadAnnaPlayer();
+ void loadAnnaOpponent();
+ void handleActionAnna(Fighter *fighter, FightAction action);
+ void updateOpponentAnna(Fighter *fighter);
+
+ // Ivo
+ void loadIvoPlayer();
+ void loadIvoOpponent();
+ void handleActionIvo(Fighter *fighter, FightAction action);
+ void updateIvo(Fighter *fighter);
+ bool canInteractIvo(Fighter const *fighter, FightAction action);
+ void handleOpponentActionIvo(Fighter *fighter, FightAction action);
+ void updateOpponentIvo(Fighter *fighter);
+
+ // Salko
+ void loadSalkoPlayer();
+ void loadSalkoOpponent();
+ void handleActionSalko(Fighter *fighter, FightAction action);
+ void updateSalko(Fighter *fighter);
+ bool canInteractSalko(Fighter const *fighter, FightAction action);
+ void handleOpponentActionSalko(Fighter *fighter, FightAction action);
+ void updateOpponentSalko(Fighter *fighter);
+
+ // Vesna
+ void loadVesnaPlayer();
+ void loadVesnaOpponent();
+ void handleActionVesna(Fighter *fighter, FightAction action);
+ void updateVesna(Fighter *fighter);
+ bool canInteractVesna(Fighter const *fighter, FightAction action);
+ void handleOpponentActionVesna(Fighter *fighter, FightAction action);
+ void updateOpponentVesna(Fighter *fighter);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FIGHT_H
diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp
new file mode 100644
index 0000000000..37020604f6
--- /dev/null
+++ b/engines/lastexpress/game/inventory.cpp
@@ -0,0 +1,594 @@
+/* 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/inventory.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.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"
+
+
+#define drawItem(x, y, index, brightness) { Icon icon((CursorStyle)(index)); icon.setPosition(x, y); icon.setBrightness(brightness); _engine->getGraphicsManager()->draw(&icon, GraphicsManager::kBackgroundInventory); }
+
+namespace LastExpress {
+
+Inventory::Inventory(LastExpressEngine *engine) : _engine(engine), _selectedItem(kItemNone), _highlightedItem(kItemNone), _opened(false), _visible(false),
+ _showingHourGlass(false), _blinkingEgg(false), _blinkingTime(0), _blinkingInterval(_defaultBlinkingInterval), _blinkingBrightness(100),
+ _flagUseMagnifier(false), _flag1(false), _flag2(false), _flagEggHightlighted(false), _itemScene(NULL) {
+
+ _inventoryRect = Common::Rect(0, 0, 32, 32);
+ _menuRect = Common::Rect(608, 448, 640, 480);
+ _selectedRect = Common::Rect(44, 0, 76, 32);
+
+ init();
+}
+
+Inventory::~Inventory() {
+ _itemScene = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Inventory handling
+//////////////////////////////////////////////////////////////////////////
+
+// Initialize inventory contents
+void Inventory::init() {
+ // ID
+ _entries[kItemMatchBox].cursor = kCursorMatchBox;
+ _entries[kItemTelegram].cursor = kCursorTelegram;
+ _entries[kItemPassengerList].cursor = kCursorPassengerList;
+ _entries[kItemArticle].cursor = kCursorArticle;
+ _entries[kItemScarf].cursor = kCursorScarf;
+ _entries[kItemPaper].cursor = kCursorPaper;
+ _entries[kItemParchemin].cursor = kCursorParchemin;
+ _entries[kItemMatch].cursor = kCursorMatch;
+ _entries[kItemWhistle].cursor = kCursorWhistle;
+ _entries[kItemKey].cursor = kCursorKey;
+ _entries[kItemBomb].cursor = kCursorBomb;
+ _entries[kItemFirebird].cursor = kCursorFirebird;
+ _entries[kItemBriefcase].cursor = kCursorBriefcase;
+ _entries[kItemCorpse].cursor = kCursorCorpse;
+
+ // Selectable
+ _entries[kItemMatchBox].isSelectable = true;
+ _entries[kItemMatch].isSelectable = true;
+ _entries[kItemTelegram].isSelectable = true;
+ _entries[kItemWhistle].isSelectable = true;
+ _entries[kItemKey].isSelectable = true;
+ _entries[kItemFirebird].isSelectable = true;
+ _entries[kItemBriefcase].isSelectable = true;
+ _entries[kItemCorpse].isSelectable = true;
+ _entries[kItemPassengerList].isSelectable = true;
+
+ // Auto selection
+ _entries[kItem2].manualSelect = false;
+ _entries[kItem3].manualSelect = false;
+ _entries[kItem5].manualSelect = false;
+ _entries[kItem7].manualSelect = false;
+ _entries[kItem9].manualSelect = false;
+ _entries[kItem11].manualSelect = false;
+ _entries[kItemBeetle].manualSelect = false;
+ _entries[kItem17].manualSelect = false;
+ _entries[kItemFirebird].manualSelect = false;
+ _entries[kItemBriefcase].manualSelect = false;
+ _entries[kItemCorpse].manualSelect = false;
+ _entries[kItemGreenJacket].manualSelect = false;
+ _entries[kItem22].manualSelect = false;
+
+ // Scene
+ _entries[kItemMatchBox].scene = kSceneMatchbox;
+ _entries[kItemTelegram].scene = kSceneTelegram;
+ _entries[kItemPassengerList].scene = kScenePassengerList;
+ _entries[kItemScarf].scene = kSceneScarf;
+ _entries[kItemParchemin].scene = kSceneParchemin;
+ _entries[kItemArticle].scene = kSceneArticle;
+ _entries[kItemPaper].scene = kScenePaper;
+ _entries[kItemFirebird].scene = kSceneFirebird;
+ _entries[kItemBriefcase].scene = kSceneBriefcase;
+
+ // Has item
+ _entries[kItemTelegram].isPresent = true;
+ _entries[kItemArticle].isPresent = true;
+
+ _selectedItem = kItemNone;
+}
+
+// FIXME we need to draw cursors with full background opacity so that whatever is in the background is erased
+// this saved us clearing some part of the background when switching between states
+
+// TODO if we draw inventory objects on screen, we need to load a new scene.
+// Signal that the inventory has taken over the screen and stop processing mouse events after we have been called
+bool Inventory::handleMouseEvent(const Common::Event &ev) {
+
+ // Do not show inventory when on the menu screen
+ if (getMenu()->isShown() || !_visible)
+ return false;
+
+ // Flag to know whether to restore the current cursor or not
+ bool insideInventory = false;
+
+ // Egg (menu)
+ if (_menuRect.contains(ev.mouse)) {
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ // If clicked, show the menu
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ getSound()->playSound(kEntityPlayer, "LIB039");
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+
+ // TODO can we return directly or do we need to make sure the state will be "valid" when we come back from the menu
+ return true;
+ } else {
+ // Highlight if needed
+ if (_highlightedItem != getMenu()->getGameId() + 39) {
+ _highlightedItem = (InventoryItem)(getMenu()->getGameId() + 39);
+ drawItem(608, 448, _highlightedItem, 100)
+
+ askForRedraw();
+ }
+ }
+ } else {
+ // remove highlight if needed
+ if (_highlightedItem == getMenu()->getGameId() + 39) {
+ drawItem(608, 448, _highlightedItem, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ // Portrait (inventory)
+ if (_inventoryRect.contains(ev.mouse)) {
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ // If clicked, show pressed state and display inventory
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ open();
+ } else {
+ // Highlight if needed
+ if (_highlightedItem != (InventoryItem)getProgress().portrait && !_opened) {
+ _highlightedItem = (InventoryItem)getProgress().portrait;
+ drawItem(0, 0, getProgress().portrait, 100)
+
+ askForRedraw();
+ }
+ }
+ } else {
+ // remove highlight if needed
+ if (_highlightedItem == (InventoryItem)getProgress().portrait && !_opened) {
+ drawItem(0, 0, getProgress().portrait, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ // If the inventory is open, check all items rect to see if we need to highlight one / handle click
+ if (_opened) {
+
+ // Always show normal cursor when the inventory is opened
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ bool selected = false;
+
+ // Iterate over items
+ int16 y = 44;
+ for (int i = 1; i < 32; i++) {
+ if (!hasItem((InventoryItem)i))
+ continue;
+
+ if (Common::Rect(0, y, 32, 32 + y).contains(ev.mouse)) {
+
+ // If released with an item highlighted, show this item
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ if (_entries[i].isSelectable) {
+ selected = true;
+ _selectedItem = (InventoryItem)i;
+ drawItem(44, 0, get(_selectedItem)->cursor, 100)
+ }
+
+ examine((InventoryItem)i);
+ break;
+ } else {
+ if (_highlightedItem != i) {
+ drawItem(0, y, _entries[i].cursor, 100)
+ _highlightedItem = (InventoryItem)i;
+ askForRedraw();
+ }
+ }
+ } else {
+ // Remove highlight if necessary
+ if (_highlightedItem == i) {
+ drawItem(0, y, _entries[i].cursor, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ y += 40;
+ }
+
+ // Right button is released: we need to close the inventory
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ // Not on a selectable item: unselect the current item
+ if (!selected)
+ unselectItem();
+
+ close();
+ }
+ }
+
+ // Selected item
+ if (_selectedItem != kItemNone && _selectedRect.contains(ev.mouse)) {
+ insideInventory = true;
+
+ // Show magnifier icon
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ examine((InventoryItem)_selectedItem);
+ }
+ }
+
+ // If the egg is blinking, refresh
+ if (_blinkingEgg)
+ drawEgg();
+
+ // Restore cursor
+ //if (!insideInventory)
+ // _engine->getCursor()->setStyle(getLogic()->getCursorStyle());
+
+ return insideInventory;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// UI
+//////////////////////////////////////////////////////////////////////////
+void Inventory::show() {
+ clearBg(GraphicsManager::kBackgroundInventory);
+ askForRedraw();
+
+ // Show portrait (first draw, cannot be highlighted)
+ drawItem(0, 0, getProgress().portrait, 50)
+
+ // Show selected item
+ if (_selectedItem != kItemNone)
+ drawItem(44, 0, _selectedItem, 100)
+
+ drawEgg();
+}
+
+void Inventory::setPortrait(InventoryItem item) const {
+ getProgress().portrait = item;
+ drawItem(0, 0, getProgress().portrait, 50);
+}
+
+void Inventory::blinkEgg(bool enabled) {
+ _blinkingEgg = enabled;
+
+ // Reset state
+ _showingHourGlass = false;
+
+ // Show egg at full brightness for first step if blinking
+ if (_blinkingEgg)
+ drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness)
+ else {
+ // Reset values
+ _blinkingBrightness = 100;
+ _blinkingInterval = _defaultBlinkingInterval;
+ drawItem(608, 448, getMenu()->getGameId() + 39, 50) // normal egg state
+ }
+
+ askForRedraw();
+}
+
+void Inventory::showHourGlass() const{
+ if (!getFlags()->flag_5) {
+ drawItem(608, 448, kCursorHourGlass, 100);
+ }
+
+ askForRedraw();
+
+ getFlags()->shouldDrawEggOrHourGlass = true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Items
+//////////////////////////////////////////////////////////////////////////
+Inventory::InventoryEntry *Inventory::get(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ error("Inventory::getEntry: Invalid inventory item!");
+
+ return &_entries[item];
+}
+
+void Inventory::addItem(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ get(item)->isPresent = true;
+ get(item)->location = kObjectLocationNone;
+
+ // Auto-select item if necessary
+ if (get(item)->cursor && !get(item)->manualSelect) {
+ _selectedItem = (InventoryItem)get(item)->cursor;
+ drawItem(44, 0, _selectedItem, 100)
+ askForRedraw();
+ }
+}
+
+void Inventory::removeItem(InventoryItem item, ObjectLocation newLocation) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ get(item)->isPresent = false;
+ get(item)->location = newLocation;
+
+ if (get(item)->cursor == (CursorStyle)_selectedItem) {
+ _selectedItem = kItemNone;
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32));
+ askForRedraw();
+ }
+}
+
+bool Inventory::hasItem(InventoryItem item) {
+ if (get(item)->isPresent && item < kPortraitOriginal)
+ return true;
+
+ return false;
+}
+
+void Inventory::selectItem(InventoryItem item) {
+ _selectedItem = item;
+
+ drawItem(44, 0, get(_selectedItem)->cursor, 100)
+ askForRedraw();
+}
+
+void Inventory::unselectItem() {
+ _selectedItem = kItemNone;
+
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32));
+ askForRedraw();
+}
+
+void Inventory::setLocationAndProcess(InventoryItem item, ObjectLocation location) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ if (get(item)->location == location)
+ return;
+
+ get(item)->location = location;
+
+ if (isItemSceneParameter(item) && !getFlags()->flag_0)
+ getScenes()->processScene();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void Inventory::saveLoadWithSerializer(Common::Serializer &) {
+ error("Inventory::saveLoadWithSerializer: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String Inventory::toString() {
+ Common::String ret = "";
+
+ for (int i = 0; i < kPortraitOriginal; i++)
+ ret += Common::String::printf("%d : %s\n", i, _entries[i].toString().c_str());
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private methods
+//////////////////////////////////////////////////////////////////////////
+InventoryItem Inventory::getFirstExaminableItem() const {
+
+ int index = 0;
+ InventoryEntry entry = _entries[index];
+ while (!entry.isPresent || !entry.cursor || entry.manualSelect) {
+ index++;
+ entry = _entries[index];
+
+ if (index >= kPortraitOriginal)
+ return kItemNone;
+ }
+
+ return (InventoryItem)index;
+}
+
+bool Inventory::isItemSceneParameter(InventoryItem item) const {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ switch(scene->type) {
+ default:
+ return false;
+
+ case Scene::kTypeItem:
+ if (scene->param1 == item)
+ return true;
+ break;
+
+ case Scene::kTypeItem2:
+ if (scene->param1 == item || scene->param2 == item)
+ return true;
+ break;
+
+ case Scene::kTypeObjectItem:
+ if (scene->param2 == item)
+ return true;
+ break;
+
+ case Scene::kTypeItem3:
+ if (scene->param1 == item || scene->param2 == item || scene->param3 == item)
+ return true;
+ break;
+
+ case Scene::kTypeCompartmentsItem:
+ if (scene->param2 == item)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+// Examine an inventory item
+void Inventory::examine(InventoryItem item) {
+ SceneIndex index = get(item)->scene;
+ if (!index)
+ return;
+
+ /*if (!getState()->sceneUseBackup ||
+ (getState()->sceneBackup2 && getFirstExaminableItem() == _selectedItem))
+ flag = 1;*/
+
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneBackup = getState()->scene;
+ getState()->sceneUseBackup = true;
+
+ getScenes()->loadScene(index);
+ } else {
+
+ if (!getState()->sceneBackup2)
+ return;
+
+ if (getFirstExaminableItem() == _selectedItem) {
+ index = getState()->sceneBackup2;
+ getState()->sceneBackup2 = kSceneNone;
+ getScenes()->loadScene(index);
+ }
+ }
+}
+
+// FIXME: see different callers and adjust
+// - draw with different brightness if mousing over
+void Inventory::drawEgg() const {
+ if (!getFlags()->flag_5)
+ drawItem(608, 448, getMenu()->getGameId() + 39, 50)
+
+ getFlags()->shouldDrawEggOrHourGlass = false;
+}
+
+// Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit.
+void Inventory::drawBlinkingEgg() {
+
+ warning("Inventory::drawEgg - blinking not implemented!");
+
+ //// TODO show egg (with or without mouseover)
+
+ //// Play timer sound
+ //if (getGlobalTimer() < 90) {
+ // if (getGlobalTimer() + ticks >= 90)
+ // getSound()->playSoundWithSubtitles("TIMER.SND", 50331664, kEntityPlayer);
+
+ // if (getSound()->isBuffered("TIMER"))
+ // setGlobalTimer(0);
+ //}
+
+ //// Restore egg to standard brightness
+ //if (!getGlobalTimer()) {
+ //
+ //}
+
+
+ //drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness)
+
+ //// TODO if delta time > _blinkingInterval, update egg & ask for redraw then adjust blinking time and remaining time
+ //
+
+ //// Reset values and stop blinking
+ //if (_blinkingTime == 0)
+ // blinkEgg(false);
+
+ askForRedraw();
+}
+
+// Close inventory: clear items and reset icon
+void Inventory::open() {
+ _opened = true;
+
+ // Show selected state
+ drawItem(0, 0, getProgress().portrait + 1, 100)
+
+ int16 y = 44;
+
+ // Iterate over items
+ for (uint i = 1; i < 32; i++) {
+ if (_entries[i].isPresent) {
+ drawItem(0, y, _entries[i].cursor, 50)
+ y += 40;
+ }
+ }
+
+ askForRedraw();
+}
+
+// Close inventory: clear items and reset icon
+void Inventory::close() {
+ _opened = false;
+
+ // Fallback to unselected state
+ drawItem(0, 0, getProgress().portrait, 100)
+
+ // Erase rectangle for all inventory items
+ int count = 0;
+ for (uint i = 1; i < 32; i++) {
+ if (_entries[i].isPresent) {
+ count++;
+ }
+ }
+
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(0, 44, 32, (int16)(44 + 44 * count)));
+
+ askForRedraw();
+}
+
+Common::Rect Inventory::getItemRect(int16 index) const{
+ return Common::Rect(0, (int16)((32 + 12) * (index + 1)), 32, (int16)((32 + 12) * (index + 2))); // space between items = 12px
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/inventory.h b/engines/lastexpress/game/inventory.h
new file mode 100644
index 0000000000..6babe3e60e
--- /dev/null
+++ b/engines/lastexpress/game/inventory.h
@@ -0,0 +1,169 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_INVENTORY_H
+#define LASTEXPRESS_INVENTORY_H
+
+/*
+ Inventory entry (32 entries)
+ ----------------------------
+
+ byte {1} - Item ID (set to 0 for "undefined" items)
+ byte {1} - Scene ID
+ byte {1} - ??
+ byte {1} - Selectable (1 if item is selectable, 0 otherwise)
+ byte {1} - Is item in inventory (set to 1 for telegram and article)
+ byte {1} - Auto selection (1 for no auto selection, 0 otherwise)
+ byte {1} - Location
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/events.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Scene;
+
+class Inventory : Common::Serializable, public EventHandler {
+public:
+
+ // Entry
+ struct InventoryEntry {
+ CursorStyle cursor;
+ SceneIndex scene;
+ byte field_2;
+ bool isSelectable;
+ bool isPresent;
+ bool manualSelect;
+ ObjectLocation location;
+
+ InventoryEntry() {
+ cursor = kCursorNormal;
+ scene = kSceneNone;
+ field_2 = 0;
+ isSelectable = false;
+ isPresent = false;
+ manualSelect = true;
+ location = kObjectLocationNone;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("{ %d - %d - %d - %d - %d - %d - %d }", cursor, scene, field_2, isSelectable, isPresent, manualSelect, location);
+ }
+ };
+
+ Inventory(LastExpressEngine *engine);
+ ~Inventory();
+
+ // Inventory contents
+ void addItem(InventoryItem item);
+ void removeItem(InventoryItem item, ObjectLocation newLocation = kObjectLocationNone);
+ bool hasItem(InventoryItem item);
+ void selectItem(InventoryItem item);
+ void unselectItem();
+ InventoryItem getSelectedItem() { return _selectedItem; }
+
+ InventoryEntry *get(InventoryItem item);
+ InventoryEntry *getSelectedEntry() { return get(_selectedItem); }
+
+ InventoryItem getFirstExaminableItem() const;
+ void setLocationAndProcess(InventoryItem item, ObjectLocation location);
+
+ // UI Control
+ void show();
+ void blinkEgg(bool enabled);
+ void showHourGlass() const;
+ void setPortrait(InventoryItem item) const;
+ void drawEgg() const;
+ void drawBlinkingEgg();
+
+ // Handle inventory UI events.
+ bool handleMouseEvent(const Common::Event &ev);
+
+ // State
+ bool isMagnifierInUse() { return _flagUseMagnifier; }
+ bool isFlag1() { return _flag1; }
+ bool isFlag2() { return _flag2; }
+ bool isEggHighlighted() { return _flagEggHightlighted; }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ static const uint32 _defaultBlinkingInterval = 250; ///< Default blinking interval in ms
+
+ LastExpressEngine *_engine;
+
+ InventoryEntry _entries[32];
+ InventoryItem _selectedItem;
+ InventoryItem _highlightedItem;
+ bool _opened;
+ bool _visible;
+
+ bool _showingHourGlass;
+ bool _blinkingEgg;
+ uint32 _blinkingTime;
+ uint32 _blinkingInterval;
+ uint32 _blinkingBrightness;
+
+ // Flags
+ bool _flagUseMagnifier;
+ bool _flag1;
+ bool _flag2;
+ bool _flagEggHightlighted;
+
+ Scene *_itemScene;
+
+ // Important rects
+ Common::Rect _inventoryRect;
+ Common::Rect _menuRect;
+ Common::Rect _selectedRect;
+
+ void init();
+
+ void open();
+ void close();
+ void examine(InventoryItem item);
+ Common::Rect getItemRect(int16 index) const;
+
+ bool isItemSceneParameter(InventoryItem item) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_INVENTORY_H
diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp
new file mode 100644
index 0000000000..4aedd04a0c
--- /dev/null
+++ b/engines/lastexpress/game/logic.cpp
@@ -0,0 +1,577 @@
+/* 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/logic.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+
+// Entities
+#include "lastexpress/entities/chapters.h"
+
+// Game
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.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 EVENT_TICKS_BEETWEEN_SAVEGAMES 450
+#define GAME_TICKS_BEETWEEN_SAVEGAMES 2700
+
+Logic::Logic(LastExpressEngine *engine) : _engine(engine) {
+ _action = new Action(engine);
+ _beetle = new Beetle(engine);
+ _entities = new Entities(engine);
+ _fight = new Fight(engine);
+ _saveload = new SaveLoad(engine);
+ _state = new State(engine);
+
+ // Flags
+ _flagActionPerformed = false;
+ _ignoreFrameInterval = false;
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+}
+
+Logic::~Logic() {
+ delete _action;
+ delete _beetle;
+ delete _fight;
+ delete _entities;
+ delete _saveload;
+ delete _state;
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Event Handling
+//////////////////////////////////////////////////////////////////////////
+#define REDRAW_CURSOR() { \
+ if (getInventory()->isMagnifierInUse()) \
+ _engine->getCursor()->setStyle(kCursorMagnifier); \
+ if (getInventory()->isFlag1() \
+ || getInventory()->isFlag2() \
+ || getInventory()->isEggHighlighted()) \
+ _engine->getCursor()->setStyle(kCursorNormal); \
+ return; \
+}
+
+void Logic::eventMouse(const Common::Event &ev) {
+ bool hotspotHandled = false;
+
+ // Reset mouse flags
+ getFlags()->mouseLeftClick = false;
+ getFlags()->mouseRightClick = false;
+
+ // Process event flags
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ if (getFlags()->frameInterval)
+ _ignoreFrameInterval = false;
+
+ getFlags()->frameInterval = false;
+ }
+
+ if (getFlags()->flag_0) {
+ if (ev.type == Common::EVENT_LBUTTONUP || ev.type == Common::EVENT_RBUTTONUP) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ getFlags()->frameInterval = true;
+ }
+ return;
+ }
+
+ if (_ignoreFrameInterval && getScenes()->checkCurrentPosition(true) && _engine->getCursor()->getStyle() == kCursorForward) {
+ getFlags()->shouldRedraw = false;
+ getFlags()->flag_0 = true;
+ return;
+ }
+
+ // Update coordinates
+ getGameState()->setCoordinates(ev.mouse);
+
+ // Handle inventory
+ getInventory()->handleMouseEvent(ev);
+
+ // Stop processing is inside the menu
+ if (getMenu()->isShown())
+ return;
+
+ // Handle whistle case
+ if (getInventory()->getSelectedItem() == kItemWhistle
+ && !getProgress().isEggOpen
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemWhistle)->cursor);
+
+ // Check if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP && !getSound()->isBuffered("LIB045")) {
+
+ getSound()->playSoundEvent(kEntityPlayer, 45);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 26) || getEntities()->isPlayerPosition(kCarGreenSleeping, 25) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction226078300);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 26) || getEntities()->isPlayerPosition(kCarRedSleeping, 25) || getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction226078300);
+ }
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle match case
+ if (getInventory()->getSelectedItem() == kItemMatch
+ && (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && getProgress().jacket == kJacketGreen
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()
+ && (getInventory()->get(kItem2)->location == kObjectLocationNone || getEntityData(kEntityPlayer)->car != kCarRedSleeping || getEntityData(kEntityPlayer)->entityPosition != kPosition_2300)) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemMatch)->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+
+ getScenes()->processScene();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle entity item case
+ EntityIndex entityIndex = getEntities()->canInteractWith(ev.mouse);
+ if (entityIndex
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ InventoryItem item = getEntityData(entityIndex)->inventoryItem;
+ if (getInventory()->hasItem((InventoryItem)(item & kItemToggleHigh))) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(item & kItemToggleHigh))->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, (InventoryItem)(item & kItemToggleHigh));
+ } else if ((InventoryItem)(item & kItemInvalid)) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(kCursorTalk2);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, kCursorNormal);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Handle standard actions
+ if (hotspotHandled || getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ return;
+
+ // Magnifier in use
+ if (getInventory()->isMagnifierInUse()) {
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted())
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ return;
+ }
+
+ // Check hotspots
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (!(*it)->isInside(ev.mouse))
+ continue;
+
+ if ((*it)->location < location)
+ continue;
+
+ if (!getAction()->getCursor(**it))
+ continue;
+
+ Scene *hotspotScene = getScenes()->get((*it)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*it)->cursor == kCursorTurnRight
+ || (*it)->cursor == kCursorTurnLeft) {
+ location = (*it)->location;
+ hotspot = *it;
+ }
+ }
+
+ // No hotspot found: show the normal cursor
+ if (!hotspot) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ return;
+ }
+
+ // Found an hotspot: update the cursor and perform the action if the user clicked the mouse
+ _engine->getCursor()->setStyle(getAction()->getCursor(*hotspot));
+
+ if (ev.type != Common::EVENT_LBUTTONUP || _flagActionPerformed)
+ return;
+
+ _flagActionPerformed = true;
+
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getFlags()->shouldRedraw = false;
+
+ getScenes()->setScene(testScene);
+
+ if (getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ // Switch to next chapter if necessary
+ if (hotspot->action == SceneHotspot::kActionSwitchChapter && hotspot->param1 == getState()->progress.chapter)
+ switchChapter();
+}
+
+void Logic::eventTick(const Common::Event &) {
+ uint ticks = 1;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust ticks if an action has been performed
+ if (_flagActionPerformed)
+ ticks = 10;
+
+ _flagActionPerformed = false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw the blinking egg if needed
+ if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawBlinkingEgg();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust time and save game if needed
+ if (getFlags()->isGameRunning) {
+ getState()->timeTicks += ticks;
+ getState()->time += ticks * getState()->timeDelta;
+
+ if (getState()->timeDelta) {
+
+ // Auto-save
+ if (!_ticksSinceLastSavegame) {
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+ getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, kEventNone);
+ }
+
+ // Save after game ticks interval
+ if ((getState()->timeTicks - getSaveLoad()->getLastSavegameTicks()) > GAME_TICKS_BEETWEEN_SAVEGAMES)
+ getSaveLoad()->saveGame(kSavegameTypeTickInterval, kEntityChapters, kEventNone);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load scene and process hotspot
+ if (getFlags()->flag_0 && !getFlags()->mouseLeftClick && !getFlags()->mouseRightClick) {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ if (getScenes()->checkCurrentPosition(true)
+ && !getEntities()->getPosition(scene->car, scene->position)) {
+
+ // Process hotspot
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getScenes()->setScene(testScene);
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ if (getFlags()->isGameRunning)
+ getSavePoints()->callAndProcess();
+
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ return;
+ }
+
+ // Stop processing if the game is paused
+ if (!getFlags()->isGameRunning)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update beetle, savepoints, entities and draw frames
+ if (_beetle->isLoaded())
+ _beetle->update();
+
+ getSavePoints()->callAndProcess();
+ getEntities()->updateCallbacks();
+ getScenes()->drawFrames(true);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update cursor if we can interact with an entity
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (!entity) {
+ if (_engine->getCursor()->getStyle() >= kCursorTalk2)
+ updateCursor(false);
+
+ return;
+ }
+
+ // Show item cursor on entity
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh)) && (int)getEntityData(entity)->inventoryItem != (int)kCursorTalk2) {
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor);
+ return;
+ }
+
+ getLogic()->updateCursor(false);
+ _engine->getCursor()->setStyle(kCursorTalk2);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game over, Chapters & credits
+//////////////////////////////////////////////////////////////////////////
+
+// Handle game over
+void Logic::gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const {
+
+ getSound()->processEntries();
+ getEntities()->reset();
+ getFlags()->isGameRunning = false;
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ if (showScene) {
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ if (sceneIndex && !getFlags()->mouseRightClick) {
+ getScenes()->loadScene(sceneIndex);
+
+ while (getSound()->isBuffered(kEntityTables4)) {
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+
+ // Show Menu
+ getMenu()->show(false, type, value);
+}
+
+void Logic::switchChapter() const {
+ getSound()->clearStatus();
+
+ switch(getState()->progress.chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->addItem(kItemMatchBox);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter2);
+ break;
+
+ case kChapter2:
+ getInventory()->addItem(kItemScarf);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter3);
+ break;
+
+ case kChapter3:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation4;
+ getInventory()->get(kItemFirebird)->isPresent = false;
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getInventory()->addItem(kItemWhistle);
+ getInventory()->addItem(kItemKey);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter4);
+ break;
+
+ case kChapter4:
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter5);
+ break;
+
+ case kChapter5:
+ playFinalSequence();
+ break;
+ }
+}
+
+void Logic::playFinalSequence() const {
+ getSound()->processEntries();
+
+ _action->playAnimation(kEventFinalSequence);
+ showCredits();
+
+ getEntities()->reset();
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+}
+
+void Logic::showCredits() const {
+ error("Logic::showCredits: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void Logic::updateCursor(bool) const { /* the cursor is always updated, even when we don't want to redraw it */
+ CursorStyle style = kCursorNormal;
+ bool interact = false;
+
+ if (getInventory()->getSelectedItem() != kItemWhistle
+ || getProgress().isEggOpen
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()) {
+
+ if (getInventory()->getSelectedItem() != kItemMatch
+ || (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getProgress().jacket != kJacketGreen
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()
+ || (getInventory()->get(kItem2)->location
+ && getEntityData(kEntityPlayer)->car == kCarRedSleeping
+ && getEntityData(kEntityPlayer)->entityPosition == kPosition_2300)) {
+
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (entity
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))) {
+ interact = true;
+ style = getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor;
+ } else if ((int)getEntityData(entity)->inventoryItem == kItemInvalid) {
+ interact = true;
+ style = kCursorTalk2;
+ }
+ }
+
+ if (!interact
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ // Check all hotspots
+ for (Common::Array<SceneHotspot *>::iterator i = scene->getHotspots()->begin(); i != scene->getHotspots()->end(); ++i) {
+ if ((*i)->isInside(getCoords()) && (*i)->location >= location) {
+ if (getAction()->getCursor(**i)) {
+ Scene *hotspotScene = getScenes()->get((*i)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*i)->cursor == kCursorTurnRight
+ || (*i)->cursor == kCursorTurnLeft) {
+ hotspot = *i;
+ location = (*i)->location;
+ }
+ }
+ }
+ }
+
+ style = (hotspot) ? getAction()->getCursor(*hotspot) : kCursorNormal;
+ }
+ } else {
+ style = getInventory()->get(kItemMatch)->cursor;
+ }
+
+ } else {
+ style = getInventory()->get(kItemWhistle)->cursor;
+ }
+
+ if (getInventory()->isMagnifierInUse())
+ style = kCursorMagnifier;
+
+ if (getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ style = kCursorNormal;
+
+ _engine->getCursor()->setStyle(style);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h
new file mode 100644
index 0000000000..7e01655b68
--- /dev/null
+++ b/engines/lastexpress/game/logic.h
@@ -0,0 +1,91 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_LOGIC_H
+#define LASTEXPRESS_LOGIC_H
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/game/entities.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/events.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Action;
+class Beetle;
+class Debugger;
+class Entities;
+class Fight;
+class SaveLoad;
+class State;
+
+class Logic : public EventHandler {
+public:
+ Logic(LastExpressEngine *engine);
+ ~Logic();
+
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ void gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const;
+ void playFinalSequence() const;
+ void updateCursor(bool redraw = true) const;
+
+ Action *getGameAction() { return _action; }
+ Beetle *getGameBeetle() { return _beetle; }
+ Entities *getGameEntities() { return _entities; }
+ Fight *getGameFight() { return _fight; }
+ SaveLoad *getGameSaveLoad() { return _saveload; }
+ State *getGameState() { return _state; }
+
+private:
+ LastExpressEngine *_engine;
+
+ Action *_action; ///< Actions
+ Beetle *_beetle; ///< Beetle catching
+ Entities *_entities; ///< Entities
+ Fight *_fight; ///< Fight handling
+ SaveLoad *_saveload; ///< Save & loading
+ State *_state; ///< Game state
+
+ void switchChapter() const;
+ void showCredits() const;
+
+ // Flags & Members
+ bool _flagActionPerformed;
+ bool _ignoreFrameInterval;
+ int _ticksSinceLastSavegame;
+
+ friend class Debugger;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_LOGIC_H
diff --git a/engines/lastexpress/game/menu.cpp b/engines/lastexpress/game/menu.cpp
new file mode 100644
index 0000000000..19678a3255
--- /dev/null
+++ b/engines/lastexpress/game/menu.cpp
@@ -0,0 +1,1564 @@
+/* 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/menu.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savegame.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"
+
+#define getNextGameId() (GameId)((_gameId + 1) % 6)
+
+namespace LastExpress {
+
+// Bottom-left buttons (quit.seq)
+enum StartMenuButtons {
+ kButtonVolumeDownPushed,
+ kButtonVolumeDown,
+ kButtonVolume,
+ kButtonVolumeUp,
+ kButtonVolumeUpPushed,
+ kButtonBrightnessDownPushed, // 5
+ kButtonBrightnessDown,
+ kButtonBrightness,
+ kButtonBrightnessUp,
+ kButtonBrightnessUpPushed,
+ kButtonQuit, // 10
+ kButtonQuitPushed
+};
+
+// Egg buttons (buttns.seq)
+enum StartMenuEggButtons {
+ kButtonShield,
+ kButtonRewind,
+ kButtonRewindPushed,
+ kButtonForward,
+ kButtonForwardPushed,
+ kButtonCredits, // 5
+ kButtonCreditsPushed,
+ kButtonContinue
+};
+
+// Tooltips sequence (helpnewr.seq)
+enum StartMenuTooltips {
+ kTooltipInsertCd1,
+ kTooltipInsertCd2,
+ kTooltipInsertCd3,
+ kTooltipContinueGame,
+ kTooltipReplayGame,
+ kTooltipContinueRewoundGame, // 5
+ kTooltipViewGameEnding,
+ kTooltipStartAnotherGame,
+ kTooltipVolumeUp,
+ kTooltipVolumeDown,
+ kTooltipBrightnessUp, // 10
+ kTooltipBrightnessDown,
+ kTooltipQuit,
+ kTooltipRewindParis,
+ kTooltipForwardStrasbourg,
+ kTooltipRewindStrasbourg, // 15
+ kTooltipRewindMunich,
+ kTooltipForwardMunich,
+ kTooltipForwardVienna,
+ kTooltipRewindVienna,
+ kTooltipRewindBudapest, // 20
+ kTooltipForwardBudapest,
+ kTooltipForwardBelgrade,
+ kTooltipRewindBelgrade,
+ kTooltipForwardConstantinople,
+ kTooltipSwitchBlueGame, // 25
+ kTooltipSwitchRedGame,
+ kTooltipSwitchGoldGame,
+ kTooltipSwitchGreenGame,
+ kTooltipSwitchTealGame,
+ kTooltipSwitchPurpleGame, // 30
+ kTooltipPlayNewGame,
+ kTooltipCredits,
+ kTooltipFastForward,
+ kTooltipRewind
+};
+
+//////////////////////////////////////////////////////////////////////////
+// DATA
+//////////////////////////////////////////////////////////////////////////
+
+// Information about the cities on the train line
+static const struct {
+ uint8 frame;
+ TimeValue time;
+} _trainCities[31] = {
+ {0, kTimeCityParis},
+ {9, kTimeCityEpernay},
+ {11, kTimeCityChalons},
+ {16, kTimeCityBarLeDuc},
+ {21, kTimeCityNancy},
+ {25, kTimeCityLuneville},
+ {35, kTimeCityAvricourt},
+ {37, kTimeCityDeutschAvricourt},
+ {40, kTimeCityStrasbourg},
+ {53, kTimeCityBadenOos},
+ {56, kTimeCityKarlsruhe},
+ {60, kTimeCityStuttgart},
+ {63, kTimeCityGeislingen},
+ {66, kTimeCityUlm},
+ {68, kTimeCityAugsburg},
+ {73, kTimeCityMunich},
+ {84, kTimeCitySalzbourg},
+ {89, kTimeCityAttnangPuchheim},
+ {97, kTimeCityWels},
+ {100, kTimeCityLinz},
+ {104, kTimeCityAmstetten},
+ {111, kTimeCityVienna},
+ {120, kTimeCityPoszony},
+ {124, kTimeCityGalanta},
+ {132, kTimeCityBudapest},
+ {148, kTimeCityBelgrade},
+ /* Line 1 ends at 150 - line 2 begins at 0 */
+ {157, kTimeCityNish},
+ {165, kTimeCityTzaribrod},
+ {174, kTimeCitySofia},
+ {198, kTimeCityAdrianople},
+ {210, kTimeCityConstantinople}};
+
+static const struct {
+ TimeValue time;
+ uint index;
+ StartMenuTooltips rewind;
+ StartMenuTooltips forward;
+} _cityButtonsInfo[7] = {
+ {kTimeCityParis, 64, kTooltipRewindParis, kTooltipRewindParis},
+ {kTimeCityStrasbourg, 128, kTooltipRewindStrasbourg, kTooltipForwardStrasbourg},
+ {kTimeCityMunich, 129, kTooltipRewindMunich, kTooltipForwardMunich},
+ {kTimeCityVienna, 130, kTooltipRewindVienna, kTooltipForwardVienna},
+ {kTimeCityBudapest, 131, kTooltipRewindBudapest, kTooltipForwardBudapest},
+ {kTimeCityBelgrade, 132, kTooltipRewindBelgrade, kTooltipForwardBelgrade},
+ {kTimeCityConstantinople, 192, kTooltipForwardConstantinople, kTooltipForwardConstantinople}
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Clock
+//////////////////////////////////////////////////////////////////////////
+class Clock {
+public:
+ Clock(LastExpressEngine *engine);
+ ~Clock();
+
+ void draw(uint32 time);
+ void clear();
+
+private:
+ LastExpressEngine *_engine;
+
+ // Frames
+ SequenceFrame *_frameMinutes;
+ SequenceFrame *_frameHour;
+ SequenceFrame *_frameSun;
+ SequenceFrame *_frameDate;
+};
+
+Clock::Clock(LastExpressEngine *engine) : _engine(engine), _frameMinutes(NULL), _frameHour(NULL), _frameSun(NULL), _frameDate(NULL) {
+ _frameMinutes = new SequenceFrame(loadSequence("eggmin.seq"), 0, true);
+ _frameHour = new SequenceFrame(loadSequence("egghour.seq"), 0, true);
+ _frameSun = new SequenceFrame(loadSequence("sun.seq"), 0, true);
+ _frameDate = new SequenceFrame(loadSequence("datenew.seq"), 0, true);
+}
+
+Clock::~Clock() {
+ delete _frameMinutes;
+ delete _frameHour;
+ delete _frameSun;
+ delete _frameDate;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+void Clock::clear() {
+ getScenes()->removeAndRedraw(&_frameMinutes, false);
+ getScenes()->removeAndRedraw(&_frameHour, false);
+ getScenes()->removeAndRedraw(&_frameSun, false);
+ getScenes()->removeAndRedraw(&_frameDate, false);
+}
+
+void Clock::draw(uint32 time) {
+ assert(time >= kTimeCityParis && time <= kTimeCityConstantinople);
+
+ // Check that sequences have been loaded
+ if (!_frameMinutes || !_frameHour || !_frameSun || !_frameDate)
+ error("Clock::process: clock sequences have not been loaded correctly!");
+
+ // Clear existing frames
+ clear();
+
+ // Game starts at: 1037700 = 7:13 p.m. on July 24, 1914
+ // Game ends at: 4941000 = 7:30 p.m. on July 26, 1914
+ // Game lasts for: 3903300 = 2 days + 17 mins = 2897 mins
+
+ // 15 = 1 second
+ // 15 * 60 = 900 = 1 minute
+ // 900 * 60 = 54000 = 1 hour
+ // 54000 * 24 = 1296000 = 1 day
+
+ // Calculate each sequence index from the current time
+ uint8 hour = (uint8)((time % 1296000) / 54000);
+ uint8 minute = (uint8)((time % 54000) / 900);
+ uint32 index_date = 18 * time / 1296000;
+ if (hour == 23)
+ index_date += 18 * minute / 60;
+
+ // Set sequences frames
+ _frameMinutes->setFrame(minute);
+ _frameHour->setFrame((5 * hour + minute / 12) % 60);
+ _frameSun->setFrame((5 * hour + minute / 12) % 120);
+ _frameDate->setFrame((uint16)index_date);
+
+ // Adjust z-order and queue
+ _frameMinutes->getInfo()->location = 1;
+ _frameHour->getInfo()->location = 1;
+ _frameSun->getInfo()->location = 1;
+ _frameDate->getInfo()->location = 1;
+
+ getScenes()->addToQueue(_frameMinutes);
+ getScenes()->addToQueue(_frameHour);
+ getScenes()->addToQueue(_frameSun);
+ getScenes()->addToQueue(_frameDate);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// TrainLine
+//////////////////////////////////////////////////////////////////////////
+class TrainLine {
+public:
+ TrainLine(LastExpressEngine *engine);
+ ~TrainLine();
+
+ void draw(uint32 time);
+ void clear();
+
+private:
+ LastExpressEngine *_engine;
+
+ // Frames
+ SequenceFrame *_frameLine1;
+ SequenceFrame *_frameLine2;
+};
+
+TrainLine::TrainLine(LastExpressEngine *engine) : _engine(engine), _frameLine1(NULL), _frameLine2(NULL) {
+ _frameLine1 = new SequenceFrame(loadSequence("line1.seq"), 0, true);
+ _frameLine2 = new SequenceFrame(loadSequence("line2.seq"), 0, true);
+}
+
+TrainLine::~TrainLine() {
+ delete _frameLine1;
+ delete _frameLine2;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+void TrainLine::clear() {
+ getScenes()->removeAndRedraw(&_frameLine1, false);
+ getScenes()->removeAndRedraw(&_frameLine2, false);
+}
+
+// Draw the train line at the time
+// line1: 150 frames (=> Belgrade)
+// line2: 61 frames (=> Constantinople)
+void TrainLine::draw(uint32 time) {
+ assert(time >= kTimeCityParis && time <= kTimeCityConstantinople);
+
+ // Check that sequences have been loaded
+ if (!_frameLine1 || !_frameLine2)
+ error("TrainLine::process: Line sequences have not been loaded correctly!");
+
+ // Clear existing frames
+ clear();
+
+ // Get the index of the last city the train has visited
+ uint index = 0;
+ for (uint i = 0; i < ARRAYSIZE(_trainCities); i++)
+ if ((uint32)_trainCities[i].time <= time)
+ index = i;
+
+ uint16 frame;
+ if (time > (uint32)_trainCities[index].time) {
+ // Interpolate linearly to use a frame between the cities
+ uint8 diffFrames = _trainCities[index + 1].frame - _trainCities[index].frame;
+ uint diffTimeCities = (uint)(_trainCities[index + 1].time - _trainCities[index].time);
+ uint traveledTime = (time - (uint)_trainCities[index].time);
+ frame = (uint16)(_trainCities[index].frame + (traveledTime * diffFrames) / diffTimeCities);
+ } else {
+ // Exactly on the city
+ frame = _trainCities[index].frame;
+ }
+
+ // Set frame, z-order and queue
+ if (frame < 150) {
+ _frameLine1->setFrame(frame);
+
+ _frameLine1->getInfo()->location = 1;
+ getScenes()->addToQueue(_frameLine1);
+ } else {
+ // We passed Belgrade
+ _frameLine1->setFrame(149);
+ _frameLine2->setFrame(frame - 150);
+
+ _frameLine1->getInfo()->location = 1;
+ _frameLine2->getInfo()->location = 1;
+
+ getScenes()->addToQueue(_frameLine1);
+ getScenes()->addToQueue(_frameLine2);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Menu
+//////////////////////////////////////////////////////////////////////////
+Menu::Menu(LastExpressEngine *engine) : _engine(engine),
+ _seqTooltips(NULL), _seqEggButtons(NULL), _seqButtons(NULL), _seqAcorn(NULL), _seqCity1(NULL), _seqCity2(NULL), _seqCity3(NULL), _seqCredits(NULL),
+ _gameId(kGameBlue), _hasShownStartScreen(false), _hasShownIntro(false),
+ _isShowingCredits(false), _isGameStarted(false), _isShowingMenu(false),
+ _creditsSequenceIndex(0), _checkHotspotsTicks(15), _mouseFlags(Common::EVENT_INVALID), _lastHotspot(NULL),
+ _currentIndex(0), _currentTime(0), _lowerTime(0), _index(0), _index2(0), _time(0), _delta(0), _handleTimeDelta(false) {
+
+ _clock = new Clock(_engine);
+ _trainLine = new TrainLine(_engine);
+}
+
+Menu::~Menu() {
+ delete _clock;
+ delete _trainLine;
+
+ SAFE_DELETE(_seqTooltips);
+ SAFE_DELETE(_seqEggButtons);
+ SAFE_DELETE(_seqButtons);
+ SAFE_DELETE(_seqAcorn);
+ SAFE_DELETE(_seqCity1);
+ SAFE_DELETE(_seqCity2);
+ SAFE_DELETE(_seqCity3);
+ SAFE_DELETE(_seqCredits);
+
+ _lastHotspot = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+void Menu::setup() {
+
+ // Clear drawing queue
+ getScenes()->removeAndRedraw(&_frames[kOverlayAcorn], false);
+ SAFE_DELETE(_seqAcorn);
+
+ // Load Menu scene
+ // + 1 = normal menu with open egg / clock
+ // + 2 = shield menu, when no savegame exists (no game has been started)
+ _isGameStarted = _lowerTime >= kTimeStartGame;
+ getScenes()->loadScene((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2));
+ getFlags()->shouldRedraw = true;
+ getLogic()->updateCursor();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load Acorn sequence
+ _seqAcorn = loadSequence(getAcornSequenceName(_isGameStarted ? getNextGameId() : kGameBlue));
+
+ //////////////////////////////////////////////////////////////////////////
+ // Check if we loaded sequences before
+ if (_seqTooltips && _seqTooltips->count() > 0)
+ return;
+
+ // Load all static data
+ _seqTooltips = loadSequence("helpnewr.seq");
+ _seqEggButtons = loadSequence("buttns.seq");
+ _seqButtons = loadSequence("quit.seq");
+ _seqCity1 = loadSequence("jlinetl.seq");
+ _seqCity2 = loadSequence("jlinecen.seq");
+ _seqCity3 = loadSequence("jlinebr.seq");
+ _seqCredits = loadSequence("credits.seq");
+
+ _frames[kOverlayTooltip] = new SequenceFrame(_seqTooltips);
+ _frames[kOverlayEggButtons] = new SequenceFrame(_seqEggButtons);
+ _frames[kOverlayButtons] = new SequenceFrame(_seqButtons);
+ _frames[kOverlayAcorn] = new SequenceFrame(_seqAcorn);
+ _frames[kOverlayCity1] = new SequenceFrame(_seqCity1);
+ _frames[kOverlayCity2] = new SequenceFrame(_seqCity2);
+ _frames[kOverlayCity3] = new SequenceFrame(_seqCity3);
+ _frames[kOverlayCredits] = new SequenceFrame(_seqCredits);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Handle events
+void Menu::eventMouse(const Common::Event &ev) {
+ if (!getFlags()->shouldRedraw)
+ return;
+
+ bool redraw = true;
+ getFlags()->shouldRedraw = false;
+
+ // Update coordinates
+ setCoords(ev.mouse);
+ //_mouseFlags = (Common::EventType)(ev.type & Common::EVENT_LBUTTONUP);
+
+ if (_isShowingCredits) {
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ showFrame(kOverlayCredits, -1, true);
+ _isShowingCredits = false;
+ }
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ // Last frame of the credits
+ if (_seqCredits && _creditsSequenceIndex == _seqCredits->count() - 1) {
+ showFrame(kOverlayCredits, -1, true);
+ _isShowingCredits = false;
+ } else {
+ ++_creditsSequenceIndex;
+ showFrame(kOverlayCredits, _creditsSequenceIndex, true);
+ }
+ }
+ } else {
+ // Check for hotspots
+ SceneHotspot *hotspot = NULL;
+ getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot);
+
+ if (_lastHotspot != hotspot || ev.type == Common::EVENT_LBUTTONUP) {
+ _lastHotspot = hotspot;
+
+ if (ev.type == Common::EVENT_MOUSEMOVE) { /* todo check event type */
+ if (!_handleTimeDelta && hasTimeDelta())
+ setTime();
+ }
+
+ if (hotspot) {
+ redraw = handleEvent((StartMenuAction)hotspot->action, ev.type);
+ getFlags()->mouseRightClick = false;
+ getFlags()->mouseLeftClick = false;
+ } else {
+ hideOverlays();
+ }
+ }
+ }
+
+ if (redraw) {
+ getFlags()->shouldRedraw = true;
+ askForRedraw();
+ }
+}
+
+void Menu::eventTick(const Common::Event&) {
+ if (hasTimeDelta())
+ adjustTime();
+ else if (_handleTimeDelta)
+ _handleTimeDelta = false;
+
+ // Check hotspots
+ if (!--_checkHotspotsTicks) {
+ checkHotspots();
+ _checkHotspotsTicks = 15;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Show the intro and load the main menu scene
+void Menu::show(bool doSavegame, SavegameType type, uint32 value) {
+
+ if (_isShowingMenu)
+ return;
+
+ _isShowingMenu = true;
+ getEntities()->reset();
+
+ // If no blue savegame exists, this might be the first time we start the game, so we show the full intro
+ if (!getFlags()->mouseRightClick) {
+ if (!SaveLoad::isSavegameValid(kGameBlue) && _engine->getResourceManager()->loadArchive(kArchiveCd1)) {
+
+ if (!_hasShownIntro) {
+ // Show Broderbrund logo
+ Animation animation;
+ if (animation.load(getArchive("1930.nis")))
+ animation.play();
+
+ getFlags()->mouseRightClick = false;
+
+ // Play intro music
+ getSound()->playSoundWithSubtitles("MUS001.SND", SoundManager::kFlagMusic, kEntityPlayer);
+
+ // Show The Smoking Car logo
+ if (animation.load(getArchive("1931.nis")))
+ animation.play();
+
+ _hasShownIntro = true;
+ }
+ } else {
+ // Only show the quick intro
+ if (!_hasShownStartScreen) {
+ getSound()->playSoundWithSubtitles("MUS018.SND", SoundManager::kFlagMusic, kEntityPlayer);
+ getScenes()->loadScene(kSceneStartScreen);
+
+ // Original game waits 60 frames and loops Sound::unknownFunction1 unless the right button is pressed
+ uint32 nextFrameCount = getFrameCount() + 60;
+ while (getFrameCount() < nextFrameCount) {
+ _engine->pollEvents();
+
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+ }
+
+ _hasShownStartScreen = true;
+
+ // Init Menu
+ init(doSavegame, type, value);
+
+ // Setup sound
+ getSound()->unknownFunction4();
+ getSound()->resetQueue(SoundManager::kSoundType11, SoundManager::kSoundType13);
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+
+ // Init flags & misc
+ _isShowingCredits = false;
+ _handleTimeDelta = hasTimeDelta();
+ getInventory()->unselectItem();
+
+ // Set Cursor type
+ _engine->getCursor()->setStyle(kCursorNormal);
+ _engine->getCursor()->show(true);
+
+ setup();
+ checkHotspots();
+
+ // Set event handlers
+ SET_EVENT_HANDLERS(Menu, this);
+}
+
+bool Menu::handleEvent(StartMenuAction action, Common::EventType type) {
+ bool clicked = (type == Common::EVENT_LBUTTONUP);
+
+ switch(action) {
+ default:
+ hideOverlays();
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuCredits:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ showFrame(kOverlayEggButtons, kButtonCreditsPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ hideOverlays();
+
+ _isShowingCredits = true;
+ _creditsSequenceIndex = 0;
+
+ showFrame(kOverlayCredits, 0, true);
+ } else {
+ // TODO check flags ?
+
+ showFrame(kOverlayEggButtons, kButtonCredits, true);
+ showFrame(kOverlayTooltip, kTooltipCredits, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuQuitGame:
+ showFrame(kOverlayTooltip, kTooltipQuit, true);
+
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonQuitPushed, true);
+
+ getSound()->clearStatus();
+ getSound()->updateQueue();
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ // FIXME uncomment when sound queue is properly implemented
+ /*while (getSound()->isBuffered("LIB046"))
+ getSound()->updateQueue();*/
+
+ getFlags()->shouldRedraw = false;
+
+ Engine::quitGame();
+
+ return false;
+ } else {
+ showFrame(kOverlayButtons, kButtonQuit, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuCase4:
+ if (clicked)
+ _index = 0;
+ // fall down to kMenuContinue
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuContinue: {
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Determine the proper CD archive
+ ArchiveIndex cd = kArchiveCd1;
+ if (getProgress().chapter > kChapter1)
+ cd = (getProgress().chapter > kChapter3) ? kArchiveCd3 : kArchiveCd2;
+
+ // Show tooltips & buttons to start a game, continue a game or load the proper cd
+ if (ResourceManager::isArchivePresent(cd)) {
+ if (_isGameStarted) {
+ showFrame(kOverlayEggButtons, kButtonContinue, true);
+
+ if (_index2 == _index) {
+ showFrame(kOverlayTooltip, isGameFinished() ? kTooltipViewGameEnding : kTooltipContinueGame, true);
+ } else {
+ showFrame(kOverlayTooltip, kTooltipContinueRewoundGame, true);
+ }
+
+ } else {
+ showFrame(kOverlayEggButtons, kButtonShield, true);
+ showFrame(kOverlayTooltip, kTooltipPlayNewGame, true);
+ }
+ } else {
+ showFrame(kOverlayEggButtons, -1, true);
+ showFrame(kOverlayTooltip, cd - 1, true);
+ }
+
+ if (!clicked)
+ break;
+
+ // Try loading the archive file
+ if (!_engine->getResourceManager()->loadArchive(cd))
+ break;
+
+ // Load the train data file and setup game
+ getScenes()->loadSceneDataFile(cd);
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ // Setup new game
+ getSavePoints()->reset();
+ setLogicEventHandlers();
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 3));
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 4));
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 5));
+
+ if (!getFlags()->mouseRightClick) {
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ // Show intro
+ Animation animation;
+ if (animation.load(getArchive("1601.nis")))
+ animation.play();
+
+ getEvent(kEventIntro) = 1;
+ }
+ }
+ }
+ }
+
+ if (!getEvent(kEventIntro)) {
+ getEvent(kEventIntro) = 1;
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+ }
+
+ // Setup game
+ getFlags()->isGameRunning = true;
+ startGame();
+
+ if (!_isShowingMenu)
+ getInventory()->show();
+
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuSwitchSaveGame:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ showFrame(kOverlayAcorn, 1, true);
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB047");
+
+ // Setup new menu screen
+ switchGame();
+ setup();
+
+ // Set fight state to 0
+ getFight()->resetState();
+
+ return true;
+ }
+
+ // TODO Check for flag
+
+ showFrame(kOverlayAcorn, 0, true);
+
+ if (_isGameStarted) {
+ showFrame(kOverlayTooltip, kTooltipSwitchBlueGame, true);
+ break;
+ }
+
+ if (_gameId == kGameGold) {
+ showFrame(kOverlayTooltip, kTooltipSwitchBlueGame, true);
+ break;
+ }
+
+ if (!SaveLoad::isSavegameValid(getNextGameId())) {
+ showFrame(kOverlayTooltip, kTooltipStartAnotherGame, true);
+ break;
+ }
+
+ // Stupid tooltips ids are not in order, so we can't just increment them...
+ switch(_gameId) {
+ default:
+ break;
+
+ case kGameBlue:
+ showFrame(kOverlayTooltip, kTooltipSwitchRedGame, true);
+ break;
+
+ case kGameRed:
+ showFrame(kOverlayTooltip, kTooltipSwitchGreenGame, true);
+ break;
+
+ case kGameGreen:
+ showFrame(kOverlayTooltip, kTooltipSwitchPurpleGame, true);
+ break;
+
+ case kGamePurple:
+ showFrame(kOverlayTooltip, kTooltipSwitchTealGame, true);
+ break;
+
+ case kGameTeal:
+ showFrame(kOverlayTooltip, kTooltipSwitchGoldGame, true);
+ break;
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuRewindGame:
+ if (!_index || _currentTime < _time) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ if (hasTimeDelta())
+ _handleTimeDelta = false;
+
+ showFrame(kOverlayEggButtons, kButtonRewindPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ rewindTime();
+
+ _handleTimeDelta = false;
+ } else {
+ showFrame(kOverlayEggButtons, kButtonRewind, true);
+ showFrame(kOverlayTooltip, kTooltipRewind, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuForwardGame:
+ if (_index2 <= _index || _currentTime > _time) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ if (hasTimeDelta())
+ _handleTimeDelta = false;
+
+ showFrame(kOverlayEggButtons, kButtonForwardPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ forwardTime();
+
+ _handleTimeDelta = false;
+ } else {
+ showFrame(kOverlayEggButtons, kButtonForward, true);
+ showFrame(kOverlayTooltip, kTooltipFastForward, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuParis:
+ moveToCity(kParis, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuStrasBourg:
+ moveToCity(kStrasbourg, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuMunich:
+ moveToCity(kMunich, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuVienna:
+ moveToCity(kVienna, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuBudapest:
+ moveToCity(kBudapest, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuBelgrade:
+ moveToCity(kBelgrade, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuConstantinople:
+ moveToCity(kConstantinople, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuDecreaseVolume:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot decrease volume further
+ if (getVolume() == 0) {
+ showFrame(kOverlayButtons, kButtonVolume, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipVolumeDown, true);
+
+ // Show highlight on button & adjust volume if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonVolumeDownPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setVolume(getVolume() - 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ uint32 nextFrameCount = getFrameCount() + 15;
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ getSound()->updateQueue();
+ }
+ } else {
+ showFrame(kOverlayButtons, kButtonVolumeDown, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuIncreaseVolume:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase volume further
+ if (getVolume() >= 7) {
+ showFrame(kOverlayButtons, kButtonVolume, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipVolumeUp, true);
+
+ // Show highlight on button & adjust volume if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonVolumeUpPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setVolume(getVolume() + 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ uint32 nextFrameCount = getFrameCount() + 15;
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ getSound()->updateQueue();
+ }
+ } else {
+ showFrame(kOverlayButtons, kButtonVolumeUp, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuDecreaseBrightness:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase brightness further
+ if (getBrightness() == 0) {
+ showFrame(kOverlayButtons, kButtonBrightness, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipBrightnessDown, true);
+
+ // Show highlight on button & adjust brightness if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonBrightnessDownPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setBrightness(getBrightness() - 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ // Reshow the background and frames (they will pick up the new brightness through the GraphicsManager)
+ _engine->getGraphicsManager()->draw(getScenes()->get((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2)), GraphicsManager::kBackgroundC, true);
+ showFrame(kOverlayTooltip, kTooltipBrightnessDown, false);
+ showFrame(kOverlayButtons, kButtonBrightnessDownPushed, false);
+ } else {
+ showFrame(kOverlayButtons, kButtonBrightnessDown, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuIncreaseBrightness:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase brightness further
+ if (getBrightness() >= 6) {
+ showFrame(kOverlayButtons, kButtonBrightness, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipBrightnessUp, true);
+
+ // Show highlight on button & adjust brightness if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonBrightnessUpPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setBrightness(getBrightness() + 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ // Reshow the background and frames (they will pick up the new brightness through the GraphicsManager)
+ _engine->getGraphicsManager()->draw(getScenes()->get((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2)), GraphicsManager::kBackgroundC, true);
+ showFrame(kOverlayTooltip, kTooltipBrightnessUp, false);
+ showFrame(kOverlayButtons, kButtonBrightnessUpPushed, false);
+ } else {
+ showFrame(kOverlayButtons, kButtonBrightnessUp, true);
+ }
+ break;
+ }
+
+ return true;
+}
+
+void Menu::setLogicEventHandlers() {
+ SET_EVENT_HANDLERS(Logic, getLogic());
+ clear();
+ _isShowingMenu = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game-related
+//////////////////////////////////////////////////////////////////////////
+void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
+
+ bool useSameIndex = true;
+
+ if (getGlobalTimer()) {
+ value = 0;
+
+ // Check if the CD file is present
+ ArchiveIndex index = kArchiveCd1;
+ switch (getProgress().chapter) {
+ default:
+ case kChapter1:
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ index = kArchiveCd2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ index = kArchiveCd3;
+ break;
+ }
+
+ if (ResourceManager::isArchivePresent(index)) {
+ setGlobalTimer(0);
+ useSameIndex = false;
+
+ // TODO remove existing savegame and reset index & savegame name
+ warning("Menu::initGame: not implemented!");
+ }
+
+ doSavegame = false;
+ } else {
+ // TODO rename saves?
+ }
+
+ // Create a new savegame if needed
+ if (!SaveLoad::isSavegamePresent(_gameId))
+ SaveLoad::writeMainHeader(_gameId);
+
+ if (doSavegame)
+ getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone);
+
+ if (!getGlobalTimer()) {
+ // TODO: remove existing savegame temp file
+ }
+
+ // Init savegame and get the header data
+ getSaveLoad()->initSavegame(_gameId, true);
+ SaveLoad::SavegameMainHeader header;
+ if (!SaveLoad::loadMainHeader(_gameId, &header))
+ error("Menu::init: Corrupted savegame - Recovery path not implemented!");
+
+ // Init Menu values
+ _index2 = header.index;
+ _lowerTime = getSaveLoad()->getEntry(_index2)->time;
+
+ if (useSameIndex)
+ _index = _index2;
+
+ //if (!getGlobalTimer())
+ // _index3 = 0;
+
+ if (!getProgress().chapter)
+ getProgress().chapter = kChapter1;
+
+ getState()->time = getSaveLoad()->getEntry(_index)->time;
+ getProgress().chapter = getSaveLoad()->getEntry(_index)->chapter;
+
+ if (_lowerTime >= kTimeStartGame) {
+ _currentTime = getState()->time;
+ _time = getState()->time;
+ _clock->draw(_time);
+ _trainLine->draw(_time);
+
+ initTime(type, value);
+ }
+}
+
+void Menu::startGame() {
+ // TODO: we need to reset the current scene
+ getState()->scene = kSceneDefault;
+
+ getEntities()->setup(true, kEntityPlayer);
+ warning("Menu::startGame: not implemented!");
+}
+
+// Switch to the next savegame
+void Menu::switchGame() {
+
+ // Switch back to blue game is the current game is not started
+ _gameId = SaveLoad::isSavegameValid(_gameId) ? getNextGameId() : kGameBlue;
+
+ // Initialize savegame if needed
+ if (!SaveLoad::isSavegamePresent(_gameId))
+ SaveLoad::writeMainHeader(_gameId);
+
+ getState()->time = 0;
+
+ // Clear menu elements
+ _clock->clear();
+ _trainLine->clear();
+
+ // Clear loaded savegame data
+ getSaveLoad()->clearEntries();
+
+ init(false, kSavegameTypeIndex, 0);
+}
+
+bool Menu::isGameFinished() const {
+ SaveLoad::SavegameEntryHeader *data = getSaveLoad()->getEntry(_index);
+
+ if (_index2 != _index)
+ return false;
+
+ if (data->type != SaveLoad::kHeaderType2)
+ return false;
+
+ return (data->event == kEventAnnaKilled
+ || data->event == kEventKronosHostageAnnaNoFirebird
+ || data->event == kEventKahinaPunchBaggageCarEntrance
+ || data->event == kEventKahinaPunchBlue
+ || data->event == kEventKahinaPunchYellow
+ || data->event == kEventKahinaPunchSalon
+ || data->event == kEventKahinaPunchKitchen
+ || data->event == kEventKahinaPunchBaggageCar
+ || data->event == kEventKahinaPunchCar
+ || data->event == kEventKahinaPunchSuite4
+ || data->event == kEventKahinaPunchRestaurant
+ || data->event == kEventKahinaPunch
+ || data->event == kEventKronosGiveFirebird
+ || data->event == kEventAugustFindCorpse
+ || data->event == kEventMertensBloodJacket
+ || data->event == kEventMertensCorpseFloor
+ || data->event == kEventMertensCorpseBed
+ || data->event == kEventCoudertBloodJacket
+ || data->event == kEventGendarmesArrestation
+ || data->event == kEventAbbotDrinkGiveDetonator
+ || data->event == kEventMilosCorpseFloor
+ || data->event == kEventLocomotiveAnnaStopsTrain
+ || data->event == kEventTrainStopped
+ || data->event == kEventCathVesnaRestaurantKilled
+ || data->event == kEventCathVesnaTrainTopKilled
+ || data->event == kEventLocomotiveConductorsDiscovered
+ || data->event == kEventViennaAugustUnloadGuns
+ || data->event == kEventViennaKronosFirebird
+ || data->event == kEventVergesAnnaDead
+ || data->event == kEventTrainExplosionBridge
+ || data->event == kEventKronosBringNothing);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Overlays & elements
+//////////////////////////////////////////////////////////////////////////
+void Menu::checkHotspots() {
+ if (!_isShowingMenu)
+ return;
+
+ if (!getFlags()->shouldRedraw)
+ return;
+
+ if (_isShowingCredits)
+ return;
+
+ SceneHotspot *hotspot = NULL;
+ getScenes()->get(getState()->scene)->checkHotSpot(getCoords(), &hotspot);
+
+ if (hotspot)
+ handleEvent((StartMenuAction)hotspot->action, _mouseFlags);
+ else
+ hideOverlays();
+}
+
+void Menu::hideOverlays() {
+ _lastHotspot = NULL;
+
+ // Hide all menu overlays
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ showFrame(it->_key, -1, false);
+
+ getScenes()->drawFrames(true);
+}
+
+void Menu::showFrame(StartMenuOverlay overlayType, int index, bool redraw) {
+ if (index == -1) {
+ getScenes()->removeFromQueue(_frames[overlayType]);
+ } else {
+ // Check that the overlay is valid
+ if (!_frames[overlayType])
+ return;
+
+ // Remove the frame and add a new one with the proper index
+ getScenes()->removeFromQueue(_frames[overlayType]);
+ _frames[overlayType]->setFrame((uint16)index);
+ getScenes()->addToQueue(_frames[overlayType]);
+ }
+
+ if (redraw)
+ getScenes()->drawFrames(true);
+}
+
+// Remove all frames from the queue
+void Menu::clear() {
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ getScenes()->removeAndRedraw(&it->_value, false);
+
+ clearBg(GraphicsManager::kBackgroundOverlay);
+}
+
+// Get the sequence name to use for the acorn highlight, depending of the currently loaded savegame
+Common::String Menu::getAcornSequenceName(GameId id) const {
+ Common::String name = "";
+ switch (id) {
+ default:
+ case kGameBlue:
+ name = "aconblu3.seq";
+ break;
+
+ case kGameRed:
+ name = "aconred.seq";
+ break;
+
+ case kGameGreen:
+ name = "acongren.seq";
+ break;
+
+ case kGamePurple:
+ name = "aconpurp.seq";
+ break;
+
+ case kGameTeal:
+ name = "aconteal.seq";
+ break;
+
+ case kGameGold:
+ name = "acongold.seq";
+ break;
+ }
+
+ return name;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Time
+//////////////////////////////////////////////////////////////////////////
+void Menu::initTime(SavegameType type, uint32 value) {
+ if (!value)
+ return;
+
+ // The savegame entry index
+ uint32 entryIndex = 0;
+
+ switch (type) {
+ default:
+ break;
+
+ case kSavegameTypeIndex:
+ entryIndex = (_index <= value) ? 1 : _index - value;
+ break;
+
+ case kSavegameTypeTime:
+ if (value < kTimeStartGame)
+ break;
+
+ entryIndex = _index;
+ if (!entryIndex)
+ break;
+
+ // Iterate through existing entries
+ do {
+ if (getSaveLoad()->getEntry(entryIndex)->time <= value)
+ break;
+
+ entryIndex--;
+ } while (entryIndex);
+ break;
+
+ case kSavegameTypeEvent:
+ entryIndex = _index;
+ if (!entryIndex)
+ break;
+
+ do {
+ if (getSaveLoad()->getEntry(entryIndex)->event == (EventIndex)value)
+ break;
+
+ entryIndex--;
+ } while (entryIndex);
+ break;
+
+ case kSavegameTypeEvent2:
+ // TODO rewrite in a more legible way
+ if (_index > 1) {
+ uint32 index = _index;
+ do {
+ if (getSaveLoad()->getEntry(index)->event == (EventIndex)value)
+ break;
+
+ index--;
+ } while (index > 1);
+
+ entryIndex = index - 1;
+ } else {
+ entryIndex = _index - 1;
+ }
+ break;
+ }
+
+ if (entryIndex) {
+ _currentIndex = entryIndex;
+ updateTime(getSaveLoad()->getEntry(entryIndex)->time);
+ }
+}
+
+void Menu::updateTime(uint32 time) {
+ if (_currentTime == _time)
+ _delta = 0;
+
+ _currentTime = time;
+
+ if (_time != time) {
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getSound()->playSoundWithSubtitles((_currentTime >= _time) ? "LIB042" : "LIB041", SoundManager::kFlagMenuClock, kEntityChapters);
+ adjustIndex(_currentTime, _time, false);
+ }
+}
+
+void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
+ uint32 index = 0;
+ int32 timeDelta = -1;
+
+ if (time1 != time2) {
+
+ index = _index;
+
+ if (time2 >= time1) {
+ if (searchEntry) {
+ uint32 currentIndex = _index;
+
+ if ((int32)_index >= 0) {
+ do {
+ // Calculate new delta
+ int32 newDelta = time1 - getSaveLoad()->getEntry(currentIndex)->time;
+
+ if (newDelta >= 0 && timeDelta >= newDelta) {
+ timeDelta = newDelta;
+ index = currentIndex;
+ }
+
+ --currentIndex;
+ } while ((int32)currentIndex >= 0);
+ }
+ } else {
+ index = _index - 1;
+ }
+ } else {
+ if (searchEntry) {
+ uint32 currentIndex = _index;
+
+ if (_index2 >= _index) {
+ do {
+ // Calculate new delta
+ int32 newDelta = getSaveLoad()->getEntry(currentIndex)->time - time1;
+
+ if (newDelta >= 0 && timeDelta > newDelta) {
+ timeDelta = newDelta;
+ index = currentIndex;
+ }
+
+ ++currentIndex;
+ } while (currentIndex >= _index2);
+ }
+ } else {
+ index = _index + 1;
+ }
+ }
+
+ _index = index;
+ checkHotspots();
+ }
+
+ if (_index == _currentIndex) {
+ if (getProgress().chapter != getSaveLoad()->getEntry(index)->chapter)
+ getProgress().chapter = getSaveLoad()->getEntry(_index)->chapter;
+ }
+}
+
+void Menu::goToTime(uint32 time) {
+
+ uint32 entryIndex = 0;
+ uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getEntry(0)->time - time));
+ uint32 index = 0;
+
+ do {
+ uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getEntry(index)->time - time));
+ if (deltaTime2 < deltaTime) {
+ deltaTime = deltaTime2;
+ entryIndex = index;
+ }
+
+ ++index;
+ } while (_index2 >= index);
+
+ _currentIndex = entryIndex;
+ updateTime(getSaveLoad()->getEntry(entryIndex)->time);
+}
+
+void Menu::setTime() {
+ _currentIndex = _index;
+ _currentTime = getSaveLoad()->getEntry(_currentIndex)->time;
+
+ if (_time == _currentTime)
+ adjustTime();
+}
+
+void Menu::forwardTime() {
+ if (_index2 <= _index)
+ return;
+
+ _currentIndex = _index2;
+ updateTime(getSaveLoad()->getEntry(_currentIndex)->time);
+}
+
+void Menu::rewindTime() {
+ if (!_index)
+ return;
+
+ _currentIndex = 0;
+ updateTime(getSaveLoad()->getEntry(_currentIndex)->time);
+}
+
+void Menu::adjustTime() {
+ uint32 originalTime = _time;
+
+ // Adjust time delta
+ uint32 timeDelta = (_delta >= 90) ? 9 : (9 * _delta + 89) / 90;
+
+ if (_currentTime < _time) {
+ _time -= 900 * timeDelta;
+
+ if (_time >= _currentTime)
+ _time = _currentTime;
+ } else {
+ _time += 900 * timeDelta;
+
+ if (_time < _currentTime)
+ _time = _currentTime;
+ }
+
+ if (_currentTime == _time && getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ _clock->draw(_time);
+ _trainLine->draw(_time);
+ getScenes()->drawFrames(true);
+
+ adjustIndex(_time, originalTime, true);
+
+ ++_delta;
+}
+
+void Menu::moveToCity(CityButton city, bool clicked) {
+ uint32 time = (uint32)_cityButtonsInfo[city].time;
+
+ // TODO Check if we have access (there seems to be more checks on some internal times) - probably : current_time (menu only) / game time / some other?
+ if (_lowerTime < time || _time == time || _currentTime == time) {
+ hideOverlays();
+ return;
+ }
+
+ // Show city overlay
+ showFrame((StartMenuOverlay)((_cityButtonsInfo[city].index >> 6) + 3), _cityButtonsInfo[city].index & 63, true);
+
+ if (clicked) {
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ goToTime(time);
+
+ _handleTimeDelta = true;
+
+ return;
+ }
+
+ // Special case of first and last cities
+ if (city == kParis || city == kConstantinople) {
+ showFrame(kOverlayTooltip, (city == kParis) ? kTooltipRewindParis : kTooltipForwardConstantinople, true);
+ return;
+ }
+
+ showFrame(kOverlayTooltip, (_time <= time) ? _cityButtonsInfo[city].forward : _cityButtonsInfo[city].rewind, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound / Brightness
+//////////////////////////////////////////////////////////////////////////
+
+// Get current volume (converted internal ScummVM value)
+uint32 Menu::getVolume() const {
+ return getState()->volume;
+}
+
+// Set the volume (converts to ScummVM values)
+void Menu::setVolume(uint32 volume) const {
+ getState()->volume = volume;
+
+ // Clamp volume
+ uint32 value = volume * Audio::Mixer::kMaxMixerVolume / 7;
+
+ if (value > Audio::Mixer::kMaxMixerVolume)
+ value = Audio::Mixer::kMaxMixerVolume;
+
+ _engine->_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, (int32)value);
+}
+
+uint32 Menu::getBrightness() const {
+ return getState()->brightness;
+}
+
+void Menu::setBrightness(uint32 brightness) const {
+ getState()->brightness = brightness;
+
+ // TODO reload cursor & font with adjusted brightness
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/menu.h b/engines/lastexpress/game/menu.h
new file mode 100644
index 0000000000..54d1eb65ec
--- /dev/null
+++ b/engines/lastexpress/game/menu.h
@@ -0,0 +1,211 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MENU_H
+#define LASTEXPRESS_MENU_H
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/hashmap.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Scene;
+class SceneHotspot;
+
+class Clock;
+class TrainLine;
+
+class Menu : public EventHandler {
+public:
+ Menu(LastExpressEngine *engine);
+ ~Menu();
+
+ void show(bool doSavegame, SavegameType type, uint32 value);
+
+ // Event handling
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ bool isShown() const { return _isShowingMenu; }
+
+ GameId getGameId() const { return _gameId; }
+
+private:
+ // Start menu events
+ enum StartMenuAction {
+ kMenuContinue = 1,
+ kMenuCredits = 2,
+ kMenuQuitGame = 3,
+ kMenuCase4 = 4,
+ kMenuSwitchSaveGame = 6,
+ kMenuRewindGame = 7,
+ kMenuForwardGame = 8,
+ kMenuParis = 10,
+ kMenuStrasBourg = 11,
+ kMenuMunich = 12,
+ kMenuVienna = 13,
+ kMenuBudapest = 14,
+ kMenuBelgrade = 15,
+ kMenuConstantinople = 16,
+ kMenuDecreaseVolume = 17,
+ kMenuIncreaseVolume = 18,
+ kMenuDecreaseBrightness = 19,
+ kMenuIncreaseBrightness = 20
+ };
+
+ // City buttons
+ enum CityButton {
+ kParis = 0,
+ kStrasbourg = 1,
+ kMunich = 2,
+ kVienna = 3,
+ kBudapest = 4,
+ kBelgrade = 5,
+ kConstantinople = 6
+ };
+
+ // Start menu overlay elements
+ enum StartMenuOverlay {
+ kOverlayTooltip, // 0
+ kOverlayEggButtons,
+ kOverlayButtons,
+ kOverlayAcorn,
+ kOverlayCity1,
+ kOverlayCity2, // 5
+ kOverlayCity3,
+ kOverlayCredits
+ };
+
+ LastExpressEngine *_engine;
+
+ // Sequences
+ Sequence* _seqTooltips;
+ Sequence* _seqEggButtons;
+ Sequence* _seqButtons;
+ Sequence* _seqAcorn;
+ Sequence* _seqCity1;
+ Sequence* _seqCity2;
+ Sequence* _seqCity3;
+ Sequence* _seqCredits;
+
+ GameId _gameId;
+
+ // Indicator to know if we need to show the start animation when showMenu is called
+ bool _hasShownStartScreen;
+ bool _hasShownIntro;
+
+ bool _isShowingCredits;
+ bool _isGameStarted;
+ bool _isShowingMenu;
+
+
+ uint16 _creditsSequenceIndex;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Event handling
+ uint32 _checkHotspotsTicks;
+ Common::EventType _mouseFlags;
+ SceneHotspot *_lastHotspot;
+
+ void init(bool doSavegame, SavegameType type, uint32 value);
+ void setup();
+ bool handleEvent(StartMenuAction action, Common::EventType type);
+ void checkHotspots();
+ void setLogicEventHandlers();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Game-related
+ void startGame();
+ void switchGame();
+ bool isGameFinished() const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Overlays & elements
+ Clock *_clock;
+ TrainLine *_trainLine;
+
+ struct MenuOverlays_EqualTo {
+ bool operator()(const StartMenuOverlay& x, const StartMenuOverlay& y) const { return x == y; }
+ };
+
+ struct MenuOverlays_Hash {
+ uint operator()(const StartMenuOverlay& x) const { return x; }
+ };
+
+ typedef Common::HashMap<StartMenuOverlay, SequenceFrame *, MenuOverlays_Hash, MenuOverlays_EqualTo> MenuFrames;
+
+ MenuFrames _frames;
+
+ void hideOverlays();
+ void showFrame(StartMenuOverlay overlay, int index, bool redraw);
+
+ void clear();
+
+ // TODO: remove?
+ void moveToCity(CityButton city, bool clicked);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Misc
+ Common::String getAcornSequenceName(GameId id) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Time
+ uint32 _currentIndex; // current savegame entry
+ uint32 _currentTime; // current game time
+ uint32 _lowerTime; // lower time value
+
+ uint32 _index;
+ uint32 _index2;
+ uint32 _time;
+ uint32 _delta;
+ bool _handleTimeDelta;
+
+ void initTime(SavegameType type, uint32 time);
+ void updateTime(uint32 time);
+ void adjustTime();
+ void adjustIndex(uint32 time1, uint32 time2, bool searchEntry);
+ void goToTime(uint32 time);
+ void setTime();
+ void forwardTime();
+ void rewindTime();
+ bool hasTimeDelta() { return (_currentTime - _time) >= 1; }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sound/Brightness related
+ uint32 getVolume() const;
+ void setVolume(uint32 volume) const;
+ uint32 getBrightness() const;
+ void setBrightness(uint32 brightness) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MENU_H
diff --git a/engines/lastexpress/game/object.cpp b/engines/lastexpress/game/object.cpp
new file mode 100644
index 0000000000..0b336b941f
--- /dev/null
+++ b/engines/lastexpress/game/object.cpp
@@ -0,0 +1,108 @@
+/* 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/object.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Common::String Objects::Object::toString() {
+ return Common::String::printf("{ %s - %d - %d - %d - %d }", ENTITY_NAME(entity), location, cursor, cursor2, location2);
+}
+
+Objects::Objects(LastExpressEngine *engine) : _engine(engine) {}
+
+const Objects::Object Objects::get(ObjectIndex index) const {
+ if (index >= kObjectMax)
+ error("Objects::get - internal error: invalid object index (%d)", index);
+
+ return _objects[index];
+}
+
+void Objects::update(ObjectIndex index, EntityIndex entity, ObjectLocation location, CursorStyle cursor, CursorStyle cursor2) {
+ if (index >= kObjectMax)
+ return;
+
+ Object *object = &_objects[index];
+
+ // Store original location
+ ObjectLocation original_location = object->location;
+
+ // Update entity
+ object->entity = entity;
+ object->location = location;
+
+ if (cursor != kCursorKeepValue || cursor2 != kCursorKeepValue) {
+ if (cursor != kCursorKeepValue)
+ object->cursor = cursor;
+ if (cursor2 != kCursorKeepValue)
+ object->cursor2 = cursor2;
+
+ getLogic()->updateCursor();
+ }
+
+ getFlags()->flag_3 = true;
+
+ // Compartments
+ if (original_location != location && (original_location == kObjectLocation2 || location == kObjectLocation2))
+ if ((index >= kObjectCompartment1 && index <= kObjectCompartment8)
+ || (index >= kObjectCompartmentA && index <= kObjectCompartmentF)) {
+ getScenes()->updateDoorsAndClock();
+ }
+}
+
+void Objects::updateLocation2(ObjectIndex index, ObjectLocation location2) {
+ if (index >= kObjectMax)
+ return;
+
+ _objects[index].location2 = location2;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void Objects::saveLoadWithSerializer(Common::Serializer &) {
+ error("Objects::saveLoadWithSerializer: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String Objects::toString() {
+ Common::String ret = "";
+
+ for (int i = 0; i < kObjectMax; i++)
+ ret += Common::String::printf("%d : %s\n", i, _objects[i].toString().c_str());
+
+ return ret;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/object.h b/engines/lastexpress/game/object.h
new file mode 100644
index 0000000000..3417e9bfcf
--- /dev/null
+++ b/engines/lastexpress/game/object.h
@@ -0,0 +1,83 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_OBJECT_H
+#define LASTEXPRESS_OBJECT_H
+
+#include "lastexpress/shared.h"
+
+#include "common/serializer.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Objects : Common::Serializable {
+public:
+
+ struct Object { // All fields should be saved as bytes
+ EntityIndex entity;
+ ObjectLocation location;
+ CursorStyle cursor;
+ CursorStyle cursor2;
+ ObjectLocation location2;
+
+ Object() {
+ entity = kEntityPlayer;
+ location = kObjectLocationNone;
+ cursor = kCursorHandKnock;
+ cursor2 = kCursorHandKnock;
+ location2 = kObjectLocationNone;
+ }
+
+ Common::String toString();
+ };
+
+ Objects(LastExpressEngine *engine);
+
+ const Object get(ObjectIndex index) const;
+ void update(ObjectIndex index, EntityIndex entity, ObjectLocation location, CursorStyle cursor, CursorStyle cursor2);
+ void updateLocation2(ObjectIndex index, ObjectLocation location2);
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ LastExpressEngine* _engine;
+
+ Object _objects[kObjectMax];
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_OBJECT_H
diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp
new file mode 100644
index 0000000000..1bd3d8239b
--- /dev/null
+++ b/engines/lastexpress/game/savegame.cpp
@@ -0,0 +1,310 @@
+/* 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/logic.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/debug.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+#include "common/file.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+// Savegame signatures
+#define SAVEGAME_SIGNATURE 0x12001200
+#define SAVEGAME_HEADER 0xE660E660
+
+// Names of savegames
+static const struct {
+ const char *saveFile;
+} gameInfo[6] = {
+ {"blue.egg"},
+ {"red.egg"},
+ {"green.egg"},
+ {"purple.egg"},
+ {"teal.egg"},
+ {"gold.egg"}
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Constructors
+//////////////////////////////////////////////////////////////////////////
+
+SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine) {
+ _gameTicksLastSavegame = 0;
+}
+
+SaveLoad::~SaveLoad() {
+ //Zero passed pointers
+ _engine = NULL;
+
+ clearEntries();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Save & Load
+//////////////////////////////////////////////////////////////////////////
+
+// Load game
+bool SaveLoad::loadGame(GameId id) {
+
+ if (!SaveLoad::isSavegamePresent(id))
+ return false;
+
+ //Common::InSaveFile *save = SaveLoad::openForLoading(id);
+ // Validate header
+
+
+
+
+ error("SaveLoad::loadgame: not implemented!");
+
+ return false;
+}
+
+// Save game
+void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
+
+ // Save ticks
+ _gameTicksLastSavegame = getState()->timeTicks;
+
+ warning("SaveLoad::savegame: not implemented!");
+}
+
+void SaveLoad::saveVolumeBrightness() {
+ warning("SaveLoad::saveVolumeBrightness: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Static Members
+//////////////////////////////////////////////////////////////////////////
+
+// Check if a specific savegame exists
+bool SaveLoad::isSavegamePresent(GameId id) {
+ if (g_system->getSavefileManager()->listSavefiles(getSavegameName(id)).size() == 0)
+ return false;
+
+ return true;
+}
+
+// Check if the game has been started in the specific savegame
+bool SaveLoad::isSavegameValid(GameId id) {
+ if (!isSavegamePresent(id)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getSavegameName(id).c_str());
+ return false;
+ }
+
+ SavegameMainHeader header;
+ if (!loadMainHeader(id, &header))
+ return false;
+
+ return validateMainHeader(header);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Headers
+//////////////////////////////////////////////////////////////////////////
+bool SaveLoad::loadMainHeader(GameId id, SavegameMainHeader* header) {
+ // Read first 32 bytes of savegame
+ Common::InSaveFile *save = openForLoading(id);
+ if (!save) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot open savegame for reading: %s", getSavegameName(id).c_str());
+ return false;
+ }
+
+ // Check there is enough data
+ if (save->size() < 32) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes): %s", save->size(), getSavegameName(id).c_str());
+ delete save;
+ return false;
+ }
+
+ header->signature = save->readUint32LE();
+ header->index = save->readUint32LE();
+ header->time = save->readUint32LE();
+ header->field_C = save->readUint32LE();
+ header->field_10 = save->readUint32LE();
+ header->brightness = save->readSint32LE();
+ header->volume = save->readSint32LE();
+ header->field_1C = save->readUint32LE();
+
+ delete save;
+
+ // Valide the header
+ if (!validateMainHeader(*header)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header for savegame %s.", getSavegameName(id).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+void SaveLoad::loadEntryHeader(Common::InSaveFile *save, SavegameEntryHeader *header) {
+ header->signature = save->readUint32LE();
+ header->type = (HeaderType)save->readUint32LE();
+ header->time = save->readUint32LE();
+ header->field_C = save->readUint32LE();
+ header->chapter = (ChapterIndex)save->readUint32LE();
+ header->event = (EventIndex)save->readUint32LE();
+ header->field_18 = save->readUint32LE();
+ header->field_1C = save->readUint32LE();
+}
+
+bool SaveLoad::validateMainHeader(const SavegameMainHeader &header) {
+ if (header.signature != SAVEGAME_SIGNATURE)
+ return false;
+
+ /* Check not needed as it can never be < 0
+ if (header.chapter < 0)
+ return false;*/
+
+ if (header.time < 32)
+ return false;
+
+ if (header.field_C < 32)
+ return false;
+
+ if (header.field_10 != 1 && header.field_10)
+ return false;
+
+ if (header.brightness < 0 || header.brightness > 6)
+ return false;
+
+ if (header.volume < 0 || header.volume > 7)
+ return false;
+
+ if (header.field_1C != 9)
+ return false;
+
+ return true;
+}
+
+bool SaveLoad::validateEntryHeader(const SavegameEntryHeader &header) {
+ if (header.signature != SAVEGAME_HEADER)
+ return false;
+
+ if (header.type < kHeaderType1 || header.type > kHeaderType5)
+ return false;
+
+ if (header.time < kTimeStartGame || header.time > kTimeCityConstantinople)
+ return false;
+
+ if (header.field_C <= 0 || header.field_C >= 15)
+ return false;
+
+ /* No check for < 0, as it cannot happen normaly */
+ if (header.chapter == 0)
+ return false;
+
+ return true;
+}
+
+SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) {
+ if (index >= _gameHeaders.size())
+ error("SaveLoad::getEntry: invalid index (was:%d, max:%d)", index, _gameHeaders.size() - 1);
+
+ return _gameHeaders[index];
+}
+
+void SaveLoad::clearEntries() {
+ for (uint i = 0; i < _gameHeaders.size(); i++)
+ SAFE_DELETE(_gameHeaders[i]);
+
+ _gameHeaders.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Init
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::writeMainHeader(GameId id) {
+ Common::OutSaveFile *save = openForSaving(id);
+ if (!save) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::initSavegame - Cannot open savegame for writing: %s", getSavegameName(id).c_str());
+ return;
+ }
+
+ // Write default values to savegame
+ save->writeUint32LE(SAVEGAME_SIGNATURE);
+ save->writeUint32LE(0);
+ save->writeUint32LE(32);
+ save->writeUint32LE(32);
+ save->writeUint32LE(0);
+ save->writeUint32LE(3);
+ save->writeUint32LE(7);
+ save->writeUint32LE(9);
+
+ delete save;
+}
+
+void SaveLoad::initSavegame(GameId id, bool resetHeaders) {
+ //Common::OutSaveFile *save = openForSaving(id);
+ //if (!save) {
+ // debugC(2, kLastExpressDebugSavegame, "SaveLoad::initSavegame - Cannot open savegame for writing: %s", getSavegameName(id).c_str());
+ // return;
+ //}
+
+ if (resetHeaders) {
+ clearEntries();
+
+ SavegameEntryHeader *header = new SavegameEntryHeader();
+ header->time = kTimeCityParis;
+ header->chapter = kChapter1;
+
+ _gameHeaders.push_back(header);
+ }
+
+ // Open the savegame and read all game headers
+
+ warning("SaveLoad::initSavegame: not implemented!");
+
+ //delete save;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private methods
+//////////////////////////////////////////////////////////////////////////
+
+// Get the file name from the savegame ID
+Common::String SaveLoad::getSavegameName(GameId id) {
+ if (id >= 6)
+ error("SaveLoad::getSavegameName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id);
+
+ return gameInfo[id].saveFile;
+}
+
+Common::InSaveFile *SaveLoad::openForLoading(GameId id) {
+ return g_system->getSavefileManager()->openForLoading(getSavegameName(id));
+}
+
+Common::OutSaveFile *SaveLoad::openForSaving(GameId id) {
+ return g_system->getSavefileManager()->openForSaving(getSavegameName(id));
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h
new file mode 100644
index 0000000000..739e6a1798
--- /dev/null
+++ b/engines/lastexpress/game/savegame.h
@@ -0,0 +1,173 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SAVELOAD_H
+#define LASTEXPRESS_SAVELOAD_H
+
+/*
+ Savegame format
+ ---------------
+
+ header: 32 bytes
+ uint32 {4} - signature: 0x12001200
+ uint32 {4} - chapter - needs to be [0; 5]
+ uint32 {4} - time - needs to be >= 32 [1061100; timeMax]
+ uint32 {4} - ?? needs to be >= 32
+ uint32 {4} - ?? needs to be = 1
+ uint32 {4} - Brightness (needs to be [0-6])
+ uint32 {4} - Volume (needs to be [0-7])
+ uint32 {4} - ?? needs to be = 9
+
+ Game data Format
+ -----------------
+
+ uint32 {4} - entity
+ uint32 {4} - current time
+ uint32 {4} - time delta (how much a tick is in "real" time)
+ uint32 {4} - time ticks
+ uint32 {4} - scene Index max: 2500
+ byte {1} - use backup scene
+ uint32 {4} - backup Scene Index 1 max: 2500
+ uint32 {4} - backup Scene Index 2 max: 2500
+ uint32 {4} - selected inventory item max: 32
+ uint32 {4*100*10} - positions (by car)
+ uint32 {4*16} - compartments
+ uint32 {4*16} - compartments ??
+ uint32 {4*128} - game progress
+ byte {512} - game events
+ byte {7*32} - inventory
+ byte {5*128} - objects
+ byte {1262*40} - entities (characters and train entities)
+
+ uint32 {4} - sound queue state
+ uint32 {4} - ??
+ uint32 {4} - number of sound entries
+ byte {count*68} - sound entries
+
+ byte {16*128} - save point data
+ uint32 {4} - number of save points (max: 128)
+ byte {count*16} - save points
+
+ ... more unknown stuff
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "common/savefile.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class SaveLoad {
+public:
+ enum HeaderType {
+ kHeaderTypeNone = 0,
+ kHeaderType1 = 1,
+ kHeaderType2 = 2,
+ kHeaderType3 = 3,
+ kHeaderType4 = 4,
+ kHeaderType5 = 5
+ };
+
+ struct SavegameMainHeader {
+ uint32 signature;
+ uint32 index;
+ uint32 time;
+ uint32 field_C;
+ uint32 field_10;
+ int32 brightness;
+ int32 volume;
+ uint32 field_1C;
+ };
+
+ struct SavegameEntryHeader {
+ uint32 signature;
+ HeaderType type;
+ uint32 time;
+ int field_C;
+ ChapterIndex chapter;
+ EventIndex event;
+ int field_18;
+ int field_1C;
+
+ SavegameEntryHeader() {
+ signature = 0;
+ type = kHeaderTypeNone;
+ time = 0;
+ field_C = 0;
+ chapter = kChapterAll;
+ event = kEventNone;
+ field_18 = 0;
+ field_1C = 0;
+ }
+ };
+
+ SaveLoad(LastExpressEngine *engine);
+ ~SaveLoad();
+
+ // Save & Load
+ bool loadGame(GameId id);
+ void saveGame(SavegameType type, EntityIndex entity, uint32 value);
+
+ void saveVolumeBrightness();
+
+ // Init
+ void initSavegame(GameId id, bool resetHeaders);
+ static void writeMainHeader(GameId id);
+
+ // Getting information
+ static bool isSavegamePresent(GameId id);
+ static bool isSavegameValid(GameId id);
+
+ // Opening save files
+ static Common::InSaveFile *openForLoading(GameId id);
+ static Common::OutSaveFile *openForSaving(GameId id);
+
+ // Headers
+ static bool loadMainHeader(GameId id, SavegameMainHeader* header);
+ SavegameEntryHeader *getEntry(uint32 index);
+ void clearEntries();
+
+ uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; }
+
+private:
+ LastExpressEngine *_engine;
+
+ uint32 _gameTicksLastSavegame;
+ Common::Array<SavegameEntryHeader *> _gameHeaders;
+
+ static Common::String getSavegameName(GameId id);
+
+ static void loadEntryHeader(Common::InSaveFile *save, SavegameEntryHeader* header);
+
+ static bool validateMainHeader(const SavegameMainHeader &header);
+ static bool validateEntryHeader(const SavegameEntryHeader &header);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SAVELOAD_H
diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp
new file mode 100644
index 0000000000..e7bae494ed
--- /dev/null
+++ b/engines/lastexpress/game/savepoint.cpp
@@ -0,0 +1,296 @@
+/* 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/savepoint.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+
+namespace LastExpress {
+
+SavePoints::SavePoints(LastExpressEngine *engine) : _engine(engine) {
+ for (int i = 0; i < 40; i++)
+ _callbacks[i] = NULL;
+}
+
+SavePoints::~SavePoints() {
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savepoints
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param) {
+ if (_savepoints.size() >= _savePointsMaxSize)
+ return;
+
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ point.param.intValue = param;
+
+ _savepoints.push_back(point);
+}
+
+void SavePoints::push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char* param) {
+ if (_savepoints.size() >= _savePointsMaxSize)
+ return;
+
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ strcpy((char *)&point.param.charValue, param);
+
+ _savepoints.push_back(point);
+}
+
+SavePoint SavePoints::pop() {
+ SavePoint point = _savepoints.front();
+ _savepoints.pop_front();
+ return point;
+}
+
+
+void SavePoints::pushAll(EntityIndex entity, ActionIndex action, uint32 param) {
+ for (uint32 index = 1; index < 40; index++) {
+ if ((EntityIndex)index != entity)
+ push(entity, (EntityIndex)index, action, param);
+ }
+}
+
+// Process all savepoints
+void SavePoints::process() {
+ while (_savepoints.size() > 0 && getFlags()->isGameRunning) {
+ SavePoint savepoint = pop();
+
+ // If this is a data savepoint, update the entity
+ // otherwise, execute the callback
+ if (!updateEntityFromData(savepoint)) {
+
+ // Call requested callback
+ Entity::Callback *callback = getCallback(savepoint.entity1);
+ if (callback && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s", ENTITY_NAME(savepoint.entity1), ACTION_NAME(savepoint.action), ENTITY_NAME(savepoint.entity2));
+ (*callback)(savepoint);
+ }
+ }
+ }
+}
+
+void SavePoints::reset() {
+ _savepoints.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Data
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::addData(EntityIndex entity, ActionIndex action, uint32 param) {
+ if (_data.size() >= _savePointsMaxSize)
+ return;
+
+ SavePointData data;
+ data.entity1 = entity;
+ data.action = action;
+ data.param = param;
+
+ _data.push_back(data);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::setCallback(EntityIndex index, Entity::Callback* callback) {
+ if (index >= 40)
+ error("SavePoints::setCallback - attempting to use an invalid entity index. Valid values 0-39, was %d", index);
+
+ if (!callback || !callback->isValid())
+ error("SavePoints::setCallback - attempting to set an invalid callback for entity %s", ENTITY_NAME(index));
+
+ _callbacks[index] = callback;
+}
+
+Entity::Callback *SavePoints::getCallback(EntityIndex index) const {
+ if (index >= 40)
+ error("SavePoints::getCallback - attempting to use an invalid entity index. Valid values 0-39, was %d", index);
+
+ return _callbacks[index];
+}
+
+void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param) const {
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ point.param.intValue = param;
+
+ Entity::Callback *callback = getCallback(entity1);
+ if (callback != NULL && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%d", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param);
+ (*callback)(point);
+ }
+}
+
+void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const {
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ strcpy((char *)&point.param.charValue, param);
+
+ Entity::Callback *callback = getCallback(entity1);
+ if (callback != NULL && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%s", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param);
+ (*callback)(point);
+ }
+}
+
+void SavePoints::callAndProcess() {
+ SavePoint savepoint; // empty parameters
+
+ // We ignore the kEntityPlayer callback in the list
+ EntityIndex index = kEntityAnna;
+
+ // Call all callbacks with empty parameters
+ bool isRunning = getFlags()->isGameRunning;
+ while (isRunning) {
+
+ Entity::Callback *callback = getCallback(index);
+ if (callback != NULL && callback->isValid()) {
+ (*callback)(savepoint);
+ isRunning = getFlags()->isGameRunning;
+ }
+
+ index = (EntityIndex)(index + 1);
+
+ // Process all savepoints when done
+ if (index >= 40) {
+ if (isRunning)
+ process();
+
+ return;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+bool SavePoints::updateEntityFromData(const SavePoint &savepoint) {
+ for (int i = 0; i < (int)_data.size(); i++) {
+
+ // Not a data savepoint!
+ if (!_data[i].entity1)
+ return false;
+
+ // Found our data!
+ if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) {
+ debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param);
+
+ // the SavePoint param value is the index of the entity call parameter to update
+ getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::saveLoadWithSerializer(Common::Serializer &s) {
+
+ // Serialize savepoint data
+ uint32 dataSize = (s.isLoading() ? _savePointsMaxSize : _data.size());
+ for (uint i = 0; i < dataSize; i++) {
+ if (s.isLoading()) {
+ SavePointData data;
+ _data.push_back(data);
+ }
+
+ s.syncAsUint32LE(_data[i].entity1);
+ s.syncAsUint32LE(_data[i].action);
+ s.syncAsUint32LE(_data[i].entity2);
+ s.syncAsUint32LE(_data[i].param);
+ }
+
+ // Skip uninitialized data if any
+ s.skip((_savePointsMaxSize - dataSize) * 16);
+
+ // Number of savepoints
+ uint32 count = _savepoints.size();
+ s.syncAsUint32LE(count);
+
+ // Savepoints
+ if (s.isLoading()) {
+ for (uint i= 0; i < count; i++) {
+ SavePoint point;
+ s.syncAsUint32LE(point.entity1);
+ s.syncAsUint32LE(point.action);
+ s.syncAsUint32LE(point.entity2);
+ s.syncAsUint32LE(point.param.intValue);
+
+ _savepoints.push_back(point);
+
+ if (_savepoints.size() >= _savePointsMaxSize)
+ break;
+ }
+ } else {
+ for (Common::List<SavePoint>::iterator it = _savepoints.begin(); it != _savepoints.end(); ++it) {
+ s.syncAsUint32LE((*it).entity1);
+ s.syncAsUint32LE((*it).action);
+ s.syncAsUint32LE((*it).entity2);
+ s.syncAsUint32LE((*it).param.intValue);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String SavePoints::toString() {
+ Common::String ret = "";
+
+ ret += "Savepoint Data\n";
+ for (uint i = 0; i < _data.size(); i++)
+ ret += _data[i].toString() + "\n";
+
+ ret += "\nSavepoints\n";
+ for (Common::List<SavePoint>::iterator it = _savepoints.begin(); it != _savepoints.end(); ++it)
+ ret += (*it).toString() + "\n";
+
+ return ret;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/savepoint.h b/engines/lastexpress/game/savepoint.h
new file mode 100644
index 0000000000..da1c9b8ed1
--- /dev/null
+++ b/engines/lastexpress/game/savepoint.h
@@ -0,0 +1,149 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SAVEPOINT_H
+#define LASTEXPRESS_SAVEPOINT_H
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/helpers.h"
+
+#include "common/array.h"
+#include "common/list.h"
+#include "common/serializer.h"
+
+/*
+ Savepoint format
+ ----------------
+
+ Save point: max: 127 - FIFO list (ie. goes back and overwrites first save point when full)
+ uint32 {4} - Entity 1
+ uint32 {4} - Action
+ uint32 {4} - Entity 2
+ uint32 {4} - Parameter
+
+ Save point Data
+ uint32 {4} - Entity 1
+ uint32 {4} - Action
+ uint32 {4} - Entity 2
+ uint32 {4} - function pointer to ??
+
+*/
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+struct SavePoint {
+ EntityIndex entity1;
+ ActionIndex action;
+ EntityIndex entity2;
+ union {
+ uint32 intValue;
+ char charValue[5];
+ } param;
+
+ SavePoint() {
+ entity1 = kEntityPlayer;
+ action = kActionNone;
+ entity2 = kEntityPlayer;
+ param.intValue = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("{ %s - %d - %s - %s }", ENTITY_NAME(entity1), action, ENTITY_NAME(entity2), param.charValue);
+ }
+};
+
+class SavePoints : Common::Serializable {
+private:
+ typedef Common::Functor1<const SavePoint&, void> Callback;
+
+public:
+
+ struct SavePointData {
+ EntityIndex entity1;
+ ActionIndex action;
+ EntityIndex entity2;
+ uint32 param;
+
+ SavePointData() {
+ entity1 = kEntityPlayer;
+ action = kActionNone;
+ entity2 = kEntityPlayer;
+ param = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf(" { %s - %d - %s - %d }", ENTITY_NAME(entity1), action, ENTITY_NAME(entity2), param);
+ }
+ };
+
+ SavePoints(LastExpressEngine *engine);
+ ~SavePoints();
+
+ // Savepoints
+ void push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0);
+ void push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char* param);
+ void pushAll(EntityIndex entity, ActionIndex action, uint32 param = 0);
+ void process();
+ void reset();
+
+ // Data
+ void addData(EntityIndex entity, ActionIndex action, uint32 param);
+
+ // Callbacks
+ void setCallback(EntityIndex index, Entity::Callback* callback);
+ Callback *getCallback(EntityIndex entity) const;
+ void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0) const;
+ void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const;
+ void callAndProcess();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ static const uint32 _savePointsMaxSize = 128;
+
+ LastExpressEngine *_engine;
+
+ Common::List<SavePoint> _savepoints; ///< could be a queue, but we need to be able to iterate on the items
+ Common::Array<SavePointData> _data;
+ Callback* _callbacks[40];
+
+ SavePoint pop();
+ bool updateEntityFromData(const SavePoint &point);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SAVEPOINT_H
diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp
new file mode 100644
index 0000000000..2187d331b5
--- /dev/null
+++ b/engines/lastexpress/game/scenes.cpp
@@ -0,0 +1,1195 @@
+/* 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/scenes.h"
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.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 {
+
+SceneManager::SceneManager(LastExpressEngine *engine) : _engine(engine),
+ _flagNoEntity(false), _flagDrawEntities(false), _flagDrawSequences(false), _flagCoordinates(false),
+ _coords(0, 0, 480, 640), _clockHours(NULL), _clockMinutes(NULL) {
+ _sceneLoader = new SceneLoader();
+}
+
+SceneManager::~SceneManager() {
+ delete _sceneLoader;
+
+ // Clear frames
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door)
+ SAFE_DELETE(*door);
+
+ _doors.clear();
+
+ SAFE_DELETE(_clockHours);
+ SAFE_DELETE(_clockMinutes);
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene cache
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::loadSceneDataFile(ArchiveIndex archive) {
+ // Demo only has CD2TRAIN.DAT file
+ if (_engine->isDemo())
+ archive = kArchiveCd2;
+
+ switch(archive) {
+ case kArchiveCd1:
+ case kArchiveCd2:
+ case kArchiveCd3:
+ if (!_sceneLoader->load(getArchive(Common::String::printf("CD%iTRAIN.DAT", archive))))
+ error("SceneManager::loadSceneDataFile: cannot load data file CD%iTRAIN.DAT", archive);
+ break;
+
+ default:
+ case kArchiveAll:
+ error("SceneManager::loadSceneDataFile: Invalid archive index (must be [1-3], was %d", archive);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene loading
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::loadScene(SceneIndex index) {
+ getFlags()->flag_0 = false;
+ getFlags()->flag_4 = true;
+
+ if (getState()->sceneUseBackup) {
+ Scene *scene = getScenes()->get(index);
+
+ if (scene->param3 != 255) {
+ getState()->sceneUseBackup = false;
+ getState()->sceneBackup2 = kSceneNone;
+ }
+ }
+
+ // Save shouldRedraw state and redraw if necessary
+ bool shouldRedraw = getFlags()->shouldRedraw;
+ if (shouldRedraw) {
+ shouldRedraw = false;
+ // TODO check whether we need to do that here
+ askForRedraw();
+ //redrawScreen();
+ }
+
+ // Set the scene
+ setScene(index);
+
+ // TODO Events method call (might be a low level graphic that we don't need)
+
+ if (getFlags()->isGameRunning && getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = shouldRedraw;
+
+ getLogic()->updateCursor();
+}
+
+void SceneManager::loadSceneFromObject(ObjectIndex object, bool alternate) {
+ switch (object) {
+ default:
+ break;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ if (alternate)
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 1) * 2));
+ else
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(38 - (object - 1) * 2));
+ break;
+
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ case kObjectCompartmentG:
+ if (alternate)
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 32) * 2));
+ else
+ loadSceneFromPosition(kCarRedSleeping, (Position)(38 - (object - 32) * 2));
+ break;
+
+ case kObjectCompartment8:
+ case kObjectCompartmentH:
+ loadSceneFromPosition(object == kObjectCompartment8 ? kCarGreenSleeping : kCarRedSleeping, alternate ? 3 : 25);
+ break;
+ }
+}
+
+void SceneManager::loadSceneFromItem(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ // Get the scene index from the item
+ SceneIndex index = getInventory()->get(item)->scene;
+ if (!index)
+ return;
+
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneUseBackup = true;
+ getState()->sceneBackup = getState()->scene;
+ }
+
+ loadScene(index);
+}
+
+void SceneManager::loadSceneFromPosition(CarIndex car, Position position, int param3) {
+ loadScene(getSceneIndexFromPosition(car, position, param3));
+}
+
+void SceneManager::loadSceneFromItemPosition(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ // Check item location
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (!entry->location)
+ return;
+
+ // Reset location
+ entry->location = kObjectLocationNone;
+
+ if (item != kItem3 && item != kItem5 && item != kItem7)
+ return;
+
+ // Set field value
+ CarIndex car = kCarRestaurant;
+ if (item == kItem5) car = kCarRedSleeping;
+ if (item == kItem7) car = kCarGreenSleeping;
+
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, car))
+ return;
+
+ if (getFlags()->flag_0)
+ return;
+
+ // Get current scene position
+ Scene *scene = getScenes()->get(getState()->scene);
+ Position position = scene->position;
+
+ if (getState()->sceneUseBackup) {
+ Scene *sceneBackup = getScenes()->get(getState()->sceneBackup);
+ position = sceneBackup->position;
+ }
+
+ // Checks are different for each item
+ if ((item == kItem3 && position == 56)
+ || (item == kItem5 && (position >= 23 && position <= 32))
+ || (item == kItem7 && (position == 1 || (position >= 22 && position <= 33)))) {
+ if (getState()->sceneUseBackup)
+ getState()->sceneBackup = getSceneIndexFromPosition(car, position);
+ else
+ loadSceneFromPosition(car, position);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene drawing & processing
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::setScene(SceneIndex index) {
+ _flagNoEntity = false;
+
+ if (_flagDrawEntities) {
+ // TODO Setup screen size (0, 80)x(480x480) (is it necessary for our animations?)
+ drawScene(index);
+ _flagNoEntity = true;
+ } else {
+ _flagDrawEntities = true;
+ drawScene(index);
+ _flagDrawEntities = false;
+ }
+}
+
+void SceneManager::drawScene(SceneIndex index) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Preprocess
+ preProcessScene(&index);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw background
+ debugC(9, kLastExpressDebugScenes, "== Drawing scene: %d ==", index);
+
+ // Update scene
+ _engine->getGraphicsManager()->draw(get(index), GraphicsManager::kBackgroundC, true);
+ getState()->scene = index;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update entities
+ Scene *scene = (getState()->sceneUseBackup ? get(getState()->sceneBackup) : get(index));
+
+ getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition;
+ getEntityData(kEntityPlayer)->car = scene->car;
+
+ getFlags()->flag_3 = true;
+
+ if (getFlags()->isGameRunning) {
+ getSavePoints()->pushAll(kEntityPlayer, kActionDrawScene);
+ getSavePoints()->process();
+
+ if (_flagNoEntity)
+ return;
+
+ getEntities()->updateFields();
+ getEntities()->updateSequences();
+ getEntities()->updateCallbacks();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Show the scene
+ askForRedraw();
+ redrawScreen();
+
+ ////////////////////////////////////////////////////////////
+ // Post process scene
+ postProcessScene();
+}
+
+void SceneManager::processScene() {
+ if (!getState()->sceneUseBackup) {
+ loadScene(getState()->scene);
+ return;
+ }
+
+ getState()->sceneUseBackup = false;
+
+ // Select item if needed
+ InventoryItem item = getInventory()->getFirstExaminableItem();
+ if (item && getInventory()->getSelectedItem() == item)
+ getInventory()->selectItem(item);
+
+ Scene *backup = getScenes()->get(getState()->sceneBackup);
+
+ if (getEntities()->getPosition(backup->car, backup->position))
+ loadScene(processIndex(getState()->sceneBackup));
+ else
+ loadScene(getState()->sceneBackup);
+}
+
+LastExpress::SceneIndex SceneManager::processIndex(SceneIndex index) {
+ Scene *scene = get(index);
+ CarIndex car = scene->car;
+
+ switch (car) {
+ default:
+ break;
+
+ case kCarRedSleeping:
+ if (checkPosition(index, kCheckPositionLookingAtDoors)) {
+ Position position = (Position)(scene->position + (checkPosition(kSceneNone, kCheckPositionLookingUp) ? -1 : 1));
+
+ if (position == 4)
+ position = 3;
+
+ if (position == 24)
+ position = 25;
+
+ if (getEntities()->getPosition(car, position))
+ return index;
+ else
+ return getSceneIndexFromPosition(car, position);
+ } else {
+ switch (scene->position) {
+ default:
+ break;
+
+ case 41:
+ case 51:
+ if (!getEntities()->getPosition(car, 39))
+ return getSceneIndexFromPosition(car, 39);
+ // Fallback to next case
+
+ case 42:
+ case 52:
+ if (!getEntities()->getPosition(car, 14))
+ return getSceneIndexFromPosition(car, 14);
+ // Fallback to next case
+
+ case 43:
+ case 53:
+ if (!getEntities()->getPosition(car, 35))
+ return getSceneIndexFromPosition(car, 35);
+ // Fallback to next case
+
+ case 44:
+ case 54:
+ if (!getEntities()->getPosition(car, 10))
+ return getSceneIndexFromPosition(car, 10);
+ // Fallback to next case
+
+ case 45:
+ case 55:
+ if (!getEntities()->getPosition(car, 32))
+ return getSceneIndexFromPosition(car, 32);
+ // Fallback to next case
+
+ case 46:
+ case 56:
+ if (!getEntities()->getPosition(car, 7))
+ return getSceneIndexFromPosition(car, 7);
+ // Fallback to next case
+
+ case 47:
+ case 57:
+ if (!getEntities()->getPosition(car, 27))
+ return getSceneIndexFromPosition(car, 27);
+ // Fallback to next case
+
+ case 48:
+ case 58:
+ if (!getEntities()->getPosition(car, 2))
+ return getSceneIndexFromPosition(car, 2);
+ break;
+ }
+ }
+ break;
+
+ case kCarRestaurant:
+ switch (scene->position) {
+ default:
+ break;
+
+ case 52:
+ case 53:
+ case 54:
+ if (!getEntities()->getPosition(car, 51))
+ return getSceneIndexFromPosition(car, 51);
+ // Fallback to next case
+
+ case 50:
+ case 56:
+ case 57:
+ case 58:
+ if (!getEntities()->getPosition(car, 55))
+ return getSceneIndexFromPosition(car, 55);
+ // Fallback to next case
+
+ case 59:
+ if (!getEntities()->getPosition(car, 60))
+ return getSceneIndexFromPosition(car, 60);
+ // Fallback to next case
+
+ case 60:
+ if (!getEntities()->getPosition(car, 59))
+ return getSceneIndexFromPosition(car, 59);
+ // Fallback to next case
+
+ case 62:
+ case 63:
+ case 64:
+ if (!getEntities()->getPosition(car, 61))
+ return getSceneIndexFromPosition(car, 61);
+ // Fallback to next case
+
+ case 66:
+ case 67:
+ case 68:
+ if (!getEntities()->getPosition(car, 65))
+ return getSceneIndexFromPosition(car, 65);
+ // Fallback to next case
+
+ case 69:
+ case 71:
+ if (!getEntities()->getPosition(car, 70))
+ return getSceneIndexFromPosition(car, 70);
+ break;
+ }
+ break;
+ }
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+bool SceneManager::checkPosition(SceneIndex index, CheckPositionType type) const {
+ Scene *scene = getScenes()->get((index ? index : getState()->scene));
+
+ CarIndex car = (CarIndex)scene->car;
+ Position position = scene->position;
+
+ bool isInSleepingCar = (car == kCarGreenSleeping || car == kCarRedSleeping);
+
+ switch (type) {
+ default:
+ error("SceneManager::checkPosition: Invalid position type: %d", type);
+
+ case kCheckPositionLookingUp:
+ return isInSleepingCar && (position >= 1 && position <= 19);
+
+ case kCheckPositionLookingDown:
+ return isInSleepingCar && (position >= 21 && position <= 40);
+
+ case kCheckPositionLookingAtDoors:
+ return isInSleepingCar && ((position >= 2 && position <= 17) || (position >= 23 && position <= 39));
+
+ case kCheckPositionLookingAtClock:
+ return car == kCarRestaurant && position == 81;
+ }
+}
+
+bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ Position position = scene->position;
+ CarIndex car = (CarIndex)scene->car;
+
+ if (!doCheckOtherCars)
+ return (car == kCarGreenSleeping || car == kCarRedSleeping)
+ && ((position >= 41 && position <= 48) || (position >= 51 && position <= 58));
+
+ if (position == 99)
+ return true;
+
+ switch (car){
+ default:
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ case kCarLocomotive:
+ if ((position >= 1 && position <= 18) || (position >= 22 && position <= 40))
+ return true;
+ break;
+
+ case kCarRestaurant:
+ if (position >= 73 && position <= 80)
+ return true;
+
+ if (position == 10 || position == 11)
+ return true;
+
+ break;
+
+ case kCarBaggage:
+ switch (position) {
+ default:
+ break;
+
+ case 10:
+ case 11:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ case 84:
+ case 90:
+ case 91:
+ return true;
+ }
+ break;
+
+ case kCarCoalTender:
+ if (position == 2 || position == 10 || position == 11)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Train
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::updateDoorsAndClock() {
+ // Clear all sequences from the list
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door) {
+ removeFromQueue(*door);
+ setCoordinates(*door);
+ SAFE_DELETE(*door);
+ }
+
+ // Cleanup doors sequences
+ _doors.clear();
+
+ if (_clockHours) {
+ removeFromQueue(_clockHours);
+ setCoordinates(_clockHours);
+ SAFE_DELETE(_clockHours);
+ }
+
+ if (_clockMinutes) {
+ removeFromQueue(_clockMinutes);
+ setCoordinates(_clockMinutes);
+ SAFE_DELETE(_clockMinutes);
+ }
+
+ // Queue doors sequences for display
+ if (checkPosition(kSceneNone, kCheckPositionLookingAtDoors)) {
+
+ ObjectIndex firstIndex = kObjectNone;
+
+ // Init objectIndex (or exit if not in one of the two compartment cars
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping)
+ firstIndex = kObjectCompartment1;
+ else if (getEntityData(kEntityPlayer)->car == kCarRedSleeping)
+ firstIndex = kObjectCompartmentA;
+ else
+ return;
+
+ // Iterate over each door
+ for (ObjectIndex index = firstIndex; index < (ObjectIndex)(firstIndex + 8); index = (ObjectIndex)(index + 1)) {
+
+ // Doors is not open, nothing to do
+ if (getObjects()->get(index).location != kObjectLocation2)
+ continue;
+
+ // Load door sequence
+ Scene *scene = getScenes()->get(getState()->scene);
+ Common::String name = Common::String::printf("633X%c-%02d.seq", (index - firstIndex) + 65, scene->position);
+ Sequence *sequence = loadSequence1(name, 255);
+
+ // If the sequence doesn't exists, skip
+ if (!sequence || !sequence->isLoaded())
+ continue;
+
+ // Adjust frame data and store in frame list
+ SequenceFrame *frame = new SequenceFrame(sequence, 0, true);
+ frame->getInfo()->location = (checkPosition(kSceneNone, kCheckPositionLookingUp) ? (firstIndex - index) - 1 : (index - firstIndex) - 8);
+
+ _doors.push_back(frame);
+
+ // Add frame to list
+ addToQueue(frame);
+ }
+ }
+
+ // Queue clock sequences for display
+ if (checkPosition(kSceneNone, kCheckPositionLookingAtClock)) {
+ // Only used in scene 349 to show the hands on the clock
+
+ Sequence *sequenceHours = loadSequence1("SCLKH-81.seq", 255);
+ Sequence *sequenceMinutes = loadSequence1("SCLKM-81.seq", 255);
+
+ // Compute hours and minutes indexes
+ uint16 hoursIndex = getState()->time % 1296000 % 54000 / 900;
+
+ uint hours = (getState()->time % 1296000) / 54000;
+ if (hours >= 12)
+ hours -= 12;
+
+ uint16 minutesIndex = (uint16)(5 * hours + hoursIndex / 12);
+
+ // Adjust z-order and store sequences to list
+ _clockHours = new SequenceFrame(sequenceHours, hoursIndex, true);
+ _clockHours->getInfo()->location = 65534;
+
+ _clockMinutes = new SequenceFrame(sequenceMinutes, minutesIndex, true);
+ _clockMinutes->getInfo()->location = 65535;
+
+ addToQueue(_clockHours);
+ addToQueue(_clockMinutes);
+ }
+}
+
+void SceneManager::resetDoorsAndClock() {
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door)
+ SAFE_DELETE(*door);
+
+ _doors.clear();
+
+ SAFE_DELETE(_clockHours);
+ SAFE_DELETE(_clockMinutes);
+
+ // Remove the beetle sequences too if needed
+ getBeetle()->unload();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sequence list
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::drawFrames(bool refreshScreen) {
+ if (!_flagDrawSequences)
+ return;
+
+ // TODO handle flag coordinates
+
+ clearBg(GraphicsManager::kBackgroundOverlay);
+
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i)
+ _engine->getGraphicsManager()->draw(*i, GraphicsManager::kBackgroundOverlay);
+
+ if (refreshScreen) {
+ askForRedraw();
+ //redrawScreen();
+
+ _flagDrawSequences = false;
+ }
+}
+
+void SceneManager::addToQueue(SequenceFrame * const frame) {
+ if (!frame)
+ return;
+
+ // First check that the frame is not already in the queue
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->equal(*i))
+ return;
+ }
+
+ debugC(8, kLastExpressDebugGraphics, "Adding frame: %s / %d", frame->getName().c_str(), frame->getFrame());
+
+ // Set flag
+ _flagDrawSequences = true;
+
+ // Queue empty: just insert the frame
+ if (_queue.empty()) {
+ _queue.push_back(frame);
+ return;
+ }
+
+ // Frame is closer: insert in first place
+ if (frame->getInfo()->location > _queue.front()->getInfo()->location) {
+ _queue.push_front(frame);
+ return;
+ }
+
+ // Insert the frame in the queue based on location
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->getInfo()->location > (*i)->getInfo()->location) {
+ _queue.insert(i, frame);
+ return;
+ }
+ }
+
+ // We are the last frame in location order, insert at the back of the queue
+ _queue.push_back(frame);
+}
+
+void SceneManager::removeFromQueue(SequenceFrame *frame) {
+ if (!frame)
+ return;
+
+ debugC(8, kLastExpressDebugGraphics, "Removing frame: %s / %d", frame->getName().c_str(), frame->getFrame());
+
+ // Check that the frame is in the queue and remove it
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->equal(*i)) {
+ _queue.erase(i);
+ _flagDrawSequences = true;
+ break;
+ }
+ }
+}
+
+void SceneManager::removeAndRedraw(SequenceFrame **frame, bool doRedraw) {
+ if (!frame)
+ return;
+
+ removeFromQueue(*frame);
+
+ if (doRedraw)
+ drawFrames(true);
+
+ SAFE_DELETE(*frame);
+}
+
+void SceneManager::resetQueue() {
+ _flagDrawSequences = true;
+
+ // The original engine only deletes decompressed data, not the "sequences" since they are just pointers to a memory pool
+ _queue.clear();
+}
+
+void SceneManager::setCoordinates(SequenceFrame *frame) {
+
+ if (!frame || frame->getInfo()->subType == 3)
+ return;
+
+ _flagCoordinates = true;
+
+ if (_coords.right > (int)frame->getInfo()->xPos1)
+ _coords.right = (int16)frame->getInfo()->xPos1;
+
+ if (_coords.bottom > (int)frame->getInfo()->yPos1)
+ _coords.bottom = (int16)frame->getInfo()->yPos1;
+
+ if (_coords.left < (int)frame->getInfo()->xPos2)
+ _coords.left = (int16)frame->getInfo()->xPos2;
+
+ if (_coords.top < (int)frame->getInfo()->yPos2)
+ _coords.top = (int16)frame->getInfo()->yPos2;
+}
+
+void SceneManager::resetCoordinates() {
+ _coords.top = 0;
+ _coords.left = 0;
+ _coords.bottom = 480;
+ _coords.right = 640;
+
+ _flagCoordinates = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////////
+SceneIndex SceneManager::getSceneIndexFromPosition(CarIndex car, Position position, int param3) {
+ // Probably can't happen (can we be called during cd-swap?)
+ if (_sceneLoader->count() <= 1)
+ return getState()->scene;
+
+ SceneIndex index = kSceneMenu;
+
+ Scene *firstScene = getScenes()->get(index);
+
+ while (firstScene->car != car
+ || firstScene->position != position
+ || ((param3 != -1 || firstScene->param3) && firstScene->param3 != param3 && firstScene->type != Scene::kTypeItem3)) {
+
+ // Increment index and look at the next scene
+ index = (SceneIndex)(index + 1);
+
+ if (index >= _sceneLoader->count())
+ return getState()->scene;
+
+ // Load the next scene
+ firstScene = getScenes()->get(index);
+ }
+
+ // Process index if necessary
+ Scene *scene = getScenes()->get(index);
+ if (getEntities()->getPosition(scene->car, scene->position))
+ return processIndex(index);
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene processing
+//////////////////////////////////////////////////////////////////////////
+
+// Process hotspots
+// - if it returns kSceneInvalid, the hotspot scene has not been modified
+// - if it returns kSceneNone, it has been modified
+//
+// Note: we use the original hotspot scene to pre-process again
+#define PROCESS_HOTSPOT_SCENE(hotspot, index) { \
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot); \
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? (hotspot)->scene : processedScene; \
+ if (testScene) { \
+ *index = (hotspot)->scene; \
+ preProcessScene(index); \
+ } \
+}
+
+void SceneManager::preProcessScene(SceneIndex *index) {
+
+ // Check index validity
+ if (*index == 0 || *index > 2500)
+ *index = kSceneMenu;
+
+ Scene *scene = getScenes()->get(*index);
+
+ switch (scene->type) {
+ case Scene::kTypeObject: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+
+ if (object >= kObjectMax)
+ break;
+
+ if (getObjects()->get(object).location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getObjects()->get(object).location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem: {
+ InventoryItem item = (InventoryItem)scene->param1;
+
+ if (item >= kPortraitOriginal)
+ break;
+
+ if (getInventory()->get(item)->location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getInventory()->get(item)->location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem2: {
+ InventoryItem item1 = (InventoryItem)scene->param1;
+ InventoryItem item2 = (InventoryItem)scene->param2;
+
+ if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getInventory()->get(item1)->location != kObjectLocationNone)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item2)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getInventory()->get(item1)->location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item2)->location != (*it)->param2)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeObjectItem: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+ InventoryItem item = (InventoryItem)scene->param2;
+
+ if (object >= kObjectMax)
+ break;
+
+ if (item >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getObjects()->get(object).location == kObjectLocation2)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getObjects()->get(object).location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item)->location != (*it)->param2)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem3: {
+ InventoryItem item1 = (InventoryItem)scene->param1;
+ InventoryItem item2 = (InventoryItem)scene->param2;
+ InventoryItem item3 = (InventoryItem)scene->param3;
+
+ if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal || item3 >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getInventory()->get(item1)->location != kObjectLocationNone)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item2)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (getInventory()->get(item3)->location != kObjectLocationNone)
+ location |= kObjectLocation4;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getInventory()->get(item1)->location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item2)->location != (*it)->param2)
+ continue;
+
+ if (getInventory()->get(item3)->location != (*it)->param3)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeObjectLocation2: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+
+ if (object >= kObjectMax)
+ break;
+
+ bool found = false;
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getObjects()->get(object).location2 != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ found = true;
+ break;
+ }
+
+ // If we haven't found a proper hotspot, use the first hotspot from the current scene
+ if (!found) {
+ Scene *sceneHotspot = getScenes()->get(*index);
+ SceneHotspot *hotspot = sceneHotspot->getHotspot();
+
+ PROCESS_HOTSPOT_SCENE(hotspot, index);
+ }
+ break;
+ }
+
+ case Scene::kTypeCompartments:
+ case Scene::kTypeCompartmentsItem:
+ if (scene->param1 >= 16)
+ break;
+
+ if (getEntities()->getCompartments(scene->param1) || getEntities()->getCompartments1(scene->param1)) {
+
+ Scene *currentScene = getScenes()->get(getState()->scene);
+
+ if ((checkPosition(getState()->scene, kCheckPositionLookingUp) && checkPosition(*index, kCheckPositionLookingUp) && currentScene->entityPosition < scene->entityPosition)
+ || (checkPosition(getState()->scene, kCheckPositionLookingDown) && checkPosition(*index, kCheckPositionLookingDown) && currentScene->entityPosition > scene->entityPosition)) {
+
+ if (State::getPowerOfTwo((uint32)getEntities()->getCompartments(scene->param1)) != 30
+ && State::getPowerOfTwo((uint32)getEntities()->getCompartments1(scene->param1)) != 30 )
+ getSound()->playSound(kEntityPlayer, "CAT1126A");
+
+ *index = scene->getHotspot()->scene;
+ } else {
+ *index = scene->getHotspot(1)->scene;
+ }
+
+ preProcessScene(index);
+ } else {
+ // Stop processing here for kTypeCompartments
+ if (scene->type == Scene::kTypeCompartments)
+ break;
+
+ InventoryItem item = (InventoryItem)scene->param2;
+ if (item >= kPortraitOriginal)
+ break;
+
+ if (getInventory()->get(item)->location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getInventory()->get(item)->location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Sound processing
+ Scene *newScene = getScenes()->get(*index);
+ if (getSound()->isBuffered(kEntityTables4)) {
+ if (newScene->type != Scene::kTypeReadText || newScene->param1)
+ getSound()->processEntry(kEntityTables4);
+ }
+
+ // Cleanup beetle sequences
+ if (getBeetle()->isLoaded()) {
+ if (newScene->type != Scene::kTypeLoadBeetleSequences)
+ getBeetle()->unload();
+ }
+}
+
+void SceneManager::postProcessScene() {
+
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ switch (scene->type) {
+ case Scene::kTypeList: {
+
+ // Adjust time
+ getState()->time += (scene->param1 + 10) * getState()->timeDelta;
+ getState()->timeTicks += (scene->param1 + 10);
+
+ // Wait for a number of frames unless right mouse is clicked
+ uint32 nextFrameCount = getFrameCount() + 4 * scene->param1;
+ if (!getFlags()->mouseRightClick) {
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ getSound()->updateSubtitles();
+ }
+ }
+
+ // Process hotspots and load scenes in the list
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (getFlags()->mouseRightClick) {
+
+ while (getScenes()->get(testScene)->type == Scene::kTypeList) {
+ hotspot = getScenes()->get(testScene)->getHotspot();
+ processedScene = getAction()->processHotspot(*hotspot);
+ testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+ }
+ }
+
+ // If several entities are there, choose one to sound "Excuse me"
+ EntityPosition entityPosition = getEntityData(kEntityPlayer)->entityPosition;
+ if (getEntityData(kEntityPlayer)->car == kCar9 && (entityPosition == kPosition_4 || entityPosition == kPosition_3)) {
+ EntityIndex entities[39];
+
+ // Init entities
+ entities[0] = kEntityPlayer;
+
+ uint progress = 0;
+
+ for (uint i = 1; i < 40 /* number of entities */; i++) {
+ CarIndex car = getEntityData((EntityIndex)i)->car;
+ EntityPosition position = getEntityData((EntityIndex)i)->entityPosition;
+
+ if (entityPosition == kPosition_4) {
+ if ((car == kCarRedSleeping && position > kPosition_9270) || (car == kCarRestaurant && position < kPosition_1540))
+ entities[progress++] = (EntityIndex)i;
+ } else {
+ if ((car == kCarGreenSleeping && position > kPosition_9270) || (car == kCarRedSleeping && position < kPosition_850))
+ entities[progress++] = (EntityIndex)i;
+ }
+ }
+
+ if (progress)
+ getSound()->excuseMe((progress == 1) ? entities[0] : entities[rnd(progress)], kEntityPlayer, SoundManager::kFlagDefault);
+ }
+
+ if (hotspot->scene)
+ setScene(hotspot->scene);
+ break;
+ }
+
+ case Scene::kTypeSavePointChapter:
+ if (getProgress().field_18 == 2)
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionEndChapter);
+ break;
+
+ case Scene::kTypeLoadBeetleSequences:
+ if ((getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3)
+ && getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ if (!getBeetle()->isLoaded())
+ getBeetle()->load();
+ }
+ break;
+
+ case Scene::kTypeGameOver:
+ if (getState()->time >= kTimeCityGalanta || getProgress().field_18 == 4)
+ break;
+
+ getSound()->processEntry(SoundManager::kSoundType7);
+ getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault);
+
+ switch (getProgress().chapter) {
+ default:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverPolice2, true);
+ break;
+
+ case kChapter1:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm, true);
+ break;
+
+ case kChapter4:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm2, true);
+ break;
+ }
+ break;
+
+ case Scene::kTypeReadText:
+ getSound()->readText(scene->param1);
+ break;
+
+ case Scene::kType133:
+ if (getFlags()->flag_0) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ getLogic()->updateCursor();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h
new file mode 100644
index 0000000000..b70526839f
--- /dev/null
+++ b/engines/lastexpress/game/scenes.h
@@ -0,0 +1,120 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SCENEMANAGER_H
+#define LASTEXPRESS_SCENEMANAGER_H
+
+#include "lastexpress/data/scene.h"
+
+#include "common/hashmap.h"
+#include "common/list.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class SceneLoader;
+class Sequence;
+class SequenceFrame;
+
+class SceneManager {
+public:
+ enum CheckPositionType {
+ kCheckPositionLookingUp,
+ kCheckPositionLookingDown,
+ kCheckPositionLookingAtDoors,
+ kCheckPositionLookingAtClock
+ };
+
+ SceneManager(LastExpressEngine *engine);
+ ~SceneManager();
+
+ // Scene cache
+ void loadSceneDataFile(ArchiveIndex archive);
+ Scene *get(SceneIndex sceneIndex) { return _sceneLoader->get(sceneIndex); }
+
+ // Scene loading
+ void setScene(SceneIndex sceneIndex);
+ void loadScene(SceneIndex sceneIndex);
+ void loadSceneFromObject(ObjectIndex object, bool alternate = false);
+ void loadSceneFromItem(InventoryItem item);
+ void loadSceneFromItemPosition(InventoryItem item);
+ void loadSceneFromPosition(CarIndex car, Position position, int param3 = -1);
+
+ // Scene drawing & processing
+ void drawScene(SceneIndex sceneIndex);
+ void processScene();
+ SceneIndex processIndex(SceneIndex sceneIndex);
+
+ // Checks
+ bool checkPosition(SceneIndex sceneIndex, CheckPositionType type) const;
+ bool checkCurrentPosition(bool doCheckOtherCars) const;
+
+ // Train
+ void updateDoorsAndClock();
+ void resetDoorsAndClock();
+
+ // Sequence queue
+ void drawFrames(bool refreshScreen);
+ void addToQueue(SequenceFrame * const frame);
+ void removeFromQueue(SequenceFrame *frame);
+ void removeAndRedraw(SequenceFrame **frame, bool doRedraw);
+ void resetQueue();
+ void setCoordinates(SequenceFrame *frame);
+
+ // Helpers
+ SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1);
+
+ void setFlagDrawSequences() { _flagDrawSequences = true; }
+
+private:
+ LastExpressEngine *_engine;
+ SceneLoader *_sceneLoader; ///< Scene loader
+
+ // Flags
+ bool _flagNoEntity;
+ bool _flagDrawEntities;
+ bool _flagDrawSequences;
+ bool _flagCoordinates;
+
+ Common::Rect _coords;
+
+ // Train sequences
+ Common::List<SequenceFrame *> _doors;
+ SequenceFrame *_clockHours;
+ SequenceFrame *_clockMinutes;
+
+ // Sequence queue
+ Common::List<SequenceFrame *> _queue;
+
+ // Scene processing
+ void preProcessScene(SceneIndex *index);
+ void postProcessScene();
+
+ void resetCoordinates();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SCENEMANAGER_H
diff --git a/engines/lastexpress/game/sound.cpp b/engines/lastexpress/game/sound.cpp
new file mode 100644
index 0000000000..f8b45bb385
--- /dev/null
+++ b/engines/lastexpress/game/sound.cpp
@@ -0,0 +1,1676 @@
+/* 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/sound.h"
+
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+// Letters & messages
+const char *messages[24] = {
+ "",
+ "TXT1001", // 1
+ "TXT1001A", // 2
+ "TXT1011", // 3
+ "TXT1012", // 4
+ "TXT1013", // 5
+ "TXT1014", // 6
+ "TXT1020", // 7
+ "TXT1030", // 8
+ "END1009B", // 50
+ "END1046", // 51
+ "END1047", // 52
+ "END1112", // 53
+ "END1112A", // 54
+ "END1503", // 55
+ "END1505A", // 56
+ "END1505B", // 57
+ "END1610", // 58
+ "END1612A", // 59
+ "END1612C", // 61
+ "END1612D", // 62
+ "ENDALRM1", // 63
+ "ENDALRM2", // 64
+ "ENDALRM3" // 65
+};
+
+const char *cities[17] = {
+ "EPERNAY",
+ "CHALONS",
+ "BARLEDUC",
+ "NANCY",
+ "LUNEVILL",
+ "AVRICOUR",
+ "DEUTSCHA",
+ "STRASBOU",
+ "BADENOOS",
+ "SALZBURG",
+ "ATTNANG",
+ "WELS",
+ "LINZ",
+ "VIENNA",
+ "POZSONY",
+ "GALANTA",
+ "POLICE"
+};
+
+const char *locomotiveSounds[5] = {
+ "ZFX1005",
+ "ZFX1006",
+ "ZFX1007",
+ "ZFX1007A",
+ "ZFX1007B"
+};
+
+static const SoundManager::FlagType soundFlags[32] = {
+ SoundManager::kFlagDefault, SoundManager::kFlag15, SoundManager::kFlag14, SoundManager::kFlag13, SoundManager::kFlag12,
+ SoundManager::kFlag11, SoundManager::kFlag11, SoundManager::kFlag10, SoundManager::kFlag10, SoundManager::kFlag9, SoundManager::kFlag9, SoundManager::kFlag8, SoundManager::kFlag8,
+ SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag6, SoundManager::kFlag6, SoundManager::kFlag6,
+ SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4,
+ SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3
+};
+
+SoundManager::SoundManager(LastExpressEngine *engine) : _engine(engine), _state(0), _currentType(kSoundType16), _flag(0) {
+ _soundStream = new StreamedSound();
+
+ // Initialize unknown data
+ _data0 = 0;
+ _data1 = 0;
+ _data2 = 0;
+
+ memset(&_buffer, 0, sizeof(_buffer));
+ memset(&_lastWarning, 0, sizeof(_lastWarning));
+}
+
+SoundManager::~SoundManager() {
+ delete _soundStream;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Timer
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::handleTimer() {
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = (*i);
+ if (entry->stream == NULL) {
+ i = _cache.reverse_erase(i);
+ continue;
+ } else if (!entry->isStreamed) {
+ entry->isStreamed = true;
+ _soundStream->load(entry->stream);
+ }
+ }
+
+ // TODO: stream any sound in the queue after filtering
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound queue management
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::updateQueue() {
+ //warning("Sound::unknownFunction1: not implemented!");
+}
+
+void SoundManager::resetQueue(SoundType type1, SoundType type2) {
+ if (!type2)
+ type2 = type1;
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->type != type1 && (*i)->type != type2)
+ resetEntry(*i);
+ }
+}
+
+void SoundManager::removeFromQueue(EntityIndex entity) {
+ SoundEntry *entry = getEntry(entity);
+
+ if (entry)
+ resetEntry(entry);
+}
+
+void SoundManager::removeFromQueue(Common::String filename) {
+ SoundEntry *entry = getEntry(filename);
+
+ if (entry)
+ resetEntry(entry);
+}
+
+void SoundManager::clearQueue() {
+ _flag |= 4;
+
+ // Wait a while for a flag to be set
+ for (int i = 0; i < 3000000; i++)
+ if (_flag & 8)
+ break;
+
+ _flag |= 8;
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = (*i);
+
+ removeEntry(entry);
+
+ // Delete entry
+ SAFE_DELETE(entry);
+ i = _cache.reverse_erase(i);
+ }
+
+ updateSubtitles();
+}
+
+bool SoundManager::isBuffered(EntityIndex entity) {
+ return (getEntry(entity) != NULL);
+}
+
+bool SoundManager::isBuffered(Common::String filename, bool testForEntity) {
+ SoundEntry *entry = getEntry(filename);
+
+ if (testForEntity)
+ return entry != NULL && !entry->entity;
+
+ return (entry != NULL);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entry
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4) {
+ if (!entry)
+ error("SoundManager::setupEntry: Invalid entry!");
+
+ entry->field_4C = a4;
+ setEntryType(entry, flag);
+ setEntryStatus(entry, flag);
+
+ // Add entry to cache
+ _cache.push_back(entry);
+
+ setupCache(entry);
+ loadSoundData(entry, name);
+}
+
+void SoundManager::setEntryType(SoundEntry *entry, FlagType flag) {
+ switch (flag & kFlagType7) {
+ default:
+ case kFlagNone:
+ entry->type = _currentType;
+ _currentType = (SoundType)(_currentType + 1);
+ break;
+
+ case kFlagType1_2: {
+ SoundEntry *previous2 = getEntry(kSoundType2);
+ if (previous2)
+ updateEntry(previous2, 0);
+
+ SoundEntry *previous = getEntry(kSoundType1);
+ if (previous) {
+ previous->type = kSoundType2;
+ updateEntry(previous, 0);
+ }
+
+ entry->type = kSoundType1;
+ }
+ break;
+
+ case kFlagType3: {
+ SoundEntry *previous = getEntry(kSoundType3);
+ if (previous) {
+ previous->type = kSoundType4;
+ updateEntry(previous, 0);
+ }
+
+ entry->type = kSoundType11;
+ }
+ break;
+
+ case kFlagType7: {
+ SoundEntry *previous = getEntry(kSoundType7);
+ if (previous)
+ previous->type = kSoundType8;
+
+ entry->type = kSoundType7;
+ }
+ break;
+
+ case kFlagType9: {
+ SoundEntry *previous = getEntry(kSoundType9);
+ if (previous)
+ previous->type = kSoundType10;
+
+ entry->type = kSoundType9;
+ }
+ break;
+
+ case kFlagType11: {
+ SoundEntry *previous = getEntry(kSoundType11);
+ if (previous)
+ previous->type = kSoundType14;
+
+ entry->type = kSoundType11;
+ }
+ break;
+
+ case kFlagType13: {
+ SoundEntry *previous = getEntry(kSoundType13);
+ if (previous)
+ previous->type = kSoundType14;
+
+ entry->type = kSoundType13;
+ }
+ break;
+ }
+}
+
+void SoundManager::setEntryStatus(SoundEntry *entry, FlagType flag) const {
+ SoundStatus status = (SoundStatus)flag;
+ if (!((status & 0xFF) & kSoundStatusClear1))
+ status = (SoundStatus)(status | kSoundStatusClear2);
+
+ if (((status & 0xFF00) >> 8) & kSoundStatusClear0)
+ entry->status.status = (uint32)status;
+ else
+ entry->status.status = (status | kSoundStatusClear4);
+}
+
+bool SoundManager::setupCache(SoundEntry *entry) {
+ warning("Sound::updateCache: not implemented!");
+ return true;
+}
+
+void SoundManager::clearStatus() {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
+ (*i)->status.status |= kSoundStatusClear3;
+}
+
+void SoundManager::loadSoundData(SoundEntry *entry, Common::String name) {
+ entry->name2 = name;
+
+ // Load sound data
+ entry->stream = getArchive(name);
+
+ if (!entry->stream)
+ entry->stream = getArchive("DEFAULT.SND");
+
+ if (entry->stream) {
+ warning("Sound::loadSoundData: not implemented!");
+ } else {
+ entry->status.status = kSoundStatusRemoved;
+ }
+}
+
+void SoundManager::resetEntry(SoundEntry * entry) const {
+ entry->status.status |= kSoundStatusRemoved;
+ entry->entity = kEntityPlayer;
+
+ if (entry->stream) {
+ if (!entry->isStreamed)
+ SAFE_DELETE(entry->stream);
+
+ entry->stream = NULL;
+ }
+}
+
+
+void SoundManager::removeEntry(SoundEntry *entry) {
+ entry->status.status |= kSoundStatusRemoved;
+
+ // Loop until ready
+ while (!(entry->status.status1 & 4) && !(_flag & 8) && (_flag & 1));
+
+ // The original game remove the entry from the cache here,
+ // but since we are called from within an iterator loop
+ // we will remove the entry there
+ // removeFromCache(entry);
+
+ if (entry->subtitle) {
+ drawSubtitles(entry->subtitle);
+ SAFE_DELETE(entry->subtitle);
+ }
+
+ if (entry->entity) {
+ if (entry->entity == kEntitySteam)
+ playLoopingSound();
+ else if (entry->entity != kEntityTrain)
+ getSavePoints()->push(kEntityPlayer, entry->entity, kActionEndSound);
+ }
+}
+
+void SoundManager::updateEntry(SoundEntry *entry, uint value) const {
+ if (!(entry->status.status3 & 64)) {
+
+ int value2 = value;
+
+ entry->status.status |= kSoundStatus_100000;
+
+ if (value) {
+ if (_flag & 32) {
+ entry->field_40 = value;
+ value2 = value * 2 + 1;
+ }
+
+ entry->field_3C = value2;
+ } else {
+ entry->field_3C = 0;
+ entry->status.status |= kSoundStatus_40000000;
+ }
+ }
+}
+
+void SoundManager::updateEntryState(SoundEntry *entry) const {
+ if (_flag & 32) {
+ if (entry->type != kSoundType9 && entry->type != kSoundType7 && entry->type != kSoundType5) {
+ uint32 status = entry->status.status & kSoundStatusClear1;
+
+ entry->status.status &= kSoundStatusClearAll;
+
+ entry->field_40 = status;
+ entry->status.status |= status * 2 + 1;
+ }
+ }
+
+ entry->status.status |= kSoundStatus_20;
+}
+
+void SoundManager::processEntry(EntityIndex entity) {
+ SoundEntry *entry = getEntry(entity);
+
+ if (entry) {
+ updateEntry(entry, 0);
+ entry->entity = kEntityPlayer;
+ }
+}
+
+void SoundManager::processEntry(SoundType type) {
+ SoundEntry *entry = getEntry(type);
+
+ if (entry)
+ updateEntry(entry, 0);
+}
+
+void SoundManager::setupEntry(SoundType type, EntityIndex index) {
+ SoundEntry *entry = getEntry(type);
+
+ if (entry)
+ entry->entity = index;
+}
+
+void SoundManager::processEntry(Common::String filename) {
+ SoundEntry *entry = getEntry(filename);
+
+ if (entry) {
+ updateEntry(entry, 0);
+ entry->entity = kEntityPlayer;
+ }
+}
+
+void SoundManager::processEntries() {
+ _state = 0;
+
+ processEntry(kSoundType1);
+ processEntry(kSoundType2);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+
+void SoundManager::unknownFunction4() {
+ warning("Sound::unknownFunction4: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entry search
+//////////////////////////////////////////////////////////////////////////
+SoundManager::SoundEntry *SoundManager::getEntry(EntityIndex index) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->entity == index)
+ return *i;
+ }
+
+ return NULL;
+}
+
+SoundManager::SoundEntry *SoundManager::getEntry(Common::String name) {
+ if (!name.contains('.'))
+ name += ".SND";
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->name2 == name)
+ return *i;
+ }
+
+ return NULL;
+}
+
+SoundManager::SoundEntry *SoundManager::getEntry(SoundType type) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->type == type)
+ return *i;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::saveLoadWithSerializer(Common::Serializer &ser) {
+ error("Sound::saveLoadWithSerializer: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game-related functions
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playSound(EntityIndex entity, Common::String filename, FlagType flag, byte a4) {
+ if (isBuffered(entity) && entity)
+ removeFromQueue(entity);
+
+ FlagType currentFlag = (flag == -1) ? getSoundFlag(entity) : (FlagType)(flag | 0x80000);
+
+ // Add .SND at the end of the filename if needed
+ if (!filename.contains('.'))
+ filename += ".SND";
+
+ if (!playSoundWithSubtitles(filename, currentFlag, entity, a4))
+ if (entity)
+ getSavePoints()->push(kEntityPlayer, entity, kActionEndSound);
+}
+
+SoundManager::SoundType SoundManager::playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4) {
+ SoundEntry* entry = new SoundEntry();
+ setupEntry(entry, filename, flag, 30);
+ entry->entity = entity;
+
+ if (a4) {
+ entry->field_48 = _data2 + 2 * a4;
+ entry->status.status |= kSoundStatus_8000;
+ } else {
+ // Get subtitles name
+ while (filename.size() > 4)
+ filename.deleteLastChar();
+
+ showSubtitles(entry, filename);
+ updateEntryState(entry);
+ }
+
+ return entry->type;
+}
+
+void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) {
+ char filename[12];
+ int values[5];
+
+ if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
+ return;
+
+ if (getEntities()->isInSalon(entity) != getEntities()->isInSalon(kEntityPlayer))
+ return;
+
+ int _action = (int)action;
+ FlagType flag = getSoundFlag(entity);
+
+ switch (action) {
+ case 36: {
+ int _param3 = (flag <= 9) ? flag + 7 : 16;
+
+ if (_param3 > 7) {
+ _data0 = (uint)_param3;
+ _data1 = _data2 + 2 * a3;
+ }
+ break;
+ }
+
+ case 37:
+ _data0 = 7;
+ _data1 = _data2 + 2 * a3;
+ break;
+
+ case 150:
+ case 156:
+ case 162:
+ case 168:
+ case 188:
+ case 198:
+ _action += 1 + (int)rnd(5);
+ break;
+
+ case 174:
+ case 184:
+ case 194:
+ _action += 1 + (int)rnd(3);
+ break;
+
+ case 180:
+ _action += 1 + (int)rnd(4);
+ break;
+
+ case 246:
+ values[0] = 0;
+ values[1] = 104;
+ values[2] = 105;
+ values[3] = 106;
+ values[4] = 116;
+ _action = values[rnd(5)];
+ break;
+
+ case 247:
+ values[0] = 11;
+ values[1] = 123;
+ values[2] = 124;
+ _action = values[rnd(3)];
+ break;
+
+ case 248:
+ values[0] = 0;
+ values[1] = 103;
+ values[2] = 108;
+ values[3] = 109;
+ _action = values[rnd(4)];
+ break;
+
+ case 249:
+ values[0] = 0;
+ values[1] = 56;
+ values[2] = 112;
+ values[3] = 113;
+ _action = values[rnd(4)];
+ break;
+
+ case 250:
+ values[0] = 0;
+ values[1] = 107;
+ values[2] = 115;
+ values[3] = 117;
+ _action = values[rnd(4)];
+ break;
+
+ case 251:
+ values[0] = 0;
+ values[1] = 11;
+ values[2] = 56;
+ values[3] = 113;
+ _action = values[rnd(4)];
+ break;
+
+ case 252:
+ values[0] = 0;
+ values[1] = 6;
+ values[2] = 109;
+ values[3] = 121;
+ _action = values[rnd(4)];
+ break;
+
+ case 254:
+ values[0] = 0;
+ values[1] = 104;
+ values[2] = 120;
+ values[3] = 121;
+ _action = values[rnd(4)];
+ break;
+
+ case 255:
+ values[0] = 0;
+ values[1] = 106;
+ values[2] = 115;
+ _action = values[rnd(3)];
+ break;
+
+ default:
+ break;
+ }
+
+ if (_action) {
+ sprintf((char *)&filename, "LIB%03d.SND", _action);
+
+ if (flag)
+ playSoundWithSubtitles((char*)&filename, flag, kEntityPlayer, a3);
+ }
+}
+
+void SoundManager::playSteam(CityIndex index) {
+ if (index >= ARRAYSIZE(cities))
+ error("SoundManager::playSteam: invalid city index (was %d, max %d)", index, ARRAYSIZE(cities));
+
+ _state |= kSoundState2;
+
+ if (!getEntry(kSoundType1))
+ playSoundWithSubtitles("STEAM.SND", kFlagSteam, kEntitySteam);
+
+ // Get the new sound entry and show subtitles
+ SoundEntry *entry = getEntry(kSoundType1);
+ if (entry)
+ showSubtitles(entry, cities[index]);
+}
+
+void SoundManager::playFightSound(byte action, byte a4) {
+ int _action = (int)action;
+ char filename[12];
+ int values[5];
+
+ switch (action) {
+ default:
+ break;
+
+ case 174:
+ case 184:
+ case 194:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ _action = values[rnd(3)];
+ break;
+
+ case 180:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ values[3] = action + 4;
+ _action = values[rnd(4)];
+ break;
+
+ case 150:
+ case 156:
+ case 162:
+ case 168:
+ case 188:
+ case 198:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ values[3] = action + 4;
+ values[4] = action + 5;
+ _action = values[rnd(5)];
+ break;
+ }
+
+ if (_action) {
+ sprintf((char *)&filename, "LIB%03d.SND", _action);
+ playSound(kEntityTrain, (char*)&filename, kFlagDefault, a4);
+ }
+}
+
+void SoundManager::playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4) {
+ if (isBuffered(getDialogName(entityDialog)))
+ removeFromQueue(getDialogName(entityDialog));
+
+ playSound(entity, getDialogName(entityDialog), flag, a4);
+}
+
+void SoundManager::playLocomotiveSound() {
+ playSound(kEntityPlayer, locomotiveSounds[rnd(5)], (FlagType)(rnd(15) + 2));
+}
+
+const char *SoundManager::getDialogName(EntityIndex entity) const {
+ switch (entity) {
+ case kEntityAnna:
+ if (getEvent(kEventAnnaDialogGoToJerusalem))
+ return "XANN12";
+
+ if (getEvent(kEventLocomotiveRestartTrain))
+ return "XANN11";
+
+ if (getEvent(kEventAnnaBaggageTies) || getEvent(kEventAnnaBaggageTies2) || getEvent(kEventAnnaBaggageTies3) || getEvent(kEventAnnaBaggageTies4))
+ return "XANN10";
+
+ if (getEvent(kEventAnnaTired) || getEvent(kEventAnnaTiredKiss))
+ return "XANN9";
+
+ if (getEvent(kEventAnnaBaggageArgument))
+ return "XANN8";
+
+ if (getEvent(kEventKronosVisit))
+ return "XANN7";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XANN6A";
+
+ if (getEvent(kEventVassiliSeizure))
+ return "XANN6";
+
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ return "XANN5";
+
+ if (getProgress().field_60)
+ return "XANN4";
+
+ if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram))
+ return "XANN3";
+
+ if (getEvent(kEventDinerMindJoin))
+ return "XANN2";
+
+ if (getEvent(kEventGotALight) || getEvent(kEventGotALightD))
+ return "XANN1";
+
+ break;
+
+ case kEntityAugust:
+ if (getEvent(kEventAugustTalkCigar))
+ return "XAUG6";
+
+ if (getEvent(kEventAugustBringBriefcase))
+ return "XAUG5";
+
+ // Getting closer to Vienna...
+ if (getState()->time > kTime2200500 && !getEvent(kEventAugustMerchandise))
+ return "XAUG4A";
+
+ if (getEvent(kEventAugustMerchandise))
+ return "XAUG4";
+
+ if (getEvent(kEventDinerAugust) || getEvent(kEventDinerAugustAlexeiBackground) || getEvent(kEventMeetAugustTylerCompartment)
+ || getEvent(kEventMeetAugustTylerCompartmentBed) || getEvent(kEventMeetAugustHisCompartment) || getEvent(kEventMeetAugustHisCompartmentBed))
+ return "XAUG3";
+
+ if (getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ return "XAUG2";
+
+ if (getProgress().eventMertensAugustWaiting)
+ return "XAUG1";
+
+ break;
+
+ case kEntityTatiana:
+ if (getEvent(kEventTatianaTylerCompartment))
+ return "XTAT6";
+
+ if (getEvent(kEventTatianaCompartmentStealEgg))
+ return "XTAT5";
+
+ if (getEvent(kEventTatianaGivePoem))
+ return "XTAT3";
+
+ if (getProgress().field_64)
+ return "XTAT1";
+
+ break;
+
+ case kEntityVassili:
+ if (getEvent(kEventCathFreePassengers))
+ return "XVAS4";
+
+ if (getEvent(kEventVassiliCompartmentStealEgg))
+ return "XVAS3";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XVAS2";
+
+ if (getEvent(kEventVassiliSeizure))
+ return "XVAS1A";
+
+ if (getProgress().field_64)
+ return "XVAS1";
+
+ break;
+
+ case kEntityAlexei:
+ if (getProgress().field_88)
+ return "XALX6";
+
+ if (getProgress().field_8C)
+ return "XALX5";
+
+ if (getProgress().field_90)
+ return "XALX4A";
+
+ if (getProgress().field_68)
+ return "XALX4";
+
+ if (getEvent(kEventAlexeiSalonPoem))
+ return "XALX3";
+
+ if (getEvent(kEventAlexeiSalonVassili))
+ return "XALX2";
+
+ if (getEvent(kEventAlexeiDiner) || getEvent(kEventAlexeiDinerOriginalJacket))
+ return "XALX1";
+
+ break;
+
+ case kEntityAbbot:
+ if (getEvent(kEventAbbotDrinkDefuse))
+ return "XABB4";
+
+ if (getEvent(kEventAbbotInvitationDrink) || getEvent(kEventDefuseBomb))
+ return "XABB3";
+
+ if (getEvent(kEventAbbotWrongCompartment) || getEvent(kEventAbbotWrongCompartmentBed))
+ return "XABB2";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XABB1";
+
+ break;
+
+ case kEntityMilos:
+ if (getEvent(kEventLocomotiveMilos) || getEvent(kEventLocomotiveMilosNight))
+ return "XMIL5";
+
+ if (getEvent(kEventMilosCompartmentVisitTyler) && (getProgress().chapter == kChapter3 || getProgress().chapter == kChapter4))
+ return "XMIL4";
+
+ if (getEvent(kEventMilosCorridorThanks) || getProgress().chapter == kChapter5)
+ return "XMIL3";
+
+ if (getEvent(kEventMilosCompartmentVisitAugust))
+ return "XMIL2";
+
+ if (getEvent(kEventMilosTylerCompartmentDefeat))
+ return "XMIL1";
+
+ break;
+
+ case kEntityVesna:
+ if (getProgress().field_94)
+ return "XVES2";
+
+ if (getProgress().field_98)
+ return "XVES1";
+
+ break;
+
+ case kEntityKronos:
+ if (getEvent(kEventKronosReturnBriefcase))
+ return "XKRO6";
+
+ if (getEvent(kEventKronosBringEggCeiling) || getEvent(kEventKronosBringEgg))
+ return "XKRO5";
+
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+ if (location != kObjectLocation6 && location != kObjectLocation5 && location != kObjectLocation2 && location != kObjectLocation1)
+ return "XKRO4A";
+ }
+
+ if (getEvent(kEventKronosConversationFirebird))
+ return "XKRO4";
+
+ if (getEvent(kEventKronosConversation)) {
+ if (!getEvent(kEventMilosCompartmentVisitAugust))
+ return "XKRO3";
+ else
+ return "XKRO2";
+ }
+
+ if (getProgress().eventMertensKronosInvitation)
+ return "XKRO1";
+
+ break;
+
+ case kEntityFrancois:
+ if (getProgress().field_9C)
+ return "XFRA3";
+
+ if (getProgress().field_A0
+ || getEvent(kEventFrancoisWhistle) || getEvent(kEventFrancoisWhistleD)
+ || getEvent(kEventFrancoisWhistleNight) || getEvent(kEventFrancoisWhistleNightD))
+ return "XFRA2";
+
+ if (getState()->time > kTimeParisEpernay) // Between Paris and Epernay
+ return "XFRA1";
+
+ break;
+
+ case kEntityMmeBoutarel:
+ if (getProgress().field_A4)
+ return "XMME4";
+
+ if (getProgress().field_A8)
+ return "XMME3";
+
+ if (getProgress().field_A0)
+ return "XMME2";
+
+ if (getProgress().field_AC)
+ return "XMME1";
+
+ break;
+
+ case kEntityBoutarel:
+ if (getProgress().eventMetBoutarel)
+ return "XMRB1";
+
+ break;
+
+ case kEntityRebecca:
+ if (getProgress().field_B4)
+ return "XREB1A";
+
+ if (getProgress().field_B8)
+ return "XREB1";
+
+ break;
+
+ case kEntitySophie:
+ if (getProgress().field_B0)
+ return "XSOP2";
+
+ if (getProgress().field_BC)
+ return "XSOP1B";
+
+ if (getProgress().field_B4)
+ return "XSOP1A";
+
+ if (getProgress().field_B8)
+ return "XSOP1";
+
+ break;
+
+ case kEntityMahmud:
+ if (getProgress().field_C4)
+ return "XMAH1";
+
+ break;
+
+ case kEntityYasmin:
+ if (getProgress().eventMetYasmin)
+ return "XHAR2";
+
+ break;
+
+ case kEntityHadija:
+ if (getProgress().eventMetHadija)
+ return "XHAR1";
+
+ break;
+
+ case kEntityAlouan:
+ if (getProgress().field_DC)
+ return "XHAR3";
+
+ break;
+
+ case kEntityGendarmes:
+ if (getProgress().field_E0)
+ return "XHAR4";
+
+ break;
+
+ case kEntityChapters:
+ if (getEvent(kEventCathDream) || getEvent(kEventCathWakingUp))
+ return "XTYL3";
+
+ return "XTYL1";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Letters & Messages
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::readText(int id){
+ if (!isBuffered(kEntityTables4))
+ return;
+
+ if (id < 0 || (id > 8 && id < 50) || id > 64)
+ error("Sound::readText - attempting to use invalid id. Valid values [1;8] - [50;64], was %d", id);
+
+ // Get proper message file (names are stored in sequence in the array but id is [1;8] - [50;64])
+ const char* text = messages[id <= 8 ? id : id - 41];
+
+ // Check if file is in cache for id [1;8]
+ if (id <= 8)
+ if (isBuffered(text))
+ removeFromQueue(text);
+
+ playSound(kEntityTables4, text, kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound bites
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playWarningCompartment(EntityIndex entity, ObjectIndex compartment) {
+
+#define PLAY_WARNING(index, sound1, sound2, sound3, sound4, sound5, sound6) { \
+ if (_lastWarning[index] + 450 >= getState()->timeTicks) { \
+ if (rnd(2)) \
+ playSound(kEntityMertens, sound1, kFlagDefault); \
+ else \
+ playSound(kEntityMertens, rnd(2) ? sound2 : sound3, kFlagDefault); \
+ } else { \
+ if (rnd(2)) \
+ playSound(kEntityMertens, sound4, kFlagDefault); \
+ else \
+ playSound(kEntityMertens, rnd(2) ? sound5 : sound6, kFlagDefault); \
+ } \
+ _lastWarning[index] = getState()->timeTicks; \
+}
+
+ if (entity != kEntityMertens && entity != kEntityCoudert)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Mertens
+ if (entity == kEntityMertens) {
+
+ switch (compartment) {
+ default:
+ break;
+
+ case kObjectCompartment2:
+ PLAY_WARNING(0, "Con1502A", "Con1500B", "Con1500C", "Con1502", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment3:
+ PLAY_WARNING(1, "Con1501A", "Con1500B", "Con1500C", "Con1501", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment4:
+ PLAY_WARNING(2, "Con1503", "Con1500B", "Con1500C", "Con1503", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ ++_lastWarning[3];
+
+ switch (_lastWarning[3]) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "Con1503C", kFlagDefault);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503E" : "Con1503A", kFlagDefault);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503B" : "Con1503D", kFlagDefault);
+ _lastWarning[3] = 0;
+ break;
+ }
+ }
+
+ return;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Coudert
+ switch (compartment) {
+ default:
+ break;
+
+ case kObjectCompartmentA:
+ if (_lastWarning[4] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1508" : "Jac1508A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentB:
+ if (_lastWarning[5] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTimeCityLinz && getState()->time < kTime2133000))
+ getSound()->playSound(kEntityCoudert, "Jac1507A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1507", kFlagDefault);
+ break;
+
+ case kObjectCompartmentC:
+ if (_lastWarning[6] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().chapter < kChapter3)
+ getSound()->playSound(kEntityCoudert, "Jac1506", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1506A" : "Jac1506B", kFlagDefault);
+ break;
+
+ case kObjectCompartmentD:
+ if (_lastWarning[7] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ getSound()->playSound(kEntityCoudert, "Jac1505", kFlagDefault);
+ break;
+
+ case kObjectCompartmentE:
+ if (_lastWarning[8] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTime2115000 && getState()->time < kTime2133000)) {
+ getSound()->playSound(kEntityCoudert, "Jac1504B", kFlagDefault);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1504" : "Jac1504A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentF:
+ if (_lastWarning[9] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTime2083500 && getState()->time < kTime2133000)) {
+ getSound()->playSound(kEntityCoudert, "Jac1503B", kFlagDefault);
+ break;
+ }
+
+ if (rnd(2) || getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070))
+ getSound()->playSound(kEntityCoudert, "Jac1503", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1503A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentG:
+ if (_lastWarning[10] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (rnd(2) || getEntities()->isInsideCompartment(kEntityMilos, kCarRedSleeping, kPosition_3050))
+ getSound()->playSound(kEntityCoudert, "Jac1502", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1502A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentH:
+ if (_lastWarning[11] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityIvo, kCarRedSleeping, kPosition_2740))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1501", kFlagDefault);
+ break;
+ }
+
+ // Update ticks (Compartments A - H are indexes 4 - 11)
+ _lastWarning[compartment - 28] = getState()->timeTicks;
+}
+
+void SoundManager::excuseMe(EntityIndex entity, EntityIndex entity2, FlagType flag) {
+ if (isBuffered(entity) && entity != kEntityPlayer && entity != kEntityChapters && entity != kEntityTrain)
+ return;
+
+ if (entity2 == kEntityFrancois || entity2 == kEntityMax)
+ return;
+
+ if (entity == kEntityFrancois && getEntityData(kEntityFrancois)->field_4A3 != 30)
+ return;
+
+ if (flag == kFlagNone)
+ flag = getSoundFlag(entity);
+
+ switch (entity) {
+ default:
+ break;
+
+ case kEntityAnna:
+ playSound(kEntityPlayer, "ANN1107A", flag);
+ break;
+
+ case kEntityAugust:
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "AUG1100A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "AUG1100B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "AUG1100C", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, "AUG1100D", flag);
+ break;
+ }
+ break;
+
+ case kEntityMertens:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, (rnd(2) ? "CON1111" : "CON1111A"), flag);
+ } else {
+ if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "CON1110A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "CON1110C", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "CON1110", flag);
+ break;
+ }
+ } else {
+ if (isNight()) {
+ playSound(kEntityPlayer, (getProgress().field_18 == 2 ? "CON1110F" : "CON1110E"));
+ } else {
+ playSound(kEntityPlayer, "CON1110D");
+ }
+ }
+ }
+ break;
+
+ case kEntityCoudert:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, "JAC1111D", flag);
+ } else {
+ if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "JAC1111", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "JAC1111A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "JAC1111B", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, "JAC1111C", flag);
+ break;
+ }
+ } else {
+ playSound(kEntityPlayer, "JAC1113B", flag);
+ }
+ }
+ break;
+
+ case kEntityPascale:
+ playSound(kEntityPlayer, (rnd(2) ? "HDE1002" : "HED1002A"), flag);
+ break;
+
+ case kEntityServers0:
+ case kEntityServers1:
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002" : "WAT1003", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002A" : "WAT1003A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002B" : "WAT1003B", flag);
+ break;
+ }
+ break;
+
+ case kEntityVerges:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, (rnd(2) ? "TRA1113A" : "TRA1113B"));
+ } else {
+ playSound(kEntityPlayer, "TRA1112", flag);
+ }
+ break;
+
+ case kEntityTatiana:
+ playSound(kEntityPlayer, (rnd(2) ? "TAT1102A" : "TAT1102B"), flag);
+ break;
+
+ case kEntityAlexei:
+ playSound(kEntityPlayer, (rnd(2) ? "ALX1099C" : "ALX1099D"), flag);
+ break;
+
+ case kEntityAbbot:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, "ABB3002C", flag);
+ } else {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "ABB3002", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "ABB3002A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "ABB3002B", flag);
+ break;
+ }
+ }
+ break;
+
+ case kEntityVesna:
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "VES1109A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "VES1109B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "VES1109C", flag);
+ break;
+ }
+ break;
+
+ case kEntityKahina:
+ playSound(kEntityPlayer, (rnd(2) ? "KAH1001" : "KAH1001A"), flag);
+ break;
+
+ case kEntityFrancois:
+ case kEntityMmeBoutarel:
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001" : "MME1103A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001A" : "MME1103B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001B" : "MME1103C", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001C" : "MME1103D", flag);
+ break;
+ }
+ break;
+
+ case kEntityBoutarel:
+ playSound(kEntityPlayer, "MRB1104", flag);
+ if (flag > 2)
+ getProgress().eventMetBoutarel = true;
+ break;
+
+ case kEntityRebecca:
+ playSound(kEntityPlayer, (rnd(2) ? "REB1106" : "REB110A"), flag);
+ break;
+
+ case kEntitySophie: {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "SOP1105", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105C" : "SOP1105A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105D" : "SOP1105B", flag);
+ break;
+ }
+ break;
+ }
+
+ case kEntityMahmud:
+ playSound(kEntityPlayer, "MAH1101", flag);
+ break;
+
+ case kEntityYasmin:
+ playSound(kEntityPlayer, "HAR1002", flag);
+ if (flag > 2)
+ getProgress().eventMetYasmin = true;
+ break;
+
+ case kEntityHadija:
+ playSound(kEntityPlayer, (rnd(2) ? "HAR1001" : "HAR1001A"), flag);
+ if (flag > 2)
+ getProgress().eventMetHadija = true;
+ break;
+
+ case kEntityAlouan:
+ playSound(kEntityPlayer, "HAR1004", flag);
+ break;
+ }
+}
+
+void SoundManager::excuseMeCath() {
+ switch(rnd(3)) {
+ default:
+ playSound(kEntityPlayer, "CAT1126B");
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "CAT1126C");
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "CAT1126D");
+ break;
+ }
+}
+
+const char *SoundManager::justCheckingCath() const {
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT5001";
+
+ case 1:
+ return "CAT5001A";
+
+ case 2:
+ return "CAT5001B";
+
+ case 3:
+ return "CAT5001C";
+ }
+
+ return "CAT5001";
+}
+
+const char *SoundManager::wrongDoorCath() const {
+ switch(rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT1125";
+
+ case 1:
+ return "CAT1125A";
+
+ case 2:
+ return "CAT1125B";
+
+ case 3:
+ return "CAT1125C";
+
+ case 4:
+ return "CAT1125D";
+ }
+
+ return "CAT1125";
+}
+
+const char *SoundManager::justAMinuteCath() const {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT1520";
+
+ case 1:
+ return "CAT1521";
+
+ case 2:
+ return "CAT1125"; // ?? is this a bug in the original?
+ }
+
+ return "CAT1520";
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound flags
+//////////////////////////////////////////////////////////////////////////
+SoundManager::FlagType SoundManager::getSoundFlag(EntityIndex entity) const {
+ if (entity == kEntityPlayer)
+ return kFlagDefault;
+
+ if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
+ return kFlagNone;
+
+ // Compute sound value
+ FlagType ret = kFlag2;
+
+ // Get default value if valid
+ int index = ABS(getEntityData(entity)->entityPosition - getEntityData(kEntityPlayer)->entityPosition) / 230;
+ if (index < 32)
+ ret = soundFlags[index];
+
+ if (getEntityData(entity)->location == kLocationOutsideTrain) {
+ if (getEntityData(entity)->car != kCarKronos
+ && !getEntities()->isOutsideAlexeiWindow()
+ && !getEntities()->isOutsideAnnaWindow())
+ return kFlagNone;
+
+ return (FlagType)(ret / 6);
+ }
+
+ switch (getEntityData(entity)->car) {
+ default:
+ break;
+
+ case kCarKronos:
+ if (getEntities()->isInKronosSalon(entity) != getEntities()->isInKronosSalon(kEntityPlayer))
+ ret = (FlagType)(ret * 2);
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInKronosSalon(entity))
+ ret = (FlagType)(ret * 2);
+
+ if (getEntityData(kEntityPlayer)->location
+ && (getEntityData(entity)->entityPosition != kPosition_1 || !getEntities()->isDistanceBetweenEntities(kEntityPlayer, entity, 400)))
+ ret = (FlagType)(ret * 2);
+ break;
+
+ case kCarRestaurant:
+ if (getEntities()->isInSalon(entity) == getEntities()->isInSalon(kEntityPlayer)
+ && (getEntities()->isInRestaurant(entity) != getEntities()->isInRestaurant(kEntityPlayer)))
+ ret = (FlagType)(ret * 2);
+ else
+ ret = (FlagType)(ret * 4);
+ break;
+ }
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Subtitles
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::updateSubtitles() {
+ //warning("SoundManager::updateSubtitles: not implemented!");
+}
+
+void SoundManager::showSubtitles(SoundEntry *entry, Common::String filename) {
+ warning("SoundManager::showSubtitles: not implemented!");
+}
+
+void SoundManager::drawSubtitles(SubtitleManager *subtitle) {
+ warning("SoundManager::drawSubtitles: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playLoopingSound() {
+ warning("SoundManager::playLoopingSound: not implemented!");
+}
+
+void SoundManager::stopAllSound() const {
+ _soundStream->stop();
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/sound.h b/engines/lastexpress/game/sound.h
new file mode 100644
index 0000000000..3cb17db80c
--- /dev/null
+++ b/engines/lastexpress/game/sound.h
@@ -0,0 +1,333 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SOUND_H
+#define LASTEXPRESS_SOUND_H
+
+/*
+
+ Sound entry: 68 bytes (this is what appears in the savegames)
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - entity
+ uint32 {4} - ??
+ uint32 {4} - ??
+ char {16} - name 1
+ char {16} - name 2
+
+ Sound queue entry: 120 bytes
+ uint16 {2} - status
+ byte {1} - ??
+ byte {1} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - file data pointer
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - archive structure pointer
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - entity
+ uint32 {4} - ??
+ uint32 {4} - ??
+ char {16} - name 1
+ char {16} - name 2
+ uint32 {4} - pointer to next entry in the queue
+ uint32 {4} - subtitle data pointer
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "common/list.h"
+#include "common/system.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class StreamedSound;
+class SubtitleManager;
+
+class SoundManager : Common::Serializable {
+public:
+ enum SoundType {
+ kSoundTypeNone = 0,
+ kSoundType1,
+ kSoundType2,
+ kSoundType3,
+ kSoundType4,
+ kSoundType5,
+ kSoundType6,
+ kSoundType7,
+ kSoundType8,
+ kSoundType9,
+ kSoundType10,
+ kSoundType11,
+ kSoundType12,
+ kSoundType13,
+ kSoundType14,
+ kSoundType15,
+ kSoundType16
+ };
+
+ enum FlagType {
+ kFlagInvalid = -1,
+ kFlagNone = 0x0,
+ kFlag2 = 0x2,
+ kFlag3 = 0x3,
+ kFlag4 = 0x4,
+ kFlag5 = 0x5,
+ kFlag6 = 0x6,
+ kFlag7 = 0x7,
+ kFlag8 = 0x8,
+ kFlag9 = 0x9,
+ kFlag10 = 0xA,
+ kFlag11 = 0xB,
+ kFlag12 = 0xC,
+ kFlag13 = 0xD,
+ kFlag14 = 0xE,
+ kFlag15 = 0xF,
+ kFlagDefault = 0x10,
+
+ kFlagType1_2 = 0x1000000,
+ kFlagSteam = 0x1001007,
+ kFlagType13 = 0x3000000,
+ kFlagMenuClock = 0x3080010,
+ kFlagType7 = 0x4000000,
+ kFlagType11 = 0x5000000,
+ kFlagMusic = 0x5000010,
+ kFlagType3 = 0x6000000,
+ kFlagLoop = 0x6001008,
+ kFlagType9 = 0x7000000
+ };
+
+ SoundManager(LastExpressEngine *engine);
+ ~SoundManager();
+
+ // Timer
+ void handleTimer();
+
+ // State
+ void resetState() { _state |= kSoundType1; }
+
+ // Sound queue
+ void updateQueue();
+ void resetQueue(SoundType type1, SoundType type2 = kSoundTypeNone);
+ void clearQueue();
+
+ // Subtitles
+ void updateSubtitles();
+
+ // Entry
+ bool isBuffered(Common::String filename, bool testForEntity = false);
+ bool isBuffered(EntityIndex entity);
+ void setupEntry(SoundType type, EntityIndex index);
+ void processEntry(EntityIndex entity);
+ void processEntry(SoundType type);
+ void processEntry(Common::String filename);
+ void processEntries();
+ void removeFromQueue(Common::String filename);
+ void removeFromQueue(EntityIndex entity);
+
+ // Misc
+ void unknownFunction4();
+ void clearStatus();
+
+ // Sound playing
+ void playSound(EntityIndex entity, Common::String filename, FlagType flag = kFlagInvalid, byte a4 = 0);
+ SoundType playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4 = 0);
+ void playSoundEvent(EntityIndex entity, byte action, byte a3 = 0);
+ void playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4);
+ void playSteam(CityIndex index);
+ void playFightSound(byte action, byte a4);
+ void playLocomotiveSound();
+ void playWarningCompartment(EntityIndex entity, ObjectIndex compartment);
+
+ // Dialog & Letters
+ void readText(int id);
+ const char *getDialogName(EntityIndex entity) const;
+
+ // Sound bites
+ void excuseMe(EntityIndex entity, EntityIndex entity2 = kEntityPlayer, FlagType flag = kFlagNone);
+ void excuseMeCath();
+ const char *justCheckingCath() const;
+ const char *wrongDoorCath() const;
+ const char *justAMinuteCath() const;
+
+ // FLags
+ SoundManager::FlagType getSoundFlag(EntityIndex index) const;
+
+ // Debug
+ void stopAllSound() const;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+private:
+ typedef int32* SoundBuffer;
+
+ enum SoundStatus {
+ kSoundStatus_20 = 0x20,
+ kSoundStatusRemoved = 0x200,
+
+ kSoundStatus_8000 = 0x8000,
+ kSoundStatus_100000 = 0x100000,
+ kSoundStatus_40000000 = 0x40000000,
+
+ kSoundStatusClear0 = 0x10,
+ kSoundStatusClear1 = 0x1F,
+ kSoundStatusClear2 = 0x80,
+ kSoundStatusClear3 = 0x200,
+ kSoundStatusClear4 = 0x800,
+ kSoundStatusClearAll = 0xFFFFFFE0
+ };
+
+ enum SoundState {
+ kSoundState0 = 0,
+ kSoundState1 = 1,
+ kSoundState2 = 2
+ };
+
+ union SoundStatusUnion {
+ uint32 status;
+ byte status1;
+ byte status2;
+ byte status3;
+ byte status4;
+
+ SoundStatusUnion() {
+ status = 0;
+ }
+ };
+
+ struct SoundEntry {
+ SoundStatusUnion status;
+ SoundType type; // int
+ //int field_8;
+ //int field_C;
+ //int field_10;
+ //int fileData;
+ //int field_18;
+ //int field_1C;
+ //int field_20;
+ //int field_24;
+ //int field_28;
+ Common::SeekableReadStream *stream; // int
+ //int field_30;
+ //int field_34;
+ //int field_38;
+ int field_3C;
+ int field_40;
+ EntityIndex entity;
+ int field_48;
+ int field_4C;
+ Common::String name1; //char[16];
+ Common::String name2; //char[16];
+ //int next; // offset to the next structure in the list (not used)
+ SubtitleManager *subtitle;
+
+ bool isStreamed; // TEMPORARY
+
+ SoundEntry() {
+ type = kSoundTypeNone;
+ stream = NULL;
+ field_3C = 0;
+ field_40 = 0;
+ entity = kEntityPlayer;
+ field_48 = 0;
+ field_4C = 0;
+
+ subtitle = NULL;
+
+ isStreamed = false;
+ };
+ };
+
+ // Engine
+ LastExpressEngine* _engine;
+
+ // State flag
+ int _state;
+ SoundType _currentType;
+
+ // Sound stream
+ StreamedSound *_soundStream;
+
+ // Unknown data
+ uint32 _data0;
+ uint32 _data1;
+ uint32 _data2;
+ uint32 _flag;
+
+ // Filters
+
+ int32 _buffer[2940]; ///< Static sound buffer
+
+ // Compartment warnings by Mertens or Coudert
+ uint32 _lastWarning[12];
+
+ // Looping sound
+ void playLoopingSound();
+
+ // Sound cache
+ Common::List<SoundEntry *> _cache;
+
+ SoundEntry *getEntry(EntityIndex index);
+ SoundEntry *getEntry(Common::String name);
+ SoundEntry *getEntry(SoundType type);
+
+ void setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4);
+ void setEntryType(SoundEntry *entry, FlagType flag);
+ void setEntryStatus(SoundEntry *entry, FlagType flag) const;
+ bool setupCache(SoundEntry *entry);
+ void loadSoundData(SoundEntry *entry, Common::String name);
+
+ void updateEntry(SoundEntry *entry, uint value) const;
+ void updateEntryState(SoundEntry *entry) const ;
+ void resetEntry(SoundEntry *entry) const;
+ void removeEntry(SoundEntry *entry);
+
+ // Subtitles
+ void showSubtitles(SoundEntry *entry, Common::String filename);
+ void drawSubtitles(SubtitleManager *subtitle);
+
+ // Sound filter
+ void applyFilter(SoundEntry *entry, SoundBuffer buffer);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SOUND_H
diff --git a/engines/lastexpress/game/state.cpp b/engines/lastexpress/game/state.cpp
new file mode 100644
index 0000000000..8675f6e312
--- /dev/null
+++ b/engines/lastexpress/game/state.cpp
@@ -0,0 +1,74 @@
+/* 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/state.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+State::State(LastExpressEngine *engine) : _engine(engine), _timer(0) {
+ _inventory = new Inventory(engine);
+ _objects = new Objects(engine);
+ _savepoints = new SavePoints(engine);
+ _state = new GameState();
+ _flags = new Flags();
+}
+
+State::~State() {
+ delete _inventory;
+ delete _objects;
+ delete _savepoints;
+ delete _state;
+ delete _flags;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+bool State::isNightTime() const {
+ return (_state->progress.chapter == kChapter1
+ || _state->progress.chapter == kChapter4
+ || (_state->progress.chapter == kChapter5 && !_state->progress.isNightTime));
+}
+
+uint32 State::getPowerOfTwo(uint32 x) {
+ if (!x || (x & 1))
+ return 0;
+
+ uint32 num = 0;
+ do {
+ x >>= 1;
+ num++;
+ } while ((x & 1) == 0);
+
+ return num;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h
new file mode 100644
index 0000000000..a8ddfc7051
--- /dev/null
+++ b/engines/lastexpress/game/state.h
@@ -0,0 +1,593 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_STATE_H
+#define LASTEXPRESS_STATE_H
+
+#include "lastexpress/shared.h"
+
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Inventory;
+class Objects;
+class SavePoints;
+
+class State {
+public:
+ struct GameProgress {
+ uint32 field_0;
+ JacketType jacket;
+ bool eventCorpseMovedFromFloor;
+ uint32 field_C;
+ bool eventCorpseFound;
+ uint32 field_14; ///< EntityIndex (used in Gendarmes)
+ uint32 field_18;
+ uint32 portrait;
+ bool eventCorpseThrown;
+ uint32 field_24;
+ uint32 field_28;
+ ChapterIndex chapter;
+ uint32 field_30;
+ bool eventMetAugust;
+ bool isNightTime;
+ uint32 field_3C;
+ uint32 field_40;
+ uint32 field_44;
+ uint32 field_48;
+ uint32 field_4C;
+ bool isTrainRunning;
+ uint32 field_54;
+ uint32 field_58;
+ uint32 field_5C;
+ uint32 field_60;
+ uint32 field_64;
+ uint32 field_68;
+ bool eventMertensAugustWaiting;
+ bool eventMertensKronosInvitation;
+ bool isEggOpen;
+ uint32 field_78; // time?
+ uint32 field_7C;
+ uint32 field_80;
+ uint32 field_84;
+ uint32 field_88;
+ uint32 field_8C;
+ uint32 field_90;
+ uint32 field_94;
+ uint32 field_98;
+ uint32 field_9C;
+ uint32 field_A0;
+ uint32 field_A4;
+ uint32 field_A8;
+ uint32 field_AC;
+ uint32 field_B0;
+ uint32 field_B4;
+ uint32 field_B8;
+ uint32 field_BC;
+ uint32 field_C0;
+ uint32 field_C4;
+ uint32 field_C8;
+ uint32 field_CC;
+ bool eventMetBoutarel;
+ bool eventMetHadija;
+ bool eventMetYasmin;
+ uint32 field_DC;
+ uint32 field_E0;
+ uint32 field_E4;
+ uint32 field_E8;
+ uint32 field_EC;
+ uint32 field_F0;
+ uint32 field_F4;
+ uint32 field_F8;
+ uint32 field_FC;
+ uint32 field_100;
+ uint32 field_104;
+ uint32 field_108;
+ uint32 field_10C;
+ uint32 field_110;
+ uint32 field_114;
+ uint32 field_118;
+ uint32 field_11C;
+ uint32 field_120;
+ uint32 field_124;
+ uint32 field_128;
+ uint32 field_12C;
+ uint32 field_130;
+ uint32 field_134;
+ uint32 field_138;
+ uint32 field_13C;
+ uint32 field_140;
+ uint32 field_144;
+ uint32 field_148;
+ uint32 field_14C;
+ uint32 field_150;
+ uint32 field_154;
+ uint32 field_158;
+ uint32 field_15C;
+ uint32 field_160;
+ uint32 field_164;
+ uint32 field_168;
+ uint32 field_16C;
+ uint32 field_170;
+ uint32 field_174;
+ uint32 field_178;
+ uint32 field_17C;
+ uint32 field_180;
+ uint32 field_184;
+ uint32 field_188;
+ uint32 field_18C;
+ uint32 field_190;
+ uint32 field_194;
+ uint32 field_198;
+ uint32 field_19C;
+ uint32 field_1A0;
+ uint32 field_1A4;
+ uint32 field_1A8;
+ uint32 field_1AC;
+ uint32 field_1B0;
+ uint32 field_1B4;
+ uint32 field_1B8;
+ uint32 field_1BC;
+ uint32 field_1C0;
+ uint32 field_1C4;
+ uint32 field_1C8;
+ uint32 field_1CC;
+ uint32 field_1D0;
+ uint32 field_1D4;
+ uint32 field_1D8;
+ uint32 field_1DC;
+ uint32 field_1E0;
+ uint32 field_1E4;
+ uint32 field_1E8;
+ uint32 field_1EC;
+ uint32 field_1F0;
+ uint32 field_1F4;
+ uint32 field_1F8;
+ uint32 field_1FC;
+
+ GameProgress() {
+ field_0 = 0;
+ jacket = kJacketOriginal;
+ eventCorpseMovedFromFloor = false;
+ field_C = 0;
+ eventCorpseFound = false;
+ field_14 = 0; // 5
+ field_18 = 0;
+ portrait = _defaultPortrait;
+ eventCorpseThrown = false;
+ field_24 = 0;
+ field_28 = 0; // 10
+ chapter = kChapter1;
+ field_30 = 0;
+ eventMetAugust = false;
+ isNightTime = false;
+ field_3C = 0; // 15
+ field_40 = 0;
+ field_44 = 0;
+ field_48 = 0;
+ field_4C = 0;
+ isTrainRunning = false; // 20
+ field_54 = 0;
+ field_58 = 0;
+ field_5C = 0;
+ field_60 = 0;
+ field_64 = 0; // 25
+ field_68 = 0;
+ eventMertensAugustWaiting = false;
+ eventMertensKronosInvitation = false;
+ isEggOpen = false;
+ field_78 = 0; // 30
+ field_7C = 0;
+ field_80 = 0;
+ field_84 = 0;
+ field_88 = 0;
+ field_8C = 0; // 35
+ field_90 = 0;
+ field_94 = 0;
+ field_98 = 0;
+ field_9C = 0;
+ field_A0 = 0; // 40
+ field_A4 = 0;
+ field_A8 = 0;
+ field_AC = 0;
+ field_B0 = 0;
+ field_B4 = 0; // 45
+ field_B8 = 0;
+ field_BC = 0;
+ field_C0 = 0;
+ field_C4 = 0;
+ field_C8 = 0; // 50
+ field_CC = 0;
+ eventMetBoutarel = false;
+ eventMetHadija = false;
+ eventMetYasmin = false;
+ field_DC = 0; // 55
+ field_E0 = 0;
+ field_E4 = 0;
+ field_E8 = 0;
+ field_EC = 0;
+ field_F0 = 0; // 60
+ field_F4 = 0;
+ field_F8 = 0;
+ field_FC = 0;
+ field_100 = 0;
+ field_104 = 0; // 65
+ field_108 = 0;
+ field_10C = 0;
+ field_110 = 0;
+ field_114 = 0;
+ field_118 = 0; // 70
+ field_11C = 0;
+ field_120 = 0;
+ field_124 = 0;
+ field_128 = 0;
+ field_12C = 0; // 75
+ field_130 = 0;
+ field_134 = 0;
+ field_138 = 0;
+ field_13C = 0;
+ field_140 = 0; // 80
+ field_144 = 0;
+ field_148 = 0;
+ field_14C = 0;
+ field_150 = 0;
+ field_154 = 0; // 85
+ field_158 = 0;
+ field_15C = 0;
+ field_160 = 0;
+ field_164 = 0;
+ field_168 = 0; // 90
+ field_16C = 0;
+ field_170 = 0;
+ field_174 = 0;
+ field_178 = 0;
+ field_17C = 0; // 95
+ field_180 = 0;
+ field_184 = 0;
+ field_188 = 0;
+ field_18C = 0;
+ field_190 = 0; // 100
+ field_194 = 0;
+ field_198 = 0;
+ field_19C = 0;
+ field_1A0 = 0;
+ field_1A4 = 0; // 105
+ field_1A8 = 0;
+ field_1AC = 0;
+ field_1B0 = 0;
+ field_1B4 = 0;
+ field_1B8 = 0; // 110
+ field_1BC = 0;
+ field_1C0 = 0;
+ field_1C4 = 0;
+ field_1C8 = 0;
+ field_1CC = 0; // 115
+ field_1D0 = 0;
+ field_1D4 = 0;
+ field_1D8 = 0;
+ field_1DC = 0;
+ field_1E0 = 0; // 120
+ field_1E4 = 0;
+ field_1E8 = 0;
+ field_1EC = 0;
+ field_1F0 = 0;
+ field_1F4 = 0; // 125
+ field_1F8 = 0;
+ field_1FC = 0;
+ }
+
+ /**
+ * Query if if a progress value is equal to the specified value.
+ *
+ * Note: This is necessary because we store different types in the progress structure
+ * and need to test a value based on an index in Action::getCursor()
+ *
+ * @param index Zero-based index of the progress structure entry
+ * @param val The value.
+ *
+ * @return true if equal, false if not.
+ */
+ bool isEqual(uint index, uint val) {
+ #define EXPOSE_VALUE(idx, name) case idx: return ((uint)name == val);
+
+ switch (index) {
+ default:
+ error("GameProgress::isEqual: invalid index value (was: %d, max:127)", index);
+ break;
+
+ EXPOSE_VALUE(0, field_0);
+ EXPOSE_VALUE(1, jacket);
+ EXPOSE_VALUE(2, eventCorpseMovedFromFloor);
+ EXPOSE_VALUE(3, field_C);
+ EXPOSE_VALUE(4, eventCorpseFound);
+ EXPOSE_VALUE(5, field_14);
+ EXPOSE_VALUE(6, field_18);
+ EXPOSE_VALUE(7, portrait);
+ EXPOSE_VALUE(8, eventCorpseThrown);
+ EXPOSE_VALUE(9, field_24);
+ EXPOSE_VALUE(10, field_28);
+ EXPOSE_VALUE(11, chapter);
+ EXPOSE_VALUE(12, field_30);
+ EXPOSE_VALUE(13, eventMetAugust);
+ EXPOSE_VALUE(14, isNightTime);
+ EXPOSE_VALUE(15, field_3C);
+ EXPOSE_VALUE(16, field_40);
+ EXPOSE_VALUE(17, field_44);
+ EXPOSE_VALUE(18, field_48);
+ EXPOSE_VALUE(19, field_4C);
+ EXPOSE_VALUE(20, isTrainRunning);
+ EXPOSE_VALUE(21, field_54);
+ EXPOSE_VALUE(22, field_58);
+ EXPOSE_VALUE(23, field_5C);
+ EXPOSE_VALUE(24, field_60);
+ EXPOSE_VALUE(25, field_64);
+ EXPOSE_VALUE(26, field_68);
+ EXPOSE_VALUE(27, eventMertensAugustWaiting);
+ EXPOSE_VALUE(28, eventMertensKronosInvitation);
+ EXPOSE_VALUE(29, isEggOpen);
+ EXPOSE_VALUE(30, field_78);
+ EXPOSE_VALUE(31, field_7C);
+ EXPOSE_VALUE(32, field_80);
+ EXPOSE_VALUE(33, field_84);
+ EXPOSE_VALUE(34, field_88);
+ EXPOSE_VALUE(35, field_8C);
+ EXPOSE_VALUE(36, field_90);
+ EXPOSE_VALUE(37, field_94);
+ EXPOSE_VALUE(38, field_98);
+ EXPOSE_VALUE(39, field_9C);
+ EXPOSE_VALUE(40, field_A0);
+ EXPOSE_VALUE(41, field_A4);
+ EXPOSE_VALUE(42, field_A8);
+ EXPOSE_VALUE(43, field_AC);
+ EXPOSE_VALUE(44, field_B0);
+ EXPOSE_VALUE(45, field_B4);
+ EXPOSE_VALUE(46, field_B8);
+ EXPOSE_VALUE(47, field_BC);
+ EXPOSE_VALUE(48, field_C0);
+ EXPOSE_VALUE(49, field_C4);
+ EXPOSE_VALUE(50, field_C8);
+ EXPOSE_VALUE(51, field_CC);
+ EXPOSE_VALUE(52, eventMetBoutarel);
+ EXPOSE_VALUE(53, eventMetHadija);
+ EXPOSE_VALUE(54, eventMetYasmin);
+ EXPOSE_VALUE(55, field_DC);
+ EXPOSE_VALUE(56, field_E0);
+ EXPOSE_VALUE(57, field_E4);
+ EXPOSE_VALUE(58, field_E8);
+ EXPOSE_VALUE(59, field_EC);
+ EXPOSE_VALUE(60, field_F0);
+ EXPOSE_VALUE(61, field_F4);
+ EXPOSE_VALUE(62, field_F8);
+ EXPOSE_VALUE(63, field_FC);
+ EXPOSE_VALUE(64, field_100);
+ EXPOSE_VALUE(65, field_104);
+ EXPOSE_VALUE(66, field_108);
+ EXPOSE_VALUE(67, field_10C);
+ EXPOSE_VALUE(68, field_110);
+ EXPOSE_VALUE(69, field_114);
+ EXPOSE_VALUE(70, field_118);
+ EXPOSE_VALUE(71, field_11C);
+ EXPOSE_VALUE(72, field_120);
+ EXPOSE_VALUE(73, field_124);
+ EXPOSE_VALUE(74, field_128);
+ EXPOSE_VALUE(75, field_12C);
+ EXPOSE_VALUE(76, field_130);
+ EXPOSE_VALUE(77, field_134);
+ EXPOSE_VALUE(78, field_138);
+ EXPOSE_VALUE(79, field_13C);
+ EXPOSE_VALUE(80, field_140);
+ EXPOSE_VALUE(81, field_144);
+ EXPOSE_VALUE(82, field_148);
+ EXPOSE_VALUE(83, field_14C);
+ EXPOSE_VALUE(84, field_150);
+ EXPOSE_VALUE(85, field_154);
+ EXPOSE_VALUE(86, field_158);
+ EXPOSE_VALUE(87, field_15C);
+ EXPOSE_VALUE(88, field_160);
+ EXPOSE_VALUE(89, field_164);
+ EXPOSE_VALUE(90, field_168);
+ EXPOSE_VALUE(91, field_16C);
+ EXPOSE_VALUE(92, field_170);
+ EXPOSE_VALUE(93, field_174);
+ EXPOSE_VALUE(94, field_178);
+ EXPOSE_VALUE(95, field_17C);
+ EXPOSE_VALUE(96, field_180);
+ EXPOSE_VALUE(97, field_184);
+ EXPOSE_VALUE(98, field_188);
+ EXPOSE_VALUE(99, field_18C);
+ EXPOSE_VALUE(100, field_190);
+ EXPOSE_VALUE(101, field_194);
+ EXPOSE_VALUE(102, field_198);
+ EXPOSE_VALUE(103, field_19C);
+ EXPOSE_VALUE(104, field_1A0);
+ EXPOSE_VALUE(105, field_1A4);
+ EXPOSE_VALUE(106, field_1A8);
+ EXPOSE_VALUE(107, field_1AC);
+ EXPOSE_VALUE(108, field_1B0);
+ EXPOSE_VALUE(109, field_1B4);
+ EXPOSE_VALUE(110, field_1B8);
+ EXPOSE_VALUE(111, field_1BC);
+ EXPOSE_VALUE(112, field_1C0);
+ EXPOSE_VALUE(113, field_1C4);
+ EXPOSE_VALUE(114, field_1C8);
+ EXPOSE_VALUE(115, field_1CC);
+ EXPOSE_VALUE(116, field_1D0);
+ EXPOSE_VALUE(117, field_1D4);
+ EXPOSE_VALUE(118, field_1D8);
+ EXPOSE_VALUE(119, field_1DC);
+ EXPOSE_VALUE(120, field_1E0);
+ EXPOSE_VALUE(121, field_1E4);
+ EXPOSE_VALUE(122, field_1E8);
+ EXPOSE_VALUE(123, field_1EC);
+ EXPOSE_VALUE(124, field_1F0);
+ EXPOSE_VALUE(125, field_1F4);
+ EXPOSE_VALUE(126, field_1F8);
+ EXPOSE_VALUE(127, field_1FC);
+ }
+ }
+ };
+
+ struct GameState {
+ // Header
+ uint32 brightness;
+ uint32 volume;
+
+ // Game data
+ uint32 field_0;
+ uint32 time;
+ uint32 timeDelta;
+ uint32 timeTicks;
+ bool sceneUseBackup; // byte
+ SceneIndex scene; // uint32
+ SceneIndex sceneBackup; // uint32
+ SceneIndex sceneBackup2; // uin32
+
+ GameProgress progress;
+ byte events[512];
+
+ GameState() {
+ brightness = _defaultBrigthness;
+ volume = _defaultVolume;
+
+ //Game data
+ time = _defaultTime;
+ timeDelta = _defaultTimeDelta;
+ timeTicks = 0;
+ sceneUseBackup = false;
+ scene = kSceneDefault;
+ sceneBackup = kSceneNone;
+ sceneBackup2 = kSceneNone;
+
+ // Clear game events
+ memset(events, 0, 512*sizeof(byte));
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String ret = "";
+
+ ret += Common::String::printf("Time: %d - Time delta: %d - Ticks: %d\n", time, timeDelta, timeTicks);
+ ret += Common::String::printf("Brightness: %d - Volume: %d - UseBackup: %d\n", brightness, volume, sceneUseBackup);
+ ret += Common::String::printf("Scene: %d - Scene backup: %d - Scene backup 2: %d\n", scene, sceneBackup, sceneBackup2);
+
+ return ret;
+ }
+ };
+
+ struct Flags {
+ bool flag_0;
+ bool flag_3;
+ bool flag_4;
+ bool flag_5;
+
+ bool frameInterval;
+
+ bool isGameRunning;
+
+ // Mouse flags
+ bool mouseLeftClick;
+ bool mouseRightClick;
+
+ bool flag_entities_0;
+ bool flag_entities_1;
+
+ bool shouldRedraw;
+ bool shouldDrawEggOrHourGlass;
+
+ Flags() {
+ flag_0 = false;
+ flag_3 = false;
+ flag_4 = false;
+ flag_5 = false;
+
+ frameInterval = false;
+
+ isGameRunning = false;
+
+ mouseRightClick = false;
+ mouseLeftClick = false;
+
+ flag_entities_0 = false;
+ flag_entities_1 = false;
+
+ shouldRedraw = false;
+ shouldDrawEggOrHourGlass = false;
+ }
+ };
+
+ State(LastExpressEngine *engine);
+ ~State();
+
+ // Accessors
+ Inventory *getGameInventory() { return _inventory; }
+ Objects *getGameObjects() { return _objects; }
+ SavePoints *getGameSavePoints() { return _savepoints; }
+ GameState *getGameState() { return _state; }
+ Flags *getGameFlags() { return _flags; }
+
+ // Time checks
+ bool isNightTime() const;
+
+ // Timer
+ int getTimer() { return _timer; }
+ void setTimer(int val) { _timer = val; }
+
+ // Coordinates
+ void setCoordinates(Common::Point coords) { _coords = coords; }
+ const Common::Point getCoordinates() { return _coords; }
+
+ // Helpers
+ static uint32 getPowerOfTwo(uint32 x);
+
+private:
+ static const uint32 _defaultBrigthness = 0x3;
+ static const uint32 _defaultVolume = 0x7;
+ static const uint32 _defaultTime = 1037700;
+ static const uint32 _defaultTimeDelta = 3;
+ static const uint32 _defaultPortrait = 32;
+
+ LastExpressEngine *_engine;
+
+ // Timer
+ int _timer;
+
+ Flags *_flags; ///< Flags
+ Inventory *_inventory; ///< Inventory
+ Objects *_objects; ///< Objects
+ SavePoints *_savepoints; ///< SavePoints
+ GameState *_state; ///< State
+ Common::Point _coords; ///< Current coordinates
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_STATE_H
diff --git a/engines/lastexpress/graphics.cpp b/engines/lastexpress/graphics.cpp
new file mode 100644
index 0000000000..e5a69d16ea
--- /dev/null
+++ b/engines/lastexpress/graphics.cpp
@@ -0,0 +1,166 @@
+/* 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/graphics.h"
+
+#include "common/system.h"
+
+namespace LastExpress {
+
+#define COLOR_KEY 0xFFFF
+
+GraphicsManager::GraphicsManager() : _changed(false) {
+ _screen.create(640, 480, 2);
+
+ // Create the game surfaces
+ _backgroundA.create(640, 480, 2);
+ _backgroundC.create(640, 480, 2);
+ _overlay.create(640, 480, 2);
+ _inventory.create(640, 480, 2);
+
+ clear(kBackgroundAll);
+}
+
+GraphicsManager::~GraphicsManager() {
+ // Free the game surfaces
+ _screen.free();
+ _backgroundA.free();
+ _backgroundC.free();
+ _overlay.free();
+ _inventory.free();
+}
+
+void GraphicsManager::update() {
+ // Update the screen if needed and reset the status
+ if (_changed) {
+ mergePlanes();
+ updateScreen();
+ _changed = false;
+ }
+}
+
+void GraphicsManager::change() {
+ _changed = true;
+}
+
+void GraphicsManager::clear(BackgroundType type) {
+ clear(type, Common::Rect(640, 480));
+}
+
+void GraphicsManager::clear(BackgroundType type, const Common::Rect &rect) {
+ switch (type) {
+ default:
+ error("GraphicsManager::clear() - Unknown background type: %d", type);
+ break;
+
+ case kBackgroundA:
+ case kBackgroundC:
+ case kBackgroundOverlay:
+ case kBackgroundInventory:
+ getSurface(type)->fillRect(rect, COLOR_KEY);
+ break;
+
+ case kBackgroundAll:
+ _backgroundA.fillRect(rect, COLOR_KEY);
+ _backgroundC.fillRect(rect, COLOR_KEY);
+ _overlay.fillRect(rect, COLOR_KEY);
+ _inventory.fillRect(rect, COLOR_KEY);
+ break;
+ }
+}
+
+bool GraphicsManager::draw(Drawable *drawable, BackgroundType type, bool transition) {
+ // TODO handle transition properly
+ if (transition)
+ clear(type);
+
+ // TODO store rect for later use
+ Common::Rect rect = drawable->draw(getSurface(type));
+
+ return (!rect.isEmpty());
+}
+
+Graphics::Surface *GraphicsManager::getSurface(BackgroundType type) {
+ switch (type) {
+ default:
+ error("GraphicsManager::getSurface() - Unknown surface type: %d", type);
+ break;
+
+ case kBackgroundA:
+ return &_backgroundA;
+
+ case kBackgroundC:
+ return &_backgroundC;
+
+ case kBackgroundOverlay:
+ return &_overlay;
+
+ case kBackgroundInventory:
+ return &_inventory;
+
+ case kBackgroundAll:
+ error("GraphicsManager::getSurface() - cannot return a surface for kBackgroundAll!");
+ break;
+ }
+}
+
+// TODO optimize to only merge dirty rects
+void GraphicsManager::mergePlanes() {
+ // Clear screen surface
+ _screen.fillRect(Common::Rect(640, 480), 0);
+
+ uint16 *screen = (uint16 *)_screen.pixels;
+ uint16 *inventory = (uint16 *)_inventory.pixels;
+ uint16 *overlay = (uint16 *)_overlay.pixels;
+ uint16 *backgroundC = (uint16 *)_backgroundC.pixels;
+ uint16 *backgroundA = (uint16 *)_backgroundA.pixels;
+
+ for (int i = 0; i < 640 * 480; i++) {
+
+ if (*inventory != COLOR_KEY)
+ *screen = *inventory;
+ else if (*overlay != COLOR_KEY)
+ *screen = *overlay;
+ else if (*backgroundA != COLOR_KEY)
+ *screen = *backgroundA;
+ else if (*backgroundC != COLOR_KEY)
+ *screen = *backgroundC;
+ else
+ *screen = 0;
+
+ inventory++;
+ screen++;
+ overlay++;
+ backgroundA++;
+ backgroundC++;
+ }
+}
+
+void GraphicsManager::updateScreen() {
+ g_system->fillScreen(0);
+ g_system->copyRectToScreen((byte *)_screen.getBasePtr(0, 0), 640 * 2, 0, 0, 640, 480);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/graphics.h b/engines/lastexpress/graphics.h
new file mode 100644
index 0000000000..5231d00f6f
--- /dev/null
+++ b/engines/lastexpress/graphics.h
@@ -0,0 +1,76 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_GRAPHICS_H
+#define LASTEXPRESS_GRAPHICS_H
+
+#include "lastexpress/drawable.h"
+
+namespace LastExpress {
+
+class GraphicsManager {
+public:
+ enum BackgroundType {
+ kBackgroundA,
+ kBackgroundC,
+ kBackgroundOverlay,
+ kBackgroundInventory,
+ kBackgroundAll
+ };
+
+ GraphicsManager();
+ ~GraphicsManager();
+
+ // Update the screen
+ void update();
+
+ // Signal a change to the screen, will cause the planes to be remerged
+ void change();
+
+ // Clear some screen parts
+ void clear(BackgroundType type);
+ void clear(BackgroundType type, const Common::Rect &rect);
+
+ // FIXME this is there for animation until we change it to use the graphic surface here instead of its private ones.
+ Graphics::Surface _screen; // Actual screen surface
+
+ bool draw(Drawable *drawable, BackgroundType type, bool transition = false);
+
+private:
+ Graphics::Surface _backgroundA; // Background A
+ Graphics::Surface _backgroundC; // Background C
+ Graphics::Surface _overlay; // Overlay
+ Graphics::Surface _inventory; // Overlay
+
+ void mergePlanes();
+ void updateScreen();
+ Graphics::Surface *getSurface(BackgroundType type);
+
+ bool _changed;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_GRAPHICS_H
diff --git a/engines/lastexpress/helpers.h b/engines/lastexpress/helpers.h
new file mode 100644
index 0000000000..eb54a1a3ce
--- /dev/null
+++ b/engines/lastexpress/helpers.h
@@ -0,0 +1,102 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_HELPERS_H
+#define LASTEXPRESS_HELPERS_H
+
+//////////////////////////////////////////////////////////////////////////
+// Misc helpers
+//////////////////////////////////////////////////////////////////////////
+
+// Misc
+#define getArchive(name) _engine->getResourceManager()->getFileStream(name)
+#define rnd(value) _engine->getRandom().getRandomNumber(value - 1)
+
+// Engine subclasses
+#define getLogic() _engine->getGameLogic()
+#define getMenu() _engine->getGameMenu()
+
+// Logic
+#define getAction() getLogic()->getGameAction()
+#define getBeetle() getLogic()->getGameBeetle()
+#define getFight() getLogic()->getGameFight()
+#define getEntities() getLogic()->getGameEntities()
+#define getSaveLoad() getLogic()->getGameSaveLoad()
+#define isNight() getLogic()->getGameState()->isNightTime()
+
+// State
+#define getState() getLogic()->getGameState()->getGameState()
+#define getEvent(id) getState()->events[id]
+#define getFlags() getLogic()->getGameState()->getGameFlags()
+#define getInventory() getLogic()->getGameState()->getGameInventory()
+#define getObjects() getLogic()->getGameState()->getGameObjects()
+#define getProgress() getState()->progress
+#define getSavePoints() getLogic()->getGameState()->getGameSavePoints()
+#define getGlobalTimer() getLogic()->getGameState()->getTimer()
+#define setGlobalTimer(timer) getLogic()->getGameState()->setTimer(timer)
+#define setCoords(coords) getLogic()->getGameState()->setCoordinates(coords)
+#define getCoords() getLogic()->getGameState()->getCoordinates()
+#define setFrameCount(count) _engine->setFrameCounter(count)
+#define getFrameCount() _engine->getFrameCounter()
+
+// Scenes
+#define getScenes() _engine->getSceneManager()
+
+// Sound
+#define getSound() _engine->getSoundManager()
+
+// Others
+#define getEntityData(entity) getEntities()->getData(entity)
+
+//////////////////////////////////////////////////////////////////////////
+// Graphics
+//////////////////////////////////////////////////////////////////////////
+
+// Sequences
+#define loadSequence(name) Sequence::load(name, getArchive(name))
+#define loadSequence1(name, field30) Sequence::load(name, getArchive(name), field30)
+
+#define clearBg(type) _engine->getGraphicsManager()->clear(type)
+#define showScene(index, type) _engine->getGraphicsManager()->draw(getScenes()->get(index), type);
+
+#define askForRedraw() _engine->getGraphicsManager()->change();
+#define redrawScreen() _engine->getGraphicsManager()->update(); _engine->_system->updateScreen();
+
+// Used to delete entity sequences
+#define SAFE_DELETE(_p) { if(_p) { delete (_p); (_p) = NULL; } }
+
+//////////////////////////////////////////////////////////////////////////
+// Output
+//////////////////////////////////////////////////////////////////////////
+extern const char *g_actionNames[];
+extern const char *g_directionNames[];
+extern const char *g_entityNames[];
+
+#define ACTION_NAME(action) (action > 18 ? Common::String::printf("%d", action).c_str() : g_actionNames[action])
+#define DIRECTION_NAME(direction) (direction >= 6 ? "INVALID" : g_directionNames[direction])
+#define ENTITY_NAME(index) (index >= 40 ? "INVALID" : g_entityNames[index])
+
+
+#endif // LASTEXPRESS_HELPERS_H
diff --git a/engines/lastexpress/lastexpress.cpp b/engines/lastexpress/lastexpress.cpp
new file mode 100644
index 0000000000..2ccdc14fbd
--- /dev/null
+++ b/engines/lastexpress/lastexpress.cpp
@@ -0,0 +1,310 @@
+/* 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/lastexpress.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/resource.h"
+
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/EventRecorder.h"
+
+#include "engines/util.h"
+
+const char *g_actionNames[] = {"None", "Action1", "Action2", "ExitCompartment", "Action4", "ExcuseMeCath", "ExcuseMe", "INVALID", "Knock", "OpenDoor", "Action10", "Action11", "Default", "INVALID", "INVALID", "INVALID", "Action16", "DrawScene", "Callback"};
+const char *g_directionNames[] = { "None", "Up", "Down", "Left", "Right", "Switch"};
+const char *g_entityNames[] = { "Player", "Anna", "August", "Mertens", "Coudert", "Pascale", "Servers0", "Servers1", "Cooks", "Verges", "Tatiana", "Vassili", "Alexei", "Abbot", "Milos", "Vesna", "Ivo", "Salko", "Kronos", "Kahina", "Francois", "MmeBoutarel", "Boutarel", "Rebecca", "Sophie", "Mahmud", "Yasmin", "Hadija", "Alouan", "Gendarmes", "Max", "Chapters", "Train", "Tables0", "Tables1", "Tables2", "Tables3", "Tables4", "Tables5", "Entity39"};
+
+
+namespace LastExpress {
+
+LastExpressEngine::LastExpressEngine(OSystem *syst, const ADGameDescription *gd) :
+ Engine(syst), _gameDescription(gd), _debugger(NULL), _cursor(NULL),
+ _font(NULL), _logic(NULL), _menu(NULL), _frameCounter(0), _lastFrameCount(0),
+ _graphicsMan(NULL), _resMan(NULL), _sceneMan(NULL), _soundMan(NULL),
+ eventMouse(NULL), eventTick(NULL), eventMouseBackup(NULL), eventTickBackup(NULL) {
+
+ // Adding the default directories
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "data");
+
+ // Initialize the custom debug levels
+ DebugMan.addDebugChannel(kLastExpressDebugAll, "All", "Debug everything");
+ DebugMan.addDebugChannel(kLastExpressDebugGraphics, "Graphics", "Debug graphics & animation/sequence playback");
+ DebugMan.addDebugChannel(kLastExpressDebugResource, "Resource", "Debug resource management");
+ DebugMan.addDebugChannel(kLastExpressDebugCursor, "Cursor", "Debug cursor handling");
+ DebugMan.addDebugChannel(kLastExpressDebugSound, "Sound", "Debug sound playback");
+ DebugMan.addDebugChannel(kLastExpressDebugSubtitle, "Subtitle", "Debug subtitles");
+ DebugMan.addDebugChannel(kLastExpressDebugSavegame, "Savegame", "Debug savegames");
+ DebugMan.addDebugChannel(kLastExpressDebugLogic, "Logic", "Debug logic");
+ DebugMan.addDebugChannel(kLastExpressDebugScenes, "Scenes", "Debug scenes & hotspots");
+ DebugMan.addDebugChannel(kLastExpressDebugUnknown, "Unknown", "Debug unknown data");
+
+ g_eventRec.registerRandomSource(_random, "lastexpress");
+}
+
+LastExpressEngine::~LastExpressEngine() {
+ _timer->removeTimerProc(&soundTimer);
+
+ // Delete the remaining objects
+ delete _cursor;
+ delete _font;
+ delete _logic;
+ delete _menu;
+ delete _graphicsMan;
+ delete _resMan;
+ delete _sceneMan;
+ delete _soundMan;
+ delete _debugger;
+
+ // Cleanup event handlers
+ SAFE_DELETE(eventMouse);
+ SAFE_DELETE(eventTick);
+ SAFE_DELETE(eventMouseBackup);
+ SAFE_DELETE(eventTickBackup);
+
+ // Zero passed pointers
+ _gameDescription = NULL;
+}
+
+// TODO: which error should we return when some game files are missing/corrupted?
+Common::Error LastExpressEngine::run() {
+ // Initialize the graphics
+ const Graphics::PixelFormat dataPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ initGraphics(640, 480, true, &dataPixelFormat);
+
+ // We do not support color conversion
+ if (_system->getScreenFormat() != dataPixelFormat)
+ return Common::kUnsupportedColorMode;
+
+ // Create debugger. It requires GFX to be initialized
+ _debugger = new Debugger(this);
+
+ // Start the resource and graphics managers
+ _resMan = new ResourceManager(isDemo());
+ if (!_resMan->loadArchive(kArchiveCd1))
+ return Common::kNoGameDataFoundError;
+
+ _graphicsMan = new GraphicsManager();
+
+ // Load the cursor data
+ _cursor = _resMan->loadCursor();
+ if (!_cursor)
+ return Common::kNoGameDataFoundError;
+
+ // Load the font data
+ _font = _resMan->loadFont();
+ if (!_font)
+ return Common::kNoGameDataFoundError;
+
+ // Start scene manager
+ _sceneMan = new SceneManager(this);
+ _sceneMan->loadSceneDataFile(kArchiveCd1);
+
+ // Game logic
+ _logic = new Logic(this);
+
+ // Start sound manager and setup timer
+ _soundMan = new SoundManager(this);
+ _timer->installTimerProc(&soundTimer, 17, this);
+
+ // Menu
+ _menu = new Menu(this);
+ _menu->show(false, kSavegameTypeIndex, 0);
+
+ while (!shouldQuit()) {
+ _soundMan->updateQueue();
+ _soundMan->updateSubtitles();
+
+ if (handleEvents())
+ continue;
+ }
+
+ return Common::kNoError;
+}
+
+void LastExpressEngine::pollEvents() {
+ Common::Event ev;
+ _eventMan->pollEvent(ev);
+
+ switch (ev.type) {
+
+ case Common::EVENT_LBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseLeftClick = true;
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseRightClick = true;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool LastExpressEngine::handleEvents() {
+ // Make sure all the subsystems have been initialized
+ if (!_debugger || !_graphicsMan)
+ error("LastExpressEngine::handleEvents: called before the required subsystems have been initialized!");
+
+ // Execute stored commands
+ if (_debugger->hasCommand()) {
+ _debugger->callCommand();
+
+ // re-attach the debugger
+ _debugger->attach();
+ }
+
+ // Show the debugger if required
+ _debugger->onFrame();
+
+ // Handle input
+ Common::Event ev;
+ while (_eventMan->pollEvent(ev)) {
+ switch (ev.type) {
+
+ case Common::EVENT_KEYDOWN:
+ // CTRL-D: Attach the debugger
+ if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
+ _debugger->attach();
+
+ //// DEBUG: Quit game on escape
+ //if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+ // quitGame();
+
+ break;
+
+ case Common::EVENT_MAINMENU:
+ // Closing the GMM
+
+ case Common::EVENT_LBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseLeftClick = true;
+
+ // Adjust frameInterval flag
+ if (_frameCounter < _lastFrameCount + 30)
+ getGameLogic()->getGameState()->getGameFlags()->frameInterval = true;
+ _lastFrameCount = _frameCounter;
+
+ if (eventMouse && eventMouse->isValid())
+ (*eventMouse)(ev);
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseRightClick = true;
+ if (eventMouse && eventMouse->isValid())
+ (*eventMouse)(ev);
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ if (eventMouse && eventMouse->isValid())
+ (*eventMouse)(ev);
+ break;
+
+ case Common::EVENT_QUIT:
+ quitGame();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Game tick event
+ if (eventTick && eventTick->isValid())
+ (*eventTick)(ev);
+
+ // Update the screen
+ _graphicsMan->update();
+ _system->updateScreen();
+ _system->delayMillis(50);
+
+ // The event loop may have triggered the quit status. In this case,
+ // stop the execution.
+ if (shouldQuit()) {
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Timer
+///////////////////////////////////////////////////////////////////////////////////
+void LastExpressEngine::soundTimer(void *refCon) {
+ ((LastExpressEngine *)refCon)->handleSoundTimer();
+}
+
+void LastExpressEngine::handleSoundTimer() {
+ if (_frameCounter & 1)
+ if (_soundMan)
+ _soundMan->handleTimer();
+
+ _frameCounter++;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Event Handling
+///////////////////////////////////////////////////////////////////////////////////
+void LastExpressEngine::backupEventHandlers() {
+ eventMouseBackup = eventMouse;
+ eventTickBackup = eventTick;
+}
+
+void LastExpressEngine::restoreEventHandlers() {
+ if (eventMouseBackup == NULL || eventTickBackup == NULL)
+ error("LastExpressEngine::restoreEventHandlers: restore called before backing up the event handlers!");
+
+ eventMouse = eventMouseBackup;
+ eventTick = eventTickBackup;
+}
+
+void LastExpressEngine::setEventHandlers(EventHandler::EventFunction *mouse, EventHandler::EventFunction *tick) {
+ eventMouse = mouse;
+ eventTick = tick;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Misc Engine
+///////////////////////////////////////////////////////////////////////////////////
+bool LastExpressEngine::hasFeature(EngineFeature f) const {
+ return (f == kSupportsRTL);
+}
+
+void LastExpressEngine::errorString(const char *buf_input, char *buf_output, int buf_output_size) {
+ snprintf(buf_output, (uint)buf_output_size, "%s", buf_input);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/lastexpress.h b/engines/lastexpress/lastexpress.h
new file mode 100644
index 0000000000..8acd36f77e
--- /dev/null
+++ b/engines/lastexpress/lastexpress.h
@@ -0,0 +1,155 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_H
+#define LASTEXPRESS_H
+
+#include "lastexpress/debug.h"
+#include "lastexpress/eventhandler.h"
+
+#include "common/random.h"
+#include "common/timer.h"
+
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+
+#include "graphics/pixelformat.h"
+
+/**
+ * This is the namespace of the LastExpress engine.
+ *
+ * Status of this engine:
+ * The basic framework for the engine is done and you can navigate through the
+ * train and interact with objects. Some logic for characters and entities has
+ * been implemented, but characters do not appear in-game yet and background
+ * sounds and animations are missing.
+ * - Resources: classes for the resource formats used by the game are mostly
+ * complete (subtitles integration/cursor transparency are missing)
+ * - Display: basic graphic manager functionality is implemented (transitions
+ * and dirty rects handling are missing)
+ * - Menu/Navigation: main menu is almost complete. Navigation and hotspot
+ * handling is also mostly implemented (with remaining bugs)
+ * - Logic: the framework is in place (with helper classes mostly complete),
+ * and some logic has been implemented (it is mostly hardcoded in the original)
+ * The shared entity code for drawing/handling of entities remains to be done.
+ *
+ * Maintainers:
+ * clone2727, jvprat, littleboy
+ *
+ * Supported games:
+ * - The Last Express
+ */
+namespace LastExpress {
+
+class Cursor;
+class Font;
+class GraphicsManager;
+class Logic;
+class Menu;
+class ResourceManager;
+class SceneManager;
+class SoundManager;
+
+class LastExpressEngine : public Engine {
+protected:
+ // Engine APIs
+ Common::Error run();
+ virtual void errorString(const char *buf_input, char *buf_output, int buf_output_size);
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual Debugger *getDebugger() { return _debugger; }
+
+public:
+ LastExpressEngine(OSystem *syst, const ADGameDescription *gd);
+ ~LastExpressEngine();
+
+ // Misc
+ Common::RandomSource getRandom() const {return _random; }
+
+ // Game
+ Cursor *getCursor() const { return _cursor; }
+ Font *getFont() const { return _font; }
+ Logic *getGameLogic() const { return _logic; }
+ Menu *getGameMenu() const { return _menu; }
+
+ // Managers
+ GraphicsManager *getGraphicsManager() const { return _graphicsMan; }
+ ResourceManager *getResourceManager() const { return _resMan; }
+ SceneManager *getSceneManager() const { return _sceneMan; }
+ SoundManager *getSoundManager() const { return _soundMan; }
+
+ // Event handling
+ bool handleEvents();
+ void pollEvents();
+
+ void backupEventHandlers();
+ void restoreEventHandlers();
+ void setEventHandlers(EventHandler::EventFunction *eventMouse, EventHandler::EventFunction *eventTick);
+
+ bool isDemo() const { return (bool)(_gameDescription->flags & ADGF_DEMO); }
+
+ // Frame Counter
+ uint32 getFrameCounter() { return _frameCounter; }
+ void setFrameCounter(uint32 count) { _frameCounter = count; }
+
+protected:
+ // Sound Timer
+ static void soundTimer(void *ptr);
+ void handleSoundTimer();
+
+private:
+ const ADGameDescription *_gameDescription;
+ Graphics::PixelFormat _pixelFormat;
+
+ // Misc
+ Debugger *_debugger;
+ Common::RandomSource _random;
+
+ // Game
+ Cursor *_cursor;
+ Font *_font;
+ Logic *_logic;
+ Menu *_menu;
+
+ // Frame counter
+ uint32 _frameCounter;
+ uint32 _lastFrameCount;
+
+ // Managers
+ GraphicsManager *_graphicsMan;
+ ResourceManager *_resMan;
+ SceneManager *_sceneMan;
+ SoundManager *_soundMan;
+
+ // Event handlers
+ EventHandler::EventFunction *eventMouse;
+ EventHandler::EventFunction *eventTick;
+
+ EventHandler::EventFunction *eventMouseBackup;
+ EventHandler::EventFunction *eventTickBackup;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_H
diff --git a/engines/lastexpress/module.mk b/engines/lastexpress/module.mk
new file mode 100644
index 0000000000..12fbb4f85b
--- /dev/null
+++ b/engines/lastexpress/module.mk
@@ -0,0 +1,73 @@
+MODULE := engines/lastexpress
+
+MODULE_OBJS := \
+ data/animation.o \
+ data/archive.o \
+ data/background.o \
+ data/cursor.o \
+ data/font.o \
+ data/scene.o \
+ data/sequence.o \
+ data/snd.o \
+ data/subtitle.o \
+ entities/entity.o \
+ entities/abbot.o \
+ entities/alexei.o \
+ entities/alouan.o \
+ entities/anna.o \
+ entities/august.o \
+ entities/boutarel.o \
+ entities/chapters.o \
+ entities/cooks.o \
+ entities/coudert.o \
+ entities/entity39.o \
+ entities/francois.o \
+ entities/gendarmes.o \
+ entities/hadija.o \
+ entities/ivo.o \
+ entities/kahina.o \
+ entities/kronos.o \
+ entities/mahmud.o \
+ entities/max.o \
+ entities/mertens.o \
+ entities/milos.o \
+ entities/mmeboutarel.o \
+ entities/pascale.o \
+ entities/rebecca.o \
+ entities/salko.o \
+ entities/servers0.o \
+ entities/servers1.o \
+ entities/sophie.o \
+ entities/tables.o \
+ entities/tatiana.o \
+ entities/train.o \
+ entities/vassili.o \
+ entities/verges.o \
+ entities/vesna.o \
+ entities/yasmin.o \
+ game/action.o \
+ game/beetle.o \
+ game/entities.o \
+ game/fight.o \
+ game/inventory.o \
+ game/logic.o \
+ game/menu.o \
+ game/object.o \
+ game/savegame.o \
+ game/savepoint.o \
+ game/scenes.o \
+ game/sound.o \
+ game/state.o \
+ debug.o \
+ detection.o \
+ graphics.o \
+ lastexpress.o \
+ resource.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_LASTEXPRESS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk \ No newline at end of file
diff --git a/engines/lastexpress/resource.cpp b/engines/lastexpress/resource.cpp
new file mode 100644
index 0000000000..5628d31f94
--- /dev/null
+++ b/engines/lastexpress/resource.cpp
@@ -0,0 +1,248 @@
+/* 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/resource.h"
+
+#include "lastexpress/data/background.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+
+namespace LastExpress {
+
+const char *archiveDemoPath = "demo.hpf";
+const char *archiveHDPath = "hd.hpf";
+const char *archiveCD1Path = "cd1.hpf";
+const char *archiveCD2Path = "cd2.hpf";
+const char *archiveCD3Path = "cd3.hpf";
+
+ResourceManager::ResourceManager(bool isDemo) : _isDemo(isDemo) {
+}
+
+ResourceManager::~ResourceManager() {
+ reset();
+}
+
+bool ResourceManager::isArchivePresent(ArchiveIndex type) {
+ switch (type) {
+ default:
+ case kArchiveAll:
+ error("ResourceManager::isArchivePresent: Only checks for single CDs are valid!");
+
+ case kArchiveCd1:
+ return Common::File::exists(archiveCD1Path);
+
+ case kArchiveCd2:
+ return Common::File::exists(archiveCD2Path);
+
+ case kArchiveCd3:
+ return Common::File::exists(archiveCD3Path);
+ }
+}
+
+// Load a specific archive collection
+// - type is ignored in the demo version
+// - use ArchiveAll to load all three cds
+// - HD.hpf is always loaded along with the selected archive(s)
+// - will remove all other archives
+bool ResourceManager::loadArchive(ArchiveIndex type) {
+ // Unload all archives
+ reset();
+
+ // Demo version
+ if (_isDemo)
+ return loadArchive(archiveDemoPath);
+
+ // Load HD
+ if (!loadArchive(archiveHDPath))
+ return false;
+
+ switch(type) {
+ case kArchiveCd1:
+ return loadArchive(archiveCD1Path);
+
+ case kArchiveCd2:
+ return loadArchive(archiveCD2Path);
+
+ case kArchiveCd3:
+ return loadArchive(archiveCD3Path);
+
+ case kArchiveAll:
+ default:
+ if (loadArchive(archiveCD1Path))
+ if (loadArchive(archiveCD2Path))
+ return loadArchive(archiveCD3Path);
+ break;
+ }
+
+ return false;
+}
+
+void ResourceManager::reset() {
+ // Free the loaded archives
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+ delete (*it);
+ }
+ _archives.clear();
+}
+
+bool ResourceManager::loadArchive(const Common::String &name) {
+ HPFArchive *archive = new HPFArchive(name);
+
+ if (archive->count() == 0) {
+ debugC(2, kLastExpressDebugResource, "Error opening archive: %s", name.c_str());
+
+ delete archive;
+
+ return false;
+ }
+
+ _archives.push_back(archive);
+
+ return true;
+}
+
+// Get a stream to file in the archive
+// - same as createReadStreamForMember except it checks if the file exists and will assert / output a debug message if not
+Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) {
+
+ // Check if the file exits in the archive
+ if (!hasFile(name)) {
+//#ifdef _DEBUG
+// error("ResourceManager::getFileStream: cannot open file: %s", name.c_str());
+//#endif
+ debugC(2, kLastExpressDebugResource, "Error opening file: %s", name.c_str());
+ return NULL;
+ }
+
+ debugC(2, kLastExpressDebugResource, "Opening file: %s", name.c_str());
+
+ return createReadStreamForMember(name);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Archive functions
+//////////////////////////////////////////////////////////////////////////
+bool ResourceManager::hasFile(const Common::String &name) {
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+ if ((*it)->hasFile(name))
+ return true;
+ }
+
+ return false;
+}
+
+int ResourceManager::listMembers(Common::ArchiveMemberList &list) {
+ int count = 0;
+
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+
+ Common::ArchiveMemberList members;
+ count += (*it)->listMembers(members);
+
+ list.insert(list.end(), members.begin(), members.end());
+ }
+
+ return count;
+}
+
+Common::ArchiveMemberPtr ResourceManager::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *ResourceManager::createReadStreamForMember(const Common::String &name) const {
+ for (Common::Array<HPFArchive * const>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+
+ Common::SeekableReadStream *stream = (*it)->createReadStreamForMember(name);
+
+ if (stream)
+ return stream;
+ }
+
+ return NULL;
+}
+
+
+// Resource loading
+
+Background *ResourceManager::loadBackground(const Common::String &name) const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember(name + ".bg");
+ if (!stream)
+ return NULL;
+
+ // Create the new background & load the data
+ Background *bg = new Background();
+ if (!bg->load(stream)) {
+ delete bg;
+ // stream should be freed by the Background instance
+ return NULL;
+ }
+
+ return bg;
+}
+
+Cursor *ResourceManager::loadCursor() const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember("cursors.tbm");
+ if (!stream)
+ return NULL;
+
+ // Create the new background
+ Cursor *c = new Cursor();
+ if (!c->load(stream)) {
+ delete c;
+ // stream should be freed by the Cursor instance
+ return NULL;
+ }
+
+ return c;
+}
+
+Font *ResourceManager::loadFont() const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember("font.dat");
+ if (!stream)
+ return NULL;
+
+ // Create the new background
+ Font *f = new Font();
+ if (!f->load(stream)) {
+ delete f;
+ // stream should be freed by the Font instance
+ return NULL;
+ }
+
+ return f;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/resource.h b/engines/lastexpress/resource.h
new file mode 100644
index 0000000000..89de8d9288
--- /dev/null
+++ b/engines/lastexpress/resource.h
@@ -0,0 +1,70 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_RESOURCE_H
+#define LASTEXPRESS_RESOURCE_H
+
+#include "lastexpress/data/archive.h"
+#include "lastexpress/shared.h"
+
+namespace LastExpress {
+
+class Background;
+class Cursor;
+class Font;
+
+class ResourceManager : public Common::Archive {
+public:
+ ResourceManager(bool demo);
+ ~ResourceManager();
+
+ // Loading
+ bool loadArchive(ArchiveIndex type);
+ static bool isArchivePresent(ArchiveIndex type);
+ Common::SeekableReadStream *getFileStream(const Common::String &name);
+
+ // Archive functions
+ bool hasFile(const Common::String &name);
+ int listMembers(Common::ArchiveMemberList &list);
+ Common::ArchiveMemberPtr getMember(const Common::String &name);
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+ // Resource loading
+ Background *loadBackground(const Common::String &name) const;
+ Cursor *loadCursor() const;
+ Font *loadFont() const;
+
+private:
+ bool _isDemo;
+
+ bool loadArchive(const Common::String &name);
+ void reset();
+
+ Common::Array<HPFArchive *> _archives;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_RESOURCE_H
diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h
new file mode 100644
index 0000000000..3cfcbb334f
--- /dev/null
+++ b/engines/lastexpress/shared.h
@@ -0,0 +1,1670 @@
+/* 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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SHARED_H
+#define LASTEXPRESS_SHARED_H
+
+#include "common/func.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Time values
+//////////////////////////////////////////////////////////////////////////
+enum TimeValue {
+ kTimeNone = 0,
+ kTime5933 = 5933,
+
+ kTimeCityParis = 1037700,
+ kTime1039500 = 1039500,
+ kTimeStartGame = 1061100,
+
+ // Chapter 1
+ kTimeChapter1 = 1062000,
+ kTime1071000 = 1071000,
+ kTimeParisEpernay = 1075500,
+ kTime1080000 = 1080000,
+ kTime1084500 = 1084500,
+ kTime1089000 = 1089000,
+ kTime1093500 = 1093500,
+ kTime1096200 = 1096200,
+ kTime1098000 = 1098000,
+ kTime1102500 = 1102500,
+ kTime1107000 = 1107000,
+ kTime1111500 = 1111500,
+ kTime1120500 = 1120500,
+ kTime1125000 = 1125000,
+ kTime1134000 = 1134000,
+ kTime1138500 = 1138500,
+ kTime1143000 = 1143000,
+ kTimeEnterEpernay = 1147500,
+ kTimeCityEpernay = 1148400,
+ kTimeExitEpernay = 1150200,
+ kTime1156500 = 1156500,
+ kTime1161000 = 1161000,
+ kTime1162800 = 1162800,
+ kTime1165500 = 1165500,
+ kTime1167300 = 1167300,
+ kTimeEnterChalons = 1170000,
+ kTimeCityChalons = 1170900,
+ kTimeExitChalons = 1173600,
+ kTime1174500 = 1174500,
+ kTime1179000 = 1179000,
+ kTime1183500 = 1183500,
+ kTime1184400 = 1184400,
+ kTime1188000 = 1188000,
+ kTime1189800 = 1189800,
+ kTime1192500 = 1192500,
+ kTime1197000 = 1197000,
+ kTime1201500 = 1201500,
+ kTime1206000 = 1206000,
+ kTime1215000 = 1215000,
+ kTime1224000 = 1224000,
+ kTime1225800 = 1225800,
+ kTimeCityBarLeDuc = 1228500,
+ kTimeExitBarLeDuc = 1231200,
+ kTime1242000 = 1242000,
+ kTime1260000 = 1260000,
+ kTimeCityNancy = 1303200,
+ kTimeExitNancy = 1307700,
+ kTime1323000 = 1323000,
+ kTimeCityLuneville = 1335600,
+ kTimeExitLuneville = 1338300,
+ kTimeCityAvricourt = 1359900,
+ kTimeExitAvricourt = 1363500,
+ kTimeCityDeutschAvricourt = 1367100,
+ kTimeExitDeutschAvricourt = 1370700,
+ kTime1386000 = 1386000,
+ kTimeBedTime = 1404000,
+ kTimeEnterStrasbourg = 1424700,
+ kTime1449000 = 1449000,
+ kTime1458000 = 1458000,
+ kTime1485000 = 1485000,
+ kTime1489500 = 1489500,
+ kTimeCityStrasbourg = 1490400,
+ kTime1492200 = 1492200,
+ kTimeExitStrasbourg = 1493100,
+ kTimeChapter1End = 1494000,
+ kTime1503000 = 1503000,
+ kTime1512000 = 1512000,
+ kTimeCityBadenOos = 1539000,
+ kTimeExitBadenOos = 1541700,
+ kTimeCityKarlsruhe = 1563300,
+ kTimeCityStuttgart = 1656000,
+ kTimeChapter1End2 = 1647000,
+ kTimeChapter1End3 = 1674000,
+ kTimeCityGeislingen = 1713600,
+ kTime1714500 = 1714500,
+ kTimeCityUlm = 1739700,
+
+ // Chapter 2
+ kTimeChapter2 = 1750500,
+ kTime1759500 = 1759500,
+ kTime1755000 = 1755000,
+ kTime1764000 = 1764000,
+ kTime1773000 = 1773000,
+ kTime1777500 = 1777500,
+ kTime1782000 = 1782000,
+ kTime1786500 = 1786500,
+ kTime1791000 = 1791000,
+ kTime1800000 = 1800000,
+ kTime1806300 = 1806300,
+ kTime1809000 = 1809000,
+ kTimeCityAugsburg = 1809900,
+ kTime1818000 = 1818000,
+ kTime1818900 = 1818900,
+ kTime1822500 = 1822500,
+ kTime1836000 = 1836000,
+ kTime1845000 = 1845000,
+ kTimeCityMunich = 1852200,
+
+ // Chapter 3
+ kTimeChapter3 = 1944000,
+ kTime1953000 = 1953000,
+ kTime1966500 = 1966500,
+ kTime1971000 = 1971000,
+ kTimeEnterSalzbourg = 1982700,
+ kTimeCitySalzbourg = 1984500,
+ kTime1989000 = 1989000,
+ kTimeExitSalzbourg = 1989900,
+ kTime1993500 = 1993500,
+ kTime1998000 = 1998000,
+ kTime2002500 = 2002500,
+ kTime2011500 = 2011500,
+ kTime2016000 = 2016000,
+ kTime2020500 = 2020500,
+ kTime2025000 = 2025000,
+ kTime2038500 = 2038500,
+ kTime2040300 = 2040300,
+ kTime2043000 = 2043000,
+ kTimeEnterAttnangPuchheim = 2047500,
+ kTimeCityAttnangPuchheim = 2049300,
+ kTime2052000 = 2052000,
+ kTimeExitAttnangPuchheim = 2052900,
+ kTime2061000 = 2061000,
+ kTime2062800 = 2062800,
+ kTime2070000 = 2070000,
+ kTimeEnterWels = 2073600,
+ kTimeCityWels = 2075400,
+ kTime2079000 = 2079000,
+ kTimeExitWels = 2079900,
+ kTime2083500 = 2083500,
+ kTime2088000 = 2088000,
+ kTime2092500 = 2092500,
+ kTime2097000 = 2097000,
+ kTimeEnterLinz = 2099700,
+ kTimeCityLinz = 2101500,
+ kTime2106000 = 2106000,
+ kTime2110500 = 2110500,
+ kTime2115000 = 2115000,
+ kTime2117700 = 2117700,
+ kTime2119500 = 2119500,
+ kTime2124000 = 2124000,
+ kTime2133000 = 2133000,
+ kTime2142000 = 2142000,
+ kTime2146500 = 2146500,
+ kTime2151000 = 2151000,
+ kTimeCityAmstetten = 2154600,
+ kTime2155500 = 2155500,
+ kTime2160000 = 2160000,
+ kTime2169000 = 2169000,
+ kTime2173500 = 2173500,
+ kTime2187000 = 2187000,
+ kTime2182500 = 2182500,
+ kTime2196000 = 2196000,
+ kTime2200500 = 2200500,
+ kTime2218500 = 2218500,
+ kTime2241000 = 2241000,
+ kTime2250000 = 2250000,
+ kTime2254500 = 2254500,
+ kTime2259000 = 2259000,
+ kTime2263500 = 2263500,
+ kTime2266200 = 2266200,
+ kTimeCityVienna = 2268000,
+
+ // Chapter 4
+ kTime2349000 = 2349000,
+ kTimeChapter4 = 2353500,
+ kTime2354400 = 2354400,
+ kTime2356200 = 2356200,
+ kTime2358000 = 2358000,
+ kTime2360700 = 2360700,
+ kTime2362500 = 2362500,
+ kTime2361600 = 2361600,
+ kTime2367000 = 2367000,
+ kTime2370600 = 2370600,
+ kTime2378700 = 2378700,
+ kTimeEnterPoszony = 2381400,
+ kTimeCityPoszony = 2383200,
+ kTime2385000 = 2385000,
+ kTimeExitPoszony = 2386800,
+ kTime2389500 = 2389500,
+ kTime2403000 = 2403000,
+ kTime2407500 = 2407500,
+ kTime2410200 = 2410200,
+ kTime2412000 = 2412000,
+ kTime2414700 = 2414700,
+ kTime2415600 = 2415600,
+ kTimeEnterGalanta = 2416500,
+ kTimeCityGalanta = 2418300,
+ kTime2421000 = 2421000,
+ kTimeExitGalanta = 2421900,
+ kTime2422800 = 2422800,
+ kTime2428200 = 2428200,
+ kTime2425500 = 2425500,
+ kTime2430000 = 2430000,
+ kTime2439000 = 2439000,
+ kTime2443500 = 2443500,
+ kTime2448000 = 2448000,
+ kTime2452500 = 2452500,
+ kTime2455200 = 2455200,
+ kTime2457000 = 2457000,
+ kTime2466000 = 2466000,
+ kTime2470500 = 2470500,
+ kTime2475000 = 2475000,
+ kTime2479500 = 2479500,
+ kTime2484000 = 2484000,
+ kTime2488500 = 2488500,
+ kTime2506500 = 2506500,
+ kTime2511000 = 2511000,
+ kTime2511900 = 2511900,
+ kTime2517300 = 2517300,
+ kTime2519100 = 2519100,
+ kTime2520000 = 2520000,
+ kTime2533500 = 2533500,
+ kTime2535300 = 2535300,
+ kTime2538000 = 2538000,
+ kTimeCityBudapest = 2551500,
+
+ // Chapter 5
+ kTimeChapter5 = 2844000,
+ kTimeTrainStopped = 2898000,
+ kTime2907000 = 2907000,
+ kTime2916000 = 2916000,
+ kTimeCityBelgrade = 2952000,
+ kTimeTrainStopped2 = 2943000,
+ kTimeCityNish = 3205800,
+ kTimeCityTzaribrod = 3492000,
+ kTime3645000 = 3645000,
+ kTimeCitySofia = 3690000,
+ kTimeCityAdrianople = 4320900,
+ kTime4923000 = 4923000,
+ kTime4929300 = 4929300,
+ kTimeCityConstantinople = 4941000,
+
+
+ kTime10881000 = 10881000,
+ kTimeEnd = 15803100,
+ kTime16451100 = 16451100,
+
+ kTimeInvalid = 2147483647,
+ kTimeInvalid2 = 0xFFFFFEDA
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Archive & Chapter ID
+//////////////////////////////////////////////////////////////////////////
+enum ArchiveIndex {
+ kArchiveAll = 0,
+ kArchiveCd1 = 1,
+ kArchiveCd2 = 2,
+ kArchiveCd3 = 3
+};
+
+enum ChapterIndex {
+ kChapterAll = 0,
+ kChapter1 = 1,
+ kChapter2 = 2,
+ kChapter3 = 3,
+ kChapter4 = 4,
+ kChapter5 = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Index of scenes
+//////////////////////////////////////////////////////////////////////////
+enum SceneIndex {
+ kSceneNone = 0,
+ kSceneMenu = 1,
+
+ // Inventory
+ kSceneMatchbox = 31,
+ kSceneTelegram = 32,
+ kScenePassengerList = 33,
+ kSceneScarf = 34,
+ kSceneParchemin = 35,
+ kSceneArticle = 36,
+ kScenePaper = 37,
+ kSceneFirebird = 38,
+ kSceneBriefcase = 39,
+
+ // Normal scenes
+ kSceneDefault = 40,
+ kScene41 = 41,
+ kSceneCompartmentCorpse = 42, // Tyler compartment with corpse on floor
+
+ // Fight
+ kSceneFightMilos = 43,
+ kSceneFightMilosBedOpened = 44,
+ kSceneFightAnna = 45,
+ kSceneFightIvo = 46,
+ kSceneFightSalko = 47,
+ kSceneFightVesna = 48,
+
+ kSceneEuropeMap = 49,
+
+ // Game over
+ kSceneGameOverStopPolice = 50,
+ kSceneGameOverTrainStopped = 51,
+ kSceneGameOverTrainStopped2 = 52,
+ kSceneGameOverTrainExplosion = 53,
+ kSceneGameOverTrainExplosion2 = 54,
+ kSceneGameOverBloodJacket = 55,
+ kSceneGameOverPolice = 56,
+ kSceneGameOverPolice1 = 57,
+ kSceneGameOverAnnaDied = 58,
+ kSceneGameOverVienna = 59,
+ kSceneGameOverVienna1 = 60,
+ kSceneGameOverVienna2 = 61,
+ kSceneGameOverAlarm = 62,
+ kSceneGameOverPolice2 = 63,
+ kSceneGameOverAlarm2 = 64,
+
+ // Start screen
+ kSceneStartScreen = 65,
+
+ kSceneBeetle = 128,
+
+ kSceneFightDefault = 820,
+
+ kSceneInvalid = 0xffffffff
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Jacket
+//////////////////////////////////////////////////////////////////////////
+enum JacketType {
+ kJacketOriginal = 0,
+ kJacketBlood = 1,
+ kJacketGreen = 2
+};
+
+//////////////////////////////////////////////////////////////////////////
+// City
+//////////////////////////////////////////////////////////////////////////
+enum CityIndex {
+ kCityEpernay = 0,
+ kCityChalons,
+ kCityBarleduc,
+ kCityNancy,
+ kCityLuneville,
+ kCityAvricourt, // 5
+ kCityDeutschAvricourt,
+ kCityStrasbourg,
+ kCityBadenOos,
+ kCitySalzbourg,
+ kCityAttnangPuchheim, // 10
+ kCityWels,
+ kCityLinz,
+ kCityVienna,
+ kCityPoszony,
+ kCityGalanta, // 15
+ kCityPolice
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame ID
+//////////////////////////////////////////////////////////////////////////
+enum GameId {
+ kGameBlue,
+ kGameRed,
+ kGameGreen,
+ kGamePurple,
+ kGameTeal,
+ kGameGold
+};
+
+enum SavegameType {
+ kSavegameTypeIndex = 0,
+ kSavegameTypeTime = 1,
+ kSavegameTypeEvent = 2,
+ kSavegameTypeEvent2 = 3,
+ kSavegameTypeAuto = 4,
+ kSavegameTypeTickInterval = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Cursor style
+//////////////////////////////////////////////////////////////////////////
+enum CursorStyle {
+ kCursorNormal,
+ kCursorForward,
+ kCursorBackward,
+ kCursorTurnRight,
+ kCursorTurnLeft,
+ kCursorUp,
+ kCursorDown,
+ kCursorLeft,
+ kCursorRight,
+ kCursorHand,
+ kCursorHandKnock, // 10
+ kCursorMagnifier,
+ kCursorHandPointer,
+ kCursorSleep,
+ kCursorTalk,
+ kCursorTalk2, // Need better name
+
+ // Items
+ kCursorMatchBox,
+ kCursorTelegram,
+ kCursorPassengerList,
+ kCursorArticle,
+ kCursorScarf, // 20
+ kCursorPaper,
+ kCursorParchemin,
+ kCursorMatch,
+ kCursorWhistle,
+ kCursorKey,
+ kCursorBomb,
+ kCursorFirebird,
+ kCursorBriefcase,
+ kCursorCorpse,
+
+ // Combat
+ kCursorPunchLeft, // 30
+ kCursorPunchRight,
+
+ // Portraits
+ kCursorPortrait, // 32
+ kCursorPortraitSelected,
+ kCursorPortraitGreen,
+ kCursorPortraitGreenSelected,
+ kCursorPortraitYellow,
+ kCursorPortraitYellowSelected,
+ kCursorHourGlass,
+ kCursorEggBlue,
+ kCursorEggRed, // 40
+ kCursorEggGreen,
+ kCursorEggPurple,
+ kCursorEggTeal,
+ kCursorEggGold,
+ kCursorEggClock,
+ kCursorNormal2,
+ kCursorBlank,
+ kCursorMAX,
+
+ // Special
+ kCursorProcess = 128,
+ kCursorKeepValue = 255
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Position - should be between 0 & 100
+//////////////////////////////////////////////////////////////////////////
+typedef unsigned char Position;
+
+//////////////////////////////////////////////////////////////////////////
+// EntityPosition
+//////////////////////////////////////////////////////////////////////////
+enum EntityPosition {
+ kPositionNone = 0,
+ kPosition_1 = 1,
+ kPosition_3 = 3,
+ kPosition_4 = 4,
+ kPosition_500 = 500,
+ kPosition_540 = 540,
+ kPosition_750 = 750,
+ kPosition_849 = 849,
+ kPosition_850 = 850,
+ kPosition_851 = 851,
+ kPosition_1200 = 1200,
+ kPosition_1430 = 1430,
+ kPosition_1500 = 1500,
+ kPosition_1540 = 1540,
+ kPosition_1750 = 1750,
+ kPosition_2000 = 2000,
+ kPosition_2087 = 2087,
+ kPosition_2086 = 2086,
+ kPosition_2088 = 2088,
+ kPosition_2110 = 2110,
+ kPosition_2300 = 2300,
+ kPosition_2330 = 2330,
+ kPosition_2410 = 2410,
+ kPosition_2436 = 2436,
+ kPosition_2490 = 2490,
+ kPosition_2500 = 2500,
+ kPosition_2587 = 2587,
+ kPosition_2588 = 2588,
+ kPosition_2690 = 2690,
+ kPosition_2740 = 2740,
+ kPosition_2830 = 2830,
+ kPosition_2980 = 2980,
+ kPosition_3050 = 3050,
+ kPosition_3110 = 3110,
+ kPosition_3390 = 3390,
+ kPosition_3450 = 3450,
+ kPosition_3500 = 3500,
+ kPosition_3650 = 3650,
+ kPosition_3760 = 3760,
+ kPosition_3820 = 3820,
+ kPosition_3890 = 3890,
+ kPosition_3969 = 3969,
+ kPosition_3970 = 3970,
+ kPosition_4070 = 4070,
+ kPosition_4100 = 4100,
+ kPosition_4370 = 4370,
+ kPosition_4455 = 4455,
+ kPosition_4460 = 4460,
+ kPosition_4500 = 4500,
+ kPosition_4590 = 4590,
+ kPosition_4680 = 4680,
+ kPosition_4689 = 4689,
+ kPosition_4690 = 4690,
+ kPosition_4691 = 4691,
+ kPosition_4770 = 4470,
+ kPosition_4840 = 4840,
+ kPosition_5000 = 5000,
+ kPosition_5090 = 5090,
+ kPosition_5140 = 5140,
+ kPosition_5419 = 5419,
+ kPosition_5420 = 5420,
+ kPosition_5440 = 5440,
+ kPosition_5500 = 5500,
+ kPosition_5540 = 5540,
+ kPosition_5610 = 5610,
+ kPosition_5790 = 5790,
+ kPosition_5799 = 5799,
+ kPosition_5800 = 5800,
+ kPosition_5810 = 5810,
+ kPosition_5890 = 5890,
+ kPosition_5900 = 5900,
+ kPosition_5970 = 5970,
+ kPosition_6000 = 6000,
+ kPosition_6130 = 6130,
+ kPosition_6160 = 6160,
+ kPosition_6220 = 6220,
+ kPosition_6410 = 6410,
+ kPosition_6460 = 6460,
+ kPosition_6469 = 6469,
+ kPosition_6470 = 6470,
+ kPosition_6471 = 6471,
+ kPosition_6800 = 6800,
+ kPosition_6850 = 6850,
+ kPosition_7000 = 7000,
+ kPosition_7160 = 7160,
+ kPosition_7250 = 7250,
+ kPosition_7320 = 7320,
+ kPosition_7500 = 7500,
+ kPosition_7510 = 7510,
+ kPosition_7850 = 7850,
+ kPosition_7870 = 7870,
+ kPosition_7900 = 7900,
+ kPosition_7950 = 7950,
+ kPosition_8000 = 8000,
+ kPosition_8012 = 8012,
+ kPosition_8013 = 8013,
+ kPosition_8160 = 8160,
+ kPosition_8200 = 8200,
+ kPosition_8500 = 8500,
+ kPosition_8512 = 8512,
+ kPosition_8513 = 8513,
+ kPosition_8514 = 8514,
+ kPosition_8800 = 8800,
+ kPosition_9020 = 9020,
+ kPosition_9269 = 9269,
+ kPosition_9250 = 9250,
+ kPosition_9270 = 9270,
+ kPosition_9271 = 9271,
+ kPosition_9460 = 9460,
+ kPosition_9500 = 9500,
+ kPosition_9510 = 9510,
+ kPosition_30000 = 30000
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Location
+//////////////////////////////////////////////////////////////////////////
+enum Location {
+ kLocationOutsideCompartment = 0,
+ kLocationInsideCompartment = 1,
+ kLocationOutsideTrain = 2
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Car
+//////////////////////////////////////////////////////////////////////////
+enum CarIndex {
+ kCarNone = 0,
+ kCarBaggageRear = 1,
+ kCarKronos = 2,
+ kCarGreenSleeping = 3,
+ kCarRedSleeping = 4,
+ kCarRestaurant = 5,
+ kCarBaggage = 6,
+ kCarCoalTender = 7,
+ kCarLocomotive = 8,
+ kCar9 = 9
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Clothes
+//////////////////////////////////////////////////////////////////////////
+enum ClothesIndex {
+ kClothesDefault = 0,
+ kClothes1 = 1,
+ kClothes2 = 2,
+ kClothes3 = 3,
+
+ kClothesInvalid
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Location of objects
+//////////////////////////////////////////////////////////////////////////
+enum ObjectLocation {
+ kObjectLocationNone = 0,
+ kObjectLocation1 = 1, // Floor?
+ kObjectLocation2 = 2, // Bed ?
+ kObjectLocation3 = 3,
+ kObjectLocation4 = 4, // Window ?
+ kObjectLocation5 = 5,
+ kObjectLocation6 = 6,
+ kObjectLocation7 = 7,
+ kObjectLocation10 = 10
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Entity direction
+//////////////////////////////////////////////////////////////////////////
+enum EntityDirection {
+ kDirectionNone = 0,
+ kDirectionUp = 1,
+ kDirectionDown = 2,
+ kDirectionLeft = 3,
+ kDirectionRight = 4,
+ kDirectionSwitch = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Combat
+//////////////////////////////////////////////////////////////////////////
+enum FightType {
+ kFightMilos = 2001,
+ kFightAnna = 2002,
+ kFightIvo = 2003,
+ kFightSalko = 2004,
+ kFightVesna = 2005
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Index of items in inventory data
+//////////////////////////////////////////////////////////////////////////
+enum InventoryItem {
+ kItemNone,
+ kItemMatchBox,
+ kItem2,
+ kItem3,
+ kItemTelegram,
+ kItem5, // 5
+ kItemPassengerList,
+ kItem7,
+ kItemScarf,
+ kItem9,
+ kItemParchemin, // 10
+ kItem11,
+ kItemMatch,
+ kItemWhistle,
+ kItemBeetle,
+ kItemKey, // 15
+ kItemBomb,
+ kItem17,
+ kItemFirebird,
+ kItemBriefcase,
+ kItemCorpse, // 20
+ kItemGreenJacket,
+ kItem22,
+ kItemPaper,
+ kItemArticle,
+ kItem25, // 25
+ kItem26,
+ kItem27,
+ kItem28,
+ kItem29,
+ kItem30, // 30
+ kItem31,
+
+ // Portrait (not an index)
+ kPortraitOriginal = 32,
+ kPortraitGreen = 34,
+ kPortraitYellow = 36,
+
+ kItemInvalid = 128,
+ kItem146 = 146,
+
+ // Toggles
+ kItemToggleHigh = 0x7F,
+ kItemToggleLow = 0xF7
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Object ID
+//////////////////////////////////////////////////////////////////////////
+enum ObjectIndex {
+ kObjectNone,
+ kObjectCompartment1,
+ kObjectCompartment2,
+ kObjectCompartment3,
+ kObjectCompartment4,
+ kObjectCompartment5, // 5
+ kObjectCompartment6,
+ kObjectCompartment7,
+ kObjectCompartment8,
+ kObjectOutsideTylerCompartment,
+ kObject10, // 10
+ kObject11,
+ kObject12,
+ kObject13,
+ kObject14,
+ kObject15, // 15
+ kObject16,
+ kObjectHandleBathroom,
+ kObjectHandleInsideBathroom,
+ kObjectKitchen,
+ kObject20, // 20
+ kObject21,
+ kObject22,
+ kObjectTrainTimeTable,
+ kObjectRedSleepingCar,
+ kObject25, // 25
+ kObjectHandleOutsideLeft,
+ kObjectHandleOutsideRight,
+ kObject28,
+ kObject29,
+ kObject30, // 30
+ kObject31,
+ kObjectCompartmentA,
+ kObjectCompartmentB,
+ kObjectCompartmentC,
+ kObjectCompartmentD, // 35
+ kObjectCompartmentE,
+ kObjectCompartmentF,
+ kObjectCompartmentG,
+ kObjectCompartmentH,
+ kObject40, // 40
+ kObject41,
+ kObject42,
+ kObject43,
+ kObjectOutsideBetweenCompartments,
+ kObjectOutsideAnnaCompartment, // 45
+ kObject46,
+ kObject47,
+ kObject48, // might be the egg
+ kObject49,
+ kObject50, // 50
+ kObject51,
+ kObject52,
+ kObject53,
+ kObject54,
+ kObjectRestaurantCar, // 55
+ kObject56,
+ kObject57,
+ kObject58,
+ kObject59,
+ kObject60, // 60
+ kObject61,
+ kObject62,
+ kObject63,
+ kObject64,
+ kObject65, // 65
+ kObject66,
+ kObject67,
+ kObject68,
+ kObject69,
+ kObject70, // 70
+ kObject71,
+ kObject72,
+ kObjectCeiling,
+ kObject74,
+ kObjectCompartmentKronos, // 75
+ kObject76,
+ kObject77,
+ kObject78,
+ kObject79,
+ kObject80, // 80
+ kObject81,
+ kObject82,
+ kObject83,
+ kObject84,
+ kObject85, // 85
+ kObject86,
+ kObject87,
+ kObject88,
+ kObject89,
+ kObject90, // 90
+ kObject91,
+ kObject92,
+ kObject93,
+ kObject94,
+ kObject95, // 95
+ kObject96,
+ kObject97,
+ kObject98,
+ kObject99,
+ kObject100, // 100
+ kObject101,
+ kObject102,
+ kObject103,
+ kObject104,
+ kObject105, // 105
+ kObject106,
+ kObject107,
+ kObject108,
+ kObjectCageMax,
+ kObject110, // 110
+ kObject111,
+ kObject112,
+ kObject113,
+ kObject114,
+ kObject115, // 115
+ kObject116,
+ kObject117,
+ kObject118,
+ kObject119,
+ kObject120, // 120
+ kObject121,
+ kObject122,
+ kObject123,
+ kObject124,
+ kObject125, // 125
+ kObject126,
+ kObject127,
+ kObjectMax
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Entity ID
+//////////////////////////////////////////////////////////////////////////
+enum EntityIndex {
+ kEntityPlayer,
+ kEntityAnna,
+ kEntityAugust,
+ kEntityMertens,
+ kEntityCoudert,
+ kEntityPascale, // 5
+ kEntityServers0,
+ kEntityServers1,
+ kEntityCooks,
+ kEntityVerges,
+ kEntityTatiana, // 10
+ kEntityVassili,
+ kEntityAlexei,
+ kEntityAbbot,
+ kEntityMilos,
+ kEntityVesna, // 15
+ kEntityIvo,
+ kEntitySalko,
+ kEntityKronos,
+ kEntityKahina,
+ kEntityFrancois, // 20
+ kEntityMmeBoutarel,
+ kEntityBoutarel,
+ kEntityRebecca,
+ kEntitySophie,
+ kEntityMahmud, // 25
+ kEntityYasmin,
+ kEntityHadija,
+ kEntityAlouan,
+ kEntityGendarmes,
+ kEntityMax, // 30
+ kEntityChapters,
+ kEntityTrain,
+ kEntityTables0,
+ kEntityTables1,
+ kEntityTables2, // 35
+ kEntityTables3,
+ kEntityTables4,
+ kEntityTables5,
+ kEntity39,
+
+ kEntitySteam = 255
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Events
+// - a single D at the end means that Cath is on the right of the "scene" (D = Down the train, U = Up the train)
+// - DD: during the day, coming down the train
+// - DU: during the day, coming up the train
+// - ND: during the night, coming down the train
+// - NU: during the night, coming up the train
+//////////////////////////////////////////////////////////////////////////
+enum EventIndex {
+ kEventNone = 0,
+ kEventGotALight = 1,
+ kEventGotALightD = 2,
+ kEventDinerMindJoin = 3,
+ kEventDinerAugustOriginalJacket = 4,
+ kEventDinerAugust = 5,
+ kEventDinerAugustAlexeiBackground = 6,
+ kEventMeetAugustTylerCompartment = 7,
+ kEventMeetAugustTylerCompartmentBed = 8,
+ kEventMeetAugustHisCompartment = 9,
+ kEventMeetAugustHisCompartmentBed = 10,
+ kEventAugustFindCorpse = 11,
+ kEventAugustPresentAnna = 12,
+ kEventAugustPresentAnnaFirstIntroduction = 13,
+ kEventAnnaIntroductionRejected = 14,
+ kEventAnnaConversationGoodNight = 15,
+ kEventAnnaVisitToCompartmentGun = 16,
+ kEventInvalid_17 = 17,
+ kEventAnnaGoodNight = 18,
+ kEventAnnaGoodNightInverse = 19,
+ kEventAugustGoodMorning = 20,
+ kEventAugustMerchandise = 21,
+ kEventAugustTalkGold = 22,
+ kEventAugustTalkGoldDay = 23,
+ kEventAugustTalkCompartmentDoor = 24,
+ kEventAugustTalkCompartmentDoorBlueRedingote = 25,
+ kEventAugustLunch = 26,
+ kEventKronosVisit = 27,
+ kEventAnnaSearchingCompartment = 28,
+ kEventAugustBringEgg = 29,
+ kEventAugustBringBriefcase = 30,
+ kEventAugustTalkCigar = 31,
+ kEventAnnaBaggageArgument = 32,
+ kEventAnnaBagagePart2 = 33,
+ kEventAnnaConversation_34 = 34,
+ kEventAugustDrink = 35,
+ kEventAnnaTired = 36,
+ kEventAnnaTiredKiss = 37,
+ kEventAnnaBaggageTies = 38,
+ kEventAnnaBaggageTies2 = 39,
+ kEventAnnaBaggageTies3 = 40,
+ kEventAnnaBaggageTies4 = 41,
+ kEventAugustUnhookCarsBetrayal = 42,
+ kEventAugustUnhookCars = 43,
+ kEventLocomotiveAnnaStopsTrain = 44,
+ kEventInvalid_45 = 45,
+ kEventTrainStopped = 46,
+ kEventAnnaKissTrainHijacked = 47,
+ kEventTrainHijacked = 48,
+ kEventAnnaKilled = 49,
+ kEventKronosGoingToInvitation = 50,
+ kEventKronosConversation = 51,
+ kEventKahinaAskSpeakFirebird = 52,
+ kEventKahinaAskSpeak = 53,
+ kEventKronosConversationFirebird = 54,
+ kEventKahinaGunYellow = 55,
+ kEventKahinaGunBlue = 56,
+ kEventKahinaGun = 57,
+ kEventKronosBringEggCeiling = 58,
+ kEventKronosBringEgg = 59,
+ kEventKronosBringNothing = 60,
+ kEventKronosReturnBriefcase = 61,
+ kEventKronosHostageAnna = 62,
+ kEventKronosGiveFirebird = 63,
+ kEventKahinaPunchBaggageCarEntrance = 64,
+ kEventKahinaPunchBlue = 65,
+ kEventKahinaPunchYellow = 66,
+ kEventKahinaPunchSalon = 67,
+ kEventKahinaPunchKitchen = 68,
+ kEventKahinaPunchBaggageCar = 69,
+ kEventKahinaPunchCar = 70,
+ kEventKahinaPunchSuite4 = 71,
+ kEventKahinaPunchRestaurant = 72,
+ kEventKronosHostageAnnaNoFirebird = 73,
+ kEventKahinaPunch = 74,
+ kEventKahinaWrongDoor = 75,
+ kEventAlexeiDiner = 76,
+ kEventAlexeiDinerOriginalJacket = 77,
+ kEventAlexeiSalonVassili = 78,
+ kEventAlexeiSalonCath = 79,
+ kEventAlexeiSalonPoem = 80,
+ kEventTatianaAskMatchSpeakRussian = 81,
+ kEventTatianaAskMatch = 82,
+ kEventTatianaGivePoem = 83,
+ kEventVassiliSeizure = 84,
+ kEventTatianaBreakfastAlexei = 85,
+ kEventTatianaBreakfast = 86,
+ kEventTatianaBreakfastGivePoem = 87,
+ kEventTatianaAlexei = 88,
+ kEventTatianaCompartmentStealEgg = 89,
+ kEventTatianaCompartment = 90,
+ kEventVassiliCompartmentStealEgg = 91,
+ kEventTatianaTylerCompartment = 92,
+ kEventTylerCastleDream= 93,
+ kEventVassiliDeadAlexei = 94,
+ kEventCathFreePassengers = 95,
+ kEventTatianaVassiliTalk = 96,
+ kEventTatianaVassiliTalkNight = 97,
+ kEventMilosTylerCompartmentVisit = 98,
+ kEventMilosTylerCompartmentBedVisit = 99,
+ kEventMilosTylerCompartment = 100,
+ kEventMilosTylerCompartmentBed = 101,
+ kEventMilosTylerCompartmentDefeat = 102,
+ kEventMilosCorpseFloor = 103,
+ kEventMilosCompartmentVisitAugust = 104,
+ kEventMilosCorridorThanks = 105,
+ kEventMilosCorridorThanksD = 106,
+ kEventMilosCompartmentVisitTyler = 107,
+ kEventLocomotiveMilos = 108,
+ kEventLocomotiveMilosNight = 109,
+ kEventAbbotIntroduction = 110,
+ kEventAbbotWrongCompartment = 111,
+ kEventAbbotWrongCompartmentBed = 112,
+ kEventAbbotInvitationDrink = 113,
+ kEventAbbotDrinkGiveDetonator = 114,
+ kEventTrainExplosionBridge = 115,
+ kEventDefuseBomb = 116,
+ kEventAbbotDrinkDefuse = 117,
+ kEventMertensLastCar = 118,
+ kEventMertensLastCarOriginalJacket = 119,
+ kEventMertensKronosInvitation = 120,
+ kEventMertensKronosInvitationCompartment = 121,
+ kEventMertensKronosInvitationClosedWindows = 122,
+ kEventMertensBloodJacket = 123,
+ kEventCoudertBloodJacket = 124,
+ kEventMertensCorpseFloor = 125,
+ kEventMertensCorpseBed = 126,
+ kEventMertensDontMakeBed = 127,
+ kEventInvalid_128 = 128,
+ kEventGendarmesArrestation = 129,
+ kEventVergesSuitcase = 130,
+ kEventVergesSuitcaseStart = 131,
+ kEventVergesSuitcaseOtherEntry = 132,
+ kEventVergesSuitcaseOtherEntryStart = 133,
+ kEventVergesSuitcaseNight = 134,
+ kEventVergesSuitcaseNightStart = 135,
+ kEventVergesSuitcaseNightOtherEntry = 136,
+ kEventVergesSuitcaseNightOtherEntryStart = 137,
+ kEventMertensAskTylerCompartment = 138,
+ kEventMertensAskTylerCompartmentD = 139,
+ kEventMertensPushCall = 140,
+ kEventMertensPushCallNight = 141,
+ kEventMertensAugustWaiting = 142,
+ kEventMertensAugustWaitingCompartment = 143,
+ kEventIntroBroderbrund = 144,
+ kEventCoudertAskTylerCompartment = 145,
+ kEventMertensKronosConcertInvitation = 146,
+ kEventCoudertGoingOutOfVassiliCompartment = 147,
+ kEventLocomotiveConductorsDiscovered = 148,
+ kEventLocomotiveConductorsLook = 149,
+ kEventMahmudWrongDoor = 150,
+ kEventMahmudWrongDoorOriginalJacket = 151,
+ kEventMahmudWrongDoorDay = 152,
+ kEventVergesEscortToDiningCar = 153,
+ kEventVergesBaggageCarOffLimits = 154,
+ kEventVergesCanIHelpYou = 155,
+ kEventCoudertBaggageCar = 156,
+ kEventCathTurningDay = 157,
+ kEventCathTurningNight = 158,
+ kEventIntro = 159,
+ kEventCathDream = 160,
+ kEventCorpseDropBridge = 161,
+ kEventTrainPassing = 162,
+ kEventVergesAnnaDead = 163,
+ kEventViennaAugustUnloadGuns = 164,
+ kEventViennaKronosFirebird = 165,
+ kEventViennaContinueGame = 166,
+ kEventCathVesnaRestaurantKilled = 167,
+ kEventCathMaxCage = 168,
+ kEventCathMaxFree = 169,
+ kEventCathMaxLickHand = 170,
+ kEventCathIvoFight = 171,
+ kEventCathSalkoTrainTopFight = 172,
+ kEventCathVesnaTrainTopFight = 173,
+ kEventCathVesnaTrainTopKilled = 174,
+ kEventCathVesnaTrainTopWin = 175,
+ kEventCathSalkoTrainTopWin = 176,
+ kEventFrancoisWhistle = 177,
+ kEventFrancoisWhistleD = 178,
+ kEventFrancoisWhistleNight = 179,
+ kEventFrancoisWhistleNightD = 180,
+ kEventFrancoisShowBeetle = 181,
+ kEventFrancoisShowBeetleD = 182,
+ kEventFrancoisTradeWhistle = 183,
+ kEventFrancoisTradeWhistleD = 184,
+ kEventFrancoisShowEgg = 185,
+ kEventFrancoisShowEggD = 186,
+ kEventFrancoisShowEggNight = 187,
+ kEventFrancoisShowEggNightD = 188,
+ kEventKronosBringFirebird = 189,
+ kEventKronosOpenFirebird = 190,
+ kEventFinalSequence = 191,
+ kEventLocomotiveRestartTrain = 192,
+ kEventLocomotiveOldBridge = 193,
+ kEventLocomotiveAbbotGetSomeRest = 194,
+ kEventLocomotiveAbbotShoveling = 195,
+ kEventLocomotiveMilosShovelingDay = 196,
+ kEventLocomotiveMilosShovelingNight = 197,
+ kEventAnnaGiveScarf = 198,
+ kEventAnnaGiveScarfDiner = 199,
+ kEventAnnaGiveScarfSalon = 200,
+ kEventAnnaGiveScarfMonogram = 201,
+ kEventAnnaGiveScarfDinerMonogram = 202,
+ kEventAnnaGiveScarfSalonMonogram = 203,
+ kEventAnnaGiveScarfAsk = 204,
+ kEventAnnaGiveScarfDinerAsk = 205,
+ kEventAnnaGiveScarfSalonAsk = 206,
+ kEventAugustArrivalInMunich = 207,
+ kEventAnnaDialogGoToJerusalem = 208,
+ kEventConcertStart = 209,
+ kEventConcertEnd = 210,
+ kEventCathFallingAsleep = 211,
+ kEventCathWakingUp = 212,
+ kEventConcertCough = 213,
+ kEventConcertSit = 214,
+ kEventConcertLeaveWithBriefcase = 215,
+ kEventCorpseDropFloorOriginal = 216,
+ kEventCorpseDropFloorGreen = 217,
+ kEventCorpsePickFloorOriginal = 218,
+ kEventCorpsePickFloorGreen = 219,
+ kEventCorpsePickFloorOpenedBedOriginal = 220,
+ kEventCorpsePickBedOriginal = 221,
+ kEventCorpsePickBedGreen = 222,
+ kEventCorpseDropBedOriginal = 223,
+ kEventCorpseDropBedGreen = 224,
+ kEventCorpseDropWindowOriginal = 225,
+ kEventCorpseDropWindowGreen = 226,
+ kEventCathFindCorpse = 227,
+ kEventCathLookOutsideWindowDay = 228,
+ kEventCathLookOutsideWindowNight = 229,
+ kEventCathGoOutsideTylerCompartmentDay = 230,
+ kEventCathGoOutsideTylerCompartmentNight = 231,
+ kEventCathGoOutsideDay = 232,
+ kEventCathGoOutsideNight = 233,
+ kEventCathSlipTylerCompartmentDay = 234,
+ kEventCathSlipTylerCompartmentNight = 235,
+ kEventCathSlipDay = 236,
+ kEventCathSlipNight = 237,
+ kEventCathGetInsideTylerCompartmentDay = 238,
+ kEventCathGetInsideTylerCompartmentNight = 239,
+ kEventCathGetInsideDay = 240,
+ kEventCathGetInsideNight = 241,
+ kEventCathGettingInsideAnnaCompartment = 242,
+ kEventCathClimbUpTrainGreenJacket = 243,
+ kEventCathClimbUpTrainNoJacketNight = 244,
+ kEventCathClimbUpTrainNoJacketDay = 245,
+ kEventCathClimbDownTrainGreenJacket = 246,
+ kEventCathClimbDownTrainNoJacketNight = 247,
+ kEventCathClimbDownTrainNoJacketDay= 248,
+ kEventCathTopTrainGreenJacket = 249,
+ kEventCathTopTrainNoJacketNight = 250,
+ kEventCathTopTrainNoJacketDay = 251,
+ kEventCathBreakCeiling = 252,
+ kEventCathJumpDownCeiling = 253,
+ kEventCathJumpUpCeilingBriefcase = 254,
+ kEventCathJumpUpCeiling = 255,
+ kEventPickGreenJacket = 256,
+ kEventPickScarfGreen = 257,
+ kEventPickScarfOriginal = 258,
+ kEventCloseMatchbox = 259,
+ kEventCathStruggleWithBonds = 260,
+ kEventCathBurnRope = 261,
+ kEventCathRemoveBonds = 262,
+ kEventCathStruggleWithBonds2 = 263,
+ kEventCathDefusingBomb = 264,
+ kEventCathSmokeNight = 265,
+ kEventCathSmokeDay = 266,
+ kEventCathOpenEgg = 267,
+ kEventCathOpenEggNoBackground = 268,
+ kEventCathCloseEgg = 269,
+ kEventCathCloseEggNoBackground = 270,
+ kEventCathUseWhistleOpenEgg = 271,
+ kEventCathUseWhistleOpenEggNoBackground = 272
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Action ID (used by entity logic)
+//////////////////////////////////////////////////////////////////////////
+enum ActionIndex {
+ kActionNone = 0,
+ kAction1 = 1,
+ kActionEndSound = 2,
+ kActionExitCompartment = 3,
+ kAction4 = 4,
+ kActionExcuseMeCath = 5,
+ kActionExcuseMe = 6,
+ kActionKnock = 8,
+ kActionOpenDoor = 9,
+ kAction10 = 10,
+ kAction11 = 11,
+ kActionDefault = 12,
+ kAction16 = 16,
+ kActionDrawScene = 17,
+ kActionCallback = 18,
+
+ /////////////////////////////
+ // Abbot
+ /////////////////////////////
+ kAction100969180 = 100969180, // Anna
+ kAction101169422 = 101169422,
+ kAction104060776 = 104060776,
+ kAction135600432 = 135600432,
+ kAction136196244 = 136196244,
+ kAction157159392 = 157159392,
+ kAction157489665 = 157489665,
+ kAction158480160 = 158480160,
+ kAction192054567 = 192054567,
+ kAction203073664 = 203073664,
+ kAction222609266 = 222609266,
+
+ /////////////////////////////
+ // Alexei
+ /////////////////////////////
+ kAction100906246 = 100906246,
+ kAction123536024 = 123536024,
+ kAction124697504 = 124697504,
+ kAction135664192 = 135664192,
+ kAction135854208 = 135854208,
+ kAction188784532 = 188784532,
+ kAction221617184 = 221617184,
+
+ /////////////////////////////
+ // Alouan
+ /////////////////////////////
+ kAction189489753 = 189489753,
+ kAction190219584 = 190219584, // Francois
+
+ /////////////////////////////
+ // Anna
+ /////////////////////////////
+ kAction136702400 = 136702400,
+ kAction139254416 = 139254416,
+ kAction156049968 = 156049968,
+ kAction157370960 = 157370960,
+ kAction157894320 = 157894320,
+ kAction159332865 = 159332865, // August
+ kAction189299008 = 189299008,
+ kAction191668032 = 191668032, // some action during or before concert?
+ kAction201437056 = 201437056,
+ kAction235856512 = 235856512,
+ kAction236060709 = 236060709,
+ kAction238936000 = 238936000,
+ kAction259136835 = 259136835,
+ kAction291662081 = 291662081,
+
+
+ /////////////////////////////
+ // August
+ /////////////////////////////
+ kAction123793792 = 123793792,
+ kAction134611040 = 134611040,
+ kAction168046720 = 168046720,
+ kAction168627977 = 168627977,
+ kAction169032608 = 169032608,
+ kAction189426612 = 189426612,
+ kAction203859488 = 203859488,
+ kAction219522616 = 219522616, // Servers0
+ kAction225182640 = 225182640,
+ kAction235257824 = 235257824,
+
+ /////////////////////////////
+ // Boutarel
+ /////////////////////////////
+ kAction125039808 = 125039808,
+ kAction134466544 = 134466544,
+ kAction135854206 = 135854206,
+ kAction159003408 = 159003408,
+ kAction203520448 = 203520448,
+ kAction237889408 = 237889408,
+
+ /////////////////////////////
+ // Chapters
+ /////////////////////////////
+ kAction135800432 = 135800432,
+ kActionChapter3 = 139122728,
+ kActionChapter5 = 139254416,
+ kAction156435676 = 156435676,
+ kAction169629818 = 169629818,
+ kAction171843264 = 171843264,
+ kAction190346110 = 190346110,
+
+ /////////////////////////////
+ // Cooks
+ /////////////////////////////
+ kAction101632192 = 101632192,
+ kAction224849280 = 224849280,
+ kAction236976550 = 236976550,
+
+ /////////////////////////////
+ // Coudert
+ /////////////////////////////
+ kAction123733488 = 123733488,
+ kAction154005632 = 154005632,
+ kAction155991520 = 155991520,
+ kAction157026693 = 157026693,
+ kAction168253822 = 168253822,
+ kAction168254872 = 168254872,
+ kAction169557824 = 169557824,
+ kAction171394341 = 171394341, // Mertens
+ kAction185671840 = 185671840,
+ kAction185737168 = 185737168,
+ kAction188570113 = 188570113,
+ kAction189026624 = 189026624,
+ kAction189750912 = 189750912,
+ kAction192063264 = 192063264, // Anna
+ kAction201431954 = 201431954, // Mertens / Verges
+ kAction201439712 = 201439712,
+ kAction205033696 = 205033696,
+ kAction205346192 = 205346192, // Francois
+ kAction219971920 = 219971920, // Anna
+ kAction223068211 = 223068211, // MmeBoutarel
+ kAction225932896 = 225932896,
+ kAction226031488 = 226031488, // Verges
+ kAction238358920 = 238358920, // Anna
+ kAction253868128 = 253868128, // Anna
+ kAction285528346 = 285528346, // Rebecca
+ kAction292048641 = 292048641,
+ kAction305159806 = 305159806,
+ kAction326348944 = 326348944,
+ kAction339669520 = 339669520, // Verges
+
+ /////////////////////////////
+ // Francois
+ /////////////////////////////
+ kAction100901266 = 100901266,
+ kAction100957716 = 100957716,
+ kAction101107728 = 101107728,
+ kAction189872836 = 189872836,
+ kAction190390860 = 190390860,
+
+ /////////////////////////////
+ // Gendarmes
+ /////////////////////////////
+ kAction168710784 = 168710784,
+ kAction169499649 = 169499649,
+
+ /////////////////////////////
+ // Kahina
+ /////////////////////////////
+ kAction137503360 = 137503360,
+
+ /////////////////////////////
+ // Kronos
+ /////////////////////////////
+ kAction137685712 = 137685712,
+ kAction138085344 = 138085344,
+ kAction171849314 = 171849314,
+ kAction235599361 = 235599361,
+
+ /////////////////////////////
+ // Mahmud
+ /////////////////////////////
+ kAction102227384 = 102227384, // Mertens
+ kAction156567128 = 156567128,
+ kAction170483072 = 170483072,
+ kAction225563840 = 225563840,
+
+ /////////////////////////////
+ // Max
+ /////////////////////////////
+ kAction71277948 = 71277948,
+ kAction158007856 = 158007856,
+ kAction101687594 = 101687594,
+ kAction122358304 = 122358304, // also Servers1/Boutarel?
+ kActionMaxFreeFromCage = 135204609,
+ kAction156622016 = 156622016,
+
+ /////////////////////////////
+ // Mertens
+ /////////////////////////////
+ kAction155604840 = 155604840, // MmeBoutarel
+ kAction169633856 = 169633856,
+ kAction188635520 = 188635520,
+ kAction190082817 = 190082817,
+ kAction192849856 = 192849856,
+ kAction204379649 = 204379649,
+ kAction224122407 = 224122407,
+ kAction238732837 = 238732837,
+ kAction238790488 = 238790488, // Tatiana
+ kAction269436673 = 269436673,
+ kAction269624833 = 269624833,
+ kAction302614416 = 302614416,
+ kAction303343617 = 303343617,
+
+ /////////////////////////////
+ // Milos
+ /////////////////////////////
+ kAction88652208 = 88652208, // Coudert
+ kAction122865568 = 122865568,
+ kAction123852928 = 123852928,
+ kAction123199584 = 123199584, // Coudert
+ kAction157691176 = 157691176,
+ kAction208228224 = 208228224,
+ kAction221683008 = 221683008,
+ kAction259125998 = 259125998,
+
+ /////////////////////////////
+ // Mme Boutarel
+ /////////////////////////////
+ kAction102484312 = 102484312,
+ kAction102752636 = 102752636,
+ kAction134289824 = 134289824,
+ kAction168986720 = 168986720,
+ kAction202221040 = 202221040,
+ kAction242526416 = 242526416,
+
+ /////////////////////////////
+ // Pascale
+ /////////////////////////////
+ kAction101824388 = 101824388,
+ kAction136059947 = 136059947,
+ kAction169750080 = 169750080,
+ kAction190605184 = 190605184,
+ kAction191604416 = 191604416,
+ kAction207769280 = 207769280,
+ kAction223262556 = 223262556,
+ kAction239072064 = 239072064,
+ kAction257489762 = 257489762,
+ kAction269479296 = 269479296,
+ kAction352703104 = 352703104,
+ kAction352768896 = 352768896,
+
+ /////////////////////////////
+ // Rebecca
+ /////////////////////////////
+ kAction125496184 = 125496184,
+ kAction155465152 = 155465152,
+ kAction155980128 = 155980128,
+ kAction169358379 = 169358379,
+ kAction224253538 = 224253538,
+ kAction254915200 = 254915200,
+
+ /////////////////////////////
+ // Salko
+ /////////////////////////////
+ kAction55996766 = 55996766,
+ kAction101169464 = 101169464,
+ kAction102675536 = 102675536, // Ivo
+ kAction136184016 = 136184016,
+
+ /////////////////////////////
+ // Servers 0
+ /////////////////////////////
+ kAction170016384 = 170016384,
+ kAction188893625 = 188893625,
+ kAction201964801 = 201964801, // August
+ kAction204704037 = 204704037,
+ kAction207330561 = 207330561,
+ kAction218128129 = 218128129,
+ kAction218586752 = 218586752,
+ kAction218983616 = 218983616,
+ kAction223712416 = 223712416,
+ kAction237485916 = 237485916,
+ kAction252568704 = 252568704,
+ kAction268773672 = 268773672, // Anna / August
+ kAction270068760 = 270068760,
+ kAction270410280 = 270410280,
+ kAction286403504 = 286403504,
+ kAction286534136 = 286534136,
+ kAction292758554 = 292758554,
+ kAction304061224 = 304061224,
+ kAction337548856 = 337548856,
+
+ /////////////////////////////
+ // Servers 1
+ /////////////////////////////
+ kAction101106391 = 101106391,
+ kAction122288808 = 122288808, // Boutarel
+ kAction123712592 = 123712592, // Ivo
+ kAction125826561 = 125826561, // August
+ kAction134486752 = 134486752, // August
+ kAction168717392 = 168717392, // Boutarel
+ kAction189688608 = 189688608,
+ kAction219377792 = 219377792,
+ kAction223002560 = 223002560,
+ kAction236237423 = 236237423,
+ kAction256200848 = 256200848,
+ kAction258136010 = 258136010,
+ kAction269485588 = 269485588,
+ kAction291721418 = 291721418,
+ kAction302203328 = 302203328,
+ kAction302996448 = 302996448,
+ kAction326144276 = 326144276,
+
+ /////////////////////////////
+ // Sophie
+ /////////////////////////////
+ kActionProceedChapter5 = 70549068,
+ kAction123668192 = 123668192,
+ kAction125242096 = 125242096,
+ kAction136654208 = 136654208,
+ kAction259921280 = 259921280,
+ kAction292775040 = 292775040,
+
+ /////////////////////////////
+ // Tables
+ /////////////////////////////
+ kActionDrawTablesWithChairs = 103798704,
+ kAction136455232 = 136455232,
+
+ /////////////////////////////
+ // Tatiana
+ /////////////////////////////
+ kAction69239528 = 69239528,
+ kAction123857088 = 123857088,
+ kAction124973510 = 124973510,
+ kAction156444784 = 156444784,
+ kAction169360385 = 169360385,
+ kAction191198209 = 191198209,
+ kAction223183000 = 223183000, // August
+ kAction236053296 = 236053296, // Alexei
+ kAction236241630 = 236241630, // Anna
+ kAction236517970 = 236517970, // Anna
+ kAction268620864 = 268620864, // August
+ kAction290869168 = 290869168,
+
+ /////////////////////////////
+ // Train
+ /////////////////////////////
+ kAction191070912 = 191070912,
+ kActionTrainStopRunning = 191350523,
+ kActionCatchBeetle = 202613084,
+ kAction203339360 = 203339360,
+ kActionTrainStartRunning = 203419131,
+ kAction203863200 = 203863200,
+ kAction222746496 = 222746496,
+ kActionBreakCeiling = 225056224,
+ kAction290410610 = 290410610,
+ kActionJumpDownCeiling = 338494260,
+
+ /////////////////////////////
+ // Verges
+ /////////////////////////////
+ kAction125233040 = 125233040, // Abbot
+ kAction125499160 = 125499160,
+ kAction155853632 = 155853632,
+ kAction158617345 = 158617345,
+ kAction167854368 = 167854368,
+ kAction168187490 = 168187490,
+ kAction168255788 = 168255788,
+ kActionDeliverMessageToTyler = 191337656,
+ kAction202558662 = 202558662,
+
+ /////////////////////////////
+ // Vassili
+ /////////////////////////////
+ kAction122732000 = 122732000,
+ kAction168459827 = 168459827,
+ kAction191477936 = 191477936,
+
+ /////////////////////////////
+ // Vesna
+ /////////////////////////////
+ kAction124190740 = 124190740,
+ kAction134427424 = 134427424,
+ kAction135024800 = 135024800,
+ kAction137165825 = 137165825,
+ kAction155913424 = 155913424,
+ kAction190412928 = 190412928,
+ kAction203663744 = 203663744,
+ kAction204832737 = 204832737,
+
+ /////////////////////////////
+ // Misc
+ /////////////////////////////
+ kAction158610240 = 158610240,
+ kAction167992577 = 167992577,
+ kAction168646401 = 168646401,
+ kAction169300225 = 169300225,
+ kAction169773228 = 169773228,
+ kActionEndChapter = 190346110,
+ kAction191001984 = 191001984,
+ kAction192637492 = 192637492,
+ kAction201959744 = 201959744,
+ kAction202621266 = 202621266,
+ kAction202884544 = 202884544,
+ kAction203078272 = 203078272,
+ kAction205034665 = 205034665,
+ kAction205294778 = 205294778,
+ kActionUseWhistle = 270751616,
+ kAction272177921 = 272177921,
+ kAction224309120 = 224309120,
+ kAction225358684 = 225358684,
+ kAction225367984 = 225367984,
+ kAction226078300 = 226078300, // Whistle
+
+ kActionEnd
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Functors classes used by the engine
+//////////////////////////////////////////////////////////////////////////
+
+// FIXME is this achievable with the existing Functor1Mem function
+template<class Arg, class Res, class T>
+class Functor1MemConst : public Common::Functor1<Arg, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg) const;
+
+ Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg v1) const {
+ return (_t->*_func)(v1);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+// FIXME move this to existing func.h file
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Result>
+struct QuaternaryFunction {
+ typedef Arg1 FirstArgumentType;
+ typedef Arg2 SecondArgumentType;
+ typedef Arg3 ThirdArgumentType;
+ typedef Arg4 FourthArgumentType;
+ typedef Result ResultType;
+};
+
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Res>
+struct Functor4 : public QuaternaryFunction<Arg1, Arg2, Arg3, Arg4, Res> {
+ virtual ~Functor4() {}
+
+ virtual bool isValid() const = 0;
+ virtual Res operator()(Arg1, Arg2, Arg3, Arg4) const = 0;
+};
+
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Res, class T>
+class Functor4Mem : public Functor4<Arg1, Arg2, Arg3, Arg4, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg1, Arg2, Arg3, Arg4);
+
+ Functor4Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg1 v1, Arg2 v2, Arg3 v3, Arg4 v4) const {
+ return (_t->*_func)(v1, v2, v3, v4);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SHARED_H