aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--common/debug.cpp12
-rw-r--r--common/debug.h21
-rw-r--r--devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj6
-rw-r--r--engines/director/dib.cpp2
-rw-r--r--engines/director/director.cpp7
-rw-r--r--engines/director/lingo/lingo-builtins.cpp119
-rw-r--r--engines/director/lingo/lingo-code.cpp101
-rw-r--r--engines/director/lingo/lingo-codegen.cpp68
-rw-r--r--engines/director/lingo/lingo-gr.cpp985
-rw-r--r--engines/director/lingo/lingo-gr.y17
-rw-r--r--engines/director/lingo/lingo-lex.cpp499
-rw-r--r--engines/director/lingo/lingo-lex.l8
-rw-r--r--engines/director/lingo/lingo-the.cpp64
-rw-r--r--engines/director/lingo/lingo-the.h16
-rw-r--r--engines/director/lingo/lingo.cpp56
-rw-r--r--engines/director/lingo/lingo.h42
-rw-r--r--engines/director/lingo/tests/math.lingo2
-rw-r--r--engines/director/score.cpp9
-rw-r--r--engines/director/sound.cpp6
-rw-r--r--engines/director/sound.h1
-rw-r--r--engines/fullpipe/fullpipe.h2
-rw-r--r--engines/fullpipe/gfx.cpp14
-rw-r--r--engines/fullpipe/statics.cpp2
-rw-r--r--engines/mohawk/riven_sound.cpp2
-rw-r--r--engines/sci/console.cpp3
-rw-r--r--engines/sci/detection.cpp4
-rw-r--r--engines/sci/detection_tables.h15
-rw-r--r--engines/sci/engine/kernel.h26
-rw-r--r--engines/sci/engine/kernel_tables.h68
-rw-r--r--engines/sci/engine/kevent.cpp71
-rw-r--r--engines/sci/engine/kfile.cpp38
-rw-r--r--engines/sci/engine/kgraphics.cpp13
-rw-r--r--engines/sci/engine/kgraphics32.cpp112
-rw-r--r--engines/sci/engine/kmisc.cpp80
-rw-r--r--engines/sci/engine/kvideo.cpp89
-rw-r--r--engines/sci/engine/savegame.cpp24
-rw-r--r--engines/sci/engine/savegame.h2
-rw-r--r--engines/sci/engine/seg_manager.h3
-rw-r--r--engines/sci/engine/state.cpp3
-rw-r--r--engines/sci/engine/state.h1
-rw-r--r--engines/sci/engine/workarounds.cpp3
-rw-r--r--engines/sci/event.cpp9
-rw-r--r--engines/sci/graphics/cache.cpp10
-rw-r--r--engines/sci/graphics/celobj32.cpp149
-rw-r--r--engines/sci/graphics/celobj32.h9
-rw-r--r--engines/sci/graphics/compare.cpp2
-rw-r--r--engines/sci/graphics/compare.h4
-rw-r--r--engines/sci/graphics/coordadjuster.cpp52
-rw-r--r--engines/sci/graphics/coordadjuster.h48
-rw-r--r--engines/sci/graphics/cursor.cpp2
-rw-r--r--engines/sci/graphics/cursor.h4
-rw-r--r--engines/sci/graphics/cursor32.cpp396
-rw-r--r--engines/sci/graphics/cursor32.h250
-rw-r--r--engines/sci/graphics/frameout.cpp114
-rw-r--r--engines/sci/graphics/frameout.h32
-rw-r--r--engines/sci/graphics/helpers.h6
-rw-r--r--engines/sci/graphics/paint16.cpp2
-rw-r--r--engines/sci/graphics/paint16.h4
-rw-r--r--engines/sci/graphics/picture.cpp2
-rw-r--r--engines/sci/graphics/picture.h6
-rw-r--r--engines/sci/graphics/screen.cpp27
-rw-r--r--engines/sci/graphics/screen_item32.h1
-rw-r--r--engines/sci/graphics/text16.cpp2
-rw-r--r--engines/sci/graphics/transitions32.cpp4
-rw-r--r--engines/sci/graphics/video32.cpp29
-rw-r--r--engines/sci/graphics/video32.h20
-rw-r--r--engines/sci/graphics/view.cpp12
-rw-r--r--engines/sci/graphics/view.h2
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/resource.cpp55
-rw-r--r--engines/sci/resource.h27
-rw-r--r--engines/sci/resource_audio.cpp38
-rw-r--r--engines/sci/resource_intern.h5
-rw-r--r--engines/sci/sci.cpp63
-rw-r--r--engines/sci/sci.h24
-rw-r--r--engines/sci/sound/audio32.cpp164
-rw-r--r--engines/sci/sound/audio32.h24
-rw-r--r--engines/sci/sound/decoders/sol.cpp19
-rw-r--r--engines/sci/video/robot_decoder.cpp1807
-rw-r--r--engines/sci/video/robot_decoder.h1454
-rw-r--r--engines/titanic/carry/bowl_ear.cpp31
-rw-r--r--engines/titanic/carry/bowl_ear.h5
-rw-r--r--engines/titanic/carry/carry_parrot.cpp2
-rw-r--r--engines/titanic/carry/central_core.cpp56
-rw-r--r--engines/titanic/carry/central_core.h4
-rw-r--r--engines/titanic/carry/chicken.h2
-rw-r--r--engines/titanic/carry/crushed_tv.cpp1
-rw-r--r--engines/titanic/carry/ear.cpp27
-rw-r--r--engines/titanic/carry/ear.h3
-rw-r--r--engines/titanic/carry/head_piece.cpp50
-rw-r--r--engines/titanic/carry/head_piece.h9
-rw-r--r--engines/titanic/carry/hose.h4
-rw-r--r--engines/titanic/carry/phonograph_ear.cpp26
-rw-r--r--engines/titanic/carry/phonograph_ear.h8
-rw-r--r--engines/titanic/core/click_responder.cpp21
-rw-r--r--engines/titanic/core/click_responder.h4
-rw-r--r--engines/titanic/core/game_object.cpp7
-rw-r--r--engines/titanic/core/game_object.h4
-rw-r--r--engines/titanic/core/named_item.cpp6
-rw-r--r--engines/titanic/core/named_item.h4
-rw-r--r--engines/titanic/core/saveable_object.cpp10
-rw-r--r--engines/titanic/core/tree_item.h5
-rw-r--r--engines/titanic/game/belbot_get_light.cpp39
-rw-r--r--engines/titanic/game/belbot_get_light.h5
-rw-r--r--engines/titanic/game/bilge_succubus.cpp51
-rw-r--r--engines/titanic/game/bomb.cpp303
-rw-r--r--engines/titanic/game/bomb.h15
-rw-r--r--engines/titanic/game/bottom_of_well_monitor.cpp72
-rw-r--r--engines/titanic/game/bottom_of_well_monitor.h9
-rw-r--r--engines/titanic/game/bowl_unlocker.cpp43
-rw-r--r--engines/titanic/game/bowl_unlocker.h9
-rw-r--r--engines/titanic/game/brain_slot.cpp115
-rw-r--r--engines/titanic/game/brain_slot.h10
-rw-r--r--engines/titanic/game/bridge_door.cpp25
-rw-r--r--engines/titanic/game/bridge_door.h4
-rw-r--r--engines/titanic/game/bridge_view.cpp80
-rw-r--r--engines/titanic/game/bridge_view.h7
-rw-r--r--engines/titanic/game/broken_pell_base.cpp2
-rw-r--r--engines/titanic/game/broken_pell_base.h4
-rw-r--r--engines/titanic/game/broken_pellerator.cpp107
-rw-r--r--engines/titanic/game/broken_pellerator.h5
-rw-r--r--engines/titanic/game/broken_pellerator_froz.cpp103
-rw-r--r--engines/titanic/game/broken_pellerator_froz.h5
-rw-r--r--engines/titanic/game/cage.cpp70
-rw-r--r--engines/titanic/game/cage.h8
-rw-r--r--engines/titanic/game/captains_wheel.cpp153
-rw-r--r--engines/titanic/game/captains_wheel.h7
-rw-r--r--engines/titanic/game/cell_point_button.cpp34
-rw-r--r--engines/titanic/game/cell_point_button.h7
-rw-r--r--engines/titanic/game/chev_code.cpp251
-rw-r--r--engines/titanic/game/chev_code.h15
-rw-r--r--engines/titanic/game/chev_panel.cpp88
-rw-r--r--engines/titanic/game/chev_panel.h16
-rw-r--r--engines/titanic/game/chicken_cooler.cpp33
-rw-r--r--engines/titanic/game/chicken_cooler.h2
-rw-r--r--engines/titanic/game/chicken_dispensor.cpp141
-rw-r--r--engines/titanic/game/chicken_dispensor.h8
-rw-r--r--engines/titanic/game/close_broken_pel.cpp14
-rw-r--r--engines/titanic/game/close_broken_pel.h4
-rw-r--r--engines/titanic/game/cookie.cpp17
-rw-r--r--engines/titanic/game/cookie.h3
-rw-r--r--engines/titanic/game/credits.cpp35
-rw-r--r--engines/titanic/game/credits.h3
-rw-r--r--engines/titanic/game/credits_button.cpp20
-rw-r--r--engines/titanic/game/credits_button.h3
-rw-r--r--engines/titanic/game/desk_click_responder.cpp29
-rw-r--r--engines/titanic/game/desk_click_responder.h5
-rw-r--r--engines/titanic/game/null_port_hole.cpp12
-rw-r--r--engines/titanic/game/parrot/parrot_succubus.cpp49
-rw-r--r--engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp32
-rw-r--r--engines/titanic/game/placeholder/bar_shelf_vis_centre.h8
-rw-r--r--engines/titanic/game/sgt/basin.cpp41
-rw-r--r--engines/titanic/game/sgt/basin.h4
-rw-r--r--engines/titanic/game/sgt/bedfoot.cpp92
-rw-r--r--engines/titanic/game/sgt/bedfoot.h3
-rw-r--r--engines/titanic/game/sgt/bedhead.cpp15
-rw-r--r--engines/titanic/game/sgt/bedhead.h3
-rw-r--r--engines/titanic/game/sgt/chest_of_drawers.cpp43
-rw-r--r--engines/titanic/game/sgt/chest_of_drawers.h4
-rw-r--r--engines/titanic/game/sgt/desk.cpp46
-rw-r--r--engines/titanic/game/sgt/desk.h4
-rw-r--r--engines/titanic/game/sgt/deskchair.cpp52
-rw-r--r--engines/titanic/game/sgt/deskchair.h5
-rw-r--r--engines/titanic/game/sgt/sgt_state_room.cpp79
-rw-r--r--engines/titanic/game/sgt/sgt_state_room.h9
-rw-r--r--engines/titanic/game/sgt/sgt_upper_doors_sound.cpp6
-rw-r--r--engines/titanic/game/transport/pellerator.cpp324
-rw-r--r--engines/titanic/game/transport/pellerator.h10
-rw-r--r--engines/titanic/gfx/changes_season_button.cpp11
-rw-r--r--engines/titanic/gfx/changes_season_button.h2
-rw-r--r--engines/titanic/gfx/chev_switch.cpp40
-rw-r--r--engines/titanic/gfx/chev_switch.h6
-rw-r--r--engines/titanic/gfx/toggle_switch.h2
-rw-r--r--engines/titanic/messages/bilge_dispensor_event.cpp35
-rw-r--r--engines/titanic/messages/bilge_dispensor_event.h5
-rw-r--r--engines/titanic/messages/door_auto_sound_event.cpp18
-rw-r--r--engines/titanic/messages/door_auto_sound_event.h4
-rw-r--r--engines/titanic/messages/messages.h42
-rw-r--r--engines/titanic/messages/mouse_messages.h26
-rw-r--r--engines/titanic/module.mk6
-rw-r--r--engines/titanic/moves/call_pellerator.cpp (renamed from engines/titanic/game/call_pellerator.cpp)47
-rw-r--r--engines/titanic/moves/call_pellerator.h (renamed from engines/titanic/game/call_pellerator.h)6
-rw-r--r--engines/titanic/moves/exit_pellerator.cpp86
-rw-r--r--engines/titanic/moves/exit_pellerator.h6
-rw-r--r--engines/titanic/npcs/bilge_succubus.cpp467
-rw-r--r--engines/titanic/npcs/bilge_succubus.h (renamed from engines/titanic/game/bilge_succubus.h)20
-rw-r--r--engines/titanic/npcs/liftbot.cpp3
-rw-r--r--engines/titanic/npcs/parrot_succubus.cpp152
-rw-r--r--engines/titanic/npcs/parrot_succubus.h (renamed from engines/titanic/game/parrot/parrot_succubus.h)6
-rw-r--r--engines/titanic/npcs/succubus.cpp32
-rw-r--r--engines/titanic/npcs/succubus.h10
-rw-r--r--engines/titanic/support/string.cpp16
-rw-r--r--engines/titanic/support/string.h5
194 files changed, 9690 insertions, 1970 deletions
diff --git a/NEWS b/NEWS
index df8ee4b0a9..8f1800b912 100644
--- a/NEWS
+++ b/NEWS
@@ -62,6 +62,9 @@ For a more comprehensive changelog of the latest experimental code, see:
- Fixed taskbar support on Windows 10 onwards.
- Fixed keymapping for non-QWERTY keyboards.
+ Linux port:
+ - Added basic support for the snap packaging system.
+
1.8.1 (2016-05-25)
New ports:
- Added Nintendo 3DS port.
diff --git a/common/debug.cpp b/common/debug.cpp
index ce34a00445..c61fc63dea 100644
--- a/common/debug.cpp
+++ b/common/debug.cpp
@@ -120,6 +120,18 @@ bool DebugManager::isDebugChannelEnabled(uint32 channel) {
} // End of namespace Common
+bool debugLevelSet(int level) {
+ return level <= gDebugLevel;
+}
+
+bool debugChannelSet(int level, uint32 debugChannels) {
+ if (gDebugLevel != 11)
+ if (level > gDebugLevel || !(DebugMan.isDebugChannelEnabled(debugChannels)))
+ return false;
+
+ return true;
+}
+
#ifndef DISABLE_TEXT_CONSOLE
diff --git a/common/debug.h b/common/debug.h
index 00bad81fa6..883a0bf29d 100644
--- a/common/debug.h
+++ b/common/debug.h
@@ -31,11 +31,10 @@ inline void debug(const char *s, ...) {}
inline void debug(int level, const char *s, ...) {}
inline void debugN(const char *s, ...) {}
inline void debugN(int level, const char *s, ...) {}
-inline void debugC(int level, uint32 engineChannel, const char *s, ...) {}
-inline void debugC(uint32 engineChannel, const char *s, ...) {}
-inline void debugCN(int level, uint32 engineChannel, const char *s, ...) {}
-inline void debugCN(uint32 engineChannel, const char *s, ...) {}
-
+inline void debugC(int level, uint32 debugChannels, const char *s, ...) {}
+inline void debugC(uint32 debugChannels, const char *s, ...) {}
+inline void debugCN(int level, uint32 debugChannels, const char *s, ...) {}
+inline void debugCN(uint32 debugChannels, const char *s, ...) {}
#else
@@ -111,6 +110,18 @@ void debugCN(uint32 debugChannels, const char *s, ...) GCC_PRINTF(2, 3);
#endif
/**
+ * Returns true if the debug level is set to the specified level
+ */
+bool debugLevelSet(int level);
+
+/**
+ * Returns true if the debug level and channel are active
+ *
+ * @see enableDebugChannel
+ */
+bool debugChannelSet(int level, uint32 debugChannels);
+
+/**
* The debug level. Initially set to -1, indicating that no debug output
* should be shown. Positive values usually imply an increasing number of
* debug output shall be generated, the higher the value, the more verbose the
diff --git a/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj b/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj
index 4f06a5e469..55266a875f 100644
--- a/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj
+++ b/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 076583601D660492006CBB9B /* cmake.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0765835E1D660492006CBB9B /* cmake.cpp */; };
F9A66C691396D4DF00CEE494 /* codeblocks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C5F1396D4DF00CEE494 /* codeblocks.cpp */; };
F9A66C6A1396D4DF00CEE494 /* create_project.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C621396D4DF00CEE494 /* create_project.cpp */; };
F9A66C6B1396D4DF00CEE494 /* msbuild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C651396D4DF00CEE494 /* msbuild.cpp */; };
@@ -41,6 +42,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 0765835E1D660492006CBB9B /* cmake.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmake.cpp; path = ../cmake.cpp; sourceTree = "<group>"; };
+ 0765835F1D660492006CBB9B /* cmake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cmake.h; path = ../cmake.h; sourceTree = "<group>"; };
F9A66C271396D36100CEE494 /* create_project */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = create_project; sourceTree = BUILT_PRODUCTS_DIR; };
F9A66C491396D47500CEE494 /* installer.vbs */ = {isa = PBXFileReference; lastKnownFileType = text; name = installer.vbs; path = ../scripts/installer.vbs; sourceTree = "<group>"; };
F9A66C4A1396D47500CEE494 /* postbuild.cmd */ = {isa = PBXFileReference; lastKnownFileType = text; name = postbuild.cmd; path = ../scripts/postbuild.cmd; sourceTree = "<group>"; };
@@ -76,6 +79,8 @@
F9A66C1C1396D36100CEE494 = {
isa = PBXGroup;
children = (
+ 0765835E1D660492006CBB9B /* cmake.cpp */,
+ 0765835F1D660492006CBB9B /* cmake.h */,
F9A66C861396E2F500CEE494 /* xcode.cpp */,
F9A66C841396E2D800CEE494 /* xcode.h */,
F9A66C6D1396D4E800CEE494 /* visualstudio.cpp */,
@@ -169,6 +174,7 @@
F9A66C6A1396D4DF00CEE494 /* create_project.cpp in Sources */,
F9A66C6B1396D4DF00CEE494 /* msbuild.cpp in Sources */,
F9A66C6C1396D4DF00CEE494 /* msvc.cpp in Sources */,
+ 076583601D660492006CBB9B /* cmake.cpp in Sources */,
F9A66C6F1396D4E800CEE494 /* visualstudio.cpp in Sources */,
F9A66C871396E2F500CEE494 /* xcode.cpp in Sources */,
);
diff --git a/engines/director/dib.cpp b/engines/director/dib.cpp
index 8c54ba5363..04665e7d34 100644
--- a/engines/director/dib.cpp
+++ b/engines/director/dib.cpp
@@ -62,7 +62,7 @@ void DIBDecoder::loadPalette(Common::SeekableReadStream &stream) {
uint16 steps = stream.size() / 6;
uint16 index = (steps * 3) - 1;
_paletteColorCount = steps;
- _palette = new byte[index];
+ _palette = new byte[index + 1];
for (uint8 i = 0; i < steps; i++) {
_palette[index - 2] = stream.readByte();
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index 469aeb80cb..ddc5adccdd 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -24,6 +24,7 @@
#include "common/config-manager.h"
#include "common/debug.h"
+#include "common/debug-channels.h"
#include "common/scummsys.h"
#include "common/error.h"
#include "common/events.h"
@@ -49,6 +50,9 @@ namespace Director {
DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc),
_rnd("director") {
+ DebugMan.addDebugChannel(kDebugLingoExec, "lingoexec", "Lingo Execution");
+ DebugMan.addDebugChannel(kDebugLingoCompile, "lingocompile", "Lingo Compilation");
+
if (!_mixer->isReady())
error("Sound initialization failed");
@@ -137,7 +141,8 @@ Common::HashMap<Common::String, Score *> DirectorEngine::loadMMMNames(Common::St
Common::FSList movies;
Common::HashMap<Common::String, Score *> nameMap;
- directory.getChildren(movies, Common::FSNode::kListFilesOnly);
+ if (!directory.getChildren(movies, Common::FSNode::kListFilesOnly))
+ return nameMap;
if (!movies.empty()) {
for (Common::FSList::const_iterator i = movies.begin(); i != movies.end(); ++i) {
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index d3545a7d3b..6738d4b707 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -32,25 +32,29 @@ static struct BuiltinProto {
bool parens;
} builtins[] = {
// Math
- { "abs", Lingo::b_abs, 1, 1, true }, // D2
- { "atan", Lingo::b_atan, 1, 1, true }, // D4
- { "cos", Lingo::b_cos, 1, 1, true }, // D4
- { "exp", Lingo::b_exp, 1, 1, true }, // D4
- { "float", Lingo::b_float, 1, 1, true }, // D4
- { "integer",Lingo::b_integer, 1, 1, true },
- { "integerp",Lingo::b_integerp, 1, 1, true },
- { "log", Lingo::b_log, 1, 1, true }, // D4
- { "pi", Lingo::b_pi, 0, 0, true }, // D4
- { "power", Lingo::b_power, 2, 2, true }, // D4
- { "random", Lingo::b_random, 1, 1, true }, // D2
- { "sin", Lingo::b_sin, 1, 1, true },
- { "sqrt", Lingo::b_sqrt, 1, 1, true }, // D2
- { "tan", Lingo::b_tan, 1, 1, true }, // D4
+ { "abs", Lingo::b_abs, 1, 1, true }, // D2
+ { "atan", Lingo::b_atan, 1, 1, true }, // D4
+ { "cos", Lingo::b_cos, 1, 1, true }, // D4
+ { "exp", Lingo::b_exp, 1, 1, true }, // D4
+ { "float", Lingo::b_float, 1, 1, true }, // D4
+ { "integer", Lingo::b_integer, 1, 1, true },
+ { "integerp", Lingo::b_integerp, 1, 1, true },
+ { "log", Lingo::b_log, 1, 1, true }, // D4
+ { "pi", Lingo::b_pi, 0, 0, true }, // D4
+ { "power", Lingo::b_power, 2, 2, true }, // D4
+ { "random", Lingo::b_random, 1, 1, true }, // D2
+ { "sin", Lingo::b_sin, 1, 1, true },
+ { "sqrt", Lingo::b_sqrt, 1, 1, true }, // D2
+ { "tan", Lingo::b_tan, 1, 1, true }, // D4
// String
- { "chars", Lingo::b_chars, 3, 3, true }, // D2
- { "charToNum", Lingo::b_charToNum, 1, 1, true }, // D2
- { "length", Lingo::b_length, 1, 1, true }, // D2
- { "string", Lingo::b_string, 1, 1, true }, // D2
+ { "chars", Lingo::b_chars, 3, 3, true }, // D2
+ { "charToNum", Lingo::b_charToNum, 1, 1, true }, // D2
+ { "length", Lingo::b_length, 1, 1, true }, // D2
+ { "numToChar", Lingo::b_numToChar, 1, 1, true }, // D2
+ { "offset", Lingo::b_offset, 2, 2, true }, // D2
+ { "string", Lingo::b_string, 1, 1, true }, // D2
+ { "stringp", Lingo::b_stringp, 1, 1, true }, // D2
+ { "value", Lingo::b_value, 1, 1, true }, // D2
// Files
{ "closeDA", Lingo::b_closeDA, 0, 0, false }, // D2
{ "closeResFile", Lingo::b_closeResFile, 0, 1, false }, // D2
@@ -86,8 +90,10 @@ static struct BuiltinProto {
{ "ilk", Lingo::b_ilk, 1, 2, true }, // D4
// put // D2
// set // D2
+ { "objectp", Lingo::b_objectp, 1, 1, true },
{ "showGlobals", Lingo::b_showGlobals, 0, 0, false }, // D2
{ "showLocals", Lingo::b_showLocals, 0, 0, false }, // D2
+ { "symbolp", Lingo::b_symbolp, 1, 1, true }, // D2
// Score
{ "constrainH", Lingo::b_constrainH, 2, 2, true }, // D2
{ "constrainV", Lingo::b_constrainV, 2, 2, true }, // D2
@@ -95,12 +101,14 @@ static struct BuiltinProto {
// go // D2
{ "installMenu", Lingo::b_installMenu, 1, 1, false }, // D2
{ "label", Lingo::b_label, 1, 1, true }, // D2
+ { "marker", Lingo::b_marker, 1, 1, true }, // D2
{ "moveableSprite", Lingo::b_moveableSprite,0, 0, false }, // D2
{ "puppetPalette", Lingo::b_puppetPalette, -1,0, false }, // D2
{ "puppetSound", Lingo::b_puppetSound, -1,0, false }, // D2
{ "puppetSprite", Lingo::b_puppetSprite, -1,0, false }, // D2
{ "puppetTempo", Lingo::b_puppetTempo, 1, 1, false }, // D2
{ "puppetTransition",Lingo::b_puppetTransition,-1,0, false },// D2
+ { "rollOver", Lingo::b_rollOver, 1, 1, true }, // D2
{ "spriteBox", Lingo::b_spriteBox, -1,0, false }, // D2
{ "updateStage", Lingo::b_updateStage, 0, 0, false }, // D2
{ "zoomBox", Lingo::b_zoomBox, -1,0, false }, // D2
@@ -136,6 +144,8 @@ void Lingo::initBuiltIns() {
sym->u.bltin = blt->func;
_handlers[blt->name] = sym;
+
+ _functions[(void *)sym->u.s] = new FuncDesc(blt->name, "");
}
}
@@ -341,12 +351,48 @@ void Lingo::b_length(int nargs) {
g_lingo->push(d);
}
+void Lingo::b_numToChar(int nargs) {
+ Datum d = g_lingo->pop();
+
+ d.toInt();
+
+ g_lingo->push(Datum((char)d.u.i));
+}
+
+void Lingo::b_offset(int nargs) {
+ Datum target = g_lingo->pop();
+ Datum source = g_lingo->pop();
+
+ target.toString();
+ source.toString();
+
+ warning("STUB: b_offset()");
+
+ g_lingo->push(Datum(0));
+}
+
void Lingo::b_string(int nargs) {
Datum d = g_lingo->pop();
d.toString();
g_lingo->push(d);
}
+void Lingo::b_stringp(int nargs) {
+ Datum d = g_lingo->pop();
+ int res = (d.type == STRING) ? 1 : 0;
+ d.toInt();
+ d.u.i = res;
+ g_lingo->push(d);
+}
+
+void Lingo::b_value(int nargs) {
+ Datum d = g_lingo->pop();
+ d.toInt();
+ warning("STUB: b_value()");
+ g_lingo->push(d);
+}
+
+
///////////////////
// Files
///////////////////
@@ -499,7 +545,7 @@ void Lingo::b_alert(int nargs) {
d.toString();
- warning("STUB: b_alert");
+ warning("STUB: b_alert(%s)", d.u.s->c_str());
delete d.u.s;
}
@@ -510,6 +556,14 @@ void Lingo::b_cursor(int nargs) {
warning("STUB: b_cursor(%d)", d.u.i);
}
+void Lingo::b_objectp(int nargs) {
+ Datum d = g_lingo->pop();
+ int res = (d.type == OBJECT) ? 1 : 0;
+ d.toInt();
+ d.u.i = res;
+ g_lingo->push(d);
+}
+
void Lingo::b_showGlobals(int nargs) {
warning("STUB: b_showGlobals");
}
@@ -518,6 +572,13 @@ void Lingo::b_showLocals(int nargs) {
warning("STUB: b_showLocals");
}
+void Lingo::b_symbolp(int nargs) {
+ Datum d = g_lingo->pop();
+ int res = (d.type == SYMBOL) ? 1 : 0;
+ d.toInt();
+ d.u.i = res;
+ g_lingo->push(d);
+}
///////////////////
@@ -564,6 +625,14 @@ void Lingo::b_label(int nargs) {
g_lingo->push(Datum(0));
}
+void Lingo::b_marker(int nargs) {
+ Datum d = g_lingo->pop();
+ d.toInt();
+ warning("STUB: b_marker(%d)", d.u.i);
+
+ g_lingo->push(Datum(0));
+}
+
void Lingo::b_moveableSprite(int nargs) {
Datum d = g_lingo->pop();
warning("STUB: b_moveableSprite(%d)", d.u.i);
@@ -602,6 +671,13 @@ void Lingo::b_puppetTransition(int nargs) {
g_lingo->dropStack(nargs);
}
+void Lingo::b_rollOver(int nargs) {
+ Datum d = g_lingo->pop();
+ warning("STUB: b_puppetTempo(%d)", d.u.i);
+
+ g_lingo->push(Datum(0));
+}
+
void Lingo::b_spriteBox(int nargs) {
g_lingo->printStubWithArglist("b_spriteBox", nargs);
@@ -720,10 +796,11 @@ void Lingo::factoryCall(Common::String &name, int nargs) {
s = name + "-" + *method.u.s;
+ debugC(3, kDebugLingoExec, "Stack size before call: %d", _stack.size());
call(s, nargs);
+ debugC(3, kDebugLingoExec, "Stack size after call: %d", _stack.size());
- if (method.u.s->compareToIgnoreCase("mNew")) {
- warning("Got mNew method");
+ if (!method.u.s->compareToIgnoreCase("mNew")) {
Datum d;
d.type = OBJECT;
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 6072977d07..8712f0990c 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -51,6 +51,69 @@
namespace Director {
+static struct FuncDescr {
+ const inst func;
+ const char *name;
+ const char *args;
+} funcDescr[] = {
+ { 0, "STOP", "" },
+ { Lingo::c_xpop, "c_xpop", "" },
+ { Lingo::c_printtop, "c_printtop", "" },
+ { Lingo::c_constpush, "c_constpush", "i" },
+ { Lingo::c_voidpush, "c_voidpush", "" },
+ { Lingo::c_fconstpush, "c_fconstpush", "f" },
+ { Lingo::c_stringpush, "c_stringpush", "s" },
+ { Lingo::c_varpush, "c_varpush", "s" },
+ { Lingo::c_assign, "c_assign", "" },
+ { Lingo::c_eval, "c_eval", "s" },
+ { Lingo::c_theentitypush,"c_theentitypush","ii" }, // entity, field
+ { Lingo::c_theentityassign,"c_theentityassign","ii" },
+ { Lingo::c_swap, "c_swap", "" },
+ { Lingo::c_add, "c_add", "" },
+ { Lingo::c_sub, "c_sub", "" },
+ { Lingo::c_mul, "c_mul", "" },
+ { Lingo::c_div, "c_div", "" },
+ { Lingo::c_negate, "c_negate", "" },
+ { Lingo::c_ampersand, "c_ampersand", "" },
+ { Lingo::c_concat, "c_concat", "" },
+ { Lingo::c_contains, "c_contains", "" },
+ { Lingo::c_starts, "c_starts", "" },
+ { Lingo::c_intersects, "c_intersects", "" },
+ { Lingo::c_within, "c_within", "" },
+ { Lingo::c_and, "c_and", "" },
+ { Lingo::c_or, "c_or", "" },
+ { Lingo::c_not, "c_not", "" },
+ { Lingo::c_eq, "c_eq", "" },
+ { Lingo::c_neq, "c_neq", "" },
+ { Lingo::c_gt, "c_gt", "" },
+ { Lingo::c_lt, "c_lt", "" },
+ { Lingo::c_ge, "c_ge", "" },
+ { Lingo::c_le, "c_le", "" },
+ { Lingo::c_repeatwhilecode,"c_repeatwhilecode","oo" },
+ { Lingo::c_repeatwithcode,"c_repeatwithcode","ooooos" },
+ { Lingo::c_ifcode, "c_ifcode", "oooi" },
+ { Lingo::c_whencode, "c_whencode", "os" },
+ { Lingo::c_goto, "c_goto", "" },
+ { Lingo::c_gotoloop, "c_gotoloop", "" },
+ { Lingo::c_gotonext, "c_gotonext", "" },
+ { Lingo::c_gotoprevious,"c_gotoprevious","" },
+ { Lingo::c_play, "c_play", "" },
+ { Lingo::c_playdone, "c_playdone", "" },
+ { Lingo::c_call, "c_call", "si" },
+ { Lingo::c_procret, "c_procret", "" },
+ { Lingo::c_global, "c_global", "s" },
+ { Lingo::c_open, "c_open", "" },
+ { 0, 0, 0 }
+};
+
+void Lingo::initFuncs() {
+ Symbol sym;
+ for (FuncDescr *fnc = funcDescr; fnc->name; fnc++) {
+ sym.u.func = fnc->func;
+ _functions[(void *)sym.u.s] = new FuncDesc(fnc->name, fnc->args);
+ }
+}
+
void Lingo::push(Datum d) {
_stack.push_back(d);
}
@@ -108,6 +171,9 @@ void Lingo::c_printtop(void) {
case SYMBOL:
warning("%s", d.type2str(true));
break;
+ case OBJECT:
+ warning("#%s", d.u.s->c_str());
+ break;
default:
warning("--unknown--");
}
@@ -201,8 +267,11 @@ void Lingo::c_assign() {
delete d2.u.arr;
} else if (d2.type == SYMBOL) {
d1.u.sym->u.i = d2.u.i;
+ } else if (d2.type == OBJECT) {
+ d1.u.sym->u.s = d2.u.s;
} else {
warning("c_assign: unhandled type: %s", d2.type2str());
+ d1.u.sym->u.s = d2.u.s;
}
d1.u.sym->type = d2.type;
@@ -633,24 +702,38 @@ void Lingo::c_ifcode() {
int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]);
int skipEnd = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]);
- debug(8, "executing cond (have to %s end)", skipEnd ? "skip" : "execute");
+ debugC(8, kDebugLingoExec, "executing cond (have to %s end)", skipEnd ? "skip" : "execute");
g_lingo->execute(savepc + 4); /* condition */
d = g_lingo->pop();
if (d.toInt()) {
- debug(8, "executing then");
+ debugC(8, kDebugLingoExec, "executing then");
g_lingo->execute(then);
} else if (elsep) { /* else part? */
- debug(8, "executing else");
+ debugC(8, kDebugLingoExec, "executing else");
g_lingo->execute(elsep);
}
if (!g_lingo->_returning && !skipEnd) {
g_lingo->_pc = end; /* next stmt */
- debug(8, "executing end");
- } else
- debug(8, "Skipped end");
+ debugC(8, kDebugLingoExec, "executing end");
+ } else {
+ debugC(8, kDebugLingoExec, "Skipped end");
+ }
+}
+
+void Lingo::c_whencode() {
+ Datum d;
+ int start = g_lingo->_pc;
+ int end = READ_UINT32(&(*g_lingo->_currentScript)[start]);
+ Common::String eventname((char *)&(*g_lingo->_currentScript)[start]);
+
+ start += g_lingo->calcStringAlignment(eventname.c_str());
+
+ warning("STUB: c_whencode([%5d][%5d], %s)", start, end, eventname.c_str());
+
+ g_lingo->_pc = end;
}
//************************
@@ -727,7 +810,7 @@ void Lingo::call(Common::String &name, int nargs) {
if (!g_lingo->_handlers.contains(name)) {
Symbol *s = g_lingo->lookupVar(name.c_str(), false);
if (s && s->type == OBJECT) {
- debug(3, "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str());
+ debugC(3, kDebugLingoExec, "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str());
name = *s->u.s;
}
}
@@ -781,6 +864,7 @@ void Lingo::call(Common::String &name, int nargs) {
g_lingo->push(d);
}
+ debugC(5, kDebugLingoExec, "Pushing frame %d", g_lingo->_callstack.size() + 1);
CFrame *fp = new CFrame;
fp->sp = sym;
@@ -806,7 +890,10 @@ void Lingo::c_procret() {
return;
}
+ debugC(5, kDebugLingoExec, "Popping frame %d", g_lingo->_callstack.size() + 1);
+
CFrame *fp = g_lingo->_callstack.back();
+ g_lingo->_callstack.pop_back();
g_lingo->_currentScript = fp->retscript;
g_lingo->_pc = fp->retpc;
diff --git a/engines/director/lingo/lingo-codegen.cpp b/engines/director/lingo/lingo-codegen.cpp
index bf96802bb7..32ddea47ac 100644
--- a/engines/director/lingo/lingo-codegen.cpp
+++ b/engines/director/lingo/lingo-codegen.cpp
@@ -53,17 +53,77 @@ namespace Director {
void Lingo::execute(int pc) {
for(_pc = pc; (*_currentScript)[_pc] != STOP && !_returning;) {
+ Common::String instr = decodeInstruction(_pc);
+
+ debugC(1, kDebugLingoExec, "[%3d]: %s", _pc, instr.c_str());
+
+ Common::String stack("Stack: ");
for (uint i = 0; i < _stack.size(); i++) {
- debugN(5, "%d ", _stack[i].u.i);
+ Datum d = _stack[i];
+ d.toString();
+ stack += Common::String::format("<%s> ", d.u.s->c_str());
}
- debug(5, "%s", "");
+ debugC(5, kDebugLingoExec, "%s", stack.c_str());
_pc++;
(*((*_currentScript)[_pc - 1]))();
}
}
+Common::String Lingo::decodeInstruction(int pc, int *newPc) {
+ Symbol sym;
+ Common::String res;
+
+ sym.u.func = (*_currentScript)[pc++];
+ if (_functions.contains((void *)sym.u.s)) {
+ res = _functions[(void *)sym.u.s]->name;
+ const char *pars = _functions[(void *)sym.u.s]->proto;
+ inst i;
+
+ while (*pars) {
+ switch (*pars++) {
+ case 'i':
+ {
+ i = (*_currentScript)[pc++];
+ int v = READ_UINT32(&i);
+
+ res += Common::String::format(" %d", v);
+ break;
+ }
+ case 'o':
+ {
+ i = (*_currentScript)[pc++];
+ int v = READ_UINT32(&i);
+
+ res += Common::String::format(" [%5d]", v);
+ break;
+ }
+ case 's':
+ {
+ char *s = (char *)&(*_currentScript)[pc];
+ pc += calcStringAlignment(s);
+
+ res += Common::String::format(" \"%s\"", s);
+ break;
+ }
+ default:
+ warning("decodeInstruction: Unknown parameter type: %c", pars[-1]);
+ }
+
+ if (*pars)
+ res += ',';
+ }
+ } else {
+ res = "<unknown>";
+ }
+
+ if (newPc)
+ *newPc = pc;
+
+ return res;
+}
+
Symbol *Lingo::lookupVar(const char *name, bool create, bool putInGlobalList) {
Symbol *sym;
@@ -129,7 +189,7 @@ void Lingo::define(Common::String &name, int start, int nargs, Common::String *p
if (prefix)
name = *prefix + "-" + name;
- debug(3, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs);
+ debugC(3, kDebugLingoCompile, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs);
if (!_handlers.contains(name)) { // Create variable if it was not defined
sym = new Symbol;
@@ -221,7 +281,7 @@ int Lingo::codeFunc(Common::String *s, int numpar) {
if (s->equalsIgnoreCase("me")) {
if (!g_lingo->_currentFactory.empty()) {
g_lingo->codeString(g_lingo->_currentFactory.c_str());
- debug(2, "Repaced 'me' with %s", g_lingo->_currentFactory.c_str());
+ debugC(2, kDebugLingoCompile, "Replaced 'me' with %s", g_lingo->_currentFactory.c_str());
} else {
warning("'me' out of factory method");
g_lingo->codeString(s->c_str());
diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp
index 63128058ed..be5b3eb571 100644
--- a/engines/director/lingo/lingo-gr.cpp
+++ b/engines/director/lingo/lingo-gr.cpp
@@ -486,18 +486,18 @@ union yyalloc
#endif
/* YYFINAL -- State number of the termination state. */
-#define YYFINAL 87
+#define YYFINAL 88
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 921
+#define YYLAST 931
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 83
/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 35
+#define YYNNTS 36
/* YYNRULES -- Number of rules. */
-#define YYNRULES 124
+#define YYNRULES 125
/* YYNRULES -- Number of states. */
-#define YYNSTATES 259
+#define YYNSTATES 261
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
@@ -551,69 +551,69 @@ static const yytype_uint16 yyprhs[] =
{
0, 0, 3, 7, 9, 12, 14, 15, 17, 19,
21, 23, 25, 30, 35, 40, 46, 51, 56, 62,
- 64, 66, 68, 70, 79, 91, 104, 109, 118, 130,
- 142, 149, 160, 171, 172, 176, 179, 181, 184, 186,
- 193, 195, 201, 203, 207, 211, 214, 218, 220, 222,
- 223, 224, 225, 228, 231, 233, 235, 237, 239, 244,
- 246, 248, 251, 253, 257, 261, 265, 269, 273, 277,
- 281, 285, 289, 293, 297, 300, 304, 308, 312, 316,
- 319, 322, 326, 331, 336, 339, 341, 343, 345, 348,
- 351, 354, 356, 359, 364, 367, 369, 373, 376, 379,
- 382, 385, 389, 392, 395, 397, 401, 404, 407, 410,
- 414, 417, 418, 427, 430, 431, 440, 441, 443, 447,
- 452, 453, 457, 458, 460
+ 64, 66, 68, 70, 79, 91, 104, 108, 117, 129,
+ 141, 148, 159, 170, 171, 175, 178, 180, 183, 185,
+ 192, 194, 200, 202, 206, 210, 213, 217, 219, 221,
+ 222, 223, 224, 227, 230, 234, 236, 238, 240, 242,
+ 247, 249, 251, 254, 256, 260, 264, 268, 272, 276,
+ 280, 284, 288, 292, 296, 300, 303, 307, 311, 315,
+ 319, 322, 325, 329, 334, 339, 342, 344, 346, 348,
+ 351, 354, 357, 359, 362, 367, 370, 372, 376, 379,
+ 382, 385, 388, 392, 395, 398, 400, 404, 407, 410,
+ 413, 417, 420, 421, 430, 433, 434, 443, 444, 446,
+ 450, 455, 456, 460, 461, 463
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
84, 0, -1, 84, 85, 86, -1, 86, -1, 1,
- 85, -1, 76, -1, -1, 111, -1, 105, -1, 116,
- -1, 87, -1, 89, -1, 40, 104, 33, 21, -1,
- 42, 21, 70, 104, -1, 42, 13, 70, 104, -1,
- 42, 14, 104, 70, 104, -1, 42, 21, 44, 104,
- -1, 42, 13, 44, 104, -1, 42, 14, 104, 44,
- 104, -1, 104, -1, 105, -1, 88, -1, 90, -1,
+ 85, -1, 76, -1, -1, 112, -1, 106, -1, 117,
+ -1, 87, -1, 89, -1, 40, 105, 33, 21, -1,
+ 42, 21, 70, 105, -1, 42, 13, 70, 105, -1,
+ 42, 14, 105, 70, 105, -1, 42, 21, 44, 105,
+ -1, 42, 13, 44, 105, -1, 42, 14, 105, 44,
+ 105, -1, 105, -1, 106, -1, 88, -1, 90, -1,
97, 77, 96, 78, 103, 102, 27, 41, -1, 98,
- 70, 104, 102, 44, 104, 102, 103, 102, 27, 41,
- -1, 98, 70, 104, 102, 24, 44, 104, 102, 103,
- 102, 27, 41, -1, 45, 21, 43, 104, -1, 99,
- 96, 43, 85, 103, 102, 27, 32, -1, 99, 96,
- 43, 85, 103, 102, 48, 103, 102, 27, 32, -1,
- 99, 96, 43, 85, 103, 102, 101, 92, 102, 27,
- 32, -1, 99, 96, 43, 101, 88, 102, -1, 99,
- 96, 43, 101, 88, 102, 48, 101, 88, 102, -1,
- 99, 96, 43, 101, 88, 102, 93, 102, 91, 102,
- -1, -1, 48, 101, 88, -1, 92, 95, -1, 95,
- -1, 93, 94, -1, 94, -1, 100, 96, 43, 101,
- 89, 102, -1, 93, -1, 100, 96, 43, 103, 102,
- -1, 104, -1, 104, 70, 104, -1, 77, 96, 78,
- -1, 41, 47, -1, 41, 46, 21, -1, 32, -1,
- 26, -1, -1, -1, -1, 103, 85, -1, 103, 89,
- -1, 12, -1, 15, -1, 22, -1, 17, -1, 21,
- 77, 117, 78, -1, 21, -1, 13, -1, 14, 104,
- -1, 87, -1, 104, 71, 104, -1, 104, 72, 104,
- -1, 104, 73, 104, -1, 104, 74, 104, -1, 104,
- 79, 104, -1, 104, 80, 104, -1, 104, 60, 104,
- -1, 104, 55, 104, -1, 104, 56, 104, -1, 104,
- 61, 104, -1, 104, 62, 104, -1, 63, 104, -1,
- 104, 81, 104, -1, 104, 64, 104, -1, 104, 65,
- 104, -1, 104, 66, 104, -1, 71, 104, -1, 72,
- 104, -1, 77, 104, 78, -1, 67, 104, 68, 104,
- -1, 67, 104, 69, 104, -1, 40, 104, -1, 107,
- -1, 110, -1, 28, -1, 30, 106, -1, 19, 104,
- -1, 18, 104, -1, 18, -1, 20, 117, -1, 51,
- 104, 46, 104, -1, 51, 104, -1, 21, -1, 106,
- 82, 21, -1, 31, 34, -1, 31, 37, -1, 31,
- 39, -1, 31, 108, -1, 31, 108, 109, -1, 31,
- 109, -1, 29, 104, -1, 104, -1, 38, 36, 104,
- -1, 36, 104, -1, 52, 53, -1, 52, 108, -1,
- 52, 108, 109, -1, 52, 109, -1, -1, 35, 21,
- 112, 101, 114, 85, 115, 103, -1, 49, 21, -1,
- -1, 50, 21, 113, 101, 114, 85, 115, 103, -1,
- -1, 21, -1, 114, 82, 21, -1, 114, 85, 82,
- 21, -1, -1, 21, 101, 117, -1, -1, 104, -1,
- 117, 82, 104, -1
+ 70, 105, 102, 44, 105, 102, 103, 102, 27, 41,
+ -1, 98, 70, 105, 102, 24, 44, 105, 102, 103,
+ 102, 27, 41, -1, 104, 105, 102, -1, 99, 96,
+ 43, 85, 103, 102, 27, 32, -1, 99, 96, 43,
+ 85, 103, 102, 48, 103, 102, 27, 32, -1, 99,
+ 96, 43, 85, 103, 102, 101, 92, 102, 27, 32,
+ -1, 99, 96, 43, 101, 88, 102, -1, 99, 96,
+ 43, 101, 88, 102, 48, 101, 88, 102, -1, 99,
+ 96, 43, 101, 88, 102, 93, 102, 91, 102, -1,
+ -1, 48, 101, 88, -1, 92, 95, -1, 95, -1,
+ 93, 94, -1, 94, -1, 100, 96, 43, 101, 89,
+ 102, -1, 93, -1, 100, 96, 43, 103, 102, -1,
+ 105, -1, 105, 70, 105, -1, 77, 96, 78, -1,
+ 41, 47, -1, 41, 46, 21, -1, 32, -1, 26,
+ -1, -1, -1, -1, 103, 85, -1, 103, 89, -1,
+ 45, 21, 43, -1, 12, -1, 15, -1, 22, -1,
+ 17, -1, 21, 77, 118, 78, -1, 21, -1, 13,
+ -1, 14, 105, -1, 87, -1, 105, 71, 105, -1,
+ 105, 72, 105, -1, 105, 73, 105, -1, 105, 74,
+ 105, -1, 105, 79, 105, -1, 105, 80, 105, -1,
+ 105, 60, 105, -1, 105, 55, 105, -1, 105, 56,
+ 105, -1, 105, 61, 105, -1, 105, 62, 105, -1,
+ 63, 105, -1, 105, 81, 105, -1, 105, 64, 105,
+ -1, 105, 65, 105, -1, 105, 66, 105, -1, 71,
+ 105, -1, 72, 105, -1, 77, 105, 78, -1, 67,
+ 105, 68, 105, -1, 67, 105, 69, 105, -1, 40,
+ 105, -1, 108, -1, 111, -1, 28, -1, 30, 107,
+ -1, 19, 105, -1, 18, 105, -1, 18, -1, 20,
+ 118, -1, 51, 105, 46, 105, -1, 51, 105, -1,
+ 21, -1, 107, 82, 21, -1, 31, 34, -1, 31,
+ 37, -1, 31, 39, -1, 31, 109, -1, 31, 109,
+ 110, -1, 31, 110, -1, 29, 105, -1, 105, -1,
+ 38, 36, 105, -1, 36, 105, -1, 52, 53, -1,
+ 52, 109, -1, 52, 109, 110, -1, 52, 110, -1,
+ -1, 35, 21, 113, 101, 115, 85, 116, 103, -1,
+ 49, 21, -1, -1, 50, 21, 114, 101, 115, 85,
+ 116, 103, -1, -1, 21, -1, 115, 82, 21, -1,
+ 115, 85, 82, 21, -1, -1, 21, 101, 118, -1,
+ -1, 105, -1, 118, 82, 105, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
@@ -621,17 +621,17 @@ static const yytype_uint16 yyrline[] =
{
0, 103, 103, 104, 105, 108, 113, 114, 115, 116,
117, 118, 121, 127, 133, 141, 149, 155, 163, 172,
- 173, 175, 176, 181, 192, 208, 220, 225, 232, 241,
- 250, 260, 270, 281, 282, 285, 286, 289, 290, 293,
- 301, 302, 310, 311, 312, 314, 316, 322, 328, 335,
- 337, 339, 340, 341, 344, 345, 348, 351, 355, 358,
- 362, 369, 375, 376, 377, 378, 379, 380, 381, 382,
- 383, 384, 385, 386, 387, 388, 389, 390, 391, 392,
- 393, 394, 395, 396, 399, 400, 401, 402, 404, 405,
- 408, 411, 414, 415, 416, 419, 420, 431, 432, 433,
- 434, 437, 440, 445, 446, 449, 450, 453, 454, 457,
- 460, 490, 490, 496, 499, 499, 504, 505, 506, 507,
- 509, 513, 521, 522, 523
+ 173, 175, 176, 181, 192, 208, 220, 228, 235, 244,
+ 253, 263, 273, 284, 285, 288, 289, 292, 293, 296,
+ 304, 305, 313, 314, 315, 317, 319, 325, 331, 338,
+ 340, 342, 343, 344, 347, 353, 354, 357, 360, 364,
+ 367, 371, 378, 384, 385, 386, 387, 388, 389, 390,
+ 391, 392, 393, 394, 395, 396, 397, 398, 399, 400,
+ 401, 402, 403, 404, 405, 408, 409, 410, 411, 413,
+ 414, 417, 420, 423, 424, 425, 428, 429, 440, 441,
+ 442, 443, 446, 449, 454, 455, 458, 459, 462, 463,
+ 466, 469, 499, 499, 505, 508, 508, 513, 514, 515,
+ 516, 518, 522, 530, 531, 532
};
#endif
@@ -655,9 +655,9 @@ static const char *const yytname[] =
"programline", "asgn", "stmtoneliner", "stmt", "ifstmt",
"elsestmtoneliner", "elseifstmt", "elseifstmtoneliner",
"elseifstmtoneliner1", "elseifstmt1", "cond", "repeatwhile",
- "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "expr", "func",
- "globallist", "gotofunc", "gotoframe", "gotomovie", "playfunc", "defn",
- "@1", "@2", "argdef", "argstore", "macro", "arglist", 0
+ "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "when", "expr",
+ "func", "globallist", "gotofunc", "gotoframe", "gotomovie", "playfunc",
+ "defn", "@1", "@2", "argdef", "argstore", "macro", "arglist", 0
};
#endif
@@ -686,14 +686,14 @@ static const yytype_uint8 yyr1[] =
88, 89, 89, 89, 89, 89, 89, 90, 90, 90,
90, 90, 90, 91, 91, 92, 92, 93, 93, 94,
95, 95, 96, 96, 96, 97, 98, 99, 100, 101,
- 102, 103, 103, 103, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 105, 105, 105, 105, 105, 105,
- 105, 105, 105, 105, 105, 106, 106, 107, 107, 107,
- 107, 107, 107, 108, 108, 109, 109, 110, 110, 110,
- 110, 112, 111, 111, 113, 111, 114, 114, 114, 114,
- 115, 116, 117, 117, 117
+ 102, 103, 103, 103, 104, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 106, 106, 106, 106, 106,
+ 106, 106, 106, 106, 106, 106, 107, 107, 108, 108,
+ 108, 108, 108, 108, 109, 109, 110, 110, 111, 111,
+ 111, 111, 113, 112, 112, 114, 112, 115, 115, 115,
+ 115, 116, 117, 118, 118, 118
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -701,17 +701,17 @@ static const yytype_uint8 yyr2[] =
{
0, 2, 3, 1, 2, 1, 0, 1, 1, 1,
1, 1, 4, 4, 4, 5, 4, 4, 5, 1,
- 1, 1, 1, 8, 11, 12, 4, 8, 11, 11,
+ 1, 1, 1, 8, 11, 12, 3, 8, 11, 11,
6, 10, 10, 0, 3, 2, 1, 2, 1, 6,
1, 5, 1, 3, 3, 2, 3, 1, 1, 0,
- 0, 0, 2, 2, 1, 1, 1, 1, 4, 1,
- 1, 2, 1, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 2, 3, 3, 3, 3, 2,
- 2, 3, 4, 4, 2, 1, 1, 1, 2, 2,
- 2, 1, 2, 4, 2, 1, 3, 2, 2, 2,
- 2, 3, 2, 2, 1, 3, 2, 2, 2, 3,
- 2, 0, 8, 2, 0, 8, 0, 1, 3, 4,
- 0, 3, 0, 1, 3
+ 0, 0, 2, 2, 3, 1, 1, 1, 1, 4,
+ 1, 1, 2, 1, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2, 3, 3, 3, 3,
+ 2, 2, 3, 4, 4, 2, 1, 1, 1, 2,
+ 2, 2, 1, 2, 4, 2, 1, 3, 2, 2,
+ 2, 2, 3, 2, 2, 1, 3, 2, 2, 2,
+ 3, 2, 0, 8, 2, 0, 8, 0, 1, 3,
+ 4, 0, 3, 0, 1, 3
};
/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
@@ -719,259 +719,263 @@ static const yytype_uint8 yyr2[] =
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
- 0, 0, 54, 60, 0, 55, 57, 91, 0, 122,
- 49, 56, 87, 0, 0, 47, 0, 0, 0, 0,
+ 0, 0, 55, 61, 0, 56, 58, 92, 0, 123,
+ 49, 57, 88, 0, 0, 47, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 3, 62, 21, 11, 22, 0, 0, 0, 19,
- 8, 85, 86, 7, 9, 5, 4, 59, 0, 62,
- 61, 90, 89, 123, 92, 122, 122, 95, 88, 0,
- 97, 0, 98, 0, 99, 104, 100, 102, 111, 84,
- 0, 45, 0, 0, 0, 0, 113, 114, 94, 107,
- 108, 110, 74, 0, 79, 80, 0, 1, 6, 0,
- 0, 0, 0, 42, 0, 0, 0, 0, 0, 0,
+ 0, 3, 63, 21, 11, 22, 0, 0, 0, 0,
+ 19, 8, 86, 87, 7, 9, 5, 4, 60, 0,
+ 63, 62, 91, 90, 124, 93, 123, 123, 96, 89,
+ 0, 98, 0, 99, 0, 100, 105, 101, 103, 112,
+ 85, 0, 45, 0, 0, 0, 0, 114, 115, 95,
+ 108, 109, 111, 75, 0, 80, 81, 0, 1, 6,
+ 0, 0, 0, 0, 42, 50, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 121, 0, 103, 106, 0, 101, 49, 0,
- 46, 0, 0, 0, 0, 0, 0, 49, 0, 109,
- 0, 0, 81, 2, 0, 50, 0, 0, 49, 0,
- 70, 71, 69, 72, 73, 76, 77, 78, 63, 64,
- 65, 66, 67, 68, 75, 124, 58, 96, 105, 116,
- 12, 17, 14, 0, 0, 16, 13, 26, 116, 93,
- 82, 83, 51, 0, 44, 51, 0, 43, 117, 0,
- 18, 15, 0, 50, 0, 0, 50, 50, 20, 0,
- 120, 120, 52, 53, 0, 0, 50, 49, 30, 118,
- 0, 51, 51, 0, 50, 51, 0, 51, 0, 48,
- 49, 50, 38, 0, 119, 112, 115, 23, 51, 50,
- 27, 50, 50, 40, 36, 0, 0, 37, 33, 0,
- 50, 0, 0, 35, 0, 0, 50, 49, 50, 49,
- 0, 0, 0, 0, 49, 31, 0, 32, 0, 0,
- 24, 28, 29, 50, 34, 50, 25, 41, 39
+ 0, 0, 0, 0, 122, 0, 104, 107, 0, 102,
+ 49, 0, 46, 0, 0, 0, 0, 0, 54, 49,
+ 0, 110, 0, 0, 82, 2, 0, 50, 0, 0,
+ 49, 0, 26, 71, 72, 70, 73, 74, 77, 78,
+ 79, 64, 65, 66, 67, 68, 69, 76, 125, 59,
+ 97, 106, 117, 12, 17, 14, 0, 0, 16, 13,
+ 117, 94, 83, 84, 51, 0, 44, 51, 0, 43,
+ 118, 0, 18, 15, 0, 50, 0, 0, 50, 50,
+ 20, 0, 121, 121, 52, 53, 0, 0, 50, 49,
+ 30, 119, 0, 51, 51, 0, 50, 51, 0, 51,
+ 0, 48, 49, 50, 38, 0, 120, 113, 116, 23,
+ 51, 50, 27, 50, 50, 40, 36, 0, 0, 37,
+ 33, 0, 50, 0, 0, 35, 0, 0, 50, 49,
+ 50, 49, 0, 0, 0, 0, 49, 31, 0, 32,
+ 0, 0, 24, 28, 29, 50, 34, 50, 25, 41,
+ 39
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 30, 192, 31, 49, 33, 193, 35, 238, 222,
- 223, 212, 224, 92, 36, 37, 38, 213, 248, 173,
- 183, 39, 188, 58, 41, 66, 67, 42, 43, 118,
- 127, 179, 201, 44, 54
+ -1, 30, 194, 31, 50, 33, 195, 35, 240, 224,
+ 225, 214, 226, 93, 36, 37, 38, 215, 250, 142,
+ 185, 39, 40, 190, 59, 42, 67, 68, 43, 44,
+ 120, 129, 181, 203, 45, 55
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -198
+#define YYPACT_NINF -195
static const yytype_int16 yypact[] =
{
- 243, -53, -198, -198, 385, -198, -198, 385, 385, 385,
- 791, -198, -198, 18, 573, -198, 22, 385, 7, 6,
- 26, 31, 36, 385, 613, 385, 385, 385, 385, 385,
- 4, -198, 5, -198, -198, -198, -48, -8, 512, 769,
- -198, -198, -198, -198, -198, -198, -198, -14, 385, -198,
- 769, 769, 769, 769, -21, 385, 385, -198, -4, 385,
- -198, 385, -198, 38, -198, 769, 8, -198, -198, 151,
- 54, -198, -37, 385, -29, 39, -198, -198, 649, -198,
- 8, -198, 840, 671, 840, 840, 720, -198, 341, 512,
- 385, 512, 40, 747, 385, 385, 385, 385, 385, 385,
- 385, 385, 385, 385, 385, 385, 385, 385, 385, 151,
- 385, -57, -21, 63, 769, 769, 385, -198, -198, 64,
- -198, 385, 385, 627, 385, 385, 385, -198, 385, -198,
- 385, 385, -198, -198, 9, 769, 14, 693, -53, 385,
- 769, 769, 769, 769, 769, 769, 769, 769, 818, 818,
- 840, 840, 769, 769, 769, 769, -198, -198, 769, 65,
- -198, 769, 769, 385, 385, 769, 769, 769, 65, 769,
- 769, 769, -198, -12, -198, -198, 529, 769, -198, -58,
- 769, 769, -58, 402, 49, 385, 402, -198, -198, 73,
- 13, 13, -198, -198, 71, 385, 769, -11, -17, -198,
- 78, -198, -198, 60, 769, -198, 72, -198, 77, -198,
- -198, 77, -198, 512, -198, 402, 402, -198, -198, 402,
- -198, 402, 77, 77, -198, 512, 529, -198, 57, 67,
- 402, 79, 80, -198, 81, 68, -198, -198, -198, -198,
- 85, 74, 84, 87, -16, -198, 529, -198, 468, 76,
- -198, -198, -198, 402, -198, -198, -198, -198, -198
+ 254, -59, -195, -195, 364, -195, -195, 364, 364, 364,
+ 801, -195, -195, 14, 552, -195, 23, 364, 10, 6,
+ 30, 32, 39, 364, 592, 364, 364, 364, 364, 364,
+ 4, -195, 5, -195, -195, -195, -51, 1, 491, 364,
+ 779, -195, -195, -195, -195, -195, -195, -195, -5, 364,
+ -195, 779, 779, 779, 779, -8, 364, 364, -195, -3,
+ 364, -195, 364, -195, 37, -195, 779, 12, -195, -195,
+ 606, 61, -195, -31, 364, -30, 40, -195, -195, 659,
+ -195, 12, -195, 850, 681, 850, 850, 730, -195, 320,
+ 491, 364, 491, 41, 757, 779, 364, 364, 364, 364,
+ 364, 364, 364, 364, 364, 364, 364, 364, 364, 364,
+ 364, 606, 364, -57, -8, 64, 779, 779, 364, -195,
+ -195, 65, -195, 364, 364, 637, 364, 364, -195, -195,
+ 364, -195, 364, 364, -195, -195, 15, 779, 18, 703,
+ -59, 364, -195, 779, 779, 779, 779, 779, 779, 779,
+ 779, 828, 828, 850, 850, 779, 779, 779, 779, -195,
+ -195, 779, 67, -195, 779, 779, 364, 364, 779, 779,
+ 67, 779, 779, 779, -195, -1, -195, -195, 508, 779,
+ -195, -58, 779, 779, -58, 381, 46, 364, 381, -195,
+ -195, 76, 17, 17, -195, -195, 74, 364, 779, -20,
+ -11, -195, 81, -195, -195, 62, 779, -195, 72, -195,
+ 79, -195, -195, 79, -195, 491, -195, 381, 381, -195,
+ -195, 381, -195, 381, 79, 79, -195, 491, 508, -195,
+ 58, 66, 381, 80, 83, -195, 86, 71, -195, -195,
+ -195, -195, 88, 75, 85, 87, -17, -195, 508, -195,
+ 447, 77, -195, -195, -195, 381, -195, -195, -195, -195,
+ -195
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -198, -198, 12, 25, 2, -170, 0, -198, -198, -198,
- -78, -197, -101, -61, -198, -198, -198, -186, -9, 113,
- -167, 41, 3, -198, -198, 98, -7, -198, -198, -198,
- -198, -45, -67, -198, 16
+ -195, -195, 11, 19, 2, -170, 0, -195, -195, -195,
+ -79, -191, -102, -61, -195, -195, -195, -194, -9, -12,
+ -171, -195, 38, 3, -195, -195, 99, -13, -195, -195,
+ -195, -195, -46, -67, -195, 13
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
number is the opposite. If zero, do what YYDEFACT says.
If YYTABLE_NINF, syntax error. */
-#define YYTABLE_NINF -60
+#define YYTABLE_NINF -61
static const yytype_int16 yytable[] =
{
- 34, 56, 32, 40, 87, -10, 187, 121, 186, 209,
- -51, -51, 184, 46, 227, 124, 206, 81, 45, 72,
- 73, 156, 225, 45, 189, 110, 227, 74, 134, 89,
- 136, 210, 185, 122, 215, 216, 225, 207, 219, 57,
- 221, 125, 88, 68, 61, 50, 63, 75, 51, 52,
- 53, 230, 76, 70, 71, 65, 236, 77, 69, 117,
- -51, 110, 90, 55, 78, 65, 82, 83, 84, 85,
- 86, 111, 112, 129, 116, 120, 254, 253, 113, 93,
- 45, -10, 126, 138, 157, 160, 178, 172, 34, 109,
- 32, 40, 174, 195, 199, 200, 53, 53, 203, 214,
- 114, 217, 115, 209, 220, 237, 241, 242, 243, 159,
- 239, 244, 249, 133, 123, 250, 251, 256, 168, 252,
- 211, 233, 80, 182, 202, 0, 0, 0, 0, 176,
- 93, 135, 137, 0, 0, 140, 141, 142, 143, 144,
- 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
- 175, 155, 229, 0, 0, 0, 0, 158, 0, 0,
- 0, 0, 161, 162, 235, 165, 166, 167, 0, 169,
- 0, 170, 171, 0, 0, 0, 0, 0, 0, 0,
- 177, 0, 0, 0, 119, 0, 0, 0, 208, 0,
- 0, 190, 0, 0, 191, 0, 0, 0, 0, 0,
- 0, 226, 0, 0, 180, 181, 94, 95, 0, 0,
- 0, 96, 97, 98, 0, 99, 100, 101, 0, 0,
- 0, 0, 102, 103, 104, 105, 196, 0, 246, 0,
- 106, 107, 108, 0, 0, 0, 204, 0, 0, 0,
- 0, 0, 0, -6, 1, 0, 0, 0, 255, 0,
- 0, 0, 0, 0, 93, 2, 3, 4, 5, 0,
- 6, 7, 8, 9, 10, 11, 93, 0, 0, 0,
- 0, 12, 0, 13, 14, 15, 0, 0, 16, 0,
- 0, 0, 0, 17, 18, 19, 0, 0, 20, 0,
- 0, 0, 21, 22, 23, 24, 194, 0, 0, 197,
- 198, 0, 0, 0, 0, 0, 25, 0, 0, 205,
- 26, 0, 0, 0, 27, 28, 0, 218, 0, -6,
- 29, 0, 0, 0, 228, 0, 0, 0, 0, 0,
- 0, 0, 231, 0, 232, 234, 0, 0, 0, 0,
- 0, 0, 0, 240, 0, 0, 0, 0, 0, 245,
- 0, 247, 0, 2, 3, 4, 5, 0, 6, 7,
- 8, 9, 10, 11, 0, 0, 257, 0, 258, 12,
- 0, 13, 14, 15, 0, 0, 16, 0, 0, 0,
- 0, 17, 18, 19, 0, 0, 20, 0, 0, 0,
- 21, 22, 23, 24, 0, 0, 0, 2, 3, 4,
- 5, 0, 6, 0, 25, 0, 47, 11, 26, 0,
- 0, 0, 27, 28, 2, 3, 4, 5, 29, 6,
- 7, 8, 9, 47, 11, 48, 0, 19, 0, 0,
- 12, 0, 13, 14, 15, 0, 0, 0, 0, 0,
- 0, 0, 17, 18, 19, 0, 0, 20, 25, 0,
- 0, 0, 26, 23, 24, 0, 27, 28, 0, 0,
- 0, 0, 29, 0, 0, 25, 0, 0, 0, 26,
- 0, 0, 0, 27, 28, 0, 0, 0, 45, 29,
- 2, 3, 4, 5, 0, 6, 7, 8, 9, 47,
- 11, 0, 0, 0, 0, 0, 12, 0, 13, 14,
- 15, 0, 0, 0, 0, 0, 0, 0, 17, 18,
- 19, 0, 0, 20, 0, 0, 0, 0, 0, 23,
- 24, 0, 0, 0, 2, 3, 4, 5, 0, 6,
- 0, 25, 0, 47, 11, 26, 0, 0, 0, 27,
- 28, 2, 3, 4, 5, 29, 6, 7, 8, 9,
- 47, 11, 48, 0, 19, 0, 0, 12, 0, 13,
- 14, 0, 0, 0, 0, 0, 0, 0, 0, 17,
- 0, 19, 0, 0, 0, 25, 0, 0, 0, 26,
- 23, 24, 0, 27, 28, 2, 3, 4, 5, 91,
- 6, 0, 25, 0, 47, 11, 26, 0, 0, 0,
- 27, 28, 59, 0, 0, 0, 29, 60, 0, 61,
- 62, 63, 64, 48, 0, 19, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 2, 3, 4, 5, 0,
- 6, 0, 0, 0, 47, 11, 25, 0, 0, 0,
- 26, 0, 59, 0, 27, 28, 0, 0, 0, 61,
- 29, 63, 0, 48, 0, 19, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 79, 0, 0, 0,
- 0, 163, 0, 0, 0, 0, 25, 0, 0, 0,
- 26, 0, 94, 95, 27, 28, 0, 96, 97, 98,
- 29, 99, 100, 101, 0, 128, 0, 164, 102, 103,
- 104, 105, 0, 0, 94, 95, 106, 107, 108, 96,
- 97, 98, 0, 99, 100, 101, 0, 0, 0, 0,
- 102, 103, 104, 105, 0, 0, 94, 95, 106, 107,
- 108, 96, 97, 98, 0, 99, 100, 101, 0, 130,
- 131, 0, 102, 103, 104, 105, 0, 0, 94, 95,
- 106, 107, 108, 96, 97, 98, 0, 99, 100, 101,
- 0, 0, 0, 139, 102, 103, 104, 105, 0, 0,
- 0, 132, 106, 107, 108, 94, 95, 0, 0, 0,
- 96, 97, 98, 0, 99, 100, 101, 0, 0, 0,
- 0, 102, 103, 104, 105, 0, 0, 0, 132, 106,
- 107, 108, 94, 95, 0, 0, 0, 96, 97, 98,
- 0, 99, 100, 101, 0, 0, 0, 139, 102, 103,
- 104, 105, 0, 0, 94, 95, 106, 107, 108, 96,
- 97, 98, 0, 99, 100, 101, 0, 0, 0, 0,
- 102, 103, 104, 105, 0, 0, -59, -59, 106, 107,
- 108, -59, -59, -59, 0, -59, -59, -59, 0, 0,
- 0, 0, 0, 0, -59, -59, 0, 0, 55, 0,
- -59, -59, -59, 94, 95, 0, 0, 0, 96, 97,
- 98, 0, 99, 100, 101, 0, 0, 0, 0, 0,
- 0, 104, 105, 0, 0, 94, 95, 106, 107, 108,
- 96, 97, 98, 0, 99, 100, 101, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 106,
- 107, 108
+ 34, 57, 32, 41, 88, -10, 188, 208, 189, -51,
+ -51, 82, 47, 123, 126, 211, 227, 46, 46, 73,
+ 74, 159, 229, 186, 191, 112, 90, 75, 209, 136,
+ 227, 138, 217, 218, 229, 58, 221, 212, 223, 124,
+ 127, 89, 51, 187, 69, 52, 53, 54, 62, 232,
+ 64, 76, 66, 77, 119, 70, 71, 72, 238, -51,
+ 78, 79, 66, 83, 84, 85, 86, 87, 131, 113,
+ 114, 91, 56, 118, 112, 255, 94, 95, 256, 115,
+ 46, -10, 122, 128, 140, 160, 163, 111, 180, 34,
+ 197, 32, 41, 174, 54, 54, 176, 201, 116, 202,
+ 117, 205, 216, 219, 222, 211, 239, 243, 135, 241,
+ 244, 162, 125, 245, 246, 251, 252, 253, 258, 254,
+ 170, 213, 235, 81, 184, 175, 204, 0, 94, 137,
+ 139, 178, 0, 0, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 0,
+ 158, 177, 0, 0, 231, 0, 161, 0, 0, 0,
+ 0, 164, 165, 0, 168, 169, 237, 0, 171, 0,
+ 172, 173, 0, 196, 0, 0, 199, 200, 0, 179,
+ 0, 0, 0, 0, 0, 0, 207, 0, 0, 0,
+ 210, 0, 192, 0, 220, 193, 0, 0, 0, 0,
+ 0, 230, 0, 228, 182, 183, 0, 0, 0, 233,
+ 0, 234, 236, 0, 0, 0, 0, 0, 0, 0,
+ 242, 0, 0, 0, 0, 198, 247, 0, 249, 0,
+ 248, 0, 0, 0, 0, 206, 0, 0, 0, 0,
+ 0, 0, 0, 259, 0, 260, 0, 0, 0, 0,
+ 257, 0, 0, 94, -6, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 94, 2, 3, 4, 5,
+ 0, 6, 7, 8, 9, 10, 11, 0, 0, 0,
+ 0, 0, 12, 0, 13, 14, 15, 0, 0, 16,
+ 0, 0, 0, 0, 17, 18, 19, 0, 0, 20,
+ 0, 0, 0, 21, 22, 23, 24, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 25, 0, 0,
+ 0, 26, 0, 0, 0, 27, 28, 0, 0, 0,
+ -6, 29, 2, 3, 4, 5, 0, 6, 7, 8,
+ 9, 10, 11, 0, 0, 0, 0, 0, 12, 0,
+ 13, 14, 15, 0, 0, 16, 0, 0, 0, 0,
+ 17, 18, 19, 0, 0, 20, 0, 0, 0, 21,
+ 22, 23, 24, 0, 0, 0, 2, 3, 4, 5,
+ 0, 6, 0, 25, 0, 48, 11, 26, 0, 0,
+ 0, 27, 28, 2, 3, 4, 5, 29, 6, 7,
+ 8, 9, 48, 11, 49, 0, 19, 0, 0, 12,
+ 0, 13, 14, 15, 0, 0, 0, 0, 0, 0,
+ 0, 17, 18, 19, 0, 0, 20, 25, 0, 0,
+ 0, 26, 23, 24, 0, 27, 28, 0, 0, 0,
+ 0, 29, 0, 0, 25, 0, 0, 0, 26, 0,
+ 0, 0, 27, 28, 0, 0, 0, 46, 29, 2,
+ 3, 4, 5, 0, 6, 7, 8, 9, 48, 11,
+ 0, 0, 0, 0, 0, 12, 0, 13, 14, 15,
+ 0, 0, 0, 0, 0, 0, 0, 17, 18, 19,
+ 0, 0, 20, 0, 0, 0, 0, 0, 23, 24,
+ 0, 0, 0, 2, 3, 4, 5, 0, 6, 0,
+ 25, 0, 48, 11, 26, 0, 0, 0, 27, 28,
+ 2, 3, 4, 5, 29, 6, 7, 8, 9, 48,
+ 11, 49, 0, 19, 0, 0, 12, 0, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 17, 0,
+ 19, 0, 0, 0, 25, 0, 0, 0, 26, 23,
+ 24, 0, 27, 28, 2, 3, 4, 5, 92, 6,
+ 0, 25, 0, 48, 11, 26, 0, 0, 0, 27,
+ 28, 60, 0, 0, 0, 29, 61, 0, 62, 63,
+ 64, 65, 49, 0, 19, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 3, 4, 5, 0, 6,
+ 0, 0, 0, 48, 11, 25, 0, 0, 0, 26,
+ 0, 60, 0, 27, 28, 0, 0, 0, 62, 29,
+ 64, 0, 49, 0, 19, 0, 0, 0, 0, 121,
+ 0, 0, 0, 0, 0, 80, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 25, 0, 0, 0, 26,
+ 0, 96, 97, 27, 28, 0, 98, 99, 100, 29,
+ 101, 102, 103, 0, 0, 0, 0, 104, 105, 106,
+ 107, 166, 0, 0, 0, 108, 109, 110, 0, 0,
+ 0, 0, 96, 97, 0, 0, 0, 98, 99, 100,
+ 0, 101, 102, 103, 0, 130, 0, 167, 104, 105,
+ 106, 107, 0, 0, 96, 97, 108, 109, 110, 98,
+ 99, 100, 0, 101, 102, 103, 0, 0, 0, 0,
+ 104, 105, 106, 107, 0, 0, 96, 97, 108, 109,
+ 110, 98, 99, 100, 0, 101, 102, 103, 0, 132,
+ 133, 0, 104, 105, 106, 107, 0, 0, 96, 97,
+ 108, 109, 110, 98, 99, 100, 0, 101, 102, 103,
+ 0, 0, 0, 141, 104, 105, 106, 107, 0, 0,
+ 0, 134, 108, 109, 110, 96, 97, 0, 0, 0,
+ 98, 99, 100, 0, 101, 102, 103, 0, 0, 0,
+ 0, 104, 105, 106, 107, 0, 0, 0, 134, 108,
+ 109, 110, 96, 97, 0, 0, 0, 98, 99, 100,
+ 0, 101, 102, 103, 0, 0, 0, 141, 104, 105,
+ 106, 107, 0, 0, 96, 97, 108, 109, 110, 98,
+ 99, 100, 0, 101, 102, 103, 0, 0, 0, 0,
+ 104, 105, 106, 107, 0, 0, -60, -60, 108, 109,
+ 110, -60, -60, -60, 0, -60, -60, -60, 0, 0,
+ 0, 0, 0, 0, -60, -60, 0, 0, 56, 0,
+ -60, -60, -60, 96, 97, 0, 0, 0, 98, 99,
+ 100, 0, 101, 102, 103, 0, 0, 0, 0, 0,
+ 0, 106, 107, 0, 0, 96, 97, 108, 109, 110,
+ 98, 99, 100, 0, 101, 102, 103, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 108,
+ 109, 110
};
static const yytype_int16 yycheck[] =
{
- 0, 10, 0, 0, 0, 0, 176, 44, 175, 26,
- 26, 27, 24, 1, 211, 44, 27, 24, 76, 13,
- 14, 78, 208, 76, 82, 82, 223, 21, 89, 77,
- 91, 48, 44, 70, 201, 202, 222, 48, 205, 21,
- 207, 70, 30, 21, 36, 4, 38, 21, 7, 8,
- 9, 218, 21, 46, 47, 14, 226, 21, 17, 66,
- 76, 82, 70, 77, 23, 24, 25, 26, 27, 28,
- 29, 55, 56, 80, 36, 21, 246, 244, 82, 38,
- 76, 76, 43, 43, 21, 21, 21, 78, 88, 48,
- 88, 88, 78, 44, 21, 82, 55, 56, 27, 21,
- 59, 41, 61, 26, 32, 48, 27, 27, 27, 118,
- 43, 43, 27, 88, 73, 41, 32, 41, 127, 32,
- 198, 222, 24, 168, 191, -1, -1, -1, -1, 138,
- 89, 90, 91, -1, -1, 94, 95, 96, 97, 98,
- 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
- 138, 110, 213, -1, -1, -1, -1, 116, -1, -1,
- -1, -1, 121, 122, 225, 124, 125, 126, -1, 128,
- -1, 130, 131, -1, -1, -1, -1, -1, -1, -1,
- 139, -1, -1, -1, 33, -1, -1, -1, 197, -1,
- -1, 179, -1, -1, 182, -1, -1, -1, -1, -1,
- -1, 210, -1, -1, 163, 164, 55, 56, -1, -1,
- -1, 60, 61, 62, -1, 64, 65, 66, -1, -1,
- -1, -1, 71, 72, 73, 74, 185, -1, 237, -1,
- 79, 80, 81, -1, -1, -1, 195, -1, -1, -1,
- -1, -1, -1, 0, 1, -1, -1, -1, 248, -1,
- -1, -1, -1, -1, 213, 12, 13, 14, 15, -1,
- 17, 18, 19, 20, 21, 22, 225, -1, -1, -1,
- -1, 28, -1, 30, 31, 32, -1, -1, 35, -1,
- -1, -1, -1, 40, 41, 42, -1, -1, 45, -1,
- -1, -1, 49, 50, 51, 52, 183, -1, -1, 186,
- 187, -1, -1, -1, -1, -1, 63, -1, -1, 196,
- 67, -1, -1, -1, 71, 72, -1, 204, -1, 76,
- 77, -1, -1, -1, 211, -1, -1, -1, -1, -1,
- -1, -1, 219, -1, 221, 222, -1, -1, -1, -1,
- -1, -1, -1, 230, -1, -1, -1, -1, -1, 236,
- -1, 238, -1, 12, 13, 14, 15, -1, 17, 18,
- 19, 20, 21, 22, -1, -1, 253, -1, 255, 28,
- -1, 30, 31, 32, -1, -1, 35, -1, -1, -1,
- -1, 40, 41, 42, -1, -1, 45, -1, -1, -1,
- 49, 50, 51, 52, -1, -1, -1, 12, 13, 14,
- 15, -1, 17, -1, 63, -1, 21, 22, 67, -1,
- -1, -1, 71, 72, 12, 13, 14, 15, 77, 17,
- 18, 19, 20, 21, 22, 40, -1, 42, -1, -1,
- 28, -1, 30, 31, 32, -1, -1, -1, -1, -1,
- -1, -1, 40, 41, 42, -1, -1, 45, 63, -1,
- -1, -1, 67, 51, 52, -1, 71, 72, -1, -1,
- -1, -1, 77, -1, -1, 63, -1, -1, -1, 67,
- -1, -1, -1, 71, 72, -1, -1, -1, 76, 77,
- 12, 13, 14, 15, -1, 17, 18, 19, 20, 21,
- 22, -1, -1, -1, -1, -1, 28, -1, 30, 31,
- 32, -1, -1, -1, -1, -1, -1, -1, 40, 41,
- 42, -1, -1, 45, -1, -1, -1, -1, -1, 51,
- 52, -1, -1, -1, 12, 13, 14, 15, -1, 17,
+ 0, 10, 0, 0, 0, 0, 177, 27, 178, 26,
+ 27, 24, 1, 44, 44, 26, 210, 76, 76, 13,
+ 14, 78, 213, 24, 82, 82, 77, 21, 48, 90,
+ 224, 92, 203, 204, 225, 21, 207, 48, 209, 70,
+ 70, 30, 4, 44, 21, 7, 8, 9, 36, 220,
+ 38, 21, 14, 21, 67, 17, 46, 47, 228, 76,
+ 21, 23, 24, 25, 26, 27, 28, 29, 81, 56,
+ 57, 70, 77, 36, 82, 246, 38, 39, 248, 82,
+ 76, 76, 21, 43, 43, 21, 21, 49, 21, 89,
+ 44, 89, 89, 78, 56, 57, 78, 21, 60, 82,
+ 62, 27, 21, 41, 32, 26, 48, 27, 89, 43,
+ 27, 120, 74, 27, 43, 27, 41, 32, 41, 32,
+ 129, 200, 224, 24, 170, 137, 193, -1, 90, 91,
+ 92, 140, -1, -1, 96, 97, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, -1,
+ 112, 140, -1, -1, 215, -1, 118, -1, -1, -1,
+ -1, 123, 124, -1, 126, 127, 227, -1, 130, -1,
+ 132, 133, -1, 185, -1, -1, 188, 189, -1, 141,
+ -1, -1, -1, -1, -1, -1, 198, -1, -1, -1,
+ 199, -1, 181, -1, 206, 184, -1, -1, -1, -1,
+ -1, 213, -1, 212, 166, 167, -1, -1, -1, 221,
+ -1, 223, 224, -1, -1, -1, -1, -1, -1, -1,
+ 232, -1, -1, -1, -1, 187, 238, -1, 240, -1,
+ 239, -1, -1, -1, -1, 197, -1, -1, -1, -1,
+ -1, -1, -1, 255, -1, 257, -1, -1, -1, -1,
+ 250, -1, -1, 215, 0, 1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 227, 12, 13, 14, 15,
+ -1, 17, 18, 19, 20, 21, 22, -1, -1, -1,
+ -1, -1, 28, -1, 30, 31, 32, -1, -1, 35,
+ -1, -1, -1, -1, 40, 41, 42, -1, -1, 45,
+ -1, -1, -1, 49, 50, 51, 52, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 63, -1, -1,
+ -1, 67, -1, -1, -1, 71, 72, -1, -1, -1,
+ 76, 77, 12, 13, 14, 15, -1, 17, 18, 19,
+ 20, 21, 22, -1, -1, -1, -1, -1, 28, -1,
+ 30, 31, 32, -1, -1, 35, -1, -1, -1, -1,
+ 40, 41, 42, -1, -1, 45, -1, -1, -1, 49,
+ 50, 51, 52, -1, -1, -1, 12, 13, 14, 15,
+ -1, 17, -1, 63, -1, 21, 22, 67, -1, -1,
+ -1, 71, 72, 12, 13, 14, 15, 77, 17, 18,
+ 19, 20, 21, 22, 40, -1, 42, -1, -1, 28,
+ -1, 30, 31, 32, -1, -1, -1, -1, -1, -1,
+ -1, 40, 41, 42, -1, -1, 45, 63, -1, -1,
+ -1, 67, 51, 52, -1, 71, 72, -1, -1, -1,
+ -1, 77, -1, -1, 63, -1, -1, -1, 67, -1,
+ -1, -1, 71, 72, -1, -1, -1, 76, 77, 12,
+ 13, 14, 15, -1, 17, 18, 19, 20, 21, 22,
+ -1, -1, -1, -1, -1, 28, -1, 30, 31, 32,
+ -1, -1, -1, -1, -1, -1, -1, 40, 41, 42,
+ -1, -1, 45, -1, -1, -1, -1, -1, 51, 52,
+ -1, -1, -1, 12, 13, 14, 15, -1, 17, -1,
+ 63, -1, 21, 22, 67, -1, -1, -1, 71, 72,
+ 12, 13, 14, 15, 77, 17, 18, 19, 20, 21,
+ 22, 40, -1, 42, -1, -1, 28, -1, 30, 31,
+ -1, -1, -1, -1, -1, -1, -1, -1, 40, -1,
+ 42, -1, -1, -1, 63, -1, -1, -1, 67, 51,
+ 52, -1, 71, 72, 12, 13, 14, 15, 77, 17,
-1, 63, -1, 21, 22, 67, -1, -1, -1, 71,
- 72, 12, 13, 14, 15, 77, 17, 18, 19, 20,
- 21, 22, 40, -1, 42, -1, -1, 28, -1, 30,
- 31, -1, -1, -1, -1, -1, -1, -1, -1, 40,
- -1, 42, -1, -1, -1, 63, -1, -1, -1, 67,
- 51, 52, -1, 71, 72, 12, 13, 14, 15, 77,
- 17, -1, 63, -1, 21, 22, 67, -1, -1, -1,
- 71, 72, 29, -1, -1, -1, 77, 34, -1, 36,
- 37, 38, 39, 40, -1, 42, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 12, 13, 14, 15, -1,
- 17, -1, -1, -1, 21, 22, 63, -1, -1, -1,
- 67, -1, 29, -1, 71, 72, -1, -1, -1, 36,
- 77, 38, -1, 40, -1, 42, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 53, -1, -1, -1,
- -1, 44, -1, -1, -1, -1, 63, -1, -1, -1,
- 67, -1, 55, 56, 71, 72, -1, 60, 61, 62,
- 77, 64, 65, 66, -1, 46, -1, 70, 71, 72,
+ 72, 29, -1, -1, -1, 77, 34, -1, 36, 37,
+ 38, 39, 40, -1, 42, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 12, 13, 14, 15, -1, 17,
+ -1, -1, -1, 21, 22, 63, -1, -1, -1, 67,
+ -1, 29, -1, 71, 72, -1, -1, -1, 36, 77,
+ 38, -1, 40, -1, 42, -1, -1, -1, -1, 33,
+ -1, -1, -1, -1, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 63, -1, -1, -1, 67,
+ -1, 55, 56, 71, 72, -1, 60, 61, 62, 77,
+ 64, 65, 66, -1, -1, -1, -1, 71, 72, 73,
+ 74, 44, -1, -1, -1, 79, 80, 81, -1, -1,
+ -1, -1, 55, 56, -1, -1, -1, 60, 61, 62,
+ -1, 64, 65, 66, -1, 46, -1, 70, 71, 72,
73, 74, -1, -1, 55, 56, 79, 80, 81, 60,
61, 62, -1, 64, 65, 66, -1, -1, -1, -1,
71, 72, 73, 74, -1, -1, 55, 56, 79, 80,
@@ -1005,28 +1009,29 @@ static const yytype_uint8 yystos[] =
21, 22, 28, 30, 31, 32, 35, 40, 41, 42,
45, 49, 50, 51, 52, 63, 67, 71, 72, 77,
84, 86, 87, 88, 89, 90, 97, 98, 99, 104,
- 105, 107, 110, 111, 116, 76, 85, 21, 40, 87,
- 104, 104, 104, 104, 117, 77, 101, 21, 106, 29,
- 34, 36, 37, 38, 39, 104, 108, 109, 21, 104,
- 46, 47, 13, 14, 21, 21, 21, 21, 104, 53,
- 108, 109, 104, 104, 104, 104, 104, 0, 85, 77,
- 70, 77, 96, 104, 55, 56, 60, 61, 62, 64,
- 65, 66, 71, 72, 73, 74, 79, 80, 81, 104,
- 82, 117, 117, 82, 104, 104, 36, 109, 112, 33,
- 21, 44, 70, 104, 44, 70, 43, 113, 46, 109,
- 68, 69, 78, 86, 96, 104, 96, 104, 43, 70,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 78, 21, 104, 101,
- 21, 104, 104, 44, 70, 104, 104, 104, 101, 104,
- 104, 104, 78, 102, 78, 85, 101, 104, 21, 114,
- 104, 104, 114, 103, 24, 44, 103, 88, 105, 82,
- 85, 85, 85, 89, 102, 44, 104, 102, 102, 21,
- 82, 115, 115, 27, 104, 102, 27, 48, 101, 26,
- 48, 93, 94, 100, 21, 103, 103, 41, 102, 103,
- 32, 103, 92, 93, 95, 100, 101, 94, 102, 96,
- 103, 102, 102, 95, 102, 96, 88, 48, 91, 43,
- 102, 27, 27, 27, 43, 102, 101, 102, 101, 27,
- 41, 32, 32, 103, 88, 89, 41, 102, 102
+ 105, 106, 108, 111, 112, 117, 76, 85, 21, 40,
+ 87, 105, 105, 105, 105, 118, 77, 101, 21, 107,
+ 29, 34, 36, 37, 38, 39, 105, 109, 110, 21,
+ 105, 46, 47, 13, 14, 21, 21, 21, 21, 105,
+ 53, 109, 110, 105, 105, 105, 105, 105, 0, 85,
+ 77, 70, 77, 96, 105, 105, 55, 56, 60, 61,
+ 62, 64, 65, 66, 71, 72, 73, 74, 79, 80,
+ 81, 105, 82, 118, 118, 82, 105, 105, 36, 110,
+ 113, 33, 21, 44, 70, 105, 44, 70, 43, 114,
+ 46, 110, 68, 69, 78, 86, 96, 105, 96, 105,
+ 43, 70, 102, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 78,
+ 21, 105, 101, 21, 105, 105, 44, 70, 105, 105,
+ 101, 105, 105, 105, 78, 102, 78, 85, 101, 105,
+ 21, 115, 105, 105, 115, 103, 24, 44, 103, 88,
+ 106, 82, 85, 85, 85, 89, 102, 44, 105, 102,
+ 102, 21, 82, 116, 116, 27, 105, 102, 27, 48,
+ 101, 26, 48, 93, 94, 100, 21, 103, 103, 41,
+ 102, 103, 32, 103, 92, 93, 95, 100, 101, 94,
+ 102, 96, 103, 102, 102, 95, 102, 96, 88, 48,
+ 91, 43, 102, 27, 27, 27, 43, 102, 101, 102,
+ 101, 27, 41, 32, 32, 103, 88, 89, 41, 102,
+ 102
};
#define yyerrok (yyerrstatus = 0)
@@ -1986,12 +1991,15 @@ yyreduce:
case 26:
#line 220 "engines/director/lingo/lingo-gr.y"
{
- g_lingo->code1(g_lingo->c_ifcode);
+ inst end = 0;
+ WRITE_UINT32(&end, (yyvsp[(3) - (3)].code));
+ g_lingo->code1(STOP);
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (3)].code) + 1] = end;
;}
break;
case 27:
-#line 225 "engines/director/lingo/lingo-gr.y"
+#line 228 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(5) - (8)].code));
@@ -2002,7 +2010,7 @@ yyreduce:
break;
case 28:
-#line 232 "engines/director/lingo/lingo-gr.y"
+#line 235 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(5) - (11)].code));
@@ -2015,7 +2023,7 @@ yyreduce:
break;
case 29:
-#line 241 "engines/director/lingo/lingo-gr.y"
+#line 244 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(5) - (11)].code));
@@ -2028,7 +2036,7 @@ yyreduce:
break;
case 30:
-#line 250 "engines/director/lingo/lingo-gr.y"
+#line 253 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (6)].code));
@@ -2042,7 +2050,7 @@ yyreduce:
break;
case 31:
-#line 260 "engines/director/lingo/lingo-gr.y"
+#line 263 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (10)].code));
@@ -2056,7 +2064,7 @@ yyreduce:
break;
case 32:
-#line 270 "engines/director/lingo/lingo-gr.y"
+#line 273 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (10)].code));
@@ -2070,17 +2078,17 @@ yyreduce:
break;
case 33:
-#line 281 "engines/director/lingo/lingo-gr.y"
+#line 284 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = 0; ;}
break;
case 34:
-#line 282 "engines/director/lingo/lingo-gr.y"
+#line 285 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (3)].code); ;}
break;
case 39:
-#line 293 "engines/director/lingo/lingo-gr.y"
+#line 296 "engines/director/lingo/lingo-gr.y"
{
inst then = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (6)].code));
@@ -2090,7 +2098,7 @@ yyreduce:
break;
case 41:
-#line 302 "engines/director/lingo/lingo-gr.y"
+#line 305 "engines/director/lingo/lingo-gr.y"
{
inst then = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (5)].code));
@@ -2100,22 +2108,22 @@ yyreduce:
break;
case 42:
-#line 310 "engines/director/lingo/lingo-gr.y"
+#line 313 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(STOP); ;}
break;
case 43:
-#line 311 "engines/director/lingo/lingo-gr.y"
+#line 314 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code2(g_lingo->c_eq, STOP); ;}
break;
case 45:
-#line 314 "engines/director/lingo/lingo-gr.y"
+#line 317 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = g_lingo->code3(g_lingo->c_repeatwhilecode, STOP, STOP); ;}
break;
case 46:
-#line 316 "engines/director/lingo/lingo-gr.y"
+#line 319 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code3(g_lingo->c_repeatwithcode, STOP, STOP);
g_lingo->code3(STOP, STOP, STOP);
@@ -2124,7 +2132,7 @@ yyreduce:
break;
case 47:
-#line 322 "engines/director/lingo/lingo-gr.y"
+#line 325 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_ifcode);
g_lingo->code3(STOP, STOP, STOP);
@@ -2133,7 +2141,7 @@ yyreduce:
break;
case 48:
-#line 328 "engines/director/lingo/lingo-gr.y"
+#line 331 "engines/director/lingo/lingo-gr.y"
{
inst skipEnd;
WRITE_UINT32(&skipEnd, 1); // We have to skip end to avoid multiple executions
@@ -2143,64 +2151,73 @@ yyreduce:
break;
case 49:
-#line 335 "engines/director/lingo/lingo-gr.y"
+#line 338 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = g_lingo->_currentScript->size(); ;}
break;
case 50:
-#line 337 "engines/director/lingo/lingo-gr.y"
+#line 340 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(STOP); (yyval.code) = g_lingo->_currentScript->size(); ;}
break;
case 51:
-#line 339 "engines/director/lingo/lingo-gr.y"
+#line 342 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = g_lingo->_currentScript->size(); ;}
break;
case 54:
-#line 344 "engines/director/lingo/lingo-gr.y"
- { (yyval.code) = g_lingo->codeConst((yyvsp[(1) - (1)].i)); ;}
+#line 347 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_whencode);
+ g_lingo->code1(STOP);
+ g_lingo->codeString((yyvsp[(2) - (3)].s)->c_str());
+ delete (yyvsp[(2) - (3)].s); ;}
break;
case 55:
-#line 345 "engines/director/lingo/lingo-gr.y"
+#line 353 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = g_lingo->codeConst((yyvsp[(1) - (1)].i)); ;}
+ break;
+
+ case 56:
+#line 354 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_fconstpush);
g_lingo->codeFloat((yyvsp[(1) - (1)].f)); ;}
break;
- case 56:
-#line 348 "engines/director/lingo/lingo-gr.y"
+ case 57:
+#line 357 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_stringpush);
g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); ;}
break;
- case 57:
-#line 351 "engines/director/lingo/lingo-gr.y"
+ case 58:
+#line 360 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func);
g_lingo->codeConst(0); // Put dummy value
delete (yyvsp[(1) - (1)].s); ;}
break;
- case 58:
-#line 355 "engines/director/lingo/lingo-gr.y"
+ case 59:
+#line 364 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->codeFunc((yyvsp[(1) - (4)].s), (yyvsp[(3) - (4)].narg));
delete (yyvsp[(1) - (4)].s); ;}
break;
- case 59:
-#line 358 "engines/director/lingo/lingo-gr.y"
+ case 60:
+#line 367 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_eval);
g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str());
delete (yyvsp[(1) - (1)].s); ;}
break;
- case 60:
-#line 362 "engines/director/lingo/lingo-gr.y"
+ case 61:
+#line 371 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->codeConst(0); // Put dummy id
g_lingo->code1(g_lingo->c_theentitypush);
@@ -2210,8 +2227,8 @@ yyreduce:
g_lingo->code2(e, f); ;}
break;
- case 61:
-#line 369 "engines/director/lingo/lingo-gr.y"
+ case 62:
+#line 378 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_theentitypush);
inst e = 0, f = 0;
@@ -2220,237 +2237,237 @@ yyreduce:
g_lingo->code2(e, f); ;}
break;
- case 63:
-#line 376 "engines/director/lingo/lingo-gr.y"
+ case 64:
+#line 385 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_add); ;}
break;
- case 64:
-#line 377 "engines/director/lingo/lingo-gr.y"
+ case 65:
+#line 386 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_sub); ;}
break;
- case 65:
-#line 378 "engines/director/lingo/lingo-gr.y"
+ case 66:
+#line 387 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_mul); ;}
break;
- case 66:
-#line 379 "engines/director/lingo/lingo-gr.y"
+ case 67:
+#line 388 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_div); ;}
break;
- case 67:
-#line 380 "engines/director/lingo/lingo-gr.y"
+ case 68:
+#line 389 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gt); ;}
break;
- case 68:
-#line 381 "engines/director/lingo/lingo-gr.y"
+ case 69:
+#line 390 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_lt); ;}
break;
- case 69:
-#line 382 "engines/director/lingo/lingo-gr.y"
+ case 70:
+#line 391 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_neq); ;}
break;
- case 70:
-#line 383 "engines/director/lingo/lingo-gr.y"
+ case 71:
+#line 392 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_ge); ;}
break;
- case 71:
-#line 384 "engines/director/lingo/lingo-gr.y"
+ case 72:
+#line 393 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_le); ;}
break;
- case 72:
-#line 385 "engines/director/lingo/lingo-gr.y"
+ case 73:
+#line 394 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_and); ;}
break;
- case 73:
-#line 386 "engines/director/lingo/lingo-gr.y"
+ case 74:
+#line 395 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_or); ;}
break;
- case 74:
-#line 387 "engines/director/lingo/lingo-gr.y"
+ case 75:
+#line 396 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_not); ;}
break;
- case 75:
-#line 388 "engines/director/lingo/lingo-gr.y"
+ case 76:
+#line 397 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_ampersand); ;}
break;
- case 76:
-#line 389 "engines/director/lingo/lingo-gr.y"
+ case 77:
+#line 398 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_concat); ;}
break;
- case 77:
-#line 390 "engines/director/lingo/lingo-gr.y"
+ case 78:
+#line 399 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_contains); ;}
break;
- case 78:
-#line 391 "engines/director/lingo/lingo-gr.y"
+ case 79:
+#line 400 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_starts); ;}
break;
- case 79:
-#line 392 "engines/director/lingo/lingo-gr.y"
+ case 80:
+#line 401 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (2)].code); ;}
break;
- case 80:
-#line 393 "engines/director/lingo/lingo-gr.y"
+ case 81:
+#line 402 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (2)].code); g_lingo->code1(g_lingo->c_negate); ;}
break;
- case 81:
-#line 394 "engines/director/lingo/lingo-gr.y"
+ case 82:
+#line 403 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (3)].code); ;}
break;
- case 82:
-#line 395 "engines/director/lingo/lingo-gr.y"
+ case 83:
+#line 404 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_intersects); ;}
break;
- case 83:
-#line 396 "engines/director/lingo/lingo-gr.y"
+ case 84:
+#line 405 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_within); ;}
break;
- case 84:
-#line 399 "engines/director/lingo/lingo-gr.y"
+ case 85:
+#line 408 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_printtop); ;}
break;
- case 87:
-#line 402 "engines/director/lingo/lingo-gr.y"
+ case 88:
+#line 411 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeConst(0); // Push fake value on stack
g_lingo->code1(g_lingo->c_procret); ;}
break;
- case 89:
-#line 405 "engines/director/lingo/lingo-gr.y"
+ case 90:
+#line 414 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (2)].s)]->u.func);
delete (yyvsp[(1) - (2)].s); ;}
break;
- case 90:
-#line 408 "engines/director/lingo/lingo-gr.y"
+ case 91:
+#line 417 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (2)].s)]->u.func);
delete (yyvsp[(1) - (2)].s); ;}
break;
- case 91:
-#line 411 "engines/director/lingo/lingo-gr.y"
+ case 92:
+#line 420 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code2(g_lingo->c_voidpush, g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func);
delete (yyvsp[(1) - (1)].s); ;}
break;
- case 92:
-#line 414 "engines/director/lingo/lingo-gr.y"
+ case 93:
+#line 423 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeFunc((yyvsp[(1) - (2)].s), (yyvsp[(2) - (2)].narg)); ;}
break;
- case 93:
-#line 415 "engines/director/lingo/lingo-gr.y"
+ case 94:
+#line 424 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_open); ;}
break;
- case 94:
-#line 416 "engines/director/lingo/lingo-gr.y"
+ case 95:
+#line 425 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code2(g_lingo->c_voidpush, g_lingo->c_open); ;}
break;
- case 95:
-#line 419 "engines/director/lingo/lingo-gr.y"
+ case 96:
+#line 428 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); delete (yyvsp[(1) - (1)].s); ;}
break;
- case 96:
-#line 420 "engines/director/lingo/lingo-gr.y"
+ case 97:
+#line 429 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(3) - (3)].s)->c_str()); delete (yyvsp[(3) - (3)].s); ;}
break;
- case 97:
-#line 431 "engines/director/lingo/lingo-gr.y"
+ case 98:
+#line 440 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gotoloop); ;}
break;
- case 98:
-#line 432 "engines/director/lingo/lingo-gr.y"
+ case 99:
+#line 441 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gotonext); ;}
break;
- case 99:
-#line 433 "engines/director/lingo/lingo-gr.y"
+ case 100:
+#line 442 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gotoprevious); ;}
break;
- case 100:
-#line 434 "engines/director/lingo/lingo-gr.y"
+ case 101:
+#line 443 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(1);
g_lingo->code1(g_lingo->c_goto); ;}
break;
- case 101:
-#line 437 "engines/director/lingo/lingo-gr.y"
+ case 102:
+#line 446 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(3);
g_lingo->code1(g_lingo->c_goto); ;}
break;
- case 102:
-#line 440 "engines/director/lingo/lingo-gr.y"
+ case 103:
+#line 449 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(2);
g_lingo->code1(g_lingo->c_goto); ;}
break;
- case 107:
-#line 453 "engines/director/lingo/lingo-gr.y"
+ case 108:
+#line 462 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_playdone); ;}
break;
- case 108:
-#line 454 "engines/director/lingo/lingo-gr.y"
+ case 109:
+#line 463 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(1);
g_lingo->code1(g_lingo->c_play); ;}
break;
- case 109:
-#line 457 "engines/director/lingo/lingo-gr.y"
+ case 110:
+#line 466 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(3);
g_lingo->code1(g_lingo->c_play); ;}
break;
- case 110:
-#line 460 "engines/director/lingo/lingo-gr.y"
+ case 111:
+#line 469 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(2);
g_lingo->code1(g_lingo->c_play); ;}
break;
- case 111:
-#line 490 "engines/director/lingo/lingo-gr.y"
+ case 112:
+#line 499 "engines/director/lingo/lingo-gr.y"
{ g_lingo->_indef = true; g_lingo->_currentFactory.clear(); ;}
break;
- case 112:
-#line 491 "engines/director/lingo/lingo-gr.y"
+ case 113:
+#line 500 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(0); // Push fake value on stack
g_lingo->code1(g_lingo->c_procret);
@@ -2458,53 +2475,53 @@ yyreduce:
g_lingo->_indef = false; ;}
break;
- case 113:
-#line 496 "engines/director/lingo/lingo-gr.y"
+ case 114:
+#line 505 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeFactory(*(yyvsp[(2) - (2)].s));
;}
break;
- case 114:
-#line 499 "engines/director/lingo/lingo-gr.y"
+ case 115:
+#line 508 "engines/director/lingo/lingo-gr.y"
{ g_lingo->_indef = true; ;}
break;
- case 115:
-#line 500 "engines/director/lingo/lingo-gr.y"
+ case 116:
+#line 509 "engines/director/lingo/lingo-gr.y"
{
- g_lingo->code1(STOP);
+ g_lingo->code1(g_lingo->c_procret);
g_lingo->define(*(yyvsp[(2) - (8)].s), (yyvsp[(4) - (8)].code), (yyvsp[(5) - (8)].narg) + 1, &g_lingo->_currentFactory);
g_lingo->_indef = false; ;}
break;
- case 116:
-#line 504 "engines/director/lingo/lingo-gr.y"
+ case 117:
+#line 513 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = 0; ;}
break;
- case 117:
-#line 505 "engines/director/lingo/lingo-gr.y"
+ case 118:
+#line 514 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArg((yyvsp[(1) - (1)].s)); (yyval.narg) = 1; ;}
break;
- case 118:
-#line 506 "engines/director/lingo/lingo-gr.y"
+ case 119:
+#line 515 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArg((yyvsp[(3) - (3)].s)); (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;}
break;
- case 119:
-#line 507 "engines/director/lingo/lingo-gr.y"
+ case 120:
+#line 516 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArg((yyvsp[(4) - (4)].s)); (yyval.narg) = (yyvsp[(1) - (4)].narg) + 1; ;}
break;
- case 120:
-#line 509 "engines/director/lingo/lingo-gr.y"
+ case 121:
+#line 518 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArgStore(); ;}
break;
- case 121:
-#line 513 "engines/director/lingo/lingo-gr.y"
+ case 122:
+#line 522 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code1(g_lingo->c_call);
g_lingo->codeString((yyvsp[(1) - (3)].s)->c_str());
@@ -2513,24 +2530,24 @@ yyreduce:
g_lingo->code1(numpar); ;}
break;
- case 122:
-#line 521 "engines/director/lingo/lingo-gr.y"
+ case 123:
+#line 530 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = 0; ;}
break;
- case 123:
-#line 522 "engines/director/lingo/lingo-gr.y"
+ case 124:
+#line 531 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = 1; ;}
break;
- case 124:
-#line 523 "engines/director/lingo/lingo-gr.y"
+ case 125:
+#line 532 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;}
break;
/* Line 1267 of yacc.c. */
-#line 2534 "engines/director/lingo/lingo-gr.cpp"
+#line 2551 "engines/director/lingo/lingo-gr.cpp"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
@@ -2744,6 +2761,6 @@ yyreturn:
}
-#line 526 "engines/director/lingo/lingo-gr.y"
+#line 535 "engines/director/lingo/lingo-gr.y"
diff --git a/engines/director/lingo/lingo-gr.y b/engines/director/lingo/lingo-gr.y
index ea66bc6fd9..f74244e5fd 100644
--- a/engines/director/lingo/lingo-gr.y
+++ b/engines/director/lingo/lingo-gr.y
@@ -90,7 +90,7 @@ void yyerror(char *s) {
%token tCONCAT tCONTAINS tSTARTS
%token tSPRITE tINTERSECTS tWITHIN
-%type<code> asgn begin elseif elsestmtoneliner end expr if repeatwhile repeatwith stmtlist
+%type<code> asgn begin elseif elsestmtoneliner end expr if when repeatwhile repeatwith stmtlist
%type<narg> argdef arglist
%right '='
@@ -217,8 +217,11 @@ stmt: stmtoneliner
(*g_lingo->_currentScript)[$1 + 3] = body; /* body of loop */
(*g_lingo->_currentScript)[$1 + 4] = inc; /* increment */
(*g_lingo->_currentScript)[$1 + 5] = end; } /* end, if cond fails */
- | tWHEN ID tTHEN expr {
- g_lingo->code1(g_lingo->c_ifcode);
+ | when expr end {
+ inst end = 0;
+ WRITE_UINT32(&end, $3);
+ g_lingo->code1(STOP);
+ (*g_lingo->_currentScript)[$1 + 1] = end;
}
;
@@ -341,6 +344,12 @@ stmtlist: /* nothing */ { $$ = g_lingo->_currentScript->size(); }
| stmtlist stmt
;
+when: tWHEN ID tTHEN {
+ $$ = g_lingo->code1(g_lingo->c_whencode);
+ g_lingo->code1(STOP);
+ g_lingo->codeString($2->c_str());
+ delete $2; }
+
expr: INT { $$ = g_lingo->codeConst($1); }
| FLOAT {
$$ = g_lingo->code1(g_lingo->c_fconstpush);
@@ -498,7 +507,7 @@ defn: tMACRO ID { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); }
}
| tMETHOD ID { g_lingo->_indef = true; }
begin argdef nl argstore stmtlist {
- g_lingo->code1(STOP);
+ g_lingo->code1(g_lingo->c_procret);
g_lingo->define(*$2, $4, $5 + 1, &g_lingo->_currentFactory);
g_lingo->_indef = false; } ;
argdef: /* nothing */ { $$ = 0; }
diff --git a/engines/director/lingo/lingo-lex.cpp b/engines/director/lingo/lingo-lex.cpp
index 5fbd8d8653..6953c2b96f 100644
--- a/engines/director/lingo/lingo-lex.cpp
+++ b/engines/director/lingo/lingo-lex.cpp
@@ -364,8 +364,8 @@ static void yy_fatal_error (yyconst char msg[] );
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
-#define YY_NUM_RULES 56
-#define YY_END_OF_BUFFER 57
+#define YY_NUM_RULES 57
+#define YY_END_OF_BUFFER 58
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@@ -373,28 +373,30 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
-static yyconst flex_int16_t yy_accept[191] =
+static yyconst flex_int16_t yy_accept[199] =
{ 0,
- 0, 0, 57, 55, 3, 53, 53, 55, 55, 52,
- 52, 52, 51, 52, 52, 49, 49, 49, 49, 49,
- 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
- 49, 49, 2, 2, 3, 53, 0, 0, 0, 0,
- 0, 54, 48, 1, 50, 51, 47, 45, 46, 49,
- 49, 49, 49, 49, 49, 49, 49, 49, 49, 18,
- 8, 49, 49, 49, 49, 49, 49, 49, 27, 49,
- 29, 49, 49, 49, 49, 49, 49, 49, 49, 39,
- 49, 49, 2, 2, 0, 1, 50, 4, 49, 49,
- 49, 49, 12, 49, 49, 49, 49, 0, 49, 49,
-
- 49, 49, 49, 49, 26, 49, 49, 49, 32, 49,
- 34, 49, 49, 49, 49, 49, 49, 0, 49, 6,
- 7, 11, 14, 49, 49, 49, 0, 49, 20, 21,
- 49, 49, 49, 25, 28, 30, 49, 49, 49, 49,
- 0, 38, 43, 49, 41, 10, 49, 49, 15, 49,
- 17, 49, 22, 49, 24, 49, 49, 49, 49, 37,
- 44, 49, 0, 49, 49, 16, 49, 23, 49, 33,
- 40, 35, 0, 42, 0, 49, 13, 49, 49, 0,
- 9, 5, 49, 31, 0, 49, 0, 19, 36, 0
+ 0, 0, 58, 56, 3, 54, 54, 56, 56, 53,
+ 53, 53, 52, 53, 53, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 2, 2, 3, 54, 0, 0, 0, 0,
+ 0, 55, 49, 1, 51, 52, 48, 46, 47, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 18,
+ 8, 50, 50, 50, 50, 50, 50, 50, 27, 50,
+ 29, 50, 50, 50, 50, 50, 50, 50, 50, 40,
+ 50, 50, 2, 2, 0, 1, 51, 4, 50, 50,
+ 50, 50, 12, 50, 50, 50, 50, 0, 50, 50,
+
+ 50, 50, 50, 50, 26, 50, 50, 50, 32, 50,
+ 34, 50, 50, 50, 50, 50, 50, 0, 50, 6,
+ 7, 11, 14, 50, 50, 50, 0, 50, 20, 21,
+ 50, 50, 50, 25, 28, 30, 50, 50, 50, 50,
+ 0, 39, 44, 50, 42, 10, 50, 50, 15, 50,
+ 17, 50, 22, 50, 24, 50, 50, 50, 50, 38,
+ 38, 45, 50, 0, 50, 50, 16, 50, 23, 50,
+ 33, 41, 35, 0, 38, 43, 0, 50, 13, 50,
+ 50, 0, 38, 9, 5, 50, 31, 0, 38, 50,
+ 0, 0, 19, 37, 0, 0, 36, 0
+
} ;
static yyconst flex_int32_t yy_ec[256] =
@@ -407,12 +409,12 @@ static yyconst flex_int32_t yy_ec[256] =
11, 11, 11, 11, 11, 11, 11, 7, 1, 12,
13, 14, 1, 1, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 24, 25, 26, 27, 28, 29,
- 24, 30, 31, 32, 33, 34, 35, 36, 37, 24,
- 1, 1, 1, 7, 38, 1, 39, 40, 41, 42,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 24,
+ 1, 1, 1, 7, 39, 1, 40, 41, 42, 43,
- 43, 44, 45, 46, 47, 24, 24, 48, 49, 50,
- 51, 52, 24, 53, 54, 55, 56, 57, 58, 59,
- 60, 24, 1, 1, 1, 1, 1, 1, 1, 1,
+ 44, 45, 46, 47, 48, 24, 24, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 24, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -429,130 +431,139 @@ static yyconst flex_int32_t yy_ec[256] =
1, 1, 1, 1, 1
} ;
-static yyconst flex_int32_t yy_meta[61] =
+static yyconst flex_int32_t yy_meta[63] =
{ 0,
1, 2, 3, 3, 2, 1, 1, 1, 1, 1,
4, 1, 1, 1, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 4, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+ 5, 5
} ;
-static yyconst flex_int16_t yy_base[196] =
+static yyconst flex_int16_t yy_base[204] =
{ 0,
- 0, 59, 203, 452, 63, 67, 71, 75, 167, 452,
- 157, 154, 52, 68, 143, 56, 0, 56, 57, 67,
- 72, 68, 68, 69, 85, 102, 102, 104, 80, 119,
- 113, 120, 173, 177, 181, 452, 185, 189, 193, 102,
- 99, 452, 452, 0, 80, 129, 452, 452, 452, 0,
- 91, 120, 165, 118, 126, 146, 184, 187, 171, 96,
- 0, 172, 177, 189, 178, 177, 178, 184, 0, 188,
- 0, 202, 199, 188, 192, 192, 199, 220, 219, 0,
- 226, 208, 251, 262, 217, 0, 78, 0, 219, 227,
- 230, 239, 0, 228, 229, 242, 256, 273, 257, 250,
-
- 251, 255, 263, 256, 0, 262, 253, 258, 0, 274,
- 0, 271, 267, 304, 271, 274, 273, 284, 299, 0,
- 0, 0, 0, 279, 297, 308, 297, 296, 0, 0,
- 301, 304, 314, 0, 0, 0, 311, 320, 305, 307,
- 156, 0, 0, 322, 319, 341, 321, 320, 0, 326,
- 452, 322, 0, 327, 0, 328, 329, 344, 336, 370,
- 0, 343, 375, 344, 341, 0, 345, 0, 348, 0,
- 0, 0, 380, 0, 363, 355, 0, 372, 360, 372,
- 452, 0, 363, 0, 394, 366, 398, 0, 400, 452,
- 431, 433, 438, 442, 446
-
+ 0, 61, 156, 509, 65, 69, 73, 77, 148, 509,
+ 104, 99, 54, 70, 91, 58, 0, 58, 59, 69,
+ 74, 70, 70, 71, 88, 105, 105, 110, 82, 118,
+ 117, 130, 177, 181, 185, 509, 189, 193, 197, 106,
+ 94, 509, 509, 0, 82, 132, 509, 509, 509, 0,
+ 84, 119, 169, 116, 120, 174, 186, 191, 180, 171,
+ 0, 176, 182, 194, 181, 180, 181, 186, 0, 202,
+ 0, 208, 205, 193, 198, 197, 204, 225, 225, 0,
+ 233, 214, 258, 270, 223, 0, 80, 0, 225, 226,
+ 237, 234, 0, 233, 234, 242, 260, 278, 265, 257,
+
+ 258, 265, 271, 263, 0, 270, 260, 264, 0, 281,
+ 0, 279, 273, 303, 280, 289, 293, 297, 303, 0,
+ 0, 0, 0, 296, 307, 317, 301, 303, 0, 0,
+ 308, 309, 320, 0, 0, 0, 319, 329, 313, 314,
+ 347, 0, 0, 331, 332, 202, 333, 328, 0, 337,
+ 509, 331, 0, 347, 0, 340, 339, 347, 341, 301,
+ 372, 0, 349, 383, 357, 351, 0, 363, 0, 359,
+ 0, 0, 0, 393, 399, 0, 374, 367, 0, 392,
+ 373, 390, 409, 509, 0, 379, 0, 413, 420, 384,
+ 422, 436, 0, 441, 408, 442, 446, 509, 488, 490,
+
+ 495, 499, 503
} ;
-static yyconst flex_int16_t yy_def[196] =
+static yyconst flex_int16_t yy_def[204] =
{ 0,
- 190, 1, 190, 190, 190, 190, 190, 190, 191, 190,
- 190, 190, 190, 190, 190, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 190, 190, 190, 190, 190, 190, 190, 190,
- 191, 190, 190, 193, 190, 190, 190, 190, 190, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 190, 190, 190, 193, 190, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 190, 192, 192,
-
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 190, 192, 192,
- 192, 192, 192, 192, 192, 192, 190, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 194, 192, 192, 192, 192, 190, 192, 192, 192, 192,
- 190, 192, 192, 192, 192, 192, 192, 192, 192, 194,
- 192, 192, 190, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 190, 192, 190, 192, 192, 192, 192, 190,
- 190, 192, 192, 192, 190, 192, 195, 192, 195, 0,
- 190, 190, 190, 190, 190
-
+ 198, 1, 198, 198, 198, 198, 198, 198, 199, 198,
+ 198, 198, 198, 198, 198, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 198, 198, 198, 198, 198, 198, 198, 198,
+ 199, 198, 198, 201, 198, 198, 198, 198, 198, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 198, 198, 198, 201, 198, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 198, 200, 200,
+
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 198, 200, 200,
+ 200, 200, 200, 200, 200, 200, 198, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 202, 200, 200, 200, 200, 198, 200, 200, 200, 200,
+ 198, 200, 200, 200, 200, 200, 200, 200, 200, 202,
+ 202, 200, 200, 198, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 198, 202, 200, 198, 200, 200, 200,
+ 200, 198, 202, 198, 200, 200, 200, 198, 202, 200,
+ 203, 198, 200, 203, 198, 198, 203, 0, 198, 198,
+
+ 198, 198, 198
} ;
-static yyconst flex_int16_t yy_nxt[513] =
+static yyconst flex_int16_t yy_nxt[572] =
{ 0,
4, 5, 6, 7, 8, 9, 10, 11, 12, 4,
13, 14, 10, 15, 16, 17, 18, 19, 20, 21,
- 22, 17, 23, 17, 24, 25, 26, 27, 28, 29,
- 30, 31, 17, 17, 32, 17, 17, 17, 16, 17,
- 18, 19, 20, 21, 22, 17, 23, 24, 25, 26,
- 27, 28, 29, 30, 31, 17, 17, 32, 17, 17,
- 33, 45, 46, 34, 35, 36, 36, 37, 38, 39,
- 39, 38, 38, 39, 39, 38, 37, 36, 36, 37,
- 47, 48, 51, 52, 53, 40, 57, 61, 87, 40,
- 87, 54, 59, 55, 62, 60, 63, 98, 75, 64,
-
- 98, 58, 56, 65, 42, 51, 52, 53, 88, 40,
- 57, 61, 66, 40, 54, 59, 55, 62, 60, 63,
- 67, 69, 75, 64, 58, 56, 85, 65, 72, 68,
- 70, 71, 88, 73, 79, 66, 74, 76, 45, 46,
- 80, 81, 82, 93, 67, 69, 89, 77, 92, 85,
- 78, 72, 68, 70, 71, 49, 73, 141, 79, 74,
- 141, 76, 44, 80, 43, 81, 82, 93, 94, 89,
- 77, 92, 42, 78, 83, 36, 36, 84, 84, 36,
- 36, 84, 35, 36, 36, 37, 37, 36, 36, 37,
- 38, 90, 94, 38, 38, 39, 39, 38, 97, 91,
-
- 95, 96, 190, 99, 100, 101, 106, 40, 190, 102,
- 103, 40, 190, 104, 90, 105, 107, 108, 190, 109,
- 110, 97, 91, 111, 95, 96, 99, 100, 112, 101,
- 106, 40, 102, 103, 113, 40, 104, 114, 105, 117,
- 107, 108, 109, 110, 115, 120, 111, 118, 116, 190,
- 119, 112, 83, 36, 36, 84, 121, 122, 113, 123,
- 124, 114, 117, 84, 36, 36, 84, 125, 115, 120,
- 118, 126, 116, 119, 98, 128, 132, 98, 130, 121,
- 131, 122, 123, 124, 129, 133, 190, 134, 135, 136,
- 125, 137, 138, 139, 145, 126, 140, 143, 144, 128,
-
- 132, 130, 146, 131, 127, 141, 148, 129, 141, 133,
- 134, 135, 136, 147, 137, 149, 138, 139, 145, 140,
- 143, 144, 150, 190, 151, 152, 146, 127, 153, 148,
- 142, 154, 155, 156, 157, 190, 158, 147, 159, 149,
- 161, 162, 163, 164, 168, 163, 150, 151, 152, 165,
- 166, 153, 167, 142, 154, 169, 155, 156, 157, 158,
- 170, 159, 171, 178, 161, 162, 172, 164, 168, 174,
- 176, 173, 165, 166, 173, 167, 163, 177, 169, 163,
- 179, 173, 181, 170, 173, 182, 171, 178, 183, 172,
- 184, 185, 174, 176, 186, 187, 188, 175, 187, 187,
-
- 177, 190, 187, 179, 190, 190, 181, 180, 182, 190,
- 190, 190, 183, 184, 190, 185, 190, 186, 190, 188,
- 190, 175, 190, 190, 190, 190, 190, 190, 190, 190,
- 180, 41, 41, 190, 41, 41, 50, 50, 86, 86,
- 190, 86, 86, 160, 190, 190, 160, 189, 190, 190,
- 189, 3, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
-
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190
+ 22, 17, 23, 17, 24, 25, 26, 27, 28, 17,
+ 29, 30, 31, 17, 17, 32, 17, 17, 17, 16,
+ 17, 18, 19, 20, 21, 22, 17, 23, 24, 25,
+ 26, 27, 28, 17, 29, 30, 31, 17, 17, 32,
+ 17, 17, 33, 45, 46, 34, 35, 36, 36, 37,
+ 38, 39, 39, 38, 38, 39, 39, 38, 37, 36,
+ 36, 37, 47, 48, 51, 52, 53, 40, 57, 61,
+ 87, 40, 87, 54, 59, 55, 62, 60, 63, 42,
+
+ 75, 88, 64, 49, 58, 56, 65, 44, 51, 52,
+ 53, 43, 40, 57, 61, 66, 40, 54, 59, 55,
+ 62, 60, 63, 67, 69, 75, 88, 64, 58, 56,
+ 85, 65, 68, 70, 72, 71, 76, 93, 79, 66,
+ 73, 45, 46, 74, 80, 89, 77, 92, 67, 69,
+ 78, 81, 82, 42, 85, 198, 68, 70, 72, 71,
+ 198, 76, 93, 79, 73, 198, 198, 74, 80, 89,
+ 77, 92, 98, 198, 78, 98, 81, 82, 83, 36,
+ 36, 84, 84, 36, 36, 84, 35, 36, 36, 37,
+ 37, 36, 36, 37, 38, 90, 94, 38, 38, 39,
+
+ 39, 38, 95, 164, 91, 96, 164, 97, 99, 100,
+ 101, 40, 198, 102, 103, 40, 198, 104, 105, 90,
+ 106, 94, 107, 108, 198, 109, 110, 95, 91, 111,
+ 96, 97, 99, 100, 112, 101, 40, 102, 103, 113,
+ 40, 104, 105, 114, 120, 106, 117, 107, 108, 109,
+ 110, 115, 122, 111, 118, 116, 198, 119, 112, 83,
+ 36, 36, 84, 121, 113, 123, 124, 125, 114, 120,
+ 117, 84, 36, 36, 84, 126, 115, 122, 118, 98,
+ 116, 119, 98, 128, 198, 130, 132, 121, 131, 123,
+ 124, 125, 129, 133, 198, 134, 135, 136, 137, 138,
+
+ 126, 139, 174, 140, 141, 174, 143, 141, 128, 130,
+ 127, 132, 131, 144, 145, 146, 129, 147, 133, 134,
+ 135, 136, 137, 148, 138, 149, 139, 140, 151, 142,
+ 143, 150, 198, 152, 127, 153, 154, 144, 155, 145,
+ 146, 156, 147, 157, 198, 158, 159, 148, 141, 162,
+ 149, 141, 151, 142, 163, 165, 150, 152, 166, 153,
+ 154, 167, 168, 155, 169, 172, 156, 170, 157, 158,
+ 159, 171, 173, 174, 162, 176, 174, 198, 161, 163,
+ 165, 180, 166, 178, 164, 167, 168, 164, 179, 169,
+ 172, 170, 181, 184, 174, 171, 173, 174, 185, 176,
+
+ 174, 175, 161, 174, 187, 177, 180, 178, 186, 188,
+ 174, 190, 179, 174, 191, 193, 181, 191, 184, 198,
+ 182, 192, 185, 191, 192, 175, 191, 196, 187, 183,
+ 177, 198, 198, 186, 188, 190, 198, 192, 198, 193,
+ 192, 189, 198, 197, 182, 198, 197, 197, 198, 198,
+ 197, 198, 196, 183, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 195, 198, 189, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 195, 41, 41,
+ 198, 41, 41, 50, 50, 86, 86, 198, 86, 86,
+
+ 160, 198, 198, 160, 194, 198, 198, 194, 3, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198
} ;
-static yyconst flex_int16_t yy_chk[513] =
+static yyconst flex_int16_t yy_chk[572] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -560,57 +571,63 @@ static yyconst flex_int16_t yy_chk[513] =
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 2, 13, 13, 2, 5, 5, 5, 5, 6, 6,
- 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
- 14, 14, 16, 18, 19, 6, 21, 23, 87, 7,
- 45, 20, 22, 20, 23, 22, 24, 60, 29, 25,
-
- 60, 21, 20, 25, 41, 16, 18, 19, 51, 6,
- 21, 23, 25, 7, 20, 22, 20, 23, 22, 24,
- 26, 27, 29, 25, 21, 20, 40, 25, 28, 26,
- 27, 27, 51, 28, 31, 25, 28, 30, 46, 46,
- 31, 32, 32, 55, 26, 27, 52, 30, 54, 40,
- 30, 28, 26, 27, 27, 15, 28, 141, 31, 28,
- 141, 30, 12, 31, 11, 32, 32, 55, 56, 52,
- 30, 54, 9, 30, 33, 33, 33, 33, 34, 34,
- 34, 34, 35, 35, 35, 35, 37, 37, 37, 37,
- 38, 53, 56, 38, 39, 39, 39, 39, 59, 53,
-
- 57, 58, 3, 62, 63, 64, 70, 38, 0, 65,
- 66, 39, 0, 67, 53, 68, 72, 73, 0, 74,
- 75, 59, 53, 76, 57, 58, 62, 63, 77, 64,
- 70, 38, 65, 66, 78, 39, 67, 79, 68, 82,
- 72, 73, 74, 75, 81, 90, 76, 85, 81, 0,
- 89, 77, 83, 83, 83, 83, 91, 92, 78, 94,
- 95, 79, 82, 84, 84, 84, 84, 96, 81, 90,
- 85, 97, 81, 89, 98, 99, 102, 98, 100, 91,
- 101, 92, 94, 95, 99, 103, 0, 104, 106, 107,
- 96, 108, 110, 112, 117, 97, 113, 115, 116, 99,
-
- 102, 100, 118, 101, 98, 114, 124, 99, 114, 103,
- 104, 106, 107, 119, 108, 125, 110, 112, 117, 113,
- 115, 116, 126, 0, 127, 128, 118, 98, 131, 124,
- 114, 132, 133, 137, 138, 0, 139, 119, 140, 125,
- 144, 145, 146, 147, 154, 146, 126, 127, 128, 148,
- 150, 131, 152, 114, 132, 156, 133, 137, 138, 139,
- 157, 140, 158, 167, 144, 145, 159, 147, 154, 162,
- 164, 160, 148, 150, 160, 152, 163, 165, 156, 163,
- 169, 173, 175, 157, 173, 176, 158, 167, 178, 159,
- 179, 180, 162, 164, 183, 185, 186, 163, 185, 187,
-
- 165, 189, 187, 169, 189, 0, 175, 173, 176, 0,
- 0, 0, 178, 179, 0, 180, 0, 183, 0, 186,
- 0, 163, 0, 0, 0, 0, 0, 0, 0, 0,
- 173, 191, 191, 0, 191, 191, 192, 192, 193, 193,
- 0, 193, 193, 194, 0, 0, 194, 195, 0, 0,
- 195, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
-
- 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
- 190, 190
+ 1, 1, 2, 13, 13, 2, 5, 5, 5, 5,
+ 6, 6, 6, 6, 7, 7, 7, 7, 8, 8,
+ 8, 8, 14, 14, 16, 18, 19, 6, 21, 23,
+ 87, 7, 45, 20, 22, 20, 23, 22, 24, 41,
+
+ 29, 51, 25, 15, 21, 20, 25, 12, 16, 18,
+ 19, 11, 6, 21, 23, 25, 7, 20, 22, 20,
+ 23, 22, 24, 26, 27, 29, 51, 25, 21, 20,
+ 40, 25, 26, 27, 28, 27, 30, 55, 31, 25,
+ 28, 46, 46, 28, 31, 52, 30, 54, 26, 27,
+ 30, 32, 32, 9, 40, 3, 26, 27, 28, 27,
+ 0, 30, 55, 31, 28, 0, 0, 28, 31, 52,
+ 30, 54, 60, 0, 30, 60, 32, 32, 33, 33,
+ 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
+ 37, 37, 37, 37, 38, 53, 56, 38, 39, 39,
+
+ 39, 39, 57, 146, 53, 58, 146, 59, 62, 63,
+ 64, 38, 0, 65, 66, 39, 0, 67, 68, 53,
+ 70, 56, 72, 73, 0, 74, 75, 57, 53, 76,
+ 58, 59, 62, 63, 77, 64, 38, 65, 66, 78,
+ 39, 67, 68, 79, 90, 70, 82, 72, 73, 74,
+ 75, 81, 92, 76, 85, 81, 0, 89, 77, 83,
+ 83, 83, 83, 91, 78, 94, 95, 96, 79, 90,
+ 82, 84, 84, 84, 84, 97, 81, 92, 85, 98,
+ 81, 89, 98, 99, 0, 100, 102, 91, 101, 94,
+ 95, 96, 99, 103, 0, 104, 106, 107, 108, 110,
+
+ 97, 112, 160, 113, 114, 160, 115, 114, 99, 100,
+ 98, 102, 101, 116, 117, 118, 99, 119, 103, 104,
+ 106, 107, 108, 124, 110, 125, 112, 113, 127, 114,
+ 115, 126, 0, 128, 98, 131, 132, 116, 133, 117,
+ 118, 137, 119, 138, 0, 139, 140, 124, 141, 144,
+ 125, 141, 127, 114, 145, 147, 126, 128, 148, 131,
+ 132, 150, 152, 133, 154, 158, 137, 156, 138, 139,
+ 140, 157, 159, 161, 144, 163, 161, 0, 141, 145,
+ 147, 168, 148, 165, 164, 150, 152, 164, 166, 154,
+ 158, 156, 170, 177, 174, 157, 159, 174, 178, 163,
+
+ 175, 161, 141, 175, 181, 164, 168, 165, 180, 182,
+ 183, 186, 166, 183, 188, 190, 170, 188, 177, 0,
+ 174, 189, 178, 191, 189, 161, 191, 195, 181, 175,
+ 164, 0, 0, 180, 182, 186, 0, 192, 0, 190,
+ 192, 183, 194, 196, 174, 194, 196, 197, 0, 0,
+ 197, 0, 195, 175, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 192, 0, 183, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 192, 199, 199,
+ 0, 199, 199, 200, 200, 201, 201, 0, 201, 201,
+
+ 202, 0, 0, 202, 203, 0, 0, 203, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198, 198, 198, 198, 198, 198, 198, 198, 198, 198,
+ 198
} ;
static yy_state_type yy_last_accepting_state;
@@ -684,7 +701,7 @@ static void countnl() {
g_lingo->_colnumber = strlen(p);
}
-#line 688 "engines/director/lingo/lingo-lex.cpp"
+#line 705 "engines/director/lingo/lingo-lex.cpp"
#define INITIAL 0
@@ -872,7 +889,7 @@ YY_DECL
#line 69 "engines/director/lingo/lingo-lex.l"
-#line 876 "engines/director/lingo/lingo-lex.cpp"
+#line 893 "engines/director/lingo/lingo-lex.cpp"
if ( !(yy_init) )
{
@@ -926,13 +943,13 @@ yy_match:
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 191 )
+ if ( yy_current_state >= 199 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
++yy_cp;
}
- while ( yy_base[yy_current_state] != 452 );
+ while ( yy_base[yy_current_state] != 509 );
yy_find_action:
yy_act = yy_accept[yy_current_state];
@@ -1139,6 +1156,18 @@ YY_RULE_SETUP
{
count();
+ yylval.e[0] = g_lingo->_theEntities["sqrt"]->entity;
+ yylval.e[1] = 0; // No field
+
+ return THEENTITYWITHID;
+ }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 115 "engines/director/lingo/lingo-lex.l"
+{
+ count();
+
const char *ptr = &yytext[4]; // Skip 'the '
while (*ptr == ' ' || *ptr == '\t')
ptr++;
@@ -1177,9 +1206,9 @@ YY_RULE_SETUP
warning("Unhandled the entity %s", ptr);
}
YY_BREAK
-case 37:
+case 38:
YY_RULE_SETUP
-#line 147 "engines/director/lingo/lingo-lex.l"
+#line 155 "engines/director/lingo/lingo-lex.l"
{
count();
@@ -1200,64 +1229,64 @@ YY_RULE_SETUP
warning("Unhandled the entity %s", ptr);
}
YY_BREAK
-case 38:
+case 39:
YY_RULE_SETUP
-#line 166 "engines/director/lingo/lingo-lex.l"
+#line 174 "engines/director/lingo/lingo-lex.l"
{ count(); return tTHEN; }
YY_BREAK
-case 39:
+case 40:
YY_RULE_SETUP
-#line 167 "engines/director/lingo/lingo-lex.l"
+#line 175 "engines/director/lingo/lingo-lex.l"
{ count(); return tTO; }
YY_BREAK
-case 40:
+case 41:
YY_RULE_SETUP
-#line 168 "engines/director/lingo/lingo-lex.l"
+#line 176 "engines/director/lingo/lingo-lex.l"
{ count(); return tSPRITE; }
YY_BREAK
-case 41:
+case 42:
YY_RULE_SETUP
-#line 169 "engines/director/lingo/lingo-lex.l"
+#line 177 "engines/director/lingo/lingo-lex.l"
{ count(); return tWITH; }
YY_BREAK
-case 42:
+case 43:
YY_RULE_SETUP
-#line 170 "engines/director/lingo/lingo-lex.l"
+#line 178 "engines/director/lingo/lingo-lex.l"
{ count(); return tWITHIN; }
YY_BREAK
-case 43:
+case 44:
YY_RULE_SETUP
-#line 171 "engines/director/lingo/lingo-lex.l"
+#line 179 "engines/director/lingo/lingo-lex.l"
{ count(); return tWHEN; }
YY_BREAK
-case 44:
+case 45:
YY_RULE_SETUP
-#line 172 "engines/director/lingo/lingo-lex.l"
+#line 180 "engines/director/lingo/lingo-lex.l"
{ count(); return tWHILE; }
YY_BREAK
-case 45:
+case 46:
YY_RULE_SETUP
-#line 174 "engines/director/lingo/lingo-lex.l"
+#line 182 "engines/director/lingo/lingo-lex.l"
{ count(); return tNEQ; }
YY_BREAK
-case 46:
+case 47:
YY_RULE_SETUP
-#line 175 "engines/director/lingo/lingo-lex.l"
+#line 183 "engines/director/lingo/lingo-lex.l"
{ count(); return tGE; }
YY_BREAK
-case 47:
+case 48:
YY_RULE_SETUP
-#line 176 "engines/director/lingo/lingo-lex.l"
+#line 184 "engines/director/lingo/lingo-lex.l"
{ count(); return tLE; }
YY_BREAK
-case 48:
+case 49:
YY_RULE_SETUP
-#line 177 "engines/director/lingo/lingo-lex.l"
+#line 185 "engines/director/lingo/lingo-lex.l"
{ count(); return tCONCAT; }
YY_BREAK
-case 49:
+case 50:
YY_RULE_SETUP
-#line 179 "engines/director/lingo/lingo-lex.l"
+#line 187 "engines/director/lingo/lingo-lex.l"
{
count();
yylval.s = new Common::String(yytext);
@@ -1285,43 +1314,43 @@ YY_RULE_SETUP
return ID;
}
YY_BREAK
-case 50:
+case 51:
YY_RULE_SETUP
-#line 205 "engines/director/lingo/lingo-lex.l"
+#line 213 "engines/director/lingo/lingo-lex.l"
{ count(); yylval.f = atof(yytext); return FLOAT; }
YY_BREAK
-case 51:
+case 52:
YY_RULE_SETUP
-#line 206 "engines/director/lingo/lingo-lex.l"
+#line 214 "engines/director/lingo/lingo-lex.l"
{ count(); yylval.i = strtol(yytext, NULL, 10); return INT; }
YY_BREAK
-case 52:
+case 53:
YY_RULE_SETUP
-#line 207 "engines/director/lingo/lingo-lex.l"
+#line 215 "engines/director/lingo/lingo-lex.l"
{ count(); return *yytext; }
YY_BREAK
-case 53:
-/* rule 53 can match eol */
+case 54:
+/* rule 54 can match eol */
YY_RULE_SETUP
-#line 208 "engines/director/lingo/lingo-lex.l"
+#line 216 "engines/director/lingo/lingo-lex.l"
{ return '\n'; }
YY_BREAK
-case 54:
+case 55:
YY_RULE_SETUP
-#line 209 "engines/director/lingo/lingo-lex.l"
+#line 217 "engines/director/lingo/lingo-lex.l"
{ count(); yylval.s = new Common::String(&yytext[1]); yylval.s->deleteLastChar(); return STRING; }
YY_BREAK
-case 55:
+case 56:
YY_RULE_SETUP
-#line 210 "engines/director/lingo/lingo-lex.l"
+#line 218 "engines/director/lingo/lingo-lex.l"
YY_BREAK
-case 56:
+case 57:
YY_RULE_SETUP
-#line 212 "engines/director/lingo/lingo-lex.l"
+#line 220 "engines/director/lingo/lingo-lex.l"
ECHO;
YY_BREAK
-#line 1325 "engines/director/lingo/lingo-lex.cpp"
+#line 1354 "engines/director/lingo/lingo-lex.cpp"
case YY_STATE_EOF(INITIAL):
yyterminate();
@@ -1614,7 +1643,7 @@ static int yy_get_next_buffer (void)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 191 )
+ if ( yy_current_state >= 199 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1642,11 +1671,11 @@ static int yy_get_next_buffer (void)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 191 )
+ if ( yy_current_state >= 199 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
- yy_is_jam = (yy_current_state == 190);
+ yy_is_jam = (yy_current_state == 198);
return yy_is_jam ? 0 : yy_current_state;
}
@@ -2321,7 +2350,7 @@ void yyfree (void * ptr )
#define YYTABLES_NAME "yytables"
-#line 212 "engines/director/lingo/lingo-lex.l"
+#line 220 "engines/director/lingo/lingo-lex.l"
diff --git a/engines/director/lingo/lingo-lex.l b/engines/director/lingo/lingo-lex.l
index 8c89a99764..3fca137e38 100644
--- a/engines/director/lingo/lingo-lex.l
+++ b/engines/director/lingo/lingo-lex.l
@@ -104,6 +104,14 @@ whitespace [\t ]
(?i:repeat) { count(); return tREPEAT; }
(?i:set) { count(); return tSET; }
(?i:starts) { count(); return tSTARTS; }
+(?i:the[ \t]+sqrt[\t ]+of[\t ]+) {
+ count();
+
+ yylval.e[0] = g_lingo->_theEntities["sqrt"]->entity;
+ yylval.e[1] = 0; // No field
+
+ return THEENTITYWITHID;
+ }
(?i:the[ \t]+[[:alpha:]]+[\t ]+of[\t ]+[[:alpha:]]+) {
count();
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 3b00c092ec..760576ef03 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -28,47 +28,59 @@ class Sprite;
TheEntity entities[] = {
{ kTheCast, "cast", true },
- { kTheClickOn, "clickOn", false },
+ { kTheClickOn, "clickOn", false }, // D2 function
{ kTheColorDepth, "colorDepth", false },
- { kTheColorQD, "colorQD", false },
- { kTheCommandDown, "commandDown", false },
- { kTheControlDown, "controlDown", false },
- { kTheDoubleClick, "doubleClick", false },
+ { kTheColorQD, "colorQD", false }, // D2 f
+ { kTheCommandDown, "commandDown", false }, // D2 f
+ { kTheControlDown, "controlDown", false }, // D2 f
+ { kTheDoubleClick, "doubleClick", false }, // D2 f
{ kTheExitLock, "exitlock", false },
{ kTheFloatPrecision, "floatPrecision", false },
- { kTheFrame, "frame", false },
- { kTheFreeBlock, "freeBlock", false },
- { kTheFreeBytes, "freeBytes", false },
+ { kTheFrame, "frame", false }, // D2 f
+ { kTheFreeBlock, "freeBlock", false }, // D2 f
+ { kTheFreeBytes, "freeBytes", false }, // D2 f
{ kTheItemDelimiter, "itemDelimiter", false },
- { kTheKey, "key", false },
- { kTheKeyCode, "keyCode", false },
- { kTheLastClick, "lastClick", false },
- { kTheLastEvent, "lastEvent", false },
+ { kTheKey, "key", false }, // D2 f
+ { kTheKeyCode, "keyCode", false }, // D2 f
+ { kTheLastClick, "lastClick", false }, // D2 f
+ { kTheLastEvent, "lastEvent", false }, // D2 f
{ kTheLastFrame, "lastFrame", false },
+ { kTheLastKey, "lastKey", false }, // D2 f
+ { kTheLastRoll, "lastRoll", false }, // D2 f
+ { kTheMachineType, "machineType", false }, // D2 f
+ { kTheMemorySize, "memorySize", false }, // D2 f
{ kTheMenu, "menu", true },
{ kTheMenus, "menus", false },
{ kTheMenuItem, "menuitem", true },
{ kTheMenuItems, "menuitems", false },
- { kTheMouseDown, "mouseDown", false },
+ { kTheMouseDown, "mouseDown", false }, // D2 f
{ kTheMouseDownScript, "mouseDownScript", false },
- { kTheMouseH, "mouseh", false },
- { kTheMouseUp, "mouseUp", false },
+ { kTheMouseH, "mouseH", false }, // D2 f
+ { kTheMouseUp, "mouseUp", false }, // D2 f
{ kTheMouseUpScript, "mouseUpScript", false },
- { kTheMouseV, "mousev", false },
- { kTheMovie, "movie", false },
+ { kTheMouseV, "mouseV", false }, // D2 f
+ { kTheMovie, "movie", false }, // D2 f
{ kTheMultiSound, "multiSound", false },
- { kTheOptionDown, "optionDown", false },
- { kThePathName, "pathname", false },
+ { kTheOptionDown, "optionDown", false }, // D2 f
+ { kThePathName, "pathName", false }, // D2 f
+ { kThePauseState, "pauseState", false }, // D2 f
{ kThePerFrameHook, "perframehook", false },
{ kThePreloadEventAbort,"preloadEventAbort",false },
+ { kTheResult, "result", false }, // D2 f
{ kTheRightMouseDown, "rightMouseDown", false },
{ kTheRightMouseUp, "rightMouseUp", false },
{ kTheRomanLingo, "romanLingo", false },
- { kTheShiftDown, "shiftDown", false },
+ { kTheSelection, "selection", false }, // D2 f
+ { kTheShiftDown, "shiftDown", false }, // D2 f
{ kTheSprite, "sprite", true },
+ { kTheSqrt, "sqrt", false }, // D2 f
{ kTheStage, "stage", false },
- { kTheStillDown, "stillDown", false },
- { kTheTicks, "ticks", false },
+ { kTheStageBottom, "stageBottom", false }, // D2 f
+ { kTheStageLeft, "stageLeft", false }, // D2 f
+ { kTheStageRight, "stageRight", false }, // D2 f
+ { kTheStageTop, "stageTop", false }, // D2 f
+ { kTheStillDown, "stillDown", false }, // D2 f
+ { kTheTicks, "ticks", false }, // D2 f
{ kTheTimeoutLength, "timeoutlength", false },
{ kTheTimer, "timer", false },
{ kTheWindow, "window", false },
@@ -313,6 +325,7 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
break;
case kTheCast:
d = getTheCast(id, field);
+ break;
case kThePerFrameHook:
warning("STUB: getting the perframehook");
break;
@@ -320,6 +333,11 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
d.type = INT;
d.u.i = _floatPrecision;
break;
+ case kTheSqrt:
+ id.toFloat();
+ d.type = FLOAT;
+ d.u.f = sqrt(id.u.f);
+ break;
default:
warning("Unprocessed getting field %d of entity %d", field, entity);
d.type = VOID;
@@ -466,8 +484,8 @@ Datum Lingo::getTheCast(Datum &id1, int field) {
return d;
} else {
warning("The cast %d found", id);
- return d;
}
+
cast = _vm->_currentScore->_casts[id];
castInfo = _vm->_currentScore->_castsInfo[id];
diff --git a/engines/director/lingo/lingo-the.h b/engines/director/lingo/lingo-the.h
index 7a27c0ad84..468be64140 100644
--- a/engines/director/lingo/lingo-the.h
+++ b/engines/director/lingo/lingo-the.h
@@ -52,10 +52,15 @@ enum TheEntityType {
kTheClickOn,
kTheDoubleClick,
kTheLastClick,
- kTheLastFrame,
kTheLastEvent,
+ kTheLastFrame,
+ kTheLastKey,
+ kTheLastRoll,
+ kTheMachineType,
+ kTheMemorySize,
kTheMouseDown,
kTheMouseUp,
+ kThePauseState,
kTheRightMouseUp,
kTheRightMouseDown,
kTheStillDown,
@@ -63,7 +68,10 @@ enum TheEntityType {
kTheKeyCode,
kTheControlDown,
kTheCommandDown,
+ kTheResult,
+ kTheSelection,
kTheShiftDown,
+ kTheSqrt,
kTheOptionDown,
kTheColorDepth,
@@ -74,7 +82,11 @@ enum TheEntityType {
kTheMultiSound,
kThePreloadEventAbort,
kTheRomanLingo,
- kTheStage
+ kTheStage,
+ kTheStageBottom,
+ kTheStageLeft,
+ kTheStageRight,
+ kTheStageTop
};
enum TheFieldType {
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index f3faf975fe..d37da42670 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -86,6 +86,7 @@ Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
_eventHandlerTypes[t->handler] = t->name;
initBuiltIns();
+ initFuncs();
initTheEntities();
_currentScript = 0;
@@ -120,17 +121,17 @@ const char *Lingo::findNextDefinition(const char *s) {
return NULL;
if (!strncmp(res, "macro ", 6)) {
- warning("See macro");
+ debugC(3, kDebugLingoCompile, "See macro");
return res;
}
if (!strncmp(res, "factory ", 8)) {
- warning("See factory");
+ debugC(3, kDebugLingoCompile, "See factory");
return res;
}
if (!strncmp(res, "method ", 7)) {
- warning("See method");
+ debugC(3, kDebugLingoCompile, "See method");
return res;
}
@@ -142,7 +143,7 @@ const char *Lingo::findNextDefinition(const char *s) {
}
void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
- debug(2, "Add code \"%s\" for type %d with id %d", code, type, id);
+ debugC(2, kDebugLingoCompile, "Add code \"%s\" for type %d with id %d", code, type, id);
if (_scripts[type].contains(id)) {
delete _scripts[type][id];
@@ -176,10 +177,18 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
else
_inFactory = false;
- debug(2, "Code chunk:\n#####\n%s#####", chunk.c_str());
+ debugC(2, kDebugLingoCompile, "Code chunk:\n#####\n%s#####", chunk.c_str());
parse(chunk.c_str());
+ if (debugChannelSet(3, kDebugLingoCompile)) {
+ int pc = 0;
+ while (pc < _currentScript->size()) {
+ Common::String instr = decodeInstruction(pc, &pc);
+ debugC(3, kDebugLingoCompile, "[%5d] %s", pc, instr.c_str());
+ }
+ }
+
_currentScript->clear();
begin = end;
@@ -187,7 +196,7 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
_hadError = true; // HACK: This is for preventing test execution
- debug(2, "Code chunk:\n#####\n%s#####", begin);
+ debugC(2, kDebugLingoCompile, "Code chunk:\n#####\n%s#####", begin);
parse(begin);
} else {
parse(code);
@@ -197,8 +206,16 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
_inFactory = false;
- if (_currentScript->size() && !_hadError)
- Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst));
+ if (debugChannelSet(3, kDebugLingoCompile)) {
+ if (_currentScript->size() && !_hadError)
+ Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst));
+
+ int pc = 0;
+ while (pc < _currentScript->size()) {
+ Common::String instr = decodeInstruction(pc, &pc);
+ debugC(3, kDebugLingoCompile, "[%5d] %s", pc, instr.c_str());
+ }
+ }
}
void Lingo::executeScript(ScriptType type, uint16 id) {
@@ -207,7 +224,7 @@ void Lingo::executeScript(ScriptType type, uint16 id) {
return;
}
- debug(2, "Executing script type: %d, id: %d", type, id);
+ debugC(2, kDebugLingoExec, "Executing script type: %d, id: %d", type, id);
_currentScript = _scripts[type][id];
_pc = 0;
@@ -224,7 +241,7 @@ void Lingo::processEvent(LEvent event, int entityId) {
if (!_eventHandlerTypes.contains(event))
error("processEvent: Unknown event %d for entity %d", event, entityId);
- debug(2, "processEvent(%s) for %d", _eventHandlerTypes[event], entityId);
+ debug(2, "STUB: processEvent(%s) for %d", _eventHandlerTypes[event], entityId);
}
int Lingo::alignTypes(Datum &d1, Datum &d2) {
@@ -284,6 +301,15 @@ Common::String *Datum::toString() {
delete s;
s = u.s;
break;
+ case OBJECT:
+ *s = Common::String::format("#%s", u.s->c_str());
+ break;
+ case VOID:
+ *s = "#void";
+ break;
+ case VAR:
+ *s = Common::String::format("var: #%s", u.sym->name);
+ break;
default:
warning("Incorrect operation toString() for type: %s", type2str());
}
@@ -312,6 +338,10 @@ const char *Datum::type2str(bool isk) {
return isk ? "#point" : "POINT";
case SYMBOL:
return isk ? "#symbol" : "SYMBOL";
+ case OBJECT:
+ return isk ? "#object" : "OBJECT";
+ case VAR:
+ return isk ? "#var" : "VAR";
default:
snprintf(res, 20, "-- (%d) --", type);
return res;
@@ -373,7 +403,7 @@ void Lingo::runTests() {
Common::sort(fileList.begin(), fileList.end());
- for (int i = 0; i < fileList.size(); i++) {
+ for (uint i = 0; i < fileList.size(); i++) {
Common::SeekableReadStream *const stream = SearchMan.createReadStreamForMember(fileList[i]);
if (stream) {
uint size = stream->size();
@@ -382,7 +412,7 @@ void Lingo::runTests() {
stream->read(script, size);
- warning("Compiling file %s of size %d, id: %d", fileList[i].c_str(), size, counter);
+ debugC(2, kDebugLingoCompile, "Compiling file %s of size %d, id: %d", fileList[i].c_str(), size, counter);
_hadError = false;
addCode(script, kMovieScript, counter);
@@ -390,7 +420,7 @@ void Lingo::runTests() {
if (!_hadError)
executeScript(kMovieScript, counter);
else
- warning("Skipping execution");
+ debugC(2, kDebugLingoCompile, "Skipping execution");
free(script);
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 8af20f2a15..71708cf910 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -35,6 +35,11 @@
namespace Director {
+enum {
+ kDebugLingoExec = 1 << 0,
+ kDebugLingoCompile = 1 << 1
+};
+
enum LEvent {
kEventPrepareMovie,
kEventStartMovie,
@@ -78,6 +83,30 @@ typedef void (*inst)(void);
typedef Common::Array<inst> ScriptData;
typedef Common::Array<double> FloatArray;
+struct FuncDesc {
+ Common::String name;
+ const char *proto;
+
+ FuncDesc(Common::String n, const char *p) { name = n; proto = p; }
+};
+
+struct Pointer_EqualTo {
+ bool operator()(const void *x, const void *y) const { return x == y; }
+};
+
+struct Pointer_Hash {
+ uint operator()(const void *x) const {
+#ifdef SCUMM_64BITS
+ uint64 v = (uint64)x;
+ return (v >> 32) ^ (v & 0xffffffff);
+#else
+ return (uint)x;
+#endif
+ }
+};
+
+typedef Common::HashMap<void *, FuncDesc *, Pointer_Hash, Pointer_EqualTo> FuncHash;
+
struct Symbol { /* symbol table entry */
char *name;
int type;
@@ -151,10 +180,12 @@ public:
void addCode(const char *code, ScriptType type, uint16 id);
void executeScript(ScriptType type, uint16 id);
+ Common::String decodeInstruction(int pc, int *newPC = NULL);
void processEvent(LEvent event, int entityId);
void initBuiltIns();
+ void initFuncs();
void initTheEntities();
Common::String *toLowercaseMac(Common::String *s);
@@ -236,6 +267,7 @@ public:
static void c_repeatwhilecode();
static void c_repeatwithcode();
static void c_ifcode();
+ static void c_whencode();
static void c_eq();
static void c_neq();
static void c_gt();
@@ -283,26 +315,34 @@ public:
static void b_chars(int nargs);
static void b_charToNum(int nargs);
static void b_length(int nargs);
+ static void b_numToChar(int nargs);
+ static void b_offset(int nargs);
static void b_string(int nargs);
+ static void b_stringp(int nargs);
static void b_ilk(int nargs);
static void b_alert(int nargs);
static void b_cursor(int nargs);
+ static void b_objectp(int nargs);
static void b_printFrom(int nargs);
static void b_showGlobals(int nargs);
static void b_showLocals(int nargs);
+ static void b_symbolp(int nargs);
+ static void b_value(int nargs);
static void b_constrainH(int nargs);
static void b_constrainV(int nargs);
static void b_editableText(int nargs);
static void b_installMenu(int nargs);
static void b_label(int nargs);
+ static void b_marker(int nargs);
static void b_moveableSprite(int nargs);
static void b_puppetPalette(int nargs);
static void b_puppetSound(int nargs);
static void b_puppetSprite(int nargs);
static void b_puppetTempo(int nargs);
static void b_puppetTransition(int nargs);
+ static void b_rollOver(int nargs);
static void b_spriteBox(int nargs);
static void b_updateStage(int nargs);
static void b_zoomBox(int nargs);
@@ -398,6 +438,8 @@ private:
SymbolHash _globalvars;
SymbolHash *_localvars;
+ FuncHash _functions;
+
int _pc;
StackData _stack;
diff --git a/engines/director/lingo/tests/math.lingo b/engines/director/lingo/tests/math.lingo
index 6f8ecc374f..f38b061b6a 100644
--- a/engines/director/lingo/tests/math.lingo
+++ b/engines/director/lingo/tests/math.lingo
@@ -20,3 +20,5 @@ updatestage
put (1024/4096)*100 -- 0
put (1024/4096)*100.0 -- 0.0
put ((1024*1.0)/4096)*100.0 -- 25.0
+
+put the sqrt of 9
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 6587270641..2cdff841b2 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -188,9 +188,6 @@ Score::~Score() {
if (_movieArchive)
_movieArchive->close();
- delete _surface;
- delete _trailSurface;
-
delete _font;
delete _movieArchive;
@@ -201,7 +198,7 @@ void Score::loadPalette(Common::SeekableSubReadStreamEndian &stream) {
uint16 steps = stream.size() / 6;
uint16 index = (steps * 3) - 1;
uint16 _paletteColorCount = steps;
- byte *_palette = new byte[index];
+ byte *_palette = new byte[index + 1];
for (uint8 i = 0; i < steps; i++) {
_palette[index - 2] = stream.readByte();
@@ -892,8 +889,6 @@ Frame::Frame(const Frame &frame) {
}
Frame::~Frame() {
- delete[] &_sprites;
- delete[] &_drawRects;
delete _palette;
}
@@ -977,6 +972,7 @@ void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16
if (stream.readUint16())
readPaletteInfo(stream);
offset += 16;
+ break;
default:
offset++;
stream.readByte();
@@ -1611,7 +1607,6 @@ Sprite::Sprite(const Sprite &sprite) {
Sprite::~Sprite() {
delete _cast;
- delete &_startPoint;
}
} //End of namespace Director
diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp
index 5f6d435392..d6c78a51ea 100644
--- a/engines/director/sound.cpp
+++ b/engines/director/sound.cpp
@@ -36,6 +36,12 @@ DirectorSound::DirectorSound() {
_mixer = g_system->getMixer();
}
+DirectorSound::~DirectorSound() {
+ delete _sound1;
+ delete _sound2;
+ delete _scriptSound;
+}
+
void DirectorSound::playWAV(Common::String filename, uint8 soundChannel) {
Common::File *file = new Common::File();
diff --git a/engines/director/sound.h b/engines/director/sound.h
index 87a989c596..4327b63310 100644
--- a/engines/director/sound.h
+++ b/engines/director/sound.h
@@ -39,6 +39,7 @@ private:
public:
DirectorSound();
+ ~DirectorSound();
void playWAV(Common::String filename, uint8 channelID);
void playAIFF(Common::String filename, uint8 channelID);
diff --git a/engines/fullpipe/fullpipe.h b/engines/fullpipe/fullpipe.h
index d9ab2aaa85..2012d7a344 100644
--- a/engines/fullpipe/fullpipe.h
+++ b/engines/fullpipe/fullpipe.h
@@ -48,7 +48,7 @@ namespace Fullpipe {
enum FullpipeGameFeatures {
};
-enum AccessDebugChannels {
+enum {
kDebugPathfinding = 1 << 0,
kDebugDrawing = 1 << 1,
kDebugLoading = 1 << 2,
diff --git a/engines/fullpipe/gfx.cpp b/engines/fullpipe/gfx.cpp
index dd8d8b246d..174f66a3c8 100644
--- a/engines/fullpipe/gfx.cpp
+++ b/engines/fullpipe/gfx.cpp
@@ -1122,18 +1122,20 @@ void Bitmap::copier(uint32 *dest, byte *src, int len, int32 *palette, bool cb05_
}
Bitmap *Bitmap::reverseImage(bool flip) {
+ Bitmap *b = new Bitmap(this);
+
if (flip)
- _flipping = Graphics::FLIP_H;
- else
- _flipping = Graphics::FLIP_NONE;
+ b->_flipping ^= Graphics::FLIP_H;
- return this;
+ return b;
}
Bitmap *Bitmap::flipVertical() {
- _flipping = Graphics::FLIP_V;
+ Bitmap *b = new Bitmap(this);
+
+ b->_flipping ^= Graphics::FLIP_V;
- return this;
+ return b;
}
void Bitmap::drawShaded(int type, int x, int y, byte *palette, int alpha) {
diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp
index 45cf743e51..552a17e3eb 100644
--- a/engines/fullpipe/statics.cpp
+++ b/engines/fullpipe/statics.cpp
@@ -96,7 +96,7 @@ Common::Point *StepArray::getPoint(Common::Point *point, int index, int offset)
}
bool StepArray::gotoNextPoint() {
- if (_currPointIndex < _maxPointIndex) {
+ if (_currPointIndex < _maxPointIndex - 1) {
_currPointIndex++;
return true;
} else {
diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp
index dd45f94ad3..10a23a0719 100644
--- a/engines/mohawk/riven_sound.cpp
+++ b/engines/mohawk/riven_sound.cpp
@@ -200,7 +200,7 @@ void RivenSoundManager::addAmbientSounds(const SLSTRecord &record) {
}
void RivenSoundManager::setTargetVolumes(const SLSTRecord &record) {
- for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ for (uint i = 0; i < record.volumes.size(); i++) {
_ambientSounds.sounds[i].targetVolume = record.volumes[i] * record.globalVolume / 256;
_ambientSounds.sounds[i].targetBalance = record.balances[i];
}
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 3aaf13efdb..b20ed3f8be 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -54,7 +54,6 @@
#include "sci/graphics/frameout.h"
#include "sci/graphics/paint32.h"
#include "video/coktel_decoder.h"
-#include "sci/video/robot_decoder.h"
#endif
#include "common/file.h"
@@ -266,8 +265,6 @@ void Console::postEnter() {
#ifdef ENABLE_SCI32
} else if (_videoFile.hasSuffix(".vmd")) {
videoDecoder = new Video::AdvancedVMDDecoder();
- } else if (_videoFile.hasSuffix(".rbt")) {
- videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh);
} else if (_videoFile.hasSuffix(".duk")) {
duckMode = true;
videoDecoder = new Video::AVIDecoder();
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index f5797dc106..ad2b0f31a5 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -565,8 +565,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
// the file should be over 10MB, as it contains all the game speech and is usually
// around 450MB+. The size check is for some floppy game versions like KQ6 floppy, which
// also have a small resource.aud file
- if (allFiles.contains("resource.aud") || allFiles.contains("audio001.002")) {
- Common::FSNode file = allFiles.contains("resource.aud") ? allFiles["resource.aud"] : allFiles["audio001.002"];
+ if (allFiles.contains("resource.aud") || allFiles.contains("resaud.001") || allFiles.contains("audio001.002")) {
+ Common::FSNode file = allFiles.contains("resource.aud") ? allFiles["resource.aud"] : (allFiles.contains("resaud.001") ? allFiles["resaud.001"] : allFiles["audio001.002"]);
Common::SeekableReadStream *tmpStream = file.createReadStream();
if (tmpStream->size() > 10 * 1024 * 1024) {
// We got a CD version, so set the CD flag accordingly
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index de342a3afc..eda6bfae64 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -22,15 +22,7 @@
namespace Sci {
-#define GAMEOPTION_PREFER_DIGITAL_SFX GUIO_GAMEOPTIONS1
-#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS2
-#define GAMEOPTION_FB01_MIDI GUIO_GAMEOPTIONS3
-#define GAMEOPTION_JONES_CDAUDIO GUIO_GAMEOPTIONS4
-#define GAMEOPTION_KQ6_WINDOWS_CURSORS GUIO_GAMEOPTIONS5
-#define GAMEOPTION_SQ4_SILVER_CURSORS GUIO_GAMEOPTIONS6
-#define GAMEOPTION_EGA_UNDITHER GUIO_GAMEOPTIONS7
-#define GAMEOPTION_HIGH_RESOLUTION_GRAPHICS GUIO_GAMEOPTIONS8
-#define GAMEOPTION_ENABLE_BLACK_LINED_VIDEO GUIO_GAMEOPTIONS9
+#include "sci/sci.h"
// SCI3 games have a different script format (in CSC files) and are currently unsupported
#define ENABLE_SCI3_GAMES
@@ -836,7 +828,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD },
- // Gabriel Knight - English Macintosh
+ // Gabriel Knight - English Macintosh (Floppy!)
+ // This version is hi-res ONLY, so it should NOT get GAMEOPTION_HIGH_RESOLUTION_GRAPHICS
+ // (which is meant for enforcing hi-res graphics), but instead hi-res mode should be enabled all the time.
+ // Confirmed by [md5] and originally by clone2727.
{"gk1", "", {
{"Data1", 0, "044d3bcd7e5b5bb0393d954ade8053fe", 5814918},
{"Data2", 0, "99a0c63febf9e44e12a00f99c00eae0f", 6685352},
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index b02a7e545a..90f582c291 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -421,6 +421,14 @@ reg_t kStubNull(EngineState *s, int argc, reg_t *argv);
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
+reg_t kSetCursor32(EngineState *s, int argc, reg_t *argv);
+reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv);
+reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv);
+reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv);
+reg_t kPlatform32(EngineState *s, int argc, reg_t *argv);
+reg_t kGlobalToLocal32(EngineState *s, int argc, reg_t *argv);
+reg_t kLocalToGlobal32(EngineState *s, int argc, reg_t *argv);
+
reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv);
@@ -441,10 +449,24 @@ reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv);
+reg_t kRobot(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotOpen(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotShowFrame(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetFrameSize(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotPlay(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetIsFinished(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetIsPlaying(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotClose(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetCue(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotPause(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetFrameNo(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotSetPriority(EngineState *s, int argc, reg_t *argv);
+
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDOpen(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDInit(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv);
@@ -587,9 +609,9 @@ reg_t kTextWidth(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kAutoSave(EngineState *s, int argc, reg_t *argv);
reg_t kList(EngineState *s, int argc, reg_t *argv);
-reg_t kRobot(EngineState *s, int argc, reg_t *argv);
-reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
reg_t kCD(EngineState *s, int argc, reg_t *argv);
+reg_t kCheckCD(EngineState *s, int argc, reg_t *argv);
+reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv);
reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv);
reg_t kAddBefore(EngineState *s, int argc, reg_t *argv);
reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 76c24b09e3..68611b0dee 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -396,6 +396,13 @@ static const SciKernelMapSubEntry kBitmap_subops[] = {
};
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kCD_subops[] = {
+ { SIG_SINCE_SCI21MID, 0, MAP_CALL(CheckCD), "(i)", NULL },
+ { SIG_SINCE_SCI21MID, 1, MAP_CALL(GetSavedCD), "", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL },
{ SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
@@ -406,8 +413,8 @@ static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SINCE_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
{ SIG_SINCE_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
{ SIG_SINCE_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
- { SIG_SINCE_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
- { SIG_SINCE_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
+ { SIG_SINCE_SCI21, 9, MAP_CALL(AddAfter), "lnn(.)", NULL },
+ { SIG_SINCE_SCI21, 10, MAP_CALL(AddToFront), "ln(.)", NULL },
{ SIG_SINCE_SCI21, 11, MAP_CALL(AddToEnd), "ln(.)", NULL },
{ SIG_SINCE_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
{ SIG_SINCE_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
@@ -451,6 +458,7 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = {
{ SIG_SINCE_SCI21, 0, MAP_CALL(PlayVMDOpen), "r(i)(i)", NULL },
{ SIG_SINCE_SCI21, 1, MAP_CALL(PlayVMDInit), "ii(i)(i)(ii)", NULL },
{ SIG_SINCE_SCI21, 6, MAP_CALL(PlayVMDClose), "", NULL },
+ { SIG_SINCE_SCI21, 10, MAP_CALL(PlayVMDGetStatus), "", NULL },
{ SIG_SINCE_SCI21, 14, MAP_CALL(PlayVMDPlayUntilEvent), "i(i)(i)", NULL },
{ SIG_SINCE_SCI21, 16, MAP_CALL(PlayVMDShowCursor), "i", NULL },
{ SIG_SINCE_SCI21, 17, MAP_DUMMY(PlayVMDStartBlob), "", NULL },
@@ -461,6 +469,22 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = {
};
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kRobot_subops[] = {
+ { SIG_SINCE_SCI21, 0, MAP_CALL(RobotOpen), "ioiii(i)", NULL },
+ { SIG_SINCE_SCI21, 1, MAP_CALL(RobotShowFrame), "i(ii)", NULL },
+ { SIG_SINCE_SCI21, 2, MAP_CALL(RobotGetFrameSize), "r", NULL },
+ { SIG_SINCE_SCI21, 4, MAP_CALL(RobotPlay), "", NULL },
+ { SIG_SINCE_SCI21, 5, MAP_CALL(RobotGetIsFinished), "", NULL },
+ { SIG_SINCE_SCI21, 6, MAP_CALL(RobotGetIsPlaying), "", NULL },
+ { SIG_SINCE_SCI21, 7, MAP_CALL(RobotClose), "", NULL },
+ { SIG_SINCE_SCI21, 8, MAP_CALL(RobotGetCue), "o", NULL },
+ { SIG_SINCE_SCI21, 10, MAP_CALL(RobotPause), "", NULL },
+ { SIG_SINCE_SCI21, 11, MAP_CALL(RobotGetFrameNo), "", NULL },
+ { SIG_SINCE_SCI21, 12, MAP_CALL(RobotSetPriority), "i", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kRemapColors_subops[] = {
{ SIG_SCI32, 0, MAP_CALL(RemapColorsOff), "(i)", NULL },
{ SIG_SCI32, 1, MAP_CALL(RemapColorsByRange), "iiii(i)", NULL },
@@ -569,7 +593,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
{ MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
- { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(BaseSetter), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "BaseSetter", kBaseSetter32, SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
+#endif
{ MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
{ MAP_CALL(CantBeHere), SIG_SCI16, SIGFOR_ALL, "o(l)", NULL, NULL },
#ifdef ENABLE_SCI32
@@ -638,8 +665,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
{ MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
{ MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "GlobalToLocal", kGlobalToLocal32, SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+#endif
{ MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
{ MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
{ MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
@@ -650,8 +679,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
{ MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
{ MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "LocalToGlobal", kLocalToGlobal32, SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+#endif
{ MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
{ MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
@@ -676,7 +707,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
{ MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
{ MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Platform), SIG_SCI16, SIGFOR_ALL, "(.*)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "Platform", kPlatform32, SIG_SCI32, SIGFOR_ALL, "(i)", NULL, NULL },
+#endif
{ MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
{ MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
{ MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
@@ -693,22 +727,26 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
{ MAP_CALL(SaveGame), SIG_EVERYWHERE, "[r0]i[r0](r0)", NULL, NULL },
{ MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_SINCE_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL },
- // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why
{ MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+ { MAP_CALL(SetCursor), SIG_SCI16, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+#ifdef ENABLE_SCI32
+ { "SetCursor", kSetCursor32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)", NULL, kSetCursor_workarounds },
+#endif
{ MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
{ MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
{ MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
{ MAP_CALL(SetNowSeen), SIG_SCI16, SIGFOR_ALL, "o(i)", NULL, NULL },
#ifdef ENABLE_SCI32
- { MAP_CALL(SetNowSeen), SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
+ { "SetNowSeen", kSetNowSeen32, SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
#endif
{ MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
{ MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
+ { MAP_CALL(ShakeScreen), SIG_SCI16, SIGFOR_ALL, "(i)(i)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "ShakeScreen", kShakeScreen32, SIG_SCI32, SIGFOR_ALL, "i(i)", NULL, NULL },
+#endif
{ MAP_CALL(ShowMovie), SIG_SCI16, SIGFOR_ALL, "(.*)", NULL, NULL },
#ifdef ENABLE_SCI32
{ "ShowMovie", kShowMovie32, SIG_SCI32, SIGFOR_DOS, "ri(i)(i)", NULL, NULL },
@@ -840,12 +878,12 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Kernel Functions
- { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CD), SIG_SINCE_SCI21MID, SIGFOR_ALL, "(.*)", kCD_subops, NULL },
{ MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", NULL, NULL },
{ MAP_CALL(List), SIG_SINCE_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
{ MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
{ MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", kPlayVMD_subops, NULL },
- { MAP_EMPTY(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", kRobot_subops, NULL },
{ MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
{ MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL },
{ MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii(i)(i)", NULL, NULL },
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index d7a716a504..9250e0fc13 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -34,6 +34,9 @@
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/maciconbar.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
+#endif
namespace Sci {
@@ -58,10 +61,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) {
// In case we use a simulated event we query the current mouse position
mousePos = g_sci->_gfxCursor->getPosition();
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1_EARLY)
- g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
-#endif
+
// Limit the mouse cursor position, if necessary
g_sci->_gfxCursor->refreshPosition();
@@ -86,11 +86,14 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2)
mousePos = curEvent.mousePosSci;
- else
+ else {
#endif
mousePos = curEvent.mousePos;
- // Limit the mouse cursor position, if necessary
- g_sci->_gfxCursor->refreshPosition();
+ // Limit the mouse cursor position, if necessary
+ g_sci->_gfxCursor->refreshPosition();
+#ifdef ENABLE_SCI32
+ }
+#endif
if (g_sci->getVocabulary())
g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event
@@ -281,14 +284,13 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
- reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
if (obj.getSegment()) {
int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
- g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject);
+ g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y);
writeSelectorValue(segMan, obj, SELECTOR(x), x);
writeSelectorValue(segMan, obj, SELECTOR(y), y);
@@ -300,14 +302,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
- reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
if (obj.getSegment()) {
int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
- g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject);
+ g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y);
writeSelectorValue(segMan, obj, SELECTOR(x), x);
writeSelectorValue(segMan, obj, SELECTOR(y), y);
@@ -322,4 +323,52 @@ reg_t kJoystick(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+#ifdef ENABLE_SCI32
+reg_t kGlobalToLocal32(EngineState *s, int argc, reg_t *argv) {
+ const reg_t result = argv[0];
+ const reg_t planeObj = argv[1];
+
+ bool visible = true;
+ Plane *plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
+ if (plane == nullptr) {
+ plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj);
+ visible = false;
+ }
+ if (plane == nullptr) {
+ error("kGlobalToLocal: Plane %04x:%04x not found", PRINT_REG(planeObj));
+ }
+
+ const int16 x = readSelectorValue(s->_segMan, result, SELECTOR(x)) - plane->_gameRect.left;
+ const int16 y = readSelectorValue(s->_segMan, result, SELECTOR(y)) - plane->_gameRect.top;
+
+ writeSelectorValue(s->_segMan, result, SELECTOR(x), x);
+ writeSelectorValue(s->_segMan, result, SELECTOR(y), y);
+
+ return make_reg(0, visible);
+}
+
+reg_t kLocalToGlobal32(EngineState *s, int argc, reg_t *argv) {
+ const reg_t result = argv[0];
+ const reg_t planeObj = argv[1];
+
+ bool visible = true;
+ Plane *plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
+ if (plane == nullptr) {
+ plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj);
+ visible = false;
+ }
+ if (plane == nullptr) {
+ error("kLocalToGlobal: Plane %04x:%04x not found", PRINT_REG(planeObj));
+ }
+
+ const int16 x = readSelectorValue(s->_segMan, result, SELECTOR(x)) + plane->_gameRect.left;
+ const int16 y = readSelectorValue(s->_segMan, result, SELECTOR(y)) + plane->_gameRect.top;
+
+ writeSelectorValue(s->_segMan, result, SELECTOR(x), x);
+ writeSelectorValue(s->_segMan, result, SELECTOR(y), y);
+
+ return make_reg(0, visible);
+}
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 3bcadd143c..a3a97eb7ee 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -40,6 +40,9 @@
#include "sci/engine/savegame.h"
#include "sci/sound/audio.h"
#include "sci/console.h"
+#ifdef ENABLE_SCI32
+#include "sci/resource.h"
+#endif
namespace Sci {
@@ -196,26 +199,25 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
reg_t kCD(EngineState *s, int argc, reg_t *argv) {
- // TODO: Stub
- switch (argv[0].toUint16()) {
- case 0:
- if (argc == 1) {
- // Check if a disc is in the drive
- return TRUE_REG;
- } else {
- // Check if the specified disc is in the drive
- // and return the current disc number. We just
- // return the requested disc number.
- return argv[1];
- }
- case 1:
- // Return the current CD number
- return make_reg(0, 1);
- default:
- warning("CD(%d)", argv[0].toUint16());
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kCheckCD(EngineState *s, int argc, reg_t *argv) {
+ const int16 cdNo = argc > 0 ? argv[0].toSint16() : 0;
+
+ if (cdNo) {
+ g_sci->getResMan()->findDisc(cdNo);
}
- return NULL_REG;
+ return make_reg(0, g_sci->getResMan()->getCurrentDiscNo());
+}
+
+reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv) {
+ // TODO: This is wrong, CD number needs to be available prior to
+ // the save game being loaded
+ return make_reg(0, g_sci->getResMan()->getCurrentDiscNo());
}
#endif
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index cae5a09789..d375a27954 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -579,17 +579,8 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2) {
- g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]);
- return NULL_REG;
- } else {
-#endif
- g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
- return s->r_acc;
-#ifdef ENABLE_SCI32
- }
-#endif
+ g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
+ return s->r_acc;
}
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index e458109cc2..13a209ea55 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -37,8 +37,6 @@
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
#include "sci/graphics/controls16.h"
-#include "sci/graphics/coordadjuster.h"
-#include "sci/graphics/cursor.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/picture.h"
@@ -48,6 +46,7 @@
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/celobj32.h"
#include "sci/graphics/controls32.h"
#include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class
@@ -64,6 +63,107 @@ namespace Sci {
extern void showScummVMDialog(const Common::String &message);
+reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv) {
+ reg_t object = argv[0];
+
+ const GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
+ const int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop));
+ const int16 celNo = readSelectorValue(s->_segMan, object, SELECTOR(cel));
+ const int16 x = readSelectorValue(s->_segMan, object, SELECTOR(x));
+ const int16 y = readSelectorValue(s->_segMan, object, SELECTOR(y));
+
+ CelObjView celObj(viewId, loopNo, celNo);
+
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ const Ratio scaleX(scriptWidth, celObj._scaledWidth);
+ const Ratio scaleY(scriptHeight, celObj._scaledHeight);
+
+ int16 brLeft;
+
+ if (celObj._mirrorX) {
+ brLeft = x - ((celObj._width - celObj._displace.x) * scaleX).toInt();
+ } else {
+ brLeft = x - (celObj._displace.x * scaleX).toInt();
+ }
+
+ const int16 brRight = brLeft + (celObj._width * scaleX).toInt() - 1;
+
+ writeSelectorValue(s->_segMan, object, SELECTOR(brLeft), brLeft);
+ writeSelectorValue(s->_segMan, object, SELECTOR(brRight), brRight);
+ writeSelectorValue(s->_segMan, object, SELECTOR(brBottom), y + 1);
+ writeSelectorValue(s->_segMan, object, SELECTOR(brTop), y + 1 - readSelectorValue(s->_segMan, object, SELECTOR(yStep)));
+
+ return s->r_acc;
+}
+
+reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv) {
+ const bool found = g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]);
+
+ // NOTE: MGDX is assumed to use the older kSetNowSeen since it was
+ // released before SQ6, but this has not been verified since it cannot be
+ // disassembled at the moment (Phar Lap Windows-only release)
+ if (getSciVersion() <= SCI_VERSION_2_1_EARLY ||
+ g_sci->getGameId() == GID_SQ6 ||
+ g_sci->getGameId() == GID_MOTHERGOOSEHIRES) {
+
+ if (!found) {
+ error("kSetNowSeen: Unable to find screen item %04x:%04x", PRINT_REG(argv[0]));
+ }
+ return s->r_acc;
+ }
+
+ if (!found) {
+ warning("kSetNowSeen: Unable to find screen item %04x:%04x", PRINT_REG(argv[0]));
+ }
+
+ return make_reg(0, found);
+}
+
+reg_t kSetCursor32(EngineState *s, int argc, reg_t *argv) {
+ switch (argc) {
+ case 1: {
+ if (argv[0].toSint16() == -2) {
+ g_sci->_gfxCursor32->clearRestrictedArea();
+ } else {
+ if (argv[0].isNull()) {
+ g_sci->_gfxCursor32->hide();
+ } else {
+ g_sci->_gfxCursor32->show();
+ }
+ }
+ break;
+ }
+ case 2: {
+ const Common::Point position(argv[0].toSint16(), argv[1].toSint16());
+ g_sci->_gfxCursor32->setPosition(position);
+ break;
+ }
+ case 3: {
+ g_sci->_gfxCursor32->setView(argv[0].toUint16(), argv[1].toSint16(), argv[2].toSint16());
+ break;
+ }
+ case 4: {
+ const Common::Rect restrictRect(argv[0].toSint16(),
+ argv[1].toSint16(),
+ argv[2].toSint16() + 1,
+ argv[3].toSint16() + 1);
+ g_sci->_gfxCursor32->setRestrictedArea(restrictRect);
+ break;
+ }
+ default:
+ error("kSetCursor: Invalid number of arguments (%d)", argc);
+ }
+
+ return s->r_acc;
+}
+
+reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->shakeScreen(argv[0].toSint16(), (ShakeDirection)argv[1].toSint16());
+ return s->r_acc;
+}
+
reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer();
if (buffer.screenWidth < 640 || buffer.screenHeight < 400)
@@ -266,7 +366,7 @@ reg_t kMessageBox(EngineState *s, int argc, reg_t *argv) {
* effect
*/
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
- ShowStyleType type = (ShowStyleType)argv[0].toUint16();
+ const uint16 type = argv[0].toUint16();
reg_t planeObj = argv[1];
int16 seconds = argv[2].toSint16();
// NOTE: This value seems to indicate whether the transition is an
@@ -301,6 +401,10 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
divisions = argc > 9 ? argv[9].toSint16() : -1;
}
+ if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) {
+ error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj));
+ }
+
// TODO: Reuse later for SCI2 and SCI3 implementation and then discard
// warning("kSetShowStyle: effect %d, plane: %04x:%04x (%s), sec: %d, "
// "dir: %d, prio: %d, animate: %d, ref frame: %d, black screen: %d, "
@@ -312,7 +416,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// NOTE: The order of planeObj and showStyle are reversed
// because this is how SCI3 called the corresponding method
// on the KernelMgr
- g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
+ g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, (ShowStyleType)type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
return s->r_acc;
}
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index c99540967c..448d7bb8a3 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -540,57 +540,46 @@ enum kSciPlatforms {
kSciPlatformWindows = 2
};
-enum kPlatformOps {
- kPlatformUnk0 = 0,
- kPlatformCDSpeed = 1,
- kPlatformColorDepth = 2,
- kPlatformCDCheck = 3,
- kPlatformGetPlatform = 4,
- kPlatformUnk5 = 5,
- kPlatformIsHiRes = 6,
- kPlatformIsItWindows = 7
-};
-
reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
+ enum Operation {
+ kPlatformUnknown = 0,
+ kPlatformGetPlatform = 4,
+ kPlatformUnknown5 = 5,
+ kPlatformIsHiRes = 6,
+ kPlatformWin311OrHigher = 7
+ };
+
bool isWindows = g_sci->getPlatform() == Common::kPlatformWindows;
- if (argc == 0 && getSciVersion() < SCI_VERSION_2) {
+ if (argc == 0) {
// This is called in KQ5CD with no parameters, where it seems to do some
// graphics driver check. This kernel function didn't have subfunctions
// then. If 0 is returned, the game functions normally, otherwise all
// the animations show up like a slideshow (e.g. in the intro). So we
- // return 0. However, the behavior changed for kPlatform with no
- // parameters in SCI32.
+ // return 0.
return NULL_REG;
}
+ if (g_sci->forceHiresGraphics()) {
+ // force Windows platform, so that hires-graphics are enabled
+ isWindows = true;
+ }
+
uint16 operation = (argc == 0) ? 0 : argv[0].toUint16();
switch (operation) {
- case kPlatformCDSpeed:
- // TODO: Returns CD Speed?
- warning("STUB: kPlatform(CDSpeed)");
- break;
- case kPlatformColorDepth:
- // Always returns 2
- return make_reg(0, /* 256-color */ 2);
- case kPlatformCDCheck:
- // TODO: Some sort of CD check?
- warning("STUB: kPlatform(CDCheck)");
- break;
- case kPlatformUnk0:
+ case kPlatformUnknown:
// For Mac versions, kPlatform(0) with other args has more functionality
if (g_sci->getPlatform() == Common::kPlatformMacintosh && argc > 1)
return kMacPlatform(s, argc - 1, argv + 1);
// Otherwise, fall through
case kPlatformGetPlatform:
return make_reg(0, (isWindows) ? kSciPlatformWindows : kSciPlatformDOS);
- case kPlatformUnk5:
+ case kPlatformUnknown5:
// This case needs to return the opposite of case 6 to get hires graphics
- return make_reg(0, !ConfMan.getBool("enable_high_resolution_graphics"));
+ return make_reg(0, !isWindows);
case kPlatformIsHiRes:
- return make_reg(0, ConfMan.getBool("enable_high_resolution_graphics"));
- case kPlatformIsItWindows:
+ case kPlatformWin311OrHigher:
return make_reg(0, isWindows);
default:
error("Unsupported kPlatform operation %d", operation);
@@ -599,6 +588,37 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+#ifdef ENABLE_SCI32
+reg_t kPlatform32(EngineState *s, int argc, reg_t *argv) {
+ enum Operation {
+ kGetPlatform = 0,
+ kGetCDSpeed = 1,
+ kGetColorDepth = 2,
+ kGetCDDrive = 3
+ };
+
+ const Operation operation = argc > 0 ? (Operation)argv[0].toSint16() : kGetPlatform;
+
+ switch (operation) {
+ case kGetPlatform:
+ switch (g_sci->getPlatform()) {
+ case Common::kPlatformDOS:
+ return make_reg(0, kSciPlatformDOS);
+ case Common::kPlatformWindows:
+ return make_reg(0, kSciPlatformWindows);
+ default:
+ error("Unknown platform %d", g_sci->getPlatform());
+ }
+ case kGetColorDepth:
+ return make_reg(0, /* 256 color */ 2);
+ case kGetCDSpeed:
+ case kGetCDDrive:
+ default:
+ return make_reg(0, 0);
+ }
+}
+#endif
+
reg_t kEmpty(EngineState *s, int argc, reg_t *argv) {
// Placeholder for empty kernel functions which are still called from the
// engine scripts (like the empty kSetSynonyms function in SCI1.1). This
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 86d8a4b817..b539c84f5d 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -72,11 +72,10 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
uint16 y = (screenHeight - height) / 2;
bool skipVideo = false;
- EngineState *s = g_sci->getEngineState();
if (videoDecoder->hasDirtyPalette()) {
- const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3;
- g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart);
+ const byte *palette = videoDecoder->getPalette();
+ g_system->getPaletteManager()->setPalette(palette, 0, 255);
}
while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
@@ -85,7 +84,7 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
if (frame) {
if (scaleBuffer) {
- // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
+ // TODO: Probably should do aspect ratio correction in KQ6
g_sci->_gfxScreen->scale2x((const byte *)frame->getPixels(), scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height);
} else {
@@ -93,8 +92,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
}
if (videoDecoder->hasDirtyPalette()) {
- const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3;
- g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart);
+ const byte *palette = videoDecoder->getPalette();
+ g_system->getPaletteManager()->setPalette(palette, 0, 255);
}
g_system->updateScreen();
@@ -226,6 +225,80 @@ reg_t kShowMovie32(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kRobotOpen(EngineState *s, int argc, reg_t *argv) {
+ const GuiResourceId robotId = argv[0].toUint16();
+ const reg_t plane = argv[1];
+ const int16 priority = argv[2].toSint16();
+ const int16 x = argv[3].toSint16();
+ const int16 y = argv[4].toSint16();
+ const int16 scale = argc > 5 ? argv[5].toSint16() : 128;
+ g_sci->_video32->getRobotPlayer().open(robotId, plane, priority, x, y, scale);
+ return make_reg(0, 0);
+}
+reg_t kRobotShowFrame(EngineState *s, int argc, reg_t *argv) {
+ const uint16 frameNo = argv[0].toUint16();
+ const uint16 newX = argc > 1 ? argv[1].toUint16() : (uint16)RobotDecoder::kUnspecified;
+ const uint16 newY = argc > 1 ? argv[2].toUint16() : (uint16)RobotDecoder::kUnspecified;
+ g_sci->_video32->getRobotPlayer().showFrame(frameNo, newX, newY, RobotDecoder::kUnspecified);
+ return s->r_acc;
+}
+
+reg_t kRobotGetFrameSize(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect frameRect;
+ const uint16 numFramesTotal = g_sci->_video32->getRobotPlayer().getFrameSize(frameRect);
+
+ reg_t *outRect = s->_segMan->derefRegPtr(argv[0], 4);
+ outRect[0] = make_reg(0, frameRect.left);
+ outRect[1] = make_reg(0, frameRect.top);
+ outRect[2] = make_reg(0, frameRect.right - 1);
+ outRect[3] = make_reg(0, frameRect.bottom - 1);
+
+ return make_reg(0, numFramesTotal);
+}
+
+reg_t kRobotPlay(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().resume();
+ return s->r_acc;
+}
+
+reg_t kRobotGetIsFinished(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getRobotPlayer().getStatus() == RobotDecoder::kRobotStatusEnd);
+}
+
+reg_t kRobotGetIsPlaying(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getRobotPlayer().getStatus() == RobotDecoder::kRobotStatusPlaying);
+}
+
+reg_t kRobotClose(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().close();
+ return s->r_acc;
+}
+
+reg_t kRobotGetCue(EngineState *s, int argc, reg_t *argv) {
+ writeSelectorValue(s->_segMan, argv[0], SELECTOR(signal), g_sci->_video32->getRobotPlayer().getCue());
+ return s->r_acc;
+}
+
+reg_t kRobotPause(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().pause();
+ return s->r_acc;
+}
+
+reg_t kRobotGetFrameNo(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getRobotPlayer().getFrameNo());
+}
+
+reg_t kRobotSetPriority(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().setPriority(argv[0].toSint16());
+ return s->r_acc;
+}
+
reg_t kShowMovieWin(EngineState *s, int argc, reg_t *argv) {
if (!s)
return make_reg(0, getSciVersion());
@@ -352,6 +425,10 @@ reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, g_sci->_video32->getVMDPlayer().close());
}
+reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getVMDPlayer().getStatus());
+}
+
reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv) {
const VMDPlayer::EventFlags flags = (VMDPlayer::EventFlags)argv[0].toUint16();
const int16 lastFrameNo = argc > 1 ? argv[1].toSint16() : -1;
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 7804b7892d..eeddda8390 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -48,6 +48,7 @@
#include "sci/sound/music.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
@@ -889,6 +890,29 @@ void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) {
_needsUpdate = true;
}
}
+
+void GfxCursor32::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 37) {
+ return;
+ }
+
+ s.syncAsSint32LE(_hideCount);
+ s.syncAsSint16LE(_restrictedArea.left);
+ s.syncAsSint16LE(_restrictedArea.top);
+ s.syncAsSint16LE(_restrictedArea.right);
+ s.syncAsSint16LE(_restrictedArea.bottom);
+ s.syncAsUint16LE(_cursorInfo.resourceId);
+ s.syncAsUint16LE(_cursorInfo.loopNo);
+ s.syncAsUint16LE(_cursorInfo.celNo);
+
+ if (s.isLoading()) {
+ hide();
+ setView(_cursorInfo.resourceId, _cursorInfo.loopNo, _cursorInfo.celNo);
+ if (!_hideCount) {
+ show();
+ }
+ }
+}
#endif
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 1bf66864e8..51dbbedd87 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,7 +37,7 @@ struct EngineState;
*
* Version - new/changed feature
* =============================
- * 37 - Segment entry data changed to pointers
+ * 37 - Segment entry data changed to pointers, SCI32 cursor
* 36 - SCI32 bitmap segment
* 35 - SCI32 remap
* 34 - SCI32 palettes, and store play time in ticks
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index acebecea97..8ed1c3a143 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -30,8 +30,7 @@
#include "sci/engine/vm_types.h"
#include "sci/engine/segment.h"
#ifdef ENABLE_SCI32
-// TODO: Baaaad?
-#include "sci/graphics/celobj32.h"
+#include "sci/graphics/celobj32.h" // kLowResX, kLowResY
#endif
namespace Sci {
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 2c85907628..a338beffc9 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -121,9 +121,6 @@ void EngineState::reset(bool isRestoring) {
_videoState.reset();
_syncedAudioOptions = false;
-
- _vmdPalStart = 0;
- _vmdPalEnd = 256;
}
void EngineState::speedThrottler(uint32 neededSleep) {
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index dd8d76f002..baa912b60e 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -203,7 +203,6 @@ public:
// TODO: Excise video code from the state manager
VideoState _videoState;
- uint16 _vmdPalStart, _vmdPalEnd;
bool _syncedAudioOptions;
/**
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 9b3b329418..bc1390864e 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -329,7 +329,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154
{ GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
{ GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not
- { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning
+ { GID_PQSWAT, -1, 64950, 0, NULL, "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using any menus in-game
{ GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout
{ GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309
{ GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515
@@ -688,6 +688,7 @@ const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kSetCursor_workarounds[] = {
{ GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters
+ { GID_MOTHERGOOSEHIRES,0, 0, -1, "MG", "setCursor", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // At the start of the game, an object is passed as the cel number
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 4ad2a0cfa3..b267d2ebc2 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -30,6 +30,7 @@
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#endif
#include "sci/graphics/screen.h"
@@ -168,9 +169,17 @@ SciEvent EventManager::getScummVMEvent() {
if (getSciVersion() >= SCI_VERSION_2) {
const Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer();
+ if (ev.type == Common::EVENT_MOUSEMOVE) {
+ // This will clamp `mousePos` according to the restricted zone,
+ // so any cursor or screen item associated with the mouse position
+ // does not bounce when it hits the edge (or ignore the edge)
+ g_sci->_gfxCursor32->deviceMoved(mousePos);
+ }
+
Common::Point mousePosSci = mousePos;
mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight));
noEvent.mousePosSci = input.mousePosSci = mousePosSci;
+
} else {
#endif
g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp
index fb1f557ad6..9c77f31a14 100644
--- a/engines/sci/graphics/cache.cpp
+++ b/engines/sci/graphics/cache.cpp
@@ -95,10 +95,20 @@ int16 GfxCache::kernelViewGetCelHeight(GuiResourceId viewId, int16 loopNo, int16
}
int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) {
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ return CelObjView::getNumLoops(viewId);
+ }
+#endif
return getView(viewId)->getLoopCount();
}
int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) {
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ return CelObjView::getNumCels(viewId, loopNo);
+ }
+#endif
return getView(viewId)->getCelCount(loopNo);
}
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 311684d595..d053fa2eef 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -45,7 +45,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
}
}
- int i = 1 - _activeIndex;
+ const int i = 1 - _activeIndex;
_activeIndex = i;
CelScalerTable &table = _scaleTables[i];
@@ -65,7 +65,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) {
int value = 0;
int remainder = 0;
- int num = ratio.getNumerator();
+ const int num = ratio.getNumerator();
for (int i = 0; i < size; ++i) {
*table++ = value;
remainder += ratio.getDenominator();
@@ -164,8 +164,8 @@ struct SCALER_Scale {
const byte *_row;
READER _reader;
int16 _x;
- static int16 _valuesX[1024];
- static int16 _valuesY[1024];
+ static int16 _valuesX[4096];
+ static int16 _valuesY[4096];
SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) :
_row(nullptr),
@@ -204,7 +204,7 @@ struct SCALER_Scale {
if (g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth == kLowResX) {
const int16 unscaledX = (scaledPosition.x / scaleX).toInt();
if (FLIP) {
- int lastIndex = celObj._width - 1;
+ const int lastIndex = celObj._width - 1;
for (int16 x = targetRect.left; x < targetRect.right; ++x) {
_valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX);
}
@@ -220,7 +220,7 @@ struct SCALER_Scale {
}
} else {
if (FLIP) {
- int lastIndex = celObj._width - 1;
+ const int lastIndex = celObj._width - 1;
for (int16 x = 0; x < targetRect.width(); ++x) {
_valuesX[targetRect.left + x] = lastIndex - table->valuesX[x];
}
@@ -249,9 +249,9 @@ struct SCALER_Scale {
};
template<bool FLIP, typename READER>
-int16 SCALER_Scale<FLIP, READER>::_valuesX[1024];
+int16 SCALER_Scale<FLIP, READER>::_valuesX[4096];
template<bool FLIP, typename READER>
-int16 SCALER_Scale<FLIP, READER>::_valuesY[1024];
+int16 SCALER_Scale<FLIP, READER>::_valuesY[4096];
#pragma mark -
#pragma mark CelObj - Resource readers
@@ -261,7 +261,7 @@ private:
#ifndef NDEBUG
const int16 _sourceHeight;
#endif
- byte *_pixels;
+ const byte *_pixels;
const int16 _sourceWidth;
public:
@@ -270,7 +270,7 @@ public:
_sourceHeight(celObj._height),
#endif
_sourceWidth(celObj._width) {
- byte *resource = celObj.getResPointer();
+ const byte *resource = celObj.getResPointer();
_pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24);
}
@@ -282,8 +282,8 @@ public:
struct READER_Compressed {
private:
- byte *_resource;
- byte _buffer[1024];
+ const byte *const _resource;
+ byte _buffer[4096];
uint32 _controlOffset;
uint32 _dataOffset;
uint32 _uncompressedDataOffset;
@@ -301,7 +301,7 @@ public:
_maxWidth(maxWidth) {
assert(maxWidth <= celObj._width);
- byte *celHeader = _resource + celObj._celHeaderOffset;
+ const byte *const celHeader = _resource + celObj._celHeaderOffset;
_dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24);
_uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28);
_controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32);
@@ -311,14 +311,14 @@ public:
assert(y >= 0 && y < _sourceHeight);
if (y != _y) {
// compressed data segment for row
- byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4);
+ const byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4);
// uncompressed data segment for row
- byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4);
+ const byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4);
uint8 length;
for (int16 i = 0; i < _maxWidth; i += length) {
- byte controlByte = *row++;
+ const byte controlByte = *row++;
length = controlByte;
// Run-length encoded
@@ -581,7 +581,7 @@ void CelObj::submitPalette() const {
int CelObj::_nextCacheId = 1;
CelCache *CelObj::_cache = nullptr;
-int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const {
+int CelObj::searchCache(const CelInfo32 &celInfo, int *const nextInsertIndex) const {
*nextInsertIndex = -1;
int oldestId = _nextCacheId + 1;
int oldestIndex = 0;
@@ -791,6 +791,49 @@ void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Rati
#pragma mark -
#pragma mark CelObjView
+int16 CelObjView::getNumLoops(const GuiResourceId viewId) {
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+
+ if (!resource) {
+ return 0;
+ }
+
+ assert(resource->size >= 3);
+ return resource->data[2];
+}
+
+int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) {
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+
+ if (!resource) {
+ return 0;
+ }
+
+ const byte *const data = resource->data;
+
+ const uint16 loopCount = data[2];
+ if (loopNo >= loopCount || loopNo < 0) {
+ return 0;
+ }
+
+ const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data);
+ const uint8 loopHeaderSize = data[12];
+ const uint8 viewHeaderFieldSize = 2;
+
+#ifndef NDEBUG
+ const byte *const dataMax = data + resource->size;
+#endif
+ const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopNo);
+ assert(loopHeader + 3 <= dataMax);
+
+ if ((int8)loopHeader[0] != -1) {
+ loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]);
+ assert(loopHeader >= data && loopHeader + 3 <= dataMax);
+ }
+
+ return loopHeader[2];
+}
+
CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
_info.type = kCelTypeView;
_info.resourceId = viewId;
@@ -801,7 +844,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
_transparent = true;
int cacheInsertIndex;
- int cacheIndex = searchCache(_info, &cacheInsertIndex);
+ const int cacheIndex = searchCache(_info, &cacheInsertIndex);
if (cacheIndex != -1) {
CelCacheEntry &entry = (*_cache)[cacheIndex];
const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj);
@@ -817,15 +860,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
// generates view resource metadata for both SCI16 and SCI32
// implementations
- Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
// NOTE: SCI2.1/SQ6 just silently returns here.
if (!resource) {
- warning("View resource %d not loaded", viewId);
- return;
+ error("View resource %d not found", viewId);
}
- byte *data = resource->data;
+ const byte *const data = resource->data;
_scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14);
_scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16);
@@ -844,7 +886,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
}
}
- uint16 loopCount = data[2];
+ const uint16 loopCount = data[2];
if (_info.loopNo >= loopCount) {
_info.loopNo = loopCount - 1;
}
@@ -859,7 +901,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
const uint8 loopHeaderSize = data[12];
const uint8 viewHeaderFieldSize = 2;
- byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
+ const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
if ((int8)loopHeader[0] != -1) {
if (loopHeader[1] == 1) {
@@ -874,10 +916,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
_info.celNo = celCount - 1;
}
+ if (_info.celNo < 0) {
+ error("Cel is less than 0!");
+ }
+
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8);
_celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo);
- byte *celHeader = data + _celHeaderOffset;
+ const byte *const celHeader = data + _celHeaderOffset;
_width = READ_SCI11ENDIAN_UINT16(celHeader);
_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
@@ -906,7 +952,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
}
bool CelObjView::analyzeUncompressedForRemap() const {
- byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
+ const byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
for (int i = 0; i < _width * _height; ++i) {
const byte pixel = pixels[i];
if (
@@ -923,7 +969,7 @@ bool CelObjView::analyzeUncompressedForRemap() const {
bool CelObjView::analyzeForRemap() const {
READER_Compressed reader(*this, _width);
for (int y = 0; y < _height; y++) {
- const byte *curRow = reader.getRow(y);
+ const byte *const curRow = reader.getRow(y);
for (int x = 0; x < _width; x++) {
const byte pixel = curRow[x];
if (
@@ -948,7 +994,7 @@ CelObjView *CelObjView::duplicate() const {
}
byte *CelObjView::getResPointer() const {
- const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false);
+ Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false);
if (resource == nullptr) {
error("Failed to load view %d from resource manager", _info.resourceId);
}
@@ -969,7 +1015,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_remap = false;
int cacheInsertIndex;
- int cacheIndex = searchCache(_info, &cacheInsertIndex);
+ const int cacheIndex = searchCache(_info, &cacheInsertIndex);
if (cacheIndex != -1) {
CelCacheEntry &entry = (*_cache)[cacheIndex];
const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj);
@@ -981,15 +1027,14 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
return;
}
- Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false);
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false);
// NOTE: SCI2.1/SQ6 just silently returns here.
if (!resource) {
- warning("Pic resource %d not loaded", picId);
- return;
+ error("Pic resource %d not found", picId);
}
- byte *data = resource->data;
+ const byte *const data = resource->data;
_celCount = data[2];
@@ -1000,7 +1045,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo);
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6);
- byte *celHeader = data + _celHeaderOffset;
+ const byte *const celHeader = data + _celHeaderOffset;
_width = READ_SCI11ENDIAN_UINT16(celHeader);
_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
@@ -1012,8 +1057,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38);
_relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40);
- uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
- uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
+ const uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
+ const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
if (sizeFlag2) {
_scaledWidth = sizeFlag1;
@@ -1032,7 +1077,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
if (celHeader[10] & 128) {
// NOTE: This is correct according to SCI2.1/SQ6/DOS;
// the engine re-reads the byte value as a word value
- uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
+ const uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
_transparent = flags & 1 ? true : false;
_remap = flags & 2 ? true : false;
} else {
@@ -1047,8 +1092,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
}
bool CelObjPic::analyzeUncompressedForSkip() const {
- byte *resource = getResPointer();
- byte *pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
+ const byte *const resource = getResPointer();
+ const byte *const pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
for (int i = 0; i < _width * _height; ++i) {
uint8 pixel = pixels[i];
if (pixel == _transparentColor) {
@@ -1060,7 +1105,7 @@ bool CelObjPic::analyzeUncompressedForSkip() const {
}
void CelObjPic::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) {
- Ratio square;
+ const Ratio square;
_drawMirrored = mirrorX;
drawTo(target, targetRect, scaledPosition, square, square);
}
@@ -1088,15 +1133,21 @@ CelObjMem::CelObjMem(const reg_t bitmapObject) {
_celHeaderOffset = 0;
_transparent = true;
- SciBitmap &bitmap = *g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject);
- _width = bitmap.getWidth();
- _height = bitmap.getHeight();
- _displace = bitmap.getDisplace();
- _transparentColor = bitmap.getSkipColor();
- _scaledWidth = bitmap.getScaledWidth();
- _scaledHeight = bitmap.getScaledHeight();
- _hunkPaletteOffset = bitmap.getHunkPaletteOffset();
- _remap = bitmap.getRemap();
+ SciBitmap *bitmap = g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject);
+
+ // NOTE: SSCI did no error checking here at all.
+ if (!bitmap) {
+ error("Bitmap %04x:%04x not found", PRINT_REG(bitmapObject));
+ }
+
+ _width = bitmap->getWidth();
+ _height = bitmap->getHeight();
+ _displace = bitmap->getDisplace();
+ _transparentColor = bitmap->getSkipColor();
+ _scaledWidth = bitmap->getScaledWidth();
+ _scaledHeight = bitmap->getScaledHeight();
+ _hunkPaletteOffset = bitmap->getHunkPaletteOffset();
+ _remap = bitmap->getRemap();
}
CelObjMem *CelObjMem::duplicate() const {
diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h
index eb6ce3a3c9..21e86d03e0 100644
--- a/engines/sci/graphics/celobj32.h
+++ b/engines/sci/graphics/celobj32.h
@@ -147,7 +147,7 @@ struct CelScalerTable {
* the correct column to read from the source bitmap
* when drawing a scaled version of the source bitmap.
*/
- int valuesX[1024];
+ int valuesX[4096];
/**
* The ratio used to generate the x-values.
@@ -159,7 +159,7 @@ struct CelScalerTable {
* the correct row to read from a source bitmap when
* drawing a scaled version of the source bitmap.
*/
- int valuesY[1024];
+ int valuesY[4096];
/**
* The ratio used to generate the y-values.
@@ -400,7 +400,7 @@ public:
* Reads the pixel at the given coordinates. This method
* is valid only for CelObjView and CelObjPic.
*/
- virtual uint8 readPixel(uint16 x, uint16 y, bool mirrorX) const;
+ virtual uint8 readPixel(const uint16 x, const uint16 y, const bool mirrorX) const;
/**
* Submits the palette from this cel to the palette
@@ -505,6 +505,9 @@ public:
using CelObj::draw;
+ static int16 getNumLoops(const GuiResourceId viewId);
+ static int16 getNumCels(const GuiResourceId viewId, const int16 loopNo);
+
/**
* Draws the cel to the target buffer using the
* positioning, mirroring, and scaling information from
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 130416ff60..36026a8134 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -37,7 +37,7 @@
namespace Sci {
-GfxCompare::GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster *coordAdjuster)
+GfxCompare::GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster16 *coordAdjuster)
: _segMan(segMan), _cache(cache), _screen(screen), _coordAdjuster(coordAdjuster) {
}
diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h
index c7005980d0..dd65b90bea 100644
--- a/engines/sci/graphics/compare.h
+++ b/engines/sci/graphics/compare.h
@@ -34,7 +34,7 @@ class Screen;
*/
class GfxCompare {
public:
- GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster *coordAdjuster);
+ GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster16 *coordAdjuster);
~GfxCompare();
uint16 kernelOnControl(byte screenMask, const Common::Rect &rect);
@@ -50,7 +50,7 @@ private:
SegManager *_segMan;
GfxCache *_cache;
GfxScreen *_screen;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
uint16 isOnControl(uint16 screenMask, const Common::Rect &rect);
diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp
index 93dff10382..2f22d191d0 100644
--- a/engines/sci/graphics/coordadjuster.cpp
+++ b/engines/sci/graphics/coordadjuster.cpp
@@ -32,9 +32,6 @@
namespace Sci {
-GfxCoordAdjuster::GfxCoordAdjuster() {
-}
-
GfxCoordAdjuster16::GfxCoordAdjuster16(GfxPorts *ports)
: _ports(ports) {
}
@@ -83,53 +80,4 @@ Common::Rect GfxCoordAdjuster16::pictureGetDisplayArea() {
return displayArea;
}
-#ifdef ENABLE_SCI32
-GfxCoordAdjuster32::GfxCoordAdjuster32(SegManager *segMan)
- : _segMan(segMan) {
- _scriptsRunningWidth = 0;
- _scriptsRunningHeight = 0;
-}
-
-GfxCoordAdjuster32::~GfxCoordAdjuster32() {
-}
-
-void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) {
- uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top));
- uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left));
-
- y -= planeTop;
- x -= planeLeft;
-}
-void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) {
- uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top));
- uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left));
-
- x += planeLeft;
- y += planeTop;
-}
-
-void GfxCoordAdjuster32::setScriptsResolution(uint16 width, uint16 height) {
- _scriptsRunningWidth = width;
- _scriptsRunningHeight = height;
-}
-
-void GfxCoordAdjuster32::fromDisplayToScript(int16 &y, int16 &x) {
- y = ((y * _scriptsRunningHeight) / g_sci->_gfxScreen->getHeight());
- x = ((x * _scriptsRunningWidth) / g_sci->_gfxScreen->getWidth());
-}
-
-void GfxCoordAdjuster32::fromScriptToDisplay(int16 &y, int16 &x) {
- y = ((y * g_sci->_gfxScreen->getHeight()) / _scriptsRunningHeight);
- x = ((x * g_sci->_gfxScreen->getWidth()) / _scriptsRunningWidth);
-}
-
-void GfxCoordAdjuster32::pictureSetDisplayArea(Common::Rect displayArea) {
- _pictureDisplayArea = displayArea;
-}
-
-Common::Rect GfxCoordAdjuster32::pictureGetDisplayArea() {
- return _pictureDisplayArea;
-}
-#endif
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/coordadjuster.h b/engines/sci/graphics/coordadjuster.h
index cb0227fbe4..f7ebd3ec75 100644
--- a/engines/sci/graphics/coordadjuster.h
+++ b/engines/sci/graphics/coordadjuster.h
@@ -35,27 +35,7 @@ class GfxPorts;
* most of the time sci32 doesn't do any coordinate adjustment at all
* sci16 does a lot of port adjustment on given coordinates
*/
-class GfxCoordAdjuster {
-public:
- GfxCoordAdjuster();
- virtual ~GfxCoordAdjuster() { }
-
- virtual void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG) { }
- virtual void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG) { }
-
- virtual Common::Rect onControl(Common::Rect rect) { return rect; }
- virtual void setCursorPos(Common::Point &pos) { }
- virtual void moveCursor(Common::Point &pos) { }
-
- virtual void setScriptsResolution(uint16 width, uint16 height) { }
- virtual void fromScriptToDisplay(int16 &y, int16 &x) { }
- virtual void fromDisplayToScript(int16 &y, int16 &x) { }
-
- virtual Common::Rect pictureGetDisplayArea() { return Common::Rect(0, 0); }
-private:
-};
-
-class GfxCoordAdjuster16 : public GfxCoordAdjuster {
+class GfxCoordAdjuster16 {
public:
GfxCoordAdjuster16(GfxPorts *ports);
~GfxCoordAdjuster16();
@@ -73,32 +53,6 @@ private:
GfxPorts *_ports;
};
-#ifdef ENABLE_SCI32
-class GfxCoordAdjuster32 : public GfxCoordAdjuster {
-public:
- GfxCoordAdjuster32(SegManager *segMan);
- ~GfxCoordAdjuster32();
-
- void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG);
- void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG);
-
- void setScriptsResolution(uint16 width, uint16 height);
- void fromScriptToDisplay(int16 &y, int16 &x);
- void fromDisplayToScript(int16 &y, int16 &x);
-
- void pictureSetDisplayArea(Common::Rect displayArea);
- Common::Rect pictureGetDisplayArea();
-
-private:
- SegManager *_segMan;
-
- Common::Rect _pictureDisplayArea;
-
- uint16 _scriptsRunningWidth;
- uint16 _scriptsRunningHeight;
-};
-#endif
-
} // End of namespace Sci
#endif
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index f5dd473959..c3229121c8 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -80,7 +80,7 @@ GfxCursor::~GfxCursor() {
kernelClearZoomZone();
}
-void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) {
+void GfxCursor::init(GfxCoordAdjuster16 *coordAdjuster, EventManager *event) {
_coordAdjuster = coordAdjuster;
_event = event;
}
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 5125469cfe..c57d9dab52 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -55,7 +55,7 @@ public:
GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen);
~GfxCursor();
- void init(GfxCoordAdjuster *coordAdjuster, EventManager *event);
+ void init(GfxCoordAdjuster16 *coordAdjuster, EventManager *event);
void kernelShow();
void kernelHide();
@@ -103,7 +103,7 @@ private:
ResourceManager *_resMan;
GfxScreen *_screen;
GfxPalette *_palette;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
EventManager *_event;
int _upscaledHires;
diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp
new file mode 100644
index 0000000000..014b617c74
--- /dev/null
+++ b/engines/sci/graphics/cursor32.cpp
@@ -0,0 +1,396 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/rational.h" // for Rational, operator*
+#include "common/system.h" // for OSystem, g_system
+#include "graphics/cursorman.h" // for CursorMan
+#include "sci/graphics/celobj32.h" // for CelObjView, CelInfo32, Ratio
+#include "sci/graphics/cursor32.h"
+#include "sci/graphics/frameout.h" // for GfxFrameout
+
+namespace Sci {
+
+GfxCursor32::GfxCursor32() :
+ _hideCount(0),
+ _position(0, 0),
+ _writeToVMAP(false) {
+ CursorMan.showMouse(false);
+}
+
+void GfxCursor32::init(const Buffer &vmap) {
+ _vmap = vmap;
+ _vmapRegion.rect = Common::Rect(_vmap.screenWidth, _vmap.screenHeight);
+ _vmapRegion.data = (byte *)_vmap.getPixels();
+ _restrictedArea = _vmapRegion.rect;
+}
+
+GfxCursor32::~GfxCursor32() {
+ CursorMan.showMouse(true);
+ free(_cursor.data);
+ free(_cursorBack.data);
+ free(_drawBuff1.data);
+ free(_drawBuff2.data);
+ free(_savedVmapRegion.data);
+}
+
+void GfxCursor32::hide() {
+ if (_hideCount++) {
+ return;
+ }
+
+ if (!_cursorBack.rect.isEmpty()) {
+ drawToHardware(_cursorBack);
+ }
+}
+
+void GfxCursor32::revealCursor() {
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ if (_cursorBack.rect.isEmpty()) {
+ return;
+ }
+
+ readVideo(_cursorBack);
+ _drawBuff1.rect = _cursor.rect;
+ copy(_drawBuff1, _cursorBack);
+ paint(_drawBuff1, _cursor);
+ drawToHardware(_drawBuff1);
+}
+
+void GfxCursor32::paint(DrawRegion &target, const DrawRegion &source) {
+ if (source.rect.isEmpty()) {
+ return;
+ }
+
+ Common::Rect drawRect(source.rect);
+ drawRect.clip(target.rect);
+ if (drawRect.isEmpty()) {
+ return;
+ }
+
+ const int16 sourceXOffset = drawRect.left - source.rect.left;
+ const int16 sourceYOffset = drawRect.top - source.rect.top;
+ const int16 drawRectWidth = drawRect.width();
+ const int16 drawRectHeight = drawRect.height();
+
+ byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left);
+ const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset;
+ const uint8 skipColor = source.skipColor;
+
+ const int16 sourceStride = source.rect.width() - drawRectWidth;
+ const int16 targetStride = target.rect.width() - drawRectWidth;
+
+ for (int16 y = 0; y < drawRectHeight; ++y) {
+ for (int16 x = 0; x < drawRectWidth; ++x) {
+ if (*sourcePixel != skipColor) {
+ *targetPixel = *sourcePixel;
+ }
+ ++targetPixel;
+ ++sourcePixel;
+ }
+ sourcePixel += sourceStride;
+ targetPixel += targetStride;
+ }
+}
+
+void GfxCursor32::drawToHardware(const DrawRegion &source) {
+ Common::Rect drawRect(source.rect);
+ drawRect.clip(_vmapRegion.rect);
+ const int16 sourceXOffset = drawRect.left - source.rect.left;
+ const int16 sourceYOffset = drawRect.top - source.rect.top;
+ byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset;
+
+ g_system->copyRectToScreen(sourcePixel, source.rect.width(), drawRect.left, drawRect.top, drawRect.width(), drawRect.height());
+}
+
+void GfxCursor32::unhide() {
+ if (_hideCount == 0 || --_hideCount) {
+ return;
+ }
+
+ _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y);
+ revealCursor();
+}
+
+void GfxCursor32::show() {
+ if (_hideCount) {
+ _hideCount = 0;
+ _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y);
+ revealCursor();
+ }
+}
+
+void GfxCursor32::setRestrictedArea(const Common::Rect &rect) {
+ _restrictedArea = rect;
+
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ mulru(_restrictedArea, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight), 0);
+
+ if (_position.x < rect.left) {
+ _position.x = rect.left;
+ }
+ if (_position.x >= rect.right) {
+ _position.x = rect.right - 1;
+ }
+ if (_position.y < rect.top) {
+ _position.y = rect.top;
+ }
+ if (_position.y >= rect.bottom) {
+ _position.y = rect.bottom - 1;
+ }
+
+ g_system->warpMouse(_position.x, _position.y);
+}
+
+void GfxCursor32::clearRestrictedArea() {
+ _restrictedArea = _vmapRegion.rect;
+}
+
+void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
+ hide();
+
+ _cursorInfo.resourceId = viewId;
+ _cursorInfo.loopNo = loopNo;
+ _cursorInfo.celNo = celNo;
+
+ if (viewId != -1) {
+ CelObjView view(viewId, loopNo, celNo);
+
+ _hotSpot = view._displace;
+ _width = view._width;
+ _height = view._height;
+
+ // SSCI never increased the size of cursors, but some of the cursors
+ // in early SCI32 games were designed for low-resolution display mode
+ // and so are kind of hard to pick out when running in high-resolution
+ // mode.
+ // To address this, we make some slight adjustments to cursor display
+ // in these early games:
+ // GK1: All the cursors are increased in size since they all appear to
+ // be designed for low-res display.
+ // PQ4: We only make the cursors bigger if they are above a set
+ // threshold size because inventory items usually have a
+ // high-resolution cursor representation.
+ bool pixelDouble = false;
+ if (g_sci->_gfxFrameout->_isHiRes &&
+ (g_sci->getGameId() == GID_GK1 ||
+ (g_sci->getGameId() == GID_PQ4 && _width <= 22 && _height <= 22))) {
+
+ _width *= 2;
+ _height *= 2;
+ _hotSpot.x *= 2;
+ _hotSpot.y *= 2;
+ pixelDouble = true;
+ }
+
+ _cursor.data = (byte *)realloc(_cursor.data, _width * _height);
+ _cursor.rect = Common::Rect(_width, _height);
+ memset(_cursor.data, 255, _width * _height);
+ _cursor.skipColor = 255;
+
+ Buffer target(_width, _height, _cursor.data);
+ if (pixelDouble) {
+ view.draw(target, _cursor.rect, Common::Point(0, 0), false, 2, 2);
+ } else {
+ view.draw(target, _cursor.rect, Common::Point(0, 0), false);
+ }
+ } else {
+ _hotSpot = Common::Point(0, 0);
+ _width = _height = 1;
+ _cursor.data = (byte *)realloc(_cursor.data, _width * _height);
+ _cursor.rect = Common::Rect(_width, _height);
+ *_cursor.data = _cursor.skipColor;
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ if (!_cursorBack.rect.isEmpty()) {
+ readVideo(_cursorBack);
+ }
+ }
+
+ _cursorBack.data = (byte *)realloc(_cursorBack.data, _width * _height);
+ _drawBuff1.data = (byte *)realloc(_drawBuff1.data, _width * _height);
+ _drawBuff2.data = (byte *)realloc(_drawBuff2.data, _width * _height * 4);
+ _savedVmapRegion.data = (byte *)realloc(_savedVmapRegion.data, _width * _height);
+
+ unhide();
+}
+
+void GfxCursor32::readVideo(DrawRegion &target) {
+ if (g_sci->_gfxFrameout->_frameNowVisible) {
+ copy(target, _vmapRegion);
+ } else {
+ // NOTE: SSCI would read the background for the cursor directly out of
+ // video memory here, but as far as can be determined, this does not
+ // seem to actually be necessary for proper cursor rendering
+ }
+}
+
+void GfxCursor32::copy(DrawRegion &target, const DrawRegion &source) {
+ if (source.rect.isEmpty()) {
+ return;
+ }
+
+ Common::Rect drawRect(source.rect);
+ drawRect.clip(target.rect);
+ if (drawRect.isEmpty()) {
+ return;
+ }
+
+ const int16 sourceXOffset = drawRect.left - source.rect.left;
+ const int16 sourceYOffset = drawRect.top - source.rect.top;
+ const int16 drawWidth = drawRect.width();
+ const int16 drawHeight = drawRect.height();
+
+ byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left);
+ const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset;
+
+ const int16 sourceStride = source.rect.width();
+ const int16 targetStride = target.rect.width();
+
+ for (int y = 0; y < drawHeight; ++y) {
+ memcpy(targetPixel, sourcePixel, drawWidth);
+ targetPixel += targetStride;
+ sourcePixel += sourceStride;
+ }
+}
+
+void GfxCursor32::setPosition(const Common::Point &position) {
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ _position.x = (position.x * Ratio(screenWidth, scriptWidth)).toInt();
+ _position.y = (position.y * Ratio(screenHeight, scriptHeight)).toInt();
+
+ g_system->warpMouse(_position.x, _position.y);
+}
+
+void GfxCursor32::gonnaPaint(Common::Rect paintRect) {
+ if (!_hideCount && !_writeToVMAP && !_cursorBack.rect.isEmpty()) {
+ paintRect.left &= ~3;
+ paintRect.right |= 3;
+ if (_cursorBack.rect.intersects(paintRect)) {
+ _writeToVMAP = true;
+ }
+ }
+}
+
+void GfxCursor32::paintStarting() {
+ if (_writeToVMAP) {
+ _savedVmapRegion.rect = _cursor.rect;
+ copy(_savedVmapRegion, _vmapRegion);
+ paint(_vmapRegion, _cursor);
+ }
+}
+
+void GfxCursor32::donePainting() {
+ if (_writeToVMAP) {
+ copy(_vmapRegion, _savedVmapRegion);
+ _savedVmapRegion.rect = Common::Rect();
+ _writeToVMAP = false;
+ }
+
+ if (!_hideCount && !_cursorBack.rect.isEmpty()) {
+ copy(_cursorBack, _vmapRegion);
+ }
+}
+
+void GfxCursor32::deviceMoved(Common::Point &position) {
+ if (position.x < _restrictedArea.left) {
+ position.x = _restrictedArea.left;
+ }
+ if (position.x >= _restrictedArea.right) {
+ position.x = _restrictedArea.right - 1;
+ }
+ if (position.y < _restrictedArea.top) {
+ position.y = _restrictedArea.top;
+ }
+ if (position.y >= _restrictedArea.bottom) {
+ position.y = _restrictedArea.bottom - 1;
+ }
+
+ _position = position;
+
+ g_system->warpMouse(position.x, position.y);
+ move();
+}
+
+void GfxCursor32::move() {
+ if (_hideCount) {
+ return;
+ }
+
+ // Cursor moved onto the screen after being offscreen
+ _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y);
+ if (_cursorBack.rect.isEmpty()) {
+ revealCursor();
+ return;
+ }
+
+ // Cursor moved offscreen
+ if (!_cursor.rect.intersects(_vmapRegion.rect)) {
+ drawToHardware(_cursorBack);
+ return;
+ }
+
+ if (!_cursor.rect.intersects(_cursorBack.rect)) {
+ // Cursor moved to a completely different part of the screen
+ _drawBuff1.rect = _cursor.rect;
+ _drawBuff1.rect.clip(_vmapRegion.rect);
+ readVideo(_drawBuff1);
+
+ _drawBuff2.rect = _drawBuff1.rect;
+ copy(_drawBuff2, _drawBuff1);
+
+ paint(_drawBuff1, _cursor);
+ drawToHardware(_drawBuff1);
+
+ drawToHardware(_cursorBack);
+
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ copy(_cursorBack, _drawBuff2);
+ } else {
+ // Cursor moved, but still overlaps the previous cursor location
+ Common::Rect mergedRect(_cursorBack.rect);
+ mergedRect.extend(_cursor.rect);
+ mergedRect.clip(_vmapRegion.rect);
+
+ _drawBuff2.rect = mergedRect;
+ readVideo(_drawBuff2);
+
+ copy(_drawBuff2, _cursorBack);
+
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ copy(_cursorBack, _drawBuff2);
+
+ paint(_drawBuff2, _cursor);
+ drawToHardware(_drawBuff2);
+ }
+}
+} // End of namespace Sci
diff --git a/engines/sci/graphics/cursor32.h b/engines/sci/graphics/cursor32.h
new file mode 100644
index 0000000000..d4745536b1
--- /dev/null
+++ b/engines/sci/graphics/cursor32.h
@@ -0,0 +1,250 @@
+/* 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.
+ *
+ */
+
+#ifndef SCI_GRAPHICS_CURSOR32_H
+#define SCI_GRAPHICS_CURSOR32_H
+
+#include "common/rect.h" // for Point, Rect
+#include "common/scummsys.h" // for int16, byte, uint8
+#include "common/serializer.h" // for Serializable, Serializer (ptr only)
+#include "sci/graphics/celobj32.h" // for CelInfo32
+#include "sci/graphics/helpers.h" // for GuiResourceId
+
+namespace Sci {
+
+class GfxCursor32 : Common::Serializable {
+public:
+ GfxCursor32();
+ ~GfxCursor32();
+
+ /**
+ * Initialises the cursor system with the given
+ * buffer to use as the output buffer for
+ * rendering the cursor.
+ */
+ void init(const Buffer &vmap);
+
+ /**
+ * Called when the hardware mouse moves.
+ */
+ void deviceMoved(Common::Point &position);
+
+ /**
+ * Called by GfxFrameout once for each show
+ * rectangle that is going to be drawn to
+ * hardware.
+ */
+ void gonnaPaint(Common::Rect paintRect);
+
+ /**
+ * Called by GfxFrameout when the rendering to
+ * hardware begins.
+ */
+ void paintStarting();
+
+ /**
+ * Called by GfxFrameout when the output buffer
+ * has finished rendering to hardware.
+ */
+ void donePainting();
+
+ /**
+ * Hides the cursor. Each call to `hide` will
+ * increment a hide counter, which must be
+ * returned to 0 before the cursor will be
+ * shown again.
+ */
+ void hide();
+
+ /**
+ * Shows the cursor, if the hide counter is
+ * returned to 0.
+ */
+ void unhide();
+
+ /**
+ * Shows the cursor regardless of the state of
+ * the hide counter.
+ */
+ void show();
+
+ /**
+ * Sets the view used to render the cursor.
+ */
+ void setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo);
+
+ /**
+ * Explicitly sets the position of the cursor,
+ * in game script coordinates.
+ */
+ void setPosition(const Common::Point &position);
+
+ /**
+ * Sets the region that the mouse is allowed
+ * to move within.
+ */
+ void setRestrictedArea(const Common::Rect &rect);
+
+ /**
+ * Removes restrictions on mouse movement.
+ */
+ void clearRestrictedArea();
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
+
+private:
+ struct DrawRegion {
+ Common::Rect rect;
+ byte *data;
+ uint8 skipColor;
+
+ DrawRegion() : rect(), data(nullptr) {}
+ };
+
+ /**
+ * Information about the current cursor.
+ * Used to restore cursor when loading a
+ * savegame.
+ */
+ CelInfo32 _cursorInfo;
+
+ /**
+ * Content behind the cursor? TODO
+ */
+ DrawRegion _cursorBack;
+
+ /**
+ * Scratch buffer.
+ */
+ DrawRegion _drawBuff1;
+
+ /**
+ * Scratch buffer 2.
+ */
+ DrawRegion _drawBuff2;
+
+ /**
+ * A draw region representing the current
+ * output buffer.
+ */
+ DrawRegion _vmapRegion;
+
+ /**
+ * The content behind the cursor in the
+ * output buffer.
+ */
+ DrawRegion _savedVmapRegion;
+
+ /**
+ * The cursor bitmap.
+ */
+ DrawRegion _cursor;
+
+ /**
+ * The width and height of the cursor,
+ * in screen coordinates.
+ */
+ int16 _width, _height;
+
+ /**
+ * The output buffer where the cursor is
+ * rendered.
+ */
+ Buffer _vmap;
+
+ /**
+ * The number of times the cursor has been
+ * hidden.
+ */
+ int _hideCount;
+
+ /**
+ * The rendered position of the cursor, in
+ * screen coordinates.
+ */
+ Common::Point _position;
+
+ /**
+ * The position of the cursor hot spot, relative
+ * to the cursor origin, in screen pixels.
+ */
+ Common::Point _hotSpot;
+
+ /**
+ * The area within which the cursor is allowed
+ * to move, in screen pixels.
+ */
+ Common::Rect _restrictedArea;
+
+ /**
+ * Indicates whether or not the cursor needs to
+ * be repainted on the output buffer due to a
+ * change of graphics in the area underneath the
+ * cursor.
+ */
+ bool _writeToVMAP;
+
+ /**
+ * Reads data from the output buffer or hardware
+ * to the given draw region.
+ */
+ void readVideo(DrawRegion &target);
+
+ /**
+ * Reads data from the output buffer to the
+ * given draw region.
+ */
+ void readVideoFromVmap(DrawRegion &target);
+
+ /**
+ * Copies pixel data from the given source to
+ * the given target.
+ */
+ void copy(DrawRegion &target, const DrawRegion &source);
+
+ /**
+ * Draws from the given source onto the given
+ * target, skipping pixels in the source that
+ * match the `skipColor` property.
+ */
+ void paint(DrawRegion &target, const DrawRegion &source);
+
+ /**
+ * Draws the cursor to the position it was
+ * drawn to prior to moving offscreen or being
+ * hidden by a call to `hide`.
+ */
+ void revealCursor();
+
+ /**
+ * Draws the given source to the output buffer.
+ */
+ void drawToHardware(const DrawRegion &source);
+
+ /**
+ * Renders the cursor at its new location.
+ */
+ void move();
+};
+
+} // End of namespace Sci
+#endif
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 21ffb5f937..4e0aa22669 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -29,6 +29,7 @@
#include "common/system.h"
#include "common/textconsole.h"
#include "engines/engine.h"
+#include "engines/util.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
@@ -39,47 +40,53 @@
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
#include "sci/graphics/cache.h"
-#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/compare.h"
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/font.h"
-#include "sci/graphics/screen.h"
+#include "sci/graphics/frameout.h"
#include "sci/graphics/paint32.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/plane32.h"
#include "sci/graphics/remap32.h"
+#include "sci/graphics/screen.h"
#include "sci/graphics/screen_item32.h"
#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
-#include "sci/video/robot_decoder.h"
#include "sci/graphics/transitions32.h"
+#include "sci/graphics/video32.h"
namespace Sci {
-GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions) :
+GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor) :
_isHiRes(ConfMan.getBool("enable_high_resolution_graphics")),
_palette(palette),
- _resMan(resMan),
- _screen(screen),
+ _cursor(cursor),
_segMan(segMan),
_transitions(transitions),
_benchmarkingFinished(false),
_throttleFrameOut(true),
_throttleState(0),
- // TODO: Stop using _gfxScreen
- _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr),
_remapOccurred(false),
_frameNowVisible(false),
- _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()),
_overdrawThreshold(0),
_palMorphIsOn(false) {
- _currentBuffer.setPixels(calloc(1, screen->getDisplayWidth() * screen->getDisplayHeight()));
-
- // QFG4 is the only SCI32 game that doesn't have a high-resolution toggle
+ // QFG4 is the only SCI32 game that doesn't have a high-resolution version
if (g_sci->getGameId() == GID_QFG4) {
_isHiRes = false;
}
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
+ _currentBuffer = Buffer(630, 450, nullptr);
+ } else if (_isHiRes) {
+ _currentBuffer = Buffer(640, 480, nullptr);
+ } else {
+ _currentBuffer = Buffer(320, 200, nullptr);
+ }
+ _currentBuffer.setPixels(calloc(1, _currentBuffer.screenWidth * _currentBuffer.screenHeight));
+ _screenRect = Common::Rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight);
+ initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes);
+
switch (g_sci->getGameId()) {
case GID_HOYLE5:
case GID_GK2:
@@ -96,20 +103,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
// default script width for other games is 320x200
break;
}
-
- // TODO: Nothing in the renderer really uses this. Currently,
- // the cursor renderer does, and kLocalToGlobal/kGlobalToLocal
- // do, but in the real engine (1) the cursor is handled in
- // frameOut, and (2) functions do a very simple lookup of the
- // plane and arithmetic with the plane's gameRect. In
- // principle, CoordAdjuster could be reused for
- // convertGameRectToPlaneRect, but it is not super clear yet
- // what the benefit would be to do that.
- _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
-
- // TODO: Script resolution is hard-coded per game;
- // also this must be set or else the engine will crash
- _coordAdjuster->setScriptsResolution(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight);
}
GfxFrameout::~GfxFrameout() {
@@ -493,10 +486,12 @@ void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pi
#pragma mark Rendering
void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseRect) {
-// TODO: Robot
-// if (_robot != nullptr) {
-// _robot.doRobot();
-// }
+ RobotDecoder &robotPlayer = g_sci->_video32->getRobotPlayer();
+ const bool robotIsActive = robotPlayer.getStatus() != RobotDecoder::kRobotStatusUninitialized;
+
+ if (robotIsActive) {
+ robotPlayer.doRobot();
+ }
// NOTE: The original engine allocated these as static arrays of 100
// pointers to ScreenItemList / RectList
@@ -534,10 +529,9 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR
drawScreenItemList(screenItemLists[i]);
}
-// TODO: Robot
-// if (_robot != nullptr) {
-// _robot->frameAlmostVisible();
-// }
+ if (robotIsActive) {
+ robotPlayer.frameAlmostVisible();
+ }
_palette->updateHardware(!shouldShowBits);
@@ -547,10 +541,9 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR
_frameNowVisible = true;
-// TODO: Robot
-// if (_robot != nullptr) {
-// robot->frameNowVisible();
-// }
+ if (robotIsActive) {
+ robotPlayer.frameNowVisible();
+ }
}
void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle) {
@@ -559,7 +552,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show
int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16();
- Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight());
+ Common::Rect rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight);
_showList.add(rect);
showBits();
@@ -1114,6 +1107,10 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi
}
void GfxFrameout::showBits() {
+ if (!_showList.size()) {
+ return;
+ }
+
for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) {
Common::Rect rounded(**rect);
// NOTE: SCI engine used BR-inclusive rects so used slightly
@@ -1121,13 +1118,10 @@ void GfxFrameout::showBits() {
// was always even.
rounded.left &= ~1;
rounded.right = (rounded.right + 1) & ~1;
-
- // TODO:
- // _cursor->GonnaPaint(rounded);
+ _cursor->gonnaPaint(rounded);
}
- // TODO:
- // _cursor->PaintStarting();
+ _cursor->paintStarting();
for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) {
Common::Rect rounded(**rect);
@@ -1149,8 +1143,7 @@ void GfxFrameout::showBits() {
g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height());
}
- // TODO:
- // _cursor->DonePainting();
+ _cursor->donePainting();
_showList.clear();
}
@@ -1266,6 +1259,30 @@ void GfxFrameout::showRect(const Common::Rect &rect) {
}
}
+void GfxFrameout::shakeScreen(int16 numShakes, const ShakeDirection direction) {
+ if (direction & kShakeHorizontal) {
+ // Used by QFG4 room 750
+ warning("TODO: Horizontal shake not implemented");
+ return;
+ }
+
+ while (numShakes--) {
+ if (direction & kShakeVertical) {
+ g_system->setShakePos(_isHiRes ? 8 : 4);
+ }
+
+ g_system->updateScreen();
+ g_sci->getEngineState()->wait(3);
+
+ if (direction & kShakeVertical) {
+ g_system->setShakePos(0);
+ }
+
+ g_system->updateScreen();
+ g_sci->getEngineState()->wait(3);
+ }
+}
+
#pragma mark -
#pragma mark Mouse cursor
@@ -1324,7 +1341,7 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const
return true;
}
-void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
+bool GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane));
Plane *plane = _planes.findByObject(planeObject);
@@ -1334,7 +1351,7 @@ void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject);
if (screenItem == nullptr) {
- error("kSetNowSeen: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItemObject), PRINT_REG(planeObject));
+ return false;
}
Common::Rect result = screenItem->getNowSeenRect(*plane);
@@ -1342,6 +1359,7 @@ void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top);
writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1);
writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1);
+ return true;
}
void GfxFrameout::remapMarkRedraw() {
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 012ecf9e64..e4caffd9e5 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -30,8 +30,7 @@ namespace Sci {
typedef Common::Array<DrawList> ScreenItemListList;
typedef Common::Array<RectList> EraseListList;
-class GfxCoordAdjuster32;
-class GfxScreen;
+class GfxCursor32;
class GfxTransitions32;
struct PlaneShowStyle;
@@ -41,16 +40,16 @@ struct PlaneShowStyle;
*/
class GfxFrameout {
private:
- GfxCoordAdjuster32 *_coordAdjuster;
+ GfxCursor32 *_cursor;
GfxPalette32 *_palette;
- ResourceManager *_resMan;
- GfxScreen *_screen;
SegManager *_segMan;
public:
- GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions);
+ GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor);
~GfxFrameout();
+ bool _isHiRes;
+
void clear();
void syncWithScripts(bool addElements); // this is what Game::restore does, only needed when our ScummVM dialogs are patched in
void run();
@@ -111,7 +110,7 @@ public:
void kernelAddScreenItem(const reg_t object);
void kernelUpdateScreenItem(const reg_t object);
void kernelDeleteScreenItem(const reg_t object);
- void kernelSetNowSeen(const reg_t screenItemObject) const;
+ bool kernelSetNowSeen(const reg_t screenItemObject) const;
#pragma mark -
#pragma mark Planes
@@ -196,13 +195,6 @@ private:
bool _remapOccurred;
/**
- * Whether or not the data in the current buffer is what
- * is visible to the user. During rendering updates,
- * this flag is set to false.
- */
- bool _frameNowVisible;
-
- /**
* TODO: Document
* TODO: Depending upon if the engine ever modifies this
* rect, it may be stupid to store it separately instead
@@ -308,7 +300,12 @@ private:
}
public:
- bool _isHiRes;
+ /**
+ * Whether or not the data in the current buffer is what
+ * is visible to the user. During rendering updates,
+ * this flag is set to false.
+ */
+ bool _frameNowVisible;
/**
* Whether palMorphFrameOut should be used instead of
@@ -366,6 +363,11 @@ public:
*/
void showRect(const Common::Rect &rect);
+ /**
+ * Shakes the screen.
+ */
+ void shakeScreen(const int16 numShakes, const ShakeDirection direction);
+
#pragma mark -
#pragma mark Mouse cursor
private:
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index 3fcc83c5e2..1da3749c90 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -40,8 +40,10 @@ namespace Sci {
#define MAX_CACHED_FONTS 20
#define MAX_CACHED_VIEWS 50
-#define SCI_SHAKE_DIRECTION_VERTICAL 1
-#define SCI_SHAKE_DIRECTION_HORIZONTAL 2
+enum ShakeDirection {
+ kShakeVertical = 1,
+ kShakeHorizontal = 2
+};
typedef int GuiResourceId; // is a resource-number and -1 means no parameter given
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 6004e9ce7a..91817d4060 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -41,7 +41,7 @@
namespace Sci {
-GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio)
+GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster16 *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio)
: _resMan(resMan), _segMan(segMan), _cache(cache), _ports(ports),
_coordAdjuster(coordAdjuster), _screen(screen), _palette(palette),
_transitions(transitions), _audio(audio), _EGAdrawingVisualize(false) {
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index 317388b2df..6fc9cbbdfc 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -36,7 +36,7 @@ class GfxView;
*/
class GfxPaint16 {
public:
- GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio);
+ GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster16 *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio);
~GfxPaint16();
void init(GfxAnimate *animate, GfxText16 *text16);
@@ -91,7 +91,7 @@ private:
GfxAnimate *_animate;
GfxCache *_cache;
GfxPorts *_ports;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
GfxScreen *_screen;
GfxPalette *_palette;
GfxText16 *_text16;
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 2eab391afd..0025b24476 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -35,7 +35,7 @@ namespace Sci {
//#define DEBUG_PICTURE_DRAW
-GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize)
+GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster16 *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize)
: _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) {
assert(resourceId != -1);
initData(resourceId);
diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h
index 942fa0f107..1be1ae3004 100644
--- a/engines/sci/graphics/picture.h
+++ b/engines/sci/graphics/picture.h
@@ -38,7 +38,7 @@ enum {
class GfxPorts;
class GfxScreen;
class GfxPalette;
-class GfxCoordAdjuster;
+class GfxCoordAdjuster16;
class ResourceManager;
class Resource;
@@ -48,7 +48,7 @@ class Resource;
*/
class GfxPicture {
public:
- GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize = false);
+ GfxPicture(ResourceManager *resMan, GfxCoordAdjuster16 *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize = false);
~GfxPicture();
GuiResourceId getResourceId();
@@ -84,7 +84,7 @@ private:
void vectorPatternTexturedCircle(Common::Rect box, byte size, byte color, byte prio, byte control, byte texture);
ResourceManager *_resMan;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
GfxPorts *_ports;
GfxScreen *_screen;
GfxPalette *_palette;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index c977a93817..601ab9f09f 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -53,12 +53,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) {
if (g_sci->getGameId() == GID_KQ6)
_upscaledHires = GFX_SCREEN_UPSCALED_640x440;
-#ifdef ENABLE_SCI32
- if (g_sci->getGameId() == GID_GK1)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
- if (g_sci->getGameId() == GID_PQ4)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
-#endif
}
// Japanese versions of games use hi-res font on upscaled version of the game.
@@ -90,28 +84,11 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
}
}
-#ifdef ENABLE_SCI32
- // GK1 Mac uses a 640x480 resolution too
- if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
- if (g_sci->getGameId() == GID_GK1)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
- }
-#endif
-
if (_resMan->detectHires()) {
_scriptWidth = 640;
_scriptHeight = 480;
}
-#ifdef ENABLE_SCI32
- // Phantasmagoria 1 effectively outputs 630x450
- // Coordinate translation has to use this resolution as well
- if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
- _width = 630;
- _height = 450;
- }
-#endif
-
// if not yet set, set those to script-width/height
if (!_width)
_width = _scriptWidth;
@@ -632,13 +609,13 @@ void GfxScreen::setVerticalShakePos(uint16 shakePos) {
void GfxScreen::kernelShakeScreen(uint16 shakeCount, uint16 directions) {
while (shakeCount--) {
- if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
+ if (directions & kShakeVertical)
setVerticalShakePos(10);
// TODO: horizontal shakes
g_system->updateScreen();
g_sci->getEngineState()->wait(3);
- if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
+ if (directions & kShakeVertical)
setVerticalShakePos(0);
g_system->updateScreen();
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index 3d9d5ef3d7..4221c0ea52 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -31,6 +31,7 @@ namespace Sci {
enum ScaleSignals32 {
kScaleSignalNone = 0,
+ // TODO: rename to 'manual'
kScaleSignalDoScaling32 = 1, // enables scaling when drawing that cel (involves scaleX and scaleY)
kScaleSignalUseVanishingPoint = 2,
// TODO: Is this actually a thing? I have not seen it and
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index b0f2c52791..cb6e614657 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -633,7 +633,7 @@ reg_t GfxText16::allocAndFillReferenceRectArray() {
if (rectCount) {
reg_t rectArray;
byte *rectArrayPtr = g_sci->getEngineState()->_segMan->allocDynmem(4 * 2 * (rectCount + 1), "text code reference rects", &rectArray);
- GfxCoordAdjuster *coordAdjuster = g_sci->_gfxCoordAdjuster;
+ GfxCoordAdjuster16 *coordAdjuster = g_sci->_gfxCoordAdjuster;
for (uint curRect = 0; curRect < rectCount; curRect++) {
coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].left, _codeRefRects[curRect].top);
coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].right, _codeRefRects[curRect].bottom);
diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp
index bceb0fa84d..37f608da85 100644
--- a/engines/sci/graphics/transitions32.cpp
+++ b/engines/sci/graphics/transitions32.cpp
@@ -203,10 +203,6 @@ void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeOb
color = 0;
}
- if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) {
- error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj));
- }
-
Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj);
if (plane == nullptr) {
error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj));
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp
index 51be08dac9..8b1d4ef32b 100644
--- a/engines/sci/graphics/video32.cpp
+++ b/engines/sci/graphics/video32.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "audio/mixer.h" // for Audio::Mixer::kSFXSoundType
#include "common/config-manager.h" // for ConfMan
#include "common/textconsole.h" // for warning, error
#include "common/util.h" // for ARRAYSIZE
@@ -31,7 +32,7 @@
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/event.h" // for SciEvent, EventManager, SCI_...
#include "sci/graphics/celobj32.h" // for CelInfo32, ::kLowResX, ::kLo...
-#include "sci/graphics/cursor.h" // for GfxCursor
+#include "sci/graphics/cursor32.h" // for GfxCursor32
#include "sci/graphics/frameout.h" // for GfxFrameout
#include "sci/graphics/helpers.h" // for Color, Palette
#include "sci/graphics/palette32.h" // for GfxPalette32
@@ -269,6 +270,11 @@ void AVIPlayer::init() {
g_sci->_gfxFrameout->addScreenItem(*_screenItem);
g_sci->_gfxFrameout->frameOut(true);
} else {
+ // Attempting to draw a palettized cursor into a 24bpp surface will
+ // cause memory corruption, so hide the cursor in this mode (SCI did not
+ // have a 24bpp mode but just directed VFW to display videos instead)
+ g_sci->_gfxCursor32->hide();
+
const Buffer &currentBuffer = g_sci->_gfxFrameout->getCurrentBuffer();
const Graphics::PixelFormat format = _decoder->getPixelFormat();
initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, g_sci->_gfxFrameout->_isHiRes, &format);
@@ -326,6 +332,7 @@ AVIPlayer::IOStatus AVIPlayer::close() {
const Buffer &currentBuffer = g_sci->_gfxFrameout->getCurrentBuffer();
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, isHiRes, &format);
+ g_sci->_gfxCursor32->unhide();
}
_decoder->close();
@@ -597,7 +604,7 @@ VMDPlayer::IOStatus VMDPlayer::close() {
}
if (!_showCursor) {
- g_sci->_gfxCursor->kernelShow();
+ g_sci->_gfxCursor32->unhide();
}
_lastYieldedFrameNo = 0;
@@ -606,6 +613,22 @@ VMDPlayer::IOStatus VMDPlayer::close() {
return kIOSuccess;
}
+VMDPlayer::VMDStatus VMDPlayer::getStatus() const {
+ if (!_isOpen) {
+ return kVMDNotOpen;
+ }
+ if (_decoder->isPaused()) {
+ return kVMDPaused;
+ }
+ if (_decoder->isPlaying()) {
+ return kVMDPlaying;
+ }
+ if (_decoder->endOfVideo()) {
+ return kVMDFinished;
+ }
+ return kVMDOpen;
+}
+
VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval) {
assert(lastFrameNo >= -1);
@@ -658,7 +681,7 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
_isInitialized = true;
if (!_showCursor) {
- g_sci->_gfxCursor->kernelHide();
+ g_sci->_gfxCursor32->hide();
}
Common::Rect vmdRect(_x,
diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h
index 0496f61d5d..75b8fb2d21 100644
--- a/engines/sci/graphics/video32.h
+++ b/engines/sci/graphics/video32.h
@@ -27,6 +27,7 @@
#include "common/scummsys.h" // for int16, uint8, uint16, int32
#include "common/str.h" // for String
#include "sci/engine/vm_types.h" // for reg_t
+#include "sci/video/robot_decoder.h" // for RobotDecoder
namespace Video {
class AdvancedVMDDecoder;
@@ -267,6 +268,15 @@ public:
kEventFlagReverse = 0x80
};
+ enum VMDStatus {
+ kVMDNotOpen = 0,
+ kVMDOpen = 1,
+ kVMDPlaying = 2,
+ kVMDPaused = 3,
+ kVMDStopped = 4,
+ kVMDFinished = 5
+ };
+
VMDPlayer(SegManager *segMan, EventManager *eventMan);
~VMDPlayer();
@@ -294,6 +304,11 @@ public:
*/
IOStatus close();
+ /**
+ * Gets the playback status of the VMD player.
+ */
+ VMDStatus getStatus() const;
+
// NOTE: Was WaitForEvent in SSCI
EventFlags kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval);
@@ -505,16 +520,19 @@ public:
Video32(SegManager *segMan, EventManager *eventMan) :
_SEQPlayer(segMan),
_AVIPlayer(segMan, eventMan),
- _VMDPlayer(segMan, eventMan) {}
+ _VMDPlayer(segMan, eventMan),
+ _robotPlayer(segMan) {}
SEQPlayer &getSEQPlayer() { return _SEQPlayer; }
AVIPlayer &getAVIPlayer() { return _AVIPlayer; }
VMDPlayer &getVMDPlayer() { return _VMDPlayer; }
+ RobotDecoder &getRobotPlayer() { return _robotPlayer; }
private:
SEQPlayer _SEQPlayer;
AVIPlayer _AVIPlayer;
VMDPlayer _VMDPlayer;
+ RobotDecoder _robotPlayer;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 1939e66179..0c09fcbb30 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -351,18 +351,6 @@ void GfxView::initData(GuiResourceId resourceId) {
celData += celSize;
}
}
-#ifdef ENABLE_SCI32
- // adjust width/height returned to scripts
- if (_sci2ScaleRes != SCI_VIEW_NATIVERES_NONE) {
- for (loopNo = 0; loopNo < _loopCount; loopNo++)
- for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
- _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight, _sci2ScaleRes);
- } else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) {
- for (loopNo = 0; loopNo < _loopCount; loopNo++)
- for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
- _coordAdjuster->fromDisplayToScript(_loop[loopNo].cel[celNo].scriptHeight, _loop[loopNo].cel[celNo].scriptWidth);
- }
-#endif
break;
default:
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 96b48c0477..5e422468b5 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -92,7 +92,7 @@ private:
void unditherBitmap(byte *bitmap, int16 width, int16 height, byte clearKey);
ResourceManager *_resMan;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
GfxScreen *_screen;
GfxPalette *_palette;
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 18d97ea57e..eb2c6a148b 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -93,6 +93,7 @@ MODULE_OBJS += \
graphics/text32.o \
graphics/transitions32.o \
graphics/video32.o \
+ graphics/cursor32.o \
sound/audio32.o \
sound/decoders/sol.o \
video/robot_decoder.o
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 48278e35a7..2bd941b11a 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -573,6 +573,9 @@ Resource *ResourceManager::testResource(ResourceId id) {
}
int ResourceManager::addAppropriateSources() {
+#ifdef ENABLE_SCI32
+ _multiDiscAudio = false;
+#endif
if (Common::File::exists("resource.map")) {
// SCI0-SCI2 file naming scheme
ResourceSource *map = addExternalMap("resource.map");
@@ -615,6 +618,10 @@ int ResourceManager::addAppropriateSources() {
if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size())
return 0;
+ if (Common::File::exists("resaud.001")) {
+ _multiDiscAudio = true;
+ }
+
for (Common::ArchiveMemberList::const_iterator mapIterator = mapFiles.begin(); mapIterator != mapFiles.end(); ++mapIterator) {
Common::String mapName = (*mapIterator)->getName();
int mapNumber = atoi(strrchr(mapName.c_str(), '.') + 1);
@@ -859,6 +866,13 @@ void ResourceManager::addResourcesFromChunk(uint16 id) {
scanNewSources();
}
+void ResourceManager::findDisc(const int16 discNo) {
+ // Since all resources are expected to be copied from the original discs
+ // into a single game directory, this call just records the number of the CD
+ // that the game has requested
+ _currentDiscNo = discNo;
+}
+
#endif
void ResourceManager::freeResourceSources() {
@@ -878,7 +892,9 @@ void ResourceManager::init() {
_LRU.clear();
_resMap.clear();
_audioMapSCI1 = NULL;
-
+#ifdef ENABLE_SCI32
+ _currentDiscNo = 1;
+#endif
// FIXME: put this in an Init() function, so that we can error out if detection fails completely
_mapVersion = detectMapVersion();
@@ -1477,6 +1493,12 @@ void ResourceManager::readResourcePatchesBase36() {
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
name = (*x)->getName();
+ // The S/T prefixes often conflict with non-patch files and generate
+ // spurious warnings about invalid patches
+ if (name.hasSuffix(".DLL") || name.hasSuffix(".EXE") || name.hasSuffix(".TXT")) {
+ continue;
+ }
+
ResourceId resource36 = convertPatchNameBase36((ResourceType)i, name);
/*
@@ -1738,11 +1760,42 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// if we use the first entries in the resource file, half of the
// game will be English and umlauts will also be missing :P
if (resource->_source->getSourceType() == kSourceVolume) {
+ // Maps are read during the scanning process (below), so
+ // need to be treated as unallocated in order for the new
+ // data from this volume to be picked up and used
+ if (resId.getType() == kResourceTypeMap) {
+ resource->_status = kResStatusNoMalloc;
+ }
resource->_source = source;
resource->_fileOffset = fileOffset;
resource->size = 0;
}
}
+
+#ifdef ENABLE_SCI32
+ // Different CDs may have different audio maps on each disc. The
+ // ResourceManager does not know how to deal with this; it expects
+ // each resource ID to be unique across an entire game. To work
+ // around this problem, all audio maps from this disc must be
+ // processed immediately, since they will be replaced by the audio
+ // map from the next disc on the next call to readResourceMapSCI1
+ if (_multiDiscAudio && resId.getType() == kResourceTypeMap) {
+ IntMapResourceSource *audioMap = static_cast<IntMapResourceSource *>(addSource(new IntMapResourceSource("MAP", mapVolumeNr, resId.getNumber())));
+ Common::String volumeName;
+ if (resId.getNumber() == 65535) {
+ volumeName = Common::String::format("RESSFX.%03d", mapVolumeNr);
+ } else {
+ volumeName = Common::String::format("RESAUD.%03d", mapVolumeNr);
+ }
+
+ ResourceSource *audioVolume = addSource(new AudioVolumeResourceSource(this, volumeName, audioMap, mapVolumeNr));
+ if (!audioMap->_scanned) {
+ audioVolume->_scanned = true;
+ audioMap->_scanned = true;
+ audioMap->scanSource(this);
+ }
+ }
+#endif
}
}
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index f70bf48bd4..70db5909b7 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -296,6 +296,7 @@ protected:
typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash> ResourceMap;
+class IntMapResourceSource;
class ResourceManager {
// FIXME: These 'friend' declarations are meant to be a temporary hack to
// ease transition to the ResourceSource class system.
@@ -397,6 +398,30 @@ public:
* resource manager.
*/
void addResourcesFromChunk(uint16 id);
+
+ /**
+ * Updates the currently active disc number.
+ */
+ void findDisc(const int16 discNo);
+
+ /**
+ * Gets the currently active disc number.
+ */
+ int16 getCurrentDiscNo() const { return _currentDiscNo; }
+
+private:
+ /**
+ * The currently active disc number.
+ */
+ int16 _currentDiscNo;
+
+ /**
+ * If true, the game has multiple audio volumes that contain different
+ * audio files for each disc.
+ */
+ bool _multiDiscAudio;
+
+public:
#endif
bool detectHires();
@@ -520,7 +545,7 @@ protected:
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
- int readAudioMapSCI11(ResourceSource *map);
+ int readAudioMapSCI11(IntMapResourceSource *map);
/**
* Reads SCI1 audio map files.
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 5ab443a16d..cbc4a02739 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -277,7 +277,7 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
// w syncSize (iff seq has bit 7 set)
// w syncAscSize (iff seq has bit 6 set)
-int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
+int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
#ifndef ENABLE_SCI32
// SCI32 support is not built in. Check if this is a SCI32 game
// and if it is abort here.
@@ -286,17 +286,19 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
#endif
uint32 offset = 0;
- Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false);
+ Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_mapNumber), false);
if (!mapRes) {
- warning("Failed to open %i.MAP", map->_volumeNumber);
+ warning("Failed to open %i.MAP", map->_mapNumber);
return SCI_ERROR_RESMAP_NOT_FOUND;
}
- ResourceSource *src = findVolume(map, 0);
+ ResourceSource *src = findVolume(map, map->_volumeNumber);
- if (!src)
+ if (!src) {
+ warning("Failed to find volume for %i.MAP", map->_mapNumber);
return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
+ }
byte *ptr = mapRes->data;
@@ -309,7 +311,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
break;
}
- if (map->_volumeNumber == 65535) {
+ if (map->_mapNumber == 65535) {
while (ptr < mapRes->data + mapRes->size) {
uint16 n = READ_LE_UINT16(ptr);
ptr += 2;
@@ -327,7 +329,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
addResource(ResourceId(kResourceTypeAudio, n), src, offset);
}
- } else if (map->_volumeNumber == 0 && entrySize == 10 && ptr[3] == 0) {
+ } else if (map->_mapNumber == 0 && entrySize == 10 && ptr[3] == 0) {
// QFG3 demo format
// ptr[3] would be 'seq' in the normal format and cannot possibly be 0
while (ptr < mapRes->data + mapRes->size) {
@@ -344,7 +346,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
}
- } else if (map->_volumeNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
+ } else if (map->_mapNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
// LB2 Floppy/Mother Goose SCI1.1 format
Common::SeekableReadStream *stream = getVolumeFile(src);
@@ -400,7 +402,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
// FIXME: The sync36 resource seems to be two bytes too big in KQ6CD
// (bytes taken from the RAVE resource right after it)
if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->_volumeNumber, n & 0xffffff3f), src, offset, syncSize);
+ addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize);
}
if (n & 0x40) {
@@ -410,12 +412,12 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
ptr += 2;
if (kq6HiresSyncSize > 0) {
- addResource(ResourceId(kResourceTypeRave, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize);
+ addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize);
syncSize += kq6HiresSyncSize;
}
}
- addResource(ResourceId(kResourceTypeAudio36, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize);
+ addResource(ResourceId(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize);
}
}
@@ -937,13 +939,21 @@ void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *
}
bool ResourceManager::addAudioSources() {
+#ifdef ENABLE_SCI32
+ // Multi-disc audio is added during addAppropriateSources for those titles
+ // that require it
+ if (_multiDiscAudio) {
+ return true;
+ }
+#endif
+
Common::List<ResourceId> resources = listResources(kResourceTypeMap);
Common::List<ResourceId>::iterator itr;
for (itr = resources.begin(); itr != resources.end(); ++itr) {
- ResourceSource *src = addSource(new IntMapResourceSource("MAP", itr->getNumber()));
+ ResourceSource *src = addSource(new IntMapResourceSource("MAP", 0, itr->getNumber()));
- if ((itr->getNumber() == 65535) && Common::File::exists("RESOURCE.SFX"))
+ if (itr->getNumber() == 65535 && Common::File::exists("RESOURCE.SFX"))
addSource(new AudioVolumeResourceSource(this, "RESOURCE.SFX", src, 0));
else if (Common::File::exists("RESOURCE.AUD"))
addSource(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0));
@@ -991,7 +1001,7 @@ void ResourceManager::changeAudioDirectory(Common::String path) {
if ((it->getNumber() == 65535))
continue;
- ResourceSource *src = addSource(new IntMapResourceSource(mapName, it->getNumber()));
+ ResourceSource *src = addSource(new IntMapResourceSource(mapName, 0, it->getNumber()));
addSource(new AudioVolumeResourceSource(this, audioResourceName, src, 0));
}
diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h
index 461d684005..fe4b0a97f4 100644
--- a/engines/sci/resource_intern.h
+++ b/engines/sci/resource_intern.h
@@ -134,8 +134,9 @@ public:
class IntMapResourceSource : public ResourceSource {
public:
- IntMapResourceSource(const Common::String &name, int volNum)
- : ResourceSource(kSourceIntMap, name, volNum) {
+ uint16 _mapNumber;
+ IntMapResourceSource(const Common::String &name, int volNum, int mapNum)
+ : ResourceSource(kSourceIntMap, name, volNum), _mapNumber(mapNum) {
}
virtual void scanSource(ResourceManager *resMan);
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 4c178b6ed7..6c51060296 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -65,6 +65,7 @@
#ifdef ENABLE_SCI32
#include "sci/graphics/controls32.h"
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
@@ -72,17 +73,12 @@
#include "sci/graphics/transitions32.h"
#include "sci/graphics/video32.h"
#include "sci/sound/audio32.h"
-// TODO: Move this to video32
-#include "sci/video/robot_decoder.h"
#endif
namespace Sci {
SciEngine *g_sci = 0;
-
-class GfxDriver;
-
SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId)
: Engine(syst), _gameDescription(desc), _gameId(gameId), _rng("sci") {
@@ -96,6 +92,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
#ifdef ENABLE_SCI32
_audio32 = nullptr;
_video32 = nullptr;
+ _gfxCursor32 = nullptr;
#endif
_features = 0;
_resMan = 0;
@@ -130,6 +127,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded");
DebugMan.addDebugChannel(kDebugLevelScriptPatcher, "ScriptPatcher", "Notifies when scripts are patched");
DebugMan.addDebugChannel(kDebugLevelWorkarounds, "Workarounds", "Notifies when workarounds are triggered");
+ DebugMan.addDebugChannel(kDebugLevelVideo, "Video", "Video (SEQ, VMD, RBT) debugging");
DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging");
DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging");
DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game");
@@ -171,11 +169,11 @@ SciEngine::~SciEngine() {
delete _gfxControls32;
delete _gfxPaint32;
delete _gfxText32;
- delete _robotDecoder;
// GfxFrameout and GfxPalette32 must be deleted after Video32 since
// destruction of screen items in the Video32 destructor relies on these
// components
delete _video32;
+ delete _gfxCursor32;
delete _gfxPalette32;
delete _gfxTransitions32;
delete _gfxFrameout;
@@ -244,35 +242,31 @@ Common::Error SciEngine::run() {
_scriptPatcher = new ScriptPatcher();
SegManager *segMan = new SegManager(_resMan, _scriptPatcher);
- // Read user option for hires graphics
+ // Read user option for forcing hires graphics
// Only show/selectable for:
// - King's Quest 6 CD
// - King's Quest 6 CD demo
// - Gabriel Knight 1 CD
// - Police Quest 4 CD
// TODO: Check, if Gabriel Knight 1 floppy supports high resolution
- // TODO: Check, if Gabriel Knight 1 on Mac supports high resolution
- switch (getPlatform()) {
- case Common::kPlatformDOS:
- case Common::kPlatformWindows:
- // Only DOS+Windows
- switch (_gameId) {
- case GID_KQ6:
- case GID_GK1:
- case GID_PQ4:
- if (isCD())
- _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
- break;
- default:
- break;
- }
- default:
- break;
- };
+ //
+ // Gabriel Knight 1 on Mac is hi-res only, so it should NOT get this option.
+ // Confirmed by [md5] and originally by clone2727.
+ if (Common::checkGameGUIOption(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, ConfMan.get("guioptions"))) {
+ // GAMEOPTION_HIGH_RESOLUTION_GRAPHICS is available for the currently detected game,
+ // so read the user option now.
+ // We need to do this, because the option's default is "true", but we don't want "true"
+ // for any game that does not have this option.
+ _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
+ }
- // Initialize the game screen
- _gfxScreen = new GfxScreen(_resMan);
- _gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering"));
+ if (getSciVersion() < SCI_VERSION_2) {
+ // Initialize the game screen
+ _gfxScreen = new GfxScreen(_resMan);
+ _gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering"));
+ } else {
+ _gfxScreen = nullptr;
+ }
_kernel = new Kernel(_resMan, segMan);
_kernel->init();
@@ -709,12 +703,12 @@ void SciEngine::initGraphics() {
#ifdef ENABLE_SCI32
_gfxControls32 = 0;
_gfxText32 = 0;
- _robotDecoder = 0;
_gfxFrameout = 0;
_gfxPaint32 = 0;
_gfxPalette32 = 0;
_gfxRemap32 = 0;
_gfxTransitions32 = 0;
+ _gfxCursor32 = 0;
#endif
if (hasMacIconBar())
@@ -734,24 +728,23 @@ void SciEngine::initGraphics() {
#endif
_gfxCache = new GfxCache(_resMan, _gfxScreen, _gfxPalette16);
- _gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _gfxScreen);
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2) {
// SCI32 graphic objects creation
- _gfxCoordAdjuster = new GfxCoordAdjuster32(_gamestate->_segMan);
- _gfxCursor->init(_gfxCoordAdjuster, _eventMan);
- _gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
+ _gfxCursor32 = new GfxCursor32();
+ _gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, nullptr, _gfxCoordAdjuster);
_gfxPaint32 = new GfxPaint32(_gamestate->_segMan);
- _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
_gfxTransitions32 = new GfxTransitions32(_gamestate->_segMan);
- _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32, _gfxTransitions32);
+ _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _gfxPalette32, _gfxTransitions32, _gfxCursor32);
+ _gfxCursor32->init(_gfxFrameout->getCurrentBuffer());
_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache);
_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
_gfxFrameout->run();
} else {
#endif
// SCI0-SCI1.1 graphic objects creation
+ _gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _gfxScreen);
_gfxPorts = new GfxPorts(_gamestate->_segMan, _gfxScreen);
_gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts);
_gfxCursor->init(_gfxCoordAdjuster, _eventMan);
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index a42095259b..b336eb8cce 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -45,6 +45,18 @@ struct ADGameDescription;
*/
namespace Sci {
+// GUI-options, primarily used by detection_tables.h
+#define GAMEOPTION_PREFER_DIGITAL_SFX GUIO_GAMEOPTIONS1
+#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS2
+#define GAMEOPTION_FB01_MIDI GUIO_GAMEOPTIONS3
+#define GAMEOPTION_JONES_CDAUDIO GUIO_GAMEOPTIONS4
+#define GAMEOPTION_KQ6_WINDOWS_CURSORS GUIO_GAMEOPTIONS5
+#define GAMEOPTION_SQ4_SILVER_CURSORS GUIO_GAMEOPTIONS6
+#define GAMEOPTION_EGA_UNDITHER GUIO_GAMEOPTIONS7
+// HIGH_RESOLUTION_GRAPHICS availability is checked for in SciEngine::run()
+#define GAMEOPTION_HIGH_RESOLUTION_GRAPHICS GUIO_GAMEOPTIONS8
+#define GAMEOPTION_ENABLE_BLACK_LINED_VIDEO GUIO_GAMEOPTIONS9
+
struct EngineState;
class Vocabulary;
class ResourceManager;
@@ -63,7 +75,7 @@ class GfxCache;
class GfxCompare;
class GfxControls16;
class GfxControls32;
-class GfxCoordAdjuster;
+class GfxCoordAdjuster16;
class GfxCursor;
class GfxMacIconBar;
class GfxMenu;
@@ -80,12 +92,11 @@ class GfxText32;
class GfxTransitions;
#ifdef ENABLE_SCI32
-// TODO: Move RobotDecoder to Video32
-class RobotDecoder;
class GfxFrameout;
class Audio32;
class Video32;
class GfxTransitions32;
+class GfxCursor32;
#endif
// our engine debug levels
@@ -113,7 +124,8 @@ enum kDebugLevels {
kDebugLevelOnStartup = 1 << 20,
kDebugLevelDebugMode = 1 << 21,
kDebugLevelScriptPatcher = 1 << 22,
- kDebugLevelWorkarounds = 1 << 23
+ kDebugLevelWorkarounds = 1 << 23,
+ kDebugLevelVideo = 1 << 24
};
enum SciGameId {
@@ -357,7 +369,7 @@ public:
GfxCompare *_gfxCompare;
GfxControls16 *_gfxControls16; // Controls for 16-bit gfx
GfxControls32 *_gfxControls32; // Controls for 32-bit gfx
- GfxCoordAdjuster *_gfxCoordAdjuster;
+ GfxCoordAdjuster16 *_gfxCoordAdjuster;
GfxCursor *_gfxCursor;
GfxMenu *_gfxMenu; // Menu for 16-bit gfx
GfxPalette *_gfxPalette16;
@@ -376,9 +388,9 @@ public:
#ifdef ENABLE_SCI32
Audio32 *_audio32;
Video32 *_video32;
- RobotDecoder *_robotDecoder;
GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx
GfxTransitions32 *_gfxTransitions32;
+ GfxCursor32 *_gfxCursor32;
#endif
AudioPlayer *_audio;
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 288b7c00f5..4af474b918 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -164,7 +164,7 @@ Audio32::~Audio32() {
#pragma mark -
#pragma mark AudioStream implementation
-int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop) {
+int Audio32::writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop) {
int samplesToRead = numSamples;
// The parent rate converter will request N * 2
@@ -182,7 +182,8 @@ int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream
do {
if (loop && sourceStream->endOfStream()) {
- sourceStream->rewind();
+ Audio::RewindableAudioStream *rewindableStream = dynamic_cast<Audio::RewindableAudioStream *>(sourceStream);
+ rewindableStream->rewind();
}
const int loopSamplesWritten = converter->flow(*sourceStream, targetBuffer, samplesToRead, leftVolume, rightVolume);
@@ -305,7 +306,14 @@ int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) {
}
if (channel.robot) {
- // TODO: Robot audio into output buffer
+ if (channel.stream->endOfStream()) {
+ stop(channelIndex--);
+ } else {
+ const int channelSamplesWritten = writeAudioInternal(channel.stream, channel.converter, buffer, numSamples, kMaxVolume, kMaxVolume, channel.loop);
+ if (channelSamplesWritten > maxSamplesWritten) {
+ maxSamplesWritten = channelSamplesWritten;
+ }
+ }
continue;
}
@@ -443,9 +451,9 @@ void Audio32::freeUnusedChannels() {
Common::StackLock lock(_mutex);
for (int channelIndex = 0; channelIndex < _numActiveChannels; ++channelIndex) {
const AudioChannel &channel = getChannel(channelIndex);
- if (channel.stream->endOfStream()) {
+ if (!channel.robot && channel.stream->endOfStream()) {
if (channel.loop) {
- channel.stream->rewind();
+ dynamic_cast<Audio::SeekableAudioStream *>(channel.stream)->rewind();
} else {
stop(channelIndex--);
}
@@ -466,21 +474,29 @@ void Audio32::freeChannel(const int16 channelIndex) {
Common::StackLock lock(_mutex);
AudioChannel &channel = getChannel(channelIndex);
- // We cannot unlock resources from the audio thread
- // because ResourceManager is not thread-safe; instead,
- // we just record that the resource needs unlocking and
- // unlock it whenever we are on the main thread again
- if (_inAudioThread) {
- _resourcesToUnlock.push_back(channel.resource);
+ // Robots have no corresponding resource to free
+ if (channel.robot) {
+ delete channel.stream;
+ channel.stream = nullptr;
+ channel.robot = false;
} else {
- _resMan->unlockResource(channel.resource);
+ // We cannot unlock resources from the audio thread
+ // because ResourceManager is not thread-safe; instead,
+ // we just record that the resource needs unlocking and
+ // unlock it whenever we are on the main thread again
+ if (_inAudioThread) {
+ _resourcesToUnlock.push_back(channel.resource);
+ } else {
+ _resMan->unlockResource(channel.resource);
+ }
+
+ channel.resource = nullptr;
+ delete channel.stream;
+ channel.stream = nullptr;
+ delete channel.resourceStream;
+ channel.resourceStream = nullptr;
}
- channel.resource = nullptr;
- delete channel.stream;
- channel.stream = nullptr;
- delete channel.resourceStream;
- channel.resourceStream = nullptr;
delete channel.converter;
channel.converter = nullptr;
@@ -527,6 +543,111 @@ void Audio32::setNumOutputChannels(int16 numChannels) {
}
#pragma mark -
+#pragma mark Robot
+
+int16 Audio32::findRobotChannel() const {
+ Common::StackLock lock(_mutex);
+ for (int16 i = 0; i < _numActiveChannels; ++i) {
+ if (_channels[i].robot) {
+ return i;
+ }
+ }
+
+ return kNoExistingChannel;
+}
+
+bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) {
+ // Stop immediately
+ if (packet.dataSize == 0) {
+ warning("Stopping robot stream by zero-length packet");
+ return stopRobotAudio();
+ }
+
+ // Flush and then stop
+ if (packet.dataSize == -1) {
+ warning("Stopping robot stream by negative-length packet");
+ return finishRobotAudio();
+ }
+
+ Common::StackLock lock(_mutex);
+ int16 channelIndex = findRobotChannel();
+
+ bool isNewChannel = false;
+ if (channelIndex == kNoExistingChannel) {
+ if (_numActiveChannels == _channels.size()) {
+ return false;
+ }
+
+ channelIndex = _numActiveChannels++;
+ isNewChannel = true;
+ }
+
+ AudioChannel &channel = getChannel(channelIndex);
+
+ if (isNewChannel) {
+ channel.id = ResourceId();
+ channel.resource = nullptr;
+ channel.loop = false;
+ channel.robot = true;
+ channel.fadeStartTick = 0;
+ channel.pausedAtTick = 0;
+ channel.soundNode = NULL_REG;
+ channel.volume = kMaxVolume;
+ // TODO: SCI3 introduces stereo audio
+ channel.pan = -1;
+ channel.converter = Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false);
+ // The RobotAudioStream buffer size is
+ // ((bytesPerSample * channels * sampleRate * 2000ms) / 1000ms) & ~3
+ // where bytesPerSample = 2, channels = 1, and sampleRate = 22050
+ channel.stream = new RobotAudioStream(88200);
+ _robotAudioPaused = false;
+
+ if (_numActiveChannels == 1) {
+ _startedAtTick = g_sci->getTickCount();
+ }
+ }
+
+ return static_cast<RobotAudioStream *>(channel.stream)->addPacket(packet);
+}
+
+bool Audio32::queryRobotAudio(RobotAudioStream::StreamState &status) const {
+ Common::StackLock lock(_mutex);
+
+ const int16 channelIndex = findRobotChannel();
+ if (channelIndex == kNoExistingChannel) {
+ status.bytesPlaying = 0;
+ return false;
+ }
+
+ status = static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->getStatus();
+ return true;
+}
+
+bool Audio32::finishRobotAudio() {
+ Common::StackLock lock(_mutex);
+
+ const int16 channelIndex = findRobotChannel();
+ if (channelIndex == kNoExistingChannel) {
+ return false;
+ }
+
+ static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->finish();
+ return true;
+}
+
+bool Audio32::stopRobotAudio() {
+ Common::StackLock lock(_mutex);
+
+ const int16 channelIndex = findRobotChannel();
+ if (channelIndex == kNoExistingChannel) {
+ return false;
+ }
+
+ stop(channelIndex);
+ return true;
+}
+
+#pragma mark -
#pragma mark Playback
uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool autoPlay, const bool loop, const int16 volume, const reg_t soundNode, const bool monitor) {
@@ -536,14 +657,15 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
if (channelIndex != kNoExistingChannel) {
AudioChannel &channel = getChannel(channelIndex);
+ Audio::SeekableAudioStream *stream = dynamic_cast<Audio::SeekableAudioStream *>(channel.stream);
if (channel.pausedAtTick) {
resume(channelIndex);
- return MIN(65534, 1 + channel.stream->getLength().msecs() * 60 / 1000);
+ return MIN(65534, 1 + stream->getLength().msecs() * 60 / 1000);
}
warning("Tried to resume channel %s that was not paused", channel.id.toString().c_str());
- return MIN(65534, 1 + channel.stream->getLength().msecs() * 60 / 1000);
+ return MIN(65534, 1 + stream->getLength().msecs() * 60 / 1000);
}
if (_numActiveChannels == _channels.size()) {
@@ -642,7 +764,7 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
// use audio streams, and allocate and fill the monitoring buffer
// when reading audio data from the stream.
- channel.duration = /* round up */ 1 + (channel.stream->getLength().msecs() * 60 / 1000);
+ channel.duration = /* round up */ 1 + (dynamic_cast<Audio::SeekableAudioStream *>(channel.stream)->getLength().msecs() * 60 / 1000);
const uint32 now = g_sci->getTickCount();
channel.pausedAtTick = autoPlay ? 0 : now;
@@ -687,8 +809,6 @@ bool Audio32::resume(const int16 channelIndex) {
if (channel.robot) {
channel.startedAtTick += now - channel.pausedAtTick;
channel.pausedAtTick = 0;
- // TODO: Robot
- // StartRobot();
return true;
}
}
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index ac3176cc5a..a9905ab6bf 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -30,8 +30,10 @@
#include "common/scummsys.h" // for int16, uint8, uint32, uint16
#include "engines/sci/resource.h" // for ResourceId
#include "sci/engine/vm_types.h" // for reg_t, NULL_REG
+#include "sci/video/robot_decoder.h" // for RobotAudioStream
namespace Sci {
+#pragma mark AudioChannel
/**
* An audio channel used by the software SCI mixer.
@@ -53,14 +55,11 @@ struct AudioChannel {
Common::SeekableReadStream *resourceStream;
/**
- * The audio stream loaded into this channel.
- * `SeekableAudioStream` is used here instead of
- * `RewindableAudioStream` because
- * `RewindableAudioStream` does not include the
- * `getLength` function, which is needed to tell the
- * game engine the duration of audio streams.
+ * The audio stream loaded into this channel. Can cast
+ * to `SeekableAudioStream` for normal channels and
+ * `RobotAudioStream` for robot channels.
*/
- Audio::SeekableAudioStream *stream;
+ Audio::AudioStream *stream;
/**
* The converter used to transform and merge the input
@@ -188,7 +187,7 @@ private:
* Mixes audio from the given source stream into the
* target buffer using the given rate converter.
*/
- int writeAudioInternal(Audio::RewindableAudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop);
+ int writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop);
#pragma mark -
#pragma mark Channel management
@@ -395,9 +394,18 @@ private:
#pragma mark -
#pragma mark Robot
public:
+ bool playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet);
+ bool queryRobotAudio(RobotAudioStream::StreamState &outStatus) const;
+ bool finishRobotAudio();
+ bool stopRobotAudio();
private:
/**
+ * Finds a channel that is configured for robot playback.
+ */
+ int16 findRobotChannel() const;
+
+ /**
* When true, channels marked as robot audio will not be
* played.
*/
diff --git a/engines/sci/sound/decoders/sol.cpp b/engines/sci/sound/decoders/sol.cpp
index e445403120..ee1ba35406 100644
--- a/engines/sci/sound/decoders/sol.cpp
+++ b/engines/sci/sound/decoders/sol.cpp
@@ -21,6 +21,7 @@
*/
#include "audio/audiostream.h"
+#include "audio/rate.h"
#include "audio/decoders/raw.h"
#include "common/substream.h"
#include "common/util.h"
@@ -52,7 +53,7 @@ static const byte tableDPCM8[8] = { 0, 1, 2, 3, 6, 10, 15, 21 };
* Decompresses 16-bit DPCM compressed audio. Each byte read
* outputs one sample into the decompression buffer.
*/
-static void deDPCM16(int16 *out, Common::ReadStream &audioStream, uint32 numBytes, int16 &sample) {
+static void deDPCM16(int16 *out, Common::ReadStream &audioStream, const uint32 numBytes, int16 &sample) {
for (uint32 i = 0; i < numBytes; ++i) {
const uint8 delta = audioStream.readByte();
if (delta & 0x80) {
@@ -65,6 +66,19 @@ static void deDPCM16(int16 *out, Common::ReadStream &audioStream, uint32 numByte
}
}
+void deDPCM16(int16 *out, const byte *in, const uint32 numBytes, int16 &sample) {
+ for (uint32 i = 0; i < numBytes; ++i) {
+ const uint8 delta = *in++;
+ if (delta & 0x80) {
+ sample -= tableDPCM16[delta & 0x7f];
+ } else {
+ sample += tableDPCM16[delta];
+ }
+ sample = CLIP<int16>(sample, -32768, 32767);
+ *out++ = TO_LE_16(sample);
+ }
+}
+
/**
* Decompresses one half of an 8-bit DPCM compressed audio
* byte.
@@ -178,7 +192,7 @@ int SOLStream<STEREO, S16BIT>::getRate() const {
template <bool STEREO, bool S16BIT>
bool SOLStream<STEREO, S16BIT>::endOfData() const {
- return _stream->eos() || _stream->pos() >= _dataOffset + _rawDataSize;
+ return _stream->eos() || _stream->pos() >= _rawDataSize;
}
template <bool STEREO, bool S16BIT>
@@ -269,5 +283,4 @@ Audio::SeekableAudioStream *makeSOLStream(Common::SeekableReadStream *headerStre
return Audio::makeRawStream(dataStream, sampleRate, rawFlags, disposeAfterUse);
}
-
}
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index a2795d21f9..f3354f9e44 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -20,391 +20,1598 @@
*
*/
-#include "common/archive.h"
-#include "common/stream.h"
-#include "common/substream.h"
-#include "common/system.h"
-#include "common/textconsole.h"
-#include "common/util.h"
-
-#include "graphics/surface.h"
-#include "audio/audiostream.h"
-#include "audio/decoders/raw.h"
-
-#include "sci/resource.h"
-#include "sci/util.h"
-#include "sci/sound/audio.h"
#include "sci/video/robot_decoder.h"
+#include "common/archive.h" // for SearchMan
+#include "common/debug.h" // for debugC
+#include "common/endian.h" // for MKTAG
+#include "common/memstream.h" // for MemoryReadStream
+#include "common/platform.h" // for Platform::kPlatformMacintosh
+#include "common/rational.h" // for operator*, Rational
+#include "common/str.h" // for String
+#include "common/stream.h" // for SeekableReadStream
+#include "common/substream.h" // for SeekableSubReadStreamEndian
+#include "common/textconsole.h" // for error, warning
+#include "common/types.h" // for Flag::NO, Flag::YES
+#include "sci/engine/seg_manager.h" // for SegManager
+#include "sci/graphics/celobj32.h" // for Ratio, ::kLowResX, ::kLowResY
+#include "sci/graphics/text32.h" // for BitmapResource
+#include "sci/sound/audio32.h" // for Audio32
+#include "sci/sci.h" // for kDebugLevels::kDebugLevelVideo
+#include "sci/util.h" // for READ_SCI11ENDIAN_UINT16, READ_SC...
namespace Sci {
-// TODO:
-// - Positioning
-// - Proper handling of frame scaling - scaled frames look squashed
-// (probably because both dimensions should be scaled)
-// - Transparency support
-// - Timing - the arbitrary 100ms delay between each frame is not quite right
-// - Proper handling of sound chunks in some cases, so that the frame size
-// table can be ignored (it's only used to determine the correct sound chunk
-// size at the moment, cause it can be wrong in some cases)
-// - Fix audio "hiccups" - probably data that shouldn't be in the audio frames
-
-
-// Some non technical information on robot files, from an interview with
-// Greg Tomko-Pavia of Sierra On-Line
-// Taken from http://anthonylarme.tripod.com/phantas/phintgtp.html
-//
-// (...) What we needed was a way of playing video, but have it blend into
-// normal room art instead of occupying its own rectangular area. Room art
-// consists of a background pic overlaid with various animating cels
-// (traditional lingo: sprites). The cels each have a priority that determines
-// who is on top and who is behind in the drawing order. Cels are read from
-// *.v56 files (another proprietary format). A Robot is video frames with
-// transparent background including priority and x,y information. Thus, it is
-// like a cel, except it comes from an RBT - not a v56. Because it blends into
-// our graphics engine, it looks just like a part of the room. A RBT can move
-// around the screen and go behind other objects. (...)
-
-enum RobotPalTypes {
- kRobotPalVariable = 0,
- kRobotPalConstant = 1
-};
-
-RobotDecoder::RobotDecoder(bool isBigEndian) {
- _fileStream = 0;
- _pos = Common::Point(0, 0);
- _isBigEndian = isBigEndian;
- _frameTotalSize = 0;
+#pragma mark RobotAudioStream
+
+extern void deDPCM16(int16 *out, const byte *in, const uint32 numBytes, int16 &sample);
+
+RobotAudioStream::RobotAudioStream(const int32 bufferSize) :
+ _loopBuffer((byte *)malloc(bufferSize)),
+ _loopBufferSize(bufferSize),
+ _decompressionBuffer(nullptr),
+ _decompressionBufferSize(0),
+ _decompressionBufferPosition(-1),
+ _waiting(true),
+ _finished(false),
+ _firstPacketPosition(-1) {}
+
+RobotAudioStream::~RobotAudioStream() {
+ free(_loopBuffer);
+ free(_decompressionBuffer);
}
-RobotDecoder::~RobotDecoder() {
- close();
+static void interpolateChannel(int16 *buffer, int32 numSamples, const int8 bufferIndex) {
+ if (numSamples <= 0) {
+ return;
+ }
+
+ if (bufferIndex) {
+ int16 lastSample = *buffer;
+ int sample = lastSample;
+ int16 *target = buffer + 1;
+ const int16 *source = buffer + 2;
+ --numSamples;
+
+ while (numSamples--) {
+ sample = *source + lastSample;
+ lastSample = *source;
+ sample /= 2;
+ *target = sample;
+ source += 2;
+ target += 2;
+ }
+
+ *target = sample;
+ } else {
+ int16 *target = buffer;
+ const int16 *source = buffer + 1;
+ int16 lastSample = *source;
+
+ while (numSamples--) {
+ int sample = *source + lastSample;
+ lastSample = *source;
+ sample /= 2;
+ *target = sample;
+ source += 2;
+ target += 2;
+ }
+ }
}
-bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) {
- close();
+static void copyEveryOtherSample(int16 *out, const int16 *in, int numSamples) {
+ while (numSamples--) {
+ *out = *in++;
+ out += 2;
+ }
+}
+
+bool RobotAudioStream::addPacket(const RobotAudioPacket &packet) {
+ Common::StackLock lock(_mutex);
+
+ if (_finished) {
+ warning("Packet %d sent to finished robot audio stream", packet.position);
+ return false;
+ }
+
+ // `packet.position` is the decompressed (doubled) position of the packet,
+ // so values of `position` will always be divisible either by 2 (even) or by
+ // 4 (odd).
+ const int8 bufferIndex = packet.position % 4 ? 1 : 0;
+
+ // Packet 0 is the first primer, packet 2 is the second primer,
+ // packet 4+ are regular audio data
+ if (packet.position <= 2 && _firstPacketPosition == -1) {
+ _readHead = 0;
+ _readHeadAbs = 0;
+ _maxWriteAbs = _loopBufferSize;
+ _writeHeadAbs = 2;
+ _jointMin[0] = 0;
+ _jointMin[1] = 2;
+ _waiting = true;
+ _finished = false;
+ _firstPacketPosition = packet.position;
+ fillRobotBuffer(packet, bufferIndex);
+ return true;
+ }
- _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES);
+ const int32 packetEndByte = packet.position + (packet.dataSize * sizeof(int16) * kEOSExpansion);
- readHeaderChunk();
+ // Already read all the way past this packet (or already wrote valid samples
+ // to this channel all the way past this packet), so discard it
+ if (packetEndByte <= MAX(_readHeadAbs, _jointMin[bufferIndex])) {
+ debugC(kDebugLevelVideo, "Rejecting packet %d, read past %d / %d", packet.position, _readHeadAbs, _jointMin[bufferIndex]);
+ return true;
+ }
- // There are several versions of robot files, ranging from 3 to 6.
- // v3: no known examples
- // v4: PQ:SWAT demo
- // v5: SCI2.1 and SCI3 games
- // v6: SCI3 games
- if (_header.version < 4 || _header.version > 6)
- error("Unknown robot version: %d", _header.version);
+ // The loop buffer is full, so tell the caller to send the packet again
+ // later
+ if (_maxWriteAbs <= _jointMin[bufferIndex]) {
+ debugC(kDebugLevelVideo, "Rejecting packet %d, full buffer", packet.position);
+ return false;
+ }
- RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount);
- addTrack(videoTrack);
+ fillRobotBuffer(packet, bufferIndex);
- if (_header.hasSound)
- addTrack(new RobotAudioTrack());
+ // This packet is the second primer, so allow playback to begin
+ if (_firstPacketPosition != -1 && _firstPacketPosition != packet.position) {
+ debugC(kDebugLevelVideo, "Done waiting. Robot audio begins");
+ _waiting = false;
+ _firstPacketPosition = -1;
+ }
- videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize);
- readFrameSizesChunk();
- videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize);
+ // Only part of the packet could be read into the loop buffer before it was
+ // full, so tell the caller to send the packet again later
+ if (packetEndByte > _maxWriteAbs) {
+ debugC(kDebugLevelVideo, "Partial read of packet %d (%d / %d)", packet.position, packetEndByte - _maxWriteAbs, packetEndByte - packet.position);
+ return false;
+ }
+
+ // The entire packet was successfully read into the loop buffer
return true;
}
-bool RobotDecoder::load(GuiResourceId id) {
- // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) -
- // its drawn at odd coordinates. SV can't play it either (along with some
- // others), so it must be some new functionality added in RAMA's robot
- // videos. Skip it for now.
- if (g_sci->getGameId() == GID_RAMA && id == 1003)
- return false;
+void RobotAudioStream::fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex) {
+ int32 sourceByte = 0;
- // Robots for the options in the RAMA menu
- if (g_sci->getGameId() == GID_RAMA && (id >= 1004 && id <= 1009))
- return false;
+ const int32 decompressedSize = packet.dataSize * sizeof(int16);
+ if (_decompressionBufferPosition != packet.position) {
+ if (decompressedSize != _decompressionBufferSize) {
+ _decompressionBuffer = (byte *)realloc(_decompressionBuffer, decompressedSize);
+ _decompressionBufferSize = decompressedSize;
+ }
- // TODO: The robot video in the Lighthouse demo gets stuck
- if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16)
- return false;
+ int16 carry = 0;
+ deDPCM16((int16 *)_decompressionBuffer, packet.data, packet.dataSize, carry);
+ _decompressionBufferPosition = packet.position;
+ }
+
+ int32 numBytes = decompressedSize;
+ int32 packetPosition = packet.position;
+ int32 endByte = packet.position + decompressedSize * kEOSExpansion;
+ int32 startByte = MAX(_readHeadAbs + bufferIndex * 2, _jointMin[bufferIndex]);
+ int32 maxWriteByte = _maxWriteAbs + bufferIndex * 2;
+ if (packetPosition < startByte) {
+ sourceByte = (startByte - packetPosition) / kEOSExpansion;
+ numBytes -= sourceByte;
+ packetPosition = startByte;
+ }
+ if (packetPosition > maxWriteByte) {
+ numBytes += (packetPosition - maxWriteByte) / kEOSExpansion;
+ packetPosition = maxWriteByte;
+ }
+ if (endByte > maxWriteByte) {
+ numBytes -= (endByte - maxWriteByte) / kEOSExpansion;
+ endByte = maxWriteByte;
+ }
- Common::String fileName = Common::String::format("%d.rbt", id);
+ const int32 maxJointMin = MAX(_jointMin[0], _jointMin[1]);
+ if (endByte > maxJointMin) {
+ _writeHeadAbs += endByte - maxJointMin;
+ }
+
+ if (packetPosition > _jointMin[bufferIndex]) {
+ int32 packetEndByte = packetPosition % _loopBufferSize;
+ int32 targetBytePosition;
+ int32 numBytesToEnd;
+ if ((packetPosition & ~3) > (_jointMin[1 - bufferIndex] & ~3)) {
+ targetBytePosition = _jointMin[1 - bufferIndex] % _loopBufferSize;
+ if (targetBytePosition >= packetEndByte) {
+ numBytesToEnd = _loopBufferSize - targetBytePosition;
+ memset(_loopBuffer + targetBytePosition, 0, numBytesToEnd);
+ targetBytePosition = (1 - bufferIndex) ? 2 : 0;
+ }
+ numBytesToEnd = packetEndByte - targetBytePosition;
+ if (numBytesToEnd > 0) {
+ memset(_loopBuffer + targetBytePosition, 0, numBytesToEnd);
+ }
+ }
+ targetBytePosition = _jointMin[bufferIndex] % _loopBufferSize;
+ if (targetBytePosition >= packetEndByte) {
+ numBytesToEnd = _loopBufferSize - targetBytePosition;
+ interpolateChannel((int16 *)(_loopBuffer + targetBytePosition), numBytesToEnd / sizeof(int16) / kEOSExpansion, 0);
+ targetBytePosition = bufferIndex ? 2 : 0;
+ }
+ numBytesToEnd = packetEndByte - targetBytePosition;
+ if (numBytesToEnd > 0) {
+ interpolateChannel((int16 *)(_loopBuffer + targetBytePosition), numBytesToEnd / sizeof(int16) / kEOSExpansion, 0);
+ }
+ }
+
+ if (numBytes > 0) {
+ int32 targetBytePosition = packetPosition % _loopBufferSize;
+ int32 packetEndByte = endByte % _loopBufferSize;
+ int32 numBytesToEnd = 0;
+ if (targetBytePosition >= packetEndByte) {
+ numBytesToEnd = (_loopBufferSize - (targetBytePosition & ~3)) / kEOSExpansion;
+ copyEveryOtherSample((int16 *)(_loopBuffer + targetBytePosition), (int16 *)(_decompressionBuffer + sourceByte), numBytesToEnd / kEOSExpansion);
+ targetBytePosition = bufferIndex ? 2 : 0;
+ }
+ copyEveryOtherSample((int16 *)(_loopBuffer + targetBytePosition), (int16 *)(_decompressionBuffer + sourceByte + numBytesToEnd), (packetEndByte - targetBytePosition) / sizeof(int16) / kEOSExpansion);
+ }
+ _jointMin[bufferIndex] = endByte;
+}
+
+void RobotAudioStream::interpolateMissingSamples(int32 numSamples) {
+ int32 numBytes = numSamples * sizeof(int16) * kEOSExpansion;
+ int32 targetPosition = _readHead;
+
+ if (_readHeadAbs > _jointMin[1]) {
+ if (_readHeadAbs > _jointMin[0]) {
+ if (targetPosition + numBytes >= _loopBufferSize) {
+ const int32 numBytesToEdge = (_loopBufferSize - targetPosition);
+ memset(_loopBuffer + targetPosition, 0, numBytesToEdge);
+ numBytes -= numBytesToEdge;
+ targetPosition = 0;
+ }
+ memset(_loopBuffer + targetPosition, 0, numBytes);
+ _jointMin[0] += numBytes;
+ _jointMin[1] += numBytes;
+ } else {
+ if (targetPosition + numBytes >= _loopBufferSize) {
+ const int32 numSamplesToEdge = (_loopBufferSize - targetPosition) / sizeof(int16) / kEOSExpansion;
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamplesToEdge, 1);
+ numSamples -= numSamplesToEdge;
+ targetPosition = 0;
+ }
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamples, 1);
+ _jointMin[1] += numBytes;
+ }
+ } else if (_readHeadAbs > _jointMin[0]) {
+ if (targetPosition + numBytes >= _loopBufferSize) {
+ const int32 numSamplesToEdge = (_loopBufferSize - targetPosition) / sizeof(int16) / kEOSExpansion;
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamplesToEdge, 0);
+ numSamples -= numSamplesToEdge;
+ targetPosition = 2;
+ }
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamples, 0);
+ _jointMin[0] += numBytes;
+ }
+}
+
+void RobotAudioStream::finish() {
+ Common::StackLock lock(_mutex);
+ _finished = true;
+}
+
+RobotAudioStream::StreamState RobotAudioStream::getStatus() const {
+ Common::StackLock lock(_mutex);
+ StreamState status;
+ status.bytesPlaying = _readHeadAbs;
+ status.rate = getRate();
+ status.bits = 8 * sizeof(int16);
+ return status;
+}
+
+int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ if (_waiting) {
+ return 0;
+ }
+
+ assert(!((_writeHeadAbs - _readHeadAbs) & 1));
+ const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(Audio::st_sample_t);
+ numSamples = MIN(numSamples, maxNumSamples);
+
+ if (!numSamples) {
+ return 0;
+ }
+
+ interpolateMissingSamples(numSamples);
+
+ Audio::st_sample_t *inBuffer = (Audio::st_sample_t *)(_loopBuffer + _readHead);
+
+ assert(!((_loopBufferSize - _readHead) & 1));
+ const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(Audio::st_sample_t);
+
+ int numSamplesToRead = MIN(numSamples, numSamplesToEnd);
+ Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
+
+ if (numSamplesToRead < numSamples) {
+ inBuffer = (Audio::st_sample_t *)_loopBuffer;
+ outBuffer += numSamplesToRead;
+ numSamplesToRead = numSamples - numSamplesToRead;
+ Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
+ }
+
+ const int32 numBytes = numSamples * sizeof(Audio::st_sample_t);
+
+ _readHead += numBytes;
+ if (_readHead > _loopBufferSize) {
+ _readHead -= _loopBufferSize;
+ }
+ _readHeadAbs += numBytes;
+ _maxWriteAbs += numBytes;
+ assert(!(_readHead & 1));
+ assert(!(_readHeadAbs & 1));
+
+ return numSamples;
+}
+
+#pragma mark -
+#pragma mark RobotDecoder
+
+RobotDecoder::RobotDecoder(SegManager *segMan) :
+ _delayTime(this),
+ _segMan(segMan),
+ _status(kRobotStatusUninitialized),
+ _audioBuffer(nullptr),
+ _rawPalette((uint8 *)malloc(kRawPaletteSize)) {}
+
+RobotDecoder::~RobotDecoder() {
+ close();
+ free(_rawPalette);
+ free(_audioBuffer);
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Initialization
+
+void RobotDecoder::initStream(const GuiResourceId robotId) {
+ const Common::String fileName = Common::String::format("%d.rbt", robotId);
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName);
+ _fileOffset = 0;
- if (!stream) {
- warning("Unable to open robot file %s", fileName.c_str());
- return false;
+ if (stream == nullptr) {
+ error("Unable to open robot file %s", fileName.c_str());
+ }
+
+ const uint16 id = stream->readUint16LE();
+ if (id != 0x16) {
+ error("Invalid robot file %s", fileName.c_str());
+ }
+
+ // TODO: Mac version not tested, so this could be totally wrong
+ _stream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), g_sci->getPlatform() == Common::kPlatformMacintosh, DisposeAfterUse::YES);
+ _stream->seek(2, SEEK_SET);
+ if (_stream->readUint32BE() != MKTAG('S', 'O', 'L', 0)) {
+ error("Resource %s is not Robot type!", fileName.c_str());
+ }
+}
+
+void RobotDecoder::initPlayback() {
+ _startFrameNo = 0;
+ _startTime = -1;
+ _startingFrameNo = -1;
+ _cueForceShowFrame = -1;
+ _previousFrameNo = -1;
+ _currentFrameNo = 0;
+ _status = kRobotStatusPaused;
+}
+
+void RobotDecoder::initAudio() {
+ _syncFrame = true;
+
+ _audioRecordInterval = RobotAudioStream::kRobotSampleRate / _frameRate;
+
+ // TODO: Might actually be for all games newer than Lighthouse; check to
+ // see which games have this condition.
+ if (g_sci->getGameId() != GID_LIGHTHOUSE && !(_audioRecordInterval & 1)) {
+ ++_audioRecordInterval;
+ }
+
+ _expectedAudioBlockSize = _audioBlockSize - kAudioBlockHeaderSize;
+ _audioBuffer = (byte *)realloc(_audioBuffer, kRobotZeroCompressSize + _expectedAudioBlockSize);
+
+ if (_primerReservedSize != 0) {
+ const int32 primerHeaderPosition = _stream->pos();
+ _totalPrimerSize = _stream->readSint32();
+ const int16 compressionType = _stream->readSint16();
+ _evenPrimerSize = _stream->readSint32();
+ _oddPrimerSize = _stream->readSint32();
+ _primerPosition = _stream->pos();
+
+ if (compressionType) {
+ error("Unknown audio header compression type %d", compressionType);
+ }
+
+ if (_evenPrimerSize + _oddPrimerSize != _primerReservedSize) {
+ _stream->seek(primerHeaderPosition + _primerReservedSize, SEEK_SET);
+ }
+ } else if (_primerZeroCompressFlag) {
+ _evenPrimerSize = 19922;
+ _oddPrimerSize = 21024;
+ }
+
+ _firstAudioRecordPosition = _evenPrimerSize * 2;
+
+ const int usedEachFrame = (RobotAudioStream::kRobotSampleRate / 2) / _frameRate;
+ _maxSkippablePackets = MAX(0, _audioBlockSize / usedEachFrame - 1);
+}
+
+void RobotDecoder::initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize) {
+ _position = Common::Point(x, y);
+
+ if (scale != 128) {
+ _scaleInfo.x = scale;
+ _scaleInfo.y = scale;
+ _scaleInfo.signal = kScaleSignalDoScaling32;
+ }
+
+ _plane = g_sci->_gfxFrameout->getPlanes().findByObject(plane);
+ if (_plane == nullptr) {
+ error("Invalid plane %04x:%04x passed to RobotDecoder::open", PRINT_REG(plane));
+ }
+
+ _minFrameRate = _frameRate - kMaxFrameRateDrift;
+ _maxFrameRate = _frameRate + kMaxFrameRateDrift;
+
+ if (_xResolution == 0 || _yResolution == 0) {
+ // TODO: Default values were taken from RESOURCE.CFG hires property
+ // if it exists, so need to check games' configuration files for those
+ _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
}
- return loadStream(stream);
+ if (hasPalette) {
+ _stream->read(_rawPalette, paletteSize);
+ } else {
+ _stream->seek(paletteSize, SEEK_CUR);
+ }
+
+ _screenItemList.reserve(kScreenItemListSize);
+ _maxCelArea.reserve(kFixedCelListSize);
+
+ // Fixed cel buffers are for version 5 and newer
+ _fixedCels.reserve(MIN(_maxCelsPerFrame, (int16)kFixedCelListSize));
+ _celDecompressionBuffer.reserve(_maxCelArea[0] + SciBitmap::getBitmapHeaderSize() + kRawPaletteSize);
+ _celDecompressionArea = _maxCelArea[0];
+}
+
+void RobotDecoder::initRecordAndCuePositions() {
+ PositionList recordSizes;
+ _videoSizes.reserve(_numFramesTotal);
+ _recordPositions.reserve(_numFramesTotal);
+ recordSizes.reserve(_numFramesTotal);
+
+ switch(_version) {
+ case 5: // 16-bit sizes and positions
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ _videoSizes.push_back(_stream->readUint16());
+ }
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ recordSizes.push_back(_stream->readUint16());
+ }
+ break;
+ case 6: // 32-bit sizes and positions
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ _videoSizes.push_back(_stream->readSint32());
+ }
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ recordSizes.push_back(_stream->readSint32());
+ }
+ break;
+ default:
+ error("Unknown Robot version %d", _version);
+ }
+
+ for (int i = 0; i < kCueListSize; ++i) {
+ _cueTimes[i] = _stream->readSint32();
+ }
+
+ for (int i = 0; i < kCueListSize; ++i) {
+ _cueValues[i] = _stream->readUint16();
+ }
+
+ Common::copy(_cueTimes, _cueTimes + kCueListSize, _masterCueTimes);
+
+ int bytesRemaining = (_stream->pos() - _fileOffset) % kRobotFrameSize;
+ if (bytesRemaining != 0) {
+ _stream->seek(kRobotFrameSize - bytesRemaining, SEEK_CUR);
+ }
+
+ int position = _stream->pos();
+ _recordPositions.push_back(position);
+ for (int i = 0; i < _numFramesTotal - 1; ++i) {
+ position += recordSizes[i];
+ _recordPositions.push_back(position);
+ }
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Playback
+
+void RobotDecoder::open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale) {
+ if (_status != kRobotStatusUninitialized) {
+ warning("Last robot was not closed");
+ close();
+ }
+
+ initStream(robotId);
+
+ _version = _stream->readUint16();
+
+ // TODO: Version 4 for PQ:SWAT demo?
+ if (_version < 5 || _version > 6) {
+ error("Unsupported version %d of Robot resource", _version);
+ }
+
+ debugC(kDebugLevelVideo, "Opening version %d robot %d", _version, robotId);
+
+ initPlayback();
+
+ _audioBlockSize = _stream->readUint16();
+ _primerZeroCompressFlag = _stream->readSint16();
+ _stream->seek(2, SEEK_CUR); // unused
+ _numFramesTotal = _stream->readUint16();
+ const uint16 paletteSize = _stream->readUint16();
+ _primerReservedSize = _stream->readUint16();
+ _xResolution = _stream->readSint16();
+ _yResolution = _stream->readSint16();
+ const bool hasPalette = (bool)_stream->readByte();
+ _hasAudio = (bool)_stream->readByte();
+ _stream->seek(2, SEEK_CUR); // unused
+ _frameRate = _normalFrameRate = _stream->readSint16();
+ _isHiRes = (bool)_stream->readSint16();
+ _maxSkippablePackets = _stream->readSint16();
+ _maxCelsPerFrame = _stream->readSint16();
+
+ // used for memory preallocation of fixed cels
+ _maxCelArea.push_back(_stream->readSint32());
+ _maxCelArea.push_back(_stream->readSint32());
+ _maxCelArea.push_back(_stream->readSint32());
+ _maxCelArea.push_back(_stream->readSint32());
+ _stream->seek(8, SEEK_CUR); // reserved
+
+ if (_hasAudio) {
+ initAudio();
+ } else {
+ _stream->seek(_primerReservedSize, SEEK_CUR);
+ }
+
+ _priority = priority;
+ initVideo(x, y, scale, plane, hasPalette, paletteSize);
+ initRecordAndCuePositions();
}
void RobotDecoder::close() {
- VideoDecoder::close();
+ if (_status == kRobotStatusUninitialized) {
+ return;
+ }
+
+ debugC(kDebugLevelVideo, "Closing robot");
- delete _fileStream;
- _fileStream = 0;
+ _status = kRobotStatusUninitialized;
+ _videoSizes.clear();
+ _recordPositions.clear();
+ _celDecompressionBuffer.clear();
+ _doVersion5Scratch.clear();
+ delete _stream;
+ _stream = nullptr;
+
+ for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) {
+ if (_celHandles[i].status == CelHandleInfo::kFrameLifetime) {
+ _segMan->freeBitmap(_celHandles[i].bitmapId);
+ }
+ }
+ _celHandles.clear();
+
+ for (FixedCelsList::size_type i = 0; i < _fixedCels.size(); ++i) {
+ _segMan->freeBitmap(_fixedCels[i]);
+ }
+ _fixedCels.clear();
+
+ if (g_sci->_gfxFrameout->getPlanes().findByObject(_plane->_object) != nullptr) {
+ for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ if (_screenItemList[i] != nullptr) {
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]);
+ }
+ }
+ }
+ _screenItemList.clear();
- delete[] _frameTotalSize;
- _frameTotalSize = 0;
+ if (_hasAudio) {
+ _audioList.reset();
+ }
}
-void RobotDecoder::readNextPacket() {
- // Get our track
- RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0);
- videoTrack->increaseCurFrame();
- Graphics::Surface *surface = videoTrack->getSurface();
+void RobotDecoder::pause() {
+ if (_status != kRobotStatusPlaying) {
+ return;
+ }
+
+ if (_hasAudio) {
+ _audioList.stopAudioNow();
+ }
+
+ _status = kRobotStatusPaused;
+ _frameRate = _normalFrameRate;
+}
- if (videoTrack->endOfTrack())
+void RobotDecoder::resume() {
+ if (_status != kRobotStatusPaused) {
return;
+ }
+
+ _startingFrameNo = _currentFrameNo;
+ _status = kRobotStatusPlaying;
+ if (_hasAudio) {
+ primeAudio(_currentFrameNo * 60 / _frameRate);
+ _syncFrame = true;
+ }
+
+ setRobotTime(_currentFrameNo);
+ for (int i = 0; i < kCueListSize; ++i) {
+ if (_masterCueTimes[i] != -1 && _masterCueTimes[i] < _currentFrameNo) {
+ _cueTimes[i] = -1;
+ } else {
+ _cueTimes[i] = _masterCueTimes[i];
+ }
+ }
+}
- // Read frame image header (24 bytes)
- _fileStream->skip(3);
- byte frameScale = _fileStream->readByte();
- uint16 frameWidth = _fileStream->readUint16();
- uint16 frameHeight = _fileStream->readUint16();
- _fileStream->skip(4); // unknown, almost always 0
- uint16 frameX = _fileStream->readUint16();
- uint16 frameY = _fileStream->readUint16();
-
- // TODO: In v4 robot files, frameX and frameY have a different meaning.
- // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up
- // correctly.
- if (_header.version == 4)
- frameX = frameY = 0;
-
- uint16 compressedSize = _fileStream->readUint16();
- uint16 frameFragments = _fileStream->readUint16();
- _fileStream->skip(4); // unknown
- uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100;
-
- // FIXME: A frame's height + position can go off limits... why? With the
- // following, we cut the contents to fit the frame
- uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY);
-
- // FIXME: Same goes for the frame's width + position. In this case, we
- // modify the position to fit the contents on screen.
- if (frameWidth + frameX > surface->w)
- frameX = surface->w - frameWidth;
-
- assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h);
-
- DecompressorLZS lzs;
- byte *decompressedFrame = new byte[decompressedSize];
- byte *outPtr = decompressedFrame;
-
- if (_header.version == 4) {
- // v4 has just the one fragment, it seems, and ignores the fragment count
- Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedSize);
- lzs.unpack(&fragmentStream, outPtr, compressedSize, decompressedSize);
+void RobotDecoder::showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority) {
+ debugC(kDebugLevelVideo, "Show frame %d (%d %d %d)", frameNo, newX, newY, newPriority);
+
+ if (newX != kUnspecified) {
+ _position.x = newX;
+ }
+
+ if (newY != kUnspecified) {
+ _position.y = newY;
+ }
+
+ if (newPriority != kUnspecified) {
+ _priority = newPriority;
+ }
+
+ _currentFrameNo = frameNo;
+ pause();
+
+ if (frameNo != _previousFrameNo) {
+ seekToFrame(frameNo);
+ doVersion5(false);
} else {
- for (uint16 i = 0; i < frameFragments; ++i) {
- uint32 compressedFragmentSize = _fileStream->readUint32();
- uint32 decompressedFragmentSize = _fileStream->readUint32();
- uint16 compressionType = _fileStream->readUint16();
-
- if (compressionType == 0) {
- Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedFragmentSize);
- lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize);
- } else if (compressionType == 2) { // untested
- _fileStream->read(outPtr, compressedFragmentSize);
+ for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ if (_isHiRes) {
+ SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[i].bitmapId);
+
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ if (scriptWidth == kLowResX && scriptHeight == kLowResY) {
+ const Ratio lowResToScreenX(screenWidth, kLowResX);
+ const Ratio lowResToScreenY(screenHeight, kLowResY);
+ const Ratio screenToLowResX(kLowResX, screenWidth);
+ const Ratio screenToLowResY(kLowResY, screenHeight);
+
+ const int16 scaledX = _originalScreenItemX[i] + (_position.x * lowResToScreenX).toInt();
+ const int16 scaledY1 = _originalScreenItemY[i] + (_position.y * lowResToScreenY).toInt();
+ const int16 scaledY2 = scaledY1 + bitmap.getHeight() - 1;
+
+ const int16 lowResX = (scaledX * screenToLowResX).toInt();
+ const int16 lowResY = (scaledY2 * screenToLowResY).toInt();
+
+ bitmap.setDisplace(Common::Point(
+ (scaledX - (lowResX * lowResToScreenX).toInt()) * -1,
+ (lowResY * lowResToScreenY).toInt() - scaledY1
+ ));
+
+ _screenItemX[i] = lowResX;
+ _screenItemY[i] = lowResY;
+ } else {
+ const int16 scaledX = _originalScreenItemX[i] + _position.x;
+ const int16 scaledY = _originalScreenItemY[i] + _position.y + bitmap.getHeight() - 1;
+ bitmap.setDisplace(Common::Point(0, bitmap.getHeight() - 1));
+ _screenItemX[i] = scaledX;
+ _screenItemY[i] = scaledY;
+ }
+ } else {
+ _screenItemX[i] = _originalScreenItemX[i] + _position.x;
+ _screenItemY[i] = _originalScreenItemY[i] + _position.y;
+ }
+
+ if (_screenItemList[i] == nullptr) {
+ CelInfo32 celInfo;
+ celInfo.type = kCelTypeMem;
+ celInfo.bitmap = _celHandles[i].bitmapId;
+ ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo);
+ _screenItemList[i] = screenItem;
+ screenItem->_position = Common::Point(_screenItemX[i], _screenItemY[i]);
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_priority = _priority;
+ screenItem->_fixedPriority = true;
+ }
+ g_sci->_gfxFrameout->addScreenItem(*screenItem);
} else {
- error("Unknown frame compression found: %d", compressionType);
+ ScreenItem *screenItem = _screenItemList[i];
+ screenItem->_celInfo.bitmap = _celHandles[i].bitmapId;
+ screenItem->_position = Common::Point(_screenItemX[i], _screenItemY[i]);
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_priority = _priority;
+ screenItem->_fixedPriority = true;
+ }
+ g_sci->_gfxFrameout->updateScreenItem(*screenItem);
+ }
+ }
+ }
+
+ _previousFrameNo = frameNo;
+}
+
+int16 RobotDecoder::getCue() const {
+ if (_status == kRobotStatusUninitialized ||
+ _status == kRobotStatusPaused ||
+ _syncFrame) {
+ return 0;
+ }
+
+ if (_status == kRobotStatusEnd) {
+ return -1;
+ }
+
+ const uint16 estimatedNextFrameNo = MIN(calculateNextFrameNo(_delayTime.predictedTicks()), _numFramesTotal);
+
+ for (int i = 0; i < kCueListSize; ++i) {
+ if (_cueTimes[i] != -1 && _cueTimes[i] <= estimatedNextFrameNo) {
+ if (_cueTimes[i] >= _previousFrameNo) {
+ _cueForceShowFrame = _cueTimes[i] + 1;
}
- outPtr += decompressedFragmentSize;
+ _cueTimes[i] = -1;
+ return _cueValues[i];
}
}
- // Copy over the decompressed frame
- byte *inFrame = decompressedFrame;
- byte *outFrame = (byte *)surface->getPixels();
+ return 0;
+}
- // Black out the surface
- memset(outFrame, 0, surface->w * surface->h);
+int16 RobotDecoder::getFrameNo() const {
+ if (_status == kRobotStatusUninitialized) {
+ return 0;
+ }
- // Move to the correct y coordinate
- outFrame += surface->w * frameY;
+ return _currentFrameNo;
+}
+
+RobotDecoder::RobotStatus RobotDecoder::getStatus() const {
+ return _status;
+}
- for (uint16 y = 0; y < scaledHeight; y++) {
- memcpy(outFrame + frameX, inFrame, frameWidth);
- inFrame += frameWidth;
- outFrame += surface->w;
+bool RobotDecoder::seekToFrame(const int frameNo) {
+ return _stream->seek(_recordPositions[frameNo], SEEK_SET);
+}
+
+void RobotDecoder::setRobotTime(const int frameNo) {
+ _startTime = getTickCount();
+ _startFrameNo = frameNo;
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Timing
+
+RobotDecoder::DelayTime::DelayTime(RobotDecoder *decoder) :
+ _decoder(decoder) {
+ for (int i = 0; i < kDelayListSize; ++i) {
+ _timestamps[i] = i;
+ _delays[i] = 0;
}
- delete[] decompressedFrame;
+ _oldestTimestamp = 0;
+ _newestTimestamp = kDelayListSize - 1;
+ _startTime = 0;
+}
- uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize);
+void RobotDecoder::DelayTime::startTiming() {
+ _startTime = _decoder->getTickCount();
+}
-// TODO: The audio chunk size below is usually correct, but there are some
-// exceptions (e.g. robot 4902 in Phantasmagoria, towards its end)
-#if 0
- // Read frame audio header (14 bytes)
- _fileStream->skip(2); // buffer position
- _fileStream->skip(2); // unknown (usually 1)
- _fileStream->skip(2); /*uint16 audioChunkSize = _fileStream->readUint16() + 8;*/
- _fileStream->skip(2);
-#endif
+void RobotDecoder::DelayTime::endTiming() {
+ const int timeDelta = _decoder->getTickCount() - _startTime;
+ for (uint i = 0; i < kDelayListSize; ++i) {
+ if (_timestamps[i] == _oldestTimestamp) {
+ _timestamps[i] = ++_newestTimestamp;
+ _delays[i] = timeDelta;
+ break;
+ }
+ }
+ ++_newestTimestamp;
+ _startTime = 0;
+ sortList();
+}
- // Queue the next audio frame
- // FIXME: For some reason, there are audio hiccups/gaps
- if (_header.hasSound) {
- RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1);
- _fileStream->skip(8); // header
- audioChunkSize -= 8;
- audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2);
+bool RobotDecoder::DelayTime::timingInProgress() const {
+ return _startTime != 0;
+}
+
+int RobotDecoder::DelayTime::predictedTicks() const {
+ return _delays[kDelayListSize / 2];
+}
+
+void RobotDecoder::DelayTime::sortList() {
+ for (uint i = 0; i < kDelayListSize - 1; ++i) {
+ int smallestDelay = _delays[i];
+ uint smallestIndex = i;
+
+ for (uint j = i + 1; j < kDelayListSize - 1; ++j) {
+ if (_delays[j] < smallestDelay) {
+ smallestDelay = _delays[j];
+ smallestIndex = j;
+ }
+ }
+
+ if (smallestIndex != i) {
+ SWAP(_delays[i], _delays[smallestIndex]);
+ SWAP(_timestamps[i], _timestamps[smallestIndex]);
+ }
+ }
+}
+
+uint16 RobotDecoder::calculateNextFrameNo(const uint32 extraTicks) const {
+ return ticksToFrames(getTickCount() + extraTicks - _startTime) + _startFrameNo;
+}
+
+uint32 RobotDecoder::ticksToFrames(const uint32 ticks) const {
+ return (ticks * _frameRate) / 60;
+}
+
+uint32 RobotDecoder::getTickCount() const {
+ return g_sci->getTickCount();
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Audio
+
+RobotDecoder::AudioList::AudioList() :
+ _blocks(),
+ _blocksSize(0),
+ _oldestBlockIndex(0),
+ _newestBlockIndex(0),
+ _startOffset(0),
+ _status(kRobotAudioReady) {}
+
+void RobotDecoder::AudioList::startAudioNow() {
+ submitDriverMax();
+ g_sci->_audio32->resume(kRobotChannel);
+ _status = kRobotAudioPlaying;
+}
+
+void RobotDecoder::AudioList::stopAudio() {
+ g_sci->_audio32->finishRobotAudio();
+ freeAudioBlocks();
+ _status = kRobotAudioStopping;
+}
+
+void RobotDecoder::AudioList::stopAudioNow() {
+ if (_status == kRobotAudioPlaying || _status == kRobotAudioStopping || _status == kRobotAudioPaused) {
+ g_sci->_audio32->stopRobotAudio();
+ _status = kRobotAudioStopped;
+ }
+
+ freeAudioBlocks();
+}
+
+void RobotDecoder::AudioList::submitDriverMax() {
+ while (_blocksSize != 0) {
+ if (!_blocks[_oldestBlockIndex]->submit(_startOffset)) {
+ return;
+ }
+
+ delete _blocks[_oldestBlockIndex];
+ _blocks[_oldestBlockIndex] = nullptr;
+ ++_oldestBlockIndex;
+ if (_oldestBlockIndex == kAudioListSize) {
+ _oldestBlockIndex = 0;
+ }
+
+ --_blocksSize;
+ }
+}
+
+void RobotDecoder::AudioList::addBlock(const int position, const int size, const byte *data) {
+ assert(data != nullptr);
+ assert(size >= 0);
+ assert(position >= -1);
+
+ if (_blocksSize == kAudioListSize) {
+ delete _blocks[_oldestBlockIndex];
+ _blocks[_oldestBlockIndex] = nullptr;
+ ++_oldestBlockIndex;
+ if (_oldestBlockIndex == kAudioListSize) {
+ _oldestBlockIndex = 0;
+ }
+ --_blocksSize;
+ }
+
+ if (_blocksSize == 0) {
+ _oldestBlockIndex = _newestBlockIndex = 0;
} else {
- _fileStream->skip(audioChunkSize);
- }
-}
-
-void RobotDecoder::readHeaderChunk() {
- // Header (60 bytes)
- _fileStream->skip(6);
- _header.version = _fileStream->readUint16();
- _header.audioChunkSize = _fileStream->readUint16();
- _header.audioSilenceSize = _fileStream->readUint16();
- _fileStream->skip(2);
- _header.frameCount = _fileStream->readUint16();
- _header.paletteDataSize = _fileStream->readUint16();
- _header.unkChunkDataSize = _fileStream->readUint16();
- _fileStream->skip(5);
- _header.hasSound = _fileStream->readByte();
- _fileStream->skip(34);
-
- // Some videos (e.g. robot 1305 in Phantasmagoria and
- // robot 184 in Lighthouse) have an unknown chunk before
- // the palette chunk (probably used for sound preloading).
- // Skip it here.
- if (_header.unkChunkDataSize)
- _fileStream->skip(_header.unkChunkDataSize);
-}
-
-void RobotDecoder::readFrameSizesChunk() {
- // The robot video file contains 2 tables, with one entry for each frame:
- // - A table containing the size of the image in each video frame
- // - A table containing the total size of each video frame.
- // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots,
- // they contain 32-bit integers.
-
- _frameTotalSize = new uint32[_header.frameCount];
-
- // TODO: The table reading code can probably be removed once the
- // audio chunk size is figured out (check the TODO inside processNextFrame())
-#if 0
- // We don't need any of the two tables to play the video, so we ignore
- // both of them.
- uint16 wordSize = _header.version == 6 ? 4 : 2;
- _fileStream->skip(_header.frameCount * wordSize * 2);
-#else
- switch (_header.version) {
- case 4:
- case 5: // sizes are 16-bit integers
- // Skip table with frame image sizes, as we don't need it
- _fileStream->skip(_header.frameCount * 2);
- for (int i = 0; i < _header.frameCount; ++i)
- _frameTotalSize[i] = _fileStream->readUint16();
- break;
- case 6: // sizes are 32-bit integers
- // Skip table with frame image sizes, as we don't need it
- _fileStream->skip(_header.frameCount * 4);
- for (int i = 0; i < _header.frameCount; ++i)
- _frameTotalSize[i] = _fileStream->readUint32();
- break;
- default:
- error("Can't yet handle index table for robot version %d", _header.version);
+ ++_newestBlockIndex;
+ if (_newestBlockIndex == kAudioListSize) {
+ _newestBlockIndex = 0;
+ }
}
-#endif
- // 2 more unknown tables
- _fileStream->skip(1024 + 512);
+ _blocks[_newestBlockIndex] = new AudioBlock(position, size, data);
+ ++_blocksSize;
+}
- // Pad to nearest 2 kilobytes
- uint32 curPos = _fileStream->pos();
- if (curPos & 0x7ff)
- _fileStream->seek((curPos & ~0x7ff) + 2048);
+void RobotDecoder::AudioList::reset() {
+ stopAudioNow();
+ _startOffset = 0;
+ _status = kRobotAudioReady;
}
-RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) {
- _surface = new Graphics::Surface();
- _curFrame = -1;
- _dirtyPalette = false;
+void RobotDecoder::AudioList::prepareForPrimer() {
+ g_sci->_audio32->pause(kRobotChannel);
+ _status = kRobotAudioPaused;
}
-RobotDecoder::RobotVideoTrack::~RobotVideoTrack() {
- _surface->free();
- delete _surface;
+void RobotDecoder::AudioList::setAudioOffset(const int offset) {
+ _startOffset = offset;
}
-uint16 RobotDecoder::RobotVideoTrack::getWidth() const {
- return _surface->w;
+RobotDecoder::AudioList::AudioBlock::AudioBlock(const int position, const int size, const byte* const data) :
+ _position(position),
+ _size(size) {
+ _data = (byte *)malloc(size);
+ memcpy(_data, data, size);
}
-uint16 RobotDecoder::RobotVideoTrack::getHeight() const {
- return _surface->h;
+RobotDecoder::AudioList::AudioBlock::~AudioBlock() {
+ free(_data);
}
-Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const {
- return _surface->format;
+bool RobotDecoder::AudioList::AudioBlock::submit(const int startOffset) {
+ assert(_data != nullptr);
+ RobotAudioStream::RobotAudioPacket packet(_data, _size, (_position - startOffset) * 2);
+ return g_sci->_audio32->playRobotAudio(packet);
}
-void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) {
- byte *paletteData = new byte[chunkSize];
- stream->read(paletteData, chunkSize);
+void RobotDecoder::AudioList::freeAudioBlocks() {
+ while (_blocksSize != 0) {
+ delete _blocks[_oldestBlockIndex];
+ _blocks[_oldestBlockIndex] = nullptr;
+ ++_oldestBlockIndex;
+ if (_oldestBlockIndex == kAudioListSize) {
+ _oldestBlockIndex = 0;
+ }
+
+ --_blocksSize;
+ }
+}
- // SCI1.1 palette
- byte palFormat = paletteData[32];
- uint16 palColorStart = paletteData[25];
- uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29);
+bool RobotDecoder::primeAudio(const uint32 startTick) {
+ bool success = true;
+ _audioList.reset();
+
+ if (startTick == 0) {
+ _audioList.prepareForPrimer();
+ byte *evenPrimerBuff = new byte[_evenPrimerSize];
+ byte *oddPrimerBuff = new byte[_oddPrimerSize];
+
+ success = readPrimerData(evenPrimerBuff, oddPrimerBuff);
+ if (success) {
+ if (_evenPrimerSize != 0) {
+ _audioList.addBlock(0, _evenPrimerSize, evenPrimerBuff);
+ }
+ if (_oddPrimerSize != 0) {
+ _audioList.addBlock(1, _oddPrimerSize, oddPrimerBuff);
+ }
+ }
+
+ delete[] evenPrimerBuff;
+ delete[] oddPrimerBuff;
+ } else {
+ assert(_evenPrimerSize * 2 >= _audioRecordInterval || _oddPrimerSize * 2 >= _audioRecordInterval);
+
+ int audioStartFrame = 0;
+ int videoStartFrame = startTick * _frameRate / 60;
+ assert(videoStartFrame < _numFramesTotal);
+
+ int audioStartPosition = (startTick * RobotAudioStream::kRobotSampleRate) / 60;
+ if (audioStartPosition & 1) {
+ audioStartPosition--;
+ }
+ _audioList.setAudioOffset(audioStartPosition);
+ _audioList.prepareForPrimer();
+
+ if (audioStartPosition < _evenPrimerSize * 2 ||
+ audioStartPosition + 1 < _oddPrimerSize * 2) {
+
+ byte *evenPrimerBuffer = new byte[_evenPrimerSize];
+ byte *oddPrimerBuffer = new byte[_oddPrimerSize];
+ success = readPrimerData(evenPrimerBuffer, oddPrimerBuffer);
+ if (success) {
+ int halfAudioStartPosition = audioStartPosition / 2;
+ if (audioStartPosition < _evenPrimerSize * 2) {
+ _audioList.addBlock(audioStartPosition, _evenPrimerSize - halfAudioStartPosition, &evenPrimerBuffer[halfAudioStartPosition]);
+ }
+
+ if (audioStartPosition + 1 < _oddPrimerSize * 2) {
+ _audioList.addBlock(audioStartPosition + 1, _oddPrimerSize - halfAudioStartPosition, &oddPrimerBuffer[halfAudioStartPosition]);
+ }
+ }
+
+ delete[] evenPrimerBuffer;
+ delete[] oddPrimerBuffer;
+ }
- int palOffset = 37;
- memset(_palette, 0, 256 * 3);
+ if (audioStartPosition >= _firstAudioRecordPosition) {
+ int audioRecordSize = _expectedAudioBlockSize;
+ assert(audioRecordSize > 0);
+ assert(_audioRecordInterval > 0);
+ assert(_firstAudioRecordPosition >= 0);
- for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
- if (palFormat == kRobotPalVariable)
- palOffset++;
- _palette[colorNo * 3 + 0] = paletteData[palOffset++];
- _palette[colorNo * 3 + 1] = paletteData[palOffset++];
- _palette[colorNo * 3 + 2] = paletteData[palOffset++];
+ audioStartFrame = (audioStartPosition - _firstAudioRecordPosition) / _audioRecordInterval;
+ assert(audioStartFrame < videoStartFrame);
+
+ if (audioStartFrame > 0) {
+ int lastAudioFrame = audioStartFrame - 1;
+ int oddRemainder = lastAudioFrame & 1;
+ int audioRecordStart = (lastAudioFrame * _audioRecordInterval) + oddRemainder + _firstAudioRecordPosition;
+ int audioRecordEnd = (audioRecordStart + ((audioRecordSize - 1) * 2)) + oddRemainder + _firstAudioRecordPosition;
+
+ if (audioStartPosition >= audioRecordStart && audioStartPosition <= audioRecordEnd) {
+ --audioStartFrame;
+ }
+ }
+
+ assert(!(audioStartPosition & 1));
+ if (audioStartFrame & 1) {
+ ++audioStartPosition;
+ }
+
+ if (!readPartialAudioRecordAndSubmit(audioStartFrame, audioStartPosition)) {
+ return false;
+ }
+
+ ++audioStartFrame;
+ assert(audioStartFrame < videoStartFrame);
+
+ int oddRemainder = audioStartFrame & 1;
+ int audioRecordStart = (audioStartFrame * _audioRecordInterval) + oddRemainder + _firstAudioRecordPosition;
+ int audioRecordEnd = (audioRecordStart + ((audioRecordSize - 1) * 2)) + oddRemainder + _firstAudioRecordPosition;
+
+ if (audioStartPosition >= audioRecordStart && audioStartPosition <= audioRecordEnd) {
+ if (!readPartialAudioRecordAndSubmit(audioStartFrame, audioStartPosition + 1)) {
+ return false;
+ }
+
+ ++audioStartFrame;
+ }
+ }
+
+ int audioPosition, audioSize;
+ for (int i = audioStartFrame; i < videoStartFrame; i++) {
+ if (!readAudioDataFromRecord(i, _audioBuffer, audioPosition, audioSize)) {
+ break;
+ }
+
+ _audioList.addBlock(audioPosition, audioSize, _audioBuffer);
+ }
}
- _dirtyPalette = true;
- delete[] paletteData;
+ return success;
}
-void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) {
- // This is an O(n) operation, as each frame has a different size.
- // We need to know the actual frame size to have a constant video size.
- uint32 pos = stream->pos();
+bool RobotDecoder::readPrimerData(byte *outEvenBuffer, byte *outOddBuffer) {
+ if (_primerReservedSize != 0) {
+ if (_totalPrimerSize != 0) {
+ _stream->seek(_primerPosition, SEEK_SET);
+ if (_evenPrimerSize > 0) {
+ _stream->read(outEvenBuffer, _evenPrimerSize);
+ }
+
+ if (_oddPrimerSize > 0) {
+ _stream->read(outOddBuffer, _oddPrimerSize);
+ }
+ }
+ } else if (_primerZeroCompressFlag) {
+ memset(outEvenBuffer, 0, _evenPrimerSize);
+ memset(outOddBuffer, 0, _oddPrimerSize);
+ } else {
+ error("ReadPrimerData - Flags corrupt");
+ }
+
+ return !_stream->err();
+}
+
+bool RobotDecoder::readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize) {
+ _stream->seek(_recordPositions[frameNo] + _videoSizes[frameNo], SEEK_SET);
+ _audioList.submitDriverMax();
+
+ // Compressed absolute position of the audio block in the audio stream
+ const int position = _stream->readSint32();
- uint16 width = 0, height = 0;
+ // Size of the block of audio, excluding the audio block header
+ int size = _stream->readSint32();
- for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
- stream->skip(4);
- uint16 frameWidth = stream->readUint16();
- uint16 frameHeight = stream->readUint16();
- if (frameWidth > width)
- width = frameWidth;
- if (frameHeight > height)
- height = frameHeight;
- stream->skip(frameSizes[curFrame] - 8);
+ assert(size <= _expectedAudioBlockSize);
+
+ if (position == 0) {
+ return false;
}
- stream->seek(pos);
+ if (size != _expectedAudioBlockSize) {
+ memset(outBuffer, 0, kRobotZeroCompressSize);
+ _stream->read(outBuffer + kRobotZeroCompressSize, size);
+ size += kRobotZeroCompressSize;
+ } else {
+ _stream->read(outBuffer, size);
+ }
- _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ outAudioPosition = position;
+ outAudioSize = size;
+ return !_stream->err();
}
-RobotDecoder::RobotAudioTrack::RobotAudioTrack() {
- _audioStream = Audio::makeQueuingAudioStream(11025, false);
+bool RobotDecoder::readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition) {
+ int audioPosition, audioSize;
+ bool success = readAudioDataFromRecord(startFrame, _audioBuffer, audioPosition, audioSize);
+ if (success) {
+ const int relativeStartOffset = (startPosition - audioPosition) / 2;
+ _audioList.addBlock(startPosition, audioSize - relativeStartOffset, _audioBuffer + relativeStartOffset);
+ }
+
+ return success;
}
-RobotDecoder::RobotAudioTrack::~RobotAudioTrack() {
- delete _audioStream;
+#pragma mark -
+#pragma mark RobotDecoder - Rendering
+
+uint16 RobotDecoder::getFrameSize(Common::Rect &outRect) const {
+ outRect.clip(0, 0);
+ for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ ScreenItem &screenItem = *_screenItemList[i];
+ outRect.extend(screenItem.getNowSeenRect(*_plane));
+ }
+
+ return _numFramesTotal;
}
-void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) {
- _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+void RobotDecoder::doRobot() {
+ if (_status != kRobotStatusPlaying) {
+ return;
+ }
+
+ if (!_syncFrame) {
+ if (_cueForceShowFrame != -1) {
+ _currentFrameNo = _cueForceShowFrame;
+ _cueForceShowFrame = -1;
+ } else {
+ const int nextFrameNo = calculateNextFrameNo(_delayTime.predictedTicks());
+ if (nextFrameNo < _currentFrameNo) {
+ return;
+ }
+ _currentFrameNo = nextFrameNo;
+ }
+ }
+
+ if (_currentFrameNo >= _numFramesTotal) {
+ const int finalFrameNo = _numFramesTotal - 1;
+ if (_previousFrameNo == finalFrameNo) {
+ _status = kRobotStatusEnd;
+ if (_hasAudio) {
+ _audioList.stopAudio();
+ _frameRate = _normalFrameRate;
+ _hasAudio = false;
+ }
+ return;
+ } else {
+ _currentFrameNo = finalFrameNo;
+ }
+ }
+
+ if (_currentFrameNo == _previousFrameNo) {
+ _audioList.submitDriverMax();
+ return;
+ }
+
+ if (_hasAudio) {
+ for (int candidateFrameNo = _previousFrameNo + _maxSkippablePackets + 1; candidateFrameNo < _currentFrameNo; candidateFrameNo += _maxSkippablePackets + 1) {
+
+ _audioList.submitDriverMax();
+
+ int audioPosition, audioSize;
+ if (readAudioDataFromRecord(candidateFrameNo, _audioBuffer, audioPosition, audioSize)) {
+ _audioList.addBlock(audioPosition, audioSize, _audioBuffer);
+ }
+ }
+ _audioList.submitDriverMax();
+ }
+
+ _delayTime.startTiming();
+ seekToFrame(_currentFrameNo);
+ doVersion5();
+ if (_hasAudio) {
+ _audioList.submitDriverMax();
+ }
+}
+
+void RobotDecoder::frameAlmostVisible() {
+ if (_status == kRobotStatusPlaying && !_syncFrame) {
+ if (_previousFrameNo != _currentFrameNo) {
+ while (calculateNextFrameNo() < _currentFrameNo) {
+ _audioList.submitDriverMax();
+ }
+ }
+ }
+}
+
+void RobotDecoder::frameNowVisible() {
+ if (_status != kRobotStatusPlaying) {
+ return;
+ }
+
+ if (_syncFrame) {
+ _syncFrame = false;
+ if (_hasAudio) {
+ _audioList.startAudioNow();
+ _checkAudioSyncTime = _startTime + kAudioSyncCheckInterval;
+ }
+
+ setRobotTime(_currentFrameNo);
+ }
+
+ if (_delayTime.timingInProgress()) {
+ _delayTime.endTiming();
+ }
+
+ if (_hasAudio) {
+ _audioList.submitDriverMax();
+ }
+
+ if (_previousFrameNo != _currentFrameNo) {
+ _previousFrameNo = _currentFrameNo;
+ }
+
+ if (!_syncFrame && _hasAudio && getTickCount() >= _checkAudioSyncTime) {
+ RobotAudioStream::StreamState status;
+ const bool success = g_sci->_audio32->queryRobotAudio(status);
+ if (!success) {
+ return;
+ }
+
+ const int bytesPerFrame = status.rate / _normalFrameRate * (status.bits == 16 ? 2 : 1);
+ // check again in 1/3rd second
+ _checkAudioSyncTime = getTickCount() + 60 / 3;
+
+ const int currentVideoFrameNo = calculateNextFrameNo() - _startingFrameNo;
+ const int currentAudioFrameNo = status.bytesPlaying / bytesPerFrame;
+ debugC(kDebugLevelVideo, "Video frame %d %s audio frame %d", currentVideoFrameNo, currentVideoFrameNo == currentAudioFrameNo ? "=" : currentVideoFrameNo < currentAudioFrameNo ? "<" : ">", currentAudioFrameNo);
+ if (currentVideoFrameNo < _numFramesTotal &&
+ currentAudioFrameNo < _numFramesTotal) {
+
+ bool shouldResetRobotTime = false;
+
+ if (currentAudioFrameNo < currentVideoFrameNo - 1 && _frameRate != _minFrameRate) {
+ debugC(kDebugLevelVideo, "[v] Reducing frame rate");
+ _frameRate = _minFrameRate;
+ shouldResetRobotTime = true;
+ } else if (currentAudioFrameNo > currentVideoFrameNo + 1 && _frameRate != _maxFrameRate) {
+ debugC(kDebugLevelVideo, "[^] Increasing frame rate");
+ _frameRate = _maxFrameRate;
+ shouldResetRobotTime = true;
+ } else if (_frameRate != _normalFrameRate) {
+ debugC(kDebugLevelVideo, "[=] Setting to normal frame rate");
+ _frameRate = _normalFrameRate;
+ shouldResetRobotTime = true;
+ }
+
+ if (shouldResetRobotTime) {
+ if (currentAudioFrameNo < _currentFrameNo) {
+ setRobotTime(_currentFrameNo);
+ } else {
+ setRobotTime(currentAudioFrameNo);
+ }
+ }
+ }
+ }
+}
+
+void RobotDecoder::expandCel(byte* target, const byte* source, const int16 celWidth, const int16 celHeight) const {
+ assert(source != nullptr && target != nullptr);
+
+ const int sourceHeight = (celHeight * _verticalScaleFactor) / 100;
+ assert(sourceHeight > 0);
+
+ const int16 numerator = celHeight;
+ const int16 denominator = sourceHeight;
+ int remainder = 0;
+ for (int16 y = sourceHeight - 1; y >= 0; --y) {
+ remainder += numerator;
+ int16 linesToDraw = remainder / denominator;
+ remainder %= denominator;
+
+ while (linesToDraw--) {
+ memcpy(target, source, celWidth);
+ target += celWidth;
+ }
+
+ source += celWidth;
+ }
+}
+
+void RobotDecoder::setPriority(const int16 newPriority) {
+ _priority = newPriority;
+}
+
+void RobotDecoder::doVersion5(const bool shouldSubmitAudio) {
+ const RobotScreenItemList::size_type oldScreenItemCount = _screenItemList.size();
+ const int videoSize = _videoSizes[_currentFrameNo];
+ _doVersion5Scratch.resize(videoSize);
+
+ byte *videoFrameData = _doVersion5Scratch.begin();
+
+ if (!_stream->read(videoFrameData, videoSize)) {
+ error("RobotDecoder::doVersion5: Read error");
+ }
+
+ const RobotScreenItemList::size_type screenItemCount = READ_SCI11ENDIAN_UINT16(videoFrameData);
+
+ if (screenItemCount > kScreenItemListSize) {
+ return;
+ }
+
+ if (_hasAudio &&
+ (getSciVersion() < SCI_VERSION_3 || shouldSubmitAudio)) {
+ int audioPosition, audioSize;
+ if (readAudioDataFromRecord(_currentFrameNo, _audioBuffer, audioPosition, audioSize)) {
+ _audioList.addBlock(audioPosition, audioSize, _audioBuffer);
+ }
+ }
+
+ if (screenItemCount > oldScreenItemCount) {
+ _screenItemList.resize(screenItemCount);
+ _screenItemX.resize(screenItemCount);
+ _screenItemY.resize(screenItemCount);
+ _originalScreenItemX.resize(screenItemCount);
+ _originalScreenItemY.resize(screenItemCount);
+ }
+
+ createCels5(videoFrameData + 2, screenItemCount, true);
+ for (RobotScreenItemList::size_type i = 0; i < screenItemCount; ++i) {
+ Common::Point position(_screenItemX[i], _screenItemY[i]);
+
+// TODO: Version 6 robot?
+// int scaleXRemainder;
+ if (_scaleInfo.signal == kScaleSignalDoScaling32) {
+ position.x = (position.x * _scaleInfo.x) / 128;
+// TODO: Version 6 robot?
+// scaleXRemainder = (position.x * _scaleInfo.x) % 128;
+ position.y = (position.y * _scaleInfo.y) / 128;
+ }
+
+ if (_screenItemList[i] == nullptr) {
+ CelInfo32 celInfo;
+ celInfo.bitmap = _celHandles[i].bitmapId;
+ ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo, position, _scaleInfo);
+ _screenItemList[i] = screenItem;
+ // TODO: Version 6 robot?
+ // screenItem->_field_30 = scaleXRemainder;
+
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_fixedPriority = true;
+ screenItem->_priority = _priority;
+ }
+ g_sci->_gfxFrameout->addScreenItem(*screenItem);
+ } else {
+ ScreenItem *screenItem = _screenItemList[i];
+ screenItem->_celInfo.bitmap = _celHandles[i].bitmapId;
+ screenItem->_position = position;
+ // TODO: Version 6 robot?
+ // screenItem->_field_30 = scaleXRemainder;
+
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_fixedPriority = true;
+ screenItem->_priority = _priority;
+ }
+ g_sci->_gfxFrameout->updateScreenItem(*screenItem);
+ }
+ }
+
+ for (RobotScreenItemList::size_type i = screenItemCount; i < oldScreenItemCount; ++i) {
+ if (_screenItemList[i] != nullptr) {
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]);
+ _screenItemList[i] = nullptr;
+ }
+ }
+}
+
+void RobotDecoder::createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette) {
+ preallocateCelMemory(rawVideoData, numCels);
+ for (int16 i = 0; i < numCels; ++i) {
+ rawVideoData += createCel5(rawVideoData, i, usePalette);
+ }
+}
+
+uint32 RobotDecoder::createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette) {
+ _verticalScaleFactor = rawVideoData[1];
+ const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2);
+ const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4);
+ const Common::Point celPosition((int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 10),
+ (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 12));
+ const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14);
+ const int16 numDataChunks = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 16);
+
+ rawVideoData += kCelHeaderSize;
+
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ Common::Point displace;
+ if (scriptWidth == kLowResX && scriptHeight == kLowResY) {
+ const Ratio lowResToScreenX(screenWidth, kLowResX);
+ const Ratio lowResToScreenY(screenHeight, kLowResY);
+ const Ratio screenToLowResX(kLowResX, screenWidth);
+ const Ratio screenToLowResY(kLowResY, screenHeight);
+
+ const int16 scaledX = celPosition.x + (_position.x * lowResToScreenX).toInt();
+ const int16 scaledY1 = celPosition.y + (_position.y * lowResToScreenY).toInt();
+ const int16 scaledY2 = scaledY1 + celHeight - 1;
+
+ const int16 lowResX = (scaledX * screenToLowResX).toInt();
+ const int16 lowResY = (scaledY2 * screenToLowResY).toInt();
+
+ displace.x = (scaledX - (lowResX * lowResToScreenX).toInt()) * -1;
+ displace.y = (lowResY * lowResToScreenY).toInt() - scaledY1;
+ _screenItemX[screenItemIndex] = lowResX;
+ _screenItemY[screenItemIndex] = lowResY;
+
+ debugC(kDebugLevelVideo, "Low resolution position c: %d %d l: %d/%d %d/%d d: %d %d s: %d/%d %d/%d x: %d y: %d", celPosition.x, celPosition.y, lowResX, scriptWidth, lowResY, scriptHeight, displace.x, displace.y, scaledX, screenWidth, scaledY2, screenHeight, scaledX - displace.x, scaledY2 - displace.y);
+ } else {
+ const int16 highResX = celPosition.x + _position.x;
+ const int16 highResY = celPosition.y + _position.y + celHeight - 1;
+
+ displace.x = 0;
+ displace.y = celHeight - 1;
+ _screenItemX[screenItemIndex] = highResX;
+ _screenItemY[screenItemIndex] = highResY;
+
+ debugC(kDebugLevelVideo, "High resolution position c: %d %d s: %d %d d: %d %d", celPosition.x, celPosition.y, highResX, highResY, displace.x, displace.y);
+ }
+
+ _originalScreenItemX[screenItemIndex] = celPosition.x;
+ _originalScreenItemY[screenItemIndex] = celPosition.y;
+
+ assert(_celHandles[screenItemIndex].area >= celWidth * celHeight);
+
+ SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[screenItemIndex].bitmapId);
+ assert(bitmap.getWidth() == celWidth && bitmap.getHeight() == celHeight);
+ assert(bitmap.getScaledWidth() == _xResolution && bitmap.getScaledHeight() == _yResolution);
+ assert(bitmap.getHunkPaletteOffset() == (uint32)bitmap.getWidth() * bitmap.getHeight() + SciBitmap::getBitmapHeaderSize());
+ bitmap.setDisplace(displace);
+
+ byte *targetBuffer = nullptr;
+ if (_verticalScaleFactor == 100) {
+ // direct copy to bitmap
+ targetBuffer = bitmap.getPixels();
+ } else {
+ // go through squashed cel decompressor
+ _celDecompressionBuffer.resize(_celDecompressionArea >= celWidth * (celHeight * _verticalScaleFactor / 100));
+ targetBuffer = _celDecompressionBuffer.begin();
+ }
+
+ for (int i = 0; i < numDataChunks; ++i) {
+ uint compressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData);
+ uint decompressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData + 4);
+ uint16 compressionType = READ_SCI11ENDIAN_UINT16(rawVideoData + 8);
+ rawVideoData += 10;
+
+ switch (compressionType) {
+ case kCompressionLZS: {
+ Common::MemoryReadStream videoDataStream(rawVideoData, compressedSize, DisposeAfterUse::NO);
+ _decompressor.unpack(&videoDataStream, targetBuffer, compressedSize, decompressedSize);
+ break;
+ }
+ case kCompressionNone:
+ Common::copy(rawVideoData, rawVideoData + decompressedSize, targetBuffer);
+ break;
+ default:
+ error("Unknown compression type %d!", compressionType);
+ }
+
+ rawVideoData += compressedSize;
+ targetBuffer += decompressedSize;
+ }
+
+ if (_verticalScaleFactor != 100) {
+ expandCel(bitmap.getPixels(), _celDecompressionBuffer.begin(), celWidth, celHeight);
+ }
+
+ if (usePalette) {
+ Common::copy(_rawPalette, _rawPalette + kRawPaletteSize, bitmap.getHunkPalette());
+ }
+
+ return kCelHeaderSize + dataSize;
}
-Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const {
- return _audioStream;
+void RobotDecoder::preallocateCelMemory(const byte *rawVideoData, const int16 numCels) {
+ for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) {
+ CelHandleInfo &celHandle = _celHandles[i];
+
+ if (celHandle.status == CelHandleInfo::kFrameLifetime) {
+ _segMan->freeBitmap(celHandle.bitmapId);
+ celHandle.bitmapId = NULL_REG;
+ celHandle.status = CelHandleInfo::kNoCel;
+ celHandle.area = 0;
+ }
+ }
+ _celHandles.resize(numCels);
+
+ const int numFixedCels = MIN(numCels, (int16)kFixedCelListSize);
+ for (int i = 0; i < numFixedCels; ++i) {
+ CelHandleInfo &celHandle = _celHandles[i];
+
+ // NOTE: There was a check to see if the cel handle was not allocated
+ // here, for some reason, which would mean that nothing was ever
+ // allocated from fixed cels, because the _celHandles array just got
+ // deleted and recreated...
+ if (celHandle.bitmapId == NULL_REG) {
+ break;
+ }
+
+ celHandle.bitmapId = _fixedCels[i];
+ celHandle.status = CelHandleInfo::kRobotLifetime;
+ celHandle.area = _maxCelArea[i];
+ }
+
+ uint maxFrameArea = 0;
+ for (int i = 0; i < numCels; ++i) {
+ const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2);
+ const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4);
+ const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14);
+ const uint area = celWidth * celHeight;
+
+ if (area > maxFrameArea) {
+ maxFrameArea = area;
+ }
+
+ CelHandleInfo &celHandle = _celHandles[i];
+ if (celHandle.status == CelHandleInfo::kRobotLifetime) {
+ if (_maxCelArea[i] < area) {
+ _segMan->freeBitmap(celHandle.bitmapId);
+ _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false);
+ celHandle.area = area;
+ celHandle.status = CelHandleInfo::kFrameLifetime;
+ }
+ } else if (celHandle.status == CelHandleInfo::kNoCel) {
+ _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false);
+ celHandle.area = area;
+ celHandle.status = CelHandleInfo::kFrameLifetime;
+ } else {
+ error("Cel Handle has bad status");
+ }
+
+ rawVideoData += kCelHeaderSize + dataSize;
+ }
+
+ if (maxFrameArea > _celDecompressionBuffer.size()) {
+ _celDecompressionBuffer.resize(maxFrameArea);
+ }
}
} // End of namespace Sci
diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h
index 4faea5008a..5fd6ad49c4 100644
--- a/engines/sci/video/robot_decoder.h
+++ b/engines/sci/video/robot_decoder.h
@@ -20,109 +20,1407 @@
*
*/
-#ifndef SCI_VIDEO_ROBOT_DECODER_H
-#define SCI_VIDEO_ROBOT_DECODER_H
+#ifndef SCI_SOUND_DECODERS_ROBOT_H
+#define SCI_SOUND_DECODERS_ROBOT_H
-#include "common/rational.h"
-#include "common/rect.h"
-#include "video/video_decoder.h"
+#include "audio/audiostream.h" // for AudioStream
+#include "audio/rate.h" // for st_sample_t
+#include "common/array.h" // for Array
+#include "common/mutex.h" // for StackLock, Mutex
+#include "common/rect.h" // for Point, Rect (ptr only)
+#include "common/scummsys.h" // for int16, int32, byte, uint16
+#include "sci/engine/vm_types.h" // for NULL_REG, reg_t
+#include "sci/graphics/helpers.h" // for GuiResourceId
+#include "sci/graphics/screen_item32.h" // for ScaleInfo, ScreenItem (ptr o...
-namespace Audio {
-class QueuingAudioStream;
-}
+namespace Common { class SeekableSubReadStreamEndian; }
+namespace Sci {
+class Plane;
+class SegManager;
-namespace Common {
-class SeekableSubReadStreamEndian;
-}
+// Notes on Robot v5/v6 format:
+//
+// Robot is a packetized streaming AV format that encodes multiple bitmaps +
+// positioning data, plus synchronised audio, for rendering in the SCI graphics
+// system.
+//
+// Unlike traditional AV formats, Robot videos almost always require playback
+// within the game engine because certain information (like the resolution of
+// the Robot coordinates and the background for the video) is dependent on data
+// that does not exist within the Robot file itself.
+//
+// The Robot container consists of a file header, an optional primer audio
+// section, an optional colour palette, a frame seek index, a set of cuepoints,
+// and variable-sized packets of compressed video+audio data.
+//
+// Integers in Robot files are coded using native endianness (LSB for x86
+// versions, MSB for 68k/PPC versions).
+//
+// Robot video coding is a relatively simple variable-length compression with no
+// interframe compression. Each cel in a frame is constructed from multiple
+// contiguous data blocks, each of which can be independently compressed with
+// LZS or left uncompressed. An entire cel can also be line decimated, where
+// lines are deleted from the source bitmap at compression time and are
+// reconstructed by decompression using line doubling. Each cel also includes
+// coordinates where it should be placed within the video frame, relative to the
+// top-left corner of the frame.
+//
+// Audio coding is fixed-length, and all audio blocks except for the primer
+// audio are the same size. Audio is encoded with Sierra SOL DPCM16 compression,
+// and is split into two channels ('even' and 'odd'), each at a 11025Hz sample
+// rate. The original signal is restored by interleaving samples from the two
+// channels together. Channel packets are 'even' if they have an ''absolute
+// position of audio'' that is evenly divisible by 2; otherwise, they are 'odd'.
+// Because the channels use DPCM compression, there is an 8-byte runway at the
+// start of every audio block that is never written to the output stream, which
+// is used to move the signal to the correct location by the 9th sample.
+//
+// File header (v5/v6):
+//
+// byte | description
+// 0 | signature 0x16
+// 1 | unused
+// 2-5 | signature 'SOL\0'
+// 6-7 | version (4, 5, and 6 are the only known versions)
+// 8-9 | size of audio blocks
+// 10-11 | primer is compressed flag
+// 12-13 | unused
+// 14-15 | total number of video frames
+// 16-17 | embedded palette size, in bytes
+// 18-19 | primer reserved size
+// 20-21 | coordinate X-resolution (if 0, uses game coordinates)
+// 22-23 | coordinate Y-resolution (if 0, uses game coordinates)
+// 24 | if non-zero, Robot includes a palette
+// 25 | if non-zero, Robot includes audio
+// 26-27 | unused
+// 28-29 | the frame rate, in frames per second
+// 30-31 | coordinate conversion flag; if true, screen item coordinates
+// | from the robot should be used as-is with NO conversion when
+// | explicitly displaying a specific frame
+// 32-33 | the maximum number of packets that can be skipped without causing
+// | audio drop-out
+// 34-35 | the maximum possible number of cels that will be displayed in any
+// | frame of the robot
+// 36-39 | the maximum possible size, in bytes, of the first fixed cel
+// 40-43 | the maximum possible size, in bytes, of the second fixed cel
+// 44-47 | the maximum possible size, in bytes, of the third fixed cel
+// 48-51 | the maximum possible size, in bytes, of the fourth fixed cel
+// 52-59 | unused
+//
+// If the ''file includes audio'' flag is false, seek ''primer reserved size''
+// bytes from the end of the file header to get past a padding zone.
+//
+// If the ''file includes audio'' flag is true, and the ''primer reserved size''
+// is not zero, the data immediately after the file header consists of an audio
+// primer header plus compressed audio data:
+//
+// Audio primer header:
+//
+// byte | description
+// 0-3 | the size, in bytes, of the entire primer audio section
+// 4-5 | the compression format of the primer audio (must be zero)
+// 6-9 | the size, in bytes, of the "even" primer
+// 10-13 | the size, in bytes, of the "odd" primer
+//
+// If the combined sizes of the even and odd primers do not match the ''primer
+// reserved size'', the next header block can be found ''primer reserved size''
+// bytes from the *start* of the audio primer header.
+//
+// Otherwise, if the Robot has audio, and the ''primer reserved size'' is zero,
+// and the ''primer is compressed flag'' is set, the "even" primer size is
+// 19922, the "odd" primer size is 21024, and the "even" and "odd" buffers
+// should be zero-filled.
+//
+// Any other combination of these flags is an error.
+//
+// If the Robot has a palette, the next ''palette size'' bytes should be read
+// as a SCI HunkPalette. Otherwise, seek ''palette size'' bytes from the current
+// position to get to the frame index.
+//
+// The next section of the Robot is the video frame size index. In version 5
+// robots, read ''total number of frames'' 16-bit integers to get the size of
+// the compressed video for each frame. For version 6 robots, use 32-bit
+// integers.
+//
+// The next section of the Robot is the packet size index (combined compressed
+// size of video + audio for each frame). In version 5 Robots, read ''total
+// number of frames'' 16-bit integers. In version 6 robots, use 32-bit integers.
+//
+// The next section of the Robot is the cue times index. Read 256 32-bit
+// integers, which represent the number of ticks from the start of playback that
+// the given cue point falls on.
+//
+// The next section of the Robot is the cue values index. Read 256 16-bit
+// integers, which represent the actual cue values that will be passed back to
+// the game engine when a cue is requested.
+//
+// Finally, to get to the first frame packet, seek from the current position to
+// the start of the next 2048-byte-aligned sector.
+//
+// Frame packet:
+//
+// byte | description
+// 0..n | video data (size is in the ''video frame size index'')
+// n+1.. | optional audio data (size is ''size of audio blocks'')
+//
+// Video data:
+//
+// byte | description
+// 0-2 | number of cels in the frame (max 10)
+// 3..n | cels
+//
+// Cel:
+//
+// 0-17 | cel header
+// 18..n | data chunks
+//
+// Cel header:
+//
+// byte | description
+// 0 | unused
+// 1 | vertical scale factor, in percent decimation (100 = no decimation,
+// | 50 = 50% of lines were removed)
+// 2-3 | cel width
+// 4-5 | cel height
+// 6-9 | unused
+// 10-11 | cel x-position, in Robot coordinates
+// 12-13 | cel y-position, in Robot coordinates
+// 14-15 | cel total data chunk size, in bytes
+// 16-17 | number of data chunks
+//
+// Cel data chunk:
+//
+// 0-9 | cel data chunk header
+// 10..n | cel data
+//
+// Cel data chunk header:
+//
+// byte | description
+// 0-3 | compressed size
+// 4-7 | decompressed size
+// 8-9 | compression type (0 = LZS, 2 = uncompressed)
+//
+// Random frame seeking can be done by calculating the address of the frame
+// packet by adding up the ''packet size index'' entries up to the current
+// frame. This will normally disable audio playback, as audio data in a packet
+// does not correspond to the video in the same packet.
+//
+// Audio data is placed immediately after the end of the video data in a packet,
+// and consists of an audio header plus compressed audio data:
+//
+// Audio data:
+//
+// byte | description
+// 0-7 | audio data header
+// 8-15 | DPCM runway
+// 16..n | compressed audio data
+//
+// Audio data header:
+//
+// byte | description
+// 0-3 | absolute position of audio in the audio stream
+// 4-7 | the size of the audio block, excluding the header
+//
+// When a block of audio is processed, first check to ensure that the
+// decompressed audio block's `position * 2 + length * 4` runs past the end of
+// the last packet of the same evenness/oddness. Discard the audio block
+// entirely if data has already been written past the end of this block for this
+// channel, or if the read head has already read past the end of this audio
+// block.
+//
+// If the block is not discarded, apply DPCM decompression to the entire block,
+// starting from beginning of the DPCM runway, using an initial sample value of
+// 0. Then, copy every sample from the decompressed source outside of the DPCM
+// runway into every *other* sample of the final audio buffer (1 -> 2, 2 -> 4,
+// 3 -> 6, etc.).
+//
+// Finally, for any skipped samples where the opposing (even/odd) channel did
+// not yet write, interpolate the skipped areas by adding together the
+// neighbouring samples from this audio block and dividing by two. (This allows
+// the audio quality to degrade to 11kHz in case it takes too long to decode all
+// the frames in the stream). Interpolated samples must not be written on top of
+// true data from the opposing channel. Audio from later packets must also not
+// be written on top of data in the same channel that was already written by an
+// earlier packet, in particular because the first 8 bytes of the next packet
+// are garbage data used to move the waveform to the correct position (due to
+// the use of DPCM compression).
-namespace Sci {
+#pragma mark -
+#pragma mark RobotAudioStream
+
+/**
+ * A Robot audio stream is a simple loop buffer
+ * that accepts audio blocks from the Robot engine.
+ */
+class RobotAudioStream : public Audio::AudioStream {
+public:
+ enum {
+ /**
+ * The sample rate used for all robot audio.
+ */
+ kRobotSampleRate = 22050,
+
+ /**
+ * Multiplier for the size of a packet that
+ * is being expanded by writing to every other
+ * byte of the target buffer.
+ */
+ kEOSExpansion = 2
+ };
+
+ /**
+ * Playback state information. Used for framerate
+ * calculation.
+ */
+ struct StreamState {
+ /**
+ * The current position of the read head of
+ * the audio stream.
+ */
+ int bytesPlaying;
+
+ /**
+ * The sample rate of the audio stream.
+ * Always 22050.
+ */
+ uint16 rate;
+
+ /**
+ * The bit depth of the audio stream.
+ * Always 16.
+ */
+ uint8 bits;
+ };
+
+ /**
+ * A single packet of compressed audio from a
+ * Robot data stream.
+ */
+ struct RobotAudioPacket {
+ /**
+ * Raw DPCM-compressed audio data.
+ */
+ byte *data;
+
+ /**
+ * The size of the compressed audio data,
+ * in bytes.
+ */
+ int dataSize;
+
+ /**
+ * The uncompressed, file-relative position
+ * of this audio packet.
+ */
+ int position;
+
+ RobotAudioPacket(byte *data_, const int dataSize_, const int position_) :
+ data(data_), dataSize(dataSize_), position(position_) {}
+ };
+
+ RobotAudioStream(const int32 bufferSize);
+ virtual ~RobotAudioStream();
+
+ /**
+ * Adds a new audio packet to the stream.
+ * @returns `true` if the audio packet was fully
+ * consumed, otherwise `false`.
+ */
+ bool addPacket(const RobotAudioPacket &packet);
+
+ /**
+ * Prevents any additional audio packets from
+ * being added to the audio stream.
+ */
+ void finish();
+
+ /**
+ * Returns the current status of the audio
+ * stream.
+ */
+ StreamState getStatus() const;
+
+private:
+ Common::Mutex _mutex;
+
+ /**
+ * Loop buffer for playback. Contains decompressed
+ * 16-bit PCM samples.
+ */
+ byte *_loopBuffer;
+
+ /**
+ * The size of the loop buffer, in bytes.
+ */
+ int32 _loopBufferSize;
+
+ /**
+ * The position of the read head within the loop
+ * buffer, in bytes.
+ */
+ int32 _readHead;
+
+ /**
+ * The lowest file position that can be buffered,
+ * in uncompressed bytes.
+ */
+ int32 _readHeadAbs;
+
+ /**
+ * The highest file position that can be buffered,
+ * in uncompressed bytes.
+ */
+ int32 _maxWriteAbs;
+
+ /**
+ * The highest file position, in uncompressed bytes,
+ * that has been written to the stream.
+ * Different from `_maxWriteAbs`, which is the highest
+ * uncompressed position which *can* be written right
+ * now.
+ */
+ int32 _writeHeadAbs;
+
+ /**
+ * The highest file position, in uncompressed bytes,
+ * that has been written to the even & odd sides of
+ * the stream.
+ *
+ * Index 0 corresponds to the 'even' side; index
+ * 1 correspond to the 'odd' side.
+ */
+ int32 _jointMin[2];
+
+ /**
+ * When `true`, the stream is waiting for all primer
+ * blocks to be received before allowing playback to
+ * begin.
+ */
+ bool _waiting;
+
+ /**
+ * When `true`, the stream will accept no more audio
+ * blocks.
+ */
+ bool _finished;
+
+ /**
+ * The uncompressed position of the first packet of
+ * robot data. Used to decide whether all primer
+ * blocks have been received and the stream should
+ * be started.
+ */
+ int32 _firstPacketPosition;
+
+ /**
+ * Decompression buffer, used to temporarily store
+ * an uncompressed block of audio data.
+ */
+ byte *_decompressionBuffer;
+
+ /**
+ * The size of the decompression buffer, in bytes.
+ */
+ int32 _decompressionBufferSize;
+
+ /**
+ * The position of the packet currently in the
+ * decompression buffer. Used to avoid
+ * re-decompressing audio data that has already
+ * been decompressed during a partial packet read.
+ */
+ int32 _decompressionBufferPosition;
+
+ /**
+ * Calculates the absolute ranges for new fills
+ * into the loop buffer.
+ */
+ void fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex);
+
+ /**
+ * Interpolates `numSamples` samples from the read
+ * head, if no true samples were written for one
+ * (or both) of the joint channels.
+ */
+ void interpolateMissingSamples(const int32 numSamples);
+
+#pragma mark -
+#pragma mark RobotAudioStream - AudioStream implementation
+public:
+ int readBuffer(Audio::st_sample_t *outBuffer, int numSamples) override;
+ virtual bool isStereo() const override { return false; };
+ virtual int getRate() const override { return 22050; };
+ virtual bool endOfData() const override {
+ Common::StackLock lock(_mutex);
+ return _readHeadAbs >= _writeHeadAbs;
+ };
+ virtual bool endOfStream() const override {
+ Common::StackLock lock(_mutex);
+ return _finished && endOfData();
+ }
+};
+
+#pragma mark -
+#pragma mark RobotDecoder
+
+/**
+ * RobotDecoder implements the logic required
+ * for Robot animations.
+ *
+ * @note A paused or finished RobotDecoder was
+ * classified as serializable in SCI3, but the
+ * save/load code would attempt to use uninitialised
+ * values, so it seems that robots were not ever
+ * actually able to be saved.
+ */
+class RobotDecoder {
+public:
+ RobotDecoder(SegManager *segMan);
+ ~RobotDecoder();
+
+private:
+ SegManager *_segMan;
+
+#pragma mark Constants
+public:
+ /**
+ * The playback status of the robot.
+ */
+ enum RobotStatus {
+ kRobotStatusUninitialized = 0,
+ kRobotStatusPlaying = 1,
+ kRobotStatusEnd = 2,
+ kRobotStatusPaused = 3
+ };
+
+ enum {
+ // Special high value used to represent
+ // parameters that should be left unchanged
+ // when calling `showFrame`
+ kUnspecified = 50000
+ };
+
+private:
+ enum {
+ /**
+ * Maximum number of on-screen screen items.
+ */
+ kScreenItemListSize = 10,
+
+ /**
+ * Maximum number of queued audio blocks.
+ */
+ kAudioListSize = 10,
+
+ /**
+ * Maximum number of samples used for frame timing.
+ */
+ kDelayListSize = 10,
+
+ /**
+ * Maximum number of cues.
+ */
+ kCueListSize = 256,
+
+ /**
+ * Maximum number of 'fixed' cels that never
+ * change for the duration of a robot.
+ */
+ kFixedCelListSize = 4,
+
+ /**
+ * The size of a hunk palette in the Robot stream.
+ */
+ kRawPaletteSize = 1200,
+
+ /**
+ * The size of a frame of Robot data. This
+ * value was used to align the first block of
+ * data after the main Robot header to the next
+ * CD sector.
+ */
+ kRobotFrameSize = 2048,
+
+ /**
+ * The size of a block of zero-compressed
+ * audio. Used to fill audio when the size of
+ * an audio packet does not match the expected
+ * packet size.
+ */
+ kRobotZeroCompressSize = 2048,
-class RobotDecoder : public Video::VideoDecoder {
+ /**
+ * The size of the audio block header, in bytes.
+ * The audio block header consists of the
+ * compressed size of the audio in the record,
+ * plus the position of the audio in the
+ * compressed data stream.
+ */
+ kAudioBlockHeaderSize = 8,
+
+ /**
+ * The size of a Robot cel header, in bytes.
+ */
+ kCelHeaderSize = 22,
+
+ /**
+ * The maximum amount that the frame rate is
+ * allowed to drift from the nominal frame rate
+ * in order to correct for AV drift or slow
+ * playback.
+ */
+ kMaxFrameRateDrift = 1
+ };
+
+ /**
+ * The version number for the currently loaded
+ * robot.
+ *
+ * There are several known versions of robot:
+ *
+ * v2: before Nov 1994; no known examples
+ * v3: before Nov 1994; no known examples
+ * v4: Jan 1995; PQ:SWAT demo
+ * v5: Mar 1995; SCI2.1 and SCI3 games
+ * v6: SCI3 games
+ */
+ uint16 _version;
+
+#pragma mark -
+#pragma mark Initialisation
+private:
+ /**
+ * Sets up the read stream for the robot.
+ */
+ void initStream(const GuiResourceId robotId);
+
+ /**
+ * Sets up the initial values for playback control.
+ */
+ void initPlayback();
+
+ /**
+ * Sets up the initial values for audio decoding.
+ */
+ void initAudio();
+
+ /**
+ * Sets up the initial values for video rendering.
+ */
+ void initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize);
+
+ /**
+ * Sets up the robot's data record and cue positions.
+ */
+ void initRecordAndCuePositions();
+
+#pragma mark -
+#pragma mark Playback
public:
- RobotDecoder(bool isBigEndian);
- virtual ~RobotDecoder();
+ /**
+ * Opens a robot file for playback.
+ * Newly opened robots are paused by default.
+ */
+ void open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale);
- bool loadStream(Common::SeekableReadStream *stream);
- bool load(GuiResourceId id);
+ /**
+ * Closes the currently open robot file.
+ */
void close();
- void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); }
- Common::Point getPos() const { return _pos; }
+ /**
+ * Pauses the robot. Once paused, the audio for a robot
+ * is disabled until the end of playback.
+ */
+ void pause();
+
+ /**
+ * Resumes a paused robot.
+ */
+ void resume();
+
+ /**
+ * Moves robot to the specified frame and pauses playback.
+ *
+ * @note Called DisplayFrame in SSCI.
+ */
+ void showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority);
+
+ /**
+ * Retrieves the value associated with the
+ * current cue point.
+ */
+ int16 getCue() const;
+
+ /**
+ * Gets the currently displayed frame.
+ */
+ int16 getFrameNo() const;
+
+ /**
+ * Gets the playback status of the player.
+ */
+ RobotStatus getStatus() const;
+
+private:
+ /**
+ * The read stream containing raw robot data.
+ */
+ Common::SeekableSubReadStreamEndian *_stream;
+
+ /**
+ * The current status of the player.
+ */
+ RobotStatus _status;
+
+ typedef Common::Array<int> PositionList;
+
+ /**
+ * A map of frame numbers to byte offsets within `_stream`.
+ */
+ PositionList _recordPositions;
+
+ /**
+ * The offset of the Robot file within a
+ * resource bundle.
+ */
+ int32 _fileOffset;
+
+ /**
+ * A list of cue times that is updated to
+ * prevent earlier cue values from being
+ * given to the game more than once.
+ */
+ mutable int32 _cueTimes[kCueListSize];
+
+ /**
+ * The original list of cue times from the
+ * raw Robot data.
+ */
+ int32 _masterCueTimes[kCueListSize];
+
+ /**
+ * The list of values to provide to a game
+ * when a cue value is requested.
+ */
+ int32 _cueValues[kCueListSize];
+
+ /**
+ * The current playback frame rate.
+ */
+ int16 _frameRate;
+
+ /**
+ * The nominal playback frame rate.
+ */
+ int16 _normalFrameRate;
+
+ /**
+ * The minimal playback frame rate. Used to
+ * correct for AV sync drift when the video
+ * is more than one frame ahead of the audio.
+ */
+ int16 _minFrameRate;
+
+ /**
+ * The maximum playback frame rate. Used to
+ * correct for AV sync drift when the video
+ * is more than one frame behind the audio.
+ */
+ int16 _maxFrameRate;
+
+ /**
+ * The maximum number of record blocks that
+ * can be skipped without causing audio to
+ * drop out.
+ */
+ int16 _maxSkippablePackets;
+
+ /**
+ * The currently displayed frame number.
+ */
+ int _currentFrameNo;
+
+ /**
+ * The last displayed frame number.
+ */
+ int _previousFrameNo;
+
+ /**
+ * The time, in ticks, when the robot was
+ * last started or resumed.
+ */
+ int32 _startTime;
+
+ /**
+ * The first frame displayed when the
+ * robot was resumed.
+ */
+ int32 _startFrameNo;
+
+ /**
+ * The last frame displayed when the robot
+ * was resumed.
+ */
+ int32 _startingFrameNo;
+
+ /**
+ * Seeks the raw data stream to the record for
+ * the given frame number.
+ */
+ bool seekToFrame(const int frameNo);
-protected:
- void readNextPacket();
+ /**
+ * Sets the start time and frame of the robot
+ * when the robot is started or resumed.
+ */
+ void setRobotTime(const int frameNo);
+#pragma mark -
+#pragma mark Timing
private:
- class RobotVideoTrack : public FixedRateVideoTrack {
+ /**
+ * This class tracks the amount of time it takes for
+ * a frame of robot animation to be rendered. This
+ * information is used by the player to speculatively
+ * skip rendering of future frames to keep the
+ * animation in sync with the robot audio.
+ */
+ class DelayTime {
public:
- RobotVideoTrack(int frameCount);
- ~RobotVideoTrack();
-
- uint16 getWidth() const;
- uint16 getHeight() const;
- Graphics::PixelFormat getPixelFormat() const;
- int getCurFrame() const { return _curFrame; }
- int getFrameCount() const { return _frameCount; }
- const Graphics::Surface *decodeNextFrame() { return _surface; }
- const byte *getPalette() const { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
-
- void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize);
- void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes);
- Graphics::Surface *getSurface() { return _surface; }
- void increaseCurFrame() { _curFrame++; }
-
- protected:
- Common::Rational getFrameRate() const { return Common::Rational(60, 10); }
+ DelayTime(RobotDecoder *decoder);
+
+ /**
+ * Starts performance timing.
+ */
+ void startTiming();
+
+ /**
+ * Ends performance timing.
+ */
+ void endTiming();
+
+ /**
+ * Returns whether or not timing is currently in
+ * progress.
+ */
+ bool timingInProgress() const;
+
+ /**
+ * Returns the median time, in ticks, of the
+ * currently stored timing samples.
+ */
+ int predictedTicks() const;
private:
- int _frameCount;
- int _curFrame;
- byte _palette[256 * 3];
- mutable bool _dirtyPalette;
- Graphics::Surface *_surface;
+ RobotDecoder *_decoder;
+
+ /**
+ * The start time, in ticks, of the current timing
+ * loop. If no loop is in progress, the value is 0.
+ *
+ * @note This is slightly different than SSCI where
+ * the not-timing value was -1.
+ */
+ uint32 _startTime;
+
+ /**
+ * A sorted list containing the timing data for
+ * the last `kDelayListSize` frames, in ticks.
+ */
+ int _delays[kDelayListSize];
+
+ /**
+ * A list of monotonically increasing identifiers
+ * used to identify and replace the oldest sample
+ * in the `_delays` array when finishing the
+ * next timing operation.
+ */
+ uint _timestamps[kDelayListSize];
+
+ /**
+ * The identifier of the oldest timing.
+ */
+ uint _oldestTimestamp;
+
+ /**
+ * The identifier of the newest timing.
+ */
+ uint _newestTimestamp;
+
+ /**
+ * Sorts the list of timings.
+ */
+ void sortList();
};
- class RobotAudioTrack : public AudioTrack {
+ /**
+ * Calculates the next frame number that needs
+ * to be rendered, using the timing data
+ * collected by DelayTime.
+ */
+ uint16 calculateNextFrameNo(const uint32 extraTicks = 0) const;
+
+ /**
+ * Calculates and returns the number of frames
+ * that should be rendered in `ticks` time,
+ * according to the current target frame rate
+ * of the robot.
+ */
+ uint32 ticksToFrames(const uint32 ticks) const;
+
+ /**
+ * Gets the current game time, in ticks.
+ */
+ uint32 getTickCount() const;
+
+ /**
+ * The performance timer for the robot.
+ */
+ DelayTime _delayTime;
+
+#pragma mark -
+#pragma mark Audio
+private:
+ enum {
+ /**
+ * The number of ticks that should elapse
+ * between each AV sync check.
+ */
+ kAudioSyncCheckInterval = 5 * 60 /* 5 seconds */
+ };
+
+ /**
+ * The status of the audio track of a Robot
+ * animation.
+ */
+ enum RobotAudioStatus {
+ kRobotAudioReady = 1,
+ kRobotAudioStopped = 2,
+ kRobotAudioPlaying = 3,
+ kRobotAudioPaused = 4,
+ kRobotAudioStopping = 5
+ };
+
+#pragma mark -
+#pragma mark Audio - AudioList
+private:
+ /**
+ * This class manages packetized audio playback
+ * for robots.
+ */
+ class AudioList {
public:
- RobotAudioTrack();
- ~RobotAudioTrack();
+ AudioList();
+
+ /**
+ * Starts playback of robot audio.
+ */
+ void startAudioNow();
+
+ /**
+ * Stops playback of robot audio, allowing
+ * any queued audio to finish playing back.
+ */
+ void stopAudio();
+
+ /**
+ * Stops playback of robot audio immediately.
+ */
+ void stopAudioNow();
+
+ /**
+ * Submits as many blocks of audio as possible
+ * to the audio engine.
+ */
+ void submitDriverMax();
+
+ /**
+ * Adds a new AudioBlock to the queue.
+ *
+ * @param position The absolute position of the
+ * audio for the block, in compressed bytes.
+ * @param size The size of the buffer.
+ * @param buffer A pointer to compressed audio
+ * data that will be copied into the new
+ * AudioBlock.
+ */
+ void addBlock(const int position, const int size, const byte *buffer);
- Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; }
+ /**
+ * Immediately stops any active playback and
+ * purges all audio data in the audio list.
+ */
+ void reset();
- void queueBuffer(byte *buffer, int size);
+ /**
+ * Pauses the robot audio channel in
+ * preparation for the first block of audio
+ * data to be read.
+ */
+ void prepareForPrimer();
- protected:
- Audio::AudioStream *getAudioStream() const;
+ /**
+ * Sets the audio offset which is used to
+ * offset the position of audio packets
+ * sent to the audio stream.
+ */
+ void setAudioOffset(const int offset);
+
+#pragma mark -
+#pragma mark Audio - AudioList - AudioBlock
private:
- Audio::QueuingAudioStream *_audioStream;
+ /**
+ * AudioBlock represents a block of audio
+ * from the Robot's audio track.
+ */
+ class AudioBlock {
+ public:
+ AudioBlock(const int position, const int size, const byte *const data);
+ ~AudioBlock();
+
+ /**
+ * Submits the block of audio to the
+ * audio manager.
+ * @returns true if the block was fully
+ * read, or false if the block was not
+ * read or only partially read.
+ */
+ bool submit(const int startOffset);
+
+ private:
+ /**
+ * The absolute position, in compressed
+ * bytes, of this audio block's audio
+ * data in the audio stream.
+ */
+ int _position;
+
+ /**
+ * The compressed size, in bytes, of
+ * this audio block's audio data.
+ */
+ int _size;
+
+ /**
+ * A buffer containing raw
+ * SOL-compressed audio data.
+ */
+ byte *_data;
+ };
+
+ /**
+ * The list of compressed audio blocks
+ * submitted for playback.
+ */
+ AudioBlock *_blocks[kAudioListSize];
+
+ /**
+ * The number of blocks in `_blocks` that are
+ * ready to be submitted.
+ */
+ uint8 _blocksSize;
+
+ /**
+ * The index of the oldest submitted audio block.
+ */
+ uint8 _oldestBlockIndex;
+
+ /**
+ * The index of the newest submitted audio block.
+ */
+ uint8 _newestBlockIndex;
+
+ /**
+ * The offset used when sending packets to the
+ * audio stream.
+ */
+ int _startOffset;
+
+ /**
+ * The status of robot audio playback.
+ */
+ RobotAudioStatus _status;
+
+ /**
+ * Frees all audio blocks in the `_blocks` list.
+ */
+ void freeAudioBlocks();
};
- struct RobotHeader {
- // 6 bytes, identifier bytes
- uint16 version;
- uint16 audioChunkSize;
- uint16 audioSilenceSize;
- // 2 bytes, unknown
- uint16 frameCount;
- uint16 paletteDataSize;
- uint16 unkChunkDataSize;
- // 5 bytes, unknown
- byte hasSound;
- // 34 bytes, unknown
- } _header;
-
- void readHeaderChunk();
- void readFrameSizesChunk();
-
- Common::Point _pos;
- bool _isBigEndian;
- uint32 *_frameTotalSize;
-
- Common::SeekableSubReadStreamEndian *_fileStream;
-};
+ /**
+ * Whether or not this robot animation has
+ * an audio track.
+ */
+ bool _hasAudio;
+
+ /**
+ * The audio list for the current robot.
+ */
+ AudioList _audioList;
+
+ /**
+ * The size, in bytes, of a block of audio data,
+ * excluding the audio block header.
+ */
+ uint16 _audioBlockSize;
+
+ /**
+ * The expected size of a block of audio data,
+ * in bytes, excluding the audio block header.
+ */
+ int16 _expectedAudioBlockSize;
+
+ /**
+ * The number of compressed audio bytes that are
+ * needed per frame to fill the audio buffer
+ * without causing audio to drop out.
+ */
+ int16 _audioRecordInterval;
+
+ /**
+ * If true, primer audio buffers should be filled
+ * with silence instead of trying to read buffers
+ * from the Robot data.
+ */
+ uint16 _primerZeroCompressFlag;
+
+ /**
+ * The size, in bytes, of the primer audio in the
+ * Robot, including any extra alignment padding.
+ */
+ uint16 _primerReservedSize;
+
+ /**
+ * The combined size, in bytes, of the even and odd
+ * primer channels.
+ */
+ int32 _totalPrimerSize;
+
+ /**
+ * The absolute offset of the primer audio data in
+ * the robot data stream.
+ */
+ int32 _primerPosition;
+
+ /**
+ * The size, in bytes, of the even primer.
+ */
+ int32 _evenPrimerSize;
+
+ /**
+ * The size, in bytes, of the odd primer.
+ */
+ int32 _oddPrimerSize;
+
+ /**
+ * The absolute position in the audio stream of
+ * the first audio packet.
+ */
+ int32 _firstAudioRecordPosition;
-} // End of namespace Sci
+ /**
+ * A temporary buffer used to hold one frame of
+ * raw (DPCM-compressed) audio when reading audio
+ * records from the robot stream.
+ */
+ byte *_audioBuffer;
+ /**
+ * The next tick count when AV sync should be
+ * checked and framerate adjustments made, if
+ * necessary.
+ */
+ uint32 _checkAudioSyncTime;
+
+ /**
+ * Primes the audio buffer with the first frame
+ * of audio data.
+ *
+ * @note `primeAudio` was `InitAudio` in SSCI
+ */
+ bool primeAudio(const uint32 startTick);
+
+ /**
+ * Reads primer data from the robot data stream
+ * and puts it into the given buffers.
+ */
+ bool readPrimerData(byte *outEvenBuffer, byte *outOddBuffer);
+
+ /**
+ * Reads audio data for the given frame number
+ * into the given buffer.
+ *
+ * @param outAudioPosition The position of the
+ * audio, in compressed bytes, in the data stream.
+ * @param outAudioSize The size of the audio data,
+ * in compressed bytes.
+ */
+ bool readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize);
+
+ /**
+ * Submits part of the audio packet of the given
+ * frame to the audio list, starting `startPosition`
+ * bytes into the audio.
+ */
+ bool readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition);
+
+#pragma mark -
+#pragma mark Rendering
+public:
+ /**
+ * Puts the current dimensions of the robot, in game script
+ * coordinates, into the given rect, and returns the total
+ * number of frames in the robot animation.
+ */
+ uint16 getFrameSize(Common::Rect &outRect) const;
+
+ /**
+ * Pumps the robot player for the next frame of video.
+ * This is the main rendering function.
+ */
+ void doRobot();
+
+ /**
+ * Submits any outstanding audio blocks that should
+ * be added to the queue before the robot frame
+ * becomes visible.
+ */
+ void frameAlmostVisible();
+
+ /**
+ * Evaluates frame drift and makes modifications to
+ * the player in order to ensure that future frames
+ * will arrive on time.
+ */
+ void frameNowVisible();
+
+ /**
+ * Scales a vertically compressed cel to its original
+ * uncompressed dimensions.
+ */
+ void expandCel(byte *target, const byte* source, const int16 celWidth, const int16 celHeight) const;
+
+ /**
+ * Sets the visual priority of the robot.
+ * @see Plane::_priority
+ */
+ void setPriority(const int16 newPriority);
+
+private:
+ enum CompressionType {
+ kCompressionLZS = 0,
+ kCompressionNone = 2
+ };
+
+ /**
+ * Describes the state of a Robot video cel.
+ */
+ struct CelHandleInfo {
+ /**
+ * The persistence level of Robot cels.
+ */
+ enum CelHandleLifetime {
+ kNoCel = 0,
+ kFrameLifetime = 1,
+ kRobotLifetime = 2
+ };
+
+ /**
+ * A reg_t pointer to an in-memory
+ * bitmap containing the cel.
+ */
+ reg_t bitmapId;
+
+ /**
+ * The lifetime of the cel, either just
+ * for this frame or for the entire
+ * duration of the robot playback.
+ */
+ CelHandleLifetime status;
+
+ /**
+ * The size, in pixels, of the decompressed
+ * cel.
+ */
+ int area;
+
+ CelHandleInfo() : bitmapId(NULL_REG), status(kNoCel), area(0) {}
+ };
+
+ typedef Common::Array<ScreenItem *> RobotScreenItemList;
+ typedef Common::Array<CelHandleInfo> CelHandleList;
+ typedef Common::Array<int> VideoSizeList;
+ typedef Common::Array<uint> MaxCelAreaList;
+ typedef Common::Array<reg_t> FixedCelsList;
+ typedef Common::Array<Common::Point> CelPositionsList;
+ typedef Common::Array<byte> ScratchMemory;
+
+ /**
+ * Renders a version 5/6 robot frame.
+ */
+ void doVersion5(const bool shouldSubmitAudio = true);
+
+ /**
+ * Creates screen items for a version 5/6 robot.
+ */
+ void createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette);
+
+ /**
+ * Creates a single screen item for a cel in a
+ * version 5/6 robot.
+ *
+ * Returns the size, in bytes, of the raw cel data.
+ */
+ uint32 createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette);
+
+ /**
+ * Preallocates memory for the next `numCels` cels
+ * in the robot data stream.
+ */
+ void preallocateCelMemory(const byte *rawVideoData, const int16 numCels);
+
+ /**
+ * The decompressor for LZS-compressed cels.
+ */
+ DecompressorLZS _decompressor;
+
+ /**
+ * The origin of the robot animation, in screen
+ * coordinates.
+ */
+ Common::Point _position;
+
+ /**
+ * Global scaling applied to the robot.
+ */
+ ScaleInfo _scaleInfo;
+
+ /**
+ * The native resolution of the robot.
+ */
+ int16 _xResolution, _yResolution;
+
+ /**
+ * Whether or not the coordinates read from robot
+ * data are high resolution.
+ */
+ bool _isHiRes;
+
+ /**
+ * The maximum number of cels that will be rendered
+ * on any given frame in this robot. Used for
+ * preallocation of cel memory.
+ */
+ int16 _maxCelsPerFrame;
+
+ /**
+ * The maximum areas, in pixels, for each of
+ * the fixed cels in the robot. Used for
+ * preallocation of cel memory.
+ */
+ MaxCelAreaList _maxCelArea;
+
+ /**
+ * The hunk palette to use when rendering the
+ * current frame, if the `usePalette` flag was set
+ * in the robot header.
+ */
+ uint8 *_rawPalette;
+
+ /**
+ * A list of the raw video data sizes, in bytes,
+ * for each frame of the robot.
+ */
+ VideoSizeList _videoSizes;
+
+ /**
+ * A list of cels that will be present for the
+ * entire duration of the robot animation.
+ */
+ FixedCelsList _fixedCels;
+
+ /**
+ * A list of handles for each cel in the current
+ * frame.
+ */
+ CelHandleList _celHandles;
+
+ /**
+ * Scratch memory used to temporarily store
+ * decompressed cel data for vertically squashed
+ * cels.
+ */
+ ScratchMemory _celDecompressionBuffer;
+
+ /**
+ * The size, in bytes, of the squashed cel
+ * decompression buffer.
+ */
+ int _celDecompressionArea;
+
+ /**
+ * If true, the robot just started playing and
+ * is awaiting output for the first frame.
+ */
+ bool _syncFrame;
+
+ /**
+ * Scratch memory used to store the compressed robot
+ * video data for the current frame.
+ */
+ ScratchMemory _doVersion5Scratch;
+
+ /**
+ * When set to a non-negative value, forces the next
+ * call to doRobot to render the given frame number
+ * instead of whatever frame would have normally been
+ * rendered.
+ */
+ mutable int _cueForceShowFrame;
+
+ /**
+ * The plane where the robot animation will be drawn.
+ */
+ Plane *_plane;
+
+ /**
+ * A list of pointers to ScreenItems used by the robot.
+ */
+ RobotScreenItemList _screenItemList;
+
+ /**
+ * The positions of the various screen items in this
+ * robot, in screen coordinates.
+ */
+ Common::Array<int16> _screenItemX, _screenItemY;
+
+ /**
+ * The raw position values from the cel header for
+ * each screen item currently on-screen.
+ */
+ Common::Array<int16> _originalScreenItemX, _originalScreenItemY;
+
+ /**
+ * The duration of the current robot, in frames.
+ */
+ uint16 _numFramesTotal;
+
+ /**
+ * The screen priority of the video.
+ * @see ScreenItem::_priority
+ */
+ int16 _priority;
+
+ /**
+ * The amount of visual vertical compression applied
+ * to the current cel. A value of 100 means no
+ * compression; a value above 100 indicates how much
+ * the cel needs to be scaled along the y-axis to
+ * return to its original dimensions.
+ */
+ uint8 _verticalScaleFactor;
+};
+} // end of namespace Sci
#endif
diff --git a/engines/titanic/carry/bowl_ear.cpp b/engines/titanic/carry/bowl_ear.cpp
index bb5172e580..852a77899a 100644
--- a/engines/titanic/carry/bowl_ear.cpp
+++ b/engines/titanic/carry/bowl_ear.cpp
@@ -24,6 +24,13 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBowlEar, CEar)
+ ON_MESSAGE(PETGainedObjectMsg)
+ ON_MESSAGE(ReplaceBowlAndNutsMsg)
+ ON_MESSAGE(NutPuzzleMsg)
+ ON_MESSAGE(MouseDragStartMsg)
+END_MESSAGE_MAP()
+
void CBowlEar::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CEar::save(file, indent);
@@ -34,4 +41,28 @@ void CBowlEar::load(SimpleFile *file) {
CEar::load(file);
}
+bool CBowlEar::PETGainedObjectMsg(CPETGainedObjectMsg *msg) {
+ CBowlStateChangeMsg changeMsg(3);
+ changeMsg.execute("ParrotNutBowlActor");
+
+ return CEar::PETGainedObjectMsg(msg);
+}
+
+bool CBowlEar::ReplaceBowlAndNutsMsg(CReplaceBowlAndNutsMsg *msg) {
+ setVisible(false);
+ return true;
+}
+
+bool CBowlEar::NutPuzzleMsg(CNutPuzzleMsg *msg) {
+ if (msg->_value == "BowlUnlocked")
+ _fieldE0 = 1;
+
+ return true;
+}
+
+bool CBowlEar::MouseDragStartMsg(CMouseDragStartMsg *msg) {
+ setVisible(true);
+ return CEar::MouseDragStartMsg(msg);
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/carry/bowl_ear.h b/engines/titanic/carry/bowl_ear.h
index 4f2fbea478..d78092f6d7 100644
--- a/engines/titanic/carry/bowl_ear.h
+++ b/engines/titanic/carry/bowl_ear.h
@@ -28,6 +28,11 @@
namespace Titanic {
class CBowlEar : public CEar {
+ DECLARE_MESSAGE_MAP;
+ bool PETGainedObjectMsg(CPETGainedObjectMsg *msg);
+ bool ReplaceBowlAndNutsMsg(CReplaceBowlAndNutsMsg *msg);
+ bool NutPuzzleMsg(CNutPuzzleMsg *msg);
+ bool MouseDragStartMsg(CMouseDragStartMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/carry/carry_parrot.cpp b/engines/titanic/carry/carry_parrot.cpp
index cf96204122..8a453e348c 100644
--- a/engines/titanic/carry/carry_parrot.cpp
+++ b/engines/titanic/carry/carry_parrot.cpp
@@ -111,7 +111,7 @@ bool CCarryParrot::MouseDragEndMsg(CMouseDragEndMsg *msg) {
if (compareViewNameTo("ParrotLobby.Node 1.N")) {
if (msg->_mousePos.x >= 75 && msg->_mousePos.x <= 565 &&
- !CParrot::_v2 && !CCage::_v2) {
+ !CParrot::_v2 && !CCage::_open) {
setVisible(false);
_fieldE0 = 0;
CTreeItem *perchedParrot = findUnder(getRoot(), "PerchedParrot");
diff --git a/engines/titanic/carry/central_core.cpp b/engines/titanic/carry/central_core.cpp
index a50c95abbc..e210b34cbe 100644
--- a/engines/titanic/carry/central_core.cpp
+++ b/engines/titanic/carry/central_core.cpp
@@ -21,9 +21,16 @@
*/
#include "titanic/carry/central_core.h"
+#include "titanic/npcs/parrot.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCentralCore, CBrain)
+ ON_MESSAGE(UseWithOtherMsg)
+ ON_MESSAGE(DropZoneLostObjectMsg)
+ ON_MESSAGE(DropZoneGotObjectMsg)
+END_MESSAGE_MAP()
+
void CCentralCore::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CBrain::save(file, indent);
@@ -34,4 +41,53 @@ void CCentralCore::load(SimpleFile *file) {
CBrain::load(file);
}
+bool CCentralCore::UseWithOtherMsg(CUseWithOtherMsg *msg) {
+ CString name = msg->_other->getName();
+ if (name == "HammerDispensorButton") {
+ CPuzzleSolvedMsg solvedMsg;
+ solvedMsg.execute("BigHammer");
+ } else if (name == "SpeechCentre") {
+ CShowTextMsg textMsg("This does not reach.");
+ textMsg.execute("PET");
+ }
+
+ return CBrain::UseWithOtherMsg(msg);
+}
+
+bool CCentralCore::DropZoneLostObjectMsg(CDropZoneLostObjectMsg *msg) {
+ CString name = msg->_object->getName();
+ if (name == "PerchCoreHolder") {
+ CParrot::_v2 = 1;
+ if (isEquals("CentralCore"))
+ CParrot::_v5 = 0;
+
+ CActMsg actMsg("LosePerch");
+ actMsg.execute("ParrotLobbyController");
+ } else if (name == "PerchHolder") {
+ CActMsg actMsg("LoseStick");
+ actMsg.execute("ParrotLobbyController");
+ }
+
+ return true;
+}
+
+bool CCentralCore::DropZoneGotObjectMsg(CDropZoneGotObjectMsg *msg) {
+ CString name = msg->_object->getName();
+ if (name == "PerchCoreHolder") {
+ if (isEquals("CentralCore")) {
+ CParrot::_v5 = 1;
+ CActMsg actMsg("CoreReplaced");
+ actMsg.execute("ParrotCage");
+ }
+
+ CActMsg actMsg("GainPerch");
+ actMsg.execute("ParrotLobbyController");
+ } else if (name == "PerchHolder") {
+ CActMsg actMsg("GainStick");
+ actMsg.execute("ParrotLobbyController");
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/carry/central_core.h b/engines/titanic/carry/central_core.h
index 9d7bef2c13..cc5d9c2f95 100644
--- a/engines/titanic/carry/central_core.h
+++ b/engines/titanic/carry/central_core.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CCentralCore : public CBrain {
+ DECLARE_MESSAGE_MAP;
+ bool UseWithOtherMsg(CUseWithOtherMsg *msg);
+ bool DropZoneLostObjectMsg(CDropZoneLostObjectMsg *msg);
+ bool DropZoneGotObjectMsg(CDropZoneGotObjectMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/carry/chicken.h b/engines/titanic/carry/chicken.h
index 65fe30fd81..e64ae458a4 100644
--- a/engines/titanic/carry/chicken.h
+++ b/engines/titanic/carry/chicken.h
@@ -41,7 +41,7 @@ class CChicken : public CCarry {
bool MouseDragEndMsg(CMouseDragEndMsg *msg);
bool PETObjectStateMsg(CPETObjectStateMsg *msg);
bool PETLostObjectMsg(CPETLostObjectMsg *msg);
-private:
+public:
static int _v1;
public:
int _field12C;
diff --git a/engines/titanic/carry/crushed_tv.cpp b/engines/titanic/carry/crushed_tv.cpp
index a265b611a9..486537d28e 100644
--- a/engines/titanic/carry/crushed_tv.cpp
+++ b/engines/titanic/carry/crushed_tv.cpp
@@ -76,5 +76,4 @@ bool CCrushedTV::MouseDragStartMsg(CMouseDragStartMsg *msg) {
return CCarry::MouseDragStartMsg(msg);
}
-
} // End of namespace Titanic
diff --git a/engines/titanic/carry/ear.cpp b/engines/titanic/carry/ear.cpp
index 8d85e247f7..a2234bc6dc 100644
--- a/engines/titanic/carry/ear.cpp
+++ b/engines/titanic/carry/ear.cpp
@@ -21,9 +21,15 @@
*/
#include "titanic/carry/ear.h"
+#include "titanic/game/head_slot.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CEar, CHeadPiece)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(UseWithOtherMsg)
+END_MESSAGE_MAP()
+
CEar::CEar() : CHeadPiece() {
}
@@ -37,4 +43,25 @@ void CEar::load(SimpleFile *file) {
CHeadPiece::load(file);
}
+bool CEar::ActMsg(CActMsg *msg) {
+ if (msg->_action == "MusicSolved")
+ _fieldE0 = true;
+ return true;
+}
+
+bool CEar::UseWithOtherMsg(CUseWithOtherMsg *msg) {
+ CHeadSlot *slot = dynamic_cast<CHeadSlot *>(msg->_other);
+ if (slot) {
+ setVisible(false);
+ petMoveToHiddenRoom();
+ setPosition(Point(0, 0));
+
+ CAddHeadPieceMsg addMsg(getName());
+ if (addMsg._value != "NULL")
+ addMsg.execute(addMsg._value == "Ear1" ? "Ear1Slot" : "Ear2Slot");
+ }
+
+ return CCarry::UseWithOtherMsg(msg);
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/carry/ear.h b/engines/titanic/carry/ear.h
index edef873d35..a357f46bbf 100644
--- a/engines/titanic/carry/ear.h
+++ b/engines/titanic/carry/ear.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CEar : public CHeadPiece {
+ DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool UseWithOtherMsg(CUseWithOtherMsg *msg);
public:
CLASSDEF;
CEar();
diff --git a/engines/titanic/carry/head_piece.cpp b/engines/titanic/carry/head_piece.cpp
index ae709644a0..34850488a7 100644
--- a/engines/titanic/carry/head_piece.cpp
+++ b/engines/titanic/carry/head_piece.cpp
@@ -24,13 +24,19 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CHeadPiece, CCarry)
+ ON_MESSAGE(SenseWorkingMsg)
+ ON_MESSAGE(PETGainedObjectMsg)
+ ON_MESSAGE(MouseDragStartMsg)
+END_MESSAGE_MAP()
+
CHeadPiece::CHeadPiece() : CCarry(), _string6("Not Working"),
- _field12C(0), _field13C(0) {
+ _flag(0), _field13C(false) {
}
void CHeadPiece::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_field12C, indent);
+ file->writeNumberLine(_flag, indent);
file->writeQuotedLine(_string6, indent);
file->writeNumberLine(_field13C, indent);
@@ -39,11 +45,49 @@ void CHeadPiece::save(SimpleFile *file, int indent) {
void CHeadPiece::load(SimpleFile *file) {
file->readNumber();
- _field12C = file->readNumber();
+ _flag = file->readNumber();
_string6 = file->readString();
_field13C = file->readNumber();
CCarry::load(file);
}
+bool CHeadPiece::SenseWorkingMsg(CSenseWorkingMsg *msg) {
+ _string6 = msg->_value;
+ return true;
+}
+
+bool CHeadPiece::PETGainedObjectMsg(CPETGainedObjectMsg *msg) {
+ _visibleFrame = 1;
+ if (!_field13C) {
+ stateInc38();
+ _field13C = true;
+ }
+
+ return true;
+}
+
+bool CHeadPiece::MouseDragStartMsg(CMouseDragStartMsg *msg) {
+ if (!checkPoint(msg->_mousePos, false, true)) {
+ return false;
+ } else if (!_fieldE0) {
+ return true;
+ }
+
+ if (_flag) {
+ setVisible(true);
+ moveToView();
+ setPosition(Point(msg->_mousePos.x - _bounds.width() / 2,
+ msg->_mousePos.y - _bounds.height() / 2));
+
+ CTakeHeadPieceMsg takeMsg(getName());
+ if (takeMsg._value != "NULL")
+ takeMsg.execute("TitaniaControl");
+
+ _flag = false;
+ }
+
+ return CCarry::MouseDragStartMsg(msg);
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/carry/head_piece.h b/engines/titanic/carry/head_piece.h
index 05ac772853..a76496072b 100644
--- a/engines/titanic/carry/head_piece.h
+++ b/engines/titanic/carry/head_piece.h
@@ -24,14 +24,19 @@
#define TITANIC_HEAD_PIECE_H
#include "titanic/carry/carry.h"
+#include "titanic/messages/pet_messages.h"
namespace Titanic {
class CHeadPiece : public CCarry {
+ DECLARE_MESSAGE_MAP;
+ bool SenseWorkingMsg(CSenseWorkingMsg *msg);
+ bool PETGainedObjectMsg(CPETGainedObjectMsg *msg);
+ bool MouseDragStartMsg(CMouseDragStartMsg *msg);
private:
- int _field12C;
+ bool _flag;
CString _string6;
- int _field13C;
+ bool _field13C;
public:
CLASSDEF;
CHeadPiece();
diff --git a/engines/titanic/carry/hose.h b/engines/titanic/carry/hose.h
index ebd45860e8..77ab437b8b 100644
--- a/engines/titanic/carry/hose.h
+++ b/engines/titanic/carry/hose.h
@@ -34,10 +34,10 @@ struct CHoseStatics {
class CHose : public CCarry {
protected:
- static CHoseStatics *_statics;
-
CString _string6;
public:
+ static CHoseStatics *_statics;
+public:
CLASSDEF;
CHose();
static void init();
diff --git a/engines/titanic/carry/phonograph_ear.cpp b/engines/titanic/carry/phonograph_ear.cpp
index ceb71babd2..9cd461d7e0 100644
--- a/engines/titanic/carry/phonograph_ear.cpp
+++ b/engines/titanic/carry/phonograph_ear.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CPhonographEar, CEar)
+ ON_MESSAGE(CorrectMusicPlayedMsg)
+ ON_MESSAGE(PETGainedObjectMsg)
+ ON_MESSAGE(TimerMsg)
+END_MESSAGE_MAP()
+
void CPhonographEar::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_field140, indent);
@@ -36,4 +42,24 @@ void CPhonographEar::load(SimpleFile *file) {
CEar::load(file);
}
+bool CPhonographEar::CorrectMusicPlayedMsg(CCorrectMusicPlayedMsg *msg) {
+ _fieldE0 = true;
+ return true;
+}
+
+bool CPhonographEar::PETGainedObjectMsg(CPETGainedObjectMsg *msg) {
+ if (_field140) {
+ _field140 = false;
+ addTimer(1000);
+ }
+
+ return PETGainedObjectMsg(msg);
+}
+
+bool CPhonographEar::TimerMsg(CTimerMsg *msg) {
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("Replacement Phonograph Ear");
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/carry/phonograph_ear.h b/engines/titanic/carry/phonograph_ear.h
index 582db9f7ef..b5db015f90 100644
--- a/engines/titanic/carry/phonograph_ear.h
+++ b/engines/titanic/carry/phonograph_ear.h
@@ -28,11 +28,15 @@
namespace Titanic {
class CPhonographEar : public CEar {
+ DECLARE_MESSAGE_MAP;
+ bool CorrectMusicPlayedMsg(CCorrectMusicPlayedMsg *msg);
+ bool PETGainedObjectMsg(CPETGainedObjectMsg *msg);
+ bool TimerMsg(CTimerMsg *msg);
private:
- int _field140;
+ bool _field140;
public:
CLASSDEF;
- CPhonographEar() : CEar(), _field140(1) {}
+ CPhonographEar() : CEar(), _field140(true) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/core/click_responder.cpp b/engines/titanic/core/click_responder.cpp
index f9694557df..9a0e0de7ab 100644
--- a/engines/titanic/core/click_responder.cpp
+++ b/engines/titanic/core/click_responder.cpp
@@ -24,20 +24,33 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CClickResponder, CGameObject)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
void CClickResponder::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string1, indent);
- file->writeQuotedLine(_string2, indent);
+ file->writeQuotedLine(_message, indent);
+ file->writeQuotedLine(_soundName, indent);
CGameObject::save(file, indent);
}
void CClickResponder::load(SimpleFile *file) {
file->readNumber();
- _string1 = file->readString();
- _string2 = file->readString();
+ _message = file->readString();
+ _soundName = file->readString();
CGameObject::load(file);
}
+bool CClickResponder::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (!_soundName.empty())
+ playSound(_soundName);
+ if (!_message.empty())
+ petDisplayMessage(_message);
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/core/click_responder.h b/engines/titanic/core/click_responder.h
index 78381b9948..40f22d7906 100644
--- a/engines/titanic/core/click_responder.h
+++ b/engines/titanic/core/click_responder.h
@@ -28,8 +28,10 @@
namespace Titanic {
class CClickResponder : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
protected:
- CString _string1, _string2;
+ CString _message, _soundName;
public:
CLASSDEF;
diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp
index 12d4c5603a..a61dd17003 100644
--- a/engines/titanic/core/game_object.cpp
+++ b/engines/titanic/core/game_object.cpp
@@ -1442,7 +1442,7 @@ void CGameObject::resetMail() {
mailMan->resetValue();
}
-int CGameObject::getNewRandomNumber(int max, int *oldVal) {
+int CGameObject::getRandomNumber(int max, int *oldVal) {
if (oldVal) {
int startingVal = *oldVal;
while (*oldVal == startingVal && max > 0)
@@ -1608,10 +1608,9 @@ void CGameObject::starFn1(int v) {
starControl->fn1(v);
}
-void CGameObject::starFn2() {
+bool CGameObject::starFn2() {
CStarControl *starControl = getStarControl();
- if (starControl)
- starControl->fn4();
+ return starControl ? starControl->fn4() : false;
}
/*------------------------------------------------------------------------*/
diff --git a/engines/titanic/core/game_object.h b/engines/titanic/core/game_object.h
index cdb5f6906a..0749bde5f2 100644
--- a/engines/titanic/core/game_object.h
+++ b/engines/titanic/core/game_object.h
@@ -531,7 +531,7 @@ protected:
/**
* Gets a new random number
*/
- int getNewRandomNumber(int max, int *oldVal = nullptr);
+ int getRandomNumber(int max, int *oldVal = nullptr);
public:
Rect _bounds;
bool _isMail;
@@ -884,7 +884,7 @@ public:
CStarControl *getStarControl() const;
void starFn1(int v);
- void starFn2();
+ bool starFn2();
/*--- CTrueTalkManager Methods ---*/
diff --git a/engines/titanic/core/named_item.cpp b/engines/titanic/core/named_item.cpp
index 6eafbf8c8b..9c4c28d04d 100644
--- a/engines/titanic/core/named_item.cpp
+++ b/engines/titanic/core/named_item.cpp
@@ -51,11 +51,11 @@ void CNamedItem::load(SimpleFile *file) {
CTreeItem::load(file);
}
-int CNamedItem::compareTo(const CString &name, int maxLen) const {
+bool CNamedItem::isEquals(const CString &name, int maxLen) const {
if (maxLen) {
- return getName().left(maxLen).compareToIgnoreCase(name);
+ return getName().left(maxLen).compareToIgnoreCase(name) == 0;
} else {
- return getName().compareToIgnoreCase(name);
+ return getName().compareToIgnoreCase(name) == 0;
}
}
diff --git a/engines/titanic/core/named_item.h b/engines/titanic/core/named_item.h
index acd59f344e..9ee3d490ae 100644
--- a/engines/titanic/core/named_item.h
+++ b/engines/titanic/core/named_item.h
@@ -59,9 +59,9 @@ public:
virtual const CString getName() const { return _name; }
/**
- * Compares the name of the item to a passed name
+ * Returns true if the item's name matches a passed name
*/
- virtual int compareTo(const CString &name, int maxLen = 0) const;
+ virtual bool isEquals(const CString &name, int maxLen = 0) const;
/**
* Find a parent node for the item
diff --git a/engines/titanic/core/saveable_object.cpp b/engines/titanic/core/saveable_object.cpp
index 62cee47045..5fc2d7e738 100644
--- a/engines/titanic/core/saveable_object.cpp
+++ b/engines/titanic/core/saveable_object.cpp
@@ -92,7 +92,7 @@
#include "titanic/game/bar_menu.h"
#include "titanic/game/bar_menu_button.h"
#include "titanic/game/belbot_get_light.h"
-#include "titanic/game/bilge_succubus.h"
+#include "titanic/npcs/bilge_succubus.h"
#include "titanic/game/bomb.h"
#include "titanic/game/bottom_of_well_monitor.h"
#include "titanic/game/bowl_unlocker.h"
@@ -103,7 +103,6 @@
#include "titanic/game/broken_pellerator.h"
#include "titanic/game/broken_pellerator_froz.h"
#include "titanic/game/cage.h"
-#include "titanic/game/call_pellerator.h"
#include "titanic/game/captains_wheel.h"
#include "titanic/game/cdrom.h"
#include "titanic/game/cdrom_computer.h"
@@ -228,7 +227,7 @@
#include "titanic/game/parrot/parrot_nut_bowl_actor.h"
#include "titanic/game/parrot/parrot_nut_eater.h"
#include "titanic/game/parrot/parrot_perch_holder.h"
-#include "titanic/game/parrot/parrot_succubus.h"
+#include "titanic/npcs/parrot_succubus.h"
#include "titanic/game/parrot/parrot_trigger.h"
#include "titanic/game/parrot/player_meets_parrot.h"
#include "titanic/game/pet/pet.h"
@@ -333,6 +332,7 @@
#include "titanic/messages/pet_messages.h"
#include "titanic/messages/service_elevator_door.h"
+#include "titanic/moves/call_pellerator.h"
#include "titanic/moves/enter_bomb_room.h"
#include "titanic/moves/enter_bridge.h"
#include "titanic/moves/enter_exit_first_class_state.h"
@@ -757,7 +757,7 @@ DEFFN(CAutoSoundEvent);
DEFFN(CBilgeAutoSoundEvent);
DEFFN(CBilgeDispensorEvent);
DEFFN(CBodyInBilgeRoomMsg);
-DEFFN(CBowlStateChange);
+DEFFN(CBowlStateChangeMsg);
DEFFN(CCarryObjectArrivedMsg);
DEFFN(CChangeMusicMsg);
DEFFN(CChangeSeasonMsg);
@@ -1344,7 +1344,7 @@ void CSaveableObject::initClassList() {
ADDFN(CBilgeAutoSoundEvent, CAutoSoundEvent);
ADDFN(CBilgeDispensorEvent, CAutoSoundEvent);
ADDFN(CBodyInBilgeRoomMsg, CMessage);
- ADDFN(CBowlStateChange, CMessage);
+ ADDFN(CBowlStateChangeMsg, CMessage);
ADDFN(CCarryObjectArrivedMsg, CMessage);
ADDFN(CChangeMusicMsg, CMessage);
ADDFN(CChangeSeasonMsg, CMessage);
diff --git a/engines/titanic/core/tree_item.h b/engines/titanic/core/tree_item.h
index 45ce5164ac..b2d40daab9 100644
--- a/engines/titanic/core/tree_item.h
+++ b/engines/titanic/core/tree_item.h
@@ -125,6 +125,11 @@ public:
virtual const CString getName() const { return CString(); }
/**
+ * Returns true if the item's name matches a passed name
+ */
+ virtual bool isEquals(const CString &name, int maxLen = 0) const { return false; }
+
+ /**
* Compares the name of the item to a passed name
*/
virtual int compareTo(const CString &name, int maxLen = 0) const { return false; }
diff --git a/engines/titanic/game/belbot_get_light.cpp b/engines/titanic/game/belbot_get_light.cpp
index 3e678a8a0c..2cc4c3ae19 100644
--- a/engines/titanic/game/belbot_get_light.cpp
+++ b/engines/titanic/game/belbot_get_light.cpp
@@ -24,6 +24,13 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBelbotGetLight, CGameObject)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(MovieFrameMsg)
+ ON_MESSAGE(EnterViewMsg)
+END_MESSAGE_MAP()
+
void CBelbotGetLight::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeQuotedLine(_value, indent);
@@ -36,4 +43,36 @@ void CBelbotGetLight::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CBelbotGetLight::ActMsg(CActMsg *msg) {
+ if (msg->_action == "BellbotGetLight") {
+ _value = getFullViewName();
+ lockMouse();
+ changeView("1stClassState.Node 11.N", "");
+ }
+
+ return true;
+}
+
+bool CBelbotGetLight::MovieEndMsg(CMovieEndMsg *msg) {
+ sleep(1000);
+ changeView(_value, "");
+ unlockMouse();
+ return true;
+}
+
+bool CBelbotGetLight::MovieFrameMsg(CMovieFrameMsg *msg) {
+ if (getMovieFrame() == 37) {
+ CActMsg actMsg("BellbotGetLight");
+ actMsg.execute("Eye1");
+ }
+
+ return true;
+}
+
+bool CBelbotGetLight::EnterViewMsg(CEnterViewMsg *msg) {
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ movieEvent(37);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/belbot_get_light.h b/engines/titanic/game/belbot_get_light.h
index a3aa0f737e..1707ad4793 100644
--- a/engines/titanic/game/belbot_get_light.h
+++ b/engines/titanic/game/belbot_get_light.h
@@ -28,6 +28,11 @@
namespace Titanic {
class CBelbotGetLight : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool MovieFrameMsg(CMovieFrameMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
private:
CString _value;
public:
diff --git a/engines/titanic/game/bilge_succubus.cpp b/engines/titanic/game/bilge_succubus.cpp
deleted file mode 100644
index ceee3f7740..0000000000
--- a/engines/titanic/game/bilge_succubus.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "titanic/game/bilge_succubus.h"
-
-namespace Titanic {
-
-CBilgeSuccUBus::CBilgeSuccUBus() : CSuccUBus(), _field1DC(0),
- _field1E0(0), _field1E4(0), _field1E8(0) {
-}
-
-void CBilgeSuccUBus::save(SimpleFile *file, int indent) {
- file->writeNumberLine(1, indent);
- file->writeNumberLine(_field1DC, indent);
- file->writeNumberLine(_field1E0, indent);
- file->writeNumberLine(_field1E4, indent);
- file->writeNumberLine(_field1E8, indent);
-
- CSuccUBus::save(file, indent);
-}
-
-void CBilgeSuccUBus::load(SimpleFile *file) {
- file->readNumber();
- _field1DC = file->readNumber();
- _field1E0 = file->readNumber();
- _field1E4 = file->readNumber();
- _field1E8 = file->readNumber();
-
- CSuccUBus::load(file);
-}
-
-} // End of namespace Titanic
diff --git a/engines/titanic/game/bomb.cpp b/engines/titanic/game/bomb.cpp
index 9a08f26ece..b1f3fdc5f7 100644
--- a/engines/titanic/game/bomb.cpp
+++ b/engines/titanic/game/bomb.cpp
@@ -25,17 +25,64 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBomb, CBackground)
+ ON_MESSAGE(StatusChangeMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TimerMsg)
+ ON_MESSAGE(TrueTalkGetStateValueMsg)
+ ON_MESSAGE(SetFrameMsg)
+END_MESSAGE_MAP()
+
+static const char *const WAVE_NAMES1[] = {
+ "z#353.wav", "z#339.wav", "z#325.wav", "z#311.wav", "z#297.wav",
+ "z#283.wav", "z#269.wav", "z#255.wav", "z#241.wav"
+};
+
+static const char *const WAVE_NAMES2[] = {
+ "", "z#352.wav", "z#338.wav", "z#324.wav", "z#310.wav", "z#296.wav",
+ "z#281.wav", "z#268.wav", "z#254.wav", "z#240.wav", "", "z#351.wav",
+ "z#337.wav", "z#323.wav", "z#309.wav", "z#295.wav", "z#282.wav",
+ "z#267.wav", "z#253.wav", "z#239.wav"
+};
+
+static const char *const WAVE_NAMES3[100] = {
+ "bombcountdown_c0.wav", "z#355.wav", "z#341.wav", "z#327.wav", "z#313.wav",
+ "z#299.wav", "z#285.wav", "z#271.wav", "z#257.wav", "z#243.wav",
+ "z#354.wav", "z#350.wav", "z#349.wav", "z#348.wav", "z#347.wav",
+ "z#346.wav", "z#345.wav", "z#344.wav", "z#343.wav", "z#342.wav",
+ "z#340.wav", "z#336.wav", "z#335.wav", "z#334.wav", "z#333.wav",
+ "z#332.wav", "z#331.wav", "z#330.wav", "z#329.wav", "z#328.wav",
+ "z#326.wav", "z#322.wav", "z#321.wav", "z#320.wav", "z#319.wav",
+ "z#318.wav", "z#317.wav", "z#316.wav", "z#315.wav", "z#314.wav",
+ "z#312.wav", "z#308.wav", "z#307.wav", "z#306.wav", "z#305.wav",
+ "z#304.wav", "z#303.wav", "z#302.wav", "z#301.wav", "z#300.wav",
+ "z#298.wav", "z#294.wav", "z#293.wav", "z#292.wav", "z#291.wav",
+ "z#290.wav", "z#289.wav", "z#288.wav", "z#287.wav", "z#286.wav",
+ "z#284.wav", "z#280.wav", "z#279.wav", "z#278.wav", "z#277.wav",
+ "z#276.wav", "z#275.wav", "z#274.wav", "z#273.wav", "z#272.wav",
+ "z#270.wav", "z#266.wav", "z#265.wav", "z#264.wav", "z#263.wav",
+ "z#262.wav", "z#261.wav", "z#260.wav", "z#259.wav", "z#258.wav",
+ "z#256.wav", "z#252.wav", "z#251.wav", "z#250.wav", "z#249.wav",
+ "z#248.wav", "z#247.wav", "z#246.wav", "z#245.wav", "z#244.wav",
+ "z#242.wav", "z#238.wav", "z#237.wav", "z#236.wav", "z#235.wav",
+ "z#234.wav", "z#233.wav", "z#232.wav", "z#231.wav", "z#230.wav",
+};
+
CBomb::CBomb() : CBackground() {
_fieldE0 = 0;
_fieldE4 = 0;
_fieldE8 = 17;
_fieldEC = 9;
_fieldF0 = 0;
- _fieldF4 = 999;
- _fieldF8 = 0;
+ _countdown = 999;
+ _soundHandle = 0;
_fieldFC = 0;
_startingTicks = 0;
- _field104 = 60;
+ _volume = 60;
}
void CBomb::save(SimpleFile *file, int indent) {
@@ -45,11 +92,11 @@ void CBomb::save(SimpleFile *file, int indent) {
file->writeNumberLine(_fieldE8, indent);
file->writeNumberLine(_fieldEC, indent);
file->writeNumberLine(_fieldF0, indent);
- file->writeNumberLine(_fieldF4, indent);
- file->writeNumberLine(_fieldF8, indent);
+ file->writeNumberLine(_countdown, indent);
+ file->writeNumberLine(_soundHandle, indent);
file->writeNumberLine(_fieldFC, indent);
file->writeNumberLine(_startingTicks, indent);
- file->writeNumberLine(_field104, indent);
+ file->writeNumberLine(_volume, indent);
CBackground::save(file, indent);
}
@@ -61,21 +108,257 @@ void CBomb::load(SimpleFile *file) {
_fieldE8 = file->readNumber();
_fieldEC = file->readNumber();
_fieldF0 = file->readNumber();
- _fieldF4 = file->readNumber();
- _fieldF8 = file->readNumber();
+ _countdown = file->readNumber();
+ _soundHandle = file->readNumber();
_fieldFC = file->readNumber();
_startingTicks = file->readNumber();
- _field104 = file->readNumber();
+ _volume = file->readNumber();
CBackground::load(file);
}
+bool CBomb::StatusChangeMsg(CStatusChangeMsg *msg) {
+ _fieldE4 += msg->_newStatus;
+
+ if (_fieldE4 == 23) {
+ startAnimTimer("Disarmed", 2000);
+ lockMouse();
+ }
+
+ _fieldF0 %= 1000;
+ if (!(_fieldF0 % 20) && _countdown < 995) {
+ int val = getRandomNumber(5) + 25;
+ if (_fieldF0 < 20 || _fieldF0 > 80)
+ val = 28;
+
+ CString name;
+ switch (val - 25) {
+ case 0:
+ name = "z#372.wav";
+ break;
+ case 1:
+ name = "z#371.wav";
+ break;
+ case 2:
+ name = "z#370.wav";
+ break;
+ case 3:
+ name = "z#369.wav";
+ break;
+ case 4:
+ name = "z#368.wav";
+ break;
+ default:
+ name = "z#366.wav";
+ break;
+ }
+
+ _soundHandle = queueSound(name, _soundHandle, _volume);
+ }
+
+ return true;
+}
+
+bool CBomb::EnterViewMsg(CEnterViewMsg *msg) {
+ _fieldE4 = 2;
+ return true;
+}
+
+bool CBomb::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ playSound("z#62.wav");
+
+ if (_fieldE0) {
+ stopSound(_soundHandle);
+ if (_fieldE4 < 23) {
+ _fieldE8 = MIN(_fieldE8 + 1, 23);
+
+ CString name;
+ switch (_fieldE8) {
+ case 18:
+ name = "z#380.wav";
+ break;
+ case 19:
+ name = "z#379.wav";
+ break;
+ case 20:
+ name = "z#377.wav";
+ break;
+ case 21:
+ name = "z#376.wav";
+ break;
+ case 22:
+ name = "z#375.wav";
+ break;
+ default:
+ name = "z#374.wav";
+ break;
+ }
+
+ _soundHandle = queueSound(name, _soundHandle, _volume);
+ _countdown = 999;
+ }
+ } else {
+ _soundHandle = playSound("z#389.wav", _volume);
+ _fieldE0 = true;
+ CActMsg actMsg("Arm Bomb");
+ actMsg.execute("EndExplodeShip");
+ }
+
+ return true;
+}
+
bool CBomb::EnterRoomMsg(CEnterRoomMsg *msg) {
- _fieldE8 = 12;
+ _fieldE8 = 17;
_fieldEC = 9;
_fieldF0 = 0;
_startingTicks = g_vm->_events->getTicksCount();
return true;
}
+bool CBomb::ActMsg(CActMsg *msg) {
+ if (msg->_action == "Hit") {
+ playSound("z#63.wav");
+ stopSound(_soundHandle);
+
+ if (_fieldEC < 17)
+ ++_fieldEC;
+
+ CString name;
+ switch (_fieldEC) {
+ case 10:
+ name = "z#388.wav";
+ break;
+ case 11:
+ name = "z#387.wav";
+ break;
+ case 12:
+ name = "z#386.wav";
+ break;
+ case 13:
+ name = "z#385.wav";
+ break;
+ case 14:
+ name = "z#384.wav";
+ break;
+ case 15:
+ name = "z#383.wav";
+ break;
+ case 16:
+ name = "z#382.wav";
+ break;
+ default:
+ name = "z#381.wav";
+ break;
+ }
+
+ _soundHandle = queueSound(name, _soundHandle, _volume);
+ _countdown = 999;
+ }
+
+ return true;
+}
+
+bool CBomb::TurnOn(CTurnOn *msg) {
+ if (!_fieldE0) {
+ _soundHandle = playSound("z#389.wav", _volume);
+ _fieldE0 = true;
+
+ CActMsg actMsg("Arm Bomb");
+ actMsg.execute("EndExplodeShip");
+ addTimer(0);
+ }
+
+ changeView("Titania.Node 8.W", "");
+ CActMsg actMsg("Titania.Node 8.N");
+ actMsg.execute("BombNav");
+ actMsg.execute("EnterBombRoom");
+
+ return true;
+}
+
+bool CBomb::TimerMsg(CTimerMsg *msg) {
+ if (msg->_action == "Disarmed") {
+ stopSound(_soundHandle);
+ playSound("z#364.wav", _volume);
+
+ CActMsg actMsg1("Disarm Bomb");
+ actMsg1.execute("EndExplodeShip");
+ _fieldE0 = false;
+ CActMsg actMsg2("Titania.Node 5.N");
+ actMsg2.execute("BombNav");
+ actMsg2.execute("EnterBombNav");
+
+ changeView("Titania.Node 8.W", "");
+ changeView("Titania.Node 13.N", "");
+ unlockMouse();
+ }
+
+ if (compareRoomNameTo("Titania")) {
+ if (msg->_actionVal == 1 && getRandomNumber(9) == 0) {
+ if (!_fieldE0)
+ return true;
+
+ CParrotSpeakMsg speakMsg("Bomb", "BombCountdown");
+ speakMsg.execute("PerchedParrot");
+ }
+
+ if (_fieldE0) {
+ if (isSoundActive(_soundHandle)) {
+ if (msg->_actionVal == 0) {
+ addTimer(1, 1000, 0);
+ } else {
+ _soundHandle = 0;
+ int section = _countdown / 100;
+ int index = _countdown % 100;
+
+ if (_countdown >= 100) {
+ CString name1 = index ? WAVE_NAMES2[section] :
+ WAVE_NAMES1[section];
+ playSound(name1, _volume);
+ }
+
+ CString name2 = WAVE_NAMES3[index];
+ if (_countdown == 10) {
+ name2 = "z#229.wav";
+ _countdown = 998;
+ }
+
+ if (_soundHandle > 0) {
+ _soundHandle = queueSound(name2, _soundHandle, _volume);
+ } else {
+ _soundHandle = playSound(name2, _volume);
+ }
+
+ --_countdown;
+ addTimer(0, 1000, 0);
+ }
+ } else {
+ addTimer(0, 100, 0);
+ }
+ }
+ } else {
+ if (_fieldE0) {
+ --_countdown;
+ addTimer(6000);
+
+ if (_countdown < 11)
+ _countdown = getRandomNumber(900) + 50;
+ }
+ }
+
+ return true;
+}
+
+bool CBomb::TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg) {
+ if (msg->_stateNum == 10)
+ msg->_stateVal = _fieldE0;
+
+ return true;
+}
+
+bool CBomb::SetFrameMsg(CSetFrameMsg *msg) {
+ _volume = msg->_frameNumber;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/bomb.h b/engines/titanic/game/bomb.h
index ab4df16db0..f78c42cff0 100644
--- a/engines/titanic/game/bomb.h
+++ b/engines/titanic/game/bomb.h
@@ -29,18 +29,27 @@
namespace Titanic {
class CBomb : public CBackground {
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
bool EnterRoomMsg(CEnterRoomMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool TurnOn(CTurnOn *msg);
+ bool TimerMsg(CTimerMsg *msg);
+ bool TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg);
+ bool SetFrameMsg(CSetFrameMsg *msg);
+ DECLARE_MESSAGE_MAP;
private:
int _fieldE0;
int _fieldE4;
int _fieldE8;
int _fieldEC;
int _fieldF0;
- int _fieldF4;
- int _fieldF8;
+ int _countdown;
+ int _soundHandle;
int _fieldFC;
int _startingTicks;
- int _field104;
+ int _volume;
public:
CLASSDEF;
CBomb();
diff --git a/engines/titanic/game/bottom_of_well_monitor.cpp b/engines/titanic/game/bottom_of_well_monitor.cpp
index beb2a80ce9..373fe4cbdc 100644
--- a/engines/titanic/game/bottom_of_well_monitor.cpp
+++ b/engines/titanic/game/bottom_of_well_monitor.cpp
@@ -24,6 +24,13 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBottomOfWellMonitor, CGameObject)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(LeaveViewMsg)
+END_MESSAGE_MAP()
+
int CBottomOfWellMonitor::_v1;
int CBottomOfWellMonitor::_v2;
@@ -31,7 +38,7 @@ void CBottomOfWellMonitor::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_v1, indent);
file->writeNumberLine(_v2, indent);
- file->writeNumberLine(_value, indent);
+ file->writeNumberLine(_flag, indent);
CGameObject::save(file, indent);
}
@@ -39,8 +46,69 @@ void CBottomOfWellMonitor::load(SimpleFile *file) {
file->readNumber();
_v1 = file->readNumber();
_v2 = file->readNumber();
- _value = file->readNumber();
+ _flag = file->readNumber();
CGameObject::load(file);
}
+bool CBottomOfWellMonitor::ActMsg(CActMsg *msg) {
+ if (msg->_action == "TelevisionTaken") {
+ _v1 = 0;
+ _cursorId = CURSOR_ARROW;
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("CrushedTV2NE");
+ visibleMsg.execute("CrushedTV4SW");
+ _cursorId = CURSOR_ARROW;
+ } else if (msg->_action == "LiftbotHeadTaken") {
+ _v2 = 0;
+ _cursorId = CURSOR_ARROW;
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("LiftbotHead2NE");
+ visibleMsg.execute("LiftbotHead4SW");
+ _cursorId = CURSOR_ARROW;
+ } else if (msg->_action == "LiftbotHeadTaken") {
+ _v2 = 1;
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("CrushedTV2NE");
+ visibleMsg.execute("CrushedTV4SW");
+ _cursorId = CURSOR_MOVE_DOWN1;
+ }
+
+ return true;
+}
+
+bool CBottomOfWellMonitor::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (!compareTo("BOWTelevisionMonitor")) {
+ if (_v2)
+ changeView("BottomOfWell.Node 8.N", "");
+ } else {
+ if (_v1)
+ changeView("BottomOfWell.Node 7.N", "");
+ }
+
+ return true;
+}
+
+bool CBottomOfWellMonitor::EnterViewMsg(CEnterViewMsg *msg) {
+ if (_flag) {
+ if (!compareTo("BOWTelevisionMonitor")) {
+ if (_v2) {
+ changeView("BottomOfWell.Node 8.N", "");
+ _flag = false;
+ }
+ } else {
+ if (_v1) {
+ changeView("BottomOfWell.Node 7.N", "");
+ _flag = false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CBottomOfWellMonitor::LeaveViewMsg(CLeaveViewMsg *msg) {
+ _flag = true;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/bottom_of_well_monitor.h b/engines/titanic/game/bottom_of_well_monitor.h
index 65424aad70..be9ae2c093 100644
--- a/engines/titanic/game/bottom_of_well_monitor.h
+++ b/engines/titanic/game/bottom_of_well_monitor.h
@@ -28,12 +28,17 @@
namespace Titanic {
class CBottomOfWellMonitor : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
public:
static int _v1, _v2;
- int _value;
+ bool _flag;
public:
CLASSDEF;
- CBottomOfWellMonitor() : _value(1) {}
+ CBottomOfWellMonitor() : _flag(true) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/bowl_unlocker.cpp b/engines/titanic/game/bowl_unlocker.cpp
index c3c501dbd6..c4adac34f2 100644
--- a/engines/titanic/game/bowl_unlocker.cpp
+++ b/engines/titanic/game/bowl_unlocker.cpp
@@ -21,19 +21,58 @@
*/
#include "titanic/game/bowl_unlocker.h"
+#include "titanic/core/room_item.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBowlUnlocker, CGameObject)
+ ON_MESSAGE(NutPuzzleMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LeaveViewMsg)
+END_MESSAGE_MAP()
+
void CBowlUnlocker::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_value, indent);
+ file->writeNumberLine(_bowlUnlocked, indent);
CGameObject::save(file, indent);
}
void CBowlUnlocker::load(SimpleFile *file) {
file->readNumber();
- _value = file->readNumber();
+ _bowlUnlocked = file->readNumber();
CGameObject::load(file);
}
+bool CBowlUnlocker::NutPuzzleMsg(CNutPuzzleMsg *msg) {
+ if (msg->_value == "UnlockBowl") {
+ setVisible(true);
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ }
+
+ return true;
+}
+
+bool CBowlUnlocker::MovieEndMsg(CMovieEndMsg *msg) {
+ setVisible(false);
+ _bowlUnlocked = true;
+
+ CNutPuzzleMsg puzzleMsg("BowlUnlocked");
+ puzzleMsg.execute(getRoom(), nullptr, MSGFLAG_SCAN);
+
+ playSound("z#47.wav");
+ return true;
+}
+
+bool CBowlUnlocker::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_bowlUnlocked)
+ msg->execute("Ear1");
+ return true;
+}
+
+bool CBowlUnlocker::LeaveViewMsg(CLeaveViewMsg *msg) {
+ _bowlUnlocked = false;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/bowl_unlocker.h b/engines/titanic/game/bowl_unlocker.h
index 2559ac2c52..b940661904 100644
--- a/engines/titanic/game/bowl_unlocker.h
+++ b/engines/titanic/game/bowl_unlocker.h
@@ -28,11 +28,16 @@
namespace Titanic {
class CBowlUnlocker : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool NutPuzzleMsg(CNutPuzzleMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
public:
- int _value;
+ bool _bowlUnlocked;
public:
CLASSDEF;
- CBowlUnlocker() : CGameObject(), _value(0) {}
+ CBowlUnlocker() : CGameObject(), _bowlUnlocked(false) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/brain_slot.cpp b/engines/titanic/game/brain_slot.cpp
index f1963142ac..57521ead88 100644
--- a/engines/titanic/game/brain_slot.cpp
+++ b/engines/titanic/game/brain_slot.cpp
@@ -21,18 +21,27 @@
*/
#include "titanic/game/brain_slot.h"
+#include "titanic/core/project_item.h"
namespace Titanic {
-int CBrainSlot::_v1;
-int CBrainSlot::_v2;
+BEGIN_MESSAGE_MAP(CBrainSlot, CGameObject)
+ ON_MESSAGE(SetFrameMsg)
+ ON_MESSAGE(AddHeadPieceMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MouseDragStartMsg)
+END_MESSAGE_MAP()
+
+bool CBrainSlot::_added;
+bool CBrainSlot::_woken;
void CBrainSlot::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_value1, indent);
- file->writeQuotedLine(_value2, indent);
- file->writeNumberLine(_v1, indent);
- file->writeNumberLine(_v2, indent);
+ file->writeQuotedLine(_target, indent);
+ file->writeNumberLine(_added, indent);
+ file->writeNumberLine(_woken, indent);
CGameObject::save(file, indent);
}
@@ -40,11 +49,101 @@ void CBrainSlot::save(SimpleFile *file, int indent) {
void CBrainSlot::load(SimpleFile *file) {
file->readNumber();
_value1 = file->readNumber();
- _value2 = file->readString();
- _v1 = file->readNumber();
- _v2 = file->readNumber();
+ _target = file->readString();
+ _added = file->readNumber();
+ _woken = file->readNumber();
CGameObject::load(file);
}
+bool CBrainSlot::SetFrameMsg(CSetFrameMsg *msg) {
+ loadFrame(msg->_frameNumber);
+ _value1 = 1;
+ return true;
+}
+
+bool CBrainSlot::AddHeadPieceMsg(CAddHeadPieceMsg *msg) {
+ _added = true;
+ _cursorId = CURSOR_HAND;
+ CAddHeadPieceMsg addMsg("NULL");
+
+ if (isEquals("AuditoryCentreSlot")) {
+ if (msg->_value == "AuditoryCentre")
+ addMsg._value = "AuditoryCentre";
+ } else if (isEquals("SpeechCentreSlot")) {
+ if (msg->_value == "SpeechCentre")
+ addMsg._value = "SpeechCentre";
+ } else if (isEquals("OlfactoryCentreSlot")) {
+ if (msg->_value == "OlfactoryCentre")
+ addMsg._value = "OlfactoryCentre";
+ } else if (isEquals("VisionCentreSlot")) {
+ if (msg->_value == "VisionCentre")
+ addMsg._value = "VisionCentre";
+ } else if (isEquals("CentralCoreSlot")) {
+ if (msg->_value == "CentralCore")
+ addMsg._value = "CentralCore";
+ }
+
+ if (addMsg._value != "NULL")
+ addMsg.execute("TitaniaControl");
+
+ if (addMsg._value == "OlfactoryCentre")
+ loadFrame(2);
+ else if (addMsg._value == "AuditoryCentre")
+ loadFrame(1);
+ else if (addMsg._value == "SpeechCentre")
+ loadFrame(3);
+ else if (addMsg._value == "VisionCentre")
+ loadFrame(4);
+ else if (addMsg._value == "CentralCore") {
+ CActMsg actMsg("Insert Central Core");
+ actMsg.execute("CentralCoreSlot");
+ }
+
+ _target = msg->_value;
+ _value1 = 1;
+ return true;
+}
+
+bool CBrainSlot::EnterViewMsg(CEnterViewMsg *msg) {
+ if (getName() == "CentralCoreSlot")
+ loadFrame(21);
+ if (_woken)
+ _cursorId = CURSOR_ARROW;
+
+ return true;
+}
+
+bool CBrainSlot::ActMsg(CActMsg *msg) {
+ if (msg->_action == "Insert Central Core")
+ playMovie(0, 21, 0);
+ else if (msg->_action == "Woken")
+ _woken = true;
+
+ return true;
+}
+
+bool CBrainSlot::MouseDragStartMsg(CMouseDragStartMsg *msg) {
+ if (!_value1 || _woken || !checkPoint(msg->_mousePos, false, true))
+ return false;
+
+ _cursorId = CURSOR_ARROW;
+ CVisibleMsg visibleMsg(true);
+ visibleMsg.execute(_target);
+ CTakeHeadPieceMsg takeMsg(_target);
+ takeMsg.execute("TitaniaControl");
+
+ loadFrame(isEquals("CentralCoreSlot") ? 21 : 0);
+ _value1 = 0;
+
+ CPassOnDragStartMsg passMsg;
+ passMsg._mousePos = msg->_mousePos;
+ passMsg.execute(_target);
+
+ msg->_dragItem = getRoot()->findByName(_target);
+ _added = false;
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/brain_slot.h b/engines/titanic/game/brain_slot.h
index 94b6d7f227..fce9ab02c7 100644
--- a/engines/titanic/game/brain_slot.h
+++ b/engines/titanic/game/brain_slot.h
@@ -28,11 +28,17 @@
namespace Titanic {
class CBrainSlot : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool SetFrameMsg(CSetFrameMsg *msg);
+ bool AddHeadPieceMsg(CAddHeadPieceMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool MouseDragStartMsg(CMouseDragStartMsg *msg);
public:
- static int _v1, _v2;
+ static bool _added, _woken;
public:
int _value1;
- CString _value2;
+ CString _target;
public:
CLASSDEF;
CBrainSlot() : CGameObject(), _value1(0) {}
diff --git a/engines/titanic/game/bridge_door.cpp b/engines/titanic/game/bridge_door.cpp
index 57cdbd23ad..bfa30fd650 100644
--- a/engines/titanic/game/bridge_door.cpp
+++ b/engines/titanic/game/bridge_door.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBridgeDoor, CGameObject)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(StatusChangeMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CBridgeDoor::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CGameObject::save(file, indent);
@@ -34,4 +40,23 @@ void CBridgeDoor::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CBridgeDoor::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ setVisible(true);
+ playMovie(0, 6, 0);
+ changeView("Titania.Node 12.N");
+
+ return true;
+}
+
+bool CBridgeDoor::StatusChangeMsg(CStatusChangeMsg *msg) {
+ setVisible(true);
+ playMovie(7, 0, MOVIE_NOTIFY_OBJECT);
+ return true;
+}
+
+bool CBridgeDoor::MovieEndMsg(CMovieEndMsg *msg) {
+ setVisible(false);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/bridge_door.h b/engines/titanic/game/bridge_door.h
index c1872a29be..010a8b8bc0 100644
--- a/engines/titanic/game/bridge_door.h
+++ b/engines/titanic/game/bridge_door.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CBridgeDoor : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/bridge_view.cpp b/engines/titanic/game/bridge_view.cpp
index 9854969494..466480a64c 100644
--- a/engines/titanic/game/bridge_view.cpp
+++ b/engines/titanic/game/bridge_view.cpp
@@ -24,16 +24,92 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBridgeView, CBackground)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CBridgeView::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_fieldE0, indent);
+ file->writeNumberLine(_mode, indent);
CBackground::save(file, indent);
}
void CBridgeView::load(SimpleFile *file) {
file->readNumber();
- _fieldE0 = file->readNumber();
+ _mode = file->readNumber();
CBackground::load(file);
}
+bool CBridgeView::ActMsg(CActMsg *msg) {
+ CTurnOn onMsg;
+ CSetVolumeMsg volumeMsg;
+ volumeMsg._secondsTransition = 1;
+
+ if (msg->_action == "End") {
+ _mode = 4;
+ petLockInput();
+ petHide();
+ setVisible(true);
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ } else if (msg->_action == "Go") {
+ _mode = 1;
+ setVisible(true);
+ volumeMsg._volume = 100;
+ volumeMsg.execute("EngineSounds");
+ onMsg.execute("EngineSounds");
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ } else {
+ volumeMsg._volume = 50;
+ volumeMsg.execute("EngineSounds");
+ onMsg.execute("EngineSounds");
+
+ if (msg->_action == "Cruise") {
+ _mode = 2;
+ setVisible(true);
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ } else if (msg->_action == "GoENd") {
+ _mode = 3;
+ setVisible(true);
+ CChangeMusicMsg musicMsg;
+ musicMsg._flags = 1;
+ musicMsg.execute("BridgeAutoMusicPlayer");
+ playSound("a#42.wav");
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ }
+ }
+
+ return true;
+}
+
+bool CBridgeView::MovieEndMsg(CMovieEndMsg *msg) {
+ CTurnOff offMsg;
+ offMsg.execute("EngineSounds");
+
+ switch (_mode) {
+ case 0:
+ case 1:
+ setVisible(false);
+ dec54();
+ break;
+
+ case 2: {
+ setVisible(false);
+ CActMsg actMsg("End");
+ actMsg.execute("HomeSequence");
+ break;
+ }
+
+ case 3:
+ setVisible(false);
+ changeView("TheEnd.Node 3.N");
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/bridge_view.h b/engines/titanic/game/bridge_view.h
index d7c7c35aa9..45cfa3f4c8 100644
--- a/engines/titanic/game/bridge_view.h
+++ b/engines/titanic/game/bridge_view.h
@@ -28,11 +28,14 @@
namespace Titanic {
class CBridgeView : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
- int _fieldE0;
+ int _mode;
public:
CLASSDEF;
- CBridgeView() : CBackground(), _fieldE0(0) {}
+ CBridgeView() : CBackground(), _mode(0) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/broken_pell_base.cpp b/engines/titanic/game/broken_pell_base.cpp
index 59e2b9bca1..02c2d873ac 100644
--- a/engines/titanic/game/broken_pell_base.cpp
+++ b/engines/titanic/game/broken_pell_base.cpp
@@ -26,7 +26,7 @@ namespace Titanic {
EMPTY_MESSAGE_MAP(CBrokenPellBase, CBackground);
-int CBrokenPellBase::_v1;
+bool CBrokenPellBase::_v1;
int CBrokenPellBase::_v2;
void CBrokenPellBase::save(SimpleFile *file, int indent) {
diff --git a/engines/titanic/game/broken_pell_base.h b/engines/titanic/game/broken_pell_base.h
index f63cd0112b..4ca7eddd20 100644
--- a/engines/titanic/game/broken_pell_base.h
+++ b/engines/titanic/game/broken_pell_base.h
@@ -29,8 +29,8 @@ namespace Titanic {
class CBrokenPellBase : public CBackground {
DECLARE_MESSAGE_MAP;
-private:
- static int _v1;
+protected:
+ static bool _v1;
static int _v2;
int _fieldE0;
diff --git a/engines/titanic/game/broken_pellerator.cpp b/engines/titanic/game/broken_pellerator.cpp
index d3b204b1e5..8fb7244b7e 100644
--- a/engines/titanic/game/broken_pellerator.cpp
+++ b/engines/titanic/game/broken_pellerator.cpp
@@ -21,9 +21,17 @@
*/
#include "titanic/game/broken_pellerator.h"
+#include "titanic/core/view_item.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBrokenPellerator, CBrokenPellBase)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CBrokenPellerator::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeQuotedLine(_string2, indent);
@@ -44,4 +52,103 @@ void CBrokenPellerator::load(SimpleFile *file) {
CBrokenPellBase::load(file);
}
+bool CBrokenPellerator::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_v1) {
+ changeView(_v2 ? _string5 : _string4);
+ } else {
+ if (_v2) {
+ playMovie(28, 43, 0);
+ } else {
+ playMovie(0, 14, MOVIE_NOTIFY_OBJECT);
+ }
+
+ _v1 = true;
+ }
+
+ return true;
+}
+
+bool CBrokenPellerator::LeaveViewMsg(CLeaveViewMsg *msg) {
+ CString name = msg->_newView->getNodeViewName();
+ if (name == "Node 3.S" || name == "Node 3.N") {
+ _v1 = false;
+ loadFrame(0);
+ }
+
+ return true;
+}
+
+bool CBrokenPellerator::ActMsg(CActMsg *msg) {
+ if (msg->_action == "PlayerGetsHose") {
+ _v2 = 1;
+ loadFrame(43);
+
+ CStatusChangeMsg statusMsg;
+ statusMsg.execute("PickupHose");
+ } else {
+ _fieldE0 = 0;
+ bool closeFlag = msg->_action == "Close";
+ if (msg->_action == "CloseLeft") {
+ closeFlag = true;
+ _fieldE0 = 1;
+ }
+ if (msg->_action == "CloseRight") {
+ closeFlag = true;
+ _fieldE0 = 2;
+ }
+
+ if (closeFlag) {
+ if (_v1) {
+ _v1 = false;
+ if (_v2)
+ playMovie(43, 57, MOVIE_NOTIFY_OBJECT);
+ else
+ playMovie(14, 28, MOVIE_NOTIFY_OBJECT);
+ } else {
+ switch (_fieldE0) {
+ case 1:
+ changeView(_string2);
+ break;
+ case 2:
+ changeView(_string3);
+ break;
+ default:
+ break;
+ }
+
+ _fieldE0 = 0;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CBrokenPellerator::MovieEndMsg(CMovieEndMsg *msg) {
+ if (msg->_endFrame == 14) {
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = 1;
+ statusMsg.execute("PickUpHose");
+ }
+
+ if (msg->_endFrame == 28) {
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = 0;
+ statusMsg.execute("PickUpHose");
+ }
+
+ switch (_fieldE0) {
+ case 1:
+ changeView(_string2);
+ break;
+ case 2:
+ changeView(_string3);
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/broken_pellerator.h b/engines/titanic/game/broken_pellerator.h
index 6fbde91053..3b8c3ba587 100644
--- a/engines/titanic/game/broken_pellerator.h
+++ b/engines/titanic/game/broken_pellerator.h
@@ -28,6 +28,11 @@
namespace Titanic {
class CBrokenPellerator : public CBrokenPellBase {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
private:
CString _string2;
CString _string3;
diff --git a/engines/titanic/game/broken_pellerator_froz.cpp b/engines/titanic/game/broken_pellerator_froz.cpp
index 4b21ea93d0..690ab76820 100644
--- a/engines/titanic/game/broken_pellerator_froz.cpp
+++ b/engines/titanic/game/broken_pellerator_froz.cpp
@@ -21,9 +21,17 @@
*/
#include "titanic/game/broken_pellerator_froz.h"
+#include "titanic/core/view_item.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBrokenPelleratorFroz, CBrokenPellBase)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CBrokenPelleratorFroz::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeQuotedLine(_string2, indent);
@@ -44,4 +52,99 @@ void CBrokenPelleratorFroz::load(SimpleFile *file) {
CBrokenPellBase::load(file);
}
+bool CBrokenPelleratorFroz::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_v1) {
+ changeView(_v2 ? _string5 : _string4);
+ } else {
+ _v1 = true;
+ if (_v2) {
+ playMovie(0, 13, 0);
+ } else {
+ playMovie(43, 55, MOVIE_NOTIFY_OBJECT);
+ }
+ }
+
+ return true;
+}
+
+bool CBrokenPelleratorFroz::LeaveViewMsg(CLeaveViewMsg *msg) {
+ CString name = msg->_newView->getNodeViewName();
+
+ if (name == "Node 3.S" || name == "Node 3.E") {
+ _v1 = false;
+ loadFrame(0);
+ }
+
+ return true;
+}
+
+bool CBrokenPelleratorFroz::ActMsg(CActMsg *msg) {
+ if (msg->_action == "PlayerGetsHose") {
+ _v2 = 1;
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = 0;
+ statusMsg.execute("FPickUpHose");
+ } else {
+ _fieldE0 = 0;
+ bool closeFlag = msg->_action == "Close";
+ if (msg->_action == "CloseLeft") {
+ closeFlag = true;
+ _fieldE0 = 1;
+ }
+ if (msg->_action == "CloseRight") {
+ closeFlag = true;
+ _fieldE0 = 2;
+ }
+
+ if (closeFlag) {
+ if (_v1) {
+ _v1 = false;
+ if (_v2)
+ playMovie(29, 42, MOVIE_NOTIFY_OBJECT);
+ else
+ playMovie(72, 84, MOVIE_NOTIFY_OBJECT);
+ } else {
+ switch (_fieldE0) {
+ case 1:
+ changeView(_string2);
+ break;
+ case 2:
+ changeView(_string3);
+ break;
+ default:
+ break;
+ }
+
+ _fieldE0 = 0;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CBrokenPelleratorFroz::MovieEndMsg(CMovieEndMsg *msg) {
+ if (msg->_endFrame == 55) {
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = 1;
+ statusMsg.execute("FPickUpHose");
+ }
+
+ if (msg->_endFrame == 84) {
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = 0;
+ statusMsg.execute("FPickUpHose");
+ }
+
+ if (_fieldE0 == 1) {
+ changeView(_string2);
+ _fieldE0 = 0;
+ } else if (_fieldE0 == 2) {
+ changeView(_string3);
+ _fieldE0 = 0;
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/broken_pellerator_froz.h b/engines/titanic/game/broken_pellerator_froz.h
index 1df6d2d0b2..ccdae6ffa8 100644
--- a/engines/titanic/game/broken_pellerator_froz.h
+++ b/engines/titanic/game/broken_pellerator_froz.h
@@ -28,6 +28,11 @@
namespace Titanic {
class CBrokenPelleratorFroz : public CBrokenPellBase {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
private:
CString _string2;
CString _string3;
diff --git a/engines/titanic/game/cage.cpp b/engines/titanic/game/cage.cpp
index 7fbc052278..bbac384cea 100644
--- a/engines/titanic/game/cage.cpp
+++ b/engines/titanic/game/cage.cpp
@@ -21,16 +21,25 @@
*/
#include "titanic/game/cage.h"
+#include "titanic/npcs/parrot.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCage, CBackground)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(PreEnterViewMsg)
+ ON_MESSAGE(MouseMoveMsg)
+END_MESSAGE_MAP()
+
int CCage::_v1;
-int CCage::_v2;
+bool CCage::_open;
void CCage::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_v1, indent);
- file->writeNumberLine(_v2, indent);
+ file->writeNumberLine(_open, indent);
CBackground::save(file, indent);
}
@@ -38,9 +47,64 @@ void CCage::save(SimpleFile *file, int indent) {
void CCage::load(SimpleFile *file) {
file->readNumber();
_v1 = file->readNumber();
- _v2 = file->readNumber();
+ _open = file->readNumber();
CBackground::load(file);
}
+bool CCage::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (CParrot::_v4 && !CParrot::_v5) {
+ CActMsg actMsg(_open ? "Open" : "Shut");
+ actMsg.execute(this);
+ }
+
+ return true;
+}
+
+bool CCage::ActMsg(CActMsg *msg) {
+ if (msg->_action == "Shut") {
+ if (!_open) {
+ playClip("Shut", MOVIE_STOP_PREVIOUS | MOVIE_NOTIFY_OBJECT);
+ disableMouse();
+ }
+ } else if (msg->_action == "Open") {
+ if (_open) {
+ playClip("Open", MOVIE_STOP_PREVIOUS | MOVIE_NOTIFY_OBJECT);
+ disableMouse();
+ }
+ } else if (msg->_action == "CoreReplaced") {
+ CActMsg actMsg("Shut");
+ actMsg.execute(this);
+ } else if (msg->_action == "OpenNow") {
+ loadFrame(0);
+ _open = false;
+ }
+
+ return true;
+}
+
+bool CCage::MovieEndMsg(CMovieEndMsg *msg) {
+ enableMouse();
+ _open = clipExistsByEnd("Shut", msg->_endFrame);
+
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = _open ? 1 : (CParrot::_v4 == 0 ? 1 : 0);
+ statusMsg.execute("PerchCoreHolder");
+
+ return true;
+}
+
+bool CCage::PreEnterViewMsg(CPreEnterViewMsg *msg) {
+ loadSurface();
+ _open = CParrot::_v4 != 0;
+ loadFrame(_open ? 8 : 0);
+
+ return true;
+}
+
+bool CCage::MouseMoveMsg(CMouseMoveMsg *msg) {
+ _cursorId = CParrot::_v4 && !CParrot::_v5 ? CURSOR_ACTIVATE : CURSOR_ARROW;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/cage.h b/engines/titanic/game/cage.h
index bbce978489..48b1b46ab7 100644
--- a/engines/titanic/game/cage.h
+++ b/engines/titanic/game/cage.h
@@ -28,9 +28,15 @@
namespace Titanic {
class CCage : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool PreEnterViewMsg(CPreEnterViewMsg *msg);
+ bool MouseMoveMsg(CMouseMoveMsg *msg);
public:
static int _v1;
- static int _v2;
+ static bool _open;
public:
CLASSDEF;
diff --git a/engines/titanic/game/captains_wheel.cpp b/engines/titanic/game/captains_wheel.cpp
index c84c9194ce..79908b561d 100644
--- a/engines/titanic/game/captains_wheel.cpp
+++ b/engines/titanic/game/captains_wheel.cpp
@@ -24,6 +24,15 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCaptainsWheel, CBackground)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
CCaptainsWheel::CCaptainsWheel() : CBackground(),
_fieldE0(0), _fieldE4(0), _fieldE8(0), _fieldEC(0),
_fieldF0(0), _fieldF4(0) {
@@ -53,4 +62,148 @@ void CCaptainsWheel::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CCaptainsWheel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_fieldE0) {
+ _fieldE0 = false;
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ playMovie(162, 168, 0);
+ } else {
+ playMovie(0, 8, MOVIE_NOTIFY_OBJECT);
+ }
+
+ return true;
+}
+
+bool CCaptainsWheel::LeaveViewMsg(CLeaveViewMsg *msg) {
+ if (_fieldE0) {
+ _fieldE0 = false;
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ playMovie(162, 168, MOVIE_GAMESTATE);
+ }
+
+ return true;
+}
+
+bool CCaptainsWheel::ActMsg(CActMsg *msg) {
+ if (msg->_action == "Spin") {
+ if (_fieldE0) {
+ CTurnOn onMsg;
+ onMsg.execute("RatchetySound");
+ playMovie(8, 142, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ }
+ } else if (msg->_action == "Honk") {
+ if (_fieldE0) {
+ playMovie(150, 160, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ }
+ } else if (msg->_action == "Go") {
+ if (!_fieldE0) {
+ inc54();
+ _fieldE0 = false;
+ _fieldE4 = 1;
+
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ playMovie(162, 168, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ }
+ } else if (msg->_action == "Cruise") {
+ if (_fieldE0) {
+ inc54();
+ _fieldE0 = false;
+ _fieldE4 = 2;
+
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ playMovie(162, 168, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ }
+ } else if (msg->_action == "SetDestin") {
+ playSound("a#44.wav");
+ CSetVolumeMsg volumeMsg;
+ volumeMsg._volume = 25;
+ volumeMsg.execute("EngineSounds");
+ CTurnOn onMsg;
+ onMsg.execute("EngineSounds");
+ _fieldF0 = 1;
+ } else if (msg->_action == "ClearDestin") {
+ _fieldF0 = 0;
+ }
+
+ return true;
+}
+
+bool CCaptainsWheel::TurnOff(CTurnOff *msg) {
+ CSignalObject signalMsg;
+ signalMsg._numValue = 0;
+
+ static const char *const NAMES[8] = {
+ "WheelSpin", "SeagullHorn", "WheelStopButt", "StopHotSpot",
+ "WheelCruiseButt", "CruiseHotSpot", "WheelGoButt","GoHotSpot"
+ };
+ for (int idx = 0; idx < 8; ++idx)
+ signalMsg.execute(NAMES[idx]);
+
+ return true;
+}
+
+bool CCaptainsWheel::TurnOn(CTurnOn *msg) {
+ CSignalObject signalMsg;
+ signalMsg._numValue = 1;
+ signalMsg.execute("WheelSpin");
+ signalMsg.execute("SeagullHorn");
+
+ if (_fieldE0) {
+ signalMsg.execute("WheelStopButt");
+ signalMsg.execute("StopHotSpot");
+ }
+
+ if (_fieldEC) {
+ signalMsg.execute("WheelCruiseButt");
+ signalMsg.execute("CruiseHotSpot");
+ }
+
+ if (_fieldF0) {
+ signalMsg.execute("WheelGoButt");
+ signalMsg.execute("GoHotSpot");
+ }
+
+ return true;
+}
+
+bool CCaptainsWheel::MovieEndMsg(CMovieEndMsg *msg) {
+ if (msg->_endFrame == 8) {
+ _fieldE0 = true;
+ CTurnOn onMsg;
+ onMsg.execute(this);
+ }
+
+ if (msg->_endFrame == 142) {
+ CTurnOff offMsg;
+ offMsg.execute("RatchetySound");
+ }
+
+ if (msg->_endFrame == 168) {
+ switch (_fieldE4) {
+ case 1: {
+ CActMsg actMsg(starFn2() ? "GoEnd" : "Go");
+ actMsg.execute("GoSequence");
+ break;
+ }
+
+ case 2: {
+ CActMsg actMsg("Cruise");
+ actMsg.execute("CruiseSequence");
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ _fieldE4 = 0;
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/captains_wheel.h b/engines/titanic/game/captains_wheel.h
index 549dcbe685..3aca45c21f 100644
--- a/engines/titanic/game/captains_wheel.h
+++ b/engines/titanic/game/captains_wheel.h
@@ -28,6 +28,13 @@
namespace Titanic {
class CCaptainsWheel : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool TurnOn(CTurnOn *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
int _fieldE0;
int _fieldE4;
diff --git a/engines/titanic/game/cell_point_button.cpp b/engines/titanic/game/cell_point_button.cpp
index 18ece09cb0..207dd73543 100644
--- a/engines/titanic/game/cell_point_button.cpp
+++ b/engines/titanic/game/cell_point_button.cpp
@@ -24,12 +24,17 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCellPointButton, CBackground)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(EnterViewMsg)
+END_MESSAGE_MAP()
+
CCellPointButton::CCellPointButton() : CBackground() {
_fieldE0 = 0;
_fieldE4 = 0;
_fieldE8 = 0;
_fieldEC = 0;
- _fieldF0 = 0;
+ _regionNum = 0;
_fieldF4 = 0;
_fieldF8 = 0;
_fieldFC = 0;
@@ -44,7 +49,7 @@ void CCellPointButton::save(SimpleFile *file, int indent) {
file->writeNumberLine(_fieldE4, indent);
file->writeNumberLine(_fieldE8, indent);
file->writeNumberLine(_fieldEC, indent);
- file->writeNumberLine(_fieldF0, indent);
+ file->writeNumberLine(_regionNum, indent);
file->writeNumberLine(_fieldF4, indent);
file->writeNumberLine(_fieldF8, indent);
file->writeNumberLine(_fieldFC, indent);
@@ -52,7 +57,7 @@ void CCellPointButton::save(SimpleFile *file, int indent) {
file->writeNumberLine(_field104, indent);
file->writeNumberLine(_field108, indent);
file->writeQuotedLine(_string3, indent);
- file->writeNumberLine(_field118, indent);
+ file->writeNumberLine(_dialNum, indent);
CBackground::save(file, indent);
}
@@ -63,7 +68,7 @@ void CCellPointButton::load(SimpleFile *file) {
_fieldE4 = file->readNumber();
_fieldE8 = file->readNumber();
_fieldEC = file->readNumber();
- _fieldF0 = file->readNumber();
+ _regionNum = file->readNumber();
_fieldF4 = file->readNumber();
_fieldF8 = file->readNumber();
_fieldFC = file->readNumber();
@@ -71,9 +76,28 @@ void CCellPointButton::load(SimpleFile *file) {
_field104 = file->readNumber();
_field108 = file->readNumber();
_string3 = file->readString();
- _field118 = file->readNumber();
+ _dialNum = file->readNumber();
CBackground::load(file);
}
+bool CCellPointButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (getRandomNumber(2) == 0) {
+ CParrotSpeakMsg speakMsg("Cellpoints", _string3);
+ speakMsg.execute("PerchedParrot");
+ }
+
+ playMovie(0);
+ _regionNum = _regionNum ? 0 : 1;
+ playSound("z#425.wav");
+ talkSetDialRegion(_string3, _dialNum, _regionNum);
+
+ return true;
+}
+
+bool CCellPointButton::EnterViewMsg(CEnterViewMsg *msg) {
+ _regionNum = talkGetDialRegion(_string3, _dialNum);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/cell_point_button.h b/engines/titanic/game/cell_point_button.h
index 6f1fdc3809..33f58cbb83 100644
--- a/engines/titanic/game/cell_point_button.h
+++ b/engines/titanic/game/cell_point_button.h
@@ -28,12 +28,15 @@
namespace Titanic {
class CCellPointButton : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
public:
int _fieldE0;
int _fieldE4;
int _fieldE8;
int _fieldEC;
- int _fieldF0;
+ int _regionNum;
int _fieldF4;
int _fieldF8;
int _fieldFC;
@@ -41,7 +44,7 @@ public:
int _field104;
int _field108;
CString _string3;
- int _field118;
+ int _dialNum;
public:
CLASSDEF;
CCellPointButton();
diff --git a/engines/titanic/game/chev_code.cpp b/engines/titanic/game/chev_code.cpp
index ebc20578b7..07225f0cf8 100644
--- a/engines/titanic/game/chev_code.cpp
+++ b/engines/titanic/game/chev_code.cpp
@@ -24,16 +24,263 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChevCode, CGameObject)
+ ON_MESSAGE(SetChevLiftBits)
+ ON_MESSAGE(SetChevClassBits)
+ ON_MESSAGE(SetChevFloorBits)
+ ON_MESSAGE(SetChevRoomBits)
+ ON_MESSAGE(GetChevLiftNum)
+ ON_MESSAGE(GetChevClassNum)
+ ON_MESSAGE(GetChevFloorNum)
+ ON_MESSAGE(GetChevRoomNum)
+ ON_MESSAGE(CheckChevCode)
+ ON_MESSAGE(GetChevCodeFromRoomNameMsg)
+END_MESSAGE_MAP()
+
void CChevCode::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_value, indent);
+ file->writeNumberLine(_chevCode, indent);
CGameObject::save(file, indent);
}
void CChevCode::load(SimpleFile *file) {
file->readNumber();
- _value = file->readNumber();
+ _chevCode = file->readNumber();
CGameObject::load(file);
}
+bool CChevCode::SetChevLiftBits(CSetChevLiftBits *msg) {
+ _chevCode &= ~0xC0000;
+ if (msg->_liftNum > 0 && msg->_liftNum < 5)
+ _chevCode = ((msg->_liftNum - 1) << 18) | _chevCode;
+
+ return true;
+}
+
+bool CChevCode::SetChevClassBits(CSetChevClassBits *msg) {
+ _chevCode &= ~0x30000;
+ if (msg->_classNum > 0 && msg->_classNum < 4)
+ _chevCode = (msg->_classNum << 16) | msg->_classNum;
+
+ return true;
+}
+
+bool CChevCode::SetChevFloorBits(CSetChevFloorBits *msg) {
+ int section = (msg->_floorNum + 4) / 10;
+ int index = (msg->_floorNum + 4) % 10;
+ _chevCode &= ~0xFF00;
+
+ int val;
+ switch (section) {
+ case 0:
+ val = 144;
+ break;
+ case 1:
+ val = 208;
+ break;
+ case 2:
+ val = 224;
+ break;
+ case 3:
+ val = 240;
+ break;
+ default:
+ break;
+ }
+
+ _chevCode |= ((index + val) << 8);
+ return true;
+}
+
+bool CChevCode::SetChevRoomBits(CSetChevRoomBits *msg) {
+ _chevCode &= ~0xff;
+ if (msg->_roomNum > 0 && msg->_roomNum < 128)
+ _chevCode |= msg->_roomNum * 2;
+
+ return true;
+}
+
+bool CChevCode::GetChevLiftNum(CGetChevLiftNum *msg) {
+ msg->_liftNum = (_chevCode >> 18) & 3 + 1;
+ return true;
+}
+
+bool CChevCode::GetChevClassNum(CGetChevClassNum *msg) {
+ msg->_classNum = (_chevCode >> 16) & 3;
+ return true;
+}
+
+bool CChevCode::GetChevFloorNum(CGetChevFloorNum *msg) {
+ int val1 = (_chevCode >> 8) & 0xF;
+ int val2 = (_chevCode >> 12) & 0xF - 9;
+
+ switch (val2) {
+ case 0:
+ val2 = 0;
+ break;
+ case 4:
+ val2 = 1;
+ break;
+ case 5:
+ val2 = 2;
+ break;
+ case 6:
+ val2 = 3;
+ break;
+ default:
+ val2 = 4;
+ break;
+ }
+
+ msg->_floorNum = (val1 >= 10) ? 0 : val1 * 10;
+ return true;
+}
+
+bool CChevCode::GetChevRoomNum(CGetChevRoomNum *msg) {
+ msg->_roomNum = (_chevCode >> 1) & 0x7F;
+ return true;
+}
+
+bool CChevCode::CheckChevCode(CCheckChevCode *msg) {
+ CGetChevClassNum getClassMsg;
+ CGetChevLiftNum getLiftMsg;
+ CGetChevFloorNum getFloorMsg;
+ CGetChevRoomNum getRoomMsg;
+ CString roomName;
+ int classNum = 0;
+ uint bits;
+
+ if (_chevCode & 1) {
+ switch (_chevCode) {
+ case 0x1D0D9:
+ roomName = "ParrLobby";
+ classNum = 4;
+ break;
+ case 0x196D9:
+ roomName = "FCRestrnt";
+ classNum = 4;
+ break;
+ case 0x39FCB:
+ roomName = "Bridge";
+ classNum = 4;
+ break;
+ case 0x2F86D:
+ roomName = "CrtrsCham";
+ classNum = 4;
+ break;
+ case 0x465FB:
+ roomName = "SculpCham";
+ classNum = 4;
+ break;
+ case 0x3D94B:
+ roomName = "BilgeRoom";
+ classNum = 4;
+ break;
+ case 0x59FAD:
+ roomName = "BoWell";
+ classNum = 4;
+ break;
+ case 0x4D6AF:
+ roomName = "Arboretum";
+ classNum = 4;
+ break;
+ case 0x8A397:
+ roomName = "TitRoom";
+ classNum = 4;
+ break;
+ case 0x79C45:
+ roomName = "PromDeck";
+ classNum = 4;
+ break;
+ case 0xB3D97:
+ roomName = "Bar";
+ classNum = 4;
+ break;
+ case 0xCC971:
+ roomName = "EmbLobby";
+ classNum = 4;
+ break;
+ case 0xF34DB:
+ roomName = "MusicRoom";
+ classNum = 4;
+ break;
+ default:
+ roomName = "BadRoom";
+ classNum = 5;
+ break;
+ }
+
+ bits = classNum == 5 ? 0x3D94B : _chevCode;
+ } else {
+ getFloorMsg.execute(this);
+ getRoomMsg.execute(this);
+ getClassMsg.execute(this);
+ getLiftMsg.execute(this);
+ if (getFloorMsg._floorNum > 37 || getRoomMsg._roomNum > 18)
+ classNum = 5;
+
+ if (classNum == 5) {
+ bits = 0x3D94B;
+ } else {
+ switch (getClassMsg._classNum) {
+ case 1:
+ if (getFloorMsg._floorNum >= 2 && getFloorMsg._floorNum <= 18
+ && getRoomMsg._roomNum >= 1 && getRoomMsg._roomNum <= 3
+ && getLiftMsg._liftNum >= 1 && getLiftMsg._liftNum <= 4)
+ classNum = 1;
+ else
+ classNum = 5;
+ break;
+
+ case 2:
+ if (getFloorMsg._floorNum >= 19 && getFloorMsg._floorNum <= 26
+ && getRoomMsg._roomNum >= 1 && getRoomMsg._roomNum <= 5
+ && getLiftMsg._liftNum >= 1 && getLiftMsg._liftNum <= 4)
+ classNum = 2;
+ else
+ classNum = 5;
+ break;
+
+ case 3:
+ if (getFloorMsg._floorNum >= 27 && getFloorMsg._floorNum <= 37
+ && getRoomMsg._roomNum >= 1 && getRoomMsg._roomNum <= 18
+ && (getLiftMsg._liftNum & 1) == 1
+ && getLiftMsg._liftNum >= 1 && getLiftMsg._liftNum <= 4)
+ classNum = 3;
+ else
+ classNum = 5;
+ break;
+ }
+ }
+ // TODO
+ }
+
+ msg->_classNum = classNum;
+ msg->_chevCode = bits;
+
+ // WORKAROUND: Skipped code from original that was for debugging purposes only
+ return true;
+}
+
+bool CChevCode::GetChevCodeFromRoomNameMsg(CGetChevCodeFromRoomNameMsg *msg) {
+ static const char *const ROOM_NAMES[13] = {
+ "ParrotLobby", "sculptureChamber", "Bar", "EmbLobby", "MusicRoom",
+ "Titania", "BottomOfWell", "Arboretum", "PromenadeDeck",
+ "FCRestrnt", "CrtrsCham", "BilgeRoom", "Bridge"
+ };
+ static const uint CHEV_CODES[13] = {
+ 0x1D0D9, 0x465FB, 0xB3D97, 0xCC971, 0xF34DB, 0x8A397, 0x59FAD,
+ 0x4D6AF, 0x79C45, 0x196D9, 0x2F86D, 0x3D94B, 0x39FCB
+ };
+
+ for (int idx = 0; idx < 13; ++idx) {
+ if (msg->_roomName == ROOM_NAMES[idx]) {
+ msg->_chevCode = CHEV_CODES[idx];
+ break;
+ }
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/chev_code.h b/engines/titanic/game/chev_code.h
index c4552d00a2..4a71b13f9e 100644
--- a/engines/titanic/game/chev_code.h
+++ b/engines/titanic/game/chev_code.h
@@ -28,11 +28,22 @@
namespace Titanic {
class CChevCode : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool SetChevLiftBits(CSetChevLiftBits *msg);
+ bool SetChevClassBits(CSetChevClassBits *msg);
+ bool SetChevFloorBits(CSetChevFloorBits *msg);
+ bool SetChevRoomBits(CSetChevRoomBits *msg);
+ bool GetChevLiftNum(CGetChevLiftNum *msg);
+ bool GetChevClassNum(CGetChevClassNum *msg);
+ bool GetChevFloorNum(CGetChevFloorNum *msg);
+ bool GetChevRoomNum(CGetChevRoomNum *msg);
+ bool CheckChevCode(CCheckChevCode *msg);
+ bool GetChevCodeFromRoomNameMsg(CGetChevCodeFromRoomNameMsg *msg);
public:
- int _value;
+ int _chevCode;
public:
CLASSDEF;
- CChevCode() : CGameObject(), _value(0) {}
+ CChevCode() : CGameObject(), _chevCode(0) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/chev_panel.cpp b/engines/titanic/game/chev_panel.cpp
index 245968e356..c644776bc9 100644
--- a/engines/titanic/game/chev_panel.cpp
+++ b/engines/titanic/game/chev_panel.cpp
@@ -21,25 +21,101 @@
*/
#include "titanic/game/chev_panel.h"
+#include "titanic/game/chev_code.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChevPanel, CGameObject)
+ ON_MESSAGE(MouseDragStartMsg)
+ ON_MESSAGE(MouseDragMoveMsg)
+ ON_MESSAGE(MouseButtonUpMsg)
+ ON_MESSAGE(SetChevPanelBitMsg)
+ ON_MESSAGE(MouseDragEndMsg)
+ ON_MESSAGE(ClearChevPanelBits)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(SetChevPanelButtonsMsg)
+END_MESSAGE_MAP()
+
void CChevPanel::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_fieldBC, indent);
- file->writeNumberLine(_fieldC0, indent);
- file->writeNumberLine(_fieldC4, indent);
+ file->writeNumberLine(_startPos.x, indent);
+ file->writeNumberLine(_startPos.y, indent);
+ file->writeNumberLine(_chevCode, indent);
CGameObject::save(file, indent);
}
void CChevPanel::load(SimpleFile *file) {
file->readNumber();
- _fieldBC = file->readNumber();
- _fieldC0 = file->readNumber();
- _fieldC4 = file->readNumber();
+ _startPos.x = file->readNumber();
+ _startPos.y = file->readNumber();
+ _chevCode = file->readNumber();
CGameObject::load(file);
}
+bool CChevPanel::MouseDragStartMsg(CMouseDragStartMsg *msg) {
+ if (checkStartDragging(msg)) {
+ _startPos = Point(msg->_mousePos.x - _bounds.left,
+ msg->_mousePos.y - _bounds.top);
+ CChildDragStartMsg dragMsg(_startPos);
+ dragMsg.execute(this, nullptr, MSGFLAG_SCAN);
+ }
+
+ return true;
+}
+
+bool CChevPanel::MouseDragMoveMsg(CMouseDragMoveMsg *msg) {
+ CChildDragMoveMsg dragMsg(_startPos);
+ dragMsg.execute(this, nullptr, MSGFLAG_SCAN);
+
+ setPosition(msg->_mousePos - _startPos);
+ return true;
+}
+
+bool CChevPanel::MouseButtonUpMsg(CMouseButtonUpMsg *msg) {
+ CChevCode chevCode;
+ chevCode._chevCode = _chevCode;
+ CCheckChevCode checkCode;
+ checkCode.execute(this);
+ CClearChevPanelBits panelBits;
+ panelBits.execute(this, nullptr, MSGFLAG_SCAN);
+ CSetChevPanelButtonsMsg setMsg;
+ setMsg._chevCode = checkCode._chevCode;
+ setMsg.execute(this);
+
+ return true;
+}
+
+bool CChevPanel::SetChevPanelBitMsg(CSetChevPanelBitMsg *msg) {
+ _chevCode = _chevCode & ~(1 << msg->_value1) | (msg->_value2 << msg->_value1);
+ return true;
+}
+
+bool CChevPanel::MouseDragEndMsg(CMouseDragEndMsg *msg) {
+ setPosition(msg->_mousePos - _startPos);
+ return true;
+}
+
+bool CChevPanel::ClearChevPanelBits(CClearChevPanelBits *msg) {
+ CSetChevPanelButtonsMsg setMsg;
+ setMsg._chevCode = 0;
+ setMsg.execute(this);
+
+ return true;
+}
+
+bool CChevPanel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ return true;
+}
+
+bool CChevPanel::SetChevPanelButtonsMsg(CSetChevPanelButtonsMsg *msg) {
+ _chevCode = msg->_chevCode;
+ CSetChevButtonImageMsg setMsg;
+ setMsg._value2 = 1;
+ setMsg.execute(this, nullptr, MSGFLAG_SCAN);
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/chev_panel.h b/engines/titanic/game/chev_panel.h
index 99b5501ac2..bcfb920221 100644
--- a/engines/titanic/game/chev_panel.h
+++ b/engines/titanic/game/chev_panel.h
@@ -28,13 +28,21 @@
namespace Titanic {
class CChevPanel : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool MouseDragStartMsg(CMouseDragStartMsg *msg);
+ bool MouseDragMoveMsg(CMouseDragMoveMsg *msg);
+ bool MouseButtonUpMsg(CMouseButtonUpMsg *msg);
+ bool SetChevPanelBitMsg(CSetChevPanelBitMsg *msg);
+ bool MouseDragEndMsg(CMouseDragEndMsg *msg);
+ bool ClearChevPanelBits(CClearChevPanelBits *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool SetChevPanelButtonsMsg(CSetChevPanelButtonsMsg *msg);
public:
- int _fieldBC;
- int _fieldC0;
- int _fieldC4;
+ Point _startPos;
+ int _chevCode;
public:
CLASSDEF;
- CChevPanel() : _fieldBC(0), _fieldC0(0), _fieldC4(0) {}
+ CChevPanel() : _chevCode(0) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/chicken_cooler.cpp b/engines/titanic/game/chicken_cooler.cpp
index 29232e10bf..d10405de38 100644
--- a/engines/titanic/game/chicken_cooler.cpp
+++ b/engines/titanic/game/chicken_cooler.cpp
@@ -21,9 +21,15 @@
*/
#include "titanic/game/chicken_cooler.h"
+#include "titanic/carry/chicken.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChickenCooler, CGameObject)
+ ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(EnterViewMsg)
+END_MESSAGE_MAP()
+
void CChickenCooler::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_fieldBC, indent);
@@ -41,7 +47,32 @@ void CChickenCooler::load(SimpleFile *file) {
}
bool CChickenCooler::EnterRoomMsg(CEnterRoomMsg *msg) {
- warning("CChickenCoolor::handlEvent");
+ if (_fieldC0) {
+ CGameObject *obj = getMailManFirstObject();
+ if (obj) {
+ // WORKAROUND: Redundant loop for chicken in originalhere
+ } else {
+ getNextMail(nullptr);
+ if (CChicken::_v1 > _fieldBC)
+ CChicken::_v1 = _fieldBC;
+ }
+ }
+
+ return true;
+}
+
+bool CChickenCooler::EnterViewMsg(CEnterViewMsg *msg) {
+ if (!_fieldC0) {
+ for (CGameObject *obj = getMailManFirstObject(); obj;
+ obj = getNextMail(obj)) {
+ if (obj->isEquals("Chicken"))
+ return true;
+ }
+
+ if (CChicken::_v1 > _fieldBC)
+ CChicken::_v1 = _fieldBC;
+ }
+
return true;
}
diff --git a/engines/titanic/game/chicken_cooler.h b/engines/titanic/game/chicken_cooler.h
index 724727b905..54dba90686 100644
--- a/engines/titanic/game/chicken_cooler.h
+++ b/engines/titanic/game/chicken_cooler.h
@@ -29,7 +29,9 @@
namespace Titanic {
class CChickenCooler : public CGameObject {
+ DECLARE_MESSAGE_MAP;
bool EnterRoomMsg(CEnterRoomMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
private:
int _fieldBC;
int _fieldC0;
diff --git a/engines/titanic/game/chicken_dispensor.cpp b/engines/titanic/game/chicken_dispensor.cpp
index a9bf576765..7fb8fefcda 100644
--- a/engines/titanic/game/chicken_dispensor.cpp
+++ b/engines/titanic/game/chicken_dispensor.cpp
@@ -21,9 +21,21 @@
*/
#include "titanic/game/chicken_dispensor.h"
+#include "titanic/core/project_item.h"
+#include "titanic/pet_control/pet_control.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChickenDispensor, CBackground)
+ ON_MESSAGE(StatusChangeMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(MouseDragStartMsg)
+ ON_MESSAGE(TurnOff)
+END_MESSAGE_MAP()
+
CChickenDispensor::CChickenDispensor() : CBackground(),
_fieldE0(0), _fieldE4(0), _fieldE8(0) {
}
@@ -45,4 +57,133 @@ void CChickenDispensor::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CChickenDispensor::StatusChangeMsg(CStatusChangeMsg *msg) {
+ msg->execute("SGTRestLeverAnimation");
+ int v1 = _fieldE8 ? 0 : _fieldE4;
+ CPetControl *pet = getPetControl();
+ CGameObject *obj;
+
+ for (obj = pet->getFirstObject(); obj; obj = pet->getNextObject(obj)) {
+ if (obj->isEquals("Chicken")) {
+ petDisplayMessage(1, "Chickens are allocated on a one-per-customer basis.");
+ return true;
+ }
+ }
+
+ for (obj = getMailManFirstObject(); obj; obj = getNextMail(obj)) {
+ if (obj->isEquals("Chicken")) {
+ petDisplayMessage(1, "Chickens are allocated on a one-per-customer basis.");
+ return true;
+ }
+ }
+
+ if (v1 == 1 || v1 == 2)
+ _fieldE8 = 1;
+
+ switch (v1) {
+ case 0:
+ petDisplayMessage(1, "Only one piece of chicken per passenger. Thank you.");
+ break;
+ case 1:
+ setVisible(true);
+ if (_fieldE0) {
+ playMovie(0, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("z#400.wav");
+ _fieldE4 = 0;
+ } else {
+ playMovie(12, 16, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ _fieldE8 = 1;
+ _fieldE4 = 0;
+ }
+ break;
+
+ case 2:
+ setVisible(true);
+ if (_fieldE0) {
+ playMovie(0, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("z#400.wav");
+ } else {
+ playMovie(12, 16, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ _fieldE8 = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool CChickenDispensor::MovieEndMsg(CMovieEndMsg *msg) {
+ if (getMovieFrame() == 16) {
+ playSound("b#50.wav", 50);
+ CActMsg actMsg("Dispense Chicken");
+ actMsg.execute("Chicken");
+ } else if (_fieldE8) {
+ _cursorId = CURSOR_ARROW;
+ loadFrame(0);
+ setVisible(false);
+ if (_fieldE4 == 2)
+ _fieldE8 = 0;
+ } else {
+ loadFrame(0);
+ setVisible(false);
+ changeView("SgtLobby.Node 1.N");
+ }
+
+ return true;
+}
+
+bool CChickenDispensor::ActMsg(CActMsg *msg) {
+ if (msg->_action == "EnableObject")
+ _fieldE0 = 0;
+ else if (msg->_action == "DisableObject")
+ _fieldE0 = 1;
+ else if (msg->_action == "IncreaseQuantity")
+ _fieldE4 = 2;
+ else if (msg->_action == "DecreaseQuantity")
+ _fieldE4 = 1;
+
+ return true;
+}
+
+bool CChickenDispensor::LeaveViewMsg(CLeaveViewMsg *msg) {
+ return true;
+}
+
+bool CChickenDispensor::EnterViewMsg(CEnterViewMsg *msg) {
+ playSound("b#51.wav");
+ _fieldE8 = 0;
+ _cursorId = CURSOR_ARROW;
+ return true;
+}
+
+bool CChickenDispensor::MouseDragStartMsg(CMouseDragStartMsg *msg) {
+ if (getMovieFrame() == 16) {
+ setVisible(false);
+ loadFrame(0);
+ _cursorId = CURSOR_ARROW;
+ _fieldE8 = 1;
+
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("Chicken");
+ CPassOnDragStartMsg passMsg(msg->_mousePos, 1);
+ passMsg.execute("Chicken");
+
+ msg->_dragItem = getRoot()->findByName("Chicken");
+ }
+
+ return true;
+}
+
+bool CChickenDispensor::TurnOff(CTurnOff *msg) {
+ if (getMovieFrame() == 16)
+ setVisible(false);
+ playMovie(16, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ _fieldE8 = 0;
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/chicken_dispensor.h b/engines/titanic/game/chicken_dispensor.h
index d86b850871..5e3ba47ee8 100644
--- a/engines/titanic/game/chicken_dispensor.h
+++ b/engines/titanic/game/chicken_dispensor.h
@@ -28,6 +28,14 @@
namespace Titanic {
class CChickenDispensor : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool MouseDragStartMsg(CMouseDragStartMsg *msg);
+ bool TurnOff(CTurnOff *msg);
public:
int _fieldE0;
int _fieldE4;
diff --git a/engines/titanic/game/close_broken_pel.cpp b/engines/titanic/game/close_broken_pel.cpp
index d27441ac96..c234590849 100644
--- a/engines/titanic/game/close_broken_pel.cpp
+++ b/engines/titanic/game/close_broken_pel.cpp
@@ -24,16 +24,26 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCloseBrokenPel, CBackground)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
void CCloseBrokenPel::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string3, indent);
+ file->writeQuotedLine(_target, indent);
CBackground::save(file, indent);
}
void CCloseBrokenPel::load(SimpleFile *file) {
file->readNumber();
- _string3 = file->readString();
+ _target = file->readString();
CBackground::load(file);
}
+bool CCloseBrokenPel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ CActMsg actMsg("Close");
+ actMsg.execute(_target);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/close_broken_pel.h b/engines/titanic/game/close_broken_pel.h
index aacda6c002..4bd66255df 100644
--- a/engines/titanic/game/close_broken_pel.h
+++ b/engines/titanic/game/close_broken_pel.h
@@ -28,8 +28,10 @@
namespace Titanic {
class CCloseBrokenPel : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
public:
- CString _string3;
+ CString _target;
public:
CLASSDEF;
diff --git a/engines/titanic/game/cookie.cpp b/engines/titanic/game/cookie.cpp
index 915bb93b4a..96edca4058 100644
--- a/engines/titanic/game/cookie.cpp
+++ b/engines/titanic/game/cookie.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCookie, CGameObject)
+ ON_MESSAGE(LeaveNodeMsg)
+ ON_MESSAGE(FreshenCookieMsg)
+END_MESSAGE_MAP()
+
void CCookie::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_value1, indent);
@@ -40,4 +45,16 @@ void CCookie::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CCookie::LeaveNodeMsg(CLeaveNodeMsg *msg) {
+ if (_value2)
+ _value1 = 1;
+ return true;
+}
+
+bool CCookie::FreshenCookieMsg(CFreshenCookieMsg *msg) {
+ _value1 = msg->_value2;
+ _value2 = msg->_value1;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/cookie.h b/engines/titanic/game/cookie.h
index 7ae04f1144..2018deeb3e 100644
--- a/engines/titanic/game/cookie.h
+++ b/engines/titanic/game/cookie.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CCookie : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool LeaveNodeMsg(CLeaveNodeMsg *msg);
+ bool FreshenCookieMsg(CFreshenCookieMsg *msg);
public:
int _value1;
int _value2;
diff --git a/engines/titanic/game/credits.cpp b/engines/titanic/game/credits.cpp
index 7078d41a17..d9149f6dd2 100644
--- a/engines/titanic/game/credits.cpp
+++ b/engines/titanic/game/credits.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCredits, CGameObject)
+ ON_MESSAGE(SignalObject)
+ ON_MESSAGE(TimerMsg)
+END_MESSAGE_MAP()
+
CCredits::CCredits() : CGameObject(), _fieldBC(-1), _fieldC0(1) {
}
@@ -43,4 +48,34 @@ void CCredits::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CCredits::SignalObject(CSignalObject *msg) {
+ petHide();
+ disableMouse();
+ addTimer(50);
+ return true;
+}
+
+bool CCredits::TimerMsg(CTimerMsg *msg) {
+ stopGlobalSound(true, -1);
+ setVisible(true);
+ loadSound("a#16.wav");
+ loadSound("a#24.wav");
+
+ playCutscene(0, 18);
+ playGlobalSound("a#16.wav", -1, false, false, 0);
+ playCutscene(19, 642);
+ playSound("a#24.wav");
+ playCutscene(643, 750);
+
+ COpeningCreditsMsg creditsMsg;
+ creditsMsg.execute("Service Elevator Entity");
+ changeView("EmbLobby.Node 6.S");
+
+ setVisible(false);
+ petShow();
+ enableMouse();
+ stopGlobalSound(true, -1);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/credits.h b/engines/titanic/game/credits.h
index fa9794b6de..23fd25584d 100644
--- a/engines/titanic/game/credits.h
+++ b/engines/titanic/game/credits.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CCredits : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool SignalObject(CSignalObject *msg);
+ bool TimerMsg(CTimerMsg *msg);
public:
int _fieldBC, _fieldC0;
public:
diff --git a/engines/titanic/game/credits_button.cpp b/engines/titanic/game/credits_button.cpp
index 90bb1b5ebe..ee8f7bb329 100644
--- a/engines/titanic/game/credits_button.cpp
+++ b/engines/titanic/game/credits_button.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCreditsButton, CBackground)
+ ON_MESSAGE(MouseButtonUpMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
CCreditsButton::CCreditsButton() : CBackground(), _fieldE0(1) {
}
@@ -39,4 +44,19 @@ void CCreditsButton::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CCreditsButton::MouseButtonUpMsg(CMouseButtonUpMsg *msg) {
+ return true;
+}
+
+bool CCreditsButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_fieldE0) {
+ playSound("a#20.wav");
+ CSignalObject signalMsg;
+ signalMsg._numValue = 1;
+ signalMsg.execute("CreditsPlayer");
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/credits_button.h b/engines/titanic/game/credits_button.h
index 5e0bf96677..4a53083195 100644
--- a/engines/titanic/game/credits_button.h
+++ b/engines/titanic/game/credits_button.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CCreditsButton : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonUpMsg(CMouseButtonUpMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
public:
int _fieldE0;
public:
diff --git a/engines/titanic/game/desk_click_responder.cpp b/engines/titanic/game/desk_click_responder.cpp
index d9b2cb64b4..2cd5d1e83b 100644
--- a/engines/titanic/game/desk_click_responder.cpp
+++ b/engines/titanic/game/desk_click_responder.cpp
@@ -21,13 +21,19 @@
*/
#include "titanic/game/desk_click_responder.h"
+#include "titanic/titanic.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDeskClickResponder, CClickResponder)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LoadSuccessMsg)
+END_MESSAGE_MAP()
+
void CDeskClickResponder::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_fieldD4, indent);
- file->writeNumberLine(_fieldD8, indent);
+ file->writeNumberLine(_ticks, indent);
CClickResponder::save(file, indent);
}
@@ -35,9 +41,28 @@ void CDeskClickResponder::save(SimpleFile *file, int indent) {
void CDeskClickResponder::load(SimpleFile *file) {
file->readNumber();
_fieldD4 = file->readNumber();
- _fieldD8 = file->readNumber();
+ _ticks = file->readNumber();
CClickResponder::load(file);
}
+bool CDeskClickResponder::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ _fieldD4 = (_fieldD4 + 1) % 3;
+ if (_fieldD4)
+ return CClickResponder::MouseButtonDownMsg(msg);
+
+ uint ticks = g_vm->_events->getTicksCount();
+ if (!_ticks || ticks > (_ticks + 4000)) {
+ playSound("a#22.wav");
+ _ticks = ticks;
+ }
+
+ return true;
+}
+
+bool CDeskClickResponder::LoadSuccessMsg(CLoadSuccessMsg *msg) {
+ _ticks = 0;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/desk_click_responder.h b/engines/titanic/game/desk_click_responder.h
index 12825ba9de..13cf7f4b87 100644
--- a/engines/titanic/game/desk_click_responder.h
+++ b/engines/titanic/game/desk_click_responder.h
@@ -28,9 +28,12 @@
namespace Titanic {
class CDeskClickResponder : public CClickResponder {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LoadSuccessMsg(CLoadSuccessMsg *msg);
protected:
int _fieldD4;
- int _fieldD8;
+ uint _ticks;
public:
CLASSDEF;
diff --git a/engines/titanic/game/null_port_hole.cpp b/engines/titanic/game/null_port_hole.cpp
index e651b1b59f..b1514c7cbf 100644
--- a/engines/titanic/game/null_port_hole.cpp
+++ b/engines/titanic/game/null_port_hole.cpp
@@ -27,22 +27,22 @@ namespace Titanic {
EMPTY_MESSAGE_MAP(CNullPortHole, CClickResponder);
CNullPortHole::CNullPortHole() : CClickResponder() {
- _string1 = "For a better view, why not visit the Promenade Deck?";
- _string2 = "b#48.wav";
+ _message = "For a better view, why not visit the Promenade Deck?";
+ _soundName = "b#48.wav";
}
void CNullPortHole::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string2, indent);
- file->writeQuotedLine(_string1, indent);
+ file->writeQuotedLine(_soundName, indent);
+ file->writeQuotedLine(_message, indent);
CClickResponder::save(file, indent);
}
void CNullPortHole::load(SimpleFile *file) {
file->readNumber();
- _string2 = file->readString();
- _string1 = file->readString();
+ _soundName = file->readString();
+ _message = file->readString();
CClickResponder::load(file);
}
diff --git a/engines/titanic/game/parrot/parrot_succubus.cpp b/engines/titanic/game/parrot/parrot_succubus.cpp
deleted file mode 100644
index 02a29b748e..0000000000
--- a/engines/titanic/game/parrot/parrot_succubus.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "titanic/game/parrot/parrot_succubus.h"
-
-namespace Titanic {
-
-CParrotSuccUBus::CParrotSuccUBus() : CSuccUBus(), _field1DC(0),
- _field1EC(0), _field1F0(376), _field1F4(393) {
-}
-
-void CParrotSuccUBus::save(SimpleFile *file, int indent) {
- file->writeNumberLine(1, indent);
- file->writeNumberLine(_field1DC, indent);
- file->writeQuotedLine(_string3, indent);
- file->writeNumberLine(_field1EC, indent);
-
- CSuccUBus::save(file, indent);
-}
-
-void CParrotSuccUBus::load(SimpleFile *file) {
- file->readNumber();
- _field1DC = file->readNumber();
- _string3 = file->readString();
- _field1EC = file->readNumber();
-
- CSuccUBus::load(file);
-}
-
-} // End of namespace Titanic
diff --git a/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp b/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp
index a8a33fe1b1..fc5d680f0c 100644
--- a/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp
+++ b/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp
@@ -24,16 +24,44 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBarShelfVisCentre, CPlaceHolderItem)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(TimerMsg)
+ ON_MESSAGE(EnterViewMsg)
+END_MESSAGE_MAP()
+
void CBarShelfVisCentre::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_value, indent);
+ file->writeNumberLine(_flag, indent);
CPlaceHolderItem::save(file, indent);
}
void CBarShelfVisCentre::load(SimpleFile *file) {
file->readNumber();
- _value = file->readNumber();
+ _flag = file->readNumber();
CPlaceHolderItem::load(file);
}
+bool CBarShelfVisCentre::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (!_flag) {
+ CActMsg actMsg("ClickOnVision");
+ actMsg.execute("Barbot");
+ addTimer(3000);
+ _flag = true;
+ }
+
+ return true;
+}
+
+bool CBarShelfVisCentre::TimerMsg(CTimerMsg *msg) {
+ _flag = false;
+ return true;
+}
+
+bool CBarShelfVisCentre::EnterViewMsg(CEnterViewMsg *msg) {
+ _flag = false;
+ return true;
+}
+
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/placeholder/bar_shelf_vis_centre.h b/engines/titanic/game/placeholder/bar_shelf_vis_centre.h
index a53ef2633f..672655d368 100644
--- a/engines/titanic/game/placeholder/bar_shelf_vis_centre.h
+++ b/engines/titanic/game/placeholder/bar_shelf_vis_centre.h
@@ -28,11 +28,15 @@
namespace Titanic {
class CBarShelfVisCentre : public CPlaceHolderItem {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool TimerMsg(CTimerMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
private:
- int _value;
+ bool _flag;
public:
CLASSDEF;
- CBarShelfVisCentre() : CPlaceHolderItem(), _value(0) {}
+ CBarShelfVisCentre() : CPlaceHolderItem(), _flag(false) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/sgt/basin.cpp b/engines/titanic/game/sgt/basin.cpp
index 1eb1d161c9..775c67deca 100644
--- a/engines/titanic/game/sgt/basin.cpp
+++ b/engines/titanic/game/sgt/basin.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBasin, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CBasin::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +40,39 @@ void CBasin::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CBasin::TurnOn(CTurnOn *msg) {
+ if (_statics->_v10 == "Open" && _statics->_v11 == "Closed"
+ && _statics->_v2 == "Closed") {
+ setVisible(true);
+ _statics->_v11 = "Open";
+ _fieldE0 = 0;
+ _startFrame = 0;
+ _endFrame = 6;
+ playMovie(0, 6, MOVIE_GAMESTATE);
+ playSound("b#13.wav");
+ }
+
+ return true;
+}
+
+bool CBasin::TurnOff(CTurnOff *msg) {
+ if (_statics->_v11 == "Open") {
+ _statics->_v11 = "Closed";
+ _fieldE0 = 1;
+ _startFrame = 8;
+ _endFrame = 14;
+ playMovie(8, 14, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("b#13.wav");
+ }
+
+ return true;
+}
+
+bool CBasin::MovieEndMsg(CMovieEndMsg *msg) {
+ if (_statics->_v11 == "Closed")
+ setVisible(false);
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/basin.h b/engines/titanic/game/sgt/basin.h
index e4a36eb841..1fcb469824 100644
--- a/engines/titanic/game/sgt/basin.h
+++ b/engines/titanic/game/sgt/basin.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CBasin : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/bedfoot.cpp b/engines/titanic/game/sgt/bedfoot.cpp
index 18ea07aca0..ff7d64569a 100644
--- a/engines/titanic/game/sgt/bedfoot.cpp
+++ b/engines/titanic/game/sgt/bedfoot.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBedfoot, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+END_MESSAGE_MAP()
+
void CBedfoot::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +39,91 @@ void CBedfoot::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CBedfoot::TurnOn(CTurnOn *msg) {
+ if (_statics->_v2 == "Closed" && _statics->_v11 == "Closed") {
+ _fieldE0 = 0;
+ _startFrame = 0;
+ if (_statics->_v10 == "Open") {
+ _endFrame = 13;
+ _statics->_v2 = "Open";
+ playSound("b#7.wav");
+ } else {
+ _endFrame = 17;
+ _statics->_v2 = "NotOnWashstand";
+ playSound("b#4.wav");
+ }
+
+ playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE);
+ } else if (_statics->_v2 == "RestingUnderTV") {
+ _fieldE0 = 0;
+ _startFrame = 8;
+ if (_statics->_v10 == "Open") {
+ _statics->_v2 = "Open";
+ playSound("189_436_bed down 1.wav");
+ } else {
+ _statics->_v2 = "NotOnWashstand";
+ playSound("192_436_bed hits floor.wav");
+ }
+
+ playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE);
+ }
+
+ if (_statics->_v2 == "Open")
+ _statics->_v1 = "Closed";
+ else if (_statics->_v2 == "NotOnWashstand")
+ _statics->_v1 = "ClosedWrong";
+
+ return true;
+}
+
+bool CBedfoot::TurnOff(CTurnOff *msg) {
+ if (_statics->_v1 == "Closed" || _statics->_v1 == "ClosedWrong") {
+ setVisible(true);
+ CVisibleMsg visibleMsg(false);
+ visibleMsg.execute("Bedhead");
+ }
+
+ if (_statics->_v2 == "Open" && _statics->_v1 == "Closed") {
+ _fieldE0 = 0;
+ _startFrame = 20;
+ if (_statics->_v4 == "Closed") {
+ _statics->_v2 = "Closed";
+ _endFrame = 30;
+ } else {
+ _statics->_v2 = "RestingUnderTV";
+ _endFrame = 25;
+ }
+
+ playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE);
+ playSound("b#7.wav");
+
+ } else if (_statics->_v2 == "NotOnWashstand" && _statics->_v1 == "ClosedWrong") {
+ _fieldE0 = 0;
+ _startFrame = 17;
+
+ if (_statics->_v4 == "Closed") {
+ _statics->_v2 = "Closed";
+ _endFrame = 30;
+ } else {
+ _statics->_v2 = "RestingUnderTV";
+ _endFrame = 25;
+ }
+
+ playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE);
+ playSound("b#7.wav");
+
+ } else if (_statics->_v2 == "RestingUTV" && _statics->_v4 == "Closed") {
+ _statics->_v2 = "Closed";
+ _startFrame = 25;
+ _endFrame = 30;
+ playMovie(25, 30, MOVIE_GAMESTATE);
+ playSound("b#7.wav");
+ }
+
+ if (_statics->_v2 == "Closed")
+ _statics->_v1 = "Closed";
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/bedfoot.h b/engines/titanic/game/sgt/bedfoot.h
index df3db42d6d..cc7b82b075 100644
--- a/engines/titanic/game/sgt/bedfoot.h
+++ b/engines/titanic/game/sgt/bedfoot.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CBedfoot : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/bedhead.cpp b/engines/titanic/game/sgt/bedhead.cpp
index fad7272f3a..6f427ab625 100644
--- a/engines/titanic/game/sgt/bedhead.cpp
+++ b/engines/titanic/game/sgt/bedhead.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBedhead, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+END_MESSAGE_MAP()
+
void CBedhead::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +39,14 @@ void CBedhead::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CBedhead::TurnOn(CTurnOn *msg) {
+ // TODO
+ return true;
+}
+
+bool CBedhead::TurnOff(CTurnOff *msg) {
+ // TODO
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/bedhead.h b/engines/titanic/game/sgt/bedhead.h
index f1ba31786c..665dec021c 100644
--- a/engines/titanic/game/sgt/bedhead.h
+++ b/engines/titanic/game/sgt/bedhead.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CBedhead : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/chest_of_drawers.cpp b/engines/titanic/game/sgt/chest_of_drawers.cpp
index be62e12c8e..d9c72d3021 100644
--- a/engines/titanic/game/sgt/chest_of_drawers.cpp
+++ b/engines/titanic/game/sgt/chest_of_drawers.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChestOfDrawers, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CChestOfDrawers::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +40,41 @@ void CChestOfDrawers::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CChestOfDrawers::TurnOn(CTurnOn *msg) {
+ if (_statics->_v6 == "Closed" && _statics->_v5 == "Open") {
+ _fieldE0 = false;
+ _statics->_v6 = "Open";
+ _startFrame = 1;
+ _endFrame = 14;
+ playSound("b#11.wav");
+ }
+
+ return true;
+}
+
+bool CChestOfDrawers::TurnOff(CTurnOff *msg) {
+ if (_statics->_v6 == "Open" && _statics->_v5 == "Closed") {
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("Drawer");
+ _statics->_v6 = "Closed";
+ _fieldE0 = true;
+
+ _startFrame = 14;
+ _endFrame = 27;
+ playMovie(14, 27, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("b#11.wav");
+ }
+
+ return true;
+}
+
+bool CChestOfDrawers::MovieEndMsg(CMovieEndMsg *msg) {
+ if (_statics->_v6 == "Open") {
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("Drawer");
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/chest_of_drawers.h b/engines/titanic/game/sgt/chest_of_drawers.h
index 16a1bf8fea..5bf852902b 100644
--- a/engines/titanic/game/sgt/chest_of_drawers.h
+++ b/engines/titanic/game/sgt/chest_of_drawers.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CChestOfDrawers : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/desk.cpp b/engines/titanic/game/sgt/desk.cpp
index 4dd0fdab92..51be14adb9 100644
--- a/engines/titanic/game/sgt/desk.cpp
+++ b/engines/titanic/game/sgt/desk.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDesk, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CDesk::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +40,44 @@ void CDesk::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CDesk::TurnOn(CTurnOn *msg) {
+ if (_statics->_v5 == "Closed" && _statics->_v1 == "RestingG"
+ && _statics->_v1 == "OpenWrong") {
+ _statics->_v5 = "Open";
+ _fieldE0 = false;
+ _startFrame = 1;
+ _endFrame = 26;
+ playMovie(1, 26, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("b#12.wav");
+ }
+
+ return true;
+}
+
+bool CDesk::TurnOff(CTurnOff *msg) {
+ if (_statics->_v5 == "Open" && _statics->_v6 == "Closed"
+ && _statics->_v1 == "Open") {
+ CVisibleMsg visibleMsg(false);
+ visibleMsg.execute("ChestOfDrawers");
+
+ _statics->_v5 = "Closed";
+ _fieldE0 = true;
+ _startFrame = 26;
+ _endFrame = 51;
+ playMovie(26, 51, MOVIE_GAMESTATE);
+ playSound("b#9.wav");
+ }
+
+ return true;
+}
+
+bool CDesk::MovieEndMsg(CMovieEndMsg *msg) {
+ if (_statics->_v5 == "Open") {
+ CVisibleMsg visibleMsg(true);
+ visibleMsg.execute("ChestOfDrawers");
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/desk.h b/engines/titanic/game/sgt/desk.h
index 77b5fa17af..8b9e1fe841 100644
--- a/engines/titanic/game/sgt/desk.h
+++ b/engines/titanic/game/sgt/desk.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CDesk : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/deskchair.cpp b/engines/titanic/game/sgt/deskchair.cpp
index a4a2badeb0..7f64c2ee34 100644
--- a/engines/titanic/game/sgt/deskchair.cpp
+++ b/engines/titanic/game/sgt/deskchair.cpp
@@ -24,6 +24,13 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDeskchair, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CDeskchair::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +41,49 @@ void CDeskchair::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CDeskchair::TurnOn(CTurnOn *msg) {
+ if (_statics->_v8 == "Closed" && _statics->_v9 == "Closed") {
+ setVisible(true);
+ _statics->_v9 = "Open";
+ _fieldE0 = false;
+ _startFrame = 0;
+ _endFrame = 16;
+ playMovie(0, 16, MOVIE_GAMESTATE);
+ playSound("b#8.wav");
+ }
+
+ return true;
+}
+
+bool CDeskchair::TurnOff(CTurnOff *msg) {
+ if (_statics->_v9 == "Open") {
+ _statics->_v9 = "Closed";
+ _fieldE0 = true;
+ _startFrame = 16;
+ _endFrame = 32;
+ playMovie(16, 32, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("b#2.wav");
+ }
+
+ return true;
+}
+
+bool CDeskchair::ActMsg(CActMsg *msg) {
+ if (msg->_action == "Smash") {
+ setVisible(false);
+ _statics->_v9 = "Closed";
+ _fieldE0 = true;
+ loadFrame(0);
+ return true;
+ } else {
+ return CSGTStateRoom::ActMsg(msg);
+ }
+}
+
+bool CDeskchair::MovieEndMsg(CMovieEndMsg *msg) {
+ if (_statics->_v9 == "Closed")
+ setVisible(false);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/deskchair.h b/engines/titanic/game/sgt/deskchair.h
index 5181b650d2..6e7bbe4169 100644
--- a/engines/titanic/game/sgt/deskchair.h
+++ b/engines/titanic/game/sgt/deskchair.h
@@ -28,6 +28,11 @@
namespace Titanic {
class CDeskchair : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/sgt_state_room.cpp b/engines/titanic/game/sgt/sgt_state_room.cpp
index 55f08de8b4..c089e401b8 100644
--- a/engines/titanic/game/sgt/sgt_state_room.cpp
+++ b/engines/titanic/game/sgt/sgt_state_room.cpp
@@ -21,17 +21,22 @@
*/
#include "titanic/game/sgt/sgt_state_room.h"
+#include "titanic/pet_control/pet_control.h"
namespace Titanic {
BEGIN_MESSAGE_MAP(CSGTStateRoom, CBackground)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(VisibleMsg)
ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(LeaveRoomMsg)
END_MESSAGE_MAP()
CSGTStateRoomStatics *CSGTStateRoom::_statics;
void CSGTStateRoom::init() {
_statics = new CSGTStateRoomStatics();
+ _statics->_v1 = "Closed";
}
void CSGTStateRoom::deinit() {
@@ -94,8 +99,80 @@ void CSGTStateRoom::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CSGTStateRoom::ActMsg(CActMsg *msg) {
+ CPetControl *pet = getPetControl();
+ uint roomFlags = pet->getRoomFlags();
+ uint assignedRoom = pet->getAssignedRoomFlags();
+
+ if (roomFlags != assignedRoom) {
+ petDisplayMessage("This is not your assigned room. Please do not enjoy.");
+ } else if (_fieldE0) {
+ CTurnOn onMsg;
+ onMsg.execute(this);
+ } else {
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ }
+
+ return true;
+}
+
+bool CSGTStateRoom::VisibleMsg(CVisibleMsg *msg) {
+ setVisible(msg->_visible);
+ return true;
+}
+
bool CSGTStateRoom::EnterRoomMsg(CEnterRoomMsg *msg) {
- warning("CSGTStateRoom::handleEvent");
+ CPetControl *pet = getPetControl();
+ uint roomFlags = pet->getRoomFlags();
+ uint assignedRoom = pet->getAssignedRoomFlags();
+
+ if (roomFlags == assignedRoom) {
+ loadFrame(_fieldE8);
+ _fieldE0 = _fieldEC;
+ setVisible(_fieldF0);
+
+ if (isEquals("Desk") && _statics->_v5 == "Closed")
+ loadFrame(1);
+ }
+
+ if (isEquals("Drawer")) {
+ petSetArea(PET_REMOTE);
+ if (roomFlags == assignedRoom && getPassengerClass() == 3
+ && _statics->_v13) {
+ playSound("b#21.wav");
+ _statics->_v13 = 0;
+ }
+
+ _statics->_v7 = "Closed";
+ setVisible(false);
+ _fieldE0 = true;
+ } else if (roomFlags != assignedRoom) {
+ loadFrame(0);
+ if (_fieldE4) {
+ setVisible(true);
+ if (isEquals("Desk"))
+ loadFrame(1);
+ } else {
+ setVisible(false);
+ }
+ }
+
+ return true;
+}
+
+bool CSGTStateRoom::LeaveRoomMsg(CLeaveRoomMsg *msg) {
+ CPetControl *pet = getPetControl();
+ uint roomFlags = pet->getRoomFlags();
+ uint assignedRoom = pet->getAssignedRoomFlags();
+
+ if (roomFlags == assignedRoom) {
+ _fieldE8 = getMovieFrame();
+ _fieldEC = _fieldE0;
+ _fieldF0 = _visible;
+ }
+
+ _statics->_v14 = roomFlags;
return true;
}
diff --git a/engines/titanic/game/sgt/sgt_state_room.h b/engines/titanic/game/sgt/sgt_state_room.h
index d9ffdb8e9e..3975f7b59b 100644
--- a/engines/titanic/game/sgt/sgt_state_room.h
+++ b/engines/titanic/game/sgt/sgt_state_room.h
@@ -47,15 +47,18 @@ struct CSGTStateRoomStatics {
class CSGTStateRoom : public CBackground {
DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool VisibleMsg(CVisibleMsg *msg);
bool EnterRoomMsg(CEnterRoomMsg *msg);
+ bool LeaveRoomMsg(CLeaveRoomMsg *msg);
protected:
static CSGTStateRoomStatics *_statics;
protected:
- int _fieldE0;
+ bool _fieldE0;
int _fieldE4;
int _fieldE8;
- int _fieldEC;
- int _fieldF0;
+ bool _fieldEC;
+ bool _fieldF0;
public:
CLASSDEF;
CSGTStateRoom();
diff --git a/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp b/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp
index ed37b0a5c7..72cd7f9037 100644
--- a/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp
+++ b/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp
@@ -25,19 +25,19 @@
namespace Titanic {
CSGTUpperDoorsSound::CSGTUpperDoorsSound() {
- _string2 = "b#53.wav";
+ _soundName = "b#53.wav";
}
void CSGTUpperDoorsSound::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string2, indent);
+ file->writeQuotedLine(_soundName, indent);
CClickResponder::save(file, indent);
}
void CSGTUpperDoorsSound::load(SimpleFile *file) {
file->readNumber();
- _string2 = file->readString();
+ _soundName = file->readString();
CClickResponder::load(file);
}
diff --git a/engines/titanic/game/transport/pellerator.cpp b/engines/titanic/game/transport/pellerator.cpp
index e789c20a3d..5bc2423478 100644
--- a/engines/titanic/game/transport/pellerator.cpp
+++ b/engines/titanic/game/transport/pellerator.cpp
@@ -21,34 +21,344 @@
*/
#include "titanic/game/transport/pellerator.h"
+#include "titanic/core/room_item.h"
namespace Titanic {
+static const char *const WAVE_NAMES[10] = {
+ "z#465.wav", "z#456.wav", "z#455.wav", "z#453.wav",
+ "z#452.wav", "NoStandingInFunnyWays", "z#450.wav",
+ "z#449.wav", "z#435.wav", "z#434.wav"
+};
+
BEGIN_MESSAGE_MAP(CPellerator, CTransport)
+ ON_MESSAGE(StatusChangeMsg)
ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(TimerMsg)
END_MESSAGE_MAP()
-int CPellerator::_v1;
-int CPellerator::_v2;
+int CPellerator::_soundHandle;
+int CPellerator::_destination;
+
+CPellerator::CPellerator() : CTransport() {
+}
void CPellerator::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_v1, indent);
- file->writeNumberLine(_v2, indent);
+ file->writeNumberLine(_soundHandle, indent);
+ file->writeNumberLine(_destination, indent);
CTransport::save(file, indent);
}
void CPellerator::load(SimpleFile *file) {
file->readNumber();
- _v1 = file->readNumber();
- _v2 = file->readNumber();
+ _soundHandle = file->readNumber();
+ _destination = file->readNumber();
CTransport::load(file);
}
+bool CPellerator::StatusChangeMsg(CStatusChangeMsg *msg) {
+ setVisible(true);
+ playGlobalSound("z#74.wav", -2, true, true, 0);
+ int classNum = getPassengerClass();
+ int newDest = msg->_newStatus;
+
+ if (msg->_newStatus == _destination) {
+ petDisplayMessage(1, "You are already at your chosen destination.");
+ } else if (classNum == 3 || (msg->_newStatus > 4 && classNum != 1)) {
+ petDisplayMessage(1, "Passengers of your class are not permitted to enter this area.");
+ } else if (newDest > _destination) {
+ CString name = getName();
+ changeView(name == "PelleratorObject2" ?
+ "Pellerator.Node 1.N" : "Pellerator.Node 1.S");
+
+ if (name == "PelleratorObject") {
+ for (; _destination < newDest; ++_destination) {
+ switch (_destination) {
+ case 0:
+ case 1:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(299, 304, 0);
+ playMovie(305, 313, MOVIE_GAMESTATE);
+ break;
+
+ case 2:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(299, 304, 0);
+ for (int idx = 0; idx < 5; ++idx)
+ playMovie(253, 263, 0);
+ playMovie(153, 197, 0);
+ for (int idx = 0; idx < 5; ++idx)
+ playMovie(253, 263, 0);
+ playMovie(290, 293, MOVIE_GAMESTATE);
+ break;
+
+ case 4:
+ playMovie(267, 270, 0);
+ for (int idx = 0; idx < 5; ++idx)
+ playMovie(253, 263, 0);
+ playMovie(3, 71, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(253, 263, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ break;
+
+ case 5:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(299, 304, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(253, 263, 0);
+ playMovie(3, 71, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(299, 304, 0);
+
+ }
+ }
+ } else {
+ for (; _destination < newDest; ++_destination) {
+ switch (_destination) {
+ case 0:
+ case 1:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(299, 304, 0);
+ playMovie(305, 313, MOVIE_GAMESTATE);
+ break;
+
+ case 2:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 4; ++idx)
+ playMovie(299, 304, 0);
+ for (int idx = 0; idx < 15; ++idx)
+ playMovie(245, 255, 0);
+ playMovie(264, 267, MOVIE_GAMESTATE);
+ ++_destination;
+ break;
+
+ case 4:
+ playMovie(241, 244, 0);
+ for (int idx = 0; idx < 15; ++idx)
+ playMovie(245, 255, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ break;
+
+ case 5:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(229, 304, 0);
+ for (int idx = 0; idx < 12; ++idx)
+ playMovie(245, 255, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(299, 304, 0);
+ playMovie(305, 313, MOVIE_GAMESTATE);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ playMovie(264, 264, MOVIE_NOTIFY_OBJECT);
+ _destination = newDest;
+ } else if (newDest < _destination) {
+ CString name = getName();
+ changeView(name == "PelleratorObject2" ?
+ "Pellerator.Node 1.N" : "Pellerator.Node 1.S");
+
+ if (name == "PelleratorObject") {
+ for (; _destination > newDest; --_destination) {
+ switch (_destination) {
+ case 0:
+ case 1:
+ playMovie(351, 359, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ break;
+
+ case 3:
+ playMovie(241, 244, 0);
+ for (int idx = 0; idx < 5; ++idx)
+ playMovie(245, 255, 0);
+ playMovie(197, 239, 0);
+ for (int idx = 0; idx < 5; ++idx)
+ playMovie(245, 255, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ --_destination;
+ break;
+
+ case 4:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(299, 304, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(245, 255, 0);
+ playMovie(78, 149, 0);
+ for (int idx = 0; idx < 5; ++idx)
+ playMovie(245, 255, 0);
+ playMovie(264, 267, MOVIE_GAMESTATE);
+ break;
+
+ case 5:
+ playMovie(351, 359, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(336, 341, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(245, 255, 0);
+ playMovie(78, 149, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ for (; _destination > newDest; --_destination) {
+ switch (_destination) {
+ case 0:
+ case 1:
+ playMovie(351, 359, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ break;
+
+ case 3:
+ playMovie(267, 270, 0);
+ for (int idx = 0; idx < 15; ++idx)
+ playMovie(253, 263, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ --_destination;
+ break;
+
+ case 4:
+ playMovie(315, 323, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(299, 304, 0);
+ for (int idx = 0; idx < 15; ++idx)
+ playMovie(253, 263, 0);
+ playMovie(290, 293, MOVIE_GAMESTATE);
+ break;
+
+ case 5:
+ playMovie(351, 359, 0);
+ for (int idx = 0; idx < 7; ++idx)
+ playMovie(336, 341, 0);
+ for (int idx = 0; idx < 13; ++idx)
+ playMovie(253, 263, 0);
+ for (int idx = 0; idx < 3; ++idx)
+ playMovie(336, 341, 0);
+ playMovie(342, 348, MOVIE_GAMESTATE);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ playMovie(264, 264, MOVIE_NOTIFY_OBJECT);
+ _destination = newDest;
+ }
+
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = _destination;
+ statusMsg.execute("ExitPellerator");
+
+ return true;
+}
+
bool CPellerator::EnterRoomMsg(CEnterRoomMsg *msg) {
- warning("CPellerator::handleEvent");
+ if (isEquals("PelleratorObject")) {
+ for (int idx = 0; idx < 10; ++idx)
+ loadSound(WAVE_NAMES[idx]);
+ addTimer(10000);
+ }
+
+ CString name = msg->_oldRoom ? msg->_oldRoom->getName() : "";
+ int oldVal = _destination;
+
+ if (name.empty()) {
+ _destination = 4;
+ oldVal = 4;
+ } else if (name == "PromenadeDeck") {
+ _destination = 0;
+ } else if (name == "MusicRoomLobby") {
+ _destination = 1;
+ } else if (name == "Bar") {
+ _destination = 2;
+ } else if (name == "TopOfWell") {
+ _destination = 4;
+ } else if (name == "1stClassRestaurant") {
+ _destination = 5;
+ } else if (name == "Arboretum" || name == "FrozenArboretum") {
+ _destination = 6;
+ }
+
+ if (_destination != oldVal) {
+ CStatusChangeMsg statusMsg;
+ statusMsg._newStatus = _destination;
+ statusMsg.execute("ExitPellerator");
+ }
+
+ loadFrame(264);
+ return true;
+}
+
+bool CPellerator::MovieEndMsg(CMovieEndMsg *msg) {
+ setVisible(false);
+ stopGlobalSound(true, -1);
+
+ switch (_destination) {
+ case 0:
+ _soundHandle = queueSound("z#429.wav", _soundHandle);
+ break;
+ case 1:
+ _soundHandle = queueSound("z#430.wav", _soundHandle);
+ break;
+ case 2:
+ _soundHandle = queueSound("z#431.wav", _soundHandle);
+ break;
+ case 4:
+ _soundHandle = queueSound("z#428.wav", _soundHandle);
+ break;
+ case 5:
+ _soundHandle = queueSound("z#433.wav", _soundHandle);
+ break;
+ case 6:
+ _soundHandle = queueSound("z#432.wav", _soundHandle);
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool CPellerator::TimerMsg(CTimerMsg *msg) {
+ if (compareRoomNameTo("Pellerator")) {
+ _soundHandle = queueSound(WAVE_NAMES[getRandomNumber(9)], _soundHandle);
+ addTimer(20000 + getRandomNumber(10000));
+ }
+
return true;
}
diff --git a/engines/titanic/game/transport/pellerator.h b/engines/titanic/game/transport/pellerator.h
index fa400a49cd..c634f435cc 100644
--- a/engines/titanic/game/transport/pellerator.h
+++ b/engines/titanic/game/transport/pellerator.h
@@ -30,12 +30,16 @@ namespace Titanic {
class CPellerator : public CTransport {
DECLARE_MESSAGE_MAP;
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
bool EnterRoomMsg(CEnterRoomMsg *msg);
-private:
- static int _v1;
- static int _v2;
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool TimerMsg(CTimerMsg *msg);
+public:
+ static int _soundHandle;
+ static int _destination;
public:
CLASSDEF;
+ CPellerator();
/**
* Save the data for the class to file
diff --git a/engines/titanic/gfx/changes_season_button.cpp b/engines/titanic/gfx/changes_season_button.cpp
index d5242ad890..584a9542f3 100644
--- a/engines/titanic/gfx/changes_season_button.cpp
+++ b/engines/titanic/gfx/changes_season_button.cpp
@@ -21,9 +21,14 @@
*/
#include "titanic/gfx/changes_season_button.h"
+#include "titanic/core/project_item.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChangesSeasonButton, CSTButton)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
CChangesSeasonButton::CChangesSeasonButton() : CSTButton() {
}
@@ -37,4 +42,10 @@ void CChangesSeasonButton::load(SimpleFile *file) {
CSTButton::load(file);
}
+bool CChangesSeasonButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ CChangeSeasonMsg changeMsg(_actionName);
+ changeMsg.execute(getRoot(), nullptr, MSGFLAG_SCAN);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/gfx/changes_season_button.h b/engines/titanic/gfx/changes_season_button.h
index 2b58a3199b..4f588187eb 100644
--- a/engines/titanic/gfx/changes_season_button.h
+++ b/engines/titanic/gfx/changes_season_button.h
@@ -28,6 +28,8 @@
namespace Titanic {
class CChangesSeasonButton : public CSTButton {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
public:
CLASSDEF;
CChangesSeasonButton();
diff --git a/engines/titanic/gfx/chev_switch.cpp b/engines/titanic/gfx/chev_switch.cpp
index a6ce93098c..177f0ada76 100644
--- a/engines/titanic/gfx/chev_switch.cpp
+++ b/engines/titanic/gfx/chev_switch.cpp
@@ -24,7 +24,13 @@
namespace Titanic {
-CChevSwitch::CChevSwitch() : CToggleSwitch() {
+BEGIN_MESSAGE_MAP(CChevSwitch, CToggleSwitch)
+ ON_MESSAGE(MouseButtonUpMsg)
+ ON_MESSAGE(SetChevButtonImageMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
+CChevSwitch::CChevSwitch() : CToggleSwitch(), _value(0) {
}
void CChevSwitch::save(SimpleFile *file, int indent) {
@@ -37,4 +43,36 @@ void CChevSwitch::load(SimpleFile *file) {
CToggleSwitch::load(file);
}
+bool CChevSwitch::MouseButtonUpMsg(CMouseButtonUpMsg *msg) {
+ return true;
+}
+
+bool CChevSwitch::SetChevButtonImageMsg(CSetChevButtonImageMsg *msg) {
+ if (msg->_value2 && getParent()) {
+ error("TODO: Don't know parent type");
+ }
+
+ _fieldBC = msg->_value1;
+ if (_fieldBC) {
+ loadImage((_value & 1) ? "on_odd.tga" : "on_even.tga");
+ } else {
+ loadImage((_value & 1) ? "off_odd.tga" : "off_even.tga");
+ }
+
+ return true;
+}
+
+bool CChevSwitch::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ _fieldBC ^= 1;
+ if (getParent()) {
+ CSetChevPanelBitMsg bitMsg(_value, _fieldBC);
+ bitMsg.execute(getParent());
+ }
+
+ CSetChevButtonImageMsg chevMsg(_fieldBC, 0);
+ chevMsg.execute(this);
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/gfx/chev_switch.h b/engines/titanic/gfx/chev_switch.h
index 0305a6ca83..01da53c854 100644
--- a/engines/titanic/gfx/chev_switch.h
+++ b/engines/titanic/gfx/chev_switch.h
@@ -28,6 +28,12 @@
namespace Titanic {
class CChevSwitch : public CToggleSwitch {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonUpMsg(CMouseButtonUpMsg *msg);
+ bool SetChevButtonImageMsg(CSetChevButtonImageMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+public:
+ int _value;
public:
CLASSDEF;
CChevSwitch();
diff --git a/engines/titanic/gfx/toggle_switch.h b/engines/titanic/gfx/toggle_switch.h
index ae96c75ebd..8e7d057d8c 100644
--- a/engines/titanic/gfx/toggle_switch.h
+++ b/engines/titanic/gfx/toggle_switch.h
@@ -28,7 +28,7 @@
namespace Titanic {
class CToggleSwitch : public CGameObject {
-private:
+protected:
int _fieldBC;
Point _pos1;
public:
diff --git a/engines/titanic/messages/bilge_dispensor_event.cpp b/engines/titanic/messages/bilge_dispensor_event.cpp
index 043ffe75d3..584da00a6f 100644
--- a/engines/titanic/messages/bilge_dispensor_event.cpp
+++ b/engines/titanic/messages/bilge_dispensor_event.cpp
@@ -24,6 +24,13 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBilgeDispensorEvent, CAutoSoundEvent)
+ ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(LeaveRoomMsg)
+ ON_MESSAGE(FrameMsg)
+ ON_MESSAGE(StatusChangeMsg)
+END_MESSAGE_MAP()
+
void CBilgeDispensorEvent::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CAutoSoundEvent::save(file, indent);
@@ -39,4 +46,32 @@ bool CBilgeDispensorEvent::EnterRoomMsg(CEnterRoomMsg *msg) {
return true;
}
+bool CBilgeDispensorEvent::LeaveRoomMsg(CLeaveRoomMsg *msg) {
+ _value1 = -1;
+ return true;
+}
+
+bool CBilgeDispensorEvent::FrameMsg(CFrameMsg *msg) {
+ if (_value1 >= 0 && (_value1 & 0xffff) == 0x4000) {
+ int volume = 20 + getRandomNumber(30);
+ int val3 = getRandomNumber(20) - 10;
+
+ if (getRandomNumber(2) == 0) {
+ playSound("b#18.wav", volume, val3);
+ }
+ }
+
+ CAutoSoundEvent::FrameMsg(msg);
+ return true;
+}
+
+bool CBilgeDispensorEvent::StatusChangeMsg(CStatusChangeMsg *msg) {
+ if (msg->_newStatus == 1)
+ _value1 = -1;
+ else if (msg->_newStatus == 2)
+ _value1 = 0;
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/messages/bilge_dispensor_event.h b/engines/titanic/messages/bilge_dispensor_event.h
index 96ef92a54e..61d3116db4 100644
--- a/engines/titanic/messages/bilge_dispensor_event.h
+++ b/engines/titanic/messages/bilge_dispensor_event.h
@@ -29,9 +29,14 @@
namespace Titanic {
class CBilgeDispensorEvent : public CAutoSoundEvent {
+ DECLARE_MESSAGE_MAP;
bool EnterRoomMsg(CEnterRoomMsg *msg);
+ bool LeaveRoomMsg(CLeaveRoomMsg *msg);
+ bool FrameMsg(CFrameMsg *msg);
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
public:
CLASSDEF;
+ CBilgeDispensorEvent() : CAutoSoundEvent() {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/messages/door_auto_sound_event.cpp b/engines/titanic/messages/door_auto_sound_event.cpp
index b9cedae6de..7618577e50 100644
--- a/engines/titanic/messages/door_auto_sound_event.cpp
+++ b/engines/titanic/messages/door_auto_sound_event.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDoorAutoSoundEvent, CAutoSoundEvent)
+ ON_MESSAGE(PreEnterNodeMsg)
+ ON_MESSAGE(LeaveNodeMsg)
+ ON_MESSAGE(TimerMsg)
+END_MESSAGE_MAP()
+
void CDoorAutoSoundEvent::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeQuotedLine(_string1, indent);
@@ -44,4 +50,16 @@ void CDoorAutoSoundEvent::load(SimpleFile *file) {
CAutoSoundEvent::load(file);
}
+bool CDoorAutoSoundEvent::PreEnterNodeMsg(CPreEnterNodeMsg *msg) {
+ return true;
+}
+
+bool CDoorAutoSoundEvent::LeaveNodeMsg(CLeaveNodeMsg *msg) {
+ return true;
+}
+
+bool CDoorAutoSoundEvent::TimerMsg(CTimerMsg *msg) {
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/messages/door_auto_sound_event.h b/engines/titanic/messages/door_auto_sound_event.h
index e6ea1b0f98..8b064a7221 100644
--- a/engines/titanic/messages/door_auto_sound_event.h
+++ b/engines/titanic/messages/door_auto_sound_event.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CDoorAutoSoundEvent : public CAutoSoundEvent {
+ DECLARE_MESSAGE_MAP;
+ bool PreEnterNodeMsg(CPreEnterNodeMsg *msg);
+ bool LeaveNodeMsg(CLeaveNodeMsg *msg);
+ bool TimerMsg(CTimerMsg *msg);
public:
CString _string1;
CString _string2;
diff --git a/engines/titanic/messages/messages.h b/engines/titanic/messages/messages.h
index 71807f4e99..e6d494ebc8 100644
--- a/engines/titanic/messages/messages.h
+++ b/engines/titanic/messages/messages.h
@@ -206,15 +206,13 @@ MESSAGE1(CAnimateMaitreDMsg, int, value, 0);
MESSAGE1(CArboretumGateMsg, int, value, 0);
MESSAGE0(CArmPickedUpFromTableMsg);
MESSAGE0(CBodyInBilgeRoomMsg);
-MESSAGE1(CBowlStateChange, int, value, 0);
+MESSAGE1(CBowlStateChangeMsg, int, value, 0);
MESSAGE2(CCarryObjectArrivedMsg, CString, strValue, "", int, numValue, 0);
MESSAGE2(CChangeMusicMsg, CString, filename, "", int, flags, 0);
MESSAGE1(CChangeSeasonMsg, CString, season, "Summer");
MESSAGE0(CCheckAllPossibleCodes);
-MESSAGE2(CCheckChevCode, int, value1, 0, int, value2, 0);
+MESSAGE2(CCheckChevCode, int, classNum, 0, uint, chevCode, 0);
MESSAGE1(CChildDragEndMsg, int, value, 0);
-MESSAGE2(CChildDragMoveMsg, int, value1, 0, int, value2, 0);
-MESSAGE2(CChildDragStartMsg, int, value1, 0, int, value2, 0);
MESSAGE0(CClearChevPanelBits);
MESSAGE0(CCorrectMusicPlayedMsg);
MESSAGE0(CCreateMusicPlayerMsg);
@@ -228,8 +226,8 @@ MESSAGE0(CDonNavHelmet);
MESSAGE1(CDoorbotNeededInElevatorMsg, int, value, 0);
MESSAGE0(CDoorbotNeededInHomeMsg);
MESSAGE1(CDropObjectMsg, CCarry *, item, nullptr);
-MESSAGE1(CDropZoneGotObjectMsg, int, value, 0);
-MESSAGE1(CDropZoneLostObjectMsg, int, value, 0);
+MESSAGE1(CDropZoneGotObjectMsg, CGameObject *, object, nullptr);
+MESSAGE1(CDropZoneLostObjectMsg, CGameObject *, object, nullptr);
MESSAGE1(CEjectCylinderMsg, int, value, 0);
MESSAGE2(CPreEnterNodeMsg, CNodeItem *, oldNode, nullptr, CNodeItem *, newNode, nullptr);
MESSAGE2(CPreEnterRoomMsg, CRoomItem *, oldRoom, nullptr, CRoomItem *, newRoom, nullptr);
@@ -240,16 +238,16 @@ MESSAGE2(CEnterViewMsg, CViewItem *, oldView, nullptr, CViewItem *, newView, nul
MESSAGE0(CErasePhonographCylinderMsg);
MESSAGE1(CFrameMsg, uint, ticks, 0);
MESSAGE2(CFreshenCookieMsg, int, value1, 0, int, value2, 0);
-MESSAGE1(CGetChevClassBits, int, value, 0);
-MESSAGE1(CGetChevClassNum, int, value, 0);
-MESSAGE2(CGetChevCodeFromRoomNameMsg, CString, strValue, "", int, numValue, 0);
-MESSAGE1(CGetChevFloorBits, int, value, 0);
-MESSAGE1(CGetChevFloorNum, int, value, 0);
-MESSAGE1(CGetChevLiftBits, int, value, 0);
-MESSAGE1(CGetChevLiftNum, int, value, 0);
-MESSAGE1(CGetChevRoomBits, int, value, 0);
-MESSAGE1(CGetChevRoomNum, int, value, 0);
-MESSAGE2(CHoseConnectedMsg, int, value1, 1, int, value2, 0);
+MESSAGE1(CGetChevClassBits, int, classBits, 0);
+MESSAGE1(CGetChevClassNum, int, classNum, 0);
+MESSAGE2(CGetChevCodeFromRoomNameMsg, CString, roomName, "", uint, chevCode, 0);
+MESSAGE1(CGetChevFloorBits, int, floorBits, 0);
+MESSAGE1(CGetChevFloorNum, int, floorNum, 0);
+MESSAGE1(CGetChevLiftBits, int, liftBits, 0);
+MESSAGE1(CGetChevLiftNum, int, liftNum, 0);
+MESSAGE1(CGetChevRoomBits, int, roomNum, 0);
+MESSAGE1(CGetChevRoomNum, int, roomNum, 0);
+MESSAGE2(CHoseConnectedMsg, int, value, 1, CGameObject *, object, nullptr);
MESSAGE0(CInitializeAnimMsg);
MESSAGE1(CIsEarBowlPuzzleDone, int, value, 0);
MESSAGE3(CIsHookedOnMsg, Rect, rect, Rect(), bool, result, false, CString, string1, "");
@@ -288,7 +286,7 @@ MESSAGE2(CPlayRangeMsg, int, value1, 0, int, value2, 0);
MESSAGE2(CPlayerTriesRestaurantTableMsg, int, value1, 0, int, value2, 0);
MESSAGE1(CPreSaveMsg, int, value, 0);
MESSAGE1(CProdMaitreDMsg, int, value, 0);
-MESSAGE2(CPumpingMsg, int, value1, 0, int, value2, 0);
+MESSAGE2(CPumpingMsg, int, value, 0, CGameObject *, object, nullptr);
MESSAGE1(CPutBotBackInHisBoxMsg, int, value, 0);
MESSAGE1(CPutParrotBackMsg, int, value, 0);
MESSAGE0(CPuzzleSolvedMsg);
@@ -308,12 +306,12 @@ MESSAGE2(CServiceElevatorFloorChangeMsg, int, value1, 0, int, value2, 0);
MESSAGE0(CServiceElevatorFloorRequestMsg);
MESSAGE1(CServiceElevatorMsg, int, value, 4);
MESSAGE2(CSetChevButtonImageMsg, int, value1, 0, int, value2, 0);
-MESSAGE1(CSetChevClassBits, int, value, 0);
-MESSAGE1(CSetChevFloorBits, int, value, 0);
-MESSAGE1(CSetChevLiftBits, int, value, 0);
+MESSAGE1(CSetChevClassBits, int, classNum, 0);
+MESSAGE1(CSetChevFloorBits, int, floorNum, 0);
+MESSAGE1(CSetChevLiftBits, int, liftNum, 0);
MESSAGE2(CSetChevPanelBitMsg, int, value1, 0, int, value2, 0);
-MESSAGE1(CSetChevPanelButtonsMsg, int, value, 0);
-MESSAGE1(CSetChevRoomBits, int, value, 0);
+MESSAGE1(CSetChevPanelButtonsMsg, int, chevCode, 0);
+MESSAGE1(CSetChevRoomBits, int, roomNum, 0);
MESSAGE1(CSetFrameMsg, int, frameNumber, 0);
MESSAGE0(CSetMusicControlsMsg);
MESSAGE2(CSetVarMsg, CString, varName, "", int, value, 0);
diff --git a/engines/titanic/messages/mouse_messages.h b/engines/titanic/messages/mouse_messages.h
index d17bd51c78..e7c419bbdc 100644
--- a/engines/titanic/messages/mouse_messages.h
+++ b/engines/titanic/messages/mouse_messages.h
@@ -179,6 +179,32 @@ public:
}
};
+class CChildDragMoveMsg : public CMessage {
+public:
+ Point _mousePos;
+public:
+ CLASSDEF;
+ CChildDragMoveMsg() : CMessage() {}
+ CChildDragMoveMsg(const Point &pt) : CMessage(), _mousePos(pt) {}
+
+ static bool isSupportedBy(const CTreeItem *item) {
+ return supports(item, _type);
+ }
+};
+
+class CChildDragStartMsg : public CMessage {
+public:
+ Point _mousePos;
+public:
+ CLASSDEF;
+ CChildDragStartMsg() : CMessage() {}
+ CChildDragStartMsg(const Point &pt) : CMessage(), _mousePos(pt) {}
+
+ static bool isSupportedBy(const CTreeItem *item) {
+ return supports(item, _type);
+ }
+};
+
} // End of namespace Titanic
#endif /* TITANIC_MOUSE_MESSAGES_H */
diff --git a/engines/titanic/module.mk b/engines/titanic/module.mk
index 5c041174a2..a3afc7e4a4 100644
--- a/engines/titanic/module.mk
+++ b/engines/titanic/module.mk
@@ -81,7 +81,6 @@ MODULE_OBJS := \
game/arb_background.o \
game/arboretum_gate.o \
game/auto_animate.o \
- game/bilge_succubus.o \
game/bar_menu.o \
game/bar_menu_button.o \
game/bar_bell.o \
@@ -95,7 +94,6 @@ MODULE_OBJS := \
game/broken_pell_base.o \
game/broken_pellerator.o \
game/broken_pellerator_froz.o \
- game/call_pellerator.o \
game/cage.o \
game/captains_wheel.o \
game/cdrom.o \
@@ -221,7 +219,6 @@ MODULE_OBJS := \
game/parrot/parrot_nut_bowl_actor.o \
game/parrot/parrot_nut_eater.o \
game/parrot/parrot_perch_holder.o \
- game/parrot/parrot_succubus.o \
game/parrot/parrot_trigger.o \
game/parrot/player_meets_parrot.o \
game/pet/pet.o \
@@ -319,6 +316,7 @@ MODULE_OBJS := \
messages/messages.o \
messages/mouse_messages.o \
messages/service_elevator_door.o \
+ moves/call_pellerator.o \
moves/enter_bomb_room.o \
moves/enter_bridge.o \
moves/enter_exit_first_class_state.o \
@@ -343,6 +341,7 @@ MODULE_OBJS := \
moves/trip_down_canal.o \
npcs/barbot.o \
npcs/bellbot.o \
+ npcs/bilge_succubus.o \
npcs/callbot.o \
npcs/character.o \
npcs/deskbot.o \
@@ -351,6 +350,7 @@ MODULE_OBJS := \
npcs/maitre_d.o \
npcs/mobile.o \
npcs/parrot.o \
+ npcs/parrot_succubus.o \
npcs/robot_controller.o \
npcs/starlings.o \
npcs/succubus.o \
diff --git a/engines/titanic/game/call_pellerator.cpp b/engines/titanic/moves/call_pellerator.cpp
index 0ea48131b1..0dd8195277 100644
--- a/engines/titanic/game/call_pellerator.cpp
+++ b/engines/titanic/moves/call_pellerator.cpp
@@ -20,10 +20,17 @@
*
*/
-#include "titanic/game/call_pellerator.h"
+#include "titanic/moves/call_pellerator.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCallPellerator, CGameObject)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(PETActivateMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
void CCallPellerator::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CGameObject::save(file, indent);
@@ -34,4 +41,42 @@ void CCallPellerator::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CCallPellerator::EnterViewMsg(CEnterViewMsg *msg) {
+ petSetArea(PET_REMOTE);
+ petHighlightGlyph(1);
+ CString name = getFullViewName();
+
+ if (name == "TopOfWell.Node 6.S") {
+ petDisplayMessage(2, "You are standing outside the Pellerator.");
+ }
+
+ petSetRemoteTarget();
+ return true;
+}
+
+bool CCallPellerator::LeaveViewMsg(CLeaveViewMsg *msg) {
+ petClear();
+ return true;
+}
+
+bool CCallPellerator::PETActivateMsg(CPETActivateMsg *msg) {
+ CString name = getFullViewName();
+
+ if (msg->_name == "Pellerator") {
+ if (petDoorOrBellbotPresent()) {
+ petDisplayMessage("I'm sorry, you cannot enter this pellerator at present as a bot is in the way.");
+ } else if (name == "Bar.Node 1.S") {
+ changeView("Pellerator.Node 1.S");
+ } else {
+ changeView("Pellerator.Node 1.N");
+ }
+ }
+
+ return true;
+}
+
+bool CCallPellerator::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/call_pellerator.h b/engines/titanic/moves/call_pellerator.h
index 7da4b40794..3a1ef3823a 100644
--- a/engines/titanic/game/call_pellerator.h
+++ b/engines/titanic/moves/call_pellerator.h
@@ -24,10 +24,16 @@
#define TITANIC_CALL_PELLERATOR_H
#include "titanic/core/game_object.h"
+#include "titanic/messages/pet_messages.h"
namespace Titanic {
class CCallPellerator : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool PETActivateMsg(CPETActivateMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/moves/exit_pellerator.cpp b/engines/titanic/moves/exit_pellerator.cpp
index 68a2a8da91..12ca2c1e3c 100644
--- a/engines/titanic/moves/exit_pellerator.cpp
+++ b/engines/titanic/moves/exit_pellerator.cpp
@@ -21,9 +21,16 @@
*/
#include "titanic/moves/exit_pellerator.h"
+#include "titanic/game/transport/pellerator.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CExitPellerator, CGameObject)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(StatusChangeMsg)
+ ON_MESSAGE(ChangeSeasonMsg)
+END_MESSAGE_MAP()
+
CExitPelleratorStatics *CExitPellerator::_statics;
void CExitPellerator::init() {
@@ -38,7 +45,7 @@ void CExitPellerator::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeQuotedLine(_statics->_v1, indent);
file->writeNumberLine(_statics->_v2, indent);
- file->writeNumberLine(_statics->_v3, indent);
+ file->writeNumberLine(_statics->_isWinter, indent);
CGameObject::save(file, indent);
}
@@ -47,9 +54,84 @@ void CExitPellerator::load(SimpleFile *file) {
file->readNumber();
_statics->_v1 = file->readString();
_statics->_v2 = file->readNumber();
- _statics->_v3 = file->readNumber();
+ _statics->_isWinter = file->readNumber();
CGameObject::load(file);
}
+bool CExitPellerator::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ CString name = getName();
+
+ if (name == "ExitPellerator") {
+ if (_statics->_v2 != 2) {
+ switch (getRandomNumber(2)) {
+ case 0:
+ CPellerator::_soundHandle = queueSound("z#457.wav", CPellerator::_soundHandle);
+ break;
+ case 1:
+ CPellerator::_soundHandle = queueSound("z#458.wav", CPellerator::_soundHandle);
+ break;
+ default:
+ CPellerator::_soundHandle = queueSound("z#464.wav", CPellerator::_soundHandle);
+ break;
+ }
+ }
+
+ switch (_statics->_v2) {
+ case 0:
+ changeView("PromenadeDeck.Node 1.W");
+ break;
+ case 1:
+ changeView("MusicRoomLobby.Node 1.S");
+ break;
+ case 4:
+ changeView("TopOfWell.Node 6.N");
+ break;
+ case 5:
+ changeView("1stClassRestaurant.Lobby Node.E");
+ break;
+ case 6:
+ changeView(_statics->_isWinter ? "FrozenArboretum.Node 4.S" : "Arboretum.Node 4.W");
+ break;
+ default:
+ petDisplayMessage(2, "Please exit from the other side.");
+ CPellerator::_soundHandle = queueSound("z#438.wav", CPellerator::_soundHandle);
+
+ }
+ } else if (name == "ExitPellerator2") {
+ if (_statics->_v2 == 2) {
+ switch (getRandomNumber(2)) {
+ case 0:
+ CPellerator::_soundHandle = queueSound("z#457.wav", CPellerator::_soundHandle);
+ break;
+ case 1:
+ CPellerator::_soundHandle = queueSound("z#458.wav", CPellerator::_soundHandle);
+ break;
+ default:
+ CPellerator::_soundHandle = queueSound("z#464.wav", CPellerator::_soundHandle);
+ break;
+ }
+ }
+
+ if (_statics->_v2 == 2) {
+ changeView("Bar.Node 1.N");
+ } else {
+ petDisplayMessage(2, "Please exit from the other side.");
+ CPellerator::_soundHandle = queueSound("z#438.wav", CPellerator::_soundHandle);
+ }
+ }
+
+ return true;
+}
+
+bool CExitPellerator::StatusChangeMsg(CStatusChangeMsg *msg) {
+ _statics->_v2 = msg->_newStatus;
+ return true;
+}
+
+bool CExitPellerator::ChangeSeasonMsg(CChangeSeasonMsg *msg) {
+ _statics->_isWinter = msg->_season == "Winter";
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/moves/exit_pellerator.h b/engines/titanic/moves/exit_pellerator.h
index 280d1e9a6c..8819d64355 100644
--- a/engines/titanic/moves/exit_pellerator.h
+++ b/engines/titanic/moves/exit_pellerator.h
@@ -30,10 +30,14 @@ namespace Titanic {
struct CExitPelleratorStatics {
CString _v1;
int _v2;
- int _v3;
+ bool _isWinter;
};
class CExitPellerator : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
+ bool ChangeSeasonMsg(CChangeSeasonMsg *msg);
private:
static CExitPelleratorStatics *_statics;
public:
diff --git a/engines/titanic/npcs/bilge_succubus.cpp b/engines/titanic/npcs/bilge_succubus.cpp
new file mode 100644
index 0000000000..16064bf212
--- /dev/null
+++ b/engines/titanic/npcs/bilge_succubus.cpp
@@ -0,0 +1,467 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "titanic/npcs/bilge_succubus.h"
+#include "titanic/carry/chicken.h"
+#include "titanic/core/view_item.h"
+#include "titanic/pet_control/pet_control.h"
+
+namespace Titanic {
+
+BEGIN_MESSAGE_MAP(CBilgeSuccUBus, CSuccUBus)
+ ON_MESSAGE(FrameMsg)
+ ON_MESSAGE(PETReceiveMsg)
+ ON_MESSAGE(PETDeliverMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(SubAcceptCCarryMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(TrueTalkGetStateValueMsg)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+END_MESSAGE_MAP()
+
+CBilgeSuccUBus::CBilgeSuccUBus() : CSuccUBus(),
+ _bilgeStartFrame1(-1), _bilgeEndFrame1(-1),
+ _bilgeStartFrame2(-1), _bilgeEndFrame2(-1) {
+}
+
+void CBilgeSuccUBus::save(SimpleFile *file, int indent) {
+ file->writeNumberLine(1, indent);
+ file->writeNumberLine(_bilgeStartFrame1, indent);
+ file->writeNumberLine(_bilgeEndFrame1, indent);
+ file->writeNumberLine(_bilgeStartFrame2, indent);
+ file->writeNumberLine(_bilgeEndFrame2, indent);
+
+ CSuccUBus::save(file, indent);
+}
+
+void CBilgeSuccUBus::load(SimpleFile *file) {
+ file->readNumber();
+ _bilgeStartFrame1 = file->readNumber();
+ _bilgeEndFrame1 = file->readNumber();
+ _bilgeStartFrame2 = file->readNumber();
+ _bilgeEndFrame2 = file->readNumber();
+
+ CSuccUBus::load(file);
+}
+
+bool CBilgeSuccUBus::FrameMsg(CFrameMsg *msg) {
+ return true;
+}
+
+bool CBilgeSuccUBus::PETReceiveMsg(CPETReceiveMsg *msg) {
+ CPetControl *pet = getPetControl();
+
+ if (_v2) {
+ if (_startFrame4 >= 0)
+ playMovie(_startFrame4, _endFrame4, MOVIE_GAMESTATE);
+ if (_startFrame5 >= 0)
+ playMovie(_startFrame5, _endFrame5, MOVIE_GAMESTATE);
+
+ playSound("z#28.wav", 70);
+ } else if (!_enabled) {
+ petDisplayMessage(2, "The Succ-U-Bus is in Standby, or \"Off\" mode at present.");
+ return false;
+ } else if (!pet) {
+ return false;
+ } else {
+ uint roomFlags = pet->getRoomFlags();
+ CGameObject *mailObject = findMailByFlags(
+ _v3 && compareRoomNameTo("Titania") ? 3 : _field140,
+ roomFlags);
+
+ if (mailObject) {
+ _mailP = mailObject;
+ if (_startFrame4 >= 0)
+ playMovie(_startFrame4, _endFrame4, MOVIE_GAMESTATE);
+ } else {
+ petDisplayMessage(2, "There is currently nothing to deliver.");
+ }
+ }
+
+ return true;
+}
+
+bool CBilgeSuccUBus::PETDeliverMsg(CPETDeliverMsg *msg) {
+ CPetControl *pet = getPetControl();
+ if (!_enabled || !pet)
+ return true;
+
+ uint petRoomFlags = pet->getRoomFlags();
+ CGameObject *mailObject = findMail(petRoomFlags);
+
+ if (!mailObject) {
+ petDisplayMessage(2, "There is currently nothing in the tray to send.");
+ return true;
+ }
+
+ _field19C = 0;
+ _mailP = mailObject;
+
+ uint roomFlags = _roomFlags;
+ if (!pet->testRooms5(roomFlags) ||
+ getPassengerClass() > pet->getMailDest(roomFlags)) {
+ roomFlags = pet->getSpecialRoomFlags("BilgeRoom");
+ _field19C = 1;
+ }
+
+ _isChicken = mailObject->getName() == "Chicken";
+ _isFeathers = mailObject->getName() == "Feathers";
+ _field158 = 0;
+
+ if (_v2) {
+ if (_isFeathers) {
+ startTalking(this, 230022);
+ _field158 = 1;
+
+ if (_startFrame3 >= 0)
+ playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT);
+
+ if (_bilgeStartFrame1 >= 0) {
+ playMovie(_startFrame12, _endFrame12, MOVIE_GAMESTATE);
+ playMovie(_bilgeStartFrame2, _bilgeEndFrame2, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playMovie(_bilgeStartFrame1, _bilgeEndFrame1, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ inc54();
+ }
+ } else {
+ startTalking(this, 230012);
+ _field158 = 2;
+ if (_startFrame3 >= 0)
+ playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ if (_startFrame4 >= 0)
+ playMovie(_startFrame4, _endFrame4, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ if (_startFrame5 >= 0)
+ playMovie(_startFrame5, _endFrame5, MOVIE_GAMESTATE);
+ }
+ } else {
+ if (_isFeathers) {
+ startTalking(this, 230022);
+ _field158 = 3;
+
+ if (_startFrame3 >= 0)
+ playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ if (_startFrame4 >= 0)
+ playMovie(_startFrame4, _endFrame4, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ if (_startFrame5 >= 0)
+ playMovie(_startFrame5, _endFrame5, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ } else {
+ removeMail(petRoomFlags, roomFlags);
+ startTalking(this, 230012);
+ if (_startFrame3 >= 0) {
+ _field158 = 4;
+ playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CBilgeSuccUBus::MovieEndMsg(CMovieEndMsg *msg) {
+ CPetControl *pet = getPetControl();
+
+ if (msg->_endFrame == _endFrame12) {
+ if (_startFrame10 >= 0)
+ playSound("z#27.wav");
+ } else if (msg->_endFrame == _endFrame10) {
+ if (_startFrame11 >= 0)
+ playSound("z#30.wav");
+ } else {
+ if (_endFrame9 == _endFrame10 && pet) {
+ if (_v2) {
+ startTalking(this, getRandomNumber(1) ? 230062 : 230063);
+ } else if (!findMail(pet->getRoomFlags())) {
+ switch (getRandomNumber(4)) {
+ case 0:
+ startTalking(this, 230001);
+ break;
+ case 1:
+ startTalking(this, 230002);
+ break;
+ case 2:
+ startTalking(this, 230003);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (msg->_endFrame == _endFrame3) {
+ switch (_field158) {
+ case 1:
+ stopSound(_soundHandle);
+ _soundHandle = playSound("z#3.wav");
+ break;
+ case 2:
+ stopSound(_soundHandle);
+ _soundHandle = playSound("z#12.wav");
+ break;
+ case 3:
+ if (_isChicken) {
+ startTalking(this, 230018);
+ _isChicken = false;
+ } else {
+ startTalking(this, 230013);
+ }
+ break;
+ case 4:
+ startTalking(this, 230017);
+ break;
+ default:
+ break;
+ }
+
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+
+ } else if (msg->_endFrame == _bilgeEndFrame2) {
+ playSound("z#25.wav", 70);
+ playSound("z#24.wav", 70);
+
+ } else if (msg->_endFrame == _endFrame4) {
+ if (_mailP) {
+ _mailP->petAddToInventory();
+ CVisibleMsg visibleMsg(true);
+ visibleMsg.execute(_mailP);
+
+ _mailP = nullptr;
+ petSetArea(PET_INVENTORY);
+
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+ }
+
+ } else if (msg->_endFrame == _bilgeEndFrame1) {
+ changeView("BilgeRoomWith.Node 1.N", "");
+ _v2 = 0;
+ resetMail();
+
+ if (_mailP) {
+ _mailP->petAddToInventory();
+ CVisibleMsg visibleMsg(true);
+ visibleMsg.execute(_mailP);
+
+ _mailP = nullptr;
+ petSetArea(PET_INVENTORY);
+ }
+
+ startTalking(this, 150);
+ CBodyInBilgeRoomMsg bodyMsg;
+ bodyMsg.execute("Service Elevator Entity");
+ dec54();
+ _field158 = 0;
+
+ } else {
+ _field158 = 0;
+ }
+ }
+
+ return true;
+}
+
+bool CBilgeSuccUBus::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_enabled) {
+ switch (getRandomNumber(4)) {
+ case 0:
+ case 4: {
+ _enabled = false;
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ break;
+ }
+
+ case 1:
+ startTalking(this, 230055);
+ break;
+
+ case 2:
+ startTalking(this, 230067);
+ break;
+
+ case 3:
+ startTalking(this, 230045);
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ CTurnOn onMsg;
+ onMsg.execute(this);
+ _enabled = true;
+ }
+
+ return true;
+}
+
+bool CBilgeSuccUBus::SubAcceptCCarryMsg(CSubAcceptCCarryMsg *msg) {
+ CPetControl *pet = getPetControl();
+ if (!msg->_item)
+ return false;
+
+ CCarry *item = dynamic_cast<CCarry *>(msg->_item);
+ if (!_enabled || !pet || !item) {
+ item->petAddToInventory();
+ return true;
+ }
+
+ uint petRoomFlags = pet->getRoomFlags();
+ if (mailExists(petRoomFlags)) {
+ petDisplayMessage(2, "The Succ-U-Bus is a Single Entity Delivery Device.");
+ item->petAddToInventory();
+ return true;
+ }
+
+ petContainerRemove(item);
+ pet->phonographAction("");
+ playSound("z#23.wav");
+
+ CChicken *chicken = dynamic_cast<CChicken *>(item);
+ bool chickenFlag = chicken ? chicken->_string6 == "None" : false;
+
+ if (chickenFlag) {
+ if (_startFrame2 >= 0) {
+ startTalking(this, 70219);
+ playMovie(_startFrame2, _endFrame2, 0);
+ }
+
+ if (_startFrame3 >= 0) {
+ _field158 = 5;
+ playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT);
+ }
+
+ CViewItem *view = parseView(item->_fullViewName);
+ if (view) {
+ item->setVisible(false);
+ setPosition(item->_origPos);
+ item->moveUnder(view);
+
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+ } else {
+ return false;
+ }
+ } else {
+ item->addMail(petRoomFlags);
+ if (_startFrame2 >= 0)
+ playMovie(_startFrame2, _endFrame2, 0);
+
+ petSetArea(PET_REMOTE);
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+ }
+
+ return true;
+}
+
+bool CBilgeSuccUBus::EnterViewMsg(CEnterViewMsg *msg) {
+ petSetRemoteTarget();
+ _mailP = nullptr;
+
+ if (_startFrame8 >= 0)
+ loadFrame(_startFrame8);
+
+ return true;
+}
+
+bool CBilgeSuccUBus::LeaveViewMsg(CLeaveViewMsg *msg) {
+ petDisplayMessage(2, "");
+ petClear();
+
+ if (_soundHandle != -1) {
+ stopSound(_soundHandle);
+ _soundHandle = -1;
+ }
+
+ if (_enabled) {
+ _enabled = false;
+ if (_startFrame10 >= 0)
+ playSound("z#27.wav");
+ }
+
+ performAction(true);
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+
+ return true;
+}
+
+bool CBilgeSuccUBus::TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg) {
+ if (msg->_stateNum == 1)
+ msg->_stateVal = _enabled;
+
+ return true;
+}
+
+bool CBilgeSuccUBus::TurnOn(CTurnOn *msg) {
+ CPetControl *pet = getPetControl();
+
+ if (pet) {
+ if (_startFrame9 >= 0) {
+ playMovie(_startFrame9, _endFrame9, MOVIE_NOTIFY_OBJECT);
+ playSound("z#26.wav");
+ }
+
+ if (mailExists(pet->getRoomFlags()) && _startFrame2 >= 0)
+ playMovie(_startFrame2, _endFrame2, 0);
+
+ _enabled = true;
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+
+ endTalking(this, true);
+ petSetArea(PET_REMOTE);
+ petHighlightGlyph(16);
+ }
+
+ return true;
+}
+
+bool CBilgeSuccUBus::TurnOff(CTurnOff *msg) {
+ CPetControl *pet = getPetControl();
+
+ if (pet && mailExists(pet->getRoomFlags()) && _startFrame12 >= 0)
+ playMovie(_startFrame12, _endFrame12, MOVIE_NOTIFY_OBJECT);
+ else if (_endFrame12 >= 0)
+ playMovie(_endFrame12, _endFrame12, MOVIE_NOTIFY_OBJECT);
+
+ if (_soundHandle != -1) {
+ stopSound(_soundHandle);
+ _soundHandle = -1;
+ }
+
+ if (_startFrame10 >= 0)
+ playMovie(_startFrame10, _endFrame10, MOVIE_NOTIFY_OBJECT);
+
+ _enabled = false;
+ performAction(true);
+
+ CSUBTransition transMsg;
+ transMsg.execute(this);
+
+ return true;
+}
+
+} // End of namespace Titanic
diff --git a/engines/titanic/game/bilge_succubus.h b/engines/titanic/npcs/bilge_succubus.h
index 4b2a626dc2..754949a306 100644
--- a/engines/titanic/game/bilge_succubus.h
+++ b/engines/titanic/npcs/bilge_succubus.h
@@ -28,11 +28,23 @@
namespace Titanic {
class CBilgeSuccUBus : public CSuccUBus {
+ DECLARE_MESSAGE_MAP;
+ bool FrameMsg(CFrameMsg *msg);
+ bool PETReceiveMsg(CPETReceiveMsg *msg);
+ bool PETDeliverMsg(CPETDeliverMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool SubAcceptCCarryMsg(CSubAcceptCCarryMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg);
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
public:
- int _field1DC;
- int _field1E0;
- int _field1E4;
- int _field1E8;
+ int _bilgeStartFrame1;
+ int _bilgeEndFrame1;
+ int _bilgeStartFrame2;
+ int _bilgeEndFrame2;
public:
CLASSDEF;
CBilgeSuccUBus();
diff --git a/engines/titanic/npcs/liftbot.cpp b/engines/titanic/npcs/liftbot.cpp
index fbaa60d800..3d6321bfb5 100644
--- a/engines/titanic/npcs/liftbot.cpp
+++ b/engines/titanic/npcs/liftbot.cpp
@@ -124,8 +124,7 @@ bool CLiftBot::TurnOn(CTurnOn *msg) {
_enabled = true;
if (!_flag) {
if (compareTo("LiftBotTalking", 0)) {
- CViewItem *view = findView();
- endTalking(this, MOVIE_REPEAT);
+ endTalking(this, MOVIE_REPEAT, findView());
petSetArea(PET_CONVERSATION);
_flag = true;
}
diff --git a/engines/titanic/npcs/parrot_succubus.cpp b/engines/titanic/npcs/parrot_succubus.cpp
new file mode 100644
index 0000000000..5f67b8f44c
--- /dev/null
+++ b/engines/titanic/npcs/parrot_succubus.cpp
@@ -0,0 +1,152 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "titanic/npcs/parrot_succubus.h"
+#include "titanic/pet_control/pet_control.h"
+#include "titanic/carry/hose.h"
+
+namespace Titanic {
+
+BEGIN_MESSAGE_MAP(CParrotSuccUBus, CSuccUBus)
+ ON_MESSAGE(HoseConnectedMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LeaveNodeMsg)
+END_MESSAGE_MAP()
+
+CParrotSuccUBus::CParrotSuccUBus() : CSuccUBus(), _field1DC(0),
+ _field1EC(0), _field1F0(376), _field1F4(393) {
+}
+
+void CParrotSuccUBus::save(SimpleFile *file, int indent) {
+ file->writeNumberLine(1, indent);
+ file->writeNumberLine(_field1DC, indent);
+ file->writeQuotedLine(_string3, indent);
+ file->writeNumberLine(_field1EC, indent);
+
+ CSuccUBus::save(file, indent);
+}
+
+void CParrotSuccUBus::load(SimpleFile *file) {
+ file->readNumber();
+ _field1DC = file->readNumber();
+ _string3 = file->readString();
+ _field1EC = file->readNumber();
+
+ CSuccUBus::load(file);
+}
+
+bool CParrotSuccUBus::HoseConnectedMsg(CHoseConnectedMsg *msg) {
+ CPetControl *pet = getPetControl();
+ if (msg->_value == _field1DC)
+ return true;
+ if (mailExists(pet->getRoomFlags()))
+ return false;
+
+ _field1DC = msg->_value;
+ if (_field1DC) {
+ CGameObject *item = msg->_object;
+ _string3 = item->getName();
+ CHoseConnectedMsg hoseMsg(1, this);
+ hoseMsg.execute(msg->_object);
+ item->petMoveToHiddenRoom();
+
+ CPumpingMsg pumpingMsg(1, this);
+ pumpingMsg.execute(this);
+ _field1DC = 1;
+
+ if (_enabled) {
+ _enabled = false;
+ } else {
+ playMovie(_startFrame9, _endFrame9, 0);
+ playSound("z#26.wav");
+ }
+
+ playMovie(_field1C4, _field1C8, MOVIE_NOTIFY_OBJECT);
+ } else {
+ stopMovie();
+ stopSound(_field1EC);
+ playMovie(_field1F0, _field1F4, MOVIE_NOTIFY_OBJECT);
+
+ CPumpingMsg pumpingMsg(0, this);
+ pumpingMsg.execute(_string3);
+
+ CGameObject *obj = getHiddenObject(_string3);
+ if (obj) {
+ obj->petAddToInventory();
+ obj->setVisible(true);
+ }
+
+ _enabled = true;
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ }
+
+ return true;
+}
+
+bool CParrotSuccUBus::EnterViewMsg(CEnterViewMsg *msg) {
+ if (_field1DC) {
+ playMovie(_field1CC, _field1D0, MOVIE_REPEAT);
+ return true;
+ } else {
+ return CSuccUBus::EnterViewMsg(msg);
+ }
+}
+
+bool CParrotSuccUBus::MovieEndMsg(CMovieEndMsg *msg) {
+ if (msg->_endFrame == _field1C8) {
+ playMovie(_field1CC, _field1D0, MOVIE_REPEAT);
+ _field1EC = playSound("z#472.wav");
+ return true;
+ } else {
+ return CSuccUBus::MovieEndMsg(msg);
+ }
+}
+
+bool CParrotSuccUBus::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_field1DC) {
+ CHoseConnectedMsg hoseMsg;
+ hoseMsg._value = 0;
+ hoseMsg.execute(this);
+ return true;
+ } else {
+ return CSuccUBus::MouseButtonDownMsg(msg);
+ }
+}
+
+bool CParrotSuccUBus::LeaveNodeMsg(CLeaveNodeMsg *msg) {
+ if (_field1DC) {
+ getHiddenObject(_string3);
+ if (CHose::_statics->_v2.empty()) {
+ playSound("z#51.wav");
+ CHoseConnectedMsg hoseMsg;
+ hoseMsg._value = 0;
+ hoseMsg.execute(this);
+ }
+ }
+
+ return true;
+}
+
+} // End of namespace Titanic
diff --git a/engines/titanic/game/parrot/parrot_succubus.h b/engines/titanic/npcs/parrot_succubus.h
index 6f5d9e602a..74a4a032eb 100644
--- a/engines/titanic/game/parrot/parrot_succubus.h
+++ b/engines/titanic/npcs/parrot_succubus.h
@@ -28,6 +28,12 @@
namespace Titanic {
class CParrotSuccUBus : public CSuccUBus {
+ DECLARE_MESSAGE_MAP;
+ bool HoseConnectedMsg(CHoseConnectedMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LeaveNodeMsg(CLeaveNodeMsg *msg);
public:
int _field1DC;
CString _string3;
diff --git a/engines/titanic/npcs/succubus.cpp b/engines/titanic/npcs/succubus.cpp
index 75afb1b273..872b4f33ba 100644
--- a/engines/titanic/npcs/succubus.cpp
+++ b/engines/titanic/npcs/succubus.cpp
@@ -69,11 +69,11 @@ CSuccUBus::CSuccUBus() : CTrueTalkNPC() {
_startFrame2 = 40;
_endFrame2 = 68;
_field140 = 1;
- _field144 = 0;
+ _mailP = nullptr;
_startFrame5 = 0;
_endFrame5 = 0;
- _field150 = 0xE0;
- _field154 = 0;
+ _startFrame12 = 224;
+ _endFrame12 = 248;
_field158 = 0;
_field15C = 0;
_string2 = "NULL";
@@ -124,8 +124,8 @@ void CSuccUBus::save(SimpleFile *file, int indent) {
file->writeNumberLine(_v2, indent);
file->writeNumberLine(_startFrame5, indent);
file->writeNumberLine(_endFrame5, indent);
- file->writeNumberLine(_field150, indent);
- file->writeNumberLine(_field154, indent);
+ file->writeNumberLine(_startFrame12, indent);
+ file->writeNumberLine(_endFrame12, indent);
file->writeNumberLine(_field158, indent);
file->writeNumberLine(_field15C, indent);
@@ -188,8 +188,8 @@ void CSuccUBus::load(SimpleFile *file) {
_v2 = file->readNumber();
_startFrame5 = file->readNumber();
_endFrame5 = file->readNumber();
- _field150 = file->readNumber();
- _field154 = file->readNumber();
+ _startFrame12 = file->readNumber();
+ _endFrame12 = file->readNumber();
_field158 = file->readNumber();
_field15C = file->readNumber();
@@ -238,12 +238,12 @@ bool CSuccUBus::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
CTurnOn onMsg;
onMsg.execute(this);
_enabled = true;
- } else if (getNewRandomNumber(256) < 130) {
+ } else if (getRandomNumber(256) < 130) {
_enabled = false;
CTurnOff offMsg;
offMsg.execute(this);
} else {
- switch (getNewRandomNumber(2)) {
+ switch (getRandomNumber(2)) {
case 0:
startTalking(this, 230055, findView());
break;
@@ -330,7 +330,7 @@ bool CSuccUBus::EnterViewMsg(CEnterViewMsg *msg) {
}
petSetRemoteTarget();
- _field144 = nullptr;
+ _mailP = nullptr;
if (_startFrame8 >= 0)
loadFrame(_startFrame8);
@@ -429,7 +429,7 @@ bool CSuccUBus::PETDeliverMsg(CPETDeliverMsg *msg) {
playMovie(_startFrame3, _endFrame3, 0);
if (_startFrame4 >= 0) {
- _field144 = mailObject;
+ _mailP = mailObject;
playMovie(_startFrame4, _endFrame4, MOVIE_NOTIFY_OBJECT);
}
@@ -559,7 +559,7 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) {
stopSound(_soundHandle);
_soundHandle = -1;
- switch (getNewRandomNumber(_v2 ? 7 : 5, &_field1B0)) {
+ switch (getRandomNumber(_v2 ? 7 : 5, &_field1B0)) {
case 2:
startTalking(this, 230001, findView());
break;
@@ -609,12 +609,12 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) {
}
if (msg->_endFrame == _endFrame4) {
- if (pet && _field144) {
- _field144->setMailId(petRoomFlags);
+ if (pet && _mailP) {
+ _mailP->setMailId(petRoomFlags);
}
_field188 = 1;
- _field144 = 0;
+ _mailP = 0;
if (_field1D8) {
_field1D8 = 0;
dec54();
@@ -727,7 +727,7 @@ bool CSuccUBus::SUBTransition(CSUBTransition *msg) {
bool CSuccUBus::SetChevRoomBits(CSetChevRoomBits *msg) {
if (_enabled) {
- _roomFlags = msg->_value;
+ _roomFlags = msg->_roomNum;
playSound("z#98.wav", 100);
}
diff --git a/engines/titanic/npcs/succubus.h b/engines/titanic/npcs/succubus.h
index b8e4c316a9..7ca8037a0a 100644
--- a/engines/titanic/npcs/succubus.h
+++ b/engines/titanic/npcs/succubus.h
@@ -45,13 +45,13 @@ class CSuccUBus : public CTrueTalkNPC {
bool SetChevRoomBits(CSetChevRoomBits *msg);
bool ActMsg(CActMsg *msg);
bool MouseDragStartMsg(CMouseDragStartMsg *msg);
-private:
+protected:
static bool _enabled;
static int _v1;
static int _v2;
static int _v3;
static int _v4;
-private:
+protected:
int _startFrame8;
int _endFrame8;
int _startFrame11;
@@ -67,11 +67,11 @@ private:
int _startFrame2;
int _endFrame2;
int _field140;
- CGameObject *_field144;
+ CGameObject *_mailP;
int _startFrame5;
int _endFrame5;
- int _field150;
- int _field154;
+ int _startFrame12;
+ int _endFrame12;
int _field158;
bool _field15C;
CString _string2;
diff --git a/engines/titanic/support/string.cpp b/engines/titanic/support/string.cpp
index cd39c03861..1400c25733 100644
--- a/engines/titanic/support/string.cpp
+++ b/engines/titanic/support/string.cpp
@@ -122,4 +122,20 @@ CString CString::format(const char *fmt, ...) {
return output;
}
+bool CString::operator==(const CString &x) const {
+ return compareToIgnoreCase(x) == 0;
+}
+
+bool CString::operator==(const char *x) const {
+ return compareToIgnoreCase(x) == 0;
+}
+
+bool CString::operator!=(const CString &x) const {
+ return compareToIgnoreCase(x) != 0;
+}
+
+bool CString::operator!=(const char *x) const {
+ return compareToIgnoreCase(x) != 0;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/support/string.h b/engines/titanic/support/string.h
index 9550ce88a7..487c138358 100644
--- a/engines/titanic/support/string.h
+++ b/engines/titanic/support/string.h
@@ -49,6 +49,11 @@ public:
explicit CString(char c) : Common::String(c) {}
explicit CString(int val);
+ bool operator==(const CString &x) const;
+ bool operator==(const char *x) const;
+ bool operator!=(const CString &x) const;
+ bool operator!=(const char *x) const;
+
/**
* Returns the left n characters of the string
*/