[Scummvm-git-logs] scummvm master -> 6bab26725ee6faf5ed1c005af1e364624459993a
sev-
noreply at scummvm.org
Mon Apr 6 23:11:41 UTC 2026
This automated email contains information about 29 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
6752e7286d ENGINES: BOLT: Add engine stub and detection
22990cc4ee BOLT: CARNIVAL: Initial engine function stubs
f204ad4fdf BOLT: Stub the entirety of the XPLIB.DLL code
2745133738 BOLT: Implement the entirety of XPLIB.DLL except sound
e358ac0d46 BOLT: CARNIVAL: Implement main part of the game engine
efb9fe5455 BOLT: CARNIVAL: Fix screensaver dimming routine
ca9d735bd4 BOLT: CARNIVAL: Fix palette setup when playing a video over a booth
8f27afd84e BOLT: CARNIVAL: Implement Fred's minigame
584db066a0 BOLT: CARNIVAL: Implement TopCat's minigame
1ee07b6140 BOLT: CARNIVAL: Add support for full Italian version
cc069232f4 BOLT: CARNIVAL: Implement Scooby-Doo's minigame
bb96b5c22d BOLT: CARNIVAL: Fix Fred's minigame regression
4ae8a3557a BOLT: CARNIVAL: Implement George's minigame
5a08cf4461 BOLT: CARNIVAL: Implement Huck's minigame
d0c9be04ab BOLT: CARNIVAL: Implement Yogi's minigame
9f25c67c6d BOLT: CARNIVAL: Final cleanup
a4e8528b28 BOLT: Fix module.mk
564a389c58 BOLT: Fix linux build
556f126ceb BOLT: Fix warnings
7960ce109f BOLT: Clean-up detection
b31b7569bb BOLT: Fix assets not being found in release build
87e6e8d8c1 BOLT: CARNIVAL: Some fixes and clean-ups
9fb3cfc755 BOLT: CARNIVAL: Fix more bugs
5b289038dc BOLT: Reduce include list in detection.cpp
f21d8e6800 BOLT: Add myself to engine credits
eb07f69e08 BOLT: Reduce number of files for detection
8e4480acb2 BOLT: Don't build the engine by default
14ceee7511 BOLT: Tidy up includes order
6bab26725e BOLT: Address more review comments
Commit: 6752e7286d00e4447d5f7f232802b0ed95d23302
https://github.com/scummvm/scummvm/commit/6752e7286d00e4447d5f7f232802b0ed95d23302
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
ENGINES: BOLT: Add engine stub and detection
Changed paths:
A engines/bolt/POTFILES
A engines/bolt/bolt.cpp
A engines/bolt/bolt.h
A engines/bolt/configure.engine
A engines/bolt/console.cpp
A engines/bolt/console.h
A engines/bolt/credits.pl
A engines/bolt/detection.cpp
A engines/bolt/detection.h
A engines/bolt/detection_tables.h
A engines/bolt/metaengine.cpp
A engines/bolt/metaengine.h
A engines/bolt/module.mk
diff --git a/engines/bolt/POTFILES b/engines/bolt/POTFILES
new file mode 100644
index 00000000000..980f96f1cd6
--- /dev/null
+++ b/engines/bolt/POTFILES
@@ -0,0 +1 @@
+engines/bolt/metaengine.cpp
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
new file mode 100644
index 00000000000..9df339fdd00
--- /dev/null
+++ b/engines/bolt/bolt.cpp
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "graphics/framelimiter.h"
+#include "bolt/detection.h"
+#include "bolt/console.h"
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/paletteman.h"
+
+namespace Bolt {
+
+BoltEngine *g_engine;
+
+BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
+ _gameDescription(gameDesc), _randomSource("Bolt") {
+ g_engine = this;
+}
+
+BoltEngine::~BoltEngine() {
+ delete _screen;
+}
+
+uint32 BoltEngine::getFeatures() const {
+ return _gameDescription->flags;
+}
+
+Common::String BoltEngine::getGameId() const {
+ return _gameDescription->gameId;
+}
+
+Common::Error BoltEngine::run() {
+ // Initialize 320x200 paletted graphics mode
+ initGraphics(320, 200);
+ _screen = new Graphics::Screen();
+
+ // Set the engine's debugger console
+ setDebugger(new Console());
+
+ // If a savegame was selected from the launcher, load it
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot != -1)
+ (void)loadGameState(saveSlot);
+
+ // Draw a series of boxes on screen as a sample
+ for (int i = 0; i < 100; ++i)
+ _screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
+ _screen->update();
+
+ // Simple event handling loop
+ byte pal[256 * 3] = { 0 };
+ Common::Event e;
+ int offset = 0;
+
+ Graphics::FrameLimiter limiter(g_system, 60);
+ while (!shouldQuit()) {
+ while (g_system->getEventManager()->pollEvent(e)) {
+ }
+
+ // Cycle through a simple palette
+ ++offset;
+ for (int i = 0; i < 256; ++i)
+ pal[i * 3 + 1] = (i + offset) % 256;
+ g_system->getPaletteManager()->setPalette(pal, 0, 256);
+ // Delay for a bit. All events loops should have a delay
+ // to prevent the system being unduly loaded
+ limiter.delayBeforeSwap();
+ _screen->update();
+ limiter.startFrame();
+ }
+
+ return Common::kNoError;
+}
+
+Common::Error BoltEngine::syncGame(Common::Serializer &s) {
+ // The Serializer has methods isLoading() and isSaving()
+ // if you need to specific steps; for example setting
+ // an array size after reading it's length, whereas
+ // for saving it would write the existing array's length
+ int dummy = 0;
+ s.syncAsUint32LE(dummy);
+
+ return Common::kNoError;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
new file mode 100644
index 00000000000..9c6b11b12d9
--- /dev/null
+++ b/engines/bolt/bolt.h
@@ -0,0 +1,105 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_H
+#define BOLT_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "common/random.h"
+#include "common/serializer.h"
+#include "common/util.h"
+#include "engines/engine.h"
+#include "engines/savestate.h"
+#include "graphics/screen.h"
+
+#include "bolt/detection.h"
+
+namespace Bolt {
+
+struct BoltGameDescription;
+
+class BoltEngine : public Engine {
+private:
+ const ADGameDescription *_gameDescription;
+ Common::RandomSource _randomSource;
+protected:
+ // Engine APIs
+ Common::Error run() override;
+public:
+ Graphics::Screen *_screen = nullptr;
+public:
+ BoltEngine(OSystem *syst, const ADGameDescription *gameDesc);
+ ~BoltEngine() override;
+
+ uint32 getFeatures() const;
+
+ /**
+ * Returns the game Id
+ */
+ Common::String getGameId() const;
+
+ /**
+ * Gets a random number
+ */
+ uint32 getRandomNumber(uint maxNum) {
+ return _randomSource.getRandomNumber(maxNum);
+ }
+
+ bool hasFeature(EngineFeature f) const override {
+ return
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime) ||
+ (f == kSupportsReturnToLauncher);
+ };
+
+ bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
+ return true;
+ }
+ bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
+ return true;
+ }
+
+ /**
+ * Uses a serializer to allow implementing savegame
+ * loading and saving using a single method
+ */
+ Common::Error syncGame(Common::Serializer &s);
+
+ Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
+ Common::Serializer s(nullptr, stream);
+ return syncGame(s);
+ }
+ Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
+ Common::Serializer s(stream, nullptr);
+ return syncGame(s);
+ }
+};
+
+extern BoltEngine *g_engine;
+#define SHOULD_QUIT ::Bolt::g_engine->shouldQuit()
+
+} // End of namespace Bolt
+
+#endif // BOLT_H
diff --git a/engines/bolt/configure.engine b/engines/bolt/configure.engine
new file mode 100644
index 00000000000..6629b5b25b9
--- /dev/null
+++ b/engines/bolt/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
+add_engine bolt "Bolt" no "" "" "" ""
diff --git a/engines/bolt/console.cpp b/engines/bolt/console.cpp
new file mode 100644
index 00000000000..2bb36865027
--- /dev/null
+++ b/engines/bolt/console.cpp
@@ -0,0 +1,38 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/console.h"
+
+namespace Bolt {
+
+Console::Console() : GUI::Debugger() {
+ registerCmd("test", WRAP_METHOD(Console, Cmd_test));
+}
+
+Console::~Console() {
+}
+
+bool Console::Cmd_test(int argc, const char **argv) {
+ debugPrintf("Test\n");
+ return true;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/console.h b/engines/bolt/console.h
new file mode 100644
index 00000000000..c697e77a1fd
--- /dev/null
+++ b/engines/bolt/console.h
@@ -0,0 +1,40 @@
+
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_CONSOLE_H
+#define BOLT_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Bolt {
+
+class Console : public GUI::Debugger {
+private:
+ bool Cmd_test(int argc, const char **argv);
+public:
+ Console();
+ ~Console() override;
+};
+
+} // End of namespace Bolt
+
+#endif // BOLT_CONSOLE_H
diff --git a/engines/bolt/credits.pl b/engines/bolt/credits.pl
new file mode 100644
index 00000000000..3e137cd1a0f
--- /dev/null
+++ b/engines/bolt/credits.pl
@@ -0,0 +1,3 @@
+begin_section("Bolt");
+ add_person("Name 1", "Handle 1", "");
+end_section();
diff --git a/engines/bolt/detection.cpp b/engines/bolt/detection.cpp
new file mode 100644
index 00000000000..4defbfd281f
--- /dev/null
+++ b/engines/bolt/detection.cpp
@@ -0,0 +1,45 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "base/plugins.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "common/str-array.h"
+#include "common/translation.h"
+#include "common/util.h"
+#include "bolt/detection.h"
+#include "bolt/detection_tables.h"
+
+const DebugChannelDef BoltMetaEngineDetection::debugFlagList[] = {
+ { Bolt::kDebugGraphics, "Graphics", "Graphics debug level" },
+ { Bolt::kDebugPath, "Path", "Pathfinding debug level" },
+ { Bolt::kDebugFilePath, "FilePath", "File path debug level" },
+ { Bolt::kDebugScan, "Scan", "Scan for unrecognised games" },
+ { Bolt::kDebugScript, "Script", "Enable debug script dump" },
+ DEBUG_CHANNEL_END
+};
+
+BoltMetaEngineDetection::BoltMetaEngineDetection() : AdvancedMetaEngineDetection(
+ Bolt::gameDescriptions, Bolt::boltGames) {
+}
+
+REGISTER_PLUGIN_STATIC(BOLT_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, BoltMetaEngineDetection);
diff --git a/engines/bolt/detection.h b/engines/bolt/detection.h
new file mode 100644
index 00000000000..ead614b2ab2
--- /dev/null
+++ b/engines/bolt/detection.h
@@ -0,0 +1,69 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_DETECTION_H
+#define BOLT_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Bolt {
+
+enum BoltDebugChannels {
+ kDebugGraphics = 1,
+ kDebugPath,
+ kDebugScan,
+ kDebugFilePath,
+ kDebugScript,
+};
+
+extern const PlainGameDescriptor boltGames[];
+
+extern const ADGameDescription gameDescriptions[];
+
+#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+
+} // End of namespace Bolt
+
+class BoltMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
+ static const DebugChannelDef debugFlagList[];
+
+public:
+ BoltMetaEngineDetection();
+ ~BoltMetaEngineDetection() override {}
+
+ const char *getName() const override {
+ return "bolt";
+ }
+
+ const char *getEngineName() const override {
+ return "Bolt";
+ }
+
+ const char *getOriginalCopyright() const override {
+ return "Bolt (C)";
+ }
+
+ const DebugChannelDef *getDebugChannels() const override {
+ return debugFlagList;
+ }
+};
+
+#endif // BOLT_DETECTION_H
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
new file mode 100644
index 00000000000..f3785c88ad5
--- /dev/null
+++ b/engines/bolt/detection_tables.h
@@ -0,0 +1,100 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Bolt {
+
+const PlainGameDescriptor boltGames[] = {
+ { "carnival", "Cartoon Carnival" },
+ { 0, 0 }
+};
+
+const ADGameDescription gameDescriptions[] = {
+ {
+ "carnival",
+ nullptr,
+ {
+ {"BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
+ {"FRED.BLT", 0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
+ {"GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
+ {"HUCK.BLT", 0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
+ {"SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
+ {"TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
+ {"YOGI.BLT", 0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
+ AD_LISTEND
+ },
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO1(GUIO_NONE)
+ },
+ {
+ "carnival",
+ nullptr,
+ {
+ {"ASSETS/BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
+ {"ASSETS/FRED.BLT", 0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
+ {"ASSETS/GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
+ {"ASSETS/HUCK.BLT", 0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
+ {"ASSETS/SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
+ {"ASSETS/TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
+ {"ASSETS/YOGI.BLT", 0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
+ AD_LISTEND
+ },
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO1(GUIO_NONE)
+ },
+ {
+ "carnival",
+ "Demo",
+ {
+ {"BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
+ {"GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
+ {"SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
+ {"YOGI.BLT", 0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE | ADGF_DEMO,
+ GUIO1(GUIO_NONE)
+ },
+ {
+ "carnival",
+ "Demo",
+ {
+ {"ASSETS/BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
+ {"ASSETS/GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
+ {"ASSETS/SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
+ {"ASSETS/YOGI.BLT", 0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE | ADGF_DEMO,
+ GUIO1(GUIO_NONE)
+ },
+
+ AD_TABLE_END_MARKER
+};
+
+} // End of namespace Bolt
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
new file mode 100644
index 00000000000..9beb569542f
--- /dev/null
+++ b/engines/bolt/metaengine.cpp
@@ -0,0 +1,69 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/translation.h"
+
+#include "bolt/metaengine.h"
+#include "bolt/detection.h"
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_ORIGINAL_SAVELOAD,
+ {
+ _s("Use original save/load screens"),
+ _s("Use the original save/load screens instead of the ScummVM ones"),
+ "original_menus",
+ false,
+ 0,
+ 0
+ }
+ },
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+} // End of namespace Bolt
+
+const char *BoltMetaEngine::getName() const {
+ return "bolt";
+}
+
+const ADExtraGuiOptionsMap *BoltMetaEngine::getAdvancedExtraGuiOptions() const {
+ return Bolt::optionsList;
+}
+
+Common::Error BoltMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ *engine = new Bolt::BoltEngine(syst, desc);
+ return Common::kNoError;
+}
+
+bool BoltMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return checkExtendedSaves(f) ||
+ (f == kSupportsLoadingDuringStartup);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(BOLT)
+REGISTER_PLUGIN_DYNAMIC(BOLT, PLUGIN_TYPE_ENGINE, BoltMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(BOLT, PLUGIN_TYPE_ENGINE, BoltMetaEngine);
+#endif
diff --git a/engines/bolt/metaengine.h b/engines/bolt/metaengine.h
new file mode 100644
index 00000000000..e9228d86bf5
--- /dev/null
+++ b/engines/bolt/metaengine.h
@@ -0,0 +1,43 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_METAENGINE_H
+#define BOLT_METAENGINE_H
+
+#include "engines/advancedDetector.h"
+
+class BoltMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
+public:
+ const char *getName() const override;
+
+ Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+
+ /**
+ * Determine whether the engine supports the specified MetaEngine feature.
+ *
+ * Used by e.g. the launcher to determine whether to enable the Load button.
+ */
+ bool hasFeature(MetaEngineFeature f) const override;
+
+ const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
+};
+
+#endif // BOLT_METAENGINE_H
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
new file mode 100644
index 00000000000..82c0d83fe01
--- /dev/null
+++ b/engines/bolt/module.mk
@@ -0,0 +1,17 @@
+MODULE := engines/bolt
+
+MODULE_OBJS = \
+ bolt.o \
+ console.o \
+ metaengine.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_BOLT), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o
Commit: 22990cc4ee7004cda9c105ee1716fad6af34083a
https://github.com/scummvm/scummvm/commit/22990cc4ee7004cda9c105ee1716fad6af34083a
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Initial engine function stubs
Changed paths:
A engines/bolt/booth.cpp
A engines/bolt/gfx.cpp
A engines/bolt/resource.cpp
A engines/bolt/video.cpp
A engines/bolt/xplib/xp_cursor.cpp
A engines/bolt/xplib/xp_events.cpp
A engines/bolt/xplib/xp_file.cpp
A engines/bolt/xplib/xp_gfx.cpp
A engines/bolt/xplib/xp_mem.cpp
A engines/bolt/xplib/xp_sound.cpp
A engines/bolt/xplib/xplib.cpp
A engines/bolt/xplib/xplib.h
engines/bolt/bolt.cpp
engines/bolt/bolt.h
engines/bolt/module.mk
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 9df339fdd00..19dfdd81f81 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -31,6 +31,8 @@
#include "engines/util.h"
#include "graphics/paletteman.h"
+#include "bolt/xplib/xplib.h"
+
namespace Bolt {
BoltEngine *g_engine;
@@ -38,10 +40,12 @@ BoltEngine *g_engine;
BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc), _randomSource("Bolt") {
g_engine = this;
+ _xp = new XpLib(this);
}
BoltEngine::~BoltEngine() {
delete _screen;
+ delete _xp;
}
uint32 BoltEngine::getFeatures() const {
@@ -57,40 +61,41 @@ Common::Error BoltEngine::run() {
initGraphics(320, 200);
_screen = new Graphics::Screen();
- // Set the engine's debugger console
- setDebugger(new Console());
-
- // If a savegame was selected from the launcher, load it
- int saveSlot = ConfMan.getInt("save_slot");
- if (saveSlot != -1)
- (void)loadGameState(saveSlot);
-
- // Draw a series of boxes on screen as a sample
- for (int i = 0; i < 100; ++i)
- _screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
- _screen->update();
-
- // Simple event handling loop
- byte pal[256 * 3] = { 0 };
- Common::Event e;
- int offset = 0;
-
- Graphics::FrameLimiter limiter(g_system, 60);
- while (!shouldQuit()) {
- while (g_system->getEventManager()->pollEvent(e)) {
- }
-
- // Cycle through a simple palette
- ++offset;
- for (int i = 0; i < 256; ++i)
- pal[i * 3 + 1] = (i + offset) % 256;
- g_system->getPaletteManager()->setPalette(pal, 0, 256);
- // Delay for a bit. All events loops should have a delay
- // to prevent the system being unduly loaded
- limiter.delayBeforeSwap();
- _screen->update();
- limiter.startFrame();
- }
+ //// Set the engine's debugger console
+ //setDebugger(new Console());
+ //
+ //// If a savegame was selected from the launcher, load it
+ //int saveSlot = ConfMan.getInt("save_slot");
+ //if (saveSlot != -1)
+ // (void)loadGameState(saveSlot);
+ //
+ //// Draw a series of boxes on screen as a sample
+ //for (int i = 0; i < 100; ++i)
+ // _screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
+ //_screen->update();
+ //
+ //// Simple event handling loop
+ //byte pal[256 * 3] = { 0 };
+ //Common::Event e;
+ //int offset = 0;
+ //
+ //Graphics::FrameLimiter limiter(g_system, 60);
+ //while (!shouldQuit()) {
+ // while (g_system->getEventManager()->pollEvent(e)) {
+ // }
+ //
+ // // Cycle through a simple palette
+ // ++offset;
+ // for (int i = 0; i < 256; ++i)
+ // pal[i * 3 + 1] = (i + offset) % 256;
+ // g_system->getPaletteManager()->setPalette(pal, 0, 256);
+ // // Delay for a bit. All events loops should have a delay
+ // // to prevent the system being unduly loaded
+ // limiter.delayBeforeSwap();
+ // _screen->update();
+ // limiter.startFrame();
+ //}
+ boltMain();
return Common::kNoError;
}
@@ -106,4 +111,135 @@ Common::Error BoltEngine::syncGame(Common::Serializer &s) {
return Common::kNoError;
}
+void BoltEngine::boltMain() {
+ DisplaySpecs displaySpecs[2];
+ void *testAlloc;
+ BarkerTable *barkerTable;
+ void *boothSprite;
+
+ memcpy(displaySpecs, g_displaySpecs, 16);
+
+ // g_callbackPtr = DS:0xCB;
+
+ testAlloc = _xp->allocMem(0x100000);
+ if (testAlloc == nullptr)
+ return;
+
+ _xp->freeMem(testAlloc);
+
+ _xp->randomize();
+
+ if (!allocResourceIndex())
+ goto cleanup;
+
+ g_boothsBoltLib = 0;
+
+ if (!openBOLTLib(AssetPath("booths.blt"), &g_boothsBoltIndex, &g_boothsBoltLib))
+ goto cleanup;
+
+ if (!_xp->chooseDisplaySpec(&g_displayMode, 2, displaySpecs))
+ goto cleanup;
+
+ g_displayWidth = displaySpecs[g_displayMode].width;
+ g_displayHeight = displaySpecs[g_displayMode].height;
+
+ // Center within 384x240 virtual coordinate space...
+ g_displayX = (384 - g_displayWidth) / 2;
+ g_displayY = (240 - g_displayHeight) / 2;
+
+ _xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
+
+ if (g_displayMode != 0)
+ g_rtfHandle = openRTF(AssetPath("booths.pal"));
+ else
+ g_rtfHandle = openRTF(AssetPath("booths4.pal"));
+
+ if (g_rtfHandle == nullptr)
+ goto cleanup;
+
+ playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+ boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
+
+ _xp->setTransparency(false);
+
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 0, 0);
+ displayPic(boothSprite, g_displayX, g_displayY, 0);
+
+ _xp->updateDisplay();
+
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 1, 0);
+ displayPic(boothSprite, g_displayX, g_displayY, 1);
+
+ playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+ freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
+
+ if (!getBOLTGroup(g_boothsBoltLib, 0, 1))
+ goto cleanup;
+
+ setCursorPict(memberAddr(g_boothsBoltLib, 0));
+
+ if (!initVRam(1500))
+ goto cleanup;
+
+ barkerTable = createBarker(17, 2);
+ if (barkerTable == nullptr)
+ goto cleanup;
+
+ // Register booth handlers...
+ registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
+ registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
+ registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
+ registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
+ registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
+ registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
+ registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
+ registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
+ registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
+ registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
+ registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
+ registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
+ registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
+ registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
+
+ g_lettersWon = 0;
+ _xp->setScreenSaverTimer(1800);
+
+ // The barker function runs the main loop, starting at mainEntrance()...
+ if (!checkError())
+ barker(barkerTable, 9);
+
+ freeBarker(barkerTable);
+
+cleanup:
+ freeBOLTGroup(g_boothsBoltLib, 0, 1);
+
+ if (g_boothsBoltLib != 0)
+ closeBOLTLib(&g_boothsBoltLib);
+
+ if (g_rtfHandle != nullptr)
+ closeRTF(g_rtfHandle);
+
+ freeVRam();
+ freeResourceIndex();
+}
+
+BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
+ return nullptr;
+}
+
+void BoltEngine::freeBarker(BarkerTable *table) {
+}
+
+void BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId) {
+}
+
+void BoltEngine::barker(BarkerTable *table, int16 startBooth) {
+}
+
+bool BoltEngine::checkError() {
+ return false;
+}
+
} // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 9c6b11b12d9..48e3cb8d19f 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -35,10 +35,35 @@
#include "graphics/screen.h"
#include "bolt/detection.h"
+#include "bolt/xplib/xplib.h"
namespace Bolt {
struct BoltGameDescription;
+class XpLib;
+
+struct DisplaySpecs {
+ int16 width;
+ int16 height;
+ int16 depth;
+ int16 unk;
+};
+
+struct BarkerTable {
+
+};
+
+struct BOLTLib {
+ int16 refCount;
+ int16 groupCount;
+ int32 fileHandle;
+ int32 *funcTable1;
+ int32 *funcTable2;
+ int32 *dwordPtr1;
+ int32 *dwordPtr2;
+ int32 *dwordPtr3;
+ int32 *dwordPtr4;
+};
class BoltEngine : public Engine {
private:
@@ -95,6 +120,151 @@ public:
Common::Serializer s(stream, nullptr);
return syncGame(s);
}
+
+protected:
+ DisplaySpecs g_displaySpecs[2] = {
+ {384, 240, 1, 1},
+ {320, 200, 1, 1}
+ };
+
+ XpLib *_xp;
+
+ // Entry point
+ void boltMain();
+
+ // Booths logic
+ typedef void (BoltEngine::*SideShowHandler)(void);
+
+ int g_lettersWon = 0;
+
+ void displayBooth();
+ void playAVOverBooth();
+ void hucksBooth();
+ void fredsBooth();
+ void scoobysBooth();
+ void yogisBooth();
+ void georgesBooth();
+ void topCatsBooth();
+ void mainEntrance();
+ void huckGame();
+ void fredGame();
+ void scoobyGame();
+ void yogiGame();
+ void georgeGame();
+ void topCatGame();
+ void winALetter();
+
+ void loadBooth();
+ void unloadBooth();
+ void openBooth();
+ void closeBooth();
+ void playTour();
+ void finishPlayingHelp();
+ void hotSpotActive();
+ void hoverHotSpot();
+ void boothEventLoop();
+ void resetInactivityState();
+ void handleButtonPress();
+ void playBoothAV();
+ void mapIdleAnimation();
+ void boothIdleAnimation();
+ void screensaverStep();
+ void flushInput();
+
+ BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
+ void freeBarker(BarkerTable *table);
+ void registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId);
+ void barker(BarkerTable *table, int16 startBooth);
+ bool checkError();
+
+ // Graphics
+ int g_displayMode = 0;
+ int32 g_displayX = 0;
+ int32 g_displayY = 0;
+ int32 g_displayWidth = 0;
+ int32 g_displayHeight = 0;
+
+ void setCursorPict(void *sprite);
+ void startCycle(void *cycleResource);
+ void blastColors();
+ void setColors();
+ void restoreColors();
+ void loadColors();
+ void shiftColorMap();
+ void fadeToBlack();
+ void displayColors(void *palette, int16 page, int16 flags);
+ void boltPict2Pict(void *dest, void *boltSprite);
+ void displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags);
+ void boltCycleToXPCycle();
+ void unpackColors();
+ void inRect();
+ void rectOverlap();
+
+ // Resource handling
+ BOLTLib *g_boothsBoltLib = nullptr;
+ int g_boothsBoltIndex = 0;
+
+ #define AssetPath(x) x
+
+ bool openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib);
+ void closeBOLTLib(BOLTLib **lib);
+ bool attemptFreeIndex(BOLTLib *lib, int16 groupId);
+ void loadGroupDirectory();
+ bool getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
+ void freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
+ void *getBOLTMember(BOLTLib *lib, int16 resId);
+ bool freeBOLTMember(BOLTLib *lib, int16 resId);
+
+ void *memberAddr(BOLTLib *lib, int16 resId);
+ void *memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset);
+ uint32 memberSize(BOLTLib *lib, int16 resId);
+ void *groupAddr(BOLTLib *lib, int16 groupId);
+
+ bool allocResourceIndex();
+ void freeResourceIndex();
+
+ bool initVRam(int16 poolSize);
+ void freeVRam();
+ bool vLoad(void *dest, const char *name);
+ bool vSave(void *src, uint16 srcSize, const char *name);
+ bool vDelete(const char *name);
+ void memMove(void *dest, void *src, uint16 count);
+ void *dataAddress(int16 recordOffset);
+ uint16 dataSize(int16 recordOffset);
+ bool findRecord(const char *name, int16 *outOffset);
+
+ // Videos
+ void *g_rtfHandle = nullptr;
+
+ void *openRTF(const char *fileName);
+ void closeRTF(void *rtf);
+ bool playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize);
+ bool fillRTFBuffer();
+ void flushRTFSoundQueue();
+ bool maintainRTF(int16 mode, void *outFrameData);
+ bool isRTFPlaying();
+ void killRTF();
+ void readPacket();
+ void preProcessPacket();
+ void queuePacket();
+ void deQueuePacket();
+ void allocPacket();
+ void freePacket();
+ void resetPlaybackState();
+ void sub_12980();
+ void prepareAV();
+ void maintainAV();
+ void stopAV();
+ bool playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
+ void processPacket();
+ void processRL7();
+ void processPLTE();
+ void initAV();
+ void cleanUpAV();
+ void startAnimation();
+ void maintainAudioPlay();
+ void initAnim();
+ void cleanUpAnim();
};
extern BoltEngine *g_engine;
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
new file mode 100644
index 00000000000..2af00442565
--- /dev/null
+++ b/engines/bolt/booth.cpp
@@ -0,0 +1,122 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::displayBooth() {
+}
+
+void BoltEngine::playAVOverBooth() {
+}
+
+void BoltEngine::hucksBooth() {
+}
+
+void BoltEngine::fredsBooth() {
+}
+
+void BoltEngine::scoobysBooth() {
+}
+
+void BoltEngine::yogisBooth() {
+}
+
+void BoltEngine::georgesBooth() {
+}
+
+void BoltEngine::topCatsBooth() {
+}
+
+void BoltEngine::mainEntrance() {
+}
+
+void BoltEngine::huckGame() {
+}
+
+void BoltEngine::fredGame() {
+}
+
+void BoltEngine::scoobyGame() {
+}
+
+void BoltEngine::yogiGame() {
+}
+
+void BoltEngine::georgeGame() {
+}
+
+void BoltEngine::topCatGame() {
+}
+
+void BoltEngine::winALetter() {
+}
+
+void BoltEngine::loadBooth() {
+}
+
+void BoltEngine::unloadBooth() {
+}
+
+void BoltEngine::openBooth() {
+}
+
+void BoltEngine::closeBooth() {
+}
+
+void BoltEngine::playTour() {
+}
+
+void BoltEngine::finishPlayingHelp() {
+}
+
+void BoltEngine::hotSpotActive() {
+}
+
+void BoltEngine::hoverHotSpot() {
+}
+
+void BoltEngine::boothEventLoop() {
+}
+
+void BoltEngine::resetInactivityState() {
+}
+
+void BoltEngine::handleButtonPress() {
+}
+
+void BoltEngine::playBoothAV() {
+}
+
+void BoltEngine::mapIdleAnimation() {
+}
+
+void BoltEngine::boothIdleAnimation() {
+}
+
+void BoltEngine::screensaverStep() {
+}
+
+void BoltEngine::flushInput() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/gfx.cpp b/engines/bolt/gfx.cpp
new file mode 100644
index 00000000000..d9fb5e8f211
--- /dev/null
+++ b/engines/bolt/gfx.cpp
@@ -0,0 +1,71 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::setCursorPict(void *sprite) {
+}
+
+void BoltEngine::startCycle(void *cycleResource) {
+}
+
+void BoltEngine::blastColors() {
+}
+
+void BoltEngine::setColors() {
+}
+
+void BoltEngine::restoreColors() {
+}
+
+void BoltEngine::loadColors() {
+}
+
+void BoltEngine::shiftColorMap() {
+}
+
+void BoltEngine::fadeToBlack() {
+}
+
+void BoltEngine::displayColors(void *palette, int16 page, int16 flags) {
+}
+
+void BoltEngine::boltPict2Pict(void *dest, void *boltSprite) {
+}
+
+void BoltEngine::displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags) {
+}
+
+void BoltEngine::boltCycleToXPCycle() {
+}
+
+void BoltEngine::unpackColors() {
+}
+
+void BoltEngine::inRect() {
+}
+
+void BoltEngine::rectOverlap() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index 82c0d83fe01..a0e9761d7e8 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -2,8 +2,19 @@ MODULE := engines/bolt
MODULE_OBJS = \
bolt.o \
+ booth.o \
console.o \
- metaengine.o
+ gfx.o \
+ metaengine.o \
+ resource.o \
+ video.o \
+ xplib\xp_cursor.o \
+ xplib\xp_events.o \
+ xplib\xp_file.o \
+ xplib\xp_gfx.o \
+ xplib\xp_mem.o \
+ xplib\xp_sound.o \
+ xplib\xplib.o
# This module can be built as a plugin
ifeq ($(ENABLE_BOLT), DYNAMIC_PLUGIN)
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
new file mode 100644
index 00000000000..ba4f0f3d2e5
--- /dev/null
+++ b/engines/bolt/resource.cpp
@@ -0,0 +1,112 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+bool BoltEngine::openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib) {
+ return false;
+}
+
+void BoltEngine::closeBOLTLib(BOLTLib **lib) {
+}
+
+bool BoltEngine::attemptFreeIndex(BOLTLib *lib, int16 groupId) {
+ return false;
+}
+
+void BoltEngine::loadGroupDirectory() {
+}
+
+bool BoltEngine::getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+ return false;
+}
+
+void BoltEngine::freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+}
+
+void *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
+ return nullptr;
+}
+
+bool BoltEngine::freeBOLTMember(BOLTLib *lib, int16 resId) {
+ return false;
+}
+
+void *BoltEngine::memberAddr(BOLTLib *lib, int16 resId) {
+ return nullptr;
+}
+
+void *BoltEngine::memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset) {
+ return nullptr;
+}
+
+uint32 BoltEngine::memberSize(BOLTLib *lib, int16 resId) {
+ return uint32();
+}
+
+void *BoltEngine::groupAddr(BOLTLib *lib, int16 groupId) {
+ return nullptr;
+}
+
+bool BoltEngine::allocResourceIndex() {
+ return false;
+}
+
+void BoltEngine::freeResourceIndex() {
+}
+
+bool BoltEngine::initVRam(int16 poolSize) {
+ return false;
+}
+
+void BoltEngine::freeVRam() {
+}
+
+bool BoltEngine::vLoad(void *dest, const char *name) {
+ return false;
+}
+
+bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
+ return false;
+}
+
+bool BoltEngine::vDelete(const char *name) {
+ return false;
+}
+
+void BoltEngine::memMove(void *dest, void *src, uint16 count) {
+}
+
+void *BoltEngine::dataAddress(int16 recordOffset) {
+ return nullptr;
+}
+
+uint16 BoltEngine::dataSize(int16 recordOffset) {
+ return uint16();
+}
+
+bool BoltEngine::findRecord(const char *name, int16 *outOffset) {
+ return false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/video.cpp b/engines/bolt/video.cpp
new file mode 100644
index 00000000000..fb1fd5bdb65
--- /dev/null
+++ b/engines/bolt/video.cpp
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void *BoltEngine::openRTF(const char *fileName) {
+ return nullptr;
+}
+
+void BoltEngine::closeRTF(void *rtf) {
+}
+
+bool BoltEngine::playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize) {
+ return false;
+}
+
+bool BoltEngine::fillRTFBuffer() {
+ return false;
+}
+
+void BoltEngine::flushRTFSoundQueue() {
+}
+
+bool BoltEngine::maintainRTF(int16 mode, void *outFrameData) {
+ return false;
+}
+
+bool BoltEngine::isRTFPlaying() {
+ return false;
+}
+
+void BoltEngine::killRTF() {
+}
+
+void BoltEngine::readPacket() {
+
+}
+
+void BoltEngine::preProcessPacket() {
+}
+
+void BoltEngine::queuePacket() {
+}
+
+void BoltEngine::deQueuePacket() {
+}
+
+void BoltEngine::allocPacket() {
+}
+
+void BoltEngine::freePacket() {
+}
+
+void BoltEngine::resetPlaybackState() {
+}
+
+void BoltEngine::sub_12980() {
+}
+
+void BoltEngine::prepareAV() {
+}
+
+void BoltEngine::maintainAV() {
+}
+
+void BoltEngine::stopAV() {
+}
+
+bool BoltEngine::playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+ return false;
+}
+
+void BoltEngine::processPacket() {
+}
+
+void BoltEngine::processRL7() {
+}
+
+void BoltEngine::processPLTE() {
+}
+
+void BoltEngine::initAV() {
+}
+
+void BoltEngine::cleanUpAV() {
+}
+
+void BoltEngine::startAnimation() {
+}
+
+void BoltEngine::maintainAudioPlay() {
+}
+
+void BoltEngine::initAnim() {
+}
+
+void BoltEngine::cleanUpAnim() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_cursor.cpp b/engines/bolt/xplib/xp_cursor.cpp
new file mode 100644
index 00000000000..07d9ba64af4
--- /dev/null
+++ b/engines/bolt/xplib/xp_cursor.cpp
@@ -0,0 +1,45 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::readCursor() {
+}
+
+void XpLib::setCursorPos() {
+}
+
+void XpLib::setCursorImage() {
+}
+
+void XpLib::setCursorColor() {
+}
+
+void XpLib::showCursor() {
+}
+
+void XpLib::hideCursor() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_events.cpp b/engines/bolt/xplib/xp_events.cpp
new file mode 100644
index 00000000000..32d5f0c5930
--- /dev/null
+++ b/engines/bolt/xplib/xp_events.cpp
@@ -0,0 +1,54 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::getEvent() {
+}
+
+void XpLib::peekEvent() {
+}
+
+void XpLib::postEvent() {
+}
+
+void XpLib::enableController() {
+}
+
+void XpLib::disableController() {
+}
+
+void XpLib::setInactivityTimer() {
+}
+
+void XpLib::setScreenSaverTimer(int32 time) {
+}
+
+void XpLib::startTimer() {
+}
+
+void XpLib::killTimer() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_file.cpp b/engines/bolt/xplib/xp_file.cpp
new file mode 100644
index 00000000000..9c49123c13e
--- /dev/null
+++ b/engines/bolt/xplib/xp_file.cpp
@@ -0,0 +1,39 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::openFile() {
+}
+
+void XpLib::closeFile() {
+}
+
+void XpLib::readFile() {
+}
+
+void XpLib::setFilePos() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_gfx.cpp b/engines/bolt/xplib/xp_gfx.cpp
new file mode 100644
index 00000000000..9c3f159737f
--- /dev/null
+++ b/engines/bolt/xplib/xp_gfx.cpp
@@ -0,0 +1,71 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::blit() {
+
+}
+
+void XpLib::maskBlit() {
+}
+
+void XpLib::getPalette() {
+}
+
+void XpLib::setPalette() {
+}
+
+void XpLib::startCycle() {
+}
+
+void XpLib::stopCycle() {
+}
+
+void XpLib::setScreenBrightness() {
+}
+
+bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+ return false;
+}
+
+void XpLib::setCoordSpec(int32 x, int32 y, int32 width, int32 height) {
+}
+
+void XpLib::displayPic() {
+}
+
+void XpLib::updateDisplay() {
+}
+
+void XpLib::setFrameRate() {
+}
+
+void XpLib::setTransparency(bool toggle) {
+}
+
+void XpLib::fillDisplay() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_mem.cpp b/engines/bolt/xplib/xp_mem.cpp
new file mode 100644
index 00000000000..fd636326497
--- /dev/null
+++ b/engines/bolt/xplib/xp_mem.cpp
@@ -0,0 +1,40 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::setMem() {
+}
+
+byte *XpLib::allocMem(int32 size) {
+ return nullptr;
+}
+
+void XpLib::tryAllocMem() {
+}
+
+void XpLib::freeMem(void *mem) {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_sound.cpp b/engines/bolt/xplib/xp_sound.cpp
new file mode 100644
index 00000000000..1e4835f8377
--- /dev/null
+++ b/engines/bolt/xplib/xp_sound.cpp
@@ -0,0 +1,33 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::playSound() {
+}
+
+void XpLib::stopSound() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
new file mode 100644
index 00000000000..681c750872a
--- /dev/null
+++ b/engines/bolt/xplib/xplib.cpp
@@ -0,0 +1,55 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+XpLib::XpLib(BoltEngine *bolt) {
+ _bolt = bolt;
+
+ initialize();
+}
+
+XpLib::~XpLib() {
+ terminate();
+}
+
+void XpLib::initialize() {
+}
+
+void XpLib::terminate() {
+}
+
+void XpLib::swapWord() {
+}
+
+void XpLib::swapLong() {
+}
+
+void XpLib::getRandom() {
+}
+
+void XpLib::randomize() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
new file mode 100644
index 00000000000..e3267840d46
--- /dev/null
+++ b/engines/bolt/xplib/xplib.h
@@ -0,0 +1,89 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef XPLIB_XPLIB_H
+#define XPLIB_XPLIB_H
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+struct DisplaySpecs;
+class BoltEngine;
+
+class XpLib {
+public:
+ XpLib(BoltEngine *bolt);
+ ~XpLib();
+
+ void swapWord();
+ void swapLong();
+ void blit();
+ void maskBlit();
+ void setMem();
+ void terminate();
+ void initialize();
+ void getPalette();
+ void setPalette();
+ void startCycle();
+ void stopCycle();
+ void setScreenBrightness();
+ void readCursor();
+ void setCursorPos();
+ void setCursorImage();
+ void setCursorColor();
+ void showCursor();
+ void hideCursor();
+ void getEvent();
+ void peekEvent();
+ void postEvent();
+ void enableController();
+ void disableController();
+ void setInactivityTimer();
+ void setScreenSaverTimer(int32 time);
+ bool chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs);
+ void setCoordSpec(int32 x, int32 y, int32 width, int32 height);
+ void displayPic();
+ void updateDisplay();
+ void setFrameRate();
+ void setTransparency(bool toggle);
+ void fillDisplay();
+ void getRandom();
+ void randomize();
+ void openFile();
+ void closeFile();
+ void readFile();
+ void setFilePos();
+ byte *allocMem(int32 size);
+ void tryAllocMem();
+ void freeMem(void *mem);
+ void playSound();
+ void stopSound();
+ void startTimer();
+ void killTimer();
+
+protected:
+ BoltEngine *_bolt;
+};
+
+} // End of namespace Bolt
+
+#endif // XPLIB_XPLIB_H
Commit: f204ad4fdf2992e5392b9ccdccf2ebece8a13c42
https://github.com/scummvm/scummvm/commit/f204ad4fdf2992e5392b9ccdccf2ebece8a13c42
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Stub the entirety of the XPLIB.DLL code
Changed paths:
A engines/bolt/anim.cpp
A engines/bolt/xplib/blit.cpp
A engines/bolt/xplib/cursor.cpp
A engines/bolt/xplib/display.cpp
A engines/bolt/xplib/events.cpp
A engines/bolt/xplib/file.cpp
A engines/bolt/xplib/mem.cpp
A engines/bolt/xplib/palette.cpp
A engines/bolt/xplib/random.cpp
A engines/bolt/xplib/sound.cpp
A engines/bolt/xplib/timer.cpp
R engines/bolt/video.cpp
R engines/bolt/xplib/xp_cursor.cpp
R engines/bolt/xplib/xp_events.cpp
R engines/bolt/xplib/xp_file.cpp
R engines/bolt/xplib/xp_gfx.cpp
R engines/bolt/xplib/xp_mem.cpp
R engines/bolt/xplib/xp_sound.cpp
engines/bolt/bolt.cpp
engines/bolt/bolt.h
engines/bolt/module.mk
engines/bolt/xplib/xplib.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/video.cpp b/engines/bolt/anim.cpp
similarity index 100%
rename from engines/bolt/video.cpp
rename to engines/bolt/anim.cpp
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 19dfdd81f81..b358bb00c26 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -122,7 +122,7 @@ void BoltEngine::boltMain() {
// g_callbackPtr = DS:0xCB;
testAlloc = _xp->allocMem(0x100000);
- if (testAlloc == nullptr)
+ if (!testAlloc)
return;
_xp->freeMem(testAlloc);
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 48e3cb8d19f..3fddfafb3b5 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -66,6 +66,8 @@ struct BOLTLib {
};
class BoltEngine : public Engine {
+friend class XpLib;
+
private:
const ADGameDescription *_gameDescription;
Common::RandomSource _randomSource;
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index a0e9761d7e8..b4ef08ebf3b 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -1,19 +1,23 @@
MODULE := engines/bolt
MODULE_OBJS = \
+ anim.o \
bolt.o \
booth.o \
console.o \
gfx.o \
metaengine.o \
resource.o \
- video.o \
- xplib\xp_cursor.o \
- xplib\xp_events.o \
- xplib\xp_file.o \
- xplib\xp_gfx.o \
- xplib\xp_mem.o \
- xplib\xp_sound.o \
+ xplib\blit.o \
+ xplib\cursor.o \
+ xplib\display.o \
+ xplib\events.o \
+ xplib\file.o \
+ xplib\mem.o \
+ xplib\palette.o \
+ xplib\random.o \
+ xplib\sound.o \
+ xplib\timer.o \
xplib\xplib.o
# This module can be built as a plugin
diff --git a/engines/bolt/xplib/blit.cpp b/engines/bolt/xplib/blit.cpp
new file mode 100644
index 00000000000..4237ed71a67
--- /dev/null
+++ b/engines/bolt/xplib/blit.cpp
@@ -0,0 +1,63 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+void XpLib::dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height) {
+
+}
+
+void XpLib::maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+uint32 XpLib::compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height) {
+ return 0;
+}
+
+void XpLib::rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+void XpLib::rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+uint32 XpLib::rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags) {
+ return 0;
+}
+
+uint16 XpLib::rleDataSize(void *rleData, uint16 height) {
+ return 0;
+}
+
+void XpLib::markCursorPixels(void *buffer, uint32 count) {
+
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_gfx.cpp b/engines/bolt/xplib/cursor.cpp
similarity index 66%
rename from engines/bolt/xplib/xp_gfx.cpp
rename to engines/bolt/xplib/cursor.cpp
index 9c3f159737f..923f8fb76ef 100644
--- a/engines/bolt/xplib/xp_gfx.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -24,48 +24,37 @@
namespace Bolt {
-void XpLib::blit() {
-
+bool XpLib::initCoords() {
+ return false;
}
-void XpLib::maskBlit() {
+void XpLib::shutdownCoords() {
}
-void XpLib::getPalette() {
+bool XpLib::readCursor(uint16 *outButtons, int16 *outX, int16 *outY) {
+ return false;
}
-void XpLib::setPalette() {
+void XpLib::readJoystick(int16 *outX, int16 *outY) {
}
-void XpLib::startCycle() {
+void XpLib::setCursorPos(int16 x, int16 y) {
}
-void XpLib::stopCycle() {
+void XpLib::setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY) {
}
-void XpLib::setScreenBrightness() {
+void XpLib::setCursorColor(byte r, byte g, byte b) {
}
-bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+bool XpLib::showCursor() {
return false;
}
-void XpLib::setCoordSpec(int32 x, int32 y, int32 width, int32 height) {
-}
-
-void XpLib::displayPic() {
-}
-
-void XpLib::updateDisplay() {
-}
-
-void XpLib::setFrameRate() {
-}
-
-void XpLib::setTransparency(bool toggle) {
+void XpLib::hideCursor() {
}
-void XpLib::fillDisplay() {
+void XpLib::updateCursorPosition() {
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
new file mode 100644
index 00000000000..cc779d2ba07
--- /dev/null
+++ b/engines/bolt/xplib/display.cpp
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+int16 XpLib::switchDisplayMode(int16 mode) {
+ return int16();
+}
+
+bool XpLib::initDisplay() {
+ return false;
+}
+
+void XpLib::shutdownDisplay() {
+}
+
+bool XpLib::createSurface(XPSurface *surf) {
+ return false;
+}
+
+void XpLib::freeSurface(XPSurface *surf) {
+}
+
+bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+ return false;
+}
+
+void XpLib::setCoordSpec(int16 x, int16 y, int16 width, int16 height) {
+}
+
+void XpLib::virtualToScreen(int16 *x, int16 *y) {
+}
+
+void XpLib::screenToVirtual(int16 *x, int16 *y) {
+}
+
+void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
+}
+
+bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip) {
+ return false;
+}
+
+void XpLib::addDirtyRect(ClipRect *rect) {
+}
+
+void XpLib::setFrameRate(int16 fps) {
+}
+
+void XpLib::updateDisplay() {
+}
+
+void XpLib::waitForFrameRate() {
+}
+
+void XpLib::handlePaletteTransitions() {
+}
+
+void XpLib::flushPalette() {
+}
+
+void XpLib::overlayComposite() {
+}
+
+void XpLib::compositeToScreen() {
+}
+
+void XpLib::mergeDirtyRects() {
+}
+
+void XpLib::blitDirtyRects(ClipRect *rects, int16 count) {
+}
+
+void XpLib::compositeDirtyRects(ClipRect *rects, int16 count) {
+}
+
+void XpLib::applyCursorPalette(bool enable) {
+}
+
+void XpLib::prepareBackSurface() {
+}
+
+void XpLib::setTransparency(bool toggle) {
+}
+
+void XpLib::fillDisplay(byte color, int16 page) {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
new file mode 100644
index 00000000000..77650c75153
--- /dev/null
+++ b/engines/bolt/xplib/events.cpp
@@ -0,0 +1,119 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+bool XpLib::initWindow() {
+ return false;
+}
+
+void XpLib::shutdownWindow() {
+
+}
+
+int16 XpLib::getEvent(int16 filter, uint32 *outData) {
+ return 0;
+}
+
+int16 XpLib::peekEvent(int16 filter, uint32 *outData) {
+ return 0;
+}
+
+void XpLib::unlinkEvent(int16 node) {
+
+}
+
+void XpLib::enqueueEvent(int16 node) {
+
+}
+
+void XpLib::pumpMessages() {
+
+}
+
+void XpLib::handleTimer(uint32 timerId) {
+
+}
+
+void XpLib::handleMouseMove(bool *mouseMoved) {
+
+}
+
+void XpLib::handleMouseButton(int16 down, int16 button) {
+
+}
+
+void XpLib::handleKey(int16 vkey, int16 down) {
+
+}
+
+void XpLib::postJoystickEvent(int16 source, int16 dx, int16 dy) {
+
+}
+
+void XpLib::postEvent(int16 type, uint32 data) {
+
+}
+
+bool XpLib::canDropEvent(int16 type) {
+ return false;
+}
+
+int16 XpLib::setInactivityTimer(int16 seconds) {
+ return 0;
+}
+
+int16 XpLib::setScreenSaverTimer(int16 time) {
+ return 0;
+}
+
+void XpLib::activateScreenSaver() {
+
+}
+
+void XpLib::resetInactivity() {
+
+}
+
+bool XpLib::enableController() {
+ return false;
+}
+
+void XpLib::disableController() {
+
+}
+
+void XpLib::enableMouse() {
+
+}
+
+void XpLib::disableMouse() {
+
+}
+
+int16 XpLib::getButtonState() {
+ return 0;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
new file mode 100644
index 00000000000..6a06ad897a6
--- /dev/null
+++ b/engines/bolt/xplib/file.cpp
@@ -0,0 +1,72 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::fileError(const char *message) {
+}
+
+int32 XpLib::createFile(const char *fileName) {
+ return int32();
+}
+
+void XpLib::deleteFile(const char *fileName) {
+}
+
+int32 XpLib::openFile(const char *fileName, short flags) {
+ return int32();
+}
+
+void XpLib::closeFile(int32 handle) {
+}
+
+bool XpLib::readFile(int32 handle, void *buffer, uint32 *size) {
+ return false;
+}
+
+bool XpLib::setFilePos(int32 handle, uint32 offset, int16 origin) {
+ return false;
+}
+
+void *XpLib::allocMem(uint32 size) {
+ void *result = malloc(size);
+ if (!result)
+ error("XpLib::allocMem(): Not enough memory");
+
+ return result;
+}
+
+void *XpLib::tryAllocMem(uint32 size) {
+ void *result = (void *)malloc(size);
+ if (!result)
+ warning("XpLib::tryAllocMem(): Couldn't allocate memory, returning nullptr");
+
+ return result;
+}
+
+void XpLib::freeMem(void *mem) {
+ free(mem);
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_sound.cpp b/engines/bolt/xplib/mem.cpp
similarity index 94%
rename from engines/bolt/xplib/xp_sound.cpp
rename to engines/bolt/xplib/mem.cpp
index 1e4835f8377..fe512e1b830 100644
--- a/engines/bolt/xplib/xp_sound.cpp
+++ b/engines/bolt/xplib/mem.cpp
@@ -24,10 +24,4 @@
namespace Bolt {
-void XpLib::playSound() {
-}
-
-void XpLib::stopSound() {
-}
-
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_events.cpp b/engines/bolt/xplib/palette.cpp
similarity index 76%
rename from engines/bolt/xplib/xp_events.cpp
rename to engines/bolt/xplib/palette.cpp
index 32d5f0c5930..81c6b5534f1 100644
--- a/engines/bolt/xplib/xp_events.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -24,31 +24,24 @@
namespace Bolt {
-void XpLib::getEvent() {
-}
-
-void XpLib::peekEvent() {
-}
-
-void XpLib::postEvent() {
-}
-void XpLib::enableController() {
+void XpLib::getPalette(uint16 startIndex, uint16 count, void *destBuf) {
}
-void XpLib::disableController() {
+void XpLib::setPalette(uint16 count, uint16 startIndex, void *srcBu) {
}
-void XpLib::setInactivityTimer() {
+bool XpLib::startCycle(XPCycleSpec *specs) {
+ return false;
}
-void XpLib::setScreenSaverTimer(int32 time) {
+void XpLib::cycleColors() {
}
-void XpLib::startTimer() {
+void XpLib::stopCycle() {
}
-void XpLib::killTimer() {
+void XpLib::setScreenBrightness(uint8 percent) {
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_mem.cpp b/engines/bolt/xplib/random.cpp
similarity index 82%
rename from engines/bolt/xplib/xp_mem.cpp
rename to engines/bolt/xplib/random.cpp
index fd636326497..412c0d47861 100644
--- a/engines/bolt/xplib/xp_mem.cpp
+++ b/engines/bolt/xplib/random.cpp
@@ -24,17 +24,12 @@
namespace Bolt {
-void XpLib::setMem() {
+int16 XpLib::getRandom(int16 range) {
+ return (int16)(((uint32)_bolt->_randomSource.getRandomNumber(UINT_MAX) * (uint32)range) / 0x8000);
}
-byte *XpLib::allocMem(int32 size) {
- return nullptr;
-}
-
-void XpLib::tryAllocMem() {
-}
-
-void XpLib::freeMem(void *mem) {
+void XpLib::randomize() {
+ _bolt->_randomSource.generateNewSeed();
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_file.cpp b/engines/bolt/xplib/sound.cpp
similarity index 72%
rename from engines/bolt/xplib/xp_file.cpp
rename to engines/bolt/xplib/sound.cpp
index 9c49123c13e..b1a26a29671 100644
--- a/engines/bolt/xplib/xp_file.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -24,16 +24,36 @@
namespace Bolt {
-void XpLib::openFile() {
+bool XpLib::pollSound(void *outData) {
+ return false;
}
-void XpLib::closeFile() {
+bool XpLib::initSound() {
+ return false;
}
-void XpLib::readFile() {
+void XpLib::waveCb() {
+
+}
+
+void XpLib::shutdownSound() {
+
+}
+
+bool XpLib::playSound(void *data, uint32 size, int16 sampleRate) {
+ return false;
+}
+
+bool XpLib::pauseSound() {
+ return false;
+}
+
+bool XpLib::resumeSound() {
+ return false;
}
-void XpLib::setFilePos() {
+bool XpLib::stopSound() {
+ return false;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_cursor.cpp b/engines/bolt/xplib/timer.cpp
similarity index 83%
rename from engines/bolt/xplib/xp_cursor.cpp
rename to engines/bolt/xplib/timer.cpp
index 07d9ba64af4..9b8d710f982 100644
--- a/engines/bolt/xplib/xp_cursor.cpp
+++ b/engines/bolt/xplib/timer.cpp
@@ -24,22 +24,22 @@
namespace Bolt {
-void XpLib::readCursor() {
+bool XpLib::initTimer() {
+ return false;
}
-void XpLib::setCursorPos() {
+void XpLib::shutdownTimer() {
}
-void XpLib::setCursorImage() {
+int32 XpLib::startTimer(int16 delay) {
+ return 0;
}
-void XpLib::setCursorColor() {
+void XpLib::timeCb() {
}
-void XpLib::showCursor() {
-}
-
-void XpLib::hideCursor() {
+bool XpLib::killTimer(int32 timerId) {
+ return false;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
index 681c750872a..4cdc6f476bb 100644
--- a/engines/bolt/xplib/xplib.cpp
+++ b/engines/bolt/xplib/xplib.cpp
@@ -40,16 +40,4 @@ void XpLib::initialize() {
void XpLib::terminate() {
}
-void XpLib::swapWord() {
-}
-
-void XpLib::swapLong() {
-}
-
-void XpLib::getRandom() {
-}
-
-void XpLib::randomize() {
-}
-
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index e3267840d46..fd4031a5c35 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -29,59 +29,186 @@ namespace Bolt {
struct DisplaySpecs;
class BoltEngine;
+typedef struct XPCycleSpec {
+ short startIndex; // +0x00: first palette index
+ short endIndex; // +0x02: last palette index
+ short delay; // +0x04: cycle period in ms
+} XPCycleSpec; // 6 bytes
+
+typedef struct XPSurface {
+ byte *pixelData; // +0x00: pixel buffer (width * height bytes, 8bpp)
+ int16 width; // +0x04: surface width
+ int16 height; // +0x06: surface height
+ byte *palette; // +0x08: RGB palette data (127 * 3 = 381 bytes)
+ int16 paletteStart; // +0x0C: first palette index (1)
+ int16 paletteCount; // +0x0E: number of palette entries (127)
+ int16 flags; // +0x10
+ long overlayData; // +0x12
+ char _pad[0x10]; // +0x16..+0x23 (unknown)
+ int16 clipX; // +0x24
+ int16 clipY; // +0x26
+} XPSurface; // total: 0x28 = 40 bytes
+
+typedef struct XPPicDesc {
+ byte *pixelData; // +0x00: pixel buffer (8bpp)
+ int16 width; // +0x04: image width
+ int16 height; // +0x06: image height
+ byte *palette; // +0x08: RGB palette data
+ int16 paletteStart; // +0x0C: first palette index
+ int16 paletteCount; // +0x0E: number of palette entries
+ int16 flags; // +0x10: bit 0 = transparent, bit 1 = RLE
+} XPPicDesc; // 0x12 = 18 bytes
+
+typedef void (*BlitFunc)(void *src, int16 srcStride, void *dest, int16 destStride, int16 width, int16 height);
+
+typedef struct ClipRect {
+ short left;
+ short top;
+ short right;
+ short bottom;
+} ClipRect;
+
class XpLib {
public:
XpLib(BoltEngine *bolt);
~XpLib();
- void swapWord();
- void swapLong();
- void blit();
- void maskBlit();
- void setMem();
- void terminate();
void initialize();
- void getPalette();
- void setPalette();
- void startCycle();
+ void terminate();
+
+ // Blit
+ void blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
+ void maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
+
+ // Palette
+ void getPalette(uint16 startIndex, uint16 count, void *destBuf);
+ void setPalette(uint16 count, uint16 startIndex, void *srcBuf);
+ bool startCycle(XPCycleSpec *specs);
+ void cycleColors();
void stopCycle();
- void setScreenBrightness();
- void readCursor();
- void setCursorPos();
- void setCursorImage();
- void setCursorColor();
- void showCursor();
+ void setScreenBrightness(uint8 percent);
+
+ // Cursor
+ bool readCursor(uint16 *outButtons, int16 *outX, int16 *outY);
+ void setCursorPos(int16 x, int16 y);
+ void setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY);
+ void setCursorColor(byte r, byte g, byte b);
+ bool showCursor();
void hideCursor();
- void getEvent();
- void peekEvent();
- void postEvent();
- void enableController();
+ void updateCursorPosition();
+
+ // Events
+ int16 getEvent(int16 filter, uint32 *outData);
+ int16 peekEvent(int16 filter, uint32 *outData);
+ void postEvent(int16 type, uint32 data);
+ int16 setInactivityTimer(int16 seconds);
+ int16 setScreenSaverTimer(int16 time);
+ bool enableController();
void disableController();
- void setInactivityTimer();
- void setScreenSaverTimer(int32 time);
+
+ // Display
bool chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs);
- void setCoordSpec(int32 x, int32 y, int32 width, int32 height);
- void displayPic();
+ void setCoordSpec(int16 x, int16 y, int16 width, int16 height);
+ void displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page);
+ void setFrameRate(int16 fps);
void updateDisplay();
- void setFrameRate();
void setTransparency(bool toggle);
- void fillDisplay();
- void getRandom();
+ void fillDisplay(byte color, int16 page);
+
+ // Random
+ int16 getRandom(int16 range);
void randomize();
- void openFile();
- void closeFile();
- void readFile();
- void setFilePos();
- byte *allocMem(int32 size);
- void tryAllocMem();
+
+ // File
+ int32 createFile(const char *fileName);
+ void deleteFile(const char *fileName);
+ int32 openFile(const char *fileName, short flags);
+ void closeFile(int32 handle);
+ bool readFile(int32 handle, void *buffer, uint32 *size);
+ bool setFilePos(int32 handle, uint32 offset, int16 origin);
+ void *allocMem(uint32 size);
+ void *tryAllocMem(uint32 size);
void freeMem(void *mem);
- void playSound();
- void stopSound();
- void startTimer();
- void killTimer();
+
+ // Sound
+ void waveCb();
+ bool playSound(void *data, uint32 size, int16 sampleRate);
+ bool pauseSound();
+ bool resumeSound();
+ bool stopSound();
+
+ // Timer
+ int32 startTimer(int16 delay);
+ void timeCb();
+ bool killTimer(int32 timerId);
protected:
BoltEngine *_bolt;
+
+ // Blit
+ void dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height);
+ uint32 compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height);
+ void rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
+ void rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
+ uint32 rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags);
+ uint16 rleDataSize(void *rleData, uint16 height);
+ void markCursorPixels(void *buffer, uint32 count);
+
+ // Cursor
+ bool initCoords();
+ void shutdownCoords();
+ void readJoystick(int16 *outX, int16 *outY);
+
+ // Events
+ bool initWindow();
+ void shutdownWindow();
+ void unlinkEvent(int16 node);
+ void enqueueEvent(int16 node);
+ void pumpMessages();
+ void handleTimer(uint32 timerId);
+ void handleMouseMove(bool *mouseMoved);
+ void handleMouseButton(int16 down, int16 button);
+ void handleKey(int16 vkey, int16 down);
+ void postJoystickEvent(int16 source, int16 dx, int16 dy);
+ bool canDropEvent(int16 type);
+ void activateScreenSaver();
+ void resetInactivity();
+ void enableMouse();
+ void disableMouse();
+ int16 getButtonState();
+
+ // Display
+ int16 switchDisplayMode(int16 mode);
+ bool initDisplay();
+ void shutdownDisplay();
+ bool createSurface(XPSurface *surf);
+ void freeSurface(XPSurface *surf);
+ void virtualToScreen(int16 *x, int16 *y);
+ void screenToVirtual(int16 *x, int16 *y);
+ bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip);
+ void addDirtyRect(ClipRect *rect);
+ void waitForFrameRate();
+ void handlePaletteTransitions();
+ void flushPalette();
+ void overlayComposite();
+ void compositeToScreen();
+ void mergeDirtyRects();
+ void blitDirtyRects(ClipRect *rects, int16 count);
+ void compositeDirtyRects(ClipRect *rects, int16 count);
+ void applyCursorPalette(bool enable);
+ void prepareBackSurface();
+
+ // File
+ void fileError(const char *message);
+
+ // Sound
+ bool pollSound(void *outData);
+ bool initSound();
+ void shutdownSound();
+
+ // Timer
+ bool initTimer();
+ void shutdownTimer();
};
} // End of namespace Bolt
Commit: 27451337381673857357aa4a70331a173abf818f
https://github.com/scummvm/scummvm/commit/27451337381673857357aa4a70331a173abf818f
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Implement the entirety of XPLIB.DLL except sound
Changed paths:
R engines/bolt/xplib/mem.cpp
engines/bolt/bolt.cpp
engines/bolt/bolt.h
engines/bolt/module.mk
engines/bolt/resource.cpp
engines/bolt/xplib/blit.cpp
engines/bolt/xplib/cursor.cpp
engines/bolt/xplib/display.cpp
engines/bolt/xplib/events.cpp
engines/bolt/xplib/file.cpp
engines/bolt/xplib/palette.cpp
engines/bolt/xplib/sound.cpp
engines/bolt/xplib/timer.cpp
engines/bolt/xplib/xplib.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index b358bb00c26..e4683cdf938 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -44,7 +44,6 @@ BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engin
}
BoltEngine::~BoltEngine() {
- delete _screen;
delete _xp;
}
@@ -57,12 +56,21 @@ Common::String BoltEngine::getGameId() const {
}
Common::Error BoltEngine::run() {
- // Initialize 320x200 paletted graphics mode
- initGraphics(320, 200);
- _screen = new Graphics::Screen();
+ ConfMan.registerDefault("extended_viewport", false);
+ if (ConfMan.hasKey("extended_viewport", _targetName)) {
+ g_extendedViewport = ConfMan.getBool("extended_viewport");
+ }
+
+ // Initialize paletted graphics mode
+ if (g_extendedViewport) {
+ initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
+ } else {
+ initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
+ }
+
+ // Set the engine's debugger console
+ setDebugger(new Console());
- //// Set the engine's debugger console
- //setDebugger(new Console());
//
//// If a savegame was selected from the launcher, load it
//int saveSlot = ConfMan.getInt("save_slot");
@@ -95,6 +103,8 @@ Common::Error BoltEngine::run() {
// _screen->update();
// limiter.startFrame();
//}
+
+ _xp->initialize();
boltMain();
return Common::kNoError;
@@ -112,13 +122,10 @@ Common::Error BoltEngine::syncGame(Common::Serializer &s) {
}
void BoltEngine::boltMain() {
- DisplaySpecs displaySpecs[2];
void *testAlloc;
BarkerTable *barkerTable;
void *boothSprite;
- memcpy(displaySpecs, g_displaySpecs, 16);
-
// g_callbackPtr = DS:0xCB;
testAlloc = _xp->allocMem(0x100000);
@@ -134,14 +141,14 @@ void BoltEngine::boltMain() {
g_boothsBoltLib = 0;
- if (!openBOLTLib(AssetPath("booths.blt"), &g_boothsBoltIndex, &g_boothsBoltLib))
+ if (!openBOLTLib(&g_boothsBoltLib, &g_boothsBoltIndex, AssetPath("booths.blt")))
goto cleanup;
- if (!_xp->chooseDisplaySpec(&g_displayMode, 2, displaySpecs))
+ if (!_xp->chooseDisplaySpec(&g_displayMode, 2, g_displaySpecs))
goto cleanup;
- g_displayWidth = displaySpecs[g_displayMode].width;
- g_displayHeight = displaySpecs[g_displayMode].height;
+ g_displayWidth = g_displaySpecs[g_displayMode].width;
+ g_displayHeight = g_displaySpecs[g_displayMode].height;
// Center within 384x240 virtual coordinate space...
g_displayX = (384 - g_displayWidth) / 2;
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 3fddfafb3b5..4042d3f725b 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -39,14 +39,17 @@
namespace Bolt {
+#define SCREEN_WIDTH 320
+#define SCREEN_HEIGHT 200
+#define EXTENDED_SCREEN_WIDTH 384
+#define EXTENDED_SCREEN_HEIGHT 240
+
struct BoltGameDescription;
class XpLib;
struct DisplaySpecs {
int16 width;
int16 height;
- int16 depth;
- int16 unk;
};
struct BarkerTable {
@@ -74,8 +77,7 @@ private:
protected:
// Engine APIs
Common::Error run() override;
-public:
- Graphics::Screen *_screen = nullptr;
+
public:
BoltEngine(OSystem *syst, const ADGameDescription *gameDesc);
~BoltEngine() override;
@@ -125,11 +127,12 @@ public:
protected:
DisplaySpecs g_displaySpecs[2] = {
- {384, 240, 1, 1},
- {320, 200, 1, 1}
+ {384, 240},
+ {320, 200}
};
XpLib *_xp;
+ bool g_extendedViewport = false;
// Entry point
void boltMain();
@@ -205,10 +208,12 @@ protected:
// Resource handling
BOLTLib *g_boothsBoltLib = nullptr;
int g_boothsBoltIndex = 0;
+ int16 g_resourceIndexCount = 1000;
+ uint32 *g_resourceIndex = nullptr;
#define AssetPath(x) x
- bool openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib);
+ bool openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName);
void closeBOLTLib(BOLTLib **lib);
bool attemptFreeIndex(BOLTLib *lib, int16 groupId);
void loadGroupDirectory();
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index b4ef08ebf3b..4f9c5b5ac04 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -13,7 +13,6 @@ MODULE_OBJS = \
xplib\display.o \
xplib\events.o \
xplib\file.o \
- xplib\mem.o \
xplib\palette.o \
xplib\random.o \
xplib\sound.o \
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index ba4f0f3d2e5..e1204a58c00 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -23,7 +23,7 @@
namespace Bolt {
-bool BoltEngine::openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib) {
+bool BoltEngine::openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName) {
return false;
}
diff --git a/engines/bolt/xplib/blit.cpp b/engines/bolt/xplib/blit.cpp
index 4237ed71a67..d7aae972728 100644
--- a/engines/bolt/xplib/blit.cpp
+++ b/engines/bolt/xplib/blit.cpp
@@ -24,40 +24,236 @@
namespace Bolt {
-void XpLib::blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+void XpLib::blit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+ int16 srcSkip = srcStride - width;
+ int16 dstSkip = dstStride - width;
+ for (int16 row = 0; row < height; row++) {
+ memcpy(dst, src, width);
+ dst += dstSkip + width;
+ src += srcSkip + width;
+ }
}
-void XpLib::dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height) {
+void XpLib::dirtyBlit(byte *src, byte *dst, uint16 width, uint16 height, byte *dirtyFlags) {
+ for (int16 row = 0; row < height; row++) {
+ byte flag = dirtyFlags[row];
-}
+ // Clean row? Skip...
+ if (flag == 3) {
+ src += width;
+ dst += width;
+ continue;
+ }
-void XpLib::maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+ // Mark row: 2 -> 3 (was dirty, now clean), else -> 0 (newly copied)
+ dirtyFlags[row] = (flag == 2) ? 3 : 0;
+ memcpy(dst, src, width);
+ }
}
-uint32 XpLib::compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height) {
- return 0;
+void XpLib::maskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+ int16 srcSkip = srcStride - width;
+ int16 dstSkip = dstStride - width;
+
+ for (int16 row = 0; row < height; row++) {
+ for (int16 col = 0; col < width; col++) {
+ byte pixel = *src++;
+ if (pixel != 0)
+ *dst = pixel;
+
+ dst++;
+ }
+
+ dst += dstSkip;
+ src += srcSkip;
+ }
}
-void XpLib::rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+void XpLib::compositeBlit(byte *src, byte *background, byte *dst, uint16 stride, uint16 width, uint16 height) {
+ int16 skip = stride - width;
+ for (int16 row = 0; row < height; row++) {
+ for (int16 col = 0; col < width; col++) {
+ byte pixel = *src++;
+ if (pixel == 0)
+ pixel = *background;
+
+ *dst++ = pixel;
+ background++;
+ }
+
+ src += skip;
+ background += skip;
+ dst += skip;
+ }
}
-void XpLib::rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+void XpLib::rleBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+ int16 dstSkip = dstStride - width;
+
+ for (int16 row = 0; row < height; row++) {
+ byte *rowEnd = dst + width;
+ while (true) {
+ byte b = *src++;
+ if ((b & 0x80) == 0) {
+ // Literal pixel
+ *dst++ = b;
+ } else {
+ // Run
+ byte val = b & 0x7F;
+ int16 count = *src++;
+ if (count == 0) {
+ count = (int16)(rowEnd - dst);
+ rowEnd = 0;
+ }
+
+ memset(dst, val, count);
+ dst += count;
+
+ if (rowEnd == 0) {
+ dst += dstSkip;
+ break;
+ }
+ }
+ }
+ }
}
-uint32 XpLib::rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags) {
- return 0;
+void XpLib::rleMaskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+ int16 dstSkip = dstStride - width;
+
+ for (int16 row = 0; row < height; row++) {
+ byte *rowEnd = dst + width;
+
+ while (true) {
+ byte b = *src++;
+ if ((b & 0x80) == 0) {
+ // Literal pixel
+ if (b != 0)
+ *dst = b;
+
+ dst++;
+ } else {
+ // Run
+ byte val = b & 0x7F;
+ int16 count = *src++;
+ if (count == 0) {
+ count = (int16)(rowEnd - dst);
+ rowEnd = 0;
+ }
+
+ if (val == 0) {
+ // Transparent run skip...
+ dst += count;
+ } else {
+ memset(dst, val, count);
+ dst += count;
+ }
+
+ if (rowEnd == 0) {
+ dst += dstSkip;
+ break;
+ }
+ }
+ }
+ }
}
-uint16 XpLib::rleDataSize(void *rleData, uint16 height) {
- return 0;
+void XpLib::rleCompositeBlit(byte *rle, byte *background, byte *dst, uint16 width, uint16 height, byte *dirtyFlags) {
+ byte *rlePtr = rle;
+ byte *bgPtr = background;
+ byte *dstPtr = dst;
+
+ for (int16 row = 0; row < height; row++) {
+ byte *rowEnd = dstPtr + width;
+
+ // Check if row is all transparent...
+ if (rlePtr[0] == 0x80 && rlePtr[1] == 0x00) {
+ if (dirtyFlags[row] & 2) {
+ // Row was already transparent: mark clean and skip...
+ dirtyFlags[row] = 3;
+ rlePtr += 2;
+ dstPtr = rowEnd;
+ bgPtr += width;
+ continue;
+ }
+
+ dirtyFlags[row] = 2;
+ } else {
+ dirtyFlags[row] = 0;
+ }
+
+ // Decode RLE tokens for this row...
+ while (true) {
+ byte b = *rlePtr++;
+ if ((b & 0x80) == 0) {
+ // Literal pixel
+ if (b != 0) {
+ *dstPtr++ = b; // opaque
+ bgPtr++;
+ } else {
+ *dstPtr++ = *bgPtr++; // transparent (copy from background)
+ }
+ } else if (b != 0x80) {
+ // Opaque run
+ byte val = b & 0x7F;
+ int16 count = *rlePtr++;
+ if (count == 0) {
+ count = rowEnd - dstPtr;
+ rowEnd = nullptr;
+ }
+
+ bgPtr += count;
+ memset(dstPtr, val, count);
+ dstPtr += count;
+
+ if (rowEnd == nullptr)
+ break;
+ } else {
+ // Transparent run (copy from background)
+ int16 count = *rlePtr++;
+ if (count == 0) {
+ count = rowEnd - dstPtr;
+ rowEnd = nullptr;
+ }
+
+ memcpy(dstPtr, bgPtr, count);
+ dstPtr += count;
+ bgPtr += count;
+
+ if (rowEnd == nullptr)
+ break;
+ }
+ }
+ }
}
-void XpLib::markCursorPixels(void *buffer, uint32 count) {
+uint16 XpLib::rleDataSize(byte *rleData, uint16 height) {
+ byte *start = rleData;
+ byte *ptr = start;
+
+ for (int16 y = 0; y < height; y++) {
+ while (true) {
+ byte b = *ptr++;
+ if (b & 0x80) {
+ byte count = *ptr++;
+ if (count == 0)
+ break;
+ }
+ }
+ }
+
+ return (uint16)(ptr - start);
+}
+void XpLib::markCursorPixels(byte *buffer, uint32 count) {
+ byte *ptr = buffer;
+ for (uint32 i = 0; i < count; i++) {
+ ptr[i] |= 0x80;
+ }
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/cursor.cpp b/engines/bolt/xplib/cursor.cpp
index 923f8fb76ef..cf1f53a3dcb 100644
--- a/engines/bolt/xplib/cursor.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -22,39 +22,131 @@
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
+#include "graphics/cursorman.h"
+
namespace Bolt {
-bool XpLib::initCoords() {
- return false;
+bool XpLib::initCursor() {
+ if (!_bolt->g_extendedViewport) {
+ g_cursorViewportWidth = SCREEN_WIDTH;
+ g_cursorViewportHeight = SCREEN_HEIGHT;
+ } else {
+ g_cursorViewportWidth = EXTENDED_SCREEN_WIDTH;
+ g_cursorViewportHeight = EXTENDED_SCREEN_HEIGHT;
+ }
+
+ g_cursorSprite.pixelData = g_cursorBuffer;
+
+ g_cursorWidth = 16;
+ g_cursorHeight = 16;
+ g_cursorScale = 2;
+
+ return true;
}
-void XpLib::shutdownCoords() {
+void XpLib::shutdownCursor() {
}
bool XpLib::readCursor(uint16 *outButtons, int16 *outX, int16 *outY) {
- return false;
+ if (g_cursorHidden == 0)
+ return false;
+
+ *outX = g_lastCursorX;
+ *outY = g_lastCursorY;
+
+ screenToVirtual(outX, outY);
+
+ if (outButtons != nullptr)
+ *outButtons = getButtonState();
+
+ return true;
}
void XpLib::readJoystick(int16 *outX, int16 *outY) {
+ g_lastCursorX = (int16)((int32)g_lastRegisteredMousePos.x * (int32)g_virtualWidth / (int32)g_cursorViewportWidth);
+ g_lastCursorY = (int16)((int32)g_lastRegisteredMousePos.y * (int32)g_virtualHeight / (int32)g_cursorViewportHeight);
+
+ *outX = g_lastCursorX;
+ *outY = g_lastCursorY;
+
+ screenToVirtual(outX, outY);
}
void XpLib::setCursorPos(int16 x, int16 y) {
+ virtualToScreen(&x, &y);
+
+ g_lastCursorX = x;
+ g_lastCursorY = y;
+
+ if (g_cursorHidden == 0)
+ updateDisplay();
+
+ int16 screenX = (int16)((int32)g_lastCursorX * (int32)g_cursorViewportWidth / (int32)g_virtualWidth);
+ int16 screenY = (int16)((int32)g_lastCursorY * (int32)g_cursorViewportHeight / (int32)g_virtualHeight);
+
+ _bolt->_system->warpMouse(screenX, screenY);
}
-void XpLib::setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY) {
+void XpLib::setCursorImage(byte *bitmap, int16 hotspotX, int16 hotspotY) {
+ byte *dest = g_cursorSprite.pixelData;
+
+ for (int16 row = 32; row > 0; row--) {
+ for (int16 mask = 0x80; mask != 0; mask >>= 1) {
+ *dest++ = (*bitmap & mask) ? 0x80 : 0x00;
+ }
+
+ bitmap++;
+ }
+
+ g_cursorHotspotX = hotspotX;
+ g_cursorHotspotY = hotspotY;
+
+ if (g_cursorHidden == 0)
+ updateDisplay();
}
void XpLib::setCursorColor(byte r, byte g, byte b) {
+ byte rgb[3] = {r, g, b};
+ setPalette(1, 128, rgb);
}
bool XpLib::showCursor() {
- return false;
+ if (g_cursorHidden <= 0)
+ return true;
+
+ g_cursorHidden--;
+ if (g_cursorHidden != 0)
+ return false;
+
+ screenToVirtual(&g_lastCursorX, &g_lastCursorY);
+ setCursorPos(g_lastCursorX, g_lastCursorY);
+ updateDisplay();
+ enableMouse();
+
+ return true;
}
void XpLib::hideCursor() {
+ g_cursorHidden++;
+
+ if (g_cursorHidden == 1) {
+ updateDisplay();
+ disableMouse();
+ }
}
void XpLib::updateCursorPosition() {
+ if (g_cursorHidden != 0)
+ return;
+
+ int16 x = (int16)((int32)g_lastRegisteredMousePos.x * (int32)g_virtualWidth / (int32)g_cursorViewportWidth);
+ int16 y = (int16)((int32)g_lastRegisteredMousePos.y * (int32)g_virtualHeight / (int32)g_cursorViewportHeight);
+
+ if (x != g_lastCursorX || y != g_lastCursorY) {
+ g_lastCursorX = x;
+ g_lastCursorY = y;
+ updateDisplay();
+ }
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
index cc779d2ba07..27b2ee82b16 100644
--- a/engines/bolt/xplib/display.cpp
+++ b/engines/bolt/xplib/display.cpp
@@ -24,87 +24,705 @@
namespace Bolt {
-int16 XpLib::switchDisplayMode(int16 mode) {
- return int16();
-}
-
bool XpLib::initDisplay() {
- return false;
+ g_virtualWidth = 320;
+ g_virtualHeight = 200;
+ g_currentDisplayPage = 0;
+
+ // Create front and back display surfaces
+ if (!createSurface(&g_surfaces[0]))
+ return false;
+
+ if (!createSurface(&g_surfaces[1]))
+ return false;
+
+ fillDisplay(0, 0);
+ fillDisplay(1, 0);
+
+ g_spriteOverlayActive = 0;
+ g_spriteOverlayEnabled = 0;
+ g_frameRateFPS = 0;
+ g_overlayCount = 0;
+ g_prevDirtyCount = 0;
+ g_prevDirtyValid = 0;
+
+ g_cursorBackgroundSave.pixelData = g_cursorBackgroundSaveBuffer;
+ g_cursorBackgroundSave.width = 16;
+ g_cursorBackgroundSave.height = 16;
+
+ return true;
}
void XpLib::shutdownDisplay() {
+ freeSurface(&g_surfaces[0]);
+ freeSurface(&g_surfaces[1]);
+ stopCycle();
}
bool XpLib::createSurface(XPSurface *surf) {
- return false;
+ if (surf->mainPic.width == g_surfaceWidth && surf->mainPic.height == g_surfaceHeight)
+ return true;
+
+ freeSurface(surf);
+
+ surf->mainPic.pixelData = (byte *)allocMem((uint32)g_surfaceWidth * (uint32)g_surfaceHeight);
+ if (!surf->mainPic.pixelData)
+ return false;
+
+ surf->mainPic.palette = (byte *)allocMem(127 * 3);
+ if (!surf->mainPic.palette)
+ return false;
+
+ surf->mainPic.width = g_surfaceWidth;
+ surf->mainPic.height = g_surfaceHeight;
+
+ for (int16 i = 0; i < 127 * 3; i += 3) {
+ surf->mainPic.palette[i] = 0;
+ surf->mainPic.palette[i + 1] = 0;
+ surf->mainPic.palette[i + 2] = 0;
+ }
+
+ surf->mainPic.paletteStart = 1;
+ surf->mainPic.paletteCount = 127;
+ surf->mainPic.flags = 0;
+ surf->overlayPic.pixelData = nullptr;
+ surf->dirtyPalStart = 0;
+ surf->dirtyPalEnd = 0;
+
+ return true;
}
void XpLib::freeSurface(XPSurface *surf) {
+ if (surf->mainPic.palette != nullptr) {
+ freeMem(surf->mainPic.palette);
+ surf->mainPic.palette = nullptr;
+ }
+
+ if (surf->mainPic.pixelData != nullptr) {
+ freeMem(surf->mainPic.pixelData);
+ surf->mainPic.pixelData = nullptr;
+ }
}
bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+ for (int16 i = 0; i < numSpecs; i++) {
+
+ if (specs[i].width == 320 && specs[i].height == 200) {
+ *outMode = i;
+ g_surfaceWidth = specs[i].width;
+ g_surfaceHeight = specs[i].height;
+
+ if (!createSurface(&g_surfaces[0]))
+ return false;
+
+ if (!createSurface(&g_surfaces[1]))
+ return false;
+
+ return true;
+ }
+ }
+
return false;
}
void XpLib::setCoordSpec(int16 x, int16 y, int16 width, int16 height) {
+ if (width == g_surfaceWidth && height == g_surfaceHeight) {
+ g_viewportOffsetX = x;
+ g_viewportOffsetY = y;
+ }
}
void XpLib::virtualToScreen(int16 *x, int16 *y) {
+ *x -= g_viewportOffsetX;
+ *y -= g_viewportOffsetY;
}
void XpLib::screenToVirtual(int16 *x, int16 *y) {
+ *x += g_viewportOffsetX;
+ *y += g_viewportOffsetY;
}
void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
+ XPSurface *surf = &g_surfaces[page];
+
+ virtualToScreen(&x, &y);
+
+ // --- Pixel data ---
+ if (pic->pixelData) {
+ bool isFullScreen = (pic->width == g_surfaceWidth && pic->height == g_surfaceHeight);
+
+ if (isFullScreen)
+ g_spriteOverlayEnabled |= 4; // Mark full-screen overwrite
+
+ // Full-screen transparent non-RLE overlayPic on surface 0: store as overlay source...
+ if (isFullScreen && page == 0 && (pic->flags & 1) && !(pic->flags & 2)) {
+ memcpy(&surf->overlayPic, pic, sizeof(pic));
+ g_spriteOverlayEnabled |= 1;
+ } else {
+ // Clear previous overlay if needed...
+ if ((g_spriteOverlayEnabled & 1) && page == 0) {
+ if (!isFullScreen)
+ clipAndBlit(&surf->overlayPic, surf, 0, 0, nullptr);
+
+ g_spriteOverlayEnabled &= ~1;
+ }
+
+ // Clip and blit to surface...
+ Common::Rect clipResult;
+ if (clipAndBlit(pic, surf, x, y, &clipResult))
+ addDirtyRect(&clipResult);
+ }
+
+ // Mark page as dirty...
+ g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+ }
+
+ // --- Palette data ---
+ if (pic->palette != nullptr) {
+ int16 palStart = pic->paletteStart;
+ int16 palEnd = palStart + pic->paletteCount - 1;
+ int16 adjStart = palStart;
+ int16 adjCount = pic->paletteCount;
+ byte *palData = pic->palette;
+
+ // Skip index 0...
+ if (adjStart == 0) {
+ adjStart = 1;
+ palData += 3;
+ }
+
+ // Clamp to page range (indices 1-127)...
+ if (adjStart > 127)
+ adjStart = 127;
+
+ if (palEnd > 127)
+ palEnd = 127;
+
+ adjCount = palEnd - adjStart + 1;
+
+ if (adjCount > 0) {
+ // Copy palette to surface's palette buffer...
+ int16 destOffset = (adjStart - surf->mainPic.paletteStart) * 3;
+ memcpy(surf->mainPic.palette + destOffset, palData, adjCount * 3);
+ }
+
+ int16 dirtyFlag = (page != 0) ? 0x40 : 0x20;
+
+ if (g_spriteOverlayEnabled & dirtyFlag) {
+ // Merge dirty range...
+ if (surf->dirtyPalStart > adjStart)
+ surf->dirtyPalStart = adjStart;
+
+ if (surf->dirtyPalEnd < palEnd)
+ surf->dirtyPalEnd = palEnd;
+ } else {
+ // First palette update, set range...
+ surf->dirtyPalStart = adjStart;
+ surf->dirtyPalEnd = palEnd;
+ g_spriteOverlayEnabled |= dirtyFlag;
+ }
+ }
}
-bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip) {
- return false;
+void XpLib::dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+ switch (mode) {
+ case 0:
+ blit(src, srcStride, dst, dstStride, width, height);
+ break;
+ case 1:
+ maskBlit(src, srcStride, dst, dstStride, width, height);
+ break;
+ case 2:
+ rleBlit(src, srcStride, dst, dstStride, width, height);
+ break;
+ case 3:
+ rleMaskBlit(src, srcStride, dst, dstStride, width, height);
+ break;
+ }
+}
+
+bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip) {
+ int16 blitMode = 0;
+
+ if (src->flags & 1)
+ blitMode |= 2; // Transparent
+
+ if (src->flags & 2)
+ blitMode |= 1; // RLE
+
+ Common::Rect srcRect(x, y, x + src->width, y + src->height);
+ Common::Rect destRect(0, 0, dest->mainPic.width, dest->mainPic.height);
+
+ Common::Rect clipped;
+ clipped = srcRect.findIntersectingRect(destRect);
+ if (clipped.isEmpty())
+ return false;
+
+ // For transparent blits, check if content actually changed...
+ if (src->flags & 1) {
+ if (memcmp(&clipped, &srcRect, 8) == 0)
+ return false;
+ }
+
+ if (outClip) {
+ outClip->left = clipped.left;
+ outClip->top = clipped.top;
+ outClip->right = clipped.right;
+ outClip->bottom = clipped.bottom;
+ }
+
+ int16 srcOff = (clipped.top - srcRect.top) * src->width + (clipped.left - srcRect.left);
+ int16 destOff = clipped.top * dest->mainPic.width + clipped.left;
+ int16 clipW = clipped.right - clipped.left;
+ int16 clipH = clipped.bottom - clipped.top;
+
+ dispatchBlit(blitMode,
+ src->pixelData + srcOff, src->width,
+ dest->mainPic.pixelData + destOff, dest->mainPic.width,
+ clipW, clipH
+ );
+
+ return true;
}
-void XpLib::addDirtyRect(ClipRect *rect) {
+void XpLib::addDirtyRect(Common::Rect *rect) {
+ if (g_spriteOverlayEnabled & 4)
+ return; // Full-screen overwrite, no need to track...
+
+ if (g_overlayCount >= 30) {
+ g_spriteOverlayEnabled |= 4; // Too many rects, mark full dirty...
+ return;
+ }
+
+ g_dirtyRects[g_overlayCount].left = rect->left;
+ g_dirtyRects[g_overlayCount].right = rect->right;
+ g_dirtyRects[g_overlayCount].top = rect->top;
+ g_dirtyRects[g_overlayCount].bottom = rect->bottom;
+
+ g_overlayCount++;
}
void XpLib::setFrameRate(int16 fps) {
+ g_frameRateFPS = fps;
+ g_nextFrameTime = _bolt->_system->getMillis();
}
void XpLib::updateDisplay() {
+ if (g_cursorHidden == 0)
+ g_spriteOverlayEnabled |= 0x80;
+ else
+ g_spriteOverlayEnabled &= ~0x80;
+
+ // This will call delayMillis (but not pollEvent, since that has to be handled elsewhere)...
+ waitForFrameRate();
+
+ handlePaletteTransitions();
+
+ // If back surface pixels changed, mark all with cursor bit and reset row flags...
+ if (g_spriteOverlayEnabled & 0x10) {
+ markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+ memset(&g_rowDirtyFlags, 0, 200);
+ }
+
+ // Render to screen!
+ if (g_spriteOverlayEnabled & 1) {
+ overlayComposite(); // RLE overlay path
+ } else {
+ compositeToScreen(); // Standard dirty-rect path
+ }
+
+ // Save current state, clear per-frame flags...
+ g_spriteOverlayActive = g_spriteOverlayEnabled;
+ g_spriteOverlayEnabled &= 0x03; // Keep only overlay + double-buffer bits...
+
+ _bolt->_system->copyRectToScreen(g_vgaFramebuffer, 320, 0, 0, 320, 200);
+ _bolt->_system->updateScreen();
}
void XpLib::waitForFrameRate() {
+ if (g_frameRateFPS == 0)
+ return;
+
+ while (_bolt->_system->getMillis() < g_nextFrameTime) {
+ _bolt->_system->delayMillis(16);
+ }
+
+ g_nextFrameTime = (uint32)(_bolt->_system->getMillis() + 1000 / (uint32)g_frameRateFPS);
}
void XpLib::handlePaletteTransitions() {
+ // Only act if palette dirty flags are set alongside pixel dirty flags...
+ if (!((g_spriteOverlayEnabled & 0x20) && (g_spriteOverlayEnabled & 0x08)) &&
+ !((g_spriteOverlayEnabled & 0x40) && (g_spriteOverlayEnabled & 0x10)))
+ return;
+
+ // Build transition code: bit 0 = entering double-buffer, bit 1 = was double-buffer
+ int16 transition = 0;
+ if (g_spriteOverlayEnabled & 2)
+ transition |= 1;
+ if (g_spriteOverlayActive & 2)
+ transition |= 2;
+
+ switch (transition) {
+ case 0:
+ // Single -> Single
+ applyCursorPalette();
+ break;
+
+ case 1: {
+ // Single -> Double
+ if (!(g_spriteOverlayEnabled & 0x40))
+ break;
+
+ // Check if back surface palette actually differs from VGA palette...
+ XPSurface *back = &g_surfaces[1];
+ int16 count = back->dirtyPalEnd - back->dirtyPalStart + 1;
+ int16 surfOffset = (back->dirtyPalStart - back->mainPic.paletteStart) * 3;
+
+ if (memcmp(&g_paletteBuffer[back->dirtyPalStart * 3],
+ back->mainPic.palette + surfOffset,
+ count * 3) != 0)
+ break;
+
+ applyCursorPalette();
+ break;
+ }
+
+ case 2: {
+ // Double -> Single
+ if (!(g_spriteOverlayEnabled & 0x20))
+ break;
+
+ // Check if front surface palette differs from cursor palette page...
+ XPSurface *front = &g_surfaces[0];
+ int16 count = front->dirtyPalEnd - front->dirtyPalStart + 1;
+ int16 surfOffset = (front->dirtyPalStart - front->mainPic.paletteStart) * 3;
+
+ if (memcmp(&g_paletteBuffer[(front->dirtyPalStart + 128) * 3],
+ front->mainPic.palette + surfOffset,
+ count * 3) != 0)
+ break;
+
+ prepareBackSurface();
+ break;
+ }
+
+ case 3:
+ // Double -> Double: no special handling
+ break;
+ }
}
void XpLib::flushPalette() {
+ // Front surface palette -> VGA indices 1-127
+ if (g_spriteOverlayEnabled & 0x20) {
+ XPSurface *front = &g_surfaces[0];
+ int16 start = front->dirtyPalStart;
+ int16 count = front->dirtyPalEnd - start + 1;
+ int16 offset = (start - front->mainPic.paletteStart) * 3;
+ setPalette(count, start, front->mainPic.palette + offset);
+ }
+
+ // Back surface palette -> VGA indices 129-255 (start + 128)
+ if (g_spriteOverlayEnabled & 0x40) {
+ XPSurface *back = &g_surfaces[1];
+ int16 start = back->dirtyPalStart;
+ int16 count = back->dirtyPalEnd - start + 1;
+ int16 offset = (start - back->mainPic.paletteStart) * 3;
+ setPalette(count, start + 128, back->mainPic.palette + offset);
+ }
}
void XpLib::overlayComposite() {
+ bool pixelsDirty = (g_spriteOverlayEnabled & 0x18) != 0;
+ bool cursorNow = (g_spriteOverlayEnabled & 0x80) != 0;
+ bool cursorPrev = (g_spriteOverlayActive & 0x80) != 0;
+
+ // Nothing changed at all, just flush the palette...
+ if (!pixelsDirty && !cursorNow && !cursorPrev) {
+ flushPalette();
+ return;
+ }
+
+ int16 cursorX = 0, cursorY = 0;
+
+ // Decode RLE overlay onto front surface...
+ if (pixelsDirty) {
+ if (g_spriteOverlayEnabled & 2) {
+ // Double-buffer: RLE composite (transparent pixels from back surface)
+ rleCompositeBlit(
+ g_surfaces[0].overlayPic.pixelData,
+ g_surfaces[1].mainPic.pixelData,
+ g_surfaces[0].mainPic.pixelData,
+ g_virtualWidth, g_virtualHeight,
+ g_rowDirtyFlags);
+ } else {
+ // Single-buffer: straight RLE decode to front
+ rleBlit(
+ g_surfaces[0].overlayPic.pixelData,
+ g_surfaces[0].overlayPic.width,
+ g_surfaces[0].mainPic.pixelData,
+ g_surfaces[0].mainPic.width,
+ g_virtualWidth, g_virtualHeight);
+ }
+ }
+
+ // Draw cursor onto front surface...
+ if (cursorNow) {
+ cursorX = g_lastCursorX - g_cursorHotspotX;
+ cursorY = g_lastCursorY - g_cursorHotspotY;
+
+ // Save background under cursor...
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+ -cursorX, -cursorY, nullptr);
+
+ // Draw cursor sprite...
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+ cursorX, cursorY, &g_cursorClipRect2);
+ }
+
+ flushPalette();
+
+ // Transfer front surface to main framebuffer...
+ if (pixelsDirty) {
+ if (g_spriteOverlayEnabled & 2) {
+ // Tracked blit: only copy rows that changed...
+ dirtyBlit(
+ g_surfaces[0].mainPic.pixelData,
+ g_vgaFramebuffer,
+ g_virtualWidth, g_virtualHeight,
+ g_rowDirtyFlags);
+ } else {
+ memcpy(g_vgaFramebuffer, g_surfaces[0].mainPic.pixelData,
+ (uint32)g_virtualWidth * (uint32)g_virtualHeight);
+ }
+ } else {
+ if (cursorNow)
+ compositeDirtyRects(&g_cursorClipRect2, 1);
+
+ if (cursorPrev)
+ compositeDirtyRects(&g_prevCursorRect2, 1);
+ }
+
+ // Save cursor rect and restore front surface
+ if (cursorNow) {
+ g_prevCursorRect2 = g_cursorClipRect2;
+
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+ cursorX, cursorY, nullptr);
+ }
}
void XpLib::compositeToScreen() {
+ int16 cursorX = 0, cursorY = 0;
+
+ // Draw cursor onto front surface...
+ if (g_spriteOverlayEnabled & 0x80) {
+ cursorX = g_lastCursorX - g_cursorHotspotX;
+ cursorY = g_lastCursorY - g_cursorHotspotY;
+
+ // Save background under cursor position...
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], -cursorX, -cursorY, nullptr);
+
+ // Draw cursor sprite at position...
+ clipAndBlit(&g_cursorSprite, &g_surfaces[0], cursorX, cursorY, &g_cursorDirtyRect);
+ }
+
+ flushPalette();
+
+ if (!(g_spriteOverlayEnabled & 2)) {
+ // SINGLE-BUFFER MODE
+
+ if (g_spriteOverlayEnabled & 0x08) {
+ if (g_spriteOverlayEnabled & 4) {
+ // Full-screen dirty, blit entire front surface to framebuffer...
+ blit(g_surfaces[0].mainPic.pixelData, g_surfaces[0].mainPic.width,
+ g_vgaFramebuffer, g_virtualWidth,
+ g_virtualWidth, g_virtualHeight);
+ } else {
+ // Partial dirty, blit changed rects only...
+ if (g_prevDirtyValid > 0) {
+ mergeDirtyRects();
+ blitDirtyRects(g_prevDirtyRects, g_prevDirtyCount);
+ }
+
+ blitDirtyRects(g_dirtyRects, g_overlayCount);
+
+ if (g_spriteOverlayEnabled & 0x80)
+ blitDirtyRects(&g_cursorDirtyRect, 1);
+
+ if (g_spriteOverlayActive & 0x80)
+ blitDirtyRects(&g_prevCursorRect, 1);
+ }
+ } else {
+ // No pixel changes - just cursor
+ if (g_spriteOverlayEnabled & 0x80)
+ blitDirtyRects(&g_cursorDirtyRect, 1);
+
+ if (g_spriteOverlayActive & 0x80)
+ blitDirtyRects(&g_prevCursorRect, 1);
+ }
+
+ } else {
+ // DOUBLE-BUFFER MODE
+
+ if (g_spriteOverlayEnabled & 0x18) {
+ if ((g_spriteOverlayEnabled & 4) ||
+ g_prevDirtyValid > 0 || g_overlayCount > 0) {
+ // Full composite...
+ compositeBlit(g_surfaces[0].mainPic.pixelData, g_surfaces[1].mainPic.pixelData,
+ g_vgaFramebuffer, g_virtualWidth, g_virtualWidth, g_virtualHeight);
+ g_overlayCount = 0;
+ g_prevDirtyCount = 0;
+ } else {
+ // Partial composite...
+ if (g_prevDirtyValid > 0) {
+ mergeDirtyRects();
+ compositeDirtyRects(g_prevDirtyRects, g_prevDirtyCount);
+ }
+
+ compositeDirtyRects(g_dirtyRects, g_overlayCount);
+
+ if (g_spriteOverlayEnabled & 0x80)
+ compositeDirtyRects(&g_cursorDirtyRect, 1);
+
+ if (g_spriteOverlayActive & 0x80)
+ compositeDirtyRects(&g_prevCursorRect, 1);
+ }
+ } else {
+ if (g_spriteOverlayEnabled & 0x80)
+ compositeDirtyRects(&g_cursorDirtyRect, 1);
+
+ if (g_spriteOverlayActive & 0x80)
+ compositeDirtyRects(&g_prevCursorRect, 1);
+ }
+ }
+
+ // Save current dirty rects for next frame...
+ if (g_prevDirtyValid > 0) {
+ g_prevDirtyValid = 0;
+ if (g_overlayCount > 0) {
+ for (int i = 0; i < g_overlayCount; i++) {
+ g_prevDirtyRects[i] = g_dirtyRects[i];
+ }
+ }
+
+ g_prevDirtyCount = g_overlayCount;
+ }
+
+ g_overlayCount = 0;
+
+ // Save cursor state for next frame...
+ if (g_spriteOverlayEnabled & 0x80) {
+ g_prevCursorRect = g_cursorDirtyRect;
+
+ // Restore front surface under cursor...
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], cursorX, cursorY, nullptr);
+ }
}
void XpLib::mergeDirtyRects() {
+ for (int16 i = 0; i < g_prevDirtyCount; i++) {
+ Common::Rect *prev = &g_prevDirtyRects[i];
+
+ for (int16 j = 0; j < g_overlayCount; j++) {
+ Common::Rect *curr = &g_dirtyRects[j];
+
+ if (curr->right < 0)
+ continue; // A negative value indicates "already consumed"...
+
+ if (!prev->intersects(*curr))
+ continue;
+
+ prev->extend(*curr);
+ curr->right *= -1; // Negate to mark consumed...
+ break;
+ }
+ }
}
-void XpLib::blitDirtyRects(ClipRect *rects, int16 count) {
+void XpLib::blitDirtyRects(Common::Rect *rects, int16 count) {
+ for (int16 i = 0; i < count; i++) {
+ Common::Rect *rect = &rects[i];
+
+ if (rect->right < 0) {
+ rect->right = -rect->right;
+ } else {
+ int16 offset = rect->top * g_virtualWidth + rect->left;
+ int16 w = rect->right - rect->left;
+ int16 h = rect->bottom - rect->top;
+
+ blit(g_surfaces[0].mainPic.pixelData + offset, g_surfaces[0].mainPic.width,
+ g_vgaFramebuffer + offset, g_virtualWidth,
+ w, h);
+ }
+ }
}
-void XpLib::compositeDirtyRects(ClipRect *rects, int16 count) {
+void XpLib::compositeDirtyRects(Common::Rect *rects, int16 count) {
+ for (int16 i = 0; i < count; i++) {
+ Common::Rect *rect = &rects[i];
+
+ if (rect->right < 0) {
+ rect->right = -rect->right;
+ } else {
+ int16 offset = rect->top * g_virtualWidth + rect->left;
+ int16 w = rect->right - rect->left;
+ int16 h = rect->bottom - rect->top;
+
+ compositeBlit(
+ g_surfaces[0].mainPic.pixelData + offset,
+ g_surfaces[1].mainPic.pixelData + offset,
+ g_vgaFramebuffer + offset,
+ g_virtualWidth, w, h);
+ }
+ }
}
-void XpLib::applyCursorPalette(bool enable) {
+void XpLib::applyCursorPalette() {
+ setPalette(127, 129, &g_paletteBuffer[3]);
+ markCursorPixels(g_vgaFramebuffer, (uint32)g_virtualWidth * (uint32)g_virtualHeight);
}
void XpLib::prepareBackSurface() {
+ XPPicDesc *src;
+ if (g_spriteOverlayEnabled & 1) {
+ src = &g_surfaces[0].overlayPic;
+ } else {
+ src = &g_surfaces[0].mainPic;
+ }
+
+ clipAndBlit(src, &g_surfaces[1], 0, 0, nullptr);
+
+ markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+
+ memcpy(g_vgaFramebuffer, g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
}
void XpLib::setTransparency(bool toggle) {
+ if (toggle) {
+ g_spriteOverlayEnabled |= 2; // Enables double-buffer compositing
+ } else {
+ g_spriteOverlayEnabled &= ~2;
+ }
}
void XpLib::fillDisplay(byte color, int16 page) {
+
+ XPSurface *surf = &g_surfaces[page];
+ uint32 size = (uint32)surf->mainPic.width * (uint32)surf->mainPic.height;
+
+ // Back surface (page 1) pixels have bit 7 set for cursor palette mapping...
+ byte fillVal = (page == 1) ? (color + 0x80) : color;
+
+ memset(surf->mainPic.pixelData, fillVal, size);
+
+ // Mark page as pixel-dirty...
+ g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+
+ // Clear overlay flag, signal full redraw needed...
+ g_spriteOverlayEnabled &= ~1;
+ g_prevDirtyValid = 1;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 77650c75153..4528c50253a 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -22,98 +22,456 @@
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
+#include "common/events.h"
+
namespace Bolt {
-bool XpLib::initWindow() {
- return false;
+bool XpLib::initEvents() {
+ for (int16 i = 0; i < 40; i++) {
+ g_events[i].next = g_eventFreeList;
+ g_eventFreeList = &g_events[i];
+ }
+
+ g_inactivityTimerId = 0;
+ g_inactivityTimeout = 0;
+ g_mouseButtonState = 0;
+ g_mouseButtonPrev = 0;
+ g_eventMouseMoved = 0;
+ g_eventKeyStates = 0;
+
+ return true;
}
-void XpLib::shutdownWindow() {
+void XpLib::shutdownEvents() {
+ g_eventFreeList = nullptr;
+ g_eventQueueTail = nullptr;
+ g_eventQueueHead = nullptr;
+ if (g_inactivityTimerId != 0) {
+ killTimer(g_inactivityTimerId);
+ g_inactivityTimerId = 0;
+ }
}
int16 XpLib::getEvent(int16 filter, uint32 *outData) {
- return 0;
+ pumpMessages();
+
+ // Search event queue for matching event type...
+ XPEvent *node = g_eventQueueHead;
+ if (filter != 0) {
+ while (node) {
+ if (node->type == filter)
+ break;
+
+ node = node->next;
+ }
+ }
+
+ if (!node)
+ return 0;
+
+ // Remove from queue...
+ unlinkEvent(node);
+
+ // Copy event data to caller...
+ *outData = node->payload;
+
+ // Return node to free list...
+ node->next = g_eventFreeList;
+ g_eventFreeList = node;
+
+ return node->type;
}
int16 XpLib::peekEvent(int16 filter, uint32 *outData) {
- return 0;
-}
+ pumpMessages();
+
+ XPEvent *node = g_eventQueueHead;
+ if (filter != 0) {
+ while (node) {
+ if (node->type == filter)
+ break;
+
+ node = node->next;
+ }
+ }
-void XpLib::unlinkEvent(int16 node) {
+ if (!node)
+ return 0;
+ // Copy data but don't remove from queue...
+ *outData = node->payload;
+ return node->type;
}
-void XpLib::enqueueEvent(int16 node) {
+void XpLib::unlinkEvent(XPEvent *node) {
+ // Remove node from doubly-linked event queue...
+ if (node->prev) {
+ XPEvent *prev = node->prev;
+ prev->next = node->next;
+ } else {
+ g_eventQueueHead = node->next;
+ }
+
+ if (node->next) {
+ XPEvent *next = node->next;
+ next->prev = node->prev;
+ } else {
+ g_eventQueueTail = node->prev;
+ }
+}
+void XpLib::enqueueEvent(XPEvent *node) {
+ // Append node to tail of event queue...
+ node->next = nullptr;
+
+ if (g_eventQueueTail) {
+ XPEvent *tail = g_eventQueueTail;
+ tail->next = node;
+ node->prev = g_eventQueueTail;
+ g_eventQueueTail = node;
+ } else {
+ // Queue was empty...
+ g_eventQueueTail = node;
+ g_eventQueueHead = node;
+ node->prev = nullptr;
+ }
}
void XpLib::pumpMessages() {
-
+ Common::Event event;
+ long timerData;
+ bool mouseMoved = false;
+
+ // Drain any pending sound events...
+ while (pollSound(&timerData) && !_bolt->shouldQuit()) {
+ updateTimers();
+ postEvent(etSound, timerData);
+ }
+
+ // Check inactivity timeout...
+ if (g_inactivityDeadline != 0) {
+ uint32 now = _bolt->_system->getMillis();
+ if (now > g_inactivityDeadline) {
+ g_inactivityDeadline = 0;
+ setCursorPos(160, 100);
+ postJoystickEvent(0, 0, 0);
+ }
+ }
+
+ // Process all pending events...
+ while (_bolt->_eventMan->pollEvent(event) && !_bolt->shouldQuit()) {
+ switch (event.type) {
+ case Bolt::CustomEventType::EVENT_TIMER:
+ handleTimer(event.customType);
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ g_lastRegisteredMousePos.x = event.mouse.x;
+ g_lastRegisteredMousePos.y = event.mouse.y;
+ handleMouseMove(&mouseMoved);
+ break;
+
+ case Common::EVENT_LBUTTONDOWN:
+ handleMouseButton(1, 0);
+ break;
+
+ case Common::EVENT_LBUTTONUP:
+ handleMouseButton(0, 0);
+ break;
+
+ case Common::EVENT_RBUTTONDOWN:
+ handleMouseButton(1, 1);
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ handleMouseButton(0, 1);
+ break;
+
+ case Common::EVENT_MBUTTONDOWN:
+ handleMouseButton(1, 2);
+ break;
+
+ case Common::EVENT_MBUTTONUP:
+ handleMouseButton(0, 2);
+ break;
+
+ case Common::EVENT_KEYDOWN:
+ handleKey(event.kbd.keycode, 1);
+ break;
+
+ case Common::EVENT_KEYUP:
+ handleKey(event.kbd.keycode, 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (mouseMoved)
+ updateCursorPosition();
+
+ updateTimers();
+ _bolt->_system->delayMillis(5);
}
void XpLib::handleTimer(uint32 timerId) {
-
+ if (timerId != g_inactivityTimerId) {
+ postEvent(etTimer, timerId);
+ return;
+ }
+
+ if (g_inactivityCountdown != 0) {
+ g_inactivityCountdown--;
+ if (g_inactivityCountdown == 0)
+ postEvent(etInactivity, 0);
+ }
+
+ if (g_cursorBlinkCountdown != 0) {
+ g_cursorBlinkCountdown--;
+ if (g_cursorBlinkCountdown == 0)
+ activateScreenSaver();
+ }
+
+ if (g_inactivityCountdown != 0 || g_cursorBlinkCountdown != 0) {
+ g_inactivityTimerId = startTimer(1000);
+ } else {
+ g_inactivityTimerId = 0;
+ }
}
void XpLib::handleMouseMove(bool *mouseMoved) {
-
+ resetInactivity();
+
+ if (g_eventKeyStates == eksMouseMode) {
+ *mouseMoved = true;
+ int16 x, y;
+ readCursor(nullptr, &x, &y);
+ postEvent(etMouseMove, ((uint32)x << 16) | (int16)y);
+ g_eventMouseMoved = 0;
+ } else if (g_eventKeyStates == eksJoystickMode) {
+ int16 joyX, joyY;
+ readJoystick(&joyX, &joyY);
+
+ int16 dx = (joyX < 155) ? -1 : (joyX > 165) ? 1 : 0;
+ int16 dy = (joyY < 96) ? -1 : (joyY > 104) ? 1 : 0;
+
+ if (dx != 0 || dy != 0) {
+ setCursorPos(160, 100);
+ postJoystickEvent(0, dx, dy);
+ }
+
+ // Set inactivity deadline 100ms from now...
+ g_inactivityDeadline = _bolt->_system->getMillis() + 100;
+ }
}
void XpLib::handleMouseButton(int16 down, int16 button) {
-
+ resetInactivity();
+
+ if (g_eventKeyStates == eksMouseMode) {
+ int16 x, y;
+ readCursor(nullptr, &x, &y);
+ postEvent(etMouseMove, ((uint32)x << 16) | (int16)y);
+ }
+
+ if (down) {
+ g_mouseButtonPrev |= (1 << button);
+ postEvent(etMouseDown, (uint32)button);
+ } else {
+ g_mouseButtonPrev &= ~(1 << button);
+ postEvent(etMouseUp, (uint32)button);
+ }
}
-void XpLib::handleKey(int16 vkey, int16 down) {
-
+void XpLib::handleKey(Common::KeyCode vkey, int16 down) {
+ resetInactivity();
+
+ if (vkey == Common::KEYCODE_ESCAPE) {
+ if (g_mouseButtonState != down) {
+ if (g_eventKeyStates == eksMouseMode) {
+ int16 x, y;
+ readCursor(nullptr, &x, &y);
+ postEvent(etMouseMove, ((uint32)x << 16) | (int16)y);
+ }
+
+ g_mouseButtonState = down;
+ postEvent(down ? etMouseDown : etMouseUp, 0);
+ }
+
+ return;
+ }
+
+ int8 *dirPtr = nullptr;
+ switch (vkey) {
+ case Common::KEYCODE_LEFT:
+ dirPtr = &g_keyStateLeft;
+ break; // VK_LEFT
+ case Common::KEYCODE_UP:
+ dirPtr = &g_keyStateUp;
+ break; // VK_UP
+ case Common::KEYCODE_RIGHT:
+ dirPtr = &g_keyStateRight;
+ break; // VK_RIGHT
+ case Common::KEYCODE_DOWN:
+ dirPtr = &g_keyStateDown;
+ break; // VK_DOWN
+ }
+
+ if (dirPtr == nullptr)
+ return;
+
+ if (*dirPtr == (int8)down)
+ return;
+
+ *dirPtr = (int8)down;
+
+ if (g_eventKeyStates == eksJoystickMode) {
+ int16 dx = g_keyStateRight - g_keyStateLeft;
+ int16 dy = g_keyStateDown - g_keyStateUp;
+ postJoystickEvent(1, dx, dy);
+ }
}
void XpLib::postJoystickEvent(int16 source, int16 dx, int16 dy) {
+ g_eventMouseMoved = source;
+ if (dx != g_lastJoystickX || dy != g_lastJoystickY) {
+ postEvent(etJoystick, ((uint32)dx << 16) | (uint16)dy);
+ g_lastJoystickX = dx;
+ g_lastJoystickY = dy;
+ }
}
-void XpLib::postEvent(int16 type, uint32 data) {
-
+void XpLib::postEvent(XPEventTypes type, uint32 data) {
+ if (type == etMouseMove) {
+ if (g_lastMouseEventData == data)
+ return;
+
+ g_lastMouseEventData = data;
+ if (g_eventQueueTail->type == etMouseMove) {
+ g_eventQueueTail->payload = data;
+ return;
+ }
+ }
+
+ XPEvent *node;
+ if (g_eventFreeList) {
+ // Allocate from free list...
+ node = g_eventFreeList;
+ g_eventFreeList = g_eventFreeList->next;
+ } else {
+ // Queue full: try to evict a droppable event...
+ if (!canDropEvent(type))
+ return;
+
+ // Walk queue from tail looking for droppable event...
+ node = g_eventQueueTail;
+ while (node) {
+ if (canDropEvent(node->type))
+ break;
+
+ node = node->prev;
+ }
+
+ if (!node)
+ node = g_eventQueueHead;
+
+ unlinkEvent(node);
+ }
+
+ node->type = type;
+ node->payload = data;
+ node->next = nullptr;
+ enqueueEvent(node);
}
bool XpLib::canDropEvent(int16 type) {
- return false;
+ switch (type) {
+ case etMouseMove:
+ case etMouseDown:
+ case etMouseUp:
+ case etJoystick:
+ return false;
+ default:
+ return true;
+ }
}
int16 XpLib::setInactivityTimer(int16 seconds) {
- return 0;
+ int16 prev = g_inactivityTimerValue;
+
+ g_inactivityTimerValue = seconds;
+ g_inactivityCountdown = seconds;
+
+ if (seconds != 0 && g_inactivityTimerId == 0)
+ g_inactivityTimerId = startTimer(1000);
+
+ // Drain any pending inactivity events from the queue...
+ uint32 payloadDummy;
+
+ // getEvent calls pumpMessages which calls pollEvent and delayMillis...
+ while (getEvent(etInactivity, &payloadDummy) == etInactivity && !_bolt->shouldQuit());
+
+ return prev;
}
-int16 XpLib::setScreenSaverTimer(int16 time) {
- return 0;
+int16 XpLib::setScreenSaverTimer(int16 seconds) {
+ int16 prev = g_screenSaverTimerValue;
+
+ g_screenSaverTimerValue = seconds;
+ g_cursorBlinkCountdown = seconds;
+
+ if (seconds != 0 && g_inactivityTimerId == 0)
+ g_inactivityTimerId = startTimer(1000);
+
+ return prev;
}
void XpLib::activateScreenSaver() {
-
+ g_inactivityTimeout = 1;
+ setScreenBrightness(25); // Dim to 25%...
}
void XpLib::resetInactivity() {
+ if (g_inactivityTimeout != 0) {
+ setScreenBrightness(100); // Restore full brightness...
+ g_inactivityTimeout = 0;
+ }
+
+ // Reset screensaver countdown
+ g_cursorBlinkCountdown = g_screenSaverTimerValue;
+ if (g_screenSaverTimerValue != 0 && g_inactivityTimerId == 0)
+ g_inactivityTimerId = startTimer(1000);
}
bool XpLib::enableController() {
- return false;
+ // Don't switch if already in mouse mode...
+ if (g_eventKeyStates == eksMouseMode)
+ return false;
+
+ g_eventKeyStates = eksJoystickMode;
+ setCursorPos(160, 100);
+ return true;
}
void XpLib::disableController() {
-
+ g_eventKeyStates = eksInputOff;
+ g_inactivityDeadline = 0;
}
void XpLib::enableMouse() {
-
+ g_eventKeyStates = eksMouseMode;
}
void XpLib::disableMouse() {
-
+ g_eventKeyStates = eksInputOff;
}
int16 XpLib::getButtonState() {
- return 0;
+ return g_mouseButtonPrev | g_mouseButtonState;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index 6a06ad897a6..f478a0178df 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -22,31 +22,53 @@
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
+#include "common/file.h"
+
namespace Bolt {
void XpLib::fileError(const char *message) {
+ stopCycle();
+ error(message);
}
-int32 XpLib::createFile(const char *fileName) {
- return int32();
-}
+Common::File *XpLib::openFile(const char *fileName, short flags) {
+ // Read-write (mode 3) and write (mode 2) are unsupported and not needed by Cartoon Carnival...
+ assert(flags == 1);
-void XpLib::deleteFile(const char *fileName) {
-}
+ Common::File *file = new Common::File();
+ if (!file->exists(fileName)) {
+ fileError("File does not exist.");
+ }
-int32 XpLib::openFile(const char *fileName, short flags) {
- return int32();
+ file->open(fileName);
+ if (!file->isOpen()) {
+ fileError("Disc error. The Disc may be dirty.");
+ return nullptr;
+ }
+
+ return file;
}
-void XpLib::closeFile(int32 handle) {
+void XpLib::closeFile(Common::File *handle) {
+ if (handle->isOpen()) {
+ handle->close();
+ }
}
-bool XpLib::readFile(int32 handle, void *buffer, uint32 *size) {
- return false;
+bool XpLib::readFile(Common::File *handle, void *buffer, uint32 *size) {
+ uint32 sizeRead = handle->read(buffer, *size);
+ bool readEntireSize = sizeRead == *size;
+ *size = sizeRead;
+
+ if (!readEntireSize) {
+ fileError("Disc error. The Disc may be dirty.");
+ }
+
+ return readEntireSize;
}
-bool XpLib::setFilePos(int32 handle, uint32 offset, int16 origin) {
- return false;
+bool XpLib::setFilePos(Common::File *handle, int32 offset, int32 origin) {
+ return handle->seek(offset + origin, SEEK_SET);
}
void *XpLib::allocMem(uint32 size) {
diff --git a/engines/bolt/xplib/mem.cpp b/engines/bolt/xplib/mem.cpp
deleted file mode 100644
index fe512e1b830..00000000000
--- a/engines/bolt/xplib/mem.cpp
+++ /dev/null
@@ -1,27 +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 3 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, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "bolt/bolt.h"
-#include "bolt/xplib/xplib.h"
-
-namespace Bolt {
-
-} // End of namespace Bolt
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index 81c6b5534f1..fd33eadd3ed 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -22,26 +22,155 @@
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
+#include "common/timer.h"
+
namespace Bolt {
+void XpLib::getPalette(uint16 startIndex, uint16 count, byte *destBuf) {
+ Common::StackLock lock(g_paletteMutex);
+
+ memcpy(destBuf, &g_paletteBuffer[startIndex * 3], count * 3);
+}
+
+void XpLib::setPalette(uint16 count, uint16 startIndex, byte *srcBuf) {
+ Common::StackLock lock(g_paletteMutex);
+
+ if (startIndex == 0 || (startIndex == 128 && count != 1)) {
+ startIndex++;
+ srcBuf += 3;
+ count--;
+ }
+
+ if (count == 0)
+ return;
+
+ memcpy(&g_paletteBuffer[startIndex * 3], srcBuf, count * 3);
+
+ // Apply brightness shift into shifted buffer...
+ const byte *src = &g_paletteBuffer[startIndex * 3];
+ byte *dst = &g_shiftedPaletteBuffer[startIndex * 3];
-void XpLib::getPalette(uint16 startIndex, uint16 count, void *destBuf) {
+ for (uint16 i = 0; i < count * 3; i++)
+ dst[i] = src[i] >> g_brightnessShift;
+
+ // Finally set the palette!
+ g_system->getPaletteManager()->setPalette(&g_shiftedPaletteBuffer[startIndex * 3], startIndex, count);
}
-void XpLib::setPalette(uint16 count, uint16 startIndex, void *srcBu) {
+void XpLib::cycleColorsCallback(void *refCon) {
+ XpLib *xp = (XpLib *)refCon;
+ xp->cycleColors();
}
-bool XpLib::startCycle(XPCycleSpec *specs) {
- return false;
+bool XpLib::startCycle(XPCycleState *specs) {
+ Common::StackLock lock(g_paletteMutex);
+
+ stopCycle();
+
+ bool anyActive = false;
+ uint32 now = g_system->getMillis();
+
+ for (int16 i = 0; i < 4; i++) {
+ if (specs[i].startIndex == specs[i].endIndex) {
+ g_cycleStates[i].active = false;
+ continue;
+ }
+
+ g_cycleStates[i].startIndex = specs[i].startIndex;
+ g_cycleStates[i].endIndex = specs[i].endIndex;
+ g_cycleStates[i].delay = specs[i].delay;
+ g_cycleStates[i].nextFire = now + specs[i].delay;
+ g_cycleStates[i].active = true;
+ anyActive = true;
+ }
+
+ if (anyActive) {
+ _bolt->getTimerManager()->installTimerProc(
+ cycleColorsCallback, 50 * 1000, this, "BoltPalCycle");
+ }
+
+ return true;
}
void XpLib::cycleColors() {
+ Common::StackLock lock(g_paletteMutex);
+
+ for (int i = 0; i < 4; i++) {
+ if (!g_cycleStates[i].active)
+ continue;
+
+ uint32 now = g_system->getMillis();
+ if (now < g_cycleStates[i].nextFire)
+ continue;
+
+ g_cycleStates[i].nextFire = now + g_cycleStates[i].delay;
+
+ int16 startIdx = g_cycleStates[i].startIndex;
+ int16 endIdx = g_cycleStates[i].endIndex;
+
+ if (startIdx < endIdx) {
+ int16 count = endIdx - startIdx + 1;
+ if (count > 19 || startIdx < 0 || endIdx > 255)
+ continue;
+
+ // Read current palette range...
+ getPalette(startIdx, count, &g_cycleTempPalette[3]);
+
+ // Save last entry into first position...
+ memcpy(g_cycleTempPalette, &g_cycleTempPalette[count * 3], 3);
+
+ // Write back shifted by one (swap buffer starts 3 bytes earlier)...
+ setPalette(count, startIdx, g_cycleTempPalette);
+
+ } else if (startIdx > endIdx) {
+ int16 count = startIdx - endIdx + 1;
+ if (count > 19 || endIdx < 0 || startIdx > 255)
+ continue;
+
+ // Read current palette range...
+ getPalette(endIdx, count, g_cycleTempPalette);
+
+ // Save first entry, append after last...
+ memcpy(&g_cycleTempPalette[count * 3], g_cycleTempPalette, 3);
+
+ // Write back shifted by one (starts one entry later)...
+ setPalette(count, endIdx, &g_cycleTempPalette[3]);
+ }
+ }
}
void XpLib::stopCycle() {
+ Common::StackLock lock(g_paletteMutex);
+
+ bool wasActive = false;
+ for (int16 i = 0; i < 4; i++) {
+ if (g_cycleStates[i].active) {
+ wasActive = true;
+ g_cycleStates[i].active = false;
+ }
+ }
+
+ if (wasActive)
+ _bolt->getTimerManager()->removeTimerProc(cycleColorsCallback);
}
void XpLib::setScreenBrightness(uint8 percent) {
+ Common::StackLock lock(g_paletteMutex);
+
+ if (percent >= 100) {
+ g_brightnessShift = 2; // Full brightness (VGA DAC is 6-bit)
+ } else if (percent >= 50) {
+ g_brightnessShift = 3; // 50%
+ } else if (percent >= 25) {
+ g_brightnessShift = 4; // 25%
+ } else if (percent >= 12) {
+ g_brightnessShift = 5; // 12%
+ } else {
+ g_brightnessShift = 6; // Near black
+ }
+
+ // Re-apply entire palette with new brightness...
+ setPalette(256, 0, g_paletteBuffer);
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index b1a26a29671..2bb62117449 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -25,11 +25,11 @@
namespace Bolt {
bool XpLib::pollSound(void *outData) {
- return false;
+ return true;
}
bool XpLib::initSound() {
- return false;
+ return true;
}
void XpLib::waveCb() {
@@ -40,7 +40,7 @@ void XpLib::shutdownSound() {
}
-bool XpLib::playSound(void *data, uint32 size, int16 sampleRate) {
+bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
return false;
}
diff --git a/engines/bolt/xplib/timer.cpp b/engines/bolt/xplib/timer.cpp
index 9b8d710f982..16cc3294868 100644
--- a/engines/bolt/xplib/timer.cpp
+++ b/engines/bolt/xplib/timer.cpp
@@ -25,20 +25,63 @@
namespace Bolt {
bool XpLib::initTimer() {
- return false;
+ for (int i = 0; i < ARRAYSIZE(g_timers); i++)
+ g_timers[i].active = false;
+
+ g_nextTimerId = 1;
+ g_timerInitialized = true;
+
+ return true;
}
void XpLib::shutdownTimer() {
+ if (!g_timerInitialized) {
+ g_timerInitialized = false;
+
+ for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+ g_timers[i].active = false;
+ }
+ }
}
-int32 XpLib::startTimer(int16 delay) {
+uint32 XpLib::startTimer(int16 delay) {
+ uint32 now = _bolt->_system->getMillis();
+
+ for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+ if (!g_timers[i].active) {
+ g_timers[i].id = g_nextTimerId++;
+
+ if (g_nextTimerId == 0)
+ g_nextTimerId = 1;
+
+ g_timers[i].deadline = now + delay;
+ g_timers[i].active = true;
+ return g_timers[i].id;
+ }
+ }
+
return 0;
}
-void XpLib::timeCb() {
+void XpLib::updateTimers() {
+ uint32 now = _bolt->_system->getMillis();
+
+ for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+ if (g_timers[i].active && now >= g_timers[i].deadline) {
+ g_timers[i].active = false;
+ handleTimer(g_timers[i].id);
+ }
+ }
}
-bool XpLib::killTimer(int32 timerId) {
+bool XpLib::killTimer(uint32 timerId) {
+ for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+ if (g_timers[i].active && g_timers[i].id == timerId) {
+ g_timers[i].active = false;
+ return true;
+ }
+ }
+
return false;
}
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
index 4cdc6f476bb..6a4057dab61 100644
--- a/engines/bolt/xplib/xplib.cpp
+++ b/engines/bolt/xplib/xplib.cpp
@@ -27,17 +27,65 @@ namespace Bolt {
XpLib::XpLib(BoltEngine *bolt) {
_bolt = bolt;
- initialize();
+ memset(g_cursorBuffer, 0, sizeof(g_cursorBuffer));
+ memset(g_cursorBackgroundSaveBuffer, 0, sizeof(g_cursorBackgroundSaveBuffer));
+
+ memset(g_paletteBuffer, 0, sizeof(g_paletteBuffer));
+ memset(g_shiftedPaletteBuffer, 0, sizeof(g_shiftedPaletteBuffer));
+ memset(g_cycleTimerIds, 0, sizeof(g_cycleTimerIds));
+ memset(g_cycleTempPalette, 0, sizeof(g_cycleTempPalette));
+
+ _screen = new Graphics::Screen();
}
XpLib::~XpLib() {
terminate();
+
+ delete _screen;
}
-void XpLib::initialize() {
+bool XpLib::initialize() {
+ if (g_xpInitialized)
+ return false;
+
+ g_xpInitialized = true;
+
+ if (!initTimer()) {
+ terminate();
+ return false;
+ }
+
+ if (!initEvents()) {
+ terminate();
+ return false;
+ }
+
+ if (!initSound()) {
+ terminate();
+ return false;
+ }
+
+ if (!initDisplay()) {
+ terminate();
+ return false;
+ }
+
+ if (!initCursor()) {
+ terminate();
+ return false;
+ }
+
+ return true;
}
void XpLib::terminate() {
+ shutdownCursor();
+ shutdownDisplay();
+ shutdownSound();
+ shutdownEvents();
+ shutdownTimer();
+
+ g_xpInitialized = false;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index fd4031a5c35..ff9057e379d 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -24,30 +24,31 @@
#include "bolt/bolt.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/mutex.h"
+#include "graphics/paletteman.h"
+
namespace Bolt {
struct DisplaySpecs;
class BoltEngine;
-typedef struct XPCycleSpec {
- short startIndex; // +0x00: first palette index
- short endIndex; // +0x02: last palette index
- short delay; // +0x04: cycle period in ms
-} XPCycleSpec; // 6 bytes
+struct XPCycleState {
+ int16 startIndex;
+ int16 endIndex;
+ int16 delay; // ms between rotations
+ uint32 nextFire; // getMillis() deadline for next rotation
+ bool active;
-typedef struct XPSurface {
- byte *pixelData; // +0x00: pixel buffer (width * height bytes, 8bpp)
- int16 width; // +0x04: surface width
- int16 height; // +0x06: surface height
- byte *palette; // +0x08: RGB palette data (127 * 3 = 381 bytes)
- int16 paletteStart; // +0x0C: first palette index (1)
- int16 paletteCount; // +0x0E: number of palette entries (127)
- int16 flags; // +0x10
- long overlayData; // +0x12
- char _pad[0x10]; // +0x16..+0x23 (unknown)
- int16 clipX; // +0x24
- int16 clipY; // +0x26
-} XPSurface; // total: 0x28 = 40 bytes
+ XPCycleState() {
+ startIndex = 0;
+ endIndex = 0;
+ delay = 0;
+ nextFire = 0;
+ active = false;
+ }
+};
typedef struct XPPicDesc {
byte *pixelData; // +0x00: pixel buffer (8bpp)
@@ -57,33 +58,93 @@ typedef struct XPPicDesc {
int16 paletteStart; // +0x0C: first palette index
int16 paletteCount; // +0x0E: number of palette entries
int16 flags; // +0x10: bit 0 = transparent, bit 1 = RLE
-} XPPicDesc; // 0x12 = 18 bytes
-typedef void (*BlitFunc)(void *src, int16 srcStride, void *dest, int16 destStride, int16 width, int16 height);
+ XPPicDesc() {
+ pixelData = nullptr;
+ width = 0;
+ height = 0;
+ palette = nullptr;
+ paletteStart = 0;
+ paletteCount = 0;
+ flags = 0;
+ }
+} XPPicDesc;
+
+typedef struct XPSurface {
+ XPPicDesc mainPic;
+ XPPicDesc overlayPic;
+ int16 dirtyPalStart; // +0x24
+ int16 dirtyPalEnd; // +0x26
+
+ XPSurface() {
+ dirtyPalStart = 0;
+ dirtyPalEnd = 0;
+ }
+} XPSurface; // total: 0x28 = 40 bytes
+
+enum XPEventTypes : int16 {
+ etEmpty = 0,
+ etTimer = 1,
+ etMouseMove = 2,
+ etMouseDown = 3,
+ etMouseUp = 4,
+ etJoystick = 5,
+ etSound = 6,
+ etInactivity = 7
+};
+
+enum XPEventKeyStates : int16 {
+ eksInputOff = 0,
+ eksMouseMode = 1,
+ eksJoystickMode = 2
+};
+
+enum CustomEventType {
+ EVENT_TIMER = Common::EVENT_USER_FIRST_AVAILABLE
+};
+
+typedef struct XPEvent {
+ XPEvent *prev;
+ XPEvent *next;
+ XPEventTypes type;
+ uint32 payload;
+
+ XPEvent() {
+ prev = nullptr;
+ next = nullptr;
+ type = etEmpty;
+ payload = 0;
+ }
+} XPEvent;
+
+typedef struct XPTimer {
+ uint32 id;
+ uint32 deadline;
+ bool active;
-typedef struct ClipRect {
- short left;
- short top;
- short right;
- short bottom;
-} ClipRect;
+ XPTimer() {
+ id = 0;
+ deadline = 0;
+ active = false;
+ }
+} XPTimer;
class XpLib {
public:
XpLib(BoltEngine *bolt);
~XpLib();
- void initialize();
+ bool initialize();
void terminate();
// Blit
- void blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
- void maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
+ void blit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+ void maskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
// Palette
- void getPalette(uint16 startIndex, uint16 count, void *destBuf);
- void setPalette(uint16 count, uint16 startIndex, void *srcBuf);
- bool startCycle(XPCycleSpec *specs);
+ void getPalette(uint16 startIndex, uint16 count, byte *destBuf);
+ void setPalette(uint16 count, uint16 startIndex, byte *srcBuf);
+ bool startCycle(XPCycleState *specs);
void cycleColors();
void stopCycle();
void setScreenBrightness(uint8 percent);
@@ -91,7 +152,7 @@ public:
// Cursor
bool readCursor(uint16 *outButtons, int16 *outX, int16 *outY);
void setCursorPos(int16 x, int16 y);
- void setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY);
+ void setCursorImage(byte *bitmap, int16 hotspotX, int16 hotspotY);
void setCursorColor(byte r, byte g, byte b);
bool showCursor();
void hideCursor();
@@ -100,9 +161,9 @@ public:
// Events
int16 getEvent(int16 filter, uint32 *outData);
int16 peekEvent(int16 filter, uint32 *outData);
- void postEvent(int16 type, uint32 data);
+ void postEvent(XPEventTypes type, uint32 data);
int16 setInactivityTimer(int16 seconds);
- int16 setScreenSaverTimer(int16 time);
+ int16 setScreenSaverTimer(int16 seconds);
bool enableController();
void disableController();
@@ -120,55 +181,77 @@ public:
void randomize();
// File
- int32 createFile(const char *fileName);
- void deleteFile(const char *fileName);
- int32 openFile(const char *fileName, short flags);
- void closeFile(int32 handle);
- bool readFile(int32 handle, void *buffer, uint32 *size);
- bool setFilePos(int32 handle, uint32 offset, int16 origin);
+ Common::File *openFile(const char *fileName, short flags);
+ void closeFile(Common::File *handle);
+ bool readFile(Common::File *handle, void *buffer, uint32 *size);
+ bool setFilePos(Common::File *handle, int32 offset, int32 origin);
void *allocMem(uint32 size);
void *tryAllocMem(uint32 size);
void freeMem(void *mem);
// Sound
void waveCb();
- bool playSound(void *data, uint32 size, int16 sampleRate);
+ bool playSound(byte *data, uint32 size, int16 sampleRate);
bool pauseSound();
bool resumeSound();
bool stopSound();
// Timer
- int32 startTimer(int16 delay);
- void timeCb();
- bool killTimer(int32 timerId);
+ uint32 startTimer(int16 delay);
+ void updateTimers();
+ bool killTimer(uint32 timerId);
protected:
- BoltEngine *_bolt;
+ BoltEngine *_bolt = nullptr;
+ bool g_xpInitialized = false;
// Blit
- void dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height);
- uint32 compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height);
- void rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
- void rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
- uint32 rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags);
- uint16 rleDataSize(void *rleData, uint16 height);
- void markCursorPixels(void *buffer, uint32 count);
+ void dirtyBlit(byte *src, byte *dst, uint16 width, uint16 height, byte *dirtyFlags);
+ void compositeBlit(byte *src, byte *background, byte *dst, uint16 stride, uint16 width, uint16 height);
+ void rleBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+ void rleMaskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+ void rleCompositeBlit(byte *rle, byte *background, byte *dst, uint16 width, uint16 height, byte *dirtyFlags);
+ uint16 rleDataSize(byte *rleData, uint16 height);
+ void markCursorPixels(byte *buffer, uint32 count);
+
+ // Palette
+ XPCycleState g_cycleStates[4];
+ byte g_paletteBuffer[3 * 256];
+ byte g_shiftedPaletteBuffer[3 * 256];
+ byte g_cycleTempPalette[3 * 20];
+ uint32 g_cycleTimerIds[4];
+ int16 g_brightnessShift = 0;
+ Common::Mutex g_paletteMutex;
+
+ static void cycleColorsCallback(void *refConf);
// Cursor
- bool initCoords();
- void shutdownCoords();
+ bool initCursor();
+ void shutdownCursor();
void readJoystick(int16 *outX, int16 *outY);
+ byte g_cursorBuffer[16 * 16];
+ int16 g_cursorWidth = 0;
+ int16 g_cursorHeight = 0;
+ int16 g_cursorScale = 0;
+ int16 g_cursorHotspotX = 0;
+ int16 g_cursorHotspotY = 0;
+ int16 g_lastCursorX = 0;
+ int16 g_lastCursorY = 0;
+ int16 g_cursorViewportWidth = 0;
+ int16 g_cursorViewportHeight = 0;
+ int16 g_cursorHidden = 1;
+
// Events
- bool initWindow();
- void shutdownWindow();
- void unlinkEvent(int16 node);
- void enqueueEvent(int16 node);
+ bool initEvents();
+ void shutdownEvents();
+ void unlinkEvent(XPEvent *node);
+ void enqueueEvent(XPEvent *node);
void pumpMessages();
void handleTimer(uint32 timerId);
void handleMouseMove(bool *mouseMoved);
void handleMouseButton(int16 down, int16 button);
- void handleKey(int16 vkey, int16 down);
+ void handleKey(Common::KeyCode vkey, int16 down);
void postJoystickEvent(int16 source, int16 dx, int16 dy);
bool canDropEvent(int16 type);
void activateScreenSaver();
@@ -177,27 +260,81 @@ protected:
void disableMouse();
int16 getButtonState();
+ XPEvent g_events[40];
+
+ int8 g_keyStateLeft = 0;
+ int8 g_keyStateRight = 0;
+ int8 g_keyStateUp = 0;
+ int8 g_keyStateDown = 0;
+ int16 g_lastJoystickX = 0;
+ int16 g_lastJoystickY = 0;
+ int16 g_mouseButtonPrev = 0;
+ int16 g_mouseButtonState = 0;
+ int16 g_eventMouseMoved = 0;
+ int16 g_eventKeyStates = 0;
+ uint32 g_inactivityDeadline = 0;
+
+ XPEvent *g_eventQueueHead = nullptr;
+ XPEvent *g_eventQueueTail = nullptr;
+ XPEvent *g_eventFreeList = nullptr;
+
+ uint32 g_lastMouseEventData = 0;
+ Common::Point g_lastRegisteredMousePos;
+
// Display
- int16 switchDisplayMode(int16 mode);
bool initDisplay();
void shutdownDisplay();
bool createSurface(XPSurface *surf);
void freeSurface(XPSurface *surf);
void virtualToScreen(int16 *x, int16 *y);
void screenToVirtual(int16 *x, int16 *y);
- bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip);
- void addDirtyRect(ClipRect *rect);
+ void dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+ bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip);
+ void addDirtyRect(Common::Rect *rect);
void waitForFrameRate();
void handlePaletteTransitions();
void flushPalette();
void overlayComposite();
void compositeToScreen();
void mergeDirtyRects();
- void blitDirtyRects(ClipRect *rects, int16 count);
- void compositeDirtyRects(ClipRect *rects, int16 count);
- void applyCursorPalette(bool enable);
+ void blitDirtyRects(Common::Rect *rects, int16 count);
+ void compositeDirtyRects(Common::Rect *rects, int16 count);
+ void applyCursorPalette();
void prepareBackSurface();
+ Graphics::Screen *_screen = nullptr;
+
+ XPSurface g_surfaces[2];
+
+ int16 g_virtualWidth = 0;
+ int16 g_virtualHeight = 0;
+ int16 g_currentDisplayPage = 0;
+ int16 g_spriteOverlayActive = 0;
+ int16 g_spriteOverlayEnabled = 0;
+ int16 g_frameRateFPS = 0;
+ int16 g_overlayCount = 0;
+ int16 g_prevDirtyCount = 0;
+ int16 g_prevDirtyValid = 0;
+ int16 g_surfaceWidth = 0;
+ int16 g_surfaceHeight = 0;
+ int16 g_viewportOffsetX = 0;
+ int16 g_viewportOffsetY = 0;
+ uint32 g_nextFrameTime = 0;
+
+ Common::Rect g_dirtyRects[30];
+ Common::Rect g_prevDirtyRects[30];
+ Common::Rect g_cursorDirtyRect;
+ Common::Rect g_prevCursorRect;
+ Common::Rect g_cursorClipRect2;
+ Common::Rect g_prevCursorRect2;
+
+ byte g_vgaFramebuffer[320 * 200];
+ byte g_cursorBackgroundSaveBuffer[16 * 16];
+ byte g_rowDirtyFlags[200];
+
+ XPPicDesc g_cursorBackgroundSave;
+ XPPicDesc g_cursorSprite;
+
// File
void fileError(const char *message);
@@ -209,6 +346,18 @@ protected:
// Timer
bool initTimer();
void shutdownTimer();
+
+ uint32 g_inactivityTimerId = 0;
+ int16 g_inactivityCountdown = 0;
+ int16 g_inactivityTimerValue = 0;
+ int16 g_cursorBlinkCountdown = 0;
+ int16 g_screenSaverTimerValue = 0;
+ int16 g_inactivityTimeout = 0;
+
+ bool g_timerInitialized = false;
+
+ XPTimer g_timers[128];
+ uint16 g_nextTimerId = 0;
};
} // End of namespace Bolt
Commit: e358ac0d4615d956b4506f1c55553c37283b5d13
https://github.com/scummvm/scummvm/commit/e358ac0d4615d956b4506f1c55553c37283b5d13
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement main part of the game engine
Changed paths:
A engines/bolt/av.cpp
A engines/bolt/barker.cpp
A engines/bolt/booths/fred.cpp
A engines/bolt/booths/george.cpp
A engines/bolt/booths/huck.cpp
A engines/bolt/booths/scooby.cpp
A engines/bolt/booths/topcat.cpp
A engines/bolt/booths/yogi.cpp
A engines/bolt/rtf.cpp
A engines/bolt/ssprite.cpp
A engines/bolt/state.cpp
A engines/bolt/swap.cpp
A engines/bolt/utils.cpp
R engines/bolt/gfx.cpp
engines/bolt/anim.cpp
engines/bolt/bolt.cpp
engines/bolt/bolt.h
engines/bolt/booth.cpp
engines/bolt/detection.h
engines/bolt/detection_tables.h
engines/bolt/metaengine.cpp
engines/bolt/module.mk
engines/bolt/resource.cpp
engines/bolt/xplib/blit.cpp
engines/bolt/xplib/cursor.cpp
engines/bolt/xplib/display.cpp
engines/bolt/xplib/events.cpp
engines/bolt/xplib/file.cpp
engines/bolt/xplib/palette.cpp
engines/bolt/xplib/sound.cpp
engines/bolt/xplib/xplib.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/anim.cpp b/engines/bolt/anim.cpp
index fb1fd5bdb65..9c869224c5e 100644
--- a/engines/bolt/anim.cpp
+++ b/engines/bolt/anim.cpp
@@ -23,98 +23,70 @@
namespace Bolt {
-void *BoltEngine::openRTF(const char *fileName) {
- return nullptr;
-}
-
-void BoltEngine::closeRTF(void *rtf) {
-}
-
-bool BoltEngine::playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize) {
- return false;
-}
-
-bool BoltEngine::fillRTFBuffer() {
- return false;
-}
-
-void BoltEngine::flushRTFSoundQueue() {
-}
-
-bool BoltEngine::maintainRTF(int16 mode, void *outFrameData) {
- return false;
-}
-
-bool BoltEngine::isRTFPlaying() {
- return false;
-}
-
-void BoltEngine::killRTF() {
-}
-
-void BoltEngine::readPacket() {
-
-}
-
-void BoltEngine::preProcessPacket() {
-}
+bool BoltEngine::startAnimation(RTFResource *rtf, int16 animIndex) {
+ if (!initAnim(rtf, animIndex)) {
+ cleanUpAnim();
+ return false;
+ }
-void BoltEngine::queuePacket() {
+ return true;
}
-void BoltEngine::deQueuePacket() {
+void BoltEngine::stopAnimation() {
+ killRTF(nullptr);
+ cleanUpAnim();
}
-void BoltEngine::allocPacket() {
-}
-
-void BoltEngine::freePacket() {
-}
-
-void BoltEngine::resetPlaybackState() {
-}
+bool BoltEngine::maintainAudioPlay(int16 mode) {
+ RTFPacket *frameData;
-void BoltEngine::sub_12980() {
-}
-
-void BoltEngine::prepareAV() {
-}
-
-void BoltEngine::maintainAV() {
-}
+ if (!maintainRTF(mode, &frameData)) {
+ cleanUpAnim();
+ return false;
+ }
-void BoltEngine::stopAV() {
-}
+ // Check for "TRGR" trigger marker in frame data
+ if (frameData && frameData->tag == MKTAG('T', 'R', 'G', 'R')) {
+ _xp->postEvent(etTrigger, 0);
+ }
-bool BoltEngine::playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
- return false;
+ return true;
}
-void BoltEngine::processPacket() {
-}
+bool BoltEngine::initAnim(RTFResource *rtf, int16 animIndex) {
+ int32 bufSize;
-void BoltEngine::processRL7() {
-}
+ g_animPrevInactivityTimer = _xp->setInactivityTimer(0);
-void BoltEngine::processPLTE() {
-}
+ // Try 80KB ring buffer, fall back to 40KB
+ bufSize = 0x14000;
+ g_animRingBuffer = (byte *)_xp->tryAllocMem(bufSize);
+ if (!g_animRingBuffer) {
+ bufSize = 0xA000;
+ g_animRingBuffer = (byte *)_xp->allocMem(bufSize);
+ }
-void BoltEngine::initAV() {
-}
+ if (!g_animRingBuffer)
+ return false;
-void BoltEngine::cleanUpAV() {
-}
+ if (!playRTF(rtf, animIndex, g_animRingBuffer, bufSize))
+ return false;
-void BoltEngine::startAnimation() {
+ return true;
}
-void BoltEngine::maintainAudioPlay() {
-}
+void BoltEngine::cleanUpAnim() {
+ if (g_animRingBuffer) {
+ _xp->freeMem(g_animRingBuffer);
+ g_animRingBuffer = nullptr;
+ }
-void BoltEngine::initAnim() {
-}
+ if (g_animFileHandle) {
+ _xp->closeFile(g_animFileHandle);
+ g_animFileHandle = nullptr;
+ }
-void BoltEngine::cleanUpAnim() {
+ _xp->setInactivityTimer(g_animPrevInactivityTimer);
}
} // End of namespace Bolt
diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
new file mode 100644
index 00000000000..c5233c5b443
--- /dev/null
+++ b/engines/bolt/av.cpp
@@ -0,0 +1,330 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::setAVBufferSize(uint32 bufSize) {
+ g_avTargetBufSize = bufSize;
+}
+
+bool BoltEngine::prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+ if (!initAV(rtfHandle, animIndex, width, height, xOff, yOff)) {
+ cleanUpAV();
+ return false;
+ }
+
+ return true;
+}
+
+bool BoltEngine::maintainAV(int16 mode) {
+ RTFPacket *frameData;
+
+ if (!maintainRTF(mode, &frameData)) {
+ cleanUpAV();
+ return false;
+ }
+
+ if (frameData)
+ processPacket(frameData);
+
+ return true;
+}
+
+void BoltEngine::stopAV() {
+ killRTF(nullptr);
+ cleanUpAV();
+}
+
+bool BoltEngine::playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+ RTFPacket *frameData = nullptr;
+ uint32 eventBuf = 0;
+ int16 eventType = 0;
+
+ // Init if not already loaded...
+ if (!g_avRingBuffer) {
+ if (!initAV(rtfHandle, animIndex, width, height, xOff, yOff)) {
+ cleanUpAV();
+ return false;
+ }
+ }
+
+ // Flush pending mouse-down events...
+ while (_xp->getEvent(etMouseDown, &eventBuf) != etEmpty && !shouldQuit()) {
+ // getEvent already calls pollEvent and delayMillis
+ }
+
+ // Main playback loop...
+ while (!shouldQuit()) {
+ eventType = _xp->getEvent(etEmpty, &eventBuf);
+
+ if (eventType == etMouseDown) {
+ // Mouse click, abort playback...
+ killRTF(nullptr);
+ cleanUpAV();
+ return false;
+ }
+
+ if (eventType != etSound && eventType != etEmpty)
+ continue;
+
+ // Advance: mode=1 on sound event, mode=0 on idle
+ if (!maintainRTF(eventType == etSound ? 1 : 0, &frameData)) {
+ cleanUpAV();
+ return true; // finished normally
+ }
+
+ if (frameData)
+ processPacket(frameData);
+ }
+
+ // Not in the original, but it helps cleaning up
+ // if the user wants to quit during a video...
+ killRTF(nullptr);
+ cleanUpAV();
+ return false;
+}
+
+void BoltEngine::processPacket(RTFPacket *packet) {
+ uint32 tag = packet->tag;
+
+ // Adaptive frame rate control...
+ if (tag == MKTAG('R', 'L', '7', ' ') || tag == MKTAG('R', 'L', '7', 'F')) {
+ int16 frameRate = packet->frameRate;
+
+ // Skip this frame if: low framerate, only 1 behind, or haven't accumulated enough...
+ if (frameRate <= 10 || packet->skipCount > 1 || g_avFrameAccum < g_avSkipLevel) {
+ g_avFrameAccum += packet->duration;
+ warning("BoltEngine::processPacket() WANTS TO RETURN BUT IT WILL SKIP FRAMES");
+ return;
+ }
+
+ // Adjust skip level based on how far behind we are...
+ if (g_avFrameAccum > g_avSkipLevel)
+ g_avSkipLevel = (g_avFrameAccum > 4) ? 4 : g_avFrameAccum;
+
+ g_avFrameAccum = 0;
+
+ int16 targetSkip;
+ if (frameRate <= 12)
+ targetSkip = 4;
+ else if (frameRate <= 15)
+ targetSkip = 3;
+ else if (frameRate <= 20)
+ targetSkip = 2;
+ else
+ targetSkip = 1;
+
+ // If keeping up fine, stop skipping entirely...
+ if (g_avSkipLevel <= 1 && targetSkip == 1 && packet->skipCount == 0) {
+ g_avSkipLevel = 0;
+ } else {
+ // Ramp toward target skip level...
+ if (targetSkip > g_avSkipLevel)
+ g_avSkipLevel = targetSkip;
+ else if (targetSkip < g_avSkipLevel)
+ g_avSkipLevel--;
+ }
+ } else if (tag == MKTAG('r', 'l', '7', ' ') || tag == MKTAG('r', 'l', '7', 'f')) {
+ g_avFrameAccum = 0;
+ }
+
+ // Process frame, palette and trigger packets...
+ tag = packet->tag;
+
+ if (tag == MKTAG('R', 'L', '7', ' ') || tag == MKTAG('R', 'L', '7', 'B') || tag == MKTAG('R', 'L', '7', 'F') ||
+ tag == MKTAG('r', 'l', '7', ' ') || tag == MKTAG('r', 'l', '7', 'f')) {
+ processRL7(packet);
+ } else if (tag == MKTAG('P', 'L', 'T', 'B') || tag == MKTAG('P', 'L', 'T', 'E') || tag == MKTAG('P', 'L', 'T', 'F')) {
+ processPLTE(packet);
+ } else if (tag == MKTAG('T', 'R', 'G', 'R')) {
+ _xp->postEvent(etTrigger, 0);
+ }
+}
+
+void BoltEngine::processRL7(RTFPacket *packet) {
+ XPPicDesc *displayDesc;
+ int page;
+ bool transparency;
+ bool updateDisplay;
+
+ uint32 tag = packet->tag;
+
+ if (tag == MKTAG('R', 'L', '7', 'B')) {
+ displayDesc = &g_avBackBufDesc;
+ } else {
+ displayDesc = &g_avFrontBufDesc;
+ }
+
+ switch (tag) {
+ case MKTAG('R', 'L', '7', ' '):
+ case MKTAG('r', 'l', '7', ' '):
+ page = stFront;
+ transparency = false;
+ updateDisplay = true;
+ break;
+ case MKTAG('R', 'L', '7', 'F'):
+ case MKTAG('r', 'l', '7', 'f'):
+ page = stFront;
+ transparency = true;
+ updateDisplay = true;
+ break;
+ case MKTAG('R', 'L', '7', 'B'):
+ page = stBack;
+ transparency = true;
+ updateDisplay = false;
+ break;
+ default:
+ return;
+ }
+
+ _xp->setTransparency(transparency);
+
+ displayDesc->pixelData = packet->dataPtr;
+
+ _xp->displayPic(displayDesc, g_avDisplayX, g_avDisplayY, page);
+
+ if (updateDisplay)
+ _xp->updateDisplay();
+
+ g_avFrontBufDesc.palette = nullptr;
+ g_avFrontBufDesc.paletteStart = 0;
+ g_avFrontBufDesc.paletteCount = 0;
+
+ if (transparency) {
+ g_avBackBufDesc.palette = nullptr;
+ g_avBackBufDesc.paletteStart = 0;
+ g_avBackBufDesc.paletteCount = 0;
+ }
+}
+
+void BoltEngine::processPLTE(RTFPacket *packet) {
+ byte *srcData = packet->dataPtr;
+
+ int16 startIndex = READ_BE_INT16(srcData); srcData += 2;
+ int16 count = READ_BE_INT16(srcData); srcData += 2;
+
+ XPPicDesc *desc;
+ byte *palBuffer;
+
+ if (packet->tag == MKTAG('P', 'L', 'T', 'B')) {
+ desc = &g_avBackBufDesc;
+ palBuffer = g_avFrontPalette;
+ } else {
+ desc = &g_avFrontBufDesc;
+ palBuffer = g_avBackPalette;
+ }
+
+ desc->paletteStart = startIndex;
+ desc->paletteCount = count;
+ desc->palette = palBuffer;
+
+ // Copy RGB triplets into palette buffer...
+ while (count-- > 0) {
+ palBuffer[0] = *srcData++;
+ palBuffer[1] = *srcData++;
+ palBuffer[2] = *srcData++;
+ palBuffer += 3;
+ }
+}
+
+bool BoltEngine::initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+ uint32 savedAllocSize = g_avTargetBufSize;
+ uint32 allocSize = 0;
+
+ g_avTargetBufSize = 0xFA000; // ~1MB
+
+ g_avDisplayY = yOff;
+ g_avDisplayX = xOff;
+
+ g_avSavedInactivityTimer = _xp->setInactivityTimer(0);
+ g_avSavedScreenSaverTimer = _xp->setScreenSaverTimer(0);
+
+ g_avBackPalette = nullptr;
+ g_avFrontPalette = nullptr;
+ g_avFrameAccum = 0;
+
+ // Allocate two 384-byte palette buffers (128 RGB triplets each)
+ g_avBackPalette = (byte *)_xp->allocMem(128 * 3);
+ if (!g_avBackPalette)
+ return false;
+
+ g_avFrontPalette = (byte *)_xp->allocMem(128 * 3);
+ if (!g_avFrontPalette)
+ return false;
+
+ // Allocate ring buffer: try saved size, shrink by 50KB until 300KB minimum
+ allocSize = savedAllocSize;
+ do {
+ g_avRingBuffer = (byte *)_xp->tryAllocMem(allocSize);
+ if (g_avRingBuffer)
+ break;
+
+ allocSize -= 0xC800;
+ } while (allocSize >= 0x4B000);
+
+ if (!g_avRingBuffer) {
+ error("BoltEngine::initAV(): Not enough memory");
+ return false;
+ }
+
+ g_avBackBufDesc.width = width;
+ g_avFrontBufDesc.width = width;
+ g_avBackBufDesc.height = height;
+ g_avFrontBufDesc.height = height;
+ g_avBackBufDesc.flags = 1;
+ g_avFrontBufDesc.flags = 1;
+ g_avBackBufDesc.palette = nullptr;
+ g_avFrontBufDesc.palette = nullptr;
+ g_avBackBufDesc.paletteStart = 0;
+ g_avFrontBufDesc.paletteStart = 0;
+ g_avBackBufDesc.paletteCount = 0;
+ g_avFrontBufDesc.paletteCount = 0;
+
+ // Open and start streaming!
+ if (!playRTF(rtfHandle, animIndex, g_avRingBuffer, allocSize))
+ return false;
+
+ return true;
+}
+
+void BoltEngine::cleanUpAV() {
+ if (g_avRingBuffer) {
+ _xp->freeMem(g_avRingBuffer);
+ g_avRingBuffer = nullptr;
+ }
+
+ if (g_avFrontPalette) {
+ _xp->freeMem(g_avFrontPalette);
+ g_avFrontPalette = nullptr;
+ }
+
+ if (g_avBackPalette) {
+ _xp->freeMem(g_avBackPalette);
+ g_avBackPalette = nullptr;
+ }
+
+ _xp->setInactivityTimer(g_avSavedInactivityTimer);
+ _xp->setScreenSaverTimer(g_avSavedScreenSaverTimer);
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/barker.cpp b/engines/bolt/barker.cpp
new file mode 100644
index 00000000000..91f663a9979
--- /dev/null
+++ b/engines/bolt/barker.cpp
@@ -0,0 +1,100 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
+ bool success = false;
+
+ BarkerTable *table = new BarkerTable();
+ if (table) {
+ table->minIndex = minIndex;
+ table->maxIndex = maxIndex;
+ table->count = maxIndex - minIndex + 1;
+
+ table->handlers = new SideShowHandler[table->count];
+ if (!table->handlers) {
+ freeBarker(table);
+ } else {
+ success = true;
+ for (int i = 0; i < table->count; i++) {
+ table->handlers[i] = nullptr;
+ }
+ }
+ }
+
+ if (success) {
+ g_curErrorCode = 0;
+ return table;
+ } else {
+ g_curErrorCode = 1;
+ return nullptr;
+ }
+}
+
+void BoltEngine::freeBarker(BarkerTable *table) {
+ if (!table)
+ return;
+
+ if (table->handlers)
+ delete[] table->handlers;
+
+ delete table;
+}
+
+bool BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId) {
+ if (table->minIndex > boothId || table->maxIndex < boothId) {
+ g_curErrorCode = 5;
+ return false;
+ }
+
+ table->handlers[boothId - table->minIndex] = handler;
+
+ g_curErrorCode = 0;
+ return true;
+}
+
+int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
+ int16 prevBooth = 0;
+ int16 currentBooth = startBooth;
+
+ while (true) {
+ SideShowHandler handler = table->handlers[currentBooth - table->minIndex];
+
+ int16 nextBooth = (this->*handler)(prevBooth);
+
+ if (nextBooth == 0)
+ break;
+
+ prevBooth = currentBooth;
+ currentBooth = nextBooth;
+ }
+
+ return g_currentBoothId;
+}
+
+bool BoltEngine::checkError() {
+ return false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index e4683cdf938..4baa8fc5b72 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -41,6 +41,8 @@ BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engin
_gameDescription(gameDesc), _randomSource("Bolt") {
g_engine = this;
_xp = new XpLib(this);
+
+ initCallbacks();
}
BoltEngine::~BoltEngine() {
@@ -62,7 +64,7 @@ Common::Error BoltEngine::run() {
}
// Initialize paletted graphics mode
- if (g_extendedViewport) {
+ if (!g_extendedViewport) {
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
} else {
initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
@@ -71,41 +73,9 @@ Common::Error BoltEngine::run() {
// Set the engine's debugger console
setDebugger(new Console());
- //
- //// If a savegame was selected from the launcher, load it
- //int saveSlot = ConfMan.getInt("save_slot");
- //if (saveSlot != -1)
- // (void)loadGameState(saveSlot);
- //
- //// Draw a series of boxes on screen as a sample
- //for (int i = 0; i < 100; ++i)
- // _screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
- //_screen->update();
- //
- //// Simple event handling loop
- //byte pal[256 * 3] = { 0 };
- //Common::Event e;
- //int offset = 0;
- //
- //Graphics::FrameLimiter limiter(g_system, 60);
- //while (!shouldQuit()) {
- // while (g_system->getEventManager()->pollEvent(e)) {
- // }
- //
- // // Cycle through a simple palette
- // ++offset;
- // for (int i = 0; i < 256; ++i)
- // pal[i * 3 + 1] = (i + offset) % 256;
- // g_system->getPaletteManager()->setPalette(pal, 0, 256);
- // // Delay for a bit. All events loops should have a delay
- // // to prevent the system being unduly loaded
- // limiter.delayBeforeSwap();
- // _screen->update();
- // limiter.startFrame();
- //}
-
_xp->initialize();
boltMain();
+ _xp->terminate();
return Common::kNoError;
}
@@ -122,13 +92,11 @@ Common::Error BoltEngine::syncGame(Common::Serializer &s) {
}
void BoltEngine::boltMain() {
- void *testAlloc;
+ byte *testAlloc;
BarkerTable *barkerTable;
- void *boothSprite;
-
- // g_callbackPtr = DS:0xCB;
+ byte *boothSprite;
- testAlloc = _xp->allocMem(0x100000);
+ testAlloc = (byte *)_xp->allocMem(0x100000);
if (!testAlloc)
return;
@@ -136,90 +104,86 @@ void BoltEngine::boltMain() {
_xp->randomize();
- if (!allocResourceIndex())
- goto cleanup;
-
- g_boothsBoltLib = 0;
-
- if (!openBOLTLib(&g_boothsBoltLib, &g_boothsBoltIndex, AssetPath("booths.blt")))
- goto cleanup;
-
- if (!_xp->chooseDisplaySpec(&g_displayMode, 2, g_displaySpecs))
- goto cleanup;
-
- g_displayWidth = g_displaySpecs[g_displayMode].width;
- g_displayHeight = g_displaySpecs[g_displayMode].height;
-
- // Center within 384x240 virtual coordinate space...
- g_displayX = (384 - g_displayWidth) / 2;
- g_displayY = (240 - g_displayHeight) / 2;
-
- _xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
-
- if (g_displayMode != 0)
- g_rtfHandle = openRTF(AssetPath("booths.pal"));
- else
- g_rtfHandle = openRTF(AssetPath("booths4.pal"));
-
- if (g_rtfHandle == nullptr)
- goto cleanup;
-
- playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
-
- boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
-
- _xp->setTransparency(false);
-
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 0, 0);
- displayPic(boothSprite, g_displayX, g_displayY, 0);
-
- _xp->updateDisplay();
-
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 1, 0);
- displayPic(boothSprite, g_displayX, g_displayY, 1);
-
- playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
-
- freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
-
- if (!getBOLTGroup(g_boothsBoltLib, 0, 1))
- goto cleanup;
-
- setCursorPict(memberAddr(g_boothsBoltLib, 0));
-
- if (!initVRam(1500))
- goto cleanup;
-
- barkerTable = createBarker(17, 2);
- if (barkerTable == nullptr)
- goto cleanup;
-
- // Register booth handlers...
- registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
- registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
- registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
- registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
- registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
- registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
- registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
- registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
- registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
- registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
- registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
- registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
- registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
- registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
-
- g_lettersWon = 0;
- _xp->setScreenSaverTimer(1800);
-
- // The barker function runs the main loop, starting at mainEntrance()...
- if (!checkError())
- barker(barkerTable, 9);
-
- freeBarker(barkerTable);
+ if (allocResourceIndex()) {
+ g_boothsBoltLib = nullptr;
+
+ if (openBOLTLib(&g_boothsBoltLib, &g_resourceDefaultCallbacks, assetPath("booths.blt"))) {
+ int16 chosenSpecId = g_extendedViewport ? 0 : 1;
+
+ if (_xp->setDisplaySpec(&g_displayMode, &g_displaySpecs[chosenSpecId])) {
+ assert(g_displayMode >= 0);
+ g_displayWidth = g_displaySpecs[g_displayMode].width;
+ g_displayHeight = g_displaySpecs[g_displayMode].height;
+
+ // Center within 384x240 virtual coordinate space...
+ g_displayX = (EXTENDED_SCREEN_WIDTH - g_displayWidth) / 2;
+ g_displayY = (EXTENDED_SCREEN_HEIGHT - g_displayHeight) / 2;
+
+ _xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
+
+ if (g_displayMode == 0) {
+ g_rtfHandle = openRTF(assetPath("cc_og.av"));
+ } else {
+ g_rtfHandle = openRTF(assetPath("cc_cr.av"));
+ }
+
+ if (g_rtfHandle) {
+ playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+ boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
+
+ _xp->setTransparency(false);
+
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stFront, 0);
+ displayPic(boothSprite, g_displayX, g_displayY, stFront);
+
+ _xp->updateDisplay();
+
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stBack, 0);
+ displayPic(boothSprite, g_displayX, g_displayY, stBack);
+
+ playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+ freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
+
+ if (getBOLTGroup(g_boothsBoltLib, 0, 1)) {
+ setCursorPict(memberAddr(g_boothsBoltLib, 0));
+
+ if (initVRam(1500)) {
+ barkerTable = createBarker(2, 17);
+ if (barkerTable) {
+ // Register booth handlers...
+ registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
+ registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
+ registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
+ registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
+ registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
+ registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
+ registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
+ registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
+ registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
+ registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
+ registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
+ registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
+ registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
+ registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
+
+ g_lettersWon = 0;
+ _xp->setScreenSaverTimer(1800);
+
+ // The barker function runs the main loop, starting at mainEntrance()...
+ if (!checkError())
+ barker(barkerTable, 9);
+
+ freeBarker(barkerTable);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
-cleanup:
freeBOLTGroup(g_boothsBoltLib, 0, 1);
if (g_boothsBoltLib != 0)
@@ -232,21 +196,23 @@ cleanup:
freeResourceIndex();
}
-BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
- return nullptr;
-}
-
-void BoltEngine::freeBarker(BarkerTable *table) {
-}
+void BoltEngine::setCursorPict(byte *sprite) {
+ byte cursorBitmap[32]; // 16x16
+ byte *srcPtr = getResolvedPtr(sprite, 0x12);
-void BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId) {
-}
+ // Convert 8bpp pixel data to 1bpp monochrome bitmap
+ for (int i = 0; i < 32; i++) {
+ cursorBitmap[i] = 0x00;
+ int16 mask = 0x80;
+ while (mask != 0x00) {
+ if (*srcPtr++ != 0x00)
+ cursorBitmap[i] |= (byte)mask;
-void BoltEngine::barker(BarkerTable *table, int16 startBooth) {
-}
+ mask >>= 1;
+ }
+ }
-bool BoltEngine::checkError() {
- return false;
+ _xp->setCursorImage(cursorBitmap, 7, 7);
}
} // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 4042d3f725b..c24dcb6fd5c 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -47,26 +47,185 @@ namespace Bolt {
struct BoltGameDescription;
class XpLib;
-struct DisplaySpecs {
+typedef struct DisplaySpecs {
+ int16 id;
int16 width;
int16 height;
-};
+} DisplaySpecs;
+
+typedef int16 (BoltEngine::*SideShowHandler)(int16 prevBooth);
struct BarkerTable {
-
+ SideShowHandler *handlers;
+ int16 count;
+ int16 minIndex;
+ int16 maxIndex;
};
-struct BOLTLib {
- int16 refCount;
- int16 groupCount;
- int32 fileHandle;
- int32 *funcTable1;
- int32 *funcTable2;
- int32 *dwordPtr1;
- int32 *dwordPtr2;
- int32 *dwordPtr3;
- int32 *dwordPtr4;
-};
+typedef struct BOLTHeader {
+ byte header[11];
+ byte groupCount;
+ uint32 groupDirOffset;
+
+ BOLTHeader() {
+ memset(header, 0, sizeof(header));
+ groupCount = 0;
+ groupDirOffset = 0;
+ }
+} BOLTHeader;
+
+typedef void (*BOLTCallback)(void);
+
+typedef struct BOLTCallbacks {
+ BOLTCallback *typeLoadCallbacks; // +0x00: type-based post-load fixup (25 entries)
+ BOLTCallback *typeFreeCallbacks; // +0x04: type-based post-free cleanup (25 entries)
+ BOLTCallback *memberLoadCallbacks; // +0x08: per-member pre-load callbacks
+ BOLTCallback *memberFreeCallbacks; // +0x0C: per-member pre-free callbacks
+ BOLTCallback *groupLoadCallbacks; // +0x10: per-group load callbacks
+ BOLTCallback *groupFreeCallbacks; // +0x14: per-group free callbacks
+
+ BOLTCallbacks() {
+ typeLoadCallbacks = nullptr;
+ typeFreeCallbacks = nullptr;
+ memberLoadCallbacks = nullptr;
+ memberFreeCallbacks = nullptr;
+ groupLoadCallbacks = nullptr;
+ groupFreeCallbacks = nullptr;
+ }
+} BOLTCallbacks;
+
+typedef struct BOLTMemberEntry {
+ byte flags; // +0x00: bit 3 = uncompressed
+ byte preLoadCbIndex; // +0x01: index into lib->userData1 callback table
+ byte preFreeCbIndex; // +0x02: index into lib->userData2 callback table
+ byte typeCbIndex; // +0x03: index into lib->loadCallbacks (post-load)
+ // and lib->freeCallbacks (post-free)
+ uint32 decompSize; // +0x04: decompressed data size (big-endian in file)
+ uint32 fileOffset; // +0x08: file offset of compressed/raw data (big-endian in file)
+ uint32 dataPtrPlaceholder;
+ byte *dataPtr; // +0x0C: pointer to loaded data (NULL if not loaded)
+
+ BOLTMemberEntry() {
+ flags = 0;
+ preLoadCbIndex = 0;
+ preFreeCbIndex = 0;
+ typeCbIndex = 0;
+ decompSize = 0;
+ fileOffset = 0;
+ dataPtrPlaceholder = 0;
+ dataPtr = nullptr;
+ }
+} BOLTMemberEntry; // 0x10 = 16 bytes
+
+typedef struct BOLTGroupEntry {
+ byte flags; // +0x00: unknown flags
+ byte loadCbIndex; // +0x01: group load callback index (into lib+0x18)
+ byte freeCbIndex; // +0x02: group free callback index (into lib+0x1C)
+ byte memberCount; // +0x03: number of members (0 = 256)
+ uint32 memberDirOffset; // +0x04: file offset to member directory (big-endian)
+ uint32 memberDataOffset; // +0x08: file offset to member data (big-endian)
+ uint32 groupDataPtrPlaceholder; // +0x0C: pointer to loaded group data block (NULL if not loaded)
+ byte *memberData;
+ BOLTMemberEntry *members;
+
+ BOLTGroupEntry() {
+ flags = 0;
+ loadCbIndex = 0;
+ freeCbIndex = 0;
+ memberCount = 0;
+ memberDirOffset = 0;
+ memberDataOffset = 0;
+ groupDataPtrPlaceholder = 0;
+ memberData = nullptr;
+ members = nullptr;
+ }
+
+ ~BOLTGroupEntry() {
+ free(memberData);
+ memberData = nullptr;
+
+ delete[] members;
+ members = nullptr;
+ }
+
+ void initMembers(int numMembers) {
+ int actualNumber = numMembers == 0 ? 256 : numMembers;
+ members = new BOLTMemberEntry[actualNumber];
+ }
+
+} BOLTGroupEntry; // 0x10 = 16 bytes
+
+typedef struct BOLTLib {
+ int16 refCount; // +0x00
+ int16 groupCount; // +0x02 (0 = 256)
+ Common::File *fileHandle; // +0x04
+ BOLTCallbacks callbacks;
+ BOLTGroupEntry *groups; // +0x20
+
+ BOLTLib(int inGroupCount) {
+ refCount = 0;
+ groupCount = 0;
+ fileHandle = nullptr;
+ groups = new BOLTGroupEntry[inGroupCount];
+ }
+
+ ~BOLTLib() {
+ delete[] groups;
+ }
+} BOLTLib;
+
+typedef struct RTFResource {
+ Common::File *fileHandle;
+ uint32 *indexTablePtr;
+ int16 entryCount;
+ byte *indexTableRawData;
+ uint32 *indexTable;
+
+ RTFResource() {
+ fileHandle = nullptr;
+ indexTablePtr = nullptr;
+ entryCount = 0;
+ indexTableRawData = nullptr;
+ indexTable = nullptr;
+ }
+
+ ~RTFResource() {
+ if (indexTable)
+ delete[] indexTable;
+ }
+
+ void reserveTableSize(int numElements) {
+ indexTable = new uint32[numElements];
+ }
+} RTFResource;
+
+struct RTFPacket;
+typedef struct RTFPacket {
+ uint32 tag; // +0x00: chunk tag (e.g. MKTAG('A','2','2','8'))
+ uint32 allocSize; // +0x04: total allocated size (including header)
+ uint32 dataSize; // +0x08: raw data size
+ byte *dataPtr; // +0x0C: pointer to data (= this + 0x1C)
+ int16 skipCount; // +0x10: how late this frame is
+ int16 frameRate; // +0x12: frame rate reference (or 50 if no sound)
+ int16 duration; // +0x14: sound duration in hundredths of a second
+ int16 timestamp; // +0x16: cumulative timeline position
+ RTFPacket *next; // +0x18: next packet in linked list
+ // +0x1C: data begins here
+ byte *ringBufPtr;
+
+ RTFPacket() {
+ tag = 0;
+ allocSize = 0;
+ dataSize = 0;
+ dataPtr = nullptr;
+ skipCount = 0;
+ frameRate = 0;
+ duration = 0;
+ timestamp = 0;
+ next = nullptr;
+ ringBufPtr = nullptr;
+ }
+} RTFPacket; // 0x1C header = 28 bytes
class BoltEngine : public Engine {
friend class XpLib;
@@ -127,151 +286,303 @@ public:
protected:
DisplaySpecs g_displaySpecs[2] = {
- {384, 240},
- {320, 200}
+ {0, 384, 240},
+ {1, 320, 200}
};
- XpLib *_xp;
+ XpLib *_xp = nullptr;
bool g_extendedViewport = false;
- // Entry point
+ // xpMain
void boltMain();
-
- // Booths logic
- typedef void (BoltEngine::*SideShowHandler)(void);
-
- int g_lettersWon = 0;
-
- void displayBooth();
- void playAVOverBooth();
- void hucksBooth();
- void fredsBooth();
- void scoobysBooth();
- void yogisBooth();
- void georgesBooth();
- void topCatsBooth();
- void mainEntrance();
- void huckGame();
- void fredGame();
- void scoobyGame();
- void yogiGame();
- void georgeGame();
- void topCatGame();
- void winALetter();
-
- void loadBooth();
+ void setCursorPict(byte *sprite);
+
+ // Booth
+ void startCycle(byte *cycleResource);
+ void displayBooth(int16 page);
+ void playAVOverBooth(int16 animIndex);
+ int16 hucksBooth(int16 prevBooth);
+ int16 fredsBooth(int16 prevBooth);
+ int16 scoobysBooth(int16 prevBooth);
+ int16 yogisBooth(int16 prevBooth);
+ int16 georgesBooth(int16 prevBooth);
+ int16 topCatsBooth(int16 prevBooth);
+ int16 mainEntrance(int16 prevBooth);
+ bool loadBooth(int16 boothId);
void unloadBooth();
- void openBooth();
+ int16 openBooth(int16 sceneId);
void closeBooth();
void playTour();
- void finishPlayingHelp();
- void hotSpotActive();
+ void finishPlayingHelp(int16 activeHotspot);
+ int16 hotSpotActive(int16 hotspot);
void hoverHotSpot();
- void boothEventLoop();
+ int16 boothEventLoop();
void resetInactivityState();
- void handleButtonPress();
+ bool handleButtonPress(int16 hotspot);
+ void blastColors(byte **paletteTable, int16 index, int16 mode);
+ void setColors(int16 index);
+ void restoreColors(int16 index);
+ void loadColors();
+ void shiftColorMap(byte *colorMap, int16 delta);
void playBoothAV();
void mapIdleAnimation();
void boothIdleAnimation();
void screensaverStep();
+ void fadeToBlack(int16 steps);
void flushInput();
-
- BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
- void freeBarker(BarkerTable *table);
- void registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId);
- void barker(BarkerTable *table, int16 startBooth);
- bool checkError();
+ int16 winALetter(int16 prevBooth);
- // Graphics
+ int16 g_lettersWon = 0;
+ bool g_allLettersWonFlag = false;
int g_displayMode = 0;
int32 g_displayX = 0;
int32 g_displayY = 0;
int32 g_displayWidth = 0;
int32 g_displayHeight = 0;
- void setCursorPict(void *sprite);
- void startCycle(void *cycleResource);
- void blastColors();
- void setColors();
- void restoreColors();
- void loadColors();
- void shiftColorMap();
- void fadeToBlack();
- void displayColors(void *palette, int16 page, int16 flags);
- void boltPict2Pict(void *dest, void *boltSprite);
- void displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags);
- void boltCycleToXPCycle();
- void unpackColors();
- void inRect();
- void rectOverlap();
-
- // Resource handling
- BOLTLib *g_boothsBoltLib = nullptr;
- int g_boothsBoltIndex = 0;
- int16 g_resourceIndexCount = 1000;
- uint32 *g_resourceIndex = nullptr;
-
- #define AssetPath(x) x
+ int16 g_boothLoadedMask = 0;
+ int16 g_currentBoothScene = 0;
+ int16 g_boothNumHotspots = 0;
+ int16 g_boothNumAnimations = 0;
+ byte *g_boothSceneDesc = nullptr;
+ byte *g_boothHotPalDescs[4];
+ Common::Rect g_boothHotspotDescs[8];
+ byte *g_boothAnimDescs[7];
+ byte *g_boothAnimPalDescs[4];
+ byte g_savedPalettes[7][30];
+ byte g_savedHotPalettes[4][9];
+ byte *g_boothPalCycleData = nullptr;
+ XPPicDesc g_boothLetterSprite;
+ bool g_needInitCursorPos = true;
+ byte *g_boothExitLeft = nullptr;
+ byte *g_boothExitRight = nullptr;
+ byte g_leftDoorNavTable[3] = {3, 2, 4};
+ byte g_rightDoorNavTable[3] = {1, 0, 5};
+
+ int16 g_cursorX = 0;
+ int16 g_cursorY = 0;
+ int16 g_hoveredHotspot = 0;
+ uint32 g_keyTimer = 0;
+ int16 g_keyReleased = 0;
+ int16 g_keyLeft = 0;
+ int16 g_keyRight = 0;
+ int16 g_keyUp = 0;
+ int16 g_keyDown = 0;
+ int16 g_keyHelp = 0;
+ int16 g_keyEnter = 0;
+
+ int16 g_screensaverStep = 0;
+ int16 g_helpPlaying = 0;
+ int16 g_helpIsIdle = 0;
+ int16 g_idleHelpAvailable = 0;
+
+ // Barker
+ BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
+ void freeBarker(BarkerTable *table);
+ bool registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId);
+ int16 barker(BarkerTable *table, int16 startBooth);
+ bool checkError();
- bool openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName);
- void closeBOLTLib(BOLTLib **lib);
+ int32 g_curErrorCode = 0;
+ int16 g_currentBoothId = 0;
+
+ // Utils
+ void displayColors(byte *palette, int16 page, int16 flags);
+ void sub_11035();
+ void boltPict2Pict(XPPicDesc *dest, byte *boltSprite);
+ void displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page);
+ void sub_11131();
+ const char *assetPath(const char *fileName);
+ void boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc);
+ void unpackColors(int16 count, byte *packedColors);
+
+ // Swap
+ void swapPicHeader();
+ void swapAndResolvePicDesc();
+ void swapFirstWord();
+ void swapFirstTwoWords();
+ void swapFirstFourWords();
+ void swapSpriteHeader();
+ void freeSpriteCleanUp();
+
+ // Resource
+ bool libRead(Common::File *fileHandle, uint32 offset, byte *dest, uint32 size);
+ void decompress(byte *dest, uint32 decompSize, byte *src);
+ void resolveIt(uint32 *ref);
+ void resolvePendingFixups();
+ void resolveFunction(uint32 *ref);
+ void resolveAllRefs();
+ byte *getResolvedPtr(byte *data, int offset);
+ bool openBOLTLib(BOLTLib **outLib, BOLTCallbacks *outIdx, const char *fileName);
+ bool closeBOLTLib(BOLTLib **lib);
bool attemptFreeIndex(BOLTLib *lib, int16 groupId);
- void loadGroupDirectory();
+ bool loadGroupDirectory();
bool getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
void freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
- void *getBOLTMember(BOLTLib *lib, int16 resId);
+ byte *getBOLTMember(BOLTLib *lib, int16 resId);
bool freeBOLTMember(BOLTLib *lib, int16 resId);
-
- void *memberAddr(BOLTLib *lib, int16 resId);
- void *memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset);
+ Common::Rect memberToRect(byte *data);
+ byte *memberAddr(BOLTLib *lib, int16 resId);
+ byte *memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset);
uint32 memberSize(BOLTLib *lib, int16 resId);
- void *groupAddr(BOLTLib *lib, int16 groupId);
-
+ byte *groupAddr(BOLTLib *lib, int16 groupId);
bool allocResourceIndex();
void freeResourceIndex();
+ void swapAllWords();
+ void swapAllLongs();
+
+ BOLTLib *g_boothsBoltLib = nullptr;
+ BOLTCallbacks g_resourceDefaultCallbacks;
+
+ static BOLTCallback g_defaultTypeLoadCallbacks[25];
+ static BOLTCallback g_defaultTypeFreeCallbacks[25];
+ static BOLTCallback g_defaultMemberLoadCallbacks[25];
+ static BOLTCallback g_defaultMemberFreeCallbacks[25];
+ static BOLTCallback g_defaultGroupLoadCallbacks[25];
+ static BOLTCallback g_defaultGroupFreeCallbacks[25];
+ static void noOpCb();
+
+ static void swapAllWordsCb();
+ static void swapPicHeaderCb();
+ static void swapAndResolvePicDescCb();
+ static void swapFirstWordCb();
+ static void swapFirstTwoWordsCb();
+ static void swapFirstFourWordsCb();
+ static void swapSpriteHeaderCb();
+ static void freeSpriteCleanUpCb();
+
+ void initCallbacks();
+
+ int16 g_resourceIndexCount = 1000;
+ uint32 **g_resourceIndex = nullptr;
+ Common::Array<byte *> g_resolvedPtrs;
+ Common::File *g_cachedFileHandle = nullptr;
+ uint32 g_cachedFilePos = 0xFFFFFFFF;
+ BOLTLib *g_boltCurrentLib = nullptr;
+ BOLTHeader g_boltFileHeader;
+ BOLTGroupEntry *g_boltCurrentGroupEntry = nullptr;
+ int16 g_boltLoadDepth = 0;
+ byte *g_boltRawMemberData = nullptr;
+ BOLTMemberEntry *g_boltCurrentMemberEntry = nullptr;
+ int16 g_pendingFixupCount = 0;
+
+ // Game state
bool initVRam(int16 poolSize);
void freeVRam();
bool vLoad(void *dest, const char *name);
bool vSave(void *src, uint16 srcSize, const char *name);
bool vDelete(const char *name);
- void memMove(void *dest, void *src, uint16 count);
- void *dataAddress(int16 recordOffset);
+ byte *dataAddress(int16 recordOffset);
uint16 dataSize(int16 recordOffset);
bool findRecord(const char *name, int16 *outOffset);
- // Videos
- void *g_rtfHandle = nullptr;
+ int32 g_vramRecordCount = 0;
+ int32 g_vramUsedBytes = 0;
+ byte *g_allocatedMemPool = nullptr;
+ uint32 g_allocatedMemPoolSize = 0;
- void *openRTF(const char *fileName);
- void closeRTF(void *rtf);
- bool playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize);
+ // RTF
+ RTFResource *openRTF(const char *fileName);
+ void closeRTF(RTFResource *rtf);
+ bool playRTF(RTFResource *rtfFile, int16 animIndex, byte *ringBuffer, int32 bufferSize);
bool fillRTFBuffer();
void flushRTFSoundQueue();
- bool maintainRTF(int16 mode, void *outFrameData);
+ bool maintainRTF(int16 mode, RTFPacket **outFrameData);
bool isRTFPlaying();
- void killRTF();
- void readPacket();
- void preProcessPacket();
- void queuePacket();
- void deQueuePacket();
- void allocPacket();
- void freePacket();
+ bool killRTF(uint32 *outFilePos);
+ bool readPacket();
+ void preProcessPacket(RTFPacket *packet);
+ void queuePacket(RTFPacket *packet);
+ RTFPacket *deQueuePacket();
+ RTFPacket *allocPacket(uint32 dataSize);
+ void freePacket(RTFPacket *packet);
void resetPlaybackState();
- void sub_12980();
- void prepareAV();
- void maintainAV();
+ void setAVBufferSize(uint32 bufSize);
+
+ RTFResource *g_rtfHandle = nullptr;
+ Common::File *g_rtfFileHandle = nullptr;
+ uint32 g_rtfChunkRemaining = 0;
+ bool g_rtfMidChunk = false;
+ RTFPacket *g_rtfCurrentPacket = nullptr;
+ byte *g_ringBufBase = nullptr;
+ byte *g_ringBufWritePtr = nullptr;
+ uint32 g_ringBufSize = 0;
+ uint32 g_ringBufFreeSpace = 0;
+ uint32 g_ringBufLowWater = 0;
+ uint32 g_ringBufHighWater = 0;
+ uint32 g_ringBufUsed = 0;
+ bool g_rtfSoundActive = false;
+ int16 g_rtfPlaybackTime = 0;
+ int16 g_rtfCumulativeTime = 0;
+ RTFPacket *g_rtfPendingFrame = nullptr;
+ RTFPacket *g_rtfSoundQueueHead = nullptr;
+ RTFPacket *g_rtfSoundPlayHead = nullptr;
+ RTFPacket *g_rtfChunkListTail = nullptr;
+ RTFPacket *g_rtfChunkListHead = nullptr;
+ int16 g_rtfChunkCount = 0;
+ int16 g_rtfQueuedSoundCount = 0;
+ int16 g_rtfSoundTiming = 0;
+ uint32 g_rtfAnimStartOffset = 0;
+ bool g_rtfNeedInitialFill = false;
+ uint32 g_rtfChunkTag = 0;
+ uint32 g_rtfChunkSize = 0;
+
+ // AV
+ bool prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
+ bool maintainAV(int16 mode);
void stopAV();
- bool playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
- void processPacket();
- void processRL7();
- void processPLTE();
- void initAV();
+ bool playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
+ void processPacket(RTFPacket *packet);
+ void processRL7(RTFPacket *packet);
+ void processPLTE(RTFPacket *packet);
+ bool initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
void cleanUpAV();
- void startAnimation();
- void maintainAudioPlay();
- void initAnim();
+
+ byte *g_avRingBuffer = nullptr;
+ uint32 g_avTargetBufSize = 0x0FA000;
+ int16 g_avSkipLevel = 0;
+ byte *g_avFrontPalette = nullptr;
+ byte *g_avBackPalette = nullptr;
+ uint32 g_avSavedInactivityTimer = 0;
+ uint32 g_avSavedScreenSaverTimer = 0;
+ int16 g_avFrameAccum = 0;
+ int16 g_avDisplayX = 0;
+ int16 g_avDisplayY = 0;
+
+ XPPicDesc g_avFrontBufDesc;
+ XPPicDesc g_avBackBufDesc;
+
+ // Anim
+ bool startAnimation(RTFResource *rtf, int16 animIndex);
+ void stopAnimation();
+ bool maintainAudioPlay(int16 mode);
+ bool initAnim(RTFResource *rtf, int16 animIndex);
void cleanUpAnim();
+
+ int16 g_animPrevInactivityTimer = 0;
+ byte *g_animRingBuffer = nullptr;
+ Common::File *g_animFileHandle = nullptr;
+
+ // --- MINIGAMES ---
+
+ // Fred
+ int16 fredGame(int16 prevBooth) { return 0; }
+
+ // George
+ int16 georgeGame(int16 prevBooth) { return 0; }
+
+ // Huck
+ int16 huckGame(int16 prevBooth) { return 0; }
+
+ // Scooby
+ int16 scoobyGame(int16 prevBooth) { return 0; }
+
+ // TopCat
+ int16 topCatGame(int16 prevBooth) { return 0; }
+
+ // Yogi
+ int16 yogiGame(int16 prevBooth) { return 0; }
};
extern BoltEngine *g_engine;
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index 2af00442565..acfe6f2457c 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -23,100 +23,1222 @@
namespace Bolt {
-void BoltEngine::displayBooth() {
-}
+void BoltEngine::startCycle(byte *cycleResource) {
+ if (!cycleResource)
+ return;
-void BoltEngine::playAVOverBooth() {
+ XPCycleState specs[4];
+ boltCycleToXPCycle(cycleResource, specs);
+ _xp->startCycle(specs);
}
-void BoltEngine::hucksBooth() {
-}
+void BoltEngine::displayBooth(int16 page) {
+ _xp->setTransparency(page);
+ _xp->displayPic(&g_boothLetterSprite, g_displayX, g_displayY, page);
+ displayColors(g_boothPalCycleData, 0, page);
-void BoltEngine::fredsBooth() {
-}
+ if (page != stFront)
+ _xp->fillDisplay(0, stFront);
-void BoltEngine::scoobysBooth() {
+ _xp->updateDisplay();
}
-void BoltEngine::yogisBooth() {
+void BoltEngine::playAVOverBooth(int16 animIndex) {
+ _xp->hideCursor();
+ displayBooth(stBack);
+ playAV(g_rtfHandle, animIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ displayBooth(stFront);
+ _xp->showCursor();
}
-void BoltEngine::georgesBooth() {
-}
+int16 BoltEngine::hucksBooth(int16 prevBooth) {
+ int16 result = openBooth(3);
+ closeBooth();
-void BoltEngine::topCatsBooth() {
+ switch (result) {
+ case -1:
+ return 0; // exit
+ case 0:
+ return 9; // main entrance
+ case 1:
+ return 4; // right neighbor (Fred)
+ case 2:
+ return 10; // default (play game)
+ case 3:
+ return 10; // play game
+ default:
+ return 10;
+ }
}
-void BoltEngine::mainEntrance() {
-}
+int16 BoltEngine::fredsBooth(int16 prevBooth) {
+ int16 result = openBooth(4);
+ closeBooth();
-void BoltEngine::huckGame() {
+ switch (result) {
+ case -1:
+ return 0;
+ case 0:
+ return 3; // left neighbor (Hucks)
+ case 1:
+ return 5; // right neighbor (Scooby)
+ case 2:
+ return 11;
+ case 3:
+ return 11;
+ default:
+ return 11;
+ }
}
-void BoltEngine::fredGame() {
-}
+int16 BoltEngine::scoobysBooth(int16 prevBooth) {
+ int16 result = openBooth(5);
+ closeBooth();
-void BoltEngine::scoobyGame() {
+ switch (result) {
+ case -1:
+ return 0;
+ case 0:
+ return 4; // left (Fred)
+ case 1:
+ return 6; // right (Yogi)
+ case 2:
+ return 12;
+ case 3:
+ return 12;
+ default:
+ return 12;
+ }
}
-void BoltEngine::yogiGame() {
+int16 BoltEngine::yogisBooth(int16 prevBooth) {
+ int16 result = openBooth(6);
+ closeBooth();
+
+ switch (result) {
+ case -1:
+ return 0;
+ case 0:
+ return 5; // left (Scooby)
+ case 1:
+ return 7; // right (George)
+ case 2:
+ return 13;
+ case 3:
+ return 13;
+ default:
+ return 13;
+ }
}
-void BoltEngine::georgeGame() {
+int16 BoltEngine::georgesBooth(int16 prevBooth) {
+ int16 result = openBooth(7);
+ closeBooth();
+
+ switch (result) {
+ case -1:
+ return 0;
+ case 0:
+ return 6; // left (Yogi)
+ case 1:
+ return 8; // right (TopCat)
+ case 2:
+ return 14;
+ case 3:
+ return 14;
+ default:
+ return 14;
+ }
}
-void BoltEngine::topCatGame() {
+int16 BoltEngine::topCatsBooth(int16 prevBooth) {
+ int16 result = openBooth(8);
+ closeBooth();
+
+ switch (result) {
+ case -1:
+ return 0;
+ case 0:
+ return 7; // left (George)
+ case 1:
+ return 9; // right (main entrance)
+ case 2:
+ return 15;
+ case 3:
+ return 15;
+ default:
+ return 15;
+ }
}
-void BoltEngine::winALetter() {
+int16 BoltEngine::mainEntrance(int16 prevBooth) {
+ int16 result = openBooth(9);
+ closeBooth();
+
+ switch (result) {
+ case -1:
+ return 0; // exit
+ case 0:
+ return 8; // left (TopCat)
+ case 1:
+ return 3; // right (Hucks)
+ case 2:
+ return 9; // self (stay)
+ case 3:
+ return 9; // self
+ case 4:
+ return 9; // self
+ case 5:
+ return 0; // exit
+ default:
+ return 9;
+ }
}
-void BoltEngine::loadBooth() {
+bool BoltEngine::loadBooth(int16 boothId) {
+ switch (boothId) {
+ case 3:
+ if (g_boothLoadedMask & 0x01)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x100, 1))
+ return false;
+ g_boothLoadedMask |= 0x01;
+ break;
+
+ case 4:
+ if (g_boothLoadedMask & 0x02)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x200, 1))
+ return false;
+ g_boothLoadedMask |= 0x02;
+ break;
+
+ case 5:
+ if (g_boothLoadedMask & 0x04)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x300, 1))
+ return false;
+ g_boothLoadedMask |= 0x04;
+ break;
+
+ case 6:
+ if (g_boothLoadedMask & 0x08)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x400, 1))
+ return false;
+ g_boothLoadedMask |= 0x08;
+ break;
+
+ case 7:
+ if (g_boothLoadedMask & 0x10)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x500, 1))
+ return false;
+ g_boothLoadedMask |= 0x10;
+ break;
+
+ case 8:
+ if (g_boothLoadedMask & 0x20)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x600, 1))
+ return false;
+ g_boothLoadedMask |= 0x20;
+ break;
+
+ case 9:
+ if (g_boothLoadedMask & 0x40)
+ break;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x700, 1))
+ return false;
+ if (!getBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1))
+ return false;
+ g_boothLoadedMask |= 0x40;
+ break;
+ }
+
+ return true;
}
void BoltEngine::unloadBooth() {
+ if (g_boothLoadedMask & 0x01)
+ freeBOLTGroup(g_boothsBoltLib, 0x100, 1);
+
+ if (g_boothLoadedMask & 0x02)
+ freeBOLTGroup(g_boothsBoltLib, 0x200, 1);
+
+ if (g_boothLoadedMask & 0x04)
+ freeBOLTGroup(g_boothsBoltLib, 0x300, 1);
+
+ if (g_boothLoadedMask & 0x08)
+ freeBOLTGroup(g_boothsBoltLib, 0x400, 1);
+
+ if (g_boothLoadedMask & 0x10)
+ freeBOLTGroup(g_boothsBoltLib, 0x500, 1);
+
+ if (g_boothLoadedMask & 0x20)
+ freeBOLTGroup(g_boothsBoltLib, 0x600, 1);
+
+ if (g_boothLoadedMask & 0x40) {
+ freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
+ freeBOLTGroup(g_boothsBoltLib, 0x700, 1);
+ }
+
+ g_boothLoadedMask = 0;
}
-void BoltEngine::openBooth() {
+int16 BoltEngine::openBooth(int16 sceneId) {
+ int16 baseResId;
+ int16 resId;
+
+ g_currentBoothScene = sceneId;
+
+ if (!loadBooth(sceneId))
+ return -1;
+
+ baseResId = (sceneId - 3) << 8;
+
+ // Main entrance has more hotspots/animations than regular booths
+ if (sceneId == 9) {
+ g_boothNumHotspots = 8;
+ g_boothNumAnimations = 7;
+ } else {
+ g_boothNumHotspots = 5;
+ g_boothNumAnimations = 4;
+ }
+
+ // Background scene descriptor
+ g_boothSceneDesc = memberAddr(g_boothsBoltLib, baseResId + 0x100);
+
+ // Load 4 palette descriptors (at DS:0x13AC, members +0x105..+0x108)
+ resId = baseResId + 0x105;
+ for (int16 i = 0; i < 4; i++) {
+ g_boothHotPalDescs[i] = memberAddr(g_boothsBoltLib, resId);
+ resId++;
+ }
+
+ // Load animation descriptors
+ if (sceneId == 9) {
+ for (int16 i = 0; i < g_boothNumAnimations; i++)
+ g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, 0x709 + i);
+ } else {
+ resId = baseResId + 0x109;
+ for (int16 i = 0; i < g_boothNumAnimations; i++) {
+ g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, resId);
+ resId++;
+ }
+ }
+
+ // Load hotspot descriptors
+ if (sceneId == 9) {
+ for (int16 i = 0; i < g_boothNumHotspots; i++)
+ g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, 0x710 + i));
+ } else {
+ resId = baseResId + 0x10D;
+ for (int16 i = 0; i < g_boothNumHotspots; i++) {
+ g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, resId));
+ resId++;
+ }
+ }
+
+ if (sceneId == 9) {
+ // Main entrance palette cycling data
+ g_boothPalCycleData = memberAddr(g_boothsBoltLib, 0x718);
+
+ // Letter marquee sprite (display mode selects variant)
+ byte *sprite = memberAddr(g_boothsBoltLib, (g_displayMode != 0 ? 0x801 : 0x800) + (g_lettersWon << 8));
+ boltPict2Pict(&g_boothLetterSprite, sprite);
+
+ // Exit sign sprites
+ g_boothExitLeft = memberAddr(g_boothsBoltLib, 0x747);
+ g_boothExitRight = memberAddr(g_boothsBoltLib, 0x748);
+ } else {
+ g_boothPalCycleData = memberAddr(g_boothsBoltLib, baseResId + 0x112);
+
+ byte *sprite = memberAddr(g_boothsBoltLib, baseResId + (g_displayMode != 0 ? 0x114 : 0x113));
+ boltPict2Pict(&g_boothLetterSprite, sprite);
+ }
+
+ displayBooth(stFront);
+ loadColors();
+ _xp->setCursorColor(255, 255, 255);
+
+ // First-time cursor position from resource data
+ if (g_needInitCursorPos) {
+ g_cursorX = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 1));
+ g_cursorY = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 2));
+ g_needInitCursorPos = false;
+ }
+
+ _xp->setCursorPos(g_cursorX, g_cursorY);
+
+ flushInput();
+ return boothEventLoop();
}
void BoltEngine::closeBooth() {
+ uint16 buttonState;
+
+ _xp->readCursor(&buttonState, &g_cursorY, &g_cursorX);
+ flushInput();
+ _xp->fillDisplay(0, 0);
+ unloadBooth();
}
void BoltEngine::playTour() {
+ int16 playing = 1;
+
+ _xp->hideCursor();
+ g_screensaverStep = 0;
+
+ if (prepareAV(g_rtfHandle, 4, g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
+ while (playing) {
+ playing = maintainAV(0);
+ if (!playing)
+ break;
+
+ uint32 eventData;
+ int16 event = _xp->getEvent(etEmpty, &eventData);
+
+ switch (event) {
+ case etMouseDown: // Abort tour
+ stopAV();
+ playing = 0;
+ break;
+
+ case etSound: // Sound marker
+ playing = maintainAV(1);
+ break;
+
+ case etTrigger: // Trigger marker from RTF stream
+ screensaverStep();
+ break;
+ }
+ }
+
+ _xp->stopCycle();
+ displayBooth(stFront);
+ }
+
+ _xp->showCursor();
}
-void BoltEngine::finishPlayingHelp() {
+void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
+ g_helpPlaying = 0;
+
+ if (g_helpIsIdle != 0) {
+ g_helpIsIdle = 0;
+ } else {
+ resetInactivityState();
+ }
+
+ if (activeHotspot != 0)
+ restoreColors(0);
+
+ if (activeHotspot != 1)
+ restoreColors(1);
+
+ if (activeHotspot != 2 && g_keyHelp == 0)
+ restoreColors(2);
+
+ g_keyLeft = 0;
+
+ if (g_currentBoothScene == 9) {
+ if (activeHotspot != 7)
+ restoreColors(7);
+
+ if (activeHotspot != 5)
+ restoreColors(5);
+
+ if (activeHotspot != 6)
+ restoreColors(6);
+
+ g_keyRight = 0;
+ g_keyUp = 0;
+ g_keyDown = 0;
+ } else {
+ if (activeHotspot != 3)
+ restoreColors(3);
+
+ g_keyEnter = 0;
+ }
}
-void BoltEngine::hotSpotActive() {
+int16 BoltEngine::hotSpotActive(int16 hotspot) {
+ switch (hotspot) {
+ case 0:
+ case 1:
+ return g_keyLeft;
+
+ case 2:
+ return (g_helpPlaying || g_keyHelp) ? 1 : 0;
+
+ case 3:
+ return g_keyEnter;
+
+ case 4:
+ return 0;
+
+ case 5:
+ return g_keyDown;
+
+ case 6:
+ return g_keyUp;
+
+ case 7:
+ return g_keyRight;
+
+ default:
+ return 0;
+ }
}
void BoltEngine::hoverHotSpot() {
+ int16 i = 0;
+
+ while (i < g_boothNumHotspots) {
+ if (g_boothHotspotDescs[i].contains(g_cursorX, g_cursorY))
+ break;
+
+ i++;
+ }
+
+ if (i == g_hoveredHotspot)
+ return;
+
+ if (g_hoveredHotspot < g_boothNumHotspots) {
+ if (!hotSpotActive(g_hoveredHotspot))
+ restoreColors(g_hoveredHotspot);
+ }
+
+ g_hoveredHotspot = i;
+
+ if (i < g_boothNumHotspots) {
+ if (!hotSpotActive(i))
+ setColors(g_hoveredHotspot);
+ }
}
-void BoltEngine::boothEventLoop() {
+int16 BoltEngine::boothEventLoop() {
+ uint32 eventData;
+ int16 exitCode = 0;
+
+ _xp->showCursor();
+ g_hoveredHotspot = -1;
+ hoverHotSpot();
+
+ while (exitCode == 0 && !shouldQuit()) {
+ _xp->setInactivityTimer(30);
+
+ if (g_helpPlaying != 0) {
+ g_helpPlaying = maintainAudioPlay(0);
+ if (g_helpPlaying == 0)
+ finishPlayingHelp(g_hoveredHotspot);
+ }
+
+ eventData = 0;
+ switch (_xp->getEvent(etEmpty, &eventData)) {
+ case etTimer:
+ if (g_keyLeft == 0 && g_keyRight == 0 &&
+ g_keyUp == 0 && g_keyDown == 0 &&
+ g_keyEnter == 0 && g_keyHelp == 0)
+ break;
+
+ if (g_keyReleased != 0) {
+ // Key release: restore hotspot colors
+ g_keyReleased = 0;
+ if (g_keyLeft != 0) {
+ restoreColors(0);
+ restoreColors(1);
+ } else if (g_keyRight != 0) {
+ restoreColors(7);
+ } else if (g_keyUp != 0) {
+ restoreColors(6);
+ } else if (g_keyDown != 0) {
+ restoreColors(5);
+ } else if (g_keyEnter != 0) {
+ restoreColors(3);
+ } else if (g_keyHelp != 0) {
+ restoreColors(2);
+
+ if (g_helpPlaying != 0) {
+ if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+ break;
+ }
+
+ restoreColors(8);
+ }
+ } else {
+ // Key press: highlight hotspot colors
+ g_keyReleased = 1;
+ if (g_keyLeft != 0) {
+ setColors(0);
+ setColors(1);
+ } else if (g_keyRight != 0) {
+ setColors(7);
+ } else if (g_keyUp != 0) {
+ setColors(6);
+ } else if (g_keyDown != 0) {
+ setColors(5);
+ } else if (g_keyEnter != 0) {
+ setColors(3);
+ } else if (g_keyHelp != 0) {
+ setColors(2);
+
+ if (g_helpPlaying != 0) {
+ if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+ break;
+ }
+
+ setColors(8);
+ }
+ }
+
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case etMouseMove:
+ g_cursorX = (int16)(eventData >> 16);
+ g_cursorY = (int16)eventData;
+ hoverHotSpot();
+ break;
+
+ case etMouseDown:
+ {
+ int16 prevHelp = g_helpPlaying;
+ if (g_helpPlaying != 0) {
+ stopAnimation();
+ finishPlayingHelp(g_hoveredHotspot);
+ }
+
+ if (prevHelp != 0 && g_hoveredHotspot == 2)
+ break;
+
+ if (g_hoveredHotspot == -1)
+ break;
+
+ exitCode = handleButtonPress(g_hoveredHotspot);
+ if (exitCode == 0) {
+ g_hoveredHotspot = -1;
+ hoverHotSpot();
+ }
+
+ break;
+ }
+
+ case etSound:
+ if (g_helpPlaying != 0) {
+ g_helpPlaying = maintainAudioPlay(1);
+ if (g_helpPlaying == 0)
+ finishPlayingHelp(g_hoveredHotspot);
+ }
+
+ break;
+
+ case etInactivity:
+ if (g_helpPlaying != 0)
+ break;
+
+ if (g_keyHelp == 0) {
+ g_keyHelp = 1;
+ g_keyTimer = _xp->startTimer(500);
+ if (g_idleHelpAvailable != 0)
+ continue;
+ } else {
+ if (g_idleHelpAvailable == 0)
+ break;
+
+ if (startAnimation(g_rtfHandle, 0x19)) {
+ g_idleHelpAvailable = 0;
+ g_helpPlaying = 1;
+ g_helpIsIdle = 1;
+ }
+ }
+
+ break;
+
+ case etTrigger:
+ if (g_helpPlaying == 0)
+ break;
+
+ if (g_currentBoothScene == 9)
+ mapIdleAnimation();
+ else
+ boothIdleAnimation();
+
+ break;
+ }
+ }
+
+ if (shouldQuit()) {
+ return -1;
+ }
+
+ _xp->hideCursor();
+ return g_hoveredHotspot;
}
void BoltEngine::resetInactivityState() {
+ if (g_keyTimer != 0) {
+ _xp->killTimer(g_keyTimer);
+ g_keyTimer = 0;
+ }
+
+ g_idleHelpAvailable = 0;
+ _xp->setInactivityTimer(30);
+
+ if (g_keyHelp != 0) {
+ restoreColors(2);
+ if (g_currentBoothScene == 9)
+ restoreColors(8);
+ }
+
+ g_keyHelp = 0;
+}
+
+bool BoltEngine::handleButtonPress(int16 hotspot) {
+ byte savedLeftDoor[3];
+ byte savedRightDoor[3];
+
+ savedLeftDoor[0] = g_leftDoorNavTable[0];
+ savedLeftDoor[1] = g_leftDoorNavTable[1];
+ savedLeftDoor[2] = g_leftDoorNavTable[2];
+ savedRightDoor[0] = g_rightDoorNavTable[0];
+ savedRightDoor[1] = g_rightDoorNavTable[1];
+ savedRightDoor[2] = g_rightDoorNavTable[2];
+
+ if (hotspot != -1)
+ resetInactivityState();
+
+ switch (hotspot) {
+ case 0:
+ case 1:
+ return true;
+
+ case 2: {
+ int16 animId;
+ switch (g_currentBoothScene) {
+ case 3:
+ animId = 0x18;
+ break;
+ case 4:
+ animId = 0x12;
+ break;
+ case 5:
+ animId = 0x13;
+ break;
+ case 6:
+ animId = 0x14;
+ break;
+ case 7:
+ animId = 0x15;
+ break;
+ case 8:
+ animId = 0x16;
+ break;
+ case 9:
+ animId = 0x17;
+ break;
+ default:
+ animId = -1;
+ break;
+ }
+
+ bool ok = false;
+ if (animId != -1)
+ ok = startAnimation(g_rtfHandle, animId);
+
+ if (ok) {
+ g_helpPlaying = 1;
+ setColors(2);
+ g_keyHelp = 0;
+ g_screensaverStep = 0;
+ }
+
+ return false;
+ }
+
+ default:
+ if (g_currentBoothScene == 9) {
+ switch (hotspot) {
+ case 3:
+ case 4:
+ {
+ byte *navTable = (hotspot == 3) ? savedLeftDoor : savedRightDoor;
+ int8 boothOffset = (int8)navTable[g_lettersWon % 3];
+ playAVOverBooth(boothOffset + 12);
+ return false;
+ }
+
+ case 5:
+ _xp->hideCursor();
+ displayBooth(stBack);
+
+ if (playAV(g_rtfHandle, 3, g_displayWidth, g_displayHeight, g_displayX, g_displayY) == 0) {
+ fadeToBlack(1);
+ _xp->setTransparency(0);
+ displayPic(getBOLTMember(g_boothsBoltLib, g_displayMode ? 0x1802 : 0x1801), g_displayX, g_displayY, 0);
+ _xp->updateDisplay();
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), 0, 0);
+ _xp->updateDisplay();
+
+ uint32 timer = _xp->startTimer(5000);
+ if (timer != 0) {
+ uint32 evtData;
+ int16 evtType;
+ do {
+ evtType = _xp->getEvent(etEmpty, &evtData);
+ } while ((evtType != etTimer || evtData != timer) && !shouldQuit());
+ }
+
+ fadeToBlack(32);
+ }
+ return true;
+
+ case 6:
+ _xp->hideCursor();
+
+ vDelete("FredBC");
+ vDelete("GeorgeBE");
+ vDelete("Huck");
+ vDelete("Scooby");
+ vDelete("TopCatBF");
+ vDelete("Yogi");
+
+ displayBooth(stBack);
+ playAV(g_rtfHandle, 5,
+ g_displayWidth, g_displayHeight,
+ g_displayX, g_displayY);
+
+ if (g_lettersWon != 0) {
+ freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
+ if (!getBOLTGroup(g_boothsBoltLib, 0x800, 1)) {
+ g_hoveredHotspot = -1;
+ return true;
+ }
+
+ boltPict2Pict(&g_boothLetterSprite, memberAddr(g_boothsBoltLib, g_displayMode ? 0x801 : 0x800));
+ }
+
+ displayBooth(stFront);
+ g_lettersWon = 0;
+ _xp->showCursor();
+ return false;
+
+ case 7:
+ playTour();
+ return false;
+ }
+ } else {
+ switch (hotspot) {
+ case 3:
+ startCycle(g_boothPalCycleData);
+ _xp->startTimer(2000);
+ return true;
+
+ case 4:
+ restoreColors(hotspot);
+ playBoothAV();
+ return false;
+ }
+ }
+
+ return false;
+ }
+}
+
+void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
+ byte localPalette[384];
+ int16 si = 0;
+
+ byte *entry = paletteTable[index];
+ int16 startIndex = (int16)READ_UINT16(entry);
+ int16 count = (int16)READ_UINT16(entry + 2);
+
+ for (int16 i = 0; i < count; i++) {
+ int16 off = i * 6;
+ localPalette[si] = entry[4 + off];
+ localPalette[si + 1] = entry[6 + off];
+ localPalette[si + 2] = entry[8 + off];
+ si += 3;
+ }
+
+ if (mode == 1)
+ _xp->setPalette(count, startIndex, localPalette);
+}
+
+void BoltEngine::setColors(int16 index) {
+ if (g_currentBoothScene == 9) {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
+ blastColors(g_boothAnimDescs, index, 1);
+ break;
+
+ case 3:
+ case 4:
+ return;
+
+ case 5:
+ case 6:
+ case 7:
+ blastColors(g_boothAnimDescs, index - 2, 1);
+ break;
+
+ case 8:
+ displayPic(g_boothExitLeft, 0, 0, 0);
+ _xp->updateDisplay();
+ break;
+ }
+ } else {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
+ blastColors(g_boothAnimDescs, index, 1);
+ break;
+
+ case 3:
+ if (g_currentBoothScene == 9)
+ break;
+
+ for (int16 i = 0; i < 4; i++)
+ blastColors(g_boothHotPalDescs, i, 1);
+
+ break;
+
+ case 4:
+ blastColors(g_boothAnimDescs, index - 1, 1);
+ break;
+ }
+ }
+}
+
+void BoltEngine::restoreColors(int16 index) {
+ if (g_currentBoothScene == 9) {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2: {
+ byte *palData = g_savedPalettes[index];
+ byte *animDesc = g_boothAnimDescs[index];
+ _xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+ break;
+ }
+
+ case 3:
+ case 4:
+ return;
+
+ case 5:
+ case 6:
+ case 7: {
+ byte *palData = g_savedPalettes[index - 2];
+ byte *animDesc = g_boothAnimDescs[index - 2];
+ _xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+ break;
+ }
+
+ case 8:
+ displayPic(g_boothExitRight, 0, 0, 0);
+ _xp->updateDisplay();
+ break;
+ }
+ } else {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2: {
+ byte *palData = g_savedPalettes[index];
+ byte *animDesc = g_boothAnimDescs[index];
+ _xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+ break;
+ }
+
+ case 3:
+ for (int16 i = 0; i < 4; i++) {
+ byte *palData = g_savedHotPalettes[i];
+ byte *palDesc = g_boothHotPalDescs[i];
+ _xp->setPalette((int16)READ_UINT16(palDesc + 2), (int16)READ_UINT16(palDesc), palData);
+ }
+ break;
+
+ case 4:
+ {
+ byte *palData = g_savedPalettes[index - 1];
+ byte *animDesc = g_boothAnimDescs[index - 1];
+ _xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+ break;
+ }
+ }
+ }
+}
+
+void BoltEngine::loadColors() {
+ for (int16 i = 0; i < g_boothNumAnimations; i++) {
+ byte *animDesc = g_boothAnimDescs[i];
+ _xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[i]);
+ }
+
+ if (g_currentBoothScene == 9) {
+ int16 last = g_boothNumAnimations - 1;
+ byte *animDesc = g_boothAnimDescs[last];
+ _xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[last]);
+ }
+
+ for (int16 i = 0; i < 4; i++) {
+ byte *palDesc = g_boothHotPalDescs[i];
+ _xp->getPalette((int16)READ_UINT16(palDesc), (int16)READ_UINT16(palDesc + 2), g_savedHotPalettes[i]);
+ }
}
-void BoltEngine::handleButtonPress() {
+void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
+ WRITE_BE_INT16(colorMap + 2, READ_BE_INT16(colorMap + 2) + delta);
+ WRITE_BE_INT16(colorMap + 4, READ_BE_INT16(colorMap + 4) + delta);
}
void BoltEngine::playBoothAV() {
+ switch (g_currentBoothScene) {
+ case 3:
+ playAVOverBooth(6);
+ break;
+ case 4:
+ playAVOverBooth(7);
+ break;
+ case 5:
+ playAVOverBooth(8);
+ break;
+ case 6:
+ playAVOverBooth(9);
+ break;
+ case 7:
+ playAVOverBooth(10);
+ break;
+ case 8:
+ playAVOverBooth(11);
+ break;
+ }
}
void BoltEngine::mapIdleAnimation() {
+ g_screensaverStep++;
+
+ switch (g_screensaverStep) {
+ case 1:
+ setColors(0);
+ setColors(1);
+ g_keyLeft = 1;
+ g_keyReleased = 1;
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case 2:
+ g_keyLeft = 0;
+ g_keyReleased = 0;
+
+ if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+ restoreColors(0);
+
+ if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+ restoreColors(1);
+
+ return;
+
+ case 3:
+ setColors(7);
+ g_keyRight = 1;
+ g_keyReleased = 1;
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case 4:
+ g_keyRight = 0;
+ g_keyReleased = 0;
+
+ if (!g_boothHotspotDescs[7] .contains(g_cursorX, g_cursorY))
+ restoreColors(7);
+ return;
+
+ case 5:
+ setColors(6);
+ g_keyUp = 1;
+ g_keyReleased = 1;
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case 6:
+ g_keyUp = 0;
+ g_keyReleased = 0;
+
+ if (!g_boothHotspotDescs[6].contains(g_cursorX, g_cursorY))
+ restoreColors(6);
+ return;
+
+ case 7:
+ setColors(5);
+ g_keyDown = 1;
+ g_keyReleased = 1;
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case 8:
+ g_keyDown = 0;
+ g_keyReleased = 0;
+ g_keyTimer = 0;
+
+ if (!g_boothHotspotDescs[5].contains(g_cursorX, g_cursorY))
+ restoreColors(5);
+ return;
+ }
}
void BoltEngine::boothIdleAnimation() {
+ g_screensaverStep++;
+
+ switch (g_screensaverStep) {
+ case 1:
+ setColors(3);
+ g_keyEnter = 1;
+ g_keyReleased = 1;
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case 2:
+ g_keyEnter = 0;
+ g_keyReleased = 0;
+
+ if (!g_boothHotspotDescs[3].contains(g_cursorX, g_cursorY))
+ restoreColors(3);
+
+ return;
+
+ case 3:
+ setColors(0);
+ setColors(1);
+ g_keyLeft = 1;
+ g_keyReleased = 1;
+ g_keyTimer = _xp->startTimer(500);
+ break;
+
+ case 4:
+ g_keyLeft = 0;
+ g_keyReleased = 0;
+ g_keyTimer = 0;
+
+ if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+ restoreColors(0);
+
+ if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+ restoreColors(1);
+
+ return;
+ }
}
void BoltEngine::screensaverStep() {
+ static const int16 cycleResIds[] = {
+ 0x700, 0x719, 0x71E, 0x720, 0x725, 0x72A, 0x72F, 0x731,
+ -1, 0x736, 0x73B, 0x73F, 0x743, -1
+ };
+
+ g_screensaverStep++;
+
+ if (!(g_screensaverStep & 1)) {
+ _xp->stopCycle();
+ return;
+ }
+
+ int16 index = g_screensaverStep >> 1;
+ if (index > 13)
+ return;
+
+ int16 resId = cycleResIds[index];
+ if (resId == -1)
+ return;
+
+ startCycle(memberAddr(g_boothsBoltLib, resId));
+}
+
+void BoltEngine::fadeToBlack(int16 steps) {
+ byte palette[768];
+ XPPicDesc desc;
+
+ desc.paletteStart = 1;
+ desc.paletteCount = 0x7F;
+
+ _xp->getPalette(0, 256, palette);
+ _xp->setFrameRate(32);
+
+ int16 scale = steps;
+ int16 factor = steps - 1;
+
+ while (scale > 0) {
+ byte *ptr = palette;
+ for (int16 i = 0; i < 256; i++) {
+ ptr[0] = (byte)((uint16)ptr[0] * factor / scale);
+ ptr[1] = (byte)((uint16)ptr[1] * factor / scale);
+ ptr[2] = (byte)((uint16)ptr[2] * factor / scale);
+ ptr += 3;
+ }
+
+ desc.palette = &palette[3];
+ _xp->displayPic(&desc, 0, 0, stFront);
+
+ desc.palette = &palette[384];
+ _xp->displayPic(&desc, 0, 0, stBack);
+
+ _xp->updateDisplay();
+ factor--;
+ scale--;
+ }
+
+ _xp->setFrameRate(0);
}
void BoltEngine::flushInput() {
+ uint32 eventBuf;
+
+ // Drain all mouse-down events
+ while (_xp->getEvent(etMouseDown, &eventBuf) == etMouseDown);
+
+ // Drain all mouse-up events
+ while (_xp->getEvent(etMouseUp, &eventBuf) == etMouseUp);
+}
+
+int16 BoltEngine::winALetter(int16 prevBooth) {
+ g_lettersWon++;
+
+ if (g_lettersWon == 15) {
+ if (g_allLettersWonFlag) {
+ g_allLettersWonFlag = false;
+ g_lettersWon = 16;
+ } else {
+ g_allLettersWonFlag = true;
+ }
+ }
+
+ playAV(g_rtfHandle, g_lettersWon + 31, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+ if (g_lettersWon >= 15)
+ g_lettersWon = 0;
+
+ // Return the booth front ID for the game just played
+ switch (prevBooth) {
+ case 10:
+ return 3; // HuckGame -> HucksBooth
+ case 11:
+ return 4; // FredGame -> FredsBooth
+ case 12:
+ return 5; // ScoobyGame -> ScoobysBooth
+ case 13:
+ return 6; // YogiGame -> YogisBooth
+ case 14:
+ return 7; // GeorgeGame -> GeorgesBooth
+ case 15:
+ return 8; // TopCatGame -> TopCatsBooth
+ default:
+ return 9; // MainEntrance
+ }
}
} // End of namespace Bolt
diff --git a/engines/bolt/gfx.cpp b/engines/bolt/booths/fred.cpp
similarity index 57%
rename from engines/bolt/gfx.cpp
rename to engines/bolt/booths/fred.cpp
index d9fb5e8f211..d0e5b38e0bf 100644
--- a/engines/bolt/gfx.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -23,49 +23,6 @@
namespace Bolt {
-void BoltEngine::setCursorPict(void *sprite) {
-}
-void BoltEngine::startCycle(void *cycleResource) {
-}
-
-void BoltEngine::blastColors() {
-}
-
-void BoltEngine::setColors() {
-}
-
-void BoltEngine::restoreColors() {
-}
-
-void BoltEngine::loadColors() {
-}
-
-void BoltEngine::shiftColorMap() {
-}
-
-void BoltEngine::fadeToBlack() {
-}
-
-void BoltEngine::displayColors(void *palette, int16 page, int16 flags) {
-}
-
-void BoltEngine::boltPict2Pict(void *dest, void *boltSprite) {
-}
-
-void BoltEngine::displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags) {
-}
-
-void BoltEngine::boltCycleToXPCycle() {
-}
-
-void BoltEngine::unpackColors() {
-}
-
-void BoltEngine::inRect() {
-}
-
-void BoltEngine::rectOverlap() {
-}
} // End of namespace Bolt
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/george.cpp
@@ -0,0 +1,28 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/huck.cpp
@@ -0,0 +1,28 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/scooby.cpp
@@ -0,0 +1,28 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/topcat.cpp
@@ -0,0 +1,28 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/yogi.cpp
@@ -0,0 +1,28 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/detection.h b/engines/bolt/detection.h
index ead614b2ab2..e36027700d0 100644
--- a/engines/bolt/detection.h
+++ b/engines/bolt/detection.h
@@ -38,7 +38,7 @@ extern const PlainGameDescriptor boltGames[];
extern const ADGameDescription gameDescriptions[];
-#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+#define GAMEOPTION_EXTEND_SCREEN GUIO_GAMEOPTIONS1
} // End of namespace Bolt
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index f3785c88ad5..7453d983699 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -43,7 +43,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
{
"carnival",
@@ -61,7 +61,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
{
"carnival",
@@ -76,7 +76,7 @@ const ADGameDescription gameDescriptions[] = {
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_UNSTABLE | ADGF_DEMO,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
{
"carnival",
@@ -91,7 +91,7 @@ const ADGameDescription gameDescriptions[] = {
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_UNSTABLE | ADGF_DEMO,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
AD_TABLE_END_MARKER
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
index 9beb569542f..a7ae1e71de2 100644
--- a/engines/bolt/metaengine.cpp
+++ b/engines/bolt/metaengine.cpp
@@ -29,11 +29,11 @@ namespace Bolt {
static const ADExtraGuiOptionsMap optionsList[] = {
{
- GAMEOPTION_ORIGINAL_SAVELOAD,
+ GAMEOPTION_EXTEND_SCREEN,
{
- _s("Use original save/load screens"),
- _s("Use the original save/load screens instead of the ScummVM ones"),
- "original_menus",
+ _s("Extended game resolution"),
+ _s("This option allows the game to play at a slightly extended resolution. This is what the Mac and CDi versions are supposed to play like."),
+ "extended_viewport",
false,
0,
0
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index 4f9c5b5ac04..5574c117a4e 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -2,12 +2,24 @@ MODULE := engines/bolt
MODULE_OBJS = \
anim.o \
+ av.o \
+ barker.o \
bolt.o \
booth.o \
+ booths\fred.o \
+ booths\george.o \
+ booths\huck.o \
+ booths\scooby.o \
+ booths\topcat.o \
+ booths\yogi.o \
console.o \
- gfx.o \
metaengine.o \
resource.o \
+ rtf.o \
+ ssprite.o \
+ state.o \
+ swap.o \
+ utils.o \
xplib\blit.o \
xplib\cursor.o \
xplib\display.o \
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index e1204a58c00..4d8ae6f4c1c 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -21,92 +21,679 @@
#include "bolt/bolt.h"
+#include "common/memstream.h"
+
namespace Bolt {
-bool BoltEngine::openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName) {
+void BoltEngine::decompress(byte *dest, uint32 decompSize, byte *src) {
+ byte cmd;
+ byte fillByte;
+ uint16 count;
+ uint16 offset;
+ byte *backRef;
+
+ for (;;) {
+ cmd = *src++;
+
+ switch (cmd & 0xC0) {
+ case 0x00:
+ count = 0x1F - (cmd & 0x1F);
+ decompSize -= count;
+ count++;
+ while (--count)
+ *dest++ = *src++;
+ break;
+
+ case 0x40:
+ count = 0x23 - (cmd & 0x1F);
+ decompSize -= count;
+ offset = ((cmd & 0x20) << 3) + *src++;
+ backRef = dest - offset;
+ count++;
+ while (--count)
+ *dest++ = *backRef++;
+ break;
+
+ case 0x80:
+ count = (0x20 - (cmd & 0x1F)) << 2;
+ if (cmd & 0x20)
+ count += 2;
+ decompSize -= count;
+ offset = *src++ * 2;
+ backRef = dest - offset;
+ count++;
+ while (--count)
+ *dest++ = *backRef++;
+ break;
+
+ case 0xC0:
+ if (cmd & 0x20) {
+ if ((int32)decompSize <= 0)
+ return;
+ } else {
+ count = (0x20 - (cmd & 0x1F) + (*src++ << 5)) << 2;
+ decompSize -= count;
+ src++;
+ fillByte = *src++;
+ count++;
+ while (--count)
+ *dest++ = fillByte;
+ }
+ break;
+ }
+ }
+}
+
+bool BoltEngine::libRead(Common::File *fileHandle, uint32 offset, byte *dest, uint32 size) {
+ uint32 bytesRead = size;
+
+ if (g_cachedFileHandle != fileHandle || g_cachedFilePos != offset) {
+ if (!_xp->setFilePos(fileHandle, offset, 0))
+ return false;
+ }
+
+ if (_xp->readFile(fileHandle, dest, &bytesRead)) {
+ if (bytesRead == size) {
+ g_cachedFileHandle = fileHandle;
+ g_cachedFilePos = offset + bytesRead;
+ return true;
+ }
+ }
+
+ g_cachedFileHandle = nullptr;
return false;
}
-void BoltEngine::closeBOLTLib(BOLTLib **lib) {
+void BoltEngine::resolveIt(uint32 *ref) {
+ if (*ref == 0xFFFFFFFF) {
+ *ref = 0;
+ return;
+ }
+
+ uint32 swapped = FROM_BE_32(*ref);
+
+ byte *resolved = memberAddrOffset(g_boltCurrentLib, swapped);
+
+ if (resolved != nullptr) {
+ uint32 idx = g_resolvedPtrs.size();
+ g_resolvedPtrs.push_back(resolved);
+ *ref = idx | 0x80000000; // High bit marks as resolved index
+ } else {
+ // Target not loaded yet, queue for later...
+ *ref = swapped;
+ if (g_pendingFixupCount < g_resourceIndexCount) {
+ g_resourceIndex[g_pendingFixupCount] = ref;
+ g_pendingFixupCount++;
+ }
+ }
}
-bool BoltEngine::attemptFreeIndex(BOLTLib *lib, int16 groupId) {
+void BoltEngine::resolvePendingFixups() {
+ while (g_pendingFixupCount > 0) {
+ g_pendingFixupCount--;
+ uint32 *ref = (uint32 *)g_resourceIndex[g_pendingFixupCount];
+
+ byte *resolved = memberAddrOffset(g_boltCurrentLib, *ref);
+
+ uint32 idx = g_resolvedPtrs.size();
+ g_resolvedPtrs.push_back(resolved);
+ *ref = idx | 0x80000000;
+ }
+}
+
+void BoltEngine::resolveFunction(uint32 *ref) {
+ if (*ref == 0xFFFFFFFF) {
+ *ref = 0;
+ return;
+ }
+
+ // TODO!
+ warning("BoltEngine::resolveFunction(): TODO!");
+ uint16 index = (uint16)*ref;
+ *ref = index;
+}
+
+void BoltEngine::resolveAllRefs() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (data == nullptr)
+ return;
+
+ uint32 count = g_boltCurrentMemberEntry->decompSize / 4;
+ uint32 *ptr = (uint32 *)data;
+ for (uint32 i = 0; i < count; i++) {
+ resolveIt(&ptr[i]);
+ }
+}
+
+byte *BoltEngine::getResolvedPtr(byte *data, int offset) {
+ uint32 val = READ_UINT32(data + offset);
+
+ if (val == 0)
+ return nullptr;
+
+ if (val & 0x80000000)
+ return g_resolvedPtrs[val & 0x7FFFFFFF];
+
+ return nullptr;
+}
+
+bool BoltEngine::openBOLTLib(BOLTLib **libPtr, BOLTCallbacks *callbacks, const char *fileName) {
+ Common::File *fileHandle = nullptr;
+ int16 groupCount = 0;
+
+ // If already open, bump reference count...
+ if (*libPtr) {
+ (*libPtr)->refCount++;
+ g_boltCurrentLib = *libPtr;
+ return true;
+ }
+
+ g_boltCurrentLib = nullptr;
+
+ fileHandle = _xp->openFile(fileName, 1);
+ if (fileHandle) {
+ // Read 16-byte BOLT file header...
+ byte headerData[16];
+
+ if (libRead(fileHandle, 0, headerData, 16)) {
+ Common::SeekableReadStream *headerDataStream = new Common::MemoryReadStream(headerData, 16, DisposeAfterUse::NO);
+
+ headerDataStream->read(g_boltFileHeader.header, sizeof(g_boltFileHeader.header));
+ g_boltFileHeader.groupCount = headerDataStream->readByte();
+ g_boltFileHeader.groupDirOffset = headerDataStream->readUint32BE();
+
+ groupCount = g_boltFileHeader.groupCount;
+ if (groupCount == 0)
+ groupCount = 256;
+
+ delete headerDataStream;
+
+ *libPtr = new BOLTLib(groupCount);
+
+ g_boltCurrentLib = *libPtr;
+ if (g_boltCurrentLib) {
+ (*libPtr)->refCount = 0;
+ (*libPtr)->groupCount = g_boltFileHeader.groupCount;
+ (*libPtr)->fileHandle = fileHandle;
+
+ (*libPtr)->callbacks.typeLoadCallbacks = callbacks->typeLoadCallbacks;
+ (*libPtr)->callbacks.typeFreeCallbacks = callbacks->typeFreeCallbacks;
+ (*libPtr)->callbacks.memberLoadCallbacks = callbacks->memberLoadCallbacks;
+ (*libPtr)->callbacks.memberFreeCallbacks = callbacks->memberFreeCallbacks;
+ (*libPtr)->callbacks.groupLoadCallbacks = callbacks->groupLoadCallbacks;
+ (*libPtr)->callbacks.groupFreeCallbacks = callbacks->groupFreeCallbacks;
+
+ byte *groupEntryRawData = (byte *)_xp->allocMem(groupCount * 16);
+
+ if (libRead(fileHandle, 0x10, groupEntryRawData, groupCount * 16)) {
+ Common::SeekableReadStream *groupEntryDataStream = new Common::MemoryReadStream(groupEntryRawData, groupCount * 16, DisposeAfterUse::NO);
+
+ for (int16 i = 0; i < groupCount; i++) {
+ (*libPtr)->groups[i].flags = groupEntryDataStream->readByte();
+ (*libPtr)->groups[i].loadCbIndex = groupEntryDataStream->readByte();
+ (*libPtr)->groups[i].freeCbIndex = groupEntryDataStream->readByte();
+ (*libPtr)->groups[i].memberCount = groupEntryDataStream->readByte();
+ (*libPtr)->groups[i].memberDirOffset = groupEntryDataStream->readUint32BE();
+ (*libPtr)->groups[i].memberDataOffset = groupEntryDataStream->readUint32BE();
+ (*libPtr)->groups[i].groupDataPtrPlaceholder = groupEntryDataStream->readUint32BE();
+ (*libPtr)->groups[i].memberData = nullptr;
+
+ (*libPtr)->groups[i].initMembers((*libPtr)->groups[i].memberCount);
+ }
+
+ delete groupEntryDataStream;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ if (fileHandle != 0)
+ _xp->closeFile(fileHandle);
+
+ if (g_boltCurrentLib != nullptr) {
+ _xp->freeMem(g_boltCurrentLib);
+ g_boltCurrentLib = nullptr;
+ }
+
return false;
}
-void BoltEngine::loadGroupDirectory() {
+bool BoltEngine::closeBOLTLib(BOLTLib **libPtr) {
+ if (!*libPtr)
+ return true;
+
+ if ((*libPtr)->refCount > 0) {
+ (*libPtr)->refCount--;
+ return true;
+ }
+
+ int16 groupCount = (*libPtr)->groupCount;
+ if (groupCount == 0) {
+ groupCount = 256;
+ warning("DEBUG, closeBOLTLib(): CHECK FOR OVERFLOW ");
+ }
+
+ // Free all groups in reverse order...
+ int16 groupId = groupCount << 8;
+ while (groupCount-- > 0) {
+ groupId -= (1 << 8);
+ freeBOLTGroup(*libPtr, groupId, 0);
+ }
+
+ _xp->closeFile((*libPtr)->fileHandle);
+ _xp->freeMem(*libPtr);
+ *libPtr = nullptr;
+
+ return true;
+}
+
+bool BoltEngine::attemptFreeIndex(BOLTLib *lib, int16 groupId) {
+ BOLTGroupEntry *groupEntry = &lib->groups[groupId >> 8];
+
+ if (!groupEntry->memberData)
+ return true;
+
+ int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+ if (memberCount == 0)
+ memberCount = 256;
+
+ for (int16 i = memberCount - 1; i >= 0; i--) {
+ BOLTMemberEntry *member = &groupEntry->members[i];
+ if (member->dataPtr)
+ return false; // Still in use
+ }
+
+ _xp->freeMem(groupEntry->memberData);
+ groupEntry->memberData = nullptr;
+
+ return true;
+}
+
+bool BoltEngine::loadGroupDirectory() {
+ int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+ if (memberCount == 0)
+ memberCount = 256;
+
+ if (g_boltCurrentGroupEntry->memberData) {
+ g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+ return true;
+ }
+
+ byte *rawMemberData = (byte *)_xp->allocMem((uint32)memberCount * 16 + 4);
+ g_boltRawMemberData = rawMemberData;
+ g_boltCurrentGroupEntry->memberData = g_boltRawMemberData;
+
+ if (rawMemberData) {
+ if (libRead(g_boltCurrentLib->fileHandle, g_boltCurrentGroupEntry->memberDataOffset, rawMemberData + 4, (uint32)memberCount * 16)) {
+ Common::SeekableReadStream *memberEntryStream = new Common::MemoryReadStream(rawMemberData + 4, (uint32)memberCount * 16, DisposeAfterUse::NO);
+
+ for (int16 i = 0; i < memberCount; i++) {
+ g_boltCurrentGroupEntry->members[i].flags = memberEntryStream->readByte();
+ g_boltCurrentGroupEntry->members[i].preLoadCbIndex = memberEntryStream->readByte();
+ g_boltCurrentGroupEntry->members[i].preFreeCbIndex = memberEntryStream->readByte();
+ g_boltCurrentGroupEntry->members[i].typeCbIndex = memberEntryStream->readByte();
+ g_boltCurrentGroupEntry->members[i].decompSize = memberEntryStream->readUint32BE();
+ g_boltCurrentGroupEntry->members[i].fileOffset = memberEntryStream->readUint32BE();
+ g_boltCurrentGroupEntry->members[i].dataPtrPlaceholder = memberEntryStream->readUint32BE();
+ g_boltCurrentGroupEntry->members[i].dataPtr = nullptr;
+ }
+
+ delete memberEntryStream;
+
+ g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+ return true;
+ }
+ }
+
+ if (g_boltRawMemberData)
+ _xp->freeMem(g_boltRawMemberData);
+
+ g_boltRawMemberData = nullptr;
+ g_boltCurrentGroupEntry->memberData = nullptr;
+ return false;
}
bool BoltEngine::getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+ g_boltLoadDepth++;
+ int16 resId = groupId & 0xFF00;
+
+ if (lib) {
+ g_boltCurrentLib = lib;
+ g_boltCurrentGroupEntry = &lib->groups[groupId >> 8];
+
+ int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+ if (memberCount == 0)
+ memberCount = 256;
+
+ if (loadGroupDirectory()) {
+ if (g_boltCurrentGroupEntry->loadCbIndex != 0) {
+ lib->callbacks.groupLoadCallbacks[g_boltCurrentGroupEntry->loadCbIndex]();
+ }
+
+ for (int16 i = 0; i < memberCount; i++) {
+ if (!getBOLTMember(lib, resId)) {
+ freeBOLTGroup(lib, resId, 0);
+ g_boltLoadDepth--;
+ return false;
+ }
+
+ resId++;
+ }
+
+ resolvePendingFixups();
+ g_boltLoadDepth--;
+ return true;
+ }
+ }
+
+ freeBOLTGroup(lib, resId, 0);
+ g_boltLoadDepth--;
return false;
}
void BoltEngine::freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+ if (!lib)
+ return;
+
+ g_boltCurrentGroupEntry = &lib->groups[groupId >> 8];
+ g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+
+ if (g_boltCurrentGroupEntry->freeCbIndex != 0) {
+ lib->callbacks.groupFreeCallbacks[g_boltCurrentGroupEntry->freeCbIndex]();
+ }
+
+ if (!g_boltRawMemberData)
+ return;
+
+ int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+ if (memberCount == 0)
+ memberCount = 256;
+
+ int16 resId = groupId & 0xFF00;
+ for (int16 i = 0; i < memberCount; i++) {
+ freeBOLTMember(lib, resId);
+ resId++;
+ }
+
+ attemptFreeIndex(lib, resId);
}
-void *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
+byte *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
+ byte memberIndex = (byte)resId;
+ uint32 compressedSize;
+ byte *tempBuf;
+
+ if (lib) {
+ g_boltCurrentLib = lib;
+ g_boltCurrentGroupEntry = &lib->groups[resId >> 8];
+
+ int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+ if (memberCount == 0)
+ memberCount = 256;
+
+ if (loadGroupDirectory()) {
+ g_boltCurrentMemberEntry = &g_boltCurrentGroupEntry->members[memberIndex];
+
+ if (g_boltCurrentMemberEntry->preLoadCbIndex != 0) {
+ lib->callbacks.memberLoadCallbacks[g_boltCurrentMemberEntry->preLoadCbIndex]();
+ }
+
+ if (g_boltCurrentMemberEntry->dataPtr != nullptr)
+ return g_boltCurrentMemberEntry->dataPtr;
+
+ g_boltCurrentMemberEntry->dataPtr = (byte *)_xp->allocMem(g_boltCurrentMemberEntry->decompSize);
+ if (g_boltCurrentMemberEntry->dataPtr) {
+ // Bit 3 in flags = uncompressed
+ if (g_boltCurrentMemberEntry->flags & 0x08) {
+ // Read directly into destination
+ if (!libRead(lib->fileHandle, g_boltCurrentMemberEntry->fileOffset, g_boltCurrentMemberEntry->dataPtr, g_boltCurrentMemberEntry->decompSize)) {
+ if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+ _xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+ g_boltCurrentMemberEntry->dataPtr = nullptr;
+ }
+
+ return nullptr;
+ }
+ } else {
+ // Compressed member
+ if (memberIndex + 1 < memberCount) {
+ BOLTMemberEntry *nextEntry = &g_boltCurrentGroupEntry->members[memberIndex + 1];
+ compressedSize = nextEntry->fileOffset;
+ } else {
+ // Last entry: total = memberDirSize + memberDirOffset + memberDataOffset
+ warning("is this even working?");
+ compressedSize = (uint32)(memberCount * 16) + g_boltCurrentGroupEntry->memberDataOffset + g_boltCurrentGroupEntry->memberDirOffset;
+ }
+
+ compressedSize -= g_boltCurrentMemberEntry->fileOffset;
+
+ tempBuf = (byte *)_xp->allocMem(compressedSize);
+ if (!tempBuf) {
+ if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+ _xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+ g_boltCurrentMemberEntry->dataPtr = nullptr;
+ }
+
+ return nullptr;
+ }
+
+ if (!libRead(lib->fileHandle, g_boltCurrentMemberEntry->fileOffset, tempBuf, compressedSize)) {
+ _xp->freeMem(tempBuf);
+
+ if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+ _xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+ g_boltCurrentMemberEntry->dataPtr = nullptr;
+ }
+
+ return nullptr;
+ }
+
+ decompress(g_boltCurrentMemberEntry->dataPtr, g_boltCurrentMemberEntry->decompSize, tempBuf);
+
+ _xp->freeMem(tempBuf);
+ }
+
+ lib->callbacks.typeLoadCallbacks[g_boltCurrentMemberEntry->typeCbIndex]();
+
+ if (g_boltLoadDepth == 0)
+ resolvePendingFixups();
+
+ return g_boltCurrentMemberEntry->dataPtr;
+ }
+ }
+ }
+
+ if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+ _xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+ g_boltCurrentMemberEntry->dataPtr = nullptr;
+ }
+
return nullptr;
}
bool BoltEngine::freeBOLTMember(BOLTLib *lib, int16 resId) {
- return false;
+ if (!lib)
+ return true;
+
+ g_boltCurrentGroupEntry = &lib->groups[resId >> 8];
+ g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+
+ if (g_boltRawMemberData == nullptr)
+ return true;
+
+ g_boltCurrentMemberEntry = &g_boltCurrentGroupEntry->members[(resId & 0xFF)];
+
+ if (g_boltCurrentMemberEntry->preFreeCbIndex != 0) {
+ lib->callbacks.memberFreeCallbacks[g_boltCurrentMemberEntry->preFreeCbIndex]();
+ }
+
+ if (g_boltCurrentMemberEntry->dataPtr == nullptr)
+ return true;
+
+ lib->callbacks.typeFreeCallbacks[g_boltCurrentMemberEntry->typeCbIndex]();
+
+ _xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+ g_boltCurrentMemberEntry->dataPtr = nullptr;
+
+ return true;
}
-void *BoltEngine::memberAddr(BOLTLib *lib, int16 resId) {
- return nullptr;
+Common::Rect BoltEngine::memberToRect(byte *data) {
+ int16 x = (int16)READ_UINT16(data);
+ int16 y = (int16)READ_UINT16(data + 2);
+ int16 w = (int16)READ_UINT16(data + 4);
+ int16 h = (int16)READ_UINT16(data + 6);
+ return Common::Rect(x, y, x + w, y + h);
}
-void *BoltEngine::memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset) {
- return nullptr;
+byte *BoltEngine::memberAddr(BOLTLib *lib, int16 resId) {
+ if (!lib)
+ return nullptr;
+
+ BOLTGroupEntry *groupEntry = &lib->groups[resId >> 8];
+
+ if (!groupEntry->memberData)
+ return nullptr;
+
+ BOLTMemberEntry *member = &groupEntry->members[(resId & 0xFF)];
+
+ return member->dataPtr;
+}
+
+byte *BoltEngine::memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset) {
+ if (!lib)
+ return nullptr;
+
+ int16 resId = (int16)(resIdAndOffset >> 16);
+ int16 offset = (int16)(resIdAndOffset & 0xFFFF);
+
+ BOLTGroupEntry *groupEntry = &lib->groups[resId >> 8];
+
+ if (!groupEntry->memberData)
+ return nullptr;
+
+ BOLTMemberEntry *member = &groupEntry->members[(resId & 0xFF)];
+ return (byte *)&member->dataPtr[offset];
}
uint32 BoltEngine::memberSize(BOLTLib *lib, int16 resId) {
- return uint32();
+ if (!lib)
+ return 0;
+
+ BOLTGroupEntry *groupEntry = &lib->groups[resId >> 8];
+
+ if (!groupEntry->memberData)
+ return 0;
+
+ BOLTMemberEntry *member = &groupEntry->members[(resId & 0xFF)];
+ return member->decompSize;
}
-void *BoltEngine::groupAddr(BOLTLib *lib, int16 groupId) {
- return nullptr;
+byte *BoltEngine::groupAddr(BOLTLib *lib, int16 groupId) {
+ if (!lib)
+ return nullptr;
+
+ return lib->groups[groupId >> 8].memberData;
}
bool BoltEngine::allocResourceIndex() {
- return false;
+ g_resourceIndex = (uint32 **)_xp->allocMem(g_resourceIndexCount * sizeof(uintptr));
+ if (!g_resourceIndex)
+ return false;
+
+ return true;
}
void BoltEngine::freeResourceIndex() {
+ if (g_resourceIndex) {
+ _xp->freeMem(g_resourceIndex);
+ g_resourceIndex = nullptr;
+ }
}
-bool BoltEngine::initVRam(int16 poolSize) {
- return false;
-}
+void BoltEngine::swapAllWords() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
-void BoltEngine::freeVRam() {
+ int16 count = int16(g_boltCurrentMemberEntry->decompSize / 2);
+ for (int16 i = 0; i < count; i++) {
+ WRITE_UINT16(data, READ_BE_INT16(data));
+ data += 2;
+ }
}
-bool BoltEngine::vLoad(void *dest, const char *name) {
- return false;
-}
+void BoltEngine::swapAllLongs() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
-bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
- return false;
+ int16 count = (int16)(g_boltCurrentMemberEntry->decompSize / 4);
+ for (int16 i = 0; i < count; i++) {
+ WRITE_UINT32(data, READ_BE_UINT32(data));
+ data += 4;
+ }
}
-bool BoltEngine::vDelete(const char *name) {
- return false;
-}
+BOLTCallback BoltEngine::g_defaultTypeLoadCallbacks[25];
+BOLTCallback BoltEngine::g_defaultTypeFreeCallbacks[25];
+BOLTCallback BoltEngine::g_defaultMemberLoadCallbacks[25];
+BOLTCallback BoltEngine::g_defaultMemberFreeCallbacks[25];
+BOLTCallback BoltEngine::g_defaultGroupLoadCallbacks[25];
+BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
-void BoltEngine::memMove(void *dest, void *src, uint16 count) {
-}
+void BoltEngine::noOpCb() {}
+void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
+void BoltEngine::swapPicHeaderCb() { ((BoltEngine *)g_engine)->swapPicHeader(); }
+void BoltEngine::swapAndResolvePicDescCb() { ((BoltEngine *)g_engine)->swapAndResolvePicDesc(); }
+void BoltEngine::swapFirstWordCb() { ((BoltEngine *)g_engine)->swapFirstWord(); }
+void BoltEngine::swapFirstTwoWordsCb() { ((BoltEngine *)g_engine)->swapFirstTwoWords(); }
+void BoltEngine::swapFirstFourWordsCb() { ((BoltEngine *)g_engine)->swapFirstFourWords(); }
+void BoltEngine::swapSpriteHeaderCb() { ((BoltEngine *)g_engine)->swapSpriteHeader(); }
+void BoltEngine::freeSpriteCleanUpCb() { ((BoltEngine *)g_engine)->freeSpriteCleanUp(); }
-void *BoltEngine::dataAddress(int16 recordOffset) {
- return nullptr;
-}
+void BoltEngine::initCallbacks() {
+ for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
+ g_defaultTypeLoadCallbacks[i] = noOpCb;
+ }
-uint16 BoltEngine::dataSize(int16 recordOffset) {
- return uint16();
-}
+ g_defaultTypeLoadCallbacks[2] = swapAllWordsCb;
+ g_defaultTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_defaultTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_defaultTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+ g_defaultTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+ g_defaultTypeLoadCallbacks[14] = swapFirstFourWordsCb;
-bool BoltEngine::findRecord(const char *name, int16 *outOffset) {
- return false;
+ for (int i = 0; i < ARRAYSIZE(g_defaultTypeFreeCallbacks); i++) {
+ g_defaultTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_defaultTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_defaultMemberLoadCallbacks); i++) {
+ g_defaultMemberLoadCallbacks[i] = noOpCb;
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_defaultMemberFreeCallbacks); i++) {
+ g_defaultMemberFreeCallbacks[i] = noOpCb;
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_defaultGroupLoadCallbacks); i++) {
+ g_defaultGroupLoadCallbacks[i] = noOpCb;
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_defaultGroupFreeCallbacks); i++) {
+ g_defaultGroupFreeCallbacks[i] = noOpCb;
+ }
+
+ g_resourceDefaultCallbacks.typeLoadCallbacks = g_defaultTypeLoadCallbacks;
+ g_resourceDefaultCallbacks.typeFreeCallbacks = g_defaultTypeFreeCallbacks;
+ g_resourceDefaultCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_resourceDefaultCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_resourceDefaultCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_resourceDefaultCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
}
} // End of namespace Bolt
diff --git a/engines/bolt/rtf.cpp b/engines/bolt/rtf.cpp
new file mode 100644
index 00000000000..e6c15d95a09
--- /dev/null
+++ b/engines/bolt/rtf.cpp
@@ -0,0 +1,526 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+#include "common/memstream.h"
+
+namespace Bolt {
+
+RTFResource *BoltEngine::openRTF(const char *fileName) {
+ uint32 headerTag = 0;
+ uint32 dataSize = 0;
+ uint32 bytesRead = 0;
+ RTFResource *rtf = nullptr;
+
+ Common::File *fileHandle = _xp->openFile(fileName, 1);
+ if (!fileHandle)
+ return nullptr;
+
+ // Read 8-byte header: tag + size
+ bytesRead = 4;
+ if (_xp->readFile(fileHandle, &headerTag, &bytesRead) && bytesRead == 4) {
+ bytesRead = 4;
+ if (_xp->readFile(fileHandle, &dataSize, &bytesRead) && bytesRead == 4) {
+ headerTag = FROM_BE_32(headerTag);
+ dataSize = FROM_BE_32(dataSize);
+
+ if (headerTag == MKTAG('M', 'A', 'P', ' ')) {
+ if ((dataSize & 3) == 0) {
+ // Allocate: 10-byte header + index data
+ rtf = new RTFResource();
+ rtf->indexTableRawData = (byte *)_xp->allocMem(dataSize);
+ if (rtf && rtf->indexTableRawData) {
+ // RTF layout:
+ // +0x00: file handle (uint32)
+ // +0x04: pointer to index table (far ptr)
+ // +0x08: entry count (word)
+ // +0x0A: index table data begins here
+ rtf->fileHandle = fileHandle;
+ rtf->indexTablePtr = rtf->indexTable;
+ rtf->entryCount = (int16)(dataSize / sizeof(uint32));
+ rtf->reserveTableSize(rtf->entryCount);
+
+ // Read offset table of available animations...
+ bytesRead = dataSize;
+ if (_xp->readFile(fileHandle, rtf->indexTableRawData, &bytesRead)) {
+ Common::SeekableReadStream *indexDataStream = new Common::MemoryReadStream((byte *)rtf->indexTableRawData, bytesRead, DisposeAfterUse::NO);
+
+ for (int16 i = 0; i < rtf->entryCount; i++) {
+ rtf->indexTable[i] = indexDataStream->readUint32BE();
+ }
+
+ return rtf;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (rtf && rtf->indexTableRawData)
+ _xp->freeMem(rtf->indexTableRawData);
+
+ if (rtf)
+ delete rtf;
+
+ if (fileHandle)
+ _xp->closeFile(fileHandle);
+
+ return nullptr;
+}
+
+void BoltEngine::closeRTF(RTFResource *rtf) {
+ _xp->closeFile(rtf->fileHandle);
+ _xp->freeMem(rtf->indexTableRawData);
+
+ delete rtf;
+ rtf = nullptr;
+}
+
+bool BoltEngine::playRTF(RTFResource *rtfFile, int16 animIndex, byte *ringBuffer, int32 bufferSize) {
+ g_rtfFileHandle = rtfFile->fileHandle;
+
+ if (animIndex >= rtfFile->entryCount)
+ return false;
+
+ uint32 *indexTable = rtfFile->indexTable;
+ if (!_xp->setFilePos(g_rtfFileHandle, indexTable[animIndex], 0))
+ return false;
+
+ g_rtfChunkRemaining = 0;
+ g_rtfMidChunk = false;
+
+ g_ringBufBase = ringBuffer;
+ g_ringBufWritePtr = ringBuffer;
+ g_ringBufSize = bufferSize;
+ g_ringBufFreeSpace = bufferSize;
+
+ if (bufferSize >= 0x96000) {
+ g_ringBufLowWater = bufferSize - 0x4B000;
+ g_ringBufHighWater = bufferSize - 0x19000;
+ } else {
+ g_ringBufLowWater = bufferSize - bufferSize / 2;
+ g_ringBufHighWater = bufferSize - bufferSize / 4;
+ }
+
+ g_ringBufUsed = 0;
+ g_rtfSoundActive = true;
+ g_rtfPlaybackTime = 0;
+ g_rtfCumulativeTime = 0;
+ g_rtfPendingFrame = nullptr;
+ g_rtfSoundQueueHead = nullptr;
+ g_rtfSoundPlayHead = nullptr;
+ g_rtfChunkListTail = nullptr;
+ g_rtfChunkListHead = nullptr;
+ g_rtfChunkCount = 0;
+ g_rtfQueuedSoundCount = 0;
+ g_rtfSoundTiming = 0;
+
+ g_rtfAnimStartOffset = indexTable[animIndex];
+ g_rtfNeedInitialFill = true;
+
+ return true;
+}
+
+bool BoltEngine::fillRTFBuffer() {
+ if (!g_rtfNeedInitialFill)
+ return false;
+
+ if (g_ringBufFreeSpace > g_ringBufLowWater && g_rtfSoundActive) {
+ g_rtfSoundActive = readPacket();
+ return true;
+ }
+
+ g_rtfNeedInitialFill = false;
+ return false;
+}
+
+void BoltEngine::flushRTFSoundQueue() {
+ while (g_rtfQueuedSoundCount < 50 && g_rtfSoundQueueHead) {
+ RTFPacket *packet = g_rtfSoundQueueHead;
+
+ if (packet->tag == MKTAG('A', '2', '2', '8')) {
+ g_rtfQueuedSoundCount++;
+ _xp->playSound(packet->dataPtr, packet->dataSize, 22050);
+
+ if (!g_rtfSoundPlayHead)
+ g_rtfSoundPlayHead = g_rtfSoundQueueHead;
+ }
+
+ g_rtfSoundQueueHead = packet->next;
+ }
+}
+
+bool BoltEngine::maintainRTF(int16 mode, RTFPacket **outFrameData) {
+ if (!g_rtfFileHandle)
+ return false;
+
+ if (!g_ringBufBase)
+ return false;
+
+ if (outFrameData)
+ *outFrameData = nullptr;
+
+ // Initial buffer fill phase...
+ if (g_rtfNeedInitialFill) {
+ if (g_ringBufFreeSpace > g_ringBufLowWater && g_rtfSoundActive) {
+ g_rtfSoundActive = readPacket();
+ return true;
+ }
+
+ g_rtfNeedInitialFill = false;
+ }
+
+ // Advance sound play head when a sound chunk finishes (mode != 0)
+ if (mode != 0) {
+ g_rtfQueuedSoundCount--;
+ g_rtfSoundTiming -= g_rtfSoundPlayHead->duration;
+ g_rtfPlaybackTime += g_rtfSoundPlayHead->duration;
+
+ // Walk to next A228 sound chunk
+ do {
+ g_rtfSoundPlayHead = g_rtfSoundPlayHead->next;
+ if (!g_rtfSoundPlayHead)
+ break;
+ } while (g_rtfSoundPlayHead->tag != MKTAG('A', '2', '2', '8'));
+ }
+
+ // Queue up to 50 pending sound chunks...
+ while (g_rtfQueuedSoundCount < 50 && g_rtfSoundQueueHead) {
+ if (g_rtfSoundQueueHead->tag == MKTAG('A', '2', '2', '8')) {
+ g_rtfQueuedSoundCount++;
+ _xp->playSound(g_rtfSoundQueueHead->dataPtr, g_rtfSoundQueueHead->dataSize, 22050);
+
+ if (!g_rtfSoundPlayHead)
+ g_rtfSoundPlayHead = g_rtfSoundQueueHead;
+ }
+
+ g_rtfSoundQueueHead = g_rtfSoundQueueHead->next;
+ }
+
+ if (g_rtfPendingFrame) {
+ freePacket(g_rtfPendingFrame);
+ g_rtfPendingFrame = nullptr;
+ }
+
+ // Playback complete?
+ if (!g_rtfSoundActive && g_rtfChunkCount == 0) {
+ resetPlaybackState();
+ return false;
+ }
+
+ // Fill ring buffer if needed...
+ if (g_rtfSoundActive && g_ringBufFreeSpace > g_ringBufLowWater) {
+ g_rtfSoundActive = readPacket();
+ if (g_ringBufFreeSpace > g_ringBufHighWater)
+ return true;
+ }
+
+ // Check if next video frame is due...
+ if (g_rtfChunkCount != 0 && (uint16)g_rtfChunkListHead->timestamp <= (uint16)g_rtfPlaybackTime) {
+ g_rtfPendingFrame = deQueuePacket();
+
+ // If it's a sound chunk, just return...
+ if (g_rtfPendingFrame->tag == MKTAG('A', '2', '2', '8'))
+ return true;
+
+ // Calculate frame timing: how late are we?
+ g_rtfPendingFrame->skipCount = g_rtfPlaybackTime - g_rtfPendingFrame->timestamp;
+
+ // Bump skip count if buffer is low...
+ if (g_rtfSoundActive && g_ringBufFreeSpace > g_ringBufLowWater)
+ g_rtfPendingFrame->skipCount += 1;
+
+ if (!g_rtfSoundActive)
+ g_rtfPendingFrame->frameRate = 50; // fixed rate, no sound
+ else
+ g_rtfPendingFrame->frameRate = g_rtfSoundTiming;
+
+ // Find next sound chunk in queue for timing info...
+ RTFPacket *chunk = g_rtfChunkListHead;
+ while (chunk) {
+ if (chunk->tag == MKTAG('A', '2', '2', '8'))
+ break;
+
+ chunk = chunk->next;
+ }
+
+ if (chunk)
+ g_rtfPendingFrame->duration = chunk->duration;
+ else
+ g_rtfPendingFrame->duration = 0;
+
+ if (outFrameData)
+ *outFrameData = g_rtfPendingFrame;
+
+ return true;
+ }
+
+ if (g_rtfSoundActive)
+ g_rtfSoundActive = readPacket();
+
+ return true;
+}
+
+bool BoltEngine::isRTFPlaying() {
+ if (g_rtfSoundActive)
+ return true;
+
+ if (g_rtfSoundQueueHead)
+ return true;
+
+ if (g_rtfQueuedSoundCount > 10)
+ return true;
+
+ return false;
+}
+
+bool BoltEngine::killRTF(uint32 *outFilePos) {
+ if (!g_rtfFileHandle)
+ return false;
+
+ if (!g_ringBufBase)
+ return false;
+
+ _xp->stopSound();
+
+ if (outFilePos)
+ *outFilePos = g_rtfAnimStartOffset;
+
+ return true;
+}
+
+bool BoltEngine::readPacket() {
+ uint32 readSize;
+ uint32 bytesRead;
+ byte padByte;
+ byte *destPtr;
+
+ if (!g_rtfMidChunk) {
+ // Read 8-byte chunk header...
+ bytesRead = 4;
+ if (!_xp->readFile(g_rtfFileHandle, &g_rtfChunkTag, &bytesRead) || bytesRead != 4)
+ return false;
+
+ bytesRead = 4;
+ if (!_xp->readFile(g_rtfFileHandle, &g_rtfChunkSize, &bytesRead) || bytesRead != 4)
+ return false;
+
+ g_rtfChunkTag = FROM_BE_32(g_rtfChunkTag);
+ g_rtfChunkSize = FROM_BE_32(g_rtfChunkSize);
+
+ // End-of-file marker: 'EOR '
+ if (g_rtfChunkTag == MKTAG('E', 'O', 'R',' '))
+ return false;
+
+ g_rtfChunkRemaining = g_rtfChunkSize;
+ g_rtfMidChunk = true;
+ g_rtfCurrentPacket = nullptr;
+ }
+
+ if (!g_rtfCurrentPacket) {
+ g_rtfCurrentPacket = allocPacket(g_rtfChunkSize);
+ if (!g_rtfCurrentPacket)
+ return true;
+
+ g_rtfCurrentPacket->tag = g_rtfChunkTag;
+ }
+
+ // Read up to 4096 bytes at a time...
+ readSize = (g_rtfChunkRemaining > 0x1000) ? 0x1000 : g_rtfChunkRemaining;
+
+ if (readSize != 0) {
+ destPtr = &g_rtfCurrentPacket->dataPtr[g_rtfChunkSize - g_rtfChunkRemaining];
+
+ bytesRead = readSize;
+ if (!_xp->readFile(g_rtfFileHandle, destPtr, &bytesRead))
+ return false;
+
+ g_rtfChunkRemaining -= bytesRead;
+ }
+
+ if (g_rtfChunkRemaining != 0)
+ return true; // More data to read...
+
+ // Chunk fully read!
+ g_rtfMidChunk = false;
+
+ // Skip padding byte for odd-sized chunks...
+ if (g_rtfChunkSize & 1) {
+ bytesRead = 1;
+ if (!_xp->readFile(g_rtfFileHandle, &padByte, &bytesRead))
+ return false;
+ }
+
+ // Finally update file position tracker...
+ g_rtfAnimStartOffset += g_rtfChunkSize + (g_rtfChunkSize & 1) + 8;
+
+ preProcessPacket(g_rtfCurrentPacket);
+
+ return true;
+}
+
+void BoltEngine::preProcessPacket(RTFPacket *packet) {
+ if (packet->tag == MKTAG('A', '2', '2', '8')) {
+ // Sound chunk: attempt to play it immediately!
+ if (g_rtfQueuedSoundCount < 50 && !g_rtfNeedInitialFill) {
+ g_rtfQueuedSoundCount++;
+ _xp->playSound(packet->dataPtr, packet->dataSize, 22050);
+
+ if (!g_rtfSoundPlayHead)
+ g_rtfSoundPlayHead = packet;
+ } else {
+ // Can't play yet, mark queue head...
+ if (!g_rtfSoundQueueHead)
+ g_rtfSoundQueueHead = packet;
+ }
+
+ // Duration in hundredths of a second: dataSize / 2205 (22050 Hz / 10)
+ packet->duration = packet->dataSize / 2205;
+ if (packet->duration == 0)
+ packet->duration = 1;
+
+ g_rtfCumulativeTime += packet->duration;
+ g_rtfSoundTiming += packet->duration;
+ }
+
+ // Stamp packet with current timeline position...
+ packet->timestamp = g_rtfCumulativeTime;
+
+ queuePacket(packet);
+}
+
+void BoltEngine::queuePacket(RTFPacket *packet) {
+ debug(5, "BoltEngine::queuePacket(): Queueing packet of type: %c%c%c%c",
+ (packet->tag >> 24) & 0xFF,
+ (packet->tag >> 16) & 0xFF,
+ (packet->tag >> 8) & 0xFF,
+ packet->tag & 0xFF);
+
+ if (g_rtfChunkListTail) {
+ g_rtfChunkListTail->next = packet;
+ } else {
+ g_rtfChunkListHead = packet;
+ }
+
+ g_rtfChunkListTail = packet;
+ packet->next = nullptr;
+ g_rtfChunkCount++;
+}
+
+RTFPacket *BoltEngine::deQueuePacket() {
+ if (g_rtfChunkListHead)
+ debug(5, "BoltEngine::deQueuePacket(): Dequeuing packet of type: %c%c%c%c",
+ (g_rtfChunkListHead->tag >> 24) & 0xFF,
+ (g_rtfChunkListHead->tag >> 16) & 0xFF,
+ (g_rtfChunkListHead->tag >> 8) & 0xFF,
+ g_rtfChunkListHead->tag & 0xFF);
+
+ RTFPacket *packet = g_rtfChunkListHead;
+
+ if (g_rtfChunkListHead) {
+ g_rtfChunkListHead = packet->next;
+
+ if (!g_rtfChunkListHead)
+ g_rtfChunkListTail = nullptr;
+
+ packet->next = nullptr;
+ }
+
+ g_rtfChunkCount--;
+ return packet;
+}
+
+RTFPacket *BoltEngine::allocPacket(uint32 dataSize) {
+ uint32 totalSize;
+ uint32 available;
+ uint32 freeAfterWrap;
+ byte *allocPtr = nullptr;
+
+ // Header (0x1C) + data, 4-byte aligned
+ totalSize = dataSize + 0x1C;
+ if (totalSize & 3)
+ totalSize = (totalSize + 4) & ~3;
+
+ // How much contiguous space from write pointer to end of buffer?
+ available = (uint32)(g_ringBufBase - g_ringBufWritePtr) + g_ringBufSize;
+
+ if (available >= g_ringBufFreeSpace) {
+ available = g_ringBufFreeSpace;
+ freeAfterWrap = 0;
+ } else {
+ freeAfterWrap = g_ringBufFreeSpace - available;
+ }
+
+ if (available >= totalSize) {
+ allocPtr = g_ringBufWritePtr;
+ } else {
+ // Not enough space before the end: try wrapping to buffer start...
+ if (freeAfterWrap < totalSize)
+ return nullptr;
+
+ allocPtr = g_ringBufBase;
+ g_ringBufWritePtr = g_ringBufBase;
+ g_ringBufUsed = available;
+ g_ringBufFreeSpace -= available;
+ }
+
+ g_ringBufWritePtr += totalSize;
+ g_ringBufFreeSpace -= totalSize;
+
+ // Init packet header and return...
+ RTFPacket *outPacket = new RTFPacket();
+ outPacket->dataPtr = &allocPtr[0x1C];
+ outPacket->dataSize = dataSize;
+ outPacket->allocSize = totalSize;
+ outPacket->ringBufPtr = allocPtr;
+
+ return outPacket;
+}
+
+void BoltEngine::freePacket(RTFPacket *packet) {
+ // Return allocated size to free space...
+ g_ringBufFreeSpace += packet->allocSize;
+
+ // Calculate distance from packet to buffer end...
+ uint32 distToEnd = g_ringBufSize - (packet->ringBufPtr - g_ringBufBase) - (packet->allocSize);
+
+ // If this was the last packet before the wrap point, reclaim the gap!
+ if (distToEnd == g_ringBufUsed) {
+ g_ringBufFreeSpace += g_ringBufUsed;
+ g_ringBufUsed = 0;
+ }
+
+ // If buffer is completely empty, reset write pointer!
+ if (g_ringBufFreeSpace == g_ringBufSize)
+ g_ringBufWritePtr = g_ringBufBase;
+
+ delete packet;
+}
+
+void BoltEngine::resetPlaybackState() {
+ g_rtfFileHandle = nullptr;
+ g_ringBufBase = nullptr;
+ g_rtfSoundActive = false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/ssprite.cpp b/engines/bolt/ssprite.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/ssprite.cpp
@@ -0,0 +1,28 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/state.cpp b/engines/bolt/state.cpp
new file mode 100644
index 00000000000..1ac67080cdb
--- /dev/null
+++ b/engines/bolt/state.cpp
@@ -0,0 +1,179 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+bool BoltEngine::initVRam(int16 poolSize) {
+ g_vramRecordCount = 0;
+ g_vramUsedBytes = 0;
+
+ g_allocatedMemPool = (byte *)_xp->allocMem(poolSize);
+ if (!g_allocatedMemPool) {
+ g_curErrorCode = 1;
+ return false;
+ }
+
+ g_allocatedMemPoolSize = poolSize;
+ g_curErrorCode = 0;
+ return true;
+}
+
+void BoltEngine::freeVRam() {
+ if (g_allocatedMemPool) {
+ _xp->freeMem(g_allocatedMemPool);
+ g_allocatedMemPool = 0;
+ }
+}
+
+bool BoltEngine::vLoad(void *dest, const char *name) {
+ char localName[82];
+ int16 recordOffset;
+
+ strcpy_s(localName, name);
+ uint32 nameLen = strlen(name);
+ if (!(nameLen & 1)) {
+ localName[nameLen] = ' ';
+ nameLen++;
+ localName[nameLen] = '\0';
+ }
+
+ if (!findRecord(localName, &recordOffset)) {
+ g_curErrorCode = 3;
+ return false;
+ }
+
+ int16 size = dataSize(recordOffset);
+ byte *addr = dataAddress(recordOffset);
+
+ memcpy(dest, addr, size);
+
+ g_curErrorCode = 0;
+ return true;
+}
+
+bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
+ char localName[82];
+
+ strcpy_s(localName, name);
+ uint32 nameLen = strlen(name);
+ if (!(nameLen & 1)) {
+ localName[nameLen] = ' ';
+ nameLen++;
+ localName[nameLen] = '\0';
+ }
+
+ uint32 nameStorageLen = nameLen + 1;
+ uint32 entrySize = nameStorageLen + srcSize + 4;
+
+ vDelete(localName);
+
+ if (entrySize + g_vramUsedBytes > g_allocatedMemPoolSize) {
+ g_curErrorCode = 4;
+ return 0;
+ }
+
+ byte *writePtr = &g_allocatedMemPool[g_vramUsedBytes];
+
+ WRITE_UINT16(writePtr, (uint16)entrySize);
+ WRITE_UINT16(writePtr + 2, (uint16)nameStorageLen);
+ writePtr += 4;
+
+ memcpy(writePtr, localName, nameStorageLen);
+ writePtr += nameStorageLen;
+
+ memcpy(writePtr, src, srcSize);
+
+ g_vramUsedBytes += entrySize;
+ g_vramRecordCount++;
+ g_curErrorCode = 0;
+ return true;
+}
+
+bool BoltEngine::vDelete(const char *name) {
+ char localName[82];
+ int16 recordOffset;
+
+ strcpy_s(localName, name);
+ uint32 nameLen = strlen(name);
+ if (!(nameLen & 1)) {
+ localName[nameLen] = ' ';
+ nameLen++;
+ localName[nameLen] = '\0';
+ }
+
+ g_curErrorCode = 3;
+
+ if (g_vramRecordCount == 0)
+ return 0;
+
+ if (!findRecord(localName, &recordOffset))
+ return 0;
+
+ int16 entrySize = (int16)READ_UINT16(&g_allocatedMemPool[recordOffset]);
+
+ if (recordOffset + entrySize < g_vramUsedBytes) {
+ memmove(g_allocatedMemPool + recordOffset,
+ g_allocatedMemPool + recordOffset + entrySize,
+ g_vramUsedBytes - (recordOffset + entrySize));
+ }
+
+ g_vramUsedBytes -= entrySize;
+ g_vramRecordCount--;
+ g_curErrorCode = 0;
+ return true;
+}
+
+byte *BoltEngine::dataAddress(int16 recordOffset) {
+ byte *entry = &g_allocatedMemPool[recordOffset];
+
+ int16 nameStorageLen = (int16)READ_UINT16(entry + 2);
+ return &g_allocatedMemPool[recordOffset + 4 + nameStorageLen];
+}
+
+uint16 BoltEngine::dataSize(int16 recordOffset) {
+ byte *entry = g_allocatedMemPool + recordOffset;
+
+ int16 entrySize = (int16)READ_UINT16(entry);
+ int16 nameStorageLen = (int16)READ_UINT16(entry + 2);
+ return entrySize - 4 - nameStorageLen;
+}
+
+bool BoltEngine::findRecord(const char *name, int16 *outOffset) {
+ int16 offset = 0;
+ byte *ptr = g_allocatedMemPool;
+
+ for (int16 i = 0; i < g_vramRecordCount; i++) {
+ char *entryName = (char *)&ptr[4];
+ if (strcmp(name, entryName) == 0) {
+ *outOffset = offset;
+ return true;
+ }
+
+ offset += (int16)READ_UINT16(ptr);
+ ptr = &g_allocatedMemPool[offset];
+ }
+
+ return false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/swap.cpp b/engines/bolt/swap.cpp
new file mode 100644
index 00000000000..c907ec506d3
--- /dev/null
+++ b/engines/bolt/swap.cpp
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::swapPicHeader() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
+
+ WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
+ WRITE_UINT16(data + 0x04, READ_BE_INT16(data + 0x04));
+}
+
+void BoltEngine::swapAndResolvePicDesc() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
+
+ for (int16 i = 0; i < 4; i++) {
+ resolveIt((uint32 *)(data + 8 + i * 4));
+ WRITE_UINT16(data + i * 2, READ_BE_INT16(data + i * 2));
+ }
+}
+
+void BoltEngine::swapFirstWord() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
+
+ WRITE_UINT16(data, READ_BE_INT16(data));
+}
+
+void BoltEngine::swapFirstTwoWords() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
+
+ WRITE_UINT16(data, READ_BE_INT16(data));
+ WRITE_UINT16(data + 2, READ_BE_INT16(data + 2));
+}
+
+void BoltEngine::swapFirstFourWords() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
+
+ WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
+ WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
+ WRITE_UINT16(data + 0x04, READ_BE_INT16(data + 0x04));
+ WRITE_UINT16(data + 0x06, READ_BE_INT16(data + 0x06));
+}
+
+void BoltEngine::swapSpriteHeader() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (!data)
+ return;
+
+ WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
+ WRITE_UINT16(data + 0x06, READ_BE_INT16(data + 0x06));
+ WRITE_UINT16(data + 0x08, READ_BE_INT16(data + 0x08));
+ WRITE_UINT16(data + 0x0A, READ_BE_INT16(data + 0x0A));
+ WRITE_UINT16(data + 0x0C, READ_BE_INT16(data + 0x0C));
+ WRITE_UINT16(data + 0x16, READ_BE_INT16(data + 0x16));
+
+ if (!(data[0] & 0x10)) {
+ uint32 idx = g_resolvedPtrs.size();
+ g_resolvedPtrs.push_back(data + 0x18);
+ WRITE_UINT32(data + 0x12, idx | 0x80000000);
+ }
+}
+
+void BoltEngine::freeSpriteCleanUp() {
+ // No-op
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
new file mode 100644
index 00000000000..67df914881a
--- /dev/null
+++ b/engines/bolt/utils.cpp
@@ -0,0 +1,156 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+#include "common/config-manager.h"
+
+namespace Bolt {
+
+void BoltEngine::displayColors(byte *palette, int16 page, int16 flags) {
+ byte localPalette[384];
+ int16 startIndex = 0, endIndex = 0;
+
+ if (!palette)
+ return;
+
+ if (flags == 0) {
+ // Indices 0-127
+ startIndex = (int16)READ_UINT16(palette + 2);
+ endIndex = MIN<int16>((int16)READ_UINT16(palette + 4), 127);
+ } else if (flags == 1) {
+ // Indices 128-255
+ startIndex = MAX<int16>((int16)READ_UINT16(palette + 2), 128);
+ endIndex = (int16)READ_UINT16(palette + 4);
+ }
+
+ // Copy RGB triplets from palette resource...
+ byte *src = palette + startIndex * 3 + 6;
+ byte *dst = localPalette;
+
+ for (int16 i = startIndex; i <= endIndex; i++) {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ }
+
+ XPPicDesc picDesc;
+
+ picDesc.pixelData = nullptr;
+ picDesc.paletteStart = (flags != 0) ? startIndex - 128 : startIndex;
+ picDesc.paletteCount = endIndex - startIndex + 1;
+ picDesc.palette = localPalette;
+
+ _xp->displayPic(&picDesc, 0, 0, page);
+}
+
+void BoltEngine::sub_11035() {
+}
+
+void BoltEngine::boltPict2Pict(XPPicDesc *dest, byte *boltSprite) {
+ dest->pixelData = getResolvedPtr(boltSprite, 0x12);
+ dest->width = (int16)READ_UINT16(boltSprite + 0x0A);
+ dest->height = (int16)READ_UINT16(boltSprite + 0x0C);
+ dest->palette = nullptr;
+ dest->paletteStart = 0;
+ dest->paletteCount = 0;
+ dest->flags = 0;
+
+ if (boltSprite[1] & 0x01)
+ dest->flags |= 1;
+
+ if (boltSprite[0] & 0x02)
+ dest->flags |= 2;
+}
+
+void BoltEngine::displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page) {
+ XPPicDesc localDesc;
+
+ if (!boltSprite)
+ return;
+
+ boltPict2Pict(&localDesc, boltSprite);
+
+ _xp->displayPic(&localDesc,
+ (int16)READ_UINT16(boltSprite + 0x06) + xOff,
+ (int16)READ_UINT16(boltSprite + 0x08) + yOff,
+ page);
+}
+
+void BoltEngine::sub_11131() {
+}
+
+const char *BoltEngine::assetPath(const char *fileName) {
+ if (Common::File::exists(fileName)) {
+ return fileName;
+ } else if (Common::File::exists(Common::Path(Common::String::format("ASSETS/%s", fileName)))) {
+ return Common::Path(Common::String::format("ASSETS/%s", fileName)).toString().c_str();
+ } else {
+ error("BoltEngine::assetPath(): Invalid game path");
+ }
+}
+
+void BoltEngine::boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc) {
+ int16 cycleIdx = 0;
+
+ for (int16 i = 0; i < 4 && cycleIdx < 4; i++) {
+ int16 type = (int16)READ_UINT16(srcData + i * 2);
+ if (type != 1)
+ continue;
+
+ byte *cycleData = getResolvedPtr(srcData, 8 + i * 4);
+ if (!cycleData)
+ continue;
+
+ if (cycleData[5] == 0) {
+ cycleDesc[cycleIdx].startIndex = (int16)READ_UINT16(cycleData);
+ cycleDesc[cycleIdx].endIndex = (int16)READ_UINT16(cycleData + 2);
+ } else {
+ cycleDesc[cycleIdx].startIndex = (int16)READ_UINT16(cycleData + 2);
+ cycleDesc[cycleIdx].endIndex = (int16)READ_UINT16(cycleData);
+ }
+
+ cycleDesc[cycleIdx].delay = (int16)(cycleData[4] * (int32)1000 / 60);
+ cycleIdx++;
+ }
+
+ while (cycleIdx < 4) {
+ cycleDesc[cycleIdx].startIndex = 0;
+ cycleDesc[cycleIdx].endIndex = 0;
+ cycleDesc[cycleIdx].delay = 0;
+ cycleIdx++;
+ }
+}
+
+void BoltEngine::unpackColors(int16 count, byte *packedColors) {
+ byte *src = packedColors;
+ byte *dst = packedColors;
+
+ while (count-- > 0) {
+ dst[0] = src[1];
+ dst[1] = src[2];
+ dst[2] = src[3];
+ dst += 3;
+ src += 4;
+ }
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/blit.cpp b/engines/bolt/xplib/blit.cpp
index d7aae972728..e8ae7c92a35 100644
--- a/engines/bolt/xplib/blit.cpp
+++ b/engines/bolt/xplib/blit.cpp
@@ -50,6 +50,8 @@ void XpLib::dirtyBlit(byte *src, byte *dst, uint16 width, uint16 height, byte *d
dirtyFlags[row] = (flag == 2) ? 3 : 0;
memcpy(dst, src, width);
+ src += width;
+ dst += width;
}
}
@@ -173,8 +175,7 @@ void XpLib::rleCompositeBlit(byte *rle, byte *background, byte *dst, uint16 widt
// Check if row is all transparent...
if (rlePtr[0] == 0x80 && rlePtr[1] == 0x00) {
if (dirtyFlags[row] & 2) {
- // Row was already transparent: mark clean and skip...
- dirtyFlags[row] = 3;
+ // Row was already transparent: skip
rlePtr += 2;
dstPtr = rowEnd;
bgPtr += width;
diff --git a/engines/bolt/xplib/cursor.cpp b/engines/bolt/xplib/cursor.cpp
index cf1f53a3dcb..cf1fef61d97 100644
--- a/engines/bolt/xplib/cursor.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -36,10 +36,9 @@ bool XpLib::initCursor() {
}
g_cursorSprite.pixelData = g_cursorBuffer;
-
- g_cursorWidth = 16;
- g_cursorHeight = 16;
- g_cursorScale = 2;
+ g_cursorSprite.width = 16;
+ g_cursorSprite.height = 16;
+ g_cursorSprite.flags = 2;
return true;
}
@@ -48,7 +47,7 @@ void XpLib::shutdownCursor() {
}
bool XpLib::readCursor(uint16 *outButtons, int16 *outX, int16 *outY) {
- if (g_cursorHidden == 0)
+ if (g_cursorHidden != 0)
return false;
*outX = g_lastCursorX;
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
index 27b2ee82b16..78ef3391987 100644
--- a/engines/bolt/xplib/display.cpp
+++ b/engines/bolt/xplib/display.cpp
@@ -25,22 +25,22 @@
namespace Bolt {
bool XpLib::initDisplay() {
- g_virtualWidth = 320;
- g_virtualHeight = 200;
- g_currentDisplayPage = 0;
+ g_virtualWidth = (_bolt->g_extendedViewport) ? EXTENDED_SCREEN_WIDTH : SCREEN_WIDTH;
+ g_virtualHeight = (_bolt->g_extendedViewport) ? EXTENDED_SCREEN_HEIGHT : SCREEN_HEIGHT;
+ g_currentDisplayPage = stFront;
// Create front and back display surfaces
- if (!createSurface(&g_surfaces[0]))
+ if (!createSurface(&g_surfaces[stFront]))
return false;
- if (!createSurface(&g_surfaces[1]))
+ if (!createSurface(&g_surfaces[stBack]))
return false;
- fillDisplay(0, 0);
- fillDisplay(1, 0);
+ fillDisplay(0, stFront);
+ fillDisplay(0, stBack);
- g_spriteOverlayActive = 0;
- g_spriteOverlayEnabled = 0;
+ g_prevRenderFlags = 0;
+ g_renderFlags = 0;
g_frameRateFPS = 0;
g_overlayCount = 0;
g_prevDirtyCount = 0;
@@ -54,8 +54,8 @@ bool XpLib::initDisplay() {
}
void XpLib::shutdownDisplay() {
- freeSurface(&g_surfaces[0]);
- freeSurface(&g_surfaces[1]);
+ freeSurface(&g_surfaces[stFront]);
+ freeSurface(&g_surfaces[stBack]);
stopCycle();
}
@@ -93,36 +93,29 @@ bool XpLib::createSurface(XPSurface *surf) {
}
void XpLib::freeSurface(XPSurface *surf) {
- if (surf->mainPic.palette != nullptr) {
+ if (surf->mainPic.palette) {
freeMem(surf->mainPic.palette);
surf->mainPic.palette = nullptr;
}
- if (surf->mainPic.pixelData != nullptr) {
+ if (surf->mainPic.pixelData) {
freeMem(surf->mainPic.pixelData);
surf->mainPic.pixelData = nullptr;
}
}
-bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
- for (int16 i = 0; i < numSpecs; i++) {
+bool XpLib::setDisplaySpec(int *outMode, DisplaySpecs *spec) {
+ *outMode = spec->id;
+ g_surfaceWidth = spec->width;
+ g_surfaceHeight = spec->height;
- if (specs[i].width == 320 && specs[i].height == 200) {
- *outMode = i;
- g_surfaceWidth = specs[i].width;
- g_surfaceHeight = specs[i].height;
-
- if (!createSurface(&g_surfaces[0]))
- return false;
-
- if (!createSurface(&g_surfaces[1]))
- return false;
+ if (!createSurface(&g_surfaces[stFront]))
+ return false;
- return true;
- }
- }
+ if (!createSurface(&g_surfaces[stBack]))
+ return false;
- return false;
+ return true;
}
void XpLib::setCoordSpec(int16 x, int16 y, int16 width, int16 height) {
@@ -152,33 +145,40 @@ void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
bool isFullScreen = (pic->width == g_surfaceWidth && pic->height == g_surfaceHeight);
if (isFullScreen)
- g_spriteOverlayEnabled |= 4; // Mark full-screen overwrite
+ g_renderFlags |= RF_FULL_REDRAW; // Mark full-screen overwrite
// Full-screen transparent non-RLE overlayPic on surface 0: store as overlay source...
- if (isFullScreen && page == 0 && (pic->flags & 1) && !(pic->flags & 2)) {
- memcpy(&surf->overlayPic, pic, sizeof(pic));
- g_spriteOverlayEnabled |= 1;
+ if (isFullScreen && page == stFront && (pic->flags & 1) && !(pic->flags & 2)) {
+ surf->overlayPic.pixelData = pic->pixelData;
+ surf->overlayPic.width = pic->width;
+ surf->overlayPic.height = pic->height;
+ surf->overlayPic.palette = pic->palette;
+ surf->overlayPic.paletteStart = pic->paletteStart;
+ surf->overlayPic.paletteCount = pic->paletteCount;
+ surf->overlayPic.flags = pic->flags;
+
+ g_renderFlags |= RF_OVERLAY_ACTIVE;
} else {
// Clear previous overlay if needed...
- if ((g_spriteOverlayEnabled & 1) && page == 0) {
+ if ((g_renderFlags & RF_OVERLAY_ACTIVE) && page == stFront) {
if (!isFullScreen)
- clipAndBlit(&surf->overlayPic, surf, 0, 0, nullptr);
+ clipAndBlit(&surf->overlayPic, &surf->mainPic, 0, 0, nullptr);
- g_spriteOverlayEnabled &= ~1;
+ g_renderFlags &= ~RF_OVERLAY_ACTIVE;
}
// Clip and blit to surface...
Common::Rect clipResult;
- if (clipAndBlit(pic, surf, x, y, &clipResult))
+ if (clipAndBlit(pic, &surf->mainPic, x, y, &clipResult))
addDirtyRect(&clipResult);
}
// Mark page as dirty...
- g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+ g_renderFlags |= (page != stFront) ? RF_BACK_DIRTY : RF_FRONT_DIRTY;
}
// --- Palette data ---
- if (pic->palette != nullptr) {
+ if (pic->palette) {
int16 palStart = pic->paletteStart;
int16 palEnd = palStart + pic->paletteCount - 1;
int16 adjStart = palStart;
@@ -206,9 +206,9 @@ void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
memcpy(surf->mainPic.palette + destOffset, palData, adjCount * 3);
}
- int16 dirtyFlag = (page != 0) ? 0x40 : 0x20;
+ int16 dirtyFlag = (page != stFront) ? RF_BACK_PAL_DIRTY : RF_FRONT_PAL_DIRTY;
- if (g_spriteOverlayEnabled & dirtyFlag) {
+ if (g_renderFlags & dirtyFlag) {
// Merge dirty range...
if (surf->dirtyPalStart > adjStart)
surf->dirtyPalStart = adjStart;
@@ -219,7 +219,7 @@ void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
// First palette update, set range...
surf->dirtyPalStart = adjStart;
surf->dirtyPalEnd = palEnd;
- g_spriteOverlayEnabled |= dirtyFlag;
+ g_renderFlags |= dirtyFlag;
}
}
}
@@ -241,7 +241,7 @@ void XpLib::dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uin
}
}
-bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip) {
+bool XpLib::clipAndBlit(XPPicDesc *src, XPPicDesc *dest, int16 x, int16 y, Common::Rect *outClip) {
int16 blitMode = 0;
if (src->flags & 1)
@@ -251,7 +251,7 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
blitMode |= 1; // RLE
Common::Rect srcRect(x, y, x + src->width, y + src->height);
- Common::Rect destRect(0, 0, dest->mainPic.width, dest->mainPic.height);
+ Common::Rect destRect(0, 0, dest->width, dest->height);
Common::Rect clipped;
clipped = srcRect.findIntersectingRect(destRect);
@@ -260,7 +260,7 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
// For transparent blits, check if content actually changed...
if (src->flags & 1) {
- if (memcmp(&clipped, &srcRect, 8) == 0)
+ if (!clipped.equals(srcRect))
return false;
}
@@ -271,14 +271,14 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
outClip->bottom = clipped.bottom;
}
- int16 srcOff = (clipped.top - srcRect.top) * src->width + (clipped.left - srcRect.left);
- int16 destOff = clipped.top * dest->mainPic.width + clipped.left;
- int16 clipW = clipped.right - clipped.left;
- int16 clipH = clipped.bottom - clipped.top;
+ int32 srcOff = (clipped.top - srcRect.top) * src->width + (clipped.left - srcRect.left);
+ int32 destOff = clipped.top * dest->width + clipped.left;
+ int32 clipW = clipped.right - clipped.left;
+ int32 clipH = clipped.bottom - clipped.top;
dispatchBlit(blitMode,
src->pixelData + srcOff, src->width,
- dest->mainPic.pixelData + destOff, dest->mainPic.width,
+ dest->pixelData + destOff, dest->width,
clipW, clipH
);
@@ -286,11 +286,11 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
}
void XpLib::addDirtyRect(Common::Rect *rect) {
- if (g_spriteOverlayEnabled & 4)
+ if (g_renderFlags & RF_FULL_REDRAW)
return; // Full-screen overwrite, no need to track...
if (g_overlayCount >= 30) {
- g_spriteOverlayEnabled |= 4; // Too many rects, mark full dirty...
+ g_renderFlags |= RF_FULL_REDRAW; // Too many rects, mark full dirty...
return;
}
@@ -309,33 +309,37 @@ void XpLib::setFrameRate(int16 fps) {
void XpLib::updateDisplay() {
if (g_cursorHidden == 0)
- g_spriteOverlayEnabled |= 0x80;
+ g_renderFlags |= RF_CURSOR_VISIBLE;
else
- g_spriteOverlayEnabled &= ~0x80;
+ g_renderFlags &= ~RF_CURSOR_VISIBLE;
- // This will call delayMillis (but not pollEvent, since that has to be handled elsewhere)...
+ // This will call delayMillis (but must not call pollEvent, otherwise events get desynced)...
waitForFrameRate();
handlePaletteTransitions();
// If back surface pixels changed, mark all with cursor bit and reset row flags...
- if (g_spriteOverlayEnabled & 0x10) {
- markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
- memset(&g_rowDirtyFlags, 0, 200);
+ if (g_renderFlags & RF_BACK_DIRTY) {
+ markCursorPixels(g_surfaces[stBack].mainPic.pixelData, (uint32)g_surfaces[stBack].mainPic.width * (uint32)g_surfaces[stBack].mainPic.height);
+ if (_bolt->g_extendedViewport) {
+ memset(g_rowDirtyFlags, 0, EXTENDED_SCREEN_HEIGHT);
+ } else {
+ memset(g_rowDirtyFlags, 0, SCREEN_HEIGHT);
+ }
}
// Render to screen!
- if (g_spriteOverlayEnabled & 1) {
+ if (g_renderFlags & RF_OVERLAY_ACTIVE) {
overlayComposite(); // RLE overlay path
} else {
compositeToScreen(); // Standard dirty-rect path
}
// Save current state, clear per-frame flags...
- g_spriteOverlayActive = g_spriteOverlayEnabled;
- g_spriteOverlayEnabled &= 0x03; // Keep only overlay + double-buffer bits...
+ g_prevRenderFlags = g_renderFlags;
+ g_renderFlags &= (RF_OVERLAY_ACTIVE | RF_DOUBLE_BUFFER); // Keep only overlay + double-buffer bits...
- _bolt->_system->copyRectToScreen(g_vgaFramebuffer, 320, 0, 0, 320, 200);
+ _bolt->_system->copyRectToScreen(g_vgaFramebuffer, g_virtualWidth, 0, 0, g_virtualWidth, g_virtualHeight);
_bolt->_system->updateScreen();
}
@@ -352,15 +356,15 @@ void XpLib::waitForFrameRate() {
void XpLib::handlePaletteTransitions() {
// Only act if palette dirty flags are set alongside pixel dirty flags...
- if (!((g_spriteOverlayEnabled & 0x20) && (g_spriteOverlayEnabled & 0x08)) &&
- !((g_spriteOverlayEnabled & 0x40) && (g_spriteOverlayEnabled & 0x10)))
+ if (!((g_renderFlags & RF_FRONT_PAL_DIRTY) && (g_renderFlags & RF_FRONT_DIRTY)) &&
+ !((g_renderFlags & RF_BACK_PAL_DIRTY) && (g_renderFlags & RF_BACK_DIRTY)))
return;
// Build transition code: bit 0 = entering double-buffer, bit 1 = was double-buffer
int16 transition = 0;
- if (g_spriteOverlayEnabled & 2)
+ if (g_renderFlags & RF_DOUBLE_BUFFER)
transition |= 1;
- if (g_spriteOverlayActive & 2)
+ if (g_prevRenderFlags & RF_DOUBLE_BUFFER)
transition |= 2;
switch (transition) {
@@ -371,11 +375,11 @@ void XpLib::handlePaletteTransitions() {
case 1: {
// Single -> Double
- if (!(g_spriteOverlayEnabled & 0x40))
+ if (!(g_renderFlags & RF_BACK_PAL_DIRTY))
break;
// Check if back surface palette actually differs from VGA palette...
- XPSurface *back = &g_surfaces[1];
+ XPSurface *back = &g_surfaces[stBack];
int16 count = back->dirtyPalEnd - back->dirtyPalStart + 1;
int16 surfOffset = (back->dirtyPalStart - back->mainPic.paletteStart) * 3;
@@ -390,11 +394,11 @@ void XpLib::handlePaletteTransitions() {
case 2: {
// Double -> Single
- if (!(g_spriteOverlayEnabled & 0x20))
+ if (!(g_renderFlags & RF_FRONT_PAL_DIRTY))
break;
// Check if front surface palette differs from cursor palette page...
- XPSurface *front = &g_surfaces[0];
+ XPSurface *front = &g_surfaces[stFront];
int16 count = front->dirtyPalEnd - front->dirtyPalStart + 1;
int16 surfOffset = (front->dirtyPalStart - front->mainPic.paletteStart) * 3;
@@ -415,8 +419,8 @@ void XpLib::handlePaletteTransitions() {
void XpLib::flushPalette() {
// Front surface palette -> VGA indices 1-127
- if (g_spriteOverlayEnabled & 0x20) {
- XPSurface *front = &g_surfaces[0];
+ if (g_renderFlags & RF_FRONT_PAL_DIRTY) {
+ XPSurface *front = &g_surfaces[stFront];
int16 start = front->dirtyPalStart;
int16 count = front->dirtyPalEnd - start + 1;
int16 offset = (start - front->mainPic.paletteStart) * 3;
@@ -424,8 +428,8 @@ void XpLib::flushPalette() {
}
// Back surface palette -> VGA indices 129-255 (start + 128)
- if (g_spriteOverlayEnabled & 0x40) {
- XPSurface *back = &g_surfaces[1];
+ if (g_renderFlags & RF_BACK_PAL_DIRTY) {
+ XPSurface *back = &g_surfaces[stBack];
int16 start = back->dirtyPalStart;
int16 count = back->dirtyPalEnd - start + 1;
int16 offset = (start - back->mainPic.paletteStart) * 3;
@@ -434,9 +438,9 @@ void XpLib::flushPalette() {
}
void XpLib::overlayComposite() {
- bool pixelsDirty = (g_spriteOverlayEnabled & 0x18) != 0;
- bool cursorNow = (g_spriteOverlayEnabled & 0x80) != 0;
- bool cursorPrev = (g_spriteOverlayActive & 0x80) != 0;
+ bool pixelsDirty = (g_renderFlags & (RF_FRONT_DIRTY | RF_BACK_DIRTY)) != 0;
+ bool cursorNow = (g_renderFlags & RF_CURSOR_VISIBLE) != 0;
+ bool cursorPrev = (g_prevRenderFlags & RF_CURSOR_VISIBLE) != 0;
// Nothing changed at all, just flush the palette...
if (!pixelsDirty && !cursorNow && !cursorPrev) {
@@ -448,21 +452,21 @@ void XpLib::overlayComposite() {
// Decode RLE overlay onto front surface...
if (pixelsDirty) {
- if (g_spriteOverlayEnabled & 2) {
+ if (g_renderFlags & RF_DOUBLE_BUFFER) {
// Double-buffer: RLE composite (transparent pixels from back surface)
rleCompositeBlit(
- g_surfaces[0].overlayPic.pixelData,
- g_surfaces[1].mainPic.pixelData,
- g_surfaces[0].mainPic.pixelData,
+ g_surfaces[stFront].overlayPic.pixelData,
+ g_surfaces[stBack].mainPic.pixelData,
+ g_surfaces[stFront].mainPic.pixelData,
g_virtualWidth, g_virtualHeight,
g_rowDirtyFlags);
} else {
// Single-buffer: straight RLE decode to front
rleBlit(
- g_surfaces[0].overlayPic.pixelData,
- g_surfaces[0].overlayPic.width,
- g_surfaces[0].mainPic.pixelData,
- g_surfaces[0].mainPic.width,
+ g_surfaces[stFront].overlayPic.pixelData,
+ g_surfaces[stFront].overlayPic.width,
+ g_surfaces[stFront].mainPic.pixelData,
+ g_surfaces[stFront].mainPic.width,
g_virtualWidth, g_virtualHeight);
}
}
@@ -473,43 +477,42 @@ void XpLib::overlayComposite() {
cursorY = g_lastCursorY - g_cursorHotspotY;
// Save background under cursor...
- clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+ clipAndBlit(&g_surfaces[stFront].mainPic, &g_cursorBackgroundSave,
-cursorX, -cursorY, nullptr);
// Draw cursor sprite...
- clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
- cursorX, cursorY, &g_cursorClipRect2);
+ clipAndBlit(&g_cursorSprite, &g_surfaces[stFront].mainPic,
+ cursorX, cursorY, &g_overlayCursorRect);
}
flushPalette();
// Transfer front surface to main framebuffer...
if (pixelsDirty) {
- if (g_spriteOverlayEnabled & 2) {
+ if (g_renderFlags & RF_DOUBLE_BUFFER) {
// Tracked blit: only copy rows that changed...
dirtyBlit(
- g_surfaces[0].mainPic.pixelData,
+ g_surfaces[stFront].mainPic.pixelData,
g_vgaFramebuffer,
g_virtualWidth, g_virtualHeight,
g_rowDirtyFlags);
} else {
- memcpy(g_vgaFramebuffer, g_surfaces[0].mainPic.pixelData,
+ memcpy(g_vgaFramebuffer, g_surfaces[stFront].mainPic.pixelData,
(uint32)g_virtualWidth * (uint32)g_virtualHeight);
}
} else {
if (cursorNow)
- compositeDirtyRects(&g_cursorClipRect2, 1);
+ compositeDirtyRects(&g_overlayCursorRect, 1);
if (cursorPrev)
- compositeDirtyRects(&g_prevCursorRect2, 1);
+ compositeDirtyRects(&g_prevOverlayCursorRect, 1);
}
// Save cursor rect and restore front surface
if (cursorNow) {
- g_prevCursorRect2 = g_cursorClipRect2;
+ g_prevOverlayCursorRect = g_overlayCursorRect;
- clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
- cursorX, cursorY, nullptr);
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, cursorX, cursorY, nullptr);
}
}
@@ -517,26 +520,26 @@ void XpLib::compositeToScreen() {
int16 cursorX = 0, cursorY = 0;
// Draw cursor onto front surface...
- if (g_spriteOverlayEnabled & 0x80) {
+ if (g_renderFlags & RF_CURSOR_VISIBLE) {
cursorX = g_lastCursorX - g_cursorHotspotX;
cursorY = g_lastCursorY - g_cursorHotspotY;
// Save background under cursor position...
- clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], -cursorX, -cursorY, nullptr);
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, -cursorX, -cursorY, nullptr);
// Draw cursor sprite at position...
- clipAndBlit(&g_cursorSprite, &g_surfaces[0], cursorX, cursorY, &g_cursorDirtyRect);
+ clipAndBlit(&g_cursorSprite, &g_surfaces[stFront].mainPic, cursorX, cursorY, &g_cursorRect);
}
flushPalette();
- if (!(g_spriteOverlayEnabled & 2)) {
+ if (!(g_renderFlags & RF_DOUBLE_BUFFER)) {
// SINGLE-BUFFER MODE
- if (g_spriteOverlayEnabled & 0x08) {
- if (g_spriteOverlayEnabled & 4) {
+ if (g_renderFlags & RF_FRONT_DIRTY) {
+ if (g_renderFlags & RF_FULL_REDRAW) {
// Full-screen dirty, blit entire front surface to framebuffer...
- blit(g_surfaces[0].mainPic.pixelData, g_surfaces[0].mainPic.width,
+ blit(g_surfaces[stFront].mainPic.pixelData, g_surfaces[stFront].mainPic.width,
g_vgaFramebuffer, g_virtualWidth,
g_virtualWidth, g_virtualHeight);
} else {
@@ -548,29 +551,29 @@ void XpLib::compositeToScreen() {
blitDirtyRects(g_dirtyRects, g_overlayCount);
- if (g_spriteOverlayEnabled & 0x80)
- blitDirtyRects(&g_cursorDirtyRect, 1);
+ if (g_renderFlags & RF_CURSOR_VISIBLE)
+ blitDirtyRects(&g_cursorRect, 1);
- if (g_spriteOverlayActive & 0x80)
+ if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
blitDirtyRects(&g_prevCursorRect, 1);
}
} else {
// No pixel changes - just cursor
- if (g_spriteOverlayEnabled & 0x80)
- blitDirtyRects(&g_cursorDirtyRect, 1);
+ if (g_renderFlags & RF_CURSOR_VISIBLE)
+ blitDirtyRects(&g_cursorRect, 1);
- if (g_spriteOverlayActive & 0x80)
+ if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
blitDirtyRects(&g_prevCursorRect, 1);
}
} else {
// DOUBLE-BUFFER MODE
- if (g_spriteOverlayEnabled & 0x18) {
- if ((g_spriteOverlayEnabled & 4) ||
+ if (g_renderFlags & (RF_FRONT_DIRTY | RF_BACK_DIRTY)) {
+ if ((g_renderFlags & RF_FULL_REDRAW) ||
g_prevDirtyValid > 0 || g_overlayCount > 0) {
// Full composite...
- compositeBlit(g_surfaces[0].mainPic.pixelData, g_surfaces[1].mainPic.pixelData,
+ compositeBlit(g_surfaces[stFront].mainPic.pixelData, g_surfaces[stBack].mainPic.pixelData,
g_vgaFramebuffer, g_virtualWidth, g_virtualWidth, g_virtualHeight);
g_overlayCount = 0;
g_prevDirtyCount = 0;
@@ -583,17 +586,17 @@ void XpLib::compositeToScreen() {
compositeDirtyRects(g_dirtyRects, g_overlayCount);
- if (g_spriteOverlayEnabled & 0x80)
- compositeDirtyRects(&g_cursorDirtyRect, 1);
+ if (g_renderFlags & RF_CURSOR_VISIBLE)
+ compositeDirtyRects(&g_cursorRect, 1);
- if (g_spriteOverlayActive & 0x80)
+ if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
compositeDirtyRects(&g_prevCursorRect, 1);
}
} else {
- if (g_spriteOverlayEnabled & 0x80)
- compositeDirtyRects(&g_cursorDirtyRect, 1);
+ if (g_renderFlags & RF_CURSOR_VISIBLE)
+ compositeDirtyRects(&g_cursorRect, 1);
- if (g_spriteOverlayActive & 0x80)
+ if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
compositeDirtyRects(&g_prevCursorRect, 1);
}
}
@@ -613,11 +616,11 @@ void XpLib::compositeToScreen() {
g_overlayCount = 0;
// Save cursor state for next frame...
- if (g_spriteOverlayEnabled & 0x80) {
- g_prevCursorRect = g_cursorDirtyRect;
+ if (g_renderFlags & RF_CURSOR_VISIBLE) {
+ g_prevCursorRect = g_cursorRect;
// Restore front surface under cursor...
- clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], cursorX, cursorY, nullptr);
+ clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, cursorX, cursorY, nullptr);
}
}
@@ -648,11 +651,11 @@ void XpLib::blitDirtyRects(Common::Rect *rects, int16 count) {
if (rect->right < 0) {
rect->right = -rect->right;
} else {
- int16 offset = rect->top * g_virtualWidth + rect->left;
- int16 w = rect->right - rect->left;
- int16 h = rect->bottom - rect->top;
+ uint32 offset = rect->top * g_virtualWidth + rect->left;
+ int32 w = rect->right - rect->left;
+ int32 h = rect->bottom - rect->top;
- blit(g_surfaces[0].mainPic.pixelData + offset, g_surfaces[0].mainPic.width,
+ blit(g_surfaces[stFront].mainPic.pixelData + offset, g_surfaces[stFront].mainPic.width,
g_vgaFramebuffer + offset, g_virtualWidth,
w, h);
}
@@ -666,13 +669,13 @@ void XpLib::compositeDirtyRects(Common::Rect *rects, int16 count) {
if (rect->right < 0) {
rect->right = -rect->right;
} else {
- int16 offset = rect->top * g_virtualWidth + rect->left;
- int16 w = rect->right - rect->left;
- int16 h = rect->bottom - rect->top;
+ uint32 offset = rect->top * g_virtualWidth + rect->left;
+ int32 w = rect->right - rect->left;
+ int32 h = rect->bottom - rect->top;
compositeBlit(
- g_surfaces[0].mainPic.pixelData + offset,
- g_surfaces[1].mainPic.pixelData + offset,
+ g_surfaces[stFront].mainPic.pixelData + offset,
+ g_surfaces[stBack].mainPic.pixelData + offset,
g_vgaFramebuffer + offset,
g_virtualWidth, w, h);
}
@@ -686,42 +689,41 @@ void XpLib::applyCursorPalette() {
void XpLib::prepareBackSurface() {
XPPicDesc *src;
- if (g_spriteOverlayEnabled & 1) {
- src = &g_surfaces[0].overlayPic;
+ if (g_renderFlags & RF_OVERLAY_ACTIVE) {
+ src = &g_surfaces[stFront].overlayPic;
} else {
- src = &g_surfaces[0].mainPic;
+ src = &g_surfaces[stFront].mainPic;
}
- clipAndBlit(src, &g_surfaces[1], 0, 0, nullptr);
+ clipAndBlit(src, &g_surfaces[stBack].mainPic, 0, 0, nullptr);
- markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+ markCursorPixels(g_surfaces[stBack].mainPic.pixelData, (uint32)g_surfaces[stBack].mainPic.width * (uint32)g_surfaces[stBack].mainPic.height);
- memcpy(g_vgaFramebuffer, g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+ memcpy(g_vgaFramebuffer, g_surfaces[stBack].mainPic.pixelData, (uint32)g_surfaces[stBack].mainPic.width * (uint32)g_surfaces[stBack].mainPic.height);
}
void XpLib::setTransparency(bool toggle) {
if (toggle) {
- g_spriteOverlayEnabled |= 2; // Enables double-buffer compositing
+ g_renderFlags |= RF_DOUBLE_BUFFER; // Enables double-buffer compositing
} else {
- g_spriteOverlayEnabled &= ~2;
+ g_renderFlags &= ~RF_DOUBLE_BUFFER;
}
}
void XpLib::fillDisplay(byte color, int16 page) {
-
XPSurface *surf = &g_surfaces[page];
uint32 size = (uint32)surf->mainPic.width * (uint32)surf->mainPic.height;
- // Back surface (page 1) pixels have bit 7 set for cursor palette mapping...
- byte fillVal = (page == 1) ? (color + 0x80) : color;
+ // Back surface pixels have bit 7 set for cursor palette mapping...
+ byte fillVal = (page == stBack) ? (color + 0x80) : color;
memset(surf->mainPic.pixelData, fillVal, size);
// Mark page as pixel-dirty...
- g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+ g_renderFlags |= (page != stFront) ? RF_BACK_DIRTY : RF_FRONT_DIRTY;
// Clear overlay flag, signal full redraw needed...
- g_spriteOverlayEnabled &= ~1;
+ g_renderFlags &= ~RF_OVERLAY_ACTIVE;
g_prevDirtyValid = 1;
}
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 4528c50253a..3dc0625d029 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -68,7 +68,7 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData) {
}
if (!node)
- return 0;
+ return etEmpty;
// Remove from queue...
unlinkEvent(node);
@@ -140,7 +140,7 @@ void XpLib::enqueueEvent(XPEvent *node) {
void XpLib::pumpMessages() {
Common::Event event;
- long timerData;
+ uint32 timerData;
bool mouseMoved = false;
// Drain any pending sound events...
@@ -149,6 +149,8 @@ void XpLib::pumpMessages() {
postEvent(etSound, timerData);
}
+ cycleColors();
+
// Check inactivity timeout...
if (g_inactivityDeadline != 0) {
uint32 now = _bolt->_system->getMillis();
@@ -350,7 +352,7 @@ void XpLib::postEvent(XPEventTypes type, uint32 data) {
return;
g_lastMouseEventData = data;
- if (g_eventQueueTail->type == etMouseMove) {
+ if (g_eventQueueTail && g_eventQueueTail->type == etMouseMove) {
g_eventQueueTail->payload = data;
return;
}
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index f478a0178df..386c05cabb9 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -31,7 +31,7 @@ void XpLib::fileError(const char *message) {
error(message);
}
-Common::File *XpLib::openFile(const char *fileName, short flags) {
+Common::File *XpLib::openFile(const char *fileName, int16 flags) {
// Read-write (mode 3) and write (mode 2) are unsupported and not needed by Cartoon Carnival...
assert(flags == 1);
@@ -76,6 +76,7 @@ void *XpLib::allocMem(uint32 size) {
if (!result)
error("XpLib::allocMem(): Not enough memory");
+ memset(result, 0, size);
return result;
}
@@ -83,6 +84,8 @@ void *XpLib::tryAllocMem(uint32 size) {
void *result = (void *)malloc(size);
if (!result)
warning("XpLib::tryAllocMem(): Couldn't allocate memory, returning nullptr");
+ else
+ memset(result, 0, size);
return result;
}
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index fd33eadd3ed..ba42e1a3cb4 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -26,48 +26,35 @@
namespace Bolt {
-void XpLib::getPalette(uint16 startIndex, uint16 count, byte *destBuf) {
- Common::StackLock lock(g_paletteMutex);
-
+void XpLib::getPalette(int16 startIndex, int16 count, byte *destBuf) {
memcpy(destBuf, &g_paletteBuffer[startIndex * 3], count * 3);
}
-void XpLib::setPalette(uint16 count, uint16 startIndex, byte *srcBuf) {
- Common::StackLock lock(g_paletteMutex);
-
+void XpLib::setPalette(int16 count, int16 startIndex, byte *srcBuf) {
if (startIndex == 0 || (startIndex == 128 && count != 1)) {
startIndex++;
srcBuf += 3;
count--;
}
- if (count == 0)
- return;
-
- memcpy(&g_paletteBuffer[startIndex * 3], srcBuf, count * 3);
-
- // Apply brightness shift into shifted buffer...
- const byte *src = &g_paletteBuffer[startIndex * 3];
- byte *dst = &g_shiftedPaletteBuffer[startIndex * 3];
+ if (count > 0) {
+ memcpy(&g_paletteBuffer[startIndex * 3], srcBuf, count * 3);
- for (uint16 i = 0; i < count * 3; i++)
- dst[i] = src[i] >> g_brightnessShift;
+ // Apply brightness shift into shifted buffer...
+ const byte *src = &g_paletteBuffer[startIndex * 3];
+ byte *dst = &g_shiftedPaletteBuffer[startIndex * 3];
- // Finally set the palette!
- g_system->getPaletteManager()->setPalette(&g_shiftedPaletteBuffer[startIndex * 3], startIndex, count);
-}
+ for (uint16 i = 0; i < count * 3; i++)
+ dst[i] = src[i] >> g_brightnessShift;
-void XpLib::cycleColorsCallback(void *refCon) {
- XpLib *xp = (XpLib *)refCon;
- xp->cycleColors();
+ // Finally set the palette!
+ g_system->getPaletteManager()->setPalette(&g_shiftedPaletteBuffer[startIndex * 3], startIndex, count);
+ }
}
bool XpLib::startCycle(XPCycleState *specs) {
- Common::StackLock lock(g_paletteMutex);
-
stopCycle();
- bool anyActive = false;
uint32 now = g_system->getMillis();
for (int16 i = 0; i < 4; i++) {
@@ -81,20 +68,12 @@ bool XpLib::startCycle(XPCycleState *specs) {
g_cycleStates[i].delay = specs[i].delay;
g_cycleStates[i].nextFire = now + specs[i].delay;
g_cycleStates[i].active = true;
- anyActive = true;
- }
-
- if (anyActive) {
- _bolt->getTimerManager()->installTimerProc(
- cycleColorsCallback, 50 * 1000, this, "BoltPalCycle");
}
return true;
}
void XpLib::cycleColors() {
- Common::StackLock lock(g_paletteMutex);
-
for (int i = 0; i < 4; i++) {
if (!g_cycleStates[i].active)
continue;
@@ -121,7 +100,6 @@ void XpLib::cycleColors() {
// Write back shifted by one (swap buffer starts 3 bytes earlier)...
setPalette(count, startIdx, g_cycleTempPalette);
-
} else if (startIdx > endIdx) {
int16 count = startIdx - endIdx + 1;
if (count > 19 || endIdx < 0 || startIdx > 255)
@@ -136,27 +114,20 @@ void XpLib::cycleColors() {
// Write back shifted by one (starts one entry later)...
setPalette(count, endIdx, &g_cycleTempPalette[3]);
}
+
+ g_system->updateScreen();
}
}
void XpLib::stopCycle() {
- Common::StackLock lock(g_paletteMutex);
-
- bool wasActive = false;
for (int16 i = 0; i < 4; i++) {
if (g_cycleStates[i].active) {
- wasActive = true;
g_cycleStates[i].active = false;
}
}
-
- if (wasActive)
- _bolt->getTimerManager()->removeTimerProc(cycleColorsCallback);
}
void XpLib::setScreenBrightness(uint8 percent) {
- Common::StackLock lock(g_paletteMutex);
-
if (percent >= 100) {
g_brightnessShift = 2; // Full brightness (VGA DAC is 6-bit)
} else if (percent >= 50) {
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index 2bb62117449..53e8c8d38cb 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -22,38 +22,209 @@
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
-namespace Bolt {
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "audio/mixer.h"
-bool XpLib::pollSound(void *outData) {
- return true;
-}
+namespace Bolt {
bool XpLib::initSound() {
+ _audioStream = Audio::makeQueuingAudioStream(22050, false);
+ _nextSoundDeadlineMs = 0;
+ _sndPlayState = 0;
+ _sndQueued = 0;
+ _sndPaused = false;
+ _sndCompletedCount = 0;
+ _sndSampleRate = 22050;
+
+ g_system->getMixer()->playStream(
+ Audio::Mixer::kSFXSoundType,
+ &_soundHandle,
+ _audioStream,
+ -1, 255, 0,
+ DisposeAfterUse::NO);
+
return true;
}
-void XpLib::waveCb() {
+bool XpLib::pollSound(uint32 *outData) {
+ *outData = 0;
-}
+ if (_sndQueued == 0 || _sndPaused)
+ return false;
-void XpLib::shutdownSound() {
+ if (!_audioStream)
+ return false;
+
+ bool ready = false;
+
+ int activeStreams = _audioStream->numQueuedStreams();
+ int completed = _sndQueued - activeStreams;
+ if (completed > (int)_sndCompletedCount)
+ _sndCompletedCount = completed;
+
+ uint32 now = _bolt->_system->getMillis();
+
+ debug(5, "pollSound: queued=%d active=%d completed=%d state=%d deadline=%u now=%u",
+ _sndQueued, activeStreams, _sndCompletedCount, _sndPlayState,
+ _sndNextDeadline, now);
+ if (_sndCompletedCount > 0) {
+ if (_sndPlayState == 0) {
+ _sndPlayState = 1;
+ } else if (_sndPlayState == 2) {
+ _sndPlayState = 0;
+ ready = true;
+ }
+ }
+
+ if (!ready) {
+ if (now >= _sndNextDeadline) {
+ if (_sndPlayState == 0) {
+ _sndPlayState = 2;
+ } else if (_sndPlayState == 1) {
+ _sndPlayState = 0;
+ ready = true;
+ }
+ }
+ }
+
+ if (!ready)
+ return false;
+
+ *outData = 1; // The original puts the elapsed buffer data in here...
+
+ // Buffer completed!
+ _sndCompletedCount--;
+ _sndQueued--;
+
+ if (!_durationQueue.empty())
+ _durationQueue.pop();
+
+ if (_sndQueued == 0)
+ return true;
+
+ // Deadline adjustment for next buffer...
+ uint32 nextDurationMs = 0;
+ if (!_durationQueue.empty())
+ nextDurationMs = _durationQueue.front();
+
+ int32 elapsed = (int32)(_sndNextDeadline - _sndBufferQueueTime);
+
+ if (elapsed > 0) {
+ elapsed /= 50;
+ int32 maxAdj = (int32)nextDurationMs / 10;
+ if (maxAdj < elapsed)
+ elapsed = maxAdj;
+ _sndNextDeadline += (nextDurationMs - elapsed);
+ } else {
+ _sndNextDeadline = _sndBufferQueueTime + nextDurationMs;
+ }
+
+ _sndBufferQueueTime = _bolt->_system->getMillis();
+
+ return true;
}
bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
- return false;
+ if (!_audioStream)
+ return false;
+
+ if (sampleRate != 22050)
+ return false;
+
+ // Drain completed buffers if full...
+ int retries = 0;
+ while (_audioStream->numQueuedStreams() >= 50) {
+ uint32 eventData;
+ if (pollSound(&eventData))
+ postEvent(etSound, eventData);
+
+ _bolt->_system->delayMillis(1);
+ if (++retries > 5000) {
+ warning("XpLib::playSound(): drain timeout");
+ return false;
+ }
+ }
+
+ byte *buf = (byte *)malloc(size);
+ if (!buf)
+ return false;
+
+ memcpy(buf, data, size);
+ _audioStream->queueBuffer(buf, size, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+
+ uint32 durationMs = (size * 1000) / (uint32)sampleRate;
+
+ // First buffer: set initial deadline...
+ if (_sndQueued == 0) {
+ uint32 now = _bolt->_system->getMillis();
+ _sndNextDeadline = now + durationMs;
+ _sndBufferQueueTime = now;
+
+ if (_sndPaused)
+ _pauseTimeMs = now;
+ }
+
+ _sndQueued++;
+ _durationQueue.push(durationMs);
+
+ return true;
}
bool XpLib::pauseSound() {
- return false;
+ if (_sndPaused)
+ return true;
+
+ _sndPaused = true;
+ _pauseTimeMs = _bolt->_system->getMillis();
+ g_system->getMixer()->pauseHandle(_soundHandle, true);
+ return true;
}
bool XpLib::resumeSound() {
- return false;
+ if (_sndQueued > 0) {
+ uint32 now = _bolt->_system->getMillis();
+ uint32 pauseDuration = now - _pauseTimeMs;
+ _sndNextDeadline += pauseDuration;
+ }
+
+ _sndPaused = false;
+ g_system->getMixer()->pauseHandle(_soundHandle, false);
+ return true;
}
bool XpLib::stopSound() {
- return false;
+ g_system->getMixer()->stopHandle(_soundHandle);
+
+ _sndQueued = 0;
+ _sndPaused = false;
+ _sndCompletedCount = 0;
+ _sndPlayState = 0;
+ _nextSoundDeadlineMs = 0;
+ _pauseTimeMs = 0;
+
+ while (!_durationQueue.empty())
+ _durationQueue.pop();
+
+ uint32 dummy;
+ while (getEvent(etSound, &dummy) == etSound);
+
+ // Recreate stream for next use...
+ _audioStream = Audio::makeQueuingAudioStream(22050, false);
+ g_system->getMixer()->playStream(
+ Audio::Mixer::kSFXSoundType,
+ &_soundHandle,
+ _audioStream,
+ -1, 255, 0,
+ DisposeAfterUse::NO);
+
+ return true;
+}
+
+void XpLib::shutdownSound() {
+ g_system->getMixer()->stopHandle(_soundHandle);
+ _audioStream = nullptr;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
index 6a4057dab61..fd3abccb0b2 100644
--- a/engines/bolt/xplib/xplib.cpp
+++ b/engines/bolt/xplib/xplib.cpp
@@ -39,8 +39,6 @@ XpLib::XpLib(BoltEngine *bolt) {
}
XpLib::~XpLib() {
- terminate();
-
delete _screen;
}
@@ -50,6 +48,30 @@ bool XpLib::initialize() {
g_xpInitialized = true;
+ //g_videoSurface = new Graphics::Surface();
+
+ if (_bolt->g_extendedViewport) {
+ g_rowDirtyFlags = (byte *)malloc(EXTENDED_SCREEN_HEIGHT);
+ assert(g_rowDirtyFlags);
+ memset(g_rowDirtyFlags, 0, EXTENDED_SCREEN_HEIGHT);
+
+ g_vgaFramebuffer = (byte *)malloc(EXTENDED_SCREEN_WIDTH * EXTENDED_SCREEN_HEIGHT);
+ assert(g_vgaFramebuffer);
+ memset(g_vgaFramebuffer, 0, EXTENDED_SCREEN_WIDTH * EXTENDED_SCREEN_HEIGHT);
+
+ //g_videoSurface->init(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT, EXTENDED_SCREEN_WIDTH, g_vgaFramebuffer, Graphics::PixelFormat::createFormatCLUT8());
+ } else {
+ g_rowDirtyFlags = (byte *)malloc(SCREEN_HEIGHT);
+ assert(g_rowDirtyFlags);
+ memset(g_rowDirtyFlags, 0, SCREEN_HEIGHT);
+
+ g_vgaFramebuffer = (byte *)malloc(SCREEN_WIDTH * SCREEN_HEIGHT);
+ assert(g_vgaFramebuffer);
+ memset(g_vgaFramebuffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT);
+
+ //g_videoSurface->init(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, g_vgaFramebuffer, Graphics::PixelFormat::createFormatCLUT8());
+ }
+
if (!initTimer()) {
terminate();
return false;
@@ -85,6 +107,14 @@ void XpLib::terminate() {
shutdownEvents();
shutdownTimer();
+ free(g_vgaFramebuffer);
+ g_vgaFramebuffer = nullptr;
+
+ free(g_rowDirtyFlags);
+ g_rowDirtyFlags = nullptr;
+
+ //delete g_videoSurface;
+
g_xpInitialized = false;
}
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index ff9057e379d..7cd1672596b 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -24,13 +24,28 @@
#include "bolt/bolt.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
#include "common/events.h"
#include "common/file.h"
#include "common/mutex.h"
+
#include "graphics/paletteman.h"
namespace Bolt {
+// RENDER FLAGS
+
+#define RF_OVERLAY_ACTIVE 0x01 // RLE overlay source stored on front surface
+#define RF_DOUBLE_BUFFER 0x02 // Double-buffer compositing enabled
+#define RF_FULL_REDRAW 0x04 // Full-screen overwrite, skip dirty tracking
+#define RF_FRONT_DIRTY 0x08 // Front surface pixels changed
+#define RF_BACK_DIRTY 0x10 // Back surface pixels changed
+#define RF_FRONT_PAL_DIRTY 0x20 // Front surface palette changed
+#define RF_BACK_PAL_DIRTY 0x40 // Back surface palette changed
+#define RF_CURSOR_VISIBLE 0x80 // Cursor should be drawn this frame
+
struct DisplaySpecs;
class BoltEngine;
@@ -70,6 +85,11 @@ typedef struct XPPicDesc {
}
} XPPicDesc;
+enum XPSurfaceType : int {
+ stFront = 0,
+ stBack
+};
+
typedef struct XPSurface {
XPPicDesc mainPic;
XPPicDesc overlayPic;
@@ -90,7 +110,8 @@ enum XPEventTypes : int16 {
etMouseUp = 4,
etJoystick = 5,
etSound = 6,
- etInactivity = 7
+ etInactivity = 7,
+ etTrigger = 8
};
enum XPEventKeyStates : int16 {
@@ -130,6 +151,8 @@ typedef struct XPTimer {
} XPTimer;
class XpLib {
+friend class BoltEngine;
+
public:
XpLib(BoltEngine *bolt);
~XpLib();
@@ -142,8 +165,8 @@ public:
void maskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
// Palette
- void getPalette(uint16 startIndex, uint16 count, byte *destBuf);
- void setPalette(uint16 count, uint16 startIndex, byte *srcBuf);
+ void getPalette(int16 startIndex, int16 count, byte *destBuf);
+ void setPalette(int16 count, int16 startIndex, byte *srcBuf);
bool startCycle(XPCycleState *specs);
void cycleColors();
void stopCycle();
@@ -168,7 +191,7 @@ public:
void disableController();
// Display
- bool chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs);
+ bool setDisplaySpec(int *outMode, DisplaySpecs *spec);
void setCoordSpec(int16 x, int16 y, int16 width, int16 height);
void displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page);
void setFrameRate(int16 fps);
@@ -181,7 +204,7 @@ public:
void randomize();
// File
- Common::File *openFile(const char *fileName, short flags);
+ Common::File *openFile(const char *fileName, int16 flags);
void closeFile(Common::File *handle);
bool readFile(Common::File *handle, void *buffer, uint32 *size);
bool setFilePos(Common::File *handle, int32 offset, int32 origin);
@@ -221,9 +244,6 @@ protected:
byte g_cycleTempPalette[3 * 20];
uint32 g_cycleTimerIds[4];
int16 g_brightnessShift = 0;
- Common::Mutex g_paletteMutex;
-
- static void cycleColorsCallback(void *refConf);
// Cursor
bool initCursor();
@@ -231,9 +251,6 @@ protected:
void readJoystick(int16 *outX, int16 *outY);
byte g_cursorBuffer[16 * 16];
- int16 g_cursorWidth = 0;
- int16 g_cursorHeight = 0;
- int16 g_cursorScale = 0;
int16 g_cursorHotspotX = 0;
int16 g_cursorHotspotY = 0;
int16 g_lastCursorX = 0;
@@ -289,7 +306,7 @@ protected:
void virtualToScreen(int16 *x, int16 *y);
void screenToVirtual(int16 *x, int16 *y);
void dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
- bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip);
+ bool clipAndBlit(XPPicDesc *src, XPPicDesc *dest, int16 x, int16 y, Common::Rect *outClip);
void addDirtyRect(Common::Rect *rect);
void waitForFrameRate();
void handlePaletteTransitions();
@@ -309,8 +326,8 @@ protected:
int16 g_virtualWidth = 0;
int16 g_virtualHeight = 0;
int16 g_currentDisplayPage = 0;
- int16 g_spriteOverlayActive = 0;
- int16 g_spriteOverlayEnabled = 0;
+ int16 g_prevRenderFlags = 0;
+ int16 g_renderFlags = 0;
int16 g_frameRateFPS = 0;
int16 g_overlayCount = 0;
int16 g_prevDirtyCount = 0;
@@ -323,14 +340,15 @@ protected:
Common::Rect g_dirtyRects[30];
Common::Rect g_prevDirtyRects[30];
- Common::Rect g_cursorDirtyRect;
+ Common::Rect g_cursorRect;
Common::Rect g_prevCursorRect;
- Common::Rect g_cursorClipRect2;
- Common::Rect g_prevCursorRect2;
+ Common::Rect g_overlayCursorRect;
+ Common::Rect g_prevOverlayCursorRect;
- byte g_vgaFramebuffer[320 * 200];
+ byte *g_vgaFramebuffer = nullptr;
+ //Graphics::Surface *g_videoSurface = nullptr;
+ byte *g_rowDirtyFlags = nullptr;
byte g_cursorBackgroundSaveBuffer[16 * 16];
- byte g_rowDirtyFlags[200];
XPPicDesc g_cursorBackgroundSave;
XPPicDesc g_cursorSprite;
@@ -339,10 +357,23 @@ protected:
void fileError(const char *message);
// Sound
- bool pollSound(void *outData);
+ bool pollSound(uint32 *outData);
bool initSound();
void shutdownSound();
+ Audio::QueuingAudioStream *_audioStream = nullptr;
+ Audio::SoundHandle _soundHandle;
+ Common::Queue<uint32> _durationQueue;
+ uint32 _nextSoundDeadlineMs = 0;
+ uint32 _pauseTimeMs = 0;
+ int16 _sndPlayState = 0;
+ int16 _sndQueued = 0;
+ int _sndCompletedCount = 0;
+ bool _sndPaused = false;
+ int16 _sndSampleRate = 22050;
+ uint32 _sndNextDeadline = 0;
+ uint32 _sndBufferQueueTime = 0;
+
// Timer
bool initTimer();
void shutdownTimer();
Commit: efb9fe545582c88a940eca530a08f3f7ccc3cab5
https://github.com/scummvm/scummvm/commit/efb9fe545582c88a940eca530a08f3f7ccc3cab5
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Fix screensaver dimming routine
Changed paths:
engines/bolt/xplib/palette.cpp
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index ba42e1a3cb4..abaaed879d9 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -129,19 +129,21 @@ void XpLib::stopCycle() {
void XpLib::setScreenBrightness(uint8 percent) {
if (percent >= 100) {
- g_brightnessShift = 2; // Full brightness (VGA DAC is 6-bit)
+ g_brightnessShift = 0; // Full brightness
} else if (percent >= 50) {
- g_brightnessShift = 3; // 50%
+ g_brightnessShift = 1; // 50%
} else if (percent >= 25) {
- g_brightnessShift = 4; // 25%
+ g_brightnessShift = 2; // 25%
} else if (percent >= 12) {
- g_brightnessShift = 5; // 12%
+ g_brightnessShift = 3; // 12%
} else {
- g_brightnessShift = 6; // Near black
+ g_brightnessShift = 4; // Near black
}
// Re-apply entire palette with new brightness...
setPalette(256, 0, g_paletteBuffer);
+
+ g_system->updateScreen();
}
} // End of namespace Bolt
Commit: ca9d735bd4160f129531b3834a9218ad482968c5
https://github.com/scummvm/scummvm/commit/ca9d735bd4160f129531b3834a9218ad482968c5
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Fix palette setup when playing a video over a booth
Changed paths:
engines/bolt/av.cpp
engines/bolt/barker.cpp
engines/bolt/booth.cpp
diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
index c5233c5b443..ff45768412b 100644
--- a/engines/bolt/av.cpp
+++ b/engines/bolt/av.cpp
@@ -114,7 +114,7 @@ void BoltEngine::processPacket(RTFPacket *packet) {
// Skip this frame if: low framerate, only 1 behind, or haven't accumulated enough...
if (frameRate <= 10 || packet->skipCount > 1 || g_avFrameAccum < g_avSkipLevel) {
g_avFrameAccum += packet->duration;
- warning("BoltEngine::processPacket() WANTS TO RETURN BUT IT WILL SKIP FRAMES");
+ debug(4, "BoltEngine::processPacket(): Skipping frame...");
return;
}
diff --git a/engines/bolt/barker.cpp b/engines/bolt/barker.cpp
index 91f663a9979..2fa68c1f179 100644
--- a/engines/bolt/barker.cpp
+++ b/engines/bolt/barker.cpp
@@ -94,7 +94,7 @@ int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
}
bool BoltEngine::checkError() {
- return false;
+ return g_curErrorCode != 0;
}
} // End of namespace Bolt
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index acfe6f2457c..6ab9fd96232 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -35,7 +35,7 @@ void BoltEngine::startCycle(byte *cycleResource) {
void BoltEngine::displayBooth(int16 page) {
_xp->setTransparency(page);
_xp->displayPic(&g_boothLetterSprite, g_displayX, g_displayY, page);
- displayColors(g_boothPalCycleData, 0, page);
+ displayColors(g_boothPalCycleData, page, 0);
if (page != stFront)
_xp->fillDisplay(0, stFront);
@@ -775,10 +775,10 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
if (playAV(g_rtfHandle, 3, g_displayWidth, g_displayHeight, g_displayX, g_displayY) == 0) {
fadeToBlack(1);
- _xp->setTransparency(0);
+ _xp->setTransparency(false);
displayPic(getBOLTMember(g_boothsBoltLib, g_displayMode ? 0x1802 : 0x1801), g_displayX, g_displayY, 0);
_xp->updateDisplay();
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), 0, 0);
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), stFront, 0);
_xp->updateDisplay();
uint32 timer = _xp->startTimer(5000);
Commit: 8f27afd84e6fdd6092d4c142d99f51c928c49962
https://github.com/scummvm/scummvm/commit/8f27afd84e6fdd6092d4c142d99f51c928c49962
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement Fred's minigame
Also add support for italian demo
Changed paths:
engines/bolt/anim.cpp
engines/bolt/bolt.cpp
engines/bolt/bolt.h
engines/bolt/booth.cpp
engines/bolt/booths/fred.cpp
engines/bolt/resource.cpp
engines/bolt/xplib/display.cpp
engines/bolt/xplib/events.cpp
engines/bolt/xplib/random.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/anim.cpp b/engines/bolt/anim.cpp
index 9c869224c5e..7d9b90fc830 100644
--- a/engines/bolt/anim.cpp
+++ b/engines/bolt/anim.cpp
@@ -26,6 +26,7 @@ namespace Bolt {
bool BoltEngine::startAnimation(RTFResource *rtf, int16 animIndex) {
if (!initAnim(rtf, animIndex)) {
cleanUpAnim();
+ debug("BoltEngine::startAnimation(): Failed to start animation %d", animIndex);
return false;
}
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 4baa8fc5b72..76a04e3e052 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -70,6 +70,9 @@ Common::Error BoltEngine::run() {
initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
}
+ if ((getFeatures() & ADGF_DEMO) != 0)
+ g_isDemo = true;
+
// Set the engine's debugger console
setDebugger(new Console());
@@ -107,7 +110,7 @@ void BoltEngine::boltMain() {
if (allocResourceIndex()) {
g_boothsBoltLib = nullptr;
- if (openBOLTLib(&g_boothsBoltLib, &g_resourceDefaultCallbacks, assetPath("booths.blt"))) {
+ if (openBOLTLib(&g_boothsBoltLib, &g_boothsBoltCallbacks, assetPath("booths.blt"))) {
int16 chosenSpecId = g_extendedViewport ? 0 : 1;
if (_xp->setDisplaySpec(&g_displayMode, &g_displaySpecs[chosenSpecId])) {
@@ -128,29 +131,33 @@ void BoltEngine::boltMain() {
}
if (g_rtfHandle) {
- playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ int16 boothGroupBase = g_isDemo ? 0x0E00 : 0x1700;
+
+ if (!g_isDemo) {
+ playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ }
- boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
+ boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
_xp->setTransparency(false);
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stFront, 0);
+ displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stFront, 0);
displayPic(boothSprite, g_displayX, g_displayY, stFront);
_xp->updateDisplay();
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stBack, 0);
+ displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stBack, 0);
displayPic(boothSprite, g_displayX, g_displayY, stBack);
- playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ playAV(g_rtfHandle, g_isDemo ? 0 : 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
- freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
+ freeBOLTGroup(g_boothsBoltLib, boothGroupBase, 1);
if (getBOLTGroup(g_boothsBoltLib, 0, 1)) {
setCursorPict(memberAddr(g_boothsBoltLib, 0));
if (initVRam(1500)) {
- barkerTable = createBarker(2, 17);
+ barkerTable = createBarker(2, g_isDemo ? 19 : 17);
if (barkerTable) {
// Register booth handlers...
registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
@@ -168,6 +175,11 @@ void BoltEngine::boltMain() {
registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
+ if (g_isDemo) {
+ registerSideShow(barkerTable, &BoltEngine::displayDemoPict, 17);
+ registerSideShow(barkerTable, &BoltEngine::endDemo, 18);
+ }
+
g_lettersWon = 0;
_xp->setScreenSaverTimer(1800);
@@ -215,4 +227,25 @@ void BoltEngine::setCursorPict(byte *sprite) {
_xp->setCursorImage(cursorBitmap, 7, 7);
}
+int16 BoltEngine::displayDemoPict(int16 prevBooth) {
+ uint32 eventData;
+
+ while (_xp->getEvent(etMouseDown, &eventData) == etMouseDown);
+
+ while (_xp->getEvent(etTimer, &eventData) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+
+ int16 memberId = (g_displayMode != 0) ? 0x1002 : 0x1001;
+ displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x1000), stFront, 0);
+
+ _xp->updateDisplay();
+
+ while (_xp->getEvent(etMouseDown, &eventData) != etMouseDown);
+
+ return prevBooth;
+}
+
} // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index c24dcb6fd5c..c0f62fb8284 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -77,12 +77,12 @@ typedef struct BOLTHeader {
typedef void (*BOLTCallback)(void);
typedef struct BOLTCallbacks {
- BOLTCallback *typeLoadCallbacks; // +0x00: type-based post-load fixup (25 entries)
- BOLTCallback *typeFreeCallbacks; // +0x04: type-based post-free cleanup (25 entries)
- BOLTCallback *memberLoadCallbacks; // +0x08: per-member pre-load callbacks
- BOLTCallback *memberFreeCallbacks; // +0x0C: per-member pre-free callbacks
- BOLTCallback *groupLoadCallbacks; // +0x10: per-group load callbacks
- BOLTCallback *groupFreeCallbacks; // +0x14: per-group free callbacks
+ BOLTCallback *typeLoadCallbacks;
+ BOLTCallback *typeFreeCallbacks;
+ BOLTCallback *memberLoadCallbacks;
+ BOLTCallback *memberFreeCallbacks;
+ BOLTCallback *groupLoadCallbacks;
+ BOLTCallback *groupFreeCallbacks;
BOLTCallbacks() {
typeLoadCallbacks = nullptr;
@@ -95,15 +95,14 @@ typedef struct BOLTCallbacks {
} BOLTCallbacks;
typedef struct BOLTMemberEntry {
- byte flags; // +0x00: bit 3 = uncompressed
- byte preLoadCbIndex; // +0x01: index into lib->userData1 callback table
- byte preFreeCbIndex; // +0x02: index into lib->userData2 callback table
- byte typeCbIndex; // +0x03: index into lib->loadCallbacks (post-load)
- // and lib->freeCallbacks (post-free)
- uint32 decompSize; // +0x04: decompressed data size (big-endian in file)
- uint32 fileOffset; // +0x08: file offset of compressed/raw data (big-endian in file)
+ byte flags;
+ byte preLoadCbIndex;
+ byte preFreeCbIndex;
+ byte typeCbIndex;
+ uint32 decompSize;
+ uint32 fileOffset;
uint32 dataPtrPlaceholder;
- byte *dataPtr; // +0x0C: pointer to loaded data (NULL if not loaded)
+ byte *dataPtr;
BOLTMemberEntry() {
flags = 0;
@@ -115,16 +114,16 @@ typedef struct BOLTMemberEntry {
dataPtrPlaceholder = 0;
dataPtr = nullptr;
}
-} BOLTMemberEntry; // 0x10 = 16 bytes
+} BOLTMemberEntry;
typedef struct BOLTGroupEntry {
- byte flags; // +0x00: unknown flags
- byte loadCbIndex; // +0x01: group load callback index (into lib+0x18)
- byte freeCbIndex; // +0x02: group free callback index (into lib+0x1C)
- byte memberCount; // +0x03: number of members (0 = 256)
- uint32 memberDirOffset; // +0x04: file offset to member directory (big-endian)
- uint32 memberDataOffset; // +0x08: file offset to member data (big-endian)
- uint32 groupDataPtrPlaceholder; // +0x0C: pointer to loaded group data block (NULL if not loaded)
+ byte flags;
+ byte loadCbIndex;
+ byte freeCbIndex;
+ byte memberCount;
+ uint32 memberDirOffset;
+ uint32 memberDataOffset;
+ uint32 groupDataPtrPlaceholder;
byte *memberData;
BOLTMemberEntry *members;
@@ -153,14 +152,14 @@ typedef struct BOLTGroupEntry {
members = new BOLTMemberEntry[actualNumber];
}
-} BOLTGroupEntry; // 0x10 = 16 bytes
+} BOLTGroupEntry;
typedef struct BOLTLib {
- int16 refCount; // +0x00
- int16 groupCount; // +0x02 (0 = 256)
- Common::File *fileHandle; // +0x04
+ int16 refCount;
+ int16 groupCount;
+ Common::File *fileHandle;
BOLTCallbacks callbacks;
- BOLTGroupEntry *groups; // +0x20
+ BOLTGroupEntry *groups;
BOLTLib(int inGroupCount) {
refCount = 0;
@@ -201,16 +200,15 @@ typedef struct RTFResource {
struct RTFPacket;
typedef struct RTFPacket {
- uint32 tag; // +0x00: chunk tag (e.g. MKTAG('A','2','2','8'))
- uint32 allocSize; // +0x04: total allocated size (including header)
- uint32 dataSize; // +0x08: raw data size
- byte *dataPtr; // +0x0C: pointer to data (= this + 0x1C)
- int16 skipCount; // +0x10: how late this frame is
- int16 frameRate; // +0x12: frame rate reference (or 50 if no sound)
- int16 duration; // +0x14: sound duration in hundredths of a second
- int16 timestamp; // +0x16: cumulative timeline position
- RTFPacket *next; // +0x18: next packet in linked list
- // +0x1C: data begins here
+ uint32 tag;
+ uint32 allocSize;
+ uint32 dataSize;
+ byte *dataPtr;
+ int16 skipCount;
+ int16 frameRate;
+ int16 duration;
+ int16 timestamp;
+ RTFPacket *next;
byte *ringBufPtr;
RTFPacket() {
@@ -225,7 +223,51 @@ typedef struct RTFPacket {
next = nullptr;
ringBufPtr = nullptr;
}
-} RTFPacket; // 0x1C header = 28 bytes
+} RTFPacket;
+
+// FRED GAME
+
+typedef struct FredSoundInfo {
+ byte *data;
+ uint32 size;
+
+ FredSoundInfo() {
+ data = nullptr;
+ size = 0;
+ }
+} FredSoundInfo;
+
+typedef struct FredEntityState {
+ uint16 flags;
+ int16 frameCountdown;
+ int16 animMode;
+ int16 frameIndex;
+ byte *animTable;
+ int16 direction;
+ int32 xPos;
+ int32 yPos;
+ int32 prevXPos;
+ int32 prevYPos;
+ int16 speed;
+ byte *pathTable;
+ int16 pathIndex;
+
+ FredEntityState() {
+ flags = 0;
+ frameCountdown = 0;
+ animMode = 0;
+ frameIndex = 0;
+ animTable = nullptr;
+ direction = 0;
+ xPos = 0;
+ yPos = 0;
+ prevXPos = 0;
+ prevYPos = 0;
+ speed = 0;
+ pathTable = nullptr;
+ pathIndex = 0;
+ }
+} FredEntityState;
class BoltEngine : public Engine {
friend class XpLib;
@@ -256,17 +298,14 @@ public:
}
bool hasFeature(EngineFeature f) const override {
- return
- (f == kSupportsLoadingDuringRuntime) ||
- (f == kSupportsSavingDuringRuntime) ||
- (f == kSupportsReturnToLauncher);
+ return (f == kSupportsReturnToLauncher);
};
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
- return true;
+ return false;
}
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
- return true;
+ return false;
}
/**
@@ -292,10 +331,12 @@ protected:
XpLib *_xp = nullptr;
bool g_extendedViewport = false;
+ bool g_isDemo = false;
// xpMain
void boltMain();
void setCursorPict(byte *sprite);
+ int16 displayDemoPict(int16 prevBooth);
// Booth
void startCycle(byte *cycleResource);
@@ -310,7 +351,7 @@ protected:
int16 mainEntrance(int16 prevBooth);
bool loadBooth(int16 boothId);
void unloadBooth();
- int16 openBooth(int16 sceneId);
+ int16 openBooth(int16 boothId);
void closeBooth();
void playTour();
void finishPlayingHelp(int16 activeHotspot);
@@ -325,12 +366,13 @@ protected:
void loadColors();
void shiftColorMap(byte *colorMap, int16 delta);
void playBoothAV();
- void mapIdleAnimation();
- void boothIdleAnimation();
- void screensaverStep();
+ void mainEntranceHelpBlink();
+ void boothHelpBlink();
+ void tourPaletteCycleStep();
void fadeToBlack(int16 steps);
void flushInput();
int16 winALetter(int16 prevBooth);
+ int16 endDemo(int16 prevBooth);
int16 g_lettersWon = 0;
bool g_allLettersWonFlag = false;
@@ -354,27 +396,34 @@ protected:
byte *g_boothPalCycleData = nullptr;
XPPicDesc g_boothLetterSprite;
bool g_needInitCursorPos = true;
- byte *g_boothExitLeft = nullptr;
- byte *g_boothExitRight = nullptr;
+ byte *g_boothVisitSignOn = nullptr;
+ byte *g_boothVisitSignOff = nullptr;
byte g_leftDoorNavTable[3] = {3, 2, 4};
byte g_rightDoorNavTable[3] = {1, 0, 5};
int16 g_cursorX = 0;
int16 g_cursorY = 0;
int16 g_hoveredHotspot = 0;
- uint32 g_keyTimer = 0;
+ uint32 g_helpTimer = 0;
int16 g_keyReleased = 0;
int16 g_keyLeft = 0;
int16 g_keyRight = 0;
int16 g_keyUp = 0;
int16 g_keyDown = 0;
- int16 g_keyHelp = 0;
+ int16 g_helpFlag = 0;
int16 g_keyEnter = 0;
- int16 g_screensaverStep = 0;
+ int16 g_tourStep = 0;
int16 g_helpPlaying = 0;
int16 g_helpIsIdle = 0;
- int16 g_idleHelpAvailable = 0;
+ int16 g_idleHelpAudioAvailable = 1;
+
+ int16 g_huckWins = 0;
+ int16 g_fredWins = 0;
+ int16 g_scoobyWins = 0;
+ int16 g_yogiWins = 0;
+ int16 g_georgeWins = 0;
+ int16 g_topCatWins = 0;
// Barker
BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
@@ -432,7 +481,7 @@ protected:
void swapAllLongs();
BOLTLib *g_boothsBoltLib = nullptr;
- BOLTCallbacks g_resourceDefaultCallbacks;
+ BOLTCallbacks g_boothsBoltCallbacks;
static BOLTCallback g_defaultTypeLoadCallbacks[25];
static BOLTCallback g_defaultTypeFreeCallbacks[25];
@@ -566,22 +615,95 @@ protected:
// --- MINIGAMES ---
- // Fred
- int16 fredGame(int16 prevBooth) { return 0; }
-
- // George
+ // --- FRED ---
+ int16 fredGame(int16 prevBooth);
+ bool initFred();
+ void cleanUpFred();
+ bool initFredLevel(int16 levelGroup, int16 palGroup);
+ void termFredLevel(int16 levelGroup, int16 palGroup);
+ void swapFredAnimEntry();
+ void swapFredAnimDesc();
+ void swapFredLevelDesc();
+ int16 playFred();
+ int16 helpFred();
+ void hiliteFredHelpObject(byte *entry, int16 highlight);
+ void helpAnimStep();
+ bool spawnBalloon();
+ int16 calcBalloonSpawnDelay();
+ int16 selectBalloonRow();
+ void setFredAnimMode(FredEntityState *state, int16 mode);
+ void renderFredScene();
+ void getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
+ void playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop);
+ void updateFredSound();
+
+ static void resolveAllRefsCb();
+ static void swapFredAnimEntryCb();
+ static void swapFredAnimDescCb();
+ static void swapFredLevelDescCb();
+
+ BOLTLib *g_fredBoltLib = nullptr;
+ BOLTCallbacks g_fredBoltCallbacks;
+
+ static BOLTCallback g_fredTypeLoadCallbacks[28];
+ static BOLTCallback g_fredTypeFreeCallbacks[28];
+
+ byte *g_fredLevelPtr = nullptr;
+ byte *g_fredBackground = nullptr;
+ byte *g_fredBalloonString = nullptr;
+ byte *g_fredPalette = nullptr;
+ byte *g_fredFacingLeftRect = nullptr;
+ byte *g_fredFacingRightRect = nullptr;
+ byte *g_fredTurningRect = nullptr;
+ byte *g_fredBalloonRect = nullptr;
+ byte *g_fredHelpEntries = nullptr;
+ byte *g_fredPlayButton = nullptr;
+ byte *g_fredSprites[10] = { nullptr };
+ FredEntityState **g_fredEntitiesTable = nullptr;
+
+ byte *g_fredRowBounds = nullptr;
+ byte *g_fredShuffleTable = nullptr;
+ byte *g_fredCycleRaw = nullptr;
+ byte *g_fredBalloonSprite = nullptr;
+ byte *g_fredPathMatrix = nullptr;
+ byte *g_fredCurrentHelpObject = nullptr;
+ byte *g_fredHoveredEntry = nullptr;
+
+ FredSoundInfo g_fredSounds[4];
+
+ FredSoundInfo *g_fredCurrentSound = nullptr;
+ FredSoundInfo *g_fredLoopSound = nullptr;
+ FredSoundInfo *g_fredPendingOneShot = nullptr;
+ FredSoundInfo *g_fredPendingLoop = nullptr;
+
+ FredEntityState g_fredSprite;
+
+ int16 g_fredSaveData[3] = { 0, 0, 0 };
+ int16 g_fredLevelIndex = 0;
+
+ int16 g_fredBalloonSpawnDelay = 0;
+ int16 g_fredBalloonSearchIdx = 0;
+ uint32 g_fredTimer = 0;
+ int16 g_fredHelpStep = -1;
+ int16 g_fredShowHelp = 1;
+
+ XPCycleState g_fredCycleSpecs[4];
+
+ const char *g_fredSaveFile = "FredBC";
+
+ // --- GEORGE ---
int16 georgeGame(int16 prevBooth) { return 0; }
- // Huck
+ // --- HUCK ---
int16 huckGame(int16 prevBooth) { return 0; }
- // Scooby
+ // --- SCOOBY ---
int16 scoobyGame(int16 prevBooth) { return 0; }
- // TopCat
+ // --- TOPCAT ---
int16 topCatGame(int16 prevBooth) { return 0; }
- // Yogi
+ // --- YOGI ---
int16 yogiGame(int16 prevBooth) { return 0; }
};
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index 6ab9fd96232..a8f26fd9ef0 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -65,7 +65,7 @@ int16 BoltEngine::hucksBooth(int16 prevBooth) {
case 2:
return 10; // default (play game)
case 3:
- return 10; // play game
+ return g_isDemo ? 17 : 10; // play game
default:
return 10;
}
@@ -85,7 +85,7 @@ int16 BoltEngine::fredsBooth(int16 prevBooth) {
case 2:
return 11;
case 3:
- return 11;
+ return g_isDemo ? 17 : 11;
default:
return 11;
}
@@ -105,6 +105,9 @@ int16 BoltEngine::scoobysBooth(int16 prevBooth) {
case 2:
return 12;
case 3:
+ if (g_isDemo && g_scoobyWins >= 2)
+ return 17;
+
return 12;
default:
return 12;
@@ -125,6 +128,9 @@ int16 BoltEngine::yogisBooth(int16 prevBooth) {
case 2:
return 13;
case 3:
+ if (g_isDemo && g_yogiWins >= 2)
+ return 17;
+
return 13;
default:
return 13;
@@ -145,6 +151,9 @@ int16 BoltEngine::georgesBooth(int16 prevBooth) {
case 2:
return 14;
case 3:
+ if (g_isDemo && g_georgeWins >= 2)
+ return 17;
+
return 14;
default:
return 14;
@@ -165,7 +174,7 @@ int16 BoltEngine::topCatsBooth(int16 prevBooth) {
case 2:
return 15;
case 3:
- return 15;
+ return g_isDemo ? 17 : 15;
default:
return 15;
}
@@ -177,7 +186,7 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
switch (result) {
case -1:
- return 0; // exit
+ return g_isDemo ? 18 : 0; // exit
case 0:
return 8; // left (TopCat)
case 1:
@@ -189,7 +198,9 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
case 4:
return 9; // self
case 5:
- return 0; // exit
+ return g_isDemo ? 18 : 0; // exit
+ case 6:
+ return g_isDemo ? 18 : 9; // Case only available in the demo
default:
return 9;
}
@@ -286,19 +297,19 @@ void BoltEngine::unloadBooth() {
g_boothLoadedMask = 0;
}
-int16 BoltEngine::openBooth(int16 sceneId) {
+int16 BoltEngine::openBooth(int16 boothId) {
int16 baseResId;
int16 resId;
- g_currentBoothScene = sceneId;
+ g_currentBoothScene = boothId;
- if (!loadBooth(sceneId))
+ if (!loadBooth(boothId))
return -1;
- baseResId = (sceneId - 3) << 8;
+ baseResId = (boothId - 3) << 8;
// Main entrance has more hotspots/animations than regular booths
- if (sceneId == 9) {
+ if (boothId == 9) {
g_boothNumHotspots = 8;
g_boothNumAnimations = 7;
} else {
@@ -317,7 +328,7 @@ int16 BoltEngine::openBooth(int16 sceneId) {
}
// Load animation descriptors
- if (sceneId == 9) {
+ if (boothId == 9) {
for (int16 i = 0; i < g_boothNumAnimations; i++)
g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, 0x709 + i);
} else {
@@ -329,7 +340,7 @@ int16 BoltEngine::openBooth(int16 sceneId) {
}
// Load hotspot descriptors
- if (sceneId == 9) {
+ if (boothId == 9) {
for (int16 i = 0; i < g_boothNumHotspots; i++)
g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, 0x710 + i));
} else {
@@ -340,7 +351,7 @@ int16 BoltEngine::openBooth(int16 sceneId) {
}
}
- if (sceneId == 9) {
+ if (boothId == 9) {
// Main entrance palette cycling data
g_boothPalCycleData = memberAddr(g_boothsBoltLib, 0x718);
@@ -348,9 +359,9 @@ int16 BoltEngine::openBooth(int16 sceneId) {
byte *sprite = memberAddr(g_boothsBoltLib, (g_displayMode != 0 ? 0x801 : 0x800) + (g_lettersWon << 8));
boltPict2Pict(&g_boothLetterSprite, sprite);
- // Exit sign sprites
- g_boothExitLeft = memberAddr(g_boothsBoltLib, 0x747);
- g_boothExitRight = memberAddr(g_boothsBoltLib, 0x748);
+ // Visit sign sprite and its off variant
+ g_boothVisitSignOn = memberAddr(g_boothsBoltLib, 0x747);
+ g_boothVisitSignOff = memberAddr(g_boothsBoltLib, 0x748);
} else {
g_boothPalCycleData = memberAddr(g_boothsBoltLib, baseResId + 0x112);
@@ -372,6 +383,12 @@ int16 BoltEngine::openBooth(int16 sceneId) {
_xp->setCursorPos(g_cursorX, g_cursorY);
flushInput();
+
+ if (g_isDemo && boothId == 9) {
+ g_helpFlag = 1;
+ g_helpTimer = _xp->startTimer(500);
+ }
+
return boothEventLoop();
}
@@ -388,9 +405,9 @@ void BoltEngine::playTour() {
int16 playing = 1;
_xp->hideCursor();
- g_screensaverStep = 0;
+ g_tourStep = 0;
- if (prepareAV(g_rtfHandle, 4, g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
+ if (prepareAV(g_rtfHandle, 4 - (g_isDemo ? 3 : 0), g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
while (playing) {
playing = maintainAV(0);
if (!playing)
@@ -400,17 +417,17 @@ void BoltEngine::playTour() {
int16 event = _xp->getEvent(etEmpty, &eventData);
switch (event) {
- case etMouseDown: // Abort tour
+ case etMouseDown:
stopAV();
playing = 0;
break;
- case etSound: // Sound marker
+ case etSound:
playing = maintainAV(1);
break;
- case etTrigger: // Trigger marker from RTF stream
- screensaverStep();
+ case etTrigger:
+ tourPaletteCycleStep();
break;
}
}
@@ -437,7 +454,7 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
if (activeHotspot != 1)
restoreColors(1);
- if (activeHotspot != 2 && g_keyHelp == 0)
+ if (activeHotspot != 2 && g_helpFlag == 0)
restoreColors(2);
g_keyLeft = 0;
@@ -470,7 +487,7 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
return g_keyLeft;
case 2:
- return (g_helpPlaying || g_keyHelp) ? 1 : 0;
+ return (g_helpPlaying || g_helpFlag) ? 1 : 0;
case 3:
return g_keyEnter;
@@ -526,9 +543,9 @@ int16 BoltEngine::boothEventLoop() {
g_hoveredHotspot = -1;
hoverHotSpot();
- while (exitCode == 0 && !shouldQuit()) {
- _xp->setInactivityTimer(30);
+ _xp->setInactivityTimer(30);
+ while (exitCode == 0 && !shouldQuit()) {
if (g_helpPlaying != 0) {
g_helpPlaying = maintainAudioPlay(0);
if (g_helpPlaying == 0)
@@ -540,7 +557,7 @@ int16 BoltEngine::boothEventLoop() {
case etTimer:
if (g_keyLeft == 0 && g_keyRight == 0 &&
g_keyUp == 0 && g_keyDown == 0 &&
- g_keyEnter == 0 && g_keyHelp == 0)
+ g_keyEnter == 0 && g_helpFlag == 0)
break;
if (g_keyReleased != 0) {
@@ -557,7 +574,7 @@ int16 BoltEngine::boothEventLoop() {
restoreColors(5);
} else if (g_keyEnter != 0) {
restoreColors(3);
- } else if (g_keyHelp != 0) {
+ } else if (g_helpFlag != 0) {
restoreColors(2);
if (g_helpPlaying != 0) {
@@ -581,7 +598,7 @@ int16 BoltEngine::boothEventLoop() {
setColors(5);
} else if (g_keyEnter != 0) {
setColors(3);
- } else if (g_keyHelp != 0) {
+ } else if (g_helpFlag != 0) {
setColors(2);
if (g_helpPlaying != 0) {
@@ -593,7 +610,7 @@ int16 BoltEngine::boothEventLoop() {
}
}
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case etMouseMove:
@@ -638,17 +655,19 @@ int16 BoltEngine::boothEventLoop() {
if (g_helpPlaying != 0)
break;
- if (g_keyHelp == 0) {
- g_keyHelp = 1;
- g_keyTimer = _xp->startTimer(500);
- if (g_idleHelpAvailable != 0)
+ if (g_helpFlag == 0) {
+ g_helpFlag = 1;
+ g_helpTimer = _xp->startTimer(500);
+ if (g_idleHelpAudioAvailable != 0) {
+ _xp->setInactivityTimer(30);
continue;
+ }
} else {
- if (g_idleHelpAvailable == 0)
+ if (g_idleHelpAudioAvailable == 0)
break;
- if (startAnimation(g_rtfHandle, 0x19)) {
- g_idleHelpAvailable = 0;
+ if (startAnimation(g_rtfHandle, g_isDemo ? 0x16 : 0x19)) {
+ g_idleHelpAudioAvailable = 0;
g_helpPlaying = 1;
g_helpIsIdle = 1;
}
@@ -661,9 +680,9 @@ int16 BoltEngine::boothEventLoop() {
break;
if (g_currentBoothScene == 9)
- mapIdleAnimation();
+ mainEntranceHelpBlink();
else
- boothIdleAnimation();
+ boothHelpBlink();
break;
}
@@ -678,21 +697,21 @@ int16 BoltEngine::boothEventLoop() {
}
void BoltEngine::resetInactivityState() {
- if (g_keyTimer != 0) {
- _xp->killTimer(g_keyTimer);
- g_keyTimer = 0;
+ if (g_helpTimer != 0) {
+ _xp->killTimer(g_helpTimer);
+ g_helpTimer = 0;
}
- g_idleHelpAvailable = 0;
+ g_idleHelpAudioAvailable = 0;
_xp->setInactivityTimer(30);
- if (g_keyHelp != 0) {
+ if (g_helpFlag != 0) {
restoreColors(2);
if (g_currentBoothScene == 9)
restoreColors(8);
}
- g_keyHelp = 0;
+ g_helpFlag = 0;
}
bool BoltEngine::handleButtonPress(int16 hotspot) {
@@ -718,25 +737,25 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
int16 animId;
switch (g_currentBoothScene) {
case 3:
- animId = 0x18;
+ animId = g_isDemo ? 0x0F : 0x18;
break;
case 4:
- animId = 0x12;
+ animId = g_isDemo ? 0x10 : 0x12;
break;
case 5:
- animId = 0x13;
+ animId = g_isDemo ? 0x11 : 0x13;
break;
case 6:
- animId = 0x14;
+ animId = g_isDemo ? 0x12 : 0x14;
break;
case 7:
- animId = 0x15;
+ animId = g_isDemo ? 0x13 : 0x15;
break;
case 8:
- animId = 0x16;
+ animId = g_isDemo ? 0x14 : 0x16;
break;
case 9:
- animId = 0x17;
+ animId = g_isDemo ? 0x15 : 0x17;
break;
default:
animId = -1;
@@ -750,8 +769,8 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
if (ok) {
g_helpPlaying = 1;
setColors(2);
- g_keyHelp = 0;
- g_screensaverStep = 0;
+ g_helpFlag = 0;
+ g_tourStep = 0;
}
return false;
@@ -765,11 +784,15 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
{
byte *navTable = (hotspot == 3) ? savedLeftDoor : savedRightDoor;
int8 boothOffset = (int8)navTable[g_lettersWon % 3];
- playAVOverBooth(boothOffset + 12);
+ playAVOverBooth(boothOffset + (g_isDemo ? 9 : 12));
return false;
}
case 5:
+ if (g_isDemo) {
+ return true;
+ }
+
_xp->hideCursor();
displayBooth(stBack);
@@ -804,8 +827,15 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
vDelete("TopCatBF");
vDelete("Yogi");
+ g_huckWins = 0;
+ g_fredWins = 0;
+ g_scoobyWins = 0;
+ g_yogiWins = 0;
+ g_georgeWins = 0;
+ g_topCatWins = 0;
+
displayBooth(stBack);
- playAV(g_rtfHandle, 5,
+ playAV(g_rtfHandle, 5 - (g_isDemo ? 3 : 0),
g_displayWidth, g_displayHeight,
g_displayX, g_displayY);
@@ -831,7 +861,7 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
} else {
switch (hotspot) {
case 3:
- startCycle(g_boothPalCycleData);
+ startCycle(g_boothSceneDesc);
_xp->startTimer(2000);
return true;
@@ -862,8 +892,9 @@ void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
si += 3;
}
- if (mode == 1)
+ if (mode == 1) {
_xp->setPalette(count, startIndex, localPalette);
+ }
}
void BoltEngine::setColors(int16 index) {
@@ -886,7 +917,7 @@ void BoltEngine::setColors(int16 index) {
break;
case 8:
- displayPic(g_boothExitLeft, 0, 0, 0);
+ displayPic(g_boothVisitSignOn, 0, 0, stFront);
_xp->updateDisplay();
break;
}
@@ -940,7 +971,7 @@ void BoltEngine::restoreColors(int16 index) {
}
case 8:
- displayPic(g_boothExitRight, 0, 0, 0);
+ displayPic(g_boothVisitSignOff, 0, 0, stFront);
_xp->updateDisplay();
break;
}
@@ -1000,36 +1031,36 @@ void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
void BoltEngine::playBoothAV() {
switch (g_currentBoothScene) {
case 3:
- playAVOverBooth(6);
+ playAVOverBooth(6 - (g_isDemo ? 3 : 0));
break;
case 4:
- playAVOverBooth(7);
+ playAVOverBooth(7 - (g_isDemo ? 3 : 0));
break;
case 5:
- playAVOverBooth(8);
+ playAVOverBooth(8 - (g_isDemo ? 3 : 0));
break;
case 6:
- playAVOverBooth(9);
+ playAVOverBooth(9 - (g_isDemo ? 3 : 0));
break;
case 7:
- playAVOverBooth(10);
+ playAVOverBooth(10 - (g_isDemo ? 3 : 0));
break;
case 8:
- playAVOverBooth(11);
+ playAVOverBooth(11 - (g_isDemo ? 3 : 0));
break;
}
}
-void BoltEngine::mapIdleAnimation() {
- g_screensaverStep++;
+void BoltEngine::mainEntranceHelpBlink() {
+ g_tourStep++;
- switch (g_screensaverStep) {
+ switch (g_tourStep) {
case 1:
setColors(0);
setColors(1);
g_keyLeft = 1;
g_keyReleased = 1;
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case 2:
@@ -1048,7 +1079,7 @@ void BoltEngine::mapIdleAnimation() {
setColors(7);
g_keyRight = 1;
g_keyReleased = 1;
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case 4:
@@ -1063,7 +1094,7 @@ void BoltEngine::mapIdleAnimation() {
setColors(6);
g_keyUp = 1;
g_keyReleased = 1;
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case 6:
@@ -1078,13 +1109,13 @@ void BoltEngine::mapIdleAnimation() {
setColors(5);
g_keyDown = 1;
g_keyReleased = 1;
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case 8:
g_keyDown = 0;
g_keyReleased = 0;
- g_keyTimer = 0;
+ g_helpTimer = 0;
if (!g_boothHotspotDescs[5].contains(g_cursorX, g_cursorY))
restoreColors(5);
@@ -1092,15 +1123,15 @@ void BoltEngine::mapIdleAnimation() {
}
}
-void BoltEngine::boothIdleAnimation() {
- g_screensaverStep++;
+void BoltEngine::boothHelpBlink() {
+ g_tourStep++;
- switch (g_screensaverStep) {
+ switch (g_tourStep) {
case 1:
setColors(3);
g_keyEnter = 1;
g_keyReleased = 1;
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case 2:
@@ -1117,13 +1148,13 @@ void BoltEngine::boothIdleAnimation() {
setColors(1);
g_keyLeft = 1;
g_keyReleased = 1;
- g_keyTimer = _xp->startTimer(500);
+ g_helpTimer = _xp->startTimer(500);
break;
case 4:
g_keyLeft = 0;
g_keyReleased = 0;
- g_keyTimer = 0;
+ g_helpTimer = 0;
if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
restoreColors(0);
@@ -1135,20 +1166,20 @@ void BoltEngine::boothIdleAnimation() {
}
}
-void BoltEngine::screensaverStep() {
+void BoltEngine::tourPaletteCycleStep() {
static const int16 cycleResIds[] = {
0x700, 0x719, 0x71E, 0x720, 0x725, 0x72A, 0x72F, 0x731,
-1, 0x736, 0x73B, 0x73F, 0x743, -1
};
- g_screensaverStep++;
+ g_tourStep++;
- if (!(g_screensaverStep & 1)) {
+ if ((g_tourStep & 1) == 0) {
_xp->stopCycle();
return;
}
- int16 index = g_screensaverStep >> 1;
+ int16 index = g_tourStep >> 1;
if (index > 13)
return;
@@ -1208,6 +1239,9 @@ void BoltEngine::flushInput() {
int16 BoltEngine::winALetter(int16 prevBooth) {
g_lettersWon++;
+ if (g_isDemo && g_lettersWon == 6)
+ return 18;
+
if (g_lettersWon == 15) {
if (g_allLettersWonFlag) {
g_allLettersWonFlag = false;
@@ -1217,7 +1251,8 @@ int16 BoltEngine::winALetter(int16 prevBooth) {
}
}
- playAV(g_rtfHandle, g_lettersWon + 31, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ int16 avIndex = g_isDemo ? 25 : 31;
+ playAV(g_rtfHandle, g_lettersWon + avIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
if (g_lettersWon >= 15)
g_lettersWon = 0;
@@ -1225,20 +1260,60 @@ int16 BoltEngine::winALetter(int16 prevBooth) {
// Return the booth front ID for the game just played
switch (prevBooth) {
case 10:
+ if (g_isDemo) {
+ g_huckWins++;
+ }
+
return 3; // HuckGame -> HucksBooth
case 11:
+ if (g_isDemo) {
+ g_fredWins++;
+ }
+
return 4; // FredGame -> FredsBooth
case 12:
+ if (g_isDemo) {
+ g_scoobyWins++;
+ }
+
return 5; // ScoobyGame -> ScoobysBooth
case 13:
+ if (g_isDemo) {
+ g_yogiWins++;
+ }
+
return 6; // YogiGame -> YogisBooth
case 14:
+ if (g_isDemo) {
+ g_georgeWins++;
+ }
+
return 7; // GeorgeGame -> GeorgesBooth
case 15:
+ if (g_isDemo) {
+ g_topCatWins++;
+ }
+
return 8; // TopCatGame -> TopCatsBooth
default:
return 9; // MainEntrance
}
}
+int16 BoltEngine::endDemo(int16 prevBooth) {
+ _xp->hideCursor();
+ _xp->setTransparency(false);
+
+ int16 baseId = (g_displayMode != 0) ? 0x0801 : 0x0800;
+ int16 memberId = baseId + (g_lettersWon << 8);
+ displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
+ displayColors(getBOLTMember(g_boothsBoltLib, 0x718), stFront, 0);
+
+ _xp->updateDisplay();
+
+ fadeToBlack(32);
+
+ return 0;
+}
+
} // End of namespace Bolt
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index d0e5b38e0bf..e4a4a69c534 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -23,6 +23,1246 @@
namespace Bolt {
+int16 BoltEngine::fredGame(int16 prevBooth) {
+ int16 savedTimer = _xp->setInactivityTimer(30);
+ if (!initFred()) {
+ cleanUpFred();
+ return 4; // FredsBooth
+ }
+
+ int16 result = playFred();
+ cleanUpFred();
+
+ _xp->setInactivityTimer(savedTimer);
+ return result;
+}
+
+bool BoltEngine::initFred() {
+ const char *path = assetPath("fred.blt");
+
+ if (!openBOLTLib(&g_fredBoltLib, &g_fredBoltCallbacks, path))
+ return false;
+
+ if (!getBOLTGroup(g_fredBoltLib, 0, 1))
+ return false;
+
+ // Load assets...
+ g_fredBackground = memberAddr(g_fredBoltLib, (g_displayMode != 0) ? 5 : 4);
+
+ g_fredBalloonString = memberAddr(g_fredBoltLib, 6);
+
+ g_fredFacingLeftRect = memberAddr(g_fredBoltLib, 0);
+ g_fredFacingRightRect = memberAddr(g_fredBoltLib, 1);
+ g_fredTurningRect = memberAddr(g_fredBoltLib, 2);
+ g_fredBalloonRect = memberAddr(g_fredBoltLib, 3);
+
+ g_fredHelpEntries = memberAddr(g_fredBoltLib, 0x38);
+ g_fredPlayButton = memberAddr(g_fredBoltLib, 0x39);
+ g_fredTimer = 0;
+
+ for (int i = 0; i < 10; i++)
+ g_fredSprites[i] = memberAddr(g_fredBoltLib, 0x28 + i);
+
+ getFredSoundInfo(g_fredBoltLib, 0x3A, &g_fredSounds[0]);
+ getFredSoundInfo(g_fredBoltLib, 0x3B, &g_fredSounds[1]);
+ getFredSoundInfo(g_fredBoltLib, 0x3C, &g_fredSounds[2]);
+ getFredSoundInfo(g_fredBoltLib, 0x3D, &g_fredSounds[3]);
+
+ g_fredLoopSound = nullptr;
+ g_fredCurrentSound = nullptr;
+ g_fredPendingLoop = nullptr;
+ g_fredPendingOneShot = nullptr;
+
+ // Init Fred sprite struct...
+ g_fredSprite.flags = 1;
+ g_fredSprite.direction = 0;
+ g_fredSprite.xPos = 0xA000; // Fixed-point coordinate
+ g_fredSprite.yPos = 0x1600; // Fixed-point coordinate
+ g_fredSprite.speed = 0x600;
+ g_fredSprite.pathTable = nullptr;
+
+ setFredAnimMode(&g_fredSprite, 0);
+
+ // Load save data...
+ if (!vLoad(&g_fredSaveData, g_fredSaveFile)) {
+ g_fredSaveData[0] = 0; // Balloon catch count
+ g_fredSaveData[1] = 0; // Level data group index, increments on level complete, caps at 9
+ g_fredSaveData[2] = 0; // Palette group index, increments on level complete, wraps at 10
+ }
+
+ if (!initFredLevel(g_fredSaveData[1], g_fredSaveData[2]))
+ return false;
+
+ g_fredLevelIndex = 0;
+ while (g_fredLevelIndex < g_fredSaveData[0]) {
+ FredEntityState *entry = g_fredEntitiesTable[g_fredLevelIndex + 1];
+ setFredAnimMode(entry, 10);
+ g_fredLevelIndex++;
+ }
+
+ // Flush timer events...
+ uint32 dummy;
+ while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+
+ // Display background on back and front buffers...
+ displayColors(g_fredPalette, stBack, 0);
+ displayPic(g_fredBackground, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+
+ _xp->setTransparency(true);
+ displayColors(g_fredPalette, stFront, 0);
+ displayColors(g_fredPalette, stBack, 1);
+ displayPic(g_fredBackground, g_displayX, g_displayY, stBack);
+
+ renderFredScene();
+ _xp->updateDisplay();
+
+ _xp->startCycle(g_fredCycleSpecs);
+ _xp->setFrameRate(15);
+
+ return true;
+}
+
+void BoltEngine::cleanUpFred() {
+ int16 savedLevel = g_fredSaveData[1]; // Level data group index
+ int16 savedPalette = g_fredSaveData[2]; // Palette group index
+
+ g_fredSaveData[0] = g_fredLevelIndex;
+
+ byte *levelPtr = g_fredLevelPtr;
+ if (READ_UINT16(levelPtr + 0x0A) <= g_fredLevelIndex) {
+ g_fredSaveData[1]++;
+ if (g_fredSaveData[1] >= 10)
+ g_fredSaveData[1] = 9;
+
+ g_fredSaveData[2]++;
+ if (g_fredSaveData[2] >= 10)
+ g_fredSaveData[2] = 0;
+
+ g_fredSaveData[0] = 0;
+ }
+
+ vSave(&g_fredSaveData, sizeof(g_fredSaveData), g_fredSaveFile);
+
+ _xp->stopCycle();
+ termFredLevel(savedLevel, savedPalette);
+
+ freeBOLTGroup(g_fredBoltLib, 0, 1);
+ closeBOLTLib(&g_fredBoltLib);
+
+ _xp->setFrameRate(0);
+ _xp->fillDisplay(0, stFront);
+ _xp->updateDisplay();
+}
+
+bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
+ // Load level data group...
+ if (!getBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
+ return false;
+
+ // Load palette group...
+ if (!getBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1)) {
+ freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+ return false;
+ }
+
+ // Level descriptor member...
+ g_fredLevelPtr = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x100);
+
+ // Palette member...
+ g_fredPalette = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x200);
+
+ // Cycle data...
+ g_fredCycleRaw = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x20A);
+ boltCycleToXPCycle(g_fredCycleRaw, g_fredCycleSpecs);
+
+ // Other resources...
+ g_fredBalloonSprite = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x209);
+ g_fredPathMatrix = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x132);
+ g_fredRowBounds = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x101);
+
+ // Allocate shuffle table: numRows * (numCols + 1)
+ uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
+ uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+
+ g_fredShuffleTable = (byte *)_xp->allocMem(numRows * (numCols + 1));
+ if (!g_fredShuffleTable) {
+ termFredLevel(levelGroup, palGroup);
+ return false;
+ }
+
+ // Build and shuffle column indices for each row...
+ for (int16 row = 0; row < numRows; row++) {
+ numCols = READ_UINT16(g_fredLevelPtr + 6);
+ int16 stride = numCols + 1;
+ int16 rowBase = stride * row;
+ g_fredShuffleTable[rowBase] = 0;
+
+ for (int16 col = 0; col < numCols; col++) {
+ g_fredShuffleTable[rowBase + col + 1] = (byte)col;
+ }
+
+ // Fisher-Yates shuffle...
+ for (int16 col = 0; col < numCols; col++) {
+ int16 randIdx = _xp->getRandom(numCols);
+ int16 posA = rowBase + col + 1;
+ int16 posB = rowBase + randIdx + 1;
+
+ byte tmp = g_fredShuffleTable[posA];
+ g_fredShuffleTable[posA] = g_fredShuffleTable[posB];
+ g_fredShuffleTable[posB] = tmp;
+ }
+ }
+
+ // Allocate balloon table...
+ uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
+ g_fredEntitiesTable = (FredEntityState **)_xp->allocMem((numBalloons + 2) * sizeof(FredEntityState *));
+ if (!g_fredEntitiesTable) {
+ termFredLevel(levelGroup, palGroup);
+ return false;
+ }
+
+ // Allocate each balloon state struct...
+ for (int16 i = 0; i < numBalloons; i++) {
+ FredEntityState *entry = new FredEntityState();
+
+ if (!entry) {
+ termFredLevel(levelGroup, palGroup);
+ return false;
+ }
+
+ entry->flags = 0;
+ g_fredEntitiesTable[i + 1] = entry;
+ }
+
+ // Sentinel: null pointer after last balloon...
+ g_fredEntitiesTable[numBalloons + 1] = nullptr;
+
+ // First entry points to Fred's sprite...
+ g_fredEntitiesTable[0] = &g_fredSprite;
+
+ // Allocate balloon type shuffle array...
+ uint16 numTypes = READ_UINT16(g_fredLevelPtr + 0x0C);
+ byte *typeShuf = (byte *)_xp->allocMem(numTypes);
+ if (!typeShuf) {
+ termFredLevel(levelGroup, palGroup);
+ return false;
+ }
+
+ for (int16 i = 0; i < numTypes; i++) {
+ typeShuf[i] = (byte)i;
+ }
+
+ // Reshuffle every numTypes balloons...
+ for (int16 i = 0; i < numBalloons; i++) {
+ if ((i % numTypes) == 0) {
+ for (int16 j = 0; j < numTypes; j++) {
+ int16 randIdx = _xp->getRandom(numTypes);
+ byte tmp = typeShuf[j];
+ typeShuf[j] = typeShuf[randIdx];
+ typeShuf[randIdx] = tmp;
+ }
+ }
+
+ // Assign balloon type...
+ byte balloonType = typeShuf[i % numTypes] + 0x0B;
+ FredEntityState *balloonEntry = g_fredEntitiesTable[i + 1];
+ setFredAnimMode(balloonEntry, balloonType);
+ }
+
+ _xp->freeMem(typeShuf);
+
+ g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+ g_fredBalloonSearchIdx = 0;
+ g_fredLevelIndex = 0;
+
+ return true;
+}
+
+void BoltEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
+ // Free balloon state structs...
+ if (g_fredEntitiesTable) {
+ uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
+ for (int16 i = 0; i < numBalloons; i++) {
+ FredEntityState *entry = g_fredEntitiesTable[i + 1];
+ if (entry) {
+ delete entry;
+ g_fredEntitiesTable[i + 1] = nullptr;
+ }
+ }
+
+ delete[] g_fredEntitiesTable;
+ g_fredEntitiesTable = nullptr;
+ }
+
+ // Free shuffle table...
+ if (g_fredShuffleTable) {
+ _xp->freeMem(g_fredShuffleTable);
+ g_fredShuffleTable = nullptr;
+ }
+
+ // Free BOLT groups...
+ freeBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1);
+ freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+}
+
+void BoltEngine::swapFredAnimEntry() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+ uint32 offset = 0;
+
+ while (offset < decompSize) {
+ WRITE_UINT16(data + offset + 4, READ_BE_INT16(data + offset + 4));
+ resolveIt((uint32 *)(data + offset));
+ offset += 6;
+ }
+}
+
+void BoltEngine::swapFredAnimDesc() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+ uint32 offset = 0;
+ byte *ptr = data;
+ byte *colorCountPtr = data + 0x0E;
+
+ while (offset < decompSize) {
+ WRITE_UINT32(ptr + 0x00, READ_BE_INT32(ptr + 0x00));
+ WRITE_UINT32(ptr + 0x08, READ_BE_INT32(ptr + 0x08));
+ WRITE_UINT16(ptr + 0x0C, READ_BE_INT16(ptr + 0x0C));
+ WRITE_UINT16(colorCountPtr, READ_BE_INT16(colorCountPtr));
+
+ uint16 colorCount = READ_UINT16(colorCountPtr);
+ unpackColors(colorCount, ptr + 0x10);
+ unpackColors(colorCount, ptr + 0x2C);
+
+ resolveIt((uint32 *)(ptr + 0x04));
+
+ offset += 0x48;
+ colorCountPtr += 0x48;
+ ptr += 0x48;
+ }
+}
+
+void BoltEngine::swapFredLevelDesc() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+
+ WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
+ WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
+ WRITE_UINT16(data + 0x04, READ_BE_INT16(data + 0x04));
+ WRITE_UINT16(data + 0x06, READ_BE_INT16(data + 0x06));
+ WRITE_UINT16(data + 0x08, READ_BE_INT16(data + 0x08));
+ WRITE_UINT16(data + 0x0A, READ_BE_INT16(data + 0x0A));
+ WRITE_UINT16(data + 0x0C, READ_BE_INT16(data + 0x0C));
+}
+
+int16 BoltEngine::playFred() {
+ int16 result = 0;
+ int16 allCaught = 0;
+ int16 exitLoop = 0;
+ int16 joystickX = 0;
+
+ // Show help on first play...
+ if (g_fredShowHelp != 0) {
+ if (!helpFred())
+ return 4;
+ }
+
+ _xp->setInactivityTimer(30);
+ _xp->enableController();
+
+ while (!exitLoop) {
+ if (joystickX != 0)
+ _xp->setInactivityTimer(30);
+
+ // Process events...
+ int16 eventType;
+ uint32 eventData;
+ while ((eventType = _xp->getEvent(0, &eventData)) != 0) {
+ switch (eventType) {
+ case etJoystick:
+ joystickX = (int16)(eventData >> 16);
+ break;
+ case etMouseDown:
+ case etInactivity:
+ if (!allCaught) {
+ int16 helpMode = (g_fredSprite.animMode == 8 || g_fredSprite.animMode == 6) ? 1 : 0;
+ setFredAnimMode(&g_fredSprite, helpMode);
+ if (!helpFred()) {
+ result = 4;
+ exitLoop = 1;
+ }
+ }
+
+ break;
+ case etSound:
+ updateFredSound();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (exitLoop)
+ break;
+
+ // Render and update...
+ renderFredScene();
+ _xp->updateDisplay();
+
+ // Balloon spawn timer...
+ g_fredBalloonSpawnDelay--;
+ if (g_fredBalloonSpawnDelay == 0) {
+ if (spawnBalloon())
+ g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+ else
+ g_fredBalloonSpawnDelay = 1;
+ }
+
+ // --- First pass: update Fred and balloons animations ---
+ int16 idx = 0;
+ FredEntityState *entry = g_fredEntitiesTable[idx];
+ while (entry) {
+ if (entry->flags & 1) { // active
+ // Save previous position...
+ entry->prevXPos = entry->xPos;
+ entry->prevYPos = entry->yPos;
+
+ // Store joystick direction...
+ entry->direction = joystickX;
+
+ // Clear frame-changed and anim-ended flags...
+ entry->flags &= 0xFFF9;
+
+ // Advance frame countdown...
+ int16 countdown = entry->frameCountdown - 1;
+ entry->frameCountdown = countdown;
+
+ if (countdown == 0) {
+ byte *animTable = entry->animTable;
+ int16 frameIdx = entry->frameIndex + 1;
+ entry->frameIndex = frameIdx;
+
+ // Check if next frame exists...
+ byte *nextSprite = getResolvedPtr(animTable, frameIdx * 6);
+ if (!nextSprite) {
+ // Loop back to frame 0...
+ entry->frameIndex = 0;
+ entry->flags |= 4; // set anim-ended
+ }
+
+ // Read new frame duration...
+ entry->frameCountdown = READ_UINT16(animTable + entry->frameIndex * 6 + 4);
+ entry->flags |= 2; // frame-changed
+ }
+
+ // Mode-specific behavior...
+ int16 mode = entry->animMode;
+ switch (mode) {
+ case 0: // Fred idle facing right
+ if (allCaught) {
+ int16 newMode = (entry->xPos < 0x8200) ? 7 : 4;
+ setFredAnimMode(entry, newMode);
+ } else {
+ int16 dir = entry->direction;
+ if (dir == 1)
+ setFredAnimMode(entry, 4); // run right
+ else if (dir == -1)
+ setFredAnimMode(entry, 7); // turn to run left
+ }
+
+ break;
+ case 2: // Fred idle facing right (variant)
+ if (allCaught) {
+ int16 newMode = (entry->xPos < 0x8200) ? 7 : 4;
+ setFredAnimMode(entry, newMode);
+ } else {
+ int16 dir = entry->direction;
+ if (dir == 1)
+ setFredAnimMode(entry, 4);
+ else if (dir == -1)
+ setFredAnimMode(entry, 7);
+ }
+
+ break;
+ case 1: // Fred idle facing left
+ if (allCaught) {
+ int16 newMode = (entry->xPos < 0x8200) ? 8 : 3;
+ setFredAnimMode(entry, newMode);
+ } else {
+ int16 dir = entry->direction;
+ if (dir == -1)
+ setFredAnimMode(entry, 8); // run left
+ else if (dir == 1)
+ setFredAnimMode(entry, 3); // turn to run right
+ }
+
+ break;
+ case 6: // Fred idle facing left (variant)
+ if (allCaught) {
+ int16 newMode = (entry->xPos < 0x8200) ? 8 : 3;
+ setFredAnimMode(entry, newMode);
+ } else {
+ int16 dir = entry->direction;
+ if (dir == -1)
+ setFredAnimMode(entry, 8);
+ else if (dir == 1)
+ setFredAnimMode(entry, 3);
+ }
+
+ break;
+ case 3: // Turn right->left
+ if (entry->flags & 4) // anim ended
+ setFredAnimMode(entry, 4);
+
+ break;
+ case 7: // Turn left->right
+ if (entry->flags & 4)
+ setFredAnimMode(entry, 8);
+
+ break;
+ case 4: // Fred running right (stopped)
+ if (!allCaught) {
+ int16 dir = entry->direction;
+ if (dir == -1)
+ setFredAnimMode(entry, 7);
+ else if (dir == 0)
+ setFredAnimMode(entry, 2); // stop facing right
+ }
+
+ break;
+ case 8: // Fred running left (stopped)
+ if (!allCaught) {
+ int16 dir = entry->direction;
+ if (dir == 1)
+ setFredAnimMode(entry, 3);
+ else if (dir == 0)
+ setFredAnimMode(entry, 6); // stop facing left
+ }
+
+ break;
+ case 5: // ?
+ if (entry->flags & 4)
+ setFredAnimMode(entry, 2);
+
+ break;
+ case 9: // ?
+ if (entry->flags & 4)
+ setFredAnimMode(entry, 6);
+
+ break;
+ }
+ }
+
+ idx++;
+ entry = g_fredEntitiesTable[idx];
+ }
+
+ // --- Second pass: move balloons and Fred ---
+ idx = 0;
+ entry = g_fredEntitiesTable[idx];
+ while (entry) {
+ if (entry->flags & 1) {
+ int16 mode = entry->animMode;
+
+ if (mode == 4) {
+ // Running right
+ int32 speed = (int32)entry->speed;
+ int32 xPos = entry->xPos + speed;
+ entry->xPos = xPos;
+
+ if (!allCaught) {
+ if (xPos > 0x11D00) {
+ entry->xPos = 0x11D00;
+ setFredAnimMode(entry, 2); // stop at right edge
+ }
+ } else {
+ if (xPos > 0x18000)
+ exitLoop = 1; // ran off right side
+ }
+
+ } else if (mode == 8) {
+ // Running left
+ int32 speed = (int32)entry->speed;
+ int32 xPos = entry->xPos - speed;
+ entry->xPos = xPos;
+
+ if (!allCaught) {
+ if (xPos < 0) {
+ entry->xPos = 0;
+ setFredAnimMode(entry, 6); // stop at left edge
+ }
+ } else {
+ if (xPos < (int32)0xFFFF9400)
+ exitLoop = 1; // ran off left side
+ }
+
+ } else if (mode == 0x0B) {
+ // Balloon floating...
+ int16 pathIdx = entry->pathIndex + 1;
+ entry->pathIndex = pathIdx;
+
+ byte *pathTable = entry->pathTable;
+ int16 pathCount = READ_UINT16(pathTable);
+
+ if (pathIdx >= pathCount) {
+ // Balloon escaped, deactivate...
+ entry->flags &= 0xFFFE;
+ } else {
+ // Update position from path table...
+ byte *pathEntry = pathTable + pathIdx * 4;
+ int32 newX = ((int32)READ_UINT16(pathEntry + 2)) << 8;
+ entry->xPos = newX;
+ int32 newY = ((int32)READ_UINT16(pathEntry + 4)) << 8;
+ entry->yPos = newY;
+ }
+ }
+ }
+
+ idx++;
+ entry = g_fredEntitiesTable[idx];
+ }
+
+ // --- Skip collision check if all caught ---
+ if (allCaught)
+ continue;
+
+ // --- Get Fred's collision rect based on current mode ---
+ byte *fredRect;
+ switch (g_fredSprite.animMode) {
+ case 6:
+ case 8:
+ fredRect = g_fredFacingLeftRect; // facing/running left
+ break;
+ case 2:
+ case 4:
+ fredRect = g_fredFacingRightRect; // facing/running right
+ break;
+ case 7:
+ fredRect = g_fredTurningRect; // turning left
+ break;
+ case 3:
+ fredRect = g_fredTurningRect; // turning right
+ break;
+ default:
+ fredRect = nullptr;
+ break;
+ }
+
+ if (!fredRect)
+ continue;
+
+ // Build Fred's bounding rect...
+ int16 fredX = ((int32)g_fredSprite.xPos >> 8) + READ_UINT16(fredRect);
+ int16 fredY = ((int32)g_fredSprite.yPos >> 8) + READ_UINT16(fredRect + 2);
+ int16 fredW = READ_UINT16(fredRect + 4);
+ int16 fredH = READ_UINT16(fredRect + 6);
+
+ // --- Third pass: check collisions with balloons ---
+ idx = 0;
+ entry = g_fredEntitiesTable[idx];
+ while (entry) {
+ if ((entry->flags & 1) && entry->animMode == 0x0B) {
+ // Build balloon bounding rect...
+ int16 bx = ((int32)entry->xPos >> 8) + READ_UINT16(g_fredBalloonRect);
+ int16 by = ((int32)entry->yPos >> 8) + READ_UINT16(g_fredBalloonRect + 2);
+ int16 bw = READ_UINT16(g_fredBalloonRect + 4);
+ int16 bh = READ_UINT16(g_fredBalloonRect + 6);
+
+ // {x, y, w, h} rect overlap test...
+ Common::Rect balloonRect(bx, by, bx + bw, by + bh);
+ Common::Rect playerRect(fredX, fredY, fredX + fredW, fredY + fredH);
+
+ if (playerRect.intersects(balloonRect)) {
+ // Caught a balloon!
+ int16 catchMode;
+ if (g_fredSprite.animMode == 6 || g_fredSprite.animMode == 8 || g_fredSprite.animMode == 7)
+ catchMode = 9; // catch facing left
+ else
+ catchMode = 5; // catch facing right
+
+ setFredAnimMode(&g_fredSprite, catchMode);
+ setFredAnimMode(entry, 0x0A); // balloon disappearing
+
+ g_fredLevelIndex++;
+ if (g_fredLevelIndex >= READ_UINT16(g_fredLevelPtr + 0x0A)) {
+ allCaught = 1;
+ result = 0x10;
+ }
+
+ // Reset spawn timer...
+ if (spawnBalloon())
+ g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+ else
+ g_fredBalloonSpawnDelay = 1;
+
+ break; // Only catch one per frame...
+ }
+ }
+
+ idx++;
+ entry = g_fredEntitiesTable[idx];
+ }
+ }
+
+ _xp->stopSound();
+ _xp->disableController();
+ return result;
+}
+
+int16 BoltEngine::helpFred() {
+ byte *firstEntry = getResolvedPtr(g_fredHelpEntries, 0);
+ byte *picDesc = getResolvedPtr(firstEntry, 4);
+
+ int16 cursorX = READ_UINT16(picDesc + 6) + READ_UINT16(picDesc + 0x0A) - 10;
+ int16 cursorY = READ_UINT16(picDesc + 8) + READ_UINT16(picDesc + 0x0C) - 10;
+
+ byte *playableEntry = nullptr;
+ int16 exitResult = -1;
+ int16 isPlaying = 0;
+ int16 soundPending = 0;
+
+ _xp->setFrameRate(0);
+ _xp->setInactivityTimer(0);
+ _xp->stopSound();
+
+ g_fredLoopSound = nullptr;
+ g_fredCurrentSound = nullptr;
+ g_fredPendingLoop = nullptr;
+ g_fredPendingOneShot = nullptr;
+
+ renderFredScene();
+
+ // Display all help entry pics on front surface...
+ int16 off = 0;
+ byte *entry;
+ while (true) {
+ entry = getResolvedPtr(g_fredHelpEntries, off);
+ if (!entry)
+ break;
+ byte *pic = getResolvedPtr(entry, 4);
+ displayPic(pic, 0, 0, stFront);
+ off += 4;
+ }
+
+ _xp->updateDisplay();
+
+ // Save current palette for each entry, find playable entry...
+ off = 0;
+ while (true) {
+ entry = getResolvedPtr(g_fredHelpEntries, off);
+ if (!entry)
+ break;
+
+ _xp->getPalette(READ_UINT16(entry + 0x0C), READ_UINT16(entry + 0x0E), entry + 0x2C);
+ WRITE_UINT32(entry + 8, READ_UINT32(entry + 8) & 0xFFFFFFFE); // clear highlight flag
+
+ if (READ_UINT32(entry) == 2)
+ playableEntry = entry;
+
+ off += 4;
+ }
+
+ // Setup cursor...
+ _xp->setCursorPos(cursorX, cursorY);
+ _xp->setCursorColor(255, 255, 0);
+ _xp->disableController();
+ _xp->showCursor();
+
+ // Highlight first entry...
+ g_fredHoveredEntry = getResolvedPtr(g_fredHelpEntries, 0);
+ hiliteFredHelpObject(getResolvedPtr(g_fredHelpEntries, 0), 1);
+
+ // Main help loop
+ while (exitResult == -1) {
+ // Handle audio playback...
+ if (isPlaying) {
+ if (!maintainAudioPlay(soundPending)) {
+ int16 highlight = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(playableEntry, highlight);
+ isPlaying = 0;
+ }
+ soundPending = 0;
+ }
+
+ // Process events...
+ uint32 eventData;
+ int16 eventType = _xp->getEvent(0, &eventData);
+
+ switch (eventType) {
+ case etTimer: {
+ // Timer expired, toggle selected entry highlight...
+ if (!g_fredCurrentHelpObject)
+ break;
+
+ if (g_fredTimer != eventData)
+ break;
+
+ g_fredTimer = _xp->startTimer(500);
+
+ byte *sel = g_fredCurrentHelpObject;
+ int16 highlighted = (READ_UINT32(sel + 8) & 1) ? 0 : 1;
+ hiliteFredHelpObject(sel, highlighted);
+ break;
+ }
+ case etMouseMove: {
+ cursorX = (int16)(eventData >> 16);
+ cursorY = (int16)(eventData & 0xFFFF);
+
+ // Hit test against all help entries...
+ off = 0;
+ byte *hitEntry = nullptr;
+ while (true) {
+ entry = getResolvedPtr(g_fredHelpEntries, off);
+ if (!entry)
+ break;
+
+ byte *pd = getResolvedPtr(entry, 4);
+ int16 ex = READ_UINT16(pd + 6);
+ int16 ey = READ_UINT16(pd + 8);
+ int16 ew = READ_UINT16(pd + 0x0A);
+ int16 eh = READ_UINT16(pd + 0x0C);
+
+ if (cursorX >= ex && cursorX < ex + ew &&
+ cursorY >= ey && cursorY < ey + eh) {
+ hitEntry = entry;
+ break;
+ }
+ off += 4;
+ }
+
+ // Update hover state...
+ if (hitEntry == g_fredHoveredEntry)
+ break;
+
+ // Unhighlight previous...
+ if (g_fredHoveredEntry &&
+ g_fredHoveredEntry != g_fredCurrentHelpObject &&
+ !(g_fredHoveredEntry == playableEntry && isPlaying)) {
+ hiliteFredHelpObject(g_fredHoveredEntry, 0);
+ }
+
+ g_fredHoveredEntry = hitEntry;
+
+ // Highlight new...
+ if (hitEntry && hitEntry != g_fredCurrentHelpObject) {
+ hiliteFredHelpObject(hitEntry, 1);
+ }
+
+ break;
+ }
+ case etMouseDown: {
+ int16 justStopped = 0;
+
+ // Stop current animation if playing...
+ if (isPlaying) {
+ if (g_fredTimer) {
+ _xp->killTimer(g_fredTimer);
+ g_fredTimer = 0;
+ }
+
+ int16 hl = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(playableEntry, hl);
+
+ hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+
+ g_fredCurrentHelpObject = nullptr;
+ stopAnimation();
+ isPlaying = 0;
+ justStopped = 1;
+ }
+
+ // Handle click on hovered entry...
+ if (!g_fredHoveredEntry)
+ break;
+
+ uint32 entryType = READ_UINT32(g_fredHoveredEntry);
+
+ if (entryType == 0) {
+ // Exit help...
+ exitResult = 0;
+ } else if (entryType == 1) {
+ // Exit help and clear show-help flag...
+ g_fredShowHelp = 0;
+ exitResult = 1;
+ } else if (entryType == 2) {
+ // Play animation...
+ if (!isPlaying && !justStopped) {
+ if (startAnimation(g_rtfHandle, 0x1B)) {
+ g_fredHelpStep = 0;
+ isPlaying = 1;
+ }
+ }
+ }
+
+ break;
+ }
+ case etSound:
+ soundPending = 1;
+ break;
+ case etTrigger:
+ if (isPlaying)
+ helpAnimStep();
+
+ break;
+ }
+ }
+
+ // Cleanup: restore help screen
+ _xp->hideCursor();
+ _xp->enableController();
+
+ // Restore all entries to highlight state matching hover
+ off = 0;
+ while (true) {
+ entry = getResolvedPtr(g_fredHelpEntries, off);
+ if (!entry)
+ break;
+ int16 hl = (entry == g_fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(entry, hl);
+ off += 4;
+ }
+
+ _xp->updateDisplay();
+
+ // Unhighlight all entries
+ off = 0;
+ while (true) {
+ entry = getResolvedPtr(g_fredHelpEntries, off);
+ if (!entry)
+ break;
+ hiliteFredHelpObject(entry, 0);
+ off += 4;
+ }
+
+ renderFredScene();
+ _xp->updateDisplay();
+
+ _xp->setFrameRate(15);
+ _xp->setInactivityTimer(30);
+ updateFredSound();
+
+ return exitResult;
+}
+
+void BoltEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
+ if (!entry)
+ return;
+
+ if (highlight) {
+ // Apply highlight palette...
+ _xp->setPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x0C), entry + 0x10);
+ WRITE_UINT32(entry + 8, READ_UINT32(entry + 8) | 1);
+ _system->updateScreen();
+ } else {
+ // Restore saved palette...
+ _xp->setPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x0C), entry + 0x2C);
+ WRITE_UINT32(entry + 8, READ_UINT32(entry + 8) & 0xFFFFFFFE);
+ _system->updateScreen();
+ }
+}
+
+void BoltEngine::helpAnimStep() {
+ if (g_fredHelpStep < 0 || g_fredHelpStep >= 4)
+ return;
+
+ // Unhighlight previous selected if not hovered...
+ int16 hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+
+ // On even steps, select entry from g_fredPlayButton table; on odd steps, deselect...
+ if (!(g_fredHelpStep & 1)) {
+ int16 idx = g_fredHelpStep >> 1;
+ g_fredCurrentHelpObject = getResolvedPtr(g_fredPlayButton, idx * 4);
+ } else {
+ g_fredCurrentHelpObject = nullptr;
+ }
+
+ g_fredTimer = 0;
+
+ // Highlight new selected entry...
+ hiliteFredHelpObject(g_fredCurrentHelpObject, 1);
+
+ // Start/stop blink timer...
+ if (!(g_fredHelpStep & 1)) {
+ g_fredTimer = _xp->startTimer(500);
+ } else {
+ if (g_fredTimer) {
+ _xp->killTimer(g_fredTimer);
+ g_fredTimer = 0;
+ }
+ }
+
+ g_fredHelpStep++;
+}
+
+bool BoltEngine::spawnBalloon() {
+ // Count active balloons...
+ int16 activeCount = 0;
+ int16 i = 0;
+
+ FredEntityState *entry;
+ while (true) {
+ entry = g_fredEntitiesTable[i];
+ if (!entry)
+ break;
+
+ if (entry->animMode == 0x0B && (entry->flags & 1))
+ activeCount++;
+
+ i++;
+ }
+
+ // Max 6 active balloons at once...
+ if (activeCount >= 6)
+ return false;
+
+ // Find next free balloon slot starting from the search index...
+ int16 searchStart = g_fredBalloonSearchIdx;
+ int16 wrapped = 0;
+ i = searchStart;
+
+ while (true) {
+ entry = g_fredEntitiesTable[i];
+ if (!entry) {
+ wrapped = 1;
+ i = -1;
+ } else if (entry->animMode == 0x0B && (entry->flags & 1) == 0) {
+ // Found free balloon slot!
+ break;
+ }
+
+ i++;
+
+ if (i == searchStart && wrapped)
+ break;
+ }
+
+ // No free slot found...
+ if (i == searchStart && wrapped)
+ return false;
+
+ g_fredBalloonSearchIdx = i + 1;
+
+ // Pick row from shuffle table...
+ int16 row = selectBalloonRow();
+
+ uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+ int16 stride = numCols + 1;
+ int16 rowBase = stride * row;
+
+ // Get next column from shuffle table...
+ byte *shuffleRow = g_fredShuffleTable + rowBase;
+ byte colCounter = shuffleRow[0];
+ byte col = g_fredShuffleTable[rowBase + colCounter + 1];
+
+ // Look up path table: g_fredPathMatrix[row][col]
+ byte *rowPaths = getResolvedPtr(g_fredPathMatrix, row * 4);
+ byte *pathTable = getResolvedPtr(rowPaths, col * 4);
+
+ // Assign path to balloon...
+ entry->pathTable = pathTable;
+
+ // Advance column counter, wrap if needed...
+ shuffleRow[0]++;
+ if (shuffleRow[0] >= numCols)
+ shuffleRow[0] = 0;
+
+ // Init balloon position from path entry 0...
+ entry->pathIndex = 0;
+
+ byte *pathEntry0 = pathTable + 0;
+ int32 startX = (int32)(int16)READ_UINT16(pathEntry0 + 2) << 8;
+ entry->xPos = startX;
+ int32 startY = (int32)(int16)READ_UINT16(pathEntry0 + 4) << 8;
+ entry->yPos = startY;
+
+ // Activate!
+ entry->flags |= 1;
+
+ return true;
+}
+
+int16 BoltEngine::calcBalloonSpawnDelay() {
+ int16 range = READ_UINT16(g_fredLevelPtr + 2);
+ int16 base = READ_UINT16(g_fredLevelPtr + 0);
+ return _xp->getRandom(range) + base;
+}
+
+int16 BoltEngine::selectBalloonRow() {
+ // Get Fred's center X position...
+ int32 fredX = g_fredSprite.xPos >> 8;
+
+ // Get Fred's sprite width/2 to find center...
+ int16 frameIdx = g_fredSprite.frameIndex;
+ byte *spriteDesc = getResolvedPtr(g_fredSprite.animTable, frameIdx * 6);
+ int16 halfWidth = READ_UINT16(spriteDesc + 0x0A) >> 1;
+ int16 fredCenter = (int16)fredX + halfWidth;
+
+ // Find which row Fred is in...
+ int16 row = 0;
+ byte *rowBounds = g_fredRowBounds; // Array of int16 x-values
+ uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
+
+ while (row < numRows) {
+ if (READ_UINT16(rowBounds + row * 2) > fredCenter)
+ break;
+ row++;
+ }
+
+ // Pick random row, rejecting if too close to Fred's row...
+ int16 rowBias = READ_UINT16(g_fredLevelPtr + 8);
+ int16 randomRow;
+ do {
+ randomRow = _xp->getRandom(numRows);
+ } while (randomRow >= row - rowBias && randomRow <= row + rowBias);
+
+ return randomRow;
+}
+
+void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
+ if (mode >= 0x0B && mode < 0x0B + (g_fredLevelPtr ? READ_UINT16(g_fredLevelPtr + 0x0C) : 0)) {
+ state->animMode = 0x0B;
+
+ int16 typeIdx = mode - 0x0B;
+ state->animTable = getResolvedPtr(g_fredBalloonSprite, typeIdx * 4);
+ state->frameIndex = 0;
+ state->frameCountdown = READ_UINT16(g_fredBalloonSprite + typeIdx * 4 + 4);
+ return;
+ }
+
+ if (mode == 0x0A) {
+ // Balloon deactivate...
+ state->animMode = 0x0A;
+ state->flags &= 0xFFFE; // clear active flag
+ return;
+ }
+
+ // Standard Fred animation...
+ state->animMode = mode;
+
+ byte *animTable = g_fredSprites[mode];
+ state->animTable = animTable;
+
+ state->frameIndex = 0;
+ state->frameCountdown = READ_UINT16(animTable + 4);
+
+ // Play sound based on mode...
+ switch (mode) {
+ case 3: // turn right
+ playFredSound(&g_fredSounds[2], nullptr);
+ break;
+ case 4: // run right
+ playFredSound(&g_fredSounds[0], &g_fredSounds[0]);
+ break;
+ case 5: // catch right
+ playFredSound(&g_fredSounds[3], nullptr);
+ break;
+ case 7: // turn left
+ playFredSound(&g_fredSounds[2], nullptr);
+ break;
+ case 8: // run left
+ playFredSound(&g_fredSounds[1], &g_fredSounds[1]);
+ break;
+ case 9: // catch left
+ playFredSound(&g_fredSounds[3], nullptr);
+ break;
+ default: // 0, 1, 2, 6
+ playFredSound(nullptr, nullptr);
+ break;
+ }
+}
+
+void BoltEngine::renderFredScene() {
+ _xp->fillDisplay(0, stFront);
+
+ int16 idx = 0;
+
+ while (true) {
+ FredEntityState *entry = g_fredEntitiesTable[idx];
+ if (!entry)
+ break;
+
+ if (entry->flags & 1) { // active
+ int16 x = (int16)(entry->xPos >> 8);
+ int16 y = (int16)(entry->yPos >> 8);
+
+ // Get current frame sprite...
+ byte *sprite = getResolvedPtr(entry->animTable, entry->frameIndex * 6);
+
+ displayPic(sprite, x, y, stFront);
+
+ // If balloon (mode 0x0B), draw string below...
+ if (entry->animMode == 0x0B) {
+ displayPic(g_fredBalloonString, x + 10, y + 25, stFront);
+ }
+ }
+
+ idx++;
+ }
+}
+
+void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+ soundInfo->data = memberAddr(lib, memberId);
+ soundInfo->size = memberSize(lib, memberId);
+}
+
+void BoltEngine::playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop) {
+ g_fredPendingOneShot = oneShot;
+ g_fredPendingLoop = loop;
+
+ if (g_fredLoopSound != nullptr || loop == nullptr) {
+ // Check if current loop is g_fredSounds[2] and new one-shot is g_fredSounds[1]
+ // (sound transition check)
+ if (g_fredCurrentSound == &g_fredSounds[3] && g_fredPendingOneShot == &g_fredSounds[2]) {
+ // Allow transition without stopping...
+ } else {
+ g_fredLoopSound = nullptr;
+ g_fredCurrentSound = nullptr;
+ _xp->stopSound();
+ }
+ }
+
+ // If there's a pending sound and nothing currently playing, trigger it!
+ if (g_fredPendingOneShot != nullptr && g_fredCurrentSound == nullptr) {
+ updateFredSound();
+ }
+}
+
+void BoltEngine::updateFredSound() {
+ bool startLoop = false;
+
+ if (g_fredLoopSound != nullptr) {
+ // Loop sound active, continue with it...
+ g_fredCurrentSound = g_fredLoopSound;
+ } else if (g_fredPendingOneShot != nullptr) {
+ // Start pending one-shot...
+ g_fredCurrentSound = g_fredPendingOneShot;
+ g_fredLoopSound = g_fredPendingLoop;
+ startLoop = (g_fredPendingLoop != nullptr);
+ g_fredPendingLoop = 0;
+ g_fredPendingOneShot = 0;
+ } else {
+ // Nothing to play...
+ g_fredCurrentSound = nullptr;
+ }
+
+ if (g_fredCurrentSound != nullptr) {
+ // Play the current sound...
+ FredSoundInfo *snd = g_fredCurrentSound;
+ _xp->playSound(snd->data, snd->size, 22050);
+
+ if (startLoop) {
+ // Queue loop sound twice...
+ FredSoundInfo *loopSnd = g_fredLoopSound;
+ _xp->playSound(loopSnd->data, loopSnd->size, 22050);
+ _xp->playSound(loopSnd->data, loopSnd->size, 22050);
+ }
+ }
+}
} // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 4d8ae6f4c1c..c84fac76c80 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -644,6 +644,9 @@ BOLTCallback BoltEngine::g_defaultMemberFreeCallbacks[25];
BOLTCallback BoltEngine::g_defaultGroupLoadCallbacks[25];
BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
+BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
+BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
+
void BoltEngine::noOpCb() {}
void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
void BoltEngine::swapPicHeaderCb() { ((BoltEngine *)g_engine)->swapPicHeader(); }
@@ -654,7 +657,14 @@ void BoltEngine::swapFirstFourWordsCb() { ((BoltEngine *)g_engine)->swapFirstFou
void BoltEngine::swapSpriteHeaderCb() { ((BoltEngine *)g_engine)->swapSpriteHeader(); }
void BoltEngine::freeSpriteCleanUpCb() { ((BoltEngine *)g_engine)->freeSpriteCleanUp(); }
+void BoltEngine::resolveAllRefsCb() { ((BoltEngine *)g_engine)->resolveAllRefs(); }
+void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimEntry(); }
+void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
+void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
+
void BoltEngine::initCallbacks() {
+ // --- BOOTHS ---
+
for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
g_defaultTypeLoadCallbacks[i] = noOpCb;
}
@@ -688,12 +698,42 @@ void BoltEngine::initCallbacks() {
g_defaultGroupFreeCallbacks[i] = noOpCb;
}
- g_resourceDefaultCallbacks.typeLoadCallbacks = g_defaultTypeLoadCallbacks;
- g_resourceDefaultCallbacks.typeFreeCallbacks = g_defaultTypeFreeCallbacks;
- g_resourceDefaultCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
- g_resourceDefaultCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
- g_resourceDefaultCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
- g_resourceDefaultCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+ g_boothsBoltCallbacks.typeLoadCallbacks = g_defaultTypeLoadCallbacks;
+ g_boothsBoltCallbacks.typeFreeCallbacks = g_defaultTypeFreeCallbacks;
+ g_boothsBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_boothsBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_boothsBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_boothsBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
+ // --- FRED ---
+
+ for (int i = 0; i < ARRAYSIZE(g_fredTypeLoadCallbacks); i++) {
+ g_fredTypeLoadCallbacks[i] = noOpCb;
+ }
+
+ g_fredTypeLoadCallbacks[2] = swapAllWordsCb;
+ g_fredTypeLoadCallbacks[6] = resolveAllRefsCb;
+ g_fredTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_fredTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_fredTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+ g_fredTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+ g_fredTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+ g_fredTypeLoadCallbacks[25] = swapFredAnimEntryCb;
+ g_fredTypeLoadCallbacks[26] = swapFredAnimDescCb;
+ g_fredTypeLoadCallbacks[27] = swapFredLevelDescCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_fredTypeFreeCallbacks); i++) {
+ g_fredTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_fredTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ g_fredBoltCallbacks.typeLoadCallbacks = g_fredTypeLoadCallbacks;
+ g_fredBoltCallbacks.typeFreeCallbacks = g_fredTypeFreeCallbacks;
+ g_fredBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_fredBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
index 78ef3391987..48ae842ef8d 100644
--- a/engines/bolt/xplib/display.cpp
+++ b/engines/bolt/xplib/display.cpp
@@ -525,7 +525,7 @@ void XpLib::compositeToScreen() {
cursorY = g_lastCursorY - g_cursorHotspotY;
// Save background under cursor position...
- clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, -cursorX, -cursorY, nullptr);
+ clipAndBlit(&g_surfaces[stFront].mainPic, &g_cursorBackgroundSave, -cursorX, -cursorY, nullptr);
// Draw cursor sprite at position...
clipAndBlit(&g_cursorSprite, &g_surfaces[stFront].mainPic, cursorX, cursorY, &g_cursorRect);
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 3dc0625d029..873bd4e509e 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -216,6 +216,7 @@ void XpLib::pumpMessages() {
updateTimers();
_bolt->_system->delayMillis(5);
+ _bolt->_system->updateScreen();
}
void XpLib::handleTimer(uint32 timerId) {
@@ -230,13 +231,13 @@ void XpLib::handleTimer(uint32 timerId) {
postEvent(etInactivity, 0);
}
- if (g_cursorBlinkCountdown != 0) {
- g_cursorBlinkCountdown--;
- if (g_cursorBlinkCountdown == 0)
+ if (g_screensaverCountdown != 0) {
+ g_screensaverCountdown--;
+ if (g_screensaverCountdown == 0)
activateScreenSaver();
}
- if (g_inactivityCountdown != 0 || g_cursorBlinkCountdown != 0) {
+ if (g_inactivityCountdown != 0 || g_screensaverCountdown != 0) {
g_inactivityTimerId = startTimer(1000);
} else {
g_inactivityTimerId = 0;
@@ -423,7 +424,7 @@ int16 XpLib::setScreenSaverTimer(int16 seconds) {
int16 prev = g_screenSaverTimerValue;
g_screenSaverTimerValue = seconds;
- g_cursorBlinkCountdown = seconds;
+ g_screensaverCountdown = seconds;
if (seconds != 0 && g_inactivityTimerId == 0)
g_inactivityTimerId = startTimer(1000);
@@ -443,7 +444,7 @@ void XpLib::resetInactivity() {
}
// Reset screensaver countdown
- g_cursorBlinkCountdown = g_screenSaverTimerValue;
+ g_screensaverCountdown = g_screenSaverTimerValue;
if (g_screenSaverTimerValue != 0 && g_inactivityTimerId == 0)
g_inactivityTimerId = startTimer(1000);
diff --git a/engines/bolt/xplib/random.cpp b/engines/bolt/xplib/random.cpp
index 412c0d47861..2063c0e9990 100644
--- a/engines/bolt/xplib/random.cpp
+++ b/engines/bolt/xplib/random.cpp
@@ -25,7 +25,8 @@
namespace Bolt {
int16 XpLib::getRandom(int16 range) {
- return (int16)(((uint32)_bolt->_randomSource.getRandomNumber(UINT_MAX) * (uint32)range) / 0x8000);
+ int16 r = (int16)_bolt->_randomSource.getRandomNumber(0x7FFF); // [0, 32767]
+ return (int16)((int32)r * (int32)(uint16)range / 0x8000L);
}
void XpLib::randomize() {
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index 7cd1672596b..bc30e2012f2 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -381,7 +381,7 @@ protected:
uint32 g_inactivityTimerId = 0;
int16 g_inactivityCountdown = 0;
int16 g_inactivityTimerValue = 0;
- int16 g_cursorBlinkCountdown = 0;
+ int16 g_screensaverCountdown = 0;
int16 g_screenSaverTimerValue = 0;
int16 g_inactivityTimeout = 0;
Commit: 584db066a00ffb29a13503b53a18f74cb632f24f
https://github.com/scummvm/scummvm/commit/584db066a00ffb29a13503b53a18f74cb632f24f
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement TopCat's minigame
Changed paths:
engines/bolt/av.cpp
engines/bolt/bolt.h
engines/bolt/booths/topcat.cpp
engines/bolt/resource.cpp
engines/bolt/xplib/events.cpp
engines/bolt/xplib/sound.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
index ff45768412b..710d13fd6d2 100644
--- a/engines/bolt/av.cpp
+++ b/engines/bolt/av.cpp
@@ -99,6 +99,7 @@ bool BoltEngine::playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, in
// Not in the original, but it helps cleaning up
// if the user wants to quit during a video...
+ assert(shouldQuit());
killRTF(nullptr);
cleanUpAV();
return false;
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index c0f62fb8284..20605cacff7 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -269,6 +269,20 @@ typedef struct FredEntityState {
}
} FredEntityState;
+typedef struct TopCatAnim {
+ int16 animType; // + 00
+ int16 animIndex;// + 02
+ int16 transitionToNextQuestionFlag;// + 04
+ int16 *seqPtr; // + 04
+
+ TopCatAnim() {
+ animType = 0;
+ animIndex = 0;
+ transitionToNextQuestionFlag = 0;
+ seqPtr = nullptr;
+ }
+} TopCatAnim;
+
class BoltEngine : public Engine {
friend class XpLib;
@@ -493,6 +507,7 @@ protected:
static void noOpCb();
static void swapAllWordsCb();
+ static void swapAllLongsCb();
static void swapPicHeaderCb();
static void swapAndResolvePicDescCb();
static void swapFirstWordCb();
@@ -701,7 +716,104 @@ protected:
int16 scoobyGame(int16 prevBooth) { return 0; }
// --- TOPCAT ---
- int16 topCatGame(int16 prevBooth) { return 0; }
+ int16 topCatGame(int16 prevBooth);
+ bool initTopCat();
+ void cleanUpTopCat();
+ int16 playTopCat();
+ int16 handleActionButton(int16 *result);
+ void queueAnim(int16 animType, int16 param);
+ bool maintainAnim(int16 soundEvent);
+ void maintainIdleSound(int16 decrement);
+ bool startNextAnim(int16 *playAnswerAnim);
+ void setAnimType(int16 newType);
+ void highlightObject(byte *entry, int16 highlight);
+ bool setupNextQuestion();
+ void adjustColors(byte *pic, int8 shift);
+ void shuffleTopCatQuestions();
+ void shuffleTopCatPermutations();
+ void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
+ void setScoreLights(int16 litMask);
+ void swapTopCatHelpEntry();
+
+ static void swapTopCatHelpEntryCb();
+
+ RTFResource *g_topCatRtfHandle = nullptr;
+ RTFResource *g_topCatAvHandle = nullptr;
+ BOLTLib *g_topCatBoltLib = nullptr;
+ BOLTCallbacks g_topCatBoltCallbacks;
+
+ static BOLTCallback g_topCatTypeLoadCallbacks[26];
+ static BOLTCallback g_topCatTypeFreeCallbacks[26];
+
+ byte *g_topCatBackgroundPalette = nullptr;
+ byte *g_topCatBackground = nullptr;
+ byte *g_topCatBackgroundAnimationPalette = nullptr;
+ byte *g_topCatGraphicsAssets = nullptr;
+
+ int16 g_topCatBackgroundAnimFrame = 0;
+ int16 g_topCatMaxBackgroundAnimFrames = 0;
+ int16 g_topCatCurrentAnimType = 0;
+ int16 g_topCatAnimStateMachineStep = 0;
+ int16 g_topCatAnimQueueSize = 0;
+ int16 g_topCatQueuedSoundFrames = 0;
+ byte *g_topCatButtonsPalette = nullptr;
+ byte *g_topCatBlinkEntries = nullptr;
+ byte *g_topCatLightsPalette = nullptr;
+ int16 *g_topCatBlinkSeqPtr = nullptr;
+ byte *g_topCatSelectedChoiceOverlayGfx = nullptr;
+ byte *g_topCatCycleData = nullptr;
+ XPCycleState g_topCatCycleSpecs[4];
+ byte *g_topCatShuffledQuestions = nullptr;
+ byte *g_topCatShuffledAnswers = nullptr;
+ byte *g_topCatAnswersPermutations = nullptr;
+ byte *g_topCatAnswers = nullptr;
+ byte *g_topCatAnswersScreenPositions = nullptr;
+ int16 g_topCatSavedScore = 0;
+ int16 g_topCatSaveHistory = 0;
+ const char *g_topCatSaveFileName = "TopCatBF";
+ const char *g_topCatStaticSaveFileName = "TopCatBFStatic";
+ int16 g_topCatScore = 0;
+ int16 g_topCatShuffledQuestionsArrayIdx = 0;
+ uint32 g_topCatBlinkTimer = 0;
+ FredSoundInfo g_topCatSoundInfo;
+ byte g_topCatSavedShuffledQuestions[60] = { 0 };
+ byte g_topCatSavedShuffledAnswers[60] = { 0 };
+ byte g_topCatSavedAnswersPermutations[60 * 3] = { 0 };
+ byte g_topCatSaveBuffer[302] = { 0 };
+ byte *g_topCatHoveredEntry = nullptr;
+ byte *g_topCatHelpButton = nullptr;
+ byte *g_topCatBackButton = nullptr;
+ byte *g_topCatBlinkEntry = nullptr;
+ int16 g_topCatCycleStep = 0;
+ int16 g_topCatDrawnQuestionId = 0;
+ int16 g_topCatCurrentAnswerIdx = 0;
+ XPCycleState g_topCatChoiceCycleState[4];
+ TopCatAnim g_topCatAnimQueue[3];
+ int16 g_topCatCorrectAnimIdx = 0;
+ int16 g_topCatWrongAnimIdx = 0;
+ int16 g_topCatShouldPlayAnswerAnim = 0;
+ int16 g_topCatMaintainSoundFlag = 0;
+ int16 g_topCatPermTableA[3] = { 0, 1, 2 };
+ int16 g_topCatPermTableB[3] = { 0, 1, 2 };
+ int16 g_topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
+ int16 g_topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
+ int16 g_topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
+ int16 g_topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
+ int16 g_topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
+ int16 g_topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
+ int16 g_topCatBlinkSeq6[25] = {
+ 0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+ 0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+ 0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+ 0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+ 0x3F
+ };
+
+ int16 *g_topCatBlinkSeqTable[7] = {
+ g_topCatBlinkSeq0, g_topCatBlinkSeq1, g_topCatBlinkSeq2,
+ g_topCatBlinkSeq3, g_topCatBlinkSeq4, g_topCatBlinkSeq5,
+ g_topCatBlinkSeq6
+ };
// --- YOGI ---
int16 yogiGame(int16 prevBooth) { return 0; }
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
index d0e5b38e0bf..133a89ca72d 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/booths/topcat.cpp
@@ -23,6 +23,1132 @@
namespace Bolt {
+int16 Bolt::BoltEngine::topCatGame(int16 prevBooth) {
+ int16 savedTimer = _xp->setInactivityTimer(30);
+ if (!initTopCat()) {
+ cleanUpTopCat();
+ return 8;
+ }
+
+ int16 result = playTopCat();
+ cleanUpTopCat();
+
+ _xp->setInactivityTimer(savedTimer);
+ return result;
+}
+
+bool BoltEngine::initTopCat() {
+ g_topCatRtfHandle = openRTF(assetPath(g_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
+ if (!g_topCatRtfHandle)
+ return false;
+
+ g_topCatAvHandle = openRTF(assetPath("topcatau.av"));
+ if (!g_topCatAvHandle)
+ return false;
+
+ if (!openBOLTLib(&g_topCatBoltLib, &g_topCatBoltCallbacks, assetPath("topcat.blt")))
+ return false;
+
+ if (!getBOLTGroup(g_topCatBoltLib, 0, 1))
+ return false;
+
+ g_topCatBackgroundPalette = memberAddr(g_topCatBoltLib, 0x20);
+ g_topCatBackground = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x22 : 0x21);
+ g_topCatBackgroundAnimationPalette = memberAddr(g_topCatBoltLib, 0x00);
+ g_topCatGraphicsAssets = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x1E : 0x1D);
+
+ g_topCatBackgroundAnimFrame = 0;
+
+ // Count background anim frames...
+ g_topCatMaxBackgroundAnimFrames = 0;
+ while (getResolvedPtr(g_topCatGraphicsAssets, g_topCatMaxBackgroundAnimFrames * 4) != 0) {
+ g_topCatMaxBackgroundAnimFrames++;
+ }
+
+ g_topCatCurrentAnimType = 0;
+ g_topCatAnimStateMachineStep = 0;
+ g_topCatQueuedSoundFrames = 0;
+ g_topCatAnimQueueSize = 0;
+
+ g_topCatButtonsPalette = memberAddr(g_topCatBoltLib, 0x3D);
+ g_topCatBlinkEntries = memberAddr(g_topCatBoltLib, 0x3E);
+
+ g_topCatLightsPalette = memberAddr(g_topCatBoltLib, 0x31);
+ g_topCatBlinkSeqPtr = nullptr;
+
+ g_topCatSelectedChoiceOverlayGfx = memberAddr(g_topCatBoltLib, 0x26);
+ g_topCatCycleData = memberAddr(g_topCatBoltLib, 0x27);
+ boltCycleToXPCycle(g_topCatCycleData, g_topCatCycleSpecs);
+
+ g_topCatChoiceCycleState->startIndex = 0;
+ g_topCatChoiceCycleState->endIndex = 0;
+ g_topCatChoiceCycleState->delay = 0;
+ g_topCatChoiceCycleState->nextFire = 0;
+ g_topCatChoiceCycleState->active = false;
+
+ g_topCatShuffledQuestions = memberAddr(g_topCatBoltLib, 0x32); // [60]
+ g_topCatShuffledAnswers = memberAddr(g_topCatBoltLib, 0x33); // [60]
+ g_topCatAnswersPermutations = memberAddr(g_topCatBoltLib, 0x34); // [60 * 3]
+ g_topCatAnswers = memberAddr(g_topCatBoltLib, 0x35);
+ g_topCatAnswersScreenPositions = memberAddr(g_topCatBoltLib, 0x36);
+
+ if (vLoad(&g_topCatSavedScore, g_topCatSaveFileName)) {
+ g_topCatScore = g_topCatSavedScore;
+ } else {
+ g_topCatScore = 0;
+ }
+
+ if (vLoad(g_topCatSaveBuffer, g_topCatStaticSaveFileName)) {
+ g_topCatShuffledQuestionsArrayIdx = READ_BE_INT16(g_topCatSaveBuffer);
+
+ int offset;
+ for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+ offset = sizeof(int16) + i;
+ g_topCatSavedShuffledQuestions[i] = g_topCatSaveBuffer[offset];
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
+ offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ g_topCatSavedShuffledAnswers[i] = g_topCatSaveBuffer[offset];
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
+ offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ g_topCatSavedAnswersPermutations[i] = g_topCatSaveBuffer[offset];
+ }
+
+ int16 answerOff = 0;
+ for (int16 i = 0; i < 60; i++) {
+ g_topCatShuffledQuestions[i] = g_topCatSavedShuffledQuestions[i];
+ g_topCatShuffledAnswers[i] = g_topCatSavedShuffledAnswers[i];
+
+ for (int16 j = 0; j < 3; j++) {
+ g_topCatAnswersPermutations[answerOff + j] = g_topCatSavedAnswersPermutations[answerOff + j];
+ }
+
+ answerOff += 3;
+ }
+ } else {
+ g_topCatShuffledQuestionsArrayIdx = -1;
+ shuffleTopCatQuestions();
+ shuffleTopCatPermutations();
+ }
+
+ g_topCatBlinkTimer = 0;
+
+ getTopCatSoundInfo(g_topCatBoltLib, 0x1F, &g_topCatSoundInfo);
+
+ uint32 dummy;
+ while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+
+ displayColors(g_topCatBackgroundPalette, stFront, 0);
+ displayPic(g_topCatBackground, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+
+ _xp->setTransparency(true);
+ displayColors(g_topCatBackgroundAnimationPalette, stFront, 0);
+ displayPic(g_topCatBackground, g_displayX, g_displayY, stBack);
+
+ byte *firstBackgroundAnimFrame = getResolvedPtr(g_topCatGraphicsAssets, 0);
+ displayPic(firstBackgroundAnimFrame, g_displayX, g_displayY, stFront);
+
+ displayColors(g_topCatBackgroundPalette, stBack, 0);
+
+ if (!setupNextQuestion())
+ return false;
+
+ _xp->updateDisplay();
+
+ g_topCatHoveredEntry = nullptr;
+
+ // Init button palettes...
+ int16 off = 0;
+ byte *entry;
+ while (true) {
+ entry = getResolvedPtr(g_topCatButtonsPalette, off);
+ if (!entry)
+ break;
+
+ if (READ_UINT32(entry) == 1)
+ g_topCatHelpButton = entry;
+
+ if (READ_UINT32(entry) == 2)
+ g_topCatBackButton = entry;
+
+ _xp->getPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x10), entry + 0x2E);
+ unpackColors(READ_UINT16(entry + 0x10), entry + 0x12);
+
+ off += 4;
+ }
+
+ // Init light palelttes...
+ int16 lightOff = 0;
+ for (int16 i = 0; i < 6; i++) {
+ byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, lightOff);
+
+ int16 colorCount = READ_UINT16(lightEntry + 4);
+ int16 palStart = READ_UINT16(lightEntry);
+ _xp->getPalette(palStart + 0x80, colorCount, lightEntry + colorCount * 4 + 8);
+
+ for (int16 j = 0; j < colorCount; j++) {
+ WRITE_UINT32(lightEntry + j * 4 + 8, READ_BE_UINT32(lightEntry + j * 4 + 8));
+ }
+
+ unpackColors(colorCount, lightEntry + 8);
+
+ lightOff += 4;
+ }
+
+ // Set lights based on saved score...
+ int16 litMask = (1 << g_topCatScore) - 1;
+ setScoreLights(litMask);
+
+ return true;
+}
+
+void BoltEngine::cleanUpTopCat() {
+ _xp->stopSound();
+ _xp->stopCycle();
+
+ if (g_topCatScore < 6) {
+ byte questionIdx = g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
+ g_topCatShuffledAnswers[questionIdx]--;
+ g_topCatShuffledQuestionsArrayIdx--;
+ } else {
+ g_topCatScore = 0;
+ }
+
+ g_topCatSavedScore = g_topCatScore;
+ g_topCatSaveHistory = g_topCatShuffledQuestionsArrayIdx;
+
+ int16 answerOff = 0;
+ for (int16 i = 0; i < 60; i++) {
+ g_topCatSavedShuffledQuestions[i] = g_topCatShuffledQuestions[i];
+ g_topCatSavedShuffledAnswers[i] = g_topCatShuffledAnswers[i];
+
+ for (int16 j = 0; j < 3; j++) {
+ g_topCatSavedAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + j];
+ }
+
+ answerOff += 3;
+ }
+
+ memset(g_topCatSaveBuffer, 0, sizeof(g_topCatSaveBuffer));
+
+ WRITE_BE_INT16(g_topCatSaveBuffer, g_topCatSaveHistory);
+
+ int offset;
+ for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+ offset = sizeof(int16) + i;
+ g_topCatSaveBuffer[offset] = g_topCatSavedShuffledQuestions[i];
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
+ offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ g_topCatSaveBuffer[offset] = g_topCatSavedShuffledAnswers[i];
+ }
+
+ for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
+ offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ g_topCatSaveBuffer[offset] = g_topCatSavedAnswersPermutations[i];
+ }
+
+ vSave(&g_topCatSavedScore, sizeof(g_topCatSavedScore), g_topCatSaveFileName);
+ vSave(g_topCatSaveBuffer, sizeof(g_topCatSaveBuffer), g_topCatStaticSaveFileName);
+
+ freeBOLTGroup(g_topCatBoltLib, 0, 1);
+ closeBOLTLib(&g_topCatBoltLib);
+
+ if (g_topCatRtfHandle) {
+ closeRTF(g_topCatRtfHandle);
+ g_topCatRtfHandle = nullptr;
+ }
+
+ if (g_topCatAvHandle) {
+ closeRTF(g_topCatAvHandle);
+ g_topCatAvHandle = nullptr;
+ }
+
+ // These currently erroneously clear some graphics while on the "win a letter"
+ // animation, unsure why the issue is not on the original...
+#if 0
+ _xp->fillDisplay(0, stFront);
+ _xp->updateDisplay();
+#endif
+}
+
+int16 BoltEngine::playTopCat() {
+ int16 result = 0;
+ int16 exitFlag = 0;
+
+ setAnimType(1);
+ queueAnim(3, 0); // Play question audio
+
+ int16 cursorX = 120;
+ int16 cursorY = 192;
+ _xp->setCursorPos(cursorY, cursorX);
+ _xp->setCursorColor(0x00, 0xFF, 0xFF);
+ _xp->showCursor();
+
+ while (true) {
+ uint32 eventData;
+ byte *soundDataPtr;
+ int16 eventType = _xp->getEvent(etEmpty, &eventData, &soundDataPtr);
+
+ switch (eventType) {
+ case etEmpty: {
+ if (!maintainAnim(0)) {
+ return 8;
+ }
+
+ break;
+ }
+
+ case etTimer: { // Timer expired
+ if (eventData != g_topCatBlinkTimer)
+ break;
+
+ if (!g_topCatBlinkEntry)
+ break;
+
+ g_topCatBlinkTimer = _xp->startTimer(500);
+
+ // Toggle highlight...
+ int16 highlight;
+ if (READ_UINT16(g_topCatBlinkEntry + 0x0C) & 1) {
+ highlight = 0;
+ } else {
+ highlight = 1;
+ }
+
+ highlightObject(g_topCatBlinkEntry, highlight);
+ break;
+ }
+
+ case etMouseMove: {
+ cursorY = (int16)(eventData >> 16);
+ cursorX = (int16)(eventData & 0xFFFF);
+
+ // Hit-test against buttons...
+ int16 entryIdx = 0;
+ byte *entry = nullptr;
+ int16 off = 0;
+
+ while (true) {
+ entry = getResolvedPtr(g_topCatButtonsPalette, off);
+ if (!entry)
+ break;
+
+ int16 entryY = READ_UINT16(entry + 4);
+ int16 entryX = READ_UINT16(entry + 6);
+ int16 entryH = READ_UINT16(entry + 8);
+ int16 entryW = READ_UINT16(entry + 0x0A);
+
+ if (cursorY > entryY &&
+ cursorY < entryY + entryH &&
+ cursorX > entryX &&
+ cursorX < entryX + entryW)
+ break;
+
+ off += 4;
+ entryIdx++;
+ }
+
+ // Unhighlight previous entry if needed...
+ if (g_topCatHoveredEntry != entry &&
+ g_topCatHoveredEntry != nullptr &&
+ g_topCatHoveredEntry != g_topCatBlinkEntry &&
+ g_topCatHoveredEntry != g_topCatBackButton) {
+
+ if (g_topCatHoveredEntry == g_topCatHelpButton) {
+ if (g_topCatCurrentAnimType != 2)
+ highlightObject(g_topCatHoveredEntry, 0);
+ } else {
+ byte *hovered = g_topCatHoveredEntry;
+ if (READ_UINT32(hovered) < 3 || READ_UINT32(hovered) >= 6)
+ highlightObject(g_topCatHoveredEntry, 0);
+ }
+ }
+
+ g_topCatHoveredEntry = entry;
+
+ // Highlight new entry if applicable...
+ if (!entry)
+ break;
+
+ if (entry == g_topCatBlinkEntry)
+ break;
+
+ if (entry == g_topCatBackButton)
+ break;
+
+ if (READ_UINT32(entry) >= 3 && READ_UINT32(entry) < 6)
+ break;
+
+ highlightObject(g_topCatHoveredEntry, 1);
+ break;
+ }
+
+ case etMouseDown: {
+ if (exitFlag)
+ break;
+
+ exitFlag = handleActionButton(&result);
+ break;
+ }
+
+ case etSound: {
+ int16 soundType;
+ if (soundDataPtr >= g_topCatSoundInfo.data && soundDataPtr < g_topCatSoundInfo.data + g_topCatSoundInfo.size) {
+ soundType = 1;
+ } else {
+ soundType = 2;
+ }
+
+ if (!maintainAnim(soundType)) {
+ return 8;
+ }
+
+ break;
+ }
+
+ case etInactivity: {
+ // Toggle blinking highlight...
+ int16 highlight;
+ if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+ g_topCatBlinkEntry != 0) {
+ if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ highlight = 1;
+ } else {
+ highlight = 0;
+ }
+ } else {
+ highlight = 0;
+ }
+
+ highlightObject(g_topCatBlinkEntry, highlight);
+
+ // Set help button as new blinking entry...
+ g_topCatBlinkEntry = g_topCatHelpButton;
+
+ if (g_topCatHelpButton != 0) {
+ if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ g_topCatBlinkTimer = _xp->startTimer(500);
+ } else {
+ g_topCatBlinkTimer = 0;
+ }
+ } else {
+ g_topCatBlinkTimer = 0;
+ }
+
+ highlightObject(g_topCatBlinkEntry, 1);
+ break;
+ }
+
+ case etTrigger: { // Palette cycle event
+ if (g_topCatCurrentAnimType == 2) {
+ // Toggle blinking highlight...
+ int16 highlight;
+ if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+ g_topCatBlinkEntry != 0) {
+ if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ highlight = 1;
+ } else {
+ highlight = 0;
+ }
+ } else {
+ highlight = 0;
+ }
+
+ highlightObject(g_topCatBlinkEntry, highlight);
+
+ // Advance to next blinking entry...
+ if (g_topCatCycleStep & 1) {
+ g_topCatBlinkEntry = nullptr;
+ } else {
+ g_topCatBlinkEntry = getResolvedPtr(g_topCatBlinkEntries, (g_topCatCycleStep >> 1) * 4);
+ }
+
+ if (g_topCatBlinkEntry != 0) {
+ if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ g_topCatBlinkTimer = _xp->startTimer(500);
+ } else {
+ g_topCatBlinkTimer = 0;
+ }
+ } else {
+ g_topCatBlinkTimer = 0;
+ }
+
+ highlightObject(g_topCatBlinkEntry, 1);
+ g_topCatCycleStep++;
+
+ } else if (g_topCatCurrentAnimType == 3) {
+ // Unhighlight previous...
+ if (g_topCatCycleStep > 3) {
+ byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+ highlightObject(prevEntry, 0);
+ } else {
+ highlightObject(g_topCatBackButton, 0);
+ }
+
+ // Highlight next...
+ byte *nextEntry = getResolvedPtr(g_topCatButtonsPalette, g_topCatCycleStep * 4);
+ highlightObject(nextEntry, 1);
+ g_topCatCycleStep++;
+ } else if (g_topCatCurrentAnimType == 6) {
+ // Light blinking animation...
+ int16 mask = READ_UINT16(g_topCatBlinkSeqPtr);
+ g_topCatBlinkSeqPtr++;
+
+ setScoreLights(mask);
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (exitFlag && g_topCatAnimQueueSize == 0 && g_topCatCurrentAnimType == 1) {
+ if (g_topCatScore != 6)
+ _xp->hideCursor();
+
+ return result;
+ }
+ }
+}
+
+int16 BoltEngine::handleActionButton(int16 *result) {
+ int16 prevState = g_topCatCurrentAnimType;
+
+ switch (g_topCatCurrentAnimType) {
+ case 2:
+ case 3:
+ stopAnimation();
+ _xp->stopSound();
+ g_topCatQueuedSoundFrames = 0;
+ setAnimType(1);
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ return 0;
+ default:
+ break;
+ }
+
+ if (g_topCatAnimQueueSize != 0 && g_topCatHoveredEntry != 0) {
+ if (READ_UINT32(g_topCatHoveredEntry) != 0)
+ return 0;
+ }
+
+ if (!g_topCatHoveredEntry)
+ return 0;
+
+ _xp->setInactivityTimer(30);
+
+ // If blinking entry is the help button, stop blinking...
+ if (g_topCatBlinkEntry == g_topCatHelpButton) {
+ int16 highlight;
+ if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+ g_topCatBlinkEntry != 0) {
+ if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ highlight = 1;
+ } else {
+ highlight = 0;
+ }
+ } else {
+ highlight = 0;
+ }
+
+ highlightObject(g_topCatBlinkEntry, highlight);
+ g_topCatBlinkEntry = 0;
+ g_topCatBlinkTimer = 0;
+ highlightObject(g_topCatBlinkEntry, 1);
+ }
+
+ int32 entryType = READ_UINT32(g_topCatHoveredEntry);
+
+ if (entryType == 0) {
+ *result = 8;
+ return 1;
+ }
+
+ if (entryType == 1) {
+ if (prevState == 2)
+ return 0;
+
+ queueAnim(2, 0); // Play help audio
+ return 0;
+ }
+
+ if (entryType == 2) {
+ if (prevState == 3)
+ return 0;
+
+ queueAnim(3, 0); // Play question audio
+ return 0;
+ }
+
+ if (entryType < 3 || entryType >= 6)
+ return 0;
+
+ // Answer choice clicked...
+ int16 answerSlot = (int16)entryType - 3;
+
+ // Display overlay pic on the answer slot...
+ int16 selectedChoiceOverlayX = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4);
+ int16 selectedChoiceOverlayY = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4 + 2);
+ byte *selectedChoiceOverlayPic = getResolvedPtr(g_topCatSelectedChoiceOverlayGfx, answerSlot * 4);
+ displayPic(selectedChoiceOverlayPic, selectedChoiceOverlayX, selectedChoiceOverlayY, stBack);
+
+ _xp->updateDisplay();
+
+ // Start palette cycle on the answer slot overlay...
+ g_topCatChoiceCycleState[0].startIndex = g_topCatCycleSpecs[answerSlot].startIndex;
+ g_topCatChoiceCycleState[0].endIndex = g_topCatCycleSpecs[answerSlot].endIndex;
+ g_topCatChoiceCycleState[0].delay = g_topCatCycleSpecs[answerSlot].delay;
+ g_topCatChoiceCycleState[0].nextFire = g_topCatCycleSpecs[answerSlot].nextFire;
+ g_topCatChoiceCycleState[0].active = g_topCatCycleSpecs[answerSlot].active;
+
+ _xp->startCycle(g_topCatChoiceCycleState);
+
+ // Check answer...
+ int8 correctAnswer = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3];
+
+ if (correctAnswer == answerSlot) {
+ // Correct! :-)
+ queueAnim(4, 0); // Correct answer animation
+ queueAnim(6, 0); // Blink the lights
+
+ g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx] |= 0x80; // Answered!
+ g_topCatScore++;
+
+ if (g_topCatScore == 6) {
+ queueAnim(6, 0); // Blink the lights
+ *result = 0x10;
+ return 1;
+ }
+
+ queueAnim(3, 1); // Transition to next question
+ } else {
+ // Wrong... :-(
+ queueAnim(5, 0); // Wrong answer animation
+ queueAnim(3, 1); // Transition to next question
+ }
+
+ return 0;
+}
+
+void BoltEngine::queueAnim(int16 animType, int16 param) {
+ int16 slot = g_topCatAnimQueueSize;
+ g_topCatAnimQueueSize++;
+
+ TopCatAnim *entry = &g_topCatAnimQueue[slot];
+
+ if (g_topCatAnimQueueSize > 3) {
+ g_topCatAnimQueueSize--;
+ return;
+ }
+
+ entry->animType = animType;
+
+ switch (animType) {
+ case 2: // Help audio
+ entry->animIndex = 0x1F;
+ break;
+
+ case 3: // Question audio
+ entry->transitionToNextQuestionFlag = param;
+ break;
+
+ case 4: // Correct answer
+ entry->animIndex = g_topCatCorrectAnimIdx;
+ g_topCatCorrectAnimIdx++;
+ g_topCatCorrectAnimIdx %= 3;
+ break;
+
+ case 5: // Wrong answer
+ entry->animIndex = g_topCatWrongAnimIdx + 3;
+ g_topCatWrongAnimIdx++;
+ g_topCatWrongAnimIdx %= 3;
+ break;
+
+ case 6: // Score light
+ if (g_topCatScore < 6)
+ entry->animIndex = g_topCatScore + 1;
+ else
+ entry->animIndex = 0;
+
+ // Store blink sequence pointer from table
+ entry->seqPtr = g_topCatBlinkSeqTable[g_topCatScore];
+ break;
+ }
+}
+
+bool BoltEngine::maintainAnim(int16 soundEvent) {
+ int16 animDone = 0;
+
+ switch (g_topCatAnimStateMachineStep) {
+ case 0: // Waiting to start next anim
+ if (g_topCatAnimQueueSize == 0)
+ break;
+
+ if (!startNextAnim(&g_topCatShouldPlayAnswerAnim))
+ return false;
+
+ g_topCatAnimStateMachineStep = 1;
+ break;
+ case 1: // Filling RTF buffer
+ if (fillRTFBuffer()) {
+ if (fillRTFBuffer())
+ break;
+ }
+
+ g_topCatAnimStateMachineStep = 2;
+ break;
+ case 2: // Waiting for sound sync
+ if (!soundEvent)
+ break;
+
+ if (g_topCatMaxBackgroundAnimFrames - g_topCatQueuedSoundFrames != g_topCatBackgroundAnimFrame)
+ break;
+
+ flushRTFSoundQueue();
+ g_topCatAnimStateMachineStep = 3;
+ break;
+ case 3: // Waiting for AV end
+ case 4: // Playing AV
+ if (g_topCatAnimStateMachineStep == 3) {
+ if (soundEvent != 2)
+ break;
+
+ g_topCatAnimStateMachineStep = 4;
+ g_topCatMaintainSoundFlag = 1;
+ }
+
+ int16 avResult;
+ if (g_topCatShouldPlayAnswerAnim != 0)
+ avResult = maintainAV(soundEvent == 2 ? 1 : 0);
+ else
+ avResult = maintainAudioPlay(soundEvent == 2 ? 1 : 0);
+
+ if (!avResult) { // AV finished
+ if (g_topCatShouldPlayAnswerAnim != 0) {
+ // Display background anim frame...
+ byte *animFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
+ displayPic(animFrame, g_displayX, g_displayY, stFront);
+ }
+
+ animDone = 1;
+ } else {
+ // Check if idle sound needs maintaining...
+ if (g_topCatMaintainSoundFlag != 0) {
+ if (!isRTFPlaying()) {
+ int16 savedFrame = g_topCatBackgroundAnimFrame;
+ g_topCatBackgroundAnimFrame = 0;
+ maintainIdleSound(0);
+ g_topCatBackgroundAnimFrame = savedFrame;
+ g_topCatMaintainSoundFlag = 0;
+ }
+ }
+ }
+
+ break;
+ }
+
+ // Advance audio and the background animation together...
+ if (soundEvent == 1 || (soundEvent == 2 && g_topCatShouldPlayAnswerAnim == 0)) {
+ g_topCatBackgroundAnimFrame++;
+ if (g_topCatBackgroundAnimFrame >= g_topCatMaxBackgroundAnimFrames)
+ g_topCatBackgroundAnimFrame = 0;
+
+ if (soundEvent == 1) {
+ if (g_topCatAnimStateMachineStep == 3 || g_topCatAnimStateMachineStep == 4) {
+ g_topCatQueuedSoundFrames--;
+ } else {
+ maintainIdleSound(1);
+ }
+ }
+
+ // If no pending sound event, display the next background anim frame...
+ uint32 peekData;
+ if (_xp->peekEvent(etSound, &peekData) != etSound) {
+ byte *backgroundFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
+ displayPic(backgroundFrame, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+ }
+ }
+
+ if (animDone)
+ setAnimType(1);
+
+ return true;
+}
+
+void BoltEngine::maintainIdleSound(int16 decrement) {
+ byte *soundData = g_topCatSoundInfo.data;
+ int16 chunkSize = (int16)(g_topCatSoundInfo.size / g_topCatMaxBackgroundAnimFrames);
+
+ if (decrement)
+ g_topCatQueuedSoundFrames--;
+
+ int16 idx = g_topCatBackgroundAnimFrame + g_topCatQueuedSoundFrames;
+
+ while (g_topCatQueuedSoundFrames < 10) {
+ g_topCatQueuedSoundFrames++;
+
+ if (idx >= g_topCatMaxBackgroundAnimFrames)
+ idx -= g_topCatMaxBackgroundAnimFrames;
+
+ _xp->playSound(soundData + chunkSize * idx, chunkSize, 22050);
+ idx++;
+ }
+}
+
+bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
+ TopCatAnim *entry = &g_topCatAnimQueue[0];
+ bool startResult = false;
+
+ if (g_topCatAnimQueueSize == 0)
+ return false;
+
+ // Setup next question, if requested...
+ if (entry->animType == 3) {
+ if (entry->transitionToNextQuestionFlag != 0) {
+ if (!setupNextQuestion())
+ return false;
+
+ _xp->updateDisplay();
+ }
+
+ entry->animIndex = g_topCatCurrentAnswerIdx * 60 + g_topCatDrawnQuestionId + 7;
+ }
+
+ int16 animType = entry->animType;
+
+ if (animType == 6) {
+ // Score light animation
+ g_topCatBlinkSeqPtr = entry->seqPtr;
+
+ *playAnswerAnim = 0;
+ startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+ } else if (animType == 3) {
+ // Question audio
+ g_topCatCycleStep = 3;
+
+ *playAnswerAnim = 0;
+ startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+ } else if (animType == 2) {
+ // Help audio
+ g_topCatCycleStep = 0;
+
+ *playAnswerAnim = 0;
+ startResult = startAnimation(g_rtfHandle, entry->animIndex);
+ } else {
+ // Correct/wrong answer animations
+ *playAnswerAnim = 1;
+ setAVBufferSize(0x4B000);
+ startResult = prepareAV(g_topCatRtfHandle, entry->animIndex,
+ g_displayWidth, g_displayHeight,
+ g_displayX, g_displayY);
+ }
+
+ if (startResult) {
+ setAnimType(entry->animType);
+ } else {
+ g_topCatCurrentAnimType = 1;
+ g_topCatAnimStateMachineStep = 0;
+ maintainIdleSound(0);
+ }
+
+ // Shift queue: copy entries 1..n down to 0..n-1
+ for (int16 i = 1; i < g_topCatAnimQueueSize; i++) {
+ g_topCatAnimQueue[i - 1] = g_topCatAnimQueue[i];
+ }
+
+ g_topCatAnimQueueSize--;
+
+ return true;
+}
+
+void BoltEngine::setAnimType(int16 newType) {
+ if (g_topCatCurrentAnimType == newType)
+ return;
+
+ // Clean-up the previous state...
+ switch (g_topCatCurrentAnimType) {
+ case 2: // Was playing the help audio
+ // Stop blinking entry
+ int16 highlight;
+ if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+ g_topCatBlinkEntry != 0) {
+ if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ highlight = 1;
+ } else {
+ highlight = 0;
+ }
+ } else {
+ highlight = 0;
+ }
+
+ highlightObject(g_topCatBlinkEntry, highlight);
+
+ g_topCatBlinkEntry = 0;
+ g_topCatBlinkTimer = 0;
+ highlightObject(g_topCatBlinkEntry, 1);
+ break;
+
+ case 3: // Was playing the question audio
+ if (g_topCatCycleStep >= 3) {
+ byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+ highlightObject(prevEntry, 0);
+ }
+
+ highlightObject(g_topCatBackButton, 0);
+ break;
+
+ case 4: // Correct answer anim
+ break;
+
+ case 5: // Wrong answer anim
+ case 6: // Score lights
+ if (g_topCatScore != 6)
+ _xp->showCursor();
+
+ break;
+
+ default:
+ break;
+ }
+
+ // Setup the next state
+ switch (newType) {
+ case 1: // Idle
+ maintainIdleSound(0);
+ break;
+
+ case 2: // Help audio
+ break;
+
+ case 3: // Question audio
+ highlightObject(g_topCatBackButton, 1);
+ break;
+
+ case 4: // Correct answer anim
+ case 5: // Wrong answer anim
+ _xp->hideCursor();
+ break;
+
+ default:
+ break;
+ }
+
+ g_topCatCurrentAnimType = newType;
+ g_topCatAnimStateMachineStep = 0;
+}
+
+void BoltEngine::highlightObject(byte *entry, int16 highlight) {
+ if (!entry)
+ return;
+
+ if (highlight) {
+ _xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0E), entry + 0x12);
+ WRITE_UINT16(entry + 0x0C, READ_UINT16(entry + 0x0C) | 1);
+ } else {
+ _xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0E), entry + 0x2E);
+ WRITE_UINT16(entry + 0x0C, READ_UINT16(entry + 0x0C) & ~1);
+ }
+
+ _system->updateScreen();
+}
+
+bool BoltEngine::setupNextQuestion() {
+ _xp->stopCycle();
+
+ int16 attempts = 0;
+ while (attempts < 60) {
+ g_topCatShuffledQuestionsArrayIdx++;
+ if (g_topCatShuffledQuestionsArrayIdx >= 60)
+ g_topCatShuffledQuestionsArrayIdx = 0;
+
+ g_topCatDrawnQuestionId = (int8)g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
+
+ if ((g_topCatDrawnQuestionId & 0x80) == 0)
+ break; // Found a question which hasn't been answered yet...
+
+ attempts++;
+ }
+
+ if (attempts >= 60) {
+ g_topCatShuffledQuestionsArrayIdx = -1;
+ shuffleTopCatQuestions();
+ shuffleTopCatPermutations();
+
+ for (int16 i = 0; i < 60; i++) {
+ g_topCatShuffledQuestions[i] &= 0x7F; // Remove "already answered" flag from all questions...
+ }
+
+ return setupNextQuestion();
+ }
+
+ g_topCatShuffledAnswers[g_topCatDrawnQuestionId]++;
+ if (g_topCatShuffledAnswers[g_topCatDrawnQuestionId] >= 3)
+ g_topCatShuffledAnswers[g_topCatDrawnQuestionId] = 0;
+
+ int16 qIdx = g_topCatDrawnQuestionId;
+ int16 permutation = (int8)g_topCatShuffledAnswers[g_topCatDrawnQuestionId];
+ g_topCatCurrentAnswerIdx = (int8)g_topCatAnswersPermutations[qIdx * 3 + permutation];
+
+ int16 groupId = g_topCatDrawnQuestionId * 0x100 + 0x100;
+ if (!getBOLTGroup(g_topCatBoltLib, groupId, 1))
+ return false;
+
+ byte *questionsAnswersGfxTable = memberAddr(g_topCatBoltLib, g_topCatDrawnQuestionId * 0x100 + 0x104);
+
+ // Display question pic...
+ byte *questionPic = getResolvedPtr(questionsAnswersGfxTable, 0);
+ displayPic(questionPic, 50, 38, stBack);
+
+ // Display the three answer choices...
+ int16 tableOff = 0;
+ for (int16 i = 0; i < 3; i++) {
+ int8 answerIdx = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3 + i];
+
+ int16 x = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4);
+ int16 y = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4 + 2);
+
+ int16 colorShift = (answerIdx - i) * 3;
+ byte *answerPic = getResolvedPtr(questionsAnswersGfxTable, tableOff + 4);
+ adjustColors(answerPic, colorShift);
+ displayPic(answerPic, x, y, stBack);
+
+ tableOff += 4;
+ }
+
+ freeBOLTGroup(g_topCatBoltLib, groupId, 1);
+
+ return true;
+}
+
+void BoltEngine::adjustColors(byte *pic, int8 shift) {
+ int16 count = READ_UINT16(pic + 0x0C);
+ byte *data = getResolvedPtr(pic, 0x12);
+
+ while (count) {
+ byte val = *data;
+
+ if (val & 0x7F) {
+ *data += shift;
+ }
+
+ data++;
+
+ if (val & 0x80) {
+ if (*data == 0)
+ count--;
+ data++;
+ }
+ }
+}
+
+void BoltEngine::shuffleTopCatQuestions() {
+ int16 answerOff = 0;
+
+ for (int16 i = 0; i < 60; i++) {
+ int16 randIdx = _xp->getRandom(60);
+
+ byte tmp = g_topCatShuffledQuestions[i];
+ g_topCatShuffledQuestions[i] = g_topCatShuffledQuestions[randIdx];
+ g_topCatShuffledQuestions[randIdx] = tmp;
+
+ for (int16 j = 0; j < 3; j++) {
+ int16 randJ = _xp->getRandom(3);
+
+ byte aTmp = g_topCatAnswersPermutations[answerOff + j];
+ g_topCatAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + randJ];
+ g_topCatAnswersPermutations[answerOff + randJ] = aTmp;
+ }
+
+ answerOff += 3;
+ }
+}
+
+void BoltEngine::shuffleTopCatPermutations() {
+ // Shuffle first permutation table...
+ for (int16 i = 0; i < 3; i++) {
+ int16 randIdx = _xp->getRandom(3);
+
+ int16 tmp = g_topCatPermTableA[i];
+ g_topCatPermTableA[i] = g_topCatPermTableA[randIdx];
+ g_topCatPermTableA[randIdx] = tmp;
+ }
+
+ // Shuffle second permutation table...
+ for (int16 i = 0; i < 3; i++) {
+ int16 randIdx = _xp->getRandom(3);
+
+ int16 tmp = g_topCatPermTableB[i];
+ g_topCatPermTableB[i] = g_topCatPermTableB[randIdx];
+ g_topCatPermTableB[randIdx] = tmp;
+ }
+}
+
+void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+ soundInfo->data = memberAddr(lib, memberId);
+ soundInfo->size = memberSize(lib, memberId);
+}
+
+void BoltEngine::setScoreLights(int16 litMask) {
+ int16 lightIdx = 0;
+ int16 tableOff = 0;
+
+ while (tableOff < 0x18) {
+ byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, tableOff);
+
+ int16 palStart = READ_UINT16(lightEntry) + 0x80;
+ int16 colorCount = READ_UINT16(lightEntry + 4);
+
+ int16 palDataIdx;
+ if (litMask & (1 << lightIdx)) {
+ palDataIdx = 0;
+ } else {
+ palDataIdx = colorCount;
+ }
+
+ byte *palData = lightEntry + (palDataIdx + 2) * 4;
+ _xp->setPalette(colorCount, palStart, palData);
+
+ tableOff += 4;
+ lightIdx++;
+ }
+
+ _system->updateScreen();
+}
+
+void BoltEngine::swapTopCatHelpEntry() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+ int16 offset = 0;
+
+ while (offset < (int32)decompSize) {
+ WRITE_UINT32(data + offset + 0x00, READ_BE_UINT32(data + offset + 0x00));
+ WRITE_UINT16(data + offset + 0x04, READ_BE_UINT16(data + offset + 0x04));
+ WRITE_UINT16(data + offset + 0x06, READ_BE_UINT16(data + offset + 0x06));
+ WRITE_UINT16(data + offset + 0x08, READ_BE_UINT16(data + offset + 0x08));
+ WRITE_UINT16(data + offset + 0x0A, READ_BE_UINT16(data + offset + 0x0A));
+ WRITE_UINT16(data + offset + 0x0C, READ_BE_UINT16(data + offset + 0x0C));
+ WRITE_UINT16(data + offset + 0x0E, READ_BE_UINT16(data + offset + 0x0E));
+ WRITE_UINT16(data + offset + 0x10, READ_BE_UINT16(data + offset + 0x10));
+
+ offset += 0x4A;
+ }
+}
} // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index c84fac76c80..29e024c643e 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -647,8 +647,12 @@ BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
+BOLTCallback BoltEngine::g_topCatTypeLoadCallbacks[26];
+BOLTCallback BoltEngine::g_topCatTypeFreeCallbacks[26];
+
void BoltEngine::noOpCb() {}
void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
+void BoltEngine::swapAllLongsCb() { ((BoltEngine *)g_engine)->swapAllLongs(); }
void BoltEngine::swapPicHeaderCb() { ((BoltEngine *)g_engine)->swapPicHeader(); }
void BoltEngine::swapAndResolvePicDescCb() { ((BoltEngine *)g_engine)->swapAndResolvePicDesc(); }
void BoltEngine::swapFirstWordCb() { ((BoltEngine *)g_engine)->swapFirstWord(); }
@@ -662,6 +666,8 @@ void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimE
void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
+void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
+
void BoltEngine::initCallbacks() {
// --- BOOTHS ---
@@ -734,6 +740,33 @@ void BoltEngine::initCallbacks() {
g_fredBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
+ // --- TOPCAT ---
+ for (int i = 0; i < ARRAYSIZE(g_topCatTypeLoadCallbacks); i++) {
+ g_topCatTypeLoadCallbacks[i] = noOpCb;
+ }
+
+ g_topCatTypeLoadCallbacks[2] = swapAllWordsCb;
+ g_topCatTypeLoadCallbacks[4] = swapAllLongsCb;
+ g_topCatTypeLoadCallbacks[6] = resolveAllRefsCb;
+ g_topCatTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_topCatTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_topCatTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+ g_topCatTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+ g_topCatTypeLoadCallbacks[25] = swapTopCatHelpEntryCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_topCatTypeFreeCallbacks); i++) {
+ g_topCatTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_topCatTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ g_topCatBoltCallbacks.typeLoadCallbacks = g_topCatTypeLoadCallbacks;
+ g_topCatBoltCallbacks.typeFreeCallbacks = g_topCatTypeFreeCallbacks;
+ g_topCatBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_topCatBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_topCatBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_topCatBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
}
} // End of namespace Bolt
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 873bd4e509e..c4e7c39683f 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -53,7 +53,7 @@ void XpLib::shutdownEvents() {
}
}
-int16 XpLib::getEvent(int16 filter, uint32 *outData) {
+int16 XpLib::getEvent(int16 filter, uint32 *outData, byte **outPtrData) {
pumpMessages();
// Search event queue for matching event type...
@@ -75,6 +75,8 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData) {
// Copy event data to caller...
*outData = node->payload;
+ if (outPtrData)
+ *outPtrData = node->payloadPtr;
// Return node to free list...
node->next = g_eventFreeList;
@@ -83,7 +85,7 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData) {
return node->type;
}
-int16 XpLib::peekEvent(int16 filter, uint32 *outData) {
+int16 XpLib::peekEvent(int16 filter, uint32 *outData, byte **outPtrData) {
pumpMessages();
XPEvent *node = g_eventQueueHead;
@@ -140,13 +142,14 @@ void XpLib::enqueueEvent(XPEvent *node) {
void XpLib::pumpMessages() {
Common::Event event;
- uint32 timerData;
+ uint32 dummy = 0;
+ byte *soundDataPtr;
bool mouseMoved = false;
// Drain any pending sound events...
- while (pollSound(&timerData) && !_bolt->shouldQuit()) {
+ while (pollSound(&soundDataPtr) && !_bolt->shouldQuit()) {
updateTimers();
- postEvent(etSound, timerData);
+ postEvent(etSound, dummy, soundDataPtr);
}
cycleColors();
@@ -347,7 +350,7 @@ void XpLib::postJoystickEvent(int16 source, int16 dx, int16 dy) {
}
}
-void XpLib::postEvent(XPEventTypes type, uint32 data) {
+void XpLib::postEvent(XPEventTypes type, uint32 data, byte *ptrData) {
if (type == etMouseMove) {
if (g_lastMouseEventData == data)
return;
@@ -387,6 +390,11 @@ void XpLib::postEvent(XPEventTypes type, uint32 data) {
node->type = type;
node->payload = data;
node->next = nullptr;
+
+ if (type == etSound) {
+ node->payloadPtr = ptrData;
+ }
+
enqueueEvent(node);
}
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index 53e8c8d38cb..0d0af282a8b 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -47,8 +47,9 @@ bool XpLib::initSound() {
return true;
}
-bool XpLib::pollSound(uint32 *outData) {
- *outData = 0;
+bool XpLib::pollSound(byte **outData) {
+ if (outData)
+ *outData = nullptr;
if (_sndQueued == 0 || _sndPaused)
return false;
@@ -92,9 +93,16 @@ bool XpLib::pollSound(uint32 *outData) {
if (!ready)
return false;
- *outData = 1; // The original puts the elapsed buffer data in here...
-
// Buffer completed!
+ byte *sourcePtr = nullptr;
+ if (!_bufferSourceQueue.empty()) {
+ sourcePtr = _bufferSourceQueue.front();
+ _bufferSourceQueue.pop();
+ }
+
+ if (outData)
+ *outData = sourcePtr;
+
_sndCompletedCount--;
_sndQueued--;
@@ -136,9 +144,10 @@ bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
// Drain completed buffers if full...
int retries = 0;
while (_audioStream->numQueuedStreams() >= 50) {
- uint32 eventData;
+ byte *eventData;
+ uint32 dummy = 0;
if (pollSound(&eventData))
- postEvent(etSound, eventData);
+ postEvent(etSound, dummy, eventData);
_bolt->_system->delayMillis(1);
if (++retries > 5000) {
@@ -168,6 +177,7 @@ bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
_sndQueued++;
_durationQueue.push(durationMs);
+ _bufferSourceQueue.push(data);
return true;
}
@@ -207,6 +217,9 @@ bool XpLib::stopSound() {
while (!_durationQueue.empty())
_durationQueue.pop();
+ while (!_bufferSourceQueue.empty())
+ _bufferSourceQueue.pop();
+
uint32 dummy;
while (getEvent(etSound, &dummy) == etSound);
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index bc30e2012f2..85a034f5e61 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -66,13 +66,13 @@ struct XPCycleState {
};
typedef struct XPPicDesc {
- byte *pixelData; // +0x00: pixel buffer (8bpp)
- int16 width; // +0x04: image width
- int16 height; // +0x06: image height
- byte *palette; // +0x08: RGB palette data
- int16 paletteStart; // +0x0C: first palette index
- int16 paletteCount; // +0x0E: number of palette entries
- int16 flags; // +0x10: bit 0 = transparent, bit 1 = RLE
+ byte *pixelData;
+ int16 width;
+ int16 height;
+ byte *palette;
+ int16 paletteStart;
+ int16 paletteCount;
+ int16 flags;
XPPicDesc() {
pixelData = nullptr;
@@ -93,14 +93,14 @@ enum XPSurfaceType : int {
typedef struct XPSurface {
XPPicDesc mainPic;
XPPicDesc overlayPic;
- int16 dirtyPalStart; // +0x24
- int16 dirtyPalEnd; // +0x26
+ int16 dirtyPalStart;
+ int16 dirtyPalEnd;
XPSurface() {
dirtyPalStart = 0;
dirtyPalEnd = 0;
}
-} XPSurface; // total: 0x28 = 40 bytes
+} XPSurface;
enum XPEventTypes : int16 {
etEmpty = 0,
@@ -129,12 +129,14 @@ typedef struct XPEvent {
XPEvent *next;
XPEventTypes type;
uint32 payload;
+ byte *payloadPtr;
XPEvent() {
prev = nullptr;
next = nullptr;
type = etEmpty;
payload = 0;
+ payloadPtr = nullptr;
}
} XPEvent;
@@ -182,9 +184,9 @@ public:
void updateCursorPosition();
// Events
- int16 getEvent(int16 filter, uint32 *outData);
- int16 peekEvent(int16 filter, uint32 *outData);
- void postEvent(XPEventTypes type, uint32 data);
+ int16 getEvent(int16 filter, uint32 *outData, byte **outPtrData = nullptr);
+ int16 peekEvent(int16 filter, uint32 *outData, byte **outPtrData = nullptr);
+ void postEvent(XPEventTypes type, uint32 data, byte *ptrData = nullptr);
int16 setInactivityTimer(int16 seconds);
int16 setScreenSaverTimer(int16 seconds);
bool enableController();
@@ -213,7 +215,6 @@ public:
void freeMem(void *mem);
// Sound
- void waveCb();
bool playSound(byte *data, uint32 size, int16 sampleRate);
bool pauseSound();
bool resumeSound();
@@ -346,7 +347,6 @@ protected:
Common::Rect g_prevOverlayCursorRect;
byte *g_vgaFramebuffer = nullptr;
- //Graphics::Surface *g_videoSurface = nullptr;
byte *g_rowDirtyFlags = nullptr;
byte g_cursorBackgroundSaveBuffer[16 * 16];
@@ -357,7 +357,7 @@ protected:
void fileError(const char *message);
// Sound
- bool pollSound(uint32 *outData);
+ bool pollSound(byte **outData);
bool initSound();
void shutdownSound();
@@ -373,6 +373,7 @@ protected:
int16 _sndSampleRate = 22050;
uint32 _sndNextDeadline = 0;
uint32 _sndBufferQueueTime = 0;
+ Common::Queue<byte *> _bufferSourceQueue;
// Timer
bool initTimer();
Commit: 1ee07b6140949dc6d417c4ee60bb5f30e2d4a474
https://github.com/scummvm/scummvm/commit/1ee07b6140949dc6d417c4ee60bb5f30e2d4a474
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Add support for full Italian version
Changed paths:
engines/bolt/detection_tables.h
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index 7453d983699..6f20dde520f 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -63,6 +63,42 @@ const ADGameDescription gameDescriptions[] = {
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
+ {
+ "carnival",
+ nullptr,
+ {
+ {"BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
+ {"FRED.BLT", 0, "2491420b98c2421e19a751422c223cc8", 302133 },
+ {"GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
+ {"HUCK.BLT", 0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
+ {"SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
+ {"TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
+ {"YOGI.BLT", 0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO1(GAMEOPTION_EXTEND_SCREEN)
+ },
+ {
+ "carnival",
+ nullptr,
+ {
+ {"ASSETS/BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
+ {"ASSETS/FRED.BLT", 0, "2491420b98c2421e19a751422c223cc8", 302133 },
+ {"ASSETS/GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
+ {"ASSETS/HUCK.BLT", 0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
+ {"ASSETS/SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
+ {"ASSETS/TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
+ {"ASSETS/YOGI.BLT", 0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE,
+ GUIO1(GAMEOPTION_EXTEND_SCREEN)
+ },
{
"carnival",
"Demo",
@@ -95,6 +131,7 @@ const ADGameDescription gameDescriptions[] = {
},
AD_TABLE_END_MARKER
+
};
} // End of namespace Bolt
Commit: cc069232f44fa0bdb0110c62b78f3eb74771bdc4
https://github.com/scummvm/scummvm/commit/cc069232f44fa0bdb0110c62b78f3eb74771bdc4
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement Scooby-Doo's minigame
Changed paths:
engines/bolt/bolt.h
engines/bolt/booths/fred.cpp
engines/bolt/booths/scooby.cpp
engines/bolt/booths/topcat.cpp
engines/bolt/resource.cpp
engines/bolt/rtf.cpp
engines/bolt/ssprite.cpp
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 20605cacff7..5c9f70f4025 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -225,17 +225,88 @@ typedef struct RTFPacket {
}
} RTFPacket;
+struct SSprite;
+
+typedef void (*SSpriteUpdateFunc)(void);
+typedef void (*SSpritePathCallback)(SSprite *);
+
+typedef struct SSprite {
+ int16 flags;
+ int16 x;
+ int16 y;
+ int16 frameCount;
+ int16 currentFrame;
+ byte **frameData;
+ SSpriteUpdateFunc updateFunc;
+ SSpritePathCallback pathCallback;
+ int16 frameTimer;
+ int16 frameRate;
+ int16 startFrame;
+ int16 stopFrame;
+ int16 velocityX;
+ int16 velocityY;
+ int16 xLimitLow;
+ int16 xLimitHigh;
+ int16 yLimitLow;
+ int16 yLimitHigh;
+ int16 accelX;
+ int16 accelY;
+ int16 dragX;
+ int16 dragY;
+ byte *pathData;
+ int16 pathOffset;
+ int16 pathLength;
+ int16 collX;
+ int16 collY;
+ int16 collW;
+ int16 collH;
+ int16 userInfo;
+
+ SSprite() {
+ flags = 0;
+ x = 0;
+ y = 0;
+ frameCount = 0;
+ currentFrame = 0;
+ frameData = nullptr;
+ updateFunc = nullptr;
+ pathCallback = nullptr;
+ frameTimer = 0;
+ frameRate = 0;
+ startFrame = 0;
+ stopFrame = 0;
+ velocityX = 0;
+ velocityY = 0;
+ xLimitLow = 0;
+ xLimitHigh = 0;
+ yLimitLow = 0;
+ yLimitHigh = 0;
+ accelX = 0;
+ accelY = 0;
+ dragX = 0;
+ dragY = 0;
+ pathData = nullptr;
+ pathOffset = 0;
+ pathLength = 0;
+ collX = 0;
+ collY = 0;
+ collW = 0;
+ collH = 0;
+ userInfo = 0;
+ }
+} SSprite;
+
// FRED GAME
-typedef struct FredSoundInfo {
+typedef struct SoundInfo {
byte *data;
uint32 size;
- FredSoundInfo() {
+ SoundInfo() {
data = nullptr;
size = 0;
}
-} FredSoundInfo;
+} SoundInfo;
typedef struct FredEntityState {
uint16 flags;
@@ -269,6 +340,83 @@ typedef struct FredEntityState {
}
} FredEntityState;
+// SCOOBY GAME
+
+typedef struct ScoobyState {
+ int16 levelNumber;
+ int16 slotIndex[10];
+ int16 levelComplete;
+ int16 wallStates[25][4];
+ int16 scoobyCell;
+ int16 scoobySavedCell;
+ int16 leftNeighbor;
+ int16 rightNeighbor;
+ int16 upNeighbor;
+ int16 downNeighbor;
+ int16 activeLevel;
+ int16 scoobyX;
+ int16 scoobyY;
+ int16 velocityX;
+ int16 velocityY;
+ int16 targetVelocityX;
+ int16 targetVelocityY;
+ int16 transitionTarget;
+ int16 currentAnim;
+ int16 direction;
+ int16 spriteFrameCount;
+ byte *frameData[6];
+
+ ScoobyState() {
+ levelNumber = 0;
+
+ for (int i = 0; i < 10; i++)
+ slotIndex[i] = 0;
+
+ levelComplete = 0;
+
+ for (int i = 0; i < 25; i++)
+ for (int j = 0; j < 4; j++)
+ wallStates[i][j] = 0;
+
+ scoobyCell = 0;
+ scoobySavedCell = 0;
+ leftNeighbor = 0;
+ rightNeighbor = 0;
+ upNeighbor = 0;
+ downNeighbor = 0;
+ activeLevel = 0;
+ scoobyX = 0;
+ scoobyY = 0;
+ velocityX = 0;
+ velocityY = 0;
+ targetVelocityX = 0;
+ targetVelocityY = 0;
+ transitionTarget = 0;
+ currentAnim = 0;
+ direction = 0;
+ spriteFrameCount = 0;
+
+ for (int i = 0; i < 6; i++)
+ frameData[i] = nullptr;
+ }
+} ScoobyState;
+
+typedef struct ScoobyRect {
+ int16 left;
+ int16 right;
+ int16 top;
+ int16 bottom;
+
+ ScoobyRect() {
+ left = 0;
+ right = 0;
+ top = 0;
+ bottom = 0;
+ }
+} ScoobyRect;
+
+// TOPCAT GAME
+
typedef struct TopCatAnim {
int16 animType; // + 00
int16 animIndex;// + 02
@@ -628,6 +776,49 @@ protected:
byte *g_animRingBuffer = nullptr;
Common::File *g_animFileHandle = nullptr;
+ // SSprite
+ void setUpSSprite(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate, int16 velocityX, int16 velocityY);
+ void animateSSprite(SSprite *sprite, int16 page);
+ void displaySSprite(SSprite *sprite, int16 x, int16 y);
+ void eraseSSprite(SSprite *sprite);
+ void setSSpriteFrames(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate);
+ void setSSpriteDrag(SSprite *sprite, int16 dragX, int16 dragY);
+ void setSSpriteAccel(SSprite *sprite, int16 accelX, int16 accelY);
+ void reverseSSpriteAccel(SSprite *sprite);
+ void addSSpriteAccel(SSprite *sprite, int16 dx, int16 dy);
+ void setSSpriteVelocity(SSprite *sprite, int16 vx, int16 vy);
+ void reverseSSpriteVelocity(SSprite *sprite);
+ void setSSpriteStart(SSprite *sprite, int16 startFrame, int16 x, int16 y);
+ void setSSpriteStop(SSprite *sprite, int16 stopFrame);
+ void setSSpritePath(SSprite *sprite, byte *pathData, int16 pathCount, SSpritePathCallback callback);
+ bool inSSprite(SSprite *sprite, int16 x, int16 y);
+ bool sSpriteCollide(SSprite *spriteA, SSprite *spriteB);
+ void setSSpriteCollision(SSprite *sprite, int16 *bounds);
+ bool sSpriteAlive(SSprite *sprite);
+ void getSSpriteLoc(SSprite *sprite, Common::Point *out);
+ void getSSpriteAccel(SSprite *sprite, int16 *out);
+ void getSSpriteVelocity(SSprite *sprite, int16 *out);
+ void getSSpriteDrag(SSprite *sprite, int16 *out);
+ void setSSpriteXLimit(SSprite *sprite, int16 high, int16 low);
+ void setSSpriteYLimit(SSprite *sprite, int16 high, int16 low);
+ void setSSpriteInfo(SSprite *sprite, int16 info);
+ int16 getSSpriteInfo(SSprite *sprite);
+ void freezeSSprite(SSprite *sprite);
+ void unfreezeSSprite(SSprite *sprite);
+
+ int16 g_spriteCollTempX = 0;
+ int16 g_spriteCollTempY = 0;
+ int16 g_spriteCollTempW = 0;
+ int16 g_spriteCollTempH = 0;
+ int16 g_spriteScreenX = 0;
+ int16 g_spriteScreenY = 0;
+ int16 g_spriteCollTempA[4] = { 0 };
+ int16 g_spriteCollTempB[4] = { 0 };
+ int16 g_spriteScreenAX = 0;
+ int16 g_spriteScreenAY = 0;
+ int16 g_spriteScreenBX = 0;
+ int16 g_spriteScreenBY = 0;
+
// --- MINIGAMES ---
// --- FRED ---
@@ -648,8 +839,8 @@ protected:
int16 selectBalloonRow();
void setFredAnimMode(FredEntityState *state, int16 mode);
void renderFredScene();
- void getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
- void playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop);
+ void getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
+ void playFredSound(SoundInfo *oneShot, SoundInfo *loop);
void updateFredSound();
static void resolveAllRefsCb();
@@ -684,12 +875,12 @@ protected:
byte *g_fredCurrentHelpObject = nullptr;
byte *g_fredHoveredEntry = nullptr;
- FredSoundInfo g_fredSounds[4];
+ SoundInfo g_fredSounds[4];
- FredSoundInfo *g_fredCurrentSound = nullptr;
- FredSoundInfo *g_fredLoopSound = nullptr;
- FredSoundInfo *g_fredPendingOneShot = nullptr;
- FredSoundInfo *g_fredPendingLoop = nullptr;
+ SoundInfo *g_fredCurrentSound = nullptr;
+ SoundInfo *g_fredLoopSound = nullptr;
+ SoundInfo *g_fredPendingOneShot = nullptr;
+ SoundInfo *g_fredPendingLoop = nullptr;
FredEntityState g_fredSprite;
@@ -713,7 +904,79 @@ protected:
int16 huckGame(int16 prevBooth) { return 0; }
// --- SCOOBY ---
- int16 scoobyGame(int16 prevBooth) { return 0; }
+ bool loadScoobyBaseAssets();
+ void cleanUpScoobyBaseAssets();
+ void displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode);
+ void drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame);
+ void drawAllMovingWalls();
+ void animateTransition(int16 memberIdx);
+ void clearPictMSb(byte *pic);
+ void initScoobyLevelGraphics();
+ bool initScoobyLevelAssets();
+ void cleanUpScoobyLevelGraphics();
+ void setScoobySpriteDirection(int16 startMember);
+ void playSoundMapScooby(int16 memberIdx);
+ void playWallSound();
+ void animateWalls();
+ void decideDirection();
+ void updateScoobySound();
+ void setScoobySound(int16 mode);
+ void updateScoobyLocation();
+ void updateScoobyWalls();
+ void updateScoobyDirection(int16 inputDir);
+ void updateScoobyTransition();
+ bool initScoobyLevel();
+ bool resumeScoobyLevel();
+ bool initScooby();
+ void cleanUpScooby();
+ int16 helpScooby();
+ void hiliteScoobyHelpObject(byte *entry, int16 highlight);
+ int16 xpDirToBOLTDir(uint32 xpDir);
+ int16 playScooby();
+ int16 scoobyGame(int16 prevBooth);
+ void swapScoobyHelpEntry();
+ void swapScoobyWordArray();
+
+ static void swapScoobyHelpEntryCb();
+ static void swapScoobyWordArrayCb();
+
+ BOLTLib *g_scoobyBoltLib = nullptr;
+ BOLTCallbacks g_scoobyBoltCallbacks;
+
+ static BOLTCallback g_scoobyTypeLoadCallbacks[28];
+ static BOLTCallback g_scoobyTypeFreeCallbacks[28];
+
+ int16 g_scoobySoundMode = 0;
+ int16 g_scoobySoundPlaying = 0;
+ int16 g_scoobyShowHelp = 1;
+
+ int16 g_scoobyLastInputDir = 0;
+ int16 g_scoobyTransitionFrom = 0;
+ int16 g_scoobyTransitionTarget = 0;
+ int16 g_scoobyTransitionVelX = 0;
+ int16 g_scoobyTransitionVelY = 0;
+ XPPicDesc g_scoobyTempPic;
+ byte g_scoobyGlobalSaveData[0x3C] = { 0 };
+ ScoobyState g_scoobyGameState;
+ SSprite g_scoobySprite;
+ byte *g_scoobyBaseData = nullptr;
+ byte *g_scoobyBgPic = nullptr;
+ byte *g_scoobyWallPicsA[4] = { nullptr };
+ byte *g_scoobyWallPicsB[5] = { nullptr };
+ ScoobyRect g_scoobyCellBounds[25];
+ Common::Point g_scoobyLevelStartXY[25];
+ byte *g_scoobyLevelData = nullptr;
+ int16 g_scoobySelectedGraphicsGroup = 0;
+ int16 g_scoobyDifficulty = 0;
+ int16 g_scoobyLevelCount = 0;
+ int16 g_scoobyMoveRequested = 0;
+ int16 g_scoobyTransitioning = 0;
+ int16 g_scoobyDesiredDir = 0;
+ int16 g_scoobyInputHoldCount = 0;
+ int16 g_scoobyWallAnimating = 0;
+ int16 g_scoobyWallAnimStep = 0;
+ int16 g_scoobyWallsToOpen = 0;
+ int16 g_scoobyWallsToClose = 0;
// --- TOPCAT ---
int16 topCatGame(int16 prevBooth);
@@ -731,7 +994,7 @@ protected:
void adjustColors(byte *pic, int8 shift);
void shuffleTopCatQuestions();
void shuffleTopCatPermutations();
- void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
+ void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
void setScoreLights(int16 litMask);
void swapTopCatHelpEntry();
@@ -775,7 +1038,7 @@ protected:
int16 g_topCatScore = 0;
int16 g_topCatShuffledQuestionsArrayIdx = 0;
uint32 g_topCatBlinkTimer = 0;
- FredSoundInfo g_topCatSoundInfo;
+ SoundInfo g_topCatSoundInfo;
byte g_topCatSavedShuffledQuestions[60] = { 0 };
byte g_topCatSavedShuffledAnswers[60] = { 0 };
byte g_topCatSavedAnswersPermutations[60 * 3] = { 0 };
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index e4a4a69c534..2622d3ec7ee 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -1206,12 +1206,12 @@ void BoltEngine::renderFredScene() {
}
}
-void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
soundInfo->data = memberAddr(lib, memberId);
soundInfo->size = memberSize(lib, memberId);
}
-void BoltEngine::playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop) {
+void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
g_fredPendingOneShot = oneShot;
g_fredPendingLoop = loop;
@@ -1253,12 +1253,12 @@ void BoltEngine::updateFredSound() {
if (g_fredCurrentSound != nullptr) {
// Play the current sound...
- FredSoundInfo *snd = g_fredCurrentSound;
+ SoundInfo *snd = g_fredCurrentSound;
_xp->playSound(snd->data, snd->size, 22050);
if (startLoop) {
// Queue loop sound twice...
- FredSoundInfo *loopSnd = g_fredLoopSound;
+ SoundInfo *loopSnd = g_fredLoopSound;
_xp->playSound(loopSnd->data, loopSnd->size, 22050);
_xp->playSound(loopSnd->data, loopSnd->size, 22050);
}
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index d0e5b38e0bf..011bcbb5d1a 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -21,8 +21,1956 @@
#include "bolt/bolt.h"
+#include "common/memstream.h"
+
namespace Bolt {
+bool BoltEngine::loadScoobyBaseAssets() {
+ if (!getBOLTGroup(g_scoobyBoltLib, 0, 1))
+ return false;
+
+ g_scoobyBaseData = memberAddr(g_scoobyBoltLib, 0);
+ return true;
+}
+
+void BoltEngine::cleanUpScoobyBaseAssets() {
+ freeBOLTGroup(g_scoobyBoltLib, 0, 1);
+}
+
+void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode) {
+ int16 clipW = clipRect[2];
+ int16 clipH = clipRect[3];
+
+ int16 picWidth = READ_UINT16(pic + 0x0A);
+ byte *pixelData = getResolvedPtr(pic, 0x12);
+
+ int32 srcOffset = (clipRect[0] - g_displayX) + picWidth * (clipRect[1] - g_displayY);
+
+ _xp->blit(pixelData + srcOffset, picWidth, g_scoobyTempPic.pixelData, clipW, clipW, clipH);
+
+ g_scoobyTempPic.width = clipW;
+ g_scoobyTempPic.height = clipH;
+ g_scoobyTempPic.palette = nullptr;
+ g_scoobyTempPic.paletteStart = 0;
+ g_scoobyTempPic.paletteCount = 0;
+ g_scoobyTempPic.flags = 0;
+
+ if (*pic & 2)
+ g_scoobyTempPic.flags |= 2;
+
+ _xp->displayPic(&g_scoobyTempPic,
+ READ_UINT16(pic + 6) + offsetX + clipRect[0],
+ READ_UINT16(pic + 8) + offsetY + clipRect[1],
+ displayMode);
+}
+
+void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame) {
+ // Clamp frames to valid range
+ if (picFrame > 5)
+ picFrame = 5;
+
+ if (bgFrame > 4)
+ bgFrame = 4;
+
+ if (picFrame < 0)
+ picFrame = 0;
+
+ if (bgFrame < 0)
+ bgFrame = 0;
+
+ int16 wallMarginX = READ_UINT16(g_scoobyLevelData + 0x0E);
+ int16 wallMarginY = READ_UINT16(g_scoobyLevelData + 0x10);
+
+ int16 posX = 0, posY = 0;
+
+ // Compute position along wall's primary axis
+ switch (direction) {
+ case 0: // up wall
+ posY = g_scoobyCellBounds[cellIdx].top - 1 - wallMarginY;
+ break;
+ case 1: // right wall
+ posX = g_scoobyCellBounds[cellIdx].right - wallMarginX;
+ break;
+ case 2: // down wall
+ posY = g_scoobyCellBounds[cellIdx].bottom - wallMarginY;
+ break;
+ case 3: // left wall
+ posX = g_scoobyCellBounds[cellIdx].left - 1 - wallMarginX;
+ break;
+ default:
+ break;
+ }
+
+ // Draw wall based on orientation
+ switch (direction) {
+ case 1: // right wall (vertical)
+ case 3: // left wall (vertical)
+ {
+ posY = g_scoobyCellBounds[cellIdx].top + wallMarginY + 1;
+
+ byte *basePic = g_scoobyWallPicsA[3];
+ int16 picW = READ_UINT16(basePic + 0x0A);
+ int16 picH = READ_UINT16(basePic + 0x0C);
+
+ int16 clipRect[4];
+ clipRect[0] = posX;
+ clipRect[1] = posY;
+ clipRect[2] = picW;
+ clipRect[3] = picH;
+
+ displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+
+ if (bgFrame != 0) {
+ displayPic(g_scoobyWallPicsA[bgFrame - 1], posX, posY, 1);
+ }
+
+ break;
+ }
+
+ case 0: // up wall (horizontal)
+ case 2: // down wall (horizontal)
+ {
+ posX = g_scoobyCellBounds[cellIdx].left + wallMarginX + 1;
+
+ byte *basePic = g_scoobyWallPicsB[4];
+ int16 picW = READ_UINT16(basePic + 0x0A);
+ int16 picH = READ_UINT16(basePic + 0x0C);
+
+ int16 clipRect[4];
+ clipRect[0] = posX;
+ clipRect[1] = posY;
+ clipRect[2] = picW;
+ clipRect[3] = picH;
+
+ displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+
+ if (picFrame != 0) {
+ displayPic(g_scoobyWallPicsB[picFrame - 1], posX, posY, 1);
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void BoltEngine::drawAllMovingWalls() {
+ ScoobyState *state = &g_scoobyGameState;
+
+ // Phase 1: Draw open passages between adjacent cells (both sides state 2)
+ for (int16 i = 0; i < 25; i++) {
+ int16 col = i % 5;
+
+ // Left wall: this cell's left and left neighbor's right both open
+ if (col != 0) {
+ if (state->wallStates[i][3] == 2 &&
+ state->wallStates[i - 1][1] == 2) {
+ drawMovingWalls(i, 3, 5, 4);
+ }
+ }
+
+ // Right wall: this cell's right and right neighbor's left both open
+ if (col != 4) {
+ if (state->wallStates[i][1] == 2 &&
+ state->wallStates[i + 1][3] == 2) {
+ drawMovingWalls(i, 1, 5, 4);
+ }
+ }
+
+ // Up wall: this cell's up and upper neighbor's down both open
+ if (i > 4) {
+ if (state->wallStates[i][0] == 2 &&
+ state->wallStates[i - 5][2] == 2) {
+ drawMovingWalls(i, 0, 5, 4);
+ }
+ }
+
+ // Down wall: this cell's down and lower neighbor's up both open
+ if (i < 20) {
+ if (state->wallStates[i][2] == 2 &&
+ state->wallStates[i + 5][0] == 2) {
+ drawMovingWalls(i, 2, 5, 4);
+ }
+ }
+ }
+
+ // Phase 2: For non-current cells, draw closed moveable walls (state 1)
+ // Skip if wall connects to current cell and matching border is also 1
+ for (int16 i = 0; i < 25; i++) {
+ if (i == state->scoobyCell)
+ continue;
+
+ for (int16 dir = 0; dir < 4; dir++) {
+ if (state->wallStates[i][dir] != 1)
+ continue;
+
+ bool shouldDraw = true;
+
+ if (i == state->leftNeighbor && dir == 1) {
+ if (state->wallStates[state->scoobyCell][3] == 1)
+ shouldDraw = false;
+ } else if (i == state->rightNeighbor && dir == 3) {
+ if (state->wallStates[state->scoobyCell][1] == 1)
+ shouldDraw = false;
+ } else if (i == state->upNeighbor && dir == 2) {
+ if (state->wallStates[state->scoobyCell][0] == 1)
+ shouldDraw = false;
+ } else if (i == state->downNeighbor && dir == 0) {
+ if (state->wallStates[state->scoobyCell][2] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ drawMovingWalls(i, dir, 5, 4);
+ }
+ }
+
+ // Phase 3: Draw dynamic walls (state 3) for the current cell
+ for (int16 dir = 0; dir < 4; dir++) {
+ if (state->wallStates[state->scoobyCell][dir] == 3) {
+ drawMovingWalls(state->scoobyCell, dir, 5, 4);
+ }
+ }
+}
+
+void BoltEngine::animateTransition(int16 memberIdx) {
+ g_scoobyGameState.spriteFrameCount = 1;
+
+ g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, memberIdx - 1);
+
+ setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+
+ g_scoobyGameState.velocityX = 0;
+ g_scoobyGameState.velocityY = 0;
+}
+
+void BoltEngine::clearPictMSb(byte *pic) {
+ // Check if bit 8 of flags word at pic+0 is set (byte at pic+1, bit 0)
+ if (READ_UINT16(pic) & 0x100)
+ return;
+
+ byte *pixelData = getResolvedPtr(pic, 0x12);
+ uint32 size = (uint32)READ_UINT16(pic + 0x0A) * (uint32)READ_UINT16(pic + 0x0C);
+
+ for (uint32 i = 0; i <= size; i++) {
+ pixelData[i] &= 0x7F;
+ }
+}
+
+void BoltEngine::initScoobyLevelGraphics() {
+ // Get palette from level data
+ byte *palette = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData));
+
+ // Get background pic based on display mode
+ int16 picMember;
+ if (g_displayMode != 0) {
+ picMember = READ_UINT16(g_scoobyLevelData + 4);
+ } else {
+ picMember = READ_UINT16(g_scoobyLevelData + 2);
+ }
+
+ g_scoobyBgPic = memberAddr(g_scoobyBoltLib, picMember);
+ clearPictMSb(g_scoobyBgPic);
+
+ // Drain timer events
+ uint32 dummy;
+ while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+
+ // Display palette to front surface
+ displayColors(palette, stBack, 0);
+
+ // Display background to front surface
+ displayPic(g_scoobyBgPic, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+
+ _xp->setTransparency(true);
+
+ // Display palette to both back surfaces
+ displayColors(palette, stFront, 0);
+ displayColors(palette, stBack, 1);
+
+ // Display background to back surface
+ displayPic(g_scoobyBgPic, g_displayX, g_displayY, stBack);
+
+ drawAllMovingWalls();
+
+ // Setup Scooby sprite
+ g_scoobyGameState.spriteFrameCount = 1;
+
+ int16 spriteMember;
+ if (g_scoobyGameState.direction == 6) {
+ spriteMember = 0x23;
+ } else {
+ spriteMember = 0x22;
+ }
+
+ g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, spriteMember);
+
+ setUpSSprite(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1, 0, 0);
+ setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+ setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
+ displaySSprite(&g_scoobySprite, g_scoobyGameState.scoobyX, g_scoobyGameState.scoobyY);
+ animateSSprite(&g_scoobySprite, 0);
+
+ // Setup palette cycle
+ XPCycleState cycleData[4];
+ byte *cycleMember = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData + 0x0A));
+ boltCycleToXPCycle(cycleMember, cycleData);
+ _xp->startCycle(cycleData);
+
+ _xp->updateDisplay();
+}
+
+bool BoltEngine::initScoobyLevelAssets() {
+ uint32 maxPicSize = 0;
+
+ // Compute graphics group index from level number
+ g_scoobySelectedGraphicsGroup = (g_scoobyGameState.levelNumber - 1) * 0x100 + 0x100;
+
+ if (!getBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1))
+ return false;
+
+ g_scoobyLevelData = memberAddr(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup);
+
+ // Load wall pic set A (4 pics at level data offset +6)
+ for (int16 i = 0; i < 4; i++) {
+ int16 memberIdx = READ_UINT16(g_scoobyLevelData + 6) + i;
+ g_scoobyWallPicsA[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+ clearPictMSb(g_scoobyWallPicsA[i]);
+
+ uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0A) *
+ (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0C);
+ if (picSize > maxPicSize)
+ maxPicSize = picSize;
+ }
+
+ // Load wall pic set B (5 pics at level data offset +8)
+ for (int16 i = 0; i < 5; i++) {
+ int16 memberIdx = READ_UINT16(g_scoobyLevelData + 8) + i;
+ g_scoobyWallPicsB[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+ clearPictMSb(g_scoobyWallPicsB[i]);
+
+ uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0A) *
+ (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0C);
+ if (picSize > maxPicSize)
+ maxPicSize = picSize;
+ }
+
+ // Allocate temp buffer for largest pic
+ g_scoobyTempPic.pixelData = (byte *)_xp->allocMem(maxPicSize);
+ if (!g_scoobyTempPic.pixelData)
+ return false;
+
+ // Determine effective level count (capped at 10)
+ if (g_scoobyGameState.levelNumber > 10) {
+ g_scoobyLevelCount = 10;
+ } else {
+ g_scoobyLevelCount = g_scoobyGameState.levelNumber;
+ }
+
+ // Look up difficulty from global save data
+ int16 levelIdx = g_scoobyLevelCount - 1;
+ int16 globalOffset = levelIdx * 6;
+ int16 saveSlot = g_scoobyGameState.slotIndex[levelIdx];
+ g_scoobyDifficulty = g_scoobyGlobalSaveData[globalOffset + saveSlot * 2];
+
+ // Load wall states for all 25 cells based on difficulty
+ for (int16 i = 0; i < 25; i++) {
+ int16 srcOffset;
+ switch (g_scoobyDifficulty) {
+ case 0:
+ srcOffset = 0x1C;
+ break;
+ case 1:
+ srcOffset = 0xE6;
+ break;
+ case 2:
+ srcOffset = 0x1B0;
+ break;
+ default:
+ continue;
+ }
+
+ for (int16 j = 0; j < 4; j++) {
+ g_scoobyGameState.wallStates[i][j] =
+ READ_UINT16(g_scoobyLevelData + i * 8 + j * 2 + srcOffset);
+ }
+ }
+
+ // Compute cell bounds and start positions for 5x5 grid
+ int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
+ int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
+
+ int16 yPos = READ_UINT16(g_scoobyLevelData + 0x14);
+ for (int16 row = 0; row < 5; row++) {
+ int16 xPos = READ_UINT16(g_scoobyLevelData + 0x12);
+
+ for (int16 col = 0; col < 5; col++) {
+ int16 cellIdx = row * 5 + col;
+
+ g_scoobyCellBounds[cellIdx].left = xPos;
+ g_scoobyCellBounds[cellIdx].right = xPos + cellW - 1;
+ g_scoobyCellBounds[cellIdx].top = yPos;
+ g_scoobyCellBounds[cellIdx].bottom = yPos + cellH - 1;
+
+ g_scoobyLevelStartXY[cellIdx].x = xPos + ((uint16)cellW >> 1);
+ g_scoobyLevelStartXY[cellIdx].y = yPos + ((uint16)cellH >> 1);
+
+ xPos += cellW;
+ }
+
+ yPos += cellH;
+ }
+
+ return true;
+}
+
+void BoltEngine::cleanUpScoobyLevelGraphics() {
+ _xp->stopCycle();
+ freeBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1);
+}
+
+void BoltEngine::setScoobySpriteDirection(int16 startMember) {
+ g_scoobyGameState.spriteFrameCount = 6;
+
+ int16 memberIdx = startMember;
+ for (int16 i = 0; i < 6; i++) {
+ g_scoobyGameState.frameData[i] = memberAddr(g_scoobyBoltLib, memberIdx + i);
+ }
+
+ setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+}
+
+void BoltEngine::playSoundMapScooby(int16 memberIdx) {
+ byte *soundData = getBOLTMember(g_scoobyBoltLib, memberIdx);
+ uint32 soundSize = memberSize(g_scoobyBoltLib, memberIdx);
+
+ if (soundData) {
+ _xp->playSound(soundData, soundSize, 22050);
+ }
+}
+
+void BoltEngine::playWallSound() {
+ if (g_scoobySoundPlaying == 0)
+ return;
+
+ _xp->stopSound();
+
+ int16 soundMember;
+ switch (g_scoobySoundPlaying) {
+ case 1:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x27C);
+ break;
+ case 2:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x27E);
+ break;
+ case 3:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x278);
+ break;
+ case 4:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x27A);
+ break;
+ case 5:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x284);
+ break;
+ case 6:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x286);
+ break;
+ case 7:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x280);
+ break;
+ case 8:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x282);
+ break;
+ case 9:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x288);
+ break;
+ case 10:
+ soundMember = READ_UINT16(g_scoobyLevelData + 0x28A);
+ break;
+ default:
+ return;
+ }
+
+ playSoundMapScooby(soundMember);
+}
+
+void BoltEngine::animateWalls() {
+ // Phase 1: Animate current cell's walls
+ int16 curCell = g_scoobyGameState.scoobyCell;
+
+ for (int16 dir = 0; dir < 4; dir++) {
+ if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
+ // Closed wall, opening animation
+ bool shouldDraw = true;
+
+ switch (dir) {
+ case 0: // up neighbor
+ if (curCell - 5 == g_scoobyGameState.scoobySavedCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][2] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 5;
+
+ break;
+ case 1: // right neighbor
+ if (curCell + 1 == g_scoobyGameState.scoobySavedCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][3] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 1;
+
+ break;
+ case 2: // down neighbor
+ if (curCell + 5 == g_scoobyGameState.scoobySavedCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][0] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 5;
+
+ break;
+ case 3: // left neighbor
+ if (curCell - 1 == g_scoobyGameState.scoobySavedCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][1] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 1;
+
+ break;
+ }
+
+ if (shouldDraw) {
+ drawMovingWalls(curCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+ }
+ } else if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
+ // Dynamic wall, closing animation
+ if (dir == 3 || dir == 1) {
+ g_scoobySoundPlaying = 4;
+ } else {
+ g_scoobySoundPlaying = 8;
+ }
+
+ drawMovingWalls(curCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+ }
+ }
+
+ // Phase 2: Animate saved cell's walls
+ int16 savedCell = g_scoobyGameState.scoobySavedCell;
+
+ for (int16 dir = 0; dir < 4; dir++) {
+ if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
+ // Closed wall in saved cell, closing animation
+ bool shouldDraw = true;
+
+ switch (dir) {
+ case 0: // up neighbor
+ if (savedCell - 5 == g_scoobyGameState.scoobyCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][2] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 6;
+
+ break;
+ case 1: // right neighbor
+ if (savedCell + 1 == g_scoobyGameState.scoobyCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][3] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 2;
+
+ break;
+ case 2: // down neighbor
+ if (savedCell + 5 == g_scoobyGameState.scoobyCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][0] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 6;
+
+ break;
+ case 3: // left neighbor
+ if (savedCell - 1 == g_scoobyGameState.scoobyCell) {
+ if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][1] == 1)
+ shouldDraw = false;
+ }
+
+ if (shouldDraw)
+ g_scoobySoundPlaying = 2;
+
+ break;
+ }
+
+ if (shouldDraw) {
+ drawMovingWalls(savedCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+ }
+ } else if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
+ // Dynamic wall in saved cell, opening animation
+ if (dir == 3 || dir == 1) {
+ g_scoobySoundPlaying = 3;
+ } else {
+ g_scoobySoundPlaying = 7;
+ }
+
+ drawMovingWalls(savedCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+ }
+ }
+
+ // Determine combined sound on first step
+ if (g_scoobyWallAnimStep == 1) {
+ if (g_scoobyWallsToClose == 0 && g_scoobyWallsToOpen > 1) {
+ g_scoobySoundPlaying = 9;
+ } else if (g_scoobyWallsToClose > 1 || (g_scoobyWallsToClose == 1 && g_scoobyWallsToOpen >= 1)) {
+ g_scoobySoundPlaying = 10;
+ }
+
+ playWallSound();
+ }
+}
+
+void BoltEngine::decideDirection() {
+ int16 cell = g_scoobyGameState.scoobyCell;
+ int16 targetDir = g_scoobyGameState.transitionTarget;
+
+ int16 primaryPos, secondaryPos, primaryCenter, secondaryCenter;
+ int16 wallDir;
+
+ switch (targetDir) {
+ case 2: // right
+ case 6: // left
+ primaryPos = g_scoobyGameState.scoobyX;
+ secondaryPos = g_scoobyGameState.scoobyY;
+ primaryCenter = g_scoobyLevelStartXY[cell].x;
+ secondaryCenter = g_scoobyLevelStartXY[cell].y;
+ wallDir = (targetDir == 6) ? 3 : 1;
+
+ break;
+ case 0: // up
+ case 4: // down
+ primaryPos = g_scoobyGameState.scoobyY;
+ secondaryPos = g_scoobyGameState.scoobyX;
+ primaryCenter = g_scoobyLevelStartXY[cell].y;
+ secondaryCenter = g_scoobyLevelStartXY[cell].x;
+ wallDir = (targetDir == 0) ? 0 : 2;
+
+ break;
+ default:
+ return;
+ }
+
+ if (secondaryPos == secondaryCenter) {
+ // Aligned on secondary axis, can move in target direction
+ g_scoobyMoveRequested = 1;
+
+ if (primaryPos == primaryCenter) {
+ // At cell center, check wall
+ if (g_scoobyGameState.wallStates[cell][wallDir] == 2 ||
+ g_scoobyGameState.wallStates[cell][wallDir] == 3) {
+ // Open passage or dynamic wall, stop
+ g_scoobyGameState.targetVelocityX = 0;
+ g_scoobyGameState.targetVelocityY = 0;
+ return;
+ }
+ }
+
+ // Wall closed, set velocity in target direction
+ switch (targetDir) {
+ case 0: // up
+ g_scoobyGameState.targetVelocityX = 0;
+ g_scoobyGameState.targetVelocityY = -3;
+ return;
+ case 2: // right
+ g_scoobyGameState.targetVelocityX = 3;
+ g_scoobyGameState.targetVelocityY = 0;
+ return;
+ case 4: // down
+ g_scoobyGameState.targetVelocityX = 0;
+ g_scoobyGameState.targetVelocityY = 3;
+ return;
+ case 6: // left
+ g_scoobyGameState.targetVelocityX = -3;
+ g_scoobyGameState.targetVelocityY = 0;
+ return;
+ default:
+ return;
+ }
+ } else {
+ // Not aligned on secondary axis, slide toward center
+ g_scoobyMoveRequested = 1;
+
+ if (g_scoobyGameState.wallStates[cell][wallDir] == 2) {
+ g_scoobyGameState.targetVelocityX = 0;
+ g_scoobyGameState.targetVelocityY = 0;
+ return;
+ }
+
+ switch (targetDir) {
+ case 2: // right, slide vertically
+ case 6: // left
+ g_scoobyGameState.targetVelocityX = 0;
+ if (g_scoobyLevelStartXY[cell].y < g_scoobyGameState.scoobyY) {
+ g_scoobyGameState.transitionTarget = 0;
+ g_scoobyGameState.targetVelocityY = -3;
+ } else {
+ g_scoobyGameState.transitionTarget = 4;
+ g_scoobyGameState.targetVelocityY = 3;
+ }
+
+ return;
+ case 0: // up, slide horizontally
+ case 4: // down
+ g_scoobyGameState.targetVelocityY = 0;
+ if (g_scoobyLevelStartXY[cell].x < g_scoobyGameState.scoobyX) {
+ g_scoobyGameState.transitionTarget = 6;
+ g_scoobyGameState.targetVelocityX = -3;
+ } else {
+ g_scoobyGameState.transitionTarget = 2;
+ g_scoobyGameState.targetVelocityX = 3;
+ }
+
+ return;
+ default:
+ return;
+ }
+ }
+}
+
+void BoltEngine::updateScoobySound() {
+ switch (g_scoobySoundMode) {
+ case 0:
+ if (g_scoobySoundPlaying == 0) {
+ _xp->stopSound();
+ }
+
+ break;
+ case 1:
+ playSoundMapScooby(0x24);
+ break;
+ case 2:
+ playSoundMapScooby(0x25);
+ break;
+ default:
+ break;
+ }
+}
+
+void BoltEngine::setScoobySound(int16 mode) {
+ if (mode != g_scoobySoundMode) {
+ g_scoobySoundMode = mode;
+ updateScoobySound();
+ }
+}
+
+void BoltEngine::updateScoobyLocation() {
+ Common::Point loc;
+ getSSpriteLoc(&g_scoobySprite, &loc);
+ g_scoobyGameState.scoobyX = loc.x;
+ g_scoobyGameState.scoobyY = loc.y;
+
+ // Find which cell Scooby is in
+ g_scoobyGameState.scoobyCell = -1;
+ for (int16 i = 0; i < 25; i++) {
+ if (g_scoobyCellBounds[i].left > g_scoobyGameState.scoobyX ||
+ g_scoobyCellBounds[i].right < g_scoobyGameState.scoobyX ||
+ g_scoobyCellBounds[i].top > g_scoobyGameState.scoobyY ||
+ g_scoobyCellBounds[i].bottom < g_scoobyGameState.scoobyY) {
+ continue;
+ }
+
+ // Found cell
+ g_scoobyGameState.scoobyCell = i;
+
+ int16 col = i % 5;
+ g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : i - 1;
+ g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : i + 1;
+ g_scoobyGameState.upNeighbor = (i < 5) ? -1 : i - 5;
+ g_scoobyGameState.downNeighbor = (i > 19) ? -1 : i + 5;
+ break;
+ }
+
+ if (g_scoobyGameState.scoobyCell != -1)
+ return;
+
+ // Scooby is outside all cells, force movement back toward grid
+ int16 gridStartX = READ_UINT16(g_scoobyLevelData + 0x12);
+ int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
+ int16 gridStartY = READ_UINT16(g_scoobyLevelData + 0x14);
+ int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
+
+ if (gridStartX > g_scoobyGameState.scoobyX) {
+ g_scoobyGameState.velocityX = -3;
+ g_scoobyGameState.velocityY = 0;
+ setScoobySpriteDirection(6);
+ } else if (gridStartX + cellW * 5 - 1 < g_scoobyGameState.scoobyX) {
+ g_scoobyGameState.velocityX = 3;
+ g_scoobyGameState.velocityY = 0;
+ setScoobySpriteDirection(0);
+ } else if (gridStartY > g_scoobyGameState.scoobyY) {
+ g_scoobyGameState.velocityX = 0;
+ g_scoobyGameState.velocityY = -3;
+ setScoobySpriteDirection(0x0C);
+ } else if (gridStartY + (cellH - 1) * 5 - 1 < g_scoobyGameState.scoobyY) {
+ g_scoobyGameState.velocityX = 0;
+ g_scoobyGameState.velocityY = 3;
+ setScoobySpriteDirection(0x12);
+ }
+
+ // Scroll Scooby off-screen
+ int16 halfW = READ_UINT16(g_scoobyBaseData + 0x0A) / 2;
+
+ while (true) {
+ if (g_displayX - halfW >= g_scoobyGameState.scoobyX)
+ break;
+
+ if (g_displayX + g_displayWidth + halfW <= g_scoobyGameState.scoobyX)
+ break;
+
+ int16 halfH = READ_UINT16(g_scoobyBaseData + 0x0C) / 2;
+ if (g_displayY - halfH >= g_scoobyGameState.scoobyY)
+ break;
+
+ if (g_displayY + g_displayHeight + halfH <= g_scoobyGameState.scoobyY)
+ break;
+
+ // Still visible, animate one frame
+ setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
+ animateSSprite(&g_scoobySprite, 0);
+ _xp->updateDisplay();
+
+ uint32 dummy;
+ if (_xp->getEvent(etSound, &dummy) == etSound)
+ updateScoobySound();
+
+ getSSpriteLoc(&g_scoobySprite, &loc);
+ g_scoobyGameState.scoobyX = loc.x;
+ g_scoobyGameState.scoobyY = loc.y;
+ }
+
+ // Scooby left the screen, level complete
+ _xp->stopSound();
+ g_scoobyGameState.levelComplete = 1;
+
+ g_scoobyGameState.levelNumber++;
+ if (g_scoobyGameState.levelNumber > 12)
+ g_scoobyGameState.levelNumber = 10;
+
+ // Cycle slot for current level
+ int16 slotIdx = g_scoobyLevelCount - 1;
+ g_scoobyGameState.slotIndex[slotIdx]++;
+ if (g_scoobyGameState.slotIndex[slotIdx] == 3)
+ g_scoobyGameState.slotIndex[slotIdx] = 0;
+}
+
+void BoltEngine::updateScoobyWalls() {
+ if (g_scoobyWallAnimating != 0) {
+ g_scoobyWallAnimStep++;
+ if (g_scoobyWallAnimStep > 5) {
+ g_scoobyWallAnimating = 0;
+ g_scoobyWallAnimStep = 5;
+ }
+
+ animateWalls();
+
+ if (g_scoobyWallAnimating == 0) {
+ g_scoobyGameState.scoobySavedCell = g_scoobyGameState.scoobyCell;
+ }
+
+ return;
+ }
+
+ // Not animating, check if Scooby is at cell center
+ int16 curCell = g_scoobyGameState.scoobyCell;
+ if (g_scoobyLevelStartXY[curCell].x != g_scoobyGameState.scoobyX)
+ return;
+
+ if (g_scoobyLevelStartXY[curCell].y != g_scoobyGameState.scoobyY)
+ return;
+
+ // At cell center, update active level
+ g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+
+ // If same cell as saved, no wall changes needed
+ if (g_scoobyGameState.scoobyCell == g_scoobyGameState.scoobySavedCell)
+ return;
+
+ // Detect wall changes between current and saved cells
+ g_scoobyWallsToOpen = 0;
+ g_scoobyWallsToClose = 0;
+
+ int16 savedCell = g_scoobyGameState.scoobySavedCell;
+
+ int16 curUp = curCell - 5;
+ int16 curLeft = curCell - 1;
+ int16 curRight = curCell + 1;
+ int16 curDown = curCell + 5;
+
+ int16 savedUp = savedCell - 5;
+ int16 savedLeft = savedCell - 1;
+ int16 savedRight = savedCell + 1;
+ int16 savedDown = savedCell + 5;
+
+ for (int16 dir = 0; dir <= 3; dir++) {
+ // Check current cell wall
+ if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
+ g_scoobyWallAnimating = 1;
+ g_scoobyWallAnimStep = 1;
+ g_scoobyWallsToClose++;
+ } else if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
+ g_scoobyWallAnimating = 1;
+ g_scoobyWallAnimStep = 1;
+ g_scoobyWallsToOpen++;
+
+ bool cancelOpen = false;
+ switch (dir) {
+ case 0:
+ if (curUp == savedCell &&
+ g_scoobyGameState.wallStates[savedCell][2] == 1)
+ cancelOpen = true;
+
+ break;
+ case 1:
+ if (curRight == savedCell &&
+ g_scoobyGameState.wallStates[savedCell][3] == 1)
+ cancelOpen = true;
+
+ break;
+ case 2:
+ if (curDown == savedCell &&
+ g_scoobyGameState.wallStates[savedCell][0] == 1)
+ cancelOpen = true;
+
+ break;
+ case 3:
+ if (curLeft == savedCell &&
+ g_scoobyGameState.wallStates[savedCell][1] == 1)
+ cancelOpen = true;
+
+ break;
+ default:
+ break;
+ }
+
+ if (cancelOpen)
+ g_scoobyWallsToOpen--;
+ }
+
+ // Check saved cell wall
+ if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
+ g_scoobyWallAnimating = 1;
+ g_scoobyWallAnimStep = 1;
+ g_scoobyWallsToOpen++;
+ } else if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
+ g_scoobyWallAnimating = 1;
+ g_scoobyWallAnimStep = 1;
+ g_scoobyWallsToClose++;
+
+ bool cancelClose = false;
+ switch (dir) {
+ case 0:
+ if (savedUp == curCell &&
+ g_scoobyGameState.wallStates[curCell][2] == 1)
+ cancelClose = true;
+
+ break;
+ case 1:
+ if (savedRight == curCell &&
+ g_scoobyGameState.wallStates[curCell][3] == 1)
+ cancelClose = true;
+
+ break;
+ case 2:
+ if (savedDown == curCell &&
+ g_scoobyGameState.wallStates[curCell][0] == 1)
+ cancelClose = true;
+
+ break;
+ case 3:
+ if (savedLeft == curCell &&
+ g_scoobyGameState.wallStates[curCell][1] == 1)
+ cancelClose = true;
+
+ break;
+ default:
+ break;
+ }
+
+ if (cancelClose)
+ g_scoobyWallsToClose--;
+ }
+ }
+
+ if (g_scoobyWallAnimating != 0) {
+ animateWalls();
+ }
+}
+
+void BoltEngine::updateScoobyDirection(int16 inputDir) {
+ int16 cell = g_scoobyGameState.scoobyCell;
+
+ // If at cell center, wall passable, and no transition active then accept immediately
+ if (g_scoobyLevelStartXY[cell].x == g_scoobyGameState.scoobyX &&
+ g_scoobyLevelStartXY[cell].y == g_scoobyGameState.scoobyY &&
+ g_scoobyGameState.wallStates[cell][inputDir] != 2 &&
+ g_scoobyTransitioning == 0) {
+ g_scoobyDesiredDir = inputDir;
+ g_scoobyInputHoldCount = 3;
+ g_scoobyLastInputDir = inputDir;
+ } else {
+ if (g_scoobyLastInputDir == inputDir) {
+ g_scoobyInputHoldCount++;
+ if (g_scoobyInputHoldCount >= 3 && g_scoobyTransitioning == 0) {
+ g_scoobyDesiredDir = g_scoobyLastInputDir;
+ }
+ } else {
+ g_scoobyInputHoldCount = 0;
+ g_scoobyLastInputDir = inputDir;
+ }
+ }
+
+ // Resolve diagonal inputs into cardinal directions
+ int16 resolvedDir = 0;
+
+#define WALL_OPEN(c, d) (g_scoobyGameState.wallStates[c][d] == 2 || g_scoobyGameState.wallStates[c][d] == 3)
+
+ switch (g_scoobyDesiredDir) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ resolvedDir = g_scoobyDesiredDir;
+ break;
+ case 1: // up-right
+ if (WALL_OPEN(cell, 0)) {
+ resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+ } else {
+ resolvedDir = WALL_OPEN(cell, 1) ? 0 : (g_scoobyDesiredDir & 0xFE);
+ }
+
+ break;
+ case 3: // down-right
+ if (WALL_OPEN(cell, 2)) {
+ resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+ } else {
+ resolvedDir = WALL_OPEN(cell, 1) ? 4 : (g_scoobyDesiredDir & 0xFE);
+ }
+
+ break;
+ case 5: // down-left
+ if (WALL_OPEN(cell, 2)) {
+ resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+ } else {
+ resolvedDir = WALL_OPEN(cell, 3) ? 4 : (g_scoobyDesiredDir & 0xFE);
+ }
+
+ break;
+ case 7: // up-left
+ if (WALL_OPEN(cell, 0)) {
+ resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+ } else {
+ resolvedDir = WALL_OPEN(cell, 3) ? 0 : (g_scoobyDesiredDir & 0xFE);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+#undef WALL_OPEN
+
+ g_scoobyMoveRequested = 0;
+
+ if (g_scoobyDesiredDir == -1) {
+ g_scoobyMoveRequested = 1;
+
+ if (g_scoobyGameState.direction == 6) {
+ g_scoobyGameState.transitionTarget = -3;
+ } else {
+ g_scoobyGameState.transitionTarget = -2;
+ }
+
+ g_scoobyGameState.targetVelocityX = 0;
+ g_scoobyGameState.targetVelocityY = 0;
+ } else {
+ g_scoobyGameState.transitionTarget = resolvedDir;
+ decideDirection();
+ }
+}
+
+void BoltEngine::updateScoobyTransition() {
+ if (g_scoobyTransitioning != 0) {
+ // Active transition in progress
+ switch (g_scoobyTransitionTarget) {
+ case -3:
+ animateTransition(0x24);
+ g_scoobyTransitioning = 0;
+ break;
+ case -2:
+ animateTransition(0x23);
+ g_scoobyTransitioning = 0;
+ break;
+ case 0:
+ setScoobySpriteDirection(0x0C);
+ g_scoobyTransitioning = 0;
+ break;
+ case 2:
+ if (g_scoobyTransitionFrom == 4) {
+ animateTransition(0x1C);
+ g_scoobyTransitionFrom = 0;
+ } else {
+ setScoobySpriteDirection(0x0);
+ g_scoobyTransitioning = 0;
+ }
+ break;
+ case 4:
+ if (g_scoobyTransitionFrom == 0) {
+ animateTransition(0x19);
+ g_scoobyTransitionFrom = 4;
+ } else {
+ setScoobySpriteDirection(0x12);
+ g_scoobyTransitioning = 0;
+ }
+ break;
+ case 6:
+ setScoobySpriteDirection(6);
+ g_scoobyTransitioning = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (g_scoobyTransitioning == 0) {
+ // Transition complete, commit state
+ switch (g_scoobyTransitionTarget) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ g_scoobyGameState.velocityX = g_scoobyTransitionVelX;
+ g_scoobyGameState.velocityY = g_scoobyTransitionVelY;
+ break;
+ default:
+ break;
+ }
+
+ g_scoobyGameState.currentAnim = g_scoobyTransitionTarget;
+
+ if (g_scoobyTransitionTarget == 6) {
+ g_scoobyGameState.direction = 6;
+ } else if (g_scoobyGameState.currentAnim == 2) {
+ g_scoobyGameState.direction = 2;
+ }
+ }
+ } else {
+ // No transition active, check if one should start
+ if (g_scoobyMoveRequested == 0)
+ goto epilogue;
+
+ g_scoobyMoveRequested = 0;
+
+ if (g_scoobyGameState.transitionTarget == g_scoobyGameState.currentAnim) {
+ // Same direction, just update velocity
+ g_scoobyGameState.velocityX = g_scoobyGameState.targetVelocityX;
+ g_scoobyGameState.velocityY = g_scoobyGameState.targetVelocityY;
+ goto epilogue;
+ }
+
+ // Start new transition
+ g_scoobyTransitioning = 1;
+ g_scoobyTransitionTarget = g_scoobyGameState.transitionTarget;
+ g_scoobyTransitionFrom = g_scoobyGameState.currentAnim;
+ g_scoobyTransitionVelX = g_scoobyGameState.targetVelocityX;
+ g_scoobyTransitionVelY = g_scoobyGameState.targetVelocityY;
+
+ // Pick transition animation based on current -> target direction.
+ switch (g_scoobyGameState.currentAnim) {
+ case -3:
+ case -2:
+ switch (g_scoobyGameState.transitionTarget) {
+ case 0:
+ if (g_scoobyGameState.currentAnim == -2)
+ animateTransition(0x1B);
+ else
+ animateTransition(0x1C);
+ break;
+ case 2:
+ animateTransition(0x1D);
+ break;
+ case 4:
+ if (g_scoobyGameState.currentAnim == -2)
+ animateTransition(0x19);
+ else
+ animateTransition(0x1A);
+ break;
+ case 6:
+ animateTransition(0x1E);
+ break;
+
+ default:
+ break;
+ }
+ break;
+ case 0:
+ switch (g_scoobyGameState.transitionTarget) {
+ case -3:
+ animateTransition(0x1C);
+ break;
+ case -2:
+ animateTransition(0x1B);
+ break;
+ case 2:
+ animateTransition(0x1F);
+ break;
+ case 4:
+ animateTransition(0x1B);
+ break;
+ case 6:
+ animateTransition(0x20);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 2:
+ switch (g_scoobyGameState.transitionTarget) {
+ case -3:
+ animateTransition(0x1D);
+ break;
+ case -2:
+ animateTransition(0x1D);
+ break;
+ case 0:
+ animateTransition(0x1F);
+ break;
+ case 4:
+ animateTransition(0x21);
+ break;
+ case 6:
+ animateTransition(0x19);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 4:
+ switch (g_scoobyGameState.transitionTarget) {
+ case -3:
+ animateTransition(0x1A);
+ break;
+ case -2:
+ animateTransition(0x19);
+ break;
+ case 0:
+ animateTransition(0x1A);
+ break;
+ case 2:
+ animateTransition(0x21);
+ break;
+ case 6:
+ animateTransition(0x22);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 6:
+ switch (g_scoobyGameState.transitionTarget) {
+ case -3:
+ animateTransition(0x1E);
+ break;
+ case -2:
+ animateTransition(0x1E);
+ break;
+ case 0:
+ animateTransition(0x20);
+ break;
+ case 2:
+
+ animateTransition(0x1A);
+ break;
+ case 4:
+ animateTransition(0x22);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+epilogue:
+ setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
+ animateSSprite(&g_scoobySprite, 0);
+
+ int16 soundMode;
+ if (g_scoobyGameState.currentAnim == -2 || g_scoobyGameState.currentAnim == -3) {
+ soundMode = 0;
+ } else if (g_scoobyGameState.velocityX == 0 && g_scoobyGameState.velocityY == 0 && g_scoobyTransitioning == 0) {
+ soundMode = 2;
+ } else {
+ soundMode = 1;
+ }
+
+ setScoobySound(soundMode);
+}
+
+bool BoltEngine::initScoobyLevel() {
+ if (!initScoobyLevelAssets())
+ return false;
+
+ // Set current level based on difficulty
+ int16 level;
+ switch (g_scoobyDifficulty) {
+ case 0:
+ level = READ_UINT16(g_scoobyLevelData + 0x1A);
+ break;
+ case 1:
+ level = READ_UINT16(g_scoobyLevelData + 0xE4);
+ break;
+ case 2:
+ level = READ_UINT16(g_scoobyLevelData + 0x1AE);
+ break;
+ default:
+ goto skipLevelSet;
+ }
+
+ g_scoobyGameState.scoobyCell = level;
+ g_scoobyGameState.scoobySavedCell = level;
+
+skipLevelSet:
+ g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+
+ // Compute neighbor levels in a 5-wide grid
+ int16 col = g_scoobyGameState.scoobyCell % 5;
+
+ g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
+ g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
+ g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
+ g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+
+ g_scoobyGameState.levelComplete = 0;
+
+ // Starting position from level table
+ g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
+ g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+
+ g_scoobyGameState.velocityX = 0;
+ g_scoobyGameState.velocityY = 0;
+ g_scoobyGameState.targetVelocityX = 0;
+ g_scoobyGameState.targetVelocityY = 0;
+ g_scoobyGameState.currentAnim = -2;
+ g_scoobyGameState.transitionTarget = -2;
+ g_scoobyGameState.direction = 2;
+
+ initScoobyLevelGraphics();
+ return true;
+}
+
+bool BoltEngine::resumeScoobyLevel() {
+ if (!initScoobyLevelAssets())
+ return false;
+
+ // Restore level from saved level
+ g_scoobyGameState.scoobyCell = g_scoobyGameState.scoobySavedCell;
+
+ // Compute neighbor levels
+ int16 col = g_scoobyGameState.scoobyCell % 5;
+
+ g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
+ g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
+ g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
+ g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+
+ // Starting position from level table
+ g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
+ g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+
+ initScoobyLevelGraphics();
+ return true;
+}
+
+bool BoltEngine::initScooby() {
+ _xp->randomize();
+
+ g_scoobyMoveRequested = 0;
+ g_scoobyTransitioning = 0;
+ g_scoobyWallAnimating = 0;
+ g_scoobyDesiredDir = -1;
+ g_scoobyInputHoldCount = 0;
+
+ if (!loadScoobyBaseAssets())
+ return false;
+
+ if (!vLoad(&g_scoobyGlobalSaveData, "ScoobyGlobal")) {
+ // Initialize global save data: 10 levels, 3 slots each
+ for (int16 level = 0; level < 10; level++) {
+ g_scoobyGameState.slotIndex[level] = 0;
+
+ int16 baseOff = level * 6;
+ for (int16 j = 0; j < 3; j++) {
+ g_scoobyGlobalSaveData[baseOff + j * 2] = 3;
+ }
+
+ // Randomly assign difficulty values 0, 1, 2 to the 3 slots
+ for (int16 j = 0; j < 3; j++) {
+ int16 slot = _xp->getRandom(3);
+
+ while (g_scoobyGlobalSaveData[baseOff + slot * 2] != 3) {
+ slot++;
+ if (slot >= 3)
+ slot = 0;
+ }
+
+ g_scoobyGlobalSaveData[baseOff + slot * 2] = j;
+ }
+ }
+ }
+
+ byte scoobyStateBuf[0x11A];
+ if (!vLoad(scoobyStateBuf, "Scooby")) {
+ g_scoobyGameState.levelNumber = 1;
+
+ if (!initScoobyLevel())
+ return false;
+ } else {
+ Common::SeekableReadStream *scoobyStateReadStream = new Common::MemoryReadStream(scoobyStateBuf, sizeof(scoobyStateBuf), DisposeAfterUse::NO);
+
+ g_scoobyGameState.levelNumber = scoobyStateReadStream->readSint16BE(); // +0x00
+
+ for (int i = 0; i < 10; i++) // +0x02
+ g_scoobyGameState.slotIndex[i] = scoobyStateReadStream->readSint16BE();
+
+ g_scoobyGameState.levelComplete = scoobyStateReadStream->readSint16BE(); // +0x16
+
+ for (int i = 0; i < 25; i++) // +0x18
+ for (int j = 0; j < 4; j++)
+ g_scoobyGameState.wallStates[i][j] = scoobyStateReadStream->readSint16BE();
+
+ g_scoobyGameState.scoobyCell = scoobyStateReadStream->readSint16BE(); // +0xE0
+ g_scoobyGameState.scoobySavedCell = scoobyStateReadStream->readSint16BE(); // +0xE2
+ g_scoobyGameState.leftNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE4
+ g_scoobyGameState.rightNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE6
+ g_scoobyGameState.upNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE8
+ g_scoobyGameState.downNeighbor = scoobyStateReadStream->readSint16BE(); // +0xEA
+ g_scoobyGameState.activeLevel = scoobyStateReadStream->readSint16BE(); // +0xEC
+ g_scoobyGameState.scoobyX = scoobyStateReadStream->readSint16BE(); // +0xEE
+ g_scoobyGameState.scoobyY = scoobyStateReadStream->readSint16BE(); // +0xF0
+ g_scoobyGameState.velocityX = scoobyStateReadStream->readSint16BE(); // +0xF2
+ g_scoobyGameState.velocityY = scoobyStateReadStream->readSint16BE(); // +0xF4
+ g_scoobyGameState.targetVelocityX = scoobyStateReadStream->readSint16BE(); // +0xF6
+ g_scoobyGameState.targetVelocityY = scoobyStateReadStream->readSint16BE(); // +0xF8
+ g_scoobyGameState.transitionTarget = scoobyStateReadStream->readSint16BE(); // +0xFA
+ g_scoobyGameState.currentAnim = scoobyStateReadStream->readSint16BE(); // +0xFC
+ g_scoobyGameState.direction = scoobyStateReadStream->readSint16BE(); // +0xFE
+ g_scoobyGameState.spriteFrameCount = scoobyStateReadStream->readSint16BE(); // +0x100
+
+ for (int i = 0; i < 6; i++) { // +0x102
+ scoobyStateReadStream->readUint32BE(); // dummy values
+ g_scoobyGameState.frameData[i] = nullptr;
+ }
+
+ assert(scoobyStateReadStream->pos() == 0x11A);
+ delete scoobyStateReadStream;
+
+ if (g_scoobyGameState.levelComplete != 0) {
+ if (!initScoobyLevel())
+ return false;
+ } else {
+ if (!resumeScoobyLevel())
+ return false;
+ }
+ }
+
+ _xp->setFrameRate(12);
+ return true;
+}
+
+void BoltEngine::cleanUpScooby() {
+ vSave(&g_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
+
+ // Serialize the game state into a flat 0x11A-byte BE buffer
+ byte scoobyStateBuf[0x11A] = {0};
+ Common::MemoryWriteStream *scoobyStateWriteStream = new Common::MemoryWriteStream(scoobyStateBuf, sizeof(scoobyStateBuf));
+
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelNumber); // +0x00
+
+ for (int i = 0; i < 10; i++) // +0x02
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.slotIndex[i]);
+
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelComplete); // +0x16
+
+ for (int i = 0; i < 25; i++) // +0x18
+ for (int j = 0; j < 4; j++)
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.wallStates[i][j]);
+
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyCell); // +0xE0
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobySavedCell); // +0xE2
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.leftNeighbor); // +0xE4
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.rightNeighbor); // +0xE6
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.upNeighbor); // +0xE8
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.downNeighbor); // +0xEA
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.activeLevel); // +0xEC
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyX); // +0xEE
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyY); // +0xF0
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityX); // +0xF2
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityY); // +0xF4
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityX); // +0xF6
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityY); // +0xF8
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.transitionTarget); // +0xFA
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.currentAnim); // +0xFC
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.direction); // +0xFE
+ scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.spriteFrameCount); // +0x100
+
+ for (int i = 0; i < 6; i++) // +0x102
+ scoobyStateWriteStream->writeUint32BE(0); // pointers saved as zero
+
+ // Sanity check: should be exactly 0x11A
+ assert(scoobyStateWriteStream->pos() == 0x11A);
+
+ vSave(scoobyStateBuf, 0x11A, "Scooby");
+
+ cleanUpScoobyLevelGraphics();
+ cleanUpScoobyBaseAssets();
+
+ if (g_scoobyTempPic.pixelData) {
+ _xp->freeMem(g_scoobyTempPic.pixelData);
+ g_scoobyTempPic.pixelData = nullptr;
+ g_scoobyTempPic.width = 0;
+ g_scoobyTempPic.height = 0;
+ }
+
+ _xp->setFrameRate(0);
+ _xp->fillDisplay(0, 0);
+ _xp->updateDisplay();
+}
+
+int16 BoltEngine::helpScooby() {
+ int16 selection = 2;
+ int16 isPlaying = 0;
+ int16 soundParam = 0;
+ byte *blinkEntry = nullptr;
+ uint32 blinkTimer = 0;
+ byte *hoveredEntry = nullptr;
+ int16 animFrameIdx = -1;
+ byte *backButton = nullptr;
+ byte *helpButton = nullptr;
+
+ byte *helpEntries[3];
+ byte *helpPics[3];
+
+ // If Scooby is mid-movement, stop and face idle direction
+ if (g_scoobyGameState.currentAnim != -2 && g_scoobyGameState.currentAnim != -3) {
+ g_scoobyGameState.velocityX = 0;
+ g_scoobyGameState.velocityY = 0;
+
+ if (g_scoobyGameState.direction == 2)
+ g_scoobyGameState.transitionTarget = -2;
+ else
+ g_scoobyGameState.transitionTarget = -3;
+
+ g_scoobyDesiredDir = -1;
+ g_scoobyInputHoldCount = 0;
+ g_scoobyMoveRequested = 1;
+
+ do {
+ updateScoobyTransition();
+ updateScoobyWalls();
+ _xp->updateDisplay();
+ } while (g_scoobyTransitioning != 0);
+
+ updateScoobyTransition();
+ updateScoobyWalls();
+ _xp->updateDisplay();
+
+ updateScoobyTransition();
+ updateScoobyWalls();
+ _xp->updateDisplay();
+ }
+
+ _xp->setFrameRate(0);
+ _xp->setInactivityTimer(0);
+ animateSSprite(&g_scoobySprite, 0);
+
+ // Load 3 help screen entries
+ for (int16 i = 0; i < 3; i++) {
+ int16 memberIdx = READ_UINT16(g_scoobyLevelData + 0x0C) + i;
+ helpEntries[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+
+ byte *entry = helpEntries[i];
+ if (READ_UINT32(entry) == 2)
+ backButton = entry;
+ if (READ_UINT32(entry) == 1)
+ helpButton = entry;
+
+ // Clear alive flag
+ WRITE_UINT32(entry + 6, READ_UINT32(entry + 6) & ~1);
+
+ // Load and display pic
+ int16 picMember = READ_UINT16(entry + 4);
+ helpPics[i] = memberAddr(g_scoobyBoltLib, picMember);
+ displayPic(helpPics[i], 0, 0, 0);
+ }
+
+ _xp->updateDisplay();
+
+ // Save normal palettes from each help entry
+ for (int16 i = 0; i < 3; i++) {
+ byte *entry = helpEntries[i];
+ _xp->getPalette(READ_UINT16(entry + 0x0A), READ_UINT16(entry + 0x0C), entry + 0x42);
+ }
+
+ // Position cursor at bottom-right of first help pic
+ int16 cursorX = READ_UINT16(helpPics[0] + 6) + READ_UINT16(helpPics[0] + 0x0A) - 10;
+ int16 cursorY = READ_UINT16(helpPics[0] + 8) + READ_UINT16(helpPics[0] + 0x0C) - 10;
+ _xp->setCursorPos(cursorX, cursorY);
+ _xp->setCursorColor(0, 127, 127);
+ _xp->disableController();
+ _xp->showCursor();
+
+ hoveredEntry = helpButton;
+ hiliteScoobyHelpObject(helpButton, 1);
+
+ // Event loop
+ while (true) {
+ // Handle ongoing audio playback
+ if (isPlaying != 0) {
+ if (!maintainAudioPlay(soundParam)) {
+ int16 hl = (backButton == hoveredEntry) ? 1 : 0;
+ hiliteScoobyHelpObject(backButton, hl);
+ isPlaying = 0;
+ }
+ soundParam = 0;
+ }
+
+ uint32 eventData;
+ int16 eventType = _xp->getEvent(0, &eventData);
+
+ switch (eventType) {
+ case etTimer: {
+ if (blinkEntry == nullptr)
+ break;
+ if (eventData != blinkTimer)
+ break;
+
+ blinkTimer = _xp->startTimer(500);
+
+ int16 hl = (READ_UINT32(blinkEntry + 6) & 1) ? 0 : 1;
+ hiliteScoobyHelpObject(blinkEntry, hl);
+ break;
+ }
+
+ case etMouseMove: {
+ int16 mouseX = (int32)eventData >> 16;
+ int16 mouseY = (int16)eventData;
+
+ byte *hitEntry = nullptr;
+ for (int16 i = 0; i < 3; i++) {
+ byte *pic = helpPics[i];
+ int16 picX = READ_UINT16(pic + 6);
+ int16 picY = READ_UINT16(pic + 8);
+ int16 picW = READ_UINT16(pic + 0x0A);
+ int16 picH = READ_UINT16(pic + 0x0C);
+
+ if (mouseX <= picX || mouseX >= picX + picW)
+ continue;
+ if (mouseY <= picY || mouseY >= picY + picH)
+ continue;
+
+ hitEntry = helpEntries[i];
+ break;
+ }
+
+ if (hoveredEntry != hitEntry) {
+ // Un-highlight old (unless null, blinking, or back button)
+ if (hoveredEntry != nullptr &&
+ hoveredEntry != blinkEntry &&
+ hoveredEntry != backButton) {
+ hiliteScoobyHelpObject(hoveredEntry, 0);
+ }
+
+ hoveredEntry = hitEntry;
+
+ // Highlight new (unless null or blinking)
+ if (hitEntry != nullptr && hitEntry != blinkEntry) {
+ hiliteScoobyHelpObject(hoveredEntry, 1);
+ }
+ }
+
+ // Un-highlight back button if not actively needed
+ if (backButton != nullptr) {
+ if (READ_UINT32(backButton + 6) & 1) {
+ if (hoveredEntry != backButton &&
+ blinkEntry != backButton &&
+ isPlaying == 0) {
+ hiliteScoobyHelpObject(backButton, 0);
+ }
+ }
+ }
+ break;
+ }
+
+ case etMouseDown: {
+ int16 wasPlaying = isPlaying;
+
+ if (isPlaying != 0) {
+ if (blinkTimer != 0) {
+ _xp->killTimer(blinkTimer);
+ blinkTimer = 0;
+ }
+
+ int16 hl = (backButton == hoveredEntry) ? 1 : 0;
+ hiliteScoobyHelpObject(backButton, hl);
+
+ hl = (blinkEntry == hoveredEntry) ? 1 : 0;
+ hiliteScoobyHelpObject(blinkEntry, hl);
+
+ blinkEntry = nullptr;
+ stopAnimation();
+ isPlaying = 0;
+ }
+
+ if (hoveredEntry == nullptr)
+ break;
+
+ selection = READ_UINT16(hoveredEntry);
+
+ if (READ_UINT32(hoveredEntry) != 2)
+ break;
+ if (wasPlaying != 0)
+ break;
+
+ if (startAnimation(g_rtfHandle, 0x1C)) {
+ animFrameIdx = 0;
+ isPlaying = 1;
+ }
+ break;
+ }
+
+ case etSound:
+ soundParam = 1;
+ break;
+
+ case etTrigger: {
+ if (isPlaying == 0)
+ break;
+ if (animFrameIdx < 0)
+ break;
+
+ int16 hl = (blinkEntry == hoveredEntry) ? 1 : 0;
+ hiliteScoobyHelpObject(blinkEntry, hl);
+
+ if (animFrameIdx == 0)
+ blinkEntry = helpEntries[1];
+ else if (animFrameIdx == 2)
+ blinkEntry = helpEntries[2];
+ else
+ blinkEntry = nullptr;
+
+ if (blinkEntry != nullptr) {
+ blinkTimer = _xp->startTimer(500);
+ } else {
+ if (blinkTimer != 0) {
+ _xp->killTimer(blinkTimer);
+ blinkTimer = 0;
+ }
+ }
+
+ hiliteScoobyHelpObject(blinkEntry, 1);
+ animFrameIdx++;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // Exit conditions
+ if (selection == 0 || selection == 1)
+ break;
+ }
+
+ // Clean-up
+ _xp->hideCursor();
+ _xp->enableController();
+
+ hiliteScoobyHelpObject(helpEntries[0], 0);
+
+ for (int16 i = 0; i < 3; i++) {
+ hiliteScoobyHelpObject(helpEntries[i], 0);
+ }
+
+ animateSSprite(&g_scoobySprite, 0);
+ _xp->updateDisplay();
+ _xp->setFrameRate(12);
+
+ return selection;
+}
+
+void BoltEngine::hiliteScoobyHelpObject(byte *entry, int16 highlight) {
+ if (!entry)
+ return;
+
+ if (highlight != 0) {
+ // Set highlighted palette
+ _xp->setPalette(READ_UINT16(entry + 0x0C), READ_UINT16(entry + 0x0A), entry + 0x0E);
+ WRITE_UINT32(entry + 6, READ_UINT32(entry + 6) | 1);
+ } else {
+ // Set normal palette
+ _xp->setPalette(READ_UINT16(entry + 0x0C), READ_UINT16(entry + 0x0A), entry + 0x42);
+ WRITE_UINT32(entry + 6, READ_UINT32(entry + 6) & ~1);
+ }
+}
+
+int16 BoltEngine::xpDirToBOLTDir(uint32 xpDir) {
+ // xpDir packs (X << 16 | Y & 0xFFFF) where X,Y are -1/0/1
+ // Returns BOLT direction:
+ // 0=up, 1=up-right, 2=right, 3=down-right,
+ // 4=down, 5=down-left, 6=left, 7=up-left, -1=none
+
+ static const uint32 dirTable[] = {
+ 0xFFFF0000, // X=-1, Y= 0 -> 6 (left)
+ 0xFFFF0001, // X=-1, Y= 1 -> 5 (down-left)
+ 0xFFFFFFFF, // X=-1, Y=-1 -> 7 (up-left)
+ 0x00000000, // X= 0, Y= 0 -> -1 (none)
+ 0x00000001, // X= 0, Y= 1 -> 4 (down)
+ 0x0000FFFF, // X= 0, Y=-1 -> 0 (up)
+ 0x00010000, // X= 1, Y= 0 -> 2 (right)
+ 0x00010001, // X= 1, Y= 1 -> 3 (down-right)
+ 0x0001FFFF, // X= 1, Y=-1 -> 1 (up-right)
+ };
+
+ static const int16 dirMap[] = {6, 5, 7, -1, 4, 0, 2, 3, 1};
+
+ for (int i = 0; i < 9; i++) {
+ if (dirTable[i] == xpDir)
+ return dirMap[i];
+ }
+
+ return 0;
+}
+
+int16 BoltEngine::playScooby() {
+ int16 inputDir = -1;
+
+ // Check if help screen should show on startup
+ if (g_scoobyShowHelp != 0) {
+ int16 helpResult = helpScooby();
+ if (helpResult == 0)
+ return 5; // exit/quit
+ }
+
+ g_scoobyShowHelp = 0;
+ _xp->enableController();
+
+ // Main game loop
+ while (true) {
+ updateScoobyLocation();
+
+ // Level complete?
+ if (g_scoobyGameState.levelComplete != 0)
+ return 16;
+
+ // Poll for event
+ uint32 eventData;
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
+ switch (eventType) {
+ case etJoystick:
+ if (eventData != 0)
+ _xp->setInactivityTimer(30);
+
+ inputDir = xpDirToBOLTDir(eventData);
+ break;
+
+ case etMouseDown:
+ case etInactivity:
+ if (helpScooby() == 0)
+ return 5;
+ g_scoobyMoveRequested = 0;
+ break;
+
+ case etSound:
+ g_scoobySoundPlaying = 0;
+ updateScoobySound();
+ break;
+
+ default:
+ break;
+ }
+
+ updateScoobyDirection(inputDir);
+ updateScoobyTransition();
+ updateScoobyWalls();
+ _xp->updateDisplay();
+ }
+}
+
+int16 BoltEngine::scoobyGame(int16 prevBooth) {
+ int16 result = 5;
+
+ if (!openBOLTLib(&g_scoobyBoltLib, &g_scoobyBoltCallbacks, assetPath("scooby.blt")))
+ return result;
+
+ int16 prevInactivity = _xp->setInactivityTimer(30);
+
+ if (initScooby()) {
+ result = playScooby();
+ }
+
+ cleanUpScooby();
+ _xp->setInactivityTimer(prevInactivity);
+ closeBOLTLib(&g_scoobyBoltLib);
+
+ return result;
+}
+
+void BoltEngine::swapScoobyHelpEntry() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (data == nullptr)
+ return;
+
+ WRITE_UINT32(data, READ_BE_UINT32(data));
+ WRITE_UINT16(data + 4, READ_BE_UINT16(data + 4));
+ WRITE_UINT32(data + 6, READ_BE_UINT32(data + 6));
+ WRITE_UINT16(data + 0x0A, READ_BE_UINT16(data + 0x0A));
+ WRITE_UINT16(data + 0x0C, READ_BE_UINT16(data + 0x0C));
+ unpackColors(13, data + 0x0E);
+}
+
+void BoltEngine::swapScoobyWordArray() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ if (data == nullptr)
+ return;
+
+ int16 count = g_boltCurrentMemberEntry->decompSize >> 1;
+ for (int16 i = 0; i < count; i++) {
+ WRITE_UINT16(data, READ_BE_UINT16(data));
+ data += 2;
+ }
+}
} // End of namespace Bolt
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
index 133a89ca72d..bd6233d20ae 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/booths/topcat.cpp
@@ -1100,7 +1100,7 @@ void BoltEngine::shuffleTopCatPermutations() {
}
}
-void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
soundInfo->data = memberAddr(lib, memberId);
soundInfo->size = memberSize(lib, memberId);
}
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 29e024c643e..facf68567ec 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -647,6 +647,9 @@ BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
+BOLTCallback BoltEngine::g_scoobyTypeLoadCallbacks[28];
+BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
+
BOLTCallback BoltEngine::g_topCatTypeLoadCallbacks[26];
BOLTCallback BoltEngine::g_topCatTypeFreeCallbacks[26];
@@ -666,11 +669,13 @@ void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimE
void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
+void BoltEngine::swapScoobyHelpEntryCb() { ((BoltEngine *)g_engine)->swapScoobyHelpEntry(); }
+void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyWordArray(); }
+
void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
void BoltEngine::initCallbacks() {
// --- BOOTHS ---
-
for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
g_defaultTypeLoadCallbacks[i] = noOpCb;
}
@@ -712,7 +717,6 @@ void BoltEngine::initCallbacks() {
g_boothsBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
// --- FRED ---
-
for (int i = 0; i < ARRAYSIZE(g_fredTypeLoadCallbacks); i++) {
g_fredTypeLoadCallbacks[i] = noOpCb;
}
@@ -741,6 +745,31 @@ void BoltEngine::initCallbacks() {
g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+ // --- SCOOBY ---
+ for (int i = 0; i < ARRAYSIZE(g_scoobyTypeLoadCallbacks); i++) {
+ g_scoobyTypeLoadCallbacks[i] = noOpCb;
+ }
+
+ g_scoobyTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_scoobyTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_scoobyTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+ g_scoobyTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+ g_scoobyTypeLoadCallbacks[26] = swapScoobyHelpEntryCb;
+ g_scoobyTypeLoadCallbacks[27] = swapScoobyWordArrayCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_scoobyTypeFreeCallbacks); i++) {
+ g_scoobyTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_scoobyTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ g_scoobyBoltCallbacks.typeLoadCallbacks = g_scoobyTypeLoadCallbacks;
+ g_scoobyBoltCallbacks.typeFreeCallbacks = g_scoobyTypeFreeCallbacks;
+ g_scoobyBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_scoobyBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_scoobyBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_scoobyBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
// --- TOPCAT ---
for (int i = 0; i < ARRAYSIZE(g_topCatTypeLoadCallbacks); i++) {
g_topCatTypeLoadCallbacks[i] = noOpCb;
diff --git a/engines/bolt/rtf.cpp b/engines/bolt/rtf.cpp
index e6c15d95a09..e12b741aa19 100644
--- a/engines/bolt/rtf.cpp
+++ b/engines/bolt/rtf.cpp
@@ -68,6 +68,8 @@ RTFResource *BoltEngine::openRTF(const char *fileName) {
rtf->indexTable[i] = indexDataStream->readUint32BE();
}
+ delete indexDataStream;
+
return rtf;
}
}
diff --git a/engines/bolt/ssprite.cpp b/engines/bolt/ssprite.cpp
index d0e5b38e0bf..90829dbdd60 100644
--- a/engines/bolt/ssprite.cpp
+++ b/engines/bolt/ssprite.cpp
@@ -23,6 +23,325 @@
namespace Bolt {
+void BoltEngine::setUpSSprite(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate, int16 velocityX, int16 velocityY) {
+ sprite->frameCount = frameCount;
+ sprite->frameData = frameData;
+ sprite->frameRate = frameRate;
+ sprite->frameTimer = frameRate;
+ sprite->velocityX = velocityX;
+ sprite->velocityY = velocityY;
+ // Read collision bounds from first frame's pic descriptor
+ byte *firstFrame = sprite->frameData[0];
+ sprite->collX = READ_UINT16(firstFrame + 6);
+ sprite->collY = READ_UINT16(firstFrame + 8);
+ sprite->collW = READ_UINT16(firstFrame + 0x0A);
+ sprite->collH = READ_UINT16(firstFrame + 0x0C);
+
+ sprite->xLimitHigh = 0;
+ sprite->xLimitLow = 0;
+ sprite->yLimitHigh = 0;
+ sprite->yLimitLow = 0;
+}
+
+void BoltEngine::animateSSprite(SSprite *sprite, int16 page) {
+ if (!(sprite->flags & 0x01))
+ return;
+
+ // Frozen: call update func or display and return
+ if (sprite->flags & 0x80) {
+ if (sprite->updateFunc) {
+ sprite->updateFunc();
+ return;
+ }
+
+ _xp->fillDisplay(0, stFront);
+ byte *frame = sprite->frameData[sprite->currentFrame];
+ displayPic(frame, sprite->x, sprite->y, page);
+ return;
+ }
+
+ // Advance frame timer
+ sprite->frameTimer--;
+ if (sprite->frameTimer == 0) {
+ sprite->frameTimer = sprite->frameRate;
+ sprite->currentFrame++;
+ if (sprite->currentFrame == sprite->frameCount)
+ sprite->currentFrame = 0;
+ }
+
+ // Movement: path or velocity
+ if (sprite->flags & 0x08) {
+ // Path mode
+ if (sprite->pathOffset >= sprite->pathLength) {
+ // Path complete
+ sprite->flags &= 0xFFF6; // clear alive + path bits
+ if (sprite->pathCallback) {
+ sprite->pathCallback(sprite);
+ }
+ } else {
+ // Read next x,y from path data
+ int16 idx = sprite->pathOffset;
+ sprite->pathOffset++;
+ int16 *pathWords = (int16 *)sprite->pathData;
+ sprite->x = pathWords[idx];
+
+ idx = sprite->pathOffset;
+ sprite->pathOffset++;
+ sprite->y = pathWords[idx];
+ }
+ } else {
+ // Velocity mode
+ int16 vx = sprite->velocityX;
+ int16 vy = sprite->velocityY;
+
+ sprite->x += vx;
+ sprite->y += vy;
+
+ sprite->velocityX += sprite->accelX;
+ sprite->velocityY += sprite->accelY;
+ }
+
+ // X limit clamping
+ if (sprite->flags & 0x20) {
+ if (sprite->x < sprite->xLimitHigh) {
+ sprite->x = sprite->xLimitHigh;
+ sprite->accelX = 0;
+ sprite->velocityX = 0;
+ } else if (sprite->x > sprite->xLimitLow) {
+ sprite->x = sprite->xLimitLow;
+ sprite->accelX = 0;
+ sprite->velocityX = 0;
+ }
+ }
+
+ // Y limit clamping
+ if (sprite->flags & 0x40) {
+ if (sprite->y < sprite->yLimitHigh) {
+ sprite->y = sprite->yLimitHigh;
+ sprite->accelY = 0;
+ sprite->velocityY = 0;
+ } else if (sprite->y > sprite->yLimitLow) {
+ sprite->y = sprite->yLimitLow;
+ sprite->accelY = 0;
+ sprite->velocityY = 0;
+ }
+ }
+
+ // Call update func or display
+ if (sprite->updateFunc) {
+ sprite->updateFunc();
+ } else {
+ _xp->fillDisplay(0, stFront);
+ byte *frame = sprite->frameData[sprite->currentFrame];
+ displayPic(frame, sprite->x, sprite->y, page);
+ }
+
+ // Stop countdown
+ if (sprite->flags & 0x04) {
+ if (sprite->stopFrame != 0) {
+ sprite->stopFrame--;
+ return;
+ }
+
+ sprite->flags &= ~0x01; // not alive
+ sprite->flags &= ~0x04; // no stop
+ return;
+ }
+
+ // Start countdown
+ if (sprite->flags & 0x02) {
+ if (sprite->startFrame != 0) {
+ sprite->startFrame--;
+ return;
+ }
+
+ sprite->flags |= 0x01; // alive
+ sprite->flags &= ~0x02; // no longer starting
+ }
+}
+
+void BoltEngine::displaySSprite(SSprite *sprite, int16 x, int16 y) {
+ sprite->x = x;
+ sprite->y = y;
+ sprite->flags |= 0x01; // alive
+}
+
+void BoltEngine::eraseSSprite(SSprite *sprite) {
+ sprite->flags &= ~0x01; // not alive
+}
+
+void BoltEngine::setSSpriteFrames(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate) {
+ sprite->frameCount = frameCount;
+ sprite->frameData = frameData;
+ sprite->currentFrame = 0;
+ sprite->frameRate = frameRate;
+}
+
+void BoltEngine::setSSpriteDrag(SSprite *sprite, int16 dragX, int16 dragY) {
+ sprite->dragX = dragX;
+ sprite->dragY = dragY;
+}
+
+void BoltEngine::setSSpriteAccel(SSprite *sprite, int16 accelX, int16 accelY) {
+ sprite->accelX = accelX;
+ sprite->accelY = accelY;
+}
+
+void BoltEngine::reverseSSpriteAccel(SSprite *sprite) {
+ sprite->accelX = -sprite->accelX;
+ sprite->accelY = -sprite->accelY;
+}
+
+void BoltEngine::addSSpriteAccel(SSprite *sprite, int16 dx, int16 dy) {
+ sprite->accelX += dx;
+ sprite->accelY += dy;
+}
+
+void BoltEngine::setSSpriteVelocity(SSprite *sprite, int16 vx, int16 vy) {
+ sprite->velocityX = vx;
+ sprite->velocityY = vy;
+}
+
+void BoltEngine::reverseSSpriteVelocity(SSprite *sprite) {
+ sprite->velocityX = -sprite->velocityX;
+ sprite->velocityY = -sprite->velocityY;
+}
+
+void BoltEngine::setSSpriteStart(SSprite *sprite, int16 startFrame, int16 x, int16 y) {
+ sprite->startFrame = startFrame;
+ sprite->x = x;
+ sprite->y = y;
+ sprite->flags |= 0x02; // started
+ sprite->flags &= ~0x01; // not alive
+ sprite->flags &= ~0x04; // no stop
+}
+
+void BoltEngine::setSSpriteStop(SSprite *sprite, int16 stopFrame) {
+ sprite->stopFrame = stopFrame;
+ sprite->flags |= 0x04; // hasStop
+}
+
+void BoltEngine::setSSpritePath(SSprite *sprite, byte *pathData, int16 pathCount, SSpritePathCallback callback) {
+ sprite->pathLength = pathCount * 2;
+ sprite->pathData = pathData;
+ sprite->pathOffset = 0;
+ sprite->pathCallback = callback;
+ sprite->flags |= 0x09; // alive + hasPath
+}
+
+bool BoltEngine::inSSprite(SSprite *sprite, int16 x, int16 y) {
+ g_spriteCollTempX = sprite->collX;
+ g_spriteCollTempY = sprite->collY;
+ g_spriteCollTempW = sprite->collW;
+ g_spriteCollTempH = sprite->collH;
+
+ g_spriteScreenX = sprite->x + g_spriteCollTempX;
+ g_spriteScreenY = sprite->y + g_spriteCollTempY;
+
+ if ((uint16)x < (uint16)g_spriteScreenX)
+ return false;
+
+ if ((uint16)y < (uint16)g_spriteScreenY)
+ return false;
+
+ if ((uint16)(g_spriteScreenX + sprite->collW) < (uint16)x)
+ return false;
+
+ if ((uint16)(g_spriteScreenY + sprite->collH) < (uint16)y)
+ return false;
+
+ return true;
+}
+
+bool BoltEngine::sSpriteCollide(SSprite *spriteA, SSprite *spriteB) {
+ g_spriteCollTempA[0] = spriteA->collX;
+ g_spriteCollTempA[1] = spriteA->collY;
+ g_spriteCollTempA[2] = spriteA->collW;
+ g_spriteCollTempA[3] = spriteA->collH;
+
+ g_spriteCollTempB[0] = spriteB->collX;
+ g_spriteCollTempB[1] = spriteB->collY;
+ g_spriteCollTempB[2] = spriteB->collW;
+ g_spriteCollTempB[3] = spriteB->collH;
+
+ g_spriteScreenAX = spriteA->x + g_spriteCollTempA[0];
+ g_spriteScreenBX = spriteB->x + g_spriteCollTempB[0];
+ g_spriteScreenAY = spriteA->y + g_spriteCollTempA[1];
+ g_spriteScreenBY = spriteB->y + g_spriteCollTempB[1];
+
+ if ((uint16)(g_spriteScreenAX + g_spriteCollTempA[2]) < (uint16)g_spriteScreenBX)
+ return false;
+ if ((uint16)(g_spriteScreenAY + g_spriteCollTempA[3]) < (uint16)g_spriteScreenBY)
+ return false;
+ if ((uint16)(g_spriteScreenBX + g_spriteCollTempB[2]) < (uint16)g_spriteScreenAX)
+ return false;
+ if ((uint16)(g_spriteScreenBY + g_spriteCollTempB[3]) < (uint16)g_spriteScreenAY)
+ return false;
+
+ return true;
+}
+
+void BoltEngine::setSSpriteCollision(SSprite *sprite, int16 *bounds) {
+ // bounds is a 4-word array: [offsetX, offsetY, width, height]
+ byte *firstFrame = sprite->frameData[0];
+
+ sprite->collX = READ_UINT16(firstFrame + 6) + bounds[0];
+ sprite->collY = bounds[1] + READ_UINT16(firstFrame + 8);
+ sprite->collW = bounds[2];
+ sprite->collH = bounds[3];
+}
+
+bool BoltEngine::sSpriteAlive(SSprite *sprite) {
+ return (sprite->flags & 0x01) != 0;
+}
+
+void BoltEngine::getSSpriteLoc(SSprite *sprite, Common::Point *out) {
+ (*out).x = sprite->x;
+ (*out).y = sprite->y;
+}
+
+void BoltEngine::getSSpriteAccel(SSprite *sprite, int16 *out) {
+ out[0] = sprite->accelX;
+ out[1] = sprite->accelY;
+}
+
+void BoltEngine::getSSpriteVelocity(SSprite *sprite, int16 *out) {
+ out[0] = sprite->velocityX;
+ out[1] = sprite->velocityY;
+}
+
+void BoltEngine::getSSpriteDrag(SSprite *sprite, int16 *out) {
+ out[0] = sprite->dragX;
+ out[1] = sprite->dragY;
+}
+
+void BoltEngine::setSSpriteXLimit(SSprite *sprite, int16 high, int16 low) {
+ sprite->xLimitHigh = high;
+ sprite->xLimitLow = low;
+ sprite->flags |= 0x20;
+}
+
+void BoltEngine::setSSpriteYLimit(SSprite *sprite, int16 high, int16 low) {
+ sprite->yLimitHigh = high;
+ sprite->yLimitLow = low;
+ sprite->flags |= 0x40;
+}
+
+void BoltEngine::setSSpriteInfo(SSprite *sprite, int16 info) {
+ sprite->userInfo = info;
+}
+
+int16 BoltEngine::getSSpriteInfo(SSprite *sprite) {
+ return sprite->userInfo;
+}
+
+void BoltEngine::freezeSSprite(SSprite *sprite) {
+ sprite->flags |= 0x80;
+}
+
+void BoltEngine::unfreezeSSprite(SSprite *sprite) {
+ sprite->flags &= 0x0F7F;
+}
} // End of namespace Bolt
Commit: bb96b5c22d29cb187fd6d94c0376114e64f0f387
https://github.com/scummvm/scummvm/commit/bb96b5c22d29cb187fd6d94c0376114e64f0f387
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Fix Fred's minigame regression
Changed paths:
engines/bolt/booths/fred.cpp
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index 2622d3ec7ee..ceaa40d8ebe 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -1131,7 +1131,7 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
int16 typeIdx = mode - 0x0B;
state->animTable = getResolvedPtr(g_fredBalloonSprite, typeIdx * 4);
state->frameIndex = 0;
- state->frameCountdown = READ_UINT16(g_fredBalloonSprite + typeIdx * 4 + 4);
+ state->frameCountdown = READ_UINT16(state->animTable + 4);
return;
}
@@ -1144,12 +1144,10 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
// Standard Fred animation...
state->animMode = mode;
-
- byte *animTable = g_fredSprites[mode];
- state->animTable = animTable;
+ state->animTable = g_fredSprites[mode];
state->frameIndex = 0;
- state->frameCountdown = READ_UINT16(animTable + 4);
+ state->frameCountdown = READ_UINT16(state->animTable + 4);
// Play sound based on mode...
switch (mode) {
Commit: 4ae8a3557ac6f46e2b24d7b9f9a71a3204892e8c
https://github.com/scummvm/scummvm/commit/4ae8a3557ac6f46e2b24d7b9f9a71a3204892e8c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement George's minigame
Changed paths:
engines/bolt/bolt.h
engines/bolt/booths/george.cpp
engines/bolt/booths/scooby.cpp
engines/bolt/resource.cpp
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 5c9f70f4025..6e4235aa41d 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -301,10 +301,14 @@ typedef struct SSprite {
typedef struct SoundInfo {
byte *data;
uint32 size;
+ byte priority;
+ byte channel;
SoundInfo() {
data = nullptr;
size = 0;
+ priority = 0;
+ channel = 0;
}
} SoundInfo;
@@ -340,6 +344,48 @@ typedef struct FredEntityState {
}
} FredEntityState;
+// GEORGE GAME
+
+typedef struct GeorgeEntityState {
+ uint16 flags;
+ int16 frameCountdown;
+ int16 animMode;
+ int16 variant;
+ int16 frameIndex;
+ byte *animTable;
+ int32 joyInput;
+ int32 x;
+ int32 y;
+ int32 prevX;
+ int32 prevY;
+ int32 velX;
+ int32 velY;
+ int32 accelX;
+ int32 accelY;
+ byte *pathTable;
+ int16 pathIndex;
+
+ GeorgeEntityState() {
+ flags = 0;
+ frameCountdown = 0;
+ animMode = 0;
+ variant = 0;
+ frameIndex = 0;
+ animTable = nullptr;
+ joyInput = 0;
+ x = 0;
+ y = 0;
+ prevX = 0;
+ prevY = 0;
+ velX = 0;
+ velY = 0;
+ accelX = 0;
+ accelY = 0;
+ pathTable = nullptr;
+ pathIndex = 0;
+ }
+} GeorgeEntityState;
+
// SCOOBY GAME
typedef struct ScoobyState {
@@ -418,10 +464,10 @@ typedef struct ScoobyRect {
// TOPCAT GAME
typedef struct TopCatAnim {
- int16 animType; // + 00
- int16 animIndex;// + 02
- int16 transitionToNextQuestionFlag;// + 04
- int16 *seqPtr; // + 04
+ int16 animType;
+ int16 animIndex;
+ int16 transitionToNextQuestionFlag;
+ int16 *seqPtr;
TopCatAnim() {
animType = 0;
@@ -898,7 +944,92 @@ protected:
const char *g_fredSaveFile = "FredBC";
// --- GEORGE ---
- int16 georgeGame(int16 prevBooth) { return 0; }
+ int16 georgeGame(int16 prevBooth);
+ bool initGeorge();
+ void cleanUpGeorge();
+ bool initGeorgeLevel(int16 level, int16 variant);
+ void termGeorgeLevel(int16 level, int16 variant);
+ void swapGeorgeFrameArray();
+ void swapGeorgeHelpEntry();
+ void swapGeorgeThresholds();
+ int16 playGeorge();
+ int16 helpGeorge();
+ void hiliteGeorgeHelpObject(byte *entry, int16 highlight);
+ void advanceHelpAnimation();
+ bool spawnSatellite();
+ int16 getRandomSatelliteWait();
+ int16 getRandomAsteroidRow();
+ bool confirmAsteroidHitTest();
+ bool spawnAsteroid();
+ int16 getRandomAsteroidWait();
+ int16 getAsteroidRow();
+ void setGeorgeAnimMode(GeorgeEntityState *sat, int16 mode);
+ void setSatelliteAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
+ void setAsteroidAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
+ void drawFlyingObjects();
+ void getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority);
+ void playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound);
+ void updateGeorgeSound();
+
+ static void swapGeorgeFrameArrayCb();
+ static void swapGeorgeHelpEntryCb();
+ static void swapGeorgeThresholdsCb();
+
+ BOLTLib *g_georgeBoltLib = nullptr;
+ BOLTCallbacks g_georgeBoltCallbacks;
+
+ static BOLTCallback g_georgeTypeLoadCallbacks[28];
+ static BOLTCallback g_georgeTypeFreeCallbacks[28];
+
+ byte *g_georgePrevActiveHelpObject = nullptr;
+ byte *g_georgeActiveHelpObject = nullptr;
+ int16 g_georgeHelpStep = -1;
+ int16 g_georgeHelpActive = 1;
+ const char *g_georgeSaveFileName = "GeorgeBE";
+
+ GeorgeEntityState **g_georgeEntityList = nullptr;
+ int16 g_georgeCarIdx = 0;
+ int16 g_georgeNumSatellites = 0;
+ int16 g_georgeFirstAsteroidIdx = 0;
+ int16 g_georgeTotalSatellites = 0;
+ byte *g_georgeCarPics[3] = { nullptr };
+ byte *g_georgeHelpObjects = nullptr;
+ byte *g_georgeHelpSequence = nullptr;
+ uint32 g_georgeHelpTimer = 0;
+ byte *g_georgeSatelliteShuffleTable = nullptr;
+ byte *g_georgeAsteroidShuffleTable = nullptr;
+ int16 *g_georgeSatelliteThresholds = nullptr;
+ int16 *g_georgeAsteroidThresholds = nullptr;
+ byte *g_georgeSatellitePaths = nullptr;
+ byte *g_georgeAsteroidPaths = nullptr;
+ byte *g_georgeCollisionRect = nullptr;
+ byte *g_georgeSatelliteCollisionRects = nullptr;
+ byte *g_georgeAsteroidCollisionRects = nullptr;
+ byte *g_georgeSatelliteGfx = nullptr;
+ byte *g_georgeAsteroidGfx = nullptr;
+ int16 g_georgeSatelliteWait = 0;
+ int16 g_georgeAsteroidWait = 0;
+ int16 g_georgeSatelliteSearchIdx = 0;
+ int16 g_georgeAsteroidSearchIdx = 0;
+ int16 g_georgeHitSearchIdx = 0;
+ byte *g_georgeBgPic = nullptr;
+ byte *g_georgePalette = nullptr;
+ byte *g_georgePalCycleRawData = nullptr;
+ XPCycleState g_georgePalCycleSpecs[4];
+ int16 g_georgeCollectedSatellitesNum = 0;
+ SoundInfo g_georgeSoundCarStartUp;
+ SoundInfo g_georgeSoundCarGoesAway;
+ SoundInfo g_georgeSoundCarLoopHi;
+ SoundInfo g_georgeSoundCarLoopLo;
+ SoundInfo g_georgeSoundCarTumble;
+ SoundInfo **g_georgeSatelliteSoundList = nullptr;
+ byte g_georgeSoundChannelCounter = 0;
+ SoundInfo *g_georgeSoundToPlay = nullptr;
+ SoundInfo *g_georgeSoundCurrent = nullptr;
+ SoundInfo *g_georgeSoundQueued = nullptr;
+ SoundInfo *g_georgeSoundNext = nullptr;
+ int16 g_georgeSaveData[3] = { 0 };
+ int16 *g_georgeThresholds = nullptr;
// --- HUCK ---
int16 huckGame(int16 prevBooth) { return 0; }
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
index d0e5b38e0bf..9fb7c82aec6 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/booths/george.cpp
@@ -23,6 +23,1371 @@
namespace Bolt {
+int16 BoltEngine::georgeGame(int16 prevBooth) {
+ int16 result;
+ int16 prevInactivity = _xp->setInactivityTimer(30);
+ if (!initGeorge()) {
+ cleanUpGeorge();
+ return 7;
+ }
+
+ result = playGeorge();
+ cleanUpGeorge();
+ _xp->setInactivityTimer(prevInactivity);
+ return result;
+}
+
+bool BoltEngine::initGeorge() {
+ if (!openBOLTLib(&g_georgeBoltLib, &g_georgeBoltCallbacks, assetPath("george.blt")))
+ return false;
+
+ if (!getBOLTGroup(g_georgeBoltLib, 0, 1))
+ return false;
+
+ g_georgeBgPic = memberAddr(g_georgeBoltLib, (g_displayMode != 0) ? 1 : 0);
+ g_georgeHelpObjects = memberAddr(g_georgeBoltLib, 0x1F);
+ g_georgeHelpSequence = memberAddr(g_georgeBoltLib, 0x20);
+
+ g_georgeCarPics[0] = memberAddr(g_georgeBoltLib, 0x10);
+ g_georgeCarPics[1] = memberAddr(g_georgeBoltLib, 0x11);
+ g_georgeCarPics[2] = memberAddr(g_georgeBoltLib, 0x12);
+ g_georgeCollisionRect = memberAddr(g_georgeBoltLib, 0x13);
+
+ getGeorgeSoundInfo(g_georgeBoltLib, 0x16, &g_georgeSoundCarTumble, 6);
+ getGeorgeSoundInfo(g_georgeBoltLib, 0x15, &g_georgeSoundCarLoopHi, 0);
+ getGeorgeSoundInfo(g_georgeBoltLib, 0x14, &g_georgeSoundCarLoopLo, 0);
+ getGeorgeSoundInfo(g_georgeBoltLib, 0x17, &g_georgeSoundCarStartUp, 3);
+ getGeorgeSoundInfo(g_georgeBoltLib, 0x18, &g_georgeSoundCarGoesAway, 3);
+
+ g_georgeSoundCurrent = nullptr;
+ g_georgeSoundToPlay = nullptr;
+ g_georgeSoundNext = nullptr;
+ g_georgeSoundQueued = nullptr;
+ g_georgeSoundChannelCounter = 0;
+
+ // Load save data...
+ if (!vLoad(&g_georgeSaveData, g_georgeSaveFileName)) {
+ g_georgeSaveData[0] = 0;
+ g_georgeSaveData[1] = 0;
+ g_georgeSaveData[2] = 0;
+ }
+
+ if (!initGeorgeLevel(g_georgeSaveData[1], g_georgeSaveData[2])) {
+ freeBOLTGroup(g_georgeBoltLib, 0, 1);
+ return false;
+ }
+
+ // Initialize satellites from list...
+ g_georgeCollectedSatellitesNum = 0;
+ while (g_georgeCollectedSatellitesNum < g_georgeSaveData[0]) {
+ setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + g_georgeCollectedSatellitesNum], 4, 0);
+ g_georgeCollectedSatellitesNum++;
+ }
+
+ // Drain timer events...
+ uint32 dummy;
+ while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+
+ // Display palette and background to front...
+ displayColors(g_georgePalette, stBack, 0);
+ displayPic(g_georgeBgPic, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+
+ _xp->setTransparency(true);
+
+ // Display palette to both surfaces...
+ displayColors(g_georgePalette, stFront, 0);
+ displayColors(g_georgePalette, stBack, 1);
+
+ // Display background to back surface...
+ displayPic(g_georgeBgPic, g_displayX, g_displayY, stBack);
+
+ drawFlyingObjects();
+
+ _xp->updateDisplay();
+ _xp->startCycle(g_georgePalCycleSpecs);
+ _xp->setFrameRate(12);
+
+ return true;
+}
+
+void BoltEngine::cleanUpGeorge() {
+ // Save current progress...
+ int16 savedLevel = g_georgeSaveData[1];
+ int16 savedVariant = g_georgeSaveData[2];
+
+ g_georgeSaveData[0] = g_georgeCollectedSatellitesNum;
+
+ // Check if collection threshold for current level was met...
+ if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
+ // Advance level...
+ g_georgeSaveData[1]++;
+ if (g_georgeSaveData[1] >= 10)
+ g_georgeSaveData[1] = 9;
+
+ // Advance variant...
+ g_georgeSaveData[2]++;
+ if (g_georgeSaveData[2] >= 10)
+ g_georgeSaveData[2] = 0;
+
+ // Reset collected count for next level...
+ g_georgeSaveData[0] = 0;
+ }
+
+ vSave(g_georgeSaveData, sizeof(g_georgeSaveData), g_georgeSaveFileName);
+
+ _xp->stopCycle();
+
+ termGeorgeLevel(savedLevel, savedVariant);
+
+ freeBOLTGroup(g_georgeBoltLib, 0, 1);
+ closeBOLTLib(&g_georgeBoltLib);
+
+ _xp->setFrameRate(0);
+ _xp->fillDisplay(0, stFront);
+ _xp->updateDisplay();
+}
+
+bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
+ int16 levelGroup = (level * 2) << 8 | 0x100;
+ int16 variantGroup = (variant * 2) << 8 | 0x200;
+
+ if (!getBOLTGroup(g_georgeBoltLib, levelGroup, 1))
+ return false;
+
+ if (!getBOLTGroup(g_georgeBoltLib, variantGroup, 1)) {
+ freeBOLTGroup(g_georgeBoltLib, levelGroup, 1);
+ return false;
+ }
+
+ g_georgeThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup);
+ g_georgePalette = memberAddr(g_georgeBoltLib, variantGroup);
+ g_georgePalCycleRawData = memberAddr(g_georgeBoltLib, variantGroup + 1);
+ boltCycleToXPCycle(g_georgePalCycleRawData, g_georgePalCycleSpecs);
+
+ g_georgeSatelliteGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x12);
+ g_georgeAsteroidGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x26);
+ g_georgeSatelliteThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x01);
+ g_georgeAsteroidThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x02);
+ g_georgeSatelliteCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x19);
+ g_georgeAsteroidCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x2A);
+ g_georgeSatellitePaths = memberAddr(g_georgeBoltLib, levelGroup + 0x27);
+ g_georgeAsteroidPaths = memberAddr(g_georgeBoltLib, levelGroup + 0x3C);
+
+ // Allocate and build asteroid shuffle table...
+ int16 numAsteroidRows = g_georgeThresholds[3];
+ int16 numAsteroidCols = g_georgeThresholds[4];
+ g_georgeSatelliteShuffleTable = (byte *)_xp->allocMem((uint32)(numAsteroidCols + 1) * (numAsteroidRows));
+ if (!g_georgeSatelliteShuffleTable) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ // Fill each row with sequential values 0..numAsteroidCols, then shuffle...
+ for (int16 row = 0; row < numAsteroidRows; row++) {
+ int16 rowBase = (numAsteroidCols + 1) * row;
+ g_georgeSatelliteShuffleTable[rowBase] = 0;
+
+ for (int16 col = 0; col < numAsteroidCols; col++)
+ g_georgeSatelliteShuffleTable[rowBase + col + 1] = (byte)col;
+
+ // Fisher-Yates shuffle...
+ for (int16 i = 0; i < numAsteroidCols; i++) {
+ int16 j = _xp->getRandom(numAsteroidCols);
+ byte tmp = g_georgeSatelliteShuffleTable[rowBase + i + 1];
+ g_georgeSatelliteShuffleTable[rowBase + i + 1] = g_georgeSatelliteShuffleTable[rowBase + j + 1];
+ g_georgeSatelliteShuffleTable[rowBase + j + 1] = tmp;
+ }
+ }
+
+ // Allocate and build satellite shuffle table...
+ int16 numSatelliteRows = g_georgeThresholds[9];
+ int16 numSatelliteCols = g_georgeThresholds[10];
+ g_georgeAsteroidShuffleTable = (byte *)_xp->allocMem((uint32)(numSatelliteCols + 1) * (numSatelliteRows));
+ if (!g_georgeAsteroidShuffleTable) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ for (int16 row = 0; row < numSatelliteRows; row++) {
+ int16 rowBase = (numSatelliteCols + 1) * row;
+ g_georgeAsteroidShuffleTable[rowBase] = 0;
+
+ for (int16 col = 0; col < numSatelliteCols; col++)
+ g_georgeAsteroidShuffleTable[rowBase + col + 1] = (byte)col;
+
+ for (int16 i = 0; i < numSatelliteCols; i++) {
+ int16 j = _xp->getRandom(numSatelliteCols);
+ byte tmp = g_georgeAsteroidShuffleTable[rowBase + i + 1];
+ g_georgeAsteroidShuffleTable[rowBase + i + 1] = g_georgeAsteroidShuffleTable[rowBase + j + 1];
+ g_georgeAsteroidShuffleTable[rowBase + j + 1] = tmp;
+ }
+ }
+
+ // Compute satellite list indices...
+ g_georgeNumSatellites = 0;
+ int16 numSatellites = g_georgeThresholds[0];
+ g_georgeFirstAsteroidIdx = numSatellites;
+
+ int16 numAsteroids = g_georgeThresholds[11];
+ if (numAsteroids < 1)
+ numAsteroids = 1;
+
+ g_georgeCarIdx = g_georgeFirstAsteroidIdx + numAsteroids;
+ g_georgeTotalSatellites = g_georgeCarIdx + 1;
+
+ // Allocate satellite list...
+ g_georgeEntityList = (GeorgeEntityState **)_xp->allocMem((g_georgeTotalSatellites + 1) * sizeof(GeorgeEntityState *));
+ if (!g_georgeEntityList) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
+ g_georgeEntityList[i] = new GeorgeEntityState();
+ if (!g_georgeEntityList[i]) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ g_georgeEntityList[i]->flags = 0;
+ }
+
+ g_georgeEntityList[g_georgeTotalSatellites] = nullptr;
+
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ assert(carSat);
+
+ carSat->flags = 1;
+ carSat->joyInput = 0;
+ carSat->x = 0x4000; // fixed point = 64.0
+ carSat->y = 0x12C00; // fixed point = 300.0
+ carSat->velX = 0;
+ carSat->velY = 0;
+ carSat->accelX = 0;
+ carSat->accelY = 0;
+ carSat->pathTable = nullptr;
+ carSat->pathIndex = 0;
+ setGeorgeAnimMode(carSat, 0);
+
+ int32 numObjectTypes = g_georgeThresholds[6];
+ byte *objShuf = (byte *)_xp->allocMem(numObjectTypes);
+ if (!objShuf) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ for (int16 i = 0; i < numObjectTypes; i++)
+ objShuf[i] = (byte)i;
+
+ // Assign random anim modes to satellites...
+ for (int16 i = 0; i < numSatellites; i++) {
+ if ((i % numObjectTypes) == 0) {
+ // Re-shuffle every full cycle
+ for (int16 j = 0; j < numObjectTypes; j++) {
+ int16 k = _xp->getRandom(numObjectTypes);
+ byte tmp = objShuf[j];
+ objShuf[j] = objShuf[k];
+ objShuf[k] = tmp;
+ }
+ }
+
+ int16 animType = objShuf[i % numObjectTypes];
+ setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + i], 3, animType);
+ }
+
+ _xp->freeMem(objShuf);
+
+ // Allocate asteroid shuffled list...
+ int16 numAsteroidTypes = g_georgeThresholds[11];
+ if (numAsteroidTypes < 1)
+ numAsteroidTypes = 1;
+
+ byte *astShuf = (byte *)_xp->allocMem(numAsteroidTypes);
+ if (!astShuf) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ for (int16 i = 0; i < numAsteroidTypes; i++)
+ astShuf[i] = (byte)i;
+
+ for (int16 i = 0; i < numAsteroids; i++) {
+ if ((i % numAsteroidTypes) == 0) {
+ for (int16 j = 0; j < numAsteroidTypes; j++) {
+ int16 k = _xp->getRandom(numAsteroidTypes);
+ byte tmp = astShuf[j];
+ astShuf[j] = astShuf[k];
+ astShuf[k] = tmp;
+ }
+ }
+
+ int16 animType = astShuf[i % numAsteroidTypes];
+ setAsteroidAnimMode(g_georgeEntityList[g_georgeFirstAsteroidIdx + i], 5, animType);
+ }
+
+ _xp->freeMem(astShuf);
+
+ // Allocate and load sound list for satellites...
+ g_georgeSatelliteSoundList = (SoundInfo **)_xp->allocMem(numObjectTypes * sizeof(SoundInfo *));
+ if (!g_georgeSatelliteSoundList) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ for (int16 i = 0; i < numObjectTypes; i++) {
+ g_georgeSatelliteSoundList[i] = new SoundInfo();
+ if (!g_georgeSatelliteSoundList[i]) {
+ termGeorgeLevel(level, variant);
+ return false;
+ }
+
+ int16 member = variantGroup | ((i + 0x1A) & 0xFF);
+ getGeorgeSoundInfo(g_georgeBoltLib, member, g_georgeSatelliteSoundList[i], 6);
+ }
+
+ g_georgeSatelliteWait = 1;
+ g_georgeSatelliteSearchIdx = 0;
+ g_georgeAsteroidSearchIdx = 0;
+ g_georgeAsteroidWait = getRandomAsteroidWait();
+ g_georgeHitSearchIdx = 0;
+ g_georgeCollectedSatellitesNum = 0;
+
+ return true;
+}
+
+void BoltEngine::termGeorgeLevel(int16 level, int16 variant) {
+ if (g_georgeEntityList) {
+ for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (sat) {
+ delete sat;
+ g_georgeEntityList[i] = nullptr;
+ }
+ }
+
+ _xp->freeMem(g_georgeEntityList);
+ g_georgeEntityList = nullptr;
+ }
+
+ if (g_georgeSatelliteSoundList) {
+ int32 numObjectTypes = g_georgeThresholds[6];
+ for (int16 i = 0; i < numObjectTypes; i++) {
+ SoundInfo *snd = g_georgeSatelliteSoundList[i];
+ if (snd) {
+ delete snd;
+ g_georgeSatelliteSoundList[i] = nullptr;
+ }
+ }
+
+ _xp->freeMem(g_georgeSatelliteSoundList);
+ g_georgeSatelliteSoundList = nullptr;
+ }
+
+ if (g_georgeAsteroidShuffleTable) {
+ _xp->freeMem(g_georgeAsteroidShuffleTable);
+ g_georgeAsteroidShuffleTable = nullptr;
+ }
+
+ if (g_georgeSatelliteShuffleTable) {
+ _xp->freeMem(g_georgeSatelliteShuffleTable);
+ g_georgeSatelliteShuffleTable = nullptr;
+ }
+
+ freeBOLTGroup(g_georgeBoltLib, (variant * 2) << 8 | 0x200, 1);
+ freeBOLTGroup(g_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
+}
+
+void BoltEngine::swapGeorgeFrameArray() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ uint32 size = g_boltCurrentMemberEntry->decompSize;
+ uint32 off = 0;
+
+ while ((int32)(size - off) > 0) {
+ WRITE_UINT16(data + off + 4, READ_BE_UINT16(data + off + 4));
+ resolveIt((uint32 *)(data + off));
+ off += 6;
+ }
+}
+
+void BoltEngine::swapGeorgeHelpEntry() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ uint32 size = g_boltCurrentMemberEntry->decompSize;
+ uint32 off = 0;
+ uint16 palCountOff = (uint16)(off + 0x10);
+
+ while ((int32)(size - off) > 0) {
+ WRITE_UINT32(data + off, READ_BE_UINT32(data + off));
+ WRITE_UINT32(data + off + 0x08, READ_BE_UINT32(data + off + 0x08));
+ WRITE_UINT16(data + off + 0x0C, READ_BE_UINT16(data + off + 0x0C));
+ WRITE_UINT16(data + off + 0x0E, READ_BE_UINT16(data + off + 0x0E));
+ WRITE_UINT16(data + palCountOff, READ_BE_UINT16(data + palCountOff));
+ unpackColors(READ_UINT16(data + palCountOff), data + off + 0x12);
+ unpackColors(READ_UINT16(data + palCountOff), data + off + 0x2E);
+ resolveIt((uint32 *)(data + off + 0x04));
+
+ off += 0x4A;
+ palCountOff += 0x4A;
+ }
+}
+
+void BoltEngine::swapGeorgeThresholds() {
+ byte *data = g_boltCurrentMemberEntry->dataPtr;
+ for (int16 i = 0; i < 12; i++)
+ WRITE_UINT16(data + i * 2, READ_BE_UINT16(data + i * 2));
+}
+
+int16 BoltEngine::playGeorge() {
+ int16 returnCode = 0;
+ int16 flyActive = 1;
+ int16 winSeq = 0;
+ int16 exitGame = 0;
+ int16 firstFrame = 1;
+ int32 joyY = 0;
+
+ if (g_georgeHelpActive) {
+ if (!helpGeorge())
+ return 7;
+ }
+
+ _xp->setInactivityTimer(30);
+ _xp->enableController();
+
+ while (!shouldQuit()) {
+ // ---- Event loop ----
+ while (!shouldQuit()) {
+ if (joyY)
+ _xp->setInactivityTimer(30);
+
+ uint32 eventData = 0;
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
+ if (!eventType)
+ break;
+
+ switch (eventType) {
+ case etJoystick:
+ joyY = (int16)eventData;
+ break;
+ case etMouseDown:
+ case etInactivity:
+ if (!winSeq) {
+ if (!helpGeorge()) {
+ returnCode = 7;
+ exitGame = 1;
+ }
+ }
+
+ break;
+ case etSound:
+ updateGeorgeSound();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (exitGame) {
+ if (!winSeq)
+ playGeorgeSound(nullptr, nullptr);
+
+ _xp->stopSound();
+ return returnCode;
+ }
+
+ drawFlyingObjects();
+ _xp->updateDisplay();
+
+ // ---- Car physics / input ----
+ if (flyActive) {
+ if (firstFrame) {
+ firstFrame = 0;
+ playGeorgeSound(&g_georgeSoundCarStartUp, &g_georgeSoundCarLoopHi);
+ }
+
+ if (joyY) {
+ flyActive = 0;
+ } else {
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ int16 carY = (int16)(carSat->y >> 8);
+
+ if (carY > 0xD0)
+ carSat->velY = 0xFFFFFA00;
+ else if (carY > 0xBC)
+ carSat->velY = 0xFFFFFB00;
+ else if (carY > 0xA8)
+ carSat->velY = 0xFFFFFC00;
+ else if (carY > 0x9E)
+ carSat->velY = 0xFFFFFD00;
+ else if (carY > 0x94)
+ carSat->velY = 0xFFFFFE00;
+ else if (carY > 0x86)
+ carSat->velY = 0xFFFFFF00;
+ else {
+ carSat->velY = 0;
+ flyActive = 0;
+ }
+ }
+ } else {
+ if (!winSeq) {
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ carSat->accelY = (joyY * 3) << 8;
+
+ if (--g_georgeSatelliteWait == 0) {
+ if (spawnSatellite())
+ g_georgeSatelliteWait = getRandomSatelliteWait();
+ else
+ g_georgeSatelliteWait = 1;
+ }
+
+ if (--g_georgeAsteroidWait == 0) {
+ if (spawnAsteroid())
+ g_georgeAsteroidWait = getRandomAsteroidWait();
+ else
+ g_georgeAsteroidWait = 1;
+ }
+ }
+ }
+
+ // ---- Pass 1: animate all satellites ----
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+ if (!(sat->flags & 1))
+ continue;
+
+ sat->prevX = sat->x;
+ sat->prevY = sat->y;
+ sat->joyInput = joyY;
+ sat->flags &= ~0x06;
+
+ if (--sat->frameCountdown == 0) {
+ int16 nextFrame = sat->frameIndex + 1;
+ sat->frameIndex = nextFrame;
+
+ byte *entry = sat->animTable + nextFrame * 6;
+ if (!READ_UINT32(entry)) {
+ sat->frameIndex = 0;
+ sat->flags |= 4;
+ }
+
+ entry = sat->animTable + sat->frameIndex * 6;
+ sat->frameCountdown = READ_UINT16(entry + 0x04);
+ sat->flags |= 2;
+ }
+
+ if (sat->animMode == 1 || sat->animMode == 2) {
+ if (sat->flags & 4)
+ setGeorgeAnimMode(sat, 0);
+ }
+ }
+
+ // ---- Pass 2: physics ----
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+ if (!(sat->flags & 1))
+ continue;
+
+ switch (sat->animMode) {
+ case 0:
+ case 1:
+ case 2: {
+ sat->x += sat->velX;
+ sat->y += sat->velY;
+ sat->velX += sat->accelX;
+ sat->velY += sat->accelY;
+
+ if (sat->x < 0x4000) {
+ sat->x = 0x4000;
+ } else {
+ int32 xMax = (int32)(winSeq ? 0x1C2 : 0x40) << 8;
+ if (sat->x > xMax)
+ sat->x = xMax;
+ }
+
+ if (sat->y < 0x2600) {
+ sat->y = 0x2600;
+ } else {
+ int32 yMax = (int32)(flyActive ? 0x12C : 0xD0) << 8;
+ if (sat->y > yMax)
+ sat->y = yMax;
+ }
+
+ if (sat->velY < (int32)0xFFFFFA00)
+ sat->velY = (int32)0xFFFFFA00;
+ else if (sat->velY > 0x600)
+ sat->velY = 0x600;
+
+ if (!winSeq) {
+ if (sat->velY <= 0) {
+ if (g_georgeSoundToPlay != &g_georgeSoundCarLoopHi)
+ playGeorgeSound(&g_georgeSoundCarLoopHi, &g_georgeSoundCarLoopHi);
+ } else {
+ if (g_georgeSoundToPlay != &g_georgeSoundCarLoopLo)
+ playGeorgeSound(&g_georgeSoundCarLoopLo, &g_georgeSoundCarLoopLo);
+ }
+ } else {
+ int32 xExit = (int32)0x1C2 << 8;
+ if (sat->x >= xExit)
+ exitGame = 1;
+ }
+ break;
+ }
+
+ case 3:
+ case 5: {
+ sat->pathIndex++;
+ byte *pathBase = sat->pathTable;
+ int16 pathLen = READ_UINT16(pathBase);
+
+ if (sat->pathIndex >= pathLen) {
+ sat->flags &= ~1;
+ } else {
+ byte *entry = pathBase + sat->pathIndex * 4;
+ sat->x = (int32)(int16)READ_UINT16(entry + 0x02) << 8;
+ sat->y = (int32)(int16)READ_UINT16(entry + 0x04) << 8;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ // ---- Pass 3: collision detection ----
+ if (!winSeq) {
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+
+ byte *carFrameData = getResolvedPtr(carSat->animTable, carSat->frameIndex * 6);
+ int16 carX1 = (int16)(carSat->x >> 8) + READ_UINT16(g_georgeCollisionRect + 0x00) + READ_UINT16(carFrameData + 0x06);
+ int16 carY1 = (int16)(carSat->y >> 8) + READ_UINT16(g_georgeCollisionRect + 0x02) + READ_UINT16(carFrameData + 0x08);
+ int16 carW = READ_UINT16(g_georgeCollisionRect + 0x04);
+ int16 carH = READ_UINT16(g_georgeCollisionRect + 0x06);
+
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+
+ if (!(sat->flags & 1))
+ continue;
+
+ if (sat->animMode == 3) {
+ if (carSat->animMode != 0)
+ continue;
+
+ byte *collRect = getResolvedPtr(g_georgeSatelliteCollisionRects, sat->variant * 4);
+ byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
+
+ int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRect + 0x00) + READ_UINT16(satFrameData + 0x06);
+ int16 sy1 = (int16)(sat->y >> 8) + READ_UINT16(collRect + 0x02) + READ_UINT16(satFrameData + 0x08);
+ int16 sw = READ_UINT16(collRect + 0x04);
+ int16 sh = READ_UINT16(collRect + 0x06);
+
+ Common::Rect carRect(carX1, carY1, carX1 + carW, carY1 + carH);
+ Common::Rect satRect(sx1, sy1, sx1 + sw, sy1 + sh);
+ if (!carRect.intersects(satRect))
+ continue;
+
+ setGeorgeAnimMode(carSat, 1);
+ setSatelliteAnimMode(sat, 4, 0);
+ g_georgeCollectedSatellitesNum++;
+
+ if (spawnSatellite())
+ g_georgeSatelliteWait = getRandomSatelliteWait();
+ else
+ g_georgeSatelliteWait = 1;
+
+ playGeorgeSound(g_georgeSatelliteSoundList[sat->variant], nullptr);
+ } else if (sat->animMode == 5) {
+ if (carSat->animMode != 0 && carSat->animMode != 1)
+ continue;
+
+ byte *collRects = getResolvedPtr(g_georgeAsteroidCollisionRects, sat->variant * 4);
+ byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
+
+ int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRects + 0x00) + READ_UINT16(satFrameData + 0x06);
+ int16 sy1 = (int16)(sat->y >> 8) + READ_UINT16(collRects + 0x02) + READ_UINT16(satFrameData + 0x08);
+ int16 sw = READ_UINT16(collRects + 0x04);
+ int16 sh = READ_UINT16(collRects + 0x06);
+
+ Common::Rect carRect(carX1, carY1, carX1 + carW, carY1 + carH);
+ Common::Rect astRect(sx1, sy1, sx1 + sw, sy1 + sh);
+ if (!carRect.intersects(astRect))
+ continue;
+
+ setGeorgeAnimMode(carSat, 2);
+
+ if (confirmAsteroidHitTest())
+ g_georgeCollectedSatellitesNum--;
+
+ playGeorgeSound(&g_georgeSoundCarTumble, nullptr);
+ break;
+ }
+ }
+ }
+
+ if (!winSeq) {
+ if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
+ if (g_georgeSoundToPlay &&
+ g_georgeSoundToPlay->priority > g_georgeSoundCarGoesAway.priority)
+ continue;
+
+ if (g_georgeSoundQueued &&
+ g_georgeSoundQueued->priority > g_georgeSoundCarGoesAway.priority)
+ continue;
+
+ bool allDone = true;
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+
+ if (i == g_georgeCarIdx)
+ continue;
+
+ if (sat->flags & 1) {
+ allDone = false;
+ break;
+ }
+ }
+
+ if (allDone) {
+ // We've won!
+ winSeq = 1;
+ returnCode = 0x10;
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ carSat->accelX = 0x200;
+ carSat->accelY = 0;
+ playGeorgeSound(&g_georgeSoundCarGoesAway, nullptr);
+ }
+ }
+ }
+ }
+
+ if (!winSeq)
+ playGeorgeSound(nullptr, nullptr);
+
+ _xp->stopSound();
+ return returnCode;
+}
+
+int16 BoltEngine::helpGeorge() {
+ byte *firstObj = getResolvedPtr(g_georgeHelpObjects, 0);
+ byte *firstInfo = getResolvedPtr(firstObj, 0x04);
+ int16 curX = READ_UINT16(firstInfo + 0x06) + READ_UINT16(firstInfo + 0x0A) - 10;
+ int16 curY = READ_UINT16(firstInfo + 0x08) + READ_UINT16(firstInfo + 0x0C) - 10;
+
+ byte *hoveredObj = nullptr;
+ int16 exitCode = -1;
+ int16 animPlaying = 0;
+ int16 audioTick = 0;
+ int16 animReady = 0;
+
+ _xp->setFrameRate(0);
+ _xp->setInactivityTimer(0);
+ _xp->stopSound();
+
+ g_georgeSoundCurrent = g_georgeSoundToPlay = g_georgeSoundNext = g_georgeSoundQueued = nullptr;
+
+ drawFlyingObjects();
+
+ for (int16 i = 0;; i++) {
+ byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ if (!obj)
+ break;
+
+ byte *sprite = getResolvedPtr(obj, 0x04);
+ displayPic(sprite, 0, 0, 0);
+ }
+
+ _xp->updateDisplay();
+
+ for (int16 i = 0;; i++) {
+ byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ if (!obj)
+ break;
+
+ _xp->getPalette(READ_UINT16(obj + 0x0C), READ_UINT16(obj + 0x10), obj + 0x2E);
+ _xp->getPalette(READ_UINT16(obj + 0x0E), READ_UINT16(obj + 0x10), obj + 0x12);
+
+ WRITE_UINT32(obj + 0x08, READ_UINT32(obj + 0x08) & ~1u);
+ if (READ_UINT32(obj) == 2)
+ hoveredObj = obj;
+ }
+
+ // Show cursor, highlight "PLAY" object
+ _xp->setCursorPos(curX, curY);
+ _xp->setCursorColor(0, 0, 0xFF);
+ _xp->showCursor();
+ g_georgeActiveHelpObject = getResolvedPtr(g_georgeHelpObjects, 0);
+ hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+
+ // ---- Event loop ----
+ while (!shouldQuit()) {
+ if (animPlaying) {
+ audioTick = maintainAudioPlay(audioTick);
+ if (!audioTick) {
+ bool sameAsHover = (hoveredObj == g_georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(hoveredObj, sameAsHover ? 1 : 0);
+ animPlaying = 0;
+ }
+ audioTick = 0;
+ }
+
+ uint32 eventData = 0;
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
+ switch (eventType) {
+ case etTimer: {
+ if (!g_georgePrevActiveHelpObject)
+ break;
+
+ if (g_georgeHelpTimer != eventData)
+ break;
+
+ g_georgeHelpTimer = _xp->startTimer(500);
+
+ bool lit = !(READ_UINT32(g_georgePrevActiveHelpObject + 0x08) & 1u);
+ hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, lit ? 1 : 0);
+
+ break;
+ }
+
+ case etMouseMove: {
+ curX = (int16)((int32)eventData >> 16);
+ curY = (int16)eventData;
+
+ byte *hit = nullptr;
+ for (int16 i = 0;; i++) {
+ byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ if (!obj)
+ break;
+
+ byte *info = getResolvedPtr(obj, 0x04);
+ int16 ox = READ_UINT16(info + 0x06);
+ int16 ow = READ_UINT16(info + 0x0A);
+ int16 oy = READ_UINT16(info + 0x08);
+ int16 oh = READ_UINT16(info + 0x0C);
+ if (curX > ox && curX < ox + ow &&
+ curY > oy && curY < oy + oh) {
+ hit = obj;
+ break;
+ }
+ }
+
+ if (hit == g_georgeActiveHelpObject)
+ break;
+
+ if (g_georgeActiveHelpObject) {
+ if (g_georgeActiveHelpObject != g_georgePrevActiveHelpObject) {
+ bool keepLit = (g_georgeActiveHelpObject == hoveredObj && animPlaying);
+ if (!keepLit)
+ hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 0);
+ }
+ }
+
+ g_georgeActiveHelpObject = hit;
+ if (!hit)
+ break;
+
+ if (hit == g_georgePrevActiveHelpObject)
+ break;
+
+ hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+ break;
+ }
+
+ case etMouseDown: {
+ animReady = 0;
+
+ if (animPlaying) {
+ if (g_georgeHelpTimer) {
+ _xp->killTimer(g_georgeHelpTimer);
+ g_georgeHelpTimer = 0;
+ }
+
+ bool hovLit = (hoveredObj == g_georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(hoveredObj, hovLit ? 1 : 0);
+ bool actLit = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, actLit ? 1 : 0);
+
+ g_georgePrevActiveHelpObject = nullptr;
+ stopAnimation();
+ animPlaying = 0;
+ animReady = 1;
+ }
+
+ if (!g_georgeActiveHelpObject)
+ break;
+
+ switch (READ_UINT16(g_georgeActiveHelpObject)) {
+ case 0:
+ case 1:
+ if (READ_UINT16(g_georgeActiveHelpObject) == 1) {
+ g_georgeHelpActive = 0;
+ }
+
+ exitCode = READ_UINT16(g_georgeActiveHelpObject);
+ break;
+ case 2:
+ if (animPlaying)
+ break;
+
+ if (animReady)
+ break;
+
+ if (startAnimation(g_rtfHandle, 30)) {
+ g_georgeHelpStep = 0;
+ animPlaying = 1;
+ }
+
+ break;
+ }
+
+ break;
+ }
+
+ case etSound:
+ audioTick = 1;
+ break;
+
+ case etTrigger:
+ if (animPlaying)
+ advanceHelpAnimation();
+ break;
+
+ default:
+ break;
+ }
+
+ if (exitCode != -1) {
+ _xp->hideCursor();
+ _xp->enableController();
+
+ // Highlight hovered object, unhighlight the rest...
+ for (int16 i = 0;; i++) {
+ byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ if (!obj)
+ break;
+
+ bool isHovered = (obj == g_georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(obj, isHovered ? 1 : 0);
+ }
+ _xp->updateDisplay();
+
+ // Unhighlight all...
+ for (int16 i = 0;; i++) {
+ byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ if (!obj)
+ break;
+
+ hiliteGeorgeHelpObject(obj, 0);
+ }
+
+ drawFlyingObjects();
+ _xp->updateDisplay();
+ _xp->setFrameRate(12);
+ _xp->setInactivityTimer(30);
+ updateGeorgeSound();
+
+ return exitCode;
+ }
+ }
+
+ return exitCode;
+}
+
+void BoltEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
+ if (!entry)
+ return;
+
+ if (highlight) {
+ _xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0C), entry + 0x12);
+ WRITE_UINT32(entry + 0x08, READ_UINT32(entry + 0x08) | 1);
+ } else {
+ _xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0C), entry + 0x2E);
+ WRITE_UINT32(entry + 0x08, READ_UINT32(entry + 0x08) & ~1);
+ }
+}
+
+void BoltEngine::advanceHelpAnimation() {
+ if (g_georgeHelpStep < 0 || g_georgeHelpStep >= 4)
+ return;
+
+ bool isHovered = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, isHovered ? 1 : 0);
+
+ byte *newActive;
+ if (!(g_georgeHelpStep & 1)) {
+ newActive = getResolvedPtr(g_georgeHelpSequence, (g_georgeHelpStep >> 1) * 4);
+ } else {
+ newActive = nullptr;
+ }
+
+ g_georgePrevActiveHelpObject = newActive;
+ g_georgeHelpTimer = 0;
+
+ hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, 1);
+
+ if (!(g_georgeHelpStep & 1)) {
+ g_georgeHelpTimer = _xp->startTimer(500);
+ } else {
+ if (g_georgeHelpTimer) {
+ _xp->killTimer(g_georgeHelpTimer);
+ g_georgeHelpTimer = 0;
+ }
+ }
+
+ g_georgeHelpStep++;
+}
+bool BoltEngine::spawnSatellite() {
+ // Count active satellites (type 3)...
+ int16 activeCount = 0;
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+
+ if (sat->animMode == 3 && (sat->flags & 1))
+ activeCount++;
+ }
+
+ // Too many active, no need to spawn more...
+ if (activeCount >= 6)
+ return false;
+
+ // Find next inactive satellite slot...
+ int16 startIdx = g_georgeSatelliteSearchIdx;
+ int16 idx = startIdx;
+ bool wrapped = false;
+ GeorgeEntityState *candidate = nullptr;
+
+ while (true) {
+ GeorgeEntityState *s = g_georgeEntityList[idx];
+ if (!s) {
+ wrapped = true;
+ idx = -1;
+ } else if (s->animMode == 3 && !(s->flags & 1)) {
+ candidate = s;
+ break;
+ }
+
+ idx++;
+ if (idx == startIdx && wrapped)
+ return false;
+ }
+
+ g_georgeSatelliteSearchIdx = idx + 1;
+
+ // Get a shuffle-table row for this asteroid...
+ int16 row = getRandomAsteroidRow();
+ int16 numCols = g_georgeThresholds[4];
+ int16 rowBase = (numCols + 1) * row;
+ int16 colIdx = g_georgeSatelliteShuffleTable[rowBase];
+ byte animIdx = g_georgeSatelliteShuffleTable[rowBase + colIdx + 1];
+
+ // Get the motion path...
+ byte *rowPaths = getResolvedPtr(g_georgeSatellitePaths, row * 4);
+ byte *path = getResolvedPtr(rowPaths, animIdx * 4);
+
+ candidate->pathTable = path;
+
+ colIdx++;
+ g_georgeSatelliteShuffleTable[rowBase] = (byte)colIdx;
+ if (colIdx >= numCols)
+ g_georgeSatelliteShuffleTable[rowBase] = 0;
+
+ // Set initial position from pic's embedded coords...
+ candidate->pathIndex = 0;
+ candidate->x = (int32)((int16)READ_UINT16(path + 0x02) << 8);
+ candidate->y = (int32)((int16)READ_UINT16(path + 0x04) << 8);
+
+ candidate->flags |= 1;
+
+ return true;
+}
+
+int16 BoltEngine::getRandomSatelliteWait() {
+ int16 range = g_georgeThresholds[2];
+ int16 topY = g_georgeThresholds[1];
+ return _xp->getRandom(range) + topY;
+}
+
+int16 BoltEngine::getRandomAsteroidRow() {
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ int16 carY = carSat->y >> 8;
+
+ // Find which row the car is in by scanning frame Y thresholds...
+ int16 numRows = g_georgeThresholds[3];
+ int16 carRow = 0;
+ int16 *thresholds = g_georgeSatelliteThresholds;
+
+ for (int16 i = 0; i < numRows; i++) {
+ if (thresholds[i] > carY)
+ break;
+
+ carRow = i + 1;
+ }
+
+ // Pick a random row, retrying if too close to car's row...
+ int16 exclusionBand = g_georgeThresholds[5];
+ int16 randRow;
+ do {
+ randRow = _xp->getRandom(g_georgeThresholds[3]);
+ } while (randRow >= (carRow - exclusionBand) && randRow < (carRow + exclusionBand));
+
+ return randRow;
+}
+
+bool BoltEngine::confirmAsteroidHitTest() {
+ GeorgeEntityState *candidate = nullptr;
+ int16 startIdx = g_georgeHitSearchIdx;
+ int16 idx = startIdx;
+ bool wrapped = false;
+
+ while (true) {
+ GeorgeEntityState *s = g_georgeEntityList[idx];
+ if (!s) {
+ wrapped = true;
+ idx = -1;
+ } else if (s->animMode == 4 && (s->flags & 1)) {
+ candidate = s;
+ break;
+ }
+
+ idx++;
+ if (idx == startIdx && wrapped)
+ return false;
+ }
+
+ g_georgeHitSearchIdx = idx + 1;
+
+ setSatelliteAnimMode(candidate, 3, candidate->variant);
+
+ return true;
+}
+
+bool BoltEngine::spawnAsteroid() {
+ // Count active asteroids (type 5)...
+ int16 activeCount = 0;
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+
+ if (sat->animMode == 5 && (sat->flags & 1))
+ activeCount++;
+ }
+
+ // No need to spawn more...
+ if (activeCount >= 1)
+ return false;
+
+ // Find next inactive asteroid slot...
+ int16 startIdx = g_georgeAsteroidSearchIdx;
+ int16 idx = startIdx;
+ bool wrapped = false;
+ GeorgeEntityState *candidate = nullptr;
+
+ while (true) {
+ GeorgeEntityState *s = g_georgeEntityList[idx];
+ if (!s) {
+ wrapped = true;
+ idx = -1;
+ } else if (s->animMode == 5 && !(s->flags & 1)) {
+ candidate = s;
+ break;
+ }
+
+ idx++;
+ if (idx == startIdx && wrapped)
+ return false;
+ }
+
+ g_georgeAsteroidSearchIdx = idx + 1;
+
+ // Get shuffle table row for this asteroid...
+ int16 row = getAsteroidRow();
+ int16 numCols = g_georgeThresholds[10];
+ int16 rowBase = (numCols + 1) * row;
+ int16 colIdx = g_georgeAsteroidShuffleTable[rowBase];
+ byte animIdx = g_georgeAsteroidShuffleTable[rowBase + colIdx + 1];
+
+ // Assign motion path...
+ byte *rowPaths = getResolvedPtr(g_georgeAsteroidPaths, row * 4);
+ byte *path = getResolvedPtr(rowPaths, animIdx * 4);
+
+ candidate->pathTable = path;
+ colIdx++;
+ g_georgeAsteroidShuffleTable[rowBase] = (byte)colIdx;
+ if (colIdx >= numCols)
+ g_georgeAsteroidShuffleTable[rowBase] = 0;
+
+ // Set initial position from pic's embedded coords...
+ candidate->pathIndex = 0;
+ candidate->x = (int32)(int16)READ_UINT16(path + 0x02) << 8;
+ candidate->y = (int32)(int16)READ_UINT16(path + 0x04) << 8;
+
+ candidate->flags |= 1;
+
+ return true;
+}
+
+int16 BoltEngine::getRandomAsteroidWait() {
+ int16 base = g_georgeThresholds[7];
+ int16 range = g_georgeThresholds[8];
+ return _xp->getRandom(range) + base;
+}
+
+int16 BoltEngine::getAsteroidRow() {
+ GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ int16 carY = carSat->y >> 8;
+
+ // Find which row the car is in by scanning frame Y thresholds...
+ int16 numRows = g_georgeThresholds[9];
+ int16 row = 0;
+ int16 *thresholds = g_georgeAsteroidThresholds;
+
+ for (int16 i = 0; i < numRows; i++) {
+ if (thresholds[i] > carY)
+ break;
+
+ row = i + 1;
+ }
+
+ return row;
+}
+
+void BoltEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
+ entity->animMode = mode;
+ entity->variant = 0;
+ entity->animTable = g_georgeCarPics[mode];
+ entity->frameIndex = 0;
+ entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
+}
+
+void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
+ if (mode == 4) {
+ // Deactivate...
+ entity->animMode = mode;
+ entity->flags &= ~1;
+ return;
+ }
+
+ entity->animMode = mode;
+ entity->variant = variant;
+ entity->animTable = getResolvedPtr(g_georgeSatelliteGfx, variant * 4);
+ entity->frameIndex = 0;
+ entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
+}
+
+void BoltEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
+ entity->animMode = mode;
+ entity->variant = variant;
+ entity->animTable = getResolvedPtr(g_georgeAsteroidGfx, variant * 4);
+ entity->frameIndex = 0;
+ entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
+}
+
+void BoltEngine::drawFlyingObjects() {
+ _xp->fillDisplay(0, stFront);
+
+ for (int16 i = 0;; i++) {
+ GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (!sat)
+ break;
+
+ if (!(sat->flags & 1))
+ continue;
+
+ int16 x = sat->x >> 8;
+ int16 y = sat->y >> 8;
+
+ byte *pic = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
+
+ displayPic(pic, x, y, stFront);
+ }
+}
+
+void BoltEngine::getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority) {
+ outInfo->data = memberAddr(boltLib, member);
+ outInfo->size = memberSize(boltLib, member);
+ outInfo->priority = priority;
+ outInfo->channel = g_georgeSoundChannelCounter++;
+}
+
+void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
+ // If a queued sound exists and has higher priority than new sound, don't replace it...
+ if (newSound && g_georgeSoundQueued) {
+ if (g_georgeSoundQueued->priority > newSound->priority)
+ return;
+ }
+
+ // Queue the new sounds...
+ g_georgeSoundQueued = newSound;
+ g_georgeSoundNext = nextSound;
+
+ // If currently playing sound has higher priority than new one, keep it...
+ if (g_georgeSoundToPlay && newSound) {
+ if (g_georgeSoundToPlay->priority > g_georgeSoundQueued->priority) {
+ // If something is queued but nothing playing, kick off playback now...
+ if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+ updateGeorgeSound();
+
+ return;
+ }
+ }
+
+ // New sound wins...
+ _xp->stopSound();
+ g_georgeSoundCurrent = nullptr;
+ g_georgeSoundToPlay = nullptr;
+
+ // If something is queued but nothing playing, kick off playback now...
+ if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+ updateGeorgeSound();
+}
+
+void BoltEngine::updateGeorgeSound() {
+ int16 playBoth = 0;
+
+ if (g_georgeSoundCurrent != nullptr) {
+ // Current sound still set, promote directly to play slot...
+ g_georgeSoundToPlay = g_georgeSoundCurrent;
+ } else if (g_georgeSoundQueued != nullptr) {
+ // Nothing current, promote queued sound...
+ g_georgeSoundToPlay = g_georgeSoundQueued;
+ g_georgeSoundCurrent = g_georgeSoundNext;
+
+ if (g_georgeSoundNext != nullptr)
+ playBoth = 1;
+
+ g_georgeSoundNext = nullptr;
+ g_georgeSoundQueued = nullptr;
+ } else {
+ g_georgeSoundToPlay = nullptr;
+ }
+
+ if (g_georgeSoundToPlay == nullptr)
+ return;
+
+ // Play primary sound...
+ _xp->playSound(g_georgeSoundToPlay->data, g_georgeSoundToPlay->size, 22050);
+
+ if (!playBoth)
+ return;
+
+ // Queue secondary sound twice...
+ _xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
+ _xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
+}
} // End of namespace Bolt
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 011bcbb5d1a..0908001c6c6 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -835,7 +835,7 @@ void BoltEngine::updateScoobyLocation() {
// Scroll Scooby off-screen
int16 halfW = READ_UINT16(g_scoobyBaseData + 0x0A) / 2;
- while (true) {
+ while (!shouldQuit()) {
if (g_displayX - halfW >= g_scoobyGameState.scoobyX)
break;
@@ -1654,7 +1654,7 @@ int16 BoltEngine::helpScooby() {
hiliteScoobyHelpObject(helpButton, 1);
// Event loop
- while (true) {
+ while (!shouldQuit()) {
// Handle ongoing audio playback
if (isPlaying != 0) {
if (!maintainAudioPlay(soundParam)) {
@@ -1886,7 +1886,7 @@ int16 BoltEngine::playScooby() {
_xp->enableController();
// Main game loop
- while (true) {
+ while (!shouldQuit()) {
updateScoobyLocation();
// Level complete?
@@ -1926,6 +1926,8 @@ int16 BoltEngine::playScooby() {
updateScoobyWalls();
_xp->updateDisplay();
}
+
+ return 0;
}
int16 BoltEngine::scoobyGame(int16 prevBooth) {
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index facf68567ec..336493a9a6a 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -647,6 +647,9 @@ BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
+BOLTCallback BoltEngine::g_georgeTypeLoadCallbacks[28];
+BOLTCallback BoltEngine::g_georgeTypeFreeCallbacks[28];
+
BOLTCallback BoltEngine::g_scoobyTypeLoadCallbacks[28];
BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
@@ -674,6 +677,10 @@ void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyW
void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
+void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
+void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
+void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
+
void BoltEngine::initCallbacks() {
// --- BOOTHS ---
for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
@@ -745,6 +752,35 @@ void BoltEngine::initCallbacks() {
g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+ // --- GEORGE ---
+ for (int i = 0; i < ARRAYSIZE(g_georgeTypeLoadCallbacks); i++) {
+ g_georgeTypeLoadCallbacks[i] = noOpCb;
+ }
+
+ g_georgeTypeLoadCallbacks[2] = swapAllWordsCb;
+ g_georgeTypeLoadCallbacks[6] = resolveAllRefsCb;
+ g_georgeTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_georgeTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_georgeTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+ g_georgeTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+ g_georgeTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+ g_georgeTypeLoadCallbacks[25] = swapGeorgeFrameArrayCb;
+ g_georgeTypeLoadCallbacks[26] = swapGeorgeHelpEntryCb;
+ g_georgeTypeLoadCallbacks[27] = swapGeorgeThresholdsCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_georgeTypeFreeCallbacks); i++) {
+ g_georgeTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_georgeTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ g_georgeBoltCallbacks.typeLoadCallbacks = g_georgeTypeLoadCallbacks;
+ g_georgeBoltCallbacks.typeFreeCallbacks = g_georgeTypeFreeCallbacks;
+ g_georgeBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_georgeBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_georgeBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_georgeBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
// --- SCOOBY ---
for (int i = 0; i < ARRAYSIZE(g_scoobyTypeLoadCallbacks); i++) {
g_scoobyTypeLoadCallbacks[i] = noOpCb;
Commit: 5a08cf4461ce4680db6f5a60782c43c323245545
https://github.com/scummvm/scummvm/commit/5a08cf4461ce4680db6f5a60782c43c323245545
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement Huck's minigame
Changed paths:
engines/bolt/bolt.h
engines/bolt/booths/huck.cpp
engines/bolt/resource.cpp
engines/bolt/utils.cpp
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 6e4235aa41d..4b8875ca129 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -312,6 +312,7 @@ typedef struct SoundInfo {
}
} SoundInfo;
+
typedef struct FredEntityState {
uint16 flags;
int16 frameCountdown;
@@ -344,6 +345,38 @@ typedef struct FredEntityState {
}
} FredEntityState;
+// HUCK GAME
+
+typedef struct HuckState {
+ int16 levelNumber;
+ int16 slotIndex[10];
+ int16 levelComplete;
+ int16 drawTable1[24];
+ int16 drawTable2[24];
+ int16 giftCount;
+ int16 hasCycle;
+ int16 selectionPending;
+ int16 selected1Slot;
+ int16 selected2Slot;
+ int16 selected1SpriteId;
+ int16 selected2SpriteId;
+
+ HuckState() {
+ levelNumber = 0;
+ memset(slotIndex, 0, sizeof(slotIndex));
+ levelComplete = 0;
+ memset(drawTable1, 0, sizeof(drawTable1));
+ memset(drawTable2, 0, sizeof(drawTable2));
+ giftCount = 0;
+ hasCycle = 0;
+ selectionPending = 0;
+ selected1Slot = 0;
+ selected2Slot = 0;
+ selected1SpriteId = 0;
+ selected2SpriteId = 0;
+ }
+} HuckState;
+
// GEORGE GAME
typedef struct GeorgeEntityState {
@@ -645,10 +678,10 @@ protected:
// Utils
void displayColors(byte *palette, int16 page, int16 flags);
- void sub_11035();
+ byte getPixel(byte *sprite, int16 localX, int16 localY);
void boltPict2Pict(XPPicDesc *dest, byte *boltSprite);
void displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page);
- void sub_11131();
+ bool pointInRect(Common::Rect *rect, int16 x, int16 y);
const char *assetPath(const char *fileName);
void boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc);
void unpackColors(int16 count, byte *packedColors);
@@ -725,7 +758,7 @@ protected:
BOLTMemberEntry *g_boltCurrentMemberEntry = nullptr;
int16 g_pendingFixupCount = 0;
- // Game state
+ // Game levelNumber
bool initVRam(int16 poolSize);
void freeVRam();
bool vLoad(void *dest, const char *name);
@@ -1032,7 +1065,70 @@ protected:
int16 *g_georgeThresholds = nullptr;
// --- HUCK ---
- int16 huckGame(int16 prevBooth) { return 0; }
+ void playSoundMapHuck(int16 memberId);
+ void waitSoundMapHuck();
+ void setHuckColors(int16 which);
+ void restoreHuckColors(int16 which);
+ void startHuckShuffleTimer();
+ bool intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out);
+ void drawGift(int16 slot);
+ void drawHuckGifts();
+ void checkHuckLevelComplete();
+ bool initHuckDisplay();
+ bool loadHuckResources();
+ void unloadHuckResources();
+ bool initHuckLevel();
+ bool resumeHuckLevel();
+ bool initHuck();
+ void huckToggleBlinking(int16 *state, int16 which);
+ void huckUpdateHotSpots(int16 x, int16 y);
+ int16 findGift(int16 x, int16 y);
+ bool handleGiftSelect(int16 x, int16 y);
+ void huckHandleActionButton(int16 x, int16 y);
+ void giftSwap();
+ void resolveHuckSelection();
+ void handleEvent(int16 eventType, uint32 eventData);
+ void playHuck();
+ void cleanUpHuck();
+ int16 huckGame(int16 prevBooth);
+ void swapHuckWordArray();
+ void swapHuckWords();
+
+ static void swapHuckWordArrayCb();
+ static void swapHuckWordsCb();
+
+ BOLTLib *g_huckBoltLib = nullptr;
+ BOLTCallbacks g_huckBoltCallbacks;
+
+ static BOLTCallback g_huckTypeLoadCallbacks[27];
+ static BOLTCallback g_huckTypeFreeCallbacks[27];
+
+ int16 g_huckSoundPlaying = 0;
+ int16 g_huckHotSpotCount = 0;
+ int16 g_huckActionState = 0;
+ uint32 g_huckScreensaverTimer = 0;
+ uint32 g_huckBlinkTimer = 0;
+ int16 g_huckScreensaverFlag = 0;
+ int16 g_huckBlinkFlag = 0;
+ uint32 g_huckShuffleTimer = 0;
+ int16 g_huckGlobal[30] = { 0 };
+ HuckState g_huckState;
+ byte *g_huckGiftPic = nullptr;
+ byte *g_huckBgPic = nullptr;
+ int16 g_huckGiftGroupId = 0;
+ int16 g_huckVariantGroupId = 0;
+ byte *g_huckBgDisplayPic = nullptr;
+ int16 g_huckScrollOffset = 0;
+ int16 g_huckPalRange[8] = { 0 };
+ int16 g_returnBooth = 0;
+ int16 g_huckExitFlag = 0;
+ int16 g_huckCursorX = 0;
+ int16 g_huckCursorY = 0;
+ byte g_huckPalSave0[15] = { 0 };
+ byte g_huckPalHighlight0[15] = { 0 };
+ byte g_huckPalSave1[15] = { 0 };
+ byte g_huckPalHighlight1[15] = { 0 };
+ XPPicDesc g_huckScratchPic;
// --- SCOOBY ---
bool loadScoobyBaseAssets();
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index d0e5b38e0bf..c137e694aaf 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -21,8 +21,915 @@
#include "bolt/bolt.h"
+#include "common/memstream.h"
+
namespace Bolt {
+void BoltEngine::playSoundMapHuck(int16 memberId) {
+ byte *soundData = getBOLTMember(g_huckBoltLib, memberId);
+ uint32 soundSize = memberSize(g_huckBoltLib, memberId);
+ if (soundData) {
+ _xp->playSound(soundData, soundSize, 22050);
+ g_huckSoundPlaying++;
+ }
+}
+
+void BoltEngine::waitSoundMapHuck() {
+ if (!g_huckSoundPlaying)
+ return;
+
+ uint32 dummy;
+ while (_xp->getEvent(etSound, &dummy) == etEmpty);
+ g_huckSoundPlaying--;
+}
+
+void BoltEngine::setHuckColors(int16 which) {
+ if (which == 0) {
+ int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
+ _xp->setPalette(count, g_huckPalRange[0], g_huckPalHighlight0);
+ } else if (which == 1) {
+ int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
+ _xp->setPalette(count, g_huckPalRange[4], g_huckPalHighlight1);
+ }
+}
+
+void BoltEngine::restoreHuckColors(int16 which) {
+ if (which == 0) {
+ int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
+ _xp->setPalette(count, g_huckPalRange[0], g_huckPalSave0);
+ } else if (which == 1) {
+ int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
+ _xp->setPalette(count, g_huckPalRange[4], g_huckPalSave1);
+ }
+}
+
+void BoltEngine::startHuckShuffleTimer() {
+ if (g_huckShuffleTimer) {
+ _xp->killTimer(g_huckShuffleTimer);
+ g_huckShuffleTimer = 0;
+ }
+
+ if (g_huckState.giftCount > 2) {
+ int16 speed = READ_UINT16(g_huckGiftPic + g_huckScrollOffset * 2 + 0x6E);
+ int32 ms = (int32)speed * 1000 / 60;
+ g_huckShuffleTimer = _xp->startTimer((int16)ms);
+ }
+}
+
+bool BoltEngine::intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out) {
+ *out = a->findIntersectingRect(*b);
+ return !out->isEmpty();
+}
+
+void BoltEngine::drawGift(int16 slot) {
+ byte *giftSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+ int16 sprStride = READ_UINT16(giftSprite + 0x0A);
+ int16 sprHeight = READ_UINT16(giftSprite + 0x0C);
+
+ memset(g_huckScratchPic.pixelData, 0, (int32)sprStride * sprHeight);
+
+ int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+ int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+ Common::Rect targetRect(slotX, slotY, slotX + sprHeight, slotY + sprStride);
+
+ int16 giftCount = READ_UINT16(g_huckGiftPic);
+ for (int16 i = 0; i < giftCount; i++) {
+ if (g_huckState.drawTable2[i] != 0)
+ continue;
+
+ byte *otherSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+ int16 otherStride = READ_UINT16(otherSprite + 0x0A);
+ int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
+ int16 ox = READ_UINT16(g_huckGiftPic + i * 2 + 0x3E);
+ int16 oy = READ_UINT16(g_huckGiftPic + i * 2 + 0x0E);
+ Common::Rect otherRect(ox, oy, ox + otherHeight, oy + otherStride);
+
+ Common::Rect isect;
+ if (!intersectRect(&targetRect, &otherRect, &isect))
+ continue;
+
+ byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
+ byte *src = srcPixels + (isect.left - ox) * otherStride + (isect.top - oy);
+
+ byte *dst = g_huckScratchPic.pixelData + (isect.left - slotX) * sprStride + (isect.top - slotY);
+
+ int16 blitWidth = isect.bottom - isect.top;
+ int16 blitHeight = isect.right - isect.left;
+ _xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
+ }
+
+ if ((g_huckState.hasCycle && g_huckState.selected1Slot == slot) || (g_huckState.selectionPending && g_huckState.selected2Slot == slot)) {
+ byte *hlSprite = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x06));
+ int16 hlStride = READ_UINT16(hlSprite + 0x0A);
+ int16 hlHeight = READ_UINT16(hlSprite + 0x0C);
+ byte *hlPixels = getResolvedPtr(hlSprite, 0x12);
+ int16 hlOffsetX = READ_UINT16(g_huckGiftPic + 0x0A);
+ int16 hlOffsetY = READ_UINT16(g_huckGiftPic + 0x08);
+
+ byte *dst = g_huckScratchPic.pixelData + hlOffsetX * sprStride + hlOffsetY;
+
+ _xp->maskBlit(hlPixels, hlStride, dst, sprStride, hlStride, hlHeight);
+ }
+
+ g_huckScratchPic.width = sprStride;
+ g_huckScratchPic.height = sprHeight;
+ g_huckScratchPic.palette = nullptr;
+ g_huckScratchPic.flags = 0;
+
+ _xp->displayPic(&g_huckScratchPic, slotY, slotX, stFront);
+}
+
+void BoltEngine::drawHuckGifts() {
+ _xp->fillDisplay(0, stFront);
+
+ int16 giftCount = READ_UINT16(g_huckGiftPic);
+
+ for (int16 slot = 0; slot < giftCount; slot++) {
+ if (g_huckState.drawTable2[slot] != 0)
+ continue;
+
+ byte *gifPtr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+ int16 x = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+ int16 y = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+ displayPic(gifPtr, x, y, stFront);
+ }
+
+ if (g_huckState.hasCycle) {
+ int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
+ int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
+ int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
+ byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+ displayPic(hlPtr, x, y, stFront);
+ }
+
+ if (g_huckState.selectionPending) {
+ int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
+ int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
+ int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
+ byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+ displayPic(hlPtr, x, y, stFront);
+ }
+}
+
+void BoltEngine::checkHuckLevelComplete() {
+ if (g_huckState.giftCount == 2) {
+ // Last pair matched, level complete!
+ g_huckExitFlag = 1;
+ g_huckState.levelComplete = 1;
+ g_huckState.levelNumber++;
+
+ if (g_huckState.levelNumber > 10)
+ g_huckState.levelNumber = 10;
+
+ _xp->fillDisplay(0, stFront);
+ } else {
+ // More pairs remain, redraw the matched slots as empty...
+ drawGift(g_huckState.selected1Slot);
+ drawGift(g_huckState.selected2Slot);
+ }
+
+ g_huckState.selected1Slot = -1;
+ g_huckState.selected2Slot = -1;
+ g_huckState.giftCount -= 2;
+
+ _xp->updateDisplay();
+
+ waitSoundMapHuck();
+ g_huckScrollOffset++;
+ startHuckShuffleTimer();
+}
+
+bool BoltEngine::initHuckDisplay() {
+ byte *palPtr = memberAddr(g_huckBoltLib, READ_UINT16(g_huckBgPic));
+
+ g_huckHotSpotCount = 0;
+ g_huckScreensaverTimer = 0;
+ g_huckBlinkTimer = 0;
+ g_huckExitFlag = 0;
+
+ int32 maxArea = 0;
+ int16 giftCount = READ_UINT16(g_huckGiftPic);
+ for (int16 i = 0; i < giftCount; i++) {
+ byte *spr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+ int32 area = (int32)READ_UINT16(spr + 0x0A) * READ_UINT16(spr + 0x0C);
+ if (area > maxArea)
+ maxArea = area;
+ }
+
+ g_huckScratchPic.pixelData = (byte *)_xp->allocMem(maxArea);
+ if (!g_huckScratchPic.pixelData)
+ return false;
+
+ uint32 dummy;
+ while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+ displayColors(palPtr, stBack, 1);
+ displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+
+ _xp->setTransparency(true);
+ displayColors(palPtr, stFront, 0);
+ displayColors(palPtr, stBack, 1);
+ displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stBack);
+ _xp->fillDisplay(0, stFront);
+ _xp->updateDisplay();
+
+ drawHuckGifts();
+ _xp->updateDisplay();
+
+ if (g_huckState.hasCycle) {
+ XPCycleState cycleSpec[4];
+ byte *cycleData = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x0C));
+ boltCycleToXPCycle(cycleData, cycleSpec);
+ _xp->startCycle(cycleSpec);
+ }
+
+ g_huckCursorY = 0x78;
+ g_huckCursorX = 0xC0;
+ _xp->setCursorPos(0x78, 0xC0);
+ _xp->setCursorColor(0xFF, 0xFF, 0xFF);
+ _xp->showCursor();
+
+ // Read palette ranges from gift pic data...
+ g_huckPalRange[0] = READ_UINT16(g_huckGiftPic + 0x8C) + 0x80;
+ g_huckPalRange[1] = READ_UINT16(g_huckGiftPic + 0x8E) + 0x80;
+ g_huckPalRange[2] = READ_UINT16(g_huckGiftPic + 0x90) + 0x80;
+ g_huckPalRange[3] = READ_UINT16(g_huckGiftPic + 0x92) + 0x80;
+ g_huckPalRange[4] = READ_UINT16(g_huckGiftPic + 0x9E) + 0x80;
+ g_huckPalRange[5] = READ_UINT16(g_huckGiftPic + 0xA0) + 0x80;
+ g_huckPalRange[6] = READ_UINT16(g_huckGiftPic + 0xA2) + 0x80;
+ g_huckPalRange[7] = READ_UINT16(g_huckGiftPic + 0xA4) + 0x80;
+
+ // Save original palette ranges, load highlight palette ranges...
+ _xp->getPalette(g_huckPalRange[0], g_huckPalRange[1] - g_huckPalRange[0] + 1, g_huckPalSave0);
+ _xp->getPalette(g_huckPalRange[2], g_huckPalRange[3] - g_huckPalRange[2] + 1, g_huckPalHighlight0);
+ _xp->getPalette(g_huckPalRange[4], g_huckPalRange[5] - g_huckPalRange[4] + 1, g_huckPalSave1);
+ _xp->getPalette(g_huckPalRange[6], g_huckPalRange[7] - g_huckPalRange[6] + 1, g_huckPalHighlight1);
+
+ return true;
+}
+
+bool BoltEngine::loadHuckResources() {
+ int16 stateIdx = g_huckState.levelNumber - 1;
+ int16 giftGroupId = (stateIdx << 10) + 0x100;
+ g_huckGiftGroupId = giftGroupId;
+
+ if (!getBOLTGroup(g_huckBoltLib, giftGroupId, 1))
+ return false;
+
+ g_huckGiftPic = memberAddr(g_huckBoltLib, giftGroupId);
+
+ for (int16 i = 0; i < 11; i++) {
+ int16 *speed = (int16 *)(g_huckGiftPic + 0x6E + i * 2);
+ if (*speed < 30)
+ *speed = 30;
+ }
+
+ // Load background display pic...
+ int16 bgMember = (g_displayMode != 0) ? READ_UINT16(g_huckGiftPic + 4) : READ_UINT16(g_huckGiftPic + 2);
+ g_huckBgDisplayPic = memberAddr(g_huckBoltLib, bgMember);
+
+ // Determine variant...
+ int16 slot = g_huckState.slotIndex[stateIdx];
+ int16 variant = g_huckGlobal[stateIdx * 3 + slot];
+ int16 variantGroupId = ((stateIdx * 4 + variant + 1) << 8) + 0x100;
+ g_huckVariantGroupId = variantGroupId;
+
+ if (!getBOLTGroup(g_huckBoltLib, variantGroupId, 1))
+ return false;
+
+ g_huckBgPic = memberAddr(g_huckBoltLib, variantGroupId);
+
+ return true;
+}
+
+void BoltEngine::unloadHuckResources() {
+ _xp->stopCycle();
+ _xp->hideCursor();
+ freeBOLTGroup(g_huckBoltLib, g_huckVariantGroupId, 1);
+ freeBOLTGroup(g_huckBoltLib, g_huckGiftGroupId, 1);
+}
+
+bool BoltEngine::initHuckLevel() {
+ // Advance slot variant (wraps 0..2)
+ int16 stateIdx = g_huckState.levelNumber - 1;
+ g_huckState.slotIndex[stateIdx]++;
+ if (g_huckState.slotIndex[stateIdx] >= 3)
+ g_huckState.slotIndex[stateIdx] = 0;
+
+ if (!loadHuckResources())
+ return false;
+
+ g_huckState.giftCount = READ_UINT16(g_huckGiftPic);
+ g_huckState.levelComplete = 0;
+ g_huckState.hasCycle = 0;
+ g_huckState.selectionPending = 0;
+ g_huckState.selected1Slot = -1;
+ g_huckState.selected2Slot = -1;
+
+ for (int16 i = 0; i < 24; i++) {
+ g_huckState.drawTable1[i] = 0;
+ g_huckState.drawTable2[i] = 0;
+ }
+
+ int16 baseId = READ_UINT16(g_huckBgPic + 2);
+
+ // Distribute gift pairs into random empty slots...
+ int16 count = 0;
+ int16 giftCount = READ_UINT16(g_huckGiftPic);
+ while (count < giftCount) {
+ int16 slot = _xp->getRandom(giftCount);
+ while (g_huckState.drawTable1[slot] > 0) {
+ slot++;
+ if (slot >= giftCount)
+ slot = 0;
+ }
+
+ // Pairs share the same member ID
+ g_huckState.drawTable1[slot] = count / 2 + baseId;
+ count++;
+ }
+
+ if (!initHuckDisplay())
+ return false;
+
+ g_huckScrollOffset = 0;
+ startHuckShuffleTimer();
+ return true;
+}
+
+bool BoltEngine::resumeHuckLevel() {
+ if (!loadHuckResources())
+ return false;
+
+ if (!initHuckDisplay())
+ return false;
+
+ if (g_huckState.giftCount > 2) {
+ g_huckScrollOffset = (READ_UINT16(g_huckGiftPic) - g_huckState.giftCount) >> 1;
+ startHuckShuffleTimer();
+ }
+
+ return true;
+}
+
+bool BoltEngine::initHuck() {
+ _xp->randomize();
+ g_huckSoundPlaying = 0;
+
+ if (!vLoad(&g_huckGlobal, "HuckGlobal")) {
+ // First run, generate random variant permutations for all 10 levels...
+ for (int16 level = 0; level < 10; level++) {
+ g_huckState.slotIndex[level] = -1;
+
+ // Fill row with sentinel value 3...
+ for (int16 j = 0; j < 3; j++)
+ g_huckGlobal[level * 3 + j] = 3;
+
+ // Place variants 0, 1, 2 into random empty slots...
+ for (int16 variant = 0; variant < 3; variant++) {
+ int16 slot = _xp->getRandom(3);
+ while (g_huckGlobal[level * 3 + slot] != 3) {
+ slot++;
+ if (slot >= 3)
+ slot = 0;
+ }
+ g_huckGlobal[level * 3 + slot] = variant;
+ }
+ }
+ }
+
+ byte huckStateBuf[0x86] = { 0 };
+
+ if (!vLoad(&huckStateBuf, "Huck")) {
+ g_huckState.levelNumber = 1;
+ return initHuckLevel();
+ }
+
+ Common::SeekableReadStream *huckStateReadStream = new Common::MemoryReadStream(huckStateBuf, sizeof(huckStateBuf), DisposeAfterUse::NO);
+
+ g_huckState.levelNumber = huckStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 10; i++)
+ g_huckState.slotIndex[i] = huckStateReadStream->readSint16BE();
+
+ g_huckState.levelComplete = huckStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 24; i++)
+ g_huckState.drawTable1[i] = huckStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 24; i++)
+ g_huckState.drawTable2[i] = huckStateReadStream->readSint16BE();
+
+ g_huckState.giftCount = huckStateReadStream->readSint16BE();
+ g_huckState.hasCycle = huckStateReadStream->readSint16BE();
+ g_huckState.selectionPending = huckStateReadStream->readSint16BE();
+ g_huckState.selected1Slot = huckStateReadStream->readSint16BE();
+ g_huckState.selected2Slot = huckStateReadStream->readSint16BE();
+ g_huckState.selected1SpriteId = huckStateReadStream->readSint16BE();
+ g_huckState.selected2SpriteId = huckStateReadStream->readSint16BE();
+
+ // Sanity check: should be exactly 0x86
+ assert(huckStateReadStream->pos() == 0x86);
+ delete huckStateReadStream;
+
+ if (g_huckState.levelComplete)
+ return initHuckLevel();
+ else
+ return resumeHuckLevel();
+}
+
+void BoltEngine::huckToggleBlinking(int16 *state, int16 which) {
+ *state = (*state == 0) ? 1 : 0;
+ if (*state)
+ setHuckColors(which);
+ else
+ restoreHuckColors(which);
+}
+
+void BoltEngine::huckUpdateHotSpots(int16 x, int16 y) {
+ Common::Rect helpRect(
+ READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
+ READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+
+ Common::Rect exitRect(
+ READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
+ READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+
+ if (helpRect.contains(x, y)) {
+ if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+ setHuckColors(0);
+ } else {
+ if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+ restoreHuckColors(0);
+ }
+
+ if (exitRect.contains(x, y)) {
+ if (!g_huckBlinkTimer)
+ setHuckColors(1);
+
+ return;
+ } else {
+ if (!g_huckBlinkTimer)
+ restoreHuckColors(1);
+ }
+}
+
+int16 BoltEngine::findGift(int16 x, int16 y) {
+ byte *sprite0 = memberAddr(g_huckBoltLib, g_huckState.drawTable1[0]);
+ int16 sprH = READ_UINT16(sprite0 + 0x0A);
+ int16 sprW = READ_UINT16(sprite0 + 0x0C);
+
+ int16 giftCount = READ_UINT16(g_huckGiftPic);
+
+ // Iterate in reverse, topmost gift wins...
+ for (int16 slot = giftCount - 1; slot >= 0; slot--) {
+ if (g_huckState.drawTable2[slot] != 0)
+ continue;
+
+ int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+ int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+ int16 slotY2 = slotY + sprH - 1;
+ int16 slotX2 = slotX + sprW - 1;
+
+ byte *sprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+
+ if (y < slotY || y > slotY2)
+ continue;
+ if (x < slotX || x > slotX2)
+ continue;
+
+ // Non-transparent pixel hit test...
+ if (getPixel(sprite, x - slotX, y - slotY) == 0)
+ continue;
+
+ return slot;
+ }
+ return -1;
+}
+
+bool BoltEngine::handleGiftSelect(int16 x, int16 y) {
+ int16 slot = findGift(x, y);
+ if (slot == -1)
+ return false;
+
+ if (!g_huckState.hasCycle) {
+ // No selection yet, select first gift...
+ g_huckState.hasCycle = 1;
+ g_huckState.selected1SpriteId = g_huckState.drawTable1[slot];
+ g_huckState.selected1Slot = slot;
+ playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xA8));
+ drawGift(slot);
+ _xp->updateDisplay();
+
+ // Start palette cycle for highlight...
+ XPCycleState cycleSpec[4];
+ byte *cycleData = memberAddr(g_huckBoltLib, (int16)READ_UINT16(g_huckGiftPic + 0x0C));
+ boltCycleToXPCycle(cycleData, cycleSpec);
+ _xp->startCycle(cycleSpec);
+
+ } else if (!g_huckState.selectionPending) {
+ if (g_huckState.selected1Slot == slot) {
+ // Same gift clicked again, deselect...
+ g_huckState.hasCycle = 0;
+ g_huckState.selected1Slot = -1;
+ playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAA));
+ drawGift(slot);
+ _xp->updateDisplay();
+ _xp->stopCycle();
+ } else {
+ // Different gift, select second...
+ g_huckState.selectionPending = 1;
+ g_huckState.selected2SpriteId = g_huckState.drawTable1[slot];
+ g_huckState.selected2Slot = slot;
+ playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAC));
+ drawGift(slot);
+ _xp->updateDisplay();
+ }
+ }
+
+ // else: already have two selections pending, ignore click...
+
+ waitSoundMapHuck();
+ startHuckShuffleTimer();
+ return true;
+}
+
+void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
+ bool var_6 = false;
+ bool var_4 = false;
+
+ if (g_huckHotSpotCount != 0) {
+ g_huckHotSpotCount = 0;
+ var_6 = true;
+ stopAnimation();
+
+ if (g_huckBlinkTimer) {
+ _xp->killTimer(g_huckBlinkTimer);
+ g_huckBlinkTimer = 0;
+ }
+
+ huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ }
+
+ Common::Rect helpRect(
+ READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
+ READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+
+ if (helpRect.contains(x, y)) {
+ var_4 = true;
+ if (g_huckScreensaverTimer) {
+ _xp->killTimer(g_huckScreensaverTimer);
+ g_huckScreensaverTimer = 0;
+ setHuckColors(0);
+ }
+
+ if (!g_huckHotSpotCount && !var_6) {
+ g_huckHotSpotCount = startAnimation(g_rtfHandle, 0x1A);
+ g_huckActionState = 0;
+ }
+ } else {
+ Common::Rect exitRect(
+ READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
+ READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+
+ if (exitRect.contains(x, y)) {
+ setHuckColors(1);
+ restoreHuckColors(0);
+ g_returnBooth = 3;
+ g_huckExitFlag = 1;
+ }
+ }
+
+ if (handleGiftSelect(x, y))
+ var_4 = true;
+
+ if (var_4) {
+ if (g_huckScreensaverTimer) {
+ _xp->killTimer(g_huckScreensaverTimer);
+ g_huckScreensaverTimer = 0;
+ restoreHuckColors(0);
+ }
+
+ _xp->setInactivityTimer(30);
+ }
+}
+
+void BoltEngine::giftSwap() {
+ if (g_huckState.giftCount <= 2)
+ return;
+
+ // Determine pool size excluding selected gifts...
+ int16 pool;
+ if (g_huckState.selectionPending)
+ pool = g_huckState.giftCount - 2;
+ else if (g_huckState.hasCycle)
+ pool = g_huckState.giftCount - 1;
+ else
+ pool = g_huckState.giftCount;
+
+ // Pick two distinct random indices into the eligible pool...
+ int16 randA = _xp->getRandom(pool);
+ int16 randB = _xp->getRandom(pool);
+ if (randA == randB) {
+ randB++;
+ if (randB >= pool)
+ randB = 0;
+ }
+
+ // Map random indices to actual slots, skipping matched/selected...
+ int16 slotA = -1;
+ int16 slotB = 0;
+ int16 counter = -1;
+ for (int16 i = 0, j = 0; j <= READ_UINT16(g_huckGiftPic); i++, j++) {
+ if (g_huckState.drawTable2[j] != 0)
+ continue;
+
+ if (j == g_huckState.selected1Slot)
+ continue;
+
+ if (j == g_huckState.selected2Slot)
+ continue;
+
+ counter++;
+
+ if (counter == randA)
+ slotA = j;
+
+ if (counter == randB)
+ slotB = j;
+ }
+
+ // If the two chosen slots have the same sprite, find a different slotB...
+ if (g_huckState.drawTable1[slotA] == g_huckState.drawTable1[slotB]) {
+ // Walk forward from slotB, skipping invalid candidates...
+ while (true) {
+ slotB++;
+ if (slotB >= READ_UINT16(g_huckGiftPic))
+ slotB = 0;
+
+ if (g_huckState.drawTable2[slotB] != 0)
+ continue;
+
+ if (slotB == g_huckState.selected2Slot)
+ continue;
+
+ if (slotB == g_huckState.selected1Slot)
+ continue;
+
+ if (slotB == slotA)
+ continue;
+
+ break;
+ }
+ }
+
+ // Swap entries in draw table...
+ int16 tmp = g_huckState.drawTable1[slotA];
+ g_huckState.drawTable1[slotA] = g_huckState.drawTable1[slotB];
+ g_huckState.drawTable1[slotB] = tmp;
+
+ drawGift(slotA);
+ drawGift(slotB);
+}
+
+void BoltEngine::resolveHuckSelection() {
+ if (!g_huckState.selectionPending)
+ return;
+
+ _xp->stopCycle();
+
+ if (g_huckState.selected1SpriteId == g_huckState.selected2SpriteId) {
+ // Match, play match sound, mark both slots as matched...
+ playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAE));
+ g_huckState.drawTable2[g_huckState.selected1Slot] = 1;
+ g_huckState.drawTable2[g_huckState.selected2Slot] = 1;
+ g_huckState.hasCycle = 0;
+ g_huckState.selectionPending = 0;
+ checkHuckLevelComplete();
+ } else {
+ // Mismatch, play mismatch sound, redraw both gifts unselected...
+ playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xB0));
+ g_huckState.hasCycle = 0;
+ g_huckState.selectionPending = 0;
+ drawGift(g_huckState.selected1Slot);
+ drawGift(g_huckState.selected2Slot);
+ g_huckState.selected1Slot = -1;
+ g_huckState.selected2Slot = -1;
+ waitSoundMapHuck();
+ }
+}
+
+void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
+ switch (eventType) {
+ case etMouseDown:
+ huckHandleActionButton(g_huckCursorX, g_huckCursorY);
+ resolveHuckSelection();
+
+ // The original does:
+ // if (g_huckExitFlag)
+ // return;
+ //
+ // and then breaks, which is redundant...
+
+ break;
+
+ case etMouseMove:
+ g_huckCursorX = (int16)(eventData >> 16);
+ g_huckCursorY = (int16)(eventData & -1);
+ huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ break;
+
+ case etTimer: {
+ if (eventData == g_huckScreensaverTimer) {
+ huckToggleBlinking(&g_huckScreensaverFlag, 0);
+ int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+ g_huckScreensaverTimer = _xp->startTimer(ms);
+ } else if (eventData == g_huckBlinkTimer) {
+ huckToggleBlinking(&g_huckBlinkFlag, 1);
+ int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+ g_huckBlinkTimer = _xp->startTimer(ms);
+ } else if (eventData == g_huckShuffleTimer) {
+ giftSwap();
+ _xp->updateDisplay();
+ startHuckShuffleTimer();
+ }
+
+ break;
+ }
+
+ case etInactivity: {
+ if (!g_huckScreensaverTimer) {
+ int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+ g_huckScreensaverTimer = _xp->startTimer(ms);
+ g_huckScreensaverFlag = 0;
+ huckToggleBlinking(&g_huckScreensaverFlag, 0);
+ _xp->setInactivityTimer(1800);
+ } else {
+ bool exitLoop = false;
+ _xp->setScreenBrightness(25);
+
+ while (!shouldQuit()) {
+ uint32 innerData = 0;
+ int16 innerType = _xp->getEvent(etEmpty, &innerData);
+ switch (innerType) {
+ case etSound:
+ if (g_huckSoundPlaying > 0)
+ g_huckSoundPlaying--;
+
+ break;
+ default:
+ _xp->setScreenBrightness(100);
+ _xp->setInactivityTimer(1800);
+ exitLoop = true;
+ break;
+ }
+
+ handleEvent(innerType, innerData);
+ if (exitLoop)
+ break;
+ }
+ }
+ }
+
+ case etSound: {
+ if (g_huckHotSpotCount != 0) {
+ g_huckHotSpotCount = maintainAudioPlay(1);
+ if (g_huckHotSpotCount == 0)
+ huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ }
+
+ if (g_huckSoundPlaying > 0)
+ g_huckSoundPlaying--;
+
+ break;
+ }
+
+ case etTrigger: {
+ g_huckActionState++;
+ if (g_huckActionState == 1) {
+ int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+ g_huckBlinkTimer = _xp->startTimer(ms);
+ g_huckBlinkFlag = 0;
+ huckToggleBlinking(&g_huckBlinkFlag, 1);
+ } else if (g_huckActionState == 2) {
+ _xp->killTimer(g_huckBlinkTimer);
+ g_huckBlinkTimer = 0;
+ restoreHuckColors(1);
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void BoltEngine::playHuck() {
+ g_returnBooth = 0x10;
+
+ while (!shouldQuit()) {
+ if (g_huckExitFlag)
+ break;
+
+ uint32 eventData = 0;
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
+ if (eventType)
+ handleEvent(eventType, eventData);
+
+ if (g_huckHotSpotCount != 0) {
+ g_huckHotSpotCount = maintainAudioPlay(0);
+ if (g_huckHotSpotCount == 0)
+ huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ }
+ }
+
+ if (shouldQuit())
+ g_returnBooth = 0;
+}
+
+void BoltEngine::cleanUpHuck() {
+ unloadHuckResources();
+
+ if (g_huckScratchPic.pixelData) {
+ _xp->freeMem(g_huckScratchPic.pixelData);
+ g_huckScratchPic.pixelData = nullptr;
+ g_huckScratchPic.palette = nullptr;
+ }
+
+ vSave(&g_huckGlobal, sizeof(g_huckGlobal), "HuckGlobal");
+
+ byte huckSaveBuf[0x86] = { 0 };
+ Common::MemoryWriteStream *huckStateWriteStream = new Common::MemoryWriteStream(huckSaveBuf, sizeof(huckSaveBuf));
+
+ huckStateWriteStream->writeSint16BE(g_huckState.levelNumber);
+
+ for (int i = 0; i < 10; i++)
+ huckStateWriteStream->writeSint16BE(g_huckState.slotIndex[i]);
+
+ huckStateWriteStream->writeSint16BE(g_huckState.levelComplete);
+
+ for (int i = 0; i < 24; i++)
+ huckStateWriteStream->writeSint16BE(g_huckState.drawTable1[i]);
+
+ for (int i = 0; i < 24; i++)
+ huckStateWriteStream->writeSint16BE(g_huckState.drawTable2[i]);
+
+ huckStateWriteStream->writeSint16BE(g_huckState.giftCount);
+ huckStateWriteStream->writeSint16BE(g_huckState.hasCycle);
+ huckStateWriteStream->writeSint16BE(g_huckState.selectionPending);
+ huckStateWriteStream->writeSint16BE(g_huckState.selected1Slot);
+ huckStateWriteStream->writeSint16BE(g_huckState.selected2Slot);
+ huckStateWriteStream->writeSint16BE(g_huckState.selected1SpriteId);
+ huckStateWriteStream->writeSint16BE(g_huckState.selected2SpriteId);
+
+ // Sanity check: should be exactly 0x86
+ assert(huckStateWriteStream->pos() == 0x86);
+ delete huckStateWriteStream;
+
+ vSave(&huckSaveBuf, 0x86, "Huck");
+
+ _xp->fillDisplay(0, stFront);
+ _xp->updateDisplay();
+}
+
+int16 BoltEngine::huckGame(int16 prevBooth) {
+ if (!openBOLTLib(&g_huckBoltLib, &g_huckBoltCallbacks, assetPath("huck.blt")))
+ return g_returnBooth;
+
+ int16 savedTimer = _xp->setInactivityTimer(30);
+
+ if (initHuck())
+ playHuck();
+
+ cleanUpHuck();
+
+ _xp->setInactivityTimer(savedTimer);
+ closeBOLTLib(&g_huckBoltLib);
+
+ return g_returnBooth;
+}
+
+void BoltEngine::swapHuckWordArray() {
+ byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+ if (!ptr)
+ return;
+
+ int16 count = (int16)(g_boltCurrentMemberEntry->decompSize / 2);
+ for (int16 i = 0; i < count; i++, ptr += 2)
+ WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+}
+
+void BoltEngine::swapHuckWords() {
+ byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+ if (!ptr)
+ return;
+ WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+ WRITE_UINT16(ptr + 2, READ_BE_UINT16(ptr + 2));
+}
} // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 336493a9a6a..0127978dd46 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -650,6 +650,9 @@ BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
BOLTCallback BoltEngine::g_georgeTypeLoadCallbacks[28];
BOLTCallback BoltEngine::g_georgeTypeFreeCallbacks[28];
+BOLTCallback BoltEngine::g_huckTypeLoadCallbacks[27];
+BOLTCallback BoltEngine::g_huckTypeFreeCallbacks[27];
+
BOLTCallback BoltEngine::g_scoobyTypeLoadCallbacks[28];
BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
@@ -672,15 +675,18 @@ void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimE
void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
+void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
+void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
+void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
+
+void BoltEngine::swapHuckWordArrayCb() { ((BoltEngine *)g_engine)->swapHuckWordArray(); }
+void BoltEngine::swapHuckWordsCb() { ((BoltEngine *)g_engine)->swapHuckWords(); }
+
void BoltEngine::swapScoobyHelpEntryCb() { ((BoltEngine *)g_engine)->swapScoobyHelpEntry(); }
void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyWordArray(); }
void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
-void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
-void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
-void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
-
void BoltEngine::initCallbacks() {
// --- BOOTHS ---
for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
@@ -781,6 +787,34 @@ void BoltEngine::initCallbacks() {
g_georgeBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
g_georgeBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+ // --- HUCK ---
+ for (int i = 0; i < ARRAYSIZE(g_huckTypeLoadCallbacks); i++) {
+ g_huckTypeLoadCallbacks[i] = noOpCb;
+ }
+
+ g_huckTypeLoadCallbacks[2] = swapAllWordsCb;
+ g_huckTypeLoadCallbacks[6] = resolveAllRefsCb;
+ g_huckTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_huckTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_huckTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+ g_huckTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+ g_huckTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+ g_huckTypeLoadCallbacks[25] = swapHuckWordArrayCb;
+ g_huckTypeLoadCallbacks[26] = swapHuckWordsCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_huckTypeFreeCallbacks); i++) {
+ g_huckTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_huckTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ g_huckBoltCallbacks.typeLoadCallbacks = g_huckTypeLoadCallbacks;
+ g_huckBoltCallbacks.typeFreeCallbacks = g_huckTypeFreeCallbacks;
+ g_huckBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_huckBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_huckBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_huckBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
// --- SCOOBY ---
for (int i = 0; i < ARRAYSIZE(g_scoobyTypeLoadCallbacks); i++) {
g_scoobyTypeLoadCallbacks[i] = noOpCb;
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
index 67df914881a..b2a3c82ebcf 100644
--- a/engines/bolt/utils.cpp
+++ b/engines/bolt/utils.cpp
@@ -62,7 +62,10 @@ void BoltEngine::displayColors(byte *palette, int16 page, int16 flags) {
_xp->displayPic(&picDesc, 0, 0, page);
}
-void BoltEngine::sub_11035() {
+byte BoltEngine::getPixel(byte *sprite, int16 localX, int16 localY) {
+ int16 sprH = READ_UINT16(sprite + 0x0A);
+ byte *pixels = getResolvedPtr(sprite, 0x12);
+ return pixels[localX * sprH + localY];
}
void BoltEngine::boltPict2Pict(XPPicDesc *dest, byte *boltSprite) {
@@ -95,7 +98,9 @@ void BoltEngine::displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page
page);
}
-void BoltEngine::sub_11131() {
+bool BoltEngine::pointInRect(Common::Rect *rect, int16 x, int16 y) {
+ // Slightly different from our rect.contains() method...
+ return rect->left <= x && x <= rect->right && rect->top <= y && y <= rect->bottom;
}
const char *BoltEngine::assetPath(const char *fileName) {
Commit: d0c9be04ab319c6de8c5ff4cacb5a6f154a36bc1
https://github.com/scummvm/scummvm/commit/d0c9be04ab319c6de8c5ff4cacb5a6f154a36bc1
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Implement Yogi's minigame
Changed paths:
engines/bolt/bolt.h
engines/bolt/booths/huck.cpp
engines/bolt/booths/yogi.cpp
engines/bolt/resource.cpp
engines/bolt/utils.cpp
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 4b8875ca129..c0cb0149957 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -510,6 +510,42 @@ typedef struct TopCatAnim {
}
} TopCatAnim;
+// YOGI GAME
+
+typedef struct YogiState {
+ int16 levelNumber;
+ int16 currentSlot;
+ int16 levelIndex[10];
+ int16 slotIndex[10];
+ int16 levelComplete;
+ int16 basketSound[24];
+ int16 basketState[24];
+ int16 basketCount;
+ int16 matchCount;
+ int16 selectionPending;
+ int16 selected1Slot;
+ int16 selected2Slot;
+ int16 sound1;
+ int16 sound2;
+
+ YogiState() {
+ levelNumber = 0;
+ currentSlot = 0;
+ memset(levelIndex, 0, sizeof(levelIndex));
+ memset(slotIndex, 0, sizeof(slotIndex));
+ levelComplete = 0;
+ memset(basketSound, 0, sizeof(basketSound));
+ memset(basketState, 0, sizeof(basketState));
+ basketCount = 0;
+ matchCount = 0;
+ selectionPending = 0;
+ selected1Slot = 0;
+ selected2Slot = 0;
+ sound1 = 0;
+ sound2 = 0;
+ }
+} YogiState;
+
class BoltEngine : public Engine {
friend class XpLib;
@@ -685,6 +721,7 @@ protected:
const char *assetPath(const char *fileName);
void boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc);
void unpackColors(int16 count, byte *packedColors);
+ bool intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out);
// Swap
void swapPicHeader();
@@ -1070,7 +1107,6 @@ protected:
void setHuckColors(int16 which);
void restoreHuckColors(int16 which);
void startHuckShuffleTimer();
- bool intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out);
void drawGift(int16 slot);
void drawHuckGifts();
void checkHuckLevelComplete();
@@ -1120,7 +1156,7 @@ protected:
byte *g_huckBgDisplayPic = nullptr;
int16 g_huckScrollOffset = 0;
int16 g_huckPalRange[8] = { 0 };
- int16 g_returnBooth = 0;
+ int16 g_huckReturnBooth = 0;
int16 g_huckExitFlag = 0;
int16 g_huckCursorX = 0;
int16 g_huckCursorY = 0;
@@ -1306,7 +1342,74 @@ protected:
};
// --- YOGI ---
- int16 yogiGame(int16 prevBooth) { return 0; }
+ void playSoundMapYogi(int16 memberId);
+ void waitSoundMapYogi();
+ void stopSoundYogi();
+ void setYogiColors(int16 which);
+ void restoreYogiColors(int16 which);
+ void drawBasket(int16 slot, byte *basketSprite);
+ void drawAllBaskets();
+ void handleYogiMatch();
+ bool loadYogiBgPic();
+ void unloadYogiBgPic();
+ void drawYogiLevel();
+ bool loadYogiLevel();
+ void unloadYogiResources();
+ bool initYogiLevel();
+ bool resumeYogiLevel();
+ bool initYogi();
+ void yogiToggleBlinking(int16 which, int16 *state);
+ void yogiUpdateHotSpots(int16 x, int16 y);
+ int16 findBasket(int16 x, int16 y);
+ void resolveYogiSelection();
+ bool handleBasketSelect(int16 x, int16 y);
+ void yogiHandleActionButton(int16 x, int16 y);
+ void handleYogiEvent(int16 eventType, uint32 eventData);
+ void playYogi();
+ void cleanUpYogi();
+ int16 yogiGame(int16 prevBooth);
+
+ void swapYogiAllWords();
+ void swapYogiFirstWord();
+
+ static void swapYogiAllWordsCb();
+ static void swapYogiFirstWordCb();
+
+ BOLTLib *g_yogiBoltLib = nullptr;
+ BOLTCallbacks g_yogiBoltCallbacks;
+
+ static BOLTCallback g_yogiTypeLoadCallbacks[27];
+ static BOLTCallback g_yogiTypeFreeCallbacks[27];
+
+ int16 g_yogiSoundPlaying = 0;
+ int16 g_yogiSoundActive = 0;
+ int16 g_yogiHotSpotCount = 0;
+ int16 g_yogiAnimActive = 0;
+ uint32 g_yogiBlinkTimer1 = 0;
+ uint32 g_yogiBlinkTimer2 = 0;
+ int16 g_yogiBlinkState1 = 0;
+ int16 g_yogiBlinkState2 = 0;
+ int16 g_yogiGlobal[0xA0] = { 0 };
+ YogiState g_yogiState;
+ byte *g_yogiBasketPic = nullptr;
+ int16 g_yogiLevelGroupId = 0;
+ byte *g_yogiBgPic = nullptr;
+ byte *g_yogiNormalSprite = nullptr;
+ byte *g_yogiHlSprite = nullptr;
+ byte *g_yogiAnimSprite = nullptr;
+ int16 g_yogiSpriteStride = 0;
+ int16 g_yogiSpriteHeight = 0;
+ int16 g_yogiPalRange[8] = { 0 };
+ int16 g_yogiReturnBooth = 0;
+ int16 g_yogiExitFlag = 0;
+ int16 g_yogiLevelId = 0;
+ int16 g_yogiCursorX = 0;
+ int16 g_yogiCursorY = 0;
+ byte g_yogiPalSave0[15] = { 0 };
+ byte g_yogiPalHighlight0[15] = { 0 };
+ byte g_yogiPalSave1[15] = { 0 };
+ byte g_yogiPalHighlight1[15] = { 0 };
+ XPPicDesc g_yogiScratchBuf;
};
extern BoltEngine *g_engine;
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index c137e694aaf..ce66c678125 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -76,11 +76,6 @@ void BoltEngine::startHuckShuffleTimer() {
}
}
-bool BoltEngine::intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out) {
- *out = a->findIntersectingRect(*b);
- return !out->isEmpty();
-}
-
void BoltEngine::drawGift(int16 slot) {
byte *giftSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
int16 sprStride = READ_UINT16(giftSprite + 0x0A);
@@ -598,7 +593,7 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
if (exitRect.contains(x, y)) {
setHuckColors(1);
restoreHuckColors(0);
- g_returnBooth = 3;
+ g_huckReturnBooth = 3;
g_huckExitFlag = 1;
}
}
@@ -829,7 +824,7 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
}
void BoltEngine::playHuck() {
- g_returnBooth = 0x10;
+ g_huckReturnBooth = 0x10;
while (!shouldQuit()) {
if (g_huckExitFlag)
@@ -837,6 +832,7 @@ void BoltEngine::playHuck() {
uint32 eventData = 0;
int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
if (eventType)
handleEvent(eventType, eventData);
@@ -848,7 +844,7 @@ void BoltEngine::playHuck() {
}
if (shouldQuit())
- g_returnBooth = 0;
+ g_huckReturnBooth = 0;
}
void BoltEngine::cleanUpHuck() {
@@ -898,7 +894,7 @@ void BoltEngine::cleanUpHuck() {
int16 BoltEngine::huckGame(int16 prevBooth) {
if (!openBOLTLib(&g_huckBoltLib, &g_huckBoltCallbacks, assetPath("huck.blt")))
- return g_returnBooth;
+ return g_huckReturnBooth;
int16 savedTimer = _xp->setInactivityTimer(30);
@@ -910,7 +906,7 @@ int16 BoltEngine::huckGame(int16 prevBooth) {
_xp->setInactivityTimer(savedTimer);
closeBOLTLib(&g_huckBoltLib);
- return g_returnBooth;
+ return g_huckReturnBooth;
}
void BoltEngine::swapHuckWordArray() {
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
index d0e5b38e0bf..26247be03a0 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/booths/yogi.cpp
@@ -21,8 +21,783 @@
#include "bolt/bolt.h"
+#include "common/memstream.h"
+
namespace Bolt {
+void BoltEngine::playSoundMapYogi(int16 memberId) {
+ byte *soundData = getBOLTMember(g_yogiBoltLib, memberId);
+ int32 soundSize = memberSize(g_yogiBoltLib, memberId);
+ if (soundData) {
+ _xp->playSound(soundData, soundSize, 22050);
+ g_yogiSoundActive = 1;
+ g_yogiSoundPlaying++;
+ }
+}
+
+void BoltEngine::waitSoundMapYogi() {
+ if (!g_yogiSoundPlaying)
+ return;
+
+ uint32 dummy;
+ while (_xp->getEvent(etSound, &dummy) == etEmpty);
+
+ g_yogiSoundActive = 0;
+ g_yogiSoundPlaying--;
+}
+
+void BoltEngine::stopSoundYogi() {
+ if (g_yogiSoundActive) {
+ _xp->stopSound();
+ g_yogiSoundActive = 0;
+ }
+}
+
+void BoltEngine::setYogiColors(int16 which) {
+ if (which == 0)
+ _xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalHighlight0);
+ else if (which == 1)
+ _xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalHighlight1);
+}
+
+void BoltEngine::restoreYogiColors(int16 which) {
+ if (which == 0)
+ _xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalSave0);
+ else if (which == 1)
+ _xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalSave1);
+}
+
+void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
+ int16 sprStride = READ_UINT16(basketSprite + 0x0A);
+ int16 sprHeight = READ_UINT16(basketSprite + 0x0C);
+
+ memset(g_yogiScratchBuf.pixelData, 0, (int32)sprStride * sprHeight);
+
+ int16 slotY = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x42);
+ int16 slotX = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x12);
+ Common::Rect targetRect(slotY, slotX, slotY + sprHeight, slotX + sprStride);
+
+ int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ for (int16 i = 0; i < basketCount; i++) {
+ if (g_yogiState.basketState[i] != 0)
+ continue;
+
+ byte *otherSprite;
+ if (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
+ otherSprite = g_yogiHlSprite;
+ else
+ otherSprite = g_yogiNormalSprite;
+
+ int16 otherStride = READ_UINT16(otherSprite + 0x0A);
+ int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
+ int16 otherY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
+ int16 otherX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+ Common::Rect otherRect(otherY, otherX, otherY + otherHeight, otherX + otherStride);
+
+ Common::Rect isect;
+ if (!intersectRect(&targetRect, &otherRect, &isect))
+ continue;
+
+ byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
+ byte *src = srcPixels + (isect.left - otherY) * otherStride + (isect.top - otherX);
+ byte *dst = g_yogiScratchBuf.pixelData + (isect.left - slotY) * sprStride + (isect.top - slotX);
+
+ int16 blitWidth = isect.bottom - isect.top;
+ int16 blitHeight = isect.right - isect.left;
+ _xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
+ }
+
+ g_yogiScratchBuf.width = sprStride;
+ g_yogiScratchBuf.height = sprHeight;
+ g_yogiScratchBuf.palette = nullptr;
+ g_yogiScratchBuf.flags = 0;
+
+ _xp->displayPic(&g_yogiScratchBuf, slotX, slotY, stFront);
+}
+
+void BoltEngine::drawAllBaskets() {
+ _xp->fillDisplay(0, stFront);
+
+ int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ for (int16 i = 0; i < basketCount; i++) {
+ if (g_yogiState.basketState[i] != 0)
+ continue;
+
+ byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
+ ? g_yogiHlSprite
+ : g_yogiNormalSprite;
+
+ int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
+ int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+ displayPic(sprite, slotX, slotY, stFront);
+ }
+}
+
+void BoltEngine::handleYogiMatch() {
+ playSoundMapYogi((int16)READ_UINT16(g_yogiBasketPic + 0x9E));
+
+ if (g_yogiState.basketCount == 2) {
+ g_yogiExitFlag = 1;
+ g_yogiState.levelComplete = 1;
+ g_yogiState.levelNumber++;
+ if (g_yogiState.levelNumber > 10)
+ g_yogiState.levelNumber = 10;
+ _xp->fillDisplay(0, stFront);
+ } else {
+ drawBasket(g_yogiState.selected1Slot, g_yogiHlSprite);
+ drawBasket(g_yogiState.selected2Slot, g_yogiHlSprite);
+ }
+
+ g_yogiState.selected1Slot = -1;
+ g_yogiState.selected2Slot = -1;
+ g_yogiState.basketCount -= 2;
+ _xp->updateDisplay();
+ waitSoundMapYogi();
+}
+
+bool BoltEngine::loadYogiBgPic() {
+ int16 groupId;
+
+ if (g_displayMode == 0) {
+ groupId = 0x100;
+ } else if (g_displayMode == 1) {
+ groupId = 0x200;
+ } else {
+ return false;
+ }
+
+ if (!getBOLTGroup(g_yogiBoltLib, groupId, 1))
+ return false;
+
+ g_yogiBgPic = memberAddr(g_yogiBoltLib, groupId + 1);
+ return true;
+}
+
+void BoltEngine::unloadYogiBgPic() {
+ freeBOLTGroup(g_yogiBoltLib, 0x100, 1);
+}
+
+void BoltEngine::drawYogiLevel() {
+ byte *palSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x72));
+
+ g_yogiSoundActive = 0;
+ g_yogiHotSpotCount = 0;
+ g_yogiBlinkTimer1 = 0;
+ g_yogiBlinkTimer2 = 0;
+ g_yogiExitFlag = 0;
+
+ uint32 dummy;
+ while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+ _xp->stopCycle();
+ _xp->setTransparency(false);
+ displayColors(palSprite, stBack, 0);
+ displayPic(g_yogiBgPic, g_displayX, g_displayY, stFront);
+ _xp->updateDisplay();
+
+ _xp->setTransparency(true);
+ displayColors(palSprite, stFront, 0);
+ displayColors(palSprite, stBack, 1);
+ displayPic(g_yogiBgPic, g_displayX, g_displayY, stBack);
+
+ drawAllBaskets();
+ _xp->updateDisplay();
+
+ g_yogiCursorX = 0xC0;
+ g_yogiCursorY = 0x78;
+ _xp->setCursorPos(0xC0, 0x78);
+ _xp->setCursorColor(0, 0, 0xFF);
+ _xp->showCursor();
+
+ g_yogiPalRange[0] = READ_UINT16(g_yogiBasketPic + 0x7C) + 0x80;
+ g_yogiPalRange[1] = READ_UINT16(g_yogiBasketPic + 0x7E) + 0x80;
+ g_yogiPalRange[2] = READ_UINT16(g_yogiBasketPic + 0x80) + 0x80;
+ g_yogiPalRange[3] = READ_UINT16(g_yogiBasketPic + 0x82) + 0x80;
+ g_yogiPalRange[4] = READ_UINT16(g_yogiBasketPic + 0x8E) + 0x80;
+ g_yogiPalRange[5] = READ_UINT16(g_yogiBasketPic + 0x90) + 0x80;
+ g_yogiPalRange[6] = READ_UINT16(g_yogiBasketPic + 0x92) + 0x80;
+ g_yogiPalRange[7] = READ_UINT16(g_yogiBasketPic + 0x94) + 0x80;
+
+ _xp->getPalette(g_yogiPalRange[0], g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalSave0);
+ _xp->getPalette(g_yogiPalRange[2], g_yogiPalRange[3] - g_yogiPalRange[2] + 1, g_yogiPalHighlight0);
+ _xp->getPalette(g_yogiPalRange[4], g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalSave1);
+ _xp->getPalette(g_yogiPalRange[6], g_yogiPalRange[7] - g_yogiPalRange[6] + 1, g_yogiPalHighlight1);
+}
+
+bool BoltEngine::loadYogiLevel() {
+ int32 maxSize = 0;
+
+ g_yogiLevelGroupId = (g_yogiState.levelNumber - 1) * 0x100 + 0x300;
+
+ if (!getBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1))
+ return false;
+
+ g_yogiBasketPic = memberAddr(g_yogiBoltLib, g_yogiLevelGroupId);
+ g_yogiNormalSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x02));
+ g_yogiHlSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x04));
+ g_yogiAnimSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x06));
+
+ int32 size;
+
+ size = (int32)READ_UINT16(g_yogiNormalSprite + 0x0A) * READ_UINT16(g_yogiNormalSprite + 0x0C);
+ if (size > maxSize)
+ maxSize = size;
+
+ size = (int32)READ_UINT16(g_yogiHlSprite + 0x0A) * READ_UINT16(g_yogiHlSprite + 0x0C);
+ if (size > maxSize)
+ maxSize = size;
+
+ size = (int32)READ_UINT16(g_yogiAnimSprite + 0x0A) * READ_UINT16(g_yogiAnimSprite + 0x0C);
+ if (size > maxSize)
+ maxSize = size;
+
+ g_yogiScratchBuf.pixelData = (byte *)_xp->allocMem(maxSize);
+ if (!g_yogiScratchBuf.pixelData)
+ return false;
+
+ g_yogiSpriteStride = READ_UINT16(g_yogiNormalSprite + 0x0A);
+ g_yogiSpriteHeight = READ_UINT16(g_yogiNormalSprite + 0x0C);
+ return true;
+}
+
+void BoltEngine::unloadYogiResources() {
+ _xp->hideCursor();
+ int16 basketGroupId = (g_yogiLevelId << 8) + 0xD00;
+ freeBOLTGroup(g_yogiBoltLib, basketGroupId, 1);
+ freeBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1);
+}
+
+bool BoltEngine::initYogiLevel() {
+ g_yogiState.currentSlot++;
+ if (g_yogiState.currentSlot >= 10)
+ g_yogiState.currentSlot = 0;
+
+ g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+
+ if (!loadYogiLevel())
+ return false;
+
+ int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ g_yogiState.basketCount = basketCount;
+ g_yogiState.levelComplete = 0;
+ g_yogiState.matchCount = 0;
+ g_yogiState.selectionPending = 0;
+ g_yogiState.selected1Slot = -1;
+ g_yogiState.selected2Slot = -1;
+
+ for (int16 i = 0; i < 0x18; i++) {
+ g_yogiState.basketSound[i] = 0x10;
+ g_yogiState.basketState[i] = 0;
+ }
+
+ int16 idx = g_yogiState.levelIndex[g_yogiLevelId];
+ int16 snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+ int16 flag = 0;
+
+ for (int16 var_4 = 0; var_4 < basketCount; var_4++) {
+ int16 rnd = _xp->getRandom(basketCount);
+ while (g_yogiState.basketSound[rnd] != 0x10) {
+ rnd++;
+ if (rnd >= basketCount)
+ rnd = 0;
+ }
+ g_yogiState.basketSound[rnd] = snd;
+
+ if (flag == 0) {
+ flag = 1;
+ } else {
+ flag = 0;
+ idx++;
+ if (idx == 0x10)
+ idx = 0;
+ snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+ }
+ }
+
+ g_yogiState.levelIndex[g_yogiLevelId] = idx;
+ drawYogiLevel();
+ return true;
+}
+
+bool BoltEngine::resumeYogiLevel() {
+ g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+
+ if (!loadYogiLevel())
+ return false;
+
+ drawYogiLevel();
+ return true;
+}
+
+bool BoltEngine::initYogi() {
+ g_yogiSoundPlaying = 0;
+
+ if (!loadYogiBgPic())
+ return false;
+
+ if (!vLoad(&g_yogiGlobal, "YogiGlobal")) {
+ int16 slotVal = 0;
+ int16 globIdx = 0;
+ int16 idx = 0;
+
+ while (slotVal < 10) {
+ g_yogiState.slotIndex[idx] = slotVal;
+ g_yogiState.levelIndex[idx] = 0;
+
+ for (int16 i = 0; i < 16; i++)
+ g_yogiGlobal[globIdx + i] = 0x10;
+
+ for (int16 di = 0; di < 16; di++) {
+ int16 rnd = _xp->getRandom(16);
+ while (g_yogiGlobal[globIdx + rnd] != 0x10) {
+ rnd++;
+ if (rnd >= 16)
+ rnd = 0;
+ }
+ g_yogiGlobal[globIdx + rnd] = di;
+ }
+
+ globIdx += 0x10;
+ idx++;
+ slotVal++;
+ }
+ }
+
+ byte yogiStateBuf[0x9C] = { 0 };
+
+ if (!vLoad(&yogiStateBuf, "Yogi")) {
+ g_yogiState.levelNumber = 1;
+ g_yogiState.currentSlot = -1;
+ return initYogiLevel();
+ }
+
+ Common::SeekableReadStream *yogiStateReadStream = new Common::MemoryReadStream(yogiStateBuf, sizeof(yogiStateBuf), DisposeAfterUse::NO);
+
+ g_yogiState.levelNumber = yogiStateReadStream->readSint16BE();
+ g_yogiState.currentSlot = yogiStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 10; i++)
+ g_yogiState.levelIndex[i] = yogiStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 10; i++)
+ g_yogiState.slotIndex[i] = yogiStateReadStream->readSint16BE();
+
+ g_yogiState.levelComplete = yogiStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 24; i++)
+ g_yogiState.basketSound[i] = yogiStateReadStream->readSint16BE();
+
+ for (int i = 0; i < 24; i++)
+ g_yogiState.basketState[i] = yogiStateReadStream->readSint16BE();
+
+ g_yogiState.basketCount = yogiStateReadStream->readSint16BE();
+ g_yogiState.matchCount = yogiStateReadStream->readSint16BE();
+ g_yogiState.selectionPending = yogiStateReadStream->readSint16BE();
+ g_yogiState.selected1Slot = yogiStateReadStream->readSint16BE();
+ g_yogiState.selected2Slot = yogiStateReadStream->readSint16BE();
+ g_yogiState.sound1 = yogiStateReadStream->readSint16BE();
+ g_yogiState.sound2 = yogiStateReadStream->readSint16BE();
+
+ // Sanity check: should be exactly 0x9C
+ assert(yogiStateReadStream->pos() == 0x9C);
+ delete yogiStateReadStream;
+
+ if (g_yogiState.levelComplete)
+ return initYogiLevel();
+ else
+ return resumeYogiLevel();
+}
+
+void BoltEngine::yogiToggleBlinking(int16 which, int16 *state) {
+ *state = (*state == 0) ? 1 : 0;
+ if (*state)
+ setYogiColors(which);
+ else
+ restoreYogiColors(which);
+}
+
+void BoltEngine::yogiUpdateHotSpots(int16 x, int16 y) {
+ Common::Rect helpRect(
+ READ_UINT16(g_yogiBasketPic + 0x74), READ_UINT16(g_yogiBasketPic + 0x78),
+ READ_UINT16(g_yogiBasketPic + 0x76), READ_UINT16(g_yogiBasketPic + 0x7A));
+
+ Common::Rect exitRect(
+ READ_UINT16(g_yogiBasketPic + 0x86), READ_UINT16(g_yogiBasketPic + 0x8A),
+ READ_UINT16(g_yogiBasketPic + 0x88), READ_UINT16(g_yogiBasketPic + 0x8C));
+
+ if (helpRect.contains(x, y)) {
+ if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+ setYogiColors(0);
+ } else {
+ if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+ restoreYogiColors(0);
+ }
+
+ if (exitRect.contains(x, y)) {
+ if (!g_yogiBlinkTimer2)
+ setYogiColors(1);
+ return;
+ } else {
+ if (!g_yogiBlinkTimer2)
+ restoreYogiColors(1);
+ }
+}
+
+int16 BoltEngine::findBasket(int16 x, int16 y) {
+ int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ for (int16 i = basketCount - 1; i >= 0; i--) {
+ if (g_yogiState.basketState[i] != 0)
+ continue;
+
+ int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+ int16 slotX2 = slotX + g_yogiSpriteStride - 1;
+ int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
+ int16 slotY2 = slotY + g_yogiSpriteHeight - 1;
+
+ if (x < slotX || x > slotX2 || y < slotY || y > slotY2)
+ continue;
+
+ byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
+ ? g_yogiHlSprite
+ : g_yogiNormalSprite;
+
+ if (!getPixel(sprite, x - slotX, y - slotY))
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+void BoltEngine::resolveYogiSelection() {
+ if (g_yogiState.sound1 == g_yogiState.sound2) {
+ g_yogiState.basketState[g_yogiState.selected1Slot] = 1;
+ g_yogiState.basketState[g_yogiState.selected2Slot] = 1;
+ handleYogiMatch();
+ g_yogiState.matchCount = 0;
+ g_yogiState.selectionPending = 0;
+ } else {
+ int16 slot1 = g_yogiState.selected1Slot;
+ g_yogiState.matchCount = 0;
+ g_yogiState.selected1Slot = -1;
+ drawBasket(slot1, g_yogiNormalSprite);
+ _xp->updateDisplay();
+ playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x9A));
+ waitSoundMapYogi();
+
+ int16 slot2 = g_yogiState.selected2Slot;
+ g_yogiState.selectionPending = 0;
+ g_yogiState.selected2Slot = -1;
+ drawBasket(slot2, g_yogiNormalSprite);
+ _xp->updateDisplay();
+ playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x9C));
+ waitSoundMapYogi();
+ }
+}
+
+bool BoltEngine::handleBasketSelect(int16 x, int16 y) {
+ int16 slot = findBasket(x, y);
+ if (slot == -1)
+ return false;
+
+ if (g_yogiState.matchCount == 0) {
+ g_yogiState.matchCount = 1;
+ g_yogiState.sound1 = g_yogiState.basketSound[slot];
+ g_yogiState.selected1Slot = slot;
+
+ playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x98));
+ drawBasket(slot, g_yogiHlSprite);
+ _xp->updateDisplay();
+ waitSoundMapYogi();
+
+ int32 soundId = (int32)((g_yogiLevelId << 8) + g_yogiState.sound1 + 0xD00);
+ playSoundMapYogi(soundId);
+
+ } else {
+ if (g_yogiState.selectionPending)
+ return true;
+
+ if (g_yogiState.selected1Slot == slot) {
+ // De-select basket...
+ g_yogiState.matchCount = 0;
+ g_yogiState.selected1Slot = -1;
+
+ stopSoundYogi();
+ playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x9A));
+ drawBasket(slot, g_yogiNormalSprite);
+ _xp->updateDisplay();
+ waitSoundMapYogi();
+
+ } else {
+ g_yogiState.selectionPending = 1;
+ g_yogiState.sound2 = g_yogiState.basketSound[slot];
+ g_yogiState.selected2Slot = slot;
+
+ if (g_yogiSoundActive) {
+ int16 slotY = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x42);
+ int16 slotX = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x12);
+ displayPic(g_yogiAnimSprite, slotX + READ_UINT16(g_yogiBasketPic + 0x0A), slotY + READ_UINT16(g_yogiBasketPic + 0x0C), stFront);
+ _xp->updateDisplay();
+ waitSoundMapYogi();
+ }
+
+ playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x98));
+ drawBasket(slot, g_yogiHlSprite);
+ _xp->updateDisplay();
+ waitSoundMapYogi();
+
+ int32 soundId = (int32)((g_yogiLevelId << 8) + g_yogiState.sound2 + 0xD00);
+ playSoundMapYogi(soundId);
+ waitSoundMapYogi();
+
+ resolveYogiSelection();
+ }
+ }
+
+ return true;
+}
+
+void BoltEngine::yogiHandleActionButton(int16 x, int16 y) {
+ bool didAction = false;
+ bool stoppedAnim = false;
+
+ if (g_yogiHotSpotCount != 0) {
+ g_yogiHotSpotCount = 0;
+ stoppedAnim = true;
+ stopAnimation();
+ if (g_yogiBlinkTimer2) {
+ _xp->killTimer(g_yogiBlinkTimer2);
+ g_yogiBlinkTimer2 = 0;
+ }
+
+ yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+ }
+
+ Common::Rect helpRect(
+ READ_UINT16(g_yogiBasketPic + 0x74), READ_UINT16(g_yogiBasketPic + 0x78),
+ READ_UINT16(g_yogiBasketPic + 0x76), READ_UINT16(g_yogiBasketPic + 0x7A));
+
+ if (helpRect.contains(x, y)) {
+ didAction = true;
+ stopSoundYogi();
+ if (g_yogiBlinkTimer1) {
+ _xp->killTimer(g_yogiBlinkTimer1);
+ g_yogiBlinkTimer1 = 0;
+ setYogiColors(0);
+ }
+ if (!g_yogiHotSpotCount && !stoppedAnim) {
+ g_yogiHotSpotCount = startAnimation(g_rtfHandle, 0x1D);
+ g_yogiAnimActive = 0;
+ }
+ } else {
+ Common::Rect exitRect(
+ READ_UINT16(g_yogiBasketPic + 0x86), READ_UINT16(g_yogiBasketPic + 0x8A),
+ READ_UINT16(g_yogiBasketPic + 0x88), READ_UINT16(g_yogiBasketPic + 0x8C));
+
+ if (exitRect.contains(x, y)) {
+ stopSoundYogi();
+ setYogiColors(1);
+ restoreYogiColors(0);
+ g_yogiReturnBooth = 6;
+ g_yogiExitFlag = 1;
+ }
+ }
+
+ if (handleBasketSelect(x, y))
+ didAction = true;
+
+ if (didAction) {
+ if (g_yogiBlinkTimer1) {
+ _xp->killTimer(g_yogiBlinkTimer1);
+ g_yogiBlinkTimer1 = 0;
+ restoreYogiColors(0);
+ }
+ _xp->setInactivityTimer(0x1E);
+ }
+}
+
+void BoltEngine::handleYogiEvent(int16 eventType, uint32 eventData) {
+ switch (eventType) {
+ case etMouseDown:
+ yogiHandleActionButton(g_yogiCursorX, g_yogiCursorY);
+ break;
+
+ case etMouseMove:
+ g_yogiCursorX = (int16)(eventData >> 16);
+ g_yogiCursorY = (int16)(eventData & -1);
+ yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+ break;
+
+ case etTimer: { // 1
+ int32 blinkInterval = ((int32)READ_UINT16(g_yogiBasketPic + 0x84)) * 1000 / 60;
+
+ if (eventData == g_yogiBlinkTimer1) {
+ yogiToggleBlinking(0, &g_yogiBlinkState1);
+ g_yogiBlinkTimer1 = _xp->startTimer((int16)blinkInterval);
+ } else if (eventData == g_yogiBlinkTimer2) {
+ yogiToggleBlinking(1, &g_yogiBlinkState2);
+ g_yogiBlinkTimer2 = _xp->startTimer((int16)blinkInterval);
+ }
+
+ break;
+ }
+
+ case etInactivity:
+ if (!g_yogiBlinkTimer1) {
+ int32 blinkInterval = ((int32)READ_UINT16(g_yogiBasketPic + 0x84)) * 1000 / 60;
+ g_yogiBlinkTimer1 = _xp->startTimer((int16)blinkInterval);
+ g_yogiBlinkState1 = 0;
+ yogiToggleBlinking(0, &g_yogiBlinkState1);
+ }
+
+ break;
+
+ case etSound:
+ if (g_yogiHotSpotCount != 0) {
+ g_yogiHotSpotCount = maintainAudioPlay(1);
+ if (g_yogiHotSpotCount == 0)
+ yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+ } else {
+ g_yogiSoundActive = 0;
+ }
+
+ if (g_yogiSoundPlaying != 0)
+ g_yogiSoundPlaying--;
+
+ break;
+
+ case etTrigger:
+ g_yogiAnimActive++;
+
+ if (g_yogiAnimActive == 1) {
+ int32 blinkInterval = ((int32)READ_UINT16(g_yogiBasketPic + 0x84)) * 1000 / 60;
+ g_yogiBlinkTimer2 = _xp->startTimer((int16)blinkInterval);
+ g_yogiBlinkState2 = 0;
+ yogiToggleBlinking(1, &g_yogiBlinkState2);
+ } else if (g_yogiAnimActive == 2) {
+ _xp->killTimer(g_yogiBlinkTimer2);
+ g_yogiBlinkTimer2 = 0;
+ restoreYogiColors(1);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+void BoltEngine::playYogi() {
+ g_yogiReturnBooth = 16;
+
+ while (!shouldQuit()) {
+ if (g_yogiExitFlag)
+ break;
+
+ uint32 eventData = 0;
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
+ if (eventType)
+ handleYogiEvent(eventType, eventData);
+
+ if (g_yogiHotSpotCount != 0) {
+ g_yogiHotSpotCount = maintainAudioPlay(0);
+ if (g_yogiHotSpotCount == 0)
+ yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+ }
+ }
+
+ if (shouldQuit())
+ g_yogiReturnBooth = 0;
+}
+
+void BoltEngine::cleanUpYogi() {
+ if (g_yogiScratchBuf.pixelData) {
+ _xp->freeMem(g_yogiScratchBuf.pixelData);
+ g_yogiScratchBuf.pixelData = nullptr;
+ g_yogiScratchBuf.width = 0;
+ g_yogiScratchBuf.height = 0;
+ }
+
+ vSave(&g_yogiGlobal, 0x140, "YogiGlobal");
+
+ byte yogiSaveBuf[0x9C] = { 0 };
+ Common::MemoryWriteStream *yogiStateWriteStream = new Common::MemoryWriteStream(yogiSaveBuf, sizeof(yogiSaveBuf));
+
+ yogiStateWriteStream->writeSint16BE(g_yogiState.levelNumber);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.currentSlot);
+
+ for (int i = 0; i < 10; i++)
+ yogiStateWriteStream->writeSint16BE(g_yogiState.levelIndex[i]);
+
+ for (int i = 0; i < 10; i++)
+ yogiStateWriteStream->writeSint16BE(g_yogiState.slotIndex[i]);
+
+ yogiStateWriteStream->writeSint16BE(g_yogiState.levelComplete);
+
+ for (int i = 0; i < 24; i++)
+ yogiStateWriteStream->writeSint16BE(g_yogiState.basketSound[i]);
+
+ for (int i = 0; i < 24; i++)
+ yogiStateWriteStream->writeSint16BE(g_yogiState.basketState[i]);
+
+ yogiStateWriteStream->writeSint16BE(g_yogiState.basketCount);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.matchCount);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.selectionPending);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.selected1Slot);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.selected2Slot);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.sound1);
+ yogiStateWriteStream->writeSint16BE(g_yogiState.sound2);
+
+ // Sanity check: should be exactly 0x9C
+ assert(yogiStateWriteStream->pos() == 0x9C);
+ delete yogiStateWriteStream;
+
+ vSave(&yogiSaveBuf, 0x9C, "Yogi");
+
+ unloadYogiResources();
+ unloadYogiBgPic();
+
+ _xp->stopCycle();
+ _xp->fillDisplay(0, stFront);
+ _xp->updateDisplay();
+}
+
+int16 BoltEngine::yogiGame(int16 prevBooth) {
+ if (!openBOLTLib(&g_yogiBoltLib, &g_yogiBoltCallbacks, assetPath("yogi.blt")))
+ return g_yogiReturnBooth;
+
+ int16 savedTimer = _xp->setInactivityTimer(30);
+
+ if (initYogi())
+ playYogi();
+
+ cleanUpYogi();
+ _xp->setInactivityTimer(savedTimer);
+ closeBOLTLib(&g_yogiBoltLib);
+
+ return g_yogiReturnBooth;
+}
+
+void BoltEngine::swapYogiAllWords() {
+ byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+ if (!ptr)
+ return;
+
+ int16 count = (int16)(g_boltCurrentMemberEntry->decompSize >> 1);
+ for (int16 i = 0; i < count; i++) {
+ WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+ ptr += 2;
+ }
+}
+
+void BoltEngine::swapYogiFirstWord() {
+ byte *ptr = (byte *)g_boltCurrentMemberEntry->dataPtr;
+ if (!ptr)
+ return;
+ WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+}
} // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 0127978dd46..19d6d886654 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -659,6 +659,9 @@ BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
BOLTCallback BoltEngine::g_topCatTypeLoadCallbacks[26];
BOLTCallback BoltEngine::g_topCatTypeFreeCallbacks[26];
+BOLTCallback BoltEngine::g_yogiTypeLoadCallbacks[27];
+BOLTCallback BoltEngine::g_yogiTypeFreeCallbacks[27];
+
void BoltEngine::noOpCb() {}
void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
void BoltEngine::swapAllLongsCb() { ((BoltEngine *)g_engine)->swapAllLongs(); }
@@ -687,6 +690,9 @@ void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyW
void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
+void BoltEngine::swapYogiAllWordsCb() { ((BoltEngine *)g_engine)->swapYogiAllWords(); }
+void BoltEngine::swapYogiFirstWordCb() { ((BoltEngine *)g_engine)->swapYogiFirstWord(); }
+
void BoltEngine::initCallbacks() {
// --- BOOTHS ---
for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
@@ -866,6 +872,31 @@ void BoltEngine::initCallbacks() {
g_topCatBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
g_topCatBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
g_topCatBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
+ // --- YOGI ---
+ for (int i = 0; i < ARRAYSIZE(g_yogiTypeLoadCallbacks); i++) {
+ g_yogiTypeLoadCallbacks[i] = noOpCb;
+ }
+
+ g_yogiTypeLoadCallbacks[2] = swapAllWordsCb;
+ g_yogiTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+ g_yogiTypeLoadCallbacks[10] = swapPicHeaderCb;
+ g_yogiTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+ g_yogiTypeLoadCallbacks[25] = swapYogiFirstWordCb;
+ g_yogiTypeLoadCallbacks[26] = swapYogiAllWordsCb;
+
+ for (int i = 0; i < ARRAYSIZE(g_yogiTypeFreeCallbacks); i++) {
+ g_yogiTypeFreeCallbacks[i] = noOpCb;
+ }
+
+ g_yogiTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+ g_yogiBoltCallbacks.typeLoadCallbacks = g_yogiTypeLoadCallbacks;
+ g_yogiBoltCallbacks.typeFreeCallbacks = g_yogiTypeFreeCallbacks;
+ g_yogiBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+ g_yogiBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+ g_yogiBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+ g_yogiBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
}
} // End of namespace Bolt
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
index b2a3c82ebcf..293d7531e6b 100644
--- a/engines/bolt/utils.cpp
+++ b/engines/bolt/utils.cpp
@@ -158,4 +158,9 @@ void BoltEngine::unpackColors(int16 count, byte *packedColors) {
}
}
+bool BoltEngine::intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out) {
+ *out = a->findIntersectingRect(*b);
+ return !out->isEmpty();
+}
+
} // End of namespace Bolt
Commit: 9f25c67c6d19bc64f63da8473929ec6e02501985
https://github.com/scummvm/scummvm/commit/9f25c67c6d19bc64f63da8473929ec6e02501985
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Final cleanup
Changed paths:
R engines/bolt/console.cpp
R engines/bolt/console.h
engines/bolt/anim.cpp
engines/bolt/av.cpp
engines/bolt/barker.cpp
engines/bolt/bolt.cpp
engines/bolt/bolt.h
engines/bolt/booth.cpp
engines/bolt/booths/fred.cpp
engines/bolt/booths/george.cpp
engines/bolt/booths/huck.cpp
engines/bolt/booths/scooby.cpp
engines/bolt/booths/topcat.cpp
engines/bolt/booths/yogi.cpp
engines/bolt/configure.engine
engines/bolt/detection_tables.h
engines/bolt/metaengine.cpp
engines/bolt/module.mk
engines/bolt/resource.cpp
engines/bolt/rtf.cpp
engines/bolt/ssprite.cpp
engines/bolt/state.cpp
engines/bolt/swap.cpp
engines/bolt/xplib/cursor.cpp
engines/bolt/xplib/display.cpp
engines/bolt/xplib/events.cpp
engines/bolt/xplib/palette.cpp
engines/bolt/xplib/timer.cpp
engines/bolt/xplib/xplib.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/anim.cpp b/engines/bolt/anim.cpp
index 7d9b90fc830..b87b54b8eff 100644
--- a/engines/bolt/anim.cpp
+++ b/engines/bolt/anim.cpp
@@ -57,37 +57,37 @@ bool BoltEngine::maintainAudioPlay(int16 mode) {
bool BoltEngine::initAnim(RTFResource *rtf, int16 animIndex) {
int32 bufSize;
- g_animPrevInactivityTimer = _xp->setInactivityTimer(0);
+ _animPrevInactivityTimer = _xp->setInactivityTimer(0);
// Try 80KB ring buffer, fall back to 40KB
bufSize = 0x14000;
- g_animRingBuffer = (byte *)_xp->tryAllocMem(bufSize);
- if (!g_animRingBuffer) {
+ _animRingBuffer = (byte *)_xp->tryAllocMem(bufSize);
+ if (!_animRingBuffer) {
bufSize = 0xA000;
- g_animRingBuffer = (byte *)_xp->allocMem(bufSize);
+ _animRingBuffer = (byte *)_xp->allocMem(bufSize);
}
- if (!g_animRingBuffer)
+ if (!_animRingBuffer)
return false;
- if (!playRTF(rtf, animIndex, g_animRingBuffer, bufSize))
+ if (!playRTF(rtf, animIndex, _animRingBuffer, bufSize))
return false;
return true;
}
void BoltEngine::cleanUpAnim() {
- if (g_animRingBuffer) {
- _xp->freeMem(g_animRingBuffer);
- g_animRingBuffer = nullptr;
+ if (_animRingBuffer) {
+ _xp->freeMem(_animRingBuffer);
+ _animRingBuffer = nullptr;
}
- if (g_animFileHandle) {
- _xp->closeFile(g_animFileHandle);
- g_animFileHandle = nullptr;
+ if (_animFileHandle) {
+ _xp->closeFile(_animFileHandle);
+ _animFileHandle = nullptr;
}
- _xp->setInactivityTimer(g_animPrevInactivityTimer);
+ _xp->setInactivityTimer(_animPrevInactivityTimer);
}
} // End of namespace Bolt
diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
index 710d13fd6d2..eb6eceb9be7 100644
--- a/engines/bolt/av.cpp
+++ b/engines/bolt/av.cpp
@@ -24,7 +24,7 @@
namespace Bolt {
void BoltEngine::setAVBufferSize(uint32 bufSize) {
- g_avTargetBufSize = bufSize;
+ _avTargetBufSize = bufSize;
}
bool BoltEngine::prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
@@ -61,7 +61,7 @@ bool BoltEngine::playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, in
int16 eventType = 0;
// Init if not already loaded...
- if (!g_avRingBuffer) {
+ if (!_avRingBuffer) {
if (!initAV(rtfHandle, animIndex, width, height, xOff, yOff)) {
cleanUpAV();
return false;
@@ -113,17 +113,17 @@ void BoltEngine::processPacket(RTFPacket *packet) {
int16 frameRate = packet->frameRate;
// Skip this frame if: low framerate, only 1 behind, or haven't accumulated enough...
- if (frameRate <= 10 || packet->skipCount > 1 || g_avFrameAccum < g_avSkipLevel) {
- g_avFrameAccum += packet->duration;
+ if (frameRate <= 10 || packet->skipCount > 1 || _avFrameAccum < _avSkipLevel) {
+ _avFrameAccum += packet->duration;
debug(4, "BoltEngine::processPacket(): Skipping frame...");
return;
}
// Adjust skip level based on how far behind we are...
- if (g_avFrameAccum > g_avSkipLevel)
- g_avSkipLevel = (g_avFrameAccum > 4) ? 4 : g_avFrameAccum;
+ if (_avFrameAccum > _avSkipLevel)
+ _avSkipLevel = (_avFrameAccum > 4) ? 4 : _avFrameAccum;
- g_avFrameAccum = 0;
+ _avFrameAccum = 0;
int16 targetSkip;
if (frameRate <= 12)
@@ -136,17 +136,17 @@ void BoltEngine::processPacket(RTFPacket *packet) {
targetSkip = 1;
// If keeping up fine, stop skipping entirely...
- if (g_avSkipLevel <= 1 && targetSkip == 1 && packet->skipCount == 0) {
- g_avSkipLevel = 0;
+ if (_avSkipLevel <= 1 && targetSkip == 1 && packet->skipCount == 0) {
+ _avSkipLevel = 0;
} else {
// Ramp toward target skip level...
- if (targetSkip > g_avSkipLevel)
- g_avSkipLevel = targetSkip;
- else if (targetSkip < g_avSkipLevel)
- g_avSkipLevel--;
+ if (targetSkip > _avSkipLevel)
+ _avSkipLevel = targetSkip;
+ else if (targetSkip < _avSkipLevel)
+ _avSkipLevel--;
}
} else if (tag == MKTAG('r', 'l', '7', ' ') || tag == MKTAG('r', 'l', '7', 'f')) {
- g_avFrameAccum = 0;
+ _avFrameAccum = 0;
}
// Process frame, palette and trigger packets...
@@ -171,9 +171,9 @@ void BoltEngine::processRL7(RTFPacket *packet) {
uint32 tag = packet->tag;
if (tag == MKTAG('R', 'L', '7', 'B')) {
- displayDesc = &g_avBackBufDesc;
+ displayDesc = &_avBackBufDesc;
} else {
- displayDesc = &g_avFrontBufDesc;
+ displayDesc = &_avFrontBufDesc;
}
switch (tag) {
@@ -202,19 +202,19 @@ void BoltEngine::processRL7(RTFPacket *packet) {
displayDesc->pixelData = packet->dataPtr;
- _xp->displayPic(displayDesc, g_avDisplayX, g_avDisplayY, page);
+ _xp->displayPic(displayDesc, _avDisplayX, _avDisplayY, page);
if (updateDisplay)
_xp->updateDisplay();
- g_avFrontBufDesc.palette = nullptr;
- g_avFrontBufDesc.paletteStart = 0;
- g_avFrontBufDesc.paletteCount = 0;
+ _avFrontBufDesc.palette = nullptr;
+ _avFrontBufDesc.paletteStart = 0;
+ _avFrontBufDesc.paletteCount = 0;
if (transparency) {
- g_avBackBufDesc.palette = nullptr;
- g_avBackBufDesc.paletteStart = 0;
- g_avBackBufDesc.paletteCount = 0;
+ _avBackBufDesc.palette = nullptr;
+ _avBackBufDesc.paletteStart = 0;
+ _avBackBufDesc.paletteCount = 0;
}
}
@@ -228,11 +228,11 @@ void BoltEngine::processPLTE(RTFPacket *packet) {
byte *palBuffer;
if (packet->tag == MKTAG('P', 'L', 'T', 'B')) {
- desc = &g_avBackBufDesc;
- palBuffer = g_avFrontPalette;
+ desc = &_avBackBufDesc;
+ palBuffer = _avFrontPalette;
} else {
- desc = &g_avFrontBufDesc;
- palBuffer = g_avBackPalette;
+ desc = &_avFrontBufDesc;
+ palBuffer = _avBackPalette;
}
desc->paletteStart = startIndex;
@@ -249,83 +249,83 @@ void BoltEngine::processPLTE(RTFPacket *packet) {
}
bool BoltEngine::initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
- uint32 savedAllocSize = g_avTargetBufSize;
+ uint32 savedAllocSize = _avTargetBufSize;
uint32 allocSize = 0;
- g_avTargetBufSize = 0xFA000; // ~1MB
+ _avTargetBufSize = 0xFA000; // ~1MB
- g_avDisplayY = yOff;
- g_avDisplayX = xOff;
+ _avDisplayY = yOff;
+ _avDisplayX = xOff;
- g_avSavedInactivityTimer = _xp->setInactivityTimer(0);
- g_avSavedScreenSaverTimer = _xp->setScreenSaverTimer(0);
+ _avSavedInactivityTimer = _xp->setInactivityTimer(0);
+ _avSavedScreenSaverTimer = _xp->setScreenSaverTimer(0);
- g_avBackPalette = nullptr;
- g_avFrontPalette = nullptr;
- g_avFrameAccum = 0;
+ _avBackPalette = nullptr;
+ _avFrontPalette = nullptr;
+ _avFrameAccum = 0;
// Allocate two 384-byte palette buffers (128 RGB triplets each)
- g_avBackPalette = (byte *)_xp->allocMem(128 * 3);
- if (!g_avBackPalette)
+ _avBackPalette = (byte *)_xp->allocMem(128 * 3);
+ if (!_avBackPalette)
return false;
- g_avFrontPalette = (byte *)_xp->allocMem(128 * 3);
- if (!g_avFrontPalette)
+ _avFrontPalette = (byte *)_xp->allocMem(128 * 3);
+ if (!_avFrontPalette)
return false;
// Allocate ring buffer: try saved size, shrink by 50KB until 300KB minimum
allocSize = savedAllocSize;
do {
- g_avRingBuffer = (byte *)_xp->tryAllocMem(allocSize);
- if (g_avRingBuffer)
+ _avRingBuffer = (byte *)_xp->tryAllocMem(allocSize);
+ if (_avRingBuffer)
break;
allocSize -= 0xC800;
} while (allocSize >= 0x4B000);
- if (!g_avRingBuffer) {
+ if (!_avRingBuffer) {
error("BoltEngine::initAV(): Not enough memory");
return false;
}
- g_avBackBufDesc.width = width;
- g_avFrontBufDesc.width = width;
- g_avBackBufDesc.height = height;
- g_avFrontBufDesc.height = height;
- g_avBackBufDesc.flags = 1;
- g_avFrontBufDesc.flags = 1;
- g_avBackBufDesc.palette = nullptr;
- g_avFrontBufDesc.palette = nullptr;
- g_avBackBufDesc.paletteStart = 0;
- g_avFrontBufDesc.paletteStart = 0;
- g_avBackBufDesc.paletteCount = 0;
- g_avFrontBufDesc.paletteCount = 0;
+ _avBackBufDesc.width = width;
+ _avFrontBufDesc.width = width;
+ _avBackBufDesc.height = height;
+ _avFrontBufDesc.height = height;
+ _avBackBufDesc.flags = 1;
+ _avFrontBufDesc.flags = 1;
+ _avBackBufDesc.palette = nullptr;
+ _avFrontBufDesc.palette = nullptr;
+ _avBackBufDesc.paletteStart = 0;
+ _avFrontBufDesc.paletteStart = 0;
+ _avBackBufDesc.paletteCount = 0;
+ _avFrontBufDesc.paletteCount = 0;
// Open and start streaming!
- if (!playRTF(rtfHandle, animIndex, g_avRingBuffer, allocSize))
+ if (!playRTF(rtfHandle, animIndex, _avRingBuffer, allocSize))
return false;
return true;
}
void BoltEngine::cleanUpAV() {
- if (g_avRingBuffer) {
- _xp->freeMem(g_avRingBuffer);
- g_avRingBuffer = nullptr;
+ if (_avRingBuffer) {
+ _xp->freeMem(_avRingBuffer);
+ _avRingBuffer = nullptr;
}
- if (g_avFrontPalette) {
- _xp->freeMem(g_avFrontPalette);
- g_avFrontPalette = nullptr;
+ if (_avFrontPalette) {
+ _xp->freeMem(_avFrontPalette);
+ _avFrontPalette = nullptr;
}
- if (g_avBackPalette) {
- _xp->freeMem(g_avBackPalette);
- g_avBackPalette = nullptr;
+ if (_avBackPalette) {
+ _xp->freeMem(_avBackPalette);
+ _avBackPalette = nullptr;
}
- _xp->setInactivityTimer(g_avSavedInactivityTimer);
- _xp->setScreenSaverTimer(g_avSavedScreenSaverTimer);
+ _xp->setInactivityTimer(_avSavedInactivityTimer);
+ _xp->setScreenSaverTimer(_avSavedScreenSaverTimer);
}
} // End of namespace Bolt
diff --git a/engines/bolt/barker.cpp b/engines/bolt/barker.cpp
index 2fa68c1f179..f9d355c70a6 100644
--- a/engines/bolt/barker.cpp
+++ b/engines/bolt/barker.cpp
@@ -44,10 +44,10 @@ BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
}
if (success) {
- g_curErrorCode = 0;
+ _curErrorCode = 0;
return table;
} else {
- g_curErrorCode = 1;
+ _curErrorCode = 1;
return nullptr;
}
}
@@ -64,13 +64,13 @@ void BoltEngine::freeBarker(BarkerTable *table) {
bool BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId) {
if (table->minIndex > boothId || table->maxIndex < boothId) {
- g_curErrorCode = 5;
+ _curErrorCode = 5;
return false;
}
table->handlers[boothId - table->minIndex] = handler;
- g_curErrorCode = 0;
+ _curErrorCode = 0;
return true;
}
@@ -90,11 +90,11 @@ int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
currentBooth = nextBooth;
}
- return g_currentBoothId;
+ return _currentBoothId;
}
bool BoltEngine::checkError() {
- return g_curErrorCode != 0;
+ return _curErrorCode != 0;
}
} // End of namespace Bolt
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 76a04e3e052..76078ea854d 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -20,9 +20,9 @@
*/
#include "bolt/bolt.h"
-#include "graphics/framelimiter.h"
#include "bolt/detection.h"
-#include "bolt/console.h"
+#include "bolt/xplib/xplib.h"
+
#include "common/scummsys.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
@@ -31,8 +31,6 @@
#include "engines/util.h"
#include "graphics/paletteman.h"
-#include "bolt/xplib/xplib.h"
-
namespace Bolt {
BoltEngine *g_engine;
@@ -60,21 +58,18 @@ Common::String BoltEngine::getGameId() const {
Common::Error BoltEngine::run() {
ConfMan.registerDefault("extended_viewport", false);
if (ConfMan.hasKey("extended_viewport", _targetName)) {
- g_extendedViewport = ConfMan.getBool("extended_viewport");
+ _extendedViewport = ConfMan.getBool("extended_viewport");
}
// Initialize paletted graphics mode
- if (!g_extendedViewport) {
+ if (!_extendedViewport) {
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
} else {
initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
}
if ((getFeatures() & ADGF_DEMO) != 0)
- g_isDemo = true;
-
- // Set the engine's debugger console
- setDebugger(new Console());
+ _isDemo = true;
_xp->initialize();
boltMain();
@@ -83,17 +78,6 @@ Common::Error BoltEngine::run() {
return Common::kNoError;
}
-Common::Error BoltEngine::syncGame(Common::Serializer &s) {
- // The Serializer has methods isLoading() and isSaving()
- // if you need to specific steps; for example setting
- // an array size after reading it's length, whereas
- // for saving it would write the existing array's length
- int dummy = 0;
- s.syncAsUint32LE(dummy);
-
- return Common::kNoError;
-}
-
void BoltEngine::boltMain() {
byte *testAlloc;
BarkerTable *barkerTable;
@@ -108,56 +92,56 @@ void BoltEngine::boltMain() {
_xp->randomize();
if (allocResourceIndex()) {
- g_boothsBoltLib = nullptr;
+ _boothsBoltLib = nullptr;
- if (openBOLTLib(&g_boothsBoltLib, &g_boothsBoltCallbacks, assetPath("booths.blt"))) {
- int16 chosenSpecId = g_extendedViewport ? 0 : 1;
+ if (openBOLTLib(&_boothsBoltLib, &_boothsBoltCallbacks, assetPath("booths.blt"))) {
+ int16 chosenSpecId = _extendedViewport ? 0 : 1;
- if (_xp->setDisplaySpec(&g_displayMode, &g_displaySpecs[chosenSpecId])) {
- assert(g_displayMode >= 0);
- g_displayWidth = g_displaySpecs[g_displayMode].width;
- g_displayHeight = g_displaySpecs[g_displayMode].height;
+ if (_xp->setDisplaySpec(&_displayMode, &_displaySpecs[chosenSpecId])) {
+ assert(_displayMode >= 0);
+ _displayWidth = _displaySpecs[_displayMode].width;
+ _displayHeight = _displaySpecs[_displayMode].height;
// Center within 384x240 virtual coordinate space...
- g_displayX = (EXTENDED_SCREEN_WIDTH - g_displayWidth) / 2;
- g_displayY = (EXTENDED_SCREEN_HEIGHT - g_displayHeight) / 2;
+ _displayX = (EXTENDED_SCREEN_WIDTH - _displayWidth) / 2;
+ _displayY = (EXTENDED_SCREEN_HEIGHT - _displayHeight) / 2;
- _xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
+ _xp->setCoordSpec(_displayX, _displayY, _displayWidth, _displayHeight);
- if (g_displayMode == 0) {
- g_rtfHandle = openRTF(assetPath("cc_og.av"));
+ if (_displayMode == 0) {
+ _rtfHandle = openRTF(assetPath("cc_og.av"));
} else {
- g_rtfHandle = openRTF(assetPath("cc_cr.av"));
+ _rtfHandle = openRTF(assetPath("cc_cr.av"));
}
- if (g_rtfHandle) {
- int16 boothGroupBase = g_isDemo ? 0x0E00 : 0x1700;
+ if (_rtfHandle) {
+ int16 boothGroupBase = _isDemo ? 0x0E00 : 0x1700;
- if (!g_isDemo) {
- playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ if (!_isDemo) {
+ playAV(_rtfHandle, 0, _displayWidth, _displayHeight, _displayX, _displayY);
}
- boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
+ boothSprite = getBOLTMember(_boothsBoltLib, (_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
_xp->setTransparency(false);
- displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stFront, 0);
- displayPic(boothSprite, g_displayX, g_displayY, stFront);
+ displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stFront, 0);
+ displayPic(boothSprite, _displayX, _displayY, stFront);
_xp->updateDisplay();
- displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stBack, 0);
- displayPic(boothSprite, g_displayX, g_displayY, stBack);
+ displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stBack, 0);
+ displayPic(boothSprite, _displayX, _displayY, stBack);
- playAV(g_rtfHandle, g_isDemo ? 0 : 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ playAV(_rtfHandle, _isDemo ? 0 : 2, _displayWidth, _displayHeight, _displayX, _displayY);
- freeBOLTGroup(g_boothsBoltLib, boothGroupBase, 1);
+ freeBOLTGroup(_boothsBoltLib, boothGroupBase, 1);
- if (getBOLTGroup(g_boothsBoltLib, 0, 1)) {
- setCursorPict(memberAddr(g_boothsBoltLib, 0));
+ if (getBOLTGroup(_boothsBoltLib, 0, 1)) {
+ setCursorPict(memberAddr(_boothsBoltLib, 0));
if (initVRam(1500)) {
- barkerTable = createBarker(2, g_isDemo ? 19 : 17);
+ barkerTable = createBarker(2, _isDemo ? 19 : 17);
if (barkerTable) {
// Register booth handlers...
registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
@@ -175,12 +159,12 @@ void BoltEngine::boltMain() {
registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
- if (g_isDemo) {
+ if (_isDemo) {
registerSideShow(barkerTable, &BoltEngine::displayDemoPict, 17);
registerSideShow(barkerTable, &BoltEngine::endDemo, 18);
}
- g_lettersWon = 0;
+ _lettersWon = 0;
_xp->setScreenSaverTimer(1800);
// The barker function runs the main loop, starting at mainEntrance()...
@@ -196,13 +180,13 @@ void BoltEngine::boltMain() {
}
}
- freeBOLTGroup(g_boothsBoltLib, 0, 1);
+ freeBOLTGroup(_boothsBoltLib, 0, 1);
- if (g_boothsBoltLib != 0)
- closeBOLTLib(&g_boothsBoltLib);
+ if (_boothsBoltLib != 0)
+ closeBOLTLib(&_boothsBoltLib);
- if (g_rtfHandle != nullptr)
- closeRTF(g_rtfHandle);
+ if (_rtfHandle != nullptr)
+ closeRTF(_rtfHandle);
freeVRam();
freeResourceIndex();
@@ -237,9 +221,9 @@ int16 BoltEngine::displayDemoPict(int16 prevBooth) {
_xp->stopCycle();
_xp->setTransparency(false);
- int16 memberId = (g_displayMode != 0) ? 0x1002 : 0x1001;
- displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1000), stFront, 0);
+ int16 memberId = (_displayMode != 0) ? 0x1002 : 0x1001;
+ displayPic(getBOLTMember(_boothsBoltLib, memberId), _displayX, _displayY, stFront);
+ displayColors(getBOLTMember(_boothsBoltLib, 0x1000), stFront, 0);
_xp->updateDisplay();
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index c0cb0149957..2a2a1b9a5f7 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -567,17 +567,11 @@ public:
*/
Common::String getGameId() const;
- /**
- * Gets a random number
- */
- uint32 getRandomNumber(uint maxNum) {
- return _randomSource.getRandomNumber(maxNum);
- }
-
bool hasFeature(EngineFeature f) const override {
return (f == kSupportsReturnToLauncher);
};
+ // This engine doesn't save anything!
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
return false;
}
@@ -585,30 +579,15 @@ public:
return false;
}
- /**
- * Uses a serializer to allow implementing savegame
- * loading and saving using a single method
- */
- Common::Error syncGame(Common::Serializer &s);
-
- Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
- Common::Serializer s(nullptr, stream);
- return syncGame(s);
- }
- Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
- Common::Serializer s(stream, nullptr);
- return syncGame(s);
- }
-
protected:
- DisplaySpecs g_displaySpecs[2] = {
+ DisplaySpecs _displaySpecs[2] = {
{0, 384, 240},
{1, 320, 200}
};
XpLib *_xp = nullptr;
- bool g_extendedViewport = false;
- bool g_isDemo = false;
+ bool _extendedViewport = false;
+ bool _isDemo = false;
// xpMain
void boltMain();
@@ -651,56 +630,56 @@ protected:
int16 winALetter(int16 prevBooth);
int16 endDemo(int16 prevBooth);
- int16 g_lettersWon = 0;
- bool g_allLettersWonFlag = false;
- int g_displayMode = 0;
- int32 g_displayX = 0;
- int32 g_displayY = 0;
- int32 g_displayWidth = 0;
- int32 g_displayHeight = 0;
-
- int16 g_boothLoadedMask = 0;
- int16 g_currentBoothScene = 0;
- int16 g_boothNumHotspots = 0;
- int16 g_boothNumAnimations = 0;
- byte *g_boothSceneDesc = nullptr;
- byte *g_boothHotPalDescs[4];
- Common::Rect g_boothHotspotDescs[8];
- byte *g_boothAnimDescs[7];
- byte *g_boothAnimPalDescs[4];
- byte g_savedPalettes[7][30];
- byte g_savedHotPalettes[4][9];
- byte *g_boothPalCycleData = nullptr;
- XPPicDesc g_boothLetterSprite;
- bool g_needInitCursorPos = true;
- byte *g_boothVisitSignOn = nullptr;
- byte *g_boothVisitSignOff = nullptr;
- byte g_leftDoorNavTable[3] = {3, 2, 4};
- byte g_rightDoorNavTable[3] = {1, 0, 5};
-
- int16 g_cursorX = 0;
- int16 g_cursorY = 0;
- int16 g_hoveredHotspot = 0;
- uint32 g_helpTimer = 0;
- int16 g_keyReleased = 0;
- int16 g_keyLeft = 0;
- int16 g_keyRight = 0;
- int16 g_keyUp = 0;
- int16 g_keyDown = 0;
- int16 g_helpFlag = 0;
- int16 g_keyEnter = 0;
-
- int16 g_tourStep = 0;
- int16 g_helpPlaying = 0;
- int16 g_helpIsIdle = 0;
- int16 g_idleHelpAudioAvailable = 1;
-
- int16 g_huckWins = 0;
- int16 g_fredWins = 0;
- int16 g_scoobyWins = 0;
- int16 g_yogiWins = 0;
- int16 g_georgeWins = 0;
- int16 g_topCatWins = 0;
+ int16 _lettersWon = 0;
+ bool _allLettersWonFlag = false;
+ int _displayMode = 0;
+ int32 _displayX = 0;
+ int32 _displayY = 0;
+ int32 _displayWidth = 0;
+ int32 _displayHeight = 0;
+
+ int16 _boothLoadedMask = 0;
+ int16 _currentBoothScene = 0;
+ int16 _boothNumHotspots = 0;
+ int16 _boothNumAnimations = 0;
+ byte *_boothSceneDesc = nullptr;
+ byte *_boothHotPalDescs[4];
+ Common::Rect _boothHotspotDescs[8];
+ byte *_boothAnimDescs[7];
+ byte *_boothAnimPalDescs[4];
+ byte _savedPalettes[7][30];
+ byte _savedHotPalettes[4][9];
+ byte *_boothPalCycleData = nullptr;
+ XPPicDesc _boothLetterSprite;
+ bool _needInitCursorPos = true;
+ byte *_boothVisitSignOn = nullptr;
+ byte *_boothVisitSignOff = nullptr;
+ byte _leftDoorNavTable[3] = { 3, 2, 4 };
+ byte _rightDoorNavTable[3] = { 1, 0, 5 };
+
+ int16 _cursorX = 0;
+ int16 _cursorY = 0;
+ int16 _hoveredHotspot = 0;
+ uint32 _helpTimer = 0;
+ int16 _keyReleased = 0;
+ int16 _keyLeft = 0;
+ int16 _keyRight = 0;
+ int16 _keyUp = 0;
+ int16 _keyDown = 0;
+ int16 _helpFlag = 0;
+ int16 _keyEnter = 0;
+
+ int16 _tourStep = 0;
+ int16 _helpPlaying = 0;
+ int16 _helpIsIdle = 0;
+ int16 _idleHelpAudioAvailable = 1;
+
+ int16 _huckWins = 0;
+ int16 _fredWins = 0;
+ int16 _scoobyWins = 0;
+ int16 _yogiWins = 0;
+ int16 _georgeWins = 0;
+ int16 _topCatWins = 0;
// Barker
BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
@@ -709,8 +688,8 @@ protected:
int16 barker(BarkerTable *table, int16 startBooth);
bool checkError();
- int32 g_curErrorCode = 0;
- int16 g_currentBoothId = 0;
+ int32 _curErrorCode = 0;
+ int16 _currentBoothId = 0;
// Utils
void displayColors(byte *palette, int16 page, int16 flags);
@@ -758,15 +737,15 @@ protected:
void swapAllWords();
void swapAllLongs();
- BOLTLib *g_boothsBoltLib = nullptr;
- BOLTCallbacks g_boothsBoltCallbacks;
+ BOLTLib *_boothsBoltLib = nullptr;
+ BOLTCallbacks _boothsBoltCallbacks;
- static BOLTCallback g_defaultTypeLoadCallbacks[25];
- static BOLTCallback g_defaultTypeFreeCallbacks[25];
- static BOLTCallback g_defaultMemberLoadCallbacks[25];
- static BOLTCallback g_defaultMemberFreeCallbacks[25];
- static BOLTCallback g_defaultGroupLoadCallbacks[25];
- static BOLTCallback g_defaultGroupFreeCallbacks[25];
+ static BOLTCallback _defaultTypeLoadCallbacks[25];
+ static BOLTCallback _defaultTypeFreeCallbacks[25];
+ static BOLTCallback _defaultMemberLoadCallbacks[25];
+ static BOLTCallback _defaultMemberFreeCallbacks[25];
+ static BOLTCallback _defaultGroupLoadCallbacks[25];
+ static BOLTCallback _defaultGroupFreeCallbacks[25];
static void noOpCb();
@@ -782,20 +761,20 @@ protected:
void initCallbacks();
- int16 g_resourceIndexCount = 1000;
- uint32 **g_resourceIndex = nullptr;
- Common::Array<byte *> g_resolvedPtrs;
- Common::File *g_cachedFileHandle = nullptr;
- uint32 g_cachedFilePos = 0xFFFFFFFF;
- BOLTLib *g_boltCurrentLib = nullptr;
- BOLTHeader g_boltFileHeader;
- BOLTGroupEntry *g_boltCurrentGroupEntry = nullptr;
- int16 g_boltLoadDepth = 0;
- byte *g_boltRawMemberData = nullptr;
- BOLTMemberEntry *g_boltCurrentMemberEntry = nullptr;
- int16 g_pendingFixupCount = 0;
-
- // Game levelNumber
+ int16 _resourceIndexCount = 1000;
+ uint32 **_resourceIndex = nullptr;
+ Common::Array<byte *> _resolvedPtrs;
+ Common::File *_cachedFileHandle = nullptr;
+ uint32 _cachedFilePos = 0xFFFFFFFF;
+ BOLTLib *_boltCurrentLib = nullptr;
+ BOLTHeader _boltFileHeader;
+ BOLTGroupEntry *_boltCurrentGroupEntry = nullptr;
+ int16 _boltLoadDepth = 0;
+ byte *_boltRawMemberData = nullptr;
+ BOLTMemberEntry *_boltCurrentMemberEntry = nullptr;
+ int16 _pendingFixupCount = 0;
+
+ // Game state
bool initVRam(int16 poolSize);
void freeVRam();
bool vLoad(void *dest, const char *name);
@@ -805,10 +784,10 @@ protected:
uint16 dataSize(int16 recordOffset);
bool findRecord(const char *name, int16 *outOffset);
- int32 g_vramRecordCount = 0;
- int32 g_vramUsedBytes = 0;
- byte *g_allocatedMemPool = nullptr;
- uint32 g_allocatedMemPoolSize = 0;
+ int32 _vramRecordCount = 0;
+ int32 _vramUsedBytes = 0;
+ byte *_allocatedMemPool = nullptr;
+ uint32 _allocatedMemPoolSize = 0;
// RTF
RTFResource *openRTF(const char *fileName);
@@ -828,33 +807,33 @@ protected:
void resetPlaybackState();
void setAVBufferSize(uint32 bufSize);
- RTFResource *g_rtfHandle = nullptr;
- Common::File *g_rtfFileHandle = nullptr;
- uint32 g_rtfChunkRemaining = 0;
- bool g_rtfMidChunk = false;
- RTFPacket *g_rtfCurrentPacket = nullptr;
- byte *g_ringBufBase = nullptr;
- byte *g_ringBufWritePtr = nullptr;
- uint32 g_ringBufSize = 0;
- uint32 g_ringBufFreeSpace = 0;
- uint32 g_ringBufLowWater = 0;
- uint32 g_ringBufHighWater = 0;
- uint32 g_ringBufUsed = 0;
- bool g_rtfSoundActive = false;
- int16 g_rtfPlaybackTime = 0;
- int16 g_rtfCumulativeTime = 0;
- RTFPacket *g_rtfPendingFrame = nullptr;
- RTFPacket *g_rtfSoundQueueHead = nullptr;
- RTFPacket *g_rtfSoundPlayHead = nullptr;
- RTFPacket *g_rtfChunkListTail = nullptr;
- RTFPacket *g_rtfChunkListHead = nullptr;
- int16 g_rtfChunkCount = 0;
- int16 g_rtfQueuedSoundCount = 0;
- int16 g_rtfSoundTiming = 0;
- uint32 g_rtfAnimStartOffset = 0;
- bool g_rtfNeedInitialFill = false;
- uint32 g_rtfChunkTag = 0;
- uint32 g_rtfChunkSize = 0;
+ RTFResource *_rtfHandle = nullptr;
+ Common::File *_rtfFileHandle = nullptr;
+ uint32 _rtfChunkRemaining = 0;
+ bool _rtfMidChunk = false;
+ RTFPacket *_rtfCurrentPacket = nullptr;
+ byte *_ringBufBase = nullptr;
+ byte *_ringBufWritePtr = nullptr;
+ uint32 _ringBufSize = 0;
+ uint32 _ringBufFreeSpace = 0;
+ uint32 _ringBufLowWater = 0;
+ uint32 _ringBufHighWater = 0;
+ uint32 _ringBufUsed = 0;
+ bool _rtfSoundActive = false;
+ int16 _rtfPlaybackTime = 0;
+ int16 _rtfCumulativeTime = 0;
+ RTFPacket *_rtfPendingFrame = nullptr;
+ RTFPacket *_rtfSoundQueueHead = nullptr;
+ RTFPacket *_rtfSoundPlayHead = nullptr;
+ RTFPacket *_rtfChunkListTail = nullptr;
+ RTFPacket *_rtfChunkListHead = nullptr;
+ int16 _rtfChunkCount = 0;
+ int16 _rtfQueuedSoundCount = 0;
+ int16 _rtfSoundTiming = 0;
+ uint32 _rtfAnimStartOffset = 0;
+ bool _rtfNeedInitialFill = false;
+ uint32 _rtfChunkTag = 0;
+ uint32 _rtfChunkSize = 0;
// AV
bool prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
@@ -867,19 +846,19 @@ protected:
bool initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
void cleanUpAV();
- byte *g_avRingBuffer = nullptr;
- uint32 g_avTargetBufSize = 0x0FA000;
- int16 g_avSkipLevel = 0;
- byte *g_avFrontPalette = nullptr;
- byte *g_avBackPalette = nullptr;
- uint32 g_avSavedInactivityTimer = 0;
- uint32 g_avSavedScreenSaverTimer = 0;
- int16 g_avFrameAccum = 0;
- int16 g_avDisplayX = 0;
- int16 g_avDisplayY = 0;
+ byte *_avRingBuffer = nullptr;
+ uint32 _avTargetBufSize = 0x0FA000;
+ int16 _avSkipLevel = 0;
+ byte *_avFrontPalette = nullptr;
+ byte *_avBackPalette = nullptr;
+ uint32 _avSavedInactivityTimer = 0;
+ uint32 _avSavedScreenSaverTimer = 0;
+ int16 _avFrameAccum = 0;
+ int16 _avDisplayX = 0;
+ int16 _avDisplayY = 0;
- XPPicDesc g_avFrontBufDesc;
- XPPicDesc g_avBackBufDesc;
+ XPPicDesc _avFrontBufDesc;
+ XPPicDesc _avBackBufDesc;
// Anim
bool startAnimation(RTFResource *rtf, int16 animIndex);
@@ -888,9 +867,9 @@ protected:
bool initAnim(RTFResource *rtf, int16 animIndex);
void cleanUpAnim();
- int16 g_animPrevInactivityTimer = 0;
- byte *g_animRingBuffer = nullptr;
- Common::File *g_animFileHandle = nullptr;
+ int16 _animPrevInactivityTimer = 0;
+ byte *_animRingBuffer = nullptr;
+ Common::File *_animFileHandle = nullptr;
// SSprite
void setUpSSprite(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate, int16 velocityX, int16 velocityY);
@@ -922,18 +901,18 @@ protected:
void freezeSSprite(SSprite *sprite);
void unfreezeSSprite(SSprite *sprite);
- int16 g_spriteCollTempX = 0;
- int16 g_spriteCollTempY = 0;
- int16 g_spriteCollTempW = 0;
- int16 g_spriteCollTempH = 0;
- int16 g_spriteScreenX = 0;
- int16 g_spriteScreenY = 0;
- int16 g_spriteCollTempA[4] = { 0 };
- int16 g_spriteCollTempB[4] = { 0 };
- int16 g_spriteScreenAX = 0;
- int16 g_spriteScreenAY = 0;
- int16 g_spriteScreenBX = 0;
- int16 g_spriteScreenBY = 0;
+ int16 _spriteCollTempX = 0;
+ int16 _spriteCollTempY = 0;
+ int16 _spriteCollTempW = 0;
+ int16 _spriteCollTempH = 0;
+ int16 _spriteScreenX = 0;
+ int16 _spriteScreenY = 0;
+ int16 _spriteCollTempA[4] = { 0 };
+ int16 _spriteCollTempB[4] = { 0 };
+ int16 _spriteScreenAX = 0;
+ int16 _spriteScreenAY = 0;
+ int16 _spriteScreenBX = 0;
+ int16 _spriteScreenBY = 0;
// --- MINIGAMES ---
@@ -964,54 +943,52 @@ protected:
static void swapFredAnimDescCb();
static void swapFredLevelDescCb();
- BOLTLib *g_fredBoltLib = nullptr;
- BOLTCallbacks g_fredBoltCallbacks;
-
- static BOLTCallback g_fredTypeLoadCallbacks[28];
- static BOLTCallback g_fredTypeFreeCallbacks[28];
-
- byte *g_fredLevelPtr = nullptr;
- byte *g_fredBackground = nullptr;
- byte *g_fredBalloonString = nullptr;
- byte *g_fredPalette = nullptr;
- byte *g_fredFacingLeftRect = nullptr;
- byte *g_fredFacingRightRect = nullptr;
- byte *g_fredTurningRect = nullptr;
- byte *g_fredBalloonRect = nullptr;
- byte *g_fredHelpEntries = nullptr;
- byte *g_fredPlayButton = nullptr;
- byte *g_fredSprites[10] = { nullptr };
- FredEntityState **g_fredEntitiesTable = nullptr;
-
- byte *g_fredRowBounds = nullptr;
- byte *g_fredShuffleTable = nullptr;
- byte *g_fredCycleRaw = nullptr;
- byte *g_fredBalloonSprite = nullptr;
- byte *g_fredPathMatrix = nullptr;
- byte *g_fredCurrentHelpObject = nullptr;
- byte *g_fredHoveredEntry = nullptr;
-
- SoundInfo g_fredSounds[4];
-
- SoundInfo *g_fredCurrentSound = nullptr;
- SoundInfo *g_fredLoopSound = nullptr;
- SoundInfo *g_fredPendingOneShot = nullptr;
- SoundInfo *g_fredPendingLoop = nullptr;
-
- FredEntityState g_fredSprite;
-
- int16 g_fredSaveData[3] = { 0, 0, 0 };
- int16 g_fredLevelIndex = 0;
-
- int16 g_fredBalloonSpawnDelay = 0;
- int16 g_fredBalloonSearchIdx = 0;
- uint32 g_fredTimer = 0;
- int16 g_fredHelpStep = -1;
- int16 g_fredShowHelp = 1;
-
- XPCycleState g_fredCycleSpecs[4];
-
- const char *g_fredSaveFile = "FredBC";
+ BOLTLib *_fredBoltLib = nullptr;
+ BOLTCallbacks _fredBoltCallbacks;
+
+ static BOLTCallback _fredTypeLoadCallbacks[28];
+ static BOLTCallback _fredTypeFreeCallbacks[28];
+
+ byte *_fredLevelPtr = nullptr;
+ byte *_fredBackground = nullptr;
+ byte *_fredBalloonString = nullptr;
+ byte *_fredPalette = nullptr;
+ byte *_fredFacingLeftRect = nullptr;
+ byte *_fredFacingRightRect = nullptr;
+ byte *_fredTurningRect = nullptr;
+ byte *_fredBalloonRect = nullptr;
+ byte *_fredHelpEntries = nullptr;
+ byte *_fredPlayButton = nullptr;
+ byte *_fredSprites[10] = { nullptr };
+ FredEntityState **_fredEntitiesTable = nullptr;
+
+ byte *_fredRowBounds = nullptr;
+ byte *_fredShuffleTable = nullptr;
+ byte *_fredCycleRaw = nullptr;
+ byte *_fredBalloonSprite = nullptr;
+ byte *_fredPathMatrix = nullptr;
+ byte *_fredCurrentHelpObject = nullptr;
+ byte *_fredHoveredEntry = nullptr;
+
+ SoundInfo _fredSounds[4];
+
+ SoundInfo *_fredCurrentSound = nullptr;
+ SoundInfo *_fredLoopSound = nullptr;
+ SoundInfo *_fredPendingOneShot = nullptr;
+ SoundInfo *_fredPendingLoop = nullptr;
+
+ FredEntityState _fredSprite;
+
+ int16 _fredSaveData[3] = { 0, 0, 0 };
+ int16 _fredLevelIndex = 0;
+
+ int16 _fredBalloonSpawnDelay = 0;
+ int16 _fredBalloonSearchIdx = 0;
+ uint32 _fredTimer = 0;
+ int16 _fredHelpStep = -1;
+ int16 _fredShowHelp = 1;
+
+ XPCycleState _fredCycleSpecs[4];
// --- GEORGE ---
int16 georgeGame(int16 prevBooth);
@@ -1045,61 +1022,60 @@ protected:
static void swapGeorgeHelpEntryCb();
static void swapGeorgeThresholdsCb();
- BOLTLib *g_georgeBoltLib = nullptr;
- BOLTCallbacks g_georgeBoltCallbacks;
-
- static BOLTCallback g_georgeTypeLoadCallbacks[28];
- static BOLTCallback g_georgeTypeFreeCallbacks[28];
-
- byte *g_georgePrevActiveHelpObject = nullptr;
- byte *g_georgeActiveHelpObject = nullptr;
- int16 g_georgeHelpStep = -1;
- int16 g_georgeHelpActive = 1;
- const char *g_georgeSaveFileName = "GeorgeBE";
-
- GeorgeEntityState **g_georgeEntityList = nullptr;
- int16 g_georgeCarIdx = 0;
- int16 g_georgeNumSatellites = 0;
- int16 g_georgeFirstAsteroidIdx = 0;
- int16 g_georgeTotalSatellites = 0;
- byte *g_georgeCarPics[3] = { nullptr };
- byte *g_georgeHelpObjects = nullptr;
- byte *g_georgeHelpSequence = nullptr;
- uint32 g_georgeHelpTimer = 0;
- byte *g_georgeSatelliteShuffleTable = nullptr;
- byte *g_georgeAsteroidShuffleTable = nullptr;
- int16 *g_georgeSatelliteThresholds = nullptr;
- int16 *g_georgeAsteroidThresholds = nullptr;
- byte *g_georgeSatellitePaths = nullptr;
- byte *g_georgeAsteroidPaths = nullptr;
- byte *g_georgeCollisionRect = nullptr;
- byte *g_georgeSatelliteCollisionRects = nullptr;
- byte *g_georgeAsteroidCollisionRects = nullptr;
- byte *g_georgeSatelliteGfx = nullptr;
- byte *g_georgeAsteroidGfx = nullptr;
- int16 g_georgeSatelliteWait = 0;
- int16 g_georgeAsteroidWait = 0;
- int16 g_georgeSatelliteSearchIdx = 0;
- int16 g_georgeAsteroidSearchIdx = 0;
- int16 g_georgeHitSearchIdx = 0;
- byte *g_georgeBgPic = nullptr;
- byte *g_georgePalette = nullptr;
- byte *g_georgePalCycleRawData = nullptr;
- XPCycleState g_georgePalCycleSpecs[4];
- int16 g_georgeCollectedSatellitesNum = 0;
- SoundInfo g_georgeSoundCarStartUp;
- SoundInfo g_georgeSoundCarGoesAway;
- SoundInfo g_georgeSoundCarLoopHi;
- SoundInfo g_georgeSoundCarLoopLo;
- SoundInfo g_georgeSoundCarTumble;
- SoundInfo **g_georgeSatelliteSoundList = nullptr;
- byte g_georgeSoundChannelCounter = 0;
- SoundInfo *g_georgeSoundToPlay = nullptr;
- SoundInfo *g_georgeSoundCurrent = nullptr;
- SoundInfo *g_georgeSoundQueued = nullptr;
- SoundInfo *g_georgeSoundNext = nullptr;
- int16 g_georgeSaveData[3] = { 0 };
- int16 *g_georgeThresholds = nullptr;
+ BOLTLib *_georgeBoltLib = nullptr;
+ BOLTCallbacks _georgeBoltCallbacks;
+
+ static BOLTCallback _georgeTypeLoadCallbacks[28];
+ static BOLTCallback _georgeTypeFreeCallbacks[28];
+
+ byte *_georgePrevActiveHelpObject = nullptr;
+ byte *_georgeActiveHelpObject = nullptr;
+ int16 _georgeHelpStep = -1;
+ int16 _georgeHelpActive = 1;
+
+ GeorgeEntityState **_georgeEntityList = nullptr;
+ int16 _georgeCarIdx = 0;
+ int16 _georgeNumSatellites = 0;
+ int16 _georgeFirstAsteroidIdx = 0;
+ int16 _georgeTotalSatellites = 0;
+ byte *_georgeCarPics[3] = { nullptr };
+ byte *_georgeHelpObjects = nullptr;
+ byte *_georgeHelpSequence = nullptr;
+ uint32 _georgeHelpTimer = 0;
+ byte *_georgeSatelliteShuffleTable = nullptr;
+ byte *_georgeAsteroidShuffleTable = nullptr;
+ int16 *_georgeSatelliteThresholds = nullptr;
+ int16 *_georgeAsteroidThresholds = nullptr;
+ byte *_georgeSatellitePaths = nullptr;
+ byte *_georgeAsteroidPaths = nullptr;
+ byte *_georgeCollisionRect = nullptr;
+ byte *_georgeSatelliteCollisionRects = nullptr;
+ byte *_georgeAsteroidCollisionRects = nullptr;
+ byte *_georgeSatelliteGfx = nullptr;
+ byte *_georgeAsteroidGfx = nullptr;
+ int16 _georgeSatelliteWait = 0;
+ int16 _georgeAsteroidWait = 0;
+ int16 _georgeSatelliteSearchIdx = 0;
+ int16 _georgeAsteroidSearchIdx = 0;
+ int16 _georgeHitSearchIdx = 0;
+ byte *_georgeBgPic = nullptr;
+ byte *_georgePalette = nullptr;
+ byte *_georgePalCycleRawData = nullptr;
+ XPCycleState _georgePalCycleSpecs[4];
+ int16 _georgeCollectedSatellitesNum = 0;
+ SoundInfo _georgeSoundCarStartUp;
+ SoundInfo _georgeSoundCarGoesAway;
+ SoundInfo _georgeSoundCarLoopHi;
+ SoundInfo _georgeSoundCarLoopLo;
+ SoundInfo _georgeSoundCarTumble;
+ SoundInfo **_georgeSatelliteSoundList = nullptr;
+ byte _georgeSoundChannelCounter = 0;
+ SoundInfo *_georgeSoundToPlay = nullptr;
+ SoundInfo *_georgeSoundCurrent = nullptr;
+ SoundInfo *_georgeSoundQueued = nullptr;
+ SoundInfo *_georgeSoundNext = nullptr;
+ int16 _georgeSaveData[3] = { 0 };
+ int16 *_georgeThresholds = nullptr;
// --- HUCK ---
void playSoundMapHuck(int16 memberId);
@@ -1133,38 +1109,38 @@ protected:
static void swapHuckWordArrayCb();
static void swapHuckWordsCb();
- BOLTLib *g_huckBoltLib = nullptr;
- BOLTCallbacks g_huckBoltCallbacks;
-
- static BOLTCallback g_huckTypeLoadCallbacks[27];
- static BOLTCallback g_huckTypeFreeCallbacks[27];
-
- int16 g_huckSoundPlaying = 0;
- int16 g_huckHotSpotCount = 0;
- int16 g_huckActionState = 0;
- uint32 g_huckScreensaverTimer = 0;
- uint32 g_huckBlinkTimer = 0;
- int16 g_huckScreensaverFlag = 0;
- int16 g_huckBlinkFlag = 0;
- uint32 g_huckShuffleTimer = 0;
- int16 g_huckGlobal[30] = { 0 };
- HuckState g_huckState;
- byte *g_huckGiftPic = nullptr;
- byte *g_huckBgPic = nullptr;
- int16 g_huckGiftGroupId = 0;
- int16 g_huckVariantGroupId = 0;
- byte *g_huckBgDisplayPic = nullptr;
- int16 g_huckScrollOffset = 0;
- int16 g_huckPalRange[8] = { 0 };
- int16 g_huckReturnBooth = 0;
- int16 g_huckExitFlag = 0;
- int16 g_huckCursorX = 0;
- int16 g_huckCursorY = 0;
- byte g_huckPalSave0[15] = { 0 };
- byte g_huckPalHighlight0[15] = { 0 };
- byte g_huckPalSave1[15] = { 0 };
- byte g_huckPalHighlight1[15] = { 0 };
- XPPicDesc g_huckScratchPic;
+ BOLTLib *_huckBoltLib = nullptr;
+ BOLTCallbacks _huckBoltCallbacks;
+
+ static BOLTCallback _huckTypeLoadCallbacks[27];
+ static BOLTCallback _huckTypeFreeCallbacks[27];
+
+ int16 _huckSoundPlaying = 0;
+ int16 _huckHotSpotCount = 0;
+ int16 _huckActionState = 0;
+ uint32 _huckScreensaverTimer = 0;
+ uint32 _huckBlinkTimer = 0;
+ int16 _huckScreensaverFlag = 0;
+ int16 _huckBlinkFlag = 0;
+ uint32 _huckShuffleTimer = 0;
+ int16 _huckGlobal[30] = { 0 };
+ HuckState _huckState;
+ byte *_huckGiftPic = nullptr;
+ byte *_huckBgPic = nullptr;
+ int16 _huckGiftGroupId = 0;
+ int16 _huckVariantGroupId = 0;
+ byte *_huckBgDisplayPic = nullptr;
+ int16 _huckScrollOffset = 0;
+ int16 _huckPalRange[8] = { 0 };
+ int16 _huckReturnBooth = 0;
+ int16 _huckExitFlag = 0;
+ int16 _huckCursorX = 0;
+ int16 _huckCursorY = 0;
+ byte _huckPalSave0[15] = { 0 };
+ byte _huckPalHighlight0[15] = { 0 };
+ byte _huckPalSave1[15] = { 0 };
+ byte _huckPalHighlight1[15] = { 0 };
+ XPPicDesc _huckScratchPic;
// --- SCOOBY ---
bool loadScoobyBaseAssets();
@@ -1203,43 +1179,43 @@ protected:
static void swapScoobyHelpEntryCb();
static void swapScoobyWordArrayCb();
- BOLTLib *g_scoobyBoltLib = nullptr;
- BOLTCallbacks g_scoobyBoltCallbacks;
-
- static BOLTCallback g_scoobyTypeLoadCallbacks[28];
- static BOLTCallback g_scoobyTypeFreeCallbacks[28];
-
- int16 g_scoobySoundMode = 0;
- int16 g_scoobySoundPlaying = 0;
- int16 g_scoobyShowHelp = 1;
-
- int16 g_scoobyLastInputDir = 0;
- int16 g_scoobyTransitionFrom = 0;
- int16 g_scoobyTransitionTarget = 0;
- int16 g_scoobyTransitionVelX = 0;
- int16 g_scoobyTransitionVelY = 0;
- XPPicDesc g_scoobyTempPic;
- byte g_scoobyGlobalSaveData[0x3C] = { 0 };
- ScoobyState g_scoobyGameState;
- SSprite g_scoobySprite;
- byte *g_scoobyBaseData = nullptr;
- byte *g_scoobyBgPic = nullptr;
- byte *g_scoobyWallPicsA[4] = { nullptr };
- byte *g_scoobyWallPicsB[5] = { nullptr };
- ScoobyRect g_scoobyCellBounds[25];
- Common::Point g_scoobyLevelStartXY[25];
- byte *g_scoobyLevelData = nullptr;
- int16 g_scoobySelectedGraphicsGroup = 0;
- int16 g_scoobyDifficulty = 0;
- int16 g_scoobyLevelCount = 0;
- int16 g_scoobyMoveRequested = 0;
- int16 g_scoobyTransitioning = 0;
- int16 g_scoobyDesiredDir = 0;
- int16 g_scoobyInputHoldCount = 0;
- int16 g_scoobyWallAnimating = 0;
- int16 g_scoobyWallAnimStep = 0;
- int16 g_scoobyWallsToOpen = 0;
- int16 g_scoobyWallsToClose = 0;
+ BOLTLib *_scoobyBoltLib = nullptr;
+ BOLTCallbacks _scoobyBoltCallbacks;
+
+ static BOLTCallback _scoobyTypeLoadCallbacks[28];
+ static BOLTCallback _scoobyTypeFreeCallbacks[28];
+
+ int16 _scoobySoundMode = 0;
+ int16 _scoobySoundPlaying = 0;
+ int16 _scoobyShowHelp = 1;
+
+ int16 _scoobyLastInputDir = 0;
+ int16 _scoobyTransitionFrom = 0;
+ int16 _scoobyTransitionTarget = 0;
+ int16 _scoobyTransitionVelX = 0;
+ int16 _scoobyTransitionVelY = 0;
+ XPPicDesc _scoobyTempPic;
+ byte _scoobyGlobalSaveData[0x3C] = { 0 };
+ ScoobyState _scoobyGameState;
+ SSprite _scoobySprite;
+ byte *_scoobyBaseData = nullptr;
+ byte *_scoobyBgPic = nullptr;
+ byte *_scoobyWallPicsA[4] = { nullptr };
+ byte *_scoobyWallPicsB[5] = { nullptr };
+ ScoobyRect _scoobyCellBounds[25];
+ Common::Point _scoobyLevelStartXY[25];
+ byte *_scoobyLevelData = nullptr;
+ int16 _scoobySelectedGraphicsGroup = 0;
+ int16 _scoobyDifficulty = 0;
+ int16 _scoobyLevelCount = 0;
+ int16 _scoobyMoveRequested = 0;
+ int16 _scoobyTransitioning = 0;
+ int16 _scoobyDesiredDir = 0;
+ int16 _scoobyInputHoldCount = 0;
+ int16 _scoobyWallAnimating = 0;
+ int16 _scoobyWallAnimStep = 0;
+ int16 _scoobyWallsToOpen = 0;
+ int16 _scoobyWallsToClose = 0;
// --- TOPCAT ---
int16 topCatGame(int16 prevBooth);
@@ -1263,71 +1239,69 @@ protected:
static void swapTopCatHelpEntryCb();
- RTFResource *g_topCatRtfHandle = nullptr;
- RTFResource *g_topCatAvHandle = nullptr;
- BOLTLib *g_topCatBoltLib = nullptr;
- BOLTCallbacks g_topCatBoltCallbacks;
-
- static BOLTCallback g_topCatTypeLoadCallbacks[26];
- static BOLTCallback g_topCatTypeFreeCallbacks[26];
-
- byte *g_topCatBackgroundPalette = nullptr;
- byte *g_topCatBackground = nullptr;
- byte *g_topCatBackgroundAnimationPalette = nullptr;
- byte *g_topCatGraphicsAssets = nullptr;
-
- int16 g_topCatBackgroundAnimFrame = 0;
- int16 g_topCatMaxBackgroundAnimFrames = 0;
- int16 g_topCatCurrentAnimType = 0;
- int16 g_topCatAnimStateMachineStep = 0;
- int16 g_topCatAnimQueueSize = 0;
- int16 g_topCatQueuedSoundFrames = 0;
- byte *g_topCatButtonsPalette = nullptr;
- byte *g_topCatBlinkEntries = nullptr;
- byte *g_topCatLightsPalette = nullptr;
- int16 *g_topCatBlinkSeqPtr = nullptr;
- byte *g_topCatSelectedChoiceOverlayGfx = nullptr;
- byte *g_topCatCycleData = nullptr;
- XPCycleState g_topCatCycleSpecs[4];
- byte *g_topCatShuffledQuestions = nullptr;
- byte *g_topCatShuffledAnswers = nullptr;
- byte *g_topCatAnswersPermutations = nullptr;
- byte *g_topCatAnswers = nullptr;
- byte *g_topCatAnswersScreenPositions = nullptr;
- int16 g_topCatSavedScore = 0;
- int16 g_topCatSaveHistory = 0;
- const char *g_topCatSaveFileName = "TopCatBF";
- const char *g_topCatStaticSaveFileName = "TopCatBFStatic";
- int16 g_topCatScore = 0;
- int16 g_topCatShuffledQuestionsArrayIdx = 0;
- uint32 g_topCatBlinkTimer = 0;
- SoundInfo g_topCatSoundInfo;
- byte g_topCatSavedShuffledQuestions[60] = { 0 };
- byte g_topCatSavedShuffledAnswers[60] = { 0 };
- byte g_topCatSavedAnswersPermutations[60 * 3] = { 0 };
- byte g_topCatSaveBuffer[302] = { 0 };
- byte *g_topCatHoveredEntry = nullptr;
- byte *g_topCatHelpButton = nullptr;
- byte *g_topCatBackButton = nullptr;
- byte *g_topCatBlinkEntry = nullptr;
- int16 g_topCatCycleStep = 0;
- int16 g_topCatDrawnQuestionId = 0;
- int16 g_topCatCurrentAnswerIdx = 0;
- XPCycleState g_topCatChoiceCycleState[4];
- TopCatAnim g_topCatAnimQueue[3];
- int16 g_topCatCorrectAnimIdx = 0;
- int16 g_topCatWrongAnimIdx = 0;
- int16 g_topCatShouldPlayAnswerAnim = 0;
- int16 g_topCatMaintainSoundFlag = 0;
- int16 g_topCatPermTableA[3] = { 0, 1, 2 };
- int16 g_topCatPermTableB[3] = { 0, 1, 2 };
- int16 g_topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
- int16 g_topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
- int16 g_topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
- int16 g_topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
- int16 g_topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
- int16 g_topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
- int16 g_topCatBlinkSeq6[25] = {
+ RTFResource *_topCatRtfHandle = nullptr;
+ RTFResource *_topCatAvHandle = nullptr;
+ BOLTLib *_topCatBoltLib = nullptr;
+ BOLTCallbacks _topCatBoltCallbacks;
+
+ static BOLTCallback _topCatTypeLoadCallbacks[26];
+ static BOLTCallback _topCatTypeFreeCallbacks[26];
+
+ byte *_topCatBackgroundPalette = nullptr;
+ byte *_topCatBackground = nullptr;
+ byte *_topCatBackgroundAnimationPalette = nullptr;
+ byte *_topCatGraphicsAssets = nullptr;
+
+ int16 _topCatBackgroundAnimFrame = 0;
+ int16 _topCatMaxBackgroundAnimFrames = 0;
+ int16 _topCatCurrentAnimType = 0;
+ int16 _topCatAnimStateMachineStep = 0;
+ int16 _topCatAnimQueueSize = 0;
+ int16 _topCatQueuedSoundFrames = 0;
+ byte *_topCatButtonsPalette = nullptr;
+ byte *_topCatBlinkEntries = nullptr;
+ byte *_topCatLightsPalette = nullptr;
+ int16 *_topCatBlinkSeqPtr = nullptr;
+ byte *_topCatSelectedChoiceOverlayGfx = nullptr;
+ byte *_topCatCycleData = nullptr;
+ XPCycleState _topCatCycleSpecs[4];
+ byte *_topCatShuffledQuestions = nullptr;
+ byte *_topCatShuffledAnswers = nullptr;
+ byte *_topCatAnswersPermutations = nullptr;
+ byte *_topCatAnswers = nullptr;
+ byte *_topCatAnswersScreenPositions = nullptr;
+ int16 _topCatSavedScore = 0;
+ int16 _topCatSaveHistory = 0;
+ int16 _topCatScore = 0;
+ int16 _topCatShuffledQuestionsArrayIdx = 0;
+ uint32 _topCatBlinkTimer = 0;
+ SoundInfo _topCatSoundInfo;
+ byte _topCatSavedShuffledQuestions[60] = { 0 };
+ byte _topCatSavedShuffledAnswers[60] = { 0 };
+ byte _topCatSavedAnswersPermutations[60 * 3] = { 0 };
+ byte _topCatSaveBuffer[302] = { 0 };
+ byte *_topCatHoveredEntry = nullptr;
+ byte *_topCatHelpButton = nullptr;
+ byte *_topCatBackButton = nullptr;
+ byte *_topCatBlinkEntry = nullptr;
+ int16 _topCatCycleStep = 0;
+ int16 _topCatDrawnQuestionId = 0;
+ int16 _topCatCurrentAnswerIdx = 0;
+ XPCycleState _topCatChoiceCycleState[4];
+ TopCatAnim _topCatAnimQueue[3];
+ int16 _topCatCorrectAnimIdx = 0;
+ int16 _topCatWrongAnimIdx = 0;
+ int16 _topCatShouldPlayAnswerAnim = 0;
+ int16 _topCatMaintainSoundFlag = 0;
+ int16 _topCatPermTableA[3] = { 0, 1, 2 };
+ int16 _topCatPermTableB[3] = { 0, 1, 2 };
+ int16 _topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
+ int16 _topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
+ int16 _topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
+ int16 _topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
+ int16 _topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
+ int16 _topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
+ int16 _topCatBlinkSeq6[25] = {
0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
@@ -1335,10 +1309,10 @@ protected:
0x3F
};
- int16 *g_topCatBlinkSeqTable[7] = {
- g_topCatBlinkSeq0, g_topCatBlinkSeq1, g_topCatBlinkSeq2,
- g_topCatBlinkSeq3, g_topCatBlinkSeq4, g_topCatBlinkSeq5,
- g_topCatBlinkSeq6
+ int16 *_topCatBlinkSeqTable[7] = {
+ _topCatBlinkSeq0, _topCatBlinkSeq1, _topCatBlinkSeq2,
+ _topCatBlinkSeq3, _topCatBlinkSeq4, _topCatBlinkSeq5,
+ _topCatBlinkSeq6
};
// --- YOGI ---
@@ -1375,41 +1349,41 @@ protected:
static void swapYogiAllWordsCb();
static void swapYogiFirstWordCb();
- BOLTLib *g_yogiBoltLib = nullptr;
- BOLTCallbacks g_yogiBoltCallbacks;
-
- static BOLTCallback g_yogiTypeLoadCallbacks[27];
- static BOLTCallback g_yogiTypeFreeCallbacks[27];
-
- int16 g_yogiSoundPlaying = 0;
- int16 g_yogiSoundActive = 0;
- int16 g_yogiHotSpotCount = 0;
- int16 g_yogiAnimActive = 0;
- uint32 g_yogiBlinkTimer1 = 0;
- uint32 g_yogiBlinkTimer2 = 0;
- int16 g_yogiBlinkState1 = 0;
- int16 g_yogiBlinkState2 = 0;
- int16 g_yogiGlobal[0xA0] = { 0 };
- YogiState g_yogiState;
- byte *g_yogiBasketPic = nullptr;
- int16 g_yogiLevelGroupId = 0;
- byte *g_yogiBgPic = nullptr;
- byte *g_yogiNormalSprite = nullptr;
- byte *g_yogiHlSprite = nullptr;
- byte *g_yogiAnimSprite = nullptr;
- int16 g_yogiSpriteStride = 0;
- int16 g_yogiSpriteHeight = 0;
- int16 g_yogiPalRange[8] = { 0 };
- int16 g_yogiReturnBooth = 0;
- int16 g_yogiExitFlag = 0;
- int16 g_yogiLevelId = 0;
- int16 g_yogiCursorX = 0;
- int16 g_yogiCursorY = 0;
- byte g_yogiPalSave0[15] = { 0 };
- byte g_yogiPalHighlight0[15] = { 0 };
- byte g_yogiPalSave1[15] = { 0 };
- byte g_yogiPalHighlight1[15] = { 0 };
- XPPicDesc g_yogiScratchBuf;
+ BOLTLib *_yogiBoltLib = nullptr;
+ BOLTCallbacks _yogiBoltCallbacks;
+
+ static BOLTCallback _yogiTypeLoadCallbacks[27];
+ static BOLTCallback _yogiTypeFreeCallbacks[27];
+
+ int16 _yogiSoundPlaying = 0;
+ int16 _yogiSoundActive = 0;
+ int16 _yogiHotSpotCount = 0;
+ int16 _yogiAnimActive = 0;
+ uint32 _yogiBlinkTimer1 = 0;
+ uint32 _yogiBlinkTimer2 = 0;
+ int16 _yogiBlinkState1 = 0;
+ int16 _yogiBlinkState2 = 0;
+ int16 _yogiGlobal[0xA0] = { 0 };
+ YogiState _yogiState;
+ byte *_yogiBasketPic = nullptr;
+ int16 _yogiLevelGroupId = 0;
+ byte *_yogiBgPic = nullptr;
+ byte *_yogiNormalSprite = nullptr;
+ byte *_yogiHlSprite = nullptr;
+ byte *_yogiAnimSprite = nullptr;
+ int16 _yogiSpriteStride = 0;
+ int16 _yogiSpriteHeight = 0;
+ int16 _yogiPalRange[8] = { 0 };
+ int16 _yogiReturnBooth = 0;
+ int16 _yogiExitFlag = 0;
+ int16 _yogiLevelId = 0;
+ int16 _yogiCursorX = 0;
+ int16 _yogiCursorY = 0;
+ byte _yogiPalSave0[15] = { 0 };
+ byte _yogiPalHighlight0[15] = { 0 };
+ byte _yogiPalSave1[15] = { 0 };
+ byte _yogiPalHighlight1[15] = { 0 };
+ XPPicDesc _yogiScratchBuf;
};
extern BoltEngine *g_engine;
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index a8f26fd9ef0..92ce101e054 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -34,8 +34,8 @@ void BoltEngine::startCycle(byte *cycleResource) {
void BoltEngine::displayBooth(int16 page) {
_xp->setTransparency(page);
- _xp->displayPic(&g_boothLetterSprite, g_displayX, g_displayY, page);
- displayColors(g_boothPalCycleData, page, 0);
+ _xp->displayPic(&_boothLetterSprite, _displayX, _displayY, page);
+ displayColors(_boothPalCycleData, page, 0);
if (page != stFront)
_xp->fillDisplay(0, stFront);
@@ -46,7 +46,7 @@ void BoltEngine::displayBooth(int16 page) {
void BoltEngine::playAVOverBooth(int16 animIndex) {
_xp->hideCursor();
displayBooth(stBack);
- playAV(g_rtfHandle, animIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ playAV(_rtfHandle, animIndex, _displayWidth, _displayHeight, _displayX, _displayY);
displayBooth(stFront);
_xp->showCursor();
}
@@ -65,7 +65,7 @@ int16 BoltEngine::hucksBooth(int16 prevBooth) {
case 2:
return 10; // default (play game)
case 3:
- return g_isDemo ? 17 : 10; // play game
+ return _isDemo ? 17 : 10; // play game
default:
return 10;
}
@@ -85,7 +85,7 @@ int16 BoltEngine::fredsBooth(int16 prevBooth) {
case 2:
return 11;
case 3:
- return g_isDemo ? 17 : 11;
+ return _isDemo ? 17 : 11;
default:
return 11;
}
@@ -105,7 +105,7 @@ int16 BoltEngine::scoobysBooth(int16 prevBooth) {
case 2:
return 12;
case 3:
- if (g_isDemo && g_scoobyWins >= 2)
+ if (_isDemo && _scoobyWins >= 2)
return 17;
return 12;
@@ -128,7 +128,7 @@ int16 BoltEngine::yogisBooth(int16 prevBooth) {
case 2:
return 13;
case 3:
- if (g_isDemo && g_yogiWins >= 2)
+ if (_isDemo && _yogiWins >= 2)
return 17;
return 13;
@@ -151,7 +151,7 @@ int16 BoltEngine::georgesBooth(int16 prevBooth) {
case 2:
return 14;
case 3:
- if (g_isDemo && g_georgeWins >= 2)
+ if (_isDemo && _georgeWins >= 2)
return 17;
return 14;
@@ -174,7 +174,7 @@ int16 BoltEngine::topCatsBooth(int16 prevBooth) {
case 2:
return 15;
case 3:
- return g_isDemo ? 17 : 15;
+ return _isDemo ? 17 : 15;
default:
return 15;
}
@@ -186,7 +186,7 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
switch (result) {
case -1:
- return g_isDemo ? 18 : 0; // exit
+ return _isDemo ? 18 : 0; // exit
case 0:
return 8; // left (TopCat)
case 1:
@@ -198,9 +198,9 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
case 4:
return 9; // self
case 5:
- return g_isDemo ? 18 : 0; // exit
+ return _isDemo ? 18 : 0; // exit
case 6:
- return g_isDemo ? 18 : 9; // Case only available in the demo
+ return _isDemo ? 18 : 9; // Case only available in the demo
default:
return 9;
}
@@ -209,61 +209,61 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
bool BoltEngine::loadBooth(int16 boothId) {
switch (boothId) {
case 3:
- if (g_boothLoadedMask & 0x01)
+ if (_boothLoadedMask & 0x01)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x100, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x100, 1))
return false;
- g_boothLoadedMask |= 0x01;
+ _boothLoadedMask |= 0x01;
break;
case 4:
- if (g_boothLoadedMask & 0x02)
+ if (_boothLoadedMask & 0x02)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x200, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x200, 1))
return false;
- g_boothLoadedMask |= 0x02;
+ _boothLoadedMask |= 0x02;
break;
case 5:
- if (g_boothLoadedMask & 0x04)
+ if (_boothLoadedMask & 0x04)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x300, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x300, 1))
return false;
- g_boothLoadedMask |= 0x04;
+ _boothLoadedMask |= 0x04;
break;
case 6:
- if (g_boothLoadedMask & 0x08)
+ if (_boothLoadedMask & 0x08)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x400, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x400, 1))
return false;
- g_boothLoadedMask |= 0x08;
+ _boothLoadedMask |= 0x08;
break;
case 7:
- if (g_boothLoadedMask & 0x10)
+ if (_boothLoadedMask & 0x10)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x500, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x500, 1))
return false;
- g_boothLoadedMask |= 0x10;
+ _boothLoadedMask |= 0x10;
break;
case 8:
- if (g_boothLoadedMask & 0x20)
+ if (_boothLoadedMask & 0x20)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x600, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x600, 1))
return false;
- g_boothLoadedMask |= 0x20;
+ _boothLoadedMask |= 0x20;
break;
case 9:
- if (g_boothLoadedMask & 0x40)
+ if (_boothLoadedMask & 0x40)
break;
- if (!getBOLTGroup(g_boothsBoltLib, 0x700, 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x700, 1))
return false;
- if (!getBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1))
+ if (!getBOLTGroup(_boothsBoltLib, 0x800 + (_lettersWon << 8), 1))
return false;
- g_boothLoadedMask |= 0x40;
+ _boothLoadedMask |= 0x40;
break;
}
@@ -271,37 +271,37 @@ bool BoltEngine::loadBooth(int16 boothId) {
}
void BoltEngine::unloadBooth() {
- if (g_boothLoadedMask & 0x01)
- freeBOLTGroup(g_boothsBoltLib, 0x100, 1);
+ if (_boothLoadedMask & 0x01)
+ freeBOLTGroup(_boothsBoltLib, 0x100, 1);
- if (g_boothLoadedMask & 0x02)
- freeBOLTGroup(g_boothsBoltLib, 0x200, 1);
+ if (_boothLoadedMask & 0x02)
+ freeBOLTGroup(_boothsBoltLib, 0x200, 1);
- if (g_boothLoadedMask & 0x04)
- freeBOLTGroup(g_boothsBoltLib, 0x300, 1);
+ if (_boothLoadedMask & 0x04)
+ freeBOLTGroup(_boothsBoltLib, 0x300, 1);
- if (g_boothLoadedMask & 0x08)
- freeBOLTGroup(g_boothsBoltLib, 0x400, 1);
+ if (_boothLoadedMask & 0x08)
+ freeBOLTGroup(_boothsBoltLib, 0x400, 1);
- if (g_boothLoadedMask & 0x10)
- freeBOLTGroup(g_boothsBoltLib, 0x500, 1);
+ if (_boothLoadedMask & 0x10)
+ freeBOLTGroup(_boothsBoltLib, 0x500, 1);
- if (g_boothLoadedMask & 0x20)
- freeBOLTGroup(g_boothsBoltLib, 0x600, 1);
+ if (_boothLoadedMask & 0x20)
+ freeBOLTGroup(_boothsBoltLib, 0x600, 1);
- if (g_boothLoadedMask & 0x40) {
- freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
- freeBOLTGroup(g_boothsBoltLib, 0x700, 1);
+ if (_boothLoadedMask & 0x40) {
+ freeBOLTGroup(_boothsBoltLib, 0x800 + (_lettersWon << 8), 1);
+ freeBOLTGroup(_boothsBoltLib, 0x700, 1);
}
- g_boothLoadedMask = 0;
+ _boothLoadedMask = 0;
}
int16 BoltEngine::openBooth(int16 boothId) {
int16 baseResId;
int16 resId;
- g_currentBoothScene = boothId;
+ _currentBoothScene = boothId;
if (!loadBooth(boothId))
return -1;
@@ -310,63 +310,63 @@ int16 BoltEngine::openBooth(int16 boothId) {
// Main entrance has more hotspots/animations than regular booths
if (boothId == 9) {
- g_boothNumHotspots = 8;
- g_boothNumAnimations = 7;
+ _boothNumHotspots = 8;
+ _boothNumAnimations = 7;
} else {
- g_boothNumHotspots = 5;
- g_boothNumAnimations = 4;
+ _boothNumHotspots = 5;
+ _boothNumAnimations = 4;
}
// Background scene descriptor
- g_boothSceneDesc = memberAddr(g_boothsBoltLib, baseResId + 0x100);
+ _boothSceneDesc = memberAddr(_boothsBoltLib, baseResId + 0x100);
// Load 4 palette descriptors (at DS:0x13AC, members +0x105..+0x108)
resId = baseResId + 0x105;
for (int16 i = 0; i < 4; i++) {
- g_boothHotPalDescs[i] = memberAddr(g_boothsBoltLib, resId);
+ _boothHotPalDescs[i] = memberAddr(_boothsBoltLib, resId);
resId++;
}
// Load animation descriptors
if (boothId == 9) {
- for (int16 i = 0; i < g_boothNumAnimations; i++)
- g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, 0x709 + i);
+ for (int16 i = 0; i < _boothNumAnimations; i++)
+ _boothAnimDescs[i] = memberAddr(_boothsBoltLib, 0x709 + i);
} else {
resId = baseResId + 0x109;
- for (int16 i = 0; i < g_boothNumAnimations; i++) {
- g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, resId);
+ for (int16 i = 0; i < _boothNumAnimations; i++) {
+ _boothAnimDescs[i] = memberAddr(_boothsBoltLib, resId);
resId++;
}
}
// Load hotspot descriptors
if (boothId == 9) {
- for (int16 i = 0; i < g_boothNumHotspots; i++)
- g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, 0x710 + i));
+ for (int16 i = 0; i < _boothNumHotspots; i++)
+ _boothHotspotDescs[i] = memberToRect(memberAddr(_boothsBoltLib, 0x710 + i));
} else {
resId = baseResId + 0x10D;
- for (int16 i = 0; i < g_boothNumHotspots; i++) {
- g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, resId));
+ for (int16 i = 0; i < _boothNumHotspots; i++) {
+ _boothHotspotDescs[i] = memberToRect(memberAddr(_boothsBoltLib, resId));
resId++;
}
}
if (boothId == 9) {
// Main entrance palette cycling data
- g_boothPalCycleData = memberAddr(g_boothsBoltLib, 0x718);
+ _boothPalCycleData = memberAddr(_boothsBoltLib, 0x718);
// Letter marquee sprite (display mode selects variant)
- byte *sprite = memberAddr(g_boothsBoltLib, (g_displayMode != 0 ? 0x801 : 0x800) + (g_lettersWon << 8));
- boltPict2Pict(&g_boothLetterSprite, sprite);
+ byte *sprite = memberAddr(_boothsBoltLib, (_displayMode != 0 ? 0x801 : 0x800) + (_lettersWon << 8));
+ boltPict2Pict(&_boothLetterSprite, sprite);
// Visit sign sprite and its off variant
- g_boothVisitSignOn = memberAddr(g_boothsBoltLib, 0x747);
- g_boothVisitSignOff = memberAddr(g_boothsBoltLib, 0x748);
+ _boothVisitSignOn = memberAddr(_boothsBoltLib, 0x747);
+ _boothVisitSignOff = memberAddr(_boothsBoltLib, 0x748);
} else {
- g_boothPalCycleData = memberAddr(g_boothsBoltLib, baseResId + 0x112);
+ _boothPalCycleData = memberAddr(_boothsBoltLib, baseResId + 0x112);
- byte *sprite = memberAddr(g_boothsBoltLib, baseResId + (g_displayMode != 0 ? 0x114 : 0x113));
- boltPict2Pict(&g_boothLetterSprite, sprite);
+ byte *sprite = memberAddr(_boothsBoltLib, baseResId + (_displayMode != 0 ? 0x114 : 0x113));
+ boltPict2Pict(&_boothLetterSprite, sprite);
}
displayBooth(stFront);
@@ -374,19 +374,19 @@ int16 BoltEngine::openBooth(int16 boothId) {
_xp->setCursorColor(255, 255, 255);
// First-time cursor position from resource data
- if (g_needInitCursorPos) {
- g_cursorX = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 1));
- g_cursorY = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 2));
- g_needInitCursorPos = false;
+ if (_needInitCursorPos) {
+ _cursorX = (int16)READ_UINT16(memberAddr(_boothsBoltLib, 1));
+ _cursorY = (int16)READ_UINT16(memberAddr(_boothsBoltLib, 2));
+ _needInitCursorPos = false;
}
- _xp->setCursorPos(g_cursorX, g_cursorY);
+ _xp->setCursorPos(_cursorX, _cursorY);
flushInput();
- if (g_isDemo && boothId == 9) {
- g_helpFlag = 1;
- g_helpTimer = _xp->startTimer(500);
+ if (_isDemo && boothId == 9) {
+ _helpFlag = 1;
+ _helpTimer = _xp->startTimer(500);
}
return boothEventLoop();
@@ -395,7 +395,7 @@ int16 BoltEngine::openBooth(int16 boothId) {
void BoltEngine::closeBooth() {
uint16 buttonState;
- _xp->readCursor(&buttonState, &g_cursorY, &g_cursorX);
+ _xp->readCursor(&buttonState, &_cursorY, &_cursorX);
flushInput();
_xp->fillDisplay(0, 0);
unloadBooth();
@@ -405,10 +405,10 @@ void BoltEngine::playTour() {
int16 playing = 1;
_xp->hideCursor();
- g_tourStep = 0;
+ _tourStep = 0;
- if (prepareAV(g_rtfHandle, 4 - (g_isDemo ? 3 : 0), g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
- while (playing) {
+ if (prepareAV(_rtfHandle, 4 - (_isDemo ? 3 : 0), _displayWidth, _displayHeight, _displayX, _displayY)) {
+ while (playing && !shouldQuit()) {
playing = maintainAV(0);
if (!playing)
break;
@@ -432,6 +432,9 @@ void BoltEngine::playTour() {
}
}
+ if (shouldQuit())
+ stopAV();
+
_xp->stopCycle();
displayBooth(stFront);
}
@@ -440,10 +443,10 @@ void BoltEngine::playTour() {
}
void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
- g_helpPlaying = 0;
+ _helpPlaying = 0;
- if (g_helpIsIdle != 0) {
- g_helpIsIdle = 0;
+ if (_helpIsIdle != 0) {
+ _helpIsIdle = 0;
} else {
resetInactivityState();
}
@@ -454,12 +457,12 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
if (activeHotspot != 1)
restoreColors(1);
- if (activeHotspot != 2 && g_helpFlag == 0)
+ if (activeHotspot != 2 && _helpFlag == 0)
restoreColors(2);
- g_keyLeft = 0;
+ _keyLeft = 0;
- if (g_currentBoothScene == 9) {
+ if (_currentBoothScene == 9) {
if (activeHotspot != 7)
restoreColors(7);
@@ -469,14 +472,14 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
if (activeHotspot != 6)
restoreColors(6);
- g_keyRight = 0;
- g_keyUp = 0;
- g_keyDown = 0;
+ _keyRight = 0;
+ _keyUp = 0;
+ _keyDown = 0;
} else {
if (activeHotspot != 3)
restoreColors(3);
- g_keyEnter = 0;
+ _keyEnter = 0;
}
}
@@ -484,25 +487,25 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
switch (hotspot) {
case 0:
case 1:
- return g_keyLeft;
+ return _keyLeft;
case 2:
- return (g_helpPlaying || g_helpFlag) ? 1 : 0;
+ return (_helpPlaying || _helpFlag) ? 1 : 0;
case 3:
- return g_keyEnter;
+ return _keyEnter;
case 4:
return 0;
case 5:
- return g_keyDown;
+ return _keyDown;
case 6:
- return g_keyUp;
+ return _keyUp;
case 7:
- return g_keyRight;
+ return _keyRight;
default:
return 0;
@@ -512,26 +515,26 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
void BoltEngine::hoverHotSpot() {
int16 i = 0;
- while (i < g_boothNumHotspots) {
- if (g_boothHotspotDescs[i].contains(g_cursorX, g_cursorY))
+ while (i < _boothNumHotspots) {
+ if (_boothHotspotDescs[i].contains(_cursorX, _cursorY))
break;
i++;
}
- if (i == g_hoveredHotspot)
+ if (i == _hoveredHotspot)
return;
- if (g_hoveredHotspot < g_boothNumHotspots) {
- if (!hotSpotActive(g_hoveredHotspot))
- restoreColors(g_hoveredHotspot);
+ if (_hoveredHotspot < _boothNumHotspots) {
+ if (!hotSpotActive(_hoveredHotspot))
+ restoreColors(_hoveredHotspot);
}
- g_hoveredHotspot = i;
+ _hoveredHotspot = i;
- if (i < g_boothNumHotspots) {
+ if (i < _boothNumHotspots) {
if (!hotSpotActive(i))
- setColors(g_hoveredHotspot);
+ setColors(_hoveredHotspot);
}
}
@@ -540,45 +543,45 @@ int16 BoltEngine::boothEventLoop() {
int16 exitCode = 0;
_xp->showCursor();
- g_hoveredHotspot = -1;
+ _hoveredHotspot = -1;
hoverHotSpot();
_xp->setInactivityTimer(30);
while (exitCode == 0 && !shouldQuit()) {
- if (g_helpPlaying != 0) {
- g_helpPlaying = maintainAudioPlay(0);
- if (g_helpPlaying == 0)
- finishPlayingHelp(g_hoveredHotspot);
+ if (_helpPlaying != 0) {
+ _helpPlaying = maintainAudioPlay(0);
+ if (_helpPlaying == 0)
+ finishPlayingHelp(_hoveredHotspot);
}
eventData = 0;
switch (_xp->getEvent(etEmpty, &eventData)) {
case etTimer:
- if (g_keyLeft == 0 && g_keyRight == 0 &&
- g_keyUp == 0 && g_keyDown == 0 &&
- g_keyEnter == 0 && g_helpFlag == 0)
+ if (_keyLeft == 0 && _keyRight == 0 &&
+ _keyUp == 0 && _keyDown == 0 &&
+ _keyEnter == 0 && _helpFlag == 0)
break;
- if (g_keyReleased != 0) {
+ if (_keyReleased != 0) {
// Key release: restore hotspot colors
- g_keyReleased = 0;
- if (g_keyLeft != 0) {
+ _keyReleased = 0;
+ if (_keyLeft != 0) {
restoreColors(0);
restoreColors(1);
- } else if (g_keyRight != 0) {
+ } else if (_keyRight != 0) {
restoreColors(7);
- } else if (g_keyUp != 0) {
+ } else if (_keyUp != 0) {
restoreColors(6);
- } else if (g_keyDown != 0) {
+ } else if (_keyDown != 0) {
restoreColors(5);
- } else if (g_keyEnter != 0) {
+ } else if (_keyEnter != 0) {
restoreColors(3);
- } else if (g_helpFlag != 0) {
+ } else if (_helpFlag != 0) {
restoreColors(2);
- if (g_helpPlaying != 0) {
- if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+ if (_helpPlaying != 0) {
+ if (_helpIsIdle == 0 || _currentBoothScene != 9)
break;
}
@@ -586,23 +589,23 @@ int16 BoltEngine::boothEventLoop() {
}
} else {
// Key press: highlight hotspot colors
- g_keyReleased = 1;
- if (g_keyLeft != 0) {
+ _keyReleased = 1;
+ if (_keyLeft != 0) {
setColors(0);
setColors(1);
- } else if (g_keyRight != 0) {
+ } else if (_keyRight != 0) {
setColors(7);
- } else if (g_keyUp != 0) {
+ } else if (_keyUp != 0) {
setColors(6);
- } else if (g_keyDown != 0) {
+ } else if (_keyDown != 0) {
setColors(5);
- } else if (g_keyEnter != 0) {
+ } else if (_keyEnter != 0) {
setColors(3);
- } else if (g_helpFlag != 0) {
+ } else if (_helpFlag != 0) {
setColors(2);
- if (g_helpPlaying != 0) {
- if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+ if (_helpPlaying != 0) {
+ if (_helpIsIdle == 0 || _currentBoothScene != 9)
break;
}
@@ -610,32 +613,32 @@ int16 BoltEngine::boothEventLoop() {
}
}
- g_helpTimer = _xp->startTimer(500);
+ _helpTimer = _xp->startTimer(500);
break;
case etMouseMove:
- g_cursorX = (int16)(eventData >> 16);
- g_cursorY = (int16)eventData;
+ _cursorX = (int16)(eventData >> 16);
+ _cursorY = (int16)eventData;
hoverHotSpot();
break;
case etMouseDown:
{
- int16 prevHelp = g_helpPlaying;
- if (g_helpPlaying != 0) {
+ int16 prevHelp = _helpPlaying;
+ if (_helpPlaying != 0) {
stopAnimation();
- finishPlayingHelp(g_hoveredHotspot);
+ finishPlayingHelp(_hoveredHotspot);
}
- if (prevHelp != 0 && g_hoveredHotspot == 2)
+ if (prevHelp != 0 && _hoveredHotspot == 2)
break;
- if (g_hoveredHotspot == -1)
+ if (_hoveredHotspot == -1)
break;
- exitCode = handleButtonPress(g_hoveredHotspot);
+ exitCode = handleButtonPress(_hoveredHotspot);
if (exitCode == 0) {
- g_hoveredHotspot = -1;
+ _hoveredHotspot = -1;
hoverHotSpot();
}
@@ -643,43 +646,43 @@ int16 BoltEngine::boothEventLoop() {
}
case etSound:
- if (g_helpPlaying != 0) {
- g_helpPlaying = maintainAudioPlay(1);
- if (g_helpPlaying == 0)
- finishPlayingHelp(g_hoveredHotspot);
+ if (_helpPlaying != 0) {
+ _helpPlaying = maintainAudioPlay(1);
+ if (_helpPlaying == 0)
+ finishPlayingHelp(_hoveredHotspot);
}
break;
case etInactivity:
- if (g_helpPlaying != 0)
+ if (_helpPlaying != 0)
break;
- if (g_helpFlag == 0) {
- g_helpFlag = 1;
- g_helpTimer = _xp->startTimer(500);
- if (g_idleHelpAudioAvailable != 0) {
+ if (_helpFlag == 0) {
+ _helpFlag = 1;
+ _helpTimer = _xp->startTimer(500);
+ if (_idleHelpAudioAvailable != 0) {
_xp->setInactivityTimer(30);
continue;
}
} else {
- if (g_idleHelpAudioAvailable == 0)
+ if (_idleHelpAudioAvailable == 0)
break;
- if (startAnimation(g_rtfHandle, g_isDemo ? 0x16 : 0x19)) {
- g_idleHelpAudioAvailable = 0;
- g_helpPlaying = 1;
- g_helpIsIdle = 1;
+ if (startAnimation(_rtfHandle, _isDemo ? 0x16 : 0x19)) {
+ _idleHelpAudioAvailable = 0;
+ _helpPlaying = 1;
+ _helpIsIdle = 1;
}
}
break;
case etTrigger:
- if (g_helpPlaying == 0)
+ if (_helpPlaying == 0)
break;
- if (g_currentBoothScene == 9)
+ if (_currentBoothScene == 9)
mainEntranceHelpBlink();
else
boothHelpBlink();
@@ -693,37 +696,37 @@ int16 BoltEngine::boothEventLoop() {
}
_xp->hideCursor();
- return g_hoveredHotspot;
+ return _hoveredHotspot;
}
void BoltEngine::resetInactivityState() {
- if (g_helpTimer != 0) {
- _xp->killTimer(g_helpTimer);
- g_helpTimer = 0;
+ if (_helpTimer != 0) {
+ _xp->killTimer(_helpTimer);
+ _helpTimer = 0;
}
- g_idleHelpAudioAvailable = 0;
+ _idleHelpAudioAvailable = 0;
_xp->setInactivityTimer(30);
- if (g_helpFlag != 0) {
+ if (_helpFlag != 0) {
restoreColors(2);
- if (g_currentBoothScene == 9)
+ if (_currentBoothScene == 9)
restoreColors(8);
}
- g_helpFlag = 0;
+ _helpFlag = 0;
}
bool BoltEngine::handleButtonPress(int16 hotspot) {
byte savedLeftDoor[3];
byte savedRightDoor[3];
- savedLeftDoor[0] = g_leftDoorNavTable[0];
- savedLeftDoor[1] = g_leftDoorNavTable[1];
- savedLeftDoor[2] = g_leftDoorNavTable[2];
- savedRightDoor[0] = g_rightDoorNavTable[0];
- savedRightDoor[1] = g_rightDoorNavTable[1];
- savedRightDoor[2] = g_rightDoorNavTable[2];
+ savedLeftDoor[0] = _leftDoorNavTable[0];
+ savedLeftDoor[1] = _leftDoorNavTable[1];
+ savedLeftDoor[2] = _leftDoorNavTable[2];
+ savedRightDoor[0] = _rightDoorNavTable[0];
+ savedRightDoor[1] = _rightDoorNavTable[1];
+ savedRightDoor[2] = _rightDoorNavTable[2];
if (hotspot != -1)
resetInactivityState();
@@ -735,27 +738,27 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
case 2: {
int16 animId;
- switch (g_currentBoothScene) {
+ switch (_currentBoothScene) {
case 3:
- animId = g_isDemo ? 0x0F : 0x18;
+ animId = _isDemo ? 0x0F : 0x18;
break;
case 4:
- animId = g_isDemo ? 0x10 : 0x12;
+ animId = _isDemo ? 0x10 : 0x12;
break;
case 5:
- animId = g_isDemo ? 0x11 : 0x13;
+ animId = _isDemo ? 0x11 : 0x13;
break;
case 6:
- animId = g_isDemo ? 0x12 : 0x14;
+ animId = _isDemo ? 0x12 : 0x14;
break;
case 7:
- animId = g_isDemo ? 0x13 : 0x15;
+ animId = _isDemo ? 0x13 : 0x15;
break;
case 8:
- animId = g_isDemo ? 0x14 : 0x16;
+ animId = _isDemo ? 0x14 : 0x16;
break;
case 9:
- animId = g_isDemo ? 0x15 : 0x17;
+ animId = _isDemo ? 0x15 : 0x17;
break;
default:
animId = -1;
@@ -764,44 +767,44 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
bool ok = false;
if (animId != -1)
- ok = startAnimation(g_rtfHandle, animId);
+ ok = startAnimation(_rtfHandle, animId);
if (ok) {
- g_helpPlaying = 1;
+ _helpPlaying = 1;
setColors(2);
- g_helpFlag = 0;
- g_tourStep = 0;
+ _helpFlag = 0;
+ _tourStep = 0;
}
return false;
}
default:
- if (g_currentBoothScene == 9) {
+ if (_currentBoothScene == 9) {
switch (hotspot) {
case 3:
case 4:
{
byte *navTable = (hotspot == 3) ? savedLeftDoor : savedRightDoor;
- int8 boothOffset = (int8)navTable[g_lettersWon % 3];
- playAVOverBooth(boothOffset + (g_isDemo ? 9 : 12));
+ int8 boothOffset = (int8)navTable[_lettersWon % 3];
+ playAVOverBooth(boothOffset + (_isDemo ? 9 : 12));
return false;
}
case 5:
- if (g_isDemo) {
+ if (_isDemo) {
return true;
}
_xp->hideCursor();
displayBooth(stBack);
- if (playAV(g_rtfHandle, 3, g_displayWidth, g_displayHeight, g_displayX, g_displayY) == 0) {
+ if (playAV(_rtfHandle, 3, _displayWidth, _displayHeight, _displayX, _displayY) == 0) {
fadeToBlack(1);
_xp->setTransparency(false);
- displayPic(getBOLTMember(g_boothsBoltLib, g_displayMode ? 0x1802 : 0x1801), g_displayX, g_displayY, 0);
+ displayPic(getBOLTMember(_boothsBoltLib, _displayMode ? 0x1802 : 0x1801), _displayX, _displayY, 0);
_xp->updateDisplay();
- displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), stFront, 0);
+ displayColors(getBOLTMember(_boothsBoltLib, 0x1800), stFront, 0);
_xp->updateDisplay();
uint32 timer = _xp->startTimer(5000);
@@ -827,30 +830,30 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
vDelete("TopCatBF");
vDelete("Yogi");
- g_huckWins = 0;
- g_fredWins = 0;
- g_scoobyWins = 0;
- g_yogiWins = 0;
- g_georgeWins = 0;
- g_topCatWins = 0;
+ _huckWins = 0;
+ _fredWins = 0;
+ _scoobyWins = 0;
+ _yogiWins = 0;
+ _georgeWins = 0;
+ _topCatWins = 0;
displayBooth(stBack);
- playAV(g_rtfHandle, 5 - (g_isDemo ? 3 : 0),
- g_displayWidth, g_displayHeight,
- g_displayX, g_displayY);
-
- if (g_lettersWon != 0) {
- freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
- if (!getBOLTGroup(g_boothsBoltLib, 0x800, 1)) {
- g_hoveredHotspot = -1;
+ playAV(_rtfHandle, 5 - (_isDemo ? 3 : 0),
+ _displayWidth, _displayHeight,
+ _displayX, _displayY);
+
+ if (_lettersWon != 0) {
+ freeBOLTGroup(_boothsBoltLib, 0x800 + (_lettersWon << 8), 1);
+ if (!getBOLTGroup(_boothsBoltLib, 0x800, 1)) {
+ _hoveredHotspot = -1;
return true;
}
- boltPict2Pict(&g_boothLetterSprite, memberAddr(g_boothsBoltLib, g_displayMode ? 0x801 : 0x800));
+ boltPict2Pict(&_boothLetterSprite, memberAddr(_boothsBoltLib, _displayMode ? 0x801 : 0x800));
}
displayBooth(stFront);
- g_lettersWon = 0;
+ _lettersWon = 0;
_xp->showCursor();
return false;
@@ -861,7 +864,7 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
} else {
switch (hotspot) {
case 3:
- startCycle(g_boothSceneDesc);
+ startCycle(_boothSceneDesc);
_xp->startTimer(2000);
return true;
@@ -898,12 +901,12 @@ void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
}
void BoltEngine::setColors(int16 index) {
- if (g_currentBoothScene == 9) {
+ if (_currentBoothScene == 9) {
switch (index) {
case 0:
case 1:
case 2:
- blastColors(g_boothAnimDescs, index, 1);
+ blastColors(_boothAnimDescs, index, 1);
break;
case 3:
@@ -913,11 +916,11 @@ void BoltEngine::setColors(int16 index) {
case 5:
case 6:
case 7:
- blastColors(g_boothAnimDescs, index - 2, 1);
+ blastColors(_boothAnimDescs, index - 2, 1);
break;
case 8:
- displayPic(g_boothVisitSignOn, 0, 0, stFront);
+ displayPic(_boothVisitSignOn, 0, 0, stFront);
_xp->updateDisplay();
break;
}
@@ -926,33 +929,33 @@ void BoltEngine::setColors(int16 index) {
case 0:
case 1:
case 2:
- blastColors(g_boothAnimDescs, index, 1);
+ blastColors(_boothAnimDescs, index, 1);
break;
case 3:
- if (g_currentBoothScene == 9)
+ if (_currentBoothScene == 9)
break;
for (int16 i = 0; i < 4; i++)
- blastColors(g_boothHotPalDescs, i, 1);
+ blastColors(_boothHotPalDescs, i, 1);
break;
case 4:
- blastColors(g_boothAnimDescs, index - 1, 1);
+ blastColors(_boothAnimDescs, index - 1, 1);
break;
}
}
}
void BoltEngine::restoreColors(int16 index) {
- if (g_currentBoothScene == 9) {
+ if (_currentBoothScene == 9) {
switch (index) {
case 0:
case 1:
case 2: {
- byte *palData = g_savedPalettes[index];
- byte *animDesc = g_boothAnimDescs[index];
+ byte *palData = _savedPalettes[index];
+ byte *animDesc = _boothAnimDescs[index];
_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
break;
}
@@ -964,14 +967,14 @@ void BoltEngine::restoreColors(int16 index) {
case 5:
case 6:
case 7: {
- byte *palData = g_savedPalettes[index - 2];
- byte *animDesc = g_boothAnimDescs[index - 2];
+ byte *palData = _savedPalettes[index - 2];
+ byte *animDesc = _boothAnimDescs[index - 2];
_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
break;
}
case 8:
- displayPic(g_boothVisitSignOff, 0, 0, stFront);
+ displayPic(_boothVisitSignOff, 0, 0, stFront);
_xp->updateDisplay();
break;
}
@@ -980,24 +983,24 @@ void BoltEngine::restoreColors(int16 index) {
case 0:
case 1:
case 2: {
- byte *palData = g_savedPalettes[index];
- byte *animDesc = g_boothAnimDescs[index];
+ byte *palData = _savedPalettes[index];
+ byte *animDesc = _boothAnimDescs[index];
_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
break;
}
case 3:
for (int16 i = 0; i < 4; i++) {
- byte *palData = g_savedHotPalettes[i];
- byte *palDesc = g_boothHotPalDescs[i];
+ byte *palData = _savedHotPalettes[i];
+ byte *palDesc = _boothHotPalDescs[i];
_xp->setPalette((int16)READ_UINT16(palDesc + 2), (int16)READ_UINT16(palDesc), palData);
}
break;
case 4:
{
- byte *palData = g_savedPalettes[index - 1];
- byte *animDesc = g_boothAnimDescs[index - 1];
+ byte *palData = _savedPalettes[index - 1];
+ byte *animDesc = _boothAnimDescs[index - 1];
_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
break;
}
@@ -1006,20 +1009,20 @@ void BoltEngine::restoreColors(int16 index) {
}
void BoltEngine::loadColors() {
- for (int16 i = 0; i < g_boothNumAnimations; i++) {
- byte *animDesc = g_boothAnimDescs[i];
- _xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[i]);
+ for (int16 i = 0; i < _boothNumAnimations; i++) {
+ byte *animDesc = _boothAnimDescs[i];
+ _xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), _savedPalettes[i]);
}
- if (g_currentBoothScene == 9) {
- int16 last = g_boothNumAnimations - 1;
- byte *animDesc = g_boothAnimDescs[last];
- _xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[last]);
+ if (_currentBoothScene == 9) {
+ int16 last = _boothNumAnimations - 1;
+ byte *animDesc = _boothAnimDescs[last];
+ _xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), _savedPalettes[last]);
}
for (int16 i = 0; i < 4; i++) {
- byte *palDesc = g_boothHotPalDescs[i];
- _xp->getPalette((int16)READ_UINT16(palDesc), (int16)READ_UINT16(palDesc + 2), g_savedHotPalettes[i]);
+ byte *palDesc = _boothHotPalDescs[i];
+ _xp->getPalette((int16)READ_UINT16(palDesc), (int16)READ_UINT16(palDesc + 2), _savedHotPalettes[i]);
}
}
@@ -1029,116 +1032,116 @@ void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
}
void BoltEngine::playBoothAV() {
- switch (g_currentBoothScene) {
+ switch (_currentBoothScene) {
case 3:
- playAVOverBooth(6 - (g_isDemo ? 3 : 0));
+ playAVOverBooth(6 - (_isDemo ? 3 : 0));
break;
case 4:
- playAVOverBooth(7 - (g_isDemo ? 3 : 0));
+ playAVOverBooth(7 - (_isDemo ? 3 : 0));
break;
case 5:
- playAVOverBooth(8 - (g_isDemo ? 3 : 0));
+ playAVOverBooth(8 - (_isDemo ? 3 : 0));
break;
case 6:
- playAVOverBooth(9 - (g_isDemo ? 3 : 0));
+ playAVOverBooth(9 - (_isDemo ? 3 : 0));
break;
case 7:
- playAVOverBooth(10 - (g_isDemo ? 3 : 0));
+ playAVOverBooth(10 - (_isDemo ? 3 : 0));
break;
case 8:
- playAVOverBooth(11 - (g_isDemo ? 3 : 0));
+ playAVOverBooth(11 - (_isDemo ? 3 : 0));
break;
}
}
void BoltEngine::mainEntranceHelpBlink() {
- g_tourStep++;
+ _tourStep++;
- switch (g_tourStep) {
+ switch (_tourStep) {
case 1:
setColors(0);
setColors(1);
- g_keyLeft = 1;
- g_keyReleased = 1;
- g_helpTimer = _xp->startTimer(500);
+ _keyLeft = 1;
+ _keyReleased = 1;
+ _helpTimer = _xp->startTimer(500);
break;
case 2:
- g_keyLeft = 0;
- g_keyReleased = 0;
+ _keyLeft = 0;
+ _keyReleased = 0;
- if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[0].contains(_cursorX, _cursorY))
restoreColors(0);
- if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[1].contains(_cursorX, _cursorY))
restoreColors(1);
return;
case 3:
setColors(7);
- g_keyRight = 1;
- g_keyReleased = 1;
- g_helpTimer = _xp->startTimer(500);
+ _keyRight = 1;
+ _keyReleased = 1;
+ _helpTimer = _xp->startTimer(500);
break;
case 4:
- g_keyRight = 0;
- g_keyReleased = 0;
+ _keyRight = 0;
+ _keyReleased = 0;
- if (!g_boothHotspotDescs[7] .contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[7] .contains(_cursorX, _cursorY))
restoreColors(7);
return;
case 5:
setColors(6);
- g_keyUp = 1;
- g_keyReleased = 1;
- g_helpTimer = _xp->startTimer(500);
+ _keyUp = 1;
+ _keyReleased = 1;
+ _helpTimer = _xp->startTimer(500);
break;
case 6:
- g_keyUp = 0;
- g_keyReleased = 0;
+ _keyUp = 0;
+ _keyReleased = 0;
- if (!g_boothHotspotDescs[6].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[6].contains(_cursorX, _cursorY))
restoreColors(6);
return;
case 7:
setColors(5);
- g_keyDown = 1;
- g_keyReleased = 1;
- g_helpTimer = _xp->startTimer(500);
+ _keyDown = 1;
+ _keyReleased = 1;
+ _helpTimer = _xp->startTimer(500);
break;
case 8:
- g_keyDown = 0;
- g_keyReleased = 0;
- g_helpTimer = 0;
+ _keyDown = 0;
+ _keyReleased = 0;
+ _helpTimer = 0;
- if (!g_boothHotspotDescs[5].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[5].contains(_cursorX, _cursorY))
restoreColors(5);
return;
}
}
void BoltEngine::boothHelpBlink() {
- g_tourStep++;
+ _tourStep++;
- switch (g_tourStep) {
+ switch (_tourStep) {
case 1:
setColors(3);
- g_keyEnter = 1;
- g_keyReleased = 1;
- g_helpTimer = _xp->startTimer(500);
+ _keyEnter = 1;
+ _keyReleased = 1;
+ _helpTimer = _xp->startTimer(500);
break;
case 2:
- g_keyEnter = 0;
- g_keyReleased = 0;
+ _keyEnter = 0;
+ _keyReleased = 0;
- if (!g_boothHotspotDescs[3].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[3].contains(_cursorX, _cursorY))
restoreColors(3);
return;
@@ -1146,20 +1149,20 @@ void BoltEngine::boothHelpBlink() {
case 3:
setColors(0);
setColors(1);
- g_keyLeft = 1;
- g_keyReleased = 1;
- g_helpTimer = _xp->startTimer(500);
+ _keyLeft = 1;
+ _keyReleased = 1;
+ _helpTimer = _xp->startTimer(500);
break;
case 4:
- g_keyLeft = 0;
- g_keyReleased = 0;
- g_helpTimer = 0;
+ _keyLeft = 0;
+ _keyReleased = 0;
+ _helpTimer = 0;
- if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[0].contains(_cursorX, _cursorY))
restoreColors(0);
- if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+ if (!_boothHotspotDescs[1].contains(_cursorX, _cursorY))
restoreColors(1);
return;
@@ -1172,14 +1175,14 @@ void BoltEngine::tourPaletteCycleStep() {
-1, 0x736, 0x73B, 0x73F, 0x743, -1
};
- g_tourStep++;
+ _tourStep++;
- if ((g_tourStep & 1) == 0) {
+ if ((_tourStep & 1) == 0) {
_xp->stopCycle();
return;
}
- int16 index = g_tourStep >> 1;
+ int16 index = _tourStep >> 1;
if (index > 13)
return;
@@ -1187,7 +1190,7 @@ void BoltEngine::tourPaletteCycleStep() {
if (resId == -1)
return;
- startCycle(memberAddr(g_boothsBoltLib, resId));
+ startCycle(memberAddr(_boothsBoltLib, resId));
}
void BoltEngine::fadeToBlack(int16 steps) {
@@ -1237,61 +1240,61 @@ void BoltEngine::flushInput() {
}
int16 BoltEngine::winALetter(int16 prevBooth) {
- g_lettersWon++;
+ _lettersWon++;
- if (g_isDemo && g_lettersWon == 6)
+ if (_isDemo && _lettersWon == 6)
return 18;
- if (g_lettersWon == 15) {
- if (g_allLettersWonFlag) {
- g_allLettersWonFlag = false;
- g_lettersWon = 16;
+ if (_lettersWon == 15) {
+ if (_allLettersWonFlag) {
+ _allLettersWonFlag = false;
+ _lettersWon = 16;
} else {
- g_allLettersWonFlag = true;
+ _allLettersWonFlag = true;
}
}
- int16 avIndex = g_isDemo ? 25 : 31;
- playAV(g_rtfHandle, g_lettersWon + avIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+ int16 avIndex = _isDemo ? 25 : 31;
+ playAV(_rtfHandle, _lettersWon + avIndex, _displayWidth, _displayHeight, _displayX, _displayY);
- if (g_lettersWon >= 15)
- g_lettersWon = 0;
+ if (_lettersWon >= 15)
+ _lettersWon = 0;
// Return the booth front ID for the game just played
switch (prevBooth) {
case 10:
- if (g_isDemo) {
- g_huckWins++;
+ if (_isDemo) {
+ _huckWins++;
}
return 3; // HuckGame -> HucksBooth
case 11:
- if (g_isDemo) {
- g_fredWins++;
+ if (_isDemo) {
+ _fredWins++;
}
return 4; // FredGame -> FredsBooth
case 12:
- if (g_isDemo) {
- g_scoobyWins++;
+ if (_isDemo) {
+ _scoobyWins++;
}
return 5; // ScoobyGame -> ScoobysBooth
case 13:
- if (g_isDemo) {
- g_yogiWins++;
+ if (_isDemo) {
+ _yogiWins++;
}
return 6; // YogiGame -> YogisBooth
case 14:
- if (g_isDemo) {
- g_georgeWins++;
+ if (_isDemo) {
+ _georgeWins++;
}
return 7; // GeorgeGame -> GeorgesBooth
case 15:
- if (g_isDemo) {
- g_topCatWins++;
+ if (_isDemo) {
+ _topCatWins++;
}
return 8; // TopCatGame -> TopCatsBooth
@@ -1304,10 +1307,10 @@ int16 BoltEngine::endDemo(int16 prevBooth) {
_xp->hideCursor();
_xp->setTransparency(false);
- int16 baseId = (g_displayMode != 0) ? 0x0801 : 0x0800;
- int16 memberId = baseId + (g_lettersWon << 8);
- displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
- displayColors(getBOLTMember(g_boothsBoltLib, 0x718), stFront, 0);
+ int16 baseId = (_displayMode != 0) ? 0x0801 : 0x0800;
+ int16 memberId = baseId + (_lettersWon << 8);
+ displayPic(getBOLTMember(_boothsBoltLib, memberId), _displayX, _displayY, stFront);
+ displayColors(getBOLTMember(_boothsBoltLib, 0x718), stFront, 0);
_xp->updateDisplay();
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index ceaa40d8ebe..dd2158b1b78 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -41,64 +41,64 @@ int16 BoltEngine::fredGame(int16 prevBooth) {
bool BoltEngine::initFred() {
const char *path = assetPath("fred.blt");
- if (!openBOLTLib(&g_fredBoltLib, &g_fredBoltCallbacks, path))
+ if (!openBOLTLib(&_fredBoltLib, &_fredBoltCallbacks, path))
return false;
- if (!getBOLTGroup(g_fredBoltLib, 0, 1))
+ if (!getBOLTGroup(_fredBoltLib, 0, 1))
return false;
// Load assets...
- g_fredBackground = memberAddr(g_fredBoltLib, (g_displayMode != 0) ? 5 : 4);
+ _fredBackground = memberAddr(_fredBoltLib, (_displayMode != 0) ? 5 : 4);
- g_fredBalloonString = memberAddr(g_fredBoltLib, 6);
+ _fredBalloonString = memberAddr(_fredBoltLib, 6);
- g_fredFacingLeftRect = memberAddr(g_fredBoltLib, 0);
- g_fredFacingRightRect = memberAddr(g_fredBoltLib, 1);
- g_fredTurningRect = memberAddr(g_fredBoltLib, 2);
- g_fredBalloonRect = memberAddr(g_fredBoltLib, 3);
+ _fredFacingLeftRect = memberAddr(_fredBoltLib, 0);
+ _fredFacingRightRect = memberAddr(_fredBoltLib, 1);
+ _fredTurningRect = memberAddr(_fredBoltLib, 2);
+ _fredBalloonRect = memberAddr(_fredBoltLib, 3);
- g_fredHelpEntries = memberAddr(g_fredBoltLib, 0x38);
- g_fredPlayButton = memberAddr(g_fredBoltLib, 0x39);
- g_fredTimer = 0;
+ _fredHelpEntries = memberAddr(_fredBoltLib, 0x38);
+ _fredPlayButton = memberAddr(_fredBoltLib, 0x39);
+ _fredTimer = 0;
for (int i = 0; i < 10; i++)
- g_fredSprites[i] = memberAddr(g_fredBoltLib, 0x28 + i);
+ _fredSprites[i] = memberAddr(_fredBoltLib, 0x28 + i);
- getFredSoundInfo(g_fredBoltLib, 0x3A, &g_fredSounds[0]);
- getFredSoundInfo(g_fredBoltLib, 0x3B, &g_fredSounds[1]);
- getFredSoundInfo(g_fredBoltLib, 0x3C, &g_fredSounds[2]);
- getFredSoundInfo(g_fredBoltLib, 0x3D, &g_fredSounds[3]);
+ getFredSoundInfo(_fredBoltLib, 0x3A, &_fredSounds[0]);
+ getFredSoundInfo(_fredBoltLib, 0x3B, &_fredSounds[1]);
+ getFredSoundInfo(_fredBoltLib, 0x3C, &_fredSounds[2]);
+ getFredSoundInfo(_fredBoltLib, 0x3D, &_fredSounds[3]);
- g_fredLoopSound = nullptr;
- g_fredCurrentSound = nullptr;
- g_fredPendingLoop = nullptr;
- g_fredPendingOneShot = nullptr;
+ _fredLoopSound = nullptr;
+ _fredCurrentSound = nullptr;
+ _fredPendingLoop = nullptr;
+ _fredPendingOneShot = nullptr;
// Init Fred sprite struct...
- g_fredSprite.flags = 1;
- g_fredSprite.direction = 0;
- g_fredSprite.xPos = 0xA000; // Fixed-point coordinate
- g_fredSprite.yPos = 0x1600; // Fixed-point coordinate
- g_fredSprite.speed = 0x600;
- g_fredSprite.pathTable = nullptr;
+ _fredSprite.flags = 1;
+ _fredSprite.direction = 0;
+ _fredSprite.xPos = 0xA000; // Fixed-point coordinate
+ _fredSprite.yPos = 0x1600; // Fixed-point coordinate
+ _fredSprite.speed = 0x600;
+ _fredSprite.pathTable = nullptr;
- setFredAnimMode(&g_fredSprite, 0);
+ setFredAnimMode(&_fredSprite, 0);
// Load save data...
- if (!vLoad(&g_fredSaveData, g_fredSaveFile)) {
- g_fredSaveData[0] = 0; // Balloon catch count
- g_fredSaveData[1] = 0; // Level data group index, increments on level complete, caps at 9
- g_fredSaveData[2] = 0; // Palette group index, increments on level complete, wraps at 10
+ if (!vLoad(&_fredSaveData, "FredBC")) {
+ _fredSaveData[0] = 0; // Balloon catch count
+ _fredSaveData[1] = 0; // Level data group index, increments on level complete, caps at 9
+ _fredSaveData[2] = 0; // Palette group index, increments on level complete, wraps at 10
}
- if (!initFredLevel(g_fredSaveData[1], g_fredSaveData[2]))
+ if (!initFredLevel(_fredSaveData[1], _fredSaveData[2]))
return false;
- g_fredLevelIndex = 0;
- while (g_fredLevelIndex < g_fredSaveData[0]) {
- FredEntityState *entry = g_fredEntitiesTable[g_fredLevelIndex + 1];
+ _fredLevelIndex = 0;
+ while (_fredLevelIndex < _fredSaveData[0]) {
+ FredEntityState *entry = _fredEntitiesTable[_fredLevelIndex + 1];
setFredAnimMode(entry, 10);
- g_fredLevelIndex++;
+ _fredLevelIndex++;
}
// Flush timer events...
@@ -109,50 +109,50 @@ bool BoltEngine::initFred() {
_xp->setTransparency(false);
// Display background on back and front buffers...
- displayColors(g_fredPalette, stBack, 0);
- displayPic(g_fredBackground, g_displayX, g_displayY, stFront);
+ displayColors(_fredPalette, stBack, 0);
+ displayPic(_fredBackground, _displayX, _displayY, stFront);
_xp->updateDisplay();
_xp->setTransparency(true);
- displayColors(g_fredPalette, stFront, 0);
- displayColors(g_fredPalette, stBack, 1);
- displayPic(g_fredBackground, g_displayX, g_displayY, stBack);
+ displayColors(_fredPalette, stFront, 0);
+ displayColors(_fredPalette, stBack, 1);
+ displayPic(_fredBackground, _displayX, _displayY, stBack);
renderFredScene();
_xp->updateDisplay();
- _xp->startCycle(g_fredCycleSpecs);
+ _xp->startCycle(_fredCycleSpecs);
_xp->setFrameRate(15);
return true;
}
void BoltEngine::cleanUpFred() {
- int16 savedLevel = g_fredSaveData[1]; // Level data group index
- int16 savedPalette = g_fredSaveData[2]; // Palette group index
+ int16 savedLevel = _fredSaveData[1]; // Level data group index
+ int16 savedPalette = _fredSaveData[2]; // Palette group index
- g_fredSaveData[0] = g_fredLevelIndex;
+ _fredSaveData[0] = _fredLevelIndex;
- byte *levelPtr = g_fredLevelPtr;
- if (READ_UINT16(levelPtr + 0x0A) <= g_fredLevelIndex) {
- g_fredSaveData[1]++;
- if (g_fredSaveData[1] >= 10)
- g_fredSaveData[1] = 9;
+ byte *levelPtr = _fredLevelPtr;
+ if (READ_UINT16(levelPtr + 0x0A) <= _fredLevelIndex) {
+ _fredSaveData[1]++;
+ if (_fredSaveData[1] >= 10)
+ _fredSaveData[1] = 9;
- g_fredSaveData[2]++;
- if (g_fredSaveData[2] >= 10)
- g_fredSaveData[2] = 0;
+ _fredSaveData[2]++;
+ if (_fredSaveData[2] >= 10)
+ _fredSaveData[2] = 0;
- g_fredSaveData[0] = 0;
+ _fredSaveData[0] = 0;
}
- vSave(&g_fredSaveData, sizeof(g_fredSaveData), g_fredSaveFile);
+ vSave(&_fredSaveData, sizeof(_fredSaveData), "FredBC");
_xp->stopCycle();
termFredLevel(savedLevel, savedPalette);
- freeBOLTGroup(g_fredBoltLib, 0, 1);
- closeBOLTLib(&g_fredBoltLib);
+ freeBOLTGroup(_fredBoltLib, 0, 1);
+ closeBOLTLib(&_fredBoltLib);
_xp->setFrameRate(0);
_xp->fillDisplay(0, stFront);
@@ -161,49 +161,49 @@ void BoltEngine::cleanUpFred() {
bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
// Load level data group...
- if (!getBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
+ if (!getBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
return false;
// Load palette group...
- if (!getBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1)) {
- freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+ if (!getBOLTGroup(_fredBoltLib, palGroup * 0x200 + 0x200, 1)) {
+ freeBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
return false;
}
// Level descriptor member...
- g_fredLevelPtr = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x100);
+ _fredLevelPtr = memberAddr(_fredBoltLib, levelGroup * 0x200 + 0x100);
// Palette member...
- g_fredPalette = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x200);
+ _fredPalette = memberAddr(_fredBoltLib, palGroup * 0x200 + 0x200);
// Cycle data...
- g_fredCycleRaw = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x20A);
- boltCycleToXPCycle(g_fredCycleRaw, g_fredCycleSpecs);
+ _fredCycleRaw = memberAddr(_fredBoltLib, palGroup * 0x200 + 0x20A);
+ boltCycleToXPCycle(_fredCycleRaw, _fredCycleSpecs);
// Other resources...
- g_fredBalloonSprite = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x209);
- g_fredPathMatrix = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x132);
- g_fredRowBounds = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x101);
+ _fredBalloonSprite = memberAddr(_fredBoltLib, palGroup * 0x200 + 0x209);
+ _fredPathMatrix = memberAddr(_fredBoltLib, levelGroup * 0x200 + 0x132);
+ _fredRowBounds = memberAddr(_fredBoltLib, levelGroup * 0x200 + 0x101);
// Allocate shuffle table: numRows * (numCols + 1)
- uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
- uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+ uint16 numRows = READ_UINT16(_fredLevelPtr + 4);
+ uint16 numCols = READ_UINT16(_fredLevelPtr + 6);
- g_fredShuffleTable = (byte *)_xp->allocMem(numRows * (numCols + 1));
- if (!g_fredShuffleTable) {
+ _fredShuffleTable = (byte *)_xp->allocMem(numRows * (numCols + 1));
+ if (!_fredShuffleTable) {
termFredLevel(levelGroup, palGroup);
return false;
}
// Build and shuffle column indices for each row...
for (int16 row = 0; row < numRows; row++) {
- numCols = READ_UINT16(g_fredLevelPtr + 6);
+ numCols = READ_UINT16(_fredLevelPtr + 6);
int16 stride = numCols + 1;
int16 rowBase = stride * row;
- g_fredShuffleTable[rowBase] = 0;
+ _fredShuffleTable[rowBase] = 0;
for (int16 col = 0; col < numCols; col++) {
- g_fredShuffleTable[rowBase + col + 1] = (byte)col;
+ _fredShuffleTable[rowBase + col + 1] = (byte)col;
}
// Fisher-Yates shuffle...
@@ -212,16 +212,16 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
int16 posA = rowBase + col + 1;
int16 posB = rowBase + randIdx + 1;
- byte tmp = g_fredShuffleTable[posA];
- g_fredShuffleTable[posA] = g_fredShuffleTable[posB];
- g_fredShuffleTable[posB] = tmp;
+ byte tmp = _fredShuffleTable[posA];
+ _fredShuffleTable[posA] = _fredShuffleTable[posB];
+ _fredShuffleTable[posB] = tmp;
}
}
// Allocate balloon table...
- uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
- g_fredEntitiesTable = (FredEntityState **)_xp->allocMem((numBalloons + 2) * sizeof(FredEntityState *));
- if (!g_fredEntitiesTable) {
+ uint16 numBalloons = READ_UINT16(_fredLevelPtr + 0x0A);
+ _fredEntitiesTable = (FredEntityState **)_xp->allocMem((numBalloons + 2) * sizeof(FredEntityState *));
+ if (!_fredEntitiesTable) {
termFredLevel(levelGroup, palGroup);
return false;
}
@@ -236,17 +236,17 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
}
entry->flags = 0;
- g_fredEntitiesTable[i + 1] = entry;
+ _fredEntitiesTable[i + 1] = entry;
}
// Sentinel: null pointer after last balloon...
- g_fredEntitiesTable[numBalloons + 1] = nullptr;
+ _fredEntitiesTable[numBalloons + 1] = nullptr;
// First entry points to Fred's sprite...
- g_fredEntitiesTable[0] = &g_fredSprite;
+ _fredEntitiesTable[0] = &_fredSprite;
// Allocate balloon type shuffle array...
- uint16 numTypes = READ_UINT16(g_fredLevelPtr + 0x0C);
+ uint16 numTypes = READ_UINT16(_fredLevelPtr + 0x0C);
byte *typeShuf = (byte *)_xp->allocMem(numTypes);
if (!typeShuf) {
termFredLevel(levelGroup, palGroup);
@@ -270,49 +270,49 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
// Assign balloon type...
byte balloonType = typeShuf[i % numTypes] + 0x0B;
- FredEntityState *balloonEntry = g_fredEntitiesTable[i + 1];
+ FredEntityState *balloonEntry = _fredEntitiesTable[i + 1];
setFredAnimMode(balloonEntry, balloonType);
}
_xp->freeMem(typeShuf);
- g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
- g_fredBalloonSearchIdx = 0;
- g_fredLevelIndex = 0;
+ _fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+ _fredBalloonSearchIdx = 0;
+ _fredLevelIndex = 0;
return true;
}
void BoltEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
// Free balloon state structs...
- if (g_fredEntitiesTable) {
- uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
+ if (_fredEntitiesTable) {
+ uint16 numBalloons = READ_UINT16(_fredLevelPtr + 0x0A);
for (int16 i = 0; i < numBalloons; i++) {
- FredEntityState *entry = g_fredEntitiesTable[i + 1];
+ FredEntityState *entry = _fredEntitiesTable[i + 1];
if (entry) {
delete entry;
- g_fredEntitiesTable[i + 1] = nullptr;
+ _fredEntitiesTable[i + 1] = nullptr;
}
}
- delete[] g_fredEntitiesTable;
- g_fredEntitiesTable = nullptr;
+ delete[] _fredEntitiesTable;
+ _fredEntitiesTable = nullptr;
}
// Free shuffle table...
- if (g_fredShuffleTable) {
- _xp->freeMem(g_fredShuffleTable);
- g_fredShuffleTable = nullptr;
+ if (_fredShuffleTable) {
+ _xp->freeMem(_fredShuffleTable);
+ _fredShuffleTable = nullptr;
}
// Free BOLT groups...
- freeBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1);
- freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+ freeBOLTGroup(_fredBoltLib, palGroup * 0x200 + 0x200, 1);
+ freeBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
}
void BoltEngine::swapFredAnimEntry() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
- uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
+ uint32 decompSize = _boltCurrentMemberEntry->decompSize;
uint32 offset = 0;
while (offset < decompSize) {
@@ -323,8 +323,8 @@ void BoltEngine::swapFredAnimEntry() {
}
void BoltEngine::swapFredAnimDesc() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
- uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
+ uint32 decompSize = _boltCurrentMemberEntry->decompSize;
uint32 offset = 0;
byte *ptr = data;
byte *colorCountPtr = data + 0x0E;
@@ -348,7 +348,7 @@ void BoltEngine::swapFredAnimDesc() {
}
void BoltEngine::swapFredLevelDesc() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
@@ -366,7 +366,7 @@ int16 BoltEngine::playFred() {
int16 joystickX = 0;
// Show help on first play...
- if (g_fredShowHelp != 0) {
+ if (_fredShowHelp != 0) {
if (!helpFred())
return 4;
}
@@ -389,8 +389,8 @@ int16 BoltEngine::playFred() {
case etMouseDown:
case etInactivity:
if (!allCaught) {
- int16 helpMode = (g_fredSprite.animMode == 8 || g_fredSprite.animMode == 6) ? 1 : 0;
- setFredAnimMode(&g_fredSprite, helpMode);
+ int16 helpMode = (_fredSprite.animMode == 8 || _fredSprite.animMode == 6) ? 1 : 0;
+ setFredAnimMode(&_fredSprite, helpMode);
if (!helpFred()) {
result = 4;
exitLoop = 1;
@@ -414,17 +414,17 @@ int16 BoltEngine::playFred() {
_xp->updateDisplay();
// Balloon spawn timer...
- g_fredBalloonSpawnDelay--;
- if (g_fredBalloonSpawnDelay == 0) {
+ _fredBalloonSpawnDelay--;
+ if (_fredBalloonSpawnDelay == 0) {
if (spawnBalloon())
- g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+ _fredBalloonSpawnDelay = calcBalloonSpawnDelay();
else
- g_fredBalloonSpawnDelay = 1;
+ _fredBalloonSpawnDelay = 1;
}
// --- First pass: update Fred and balloons animations ---
int16 idx = 0;
- FredEntityState *entry = g_fredEntitiesTable[idx];
+ FredEntityState *entry = _fredEntitiesTable[idx];
while (entry) {
if (entry->flags & 1) { // active
// Save previous position...
@@ -558,12 +558,12 @@ int16 BoltEngine::playFred() {
}
idx++;
- entry = g_fredEntitiesTable[idx];
+ entry = _fredEntitiesTable[idx];
}
// --- Second pass: move balloons and Fred ---
idx = 0;
- entry = g_fredEntitiesTable[idx];
+ entry = _fredEntitiesTable[idx];
while (entry) {
if (entry->flags & 1) {
int16 mode = entry->animMode;
@@ -623,7 +623,7 @@ int16 BoltEngine::playFred() {
}
idx++;
- entry = g_fredEntitiesTable[idx];
+ entry = _fredEntitiesTable[idx];
}
// --- Skip collision check if all caught ---
@@ -632,20 +632,20 @@ int16 BoltEngine::playFred() {
// --- Get Fred's collision rect based on current mode ---
byte *fredRect;
- switch (g_fredSprite.animMode) {
+ switch (_fredSprite.animMode) {
case 6:
case 8:
- fredRect = g_fredFacingLeftRect; // facing/running left
+ fredRect = _fredFacingLeftRect; // facing/running left
break;
case 2:
case 4:
- fredRect = g_fredFacingRightRect; // facing/running right
+ fredRect = _fredFacingRightRect; // facing/running right
break;
case 7:
- fredRect = g_fredTurningRect; // turning left
+ fredRect = _fredTurningRect; // turning left
break;
case 3:
- fredRect = g_fredTurningRect; // turning right
+ fredRect = _fredTurningRect; // turning right
break;
default:
fredRect = nullptr;
@@ -656,21 +656,21 @@ int16 BoltEngine::playFred() {
continue;
// Build Fred's bounding rect...
- int16 fredX = ((int32)g_fredSprite.xPos >> 8) + READ_UINT16(fredRect);
- int16 fredY = ((int32)g_fredSprite.yPos >> 8) + READ_UINT16(fredRect + 2);
+ int16 fredX = ((int32)_fredSprite.xPos >> 8) + READ_UINT16(fredRect);
+ int16 fredY = ((int32)_fredSprite.yPos >> 8) + READ_UINT16(fredRect + 2);
int16 fredW = READ_UINT16(fredRect + 4);
int16 fredH = READ_UINT16(fredRect + 6);
// --- Third pass: check collisions with balloons ---
idx = 0;
- entry = g_fredEntitiesTable[idx];
+ entry = _fredEntitiesTable[idx];
while (entry) {
if ((entry->flags & 1) && entry->animMode == 0x0B) {
// Build balloon bounding rect...
- int16 bx = ((int32)entry->xPos >> 8) + READ_UINT16(g_fredBalloonRect);
- int16 by = ((int32)entry->yPos >> 8) + READ_UINT16(g_fredBalloonRect + 2);
- int16 bw = READ_UINT16(g_fredBalloonRect + 4);
- int16 bh = READ_UINT16(g_fredBalloonRect + 6);
+ int16 bx = ((int32)entry->xPos >> 8) + READ_UINT16(_fredBalloonRect);
+ int16 by = ((int32)entry->yPos >> 8) + READ_UINT16(_fredBalloonRect + 2);
+ int16 bw = READ_UINT16(_fredBalloonRect + 4);
+ int16 bh = READ_UINT16(_fredBalloonRect + 6);
// {x, y, w, h} rect overlap test...
Common::Rect balloonRect(bx, by, bx + bw, by + bh);
@@ -679,32 +679,32 @@ int16 BoltEngine::playFred() {
if (playerRect.intersects(balloonRect)) {
// Caught a balloon!
int16 catchMode;
- if (g_fredSprite.animMode == 6 || g_fredSprite.animMode == 8 || g_fredSprite.animMode == 7)
+ if (_fredSprite.animMode == 6 || _fredSprite.animMode == 8 || _fredSprite.animMode == 7)
catchMode = 9; // catch facing left
else
catchMode = 5; // catch facing right
- setFredAnimMode(&g_fredSprite, catchMode);
+ setFredAnimMode(&_fredSprite, catchMode);
setFredAnimMode(entry, 0x0A); // balloon disappearing
- g_fredLevelIndex++;
- if (g_fredLevelIndex >= READ_UINT16(g_fredLevelPtr + 0x0A)) {
+ _fredLevelIndex++;
+ if (_fredLevelIndex >= READ_UINT16(_fredLevelPtr + 0x0A)) {
allCaught = 1;
result = 0x10;
}
// Reset spawn timer...
if (spawnBalloon())
- g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+ _fredBalloonSpawnDelay = calcBalloonSpawnDelay();
else
- g_fredBalloonSpawnDelay = 1;
+ _fredBalloonSpawnDelay = 1;
break; // Only catch one per frame...
}
}
idx++;
- entry = g_fredEntitiesTable[idx];
+ entry = _fredEntitiesTable[idx];
}
}
@@ -714,7 +714,7 @@ int16 BoltEngine::playFred() {
}
int16 BoltEngine::helpFred() {
- byte *firstEntry = getResolvedPtr(g_fredHelpEntries, 0);
+ byte *firstEntry = getResolvedPtr(_fredHelpEntries, 0);
byte *picDesc = getResolvedPtr(firstEntry, 4);
int16 cursorX = READ_UINT16(picDesc + 6) + READ_UINT16(picDesc + 0x0A) - 10;
@@ -729,10 +729,10 @@ int16 BoltEngine::helpFred() {
_xp->setInactivityTimer(0);
_xp->stopSound();
- g_fredLoopSound = nullptr;
- g_fredCurrentSound = nullptr;
- g_fredPendingLoop = nullptr;
- g_fredPendingOneShot = nullptr;
+ _fredLoopSound = nullptr;
+ _fredCurrentSound = nullptr;
+ _fredPendingLoop = nullptr;
+ _fredPendingOneShot = nullptr;
renderFredScene();
@@ -740,7 +740,7 @@ int16 BoltEngine::helpFred() {
int16 off = 0;
byte *entry;
while (true) {
- entry = getResolvedPtr(g_fredHelpEntries, off);
+ entry = getResolvedPtr(_fredHelpEntries, off);
if (!entry)
break;
byte *pic = getResolvedPtr(entry, 4);
@@ -753,7 +753,7 @@ int16 BoltEngine::helpFred() {
// Save current palette for each entry, find playable entry...
off = 0;
while (true) {
- entry = getResolvedPtr(g_fredHelpEntries, off);
+ entry = getResolvedPtr(_fredHelpEntries, off);
if (!entry)
break;
@@ -773,15 +773,15 @@ int16 BoltEngine::helpFred() {
_xp->showCursor();
// Highlight first entry...
- g_fredHoveredEntry = getResolvedPtr(g_fredHelpEntries, 0);
- hiliteFredHelpObject(getResolvedPtr(g_fredHelpEntries, 0), 1);
+ _fredHoveredEntry = getResolvedPtr(_fredHelpEntries, 0);
+ hiliteFredHelpObject(getResolvedPtr(_fredHelpEntries, 0), 1);
// Main help loop
while (exitResult == -1) {
// Handle audio playback...
if (isPlaying) {
if (!maintainAudioPlay(soundPending)) {
- int16 highlight = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+ int16 highlight = (playableEntry == _fredHoveredEntry) ? 1 : 0;
hiliteFredHelpObject(playableEntry, highlight);
isPlaying = 0;
}
@@ -795,15 +795,15 @@ int16 BoltEngine::helpFred() {
switch (eventType) {
case etTimer: {
// Timer expired, toggle selected entry highlight...
- if (!g_fredCurrentHelpObject)
+ if (!_fredCurrentHelpObject)
break;
- if (g_fredTimer != eventData)
+ if (_fredTimer != eventData)
break;
- g_fredTimer = _xp->startTimer(500);
+ _fredTimer = _xp->startTimer(500);
- byte *sel = g_fredCurrentHelpObject;
+ byte *sel = _fredCurrentHelpObject;
int16 highlighted = (READ_UINT32(sel + 8) & 1) ? 0 : 1;
hiliteFredHelpObject(sel, highlighted);
break;
@@ -816,7 +816,7 @@ int16 BoltEngine::helpFred() {
off = 0;
byte *hitEntry = nullptr;
while (true) {
- entry = getResolvedPtr(g_fredHelpEntries, off);
+ entry = getResolvedPtr(_fredHelpEntries, off);
if (!entry)
break;
@@ -835,20 +835,20 @@ int16 BoltEngine::helpFred() {
}
// Update hover state...
- if (hitEntry == g_fredHoveredEntry)
+ if (hitEntry == _fredHoveredEntry)
break;
// Unhighlight previous...
- if (g_fredHoveredEntry &&
- g_fredHoveredEntry != g_fredCurrentHelpObject &&
- !(g_fredHoveredEntry == playableEntry && isPlaying)) {
- hiliteFredHelpObject(g_fredHoveredEntry, 0);
+ if (_fredHoveredEntry &&
+ _fredHoveredEntry != _fredCurrentHelpObject &&
+ !(_fredHoveredEntry == playableEntry && isPlaying)) {
+ hiliteFredHelpObject(_fredHoveredEntry, 0);
}
- g_fredHoveredEntry = hitEntry;
+ _fredHoveredEntry = hitEntry;
// Highlight new...
- if (hitEntry && hitEntry != g_fredCurrentHelpObject) {
+ if (hitEntry && hitEntry != _fredCurrentHelpObject) {
hiliteFredHelpObject(hitEntry, 1);
}
@@ -859,41 +859,41 @@ int16 BoltEngine::helpFred() {
// Stop current animation if playing...
if (isPlaying) {
- if (g_fredTimer) {
- _xp->killTimer(g_fredTimer);
- g_fredTimer = 0;
+ if (_fredTimer) {
+ _xp->killTimer(_fredTimer);
+ _fredTimer = 0;
}
- int16 hl = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+ int16 hl = (playableEntry == _fredHoveredEntry) ? 1 : 0;
hiliteFredHelpObject(playableEntry, hl);
- hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
- hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+ hl = (_fredCurrentHelpObject == _fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(_fredCurrentHelpObject, hl);
- g_fredCurrentHelpObject = nullptr;
+ _fredCurrentHelpObject = nullptr;
stopAnimation();
isPlaying = 0;
justStopped = 1;
}
// Handle click on hovered entry...
- if (!g_fredHoveredEntry)
+ if (!_fredHoveredEntry)
break;
- uint32 entryType = READ_UINT32(g_fredHoveredEntry);
+ uint32 entryType = READ_UINT32(_fredHoveredEntry);
if (entryType == 0) {
// Exit help...
exitResult = 0;
} else if (entryType == 1) {
// Exit help and clear show-help flag...
- g_fredShowHelp = 0;
+ _fredShowHelp = 0;
exitResult = 1;
} else if (entryType == 2) {
// Play animation...
if (!isPlaying && !justStopped) {
- if (startAnimation(g_rtfHandle, 0x1B)) {
- g_fredHelpStep = 0;
+ if (startAnimation(_rtfHandle, 0x1B)) {
+ _fredHelpStep = 0;
isPlaying = 1;
}
}
@@ -919,10 +919,10 @@ int16 BoltEngine::helpFred() {
// Restore all entries to highlight state matching hover
off = 0;
while (true) {
- entry = getResolvedPtr(g_fredHelpEntries, off);
+ entry = getResolvedPtr(_fredHelpEntries, off);
if (!entry)
break;
- int16 hl = (entry == g_fredHoveredEntry) ? 1 : 0;
+ int16 hl = (entry == _fredHoveredEntry) ? 1 : 0;
hiliteFredHelpObject(entry, hl);
off += 4;
}
@@ -932,7 +932,7 @@ int16 BoltEngine::helpFred() {
// Unhighlight all entries
off = 0;
while (true) {
- entry = getResolvedPtr(g_fredHelpEntries, off);
+ entry = getResolvedPtr(_fredHelpEntries, off);
if (!entry)
break;
hiliteFredHelpObject(entry, 0);
@@ -967,37 +967,37 @@ void BoltEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
}
void BoltEngine::helpAnimStep() {
- if (g_fredHelpStep < 0 || g_fredHelpStep >= 4)
+ if (_fredHelpStep < 0 || _fredHelpStep >= 4)
return;
// Unhighlight previous selected if not hovered...
- int16 hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
- hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+ int16 hl = (_fredCurrentHelpObject == _fredHoveredEntry) ? 1 : 0;
+ hiliteFredHelpObject(_fredCurrentHelpObject, hl);
- // On even steps, select entry from g_fredPlayButton table; on odd steps, deselect...
- if (!(g_fredHelpStep & 1)) {
- int16 idx = g_fredHelpStep >> 1;
- g_fredCurrentHelpObject = getResolvedPtr(g_fredPlayButton, idx * 4);
+ // On even steps, select entry from _fredPlayButton table; on odd steps, deselect...
+ if (!(_fredHelpStep & 1)) {
+ int16 idx = _fredHelpStep >> 1;
+ _fredCurrentHelpObject = getResolvedPtr(_fredPlayButton, idx * 4);
} else {
- g_fredCurrentHelpObject = nullptr;
+ _fredCurrentHelpObject = nullptr;
}
- g_fredTimer = 0;
+ _fredTimer = 0;
// Highlight new selected entry...
- hiliteFredHelpObject(g_fredCurrentHelpObject, 1);
+ hiliteFredHelpObject(_fredCurrentHelpObject, 1);
// Start/stop blink timer...
- if (!(g_fredHelpStep & 1)) {
- g_fredTimer = _xp->startTimer(500);
+ if (!(_fredHelpStep & 1)) {
+ _fredTimer = _xp->startTimer(500);
} else {
- if (g_fredTimer) {
- _xp->killTimer(g_fredTimer);
- g_fredTimer = 0;
+ if (_fredTimer) {
+ _xp->killTimer(_fredTimer);
+ _fredTimer = 0;
}
}
- g_fredHelpStep++;
+ _fredHelpStep++;
}
bool BoltEngine::spawnBalloon() {
@@ -1007,7 +1007,7 @@ bool BoltEngine::spawnBalloon() {
FredEntityState *entry;
while (true) {
- entry = g_fredEntitiesTable[i];
+ entry = _fredEntitiesTable[i];
if (!entry)
break;
@@ -1022,12 +1022,12 @@ bool BoltEngine::spawnBalloon() {
return false;
// Find next free balloon slot starting from the search index...
- int16 searchStart = g_fredBalloonSearchIdx;
+ int16 searchStart = _fredBalloonSearchIdx;
int16 wrapped = 0;
i = searchStart;
while (true) {
- entry = g_fredEntitiesTable[i];
+ entry = _fredEntitiesTable[i];
if (!entry) {
wrapped = 1;
i = -1;
@@ -1046,22 +1046,22 @@ bool BoltEngine::spawnBalloon() {
if (i == searchStart && wrapped)
return false;
- g_fredBalloonSearchIdx = i + 1;
+ _fredBalloonSearchIdx = i + 1;
// Pick row from shuffle table...
int16 row = selectBalloonRow();
- uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+ uint16 numCols = READ_UINT16(_fredLevelPtr + 6);
int16 stride = numCols + 1;
int16 rowBase = stride * row;
// Get next column from shuffle table...
- byte *shuffleRow = g_fredShuffleTable + rowBase;
+ byte *shuffleRow = _fredShuffleTable + rowBase;
byte colCounter = shuffleRow[0];
- byte col = g_fredShuffleTable[rowBase + colCounter + 1];
+ byte col = _fredShuffleTable[rowBase + colCounter + 1];
- // Look up path table: g_fredPathMatrix[row][col]
- byte *rowPaths = getResolvedPtr(g_fredPathMatrix, row * 4);
+ // Look up path table: _fredPathMatrix[row][col]
+ byte *rowPaths = getResolvedPtr(_fredPathMatrix, row * 4);
byte *pathTable = getResolvedPtr(rowPaths, col * 4);
// Assign path to balloon...
@@ -1088,25 +1088,25 @@ bool BoltEngine::spawnBalloon() {
}
int16 BoltEngine::calcBalloonSpawnDelay() {
- int16 range = READ_UINT16(g_fredLevelPtr + 2);
- int16 base = READ_UINT16(g_fredLevelPtr + 0);
+ int16 range = READ_UINT16(_fredLevelPtr + 2);
+ int16 base = READ_UINT16(_fredLevelPtr + 0);
return _xp->getRandom(range) + base;
}
int16 BoltEngine::selectBalloonRow() {
// Get Fred's center X position...
- int32 fredX = g_fredSprite.xPos >> 8;
+ int32 fredX = _fredSprite.xPos >> 8;
// Get Fred's sprite width/2 to find center...
- int16 frameIdx = g_fredSprite.frameIndex;
- byte *spriteDesc = getResolvedPtr(g_fredSprite.animTable, frameIdx * 6);
+ int16 frameIdx = _fredSprite.frameIndex;
+ byte *spriteDesc = getResolvedPtr(_fredSprite.animTable, frameIdx * 6);
int16 halfWidth = READ_UINT16(spriteDesc + 0x0A) >> 1;
int16 fredCenter = (int16)fredX + halfWidth;
// Find which row Fred is in...
int16 row = 0;
- byte *rowBounds = g_fredRowBounds; // Array of int16 x-values
- uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
+ byte *rowBounds = _fredRowBounds; // Array of int16 x-values
+ uint16 numRows = READ_UINT16(_fredLevelPtr + 4);
while (row < numRows) {
if (READ_UINT16(rowBounds + row * 2) > fredCenter)
@@ -1115,7 +1115,7 @@ int16 BoltEngine::selectBalloonRow() {
}
// Pick random row, rejecting if too close to Fred's row...
- int16 rowBias = READ_UINT16(g_fredLevelPtr + 8);
+ int16 rowBias = READ_UINT16(_fredLevelPtr + 8);
int16 randomRow;
do {
randomRow = _xp->getRandom(numRows);
@@ -1125,11 +1125,11 @@ int16 BoltEngine::selectBalloonRow() {
}
void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
- if (mode >= 0x0B && mode < 0x0B + (g_fredLevelPtr ? READ_UINT16(g_fredLevelPtr + 0x0C) : 0)) {
+ if (mode >= 0x0B && mode < 0x0B + (_fredLevelPtr ? READ_UINT16(_fredLevelPtr + 0x0C) : 0)) {
state->animMode = 0x0B;
int16 typeIdx = mode - 0x0B;
- state->animTable = getResolvedPtr(g_fredBalloonSprite, typeIdx * 4);
+ state->animTable = getResolvedPtr(_fredBalloonSprite, typeIdx * 4);
state->frameIndex = 0;
state->frameCountdown = READ_UINT16(state->animTable + 4);
return;
@@ -1144,7 +1144,7 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
// Standard Fred animation...
state->animMode = mode;
- state->animTable = g_fredSprites[mode];
+ state->animTable = _fredSprites[mode];
state->frameIndex = 0;
state->frameCountdown = READ_UINT16(state->animTable + 4);
@@ -1152,22 +1152,22 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
// Play sound based on mode...
switch (mode) {
case 3: // turn right
- playFredSound(&g_fredSounds[2], nullptr);
+ playFredSound(&_fredSounds[2], nullptr);
break;
case 4: // run right
- playFredSound(&g_fredSounds[0], &g_fredSounds[0]);
+ playFredSound(&_fredSounds[0], &_fredSounds[0]);
break;
case 5: // catch right
- playFredSound(&g_fredSounds[3], nullptr);
+ playFredSound(&_fredSounds[3], nullptr);
break;
case 7: // turn left
- playFredSound(&g_fredSounds[2], nullptr);
+ playFredSound(&_fredSounds[2], nullptr);
break;
case 8: // run left
- playFredSound(&g_fredSounds[1], &g_fredSounds[1]);
+ playFredSound(&_fredSounds[1], &_fredSounds[1]);
break;
case 9: // catch left
- playFredSound(&g_fredSounds[3], nullptr);
+ playFredSound(&_fredSounds[3], nullptr);
break;
default: // 0, 1, 2, 6
playFredSound(nullptr, nullptr);
@@ -1181,7 +1181,7 @@ void BoltEngine::renderFredScene() {
int16 idx = 0;
while (true) {
- FredEntityState *entry = g_fredEntitiesTable[idx];
+ FredEntityState *entry = _fredEntitiesTable[idx];
if (!entry)
break;
@@ -1196,7 +1196,7 @@ void BoltEngine::renderFredScene() {
// If balloon (mode 0x0B), draw string below...
if (entry->animMode == 0x0B) {
- displayPic(g_fredBalloonString, x + 10, y + 25, stFront);
+ displayPic(_fredBalloonString, x + 10, y + 25, stFront);
}
}
@@ -1210,23 +1210,23 @@ void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *sound
}
void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
- g_fredPendingOneShot = oneShot;
- g_fredPendingLoop = loop;
+ _fredPendingOneShot = oneShot;
+ _fredPendingLoop = loop;
- if (g_fredLoopSound != nullptr || loop == nullptr) {
- // Check if current loop is g_fredSounds[2] and new one-shot is g_fredSounds[1]
+ if (_fredLoopSound != nullptr || loop == nullptr) {
+ // Check if current loop is _fredSounds[2] and new one-shot is _fredSounds[1]
// (sound transition check)
- if (g_fredCurrentSound == &g_fredSounds[3] && g_fredPendingOneShot == &g_fredSounds[2]) {
+ if (_fredCurrentSound == &_fredSounds[3] && _fredPendingOneShot == &_fredSounds[2]) {
// Allow transition without stopping...
} else {
- g_fredLoopSound = nullptr;
- g_fredCurrentSound = nullptr;
+ _fredLoopSound = nullptr;
+ _fredCurrentSound = nullptr;
_xp->stopSound();
}
}
// If there's a pending sound and nothing currently playing, trigger it!
- if (g_fredPendingOneShot != nullptr && g_fredCurrentSound == nullptr) {
+ if (_fredPendingOneShot != nullptr && _fredCurrentSound == nullptr) {
updateFredSound();
}
}
@@ -1234,29 +1234,29 @@ void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
void BoltEngine::updateFredSound() {
bool startLoop = false;
- if (g_fredLoopSound != nullptr) {
+ if (_fredLoopSound != nullptr) {
// Loop sound active, continue with it...
- g_fredCurrentSound = g_fredLoopSound;
- } else if (g_fredPendingOneShot != nullptr) {
+ _fredCurrentSound = _fredLoopSound;
+ } else if (_fredPendingOneShot != nullptr) {
// Start pending one-shot...
- g_fredCurrentSound = g_fredPendingOneShot;
- g_fredLoopSound = g_fredPendingLoop;
- startLoop = (g_fredPendingLoop != nullptr);
- g_fredPendingLoop = 0;
- g_fredPendingOneShot = 0;
+ _fredCurrentSound = _fredPendingOneShot;
+ _fredLoopSound = _fredPendingLoop;
+ startLoop = (_fredPendingLoop != nullptr);
+ _fredPendingLoop = 0;
+ _fredPendingOneShot = 0;
} else {
// Nothing to play...
- g_fredCurrentSound = nullptr;
+ _fredCurrentSound = nullptr;
}
- if (g_fredCurrentSound != nullptr) {
+ if (_fredCurrentSound != nullptr) {
// Play the current sound...
- SoundInfo *snd = g_fredCurrentSound;
+ SoundInfo *snd = _fredCurrentSound;
_xp->playSound(snd->data, snd->size, 22050);
if (startLoop) {
// Queue loop sound twice...
- SoundInfo *loopSnd = g_fredLoopSound;
+ SoundInfo *loopSnd = _fredLoopSound;
_xp->playSound(loopSnd->data, loopSnd->size, 22050);
_xp->playSound(loopSnd->data, loopSnd->size, 22050);
}
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
index 9fb7c82aec6..12f816bcd8f 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/booths/george.cpp
@@ -39,50 +39,50 @@ int16 BoltEngine::georgeGame(int16 prevBooth) {
}
bool BoltEngine::initGeorge() {
- if (!openBOLTLib(&g_georgeBoltLib, &g_georgeBoltCallbacks, assetPath("george.blt")))
+ if (!openBOLTLib(&_georgeBoltLib, &_georgeBoltCallbacks, assetPath("george.blt")))
return false;
- if (!getBOLTGroup(g_georgeBoltLib, 0, 1))
+ if (!getBOLTGroup(_georgeBoltLib, 0, 1))
return false;
- g_georgeBgPic = memberAddr(g_georgeBoltLib, (g_displayMode != 0) ? 1 : 0);
- g_georgeHelpObjects = memberAddr(g_georgeBoltLib, 0x1F);
- g_georgeHelpSequence = memberAddr(g_georgeBoltLib, 0x20);
+ _georgeBgPic = memberAddr(_georgeBoltLib, (_displayMode != 0) ? 1 : 0);
+ _georgeHelpObjects = memberAddr(_georgeBoltLib, 0x1F);
+ _georgeHelpSequence = memberAddr(_georgeBoltLib, 0x20);
- g_georgeCarPics[0] = memberAddr(g_georgeBoltLib, 0x10);
- g_georgeCarPics[1] = memberAddr(g_georgeBoltLib, 0x11);
- g_georgeCarPics[2] = memberAddr(g_georgeBoltLib, 0x12);
- g_georgeCollisionRect = memberAddr(g_georgeBoltLib, 0x13);
+ _georgeCarPics[0] = memberAddr(_georgeBoltLib, 0x10);
+ _georgeCarPics[1] = memberAddr(_georgeBoltLib, 0x11);
+ _georgeCarPics[2] = memberAddr(_georgeBoltLib, 0x12);
+ _georgeCollisionRect = memberAddr(_georgeBoltLib, 0x13);
- getGeorgeSoundInfo(g_georgeBoltLib, 0x16, &g_georgeSoundCarTumble, 6);
- getGeorgeSoundInfo(g_georgeBoltLib, 0x15, &g_georgeSoundCarLoopHi, 0);
- getGeorgeSoundInfo(g_georgeBoltLib, 0x14, &g_georgeSoundCarLoopLo, 0);
- getGeorgeSoundInfo(g_georgeBoltLib, 0x17, &g_georgeSoundCarStartUp, 3);
- getGeorgeSoundInfo(g_georgeBoltLib, 0x18, &g_georgeSoundCarGoesAway, 3);
+ getGeorgeSoundInfo(_georgeBoltLib, 0x16, &_georgeSoundCarTumble, 6);
+ getGeorgeSoundInfo(_georgeBoltLib, 0x15, &_georgeSoundCarLoopHi, 0);
+ getGeorgeSoundInfo(_georgeBoltLib, 0x14, &_georgeSoundCarLoopLo, 0);
+ getGeorgeSoundInfo(_georgeBoltLib, 0x17, &_georgeSoundCarStartUp, 3);
+ getGeorgeSoundInfo(_georgeBoltLib, 0x18, &_georgeSoundCarGoesAway, 3);
- g_georgeSoundCurrent = nullptr;
- g_georgeSoundToPlay = nullptr;
- g_georgeSoundNext = nullptr;
- g_georgeSoundQueued = nullptr;
- g_georgeSoundChannelCounter = 0;
+ _georgeSoundCurrent = nullptr;
+ _georgeSoundToPlay = nullptr;
+ _georgeSoundNext = nullptr;
+ _georgeSoundQueued = nullptr;
+ _georgeSoundChannelCounter = 0;
// Load save data...
- if (!vLoad(&g_georgeSaveData, g_georgeSaveFileName)) {
- g_georgeSaveData[0] = 0;
- g_georgeSaveData[1] = 0;
- g_georgeSaveData[2] = 0;
+ if (!vLoad(&_georgeSaveData, "GeorgeBE")) {
+ _georgeSaveData[0] = 0;
+ _georgeSaveData[1] = 0;
+ _georgeSaveData[2] = 0;
}
- if (!initGeorgeLevel(g_georgeSaveData[1], g_georgeSaveData[2])) {
- freeBOLTGroup(g_georgeBoltLib, 0, 1);
+ if (!initGeorgeLevel(_georgeSaveData[1], _georgeSaveData[2])) {
+ freeBOLTGroup(_georgeBoltLib, 0, 1);
return false;
}
// Initialize satellites from list...
- g_georgeCollectedSatellitesNum = 0;
- while (g_georgeCollectedSatellitesNum < g_georgeSaveData[0]) {
- setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + g_georgeCollectedSatellitesNum], 4, 0);
- g_georgeCollectedSatellitesNum++;
+ _georgeCollectedSatellitesNum = 0;
+ while (_georgeCollectedSatellitesNum < _georgeSaveData[0]) {
+ setSatelliteAnimMode(_georgeEntityList[_georgeNumSatellites + _georgeCollectedSatellitesNum], 4, 0);
+ _georgeCollectedSatellitesNum++;
}
// Drain timer events...
@@ -93,23 +93,23 @@ bool BoltEngine::initGeorge() {
_xp->setTransparency(false);
// Display palette and background to front...
- displayColors(g_georgePalette, stBack, 0);
- displayPic(g_georgeBgPic, g_displayX, g_displayY, stFront);
+ displayColors(_georgePalette, stBack, 0);
+ displayPic(_georgeBgPic, _displayX, _displayY, stFront);
_xp->updateDisplay();
_xp->setTransparency(true);
// Display palette to both surfaces...
- displayColors(g_georgePalette, stFront, 0);
- displayColors(g_georgePalette, stBack, 1);
+ displayColors(_georgePalette, stFront, 0);
+ displayColors(_georgePalette, stBack, 1);
// Display background to back surface...
- displayPic(g_georgeBgPic, g_displayX, g_displayY, stBack);
+ displayPic(_georgeBgPic, _displayX, _displayY, stBack);
drawFlyingObjects();
_xp->updateDisplay();
- _xp->startCycle(g_georgePalCycleSpecs);
+ _xp->startCycle(_georgePalCycleSpecs);
_xp->setFrameRate(12);
return true;
@@ -117,35 +117,35 @@ bool BoltEngine::initGeorge() {
void BoltEngine::cleanUpGeorge() {
// Save current progress...
- int16 savedLevel = g_georgeSaveData[1];
- int16 savedVariant = g_georgeSaveData[2];
+ int16 savedLevel = _georgeSaveData[1];
+ int16 savedVariant = _georgeSaveData[2];
- g_georgeSaveData[0] = g_georgeCollectedSatellitesNum;
+ _georgeSaveData[0] = _georgeCollectedSatellitesNum;
// Check if collection threshold for current level was met...
- if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
+ if (_georgeCollectedSatellitesNum >= _georgeThresholds[0]) {
// Advance level...
- g_georgeSaveData[1]++;
- if (g_georgeSaveData[1] >= 10)
- g_georgeSaveData[1] = 9;
+ _georgeSaveData[1]++;
+ if (_georgeSaveData[1] >= 10)
+ _georgeSaveData[1] = 9;
// Advance variant...
- g_georgeSaveData[2]++;
- if (g_georgeSaveData[2] >= 10)
- g_georgeSaveData[2] = 0;
+ _georgeSaveData[2]++;
+ if (_georgeSaveData[2] >= 10)
+ _georgeSaveData[2] = 0;
// Reset collected count for next level...
- g_georgeSaveData[0] = 0;
+ _georgeSaveData[0] = 0;
}
- vSave(g_georgeSaveData, sizeof(g_georgeSaveData), g_georgeSaveFileName);
+ vSave(_georgeSaveData, sizeof(_georgeSaveData), "GeorgeBE");
_xp->stopCycle();
termGeorgeLevel(savedLevel, savedVariant);
- freeBOLTGroup(g_georgeBoltLib, 0, 1);
- closeBOLTLib(&g_georgeBoltLib);
+ freeBOLTGroup(_georgeBoltLib, 0, 1);
+ closeBOLTLib(&_georgeBoltLib);
_xp->setFrameRate(0);
_xp->fillDisplay(0, stFront);
@@ -156,33 +156,33 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
int16 levelGroup = (level * 2) << 8 | 0x100;
int16 variantGroup = (variant * 2) << 8 | 0x200;
- if (!getBOLTGroup(g_georgeBoltLib, levelGroup, 1))
+ if (!getBOLTGroup(_georgeBoltLib, levelGroup, 1))
return false;
- if (!getBOLTGroup(g_georgeBoltLib, variantGroup, 1)) {
- freeBOLTGroup(g_georgeBoltLib, levelGroup, 1);
+ if (!getBOLTGroup(_georgeBoltLib, variantGroup, 1)) {
+ freeBOLTGroup(_georgeBoltLib, levelGroup, 1);
return false;
}
- g_georgeThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup);
- g_georgePalette = memberAddr(g_georgeBoltLib, variantGroup);
- g_georgePalCycleRawData = memberAddr(g_georgeBoltLib, variantGroup + 1);
- boltCycleToXPCycle(g_georgePalCycleRawData, g_georgePalCycleSpecs);
+ _georgeThresholds = (int16 *)memberAddr(_georgeBoltLib, levelGroup);
+ _georgePalette = memberAddr(_georgeBoltLib, variantGroup);
+ _georgePalCycleRawData = memberAddr(_georgeBoltLib, variantGroup + 1);
+ boltCycleToXPCycle(_georgePalCycleRawData, _georgePalCycleSpecs);
- g_georgeSatelliteGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x12);
- g_georgeAsteroidGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x26);
- g_georgeSatelliteThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x01);
- g_georgeAsteroidThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x02);
- g_georgeSatelliteCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x19);
- g_georgeAsteroidCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x2A);
- g_georgeSatellitePaths = memberAddr(g_georgeBoltLib, levelGroup + 0x27);
- g_georgeAsteroidPaths = memberAddr(g_georgeBoltLib, levelGroup + 0x3C);
+ _georgeSatelliteGfx = memberAddr(_georgeBoltLib, variantGroup + 0x12);
+ _georgeAsteroidGfx = memberAddr(_georgeBoltLib, variantGroup + 0x26);
+ _georgeSatelliteThresholds = (int16 *)memberAddr(_georgeBoltLib, levelGroup + 0x01);
+ _georgeAsteroidThresholds = (int16 *)memberAddr(_georgeBoltLib, levelGroup + 0x02);
+ _georgeSatelliteCollisionRects = memberAddr(_georgeBoltLib, variantGroup + 0x19);
+ _georgeAsteroidCollisionRects = memberAddr(_georgeBoltLib, variantGroup + 0x2A);
+ _georgeSatellitePaths = memberAddr(_georgeBoltLib, levelGroup + 0x27);
+ _georgeAsteroidPaths = memberAddr(_georgeBoltLib, levelGroup + 0x3C);
// Allocate and build asteroid shuffle table...
- int16 numAsteroidRows = g_georgeThresholds[3];
- int16 numAsteroidCols = g_georgeThresholds[4];
- g_georgeSatelliteShuffleTable = (byte *)_xp->allocMem((uint32)(numAsteroidCols + 1) * (numAsteroidRows));
- if (!g_georgeSatelliteShuffleTable) {
+ int16 numAsteroidRows = _georgeThresholds[3];
+ int16 numAsteroidCols = _georgeThresholds[4];
+ _georgeSatelliteShuffleTable = (byte *)_xp->allocMem((uint32)(numAsteroidCols + 1) * (numAsteroidRows));
+ if (!_georgeSatelliteShuffleTable) {
termGeorgeLevel(level, variant);
return false;
}
@@ -190,76 +190,76 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
// Fill each row with sequential values 0..numAsteroidCols, then shuffle...
for (int16 row = 0; row < numAsteroidRows; row++) {
int16 rowBase = (numAsteroidCols + 1) * row;
- g_georgeSatelliteShuffleTable[rowBase] = 0;
+ _georgeSatelliteShuffleTable[rowBase] = 0;
for (int16 col = 0; col < numAsteroidCols; col++)
- g_georgeSatelliteShuffleTable[rowBase + col + 1] = (byte)col;
+ _georgeSatelliteShuffleTable[rowBase + col + 1] = (byte)col;
// Fisher-Yates shuffle...
for (int16 i = 0; i < numAsteroidCols; i++) {
int16 j = _xp->getRandom(numAsteroidCols);
- byte tmp = g_georgeSatelliteShuffleTable[rowBase + i + 1];
- g_georgeSatelliteShuffleTable[rowBase + i + 1] = g_georgeSatelliteShuffleTable[rowBase + j + 1];
- g_georgeSatelliteShuffleTable[rowBase + j + 1] = tmp;
+ byte tmp = _georgeSatelliteShuffleTable[rowBase + i + 1];
+ _georgeSatelliteShuffleTable[rowBase + i + 1] = _georgeSatelliteShuffleTable[rowBase + j + 1];
+ _georgeSatelliteShuffleTable[rowBase + j + 1] = tmp;
}
}
// Allocate and build satellite shuffle table...
- int16 numSatelliteRows = g_georgeThresholds[9];
- int16 numSatelliteCols = g_georgeThresholds[10];
- g_georgeAsteroidShuffleTable = (byte *)_xp->allocMem((uint32)(numSatelliteCols + 1) * (numSatelliteRows));
- if (!g_georgeAsteroidShuffleTable) {
+ int16 numSatelliteRows = _georgeThresholds[9];
+ int16 numSatelliteCols = _georgeThresholds[10];
+ _georgeAsteroidShuffleTable = (byte *)_xp->allocMem((uint32)(numSatelliteCols + 1) * (numSatelliteRows));
+ if (!_georgeAsteroidShuffleTable) {
termGeorgeLevel(level, variant);
return false;
}
for (int16 row = 0; row < numSatelliteRows; row++) {
int16 rowBase = (numSatelliteCols + 1) * row;
- g_georgeAsteroidShuffleTable[rowBase] = 0;
+ _georgeAsteroidShuffleTable[rowBase] = 0;
for (int16 col = 0; col < numSatelliteCols; col++)
- g_georgeAsteroidShuffleTable[rowBase + col + 1] = (byte)col;
+ _georgeAsteroidShuffleTable[rowBase + col + 1] = (byte)col;
for (int16 i = 0; i < numSatelliteCols; i++) {
int16 j = _xp->getRandom(numSatelliteCols);
- byte tmp = g_georgeAsteroidShuffleTable[rowBase + i + 1];
- g_georgeAsteroidShuffleTable[rowBase + i + 1] = g_georgeAsteroidShuffleTable[rowBase + j + 1];
- g_georgeAsteroidShuffleTable[rowBase + j + 1] = tmp;
+ byte tmp = _georgeAsteroidShuffleTable[rowBase + i + 1];
+ _georgeAsteroidShuffleTable[rowBase + i + 1] = _georgeAsteroidShuffleTable[rowBase + j + 1];
+ _georgeAsteroidShuffleTable[rowBase + j + 1] = tmp;
}
}
// Compute satellite list indices...
- g_georgeNumSatellites = 0;
- int16 numSatellites = g_georgeThresholds[0];
- g_georgeFirstAsteroidIdx = numSatellites;
+ _georgeNumSatellites = 0;
+ int16 numSatellites = _georgeThresholds[0];
+ _georgeFirstAsteroidIdx = numSatellites;
- int16 numAsteroids = g_georgeThresholds[11];
+ int16 numAsteroids = _georgeThresholds[11];
if (numAsteroids < 1)
numAsteroids = 1;
- g_georgeCarIdx = g_georgeFirstAsteroidIdx + numAsteroids;
- g_georgeTotalSatellites = g_georgeCarIdx + 1;
+ _georgeCarIdx = _georgeFirstAsteroidIdx + numAsteroids;
+ _georgeTotalSatellites = _georgeCarIdx + 1;
// Allocate satellite list...
- g_georgeEntityList = (GeorgeEntityState **)_xp->allocMem((g_georgeTotalSatellites + 1) * sizeof(GeorgeEntityState *));
- if (!g_georgeEntityList) {
+ _georgeEntityList = (GeorgeEntityState **)_xp->allocMem((_georgeTotalSatellites + 1) * sizeof(GeorgeEntityState *));
+ if (!_georgeEntityList) {
termGeorgeLevel(level, variant);
return false;
}
- for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
- g_georgeEntityList[i] = new GeorgeEntityState();
- if (!g_georgeEntityList[i]) {
+ for (int16 i = 0; i < _georgeTotalSatellites; i++) {
+ _georgeEntityList[i] = new GeorgeEntityState();
+ if (!_georgeEntityList[i]) {
termGeorgeLevel(level, variant);
return false;
}
- g_georgeEntityList[i]->flags = 0;
+ _georgeEntityList[i]->flags = 0;
}
- g_georgeEntityList[g_georgeTotalSatellites] = nullptr;
+ _georgeEntityList[_georgeTotalSatellites] = nullptr;
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
assert(carSat);
carSat->flags = 1;
@@ -274,7 +274,7 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
carSat->pathIndex = 0;
setGeorgeAnimMode(carSat, 0);
- int32 numObjectTypes = g_georgeThresholds[6];
+ int32 numObjectTypes = _georgeThresholds[6];
byte *objShuf = (byte *)_xp->allocMem(numObjectTypes);
if (!objShuf) {
termGeorgeLevel(level, variant);
@@ -297,13 +297,13 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
}
int16 animType = objShuf[i % numObjectTypes];
- setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + i], 3, animType);
+ setSatelliteAnimMode(_georgeEntityList[_georgeNumSatellites + i], 3, animType);
}
_xp->freeMem(objShuf);
// Allocate asteroid shuffled list...
- int16 numAsteroidTypes = g_georgeThresholds[11];
+ int16 numAsteroidTypes = _georgeThresholds[11];
if (numAsteroidTypes < 1)
numAsteroidTypes = 1;
@@ -327,84 +327,84 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
}
int16 animType = astShuf[i % numAsteroidTypes];
- setAsteroidAnimMode(g_georgeEntityList[g_georgeFirstAsteroidIdx + i], 5, animType);
+ setAsteroidAnimMode(_georgeEntityList[_georgeFirstAsteroidIdx + i], 5, animType);
}
_xp->freeMem(astShuf);
// Allocate and load sound list for satellites...
- g_georgeSatelliteSoundList = (SoundInfo **)_xp->allocMem(numObjectTypes * sizeof(SoundInfo *));
- if (!g_georgeSatelliteSoundList) {
+ _georgeSatelliteSoundList = (SoundInfo **)_xp->allocMem(numObjectTypes * sizeof(SoundInfo *));
+ if (!_georgeSatelliteSoundList) {
termGeorgeLevel(level, variant);
return false;
}
for (int16 i = 0; i < numObjectTypes; i++) {
- g_georgeSatelliteSoundList[i] = new SoundInfo();
- if (!g_georgeSatelliteSoundList[i]) {
+ _georgeSatelliteSoundList[i] = new SoundInfo();
+ if (!_georgeSatelliteSoundList[i]) {
termGeorgeLevel(level, variant);
return false;
}
int16 member = variantGroup | ((i + 0x1A) & 0xFF);
- getGeorgeSoundInfo(g_georgeBoltLib, member, g_georgeSatelliteSoundList[i], 6);
+ getGeorgeSoundInfo(_georgeBoltLib, member, _georgeSatelliteSoundList[i], 6);
}
- g_georgeSatelliteWait = 1;
- g_georgeSatelliteSearchIdx = 0;
- g_georgeAsteroidSearchIdx = 0;
- g_georgeAsteroidWait = getRandomAsteroidWait();
- g_georgeHitSearchIdx = 0;
- g_georgeCollectedSatellitesNum = 0;
+ _georgeSatelliteWait = 1;
+ _georgeSatelliteSearchIdx = 0;
+ _georgeAsteroidSearchIdx = 0;
+ _georgeAsteroidWait = getRandomAsteroidWait();
+ _georgeHitSearchIdx = 0;
+ _georgeCollectedSatellitesNum = 0;
return true;
}
void BoltEngine::termGeorgeLevel(int16 level, int16 variant) {
- if (g_georgeEntityList) {
- for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ if (_georgeEntityList) {
+ for (int16 i = 0; i < _georgeTotalSatellites; i++) {
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (sat) {
delete sat;
- g_georgeEntityList[i] = nullptr;
+ _georgeEntityList[i] = nullptr;
}
}
- _xp->freeMem(g_georgeEntityList);
- g_georgeEntityList = nullptr;
+ _xp->freeMem(_georgeEntityList);
+ _georgeEntityList = nullptr;
}
- if (g_georgeSatelliteSoundList) {
- int32 numObjectTypes = g_georgeThresholds[6];
+ if (_georgeSatelliteSoundList) {
+ int32 numObjectTypes = _georgeThresholds[6];
for (int16 i = 0; i < numObjectTypes; i++) {
- SoundInfo *snd = g_georgeSatelliteSoundList[i];
+ SoundInfo *snd = _georgeSatelliteSoundList[i];
if (snd) {
delete snd;
- g_georgeSatelliteSoundList[i] = nullptr;
+ _georgeSatelliteSoundList[i] = nullptr;
}
}
- _xp->freeMem(g_georgeSatelliteSoundList);
- g_georgeSatelliteSoundList = nullptr;
+ _xp->freeMem(_georgeSatelliteSoundList);
+ _georgeSatelliteSoundList = nullptr;
}
- if (g_georgeAsteroidShuffleTable) {
- _xp->freeMem(g_georgeAsteroidShuffleTable);
- g_georgeAsteroidShuffleTable = nullptr;
+ if (_georgeAsteroidShuffleTable) {
+ _xp->freeMem(_georgeAsteroidShuffleTable);
+ _georgeAsteroidShuffleTable = nullptr;
}
- if (g_georgeSatelliteShuffleTable) {
- _xp->freeMem(g_georgeSatelliteShuffleTable);
- g_georgeSatelliteShuffleTable = nullptr;
+ if (_georgeSatelliteShuffleTable) {
+ _xp->freeMem(_georgeSatelliteShuffleTable);
+ _georgeSatelliteShuffleTable = nullptr;
}
- freeBOLTGroup(g_georgeBoltLib, (variant * 2) << 8 | 0x200, 1);
- freeBOLTGroup(g_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
+ freeBOLTGroup(_georgeBoltLib, (variant * 2) << 8 | 0x200, 1);
+ freeBOLTGroup(_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
}
void BoltEngine::swapGeorgeFrameArray() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
- uint32 size = g_boltCurrentMemberEntry->decompSize;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
+ uint32 size = _boltCurrentMemberEntry->decompSize;
uint32 off = 0;
while ((int32)(size - off) > 0) {
@@ -415,8 +415,8 @@ void BoltEngine::swapGeorgeFrameArray() {
}
void BoltEngine::swapGeorgeHelpEntry() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
- uint32 size = g_boltCurrentMemberEntry->decompSize;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
+ uint32 size = _boltCurrentMemberEntry->decompSize;
uint32 off = 0;
uint16 palCountOff = (uint16)(off + 0x10);
@@ -436,7 +436,7 @@ void BoltEngine::swapGeorgeHelpEntry() {
}
void BoltEngine::swapGeorgeThresholds() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
for (int16 i = 0; i < 12; i++)
WRITE_UINT16(data + i * 2, READ_BE_UINT16(data + i * 2));
}
@@ -449,7 +449,7 @@ int16 BoltEngine::playGeorge() {
int16 firstFrame = 1;
int32 joyY = 0;
- if (g_georgeHelpActive) {
+ if (_georgeHelpActive) {
if (!helpGeorge())
return 7;
}
@@ -505,13 +505,13 @@ int16 BoltEngine::playGeorge() {
if (flyActive) {
if (firstFrame) {
firstFrame = 0;
- playGeorgeSound(&g_georgeSoundCarStartUp, &g_georgeSoundCarLoopHi);
+ playGeorgeSound(&_georgeSoundCarStartUp, &_georgeSoundCarLoopHi);
}
if (joyY) {
flyActive = 0;
} else {
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
int16 carY = (int16)(carSat->y >> 8);
if (carY > 0xD0)
@@ -533,28 +533,28 @@ int16 BoltEngine::playGeorge() {
}
} else {
if (!winSeq) {
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
carSat->accelY = (joyY * 3) << 8;
- if (--g_georgeSatelliteWait == 0) {
+ if (--_georgeSatelliteWait == 0) {
if (spawnSatellite())
- g_georgeSatelliteWait = getRandomSatelliteWait();
+ _georgeSatelliteWait = getRandomSatelliteWait();
else
- g_georgeSatelliteWait = 1;
+ _georgeSatelliteWait = 1;
}
- if (--g_georgeAsteroidWait == 0) {
+ if (--_georgeAsteroidWait == 0) {
if (spawnAsteroid())
- g_georgeAsteroidWait = getRandomAsteroidWait();
+ _georgeAsteroidWait = getRandomAsteroidWait();
else
- g_georgeAsteroidWait = 1;
+ _georgeAsteroidWait = 1;
}
}
}
// ---- Pass 1: animate all satellites ----
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
if (!(sat->flags & 1))
@@ -588,7 +588,7 @@ int16 BoltEngine::playGeorge() {
// ---- Pass 2: physics ----
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
if (!(sat->flags & 1))
@@ -626,11 +626,11 @@ int16 BoltEngine::playGeorge() {
if (!winSeq) {
if (sat->velY <= 0) {
- if (g_georgeSoundToPlay != &g_georgeSoundCarLoopHi)
- playGeorgeSound(&g_georgeSoundCarLoopHi, &g_georgeSoundCarLoopHi);
+ if (_georgeSoundToPlay != &_georgeSoundCarLoopHi)
+ playGeorgeSound(&_georgeSoundCarLoopHi, &_georgeSoundCarLoopHi);
} else {
- if (g_georgeSoundToPlay != &g_georgeSoundCarLoopLo)
- playGeorgeSound(&g_georgeSoundCarLoopLo, &g_georgeSoundCarLoopLo);
+ if (_georgeSoundToPlay != &_georgeSoundCarLoopLo)
+ playGeorgeSound(&_georgeSoundCarLoopLo, &_georgeSoundCarLoopLo);
}
} else {
int32 xExit = (int32)0x1C2 << 8;
@@ -663,16 +663,16 @@ int16 BoltEngine::playGeorge() {
// ---- Pass 3: collision detection ----
if (!winSeq) {
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
byte *carFrameData = getResolvedPtr(carSat->animTable, carSat->frameIndex * 6);
- int16 carX1 = (int16)(carSat->x >> 8) + READ_UINT16(g_georgeCollisionRect + 0x00) + READ_UINT16(carFrameData + 0x06);
- int16 carY1 = (int16)(carSat->y >> 8) + READ_UINT16(g_georgeCollisionRect + 0x02) + READ_UINT16(carFrameData + 0x08);
- int16 carW = READ_UINT16(g_georgeCollisionRect + 0x04);
- int16 carH = READ_UINT16(g_georgeCollisionRect + 0x06);
+ int16 carX1 = (int16)(carSat->x >> 8) + READ_UINT16(_georgeCollisionRect + 0x00) + READ_UINT16(carFrameData + 0x06);
+ int16 carY1 = (int16)(carSat->y >> 8) + READ_UINT16(_georgeCollisionRect + 0x02) + READ_UINT16(carFrameData + 0x08);
+ int16 carW = READ_UINT16(_georgeCollisionRect + 0x04);
+ int16 carH = READ_UINT16(_georgeCollisionRect + 0x06);
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
@@ -683,7 +683,7 @@ int16 BoltEngine::playGeorge() {
if (carSat->animMode != 0)
continue;
- byte *collRect = getResolvedPtr(g_georgeSatelliteCollisionRects, sat->variant * 4);
+ byte *collRect = getResolvedPtr(_georgeSatelliteCollisionRects, sat->variant * 4);
byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRect + 0x00) + READ_UINT16(satFrameData + 0x06);
@@ -698,19 +698,19 @@ int16 BoltEngine::playGeorge() {
setGeorgeAnimMode(carSat, 1);
setSatelliteAnimMode(sat, 4, 0);
- g_georgeCollectedSatellitesNum++;
+ _georgeCollectedSatellitesNum++;
if (spawnSatellite())
- g_georgeSatelliteWait = getRandomSatelliteWait();
+ _georgeSatelliteWait = getRandomSatelliteWait();
else
- g_georgeSatelliteWait = 1;
+ _georgeSatelliteWait = 1;
- playGeorgeSound(g_georgeSatelliteSoundList[sat->variant], nullptr);
+ playGeorgeSound(_georgeSatelliteSoundList[sat->variant], nullptr);
} else if (sat->animMode == 5) {
if (carSat->animMode != 0 && carSat->animMode != 1)
continue;
- byte *collRects = getResolvedPtr(g_georgeAsteroidCollisionRects, sat->variant * 4);
+ byte *collRects = getResolvedPtr(_georgeAsteroidCollisionRects, sat->variant * 4);
byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRects + 0x00) + READ_UINT16(satFrameData + 0x06);
@@ -726,31 +726,31 @@ int16 BoltEngine::playGeorge() {
setGeorgeAnimMode(carSat, 2);
if (confirmAsteroidHitTest())
- g_georgeCollectedSatellitesNum--;
+ _georgeCollectedSatellitesNum--;
- playGeorgeSound(&g_georgeSoundCarTumble, nullptr);
+ playGeorgeSound(&_georgeSoundCarTumble, nullptr);
break;
}
}
}
if (!winSeq) {
- if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
- if (g_georgeSoundToPlay &&
- g_georgeSoundToPlay->priority > g_georgeSoundCarGoesAway.priority)
+ if (_georgeCollectedSatellitesNum >= _georgeThresholds[0]) {
+ if (_georgeSoundToPlay &&
+ _georgeSoundToPlay->priority > _georgeSoundCarGoesAway.priority)
continue;
- if (g_georgeSoundQueued &&
- g_georgeSoundQueued->priority > g_georgeSoundCarGoesAway.priority)
+ if (_georgeSoundQueued &&
+ _georgeSoundQueued->priority > _georgeSoundCarGoesAway.priority)
continue;
bool allDone = true;
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
- if (i == g_georgeCarIdx)
+ if (i == _georgeCarIdx)
continue;
if (sat->flags & 1) {
@@ -763,10 +763,10 @@ int16 BoltEngine::playGeorge() {
// We've won!
winSeq = 1;
returnCode = 0x10;
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
carSat->accelX = 0x200;
carSat->accelY = 0;
- playGeorgeSound(&g_georgeSoundCarGoesAway, nullptr);
+ playGeorgeSound(&_georgeSoundCarGoesAway, nullptr);
}
}
}
@@ -780,7 +780,7 @@ int16 BoltEngine::playGeorge() {
}
int16 BoltEngine::helpGeorge() {
- byte *firstObj = getResolvedPtr(g_georgeHelpObjects, 0);
+ byte *firstObj = getResolvedPtr(_georgeHelpObjects, 0);
byte *firstInfo = getResolvedPtr(firstObj, 0x04);
int16 curX = READ_UINT16(firstInfo + 0x06) + READ_UINT16(firstInfo + 0x0A) - 10;
int16 curY = READ_UINT16(firstInfo + 0x08) + READ_UINT16(firstInfo + 0x0C) - 10;
@@ -795,12 +795,12 @@ int16 BoltEngine::helpGeorge() {
_xp->setInactivityTimer(0);
_xp->stopSound();
- g_georgeSoundCurrent = g_georgeSoundToPlay = g_georgeSoundNext = g_georgeSoundQueued = nullptr;
+ _georgeSoundCurrent = _georgeSoundToPlay = _georgeSoundNext = _georgeSoundQueued = nullptr;
drawFlyingObjects();
for (int16 i = 0;; i++) {
- byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
if (!obj)
break;
@@ -811,7 +811,7 @@ int16 BoltEngine::helpGeorge() {
_xp->updateDisplay();
for (int16 i = 0;; i++) {
- byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
if (!obj)
break;
@@ -827,15 +827,15 @@ int16 BoltEngine::helpGeorge() {
_xp->setCursorPos(curX, curY);
_xp->setCursorColor(0, 0, 0xFF);
_xp->showCursor();
- g_georgeActiveHelpObject = getResolvedPtr(g_georgeHelpObjects, 0);
- hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+ _georgeActiveHelpObject = getResolvedPtr(_georgeHelpObjects, 0);
+ hiliteGeorgeHelpObject(_georgeActiveHelpObject, 1);
// ---- Event loop ----
while (!shouldQuit()) {
if (animPlaying) {
audioTick = maintainAudioPlay(audioTick);
if (!audioTick) {
- bool sameAsHover = (hoveredObj == g_georgeActiveHelpObject);
+ bool sameAsHover = (hoveredObj == _georgeActiveHelpObject);
hiliteGeorgeHelpObject(hoveredObj, sameAsHover ? 1 : 0);
animPlaying = 0;
}
@@ -847,16 +847,16 @@ int16 BoltEngine::helpGeorge() {
switch (eventType) {
case etTimer: {
- if (!g_georgePrevActiveHelpObject)
+ if (!_georgePrevActiveHelpObject)
break;
- if (g_georgeHelpTimer != eventData)
+ if (_georgeHelpTimer != eventData)
break;
- g_georgeHelpTimer = _xp->startTimer(500);
+ _georgeHelpTimer = _xp->startTimer(500);
- bool lit = !(READ_UINT32(g_georgePrevActiveHelpObject + 0x08) & 1u);
- hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, lit ? 1 : 0);
+ bool lit = !(READ_UINT32(_georgePrevActiveHelpObject + 0x08) & 1u);
+ hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, lit ? 1 : 0);
break;
}
@@ -867,7 +867,7 @@ int16 BoltEngine::helpGeorge() {
byte *hit = nullptr;
for (int16 i = 0;; i++) {
- byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
if (!obj)
break;
@@ -883,25 +883,25 @@ int16 BoltEngine::helpGeorge() {
}
}
- if (hit == g_georgeActiveHelpObject)
+ if (hit == _georgeActiveHelpObject)
break;
- if (g_georgeActiveHelpObject) {
- if (g_georgeActiveHelpObject != g_georgePrevActiveHelpObject) {
- bool keepLit = (g_georgeActiveHelpObject == hoveredObj && animPlaying);
+ if (_georgeActiveHelpObject) {
+ if (_georgeActiveHelpObject != _georgePrevActiveHelpObject) {
+ bool keepLit = (_georgeActiveHelpObject == hoveredObj && animPlaying);
if (!keepLit)
- hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 0);
+ hiliteGeorgeHelpObject(_georgeActiveHelpObject, 0);
}
}
- g_georgeActiveHelpObject = hit;
+ _georgeActiveHelpObject = hit;
if (!hit)
break;
- if (hit == g_georgePrevActiveHelpObject)
+ if (hit == _georgePrevActiveHelpObject)
break;
- hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+ hiliteGeorgeHelpObject(_georgeActiveHelpObject, 1);
break;
}
@@ -909,33 +909,33 @@ int16 BoltEngine::helpGeorge() {
animReady = 0;
if (animPlaying) {
- if (g_georgeHelpTimer) {
- _xp->killTimer(g_georgeHelpTimer);
- g_georgeHelpTimer = 0;
+ if (_georgeHelpTimer) {
+ _xp->killTimer(_georgeHelpTimer);
+ _georgeHelpTimer = 0;
}
- bool hovLit = (hoveredObj == g_georgeActiveHelpObject);
+ bool hovLit = (hoveredObj == _georgeActiveHelpObject);
hiliteGeorgeHelpObject(hoveredObj, hovLit ? 1 : 0);
- bool actLit = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
- hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, actLit ? 1 : 0);
+ bool actLit = (_georgePrevActiveHelpObject == _georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, actLit ? 1 : 0);
- g_georgePrevActiveHelpObject = nullptr;
+ _georgePrevActiveHelpObject = nullptr;
stopAnimation();
animPlaying = 0;
animReady = 1;
}
- if (!g_georgeActiveHelpObject)
+ if (!_georgeActiveHelpObject)
break;
- switch (READ_UINT16(g_georgeActiveHelpObject)) {
+ switch (READ_UINT16(_georgeActiveHelpObject)) {
case 0:
case 1:
- if (READ_UINT16(g_georgeActiveHelpObject) == 1) {
- g_georgeHelpActive = 0;
+ if (READ_UINT16(_georgeActiveHelpObject) == 1) {
+ _georgeHelpActive = 0;
}
- exitCode = READ_UINT16(g_georgeActiveHelpObject);
+ exitCode = READ_UINT16(_georgeActiveHelpObject);
break;
case 2:
if (animPlaying)
@@ -944,8 +944,8 @@ int16 BoltEngine::helpGeorge() {
if (animReady)
break;
- if (startAnimation(g_rtfHandle, 30)) {
- g_georgeHelpStep = 0;
+ if (startAnimation(_rtfHandle, 30)) {
+ _georgeHelpStep = 0;
animPlaying = 1;
}
@@ -974,18 +974,18 @@ int16 BoltEngine::helpGeorge() {
// Highlight hovered object, unhighlight the rest...
for (int16 i = 0;; i++) {
- byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
if (!obj)
break;
- bool isHovered = (obj == g_georgeActiveHelpObject);
+ bool isHovered = (obj == _georgeActiveHelpObject);
hiliteGeorgeHelpObject(obj, isHovered ? 1 : 0);
}
_xp->updateDisplay();
// Unhighlight all...
for (int16 i = 0;; i++) {
- byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+ byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
if (!obj)
break;
@@ -1019,40 +1019,40 @@ void BoltEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
}
void BoltEngine::advanceHelpAnimation() {
- if (g_georgeHelpStep < 0 || g_georgeHelpStep >= 4)
+ if (_georgeHelpStep < 0 || _georgeHelpStep >= 4)
return;
- bool isHovered = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
- hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, isHovered ? 1 : 0);
+ bool isHovered = (_georgePrevActiveHelpObject == _georgeActiveHelpObject);
+ hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, isHovered ? 1 : 0);
byte *newActive;
- if (!(g_georgeHelpStep & 1)) {
- newActive = getResolvedPtr(g_georgeHelpSequence, (g_georgeHelpStep >> 1) * 4);
+ if (!(_georgeHelpStep & 1)) {
+ newActive = getResolvedPtr(_georgeHelpSequence, (_georgeHelpStep >> 1) * 4);
} else {
newActive = nullptr;
}
- g_georgePrevActiveHelpObject = newActive;
- g_georgeHelpTimer = 0;
+ _georgePrevActiveHelpObject = newActive;
+ _georgeHelpTimer = 0;
- hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, 1);
+ hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, 1);
- if (!(g_georgeHelpStep & 1)) {
- g_georgeHelpTimer = _xp->startTimer(500);
+ if (!(_georgeHelpStep & 1)) {
+ _georgeHelpTimer = _xp->startTimer(500);
} else {
- if (g_georgeHelpTimer) {
- _xp->killTimer(g_georgeHelpTimer);
- g_georgeHelpTimer = 0;
+ if (_georgeHelpTimer) {
+ _xp->killTimer(_georgeHelpTimer);
+ _georgeHelpTimer = 0;
}
}
- g_georgeHelpStep++;
+ _georgeHelpStep++;
}
bool BoltEngine::spawnSatellite() {
// Count active satellites (type 3)...
int16 activeCount = 0;
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
@@ -1065,13 +1065,13 @@ bool BoltEngine::spawnSatellite() {
return false;
// Find next inactive satellite slot...
- int16 startIdx = g_georgeSatelliteSearchIdx;
+ int16 startIdx = _georgeSatelliteSearchIdx;
int16 idx = startIdx;
bool wrapped = false;
GeorgeEntityState *candidate = nullptr;
while (true) {
- GeorgeEntityState *s = g_georgeEntityList[idx];
+ GeorgeEntityState *s = _georgeEntityList[idx];
if (!s) {
wrapped = true;
idx = -1;
@@ -1085,25 +1085,25 @@ bool BoltEngine::spawnSatellite() {
return false;
}
- g_georgeSatelliteSearchIdx = idx + 1;
+ _georgeSatelliteSearchIdx = idx + 1;
// Get a shuffle-table row for this asteroid...
int16 row = getRandomAsteroidRow();
- int16 numCols = g_georgeThresholds[4];
+ int16 numCols = _georgeThresholds[4];
int16 rowBase = (numCols + 1) * row;
- int16 colIdx = g_georgeSatelliteShuffleTable[rowBase];
- byte animIdx = g_georgeSatelliteShuffleTable[rowBase + colIdx + 1];
+ int16 colIdx = _georgeSatelliteShuffleTable[rowBase];
+ byte animIdx = _georgeSatelliteShuffleTable[rowBase + colIdx + 1];
// Get the motion path...
- byte *rowPaths = getResolvedPtr(g_georgeSatellitePaths, row * 4);
+ byte *rowPaths = getResolvedPtr(_georgeSatellitePaths, row * 4);
byte *path = getResolvedPtr(rowPaths, animIdx * 4);
candidate->pathTable = path;
colIdx++;
- g_georgeSatelliteShuffleTable[rowBase] = (byte)colIdx;
+ _georgeSatelliteShuffleTable[rowBase] = (byte)colIdx;
if (colIdx >= numCols)
- g_georgeSatelliteShuffleTable[rowBase] = 0;
+ _georgeSatelliteShuffleTable[rowBase] = 0;
// Set initial position from pic's embedded coords...
candidate->pathIndex = 0;
@@ -1116,19 +1116,19 @@ bool BoltEngine::spawnSatellite() {
}
int16 BoltEngine::getRandomSatelliteWait() {
- int16 range = g_georgeThresholds[2];
- int16 topY = g_georgeThresholds[1];
+ int16 range = _georgeThresholds[2];
+ int16 topY = _georgeThresholds[1];
return _xp->getRandom(range) + topY;
}
int16 BoltEngine::getRandomAsteroidRow() {
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
int16 carY = carSat->y >> 8;
// Find which row the car is in by scanning frame Y thresholds...
- int16 numRows = g_georgeThresholds[3];
+ int16 numRows = _georgeThresholds[3];
int16 carRow = 0;
- int16 *thresholds = g_georgeSatelliteThresholds;
+ int16 *thresholds = _georgeSatelliteThresholds;
for (int16 i = 0; i < numRows; i++) {
if (thresholds[i] > carY)
@@ -1138,10 +1138,10 @@ int16 BoltEngine::getRandomAsteroidRow() {
}
// Pick a random row, retrying if too close to car's row...
- int16 exclusionBand = g_georgeThresholds[5];
+ int16 exclusionBand = _georgeThresholds[5];
int16 randRow;
do {
- randRow = _xp->getRandom(g_georgeThresholds[3]);
+ randRow = _xp->getRandom(_georgeThresholds[3]);
} while (randRow >= (carRow - exclusionBand) && randRow < (carRow + exclusionBand));
return randRow;
@@ -1149,12 +1149,12 @@ int16 BoltEngine::getRandomAsteroidRow() {
bool BoltEngine::confirmAsteroidHitTest() {
GeorgeEntityState *candidate = nullptr;
- int16 startIdx = g_georgeHitSearchIdx;
+ int16 startIdx = _georgeHitSearchIdx;
int16 idx = startIdx;
bool wrapped = false;
while (true) {
- GeorgeEntityState *s = g_georgeEntityList[idx];
+ GeorgeEntityState *s = _georgeEntityList[idx];
if (!s) {
wrapped = true;
idx = -1;
@@ -1168,7 +1168,7 @@ bool BoltEngine::confirmAsteroidHitTest() {
return false;
}
- g_georgeHitSearchIdx = idx + 1;
+ _georgeHitSearchIdx = idx + 1;
setSatelliteAnimMode(candidate, 3, candidate->variant);
@@ -1179,7 +1179,7 @@ bool BoltEngine::spawnAsteroid() {
// Count active asteroids (type 5)...
int16 activeCount = 0;
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
@@ -1192,13 +1192,13 @@ bool BoltEngine::spawnAsteroid() {
return false;
// Find next inactive asteroid slot...
- int16 startIdx = g_georgeAsteroidSearchIdx;
+ int16 startIdx = _georgeAsteroidSearchIdx;
int16 idx = startIdx;
bool wrapped = false;
GeorgeEntityState *candidate = nullptr;
while (true) {
- GeorgeEntityState *s = g_georgeEntityList[idx];
+ GeorgeEntityState *s = _georgeEntityList[idx];
if (!s) {
wrapped = true;
idx = -1;
@@ -1212,24 +1212,24 @@ bool BoltEngine::spawnAsteroid() {
return false;
}
- g_georgeAsteroidSearchIdx = idx + 1;
+ _georgeAsteroidSearchIdx = idx + 1;
// Get shuffle table row for this asteroid...
int16 row = getAsteroidRow();
- int16 numCols = g_georgeThresholds[10];
+ int16 numCols = _georgeThresholds[10];
int16 rowBase = (numCols + 1) * row;
- int16 colIdx = g_georgeAsteroidShuffleTable[rowBase];
- byte animIdx = g_georgeAsteroidShuffleTable[rowBase + colIdx + 1];
+ int16 colIdx = _georgeAsteroidShuffleTable[rowBase];
+ byte animIdx = _georgeAsteroidShuffleTable[rowBase + colIdx + 1];
// Assign motion path...
- byte *rowPaths = getResolvedPtr(g_georgeAsteroidPaths, row * 4);
+ byte *rowPaths = getResolvedPtr(_georgeAsteroidPaths, row * 4);
byte *path = getResolvedPtr(rowPaths, animIdx * 4);
candidate->pathTable = path;
colIdx++;
- g_georgeAsteroidShuffleTable[rowBase] = (byte)colIdx;
+ _georgeAsteroidShuffleTable[rowBase] = (byte)colIdx;
if (colIdx >= numCols)
- g_georgeAsteroidShuffleTable[rowBase] = 0;
+ _georgeAsteroidShuffleTable[rowBase] = 0;
// Set initial position from pic's embedded coords...
candidate->pathIndex = 0;
@@ -1242,19 +1242,19 @@ bool BoltEngine::spawnAsteroid() {
}
int16 BoltEngine::getRandomAsteroidWait() {
- int16 base = g_georgeThresholds[7];
- int16 range = g_georgeThresholds[8];
+ int16 base = _georgeThresholds[7];
+ int16 range = _georgeThresholds[8];
return _xp->getRandom(range) + base;
}
int16 BoltEngine::getAsteroidRow() {
- GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+ GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
int16 carY = carSat->y >> 8;
// Find which row the car is in by scanning frame Y thresholds...
- int16 numRows = g_georgeThresholds[9];
+ int16 numRows = _georgeThresholds[9];
int16 row = 0;
- int16 *thresholds = g_georgeAsteroidThresholds;
+ int16 *thresholds = _georgeAsteroidThresholds;
for (int16 i = 0; i < numRows; i++) {
if (thresholds[i] > carY)
@@ -1269,7 +1269,7 @@ int16 BoltEngine::getAsteroidRow() {
void BoltEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
entity->animMode = mode;
entity->variant = 0;
- entity->animTable = g_georgeCarPics[mode];
+ entity->animTable = _georgeCarPics[mode];
entity->frameIndex = 0;
entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
}
@@ -1284,7 +1284,7 @@ void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int
entity->animMode = mode;
entity->variant = variant;
- entity->animTable = getResolvedPtr(g_georgeSatelliteGfx, variant * 4);
+ entity->animTable = getResolvedPtr(_georgeSatelliteGfx, variant * 4);
entity->frameIndex = 0;
entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
}
@@ -1292,7 +1292,7 @@ void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int
void BoltEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
entity->animMode = mode;
entity->variant = variant;
- entity->animTable = getResolvedPtr(g_georgeAsteroidGfx, variant * 4);
+ entity->animTable = getResolvedPtr(_georgeAsteroidGfx, variant * 4);
entity->frameIndex = 0;
entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
}
@@ -1301,7 +1301,7 @@ void BoltEngine::drawFlyingObjects() {
_xp->fillDisplay(0, stFront);
for (int16 i = 0;; i++) {
- GeorgeEntityState *sat = g_georgeEntityList[i];
+ GeorgeEntityState *sat = _georgeEntityList[i];
if (!sat)
break;
@@ -1321,25 +1321,25 @@ void BoltEngine::getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *o
outInfo->data = memberAddr(boltLib, member);
outInfo->size = memberSize(boltLib, member);
outInfo->priority = priority;
- outInfo->channel = g_georgeSoundChannelCounter++;
+ outInfo->channel = _georgeSoundChannelCounter++;
}
void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
// If a queued sound exists and has higher priority than new sound, don't replace it...
- if (newSound && g_georgeSoundQueued) {
- if (g_georgeSoundQueued->priority > newSound->priority)
+ if (newSound && _georgeSoundQueued) {
+ if (_georgeSoundQueued->priority > newSound->priority)
return;
}
// Queue the new sounds...
- g_georgeSoundQueued = newSound;
- g_georgeSoundNext = nextSound;
+ _georgeSoundQueued = newSound;
+ _georgeSoundNext = nextSound;
// If currently playing sound has higher priority than new one, keep it...
- if (g_georgeSoundToPlay && newSound) {
- if (g_georgeSoundToPlay->priority > g_georgeSoundQueued->priority) {
+ if (_georgeSoundToPlay && newSound) {
+ if (_georgeSoundToPlay->priority > _georgeSoundQueued->priority) {
// If something is queued but nothing playing, kick off playback now...
- if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+ if (_georgeSoundQueued && !_georgeSoundToPlay)
updateGeorgeSound();
return;
@@ -1348,46 +1348,46 @@ void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
// New sound wins...
_xp->stopSound();
- g_georgeSoundCurrent = nullptr;
- g_georgeSoundToPlay = nullptr;
+ _georgeSoundCurrent = nullptr;
+ _georgeSoundToPlay = nullptr;
// If something is queued but nothing playing, kick off playback now...
- if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+ if (_georgeSoundQueued && !_georgeSoundToPlay)
updateGeorgeSound();
}
void BoltEngine::updateGeorgeSound() {
int16 playBoth = 0;
- if (g_georgeSoundCurrent != nullptr) {
+ if (_georgeSoundCurrent != nullptr) {
// Current sound still set, promote directly to play slot...
- g_georgeSoundToPlay = g_georgeSoundCurrent;
- } else if (g_georgeSoundQueued != nullptr) {
+ _georgeSoundToPlay = _georgeSoundCurrent;
+ } else if (_georgeSoundQueued != nullptr) {
// Nothing current, promote queued sound...
- g_georgeSoundToPlay = g_georgeSoundQueued;
- g_georgeSoundCurrent = g_georgeSoundNext;
+ _georgeSoundToPlay = _georgeSoundQueued;
+ _georgeSoundCurrent = _georgeSoundNext;
- if (g_georgeSoundNext != nullptr)
+ if (_georgeSoundNext != nullptr)
playBoth = 1;
- g_georgeSoundNext = nullptr;
- g_georgeSoundQueued = nullptr;
+ _georgeSoundNext = nullptr;
+ _georgeSoundQueued = nullptr;
} else {
- g_georgeSoundToPlay = nullptr;
+ _georgeSoundToPlay = nullptr;
}
- if (g_georgeSoundToPlay == nullptr)
+ if (_georgeSoundToPlay == nullptr)
return;
// Play primary sound...
- _xp->playSound(g_georgeSoundToPlay->data, g_georgeSoundToPlay->size, 22050);
+ _xp->playSound(_georgeSoundToPlay->data, _georgeSoundToPlay->size, 22050);
if (!playBoth)
return;
// Queue secondary sound twice...
- _xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
- _xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
+ _xp->playSound(_georgeSoundCurrent->data, _georgeSoundCurrent->size, 22050);
+ _xp->playSound(_georgeSoundCurrent->data, _georgeSoundCurrent->size, 22050);
}
} // End of namespace Bolt
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index ce66c678125..8efc0886589 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -26,77 +26,77 @@
namespace Bolt {
void BoltEngine::playSoundMapHuck(int16 memberId) {
- byte *soundData = getBOLTMember(g_huckBoltLib, memberId);
- uint32 soundSize = memberSize(g_huckBoltLib, memberId);
+ byte *soundData = getBOLTMember(_huckBoltLib, memberId);
+ uint32 soundSize = memberSize(_huckBoltLib, memberId);
if (soundData) {
_xp->playSound(soundData, soundSize, 22050);
- g_huckSoundPlaying++;
+ _huckSoundPlaying++;
}
}
void BoltEngine::waitSoundMapHuck() {
- if (!g_huckSoundPlaying)
+ if (!_huckSoundPlaying)
return;
uint32 dummy;
while (_xp->getEvent(etSound, &dummy) == etEmpty);
- g_huckSoundPlaying--;
+ _huckSoundPlaying--;
}
void BoltEngine::setHuckColors(int16 which) {
if (which == 0) {
- int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
- _xp->setPalette(count, g_huckPalRange[0], g_huckPalHighlight0);
+ int16 count = _huckPalRange[1] - _huckPalRange[0] + 1;
+ _xp->setPalette(count, _huckPalRange[0], _huckPalHighlight0);
} else if (which == 1) {
- int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
- _xp->setPalette(count, g_huckPalRange[4], g_huckPalHighlight1);
+ int16 count = _huckPalRange[5] - _huckPalRange[4] + 1;
+ _xp->setPalette(count, _huckPalRange[4], _huckPalHighlight1);
}
}
void BoltEngine::restoreHuckColors(int16 which) {
if (which == 0) {
- int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
- _xp->setPalette(count, g_huckPalRange[0], g_huckPalSave0);
+ int16 count = _huckPalRange[1] - _huckPalRange[0] + 1;
+ _xp->setPalette(count, _huckPalRange[0], _huckPalSave0);
} else if (which == 1) {
- int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
- _xp->setPalette(count, g_huckPalRange[4], g_huckPalSave1);
+ int16 count = _huckPalRange[5] - _huckPalRange[4] + 1;
+ _xp->setPalette(count, _huckPalRange[4], _huckPalSave1);
}
}
void BoltEngine::startHuckShuffleTimer() {
- if (g_huckShuffleTimer) {
- _xp->killTimer(g_huckShuffleTimer);
- g_huckShuffleTimer = 0;
+ if (_huckShuffleTimer) {
+ _xp->killTimer(_huckShuffleTimer);
+ _huckShuffleTimer = 0;
}
- if (g_huckState.giftCount > 2) {
- int16 speed = READ_UINT16(g_huckGiftPic + g_huckScrollOffset * 2 + 0x6E);
+ if (_huckState.giftCount > 2) {
+ int16 speed = READ_UINT16(_huckGiftPic + _huckScrollOffset * 2 + 0x6E);
int32 ms = (int32)speed * 1000 / 60;
- g_huckShuffleTimer = _xp->startTimer((int16)ms);
+ _huckShuffleTimer = _xp->startTimer((int16)ms);
}
}
void BoltEngine::drawGift(int16 slot) {
- byte *giftSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+ byte *giftSprite = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
int16 sprStride = READ_UINT16(giftSprite + 0x0A);
int16 sprHeight = READ_UINT16(giftSprite + 0x0C);
- memset(g_huckScratchPic.pixelData, 0, (int32)sprStride * sprHeight);
+ memset(_huckScratchPic.pixelData, 0, (int32)sprStride * sprHeight);
- int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
- int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+ int16 slotX = READ_UINT16(_huckGiftPic + slot * 2 + 0x3E);
+ int16 slotY = READ_UINT16(_huckGiftPic + slot * 2 + 0x0E);
Common::Rect targetRect(slotX, slotY, slotX + sprHeight, slotY + sprStride);
- int16 giftCount = READ_UINT16(g_huckGiftPic);
+ int16 giftCount = READ_UINT16(_huckGiftPic);
for (int16 i = 0; i < giftCount; i++) {
- if (g_huckState.drawTable2[i] != 0)
+ if (_huckState.drawTable2[i] != 0)
continue;
- byte *otherSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+ byte *otherSprite = memberAddr(_huckBoltLib, _huckState.drawTable1[i]);
int16 otherStride = READ_UINT16(otherSprite + 0x0A);
int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
- int16 ox = READ_UINT16(g_huckGiftPic + i * 2 + 0x3E);
- int16 oy = READ_UINT16(g_huckGiftPic + i * 2 + 0x0E);
+ int16 ox = READ_UINT16(_huckGiftPic + i * 2 + 0x3E);
+ int16 oy = READ_UINT16(_huckGiftPic + i * 2 + 0x0E);
Common::Rect otherRect(ox, oy, ox + otherHeight, oy + otherStride);
Common::Rect isect;
@@ -106,113 +106,113 @@ void BoltEngine::drawGift(int16 slot) {
byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
byte *src = srcPixels + (isect.left - ox) * otherStride + (isect.top - oy);
- byte *dst = g_huckScratchPic.pixelData + (isect.left - slotX) * sprStride + (isect.top - slotY);
+ byte *dst = _huckScratchPic.pixelData + (isect.left - slotX) * sprStride + (isect.top - slotY);
int16 blitWidth = isect.bottom - isect.top;
int16 blitHeight = isect.right - isect.left;
_xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
}
- if ((g_huckState.hasCycle && g_huckState.selected1Slot == slot) || (g_huckState.selectionPending && g_huckState.selected2Slot == slot)) {
- byte *hlSprite = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x06));
+ if ((_huckState.hasCycle && _huckState.selected1Slot == slot) || (_huckState.selectionPending && _huckState.selected2Slot == slot)) {
+ byte *hlSprite = memberAddr(_huckBoltLib, READ_UINT16(_huckGiftPic + 0x06));
int16 hlStride = READ_UINT16(hlSprite + 0x0A);
int16 hlHeight = READ_UINT16(hlSprite + 0x0C);
byte *hlPixels = getResolvedPtr(hlSprite, 0x12);
- int16 hlOffsetX = READ_UINT16(g_huckGiftPic + 0x0A);
- int16 hlOffsetY = READ_UINT16(g_huckGiftPic + 0x08);
+ int16 hlOffsetX = READ_UINT16(_huckGiftPic + 0x0A);
+ int16 hlOffsetY = READ_UINT16(_huckGiftPic + 0x08);
- byte *dst = g_huckScratchPic.pixelData + hlOffsetX * sprStride + hlOffsetY;
+ byte *dst = _huckScratchPic.pixelData + hlOffsetX * sprStride + hlOffsetY;
_xp->maskBlit(hlPixels, hlStride, dst, sprStride, hlStride, hlHeight);
}
- g_huckScratchPic.width = sprStride;
- g_huckScratchPic.height = sprHeight;
- g_huckScratchPic.palette = nullptr;
- g_huckScratchPic.flags = 0;
+ _huckScratchPic.width = sprStride;
+ _huckScratchPic.height = sprHeight;
+ _huckScratchPic.palette = nullptr;
+ _huckScratchPic.flags = 0;
- _xp->displayPic(&g_huckScratchPic, slotY, slotX, stFront);
+ _xp->displayPic(&_huckScratchPic, slotY, slotX, stFront);
}
void BoltEngine::drawHuckGifts() {
_xp->fillDisplay(0, stFront);
- int16 giftCount = READ_UINT16(g_huckGiftPic);
+ int16 giftCount = READ_UINT16(_huckGiftPic);
for (int16 slot = 0; slot < giftCount; slot++) {
- if (g_huckState.drawTable2[slot] != 0)
+ if (_huckState.drawTable2[slot] != 0)
continue;
- byte *gifPtr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
- int16 x = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
- int16 y = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+ byte *gifPtr = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
+ int16 x = READ_UINT16(_huckGiftPic + slot * 2 + 0x0E);
+ int16 y = READ_UINT16(_huckGiftPic + slot * 2 + 0x3E);
displayPic(gifPtr, x, y, stFront);
}
- if (g_huckState.hasCycle) {
- int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
- int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
- int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
- byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+ if (_huckState.hasCycle) {
+ int16 x = READ_UINT16(_huckGiftPic + _huckState.selected1Slot * 2 + 0x0E) + READ_UINT16(_huckGiftPic + 0x08);
+ int16 y = READ_UINT16(_huckGiftPic + _huckState.selected1Slot * 2 + 0x3E) + READ_UINT16(_huckGiftPic + 0x0A);
+ int16 hlMember = READ_UINT16(_huckGiftPic + 0x06);
+ byte *hlPtr = memberAddr(_huckBoltLib, hlMember);
displayPic(hlPtr, x, y, stFront);
}
- if (g_huckState.selectionPending) {
- int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
- int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
- int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
- byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+ if (_huckState.selectionPending) {
+ int16 x = READ_UINT16(_huckGiftPic + _huckState.selected2Slot * 2 + 0x0E) + READ_UINT16(_huckGiftPic + 0x08);
+ int16 y = READ_UINT16(_huckGiftPic + _huckState.selected2Slot * 2 + 0x3E) + READ_UINT16(_huckGiftPic + 0x0A);
+ int16 hlMember = READ_UINT16(_huckGiftPic + 0x06);
+ byte *hlPtr = memberAddr(_huckBoltLib, hlMember);
displayPic(hlPtr, x, y, stFront);
}
}
void BoltEngine::checkHuckLevelComplete() {
- if (g_huckState.giftCount == 2) {
+ if (_huckState.giftCount == 2) {
// Last pair matched, level complete!
- g_huckExitFlag = 1;
- g_huckState.levelComplete = 1;
- g_huckState.levelNumber++;
+ _huckExitFlag = 1;
+ _huckState.levelComplete = 1;
+ _huckState.levelNumber++;
- if (g_huckState.levelNumber > 10)
- g_huckState.levelNumber = 10;
+ if (_huckState.levelNumber > 10)
+ _huckState.levelNumber = 10;
_xp->fillDisplay(0, stFront);
} else {
// More pairs remain, redraw the matched slots as empty...
- drawGift(g_huckState.selected1Slot);
- drawGift(g_huckState.selected2Slot);
+ drawGift(_huckState.selected1Slot);
+ drawGift(_huckState.selected2Slot);
}
- g_huckState.selected1Slot = -1;
- g_huckState.selected2Slot = -1;
- g_huckState.giftCount -= 2;
+ _huckState.selected1Slot = -1;
+ _huckState.selected2Slot = -1;
+ _huckState.giftCount -= 2;
_xp->updateDisplay();
waitSoundMapHuck();
- g_huckScrollOffset++;
+ _huckScrollOffset++;
startHuckShuffleTimer();
}
bool BoltEngine::initHuckDisplay() {
- byte *palPtr = memberAddr(g_huckBoltLib, READ_UINT16(g_huckBgPic));
+ byte *palPtr = memberAddr(_huckBoltLib, READ_UINT16(_huckBgPic));
- g_huckHotSpotCount = 0;
- g_huckScreensaverTimer = 0;
- g_huckBlinkTimer = 0;
- g_huckExitFlag = 0;
+ _huckHotSpotCount = 0;
+ _huckScreensaverTimer = 0;
+ _huckBlinkTimer = 0;
+ _huckExitFlag = 0;
int32 maxArea = 0;
- int16 giftCount = READ_UINT16(g_huckGiftPic);
+ int16 giftCount = READ_UINT16(_huckGiftPic);
for (int16 i = 0; i < giftCount; i++) {
- byte *spr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+ byte *spr = memberAddr(_huckBoltLib, _huckState.drawTable1[i]);
int32 area = (int32)READ_UINT16(spr + 0x0A) * READ_UINT16(spr + 0x0C);
if (area > maxArea)
maxArea = area;
}
- g_huckScratchPic.pixelData = (byte *)_xp->allocMem(maxArea);
- if (!g_huckScratchPic.pixelData)
+ _huckScratchPic.pixelData = (byte *)_xp->allocMem(maxArea);
+ if (!_huckScratchPic.pixelData)
return false;
uint32 dummy;
@@ -221,81 +221,81 @@ bool BoltEngine::initHuckDisplay() {
_xp->stopCycle();
_xp->setTransparency(false);
displayColors(palPtr, stBack, 1);
- displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stFront);
+ displayPic(_huckBgDisplayPic, _displayX, _displayY, stFront);
_xp->updateDisplay();
_xp->setTransparency(true);
displayColors(palPtr, stFront, 0);
displayColors(palPtr, stBack, 1);
- displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stBack);
+ displayPic(_huckBgDisplayPic, _displayX, _displayY, stBack);
_xp->fillDisplay(0, stFront);
_xp->updateDisplay();
drawHuckGifts();
_xp->updateDisplay();
- if (g_huckState.hasCycle) {
+ if (_huckState.hasCycle) {
XPCycleState cycleSpec[4];
- byte *cycleData = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x0C));
+ byte *cycleData = memberAddr(_huckBoltLib, READ_UINT16(_huckGiftPic + 0x0C));
boltCycleToXPCycle(cycleData, cycleSpec);
_xp->startCycle(cycleSpec);
}
- g_huckCursorY = 0x78;
- g_huckCursorX = 0xC0;
+ _huckCursorY = 0x78;
+ _huckCursorX = 0xC0;
_xp->setCursorPos(0x78, 0xC0);
_xp->setCursorColor(0xFF, 0xFF, 0xFF);
_xp->showCursor();
// Read palette ranges from gift pic data...
- g_huckPalRange[0] = READ_UINT16(g_huckGiftPic + 0x8C) + 0x80;
- g_huckPalRange[1] = READ_UINT16(g_huckGiftPic + 0x8E) + 0x80;
- g_huckPalRange[2] = READ_UINT16(g_huckGiftPic + 0x90) + 0x80;
- g_huckPalRange[3] = READ_UINT16(g_huckGiftPic + 0x92) + 0x80;
- g_huckPalRange[4] = READ_UINT16(g_huckGiftPic + 0x9E) + 0x80;
- g_huckPalRange[5] = READ_UINT16(g_huckGiftPic + 0xA0) + 0x80;
- g_huckPalRange[6] = READ_UINT16(g_huckGiftPic + 0xA2) + 0x80;
- g_huckPalRange[7] = READ_UINT16(g_huckGiftPic + 0xA4) + 0x80;
+ _huckPalRange[0] = READ_UINT16(_huckGiftPic + 0x8C) + 0x80;
+ _huckPalRange[1] = READ_UINT16(_huckGiftPic + 0x8E) + 0x80;
+ _huckPalRange[2] = READ_UINT16(_huckGiftPic + 0x90) + 0x80;
+ _huckPalRange[3] = READ_UINT16(_huckGiftPic + 0x92) + 0x80;
+ _huckPalRange[4] = READ_UINT16(_huckGiftPic + 0x9E) + 0x80;
+ _huckPalRange[5] = READ_UINT16(_huckGiftPic + 0xA0) + 0x80;
+ _huckPalRange[6] = READ_UINT16(_huckGiftPic + 0xA2) + 0x80;
+ _huckPalRange[7] = READ_UINT16(_huckGiftPic + 0xA4) + 0x80;
// Save original palette ranges, load highlight palette ranges...
- _xp->getPalette(g_huckPalRange[0], g_huckPalRange[1] - g_huckPalRange[0] + 1, g_huckPalSave0);
- _xp->getPalette(g_huckPalRange[2], g_huckPalRange[3] - g_huckPalRange[2] + 1, g_huckPalHighlight0);
- _xp->getPalette(g_huckPalRange[4], g_huckPalRange[5] - g_huckPalRange[4] + 1, g_huckPalSave1);
- _xp->getPalette(g_huckPalRange[6], g_huckPalRange[7] - g_huckPalRange[6] + 1, g_huckPalHighlight1);
+ _xp->getPalette(_huckPalRange[0], _huckPalRange[1] - _huckPalRange[0] + 1, _huckPalSave0);
+ _xp->getPalette(_huckPalRange[2], _huckPalRange[3] - _huckPalRange[2] + 1, _huckPalHighlight0);
+ _xp->getPalette(_huckPalRange[4], _huckPalRange[5] - _huckPalRange[4] + 1, _huckPalSave1);
+ _xp->getPalette(_huckPalRange[6], _huckPalRange[7] - _huckPalRange[6] + 1, _huckPalHighlight1);
return true;
}
bool BoltEngine::loadHuckResources() {
- int16 stateIdx = g_huckState.levelNumber - 1;
+ int16 stateIdx = _huckState.levelNumber - 1;
int16 giftGroupId = (stateIdx << 10) + 0x100;
- g_huckGiftGroupId = giftGroupId;
+ _huckGiftGroupId = giftGroupId;
- if (!getBOLTGroup(g_huckBoltLib, giftGroupId, 1))
+ if (!getBOLTGroup(_huckBoltLib, giftGroupId, 1))
return false;
- g_huckGiftPic = memberAddr(g_huckBoltLib, giftGroupId);
+ _huckGiftPic = memberAddr(_huckBoltLib, giftGroupId);
for (int16 i = 0; i < 11; i++) {
- int16 *speed = (int16 *)(g_huckGiftPic + 0x6E + i * 2);
+ int16 *speed = (int16 *)(_huckGiftPic + 0x6E + i * 2);
if (*speed < 30)
*speed = 30;
}
// Load background display pic...
- int16 bgMember = (g_displayMode != 0) ? READ_UINT16(g_huckGiftPic + 4) : READ_UINT16(g_huckGiftPic + 2);
- g_huckBgDisplayPic = memberAddr(g_huckBoltLib, bgMember);
+ int16 bgMember = (_displayMode != 0) ? READ_UINT16(_huckGiftPic + 4) : READ_UINT16(_huckGiftPic + 2);
+ _huckBgDisplayPic = memberAddr(_huckBoltLib, bgMember);
// Determine variant...
- int16 slot = g_huckState.slotIndex[stateIdx];
- int16 variant = g_huckGlobal[stateIdx * 3 + slot];
+ int16 slot = _huckState.slotIndex[stateIdx];
+ int16 variant = _huckGlobal[stateIdx * 3 + slot];
int16 variantGroupId = ((stateIdx * 4 + variant + 1) << 8) + 0x100;
- g_huckVariantGroupId = variantGroupId;
+ _huckVariantGroupId = variantGroupId;
- if (!getBOLTGroup(g_huckBoltLib, variantGroupId, 1))
+ if (!getBOLTGroup(_huckBoltLib, variantGroupId, 1))
return false;
- g_huckBgPic = memberAddr(g_huckBoltLib, variantGroupId);
+ _huckBgPic = memberAddr(_huckBoltLib, variantGroupId);
return true;
}
@@ -303,54 +303,54 @@ bool BoltEngine::loadHuckResources() {
void BoltEngine::unloadHuckResources() {
_xp->stopCycle();
_xp->hideCursor();
- freeBOLTGroup(g_huckBoltLib, g_huckVariantGroupId, 1);
- freeBOLTGroup(g_huckBoltLib, g_huckGiftGroupId, 1);
+ freeBOLTGroup(_huckBoltLib, _huckVariantGroupId, 1);
+ freeBOLTGroup(_huckBoltLib, _huckGiftGroupId, 1);
}
bool BoltEngine::initHuckLevel() {
// Advance slot variant (wraps 0..2)
- int16 stateIdx = g_huckState.levelNumber - 1;
- g_huckState.slotIndex[stateIdx]++;
- if (g_huckState.slotIndex[stateIdx] >= 3)
- g_huckState.slotIndex[stateIdx] = 0;
+ int16 stateIdx = _huckState.levelNumber - 1;
+ _huckState.slotIndex[stateIdx]++;
+ if (_huckState.slotIndex[stateIdx] >= 3)
+ _huckState.slotIndex[stateIdx] = 0;
if (!loadHuckResources())
return false;
- g_huckState.giftCount = READ_UINT16(g_huckGiftPic);
- g_huckState.levelComplete = 0;
- g_huckState.hasCycle = 0;
- g_huckState.selectionPending = 0;
- g_huckState.selected1Slot = -1;
- g_huckState.selected2Slot = -1;
+ _huckState.giftCount = READ_UINT16(_huckGiftPic);
+ _huckState.levelComplete = 0;
+ _huckState.hasCycle = 0;
+ _huckState.selectionPending = 0;
+ _huckState.selected1Slot = -1;
+ _huckState.selected2Slot = -1;
for (int16 i = 0; i < 24; i++) {
- g_huckState.drawTable1[i] = 0;
- g_huckState.drawTable2[i] = 0;
+ _huckState.drawTable1[i] = 0;
+ _huckState.drawTable2[i] = 0;
}
- int16 baseId = READ_UINT16(g_huckBgPic + 2);
+ int16 baseId = READ_UINT16(_huckBgPic + 2);
// Distribute gift pairs into random empty slots...
int16 count = 0;
- int16 giftCount = READ_UINT16(g_huckGiftPic);
+ int16 giftCount = READ_UINT16(_huckGiftPic);
while (count < giftCount) {
int16 slot = _xp->getRandom(giftCount);
- while (g_huckState.drawTable1[slot] > 0) {
+ while (_huckState.drawTable1[slot] > 0) {
slot++;
if (slot >= giftCount)
slot = 0;
}
// Pairs share the same member ID
- g_huckState.drawTable1[slot] = count / 2 + baseId;
+ _huckState.drawTable1[slot] = count / 2 + baseId;
count++;
}
if (!initHuckDisplay())
return false;
- g_huckScrollOffset = 0;
+ _huckScrollOffset = 0;
startHuckShuffleTimer();
return true;
}
@@ -362,8 +362,8 @@ bool BoltEngine::resumeHuckLevel() {
if (!initHuckDisplay())
return false;
- if (g_huckState.giftCount > 2) {
- g_huckScrollOffset = (READ_UINT16(g_huckGiftPic) - g_huckState.giftCount) >> 1;
+ if (_huckState.giftCount > 2) {
+ _huckScrollOffset = (READ_UINT16(_huckGiftPic) - _huckState.giftCount) >> 1;
startHuckShuffleTimer();
}
@@ -372,26 +372,26 @@ bool BoltEngine::resumeHuckLevel() {
bool BoltEngine::initHuck() {
_xp->randomize();
- g_huckSoundPlaying = 0;
+ _huckSoundPlaying = 0;
- if (!vLoad(&g_huckGlobal, "HuckGlobal")) {
+ if (!vLoad(&_huckGlobal, "HuckGlobal")) {
// First run, generate random variant permutations for all 10 levels...
for (int16 level = 0; level < 10; level++) {
- g_huckState.slotIndex[level] = -1;
+ _huckState.slotIndex[level] = -1;
// Fill row with sentinel value 3...
for (int16 j = 0; j < 3; j++)
- g_huckGlobal[level * 3 + j] = 3;
+ _huckGlobal[level * 3 + j] = 3;
// Place variants 0, 1, 2 into random empty slots...
for (int16 variant = 0; variant < 3; variant++) {
int16 slot = _xp->getRandom(3);
- while (g_huckGlobal[level * 3 + slot] != 3) {
+ while (_huckGlobal[level * 3 + slot] != 3) {
slot++;
if (slot >= 3)
slot = 0;
}
- g_huckGlobal[level * 3 + slot] = variant;
+ _huckGlobal[level * 3 + slot] = variant;
}
}
}
@@ -399,38 +399,38 @@ bool BoltEngine::initHuck() {
byte huckStateBuf[0x86] = { 0 };
if (!vLoad(&huckStateBuf, "Huck")) {
- g_huckState.levelNumber = 1;
+ _huckState.levelNumber = 1;
return initHuckLevel();
}
Common::SeekableReadStream *huckStateReadStream = new Common::MemoryReadStream(huckStateBuf, sizeof(huckStateBuf), DisposeAfterUse::NO);
- g_huckState.levelNumber = huckStateReadStream->readSint16BE();
+ _huckState.levelNumber = huckStateReadStream->readSint16BE();
for (int i = 0; i < 10; i++)
- g_huckState.slotIndex[i] = huckStateReadStream->readSint16BE();
+ _huckState.slotIndex[i] = huckStateReadStream->readSint16BE();
- g_huckState.levelComplete = huckStateReadStream->readSint16BE();
+ _huckState.levelComplete = huckStateReadStream->readSint16BE();
for (int i = 0; i < 24; i++)
- g_huckState.drawTable1[i] = huckStateReadStream->readSint16BE();
+ _huckState.drawTable1[i] = huckStateReadStream->readSint16BE();
for (int i = 0; i < 24; i++)
- g_huckState.drawTable2[i] = huckStateReadStream->readSint16BE();
+ _huckState.drawTable2[i] = huckStateReadStream->readSint16BE();
- g_huckState.giftCount = huckStateReadStream->readSint16BE();
- g_huckState.hasCycle = huckStateReadStream->readSint16BE();
- g_huckState.selectionPending = huckStateReadStream->readSint16BE();
- g_huckState.selected1Slot = huckStateReadStream->readSint16BE();
- g_huckState.selected2Slot = huckStateReadStream->readSint16BE();
- g_huckState.selected1SpriteId = huckStateReadStream->readSint16BE();
- g_huckState.selected2SpriteId = huckStateReadStream->readSint16BE();
+ _huckState.giftCount = huckStateReadStream->readSint16BE();
+ _huckState.hasCycle = huckStateReadStream->readSint16BE();
+ _huckState.selectionPending = huckStateReadStream->readSint16BE();
+ _huckState.selected1Slot = huckStateReadStream->readSint16BE();
+ _huckState.selected2Slot = huckStateReadStream->readSint16BE();
+ _huckState.selected1SpriteId = huckStateReadStream->readSint16BE();
+ _huckState.selected2SpriteId = huckStateReadStream->readSint16BE();
// Sanity check: should be exactly 0x86
assert(huckStateReadStream->pos() == 0x86);
delete huckStateReadStream;
- if (g_huckState.levelComplete)
+ if (_huckState.levelComplete)
return initHuckLevel();
else
return resumeHuckLevel();
@@ -446,50 +446,50 @@ void BoltEngine::huckToggleBlinking(int16 *state, int16 which) {
void BoltEngine::huckUpdateHotSpots(int16 x, int16 y) {
Common::Rect helpRect(
- READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
- READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+ READ_UINT16(_huckGiftPic + 0x84), READ_UINT16(_huckGiftPic + 0x88),
+ READ_UINT16(_huckGiftPic + 0x86), READ_UINT16(_huckGiftPic + 0x8A));
Common::Rect exitRect(
- READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
- READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+ READ_UINT16(_huckGiftPic + 0x96), READ_UINT16(_huckGiftPic + 0x9A),
+ READ_UINT16(_huckGiftPic + 0x98), READ_UINT16(_huckGiftPic + 0x9C));
if (helpRect.contains(x, y)) {
- if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+ if (!_huckScreensaverTimer && !_huckHotSpotCount)
setHuckColors(0);
} else {
- if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+ if (!_huckScreensaverTimer && !_huckHotSpotCount)
restoreHuckColors(0);
}
if (exitRect.contains(x, y)) {
- if (!g_huckBlinkTimer)
+ if (!_huckBlinkTimer)
setHuckColors(1);
return;
} else {
- if (!g_huckBlinkTimer)
+ if (!_huckBlinkTimer)
restoreHuckColors(1);
}
}
int16 BoltEngine::findGift(int16 x, int16 y) {
- byte *sprite0 = memberAddr(g_huckBoltLib, g_huckState.drawTable1[0]);
+ byte *sprite0 = memberAddr(_huckBoltLib, _huckState.drawTable1[0]);
int16 sprH = READ_UINT16(sprite0 + 0x0A);
int16 sprW = READ_UINT16(sprite0 + 0x0C);
- int16 giftCount = READ_UINT16(g_huckGiftPic);
+ int16 giftCount = READ_UINT16(_huckGiftPic);
// Iterate in reverse, topmost gift wins...
for (int16 slot = giftCount - 1; slot >= 0; slot--) {
- if (g_huckState.drawTable2[slot] != 0)
+ if (_huckState.drawTable2[slot] != 0)
continue;
- int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
- int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+ int16 slotY = READ_UINT16(_huckGiftPic + slot * 2 + 0x3E);
+ int16 slotX = READ_UINT16(_huckGiftPic + slot * 2 + 0x0E);
int16 slotY2 = slotY + sprH - 1;
int16 slotX2 = slotX + sprW - 1;
- byte *sprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+ byte *sprite = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
if (y < slotY || y > slotY2)
continue;
@@ -510,36 +510,36 @@ bool BoltEngine::handleGiftSelect(int16 x, int16 y) {
if (slot == -1)
return false;
- if (!g_huckState.hasCycle) {
+ if (!_huckState.hasCycle) {
// No selection yet, select first gift...
- g_huckState.hasCycle = 1;
- g_huckState.selected1SpriteId = g_huckState.drawTable1[slot];
- g_huckState.selected1Slot = slot;
- playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xA8));
+ _huckState.hasCycle = 1;
+ _huckState.selected1SpriteId = _huckState.drawTable1[slot];
+ _huckState.selected1Slot = slot;
+ playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xA8));
drawGift(slot);
_xp->updateDisplay();
// Start palette cycle for highlight...
XPCycleState cycleSpec[4];
- byte *cycleData = memberAddr(g_huckBoltLib, (int16)READ_UINT16(g_huckGiftPic + 0x0C));
+ byte *cycleData = memberAddr(_huckBoltLib, (int16)READ_UINT16(_huckGiftPic + 0x0C));
boltCycleToXPCycle(cycleData, cycleSpec);
_xp->startCycle(cycleSpec);
- } else if (!g_huckState.selectionPending) {
- if (g_huckState.selected1Slot == slot) {
+ } else if (!_huckState.selectionPending) {
+ if (_huckState.selected1Slot == slot) {
// Same gift clicked again, deselect...
- g_huckState.hasCycle = 0;
- g_huckState.selected1Slot = -1;
- playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAA));
+ _huckState.hasCycle = 0;
+ _huckState.selected1Slot = -1;
+ playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xAA));
drawGift(slot);
_xp->updateDisplay();
_xp->stopCycle();
} else {
// Different gift, select second...
- g_huckState.selectionPending = 1;
- g_huckState.selected2SpriteId = g_huckState.drawTable1[slot];
- g_huckState.selected2Slot = slot;
- playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAC));
+ _huckState.selectionPending = 1;
+ _huckState.selected2SpriteId = _huckState.drawTable1[slot];
+ _huckState.selected2Slot = slot;
+ playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xAC));
drawGift(slot);
_xp->updateDisplay();
}
@@ -556,45 +556,45 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
bool var_6 = false;
bool var_4 = false;
- if (g_huckHotSpotCount != 0) {
- g_huckHotSpotCount = 0;
+ if (_huckHotSpotCount != 0) {
+ _huckHotSpotCount = 0;
var_6 = true;
stopAnimation();
- if (g_huckBlinkTimer) {
- _xp->killTimer(g_huckBlinkTimer);
- g_huckBlinkTimer = 0;
+ if (_huckBlinkTimer) {
+ _xp->killTimer(_huckBlinkTimer);
+ _huckBlinkTimer = 0;
}
- huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ huckUpdateHotSpots(_huckCursorX, _huckCursorY);
}
Common::Rect helpRect(
- READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
- READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+ READ_UINT16(_huckGiftPic + 0x84), READ_UINT16(_huckGiftPic + 0x88),
+ READ_UINT16(_huckGiftPic + 0x86), READ_UINT16(_huckGiftPic + 0x8A));
if (helpRect.contains(x, y)) {
var_4 = true;
- if (g_huckScreensaverTimer) {
- _xp->killTimer(g_huckScreensaverTimer);
- g_huckScreensaverTimer = 0;
+ if (_huckScreensaverTimer) {
+ _xp->killTimer(_huckScreensaverTimer);
+ _huckScreensaverTimer = 0;
setHuckColors(0);
}
- if (!g_huckHotSpotCount && !var_6) {
- g_huckHotSpotCount = startAnimation(g_rtfHandle, 0x1A);
- g_huckActionState = 0;
+ if (!_huckHotSpotCount && !var_6) {
+ _huckHotSpotCount = startAnimation(_rtfHandle, 0x1A);
+ _huckActionState = 0;
}
} else {
Common::Rect exitRect(
- READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
- READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+ READ_UINT16(_huckGiftPic + 0x96), READ_UINT16(_huckGiftPic + 0x9A),
+ READ_UINT16(_huckGiftPic + 0x98), READ_UINT16(_huckGiftPic + 0x9C));
if (exitRect.contains(x, y)) {
setHuckColors(1);
restoreHuckColors(0);
- g_huckReturnBooth = 3;
- g_huckExitFlag = 1;
+ _huckReturnBooth = 3;
+ _huckExitFlag = 1;
}
}
@@ -602,9 +602,9 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
var_4 = true;
if (var_4) {
- if (g_huckScreensaverTimer) {
- _xp->killTimer(g_huckScreensaverTimer);
- g_huckScreensaverTimer = 0;
+ if (_huckScreensaverTimer) {
+ _xp->killTimer(_huckScreensaverTimer);
+ _huckScreensaverTimer = 0;
restoreHuckColors(0);
}
@@ -613,17 +613,17 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
}
void BoltEngine::giftSwap() {
- if (g_huckState.giftCount <= 2)
+ if (_huckState.giftCount <= 2)
return;
// Determine pool size excluding selected gifts...
int16 pool;
- if (g_huckState.selectionPending)
- pool = g_huckState.giftCount - 2;
- else if (g_huckState.hasCycle)
- pool = g_huckState.giftCount - 1;
+ if (_huckState.selectionPending)
+ pool = _huckState.giftCount - 2;
+ else if (_huckState.hasCycle)
+ pool = _huckState.giftCount - 1;
else
- pool = g_huckState.giftCount;
+ pool = _huckState.giftCount;
// Pick two distinct random indices into the eligible pool...
int16 randA = _xp->getRandom(pool);
@@ -638,14 +638,14 @@ void BoltEngine::giftSwap() {
int16 slotA = -1;
int16 slotB = 0;
int16 counter = -1;
- for (int16 i = 0, j = 0; j <= READ_UINT16(g_huckGiftPic); i++, j++) {
- if (g_huckState.drawTable2[j] != 0)
+ for (int16 i = 0, j = 0; j <= READ_UINT16(_huckGiftPic); i++, j++) {
+ if (_huckState.drawTable2[j] != 0)
continue;
- if (j == g_huckState.selected1Slot)
+ if (j == _huckState.selected1Slot)
continue;
- if (j == g_huckState.selected2Slot)
+ if (j == _huckState.selected2Slot)
continue;
counter++;
@@ -658,20 +658,20 @@ void BoltEngine::giftSwap() {
}
// If the two chosen slots have the same sprite, find a different slotB...
- if (g_huckState.drawTable1[slotA] == g_huckState.drawTable1[slotB]) {
+ if (_huckState.drawTable1[slotA] == _huckState.drawTable1[slotB]) {
// Walk forward from slotB, skipping invalid candidates...
while (true) {
slotB++;
- if (slotB >= READ_UINT16(g_huckGiftPic))
+ if (slotB >= READ_UINT16(_huckGiftPic))
slotB = 0;
- if (g_huckState.drawTable2[slotB] != 0)
+ if (_huckState.drawTable2[slotB] != 0)
continue;
- if (slotB == g_huckState.selected2Slot)
+ if (slotB == _huckState.selected2Slot)
continue;
- if (slotB == g_huckState.selected1Slot)
+ if (slotB == _huckState.selected1Slot)
continue;
if (slotB == slotA)
@@ -682,37 +682,37 @@ void BoltEngine::giftSwap() {
}
// Swap entries in draw table...
- int16 tmp = g_huckState.drawTable1[slotA];
- g_huckState.drawTable1[slotA] = g_huckState.drawTable1[slotB];
- g_huckState.drawTable1[slotB] = tmp;
+ int16 tmp = _huckState.drawTable1[slotA];
+ _huckState.drawTable1[slotA] = _huckState.drawTable1[slotB];
+ _huckState.drawTable1[slotB] = tmp;
drawGift(slotA);
drawGift(slotB);
}
void BoltEngine::resolveHuckSelection() {
- if (!g_huckState.selectionPending)
+ if (!_huckState.selectionPending)
return;
_xp->stopCycle();
- if (g_huckState.selected1SpriteId == g_huckState.selected2SpriteId) {
+ if (_huckState.selected1SpriteId == _huckState.selected2SpriteId) {
// Match, play match sound, mark both slots as matched...
- playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAE));
- g_huckState.drawTable2[g_huckState.selected1Slot] = 1;
- g_huckState.drawTable2[g_huckState.selected2Slot] = 1;
- g_huckState.hasCycle = 0;
- g_huckState.selectionPending = 0;
+ playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xAE));
+ _huckState.drawTable2[_huckState.selected1Slot] = 1;
+ _huckState.drawTable2[_huckState.selected2Slot] = 1;
+ _huckState.hasCycle = 0;
+ _huckState.selectionPending = 0;
checkHuckLevelComplete();
} else {
// Mismatch, play mismatch sound, redraw both gifts unselected...
- playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xB0));
- g_huckState.hasCycle = 0;
- g_huckState.selectionPending = 0;
- drawGift(g_huckState.selected1Slot);
- drawGift(g_huckState.selected2Slot);
- g_huckState.selected1Slot = -1;
- g_huckState.selected2Slot = -1;
+ playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xB0));
+ _huckState.hasCycle = 0;
+ _huckState.selectionPending = 0;
+ drawGift(_huckState.selected1Slot);
+ drawGift(_huckState.selected2Slot);
+ _huckState.selected1Slot = -1;
+ _huckState.selected2Slot = -1;
waitSoundMapHuck();
}
}
@@ -720,11 +720,11 @@ void BoltEngine::resolveHuckSelection() {
void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
switch (eventType) {
case etMouseDown:
- huckHandleActionButton(g_huckCursorX, g_huckCursorY);
+ huckHandleActionButton(_huckCursorX, _huckCursorY);
resolveHuckSelection();
// The original does:
- // if (g_huckExitFlag)
+ // if (_huckExitFlag)
// return;
//
// and then breaks, which is redundant...
@@ -732,21 +732,21 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
break;
case etMouseMove:
- g_huckCursorX = (int16)(eventData >> 16);
- g_huckCursorY = (int16)(eventData & -1);
- huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ _huckCursorX = (int16)(eventData >> 16);
+ _huckCursorY = (int16)(eventData & -1);
+ huckUpdateHotSpots(_huckCursorX, _huckCursorY);
break;
case etTimer: {
- if (eventData == g_huckScreensaverTimer) {
- huckToggleBlinking(&g_huckScreensaverFlag, 0);
- int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
- g_huckScreensaverTimer = _xp->startTimer(ms);
- } else if (eventData == g_huckBlinkTimer) {
- huckToggleBlinking(&g_huckBlinkFlag, 1);
- int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
- g_huckBlinkTimer = _xp->startTimer(ms);
- } else if (eventData == g_huckShuffleTimer) {
+ if (eventData == _huckScreensaverTimer) {
+ huckToggleBlinking(&_huckScreensaverFlag, 0);
+ int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+ _huckScreensaverTimer = _xp->startTimer(ms);
+ } else if (eventData == _huckBlinkTimer) {
+ huckToggleBlinking(&_huckBlinkFlag, 1);
+ int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+ _huckBlinkTimer = _xp->startTimer(ms);
+ } else if (eventData == _huckShuffleTimer) {
giftSwap();
_xp->updateDisplay();
startHuckShuffleTimer();
@@ -756,11 +756,11 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
}
case etInactivity: {
- if (!g_huckScreensaverTimer) {
- int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
- g_huckScreensaverTimer = _xp->startTimer(ms);
- g_huckScreensaverFlag = 0;
- huckToggleBlinking(&g_huckScreensaverFlag, 0);
+ if (!_huckScreensaverTimer) {
+ int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+ _huckScreensaverTimer = _xp->startTimer(ms);
+ _huckScreensaverFlag = 0;
+ huckToggleBlinking(&_huckScreensaverFlag, 0);
_xp->setInactivityTimer(1800);
} else {
bool exitLoop = false;
@@ -771,8 +771,8 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
int16 innerType = _xp->getEvent(etEmpty, &innerData);
switch (innerType) {
case etSound:
- if (g_huckSoundPlaying > 0)
- g_huckSoundPlaying--;
+ if (_huckSoundPlaying > 0)
+ _huckSoundPlaying--;
break;
default:
@@ -790,28 +790,28 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
}
case etSound: {
- if (g_huckHotSpotCount != 0) {
- g_huckHotSpotCount = maintainAudioPlay(1);
- if (g_huckHotSpotCount == 0)
- huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ if (_huckHotSpotCount != 0) {
+ _huckHotSpotCount = maintainAudioPlay(1);
+ if (_huckHotSpotCount == 0)
+ huckUpdateHotSpots(_huckCursorX, _huckCursorY);
}
- if (g_huckSoundPlaying > 0)
- g_huckSoundPlaying--;
+ if (_huckSoundPlaying > 0)
+ _huckSoundPlaying--;
break;
}
case etTrigger: {
- g_huckActionState++;
- if (g_huckActionState == 1) {
- int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
- g_huckBlinkTimer = _xp->startTimer(ms);
- g_huckBlinkFlag = 0;
- huckToggleBlinking(&g_huckBlinkFlag, 1);
- } else if (g_huckActionState == 2) {
- _xp->killTimer(g_huckBlinkTimer);
- g_huckBlinkTimer = 0;
+ _huckActionState++;
+ if (_huckActionState == 1) {
+ int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+ _huckBlinkTimer = _xp->startTimer(ms);
+ _huckBlinkFlag = 0;
+ huckToggleBlinking(&_huckBlinkFlag, 1);
+ } else if (_huckActionState == 2) {
+ _xp->killTimer(_huckBlinkTimer);
+ _huckBlinkTimer = 0;
restoreHuckColors(1);
}
@@ -824,10 +824,10 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
}
void BoltEngine::playHuck() {
- g_huckReturnBooth = 0x10;
+ _huckReturnBooth = 0x10;
while (!shouldQuit()) {
- if (g_huckExitFlag)
+ if (_huckExitFlag)
break;
uint32 eventData = 0;
@@ -836,51 +836,51 @@ void BoltEngine::playHuck() {
if (eventType)
handleEvent(eventType, eventData);
- if (g_huckHotSpotCount != 0) {
- g_huckHotSpotCount = maintainAudioPlay(0);
- if (g_huckHotSpotCount == 0)
- huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+ if (_huckHotSpotCount != 0) {
+ _huckHotSpotCount = maintainAudioPlay(0);
+ if (_huckHotSpotCount == 0)
+ huckUpdateHotSpots(_huckCursorX, _huckCursorY);
}
}
if (shouldQuit())
- g_huckReturnBooth = 0;
+ _huckReturnBooth = 0;
}
void BoltEngine::cleanUpHuck() {
unloadHuckResources();
- if (g_huckScratchPic.pixelData) {
- _xp->freeMem(g_huckScratchPic.pixelData);
- g_huckScratchPic.pixelData = nullptr;
- g_huckScratchPic.palette = nullptr;
+ if (_huckScratchPic.pixelData) {
+ _xp->freeMem(_huckScratchPic.pixelData);
+ _huckScratchPic.pixelData = nullptr;
+ _huckScratchPic.palette = nullptr;
}
- vSave(&g_huckGlobal, sizeof(g_huckGlobal), "HuckGlobal");
+ vSave(&_huckGlobal, sizeof(_huckGlobal), "HuckGlobal");
byte huckSaveBuf[0x86] = { 0 };
Common::MemoryWriteStream *huckStateWriteStream = new Common::MemoryWriteStream(huckSaveBuf, sizeof(huckSaveBuf));
- huckStateWriteStream->writeSint16BE(g_huckState.levelNumber);
+ huckStateWriteStream->writeSint16BE(_huckState.levelNumber);
for (int i = 0; i < 10; i++)
- huckStateWriteStream->writeSint16BE(g_huckState.slotIndex[i]);
+ huckStateWriteStream->writeSint16BE(_huckState.slotIndex[i]);
- huckStateWriteStream->writeSint16BE(g_huckState.levelComplete);
+ huckStateWriteStream->writeSint16BE(_huckState.levelComplete);
for (int i = 0; i < 24; i++)
- huckStateWriteStream->writeSint16BE(g_huckState.drawTable1[i]);
+ huckStateWriteStream->writeSint16BE(_huckState.drawTable1[i]);
for (int i = 0; i < 24; i++)
- huckStateWriteStream->writeSint16BE(g_huckState.drawTable2[i]);
+ huckStateWriteStream->writeSint16BE(_huckState.drawTable2[i]);
- huckStateWriteStream->writeSint16BE(g_huckState.giftCount);
- huckStateWriteStream->writeSint16BE(g_huckState.hasCycle);
- huckStateWriteStream->writeSint16BE(g_huckState.selectionPending);
- huckStateWriteStream->writeSint16BE(g_huckState.selected1Slot);
- huckStateWriteStream->writeSint16BE(g_huckState.selected2Slot);
- huckStateWriteStream->writeSint16BE(g_huckState.selected1SpriteId);
- huckStateWriteStream->writeSint16BE(g_huckState.selected2SpriteId);
+ huckStateWriteStream->writeSint16BE(_huckState.giftCount);
+ huckStateWriteStream->writeSint16BE(_huckState.hasCycle);
+ huckStateWriteStream->writeSint16BE(_huckState.selectionPending);
+ huckStateWriteStream->writeSint16BE(_huckState.selected1Slot);
+ huckStateWriteStream->writeSint16BE(_huckState.selected2Slot);
+ huckStateWriteStream->writeSint16BE(_huckState.selected1SpriteId);
+ huckStateWriteStream->writeSint16BE(_huckState.selected2SpriteId);
// Sanity check: should be exactly 0x86
assert(huckStateWriteStream->pos() == 0x86);
@@ -893,8 +893,8 @@ void BoltEngine::cleanUpHuck() {
}
int16 BoltEngine::huckGame(int16 prevBooth) {
- if (!openBOLTLib(&g_huckBoltLib, &g_huckBoltCallbacks, assetPath("huck.blt")))
- return g_huckReturnBooth;
+ if (!openBOLTLib(&_huckBoltLib, &_huckBoltCallbacks, assetPath("huck.blt")))
+ return _huckReturnBooth;
int16 savedTimer = _xp->setInactivityTimer(30);
@@ -904,23 +904,23 @@ int16 BoltEngine::huckGame(int16 prevBooth) {
cleanUpHuck();
_xp->setInactivityTimer(savedTimer);
- closeBOLTLib(&g_huckBoltLib);
+ closeBOLTLib(&_huckBoltLib);
- return g_huckReturnBooth;
+ return _huckReturnBooth;
}
void BoltEngine::swapHuckWordArray() {
- byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+ byte *ptr = _boltCurrentMemberEntry->dataPtr;
if (!ptr)
return;
- int16 count = (int16)(g_boltCurrentMemberEntry->decompSize / 2);
+ int16 count = (int16)(_boltCurrentMemberEntry->decompSize / 2);
for (int16 i = 0; i < count; i++, ptr += 2)
WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
}
void BoltEngine::swapHuckWords() {
- byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+ byte *ptr = _boltCurrentMemberEntry->dataPtr;
if (!ptr)
return;
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 0908001c6c6..a6512a7dffc 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -26,15 +26,15 @@
namespace Bolt {
bool BoltEngine::loadScoobyBaseAssets() {
- if (!getBOLTGroup(g_scoobyBoltLib, 0, 1))
+ if (!getBOLTGroup(_scoobyBoltLib, 0, 1))
return false;
- g_scoobyBaseData = memberAddr(g_scoobyBoltLib, 0);
+ _scoobyBaseData = memberAddr(_scoobyBoltLib, 0);
return true;
}
void BoltEngine::cleanUpScoobyBaseAssets() {
- freeBOLTGroup(g_scoobyBoltLib, 0, 1);
+ freeBOLTGroup(_scoobyBoltLib, 0, 1);
}
void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode) {
@@ -44,21 +44,21 @@ void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int
int16 picWidth = READ_UINT16(pic + 0x0A);
byte *pixelData = getResolvedPtr(pic, 0x12);
- int32 srcOffset = (clipRect[0] - g_displayX) + picWidth * (clipRect[1] - g_displayY);
+ int32 srcOffset = (clipRect[0] - _displayX) + picWidth * (clipRect[1] - _displayY);
- _xp->blit(pixelData + srcOffset, picWidth, g_scoobyTempPic.pixelData, clipW, clipW, clipH);
+ _xp->blit(pixelData + srcOffset, picWidth, _scoobyTempPic.pixelData, clipW, clipW, clipH);
- g_scoobyTempPic.width = clipW;
- g_scoobyTempPic.height = clipH;
- g_scoobyTempPic.palette = nullptr;
- g_scoobyTempPic.paletteStart = 0;
- g_scoobyTempPic.paletteCount = 0;
- g_scoobyTempPic.flags = 0;
+ _scoobyTempPic.width = clipW;
+ _scoobyTempPic.height = clipH;
+ _scoobyTempPic.palette = nullptr;
+ _scoobyTempPic.paletteStart = 0;
+ _scoobyTempPic.paletteCount = 0;
+ _scoobyTempPic.flags = 0;
if (*pic & 2)
- g_scoobyTempPic.flags |= 2;
+ _scoobyTempPic.flags |= 2;
- _xp->displayPic(&g_scoobyTempPic,
+ _xp->displayPic(&_scoobyTempPic,
READ_UINT16(pic + 6) + offsetX + clipRect[0],
READ_UINT16(pic + 8) + offsetY + clipRect[1],
displayMode);
@@ -78,24 +78,24 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
if (bgFrame < 0)
bgFrame = 0;
- int16 wallMarginX = READ_UINT16(g_scoobyLevelData + 0x0E);
- int16 wallMarginY = READ_UINT16(g_scoobyLevelData + 0x10);
+ int16 wallMarginX = READ_UINT16(_scoobyLevelData + 0x0E);
+ int16 wallMarginY = READ_UINT16(_scoobyLevelData + 0x10);
int16 posX = 0, posY = 0;
// Compute position along wall's primary axis
switch (direction) {
case 0: // up wall
- posY = g_scoobyCellBounds[cellIdx].top - 1 - wallMarginY;
+ posY = _scoobyCellBounds[cellIdx].top - 1 - wallMarginY;
break;
case 1: // right wall
- posX = g_scoobyCellBounds[cellIdx].right - wallMarginX;
+ posX = _scoobyCellBounds[cellIdx].right - wallMarginX;
break;
case 2: // down wall
- posY = g_scoobyCellBounds[cellIdx].bottom - wallMarginY;
+ posY = _scoobyCellBounds[cellIdx].bottom - wallMarginY;
break;
case 3: // left wall
- posX = g_scoobyCellBounds[cellIdx].left - 1 - wallMarginX;
+ posX = _scoobyCellBounds[cellIdx].left - 1 - wallMarginX;
break;
default:
break;
@@ -106,9 +106,9 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
case 1: // right wall (vertical)
case 3: // left wall (vertical)
{
- posY = g_scoobyCellBounds[cellIdx].top + wallMarginY + 1;
+ posY = _scoobyCellBounds[cellIdx].top + wallMarginY + 1;
- byte *basePic = g_scoobyWallPicsA[3];
+ byte *basePic = _scoobyWallPicsA[3];
int16 picW = READ_UINT16(basePic + 0x0A);
int16 picH = READ_UINT16(basePic + 0x0C);
@@ -118,10 +118,10 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
clipRect[2] = picW;
clipRect[3] = picH;
- displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+ displayPicClipHack(_scoobyBgPic, 0, 0, clipRect, 1);
if (bgFrame != 0) {
- displayPic(g_scoobyWallPicsA[bgFrame - 1], posX, posY, 1);
+ displayPic(_scoobyWallPicsA[bgFrame - 1], posX, posY, 1);
}
break;
@@ -130,9 +130,9 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
case 0: // up wall (horizontal)
case 2: // down wall (horizontal)
{
- posX = g_scoobyCellBounds[cellIdx].left + wallMarginX + 1;
+ posX = _scoobyCellBounds[cellIdx].left + wallMarginX + 1;
- byte *basePic = g_scoobyWallPicsB[4];
+ byte *basePic = _scoobyWallPicsB[4];
int16 picW = READ_UINT16(basePic + 0x0A);
int16 picH = READ_UINT16(basePic + 0x0C);
@@ -142,10 +142,10 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
clipRect[2] = picW;
clipRect[3] = picH;
- displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+ displayPicClipHack(_scoobyBgPic, 0, 0, clipRect, 1);
if (picFrame != 0) {
- displayPic(g_scoobyWallPicsB[picFrame - 1], posX, posY, 1);
+ displayPic(_scoobyWallPicsB[picFrame - 1], posX, posY, 1);
}
break;
@@ -157,7 +157,7 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
}
void BoltEngine::drawAllMovingWalls() {
- ScoobyState *state = &g_scoobyGameState;
+ ScoobyState *state = &_scoobyGameState;
// Phase 1: Draw open passages between adjacent cells (both sides state 2)
for (int16 i = 0; i < 25; i++) {
@@ -236,14 +236,14 @@ void BoltEngine::drawAllMovingWalls() {
}
void BoltEngine::animateTransition(int16 memberIdx) {
- g_scoobyGameState.spriteFrameCount = 1;
+ _scoobyGameState.spriteFrameCount = 1;
- g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, memberIdx - 1);
+ _scoobyGameState.frameData[0] = memberAddr(_scoobyBoltLib, memberIdx - 1);
- setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+ setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
- g_scoobyGameState.velocityX = 0;
- g_scoobyGameState.velocityY = 0;
+ _scoobyGameState.velocityX = 0;
+ _scoobyGameState.velocityY = 0;
}
void BoltEngine::clearPictMSb(byte *pic) {
@@ -261,18 +261,18 @@ void BoltEngine::clearPictMSb(byte *pic) {
void BoltEngine::initScoobyLevelGraphics() {
// Get palette from level data
- byte *palette = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData));
+ byte *palette = memberAddr(_scoobyBoltLib, READ_UINT16(_scoobyLevelData));
// Get background pic based on display mode
int16 picMember;
- if (g_displayMode != 0) {
- picMember = READ_UINT16(g_scoobyLevelData + 4);
+ if (_displayMode != 0) {
+ picMember = READ_UINT16(_scoobyLevelData + 4);
} else {
- picMember = READ_UINT16(g_scoobyLevelData + 2);
+ picMember = READ_UINT16(_scoobyLevelData + 2);
}
- g_scoobyBgPic = memberAddr(g_scoobyBoltLib, picMember);
- clearPictMSb(g_scoobyBgPic);
+ _scoobyBgPic = memberAddr(_scoobyBoltLib, picMember);
+ clearPictMSb(_scoobyBgPic);
// Drain timer events
uint32 dummy;
@@ -285,7 +285,7 @@ void BoltEngine::initScoobyLevelGraphics() {
displayColors(palette, stBack, 0);
// Display background to front surface
- displayPic(g_scoobyBgPic, g_displayX, g_displayY, stFront);
+ displayPic(_scoobyBgPic, _displayX, _displayY, stFront);
_xp->updateDisplay();
_xp->setTransparency(true);
@@ -295,31 +295,31 @@ void BoltEngine::initScoobyLevelGraphics() {
displayColors(palette, stBack, 1);
// Display background to back surface
- displayPic(g_scoobyBgPic, g_displayX, g_displayY, stBack);
+ displayPic(_scoobyBgPic, _displayX, _displayY, stBack);
drawAllMovingWalls();
// Setup Scooby sprite
- g_scoobyGameState.spriteFrameCount = 1;
+ _scoobyGameState.spriteFrameCount = 1;
int16 spriteMember;
- if (g_scoobyGameState.direction == 6) {
+ if (_scoobyGameState.direction == 6) {
spriteMember = 0x23;
} else {
spriteMember = 0x22;
}
- g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, spriteMember);
+ _scoobyGameState.frameData[0] = memberAddr(_scoobyBoltLib, spriteMember);
- setUpSSprite(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1, 0, 0);
- setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
- setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
- displaySSprite(&g_scoobySprite, g_scoobyGameState.scoobyX, g_scoobyGameState.scoobyY);
- animateSSprite(&g_scoobySprite, 0);
+ setUpSSprite(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1, 0, 0);
+ setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
+ setSSpriteVelocity(&_scoobySprite, _scoobyGameState.velocityX, _scoobyGameState.velocityY);
+ displaySSprite(&_scoobySprite, _scoobyGameState.scoobyX, _scoobyGameState.scoobyY);
+ animateSSprite(&_scoobySprite, 0);
// Setup palette cycle
XPCycleState cycleData[4];
- byte *cycleMember = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData + 0x0A));
+ byte *cycleMember = memberAddr(_scoobyBoltLib, READ_UINT16(_scoobyLevelData + 0x0A));
boltCycleToXPCycle(cycleMember, cycleData);
_xp->startCycle(cycleData);
@@ -330,59 +330,59 @@ bool BoltEngine::initScoobyLevelAssets() {
uint32 maxPicSize = 0;
// Compute graphics group index from level number
- g_scoobySelectedGraphicsGroup = (g_scoobyGameState.levelNumber - 1) * 0x100 + 0x100;
+ _scoobySelectedGraphicsGroup = (_scoobyGameState.levelNumber - 1) * 0x100 + 0x100;
- if (!getBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1))
+ if (!getBOLTGroup(_scoobyBoltLib, _scoobySelectedGraphicsGroup, 1))
return false;
- g_scoobyLevelData = memberAddr(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup);
+ _scoobyLevelData = memberAddr(_scoobyBoltLib, _scoobySelectedGraphicsGroup);
// Load wall pic set A (4 pics at level data offset +6)
for (int16 i = 0; i < 4; i++) {
- int16 memberIdx = READ_UINT16(g_scoobyLevelData + 6) + i;
- g_scoobyWallPicsA[i] = memberAddr(g_scoobyBoltLib, memberIdx);
- clearPictMSb(g_scoobyWallPicsA[i]);
+ int16 memberIdx = READ_UINT16(_scoobyLevelData + 6) + i;
+ _scoobyWallPicsA[i] = memberAddr(_scoobyBoltLib, memberIdx);
+ clearPictMSb(_scoobyWallPicsA[i]);
- uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0A) *
- (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0C);
+ uint32 picSize = (uint32)READ_UINT16(_scoobyWallPicsA[i] + 0x0A) *
+ (uint32)READ_UINT16(_scoobyWallPicsA[i] + 0x0C);
if (picSize > maxPicSize)
maxPicSize = picSize;
}
// Load wall pic set B (5 pics at level data offset +8)
for (int16 i = 0; i < 5; i++) {
- int16 memberIdx = READ_UINT16(g_scoobyLevelData + 8) + i;
- g_scoobyWallPicsB[i] = memberAddr(g_scoobyBoltLib, memberIdx);
- clearPictMSb(g_scoobyWallPicsB[i]);
+ int16 memberIdx = READ_UINT16(_scoobyLevelData + 8) + i;
+ _scoobyWallPicsB[i] = memberAddr(_scoobyBoltLib, memberIdx);
+ clearPictMSb(_scoobyWallPicsB[i]);
- uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0A) *
- (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0C);
+ uint32 picSize = (uint32)READ_UINT16(_scoobyWallPicsB[i] + 0x0A) *
+ (uint32)READ_UINT16(_scoobyWallPicsB[i] + 0x0C);
if (picSize > maxPicSize)
maxPicSize = picSize;
}
// Allocate temp buffer for largest pic
- g_scoobyTempPic.pixelData = (byte *)_xp->allocMem(maxPicSize);
- if (!g_scoobyTempPic.pixelData)
+ _scoobyTempPic.pixelData = (byte *)_xp->allocMem(maxPicSize);
+ if (!_scoobyTempPic.pixelData)
return false;
// Determine effective level count (capped at 10)
- if (g_scoobyGameState.levelNumber > 10) {
- g_scoobyLevelCount = 10;
+ if (_scoobyGameState.levelNumber > 10) {
+ _scoobyLevelCount = 10;
} else {
- g_scoobyLevelCount = g_scoobyGameState.levelNumber;
+ _scoobyLevelCount = _scoobyGameState.levelNumber;
}
// Look up difficulty from global save data
- int16 levelIdx = g_scoobyLevelCount - 1;
+ int16 levelIdx = _scoobyLevelCount - 1;
int16 globalOffset = levelIdx * 6;
- int16 saveSlot = g_scoobyGameState.slotIndex[levelIdx];
- g_scoobyDifficulty = g_scoobyGlobalSaveData[globalOffset + saveSlot * 2];
+ int16 saveSlot = _scoobyGameState.slotIndex[levelIdx];
+ _scoobyDifficulty = _scoobyGlobalSaveData[globalOffset + saveSlot * 2];
// Load wall states for all 25 cells based on difficulty
for (int16 i = 0; i < 25; i++) {
int16 srcOffset;
- switch (g_scoobyDifficulty) {
+ switch (_scoobyDifficulty) {
case 0:
srcOffset = 0x1C;
break;
@@ -397,29 +397,29 @@ bool BoltEngine::initScoobyLevelAssets() {
}
for (int16 j = 0; j < 4; j++) {
- g_scoobyGameState.wallStates[i][j] =
- READ_UINT16(g_scoobyLevelData + i * 8 + j * 2 + srcOffset);
+ _scoobyGameState.wallStates[i][j] =
+ READ_UINT16(_scoobyLevelData + i * 8 + j * 2 + srcOffset);
}
}
// Compute cell bounds and start positions for 5x5 grid
- int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
- int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
+ int16 cellW = READ_UINT16(_scoobyLevelData + 0x16);
+ int16 cellH = READ_UINT16(_scoobyLevelData + 0x18);
- int16 yPos = READ_UINT16(g_scoobyLevelData + 0x14);
+ int16 yPos = READ_UINT16(_scoobyLevelData + 0x14);
for (int16 row = 0; row < 5; row++) {
- int16 xPos = READ_UINT16(g_scoobyLevelData + 0x12);
+ int16 xPos = READ_UINT16(_scoobyLevelData + 0x12);
for (int16 col = 0; col < 5; col++) {
int16 cellIdx = row * 5 + col;
- g_scoobyCellBounds[cellIdx].left = xPos;
- g_scoobyCellBounds[cellIdx].right = xPos + cellW - 1;
- g_scoobyCellBounds[cellIdx].top = yPos;
- g_scoobyCellBounds[cellIdx].bottom = yPos + cellH - 1;
+ _scoobyCellBounds[cellIdx].left = xPos;
+ _scoobyCellBounds[cellIdx].right = xPos + cellW - 1;
+ _scoobyCellBounds[cellIdx].top = yPos;
+ _scoobyCellBounds[cellIdx].bottom = yPos + cellH - 1;
- g_scoobyLevelStartXY[cellIdx].x = xPos + ((uint16)cellW >> 1);
- g_scoobyLevelStartXY[cellIdx].y = yPos + ((uint16)cellH >> 1);
+ _scoobyLevelStartXY[cellIdx].x = xPos + ((uint16)cellW >> 1);
+ _scoobyLevelStartXY[cellIdx].y = yPos + ((uint16)cellH >> 1);
xPos += cellW;
}
@@ -432,23 +432,23 @@ bool BoltEngine::initScoobyLevelAssets() {
void BoltEngine::cleanUpScoobyLevelGraphics() {
_xp->stopCycle();
- freeBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1);
+ freeBOLTGroup(_scoobyBoltLib, _scoobySelectedGraphicsGroup, 1);
}
void BoltEngine::setScoobySpriteDirection(int16 startMember) {
- g_scoobyGameState.spriteFrameCount = 6;
+ _scoobyGameState.spriteFrameCount = 6;
int16 memberIdx = startMember;
for (int16 i = 0; i < 6; i++) {
- g_scoobyGameState.frameData[i] = memberAddr(g_scoobyBoltLib, memberIdx + i);
+ _scoobyGameState.frameData[i] = memberAddr(_scoobyBoltLib, memberIdx + i);
}
- setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+ setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
}
void BoltEngine::playSoundMapScooby(int16 memberIdx) {
- byte *soundData = getBOLTMember(g_scoobyBoltLib, memberIdx);
- uint32 soundSize = memberSize(g_scoobyBoltLib, memberIdx);
+ byte *soundData = getBOLTMember(_scoobyBoltLib, memberIdx);
+ uint32 soundSize = memberSize(_scoobyBoltLib, memberIdx);
if (soundData) {
_xp->playSound(soundData, soundSize, 22050);
@@ -456,42 +456,42 @@ void BoltEngine::playSoundMapScooby(int16 memberIdx) {
}
void BoltEngine::playWallSound() {
- if (g_scoobySoundPlaying == 0)
+ if (_scoobySoundPlaying == 0)
return;
_xp->stopSound();
int16 soundMember;
- switch (g_scoobySoundPlaying) {
+ switch (_scoobySoundPlaying) {
case 1:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x27C);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x27C);
break;
case 2:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x27E);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x27E);
break;
case 3:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x278);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x278);
break;
case 4:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x27A);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x27A);
break;
case 5:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x284);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x284);
break;
case 6:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x286);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x286);
break;
case 7:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x280);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x280);
break;
case 8:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x282);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x282);
break;
case 9:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x288);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x288);
break;
case 10:
- soundMember = READ_UINT16(g_scoobyLevelData + 0x28A);
+ soundMember = READ_UINT16(_scoobyLevelData + 0x28A);
break;
default:
return;
@@ -502,143 +502,143 @@ void BoltEngine::playWallSound() {
void BoltEngine::animateWalls() {
// Phase 1: Animate current cell's walls
- int16 curCell = g_scoobyGameState.scoobyCell;
+ int16 curCell = _scoobyGameState.scoobyCell;
for (int16 dir = 0; dir < 4; dir++) {
- if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
+ if (_scoobyGameState.wallStates[curCell][dir] == 1) {
// Closed wall, opening animation
bool shouldDraw = true;
switch (dir) {
case 0: // up neighbor
- if (curCell - 5 == g_scoobyGameState.scoobySavedCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][2] == 1)
+ if (curCell - 5 == _scoobyGameState.scoobySavedCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][2] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 5;
+ _scoobySoundPlaying = 5;
break;
case 1: // right neighbor
- if (curCell + 1 == g_scoobyGameState.scoobySavedCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][3] == 1)
+ if (curCell + 1 == _scoobyGameState.scoobySavedCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][3] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 1;
+ _scoobySoundPlaying = 1;
break;
case 2: // down neighbor
- if (curCell + 5 == g_scoobyGameState.scoobySavedCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][0] == 1)
+ if (curCell + 5 == _scoobyGameState.scoobySavedCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][0] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 5;
+ _scoobySoundPlaying = 5;
break;
case 3: // left neighbor
- if (curCell - 1 == g_scoobyGameState.scoobySavedCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][1] == 1)
+ if (curCell - 1 == _scoobyGameState.scoobySavedCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][1] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 1;
+ _scoobySoundPlaying = 1;
break;
}
if (shouldDraw) {
- drawMovingWalls(curCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+ drawMovingWalls(curCell, dir, 5 - _scoobyWallAnimStep, 4 - _scoobyWallAnimStep);
}
- } else if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
+ } else if (_scoobyGameState.wallStates[curCell][dir] == 3) {
// Dynamic wall, closing animation
if (dir == 3 || dir == 1) {
- g_scoobySoundPlaying = 4;
+ _scoobySoundPlaying = 4;
} else {
- g_scoobySoundPlaying = 8;
+ _scoobySoundPlaying = 8;
}
- drawMovingWalls(curCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+ drawMovingWalls(curCell, dir, _scoobyWallAnimStep, _scoobyWallAnimStep);
}
}
// Phase 2: Animate saved cell's walls
- int16 savedCell = g_scoobyGameState.scoobySavedCell;
+ int16 savedCell = _scoobyGameState.scoobySavedCell;
for (int16 dir = 0; dir < 4; dir++) {
- if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
+ if (_scoobyGameState.wallStates[savedCell][dir] == 1) {
// Closed wall in saved cell, closing animation
bool shouldDraw = true;
switch (dir) {
case 0: // up neighbor
- if (savedCell - 5 == g_scoobyGameState.scoobyCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][2] == 1)
+ if (savedCell - 5 == _scoobyGameState.scoobyCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][2] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 6;
+ _scoobySoundPlaying = 6;
break;
case 1: // right neighbor
- if (savedCell + 1 == g_scoobyGameState.scoobyCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][3] == 1)
+ if (savedCell + 1 == _scoobyGameState.scoobyCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][3] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 2;
+ _scoobySoundPlaying = 2;
break;
case 2: // down neighbor
- if (savedCell + 5 == g_scoobyGameState.scoobyCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][0] == 1)
+ if (savedCell + 5 == _scoobyGameState.scoobyCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][0] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 6;
+ _scoobySoundPlaying = 6;
break;
case 3: // left neighbor
- if (savedCell - 1 == g_scoobyGameState.scoobyCell) {
- if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][1] == 1)
+ if (savedCell - 1 == _scoobyGameState.scoobyCell) {
+ if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][1] == 1)
shouldDraw = false;
}
if (shouldDraw)
- g_scoobySoundPlaying = 2;
+ _scoobySoundPlaying = 2;
break;
}
if (shouldDraw) {
- drawMovingWalls(savedCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+ drawMovingWalls(savedCell, dir, _scoobyWallAnimStep, _scoobyWallAnimStep);
}
- } else if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
+ } else if (_scoobyGameState.wallStates[savedCell][dir] == 3) {
// Dynamic wall in saved cell, opening animation
if (dir == 3 || dir == 1) {
- g_scoobySoundPlaying = 3;
+ _scoobySoundPlaying = 3;
} else {
- g_scoobySoundPlaying = 7;
+ _scoobySoundPlaying = 7;
}
- drawMovingWalls(savedCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+ drawMovingWalls(savedCell, dir, 5 - _scoobyWallAnimStep, 4 - _scoobyWallAnimStep);
}
}
// Determine combined sound on first step
- if (g_scoobyWallAnimStep == 1) {
- if (g_scoobyWallsToClose == 0 && g_scoobyWallsToOpen > 1) {
- g_scoobySoundPlaying = 9;
- } else if (g_scoobyWallsToClose > 1 || (g_scoobyWallsToClose == 1 && g_scoobyWallsToOpen >= 1)) {
- g_scoobySoundPlaying = 10;
+ if (_scoobyWallAnimStep == 1) {
+ if (_scoobyWallsToClose == 0 && _scoobyWallsToOpen > 1) {
+ _scoobySoundPlaying = 9;
+ } else if (_scoobyWallsToClose > 1 || (_scoobyWallsToClose == 1 && _scoobyWallsToOpen >= 1)) {
+ _scoobySoundPlaying = 10;
}
playWallSound();
@@ -646,8 +646,8 @@ void BoltEngine::animateWalls() {
}
void BoltEngine::decideDirection() {
- int16 cell = g_scoobyGameState.scoobyCell;
- int16 targetDir = g_scoobyGameState.transitionTarget;
+ int16 cell = _scoobyGameState.scoobyCell;
+ int16 targetDir = _scoobyGameState.transitionTarget;
int16 primaryPos, secondaryPos, primaryCenter, secondaryCenter;
int16 wallDir;
@@ -655,19 +655,19 @@ void BoltEngine::decideDirection() {
switch (targetDir) {
case 2: // right
case 6: // left
- primaryPos = g_scoobyGameState.scoobyX;
- secondaryPos = g_scoobyGameState.scoobyY;
- primaryCenter = g_scoobyLevelStartXY[cell].x;
- secondaryCenter = g_scoobyLevelStartXY[cell].y;
+ primaryPos = _scoobyGameState.scoobyX;
+ secondaryPos = _scoobyGameState.scoobyY;
+ primaryCenter = _scoobyLevelStartXY[cell].x;
+ secondaryCenter = _scoobyLevelStartXY[cell].y;
wallDir = (targetDir == 6) ? 3 : 1;
break;
case 0: // up
case 4: // down
- primaryPos = g_scoobyGameState.scoobyY;
- secondaryPos = g_scoobyGameState.scoobyX;
- primaryCenter = g_scoobyLevelStartXY[cell].y;
- secondaryCenter = g_scoobyLevelStartXY[cell].x;
+ primaryPos = _scoobyGameState.scoobyY;
+ secondaryPos = _scoobyGameState.scoobyX;
+ primaryCenter = _scoobyLevelStartXY[cell].y;
+ secondaryCenter = _scoobyLevelStartXY[cell].x;
wallDir = (targetDir == 0) ? 0 : 2;
break;
@@ -677,15 +677,15 @@ void BoltEngine::decideDirection() {
if (secondaryPos == secondaryCenter) {
// Aligned on secondary axis, can move in target direction
- g_scoobyMoveRequested = 1;
+ _scoobyMoveRequested = 1;
if (primaryPos == primaryCenter) {
// At cell center, check wall
- if (g_scoobyGameState.wallStates[cell][wallDir] == 2 ||
- g_scoobyGameState.wallStates[cell][wallDir] == 3) {
+ if (_scoobyGameState.wallStates[cell][wallDir] == 2 ||
+ _scoobyGameState.wallStates[cell][wallDir] == 3) {
// Open passage or dynamic wall, stop
- g_scoobyGameState.targetVelocityX = 0;
- g_scoobyGameState.targetVelocityY = 0;
+ _scoobyGameState.targetVelocityX = 0;
+ _scoobyGameState.targetVelocityY = 0;
return;
}
}
@@ -693,56 +693,56 @@ void BoltEngine::decideDirection() {
// Wall closed, set velocity in target direction
switch (targetDir) {
case 0: // up
- g_scoobyGameState.targetVelocityX = 0;
- g_scoobyGameState.targetVelocityY = -3;
+ _scoobyGameState.targetVelocityX = 0;
+ _scoobyGameState.targetVelocityY = -3;
return;
case 2: // right
- g_scoobyGameState.targetVelocityX = 3;
- g_scoobyGameState.targetVelocityY = 0;
+ _scoobyGameState.targetVelocityX = 3;
+ _scoobyGameState.targetVelocityY = 0;
return;
case 4: // down
- g_scoobyGameState.targetVelocityX = 0;
- g_scoobyGameState.targetVelocityY = 3;
+ _scoobyGameState.targetVelocityX = 0;
+ _scoobyGameState.targetVelocityY = 3;
return;
case 6: // left
- g_scoobyGameState.targetVelocityX = -3;
- g_scoobyGameState.targetVelocityY = 0;
+ _scoobyGameState.targetVelocityX = -3;
+ _scoobyGameState.targetVelocityY = 0;
return;
default:
return;
}
} else {
// Not aligned on secondary axis, slide toward center
- g_scoobyMoveRequested = 1;
+ _scoobyMoveRequested = 1;
- if (g_scoobyGameState.wallStates[cell][wallDir] == 2) {
- g_scoobyGameState.targetVelocityX = 0;
- g_scoobyGameState.targetVelocityY = 0;
+ if (_scoobyGameState.wallStates[cell][wallDir] == 2) {
+ _scoobyGameState.targetVelocityX = 0;
+ _scoobyGameState.targetVelocityY = 0;
return;
}
switch (targetDir) {
case 2: // right, slide vertically
case 6: // left
- g_scoobyGameState.targetVelocityX = 0;
- if (g_scoobyLevelStartXY[cell].y < g_scoobyGameState.scoobyY) {
- g_scoobyGameState.transitionTarget = 0;
- g_scoobyGameState.targetVelocityY = -3;
+ _scoobyGameState.targetVelocityX = 0;
+ if (_scoobyLevelStartXY[cell].y < _scoobyGameState.scoobyY) {
+ _scoobyGameState.transitionTarget = 0;
+ _scoobyGameState.targetVelocityY = -3;
} else {
- g_scoobyGameState.transitionTarget = 4;
- g_scoobyGameState.targetVelocityY = 3;
+ _scoobyGameState.transitionTarget = 4;
+ _scoobyGameState.targetVelocityY = 3;
}
return;
case 0: // up, slide horizontally
case 4: // down
- g_scoobyGameState.targetVelocityY = 0;
- if (g_scoobyLevelStartXY[cell].x < g_scoobyGameState.scoobyX) {
- g_scoobyGameState.transitionTarget = 6;
- g_scoobyGameState.targetVelocityX = -3;
+ _scoobyGameState.targetVelocityY = 0;
+ if (_scoobyLevelStartXY[cell].x < _scoobyGameState.scoobyX) {
+ _scoobyGameState.transitionTarget = 6;
+ _scoobyGameState.targetVelocityX = -3;
} else {
- g_scoobyGameState.transitionTarget = 2;
- g_scoobyGameState.targetVelocityX = 3;
+ _scoobyGameState.transitionTarget = 2;
+ _scoobyGameState.targetVelocityX = 3;
}
return;
@@ -753,9 +753,9 @@ void BoltEngine::decideDirection() {
}
void BoltEngine::updateScoobySound() {
- switch (g_scoobySoundMode) {
+ switch (_scoobySoundMode) {
case 0:
- if (g_scoobySoundPlaying == 0) {
+ if (_scoobySoundPlaying == 0) {
_xp->stopSound();
}
@@ -772,149 +772,149 @@ void BoltEngine::updateScoobySound() {
}
void BoltEngine::setScoobySound(int16 mode) {
- if (mode != g_scoobySoundMode) {
- g_scoobySoundMode = mode;
+ if (mode != _scoobySoundMode) {
+ _scoobySoundMode = mode;
updateScoobySound();
}
}
void BoltEngine::updateScoobyLocation() {
Common::Point loc;
- getSSpriteLoc(&g_scoobySprite, &loc);
- g_scoobyGameState.scoobyX = loc.x;
- g_scoobyGameState.scoobyY = loc.y;
+ getSSpriteLoc(&_scoobySprite, &loc);
+ _scoobyGameState.scoobyX = loc.x;
+ _scoobyGameState.scoobyY = loc.y;
// Find which cell Scooby is in
- g_scoobyGameState.scoobyCell = -1;
+ _scoobyGameState.scoobyCell = -1;
for (int16 i = 0; i < 25; i++) {
- if (g_scoobyCellBounds[i].left > g_scoobyGameState.scoobyX ||
- g_scoobyCellBounds[i].right < g_scoobyGameState.scoobyX ||
- g_scoobyCellBounds[i].top > g_scoobyGameState.scoobyY ||
- g_scoobyCellBounds[i].bottom < g_scoobyGameState.scoobyY) {
+ if (_scoobyCellBounds[i].left > _scoobyGameState.scoobyX ||
+ _scoobyCellBounds[i].right < _scoobyGameState.scoobyX ||
+ _scoobyCellBounds[i].top > _scoobyGameState.scoobyY ||
+ _scoobyCellBounds[i].bottom < _scoobyGameState.scoobyY) {
continue;
}
// Found cell
- g_scoobyGameState.scoobyCell = i;
+ _scoobyGameState.scoobyCell = i;
int16 col = i % 5;
- g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : i - 1;
- g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : i + 1;
- g_scoobyGameState.upNeighbor = (i < 5) ? -1 : i - 5;
- g_scoobyGameState.downNeighbor = (i > 19) ? -1 : i + 5;
+ _scoobyGameState.leftNeighbor = (col == 0) ? -1 : i - 1;
+ _scoobyGameState.rightNeighbor = (col == 4) ? -1 : i + 1;
+ _scoobyGameState.upNeighbor = (i < 5) ? -1 : i - 5;
+ _scoobyGameState.downNeighbor = (i > 19) ? -1 : i + 5;
break;
}
- if (g_scoobyGameState.scoobyCell != -1)
+ if (_scoobyGameState.scoobyCell != -1)
return;
// Scooby is outside all cells, force movement back toward grid
- int16 gridStartX = READ_UINT16(g_scoobyLevelData + 0x12);
- int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
- int16 gridStartY = READ_UINT16(g_scoobyLevelData + 0x14);
- int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
-
- if (gridStartX > g_scoobyGameState.scoobyX) {
- g_scoobyGameState.velocityX = -3;
- g_scoobyGameState.velocityY = 0;
+ int16 gridStartX = READ_UINT16(_scoobyLevelData + 0x12);
+ int16 cellW = READ_UINT16(_scoobyLevelData + 0x16);
+ int16 gridStartY = READ_UINT16(_scoobyLevelData + 0x14);
+ int16 cellH = READ_UINT16(_scoobyLevelData + 0x18);
+
+ if (gridStartX > _scoobyGameState.scoobyX) {
+ _scoobyGameState.velocityX = -3;
+ _scoobyGameState.velocityY = 0;
setScoobySpriteDirection(6);
- } else if (gridStartX + cellW * 5 - 1 < g_scoobyGameState.scoobyX) {
- g_scoobyGameState.velocityX = 3;
- g_scoobyGameState.velocityY = 0;
+ } else if (gridStartX + cellW * 5 - 1 < _scoobyGameState.scoobyX) {
+ _scoobyGameState.velocityX = 3;
+ _scoobyGameState.velocityY = 0;
setScoobySpriteDirection(0);
- } else if (gridStartY > g_scoobyGameState.scoobyY) {
- g_scoobyGameState.velocityX = 0;
- g_scoobyGameState.velocityY = -3;
+ } else if (gridStartY > _scoobyGameState.scoobyY) {
+ _scoobyGameState.velocityX = 0;
+ _scoobyGameState.velocityY = -3;
setScoobySpriteDirection(0x0C);
- } else if (gridStartY + (cellH - 1) * 5 - 1 < g_scoobyGameState.scoobyY) {
- g_scoobyGameState.velocityX = 0;
- g_scoobyGameState.velocityY = 3;
+ } else if (gridStartY + (cellH - 1) * 5 - 1 < _scoobyGameState.scoobyY) {
+ _scoobyGameState.velocityX = 0;
+ _scoobyGameState.velocityY = 3;
setScoobySpriteDirection(0x12);
}
// Scroll Scooby off-screen
- int16 halfW = READ_UINT16(g_scoobyBaseData + 0x0A) / 2;
+ int16 halfW = READ_UINT16(_scoobyBaseData + 0x0A) / 2;
while (!shouldQuit()) {
- if (g_displayX - halfW >= g_scoobyGameState.scoobyX)
+ if (_displayX - halfW >= _scoobyGameState.scoobyX)
break;
- if (g_displayX + g_displayWidth + halfW <= g_scoobyGameState.scoobyX)
+ if (_displayX + _displayWidth + halfW <= _scoobyGameState.scoobyX)
break;
- int16 halfH = READ_UINT16(g_scoobyBaseData + 0x0C) / 2;
- if (g_displayY - halfH >= g_scoobyGameState.scoobyY)
+ int16 halfH = READ_UINT16(_scoobyBaseData + 0x0C) / 2;
+ if (_displayY - halfH >= _scoobyGameState.scoobyY)
break;
- if (g_displayY + g_displayHeight + halfH <= g_scoobyGameState.scoobyY)
+ if (_displayY + _displayHeight + halfH <= _scoobyGameState.scoobyY)
break;
// Still visible, animate one frame
- setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
- animateSSprite(&g_scoobySprite, 0);
+ setSSpriteVelocity(&_scoobySprite, _scoobyGameState.velocityX, _scoobyGameState.velocityY);
+ animateSSprite(&_scoobySprite, 0);
_xp->updateDisplay();
uint32 dummy;
if (_xp->getEvent(etSound, &dummy) == etSound)
updateScoobySound();
- getSSpriteLoc(&g_scoobySprite, &loc);
- g_scoobyGameState.scoobyX = loc.x;
- g_scoobyGameState.scoobyY = loc.y;
+ getSSpriteLoc(&_scoobySprite, &loc);
+ _scoobyGameState.scoobyX = loc.x;
+ _scoobyGameState.scoobyY = loc.y;
}
// Scooby left the screen, level complete
_xp->stopSound();
- g_scoobyGameState.levelComplete = 1;
+ _scoobyGameState.levelComplete = 1;
- g_scoobyGameState.levelNumber++;
- if (g_scoobyGameState.levelNumber > 12)
- g_scoobyGameState.levelNumber = 10;
+ _scoobyGameState.levelNumber++;
+ if (_scoobyGameState.levelNumber > 12)
+ _scoobyGameState.levelNumber = 10;
// Cycle slot for current level
- int16 slotIdx = g_scoobyLevelCount - 1;
- g_scoobyGameState.slotIndex[slotIdx]++;
- if (g_scoobyGameState.slotIndex[slotIdx] == 3)
- g_scoobyGameState.slotIndex[slotIdx] = 0;
+ int16 slotIdx = _scoobyLevelCount - 1;
+ _scoobyGameState.slotIndex[slotIdx]++;
+ if (_scoobyGameState.slotIndex[slotIdx] == 3)
+ _scoobyGameState.slotIndex[slotIdx] = 0;
}
void BoltEngine::updateScoobyWalls() {
- if (g_scoobyWallAnimating != 0) {
- g_scoobyWallAnimStep++;
- if (g_scoobyWallAnimStep > 5) {
- g_scoobyWallAnimating = 0;
- g_scoobyWallAnimStep = 5;
+ if (_scoobyWallAnimating != 0) {
+ _scoobyWallAnimStep++;
+ if (_scoobyWallAnimStep > 5) {
+ _scoobyWallAnimating = 0;
+ _scoobyWallAnimStep = 5;
}
animateWalls();
- if (g_scoobyWallAnimating == 0) {
- g_scoobyGameState.scoobySavedCell = g_scoobyGameState.scoobyCell;
+ if (_scoobyWallAnimating == 0) {
+ _scoobyGameState.scoobySavedCell = _scoobyGameState.scoobyCell;
}
return;
}
// Not animating, check if Scooby is at cell center
- int16 curCell = g_scoobyGameState.scoobyCell;
- if (g_scoobyLevelStartXY[curCell].x != g_scoobyGameState.scoobyX)
+ int16 curCell = _scoobyGameState.scoobyCell;
+ if (_scoobyLevelStartXY[curCell].x != _scoobyGameState.scoobyX)
return;
- if (g_scoobyLevelStartXY[curCell].y != g_scoobyGameState.scoobyY)
+ if (_scoobyLevelStartXY[curCell].y != _scoobyGameState.scoobyY)
return;
// At cell center, update active level
- g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+ _scoobyGameState.activeLevel = _scoobyGameState.scoobyCell;
// If same cell as saved, no wall changes needed
- if (g_scoobyGameState.scoobyCell == g_scoobyGameState.scoobySavedCell)
+ if (_scoobyGameState.scoobyCell == _scoobyGameState.scoobySavedCell)
return;
// Detect wall changes between current and saved cells
- g_scoobyWallsToOpen = 0;
- g_scoobyWallsToClose = 0;
+ _scoobyWallsToOpen = 0;
+ _scoobyWallsToClose = 0;
- int16 savedCell = g_scoobyGameState.scoobySavedCell;
+ int16 savedCell = _scoobyGameState.scoobySavedCell;
int16 curUp = curCell - 5;
int16 curLeft = curCell - 1;
@@ -928,38 +928,38 @@ void BoltEngine::updateScoobyWalls() {
for (int16 dir = 0; dir <= 3; dir++) {
// Check current cell wall
- if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
- g_scoobyWallAnimating = 1;
- g_scoobyWallAnimStep = 1;
- g_scoobyWallsToClose++;
- } else if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
- g_scoobyWallAnimating = 1;
- g_scoobyWallAnimStep = 1;
- g_scoobyWallsToOpen++;
+ if (_scoobyGameState.wallStates[curCell][dir] == 3) {
+ _scoobyWallAnimating = 1;
+ _scoobyWallAnimStep = 1;
+ _scoobyWallsToClose++;
+ } else if (_scoobyGameState.wallStates[curCell][dir] == 1) {
+ _scoobyWallAnimating = 1;
+ _scoobyWallAnimStep = 1;
+ _scoobyWallsToOpen++;
bool cancelOpen = false;
switch (dir) {
case 0:
if (curUp == savedCell &&
- g_scoobyGameState.wallStates[savedCell][2] == 1)
+ _scoobyGameState.wallStates[savedCell][2] == 1)
cancelOpen = true;
break;
case 1:
if (curRight == savedCell &&
- g_scoobyGameState.wallStates[savedCell][3] == 1)
+ _scoobyGameState.wallStates[savedCell][3] == 1)
cancelOpen = true;
break;
case 2:
if (curDown == savedCell &&
- g_scoobyGameState.wallStates[savedCell][0] == 1)
+ _scoobyGameState.wallStates[savedCell][0] == 1)
cancelOpen = true;
break;
case 3:
if (curLeft == savedCell &&
- g_scoobyGameState.wallStates[savedCell][1] == 1)
+ _scoobyGameState.wallStates[savedCell][1] == 1)
cancelOpen = true;
break;
@@ -968,42 +968,42 @@ void BoltEngine::updateScoobyWalls() {
}
if (cancelOpen)
- g_scoobyWallsToOpen--;
+ _scoobyWallsToOpen--;
}
// Check saved cell wall
- if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
- g_scoobyWallAnimating = 1;
- g_scoobyWallAnimStep = 1;
- g_scoobyWallsToOpen++;
- } else if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
- g_scoobyWallAnimating = 1;
- g_scoobyWallAnimStep = 1;
- g_scoobyWallsToClose++;
+ if (_scoobyGameState.wallStates[savedCell][dir] == 3) {
+ _scoobyWallAnimating = 1;
+ _scoobyWallAnimStep = 1;
+ _scoobyWallsToOpen++;
+ } else if (_scoobyGameState.wallStates[savedCell][dir] == 1) {
+ _scoobyWallAnimating = 1;
+ _scoobyWallAnimStep = 1;
+ _scoobyWallsToClose++;
bool cancelClose = false;
switch (dir) {
case 0:
if (savedUp == curCell &&
- g_scoobyGameState.wallStates[curCell][2] == 1)
+ _scoobyGameState.wallStates[curCell][2] == 1)
cancelClose = true;
break;
case 1:
if (savedRight == curCell &&
- g_scoobyGameState.wallStates[curCell][3] == 1)
+ _scoobyGameState.wallStates[curCell][3] == 1)
cancelClose = true;
break;
case 2:
if (savedDown == curCell &&
- g_scoobyGameState.wallStates[curCell][0] == 1)
+ _scoobyGameState.wallStates[curCell][0] == 1)
cancelClose = true;
break;
case 3:
if (savedLeft == curCell &&
- g_scoobyGameState.wallStates[curCell][1] == 1)
+ _scoobyGameState.wallStates[curCell][1] == 1)
cancelClose = true;
break;
@@ -1012,79 +1012,79 @@ void BoltEngine::updateScoobyWalls() {
}
if (cancelClose)
- g_scoobyWallsToClose--;
+ _scoobyWallsToClose--;
}
}
- if (g_scoobyWallAnimating != 0) {
+ if (_scoobyWallAnimating != 0) {
animateWalls();
}
}
void BoltEngine::updateScoobyDirection(int16 inputDir) {
- int16 cell = g_scoobyGameState.scoobyCell;
+ int16 cell = _scoobyGameState.scoobyCell;
// If at cell center, wall passable, and no transition active then accept immediately
- if (g_scoobyLevelStartXY[cell].x == g_scoobyGameState.scoobyX &&
- g_scoobyLevelStartXY[cell].y == g_scoobyGameState.scoobyY &&
- g_scoobyGameState.wallStates[cell][inputDir] != 2 &&
- g_scoobyTransitioning == 0) {
- g_scoobyDesiredDir = inputDir;
- g_scoobyInputHoldCount = 3;
- g_scoobyLastInputDir = inputDir;
+ if (_scoobyLevelStartXY[cell].x == _scoobyGameState.scoobyX &&
+ _scoobyLevelStartXY[cell].y == _scoobyGameState.scoobyY &&
+ _scoobyGameState.wallStates[cell][inputDir] != 2 &&
+ _scoobyTransitioning == 0) {
+ _scoobyDesiredDir = inputDir;
+ _scoobyInputHoldCount = 3;
+ _scoobyLastInputDir = inputDir;
} else {
- if (g_scoobyLastInputDir == inputDir) {
- g_scoobyInputHoldCount++;
- if (g_scoobyInputHoldCount >= 3 && g_scoobyTransitioning == 0) {
- g_scoobyDesiredDir = g_scoobyLastInputDir;
+ if (_scoobyLastInputDir == inputDir) {
+ _scoobyInputHoldCount++;
+ if (_scoobyInputHoldCount >= 3 && _scoobyTransitioning == 0) {
+ _scoobyDesiredDir = _scoobyLastInputDir;
}
} else {
- g_scoobyInputHoldCount = 0;
- g_scoobyLastInputDir = inputDir;
+ _scoobyInputHoldCount = 0;
+ _scoobyLastInputDir = inputDir;
}
}
// Resolve diagonal inputs into cardinal directions
int16 resolvedDir = 0;
-#define WALL_OPEN(c, d) (g_scoobyGameState.wallStates[c][d] == 2 || g_scoobyGameState.wallStates[c][d] == 3)
+#define WALL_OPEN(c, d) (_scoobyGameState.wallStates[c][d] == 2 || _scoobyGameState.wallStates[c][d] == 3)
- switch (g_scoobyDesiredDir) {
+ switch (_scoobyDesiredDir) {
case 0:
case 2:
case 4:
case 6:
- resolvedDir = g_scoobyDesiredDir;
+ resolvedDir = _scoobyDesiredDir;
break;
case 1: // up-right
if (WALL_OPEN(cell, 0)) {
- resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+ resolvedDir = WALL_OPEN(cell, 1) ? (_scoobyDesiredDir & 0xFE) : 2;
} else {
- resolvedDir = WALL_OPEN(cell, 1) ? 0 : (g_scoobyDesiredDir & 0xFE);
+ resolvedDir = WALL_OPEN(cell, 1) ? 0 : (_scoobyDesiredDir & 0xFE);
}
break;
case 3: // down-right
if (WALL_OPEN(cell, 2)) {
- resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+ resolvedDir = WALL_OPEN(cell, 1) ? (_scoobyDesiredDir & 0xFE) : 2;
} else {
- resolvedDir = WALL_OPEN(cell, 1) ? 4 : (g_scoobyDesiredDir & 0xFE);
+ resolvedDir = WALL_OPEN(cell, 1) ? 4 : (_scoobyDesiredDir & 0xFE);
}
break;
case 5: // down-left
if (WALL_OPEN(cell, 2)) {
- resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+ resolvedDir = WALL_OPEN(cell, 3) ? (_scoobyDesiredDir & 0xFE) : 6;
} else {
- resolvedDir = WALL_OPEN(cell, 3) ? 4 : (g_scoobyDesiredDir & 0xFE);
+ resolvedDir = WALL_OPEN(cell, 3) ? 4 : (_scoobyDesiredDir & 0xFE);
}
break;
case 7: // up-left
if (WALL_OPEN(cell, 0)) {
- resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+ resolvedDir = WALL_OPEN(cell, 3) ? (_scoobyDesiredDir & 0xFE) : 6;
} else {
- resolvedDir = WALL_OPEN(cell, 3) ? 0 : (g_scoobyDesiredDir & 0xFE);
+ resolvedDir = WALL_OPEN(cell, 3) ? 0 : (_scoobyDesiredDir & 0xFE);
}
break;
@@ -1094,117 +1094,117 @@ void BoltEngine::updateScoobyDirection(int16 inputDir) {
#undef WALL_OPEN
- g_scoobyMoveRequested = 0;
+ _scoobyMoveRequested = 0;
- if (g_scoobyDesiredDir == -1) {
- g_scoobyMoveRequested = 1;
+ if (_scoobyDesiredDir == -1) {
+ _scoobyMoveRequested = 1;
- if (g_scoobyGameState.direction == 6) {
- g_scoobyGameState.transitionTarget = -3;
+ if (_scoobyGameState.direction == 6) {
+ _scoobyGameState.transitionTarget = -3;
} else {
- g_scoobyGameState.transitionTarget = -2;
+ _scoobyGameState.transitionTarget = -2;
}
- g_scoobyGameState.targetVelocityX = 0;
- g_scoobyGameState.targetVelocityY = 0;
+ _scoobyGameState.targetVelocityX = 0;
+ _scoobyGameState.targetVelocityY = 0;
} else {
- g_scoobyGameState.transitionTarget = resolvedDir;
+ _scoobyGameState.transitionTarget = resolvedDir;
decideDirection();
}
}
void BoltEngine::updateScoobyTransition() {
- if (g_scoobyTransitioning != 0) {
+ if (_scoobyTransitioning != 0) {
// Active transition in progress
- switch (g_scoobyTransitionTarget) {
+ switch (_scoobyTransitionTarget) {
case -3:
animateTransition(0x24);
- g_scoobyTransitioning = 0;
+ _scoobyTransitioning = 0;
break;
case -2:
animateTransition(0x23);
- g_scoobyTransitioning = 0;
+ _scoobyTransitioning = 0;
break;
case 0:
setScoobySpriteDirection(0x0C);
- g_scoobyTransitioning = 0;
+ _scoobyTransitioning = 0;
break;
case 2:
- if (g_scoobyTransitionFrom == 4) {
+ if (_scoobyTransitionFrom == 4) {
animateTransition(0x1C);
- g_scoobyTransitionFrom = 0;
+ _scoobyTransitionFrom = 0;
} else {
setScoobySpriteDirection(0x0);
- g_scoobyTransitioning = 0;
+ _scoobyTransitioning = 0;
}
break;
case 4:
- if (g_scoobyTransitionFrom == 0) {
+ if (_scoobyTransitionFrom == 0) {
animateTransition(0x19);
- g_scoobyTransitionFrom = 4;
+ _scoobyTransitionFrom = 4;
} else {
setScoobySpriteDirection(0x12);
- g_scoobyTransitioning = 0;
+ _scoobyTransitioning = 0;
}
break;
case 6:
setScoobySpriteDirection(6);
- g_scoobyTransitioning = 0;
+ _scoobyTransitioning = 0;
break;
default:
break;
}
- if (g_scoobyTransitioning == 0) {
+ if (_scoobyTransitioning == 0) {
// Transition complete, commit state
- switch (g_scoobyTransitionTarget) {
+ switch (_scoobyTransitionTarget) {
case 0:
case 2:
case 4:
case 6:
- g_scoobyGameState.velocityX = g_scoobyTransitionVelX;
- g_scoobyGameState.velocityY = g_scoobyTransitionVelY;
+ _scoobyGameState.velocityX = _scoobyTransitionVelX;
+ _scoobyGameState.velocityY = _scoobyTransitionVelY;
break;
default:
break;
}
- g_scoobyGameState.currentAnim = g_scoobyTransitionTarget;
+ _scoobyGameState.currentAnim = _scoobyTransitionTarget;
- if (g_scoobyTransitionTarget == 6) {
- g_scoobyGameState.direction = 6;
- } else if (g_scoobyGameState.currentAnim == 2) {
- g_scoobyGameState.direction = 2;
+ if (_scoobyTransitionTarget == 6) {
+ _scoobyGameState.direction = 6;
+ } else if (_scoobyGameState.currentAnim == 2) {
+ _scoobyGameState.direction = 2;
}
}
} else {
// No transition active, check if one should start
- if (g_scoobyMoveRequested == 0)
+ if (_scoobyMoveRequested == 0)
goto epilogue;
- g_scoobyMoveRequested = 0;
+ _scoobyMoveRequested = 0;
- if (g_scoobyGameState.transitionTarget == g_scoobyGameState.currentAnim) {
+ if (_scoobyGameState.transitionTarget == _scoobyGameState.currentAnim) {
// Same direction, just update velocity
- g_scoobyGameState.velocityX = g_scoobyGameState.targetVelocityX;
- g_scoobyGameState.velocityY = g_scoobyGameState.targetVelocityY;
+ _scoobyGameState.velocityX = _scoobyGameState.targetVelocityX;
+ _scoobyGameState.velocityY = _scoobyGameState.targetVelocityY;
goto epilogue;
}
// Start new transition
- g_scoobyTransitioning = 1;
- g_scoobyTransitionTarget = g_scoobyGameState.transitionTarget;
- g_scoobyTransitionFrom = g_scoobyGameState.currentAnim;
- g_scoobyTransitionVelX = g_scoobyGameState.targetVelocityX;
- g_scoobyTransitionVelY = g_scoobyGameState.targetVelocityY;
+ _scoobyTransitioning = 1;
+ _scoobyTransitionTarget = _scoobyGameState.transitionTarget;
+ _scoobyTransitionFrom = _scoobyGameState.currentAnim;
+ _scoobyTransitionVelX = _scoobyGameState.targetVelocityX;
+ _scoobyTransitionVelY = _scoobyGameState.targetVelocityY;
// Pick transition animation based on current -> target direction.
- switch (g_scoobyGameState.currentAnim) {
+ switch (_scoobyGameState.currentAnim) {
case -3:
case -2:
- switch (g_scoobyGameState.transitionTarget) {
+ switch (_scoobyGameState.transitionTarget) {
case 0:
- if (g_scoobyGameState.currentAnim == -2)
+ if (_scoobyGameState.currentAnim == -2)
animateTransition(0x1B);
else
animateTransition(0x1C);
@@ -1213,7 +1213,7 @@ void BoltEngine::updateScoobyTransition() {
animateTransition(0x1D);
break;
case 4:
- if (g_scoobyGameState.currentAnim == -2)
+ if (_scoobyGameState.currentAnim == -2)
animateTransition(0x19);
else
animateTransition(0x1A);
@@ -1227,7 +1227,7 @@ void BoltEngine::updateScoobyTransition() {
}
break;
case 0:
- switch (g_scoobyGameState.transitionTarget) {
+ switch (_scoobyGameState.transitionTarget) {
case -3:
animateTransition(0x1C);
break;
@@ -1249,7 +1249,7 @@ void BoltEngine::updateScoobyTransition() {
break;
case 2:
- switch (g_scoobyGameState.transitionTarget) {
+ switch (_scoobyGameState.transitionTarget) {
case -3:
animateTransition(0x1D);
break;
@@ -1271,7 +1271,7 @@ void BoltEngine::updateScoobyTransition() {
break;
case 4:
- switch (g_scoobyGameState.transitionTarget) {
+ switch (_scoobyGameState.transitionTarget) {
case -3:
animateTransition(0x1A);
break;
@@ -1293,7 +1293,7 @@ void BoltEngine::updateScoobyTransition() {
break;
case 6:
- switch (g_scoobyGameState.transitionTarget) {
+ switch (_scoobyGameState.transitionTarget) {
case -3:
animateTransition(0x1E);
break;
@@ -1322,13 +1322,13 @@ void BoltEngine::updateScoobyTransition() {
}
epilogue:
- setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
- animateSSprite(&g_scoobySprite, 0);
+ setSSpriteVelocity(&_scoobySprite, _scoobyGameState.velocityX, _scoobyGameState.velocityY);
+ animateSSprite(&_scoobySprite, 0);
int16 soundMode;
- if (g_scoobyGameState.currentAnim == -2 || g_scoobyGameState.currentAnim == -3) {
+ if (_scoobyGameState.currentAnim == -2 || _scoobyGameState.currentAnim == -3) {
soundMode = 0;
- } else if (g_scoobyGameState.velocityX == 0 && g_scoobyGameState.velocityY == 0 && g_scoobyTransitioning == 0) {
+ } else if (_scoobyGameState.velocityX == 0 && _scoobyGameState.velocityY == 0 && _scoobyTransitioning == 0) {
soundMode = 2;
} else {
soundMode = 1;
@@ -1343,47 +1343,47 @@ bool BoltEngine::initScoobyLevel() {
// Set current level based on difficulty
int16 level;
- switch (g_scoobyDifficulty) {
+ switch (_scoobyDifficulty) {
case 0:
- level = READ_UINT16(g_scoobyLevelData + 0x1A);
+ level = READ_UINT16(_scoobyLevelData + 0x1A);
break;
case 1:
- level = READ_UINT16(g_scoobyLevelData + 0xE4);
+ level = READ_UINT16(_scoobyLevelData + 0xE4);
break;
case 2:
- level = READ_UINT16(g_scoobyLevelData + 0x1AE);
+ level = READ_UINT16(_scoobyLevelData + 0x1AE);
break;
default:
goto skipLevelSet;
}
- g_scoobyGameState.scoobyCell = level;
- g_scoobyGameState.scoobySavedCell = level;
+ _scoobyGameState.scoobyCell = level;
+ _scoobyGameState.scoobySavedCell = level;
skipLevelSet:
- g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+ _scoobyGameState.activeLevel = _scoobyGameState.scoobyCell;
// Compute neighbor levels in a 5-wide grid
- int16 col = g_scoobyGameState.scoobyCell % 5;
+ int16 col = _scoobyGameState.scoobyCell % 5;
- g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
- g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
- g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
- g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+ _scoobyGameState.leftNeighbor = (col == 0) ? -1 : _scoobyGameState.scoobyCell - 1;
+ _scoobyGameState.rightNeighbor = (col == 4) ? -1 : _scoobyGameState.scoobyCell + 1;
+ _scoobyGameState.upNeighbor = (_scoobyGameState.scoobyCell < 5) ? -1 : _scoobyGameState.scoobyCell - 5;
+ _scoobyGameState.downNeighbor = (_scoobyGameState.scoobyCell > 19) ? -1 : _scoobyGameState.scoobyCell + 5;
- g_scoobyGameState.levelComplete = 0;
+ _scoobyGameState.levelComplete = 0;
// Starting position from level table
- g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
- g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+ _scoobyGameState.scoobyX = _scoobyLevelStartXY[_scoobyGameState.activeLevel].x;
+ _scoobyGameState.scoobyY = _scoobyLevelStartXY[_scoobyGameState.activeLevel].y;
- g_scoobyGameState.velocityX = 0;
- g_scoobyGameState.velocityY = 0;
- g_scoobyGameState.targetVelocityX = 0;
- g_scoobyGameState.targetVelocityY = 0;
- g_scoobyGameState.currentAnim = -2;
- g_scoobyGameState.transitionTarget = -2;
- g_scoobyGameState.direction = 2;
+ _scoobyGameState.velocityX = 0;
+ _scoobyGameState.velocityY = 0;
+ _scoobyGameState.targetVelocityX = 0;
+ _scoobyGameState.targetVelocityY = 0;
+ _scoobyGameState.currentAnim = -2;
+ _scoobyGameState.transitionTarget = -2;
+ _scoobyGameState.direction = 2;
initScoobyLevelGraphics();
return true;
@@ -1394,19 +1394,19 @@ bool BoltEngine::resumeScoobyLevel() {
return false;
// Restore level from saved level
- g_scoobyGameState.scoobyCell = g_scoobyGameState.scoobySavedCell;
+ _scoobyGameState.scoobyCell = _scoobyGameState.scoobySavedCell;
// Compute neighbor levels
- int16 col = g_scoobyGameState.scoobyCell % 5;
+ int16 col = _scoobyGameState.scoobyCell % 5;
- g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
- g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
- g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
- g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+ _scoobyGameState.leftNeighbor = (col == 0) ? -1 : _scoobyGameState.scoobyCell - 1;
+ _scoobyGameState.rightNeighbor = (col == 4) ? -1 : _scoobyGameState.scoobyCell + 1;
+ _scoobyGameState.upNeighbor = (_scoobyGameState.scoobyCell < 5) ? -1 : _scoobyGameState.scoobyCell - 5;
+ _scoobyGameState.downNeighbor = (_scoobyGameState.scoobyCell > 19) ? -1 : _scoobyGameState.scoobyCell + 5;
// Starting position from level table
- g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
- g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+ _scoobyGameState.scoobyX = _scoobyLevelStartXY[_scoobyGameState.activeLevel].x;
+ _scoobyGameState.scoobyY = _scoobyLevelStartXY[_scoobyGameState.activeLevel].y;
initScoobyLevelGraphics();
return true;
@@ -1415,87 +1415,87 @@ bool BoltEngine::resumeScoobyLevel() {
bool BoltEngine::initScooby() {
_xp->randomize();
- g_scoobyMoveRequested = 0;
- g_scoobyTransitioning = 0;
- g_scoobyWallAnimating = 0;
- g_scoobyDesiredDir = -1;
- g_scoobyInputHoldCount = 0;
+ _scoobyMoveRequested = 0;
+ _scoobyTransitioning = 0;
+ _scoobyWallAnimating = 0;
+ _scoobyDesiredDir = -1;
+ _scoobyInputHoldCount = 0;
if (!loadScoobyBaseAssets())
return false;
- if (!vLoad(&g_scoobyGlobalSaveData, "ScoobyGlobal")) {
+ if (!vLoad(&_scoobyGlobalSaveData, "ScoobyGlobal")) {
// Initialize global save data: 10 levels, 3 slots each
for (int16 level = 0; level < 10; level++) {
- g_scoobyGameState.slotIndex[level] = 0;
+ _scoobyGameState.slotIndex[level] = 0;
int16 baseOff = level * 6;
for (int16 j = 0; j < 3; j++) {
- g_scoobyGlobalSaveData[baseOff + j * 2] = 3;
+ _scoobyGlobalSaveData[baseOff + j * 2] = 3;
}
// Randomly assign difficulty values 0, 1, 2 to the 3 slots
for (int16 j = 0; j < 3; j++) {
int16 slot = _xp->getRandom(3);
- while (g_scoobyGlobalSaveData[baseOff + slot * 2] != 3) {
+ while (_scoobyGlobalSaveData[baseOff + slot * 2] != 3) {
slot++;
if (slot >= 3)
slot = 0;
}
- g_scoobyGlobalSaveData[baseOff + slot * 2] = j;
+ _scoobyGlobalSaveData[baseOff + slot * 2] = j;
}
}
}
byte scoobyStateBuf[0x11A];
if (!vLoad(scoobyStateBuf, "Scooby")) {
- g_scoobyGameState.levelNumber = 1;
+ _scoobyGameState.levelNumber = 1;
if (!initScoobyLevel())
return false;
} else {
Common::SeekableReadStream *scoobyStateReadStream = new Common::MemoryReadStream(scoobyStateBuf, sizeof(scoobyStateBuf), DisposeAfterUse::NO);
- g_scoobyGameState.levelNumber = scoobyStateReadStream->readSint16BE(); // +0x00
+ _scoobyGameState.levelNumber = scoobyStateReadStream->readSint16BE(); // +0x00
for (int i = 0; i < 10; i++) // +0x02
- g_scoobyGameState.slotIndex[i] = scoobyStateReadStream->readSint16BE();
+ _scoobyGameState.slotIndex[i] = scoobyStateReadStream->readSint16BE();
- g_scoobyGameState.levelComplete = scoobyStateReadStream->readSint16BE(); // +0x16
+ _scoobyGameState.levelComplete = scoobyStateReadStream->readSint16BE(); // +0x16
for (int i = 0; i < 25; i++) // +0x18
for (int j = 0; j < 4; j++)
- g_scoobyGameState.wallStates[i][j] = scoobyStateReadStream->readSint16BE();
-
- g_scoobyGameState.scoobyCell = scoobyStateReadStream->readSint16BE(); // +0xE0
- g_scoobyGameState.scoobySavedCell = scoobyStateReadStream->readSint16BE(); // +0xE2
- g_scoobyGameState.leftNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE4
- g_scoobyGameState.rightNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE6
- g_scoobyGameState.upNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE8
- g_scoobyGameState.downNeighbor = scoobyStateReadStream->readSint16BE(); // +0xEA
- g_scoobyGameState.activeLevel = scoobyStateReadStream->readSint16BE(); // +0xEC
- g_scoobyGameState.scoobyX = scoobyStateReadStream->readSint16BE(); // +0xEE
- g_scoobyGameState.scoobyY = scoobyStateReadStream->readSint16BE(); // +0xF0
- g_scoobyGameState.velocityX = scoobyStateReadStream->readSint16BE(); // +0xF2
- g_scoobyGameState.velocityY = scoobyStateReadStream->readSint16BE(); // +0xF4
- g_scoobyGameState.targetVelocityX = scoobyStateReadStream->readSint16BE(); // +0xF6
- g_scoobyGameState.targetVelocityY = scoobyStateReadStream->readSint16BE(); // +0xF8
- g_scoobyGameState.transitionTarget = scoobyStateReadStream->readSint16BE(); // +0xFA
- g_scoobyGameState.currentAnim = scoobyStateReadStream->readSint16BE(); // +0xFC
- g_scoobyGameState.direction = scoobyStateReadStream->readSint16BE(); // +0xFE
- g_scoobyGameState.spriteFrameCount = scoobyStateReadStream->readSint16BE(); // +0x100
+ _scoobyGameState.wallStates[i][j] = scoobyStateReadStream->readSint16BE();
+
+ _scoobyGameState.scoobyCell = scoobyStateReadStream->readSint16BE(); // +0xE0
+ _scoobyGameState.scoobySavedCell = scoobyStateReadStream->readSint16BE(); // +0xE2
+ _scoobyGameState.leftNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE4
+ _scoobyGameState.rightNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE6
+ _scoobyGameState.upNeighbor = scoobyStateReadStream->readSint16BE(); // +0xE8
+ _scoobyGameState.downNeighbor = scoobyStateReadStream->readSint16BE(); // +0xEA
+ _scoobyGameState.activeLevel = scoobyStateReadStream->readSint16BE(); // +0xEC
+ _scoobyGameState.scoobyX = scoobyStateReadStream->readSint16BE(); // +0xEE
+ _scoobyGameState.scoobyY = scoobyStateReadStream->readSint16BE(); // +0xF0
+ _scoobyGameState.velocityX = scoobyStateReadStream->readSint16BE(); // +0xF2
+ _scoobyGameState.velocityY = scoobyStateReadStream->readSint16BE(); // +0xF4
+ _scoobyGameState.targetVelocityX = scoobyStateReadStream->readSint16BE(); // +0xF6
+ _scoobyGameState.targetVelocityY = scoobyStateReadStream->readSint16BE(); // +0xF8
+ _scoobyGameState.transitionTarget = scoobyStateReadStream->readSint16BE(); // +0xFA
+ _scoobyGameState.currentAnim = scoobyStateReadStream->readSint16BE(); // +0xFC
+ _scoobyGameState.direction = scoobyStateReadStream->readSint16BE(); // +0xFE
+ _scoobyGameState.spriteFrameCount = scoobyStateReadStream->readSint16BE(); // +0x100
for (int i = 0; i < 6; i++) { // +0x102
scoobyStateReadStream->readUint32BE(); // dummy values
- g_scoobyGameState.frameData[i] = nullptr;
+ _scoobyGameState.frameData[i] = nullptr;
}
assert(scoobyStateReadStream->pos() == 0x11A);
delete scoobyStateReadStream;
- if (g_scoobyGameState.levelComplete != 0) {
+ if (_scoobyGameState.levelComplete != 0) {
if (!initScoobyLevel())
return false;
} else {
@@ -1509,40 +1509,40 @@ bool BoltEngine::initScooby() {
}
void BoltEngine::cleanUpScooby() {
- vSave(&g_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
+ vSave(&_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
// Serialize the game state into a flat 0x11A-byte BE buffer
byte scoobyStateBuf[0x11A] = {0};
Common::MemoryWriteStream *scoobyStateWriteStream = new Common::MemoryWriteStream(scoobyStateBuf, sizeof(scoobyStateBuf));
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelNumber); // +0x00
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.levelNumber); // +0x00
for (int i = 0; i < 10; i++) // +0x02
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.slotIndex[i]);
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.slotIndex[i]);
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelComplete); // +0x16
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.levelComplete); // +0x16
for (int i = 0; i < 25; i++) // +0x18
for (int j = 0; j < 4; j++)
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.wallStates[i][j]);
-
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyCell); // +0xE0
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobySavedCell); // +0xE2
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.leftNeighbor); // +0xE4
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.rightNeighbor); // +0xE6
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.upNeighbor); // +0xE8
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.downNeighbor); // +0xEA
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.activeLevel); // +0xEC
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyX); // +0xEE
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyY); // +0xF0
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityX); // +0xF2
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityY); // +0xF4
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityX); // +0xF6
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityY); // +0xF8
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.transitionTarget); // +0xFA
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.currentAnim); // +0xFC
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.direction); // +0xFE
- scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.spriteFrameCount); // +0x100
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.wallStates[i][j]);
+
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobyCell); // +0xE0
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobySavedCell); // +0xE2
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.leftNeighbor); // +0xE4
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.rightNeighbor); // +0xE6
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.upNeighbor); // +0xE8
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.downNeighbor); // +0xEA
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.activeLevel); // +0xEC
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobyX); // +0xEE
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobyY); // +0xF0
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.velocityX); // +0xF2
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.velocityY); // +0xF4
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.targetVelocityX); // +0xF6
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.targetVelocityY); // +0xF8
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.transitionTarget); // +0xFA
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.currentAnim); // +0xFC
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.direction); // +0xFE
+ scoobyStateWriteStream->writeSint16BE(_scoobyGameState.spriteFrameCount); // +0x100
for (int i = 0; i < 6; i++) // +0x102
scoobyStateWriteStream->writeUint32BE(0); // pointers saved as zero
@@ -1555,11 +1555,11 @@ void BoltEngine::cleanUpScooby() {
cleanUpScoobyLevelGraphics();
cleanUpScoobyBaseAssets();
- if (g_scoobyTempPic.pixelData) {
- _xp->freeMem(g_scoobyTempPic.pixelData);
- g_scoobyTempPic.pixelData = nullptr;
- g_scoobyTempPic.width = 0;
- g_scoobyTempPic.height = 0;
+ if (_scoobyTempPic.pixelData) {
+ _xp->freeMem(_scoobyTempPic.pixelData);
+ _scoobyTempPic.pixelData = nullptr;
+ _scoobyTempPic.width = 0;
+ _scoobyTempPic.height = 0;
}
_xp->setFrameRate(0);
@@ -1582,24 +1582,24 @@ int16 BoltEngine::helpScooby() {
byte *helpPics[3];
// If Scooby is mid-movement, stop and face idle direction
- if (g_scoobyGameState.currentAnim != -2 && g_scoobyGameState.currentAnim != -3) {
- g_scoobyGameState.velocityX = 0;
- g_scoobyGameState.velocityY = 0;
+ if (_scoobyGameState.currentAnim != -2 && _scoobyGameState.currentAnim != -3) {
+ _scoobyGameState.velocityX = 0;
+ _scoobyGameState.velocityY = 0;
- if (g_scoobyGameState.direction == 2)
- g_scoobyGameState.transitionTarget = -2;
+ if (_scoobyGameState.direction == 2)
+ _scoobyGameState.transitionTarget = -2;
else
- g_scoobyGameState.transitionTarget = -3;
+ _scoobyGameState.transitionTarget = -3;
- g_scoobyDesiredDir = -1;
- g_scoobyInputHoldCount = 0;
- g_scoobyMoveRequested = 1;
+ _scoobyDesiredDir = -1;
+ _scoobyInputHoldCount = 0;
+ _scoobyMoveRequested = 1;
do {
updateScoobyTransition();
updateScoobyWalls();
_xp->updateDisplay();
- } while (g_scoobyTransitioning != 0);
+ } while (_scoobyTransitioning != 0);
updateScoobyTransition();
updateScoobyWalls();
@@ -1612,12 +1612,12 @@ int16 BoltEngine::helpScooby() {
_xp->setFrameRate(0);
_xp->setInactivityTimer(0);
- animateSSprite(&g_scoobySprite, 0);
+ animateSSprite(&_scoobySprite, 0);
// Load 3 help screen entries
for (int16 i = 0; i < 3; i++) {
- int16 memberIdx = READ_UINT16(g_scoobyLevelData + 0x0C) + i;
- helpEntries[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+ int16 memberIdx = READ_UINT16(_scoobyLevelData + 0x0C) + i;
+ helpEntries[i] = memberAddr(_scoobyBoltLib, memberIdx);
byte *entry = helpEntries[i];
if (READ_UINT32(entry) == 2)
@@ -1630,7 +1630,7 @@ int16 BoltEngine::helpScooby() {
// Load and display pic
int16 picMember = READ_UINT16(entry + 4);
- helpPics[i] = memberAddr(g_scoobyBoltLib, picMember);
+ helpPics[i] = memberAddr(_scoobyBoltLib, picMember);
displayPic(helpPics[i], 0, 0, 0);
}
@@ -1762,7 +1762,7 @@ int16 BoltEngine::helpScooby() {
if (wasPlaying != 0)
break;
- if (startAnimation(g_rtfHandle, 0x1C)) {
+ if (startAnimation(_rtfHandle, 0x1C)) {
animFrameIdx = 0;
isPlaying = 1;
}
@@ -1822,7 +1822,7 @@ int16 BoltEngine::helpScooby() {
hiliteScoobyHelpObject(helpEntries[i], 0);
}
- animateSSprite(&g_scoobySprite, 0);
+ animateSSprite(&_scoobySprite, 0);
_xp->updateDisplay();
_xp->setFrameRate(12);
@@ -1876,13 +1876,13 @@ int16 BoltEngine::playScooby() {
int16 inputDir = -1;
// Check if help screen should show on startup
- if (g_scoobyShowHelp != 0) {
+ if (_scoobyShowHelp != 0) {
int16 helpResult = helpScooby();
if (helpResult == 0)
return 5; // exit/quit
}
- g_scoobyShowHelp = 0;
+ _scoobyShowHelp = 0;
_xp->enableController();
// Main game loop
@@ -1890,7 +1890,7 @@ int16 BoltEngine::playScooby() {
updateScoobyLocation();
// Level complete?
- if (g_scoobyGameState.levelComplete != 0)
+ if (_scoobyGameState.levelComplete != 0)
return 16;
// Poll for event
@@ -1909,11 +1909,11 @@ int16 BoltEngine::playScooby() {
case etInactivity:
if (helpScooby() == 0)
return 5;
- g_scoobyMoveRequested = 0;
+ _scoobyMoveRequested = 0;
break;
case etSound:
- g_scoobySoundPlaying = 0;
+ _scoobySoundPlaying = 0;
updateScoobySound();
break;
@@ -1933,7 +1933,7 @@ int16 BoltEngine::playScooby() {
int16 BoltEngine::scoobyGame(int16 prevBooth) {
int16 result = 5;
- if (!openBOLTLib(&g_scoobyBoltLib, &g_scoobyBoltCallbacks, assetPath("scooby.blt")))
+ if (!openBOLTLib(&_scoobyBoltLib, &_scoobyBoltCallbacks, assetPath("scooby.blt")))
return result;
int16 prevInactivity = _xp->setInactivityTimer(30);
@@ -1944,13 +1944,13 @@ int16 BoltEngine::scoobyGame(int16 prevBooth) {
cleanUpScooby();
_xp->setInactivityTimer(prevInactivity);
- closeBOLTLib(&g_scoobyBoltLib);
+ closeBOLTLib(&_scoobyBoltLib);
return result;
}
void BoltEngine::swapScoobyHelpEntry() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
if (data == nullptr)
return;
@@ -1963,11 +1963,11 @@ void BoltEngine::swapScoobyHelpEntry() {
}
void BoltEngine::swapScoobyWordArray() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
if (data == nullptr)
return;
- int16 count = g_boltCurrentMemberEntry->decompSize >> 1;
+ int16 count = _boltCurrentMemberEntry->decompSize >> 1;
for (int16 i = 0; i < count; i++) {
WRITE_UINT16(data, READ_BE_UINT16(data));
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
index bd6233d20ae..d83e4bd707c 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/booths/topcat.cpp
@@ -39,105 +39,105 @@ int16 Bolt::BoltEngine::topCatGame(int16 prevBooth) {
}
bool BoltEngine::initTopCat() {
- g_topCatRtfHandle = openRTF(assetPath(g_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
- if (!g_topCatRtfHandle)
+ _topCatRtfHandle = openRTF(assetPath(_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
+ if (!_topCatRtfHandle)
return false;
- g_topCatAvHandle = openRTF(assetPath("topcatau.av"));
- if (!g_topCatAvHandle)
+ _topCatAvHandle = openRTF(assetPath("topcatau.av"));
+ if (!_topCatAvHandle)
return false;
- if (!openBOLTLib(&g_topCatBoltLib, &g_topCatBoltCallbacks, assetPath("topcat.blt")))
+ if (!openBOLTLib(&_topCatBoltLib, &_topCatBoltCallbacks, assetPath("topcat.blt")))
return false;
- if (!getBOLTGroup(g_topCatBoltLib, 0, 1))
+ if (!getBOLTGroup(_topCatBoltLib, 0, 1))
return false;
- g_topCatBackgroundPalette = memberAddr(g_topCatBoltLib, 0x20);
- g_topCatBackground = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x22 : 0x21);
- g_topCatBackgroundAnimationPalette = memberAddr(g_topCatBoltLib, 0x00);
- g_topCatGraphicsAssets = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x1E : 0x1D);
+ _topCatBackgroundPalette = memberAddr(_topCatBoltLib, 0x20);
+ _topCatBackground = memberAddr(_topCatBoltLib, _displayMode != 0 ? 0x22 : 0x21);
+ _topCatBackgroundAnimationPalette = memberAddr(_topCatBoltLib, 0x00);
+ _topCatGraphicsAssets = memberAddr(_topCatBoltLib, _displayMode != 0 ? 0x1E : 0x1D);
- g_topCatBackgroundAnimFrame = 0;
+ _topCatBackgroundAnimFrame = 0;
// Count background anim frames...
- g_topCatMaxBackgroundAnimFrames = 0;
- while (getResolvedPtr(g_topCatGraphicsAssets, g_topCatMaxBackgroundAnimFrames * 4) != 0) {
- g_topCatMaxBackgroundAnimFrames++;
+ _topCatMaxBackgroundAnimFrames = 0;
+ while (getResolvedPtr(_topCatGraphicsAssets, _topCatMaxBackgroundAnimFrames * 4) != 0) {
+ _topCatMaxBackgroundAnimFrames++;
}
- g_topCatCurrentAnimType = 0;
- g_topCatAnimStateMachineStep = 0;
- g_topCatQueuedSoundFrames = 0;
- g_topCatAnimQueueSize = 0;
+ _topCatCurrentAnimType = 0;
+ _topCatAnimStateMachineStep = 0;
+ _topCatQueuedSoundFrames = 0;
+ _topCatAnimQueueSize = 0;
- g_topCatButtonsPalette = memberAddr(g_topCatBoltLib, 0x3D);
- g_topCatBlinkEntries = memberAddr(g_topCatBoltLib, 0x3E);
+ _topCatButtonsPalette = memberAddr(_topCatBoltLib, 0x3D);
+ _topCatBlinkEntries = memberAddr(_topCatBoltLib, 0x3E);
- g_topCatLightsPalette = memberAddr(g_topCatBoltLib, 0x31);
- g_topCatBlinkSeqPtr = nullptr;
+ _topCatLightsPalette = memberAddr(_topCatBoltLib, 0x31);
+ _topCatBlinkSeqPtr = nullptr;
- g_topCatSelectedChoiceOverlayGfx = memberAddr(g_topCatBoltLib, 0x26);
- g_topCatCycleData = memberAddr(g_topCatBoltLib, 0x27);
- boltCycleToXPCycle(g_topCatCycleData, g_topCatCycleSpecs);
+ _topCatSelectedChoiceOverlayGfx = memberAddr(_topCatBoltLib, 0x26);
+ _topCatCycleData = memberAddr(_topCatBoltLib, 0x27);
+ boltCycleToXPCycle(_topCatCycleData, _topCatCycleSpecs);
- g_topCatChoiceCycleState->startIndex = 0;
- g_topCatChoiceCycleState->endIndex = 0;
- g_topCatChoiceCycleState->delay = 0;
- g_topCatChoiceCycleState->nextFire = 0;
- g_topCatChoiceCycleState->active = false;
+ _topCatChoiceCycleState->startIndex = 0;
+ _topCatChoiceCycleState->endIndex = 0;
+ _topCatChoiceCycleState->delay = 0;
+ _topCatChoiceCycleState->nextFire = 0;
+ _topCatChoiceCycleState->active = false;
- g_topCatShuffledQuestions = memberAddr(g_topCatBoltLib, 0x32); // [60]
- g_topCatShuffledAnswers = memberAddr(g_topCatBoltLib, 0x33); // [60]
- g_topCatAnswersPermutations = memberAddr(g_topCatBoltLib, 0x34); // [60 * 3]
- g_topCatAnswers = memberAddr(g_topCatBoltLib, 0x35);
- g_topCatAnswersScreenPositions = memberAddr(g_topCatBoltLib, 0x36);
+ _topCatShuffledQuestions = memberAddr(_topCatBoltLib, 0x32); // [60]
+ _topCatShuffledAnswers = memberAddr(_topCatBoltLib, 0x33); // [60]
+ _topCatAnswersPermutations = memberAddr(_topCatBoltLib, 0x34); // [60 * 3]
+ _topCatAnswers = memberAddr(_topCatBoltLib, 0x35);
+ _topCatAnswersScreenPositions = memberAddr(_topCatBoltLib, 0x36);
- if (vLoad(&g_topCatSavedScore, g_topCatSaveFileName)) {
- g_topCatScore = g_topCatSavedScore;
+ if (vLoad(&_topCatSavedScore, "TopCatBF")) {
+ _topCatScore = _topCatSavedScore;
} else {
- g_topCatScore = 0;
+ _topCatScore = 0;
}
- if (vLoad(g_topCatSaveBuffer, g_topCatStaticSaveFileName)) {
- g_topCatShuffledQuestionsArrayIdx = READ_BE_INT16(g_topCatSaveBuffer);
+ if (vLoad(_topCatSaveBuffer, "TopCatBFStatic")) {
+ _topCatShuffledQuestionsArrayIdx = READ_BE_INT16(_topCatSaveBuffer);
int offset;
- for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+ for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledQuestions); i++) {
offset = sizeof(int16) + i;
- g_topCatSavedShuffledQuestions[i] = g_topCatSaveBuffer[offset];
+ _topCatSavedShuffledQuestions[i] = _topCatSaveBuffer[offset];
}
- for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
- offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
- g_topCatSavedShuffledAnswers[i] = g_topCatSaveBuffer[offset];
+ for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledAnswers); i++) {
+ offset = sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ _topCatSavedShuffledAnswers[i] = _topCatSaveBuffer[offset];
}
- for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
- offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
- g_topCatSavedAnswersPermutations[i] = g_topCatSaveBuffer[offset];
+ for (int i = 0; i < ARRAYSIZE(_topCatSavedAnswersPermutations); i++) {
+ offset = sizeof(_topCatSavedShuffledAnswers) + sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ _topCatSavedAnswersPermutations[i] = _topCatSaveBuffer[offset];
}
int16 answerOff = 0;
for (int16 i = 0; i < 60; i++) {
- g_topCatShuffledQuestions[i] = g_topCatSavedShuffledQuestions[i];
- g_topCatShuffledAnswers[i] = g_topCatSavedShuffledAnswers[i];
+ _topCatShuffledQuestions[i] = _topCatSavedShuffledQuestions[i];
+ _topCatShuffledAnswers[i] = _topCatSavedShuffledAnswers[i];
for (int16 j = 0; j < 3; j++) {
- g_topCatAnswersPermutations[answerOff + j] = g_topCatSavedAnswersPermutations[answerOff + j];
+ _topCatAnswersPermutations[answerOff + j] = _topCatSavedAnswersPermutations[answerOff + j];
}
answerOff += 3;
}
} else {
- g_topCatShuffledQuestionsArrayIdx = -1;
+ _topCatShuffledQuestionsArrayIdx = -1;
shuffleTopCatQuestions();
shuffleTopCatPermutations();
}
- g_topCatBlinkTimer = 0;
+ _topCatBlinkTimer = 0;
- getTopCatSoundInfo(g_topCatBoltLib, 0x1F, &g_topCatSoundInfo);
+ getTopCatSoundInfo(_topCatBoltLib, 0x1F, &_topCatSoundInfo);
uint32 dummy;
while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -145,39 +145,39 @@ bool BoltEngine::initTopCat() {
_xp->stopCycle();
_xp->setTransparency(false);
- displayColors(g_topCatBackgroundPalette, stFront, 0);
- displayPic(g_topCatBackground, g_displayX, g_displayY, stFront);
+ displayColors(_topCatBackgroundPalette, stFront, 0);
+ displayPic(_topCatBackground, _displayX, _displayY, stFront);
_xp->updateDisplay();
_xp->setTransparency(true);
- displayColors(g_topCatBackgroundAnimationPalette, stFront, 0);
- displayPic(g_topCatBackground, g_displayX, g_displayY, stBack);
+ displayColors(_topCatBackgroundAnimationPalette, stFront, 0);
+ displayPic(_topCatBackground, _displayX, _displayY, stBack);
- byte *firstBackgroundAnimFrame = getResolvedPtr(g_topCatGraphicsAssets, 0);
- displayPic(firstBackgroundAnimFrame, g_displayX, g_displayY, stFront);
+ byte *firstBackgroundAnimFrame = getResolvedPtr(_topCatGraphicsAssets, 0);
+ displayPic(firstBackgroundAnimFrame, _displayX, _displayY, stFront);
- displayColors(g_topCatBackgroundPalette, stBack, 0);
+ displayColors(_topCatBackgroundPalette, stBack, 0);
if (!setupNextQuestion())
return false;
_xp->updateDisplay();
- g_topCatHoveredEntry = nullptr;
+ _topCatHoveredEntry = nullptr;
// Init button palettes...
int16 off = 0;
byte *entry;
while (true) {
- entry = getResolvedPtr(g_topCatButtonsPalette, off);
+ entry = getResolvedPtr(_topCatButtonsPalette, off);
if (!entry)
break;
if (READ_UINT32(entry) == 1)
- g_topCatHelpButton = entry;
+ _topCatHelpButton = entry;
if (READ_UINT32(entry) == 2)
- g_topCatBackButton = entry;
+ _topCatBackButton = entry;
_xp->getPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x10), entry + 0x2E);
unpackColors(READ_UINT16(entry + 0x10), entry + 0x12);
@@ -188,7 +188,7 @@ bool BoltEngine::initTopCat() {
// Init light palelttes...
int16 lightOff = 0;
for (int16 i = 0; i < 6; i++) {
- byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, lightOff);
+ byte *lightEntry = getResolvedPtr(_topCatLightsPalette, lightOff);
int16 colorCount = READ_UINT16(lightEntry + 4);
int16 palStart = READ_UINT16(lightEntry);
@@ -204,7 +204,7 @@ bool BoltEngine::initTopCat() {
}
// Set lights based on saved score...
- int16 litMask = (1 << g_topCatScore) - 1;
+ int16 litMask = (1 << _topCatScore) - 1;
setScoreLights(litMask);
return true;
@@ -214,63 +214,63 @@ void BoltEngine::cleanUpTopCat() {
_xp->stopSound();
_xp->stopCycle();
- if (g_topCatScore < 6) {
- byte questionIdx = g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
- g_topCatShuffledAnswers[questionIdx]--;
- g_topCatShuffledQuestionsArrayIdx--;
+ if (_topCatScore < 6) {
+ byte questionIdx = _topCatShuffledQuestions[_topCatShuffledQuestionsArrayIdx];
+ _topCatShuffledAnswers[questionIdx]--;
+ _topCatShuffledQuestionsArrayIdx--;
} else {
- g_topCatScore = 0;
+ _topCatScore = 0;
}
- g_topCatSavedScore = g_topCatScore;
- g_topCatSaveHistory = g_topCatShuffledQuestionsArrayIdx;
+ _topCatSavedScore = _topCatScore;
+ _topCatSaveHistory = _topCatShuffledQuestionsArrayIdx;
int16 answerOff = 0;
for (int16 i = 0; i < 60; i++) {
- g_topCatSavedShuffledQuestions[i] = g_topCatShuffledQuestions[i];
- g_topCatSavedShuffledAnswers[i] = g_topCatShuffledAnswers[i];
+ _topCatSavedShuffledQuestions[i] = _topCatShuffledQuestions[i];
+ _topCatSavedShuffledAnswers[i] = _topCatShuffledAnswers[i];
for (int16 j = 0; j < 3; j++) {
- g_topCatSavedAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + j];
+ _topCatSavedAnswersPermutations[answerOff + j] = _topCatAnswersPermutations[answerOff + j];
}
answerOff += 3;
}
- memset(g_topCatSaveBuffer, 0, sizeof(g_topCatSaveBuffer));
+ memset(_topCatSaveBuffer, 0, sizeof(_topCatSaveBuffer));
- WRITE_BE_INT16(g_topCatSaveBuffer, g_topCatSaveHistory);
+ WRITE_BE_INT16(_topCatSaveBuffer, _topCatSaveHistory);
int offset;
- for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+ for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledQuestions); i++) {
offset = sizeof(int16) + i;
- g_topCatSaveBuffer[offset] = g_topCatSavedShuffledQuestions[i];
+ _topCatSaveBuffer[offset] = _topCatSavedShuffledQuestions[i];
}
- for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
- offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
- g_topCatSaveBuffer[offset] = g_topCatSavedShuffledAnswers[i];
+ for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledAnswers); i++) {
+ offset = sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ _topCatSaveBuffer[offset] = _topCatSavedShuffledAnswers[i];
}
- for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
- offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
- g_topCatSaveBuffer[offset] = g_topCatSavedAnswersPermutations[i];
+ for (int i = 0; i < ARRAYSIZE(_topCatSavedAnswersPermutations); i++) {
+ offset = sizeof(_topCatSavedShuffledAnswers) + sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+ _topCatSaveBuffer[offset] = _topCatSavedAnswersPermutations[i];
}
- vSave(&g_topCatSavedScore, sizeof(g_topCatSavedScore), g_topCatSaveFileName);
- vSave(g_topCatSaveBuffer, sizeof(g_topCatSaveBuffer), g_topCatStaticSaveFileName);
+ vSave(&_topCatSavedScore, sizeof(_topCatSavedScore), "TopCatBF");
+ vSave(_topCatSaveBuffer, sizeof(_topCatSaveBuffer), "TopCatBFStatic");
- freeBOLTGroup(g_topCatBoltLib, 0, 1);
- closeBOLTLib(&g_topCatBoltLib);
+ freeBOLTGroup(_topCatBoltLib, 0, 1);
+ closeBOLTLib(&_topCatBoltLib);
- if (g_topCatRtfHandle) {
- closeRTF(g_topCatRtfHandle);
- g_topCatRtfHandle = nullptr;
+ if (_topCatRtfHandle) {
+ closeRTF(_topCatRtfHandle);
+ _topCatRtfHandle = nullptr;
}
- if (g_topCatAvHandle) {
- closeRTF(g_topCatAvHandle);
- g_topCatAvHandle = nullptr;
+ if (_topCatAvHandle) {
+ closeRTF(_topCatAvHandle);
+ _topCatAvHandle = nullptr;
}
// These currently erroneously clear some graphics while on the "win a letter"
@@ -309,23 +309,23 @@ int16 BoltEngine::playTopCat() {
}
case etTimer: { // Timer expired
- if (eventData != g_topCatBlinkTimer)
+ if (eventData != _topCatBlinkTimer)
break;
- if (!g_topCatBlinkEntry)
+ if (!_topCatBlinkEntry)
break;
- g_topCatBlinkTimer = _xp->startTimer(500);
+ _topCatBlinkTimer = _xp->startTimer(500);
// Toggle highlight...
int16 highlight;
- if (READ_UINT16(g_topCatBlinkEntry + 0x0C) & 1) {
+ if (READ_UINT16(_topCatBlinkEntry + 0x0C) & 1) {
highlight = 0;
} else {
highlight = 1;
}
- highlightObject(g_topCatBlinkEntry, highlight);
+ highlightObject(_topCatBlinkEntry, highlight);
break;
}
@@ -339,7 +339,7 @@ int16 BoltEngine::playTopCat() {
int16 off = 0;
while (true) {
- entry = getResolvedPtr(g_topCatButtonsPalette, off);
+ entry = getResolvedPtr(_topCatButtonsPalette, off);
if (!entry)
break;
@@ -359,37 +359,37 @@ int16 BoltEngine::playTopCat() {
}
// Unhighlight previous entry if needed...
- if (g_topCatHoveredEntry != entry &&
- g_topCatHoveredEntry != nullptr &&
- g_topCatHoveredEntry != g_topCatBlinkEntry &&
- g_topCatHoveredEntry != g_topCatBackButton) {
-
- if (g_topCatHoveredEntry == g_topCatHelpButton) {
- if (g_topCatCurrentAnimType != 2)
- highlightObject(g_topCatHoveredEntry, 0);
+ if (_topCatHoveredEntry != entry &&
+ _topCatHoveredEntry != nullptr &&
+ _topCatHoveredEntry != _topCatBlinkEntry &&
+ _topCatHoveredEntry != _topCatBackButton) {
+
+ if (_topCatHoveredEntry == _topCatHelpButton) {
+ if (_topCatCurrentAnimType != 2)
+ highlightObject(_topCatHoveredEntry, 0);
} else {
- byte *hovered = g_topCatHoveredEntry;
+ byte *hovered = _topCatHoveredEntry;
if (READ_UINT32(hovered) < 3 || READ_UINT32(hovered) >= 6)
- highlightObject(g_topCatHoveredEntry, 0);
+ highlightObject(_topCatHoveredEntry, 0);
}
}
- g_topCatHoveredEntry = entry;
+ _topCatHoveredEntry = entry;
// Highlight new entry if applicable...
if (!entry)
break;
- if (entry == g_topCatBlinkEntry)
+ if (entry == _topCatBlinkEntry)
break;
- if (entry == g_topCatBackButton)
+ if (entry == _topCatBackButton)
break;
if (READ_UINT32(entry) >= 3 && READ_UINT32(entry) < 6)
break;
- highlightObject(g_topCatHoveredEntry, 1);
+ highlightObject(_topCatHoveredEntry, 1);
break;
}
@@ -403,7 +403,7 @@ int16 BoltEngine::playTopCat() {
case etSound: {
int16 soundType;
- if (soundDataPtr >= g_topCatSoundInfo.data && soundDataPtr < g_topCatSoundInfo.data + g_topCatSoundInfo.size) {
+ if (soundDataPtr >= _topCatSoundInfo.data && soundDataPtr < _topCatSoundInfo.data + _topCatSoundInfo.size) {
soundType = 1;
} else {
soundType = 2;
@@ -419,9 +419,9 @@ int16 BoltEngine::playTopCat() {
case etInactivity: {
// Toggle blinking highlight...
int16 highlight;
- if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
- g_topCatBlinkEntry != 0) {
- if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ if (_topCatBlinkEntry == _topCatHoveredEntry &&
+ _topCatBlinkEntry != 0) {
+ if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
highlight = 1;
} else {
highlight = 0;
@@ -430,32 +430,32 @@ int16 BoltEngine::playTopCat() {
highlight = 0;
}
- highlightObject(g_topCatBlinkEntry, highlight);
+ highlightObject(_topCatBlinkEntry, highlight);
// Set help button as new blinking entry...
- g_topCatBlinkEntry = g_topCatHelpButton;
+ _topCatBlinkEntry = _topCatHelpButton;
- if (g_topCatHelpButton != 0) {
- if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
- g_topCatBlinkTimer = _xp->startTimer(500);
+ if (_topCatHelpButton != 0) {
+ if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
+ _topCatBlinkTimer = _xp->startTimer(500);
} else {
- g_topCatBlinkTimer = 0;
+ _topCatBlinkTimer = 0;
}
} else {
- g_topCatBlinkTimer = 0;
+ _topCatBlinkTimer = 0;
}
- highlightObject(g_topCatBlinkEntry, 1);
+ highlightObject(_topCatBlinkEntry, 1);
break;
}
case etTrigger: { // Palette cycle event
- if (g_topCatCurrentAnimType == 2) {
+ if (_topCatCurrentAnimType == 2) {
// Toggle blinking highlight...
int16 highlight;
- if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
- g_topCatBlinkEntry != 0) {
- if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ if (_topCatBlinkEntry == _topCatHoveredEntry &&
+ _topCatBlinkEntry != 0) {
+ if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
highlight = 1;
} else {
highlight = 0;
@@ -464,45 +464,45 @@ int16 BoltEngine::playTopCat() {
highlight = 0;
}
- highlightObject(g_topCatBlinkEntry, highlight);
+ highlightObject(_topCatBlinkEntry, highlight);
// Advance to next blinking entry...
- if (g_topCatCycleStep & 1) {
- g_topCatBlinkEntry = nullptr;
+ if (_topCatCycleStep & 1) {
+ _topCatBlinkEntry = nullptr;
} else {
- g_topCatBlinkEntry = getResolvedPtr(g_topCatBlinkEntries, (g_topCatCycleStep >> 1) * 4);
+ _topCatBlinkEntry = getResolvedPtr(_topCatBlinkEntries, (_topCatCycleStep >> 1) * 4);
}
- if (g_topCatBlinkEntry != 0) {
- if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
- g_topCatBlinkTimer = _xp->startTimer(500);
+ if (_topCatBlinkEntry != 0) {
+ if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
+ _topCatBlinkTimer = _xp->startTimer(500);
} else {
- g_topCatBlinkTimer = 0;
+ _topCatBlinkTimer = 0;
}
} else {
- g_topCatBlinkTimer = 0;
+ _topCatBlinkTimer = 0;
}
- highlightObject(g_topCatBlinkEntry, 1);
- g_topCatCycleStep++;
+ highlightObject(_topCatBlinkEntry, 1);
+ _topCatCycleStep++;
- } else if (g_topCatCurrentAnimType == 3) {
+ } else if (_topCatCurrentAnimType == 3) {
// Unhighlight previous...
- if (g_topCatCycleStep > 3) {
- byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+ if (_topCatCycleStep > 3) {
+ byte *prevEntry = getResolvedPtr(_topCatButtonsPalette, (_topCatCycleStep - 1) * 4);
highlightObject(prevEntry, 0);
} else {
- highlightObject(g_topCatBackButton, 0);
+ highlightObject(_topCatBackButton, 0);
}
// Highlight next...
- byte *nextEntry = getResolvedPtr(g_topCatButtonsPalette, g_topCatCycleStep * 4);
+ byte *nextEntry = getResolvedPtr(_topCatButtonsPalette, _topCatCycleStep * 4);
highlightObject(nextEntry, 1);
- g_topCatCycleStep++;
- } else if (g_topCatCurrentAnimType == 6) {
+ _topCatCycleStep++;
+ } else if (_topCatCurrentAnimType == 6) {
// Light blinking animation...
- int16 mask = READ_UINT16(g_topCatBlinkSeqPtr);
- g_topCatBlinkSeqPtr++;
+ int16 mask = READ_UINT16(_topCatBlinkSeqPtr);
+ _topCatBlinkSeqPtr++;
setScoreLights(mask);
}
@@ -514,8 +514,8 @@ int16 BoltEngine::playTopCat() {
break;
}
- if (exitFlag && g_topCatAnimQueueSize == 0 && g_topCatCurrentAnimType == 1) {
- if (g_topCatScore != 6)
+ if (exitFlag && _topCatAnimQueueSize == 0 && _topCatCurrentAnimType == 1) {
+ if (_topCatScore != 6)
_xp->hideCursor();
return result;
@@ -524,14 +524,14 @@ int16 BoltEngine::playTopCat() {
}
int16 BoltEngine::handleActionButton(int16 *result) {
- int16 prevState = g_topCatCurrentAnimType;
+ int16 prevState = _topCatCurrentAnimType;
- switch (g_topCatCurrentAnimType) {
+ switch (_topCatCurrentAnimType) {
case 2:
case 3:
stopAnimation();
_xp->stopSound();
- g_topCatQueuedSoundFrames = 0;
+ _topCatQueuedSoundFrames = 0;
setAnimType(1);
break;
@@ -543,22 +543,22 @@ int16 BoltEngine::handleActionButton(int16 *result) {
break;
}
- if (g_topCatAnimQueueSize != 0 && g_topCatHoveredEntry != 0) {
- if (READ_UINT32(g_topCatHoveredEntry) != 0)
+ if (_topCatAnimQueueSize != 0 && _topCatHoveredEntry != 0) {
+ if (READ_UINT32(_topCatHoveredEntry) != 0)
return 0;
}
- if (!g_topCatHoveredEntry)
+ if (!_topCatHoveredEntry)
return 0;
_xp->setInactivityTimer(30);
// If blinking entry is the help button, stop blinking...
- if (g_topCatBlinkEntry == g_topCatHelpButton) {
+ if (_topCatBlinkEntry == _topCatHelpButton) {
int16 highlight;
- if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
- g_topCatBlinkEntry != 0) {
- if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ if (_topCatBlinkEntry == _topCatHoveredEntry &&
+ _topCatBlinkEntry != 0) {
+ if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
highlight = 1;
} else {
highlight = 0;
@@ -567,13 +567,13 @@ int16 BoltEngine::handleActionButton(int16 *result) {
highlight = 0;
}
- highlightObject(g_topCatBlinkEntry, highlight);
- g_topCatBlinkEntry = 0;
- g_topCatBlinkTimer = 0;
- highlightObject(g_topCatBlinkEntry, 1);
+ highlightObject(_topCatBlinkEntry, highlight);
+ _topCatBlinkEntry = 0;
+ _topCatBlinkTimer = 0;
+ highlightObject(_topCatBlinkEntry, 1);
}
- int32 entryType = READ_UINT32(g_topCatHoveredEntry);
+ int32 entryType = READ_UINT32(_topCatHoveredEntry);
if (entryType == 0) {
*result = 8;
@@ -603,34 +603,34 @@ int16 BoltEngine::handleActionButton(int16 *result) {
int16 answerSlot = (int16)entryType - 3;
// Display overlay pic on the answer slot...
- int16 selectedChoiceOverlayX = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4);
- int16 selectedChoiceOverlayY = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4 + 2);
- byte *selectedChoiceOverlayPic = getResolvedPtr(g_topCatSelectedChoiceOverlayGfx, answerSlot * 4);
+ int16 selectedChoiceOverlayX = READ_UINT16(_topCatAnswersScreenPositions + answerSlot * 4);
+ int16 selectedChoiceOverlayY = READ_UINT16(_topCatAnswersScreenPositions + answerSlot * 4 + 2);
+ byte *selectedChoiceOverlayPic = getResolvedPtr(_topCatSelectedChoiceOverlayGfx, answerSlot * 4);
displayPic(selectedChoiceOverlayPic, selectedChoiceOverlayX, selectedChoiceOverlayY, stBack);
_xp->updateDisplay();
// Start palette cycle on the answer slot overlay...
- g_topCatChoiceCycleState[0].startIndex = g_topCatCycleSpecs[answerSlot].startIndex;
- g_topCatChoiceCycleState[0].endIndex = g_topCatCycleSpecs[answerSlot].endIndex;
- g_topCatChoiceCycleState[0].delay = g_topCatCycleSpecs[answerSlot].delay;
- g_topCatChoiceCycleState[0].nextFire = g_topCatCycleSpecs[answerSlot].nextFire;
- g_topCatChoiceCycleState[0].active = g_topCatCycleSpecs[answerSlot].active;
+ _topCatChoiceCycleState[0].startIndex = _topCatCycleSpecs[answerSlot].startIndex;
+ _topCatChoiceCycleState[0].endIndex = _topCatCycleSpecs[answerSlot].endIndex;
+ _topCatChoiceCycleState[0].delay = _topCatCycleSpecs[answerSlot].delay;
+ _topCatChoiceCycleState[0].nextFire = _topCatCycleSpecs[answerSlot].nextFire;
+ _topCatChoiceCycleState[0].active = _topCatCycleSpecs[answerSlot].active;
- _xp->startCycle(g_topCatChoiceCycleState);
+ _xp->startCycle(_topCatChoiceCycleState);
// Check answer...
- int8 correctAnswer = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3];
+ int8 correctAnswer = (int8)_topCatAnswers[_topCatCurrentAnswerIdx * 3];
if (correctAnswer == answerSlot) {
// Correct! :-)
queueAnim(4, 0); // Correct answer animation
queueAnim(6, 0); // Blink the lights
- g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx] |= 0x80; // Answered!
- g_topCatScore++;
+ _topCatShuffledQuestions[_topCatShuffledQuestionsArrayIdx] |= 0x80; // Answered!
+ _topCatScore++;
- if (g_topCatScore == 6) {
+ if (_topCatScore == 6) {
queueAnim(6, 0); // Blink the lights
*result = 0x10;
return 1;
@@ -647,13 +647,13 @@ int16 BoltEngine::handleActionButton(int16 *result) {
}
void BoltEngine::queueAnim(int16 animType, int16 param) {
- int16 slot = g_topCatAnimQueueSize;
- g_topCatAnimQueueSize++;
+ int16 slot = _topCatAnimQueueSize;
+ _topCatAnimQueueSize++;
- TopCatAnim *entry = &g_topCatAnimQueue[slot];
+ TopCatAnim *entry = &_topCatAnimQueue[slot];
- if (g_topCatAnimQueueSize > 3) {
- g_topCatAnimQueueSize--;
+ if (_topCatAnimQueueSize > 3) {
+ _topCatAnimQueueSize--;
return;
}
@@ -669,25 +669,25 @@ void BoltEngine::queueAnim(int16 animType, int16 param) {
break;
case 4: // Correct answer
- entry->animIndex = g_topCatCorrectAnimIdx;
- g_topCatCorrectAnimIdx++;
- g_topCatCorrectAnimIdx %= 3;
+ entry->animIndex = _topCatCorrectAnimIdx;
+ _topCatCorrectAnimIdx++;
+ _topCatCorrectAnimIdx %= 3;
break;
case 5: // Wrong answer
- entry->animIndex = g_topCatWrongAnimIdx + 3;
- g_topCatWrongAnimIdx++;
- g_topCatWrongAnimIdx %= 3;
+ entry->animIndex = _topCatWrongAnimIdx + 3;
+ _topCatWrongAnimIdx++;
+ _topCatWrongAnimIdx %= 3;
break;
case 6: // Score light
- if (g_topCatScore < 6)
- entry->animIndex = g_topCatScore + 1;
+ if (_topCatScore < 6)
+ entry->animIndex = _topCatScore + 1;
else
entry->animIndex = 0;
// Store blink sequence pointer from table
- entry->seqPtr = g_topCatBlinkSeqTable[g_topCatScore];
+ entry->seqPtr = _topCatBlinkSeqTable[_topCatScore];
break;
}
}
@@ -695,15 +695,15 @@ void BoltEngine::queueAnim(int16 animType, int16 param) {
bool BoltEngine::maintainAnim(int16 soundEvent) {
int16 animDone = 0;
- switch (g_topCatAnimStateMachineStep) {
+ switch (_topCatAnimStateMachineStep) {
case 0: // Waiting to start next anim
- if (g_topCatAnimQueueSize == 0)
+ if (_topCatAnimQueueSize == 0)
break;
- if (!startNextAnim(&g_topCatShouldPlayAnswerAnim))
+ if (!startNextAnim(&_topCatShouldPlayAnswerAnim))
return false;
- g_topCatAnimStateMachineStep = 1;
+ _topCatAnimStateMachineStep = 1;
break;
case 1: // Filling RTF buffer
if (fillRTFBuffer()) {
@@ -711,51 +711,51 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
break;
}
- g_topCatAnimStateMachineStep = 2;
+ _topCatAnimStateMachineStep = 2;
break;
case 2: // Waiting for sound sync
if (!soundEvent)
break;
- if (g_topCatMaxBackgroundAnimFrames - g_topCatQueuedSoundFrames != g_topCatBackgroundAnimFrame)
+ if (_topCatMaxBackgroundAnimFrames - _topCatQueuedSoundFrames != _topCatBackgroundAnimFrame)
break;
flushRTFSoundQueue();
- g_topCatAnimStateMachineStep = 3;
+ _topCatAnimStateMachineStep = 3;
break;
case 3: // Waiting for AV end
case 4: // Playing AV
- if (g_topCatAnimStateMachineStep == 3) {
+ if (_topCatAnimStateMachineStep == 3) {
if (soundEvent != 2)
break;
- g_topCatAnimStateMachineStep = 4;
- g_topCatMaintainSoundFlag = 1;
+ _topCatAnimStateMachineStep = 4;
+ _topCatMaintainSoundFlag = 1;
}
int16 avResult;
- if (g_topCatShouldPlayAnswerAnim != 0)
+ if (_topCatShouldPlayAnswerAnim != 0)
avResult = maintainAV(soundEvent == 2 ? 1 : 0);
else
avResult = maintainAudioPlay(soundEvent == 2 ? 1 : 0);
if (!avResult) { // AV finished
- if (g_topCatShouldPlayAnswerAnim != 0) {
+ if (_topCatShouldPlayAnswerAnim != 0) {
// Display background anim frame...
- byte *animFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
- displayPic(animFrame, g_displayX, g_displayY, stFront);
+ byte *animFrame = getResolvedPtr(_topCatGraphicsAssets, _topCatBackgroundAnimFrame * 4);
+ displayPic(animFrame, _displayX, _displayY, stFront);
}
animDone = 1;
} else {
// Check if idle sound needs maintaining...
- if (g_topCatMaintainSoundFlag != 0) {
+ if (_topCatMaintainSoundFlag != 0) {
if (!isRTFPlaying()) {
- int16 savedFrame = g_topCatBackgroundAnimFrame;
- g_topCatBackgroundAnimFrame = 0;
+ int16 savedFrame = _topCatBackgroundAnimFrame;
+ _topCatBackgroundAnimFrame = 0;
maintainIdleSound(0);
- g_topCatBackgroundAnimFrame = savedFrame;
- g_topCatMaintainSoundFlag = 0;
+ _topCatBackgroundAnimFrame = savedFrame;
+ _topCatMaintainSoundFlag = 0;
}
}
}
@@ -764,14 +764,14 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
}
// Advance audio and the background animation together...
- if (soundEvent == 1 || (soundEvent == 2 && g_topCatShouldPlayAnswerAnim == 0)) {
- g_topCatBackgroundAnimFrame++;
- if (g_topCatBackgroundAnimFrame >= g_topCatMaxBackgroundAnimFrames)
- g_topCatBackgroundAnimFrame = 0;
+ if (soundEvent == 1 || (soundEvent == 2 && _topCatShouldPlayAnswerAnim == 0)) {
+ _topCatBackgroundAnimFrame++;
+ if (_topCatBackgroundAnimFrame >= _topCatMaxBackgroundAnimFrames)
+ _topCatBackgroundAnimFrame = 0;
if (soundEvent == 1) {
- if (g_topCatAnimStateMachineStep == 3 || g_topCatAnimStateMachineStep == 4) {
- g_topCatQueuedSoundFrames--;
+ if (_topCatAnimStateMachineStep == 3 || _topCatAnimStateMachineStep == 4) {
+ _topCatQueuedSoundFrames--;
} else {
maintainIdleSound(1);
}
@@ -780,8 +780,8 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
// If no pending sound event, display the next background anim frame...
uint32 peekData;
if (_xp->peekEvent(etSound, &peekData) != etSound) {
- byte *backgroundFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
- displayPic(backgroundFrame, g_displayX, g_displayY, stFront);
+ byte *backgroundFrame = getResolvedPtr(_topCatGraphicsAssets, _topCatBackgroundAnimFrame * 4);
+ displayPic(backgroundFrame, _displayX, _displayY, stFront);
_xp->updateDisplay();
}
}
@@ -793,19 +793,19 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
}
void BoltEngine::maintainIdleSound(int16 decrement) {
- byte *soundData = g_topCatSoundInfo.data;
- int16 chunkSize = (int16)(g_topCatSoundInfo.size / g_topCatMaxBackgroundAnimFrames);
+ byte *soundData = _topCatSoundInfo.data;
+ int16 chunkSize = (int16)(_topCatSoundInfo.size / _topCatMaxBackgroundAnimFrames);
if (decrement)
- g_topCatQueuedSoundFrames--;
+ _topCatQueuedSoundFrames--;
- int16 idx = g_topCatBackgroundAnimFrame + g_topCatQueuedSoundFrames;
+ int16 idx = _topCatBackgroundAnimFrame + _topCatQueuedSoundFrames;
- while (g_topCatQueuedSoundFrames < 10) {
- g_topCatQueuedSoundFrames++;
+ while (_topCatQueuedSoundFrames < 10) {
+ _topCatQueuedSoundFrames++;
- if (idx >= g_topCatMaxBackgroundAnimFrames)
- idx -= g_topCatMaxBackgroundAnimFrames;
+ if (idx >= _topCatMaxBackgroundAnimFrames)
+ idx -= _topCatMaxBackgroundAnimFrames;
_xp->playSound(soundData + chunkSize * idx, chunkSize, 22050);
idx++;
@@ -813,10 +813,10 @@ void BoltEngine::maintainIdleSound(int16 decrement) {
}
bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
- TopCatAnim *entry = &g_topCatAnimQueue[0];
+ TopCatAnim *entry = &_topCatAnimQueue[0];
bool startResult = false;
- if (g_topCatAnimQueueSize == 0)
+ if (_topCatAnimQueueSize == 0)
return false;
// Setup next question, if requested...
@@ -828,68 +828,68 @@ bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
_xp->updateDisplay();
}
- entry->animIndex = g_topCatCurrentAnswerIdx * 60 + g_topCatDrawnQuestionId + 7;
+ entry->animIndex = _topCatCurrentAnswerIdx * 60 + _topCatDrawnQuestionId + 7;
}
int16 animType = entry->animType;
if (animType == 6) {
// Score light animation
- g_topCatBlinkSeqPtr = entry->seqPtr;
+ _topCatBlinkSeqPtr = entry->seqPtr;
*playAnswerAnim = 0;
- startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+ startResult = startAnimation(_topCatAvHandle, entry->animIndex);
} else if (animType == 3) {
// Question audio
- g_topCatCycleStep = 3;
+ _topCatCycleStep = 3;
*playAnswerAnim = 0;
- startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+ startResult = startAnimation(_topCatAvHandle, entry->animIndex);
} else if (animType == 2) {
// Help audio
- g_topCatCycleStep = 0;
+ _topCatCycleStep = 0;
*playAnswerAnim = 0;
- startResult = startAnimation(g_rtfHandle, entry->animIndex);
+ startResult = startAnimation(_rtfHandle, entry->animIndex);
} else {
// Correct/wrong answer animations
*playAnswerAnim = 1;
setAVBufferSize(0x4B000);
- startResult = prepareAV(g_topCatRtfHandle, entry->animIndex,
- g_displayWidth, g_displayHeight,
- g_displayX, g_displayY);
+ startResult = prepareAV(_topCatRtfHandle, entry->animIndex,
+ _displayWidth, _displayHeight,
+ _displayX, _displayY);
}
if (startResult) {
setAnimType(entry->animType);
} else {
- g_topCatCurrentAnimType = 1;
- g_topCatAnimStateMachineStep = 0;
+ _topCatCurrentAnimType = 1;
+ _topCatAnimStateMachineStep = 0;
maintainIdleSound(0);
}
// Shift queue: copy entries 1..n down to 0..n-1
- for (int16 i = 1; i < g_topCatAnimQueueSize; i++) {
- g_topCatAnimQueue[i - 1] = g_topCatAnimQueue[i];
+ for (int16 i = 1; i < _topCatAnimQueueSize; i++) {
+ _topCatAnimQueue[i - 1] = _topCatAnimQueue[i];
}
- g_topCatAnimQueueSize--;
+ _topCatAnimQueueSize--;
return true;
}
void BoltEngine::setAnimType(int16 newType) {
- if (g_topCatCurrentAnimType == newType)
+ if (_topCatCurrentAnimType == newType)
return;
// Clean-up the previous state...
- switch (g_topCatCurrentAnimType) {
+ switch (_topCatCurrentAnimType) {
case 2: // Was playing the help audio
// Stop blinking entry
int16 highlight;
- if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
- g_topCatBlinkEntry != 0) {
- if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+ if (_topCatBlinkEntry == _topCatHoveredEntry &&
+ _topCatBlinkEntry != 0) {
+ if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
highlight = 1;
} else {
highlight = 0;
@@ -898,20 +898,20 @@ void BoltEngine::setAnimType(int16 newType) {
highlight = 0;
}
- highlightObject(g_topCatBlinkEntry, highlight);
+ highlightObject(_topCatBlinkEntry, highlight);
- g_topCatBlinkEntry = 0;
- g_topCatBlinkTimer = 0;
- highlightObject(g_topCatBlinkEntry, 1);
+ _topCatBlinkEntry = 0;
+ _topCatBlinkTimer = 0;
+ highlightObject(_topCatBlinkEntry, 1);
break;
case 3: // Was playing the question audio
- if (g_topCatCycleStep >= 3) {
- byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+ if (_topCatCycleStep >= 3) {
+ byte *prevEntry = getResolvedPtr(_topCatButtonsPalette, (_topCatCycleStep - 1) * 4);
highlightObject(prevEntry, 0);
}
- highlightObject(g_topCatBackButton, 0);
+ highlightObject(_topCatBackButton, 0);
break;
case 4: // Correct answer anim
@@ -919,7 +919,7 @@ void BoltEngine::setAnimType(int16 newType) {
case 5: // Wrong answer anim
case 6: // Score lights
- if (g_topCatScore != 6)
+ if (_topCatScore != 6)
_xp->showCursor();
break;
@@ -938,7 +938,7 @@ void BoltEngine::setAnimType(int16 newType) {
break;
case 3: // Question audio
- highlightObject(g_topCatBackButton, 1);
+ highlightObject(_topCatBackButton, 1);
break;
case 4: // Correct answer anim
@@ -950,8 +950,8 @@ void BoltEngine::setAnimType(int16 newType) {
break;
}
- g_topCatCurrentAnimType = newType;
- g_topCatAnimStateMachineStep = 0;
+ _topCatCurrentAnimType = newType;
+ _topCatAnimStateMachineStep = 0;
}
void BoltEngine::highlightObject(byte *entry, int16 highlight) {
@@ -974,43 +974,43 @@ bool BoltEngine::setupNextQuestion() {
int16 attempts = 0;
while (attempts < 60) {
- g_topCatShuffledQuestionsArrayIdx++;
- if (g_topCatShuffledQuestionsArrayIdx >= 60)
- g_topCatShuffledQuestionsArrayIdx = 0;
+ _topCatShuffledQuestionsArrayIdx++;
+ if (_topCatShuffledQuestionsArrayIdx >= 60)
+ _topCatShuffledQuestionsArrayIdx = 0;
- g_topCatDrawnQuestionId = (int8)g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
+ _topCatDrawnQuestionId = (int8)_topCatShuffledQuestions[_topCatShuffledQuestionsArrayIdx];
- if ((g_topCatDrawnQuestionId & 0x80) == 0)
+ if ((_topCatDrawnQuestionId & 0x80) == 0)
break; // Found a question which hasn't been answered yet...
attempts++;
}
if (attempts >= 60) {
- g_topCatShuffledQuestionsArrayIdx = -1;
+ _topCatShuffledQuestionsArrayIdx = -1;
shuffleTopCatQuestions();
shuffleTopCatPermutations();
for (int16 i = 0; i < 60; i++) {
- g_topCatShuffledQuestions[i] &= 0x7F; // Remove "already answered" flag from all questions...
+ _topCatShuffledQuestions[i] &= 0x7F; // Remove "already answered" flag from all questions...
}
return setupNextQuestion();
}
- g_topCatShuffledAnswers[g_topCatDrawnQuestionId]++;
- if (g_topCatShuffledAnswers[g_topCatDrawnQuestionId] >= 3)
- g_topCatShuffledAnswers[g_topCatDrawnQuestionId] = 0;
+ _topCatShuffledAnswers[_topCatDrawnQuestionId]++;
+ if (_topCatShuffledAnswers[_topCatDrawnQuestionId] >= 3)
+ _topCatShuffledAnswers[_topCatDrawnQuestionId] = 0;
- int16 qIdx = g_topCatDrawnQuestionId;
- int16 permutation = (int8)g_topCatShuffledAnswers[g_topCatDrawnQuestionId];
- g_topCatCurrentAnswerIdx = (int8)g_topCatAnswersPermutations[qIdx * 3 + permutation];
+ int16 qIdx = _topCatDrawnQuestionId;
+ int16 permutation = (int8)_topCatShuffledAnswers[_topCatDrawnQuestionId];
+ _topCatCurrentAnswerIdx = (int8)_topCatAnswersPermutations[qIdx * 3 + permutation];
- int16 groupId = g_topCatDrawnQuestionId * 0x100 + 0x100;
- if (!getBOLTGroup(g_topCatBoltLib, groupId, 1))
+ int16 groupId = _topCatDrawnQuestionId * 0x100 + 0x100;
+ if (!getBOLTGroup(_topCatBoltLib, groupId, 1))
return false;
- byte *questionsAnswersGfxTable = memberAddr(g_topCatBoltLib, g_topCatDrawnQuestionId * 0x100 + 0x104);
+ byte *questionsAnswersGfxTable = memberAddr(_topCatBoltLib, _topCatDrawnQuestionId * 0x100 + 0x104);
// Display question pic...
byte *questionPic = getResolvedPtr(questionsAnswersGfxTable, 0);
@@ -1019,10 +1019,10 @@ bool BoltEngine::setupNextQuestion() {
// Display the three answer choices...
int16 tableOff = 0;
for (int16 i = 0; i < 3; i++) {
- int8 answerIdx = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3 + i];
+ int8 answerIdx = (int8)_topCatAnswers[_topCatCurrentAnswerIdx * 3 + i];
- int16 x = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4);
- int16 y = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4 + 2);
+ int16 x = READ_UINT16(_topCatAnswersScreenPositions + answerIdx * 4);
+ int16 y = READ_UINT16(_topCatAnswersScreenPositions + answerIdx * 4 + 2);
int16 colorShift = (answerIdx - i) * 3;
byte *answerPic = getResolvedPtr(questionsAnswersGfxTable, tableOff + 4);
@@ -1032,7 +1032,7 @@ bool BoltEngine::setupNextQuestion() {
tableOff += 4;
}
- freeBOLTGroup(g_topCatBoltLib, groupId, 1);
+ freeBOLTGroup(_topCatBoltLib, groupId, 1);
return true;
}
@@ -1064,16 +1064,16 @@ void BoltEngine::shuffleTopCatQuestions() {
for (int16 i = 0; i < 60; i++) {
int16 randIdx = _xp->getRandom(60);
- byte tmp = g_topCatShuffledQuestions[i];
- g_topCatShuffledQuestions[i] = g_topCatShuffledQuestions[randIdx];
- g_topCatShuffledQuestions[randIdx] = tmp;
+ byte tmp = _topCatShuffledQuestions[i];
+ _topCatShuffledQuestions[i] = _topCatShuffledQuestions[randIdx];
+ _topCatShuffledQuestions[randIdx] = tmp;
for (int16 j = 0; j < 3; j++) {
int16 randJ = _xp->getRandom(3);
- byte aTmp = g_topCatAnswersPermutations[answerOff + j];
- g_topCatAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + randJ];
- g_topCatAnswersPermutations[answerOff + randJ] = aTmp;
+ byte aTmp = _topCatAnswersPermutations[answerOff + j];
+ _topCatAnswersPermutations[answerOff + j] = _topCatAnswersPermutations[answerOff + randJ];
+ _topCatAnswersPermutations[answerOff + randJ] = aTmp;
}
answerOff += 3;
@@ -1085,18 +1085,18 @@ void BoltEngine::shuffleTopCatPermutations() {
for (int16 i = 0; i < 3; i++) {
int16 randIdx = _xp->getRandom(3);
- int16 tmp = g_topCatPermTableA[i];
- g_topCatPermTableA[i] = g_topCatPermTableA[randIdx];
- g_topCatPermTableA[randIdx] = tmp;
+ int16 tmp = _topCatPermTableA[i];
+ _topCatPermTableA[i] = _topCatPermTableA[randIdx];
+ _topCatPermTableA[randIdx] = tmp;
}
// Shuffle second permutation table...
for (int16 i = 0; i < 3; i++) {
int16 randIdx = _xp->getRandom(3);
- int16 tmp = g_topCatPermTableB[i];
- g_topCatPermTableB[i] = g_topCatPermTableB[randIdx];
- g_topCatPermTableB[randIdx] = tmp;
+ int16 tmp = _topCatPermTableB[i];
+ _topCatPermTableB[i] = _topCatPermTableB[randIdx];
+ _topCatPermTableB[randIdx] = tmp;
}
}
@@ -1110,7 +1110,7 @@ void BoltEngine::setScoreLights(int16 litMask) {
int16 tableOff = 0;
while (tableOff < 0x18) {
- byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, tableOff);
+ byte *lightEntry = getResolvedPtr(_topCatLightsPalette, tableOff);
int16 palStart = READ_UINT16(lightEntry) + 0x80;
int16 colorCount = READ_UINT16(lightEntry + 4);
@@ -1133,8 +1133,8 @@ void BoltEngine::setScoreLights(int16 litMask) {
}
void BoltEngine::swapTopCatHelpEntry() {
- byte *data = g_boltCurrentMemberEntry->dataPtr;
- uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+ byte *data = _boltCurrentMemberEntry->dataPtr;
+ uint32 decompSize = _boltCurrentMemberEntry->decompSize;
int16 offset = 0;
while (offset < (int32)decompSize) {
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
index 26247be03a0..9bea7ab3faf 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/booths/yogi.cpp
@@ -26,72 +26,72 @@
namespace Bolt {
void BoltEngine::playSoundMapYogi(int16 memberId) {
- byte *soundData = getBOLTMember(g_yogiBoltLib, memberId);
- int32 soundSize = memberSize(g_yogiBoltLib, memberId);
+ byte *soundData = getBOLTMember(_yogiBoltLib, memberId);
+ int32 soundSize = memberSize(_yogiBoltLib, memberId);
if (soundData) {
_xp->playSound(soundData, soundSize, 22050);
- g_yogiSoundActive = 1;
- g_yogiSoundPlaying++;
+ _yogiSoundActive = 1;
+ _yogiSoundPlaying++;
}
}
void BoltEngine::waitSoundMapYogi() {
- if (!g_yogiSoundPlaying)
+ if (!_yogiSoundPlaying)
return;
uint32 dummy;
while (_xp->getEvent(etSound, &dummy) == etEmpty);
- g_yogiSoundActive = 0;
- g_yogiSoundPlaying--;
+ _yogiSoundActive = 0;
+ _yogiSoundPlaying--;
}
void BoltEngine::stopSoundYogi() {
- if (g_yogiSoundActive) {
+ if (_yogiSoundActive) {
_xp->stopSound();
- g_yogiSoundActive = 0;
+ _yogiSoundActive = 0;
}
}
void BoltEngine::setYogiColors(int16 which) {
if (which == 0)
- _xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalHighlight0);
+ _xp->setPalette(_yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalRange[0], _yogiPalHighlight0);
else if (which == 1)
- _xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalHighlight1);
+ _xp->setPalette(_yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalRange[4], _yogiPalHighlight1);
}
void BoltEngine::restoreYogiColors(int16 which) {
if (which == 0)
- _xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalSave0);
+ _xp->setPalette(_yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalRange[0], _yogiPalSave0);
else if (which == 1)
- _xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalSave1);
+ _xp->setPalette(_yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalRange[4], _yogiPalSave1);
}
void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
int16 sprStride = READ_UINT16(basketSprite + 0x0A);
int16 sprHeight = READ_UINT16(basketSprite + 0x0C);
- memset(g_yogiScratchBuf.pixelData, 0, (int32)sprStride * sprHeight);
+ memset(_yogiScratchBuf.pixelData, 0, (int32)sprStride * sprHeight);
- int16 slotY = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x42);
- int16 slotX = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x12);
+ int16 slotY = READ_UINT16(_yogiBasketPic + slot * 2 + 0x42);
+ int16 slotX = READ_UINT16(_yogiBasketPic + slot * 2 + 0x12);
Common::Rect targetRect(slotY, slotX, slotY + sprHeight, slotX + sprStride);
- int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ int16 basketCount = READ_UINT16(_yogiBasketPic);
for (int16 i = 0; i < basketCount; i++) {
- if (g_yogiState.basketState[i] != 0)
+ if (_yogiState.basketState[i] != 0)
continue;
byte *otherSprite;
- if (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
- otherSprite = g_yogiHlSprite;
+ if (i == _yogiState.selected1Slot || i == _yogiState.selected2Slot)
+ otherSprite = _yogiHlSprite;
else
- otherSprite = g_yogiNormalSprite;
+ otherSprite = _yogiNormalSprite;
int16 otherStride = READ_UINT16(otherSprite + 0x0A);
int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
- int16 otherY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
- int16 otherX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+ int16 otherY = READ_UINT16(_yogiBasketPic + i * 2 + 0x42);
+ int16 otherX = READ_UINT16(_yogiBasketPic + i * 2 + 0x12);
Common::Rect otherRect(otherY, otherX, otherY + otherHeight, otherX + otherStride);
Common::Rect isect;
@@ -100,57 +100,57 @@ void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
byte *src = srcPixels + (isect.left - otherY) * otherStride + (isect.top - otherX);
- byte *dst = g_yogiScratchBuf.pixelData + (isect.left - slotY) * sprStride + (isect.top - slotX);
+ byte *dst = _yogiScratchBuf.pixelData + (isect.left - slotY) * sprStride + (isect.top - slotX);
int16 blitWidth = isect.bottom - isect.top;
int16 blitHeight = isect.right - isect.left;
_xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
}
- g_yogiScratchBuf.width = sprStride;
- g_yogiScratchBuf.height = sprHeight;
- g_yogiScratchBuf.palette = nullptr;
- g_yogiScratchBuf.flags = 0;
+ _yogiScratchBuf.width = sprStride;
+ _yogiScratchBuf.height = sprHeight;
+ _yogiScratchBuf.palette = nullptr;
+ _yogiScratchBuf.flags = 0;
- _xp->displayPic(&g_yogiScratchBuf, slotX, slotY, stFront);
+ _xp->displayPic(&_yogiScratchBuf, slotX, slotY, stFront);
}
void BoltEngine::drawAllBaskets() {
_xp->fillDisplay(0, stFront);
- int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ int16 basketCount = READ_UINT16(_yogiBasketPic);
for (int16 i = 0; i < basketCount; i++) {
- if (g_yogiState.basketState[i] != 0)
+ if (_yogiState.basketState[i] != 0)
continue;
- byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
- ? g_yogiHlSprite
- : g_yogiNormalSprite;
+ byte *sprite = (i == _yogiState.selected1Slot || i == _yogiState.selected2Slot)
+ ? _yogiHlSprite
+ : _yogiNormalSprite;
- int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
- int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+ int16 slotY = READ_UINT16(_yogiBasketPic + i * 2 + 0x42);
+ int16 slotX = READ_UINT16(_yogiBasketPic + i * 2 + 0x12);
displayPic(sprite, slotX, slotY, stFront);
}
}
void BoltEngine::handleYogiMatch() {
- playSoundMapYogi((int16)READ_UINT16(g_yogiBasketPic + 0x9E));
-
- if (g_yogiState.basketCount == 2) {
- g_yogiExitFlag = 1;
- g_yogiState.levelComplete = 1;
- g_yogiState.levelNumber++;
- if (g_yogiState.levelNumber > 10)
- g_yogiState.levelNumber = 10;
+ playSoundMapYogi((int16)READ_UINT16(_yogiBasketPic + 0x9E));
+
+ if (_yogiState.basketCount == 2) {
+ _yogiExitFlag = 1;
+ _yogiState.levelComplete = 1;
+ _yogiState.levelNumber++;
+ if (_yogiState.levelNumber > 10)
+ _yogiState.levelNumber = 10;
_xp->fillDisplay(0, stFront);
} else {
- drawBasket(g_yogiState.selected1Slot, g_yogiHlSprite);
- drawBasket(g_yogiState.selected2Slot, g_yogiHlSprite);
+ drawBasket(_yogiState.selected1Slot, _yogiHlSprite);
+ drawBasket(_yogiState.selected2Slot, _yogiHlSprite);
}
- g_yogiState.selected1Slot = -1;
- g_yogiState.selected2Slot = -1;
- g_yogiState.basketCount -= 2;
+ _yogiState.selected1Slot = -1;
+ _yogiState.selected2Slot = -1;
+ _yogiState.basketCount -= 2;
_xp->updateDisplay();
waitSoundMapYogi();
}
@@ -158,33 +158,33 @@ void BoltEngine::handleYogiMatch() {
bool BoltEngine::loadYogiBgPic() {
int16 groupId;
- if (g_displayMode == 0) {
+ if (_displayMode == 0) {
groupId = 0x100;
- } else if (g_displayMode == 1) {
+ } else if (_displayMode == 1) {
groupId = 0x200;
} else {
return false;
}
- if (!getBOLTGroup(g_yogiBoltLib, groupId, 1))
+ if (!getBOLTGroup(_yogiBoltLib, groupId, 1))
return false;
- g_yogiBgPic = memberAddr(g_yogiBoltLib, groupId + 1);
+ _yogiBgPic = memberAddr(_yogiBoltLib, groupId + 1);
return true;
}
void BoltEngine::unloadYogiBgPic() {
- freeBOLTGroup(g_yogiBoltLib, 0x100, 1);
+ freeBOLTGroup(_yogiBoltLib, 0x100, 1);
}
void BoltEngine::drawYogiLevel() {
- byte *palSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x72));
+ byte *palSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x72));
- g_yogiSoundActive = 0;
- g_yogiHotSpotCount = 0;
- g_yogiBlinkTimer1 = 0;
- g_yogiBlinkTimer2 = 0;
- g_yogiExitFlag = 0;
+ _yogiSoundActive = 0;
+ _yogiHotSpotCount = 0;
+ _yogiBlinkTimer1 = 0;
+ _yogiBlinkTimer2 = 0;
+ _yogiExitFlag = 0;
uint32 dummy;
while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -192,116 +192,116 @@ void BoltEngine::drawYogiLevel() {
_xp->stopCycle();
_xp->setTransparency(false);
displayColors(palSprite, stBack, 0);
- displayPic(g_yogiBgPic, g_displayX, g_displayY, stFront);
+ displayPic(_yogiBgPic, _displayX, _displayY, stFront);
_xp->updateDisplay();
_xp->setTransparency(true);
displayColors(palSprite, stFront, 0);
displayColors(palSprite, stBack, 1);
- displayPic(g_yogiBgPic, g_displayX, g_displayY, stBack);
+ displayPic(_yogiBgPic, _displayX, _displayY, stBack);
drawAllBaskets();
_xp->updateDisplay();
- g_yogiCursorX = 0xC0;
- g_yogiCursorY = 0x78;
+ _yogiCursorX = 0xC0;
+ _yogiCursorY = 0x78;
_xp->setCursorPos(0xC0, 0x78);
_xp->setCursorColor(0, 0, 0xFF);
_xp->showCursor();
- g_yogiPalRange[0] = READ_UINT16(g_yogiBasketPic + 0x7C) + 0x80;
- g_yogiPalRange[1] = READ_UINT16(g_yogiBasketPic + 0x7E) + 0x80;
- g_yogiPalRange[2] = READ_UINT16(g_yogiBasketPic + 0x80) + 0x80;
- g_yogiPalRange[3] = READ_UINT16(g_yogiBasketPic + 0x82) + 0x80;
- g_yogiPalRange[4] = READ_UINT16(g_yogiBasketPic + 0x8E) + 0x80;
- g_yogiPalRange[5] = READ_UINT16(g_yogiBasketPic + 0x90) + 0x80;
- g_yogiPalRange[6] = READ_UINT16(g_yogiBasketPic + 0x92) + 0x80;
- g_yogiPalRange[7] = READ_UINT16(g_yogiBasketPic + 0x94) + 0x80;
-
- _xp->getPalette(g_yogiPalRange[0], g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalSave0);
- _xp->getPalette(g_yogiPalRange[2], g_yogiPalRange[3] - g_yogiPalRange[2] + 1, g_yogiPalHighlight0);
- _xp->getPalette(g_yogiPalRange[4], g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalSave1);
- _xp->getPalette(g_yogiPalRange[6], g_yogiPalRange[7] - g_yogiPalRange[6] + 1, g_yogiPalHighlight1);
+ _yogiPalRange[0] = READ_UINT16(_yogiBasketPic + 0x7C) + 0x80;
+ _yogiPalRange[1] = READ_UINT16(_yogiBasketPic + 0x7E) + 0x80;
+ _yogiPalRange[2] = READ_UINT16(_yogiBasketPic + 0x80) + 0x80;
+ _yogiPalRange[3] = READ_UINT16(_yogiBasketPic + 0x82) + 0x80;
+ _yogiPalRange[4] = READ_UINT16(_yogiBasketPic + 0x8E) + 0x80;
+ _yogiPalRange[5] = READ_UINT16(_yogiBasketPic + 0x90) + 0x80;
+ _yogiPalRange[6] = READ_UINT16(_yogiBasketPic + 0x92) + 0x80;
+ _yogiPalRange[7] = READ_UINT16(_yogiBasketPic + 0x94) + 0x80;
+
+ _xp->getPalette(_yogiPalRange[0], _yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalSave0);
+ _xp->getPalette(_yogiPalRange[2], _yogiPalRange[3] - _yogiPalRange[2] + 1, _yogiPalHighlight0);
+ _xp->getPalette(_yogiPalRange[4], _yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalSave1);
+ _xp->getPalette(_yogiPalRange[6], _yogiPalRange[7] - _yogiPalRange[6] + 1, _yogiPalHighlight1);
}
bool BoltEngine::loadYogiLevel() {
int32 maxSize = 0;
- g_yogiLevelGroupId = (g_yogiState.levelNumber - 1) * 0x100 + 0x300;
+ _yogiLevelGroupId = (_yogiState.levelNumber - 1) * 0x100 + 0x300;
- if (!getBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1))
+ if (!getBOLTGroup(_yogiBoltLib, _yogiLevelGroupId, 1))
return false;
- g_yogiBasketPic = memberAddr(g_yogiBoltLib, g_yogiLevelGroupId);
- g_yogiNormalSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x02));
- g_yogiHlSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x04));
- g_yogiAnimSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x06));
+ _yogiBasketPic = memberAddr(_yogiBoltLib, _yogiLevelGroupId);
+ _yogiNormalSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x02));
+ _yogiHlSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x04));
+ _yogiAnimSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x06));
int32 size;
- size = (int32)READ_UINT16(g_yogiNormalSprite + 0x0A) * READ_UINT16(g_yogiNormalSprite + 0x0C);
+ size = (int32)READ_UINT16(_yogiNormalSprite + 0x0A) * READ_UINT16(_yogiNormalSprite + 0x0C);
if (size > maxSize)
maxSize = size;
- size = (int32)READ_UINT16(g_yogiHlSprite + 0x0A) * READ_UINT16(g_yogiHlSprite + 0x0C);
+ size = (int32)READ_UINT16(_yogiHlSprite + 0x0A) * READ_UINT16(_yogiHlSprite + 0x0C);
if (size > maxSize)
maxSize = size;
- size = (int32)READ_UINT16(g_yogiAnimSprite + 0x0A) * READ_UINT16(g_yogiAnimSprite + 0x0C);
+ size = (int32)READ_UINT16(_yogiAnimSprite + 0x0A) * READ_UINT16(_yogiAnimSprite + 0x0C);
if (size > maxSize)
maxSize = size;
- g_yogiScratchBuf.pixelData = (byte *)_xp->allocMem(maxSize);
- if (!g_yogiScratchBuf.pixelData)
+ _yogiScratchBuf.pixelData = (byte *)_xp->allocMem(maxSize);
+ if (!_yogiScratchBuf.pixelData)
return false;
- g_yogiSpriteStride = READ_UINT16(g_yogiNormalSprite + 0x0A);
- g_yogiSpriteHeight = READ_UINT16(g_yogiNormalSprite + 0x0C);
+ _yogiSpriteStride = READ_UINT16(_yogiNormalSprite + 0x0A);
+ _yogiSpriteHeight = READ_UINT16(_yogiNormalSprite + 0x0C);
return true;
}
void BoltEngine::unloadYogiResources() {
_xp->hideCursor();
- int16 basketGroupId = (g_yogiLevelId << 8) + 0xD00;
- freeBOLTGroup(g_yogiBoltLib, basketGroupId, 1);
- freeBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1);
+ int16 basketGroupId = (_yogiLevelId << 8) + 0xD00;
+ freeBOLTGroup(_yogiBoltLib, basketGroupId, 1);
+ freeBOLTGroup(_yogiBoltLib, _yogiLevelGroupId, 1);
}
bool BoltEngine::initYogiLevel() {
- g_yogiState.currentSlot++;
- if (g_yogiState.currentSlot >= 10)
- g_yogiState.currentSlot = 0;
+ _yogiState.currentSlot++;
+ if (_yogiState.currentSlot >= 10)
+ _yogiState.currentSlot = 0;
- g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+ _yogiLevelId = _yogiState.slotIndex[_yogiState.currentSlot];
if (!loadYogiLevel())
return false;
- int16 basketCount = READ_UINT16(g_yogiBasketPic);
- g_yogiState.basketCount = basketCount;
- g_yogiState.levelComplete = 0;
- g_yogiState.matchCount = 0;
- g_yogiState.selectionPending = 0;
- g_yogiState.selected1Slot = -1;
- g_yogiState.selected2Slot = -1;
+ int16 basketCount = READ_UINT16(_yogiBasketPic);
+ _yogiState.basketCount = basketCount;
+ _yogiState.levelComplete = 0;
+ _yogiState.matchCount = 0;
+ _yogiState.selectionPending = 0;
+ _yogiState.selected1Slot = -1;
+ _yogiState.selected2Slot = -1;
for (int16 i = 0; i < 0x18; i++) {
- g_yogiState.basketSound[i] = 0x10;
- g_yogiState.basketState[i] = 0;
+ _yogiState.basketSound[i] = 0x10;
+ _yogiState.basketState[i] = 0;
}
- int16 idx = g_yogiState.levelIndex[g_yogiLevelId];
- int16 snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+ int16 idx = _yogiState.levelIndex[_yogiLevelId];
+ int16 snd = _yogiGlobal[_yogiLevelId * 0x10 + idx];
int16 flag = 0;
for (int16 var_4 = 0; var_4 < basketCount; var_4++) {
int16 rnd = _xp->getRandom(basketCount);
- while (g_yogiState.basketSound[rnd] != 0x10) {
+ while (_yogiState.basketSound[rnd] != 0x10) {
rnd++;
if (rnd >= basketCount)
rnd = 0;
}
- g_yogiState.basketSound[rnd] = snd;
+ _yogiState.basketSound[rnd] = snd;
if (flag == 0) {
flag = 1;
@@ -310,17 +310,17 @@ bool BoltEngine::initYogiLevel() {
idx++;
if (idx == 0x10)
idx = 0;
- snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+ snd = _yogiGlobal[_yogiLevelId * 0x10 + idx];
}
}
- g_yogiState.levelIndex[g_yogiLevelId] = idx;
+ _yogiState.levelIndex[_yogiLevelId] = idx;
drawYogiLevel();
return true;
}
bool BoltEngine::resumeYogiLevel() {
- g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+ _yogiLevelId = _yogiState.slotIndex[_yogiState.currentSlot];
if (!loadYogiLevel())
return false;
@@ -330,31 +330,31 @@ bool BoltEngine::resumeYogiLevel() {
}
bool BoltEngine::initYogi() {
- g_yogiSoundPlaying = 0;
+ _yogiSoundPlaying = 0;
if (!loadYogiBgPic())
return false;
- if (!vLoad(&g_yogiGlobal, "YogiGlobal")) {
+ if (!vLoad(&_yogiGlobal, "YogiGlobal")) {
int16 slotVal = 0;
int16 globIdx = 0;
int16 idx = 0;
while (slotVal < 10) {
- g_yogiState.slotIndex[idx] = slotVal;
- g_yogiState.levelIndex[idx] = 0;
+ _yogiState.slotIndex[idx] = slotVal;
+ _yogiState.levelIndex[idx] = 0;
for (int16 i = 0; i < 16; i++)
- g_yogiGlobal[globIdx + i] = 0x10;
+ _yogiGlobal[globIdx + i] = 0x10;
for (int16 di = 0; di < 16; di++) {
int16 rnd = _xp->getRandom(16);
- while (g_yogiGlobal[globIdx + rnd] != 0x10) {
+ while (_yogiGlobal[globIdx + rnd] != 0x10) {
rnd++;
if (rnd >= 16)
rnd = 0;
}
- g_yogiGlobal[globIdx + rnd] = di;
+ _yogiGlobal[globIdx + rnd] = di;
}
globIdx += 0x10;
@@ -366,43 +366,43 @@ bool BoltEngine::initYogi() {
byte yogiStateBuf[0x9C] = { 0 };
if (!vLoad(&yogiStateBuf, "Yogi")) {
- g_yogiState.levelNumber = 1;
- g_yogiState.currentSlot = -1;
+ _yogiState.levelNumber = 1;
+ _yogiState.currentSlot = -1;
return initYogiLevel();
}
Common::SeekableReadStream *yogiStateReadStream = new Common::MemoryReadStream(yogiStateBuf, sizeof(yogiStateBuf), DisposeAfterUse::NO);
- g_yogiState.levelNumber = yogiStateReadStream->readSint16BE();
- g_yogiState.currentSlot = yogiStateReadStream->readSint16BE();
+ _yogiState.levelNumber = yogiStateReadStream->readSint16BE();
+ _yogiState.currentSlot = yogiStateReadStream->readSint16BE();
for (int i = 0; i < 10; i++)
- g_yogiState.levelIndex[i] = yogiStateReadStream->readSint16BE();
+ _yogiState.levelIndex[i] = yogiStateReadStream->readSint16BE();
for (int i = 0; i < 10; i++)
- g_yogiState.slotIndex[i] = yogiStateReadStream->readSint16BE();
+ _yogiState.slotIndex[i] = yogiStateReadStream->readSint16BE();
- g_yogiState.levelComplete = yogiStateReadStream->readSint16BE();
+ _yogiState.levelComplete = yogiStateReadStream->readSint16BE();
for (int i = 0; i < 24; i++)
- g_yogiState.basketSound[i] = yogiStateReadStream->readSint16BE();
+ _yogiState.basketSound[i] = yogiStateReadStream->readSint16BE();
for (int i = 0; i < 24; i++)
- g_yogiState.basketState[i] = yogiStateReadStream->readSint16BE();
+ _yogiState.basketState[i] = yogiStateReadStream->readSint16BE();
- g_yogiState.basketCount = yogiStateReadStream->readSint16BE();
- g_yogiState.matchCount = yogiStateReadStream->readSint16BE();
- g_yogiState.selectionPending = yogiStateReadStream->readSint16BE();
- g_yogiState.selected1Slot = yogiStateReadStream->readSint16BE();
- g_yogiState.selected2Slot = yogiStateReadStream->readSint16BE();
- g_yogiState.sound1 = yogiStateReadStream->readSint16BE();
- g_yogiState.sound2 = yogiStateReadStream->readSint16BE();
+ _yogiState.basketCount = yogiStateReadStream->readSint16BE();
+ _yogiState.matchCount = yogiStateReadStream->readSint16BE();
+ _yogiState.selectionPending = yogiStateReadStream->readSint16BE();
+ _yogiState.selected1Slot = yogiStateReadStream->readSint16BE();
+ _yogiState.selected2Slot = yogiStateReadStream->readSint16BE();
+ _yogiState.sound1 = yogiStateReadStream->readSint16BE();
+ _yogiState.sound2 = yogiStateReadStream->readSint16BE();
// Sanity check: should be exactly 0x9C
assert(yogiStateReadStream->pos() == 0x9C);
delete yogiStateReadStream;
- if (g_yogiState.levelComplete)
+ if (_yogiState.levelComplete)
return initYogiLevel();
else
return resumeYogiLevel();
@@ -418,48 +418,48 @@ void BoltEngine::yogiToggleBlinking(int16 which, int16 *state) {
void BoltEngine::yogiUpdateHotSpots(int16 x, int16 y) {
Common::Rect helpRect(
- READ_UINT16(g_yogiBasketPic + 0x74), READ_UINT16(g_yogiBasketPic + 0x78),
- READ_UINT16(g_yogiBasketPic + 0x76), READ_UINT16(g_yogiBasketPic + 0x7A));
+ READ_UINT16(_yogiBasketPic + 0x74), READ_UINT16(_yogiBasketPic + 0x78),
+ READ_UINT16(_yogiBasketPic + 0x76), READ_UINT16(_yogiBasketPic + 0x7A));
Common::Rect exitRect(
- READ_UINT16(g_yogiBasketPic + 0x86), READ_UINT16(g_yogiBasketPic + 0x8A),
- READ_UINT16(g_yogiBasketPic + 0x88), READ_UINT16(g_yogiBasketPic + 0x8C));
+ READ_UINT16(_yogiBasketPic + 0x86), READ_UINT16(_yogiBasketPic + 0x8A),
+ READ_UINT16(_yogiBasketPic + 0x88), READ_UINT16(_yogiBasketPic + 0x8C));
if (helpRect.contains(x, y)) {
- if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+ if (!_yogiBlinkTimer1 && !_yogiHotSpotCount)
setYogiColors(0);
} else {
- if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+ if (!_yogiBlinkTimer1 && !_yogiHotSpotCount)
restoreYogiColors(0);
}
if (exitRect.contains(x, y)) {
- if (!g_yogiBlinkTimer2)
+ if (!_yogiBlinkTimer2)
setYogiColors(1);
return;
} else {
- if (!g_yogiBlinkTimer2)
+ if (!_yogiBlinkTimer2)
restoreYogiColors(1);
}
}
int16 BoltEngine::findBasket(int16 x, int16 y) {
- int16 basketCount = READ_UINT16(g_yogiBasketPic);
+ int16 basketCount = READ_UINT16(_yogiBasketPic);
for (int16 i = basketCount - 1; i >= 0; i--) {
- if (g_yogiState.basketState[i] != 0)
+ if (_yogiState.basketState[i] != 0)
continue;
- int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
- int16 slotX2 = slotX + g_yogiSpriteStride - 1;
- int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
- int16 slotY2 = slotY + g_yogiSpriteHeight - 1;
+ int16 slotX = READ_UINT16(_yogiBasketPic + i * 2 + 0x12);
+ int16 slotX2 = slotX + _yogiSpriteStride - 1;
+ int16 slotY = READ_UINT16(_yogiBasketPic + i * 2 + 0x42);
+ int16 slotY2 = slotY + _yogiSpriteHeight - 1;
if (x < slotX || x > slotX2 || y < slotY || y > slotY2)
continue;
- byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
- ? g_yogiHlSprite
- : g_yogiNormalSprite;
+ byte *sprite = (i == _yogiState.selected1Slot || i == _yogiState.selected2Slot)
+ ? _yogiHlSprite
+ : _yogiNormalSprite;
if (!getPixel(sprite, x - slotX, y - slotY))
continue;
@@ -471,27 +471,27 @@ int16 BoltEngine::findBasket(int16 x, int16 y) {
}
void BoltEngine::resolveYogiSelection() {
- if (g_yogiState.sound1 == g_yogiState.sound2) {
- g_yogiState.basketState[g_yogiState.selected1Slot] = 1;
- g_yogiState.basketState[g_yogiState.selected2Slot] = 1;
+ if (_yogiState.sound1 == _yogiState.sound2) {
+ _yogiState.basketState[_yogiState.selected1Slot] = 1;
+ _yogiState.basketState[_yogiState.selected2Slot] = 1;
handleYogiMatch();
- g_yogiState.matchCount = 0;
- g_yogiState.selectionPending = 0;
+ _yogiState.matchCount = 0;
+ _yogiState.selectionPending = 0;
} else {
- int16 slot1 = g_yogiState.selected1Slot;
- g_yogiState.matchCount = 0;
- g_yogiState.selected1Slot = -1;
- drawBasket(slot1, g_yogiNormalSprite);
+ int16 slot1 = _yogiState.selected1Slot;
+ _yogiState.matchCount = 0;
Commit: a4e8528b28b07a0a8a179823a171848ae9d2510b
https://github.com/scummvm/scummvm/commit/a4e8528b28b07a0a8a179823a171848ae9d2510b
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Fix module.mk
Changed paths:
engines/bolt/module.mk
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index d11f1f1dafd..fe2ff69f77b 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -6,12 +6,12 @@ MODULE_OBJS = \
barker.o \
bolt.o \
booth.o \
- booths\fred.o \
- booths\george.o \
- booths\huck.o \
- booths\scooby.o \
- booths\topcat.o \
- booths\yogi.o \
+ booths/fred.o \
+ booths/george.o \
+ booths/huck.o \
+ booths/scooby.o \
+ booths/topcat.o \
+ booths/yogi.o \
metaengine.o \
resource.o \
rtf.o \
@@ -19,16 +19,16 @@ MODULE_OBJS = \
state.o \
swap.o \
utils.o \
- xplib\blit.o \
- xplib\cursor.o \
- xplib\display.o \
- xplib\events.o \
- xplib\file.o \
- xplib\palette.o \
- xplib\random.o \
- xplib\sound.o \
- xplib\timer.o \
- xplib\xplib.o
+ xplib/blit.o \
+ xplib/cursor.o \
+ xplib/display.o \
+ xplib/events.o \
+ xplib/file.o \
+ xplib/palette.o \
+ xplib/random.o \
+ xplib/sound.o \
+ xplib/timer.o \
+ xplib/xplib.o
# This module can be built as a plugin
ifeq ($(ENABLE_BOLT), DYNAMIC_PLUGIN)
Commit: 564a389c582e3cb632a7c209ea7c637f8a2080ed
https://github.com/scummvm/scummvm/commit/564a389c582e3cb632a7c209ea7c637f8a2080ed
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Fix linux build
Changed paths:
engines/bolt/booths/huck.cpp
engines/bolt/state.cpp
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index 8efc0886589..257f525b3fb 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -787,6 +787,8 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
break;
}
}
+
+ break;
}
case etSound: {
diff --git a/engines/bolt/state.cpp b/engines/bolt/state.cpp
index 4e6bfdc8331..b118a68e30d 100644
--- a/engines/bolt/state.cpp
+++ b/engines/bolt/state.cpp
@@ -49,7 +49,7 @@ bool BoltEngine::vLoad(void *dest, const char *name) {
char localName[82];
int16 recordOffset;
- strcpy_s(localName, name);
+ Common::strcpy_s(localName, name);
uint32 nameLen = strlen(name);
if (!(nameLen & 1)) {
localName[nameLen] = ' ';
@@ -74,7 +74,7 @@ bool BoltEngine::vLoad(void *dest, const char *name) {
bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
char localName[82];
- strcpy_s(localName, name);
+ Common::strcpy_s(localName, name);
uint32 nameLen = strlen(name);
if (!(nameLen & 1)) {
localName[nameLen] = ' ';
@@ -113,7 +113,7 @@ bool BoltEngine::vDelete(const char *name) {
char localName[82];
int16 recordOffset;
- strcpy_s(localName, name);
+ Common::strcpy_s(localName, name);
uint32 nameLen = strlen(name);
if (!(nameLen & 1)) {
localName[nameLen] = ' ';
Commit: 556f126ceb7bc5f39847291fc1db589bfa5c4b12
https://github.com/scummvm/scummvm/commit/556f126ceb7bc5f39847291fc1db589bfa5c4b12
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Fix warnings
Changed paths:
engines/bolt/xplib/events.cpp
engines/bolt/xplib/file.cpp
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 5637069b224..45b8060f6cc 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -323,6 +323,8 @@ void XpLib::handleKey(Common::KeyCode vkey, int16 down) {
case Common::KEYCODE_DOWN:
dirPtr = &_keyStateDown;
break; // VK_DOWN
+ default:
+ break;
}
if (dirPtr == nullptr)
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index 386c05cabb9..48dac0ec895 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -28,7 +28,7 @@ namespace Bolt {
void XpLib::fileError(const char *message) {
stopCycle();
- error(message);
+ error("%s", message);
}
Common::File *XpLib::openFile(const char *fileName, int16 flags) {
Commit: 7960ce109f5ef28747b520cccd2d4c6764b81e28
https://github.com/scummvm/scummvm/commit/7960ce109f5ef28747b520cccd2d4c6764b81e28
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Clean-up detection
Changed paths:
engines/bolt/detection.cpp
engines/bolt/detection.h
engines/bolt/detection_tables.h
diff --git a/engines/bolt/detection.cpp b/engines/bolt/detection.cpp
index 4defbfd281f..287a42849d4 100644
--- a/engines/bolt/detection.cpp
+++ b/engines/bolt/detection.cpp
@@ -29,17 +29,14 @@
#include "bolt/detection.h"
#include "bolt/detection_tables.h"
-const DebugChannelDef BoltMetaEngineDetection::debugFlagList[] = {
- { Bolt::kDebugGraphics, "Graphics", "Graphics debug level" },
- { Bolt::kDebugPath, "Path", "Pathfinding debug level" },
- { Bolt::kDebugFilePath, "FilePath", "File path debug level" },
- { Bolt::kDebugScan, "Scan", "Scan for unrecognised games" },
- { Bolt::kDebugScript, "Script", "Enable debug script dump" },
- DEBUG_CHANNEL_END
+static const char *const directoryGlobs[] = {
+ "ASSETS",
+ nullptr
};
-BoltMetaEngineDetection::BoltMetaEngineDetection() : AdvancedMetaEngineDetection(
- Bolt::gameDescriptions, Bolt::boltGames) {
+BoltMetaEngineDetection::BoltMetaEngineDetection() : AdvancedMetaEngineDetection(Bolt::gameDescriptions, Bolt::boltGames) {
+ _maxScanDepth = 2;
+ _directoryGlobs = directoryGlobs;
}
REGISTER_PLUGIN_STATIC(BOLT_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, BoltMetaEngineDetection);
diff --git a/engines/bolt/detection.h b/engines/bolt/detection.h
index e36027700d0..8ce6d37c0bd 100644
--- a/engines/bolt/detection.h
+++ b/engines/bolt/detection.h
@@ -26,14 +26,6 @@
namespace Bolt {
-enum BoltDebugChannels {
- kDebugGraphics = 1,
- kDebugPath,
- kDebugScan,
- kDebugFilePath,
- kDebugScript,
-};
-
extern const PlainGameDescriptor boltGames[];
extern const ADGameDescription gameDescriptions[];
@@ -43,8 +35,6 @@ extern const ADGameDescription gameDescriptions[];
} // End of namespace Bolt
class BoltMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
- static const DebugChannelDef debugFlagList[];
-
public:
BoltMetaEngineDetection();
~BoltMetaEngineDetection() override {}
@@ -54,15 +44,11 @@ public:
}
const char *getEngineName() const override {
- return "Bolt";
+ return "Cartoon Carnival";
}
const char *getOriginalCopyright() const override {
- return "Bolt (C)";
- }
-
- const DebugChannelDef *getDebugChannels() const override {
- return debugFlagList;
+ return "Hanna-Barbera's Cartoon Carnival (C) 1993-1995 Funhouse Design";
}
};
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index fcf6df448ff..9b7980161f1 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -45,24 +45,6 @@ const ADGameDescription gameDescriptions[] = {
ADGF_TESTING,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
- {
- "carnival",
- nullptr,
- {
- {"ASSETS/BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
- {"ASSETS/FRED.BLT", 0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
- {"ASSETS/GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
- {"ASSETS/HUCK.BLT", 0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
- {"ASSETS/SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
- {"ASSETS/TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
- {"ASSETS/YOGI.BLT", 0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
- AD_LISTEND
- },
- Common::EN_USA,
- Common::kPlatformWindows,
- ADGF_TESTING,
- GUIO1(GAMEOPTION_EXTEND_SCREEN)
- },
{
"carnival",
nullptr,
@@ -81,24 +63,6 @@ const ADGameDescription gameDescriptions[] = {
ADGF_TESTING,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
- {
- "carnival",
- nullptr,
- {
- {"ASSETS/BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
- {"ASSETS/FRED.BLT", 0, "2491420b98c2421e19a751422c223cc8", 302133 },
- {"ASSETS/GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
- {"ASSETS/HUCK.BLT", 0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
- {"ASSETS/SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
- {"ASSETS/TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
- {"ASSETS/YOGI.BLT", 0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
- AD_LISTEND
- },
- Common::IT_ITA,
- Common::kPlatformWindows,
- ADGF_TESTING,
- GUIO1(GAMEOPTION_EXTEND_SCREEN)
- },
{
"carnival",
"Demo",
@@ -114,21 +78,6 @@ const ADGameDescription gameDescriptions[] = {
ADGF_TESTING | ADGF_DEMO,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
- {
- "carnival",
- "Demo",
- {
- {"ASSETS/BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
- {"ASSETS/GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
- {"ASSETS/SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
- {"ASSETS/YOGI.BLT", 0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
- AD_LISTEND
- },
- Common::IT_ITA,
- Common::kPlatformWindows,
- ADGF_TESTING | ADGF_DEMO,
- GUIO1(GAMEOPTION_EXTEND_SCREEN)
- },
AD_TABLE_END_MARKER
Commit: b31b7569bb8f58e9680a3ecd443d69494325284d
https://github.com/scummvm/scummvm/commit/b31b7569bb8f58e9680a3ecd443d69494325284d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Fix assets not being found in release build
Changed paths:
engines/bolt/bolt.cpp
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 76078ea854d..54db77bf4d6 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -56,6 +56,9 @@ Common::String BoltEngine::getGameId() const {
}
Common::Error BoltEngine::run() {
+ const Common::FSNode gameDataDir(ConfMan.getPath("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "assets");
+
ConfMan.registerDefault("extended_viewport", false);
if (ConfMan.hasKey("extended_viewport", _targetName)) {
_extendedViewport = ConfMan.getBool("extended_viewport");
Commit: 87e6e8d8c16113763d32151212ef853ac2d07a2c
https://github.com/scummvm/scummvm/commit/87e6e8d8c16113763d32151212ef853ac2d07a2c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Some fixes and clean-ups
Changed paths:
engines/bolt/booth.cpp
engines/bolt/resource.cpp
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index 92ce101e054..beef808cc12 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -740,25 +740,25 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
int16 animId;
switch (_currentBoothScene) {
case 3:
- animId = _isDemo ? 0x0F : 0x18;
+ animId = _isDemo ? 0x0F : 0x12;
break;
case 4:
- animId = _isDemo ? 0x10 : 0x12;
+ animId = _isDemo ? 0x10 : 0x13;
break;
case 5:
- animId = _isDemo ? 0x11 : 0x13;
+ animId = _isDemo ? 0x11 : 0x14;
break;
case 6:
- animId = _isDemo ? 0x12 : 0x14;
+ animId = _isDemo ? 0x12 : 0x15;
break;
case 7:
- animId = _isDemo ? 0x13 : 0x15;
+ animId = _isDemo ? 0x13 : 0x16;
break;
case 8:
- animId = _isDemo ? 0x14 : 0x16;
+ animId = _isDemo ? 0x14 : 0x17;
break;
case 9:
- animId = _isDemo ? 0x15 : 0x17;
+ animId = _isDemo ? 0x15 : 0x18;
break;
default:
animId = -1;
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 23191227084..68ea9122273 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -147,8 +147,6 @@ void BoltEngine::resolveFunction(uint32 *ref) {
return;
}
- // TODO!
- warning("BoltEngine::resolveFunction(): TODO!");
uint16 index = (uint16)*ref;
*ref = index;
}
@@ -272,7 +270,6 @@ bool BoltEngine::closeBOLTLib(BOLTLib **libPtr) {
int16 groupCount = (*libPtr)->groupCount;
if (groupCount == 0) {
groupCount = 256;
- warning("DEBUG, closeBOLTLib(): CHECK FOR OVERFLOW ");
}
// Free all groups in reverse order...
@@ -463,7 +460,6 @@ byte *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
compressedSize = nextEntry->fileOffset;
} else {
// Last entry: total = memberDirSize + memberDirOffset + memberDataOffset
- warning("is this even working?");
compressedSize = (uint32)(memberCount * 16) + _boltCurrentGroupEntry->memberDataOffset + _boltCurrentGroupEntry->memberDirOffset;
}
Commit: 9fb3cfc7555ededb598041fcd3138643a2578c35
https://github.com/scummvm/scummvm/commit/9fb3cfc7555ededb598041fcd3138643a2578c35
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: CARNIVAL: Fix more bugs
Changed paths:
engines/bolt/booths/huck.cpp
engines/bolt/booths/scooby.cpp
engines/bolt/resource.cpp
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index 257f525b3fb..2da1115040c 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -638,7 +638,7 @@ void BoltEngine::giftSwap() {
int16 slotA = -1;
int16 slotB = 0;
int16 counter = -1;
- for (int16 i = 0, j = 0; j <= READ_UINT16(_huckGiftPic); i++, j++) {
+ for (int16 i = 0, j = 0; j < READ_UINT16(_huckGiftPic); i++, j++) {
if (_huckState.drawTable2[j] != 0)
continue;
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index a6512a7dffc..72a9177d43d 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -254,7 +254,7 @@ void BoltEngine::clearPictMSb(byte *pic) {
byte *pixelData = getResolvedPtr(pic, 0x12);
uint32 size = (uint32)READ_UINT16(pic + 0x0A) * (uint32)READ_UINT16(pic + 0x0C);
- for (uint32 i = 0; i <= size; i++) {
+ for (uint32 i = 0; i < size; i++) {
pixelData[i] &= 0x7F;
}
}
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 68ea9122273..eb15aaaf516 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -251,7 +251,7 @@ bool BoltEngine::openBOLTLib(BOLTLib **libPtr, BOLTCallbacks *callbacks, const c
_xp->closeFile(fileHandle);
if (_boltCurrentLib != nullptr) {
- _xp->freeMem(_boltCurrentLib);
+ delete _boltCurrentLib;
_boltCurrentLib = nullptr;
}
@@ -280,7 +280,7 @@ bool BoltEngine::closeBOLTLib(BOLTLib **libPtr) {
}
_xp->closeFile((*libPtr)->fileHandle);
- _xp->freeMem(*libPtr);
+ delete *libPtr;
*libPtr = nullptr;
return true;
Commit: 5b289038dc5101f7defe944e0f4d0362df83b9d1
https://github.com/scummvm/scummvm/commit/5b289038dc5101f7defe944e0f4d0362df83b9d1
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Reduce include list in detection.cpp
Changed paths:
engines/bolt/detection.cpp
diff --git a/engines/bolt/detection.cpp b/engines/bolt/detection.cpp
index 287a42849d4..645887b698c 100644
--- a/engines/bolt/detection.cpp
+++ b/engines/bolt/detection.cpp
@@ -20,12 +20,6 @@
*/
#include "base/plugins.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/md5.h"
-#include "common/str-array.h"
-#include "common/translation.h"
-#include "common/util.h"
#include "bolt/detection.h"
#include "bolt/detection_tables.h"
Commit: f21d8e6800e98ec18438739987c8075ca7b3b04d
https://github.com/scummvm/scummvm/commit/f21d8e6800e98ec18438739987c8075ca7b3b04d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Add myself to engine credits
Changed paths:
engines/bolt/credits.pl
diff --git a/engines/bolt/credits.pl b/engines/bolt/credits.pl
index 3e137cd1a0f..e4a88db0393 100644
--- a/engines/bolt/credits.pl
+++ b/engines/bolt/credits.pl
@@ -1,3 +1,3 @@
begin_section("Bolt");
- add_person("Name 1", "Handle 1", "");
+ add_person("Andrea Boscarino", "Bosca", "");
end_section();
Commit: eb07f69e08c2576c36fa4c5311f5b9ec99e2d9de
https://github.com/scummvm/scummvm/commit/eb07f69e08c2576c36fa4c5311f5b9ec99e2d9de
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Reduce number of files for detection
Also mark the game as ADGF_UNSTABLE
Changed paths:
engines/bolt/detection_tables.h
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index 9b7980161f1..dbc3954fc2c 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -32,17 +32,12 @@ const ADGameDescription gameDescriptions[] = {
nullptr,
{
{"BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
- {"FRED.BLT", 0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
- {"GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
- {"HUCK.BLT", 0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
{"SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
- {"TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
- {"YOGI.BLT", 0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
AD_LISTEND
},
Common::EN_USA,
Common::kPlatformWindows,
- ADGF_TESTING,
+ ADGF_UNSTABLE,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
{
@@ -50,17 +45,12 @@ const ADGameDescription gameDescriptions[] = {
nullptr,
{
{"BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
- {"FRED.BLT", 0, "2491420b98c2421e19a751422c223cc8", 302133 },
- {"GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
- {"HUCK.BLT", 0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
{"SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
- {"TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
- {"YOGI.BLT", 0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformWindows,
- ADGF_TESTING,
+ ADGF_UNSTABLE,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
{
@@ -68,14 +58,12 @@ const ADGameDescription gameDescriptions[] = {
"Demo",
{
{"BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
- {"GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
{"SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
- {"YOGI.BLT", 0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformWindows,
- ADGF_TESTING | ADGF_DEMO,
+ ADGF_UNSTABLE | ADGF_DEMO,
GUIO1(GAMEOPTION_EXTEND_SCREEN)
},
Commit: 8e4480acb29a6b615fe054e2c474ad1b9fb5221c
https://github.com/scummvm/scummvm/commit/8e4480acb29a6b615fe054e2c474ad1b9fb5221c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Don't build the engine by default
Changed paths:
engines/bolt/configure.engine
diff --git a/engines/bolt/configure.engine b/engines/bolt/configure.engine
index 8956855cc72..6629b5b25b9 100644
--- a/engines/bolt/configure.engine
+++ b/engines/bolt/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine bolt "Bolt" yes "" "" "" ""
+add_engine bolt "Bolt" no "" "" "" ""
Commit: 14ceee75112491512c383547e23176bbf1b979b0
https://github.com/scummvm/scummvm/commit/14ceee75112491512c383547e23176bbf1b979b0
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Tidy up includes order
Changed paths:
engines/bolt/bolt.cpp
engines/bolt/booths/huck.cpp
engines/bolt/booths/scooby.cpp
engines/bolt/booths/yogi.cpp
engines/bolt/metaengine.cpp
engines/bolt/resource.cpp
engines/bolt/rtf.cpp
engines/bolt/utils.cpp
engines/bolt/xplib/cursor.cpp
engines/bolt/xplib/events.cpp
engines/bolt/xplib/file.cpp
engines/bolt/xplib/palette.cpp
engines/bolt/xplib/sound.cpp
engines/bolt/xplib/xplib.h
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 54db77bf4d6..70b53368b7f 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -19,10 +19,6 @@
*
*/
-#include "bolt/bolt.h"
-#include "bolt/detection.h"
-#include "bolt/xplib/xplib.h"
-
#include "common/scummsys.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
@@ -31,6 +27,10 @@
#include "engines/util.h"
#include "graphics/paletteman.h"
+#include "bolt/bolt.h"
+#include "bolt/detection.h"
+#include "bolt/xplib/xplib.h"
+
namespace Bolt {
BoltEngine *g_engine;
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index 2da1115040c..28c6ab4649a 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -19,10 +19,10 @@
*
*/
-#include "bolt/bolt.h"
-
#include "common/memstream.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
void BoltEngine::playSoundMapHuck(int16 memberId) {
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 72a9177d43d..4f401fe8cda 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -19,10 +19,10 @@
*
*/
-#include "bolt/bolt.h"
-
#include "common/memstream.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
bool BoltEngine::loadScoobyBaseAssets() {
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
index 9bea7ab3faf..52c5dbe5423 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/booths/yogi.cpp
@@ -19,10 +19,10 @@
*
*/
-#include "bolt/bolt.h"
-
#include "common/memstream.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
void BoltEngine::playSoundMapYogi(int16 memberId) {
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
index f43b7fe8e24..ef801c093fc 100644
--- a/engines/bolt/metaengine.cpp
+++ b/engines/bolt/metaengine.cpp
@@ -21,9 +21,9 @@
#include "common/translation.h"
+#include "bolt/bolt.h"
#include "bolt/metaengine.h"
#include "bolt/detection.h"
-#include "bolt/bolt.h"
namespace Bolt {
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index eb15aaaf516..7fe4d40bc70 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -19,10 +19,10 @@
*
*/
-#include "bolt/bolt.h"
-
#include "common/memstream.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
void BoltEngine::decompress(byte *dest, uint32 decompSize, byte *src) {
diff --git a/engines/bolt/rtf.cpp b/engines/bolt/rtf.cpp
index 9b002e4f21e..b953548ceef 100644
--- a/engines/bolt/rtf.cpp
+++ b/engines/bolt/rtf.cpp
@@ -19,10 +19,10 @@
*
*/
-#include "bolt/bolt.h"
-
#include "common/memstream.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
RTFResource *BoltEngine::openRTF(const char *fileName) {
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
index 293d7531e6b..26eea1a881f 100644
--- a/engines/bolt/utils.cpp
+++ b/engines/bolt/utils.cpp
@@ -19,10 +19,10 @@
*
*/
-#include "bolt/bolt.h"
-
#include "common/config-manager.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
void BoltEngine::displayColors(byte *palette, int16 page, int16 flags) {
diff --git a/engines/bolt/xplib/cursor.cpp b/engines/bolt/xplib/cursor.cpp
index 57cdc49ec3a..0398522eb8e 100644
--- a/engines/bolt/xplib/cursor.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -19,11 +19,11 @@
*
*/
+#include "graphics/cursorman.h"
+
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
-#include "graphics/cursorman.h"
-
namespace Bolt {
bool XpLib::initCursor() {
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 45b8060f6cc..f4917624b64 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -19,11 +19,11 @@
*
*/
+#include "common/events.h"
+
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
-#include "common/events.h"
-
namespace Bolt {
bool XpLib::initEvents() {
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index 48dac0ec895..b3b1bbcef02 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -19,11 +19,11 @@
*
*/
+#include "common/file.h"
+
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
-#include "common/file.h"
-
namespace Bolt {
void XpLib::fileError(const char *message) {
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index 4ebde565844..8c76aa1619b 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -19,11 +19,11 @@
*
*/
+#include "common/timer.h"
+
#include "bolt/bolt.h"
#include "bolt/xplib/xplib.h"
-#include "common/timer.h"
-
namespace Bolt {
void XpLib::getPalette(int16 startIndex, int16 count, byte *destBuf) {
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index 0d0af282a8b..108b6d1f9f1 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -19,13 +19,13 @@
*
*/
-#include "bolt/bolt.h"
-#include "bolt/xplib/xplib.h"
-
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "audio/mixer.h"
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
namespace Bolt {
bool XpLib::initSound() {
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index 0bdf0765809..ac42467a819 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -22,8 +22,6 @@
#ifndef XPLIB_XPLIB_H
#define XPLIB_XPLIB_H
-#include "bolt/bolt.h"
-
#include "audio/audiostream.h"
#include "audio/mixer.h"
@@ -33,6 +31,8 @@
#include "graphics/paletteman.h"
+#include "bolt/bolt.h"
+
namespace Bolt {
// RENDER FLAGS
Commit: 6bab26725ee6faf5ed1c005af1e364624459993a
https://github.com/scummvm/scummvm/commit/6bab26725ee6faf5ed1c005af1e364624459993a
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00
Commit Message:
BOLT: Address more review comments
Changed paths:
engines/bolt/booths/fred.cpp
engines/bolt/booths/george.cpp
engines/bolt/booths/scooby.cpp
engines/bolt/xplib/events.cpp
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index dd2158b1b78..0bba92b44ce 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -101,7 +101,7 @@ bool BoltEngine::initFred() {
_fredLevelIndex++;
}
- // Flush timer events...
+ // Flush non-timer events...
uint32 dummy;
while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -381,7 +381,7 @@ int16 BoltEngine::playFred() {
// Process events...
int16 eventType;
uint32 eventData;
- while ((eventType = _xp->getEvent(0, &eventData)) != 0) {
+ while ((eventType = _xp->getEvent(etEmpty, &eventData)) != etEmpty) {
switch (eventType) {
case etJoystick:
joystickX = (int16)(eventData >> 16);
@@ -790,7 +790,7 @@ int16 BoltEngine::helpFred() {
// Process events...
uint32 eventData;
- int16 eventType = _xp->getEvent(0, &eventData);
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
switch (eventType) {
case etTimer: {
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
index 12f816bcd8f..c4e21972bf2 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/booths/george.cpp
@@ -85,7 +85,7 @@ bool BoltEngine::initGeorge() {
_georgeCollectedSatellitesNum++;
}
- // Drain timer events...
+ // Drain non-timer events...
uint32 dummy;
while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -855,8 +855,8 @@ int16 BoltEngine::helpGeorge() {
_georgeHelpTimer = _xp->startTimer(500);
- bool lit = !(READ_UINT32(_georgePrevActiveHelpObject + 0x08) & 1u);
- hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, lit ? 1 : 0);
+ bool lit = !(READ_UINT32(_georgePrevActiveHelpObject + 0x08) & 1u);
+ hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, lit ? 1 : 0);
break;
}
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 4f401fe8cda..0d126965e52 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -274,7 +274,7 @@ void BoltEngine::initScoobyLevelGraphics() {
_scoobyBgPic = memberAddr(_scoobyBoltLib, picMember);
clearPictMSb(_scoobyBgPic);
- // Drain timer events
+ // Drain non-timer events
uint32 dummy;
while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -1666,7 +1666,7 @@ int16 BoltEngine::helpScooby() {
}
uint32 eventData;
- int16 eventType = _xp->getEvent(0, &eventData);
+ int16 eventType = _xp->getEvent(etEmpty, &eventData);
switch (eventType) {
case etTimer: {
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index f4917624b64..eae1ee3562b 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -58,7 +58,7 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData, byte **outPtrData) {
// Search event queue for matching event type...
XPEvent *node = _eventQueueHead;
- if (filter != 0) {
+ if (filter != etEmpty) {
while (node) {
if (node->type == filter)
break;
@@ -89,7 +89,7 @@ int16 XpLib::peekEvent(int16 filter, uint32 *outData, byte **outPtrData) {
pumpMessages();
XPEvent *node = _eventQueueHead;
- if (filter != 0) {
+ if (filter != etEmpty) {
while (node) {
if (node->type == filter)
break;
@@ -99,7 +99,7 @@ int16 XpLib::peekEvent(int16 filter, uint32 *outData, byte **outPtrData) {
}
if (!node)
- return 0;
+ return etEmpty;
// Copy data but don't remove from queue...
*outData = node->payload;
More information about the Scummvm-git-logs
mailing list