[Scummvm-git-logs] scummvm master -> 878fbe404ca5d8d56a559909a760c1b503305aa2
sev-
noreply at scummvm.org
Sun Feb 5 20:59:52 UTC 2023
This automated email contains information about 83 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
650e0ce7a6 IMMORTAL: Initial skeleton for Immortal engine
4c30f05733 IMMORTAL: ProDos disk file support with implementation of Archive
9b7f4f3768 IMMORTAL: Add disk archive object to SearchMan
2d67a8cf0d IMMORTAL: Add check for file data null pointer and fix file name parameter
8ee7878dff IMMORTAL: Volume bitmap stored as byte * instead of Common::Array
566d3b3cd0 IMMORTAL: Translation of source decompression to compression.cpp
3e560a0ca5 IMMORTAL: ProDos -> ProDOS
05bfec4b60 IMMORTAL: unCompress() returns memoryReadStream instead of int, and bitmasks have enum
df41580e05 IMMORTAL: Fix file data offset calculation error in sapling and tree files
3da314ad88 IMMORTAL: Fix incorrect conditional in primary while loop of compression.cpp
0c9b09a41d IMMORTAL: Better comments and additional extension enum entry
432ecfb1cf IMMORTAL: Preliminary translation of game loop from kernal.gs, driver.gs, and logic.gs
e8d06bf6b6 IMMORTAL: Disk.cpp bug fix to parse correct number of files
d77cc8f5db IMMORTAL: Compression is part of engine class, also minor changes to a couple of statements
676cf5f7b5 IMMORTAL: Update to engine skeleton and addition of sprite_list, sprites, misc, and cycle
c5181c75de IMMORTAL: Indentation spaces -> tabs
0310509fa1 IMMORTAL: Minor formatting in disk.h
032377fe77 IMMORTAL: Add wizard data sprite indexes
d475e36f75 IMMORTAL: New functions added to kernal + update to initdatasprite in sprites.cpp
09a1bba0d0 IMMORTAL: Add _playing and _themePaused
db485ce349 IMMORTAL: Fill out much of logic.cpp skeleton
8391e33c71 IMMORTAL: Add SpriteFrame enum
c942b7f095 IMMORTAL: Add drawChr.cpp with function declarations and entry in module.mk
076742aa14 IMMORTAL: Logic skeleton filled out and necessary parts of story.h added
daa43ce02b IMMORTAL: Space/tab formatting
a1b5ea7187 IMMORTAL: Final two functions of Logic skeleton filled out (makecertificate() and miscinit())
09078a9de5 IMMORTAL: MonsterID enum moved to immortal.h
8ecffca966 IMMORTAL: Add level skeleton
8516b39aa1 IMMORTAL: Add story.cpp and related additions to story.h
35d8b166fe IMMORTAL: Level 0 of story.cpp completed, story.h filled out, and related level.cpp revised
bb56b8cdab IMMORTAL: Add skeleton of Room Object
204dda79b0 IMMORTAL: Add static story STR definitions
9410617da4 IMMORTAL: Add util.h and util.cpp, move several misc functions to util.cpp
af28dec2e9 IMMORTAL: Add bitmask.h and move bitmask enums from immortal.h to bitmask.h
b761800cfb IMMORTAL: Add definitions.h and move enums str, motives, and cyc from story.h to definitions.h
63a6fe2851 IMMORTAL: Update room skeleton, add flameSet, Univ, Door, and Bullet
fe6ef5d0d6 IMMORTAL: Spaces -> Tabs
22a3535e34 IMMORTAL: levelLoadFile initializes level doors
3d696d0e01 IMMORTAL: Frame -> Image, and Image/DataSprite/Cycle moved to sprite_list.h
435727b521 IMMORTAL: Util::X -> Immortal::Util::X
8b5b74fc84 IMMORTAL: Move CycID to definitions.h
55be069f92 IMMORTAL: Implement Cycles from Cyc.GS
6ba6e0fa2c IMMORTAL: Translation of Cycle is more accurate with addition of cycPtrs
c0c1d2e781 IMMORTAL: All engine members initialized to values now
1336c89bdf IMMORTAL: Add non-level static Cycle defintions + enum
ef711df128 IMMORTAL: Util -> Utilities, and addition of addSprite to utilities
5940f2312c IMMORTAL: Add univAddSprite to univ.cpp
821a539b6a IMMORTAL: Implement Cycles more accurately, under Room instead of Immortal, except for cycleFreeAll()
d13fbf16a5 IMMORTAL: Implement flameSet.cpp
259e993f68 IMMORTAL: Spaces -> Tabs
4b28cc268d IMMORTAL: Remove redundant namespaces in Utilities
ff06e73d6f IMMORTAL: Room includes reference to sprite data from ImmortalEngine
89cbe5974d IMMORTAL: Initial implementation of superSprites() + adjustment to sprite structs
b27988471c IMMORTAL: Implement utilities::inside() and insideRect()
8d9994ba56 IMMORTAL: Sprite drawing preliminarily implemented
d8944244a1 IMMORTAL: Implement sprite drawing through superSprites()
0467dbdb78 IMMORTAL: Enable sprite drawing in printChr() and drawGauge()
a2bd437c5e IMMORTAL: addSprite() moved out of utilities
cdf322b93a IMMORTAL: BMW is assumed to be in bytes and is converted to pixels for drawing
a3697c3bd5 IMMORTAL: univAddSprite() calls addSprite() through g_immortal instead of Utilities
c996c64b8c IMMORTAL: uint8 -> uint16 for several variables + initStoryStatic moved to story.cpp
5c2163db8e IMMORTAL: Room object utilizes g_immortal instead of passing pointers from engine members
bf95330f2c IMMORTAL: level.cpp no longer passes pointers to room object
7284826303 IMMORTAL: g_engine -> g_immortal + minor adjustments
4ca64366dc IMMORTAL: flameSet and Cycle utilize g_immortal instead of utilities
13ab4219ae IMMORTAL Remove kStrNoDesc because kStrNull exists
9e911f29d6 IMMORTAL: Text rendering through textSub() and associated functions preliminary implementation
d47efc743b IMMORTAL: Preliminary loadMazeGraphics() and loadUniv() functions
a188dced15 IMMORTAL: Formatting and comment adjustments across all several files
3feb762d90 IMMORTAL: Implement loadUniv() and add stub for makeBlisters()
8232bfd367 IMMORTAL: Update disk.cpp to use strncpy() instead of strcpy()
e16f087c54 IMMORTAL: Rewrite loadUniv() based on new understanding of CNM
55590df288 IMMORTAL: Fix whitespace at end of most files
98247630c2 IMMORTAL: Remove unnecessary commented line
9cbe97d557 IMMORTAL: IMMORTAL_IMMORTAL_H -> IMMORTAL_H
4103ebe600 IMMORTAL: Fix formatting for switch statements
7e0c5b39ab IMMORTAL: Fix formatting for casting
8a1c1764af IMMORTAL: Add TODO comments for unimplemented methods
9c2e1a55a8 IMMORTAL: Remove unused debug console channel method
deb3794626 IMMORTAL: Use AStyle to fix indentation and format issues across all files
ffb7ffc6eb IMMORTAL: Pass flameSet list by reference instead of copy
4116c881e6 IMMORTAL: Change loadPalette to use readUint16LE() instead of read()
878fbe404c IMMORTAL: Clean up some of the header dependancies
Commit: 650e0ce7a6db4ba3df12bf73d50cd4d8b6b4d411
https://github.com/scummvm/scummvm/commit/650e0ce7a6db4ba3df12bf73d50cd4d8b6b4d411
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Initial skeleton for Immortal engine
Changed paths:
A engines/immortal/configure.engine
A engines/immortal/credits.pl
A engines/immortal/detection.cpp
A engines/immortal/detection.h
A engines/immortal/detection_tables.h
A engines/immortal/immortal.cpp
A engines/immortal/immortal.h
A engines/immortal/metaengine.cpp
A engines/immortal/metaengine.h
A engines/immortal/module.mk
diff --git a/engines/immortal/configure.engine b/engines/immortal/configure.engine
new file mode 100644
index 00000000000..835d5bdc5f6
--- /dev/null
+++ b/engines/immortal/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]
+add_engine immortal "The Immortal" no "" "" ""
diff --git a/engines/immortal/credits.pl b/engines/immortal/credits.pl
new file mode 100644
index 00000000000..c9fc8d7affd
--- /dev/null
+++ b/engines/immortal/credits.pl
@@ -0,0 +1,4 @@
+begin_section("Immortal");
+ add_person("Michael Hayman", "Quote58", "");
+ #add_person("Eugene ?", "JoeFish", "");
+end_section();
diff --git a/engines/immortal/detection.cpp b/engines/immortal/detection.cpp
new file mode 100644
index 00000000000..3f3615cfe76
--- /dev/null
+++ b/engines/immortal/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 "engines/advancedDetector.h"
+#include "immortal/immortal.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 "immortal/detection.h"
+#include "immortal/detection_tables.h"
+
+const DebugChannelDef ImmortalMetaEngineDetection::debugFlagList[] = {
+ { Immortal::kDebugTest, "Test", "Test debug channel" },
+ DEBUG_CHANNEL_END
+};
+
+ImmortalMetaEngineDetection::ImmortalMetaEngineDetection() : AdvancedMetaEngineDetection(Immortal::gameDescriptions,
+ sizeof(ADGameDescription), Immortal::immortalGames) {
+}
+
+REGISTER_PLUGIN_STATIC(IMMORTAL_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ImmortalMetaEngineDetection);
diff --git a/engines/immortal/detection.h b/engines/immortal/detection.h
new file mode 100644
index 00000000000..fa4c3fcbf9a
--- /dev/null
+++ b/engines/immortal/detection.h
@@ -0,0 +1,62 @@
+/* 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 IMMORTAL_DETECTION_H
+#define IMMORTAL_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Immortal {
+
+enum ImmortalDebugChannels {
+ kDebugTest = 1 << 0
+};
+
+extern const PlainGameDescriptor immortalGames[];
+extern const ADGameDescription gameDescriptions[];
+
+} // namespace Immortal
+
+class ImmortalMetaEngineDetection : public AdvancedMetaEngineDetection {
+ static const DebugChannelDef debugFlagList[];
+
+public:
+ ImmortalMetaEngineDetection();
+ ~ImmortalMetaEngineDetection() override {}
+
+ const char *getName() const override {
+ return "immortal";
+ }
+
+ const char *getEngineName() const override {
+ return "The Immortal";
+ }
+
+ const char *getOriginalCopyright() const override {
+ return "(c)1990 Will Harvey & Electronic Arts";
+ }
+
+ const DebugChannelDef *getDebugChannels() const override {
+ return debugFlagList;
+ }
+};
+
+#endif
diff --git a/engines/immortal/detection_tables.h b/engines/immortal/detection_tables.h
new file mode 100644
index 00000000000..d1d9bfcd430
--- /dev/null
+++ b/engines/immortal/detection_tables.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/>.
+ *
+ */
+
+namespace Immortal {
+
+const PlainGameDescriptor immortalGames[] = {
+ { "immortal", "The Immortal" },
+ { 0, 0 }
+};
+
+const ADGameDescription gameDescriptions[] = {
+ {
+ "immortal",
+ nullptr,
+ AD_ENTRY1s("IMMORTAL.dsk", "094941fd71e6d2cdaa96c9664d32329e", 819200),
+ Common::EN_ANY,
+ Common::kPlatformApple2GS,
+ ADGF_UNSTABLE,
+ GUIO1(GUIO_NONE)
+ },
+
+ AD_TABLE_END_MARKER
+};
+
+} // namespace Immortal
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
new file mode 100644
index 00000000000..b5c664242d8
--- /dev/null
+++ b/engines/immortal/immortal.cpp
@@ -0,0 +1,90 @@
+/* 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 "immortal/immortal.h"
+#include "immortal/detection.h"
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+
+#include "engines/util.h"
+#include "audio/mixer.h"
+
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Immortal {
+
+ImmortalEngine *g_engine;
+
+ImmortalEngine::ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc)
+ : Engine(syst)
+ , _gameDescription(gameDesc)
+ , _randomSource("Immortal") {
+ g_engine = this;
+ debug("ImmortalEngine::ImmortalEngine");
+}
+
+ImmortalEngine::~ImmortalEngine() {
+ debug("ImmortalEngine::~ImmortalEngine");
+}
+
+uint32 ImmortalEngine::getFeatures() const {
+ return _gameDescription->flags;
+}
+
+Common::String ImmortalEngine::getGameId() const {
+ return _gameDescription->gameId;
+}
+
+Common::Error ImmortalEngine::run() {
+ initGraphics(320, 200);
+
+ while (!shouldQuit()) {
+ int64 loopStart = g_system->getMillis();
+
+ int64 loopEnd = 16 - (g_system->getMillis() - loopStart);
+ if (loopEnd > 0)
+ g_system->delayMillis(loopEnd);
+ }
+
+ return Common::kNoError;
+
+}
+
+Common::Error ImmortalEngine::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;
+}
+
+} // namespace Immortal
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
new file mode 100644
index 00000000000..291cbc93def
--- /dev/null
+++ b/engines/immortal/immortal.h
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 IMMORTAL_IMMORTAL_H
+#define IMMORTAL_IMMORTAL_H
+
+#include "audio/mixer.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 "common/platform.h"
+
+#include "engines/engine.h"
+#include "engines/savestate.h"
+#include "graphics/screen.h"
+
+#include "immortal/detection.h"
+
+namespace Immortal {
+
+struct ImmortalGameDescription;
+
+class ImmortalEngine : public Engine {
+private:
+ Common::RandomSource _randomSource;
+protected:
+ // Engine APIs
+ Common::Error run() override;
+public:
+ const ADGameDescription *_gameDescription;
+
+public:
+ ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc);
+ ~ImmortalEngine() 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() override {
+ return true;
+ }
+ bool canSaveGameStateCurrently() 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) {
+ Common::Serializer s(nullptr, stream);
+ return syncGame(s);
+ } */
+
+ /* Common::Error loadGameStream(Common::SeekableReadStream *stream) {
+ Common::Serializer s(stream, nullptr);
+ return syncGame(s);
+ } */
+};
+
+extern ImmortalEngine *g_engine;
+#define SHOULD_QUIT ::Immortal::g_engine->shouldQuit()
+
+} // namespace Immortal
+
+#endif
diff --git a/engines/immortal/metaengine.cpp b/engines/immortal/metaengine.cpp
new file mode 100644
index 00000000000..b736f5f8042
--- /dev/null
+++ b/engines/immortal/metaengine.cpp
@@ -0,0 +1,51 @@
+/* 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 "immortal/metaengine.h"
+#include "immortal/detection.h"
+#include "immortal/immortal.h"
+
+const char *ImmortalMetaEngine::getName() const {
+ return "immortal";
+}
+
+Common::Error ImmortalMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ *engine = new Immortal::ImmortalEngine(syst, desc);
+ return Common::kNoError;
+}
+
+bool ImmortalMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return false;
+/* return
+ (f == kSavesUseExtendedFormat) ||
+ (f == kSimpleSavesNames) ||
+ (f == kSupportsListSaves) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSupportsLoadingDuringStartup); */
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(IMMORTAL)
+REGISTER_PLUGIN_DYNAMIC(IMMORTAL, PLUGIN_TYPE_ENGINE, ImmortalMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(IMMORTAL, PLUGIN_TYPE_ENGINE, ImmortalMetaEngine);
+#endif
diff --git a/engines/immortal/metaengine.h b/engines/immortal/metaengine.h
new file mode 100644
index 00000000000..cb80691c010
--- /dev/null
+++ b/engines/immortal/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 IMMORTAL_METAENGINE_H
+#define IMMORTAL_METAENGINE_H
+
+#include "base/plugins.h"
+#include "engines/achievements.h"
+#include "engines/advancedDetector.h"
+
+class ImmortalMetaEngine : public AdvancedMetaEngine {
+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;
+};
+
+#endif
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
new file mode 100644
index 00000000000..a0aa13f3841
--- /dev/null
+++ b/engines/immortal/module.mk
@@ -0,0 +1,16 @@
+MODULE := engines/immortal
+
+MODULE_OBJS = \
+ immortal.o \
+ metaengine.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_IMMORTAL), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o
Commit: 4c30f057331df05e5d5f481e9eae781b36b7002c
https://github.com/scummvm/scummvm/commit/4c30f057331df05e5d5f481e9eae781b36b7002c
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: ProDos disk file support with implementation of Archive
Changed paths:
A engines/immortal/disk.cpp
A engines/immortal/disk.h
engines/immortal/module.mk
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
new file mode 100644
index 00000000000..35f516a9582
--- /dev/null
+++ b/engines/immortal/disk.cpp
@@ -0,0 +1,462 @@
+/* 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/debug.h"
+#include "common/file.h"
+
+#include "immortal/immortal.h"
+#include "immortal/disk.h"
+
+namespace Immortal {
+
+// --- ProDosFile methods ---
+
+ProDosFile::ProDosFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk)
+ : _type(type)
+ , _totalBlocks(tBlk)
+ , _eof(eof)
+ , _blockPtr(bPtr)
+ , _disk(disk) {
+ strcpy(_name, name);
+ }
+
+/* For debugging purposes, this prints the meta data of a file */
+
+void ProDosFile::printInfo() {
+ debug("File: %s", _name);
+ debug("Type: %02X", _type);
+ debug("Blocks: %d", _totalBlocks);
+ debug("Size: %u", _eof);
+ debug("");
+}
+
+/* For Common::Archive, this method just returns a string of the name */
+
+Common::String ProDosFile::getName() const {
+ return Common::String(_name);
+}
+
+/* This method is used to get a single block of data from the disk,
+ * but is not strictly 512 bytes. This is so that it can get only what
+ * it needs when in the final block. It then adds it into the allocated
+ * memory starting at memOffset
+ */
+
+void ProDosFile::getDataBlock(byte *memOffset, int offset, int size) const {
+
+ // All this method needs to do is read (size) of data at (offset) into (memOffset)
+ _disk->seek(offset);
+ _disk->read(memOffset, size);
+}
+
+/* To put together a sapling file, you need to loop through the index
+ * block, adding to the file data one block at a time. This method also
+ * returns the size of data it got, just to make it a little simpler to
+ * determine the new position within the byte data.
+ */
+
+int ProDosFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
+ int dataSize; // For most of the blocks, this will be kBlockSize, but the last one will be the calculated remainder
+ int readSize = 0; // This keeps track of the new pointer position to read data to, by updating the size of data read last
+ int dataOffset; // Where in the disk to read from
+ int diskPos; // Current position of cursor
+
+ for (int i = 0; i < blockNum; i++) {
+ dataSize = (i == (blockNum - 1)) ? rem : ProDosDisk::kBlockSize;
+ dataOffset = _disk->readByte(); // Low byte is first
+
+ /* The cursor needs to know where to get the next pointer from in the index block,
+ * but it also needs to jump to the offset of data to read it, so we need to preserve
+ * the position in the index block it was in before.
+ */
+ diskPos = _disk->pos();
+
+ _disk->skip(255); // The high bytes are stored at the end of the block instead because reasons???
+ dataOffset += (_disk->readByte() << 8) * ProDosDisk::kBlockSize; // High byte is second
+
+ getDataBlock(memOffset + readSize, dataOffset, dataSize);
+ readSize += dataSize;
+
+ // And now we resume the position before this call
+ _disk->seek(diskPos);
+ }
+ return readSize;
+}
+
+/* Extracting file data is a little tricky, as the blocks are spread out in the disk. There are 3 types
+ * of regular files. Seed, Sapling, and Tree. A Seed file only needs a single data block, while a
+ * Sapling needs an index block to manage up to 256 data blocks, and a Tree file needs an index block
+ * to manage up to 128 (only uses half the block) index blocks. This is also an Archive method as it
+ * returns a read stream of the file contents.
+ */
+
+Common::SeekableReadStream *ProDosFile::createReadStream() const {
+
+ // We know the total byte size of the data, so we can allocate the full amount right away
+ byte *finalData = (byte *)malloc(_eof);
+
+ /* For a seed, this is a direct pointer to data. For a sapling it is an index file,
+ * and for a tree it is a master index file.
+ */
+ int indexBlock = _blockPtr * ProDosDisk::kBlockSize;
+
+ /* For a sapling or tree, the size needs to be calculated, as they are made from multiple blocks.
+ * _totalBlocks *includes* the index block, so the blocks before the oef block are _totalBlocks-2
+ */
+ int remainder = _eof - ((_totalBlocks - 2) * ProDosDisk::kBlockSize);
+
+ // For a seed file, the end of file value is also the size in the block, because it's just the one block
+ if (_type == kFileTypeSeed) {
+ getDataBlock(finalData, indexBlock, _eof);
+
+ } else if (_type == kFileTypeSapling) {
+ _disk->seek(indexBlock);
+ parseIndexBlock(finalData, _totalBlocks - 1, remainder);
+
+ } else {
+ // If it's not a seed and not a sapling, it's a tree.
+ _disk->seek(indexBlock);
+
+ /* A sapling can have an index block of up to 256, so if it is a tree,
+ * that means it has more than 256 blocks
+ */
+ int indexNum = (_totalBlocks - 1) / 256;
+ int indexNumR = (_totalBlocks - 1) % 256;
+
+ /* However, to know how many index blocks there are, we need to know the remainder
+ * so we can figure out if it's ex. 2 index blocks, or 2 and some portion of a 3rd
+ */
+ indexNum += indexNumR;
+ int blockNum;
+ int indexOffset;
+ int readSize = 0;
+
+ // Now we can loop through the master index file, parsing the individual index files similar to a sapling
+ for (int i = 0; i < indexNum; i++) {
+ blockNum = (i == indexNum - 1) ? indexNumR : 256;
+
+ indexOffset = _disk->readByte();
+ int diskPos = _disk->pos();
+
+ _disk->skip(255);
+ indexOffset += (_disk->readByte() << 8) * ProDosDisk::kBlockSize;
+
+ _disk->seek(indexOffset);
+ readSize += parseIndexBlock(finalData + readSize, blockNum, remainder);
+
+ _disk->seek(diskPos);
+ }
+ }
+ return new Common::MemoryReadStream(finalData, _eof, DisposeAfterUse::YES);
+}
+
+// --- ProDosDisk methods ---
+
+/* The time and date are compressed into 16bit words, so to make them useable
+ * we have to decompress them by masking the other bits and then shifting
+ * to the lowest bit so that they can be stored in 8 bits each.
+ */
+
+void ProDosDisk::getDate(Date *d, uint16 date) {
+ d->_day = date & 0x001f;
+ d->_month = (date & 0x01e0) >> 5;
+ d->_year = (date & 0xfe00) >> 9;
+}
+
+void ProDosDisk::getTime(Time *t, uint16 time) {
+ t->_minute = time & 0x003f;
+ t->_hour = (time & 0x1f00) >> 8;
+}
+
+/* Adds most of the header data to a directory header struct */
+
+void ProDosDisk::getHeader(DirHeader *h) {
+
+ /* The type and nameLen fields are stored in the same byte,
+ * so we need to split the byte, and shift the high bits to
+ * make it readable as an int
+ */
+ uint8 tempByte = _disk.readByte();
+ h->_nameLen = tempByte & 0xf;
+ h->_type = (tempByte & 0xf0) >> 4;
+
+ // The name field is stored in 15 bytes with no null character (aside from unused chars being 00)
+ _disk.read(h->_name, 15);
+ _disk.read(h->_reserved, 8);
+
+ // The time and date can be decompressed into structs right away
+ getDate(&(h->_date), _disk.readUint16LE());
+ getTime(&(h->_time), _disk.readUint16LE());
+
+ h->_ver = _disk.readByte();
+ h->_minVer = _disk.readByte();
+ h->_access = _disk.readByte();
+ h->_entryLen = _disk.readByte();
+ h->_entriesPerBlock = _disk.readByte();
+ h->_fileCount = _disk.readUint16LE();
+}
+
+/* Since a subdirectory header is mostly the same a volume header, we will reuse the code where we can */
+
+void ProDosDisk::getDirectoryHeader(DirHeader *h) {
+ getHeader(h);
+ h->_parentBlockPtr = _disk.readUint16LE();
+ h->_parentEntryIndex = _disk.readByte();
+ h->_parentEntryLen = _disk.readUint16LE();
+}
+
+/* This is a little sneaky, but since the bulk of the header is the same, we're just going to pretend the volume header
+ * is a directory header for the purose of filling it out with the same code
+ */
+
+void ProDosDisk::getVolumeHeader(VolHeader *h) {
+ getHeader((DirHeader *)h);
+ h->_bitmapPtr = _disk.readUint16LE();
+ h->_volBlocks = _disk.readUint16LE();
+ _volBlocks = h->_volBlocks;
+}
+
+/* Getting a file entry header is very similar to getting a header, but with different data. */
+
+void ProDosDisk::getFileEntry(FileEntry *f) {
+ uint8 tempByte = _disk.readByte();
+ f->_nameLen = tempByte & 0xf;
+ f->_type = (tempByte & 0xf0) >> 4;
+
+ _disk.read(f->_name, 15);
+ f->_ext = _disk.readByte();
+ f->_blockPtr = _disk.readUint16LE();
+ f->_totalBlocks = _disk.readUint16LE();
+
+ // The file size in bytes is stored as a long (3 bytes), lowest to highest
+ f->_eof = _disk.readByte() + (_disk.readByte() << 8) + (_disk.readByte() << 16);
+
+ getDate(&(f->_date), _disk.readUint16LE());
+ getTime(&(f->_time), _disk.readUint16LE());
+
+ f->_ver = _disk.readByte();
+ f->_minVer = _disk.readByte();
+ f->_access = _disk.readByte();
+ f->_varUse = _disk.readUint16LE();
+
+ getDate(&(f->_modDate), _disk.readUint16LE());
+ getTime(&(f->_modTime), _disk.readUint16LE());
+
+ f->_dirHeadPtr = _disk.readUint16LE();
+}
+
+/* This is basically a loop based on the number of total files indicated by the header (including deleted file entries),
+ * which parses the file entry, and if it is a regular file (ie. active and not a pascal area) then it will create a file object.
+ * If it is instead a subdirectory file entry, it will use this same function to search in that directory creating files
+ * and continue like that until all directories have been explored. Along the way it puts together the current file path,
+ * which is stored with the file object so that the engine can search by path name.
+ */
+
+void ProDosDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path) {
+ int currPos;
+ int parsedFiles = 0;
+
+ for (int i = 0; i < h->_fileCount; i++) {
+
+ // When we have read all the files for a given block (_entriesPerBlock), we need to change to the next block of the directory
+ if (parsedFiles > h->_entriesPerBlock) {
+ parsedFiles = 0;
+ _disk.seek(n * kBlockSize);
+ p = _disk.readUint16LE();
+ n = _disk.readUint16LE();
+ }
+
+ FileEntry fileEntry;
+ getFileEntry(&fileEntry);
+
+ parsedFiles++;
+ currPos = _disk.pos();
+
+ // It is a regular file if (dead < file type < pascal) and the file has a size
+ if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
+ char cString[16];
+ for (int j = 0; j < 15; j++) {
+ cString[j] = fileEntry._name[j];
+ }
+ cString[15] = 0;
+ Common::String fileName = path + cString;
+ debug("%s", fileName.c_str());
+ //debug("%s", fileName.c_str());
+ ProDosFile *currFile = new ProDosFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
+ //debug("%s", currFile._name);
+ currFile->printInfo();
+
+ _files.setVal(fileName, Common::SharedPtr<ProDosFile>(currFile));
+ _disk.seek(currPos);
+
+ // Otherwise, if it is a subdirectory, we want to explore that subdirectory
+ } else if (fileEntry._type == kFileTypeSubDir) {
+
+ _disk.seek(fileEntry._blockPtr * kBlockSize);
+ debug("--- diving into a subdirectory ---");
+
+ uint16 subP = _disk.readUint16LE();
+ uint16 subN = _disk.readUint16LE();
+ DirHeader subHead;
+ getDirectoryHeader(&subHead);
+
+ path += Common::String(subHead._name);
+ path += '/';
+ searchDirectory(&subHead, subP, subN, path);
+
+ debug("--- surfacing to parent directory ---");
+ _disk.seek(currPos);
+ }
+ }
+}
+
+/* This just gets the volume bitmap by finding out how many blocks it takes up and then adding them to vector of the data. */
+
+void ProDosDisk::getVolumeBitmap(VolHeader *h) {
+ int currPos = _disk.pos();
+ _disk.seek(h->_bitmapPtr * kBlockSize);
+ int bitmapNum = 1;
+ if (_volBlocks >= 4096) {
+ bitmapNum = _volBlocks / 4096;
+ }
+ for (int i = 0; i < bitmapNum; i++) {
+ byte bitmapData[512];
+ _disk.read(bitmapData, kBlockSize);
+ Common::Array<byte> volBitmapBlock = Common::Array<byte>(kBlockSize);
+ for (int j = 0; j < kBlockSize; j++) {
+ volBitmapBlock[j] = bitmapData[j];
+ }
+ _volBitmap.push_back(volBitmapBlock);
+ }
+ _disk.seek(currPos);
+}
+
+/* Gets the volume information and parses the filesystem, adding file objects to a map as it goes */
+
+bool ProDosDisk::open(const Common::String filename) {
+ debug("opening %s", filename.c_str());
+
+ _disk.open(filename);
+ _disk.read(_loader1, kBlockSize);
+ _disk.read(_loader2, kBlockSize);
+
+ uint16 prev = _disk.readUint16LE(); // This is always going to be 0 for the volume header, but there's also no reason to skip it
+ uint16 next = _disk.readUint16LE();
+
+ VolHeader header;
+ getVolumeHeader(&header);
+ debug("volume name: %s", header._name);
+ debug("volume created %d/%d/19%d", header._date._day, header._date._month, header._date._year);
+
+ getVolumeBitmap(&header);
+
+ Common::String pathName; // This is so that the path name starts blank, and then for every directory searched it adds the directory name to the path
+ searchDirectory((DirHeader *)&header, prev, next, pathName);
+
+ return true; // When I get to error checking on this, the bool will be useful
+}
+
+/* Constructor simply calls open(), and if it is successful it prints a statement */
+
+ProDosDisk::ProDosDisk(const Common::String filename) {
+ if (open(filename)) {
+ debug ("%s has been loaded", filename.c_str());
+ }
+}
+
+/* Destructor closes the disk and clears the map of files */
+
+ProDosDisk::~ProDosDisk() {
+ _disk.close();
+ _files.clear();
+}
+
+// --- Common::Archive methods ---
+
+bool ProDosDisk::hasFile(const Common::Path &path) const {
+ Common::String name = path.toString();
+ return _files.contains(name);
+}
+
+int ProDosDisk::listMembers(Common::ArchiveMemberList &list) const {
+ int f = 0;
+ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>>::const_iterator it;
+ for (it = _files.begin(); it != _files.end(); ++it) {
+ list.push_back(Common::ArchiveMemberList::value_type(it->_value));
+ ++f;
+ }
+ return f;
+}
+
+const Common::ArchiveMemberPtr ProDosDisk::getMember(const Common::Path &path) const {
+ Common::String name = path.toString();
+ if (!_files.contains(name)) {
+ return Common::ArchiveMemberPtr();
+ }
+ return _files.getValOrDefault(name);
+}
+
+Common::SeekableReadStream *ProDosDisk::createReadStreamForMember(const Common::Path &path) const {
+ Common::String name = path.toString();
+ Common::SharedPtr<ProDosFile> f = _files.getValOrDefault(name);
+ return f->createReadStream();
+}
+
+} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
new file mode 100644
index 00000000000..bc173ec3bf5
--- /dev/null
+++ b/engines/immortal/disk.h
@@ -0,0 +1,250 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 IMMORTAL_DSK_H
+#define IMMORTAL_DSK_H
+
+#include "common/memstream.h"
+#include "common/file.h"
+#include "common/debug.h"
+
+/* Quick note about ProDos:
+ * This disk code handles inactive, seedling, sapling, tree, and subdirectory files.
+ * It does *not* handle sparse files at the moment. If a sparse file exists, it may not
+ * be read correctly. It also does not do anything with Pascal files, but those should not
+ * matter for game engines anyway.
+ */
+
+/* There is also one thing that currently has an error. If a file size is the full 15 chars,
+ * it seems to add a char of some kind to the end and can't be looked up by that value.
+ * Probably just a difference in string encoding that I need to sort out
+ */
+
+namespace Immortal {
+
+// These values define for ProDos how to read the file entry, and also whether it's a keyblock (if it is a directory header, it's the keyblock of that directory)
+enum FileType : char {
+ kFileTypeDead = 0,
+ kFileTypeSeed = 1,
+ kFileTypeSapling = 2,
+ kFileTypeTree = 3,
+ kFileTypePascal = 4,
+ kFileTypeSubDir = 0x0D,
+ kFileTypeSubHead = 0x0E,
+ kFileTypeVolHead = 0x0F
+};
+
+/* File extensions for all the ProDos supported file types
+ * NOTE: ProDos user defined files are F1-F8. If they are ever required,
+ * they can be added to this enum.
+ */
+enum FileExt {
+ kFileExtNull = 0,
+ kFileExtBad = 1,
+ kFileExtTxt = 4,
+ kFileExtBin = 6,
+ kFileExtGfx = 8,
+ kFileExtDir = 0xF,
+ kFileExtDB = 0x19,
+ kFileExtWord = 0x1A,
+ kFileExtSpread = 0x1B,
+ kFileExtPascal = 0xEF,
+ kFileExtPDCI = 0xF0,
+ kFileExtPDRes = 0xF9,
+ kFileExtIBProg = 0xFA,
+ kFileExtIBVar = 0xFB,
+ kFileExtAPSProg = 0xFC,
+ kFileExtAPSVar = 0xFD,
+ kFileExtEDASM = 0xFE,
+ kFileExtPDSys = 0xFF
+};
+
+/* A ProDos file simply contains meta data about the file and the ability to
+ * find and put together the data blocks that make up the file contents.
+ * This implements Common::ArchiveMember so that it can be used directly in
+ * the Archive methods in ProDosDisk.
+ */
+
+class ProDosFile : public Common::ArchiveMember {
+public:
+ ProDosFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
+ ~ProDosFile() {};
+
+ // These are the Common::ArchiveMember related functions
+ Common::String getName() const override;
+ Common::SeekableReadStream *createReadStream() const override;
+ void getDataBlock(byte *memOffset, int offset, int size) const;
+ int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const;
+ void printInfo();
+
+private:
+ char _name[15];
+ uint8 _type;
+ uint16 _blockPtr;
+ uint16 _totalBlocks;
+ uint32 _eof;
+ Common::File *_disk;
+};
+
+/* This class defines the entire disk volume. Upon using the open() method,
+ * it will parse the volume and add them to a hashmap where the file path
+ * returns a file object. This implements Common::Archive to allow regular
+ * file operations to work with it.
+ */
+
+class ProDosDisk : public Common::Archive {
+public:
+ static const int kBlockSize = 512; // A ProDos block is always 512 bytes
+
+ ProDosDisk(const Common::String filename);
+ ~ProDosDisk();
+
+ // Called from the constructor, parses the volume and fills the hashmap with files
+ bool open(const Common::String filename);
+
+ // These are the Common::Archive related methods
+ bool hasFile(const Common::Path &path) const override;
+ int listMembers(Common::ArchiveMemberList &list) const override;
+ const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
+ Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
+
+private:
+ byte _loader1[512]; // There's no reason these would be needed, but why not include them just in case
+ byte _loader2[512];
+Common::String _name; // Name of volume
+ Common::File _disk; // The volume file itself
+ int _volBlocks; // Total blocks in volume
+
+Common::Array<byte> _volBitmap; // Not super useful, but could be used with _totalBlocks to check whether the disk is corrupted
+Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDosFile
+
+ struct Date {
+ uint8 _day;
+ uint8 _month;
+ uint8 _year;
+ };
+
+ struct Time {
+ uint8 _hour;
+ uint8 _minute;
+ };
+
+ struct VolHeader {
+ uint8 _type; // Not really important for a volume header, as this will always be F
+ uint8 _nameLen;
+ char _name[15];
+ byte _reserved[8]; // Extra space reserved for possible future uses, not important
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer; // Should pretty much always be 0 as far as I know
+ uint8 _access; // If this ends up useful, there should be an enum for the access values
+ uint8 _entryLen; // Always 27 in ProDos 1.0
+ uint8 _entriesPerBlock; // Always 0D in ProDos 1.0
+ uint16 _fileCount; // Number of files across all data blocks in this directory
+ uint16 _bitmapPtr; // Block pointer to the keyblock of the bitmap for the entire volume
+ uint16 _volBlocks; // Blocks in entire volume
+ };
+
+ struct DirHeader {
+ uint8 _type;
+ uint8 _nameLen;
+ char _name[15];
+ byte _reserved[8];
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer;
+ uint8 _access;
+ uint8 _entryLen;
+ uint8 _entriesPerBlock;
+ uint16 _fileCount;
+ uint16 _parentBlockPtr; // These values allow ProDos to navigate back out of a directory, but they aren't really needed by the class to navigate
+ uint8 _parentEntryIndex; // Index in the current directory
+ uint8 _parentEntryLen; // This is always 27 in ProDos 1.0
+ };
+
+ struct FileEntry {
+ uint8 _type; // 0 = inactive, 1-3 = file, 4 = pascal area, 14 = subdirectory, 15 = volume directory
+ uint8 _nameLen;
+ char _name[15];
+ uint8 _ext; // File extension, uses the enum FileExt
+ uint16 _blockPtr; // Block pointer to data for seedling, index block for sapling, or master block for tree
+ uint16 _totalBlocks; // Really important to remember this is the total *including* the index block
+ uint32 _eof; // This is a long (3 bytes, read low to high) value representing the total readable data in a file (unless it's a sparse file, be careful!)
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer;
+ uint8 _access;
+ uint16 _varUse;
+ Date _modDate;
+ Time _modTime;
+ uint16 _dirHeadPtr; // Pointer to the key block of the directory that contains this file entry
+ };
+
+ void getDate(Date *d, uint16 date); // Decompresses the date into a struct
+ void getTime(Time *t, uint16 time); // Decompresses the time into a struct
+ void getHeader(DirHeader *h); // Adds the main header values to the struct
+ void getDirectoryHeader(DirHeader *h); // Uses getHeader and then fills in the values for the parent directory
+ void getVolumeHeader(VolHeader *dir); // Uses getHeader and then fills in the volume related information (there is no parent directory to this one)
+ void getFileEntry(FileEntry *f); // Adds all of the file entry information to the struct
+ void searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path); // Recursively searches all files within a directory, and dives into subdirectories to search them as well
+ void getVolumeBitmap(VolHeader *h); // Puts together the volume bitmap
+};
+
+
+} // namespace Immortal
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index a0aa13f3841..684f8af133c 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/immortal
MODULE_OBJS = \
immortal.o \
+ disk.o \
metaengine.o
# This module can be built as a plugin
Commit: 9b7f4f37683254901f06fe0eeaecd0a560284f46
https://github.com/scummvm/scummvm/commit/9b7f4f37683254901f06fe0eeaecd0a560284f46
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add disk archive object to SearchMan
Changed paths:
engines/immortal/immortal.cpp
engines/immortal/immortal.h
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index b5c664242d8..ffa04323292 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -21,6 +21,7 @@
#include "immortal/immortal.h"
#include "immortal/detection.h"
+#include "immortal/disk.h"
#include "common/scummsys.h"
#include "common/config-manager.h"
@@ -30,6 +31,7 @@
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/error.h"
+#include "common/file.h"
#include "engines/util.h"
#include "audio/mixer.h"
@@ -46,6 +48,10 @@ ImmortalEngine::ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc)
, _gameDescription(gameDesc)
, _randomSource("Immortal") {
g_engine = this;
+
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "game");
+
debug("ImmortalEngine::ImmortalEngine");
}
@@ -64,8 +70,44 @@ Common::String ImmortalEngine::getGameId() const {
Common::Error ImmortalEngine::run() {
initGraphics(320, 200);
+ if (SearchMan.hasFile("IMMORTAL.dsk")) {
+ ProDosDisk *diskBoot = new ProDosDisk("IMMORTAL.dsk");
+ if (diskBoot) {
+ debug("Boot disk found");
+ SearchMan.add("IMMORTAL.dsk", diskBoot, 0, true);
+ }
+ }
+
+ if (SearchMan.hasFile("IMMORTAL_GFX.dsk")) {
+ ProDosDisk *diskGFX = new ProDosDisk("IMMORTAL_GFX.dsk");
+ if (diskGFX) {
+ debug("Gfx disk found");
+ SearchMan.add("IMMORTAL_GFX.dsk", diskGFX, 0, true);
+ }
+ }
+
+ Common::File f;
+ if (!f.open("LOAD.OBJ")) {
+ debug("oh no :(");
+ }
+
+ debug("first file loaded");
+ f.close();
+
+ Common::File f2;
+ if (!f2.open("GOBLIN.SPR")) {
+ debug("oh no :((");
+ }
+
+ debug("second file loaded");
+ f2.close();
+
+
while (!shouldQuit()) {
+
int64 loopStart = g_system->getMillis();
+ Common::Event event;
+ g_system->getEventManager()->pollEvent(event);
int64 loopEnd = 16 - (g_system->getMillis() - loopStart);
if (loopEnd > 0)
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 291cbc93def..fc64b0f2f73 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -43,10 +43,12 @@
namespace Immortal {
struct ImmortalGameDescription;
+class ProDosDisk;
class ImmortalEngine : public Engine {
private:
Common::RandomSource _randomSource;
+
protected:
// Engine APIs
Common::Error run() override;
Commit: 2d67a8cf0dae01ea1333ebcc98d06a3798f11e9f
https://github.com/scummvm/scummvm/commit/2d67a8cf0dae01ea1333ebcc98d06a3798f11e9f
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add check for file data null pointer and fix file name parameter
Changed paths:
engines/immortal/disk.cpp
engines/immortal/disk.h
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index 35f516a9582..d14713a4591 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -45,8 +45,7 @@ void ProDosFile::printInfo() {
debug("File: %s", _name);
debug("Type: %02X", _type);
debug("Blocks: %d", _totalBlocks);
- debug("Size: %u", _eof);
- debug("");
+ debug("Size: %u\n", _eof);
}
/* For Common::Archive, this method just returns a string of the name */
@@ -199,8 +198,11 @@ void ProDosDisk::getHeader(DirHeader *h) {
h->_nameLen = tempByte & 0xf;
h->_type = (tempByte & 0xf0) >> 4;
- // The name field is stored in 15 bytes with no null character (aside from unused chars being 00)
+ /* The name field is stored in 15 bytes with no null character (unused chars default to 0).
+ * To make it easier to use the name, we will add a terminator regardless.
+ */
_disk.read(h->_name, 15);
+ h->_name[15] = 0;
_disk.read(h->_reserved, 8);
// The time and date can be decompressed into structs right away
@@ -243,6 +245,7 @@ void ProDosDisk::getFileEntry(FileEntry *f) {
f->_type = (tempByte & 0xf0) >> 4;
_disk.read(f->_name, 15);
+ f->_name[15] = 0;
f->_ext = _disk.readByte();
f->_blockPtr = _disk.readUint16LE();
f->_totalBlocks = _disk.readUint16LE();
@@ -293,17 +296,9 @@ void ProDosDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
// It is a regular file if (dead < file type < pascal) and the file has a size
if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
- char cString[16];
- for (int j = 0; j < 15; j++) {
- cString[j] = fileEntry._name[j];
- }
- cString[15] = 0;
- Common::String fileName = path + cString;
+ Common::String fileName = path + fileEntry._name;
debug("%s", fileName.c_str());
- //debug("%s", fileName.c_str());
ProDosFile *currFile = new ProDosFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
- //debug("%s", currFile._name);
- currFile->printInfo();
_files.setVal(fileName, Common::SharedPtr<ProDosFile>(currFile));
_disk.seek(currPos);
@@ -365,7 +360,7 @@ bool ProDosDisk::open(const Common::String filename) {
VolHeader header;
getVolumeHeader(&header);
debug("volume name: %s", header._name);
- debug("volume created %d/%d/19%d", header._date._day, header._date._month, header._date._year);
+ //debug("volume created %d/%d/19%d", header._date._day, header._date._month, header._date._year);
getVolumeBitmap(&header);
@@ -397,6 +392,11 @@ bool ProDosDisk::hasFile(const Common::Path &path) const {
return _files.contains(name);
}
+/* To create a list of files in the Archive, we define an iterator for the object type
+ * used by the Archive member, and then loop through the hashmap, adding the object
+ * pointer returned as the value from the given path. This also returns the size.
+ */
+
int ProDosDisk::listMembers(Common::ArchiveMemberList &list) const {
int f = 0;
Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>>::const_iterator it;
@@ -415,9 +415,17 @@ const Common::ArchiveMemberPtr ProDosDisk::getMember(const Common::Path &path) c
return _files.getValOrDefault(name);
}
+/* This method is called on Archive members as it searches for the correct one,
+ * so if this member is not the correct one, we return a null pointer.
+ */
+
Common::SeekableReadStream *ProDosDisk::createReadStreamForMember(const Common::Path &path) const {
Common::String name = path.toString();
+ if (!_files.contains(name)) {
+ return nullptr;
+ }
Common::SharedPtr<ProDosFile> f = _files.getValOrDefault(name);
+ f->printInfo();
return f->createReadStream();
}
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index bc173ec3bf5..d51971af997 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -33,11 +33,6 @@
* matter for game engines anyway.
*/
-/* There is also one thing that currently has an error. If a file size is the full 15 chars,
- * it seems to add a char of some kind to the end and can't be looked up by that value.
- * Probably just a difference in string encoding that I need to sort out
- */
-
namespace Immortal {
// These values define for ProDos how to read the file entry, and also whether it's a keyblock (if it is a directory header, it's the keyblock of that directory)
@@ -85,7 +80,7 @@ enum FileExt {
class ProDosFile : public Common::ArchiveMember {
public:
- ProDosFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
+ ProDosFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
~ProDosFile() {};
// These are the Common::ArchiveMember related functions
@@ -93,10 +88,12 @@ public:
Common::SeekableReadStream *createReadStream() const override;
void getDataBlock(byte *memOffset, int offset, int size) const;
int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const;
+
+ // Mostly for debugging purposes, just prints the metadata
void printInfo();
private:
- char _name[15];
+ char _name[16];
uint8 _type;
uint16 _blockPtr;
uint16 _totalBlocks;
@@ -117,7 +114,7 @@ public:
ProDosDisk(const Common::String filename);
~ProDosDisk();
- // Called from the constructor, parses the volume and fills the hashmap with files
+ // Called from the constructor, it parses the volume and fills the hashmap with files
bool open(const Common::String filename);
// These are the Common::Archive related methods
@@ -150,7 +147,7 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashma
struct VolHeader {
uint8 _type; // Not really important for a volume header, as this will always be F
uint8 _nameLen;
- char _name[15];
+ char _name[16];
byte _reserved[8]; // Extra space reserved for possible future uses, not important
Date _date;
Time _time;
@@ -167,7 +164,7 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashma
struct DirHeader {
uint8 _type;
uint8 _nameLen;
- char _name[15];
+ char _name[16];
byte _reserved[8];
Date _date;
Time _time;
@@ -185,7 +182,7 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashma
struct FileEntry {
uint8 _type; // 0 = inactive, 1-3 = file, 4 = pascal area, 14 = subdirectory, 15 = volume directory
uint8 _nameLen;
- char _name[15];
+ char _name[16];
uint8 _ext; // File extension, uses the enum FileExt
uint16 _blockPtr; // Block pointer to data for seedling, index block for sapling, or master block for tree
uint16 _totalBlocks; // Really important to remember this is the total *including* the index block
@@ -207,7 +204,7 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashma
void getDirectoryHeader(DirHeader *h); // Uses getHeader and then fills in the values for the parent directory
void getVolumeHeader(VolHeader *dir); // Uses getHeader and then fills in the volume related information (there is no parent directory to this one)
void getFileEntry(FileEntry *f); // Adds all of the file entry information to the struct
- void searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path); // Recursively searches all files within a directory, and dives into subdirectories to search them as well
+ void searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path); // Recursively searches all files within a directory, by calling itself for subdirectories
void getVolumeBitmap(VolHeader *h); // Puts together the volume bitmap
};
Commit: 8ee7878dffeab42b7ad0c423109561e2e699ae61
https://github.com/scummvm/scummvm/commit/8ee7878dffeab42b7ad0c423109561e2e699ae61
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Volume bitmap stored as byte * instead of Common::Array
Changed paths:
engines/immortal/disk.cpp
engines/immortal/disk.h
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index d14713a4591..736c4606557 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -324,25 +324,25 @@ void ProDosDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
}
}
-/* This just gets the volume bitmap by finding out how many blocks it takes up and then adding them to vector of the data. */
+/* The volume bitmap is a bitmap spanning as many blocks as is required to store 1 bit for every
+ * block on the disk. There are 8 bits per byte and 512 bytes per block, so it needs
+ * ((total_blocks / 4096) + 1 (if remainder)) * 512 bytes.
+ */
void ProDosDisk::getVolumeBitmap(VolHeader *h) {
int currPos = _disk.pos();
- _disk.seek(h->_bitmapPtr * kBlockSize);
- int bitmapNum = 1;
- if (_volBlocks >= 4096) {
- bitmapNum = _volBlocks / 4096;
- }
- for (int i = 0; i < bitmapNum; i++) {
- byte bitmapData[512];
- _disk.read(bitmapData, kBlockSize);
- Common::Array<byte> volBitmapBlock = Common::Array<byte>(kBlockSize);
- for (int j = 0; j < kBlockSize; j++) {
- volBitmapBlock[j] = bitmapData[j];
- }
- _volBitmap.push_back(volBitmapBlock);
+ int bitmapSize;
+
+ bitmapSize = _volBlocks / 4096;
+ if ((_volBlocks % 4096) > 0) {
+ bitmapSize++;
}
- _disk.seek(currPos);
+
+ _volBitmap = (byte *)malloc(bitmapSize * kBlockSize);
+ _disk.seek(h->_bitmapPtr * kBlockSize);
+ _disk.read(_volBitmap, bitmapSize);
+
+ _disk.seek(currPos);
}
/* Gets the volume information and parses the filesystem, adding file objects to a map as it goes */
@@ -383,6 +383,7 @@ ProDosDisk::ProDosDisk(const Common::String filename) {
ProDosDisk::~ProDosDisk() {
_disk.close();
_files.clear();
+ delete _volBitmap;
}
// --- Common::Archive methods ---
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index d51971af997..4f8c1ea3599 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -124,13 +124,12 @@ public:
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
- byte _loader1[512]; // There's no reason these would be needed, but why not include them just in case
- byte _loader2[512];
-Common::String _name; // Name of volume
- Common::File _disk; // The volume file itself
- int _volBlocks; // Total blocks in volume
-
-Common::Array<byte> _volBitmap; // Not super useful, but could be used with _totalBlocks to check whether the disk is corrupted
+ byte _loader1[512]; // There's no reason these would be needed, but why not include them just in case
+ byte _loader2[512];
+Common::String _name; // Name of volume
+ Common::File _disk; // The volume file itself
+ int _volBlocks; // Total blocks in volume
+ byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDosFile
struct Date {
Commit: 566d3b3cd0e082e52c1ec60fad2c0b5bf98bafa3
https://github.com/scummvm/scummvm/commit/566d3b3cd0e082e52c1ec60fad2c0b5bf98bafa3
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Translation of source decompression to compression.cpp
Changed paths:
A engines/immortal/compression.cpp
A engines/immortal/compression.h
engines/immortal/module.mk
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
new file mode 100644
index 00000000000..16b24467e6e
--- /dev/null
+++ b/engines/immortal/compression.cpp
@@ -0,0 +1,278 @@
+ /* 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/debug.h"
+#include "common/file.h"
+#include "common/memstream.h"
+#include "immortal/compression.h"
+
+/* In: Source data as File, size of data, pointer that will be replaced with pointer to the data
+ * Out: size of uncompressed data
+ */
+namespace Compression {
+
+int unCompress(Common::File *src, int srcLen, byte *dst) {
+ uint16 k12BitWord = 0x0F9F; // Code is stored in lower 12 bits of word, probably doesn't need to be const
+
+ Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::YES);
+
+ //debug("lets get started");
+
+ // If the source data has no length, we certainly do not want to decompress it
+ if (srcLen == 0) {
+ return -1; // Classic C error code
+ }
+
+ //debug("not null at least");
+ // This is the 20k bytes the compression gets allocated to work with for the dictionary and the stack of chars
+ uint16 start[0x4000]; // Rename! <- I did? Probably still needs a better name though
+ uint16 ptk[0x4000]; // Pointer to keys?
+ byte stack[0x4000]; // Stack of chars to be stored
+
+ // These are the main variables we'll need for this. They were basically scratch ram in the original
+ uint16 findEmpty;
+ uint16 code; // Needs to be ASL to index with
+ uint16 inputCode;
+ uint16 finalChar;
+ uint16 myCode; // Silly name is silly
+ uint16 oldCode;
+ uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
+ uint16 evenOdd = 0;
+ uint16 topStack = 0;
+
+ byte outByte;
+
+ setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k for some reason
+ bool carry = true; // This will represent the carry flag so we can make this a clean loop
+
+ code = getInputCode(carry, src, srcLen, evenOdd); // Get the first code
+ if (carry == false) {
+ return -1; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here
+ }
+
+ finalChar = code;
+ oldCode = code;
+ myCode = code;
+
+ outByte = code & 0x00FF;
+ dstW.writeByte(outByte); // Take just the lower byte and write it the output
+
+ //debug("first data write: %02X", outByte);
+
+ // :nextcode
+ while (carry == true) {
+
+ code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
+ if (carry == true) {
+
+ index = code << 1; // Could just write this code*2 probably
+ inputCode = code;
+ myCode = code;
+
+ //debug("%04X, %04X, %04X", index, inputCode, myCode);
+
+ uint16 a = start[index] & 0x0F00;
+ uint16 b = ptk[index] & 0xFF00;
+ if ((a & b) == 0) { // Empty code
+ index = topStack;
+ outByte = finalChar & 0x00FF;
+ stack[index] = outByte;
+ topStack++;
+ myCode = oldCode;
+ }
+
+ //debug("%04X, %04X, %04X, %04X, %02X", index, inputCode, myCode, topStack, outByte);
+
+ //debug("nextsymbol");
+ // :nextsymbol
+ index = myCode << 1;
+ while (index >= 0x200) {
+ myCode = start[index] & k12BitWord;
+ outByte = ptk[index] & 0x00FF;
+ index = topStack;
+ stack[index] = outByte;
+ topStack++;
+ //debug("i: %02X, tB: %02X, mC: %02X, b: %02X", index, topStack, myCode, outByte);
+ index = myCode << 1;
+ }
+
+ //debug("singlechar");
+ // :singlechar
+ finalChar = (myCode >> 1);
+ outByte = finalChar & 0x00FF;
+
+ //debug("second data write: %02X", outByte);
+ dstW.writeByte(outByte);
+
+ //debug("topstack %d", topStack);
+
+ // :dump
+ while (topStack != 0xFFFF) { // Dump the chars on the stack into the output file
+ outByte = stack[topStack] & 0x00FF;
+ //debug("dumping stack %02X", temp);
+ dstW.writeByte(outByte);
+ topStack--;
+ }
+ topStack = 0; // Won't this always be 0 because of the while loop?
+ code = getMember(oldCode, finalChar, findEmpty, start, ptk);
+ oldCode = inputCode;
+ }
+ }
+ dst = dstW.getData();
+ return dstW.size();
+}
+
+void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
+ for (int i = 0x3FFF; i >= 0; i--) {
+ start[i] = 0;
+ ptk[i] = 0;
+ }
+ for (int i=0x0FF; i >=0; i--) {
+ ptk[i] = 0x100;
+ }
+ findEmpty = 0x8000;
+}
+
+int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
+ uint16 k12BitWord = 0x0F9F;
+
+ if (srcLen == 0) {
+ carry = false;
+ return 0;
+ }
+
+ uint16 c;
+ if (evenOdd != 0) { // Odd
+ srcLen--;
+ evenOdd--;
+ c = (src->readUint16LE() >> 3) & 0x00FE; // & #-1-1 ????
+ } else { // Even
+ srcLen -= 2;
+ evenOdd++;
+ c = (src->readUint16LE() & k12BitWord) << 1;
+ src->seek(-1, SEEK_CUR);
+ }
+ return c;
+}
+
+// This function is effectively void, as the return value is only used in compression
+uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
+ // k and codeW are local variables with the value of oldCode and finalChar
+ uint16 k12BitWord = 0x0F9F;
+
+ uint16 hash;
+ hash = (k << 3) ^ k;
+ hash = (hash << 1) ^ codeW;
+ hash <<= 1;
+
+ hash = (hash >= 0x200) ? hash : hash + 0x200;
+
+ uint16 a = start[hash] & 0x0F00;
+ uint16 b = ptk[hash] & 0xFF00;
+ if (a | b) {
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+ return k | 0x100;
+ }
+
+ bool ag = true;
+ while (ag == true) {
+ if ((start[hash] & k12BitWord) == codeW) {
+ if ((ptk[hash] & 0x00FF) == k) {
+ return hash >> 1;
+ }
+ }
+
+ uint16 tmp = start[hash] & 0xF000;
+ if (tmp == 0) {
+ appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
+ ag = false;
+ } else {
+ tmp >>= 4;
+ hash &= 0xFF00; // The 65816 can XBA to flip the bytes in a word, instead we have mask and shift over
+ hash >>= 8;
+ hash = (hash | tmp) << 1;
+ }
+ }
+ return hash;
+}
+
+void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp) {
+ uint16 prev;
+ uint16 link;
+
+ prev = hash;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+ return;
+ }
+
+ bool found = false;
+ while (found == false) {
+ hash -= 2;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+ found = true;
+ }
+ uint16 cond;
+ cond = start[hash] & 0xF000;
+ cond |= ptk[hash];
+ if ( (cond & 0xFF00) == 0) {
+ findEmpty = hash;
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+ link = hash >> 1;
+ tmp = link & 0x00FF; // Another classic XBA situation, although this time it's no less efficient here
+ tmp <<= 8;
+ ptk[prev] = (ptk[prev] & 0x00FF) | tmp;
+ start[prev] = ((link >> 4) & 0xF000) | start[prev]; // Yikes this statement is gross
+ found = true;
+ }
+ }
+}
+
+} // namespace compression
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/compression.h b/engines/immortal/compression.h
new file mode 100644
index 00000000000..8770cb4d430
--- /dev/null
+++ b/engines/immortal/compression.h
@@ -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/>.
+ *
+ */
+
+#ifndef IMMORTAL_COMPRESSION_H
+#define IMMORTAL_COMPRESSION_H
+
+#include "common/file.h"
+#include "common/memstream.h"
+
+namespace Compression {
+
+int unCompress(Common::File *src, int srcLen, byte *out);
+void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
+int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
+uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]);
+void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
+
+} // namespace compression
+
+#endif
\ No newline at end of file
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 684f8af133c..15d7f541677 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -3,7 +3,8 @@ MODULE := engines/immortal
MODULE_OBJS = \
immortal.o \
disk.o \
- metaengine.o
+ metaengine.o \
+ compression.o
# This module can be built as a plugin
ifeq ($(ENABLE_IMMORTAL), DYNAMIC_PLUGIN)
Commit: 3e560a0ca5bc0b9e5de24c27b9251eeca3fbf046
https://github.com/scummvm/scummvm/commit/3e560a0ca5bc0b9e5de24c27b9251eeca3fbf046
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: ProDos -> ProDOS
Changed paths:
engines/immortal/disk.cpp
engines/immortal/disk.h
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index 736c4606557..5979906ca6c 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -22,15 +22,13 @@
#include "common/debug.h"
#include "common/file.h"
-
-#include "immortal/immortal.h"
#include "immortal/disk.h"
namespace Immortal {
-// --- ProDosFile methods ---
+// --- ProDOSFile methods ---
-ProDosFile::ProDosFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk)
+ProDOSFile::ProDOSFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk)
: _type(type)
, _totalBlocks(tBlk)
, _eof(eof)
@@ -41,7 +39,7 @@ ProDosFile::ProDosFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint1
/* For debugging purposes, this prints the meta data of a file */
-void ProDosFile::printInfo() {
+void ProDOSFile::printInfo() {
debug("File: %s", _name);
debug("Type: %02X", _type);
debug("Blocks: %d", _totalBlocks);
@@ -50,7 +48,7 @@ void ProDosFile::printInfo() {
/* For Common::Archive, this method just returns a string of the name */
-Common::String ProDosFile::getName() const {
+Common::String ProDOSFile::getName() const {
return Common::String(_name);
}
@@ -60,7 +58,7 @@ Common::String ProDosFile::getName() const {
* memory starting at memOffset
*/
-void ProDosFile::getDataBlock(byte *memOffset, int offset, int size) const {
+void ProDOSFile::getDataBlock(byte *memOffset, int offset, int size) const {
// All this method needs to do is read (size) of data at (offset) into (memOffset)
_disk->seek(offset);
@@ -73,14 +71,14 @@ void ProDosFile::getDataBlock(byte *memOffset, int offset, int size) const {
* determine the new position within the byte data.
*/
-int ProDosFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
+int ProDOSFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
int dataSize; // For most of the blocks, this will be kBlockSize, but the last one will be the calculated remainder
int readSize = 0; // This keeps track of the new pointer position to read data to, by updating the size of data read last
int dataOffset; // Where in the disk to read from
int diskPos; // Current position of cursor
for (int i = 0; i < blockNum; i++) {
- dataSize = (i == (blockNum - 1)) ? rem : ProDosDisk::kBlockSize;
+ dataSize = (i == (blockNum - 1)) ? rem : ProDOSDisk::kBlockSize;
dataOffset = _disk->readByte(); // Low byte is first
/* The cursor needs to know where to get the next pointer from in the index block,
@@ -90,7 +88,7 @@ int ProDosFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
diskPos = _disk->pos();
_disk->skip(255); // The high bytes are stored at the end of the block instead because reasons???
- dataOffset += (_disk->readByte() << 8) * ProDosDisk::kBlockSize; // High byte is second
+ dataOffset += (_disk->readByte() << 8) * ProDOSDisk::kBlockSize; // High byte is second
getDataBlock(memOffset + readSize, dataOffset, dataSize);
readSize += dataSize;
@@ -108,7 +106,7 @@ int ProDosFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
* returns a read stream of the file contents.
*/
-Common::SeekableReadStream *ProDosFile::createReadStream() const {
+Common::SeekableReadStream *ProDOSFile::createReadStream() const {
// We know the total byte size of the data, so we can allocate the full amount right away
byte *finalData = (byte *)malloc(_eof);
@@ -116,12 +114,12 @@ Common::SeekableReadStream *ProDosFile::createReadStream() const {
/* For a seed, this is a direct pointer to data. For a sapling it is an index file,
* and for a tree it is a master index file.
*/
- int indexBlock = _blockPtr * ProDosDisk::kBlockSize;
+ int indexBlock = _blockPtr * ProDOSDisk::kBlockSize;
/* For a sapling or tree, the size needs to be calculated, as they are made from multiple blocks.
* _totalBlocks *includes* the index block, so the blocks before the oef block are _totalBlocks-2
*/
- int remainder = _eof - ((_totalBlocks - 2) * ProDosDisk::kBlockSize);
+ int remainder = _eof - ((_totalBlocks - 2) * ProDOSDisk::kBlockSize);
// For a seed file, the end of file value is also the size in the block, because it's just the one block
if (_type == kFileTypeSeed) {
@@ -157,7 +155,7 @@ Common::SeekableReadStream *ProDosFile::createReadStream() const {
int diskPos = _disk->pos();
_disk->skip(255);
- indexOffset += (_disk->readByte() << 8) * ProDosDisk::kBlockSize;
+ indexOffset += (_disk->readByte() << 8) * ProDOSDisk::kBlockSize;
_disk->seek(indexOffset);
readSize += parseIndexBlock(finalData + readSize, blockNum, remainder);
@@ -168,27 +166,27 @@ Common::SeekableReadStream *ProDosFile::createReadStream() const {
return new Common::MemoryReadStream(finalData, _eof, DisposeAfterUse::YES);
}
-// --- ProDosDisk methods ---
+// --- ProDOSDisk methods ---
/* The time and date are compressed into 16bit words, so to make them useable
* we have to decompress them by masking the other bits and then shifting
* to the lowest bit so that they can be stored in 8 bits each.
*/
-void ProDosDisk::getDate(Date *d, uint16 date) {
+void ProDOSDisk::getDate(Date *d, uint16 date) {
d->_day = date & 0x001f;
d->_month = (date & 0x01e0) >> 5;
d->_year = (date & 0xfe00) >> 9;
}
-void ProDosDisk::getTime(Time *t, uint16 time) {
+void ProDOSDisk::getTime(Time *t, uint16 time) {
t->_minute = time & 0x003f;
t->_hour = (time & 0x1f00) >> 8;
}
/* Adds most of the header data to a directory header struct */
-void ProDosDisk::getHeader(DirHeader *h) {
+void ProDOSDisk::getHeader(DirHeader *h) {
/* The type and nameLen fields are stored in the same byte,
* so we need to split the byte, and shift the high bits to
@@ -219,7 +217,7 @@ void ProDosDisk::getHeader(DirHeader *h) {
/* Since a subdirectory header is mostly the same a volume header, we will reuse the code where we can */
-void ProDosDisk::getDirectoryHeader(DirHeader *h) {
+void ProDOSDisk::getDirectoryHeader(DirHeader *h) {
getHeader(h);
h->_parentBlockPtr = _disk.readUint16LE();
h->_parentEntryIndex = _disk.readByte();
@@ -230,7 +228,7 @@ void ProDosDisk::getDirectoryHeader(DirHeader *h) {
* is a directory header for the purose of filling it out with the same code
*/
-void ProDosDisk::getVolumeHeader(VolHeader *h) {
+void ProDOSDisk::getVolumeHeader(VolHeader *h) {
getHeader((DirHeader *)h);
h->_bitmapPtr = _disk.readUint16LE();
h->_volBlocks = _disk.readUint16LE();
@@ -239,7 +237,7 @@ void ProDosDisk::getVolumeHeader(VolHeader *h) {
/* Getting a file entry header is very similar to getting a header, but with different data. */
-void ProDosDisk::getFileEntry(FileEntry *f) {
+void ProDOSDisk::getFileEntry(FileEntry *f) {
uint8 tempByte = _disk.readByte();
f->_nameLen = tempByte & 0xf;
f->_type = (tempByte & 0xf0) >> 4;
@@ -274,7 +272,7 @@ void ProDosDisk::getFileEntry(FileEntry *f) {
* which is stored with the file object so that the engine can search by path name.
*/
-void ProDosDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path) {
+void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path) {
int currPos;
int parsedFiles = 0;
@@ -298,9 +296,9 @@ void ProDosDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
Common::String fileName = path + fileEntry._name;
debug("%s", fileName.c_str());
- ProDosFile *currFile = new ProDosFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
+ ProDOSFile *currFile = new ProDOSFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
- _files.setVal(fileName, Common::SharedPtr<ProDosFile>(currFile));
+ _files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
_disk.seek(currPos);
// Otherwise, if it is a subdirectory, we want to explore that subdirectory
@@ -329,7 +327,7 @@ void ProDosDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
* ((total_blocks / 4096) + 1 (if remainder)) * 512 bytes.
*/
-void ProDosDisk::getVolumeBitmap(VolHeader *h) {
+void ProDOSDisk::getVolumeBitmap(VolHeader *h) {
int currPos = _disk.pos();
int bitmapSize;
@@ -347,7 +345,7 @@ void ProDosDisk::getVolumeBitmap(VolHeader *h) {
/* Gets the volume information and parses the filesystem, adding file objects to a map as it goes */
-bool ProDosDisk::open(const Common::String filename) {
+bool ProDOSDisk::open(const Common::String filename) {
debug("opening %s", filename.c_str());
_disk.open(filename);
@@ -372,7 +370,7 @@ bool ProDosDisk::open(const Common::String filename) {
/* Constructor simply calls open(), and if it is successful it prints a statement */
-ProDosDisk::ProDosDisk(const Common::String filename) {
+ProDOSDisk::ProDOSDisk(const Common::String filename) {
if (open(filename)) {
debug ("%s has been loaded", filename.c_str());
}
@@ -380,15 +378,15 @@ ProDosDisk::ProDosDisk(const Common::String filename) {
/* Destructor closes the disk and clears the map of files */
-ProDosDisk::~ProDosDisk() {
+ProDOSDisk::~ProDOSDisk() {
_disk.close();
_files.clear();
- delete _volBitmap;
+ delete _volBitmap; // Should this be free() instead?
}
// --- Common::Archive methods ---
-bool ProDosDisk::hasFile(const Common::Path &path) const {
+bool ProDOSDisk::hasFile(const Common::Path &path) const {
Common::String name = path.toString();
return _files.contains(name);
}
@@ -398,9 +396,9 @@ bool ProDosDisk::hasFile(const Common::Path &path) const {
* pointer returned as the value from the given path. This also returns the size.
*/
-int ProDosDisk::listMembers(Common::ArchiveMemberList &list) const {
+int ProDOSDisk::listMembers(Common::ArchiveMemberList &list) const {
int f = 0;
- Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>>::const_iterator it;
+ Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>>::const_iterator it;
for (it = _files.begin(); it != _files.end(); ++it) {
list.push_back(Common::ArchiveMemberList::value_type(it->_value));
++f;
@@ -408,7 +406,7 @@ int ProDosDisk::listMembers(Common::ArchiveMemberList &list) const {
return f;
}
-const Common::ArchiveMemberPtr ProDosDisk::getMember(const Common::Path &path) const {
+const Common::ArchiveMemberPtr ProDOSDisk::getMember(const Common::Path &path) const {
Common::String name = path.toString();
if (!_files.contains(name)) {
return Common::ArchiveMemberPtr();
@@ -420,12 +418,12 @@ const Common::ArchiveMemberPtr ProDosDisk::getMember(const Common::Path &path) c
* so if this member is not the correct one, we return a null pointer.
*/
-Common::SeekableReadStream *ProDosDisk::createReadStreamForMember(const Common::Path &path) const {
+Common::SeekableReadStream *ProDOSDisk::createReadStreamForMember(const Common::Path &path) const {
Common::String name = path.toString();
if (!_files.contains(name)) {
return nullptr;
}
- Common::SharedPtr<ProDosFile> f = _files.getValOrDefault(name);
+ Common::SharedPtr<ProDOSFile> f = _files.getValOrDefault(name);
f->printInfo();
return f->createReadStream();
}
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index 4f8c1ea3599..8ab903b7b8e 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -19,14 +19,14 @@
*
*/
-#ifndef IMMORTAL_DSK_H
-#define IMMORTAL_DSK_H
+#ifndef IMMORTAL_DISK_H
+#define IMMORTAL_DISK_H
#include "common/memstream.h"
#include "common/file.h"
#include "common/debug.h"
-/* Quick note about ProDos:
+/* Quick note about ProDOS:
* This disk code handles inactive, seedling, sapling, tree, and subdirectory files.
* It does *not* handle sparse files at the moment. If a sparse file exists, it may not
* be read correctly. It also does not do anything with Pascal files, but those should not
@@ -35,7 +35,7 @@
namespace Immortal {
-// These values define for ProDos how to read the file entry, and also whether it's a keyblock (if it is a directory header, it's the keyblock of that directory)
+// These values define for ProDOS how to read the file entry, and also whether it's a keyblock (if it is a directory header, it's the keyblock of that directory)
enum FileType : char {
kFileTypeDead = 0,
kFileTypeSeed = 1,
@@ -47,8 +47,8 @@ enum FileType : char {
kFileTypeVolHead = 0x0F
};
-/* File extensions for all the ProDos supported file types
- * NOTE: ProDos user defined files are F1-F8. If they are ever required,
+/* File extensions for all the ProDOS supported file types
+ * NOTE: ProDOS user defined files are F1-F8. If they are ever required,
* they can be added to this enum.
*/
enum FileExt {
@@ -72,16 +72,16 @@ enum FileExt {
kFileExtPDSys = 0xFF
};
-/* A ProDos file simply contains meta data about the file and the ability to
+/* A ProDOS file simply contains meta data about the file and the ability to
* find and put together the data blocks that make up the file contents.
* This implements Common::ArchiveMember so that it can be used directly in
- * the Archive methods in ProDosDisk.
+ * the Archive methods in ProDOSDisk.
*/
-class ProDosFile : public Common::ArchiveMember {
+class ProDOSFile : public Common::ArchiveMember {
public:
- ProDosFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
- ~ProDosFile() {};
+ ProDOSFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
+ ~ProDOSFile() {};
// These are the Common::ArchiveMember related functions
Common::String getName() const override;
@@ -107,12 +107,12 @@ private:
* file operations to work with it.
*/
-class ProDosDisk : public Common::Archive {
+class ProDOSDisk : public Common::Archive {
public:
- static const int kBlockSize = 512; // A ProDos block is always 512 bytes
+ static const int kBlockSize = 512; // A ProDOS block is always 512 bytes
- ProDosDisk(const Common::String filename);
- ~ProDosDisk();
+ ProDOSDisk(const Common::String filename);
+ ~ProDOSDisk();
// Called from the constructor, it parses the volume and fills the hashmap with files
bool open(const Common::String filename);
@@ -130,7 +130,7 @@ Common::String _name; // Name of volume
Common::File _disk; // The volume file itself
int _volBlocks; // Total blocks in volume
byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
-Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDosFile
+Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDOSFile
struct Date {
uint8 _day;
@@ -153,8 +153,8 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashma
uint8 _ver;
uint8 _minVer; // Should pretty much always be 0 as far as I know
uint8 _access; // If this ends up useful, there should be an enum for the access values
- uint8 _entryLen; // Always 27 in ProDos 1.0
- uint8 _entriesPerBlock; // Always 0D in ProDos 1.0
+ uint8 _entryLen; // Always 27 in ProDOS 1.0
+ uint8 _entriesPerBlock; // Always 0D in ProDOS 1.0
uint16 _fileCount; // Number of files across all data blocks in this directory
uint16 _bitmapPtr; // Block pointer to the keyblock of the bitmap for the entire volume
uint16 _volBlocks; // Blocks in entire volume
@@ -173,9 +173,9 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDosFile>> _files; // Hashma
uint8 _entryLen;
uint8 _entriesPerBlock;
uint16 _fileCount;
- uint16 _parentBlockPtr; // These values allow ProDos to navigate back out of a directory, but they aren't really needed by the class to navigate
+ uint16 _parentBlockPtr; // These values allow ProDOS to navigate back out of a directory, but they aren't really needed by the class to navigate
uint8 _parentEntryIndex; // Index in the current directory
- uint8 _parentEntryLen; // This is always 27 in ProDos 1.0
+ uint8 _parentEntryLen; // This is always 27 in ProDOS 1.0
};
struct FileEntry {
Commit: 05bfec4b60cf34929c5068fc7c17830bf01d0d57
https://github.com/scummvm/scummvm/commit/05bfec4b60cf34929c5068fc7c17830bf01d0d57
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: unCompress() returns memoryReadStream instead of int, and bitmasks have enum
Changed paths:
engines/immortal/compression.cpp
engines/immortal/compression.h
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index 16b24467e6e..9dd9fbb6857 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -24,58 +24,66 @@
#include "common/memstream.h"
#include "immortal/compression.h"
-/* In: Source data as File, size of data, pointer that will be replaced with pointer to the data
- * Out: size of uncompressed data
+/* Decompression:
+ * This decompression algorithm follows the source assembly very closely,
+ * which is itself a modification to LZW (a derivitive of LZ78).
+ * In: Source data as File, size of data
+ * Out: Pointer to uncompressed data as SeekableReadStream
*/
namespace Compression {
-int unCompress(Common::File *src, int srcLen, byte *dst) {
- uint16 k12BitWord = 0x0F9F; // Code is stored in lower 12 bits of word, probably doesn't need to be const
-
- Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::YES);
-
- //debug("lets get started");
+// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
+enum BitMask : uint16 {
+ kMask12Bit = 0x0F9F, // Code (pos, 00, len) is stored in lower 12 bits of word
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000
+};
+Common::SeekableReadStream *unCompress(Common::File *src, int srcLen) {
// If the source data has no length, we certainly do not want to decompress it
if (srcLen == 0) {
- return -1; // Classic C error code
+ return nullptr;
}
- //debug("not null at least");
- // This is the 20k bytes the compression gets allocated to work with for the dictionary and the stack of chars
- uint16 start[0x4000]; // Rename! <- I did? Probably still needs a better name though
- uint16 ptk[0x4000]; // Pointer to keys?
- byte stack[0x4000]; // Stack of chars to be stored
+ /* This will be the dynamically re-allocated writeable memory stream.
+ * We do not want it to be deleted from scope, as this location is where
+ * the readstream being returned will point to.
+ */
+ Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::NO);
+
+ // The 20k bytes of memory that compression gets allocated to work with for the dictionary and the stack of chars
+ uint16 start[0x4000]; // Really needs a better name, remember to do this future me
+ uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
+ byte stack[0x4000]; // Stack of chars to be stored
- // These are the main variables we'll need for this. They were basically scratch ram in the original
+ // These are the main variables we'll need for this
uint16 findEmpty;
- uint16 code; // Needs to be ASL to index with
+ uint16 code; // Needs to be ASL to index with
uint16 inputCode;
uint16 finalChar;
- uint16 myCode; // Silly name is silly
+ uint16 myCode; // Silly name is silly
uint16 oldCode;
- uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
- uint16 evenOdd = 0;
+ uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
+ uint16 evenOdd = 0;
uint16 topStack = 0;
- byte outByte;
+ byte outByte; // If only we could SEP #$20 like the 65816
- setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k for some reason
- bool carry = true; // This will represent the carry flag so we can make this a clean loop
+ setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k
+ bool carry = true; // This will represent the carry flag so we can make this a clean loop
code = getInputCode(carry, src, srcLen, evenOdd); // Get the first code
if (carry == false) {
- return -1; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here
+ return nullptr; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here so we might as well
}
finalChar = code;
oldCode = code;
myCode = code;
- outByte = code & 0x00FF;
- dstW.writeByte(outByte); // Take just the lower byte and write it the output
-
- //debug("first data write: %02X", outByte);
+ outByte = code & kMaskLow;
+ dstW.writeByte(outByte); // Take just the lower byte and write it the output
// :nextcode
while (carry == true) {
@@ -83,100 +91,96 @@ int unCompress(Common::File *src, int srcLen, byte *dst) {
code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
if (carry == true) {
- index = code << 1; // Could just write this code*2 probably
+ index = code << 1;
inputCode = code;
- myCode = code;
-
- //debug("%04X, %04X, %04X", index, inputCode, myCode);
+ myCode = code;
uint16 a = start[index] & 0x0F00;
- uint16 b = ptk[index] & 0xFF00;
- if ((a & b) == 0) { // Empty code
- index = topStack;
- outByte = finalChar & 0x00FF;
+ uint16 b = ptk[index] & kMaskHigh;
+ if ((a & b) == 0) { // Empty code
+ index = topStack;
+ outByte = finalChar & kMaskLow;
stack[index] = outByte;
topStack++;
myCode = oldCode;
}
- //debug("%04X, %04X, %04X, %04X, %02X", index, inputCode, myCode, topStack, outByte);
-
- //debug("nextsymbol");
// :nextsymbol
index = myCode << 1;
while (index >= 0x200) {
- myCode = start[index] & k12BitWord;
- outByte = ptk[index] & 0x00FF;
- index = topStack;
+ myCode = start[index] & kMask12Bit;
+ outByte = ptk[index] & kMaskLow;
+ index = topStack;
stack[index] = outByte;
topStack++;
- //debug("i: %02X, tB: %02X, mC: %02X, b: %02X", index, topStack, myCode, outByte);
index = myCode << 1;
}
- //debug("singlechar");
// :singlechar
finalChar = (myCode >> 1);
- outByte = finalChar & 0x00FF;
-
- //debug("second data write: %02X", outByte);
+ outByte = finalChar & kMaskLow;
dstW.writeByte(outByte);
- //debug("topstack %d", topStack);
-
// :dump
while (topStack != 0xFFFF) { // Dump the chars on the stack into the output file
- outByte = stack[topStack] & 0x00FF;
- //debug("dumping stack %02X", temp);
+ outByte = stack[topStack] & kMaskLow;
dstW.writeByte(outByte);
topStack--;
}
- topStack = 0; // Won't this always be 0 because of the while loop?
- code = getMember(oldCode, finalChar, findEmpty, start, ptk);
- oldCode = inputCode;
+
+ topStack = 0; // Won't this always be 0 because of the while loop?
+ code = getMember(oldCode, finalChar, findEmpty, start, ptk);
+ oldCode = inputCode;
}
+
}
- dst = dstW.getData();
- return dstW.size();
+
+ // Return a readstream with a pointer to the data in the write stream.
+ // This one we do want to dispose after using, because it will be in the scope of the engine itself
+ return new Common::MemoryReadStream(dstW.getData(), dstW.size(), DisposeAfterUse::YES);
}
void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
+ // Clear the whole dictionary
for (int i = 0x3FFF; i >= 0; i--) {
start[i] = 0;
- ptk[i] = 0;
+ ptk[i] = 0;
}
- for (int i=0x0FF; i >=0; i--) {
- ptk[i] = 0x100;
+
+ // Set the initial 256 bytes to be value 256, these are the characters without extensions
+ for (int i = 0x0FF; i >= 0; i--) {
+ ptk[i] = 0x100;
}
+
+ // This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
findEmpty = 0x8000;
}
int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
- uint16 k12BitWord = 0x0F9F;
-
+ // Yes, lets check this despite having already checked it before we got here...
if (srcLen == 0) {
carry = false;
return 0;
}
uint16 c;
- if (evenOdd != 0) { // Odd
+ if (evenOdd != 0) { // Odd
srcLen--;
evenOdd--;
- c = (src->readUint16LE() >> 3) & 0x00FE; // & #-1-1 ????
- } else { // Even
+ c = (src->readUint16LE() >> 3) & 0x00FE; // & #-1-1
+ } else { // Even
srcLen -= 2;
evenOdd++;
- c = (src->readUint16LE() & k12BitWord) << 1;
+ c = (src->readUint16LE() & kMask12Bit) << 1;
src->seek(-1, SEEK_CUR);
}
return c;
}
-// This function is effectively void, as the return value is only used in compression
uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
+ // This function is effectively void, as the return value is only used in compression
+
// k and codeW are local variables with the value of oldCode and finalChar
- uint16 k12BitWord = 0x0F9F;
uint16 hash;
hash = (k << 3) ^ k;
@@ -186,28 +190,32 @@ uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint
hash = (hash >= 0x200) ? hash : hash + 0x200;
uint16 a = start[hash] & 0x0F00;
- uint16 b = ptk[hash] & 0xFF00;
+ uint16 b = ptk[hash] & kMaskHigh;
if (a | b) {
start[hash] = codeW;
- ptk[hash] = k | 0x100;
- return k | 0x100;
+ ptk[hash] = k | 0x100;
+ return ptk[hash];
}
+ // This loop is a bit wacky, due to the way the jumps were stuctured in the source
bool ag = true;
while (ag == true) {
- if ((start[hash] & k12BitWord) == codeW) {
- if ((ptk[hash] & 0x00FF) == k) {
+ if ((start[hash] & kMask12Bit) == codeW) {
+ if ((ptk[hash] & kMaskLow) == k) {
return hash >> 1;
}
}
- uint16 tmp = start[hash] & 0xF000;
+ uint16 tmp = start[hash] & kMaskLast;
if (tmp == 0) {
+
+ // I've separated this into it's own function for the sake of this loop being readable
appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
ag = false;
+
} else {
tmp >>= 4;
- hash &= 0xFF00; // The 65816 can XBA to flip the bytes in a word, instead we have mask and shift over
+ hash &= kMaskHigh; // The 65816 can XBA to flip the bytes in a word, instead we have mask and shift over :(
hash >>= 8;
hash = (hash | tmp) << 1;
}
@@ -222,29 +230,34 @@ void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16
prev = hash;
if (hash >= 0x200) {
setupDictionary(start, ptk, findEmpty);
- return;
- }
+
+ } else {
+ bool found = false;
+ while (found == false) {
+ hash -= 2;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+ found = true;
+ }
- bool found = false;
- while (found == false) {
- hash -= 2;
- if (hash >= 0x200) {
- setupDictionary(start, ptk, findEmpty);
- found = true;
- }
- uint16 cond;
- cond = start[hash] & 0xF000;
- cond |= ptk[hash];
- if ( (cond & 0xFF00) == 0) {
- findEmpty = hash;
- start[hash] = codeW;
- ptk[hash] = k | 0x100;
- link = hash >> 1;
- tmp = link & 0x00FF; // Another classic XBA situation, although this time it's no less efficient here
- tmp <<= 8;
- ptk[prev] = (ptk[prev] & 0x00FF) | tmp;
- start[prev] = ((link >> 4) & 0xF000) | start[prev]; // Yikes this statement is gross
- found = true;
+ // Split up the conditional statement to be easier to follow
+ uint16 cond;
+ cond = start[hash] & kMaskLast;
+ cond |= ptk[hash];
+
+ if ((cond & kMaskHigh) == 0) {
+ findEmpty = hash;
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+
+ link = hash >> 1;
+ tmp = link & kMaskLow; // Another classic XBA situation, although it's nicer this time
+ tmp <<= 8; // Because we can just grab the low bytes and shift them forward (it's still much faster to XBA though)
+
+ ptk[prev] = (ptk[prev] & kMaskLow) | tmp;
+ start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
+ found = true;
+ }
}
}
}
diff --git a/engines/immortal/compression.h b/engines/immortal/compression.h
index 8770cb4d430..96633758e0b 100644
--- a/engines/immortal/compression.h
+++ b/engines/immortal/compression.h
@@ -27,7 +27,8 @@
namespace Compression {
-int unCompress(Common::File *src, int srcLen, byte *out);
+// Only unCompress() is called from outside Compression, the others are subroutines.
+Common::SeekableReadStream *unCompress(Common::File *src, int srcLen);
void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]);
Commit: df41580e058580c64b04d77277efb1ce136f6138
https://github.com/scummvm/scummvm/commit/df41580e058580c64b04d77277efb1ce136f6138
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Fix file data offset calculation error in sapling and tree files
Changed paths:
engines/immortal/disk.cpp
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index 5979906ca6c..49c940182be 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -88,7 +88,7 @@ int ProDOSFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
diskPos = _disk->pos();
_disk->skip(255); // The high bytes are stored at the end of the block instead because reasons???
- dataOffset += (_disk->readByte() << 8) * ProDOSDisk::kBlockSize; // High byte is second
+ dataOffset = (dataOffset + (_disk->readByte() << 8)) * ProDOSDisk::kBlockSize; // High byte is second
getDataBlock(memOffset + readSize, dataOffset, dataSize);
readSize += dataSize;
@@ -155,7 +155,7 @@ Common::SeekableReadStream *ProDOSFile::createReadStream() const {
int diskPos = _disk->pos();
_disk->skip(255);
- indexOffset += (_disk->readByte() << 8) * ProDOSDisk::kBlockSize;
+ indexOffset = (indexOffset + (_disk->readByte() << 8)) * ProDOSDisk::kBlockSize;
_disk->seek(indexOffset);
readSize += parseIndexBlock(finalData + readSize, blockNum, remainder);
Commit: 3da314ad88ca59f3e13dd68cf489373327b342c9
https://github.com/scummvm/scummvm/commit/3da314ad88ca59f3e13dd68cf489373327b342c9
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Fix incorrect conditional in primary while loop of compression.cpp
Changed paths:
engines/immortal/compression.cpp
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index 9dd9fbb6857..03cd608c68c 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -34,232 +34,241 @@ namespace Compression {
// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
enum BitMask : uint16 {
- kMask12Bit = 0x0F9F, // Code (pos, 00, len) is stored in lower 12 bits of word
+ kMask12Bit = 0x0F9F, // Code (pos, 00, len) is stored in lower 12 bits of word
kMaskLow = 0x00FF,
kMaskHigh = 0xFF00,
kMaskLast = 0xF000
};
-Common::SeekableReadStream *unCompress(Common::File *src, int srcLen) {
- // If the source data has no length, we certainly do not want to decompress it
- if (srcLen == 0) {
- return nullptr;
- }
+Common::SeekableReadStream *unCompress(Common::File *src, int srcLen) {
+ /* Note: this function does not seek() in the file, which means
+ * that if there is a header on the data, the expectation is that
+ * seek() was already used to move past the header before this function.
+ */
- /* This will be the dynamically re-allocated writeable memory stream.
- * We do not want it to be deleted from scope, as this location is where
- * the readstream being returned will point to.
- */
- Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::NO);
+ // If the source data has no length, we certainly do not want to decompress it
+ if (srcLen == 0) {
+ return nullptr;
+ }
- // The 20k bytes of memory that compression gets allocated to work with for the dictionary and the stack of chars
- uint16 start[0x4000]; // Really needs a better name, remember to do this future me
- uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
- byte stack[0x4000]; // Stack of chars to be stored
+ /* This will be the dynamically re-allocated writeable memory stream.
+ * We do not want it to be deleted from scope, as this location is where
+ * the readstream being returned will point to.
+ */
+ Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::NO);
+
+ // The 20k bytes of memory that compression gets allocated to work with for the dictionary and the stack of chars
+ uint16 start[0x4000]; // Really needs a better name, remember to do this future me
+ uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
+ byte stack[0x4000]; // Stack of chars to be stored
// These are the main variables we'll need for this
uint16 findEmpty;
- uint16 code; // Needs to be ASL to index with
+ uint16 code; // Needs to be ASL to index with
uint16 inputCode;
uint16 finalChar;
- uint16 myCode; // Silly name is silly
+ uint16 myCode; // Silly name is silly
uint16 oldCode;
- uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
- uint16 evenOdd = 0;
- uint16 topStack = 0;
-
- byte outByte; // If only we could SEP #$20 like the 65816
-
- setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k
- bool carry = true; // This will represent the carry flag so we can make this a clean loop
-
- code = getInputCode(carry, src, srcLen, evenOdd); // Get the first code
- if (carry == false) {
- return nullptr; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here so we might as well
- }
-
- finalChar = code;
- oldCode = code;
- myCode = code;
-
- outByte = code & kMaskLow;
- dstW.writeByte(outByte); // Take just the lower byte and write it the output
-
- // :nextcode
- while (carry == true) {
-
- code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
- if (carry == true) {
-
- index = code << 1;
- inputCode = code;
- myCode = code;
-
- uint16 a = start[index] & 0x0F00;
- uint16 b = ptk[index] & kMaskHigh;
- if ((a & b) == 0) { // Empty code
- index = topStack;
- outByte = finalChar & kMaskLow;
- stack[index] = outByte;
- topStack++;
- myCode = oldCode;
- }
-
- // :nextsymbol
- index = myCode << 1;
- while (index >= 0x200) {
- myCode = start[index] & kMask12Bit;
- outByte = ptk[index] & kMaskLow;
- index = topStack;
- stack[index] = outByte;
- topStack++;
- index = myCode << 1;
- }
-
- // :singlechar
- finalChar = (myCode >> 1);
- outByte = finalChar & kMaskLow;
- dstW.writeByte(outByte);
-
- // :dump
- while (topStack != 0xFFFF) { // Dump the chars on the stack into the output file
- outByte = stack[topStack] & kMaskLow;
- dstW.writeByte(outByte);
- topStack--;
- }
-
- topStack = 0; // Won't this always be 0 because of the while loop?
- code = getMember(oldCode, finalChar, findEmpty, start, ptk);
- oldCode = inputCode;
- }
-
- }
-
- // Return a readstream with a pointer to the data in the write stream.
- // This one we do want to dispose after using, because it will be in the scope of the engine itself
- return new Common::MemoryReadStream(dstW.getData(), dstW.size(), DisposeAfterUse::YES);
+ uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
+ uint16 evenOdd = 0;
+ uint16 topStack = 0;
+
+ byte outByte; // If only we could SEP #$20 like the 65816
+
+ setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k
+ bool carry = true; // This will represent the carry flag so we can make this a clean loop
+
+ code = getInputCode(carry, src, srcLen, evenOdd); // Get the first code
+ if (carry == false) {
+ return nullptr; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here so we might as well
+ }
+
+ finalChar = code;
+ oldCode = code;
+ myCode = code;
+
+ outByte = code & kMaskLow;
+ dstW.writeByte(outByte); // Take just the lower byte and write it the output
+
+ // :nextcode
+ while (carry == true) {
+
+ code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
+ if (carry == true) {
+
+ index = code << 1;
+ inputCode = code;
+ myCode = code;
+
+ // Split up the conditional statement to be easier to follow
+ uint16 cond;
+ cond = start[index] & kMaskLast;
+ cond |= ptk[index];
+
+ if ((cond & kMaskHigh) == 0) { // Empty code
+ index = topStack;
+ outByte = finalChar & kMaskLow;
+ stack[index] = outByte;
+ topStack++;
+ myCode = oldCode;
+ }
+
+ // :nextsymbol
+ index = myCode << 1;
+ while (index >= 0x200) {
+ myCode = start[index] & kMask12Bit;
+ outByte = ptk[index] & kMaskLow;
+ index = topStack;
+ stack[index] = outByte;
+ topStack++;
+ index = myCode << 1;
+ }
+
+ // :singlechar
+ finalChar = (myCode >> 1);
+ outByte = finalChar & kMaskLow;
+ dstW.writeByte(outByte);
+
+ // :dump
+ while (topStack != 0xFFFF) { // Dump the chars on the stack into the output file
+ outByte = stack[topStack] & kMaskLow;
+ dstW.writeByte(outByte);
+ topStack--;
+ }
+
+ topStack = 0; // Won't this always be 0 because of the while loop?
+ code = getMember(oldCode, finalChar, findEmpty, start, ptk);
+ oldCode = inputCode;
+ }
+
+ }
+
+ // Return a readstream with a pointer to the data in the write stream.
+ // This one we do want to dispose after using, because it will be in the scope of the engine itself
+ return new Common::MemoryReadStream(dstW.getData(), dstW.size(), DisposeAfterUse::YES);
}
void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
- // Clear the whole dictionary
- for (int i = 0x3FFF; i >= 0; i--) {
- start[i] = 0;
- ptk[i] = 0;
- }
-
- // Set the initial 256 bytes to be value 256, these are the characters without extensions
- for (int i = 0x0FF; i >= 0; i--) {
- ptk[i] = 0x100;
- }
-
- // This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
- findEmpty = 0x8000;
+ // Clear the whole dictionary
+ for (int i = 0x3FFF; i >= 0; i--) {
+ start[i] = 0;
+ ptk[i] = 0;
+ }
+
+ // Set the initial 256 bytes to be value 256, these are the characters without extensions
+ for (int i = 0x0FF; i >= 0; i--) {
+ ptk[i] = 0x100;
+ }
+
+ // This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
+ findEmpty = 0x8000;
}
int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
- // Yes, lets check this despite having already checked it before we got here...
- if (srcLen == 0) {
- carry = false;
- return 0;
- }
-
- uint16 c;
- if (evenOdd != 0) { // Odd
- srcLen--;
- evenOdd--;
- c = (src->readUint16LE() >> 3) & 0x00FE; // & #-1-1
- } else { // Even
- srcLen -= 2;
- evenOdd++;
- c = (src->readUint16LE() & kMask12Bit) << 1;
- src->seek(-1, SEEK_CUR);
- }
- return c;
+ // Check if we're at the end of the file
+ if (srcLen == 0) {
+ carry = false;
+ return 0;
+ }
+
+ uint16 c;
+ if (evenOdd != 0) { // Odd
+ srcLen--;
+ evenOdd--;
+ c = (src->readUint16BE() >> 3) & 0x00FE; // & #-1-1
+ } else { // Even
+ srcLen -= 2;
+ evenOdd++;
+ c = (src->readUint16BE() & kMask12Bit) << 1;
+ src->seek(-1, SEEK_CUR);
+ }
+ return c;
}
uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
- // This function is effectively void, as the return value is only used in compression
-
- // k and codeW are local variables with the value of oldCode and finalChar
-
- uint16 hash;
- hash = (k << 3) ^ k;
- hash = (hash << 1) ^ codeW;
- hash <<= 1;
-
- hash = (hash >= 0x200) ? hash : hash + 0x200;
-
- uint16 a = start[hash] & 0x0F00;
- uint16 b = ptk[hash] & kMaskHigh;
- if (a | b) {
- start[hash] = codeW;
- ptk[hash] = k | 0x100;
- return ptk[hash];
- }
-
- // This loop is a bit wacky, due to the way the jumps were stuctured in the source
- bool ag = true;
- while (ag == true) {
- if ((start[hash] & kMask12Bit) == codeW) {
- if ((ptk[hash] & kMaskLow) == k) {
- return hash >> 1;
- }
- }
-
- uint16 tmp = start[hash] & kMaskLast;
- if (tmp == 0) {
-
- // I've separated this into it's own function for the sake of this loop being readable
- appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
- ag = false;
-
- } else {
- tmp >>= 4;
- hash &= kMaskHigh; // The 65816 can XBA to flip the bytes in a word, instead we have mask and shift over :(
- hash >>= 8;
- hash = (hash | tmp) << 1;
- }
- }
- return hash;
+ // This function is effectively void, as the return value is only used in compression
+
+ // k and codeW are local variables with the value of oldCode and finalChar
+
+ uint16 hash;
+ hash = (k << 3) ^ k;
+ hash = (hash << 1) ^ codeW;
+ hash <<= 1;
+
+ hash = (hash >= 0x200) ? hash : hash + 0x200;
+
+ uint16 a = start[hash] & 0x0F00;
+ uint16 b = ptk[hash] & kMaskHigh;
+ if (a | b) {
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+ return ptk[hash];
+ }
+
+ // This loop is a bit wacky, due to the way the jumps were stuctured in the source
+ bool ag = true;
+ uint16 tmp;
+ while (ag == true) {
+ if ((start[hash] & kMask12Bit) == codeW) {
+ if ((ptk[hash] & kMaskLow) == k) {
+ return hash >> 1;
+ }
+ }
+
+ tmp = start[hash] & kMaskLast;
+ if (tmp == 0) {
+
+ // I've separated this into it's own function for the sake of this loop being readable
+ appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
+ ag = false;
+
+ } else {
+ tmp >>= 4;
+ hash = ptk[hash] & kMaskHigh; // The 65816 can XBA to flip the bytes in a word, instead we have mask and shift over :(
+ hash >>= 8;
+ hash = (hash | tmp) << 1;
+ }
+ }
+ return hash;
}
void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp) {
- uint16 prev;
- uint16 link;
-
- prev = hash;
- if (hash >= 0x200) {
- setupDictionary(start, ptk, findEmpty);
-
- } else {
- bool found = false;
- while (found == false) {
- hash -= 2;
- if (hash >= 0x200) {
- setupDictionary(start, ptk, findEmpty);
- found = true;
- }
-
- // Split up the conditional statement to be easier to follow
- uint16 cond;
- cond = start[hash] & kMaskLast;
- cond |= ptk[hash];
-
- if ((cond & kMaskHigh) == 0) {
- findEmpty = hash;
- start[hash] = codeW;
- ptk[hash] = k | 0x100;
-
- link = hash >> 1;
- tmp = link & kMaskLow; // Another classic XBA situation, although it's nicer this time
- tmp <<= 8; // Because we can just grab the low bytes and shift them forward (it's still much faster to XBA though)
-
- ptk[prev] = (ptk[prev] & kMaskLow) | tmp;
- start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
- found = true;
- }
- }
- }
+ uint16 prev;
+ uint16 link;
+
+ prev = hash;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+
+ } else {
+ bool found = false;
+ while (found == false) {
+ hash -= 2;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+ found = true;
+ }
+
+ // Split up the conditional statement to be easier to follow
+ uint16 cond;
+ cond = start[hash] & kMaskLast;
+ cond |= ptk[hash];
+
+ if ((cond & kMaskHigh) == 0) {
+ findEmpty = hash;
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+
+ link = hash >> 1;
+ tmp = link & kMaskLow; // Another classic XBA situation, although it's nicer this time
+ tmp <<= 8; // Because we can just grab the low bytes and shift them forward (it's still much faster to XBA though)
+
+ ptk[prev] = (ptk[prev] & kMaskLow) | tmp;
+ start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
+ found = true;
+ }
+ }
+ }
}
} // namespace compression
Commit: 0c9b09a41d37ae8df342f0ce84a2eb282845ed88
https://github.com/scummvm/scummvm/commit/0c9b09a41d37ae8df342f0ce84a2eb282845ed88
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Better comments and additional extension enum entry
Changed paths:
engines/immortal/disk.cpp
engines/immortal/disk.h
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index 49c940182be..eb0e82b3991 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -42,6 +42,7 @@ ProDOSFile::ProDOSFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint1
void ProDOSFile::printInfo() {
debug("File: %s", _name);
debug("Type: %02X", _type);
+ debug("data: %d", _blockPtr);
debug("Blocks: %d", _totalBlocks);
debug("Size: %u\n", _eof);
}
@@ -288,14 +289,14 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
FileEntry fileEntry;
getFileEntry(&fileEntry);
-
+ //debug("%s", fileEntry._name);
parsedFiles++;
currPos = _disk.pos();
// It is a regular file if (dead < file type < pascal) and the file has a size
if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
- Common::String fileName = path + fileEntry._name;
- debug("%s", fileName.c_str());
+ Common::String fileName = path + fileEntry._name;
+ debug("%s, %08X", fileName.c_str(), fileEntry._eof);
ProDOSFile *currFile = new ProDOSFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
_files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
@@ -358,7 +359,6 @@ bool ProDOSDisk::open(const Common::String filename) {
VolHeader header;
getVolumeHeader(&header);
debug("volume name: %s", header._name);
- //debug("volume created %d/%d/19%d", header._date._day, header._date._month, header._date._year);
getVolumeBitmap(&header);
@@ -381,11 +381,12 @@ ProDOSDisk::ProDOSDisk(const Common::String filename) {
ProDOSDisk::~ProDOSDisk() {
_disk.close();
_files.clear();
- delete _volBitmap; // Should this be free() instead?
+ free(_volBitmap); // Should this be free() or delete?
}
// --- Common::Archive methods ---
+// Very simple, just checks if the dictionary contains the path name
bool ProDOSDisk::hasFile(const Common::Path &path) const {
Common::String name = path.toString();
return _files.contains(name);
@@ -406,6 +407,7 @@ int ProDOSDisk::listMembers(Common::ArchiveMemberList &list) const {
return f;
}
+// If the dictionary contains the path name (could probably call hasFile() instead), get the object
const Common::ArchiveMemberPtr ProDOSDisk::getMember(const Common::Path &path) const {
Common::String name = path.toString();
if (!_files.contains(name)) {
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index 8ab903b7b8e..a6045fc4a68 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -25,6 +25,7 @@
#include "common/memstream.h"
#include "common/file.h"
#include "common/debug.h"
+#include "common/error.h"
/* Quick note about ProDOS:
* This disk code handles inactive, seedling, sapling, tree, and subdirectory files.
@@ -61,6 +62,7 @@ enum FileExt {
kFileExtDB = 0x19,
kFileExtWord = 0x1A,
kFileExtSpread = 0x1B,
+ kFileExtSTART = 0xB3,
kFileExtPascal = 0xEF,
kFileExtPDCI = 0xF0,
kFileExtPDRes = 0xF9,
@@ -69,7 +71,7 @@ enum FileExt {
kFileExtAPSProg = 0xFC,
kFileExtAPSVar = 0xFD,
kFileExtEDASM = 0xFE,
- kFileExtPDSys = 0xFF
+ kFileExtSYS = 0xFF
};
/* A ProDOS file simply contains meta data about the file and the ability to
@@ -83,22 +85,22 @@ public:
ProDOSFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
~ProDOSFile() {};
- // These are the Common::ArchiveMember related functions
- Common::String getName() const override;
- Common::SeekableReadStream *createReadStream() const override;
- void getDataBlock(byte *memOffset, int offset, int size) const;
- int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const;
+ // -- These are the Common::ArchiveMember related functions --
+ Common::String getName() const override; // Returns _name
+ Common::SeekableReadStream *createReadStream() const override; // This is what the archive needs to create a file
+ void getDataBlock(byte *memOffset, int offset, int size) const; // Gets data up to the size of a single data block (512 bytes)
+ int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const; // Uses getDataBlock() on every pointer in the index file, adding them to byte * memory block
- // Mostly for debugging purposes, just prints the metadata
+ // For debugging purposes, just prints the metadata
void printInfo();
private:
char _name[16];
- uint8 _type;
- uint16 _blockPtr;
+ uint8 _type; // As defined by enum FileType
+ uint16 _blockPtr; // Block index in volume of index block or data
uint16 _totalBlocks;
- uint32 _eof;
- Common::File *_disk;
+ uint32 _eof; // End Of File, used generally as size (exception being sparse files)
+ Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
};
/* This class defines the entire disk volume. Upon using the open() method,
@@ -109,10 +111,10 @@ private:
class ProDOSDisk : public Common::Archive {
public:
- static const int kBlockSize = 512; // A ProDOS block is always 512 bytes
+ static const int kBlockSize = 512; // A ProDOS block is always 512 bytes (should this be an enum?)
ProDOSDisk(const Common::String filename);
- ~ProDOSDisk();
+ ~ProDOSDisk(); // Frees the memory used in the dictionary and the volume bitmap
// Called from the constructor, it parses the volume and fills the hashmap with files
bool open(const Common::String filename);
@@ -124,8 +126,8 @@ public:
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
- byte _loader1[512]; // There's no reason these would be needed, but why not include them just in case
- byte _loader2[512];
+ byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
+ byte _loader2[kBlockSize];
Common::String _name; // Name of volume
Common::File _disk; // The volume file itself
int _volBlocks; // Total blocks in volume
Commit: 432ecfb1cf9fd2ab4d2bb8f9d0f0e9c3dfedc746
https://github.com/scummvm/scummvm/commit/432ecfb1cf9fd2ab4d2bb8f9d0f0e9c3dfedc746
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Preliminary translation of game loop from kernal.gs, driver.gs, and logic.gs
Changed paths:
A engines/immortal/kernal.cpp
A engines/immortal/logic.cpp
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/module.mk
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index ffa04323292..ba4f80d183d 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -22,6 +22,7 @@
#include "immortal/immortal.h"
#include "immortal/detection.h"
#include "immortal/disk.h"
+#include "immortal/compression.h"
#include "common/scummsys.h"
#include "common/config-manager.h"
@@ -36,9 +37,6 @@
#include "engines/util.h"
#include "audio/mixer.h"
-#include "graphics/palette.h"
-#include "graphics/surface.h"
-
namespace Immortal {
ImmortalEngine *g_engine;
@@ -49,80 +47,170 @@ ImmortalEngine::ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc)
, _randomSource("Immortal") {
g_engine = this;
+ // Add the game folder to the search manager path variable
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "game");
+ // Confirm that the engine was created
debug("ImmortalEngine::ImmortalEngine");
}
ImmortalEngine::~ImmortalEngine() {
+ _window.close();
+ _font.close();
+
+ // Confirm that the engine was destroyed
debug("ImmortalEngine::~ImmortalEngine");
}
+// --- Functions to make things a little more simple ---
+
+uint16 ImmortalEngine::xba(uint16 ab) {
+ /* XBA in 65816 swaps the low and high bits of a given word in A.
+ * This is used very frequently, so this function just makes
+ * initial translation a little more straightforward. Eventually,
+ * logic should be refactored to not require this.
+ */
+ return ((ab & kMaskLow) << 8) + ((ab & kMaskHigh) >> 8);
+}
+
+uint16 ImmortalEngine::rol(uint16 ab, int n) {
+ /* Oops, another opcode that doesn't have a nice translation.
+ * This just replicates bit rotation because apparently C
+ * doesn't have this natively??? This assumes a 16 bit
+ * unsigned int because that's all we need for the 65816.
+ */
+ return (ab << n) | (ab >> (16 - n));
+}
+
+uint16 ImmortalEngine::ror(uint16 ab, int n) {
+ /* The way this works is very straightforward. We start by
+ * performing the bit shift like normal:
+ * 0001 -> 0000
+ * Then we need an easy way to apply the bit whether it fell
+ * off the end or not, so we bit shift the opposite direction
+ * for the length of the word. If the bit wouldn't have
+ * fallen off, then it applies a 0, but if it would have,
+ * then we get a 1 on the opposite bit, just like the carry.
+ */
+ return (ab >> n) | (ab << (16 - n));
+}
+
+uint16 ImmortalEngine::mult16(uint16 a, uint16 b) {
+ /* We aren't using the game's multiplication function (mult16), but we do want
+ * to retain the ability to drop the second word, without doing (uint16) every time
+ */
+ return (uint16) (a * b);
+}
+// -----------------------------------------------------
+
uint32 ImmortalEngine::getFeatures() const {
+ // No specific features currently
return _gameDescription->flags;
}
Common::String ImmortalEngine::getGameId() const {
+ // No game ID currently
return _gameDescription->gameId;
}
-Common::Error ImmortalEngine::run() {
- initGraphics(320, 200);
-
+Common::ErrorCode ImmortalEngine::initDisks() {
+ // Check for the boot disk
if (SearchMan.hasFile("IMMORTAL.dsk")) {
- ProDosDisk *diskBoot = new ProDosDisk("IMMORTAL.dsk");
+
+ // Instantiate the disk as an object. The disk will then open and parse itself
+ ProDOSDisk *diskBoot = new ProDOSDisk("IMMORTAL.dsk");
if (diskBoot) {
+
+ // With the disk successfully parsed, it can be added to the search manager
debug("Boot disk found");
SearchMan.add("IMMORTAL.dsk", diskBoot, 0, true);
}
+ } else {
+ debug("Please insert Boot disk...");
+ return Common::kPathDoesNotExist;
}
+ // Check for the gfx disk
if (SearchMan.hasFile("IMMORTAL_GFX.dsk")) {
- ProDosDisk *diskGFX = new ProDosDisk("IMMORTAL_GFX.dsk");
+ ProDOSDisk *diskGFX = new ProDOSDisk("IMMORTAL_GFX.dsk");
if (diskGFX) {
debug("Gfx disk found");
SearchMan.add("IMMORTAL_GFX.dsk", diskGFX, 0, true);
}
+ } else {
+ debug("Please insert GFX disk...");
+ return Common::kPathDoesNotExist;
}
- Common::File f;
- if (!f.open("LOAD.OBJ")) {
- debug("oh no :(");
- }
+ // All good, return with no error
+ return Common::kNoError;
+}
- debug("first file loaded");
- f.close();
+Common::Error ImmortalEngine::run() {
+ initGraphics(_resH, _resV);
- Common::File f2;
- if (!f2.open("GOBLIN.SPR")) {
- debug("oh no :((");
- }
+ _mainSurface = new Graphics::Surface();
+ _mainSurface->create(_resH, _resV, Graphics::PixelFormat::createFormatCLUT8());
+
+ _screenBuff = new byte[_screenSize];
- debug("second file loaded");
- f2.close();
+ if (initDisks() != Common::kNoError) {
+ debug("Some kind of disc error!");
+ return Common::kPathDoesNotExist;
+ }
+ //Main:
+ _zero = 0;
+ _dim = 0;
+ _usingNormal = 0;
+ _draw = 1;
+
+ loadPalette(); // We need to grab the palette from the disk first
+ useNormal(); // The first palette will be the default
+ loadFont(); // Load the font sprite
+ loadWindow(); // Load the window background
+ loadSingles("Song A"); // Music
+ loadSprites(); // Get all the sprite data into memory
+ // playing = kPlayingNothing;
+ // themepaused = 0;
+ clearSprites(); // Clear the sprites before we start
+ logicInit(); // Init the game logic
while (!shouldQuit()) {
-
+
+ /* The game loop runs at 60fps, which is 16 milliseconds per frame.
+ * This loop keeps that time by getting the time in milliseconds at the start of the loop,
+ * then again at the end, and the difference between them is the remainder
+ * of the frame budget. If that remainder is within the 16 millisecond budget,
+ * then it delays ScummVM for the remainder. If it is 0 or negative, then it continues.
+ */
int64 loopStart = g_system->getMillis();
- Common::Event event;
- g_system->getEventManager()->pollEvent(event);
+
+ // Kernal main function
+ int err = main();
+
+ if (err != Common::kNoError) {
+ debug("To err is human, to really screw up you need an Apple IIGS!");
+ return Common::kUnknownError;
+ }
int64 loopEnd = 16 - (g_system->getMillis() - loopStart);
- if (loopEnd > 0)
+ if (loopEnd > 0) {
+ //debug("remaining budget: %d", loopEnd);
g_system->delayMillis(loopEnd);
+ }
}
return Common::kNoError;
-
}
Common::Error ImmortalEngine::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
+ /* 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);
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index fc64b0f2f73..ced4ac04a64 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -28,6 +28,8 @@
#include "common/system.h"
#include "common/error.h"
#include "common/fs.h"
+#include "common/file.h"
+#include "common/memstream.h"
#include "common/hash-str.h"
#include "common/random.h"
#include "common/serializer.h"
@@ -39,10 +41,37 @@
#include "graphics/screen.h"
#include "immortal/detection.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
namespace Immortal {
+enum BitMask16 : uint16 {
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000,
+ kMaskFirst = 0x000F,
+ kMaskHLow = 0x0F00,
+ kMaskLHigh = 0x00F0,
+ kMaskNeg = 0x8000,
+};
+
+enum BitMask8 : uint8 {
+ kMaskASCII = 0x7F, // the non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMask8High = 0xF0,
+ kMask8Low = 0x0F,
+};
+
+
+enum ColourMask : uint16 {
+ kMaskRed = 0x0F00,
+ kMaskGreen = 0x00F0,
+ kMaskBlue = 0x000F
+};
+
struct ImmortalGameDescription;
+
+// Forward declaration because we will need the Disk class
class ProDosDisk;
class ImmortalEngine : public Engine {
@@ -52,22 +81,99 @@ private:
protected:
// Engine APIs
Common::Error run() override;
-public:
- const ADGameDescription *_gameDescription;
public:
ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc);
~ImmortalEngine() override;
- uint32 getFeatures() const;
+ /* Terrible functions because C doesn't like
+ * bit manipulation enough
+ */
+ uint16 xba(uint16 ab); // This just replicates the XBA command from the 65816, because flipping the byte order is somehow not a common library function???
+ uint16 rol(uint16 ab, int n); // Rotate bits left by n
+ uint16 ror(uint16 ab, int n); // Rotate bits right by n
+ uint16 mult16(uint16 a, uint16 b); // Just avoids using (uint16) everywhere, and is slightly closer to the original
- /**
- * Returns the game Id
+ const ADGameDescription *_gameDescription;
+
+ /* 'global' members
+ */
+ bool _draw; // Whether the screen should draw this frame
+ bool _dim; // Whether the palette is dim
+ bool _usingNormal; // Whether the palette is using normal
+ int _zero; // No idea what this is yet
+ Common::File _window; // Bitmap of the window around the game
+ Common::File _font; // The gfx for font sprites? Why is this not done with tilemaps...
+
+ Common::HashMap<Common::String, Common::File> _sprites; // Dictionary containing all sprites
+
+ /* Screen related members
*/
- Common::String getGameId() const;
+ Graphics::Surface *_mainSurface;
+ byte *_screenBuff; // The final buffer that will transfer to the screen
+ const int _resH = 320;
+ const int _resV = 200;
+ const int _screenSize = (_resH * _resV) * 2; // The size of the screen buffer is 320x200
- /**
- * Gets a random number
+ /* Palette related members
+ */
+ const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
+ uint16 _palDefault[16];
+ uint16 _palWhite[16];
+ uint16 _palBlack[16];
+ uint16 _palDim[16];
+ byte _palRGB[48]; // Palette that ScummVM actually uses, which is an RGB conversion of the original
+ int _dontResetColors = 0; // Not sure yet
+
+ /* Functions from Kernal.gs and Driver.gs
+ */
+
+ // Palette functions
+ void loadPalette(); // Get the static palette data from the disk
+ void setColors(uint16 pal[]); // Applies the current palette to the ScummVM surface palette
+ void fixColors(); // Determine whether the screen should be dim or normal
+ void useNormal();
+ void useDim();
+ void useBlack();
+ void useWhite();
+ void pump(); // Alternates between white and black with delays in between (flashes screen)
+ void fadePal(uint16 pal[], int count, uint16 target[]); // Fades the palette except the frame
+ void fade(uint16 pal[], int dir, int delay); // Calls fadePal() by a given delay each iteration
+ void fadeOut(int j); // Calls Fade with a delay of j jiffies and direction 1
+ void fadeIn(int j); // || and direction 0
+
+ void delay(int j); // Delay engine by j jiffies
+ void delay4(int j); // || /4
+ void delay8(int j); // || /8
+
+ Common::ErrorCode main(); // Main game loop
+ void loadWindow(); // Gets the window.bm file
+ void loadFont(); // Gets the font.spr file, and centers the sprite
+ Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
+ void loadSingles(Common::String songName); // Loads and then parse the maze song
+ void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
+ void clearSprites(); // Clears all sprites before drawing the current frame
+ //void Misc::textPrint(int num);
+ void logicInit();
+ void userIO(); // Get input
+ void pollKeys(); // Buffer input
+ void noNetwork(); // Setup input mirrors
+ void keyTraps(); // Seems to be debug only
+ void logic(); // Keeps time, handles win and lose conditions, then general logic
+ void restartLogic(); // This is the actual logic init
+ int logicFreeze(); // Overcomplicated way to check if game over or level over
+ void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
+
+ void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
+ void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
+
+ /* General engine functions
+ */
+ Common::ErrorCode initDisks(); // Opens and parses IMMORTAL.dsk and IMMORTAL_GFX.dsk
+ uint32 getFeatures() const; // Returns the game description flags
+ Common::String getGameId() const; // Returns the game Id
+
+ /* Gets a random number
*/
uint32 getRandomNumber(uint maxNum) {
return _randomSource.getRandomNumber(maxNum);
@@ -87,8 +193,7 @@ public:
return true;
}
- /**
- * Uses a serializer to allow implementing savegame
+ /* Uses a serializer to allow implementing savegame
* loading and saving using a single method
*/
Common::Error syncGame(Common::Serializer &s);
@@ -96,9 +201,9 @@ public:
/* Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) {
Common::Serializer s(nullptr, stream);
return syncGame(s);
- } */
+ }
- /* Common::Error loadGameStream(Common::SeekableReadStream *stream) {
+ Common::Error loadGameStream(Common::SeekableReadStream *stream) {
Common::Serializer s(stream, nullptr);
return syncGame(s);
} */
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
new file mode 100644
index 00000000000..284245ff6ad
--- /dev/null
+++ b/engines/immortal/kernal.cpp
@@ -0,0 +1,419 @@
+/* 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/>.
+ *
+ */
+
+/* This file covers both Kernal.GS and Driver.GS.
+ * This is because most of Driver.GS is hardware specific,
+ * and what is not (the slightly abstracted aspects), is
+ * directly connected to kernal, and might as well be
+ * considered part of the same process.
+ */
+
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/events.h"
+
+#include "immortal/immortal.h"
+#include "immortal/disk.h"
+#include "immortal/compression.h"
+
+namespace Immortal {
+
+/*
+ *
+ * ----- -----
+ * ----- Main Functions -----
+ * ----- -----
+ *
+ */
+
+Common::ErrorCode ImmortalEngine::main() {
+ Common::Event event;
+ g_system->getEventManager()->pollEvent(event);
+
+ userIO();
+ noNetwork();
+ pollKeys();
+ logic();
+ pollKeys();
+ if (logicFreeze() == 0) {
+ drawUniv();
+ pollKeys();
+ fixColors();
+ copyToScreen();
+ pollKeys();
+ }
+
+ return Common::kNoError;
+}
+
+void ImmortalEngine::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
+ g_system->delayMillis(j * 56);
+}
+
+void ImmortalEngine::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
+ g_system->delayMillis(j * 14);
+}
+
+void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
+ g_system->delayMillis(j * 7);
+}
+
+/*
+ *
+ * ----- -----
+ * ----- Screen Drawing Functions -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::drawUniv() {
+ /* The byte buffer for the screen (_screenBuff) has one byte for
+ * every pixel, with the resolution of the game being 320x200.
+ * For a bitmap like the window frame, all we need to do is
+ * extract the pixel out of each nyble (half byte) of the data,
+ * by looping over it one row at a time.
+ */
+
+ // Apply the window frame to the buffer
+ _window.seek(0);
+ byte pixel;
+ int pos;
+ for (int y = 0; y < _resV; y++) {
+ for (int x = 0; x < _resH; x += 2) {
+ pos = (y * _resH) + x;
+ pixel = _window.readByte();
+ _screenBuff[pos] = (pixel & kMask8High) >> 4;
+ _screenBuff[pos + 1] = pixel & kMask8Low;
+ }
+ }
+
+ /* copyRectToSurface will apply the screenbuffer to the ScummVM surface.
+ * We want to do 320 bytes per scanline, at location (0,0), with a
+ * size of 320x200.
+ */
+ _mainSurface->copyRectToSurface(_screenBuff, _resH, 0, 0, _resH, _resV);
+
+}
+
+void ImmortalEngine::copyToScreen() {
+ if (_draw == 1) {
+ g_system->copyRectToScreen((byte *)_mainSurface->getPixels(), _resH, 0, 0, _resH, _resV);
+ g_system->updateScreen();
+ }
+}
+
+void ImmortalEngine::clearScreen() {
+ //fill the visible screen with black pixels by drawing a rectangle
+
+ //rect(32, 20, 256, 128, 0)
+
+ if ((_dontResetColors & kMaskLow) == 0) {
+ useNormal();
+ }
+}
+
+/*
+ *
+ * ----- -----
+ * ----- File Loading -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::loadSprites() {
+ // Load MoreSprites.spr
+
+
+}
+
+void ImmortalEngine::loadWindow() {
+ // Initialize the window bitmap
+ if (!_window.open("WINDOWS.BM")) {
+ debug("oh nose :(");
+ }
+}
+
+void ImmortalEngine::loadFont() {
+ // Initialize the font sprite
+ if (!_font.open("FONT.SPR")) {
+ debug("oh nose :(");
+ }
+}
+
+Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
+ Common::File f;
+ if (!f.open(fileName)) {
+ debug("*surprised pikachu face*");
+ return nullptr;
+ }
+
+ /* This isn't the most efficient way to do this (could just read a 32bit uint and compare),
+ * but this makes it more obvious what the source was doing. We want to know if the 4 bytes
+ * at file[8] are 'C' 'M' 'P' '0', so this grabs just the ascii bits of those 4 bytes,
+ * allowing us to directly compare it with 'CMP0'.
+ */
+ char compSig[] = "CMP0";
+ char sig[] = "0000";
+
+ f.seek(8);
+
+ for (int i = 0; i < 4; i++) {
+ sig[i] = f.readByte() & kMaskASCII;
+ }
+
+ if (strcmp(sig, compSig) == 0) {
+ debug("compressed");
+
+ /* The size of the compressed data is stored in the header, but doesn't
+ * account for the FORM part?? Also, **technically** this is a uint32LE,
+ * but the engine itself actually /doesn't/ use it like that. It only
+ * decrements the first word (although it compares against the second half,
+ * as if it is expecting that to be zero? It's a little bizarre).
+ */
+ f.seek(6);
+ int len = f.readUint16LE() - 4;
+
+ // Compressed files have a 12 byte header before the data
+ f.seek(12);
+ return Compression::unCompress(&f, len);
+ }
+
+ byte *out = (byte *)malloc(f.size());
+ f.read(out, f.size());
+ return new Common::MemoryReadStream(out, f.size(), DisposeAfterUse::YES);
+
+}
+
+
+/*
+ *
+ * ----- -----
+ * ----- Palette Functions -----
+ * ----- -----
+ *
+ */
+
+/* Palettes on the Apple IIGS:
+ * In High-res mode you have 2 options: 320x200 @ 4bpp or 320x640 @ 2bpp.
+ * The Immortal uses the former, giving us 16 colours to use
+ * for any given pixel on the screen (ignoring per scanline palettes because
+ * The Immortal does not use them). This 16 colour palette is made of 2 byte
+ * words containing the RGB components in the form 0RGB.
+ *
+ * The equivalent palette for ScummVM is a byte stream of up to 256
+ * colours composed of 3 bytes each, ending with a transparency byte.
+ *
+ * Because each colour in the game palette is only a single nyble (4 bits),
+ * we also need to multiply the nyble up to the size of a byte (* 16, or << 4).
+ */
+
+void ImmortalEngine::loadPalette() {
+ // The palettes are stored at a particular location in the disk, this just grabs them
+ Common::File d;
+ d.open("IMMORTAL.dsk");
+ d.seek(kPaletteOffset);
+
+ d.read(_palDefault, 32);
+ d.read(_palWhite, 32);
+ d.read(_palBlack, 32);
+ d.read(_palDim, 32);
+
+ d.close();
+}
+
+void ImmortalEngine::setColors(uint16 pal[]) {
+ // The RGB palette is 3 bytes per entry, and each byte is a colour
+ for (int i = 0; i < 16; i++) {
+
+ // The palette gets masked so it can update only specific indexes and uses FFFF to do so. However the check is simply for a negative
+ if (pal[i] < kMaskNeg) {
+
+ // Green is already the correct size, being the second nyble (00G0)
+ // Red is in the first nyble of the high byte, so it needs to move right by 4 bits (0R00 -> 00R0)
+ // Blue is the first nyble of the first byte, so it needs to move left by 4 bits (000B -> 00B0)
+ _palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4);
+ _palRGB[(i * 3) + 1] = ((pal[i] & kMaskGreen));
+ _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) << 4;
+ }
+ }
+ // Palette index to update first is 0, and there are 16 colours to update
+ g_system->getPaletteManager()->setPalette(_palRGB, 0, 16);
+ g_system->updateScreen();
+}
+
+void ImmortalEngine::fixColors() {
+ // Pretty silly that this is done with two separate variables, could just index by one...
+ if (_dim == true) {
+ if (_usingNormal == true) {
+ useDim();
+ }
+ } else {
+ if (_usingNormal == false) {
+ useNormal();
+ }
+ }
+}
+
+void ImmortalEngine::pump() {
+ // Flashes the screen (except the frame thankfully) white, black, white, black, then clears the screen and goes back to normal
+ useWhite();
+ g_system->updateScreen();
+ delay(2);
+ useBlack();
+ g_system->updateScreen();
+ delay(2);
+ useWhite();
+ g_system->updateScreen();
+ delay(2);
+ useBlack();
+ g_system->updateScreen();
+ clearScreen();
+ // Why does it do this instead of setting _dontResetColors for clearScreen() instead?
+ useNormal();
+}
+
+void ImmortalEngine::fadePal(uint16 pal[], int count, uint16 target[]) {
+ /* This will fade the palette used by everything inside the game screen
+ * but will not touch the window frame palette. It essentially takes the
+ * color value nyble, multiplies it by a multiplier, then takes the whole
+ * number result and inserts it into the word at the palette index of the
+ * temporary palette. This could I'm sure be done with regular multiplication
+ * and division operators, but in case the bits that get dropped are otherwise
+ * kept, this is a direct translation of the bit manipulation sequence.
+ */
+ int maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+
+ uint16 result;
+ uint16 temp;
+
+ for (int i = 15; i >= 0; i--) {
+ result = maskPal[i];
+ if (result == 0) {
+ result = pal[i];
+ if (result != 0xFFFF) {
+ // Blue = 0RGB -> 000B -> 0BBB -> BB0B -> 000B
+ result = (xba(mult16((result & kMaskFirst), count))) & kMaskFirst;
+
+ // Green = 0RGB -> 00RG -> 000G -> 0GGG -> GG0G -> 000G -> 00G0 -> 00GB
+ temp = mult16(((pal[i] >> 4) & kMaskFirst), count);
+ temp = (xba(temp) & kMaskFirst) << 4;
+ result = temp | result;
+
+ // Red = 0RGB -> GB0R -> 000R -> 0RRR -> RR0R -> 000R -> 0R00 -> 0RGB
+ temp = xba(pal[i]) & kMaskFirst;
+ temp = xba(mult16(temp, count));
+ temp = xba(temp & kMaskFirst);
+ result = temp | result;
+ }
+ }
+ target[i] = result;
+ }
+}
+
+void ImmortalEngine::fade(uint16 pal[], int dir, int delay) {
+ // This temp palette will have FFFF in it, which will be understood as masks by setColors()
+ uint16 target[16];
+ uint16 count;
+
+ // Originally used a branch, but this is functionally identical and much nicer
+ count = dir * 256;
+
+ while ((count >= 0) && (count <= 256)) {
+ fadePal(pal, count, target);
+ delay8(delay);
+ setColors(target);
+
+ // Same as above, it was originally a branch, this does the same thing
+ count += (dir == 0) ? 16 : -16;
+ }
+}
+
+// These two can probably be removed and instead use an enum to declare fadeout/in
+void ImmortalEngine::fadeOut(int j) {
+ fade(_palDefault, 1, j);
+}
+
+void ImmortalEngine::fadeIn(int j) {
+ fade(_palDefault, 0, j);
+}
+
+// These two can probably be removed since the extra call in C doesn't have the setup needed in ASM
+void ImmortalEngine::useBlack() {
+ setColors(_palBlack);
+}
+void ImmortalEngine::useWhite() {
+ setColors(_palBlack);
+}
+
+void ImmortalEngine::useNormal() {
+ setColors(_palDefault);
+ _usingNormal = true;
+}
+
+void ImmortalEngine::useDim() {
+ setColors(_palDim);
+ _usingNormal = false;
+}
+
+
+/*
+ *
+ * ----- -----
+ * ----- Input Functions -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::userIO() {}
+void ImmortalEngine::pollKeys() {}
+void ImmortalEngine::noNetwork() {}
+
+void ImmortalEngine::loadSingles(Common::String songName) {
+ debug("%s", songName.c_str());
+}
+void ImmortalEngine::clearSprites() {}
+void ImmortalEngine::keyTraps() {}
+
+
+} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
new file mode 100644
index 00000000000..8a1fd3a7474
--- /dev/null
+++ b/engines/immortal/logic.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 "common/debug.h"
+#include "common/error.h"
+#include "common/events.h"
+
+#include "immortal/immortal.h"
+
+namespace Immortal {
+
+void ImmortalEngine::logicInit() {
+ debug("init logic here");
+}
+
+void ImmortalEngine::logic() {
+}
+
+void ImmortalEngine::restartLogic() {
+}
+
+int ImmortalEngine::logicFreeze() {
+ return 0;
+}
+
+
+} // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 15d7f541677..b3ba49aaff9 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -4,7 +4,9 @@ MODULE_OBJS = \
immortal.o \
disk.o \
metaengine.o \
- compression.o
+ compression.o \
+ kernal.o \
+ logic.o
# This module can be built as a plugin
ifeq ($(ENABLE_IMMORTAL), DYNAMIC_PLUGIN)
Commit: e8d06bf6b601e81fbfd251f6f2eadb9cd458db24
https://github.com/scummvm/scummvm/commit/e8d06bf6b601e81fbfd251f6f2eadb9cd458db24
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Disk.cpp bug fix to parse correct number of files
Changed paths:
engines/immortal/disk.cpp
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index eb0e82b3991..f92501183c1 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -278,9 +278,8 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
int parsedFiles = 0;
for (int i = 0; i < h->_fileCount; i++) {
-
// When we have read all the files for a given block (_entriesPerBlock), we need to change to the next block of the directory
- if (parsedFiles > h->_entriesPerBlock) {
+ if (parsedFiles == h->_entriesPerBlock) {
parsedFiles = 0;
_disk.seek(n * kBlockSize);
p = _disk.readUint16LE();
@@ -295,8 +294,8 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
// It is a regular file if (dead < file type < pascal) and the file has a size
if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
- Common::String fileName = path + fileEntry._name;
- debug("%s, %08X", fileName.c_str(), fileEntry._eof);
+ Common::String fileName = path + fileEntry._name;
+ debug("%s", fileName.c_str());
ProDOSFile *currFile = new ProDOSFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
_files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
@@ -313,8 +312,8 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
DirHeader subHead;
getDirectoryHeader(&subHead);
- path += Common::String(subHead._name);
- path += '/';
+ // Give it a temporary new path name by sticking the name of the subdirectory on to the end of the current path
+ Common::String subPath = Common::String(path + subHead._name + '/');
searchDirectory(&subHead, subP, subN, path);
debug("--- surfacing to parent directory ---");
Commit: d77cc8f5db2d7d34ab8b353bb8743b81cf807aed
https://github.com/scummvm/scummvm/commit/d77cc8f5db2d7d34ab8b353bb8743b81cf807aed
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Compression is part of engine class, also minor changes to a couple of statements
Changed paths:
R engines/immortal/compression.h
engines/immortal/compression.cpp
engines/immortal/immortal.h
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index 03cd608c68c..5a9dba9ffa5 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -19,10 +19,7 @@
*
*/
-#include "common/debug.h"
-#include "common/file.h"
-#include "common/memstream.h"
-#include "immortal/compression.h"
+#include "immortal/immortal.h"
/* Decompression:
* This decompression algorithm follows the source assembly very closely,
@@ -30,17 +27,9 @@
* In: Source data as File, size of data
* Out: Pointer to uncompressed data as SeekableReadStream
*/
-namespace Compression {
+namespace Immortal {
-// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
-enum BitMask : uint16 {
- kMask12Bit = 0x0F9F, // Code (pos, 00, len) is stored in lower 12 bits of word
- kMaskLow = 0x00FF,
- kMaskHigh = 0xFF00,
- kMaskLast = 0xF000
-};
-
-Common::SeekableReadStream *unCompress(Common::File *src, int srcLen) {
+Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int srcLen) {
/* Note: this function does not seek() in the file, which means
* that if there is a header on the data, the expectation is that
* seek() was already used to move past the header before this function.
@@ -136,19 +125,20 @@ Common::SeekableReadStream *unCompress(Common::File *src, int srcLen) {
topStack--;
}
- topStack = 0; // Won't this always be 0 because of the while loop?
+ topStack = 0;
code = getMember(oldCode, finalChar, findEmpty, start, ptk);
oldCode = inputCode;
}
}
- // Return a readstream with a pointer to the data in the write stream.
- // This one we do want to dispose after using, because it will be in the scope of the engine itself
+ /* Return a readstream with a pointer to the data in the write stream.
+ * This one we do want to dispose after using, because it will be in the scope of the engine itself
+ */
return new Common::MemoryReadStream(dstW.getData(), dstW.size(), DisposeAfterUse::YES);
}
-void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
+void ImmortalEngine::setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
// Clear the whole dictionary
for (int i = 0x3FFF; i >= 0; i--) {
start[i] = 0;
@@ -156,15 +146,15 @@ void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
}
// Set the initial 256 bytes to be value 256, these are the characters without extensions
- for (int i = 0x0FF; i >= 0; i--) {
- ptk[i] = 0x100;
+ for (int i = 255; i >= 0; i--) {
+ ptk[i] = 256;
}
// This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
findEmpty = 0x8000;
}
-int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
+int ImmortalEngine::getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
// Check if we're at the end of the file
if (srcLen == 0) {
carry = false;
@@ -185,12 +175,15 @@ int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
return c;
}
-uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
+uint16 ImmortalEngine::getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
// This function is effectively void, as the return value is only used in compression
// k and codeW are local variables with the value of oldCode and finalChar
uint16 hash;
+ uint16 tmp;
+ bool ag = true;
+
hash = (k << 3) ^ k;
hash = (hash << 1) ^ codeW;
hash <<= 1;
@@ -206,8 +199,6 @@ uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint
}
// This loop is a bit wacky, due to the way the jumps were stuctured in the source
- bool ag = true;
- uint16 tmp;
while (ag == true) {
if ((start[hash] & kMask12Bit) == codeW) {
if ((ptk[hash] & kMaskLow) == k) {
@@ -217,22 +208,20 @@ uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint
tmp = start[hash] & kMaskLast;
if (tmp == 0) {
-
// I've separated this into it's own function for the sake of this loop being readable
appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
ag = false;
} else {
- tmp >>= 4;
- hash = ptk[hash] & kMaskHigh; // The 65816 can XBA to flip the bytes in a word, instead we have mask and shift over :(
- hash >>= 8;
- hash = (hash | tmp) << 1;
+ hash = xba(ptk[hash]);
+ hash = (hash & kMaskLow) | (tmp >> 4);
+ hash <<= 1;
}
}
return hash;
}
-void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp) {
+void ImmortalEngine::appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp) {
uint16 prev;
uint16 link;
@@ -260,18 +249,17 @@ void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16
ptk[hash] = k | 0x100;
link = hash >> 1;
- tmp = link & kMaskLow; // Another classic XBA situation, although it's nicer this time
- tmp <<= 8; // Because we can just grab the low bytes and shift them forward (it's still much faster to XBA though)
- ptk[prev] = (ptk[prev] & kMaskLow) | tmp;
- start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
+ ptk[prev] = (link << 8) | (ptk[prev] & kMaskLow);
+ //start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
+ start[prev] |= (link >> 4) & kMaskLast;
found = true;
}
}
}
}
-} // namespace compression
+} // namespace immortal
diff --git a/engines/immortal/compression.h b/engines/immortal/compression.h
deleted file mode 100644
index 96633758e0b..00000000000
--- a/engines/immortal/compression.h
+++ /dev/null
@@ -1,39 +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/>.
- *
- */
-
-#ifndef IMMORTAL_COMPRESSION_H
-#define IMMORTAL_COMPRESSION_H
-
-#include "common/file.h"
-#include "common/memstream.h"
-
-namespace Compression {
-
-// Only unCompress() is called from outside Compression, the others are subroutines.
-Common::SeekableReadStream *unCompress(Common::File *src, int srcLen);
-void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
-int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
-uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]);
-void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
-
-} // namespace compression
-
-#endif
\ No newline at end of file
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index ced4ac04a64..62412401bdf 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -24,6 +24,8 @@
#include "audio/mixer.h"
+#include "common/debug.h"
+#include "common/events.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
@@ -38,14 +40,19 @@
#include "engines/engine.h"
#include "engines/savestate.h"
-#include "graphics/screen.h"
-#include "immortal/detection.h"
+#include "graphics/screen.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
+#include "immortal/detection.h"
+#include "immortal/disk.h"
+
namespace Immortal {
+#include "immortal/sprite_list.h"
+
+// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
enum BitMask16 : uint16 {
kMaskLow = 0x00FF,
kMaskHigh = 0xFF00,
@@ -54,12 +61,13 @@ enum BitMask16 : uint16 {
kMaskHLow = 0x0F00,
kMaskLHigh = 0x00F0,
kMaskNeg = 0x8000,
+ kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
};
enum BitMask8 : uint8 {
- kMaskASCII = 0x7F, // the non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
kMask8High = 0xF0,
- kMask8Low = 0x0F,
+ kMask8Low = 0x0F
};
@@ -69,6 +77,23 @@ enum ColourMask : uint16 {
kMaskBlue = 0x000F
};
+struct DataSprite {
+ uint8 _cenX; // These are the base center positions
+ uint8 _cenY;
+ byte *_bitmap; // Pointer to actual data
+Common::SeekableReadStream *_file; // This will likely be removed later
+};
+
+struct Sprite {
+ uint16 _frame; // Something something background?
+ uint16 _activeFrame; // Why the heck is this inside the individual sprite data?
+ uint16 _X;
+ uint16 _Y;
+ uint16 _on; // 1 = active
+ uint16 _priority;
+DataSprite _dSprite;
+};
+
struct ImmortalGameDescription;
// Forward declaration because we will need the Disk class
@@ -96,26 +121,54 @@ public:
const ADGameDescription *_gameDescription;
- /* 'global' members
+ /*
+ * --- Members ---
+ *
*/
- bool _draw; // Whether the screen should draw this frame
- bool _dim; // Whether the palette is dim
- bool _usingNormal; // Whether the palette is using normal
- int _zero; // No idea what this is yet
- Common::File _window; // Bitmap of the window around the game
- Common::File _font; // The gfx for font sprites? Why is this not done with tilemaps...
- Common::HashMap<Common::String, Common::File> _sprites; // Dictionary containing all sprites
+ /*
+ * Constants
+ */
+ const int kResH = 320;
+ const int kResV = 200;
+ const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
+ const int kMaxSprites = 32; // Number of sprites allowed at once
+ const int kWizardX = 28; // Common sprite center for some reason
+ const int kWizardY = 37;
+
+ /*
+ * 'global' members
+ */
+ bool _draw = 0; // Whether the screen should draw this frame
+ bool _dim = 0; // Whether the palette is dim
+ bool _usingNormal = 0; // Whether the palette is using normal
+ int _zero = 0; // No idea what this is yet
+ uint8 _gameOverFlag = 0;
+ uint8 _levelOver = 0;
+ uint8 _themePaused = 0;
+ int _level = 0;
+ int _titlesShown = 0;
+ int _time = 0;
+ int _promoting = 0;
+ int _restart = 0;
+ int _lastCertLen = 0;
+
+ /*
+ * Asset related members
+ */
+ DataSprite _font; // The font sprite data is loaded separate from other sprite stuff
+ byte *_window; // Bitmap of the window around the game
+ Sprite _sprites[32]; // A contiguous series of sprites (this is the same as kMaxSprites, but it can't be used for this)
+ DataSprite _dataSprites[kFont+1]; // All the sprite data, indexed by SpriteFile
- /* Screen related members
+ /*
+ * Screen related members
*/
Graphics::Surface *_mainSurface;
byte *_screenBuff; // The final buffer that will transfer to the screen
- const int _resH = 320;
- const int _resV = 200;
- const int _screenSize = (_resH * _resV) * 2; // The size of the screen buffer is 320x200
- /* Palette related members
+ /*
+ * Palette related members
*/
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
uint16 _palDefault[16];
@@ -125,10 +178,32 @@ public:
byte _palRGB[48]; // Palette that ScummVM actually uses, which is an RGB conversion of the original
int _dontResetColors = 0; // Not sure yet
- /* Functions from Kernal.gs and Driver.gs
+
+
+ /*
+ * --- Functions ---
+ *
*/
- // Palette functions
+ /*
+ * [Kernal.cpp] Functions from Kernal.gs and Driver.gs
+ */
+
+ Common::ErrorCode main(); // Main game loop
+
+ // Screen
+ void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
+ void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
+ void loadWindow(); // Gets the window.bm file
+ void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
+ void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
+
+ // Misc engine
+ void delay(int j); // Delay engine by j jiffies
+ void delay4(int j); // || /4
+ void delay8(int j); // || /8
+
+ // Palette
void loadPalette(); // Get the static palette data from the disk
void setColors(uint16 pal[]); // Applies the current palette to the ScummVM surface palette
void fixColors(); // Determine whether the screen should be dim or normal
@@ -141,34 +216,71 @@ public:
void fade(uint16 pal[], int dir, int delay); // Calls fadePal() by a given delay each iteration
void fadeOut(int j); // Calls Fade with a delay of j jiffies and direction 1
void fadeIn(int j); // || and direction 0
+ void normalFadeOut();
+ void slowFadeOut();
+ void normalFadeIn();
- void delay(int j); // Delay engine by j jiffies
- void delay4(int j); // || /4
- void delay8(int j); // || /8
-
- Common::ErrorCode main(); // Main game loop
- void loadWindow(); // Gets the window.bm file
- void loadFont(); // Gets the font.spr file, and centers the sprite
+ // Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
+ void loadFont(); // Gets the font.spr file, and centers the sprite
void loadSingles(Common::String songName); // Loads and then parse the maze song
- void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
void clearSprites(); // Clears all sprites before drawing the current frame
- //void Misc::textPrint(int num);
- void logicInit();
+ void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
+
+ // Input
void userIO(); // Get input
void pollKeys(); // Buffer input
void noNetwork(); // Setup input mirrors
void keyTraps(); // Seems to be debug only
+
+ /*
+ * [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
+ */
+
+ // ??
+ void setSpriteCenter(Common::SeekableReadStream *f, int num, uint8 cenX, uint8 cenY); // Basically initializes the data sprite
+ int getNumFrames(int file, int num);
+
+ /*
+ * [Logic.cpp] Functions from Logic.GS
+ */
+
+ // Misc
+ void logicInit();
void logic(); // Keeps time, handles win and lose conditions, then general logic
void restartLogic(); // This is the actual logic init
- int logicFreeze(); // Overcomplicated way to check if game over or level over
- void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
-
- void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
- void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
+ int logicFreeze(); // Overcomplicated way to check if game over or level over
+ int getLevel(); // Literally just return _level...
+ void gameOverDisplay();
+ void gameOver();
+ void levelOver();
+
+ /*
+ * [Misc.cpp] Functions from Misc
+ */
+
+ //void Misc::textPrint(int num);
- /* General engine functions
+ /*
+ * [Compression.cpp] Functions from Compression.GS
*/
+
+ // Main routines
+ Common::SeekableReadStream *unCompress(Common::File *src, int srcLen);
+
+ // Subroutines called by unCompress
+ void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
+ int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
+ uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]);
+ void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
+
+
+
+ /*
+ * --- ScummVM general engine Functions ---
+ *
+ */
+
Common::ErrorCode initDisks(); // Opens and parses IMMORTAL.dsk and IMMORTAL_GFX.dsk
uint32 getFeatures() const; // Returns the game description flags
Common::String getGameId() const; // Returns the game Id
Commit: 676cf5f7b5bea09bbd97043053fc73d7d3cb6edb
https://github.com/scummvm/scummvm/commit/676cf5f7b5bea09bbd97043053fc73d7d3cb6edb
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Update to engine skeleton and addition of sprite_list, sprites, misc, and cycle
Changed paths:
A engines/immortal/cycle.cpp
A engines/immortal/misc.cpp
A engines/immortal/sprite_list.h
A engines/immortal/sprites.cpp
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/module.mk
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
new file mode 100644
index 00000000000..1eb43200ea2
--- /dev/null
+++ b/engines/immortal/cycle.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 "immortal/immortal.h"
+
+namespace Immortal {
+
+void ImmortalEngine::cycleNew() {}
+ int ImmortalEngine::getCycleChr() {
+ return 0;
+ }
+void ImmortalEngine::cycleFreeAll() {}
+void ImmortalEngine::cycleGetFile() {}
+void ImmortalEngine::cycleGetNum() {}
+void ImmortalEngine::cycleGetIndex() {}
+void ImmortalEngine::cycleSetIndex() {}
+void ImmortalEngine::cycleGetFrame() {}
+void ImmortalEngine::cycleAdvance() {}
+
+
+} // namespace Immortal
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index ba4f80d183d..13856bca0f3 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -19,23 +19,12 @@
*
*/
-#include "immortal/immortal.h"
-#include "immortal/detection.h"
-#include "immortal/disk.h"
-#include "immortal/compression.h"
-
-#include "common/scummsys.h"
#include "common/config-manager.h"
-#include "common/debug-channels.h"
-#include "common/events.h"
#include "common/system.h"
-#include "common/debug.h"
-#include "common/debug-channels.h"
-#include "common/error.h"
-#include "common/file.h"
#include "engines/util.h"
-#include "audio/mixer.h"
+
+#include "immortal/immortal.h"
namespace Immortal {
@@ -56,9 +45,6 @@ ImmortalEngine::ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc)
}
ImmortalEngine::~ImmortalEngine() {
- _window.close();
- _font.close();
-
// Confirm that the engine was destroyed
debug("ImmortalEngine::~ImmortalEngine");
}
@@ -71,7 +57,7 @@ uint16 ImmortalEngine::xba(uint16 ab) {
* initial translation a little more straightforward. Eventually,
* logic should be refactored to not require this.
*/
- return ((ab & kMaskLow) << 8) + ((ab & kMaskHigh) >> 8);
+ return ((ab & kMaskLow) << 8) | ((ab & kMaskHigh) >> 8);
}
uint16 ImmortalEngine::rol(uint16 ab, int n) {
@@ -80,20 +66,24 @@ uint16 ImmortalEngine::rol(uint16 ab, int n) {
* doesn't have this natively??? This assumes a 16 bit
* unsigned int because that's all we need for the 65816.
*/
- return (ab << n) | (ab >> (16 - n));
+ return (ab << n) | (ab >> (-n & 15));
}
uint16 ImmortalEngine::ror(uint16 ab, int n) {
/* The way this works is very straightforward. We start by
- * performing the bit shift like normal:
- * 0001 -> 0000
+ * performing the bit shift like normal: 0001 -> 0000
* Then we need an easy way to apply the bit whether it fell
* off the end or not, so we bit shift the opposite direction
- * for the length of the word. If the bit wouldn't have
- * fallen off, then it applies a 0, but if it would have,
- * then we get a 1 on the opposite bit, just like the carry.
+ * for the length of the word minus the size of the shift.
+ * This way, the bit that we shifted normally, gets
+ * separately moved to the other end: 0001 -> 1000
+ * In other words, the space C uses to evaluate the second
+ * part of the expression, is simulating the register for the
+ * carry flag. To avoid branching in case of a 0, we get the
+ * shift size by using the negative of the number as an
+ * implicit subtraction and grabbing just the relevant bits.
*/
- return (ab >> n) | (ab << (16 - n));
+ return (ab >> n) | (ab << (-n & 15));
}
uint16 ImmortalEngine::mult16(uint16 a, uint16 b) {
@@ -148,12 +138,12 @@ Common::ErrorCode ImmortalEngine::initDisks() {
}
Common::Error ImmortalEngine::run() {
- initGraphics(_resH, _resV);
+ initGraphics(kResH, kResV);
_mainSurface = new Graphics::Surface();
- _mainSurface->create(_resH, _resV, Graphics::PixelFormat::createFormatCLUT8());
+ _mainSurface->create(kResH, kResV, Graphics::PixelFormat::createFormatCLUT8());
- _screenBuff = new byte[_screenSize];
+ _screenBuff = new byte[kScreenSize];
if (initDisks() != Common::kNoError) {
debug("Some kind of disc error!");
@@ -177,6 +167,8 @@ Common::Error ImmortalEngine::run() {
clearSprites(); // Clear the sprites before we start
logicInit(); // Init the game logic
+ _err = Common::kNoError;
+
while (!shouldQuit()) {
/* The game loop runs at 60fps, which is 16 milliseconds per frame.
@@ -187,10 +179,24 @@ Common::Error ImmortalEngine::run() {
*/
int64 loopStart = g_system->getMillis();
- // Kernal main function
- int err = main();
-
- if (err != Common::kNoError) {
+ // Main
+ Common::Event event;
+ g_system->getEventManager()->pollEvent(event);
+
+ userIO();
+ noNetwork();
+ pollKeys();
+ logic();
+ pollKeys();
+ if (logicFreeze() == 0) {
+ drawUniv();
+ pollKeys();
+ fixColors();
+ copyToScreen();
+ pollKeys();
+ }
+
+ if (_err != Common::kNoError) {
debug("To err is human, to really screw up you need an Apple IIGS!");
return Common::kUnknownError;
}
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 62412401bdf..b3914ddb512 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -25,6 +25,7 @@
#include "audio/mixer.h"
#include "common/debug.h"
+#include "common/debug-channels.h"
#include "common/events.h"
#include "common/scummsys.h"
#include "common/system.h"
@@ -48,9 +49,9 @@
#include "immortal/detection.h"
#include "immortal/disk.h"
-namespace Immortal {
+#include "immortal/sprite_list.h" // This is an enum of all available sprites
-#include "immortal/sprite_list.h"
+namespace Immortal {
// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
enum BitMask16 : uint16 {
@@ -78,20 +79,25 @@ enum ColourMask : uint16 {
};
struct DataSprite {
- uint8 _cenX; // These are the base center positions
- uint8 _cenY;
- byte *_bitmap; // Pointer to actual data
-Common::SeekableReadStream *_file; // This will likely be removed later
+ uint8 _cenX; // These are the base center positions
+ uint8 _cenY;
+ byte *_bitmap; // Pointer to actual data
+Common::SeekableReadStream *_file; // This will likely be removed later
};
struct Sprite {
- uint16 _frame; // Something something background?
- uint16 _activeFrame; // Why the heck is this inside the individual sprite data?
- uint16 _X;
- uint16 _Y;
- uint16 _on; // 1 = active
- uint16 _priority;
-DataSprite _dSprite;
+ int _frame; // Current frame of the cycle
+ uint16 _X;
+ uint16 _Y;
+ uint16 _on; // 1 = active
+ uint16 _priority;
+DataSprite *_dSprite;
+};
+
+struct Cycle {
+DataSprite *_dSprite;
+ int _numCycles;
+ int *_frames;
};
struct ImmortalGameDescription;
@@ -111,6 +117,8 @@ public:
ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc);
~ImmortalEngine() override;
+ const ADGameDescription *_gameDescription;
+
/* Terrible functions because C doesn't like
* bit manipulation enough
*/
@@ -119,8 +127,6 @@ public:
uint16 ror(uint16 ab, int n); // Rotate bits right by n
uint16 mult16(uint16 a, uint16 b); // Just avoids using (uint16) everywhere, and is slightly closer to the original
- const ADGameDescription *_gameDescription;
-
/*
* --- Members ---
*
@@ -129,36 +135,52 @@ public:
/*
* Constants
*/
+ // Screen
const int kResH = 320;
const int kResV = 200;
+ const int kScreenW = 128;
+ const int kScreenH = 128;
const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
+
+ // Disk offsets
+ const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
+
+ // Sprite constants
+ const int kMaxCycles = 32;
const int kMaxSprites = 32; // Number of sprites allowed at once
+ const int kMaxSpriteAbove = 48; // Maximum sprite extents from center
+ const int kMaxSpriteBelow = 16;
+ const int kMaxSpriteLeft = 16;
+ const int kMaxSpriteRight = 16;
const int kWizardX = 28; // Common sprite center for some reason
const int kWizardY = 37;
/*
* 'global' members
*/
- bool _draw = 0; // Whether the screen should draw this frame
- bool _dim = 0; // Whether the palette is dim
- bool _usingNormal = 0; // Whether the palette is using normal
- int _zero = 0; // No idea what this is yet
+ Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
+ bool _draw = 0; // Whether the screen should draw this frame
+ bool _dim = 0; // Whether the palette is dim
+ bool _usingNormal = 0; // Whether the palette is using normal
+ int _zero = 0; // No idea what this is yet
uint8 _gameOverFlag = 0;
- uint8 _levelOver = 0;
- uint8 _themePaused = 0;
- int _level = 0;
- int _titlesShown = 0;
- int _time = 0;
- int _promoting = 0;
- int _restart = 0;
- int _lastCertLen = 0;
+ uint8 _levelOver = 0;
+ uint8 _themePaused = 0;
+ int _level = 0;
+ int _titlesShown = 0;
+ int _time = 0;
+ int _promoting = 0;
+ int _restart = 0;
+ int _lastCertLen = 0;
/*
* Asset related members
*/
+ int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
DataSprite _font; // The font sprite data is loaded separate from other sprite stuff
byte *_window; // Bitmap of the window around the game
Sprite _sprites[32]; // A contiguous series of sprites (this is the same as kMaxSprites, but it can't be used for this)
+ Cycle _cycles[32]; // Animation cycle for each sprite slot
DataSprite _dataSprites[kFont+1]; // All the sprite data, indexed by SpriteFile
/*
@@ -166,11 +188,11 @@ public:
*/
Graphics::Surface *_mainSurface;
byte *_screenBuff; // The final buffer that will transfer to the screen
-
+ uint16 _viewPortX;
+ uint16 _viewPortY;
/*
* Palette related members
*/
- const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
uint16 _palDefault[16];
uint16 _palWhite[16];
uint16 _palBlack[16];
@@ -179,7 +201,6 @@ public:
int _dontResetColors = 0; // Not sure yet
-
/*
* --- Functions ---
*
@@ -189,19 +210,20 @@ public:
* [Kernal.cpp] Functions from Kernal.gs and Driver.gs
*/
- Common::ErrorCode main(); // Main game loop
-
// Screen
void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
+ void rect(int x, int y, int w, int h); // Draws a solid rectangle at x,y with size w,h. Also shadows for blit?
void loadWindow(); // Gets the window.bm file
void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
+ void mungeBM(); // Put together final bitmap?
+ void blit(); // Will probably want this to be it's own function
+ void blit40(); // Uses macro blit 40 times
+ void sBlit();
+ void scroll();
// Misc engine
- void delay(int j); // Delay engine by j jiffies
- void delay4(int j); // || /4
- void delay8(int j); // || /8
// Palette
void loadPalette(); // Get the static palette data from the disk
@@ -226,12 +248,20 @@ public:
void loadSingles(Common::String songName); // Loads and then parse the maze song
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
+ void addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p);
// Input
void userIO(); // Get input
void pollKeys(); // Buffer input
void noNetwork(); // Setup input mirrors
void keyTraps(); // Seems to be debug only
+ void blit8(); // This is actually just input, but it is called blit because it does a 'paddle blit' 8 times
+
+ // These will replace the myriad of hardware input handling from the source
+ void getInput();
+ void addKeyBuffer();
+ void clearKeyBuff();
+
/*
* [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
@@ -239,7 +269,28 @@ public:
// ??
void setSpriteCenter(Common::SeekableReadStream *f, int num, uint8 cenX, uint8 cenY); // Basically initializes the data sprite
- int getNumFrames(int file, int num);
+ int getNumFrames(int file, int num);
+
+ /*
+ * [Cycle.cpp] Functions from Cyc
+ */
+
+ /* Unneccessary
+ void cycleInit();
+ void cycleFree();
+ void cycleGetNumFrames();
+ void cycleGetList();*/
+
+ // Misc
+ void cycleNew(); // Adds a cycle to the current list
+ int getCycleChr();
+ void cycleFreeAll(); // Delete all cycles
+ void cycleGetFile();
+ void cycleGetNum();
+ void cycleGetIndex();
+ void cycleSetIndex();
+ void cycleGetFrame();
+ void cycleAdvance();
/*
* [Logic.cpp] Functions from Logic.GS
@@ -249,8 +300,8 @@ public:
void logicInit();
void logic(); // Keeps time, handles win and lose conditions, then general logic
void restartLogic(); // This is the actual logic init
- int logicFreeze(); // Overcomplicated way to check if game over or level over
- int getLevel(); // Literally just return _level...
+ int logicFreeze(); // Overcomplicated way to check if game over or level over
+ int getLevel(); // Literally just return _level...
void gameOverDisplay();
void gameOver();
void levelOver();
@@ -259,7 +310,32 @@ public:
* [Misc.cpp] Functions from Misc
*/
- //void Misc::textPrint(int num);
+ // Misc
+ void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
+ void delay4(int j); // || /4
+ void delay8(int j); // || /8
+ void miscInit();
+ void setRandomSeed();
+ void getRandom();
+ void myDelay();
+
+ // Text printing
+ void textPrint(int num);
+ void textSub();
+ void textEnd();
+ void textMiddle();
+ void textBeginning();
+ void yesNo();
+
+ // Input related
+ void buttonPressed();
+ void firePressed();
+
+ // Screen related
+ void inside(int p, int p2, int a);
+ void insideRect(int p, int r);
+ void updateHitGuage();
+
/*
* [Compression.cpp] Functions from Compression.GS
@@ -270,7 +346,7 @@ public:
// Subroutines called by unCompress
void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
- int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
+ int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]);
void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 284245ff6ad..79a8dfc8460 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -26,56 +26,10 @@
* considered part of the same process.
*/
-#include "common/debug.h"
-#include "common/error.h"
-#include "common/events.h"
-
#include "immortal/immortal.h"
-#include "immortal/disk.h"
-#include "immortal/compression.h"
namespace Immortal {
-/*
- *
- * ----- -----
- * ----- Main Functions -----
- * ----- -----
- *
- */
-
-Common::ErrorCode ImmortalEngine::main() {
- Common::Event event;
- g_system->getEventManager()->pollEvent(event);
-
- userIO();
- noNetwork();
- pollKeys();
- logic();
- pollKeys();
- if (logicFreeze() == 0) {
- drawUniv();
- pollKeys();
- fixColors();
- copyToScreen();
- pollKeys();
- }
-
- return Common::kNoError;
-}
-
-void ImmortalEngine::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
- g_system->delayMillis(j * 56);
-}
-
-void ImmortalEngine::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
- g_system->delayMillis(j * 14);
-}
-
-void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
- g_system->delayMillis(j * 7);
-}
-
/*
*
* ----- -----
@@ -85,37 +39,27 @@ void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
*/
void ImmortalEngine::drawUniv() {
- /* The byte buffer for the screen (_screenBuff) has one byte for
- * every pixel, with the resolution of the game being 320x200.
- * For a bitmap like the window frame, all we need to do is
- * extract the pixel out of each nyble (half byte) of the data,
- * by looping over it one row at a time.
- */
- // Apply the window frame to the buffer
- _window.seek(0);
- byte pixel;
- int pos;
- for (int y = 0; y < _resV; y++) {
- for (int x = 0; x < _resH; x += 2) {
- pos = (y * _resH) + x;
- pixel = _window.readByte();
- _screenBuff[pos] = (pixel & kMask8High) >> 4;
- _screenBuff[pos + 1] = pixel & kMask8Low;
- }
- }
+ // drawBackground() = draw floor parts of leftmask rightmask and maskers
+ // addRows() = add rows to drawitem array
+ // addSprites() = add all active sprites that are in the viewport, into a list that will be sorted by priority
+ // sortDrawItems() = sort said items
+ // drawItems() = draw the items over the background
+
+ // To start constructing the screem, we start with the frame as the base
+ memcpy(_screenBuff, _window, kScreenSize);
/* copyRectToSurface will apply the screenbuffer to the ScummVM surface.
* We want to do 320 bytes per scanline, at location (0,0), with a
* size of 320x200.
*/
- _mainSurface->copyRectToSurface(_screenBuff, _resH, 0, 0, _resH, _resV);
+ _mainSurface->copyRectToSurface(_screenBuff, kResH, 0, 0, kResH, kResV);
}
void ImmortalEngine::copyToScreen() {
if (_draw == 1) {
- g_system->copyRectToScreen((byte *)_mainSurface->getPixels(), _resH, 0, 0, _resH, _resV);
+ g_system->copyRectToScreen((byte *)_mainSurface->getPixels(), kResH, 0, 0, kResH, kResV);
g_system->updateScreen();
}
}
@@ -130,32 +74,174 @@ void ImmortalEngine::clearScreen() {
}
}
+void ImmortalEngine::whiteScreen() {
+ //fill the visible screen with black pixels by drawing a rectangle
+
+ //rect(32, 20, 256, 128, 13)
+}
+
+void ImmortalEngine::mungeBM() {}
+void ImmortalEngine::blit() {}
+void ImmortalEngine::blit40() {}
+void ImmortalEngine::sBlit() {}
+void ImmortalEngine::scroll() {}
+
+
/*
*
- * ----- -----
- * ----- File Loading -----
- * ----- -----
+ * ----- -----
+ * ----- Asset Init -----
+ * ----- -----
*
*/
+void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p) {
+ if (_numSprites != kMaxSprites) {
+ if (x >= (kScreenW + kMaxSpriteLeft)) {
+ x |= kMaskHigh; // Make it negative
+ }
+ _sprites[_numSprites]._X = (x << 1) + _viewPortX;
+
+ if (y >= (kMaxSpriteAbove + kScreenH)) {
+ y |= kMaskHigh;
+ }
+ _sprites[_numSprites]._Y = (y << 1) + _viewPortY;
+
+ if (p >= 0x80) {
+ p |= kMaskHigh;
+ }
+ _sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
+
+ _sprites[_numSprites]._frame = frame;
+ _sprites[_numSprites]._dSprite = &_dataSprites[n];
+ _sprites[_numSprites]._on = 1;
+ _numSprites += 1;
+
+ } else {
+ debug("Max sprites reached beeeeeep!!");
+ }
+
+}
+
+void ImmortalEngine::clearSprites() {
+ // Just sets the 'active' flag on all possible sprites to 0
+ for (int i = 0; i < kMaxSprites; i++) {
+ _sprites[i]._on = 0;
+ }
+}
+
void ImmortalEngine::loadSprites() {
- // Load MoreSprites.spr
+ /* This is a bit weird, so I'll explain.
+ * In the source, this routine loads the files onto the heap, and then
+ * goes through a table of sprites in the form file_index, sprite_num, center_x, center_y.
+ * It uses file_index to get a pointer to the start of the file on the heap,
+ * which it then uses to set the center x/y variables in the file itself.
+ * ie. file_pointer[file_index]+((sprite_num<<3)+4) = center_x.
+ * We aren't going to have the sprite properties inside the file data, so instead
+ * we have an array of all game sprites _dataSprites which is indexed
+ * soley by a sprite number now. This also means that a sprite itself has a reference to
+ * a datasprite, instead of the sprite index and separate the file pointer. Datasprite
+ * is what needs the file, so that's where the pointer is. The index isn't used by
+ * the sprite or datasprite themselves, so it isn't a member of either of them.
+ */
+
+ Common::String spriteNames[] = {"MORESPRITES.SPR", "NORLAC.SPR", "POWWOW.SPR", "TURRETS.SPR",
+ "WORM.SPR", "IANSPRITES.SPR", "LAST.SPR", "DOORSPRITES.SPR",
+ "GENSPRITES.SPR", "DRAGON.SPR", "MORDAMIR.SPR", "FLAMES.SPR",
+ "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "ULINDOR.SPR",
+ "SPIDER.SPR", "DRAG.SPR"};
+
+ Common::SeekableReadStream *files[19];
+
+ // Number of sprites in each file
+ int spriteNum[] = {10, 5, 7, 10, 4, 6, 3, 10, 5, 3, 2, 1, 3, 2, 9, 10, 9, 10, 9};
+
+ // Pairs of (x,y) for each sprite
+ // Should probably have made this a 2d array, oops
+ uint8 centerXY[] = {16,56, 16,32, 27,39, 16,16, 32,16, 34,83, 28,37, 8,12, 8,19, 24,37,
+ /* Norlac */ 46,18, 40,0, 8,13, 32,48, 32,40,
+ /* Powwow */ 53,43, 28,37, 27,37, 26,30, 26,30, 26,29, 28,25,
+ /* Turrets */ 34,42, 28,37, 24,32, 32,56, 26,56, 8,48, 8,32, 8,14, 8,24, 32,44,
+ /* Worm */ 20,65, 25,46, 9,56, 20,53,
+ /* Iansprites */ 24,50, 32,52, 32,53, 32,52, 40,16, 40,16,
+ /* Last */ 32,56, 24,32, 24,36,
+ /* Doorsprites */ 0,64, 4,49, 18,49, 18,56, 24,32, 24,16, 24,56, 24,32, 24,32, 36,32,
+ /* Gensprites */ 16,44, 16,28, 32,24, 34,45, 20,28,
+ /* Dragon */ 24,93, 32,48, 0,64,
+ /* Mordamir */ 104,104, 30,30,
+ /* Flames */ 64,0,
+ /* Rope */ 0,80, 32,52, 32,40,
+ /* Rescue */ 0,112, 0,112,
+ /* Troll */ 28,38, 28,37, 28,37, 31,38, 28,37, 25,39, 28,37, 28,37, 28,37,
+ /* Goblin */ 28,38, 30,38, 26,37, 30,38, 26,37, 26,37, 26,37, 26,37, 26,36, 44,32,
+ /* Ulindor */ 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42,
+ /* Spider */ 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44,
+ /* Drag */ 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36};
+
+ // Load all sprite files
+ for (int i = 0; i < 19; i++) {
+ files[i] = loadIFF(spriteNames[i]);
+ }
+ // s = current sprite index, f = current file index, n = current number of sprites for this file
+ int s = 0;
+ for (int f = 0; f < 19; f++) {
+ for (int n = 0; n < (spriteNum[f] * 2); n += 2, s++) {
+ DataSprite d;
+ _dataSprites[s] = d;
+ setSpriteCenter(files[f], s, centerXY[s * 2], centerXY[(s * 2) + 1]);
+ }
+ }
}
void ImmortalEngine::loadWindow() {
// Initialize the window bitmap
- if (!_window.open("WINDOWS.BM")) {
+ Common::File f;
+ _window = new byte[kScreenSize];
+
+ if (f.open("WINDOWS.BM")) {
+
+ /* The byte buffer for the screen (_screenBuff) has one byte for
+ * every pixel, with the resolution of the game being 320x200.
+ * For a bitmap like the window frame, all we need to do is
+ * extract the pixel out of each nyble (half byte) of the data,
+ * by looping over it one row at a time.
+ */
+
+ byte pixel;
+ int pos;
+ for (int y = 0; y < kResV; y++) {
+ for (int x = 0; x < kResH; x += 2) {
+ pos = (y * kResH) + x;
+ pixel = f.readByte();
+ _window[pos] = (pixel & kMask8High) >> 4;
+ _window[pos + 1] = pixel & kMask8Low;
+ }
+ }
+
+ // Now that the bitmap is processed and stored in a byte buffer, we can close the file
+ f.close();
+
+ } else {
+ // Should probably give an error or something here
debug("oh nose :(");
}
}
void ImmortalEngine::loadFont() {
- // Initialize the font sprite
- if (!_font.open("FONT.SPR")) {
+ // Initialize the font data sprite
+ Common::SeekableReadStream *f = loadIFF("FONT.SPR");
+
+ DataSprite d;
+ _dataSprites[kFont] = d;
+
+ if (f) {
+ setSpriteCenter(f, kFont, 16, 0);
+ } else {
debug("oh nose :(");
}
+
}
Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
@@ -193,7 +279,7 @@ Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
// Compressed files have a 12 byte header before the data
f.seek(12);
- return Compression::unCompress(&f, len);
+ return unCompress(&f, len);
}
byte *out = (byte *)malloc(f.size());
@@ -229,8 +315,8 @@ void ImmortalEngine::loadPalette() {
// The palettes are stored at a particular location in the disk, this just grabs them
Common::File d;
d.open("IMMORTAL.dsk");
+
d.seek(kPaletteOffset);
-
d.read(_palDefault, 32);
d.read(_palWhite, 32);
d.read(_palBlack, 32);
@@ -249,9 +335,10 @@ void ImmortalEngine::setColors(uint16 pal[]) {
// Green is already the correct size, being the second nyble (00G0)
// Red is in the first nyble of the high byte, so it needs to move right by 4 bits (0R00 -> 00R0)
// Blue is the first nyble of the first byte, so it needs to move left by 4 bits (000B -> 00B0)
- _palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4);
- _palRGB[(i * 3) + 1] = ((pal[i] & kMaskGreen));
- _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) << 4;
+ // We also need to repeat the bits so that the colour is the same proportion of 255 as it is 15
+ _palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4) | ((pal[i] & kMaskRed) >> 8);
+ _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4) ;
+ _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
}
}
// Palette index to update first is 0, and there are 16 colours to update
@@ -336,7 +423,7 @@ void ImmortalEngine::fade(uint16 pal[], int dir, int delay) {
uint16 target[16];
uint16 count;
- // Originally used a branch, but this is functionally identical and much nicer
+ // Originally used a branch, but this is functionally identical and much cleaner
count = dir * 256;
while ((count >= 0) && (count <= 256)) {
@@ -354,10 +441,22 @@ void ImmortalEngine::fadeOut(int j) {
fade(_palDefault, 1, j);
}
+void ImmortalEngine::normalFadeOut() {
+ fadeOut(15);
+}
+
+void ImmortalEngine::slowFadeOut() {
+ fadeOut(28);
+}
+
void ImmortalEngine::fadeIn(int j) {
fade(_palDefault, 0, j);
}
+void ImmortalEngine::normalFadeIn() {
+ fadeIn(15);
+}
+
// These two can probably be removed since the extra call in C doesn't have the setup needed in ASM
void ImmortalEngine::useBlack() {
setColors(_palBlack);
@@ -388,12 +487,25 @@ void ImmortalEngine::useDim() {
void ImmortalEngine::userIO() {}
void ImmortalEngine::pollKeys() {}
void ImmortalEngine::noNetwork() {}
+void ImmortalEngine::keyTraps() {}
+void ImmortalEngine::blit8() {}
+void ImmortalEngine::getInput() {}
+void ImmortalEngine::addKeyBuffer() {}
+void ImmortalEngine::clearKeyBuff() {}
+
+
+/*
+ *
+ * ----- -----
+ * ----- Sound/Music Functions -----
+ * ----- -----
+ *
+ */
void ImmortalEngine::loadSingles(Common::String songName) {
debug("%s", songName.c_str());
}
-void ImmortalEngine::clearSprites() {}
-void ImmortalEngine::keyTraps() {}
+
} // namespace Immortal
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 8a1fd3a7474..750e59a399b 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -19,27 +19,50 @@
*
*/
-#include "common/debug.h"
-#include "common/error.h"
-#include "common/events.h"
-
#include "immortal/immortal.h"
namespace Immortal {
+// There's no way this routine needs to still be here. In fact I'm not sure it needed to be in the game anyway?
+int ImmortalEngine::getLevel() {
+ return _level;
+}
+
void ImmortalEngine::logicInit() {
- debug("init logic here");
+ _titlesShown = 0;
+ _time = 0;
+ _promoting = 0;
+ _restart = 1;
+ //level_initAtStartOfGameOnly
+ _lastCertLen = 0;
}
void ImmortalEngine::logic() {
+ _time += 1;
+ // if time overflows the counter, inc the high byte? What the heck...
+
}
void ImmortalEngine::restartLogic() {
}
int ImmortalEngine::logicFreeze() {
- return 0;
+ // Very silly way of checking if the level is over and/or the game is over
+ int g = _gameOverFlag | _levelOver;
+ return (g ^ 1) >> 1;
}
+void ImmortalEngine::gameOverDisplay() {
+ _themePaused |= 2;
+ //text_print(kGameOverString)
+}
+
+void ImmortalEngine::gameOver() {
+ _gameOverFlag = 1;
+}
+
+void ImmortalEngine::levelOver() {
+ _levelOver = 1;
+}
} // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
new file mode 100644
index 00000000000..3457ccb0a3e
--- /dev/null
+++ b/engines/immortal/misc.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 "immortal/immortal.h"
+
+namespace Immortal {
+
+/*
+ *
+ * ----- -----
+ * ----- Main Functions -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
+ g_system->delayMillis(j * 56);
+}
+
+void ImmortalEngine::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
+ g_system->delayMillis(j * 14);
+}
+
+void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
+ g_system->delayMillis(j * 7);
+}
+
+void ImmortalEngine::miscInit() {}
+void ImmortalEngine::setRandomSeed() {}
+void ImmortalEngine::getRandom() {}
+void ImmortalEngine::myDelay() {}
+
+
+/*
+ *
+ * ----- -----
+ * ----- Text Printing -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::textPrint(int num) {}
+void ImmortalEngine::textSub() {}
+void ImmortalEngine::textEnd() {}
+void ImmortalEngine::textMiddle() {}
+void ImmortalEngine::textBeginning() {}
+void ImmortalEngine::yesNo() {}
+
+
+/*
+ *
+ * ----- -----
+ * ----- Input Related -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::buttonPressed() {}
+void ImmortalEngine::firePressed() {}
+
+
+/*
+ *
+ * ----- -----
+ * ----- Screen Related -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::inside(int p, int p2, int a) {}
+void ImmortalEngine::insideRect(int p, int r) {}
+void ImmortalEngine::updateHitGuage() {}
+
+
+
+
+
+} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index b3ba49aaff9..fa12eb2f94f 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -6,7 +6,10 @@ MODULE_OBJS = \
metaengine.o \
compression.o \
kernal.o \
- logic.o
+ logic.o \
+ sprites.o \
+ misc.o \
+ cycle.o
# This module can be built as a plugin
ifeq ($(ENABLE_IMMORTAL), DYNAMIC_PLUGIN)
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
new file mode 100644
index 00000000000..a385db5bd17
--- /dev/null
+++ b/engines/immortal/sprite_list.h
@@ -0,0 +1,203 @@
+/* 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 IMMORTAL_SPRITE_LIST_H
+#define IMMORTAL_SPRITE_LIST_H
+
+namespace Immortal {
+
+enum SpriteName {
+ // Moresprites 10
+ kCandle,
+ kWow,
+ kAnaVanish,
+ kSink,
+ kTrapdoor,
+ kWizPhant,
+ kVanish,
+ kShadow,
+ kSlime,
+ kSlimeDeath,
+
+ // Norlac 5
+ kBridge,
+ kVortex,
+ kBubble,
+ kNorlac,
+ kNolac2,
+
+ // Powwow 7
+ kPlanners,
+ kUgh,
+ kIsDevoured,
+ kIsBadCrawl,
+ kIsGoodCrawl,
+ kLeg,
+ kIsWebbed,
+
+ // Turrets 10
+ kSleep,
+ kShrink,
+ kLocksmith,
+ kAnaGlimpse,
+ kMadKing,
+ kTorch,
+ kPipe,
+ kProjectile,
+ kKnife,
+ kAnaHug,
+
+ // Worm 4
+ kWorm0,
+ kWorm1,
+ kSpike,
+ kIsSpiked,
+
+ // Iansprites 6
+ kMurder,
+ kWizCrawlUp,
+ kWizLight,
+ kWizBattle,
+ kDown,
+ kNorlacDown,
+
+ // Lastsprites 3
+ kWaterLadder,
+ kPulledDown,
+ kSpill,
+
+ // Doorsprites 10
+ kDoor,
+ kTele,
+ kBomb,
+ kTorched,
+ kLadderTop,
+ kSecret,
+ kLadderBottom,
+ kSlipped,
+ kGoblinSlipped,
+ kFlame,
+
+ // General 5
+ kArrow,
+ kSpark,
+ kObject,
+ kBigBurst,
+ kBeam,
+
+ // Mordamir 3
+ kLight,
+ kMord,
+ kDragMask,
+
+ // Dragon2 2
+ kDFlames,
+ kThroat,
+
+ // Dragon 1
+ kDragon,
+
+ // Rope 3
+ kChop,
+ kHead,
+ kNurse,
+
+ // Rescue 2
+ kRescue1,
+ kRescue2,
+
+ // Troll 9
+ kTroll0,
+ kTroll1,
+ kTroll2,
+ kTroll3,
+ kTroll4,
+ kTroll5,
+ kTroll6,
+ kTroll7,
+ kTroll8,
+
+ // Goblin 10
+ kGoblin0,
+ kGoblin1,
+ kGoblin2,
+ kGoblin3,
+ kGoblin4,
+ kGoblin5,
+ kGoblin6,
+ kGoblin7,
+ kGoblin8,
+ kGoblin9,
+
+ //Ulindor 9
+ kUlindor0,
+ kUlindor1,
+ kUlindor2,
+ kUlindor3,
+ kUlindor4,
+ kUlindor5,
+ kUlindor6,
+ kUlindor7,
+ kUlindor8,
+
+ //Spider 10
+ kSpider0,
+ kSpider1,
+ kSpider2,
+ kSpider3,
+ kSpider4,
+ kSpider5,
+ kSpider6,
+ kSpider7,
+ kSpider8,
+ kSpider9,
+
+ //Drag 9
+ kDrag0,
+ kDrag1,
+ kDrag2,
+ kDrag3,
+ kDrag4,
+ kDrag5,
+ kDrag6,
+ kDrag7,
+ kDrag8,
+
+ // Font
+ kFont
+};
+
+} // namespace immortal
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
new file mode 100644
index 00000000000..0329cf6a420
--- /dev/null
+++ b/engines/immortal/sprites.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 "immortal/immortal.h"
+
+namespace Immortal {
+
+/*
+ *
+ * ----- -----
+ * ----- Main Functions -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::setSpriteCenter(Common::SeekableReadStream *f, int num, uint8 cenX, uint8 cenY) {
+ // Very simple, just initialize what we can of the data sprite
+ _dataSprites[num]._cenX = cenX;
+ _dataSprites[num]._cenY = cenY;
+ _dataSprites[num]._file = f;
+}
+
+
+
+
+
+
+} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Commit: c5181c75defb6ca4ac1217a67e56761960e043ef
https://github.com/scummvm/scummvm/commit/c5181c75defb6ca4ac1217a67e56761960e043ef
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Indentation spaces -> tabs
Changed paths:
engines/immortal/compression.cpp
engines/immortal/credits.pl
engines/immortal/cycle.cpp
engines/immortal/disk.cpp
engines/immortal/disk.h
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/metaengine.cpp
engines/immortal/misc.cpp
engines/immortal/sprite_list.h
engines/immortal/sprites.cpp
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index 5a9dba9ffa5..aa0b2eae8cf 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -30,233 +30,233 @@
namespace Immortal {
Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int srcLen) {
- /* Note: this function does not seek() in the file, which means
- * that if there is a header on the data, the expectation is that
- * seek() was already used to move past the header before this function.
- */
-
- // If the source data has no length, we certainly do not want to decompress it
- if (srcLen == 0) {
- return nullptr;
- }
-
- /* This will be the dynamically re-allocated writeable memory stream.
- * We do not want it to be deleted from scope, as this location is where
- * the readstream being returned will point to.
- */
- Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::NO);
-
- // The 20k bytes of memory that compression gets allocated to work with for the dictionary and the stack of chars
- uint16 start[0x4000]; // Really needs a better name, remember to do this future me
- uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
- byte stack[0x4000]; // Stack of chars to be stored
-
- // These are the main variables we'll need for this
- uint16 findEmpty;
- uint16 code; // Needs to be ASL to index with
- uint16 inputCode;
- uint16 finalChar;
- uint16 myCode; // Silly name is silly
- uint16 oldCode;
- uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
- uint16 evenOdd = 0;
- uint16 topStack = 0;
-
- byte outByte; // If only we could SEP #$20 like the 65816
-
- setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k
- bool carry = true; // This will represent the carry flag so we can make this a clean loop
-
- code = getInputCode(carry, src, srcLen, evenOdd); // Get the first code
- if (carry == false) {
- return nullptr; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here so we might as well
- }
-
- finalChar = code;
- oldCode = code;
- myCode = code;
-
- outByte = code & kMaskLow;
- dstW.writeByte(outByte); // Take just the lower byte and write it the output
-
- // :nextcode
- while (carry == true) {
-
- code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
- if (carry == true) {
-
- index = code << 1;
- inputCode = code;
- myCode = code;
-
- // Split up the conditional statement to be easier to follow
- uint16 cond;
- cond = start[index] & kMaskLast;
- cond |= ptk[index];
-
- if ((cond & kMaskHigh) == 0) { // Empty code
- index = topStack;
- outByte = finalChar & kMaskLow;
- stack[index] = outByte;
- topStack++;
- myCode = oldCode;
- }
-
- // :nextsymbol
- index = myCode << 1;
- while (index >= 0x200) {
- myCode = start[index] & kMask12Bit;
- outByte = ptk[index] & kMaskLow;
- index = topStack;
- stack[index] = outByte;
- topStack++;
- index = myCode << 1;
- }
-
- // :singlechar
- finalChar = (myCode >> 1);
- outByte = finalChar & kMaskLow;
- dstW.writeByte(outByte);
-
- // :dump
- while (topStack != 0xFFFF) { // Dump the chars on the stack into the output file
- outByte = stack[topStack] & kMaskLow;
- dstW.writeByte(outByte);
- topStack--;
- }
-
- topStack = 0;
- code = getMember(oldCode, finalChar, findEmpty, start, ptk);
- oldCode = inputCode;
- }
-
- }
-
- /* Return a readstream with a pointer to the data in the write stream.
- * This one we do want to dispose after using, because it will be in the scope of the engine itself
- */
- return new Common::MemoryReadStream(dstW.getData(), dstW.size(), DisposeAfterUse::YES);
+ /* Note: this function does not seek() in the file, which means
+ * that if there is a header on the data, the expectation is that
+ * seek() was already used to move past the header before this function.
+ */
+
+ // If the source data has no length, we certainly do not want to decompress it
+ if (srcLen == 0) {
+ return nullptr;
+ }
+
+ /* This will be the dynamically re-allocated writeable memory stream.
+ * We do not want it to be deleted from scope, as this location is where
+ * the readstream being returned will point to.
+ */
+ Common::MemoryWriteStreamDynamic dstW(DisposeAfterUse::NO);
+
+ // The 20k bytes of memory that compression gets allocated to work with for the dictionary and the stack of chars
+ uint16 start[0x4000]; // Really needs a better name, remember to do this future me
+ uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
+ byte stack[0x4000]; // Stack of chars to be stored
+
+ // These are the main variables we'll need for this
+ uint16 findEmpty;
+ uint16 code; // Needs to be ASL to index with
+ uint16 inputCode;
+ uint16 finalChar;
+ uint16 myCode; // Silly name is silly
+ uint16 oldCode;
+ uint16 index; // The Y register was used to index the byte array's, this will sort of take its place
+ uint16 evenOdd = 0;
+ uint16 topStack = 0;
+
+ byte outByte; // If only we could SEP #$20 like the 65816
+
+ setupDictionary(start, ptk, findEmpty); // Clear the dictionary and also set findEmpty to 8k
+ bool carry = true; // This will represent the carry flag so we can make this a clean loop
+
+ code = getInputCode(carry, src, srcLen, evenOdd); // Get the first code
+ if (carry == false) {
+ return nullptr; // This is essentially the same as the first error check, but the source returns an error code and didn't even check it here so we might as well
+ }
+
+ finalChar = code;
+ oldCode = code;
+ myCode = code;
+
+ outByte = code & kMaskLow;
+ dstW.writeByte(outByte); // Take just the lower byte and write it the output
+
+ // :nextcode
+ while (carry == true) {
+
+ code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
+ if (carry == true) {
+
+ index = code << 1;
+ inputCode = code;
+ myCode = code;
+
+ // Split up the conditional statement to be easier to follow
+ uint16 cond;
+ cond = start[index] & kMaskLast;
+ cond |= ptk[index];
+
+ if ((cond & kMaskHigh) == 0) { // Empty code
+ index = topStack;
+ outByte = finalChar & kMaskLow;
+ stack[index] = outByte;
+ topStack++;
+ myCode = oldCode;
+ }
+
+ // :nextsymbol
+ index = myCode << 1;
+ while (index >= 0x200) {
+ myCode = start[index] & kMask12Bit;
+ outByte = ptk[index] & kMaskLow;
+ index = topStack;
+ stack[index] = outByte;
+ topStack++;
+ index = myCode << 1;
+ }
+
+ // :singlechar
+ finalChar = (myCode >> 1);
+ outByte = finalChar & kMaskLow;
+ dstW.writeByte(outByte);
+
+ // :dump
+ while (topStack != 0xFFFF) { // Dump the chars on the stack into the output file
+ outByte = stack[topStack] & kMaskLow;
+ dstW.writeByte(outByte);
+ topStack--;
+ }
+
+ topStack = 0;
+ code = getMember(oldCode, finalChar, findEmpty, start, ptk);
+ oldCode = inputCode;
+ }
+
+ }
+
+ /* Return a readstream with a pointer to the data in the write stream.
+ * This one we do want to dispose after using, because it will be in the scope of the engine itself
+ */
+ return new Common::MemoryReadStream(dstW.getData(), dstW.size(), DisposeAfterUse::YES);
}
void ImmortalEngine::setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty) {
- // Clear the whole dictionary
- for (int i = 0x3FFF; i >= 0; i--) {
- start[i] = 0;
- ptk[i] = 0;
- }
-
- // Set the initial 256 bytes to be value 256, these are the characters without extensions
- for (int i = 255; i >= 0; i--) {
- ptk[i] = 256;
- }
-
- // This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
- findEmpty = 0x8000;
+ // Clear the whole dictionary
+ for (int i = 0x3FFF; i >= 0; i--) {
+ start[i] = 0;
+ ptk[i] = 0;
+ }
+
+ // Set the initial 256 bytes to be value 256, these are the characters without extensions
+ for (int i = 255; i >= 0; i--) {
+ ptk[i] = 256;
+ }
+
+ // This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
+ findEmpty = 0x8000;
}
int ImmortalEngine::getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd) {
- // Check if we're at the end of the file
- if (srcLen == 0) {
- carry = false;
- return 0;
- }
-
- uint16 c;
- if (evenOdd != 0) { // Odd
- srcLen--;
- evenOdd--;
- c = (src->readUint16BE() >> 3) & 0x00FE; // & #-1-1
- } else { // Even
- srcLen -= 2;
- evenOdd++;
- c = (src->readUint16BE() & kMask12Bit) << 1;
- src->seek(-1, SEEK_CUR);
- }
- return c;
+ // Check if we're at the end of the file
+ if (srcLen == 0) {
+ carry = false;
+ return 0;
+ }
+
+ uint16 c;
+ if (evenOdd != 0) { // Odd
+ srcLen--;
+ evenOdd--;
+ c = (src->readUint16BE() >> 3) & 0x00FE; // & #-1-1
+ } else { // Even
+ srcLen -= 2;
+ evenOdd++;
+ c = (src->readUint16BE() & kMask12Bit) << 1;
+ src->seek(-1, SEEK_CUR);
+ }
+ return c;
}
uint16 ImmortalEngine::getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
- // This function is effectively void, as the return value is only used in compression
-
- // k and codeW are local variables with the value of oldCode and finalChar
-
- uint16 hash;
- uint16 tmp;
- bool ag = true;
-
- hash = (k << 3) ^ k;
- hash = (hash << 1) ^ codeW;
- hash <<= 1;
-
- hash = (hash >= 0x200) ? hash : hash + 0x200;
-
- uint16 a = start[hash] & 0x0F00;
- uint16 b = ptk[hash] & kMaskHigh;
- if (a | b) {
- start[hash] = codeW;
- ptk[hash] = k | 0x100;
- return ptk[hash];
- }
-
- // This loop is a bit wacky, due to the way the jumps were stuctured in the source
- while (ag == true) {
- if ((start[hash] & kMask12Bit) == codeW) {
- if ((ptk[hash] & kMaskLow) == k) {
- return hash >> 1;
- }
- }
-
- tmp = start[hash] & kMaskLast;
- if (tmp == 0) {
- // I've separated this into it's own function for the sake of this loop being readable
- appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
- ag = false;
-
- } else {
- hash = xba(ptk[hash]);
- hash = (hash & kMaskLow) | (tmp >> 4);
- hash <<= 1;
- }
- }
- return hash;
+ // This function is effectively void, as the return value is only used in compression
+
+ // k and codeW are local variables with the value of oldCode and finalChar
+
+ uint16 hash;
+ uint16 tmp;
+ bool ag = true;
+
+ hash = (k << 3) ^ k;
+ hash = (hash << 1) ^ codeW;
+ hash <<= 1;
+
+ hash = (hash >= 0x200) ? hash : hash + 0x200;
+
+ uint16 a = start[hash] & 0x0F00;
+ uint16 b = ptk[hash] & kMaskHigh;
+ if (a | b) {
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+ return ptk[hash];
+ }
+
+ // This loop is a bit wacky, due to the way the jumps were stuctured in the source
+ while (ag == true) {
+ if ((start[hash] & kMask12Bit) == codeW) {
+ if ((ptk[hash] & kMaskLow) == k) {
+ return hash >> 1;
+ }
+ }
+
+ tmp = start[hash] & kMaskLast;
+ if (tmp == 0) {
+ // I've separated this into it's own function for the sake of this loop being readable
+ appendList(codeW, k, hash, findEmpty, start, ptk, tmp);
+ ag = false;
+
+ } else {
+ hash = xba(ptk[hash]);
+ hash = (hash & kMaskLow) | (tmp >> 4);
+ hash <<= 1;
+ }
+ }
+ return hash;
}
void ImmortalEngine::appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp) {
- uint16 prev;
- uint16 link;
-
- prev = hash;
- if (hash >= 0x200) {
- setupDictionary(start, ptk, findEmpty);
-
- } else {
- bool found = false;
- while (found == false) {
- hash -= 2;
- if (hash >= 0x200) {
- setupDictionary(start, ptk, findEmpty);
- found = true;
- }
-
- // Split up the conditional statement to be easier to follow
- uint16 cond;
- cond = start[hash] & kMaskLast;
- cond |= ptk[hash];
-
- if ((cond & kMaskHigh) == 0) {
- findEmpty = hash;
- start[hash] = codeW;
- ptk[hash] = k | 0x100;
-
- link = hash >> 1;
-
- ptk[prev] = (link << 8) | (ptk[prev] & kMaskLow);
- //start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
- start[prev] |= (link >> 4) & kMaskLast;
- found = true;
- }
- }
- }
+ uint16 prev;
+ uint16 link;
+
+ prev = hash;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+
+ } else {
+ bool found = false;
+ while (found == false) {
+ hash -= 2;
+ if (hash >= 0x200) {
+ setupDictionary(start, ptk, findEmpty);
+ found = true;
+ }
+
+ // Split up the conditional statement to be easier to follow
+ uint16 cond;
+ cond = start[hash] & kMaskLast;
+ cond |= ptk[hash];
+
+ if ((cond & kMaskHigh) == 0) {
+ findEmpty = hash;
+ start[hash] = codeW;
+ ptk[hash] = k | 0x100;
+
+ link = hash >> 1;
+
+ ptk[prev] = (link << 8) | (ptk[prev] & kMaskLow);
+ //start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
+ start[prev] |= (link >> 4) & kMaskLast;
+ found = true;
+ }
+ }
+ }
}
} // namespace immortal
diff --git a/engines/immortal/credits.pl b/engines/immortal/credits.pl
index c9fc8d7affd..59b13cf59b4 100644
--- a/engines/immortal/credits.pl
+++ b/engines/immortal/credits.pl
@@ -1,4 +1,3 @@
begin_section("Immortal");
add_person("Michael Hayman", "Quote58", "");
- #add_person("Eugene ?", "JoeFish", "");
end_section();
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index 1eb43200ea2..c02570f93d6 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -25,7 +25,7 @@ namespace Immortal {
void ImmortalEngine::cycleNew() {}
int ImmortalEngine::getCycleChr() {
- return 0;
+ return 0;
}
void ImmortalEngine::cycleFreeAll() {}
void ImmortalEngine::cycleGetFile() {}
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index f92501183c1..340a07d409e 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -29,28 +29,28 @@ namespace Immortal {
// --- ProDOSFile methods ---
ProDOSFile::ProDOSFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk)
- : _type(type)
- , _totalBlocks(tBlk)
- , _eof(eof)
- , _blockPtr(bPtr)
- , _disk(disk) {
- strcpy(_name, name);
- }
+ : _type(type)
+ , _totalBlocks(tBlk)
+ , _eof(eof)
+ , _blockPtr(bPtr)
+ , _disk(disk) {
+ strcpy(_name, name);
+ }
/* For debugging purposes, this prints the meta data of a file */
void ProDOSFile::printInfo() {
- debug("File: %s", _name);
- debug("Type: %02X", _type);
- debug("data: %d", _blockPtr);
- debug("Blocks: %d", _totalBlocks);
- debug("Size: %u\n", _eof);
+ debug("File: %s", _name);
+ debug("Type: %02X", _type);
+ debug("data: %d", _blockPtr);
+ debug("Blocks: %d", _totalBlocks);
+ debug("Size: %u\n", _eof);
}
/* For Common::Archive, this method just returns a string of the name */
Common::String ProDOSFile::getName() const {
- return Common::String(_name);
+ return Common::String(_name);
}
/* This method is used to get a single block of data from the disk,
@@ -61,9 +61,9 @@ Common::String ProDOSFile::getName() const {
void ProDOSFile::getDataBlock(byte *memOffset, int offset, int size) const {
- // All this method needs to do is read (size) of data at (offset) into (memOffset)
- _disk->seek(offset);
- _disk->read(memOffset, size);
+ // All this method needs to do is read (size) of data at (offset) into (memOffset)
+ _disk->seek(offset);
+ _disk->read(memOffset, size);
}
/* To put together a sapling file, you need to loop through the index
@@ -73,31 +73,31 @@ void ProDOSFile::getDataBlock(byte *memOffset, int offset, int size) const {
*/
int ProDOSFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
- int dataSize; // For most of the blocks, this will be kBlockSize, but the last one will be the calculated remainder
- int readSize = 0; // This keeps track of the new pointer position to read data to, by updating the size of data read last
- int dataOffset; // Where in the disk to read from
- int diskPos; // Current position of cursor
-
- for (int i = 0; i < blockNum; i++) {
- dataSize = (i == (blockNum - 1)) ? rem : ProDOSDisk::kBlockSize;
- dataOffset = _disk->readByte(); // Low byte is first
-
- /* The cursor needs to know where to get the next pointer from in the index block,
- * but it also needs to jump to the offset of data to read it, so we need to preserve
- * the position in the index block it was in before.
- */
- diskPos = _disk->pos();
-
- _disk->skip(255); // The high bytes are stored at the end of the block instead because reasons???
- dataOffset = (dataOffset + (_disk->readByte() << 8)) * ProDOSDisk::kBlockSize; // High byte is second
-
- getDataBlock(memOffset + readSize, dataOffset, dataSize);
- readSize += dataSize;
-
- // And now we resume the position before this call
- _disk->seek(diskPos);
- }
- return readSize;
+ int dataSize; // For most of the blocks, this will be kBlockSize, but the last one will be the calculated remainder
+ int readSize = 0; // This keeps track of the new pointer position to read data to, by updating the size of data read last
+ int dataOffset; // Where in the disk to read from
+ int diskPos; // Current position of cursor
+
+ for (int i = 0; i < blockNum; i++) {
+ dataSize = (i == (blockNum - 1)) ? rem : ProDOSDisk::kBlockSize;
+ dataOffset = _disk->readByte(); // Low byte is first
+
+ /* The cursor needs to know where to get the next pointer from in the index block,
+ * but it also needs to jump to the offset of data to read it, so we need to preserve
+ * the position in the index block it was in before.
+ */
+ diskPos = _disk->pos();
+
+ _disk->skip(255); // The high bytes are stored at the end of the block instead because reasons???
+ dataOffset = (dataOffset + (_disk->readByte() << 8)) * ProDOSDisk::kBlockSize; // High byte is second
+
+ getDataBlock(memOffset + readSize, dataOffset, dataSize);
+ readSize += dataSize;
+
+ // And now we resume the position before this call
+ _disk->seek(diskPos);
+ }
+ return readSize;
}
/* Extracting file data is a little tricky, as the blocks are spread out in the disk. There are 3 types
@@ -109,62 +109,62 @@ int ProDOSFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
Common::SeekableReadStream *ProDOSFile::createReadStream() const {
- // We know the total byte size of the data, so we can allocate the full amount right away
- byte *finalData = (byte *)malloc(_eof);
-
- /* For a seed, this is a direct pointer to data. For a sapling it is an index file,
- * and for a tree it is a master index file.
- */
- int indexBlock = _blockPtr * ProDOSDisk::kBlockSize;
-
- /* For a sapling or tree, the size needs to be calculated, as they are made from multiple blocks.
- * _totalBlocks *includes* the index block, so the blocks before the oef block are _totalBlocks-2
- */
- int remainder = _eof - ((_totalBlocks - 2) * ProDOSDisk::kBlockSize);
-
- // For a seed file, the end of file value is also the size in the block, because it's just the one block
- if (_type == kFileTypeSeed) {
- getDataBlock(finalData, indexBlock, _eof);
-
- } else if (_type == kFileTypeSapling) {
- _disk->seek(indexBlock);
- parseIndexBlock(finalData, _totalBlocks - 1, remainder);
-
- } else {
- // If it's not a seed and not a sapling, it's a tree.
- _disk->seek(indexBlock);
-
- /* A sapling can have an index block of up to 256, so if it is a tree,
- * that means it has more than 256 blocks
- */
- int indexNum = (_totalBlocks - 1) / 256;
- int indexNumR = (_totalBlocks - 1) % 256;
-
- /* However, to know how many index blocks there are, we need to know the remainder
- * so we can figure out if it's ex. 2 index blocks, or 2 and some portion of a 3rd
- */
- indexNum += indexNumR;
- int blockNum;
- int indexOffset;
- int readSize = 0;
-
- // Now we can loop through the master index file, parsing the individual index files similar to a sapling
- for (int i = 0; i < indexNum; i++) {
- blockNum = (i == indexNum - 1) ? indexNumR : 256;
-
- indexOffset = _disk->readByte();
- int diskPos = _disk->pos();
-
- _disk->skip(255);
- indexOffset = (indexOffset + (_disk->readByte() << 8)) * ProDOSDisk::kBlockSize;
-
- _disk->seek(indexOffset);
- readSize += parseIndexBlock(finalData + readSize, blockNum, remainder);
-
- _disk->seek(diskPos);
- }
- }
- return new Common::MemoryReadStream(finalData, _eof, DisposeAfterUse::YES);
+ // We know the total byte size of the data, so we can allocate the full amount right away
+ byte *finalData = (byte *)malloc(_eof);
+
+ /* For a seed, this is a direct pointer to data. For a sapling it is an index file,
+ * and for a tree it is a master index file.
+ */
+ int indexBlock = _blockPtr * ProDOSDisk::kBlockSize;
+
+ /* For a sapling or tree, the size needs to be calculated, as they are made from multiple blocks.
+ * _totalBlocks *includes* the index block, so the blocks before the oef block are _totalBlocks-2
+ */
+ int remainder = _eof - ((_totalBlocks - 2) * ProDOSDisk::kBlockSize);
+
+ // For a seed file, the end of file value is also the size in the block, because it's just the one block
+ if (_type == kFileTypeSeed) {
+ getDataBlock(finalData, indexBlock, _eof);
+
+ } else if (_type == kFileTypeSapling) {
+ _disk->seek(indexBlock);
+ parseIndexBlock(finalData, _totalBlocks - 1, remainder);
+
+ } else {
+ // If it's not a seed and not a sapling, it's a tree.
+ _disk->seek(indexBlock);
+
+ /* A sapling can have an index block of up to 256, so if it is a tree,
+ * that means it has more than 256 blocks
+ */
+ int indexNum = (_totalBlocks - 1) / 256;
+ int indexNumR = (_totalBlocks - 1) % 256;
+
+ /* However, to know how many index blocks there are, we need to know the remainder
+ * so we can figure out if it's ex. 2 index blocks, or 2 and some portion of a 3rd
+ */
+ indexNum += indexNumR;
+ int blockNum;
+ int indexOffset;
+ int readSize = 0;
+
+ // Now we can loop through the master index file, parsing the individual index files similar to a sapling
+ for (int i = 0; i < indexNum; i++) {
+ blockNum = (i == indexNum - 1) ? indexNumR : 256;
+
+ indexOffset = _disk->readByte();
+ int diskPos = _disk->pos();
+
+ _disk->skip(255);
+ indexOffset = (indexOffset + (_disk->readByte() << 8)) * ProDOSDisk::kBlockSize;
+
+ _disk->seek(indexOffset);
+ readSize += parseIndexBlock(finalData + readSize, blockNum, remainder);
+
+ _disk->seek(diskPos);
+ }
+ }
+ return new Common::MemoryReadStream(finalData, _eof, DisposeAfterUse::YES);
}
// --- ProDOSDisk methods ---
@@ -175,54 +175,54 @@ Common::SeekableReadStream *ProDOSFile::createReadStream() const {
*/
void ProDOSDisk::getDate(Date *d, uint16 date) {
- d->_day = date & 0x001f;
- d->_month = (date & 0x01e0) >> 5;
- d->_year = (date & 0xfe00) >> 9;
+ d->_day = date & 0x001f;
+ d->_month = (date & 0x01e0) >> 5;
+ d->_year = (date & 0xfe00) >> 9;
}
void ProDOSDisk::getTime(Time *t, uint16 time) {
- t->_minute = time & 0x003f;
- t->_hour = (time & 0x1f00) >> 8;
+ t->_minute = time & 0x003f;
+ t->_hour = (time & 0x1f00) >> 8;
}
/* Adds most of the header data to a directory header struct */
void ProDOSDisk::getHeader(DirHeader *h) {
- /* The type and nameLen fields are stored in the same byte,
- * so we need to split the byte, and shift the high bits to
- * make it readable as an int
- */
- uint8 tempByte = _disk.readByte();
- h->_nameLen = tempByte & 0xf;
- h->_type = (tempByte & 0xf0) >> 4;
-
- /* The name field is stored in 15 bytes with no null character (unused chars default to 0).
- * To make it easier to use the name, we will add a terminator regardless.
- */
- _disk.read(h->_name, 15);
- h->_name[15] = 0;
- _disk.read(h->_reserved, 8);
-
- // The time and date can be decompressed into structs right away
- getDate(&(h->_date), _disk.readUint16LE());
- getTime(&(h->_time), _disk.readUint16LE());
-
- h->_ver = _disk.readByte();
- h->_minVer = _disk.readByte();
- h->_access = _disk.readByte();
- h->_entryLen = _disk.readByte();
- h->_entriesPerBlock = _disk.readByte();
- h->_fileCount = _disk.readUint16LE();
+ /* The type and nameLen fields are stored in the same byte,
+ * so we need to split the byte, and shift the high bits to
+ * make it readable as an int
+ */
+ uint8 tempByte = _disk.readByte();
+ h->_nameLen = tempByte & 0xf;
+ h->_type = (tempByte & 0xf0) >> 4;
+
+ /* The name field is stored in 15 bytes with no null character (unused chars default to 0).
+ * To make it easier to use the name, we will add a terminator regardless.
+ */
+ _disk.read(h->_name, 15);
+ h->_name[15] = 0;
+ _disk.read(h->_reserved, 8);
+
+ // The time and date can be decompressed into structs right away
+ getDate(&(h->_date), _disk.readUint16LE());
+ getTime(&(h->_time), _disk.readUint16LE());
+
+ h->_ver = _disk.readByte();
+ h->_minVer = _disk.readByte();
+ h->_access = _disk.readByte();
+ h->_entryLen = _disk.readByte();
+ h->_entriesPerBlock = _disk.readByte();
+ h->_fileCount = _disk.readUint16LE();
}
/* Since a subdirectory header is mostly the same a volume header, we will reuse the code where we can */
void ProDOSDisk::getDirectoryHeader(DirHeader *h) {
- getHeader(h);
- h->_parentBlockPtr = _disk.readUint16LE();
- h->_parentEntryIndex = _disk.readByte();
- h->_parentEntryLen = _disk.readUint16LE();
+ getHeader(h);
+ h->_parentBlockPtr = _disk.readUint16LE();
+ h->_parentEntryIndex = _disk.readByte();
+ h->_parentEntryLen = _disk.readUint16LE();
}
/* This is a little sneaky, but since the bulk of the header is the same, we're just going to pretend the volume header
@@ -230,40 +230,40 @@ void ProDOSDisk::getDirectoryHeader(DirHeader *h) {
*/
void ProDOSDisk::getVolumeHeader(VolHeader *h) {
- getHeader((DirHeader *)h);
- h->_bitmapPtr = _disk.readUint16LE();
- h->_volBlocks = _disk.readUint16LE();
- _volBlocks = h->_volBlocks;
+ getHeader((DirHeader *)h);
+ h->_bitmapPtr = _disk.readUint16LE();
+ h->_volBlocks = _disk.readUint16LE();
+ _volBlocks = h->_volBlocks;
}
/* Getting a file entry header is very similar to getting a header, but with different data. */
void ProDOSDisk::getFileEntry(FileEntry *f) {
- uint8 tempByte = _disk.readByte();
- f->_nameLen = tempByte & 0xf;
- f->_type = (tempByte & 0xf0) >> 4;
+ uint8 tempByte = _disk.readByte();
+ f->_nameLen = tempByte & 0xf;
+ f->_type = (tempByte & 0xf0) >> 4;
- _disk.read(f->_name, 15);
- f->_name[15] = 0;
- f->_ext = _disk.readByte();
- f->_blockPtr = _disk.readUint16LE();
- f->_totalBlocks = _disk.readUint16LE();
+ _disk.read(f->_name, 15);
+ f->_name[15] = 0;
+ f->_ext = _disk.readByte();
+ f->_blockPtr = _disk.readUint16LE();
+ f->_totalBlocks = _disk.readUint16LE();
- // The file size in bytes is stored as a long (3 bytes), lowest to highest
- f->_eof = _disk.readByte() + (_disk.readByte() << 8) + (_disk.readByte() << 16);
+ // The file size in bytes is stored as a long (3 bytes), lowest to highest
+ f->_eof = _disk.readByte() + (_disk.readByte() << 8) + (_disk.readByte() << 16);
- getDate(&(f->_date), _disk.readUint16LE());
- getTime(&(f->_time), _disk.readUint16LE());
+ getDate(&(f->_date), _disk.readUint16LE());
+ getTime(&(f->_time), _disk.readUint16LE());
- f->_ver = _disk.readByte();
- f->_minVer = _disk.readByte();
- f->_access = _disk.readByte();
- f->_varUse = _disk.readUint16LE();
+ f->_ver = _disk.readByte();
+ f->_minVer = _disk.readByte();
+ f->_access = _disk.readByte();
+ f->_varUse = _disk.readUint16LE();
- getDate(&(f->_modDate), _disk.readUint16LE());
- getTime(&(f->_modTime), _disk.readUint16LE());
+ getDate(&(f->_modDate), _disk.readUint16LE());
+ getTime(&(f->_modTime), _disk.readUint16LE());
- f->_dirHeadPtr = _disk.readUint16LE();
+ f->_dirHeadPtr = _disk.readUint16LE();
}
/* This is basically a loop based on the number of total files indicated by the header (including deleted file entries),
@@ -274,52 +274,52 @@ void ProDOSDisk::getFileEntry(FileEntry *f) {
*/
void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path) {
- int currPos;
- int parsedFiles = 0;
-
- for (int i = 0; i < h->_fileCount; i++) {
- // When we have read all the files for a given block (_entriesPerBlock), we need to change to the next block of the directory
- if (parsedFiles == h->_entriesPerBlock) {
- parsedFiles = 0;
- _disk.seek(n * kBlockSize);
- p = _disk.readUint16LE();
- n = _disk.readUint16LE();
- }
-
- FileEntry fileEntry;
- getFileEntry(&fileEntry);
- //debug("%s", fileEntry._name);
- parsedFiles++;
- currPos = _disk.pos();
-
- // It is a regular file if (dead < file type < pascal) and the file has a size
- if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
- Common::String fileName = path + fileEntry._name;
- debug("%s", fileName.c_str());
- ProDOSFile *currFile = new ProDOSFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
-
- _files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
- _disk.seek(currPos);
-
- // Otherwise, if it is a subdirectory, we want to explore that subdirectory
- } else if (fileEntry._type == kFileTypeSubDir) {
-
- _disk.seek(fileEntry._blockPtr * kBlockSize);
- debug("--- diving into a subdirectory ---");
-
- uint16 subP = _disk.readUint16LE();
- uint16 subN = _disk.readUint16LE();
- DirHeader subHead;
- getDirectoryHeader(&subHead);
-
- // Give it a temporary new path name by sticking the name of the subdirectory on to the end of the current path
- Common::String subPath = Common::String(path + subHead._name + '/');
- searchDirectory(&subHead, subP, subN, path);
-
- debug("--- surfacing to parent directory ---");
- _disk.seek(currPos);
- }
- }
+ int currPos;
+ int parsedFiles = 0;
+
+ for (int i = 0; i < h->_fileCount; i++) {
+ // When we have read all the files for a given block (_entriesPerBlock), we need to change to the next block of the directory
+ if (parsedFiles == h->_entriesPerBlock) {
+ parsedFiles = 0;
+ _disk.seek(n * kBlockSize);
+ p = _disk.readUint16LE();
+ n = _disk.readUint16LE();
+ }
+
+ FileEntry fileEntry;
+ getFileEntry(&fileEntry);
+ //debug("%s", fileEntry._name);
+ parsedFiles++;
+ currPos = _disk.pos();
+
+ // It is a regular file if (dead < file type < pascal) and the file has a size
+ if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
+ Common::String fileName = path + fileEntry._name;
+ debug("%s", fileName.c_str());
+ ProDOSFile *currFile = new ProDOSFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
+
+ _files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
+ _disk.seek(currPos);
+
+ // Otherwise, if it is a subdirectory, we want to explore that subdirectory
+ } else if (fileEntry._type == kFileTypeSubDir) {
+
+ _disk.seek(fileEntry._blockPtr * kBlockSize);
+ debug("--- diving into a subdirectory ---");
+
+ uint16 subP = _disk.readUint16LE();
+ uint16 subN = _disk.readUint16LE();
+ DirHeader subHead;
+ getDirectoryHeader(&subHead);
+
+ // Give it a temporary new path name by sticking the name of the subdirectory on to the end of the current path
+ Common::String subPath = Common::String(path + subHead._name + '/');
+ searchDirectory(&subHead, subP, subN, path);
+
+ debug("--- surfacing to parent directory ---");
+ _disk.seek(currPos);
+ }
+ }
}
/* The volume bitmap is a bitmap spanning as many blocks as is required to store 1 bit for every
@@ -328,67 +328,67 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
*/
void ProDOSDisk::getVolumeBitmap(VolHeader *h) {
- int currPos = _disk.pos();
- int bitmapSize;
+ int currPos = _disk.pos();
+ int bitmapSize;
- bitmapSize = _volBlocks / 4096;
- if ((_volBlocks % 4096) > 0) {
- bitmapSize++;
- }
+ bitmapSize = _volBlocks / 4096;
+ if ((_volBlocks % 4096) > 0) {
+ bitmapSize++;
+ }
- _volBitmap = (byte *)malloc(bitmapSize * kBlockSize);
- _disk.seek(h->_bitmapPtr * kBlockSize);
- _disk.read(_volBitmap, bitmapSize);
+ _volBitmap = (byte *)malloc(bitmapSize * kBlockSize);
+ _disk.seek(h->_bitmapPtr * kBlockSize);
+ _disk.read(_volBitmap, bitmapSize);
- _disk.seek(currPos);
+ _disk.seek(currPos);
}
/* Gets the volume information and parses the filesystem, adding file objects to a map as it goes */
bool ProDOSDisk::open(const Common::String filename) {
- debug("opening %s", filename.c_str());
+ debug("opening %s", filename.c_str());
- _disk.open(filename);
- _disk.read(_loader1, kBlockSize);
- _disk.read(_loader2, kBlockSize);
+ _disk.open(filename);
+ _disk.read(_loader1, kBlockSize);
+ _disk.read(_loader2, kBlockSize);
- uint16 prev = _disk.readUint16LE(); // This is always going to be 0 for the volume header, but there's also no reason to skip it
- uint16 next = _disk.readUint16LE();
+ uint16 prev = _disk.readUint16LE(); // This is always going to be 0 for the volume header, but there's also no reason to skip it
+ uint16 next = _disk.readUint16LE();
- VolHeader header;
- getVolumeHeader(&header);
- debug("volume name: %s", header._name);
+ VolHeader header;
+ getVolumeHeader(&header);
+ debug("volume name: %s", header._name);
- getVolumeBitmap(&header);
+ getVolumeBitmap(&header);
- Common::String pathName; // This is so that the path name starts blank, and then for every directory searched it adds the directory name to the path
- searchDirectory((DirHeader *)&header, prev, next, pathName);
+ Common::String pathName; // This is so that the path name starts blank, and then for every directory searched it adds the directory name to the path
+ searchDirectory((DirHeader *)&header, prev, next, pathName);
- return true; // When I get to error checking on this, the bool will be useful
+ return true; // When I get to error checking on this, the bool will be useful
}
/* Constructor simply calls open(), and if it is successful it prints a statement */
ProDOSDisk::ProDOSDisk(const Common::String filename) {
- if (open(filename)) {
- debug ("%s has been loaded", filename.c_str());
- }
+ if (open(filename)) {
+ debug ("%s has been loaded", filename.c_str());
+ }
}
/* Destructor closes the disk and clears the map of files */
ProDOSDisk::~ProDOSDisk() {
- _disk.close();
- _files.clear();
- free(_volBitmap); // Should this be free() or delete?
+ _disk.close();
+ _files.clear();
+ free(_volBitmap); // Should this be free() or delete?
}
// --- Common::Archive methods ---
// Very simple, just checks if the dictionary contains the path name
bool ProDOSDisk::hasFile(const Common::Path &path) const {
- Common::String name = path.toString();
- return _files.contains(name);
+ Common::String name = path.toString();
+ return _files.contains(name);
}
/* To create a list of files in the Archive, we define an iterator for the object type
@@ -397,22 +397,22 @@ bool ProDOSDisk::hasFile(const Common::Path &path) const {
*/
int ProDOSDisk::listMembers(Common::ArchiveMemberList &list) const {
- int f = 0;
- Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>>::const_iterator it;
- for (it = _files.begin(); it != _files.end(); ++it) {
- list.push_back(Common::ArchiveMemberList::value_type(it->_value));
- ++f;
- }
- return f;
+ int f = 0;
+ Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>>::const_iterator it;
+ for (it = _files.begin(); it != _files.end(); ++it) {
+ list.push_back(Common::ArchiveMemberList::value_type(it->_value));
+ ++f;
+ }
+ return f;
}
// If the dictionary contains the path name (could probably call hasFile() instead), get the object
const Common::ArchiveMemberPtr ProDOSDisk::getMember(const Common::Path &path) const {
- Common::String name = path.toString();
- if (!_files.contains(name)) {
- return Common::ArchiveMemberPtr();
- }
- return _files.getValOrDefault(name);
+ Common::String name = path.toString();
+ if (!_files.contains(name)) {
+ return Common::ArchiveMemberPtr();
+ }
+ return _files.getValOrDefault(name);
}
/* This method is called on Archive members as it searches for the correct one,
@@ -420,13 +420,13 @@ const Common::ArchiveMemberPtr ProDOSDisk::getMember(const Common::Path &path) c
*/
Common::SeekableReadStream *ProDOSDisk::createReadStreamForMember(const Common::Path &path) const {
- Common::String name = path.toString();
- if (!_files.contains(name)) {
- return nullptr;
- }
- Common::SharedPtr<ProDOSFile> f = _files.getValOrDefault(name);
- f->printInfo();
- return f->createReadStream();
+ Common::String name = path.toString();
+ if (!_files.contains(name)) {
+ return nullptr;
+ }
+ Common::SharedPtr<ProDOSFile> f = _files.getValOrDefault(name);
+ f->printInfo();
+ return f->createReadStream();
}
} // namespace Immortal
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index a6045fc4a68..030fbb00f88 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -38,14 +38,14 @@ namespace Immortal {
// These values define for ProDOS how to read the file entry, and also whether it's a keyblock (if it is a directory header, it's the keyblock of that directory)
enum FileType : char {
- kFileTypeDead = 0,
- kFileTypeSeed = 1,
- kFileTypeSapling = 2,
- kFileTypeTree = 3,
- kFileTypePascal = 4,
- kFileTypeSubDir = 0x0D,
- kFileTypeSubHead = 0x0E,
- kFileTypeVolHead = 0x0F
+ kFileTypeDead = 0,
+ kFileTypeSeed = 1,
+ kFileTypeSapling = 2,
+ kFileTypeTree = 3,
+ kFileTypePascal = 4,
+ kFileTypeSubDir = 0x0D,
+ kFileTypeSubHead = 0x0E,
+ kFileTypeVolHead = 0x0F
};
/* File extensions for all the ProDOS supported file types
@@ -53,25 +53,25 @@ enum FileType : char {
* they can be added to this enum.
*/
enum FileExt {
- kFileExtNull = 0,
- kFileExtBad = 1,
- kFileExtTxt = 4,
- kFileExtBin = 6,
- kFileExtGfx = 8,
- kFileExtDir = 0xF,
- kFileExtDB = 0x19,
- kFileExtWord = 0x1A,
- kFileExtSpread = 0x1B,
- kFileExtSTART = 0xB3,
- kFileExtPascal = 0xEF,
- kFileExtPDCI = 0xF0,
- kFileExtPDRes = 0xF9,
- kFileExtIBProg = 0xFA,
- kFileExtIBVar = 0xFB,
- kFileExtAPSProg = 0xFC,
- kFileExtAPSVar = 0xFD,
- kFileExtEDASM = 0xFE,
- kFileExtSYS = 0xFF
+ kFileExtNull = 0,
+ kFileExtBad = 1,
+ kFileExtTxt = 4,
+ kFileExtBin = 6,
+ kFileExtGfx = 8,
+ kFileExtDir = 0xF,
+ kFileExtDB = 0x19,
+ kFileExtWord = 0x1A,
+ kFileExtSpread = 0x1B,
+ kFileExtSTART = 0xB3,
+ kFileExtPascal = 0xEF,
+ kFileExtPDCI = 0xF0,
+ kFileExtPDRes = 0xF9,
+ kFileExtIBProg = 0xFA,
+ kFileExtIBVar = 0xFB,
+ kFileExtAPSProg = 0xFC,
+ kFileExtAPSVar = 0xFD,
+ kFileExtEDASM = 0xFE,
+ kFileExtSYS = 0xFF
};
/* A ProDOS file simply contains meta data about the file and the ability to
@@ -82,25 +82,25 @@ enum FileExt {
class ProDOSFile : public Common::ArchiveMember {
public:
- ProDOSFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
- ~ProDOSFile() {};
+ ProDOSFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
+ ~ProDOSFile() {};
- // -- These are the Common::ArchiveMember related functions --
- Common::String getName() const override; // Returns _name
- Common::SeekableReadStream *createReadStream() const override; // This is what the archive needs to create a file
- void getDataBlock(byte *memOffset, int offset, int size) const; // Gets data up to the size of a single data block (512 bytes)
- int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const; // Uses getDataBlock() on every pointer in the index file, adding them to byte * memory block
+ // -- These are the Common::ArchiveMember related functions --
+ Common::String getName() const override; // Returns _name
+ Common::SeekableReadStream *createReadStream() const override; // This is what the archive needs to create a file
+ void getDataBlock(byte *memOffset, int offset, int size) const; // Gets data up to the size of a single data block (512 bytes)
+ int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const; // Uses getDataBlock() on every pointer in the index file, adding them to byte * memory block
- // For debugging purposes, just prints the metadata
- void printInfo();
+ // For debugging purposes, just prints the metadata
+ void printInfo();
private:
- char _name[16];
- uint8 _type; // As defined by enum FileType
- uint16 _blockPtr; // Block index in volume of index block or data
- uint16 _totalBlocks;
- uint32 _eof; // End Of File, used generally as size (exception being sparse files)
- Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
+ char _name[16];
+ uint8 _type; // As defined by enum FileType
+ uint16 _blockPtr; // Block index in volume of index block or data
+ uint16 _totalBlocks;
+ uint32 _eof; // End Of File, used generally as size (exception being sparse files)
+ Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
};
/* This class defines the entire disk volume. Upon using the open() method,
@@ -111,102 +111,102 @@ private:
class ProDOSDisk : public Common::Archive {
public:
- static const int kBlockSize = 512; // A ProDOS block is always 512 bytes (should this be an enum?)
+ static const int kBlockSize = 512; // A ProDOS block is always 512 bytes (should this be an enum?)
- ProDOSDisk(const Common::String filename);
- ~ProDOSDisk(); // Frees the memory used in the dictionary and the volume bitmap
+ ProDOSDisk(const Common::String filename);
+ ~ProDOSDisk(); // Frees the memory used in the dictionary and the volume bitmap
- // Called from the constructor, it parses the volume and fills the hashmap with files
- bool open(const Common::String filename);
+ // Called from the constructor, it parses the volume and fills the hashmap with files
+ bool open(const Common::String filename);
- // These are the Common::Archive related methods
- bool hasFile(const Common::Path &path) const override;
- int listMembers(Common::ArchiveMemberList &list) const override;
- const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
- Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
+ // These are the Common::Archive related methods
+ bool hasFile(const Common::Path &path) const override;
+ int listMembers(Common::ArchiveMemberList &list) const override;
+ const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
+ Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
- byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
- byte _loader2[kBlockSize];
+ byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
+ byte _loader2[kBlockSize];
Common::String _name; // Name of volume
Common::File _disk; // The volume file itself
- int _volBlocks; // Total blocks in volume
- byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
+ int _volBlocks; // Total blocks in volume
+ byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDOSFile
- struct Date {
- uint8 _day;
- uint8 _month;
- uint8 _year;
- };
-
- struct Time {
- uint8 _hour;
- uint8 _minute;
- };
-
- struct VolHeader {
- uint8 _type; // Not really important for a volume header, as this will always be F
- uint8 _nameLen;
- char _name[16];
- byte _reserved[8]; // Extra space reserved for possible future uses, not important
- Date _date;
- Time _time;
- uint8 _ver;
- uint8 _minVer; // Should pretty much always be 0 as far as I know
- uint8 _access; // If this ends up useful, there should be an enum for the access values
- uint8 _entryLen; // Always 27 in ProDOS 1.0
- uint8 _entriesPerBlock; // Always 0D in ProDOS 1.0
- uint16 _fileCount; // Number of files across all data blocks in this directory
- uint16 _bitmapPtr; // Block pointer to the keyblock of the bitmap for the entire volume
- uint16 _volBlocks; // Blocks in entire volume
- };
-
- struct DirHeader {
- uint8 _type;
- uint8 _nameLen;
- char _name[16];
- byte _reserved[8];
- Date _date;
- Time _time;
- uint8 _ver;
- uint8 _minVer;
- uint8 _access;
- uint8 _entryLen;
- uint8 _entriesPerBlock;
- uint16 _fileCount;
- uint16 _parentBlockPtr; // These values allow ProDOS to navigate back out of a directory, but they aren't really needed by the class to navigate
- uint8 _parentEntryIndex; // Index in the current directory
- uint8 _parentEntryLen; // This is always 27 in ProDOS 1.0
- };
-
- struct FileEntry {
- uint8 _type; // 0 = inactive, 1-3 = file, 4 = pascal area, 14 = subdirectory, 15 = volume directory
- uint8 _nameLen;
- char _name[16];
- uint8 _ext; // File extension, uses the enum FileExt
- uint16 _blockPtr; // Block pointer to data for seedling, index block for sapling, or master block for tree
- uint16 _totalBlocks; // Really important to remember this is the total *including* the index block
- uint32 _eof; // This is a long (3 bytes, read low to high) value representing the total readable data in a file (unless it's a sparse file, be careful!)
- Date _date;
- Time _time;
- uint8 _ver;
- uint8 _minVer;
- uint8 _access;
- uint16 _varUse;
- Date _modDate;
- Time _modTime;
- uint16 _dirHeadPtr; // Pointer to the key block of the directory that contains this file entry
- };
-
- void getDate(Date *d, uint16 date); // Decompresses the date into a struct
- void getTime(Time *t, uint16 time); // Decompresses the time into a struct
- void getHeader(DirHeader *h); // Adds the main header values to the struct
- void getDirectoryHeader(DirHeader *h); // Uses getHeader and then fills in the values for the parent directory
- void getVolumeHeader(VolHeader *dir); // Uses getHeader and then fills in the volume related information (there is no parent directory to this one)
- void getFileEntry(FileEntry *f); // Adds all of the file entry information to the struct
- void searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path); // Recursively searches all files within a directory, by calling itself for subdirectories
- void getVolumeBitmap(VolHeader *h); // Puts together the volume bitmap
+ struct Date {
+ uint8 _day;
+ uint8 _month;
+ uint8 _year;
+ };
+
+ struct Time {
+ uint8 _hour;
+ uint8 _minute;
+ };
+
+ struct VolHeader {
+ uint8 _type; // Not really important for a volume header, as this will always be F
+ uint8 _nameLen;
+ char _name[16];
+ byte _reserved[8]; // Extra space reserved for possible future uses, not important
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer; // Should pretty much always be 0 as far as I know
+ uint8 _access; // If this ends up useful, there should be an enum for the access values
+ uint8 _entryLen; // Always 27 in ProDOS 1.0
+ uint8 _entriesPerBlock; // Always 0D in ProDOS 1.0
+ uint16 _fileCount; // Number of files across all data blocks in this directory
+ uint16 _bitmapPtr; // Block pointer to the keyblock of the bitmap for the entire volume
+ uint16 _volBlocks; // Blocks in entire volume
+ };
+
+ struct DirHeader {
+ uint8 _type;
+ uint8 _nameLen;
+ char _name[16];
+ byte _reserved[8];
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer;
+ uint8 _access;
+ uint8 _entryLen;
+ uint8 _entriesPerBlock;
+ uint16 _fileCount;
+ uint16 _parentBlockPtr; // These values allow ProDOS to navigate back out of a directory, but they aren't really needed by the class to navigate
+ uint8 _parentEntryIndex; // Index in the current directory
+ uint8 _parentEntryLen; // This is always 27 in ProDOS 1.0
+ };
+
+ struct FileEntry {
+ uint8 _type; // 0 = inactive, 1-3 = file, 4 = pascal area, 14 = subdirectory, 15 = volume directory
+ uint8 _nameLen;
+ char _name[16];
+ uint8 _ext; // File extension, uses the enum FileExt
+ uint16 _blockPtr; // Block pointer to data for seedling, index block for sapling, or master block for tree
+ uint16 _totalBlocks; // Really important to remember this is the total *including* the index block
+ uint32 _eof; // This is a long (3 bytes, read low to high) value representing the total readable data in a file (unless it's a sparse file, be careful!)
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer;
+ uint8 _access;
+ uint16 _varUse;
+ Date _modDate;
+ Time _modTime;
+ uint16 _dirHeadPtr; // Pointer to the key block of the directory that contains this file entry
+ };
+
+ void getDate(Date *d, uint16 date); // Decompresses the date into a struct
+ void getTime(Time *t, uint16 time); // Decompresses the time into a struct
+ void getHeader(DirHeader *h); // Adds the main header values to the struct
+ void getDirectoryHeader(DirHeader *h); // Uses getHeader and then fills in the values for the parent directory
+ void getVolumeHeader(VolHeader *dir); // Uses getHeader and then fills in the volume related information (there is no parent directory to this one)
+ void getFileEntry(FileEntry *f); // Adds all of the file entry information to the struct
+ void searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::String path); // Recursively searches all files within a directory, by calling itself for subdirectories
+ void getVolumeBitmap(VolHeader *h); // Puts together the volume bitmap
};
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index 13856bca0f3..c07bcfdda3d 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -181,20 +181,20 @@ Common::Error ImmortalEngine::run() {
// Main
Common::Event event;
- g_system->getEventManager()->pollEvent(event);
-
- userIO();
- noNetwork();
- pollKeys();
- logic();
- pollKeys();
- if (logicFreeze() == 0) {
- drawUniv();
- pollKeys();
- fixColors();
- copyToScreen();
- pollKeys();
- }
+ g_system->getEventManager()->pollEvent(event);
+
+ userIO();
+ noNetwork();
+ pollKeys();
+ logic();
+ pollKeys();
+ if (logicFreeze() == 0) {
+ drawUniv();
+ pollKeys();
+ fixColors();
+ copyToScreen();
+ pollKeys();
+ }
if (_err != Common::kNoError) {
debug("To err is human, to really screw up you need an Apple IIGS!");
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index b3914ddb512..3c6203e9979 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -55,33 +55,45 @@ namespace Immortal {
// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
enum BitMask16 : uint16 {
- kMaskLow = 0x00FF,
- kMaskHigh = 0xFF00,
- kMaskLast = 0xF000,
- kMaskFirst = 0x000F,
- kMaskHLow = 0x0F00,
- kMaskLHigh = 0x00F0,
- kMaskNeg = 0x8000,
- kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000,
+ kMaskFirst = 0x000F,
+ kMaskHLow = 0x0F00,
+ kMaskLHigh = 0x00F0,
+ kMaskNeg = 0x8000,
+ kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
};
enum BitMask8 : uint8 {
- kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
- kMask8High = 0xF0,
- kMask8Low = 0x0F
+ kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMask8High = 0xF0,
+ kMask8Low = 0x0F
};
-
enum ColourMask : uint16 {
kMaskRed = 0x0F00,
kMaskGreen = 0x00F0,
kMaskBlue = 0x000F
};
+enum InputAction : int {
+ kActionRestart,
+ kActionSound,
+ kActionFire
+};
+
+enum InputDirection : int {
+ kDirectionUp,
+ kDirectionLeft,
+ kDirectionDown,
+ kDirectionRight
+};
+
struct DataSprite {
- uint8 _cenX; // These are the base center positions
- uint8 _cenY;
- byte *_bitmap; // Pointer to actual data
+ uint8 _cenX; // These are the base center positions
+ uint8 _cenY;
+ byte *_bitmap; // Pointer to actual data
Common::SeekableReadStream *_file; // This will likely be removed later
};
@@ -89,15 +101,15 @@ struct Sprite {
int _frame; // Current frame of the cycle
uint16 _X;
uint16 _Y;
- uint16 _on; // 1 = active
+ uint16 _on; // 1 = active
uint16 _priority;
DataSprite *_dSprite;
};
struct Cycle {
DataSprite *_dSprite;
- int _numCycles;
- int *_frames;
+ int _numCycles;
+ int *_frames;
};
struct ImmortalGameDescription;
@@ -159,19 +171,27 @@ public:
* 'global' members
*/
Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
- bool _draw = 0; // Whether the screen should draw this frame
- bool _dim = 0; // Whether the palette is dim
- bool _usingNormal = 0; // Whether the palette is using normal
- int _zero = 0; // No idea what this is yet
- uint8 _gameOverFlag = 0;
- uint8 _levelOver = 0;
- uint8 _themePaused = 0;
- int _level = 0;
- int _titlesShown = 0;
- int _time = 0;
- int _promoting = 0;
- int _restart = 0;
- int _lastCertLen = 0;
+ bool _draw = 0; // Whether the screen should draw this frame
+ bool _dim = 0; // Whether the palette is dim
+ bool _usingNormal = 0; // Whether the palette is using normal
+ int _zero = 0; // No idea what this is yet
+ uint8 _gameOverFlag = 0;
+ uint8 _levelOver = 0;
+ uint8 _themePaused = 0;
+ int _level = 0;
+ int _titlesShown = 0;
+ int _time = 0;
+ int _promoting = 0;
+ int _restart = 0;
+ int _lastCertLen = 0;
+
+ /*
+ * Input members
+ */
+ int _pressedAction;
+ int _heldAction;
+ int _pressedDirection;
+ int _heldDirection;
/*
* Asset related members
@@ -296,6 +316,9 @@ public:
* [Logic.cpp] Functions from Logic.GS
*/
+ // Surface level
+ bool trapKeys(); // Poorly named, this checks if the player wants to restart the game
+
// Misc
void logicInit();
void logic(); // Keeps time, handles win and lose conditions, then general logic
@@ -369,9 +392,9 @@ public:
bool hasFeature(EngineFeature f) const override {
return
- (f == kSupportsLoadingDuringRuntime) ||
- (f == kSupportsSavingDuringRuntime) ||
- (f == kSupportsReturnToLauncher);
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime) ||
+ (f == kSupportsReturnToLauncher);
};
bool canLoadGameStateCurrently() override {
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 79a8dfc8460..043d7065858 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -40,44 +40,44 @@ namespace Immortal {
void ImmortalEngine::drawUniv() {
- // drawBackground() = draw floor parts of leftmask rightmask and maskers
- // addRows() = add rows to drawitem array
- // addSprites() = add all active sprites that are in the viewport, into a list that will be sorted by priority
- // sortDrawItems() = sort said items
- // drawItems() = draw the items over the background
+ // drawBackground() = draw floor parts of leftmask rightmask and maskers
+ // addRows() = add rows to drawitem array
+ // addSprites() = add all active sprites that are in the viewport, into a list that will be sorted by priority
+ // sortDrawItems() = sort said items
+ // drawItems() = draw the items over the background
- // To start constructing the screem, we start with the frame as the base
- memcpy(_screenBuff, _window, kScreenSize);
+ // To start constructing the screem, we start with the frame as the base
+ memcpy(_screenBuff, _window, kScreenSize);
- /* copyRectToSurface will apply the screenbuffer to the ScummVM surface.
- * We want to do 320 bytes per scanline, at location (0,0), with a
- * size of 320x200.
- */
- _mainSurface->copyRectToSurface(_screenBuff, kResH, 0, 0, kResH, kResV);
+ /* copyRectToSurface will apply the screenbuffer to the ScummVM surface.
+ * We want to do 320 bytes per scanline, at location (0,0), with a
+ * size of 320x200.
+ */
+ _mainSurface->copyRectToSurface(_screenBuff, kResH, 0, 0, kResH, kResV);
}
void ImmortalEngine::copyToScreen() {
- if (_draw == 1) {
- g_system->copyRectToScreen((byte *)_mainSurface->getPixels(), kResH, 0, 0, kResH, kResV);
- g_system->updateScreen();
- }
+ if (_draw == 1) {
+ g_system->copyRectToScreen((byte *)_mainSurface->getPixels(), kResH, 0, 0, kResH, kResV);
+ g_system->updateScreen();
+ }
}
void ImmortalEngine::clearScreen() {
- //fill the visible screen with black pixels by drawing a rectangle
+ //fill the visible screen with black pixels by drawing a rectangle
- //rect(32, 20, 256, 128, 0)
-
- if ((_dontResetColors & kMaskLow) == 0) {
- useNormal();
- }
+ //rect(32, 20, 256, 128, 0)
+
+ if ((_dontResetColors & kMaskLow) == 0) {
+ useNormal();
+ }
}
void ImmortalEngine::whiteScreen() {
- //fill the visible screen with black pixels by drawing a rectangle
+ //fill the visible screen with black pixels by drawing a rectangle
- //rect(32, 20, 256, 128, 13)
+ //rect(32, 20, 256, 128, 13)
}
void ImmortalEngine::mungeBM() {}
@@ -96,195 +96,195 @@ void ImmortalEngine::scroll() {}
*/
void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p) {
- if (_numSprites != kMaxSprites) {
- if (x >= (kScreenW + kMaxSpriteLeft)) {
- x |= kMaskHigh; // Make it negative
- }
- _sprites[_numSprites]._X = (x << 1) + _viewPortX;
-
- if (y >= (kMaxSpriteAbove + kScreenH)) {
- y |= kMaskHigh;
- }
- _sprites[_numSprites]._Y = (y << 1) + _viewPortY;
-
- if (p >= 0x80) {
- p |= kMaskHigh;
- }
- _sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
-
- _sprites[_numSprites]._frame = frame;
- _sprites[_numSprites]._dSprite = &_dataSprites[n];
- _sprites[_numSprites]._on = 1;
- _numSprites += 1;
-
- } else {
- debug("Max sprites reached beeeeeep!!");
- }
+ if (_numSprites != kMaxSprites) {
+ if (x >= (kScreenW + kMaxSpriteLeft)) {
+ x |= kMaskHigh; // Make it negative
+ }
+ _sprites[_numSprites]._X = (x << 1) + _viewPortX;
+
+ if (y >= (kMaxSpriteAbove + kScreenH)) {
+ y |= kMaskHigh;
+ }
+ _sprites[_numSprites]._Y = (y << 1) + _viewPortY;
+
+ if (p >= 0x80) {
+ p |= kMaskHigh;
+ }
+ _sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
+
+ _sprites[_numSprites]._frame = frame;
+ _sprites[_numSprites]._dSprite = &_dataSprites[n];
+ _sprites[_numSprites]._on = 1;
+ _numSprites += 1;
+
+ } else {
+ debug("Max sprites reached beeeeeep!!");
+ }
}
void ImmortalEngine::clearSprites() {
- // Just sets the 'active' flag on all possible sprites to 0
- for (int i = 0; i < kMaxSprites; i++) {
- _sprites[i]._on = 0;
- }
+ // Just sets the 'active' flag on all possible sprites to 0
+ for (int i = 0; i < kMaxSprites; i++) {
+ _sprites[i]._on = 0;
+ }
}
void ImmortalEngine::loadSprites() {
- /* This is a bit weird, so I'll explain.
- * In the source, this routine loads the files onto the heap, and then
- * goes through a table of sprites in the form file_index, sprite_num, center_x, center_y.
- * It uses file_index to get a pointer to the start of the file on the heap,
- * which it then uses to set the center x/y variables in the file itself.
- * ie. file_pointer[file_index]+((sprite_num<<3)+4) = center_x.
- * We aren't going to have the sprite properties inside the file data, so instead
- * we have an array of all game sprites _dataSprites which is indexed
- * soley by a sprite number now. This also means that a sprite itself has a reference to
- * a datasprite, instead of the sprite index and separate the file pointer. Datasprite
- * is what needs the file, so that's where the pointer is. The index isn't used by
- * the sprite or datasprite themselves, so it isn't a member of either of them.
- */
-
- Common::String spriteNames[] = {"MORESPRITES.SPR", "NORLAC.SPR", "POWWOW.SPR", "TURRETS.SPR",
- "WORM.SPR", "IANSPRITES.SPR", "LAST.SPR", "DOORSPRITES.SPR",
- "GENSPRITES.SPR", "DRAGON.SPR", "MORDAMIR.SPR", "FLAMES.SPR",
- "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "ULINDOR.SPR",
- "SPIDER.SPR", "DRAG.SPR"};
-
- Common::SeekableReadStream *files[19];
-
- // Number of sprites in each file
- int spriteNum[] = {10, 5, 7, 10, 4, 6, 3, 10, 5, 3, 2, 1, 3, 2, 9, 10, 9, 10, 9};
-
- // Pairs of (x,y) for each sprite
- // Should probably have made this a 2d array, oops
- uint8 centerXY[] = {16,56, 16,32, 27,39, 16,16, 32,16, 34,83, 28,37, 8,12, 8,19, 24,37,
- /* Norlac */ 46,18, 40,0, 8,13, 32,48, 32,40,
- /* Powwow */ 53,43, 28,37, 27,37, 26,30, 26,30, 26,29, 28,25,
- /* Turrets */ 34,42, 28,37, 24,32, 32,56, 26,56, 8,48, 8,32, 8,14, 8,24, 32,44,
- /* Worm */ 20,65, 25,46, 9,56, 20,53,
- /* Iansprites */ 24,50, 32,52, 32,53, 32,52, 40,16, 40,16,
- /* Last */ 32,56, 24,32, 24,36,
- /* Doorsprites */ 0,64, 4,49, 18,49, 18,56, 24,32, 24,16, 24,56, 24,32, 24,32, 36,32,
- /* Gensprites */ 16,44, 16,28, 32,24, 34,45, 20,28,
- /* Dragon */ 24,93, 32,48, 0,64,
- /* Mordamir */ 104,104, 30,30,
- /* Flames */ 64,0,
- /* Rope */ 0,80, 32,52, 32,40,
- /* Rescue */ 0,112, 0,112,
- /* Troll */ 28,38, 28,37, 28,37, 31,38, 28,37, 25,39, 28,37, 28,37, 28,37,
- /* Goblin */ 28,38, 30,38, 26,37, 30,38, 26,37, 26,37, 26,37, 26,37, 26,36, 44,32,
- /* Ulindor */ 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42,
- /* Spider */ 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44,
- /* Drag */ 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36};
-
- // Load all sprite files
- for (int i = 0; i < 19; i++) {
- files[i] = loadIFF(spriteNames[i]);
- }
-
- // s = current sprite index, f = current file index, n = current number of sprites for this file
- int s = 0;
- for (int f = 0; f < 19; f++) {
- for (int n = 0; n < (spriteNum[f] * 2); n += 2, s++) {
- DataSprite d;
- _dataSprites[s] = d;
- setSpriteCenter(files[f], s, centerXY[s * 2], centerXY[(s * 2) + 1]);
- }
- }
+ /* This is a bit weird, so I'll explain.
+ * In the source, this routine loads the files onto the heap, and then
+ * goes through a table of sprites in the form file_index, sprite_num, center_x, center_y.
+ * It uses file_index to get a pointer to the start of the file on the heap,
+ * which it then uses to set the center x/y variables in the file itself.
+ * ie. file_pointer[file_index]+((sprite_num<<3)+4) = center_x.
+ * We aren't going to have the sprite properties inside the file data, so instead
+ * we have an array of all game sprites _dataSprites which is indexed
+ * soley by a sprite number now. This also means that a sprite itself has a reference to
+ * a datasprite, instead of the sprite index and separate the file pointer. Datasprite
+ * is what needs the file, so that's where the pointer is. The index isn't used by
+ * the sprite or datasprite themselves, so it isn't a member of either of them.
+ */
+
+ Common::String spriteNames[] = {"MORESPRITES.SPR", "NORLAC.SPR", "POWWOW.SPR", "TURRETS.SPR",
+ "WORM.SPR", "IANSPRITES.SPR", "LAST.SPR", "DOORSPRITES.SPR",
+ "GENSPRITES.SPR", "DRAGON.SPR", "MORDAMIR.SPR", "FLAMES.SPR",
+ "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "ULINDOR.SPR",
+ "SPIDER.SPR", "DRAG.SPR"};
+
+ Common::SeekableReadStream *files[19];
+
+ // Number of sprites in each file
+ int spriteNum[] = {10, 5, 7, 10, 4, 6, 3, 10, 5, 3, 2, 1, 3, 2, 9, 10, 9, 10, 9};
+
+ // Pairs of (x,y) for each sprite
+ // Should probably have made this a 2d array, oops
+ uint8 centerXY[] = {16,56, 16,32, 27,39, 16,16, 32,16, 34,83, 28,37, 8,12, 8,19, 24,37,
+ /* Norlac */ 46,18, 40,0, 8,13, 32,48, 32,40,
+ /* Powwow */ 53,43, 28,37, 27,37, 26,30, 26,30, 26,29, 28,25,
+ /* Turrets */ 34,42, 28,37, 24,32, 32,56, 26,56, 8,48, 8,32, 8,14, 8,24, 32,44,
+ /* Worm */ 20,65, 25,46, 9,56, 20,53,
+ /* Iansprites */ 24,50, 32,52, 32,53, 32,52, 40,16, 40,16,
+ /* Last */ 32,56, 24,32, 24,36,
+ /* Doorsprites */ 0,64, 4,49, 18,49, 18,56, 24,32, 24,16, 24,56, 24,32, 24,32, 36,32,
+ /* Gensprites */ 16,44, 16,28, 32,24, 34,45, 20,28,
+ /* Dragon */ 24,93, 32,48, 0,64,
+ /* Mordamir */ 104,104, 30,30,
+ /* Flames */ 64,0,
+ /* Rope */ 0,80, 32,52, 32,40,
+ /* Rescue */ 0,112, 0,112,
+ /* Troll */ 28,38, 28,37, 28,37, 31,38, 28,37, 25,39, 28,37, 28,37, 28,37,
+ /* Goblin */ 28,38, 30,38, 26,37, 30,38, 26,37, 26,37, 26,37, 26,37, 26,36, 44,32,
+ /* Ulindor */ 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42,
+ /* Spider */ 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44,
+ /* Drag */ 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36};
+
+ // Load all sprite files
+ for (int i = 0; i < 19; i++) {
+ files[i] = loadIFF(spriteNames[i]);
+ }
+
+ // s = current sprite index, f = current file index, n = current number of sprites for this file
+ int s = 0;
+ for (int f = 0; f < 19; f++) {
+ for (int n = 0; n < (spriteNum[f] * 2); n += 2, s++) {
+ DataSprite d;
+ _dataSprites[s] = d;
+ setSpriteCenter(files[f], s, centerXY[s * 2], centerXY[(s * 2) + 1]);
+ }
+ }
}
void ImmortalEngine::loadWindow() {
- // Initialize the window bitmap
- Common::File f;
- _window = new byte[kScreenSize];
-
- if (f.open("WINDOWS.BM")) {
-
- /* The byte buffer for the screen (_screenBuff) has one byte for
- * every pixel, with the resolution of the game being 320x200.
- * For a bitmap like the window frame, all we need to do is
- * extract the pixel out of each nyble (half byte) of the data,
- * by looping over it one row at a time.
- */
-
- byte pixel;
- int pos;
- for (int y = 0; y < kResV; y++) {
- for (int x = 0; x < kResH; x += 2) {
- pos = (y * kResH) + x;
- pixel = f.readByte();
- _window[pos] = (pixel & kMask8High) >> 4;
- _window[pos + 1] = pixel & kMask8Low;
- }
- }
-
- // Now that the bitmap is processed and stored in a byte buffer, we can close the file
- f.close();
-
- } else {
- // Should probably give an error or something here
- debug("oh nose :(");
- }
+ // Initialize the window bitmap
+ Common::File f;
+ _window = new byte[kScreenSize];
+
+ if (f.open("WINDOWS.BM")) {
+
+ /* The byte buffer for the screen (_screenBuff) has one byte for
+ * every pixel, with the resolution of the game being 320x200.
+ * For a bitmap like the window frame, all we need to do is
+ * extract the pixel out of each nyble (half byte) of the data,
+ * by looping over it one row at a time.
+ */
+
+ byte pixel;
+ int pos;
+ for (int y = 0; y < kResV; y++) {
+ for (int x = 0; x < kResH; x += 2) {
+ pos = (y * kResH) + x;
+ pixel = f.readByte();
+ _window[pos] = (pixel & kMask8High) >> 4;
+ _window[pos + 1] = pixel & kMask8Low;
+ }
+ }
+
+ // Now that the bitmap is processed and stored in a byte buffer, we can close the file
+ f.close();
+
+ } else {
+ // Should probably give an error or something here
+ debug("oh nose :(");
+ }
}
void ImmortalEngine::loadFont() {
- // Initialize the font data sprite
- Common::SeekableReadStream *f = loadIFF("FONT.SPR");
+ // Initialize the font data sprite
+ Common::SeekableReadStream *f = loadIFF("FONT.SPR");
- DataSprite d;
- _dataSprites[kFont] = d;
+ DataSprite d;
+ _dataSprites[kFont] = d;
- if (f) {
- setSpriteCenter(f, kFont, 16, 0);
- } else {
- debug("oh nose :(");
- }
+ if (f) {
+ setSpriteCenter(f, kFont, 16, 0);
+ } else {
+ debug("oh nose :(");
+ }
}
Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
- Common::File f;
- if (!f.open(fileName)) {
- debug("*surprised pikachu face*");
- return nullptr;
- }
-
- /* This isn't the most efficient way to do this (could just read a 32bit uint and compare),
- * but this makes it more obvious what the source was doing. We want to know if the 4 bytes
- * at file[8] are 'C' 'M' 'P' '0', so this grabs just the ascii bits of those 4 bytes,
- * allowing us to directly compare it with 'CMP0'.
- */
- char compSig[] = "CMP0";
- char sig[] = "0000";
-
- f.seek(8);
-
- for (int i = 0; i < 4; i++) {
- sig[i] = f.readByte() & kMaskASCII;
- }
-
- if (strcmp(sig, compSig) == 0) {
- debug("compressed");
-
- /* The size of the compressed data is stored in the header, but doesn't
- * account for the FORM part?? Also, **technically** this is a uint32LE,
- * but the engine itself actually /doesn't/ use it like that. It only
- * decrements the first word (although it compares against the second half,
- * as if it is expecting that to be zero? It's a little bizarre).
- */
- f.seek(6);
- int len = f.readUint16LE() - 4;
-
- // Compressed files have a 12 byte header before the data
- f.seek(12);
- return unCompress(&f, len);
- }
-
- byte *out = (byte *)malloc(f.size());
- f.read(out, f.size());
- return new Common::MemoryReadStream(out, f.size(), DisposeAfterUse::YES);
+ Common::File f;
+ if (!f.open(fileName)) {
+ debug("*surprised pikachu face*");
+ return nullptr;
+ }
+
+ /* This isn't the most efficient way to do this (could just read a 32bit uint and compare),
+ * but this makes it more obvious what the source was doing. We want to know if the 4 bytes
+ * at file[8] are 'C' 'M' 'P' '0', so this grabs just the ascii bits of those 4 bytes,
+ * allowing us to directly compare it with 'CMP0'.
+ */
+ char compSig[] = "CMP0";
+ char sig[] = "0000";
+
+ f.seek(8);
+
+ for (int i = 0; i < 4; i++) {
+ sig[i] = f.readByte() & kMaskASCII;
+ }
+
+ if (strcmp(sig, compSig) == 0) {
+ debug("compressed");
+
+ /* The size of the compressed data is stored in the header, but doesn't
+ * account for the FORM part?? Also, **technically** this is a uint32LE,
+ * but the engine itself actually /doesn't/ use it like that. It only
+ * decrements the first word (although it compares against the second half,
+ * as if it is expecting that to be zero? It's a little bizarre).
+ */
+ f.seek(6);
+ int len = f.readUint16LE() - 4;
+
+ // Compressed files have a 12 byte header before the data
+ f.seek(12);
+ return unCompress(&f, len);
+ }
+
+ byte *out = (byte *)malloc(f.size());
+ f.read(out, f.size());
+ return new Common::MemoryReadStream(out, f.size(), DisposeAfterUse::YES);
}
@@ -312,167 +312,167 @@ Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
*/
void ImmortalEngine::loadPalette() {
- // The palettes are stored at a particular location in the disk, this just grabs them
- Common::File d;
- d.open("IMMORTAL.dsk");
-
- d.seek(kPaletteOffset);
- d.read(_palDefault, 32);
- d.read(_palWhite, 32);
- d.read(_palBlack, 32);
- d.read(_palDim, 32);
-
- d.close();
+ // The palettes are stored at a particular location in the disk, this just grabs them
+ Common::File d;
+ d.open("IMMORTAL.dsk");
+
+ d.seek(kPaletteOffset);
+ d.read(_palDefault, 32);
+ d.read(_palWhite, 32);
+ d.read(_palBlack, 32);
+ d.read(_palDim, 32);
+
+ d.close();
}
void ImmortalEngine::setColors(uint16 pal[]) {
- // The RGB palette is 3 bytes per entry, and each byte is a colour
- for (int i = 0; i < 16; i++) {
-
- // The palette gets masked so it can update only specific indexes and uses FFFF to do so. However the check is simply for a negative
- if (pal[i] < kMaskNeg) {
-
- // Green is already the correct size, being the second nyble (00G0)
- // Red is in the first nyble of the high byte, so it needs to move right by 4 bits (0R00 -> 00R0)
- // Blue is the first nyble of the first byte, so it needs to move left by 4 bits (000B -> 00B0)
- // We also need to repeat the bits so that the colour is the same proportion of 255 as it is 15
- _palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4) | ((pal[i] & kMaskRed) >> 8);
- _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4) ;
- _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
- }
- }
- // Palette index to update first is 0, and there are 16 colours to update
- g_system->getPaletteManager()->setPalette(_palRGB, 0, 16);
- g_system->updateScreen();
+ // The RGB palette is 3 bytes per entry, and each byte is a colour
+ for (int i = 0; i < 16; i++) {
+
+ // The palette gets masked so it can update only specific indexes and uses FFFF to do so. However the check is simply for a negative
+ if (pal[i] < kMaskNeg) {
+
+ // Green is already the correct size, being the second nyble (00G0)
+ // Red is in the first nyble of the high byte, so it needs to move right by 4 bits (0R00 -> 00R0)
+ // Blue is the first nyble of the first byte, so it needs to move left by 4 bits (000B -> 00B0)
+ // We also need to repeat the bits so that the colour is the same proportion of 255 as it is 15
+ _palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4) | ((pal[i] & kMaskRed) >> 8);
+ _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4) ;
+ _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
+ }
+ }
+ // Palette index to update first is 0, and there are 16 colours to update
+ g_system->getPaletteManager()->setPalette(_palRGB, 0, 16);
+ g_system->updateScreen();
}
void ImmortalEngine::fixColors() {
- // Pretty silly that this is done with two separate variables, could just index by one...
- if (_dim == true) {
- if (_usingNormal == true) {
- useDim();
- }
- } else {
- if (_usingNormal == false) {
- useNormal();
- }
- }
+ // Pretty silly that this is done with two separate variables, could just index by one...
+ if (_dim == true) {
+ if (_usingNormal == true) {
+ useDim();
+ }
+ } else {
+ if (_usingNormal == false) {
+ useNormal();
+ }
+ }
}
void ImmortalEngine::pump() {
- // Flashes the screen (except the frame thankfully) white, black, white, black, then clears the screen and goes back to normal
- useWhite();
- g_system->updateScreen();
- delay(2);
- useBlack();
- g_system->updateScreen();
- delay(2);
- useWhite();
- g_system->updateScreen();
- delay(2);
- useBlack();
- g_system->updateScreen();
- clearScreen();
- // Why does it do this instead of setting _dontResetColors for clearScreen() instead?
- useNormal();
+ // Flashes the screen (except the frame thankfully) white, black, white, black, then clears the screen and goes back to normal
+ useWhite();
+ g_system->updateScreen();
+ delay(2);
+ useBlack();
+ g_system->updateScreen();
+ delay(2);
+ useWhite();
+ g_system->updateScreen();
+ delay(2);
+ useBlack();
+ g_system->updateScreen();
+ clearScreen();
+ // Why does it do this instead of setting _dontResetColors for clearScreen() instead?
+ useNormal();
}
void ImmortalEngine::fadePal(uint16 pal[], int count, uint16 target[]) {
- /* This will fade the palette used by everything inside the game screen
- * but will not touch the window frame palette. It essentially takes the
- * color value nyble, multiplies it by a multiplier, then takes the whole
- * number result and inserts it into the word at the palette index of the
- * temporary palette. This could I'm sure be done with regular multiplication
- * and division operators, but in case the bits that get dropped are otherwise
- * kept, this is a direct translation of the bit manipulation sequence.
- */
- int maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
- 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
-
- uint16 result;
- uint16 temp;
-
- for (int i = 15; i >= 0; i--) {
- result = maskPal[i];
- if (result == 0) {
- result = pal[i];
- if (result != 0xFFFF) {
- // Blue = 0RGB -> 000B -> 0BBB -> BB0B -> 000B
- result = (xba(mult16((result & kMaskFirst), count))) & kMaskFirst;
-
- // Green = 0RGB -> 00RG -> 000G -> 0GGG -> GG0G -> 000G -> 00G0 -> 00GB
- temp = mult16(((pal[i] >> 4) & kMaskFirst), count);
- temp = (xba(temp) & kMaskFirst) << 4;
- result = temp | result;
-
- // Red = 0RGB -> GB0R -> 000R -> 0RRR -> RR0R -> 000R -> 0R00 -> 0RGB
- temp = xba(pal[i]) & kMaskFirst;
- temp = xba(mult16(temp, count));
- temp = xba(temp & kMaskFirst);
- result = temp | result;
- }
- }
- target[i] = result;
- }
+ /* This will fade the palette used by everything inside the game screen
+ * but will not touch the window frame palette. It essentially takes the
+ * color value nyble, multiplies it by a multiplier, then takes the whole
+ * number result and inserts it into the word at the palette index of the
+ * temporary palette. This could I'm sure be done with regular multiplication
+ * and division operators, but in case the bits that get dropped are otherwise
+ * kept, this is a direct translation of the bit manipulation sequence.
+ */
+ int maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+
+ uint16 result;
+ uint16 temp;
+
+ for (int i = 15; i >= 0; i--) {
+ result = maskPal[i];
+ if (result == 0) {
+ result = pal[i];
+ if (result != 0xFFFF) {
+ // Blue = 0RGB -> 000B -> 0BBB -> BB0B -> 000B
+ result = (xba(mult16((result & kMaskFirst), count))) & kMaskFirst;
+
+ // Green = 0RGB -> 00RG -> 000G -> 0GGG -> GG0G -> 000G -> 00G0 -> 00GB
+ temp = mult16(((pal[i] >> 4) & kMaskFirst), count);
+ temp = (xba(temp) & kMaskFirst) << 4;
+ result = temp | result;
+
+ // Red = 0RGB -> GB0R -> 000R -> 0RRR -> RR0R -> 000R -> 0R00 -> 0RGB
+ temp = xba(pal[i]) & kMaskFirst;
+ temp = xba(mult16(temp, count));
+ temp = xba(temp & kMaskFirst);
+ result = temp | result;
+ }
+ }
+ target[i] = result;
+ }
}
void ImmortalEngine::fade(uint16 pal[], int dir, int delay) {
- // This temp palette will have FFFF in it, which will be understood as masks by setColors()
- uint16 target[16];
- uint16 count;
+ // This temp palette will have FFFF in it, which will be understood as masks by setColors()
+ uint16 target[16];
+ uint16 count;
- // Originally used a branch, but this is functionally identical and much cleaner
- count = dir * 256;
+ // Originally used a branch, but this is functionally identical and much cleaner
+ count = dir * 256;
- while ((count >= 0) && (count <= 256)) {
- fadePal(pal, count, target);
- delay8(delay);
- setColors(target);
+ while ((count >= 0) && (count <= 256)) {
+ fadePal(pal, count, target);
+ delay8(delay);
+ setColors(target);
- // Same as above, it was originally a branch, this does the same thing
- count += (dir == 0) ? 16 : -16;
- }
+ // Same as above, it was originally a branch, this does the same thing
+ count += (dir == 0) ? 16 : -16;
+ }
}
// These two can probably be removed and instead use an enum to declare fadeout/in
void ImmortalEngine::fadeOut(int j) {
- fade(_palDefault, 1, j);
+ fade(_palDefault, 1, j);
}
void ImmortalEngine::normalFadeOut() {
- fadeOut(15);
+ fadeOut(15);
}
void ImmortalEngine::slowFadeOut() {
- fadeOut(28);
+ fadeOut(28);
}
void ImmortalEngine::fadeIn(int j) {
- fade(_palDefault, 0, j);
+ fade(_palDefault, 0, j);
}
void ImmortalEngine::normalFadeIn() {
- fadeIn(15);
+ fadeIn(15);
}
// These two can probably be removed since the extra call in C doesn't have the setup needed in ASM
void ImmortalEngine::useBlack() {
- setColors(_palBlack);
+ setColors(_palBlack);
}
void ImmortalEngine::useWhite() {
- setColors(_palBlack);
+ setColors(_palBlack);
}
void ImmortalEngine::useNormal() {
- setColors(_palDefault);
- _usingNormal = true;
+ setColors(_palDefault);
+ _usingNormal = true;
}
void ImmortalEngine::useDim() {
- setColors(_palDim);
- _usingNormal = false;
+ setColors(_palDim);
+ _usingNormal = false;
}
@@ -503,7 +503,7 @@ void ImmortalEngine::clearKeyBuff() {}
*/
void ImmortalEngine::loadSingles(Common::String songName) {
- debug("%s", songName.c_str());
+ debug("%s", songName.c_str());
}
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 750e59a399b..c7061c8b529 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -23,23 +23,36 @@
namespace Immortal {
+bool ImmortalEngine::trapKeys() {
+ // This weirdly named routine just checks if you want to restart the game. On the NES it pulls up a dialog with a yes/no,
+ // But on the Apple IIGS it's just the R key
+ getInput();
+ if (_pressedAction == kActionRestart) {
+ gameOver();
+ return true;
+ } else if (_pressedAction == kActionSound) {
+ //toggleSound();
+ }
+ return false;
+}
+
// There's no way this routine needs to still be here. In fact I'm not sure it needed to be in the game anyway?
int ImmortalEngine::getLevel() {
- return _level;
+ return _level;
}
void ImmortalEngine::logicInit() {
- _titlesShown = 0;
- _time = 0;
- _promoting = 0;
- _restart = 1;
- //level_initAtStartOfGameOnly
- _lastCertLen = 0;
+ _titlesShown = 0;
+ _time = 0;
+ _promoting = 0;
+ _restart = 1;
+ //level_initAtStartOfGameOnly
+ _lastCertLen = 0;
}
void ImmortalEngine::logic() {
- _time += 1;
- // if time overflows the counter, inc the high byte? What the heck...
+ _time += 1;
+ // if time overflows the counter, inc the high byte? What the heck...
}
@@ -47,22 +60,41 @@ void ImmortalEngine::restartLogic() {
}
int ImmortalEngine::logicFreeze() {
- // Very silly way of checking if the level is over and/or the game is over
- int g = _gameOverFlag | _levelOver;
- return (g ^ 1) >> 1;
+ // Very silly way of checking if the level is over and/or the game is over
+ int g = _gameOverFlag | _levelOver;
+ return (g ^ 1) >> 1;
}
void ImmortalEngine::gameOverDisplay() {
- _themePaused |= 2;
- //text_print(kGameOverString)
+ _themePaused |= 2;
+ //text_print(kGameOverString)
}
void ImmortalEngine::gameOver() {
- _gameOverFlag = 1;
+ _gameOverFlag = 1;
}
void ImmortalEngine::levelOver() {
- _levelOver = 1;
+ _levelOver = 1;
}
-} // namespace Immortal
\ No newline at end of file
+} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/metaengine.cpp b/engines/immortal/metaengine.cpp
index b736f5f8042..1448f05a663 100644
--- a/engines/immortal/metaengine.cpp
+++ b/engines/immortal/metaengine.cpp
@@ -33,15 +33,15 @@ Common::Error ImmortalMetaEngine::createInstance(OSystem *syst, Engine **engine,
}
bool ImmortalMetaEngine::hasFeature(MetaEngineFeature f) const {
- return false;
+ return false;
/* return
(f == kSavesUseExtendedFormat) ||
(f == kSimpleSavesNames) ||
- (f == kSupportsListSaves) ||
- (f == kSupportsDeleteSave) ||
- (f == kSavesSupportMetaInfo) ||
- (f == kSavesSupportThumbnail) ||
- (f == kSupportsLoadingDuringStartup); */
+ (f == kSupportsListSaves) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSupportsLoadingDuringStartup); */
}
#if PLUGIN_ENABLED_DYNAMIC(IMMORTAL)
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 3457ccb0a3e..f55680f21fa 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -32,15 +32,15 @@ namespace Immortal {
*/
void ImmortalEngine::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
- g_system->delayMillis(j * 56);
+ g_system->delayMillis(j * 56);
}
void ImmortalEngine::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
- g_system->delayMillis(j * 14);
+ g_system->delayMillis(j * 14);
}
void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
- g_system->delayMillis(j * 7);
+ g_system->delayMillis(j * 7);
}
void ImmortalEngine::miscInit() {}
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index a385db5bd17..656e8a12e34 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -25,164 +25,164 @@
namespace Immortal {
enum SpriteName {
- // Moresprites 10
- kCandle,
- kWow,
- kAnaVanish,
- kSink,
- kTrapdoor,
- kWizPhant,
- kVanish,
- kShadow,
- kSlime,
- kSlimeDeath,
-
- // Norlac 5
- kBridge,
- kVortex,
- kBubble,
- kNorlac,
- kNolac2,
-
- // Powwow 7
- kPlanners,
- kUgh,
- kIsDevoured,
- kIsBadCrawl,
- kIsGoodCrawl,
- kLeg,
- kIsWebbed,
-
- // Turrets 10
- kSleep,
- kShrink,
- kLocksmith,
- kAnaGlimpse,
- kMadKing,
- kTorch,
- kPipe,
- kProjectile,
- kKnife,
- kAnaHug,
-
- // Worm 4
- kWorm0,
- kWorm1,
- kSpike,
- kIsSpiked,
-
- // Iansprites 6
- kMurder,
- kWizCrawlUp,
- kWizLight,
- kWizBattle,
- kDown,
- kNorlacDown,
-
- // Lastsprites 3
- kWaterLadder,
- kPulledDown,
- kSpill,
-
- // Doorsprites 10
- kDoor,
- kTele,
- kBomb,
- kTorched,
- kLadderTop,
- kSecret,
- kLadderBottom,
- kSlipped,
- kGoblinSlipped,
- kFlame,
-
- // General 5
- kArrow,
- kSpark,
- kObject,
- kBigBurst,
- kBeam,
-
- // Mordamir 3
- kLight,
- kMord,
- kDragMask,
-
- // Dragon2 2
- kDFlames,
- kThroat,
-
- // Dragon 1
- kDragon,
-
- // Rope 3
- kChop,
- kHead,
- kNurse,
-
- // Rescue 2
- kRescue1,
- kRescue2,
-
- // Troll 9
- kTroll0,
- kTroll1,
- kTroll2,
- kTroll3,
- kTroll4,
- kTroll5,
- kTroll6,
- kTroll7,
- kTroll8,
-
- // Goblin 10
- kGoblin0,
- kGoblin1,
- kGoblin2,
- kGoblin3,
- kGoblin4,
- kGoblin5,
- kGoblin6,
- kGoblin7,
- kGoblin8,
- kGoblin9,
-
- //Ulindor 9
- kUlindor0,
- kUlindor1,
- kUlindor2,
- kUlindor3,
- kUlindor4,
- kUlindor5,
- kUlindor6,
- kUlindor7,
- kUlindor8,
-
- //Spider 10
- kSpider0,
- kSpider1,
- kSpider2,
- kSpider3,
- kSpider4,
- kSpider5,
- kSpider6,
- kSpider7,
- kSpider8,
- kSpider9,
-
- //Drag 9
- kDrag0,
- kDrag1,
- kDrag2,
- kDrag3,
- kDrag4,
- kDrag5,
- kDrag6,
- kDrag7,
- kDrag8,
-
- // Font
- kFont
+ // Moresprites 10
+ kCandle,
+ kWow,
+ kAnaVanish,
+ kSink,
+ kTrapdoor,
+ kWizPhant,
+ kVanish,
+ kShadow,
+ kSlime,
+ kSlimeDeath,
+
+ // Norlac 5
+ kBridge,
+ kVortex,
+ kBubble,
+ kNorlac,
+ kNolac2,
+
+ // Powwow 7
+ kPlanners,
+ kUgh,
+ kIsDevoured,
+ kIsBadCrawl,
+ kIsGoodCrawl,
+ kLeg,
+ kIsWebbed,
+
+ // Turrets 10
+ kSleep,
+ kShrink,
+ kLocksmith,
+ kAnaGlimpse,
+ kMadKing,
+ kTorch,
+ kPipe,
+ kProjectile,
+ kKnife,
+ kAnaHug,
+
+ // Worm 4
+ kWorm0,
+ kWorm1,
+ kSpike,
+ kIsSpiked,
+
+ // Iansprites 6
+ kMurder,
+ kWizCrawlUp,
+ kWizLight,
+ kWizBattle,
+ kDown,
+ kNorlacDown,
+
+ // Lastsprites 3
+ kWaterLadder,
+ kPulledDown,
+ kSpill,
+
+ // Doorsprites 10
+ kDoor,
+ kTele,
+ kBomb,
+ kTorched,
+ kLadderTop,
+ kSecret,
+ kLadderBottom,
+ kSlipped,
+ kGoblinSlipped,
+ kFlame,
+
+ // General 5
+ kArrow,
+ kSpark,
+ kObject,
+ kBigBurst,
+ kBeam,
+
+ // Mordamir 3
+ kLight,
+ kMord,
+ kDragMask,
+
+ // Dragon2 2
+ kDFlames,
+ kThroat,
+
+ // Dragon 1
+ kDragon,
+
+ // Rope 3
+ kChop,
+ kHead,
+ kNurse,
+
+ // Rescue 2
+ kRescue1,
+ kRescue2,
+
+ // Troll 9
+ kTroll0,
+ kTroll1,
+ kTroll2,
+ kTroll3,
+ kTroll4,
+ kTroll5,
+ kTroll6,
+ kTroll7,
+ kTroll8,
+
+ // Goblin 10
+ kGoblin0,
+ kGoblin1,
+ kGoblin2,
+ kGoblin3,
+ kGoblin4,
+ kGoblin5,
+ kGoblin6,
+ kGoblin7,
+ kGoblin8,
+ kGoblin9,
+
+ //Ulindor 9
+ kUlindor0,
+ kUlindor1,
+ kUlindor2,
+ kUlindor3,
+ kUlindor4,
+ kUlindor5,
+ kUlindor6,
+ kUlindor7,
+ kUlindor8,
+
+ //Spider 10
+ kSpider0,
+ kSpider1,
+ kSpider2,
+ kSpider3,
+ kSpider4,
+ kSpider5,
+ kSpider6,
+ kSpider7,
+ kSpider8,
+ kSpider9,
+
+ //Drag 9
+ kDrag0,
+ kDrag1,
+ kDrag2,
+ kDrag3,
+ kDrag4,
+ kDrag5,
+ kDrag6,
+ kDrag7,
+ kDrag8,
+
+ // Font
+ kFont
};
} // namespace immortal
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 0329cf6a420..1bad636a672 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -32,10 +32,10 @@ namespace Immortal {
*/
void ImmortalEngine::setSpriteCenter(Common::SeekableReadStream *f, int num, uint8 cenX, uint8 cenY) {
- // Very simple, just initialize what we can of the data sprite
- _dataSprites[num]._cenX = cenX;
- _dataSprites[num]._cenY = cenY;
- _dataSprites[num]._file = f;
+ // Very simple, just initialize what we can of the data sprite
+ _dataSprites[num]._cenX = cenX;
+ _dataSprites[num]._cenY = cenY;
+ _dataSprites[num]._file = f;
}
Commit: 0310509fa1f3612a6e726789451866a5515d7795
https://github.com/scummvm/scummvm/commit/0310509fa1f3612a6e726789451866a5515d7795
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Minor formatting in disk.h
Changed paths:
engines/immortal/disk.h
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index 030fbb00f88..2d581c969c4 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -83,7 +83,7 @@ enum FileExt {
class ProDOSFile : public Common::ArchiveMember {
public:
ProDOSFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
- ~ProDOSFile() {};
+ ~ProDOSFile() {}; // File does not need a destructor, because the file it reads from is a pointer to Disk, and Disk has a destructor
// -- These are the Common::ArchiveMember related functions --
Common::String getName() const override; // Returns _name
@@ -95,12 +95,12 @@ public:
void printInfo();
private:
- char _name[16];
+ char _name[16];
uint8 _type; // As defined by enum FileType
uint16 _blockPtr; // Block index in volume of index block or data
uint16 _totalBlocks;
uint32 _eof; // End Of File, used generally as size (exception being sparse files)
- Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
+Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
};
/* This class defines the entire disk volume. Upon using the open() method,
Commit: 032377fe775a8d2f8a357f46281172def1915c9d
https://github.com/scummvm/scummvm/commit/032377fe775a8d2f8a357f46281172def1915c9d
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add wizard data sprite indexes
Changed paths:
engines/immortal/sprite_list.h
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index 656e8a12e34..2411936dcf8 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -124,7 +124,7 @@ enum SpriteName {
kRescue1,
kRescue2,
- // Troll 9
+ // Troll 9 (8 directions + ?)
kTroll0,
kTroll1,
kTroll2,
@@ -135,7 +135,7 @@ enum SpriteName {
kTroll7,
kTroll8,
- // Goblin 10
+ // Goblin 10 (8 directions + ?)
kGoblin0,
kGoblin1,
kGoblin2,
@@ -147,7 +147,22 @@ enum SpriteName {
kGoblin8,
kGoblin9,
- //Ulindor 9
+ // Wizard A 8 (8 directions)
+ kWizard0,
+ kWizard1,
+ kWizard2,
+ kWizard3,
+ kWizard4,
+ kWizard5,
+ kWizard6,
+ kWizard7,
+
+ // Wizard B 3 (3 ?)
+ kWizard8,
+ kWizard9,
+ kWizard10,
+
+ // Ulindor 9 (8 directions + ?)
kUlindor0,
kUlindor1,
kUlindor2,
@@ -158,7 +173,7 @@ enum SpriteName {
kUlindor7,
kUlindor8,
- //Spider 10
+ // Spider 10 (probably not directions)
kSpider0,
kSpider1,
kSpider2,
@@ -170,7 +185,7 @@ enum SpriteName {
kSpider8,
kSpider9,
- //Drag 9
+ // Drag 9 (probably not directions)
kDrag0,
kDrag1,
kDrag2,
Commit: d475e36f7541a5c19d7019b994a861f3d24640bd
https://github.com/scummvm/scummvm/commit/d475e36f7541a5c19d7019b994a861f3d24640bd
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: New functions added to kernal + update to initdatasprite in sprites.cpp
Changed paths:
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/sprites.cpp
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 3c6203e9979..fc489eacb28 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -77,28 +77,90 @@ enum ColourMask : uint16 {
kMaskBlue = 0x000F
};
-enum InputAction : int {
- kActionRestart,
+enum Screen { // These are constants that are used for defining screen related arrays
+ kMaxRooms = 16, // Should probably put this in a different enum
+ kMaxSprites = 32, // Number of sprites allowed at once
+ kViewPortCW = 256 / 64,
+ kViewPortCH = 128 / kMaxSprites,
+ kMaxDrawItems = kViewPortCH + 1 + kMaxSprites
+};
+
+enum InputAction {
+ kActionNothing,
+ kActionRestart, // Key "R" <-- Debug?
kActionSound,
- kActionFire
+ kActionFire,
+ kActionButton,
+ kActionDBGStep // Debug key for moving engine forward one frame at a time
};
-enum InputDirection : int {
+enum InputDirection {
kDirectionUp,
kDirectionLeft,
kDirectionDown,
kDirectionRight
};
+enum ObjFlag : uint8 {
+ kObjUsesFireButton = 0x40,
+ kObjIsInvisible = 0x20,
+ kObjIsRunning = 0x10,
+ kObjIsChest = 0x08,
+ kObjIsOnGround = 0x04,
+ kObjIsF1 = 0x02,
+ kObjIsF2 = 0x01
+};
+
+enum Monster {
+ kPlayerID
+};
+
+enum Str {
+ kStrGold
+};
+
+enum CertIndex : uint8 {
+ kCertHits,
+ kCertLevel,
+ kCertLoGameFlags,
+ kCertHiGameFlags,
+ kCertQuickness,
+ kCertInvLo,
+ kCertInvHi,
+ kCertGoldLo,
+ kCertGoldHi
+};
+
+enum Song {
+ kSongNothing,
+ kSongMaze,
+ kSongCombat,
+ kSongText
+};
+
+enum GameFlags : uint8 {
+ kSavedNone = 0,
+ kSavedKing = 1,
+ kSavedAna = 2
+};
+
+struct Frame {
+ uint16 _deltaX;
+ uint16 _deltaY;
+ uint16 _rectX;
+ uint16 _rectY;
+ byte *_bitmap;
+};
+
struct DataSprite {
- uint8 _cenX; // These are the base center positions
- uint8 _cenY;
- byte *_bitmap; // Pointer to actual data
-Common::SeekableReadStream *_file; // This will likely be removed later
+ uint16 _cenX; // These are the base center positions
+ uint16 _cenY;
+ uint16 _numFrames;
+Common::Array<Frame> _frames;
};
struct Sprite {
- int _frame; // Current frame of the cycle
+ int _frame; // Index of _dSprite._frames[]
uint16 _X;
uint16 _Y;
uint16 _on; // 1 = active
@@ -106,16 +168,20 @@ struct Sprite {
DataSprite *_dSprite;
};
-struct Cycle {
-DataSprite *_dSprite;
- int _numCycles;
- int *_frames;
+struct ObjType {
+ Str _str;
+ Str _desc;
+ int _size;
+ //_pickup;
+ //_use;
+ //_run;
};
struct ImmortalGameDescription;
// Forward declaration because we will need the Disk class
class ProDosDisk;
+class Room;
class ImmortalEngine : public Engine {
private:
@@ -147,78 +213,154 @@ public:
/*
* Constants
*/
- // Screen
- const int kResH = 320;
- const int kResV = 200;
- const int kScreenW = 128;
- const int kScreenH = 128;
+
+ // Misc constants
+
+ // Screen constants
+ const int kResH = 320;
+ const int kResV = 200;
+ const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
+ const int kScreenH__ = 128; // ???
+ const int kViewPortW = 256;
+ const int kViewPortH = 128;
const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
-
+ const int kScreenLeft = 32;
+ const int kScreenTop = 20;
+ const int kTextLeft = 8;
+ const int kTextTop = 4;
+ const int kGaugeX = 0;
+ const int kGaugeY = -13; // ???
+ const int kScreenBMW = 160; // Literally no idea yet
+ const int kChrW = 64;
+ const int kChrH = 32;
+ const int kChrLen = (kChrW / 2) * kChrH;
+ const int kChrBMW = kChrW / 2;
+
+ uint16 _tChrMask[] = {0,0,0,0,-1,1,0,1,-1,0,0,2,0,-1,2,-2,0,-2,1};
+
+ uint16 _isBackground[] = {1,0,0,0,0,0,0,0,
+ 0,1,1,0,0,0,0,0,
+ 0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0};
+
+
// Disk offsets
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
- const int kMaxCycles = 32;
- const int kMaxSprites = 32; // Number of sprites allowed at once
const int kMaxSpriteAbove = 48; // Maximum sprite extents from center
const int kMaxSpriteBelow = 16;
const int kMaxSpriteLeft = 16;
const int kMaxSpriteRight = 16;
- const int kWizardX = 28; // Common sprite center for some reason
- const int kWizardY = 37;
-
+ const int kMaxSpriteW = 64;
+ const int kMaxSpriteH = 64;
+ const int kSpriteDY = 32;
+ const int kVSX = kMaxSpriteW;
+ const int kVSY = kSpriteDY;
+ const int kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
+ const int kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
+ const int kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
+ const int kMySuperBottom = kVSDY + kViewPortH;
+ const int kSuperBottom = 200;
+ const int kMySuperTop = kVSDY;
+ const int kSuperTop = 0;
+ const int kViewPortSpX = 32;
+ const int kViewPortSpY = 0;
+ const int kWizardX = 28; // Common sprite center for some reason
+ const int kWizardY = 37;
+
+ // Asset constants
+ const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
+ const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
+ const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
+ const char kGaugeStart = 1; // First kGaugeOn char to draw
+
+ // General Strings
+ const Common::String kOldGameString = "New game?%";
+ const Common::String kEnterCertificate = "Enter certificate:&-=";
+ const Common::String kBadCertificate = "Invalid certificate.@";
+ const Common::String kCertString = "End of level!&Here is your certificate:&&=";
+ const Common::String kCert2String = "&@";
+ const Common::String kTitle0 = " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]="; // Might need \ for something
+ const Common::String kTitle4 = " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=";
/*
* 'global' members
*/
+
+ // Misc
Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
bool _draw = 0; // Whether the screen should draw this frame
- bool _dim = 0; // Whether the palette is dim
- bool _usingNormal = 0; // Whether the palette is using normal
int _zero = 0; // No idea what this is yet
- uint8 _gameOverFlag = 0;
- uint8 _levelOver = 0;
- uint8 _themePaused = 0;
- int _level = 0;
+ bool _gameOverFlag = false;
+ uint8 _gameFlags; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
+ bool _themePaused; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
int _titlesShown = 0;
int _time = 0;
- int _promoting = 0;
- int _restart = 0;
+ int _promoting = 0; // I think promoting means the title stuff
+ bool _restart = false;
int _lastCertLen = 0;
+ int _cert = 0; // This will probably not be an int
- /*
- * Input members
- */
+ // Level members
+ int _maxLevels = 0; // This is determined when loading in story files
+ int _level = 0;
+ bool _levelOver = false;
+ Room *_rooms[kMaxRooms]; // Rooms within the level
+
+ // Debug members
+ bool _singleStep; // Flag for _singleStep mode
+
+ // Input members
int _pressedAction;
int _heldAction;
int _pressedDirection;
int _heldDirection;
- /*
- * Asset related members
- */
+ // Music members
+ Song _playing; // Currently playing song
+ int _themeID = 0; // Not sure yet tbh
+ int _combatID = 0;
+
+ // Asset members
int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
DataSprite _font; // The font sprite data is loaded separate from other sprite stuff
- byte *_window; // Bitmap of the window around the game
- Sprite _sprites[32]; // A contiguous series of sprites (this is the same as kMaxSprites, but it can't be used for this)
- Cycle _cycles[32]; // Animation cycle for each sprite slot
+ Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
DataSprite _dataSprites[kFont+1]; // All the sprite data, indexed by SpriteFile
- /*
- * Screen related members
- */
+ // Screen members
+ byte *_window; // Bitmap of the window around the game
+ byte *_screenBuff; // The final buffer that will transfer to the screen
+ uint16 _myCNM[(kViewPortCW+1)][(kViewPortCH+1)];
+ uint16 _myModCNM[(kViewPortCW+1)][(kViewPortCH+1)];
+ uint16 _myModLCNM[(kViewPortCW+1)][(kViewPortCH+1)];
+ uint16 _columnX[kViewPortCW+1];
+ uint16 _columnTop[kViewPortCW+1];
+ uint16 _columnIndex[kViewPortCW+1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
+ uint16 _tIndex[kMaxDrawItems];
+ uint16 _tPriority[kMaxDrawItems];
+ uint16 _viewPortX;
+ uint16 _viewPortY;
+ uint16 _myViewPortX; // Probably mirror of viewportX
+ uint16 _myViewPortY;
+ int _lastGauge = 0; // Mirror for player health, used to update health gauge display
+ uint16 _penX = 0; // Basically where in the screen we are currently drawing
+ uint16 _penY = 0;
+ uint16 _myUnivPointX;
+ uint16 _myUnivPointY;
+ int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
- byte *_screenBuff; // The final buffer that will transfer to the screen
- uint16 _viewPortX;
- uint16 _viewPortY;
- /*
- * Palette related members
- */
+
+ // Palette members
+ bool _dim = 0; // Whether the palette is dim
uint16 _palDefault[16];
uint16 _palWhite[16];
uint16 _palBlack[16];
uint16 _palDim[16];
byte _palRGB[48]; // Palette that ScummVM actually uses, which is an RGB conversion of the original
int _dontResetColors = 0; // Not sure yet
+ bool _usingNormal = 0; // Whether the palette is using normal
/*
@@ -234,6 +376,7 @@ public:
void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
void rect(int x, int y, int w, int h); // Draws a solid rectangle at x,y with size w,h. Also shadows for blit?
+ void printChr(char c);
void loadWindow(); // Gets the window.bm file
void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
@@ -242,8 +385,24 @@ public:
void blit40(); // Uses macro blit 40 times
void sBlit();
void scroll();
-
- // Misc engine
+ void makeMyCNM(); // ?
+ void drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
+ void addRows(); // Add rows to drawitem array
+ void addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
+ void sortDrawItems(); // Sort said items
+ void drawItems(); // Draw the items over the background
+
+
+ // Music
+ void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
+ void fixPause();
+ Song getPlaying();
+ void playMazeSong();
+ void playCombatSong();
+ void doGroan();
+ void musicPause(int sID);
+ void musicUnPause(int sID);
+ void loadSingles(Common::String songName); // Loads and then parse the maze song
// Palette
void loadPalette(); // Get the static palette data from the disk
@@ -265,7 +424,6 @@ public:
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
void loadFont(); // Gets the font.spr file, and centers the sprite
- void loadSingles(Common::String songName); // Loads and then parse the maze song
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
void addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p);
@@ -274,11 +432,11 @@ public:
void userIO(); // Get input
void pollKeys(); // Buffer input
void noNetwork(); // Setup input mirrors
- void keyTraps(); // Seems to be debug only
+ void waitKey(); // Waits until a key is pressed (until getInput() returns true)
void blit8(); // This is actually just input, but it is called blit because it does a 'paddle blit' 8 times
// These will replace the myriad of hardware input handling from the source
- void getInput();
+ bool getInput(); // True if there was input, false if not
void addKeyBuffer();
void clearKeyBuff();
@@ -287,48 +445,44 @@ public:
* [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
*/
- // ??
- void setSpriteCenter(Common::SeekableReadStream *f, int num, uint8 cenX, uint8 cenY); // Basically initializes the data sprite
- int getNumFrames(int file, int num);
-
- /*
- * [Cycle.cpp] Functions from Cyc
- */
-
- /* Unneccessary
- void cycleInit();
- void cycleFree();
- void cycleGetNumFrames();
- void cycleGetList();*/
+ // Init
+ void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
+
+ // Main
+ void superSprite(int s, uint16 x, uint16 y, Frame f, int bmw, byte *dst, int sT, int sB);
- // Misc
- void cycleNew(); // Adds a cycle to the current list
- int getCycleChr();
- void cycleFreeAll(); // Delete all cycles
- void cycleGetFile();
- void cycleGetNum();
- void cycleGetIndex();
- void cycleSetIndex();
- void cycleGetFrame();
- void cycleAdvance();
/*
* [Logic.cpp] Functions from Logic.GS
*/
- // Surface level
- bool trapKeys(); // Poorly named, this checks if the player wants to restart the game
+ // Debug
+ void doSingleStep(); // Let the user advance the engine one frame at a time
- // Misc
+ // Main
+ void trapKeys(); // Poorly named, this checks if the player wants to restart/pause music/use debug step
void logicInit();
void logic(); // Keeps time, handles win and lose conditions, then general logic
void restartLogic(); // This is the actual logic init
int logicFreeze(); // Overcomplicated way to check if game over or level over
+ void updateHitGauge();
+ void drawGauge(int h);
+ bool getCertificate();
+
+ // Misc
+ bool printAnd(const Common::String s);
+ bool fromOldGame();
+ void setGameFlags(uint16 f);
+ void setSavedKing();
+ bool isSavedKing();
+ void setSavedAna();
+ bool isSavedAna();
int getLevel(); // Literally just return _level...
void gameOverDisplay();
void gameOver();
void levelOver();
+
/*
* [Misc.cpp] Functions from Misc
*/
@@ -343,7 +497,7 @@ public:
void myDelay();
// Text printing
- void textPrint(int num);
+ bool textPrint(const Common::String s);
void textSub();
void textEnd();
void textMiddle();
@@ -357,7 +511,6 @@ public:
// Screen related
void inside(int p, int p2, int a);
void insideRect(int p, int r);
- void updateHitGuage();
/*
@@ -374,6 +527,31 @@ public:
void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
+ /*
+ * [Music.cpp] Functions from Music.cpp
+ */
+
+ // Misc
+
+
+ /*
+ * [DrawChr.cpp] Functions from DrawChr.cpp
+ */
+
+ // Main
+ int mungeCBM(int numChrs);
+ void storeAddr();
+ void mungeSolid();
+ void mungeLRHC();
+ void mungeLLHC();
+ void mungeULHC();
+ void mungeURHC();
+ void drawSolid(int chr, int x, int y);
+ void drawULHC(int chr, int x, int y);
+ void drawURHC(int chr, int x, int y);
+ void drawLLHC(int chr, int x, int y);
+ void drawLRHC(int chr, int x, int y);
+
/*
* --- ScummVM general engine Functions ---
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 043d7065858..8e5deb0d82a 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -39,12 +39,21 @@ namespace Immortal {
*/
void ImmortalEngine::drawUniv() {
+ // This is where the entire screen actually comes together
+ _myViewPortX = _viewPortX & 0xFFFE;
+ _myViewPortY = _viewPortY;
- // drawBackground() = draw floor parts of leftmask rightmask and maskers
- // addRows() = add rows to drawitem array
- // addSprites() = add all active sprites that are in the viewport, into a list that will be sorted by priority
- // sortDrawItems() = sort said items
- // drawItems() = draw the items over the background
+ _num2DrawItems = 0;
+
+ _myUnivPointX = !(_myViewPortX & (kChrW - 1)) + kViewPortSpX;
+ _myUnivPointY = !(_myViewPortY & (kChrH - 1)) + kViewPortSpY;
+
+ makeMyCNM();
+ drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
+ addRows(); // Add rows to drawitem array
+ addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
+ sortDrawItems(); // Sort said items
+ drawItems(); // Draw the items over the background
// To start constructing the screem, we start with the frame as the base
memcpy(_screenBuff, _window, kScreenSize);
@@ -85,7 +94,258 @@ void ImmortalEngine::blit() {}
void ImmortalEngine::blit40() {}
void ImmortalEngine::sBlit() {}
void ImmortalEngine::scroll() {}
+void ImmortalEngine::makeMyCNM() {} // ?
+
+void ImmortalEngine::addRows() {
+ // I'm not really sure how this works yet
+ int i = _num2DrawItems;
+ _tPriority[i] = !(!(_myViewPortY & (kChrH - 1)) + _myViewPortY);
+
+ for (int j = 0; j != kViewPortCH+4; j++, i++) {
+ _tIndex[i] = (j << 5) | 0x8000;
+ _tPriority[i] = _tPriority[i] - kChrH;
+ }
+ _num2DrawItems = i;
+}
+
+void ImmortalEngine::addSprites() {
+ // My goodness this routine is gross
+ int tmpNum = _num2DrawItems;
+ for (int i = 0; i < kMaxSprites; i++) {
+ if (_sprites[i]._on == 1) {
+ if ((_sprites[i]._X & 1) != 0) {
+ debug("not good! BRK");
+ return;
+ }
+ int tmpx = (_sprites[i]._X - kMaxSpriteW) - _myViewPortX;
+ if (tmpx < 0) {
+ if (tmpx + (kMaxSpriteW * 2) < 0) {
+ continue;
+ }
+ } else if (tmpx >= kViewPortW) {
+ continue;
+ }
+
+ int tmpy = (_sprites[i]._Y - kMaxSpriteH) - _myViewPortY;
+ if (tmpy < 0) {
+ if (tmpy + (kMaxSpriteH * 2) < 0) {
+ continue;
+ }
+ } else if (tmpy >= kViewPortH) {
+ continue;
+ }
+
+ DataSprite *tempD = _sprites[i]._dSprite;
+ Frame *tempF = &(_sprites[i]._dSprite->_frames[_sprites[i]._frame]);
+ int sx = ((_sprites[i]._X + tempF->_deltaX) - tempD->_cenX) - _myViewPortX;
+ int sy = ((_sprites[i]._Y + tempF->_deltaY) - tempD->_cenY) - _myViewPortY;
+
+ if (sx >= 0 ) {
+ if (sx >= kViewPortW) {
+ continue;
+ }
+ } else if ((sx + tempF->_rectX) <= 0) {
+ continue;
+ }
+
+ if (sy >= 0 ) {
+ if (sy >= kViewPortH) {
+ continue;
+ }
+ } else if ((sy + tempF->_rectY) <= 0) {
+ continue;
+ }
+
+ // Sprite is actually in viewport, we can now enter it in the sorting array
+ _tIndex[_num2DrawItems] = i;
+ _tPriority[_num2DrawItems] = _sprites[i]._priority;
+ tmpNum++;
+ if (tmpNum == kMaxDrawItems) {
+ break;
+ }
+ }
+ }
+ _num2DrawItems = tmpNum;
+}
+
+void ImmortalEngine::sortDrawItems() {
+ /* Just an implementation of bubble sort.
+ * Sorting largest to smallest entry, simply
+ * swapping every two entries if they are not in order.
+ */
+
+ int top = _num2DrawItems;
+ bool bailout;
+
+ do {
+ // Assume that the list is sorted
+ bailout = true;
+ for (int i = 1; i < top; i++) {
+ if (_tPriority[i] > _tPriority[i-1]) {
+ uint16 tmp = _tPriority[i];
+ _tPriority[i] = _tPriority[i-1];
+ _tPriority[i-1] = tmp;
+
+ // List was not sorted yet, therefor we need to check it again
+ bailout = false;
+ }
+ }
+ /* After every pass, the smallest entry is at the end of the array, so we move
+ * the end marker back by one
+ */
+ top--;
+ } while (bailout == false);
+}
+
+void ImmortalEngine::drawBGRND() {
+ // 'tmp' is y, 'cmp' is x
+
+ uint16 pointX = _myUnivPointX;
+ uint16 pointY = _myUnivPointY;
+
+ for (int y = kViewPortCH + 1, y2 = 0; y != 0; y--, y2++) {
+ for (int x = 0; x < (kViewPortCW + 1); x += (kViewPortCW + 1)) {
+ uint16 BTS = _myModLCNM[y2][x];
+
+ if (_tIsBackground[BTS] != 0) {
+ // Low Floor value, draw tile as background
+ drawSolid(_myCNM[y2][x], pointX, pointY);
+
+ } else if (_tChrMask[BTS] >= 0x8000) {
+ // Right Mask, draw upper left hand corner (ULHC) of floor
+ drawULHC(_myCNM[y2][x], pointX, pointY);
+
+ } else if (_tChrMask[BTS] != 0) {
+ // Left Mask, draw upper right hand corner (UPHC) of floor
+ drawURHC(_myCNM[y2][x], pointX, pointY);
+ }
+ pointX += kChrW; // This (and the H version) could be added to the for loop iterator arugment
+ }
+ pointX -= (kChrW * (kViewPortCW + 1)); // They could have also just done pointX = _myUnivPointX
+ pointY += kChrH;
+ }
+}
+
+void ImmortalEngine::drawItems() {
+ for (int i = 0; i < (kViewPortCW + 1); i++) {
+ _columnIndex[i] = 0;
+ }
+
+ for (int i = 0; i < (kViewPortCW + 1); i++) {
+ _columnTop[i] = _myUnivPointY;
+ }
+
+ _columnX[0] = _myUnivPointX;
+ for (int i = 1; i < (kViewPortCW + 1); i++) {
+ _columnX[i] = _myUnivPointX + kChrW;
+ }
+
+ // This is truly horrible, I should double check that this is the intended logic
+ int n = 0;
+ uint16 rowY = 0;
+ do {
+ uint16 index = _tIndex[n];
+ if (index >= 0x8000) { // If negative, it's a row to draw
+ // rowY is (I think) the position of the start of the scroll window within the tile data
+ rowY = (index & 0x7FFF) + _myUnivPointY;
+
+ // The background is a matrix of rows and columns, so for each column, we draw each row tile
+ for (int i = 0; (i < (kViewPortCW + 1)); i++) {
+ //draw the column of rows
+ while (_columnIndex[i] < ((kViewPortCW + 1) * (kViewPortCH + 1))) {
+
+ k = _myModLCNM[i][_columnIndex[i]];
+ if ((rowY - _tChrDy[k]) < _columnTop[i]) {
+ break;
+ }
+ if (_tIsBackground[k] == 0) {
+ // If it's a background tile, we already drew it (why is it in here then??)
+ if (_tChrMask[k] >= 0x8000) {
+ // Right Mask, draw lower right hand corner (LRHC)
+ drawLRHC(_myCNM[i][_columnIndex[i]], _columnTop[i], _columnX[i]);
+
+ } else if (_tChrMask[k] == 0) {
+ // Floor or cover, draw the whole CHR
+ drawSolid(_myCNM[i][_columnIndex[i]], _columnTop[i], _columnX[i]);
+
+ } else {
+ // Left Mask, draw lower left hand corner (LLHC)
+ drawLLHC(_myCNM[i][_columnIndex[i]], _columnTop[i], _columnX[i]);
+ }
+ }
+ _columnTop[i] += kChrH;
+ _columnIndex += (kViewPortCW + 1);
+ }
+ }
+
+ } else {
+ // If positive, it's a sprite
+ uint16 x = (_sprites[index]._X - _myViewPortX) + kVSX;
+ uint16 y = (_sprites[index]._Y - _myViewPortY) + kVSY;
+ superSprite(index, x, y, _sprites[index]._dSprite->_frames[_sprites[index]._frame], kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
+ }
+ n++;
+ } while (n != _num2DrawItems);
+}
+
+void ImmortalEngine::printChr(char c) {
+ // This draws a character from the font sprite table, indexed as an ascii char, using superSprite
+ c &= kMaskASCII; // Grab just the non-extended ascii part
+ if (c == ' ') {
+ _penX += 8; // A space just moves the position on the screen to draw ahead by the size of a space
+ return;
+ }
+
+ if (c == 0x27) {
+ _penX -= 2;
+ }
+
+ if ((c >= 'A') && (c <= 'Z')) {
+ _penX += 8;
+
+ } else {
+ switch (c) {
+ // Capitals, the health bar icons, and lower case m/w are all 2 chars wide
+ case 'm':
+ case 'w':
+ case 'M':
+ case 'W':
+ case 1: // Can't use the constant for this for some reason
+ case 0:
+ _penX += 8;
+ break;
+ case 'i':
+ _penX -= 3;
+ break;
+ case 'j':
+ case 't':
+ _penX -= 2;
+ break;
+ case 'l':
+ _penX -= 4;
+ default:
+ break;
+ }
+ }
+
+ uint16 x = _penX + kScreenLeft;
+ if (x < _dataSprites[kFont]._cenX) {
+ return;
+ }
+
+ uint16 y = _penY + kScreenTop;
+ if (y < _dataSprites[kFont]._cenY) {
+ return;
+ }
+
+ superSprite(0, x, y, _dataSprites[kFont]._frames[(int) c], kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
+ if ((c == 0x27) || (c == 'T')) {
+ _penX -= 2; // Why is this done twice??
+ }
+
+ _penX += 8;
+}
/*
*
@@ -97,12 +357,12 @@ void ImmortalEngine::scroll() {}
void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p) {
if (_numSprites != kMaxSprites) {
- if (x >= (kScreenW + kMaxSpriteLeft)) {
+ if (x >= (kResH + kMaxSpriteLeft)) {
x |= kMaskHigh; // Make it negative
}
_sprites[_numSprites]._X = (x << 1) + _viewPortX;
- if (y >= (kMaxSpriteAbove + kScreenH)) {
+ if (y >= (kMaxSpriteAbove + kResV)) {
y |= kMaskHigh;
}
_sprites[_numSprites]._Y = (y << 1) + _viewPortY;
@@ -148,17 +408,15 @@ void ImmortalEngine::loadSprites() {
Common::String spriteNames[] = {"MORESPRITES.SPR", "NORLAC.SPR", "POWWOW.SPR", "TURRETS.SPR",
"WORM.SPR", "IANSPRITES.SPR", "LAST.SPR", "DOORSPRITES.SPR",
"GENSPRITES.SPR", "DRAGON.SPR", "MORDAMIR.SPR", "FLAMES.SPR",
- "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "ULINDOR.SPR",
- "SPIDER.SPR", "DRAG.SPR"};
-
- Common::SeekableReadStream *files[19];
+ "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "WIZARDA.SPR",
+ "WIZARDB.SPR", "ULINDOR.SPR", "SPIDER.SPR", "DRAG.SPR"};
// Number of sprites in each file
- int spriteNum[] = {10, 5, 7, 10, 4, 6, 3, 10, 5, 3, 2, 1, 3, 2, 9, 10, 9, 10, 9};
+ int spriteNum[] = {10, 5, 7, 10, 4, 6, 3, 10, 5, 3, 2, 1, 3, 2, 9, 10, 8, 3, 9, 10, 9};
// Pairs of (x,y) for each sprite
// Should probably have made this a 2d array, oops
- uint8 centerXY[] = {16,56, 16,32, 27,39, 16,16, 32,16, 34,83, 28,37, 8,12, 8,19, 24,37,
+ uint16 centerXY[] = {16,56, 16,32, 27,39, 16,16, 32,16, 34,83, 28,37, 8,12, 8,19, 24,37,
/* Norlac */ 46,18, 40,0, 8,13, 32,48, 32,40,
/* Powwow */ 53,43, 28,37, 27,37, 26,30, 26,30, 26,29, 28,25,
/* Turrets */ 34,42, 28,37, 24,32, 32,56, 26,56, 8,48, 8,32, 8,14, 8,24, 32,44,
@@ -174,25 +432,25 @@ void ImmortalEngine::loadSprites() {
/* Rescue */ 0,112, 0,112,
/* Troll */ 28,38, 28,37, 28,37, 31,38, 28,37, 25,39, 28,37, 28,37, 28,37,
/* Goblin */ 28,38, 30,38, 26,37, 30,38, 26,37, 26,37, 26,37, 26,37, 26,36, 44,32,
+ /* Wizarda */ 28,37, 28,37, 28,37, 28,37, 28,37, 28,37, 28,37, 28,37,
+ /* Wizardb */ 28,37, 28,37, 28,37,
/* Ulindor */ 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42,
/* Spider */ 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44,
/* Drag */ 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36};
- // Load all sprite files
- for (int i = 0; i < 19; i++) {
- files[i] = loadIFF(spriteNames[i]);
- }
-
// s = current sprite index, f = current file index, n = current number of sprites for this file
int s = 0;
- for (int f = 0; f < 19; f++) {
+ for (int f = 0; f < 21; f++) {
+ // For every sprite file, open it and get the pointer
+ Common::SeekableReadStream *file = loadIFF(spriteNames[f]);
+
for (int n = 0; n < (spriteNum[f] * 2); n += 2, s++) {
+ // For every data sprite in the file, make a datasprite and initialize it
DataSprite d;
+ initDataSprite(file, &d, n/2, centerXY[s * 2], centerXY[(s * 2) + 1]);
_dataSprites[s] = d;
- setSpriteCenter(files[f], s, centerXY[s * 2], centerXY[(s * 2) + 1]);
}
}
-
}
void ImmortalEngine::loadWindow() {
@@ -232,14 +490,14 @@ void ImmortalEngine::loadWindow() {
void ImmortalEngine::loadFont() {
// Initialize the font data sprite
Common::SeekableReadStream *f = loadIFF("FONT.SPR");
-
DataSprite d;
- _dataSprites[kFont] = d;
if (f) {
- setSpriteCenter(f, kFont, 16, 0);
+ initDataSprite(f, &d, 0, 16, 0);
+ _dataSprites[kFont] = d;
+
} else {
- debug("oh nose :(");
+ debug("file doesn't exit?!");
}
}
@@ -281,11 +539,12 @@ Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
f.seek(12);
return unCompress(&f, len);
}
+ // Gotta remember we just moved the cursor around a bunch, need to reset it to read the file
+ f.seek(SEEK_SET);
byte *out = (byte *)malloc(f.size());
f.read(out, f.size());
return new Common::MemoryReadStream(out, f.size(), DisposeAfterUse::YES);
-
}
@@ -335,10 +594,10 @@ void ImmortalEngine::setColors(uint16 pal[]) {
// Green is already the correct size, being the second nyble (00G0)
// Red is in the first nyble of the high byte, so it needs to move right by 4 bits (0R00 -> 00R0)
// Blue is the first nyble of the first byte, so it needs to move left by 4 bits (000B -> 00B0)
- // We also need to repeat the bits so that the colour is the same proportion of 255 as it is 15
+ // We also need to repeat the bits so that the colour is the same proportion of 255 as it is of 15
_palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4) | ((pal[i] & kMaskRed) >> 8);
- _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4) ;
- _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
+ _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4);
+ _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
}
}
// Palette index to update first is 0, and there are 16 colours to update
@@ -382,14 +641,14 @@ void ImmortalEngine::fadePal(uint16 pal[], int count, uint16 target[]) {
* but will not touch the window frame palette. It essentially takes the
* color value nyble, multiplies it by a multiplier, then takes the whole
* number result and inserts it into the word at the palette index of the
- * temporary palette. This could I'm sure be done with regular multiplication
+ * temporary palette. This could I'm sure, be done with regular multiplication
* and division operators, but in case the bits that get dropped are otherwise
* kept, this is a direct translation of the bit manipulation sequence.
*/
- int maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
- 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+ uint16 maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
uint16 result;
uint16 temp;
@@ -397,17 +656,20 @@ void ImmortalEngine::fadePal(uint16 pal[], int count, uint16 target[]) {
for (int i = 15; i >= 0; i--) {
result = maskPal[i];
if (result == 0) {
+ // If the equivalent maskPal entry is 0, then it is a colour we want to fade
result = pal[i];
if (result != 0xFFFF) {
- // Blue = 0RGB -> 000B -> 0BBB -> BB0B -> 000B
+ // If we have not reached FFFF in one direction or the other, we keep going
+
+ // Blue = 0RGB -> 000B -> 0Bbb -> bb0B -> 000B
result = (xba(mult16((result & kMaskFirst), count))) & kMaskFirst;
- // Green = 0RGB -> 00RG -> 000G -> 0GGG -> GG0G -> 000G -> 00G0 -> 00GB
+ // Green = 0RGB -> 00RG -> 000G -> 0Ggg -> gg0G -> 000G -> 00G0 -> 00GB
temp = mult16(((pal[i] >> 4) & kMaskFirst), count);
temp = (xba(temp) & kMaskFirst) << 4;
result = temp | result;
- // Red = 0RGB -> GB0R -> 000R -> 0RRR -> RR0R -> 000R -> 0R00 -> 0RGB
+ // Red = 0RGB -> GB0R -> 000R -> 0Rrr -> rr0R -> 000R -> 0R00 -> 0RGB
temp = xba(pal[i]) & kMaskFirst;
temp = xba(mult16(temp, count));
temp = xba(temp & kMaskFirst);
@@ -487,9 +749,21 @@ void ImmortalEngine::useDim() {
void ImmortalEngine::userIO() {}
void ImmortalEngine::pollKeys() {}
void ImmortalEngine::noNetwork() {}
-void ImmortalEngine::keyTraps() {}
+
+void ImmortalEngine::waitKey() {
+ bool wait = true;
+ while (wait == true) {
+ if (getInput() == true) {
+ wait = false;
+ }
+ }
+}
+
void ImmortalEngine::blit8() {}
-void ImmortalEngine::getInput() {}
+bool ImmortalEngine::getInput() {
+ return true;
+}
+
void ImmortalEngine::addKeyBuffer() {}
void ImmortalEngine::clearKeyBuff() {}
@@ -502,6 +776,63 @@ void ImmortalEngine::clearKeyBuff() {}
*
*/
+void ImmortalEngine::toggleSound() {
+ // Interestingly, this does not mute or turn off the sound, it actually pauses it
+ _themePaused = !_themePaused;
+ fixPause();
+}
+
+void ImmortalEngine::fixPause() {
+ /* The code for this is a little strange, but the idea is that you have
+ * a level theme, and a combat theme, that can both be active. So first you
+ * pause the level theme, and then you pause the combat theme.
+ * The way it does it is weird though. Here's the logic:
+ * if playing either text or maze song, check if the theme is paused. else, just go ahead and pause.
+ * Same thing for combat song. A little odd.
+ */
+
+ // This is a nasty bit of code isn't it? It's accurate to the source though :D
+ switch (_playing) {
+ case kSongText:
+ case kSongMaze:
+ if (_themePaused) {
+ musicUnPause(_themeID);
+ break;
+ }
+ default:
+ musicPause(_themeID);
+ break;
+ }
+
+ // Strictly speaking this should probably be a single function called twice, but the source writes out both so I will too
+ switch (_playing) {
+ case kSongCombat:
+ if (_themePaused) {
+ musicUnPause(_combatID);
+ break;
+ }
+ default:
+ musicPause(_combatID);
+ break;
+ }
+
+}
+
+// *** These two functions will be in music.cpp, they just aren't implemented yet ***
+void ImmortalEngine::musicPause(int sID) {}
+void ImmortalEngine::musicUnPause(int sID) {}
+// ***
+
+Song ImmortalEngine::getPlaying() {
+ return kSongMaze;
+}
+
+void ImmortalEngine::playMazeSong() {
+}
+
+void ImmortalEngine::playCombatSong() {
+}
+
void ImmortalEngine::loadSingles(Common::String songName) {
debug("%s", songName.c_str());
}
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 1bad636a672..ae93b2f4258 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -31,17 +31,49 @@ namespace Immortal {
*
*/
-void ImmortalEngine::setSpriteCenter(Common::SeekableReadStream *f, int num, uint8 cenX, uint8 cenY) {
- // Very simple, just initialize what we can of the data sprite
- _dataSprites[num]._cenX = cenX;
- _dataSprites[num]._cenY = cenY;
- _dataSprites[num]._file = f;
+// This function is basically setSpriteCenter + getSpriteInfo from the source
+void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY) {
+ // We set the center X and Y, for some reason
+ d->_cenX = cenX;
+ d->_cenY = cenY;
+
+ // But now we need to get the rest of the meta data for each frame
+ // index is the index of the sprite within the file (not the same as the sprite name enum)
+ index *= 8;
+ f->seek(index);
+
+ index = f->readUint16LE();
+ uint16 numFrames = f->readUint16LE();
+
+ d->_numFrames = numFrames;
+ debug("Number of Frames: %d", numFrames);
+
+ // Only here for dragon, but just in case, it's a high number so it should catch others
+ if (numFrames >= 0x0200) {
+ debug("** Crazy large value, this isn't a frame number **");
+ return;
+ }
+
+ Common::Array<Frame> frames;
+
+ for (int i = 0; i < numFrames; i++) {
+ Frame newFrame;
+ f->seek(index + (i*2));
+ int ptrFrame = f->readUint16LE();
+ f->seek(ptrFrame);
+ newFrame._deltaX = f->readUint16LE() << 1; // the ASL might not be required, depending on how I translate the sprite drawing
+ newFrame._deltaY = f->readUint16LE();
+ newFrame._rectX = f->readUint16LE();
+ newFrame._rectY = f->readUint16LE();
+ frames.push_back(newFrame);
+ // This is probably where we will get the bitmap when I know how to get it
+ }
+
+ d->_frames = frames;
}
-
-
-
+void ImmortalEngine::superSprite(int s, uint16 x, uint16 y, Frame f, int bmw, byte *dst, int sT, int sB) {}
} // namespace Immortal
Commit: 09a1bba0d0586210f1f78d43d761a7be47bb362f
https://github.com/scummvm/scummvm/commit/09a1bba0d0586210f1f78d43d761a7be47bb362f
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add _playing and _themePaused
Changed paths:
engines/immortal/immortal.cpp
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index c07bcfdda3d..623a5cdc3dc 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -162,8 +162,8 @@ Common::Error ImmortalEngine::run() {
loadWindow(); // Load the window background
loadSingles("Song A"); // Music
loadSprites(); // Get all the sprite data into memory
- // playing = kPlayingNothing;
- // themepaused = 0;
+ _playing = kSongNothing;
+ _themePaused = 0;
clearSprites(); // Clear the sprites before we start
logicInit(); // Init the game logic
Commit: db485ce3493610a321e04c6de4216b5ee221fe91
https://github.com/scummvm/scummvm/commit/db485ce3493610a321e04c6de4216b5ee221fe91
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Fill out much of logic.cpp skeleton
Changed paths:
engines/immortal/logic.cpp
engines/immortal/misc.cpp
engines/immortal/module.mk
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index c7061c8b529..52b3c425d65 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -23,40 +23,295 @@
namespace Immortal {
-bool ImmortalEngine::trapKeys() {
- // This weirdly named routine just checks if you want to restart the game. On the NES it pulls up a dialog with a yes/no,
- // But on the Apple IIGS it's just the R key
- getInput();
- if (_pressedAction == kActionRestart) {
- gameOver();
- return true;
- } else if (_pressedAction == kActionSound) {
- //toggleSound();
- }
- return false;
-}
-
-// There's no way this routine needs to still be here. In fact I'm not sure it needed to be in the game anyway?
-int ImmortalEngine::getLevel() {
- return _level;
-}
-
void ImmortalEngine::logicInit() {
_titlesShown = 0;
_time = 0;
_promoting = 0;
- _restart = 1;
+ _restart = true;
//level_initAtStartOfGameOnly
_lastCertLen = 0;
}
+void ImmortalEngine::restartLogic() {
+ _singleStep = false;
+ _levelOver = false;
+ _gameFlags = kSavedNone;
+
+ // Here's where the majority of the game actually gets initialized
+ //miscInit();
+ //qarrayInit();
+ //cycInit(); <-- room.initCycles()
+ //fsetInit(); <-- room.initTorches()
+ //levelInit(); <-- presumably creates room
+ //roomInit(); <-- will be run in constructor of room
+ //monstInit(); <-- room.initMonsters() \
+ //objectInit(); <-- room.initObjects()
+ //doorInit(); <-- room.initDoors() |- probably all get run from room constructor
+ //sparkInit(); <-- room.initSparks()
+ //bulletInit(); <-- room.initProjectiles() /
+ //objectInit(); <-- again? Odd...
+ //genericSpriteInit(); <-- room.initGenSprites()
+
+ // Probably will be written as:
+ // qarrayInit(); <-- will probably just be like, _qarray = new Common::Array<int? *>();
+ // levelInit();
+
+ if (fromOldGame() == false) {
+ _level = 0;
+ //levelNew();
+ }
+
+ if (_level != 7) {
+ _themePaused = true; // and #-1-2 = set both flags for themePaused
+ }
+}
+
void ImmortalEngine::logic() {
+ trapKeys(); // First thing in any gameloop is to check if we should restart/toggle sound
_time += 1;
- // if time overflows the counter, inc the high byte? What the heck...
+ /* This is actually the main game state loop. I think the best way to translate it
+ * is as a do-while loop. As in, check if the gamestate says we need to restart,
+ * and if so, restart the logic and check again
+ * Personally, I think this should have been a jump table for the different
+ * game state routines, indexed by a single game state variable.
+ * Ie. LDX _gameState : JMP (gameStates),X
+ * Much cleaner I think. Regardless, this will probably be a switch statement eventually.
+ */
+ do {
+
+ if (_restart == true) {
+ restartLogic();
+ _restart = false;
+ }
+
+ // This is the original logic, but I think it makes more sense if this were an else if statement
+ if (_gameOverFlag == true) {
+ gameOver();
+ _gameOverFlag = false;
+ _restart = true;
+
+ } else if (_levelOver == true) {
+ _themePaused = true;
+ _levelOver = false;
+
+ if (_level == (_maxLevels-1)) {
+ //textPrint(kPrintYouWin);
+ debug("YOU WIN");
+
+ } else {
+ //makeCertificate();
+ //printCertificate();
+ if (_level == 0) {
+ //manual2(); // <- debug?
+ }
+ _promoting = 1;
+ }
+ _restart = true;
+
+ } else {
+
+ // Here's where the gameplay sequence actually happens!
+ doSingleStep(); // Debug step function
+ //monstRunAll();
+ //objectRunAll();
+ //doInfiniteHallways();
+ //levelDrawAll();
+ updateHitGauge();
+
+ // What the heck? Check if we are in level 0: room 0, with no lit torches, and no projectiles
+ // If so, dim the screen
+ _dim = 0;
+ if ((_level == 0) && (/*_currentLevel.getShowRoom()*/0 == 0) && (/*roomLighted()*/false == false) && (/*getNumBullets()*/ 0 == 0)) {
+ _dim += 1;
+ }
+
+ if (_level == 7) {
+ doGroan();
+ }
+
+ if (/*monstIsCombat(kPlayerID)*/true == true) {
+ if (getPlaying() != kSongCombat) {
+ playCombatSong();
+ }
+ } else {
+ if (getPlaying() != kSongMaze) {
+ playMazeSong();
+ }
+ }
+ }
+
+ } while (_restart == true);
}
-void ImmortalEngine::restartLogic() {
+void ImmortalEngine::trapKeys() {
+ /* Weird name for a normal routine. It simply checks for the
+ * restart key (or button on the nes), or the sound toggle,
+ * (if debug mode is active it also checks for the
+ * _singleStep key), and then performs a high level action
+ * (on the NES it only checks restart, and it opens a dialog to do it)
+ */
+ getInput();
+ switch (_pressedAction) {
+ case kActionDBGStep:
+ _singleStep = true;
+ break;
+ case kActionRestart:
+ gameOver();
+ break;
+ case kActionSound:
+ toggleSound();
+ default:
+ break;
+ }
+}
+
+void ImmortalEngine::doSingleStep() {
+ /* This is a very cool debug routine. If you press the _singleStep key,
+ * the engine enters this debug mode where it waits for another key press.
+ * If the key is anything other than the _singleStep key, it will advance
+ * the engine one frame (or rather, until we hit this routine again, which
+ * should be one frame). If you hit the _singleStep key, it will exit the mode
+ * and advance normally again.
+ */
+ if (_singleStep == true) {
+ // If singleStep mode is active, stop the engine until we get input
+ waitKey();
+ // If the input is anything other than DGBStep, advance one frame
+ if (_pressedAction == kActionDBGStep) {
+ // Otherwise, we want to exit the mode
+ _singleStep = false;
+ }
+ }
+}
+
+void ImmortalEngine::updateHitGauge() {
+ /* This HUD (essentially) drawing routine is a bit weird because
+ * the game was originally meant to have multiple player characters
+ * in the room at once. So the engine sees the player as a 'monster'
+ * in the same way it sees enemies (and presumably would have seen other players).
+ * As such, this routine asks the room to ask the monster called player,
+ * what their health is. If the game considered the player unique, this would
+ * probably just check a global 'health' variable instead.
+ */
+ //int h = _rooms[_currentRoom]._monsters[kPlayerID]._getHits();
+ int hits = 0;
+ if (hits != _lastGauge) {
+ // Update the mirror value if the health has changed since last frame
+ _lastGauge = hits;
+ drawGauge(hits);
+ }
+}
+
+void ImmortalEngine::drawGauge(int h) {
+ /* Draw the health bar:
+ * We have two variables, the current health (number of hits remaining),
+ * and the difference betweeen the current health and max health (16).
+ * We then do some silly branching logic that is functionally the same
+ * as a for loop for the available health, and then another for unavailable health.
+ * But we could also write it much more efficiently like this:
+ * sta tmp : lda #$16 : tay : dey : sub tmp : tax
+ * -
+ * txa : beq +
+ * lda #$1 : dex
+ * +
+ * jsr draw
+ * dey : bpl -
+ * Ie. Loop over the entire bar, and once you run out of one icon to draw, that 0 becomes
+ * the index of the chr for the other icons.
+ */
+ int r = 16 - h;
+ _penX = kGaugeX;
+ _penY = kGaugeY;
+ h--;
+ if (h >= 0) {
+ // This could be written as a regular for loop, but the game thinks of start/stop as different from on
+ printChr(kGaugeStart);
+ h--;
+ for (; h >= 0; h--) {
+ if (h == 0) {
+ // Redundant code is redundant
+ printChr(kGaugeStop);
+
+ } else {
+ printChr(kGaugeOn);
+ }
+ }
+
+ } else {
+ // Oh hey, this one is indeed a normal for loop
+ for (; r >= 0; r--) {
+ printChr(kGaugeOff);
+ }
+ }
+}
+
+void ImmortalEngine::doGroan() {
+ //getRandom();
+}
+
+bool ImmortalEngine::printAnd(const Common::String s) {
+ // Just prints what it's given and then checks for input
+ textPrint(s);
+ getInput();
+ if (_heldAction != kActionNothing) {
+ return true;
+ }
+ return false;
+}
+
+bool ImmortalEngine::fromOldGame() {
+ /*
+ if (_titlesShown == 0) {
+ _titlesShown++;
+ _dontResetColors = 1;
+ printAnd(kTitle0);
+ printAnd(kTitle4);
+ getInput();
+ return false;
+ }
+
+ _dontResetColors = 0;
+ if (_promoting == 1) {
+ _promoting = 0;
+
+ } else {
+
+ do {
+ if (!textPrint(kOldGameString)) {
+ // They choose not to load an old game
+ return false;
+ }
+ } while (getCertificate());
+
+ if (_lastCertLen == 0) {
+ return false;
+ }
+ }
+
+ _level = _cert + kCertLevel;
+ setGameFlags(((_cert + kCertHiGameFlags) << 4) | (_cert + kCertLoGameFlags));
+ uint16 hits = _cert + kCertHits;
+ uint16 quick = _cert + kCertQuickness;
+ uint16 gold = ((_cert + kCertGoldHi) << 4) | (_cert + kCertGoldLo);
+ // monstMakePlayer(hits, quick, gold); <- will become room.makePlayer();
+
+ uint8 tmp = 3;
+ uint8 flags = kObjIsRunning;
+ uint8 frame;
+ uint8 type;
+
+ // room.makeObject(tmp, flags, gold, )*/
+ return true;
+}
+
+void ImmortalEngine::setGameFlags(uint16 f) {
+
+}
+
+// There's no way this routine needs to still be here. In fact I'm not sure it needed to be in the game anyway?
+int ImmortalEngine::getLevel() {
+ return _level;
}
int ImmortalEngine::logicFreeze() {
@@ -66,8 +321,9 @@ int ImmortalEngine::logicFreeze() {
}
void ImmortalEngine::gameOverDisplay() {
- _themePaused |= 2;
+ _themePaused = true;
//text_print(kGameOverString)
+ debug("GAME OVER");
}
void ImmortalEngine::gameOver() {
@@ -78,6 +334,34 @@ void ImmortalEngine::levelOver() {
_levelOver = 1;
}
+void ImmortalEngine::setSavedKing() {
+ _gameFlags |= kSavedKing;
+}
+
+bool ImmortalEngine::isSavedKing() {
+ if ((_gameFlags & kSavedKing) == 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ImmortalEngine::setSavedAna() {
+ _gameFlags |= kSavedAna;
+}
+
+bool ImmortalEngine::isSavedAna() {
+ if ((_gameFlags & kSavedAna) == 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ImmortalEngine::getCertificate() {
+ return true;
+}
+
} // namespace Immortal
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index f55680f21fa..8f49b224363 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -57,7 +57,9 @@ void ImmortalEngine::myDelay() {}
*
*/
-void ImmortalEngine::textPrint(int num) {}
+bool ImmortalEngine::textPrint(const Common::String s) {
+ return true;
+}
void ImmortalEngine::textSub() {}
void ImmortalEngine::textEnd() {}
void ImmortalEngine::textMiddle() {}
@@ -87,10 +89,6 @@ void ImmortalEngine::firePressed() {}
void ImmortalEngine::inside(int p, int p2, int a) {}
void ImmortalEngine::insideRect(int p, int r) {}
-void ImmortalEngine::updateHitGuage() {}
-
-
-
} // namespace Immortal
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index fa12eb2f94f..6e2c6e8c62c 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -1,13 +1,13 @@
MODULE := engines/immortal
MODULE_OBJS = \
- immortal.o \
- disk.o \
metaengine.o \
- compression.o \
+ disk.o \
+ immortal.o \
kernal.o \
logic.o \
sprites.o \
+ compression.o \
misc.o \
cycle.o
Commit: 8391e33c71126f0a7586808a87aacc1f22a3c693
https://github.com/scummvm/scummvm/commit/8391e33c71126f0a7586808a87aacc1f22a3c693
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add SpriteFrame enum
Changed paths:
engines/immortal/sprite_list.h
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index 2411936dcf8..b4e80963e62 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -24,6 +24,68 @@
namespace Immortal {
+enum SpriteFrame {
+ // Chest frames
+ kChest0Frame,
+ kOpenChestFrame,
+ kRingFrame,
+ kKnifeFrame,
+ kDeadGoblinFrame,
+
+ // Normal frames
+ kSwordFrame,
+ kKeyFrame,
+ kYesIconOff,
+ kYesIconOn,
+ kNoIconOff,
+ kNoIconOn,
+ kChoiceFrame,
+ kEraseChoseFrame,
+ kSwordBigFrame,
+ kVaseBigFrame,
+ kVaseFrame,
+ kBrokenFrame,
+ kKeyBigFrame,
+ kBagFrame,
+ kBagBigFrame,
+ kBookBigFrame,
+ kBookFrame,
+ kScrollFrame,
+ kScrollBigFrame,
+ kOkayFrame,
+ kAltarFrame,
+ kGoldBigFrame,
+ kMapBigFrame,
+ kSemblanceFrame,
+ kTrapDoorFrame,
+ kBonesFrame,
+ kSackBigFrame,
+ kSporesFrame,
+ kGemGlintFrame,
+ kStoneFrame,
+ kGreenStoneFrame,
+ kGemBigFrame,
+ kStoneBigFrame,
+ kPileFrame,
+ kNoteBigFrame,
+
+ // 45 - 48 are Merchant frames
+ kMerchantFrame,
+
+ // Remaining frames
+ kCoinBigFrame = 49,
+ kPileBigFrame,
+ kKingFrame,
+ kDeadKingFrame,
+ kBombBigFrame,
+ kRingBigFrame,
+ kKnifeBigFrame,
+ kCarpetBigFrame,
+ kAnaInHoleFrame,
+ kAnaNotInHoleFrame,
+ kInvisRingFrame
+};
+
enum SpriteName {
// Moresprites 10
kCandle,
Commit: c942b7f0957cd626fade9287e0ccee684e78e246
https://github.com/scummvm/scummvm/commit/c942b7f0957cd626fade9287e0ccee684e78e246
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add drawChr.cpp with function declarations and entry in module.mk
Changed paths:
A engines/immortal/drawChr.cpp
engines/immortal/module.mk
diff --git a/engines/immortal/drawChr.cpp b/engines/immortal/drawChr.cpp
new file mode 100644
index 00000000000..7ef77391543
--- /dev/null
+++ b/engines/immortal/drawChr.cpp
@@ -0,0 +1,41 @@
+/* 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 "immortal/immortal.h"
+
+namespace Immortal {
+
+int ImmortalEngine::mungeCBM(int numChrs) {
+ return 0;
+}
+void ImmortalEngine::storeAddr() {}
+void ImmortalEngine::mungeSolid() {}
+void ImmortalEngine::mungeLRHC() {}
+void ImmortalEngine::mungeLLHC() {}
+void ImmortalEngine::mungeULHC() {}
+void ImmortalEngine::mungeURHC() {}
+void ImmortalEngine::drawSolid(int chr, int x, int y) {}
+void ImmortalEngine::drawULHC(int chr, int x, int y) {}
+void ImmortalEngine::drawURHC(int chr, int x, int y) {}
+void ImmortalEngine::drawLLHC(int chr, int x, int y) {}
+void ImmortalEngine::drawLRHC(int chr, int x, int y) {}
+
+} // namespace immortal
\ No newline at end of file
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 6e2c6e8c62c..18b5dfdf28b 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -9,7 +9,18 @@ MODULE_OBJS = \
sprites.o \
compression.o \
misc.o \
- cycle.o
+ cycle.o \
+ drawChr.o
+
+# level.o \
+# universe.o \
+# room.o \
+# object.o \
+# door.o \
+# flameset.o \
+# bullet.o \
+# monster.o \
+# motives.o
# This module can be built as a plugin
ifeq ($(ENABLE_IMMORTAL), DYNAMIC_PLUGIN)
Commit: 076742aa14fe55251fcaa7137c7ff1872e1063cc
https://github.com/scummvm/scummvm/commit/076742aa14fe55251fcaa7137c7ff1872e1063cc
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Logic skeleton filled out and necessary parts of story.h added
Changed paths:
A engines/immortal/story.h
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index fc489eacb28..584e7dc0f77 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -50,6 +50,7 @@
#include "immortal/disk.h"
#include "immortal/sprite_list.h" // This is an enum of all available sprites
+#include "immortal/story.h"
namespace Immortal {
@@ -71,12 +72,20 @@ enum BitMask8 : uint8 {
kMask8Low = 0x0F
};
-enum ColourMask : uint16 {
+enum ColourBitMask : uint16 {
kMaskRed = 0x0F00,
kMaskGreen = 0x00F0,
kMaskBlue = 0x000F
};
+enum ChrMask : uint16 {
+ kChr0 = 0x0000,
+ kChrL = 0x0001,
+ kChrR = 0xFFFF,
+ kChrLD = 0x0002,
+ kChrRD = 0xFFFE
+};
+
enum Screen { // These are constants that are used for defining screen related arrays
kMaxRooms = 16, // Should probably put this in a different enum
kMaxSprites = 32, // Number of sprites allowed at once
@@ -87,10 +96,11 @@ enum Screen { // These are constants that are used for defining screen
enum InputAction {
kActionNothing,
+ kActionKey,
kActionRestart, // Key "R" <-- Debug?
kActionSound,
kActionFire,
- kActionButton,
+ kActionButton, // Does this just refer to whatever is not the fire button?
kActionDBGStep // Debug key for moving engine forward one frame at a time
};
@@ -101,24 +111,6 @@ enum InputDirection {
kDirectionRight
};
-enum ObjFlag : uint8 {
- kObjUsesFireButton = 0x40,
- kObjIsInvisible = 0x20,
- kObjIsRunning = 0x10,
- kObjIsChest = 0x08,
- kObjIsOnGround = 0x04,
- kObjIsF1 = 0x02,
- kObjIsF2 = 0x01
-};
-
-enum Monster {
- kPlayerID
-};
-
-enum Str {
- kStrGold
-};
-
enum CertIndex : uint8 {
kCertHits,
kCertLevel,
@@ -168,15 +160,6 @@ struct Sprite {
DataSprite *_dSprite;
};
-struct ObjType {
- Str _str;
- Str _desc;
- int _size;
- //_pickup;
- //_use;
- //_run;
-};
-
struct ImmortalGameDescription;
// Forward declaration because we will need the Disk class
@@ -215,6 +198,19 @@ public:
*/
// Misc constants
+ const int kNumLengths = 21;
+ const int kNiceTime = 36;
+ const int kMaxCertificate = 16;
+
+ // this should really be a char array, but inserting frame values will be stupid so it's just a string instead
+ const Common::String genStr[11] = {"New game?%", "Enter certificate:&-=", "Invalid certificate.@",
+ "End of level!&Here is your certificate:&&=", "&@",
+ " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=", // Might need \ for something
+ " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
+ "#" + Common::String(kGoldBigFrame) + "$0 gold@",
+ "Congratulations!&&Play again?@",
+ "Enter certificate:&-=",
+ "Game Over&&Play again?@"};
// Screen constants
const int kResH = 320;
@@ -231,20 +227,31 @@ public:
const int kGaugeX = 0;
const int kGaugeY = -13; // ???
const int kScreenBMW = 160; // Literally no idea yet
- const int kChrW = 64;
- const int kChrH = 32;
+ const uint16 kChrW = 64;
+ const uint16 kChrH = 32;
+ const uint16 kChrH2 = kChrH * 2;
+ const uint16 kChrH3 = kChrH * 3;
const int kChrLen = (kChrW / 2) * kChrH;
const int kChrBMW = kChrW / 2;
-
- uint16 _tChrMask[] = {0,0,0,0,-1,1,0,1,-1,0,0,2,0,-1,2,-2,0,-2,1};
-
- uint16 _isBackground[] = {1,0,0,0,0,0,0,0,
- 0,1,1,0,0,0,0,0,
- 0,0,0,
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,
- 0};
-
+ const int kLCutaway = 4;
+
+ const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
+ kChrH2, kChrH, kChrH2, kChrH2, kChr0,
+ kChr0, kChrH2, kChrH, kChrH2, kChrH2,
+ kChrH2, kChrH, kChrH2, kChrH2};
+
+ const uint16 kChrMask[19] = {kChr0, kChr0, kChr0, kChr0,
+ kChrR, kChrL, kChr0, kChrL,
+ kChrR, kChr0, kChr0, kChrLD,
+ kChr0, kChrR, kChrLD, kChrRD,
+ kChr0, kChrRD, kChrL};
+
+ const uint16 kIsBackground[36] = {1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0};
// Disk offsets
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
@@ -272,25 +279,20 @@ public:
const int kWizardY = 37;
// Asset constants
- const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
- const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
- const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
- const char kGaugeStart = 1; // First kGaugeOn char to draw
-
- // General Strings
- const Common::String kOldGameString = "New game?%";
- const Common::String kEnterCertificate = "Enter certificate:&-=";
- const Common::String kBadCertificate = "Invalid certificate.@";
- const Common::String kCertString = "End of level!&Here is your certificate:&&=";
- const Common::String kCert2String = "&@";
- const Common::String kTitle0 = " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]="; // Might need \ for something
- const Common::String kTitle4 = " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=";
+ const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
+ const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
+ const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
+ const char kGaugeStart = 1; // First kGaugeOn char to draw
+
+
/*
* 'global' members
*/
// Misc
Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
+ uint8 _certificate[16]; // The certificate (password) is basically the inventory/equipment array
+ uint8 _lastCertLen = 0;
bool _draw = 0; // Whether the screen should draw this frame
int _zero = 0; // No idea what this is yet
bool _gameOverFlag = false;
@@ -300,14 +302,12 @@ public:
int _time = 0;
int _promoting = 0; // I think promoting means the title stuff
bool _restart = false;
- int _lastCertLen = 0;
- int _cert = 0; // This will probably not be an int
// Level members
- int _maxLevels = 0; // This is determined when loading in story files
- int _level = 0;
- bool _levelOver = false;
- Room *_rooms[kMaxRooms]; // Rooms within the level
+ int _maxLevels = 0; // This is determined when loading in story files
+ int _level = 0;
+ bool _levelOver = false;
+ Room *_rooms[kMaxRooms]; // Rooms within the level
// Debug members
bool _singleStep; // Flag for _singleStep mode
@@ -327,17 +327,17 @@ public:
int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
DataSprite _font; // The font sprite data is loaded separate from other sprite stuff
Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
- DataSprite _dataSprites[kFont+1]; // All the sprite data, indexed by SpriteFile
+ DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
// Screen members
- byte *_window; // Bitmap of the window around the game
- byte *_screenBuff; // The final buffer that will transfer to the screen
- uint16 _myCNM[(kViewPortCW+1)][(kViewPortCH+1)];
- uint16 _myModCNM[(kViewPortCW+1)][(kViewPortCH+1)];
- uint16 _myModLCNM[(kViewPortCW+1)][(kViewPortCH+1)];
- uint16 _columnX[kViewPortCW+1];
- uint16 _columnTop[kViewPortCW+1];
- uint16 _columnIndex[kViewPortCW+1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
+ byte *_window; // Bitmap of the window around the game
+ byte *_screenBuff; // The final buffer that will transfer to the screen
+ uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+ uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+ uint16 _myModLCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+ uint16 _columnX[kViewPortCW + 1];
+ uint16 _columnTop[kViewPortCW + 1];
+ uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
uint16 _tIndex[kMaxDrawItems];
uint16 _tPriority[kMaxDrawItems];
uint16 _viewPortX;
@@ -353,14 +353,14 @@ public:
Graphics::Surface *_mainSurface;
// Palette members
- bool _dim = 0; // Whether the palette is dim
+ int _dontResetColors = 0; // Not sure yet
+ bool _usingNormal = 0; // Whether the palette is using normal
+ bool _dim = 0; // Whether the palette is dim
uint16 _palDefault[16];
uint16 _palWhite[16];
uint16 _palBlack[16];
uint16 _palDim[16];
byte _palRGB[48]; // Palette that ScummVM actually uses, which is an RGB conversion of the original
- int _dontResetColors = 0; // Not sure yet
- bool _usingNormal = 0; // Whether the palette is using normal
/*
@@ -376,6 +376,7 @@ public:
void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
void rect(int x, int y, int w, int h); // Draws a solid rectangle at x,y with size w,h. Also shadows for blit?
+ void backspace(); // Moves draw position back and draws empty rect in place of char
void printChr(char c);
void loadWindow(); // Gets the window.bm file
void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
@@ -392,7 +393,6 @@ public:
void sortDrawItems(); // Sort said items
void drawItems(); // Draw the items over the background
-
// Music
void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
void fixPause();
@@ -423,6 +423,7 @@ public:
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
+ //void loadMazeGraphics(); // Creates a universe with a maze
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
@@ -461,18 +462,22 @@ public:
// Main
void trapKeys(); // Poorly named, this checks if the player wants to restart/pause music/use debug step
+ int keyOrButton(); // Returns value based on whether it was a keyboard key or a button press
void logicInit();
void logic(); // Keeps time, handles win and lose conditions, then general logic
void restartLogic(); // This is the actual logic init
int logicFreeze(); // Overcomplicated way to check if game over or level over
void updateHitGauge();
void drawGauge(int h);
+ void calcCheckSum(int l, uint8 checksum[]); // Checksum is one word, but the source called it CheckSum
bool getCertificate();
+ void printCertificate();
// Misc
- bool printAnd(const Common::String s);
+ bool printAnd(Str s);
bool fromOldGame();
void setGameFlags(uint16 f);
+ uint16 getGameFlags();
void setSavedKing();
bool isSavedKing();
void setSavedAna();
@@ -497,11 +502,11 @@ public:
void myDelay();
// Text printing
- bool textPrint(const Common::String s);
+ bool textPrint(Str s);
void textSub();
- void textEnd();
- void textMiddle();
- void textBeginning();
+ void textEnd(Str s);
+ void textMiddle(Str s);
+ void textBeginning(Str s);
void yesNo();
// Input related
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 8e5deb0d82a..79b15bfb514 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -207,15 +207,15 @@ void ImmortalEngine::drawBGRND() {
for (int x = 0; x < (kViewPortCW + 1); x += (kViewPortCW + 1)) {
uint16 BTS = _myModLCNM[y2][x];
- if (_tIsBackground[BTS] != 0) {
+ if (kIsBackground[BTS] != 0) {
// Low Floor value, draw tile as background
drawSolid(_myCNM[y2][x], pointX, pointY);
- } else if (_tChrMask[BTS] >= 0x8000) {
+ } else if (kChrMask[BTS] >= 0x8000) {
// Right Mask, draw upper left hand corner (ULHC) of floor
drawULHC(_myCNM[y2][x], pointX, pointY);
- } else if (_tChrMask[BTS] != 0) {
+ } else if (kChrMask[BTS] != 0) {
// Left Mask, draw upper right hand corner (UPHC) of floor
drawURHC(_myCNM[y2][x], pointX, pointY);
}
@@ -254,17 +254,20 @@ void ImmortalEngine::drawItems() {
//draw the column of rows
while (_columnIndex[i] < ((kViewPortCW + 1) * (kViewPortCH + 1))) {
- k = _myModLCNM[i][_columnIndex[i]];
- if ((rowY - _tChrDy[k]) < _columnTop[i]) {
+ uint16 k = _myModLCNM[i][_columnIndex[i]];
+ // ******* This is just so that the array can be indexed right now, will remove when myModLCNM is actually useable
+ k = 0;
+ // *****************************
+ if ((rowY - kChrDy[k]) < _columnTop[i]) {
break;
}
- if (_tIsBackground[k] == 0) {
+ if (kIsBackground[k] == 0) {
// If it's a background tile, we already drew it (why is it in here then??)
- if (_tChrMask[k] >= 0x8000) {
+ if (kChrMask[k] >= 0x8000) {
// Right Mask, draw lower right hand corner (LRHC)
drawLRHC(_myCNM[i][_columnIndex[i]], _columnTop[i], _columnX[i]);
- } else if (_tChrMask[k] == 0) {
+ } else if (kChrMask[k] == 0) {
// Floor or cover, draw the whole CHR
drawSolid(_myCNM[i][_columnIndex[i]], _columnTop[i], _columnX[i]);
@@ -274,7 +277,7 @@ void ImmortalEngine::drawItems() {
}
}
_columnTop[i] += kChrH;
- _columnIndex += (kViewPortCW + 1);
+ _columnIndex[i] += (kViewPortCW + 1);
}
}
@@ -288,6 +291,12 @@ void ImmortalEngine::drawItems() {
} while (n != _num2DrawItems);
}
+void ImmortalEngine::backspace() {
+ // Just moves the drawing position back by a char, and then draws an empty rect there (I think)
+ _penX -= 8;
+ //rect(_penX + 32, 40, 8, 16, 0);
+}
+
void ImmortalEngine::printChr(char c) {
// This draws a character from the font sprite table, indexed as an ascii char, using superSprite
c &= kMaskASCII; // Grab just the non-extended ascii part
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 52b3c425d65..d8191ae7881 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -23,6 +23,12 @@
namespace Immortal {
+int ImmortalEngine::logicFreeze() {
+ // Very silly way of checking if the level is over and/or the game is over
+ int g = _gameOverFlag | _levelOver;
+ return (g ^ 1) >> 1;
+}
+
void ImmortalEngine::logicInit() {
_titlesShown = 0;
_time = 0;
@@ -58,7 +64,7 @@ void ImmortalEngine::restartLogic() {
if (fromOldGame() == false) {
_level = 0;
- //levelNew();
+ //levelNew(_level);
}
if (_level != 7) {
@@ -96,8 +102,7 @@ void ImmortalEngine::logic() {
_levelOver = false;
if (_level == (_maxLevels-1)) {
- //textPrint(kPrintYouWin);
- debug("YOU WIN");
+ textPrint(kStrYouWin);
} else {
//makeCertificate();
@@ -123,7 +128,7 @@ void ImmortalEngine::logic() {
// If so, dim the screen
_dim = 0;
if ((_level == 0) && (/*_currentLevel.getShowRoom()*/0 == 0) && (/*roomLighted()*/false == false) && (/*getNumBullets()*/ 0 == 0)) {
- _dim += 1;
+ //_dim += 1;
}
if (_level == 7) {
@@ -166,6 +171,25 @@ void ImmortalEngine::trapKeys() {
}
}
+int ImmortalEngine::keyOrButton() {
+ // Returns a key if a key was pressed, or 13 if a button was pressed
+
+ int button = 0;
+ while (button == 0) {
+ getInput();
+ switch (_pressedAction) {
+ case kActionKey:
+ button = _pressedAction;
+ case kActionFire:
+ case kActionButton:
+ button = 13;
+ default:
+ break;
+ }
+ }
+ return button;
+}
+
void ImmortalEngine::doSingleStep() {
/* This is a very cool debug routine. If you press the _singleStep key,
* the engine enters this debug mode where it waits for another key press.
@@ -194,7 +218,7 @@ void ImmortalEngine::updateHitGauge() {
* what their health is. If the game considered the player unique, this would
* probably just check a global 'health' variable instead.
*/
- //int h = _rooms[_currentRoom]._monsters[kPlayerID]._getHits();
+ //int hits = _rooms[_currentRoom]._monsters[kPlayerID]._getHits();
int hits = 0;
if (hits != _lastGauge) {
// Update the mirror value if the health has changed since last frame
@@ -246,11 +270,8 @@ void ImmortalEngine::drawGauge(int h) {
}
}
-void ImmortalEngine::doGroan() {
- //getRandom();
-}
-
-bool ImmortalEngine::printAnd(const Common::String s) {
+bool ImmortalEngine::printAnd(Str s) {
+ // Only ever used by fromOldGame()
// Just prints what it's given and then checks for input
textPrint(s);
getInput();
@@ -261,12 +282,16 @@ bool ImmortalEngine::printAnd(const Common::String s) {
}
bool ImmortalEngine::fromOldGame() {
- /*
+ /* This is the basic load game routine (and also title display).
+ * It lets the user enter a password, or start a new game.
+ * Either way it sets up the inventory for the level, and also
+ * various object related things for the specific level.
+ */
if (_titlesShown == 0) {
_titlesShown++;
_dontResetColors = 1;
- printAnd(kTitle0);
- printAnd(kTitle4);
+ printAnd(kStrTitle0);
+ printAnd(kStrTitle4);
getInput();
return false;
}
@@ -278,52 +303,260 @@ bool ImmortalEngine::fromOldGame() {
} else {
do {
- if (!textPrint(kOldGameString)) {
+ if (!textPrint(kStrOldGame)) {
// They choose not to load an old game
return false;
}
- } while (getCertificate());
+ } while (getCertificate() == true);
if (_lastCertLen == 0) {
return false;
}
}
- _level = _cert + kCertLevel;
- setGameFlags(((_cert + kCertHiGameFlags) << 4) | (_cert + kCertLoGameFlags));
- uint16 hits = _cert + kCertHits;
- uint16 quick = _cert + kCertQuickness;
- uint16 gold = ((_cert + kCertGoldHi) << 4) | (_cert + kCertGoldLo);
+ // Set game flags
+ _level = _certificate[kCertLevel];
+
+ setGameFlags((_certificate[kCertHiGameFlags] << 4) | _certificate[kCertLoGameFlags]);
+
+ // Create the player
+
+ //uint8 hits = _certificate[kCertHits];
+ //uint8 quick = _certificate[kCertQuickness];
+ //uint8 gold = (_certificate[kCertGoldHi] << 4) | _certificate[kCertGoldLo];
// monstMakePlayer(hits, quick, gold); <- will become room.makePlayer();
- uint8 tmp = 3;
- uint8 flags = kObjIsRunning;
- uint8 frame;
- uint8 type;
+ // Create the inventory
+ // room.makeObject(3, kObjIsRunning, 0, goldType);
+
+ // Hi bits of inventory
+ int certInv = _certificate[kCertInvHi];
+
+ if ((certInv & 1) != 0 ) {
+ if (_level < 2) {
+ //room.makeObject(3, 0, 0, waterType);
+ }
+ }
+
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, 0, kRingFrame, dunRingType);
+ }
- // room.makeObject(tmp, flags, gold, )*/
+ if ((certInv & 4) != 0) {
+ if (_level < 6) {
+ //room.makeObject(3, 0, kSporesFrame, wormFoodType);
+ }
+ }
+
+ if ((certInv & 8) != 0) {
+ //room.makeObject(3, 0, 0 (?), coinType);
+ }
+
+
+ // Low bits of inventory
+ certInv = _certificate[kCertInvLo];
+
+ // This would have been much more clean as a set of tables instead of a long branching tree
+ switch (_certificate[kCertLevel]) {
+ case 1:
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, sporesType);
+ }
+
+ if ((certInv & 4) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, wowCharmType);
+ }
+
+ break;
+ case 4:
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, kIsInvisible, kSporesFrame, coffeeType);
+ }
+
+ break;
+ case 3:
+ if ((certInv & 1) != 0) {
+ //room.makeObject(3, kIsRunning, kRingFrame, faceRingType);
+ }
+
+ break;
+ case 7:
+ if ((certInv & 1) != 0) {
+ //room.makeObject(6, kUsesFireButton, kSporesFrame, bronzeType);
+ }
+
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, tractorType);
+ }
+
+ if ((certInv & 4) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, antiType);
+ }
+
+ default:
+ break;
+ }
+ //levelNew(_level)
return true;
}
+void ImmortalEngine::calcCheckSum(int l, uint8 checksum[]) {
+ checksum[0] = 4;
+ checksum[1] = 0xa5;
+
+ /* The game logic seems to allow a len of 4 (cmp 4 : bcc),
+ * but the checksum iny before it checks if the sizes are the same,
+ * so shouldn't a cert of len 4 cause it to loop 0xfffc times?
+ */
+ for (int i = 4; i <= l; i++) {
+ checksum[0] = (_certificate[i] + checksum[0]) ^ checksum[1];
+ checksum[1] = (_certificate[i] << 1) + checksum[1];
+ }
+
+ checksum[3] = checksum[1] >> 4;
+ checksum[2] = checksum[1] & 0xf;
+ checksum[1] = checksum[0] >> 4;
+ checksum[0] = checksum[0] & 0xf;
+}
+
+bool ImmortalEngine::getCertificate() {
+ textPrint(kStrCertificate);
+ int certLen = 0;
+ bool entered = false;
+ int k = 0;
+
+ // My goodness the logic for this is a mess.
+ while (entered == false) {
+ k = keyOrButton();
+ if (k == 13) {
+ entered = true;
+
+ } else if (k == 0x7f) {
+ // The input was a backspace
+ if (certLen != 0) {
+ // Length is one smaller now
+ // move the drawing position back and reprint the '-' char
+ certLen--;
+ backspace();
+ backspace();
+ printChr('-');
+ }
+
+ } else {
+ // The input was a key
+
+ if (certLen != kMaxCertificate) {
+ if ((k >= 'a') && (k < '{')) {
+ k -= 0x20;
+ }
+
+ if (k >= '0') {
+ if (k < ('9' + 1)) {
+ k -= '0';
+ }
+
+ else {
+ if (k < 'A') {
+ // Terrible, I know. But this seems to be the logic.
+ continue;
+ }
+
+ if (k < ('F' + 1)) {
+ k -= ('A' - 10);
+ }
+ }
+
+ int certK = k;
+ if ((k < ('Z' + 1)) && (k >= 'A')) {
+ k += ('a' - 'A');
+ }
+ backspace();
+ printChr(k);
+ printChr('-');
+ _certificate[certLen] = certK;
+ certLen++;
+ }
+ }
+ }
+ }
+
+ // Input of certificate is finished
+ if (certLen == 0) {
+ certLen = _lastCertLen;
+ }
+ if (certLen != 0) {
+ if (certLen < 4) {
+ textPrint(kStrBadCertificate);
+ return false;
+ }
+ uint8 checksum[4];
+ calcCheckSum(certLen, checksum);
+ for (int i = 0; i < 4; i++) {
+ if (checksum[i] != _certificate[i]) {
+ textPrint(kStrBadCertificate);
+ return false;
+ }
+ }
+ }
+
+ // Cert is good
+ _lastCertLen = certLen;
+ return true;
+}
+
+void ImmortalEngine::printCertificate() {
+ /* In contrast to the other certificate routines,
+ * this one is nice and simple. You could also
+ * just add the appropriate offset for the letters,
+ * but grabbing it from a table is faster and doesn't
+ * use a lot of space (especially if it's used anywhere else).
+ * Why doesn't the game use rom table indexing like this more often?
+ */
+ char toHex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ textBeginning(kStrCertificate);
+ for (int i = 0; i < _lastCertLen; i++) {
+ printChr(toHex[_certificate[i]]);
+ }
+ textEnd(kStrCertificate2);
+}
+
+bool ImmortalEngine::isSavedKing() {
+ if ((_gameFlags & kSavedKing) == 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ImmortalEngine::isSavedAna() {
+ if ((_gameFlags & kSavedAna) == 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/*
+ * Functions that don't really need to be functions
+ */
+
void ImmortalEngine::setGameFlags(uint16 f) {
+ _gameFlags = f;
+}
+uint16 ImmortalEngine::getGameFlags() {
+ return _gameFlags;
}
-// There's no way this routine needs to still be here. In fact I'm not sure it needed to be in the game anyway?
int ImmortalEngine::getLevel() {
return _level;
}
-int ImmortalEngine::logicFreeze() {
- // Very silly way of checking if the level is over and/or the game is over
- int g = _gameOverFlag | _levelOver;
- return (g ^ 1) >> 1;
-}
-
void ImmortalEngine::gameOverDisplay() {
_themePaused = true;
- //text_print(kGameOverString)
- debug("GAME OVER");
+ textPrint(kStrGameOver);
}
void ImmortalEngine::gameOver() {
@@ -338,30 +571,20 @@ void ImmortalEngine::setSavedKing() {
_gameFlags |= kSavedKing;
}
-bool ImmortalEngine::isSavedKing() {
- if ((_gameFlags & kSavedKing) == 1) {
- return true;
- } else {
- return false;
- }
-}
-
void ImmortalEngine::setSavedAna() {
_gameFlags |= kSavedAna;
}
-bool ImmortalEngine::isSavedAna() {
- if ((_gameFlags & kSavedAna) == 1) {
- return true;
- } else {
- return false;
- }
-}
-bool ImmortalEngine::getCertificate() {
- return true;
+/*
+ * Not relevant yet (music)
+ */
+
+void ImmortalEngine::doGroan() {
+ //getRandom();
}
+
} // namespace Immortal
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 8f49b224363..39a86565558 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -57,13 +57,13 @@ void ImmortalEngine::myDelay() {}
*
*/
-bool ImmortalEngine::textPrint(const Common::String s) {
+bool ImmortalEngine::textPrint(Str s) {
return true;
}
void ImmortalEngine::textSub() {}
-void ImmortalEngine::textEnd() {}
-void ImmortalEngine::textMiddle() {}
-void ImmortalEngine::textBeginning() {}
+void ImmortalEngine::textEnd(Str s) {}
+void ImmortalEngine::textMiddle(Str s) {}
+void ImmortalEngine::textBeginning(Str s) {}
void ImmortalEngine::yesNo() {}
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
new file mode 100644
index 00000000000..644bbb13bc6
--- /dev/null
+++ b/engines/immortal/story.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 IMMORTAL_STORY_H
+#define IMMORTAL_STORY_H
+
+namespace Immortal {
+
+enum ObjFlag : uint8 {
+ kObjUsesFireButton = 0x40,
+ kObjIsInvisible = 0x20,
+ kObjIsRunning = 0x10,
+ kObjIsChest = 0x08,
+ kObjIsOnGround = 0x04,
+ kObjIsF1 = 0x02,
+ kObjIsF2 = 0x01
+};
+
+enum MonsterID {
+ kPlayerID
+};
+
+enum Str {
+ kStrOldGame,
+ kStrEnterCertificate,
+ kStrBadCertificate,
+ kStrCertificate,
+ kStrCertificate2,
+ kStrTitle0,
+ kStrTitle4,
+ kStrGold,
+ kStrYouWin,
+ kStrGameOver
+};
+
+struct Pickup {
+ //pointer to function
+ int _param;
+};
+
+struct Use {
+ //pointer to function
+ int _param;
+};
+
+struct ObjType {
+ Str _str;
+ Str _desc;
+ int _size;
+ Pickup _pickup;
+ Use _use;
+ Use _run;
+};
+
+} // namespace immortal
+
+#endif
\ No newline at end of file
Commit: daa43ce02b56588c96a4e5eca164fdb0cc8a81f6
https://github.com/scummvm/scummvm/commit/daa43ce02b56588c96a4e5eca164fdb0cc8a81f6
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Space/tab formatting
Changed paths:
engines/immortal/compression.cpp
engines/immortal/metaengine.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index aa0b2eae8cf..0f39f6ff806 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -1,4 +1,4 @@
- /* ScummVM - Graphic Adventure Engine
+/* 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
diff --git a/engines/immortal/metaengine.cpp b/engines/immortal/metaengine.cpp
index 1448f05a663..95fcb9ece99 100644
--- a/engines/immortal/metaengine.cpp
+++ b/engines/immortal/metaengine.cpp
@@ -33,15 +33,13 @@ Common::Error ImmortalMetaEngine::createInstance(OSystem *syst, Engine **engine,
}
bool ImmortalMetaEngine::hasFeature(MetaEngineFeature f) const {
- return false;
-/* return
- (f == kSavesUseExtendedFormat) ||
- (f == kSimpleSavesNames) ||
- (f == kSupportsListSaves) ||
- (f == kSupportsDeleteSave) ||
- (f == kSavesSupportMetaInfo) ||
- (f == kSavesSupportThumbnail) ||
- (f == kSupportsLoadingDuringStartup); */
+ return (f == kSavesUseExtendedFormat) ||
+ (f == kSimpleSavesNames) ||
+ (f == kSupportsListSaves) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSupportsLoadingDuringStartup);
}
#if PLUGIN_ENABLED_DYNAMIC(IMMORTAL)
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 39a86565558..fadbb9f4d6f 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -58,7 +58,7 @@ void ImmortalEngine::myDelay() {}
*/
bool ImmortalEngine::textPrint(Str s) {
- return true;
+ return true;
}
void ImmortalEngine::textSub() {}
void ImmortalEngine::textEnd(Str s) {}
Commit: a1b5ea7187d48cc3f7559d242ab5ab0addc57f97
https://github.com/scummvm/scummvm/commit/a1b5ea7187d48cc3f7559d242ab5ab0addc57f97
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Final two functions of Logic skeleton filled out (makecertificate() and miscinit())
Changed paths:
engines/immortal/immortal.h
engines/immortal/logic.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 584e7dc0f77..3f86e6764dc 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -202,7 +202,7 @@ public:
const int kNiceTime = 36;
const int kMaxCertificate = 16;
- // this should really be a char array, but inserting frame values will be stupid so it's just a string instead
+ // This should really be a char array, but inserting frame values will be stupid so it's just a string instead
const Common::String genStr[11] = {"New game?%", "Enter certificate:&-=", "Invalid certificate.@",
"End of level!&Here is your certificate:&&=", "&@",
" Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=", // Might need \ for something
@@ -469,6 +469,7 @@ public:
int logicFreeze(); // Overcomplicated way to check if game over or level over
void updateHitGauge();
void drawGauge(int h);
+ void makeCertificate();
void calcCheckSum(int l, uint8 checksum[]); // Checksum is one word, but the source called it CheckSum
bool getCertificate();
void printCertificate();
@@ -526,10 +527,10 @@ public:
Common::SeekableReadStream *unCompress(Common::File *src, int srcLen);
// Subroutines called by unCompress
- void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
- int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
+ void setupDictionary(uint16 start[], uint16 ptk[], uint16 &findEmpty);
+ int getInputCode(bool &carry, Common::File *src, int &srcLen, uint16 &evenOdd);
uint16 getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]);
- void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
+ void appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &findEmpty, uint16 start[], uint16 ptk[], uint16 &tmp);
/*
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index d8191ae7881..2a577a1e169 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -44,7 +44,7 @@ void ImmortalEngine::restartLogic() {
_gameFlags = kSavedNone;
// Here's where the majority of the game actually gets initialized
- //miscInit();
+ miscInit();
//qarrayInit();
//cycInit(); <-- room.initCycles()
//fsetInit(); <-- room.initTorches()
@@ -105,11 +105,8 @@ void ImmortalEngine::logic() {
textPrint(kStrYouWin);
} else {
- //makeCertificate();
- //printCertificate();
- if (_level == 0) {
- //manual2(); // <- debug?
- }
+ makeCertificate();
+ printCertificate();
_promoting = 1;
}
_restart = true;
@@ -400,6 +397,93 @@ bool ImmortalEngine::fromOldGame() {
return true;
}
+void ImmortalEngine::makeCertificate() {
+ /* The code for this bit doesn't really make sense,
+ * so I will write it as it is, but I am noting here
+ * that it should be:
+ * jsr monst_getGold : ... sta certificate+certgoldhi
+ * jsr monst_getQuickness : sta certificate+certquickness
+ * instead of getquickness : get gold : sta gold : sta quickness
+ * also no need to ldx 0 since this is player only ram right?
+ */
+
+ //uint8 q = room._playerQuickness
+ //uint16 g = room._playerGold
+ uint16 g = 0;
+
+ _certificate[kCertGoldLo] = g & 0xf;
+ _certificate[kCertGoldHi] = g >> 4;
+ _certificate[kCertQuickness] = g >> 4; // Should actually be = q, but this is what the game does
+
+ _certificate[kCertHits] = 0; //room._playerHits
+ _certificate[kCertLoGameFlags] = getGameFlags() & 0xf;
+ _certificate[kCertLoGameFlags] = getGameFlags() >> 4;
+
+ _certificate[kCertLevel] = _level + 1;
+ _certificate[kCertInvLo] = 0;
+ _certificate[kCertInvHi] = 0;
+
+ if (true/*room.monster[kPlayerID].hasObject(waterType)*/) {
+ _certificate[kCertInvHi] |= 1;
+ }
+
+ if (true/*room.monster[kPlayerID].hasObject(dunRingType)*/) {
+ _certificate[kCertInvHi] |= 2;
+ }
+
+ if (true/*room.monster[kPlayerID].hasObject(wormFoodType)*/) {
+ _certificate[kCertInvHi] |= 4;
+ }
+
+ if (true/*room.monster[kPlayerID].hasObject(coinType)*/) {
+ _certificate[kCertInvHi] |= 8;
+ }
+
+ // The lo byte of the inventory is used for items that only exist on a specific level, and are removed after
+ switch (_certificate[kCertLevel]) {
+ case 1:
+ if (true/*room.monster[kPlayerID].hasObject(sporesType)*/) {
+ _certificate[kCertInvLo] |= 2;
+ }
+
+ if (true/*room.monster[kPlayerID].hasObject(wowCharmType)*/) {
+ _certificate[kCertInvLo] |= 4;
+ }
+
+ case 3:
+ if (true/*room.monster[kPlayerID].hasObject(faceRingType)*/) {
+ _certificate[kCertInvLo] |= 1;
+ }
+
+ case 4:
+ if (true/*room.monster[kPlayerID].hasObject(coffeeType)*/) {
+ _certificate[kCertInvLo] |= 2;
+ }
+
+ case 7:
+ if (true/*room.monster[kPlayerID].hasObject(bronzeType)*/) {
+ _certificate[kCertInvLo] |= 1;
+ }
+
+ if (true/*room.monster[kPlayerID].hasObject(tractorType)*/) {
+ _certificate[kCertInvLo] |= 2;
+ }
+
+ if (true/*room.monster[kPlayerID].hasObject(antiType)*/) {
+ _certificate[kCertInvLo] |= 4;
+ }
+
+ default:
+ _lastCertLen = 13;
+ uint8 checksum[4];
+ calcCheckSum(_lastCertLen, checksum);
+ _certificate[0] = checksum[0];
+ _certificate[1] = checksum[1];
+ _certificate[2] = checksum[2];
+ _certificate[3] = checksum[3];
+ }
+}
+
void ImmortalEngine::calcCheckSum(int l, uint8 checksum[]) {
checksum[0] = 4;
checksum[1] = 0xa5;
@@ -434,10 +518,8 @@ bool ImmortalEngine::getCertificate() {
} else if (k == 0x7f) {
// The input was a backspace
if (certLen != 0) {
- // Length is one smaller now
- // move the drawing position back and reprint the '-' char
- certLen--;
- backspace();
+ certLen--; // Length is one smaller now
+ backspace(); // move the drawing position back and reprint the '-' char
backspace();
printChr('-');
}
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index fadbb9f4d6f..379e2571c1c 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -43,7 +43,11 @@ void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
g_system->delayMillis(j * 7);
}
-void ImmortalEngine::miscInit() {}
+void ImmortalEngine::miscInit() {
+ // In the source, this is where the seed for the rng is set, but we don't need to do that as we used _randomSource
+ _lastGauge = 0;
+}
+
void ImmortalEngine::setRandomSeed() {}
void ImmortalEngine::getRandom() {}
void ImmortalEngine::myDelay() {}
Commit: 09078a9de521e1825e1ea4ac9ef240779e9007a9
https://github.com/scummvm/scummvm/commit/09078a9de521e1825e1ea4ac9ef240779e9007a9
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: MonsterID enum moved to immortal.h
Changed paths:
engines/immortal/immortal.h
engines/immortal/story.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 3f86e6764dc..d56022d9bce 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -111,6 +111,10 @@ enum InputDirection {
kDirectionRight
};
+enum MonsterID {
+ kPlayerID
+};
+
enum CertIndex : uint8 {
kCertHits,
kCertLevel,
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 644bbb13bc6..bfd5d5435e7 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -34,10 +34,6 @@ enum ObjFlag : uint8 {
kObjIsF2 = 0x01
};
-enum MonsterID {
- kPlayerID
-};
-
enum Str {
kStrOldGame,
kStrEnterCertificate,
Commit: 8ecffca96630f151dc7ac82be7dcb099a6433ae0
https://github.com/scummvm/scummvm/commit/8ecffca96630f151dc7ac82be7dcb099a6433ae0
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add level skeleton
Changed paths:
A engines/immortal/level.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/module.mk
engines/immortal/story.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index d56022d9bce..913e6d2fac7 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -115,6 +115,12 @@ enum MonsterID {
kPlayerID
};
+enum LevelType {
+ kRoomType,
+ kMonsterType,
+ kObjectType
+};
+
enum CertIndex : uint8 {
kCertHits,
kCertLevel,
@@ -206,15 +212,16 @@ public:
const int kNiceTime = 36;
const int kMaxCertificate = 16;
+ // Max strings = 250
// This should really be a char array, but inserting frame values will be stupid so it's just a string instead
- const Common::String genStr[11] = {"New game?%", "Enter certificate:&-=", "Invalid certificate.@",
- "End of level!&Here is your certificate:&&=", "&@",
- " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=", // Might need \ for something
- " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
- "#" + Common::String(kGoldBigFrame) + "$0 gold@",
- "Congratulations!&&Play again?@",
- "Enter certificate:&-=",
- "Game Over&&Play again?@"};
+ const Common::String stringPtrs[250] = {"New game?%", "Enter certificate:&-=", "Invalid certificate.@",
+ "End of level!&Here is your certificate:&&=", "&@",
+ " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=", // Might need \ for something
+ " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
+ "#" + Common::String(kGoldBigFrame) + "$0 gold@",
+ "Congratulations!&&Play again?@",
+ "Enter certificate:&-=",
+ "Game Over&&Play again?@"};
// Screen constants
const int kResH = 320;
@@ -288,6 +295,10 @@ public:
const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
const char kGaugeStart = 1; // First kGaugeOn char to draw
+ // Level constants
+ const int kMaxFilesPerLevel = 16;
+ const int kMaxPartInstances = 4;
+ const int kLevelToMaze[8] = {0,0,1,1,2,2,2,3};
/*
* 'global' members
@@ -311,6 +322,24 @@ public:
int _maxLevels = 0; // This is determined when loading in story files
int _level = 0;
bool _levelOver = false;
+ int _count;
+ int _lastLevelLoaded;
+ int _lastSongLoaded;
+ int _storyLevel;
+ int _storyX;
+ int _loadA;
+ int _loadY;
+ uint16 _initialX;
+ uint16 _initialY;
+ int _initialBX;
+ int _initialBY;
+ int _dRoomNum;
+ int _initialRoom;
+ int _currentRoom;
+ int _lastType;
+ int _roomCellX;
+ int _roomCellY;
+ Story _stories[8];
Room *_rooms[kMaxRooms]; // Rooms within the level
// Debug members
@@ -404,6 +433,7 @@ public:
void playMazeSong();
void playCombatSong();
void doGroan();
+ void stopMusic();
void musicPause(int sID);
void musicUnPause(int sID);
void loadSingles(Common::String songName); // Loads and then parse the maze song
@@ -447,14 +477,22 @@ public:
/*
- * [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
+ * [DrawChr.cpp] Functions from DrawChr.cpp
*/
- // Init
- void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
-
// Main
- void superSprite(int s, uint16 x, uint16 y, Frame f, int bmw, byte *dst, int sT, int sB);
+ int mungeCBM(int numChrs);
+ void storeAddr();
+ void mungeSolid();
+ void mungeLRHC();
+ void mungeLLHC();
+ void mungeULHC();
+ void mungeURHC();
+ void drawSolid(int chr, int x, int y);
+ void drawULHC(int chr, int x, int y);
+ void drawURHC(int chr, int x, int y);
+ void drawLLHC(int chr, int x, int y);
+ void drawLRHC(int chr, int x, int y);
/*
@@ -523,6 +561,34 @@ public:
void insideRect(int p, int r);
+ /*
+ * [Level.cpp] Functions from level.GS
+ */
+ // Init
+ void levelInitAtStartOfGameOnly();
+ void levelInit();
+
+ // Main
+ void levelStory(int l);
+ void levelLoadFile(int l);
+ void levelNew(int l);
+ void levelDrawAll();
+ void levelShowRoom(int r, int bX, int bY);
+ bool levelIsShowRoom(int r);
+ bool levelIsLoaded(int l);
+ void univAtNew(int l);
+
+ /*
+ * [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
+ */
+
+ // Init
+ void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
+
+ // Main
+ void superSprite(int s, uint16 x, uint16 y, Frame f, int bmw, byte *dst, int sT, int sB);
+
+
/*
* [Compression.cpp] Functions from Compression.GS
*/
@@ -543,26 +609,6 @@ public:
// Misc
-
- /*
- * [DrawChr.cpp] Functions from DrawChr.cpp
- */
-
- // Main
- int mungeCBM(int numChrs);
- void storeAddr();
- void mungeSolid();
- void mungeLRHC();
- void mungeLLHC();
- void mungeULHC();
- void mungeURHC();
- void drawSolid(int chr, int x, int y);
- void drawULHC(int chr, int x, int y);
- void drawURHC(int chr, int x, int y);
- void drawLLHC(int chr, int x, int y);
- void drawLRHC(int chr, int x, int y);
-
-
/*
* --- ScummVM general engine Functions ---
*
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 79b15bfb514..50f9fa16caf 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -846,7 +846,11 @@ void ImmortalEngine::loadSingles(Common::String songName) {
debug("%s", songName.c_str());
}
-
+void ImmortalEngine::stopMusic() {
+ //musicStop(-1)
+ _playing = kSongNothing;
+ //stopSound();
+}
} // namespace Immortal
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
new file mode 100644
index 00000000000..878bbf0c5dc
--- /dev/null
+++ b/engines/immortal/level.cpp
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 "immortal/immortal.h"
+
+namespace Immortal {
+
+void ImmortalEngine::levelInitAtStartOfGameOnly() {
+ _lastLevelLoaded = -1;
+ _lastSongLoaded = -1;
+}
+
+void ImmortalEngine::levelInit() {
+ _count = 0;
+}
+
+void ImmortalEngine::levelNew(int l) {
+ stopMusic();
+ clearScreen();
+ /* commented out in the source for some reason? */
+ for (int i = 0; i < kMaxRooms; i++) {
+ //_rooms[i].delete();
+ }
+
+ levelStory(l);
+ if (kLevelToMaze[l] != _lastLevelLoaded) {
+ _lastLevelLoaded = kLevelToMaze[l];
+ //loadMaze(l);
+ }
+
+ if (_level != _lastSongLoaded) {
+ //loadSong(l);
+ }
+
+ //startMusic();
+ //monstSetXY -> _rooms[_currentRoom].monsters[kPlayerID].setXY(_initialBX, _initialBY);
+
+ //univSetXY(_initialX << 3, _initialY << 3);
+
+ levelShowRoom(_initialRoom, _initialBX, _initialBY);
+}
+
+void ImmortalEngine::levelStory(int l) {
+ levelLoadFile(l);
+}
+
+//loadStoryFiles() {}
+
+void ImmortalEngine::levelLoadFile(int l) {
+ /* Originally, this searched through story.gs and ignored the data entries.
+ * However, we have the STR entries separate from the story entries, so
+ * we can just index the story files array directly.
+ * It also used a 16 byte buffer to read in a story entry, but again this
+ * is equivalent to the overhead of reading the array entry I think.
+ */
+
+ _dRoomNum = 0;
+ bool done = false;
+ int type = 0;//story[l];
+
+ // instead of switch statement, just make a room for each, because the rooms will have the relevant info
+
+ // Once again, would be better as an indexed JSR instead of a set of comparisons and branches
+ while (done == false) {
+ switch (type & kOPMaskRecord) {
+ case kOPMaskRoom:
+// roomNew();
+ break;
+ case kOPMaskInRoom:
+// inRoomNew();
+ break;
+ case kOPMaskFlame:
+// fsetNew();
+ break;
+ case kOPMaskUnivAt:
+ univAtNew(l);
+ break;
+ case kOPMaskMonster:
+// monstNew();
+ break;
+ case kOPMaskDoor:
+// doorNew();
+ break;
+ case kOPMaskObject:
+// objectNew();
+ break;
+ default:
+ done = true;
+ }
+ }
+}
+
+void ImmortalEngine::univAtNew(int l) {
+ _initialRoom = _dRoomNum;
+ _initialX = 0;//_stories[l]._initialX;
+ _initialY = 0;//_stories[l]._initialY;
+ _initialBX = 0;//_stories[l]._initialBX;
+ _initialBY = 0;//_stories[l]._initialBY;
+ //doorToNextLevel(_stories[l]._doorToNextLevel, _initialBX, _initialBY);
+ //doorSetLadders(_stories[l]._doorSetLadders);
+ //roomSetHole(_stories[l]._setHole, _stories[l]._setHoleX, _stories[l]._setHoleY);
+ //monstRepos(kPlayerID);
+
+}
+
+void ImmortalEngine::levelDrawAll() {
+ _count++;
+ //univAutoCenter();
+ clearSprites();
+ //rooms[_currentRoom].drawContents();
+}
+
+void ImmortalEngine::levelShowRoom(int r, int bX, int bY) {
+ _currentRoom = r;
+ //univSetRoom(r, bX, bY);
+ //fset, spark, bullet, and door get set to the current room
+ //roomGetCell(r, bX, bY);
+ //x, y <- roomGetXY(r, bX, bY);
+ //x += bX;
+ //y += bY;
+ //x <<= 1;
+ //blister();
+}
+
+bool ImmortalEngine::levelIsLoaded(int l) {
+ if (l == _storyLevel) {
+ return true;
+ }
+ return false;
+}
+
+bool ImmortalEngine::levelIsShowRoom(int r) {
+ if (r == _currentRoom) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 2a577a1e169..ebeea26369a 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -34,7 +34,7 @@ void ImmortalEngine::logicInit() {
_time = 0;
_promoting = 0;
_restart = true;
- //level_initAtStartOfGameOnly
+ levelInitAtStartOfGameOnly();
_lastCertLen = 0;
}
@@ -48,7 +48,7 @@ void ImmortalEngine::restartLogic() {
//qarrayInit();
//cycInit(); <-- room.initCycles()
//fsetInit(); <-- room.initTorches()
- //levelInit(); <-- presumably creates room
+ levelInit();
//roomInit(); <-- will be run in constructor of room
//monstInit(); <-- room.initMonsters() \
//objectInit(); <-- room.initObjects()
@@ -64,7 +64,7 @@ void ImmortalEngine::restartLogic() {
if (fromOldGame() == false) {
_level = 0;
- //levelNew(_level);
+ levelNew(_level);
}
if (_level != 7) {
@@ -118,7 +118,7 @@ void ImmortalEngine::logic() {
//monstRunAll();
//objectRunAll();
//doInfiniteHallways();
- //levelDrawAll();
+ levelDrawAll();
updateHitGauge();
// What the heck? Check if we are in level 0: room 0, with no lit torches, and no projectiles
@@ -393,7 +393,7 @@ bool ImmortalEngine::fromOldGame() {
default:
break;
}
- //levelNew(_level)
+ levelNew(_level);
return true;
}
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 18b5dfdf28b..db9aab28f48 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -10,9 +10,9 @@ MODULE_OBJS = \
compression.o \
misc.o \
cycle.o \
- drawChr.o
+ drawChr.o \
+ level.o
-# level.o \
# universe.o \
# room.o \
# object.o \
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index bfd5d5435e7..42f0d009ea8 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -24,6 +24,17 @@
namespace Immortal {
+enum OPMask : uint8 {
+ kOPMaskRoom,
+ kOPMaskInRoom,
+ kOPMaskFlame,
+ kOPMaskUnivAt,
+ kOPMaskMonster,
+ kOPMaskDoor,
+ kOPMaskObject,
+ kOPMaskRecord
+};
+
enum ObjFlag : uint8 {
kObjUsesFireButton = 0x40,
kObjIsInvisible = 0x20,
@@ -47,6 +58,10 @@ enum Str {
kStrGameOver
};
+struct Story {
+ int x;
+};
+
struct Pickup {
//pointer to function
int _param;
Commit: 8516b39aa1a50722b74a03526f87d6b222d7d687
https://github.com/scummvm/scummvm/commit/8516b39aa1a50722b74a03526f87d6b222d7d687
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add story.cpp and related additions to story.h
Changed paths:
A engines/immortal/story.cpp
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/module.mk
engines/immortal/story.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 913e6d2fac7..3296f741ea4 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -87,13 +87,22 @@ enum ChrMask : uint16 {
};
enum Screen { // These are constants that are used for defining screen related arrays
- kMaxRooms = 16, // Should probably put this in a different enum
kMaxSprites = 32, // Number of sprites allowed at once
kViewPortCW = 256 / 64,
kViewPortCH = 128 / kMaxSprites,
kMaxDrawItems = kViewPortCH + 1 + kMaxSprites
};
+enum StoryMaxes {
+ kMaxRooms = 16,
+ kMaxDoors = 10,
+ kMaxFlames = 32,
+ kMaxFlamesInRoom = 5,
+ kMaxObjects = 42,
+ kMaxMonsters = 20,
+ kMaxGenSprites = 6
+};
+
enum InputAction {
kActionNothing,
kActionKey,
@@ -141,9 +150,9 @@ enum Song {
};
enum GameFlags : uint8 {
- kSavedNone = 0,
- kSavedKing = 1,
- kSavedAna = 2
+ kSavedNone,
+ kSavedKing,
+ kSavedAna
};
struct Frame {
@@ -222,7 +231,6 @@ public:
"Congratulations!&&Play again?@",
"Enter certificate:&-=",
"Game Over&&Play again?@"};
-
// Screen constants
const int kResH = 320;
const int kResV = 200;
@@ -296,9 +304,10 @@ public:
const char kGaugeStart = 1; // First kGaugeOn char to draw
// Level constants
+ const int kStoryNull = 5;
const int kMaxFilesPerLevel = 16;
const int kMaxPartInstances = 4;
- const int kLevelToMaze[8] = {0,0,1,1,2,2,2,3};
+ const int kLevelToMaze[8] = {0,0,1,1,2,2,2,3};
/*
* 'global' members
@@ -318,6 +327,9 @@ public:
int _promoting = 0; // I think promoting means the title stuff
bool _restart = false;
+ // Story members
+ Story _stories[8];
+
// Level members
int _maxLevels = 0; // This is determined when loading in story files
int _level = 0;
@@ -339,7 +351,6 @@ public:
int _lastType;
int _roomCellX;
int _roomCellY;
- Story _stories[8];
Room *_rooms[kMaxRooms]; // Rooms within the level
// Debug members
@@ -578,6 +589,13 @@ public:
bool levelIsLoaded(int l);
void univAtNew(int l);
+
+ /*
+ * [Story.cpp] Functions from Story.cpp
+ */
+ // Init
+ void loadStoryFiles();
+
/*
* [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
*/
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 878bbf0c5dc..1569990829d 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -24,6 +24,7 @@
namespace Immortal {
void ImmortalEngine::levelInitAtStartOfGameOnly() {
+ loadStoryFiles();
_lastLevelLoaded = -1;
_lastSongLoaded = -1;
}
@@ -62,8 +63,6 @@ void ImmortalEngine::levelStory(int l) {
levelLoadFile(l);
}
-//loadStoryFiles() {}
-
void ImmortalEngine::levelLoadFile(int l) {
/* Originally, this searched through story.gs and ignored the data entries.
* However, we have the STR entries separate from the story entries, so
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index db9aab28f48..6c460cff60a 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -11,7 +11,8 @@ MODULE_OBJS = \
misc.o \
cycle.o \
drawChr.o \
- level.o
+ level.o \
+ story.o
# universe.o \
# room.o \
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
new file mode 100644
index 00000000000..a32b149c3a9
--- /dev/null
+++ b/engines/immortal/story.cpp
@@ -0,0 +1,137 @@
+/* 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/>.
+ *
+ */
+
+/* [Alternate Name: Level Subsystem/Script Data]
+ * --- Story File ---
+ * A story file (as defined in story.h) is a set of ROM
+ * data that describes the properties of a level. This includes
+ * the coordinates for each room, the doors in the level,
+ * the torches, objects, monsters, etc. It also included the string
+ * data in the source code, but technically it was writing those
+ * strings to a separate string bank, so they weren't contiguous
+ * with the story files. These story files are read in when loading
+ * a new level, and are used to construct the room object, the monster
+ * objects, and everything in the rooms.
+ */
+
+#include "immortal/immortal.h"
+
+namespace Immortal {
+
+void ImmortalEngine::loadStoryFiles() {
+ /* The way I am doing this, there will be essentially duplicate data
+ * for the in-room objects. This is because the original also
+ * effectively duplicated data. There was less overhead of course,
+ * as these structs are considered classes by c++. However I think
+ * that logically speaking, this is what the original was doing.
+ * It's not ideal to do it this way, but my guess for why the source
+ * didn't just read directly (ex. door object data will never change,
+ * it is ROM) is that the story bank was too far from the general work
+ * memory used for the object data. Could be something else though.
+ */
+
+ // Level 0: Intro 1
+
+ _stories[0]._levelNum = 0; // These aren't really needed anymore
+ _stories[0]._partNum = 1;
+
+ int univRoom = 4; // The room the player starts in when beginning this level
+ uint8 univRoomX = 512;
+ uint8 univRoomY = 416;
+ int byteArray[] = {-1, -1, kStoryNull, 2, 0, univRoom, (704 / 64),(544 / 32)};
+ _stories[0]._UnivAt = UnivAt(1024 / 8, 480 / 8, (1152 - univRoomX) / 2, 464 - univRoomY, byteArray);
+
+ // All of the rooms for level 0
+ SRoom rooms[8] = {SRoom(384, 256, kRoomFlag0), SRoom(512, 64, kRoomFlag0), SRoom(640, 160, kRoomFlag0), SRoom(768, 224, kRoomFlag0),
+ SRoom(univRoomX, univRoomY, kRoomFlag0), SRoom(960, 512, kRoomFlag0), SRoom(1024, 352, kRoomFlag0), SRoom(896, 64, kRoomFlag0)};
+ _stories[0]._rooms = rooms;
+
+ // All of the doors for level 0
+ SDoor doors[7] = {SDoor(kLeft, 704, 224, 0, 2, false), SDoor(kRight, 576, 352, 4, 0, true),
+ SDoor(kRight, 704,96, 2, 1, false), SDoor(kRight, 960,128, 7, 2, false),
+ SDoor(kRight, 1088,160, 3, 7, false), SDoor(kRight, 1088,320, 6, 3, false),
+ SDoor(kRight, 896,416, 4, 3, false)};
+ _stories[0]._doors = doors;
+
+ // All of the flames for level 0
+ // Macro for flames is (x - roomx), (y - roomy), pattern number
+ SFlame f5[2] = {SFlame(512 - 384, (240 + 32) - 256, kFlameOff), SFlame(672 - 384, (240 + 32) - 256, kFlameOff)};
+ SFlame f7[3] = {SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(928 - 384, (48 + 32) - 256, kFlameNormal)};
+ SFlame f8[1] = {SFlame(800 - 640, (144 + 32) - 160, kFlameNormal)};
+ SFlame f9[3] = {SFlame(768 - 768, (304 + 32) - 224, kFlameNormal), SFlame((928 - 768), (304 + 32) - 224, kFlameNormal), SFlame((1024 - 768), (240 + 32) - 224, kFlameNormal)};
+ SFlame fA[3] = {SFlame(672 - 512, (400 + 32) - 416, kFlameNormal), SFlame((800 - 64) - 512, (496 - 32) - 416, kFlameNormal), SFlame(576 - 512, (528 + 32) - 416, kFlameNormal)};
+ SFlame fD[1] = {SFlame(1024 - 960, (496 + 32) - 512, kFlameNormal)};
+ SFlame fE[1] = {SFlame(1184 - 1024, 432 - 352, kFlameCandle)};
+ SFlame fF[1] = {SFlame(1024 - 896, (144 + 32) - 64, kFlameNormal)};
+ SFlame *flames[8] = {f5, f7, f8, f9, fA, fD, fE, fF};
+ _stories[0]._flames = flames;
+
+ // All of the objects for level 0
+ SObj o5[3];
+ SObj o7[1];
+ SObj o8[1];
+ SObj o9[9];
+ SObj oE[4];
+ SObj *objects[8] = {o5, o7, o8, o9, nullptr, nullptr, oE, nullptr};
+ _stories[0]._objects = objects;
+
+ // All of the monsters for level 0
+ SMonster m5[2];
+ SMonster m9[3];
+ SMonster mE[1];
+ SMonster mF[1];
+ SMonster *monsters[8] = {m5, nullptr, nullptr, m9, nullptr, nullptr, mE, mF};
+ _stories[0]._monsters = monsters;
+}
+
+} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 42f0d009ea8..3524a3c3270 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -24,6 +24,25 @@
namespace Immortal {
+enum DoorDir : bool {
+ kLeft = false,
+ kRight = true
+};
+
+enum RoomFlag : uint8 {
+ kRoomFlag0 = 0x1,
+ kRoomFlag1 = 0x2,
+ kRoomFlag2 = 0x4,
+ kRoomFlag3 = 0x8
+};
+
+enum FPattern {
+ kFlameNormal,
+ kFlameCandle,
+ kFlameOff,
+ kFlameGusty
+};
+
enum OPMask : uint8 {
kOPMaskRoom,
kOPMaskInRoom,
@@ -45,6 +64,22 @@ enum ObjFlag : uint8 {
kObjIsF2 = 0x01
};
+enum MonsterFlag : uint8 {
+ kMonstIsTough = 0x10,
+ kMonstIsDead = 0x20,
+ kMonstIsPoss = 0x40,
+ kMonstIsBaby = 0x40,
+ kMonstIsEngage = 0x80
+};
+
+enum IsA : uint8 {
+ kIsAF1 = 0x20,
+ kIsAF2 = 0x40
+};
+
+enum Program { // This will likely be moved to a monster ai specific file later
+};
+
enum Str {
kStrOldGame,
kStrEnterCertificate,
@@ -55,11 +90,7 @@ enum Str {
kStrTitle4,
kStrGold,
kStrYouWin,
- kStrGameOver
-};
-
-struct Story {
- int x;
+ kStrGameOver,
};
struct Pickup {
@@ -76,11 +107,137 @@ struct ObjType {
Str _str;
Str _desc;
int _size;
- Pickup _pickup;
+ Pickup _pickup;
Use _use;
Use _run;
};
+
+/* Strictly speaking, many of these structs (which were rom data written dynamically
+ * with compiler macros) combine multiple properties into single bytes (ex. room uses
+ * bits 0-2 of X to also hold the roomOP, and bits 0-2 of Y to hold flags). However
+ * for the moment there's no need to replicate this particular bit of space saving.
+ */
+struct SRoom {
+ uint8 _x;
+ uint8 _y;
+ RoomFlag _flags;
+ SRoom() {}
+ SRoom(uint8 x, uint8 y, RoomFlag f) {
+ _x = x;
+ _y = y;
+ _flags = f;
+ }
+};
+
+struct SDoor {
+ DoorDir _dir;
+ uint8 _x;
+ uint8 _y;
+ uint8 _fromRoom;
+ uint8 _toRoom;
+ bool _isLocked;
+ SDoor() {}
+ SDoor(DoorDir d, uint8 x, uint8 y, uint8 f, uint8 t, bool l) {
+ _dir = d;
+ _x = x;
+ _y = y;
+ _fromRoom = f;
+ _toRoom = t;
+ _isLocked = l;
+ }
+};
+
+struct SFlame {
+ uint8 _x;
+ uint8 _y;
+ FPattern _pattern;
+ SFlame() {}
+ SFlame(uint8 x, uint8 y, FPattern p) {
+ _x = x;
+ _y = y;
+ _pattern = p;
+ }
+};
+
+struct UnivAt {
+ uint8 _initialUnivX;
+ uint8 _initialUnivY;
+ uint8 _playerPointX;
+ uint8 _playerPointY;
+ int *_ladders;
+ UnivAt() {}
+ UnivAt(uint8 iX, uint8 iY, uint8 pX, uint8 pY, int l[]) {
+ _initialUnivX = iX;
+ _initialUnivY = iY;
+ _playerPointX = pX;
+ _playerPointY = pY;
+ _ladders = l;
+ }
+};
+
+struct SObj {
+ uint8 _x;
+ uint8 _y;
+ ObjType _type;
+ ObjFlag _flags;
+ uint8 _tmp;
+SpriteFrame _frame;
+ SObj() {}
+ SObj(uint8 x, uint8 y, ObjType t, ObjFlag f, uint8 tmp, SpriteFrame s) {
+ _x = x;
+ _y = y;
+ _type = t;
+ _flags = f;
+ _tmp = tmp;
+ _frame = s;
+ }
+};
+
+struct SMonster {
+ uint8 _x;
+ uint8 _y;
+ uint8 _hits;
+ uint8 _madAt;
+ IsA _isA;
+ Program _program;
+ SpriteName _sprite;
+MonsterFlag _flags;
+ SMonster() {}
+ SMonster(uint8 x, uint8 y, uint8 h, uint8 m, IsA i, Program p, SpriteName s, MonsterFlag mf) {
+ _x = x;
+ _y = y;
+ _hits = h;
+ _madAt = m;
+ _isA = i;
+ _program = p;
+ _sprite = s;
+ _flags = mf;
+ }
+};
+
+struct Story {
+ int _levelNum;
+ int _partNum;
+ UnivAt _UnivAt;
+ SRoom *_rooms;
+ SDoor *_doors;
+ SFlame **_flames;
+ SObj **_objects;
+SMonster **_monsters;
+};
+
} // namespace immortal
-#endif
\ No newline at end of file
+#endif
+
+
+
+
+
+
+
+
+
+
+
Commit: 35d8b166fe0f5d3631dee0c115b83f5da1a4d122
https://github.com/scummvm/scummvm/commit/35d8b166fe0f5d3631dee0c115b83f5da1a4d122
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Level 0 of story.cpp completed, story.h filled out, and related level.cpp revised
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/sprite_list.h
engines/immortal/story.cpp
engines/immortal/story.h
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index c02570f93d6..e0a07d5cbde 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -19,21 +19,27 @@
*
*/
-#include "immortal/immortal.h"
+/* [Alternate Name: Sprite Animation Processing]
+ * --- What is a Cycle ---
+ */
+
+//#include "immortal/room.h"
namespace Immortal {
-void ImmortalEngine::cycleNew() {}
- int ImmortalEngine::getCycleChr() {
+// Most of these functions can probably be removed eventually
+/*
+void Room::cycleNew() {}
+ int Room::getCycleChr() {
return 0;
}
-void ImmortalEngine::cycleFreeAll() {}
-void ImmortalEngine::cycleGetFile() {}
-void ImmortalEngine::cycleGetNum() {}
-void ImmortalEngine::cycleGetIndex() {}
-void ImmortalEngine::cycleSetIndex() {}
-void ImmortalEngine::cycleGetFrame() {}
-void ImmortalEngine::cycleAdvance() {}
-
+void Room::cycleFreeAll() {}
+void Room::cycleGetFile() {}
+void Room::cycleGetNum() {}
+void Room::cycleGetIndex() {}
+void Room::cycleSetIndex() {}
+void Room::cycleGetFrame() {}
+void Room::cycleAdvance() {}
+*/
} // namespace Immortal
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 3296f741ea4..856616dafae 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -578,6 +578,7 @@ public:
// Init
void levelInitAtStartOfGameOnly();
void levelInit();
+ //void levelGetCount <-- lda count
// Main
void levelStory(int l);
@@ -588,6 +589,9 @@ public:
bool levelIsShowRoom(int r);
bool levelIsLoaded(int l);
void univAtNew(int l);
+ //void getLastType <-- lda lastType
+ //void setLastType <-- sta lastType
+ //void getShowRoom <-- lda currentRoom
/*
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 1569990829d..91c3aa43b2b 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -64,60 +64,58 @@ void ImmortalEngine::levelStory(int l) {
}
void ImmortalEngine::levelLoadFile(int l) {
- /* Originally, this searched through story.gs and ignored the data entries.
- * However, we have the STR entries separate from the story entries, so
- * we can just index the story files array directly.
- * It also used a 16 byte buffer to read in a story entry, but again this
- * is equivalent to the overhead of reading the array entry I think.
+ _dRoomNum = 0;
+
+ /* This was originally a large branching tree that checked the identifier of each entry and
+ * Processed them all for the story. Once again, this would have been better as an indexed
+ * JSR instead of a set of comparisons and branches. Regardless, we instead use the information
+ * in the story struct to create the rooms and then populate them.
*/
- _dRoomNum = 0;
- bool done = false;
- int type = 0;//story[l];
-
- // instead of switch statement, just make a room for each, because the rooms will have the relevant info
-
- // Once again, would be better as an indexed JSR instead of a set of comparisons and branches
- while (done == false) {
- switch (type & kOPMaskRecord) {
- case kOPMaskRoom:
-// roomNew();
- break;
- case kOPMaskInRoom:
-// inRoomNew();
- break;
- case kOPMaskFlame:
-// fsetNew();
- break;
- case kOPMaskUnivAt:
- univAtNew(l);
- break;
- case kOPMaskMonster:
-// monstNew();
- break;
- case kOPMaskDoor:
-// doorNew();
- break;
- case kOPMaskObject:
-// objectNew();
- break;
- default:
- done = true;
+ // Create the rooms and doors, then populate the rooms with their objects and actors
+ for (int r = 0; r < _stories[l]._rooms.size(); r++) {
+ //roomNew(_stories[l]._rooms[i]);
+ //doorNew(_stories[l]._doors[i]);
+ debug("Room %d", r);
+ for (int f = 0; f < _stories[l]._flames.size(); f++) {
+ if (_stories[l]._flames[r].size() > 0) {
+ //fsetNew(_stories[l]._flames[r][f]);
+ debugN("F%d", f);
+ }
+ }
+ debug("");
+
+ for (int o = 0; o < _stories[l]._objects.size(); o++) {
+ if (_stories[l]._objects[r].size() > 0) {
+ //objNew(_stories[l]._objects[r][o]);
+ debugN("O%d", o);
+ }
}
+ debug("");
+
+ for (int m = 0; m < _stories[l]._monsters.size(); m++) {
+ if (_stories[l]._monsters[r].size() > 0) {
+ //monstNew(_stories[l]._monsters[r][m]);
+ debugN("M%d", m);
+ }
+ }
+ debug("");
}
+
+ // Set up the _initial variables for the engine scope
+ univAtNew(l);
}
void ImmortalEngine::univAtNew(int l) {
_initialRoom = _dRoomNum;
- _initialX = 0;//_stories[l]._initialX;
- _initialY = 0;//_stories[l]._initialY;
- _initialBX = 0;//_stories[l]._initialBX;
- _initialBY = 0;//_stories[l]._initialBY;
+ _initialX = _stories[l]._initialUnivX;
+ _initialY = _stories[l]._initialUnivY;
+ _initialBX = _stories[l]._playerPointX;
+ _initialBY = _stories[l]._playerPointY;
//doorToNextLevel(_stories[l]._doorToNextLevel, _initialBX, _initialBY);
//doorSetLadders(_stories[l]._doorSetLadders);
//roomSetHole(_stories[l]._setHole, _stories[l]._setHoleX, _stories[l]._setHoleY);
//monstRepos(kPlayerID);
-
}
void ImmortalEngine::levelDrawAll() {
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index b4e80963e62..a0829331250 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -25,8 +25,11 @@
namespace Immortal {
enum SpriteFrame {
+ // Null
+ kNoFrame,
+
// Chest frames
- kChest0Frame,
+ kChest0Frame = 0,
kOpenChestFrame,
kRingFrame,
kKnifeFrame,
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index a32b149c3a9..f35d7b8f3b8 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -27,79 +27,155 @@
* the torches, objects, monsters, etc. It also included the string
* data in the source code, but technically it was writing those
* strings to a separate string bank, so they weren't contiguous
- * with the story files. These story files are read in when loading
+ * with the story files (sometimes?). These story files are read in when loading
* a new level, and are used to construct the room object, the monster
* objects, and everything in the rooms.
*/
+/* UNIVAT 1024,480, 1152, 464, \-1, -1, zip,level1Ladders, rooma, 704/64, 544/32\
+ UNIVAT 304, 448, 472+32, 500+16, \-1, -1, zip,level12Ladders, -1, 0, 0\
+ UNIVAT 600, 450, 560, 598, \-1, r2.b+(16*r2.a), zip,level3Ladders, r2.b, 640/64, 576/32\
+ UNIVAT 120, 540, 188, 584, \-1, -1, zip,level4Ladders, -1, 0, 0\
+ UNIVAT 64, 128, 128, 128+32, \-1, -1, zip,level5Ladders, -1, 1088/64, 928/32\
+ UNIVAT 768, 224, 896, 288-16, \-1, -1, zip,level5Ladders, -1, 1088/64, 928/32\
+ UNIVAT 896, 672+64, 960, 832-16, \-1, -1, zip,level6Ladders, -1, 0, 0\
+ UNIVAT 688, 800, 912-64, 888-32, \-1, -1, zip,level7Ladders, -1, 1088/64, 928/32\
+ UNIVAT 64, 704, 64+96, 704+64, \-1, -1, zip,level8Ladders, -1, 0, 0\
+*/
+
#include "immortal/immortal.h"
namespace Immortal {
void ImmortalEngine::loadStoryFiles() {
- /* The way I am doing this, there will be essentially duplicate data
- * for the in-room objects. This is because the original also
- * effectively duplicated data. There was less overhead of course,
- * as these structs are considered classes by c++. However I think
- * that logically speaking, this is what the original was doing.
- * It's not ideal to do it this way, but my guess for why the source
- * didn't just read directly (ex. door object data will never change,
- * it is ROM) is that the story bank was too far from the general work
- * memory used for the object data. Could be something else though.
+ /* There is one major difference between the source logic and this method.
+ * It doesn't change the game logic, but it does change the logic of storing
+ * the initial rom data. In the source, because there are no language based
+ * arrays available (the array/qarray have overhead and are not designed for this),
+ * the story entries are written out dynamically to ensure everything links together
+ * (in quite a clever way, but does require a lot of untangling to see).
+ * On the game end however, this means that to populate a level with it's objects,
+ * rooms, etc. It has to look at every single entry individually, and check the 'recordop'.
+ * This tells the game what kind of entry it is, and therefor which routine to call.
+ * But, the catch is that making sure the right entry goes with the right room is tricky.
+ * In certain cases, there are references to the rooms. In most however it relies on
+ * INROOM, which is a macro that basically sets the dynamic variable keeping track of what
+ * room the current entry is using for x/y coordinates. This doesn't serve any purpose
+ * for us though, because we can use real arrays and structs for the stories, which is what
+ * I believe the source would have used (though even the DOS version did it this way so
+ * who knows). All of this to say, instead of INROOM, the equivlent here is basically
+ * checking for nullptr within arrays that are always the size of the number of rooms.
*/
- // Level 0: Intro 1
+ // *NOTE* the data types Trap and Program will be in the static Story area, and referenced by an enum
+
+ const uint8 kZip = 5;
- _stories[0]._levelNum = 0; // These aren't really needed anymore
- _stories[0]._partNum = 1;
-
+ /*
+ * ::: Level 0: Intro 1 :::
+ */
+
+ /* Universe related properties
+ * including spawn point and entry/exit points
+ */
int univRoom = 4; // The room the player starts in when beginning this level
uint8 univRoomX = 512;
uint8 univRoomY = 416;
- int byteArray[] = {-1, -1, kStoryNull, 2, 0, univRoom, (704 / 64),(544 / 32)};
- _stories[0]._UnivAt = UnivAt(1024 / 8, 480 / 8, (1152 - univRoomX) / 2, 464 - univRoomY, byteArray);
- // All of the rooms for level 0
- SRoom rooms[8] = {SRoom(384, 256, kRoomFlag0), SRoom(512, 64, kRoomFlag0), SRoom(640, 160, kRoomFlag0), SRoom(768, 224, kRoomFlag0),
- SRoom(univRoomX, univRoomY, kRoomFlag0), SRoom(960, 512, kRoomFlag0), SRoom(1024, 352, kRoomFlag0), SRoom(896, 64, kRoomFlag0)};
+ _stories[0]._level = 0;
+ _stories[0]._part = 1;
+ _stories[0]._initialUnivX = 1024 / 8;
+ _stories[0]._initialUnivY = 480 / 8;
+ _stories[0]._playerPointX = (1152 - univRoomX) / 2;
+ _stories[0]._playerPointY = 464 - univRoomY;
+
+ Common::Array<int> ladders{-1, -1, kStoryNull, 2, 0, univRoom, (704 / 64),(544 / 32)};
+ _stories[0]._ladders = ladders;
+
+ /* All of the rooms
+ */
+ Common::Array<SRoom> rooms{SRoom(384, 256, kRoomFlag0), SRoom(512, 64, kRoomFlag0),
+ SRoom(640, 160, kRoomFlag0), SRoom(768, 224, kRoomFlag0),
+ SRoom(univRoomX, univRoomY, kRoomFlag0), SRoom(960, 512, kRoomFlag0),
+ SRoom(1024, 352, kRoomFlag0), SRoom(896, 64, kRoomFlag0)};
_stories[0]._rooms = rooms;
- // All of the doors for level 0
- SDoor doors[7] = {SDoor(kLeft, 704, 224, 0, 2, false), SDoor(kRight, 576, 352, 4, 0, true),
- SDoor(kRight, 704,96, 2, 1, false), SDoor(kRight, 960,128, 7, 2, false),
- SDoor(kRight, 1088,160, 3, 7, false), SDoor(kRight, 1088,320, 6, 3, false),
- SDoor(kRight, 896,416, 4, 3, false)};
+ /* All of the doors
+ */
+ Common::Array<SDoor> doors{SDoor(kLeft, 704, 224, 0, 2, false), SDoor(kRight, 576, 352, 4, 0, true),
+ SDoor(kRight, 704,96, 2, 1, false), SDoor(kRight, 960,128, 7, 2, false),
+ SDoor(kRight, 1088,160, 3, 7, false), SDoor(kRight, 1088,320, 6, 3, false),
+ SDoor(kRight, 896,416, 4, 3, false)};
_stories[0]._doors = doors;
- // All of the flames for level 0
- // Macro for flames is (x - roomx), (y - roomy), pattern number
- SFlame f5[2] = {SFlame(512 - 384, (240 + 32) - 256, kFlameOff), SFlame(672 - 384, (240 + 32) - 256, kFlameOff)};
- SFlame f7[3] = {SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(928 - 384, (48 + 32) - 256, kFlameNormal)};
- SFlame f8[1] = {SFlame(800 - 640, (144 + 32) - 160, kFlameNormal)};
- SFlame f9[3] = {SFlame(768 - 768, (304 + 32) - 224, kFlameNormal), SFlame((928 - 768), (304 + 32) - 224, kFlameNormal), SFlame((1024 - 768), (240 + 32) - 224, kFlameNormal)};
- SFlame fA[3] = {SFlame(672 - 512, (400 + 32) - 416, kFlameNormal), SFlame((800 - 64) - 512, (496 - 32) - 416, kFlameNormal), SFlame(576 - 512, (528 + 32) - 416, kFlameNormal)};
- SFlame fD[1] = {SFlame(1024 - 960, (496 + 32) - 512, kFlameNormal)};
- SFlame fE[1] = {SFlame(1184 - 1024, 432 - 352, kFlameCandle)};
- SFlame fF[1] = {SFlame(1024 - 896, (144 + 32) - 64, kFlameNormal)};
- SFlame *flames[8] = {f5, f7, f8, f9, fA, fD, fE, fF};
+ /* All of the flames
+ * Macro for flames is (x - roomx), (y - roomy), pattern number
+ */
+ Common::Array<SFlame> f5{SFlame(512 - 384, (240 + 32) - 256, kFlameOff), SFlame(672 - 384, (240 + 32) - 256, kFlameOff)};
+ Common::Array<SFlame> f7{SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(576 - 384, (112 + 32) - 256, kFlameNormal),
+ SFlame(928 - 384, (48 + 32) - 256, kFlameNormal)};
+ Common::Array<SFlame> f8{SFlame(800 - 640, (144 + 32) - 160, kFlameNormal)};
+ Common::Array<SFlame> f9{SFlame(768 - 768, (304 + 32) - 224, kFlameNormal), SFlame((928 - 768), (304 + 32) - 224, kFlameNormal),
+ SFlame(1024 - 768, (240 + 32) - 224, kFlameNormal)};
+ Common::Array<SFlame> fA{SFlame(672 - 512, (400 + 32) - 416, kFlameNormal), SFlame((800 - 64) - 512, (496 - 32) - 416, kFlameNormal),
+ SFlame(576 - 512, (528 + 32) - 416, kFlameNormal)};
+ Common::Array<SFlame> fD{SFlame(1024 - 960, (496 + 32) - 512, kFlameNormal)};
+ Common::Array<SFlame> fE{SFlame(1184 - 1024, 432 - 352, kFlameCandle)};
+ Common::Array<SFlame> fF{SFlame(1024 - 896, (144 + 32) - 64, kFlameNormal)};
+ CArray2D<SFlame> flames{f5, f7, f8, f9, fA, fD, fE, fF};
_stories[0]._flames = flames;
- // All of the objects for level 0
- SObj o5[3];
- SObj o7[1];
- SObj o8[1];
- SObj o9[9];
- SObj oE[4];
- SObj *objects[8] = {o5, o7, o8, o9, nullptr, nullptr, oE, nullptr};
+ /* All of the objects
+ * Macro for traps is arrowType,freq,#sinkTraps,#1(going toward 5),#3,#5,#7,#trapdoors
+ */
+ Common::Array<uint8> noTraps{};
+ Common::Array<uint8> o5Traps{0,0x80,0,0,0,0,0,5};
+ Common::Array<uint8> o7Traps{0,0x80,15,5,3,0,0,0};
+ Common::Array<uint8> o8Traps{0,0x80,0,0,0,0,0,3};
+
+ Common::Array<SObj> noObj{};
+ Common::Array<SObj> o5{SObj(kZip, kZip, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o5Traps),
+ SObj(459, 379, kTypeCoin, kRingFrame, kObjNone, noTraps),
+ SObj(446, 327, kTypeWowCharm, kScrollFrame, kObjNone, noTraps)};
+ Common::Array<SObj> o7{SObj(145, 138, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o7Traps)};
+ Common::Array<SObj> o8{SObj(kZip, kZip, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o8Traps)};
+ Common::Array<SObj> o9{SObj(1052, 309, kTypeDead, kDeadGoblinFrame, kObjIsChest + kObjIsOnGround, noTraps),
+ SObj(kZip, kZip, kTypeFireBall, kScrollFrame, kObjUsesFireButton, noTraps),
+ SObj(128, 464, kTypeDunRing, kRingFrame, 0, noTraps),
+ SObj(837, 421, kTypeChest, kChest0Frame, kObjIsChest, noTraps),
+ SObj(kZip, kZip, kTypeDeathMap, kScrollFrame, 0, noTraps),
+ SObj(597, 457, kTypeWater, kVaseFrame, 0, noTraps),
+ SObj(kZip, kZip, kTypeSpores, kSporesFrame, 0, noTraps),
+ SObj(kZip, kZip, kTypeWormFood, kNoFrame, 0, noTraps),
+ SObj(205, 158, kTypeChestKey, kKeyFrame, 0, noTraps)};
+ Common::Array<SObj> oE{SObj(1184, 426, kTypePhant, kAltarFrame, 0, noTraps),
+ SObj(145, 138, kTypeGold, kNoFrame, kObjIsRunning, noTraps),
+ SObj(671, 461, kTypeHay, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps),
+ SObj(780, 508, kTypeBeam, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps)};
+ CArray2D<SObj> objects{o5, o7, o8, o9, noObj, noObj, oE, noObj};
_stories[0]._objects = objects;
- // All of the monsters for level 0
- SMonster m5[2];
- SMonster m9[3];
- SMonster mE[1];
- SMonster mF[1];
- SMonster *monsters[8] = {m5, nullptr, nullptr, m9, nullptr, nullptr, mE, mF};
+ /* All of the monsters
+ * A 'Program' is just an array of pointers to 'Motives'
+ */
+ Common::Array<Motive> progShade{kMotiveRoomCombat, kMotiveShadeFind, kMotiveShadeLoose, kMotiveEngage, kMotiveUpdateGoal, kMotiveFollow, kMotiveShadeHesitate};
+ Common::Array<Motive> progEasy{kMotiveEasyRoomCombat, kMotiveFind8, kMotiveLoose4, kMotiveEngage, kMotiveUpdateGoal, kMotiveFollow};
+ Common::Array<Motive> progUlindor{kMotiveDefensiveCombat, kMotiveEngage, kMotiveUlinTalk, kMotiveGive, kMotiveUseUpMonster};
+ Common::Array<Motive> progGoblin5{kMotiveAliveRoomCombat, kMotiveFindAlways, kMotiveLoose4, kMotiveEngage, kMotiveUpdateGoal, kMotiveFollow};
+ Common::Array<Motive> progPlayer{kMotivePlayerCombat, kMotiveJoystick, kMotivePlayerDoor};
+ Common::Array<Motive> progWill2{kMotiveRoomCombat, kMotivewaittalk2, kMotiveFindAlways, kMotiveGetDisturbed, kMotiveLoose32, kMotiveUpdateGoal, kMotiveIfNot1Skip1, kMotiveFollow, kMotiveEngage};
+
+ Common::Array<SMonster> noMonst{};
+ Common::Array<SMonster> m5{SMonster(448, 344, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow),
+ SMonster(590, 381, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow)};
+ Common::Array<SMonster> m9{SMonster(1106, 258, 3, kMonstPlayer, kMonstA + kMonstIsEngage, progEasy, kGoblin0),
+ SMonster(832, 364, 10, kMonstA, kMonstB + kMonstIsPoss, progUlindor, kUlindor3),
+ SMonster(838, 370, 15, kMonstPlayer, kMonstA + kMonstIsEngage, progGoblin5, kGoblin7)};
+ Common::Array<SMonster> mE{SMonster(1136, 464, 15, kMonstMonster, kMonstPlayer + kMonstIsEngage, progPlayer, kWizard0)};
+ Common::Array<SMonster> mF{SMonster(1182, 116, 5, kMonstPlayer, kMonstA + kMonstIsEngage, progWill2, kGoblin5)};
+ CArray2D<SMonster> monsters{m5, noMonst, noMonst, m9, noMonst, noMonst, mE, mF};
_stories[0]._monsters = monsters;
+
}
} // namespace Immortal
@@ -120,13 +196,6 @@ void ImmortalEngine::loadStoryFiles() {
-
-
-
-
-
-
-
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 3524a3c3270..332e93b990e 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -24,26 +24,29 @@
namespace Immortal {
+// We need a few two-dimentional vectors, and writing them out in full each time is tedious
+template<class T> using CArray2D = Common::Array<Common::Array<T>>;
+
enum DoorDir : bool {
kLeft = false,
kRight = true
};
-enum RoomFlag : uint8 {
+enum RoomFlag : uint8 { // Generic properties available to each room
kRoomFlag0 = 0x1,
kRoomFlag1 = 0x2,
kRoomFlag2 = 0x4,
kRoomFlag3 = 0x8
};
-enum FPattern {
+enum FPattern : uint8 { // This defines which Cyc animation it uses
kFlameNormal,
kFlameCandle,
kFlameOff,
kFlameGusty
};
-enum OPMask : uint8 {
+enum OPMask : uint8 { // These are not actually needed anymore, they were for the original compiler method for making story.gs. Keeping it just in case for now
kOPMaskRoom,
kOPMaskInRoom,
kOPMaskFlame,
@@ -61,27 +64,62 @@ enum ObjFlag : uint8 {
kObjIsChest = 0x08,
kObjIsOnGround = 0x04,
kObjIsF1 = 0x02,
- kObjIsF2 = 0x01
+ kObjIsF2 = 0x01,
+ kObjNone = 0x0
};
enum MonsterFlag : uint8 {
+ kMonstIsNone = 0x00,
kMonstIsTough = 0x10,
kMonstIsDead = 0x20,
kMonstIsPoss = 0x40,
kMonstIsBaby = 0x40,
- kMonstIsEngage = 0x80
+ kMonstIsEngage = 0x80,
+ kMonstPlayer = 0x00,
+ kMonstMonster = 0x01,
+ kMonstAnybody = 0x02,
+ kMonstNobody = 0x03,
+ kMonstA = 0x04,
+ kMonstB = 0x05,
+ kMonstC = 0x06,
+ kMonstD = 0x07
};
enum IsA : uint8 {
kIsAF1 = 0x20,
- kIsAF2 = 0x40
+ kIsAF2 = 0x40,
+ kIsANone = 0x0,
};
-enum Program { // This will likely be moved to a monster ai specific file later
+enum Motive { // This will likely be moved to a monster ai specific file later
+ kMotiveRoomCombat,
+ kMotiveShadeFind,
+ kMotiveShadeLoose,
+ kMotiveEngage,
+ kMotiveUpdateGoal,
+ kMotiveFollow,
+ kMotiveShadeHesitate,
+ kMotiveEasyRoomCombat,
+ kMotiveFind8,
+ kMotiveLoose4,
+ kMotiveDefensiveCombat,
+ kMotiveUlinTalk,
+ kMotiveGive,
+ kMotiveUseUpMonster,
+ kMotiveAliveRoomCombat,
+ kMotiveFindAlways,
+ kMotivePlayerCombat,
+ kMotiveJoystick,
+ kMotivePlayerDoor,
+ kMotivewaittalk2,
+ kMotiveGetDisturbed,
+ kMotiveLoose32,
+ kMotiveIfNot1Skip1,
};
enum Str {
- kStrOldGame,
+ kStrNoDesc,
+ kStrOldGame = 0,
kStrEnterCertificate,
kStrBadCertificate,
kStrCertificate,
@@ -93,6 +131,33 @@ enum Str {
kStrGameOver,
};
+enum SObjType {
+ kTypeTrap,
+ kTypeCoin,
+ kTypeWowCharm,
+ kTypeDead,
+ kTypeFireBall,
+ kTypeDunRing,
+ kTypeChest,
+ kTypeDeathMap,
+ kTypeWater,
+ kTypeSpores,
+ kTypeWormFood,
+ kTypeChestKey,
+ kTypePhant,
+ kTypeGold,
+ kTypeHay,
+ kTypeBeam
+};
+
+enum SObjPickup {
+
+};
+
+enum SObjUse {
+
+};
+
struct Pickup {
//pointer to function
int _param;
@@ -104,25 +169,24 @@ struct Use {
};
struct ObjType {
- Str _str;
- Str _desc;
- int _size;
+ Str _str = kStrNoDesc;
+ Str _desc = kStrNoDesc;
+ int _size = 0;
Pickup _pickup;
Use _use;
Use _run;
};
-
/* Strictly speaking, many of these structs (which were rom data written dynamically
* with compiler macros) combine multiple properties into single bytes (ex. room uses
* bits 0-2 of X to also hold the roomOP, and bits 0-2 of Y to hold flags). However
* for the moment there's no need to replicate this particular bit of space saving.
*/
struct SRoom {
- uint8 _x;
- uint8 _y;
- RoomFlag _flags;
- SRoom() {}
+ uint8 _x = 0;
+ uint8 _y = 0;
+ RoomFlag _flags = kRoomFlag0;
+
SRoom(uint8 x, uint8 y, RoomFlag f) {
_x = x;
_y = y;
@@ -131,100 +195,88 @@ struct SRoom {
};
struct SDoor {
- DoorDir _dir;
- uint8 _x;
- uint8 _y;
- uint8 _fromRoom;
- uint8 _toRoom;
- bool _isLocked;
- SDoor() {}
+DoorDir _dir = kLeft;
+ uint8 _x = 0;
+ uint8 _y = 0;
+ uint8 _fromRoom = 0;
+ uint8 _toRoom = 0;
+ bool _isLocked = false;
+
SDoor(DoorDir d, uint8 x, uint8 y, uint8 f, uint8 t, bool l) {
- _dir = d;
- _x = x;
- _y = y;
- _fromRoom = f;
- _toRoom = t;
- _isLocked = l;
+ _dir = d;
+ _x = x;
+ _y = y;
+ _fromRoom = f;
+ _toRoom = t;
+ _isLocked = l;
}
};
struct SFlame {
- uint8 _x;
- uint8 _y;
- FPattern _pattern;
- SFlame() {}
+ uint8 _x = 0;
+ uint8 _y = 0;
+FPattern _pattern = kFlameOff;
+
SFlame(uint8 x, uint8 y, FPattern p) {
_x = x;
_y = y;
- _pattern = p;
+ _pattern = p;
}
};
-struct UnivAt {
- uint8 _initialUnivX;
- uint8 _initialUnivY;
- uint8 _playerPointX;
- uint8 _playerPointY;
- int *_ladders;
- UnivAt() {}
- UnivAt(uint8 iX, uint8 iY, uint8 pX, uint8 pY, int l[]) {
- _initialUnivX = iX;
- _initialUnivY = iY;
- _playerPointX = pX;
- _playerPointY = pY;
- _ladders = l;
- }
-};
-
struct SObj {
- uint8 _x;
- uint8 _y;
- ObjType _type;
- ObjFlag _flags;
- uint8 _tmp;
-SpriteFrame _frame;
- SObj() {}
- SObj(uint8 x, uint8 y, ObjType t, ObjFlag f, uint8 tmp, SpriteFrame s) {
+ uint8 _x = 0;
+ uint8 _y = 0;
+ SObjType _type = kTypeTrap;
+ uint8 _flags = 0;
+SpriteFrame _frame = kNoFrame;
+Common::Array<uint8> _traps;
+
+ SObj(uint8 x, uint8 y, SObjType t, SpriteFrame s, uint8 f, Common::Array<uint8> traps) {
_x = x;
_y = y;
_type = t;
_flags = f;
- _tmp = tmp;
+ _traps = traps;
_frame = s;
}
};
struct SMonster {
- uint8 _x;
- uint8 _y;
- uint8 _hits;
- uint8 _madAt;
- IsA _isA;
- Program _program;
- SpriteName _sprite;
-MonsterFlag _flags;
- SMonster() {}
- SMonster(uint8 x, uint8 y, uint8 h, uint8 m, IsA i, Program p, SpriteName s, MonsterFlag mf) {
- _x = x;
- _y = y;
+ uint8 _x = 0;
+ uint8 _y = 0;
+ uint8 _hits = 0;
+MonsterFlag _madAt = kMonstIsNone;
+ uint8 _flags = 0;
+ SpriteName _sprite = kCandle;
+Common::Array<Motive> _program;
+
+ SMonster(uint8 x, uint8 y, uint8 h, MonsterFlag m, uint8 f, Common::Array<Motive> p, SpriteName s) {
+ _x = x;
+ _y = y;
_hits = h;
_madAt = m;
- _isA = i;
+ _flags = f;
_program = p;
_sprite = s;
- _flags = mf;
}
};
struct Story {
- int _levelNum;
- int _partNum;
- UnivAt _UnivAt;
- SRoom *_rooms;
- SDoor *_doors;
- SFlame **_flames;
- SObj **_objects;
-SMonster **_monsters;
+ int _level = 0;
+ int _part = 1;
+
+ uint8 _initialUnivX = 0;
+ uint8 _initialUnivY = 0;
+ uint8 _playerPointX = 0;
+ uint8 _playerPointY = 0;
+
+ Common::Array<int> _ladders;
+Common::Array<SRoom> _rooms;
+Common::Array<SDoor> _doors;
+ CArray2D<SFlame> _flames;
+ CArray2D<SObj> _objects;
+ CArray2D<SMonster> _monsters;
};
} // namespace immortal
Commit: bb56b8cdab463dfa5b1df402dea95dd27ad68e80
https://github.com/scummvm/scummvm/commit/bb56b8cdab463dfa5b1df402dea95dd27ad68e80
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add skeleton of Room Object
Changed paths:
A engines/immortal/room.cpp
A engines/immortal/room.h
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/module.mk
engines/immortal/story.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 856616dafae..cfb0ffe64f1 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -49,7 +49,6 @@
#include "immortal/detection.h"
#include "immortal/disk.h"
-#include "immortal/sprite_list.h" // This is an enum of all available sprites
#include "immortal/story.h"
namespace Immortal {
@@ -217,8 +216,8 @@ public:
*/
// Misc constants
- const int kNumLengths = 21;
- const int kNiceTime = 36;
+ const int kNumLengths = 21;
+ const int kNiceTime = 36;
const int kMaxCertificate = 16;
// Max strings = 250
@@ -232,27 +231,27 @@ public:
"Enter certificate:&-=",
"Game Over&&Play again?@"};
// Screen constants
- const int kResH = 320;
- const int kResV = 200;
- const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
- const int kScreenH__ = 128; // ???
- const int kViewPortW = 256;
- const int kViewPortH = 128;
- const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
- const int kScreenLeft = 32;
- const int kScreenTop = 20;
- const int kTextLeft = 8;
- const int kTextTop = 4;
- const int kGaugeX = 0;
- const int kGaugeY = -13; // ???
- const int kScreenBMW = 160; // Literally no idea yet
- const uint16 kChrW = 64;
- const uint16 kChrH = 32;
- const uint16 kChrH2 = kChrH * 2;
- const uint16 kChrH3 = kChrH * 3;
- const int kChrLen = (kChrW / 2) * kChrH;
- const int kChrBMW = kChrW / 2;
- const int kLCutaway = 4;
+ const int kResH = 320;
+ const int kResV = 200;
+ const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
+ const int kScreenH__ = 128; // ???
+ const int kViewPortW = 256;
+ const int kViewPortH = 128;
+ const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
+ const int kScreenLeft = 32;
+ const int kScreenTop = 20;
+ const int kTextLeft = 8;
+ const int kTextTop = 4;
+ const int kGaugeX = 0;
+ const int kGaugeY = -13; // ???
+ const int kScreenBMW = 160; // Literally no idea yet
+ const uint16 kChrW = 64;
+ const uint16 kChrH = 32;
+ const uint16 kChrH2 = kChrH * 2;
+ const uint16 kChrH3 = kChrH * 3;
+ const int kChrLen = (kChrW / 2) * kChrH;
+ const int kChrBMW = kChrW / 2;
+ const int kLCutaway = 4;
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
kChrH2, kChrH, kChrH2, kChrH2, kChr0,
@@ -273,7 +272,7 @@ public:
0, 0, 0, 0, 0, 0};
// Disk offsets
- const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
+ const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
const int kMaxSpriteAbove = 48; // Maximum sprite extents from center
@@ -574,6 +573,7 @@ public:
/*
* [Level.cpp] Functions from level.GS
+ * < All functions implemented (in some capacity)! >
*/
// Init
void levelInitAtStartOfGameOnly();
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 91c3aa43b2b..0a18afd44f6 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -19,10 +19,13 @@
*
*/
+#include "immortal/room.h"
#include "immortal/immortal.h"
namespace Immortal {
+struct Flame;
+
void ImmortalEngine::levelInitAtStartOfGameOnly() {
loadStoryFiles();
_lastLevelLoaded = -1;
@@ -38,7 +41,7 @@ void ImmortalEngine::levelNew(int l) {
clearScreen();
/* commented out in the source for some reason? */
for (int i = 0; i < kMaxRooms; i++) {
- //_rooms[i].delete();
+ delete _rooms[i];
}
levelStory(l);
@@ -64,7 +67,7 @@ void ImmortalEngine::levelStory(int l) {
}
void ImmortalEngine::levelLoadFile(int l) {
- _dRoomNum = 0;
+// _dRoomNum = 0;
/* This was originally a large branching tree that checked the identifier of each entry and
* Processed them all for the story. Once again, this would have been better as an indexed
@@ -74,12 +77,13 @@ void ImmortalEngine::levelLoadFile(int l) {
// Create the rooms and doors, then populate the rooms with their objects and actors
for (int r = 0; r < _stories[l]._rooms.size(); r++) {
- //roomNew(_stories[l]._rooms[i]);
+ _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags);
//doorNew(_stories[l]._doors[i]);
debug("Room %d", r);
for (int f = 0; f < _stories[l]._flames.size(); f++) {
if (_stories[l]._flames[r].size() > 0) {
- //fsetNew(_stories[l]._flames[r][f]);
+ //Flame flame;
+ //_rooms[r]->_flames.push_back(flame);
debugN("F%d", f);
}
}
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 6c460cff60a..ac6c8f3bed0 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -12,10 +12,10 @@ MODULE_OBJS = \
cycle.o \
drawChr.o \
level.o \
- story.o
+ story.o \
+ room.o
# universe.o \
-# room.o \
# object.o \
# door.o \
# flameset.o \
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
new file mode 100644
index 00000000000..dc6f49db2ad
--- /dev/null
+++ b/engines/immortal/room.cpp
@@ -0,0 +1,94 @@
+/* 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 "immortal/room.h"
+
+namespace Immortal {
+
+Room::Room(uint8 x, uint8 y, RoomFlag f) {
+ _xPos = x;
+ _yPos = y;
+ _flags = f;
+}
+
+void Room::addMonster() {
+ //_monsters->push_back(new Monster());
+}
+
+void Room::removeMonster() {
+ //_monsters->pop_back();
+}
+
+void Room::addObject() {
+ //_objects->push_back(new Object());
+}
+
+void Room::removeObject() {
+ //_objects->pop_back();
+}
+
+Common::Array<Monster> Room::getMonsterList() {
+ return _monsters;
+}
+
+Common::Array<Object> Room::getObjectList() {
+ return _objects;
+}
+
+void Room::getXY(uint16 &x, uint16 &y) {
+ x <<= 2;
+ y <<= 2;
+}
+
+void Room::getCell(uint16 &x, uint16 &y) {
+ x >>= 3;
+ y >>= 3;
+}
+
+void Room::setHole() {}
+void Room::drawContents() {}
+
+bool Room::getWideWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id, int spacing) {
+ return true;
+}
+
+bool Room::getWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id) {
+ return true;
+}
+
+} // namespace immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
new file mode 100644
index 00000000000..8c07a7f0161
--- /dev/null
+++ b/engines/immortal/room.h
@@ -0,0 +1,130 @@
+/* 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/>.
+ *
+ */
+
+/* --- What is a Room ---
+ *
+ */
+
+#include "common/file.h"
+#include "common/memstream.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "immortal/story.h"
+
+#ifndef IMMORTAL_ROOM_H
+#define IMMORTAL_ROOM_H
+
+namespace Immortal {
+
+enum Tile : uint8 {
+ kTileFloor,
+ kTileUpper5,
+ kTileUpper3,
+ kTileCeiling,
+ kTileTop1,
+ kTileTop7,
+ kTileWallFace,
+ kTileTopLower13,
+ kTileTopLower75,
+ kTileLower3,
+ kTileLower5,
+ kTileCeilingTile = 2
+};
+
+struct Flame {
+};
+
+// Temp
+struct Object {
+};
+
+// Temp
+struct Monster {
+};
+
+struct Spark {
+};
+
+struct Chest {
+};
+
+struct Bullet {
+};
+
+class Room {
+private:
+
+public:
+ Room(uint8 x, uint8 y, RoomFlag f);
+ ~Room() {}
+
+Common::Array<Flame> _fset;
+Common::Array<Monster> _monsters;
+Common::Array<Object> _objects;
+
+ RoomFlag _flags;
+ uint8 _xPos;
+ uint8 _yPos;
+ uint8 _holeRoom;
+ uint8 _holeCellX;
+ uint8 _holeCellY;
+
+ //void init();
+ //void inRoomNew();
+ //void getTilePair(uint8 x, uint8 y); // Modifies a struct of the tile number, aboveTile number, and the cell coordinates of the tile
+
+ void setHole();
+ void drawContents();
+ bool getTilePair(uint8 x, uint8 y, int id);
+ bool getWideWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id, int spacing);
+ bool getWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id);
+ void addMonster();
+ void addObject();
+ void removeObject();
+ void removeMonster();
+
+Common::Array<Monster> getMonsterList();
+Common::Array<Object> getObjectList();
+
+ void getXY(uint16 &x, uint16 &y);
+ void getCell(uint16 &x, uint16 &y);
+};
+
+
+
+} // namespace immortal
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 332e93b990e..472415a0c35 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -19,6 +19,8 @@
*
*/
+#include "immortal/sprite_list.h" // This is an enum of all available sprites
+
#ifndef IMMORTAL_STORY_H
#define IMMORTAL_STORY_H
Commit: 204dda79b0a3aa26f1d44ac71b173e2a6eb96541
https://github.com/scummvm/scummvm/commit/204dda79b0a3aa26f1d44ac71b173e2a6eb96541
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add static story STR definitions
Changed paths:
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/level.cpp
engines/immortal/logic.cpp
engines/immortal/story.cpp
engines/immortal/story.h
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index 623a5cdc3dc..c75105de413 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -156,6 +156,7 @@ Common::Error ImmortalEngine::run() {
_usingNormal = 0;
_draw = 1;
+ initStoryStatic(); // Init the arrays of static story elements (done at compile time in the source)
loadPalette(); // We need to grab the palette from the disk first
useNormal(); // The first palette will be the default
loadFont(); // Load the font sprite
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index cfb0ffe64f1..eef656df82e 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -154,6 +154,18 @@ enum GameFlags : uint8 {
kSavedAna
};
+struct Spark {
+};
+
+struct GenSprite {
+};
+
+struct Door {
+};
+
+struct Cycle {
+};
+
struct Frame {
uint16 _deltaX;
uint16 _deltaY;
@@ -220,16 +232,6 @@ public:
const int kNiceTime = 36;
const int kMaxCertificate = 16;
- // Max strings = 250
- // This should really be a char array, but inserting frame values will be stupid so it's just a string instead
- const Common::String stringPtrs[250] = {"New game?%", "Enter certificate:&-=", "Invalid certificate.@",
- "End of level!&Here is your certificate:&&=", "&@",
- " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=", // Might need \ for something
- " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
- "#" + Common::String(kGoldBigFrame) + "$0 gold@",
- "Congratulations!&&Play again?@",
- "Enter certificate:&-=",
- "Game Over&&Play again?@"};
// Screen constants
const int kResH = 320;
const int kResV = 200;
@@ -307,7 +309,7 @@ public:
const int kMaxFilesPerLevel = 16;
const int kMaxPartInstances = 4;
const int kLevelToMaze[8] = {0,0,1,1,2,2,2,3};
-
+//cantunlockdoor set badchestdesc
/*
* 'global' members
*/
@@ -371,6 +373,13 @@ public:
DataSprite _font; // The font sprite data is loaded separate from other sprite stuff
Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
+ Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
+ Common::Array<Motive> _motivePtrs;
+ Common::Array<Damage> _damagePtrs;
+ Common::Array<Use> _usePtrs;
+ Common::Array<Pickup> _pickupPtrs;
+ CArray2D<Motive> _programPtrs;
+ Common::Array<ObjType> _objTypePtrs;
// Screen members
byte *_window; // Bitmap of the window around the game
@@ -467,6 +476,7 @@ public:
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
+ void initStoryStatic(); // Sets up all of the global static story elements
//void loadMazeGraphics(); // Creates a universe with a maze
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
@@ -598,7 +608,7 @@ public:
* [Story.cpp] Functions from Story.cpp
*/
// Init
- void loadStoryFiles();
+ void initStoryDynamic();
/*
* [Sprites.cpp] Functions from Sprites.GS and spriteList.GS
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 50f9fa16caf..cb2a03365c1 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -364,6 +364,125 @@ void ImmortalEngine::printChr(char c) {
*
*/
+void ImmortalEngine::initStoryStatic() {
+ Common::Array<Common::String> s{"#" + Common::String(kSwordBigFrame) + "sword@",
+ "You find an Elven sword of&agility. Take it?@",
+ "Search the bones?%",
+ "}The sword permanently endows you with Elven agility and quickness in combat.@",
+ "}You notice something that looks wet and green under the pile. Search further?%",
+ "#" + Common::String(kBagBigFrame) + " dust@"
+ "}You find a bag containing Dust of Complaisance.&@"
+ "}Drop the bait on the ground here?%"
+ "}To use this dust, you throw it in the air. Do that here?%"
+ "_}Don+t bother me, I+m cutting a gem. Yes, you need it. No, you can+t have it. I wouldn+t give it to anyone, least of all you. Go away. ]]]]="
+ "_}Let me help you. Please take this gem. No, really, I insist. Take it and go with my blessings. Good luck. ]]]]="
+ "#" + Common::String(kCarpetBigFrame) + "carpet@",
+ "#" + Common::String(kBombBigFrame) + " bomb@",
+ "A gas bomb that goblins&use to paralyze trolls.&@",
+ "Take it?<>@",
+ "%",
+ " other@",
+ "#" + Common::String(kKeyBigFrame) + " key@",
+ "#" + Common::String(kKeyBigFrame) + " key@",
+ "A key to a chest.&@",
+ "The chest is open. Examine&contents?%",
+ "Put it on?%",
+ "Drop it?%",
+ "It+s unlocked. Open it?%",
+ "It+s locked but you have&the key. Open it?%",
+ "It+s locked and you don+t&have the key.@",
+ "The lock, triggered by a&complicated set of latches,&is unfamiliar to you.@",
+ "#" + Common::String(kGoldBigFrame) + "$0 gold@",
+ "You find $0 gold pieces.&&^#" + Common::String(kPileFrame) + "@",
+ "@",
+ "You can+t plant them on&stone tiles.@",
+ "It+s locked but you are&able to unlock it with&the key.@",
+ "_}The king is not dead, but the poison is taking effect. When he sees you, he attempts to speak:[(Give me water... the fountain... I give you... information... peace...+[Give him water?%",
+ "_}You dont have any water to give him. He mumbles something. Then silence... You find a key on his body.]]]]=",
+ "_}He mumbles something. Then silence... You find a key on his body.]]]]=",
+ "_}I+ll tell you how to... next level... past slime... three jewels... slime... rock becomes... floor... right, left, center of the... [Then silence. His hand opens, releasing a key.]]]]=",
+ "You find a door key.&@",
+ "You find a note.&@",
+ "#" + Common::String(kNoteBigFrame) + "note@",
+ "He+s dead.&Look for possessions?%",
+ "You don+t have it. Check&your inventory.@",
+ "Game Over&&Play again?@",
+ "Congratulations!&&Play again?@",
+ "You find a bag of bait.&@",
+ "#" + Common::String(kBagBigFrame) + " bait@",
+ "You find a stone. @",
+ "#" + Common::String(kStoneBigFrame) + " stone@",
+ "You find a red gem.&@",
+ "#" + Common::String(kGemBigFrame) + " gem@",
+ "You find a scroll with&fireball spells.&@"
+ "#" + Common::String(kScrollBigFrame) + "$ shots@",
+ "You find a map warning&you about pit traps.&@"
+ "#" + Common::String(kMapBigFrame) + " map@",
+ "#" + Common::String(kVaseBigFrame) + " oil@",
+ "You apply the oil but notice&as you walk that the leather&is drying out quickly.@"
+ "}You discover a scroll with a charm spell to use on will o+ the wisps.&@"
+ "#" + Common::String(kScrollBigFrame) + " charm@",
+ "}This charms the will o+ the wisps to follow you. Read the spell again to turn them against your enemies.@"
+ "}It looks like water. Drink it?%",
+ "Drink it?%",
+ "}It works! You are much stronger.]]]=",
+ "}It looks like it has green stuff inside. Open it?%",
+ "Now this will take&effect when you press the&fire button.@",
+ "You find a potion,&Magic Muscle.&@",
+ "#" + Common::String(kVaseBigFrame) + " potion@",
+ "You find a bottle.&@",
+ "#" + Common::String(kVaseBigFrame) + " bottle@",
+ "#" + Common::String(kRingBigFrame) + "Protean@",
+ "You find a Protean Ring.&@",
+ "You find a troll ritual knife,&used to declare a fight to&the death. @",
+ "#" + Common::String(kKnifeBigFrame) + " knife@",
+ "_}It is a fine woman+s garment. Folded inside is a ring with the words,[`To Ana, so harm will never find you. -Your loving father, Dunric.+&@",
+ "You find a small, well&crafted ring. @",
+ "#" + Common::String(kRingBigFrame) + " gift@",
+ "#" + Common::String(kRingBigFrame) + " Ana+s@",
+ "_}She is hurt and upset when she finds you don+t have her ring or won+t give it to her. She scurries back into the hole. The hole is too small for you to follow.&@",
+ "_}`Sir, can you help me,+ the girl pleads. `I was kidnapped and dragged down here. All the man would say is `Mordamir+s orders.+[I escaped using a ring my father gave me, but now I+ve lost it. Did you find it?+%",
+ "_}We have met before, old man. Do you remember? Because you helped me, you may pass. But I warn you, we are at war with the trolls.[Over this ladder, across the spikes, is troll territory. Very dangerous.@",
+ "_}You are an impostor!]]]]=",
+ "_}Old man, do you remember me? I am king of the goblins. You didn+t give me the water. You left me to die after you took the key from me. Now you will pay.]]]]=",
+ "_}You quickly fall into a deep, healing sleep...[Vivid images of a beautiful enchanted city pass by. All the city people are young and glowing. Fountains fill the city, and the splash and sparkle of water is everywhere...[Suddenly the images go black. A face appears... Mordamir!]][He is different from how you remember him. His gentle features are now withered. His kind eyes, now cold and sunken, seem to look through you with a dark, penetrating stare. You wake rejuvenated, but disturbed.]]]]]=",
+ "_}Here, take this ring in return. [I don+t know if it will help, but I heard the unpleasant little dwarf say, (Clockwise, three rings around the triangle.+[Could that be a clue to his exit puzzle? I must go. Goodbye.]]]]=",
+ "#" + Common::String(kSackBigFrame) + " spores@",
+ "You find a sack of bad&smelling spores.&@",
+ "Please insert play disk.@",
+ "New game?%",
+ "Enter certificate:&-=",
+ "Invalid certificate.@",
+ "End of level!&Here is your certificate:&&=",
+ "&@",
+ " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=",
+ " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
+ "_}Greetings, friend! Come, I+ve got something you need. These parts are plagued with slime.[You can+t venture safely without my slime oil for boots, a bargain at only 80 gold pieces.%",
+ "_}All right, 60 gold pieces for my oil. Rub it on your boots and slime won+t touch you. 60, friend.%",
+ "This room doesn+t resemble&any part of the map.@",
+ "This room resembles part&of the map.@"};
+ _strPtrs = s;
+
+ Common::Array<Motive> m{};
+ _motivePtrs = m;
+
+ Common::Array<Damage> d{};
+ _damagePtrs = d;
+
+ Common::Array<Use> u{};
+ _usePtrs = u;
+
+ Common::Array<Pickup> p{};
+ _pickupPtrs = p;
+
+ CArray2D<Motive> pr{};
+ _programPtrs = pr;
+
+ Common::Array<ObjType> o{};
+ _objTypePtrs = o;
+
+}
+
void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p) {
if (_numSprites != kMaxSprites) {
if (x >= (kResH + kMaxSpriteLeft)) {
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 0a18afd44f6..59ff8739c1f 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -27,7 +27,7 @@ namespace Immortal {
struct Flame;
void ImmortalEngine::levelInitAtStartOfGameOnly() {
- loadStoryFiles();
+ initStoryDynamic();
_lastLevelLoaded = -1;
_lastSongLoaded = -1;
}
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index ebeea26369a..6e04edbbb91 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -504,7 +504,7 @@ void ImmortalEngine::calcCheckSum(int l, uint8 checksum[]) {
}
bool ImmortalEngine::getCertificate() {
- textPrint(kStrCertificate);
+ textPrint(kStrCert);
int certLen = 0;
bool entered = false;
int k = 0;
@@ -596,11 +596,11 @@ void ImmortalEngine::printCertificate() {
*/
char toHex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
- textBeginning(kStrCertificate);
+ textBeginning(kStrCert);
for (int i = 0; i < _lastCertLen; i++) {
printChr(toHex[_certificate[i]]);
}
- textEnd(kStrCertificate2);
+ textEnd(kStrCert2);
}
bool ImmortalEngine::isSavedKing() {
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index f35d7b8f3b8..875b3f5857d 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -47,7 +47,7 @@
namespace Immortal {
-void ImmortalEngine::loadStoryFiles() {
+void ImmortalEngine::initStoryDynamic() {
/* There is one major difference between the source logic and this method.
* It doesn't change the game logic, but it does change the logic of storing
* the initial rom data. In the source, because there are no language based
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 472415a0c35..70e8fd48e49 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -67,7 +67,7 @@ enum ObjFlag : uint8 {
kObjIsOnGround = 0x04,
kObjIsF1 = 0x02,
kObjIsF2 = 0x01,
- kObjNone = 0x0
+ kObjNone = 0x0
};
enum MonsterFlag : uint8 {
@@ -121,16 +121,121 @@ enum Motive { // This will likely be moved to a monster ai specific file la
enum Str {
kStrNoDesc,
- kStrOldGame = 0,
+ kStrSword,
+ kStrSwordDesc,
+ kStrBonesText1,
+ kStrBonesText2,
+ kStrBonesText3,
+ kStrComp,
+ kStrCompDesc,
+ kStrOpenBag,
+ kStrThrowComp,
+ kStrSmithText1,
+ kStrSmithText2,
+ kStrCarpet,
+ kStrBomb,
+ kStrBombDesc,
+ kStrPickItUp,
+ kStrYesNo,
+ kStrOther,
+ kStrChestKey,
+ kStrDoorKey,
+ kStrChestKeyDesc,
+ kStrOpenChestDesc,
+ kStrPutItOn,
+ kStrDropItThen,
+ kStrChestDesc,
+ kStrGoodChestDesc,
+ kStrBadChestDesc,
+ kStrComboLock,
+ kStrGold,
+ kStrFindGold,
+ kStrNull,
+ kStrNotHere,
+ kStrUnlockDoor,
+ kStrWeak1,
+ kStrDummyWater,
+ kStrBadWizard,
+ kStrDiesAnyway,
+ kStrDoorKeyDesc,
+ kStrNoteDesc,
+ kStrNote,
+ kStrLootBodyDesc,
+ kStrNotEnough,
+ kStrGameOver,
+ kStrYouWin,
+ kStrWormFoodDesc,
+ kStrWormFood,
+ kStrStoneDesc,
+ kStrStone,
+ kStrGemDesc,
+ kStrGem,
+ kStrFireBallDesc,
+ kStrFireBall,
+ kStrDeathMapDesc,
+ kStrDeathMap,
+ kStrBoots,
+ kStrUseBoots,
+ kStrWowCharmDesc,
+ kStrWowCharm,
+ kStrUseWowCharm,
+ kStrWaterOpen,
+ kStrDrinkIt,
+ kStrItWorks,
+ kStrSBOpen,
+ kStrUsesFire,
+ kStrMuscleDesc,
+ kStrMuscle,
+ kStrSBDesc,
+ kStrSB,
+ kStrFace,
+ kStrFaceDesc,
+ kStrTRNDesc,
+ kStrTRN,
+ kStrInvisDesc,
+ kStrGoodLuckDesc,
+ kStrAnaRing,
+ kStrInvis,
+ kStrGoesAway,
+ kStrGiveHerRing,
+ kStrGive2,
+ kStrMadKingText,
+ kStrMadKing3Text,
+ kStrMadKing2Text,
+ kStrDream1,
+ kStrDream1P2,
+ kStrDream1P3,
+ kStrHowToGetOut,
+ kStrSpore,
+ kStrSporeDesc,
+ kStrRequestPlayDisc,
+ kStrOldGame,
kStrEnterCertificate,
kStrBadCertificate,
- kStrCertificate,
- kStrCertificate2,
+ kStrCert,
+ kStrCert2,
kStrTitle0,
kStrTitle4,
- kStrGold,
- kStrYouWin,
- kStrGameOver,
+ kStrMDesc,
+ kStrM3Desc,
+ kStrMapText1,
+ kStrMapText2,
+
+ // Level 0 str
+
+ // Level 1 str
+
+ // Level 2 str
+
+ // Level 3 str
+
+ // Level 4 str
+
+ // Level 5 str
+
+ // Level 6 str
+
+ // Level 7 str
};
enum SObjType {
@@ -160,6 +265,14 @@ enum SObjUse {
};
+enum SDamage {
+
+};
+
+struct Damage {
+
+};
+
struct Pickup {
//pointer to function
int _param;
Commit: 9410617da4c0c024515d668bbf9f7bf910f00a1e
https://github.com/scummvm/scummvm/commit/9410617da4c0c024515d668bbf9f7bf910f00a1e
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add util.h and util.cpp, move several misc functions to util.cpp
Changed paths:
A engines/immortal/util.cpp
A engines/immortal/util.h
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index eef656df82e..3287f2a0b69 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -22,8 +22,25 @@
#ifndef IMMORTAL_IMMORTAL_H
#define IMMORTAL_IMMORTAL_H
+// Audio is only handled in kernal, therefore it is only needed here
#include "audio/mixer.h"
+// Immortal.h is the engine, so it needs the engine headers
+#include "engines/engine.h"
+#include "engines/savestate.h"
+
+// Theorectically, all graphics should be handled through driver, which is part of kernal, which is in immortal.h
+#include "graphics/screen.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+// Detection is only needed by the main engine
+#include "immortal/detection.h"
+
+// Disk is only used by immortal.cpp
+#include "immortal/disk.h"
+
+// Common is needed by immortal.h, room.h, and monster.h
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/events.h"
@@ -39,52 +56,17 @@
#include "common/util.h"
#include "common/platform.h"
-#include "engines/engine.h"
-#include "engines/savestate.h"
+// There is a lot of bit masking that needs to happen, so this header includes several enums for immortal.h, room.h, and monster.h
+#include "immortal/bitmask.h"
-#include "graphics/screen.h"
-#include "graphics/palette.h"
-#include "graphics/surface.h"
-
-#include "immortal/detection.h"
-#include "immortal/disk.h"
+#include "immortal/util.h"
+// Story is needed by both immortal.h and room.h
#include "immortal/story.h"
namespace Immortal {
-// There is a lot of bit masking that needs to happen, so this enum makes it a little easier to read
-enum BitMask16 : uint16 {
- kMaskLow = 0x00FF,
- kMaskHigh = 0xFF00,
- kMaskLast = 0xF000,
- kMaskFirst = 0x000F,
- kMaskHLow = 0x0F00,
- kMaskLHigh = 0x00F0,
- kMaskNeg = 0x8000,
- kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
-};
-
-enum BitMask8 : uint8 {
- kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
- kMask8High = 0xF0,
- kMask8Low = 0x0F
-};
-
-enum ColourBitMask : uint16 {
- kMaskRed = 0x0F00,
- kMaskGreen = 0x00F0,
- kMaskBlue = 0x000F
-};
-
-enum ChrMask : uint16 {
- kChr0 = 0x0000,
- kChrL = 0x0001,
- kChrR = 0xFFFF,
- kChrLD = 0x0002,
- kChrRD = 0xFFFE
-};
-
+// Needed by kernal for drawing
enum Screen { // These are constants that are used for defining screen related arrays
kMaxSprites = 32, // Number of sprites allowed at once
kViewPortCW = 256 / 64,
@@ -92,16 +74,7 @@ enum Screen { // These are constants that are used for defining screen
kMaxDrawItems = kViewPortCH + 1 + kMaxSprites
};
-enum StoryMaxes {
- kMaxRooms = 16,
- kMaxDoors = 10,
- kMaxFlames = 32,
- kMaxFlamesInRoom = 5,
- kMaxObjects = 42,
- kMaxMonsters = 20,
- kMaxGenSprites = 6
-};
-
+// Needed by kernal for input
enum InputAction {
kActionNothing,
kActionKey,
@@ -119,17 +92,21 @@ enum InputDirection {
kDirectionRight
};
-enum MonsterID {
- kPlayerID
+// Needed by kernal for music
+enum Song {
+ kSongNothing,
+ kSongMaze,
+ kSongCombat,
+ kSongText
};
-enum LevelType {
- kRoomType,
- kMonsterType,
- kObjectType
+// Needed by logic for various things
+enum MonsterID {
+ kPlayerID
};
-enum CertIndex : uint8 {
+// Needed by logic for certificate processing
+enum CertificateIndex : uint8 {
kCertHits,
kCertLevel,
kCertLoGameFlags,
@@ -141,31 +118,40 @@ enum CertIndex : uint8 {
kCertGoldHi
};
-enum Song {
- kSongNothing,
- kSongMaze,
- kSongCombat,
- kSongText
-};
-
+// Needed by logic for various things
enum GameFlags : uint8 {
kSavedNone,
kSavedKing,
kSavedAna
};
+// Needed by level (maybe?)
+enum LevelType {
+ kRoomType,
+ kMonsterType,
+ kObjectType
+};
+
+// Basically the equivalent of the explosion from a projectile in other games I think
struct Spark {
};
-struct GenSprite {
+// Generic sprites can be used anywhere, just sort of misc sprites
+struct GenericSprite {
};
+// Doors are a property of the level, not the room, they defined the connections between rooms
struct Door {
+ uint8 _x = 0;
+ uint8 _y = 0;
+ uint8 _from = 0;
+ uint8 _to = 0;
+ uint8 _busyOnRight = 0;
+ uint8 _on = 0;
};
-struct Cycle {
-};
+// Sprites are handled by driver in Kernal
struct Frame {
uint16 _deltaX;
uint16 _deltaY;
@@ -192,7 +178,7 @@ DataSprite *_dSprite;
struct ImmortalGameDescription;
-// Forward declaration because we will need the Disk class
+// Forward declaration because we will need the Disk and Room classes
class ProDosDisk;
class Room;
@@ -309,7 +295,7 @@ public:
const int kMaxFilesPerLevel = 16;
const int kMaxPartInstances = 4;
const int kLevelToMaze[8] = {0,0,1,1,2,2,2,3};
-//cantunlockdoor set badchestdesc
+
/*
* 'global' members
*/
@@ -317,7 +303,7 @@ public:
// Misc
Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
uint8 _certificate[16]; // The certificate (password) is basically the inventory/equipment array
- uint8 _lastCertLen = 0;
+ uint8 _lastCertLen = 0;
bool _draw = 0; // Whether the screen should draw this frame
int _zero = 0; // No idea what this is yet
bool _gameOverFlag = false;
@@ -353,6 +339,19 @@ public:
int _roomCellX;
int _roomCellY;
Room *_rooms[kMaxRooms]; // Rooms within the level
+ Common::Array<SFlame> _allFlames[kMaxRooms]; // The level needs it's own set of flames so that the flames can be turned on/off permenantly. This is technically more like a hashmap in the source, but it could also be seen as a 2d array, just hashed together in the source
+
+ // Door members
+ uint8 _numDoors = 0;
+ uint8 _doorRoom = 0;
+ uint8 _doorToNextLevel = 0;
+ uint8 _doorCameInFrom = 0;
+ uint8 _ladders = 0;
+ uint8 _numLadders = 0;
+ uint8 _ladderInUse = 0;
+ uint8 _secretLadder = 0;
+ uint8 _secretCount = 0;
+ uint8 _secretDelta = 0;
// Debug members
bool _singleStep; // Flag for _singleStep mode
@@ -374,12 +373,12 @@ public:
Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
- Common::Array<Motive> _motivePtrs;
- Common::Array<Damage> _damagePtrs;
- Common::Array<Use> _usePtrs;
- Common::Array<Pickup> _pickupPtrs;
- CArray2D<Motive> _programPtrs;
- Common::Array<ObjType> _objTypePtrs;
+ Common::Array<Motive> _motivePtrs;
+ Common::Array<Damage> _damagePtrs;
+ Common::Array<Use> _usePtrs;
+ Common::Array<Pickup> _pickupPtrs;
+ CArray2D<Motive> _programPtrs;
+ Common::Array<ObjType> _objTypePtrs;
// Screen members
byte *_window; // Bitmap of the window around the game
@@ -403,6 +402,8 @@ public:
uint16 _myUnivPointY;
int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
+ Cyc _cycles[32];
+GenericSprite _genSprites[6];
// Palette members
int _dontResetColors = 0; // Not sure yet
@@ -556,9 +557,6 @@ public:
*/
// Misc
- void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
- void delay4(int j); // || /4
- void delay8(int j); // || /8
void miscInit();
void setRandomSeed();
void getRandom();
@@ -576,10 +574,6 @@ public:
void buttonPressed();
void firePressed();
- // Screen related
- void inside(int p, int p2, int a);
- void insideRect(int p, int r);
-
/*
* [Level.cpp] Functions from level.GS
@@ -605,8 +599,31 @@ public:
/*
- * [Story.cpp] Functions from Story.cpp
+ * [Cycle.cpp] Functions from Cyc
*/
+
+ // Misc
+ void cycleNew(); // Adds a cycle to the current list
+ int getCycleChr();
+ void cycleFreeAll(); // Delete all cycles
+ void cycleGetFile();
+ void cycleGetNum();
+ void cycleGetIndex();
+ void cycleSetIndex();
+ void cycleGetFrame();
+ void cycleAdvance();
+
+ /* Unneccessary cycle functions
+ void cycleInit();
+ void cycleFree();
+ void cycleGetNumFrames();
+ void cycleGetList();*/
+
+
+ /*
+ * [Story.cpp] Functions related to Story.GS
+ */
+
// Init
void initStoryDynamic();
@@ -636,11 +653,42 @@ public:
/*
- * [Music.cpp] Functions from Music.cpp
+ * [door.cpp] Functions from Door.GS
+ */
+
+ void roomTransfer(int r, int x, int y); // Transfers the player from the current room to a new room at x,y
+ void doorOpenSecret();
+ void doorCloseSecret();
+ //void doorToNextLevel();
+ void doorInit();
+ void doorClrLock();
+ void doorNew();
+ void doorDrawAll();
+ void doorOnDoorMat();
+ //void doorEnter(); // <-- this is actually a method of Player Monster, should probably move it there later
+ int findDoorTop(int x, int y);
+ int findDoor(int x, int y);
+ bool doLockStuff(int d, MonsterID m, int top);
+ bool inDoorTop(int x, int y, MonsterID m);
+ bool inDoor(int x, int y, MonsterID m);
+ int doorDoStep(MonsterID m, int d, int index);
+ int doorSetOn(int d);
+ int doorComeOut(MonsterID m);
+ void doorSetLadders(MonsterID m);
+
+
+ /*
+ * [Music.cpp] Functions from music.GS and sound.GS
*/
// Misc
+
+ /*
+ * [Univ.cpp] Functions from Univ.GS
+ */
+
+
/*
* --- ScummVM general engine Functions ---
*
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index cb2a03365c1..778248aa4b9 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -750,13 +750,13 @@ void ImmortalEngine::pump() {
// Flashes the screen (except the frame thankfully) white, black, white, black, then clears the screen and goes back to normal
useWhite();
g_system->updateScreen();
- delay(2);
+ Immortal::Util::delay(2);
useBlack();
g_system->updateScreen();
- delay(2);
+ Immortal::Util::delay(2);
useWhite();
g_system->updateScreen();
- delay(2);
+ Immortal::Util::delay(2);
useBlack();
g_system->updateScreen();
clearScreen();
@@ -818,7 +818,7 @@ void ImmortalEngine::fade(uint16 pal[], int dir, int delay) {
while ((count >= 0) && (count <= 256)) {
fadePal(pal, count, target);
- delay8(delay);
+ Immortal::Util::delay8(delay);
setColors(target);
// Same as above, it was originally a branch, this does the same thing
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 379e2571c1c..79e77585dd9 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -31,18 +31,6 @@ namespace Immortal {
*
*/
-void ImmortalEngine::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
- g_system->delayMillis(j * 56);
-}
-
-void ImmortalEngine::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
- g_system->delayMillis(j * 14);
-}
-
-void ImmortalEngine::delay8(int j) { // 1/8 jiffies are 7.02ms
- g_system->delayMillis(j * 7);
-}
-
void ImmortalEngine::miscInit() {
// In the source, this is where the seed for the rng is set, but we don't need to do that as we used _randomSource
_lastGauge = 0;
@@ -91,10 +79,6 @@ void ImmortalEngine::firePressed() {}
*
*/
-void ImmortalEngine::inside(int p, int p2, int a) {}
-void ImmortalEngine::insideRect(int p, int r) {}
-
-
} // namespace Immortal
diff --git a/engines/immortal/util.cpp b/engines/immortal/util.cpp
new file mode 100644
index 00000000000..0b6bc866ede
--- /dev/null
+++ b/engines/immortal/util.cpp
@@ -0,0 +1,49 @@
+/* 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 "immortal/util.h"
+
+namespace Immortal {
+
+namespace Util {
+
+void Util::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
+ g_system->delayMillis(j * 56);
+}
+
+void Util::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
+ g_system->delayMillis(j * 14);
+}
+
+void Util::delay8(int j) { // 1/8 jiffies are 7.02ms
+ g_system->delayMillis(j * 7);
+}
+
+bool Util::inside(int x1, int y1, int a, int x2, int y2) {
+ return false;
+}
+bool Util::insideRect(int x, int y, int r) {
+ return false;
+}
+
+}; // namespace Util
+
+}; // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/util.h b/engines/immortal/util.h
new file mode 100644
index 00000000000..ba7ef1df514
--- /dev/null
+++ b/engines/immortal/util.h
@@ -0,0 +1,41 @@
+/* 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 IMMORTAL_UTIL_H
+#define IMMORTAL_UTIL_H
+
+#include "common/system.h"
+
+namespace Immortal {
+
+namespace Util {
+
+void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
+void delay4(int j); // || /4
+void delay8(int j); // || /8
+bool inside(int x1, int y1, int a, int x2, int y2);
+bool insideRect(int x, int y, int r);
+
+}; // namespace Util
+
+}; // namespace Immortal
+
+#endif
\ No newline at end of file
Commit: af28dec2e9d808e8f08a1bd71a0de32e22341de5
https://github.com/scummvm/scummvm/commit/af28dec2e9d808e8f08a1bd71a0de32e22341de5
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add bitmask.h and move bitmask enums from immortal.h to bitmask.h
Changed paths:
A engines/immortal/bitmask.h
diff --git a/engines/immortal/bitmask.h b/engines/immortal/bitmask.h
new file mode 100644
index 00000000000..e1b7f6a4982
--- /dev/null
+++ b/engines/immortal/bitmask.h
@@ -0,0 +1,60 @@
+/* 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 IMMORTAL_BITMASK_H
+#define IMMORTAL_BITMASK_H
+
+namespace Immortal {
+
+enum BitMask16 : uint16 {
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000,
+ kMaskFirst = 0x000F,
+ kMaskHLow = 0x0F00,
+ kMaskLHigh = 0x00F0,
+ kMaskNeg = 0x8000,
+ kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
+};
+
+enum BitMask8 : uint8 {
+ kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMask8High = 0xF0,
+ kMask8Low = 0x0F
+};
+
+enum ColourBitMask : uint16 {
+ kMaskRed = 0x0F00,
+ kMaskGreen = 0x00F0,
+ kMaskBlue = 0x000F
+};
+
+enum ChrMask : uint16 {
+ kChr0 = 0x0000,
+ kChrL = 0x0001,
+ kChrR = 0xFFFF,
+ kChrLD = 0x0002,
+ kChrRD = 0xFFFE
+};
+
+} // namespace immortal
+
+#endif
\ No newline at end of file
Commit: b761800cfb0562ecbb45ef3a778556dd0f76ac1a
https://github.com/scummvm/scummvm/commit/b761800cfb0562ecbb45ef3a778556dd0f76ac1a
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add definitions.h and move enums str, motives, and cyc from story.h to definitions.h
Changed paths:
A engines/immortal/definitions.h
engines/immortal/story.h
diff --git a/engines/immortal/definitions.h b/engines/immortal/definitions.h
new file mode 100644
index 00000000000..73082333927
--- /dev/null
+++ b/engines/immortal/definitions.h
@@ -0,0 +1,200 @@
+/* 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 IMMORTAL_DEFINITIONS_H
+#define IMMORTAL_DEFINITIONS_H
+
+namespace Immortal {
+
+enum Cyc {
+ kCycNone
+};
+
+enum Motive { // This will likely be moved to a monster ai specific file later
+ kMotiveRoomCombat,
+ kMotiveShadeFind,
+ kMotiveShadeLoose,
+ kMotiveEngage,
+ kMotiveUpdateGoal,
+ kMotiveFollow,
+ kMotiveShadeHesitate,
+ kMotiveEasyRoomCombat,
+ kMotiveFind8,
+ kMotiveLoose4,
+ kMotiveDefensiveCombat,
+ kMotiveUlinTalk,
+ kMotiveGive,
+ kMotiveUseUpMonster,
+ kMotiveAliveRoomCombat,
+ kMotiveFindAlways,
+ kMotivePlayerCombat,
+ kMotiveJoystick,
+ kMotivePlayerDoor,
+ kMotivewaittalk2,
+ kMotiveGetDisturbed,
+ kMotiveLoose32,
+ kMotiveIfNot1Skip1,
+};
+
+enum Str {
+ kStrNoDesc,
+ kStrSword,
+ kStrSwordDesc,
+ kStrBonesText1,
+ kStrBonesText2,
+ kStrBonesText3,
+ kStrComp,
+ kStrCompDesc,
+ kStrOpenBag,
+ kStrThrowComp,
+ kStrSmithText1,
+ kStrSmithText2,
+ kStrCarpet,
+ kStrBomb,
+ kStrBombDesc,
+ kStrPickItUp,
+ kStrYesNo,
+ kStrOther,
+ kStrChestKey,
+ kStrDoorKey,
+ kStrChestKeyDesc,
+ kStrOpenChestDesc,
+ kStrPutItOn,
+ kStrDropItThen,
+ kStrChestDesc,
+ kStrGoodChestDesc,
+ kStrBadChestDesc,
+ kStrComboLock,
+ kStrGold,
+ kStrFindGold,
+ kStrNull,
+ kStrNotHere,
+ kStrUnlockDoor,
+ kStrWeak1,
+ kStrDummyWater,
+ kStrBadWizard,
+ kStrDiesAnyway,
+ kStrDoorKeyDesc,
+ kStrNoteDesc,
+ kStrNote,
+ kStrLootBodyDesc,
+ kStrNotEnough,
+ kStrGameOver,
+ kStrYouWin,
+ kStrWormFoodDesc,
+ kStrWormFood,
+ kStrStoneDesc,
+ kStrStone,
+ kStrGemDesc,
+ kStrGem,
+ kStrFireBallDesc,
+ kStrFireBall,
+ kStrDeathMapDesc,
+ kStrDeathMap,
+ kStrBoots,
+ kStrUseBoots,
+ kStrWowCharmDesc,
+ kStrWowCharm,
+ kStrUseWowCharm,
+ kStrWaterOpen,
+ kStrDrinkIt,
+ kStrItWorks,
+ kStrSBOpen,
+ kStrUsesFire,
+ kStrMuscleDesc,
+ kStrMuscle,
+ kStrSBDesc,
+ kStrSB,
+ kStrFace,
+ kStrFaceDesc,
+ kStrTRNDesc,
+ kStrTRN,
+ kStrInvisDesc,
+ kStrGoodLuckDesc,
+ kStrAnaRing,
+ kStrInvis,
+ kStrGoesAway,
+ kStrGiveHerRing,
+ kStrGive2,
+ kStrMadKingText,
+ kStrMadKing3Text,
+ kStrMadKing2Text,
+ kStrDream1,
+ kStrDream1P2,
+ kStrDream1P3,
+ kStrHowToGetOut,
+ kStrSpore,
+ kStrSporeDesc,
+ kStrRequestPlayDisc,
+ kStrOldGame,
+ kStrEnterCertificate,
+ kStrBadCertificate,
+ kStrCert,
+ kStrCert2,
+ kStrTitle0,
+ kStrTitle4,
+ kStrMDesc,
+ kStrM3Desc,
+ kStrMapText1,
+ kStrMapText2,
+
+ // Level 0 str
+
+ // Level 1 str
+
+ // Level 2 str
+
+ // Level 3 str
+
+ // Level 4 str
+
+ // Level 5 str
+
+ // Level 6 str
+
+ // Level 7 str
+
+ kCantUnlockDoor = kStrBadChestDesc
+};
+
+enum SObjType {
+ kTypeTrap,
+ kTypeCoin,
+ kTypeWowCharm,
+ kTypeDead,
+ kTypeFireBall,
+ kTypeDunRing,
+ kTypeChest,
+ kTypeDeathMap,
+ kTypeWater,
+ kTypeSpores,
+ kTypeWormFood,
+ kTypeChestKey,
+ kTypePhant,
+ kTypeGold,
+ kTypeHay,
+ kTypeBeam
+};
+
+
+} // namespace immortal
+
+#endif
\ No newline at end of file
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 70e8fd48e49..8787bf3ecdf 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -19,47 +19,45 @@
*
*/
-#include "immortal/sprite_list.h" // This is an enum of all available sprites
+// Definitions are the enum for the set of global definitions in Story.GS
+#include "immortal/definitions.h"
+
+// Sprite List is a list of all sprite definitions (could be included in definitions.h, but sprite_list.gs was a separate source file and is sprite specific)
+#include "immortal/sprite_list.h"
#ifndef IMMORTAL_STORY_H
#define IMMORTAL_STORY_H
namespace Immortal {
+struct Frame;
+struct DataSprite;
+struct Sprite;
+
// We need a few two-dimentional vectors, and writing them out in full each time is tedious
template<class T> using CArray2D = Common::Array<Common::Array<T>>;
-enum DoorDir : bool {
- kLeft = false,
- kRight = true
+// These maximum numbers aren't really needed, because most of these are vectors and have .size()
+enum StoryMaxes {
+ kMaxRooms = 16,
+ kMaxDoors = 10,
+ kMaxFlames = 32,
+ kMaxFlamesInRoom = 5,
+ kMaxObjects = 42,
+ kMaxMonsters = 20,
+ kMaxGenSprites = 6,
+ kMaxCycles = 32
};
+// These are flags that are relevant to their specific story data structures
enum RoomFlag : uint8 { // Generic properties available to each room
- kRoomFlag0 = 0x1,
- kRoomFlag1 = 0x2,
- kRoomFlag2 = 0x4,
- kRoomFlag3 = 0x8
-};
-
-enum FPattern : uint8 { // This defines which Cyc animation it uses
- kFlameNormal,
- kFlameCandle,
- kFlameOff,
- kFlameGusty
+ kRoomFlag0 = 0x01,
+ kRoomFlag1 = 0x02,
+ kRoomFlag2 = 0x04,
+ kRoomFlag3 = 0x08
};
-enum OPMask : uint8 { // These are not actually needed anymore, they were for the original compiler method for making story.gs. Keeping it just in case for now
- kOPMaskRoom,
- kOPMaskInRoom,
- kOPMaskFlame,
- kOPMaskUnivAt,
- kOPMaskMonster,
- kOPMaskDoor,
- kOPMaskObject,
- kOPMaskRecord
-};
-
-enum ObjFlag : uint8 {
+enum ObjFlag : uint8 { // Properties of the object essentially
kObjUsesFireButton = 0x40,
kObjIsInvisible = 0x20,
kObjIsRunning = 0x10,
@@ -67,10 +65,16 @@ enum ObjFlag : uint8 {
kObjIsOnGround = 0x04,
kObjIsF1 = 0x02,
kObjIsF2 = 0x01,
- kObjNone = 0x0
+ kObjNone = 0x0
+};
+
+enum IsA : uint8 { // To be completely honest, I'm not really sure what this is. It seems to be more object flags, but they act a little strangely
+ kIsAF1 = 0x20,
+ kIsAF2 = 0x40,
+ kIsANone = 0x0,
};
-enum MonsterFlag : uint8 {
+enum MonsterFlag : uint8 { // Mostly properties of the AI for a given monster, *including the player*
kMonstIsNone = 0x00,
kMonstIsTough = 0x10,
kMonstIsDead = 0x20,
@@ -87,195 +91,44 @@ enum MonsterFlag : uint8 {
kMonstD = 0x07
};
-enum IsA : uint8 {
- kIsAF1 = 0x20,
- kIsAF2 = 0x40,
- kIsANone = 0x0,
-};
-
-enum Motive { // This will likely be moved to a monster ai specific file later
- kMotiveRoomCombat,
- kMotiveShadeFind,
- kMotiveShadeLoose,
- kMotiveEngage,
- kMotiveUpdateGoal,
- kMotiveFollow,
- kMotiveShadeHesitate,
- kMotiveEasyRoomCombat,
- kMotiveFind8,
- kMotiveLoose4,
- kMotiveDefensiveCombat,
- kMotiveUlinTalk,
- kMotiveGive,
- kMotiveUseUpMonster,
- kMotiveAliveRoomCombat,
- kMotiveFindAlways,
- kMotivePlayerCombat,
- kMotiveJoystick,
- kMotivePlayerDoor,
- kMotivewaittalk2,
- kMotiveGetDisturbed,
- kMotiveLoose32,
- kMotiveIfNot1Skip1,
+// Flame pattern is used by the story data, in-room data, *and* the level based total flame data. So it needs to be in story.h to be used by immortal.h and room.h
+enum FPattern : uint8 { // This defines which Cyc animation it uses
+ kFlameNormal,
+ kFlameCandle,
+ kFlameOff,
+ kFlameGusty
};
-enum Str {
- kStrNoDesc,
- kStrSword,
- kStrSwordDesc,
- kStrBonesText1,
- kStrBonesText2,
- kStrBonesText3,
- kStrComp,
- kStrCompDesc,
- kStrOpenBag,
- kStrThrowComp,
- kStrSmithText1,
- kStrSmithText2,
- kStrCarpet,
- kStrBomb,
- kStrBombDesc,
- kStrPickItUp,
- kStrYesNo,
- kStrOther,
- kStrChestKey,
- kStrDoorKey,
- kStrChestKeyDesc,
- kStrOpenChestDesc,
- kStrPutItOn,
- kStrDropItThen,
- kStrChestDesc,
- kStrGoodChestDesc,
- kStrBadChestDesc,
- kStrComboLock,
- kStrGold,
- kStrFindGold,
- kStrNull,
- kStrNotHere,
- kStrUnlockDoor,
- kStrWeak1,
- kStrDummyWater,
- kStrBadWizard,
- kStrDiesAnyway,
- kStrDoorKeyDesc,
- kStrNoteDesc,
- kStrNote,
- kStrLootBodyDesc,
- kStrNotEnough,
- kStrGameOver,
- kStrYouWin,
- kStrWormFoodDesc,
- kStrWormFood,
- kStrStoneDesc,
- kStrStone,
- kStrGemDesc,
- kStrGem,
- kStrFireBallDesc,
- kStrFireBall,
- kStrDeathMapDesc,
- kStrDeathMap,
- kStrBoots,
- kStrUseBoots,
- kStrWowCharmDesc,
- kStrWowCharm,
- kStrUseWowCharm,
- kStrWaterOpen,
- kStrDrinkIt,
- kStrItWorks,
- kStrSBOpen,
- kStrUsesFire,
- kStrMuscleDesc,
- kStrMuscle,
- kStrSBDesc,
- kStrSB,
- kStrFace,
- kStrFaceDesc,
- kStrTRNDesc,
- kStrTRN,
- kStrInvisDesc,
- kStrGoodLuckDesc,
- kStrAnaRing,
- kStrInvis,
- kStrGoesAway,
- kStrGiveHerRing,
- kStrGive2,
- kStrMadKingText,
- kStrMadKing3Text,
- kStrMadKing2Text,
- kStrDream1,
- kStrDream1P2,
- kStrDream1P3,
- kStrHowToGetOut,
- kStrSpore,
- kStrSporeDesc,
- kStrRequestPlayDisc,
- kStrOldGame,
- kStrEnterCertificate,
- kStrBadCertificate,
- kStrCert,
- kStrCert2,
- kStrTitle0,
- kStrTitle4,
- kStrMDesc,
- kStrM3Desc,
- kStrMapText1,
- kStrMapText2,
-
- // Level 0 str
-
- // Level 1 str
-
- // Level 2 str
-
- // Level 3 str
-
- // Level 4 str
-
- // Level 5 str
-
- // Level 6 str
-
- // Level 7 str
+enum DoorDir : bool {
+ kLeft = false,
+ kRight = true
};
-enum SObjType {
- kTypeTrap,
- kTypeCoin,
- kTypeWowCharm,
- kTypeDead,
- kTypeFireBall,
- kTypeDunRing,
- kTypeChest,
- kTypeDeathMap,
- kTypeWater,
- kTypeSpores,
- kTypeWormFood,
- kTypeChestKey,
- kTypePhant,
- kTypeGold,
- kTypeHay,
- kTypeBeam
+// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
+struct Cycle {
+DataSprite *_dSprite;
+ int _numCycles;
+ int *_frames;
};
+// Object Pickup defines how an object can be picked up by the player, with different functions
enum SObjPickup {
-
};
-enum SObjUse {
-
+struct Pickup {
+ //pointer to function
+ int _param;
};
+// Iirc damage is used by object types as well as enemy types
enum SDamage {
-
};
struct Damage {
-
};
-struct Pickup {
- //pointer to function
- int _param;
+// Use is self explanitory, it defines the function and parameters for using an object
+enum SObjUse {
};
struct Use {
@@ -301,7 +154,7 @@ struct SRoom {
uint8 _x = 0;
uint8 _y = 0;
RoomFlag _flags = kRoomFlag0;
-
+ SRoom() {}
SRoom(uint8 x, uint8 y, RoomFlag f) {
_x = x;
_y = y;
@@ -316,7 +169,7 @@ DoorDir _dir = kLeft;
uint8 _fromRoom = 0;
uint8 _toRoom = 0;
bool _isLocked = false;
-
+ SDoor() {}
SDoor(DoorDir d, uint8 x, uint8 y, uint8 f, uint8 t, bool l) {
_dir = d;
_x = x;
@@ -330,12 +183,12 @@ DoorDir _dir = kLeft;
struct SFlame {
uint8 _x = 0;
uint8 _y = 0;
-FPattern _pattern = kFlameOff;
-
+FPattern _p = kFlameOff;
+ SFlame() {}
SFlame(uint8 x, uint8 y, FPattern p) {
- _x = x;
- _y = y;
- _pattern = p;
+ _x = x;
+ _y = y;
+ _p = p;
}
};
@@ -346,7 +199,7 @@ struct SObj {
uint8 _flags = 0;
SpriteFrame _frame = kNoFrame;
Common::Array<uint8> _traps;
-
+ SObj() {}
SObj(uint8 x, uint8 y, SObjType t, SpriteFrame s, uint8 f, Common::Array<uint8> traps) {
_x = x;
_y = y;
@@ -365,15 +218,15 @@ MonsterFlag _madAt = kMonstIsNone;
uint8 _flags = 0;
SpriteName _sprite = kCandle;
Common::Array<Motive> _program;
-
+ SMonster() {}
SMonster(uint8 x, uint8 y, uint8 h, MonsterFlag m, uint8 f, Common::Array<Motive> p, SpriteName s) {
_x = x;
_y = y;
- _hits = h;
- _madAt = m;
- _flags = f;
- _program = p;
- _sprite = s;
+ _hits = h;
+ _madAt = m;
+ _flags = f;
+ _program = p;
+ _sprite = s;
}
};
Commit: 63a6fe2851adf3c3b948a073cdb29db1e65849ed
https://github.com/scummvm/scummvm/commit/63a6fe2851adf3c3b948a073cdb29db1e65849ed
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Update room skeleton, add flameSet, Univ, Door, and Bullet
Changed paths:
A engines/immortal/bullet.cpp
A engines/immortal/door.cpp
A engines/immortal/flameSet.cpp
A engines/immortal/univ.cpp
engines/immortal/cycle.cpp
engines/immortal/level.cpp
engines/immortal/module.mk
engines/immortal/room.cpp
engines/immortal/room.h
diff --git a/engines/immortal/bullet.cpp b/engines/immortal/bullet.cpp
new file mode 100644
index 00000000000..e8770ec881e
--- /dev/null
+++ b/engines/immortal/bullet.cpp
@@ -0,0 +1,32 @@
+/* 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/>.
+ *
+ */
+
+/* [Alternate Name: Projectile Proccessing]
+ * --- What is a Bullet ---
+ */
+
+#include "immortal/room.h"
+
+namespace Immortal {
+
+
+
+} // namespace immortal
\ No newline at end of file
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index e0a07d5cbde..eeac6188208 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -23,23 +23,20 @@
* --- What is a Cycle ---
*/
-//#include "immortal/room.h"
+#include "immortal/immortal.h"
namespace Immortal {
-// Most of these functions can probably be removed eventually
-/*
-void Room::cycleNew() {}
- int Room::getCycleChr() {
+void ImmortalEngine::cycleNew() {}
+ int ImmortalEngine::getCycleChr() {
return 0;
}
-void Room::cycleFreeAll() {}
-void Room::cycleGetFile() {}
-void Room::cycleGetNum() {}
-void Room::cycleGetIndex() {}
-void Room::cycleSetIndex() {}
-void Room::cycleGetFrame() {}
-void Room::cycleAdvance() {}
-*/
+void ImmortalEngine::cycleFreeAll() {}
+void ImmortalEngine::cycleGetFile() {}
+void ImmortalEngine::cycleGetNum() {}
+void ImmortalEngine::cycleGetIndex() {}
+void ImmortalEngine::cycleSetIndex() {}
+void ImmortalEngine::cycleGetFrame() {}
+void ImmortalEngine::cycleAdvance() {}
} // namespace Immortal
diff --git a/engines/immortal/door.cpp b/engines/immortal/door.cpp
new file mode 100644
index 00000000000..1e93da60e60
--- /dev/null
+++ b/engines/immortal/door.cpp
@@ -0,0 +1,86 @@
+/* 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/>.
+ *
+ */
+
+/* [Alternate Name: Door Processing]
+ * --- What is a Door ---
+ */
+
+#include "immortal/immortal.h"
+
+namespace Immortal {
+
+enum DoorMask {
+ kDoorXMask = 0x1f,
+ kDoorYMask = 0x1f,
+ kDoorFullMask = 0x40,
+ kDoorOnMask = 0x60
+};
+
+enum DoorIs {
+ kDoorIsRight = 0x80,
+ kDoorIsBusy = 0x40,
+ kDoorIsLocked = 0x20
+};
+
+enum DoorSide {
+ kDoorTopPriority = 64,
+ kDoorPriority = 85 - kDoorTopPriority,
+ kDoorLeftTop = 24, // To left of this enters door
+ kDoorRightTop = 8, // To right of this enters door
+ kDoorLeftBottom = 10,
+ kDoorRightBottom = 22,
+ kDoorTopBottom = 20
+};
+
+void ImmortalEngine::doorOpenSecret() {}
+void ImmortalEngine::doorCloseSecret() {}
+void ImmortalEngine::doorInit() {}
+void ImmortalEngine::doorClrLock() {}
+void ImmortalEngine::doorNew() {}
+void ImmortalEngine::doorDrawAll() {}
+void ImmortalEngine::doorOnDoorMat() {}
+ int ImmortalEngine::findDoorTop(int x, int y) {
+ return 0;
+ }
+ int ImmortalEngine::findDoor(int x, int y) {
+ return 0;
+ }
+bool ImmortalEngine::doLockStuff(int d, MonsterID m, int top) {
+ return true;
+}
+bool ImmortalEngine::inDoorTop(int x, int y, MonsterID m) {
+ return true;
+}
+bool ImmortalEngine::inDoor(int x, int y, MonsterID m) {
+ return true;
+}
+ int ImmortalEngine::doorDoStep(MonsterID m, int d, int index) {
+ return 0;
+ }
+ int ImmortalEngine::doorSetOn(int d) {
+ return 0;
+ }
+ int ImmortalEngine::doorComeOut(MonsterID m) {
+ return 0;
+ }
+void ImmortalEngine::doorSetLadders(MonsterID m) {}
+
+} // namespace immortal
\ No newline at end of file
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
new file mode 100644
index 00000000000..70169845ce5
--- /dev/null
+++ b/engines/immortal/flameSet.cpp
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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/>.
+ *
+ */
+
+/* [Alternate Name: Torch Processing]
+ * --- What is a FlameSet ---
+ * A FlameSet is a list of torches in a given room.
+ * The level has X amount of flames total, and each
+ * room has N amount of torches, making up a FlameSet.
+ * There are 3 instances of torches in the game.
+ * First is in the story record, second is in the level torches,
+ * and lastly is the in-room torch. This is done so that
+ * the in-room torch can be lit up by a fireball and then
+ * stay permanantly lit for the next time the player enters the room.
+ */
+
+#include "immortal/room.h"
+
+namespace Immortal {
+
+void Room::flameSetRoom(Common::Array<SFlame> allFlames) {
+ for (int i = 0; i < allFlames.size(); i++) {
+ Flame f;
+ f._p = allFlames[i]._p;
+ f._x = allFlames[i]._x;
+ f._y = allFlames[i]._y;
+ f._c = flameGetCyc(0 | _candleTmp);
+ _candleTmp = 1;
+ _fset.push_back(f);
+ }
+}
+
+void Room::flameDrawAll() {
+
+}
+
+bool Room::roomLighted() {
+ // Very simple, just checks every torch and if any of them are lit, we say the room is lit
+ for (int i = 0; i < _fset.size(); i++) {
+ if (_fset[i]._p != kFlameOff) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Room::lightTorch(int x, int y) {
+ /* Checks every torch to see if it is:
+ * pattern == off, and inside the point x,y
+ * which is the fireball position. This is a
+ * little bit clever, because it saves cycles
+ * over checking x,y first, since you need to
+ * check both x,y and flame pattern. Neato.
+ */
+
+ for (int i = 0; i < _fset.size(); i++) {
+ if (_fset[i]._p == kFlameOff) {
+ if (Immortal::Util::inside(x, y, kLightTorchX, _fset[i]._x + 16, _fset[i]._y + 8)) {
+ _fset[i]._p = kFlameNormal;
+
+ }
+ }
+ }
+}
+
+Cyc Room::flameGetCyc(int first) {
+ return kCycNone;
+}
+
+} // namespace immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 59ff8739c1f..35139a967ea 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -39,10 +39,6 @@ void ImmortalEngine::levelInit() {
void ImmortalEngine::levelNew(int l) {
stopMusic();
clearScreen();
- /* commented out in the source for some reason? */
- for (int i = 0; i < kMaxRooms; i++) {
- delete _rooms[i];
- }
levelStory(l);
if (kLevelToMaze[l] != _lastLevelLoaded) {
@@ -67,7 +63,7 @@ void ImmortalEngine::levelStory(int l) {
}
void ImmortalEngine::levelLoadFile(int l) {
-// _dRoomNum = 0;
+ // _dRoomNum = 0;
/* This was originally a large branching tree that checked the identifier of each entry and
* Processed them all for the story. Once again, this would have been better as an indexed
@@ -76,34 +72,42 @@ void ImmortalEngine::levelLoadFile(int l) {
*/
// Create the rooms and doors, then populate the rooms with their objects and actors
+ debug("loading level file...");
for (int r = 0; r < _stories[l]._rooms.size(); r++) {
_rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags);
- //doorNew(_stories[l]._doors[i]);
+ //doorNew(_stories[l]._doors[r]);
debug("Room %d", r);
- for (int f = 0; f < _stories[l]._flames.size(); f++) {
- if (_stories[l]._flames[r].size() > 0) {
- //Flame flame;
- //_rooms[r]->_flames.push_back(flame);
+
+ Common::Array<SFlame> allFlames(_stories[l]._flames[r].size());
+ if (_stories[l]._flames[r].size() > 0) {
+ for (int f = 0; f < _stories[l]._flames[r].size(); f++) {
+ SFlame sf;
+ sf._p = _stories[l]._flames[r][f]._p;
+ sf._x = _stories[l]._flames[r][f]._x;
+ sf._y = _stories[l]._flames[r][f]._y;
+ allFlames[f] = sf;
debugN("F%d", f);
}
}
+ _allFlames[r] = allFlames;
debug("");
- for (int o = 0; o < _stories[l]._objects.size(); o++) {
- if (_stories[l]._objects[r].size() > 0) {
+ if (_stories[l]._objects[r].size() > 0) {
+ for (int o = 0; o < _stories[l]._objects[r].size(); o++) {
//objNew(_stories[l]._objects[r][o]);
debugN("O%d", o);
}
}
debug("");
- for (int m = 0; m < _stories[l]._monsters.size(); m++) {
- if (_stories[l]._monsters[r].size() > 0) {
+ if (_stories[l]._monsters[r].size() > 0) {
+ for (int m = 0; m < _stories[l]._monsters[r].size(); m++) {
//monstNew(_stories[l]._monsters[r][m]);
debugN("M%d", m);
}
}
debug("");
+
}
// Set up the _initial variables for the engine scope
@@ -131,6 +135,7 @@ void ImmortalEngine::levelDrawAll() {
void ImmortalEngine::levelShowRoom(int r, int bX, int bY) {
_currentRoom = r;
+ _rooms[r]->flameSetRoom(_allFlames[r]);
//univSetRoom(r, bX, bY);
//fset, spark, bullet, and door get set to the current room
//roomGetCell(r, bX, bY);
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index ac6c8f3bed0..16713fcb935 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/immortal
MODULE_OBJS = \
metaengine.o \
+ util.o \
disk.o \
immortal.o \
kernal.o \
@@ -13,13 +14,13 @@ MODULE_OBJS = \
drawChr.o \
level.o \
story.o \
- room.o
+ room.o \
+ flameSet.o \
+ univ.o \
+ door.o \
+ bullet.o
-# universe.o \
# object.o \
-# door.o \
-# flameset.o \
-# bullet.o \
# monster.o \
# motives.o
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
index dc6f49db2ad..320624f1695 100644
--- a/engines/immortal/room.cpp
+++ b/engines/immortal/room.cpp
@@ -27,6 +27,7 @@ Room::Room(uint8 x, uint8 y, RoomFlag f) {
_xPos = x;
_yPos = y;
_flags = f;
+ _candleTmp = 0;
}
void Room::addMonster() {
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 8c07a7f0161..7b7fb846382 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -23,10 +23,28 @@
*
*/
-#include "common/file.h"
-#include "common/memstream.h"
+// Common is needed by immortal.h, room.h, and monster.h
#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/scummsys.h"
+#include "common/system.h"
#include "common/error.h"
+#include "common/fs.h"
+#include "common/file.h"
+#include "common/memstream.h"
+#include "common/hash-str.h"
+#include "common/random.h"
+#include "common/serializer.h"
+#include "common/util.h"
+#include "common/platform.h"
+
+// There is a lot of bit masking that needs to happen, so this header includes several enums for immortal.h, room.h, and monster.h
+#include "immortal/bitmask.h"
+
+#include "immortal/util.h"
+
+// Story is needed by both immortal.h and room.h
#include "immortal/story.h"
#ifndef IMMORTAL_ROOM_H
@@ -49,8 +67,19 @@ enum Tile : uint8 {
kTileCeilingTile = 2
};
-struct Flame {
-};
+/* Quick note:
+ * This looks entirely redundant and silly, I agree. However
+ * this is because the source does more or less the same thing.
+ * At compile time, it creates and stores in memory what are the
+ * equivalent of structs (or maybe tuples), and then at run time
+ * when creating a room, it makes room specific versions that can
+ * be changed. So essentially it creates two RAM structs and then
+ * treats the first as ROM. As such, that's what I'm doing here.
+ * The 'Story' structs are ROM, the 'Room' structs are RAM. There
+ * are also slight differences, like how the room Flame has a reference
+ * to the Cyc it is using. Although again the Story ones are ram
+ * and could do this too.
+ */
// Temp
struct Object {
@@ -60,7 +89,11 @@ struct Object {
struct Monster {
};
-struct Spark {
+struct Flame {
+FPattern _p;
+ uint8 _x;
+ uint8 _y;
+ Cyc _c;
};
struct Chest {
@@ -76,16 +109,35 @@ public:
Room(uint8 x, uint8 y, RoomFlag f);
~Room() {}
+ /*
+ * --- Data ---
+ *
+ */
+
+ // Constants
+ const uint8 kLightTorchX = 10;
+
Common::Array<Flame> _fset;
Common::Array<Monster> _monsters;
Common::Array<Object> _objects;
- RoomFlag _flags;
- uint8 _xPos;
- uint8 _yPos;
- uint8 _holeRoom;
- uint8 _holeCellX;
- uint8 _holeCellY;
+ RoomFlag _flags;
+ uint8 _xPos;
+ uint8 _yPos;
+ uint8 _holeRoom;
+ uint8 _holeCellX;
+ uint8 _holeCellY;
+ uint8 _candleTmp; // Special case for candle in maze 0
+
+
+ /*
+ * --- Methods ---
+ *
+ */
+
+ /*
+ * [room.cpp] Functions from Room.GS
+ */
//void init();
//void inRoomNew();
@@ -100,12 +152,35 @@ Common::Array<Object> _objects;
void addObject();
void removeObject();
void removeMonster();
-
-Common::Array<Monster> getMonsterList();
-Common::Array<Object> getObjectList();
-
+ Common::Array<Monster> getMonsterList();
+ Common::Array<Object> getObjectList();
void getXY(uint16 &x, uint16 &y);
void getCell(uint16 &x, uint16 &y);
+
+
+ /*
+ * [flameSet.cpp] Functions from flameSet.GS
+ */
+
+ void flameSetRoom(Common::Array<SFlame>);
+ void flameDrawAll();
+ bool roomLighted();
+ void lightTorch(int x, int y);
+ Cyc flameGetCyc(int first);
+ //void flameFreeAll();
+ //void flameSetRoom();
+
+ /*
+ * [bullet.cpp] Functions from Bullet.GS
+ */
+
+
+
+ /*
+ * [object.cpp] Functions from Object.GS
+ */
+
+
};
diff --git a/engines/immortal/univ.cpp b/engines/immortal/univ.cpp
new file mode 100644
index 00000000000..7349cdbff8a
--- /dev/null
+++ b/engines/immortal/univ.cpp
@@ -0,0 +1,27 @@
+/* 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 "immortal/immortal.h"
+
+namespace Immortal {
+
+
+} // namespace immortal
\ No newline at end of file
Commit: fe6ef5d0d681b7032cc5a2af94adaeb6a2db193e
https://github.com/scummvm/scummvm/commit/fe6ef5d0d681b7032cc5a2af94adaeb6a2db193e
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Spaces -> Tabs
Changed paths:
engines/immortal/bitmask.h
engines/immortal/definitions.h
engines/immortal/door.cpp
engines/immortal/flameSet.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/bitmask.h b/engines/immortal/bitmask.h
index e1b7f6a4982..d9b5ba1509a 100644
--- a/engines/immortal/bitmask.h
+++ b/engines/immortal/bitmask.h
@@ -25,34 +25,34 @@
namespace Immortal {
enum BitMask16 : uint16 {
- kMaskLow = 0x00FF,
- kMaskHigh = 0xFF00,
- kMaskLast = 0xF000,
- kMaskFirst = 0x000F,
- kMaskHLow = 0x0F00,
- kMaskLHigh = 0x00F0,
- kMaskNeg = 0x8000,
- kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000,
+ kMaskFirst = 0x000F,
+ kMaskHLow = 0x0F00,
+ kMaskLHigh = 0x00F0,
+ kMaskNeg = 0x8000,
+ kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
};
enum BitMask8 : uint8 {
- kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
- kMask8High = 0xF0,
- kMask8Low = 0x0F
+ kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMask8High = 0xF0,
+ kMask8Low = 0x0F
};
enum ColourBitMask : uint16 {
- kMaskRed = 0x0F00,
- kMaskGreen = 0x00F0,
- kMaskBlue = 0x000F
+ kMaskRed = 0x0F00,
+ kMaskGreen = 0x00F0,
+ kMaskBlue = 0x000F
};
enum ChrMask : uint16 {
- kChr0 = 0x0000,
- kChrL = 0x0001,
- kChrR = 0xFFFF,
- kChrLD = 0x0002,
- kChrRD = 0xFFFE
+ kChr0 = 0x0000,
+ kChrL = 0x0001,
+ kChrR = 0xFFFF,
+ kChrLD = 0x0002,
+ kChrRD = 0xFFFE
};
} // namespace immortal
diff --git a/engines/immortal/definitions.h b/engines/immortal/definitions.h
index 73082333927..1991d790c36 100644
--- a/engines/immortal/definitions.h
+++ b/engines/immortal/definitions.h
@@ -25,173 +25,173 @@
namespace Immortal {
enum Cyc {
- kCycNone
+ kCycNone
};
enum Motive { // This will likely be moved to a monster ai specific file later
- kMotiveRoomCombat,
- kMotiveShadeFind,
- kMotiveShadeLoose,
- kMotiveEngage,
- kMotiveUpdateGoal,
- kMotiveFollow,
- kMotiveShadeHesitate,
- kMotiveEasyRoomCombat,
- kMotiveFind8,
- kMotiveLoose4,
- kMotiveDefensiveCombat,
- kMotiveUlinTalk,
- kMotiveGive,
- kMotiveUseUpMonster,
- kMotiveAliveRoomCombat,
- kMotiveFindAlways,
- kMotivePlayerCombat,
- kMotiveJoystick,
- kMotivePlayerDoor,
- kMotivewaittalk2,
- kMotiveGetDisturbed,
- kMotiveLoose32,
- kMotiveIfNot1Skip1,
+ kMotiveRoomCombat,
+ kMotiveShadeFind,
+ kMotiveShadeLoose,
+ kMotiveEngage,
+ kMotiveUpdateGoal,
+ kMotiveFollow,
+ kMotiveShadeHesitate,
+ kMotiveEasyRoomCombat,
+ kMotiveFind8,
+ kMotiveLoose4,
+ kMotiveDefensiveCombat,
+ kMotiveUlinTalk,
+ kMotiveGive,
+ kMotiveUseUpMonster,
+ kMotiveAliveRoomCombat,
+ kMotiveFindAlways,
+ kMotivePlayerCombat,
+ kMotiveJoystick,
+ kMotivePlayerDoor,
+ kMotivewaittalk2,
+ kMotiveGetDisturbed,
+ kMotiveLoose32,
+ kMotiveIfNot1Skip1,
};
enum Str {
- kStrNoDesc,
- kStrSword,
- kStrSwordDesc,
- kStrBonesText1,
- kStrBonesText2,
- kStrBonesText3,
- kStrComp,
- kStrCompDesc,
- kStrOpenBag,
- kStrThrowComp,
- kStrSmithText1,
- kStrSmithText2,
- kStrCarpet,
- kStrBomb,
- kStrBombDesc,
- kStrPickItUp,
- kStrYesNo,
- kStrOther,
- kStrChestKey,
- kStrDoorKey,
- kStrChestKeyDesc,
- kStrOpenChestDesc,
- kStrPutItOn,
- kStrDropItThen,
- kStrChestDesc,
- kStrGoodChestDesc,
- kStrBadChestDesc,
- kStrComboLock,
- kStrGold,
- kStrFindGold,
- kStrNull,
- kStrNotHere,
- kStrUnlockDoor,
- kStrWeak1,
- kStrDummyWater,
- kStrBadWizard,
- kStrDiesAnyway,
- kStrDoorKeyDesc,
- kStrNoteDesc,
- kStrNote,
- kStrLootBodyDesc,
- kStrNotEnough,
- kStrGameOver,
- kStrYouWin,
- kStrWormFoodDesc,
- kStrWormFood,
- kStrStoneDesc,
- kStrStone,
- kStrGemDesc,
- kStrGem,
- kStrFireBallDesc,
- kStrFireBall,
- kStrDeathMapDesc,
- kStrDeathMap,
- kStrBoots,
- kStrUseBoots,
- kStrWowCharmDesc,
- kStrWowCharm,
- kStrUseWowCharm,
- kStrWaterOpen,
- kStrDrinkIt,
- kStrItWorks,
- kStrSBOpen,
- kStrUsesFire,
- kStrMuscleDesc,
- kStrMuscle,
- kStrSBDesc,
- kStrSB,
- kStrFace,
- kStrFaceDesc,
- kStrTRNDesc,
- kStrTRN,
- kStrInvisDesc,
- kStrGoodLuckDesc,
- kStrAnaRing,
- kStrInvis,
- kStrGoesAway,
- kStrGiveHerRing,
- kStrGive2,
- kStrMadKingText,
- kStrMadKing3Text,
- kStrMadKing2Text,
- kStrDream1,
- kStrDream1P2,
- kStrDream1P3,
- kStrHowToGetOut,
- kStrSpore,
- kStrSporeDesc,
- kStrRequestPlayDisc,
- kStrOldGame,
- kStrEnterCertificate,
- kStrBadCertificate,
- kStrCert,
- kStrCert2,
- kStrTitle0,
- kStrTitle4,
- kStrMDesc,
- kStrM3Desc,
- kStrMapText1,
- kStrMapText2,
-
- // Level 0 str
-
- // Level 1 str
-
- // Level 2 str
-
- // Level 3 str
-
- // Level 4 str
-
- // Level 5 str
-
- // Level 6 str
-
- // Level 7 str
-
- kCantUnlockDoor = kStrBadChestDesc
+ kStrNoDesc,
+ kStrSword,
+ kStrSwordDesc,
+ kStrBonesText1,
+ kStrBonesText2,
+ kStrBonesText3,
+ kStrComp,
+ kStrCompDesc,
+ kStrOpenBag,
+ kStrThrowComp,
+ kStrSmithText1,
+ kStrSmithText2,
+ kStrCarpet,
+ kStrBomb,
+ kStrBombDesc,
+ kStrPickItUp,
+ kStrYesNo,
+ kStrOther,
+ kStrChestKey,
+ kStrDoorKey,
+ kStrChestKeyDesc,
+ kStrOpenChestDesc,
+ kStrPutItOn,
+ kStrDropItThen,
+ kStrChestDesc,
+ kStrGoodChestDesc,
+ kStrBadChestDesc,
+ kStrComboLock,
+ kStrGold,
+ kStrFindGold,
+ kStrNull,
+ kStrNotHere,
+ kStrUnlockDoor,
+ kStrWeak1,
+ kStrDummyWater,
+ kStrBadWizard,
+ kStrDiesAnyway,
+ kStrDoorKeyDesc,
+ kStrNoteDesc,
+ kStrNote,
+ kStrLootBodyDesc,
+ kStrNotEnough,
+ kStrGameOver,
+ kStrYouWin,
+ kStrWormFoodDesc,
+ kStrWormFood,
+ kStrStoneDesc,
+ kStrStone,
+ kStrGemDesc,
+ kStrGem,
+ kStrFireBallDesc,
+ kStrFireBall,
+ kStrDeathMapDesc,
+ kStrDeathMap,
+ kStrBoots,
+ kStrUseBoots,
+ kStrWowCharmDesc,
+ kStrWowCharm,
+ kStrUseWowCharm,
+ kStrWaterOpen,
+ kStrDrinkIt,
+ kStrItWorks,
+ kStrSBOpen,
+ kStrUsesFire,
+ kStrMuscleDesc,
+ kStrMuscle,
+ kStrSBDesc,
+ kStrSB,
+ kStrFace,
+ kStrFaceDesc,
+ kStrTRNDesc,
+ kStrTRN,
+ kStrInvisDesc,
+ kStrGoodLuckDesc,
+ kStrAnaRing,
+ kStrInvis,
+ kStrGoesAway,
+ kStrGiveHerRing,
+ kStrGive2,
+ kStrMadKingText,
+ kStrMadKing3Text,
+ kStrMadKing2Text,
+ kStrDream1,
+ kStrDream1P2,
+ kStrDream1P3,
+ kStrHowToGetOut,
+ kStrSpore,
+ kStrSporeDesc,
+ kStrRequestPlayDisc,
+ kStrOldGame,
+ kStrEnterCertificate,
+ kStrBadCertificate,
+ kStrCert,
+ kStrCert2,
+ kStrTitle0,
+ kStrTitle4,
+ kStrMDesc,
+ kStrM3Desc,
+ kStrMapText1,
+ kStrMapText2,
+
+ // Level 0 str
+
+ // Level 1 str
+
+ // Level 2 str
+
+ // Level 3 str
+
+ // Level 4 str
+
+ // Level 5 str
+
+ // Level 6 str
+
+ // Level 7 str
+
+ kCantUnlockDoor = kStrBadChestDesc
};
enum SObjType {
- kTypeTrap,
- kTypeCoin,
- kTypeWowCharm,
- kTypeDead,
- kTypeFireBall,
- kTypeDunRing,
- kTypeChest,
- kTypeDeathMap,
- kTypeWater,
- kTypeSpores,
- kTypeWormFood,
- kTypeChestKey,
- kTypePhant,
- kTypeGold,
- kTypeHay,
- kTypeBeam
+ kTypeTrap,
+ kTypeCoin,
+ kTypeWowCharm,
+ kTypeDead,
+ kTypeFireBall,
+ kTypeDunRing,
+ kTypeChest,
+ kTypeDeathMap,
+ kTypeWater,
+ kTypeSpores,
+ kTypeWormFood,
+ kTypeChestKey,
+ kTypePhant,
+ kTypeGold,
+ kTypeHay,
+ kTypeBeam
};
diff --git a/engines/immortal/door.cpp b/engines/immortal/door.cpp
index 1e93da60e60..b31a7d5fd4f 100644
--- a/engines/immortal/door.cpp
+++ b/engines/immortal/door.cpp
@@ -28,26 +28,26 @@
namespace Immortal {
enum DoorMask {
- kDoorXMask = 0x1f,
- kDoorYMask = 0x1f,
- kDoorFullMask = 0x40,
- kDoorOnMask = 0x60
+ kDoorXMask = 0x1f,
+ kDoorYMask = 0x1f,
+ kDoorFullMask = 0x40,
+ kDoorOnMask = 0x60
};
enum DoorIs {
- kDoorIsRight = 0x80,
- kDoorIsBusy = 0x40,
- kDoorIsLocked = 0x20
+ kDoorIsRight = 0x80,
+ kDoorIsBusy = 0x40,
+ kDoorIsLocked = 0x20
};
enum DoorSide {
- kDoorTopPriority = 64,
- kDoorPriority = 85 - kDoorTopPriority,
- kDoorLeftTop = 24, // To left of this enters door
- kDoorRightTop = 8, // To right of this enters door
- kDoorLeftBottom = 10,
- kDoorRightBottom = 22,
- kDoorTopBottom = 20
+ kDoorTopPriority = 64,
+ kDoorPriority = 85 - kDoorTopPriority,
+ kDoorLeftTop = 24, // To left of this enters door
+ kDoorRightTop = 8, // To right of this enters door
+ kDoorLeftBottom = 10,
+ kDoorRightBottom = 22,
+ kDoorTopBottom = 20
};
void ImmortalEngine::doorOpenSecret() {}
@@ -58,28 +58,28 @@ void ImmortalEngine::doorNew() {}
void ImmortalEngine::doorDrawAll() {}
void ImmortalEngine::doorOnDoorMat() {}
int ImmortalEngine::findDoorTop(int x, int y) {
- return 0;
+ return 0;
}
int ImmortalEngine::findDoor(int x, int y) {
- return 0;
+ return 0;
}
bool ImmortalEngine::doLockStuff(int d, MonsterID m, int top) {
- return true;
+ return true;
}
bool ImmortalEngine::inDoorTop(int x, int y, MonsterID m) {
- return true;
+ return true;
}
bool ImmortalEngine::inDoor(int x, int y, MonsterID m) {
- return true;
+ return true;
}
int ImmortalEngine::doorDoStep(MonsterID m, int d, int index) {
- return 0;
+ return 0;
}
int ImmortalEngine::doorSetOn(int d) {
- return 0;
+ return 0;
}
int ImmortalEngine::doorComeOut(MonsterID m) {
- return 0;
+ return 0;
}
void ImmortalEngine::doorSetLadders(MonsterID m) {}
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index 70169845ce5..fe335010cf4 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -36,15 +36,15 @@
namespace Immortal {
void Room::flameSetRoom(Common::Array<SFlame> allFlames) {
- for (int i = 0; i < allFlames.size(); i++) {
- Flame f;
- f._p = allFlames[i]._p;
- f._x = allFlames[i]._x;
- f._y = allFlames[i]._y;
- f._c = flameGetCyc(0 | _candleTmp);
- _candleTmp = 1;
- _fset.push_back(f);
- }
+ for (int i = 0; i < allFlames.size(); i++) {
+ Flame f;
+ f._p = allFlames[i]._p;
+ f._x = allFlames[i]._x;
+ f._y = allFlames[i]._y;
+ f._c = flameGetCyc(0 | _candleTmp);
+ _candleTmp = 1;
+ _fset.push_back(f);
+ }
}
void Room::flameDrawAll() {
@@ -52,36 +52,36 @@ void Room::flameDrawAll() {
}
bool Room::roomLighted() {
- // Very simple, just checks every torch and if any of them are lit, we say the room is lit
- for (int i = 0; i < _fset.size(); i++) {
- if (_fset[i]._p != kFlameOff) {
- return true;
- }
- }
- return false;
+ // Very simple, just checks every torch and if any of them are lit, we say the room is lit
+ for (int i = 0; i < _fset.size(); i++) {
+ if (_fset[i]._p != kFlameOff) {
+ return true;
+ }
+ }
+ return false;
}
void Room::lightTorch(int x, int y) {
- /* Checks every torch to see if it is:
- * pattern == off, and inside the point x,y
- * which is the fireball position. This is a
- * little bit clever, because it saves cycles
- * over checking x,y first, since you need to
- * check both x,y and flame pattern. Neato.
- */
-
- for (int i = 0; i < _fset.size(); i++) {
- if (_fset[i]._p == kFlameOff) {
- if (Immortal::Util::inside(x, y, kLightTorchX, _fset[i]._x + 16, _fset[i]._y + 8)) {
- _fset[i]._p = kFlameNormal;
-
- }
- }
- }
+ /* Checks every torch to see if it is:
+ * pattern == off, and inside the point x,y
+ * which is the fireball position. This is a
+ * little bit clever, because it saves cycles
+ * over checking x,y first, since you need to
+ * check both x,y and flame pattern. Neato.
+ */
+
+ for (int i = 0; i < _fset.size(); i++) {
+ if (_fset[i]._p == kFlameOff) {
+ if (Immortal::Util::inside(x, y, kLightTorchX, _fset[i]._x + 16, _fset[i]._y + 8)) {
+ _fset[i]._p = kFlameNormal;
+
+ }
+ }
+ }
}
Cyc Room::flameGetCyc(int first) {
- return kCycNone;
+ return kCycNone;
}
} // namespace immortal
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 79e77585dd9..2eb393c2265 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -32,8 +32,8 @@ namespace Immortal {
*/
void ImmortalEngine::miscInit() {
- // In the source, this is where the seed for the rng is set, but we don't need to do that as we used _randomSource
- _lastGauge = 0;
+ // In the source, this is where the seed for the rng is set, but we don't need to do that as we used _randomSource
+ _lastGauge = 0;
}
void ImmortalEngine::setRandomSeed() {}
Commit: 22a3535e34d4789d8e19edbda7ed7c805f94fcc8
https://github.com/scummvm/scummvm/commit/22a3535e34d4789d8e19edbda7ed7c805f94fcc8
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: levelLoadFile initializes level doors
Changed paths:
engines/immortal/door.cpp
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/story.cpp
engines/immortal/story.h
diff --git a/engines/immortal/door.cpp b/engines/immortal/door.cpp
index b31a7d5fd4f..a97820658bc 100644
--- a/engines/immortal/door.cpp
+++ b/engines/immortal/door.cpp
@@ -28,7 +28,7 @@
namespace Immortal {
enum DoorMask {
- kDoorXMask = 0x1f,
+ kDoorXMask = 0x1f, // Only relevant for extracting the data from the compressed bytes in the story record
kDoorYMask = 0x1f,
kDoorFullMask = 0x40,
kDoorOnMask = 0x60
@@ -54,7 +54,17 @@ void ImmortalEngine::doorOpenSecret() {}
void ImmortalEngine::doorCloseSecret() {}
void ImmortalEngine::doorInit() {}
void ImmortalEngine::doorClrLock() {}
-void ImmortalEngine::doorNew() {}
+void ImmortalEngine::doorNew(SDoor door) {
+ Door d;
+ d._x = door._x;
+ d._y = door._y;
+ d._fromRoom = door._fromRoom;
+ d._toRoom = door._toRoom;
+ d._busyOnRight = door._dir | door._x;
+ d._on = door._y & kDoorYMask;
+ _doors.push_back(d);
+}
+
void ImmortalEngine::doorDrawAll() {}
void ImmortalEngine::doorOnDoorMat() {}
int ImmortalEngine::findDoorTop(int x, int y) {
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 3287f2a0b69..927ad250d1e 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -144,13 +144,12 @@ struct GenericSprite {
struct Door {
uint8 _x = 0;
uint8 _y = 0;
- uint8 _from = 0;
- uint8 _to = 0;
+ uint8 _fromRoom = 0;
+ uint8 _toRoom = 0;
uint8 _busyOnRight = 0;
uint8 _on = 0;
};
-
// Sprites are handled by driver in Kernal
struct Frame {
uint16 _deltaX;
@@ -333,8 +332,8 @@ public:
int _initialBX;
int _initialBY;
int _dRoomNum;
- int _initialRoom;
- int _currentRoom;
+ int _initialRoom = 0;
+ int _currentRoom = 0;
int _lastType;
int _roomCellX;
int _roomCellY;
@@ -342,6 +341,7 @@ public:
Common::Array<SFlame> _allFlames[kMaxRooms]; // The level needs it's own set of flames so that the flames can be turned on/off permenantly. This is technically more like a hashmap in the source, but it could also be seen as a 2d array, just hashed together in the source
// Door members
+ Common::Array<Door> _doors;
uint8 _numDoors = 0;
uint8 _doorRoom = 0;
uint8 _doorToNextLevel = 0;
@@ -662,7 +662,7 @@ GenericSprite _genSprites[6];
//void doorToNextLevel();
void doorInit();
void doorClrLock();
- void doorNew();
+ void doorNew(SDoor door);
void doorDrawAll();
void doorOnDoorMat();
//void doorEnter(); // <-- this is actually a method of Player Monster, should probably move it there later
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 35139a967ea..b125d6ed6b3 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -63,8 +63,6 @@ void ImmortalEngine::levelStory(int l) {
}
void ImmortalEngine::levelLoadFile(int l) {
- // _dRoomNum = 0;
-
/* This was originally a large branching tree that checked the identifier of each entry and
* Processed them all for the story. Once again, this would have been better as an indexed
* JSR instead of a set of comparisons and branches. Regardless, we instead use the information
@@ -73,9 +71,14 @@ void ImmortalEngine::levelLoadFile(int l) {
// Create the rooms and doors, then populate the rooms with their objects and actors
debug("loading level file...");
+
+ for (int d = 0; d < _stories[l]._doors.size(); d++) {
+ doorNew(_stories[l]._doors[d]);
+ debug("door %d", d);
+ }
+
for (int r = 0; r < _stories[l]._rooms.size(); r++) {
_rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags);
- //doorNew(_stories[l]._doors[r]);
debug("Room %d", r);
Common::Array<SFlame> allFlames(_stories[l]._flames[r].size());
@@ -130,7 +133,7 @@ void ImmortalEngine::levelDrawAll() {
_count++;
//univAutoCenter();
clearSprites();
- //rooms[_currentRoom].drawContents();
+ _rooms[_currentRoom]->drawContents();
}
void ImmortalEngine::levelShowRoom(int r, int bX, int bY) {
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index 875b3f5857d..e7c1424e45a 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -102,10 +102,10 @@ void ImmortalEngine::initStoryDynamic() {
/* All of the doors
*/
- Common::Array<SDoor> doors{SDoor(kLeft, 704, 224, 0, 2, false), SDoor(kRight, 576, 352, 4, 0, true),
- SDoor(kRight, 704,96, 2, 1, false), SDoor(kRight, 960,128, 7, 2, false),
- SDoor(kRight, 1088,160, 3, 7, false), SDoor(kRight, 1088,320, 6, 3, false),
- SDoor(kRight, 896,416, 4, 3, false)};
+ Common::Array<SDoor> doors{SDoor(0, 704, 224, 0, 2, false), SDoor(1, 576, 352, 4, 0, true),
+ SDoor(1, 704, 96, 2, 1, false), SDoor(1, 960, 128, 7, 2, false),
+ SDoor(1, 1088,160, 3, 7, false), SDoor(1, 1088,320, 6, 3, false),
+ SDoor(1, 896, 416, 4, 3, false)};
_stories[0]._doors = doors;
/* All of the flames
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 8787bf3ecdf..bcb20a001c5 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -99,11 +99,6 @@ enum FPattern : uint8 { // This defines which Cyc animation it uses
kFlameGusty
};
-enum DoorDir : bool {
- kLeft = false,
- kRight = true
-};
-
// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
struct Cycle {
DataSprite *_dSprite;
@@ -163,14 +158,14 @@ struct SRoom {
};
struct SDoor {
-DoorDir _dir = kLeft;
- uint8 _x = 0;
- uint8 _y = 0;
+ uint8 _dir = 0;
+ uint8 _x = 0;
+ uint8 _y = 0;
uint8 _fromRoom = 0;
- uint8 _toRoom = 0;
+ uint8 _toRoom = 0;
bool _isLocked = false;
SDoor() {}
- SDoor(DoorDir d, uint8 x, uint8 y, uint8 f, uint8 t, bool l) {
+ SDoor(uint8 d, uint8 x, uint8 y, uint8 f, uint8 t, bool l) {
_dir = d;
_x = x;
_y = y;
Commit: 3d696d0e01618c609b9308e6dd8f9a489b6cb797
https://github.com/scummvm/scummvm/commit/3d696d0e01618c609b9308e6dd8f9a489b6cb797
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Frame -> Image, and Image/DataSprite/Cycle moved to sprite_list.h
Changed paths:
engines/immortal/immortal.h
engines/immortal/sprite_list.h
engines/immortal/sprites.cpp
engines/immortal/story.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 927ad250d1e..a063b7a67b7 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -151,23 +151,8 @@ struct Door {
};
// Sprites are handled by driver in Kernal
-struct Frame {
- uint16 _deltaX;
- uint16 _deltaY;
- uint16 _rectX;
- uint16 _rectY;
- byte *_bitmap;
-};
-
-struct DataSprite {
- uint16 _cenX; // These are the base center positions
- uint16 _cenY;
- uint16 _numFrames;
-Common::Array<Frame> _frames;
-};
-
struct Sprite {
- int _frame; // Index of _dSprite._frames[]
+ int _image; // Index of _dSprite._frames[]
uint16 _X;
uint16 _Y;
uint16 _on; // 1 = active
@@ -369,9 +354,9 @@ public:
// Asset members
int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
- DataSprite _font; // The font sprite data is loaded separate from other sprite stuff
- Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
+ Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
+ Cycle _cycles[kMaxCycles];
Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
Common::Array<Motive> _motivePtrs;
Common::Array<Damage> _damagePtrs;
@@ -402,7 +387,6 @@ public:
uint16 _myUnivPointY;
int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
- Cyc _cycles[32];
GenericSprite _genSprites[6];
// Palette members
@@ -482,7 +466,7 @@ GenericSprite _genSprites[6];
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
- void addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p);
+ void addSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p);
// Input
void userIO(); // Get input
@@ -603,15 +587,16 @@ GenericSprite _genSprites[6];
*/
// Misc
- void cycleNew(); // Adds a cycle to the current list
- int getCycleChr();
+ CycID cycleNew(CycID id); // Adds a cycle to the current list
void cycleFreeAll(); // Delete all cycles
- void cycleGetFile();
- void cycleGetNum();
- void cycleGetIndex();
- void cycleSetIndex();
- void cycleGetFrame();
- void cycleAdvance();
+ void cycleFree(CycID id);
+DataSprite *cycleGetDataSprite(CycID id); // This takes the place of getFile + getNum
+ int cycleGetIndex(CycID id);
+ void cycleSetIndex(CycID id, int f);
+ int cycleGetFrame(CycID id);
+ int cycleGetNumFrames(CycID id);
+ bool cycleAdvance(CycID id);
+ Cycle *getCycList(CycID id);
/* Unneccessary cycle functions
void cycleInit();
@@ -635,7 +620,7 @@ GenericSprite _genSprites[6];
void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
// Main
- void superSprite(int s, uint16 x, uint16 y, Frame f, int bmw, byte *dst, int sT, int sB);
+ void superSprite(int s, uint16 x, uint16 y, Image img, int bmw, byte *dst, int sT, int sB);
/*
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index a0829331250..345f31d5e3d 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -24,6 +24,29 @@
namespace Immortal {
+struct Image {
+ uint16 _deltaX;
+ uint16 _deltaY;
+ uint16 _rectX;
+ uint16 _rectY;
+ byte *_bitmap;
+};
+
+struct DataSprite {
+ uint16 _cenX; // These are the base center positions
+ uint16 _cenY;
+ uint16 _numImages;
+Common::Array<Image> _images;
+};
+
+// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
+struct Cycle {
+DataSprite *_dSprite;
+ bool _repeat;
+ int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
+ int *_frames;
+};
+
enum SpriteFrame {
// Null
kNoFrame,
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index ae93b2f4258..83bec3c409e 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -43,37 +43,37 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
f->seek(index);
index = f->readUint16LE();
- uint16 numFrames = f->readUint16LE();
+ uint16 numImages = f->readUint16LE();
- d->_numFrames = numFrames;
- debug("Number of Frames: %d", numFrames);
+ d->_numImages = numImages;
+ //debug("Number of Frames: %d", numFrames);
// Only here for dragon, but just in case, it's a high number so it should catch others
- if (numFrames >= 0x0200) {
- debug("** Crazy large value, this isn't a frame number **");
+ if (numImages >= 0x0200) {
+ //debug("** Crazy large value, this isn't a frame number **");
return;
}
- Common::Array<Frame> frames;
+ Common::Array<Image> images;
- for (int i = 0; i < numFrames; i++) {
- Frame newFrame;
+ for (int i = 0; i < numImages; i++) {
+ Image newImage;
f->seek(index + (i*2));
int ptrFrame = f->readUint16LE();
f->seek(ptrFrame);
- newFrame._deltaX = f->readUint16LE() << 1; // the ASL might not be required, depending on how I translate the sprite drawing
- newFrame._deltaY = f->readUint16LE();
- newFrame._rectX = f->readUint16LE();
- newFrame._rectY = f->readUint16LE();
- frames.push_back(newFrame);
+ newImage._deltaX = f->readUint16LE() << 1; // the ASL might not be required, depending on how I translate the sprite drawing
+ newImage._deltaY = f->readUint16LE();
+ newImage._rectX = f->readUint16LE();
+ newImage._rectY = f->readUint16LE();
+ images.push_back(newImage);
// This is probably where we will get the bitmap when I know how to get it
}
- d->_frames = frames;
+ d->_images = images;
}
-void ImmortalEngine::superSprite(int s, uint16 x, uint16 y, Frame f, int bmw, byte *dst, int sT, int sB) {}
+void ImmortalEngine::superSprite(int s, uint16 x, uint16 y, Image img, int bmw, byte *dst, int sT, int sB) {}
} // namespace Immortal
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index bcb20a001c5..aa86734c7e9 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -30,10 +30,6 @@
namespace Immortal {
-struct Frame;
-struct DataSprite;
-struct Sprite;
-
// We need a few two-dimentional vectors, and writing them out in full each time is tedious
template<class T> using CArray2D = Common::Array<Common::Array<T>>;
@@ -99,13 +95,6 @@ enum FPattern : uint8 { // This defines which Cyc animation it uses
kFlameGusty
};
-// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
-struct Cycle {
-DataSprite *_dSprite;
- int _numCycles;
- int *_frames;
-};
-
// Object Pickup defines how an object can be picked up by the player, with different functions
enum SObjPickup {
};
Commit: 435727b52112d72a7626af4280c9377307ce7df1
https://github.com/scummvm/scummvm/commit/435727b52112d72a7626af4280c9377307ce7df1
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Util::X -> Immortal::Util::X
Changed paths:
engines/immortal/util.cpp
diff --git a/engines/immortal/util.cpp b/engines/immortal/util.cpp
index 0b6bc866ede..0bc671a6fbd 100644
--- a/engines/immortal/util.cpp
+++ b/engines/immortal/util.cpp
@@ -25,23 +25,23 @@ namespace Immortal {
namespace Util {
-void Util::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
- g_system->delayMillis(j * 56);
+void Immortal::Util::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
+ g_system->delayMillis(j * 56);
}
-void Util::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
- g_system->delayMillis(j * 14);
+void Immortal::Util::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
+ g_system->delayMillis(j * 14);
}
-void Util::delay8(int j) { // 1/8 jiffies are 7.02ms
- g_system->delayMillis(j * 7);
+void Immortal::Util::delay8(int j) { // 1/8 jiffies are 7.02ms
+ g_system->delayMillis(j * 7);
}
-bool Util::inside(int x1, int y1, int a, int x2, int y2) {
- return false;
+bool Immortal::Util::inside(int x1, int y1, int a, int x2, int y2) {
+ return false;
}
-bool Util::insideRect(int x, int y, int r) {
- return false;
+bool Immortal::Util::insideRect(int x, int y, int r) {
+ return false;
}
}; // namespace Util
Commit: 8b5b74fc848834040ccba9bf53b3f8fb32fee60a
https://github.com/scummvm/scummvm/commit/8b5b74fc848834040ccba9bf53b3f8fb32fee60a
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Move CycID to definitions.h
Changed paths:
engines/immortal/definitions.h
diff --git a/engines/immortal/definitions.h b/engines/immortal/definitions.h
index 1991d790c36..db0a40c8112 100644
--- a/engines/immortal/definitions.h
+++ b/engines/immortal/definitions.h
@@ -24,8 +24,8 @@
namespace Immortal {
-enum Cyc {
- kCycNone
+enum CycID {
+ kCycNone = 0
};
enum Motive { // This will likely be moved to a monster ai specific file later
Commit: 55be069f923191e9030d4e4d88d52bd8a240f698
https://github.com/scummvm/scummvm/commit/55be069f923191e9030d4e4d88d52bd8a240f698
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Implement Cycles from Cyc.GS
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/room.h
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index eeac6188208..bd11ee480c0 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -21,22 +21,114 @@
/* [Alternate Name: Sprite Animation Processing]
* --- What is a Cycle ---
+ * Strictly speaking, a Cycle is an instruction list, but it gets a little confusing.
+ * A 'Cyc' is defined through Story as a static ROM entry of the format:
+ * file, num, numcycles, cycles[]
+ * However file + num = datasprite, and numcycles is actually a boolean for repeat animation.
+ * So really it's data, repeat, cycles[]
+ * This ROM entry is pointed to by a list of pointers, indexed by a byte pointer, which is
+ * referenced lexigraphically through the dynamic story compilation system. This pointer is then
+ * turned into a 'cycList' pointer, and stored in the RAM Cyc, which is of the format:
+ * index, cycList
+ * Where index is the index into the *instruction list*, not the frame array. However it's
+ * Usually used as an index into the frame array by subtracting the frame enum first.
+ * Here's the movement of data from ROM to RAM:
+ * list of Cycles on heap (cyc)
+ * |
+ * list of word pointers in wram to Cycles (cycPtrs)
+ * |
+ * list of lexical pointers as byte indexes into word pointers (cycID -> cyclist)
*/
#include "immortal/immortal.h"
namespace Immortal {
-void ImmortalEngine::cycleNew() {}
- int ImmortalEngine::getCycleChr() {
- return 0;
+CycID ImmortalEngine::cycleNew(CycID id) {
+ // An 'available' cyc is identified by the index being -1
+ for (int i = 0; i < kMaxCycles; i++) {
+ if (_cycles[i]._index == -1) {
+ _cycles[i]._index = 0;
+ return (CycID) i;
+ }
+ }
+ debug("Null Cyc, can not be created");
+ return (CycID) (kMaxCycles - 1);
+}
+
+void ImmortalEngine::cycleFree(CycID id) {
+ _cycles[id]._index = -1;
+}
+
+void ImmortalEngine::cycleFreeAll() {
+ for (int i = 0; i < kMaxCycles; i++) {
+ _cycles[i]._index = -1;
+ }
+}
+
+bool ImmortalEngine::cycleAdvance(CycID id) {
+ /* If we have reached the end, check if repeat == true, and set back to 0 if so
+ * Otherwise, set to the last used index */
+ _cycles[id]._index++;
+ if (_cycles[id]._frames[_cycles[id]._index] == -1) {
+ if (_cycles[id]._repeat == true) {
+ _cycles[id]._index = 0;
+ } else {
+ _cycles[id]._index--;
+ return true;
+ }
+ }
+ return false;
+}
+
+int ImmortalEngine::cycleGetFrame(CycID id) {
+ // This originally did some shenanigans in Kernal to get the number, but really it's just this
+ return _cycles[id]._frames[_cycles[id]._index];
}
-void ImmortalEngine::cycleFreeAll() {}
-void ImmortalEngine::cycleGetFile() {}
-void ImmortalEngine::cycleGetNum() {}
-void ImmortalEngine::cycleGetIndex() {}
-void ImmortalEngine::cycleSetIndex() {}
-void ImmortalEngine::cycleGetFrame() {}
-void ImmortalEngine::cycleAdvance() {}
+
+int ImmortalEngine::cycleGetNumFrames(CycID id) {
+ // Why in the world is this not kept as a property of the cycle? We have to calculate the size of the array each time
+ int index = 0;
+ while (_cycles[id]._frames[index] != -1) {
+ index++;
+ }
+ return index;
+}
+
+DataSprite *ImmortalEngine::cycleGetDataSprite(CycID id) {
+ return _cycles[id]._dSprite;
+}
+
+Cycle *ImmortalEngine::getCycList(CycID id) {
+ return &_cycles[id];
+}
+
+int ImmortalEngine::cycleGetIndex(CycID id) {
+ return _cycles[id]._index;
+}
+
+void ImmortalEngine::cycleSetIndex(CycID id, int f) {
+ _cycles[id]._index = f;
+}
+
} // namespace Immortal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 778248aa4b9..fa8e0ea5697 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -136,15 +136,15 @@ void ImmortalEngine::addSprites() {
}
DataSprite *tempD = _sprites[i]._dSprite;
- Frame *tempF = &(_sprites[i]._dSprite->_frames[_sprites[i]._frame]);
- int sx = ((_sprites[i]._X + tempF->_deltaX) - tempD->_cenX) - _myViewPortX;
- int sy = ((_sprites[i]._Y + tempF->_deltaY) - tempD->_cenY) - _myViewPortY;
+ Image *tempImg = &(_sprites[i]._dSprite->_images[_sprites[i]._image]);
+ int sx = ((_sprites[i]._X + tempImg->_deltaX) - tempD->_cenX) - _myViewPortX;
+ int sy = ((_sprites[i]._Y + tempImg->_deltaY) - tempD->_cenY) - _myViewPortY;
if (sx >= 0 ) {
if (sx >= kViewPortW) {
continue;
}
- } else if ((sx + tempF->_rectX) <= 0) {
+ } else if ((sx + tempImg->_rectX) <= 0) {
continue;
}
@@ -152,7 +152,7 @@ void ImmortalEngine::addSprites() {
if (sy >= kViewPortH) {
continue;
}
- } else if ((sy + tempF->_rectY) <= 0) {
+ } else if ((sy + tempImg->_rectY) <= 0) {
continue;
}
@@ -285,7 +285,7 @@ void ImmortalEngine::drawItems() {
// If positive, it's a sprite
uint16 x = (_sprites[index]._X - _myViewPortX) + kVSX;
uint16 y = (_sprites[index]._Y - _myViewPortY) + kVSY;
- superSprite(index, x, y, _sprites[index]._dSprite->_frames[_sprites[index]._frame], kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
+ superSprite(index, x, y, _sprites[index]._dSprite->_images[_sprites[index]._image], kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
}
n++;
} while (n != _num2DrawItems);
@@ -348,7 +348,7 @@ void ImmortalEngine::printChr(char c) {
return;
}
- superSprite(0, x, y, _dataSprites[kFont]._frames[(int) c], kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
+ superSprite(0, x, y, _dataSprites[kFont]._images[(int) c], kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
if ((c == 0x27) || (c == 'T')) {
_penX -= 2; // Why is this done twice??
}
@@ -483,7 +483,7 @@ void ImmortalEngine::initStoryStatic() {
}
-void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint16 p) {
+void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
if (_numSprites != kMaxSprites) {
if (x >= (kResH + kMaxSpriteLeft)) {
x |= kMaskHigh; // Make it negative
@@ -500,7 +500,7 @@ void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int frame, uint
}
_sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
- _sprites[_numSprites]._frame = frame;
+ _sprites[_numSprites]._image = img;
_sprites[_numSprites]._dSprite = &_dataSprites[n];
_sprites[_numSprites]._on = 1;
_numSprites += 1;
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 6e04edbbb91..2379914705e 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "immortal/room.h"
#include "immortal/immortal.h"
namespace Immortal {
@@ -45,9 +46,7 @@ void ImmortalEngine::restartLogic() {
// Here's where the majority of the game actually gets initialized
miscInit();
- //qarrayInit();
- //cycInit(); <-- room.initCycles()
- //fsetInit(); <-- room.initTorches()
+ cycleFreeAll();
levelInit();
//roomInit(); <-- will be run in constructor of room
//monstInit(); <-- room.initMonsters() \
@@ -58,15 +57,13 @@ void ImmortalEngine::restartLogic() {
//objectInit(); <-- again? Odd...
//genericSpriteInit(); <-- room.initGenSprites()
- // Probably will be written as:
- // qarrayInit(); <-- will probably just be like, _qarray = new Common::Array<int? *>();
- // levelInit();
-
if (fromOldGame() == false) {
_level = 0;
levelNew(_level);
}
+ _rooms[_currentRoom]->flameInit();
+
if (_level != 7) {
_themePaused = true; // and #-1-2 = set both flags for themePaused
}
@@ -121,18 +118,16 @@ void ImmortalEngine::logic() {
levelDrawAll();
updateHitGauge();
- // What the heck? Check if we are in level 0: room 0, with no lit torches, and no projectiles
- // If so, dim the screen
_dim = 0;
- if ((_level == 0) && (/*_currentLevel.getShowRoom()*/0 == 0) && (/*roomLighted()*/false == false) && (/*getNumBullets()*/ 0 == 0)) {
- //_dim += 1;
+ if ((_level == 0) && (/*_currentLevel.getShowRoom()*/0 == 0) && (_rooms[_currentRoom]->roomLighted() == false) && (/*getNumBullets()*/ 0 == 0)) {
+ _dim += 1;
}
if (_level == 7) {
doGroan();
}
- if (/*monstIsCombat(kPlayerID)*/true == true) {
+ if (/*monstIsCombat(kPlayerID)*/false == true) {
if (getPlaying() != kSongCombat) {
playCombatSong();
}
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 7b7fb846382..58f893182b8 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -93,7 +93,7 @@ struct Flame {
FPattern _p;
uint8 _x;
uint8 _y;
- Cyc _c;
+ CycID _c;
};
struct Chest {
@@ -128,7 +128,8 @@ Common::Array<Object> _objects;
uint8 _holeCellX;
uint8 _holeCellY;
uint8 _candleTmp; // Special case for candle in maze 0
-
+ uint8 _numFlames;
+ uint8 _numInRoom;
/*
* --- Methods ---
@@ -162,13 +163,13 @@ Common::Array<Object> _objects;
* [flameSet.cpp] Functions from flameSet.GS
*/
+ void flameInit();
void flameSetRoom(Common::Array<SFlame>);
void flameDrawAll();
bool roomLighted();
void lightTorch(int x, int y);
- Cyc flameGetCyc(int first);
- //void flameFreeAll();
- //void flameSetRoom();
+ CycID flameGetCyc(int first);
+ void flameFreeAll();
/*
* [bullet.cpp] Functions from Bullet.GS
Commit: 6ba6e0fa2cddab1c0d7b558aae74db4946b2eaa9
https://github.com/scummvm/scummvm/commit/6ba6e0fa2cddab1c0d7b558aae74db4946b2eaa9
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Translation of Cycle is more accurate with addition of cycPtrs
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/immortal.h
engines/immortal/sprite_list.h
engines/immortal/story.h
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index bd11ee480c0..4a43b28be70 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -44,20 +44,21 @@
namespace Immortal {
-CycID ImmortalEngine::cycleNew(CycID id) {
+int ImmortalEngine::cycleNew(CycID id) {
// An 'available' cyc is identified by the index being -1
for (int i = 0; i < kMaxCycles; i++) {
if (_cycles[i]._index == -1) {
_cycles[i]._index = 0;
- return (CycID) i;
+ _cycles[i]._cycList = id;
+ return i;
}
}
debug("Null Cyc, can not be created");
- return (CycID) (kMaxCycles - 1);
+ return kMaxCycles - 1;
}
-void ImmortalEngine::cycleFree(CycID id) {
- _cycles[id]._index = -1;
+void ImmortalEngine::cycleFree(int c) {
+ _cycles[c]._index = -1;
}
void ImmortalEngine::cycleFreeAll() {
@@ -66,49 +67,49 @@ void ImmortalEngine::cycleFreeAll() {
}
}
-bool ImmortalEngine::cycleAdvance(CycID id) {
+bool ImmortalEngine::cycleAdvance(int c) {
/* If we have reached the end, check if repeat == true, and set back to 0 if so
* Otherwise, set to the last used index */
- _cycles[id]._index++;
- if (_cycles[id]._frames[_cycles[id]._index] == -1) {
- if (_cycles[id]._repeat == true) {
- _cycles[id]._index = 0;
+ _cycles[c]._index++;
+ if (_cycPtrs[_cycles[c]._index]._frames[_cycles[c]._index] == -1) {
+ if (_cycPtrs[_cycles[c]._index]._repeat == true) {
+ _cycles[c]._index = 0;
} else {
- _cycles[id]._index--;
+ _cycles[c]._index--;
return true;
}
}
return false;
}
-int ImmortalEngine::cycleGetFrame(CycID id) {
+int ImmortalEngine::cycleGetFrame(int c) {
// This originally did some shenanigans in Kernal to get the number, but really it's just this
- return _cycles[id]._frames[_cycles[id]._index];
+ return _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index];
}
-int ImmortalEngine::cycleGetNumFrames(CycID id) {
+int ImmortalEngine::cycleGetNumFrames(int c) {
// Why in the world is this not kept as a property of the cycle? We have to calculate the size of the array each time
int index = 0;
- while (_cycles[id]._frames[index] != -1) {
+ while (_cycPtrs[_cycles[c]._cycList]._frames[index] != -1) {
index++;
}
return index;
}
-DataSprite *ImmortalEngine::cycleGetDataSprite(CycID id) {
- return _cycles[id]._dSprite;
+DataSprite *ImmortalEngine::cycleGetDataSprite(int c) {
+ return _cycPtrs[_cycles[c]._cycList]._dSprite;
}
-Cycle *ImmortalEngine::getCycList(CycID id) {
- return &_cycles[id];
+CycID ImmortalEngine::getCycList(int c) {
+ return _cycles[c]._cycList;
}
-int ImmortalEngine::cycleGetIndex(CycID id) {
- return _cycles[id]._index;
+int ImmortalEngine::cycleGetIndex(int c) {
+ return _cycles[c]._index;
}
-void ImmortalEngine::cycleSetIndex(CycID id, int f) {
- _cycles[id]._index = f;
+void ImmortalEngine::cycleSetIndex(int c, int f) {
+ _cycles[c]._index = f;
}
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index a063b7a67b7..994325e1354 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -353,15 +353,16 @@ public:
int _combatID = 0;
// Asset members
- int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
- DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
- Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
- Cycle _cycles[kMaxCycles];
+ int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
+ DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
+ Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
+ Cycle _cycles[kMaxCycles];
Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
Common::Array<Motive> _motivePtrs;
Common::Array<Damage> _damagePtrs;
Common::Array<Use> _usePtrs;
Common::Array<Pickup> _pickupPtrs;
+ Common::Array<SCycle> _cycPtrs;
CArray2D<Motive> _programPtrs;
Common::Array<ObjType> _objTypePtrs;
@@ -587,16 +588,16 @@ GenericSprite _genSprites[6];
*/
// Misc
- CycID cycleNew(CycID id); // Adds a cycle to the current list
- void cycleFreeAll(); // Delete all cycles
- void cycleFree(CycID id);
-DataSprite *cycleGetDataSprite(CycID id); // This takes the place of getFile + getNum
- int cycleGetIndex(CycID id);
- void cycleSetIndex(CycID id, int f);
- int cycleGetFrame(CycID id);
- int cycleGetNumFrames(CycID id);
- bool cycleAdvance(CycID id);
- Cycle *getCycList(CycID id);
+ int cycleNew(CycID id); // Adds a cycle to the current list
+ void cycleFreeAll(); // Delete all cycles
+ void cycleFree(int c);
+DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + getNum
+ int cycleGetIndex(int c);
+ void cycleSetIndex(int c, int f);
+ int cycleGetFrame(int c);
+ int cycleGetNumFrames(int c);
+ bool cycleAdvance(int c);
+ CycID getCycList(int c);
/* Unneccessary cycle functions
void cycleInit();
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index 345f31d5e3d..7633becd4b7 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -41,10 +41,8 @@ Common::Array<Image> _images;
// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
struct Cycle {
-DataSprite *_dSprite;
- bool _repeat;
- int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
- int *_frames;
+ int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
+ CycID _cycList;
};
enum SpriteFrame {
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index aa86734c7e9..759bcb9e8a6 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -134,6 +134,18 @@ struct ObjType {
* bits 0-2 of X to also hold the roomOP, and bits 0-2 of Y to hold flags). However
* for the moment there's no need to replicate this particular bit of space saving.
*/
+struct SCycle {
+DataSprite *_dSprite;
+ bool _repeat;
+ int *_frames;
+ SCycle() {}
+ SCycle(DataSprite *d, bool r, int *f) {
+ _dSprite = d;
+ _repeat = r;
+ _frames = f;
+ }
+};
+
struct SRoom {
uint8 _x = 0;
uint8 _y = 0;
Commit: c0c1d2e781dc6332d1f628fcdf82e69148a0fc1e
https://github.com/scummvm/scummvm/commit/c0c1d2e781dc6332d1f628fcdf82e69148a0fc1e
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: All engine members initialized to values now
Changed paths:
engines/immortal/immortal.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 994325e1354..4f71404eee5 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -66,14 +66,6 @@
namespace Immortal {
-// Needed by kernal for drawing
-enum Screen { // These are constants that are used for defining screen related arrays
- kMaxSprites = 32, // Number of sprites allowed at once
- kViewPortCW = 256 / 64,
- kViewPortCH = 128 / kMaxSprites,
- kMaxDrawItems = kViewPortCH + 1 + kMaxSprites
-};
-
// Needed by kernal for input
enum InputAction {
kActionNothing,
@@ -150,16 +142,6 @@ struct Door {
uint8 _on = 0;
};
-// Sprites are handled by driver in Kernal
-struct Sprite {
- int _image; // Index of _dSprite._frames[]
- uint16 _X;
- uint16 _Y;
- uint16 _on; // 1 = active
- uint16 _priority;
-DataSprite *_dSprite;
-};
-
struct ImmortalGameDescription;
// Forward declaration because we will need the Disk and Room classes
@@ -203,8 +185,6 @@ public:
const int kMaxCertificate = 16;
// Screen constants
- const int kResH = 320;
- const int kResV = 200;
const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
const int kScreenH__ = 128; // ???
const int kViewPortW = 256;
@@ -247,10 +227,6 @@ public:
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
- const int kMaxSpriteAbove = 48; // Maximum sprite extents from center
- const int kMaxSpriteBelow = 16;
- const int kMaxSpriteLeft = 16;
- const int kMaxSpriteRight = 16;
const int kMaxSpriteW = 64;
const int kMaxSpriteH = 64;
const int kSpriteDY = 32;
@@ -291,8 +267,8 @@ public:
bool _draw = 0; // Whether the screen should draw this frame
int _zero = 0; // No idea what this is yet
bool _gameOverFlag = false;
- uint8 _gameFlags; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
- bool _themePaused; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
+ uint8 _gameFlags = 0; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
+ bool _themePaused = false; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
int _titlesShown = 0;
int _time = 0;
int _promoting = 0; // I think promoting means the title stuff
@@ -302,26 +278,26 @@ public:
Story _stories[8];
// Level members
- int _maxLevels = 0; // This is determined when loading in story files
- int _level = 0;
- bool _levelOver = false;
- int _count;
- int _lastLevelLoaded;
- int _lastSongLoaded;
- int _storyLevel;
- int _storyX;
- int _loadA;
- int _loadY;
- uint16 _initialX;
- uint16 _initialY;
- int _initialBX;
- int _initialBY;
- int _dRoomNum;
- int _initialRoom = 0;
- int _currentRoom = 0;
- int _lastType;
- int _roomCellX;
- int _roomCellY;
+ int _maxLevels = 0; // This is determined when loading in story files
+ int _level = 0;
+ bool _levelOver = false;
+ int _count = 0;
+ int _lastLevelLoaded = 0;
+ int _lastSongLoaded = 0;
+ int _storyLevel = 0;
+ int _storyX = 0;
+ int _loadA = 0;
+ int _loadY = 0;
+ uint16 _initialX = 0;
+ uint16 _initialY = 0;
+ int _initialBX = 0;
+ int _initialBY = 0;
+ int _dRoomNum = 0;
+ int _initialRoom = 0;
+ int _currentRoom = 0;
+ int _lastType = 0;
+ int _roomCellX = 0;
+ int _roomCellY = 0;
Room *_rooms[kMaxRooms]; // Rooms within the level
Common::Array<SFlame> _allFlames[kMaxRooms]; // The level needs it's own set of flames so that the flames can be turned on/off permenantly. This is technically more like a hashmap in the source, but it could also be seen as a 2d array, just hashed together in the source
@@ -342,19 +318,19 @@ public:
bool _singleStep; // Flag for _singleStep mode
// Input members
- int _pressedAction;
- int _heldAction;
- int _pressedDirection;
- int _heldDirection;
+ int _pressedAction = 0;
+ int _heldAction = 0;
+ int _pressedDirection = 0;
+ int _heldDirection = 0;
// Music members
Song _playing; // Currently playing song
- int _themeID = 0; // Not sure yet tbh
- int _combatID = 0;
+ int _themeID = 0; // Not sure yet tbh
+ int _combatID = 0;
// Asset members
int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
- DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteFile
+ DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteName
Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
Cycle _cycles[kMaxCycles];
Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
@@ -362,7 +338,7 @@ public:
Common::Array<Damage> _damagePtrs;
Common::Array<Use> _usePtrs;
Common::Array<Pickup> _pickupPtrs;
- Common::Array<SCycle> _cycPtrs;
+ Common::Array<SCycle> _cycPtrs; // This is not actually a set of pointers, but it is serving the function of what was called cycPtrs in the source
CArray2D<Motive> _programPtrs;
Common::Array<ObjType> _objTypePtrs;
@@ -377,15 +353,15 @@ public:
uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
uint16 _tIndex[kMaxDrawItems];
uint16 _tPriority[kMaxDrawItems];
- uint16 _viewPortX;
- uint16 _viewPortY;
- uint16 _myViewPortX; // Probably mirror of viewportX
- uint16 _myViewPortY;
+ uint16 _viewPortX = 0;
+ uint16 _viewPortY = 0;
+ uint16 _myViewPortX = 0; // Probably mirror of viewportX
+ uint16 _myViewPortY = 0;
int _lastGauge = 0; // Mirror for player health, used to update health gauge display
uint16 _penX = 0; // Basically where in the screen we are currently drawing
uint16 _penY = 0;
- uint16 _myUnivPointX;
- uint16 _myUnivPointY;
+ uint16 _myUnivPointX = 0;
+ uint16 _myUnivPointY = 0;
int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
GenericSprite _genSprites[6];
@@ -406,6 +382,8 @@ GenericSprite _genSprites[6];
*
*/
+ void setSprites(Sprite *s);
+
/*
* [Kernal.cpp] Functions from Kernal.gs and Driver.gs
*/
@@ -467,7 +445,7 @@ GenericSprite _genSprites[6];
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
- void addSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p);
+ void kernalAddSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p);
// Input
void userIO(); // Get input
@@ -670,11 +648,6 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + g
// Misc
- /*
- * [Univ.cpp] Functions from Univ.GS
- */
-
-
/*
* --- ScummVM general engine Functions ---
*
Commit: 1336c89bdffb36b092a3010c66623b504ff0cec3
https://github.com/scummvm/scummvm/commit/1336c89bdffb36b092a3010c66623b504ff0cec3
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add non-level static Cycle defintions + enum
Changed paths:
engines/immortal/definitions.h
engines/immortal/kernal.cpp
diff --git a/engines/immortal/definitions.h b/engines/immortal/definitions.h
index db0a40c8112..8de52483cc6 100644
--- a/engines/immortal/definitions.h
+++ b/engines/immortal/definitions.h
@@ -25,7 +25,48 @@
namespace Immortal {
enum CycID {
- kCycNone = 0
+ kCycNone,
+ kCycBubble1 = 0,
+ kCycBubble2,
+ kCycSparkLBlood,
+ kCycSparkRBlood,
+ kCycSparkLWizBlood,
+ kCycSparkRWizBlood,
+ kCycBurst,
+ kCycPipe0,
+ kCycPipe1,
+ kCycPipe2,
+ kCycPipe3,
+ kCycAnaDisappears,
+ kCycAnaGlimpse,
+ kCycKnife,
+ kCycFireball0,
+ kCycArrow,
+ kCycMiniBall,
+ kCycBigBurst,
+ kCycFFlicker0,
+ kCycFFlicker1,
+ kCycFFlicker2,
+ kCycFNormal0,
+ kCycFNormal1,
+ kCycFNormal2,
+ kCycFOff,
+ kCycFCandleFlicker,
+ kCycFCandleLeap,
+ kCycFCandleJump,
+ kCycFCandleSway,
+ kCycFCandleBurst,
+ kCycSink,
+ kCycNorDown
+
+ // Level 0
+ // Level 1
+ // Level 2
+ // Level 3
+ // Level 4
+ // Level 5
+ // Level 6
+ // Level 7
};
enum Motive { // This will likely be moved to a monster ai specific file later
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index fa8e0ea5697..044fde879b0 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -463,6 +463,58 @@ void ImmortalEngine::initStoryStatic() {
"This room resembles part&of the map.@"};
_strPtrs = s;
+ // Scope, amirite?
+ Common::Array<int> cyc0{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
+ Common::Array<int> cyc1{15,16,17,18,19,20,21,22,-1};
+ Common::Array<int> cyc2{0,1,2,-1};
+ Common::Array<int> cyc3{3,4,5,-1};
+ Common::Array<int> cyc4{6,7,8,9,10,-1};
+ Common::Array<int> cyc5{11,12,13,14,15,-1};
+ Common::Array<int> cyc6{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,-1};
+ Common::Array<int> cyc7{0,1,2,3,4,-1};
+ Common::Array<int> cyc8{5,1+5,2+5,3+5,4+5,-1};
+ Common::Array<int> cyc9{10,1+10,2+10,3+10,4+10,-1};
+ Common::Array<int> cyc10{15,1+15,2+15,3+15,4+15,-1};
+ Common::Array<int> cyc11{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
+ Common::Array<int> cyc12{0,1,2,3,4,5,6,7,8,9,-1};
+ Common::Array<int> cyc13{0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, -1};
+ Common::Array<int> cyc14{31,32,33,32, 34,35,36,35, 37,38,39,38, 40,41,42,41, 43,44,45,44, 46,47,48,47, 49,50,51,50, 52,53,54,53, -1};
+ Common::Array<int> cyc15{55, -1};
+ Common::Array<int> cyc16{63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66,-1};
+ Common::Array<int> cyc17{0,1,0,-1};
+ Common::Array<int> cyc18{0,1,2,4,5,6,7,8,9,10,11,12,2,1,-1};
+ Common::Array<int> cyc19{0,0,1,2,13,14,15,16,4,2,3,-1};
+ Common::Array<int> cyc20{0,1,2,3,20,21,22,23,24,25,26,27,5,4,3,-1};
+ Common::Array<int> cyc21{0,1,2,3,-1};
+ Common::Array<int> cyc22{0,17,18,19,3,-1};
+ Common::Array<int> cyc23{0,1,-1};
+ Common::Array<int> cyc24{28,28,28,28,-1};
+ Common::Array<int> cyc25{15,16,15,16,15,1+15,1+15,-1};
+ Common::Array<int> cyc26{10+15,11+15,12+15,13+15,14+15,15+15,16+15,-1};
+ Common::Array<int> cyc27{2+15,3+15,4+15,5+15,-1};
+ Common::Array<int> cyc28{6+15,7+15,8+15,9+15,-1};
+ Common::Array<int> cyc29{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
+ Common::Array<int> cyc30{0,1,2,3,3,3,3,4,5,6,-1};
+ Common::Array<int> cyc31{0,1,2,3,4,5,6,7,8,-1};
+
+ Common::Array<SCycle> c{SCycle(kBubble, false, cyc0), SCycle(kBubble, false, cyc1),
+ SCycle(kSpark, false, cyc2), SCycle(kSpark, false, cyc3),
+ SCycle(kSpark, false, cyc4), SCycle(kSpark, false, cyc5), SCycle(kSpark, false, cyc6),
+ SCycle(kPipe, false, cyc7), SCycle(kPipe, false, cyc8),
+ SCycle(kPipe, false, cyc9), SCycle(kPipe, false, cyc10),
+ SCycle(kAnaVanish, false, cyc11), SCycle(kAnaGlimpse, false, cyc12),
+ SCycle(kKnife, true, cyc13),
+ SCycle(kSpark, true, cyc14), SCycle(kSpark, true, cyc15), SCycle(kSpark, true, cyc16),
+ SCycle(kBigBurst, false, cyc17),
+ SCycle(kFlame, false, cyc18), SCycle(kFlame, false, cyc19), SCycle(kFlame, false, cyc20),
+ SCycle(kFlame, false, cyc21), SCycle(kFlame, false, cyc22), SCycle(kFlame, false, cyc23),
+ SCycle(kFlame, false, cyc24),
+ SCycle(kCandle, false, cyc25), SCycle(kCandle, false, cyc26), SCycle(kCandle, false, cyc27),
+ SCycle(kCandle, false, cyc28), SCycle(kCandle, false, cyc29),
+ SCycle(kSink, false, cyc30),
+ SCycle(kNorlacDown, false, cyc31)};
+ _cycPtrs = c;
+
Common::Array<Motive> m{};
_motivePtrs = m;
@@ -483,32 +535,8 @@ void ImmortalEngine::initStoryStatic() {
}
-void ImmortalEngine::addSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
- if (_numSprites != kMaxSprites) {
- if (x >= (kResH + kMaxSpriteLeft)) {
- x |= kMaskHigh; // Make it negative
- }
- _sprites[_numSprites]._X = (x << 1) + _viewPortX;
-
- if (y >= (kMaxSpriteAbove + kResV)) {
- y |= kMaskHigh;
- }
- _sprites[_numSprites]._Y = (y << 1) + _viewPortY;
-
- if (p >= 0x80) {
- p |= kMaskHigh;
- }
- _sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
-
- _sprites[_numSprites]._image = img;
- _sprites[_numSprites]._dSprite = &_dataSprites[n];
- _sprites[_numSprites]._on = 1;
- _numSprites += 1;
-
- } else {
- debug("Max sprites reached beeeeeep!!");
- }
-
+void ImmortalEngine::kernalAddSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
+ Immortal::Utilities::addSprite(_sprites, _viewPortX, _viewPortY, _numSprites, &_dataSprites[n], img, x, y, p);
}
void ImmortalEngine::clearSprites() {
@@ -518,6 +546,13 @@ void ImmortalEngine::clearSprites() {
}
}
+void ImmortalEngine::cycleFreeAll() {
+ // Sets all cycle indexes to -1, indicating they are available
+ for (int i = 0; i < kMaxCycles; i++) {
+ _cycles[i]._index = -1;
+ }
+}
+
void ImmortalEngine::loadSprites() {
/* This is a bit weird, so I'll explain.
* In the source, this routine loads the files onto the heap, and then
@@ -750,13 +785,13 @@ void ImmortalEngine::pump() {
// Flashes the screen (except the frame thankfully) white, black, white, black, then clears the screen and goes back to normal
useWhite();
g_system->updateScreen();
- Immortal::Util::delay(2);
+ Immortal::Utilities::delay(2);
useBlack();
g_system->updateScreen();
- Immortal::Util::delay(2);
+ Immortal::Utilities::delay(2);
useWhite();
g_system->updateScreen();
- Immortal::Util::delay(2);
+ Immortal::Utilities::delay(2);
useBlack();
g_system->updateScreen();
clearScreen();
@@ -818,7 +853,7 @@ void ImmortalEngine::fade(uint16 pal[], int dir, int delay) {
while ((count >= 0) && (count <= 256)) {
fadePal(pal, count, target);
- Immortal::Util::delay8(delay);
+ Immortal::Utilities::delay8(delay);
setColors(target);
// Same as above, it was originally a branch, this does the same thing
Commit: ef711df128f889f1b425710216f54db5d2d6b0be
https://github.com/scummvm/scummvm/commit/ef711df128f889f1b425710216f54db5d2d6b0be
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Util -> Utilities, and addition of addSprite to utilities
Changed paths:
A engines/immortal/utilities.cpp
A engines/immortal/utilities.h
R engines/immortal/bitmask.h
R engines/immortal/util.cpp
R engines/immortal/util.h
engines/immortal/module.mk
diff --git a/engines/immortal/bitmask.h b/engines/immortal/bitmask.h
deleted file mode 100644
index d9b5ba1509a..00000000000
--- a/engines/immortal/bitmask.h
+++ /dev/null
@@ -1,60 +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/>.
- *
- */
-
-#ifndef IMMORTAL_BITMASK_H
-#define IMMORTAL_BITMASK_H
-
-namespace Immortal {
-
-enum BitMask16 : uint16 {
- kMaskLow = 0x00FF,
- kMaskHigh = 0xFF00,
- kMaskLast = 0xF000,
- kMaskFirst = 0x000F,
- kMaskHLow = 0x0F00,
- kMaskLHigh = 0x00F0,
- kMaskNeg = 0x8000,
- kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
-};
-
-enum BitMask8 : uint8 {
- kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
- kMask8High = 0xF0,
- kMask8Low = 0x0F
-};
-
-enum ColourBitMask : uint16 {
- kMaskRed = 0x0F00,
- kMaskGreen = 0x00F0,
- kMaskBlue = 0x000F
-};
-
-enum ChrMask : uint16 {
- kChr0 = 0x0000,
- kChrL = 0x0001,
- kChrR = 0xFFFF,
- kChrLD = 0x0002,
- kChrRD = 0xFFFE
-};
-
-} // namespace immortal
-
-#endif
\ No newline at end of file
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 16713fcb935..3812d27981c 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -2,7 +2,7 @@ MODULE := engines/immortal
MODULE_OBJS = \
metaengine.o \
- util.o \
+ utilities.o \
disk.o \
immortal.o \
kernal.o \
diff --git a/engines/immortal/util.cpp b/engines/immortal/util.cpp
deleted file mode 100644
index 0bc671a6fbd..00000000000
--- a/engines/immortal/util.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 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 "immortal/util.h"
-
-namespace Immortal {
-
-namespace Util {
-
-void Immortal::Util::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
- g_system->delayMillis(j * 56);
-}
-
-void Immortal::Util::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
- g_system->delayMillis(j * 14);
-}
-
-void Immortal::Util::delay8(int j) { // 1/8 jiffies are 7.02ms
- g_system->delayMillis(j * 7);
-}
-
-bool Immortal::Util::inside(int x1, int y1, int a, int x2, int y2) {
- return false;
-}
-bool Immortal::Util::insideRect(int x, int y, int r) {
- return false;
-}
-
-}; // namespace Util
-
-}; // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/util.h b/engines/immortal/util.h
deleted file mode 100644
index ba7ef1df514..00000000000
--- a/engines/immortal/util.h
+++ /dev/null
@@ -1,41 +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/>.
- *
- */
-
-#ifndef IMMORTAL_UTIL_H
-#define IMMORTAL_UTIL_H
-
-#include "common/system.h"
-
-namespace Immortal {
-
-namespace Util {
-
-void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
-void delay4(int j); // || /4
-void delay8(int j); // || /8
-bool inside(int x1, int y1, int a, int x2, int y2);
-bool insideRect(int x, int y, int r);
-
-}; // namespace Util
-
-}; // namespace Immortal
-
-#endif
\ No newline at end of file
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
new file mode 100644
index 00000000000..84c71b78ed2
--- /dev/null
+++ b/engines/immortal/utilities.cpp
@@ -0,0 +1,79 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 "immortal/utilities.h"
+
+namespace Immortal {
+
+namespace Utilities {
+
+void Immortal::Utilities::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
+ g_system->delayMillis(j * 56);
+}
+
+void Immortal::Utilities::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
+ g_system->delayMillis(j * 14);
+}
+
+void Immortal::Utilities::delay8(int j) { // 1/8 jiffies are 7.02ms
+ g_system->delayMillis(j * 7);
+}
+
+bool Immortal::Utilities::inside(int x1, int y1, int a, int x2, int y2) {
+ return false;
+}
+bool Immortal::Utilities::insideRect(int x, int y, int r) {
+ return false;
+}
+
+void Immortal::Utilities::addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p) {
+ if (num != kMaxSprites) {
+ if (x >= (kResH + kMaxSpriteLeft)) {
+ x |= kMaskHigh; // Make it negative
+ }
+
+ sprites[num]._X = (x << 1) + vpX;
+
+ if (y >= (kMaxSpriteAbove + kResV)) {
+ y |= kMaskHigh;
+ }
+
+ sprites[num]._Y = (y << 1) + vpY;
+
+ if (p >= 0x80) {
+ p |= kMaskHigh;
+ }
+
+ sprites[num]._priority = ((p + y) ^ 0xFFFF) + 1;
+
+ sprites[num]._image = img;
+ sprites[num]._dSprite = d;
+ sprites[num]._on = 1;
+ num += 1;
+
+ } else {
+ debug("Max sprites reached beeeeeep!!");
+ }
+}
+
+}; // namespace Utilities
+
+}; // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
new file mode 100644
index 00000000000..c41d1668b75
--- /dev/null
+++ b/engines/immortal/utilities.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 IMMORTAL_UTIL_H
+#define IMMORTAL_UTIL_H
+
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/system.h"
+#include "immortal/sprite_list.h"
+
+namespace Immortal {
+
+enum BitMask16 : uint16 {
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000,
+ kMaskFirst = 0x000F,
+ kMaskHLow = 0x0F00,
+ kMaskLHigh = 0x00F0,
+ kMaskNeg = 0x8000,
+ kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
+};
+
+enum BitMask8 : uint8 {
+ kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMask8High = 0xF0,
+ kMask8Low = 0x0F
+};
+
+enum ColourBitMask : uint16 {
+ kMaskRed = 0x0F00,
+ kMaskGreen = 0x00F0,
+ kMaskBlue = 0x000F
+};
+
+enum ChrMask : uint16 {
+ kChr0 = 0x0000,
+ kChrL = 0x0001,
+ kChrR = 0xFFFF,
+ kChrLD = 0x0002,
+ kChrRD = 0xFFFE
+};
+
+enum Screen { // These are constants that are used for defining screen related arrays
+ kResH = 320,
+ kResV = 200,
+ kMaxSprites = 32, // Number of sprites allowed at once
+ kViewPortCW = 256 / 64,
+ kViewPortCH = 128 / kMaxSprites,
+ kMaxDrawItems = kViewPortCH + 1 + kMaxSprites,
+ kMaxSpriteAbove = 48, // Maximum sprite extents from center
+ kMaxSpriteBelow = 16,
+ kMaxSpriteLeft = 16,
+ kMaxSpriteRight = 16
+};
+
+namespace Utilities {
+
+void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
+void delay4(int j); // || /4
+void delay8(int j); // || /8
+bool inside(int x1, int y1, int a, int x2, int y2);
+bool insideRect(int x, int y, int r);
+void addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p);
+
+}; // namespace Util
+
+}; // namespace Immortal
+
+#endif
\ No newline at end of file
Commit: 5940f2312c1340ae5e7bb152964cf9cfa496db18
https://github.com/scummvm/scummvm/commit/5940f2312c1340ae5e7bb152964cf9cfa496db18
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add univAddSprite to univ.cpp
Changed paths:
engines/immortal/room.h
engines/immortal/univ.cpp
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 58f893182b8..910101f211f 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -39,14 +39,14 @@
#include "common/util.h"
#include "common/platform.h"
-// There is a lot of bit masking that needs to happen, so this header includes several enums for immortal.h, room.h, and monster.h
-#include "immortal/bitmask.h"
-
-#include "immortal/util.h"
-
// Story is needed by both immortal.h and room.h
#include "immortal/story.h"
+// Utilities.h contains many things used by all objects, not just immortal
+#include "immortal/utilities.h"
+
+#include "immortal/immortal.h"
+
#ifndef IMMORTAL_ROOM_H
#define IMMORTAL_ROOM_H
@@ -90,10 +90,10 @@ struct Monster {
};
struct Flame {
-FPattern _p;
- uint8 _x;
- uint8 _y;
- CycID _c;
+FPattern _p = kFlameOff;
+ uint8 _x = 0;
+ uint8 _y = 0;
+ int _c = 0;
};
struct Chest {
@@ -104,9 +104,10 @@ struct Bullet {
class Room {
private:
+ Common::RandomSource _randomSource;
public:
- Room(uint8 x, uint8 y, RoomFlag f);
+ Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p);
~Room() {}
/*
@@ -115,8 +116,15 @@ public:
*/
// Constants
- const uint8 kLightTorchX = 10;
+ const uint8 kLightTorchX = 10;
+ const uint8 kMaxFlameCycs = 16;
+
+ // Other
+ Sprite *_sprites;
+ Cycle *_cycles;
+DataSprite *_dataSprites;
+Common::Array<SCycle> _cycPtrs;
Common::Array<Flame> _fset;
Common::Array<Monster> _monsters;
Common::Array<Object> _objects;
@@ -136,6 +144,10 @@ Common::Array<Object> _objects;
*
*/
+ uint32 getRandomNumber(uint maxNum) {
+ return _randomSource.getRandomNumber(maxNum);
+ }
+
/*
* [room.cpp] Functions from Room.GS
*/
@@ -145,7 +157,7 @@ Common::Array<Object> _objects;
//void getTilePair(uint8 x, uint8 y); // Modifies a struct of the tile number, aboveTile number, and the cell coordinates of the tile
void setHole();
- void drawContents();
+ void drawContents(uint16 vX, uint16 vY, int nS);
bool getTilePair(uint8 x, uint8 y, int id);
bool getWideWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id, int spacing);
bool getWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id);
@@ -159,17 +171,45 @@ Common::Array<Object> _objects;
void getCell(uint16 &x, uint16 &y);
+ /*
+ * [Cycle.cpp] Functions from Cyc
+ */
+
+ // Init
+ int cycleNew(CycID id); // Adds a cycle to the current list
+ void cycleFree(int c);
+
+ // Getters
+DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + getNum
+ int cycleGetIndex(int c);
+ int cycleGetFrame(int c);
+ int cycleGetNumFrames(int c);
+
+ // Setters
+ void cycleSetIndex(int c, int f);
+
+ // Misc
+ bool cycleAdvance(int c);
+ CycID getCycList(int c);
+
+ /* Unneccessary cycle functions
+ void cycleInit();
+ void cycleFree();
+ void cycleGetNumFrames();
+ void cycleGetList();*/
+
/*
* [flameSet.cpp] Functions from flameSet.GS
*/
+ //void flameNew() does not need to exist, because we create the duplicate SFlame in Level, and the array in immortal.h is not accessable from here
void flameInit();
- void flameSetRoom(Common::Array<SFlame>);
- void flameDrawAll();
+ void flameDrawAll(uint16 vX, uint16 vY, int nS);
bool roomLighted();
void lightTorch(int x, int y);
- CycID flameGetCyc(int first);
void flameFreeAll();
+ void flameSetRoom(Common::Array<SFlame>);
+ int flameGetCyc(Flame *f, int first);
/*
* [bullet.cpp] Functions from Bullet.GS
@@ -182,6 +222,11 @@ Common::Array<Object> _objects;
*/
+ /*
+ * [Univ.cpp] Functions from Univ.GS
+ */
+
+ void univAddSprite(uint16 vX, uint16 vY, int nS, uint16 x, uint16 y, SpriteName n, int img, uint16 p);
};
diff --git a/engines/immortal/univ.cpp b/engines/immortal/univ.cpp
index 7349cdbff8a..f52997c0b2f 100644
--- a/engines/immortal/univ.cpp
+++ b/engines/immortal/univ.cpp
@@ -19,9 +19,12 @@
*
*/
-#include "immortal/immortal.h"
+#include "immortal/room.h"
namespace Immortal {
+void Room::univAddSprite(uint16 vX, uint16 vY, int nS, uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
+ //Immortal::Utilities::addSprite(_sprites, vX, vY, nS, &_dataSprites[n], img, x, y, p);
+}
} // namespace immortal
\ No newline at end of file
Commit: 821a539b6a7e863971156f0f2ba8914c7ceeecef
https://github.com/scummvm/scummvm/commit/821a539b6a7e863971156f0f2ba8914c7ceeecef
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Implement Cycles more accurately, under Room instead of Immortal, except for cycleFreeAll()
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/room.cpp
engines/immortal/sprite_list.h
engines/immortal/story.h
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index 4a43b28be70..12b339090ba 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -21,7 +21,7 @@
/* [Alternate Name: Sprite Animation Processing]
* --- What is a Cycle ---
- * Strictly speaking, a Cycle is an instruction list, but it gets a little confusing.
+ * Strictly speaking, a Cycle is an instruction list, but more specifically:
* A 'Cyc' is defined through Story as a static ROM entry of the format:
* file, num, numcycles, cycles[]
* However file + num = datasprite, and numcycles is actually a boolean for repeat animation.
@@ -40,16 +40,17 @@
* list of lexical pointers as byte indexes into word pointers (cycID -> cyclist)
*/
-#include "immortal/immortal.h"
+#include "immortal/room.h"
namespace Immortal {
-int ImmortalEngine::cycleNew(CycID id) {
+int Room::cycleNew(CycID id) {
// An 'available' cyc is identified by the index being -1
for (int i = 0; i < kMaxCycles; i++) {
if (_cycles[i]._index == -1) {
_cycles[i]._index = 0;
_cycles[i]._cycList = id;
+ debug("made cyc, = %d", i);
return i;
}
}
@@ -57,22 +58,16 @@ int ImmortalEngine::cycleNew(CycID id) {
return kMaxCycles - 1;
}
-void ImmortalEngine::cycleFree(int c) {
+void Room::cycleFree(int c) {
_cycles[c]._index = -1;
}
-void ImmortalEngine::cycleFreeAll() {
- for (int i = 0; i < kMaxCycles; i++) {
- _cycles[i]._index = -1;
- }
-}
-
-bool ImmortalEngine::cycleAdvance(int c) {
+bool Room::cycleAdvance(int c) {
/* If we have reached the end, check if repeat == true, and set back to 0 if so
* Otherwise, set to the last used index */
_cycles[c]._index++;
- if (_cycPtrs[_cycles[c]._index]._frames[_cycles[c]._index] == -1) {
- if (_cycPtrs[_cycles[c]._index]._repeat == true) {
+ if (_cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index] == -1) {
+ if (_cycPtrs[_cycles[c]._cycList]._repeat == true) {
_cycles[c]._index = 0;
} else {
_cycles[c]._index--;
@@ -82,12 +77,13 @@ bool ImmortalEngine::cycleAdvance(int c) {
return false;
}
-int ImmortalEngine::cycleGetFrame(int c) {
+int Room::cycleGetFrame(int c) {
// This originally did some shenanigans in Kernal to get the number, but really it's just this
- return _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index];
+ debug("%d", _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index]);
+ return _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index];
}
-int ImmortalEngine::cycleGetNumFrames(int c) {
+int Room::cycleGetNumFrames(int c) {
// Why in the world is this not kept as a property of the cycle? We have to calculate the size of the array each time
int index = 0;
while (_cycPtrs[_cycles[c]._cycList]._frames[index] != -1) {
@@ -96,19 +92,19 @@ int ImmortalEngine::cycleGetNumFrames(int c) {
return index;
}
-DataSprite *ImmortalEngine::cycleGetDataSprite(int c) {
- return _cycPtrs[_cycles[c]._cycList]._dSprite;
+DataSprite *Room::cycleGetDataSprite(int c) {
+ return &_dataSprites[_cycPtrs[_cycles[c]._cycList]._sName];
}
-CycID ImmortalEngine::getCycList(int c) {
+CycID Room::getCycList(int c) {
return _cycles[c]._cycList;
}
-int ImmortalEngine::cycleGetIndex(int c) {
+int Room::cycleGetIndex(int c) {
return _cycles[c]._index;
}
-void ImmortalEngine::cycleSetIndex(int c, int f) {
+void Room::cycleSetIndex(int c, int f) {
_cycles[c]._index = f;
}
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 4f71404eee5..0dcfe60b77a 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -56,14 +56,14 @@
#include "common/util.h"
#include "common/platform.h"
-// There is a lot of bit masking that needs to happen, so this header includes several enums for immortal.h, room.h, and monster.h
-#include "immortal/bitmask.h"
-
-#include "immortal/util.h"
-
// Story is needed by both immortal.h and room.h
#include "immortal/story.h"
+// Utilities.h contains many things used by all objects, not just immortal
+#include "immortal/utilities.h"
+
+#include "immortal/room.h"
+
namespace Immortal {
// Needed by kernal for input
@@ -185,25 +185,25 @@ public:
const int kMaxCertificate = 16;
// Screen constants
- const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
- const int kScreenH__ = 128; // ???
- const int kViewPortW = 256;
- const int kViewPortH = 128;
- const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
- const int kScreenLeft = 32;
- const int kScreenTop = 20;
- const int kTextLeft = 8;
- const int kTextTop = 4;
- const int kGaugeX = 0;
- const int kGaugeY = -13; // ???
- const int kScreenBMW = 160; // Literally no idea yet
- const uint16 kChrW = 64;
- const uint16 kChrH = 32;
- const uint16 kChrH2 = kChrH * 2;
- const uint16 kChrH3 = kChrH * 3;
- const int kChrLen = (kChrW / 2) * kChrH;
- const int kChrBMW = kChrW / 2;
- const int kLCutaway = 4;
+ const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
+ const int kScreenH__ = 128; // ???
+ const int kViewPortW = 256;
+ const int kViewPortH = 128;
+ const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
+ const int kScreenLeft = 32;
+ const int kScreenTop = 20;
+ const int kTextLeft = 8;
+ const int kTextTop = 4;
+ const int kGaugeX = 0;
+ const int kGaugeY = -13; // ???
+ const int kScreenBMW = 160; // Screen BitMap Width?
+ const uint16 kChrW = 64;
+ const uint16 kChrH = 32;
+ const uint16 kChrH2 = kChrH * 2;
+ const uint16 kChrH3 = kChrH * 3;
+ const int kChrLen = (kChrW / 2) * kChrH;
+ const int kChrBMW = kChrW / 2;
+ const int kLCutaway = 4;
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
kChrH2, kChrH, kChrH2, kChrH2, kChr0,
@@ -224,31 +224,31 @@ public:
0, 0, 0, 0, 0, 0};
// Disk offsets
- const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
+ const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
- const int kMaxSpriteW = 64;
- const int kMaxSpriteH = 64;
- const int kSpriteDY = 32;
- const int kVSX = kMaxSpriteW;
- const int kVSY = kSpriteDY;
- const int kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
- const int kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
- const int kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
- const int kMySuperBottom = kVSDY + kViewPortH;
- const int kSuperBottom = 200;
- const int kMySuperTop = kVSDY;
- const int kSuperTop = 0;
- const int kViewPortSpX = 32;
- const int kViewPortSpY = 0;
- const int kWizardX = 28; // Common sprite center for some reason
- const int kWizardY = 37;
+ const int kMaxSpriteW = 64;
+ const int kMaxSpriteH = 64;
+ const int kSpriteDY = 32;
+ const int kVSX = kMaxSpriteW;
+ const int kVSY = kSpriteDY;
+ const int kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
+ const int kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
+ const int kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
+ const int kMySuperBottom = kVSDY + kViewPortH;
+ const int kSuperBottom = 200;
+ const int kMySuperTop = kVSDY;
+ const int kSuperTop = 0;
+ const int kViewPortSpX = 32;
+ const int kViewPortSpY = 0;
+ const int kWizardX = 28; // Common sprite center for some reason
+ const int kWizardY = 37;
// Asset constants
- const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
- const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
- const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
- const char kGaugeStart = 1; // First kGaugeOn char to draw
+ const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
+ const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
+ const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
+ const char kGaugeStart = 1; // First kGaugeOn char to draw
// Level constants
const int kStoryNull = 5;
@@ -263,16 +263,16 @@ public:
// Misc
Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
uint8 _certificate[16]; // The certificate (password) is basically the inventory/equipment array
- uint8 _lastCertLen = 0;
- bool _draw = 0; // Whether the screen should draw this frame
- int _zero = 0; // No idea what this is yet
- bool _gameOverFlag = false;
- uint8 _gameFlags = 0; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
- bool _themePaused = false; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
- int _titlesShown = 0;
- int _time = 0;
- int _promoting = 0; // I think promoting means the title stuff
- bool _restart = false;
+ uint8 _lastCertLen = 0;
+ bool _draw = 0; // Whether the screen should draw this frame
+ int _zero = 0; // No idea what this is yet
+ bool _gameOverFlag = false;
+ uint8 _gameFlags = 0; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
+ bool _themePaused = false; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
+ int _titlesShown = 0;
+ int _time = 0;
+ int _promoting = 0; // I think promoting means the title stuff
+ bool _restart = false;
// Story members
Story _stories[8];
@@ -315,7 +315,7 @@ public:
uint8 _secretDelta = 0;
// Debug members
- bool _singleStep; // Flag for _singleStep mode
+ bool _singleStep = false; // Flag for _singleStep mode
// Input members
int _pressedAction = 0;
@@ -353,16 +353,16 @@ public:
uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
uint16 _tIndex[kMaxDrawItems];
uint16 _tPriority[kMaxDrawItems];
- uint16 _viewPortX = 0;
- uint16 _viewPortY = 0;
- uint16 _myViewPortX = 0; // Probably mirror of viewportX
- uint16 _myViewPortY = 0;
- int _lastGauge = 0; // Mirror for player health, used to update health gauge display
- uint16 _penX = 0; // Basically where in the screen we are currently drawing
- uint16 _penY = 0;
- uint16 _myUnivPointX = 0;
- uint16 _myUnivPointY = 0;
- int _num2DrawItems = 0;
+ uint16 _viewPortX = 0;
+ uint16 _viewPortY = 0;
+ uint16 _myViewPortX = 0; // Probably mirror of viewportX
+ uint16 _myViewPortY = 0;
+ int _lastGauge = 0; // Mirror for player health, used to update health gauge display
+ uint16 _penX = 0; // Basically where in the screen we are currently drawing
+ uint16 _penY = 0;
+ uint16 _myUnivPointX = 0;
+ uint16 _myUnivPointY = 0;
+ int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
GenericSprite _genSprites[6];
@@ -382,8 +382,6 @@ GenericSprite _genSprites[6];
*
*/
- void setSprites(Sprite *s);
-
/*
* [Kernal.cpp] Functions from Kernal.gs and Driver.gs
*/
@@ -560,28 +558,12 @@ GenericSprite _genSprites[6];
//void setLastType <-- sta lastType
//void getShowRoom <-- lda currentRoom
-
/*
* [Cycle.cpp] Functions from Cyc
*/
// Misc
- int cycleNew(CycID id); // Adds a cycle to the current list
void cycleFreeAll(); // Delete all cycles
- void cycleFree(int c);
-DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + getNum
- int cycleGetIndex(int c);
- void cycleSetIndex(int c, int f);
- int cycleGetFrame(int c);
- int cycleGetNumFrames(int c);
- bool cycleAdvance(int c);
- CycID getCycList(int c);
-
- /* Unneccessary cycle functions
- void cycleInit();
- void cycleFree();
- void cycleGetNumFrames();
- void cycleGetList();*/
/*
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index b125d6ed6b3..93e776c3c24 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -74,12 +74,10 @@ void ImmortalEngine::levelLoadFile(int l) {
for (int d = 0; d < _stories[l]._doors.size(); d++) {
doorNew(_stories[l]._doors[d]);
- debug("door %d", d);
}
for (int r = 0; r < _stories[l]._rooms.size(); r++) {
- _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags);
- debug("Room %d", r);
+ _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags, _sprites, _dataSprites, _cycles, _cycPtrs);
Common::Array<SFlame> allFlames(_stories[l]._flames[r].size());
if (_stories[l]._flames[r].size() > 0) {
@@ -89,28 +87,21 @@ void ImmortalEngine::levelLoadFile(int l) {
sf._x = _stories[l]._flames[r][f]._x;
sf._y = _stories[l]._flames[r][f]._y;
allFlames[f] = sf;
- debugN("F%d", f);
}
}
_allFlames[r] = allFlames;
- debug("");
if (_stories[l]._objects[r].size() > 0) {
for (int o = 0; o < _stories[l]._objects[r].size(); o++) {
//objNew(_stories[l]._objects[r][o]);
- debugN("O%d", o);
}
}
- debug("");
if (_stories[l]._monsters[r].size() > 0) {
for (int m = 0; m < _stories[l]._monsters[r].size(); m++) {
//monstNew(_stories[l]._monsters[r][m]);
- debugN("M%d", m);
}
}
- debug("");
-
}
// Set up the _initial variables for the engine scope
@@ -123,6 +114,7 @@ void ImmortalEngine::univAtNew(int l) {
_initialY = _stories[l]._initialUnivY;
_initialBX = _stories[l]._playerPointX;
_initialBY = _stories[l]._playerPointY;
+
//doorToNextLevel(_stories[l]._doorToNextLevel, _initialBX, _initialBY);
//doorSetLadders(_stories[l]._doorSetLadders);
//roomSetHole(_stories[l]._setHole, _stories[l]._setHoleX, _stories[l]._setHoleY);
@@ -133,12 +125,14 @@ void ImmortalEngine::levelDrawAll() {
_count++;
//univAutoCenter();
clearSprites();
- _rooms[_currentRoom]->drawContents();
+ // Room needs to be able to add to the sprite list, so we need to give it a pointer to it first
+ _rooms[_currentRoom]->drawContents(_viewPortX, _viewPortY, _numSprites);
}
void ImmortalEngine::levelShowRoom(int r, int bX, int bY) {
_currentRoom = r;
- _rooms[r]->flameSetRoom(_allFlames[r]);
+ cycleFreeAll(); // This may not be needed, or it may need to be changed slightly
+ _rooms[_currentRoom]->flameSetRoom(_allFlames[r]);
//univSetRoom(r, bX, bY);
//fset, spark, bullet, and door get set to the current room
//roomGetCell(r, bX, bY);
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
index 320624f1695..9ffdcc286c7 100644
--- a/engines/immortal/room.cpp
+++ b/engines/immortal/room.cpp
@@ -23,11 +23,16 @@
namespace Immortal {
-Room::Room(uint8 x, uint8 y, RoomFlag f) {
- _xPos = x;
- _yPos = y;
- _flags = f;
- _candleTmp = 0;
+Room::Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p)
+ : _xPos(x)
+ , _yPos(y)
+ , _flags(f)
+ , _sprites(s)
+ , _dataSprites(d)
+ , _cycles(c)
+ , _cycPtrs(p)
+ , _candleTmp(0)
+ , _randomSource("Immortal") {
}
void Room::addMonster() {
@@ -65,7 +70,16 @@ void Room::getCell(uint16 &x, uint16 &y) {
}
void Room::setHole() {}
-void Room::drawContents() {}
+
+void Room::drawContents(uint16 vX, uint16 vY, int nS) {
+ flameDrawAll(vX, vY, nS);
+ //sparkDrawAll();
+ //bulletDrawAll();
+ //genSpriteDrawAll();
+ //loop over monsters and draw each
+ //loop over objects and draw each
+ //doorDrawAll();
+}
bool Room::getWideWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id, int spacing) {
return true;
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index 7633becd4b7..ad0a9af44be 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -39,10 +39,13 @@ struct DataSprite {
Common::Array<Image> _images;
};
-// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
-struct Cycle {
- int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
- CycID _cycList;
+struct Sprite {
+ int _image; // Index of _dSprite._frames[]
+ uint16 _X;
+ uint16 _Y;
+ uint16 _on; // 1 = active
+ uint16 _priority;
+DataSprite *_dSprite;
};
enum SpriteFrame {
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 759bcb9e8a6..0fb02e69ac2 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -129,18 +129,24 @@ struct ObjType {
Use _run;
};
+// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
+struct Cycle {
+ int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
+ CycID _cycList;
+};
+
/* Strictly speaking, many of these structs (which were rom data written dynamically
* with compiler macros) combine multiple properties into single bytes (ex. room uses
* bits 0-2 of X to also hold the roomOP, and bits 0-2 of Y to hold flags). However
* for the moment there's no need to replicate this particular bit of space saving.
*/
struct SCycle {
-DataSprite *_dSprite;
+SpriteName _sName;
bool _repeat;
- int *_frames;
+Common::Array<int> _frames;
SCycle() {}
- SCycle(DataSprite *d, bool r, int *f) {
- _dSprite = d;
+ SCycle(SpriteName s, bool r, Common::Array<int> f) {
+ _sName = s;
_repeat = r;
_frames = f;
}
Commit: d13fbf16a5163638b7f8c81cfa7e4af928f910f1
https://github.com/scummvm/scummvm/commit/d13fbf16a5163638b7f8c81cfa7e4af928f910f1
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Implement flameSet.cpp
Changed paths:
engines/immortal/flameSet.cpp
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index fe335010cf4..994147d01aa 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -35,23 +35,30 @@
namespace Immortal {
-void Room::flameSetRoom(Common::Array<SFlame> allFlames) {
- for (int i = 0; i < allFlames.size(); i++) {
- Flame f;
- f._p = allFlames[i]._p;
- f._x = allFlames[i]._x;
- f._y = allFlames[i]._y;
- f._c = flameGetCyc(0 | _candleTmp);
- _candleTmp = 1;
- _fset.push_back(f);
- }
+void Room::flameInit() {
+ flameFreeAll();
+ _candleTmp = 0;
}
-void Room::flameDrawAll() {
+void Room::flameFreeAll() {
+ _numFlames = 0;
+ _numInRoom = 0;
+}
+void Room::flameDrawAll(uint16 vX, uint16 vY, int nS) {
+ for (int i = 0; i < _fset.size(); i++) {
+ univAddSprite(vX, vY, nS, _fset[i]._x, _fset[i]._y, _cycPtrs[_cycles[_fset[i]._c]._cycList]._sName, cycleGetFrame(_fset[i]._c), 0);
+ if (cycleAdvance(_fset[i]._c) == true) {
+ cycleFree(_fset[i]._c);
+ _fset[i]._c = flameGetCyc(&_fset[i], 1);
+ }
+ }
}
bool Room::roomLighted() {
+ // Just for now, we say it's always lit
+ return true;
+
// Very simple, just checks every torch and if any of them are lit, we say the room is lit
for (int i = 0; i < _fset.size(); i++) {
if (_fset[i]._p != kFlameOff) {
@@ -72,7 +79,7 @@ void Room::lightTorch(int x, int y) {
for (int i = 0; i < _fset.size(); i++) {
if (_fset[i]._p == kFlameOff) {
- if (Immortal::Util::inside(x, y, kLightTorchX, _fset[i]._x + 16, _fset[i]._y + 8)) {
+ if (Immortal::Utilities::inside(x, y, kLightTorchX, _fset[i]._x + 16, _fset[i]._y + 8)) {
_fset[i]._p = kFlameNormal;
}
@@ -80,8 +87,62 @@ void Room::lightTorch(int x, int y) {
}
}
-Cyc Room::flameGetCyc(int first) {
- return kCycNone;
+void Room::flameSetRoom(Common::Array<SFlame> allFlames) {
+ for (int i = 0; i < allFlames.size(); i++) {
+ Flame f;
+ f._p = allFlames[i]._p;
+ f._x = allFlames[i]._x;
+ f._y = allFlames[i]._y;
+ f._c = flameGetCyc(&f, (0 | _candleTmp));
+ debug("made flame, cyc = %d", f._c);
+ _fset.push_back(f);
+ }
+ _candleTmp = 1;
+}
+
+int Room::flameGetCyc(Flame *f, int first) {
+ /* I must say, although this is clever, it is the most
+ * convoluted way to do this I could imagine. Here's what it does:
+ * Get a random number between 0 and 255. Now reduce this number by the length
+ * of the array for the particular flame pattern until we are at less than 0.
+ * Now add back that same length. We are now at the length of the array
+ * minus a random amount between 0 and the length of the array.
+ * This gives us a random entry within the array to start at.
+ */
+ CycID flamePatA[] = {kCycFNormal0, kCycFNormal1, kCycFNormal2,
+ kCycFNormal0, kCycFNormal1, kCycFNormal2,
+ kCycFNormal0, kCycFNormal1, kCycFNormal2,
+ kCycFNormal0, kCycFNormal1, kCycFNormal2};
+ CycID flamePatB[] = {kCycFCandleBurst, kCycFCandleSway, kCycFCandleJump,
+ kCycFCandleLeap, kCycFCandleFlicker,
+ kCycFCandleFlicker, kCycFCandleFlicker, kCycFCandleFlicker};
+ CycID flamePatC[] = {kCycFOff};
+ CycID flamePatD[] = {kCycFFlicker0, kCycFFlicker1, kCycFFlicker2};
+
+ int numFlameCycs[] = {12, 8, 1, 3};
+
+ int r = getRandomNumber(255) & (kMaxFlameCycs - 1);
+
+ do {
+ r -= numFlameCycs[(int) f->_p];
+ } while (r >= 0);
+
+ r += numFlameCycs[(int) f->_p];
+
+ // Why is this not indexed further? ie. LDA patternTable,x : STA $00 : LDA ($00),y instead of a branch tree?
+ // Pretty sure CPX 3 times is more than a single LDA (dp),y
+ switch (f->_p) {
+ case 0:
+ return cycleNew(flamePatA[r]);
+ case 1:
+ return cycleNew(flamePatB[r]);
+ case 2:
+ return cycleNew(flamePatC[r]);
+ case 3:
+ return cycleNew(flamePatD[r]);
+ default:
+ return 0;
+ }
}
} // namespace immortal
Commit: 259e993f680ec3a7dfed1749bca93298af5a5077
https://github.com/scummvm/scummvm/commit/259e993f680ec3a7dfed1749bca93298af5a5077
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Spaces -> Tabs
Changed paths:
engines/immortal/univ.cpp
engines/immortal/utilities.h
diff --git a/engines/immortal/univ.cpp b/engines/immortal/univ.cpp
index f52997c0b2f..91629b24253 100644
--- a/engines/immortal/univ.cpp
+++ b/engines/immortal/univ.cpp
@@ -24,7 +24,7 @@
namespace Immortal {
void Room::univAddSprite(uint16 vX, uint16 vY, int nS, uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
- //Immortal::Utilities::addSprite(_sprites, vX, vY, nS, &_dataSprites[n], img, x, y, p);
+ //Immortal::Utilities::addSprite(_sprites, vX, vY, nS, &_dataSprites[n], img, x, y, p);
}
} // namespace immortal
\ No newline at end of file
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
index c41d1668b75..6da4088a2e4 100644
--- a/engines/immortal/utilities.h
+++ b/engines/immortal/utilities.h
@@ -30,47 +30,47 @@
namespace Immortal {
enum BitMask16 : uint16 {
- kMaskLow = 0x00FF,
- kMaskHigh = 0xFF00,
- kMaskLast = 0xF000,
- kMaskFirst = 0x000F,
- kMaskHLow = 0x0F00,
- kMaskLHigh = 0x00F0,
- kMaskNeg = 0x8000,
- kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
+ kMaskLow = 0x00FF,
+ kMaskHigh = 0xFF00,
+ kMaskLast = 0xF000,
+ kMaskFirst = 0x000F,
+ kMaskHLow = 0x0F00,
+ kMaskLHigh = 0x00F0,
+ kMaskNeg = 0x8000,
+ kMask12Bit = 0x0F9F // Compression code (pos, 00, len) is stored in lower 12 bits of word
};
enum BitMask8 : uint8 {
- kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
- kMask8High = 0xF0,
- kMask8Low = 0x0F
+ kMaskASCII = 0x7F, // The non-extended ASCII table uses 7 bits, this makes a couple of things easier
+ kMask8High = 0xF0,
+ kMask8Low = 0x0F
};
enum ColourBitMask : uint16 {
- kMaskRed = 0x0F00,
- kMaskGreen = 0x00F0,
- kMaskBlue = 0x000F
+ kMaskRed = 0x0F00,
+ kMaskGreen = 0x00F0,
+ kMaskBlue = 0x000F
};
enum ChrMask : uint16 {
- kChr0 = 0x0000,
- kChrL = 0x0001,
- kChrR = 0xFFFF,
- kChrLD = 0x0002,
- kChrRD = 0xFFFE
+ kChr0 = 0x0000,
+ kChrL = 0x0001,
+ kChrR = 0xFFFF,
+ kChrLD = 0x0002,
+ kChrRD = 0xFFFE
};
enum Screen { // These are constants that are used for defining screen related arrays
- kResH = 320,
- kResV = 200,
- kMaxSprites = 32, // Number of sprites allowed at once
- kViewPortCW = 256 / 64,
- kViewPortCH = 128 / kMaxSprites,
- kMaxDrawItems = kViewPortCH + 1 + kMaxSprites,
- kMaxSpriteAbove = 48, // Maximum sprite extents from center
- kMaxSpriteBelow = 16,
- kMaxSpriteLeft = 16,
- kMaxSpriteRight = 16
+ kResH = 320,
+ kResV = 200,
+ kMaxSprites = 32, // Number of sprites allowed at once
+ kViewPortCW = 256 / 64,
+ kViewPortCH = 128 / kMaxSprites,
+ kMaxDrawItems = kViewPortCH + 1 + kMaxSprites,
+ kMaxSpriteAbove = 48, // Maximum sprite extents from center
+ kMaxSpriteBelow = 16,
+ kMaxSpriteLeft = 16,
+ kMaxSpriteRight = 16
};
namespace Utilities {
Commit: 4b28cc268d216f26821eb3c770ecb67c07bfd9a7
https://github.com/scummvm/scummvm/commit/4b28cc268d216f26821eb3c770ecb67c07bfd9a7
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Remove redundant namespaces in Utilities
Changed paths:
engines/immortal/utilities.cpp
engines/immortal/utilities.h
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
index 84c71b78ed2..21ca9e13810 100644
--- a/engines/immortal/utilities.cpp
+++ b/engines/immortal/utilities.cpp
@@ -23,57 +23,54 @@
namespace Immortal {
-namespace Utilities {
-
-void Immortal::Utilities::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
+void Utilities::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
g_system->delayMillis(j * 56);
}
-void Immortal::Utilities::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
+void Utilities::delay4(int j) { // Named in source quarterClock for some reason, 1/4 jiffies are 14.04ms
g_system->delayMillis(j * 14);
}
-void Immortal::Utilities::delay8(int j) { // 1/8 jiffies are 7.02ms
+void Utilities::delay8(int j) { // 1/8 jiffies are 7.02ms
g_system->delayMillis(j * 7);
}
-bool Immortal::Utilities::inside(int x1, int y1, int a, int x2, int y2) {
+bool Utilities::inside(int x1, int y1, int a, int x2, int y2) {
return false;
}
-bool Immortal::Utilities::insideRect(int x, int y, int r) {
+bool Utilities::insideRect(int x, int y, int r) {
return false;
}
-void Immortal::Utilities::addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p) {
- if (num != kMaxSprites) {
+void Utilities::addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int *num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p) {
+ debug("adding sprite...");
+ if (*num != kMaxSprites) {
if (x >= (kResH + kMaxSpriteLeft)) {
x |= kMaskHigh; // Make it negative
}
- sprites[num]._X = (x << 1) + vpX;
+ sprites[*num]._X = (x << 1) + vpX;
if (y >= (kMaxSpriteAbove + kResV)) {
y |= kMaskHigh;
}
- sprites[num]._Y = (y << 1) + vpY;
+ sprites[*num]._Y = (y << 1) + vpY;
if (p >= 0x80) {
p |= kMaskHigh;
}
- sprites[num]._priority = ((p + y) ^ 0xFFFF) + 1;
+ sprites[*num]._priority = ((p + y) ^ 0xFFFF) + 1;
- sprites[num]._image = img;
- sprites[num]._dSprite = d;
- sprites[num]._on = 1;
- num += 1;
-
+ sprites[*num]._image = img;
+ sprites[*num]._dSprite = d;
+ sprites[*num]._on = 1;
+ *num += 1;
+ debug("sprite added");
} else {
debug("Max sprites reached beeeeeep!!");
}
}
-}; // namespace Utilities
-
}; // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
index 6da4088a2e4..0c8304f22f2 100644
--- a/engines/immortal/utilities.h
+++ b/engines/immortal/utilities.h
@@ -80,7 +80,7 @@ void delay4(int j); // || /4
void delay8(int j); // || /8
bool inside(int x1, int y1, int a, int x2, int y2);
bool insideRect(int x, int y, int r);
-void addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p);
+void addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int *num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p);
}; // namespace Util
Commit: ff06e73d6f24ad501b037deae2c3ab0131963200
https://github.com/scummvm/scummvm/commit/ff06e73d6f24ad501b037deae2c3ab0131963200
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Room includes reference to sprite data from ImmortalEngine
Changed paths:
engines/immortal/level.cpp
engines/immortal/room.cpp
engines/immortal/room.h
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 93e776c3c24..92510e2022f 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -70,14 +70,13 @@ void ImmortalEngine::levelLoadFile(int l) {
*/
// Create the rooms and doors, then populate the rooms with their objects and actors
- debug("loading level file...");
for (int d = 0; d < _stories[l]._doors.size(); d++) {
doorNew(_stories[l]._doors[d]);
}
for (int r = 0; r < _stories[l]._rooms.size(); r++) {
- _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags, _sprites, _dataSprites, _cycles, _cycPtrs);
+ _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags, _sprites, _dataSprites, _cycles, _cycPtrs, &_numSprites);
Common::Array<SFlame> allFlames(_stories[l]._flames[r].size());
if (_stories[l]._flames[r].size() > 0) {
@@ -126,7 +125,7 @@ void ImmortalEngine::levelDrawAll() {
//univAutoCenter();
clearSprites();
// Room needs to be able to add to the sprite list, so we need to give it a pointer to it first
- _rooms[_currentRoom]->drawContents(_viewPortX, _viewPortY, _numSprites);
+ _rooms[_currentRoom]->drawContents(_viewPortX, _viewPortY);
}
void ImmortalEngine::levelShowRoom(int r, int bX, int bY) {
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
index 9ffdcc286c7..a66573f2e83 100644
--- a/engines/immortal/room.cpp
+++ b/engines/immortal/room.cpp
@@ -23,7 +23,7 @@
namespace Immortal {
-Room::Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p)
+Room::Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p, int *n)
: _xPos(x)
, _yPos(y)
, _flags(f)
@@ -31,6 +31,7 @@ Room::Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Com
, _dataSprites(d)
, _cycles(c)
, _cycPtrs(p)
+ , _numSprites(n)
, _candleTmp(0)
, _randomSource("Immortal") {
}
@@ -71,8 +72,8 @@ void Room::getCell(uint16 &x, uint16 &y) {
void Room::setHole() {}
-void Room::drawContents(uint16 vX, uint16 vY, int nS) {
- flameDrawAll(vX, vY, nS);
+void Room::drawContents(uint16 vX, uint16 vY) {
+ //flameDrawAll(vX, vY);
//sparkDrawAll();
//bulletDrawAll();
//genSpriteDrawAll();
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 910101f211f..b663329a3a4 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -107,7 +107,7 @@ private:
Common::RandomSource _randomSource;
public:
- Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p);
+ Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p, int *n);
~Room() {}
/*
@@ -120,8 +120,9 @@ public:
const uint8 kMaxFlameCycs = 16;
// Other
- Sprite *_sprites;
- Cycle *_cycles;
+ int *_numSprites;
+ Sprite *_sprites;
+ Cycle *_cycles;
DataSprite *_dataSprites;
Common::Array<SCycle> _cycPtrs;
@@ -157,7 +158,7 @@ Common::Array<Object> _objects;
//void getTilePair(uint8 x, uint8 y); // Modifies a struct of the tile number, aboveTile number, and the cell coordinates of the tile
void setHole();
- void drawContents(uint16 vX, uint16 vY, int nS);
+ void drawContents(uint16 vX, uint16 vY);
bool getTilePair(uint8 x, uint8 y, int id);
bool getWideWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id, int spacing);
bool getWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id);
@@ -204,7 +205,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
//void flameNew() does not need to exist, because we create the duplicate SFlame in Level, and the array in immortal.h is not accessable from here
void flameInit();
- void flameDrawAll(uint16 vX, uint16 vY, int nS);
+ void flameDrawAll(uint16 vX, uint16 vY);
bool roomLighted();
void lightTorch(int x, int y);
void flameFreeAll();
@@ -226,7 +227,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
* [Univ.cpp] Functions from Univ.GS
*/
- void univAddSprite(uint16 vX, uint16 vY, int nS, uint16 x, uint16 y, SpriteName n, int img, uint16 p);
+ void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName n, int img, uint16 p);
};
Commit: 89cbe5974d13b70f4729dd23047802903c15221e
https://github.com/scummvm/scummvm/commit/89cbe5974d13b70f4729dd23047802903c15221e
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Initial implementation of superSprites() + adjustment to sprite structs
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/flameSet.cpp
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/room.cpp
engines/immortal/sprite_list.h
engines/immortal/sprites.cpp
engines/immortal/story.h
engines/immortal/univ.cpp
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index 12b339090ba..f2da5503c5f 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -78,7 +78,16 @@ bool Room::cycleAdvance(int c) {
}
int Room::cycleGetFrame(int c) {
- // This originally did some shenanigans in Kernal to get the number, but really it's just this
+ /* The source version of this is facinating. It is basically:
+ * in: cycList, Index
+ * index -> tmp
+ * Load the value of cycPtrs + cycList (returns address of start of cyc)
+ * Add index (returns address of frame in cyc)
+ * Store to the position of the next label
+ * Load a single byte from the value at the address in the label (returns frame value within cyc)
+ * This is essentially self-modifying code, and it saves 2 bytes of DP memory over the traditional
+ * STA DP : LDA (DP)
+ */
debug("%d", _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index]);
return _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index];
}
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index 994147d01aa..c861cb67220 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -45,9 +45,9 @@ void Room::flameFreeAll() {
_numInRoom = 0;
}
-void Room::flameDrawAll(uint16 vX, uint16 vY, int nS) {
+void Room::flameDrawAll(uint16 vX, uint16 vY) {
for (int i = 0; i < _fset.size(); i++) {
- univAddSprite(vX, vY, nS, _fset[i]._x, _fset[i]._y, _cycPtrs[_cycles[_fset[i]._c]._cycList]._sName, cycleGetFrame(_fset[i]._c), 0);
+ univAddSprite(vX, vY, _fset[i]._x, _fset[i]._y, _cycPtrs[_cycles[_fset[i]._c]._cycList]._sName, cycleGetFrame(_fset[i]._c), 0);
if (cycleAdvance(_fset[i]._c) == true) {
cycleFree(_fset[i]._c);
_fset[i]._c = flameGetCyc(&_fset[i], 1);
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index c75105de413..e6b638da1d7 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -171,7 +171,6 @@ Common::Error ImmortalEngine::run() {
_err = Common::kNoError;
while (!shouldQuit()) {
-
/* The game loop runs at 60fps, which is 16 milliseconds per frame.
* This loop keeps that time by getting the time in milliseconds at the start of the loop,
* then again at the end, and the difference between them is the remainder
@@ -187,9 +186,11 @@ Common::Error ImmortalEngine::run() {
userIO();
noNetwork();
pollKeys();
- logic();
+ //logic();
pollKeys();
if (logicFreeze() == 0) {
+ DataSprite *d = &_dataSprites[kFont];
+ superSprite(d, 0xC0, 0x50, 0, 0, _screenBuff, kSuperTop, kMySuperBottom);
drawUniv();
pollKeys();
fixColors();
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 0dcfe60b77a..703b97478e4 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -358,6 +358,9 @@ public:
uint16 _myViewPortX = 0; // Probably mirror of viewportX
uint16 _myViewPortY = 0;
int _lastGauge = 0; // Mirror for player health, used to update health gauge display
+ uint16 _lastBMW = 0; // Mirrors used to determine where bitmap width needs to be re-calculated
+ uint16 _lastY = 0;
+ uint16 _lastPoint = 0;
uint16 _penX = 0; // Basically where in the screen we are currently drawing
uint16 _penY = 0;
uint16 _myUnivPointX = 0;
@@ -581,8 +584,10 @@ GenericSprite _genSprites[6];
void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
// Main
- void superSprite(int s, uint16 x, uint16 y, Image img, int bmw, byte *dst, int sT, int sB);
-
+ void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, int bmw, byte *dst, int superTop, int superBottom);
+ bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, int bmw, int superTop, int superBottom);
+ void spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, int bmw, byte *dst);
+ void spriteNotAligned();
/*
* [Compression.cpp] Functions from Compression.GS
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 044fde879b0..c36e0f0d5bd 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -55,9 +55,6 @@ void ImmortalEngine::drawUniv() {
sortDrawItems(); // Sort said items
drawItems(); // Draw the items over the background
- // To start constructing the screem, we start with the frame as the base
- memcpy(_screenBuff, _window, kScreenSize);
-
/* copyRectToSurface will apply the screenbuffer to the ScummVM surface.
* We want to do 320 bytes per scanline, at location (0,0), with a
* size of 320x200.
@@ -112,11 +109,14 @@ void ImmortalEngine::addSprites() {
// My goodness this routine is gross
int tmpNum = _num2DrawItems;
for (int i = 0; i < kMaxSprites; i++) {
+ // If the sprite is active
if (_sprites[i]._on == 1) {
+ // If sprite X is an odd number???
if ((_sprites[i]._X & 1) != 0) {
debug("not good! BRK");
return;
}
+
int tmpx = (_sprites[i]._X - kMaxSpriteW) - _myViewPortX;
if (tmpx < 0) {
if (tmpx + (kMaxSpriteW * 2) < 0) {
@@ -136,7 +136,8 @@ void ImmortalEngine::addSprites() {
}
DataSprite *tempD = _sprites[i]._dSprite;
- Image *tempImg = &(_sprites[i]._dSprite->_images[_sprites[i]._image]);
+ debug("what sprite is this: %d %d %d", i, _sprites[i]._image, _sprites[i]._dSprite->_images.size());
+ Image *tempImg = &(tempD->_images[0/*_sprites[i]._image*/]);
int sx = ((_sprites[i]._X + tempImg->_deltaX) - tempD->_cenX) - _myViewPortX;
int sy = ((_sprites[i]._Y + tempImg->_deltaY) - tempD->_cenY) - _myViewPortY;
@@ -144,7 +145,7 @@ void ImmortalEngine::addSprites() {
if (sx >= kViewPortW) {
continue;
}
- } else if ((sx + tempImg->_rectX) <= 0) {
+ } else if ((sx + tempImg->_rectW) <= 0) {
continue;
}
@@ -152,7 +153,7 @@ void ImmortalEngine::addSprites() {
if (sy >= kViewPortH) {
continue;
}
- } else if ((sy + tempImg->_rectY) <= 0) {
+ } else if ((sy + tempImg->_rectH) <= 0) {
continue;
}
@@ -285,7 +286,7 @@ void ImmortalEngine::drawItems() {
// If positive, it's a sprite
uint16 x = (_sprites[index]._X - _myViewPortX) + kVSX;
uint16 y = (_sprites[index]._Y - _myViewPortY) + kVSY;
- superSprite(index, x, y, _sprites[index]._dSprite->_images[_sprites[index]._image], kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
+ //superSprite(index, x, y, _sprites[index]._dSprite->_images[0/*_sprites[index]._image*/], kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
}
n++;
} while (n != _num2DrawItems);
@@ -348,7 +349,7 @@ void ImmortalEngine::printChr(char c) {
return;
}
- superSprite(0, x, y, _dataSprites[kFont]._images[(int) c], kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
+ //superSprite(0, x, y, _dataSprites[kFont]._images[(int) c], kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
if ((c == 0x27) || (c == 'T')) {
_penX -= 2; // Why is this done twice??
}
@@ -536,7 +537,7 @@ void ImmortalEngine::initStoryStatic() {
}
void ImmortalEngine::kernalAddSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
- Immortal::Utilities::addSprite(_sprites, _viewPortX, _viewPortY, _numSprites, &_dataSprites[n], img, x, y, p);
+ Utilities::addSprite(_sprites, _viewPortX, _viewPortY, &_numSprites, &_dataSprites[n], img, x, y, p);
}
void ImmortalEngine::clearSprites() {
@@ -644,6 +645,9 @@ void ImmortalEngine::loadWindow() {
// Now that the bitmap is processed and stored in a byte buffer, we can close the file
f.close();
+ // To start constructing the screen, we start with the frame as the base
+ memcpy(_screenBuff, _window, kScreenSize);
+
} else {
// Should probably give an error or something here
debug("oh nose :(");
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
index a66573f2e83..94107bc405b 100644
--- a/engines/immortal/room.cpp
+++ b/engines/immortal/room.cpp
@@ -73,7 +73,7 @@ void Room::getCell(uint16 &x, uint16 &y) {
void Room::setHole() {}
void Room::drawContents(uint16 vX, uint16 vY) {
- //flameDrawAll(vX, vY);
+ flameDrawAll(vX, vY);
//sparkDrawAll();
//bulletDrawAll();
//genSpriteDrawAll();
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index ad0a9af44be..d80c37fa4aa 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -24,18 +24,23 @@
namespace Immortal {
+// We need a few two-dimentional vectors, and writing them out in full each time is tedious
+template<class T> using CArray2D = Common::Array<Common::Array<T>>;
+
struct Image {
- uint16 _deltaX;
- uint16 _deltaY;
- uint16 _rectX;
- uint16 _rectY;
- byte *_bitmap;
+ uint16 _deltaX;
+ uint16 _deltaY;
+ uint16 _rectW;
+ uint16 _rectH;
+Common::Array<uint16> _scanWidth;
+Common::Array<uint16> _deltaPos;
+CArray2D<byte> _bitmap;
};
struct DataSprite {
- uint16 _cenX; // These are the base center positions
- uint16 _cenY;
- uint16 _numImages;
+ uint16 _cenX; // These are the base center positions
+ uint16 _cenY;
+ uint16 _numImages;
Common::Array<Image> _images;
};
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 83bec3c409e..1d471cd63d6 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -21,6 +21,36 @@
#include "immortal/immortal.h"
+/* -- How does image construction work --
+ * One thing to note about this translation, is that the source
+ * has a lot of address related stuff mixed in to it. This is
+ * because 'Super Sprites' could use a screen buffer and sprite
+ * data from anywhere in memory, including locations that cross
+ * bank boundaries. This means that you don't just have
+ * position -> relative position -> screen, you have
+ * position -> relative position -> relative *address* position -> screen
+ * With that out of the way, here's what a sprite is:
+ * A 'Super Sprite' is several layers of structures combined.
+ * This is both more and less complex in the source. It is structurally
+ * less complicated, only being seen as a sprite + frame, and a cycle.
+ * But in reality that comes with complicated indexing and routines
+ * designed just to get relative indexes that are already in memory.
+ * Instead, I have chosen to clean up the structure a little bit,
+ * which in turns makes it slightly more complicated on a top level.
+ * What we end up with, basically looks like this:
+ * Cycle (ram/rom)
+ * |
+ * Sprite (ram)
+ * |
+ * DataSprite (rom)
+ * |
+ * Frame (rom)
+ * |
+ * Scanline (rom)
+ * |
+ * Bitmap (rom)
+ */
+
namespace Immortal {
/*
@@ -58,22 +88,133 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
for (int i = 0; i < numImages; i++) {
Image newImage;
- f->seek(index + (i*2));
+ f->seek(index + (i * 2));
int ptrFrame = f->readUint16LE();
f->seek(ptrFrame);
- newImage._deltaX = f->readUint16LE() << 1; // the ASL might not be required, depending on how I translate the sprite drawing
+ newImage._deltaX = f->readUint16LE() << 1; // the ASL might not be required, depending on whether the data is actually in bytes or pixels <-- this also might not be used in the game anyway? Lol
newImage._deltaY = f->readUint16LE();
- newImage._rectX = f->readUint16LE();
- newImage._rectY = f->readUint16LE();
+ newImage._rectW = f->readUint16LE();
+ newImage._rectH = f->readUint16LE();
+ uint16 next = 0;
+ for (int j = 0; j < newImage._rectH; j++) {
+ next = f->readUint16LE();
+ if (next >= 0x8000) {
+ next = uint16 (0 - next);
+ } else {
+ debug("HUH");
+ }
+ //debug("First after RectH: %04X", next);
+ newImage._deltaPos.push_back(next);
+ next = f->readUint16LE();
+ //debug("Second after RectH: %04X", next);
+ newImage._scanWidth.push_back(next);
+ Common::Array<byte> b;
+ b.resize(newImage._scanWidth[j]);
+ for (int k = 0; k < newImage._scanWidth[j]; k++) {
+ b[k] = f->readByte();
+ //debugN("%02X", b[k]);
+ }
+ //debug("");
+ newImage._bitmap.push_back(b);
+ }
images.push_back(newImage);
- // This is probably where we will get the bitmap when I know how to get it
}
d->_images = images;
}
+bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, int bmw, int superTop, int superBottom) {
+ // This bit is to get the base index into the screen buffer, unless that's already been done, which is _lastPoint
+ if ((pointY != _lastY) || (bmw != _lastBMW)) {
+ _lastBMW = bmw;
+ _lastY = pointY;
+ if (pointY >= 0) {
+ _lastPoint = pointY * bmw;
+ } else {
+ pointY = !(pointY);
+ _lastPoint = pointY * bmw;
+ }
+ }
+
+ pointIndex = _lastPoint;
+
+ // Now we begin clipping, starting with totally offscreen
+ // We do this by checking if the sprite is above the top of the screen, or below the bottom of it
+ if (pointY > superBottom) {
+ return true;
+
+ } else if ((height + pointY) < superTop) {
+ return true;
+
+ // Now we actually clip top/bottom parts
+ } else {
+
+ // Starting with checking if any of the sprite is under the bottom of the screen
+ if ((height + pointY) >= superBottom) {
+ height = superBottom - pointY;
+ }
+
+ // Next we get the difference of overlap from the sprite if it is above the top
+ if ((superTop - pointY) >= 0) {
+ skipY = (superTop - pointY);
+ }
+
+ // The image is clipped, time to move the index to the sprite's first scanline base position
+ pointIndex += (pointX / 2) + dSprite->_images[img]._rectW;
+ }
+ return false;
+}
+
+void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, int bmw, byte *dst) {
+ //debug("draw the sprite");
+
+ //debug("%d, %d, %d", height, skipY, pointIndex);
+
+ byte pixel;
+ int pos;
+ debug("SPRITE START ------");
+ for (int y = skipY; y < height; y++) {
+ for (int x = 0; x < img._scanWidth[y]; x ++) {
+ pos = ((pointIndex + (y * kResH)) - img._deltaPos[y]) + (x * 2);
+ pixel = img._bitmap[y][x];
+ _screenBuff[pos] = (pixel & kMask8High) >> 4;
+ _screenBuff[pos + 1] = pixel & kMask8Low;
+ }
+ }
+ debug("SPRITE END -------");
+}
+
+void ImmortalEngine::spriteNotAligned() {
-void ImmortalEngine::superSprite(int s, uint16 x, uint16 y, Image img, int bmw, byte *dst, int sT, int sB) {}
+}
+
+void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, int bmw, byte *dst, int superTop, int superBottom) {
+ // Main image construction routine
+
+ uint16 cenX = dSprite->_cenX;
+ uint16 cenY = dSprite->_cenY;
+ uint16 dY = dSprite->_images[img]._deltaY;
+ uint16 height = dSprite->_images[img]._rectH;
+
+ uint16 skipY = 0;
+ uint16 pointIndex = 0; // This is screen and screen + 2 in the source
+
+ pointX -= cenX;
+ pointY -= cenY;
+ pointY += dY;
+
+ // Normally I would just make the return from clip be reversed, but the idea is that the return would be 'offscreen == true'
+ if (!(clipSprite(height, pointIndex, skipY, dSprite, pointX, pointY, img, bmw, superTop, superBottom))) {
+ // Alignment is determined by whether the x position of the point is positive or negative
+ if (pointX >= 0x8000) {
+ spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
+
+ } else {
+ spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
+ }
+ }
+
+}
} // namespace Immortal
@@ -94,6 +235,20 @@ void ImmortalEngine::superSprite(int s, uint16 x, uint16 y, Image img, int bmw,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 0fb02e69ac2..eeea16426e5 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -30,9 +30,6 @@
namespace Immortal {
-// We need a few two-dimentional vectors, and writing them out in full each time is tedious
-template<class T> using CArray2D = Common::Array<Common::Array<T>>;
-
// These maximum numbers aren't really needed, because most of these are vectors and have .size()
enum StoryMaxes {
kMaxRooms = 16,
diff --git a/engines/immortal/univ.cpp b/engines/immortal/univ.cpp
index 91629b24253..aef1aa889b6 100644
--- a/engines/immortal/univ.cpp
+++ b/engines/immortal/univ.cpp
@@ -23,8 +23,9 @@
namespace Immortal {
-void Room::univAddSprite(uint16 vX, uint16 vY, int nS, uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
- //Immortal::Utilities::addSprite(_sprites, vX, vY, nS, &_dataSprites[n], img, x, y, p);
+void Room::univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
+ debug("%d %d %d", *_numSprites, n, img);
+ //Utilities::addSprite(_sprites, vX, vY, _numSprites, &_dataSprites[n], img, x, y, p);
}
} // namespace immortal
\ No newline at end of file
Commit: b27988471cda4bb7427e18335ad36ca63b4c9b76
https://github.com/scummvm/scummvm/commit/b27988471cda4bb7427e18335ad36ca63b4c9b76
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Implement utilities::inside() and insideRect()
Changed paths:
engines/immortal/utilities.cpp
engines/immortal/utilities.h
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
index 21ca9e13810..4719a189eb0 100644
--- a/engines/immortal/utilities.cpp
+++ b/engines/immortal/utilities.cpp
@@ -35,10 +35,66 @@ void Utilities::delay8(int j) { // 1/8 jiffies are 7.02ms
g_system->delayMillis(j * 7);
}
-bool Utilities::inside(int x1, int y1, int a, int x2, int y2) {
- return false;
+bool Utilities::inside(uint8 dist, uint8 cenX, uint8 cenY, uint8 pointX, uint8 pointY) {
+ // you can't be within 0 distance of something
+ if (dist == 0) {
+ return false;
+ }
+
+ // we want the negative distance because this is a rectangle all the way around a point
+ uint8 negDist = ((dist ^ 0xFF) + 1) + 1;
+
+ // First is the X, so we get delta X from the points
+ uint8 dX = cenX - pointX;
+ if (dX < 0x80) {
+ // Our point is beyond the other point
+ if (dX >= dist) {
+ // And it is further than point + distance, so it's not useable
+ return false;
+ }
+
+ } else if (dX >= negDist) {
+ // If the negative delta X is *greater* than the negative distance, that means we're not far *enough* in the X
+ return false;
+ }
+
+ // Exact same system here but with the Y positions instead
+ uint8 dY = cenY - pointY;
+ if (dY < 0x80) {
+ if (dY >= dist) {
+ return false;
+ }
+
+ } else if (dY >= negDist) {
+ return false;
+ }
+
+ // If all conditions are met, we are within the distance of the point
+ return true;
}
-bool Utilities::insideRect(int x, int y, int r) {
+
+bool Utilities::insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 pointX, uint8 pointY) {
+ /* Very simple comapred to inside, we simply check
+ * first if width and height are >0, to make sure
+ * the rectangle has a size, and then we see if
+ * the point is between the point X,Y and the
+ * point X,Y + the width,height of the rect.
+ * This is done by grabbing the delta X,Y and
+ * making sure it is not negative.
+ */
+ if ((w | h) == 0) {
+ return false;
+ }
+
+ uint8 dX = pointX - rectX;
+ uint8 dY = pointY - rectY;
+
+ if ((dX < 0x80) && (dX < w)) {
+ if ((dY < 0x80) && (dY < h)) {
+ return true;
+ }
+ }
+
return false;
}
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
index 0c8304f22f2..9ddd1844884 100644
--- a/engines/immortal/utilities.h
+++ b/engines/immortal/utilities.h
@@ -78,8 +78,8 @@ namespace Utilities {
void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
void delay4(int j); // || /4
void delay8(int j); // || /8
-bool inside(int x1, int y1, int a, int x2, int y2);
-bool insideRect(int x, int y, int r);
+bool inside(uint8 dist, uint8 centX, uint8 centY, uint8 pointX, uint8 pointY);
+bool insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 pointX, uint8 pointY);
void addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int *num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p);
}; // namespace Util
Commit: 8d9994ba56a61854af6aff98876683761299e9e8
https://github.com/scummvm/scummvm/commit/8d9994ba56a61854af6aff98876683761299e9e8
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Sprite drawing preliminarily implemented
Changed paths:
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/sprites.cpp
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index e6b638da1d7..fd55add4e92 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -189,8 +189,8 @@ Common::Error ImmortalEngine::run() {
//logic();
pollKeys();
if (logicFreeze() == 0) {
- DataSprite *d = &_dataSprites[kFont];
- superSprite(d, 0xC0, 0x50, 0, 0, _screenBuff, kSuperTop, kMySuperBottom);
+ DataSprite *d = &_dataSprites[kGoblin2];
+ superSprite(d, 20, 0xA0, 4, kVSBMW, _screenBuff, kSuperTop, kMySuperBottom);
drawUniv();
pollKeys();
fixColors();
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 703b97478e4..02b2d5e45de 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -232,7 +232,7 @@ public:
const int kSpriteDY = 32;
const int kVSX = kMaxSpriteW;
const int kVSY = kSpriteDY;
- const int kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
+ const uint16 kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
const int kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
const int kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
const int kMySuperBottom = kVSDY + kViewPortH;
@@ -584,9 +584,9 @@ GenericSprite _genSprites[6];
void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
// Main
- void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, int bmw, byte *dst, int superTop, int superBottom);
- bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, int bmw, int superTop, int superBottom);
- void spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, int bmw, byte *dst);
+ void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, uint16 bmw, byte *dst, int superTop, int superBottom);
+ bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, int superTop, int superBottom);
+ void spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, uint16 bmw, byte *dst);
void spriteNotAligned();
/*
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 1d471cd63d6..45e442eb3f4 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -98,11 +98,6 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
uint16 next = 0;
for (int j = 0; j < newImage._rectH; j++) {
next = f->readUint16LE();
- if (next >= 0x8000) {
- next = uint16 (0 - next);
- } else {
- debug("HUH");
- }
//debug("First after RectH: %04X", next);
newImage._deltaPos.push_back(next);
next = f->readUint16LE();
@@ -123,20 +118,23 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
d->_images = images;
}
-bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, int bmw, int superTop, int superBottom) {
+bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, int superTop, int superBottom) {
// This bit is to get the base index into the screen buffer, unless that's already been done, which is _lastPoint
if ((pointY != _lastY) || (bmw != _lastBMW)) {
_lastBMW = bmw;
_lastY = pointY;
- if (pointY >= 0) {
+ if (pointY < 0x80) {
_lastPoint = pointY * bmw;
} else {
- pointY = !(pointY);
+ pointY = (pointY ^ 0xFF) + 1;
_lastPoint = pointY * bmw;
+ _lastPoint = 0 - _lastPoint;
}
}
pointIndex = _lastPoint;
+ return false;
+
// Now we begin clipping, starting with totally offscreen
// We do this by checking if the sprite is above the top of the screen, or below the bottom of it
@@ -155,7 +153,7 @@ bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skip
}
// Next we get the difference of overlap from the sprite if it is above the top
- if ((superTop - pointY) >= 0) {
+ if ((superTop - pointY) < 0x8000) {
skipY = (superTop - pointY);
}
@@ -165,20 +163,37 @@ bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skip
return false;
}
-void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, int bmw, byte *dst) {
+void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, uint16 bmw, byte *dst) {
//debug("draw the sprite");
- //debug("%d, %d, %d", height, skipY, pointIndex);
+ debug("%d, %d, %04X", height, skipY, pointIndex);
byte pixel;
- int pos;
+ // For debug currently, align to the word by default
+ pointIndex &= 0xFFFE;
+
+ // Position is weird
+ pointIndex += 50;
+
debug("SPRITE START ------");
- for (int y = skipY; y < height; y++) {
- for (int x = 0; x < img._scanWidth[y]; x ++) {
- pos = ((pointIndex + (y * kResH)) - img._deltaPos[y]) + (x * 2);
- pixel = img._bitmap[y][x];
- _screenBuff[pos] = (pixel & kMask8High) >> 4;
- _screenBuff[pos + 1] = pixel & kMask8Low;
+ for (int y = 0; y < height; y++, pointIndex += (bmw * 2)) {
+
+ //debug("%04X, %04X ", pointIndex, img._deltaPos[y]);
+
+ if (img._deltaPos[y] < 0x8000) {
+ pointIndex += (img._deltaPos[y] * 2);
+ }
+ else {
+ pointIndex -= ((0 - img._deltaPos[y]) * 2);
+ }
+
+ for (int x = 0; x < img._scanWidth[y]; x++, pointIndex += 2) {
+
+ //if (y > skipY) {
+ pixel = img._bitmap[y][x];
+ _screenBuff[pointIndex] = (pixel & kMask8High) >> 4;
+ _screenBuff[pointIndex + 1] = pixel & kMask8Low;
+ //}
}
}
debug("SPRITE END -------");
@@ -188,7 +203,7 @@ void ImmortalEngine::spriteNotAligned() {
}
-void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, int bmw, byte *dst, int superTop, int superBottom) {
+void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, int superTop, int superBottom) {
// Main image construction routine
uint16 cenX = dSprite->_cenX;
Commit: d8944244a1ea9610544a5cfb58b8b7cda946f132
https://github.com/scummvm/scummvm/commit/d8944244a1ea9610544a5cfb58b8b7cda946f132
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Implement sprite drawing through superSprites()
Changed paths:
engines/immortal/immortal.h
engines/immortal/sprites.cpp
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 02b2d5e45de..fe20b7cfa18 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -189,21 +189,21 @@ public:
const int kScreenH__ = 128; // ???
const int kViewPortW = 256;
const int kViewPortH = 128;
- const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
- const int kScreenLeft = 32;
- const int kScreenTop = 20;
+ const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is (320x200) * 2 byte words
+ const uint16 kScreenLeft = 32;
+ const uint16 kScreenTop = 20;
const int kTextLeft = 8;
const int kTextTop = 4;
const int kGaugeX = 0;
const int kGaugeY = -13; // ???
- const int kScreenBMW = 160; // Screen BitMap Width?
+ const uint16 kScreenBMW = 160; // Screen BitMap Width?
const uint16 kChrW = 64;
const uint16 kChrH = 32;
const uint16 kChrH2 = kChrH * 2;
const uint16 kChrH3 = kChrH * 3;
- const int kChrLen = (kChrW / 2) * kChrH;
- const int kChrBMW = kChrW / 2;
- const int kLCutaway = 4;
+ const uint16 kChrLen = (kChrW / 2) * kChrH;
+ const uint16 kChrBMW = kChrW / 2;
+ const uint16 kLCutaway = 4;
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
kChrH2, kChrH, kChrH2, kChrH2, kChr0,
@@ -227,22 +227,22 @@ public:
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
- const int kMaxSpriteW = 64;
- const int kMaxSpriteH = 64;
- const int kSpriteDY = 32;
- const int kVSX = kMaxSpriteW;
- const int kVSY = kSpriteDY;
+ const uint16 kMaxSpriteW = 64;
+ const uint16 kMaxSpriteH = 64;
+ const uint16 kSpriteDY = 32;
+ const uint16 kVSX = kMaxSpriteW;
+ const uint16 kVSY = kSpriteDY;
const uint16 kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
- const int kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
- const int kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
- const int kMySuperBottom = kVSDY + kViewPortH;
- const int kSuperBottom = 200;
- const int kMySuperTop = kVSDY;
- const int kSuperTop = 0;
- const int kViewPortSpX = 32;
- const int kViewPortSpY = 0;
- const int kWizardX = 28; // Common sprite center for some reason
- const int kWizardY = 37;
+ const uint16 kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
+ const uint16 kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
+ const uint16 kMySuperBottom = kVSDY + kViewPortH;
+ const uint16 kSuperBottom = 200;
+ const uint16 kMySuperTop = kVSDY;
+ const uint16 kSuperTop = 0;
+ const uint16 kViewPortSpX = 32;
+ const uint16 kViewPortSpY = 0;
+ const uint16 kWizardX = 28; // Common sprite center for some reason
+ const uint16 kWizardY = 37;
// Asset constants
const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
@@ -409,6 +409,7 @@ GenericSprite _genSprites[6];
void addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
void sortDrawItems(); // Sort said items
void drawItems(); // Draw the items over the background
+ void setPen(uint16 penX, uint16 penY); // Sets the 'pen' x and y positions, including making y negative if above a certain point
// Music
void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
@@ -584,10 +585,9 @@ GenericSprite _genSprites[6];
void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
// Main
- void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, uint16 bmw, byte *dst, int superTop, int superBottom);
- bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, int superTop, int superBottom);
+ void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom);
+ bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, uint16 superTop, uint16 superBottom);
void spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, uint16 bmw, byte *dst);
- void spriteNotAligned();
/*
* [Compression.cpp] Functions from Compression.GS
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 45e442eb3f4..3a982a91699 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -76,7 +76,7 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
uint16 numImages = f->readUint16LE();
d->_numImages = numImages;
- //debug("Number of Frames: %d", numFrames);
+ //debug("Number of Frames: %d", numImages);
// Only here for dragon, but just in case, it's a high number so it should catch others
if (numImages >= 0x0200) {
@@ -118,92 +118,110 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
d->_images = images;
}
-bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, int superTop, int superBottom) {
+bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, uint16 superTop, uint16 superBottom) {
+ /* Something important to note here:
+ * In the source, bmw is not *2, and pointX is /2. However, the source
+ * was using a buffer of 2 pixels per byte. In ScummVM, the screen buffer
+ * is 1 pixel per byte. This means some calculations are slightly different.
+ */
+
// This bit is to get the base index into the screen buffer, unless that's already been done, which is _lastPoint
if ((pointY != _lastY) || (bmw != _lastBMW)) {
_lastBMW = bmw;
_lastY = pointY;
- if (pointY < 0x80) {
- _lastPoint = pointY * bmw;
+ if (pointY < kMaskNeg) {
+ // The source does not double the bmw here to get the bytes, why not?
+ _lastPoint = pointY * (bmw * 2);
} else {
- pointY = (pointY ^ 0xFF) + 1;
- _lastPoint = pointY * bmw;
+ // Screen wrapping??
+ uint16 temp = (0 - pointY) + 1;
+ _lastPoint = temp * bmw;
_lastPoint = 0 - _lastPoint;
}
}
pointIndex = _lastPoint;
- return false;
-
// Now we begin clipping, starting with totally offscreen
- // We do this by checking if the sprite is above the top of the screen, or below the bottom of it
if (pointY > superBottom) {
return true;
- } else if ((height + pointY) < superTop) {
+ } else if ((pointY + height) < superTop) {
return true;
- // Now we actually clip top/bottom parts
+ /* The actual clipping is pretty simple:
+ * Lower height = stop drawing the sprite early. Higher SkipY = start drawing the sprite late
+ * So we just determine the delta for each based on superTop and superBottom
+ */
} else {
// Starting with checking if any of the sprite is under the bottom of the screen
- if ((height + pointY) >= superBottom) {
+ if ((pointY + height) >= superBottom) {
height = superBottom - pointY;
}
// Next we get the difference of overlap from the sprite if it is above the top
- if ((superTop - pointY) < 0x8000) {
+ if (uint16((superTop - pointY)) < kMaskNeg) {
skipY = (superTop - pointY);
}
// The image is clipped, time to move the index to the sprite's first scanline base position
- pointIndex += (pointX / 2) + dSprite->_images[img]._rectW;
+ pointIndex += (pointX) + dSprite->_images[img]._rectW;
}
return false;
}
void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, uint16 bmw, byte *dst) {
- //debug("draw the sprite");
-
- debug("%d, %d, %04X", height, skipY, pointIndex);
-
- byte pixel;
- // For debug currently, align to the word by default
- pointIndex &= 0xFFFE;
-
- // Position is weird
- pointIndex += 50;
-
- debug("SPRITE START ------");
+ /* This is an approximation of the sprite drawing system in the source.
+ * It is an approximation because the source needed to do some things
+ * that aren't relevant anymore, and it had some....creative solutions.
+ * For example, transparency was handled with a 256 byte table of masks
+ * that was indexed by the pixel itself, and used to find what nyble needed
+ * to be masked. However we are using a slightly different kind of screen buffer,
+ * and so I chose a more traditional method. Likewise, alignement was
+ * relevant for the source, but is not relevant here (thankfully, considering
+ * how confusing sprite drawing is when not an even position).
+ */
+ byte pixel1 = 0;
+ byte pixel2 = 0;
+
+ // For every scanline before height
for (int y = 0; y < height; y++, pointIndex += (bmw * 2)) {
- //debug("%04X, %04X ", pointIndex, img._deltaPos[y]);
-
- if (img._deltaPos[y] < 0x8000) {
+ // We increase the position by one screen width
+ if (img._deltaPos[y] < kMaskNeg) {
pointIndex += (img._deltaPos[y] * 2);
}
+
+ // And if the delta X for the line is positive, we add it. If negative we subtract
else {
pointIndex -= ((0 - img._deltaPos[y]) * 2);
}
+ // For every pixel in the scanline
for (int x = 0; x < img._scanWidth[y]; x++, pointIndex += 2) {
+ // SkipY defines the lines we don't draw because they are clipped
+ if (y >= skipY) {
+
+ // For handling transparency, I chose to simply check if the pixel is 0,
+ // as that is the transparent colour
+ pixel1 = (img._bitmap[y][x] & kMask8High) >> 4;
+ pixel2 = (img._bitmap[y][x] & kMask8Low);
+
+ if (pixel1 != 0) {
+ _screenBuff[pointIndex] = pixel1;
+ }
+
+ if (pixel2 != 0) {
+ _screenBuff[pointIndex + 1] = pixel2;
+ }
- //if (y > skipY) {
- pixel = img._bitmap[y][x];
- _screenBuff[pointIndex] = (pixel & kMask8High) >> 4;
- _screenBuff[pointIndex + 1] = pixel & kMask8Low;
- //}
+ }
}
}
- debug("SPRITE END -------");
}
-void ImmortalEngine::spriteNotAligned() {
-
-}
-
-void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, int superTop, int superBottom) {
+void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom) {
// Main image construction routine
uint16 cenX = dSprite->_cenX;
@@ -220,13 +238,9 @@ void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 poin
// Normally I would just make the return from clip be reversed, but the idea is that the return would be 'offscreen == true'
if (!(clipSprite(height, pointIndex, skipY, dSprite, pointX, pointY, img, bmw, superTop, superBottom))) {
- // Alignment is determined by whether the x position of the point is positive or negative
- if (pointX >= 0x8000) {
- spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
- } else {
- spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
- }
+ // Alignment was a factor in the assembly because it was essentially 2 pixels per byte. However ScummVM is 1 pixel per byte
+ spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
}
}
Commit: 0467dbdb78162944f178ceebff169b9b4936b67d
https://github.com/scummvm/scummvm/commit/0467dbdb78162944f178ceebff169b9b4936b67d
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Enable sprite drawing in printChr() and drawGauge()
Changed paths:
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index c36e0f0d5bd..9344ac283cf 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -48,6 +48,9 @@ void ImmortalEngine::drawUniv() {
_myUnivPointX = !(_myViewPortX & (kChrW - 1)) + kViewPortSpX;
_myUnivPointY = !(_myViewPortY & (kChrH - 1)) + kViewPortSpY;
+ // To start constructing the screen, we start with the frame as the base
+ memcpy(_screenBuff, _window, kScreenSize);
+
makeMyCNM();
drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
addRows(); // Add rows to drawitem array
@@ -286,7 +289,7 @@ void ImmortalEngine::drawItems() {
// If positive, it's a sprite
uint16 x = (_sprites[index]._X - _myViewPortX) + kVSX;
uint16 y = (_sprites[index]._Y - _myViewPortY) + kVSY;
- //superSprite(index, x, y, _sprites[index]._dSprite->_images[0/*_sprites[index]._image*/], kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
+ superSprite(_sprites[index]._dSprite, x, y, _sprites[index]._image, kVSBMW, _screenBuff, kMySuperTop, kMySuperBottom);
}
n++;
} while (n != _num2DrawItems);
@@ -349,7 +352,7 @@ void ImmortalEngine::printChr(char c) {
return;
}
- //superSprite(0, x, y, _dataSprites[kFont]._images[(int) c], kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
+ superSprite(&_dataSprites[kFont], x, y, (int) c, kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
if ((c == 0x27) || (c == 'T')) {
_penX -= 2; // Why is this done twice??
}
@@ -645,9 +648,6 @@ void ImmortalEngine::loadWindow() {
// Now that the bitmap is processed and stored in a byte buffer, we can close the file
f.close();
- // To start constructing the screen, we start with the frame as the base
- memcpy(_screenBuff, _window, kScreenSize);
-
} else {
// Should probably give an error or something here
debug("oh nose :(");
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 2379914705e..97319375a06 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -201,6 +201,18 @@ void ImmortalEngine::doSingleStep() {
}
}
+void ImmortalEngine::setPen(uint16 penX, uint16 penY) {
+ _penX = penX & kMaskLow;
+ // Is this screen wrap?
+ if ((penY & kMaskLow) < 200) {
+ _penY = penY & kMaskLow;
+ }
+
+ else {
+ _penY = penY | kMaskHigh;
+ }
+}
+
void ImmortalEngine::updateHitGauge() {
/* This HUD (essentially) drawing routine is a bit weird because
* the game was originally meant to have multiple player characters
@@ -211,7 +223,7 @@ void ImmortalEngine::updateHitGauge() {
* probably just check a global 'health' variable instead.
*/
//int hits = _rooms[_currentRoom]._monsters[kPlayerID]._getHits();
- int hits = 0;
+ int hits = 15;
if (hits != _lastGauge) {
// Update the mirror value if the health has changed since last frame
_lastGauge = hits;
@@ -237,8 +249,9 @@ void ImmortalEngine::drawGauge(int h) {
* the index of the chr for the other icons.
*/
int r = 16 - h;
- _penX = kGaugeX;
- _penY = kGaugeY;
+ setPen(kGaugeX, kGaugeY);
+ // Temporary x value, until it's clear why printchr is designed to add 8 pixels *before* drawing the char
+ _penX = 0xFFF0;
h--;
if (h >= 0) {
// This could be written as a regular for loop, but the game thinks of start/stop as different from on
@@ -248,7 +261,6 @@ void ImmortalEngine::drawGauge(int h) {
if (h == 0) {
// Redundant code is redundant
printChr(kGaugeStop);
-
} else {
printChr(kGaugeOn);
}
Commit: a2bd437c5e10e0d1fa19e86347d0725c3bcb7da7
https://github.com/scummvm/scummvm/commit/a2bd437c5e10e0d1fa19e86347d0725c3bcb7da7
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: addSprite() moved out of utilities
Changed paths:
engines/immortal/utilities.cpp
engines/immortal/utilities.h
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
index 4719a189eb0..50957dede5e 100644
--- a/engines/immortal/utilities.cpp
+++ b/engines/immortal/utilities.cpp
@@ -23,6 +23,14 @@
namespace Immortal {
+/*
+ *
+ * ----- -----
+ * ----- General Use -----
+ * ----- -----
+ *
+ */
+
void Utilities::delay(int j) { // Delay is measured in jiffies, which are 56.17ms
g_system->delayMillis(j * 56);
}
@@ -98,35 +106,4 @@ bool Utilities::insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 poi
return false;
}
-void Utilities::addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int *num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p) {
- debug("adding sprite...");
- if (*num != kMaxSprites) {
- if (x >= (kResH + kMaxSpriteLeft)) {
- x |= kMaskHigh; // Make it negative
- }
-
- sprites[*num]._X = (x << 1) + vpX;
-
- if (y >= (kMaxSpriteAbove + kResV)) {
- y |= kMaskHigh;
- }
-
- sprites[*num]._Y = (y << 1) + vpY;
-
- if (p >= 0x80) {
- p |= kMaskHigh;
- }
-
- sprites[*num]._priority = ((p + y) ^ 0xFFFF) + 1;
-
- sprites[*num]._image = img;
- sprites[*num]._dSprite = d;
- sprites[*num]._on = 1;
- *num += 1;
- debug("sprite added");
- } else {
- debug("Max sprites reached beeeeeep!!");
- }
-}
-
}; // namespace Immortal
\ No newline at end of file
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
index 9ddd1844884..c40904d8aa6 100644
--- a/engines/immortal/utilities.h
+++ b/engines/immortal/utilities.h
@@ -25,7 +25,9 @@
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/system.h"
+
#include "immortal/sprite_list.h"
+#include "immortal/definitions.h"
namespace Immortal {
@@ -75,12 +77,12 @@ enum Screen { // These are constants t
namespace Utilities {
+// Other
void delay(int j); // Delay engine by j jiffies (from driver originally, but makes more sense grouped with misc)
void delay4(int j); // || /4
void delay8(int j); // || /8
bool inside(uint8 dist, uint8 centX, uint8 centY, uint8 pointX, uint8 pointY);
bool insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 pointX, uint8 pointY);
-void addSprite(Sprite *sprites, uint16 vpX, uint16 vpY, int *num, DataSprite *d, int img, uint16 x, uint16 y, uint16 p);
}; // namespace Util
Commit: cdf322b93a71883b5ddb03ffa299184733d09d13
https://github.com/scummvm/scummvm/commit/cdf322b93a71883b5ddb03ffa299184733d09d13
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: BMW is assumed to be in bytes and is converted to pixels for drawing
Changed paths:
engines/immortal/sprites.cpp
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 3a982a91699..31969a8e0a1 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -98,18 +98,14 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
uint16 next = 0;
for (int j = 0; j < newImage._rectH; j++) {
next = f->readUint16LE();
- //debug("First after RectH: %04X", next);
newImage._deltaPos.push_back(next);
next = f->readUint16LE();
- //debug("Second after RectH: %04X", next);
newImage._scanWidth.push_back(next);
Common::Array<byte> b;
b.resize(newImage._scanWidth[j]);
for (int k = 0; k < newImage._scanWidth[j]; k++) {
b[k] = f->readByte();
- //debugN("%02X", b[k]);
}
- //debug("");
newImage._bitmap.push_back(b);
}
images.push_back(newImage);
@@ -130,8 +126,8 @@ bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skip
_lastBMW = bmw;
_lastY = pointY;
if (pointY < kMaskNeg) {
- // The source does not double the bmw here to get the bytes, why not?
- _lastPoint = pointY * (bmw * 2);
+ // For the Apple IIGS, pointY in pixels needed to be converted to bytes. For us, it's the other way around, we need bmw in pixels
+ _lastPoint = pointY * (bmw);
} else {
// Screen wrapping??
uint16 temp = (0 - pointY) + 1;
@@ -166,7 +162,7 @@ bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skip
}
// The image is clipped, time to move the index to the sprite's first scanline base position
- pointIndex += (pointX) + dSprite->_images[img]._rectW;
+ pointIndex += pointX;// + dSprite->_images[img]._rectW;
}
return false;
}
@@ -186,7 +182,7 @@ void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skip
byte pixel2 = 0;
// For every scanline before height
- for (int y = 0; y < height; y++, pointIndex += (bmw * 2)) {
+ for (int y = 0; y < height; y++, pointIndex += (bmw)) {
// We increase the position by one screen width
if (img._deltaPos[y] < kMaskNeg) {
@@ -224,6 +220,9 @@ void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skip
void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom) {
// Main image construction routine
+ // For the Apple IIGS, the bmw is in bytes, but for us it needs to be the reverse, in pixels
+ bmw <<= 1;
+
uint16 cenX = dSprite->_cenX;
uint16 cenY = dSprite->_cenY;
uint16 dY = dSprite->_images[img]._deltaY;
Commit: a3697c3bd52f6799dec458b6ef72de164ede616d
https://github.com/scummvm/scummvm/commit/a3697c3bd52f6799dec458b6ef72de164ede616d
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: univAddSprite() calls addSprite() through g_immortal instead of Utilities
Changed paths:
engines/immortal/univ.cpp
diff --git a/engines/immortal/univ.cpp b/engines/immortal/univ.cpp
index aef1aa889b6..33a8861c846 100644
--- a/engines/immortal/univ.cpp
+++ b/engines/immortal/univ.cpp
@@ -23,9 +23,9 @@
namespace Immortal {
-void Room::univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
- debug("%d %d %d", *_numSprites, n, img);
- //Utilities::addSprite(_sprites, vX, vY, _numSprites, &_dataSprites[n], img, x, y, p);
+void Room::univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s, int img, uint16 p) {
+ //debug("%d %d %d", *_numSprites, n, img);
+ //g_immortal->addSprite(vX, vY, s, img, x, y, p);
}
} // namespace immortal
\ No newline at end of file
Commit: c996c64b8c6fb578fbe6cf8991aeb6d7f064fba4
https://github.com/scummvm/scummvm/commit/c996c64b8c6fb578fbe6cf8991aeb6d7f064fba4
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: uint8 -> uint16 for several variables + initStoryStatic moved to story.cpp
Changed paths:
engines/immortal/story.cpp
engines/immortal/story.h
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index e7c1424e45a..51dd0443784 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -47,6 +47,180 @@
namespace Immortal {
+void ImmortalEngine::initStoryStatic() {
+ Common::Array<Common::String> s{"#" + Common::String(kSwordBigFrame) + "sword@",
+ "You find an Elven sword of&agility. Take it?@",
+ "Search the bones?%",
+ "}The sword permanently endows you with Elven agility and quickness in combat.@",
+ "}You notice something that looks wet and green under the pile. Search further?%",
+ "#" + Common::String(kBagBigFrame) + " dust@",
+ "}You find a bag containing Dust of Complaisance.&@",
+ "}Drop the bait on the ground here?%",
+ "}To use this dust, you throw it in the air. Do that here?%",
+ "_}Don+t bother me, I+m cutting a gem. Yes, you need it. No, you can+t have it. I wouldn+t give it to anyone, least of all you. Go away. ]]]]=",
+ "_}Let me help you. Please take this gem. No, really, I insist. Take it and go with my blessings. Good luck. ]]]]=",
+ "#" + Common::String(kCarpetBigFrame) + "carpet@",
+ "#" + Common::String(kBombBigFrame) + " bomb@",
+ "A gas bomb that goblins&use to paralyze trolls.&@",
+ "Take it?<>@",
+ "%",
+ " other@",
+ "#" + Common::String(kKeyBigFrame) + " key@",
+ "#" + Common::String(kKeyBigFrame) + " key@",
+ "A key to a chest.&@",
+ "The chest is open. Examine&contents?%",
+ "Put it on?%",
+ "Drop it?%",
+ "It+s unlocked. Open it?%",
+ "It+s locked but you have&the key. Open it?%",
+ "It+s locked and you don+t&have the key.@",
+ "The lock, triggered by a&complicated set of latches,&is unfamiliar to you.@",
+ "#" + Common::String(kGoldBigFrame) + "$0 gold@",
+ "You find $0 gold pieces.&&^#" + Common::String(kPileFrame) + "@",
+ "@",
+ "You can+t plant them on&stone tiles.@",
+ "It+s locked but you are&able to unlock it with&the key.@",
+ "_}The king is not dead, but the poison is taking effect. When he sees you, he attempts to speak:[(Give me water... the fountain... I give you... information... peace...+[Give him water?%",
+ "_}You dont have any water to give him. He mumbles something. Then silence... You find a key on his body.]]]]=",
+ "_}He mumbles something. Then silence... You find a key on his body.]]]]=",
+ "_}I+ll tell you how to... next level... past slime... three jewels... slime... rock becomes... floor... right, left, center of the... [Then silence. His hand opens, releasing a key.]]]]=",
+ "You find a door key.&@",
+ "You find a note.&@",
+ "#" + Common::String(kNoteBigFrame) + "note@",
+ "He+s dead.&Look for possessions?%",
+ "You don+t have it. Check&your inventory.@",
+ "Game Over&&Play again?@",
+ "Congratulations!&&Play again?@",
+ "You find a bag of bait.&@",
+ "#" + Common::String(kBagBigFrame) + " bait@",
+ "You find a stone. @",
+ "#" + Common::String(kStoneBigFrame) + " stone@",
+ "You find a red gem.&@",
+ "#" + Common::String(kGemBigFrame) + " gem@",
+ "You find a scroll with&fireball spells.&@",
+ "#" + Common::String(kScrollBigFrame) + "$ shots@",
+ "You find a map warning&you about pit traps.&@",
+ "#" + Common::String(kMapBigFrame) + " map@",
+ "#" + Common::String(kVaseBigFrame) + " oil@",
+ "You apply the oil but notice&as you walk that the leather&is drying out quickly.@",
+ "}You discover a scroll with a charm spell to use on will o+ the wisps.&@",
+ "#" + Common::String(kScrollBigFrame) + " charm@",
+ "}This charms the will o+ the wisps to follow you. Read the spell again to turn them against your enemies.@",
+ "}It looks like water. Drink it?%",
+ "Drink it?%",
+ "}It works! You are much stronger.]]]=",
+ "}It looks like it has green stuff inside. Open it?%",
+ "Now this will take&effect when you press the&fire button.@",
+ "You find a potion,&Magic Muscle.&@",
+ "#" + Common::String(kVaseBigFrame) + " potion@",
+ "You find a bottle.&@",
+ "#" + Common::String(kVaseBigFrame) + " bottle@",
+ "#" + Common::String(kRingBigFrame) + "Protean@",
+ "You find a Protean Ring.&@",
+ "You find a troll ritual knife,&used to declare a fight to&the death. @",
+ "#" + Common::String(kKnifeBigFrame) + " knife@",
+ "_}It is a fine woman+s garment. Folded inside is a ring with the words,[`To Ana, so harm will never find you. -Your loving father, Dunric.+&@",
+ "You find a small, well&crafted ring. @",
+ "#" + Common::String(kRingBigFrame) + " gift@",
+ "#" + Common::String(kRingBigFrame) + " Ana+s@",
+ "_}She is hurt and upset when she finds you don+t have her ring or won+t give it to her. She scurries back into the hole. The hole is too small for you to follow.&@",
+ "_}`Sir, can you help me,+ the girl pleads. `I was kidnapped and dragged down here. All the man would say is `Mordamir+s orders.+[I ~" + Common::String(kStrGive2),
+ "escaped using a ring my father gave me, but now I+ve lost it. Did you find it?+%",
+ "_}We have met before, old man. Do you remember? Because you helped me, you may pass. But I warn you, we are at war with the trolls.[Over this ladder, across the spikes, is troll territory. Very dangerous.@",
+ "_}You are an impostor!]]]]=",
+ "_}Old man, do you remember me? I am king of the goblins. You didn+t give me the water. You left me to die after you took the key from me. Now you will pay.]]]]=",
+ "_}You quickly fall into a deep, healing sleep...[Vivid images of a beautiful enchanted city pass by. All the city people are young and glowing. Fountains fill the city, and the splash and ~" + Common::String(kStrDream1P2),
+ "sparkle of water is everywhere...[Suddenly the images go black. A face appears... Mordamir!]][ ~" + Common::String(kStrDream1P3),
+ "He is different from how you remember him. His gentle features are now withered. His kind eyes, now cold and sunken, seem to look through you with a dark, penetrating stare. You wake rejuvenated, but disturbed.]]]]]=",
+ "_}Here, take this ring in return. [I don+t know if it will help, but I heard the unpleasant little dwarf say, (Clockwise, three rings around the triangle.+[Could that be a clue to his exit puzzle? I must go. Goodbye.]]]]=",
+ "#" + Common::String(kSackBigFrame) + " spores@",
+ "You find a sack of bad&smelling spores.&@",
+ "Please insert play disk.@",
+ "New game?%",
+ "Enter certificate:&-=",
+ "Invalid certificate.@",
+ "End of level!&Here is your certificate:&&=",
+ "&@",
+ "\\ Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]\\]=",
+ " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
+ "_}Greetings, friend! Come, I+ve got something you need. These parts are plagued with slime.[You can+t venture safely without my slime oil for boots, a bargain at only 80 gold pieces.%",
+ "_}All right, 60 gold pieces for my oil. Rub it on your boots and slime won+t touch you. 60, friend.%",
+ "This room doesn+t resemble&any part of the map.@",
+ "This room resembles part&of the map.@"};
+ _strPtrs = s;
+
+ // Scope, amirite?
+ Common::Array<int> cyc0{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
+ Common::Array<int> cyc1{15,16,17,18,19,20,21,22,-1};
+ Common::Array<int> cyc2{0,1,2,-1};
+ Common::Array<int> cyc3{3,4,5,-1};
+ Common::Array<int> cyc4{6,7,8,9,10,-1};
+ Common::Array<int> cyc5{11,12,13,14,15,-1};
+ Common::Array<int> cyc6{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,-1};
+ Common::Array<int> cyc7{0,1,2,3,4,-1};
+ Common::Array<int> cyc8{5,1+5,2+5,3+5,4+5,-1};
+ Common::Array<int> cyc9{10,1+10,2+10,3+10,4+10,-1};
+ Common::Array<int> cyc10{15,1+15,2+15,3+15,4+15,-1};
+ Common::Array<int> cyc11{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
+ Common::Array<int> cyc12{0,1,2,3,4,5,6,7,8,9,-1};
+ Common::Array<int> cyc13{0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, -1};
+ Common::Array<int> cyc14{31,32,33,32, 34,35,36,35, 37,38,39,38, 40,41,42,41, 43,44,45,44, 46,47,48,47, 49,50,51,50, 52,53,54,53, -1};
+ Common::Array<int> cyc15{55, -1};
+ Common::Array<int> cyc16{63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66,-1};
+ Common::Array<int> cyc17{0,1,0,-1};
+ Common::Array<int> cyc18{0,1,2,4,5,6,7,8,9,10,11,12,2,1,-1};
+ Common::Array<int> cyc19{0,0,1,2,13,14,15,16,4,2,3,-1};
+ Common::Array<int> cyc20{0,1,2,3,20,21,22,23,24,25,26,27,5,4,3,-1};
+ Common::Array<int> cyc21{0,1,2,3,-1};
+ Common::Array<int> cyc22{0,17,18,19,3,-1};
+ Common::Array<int> cyc23{0,1,-1};
+ Common::Array<int> cyc24{28,28,28,28,-1};
+ Common::Array<int> cyc25{15,16,15,16,15,1+15,1+15,-1};
+ Common::Array<int> cyc26{10+15,11+15,12+15,13+15,14+15,15+15,16+15,-1};
+ Common::Array<int> cyc27{2+15,3+15,4+15,5+15,-1};
+ Common::Array<int> cyc28{6+15,7+15,8+15,9+15,-1};
+ Common::Array<int> cyc29{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
+ Common::Array<int> cyc30{0,1,2,3,3,3,3,4,5,6,-1};
+ Common::Array<int> cyc31{0,1,2,3,4,5,6,7,8,-1};
+
+ Common::Array<SCycle> c{SCycle(kBubble, false, cyc0), SCycle(kBubble, false, cyc1),
+ SCycle(kSpark, false, cyc2), SCycle(kSpark, false, cyc3),
+ SCycle(kSpark, false, cyc4), SCycle(kSpark, false, cyc5), SCycle(kSpark, false, cyc6),
+ SCycle(kPipe, false, cyc7), SCycle(kPipe, false, cyc8),
+ SCycle(kPipe, false, cyc9), SCycle(kPipe, false, cyc10),
+ SCycle(kAnaVanish, false, cyc11), SCycle(kAnaGlimpse, false, cyc12),
+ SCycle(kKnife, true, cyc13),
+ SCycle(kSpark, true, cyc14), SCycle(kSpark, true, cyc15), SCycle(kSpark, true, cyc16),
+ SCycle(kBigBurst, false, cyc17),
+ SCycle(kFlame, false, cyc18), SCycle(kFlame, false, cyc19), SCycle(kFlame, false, cyc20),
+ SCycle(kFlame, false, cyc21), SCycle(kFlame, false, cyc22), SCycle(kFlame, false, cyc23),
+ SCycle(kFlame, false, cyc24),
+ SCycle(kCandle, false, cyc25), SCycle(kCandle, false, cyc26), SCycle(kCandle, false, cyc27),
+ SCycle(kCandle, false, cyc28), SCycle(kCandle, false, cyc29),
+ SCycle(kSink, false, cyc30),
+ SCycle(kNorlacDown, false, cyc31)};
+ _cycPtrs = c;
+
+ Common::Array<Motive> m{};
+ _motivePtrs = m;
+
+ Common::Array<Damage> d{};
+ _damagePtrs = d;
+
+ Common::Array<Use> u{};
+ _usePtrs = u;
+
+ Common::Array<Pickup> p{};
+ _pickupPtrs = p;
+
+ CArray2D<Motive> pr{};
+ _programPtrs = pr;
+
+ Common::Array<ObjType> o{};
+ _objTypePtrs = o;
+
+}
+
void ImmortalEngine::initStoryDynamic() {
/* There is one major difference between the source logic and this method.
* It doesn't change the game logic, but it does change the logic of storing
@@ -69,7 +243,7 @@ void ImmortalEngine::initStoryDynamic() {
// *NOTE* the data types Trap and Program will be in the static Story area, and referenced by an enum
- const uint8 kZip = 5;
+ const uint16 kZip = 5;
/*
* ::: Level 0: Intro 1 :::
@@ -79,8 +253,8 @@ void ImmortalEngine::initStoryDynamic() {
* including spawn point and entry/exit points
*/
int univRoom = 4; // The room the player starts in when beginning this level
- uint8 univRoomX = 512;
- uint8 univRoomY = 416;
+ uint16 univRoomX = 512;
+ uint16 univRoomY = 416;
_stories[0]._level = 0;
_stories[0]._part = 1;
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index eeea16426e5..4b369dbeb3f 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -118,8 +118,8 @@ struct Use {
};
struct ObjType {
- Str _str = kStrNoDesc;
- Str _desc = kStrNoDesc;
+ Str _str = kStrNull;
+ Str _desc = kStrNull;
int _size = 0;
Pickup _pickup;
Use _use;
@@ -150,11 +150,11 @@ Common::Array<int> _frames;
};
struct SRoom {
- uint8 _x = 0;
- uint8 _y = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
RoomFlag _flags = kRoomFlag0;
SRoom() {}
- SRoom(uint8 x, uint8 y, RoomFlag f) {
+ SRoom(uint16 x, uint16 y, RoomFlag f) {
_x = x;
_y = y;
_flags = f;
@@ -163,13 +163,13 @@ struct SRoom {
struct SDoor {
uint8 _dir = 0;
- uint8 _x = 0;
- uint8 _y = 0;
- uint8 _fromRoom = 0;
- uint8 _toRoom = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
+ uint16 _fromRoom = 0;
+ uint16 _toRoom = 0;
bool _isLocked = false;
SDoor() {}
- SDoor(uint8 d, uint8 x, uint8 y, uint8 f, uint8 t, bool l) {
+ SDoor(uint8 d, uint16 x, uint16 y, uint16 f, uint16 t, bool l) {
_dir = d;
_x = x;
_y = y;
@@ -180,11 +180,11 @@ struct SDoor {
};
struct SFlame {
- uint8 _x = 0;
- uint8 _y = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
FPattern _p = kFlameOff;
SFlame() {}
- SFlame(uint8 x, uint8 y, FPattern p) {
+ SFlame(uint16 x, uint16 y, FPattern p) {
_x = x;
_y = y;
_p = p;
@@ -192,14 +192,14 @@ FPattern _p = kFlameOff;
};
struct SObj {
- uint8 _x = 0;
- uint8 _y = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
SObjType _type = kTypeTrap;
uint8 _flags = 0;
SpriteFrame _frame = kNoFrame;
Common::Array<uint8> _traps;
SObj() {}
- SObj(uint8 x, uint8 y, SObjType t, SpriteFrame s, uint8 f, Common::Array<uint8> traps) {
+ SObj(uint16 x, uint16 y, SObjType t, SpriteFrame s, uint8 f, Common::Array<uint8> traps) {
_x = x;
_y = y;
_type = t;
@@ -210,15 +210,15 @@ Common::Array<uint8> _traps;
};
struct SMonster {
- uint8 _x = 0;
- uint8 _y = 0;
- uint8 _hits = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
+ uint16 _hits = 0;
MonsterFlag _madAt = kMonstIsNone;
uint8 _flags = 0;
SpriteName _sprite = kCandle;
Common::Array<Motive> _program;
SMonster() {}
- SMonster(uint8 x, uint8 y, uint8 h, MonsterFlag m, uint8 f, Common::Array<Motive> p, SpriteName s) {
+ SMonster(uint16 x, uint16 y, uint16 h, MonsterFlag m, uint8 f, Common::Array<Motive> p, SpriteName s) {
_x = x;
_y = y;
_hits = h;
@@ -233,10 +233,10 @@ struct Story {
int _level = 0;
int _part = 1;
- uint8 _initialUnivX = 0;
- uint8 _initialUnivY = 0;
- uint8 _playerPointX = 0;
- uint8 _playerPointY = 0;
+ uint16 _initialUnivX = 0;
+ uint16 _initialUnivY = 0;
+ uint16 _playerPointX = 0;
+ uint16 _playerPointY = 0;
Common::Array<int> _ladders;
Common::Array<SRoom> _rooms;
Commit: 5c2163db8e182846ef3a9791ba7319e827d34014
https://github.com/scummvm/scummvm/commit/5c2163db8e182846ef3a9791ba7319e827d34014
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Room object utilizes g_immortal instead of passing pointers from engine members
Changed paths:
engines/immortal/room.cpp
engines/immortal/room.h
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
index 94107bc405b..5351375162d 100644
--- a/engines/immortal/room.cpp
+++ b/engines/immortal/room.cpp
@@ -23,15 +23,10 @@
namespace Immortal {
-Room::Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p, int *n)
+Room::Room(uint8 x, uint8 y, RoomFlag f)
: _xPos(x)
, _yPos(y)
, _flags(f)
- , _sprites(s)
- , _dataSprites(d)
- , _cycles(c)
- , _cycPtrs(p)
- , _numSprites(n)
, _candleTmp(0)
, _randomSource("Immortal") {
}
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index b663329a3a4..7cc9937117b 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -107,7 +107,7 @@ private:
Common::RandomSource _randomSource;
public:
- Room(uint8 x, uint8 y, RoomFlag f, Sprite *s, DataSprite *d, Cycle *c, Common::Array<SCycle> p, int *n);
+ Room(uint8 x, uint8 y, RoomFlag f);
~Room() {}
/*
@@ -119,26 +119,20 @@ public:
const uint8 kLightTorchX = 10;
const uint8 kMaxFlameCycs = 16;
- // Other
- int *_numSprites;
- Sprite *_sprites;
- Cycle *_cycles;
-DataSprite *_dataSprites;
-
Common::Array<SCycle> _cycPtrs;
Common::Array<Flame> _fset;
Common::Array<Monster> _monsters;
Common::Array<Object> _objects;
RoomFlag _flags;
- uint8 _xPos;
- uint8 _yPos;
- uint8 _holeRoom;
- uint8 _holeCellX;
- uint8 _holeCellY;
- uint8 _candleTmp; // Special case for candle in maze 0
- uint8 _numFlames;
- uint8 _numInRoom;
+ uint8 _xPos = 0;
+ uint8 _yPos = 0;
+ uint8 _holeRoom = 0;
+ uint8 _holeCellX = 0;
+ uint8 _holeCellY = 0;
+ uint8 _candleTmp = 0; // Special case for candle in maze 0
+ uint8 _numFlames = 0;
+ uint8 _numInRoom = 0;
/*
* --- Methods ---
@@ -207,7 +201,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
void flameInit();
void flameDrawAll(uint16 vX, uint16 vY);
bool roomLighted();
- void lightTorch(int x, int y);
+ void lightTorch(uint8 x, uint8 y);
void flameFreeAll();
void flameSetRoom(Common::Array<SFlame>);
int flameGetCyc(Flame *f, int first);
@@ -227,11 +221,9 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
* [Univ.cpp] Functions from Univ.GS
*/
- void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName n, int img, uint16 p);
+ void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s, int img, uint16 p);
};
-
-
} // namespace immortal
#endif
Commit: bf95330f2cff88ae447f3e6e7054e74fded60177
https://github.com/scummvm/scummvm/commit/bf95330f2cff88ae447f3e6e7054e74fded60177
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: level.cpp no longer passes pointers to room object
Changed paths:
engines/immortal/level.cpp
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 92510e2022f..61b17098756 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -43,7 +43,7 @@ void ImmortalEngine::levelNew(int l) {
levelStory(l);
if (kLevelToMaze[l] != _lastLevelLoaded) {
_lastLevelLoaded = kLevelToMaze[l];
- //loadMaze(l);
+ //loadMazeGraphics(l);
}
if (_level != _lastSongLoaded) {
@@ -76,7 +76,7 @@ void ImmortalEngine::levelLoadFile(int l) {
}
for (int r = 0; r < _stories[l]._rooms.size(); r++) {
- _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags, _sprites, _dataSprites, _cycles, _cycPtrs, &_numSprites);
+ _rooms[r] = new Room(_stories[l]._rooms[r]._x, _stories[l]._rooms[r]._y, _stories[l]._rooms[r]._flags);
Common::Array<SFlame> allFlames(_stories[l]._flames[r].size());
if (_stories[l]._flames[r].size() > 0) {
@@ -124,7 +124,6 @@ void ImmortalEngine::levelDrawAll() {
_count++;
//univAutoCenter();
clearSprites();
- // Room needs to be able to add to the sprite list, so we need to give it a pointer to it first
_rooms[_currentRoom]->drawContents(_viewPortX, _viewPortY);
}
Commit: 7284826303a1e1b61ff2739ec29c004b16a45515
https://github.com/scummvm/scummvm/commit/7284826303a1e1b61ff2739ec29c004b16a45515
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: g_engine -> g_immortal + minor adjustments
Changed paths:
engines/immortal/immortal.cpp
engines/immortal/immortal.h
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index fd55add4e92..e3ce80031ed 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -28,13 +28,13 @@
namespace Immortal {
-ImmortalEngine *g_engine;
+ImmortalEngine *g_immortal;
ImmortalEngine::ImmortalEngine(OSystem *syst, const ADGameDescription *gameDesc)
: Engine(syst)
, _gameDescription(gameDesc)
, _randomSource("Immortal") {
- g_engine = this;
+ g_immortal = this;
// Add the game folder to the search manager path variable
const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -151,21 +151,28 @@ Common::Error ImmortalEngine::run() {
}
//Main:
- _zero = 0;
- _dim = 0;
- _usingNormal = 0;
- _draw = 1;
+ _zero = 0;
+ _draw = 1;
+ _usingNormal = 1;
+ _penY = 7;
+ _penX = 1;
initStoryStatic(); // Init the arrays of static story elements (done at compile time in the source)
loadPalette(); // We need to grab the palette from the disk first
+
+ // This is the equivalent of Main->InitGraphics->MyClearScreen in Driver
useNormal(); // The first palette will be the default
+
loadFont(); // Load the font sprite
loadWindow(); // Load the window background
loadSingles("Song A"); // Music
loadSprites(); // Get all the sprite data into memory
+
_playing = kSongNothing;
_themePaused = 0;
+
clearSprites(); // Clear the sprites before we start
+ // This is where the request play disk would happen, but that's not needed here
logicInit(); // Init the game logic
_err = Common::kNoError;
@@ -186,11 +193,9 @@ Common::Error ImmortalEngine::run() {
userIO();
noNetwork();
pollKeys();
- //logic();
+ logic();
pollKeys();
if (logicFreeze() == 0) {
- DataSprite *d = &_dataSprites[kGoblin2];
- superSprite(d, 20, 0xA0, 4, kVSBMW, _screenBuff, kSuperTop, kMySuperBottom);
drawUniv();
pollKeys();
fixColors();
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index fe20b7cfa18..fce55cf9ea6 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -77,6 +77,11 @@ enum InputAction {
kActionDBGStep // Debug key for moving engine forward one frame at a time
};
+enum ButtonHeldMask {
+ kButton0Held = 2,
+ kButton1Held = 4
+};
+
enum InputDirection {
kDirectionUp,
kDirectionLeft,
@@ -84,6 +89,12 @@ enum InputDirection {
kDirectionRight
};
+// Needed by kernal for text
+enum FadeType {
+ kTextFadeIn,
+ kTextDontFadeIn
+};
+
// Needed by kernal for music
enum Song {
kSongNothing,
@@ -92,6 +103,15 @@ enum Song {
kSongText
};
+enum Sound {
+ kSoundSwish = 6,
+ kSoundAwe,
+ kSoundHuh,
+ kSoundClank,
+ kSoundFireBall,
+ kSoundDoor
+};
+
// Needed by logic for various things
enum MonsterID {
kPlayerID
@@ -132,7 +152,7 @@ struct Spark {
struct GenericSprite {
};
-// Doors are a property of the level, not the room, they defined the connections between rooms
+// Doors are a property of the level, not the room, they define the connections between rooms
struct Door {
uint8 _x = 0;
uint8 _y = 0;
@@ -192,10 +212,10 @@ public:
const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is (320x200) * 2 byte words
const uint16 kScreenLeft = 32;
const uint16 kScreenTop = 20;
- const int kTextLeft = 8;
- const int kTextTop = 4;
- const int kGaugeX = 0;
- const int kGaugeY = -13; // ???
+ const uint8 kTextLeft = 8;
+ const uint8 kTextTop = 4;
+ const uint8 kGaugeX = 0;
+ const uint8 kGaugeY = -13; // ???
const uint16 kScreenBMW = 160; // Screen BitMap Width?
const uint16 kChrW = 64;
const uint16 kChrH = 32;
@@ -243,6 +263,18 @@ public:
const uint16 kViewPortSpY = 0;
const uint16 kWizardX = 28; // Common sprite center for some reason
const uint16 kWizardY = 37;
+ const uint16 kObjectY = 24;
+ const uint16 kObjectX = 32;
+ const uint16 kObjectHeight = 48;
+ const uint16 kObjectWidth = 64;
+
+ // Text constants
+ const uint8 kMaxRows = 5;
+ const uint8 kMaxCollumns = 26;
+
+ const uint16 kYesNoY = 88;
+ const uint16 kYesNoX1 = 8;
+ const uint16 kYesNoX2 = 182;
// Asset constants
const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
@@ -323,6 +355,14 @@ public:
int _pressedDirection = 0;
int _heldDirection = 0;
+ // Text printing members
+ uint8 _slowText = 0;
+ uint8 _formatted = 0;
+ uint8 _collumn = 0;
+ uint8 _row = 0;
+ uint8 _myButton = 0;
+ uint8 _lastYes = 0;
+
// Music members
Song _playing; // Currently playing song
int _themeID = 0; // Not sure yet tbh
@@ -343,7 +383,6 @@ public:
Common::Array<ObjType> _objTypePtrs;
// Screen members
- byte *_window; // Bitmap of the window around the game
byte *_screenBuff; // The final buffer that will transfer to the screen
uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
@@ -394,6 +433,7 @@ GenericSprite _genSprites[6];
void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
void rect(int x, int y, int w, int h); // Draws a solid rectangle at x,y with size w,h. Also shadows for blit?
void backspace(); // Moves draw position back and draws empty rect in place of char
+ void printByte(int b);
void printChr(char c);
void loadWindow(); // Gets the window.bm file
void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
@@ -406,10 +446,14 @@ GenericSprite _genSprites[6];
void makeMyCNM(); // ?
void drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
void addRows(); // Add rows to drawitem array
+ void addSprite(uint16 vpX, uint16 vpY, SpriteName s, int img, uint16 x, uint16 y, uint16 p);
void addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
void sortDrawItems(); // Sort said items
void drawItems(); // Draw the items over the background
+ void drawIcon(int img);
void setPen(uint16 penX, uint16 penY); // Sets the 'pen' x and y positions, including making y negative if above a certain point
+ void center();
+ void carriageReturn();
// Music
void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
@@ -417,11 +461,13 @@ GenericSprite _genSprites[6];
Song getPlaying();
void playMazeSong();
void playCombatSong();
+ void playTextSong();
void doGroan();
void stopMusic();
void musicPause(int sID);
void musicUnPause(int sID);
void loadSingles(Common::String songName); // Loads and then parse the maze song
+ void standardBeep();
// Palette
void loadPalette(); // Get the static palette data from the disk
@@ -443,17 +489,17 @@ GenericSprite _genSprites[6];
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
void initStoryStatic(); // Sets up all of the global static story elements
- //void loadMazeGraphics(); // Creates a universe with a maze
+ void loadMazeGraphics(int m); // Creates a universe with a maze
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
- void kernalAddSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p);
// Input
void userIO(); // Get input
void pollKeys(); // Buffer input
void noNetwork(); // Setup input mirrors
void waitKey(); // Waits until a key is pressed (until getInput() returns true)
+ void waitClick(); // Waits until one of the two buttons is pressed
void blit8(); // This is actually just input, but it is called blit because it does a 'paddle blit' 8 times
// These will replace the myriad of hardware input handling from the source
@@ -525,19 +571,31 @@ GenericSprite _genSprites[6];
void miscInit();
void setRandomSeed();
void getRandom();
- void myDelay();
+
+ // Input related
+ bool buttonPressed();
+ bool firePressed();
// Text printing
- bool textPrint(Str s);
- void textSub();
- void textEnd(Str s);
- void textMiddle(Str s);
- void textBeginning(Str s);
- void yesNo();
+ void myFadeOut();
+ void myFadeIn();
+ bool textPrint(Str s, int n);
+ bool textBeginning(Str s, int n);
+ bool textSub(Str s, FadeType f, int n);
+ void textEnd(Str s, int n);
+ void textMiddle(Str s, int n);
- // Input related
- void buttonPressed();
- void firePressed();
+ void textCR();
+ void textPageBreak(Common::String s, int &index);
+ void textAutoPageBreak();
+ void textDoSpace(Common::String s, int index);
+ void textBounceDelay();
+
+ bool yesNo();
+ void noOn();
+ void yesOn();
+
+ void myDelay(int j);
/*
@@ -680,8 +738,8 @@ GenericSprite _genSprites[6];
} */
};
-extern ImmortalEngine *g_engine;
-#define SHOULD_QUIT ::Immortal::g_engine->shouldQuit()
+extern ImmortalEngine *g_immortal;
+#define SHOULD_QUIT ::Immortal::g_immortal->shouldQuit()
} // namespace Immortal
Commit: 4ca64366dc0fc695431075fbff5466aec93dcee7
https://github.com/scummvm/scummvm/commit/4ca64366dc0fc695431075fbff5466aec93dcee7
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: flameSet and Cycle utilize g_immortal instead of utilities
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/flameSet.cpp
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index f2da5503c5f..bec8a6eba79 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -47,10 +47,10 @@ namespace Immortal {
int Room::cycleNew(CycID id) {
// An 'available' cyc is identified by the index being -1
for (int i = 0; i < kMaxCycles; i++) {
- if (_cycles[i]._index == -1) {
- _cycles[i]._index = 0;
- _cycles[i]._cycList = id;
- debug("made cyc, = %d", i);
+ if (g_immortal->_cycles[i]._index == -1) {
+ g_immortal->_cycles[i]._index = 0;
+ g_immortal->_cycles[i]._cycList = id;
+ //debug("made cyc, = %d", i);
return i;
}
}
@@ -59,18 +59,18 @@ int Room::cycleNew(CycID id) {
}
void Room::cycleFree(int c) {
- _cycles[c]._index = -1;
+ g_immortal->_cycles[c]._index = -1;
}
bool Room::cycleAdvance(int c) {
/* If we have reached the end, check if repeat == true, and set back to 0 if so
* Otherwise, set to the last used index */
- _cycles[c]._index++;
- if (_cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index] == -1) {
- if (_cycPtrs[_cycles[c]._cycList]._repeat == true) {
- _cycles[c]._index = 0;
+ g_immortal->_cycles[c]._index++;
+ if (g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._frames[g_immortal->_cycles[c]._index] == -1) {
+ if (g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._repeat == true) {
+ g_immortal->_cycles[c]._index = 0;
} else {
- _cycles[c]._index--;
+ g_immortal->_cycles[c]._index--;
return true;
}
}
@@ -88,33 +88,33 @@ int Room::cycleGetFrame(int c) {
* This is essentially self-modifying code, and it saves 2 bytes of DP memory over the traditional
* STA DP : LDA (DP)
*/
- debug("%d", _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index]);
- return _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index];
+ //debug("%d", _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index]);
+ return g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._frames[g_immortal->_cycles[c]._index];
}
int Room::cycleGetNumFrames(int c) {
// Why in the world is this not kept as a property of the cycle? We have to calculate the size of the array each time
int index = 0;
- while (_cycPtrs[_cycles[c]._cycList]._frames[index] != -1) {
+ while (g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._frames[index] != -1) {
index++;
}
return index;
}
DataSprite *Room::cycleGetDataSprite(int c) {
- return &_dataSprites[_cycPtrs[_cycles[c]._cycList]._sName];
+ return &g_immortal->_dataSprites[g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._sName];
}
CycID Room::getCycList(int c) {
- return _cycles[c]._cycList;
+ return g_immortal->_cycles[c]._cycList;
}
int Room::cycleGetIndex(int c) {
- return _cycles[c]._index;
+ return g_immortal->_cycles[c]._index;
}
void Room::cycleSetIndex(int c, int f) {
- _cycles[c]._index = f;
+ g_immortal->_cycles[c]._index = f;
}
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index c861cb67220..32e973c0734 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -47,7 +47,7 @@ void Room::flameFreeAll() {
void Room::flameDrawAll(uint16 vX, uint16 vY) {
for (int i = 0; i < _fset.size(); i++) {
- univAddSprite(vX, vY, _fset[i]._x, _fset[i]._y, _cycPtrs[_cycles[_fset[i]._c]._cycList]._sName, cycleGetFrame(_fset[i]._c), 0);
+ univAddSprite(vX, vY, _fset[i]._x, _fset[i]._y, g_immortal->_cycPtrs[g_immortal->_cycles[_fset[i]._c]._cycList]._sName, cycleGetFrame(_fset[i]._c), 0);
if (cycleAdvance(_fset[i]._c) == true) {
cycleFree(_fset[i]._c);
_fset[i]._c = flameGetCyc(&_fset[i], 1);
@@ -68,7 +68,7 @@ bool Room::roomLighted() {
return false;
}
-void Room::lightTorch(int x, int y) {
+void Room::lightTorch(uint8 x, uint8 y) {
/* Checks every torch to see if it is:
* pattern == off, and inside the point x,y
* which is the fireball position. This is a
@@ -79,7 +79,7 @@ void Room::lightTorch(int x, int y) {
for (int i = 0; i < _fset.size(); i++) {
if (_fset[i]._p == kFlameOff) {
- if (Immortal::Utilities::inside(x, y, kLightTorchX, _fset[i]._x + 16, _fset[i]._y + 8)) {
+ if (Utilities::inside(kLightTorchX, x, y, _fset[i]._x + 16, _fset[i]._y + 8)) {
_fset[i]._p = kFlameNormal;
}
Commit: 13ab4219aea5780acbe5f75bcf7ea5beeb237737
https://github.com/scummvm/scummvm/commit/13ab4219aea5780acbe5f75bcf7ea5beeb237737
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL Remove kStrNoDesc because kStrNull exists
Changed paths:
engines/immortal/definitions.h
diff --git a/engines/immortal/definitions.h b/engines/immortal/definitions.h
index 8de52483cc6..de431f4c9e5 100644
--- a/engines/immortal/definitions.h
+++ b/engines/immortal/definitions.h
@@ -96,7 +96,6 @@ enum Motive { // This will likely be moved to a monster ai
};
enum Str {
- kStrNoDesc,
kStrSword,
kStrSwordDesc,
kStrBonesText1,
Commit: 9e911f29d61a1371f0eb13451691f9b8b800ed44
https://github.com/scummvm/scummvm/commit/9e911f29d61a1371f0eb13451691f9b8b800ed44
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Text rendering through textSub() and associated functions preliminary implementation
Changed paths:
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 9344ac283cf..643ee850878 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -48,25 +48,21 @@ void ImmortalEngine::drawUniv() {
_myUnivPointX = !(_myViewPortX & (kChrW - 1)) + kViewPortSpX;
_myUnivPointY = !(_myViewPortY & (kChrH - 1)) + kViewPortSpY;
- // To start constructing the screen, we start with the frame as the base
- memcpy(_screenBuff, _window, kScreenSize);
-
makeMyCNM();
drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
addRows(); // Add rows to drawitem array
addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
sortDrawItems(); // Sort said items
drawItems(); // Draw the items over the background
+}
+void ImmortalEngine::copyToScreen() {
/* copyRectToSurface will apply the screenbuffer to the ScummVM surface.
* We want to do 320 bytes per scanline, at location (0,0), with a
* size of 320x200.
*/
_mainSurface->copyRectToSurface(_screenBuff, kResH, 0, 0, kResH, kResV);
-}
-
-void ImmortalEngine::copyToScreen() {
if (_draw == 1) {
g_system->copyRectToScreen((byte *)_mainSurface->getPixels(), kResH, 0, 0, kResH, kResV);
g_system->updateScreen();
@@ -74,19 +70,24 @@ void ImmortalEngine::copyToScreen() {
}
void ImmortalEngine::clearScreen() {
- //fill the visible screen with black pixels by drawing a rectangle
+ // Fill the visible screen with black pixels by drawing a rectangle
//rect(32, 20, 256, 128, 0)
+ // This is just temporary, until rect() is implemented
+ for (int y = 0; y < 128; y++) {
+ for (int x = 0; x < 256; x++) {
+ _screenBuff[((y + 20) * kResH) + (x + 32)] = 0;
+ }
+ }
+ _penX = kTextLeft;
+ _penY = kTextTop;
+
if ((_dontResetColors & kMaskLow) == 0) {
useNormal();
}
-}
-
-void ImmortalEngine::whiteScreen() {
- //fill the visible screen with black pixels by drawing a rectangle
- //rect(32, 20, 256, 128, 13)
+ copyToScreen();
}
void ImmortalEngine::mungeBM() {}
@@ -96,6 +97,11 @@ void ImmortalEngine::sBlit() {}
void ImmortalEngine::scroll() {}
void ImmortalEngine::makeMyCNM() {} // ?
+void ImmortalEngine::drawIcon(int img) {
+ superSprite(&_dataSprites[kObject], ((kObjectWidth / 2) + kScreenLeft) + _penX, _penY + (kObjectY + kScreenTop), img, kScreenBMW, _screenBuff, 0, 200);
+ _penY += kObjectHeight;
+}
+
void ImmortalEngine::addRows() {
// I'm not really sure how this works yet
int i = _num2DrawItems;
@@ -108,12 +114,43 @@ void ImmortalEngine::addRows() {
_num2DrawItems = i;
}
+void ImmortalEngine::addSprite(uint16 vpX, uint16 vpY, SpriteName s, int img, uint16 x, uint16 y, uint16 p) {
+ debug("adding sprite...");
+ if (_numSprites != kMaxSprites) {
+ if (x >= (kResH + kMaxSpriteLeft)) {
+ x |= kMaskHigh; // Make it negative
+ }
+
+ _sprites[_numSprites]._X = (x << 1) + vpX;
+
+ if (y >= (kMaxSpriteAbove + kResV)) {
+ y |= kMaskHigh;
+ }
+
+ _sprites[_numSprites]._Y = (y << 1) + vpY;
+
+ if (p >= 0x80) {
+ p |= kMaskHigh;
+ }
+
+ _sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
+
+ _sprites[_numSprites]._image = img;
+ _sprites[_numSprites]._dSprite = &_dataSprites[s];
+ _sprites[_numSprites]._on = 1;
+ _numSprites += 1;
+ debug("sprite added");
+ } else {
+ debug("Max sprites reached beeeeeep!!");
+ }
+}
+
void ImmortalEngine::addSprites() {
// My goodness this routine is gross
int tmpNum = _num2DrawItems;
for (int i = 0; i < kMaxSprites; i++) {
// If the sprite is active
- if (_sprites[i]._on == 1) {
+ if (/*_sprites[i]._on*/0 == 1) {
// If sprite X is an odd number???
if ((_sprites[i]._X & 1) != 0) {
debug("not good! BRK");
@@ -140,7 +177,7 @@ void ImmortalEngine::addSprites() {
DataSprite *tempD = _sprites[i]._dSprite;
debug("what sprite is this: %d %d %d", i, _sprites[i]._image, _sprites[i]._dSprite->_images.size());
- Image *tempImg = &(tempD->_images[0/*_sprites[i]._image*/]);
+ Image *tempImg = &(tempD->_images[_sprites[i]._image]);
int sx = ((_sprites[i]._X + tempImg->_deltaX) - tempD->_cenX) - _myViewPortX;
int sy = ((_sprites[i]._Y + tempImg->_deltaY) - tempD->_cenY) - _myViewPortY;
@@ -299,6 +336,38 @@ void ImmortalEngine::backspace() {
// Just moves the drawing position back by a char, and then draws an empty rect there (I think)
_penX -= 8;
//rect(_penX + 32, 40, 8, 16, 0);
+
+ // The Y is set here presumably because it's only used for the certificate
+ for (int y = 0; y < 16; y++) {
+ for (int x = 0; x < 8; x++) {
+ _screenBuff[((y + 40) * kResH) + (x + (_penX + 32))] = 0;
+ }
+ }
+}
+
+void ImmortalEngine::printByte(int b) {
+ int hundreds = 0;
+ int tens = 0;
+
+ while ((b - 100) >= 0) {
+ hundreds++;
+ b -= 100;
+ }
+
+ if (hundreds > 0) {
+ printChr(char (hundreds + '0'));
+ }
+
+ while ((b - 10) >= 0) {
+ tens++;
+ b -= 10;
+ }
+
+ if (tens > 0) {
+ printChr(char (tens + '0'));
+ }
+
+ printChr(char (b + '0'));
}
void ImmortalEngine::printChr(char c) {
@@ -310,36 +379,37 @@ void ImmortalEngine::printChr(char c) {
return;
}
+ // Why is single quote done twice?
if (c == 0x27) {
_penX -= 2;
}
- if ((c >= 'A') && (c <= 'Z')) {
+ switch (c) {
+ case 'm':
+ case 'w':
+ case 'M':
+ case 'W':
+ _penX += 8;
+ default:
+ break;
+ }
+
+ if ((((c >= 'A') && (c <= 'Z'))) || ((c == kGaugeOn) || (c == kGaugeOff))) {
_penX += 8;
+ }
- } else {
- switch (c) {
- // Capitals, the health bar icons, and lower case m/w are all 2 chars wide
- case 'm':
- case 'w':
- case 'M':
- case 'W':
- case 1: // Can't use the constant for this for some reason
- case 0:
- _penX += 8;
- break;
- case 'i':
- _penX -= 3;
- break;
- case 'j':
- case 't':
- _penX -= 2;
- break;
- case 'l':
- _penX -= 4;
- default:
- break;
- }
+ switch (c) {
+ case 'i':
+ _penX -= 3;
+ break;
+ case 'j':
+ case 't':
+ _penX -= 2;
+ break;
+ case 'l':
+ _penX -= 4;
+ default:
+ break;
}
uint16 x = _penX + kScreenLeft;
@@ -353,8 +423,15 @@ void ImmortalEngine::printChr(char c) {
}
superSprite(&_dataSprites[kFont], x, y, (int) c, kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
- if ((c == 0x27) || (c == 'T')) {
- _penX -= 2; // Why is this done twice??
+
+ // Single quote?
+ if (c == 0x27) {
+ _penX -= 2;
+ }
+
+ // If the letter was a captial T, the next letter should be a little closer
+ if (c == 'T') {
+ _penX -= 2;
}
_penX += 8;
@@ -368,181 +445,6 @@ void ImmortalEngine::printChr(char c) {
*
*/
-void ImmortalEngine::initStoryStatic() {
- Common::Array<Common::String> s{"#" + Common::String(kSwordBigFrame) + "sword@",
- "You find an Elven sword of&agility. Take it?@",
- "Search the bones?%",
- "}The sword permanently endows you with Elven agility and quickness in combat.@",
- "}You notice something that looks wet and green under the pile. Search further?%",
- "#" + Common::String(kBagBigFrame) + " dust@"
- "}You find a bag containing Dust of Complaisance.&@"
- "}Drop the bait on the ground here?%"
- "}To use this dust, you throw it in the air. Do that here?%"
- "_}Don+t bother me, I+m cutting a gem. Yes, you need it. No, you can+t have it. I wouldn+t give it to anyone, least of all you. Go away. ]]]]="
- "_}Let me help you. Please take this gem. No, really, I insist. Take it and go with my blessings. Good luck. ]]]]="
- "#" + Common::String(kCarpetBigFrame) + "carpet@",
- "#" + Common::String(kBombBigFrame) + " bomb@",
- "A gas bomb that goblins&use to paralyze trolls.&@",
- "Take it?<>@",
- "%",
- " other@",
- "#" + Common::String(kKeyBigFrame) + " key@",
- "#" + Common::String(kKeyBigFrame) + " key@",
- "A key to a chest.&@",
- "The chest is open. Examine&contents?%",
- "Put it on?%",
- "Drop it?%",
- "It+s unlocked. Open it?%",
- "It+s locked but you have&the key. Open it?%",
- "It+s locked and you don+t&have the key.@",
- "The lock, triggered by a&complicated set of latches,&is unfamiliar to you.@",
- "#" + Common::String(kGoldBigFrame) + "$0 gold@",
- "You find $0 gold pieces.&&^#" + Common::String(kPileFrame) + "@",
- "@",
- "You can+t plant them on&stone tiles.@",
- "It+s locked but you are&able to unlock it with&the key.@",
- "_}The king is not dead, but the poison is taking effect. When he sees you, he attempts to speak:[(Give me water... the fountain... I give you... information... peace...+[Give him water?%",
- "_}You dont have any water to give him. He mumbles something. Then silence... You find a key on his body.]]]]=",
- "_}He mumbles something. Then silence... You find a key on his body.]]]]=",
- "_}I+ll tell you how to... next level... past slime... three jewels... slime... rock becomes... floor... right, left, center of the... [Then silence. His hand opens, releasing a key.]]]]=",
- "You find a door key.&@",
- "You find a note.&@",
- "#" + Common::String(kNoteBigFrame) + "note@",
- "He+s dead.&Look for possessions?%",
- "You don+t have it. Check&your inventory.@",
- "Game Over&&Play again?@",
- "Congratulations!&&Play again?@",
- "You find a bag of bait.&@",
- "#" + Common::String(kBagBigFrame) + " bait@",
- "You find a stone. @",
- "#" + Common::String(kStoneBigFrame) + " stone@",
- "You find a red gem.&@",
- "#" + Common::String(kGemBigFrame) + " gem@",
- "You find a scroll with&fireball spells.&@"
- "#" + Common::String(kScrollBigFrame) + "$ shots@",
- "You find a map warning&you about pit traps.&@"
- "#" + Common::String(kMapBigFrame) + " map@",
- "#" + Common::String(kVaseBigFrame) + " oil@",
- "You apply the oil but notice&as you walk that the leather&is drying out quickly.@"
- "}You discover a scroll with a charm spell to use on will o+ the wisps.&@"
- "#" + Common::String(kScrollBigFrame) + " charm@",
- "}This charms the will o+ the wisps to follow you. Read the spell again to turn them against your enemies.@"
- "}It looks like water. Drink it?%",
- "Drink it?%",
- "}It works! You are much stronger.]]]=",
- "}It looks like it has green stuff inside. Open it?%",
- "Now this will take&effect when you press the&fire button.@",
- "You find a potion,&Magic Muscle.&@",
- "#" + Common::String(kVaseBigFrame) + " potion@",
- "You find a bottle.&@",
- "#" + Common::String(kVaseBigFrame) + " bottle@",
- "#" + Common::String(kRingBigFrame) + "Protean@",
- "You find a Protean Ring.&@",
- "You find a troll ritual knife,&used to declare a fight to&the death. @",
- "#" + Common::String(kKnifeBigFrame) + " knife@",
- "_}It is a fine woman+s garment. Folded inside is a ring with the words,[`To Ana, so harm will never find you. -Your loving father, Dunric.+&@",
- "You find a small, well&crafted ring. @",
- "#" + Common::String(kRingBigFrame) + " gift@",
- "#" + Common::String(kRingBigFrame) + " Ana+s@",
- "_}She is hurt and upset when she finds you don+t have her ring or won+t give it to her. She scurries back into the hole. The hole is too small for you to follow.&@",
- "_}`Sir, can you help me,+ the girl pleads. `I was kidnapped and dragged down here. All the man would say is `Mordamir+s orders.+[I escaped using a ring my father gave me, but now I+ve lost it. Did you find it?+%",
- "_}We have met before, old man. Do you remember? Because you helped me, you may pass. But I warn you, we are at war with the trolls.[Over this ladder, across the spikes, is troll territory. Very dangerous.@",
- "_}You are an impostor!]]]]=",
- "_}Old man, do you remember me? I am king of the goblins. You didn+t give me the water. You left me to die after you took the key from me. Now you will pay.]]]]=",
- "_}You quickly fall into a deep, healing sleep...[Vivid images of a beautiful enchanted city pass by. All the city people are young and glowing. Fountains fill the city, and the splash and sparkle of water is everywhere...[Suddenly the images go black. A face appears... Mordamir!]][He is different from how you remember him. His gentle features are now withered. His kind eyes, now cold and sunken, seem to look through you with a dark, penetrating stare. You wake rejuvenated, but disturbed.]]]]]=",
- "_}Here, take this ring in return. [I don+t know if it will help, but I heard the unpleasant little dwarf say, (Clockwise, three rings around the triangle.+[Could that be a clue to his exit puzzle? I must go. Goodbye.]]]]=",
- "#" + Common::String(kSackBigFrame) + " spores@",
- "You find a sack of bad&smelling spores.&@",
- "Please insert play disk.@",
- "New game?%",
- "Enter certificate:&-=",
- "Invalid certificate.@",
- "End of level!&Here is your certificate:&&=",
- "&@",
- " Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]]=",
- " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
- "_}Greetings, friend! Come, I+ve got something you need. These parts are plagued with slime.[You can+t venture safely without my slime oil for boots, a bargain at only 80 gold pieces.%",
- "_}All right, 60 gold pieces for my oil. Rub it on your boots and slime won+t touch you. 60, friend.%",
- "This room doesn+t resemble&any part of the map.@",
- "This room resembles part&of the map.@"};
- _strPtrs = s;
-
- // Scope, amirite?
- Common::Array<int> cyc0{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
- Common::Array<int> cyc1{15,16,17,18,19,20,21,22,-1};
- Common::Array<int> cyc2{0,1,2,-1};
- Common::Array<int> cyc3{3,4,5,-1};
- Common::Array<int> cyc4{6,7,8,9,10,-1};
- Common::Array<int> cyc5{11,12,13,14,15,-1};
- Common::Array<int> cyc6{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,-1};
- Common::Array<int> cyc7{0,1,2,3,4,-1};
- Common::Array<int> cyc8{5,1+5,2+5,3+5,4+5,-1};
- Common::Array<int> cyc9{10,1+10,2+10,3+10,4+10,-1};
- Common::Array<int> cyc10{15,1+15,2+15,3+15,4+15,-1};
- Common::Array<int> cyc11{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
- Common::Array<int> cyc12{0,1,2,3,4,5,6,7,8,9,-1};
- Common::Array<int> cyc13{0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, -1};
- Common::Array<int> cyc14{31,32,33,32, 34,35,36,35, 37,38,39,38, 40,41,42,41, 43,44,45,44, 46,47,48,47, 49,50,51,50, 52,53,54,53, -1};
- Common::Array<int> cyc15{55, -1};
- Common::Array<int> cyc16{63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66,-1};
- Common::Array<int> cyc17{0,1,0,-1};
- Common::Array<int> cyc18{0,1,2,4,5,6,7,8,9,10,11,12,2,1,-1};
- Common::Array<int> cyc19{0,0,1,2,13,14,15,16,4,2,3,-1};
- Common::Array<int> cyc20{0,1,2,3,20,21,22,23,24,25,26,27,5,4,3,-1};
- Common::Array<int> cyc21{0,1,2,3,-1};
- Common::Array<int> cyc22{0,17,18,19,3,-1};
- Common::Array<int> cyc23{0,1,-1};
- Common::Array<int> cyc24{28,28,28,28,-1};
- Common::Array<int> cyc25{15,16,15,16,15,1+15,1+15,-1};
- Common::Array<int> cyc26{10+15,11+15,12+15,13+15,14+15,15+15,16+15,-1};
- Common::Array<int> cyc27{2+15,3+15,4+15,5+15,-1};
- Common::Array<int> cyc28{6+15,7+15,8+15,9+15,-1};
- Common::Array<int> cyc29{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
- Common::Array<int> cyc30{0,1,2,3,3,3,3,4,5,6,-1};
- Common::Array<int> cyc31{0,1,2,3,4,5,6,7,8,-1};
-
- Common::Array<SCycle> c{SCycle(kBubble, false, cyc0), SCycle(kBubble, false, cyc1),
- SCycle(kSpark, false, cyc2), SCycle(kSpark, false, cyc3),
- SCycle(kSpark, false, cyc4), SCycle(kSpark, false, cyc5), SCycle(kSpark, false, cyc6),
- SCycle(kPipe, false, cyc7), SCycle(kPipe, false, cyc8),
- SCycle(kPipe, false, cyc9), SCycle(kPipe, false, cyc10),
- SCycle(kAnaVanish, false, cyc11), SCycle(kAnaGlimpse, false, cyc12),
- SCycle(kKnife, true, cyc13),
- SCycle(kSpark, true, cyc14), SCycle(kSpark, true, cyc15), SCycle(kSpark, true, cyc16),
- SCycle(kBigBurst, false, cyc17),
- SCycle(kFlame, false, cyc18), SCycle(kFlame, false, cyc19), SCycle(kFlame, false, cyc20),
- SCycle(kFlame, false, cyc21), SCycle(kFlame, false, cyc22), SCycle(kFlame, false, cyc23),
- SCycle(kFlame, false, cyc24),
- SCycle(kCandle, false, cyc25), SCycle(kCandle, false, cyc26), SCycle(kCandle, false, cyc27),
- SCycle(kCandle, false, cyc28), SCycle(kCandle, false, cyc29),
- SCycle(kSink, false, cyc30),
- SCycle(kNorlacDown, false, cyc31)};
- _cycPtrs = c;
-
- Common::Array<Motive> m{};
- _motivePtrs = m;
-
- Common::Array<Damage> d{};
- _damagePtrs = d;
-
- Common::Array<Use> u{};
- _usePtrs = u;
-
- Common::Array<Pickup> p{};
- _pickupPtrs = p;
-
- CArray2D<Motive> pr{};
- _programPtrs = pr;
-
- Common::Array<ObjType> o{};
- _objTypePtrs = o;
-
-}
-
-void ImmortalEngine::kernalAddSprite(uint16 x, uint16 y, SpriteName n, int img, uint16 p) {
- Utilities::addSprite(_sprites, _viewPortX, _viewPortY, &_numSprites, &_dataSprites[n], img, x, y, p);
-}
-
void ImmortalEngine::clearSprites() {
// Just sets the 'active' flag on all possible sprites to 0
for (int i = 0; i < kMaxSprites; i++) {
@@ -557,6 +459,10 @@ void ImmortalEngine::cycleFreeAll() {
}
}
+void ImmortalEngine::loadMazeGraphics(int m) {
+ //setColors();
+}
+
void ImmortalEngine::loadSprites() {
/* This is a bit weird, so I'll explain.
* In the source, this routine loads the files onto the heap, and then
@@ -567,7 +473,7 @@ void ImmortalEngine::loadSprites() {
* We aren't going to have the sprite properties inside the file data, so instead
* we have an array of all game sprites _dataSprites which is indexed
* soley by a sprite number now. This also means that a sprite itself has a reference to
- * a datasprite, instead of the sprite index and separate the file pointer. Datasprite
+ * a datasprite, instead of the sprite index and separately the file pointer. Datasprite
* is what needs the file, so that's where the pointer is. The index isn't used by
* the sprite or datasprite themselves, so it isn't a member of either of them.
*/
@@ -621,9 +527,16 @@ void ImmortalEngine::loadSprites() {
}
void ImmortalEngine::loadWindow() {
+ /* Technically, the source uses loadIFF to just move the window bitmap from
+ * the file straight into the virtual screen buffer. However, since
+ * we will have to extract each pixel from the buffer to use with
+ * Surface anyway, we are doing the extracting in advance, since that is
+ * more or less what is happening at this point in the source. This will
+ * likely be combined with something else in the future.
+ */
+
// Initialize the window bitmap
Common::File f;
- _window = new byte[kScreenSize];
if (f.open("WINDOWS.BM")) {
@@ -640,8 +553,8 @@ void ImmortalEngine::loadWindow() {
for (int x = 0; x < kResH; x += 2) {
pos = (y * kResH) + x;
pixel = f.readByte();
- _window[pos] = (pixel & kMask8High) >> 4;
- _window[pos + 1] = pixel & kMask8Low;
+ _screenBuff[pos] = (pixel & kMask8High) >> 4;
+ _screenBuff[pos + 1] = pixel & kMask8Low;
}
}
@@ -664,7 +577,7 @@ void ImmortalEngine::loadFont() {
_dataSprites[kFont] = d;
} else {
- debug("file doesn't exit?!");
+ debug("file doesn't exist?!");
}
}
@@ -774,12 +687,12 @@ void ImmortalEngine::setColors(uint16 pal[]) {
void ImmortalEngine::fixColors() {
// Pretty silly that this is done with two separate variables, could just index by one...
- if (_dim == true) {
- if (_usingNormal == true) {
+ if (_dim == 1) {
+ if (_usingNormal == 1) {
useDim();
}
} else {
- if (_usingNormal == false) {
+ if (_usingNormal == 0) {
useNormal();
}
}
@@ -789,13 +702,13 @@ void ImmortalEngine::pump() {
// Flashes the screen (except the frame thankfully) white, black, white, black, then clears the screen and goes back to normal
useWhite();
g_system->updateScreen();
- Immortal::Utilities::delay(2);
+ Utilities::delay(2);
useBlack();
g_system->updateScreen();
- Immortal::Utilities::delay(2);
+ Utilities::delay(2);
useWhite();
g_system->updateScreen();
- Immortal::Utilities::delay(2);
+ Utilities::delay(2);
useBlack();
g_system->updateScreen();
clearScreen();
@@ -857,7 +770,7 @@ void ImmortalEngine::fade(uint16 pal[], int dir, int delay) {
while ((count >= 0) && (count <= 256)) {
fadePal(pal, count, target);
- Immortal::Utilities::delay8(delay);
+ Utilities::delay8(delay);
setColors(target);
// Same as above, it was originally a branch, this does the same thing
@@ -896,12 +809,12 @@ void ImmortalEngine::useWhite() {
void ImmortalEngine::useNormal() {
setColors(_palDefault);
- _usingNormal = true;
+ _usingNormal = 1;
}
void ImmortalEngine::useDim() {
setColors(_palDim);
- _usingNormal = false;
+ _usingNormal = 0;
}
@@ -926,6 +839,19 @@ void ImmortalEngine::waitKey() {
}
}
+// This was originally in Motives, which is weird since it seems more like an engine level function, so it's in kernal now
+void ImmortalEngine::waitClick() {
+ bool wait = true;
+ while (wait == true) {
+ if (getInput() == true) {
+ Utilities::delay(25);
+ if (buttonPressed() || firePressed()) {
+ wait = false;
+ }
+ }
+ }
+}
+
void ImmortalEngine::blit8() {}
bool ImmortalEngine::getInput() {
return true;
@@ -1000,6 +926,10 @@ void ImmortalEngine::playMazeSong() {
void ImmortalEngine::playCombatSong() {
}
+void ImmortalEngine::playTextSong() {
+
+}
+
void ImmortalEngine::loadSingles(Common::String songName) {
debug("%s", songName.c_str());
}
@@ -1010,6 +940,34 @@ void ImmortalEngine::stopMusic() {
//stopSound();
}
+/*
+ *
+ * ----- -----
+ * ----- 'Pen' related Functions -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::setPen(uint16 penX, uint16 penY) {
+ _penX = penX & kMaskLow;
+ if ((penY & kMaskLow) < 200) {
+ _penY = penY & kMaskLow;
+ }
+
+ else {
+ _penY = penY | kMaskHigh;
+ }
+}
+
+void ImmortalEngine::center() {
+ _penX = ((uint16) 128) - (kObjectWidth / 2);
+}
+
+void ImmortalEngine::carriageReturn() {
+ _penY += 16;
+ _penX = kTextLeft;
+}
+
} // namespace Immortal
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 97319375a06..0a0c9311915 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -99,7 +99,7 @@ void ImmortalEngine::logic() {
_levelOver = false;
if (_level == (_maxLevels-1)) {
- textPrint(kStrYouWin);
+ textPrint(kStrYouWin, 0);
} else {
makeCertificate();
@@ -201,18 +201,6 @@ void ImmortalEngine::doSingleStep() {
}
}
-void ImmortalEngine::setPen(uint16 penX, uint16 penY) {
- _penX = penX & kMaskLow;
- // Is this screen wrap?
- if ((penY & kMaskLow) < 200) {
- _penY = penY & kMaskLow;
- }
-
- else {
- _penY = penY | kMaskHigh;
- }
-}
-
void ImmortalEngine::updateHitGauge() {
/* This HUD (essentially) drawing routine is a bit weird because
* the game was originally meant to have multiple player characters
@@ -250,8 +238,6 @@ void ImmortalEngine::drawGauge(int h) {
*/
int r = 16 - h;
setPen(kGaugeX, kGaugeY);
- // Temporary x value, until it's clear why printchr is designed to add 8 pixels *before* drawing the char
- _penX = 0xFFF0;
h--;
if (h >= 0) {
// This could be written as a regular for loop, but the game thinks of start/stop as different from on
@@ -277,7 +263,7 @@ void ImmortalEngine::drawGauge(int h) {
bool ImmortalEngine::printAnd(Str s) {
// Only ever used by fromOldGame()
// Just prints what it's given and then checks for input
- textPrint(s);
+ textPrint(s, 0);
getInput();
if (_heldAction != kActionNothing) {
return true;
@@ -307,7 +293,7 @@ bool ImmortalEngine::fromOldGame() {
} else {
do {
- if (!textPrint(kStrOldGame)) {
+ if (!textPrint(kStrOldGame, 0)) {
// They choose not to load an old game
return false;
}
@@ -511,7 +497,7 @@ void ImmortalEngine::calcCheckSum(int l, uint8 checksum[]) {
}
bool ImmortalEngine::getCertificate() {
- textPrint(kStrCert);
+ textPrint(kStrCert, 0);
int certLen = 0;
bool entered = false;
int k = 0;
@@ -575,14 +561,14 @@ bool ImmortalEngine::getCertificate() {
}
if (certLen != 0) {
if (certLen < 4) {
- textPrint(kStrBadCertificate);
+ textPrint(kStrBadCertificate, 0);
return false;
}
uint8 checksum[4];
calcCheckSum(certLen, checksum);
for (int i = 0; i < 4; i++) {
if (checksum[i] != _certificate[i]) {
- textPrint(kStrBadCertificate);
+ textPrint(kStrBadCertificate, 0);
return false;
}
}
@@ -603,11 +589,11 @@ void ImmortalEngine::printCertificate() {
*/
char toHex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
- textBeginning(kStrCert);
+ textBeginning(kStrCert, 0);
for (int i = 0; i < _lastCertLen; i++) {
printChr(toHex[_certificate[i]]);
}
- textEnd(kStrCert2);
+ textEnd(kStrCert2, 0);
}
bool ImmortalEngine::isSavedKing() {
@@ -645,7 +631,7 @@ int ImmortalEngine::getLevel() {
void ImmortalEngine::gameOverDisplay() {
_themePaused = true;
- textPrint(kStrGameOver);
+ textPrint(kStrGameOver, 0);
}
void ImmortalEngine::gameOver() {
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 2eb393c2265..450380b51c6 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -38,8 +38,6 @@ void ImmortalEngine::miscInit() {
void ImmortalEngine::setRandomSeed() {}
void ImmortalEngine::getRandom() {}
-void ImmortalEngine::myDelay() {}
-
/*
*
@@ -49,15 +47,357 @@ void ImmortalEngine::myDelay() {}
*
*/
-bool ImmortalEngine::textPrint(Str s) {
- return true;
+// myFadeOut and myFadeIn are just RTS in the source, but they are called quite a lot
+void ImmortalEngine::myFadeOut() {
+ return;
+}
+
+void ImmortalEngine::myFadeIn() {
+ return;
+}
+
+bool ImmortalEngine::textPrint(Str s, int n) {
+ _slowText = 0;
+ _formatted = 0;
+ _collumn = 0;
+ _row = 0;
+ playTextSong();
+ clearScreen();
+ return textSub(s, kTextFadeIn, n);
+}
+
+bool ImmortalEngine::textBeginning(Str s, int n) {
+ _slowText = 0;
+ _formatted = 0;
+ _collumn = 0;
+ _row = 0;
+ playTextSong();
+ clearScreen();
+ return textSub(s, kTextDontFadeIn, n);
+}
+
+void ImmortalEngine::textEnd(Str s, int n) {
+ textSub(s, kTextFadeIn, n);
+}
+
+void ImmortalEngine::textMiddle(Str s, int n) {
+ textSub(s, kTextDontFadeIn, n);
}
-void ImmortalEngine::textSub() {}
-void ImmortalEngine::textEnd(Str s) {}
-void ImmortalEngine::textMiddle(Str s) {}
-void ImmortalEngine::textBeginning(Str s) {}
-void ImmortalEngine::yesNo() {}
+bool ImmortalEngine::textSub(Str s, FadeType f, int n) {
+ bool done = false;
+
+ char chr = 0;
+ int index = 0;
+ Common::String text = _strPtrs[s];
+
+ while (done == false) {
+ switch (text[index]) {
+ case '@':
+ case '=':
+ case char(0):
+ done = true;
+ // This is so the while loop can be a little cleaner
+ index--;
+ break;
+ case '&':
+ textCR();
+ break;
+ case '$':
+ printByte(n);
+ copyToScreen();
+ break;
+ case '_':
+ myFadeIn();
+ _slowText = 1;
+ break;
+ case '<':
+ _slowText = 0;
+ break;
+ case '>':
+ _formatted = 0;
+ break;
+ case '\\':
+ normalFadeOut();
+ break;
+ case '/':
+ slowFadeOut();
+ break;
+ case '|':
+ normalFadeIn();
+ break;
+ case '}':
+ _formatted = 1;
+ break;
+ case ']':
+ myDelay(40);
+ break;
+ case '{':
+ index++;
+ myDelay(text[index]);
+ break;
+ case '*':
+ textPageBreak(text, index);
+ break;
+ case '[':
+ textAutoPageBreak();
+ break;
+ case '#':
+ index++;
+ drawIcon(text[index]);
+ break;
+ case '~':
+ text = _strPtrs[(int)text[index + 1]];
+ index = -1;
+ break;
+ case '^':
+ center();
+ break;
+ case '%':
+ return yesNo();
+ case '+':
+ chr = 0x27;
+ break;
+ case '(':
+ chr = 0x60;
+ break;
+ default:
+ chr = text[index];
+ _collumn++;
+ if (chr == ' ') {
+ if (text[index + 1] == '~') {
+ text = _strPtrs[(int)text[index + 2]];
+ index = -1;
+ }
+ textDoSpace(text, index);
+
+ } else {
+ printChr(chr);
+ // We need this to show up now, not when the frame ends, so we have to update the screen here
+ copyToScreen();
+ if (_slowText != 0) {
+ myDelay(5);
+ switch (chr) {
+ case '?':
+ case ':':
+ myDelay(13);
+ case '.':
+ myDelay(13);
+ case ',':
+ myDelay(13);
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ }
+ if (index == 0xFF) {
+ debug("String too long!");
+ return false;
+ }
+ index++;
+ }
+
+ //debug("printing index: %d", index);
+
+ chr = text[index];
+
+ if (f != kTextFadeIn) {
+ return false;
+ }
+
+ // If we need to display an 'OK' message
+ if (chr != '=') {
+ setPen(_penX, kYesNoY);
+ center();
+ drawIcon(kOkayFrame);
+ copyToScreen();
+ if (_slowText == 0) {
+ myFadeIn();
+ }
+ waitClick();
+ standardBeep();
+ textBounceDelay();
+
+ } else if (_slowText == 0) {
+ myFadeIn();
+ }
+
+ return false;
+}
+
+void ImmortalEngine::textCR() {
+ carriageReturn();
+ _row++;
+ _collumn = 0;
+}
+
+void ImmortalEngine::textPageBreak(Common::String s, int &index) {
+ _collumn = 0;
+ _row = 0;
+ if (_slowText == 0) {
+ myFadeIn();
+ }
+
+ index++;
+ myDelay((int) s[index]);
+ myFadeOut();
+ clearScreen();
+
+ if (_slowText != 0) {
+ myFadeIn();
+ }
+}
+
+void ImmortalEngine::textAutoPageBreak() {
+ _collumn = 0;
+ _row = 0;
+ if (_slowText == 0) {
+ myFadeIn();
+ }
+
+ myDelay(140);
+ myFadeOut();
+ clearScreen();
+
+ if (_slowText != 0) {
+ myFadeIn();
+ }
+}
+
+void ImmortalEngine::textDoSpace(Common::String s, int index) {
+ // If text is formatted, then check if the space between here and the end of the string will fit, if not, use a newline or pagebreak
+ if (_formatted != 0) {
+ bool foundEnd = false;
+ int start = index;
+ while (foundEnd == false) {
+ index++;
+ switch (s[index]) {
+ case '=':
+ case '@':
+ case '%':
+ case '[':
+ case ' ':
+ foundEnd = true;
+ default:
+ break;
+ }
+ }
+ if (((index - start) + _collumn) >= kMaxCollumns) {
+ if (_row < kMaxRows) {
+ textCR();
+
+ } else {
+ textAutoPageBreak();
+ }
+ return;
+ }
+ }
+ printChr(' ');
+}
+
+void ImmortalEngine::textBounceDelay() {
+ Utilities::delay(7);
+}
+
+bool ImmortalEngine::yesNo() {
+ uint8 tyes[9] = {0, 1, 1, 1, 0, 0, 0, 0, 0};
+
+ getInput();
+
+ if (tyes[_heldDirection] == 0) {
+ noOn();
+ _lastYes = 0;
+
+ } else {
+ yesOn();
+ _lastYes = 1;
+ }
+
+ while (buttonPressed() || firePressed()) {
+ // If they have not pressed a button yet, we get the input after a delay
+ Utilities::delay(4);
+ getInput();
+
+ // And then if they have changed direction, we play a sound and update the direction and button gfx
+ if (tyes[_heldDirection] != _lastYes) {
+ _lastYes = tyes[_heldDirection];
+ standardBeep();
+ if (_lastYes == 0) {
+ noOn();
+
+ } else {
+ yesOn();
+ }
+ // Since we need this to show up right during the text sequence, we need to update the screen
+ copyToScreen();
+ }
+ }
+
+ standardBeep();
+ textBounceDelay();
+
+ // In source this is done weirdly so that it can use a result in A, except it never uses that result, so it's just weird.
+ return (!(bool) _lastYes);
+}
+
+void ImmortalEngine::noOn() {
+ // Draw the No icon as on, and the Yes icon as off
+ setPen(kYesNoX1, kYesNoY);
+ drawIcon(kNoIconOn);
+ setPen(kYesNoX2, kYesNoY);
+ drawIcon(kYesIconOff);
+}
+
+void ImmortalEngine::yesOn() {
+ // Draw the No icon as off, and the Yes icon as on
+ setPen(kYesNoX1, kYesNoY);
+ drawIcon(kNoIconOff);
+ setPen(kYesNoX2, kYesNoY);
+ drawIcon(kYesIconOn);
+}
+
+void ImmortalEngine::myDelay(int j) {
+ int type = 0;
+
+ // Update input
+ getInput();
+
+ // 0 = neither button held, 1 = one held, 2 = both held
+ if (_heldAction & kActionButton) {
+ type++;
+ }
+
+ if (_heldAction & kActionFire) {
+ type++;
+ }
+
+ do {
+ // If the button was *pressed* and not held, then skip any delay
+ if (!buttonPressed()) {
+ return;
+ }
+
+ if (!firePressed()) {
+ return;
+ }
+
+ // Otherwise, we delay by different amounts based on what's held down
+ switch (type) {
+ case 1:
+ Utilities::delay4(1);
+ break;
+ case 0:
+ Utilities::delay(1);
+ case 2:
+ default:
+ break;
+ }
+
+ j--;
+ } while (j != 0);
+}
/*
*
@@ -67,8 +407,36 @@ void ImmortalEngine::yesNo() {}
*
*/
-void ImmortalEngine::buttonPressed() {}
-void ImmortalEngine::firePressed() {}
+bool ImmortalEngine::buttonPressed() {
+ // Returns false if the button was pressed, but not held or up
+ getInput();
+
+ if (_heldAction == kActionButton) {
+ // Zero just the button0held bit
+ _myButton &= (0xFF - kButton0Held);
+
+ } else if ((_myButton & kButton0Held) == 0) {
+ _myButton |= kButton0Held;
+ return false;
+ }
+
+ return true;
+}
+
+bool ImmortalEngine::firePressed() {
+ // Returns false if the button was pressed, but not held or up
+ getInput();
+
+ if (_heldAction == kActionFire) {
+ _myButton &= (0xFF - kButton1Held);
+
+ } else if ((_myButton & kButton1Held) == 0) {
+ _myButton |= kButton1Held;
+ return false;
+ }
+
+ return true;
+}
/*
@@ -79,6 +447,19 @@ void ImmortalEngine::firePressed() {}
*
*/
+
+/*
+ *
+ * ----- -----
+ * ----- Sound Related -----
+ * ----- -----
+ *
+ */
+
+void ImmortalEngine::standardBeep() {
+ //playNote(4, 5, 0x4C);
+}
+
} // namespace Immortal
Commit: d47efc743b9f6cda805d185f1a7f16342c5464b5
https://github.com/scummvm/scummvm/commit/d47efc743b9f6cda805d185f1a7f16342c5464b5
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Preliminary loadMazeGraphics() and loadUniv() functions
Changed paths:
engines/immortal/kernal.cpp
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 643ee850878..97a898771db 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -87,15 +87,18 @@ void ImmortalEngine::clearScreen() {
useNormal();
}
+ // Make sure it takes effect right away
copyToScreen();
}
+// These functions are not yet implemented
void ImmortalEngine::mungeBM() {}
void ImmortalEngine::blit() {}
void ImmortalEngine::blit40() {}
void ImmortalEngine::sBlit() {}
void ImmortalEngine::scroll() {}
-void ImmortalEngine::makeMyCNM() {} // ?
+void ImmortalEngine::makeMyCNM() {}
+// -----
void ImmortalEngine::drawIcon(int img) {
superSprite(&_dataSprites[kObject], ((kObjectWidth / 2) + kScreenLeft) + _penX, _penY + (kObjectY + kScreenTop), img, kScreenBMW, _screenBuff, 0, 200);
@@ -150,6 +153,7 @@ void ImmortalEngine::addSprites() {
int tmpNum = _num2DrawItems;
for (int i = 0; i < kMaxSprites; i++) {
// If the sprite is active
+ // This is commented out for testing until the issue with the function is resolved
if (/*_sprites[i]._on*/0 == 1) {
// If sprite X is an odd number???
if ((_sprites[i]._X & 1) != 0) {
@@ -176,7 +180,7 @@ void ImmortalEngine::addSprites() {
}
DataSprite *tempD = _sprites[i]._dSprite;
- debug("what sprite is this: %d %d %d", i, _sprites[i]._image, _sprites[i]._dSprite->_images.size());
+ //debug("what sprite is this: %d %d %d", i, _sprites[i]._image, _sprites[i]._dSprite->_images.size());
Image *tempImg = &(tempD->_images[_sprites[i]._image]);
int sx = ((_sprites[i]._X + tempImg->_deltaX) - tempD->_cenX) - _myViewPortX;
int sy = ((_sprites[i]._Y + tempImg->_deltaY) - tempD->_cenY) - _myViewPortY;
@@ -211,7 +215,7 @@ void ImmortalEngine::addSprites() {
void ImmortalEngine::sortDrawItems() {
/* Just an implementation of bubble sort.
- * Sorting largest to smallest entry, simply
+ * Sorting largest to smallest entry, by simply
* swapping every two entries if they are not in order.
*/
@@ -333,11 +337,11 @@ void ImmortalEngine::drawItems() {
}
void ImmortalEngine::backspace() {
- // Just moves the drawing position back by a char, and then draws an empty rect there (I think)
+ // Just moves the drawing position back by a char, and then draws an empty rect there
_penX -= 8;
//rect(_penX + 32, 40, 8, 16, 0);
- // The Y is set here presumably because it's only used for the certificate
+ // The Y is hardcoded here presumably because it's only used for the certificate
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 8; x++) {
_screenBuff[((y + 40) * kResH) + (x + (_penX + 32))] = 0;
@@ -424,7 +428,7 @@ void ImmortalEngine::printChr(char c) {
superSprite(&_dataSprites[kFont], x, y, (int) c, kScreenBMW, _screenBuff, kSuperTop, kSuperBottom);
- // Single quote?
+ // Back tick quote
if (c == 0x27) {
_penX -= 2;
}
@@ -460,7 +464,19 @@ void ImmortalEngine::cycleFreeAll() {
}
void ImmortalEngine::loadMazeGraphics(int m) {
- //setColors();
+ char mazeNum = m + '0';
+ loadUniv(mazeNum);
+ setColors(_palUniv);
+}
+
+void ImmortalEngine::loadUniv(char mazeNum) {
+ Common::String sCNM = "MAZE" + Common::String(mazeNum) + ".CNM";
+ Common::SeekableReadStream *mazeCNM = loadIFF(sCNM);
+ debug("Size of maze CNM: %ld", mazeCNM->size());
+
+ Common::String sUNV = "MAZE" + Common::String(mazeNum) + ".UNV";
+ Common::SeekableReadStream *mazeUNV = loadIFF(sUNV);
+ debug("Size of maze UNV: %ld", mazeUNV->size());
}
void ImmortalEngine::loadSprites() {
@@ -562,7 +578,7 @@ void ImmortalEngine::loadWindow() {
f.close();
} else {
- // Should probably give an error or something here
+ // Should probably give an error here
debug("oh nose :(");
}
}
@@ -577,7 +593,7 @@ void ImmortalEngine::loadFont() {
_dataSprites[kFont] = d;
} else {
- debug("file doesn't exist?!");
+ debug("file doesn't exist!");
}
}
@@ -726,9 +742,9 @@ void ImmortalEngine::fadePal(uint16 pal[], int count, uint16 target[]) {
* kept, this is a direct translation of the bit manipulation sequence.
*/
uint16 maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
- 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
uint16 result;
uint16 temp;
@@ -826,10 +842,6 @@ void ImmortalEngine::useDim() {
*
*/
-void ImmortalEngine::userIO() {}
-void ImmortalEngine::pollKeys() {}
-void ImmortalEngine::noNetwork() {}
-
void ImmortalEngine::waitKey() {
bool wait = true;
while (wait == true) {
@@ -852,14 +864,17 @@ void ImmortalEngine::waitClick() {
}
}
+// These functions are not yet implemented
void ImmortalEngine::blit8() {}
+void ImmortalEngine::addKeyBuffer() {}
+void ImmortalEngine::clearKeyBuff() {}
+void ImmortalEngine::userIO() {}
+void ImmortalEngine::pollKeys() {}
+void ImmortalEngine::noNetwork() {}
bool ImmortalEngine::getInput() {
return true;
}
-
-void ImmortalEngine::addKeyBuffer() {}
-void ImmortalEngine::clearKeyBuff() {}
-
+// ----
/*
*
@@ -917,18 +932,15 @@ void ImmortalEngine::musicUnPause(int sID) {}
// ***
Song ImmortalEngine::getPlaying() {
+ // Temporary value
return kSongMaze;
}
-void ImmortalEngine::playMazeSong() {
-}
-
-void ImmortalEngine::playCombatSong() {
-}
-
-void ImmortalEngine::playTextSong() {
-
-}
+// These functions are not yet implemented
+void ImmortalEngine::playMazeSong() {}
+void ImmortalEngine::playCombatSong() {}
+void ImmortalEngine::playTextSong() {}
+// ----
void ImmortalEngine::loadSingles(Common::String songName) {
debug("%s", songName.c_str());
@@ -948,6 +960,7 @@ void ImmortalEngine::stopMusic() {
*
*/
+// This sets the pen to a given x,y point
void ImmortalEngine::setPen(uint16 penX, uint16 penY) {
_penX = penX & kMaskLow;
if ((penY & kMaskLow) < 200) {
@@ -963,6 +976,7 @@ void ImmortalEngine::center() {
_penX = ((uint16) 128) - (kObjectWidth / 2);
}
+// Reset the X position and move the Y position down by 16 pixels
void ImmortalEngine::carriageReturn() {
_penY += 16;
_penX = kTextLeft;
Commit: a188dced1516eeec2bab993050e0f8bf4bf41d46
https://github.com/scummvm/scummvm/commit/a188dced1516eeec2bab993050e0f8bf4bf41d46
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Formatting and comment adjustments across all several files
Changed paths:
engines/immortal/cycle.cpp
engines/immortal/disk.cpp
engines/immortal/disk.h
engines/immortal/door.cpp
engines/immortal/drawChr.cpp
engines/immortal/flameSet.cpp
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/logic.cpp
engines/immortal/misc.cpp
engines/immortal/module.mk
engines/immortal/room.h
engines/immortal/sprite_list.h
engines/immortal/sprites.cpp
engines/immortal/story.cpp
engines/immortal/story.h
engines/immortal/utilities.cpp
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index bec8a6eba79..7d505a7a0fc 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -88,12 +88,11 @@ int Room::cycleGetFrame(int c) {
* This is essentially self-modifying code, and it saves 2 bytes of DP memory over the traditional
* STA DP : LDA (DP)
*/
- //debug("%d", _cycPtrs[_cycles[c]._cycList]._frames[_cycles[c]._index]);
return g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._frames[g_immortal->_cycles[c]._index];
}
int Room::cycleGetNumFrames(int c) {
- // Why in the world is this not kept as a property of the cycle? We have to calculate the size of the array each time
+ // For whatever reason, this is not a property of the cycle, so it has to be re-calculated each time
int index = 0;
while (g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._frames[index] != -1) {
index++;
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index 340a07d409e..be6103e6c30 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -295,7 +295,7 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
// It is a regular file if (dead < file type < pascal) and the file has a size
if ((kFileTypeDead < fileEntry._type) && (fileEntry._type < kFileTypePascal) && (fileEntry._eof > 0)) {
Common::String fileName = path + fileEntry._name;
- debug("%s", fileName.c_str());
+ //debug("%s", fileName.c_str());
ProDOSFile *currFile = new ProDOSFile(fileEntry._name, fileEntry._type, fileEntry._totalBlocks, fileEntry._eof, fileEntry._blockPtr, &_disk);
_files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
@@ -305,7 +305,7 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
} else if (fileEntry._type == kFileTypeSubDir) {
_disk.seek(fileEntry._blockPtr * kBlockSize);
- debug("--- diving into a subdirectory ---");
+ //debug("--- diving into a subdirectory ---");
uint16 subP = _disk.readUint16LE();
uint16 subN = _disk.readUint16LE();
@@ -316,7 +316,7 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
Common::String subPath = Common::String(path + subHead._name + '/');
searchDirectory(&subHead, subP, subN, path);
- debug("--- surfacing to parent directory ---");
+ //debug("--- surfacing to parent directory ---");
_disk.seek(currPos);
}
}
@@ -425,7 +425,6 @@ Common::SeekableReadStream *ProDOSDisk::createReadStreamForMember(const Common::
return nullptr;
}
Common::SharedPtr<ProDOSFile> f = _files.getValOrDefault(name);
- f->printInfo();
return f->createReadStream();
}
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index 2d581c969c4..57c057a8eb2 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -95,12 +95,12 @@ public:
void printInfo();
private:
- char _name[16];
- uint8 _type; // As defined by enum FileType
- uint16 _blockPtr; // Block index in volume of index block or data
- uint16 _totalBlocks;
- uint32 _eof; // End Of File, used generally as size (exception being sparse files)
-Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
+ char _name[16];
+ uint8 _type; // As defined by enum FileType
+ uint16 _blockPtr; // Block index in volume of index block or data
+ uint16 _totalBlocks;
+ uint32 _eof; // End Of File, used generally as size (exception being sparse files)
+ Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
};
/* This class defines the entire disk volume. Upon using the open() method,
@@ -126,12 +126,12 @@ public:
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
- byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
- byte _loader2[kBlockSize];
-Common::String _name; // Name of volume
- Common::File _disk; // The volume file itself
- int _volBlocks; // Total blocks in volume
- byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
+ byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
+ byte _loader2[kBlockSize];
+Common::String _name; // Name of volume
+ Common::File _disk; // The volume file itself
+ int _volBlocks; // Total blocks in volume
+ byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDOSFile
struct Date {
diff --git a/engines/immortal/door.cpp b/engines/immortal/door.cpp
index a97820658bc..ee905d84e98 100644
--- a/engines/immortal/door.cpp
+++ b/engines/immortal/door.cpp
@@ -20,7 +20,6 @@
*/
/* [Alternate Name: Door Processing]
- * --- What is a Door ---
*/
#include "immortal/immortal.h"
@@ -28,10 +27,10 @@
namespace Immortal {
enum DoorMask {
- kDoorXMask = 0x1f, // Only relevant for extracting the data from the compressed bytes in the story record
- kDoorYMask = 0x1f,
+ kDoorXMask = 0x1f, // Only relevant for extracting the data from the compressed bytes in the story record
+ kDoorYMask = 0x1f,
kDoorFullMask = 0x40,
- kDoorOnMask = 0x60
+ kDoorOnMask = 0x60
};
enum DoorIs {
@@ -50,10 +49,6 @@ enum DoorSide {
kDoorTopBottom = 20
};
-void ImmortalEngine::doorOpenSecret() {}
-void ImmortalEngine::doorCloseSecret() {}
-void ImmortalEngine::doorInit() {}
-void ImmortalEngine::doorClrLock() {}
void ImmortalEngine::doorNew(SDoor door) {
Door d;
d._x = door._x;
@@ -65,32 +60,46 @@ void ImmortalEngine::doorNew(SDoor door) {
_doors.push_back(d);
}
-void ImmortalEngine::doorDrawAll() {}
-void ImmortalEngine::doorOnDoorMat() {}
- int ImmortalEngine::findDoorTop(int x, int y) {
+
+int ImmortalEngine::findDoorTop(int x, int y) {
return 0;
- }
- int ImmortalEngine::findDoor(int x, int y) {
+}
+
+int ImmortalEngine::findDoor(int x, int y) {
return 0;
- }
+}
+
bool ImmortalEngine::doLockStuff(int d, MonsterID m, int top) {
return true;
}
+
bool ImmortalEngine::inDoorTop(int x, int y, MonsterID m) {
return true;
}
+
bool ImmortalEngine::inDoor(int x, int y, MonsterID m) {
return true;
}
- int ImmortalEngine::doorDoStep(MonsterID m, int d, int index) {
+
+int ImmortalEngine::doorDoStep(MonsterID m, int d, int index) {
return 0;
- }
- int ImmortalEngine::doorSetOn(int d) {
+}
+
+int ImmortalEngine::doorSetOn(int d) {
return 0;
- }
- int ImmortalEngine::doorComeOut(MonsterID m) {
+}
+
+int ImmortalEngine::doorComeOut(MonsterID m) {
return 0;
- }
+}
+
+// These functions are not yet implemented
void ImmortalEngine::doorSetLadders(MonsterID m) {}
+void ImmortalEngine::doorDrawAll() {}
+void ImmortalEngine::doorOnDoorMat() {}
+void ImmortalEngine::doorOpenSecret() {}
+void ImmortalEngine::doorCloseSecret() {}
+void ImmortalEngine::doorInit() {}
+void ImmortalEngine::doorClrLock() {}
} // namespace immortal
\ No newline at end of file
diff --git a/engines/immortal/drawChr.cpp b/engines/immortal/drawChr.cpp
index 7ef77391543..9a8b8e711cc 100644
--- a/engines/immortal/drawChr.cpp
+++ b/engines/immortal/drawChr.cpp
@@ -23,9 +23,11 @@
namespace Immortal {
+// These functions are not yet implemented
int ImmortalEngine::mungeCBM(int numChrs) {
return 0;
}
+
void ImmortalEngine::storeAddr() {}
void ImmortalEngine::mungeSolid() {}
void ImmortalEngine::mungeLRHC() {}
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index 32e973c0734..89732a91fde 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -47,6 +47,7 @@ void Room::flameFreeAll() {
void Room::flameDrawAll(uint16 vX, uint16 vY) {
for (int i = 0; i < _fset.size(); i++) {
+ // For every flame in the room, add the sprite to the sprite table
univAddSprite(vX, vY, _fset[i]._x, _fset[i]._y, g_immortal->_cycPtrs[g_immortal->_cycles[_fset[i]._c]._cycList]._sName, cycleGetFrame(_fset[i]._c), 0);
if (cycleAdvance(_fset[i]._c) == true) {
cycleFree(_fset[i]._c);
@@ -56,7 +57,7 @@ void Room::flameDrawAll(uint16 vX, uint16 vY) {
}
bool Room::roomLighted() {
- // Just for now, we say it's always lit
+ // For testing purposes, we say it's always lit
return true;
// Very simple, just checks every torch and if any of them are lit, we say the room is lit
@@ -94,7 +95,7 @@ void Room::flameSetRoom(Common::Array<SFlame> allFlames) {
f._x = allFlames[i]._x;
f._y = allFlames[i]._y;
f._c = flameGetCyc(&f, (0 | _candleTmp));
- debug("made flame, cyc = %d", f._c);
+ //debug("made flame, cyc = %d", f._c);
_fset.push_back(f);
}
_candleTmp = 1;
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index e3ce80031ed..a47301a3cf2 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -55,16 +55,15 @@ uint16 ImmortalEngine::xba(uint16 ab) {
/* XBA in 65816 swaps the low and high bits of a given word in A.
* This is used very frequently, so this function just makes
* initial translation a little more straightforward. Eventually,
- * logic should be refactored to not require this.
+ * code should be refactored to not require this.
*/
return ((ab & kMaskLow) << 8) | ((ab & kMaskHigh) >> 8);
}
uint16 ImmortalEngine::rol(uint16 ab, int n) {
- /* Oops, another opcode that doesn't have a nice translation.
- * This just replicates bit rotation because apparently C
- * doesn't have this natively??? This assumes a 16 bit
- * unsigned int because that's all we need for the 65816.
+ /* This just replicates bit rotation, and it
+ * assumes a 16 bit unsigned int because that's all
+ * we need for the 65816.
*/
return (ab << n) | (ab >> (-n & 15));
}
@@ -150,7 +149,7 @@ Common::Error ImmortalEngine::run() {
return Common::kPathDoesNotExist;
}
- //Main:
+ // Main:
_zero = 0;
_draw = 1;
_usingNormal = 1;
@@ -163,7 +162,7 @@ Common::Error ImmortalEngine::run() {
// This is the equivalent of Main->InitGraphics->MyClearScreen in Driver
useNormal(); // The first palette will be the default
- loadFont(); // Load the font sprite
+ loadFont(); // Load the font sprites
loadWindow(); // Load the window background
loadSingles("Song A"); // Music
loadSprites(); // Get all the sprite data into memory
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index fce55cf9ea6..7ff63fc7591 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -226,9 +226,9 @@ public:
const uint16 kLCutaway = 4;
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
- kChrH2, kChrH, kChrH2, kChrH2, kChr0,
- kChr0, kChrH2, kChrH, kChrH2, kChrH2,
- kChrH2, kChrH, kChrH2, kChrH2};
+ kChrH2, kChrH, kChrH2, kChrH2, kChr0,
+ kChr0, kChrH2, kChrH, kChrH2, kChrH2,
+ kChrH2, kChrH, kChrH2, kChrH2};
const uint16 kChrMask[19] = {kChr0, kChr0, kChr0, kChr0,
kChrR, kChrL, kChr0, kChrL,
@@ -240,8 +240,8 @@ public:
0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0};
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0};
// Disk offsets
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
@@ -347,7 +347,7 @@ public:
uint8 _secretDelta = 0;
// Debug members
- bool _singleStep = false; // Flag for _singleStep mode
+ bool _singleStep = false; // Flag for _singleStep mode
// Input members
int _pressedAction = 0;
@@ -365,11 +365,11 @@ public:
// Music members
Song _playing; // Currently playing song
- int _themeID = 0; // Not sure yet tbh
- int _combatID = 0;
+ int _themeID = 0; // Not sure yet tbh
+ int _combatID = 0;
// Asset members
- int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
+ int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteName
Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
Cycle _cycles[kMaxCycles];
@@ -384,27 +384,27 @@ public:
// Screen members
byte *_screenBuff; // The final buffer that will transfer to the screen
- uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
- uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
- uint16 _myModLCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
- uint16 _columnX[kViewPortCW + 1];
- uint16 _columnTop[kViewPortCW + 1];
- uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
+ uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+ uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+ uint16 _myModLCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+ uint16 _columnX[kViewPortCW + 1];
+ uint16 _columnTop[kViewPortCW + 1];
+ uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
uint16 _tIndex[kMaxDrawItems];
uint16 _tPriority[kMaxDrawItems];
- uint16 _viewPortX = 0;
- uint16 _viewPortY = 0;
- uint16 _myViewPortX = 0; // Probably mirror of viewportX
- uint16 _myViewPortY = 0;
- int _lastGauge = 0; // Mirror for player health, used to update health gauge display
- uint16 _lastBMW = 0; // Mirrors used to determine where bitmap width needs to be re-calculated
- uint16 _lastY = 0;
- uint16 _lastPoint = 0;
- uint16 _penX = 0; // Basically where in the screen we are currently drawing
- uint16 _penY = 0;
- uint16 _myUnivPointX = 0;
- uint16 _myUnivPointY = 0;
- int _num2DrawItems = 0;
+ uint16 _viewPortX = 0;
+ uint16 _viewPortY = 0;
+ uint16 _myViewPortX = 0; // Probably mirror of viewportX
+ uint16 _myViewPortY = 0;
+ int _lastGauge = 0; // Mirror for player health, used to update health gauge display
+ uint16 _lastBMW = 0; // Mirrors used to determine where bitmap width needs to be re-calculated
+ uint16 _lastY = 0;
+ uint16 _lastPoint = 0;
+ uint16 _penX = 0; // Basically where in the screen we are currently drawing
+ uint16 _penY = 0;
+ uint16 _myUnivPointX = 0;
+ uint16 _myUnivPointY = 0;
+ int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
GenericSprite _genSprites[6];
@@ -412,6 +412,7 @@ GenericSprite _genSprites[6];
int _dontResetColors = 0; // Not sure yet
bool _usingNormal = 0; // Whether the palette is using normal
bool _dim = 0; // Whether the palette is dim
+ uint16 _palUniv[16];
uint16 _palDefault[16];
uint16 _palWhite[16];
uint16 _palBlack[16];
@@ -489,6 +490,7 @@ GenericSprite _genSprites[6];
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
void initStoryStatic(); // Sets up all of the global static story elements
+ void loadUniv(char mazeNum);
void loadMazeGraphics(int m); // Creates a universe with a maze
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 61b17098756..47822b23d18 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -43,7 +43,7 @@ void ImmortalEngine::levelNew(int l) {
levelStory(l);
if (kLevelToMaze[l] != _lastLevelLoaded) {
_lastLevelLoaded = kLevelToMaze[l];
- //loadMazeGraphics(l);
+ loadMazeGraphics(l);
}
if (_level != _lastSongLoaded) {
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 0a0c9311915..20106566dd4 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -519,7 +519,6 @@ bool ImmortalEngine::getCertificate() {
} else {
// The input was a key
-
if (certLen != kMaxCertificate) {
if ((k >= 'a') && (k < '{')) {
k -= 0x20;
@@ -532,7 +531,6 @@ bool ImmortalEngine::getCertificate() {
else {
if (k < 'A') {
- // Terrible, I know. But this seems to be the logic.
continue;
}
@@ -584,8 +582,7 @@ void ImmortalEngine::printCertificate() {
* this one is nice and simple. You could also
* just add the appropriate offset for the letters,
* but grabbing it from a table is faster and doesn't
- * use a lot of space (especially if it's used anywhere else).
- * Why doesn't the game use rom table indexing like this more often?
+ * use a lot of space (especially if it's used anywhere else)
*/
char toHex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
@@ -614,7 +611,7 @@ bool ImmortalEngine::isSavedAna() {
/*
- * Functions that don't really need to be functions
+ * These functions don't really need to be functions
*/
void ImmortalEngine::setGameFlags(uint16 f) {
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 450380b51c6..38fe11f6cc1 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -198,9 +198,7 @@ bool ImmortalEngine::textSub(Str s, FadeType f, int n) {
}
index++;
}
-
- //debug("printing index: %d", index);
-
+
chr = text[index];
if (f != kTextFadeIn) {
diff --git a/engines/immortal/module.mk b/engines/immortal/module.mk
index 3812d27981c..3d98cfe2729 100644
--- a/engines/immortal/module.mk
+++ b/engines/immortal/module.mk
@@ -12,8 +12,8 @@ MODULE_OBJS = \
misc.o \
cycle.o \
drawChr.o \
- level.o \
- story.o \
+ level.o \
+ story.o \
room.o \
flameSet.o \
univ.o \
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 7cc9937117b..97c25fba2f1 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -119,20 +119,21 @@ public:
const uint8 kLightTorchX = 10;
const uint8 kMaxFlameCycs = 16;
-Common::Array<SCycle> _cycPtrs;
-Common::Array<Flame> _fset;
-Common::Array<Monster> _monsters;
-Common::Array<Object> _objects;
-
- RoomFlag _flags;
- uint8 _xPos = 0;
- uint8 _yPos = 0;
- uint8 _holeRoom = 0;
- uint8 _holeCellX = 0;
- uint8 _holeCellY = 0;
- uint8 _candleTmp = 0; // Special case for candle in maze 0
- uint8 _numFlames = 0;
- uint8 _numInRoom = 0;
+ Common::Array<SCycle> _cycPtrs;
+ Common::Array<Flame> _fset;
+ Common::Array<Monster> _monsters;
+ Common::Array<Object> _objects;
+
+ RoomFlag _flags;
+
+ uint8 _xPos = 0;
+ uint8 _yPos = 0;
+ uint8 _holeRoom = 0;
+ uint8 _holeCellX = 0;
+ uint8 _holeCellY = 0;
+ uint8 _candleTmp = 0; // Special case for candle in maze 0
+ uint8 _numFlames = 0;
+ uint8 _numInRoom = 0;
/*
* --- Methods ---
@@ -171,7 +172,7 @@ Common::Array<Object> _objects;
*/
// Init
- int cycleNew(CycID id); // Adds a cycle to the current list
+ int cycleNew(CycID id); // Adds a cycle to the current list
void cycleFree(int c);
// Getters
@@ -185,7 +186,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
// Misc
bool cycleAdvance(int c);
- CycID getCycList(int c);
+ CycID getCycList(int c);
/* Unneccessary cycle functions
void cycleInit();
@@ -204,7 +205,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
void lightTorch(uint8 x, uint8 y);
void flameFreeAll();
void flameSetRoom(Common::Array<SFlame>);
- int flameGetCyc(Flame *f, int first);
+ int flameGetCyc(Flame *f, int first);
/*
* [bullet.cpp] Functions from Bullet.GS
@@ -221,7 +222,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
* [Univ.cpp] Functions from Univ.GS
*/
- void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s, int img, uint16 p);
+ void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s, int img, uint16 p);
};
} // namespace immortal
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index d80c37fa4aa..26d6fb5ab6e 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -32,25 +32,25 @@ struct Image {
uint16 _deltaY;
uint16 _rectW;
uint16 _rectH;
-Common::Array<uint16> _scanWidth;
-Common::Array<uint16> _deltaPos;
-CArray2D<byte> _bitmap;
+ Common::Array<uint16> _scanWidth;
+ Common::Array<uint16> _deltaPos;
+ CArray2D<byte> _bitmap;
};
struct DataSprite {
uint16 _cenX; // These are the base center positions
uint16 _cenY;
uint16 _numImages;
-Common::Array<Image> _images;
+ Common::Array<Image> _images;
};
struct Sprite {
- int _image; // Index of _dSprite._frames[]
+ int _image; // Index of _dSprite._images[]
uint16 _X;
uint16 _Y;
uint16 _on; // 1 = active
uint16 _priority;
-DataSprite *_dSprite;
+ DataSprite *_dSprite;
};
enum SpriteFrame {
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 31969a8e0a1..c8c61f8ade5 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -63,7 +63,7 @@ namespace Immortal {
// This function is basically setSpriteCenter + getSpriteInfo from the source
void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY) {
- // We set the center X and Y, for some reason
+ // We set the center X and Y
d->_cenX = cenX;
d->_cenY = cenY;
@@ -76,7 +76,6 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
uint16 numImages = f->readUint16LE();
d->_numImages = numImages;
- //debug("Number of Frames: %d", numImages);
// Only here for dragon, but just in case, it's a high number so it should catch others
if (numImages >= 0x0200) {
@@ -91,7 +90,7 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
f->seek(index + (i * 2));
int ptrFrame = f->readUint16LE();
f->seek(ptrFrame);
- newImage._deltaX = f->readUint16LE() << 1; // the ASL might not be required, depending on whether the data is actually in bytes or pixels <-- this also might not be used in the game anyway? Lol
+ newImage._deltaX = f->readUint16LE() << 1; // This member does not seem to be used in the actual game, and it is not clear whether it needs the << 1 or if that was fixed before release
newImage._deltaY = f->readUint16LE();
newImage._rectW = f->readUint16LE();
newImage._rectH = f->readUint16LE();
@@ -127,9 +126,10 @@ bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skip
_lastY = pointY;
if (pointY < kMaskNeg) {
// For the Apple IIGS, pointY in pixels needed to be converted to bytes. For us, it's the other way around, we need bmw in pixels
+ // This should probably use mult16() instead of *
_lastPoint = pointY * (bmw);
} else {
- // Screen wrapping??
+ // Screen wrapping?
uint16 temp = (0 - pointY) + 1;
_lastPoint = temp * bmw;
_lastPoint = 0 - _lastPoint;
@@ -175,8 +175,8 @@ void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skip
* that was indexed by the pixel itself, and used to find what nyble needed
* to be masked. However we are using a slightly different kind of screen buffer,
* and so I chose a more traditional method. Likewise, alignement was
- * relevant for the source, but is not relevant here (thankfully, considering
- * how confusing sprite drawing is when not an even position).
+ * relevant for the source, but is not relevant here, and the sprite drawing
+ * is not accomplished by indexed a set of code blocks.
*/
byte pixel1 = 0;
byte pixel2 = 0;
@@ -218,7 +218,7 @@ void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skip
}
void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom) {
- // Main image construction routine
+ // Main sprite image construction routine
// For the Apple IIGS, the bmw is in bytes, but for us it needs to be the reverse, in pixels
bmw <<= 1;
@@ -229,7 +229,7 @@ void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 poin
uint16 height = dSprite->_images[img]._rectH;
uint16 skipY = 0;
- uint16 pointIndex = 0; // This is screen and screen + 2 in the source
+ uint16 pointIndex = 0; // This is 'screen' and 'screen + 2' in the source
pointX -= cenX;
pointY -= cenY;
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index 51dd0443784..c0b39350a10 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -32,7 +32,8 @@
* objects, and everything in the rooms.
*/
-/* UNIVAT 1024,480, 1152, 464, \-1, -1, zip,level1Ladders, rooma, 704/64, 544/32\
+/* These are the UNIVAT for each Story entry
+ UNIVAT 1024,480, 1152, 464, \-1, -1, zip,level1Ladders, rooma, 704/64, 544/32\
UNIVAT 304, 448, 472+32, 500+16, \-1, -1, zip,level12Ladders, -1, 0, 0\
UNIVAT 600, 450, 560, 598, \-1, r2.b+(16*r2.a), zip,level3Ladders, r2.b, 640/64, 576/32\
UNIVAT 120, 540, 188, 584, \-1, -1, zip,level4Ladders, -1, 0, 0\
@@ -149,7 +150,6 @@ void ImmortalEngine::initStoryStatic() {
"This room resembles part&of the map.@"};
_strPtrs = s;
- // Scope, amirite?
Common::Array<int> cyc0{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
Common::Array<int> cyc1{15,16,17,18,19,20,21,22,-1};
Common::Array<int> cyc2{0,1,2,-1};
@@ -158,9 +158,9 @@ void ImmortalEngine::initStoryStatic() {
Common::Array<int> cyc5{11,12,13,14,15,-1};
Common::Array<int> cyc6{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,-1};
Common::Array<int> cyc7{0,1,2,3,4,-1};
- Common::Array<int> cyc8{5,1+5,2+5,3+5,4+5,-1};
- Common::Array<int> cyc9{10,1+10,2+10,3+10,4+10,-1};
- Common::Array<int> cyc10{15,1+15,2+15,3+15,4+15,-1};
+ Common::Array<int> cyc8{5,1 + 5,2 + 5,3 + 5,4 + 5,-1};
+ Common::Array<int> cyc9{10,1 + 10,2 + 10,3 + 10,4 + 10,-1};
+ Common::Array<int> cyc10{15,1 + 15,2 + 15,3 + 15,4 + 15,-1};
Common::Array<int> cyc11{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
Common::Array<int> cyc12{0,1,2,3,4,5,6,7,8,9,-1};
Common::Array<int> cyc13{0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, -1};
@@ -175,10 +175,10 @@ void ImmortalEngine::initStoryStatic() {
Common::Array<int> cyc22{0,17,18,19,3,-1};
Common::Array<int> cyc23{0,1,-1};
Common::Array<int> cyc24{28,28,28,28,-1};
- Common::Array<int> cyc25{15,16,15,16,15,1+15,1+15,-1};
- Common::Array<int> cyc26{10+15,11+15,12+15,13+15,14+15,15+15,16+15,-1};
- Common::Array<int> cyc27{2+15,3+15,4+15,5+15,-1};
- Common::Array<int> cyc28{6+15,7+15,8+15,9+15,-1};
+ Common::Array<int> cyc25{15,16,15,16,15,1 + 15,1 + 15,-1};
+ Common::Array<int> cyc26{10 + 15,11+ 15,12 + 15,13 + 15,14 + 15,15 + 15,16 + 15,-1};
+ Common::Array<int> cyc27{2 + 15,3 + 15,4 + 15,5 + 15,-1};
+ Common::Array<int> cyc28{6 + 15,7 + 15,8 + 15,9 + 15,-1};
Common::Array<int> cyc29{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
Common::Array<int> cyc30{0,1,2,3,3,3,3,4,5,6,-1};
Common::Array<int> cyc31{0,1,2,3,4,5,6,7,8,-1};
@@ -277,9 +277,9 @@ void ImmortalEngine::initStoryDynamic() {
/* All of the doors
*/
Common::Array<SDoor> doors{SDoor(0, 704, 224, 0, 2, false), SDoor(1, 576, 352, 4, 0, true),
- SDoor(1, 704, 96, 2, 1, false), SDoor(1, 960, 128, 7, 2, false),
- SDoor(1, 1088,160, 3, 7, false), SDoor(1, 1088,320, 6, 3, false),
- SDoor(1, 896, 416, 4, 3, false)};
+ SDoor(1, 704, 96, 2, 1, false), SDoor(1, 960, 128, 7, 2, false),
+ SDoor(1, 1088,160, 3, 7, false), SDoor(1, 1088,320, 6, 3, false),
+ SDoor(1, 896, 416, 4, 3, false)};
_stories[0]._doors = doors;
/* All of the flames
@@ -309,23 +309,23 @@ void ImmortalEngine::initStoryDynamic() {
Common::Array<SObj> noObj{};
Common::Array<SObj> o5{SObj(kZip, kZip, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o5Traps),
- SObj(459, 379, kTypeCoin, kRingFrame, kObjNone, noTraps),
- SObj(446, 327, kTypeWowCharm, kScrollFrame, kObjNone, noTraps)};
+ SObj(459, 379, kTypeCoin, kRingFrame, kObjNone, noTraps),
+ SObj(446, 327, kTypeWowCharm, kScrollFrame, kObjNone, noTraps)};
Common::Array<SObj> o7{SObj(145, 138, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o7Traps)};
Common::Array<SObj> o8{SObj(kZip, kZip, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o8Traps)};
Common::Array<SObj> o9{SObj(1052, 309, kTypeDead, kDeadGoblinFrame, kObjIsChest + kObjIsOnGround, noTraps),
SObj(kZip, kZip, kTypeFireBall, kScrollFrame, kObjUsesFireButton, noTraps),
SObj(128, 464, kTypeDunRing, kRingFrame, 0, noTraps),
SObj(837, 421, kTypeChest, kChest0Frame, kObjIsChest, noTraps),
- SObj(kZip, kZip, kTypeDeathMap, kScrollFrame, 0, noTraps),
- SObj(597, 457, kTypeWater, kVaseFrame, 0, noTraps),
- SObj(kZip, kZip, kTypeSpores, kSporesFrame, 0, noTraps),
- SObj(kZip, kZip, kTypeWormFood, kNoFrame, 0, noTraps),
- SObj(205, 158, kTypeChestKey, kKeyFrame, 0, noTraps)};
+ SObj(kZip, kZip, kTypeDeathMap, kScrollFrame, 0, noTraps),
+ SObj(597, 457, kTypeWater, kVaseFrame, 0, noTraps),
+ SObj(kZip, kZip, kTypeSpores, kSporesFrame, 0, noTraps),
+ SObj(kZip, kZip, kTypeWormFood, kNoFrame, 0, noTraps),
+ SObj(205, 158, kTypeChestKey, kKeyFrame, 0, noTraps)};
Common::Array<SObj> oE{SObj(1184, 426, kTypePhant, kAltarFrame, 0, noTraps),
- SObj(145, 138, kTypeGold, kNoFrame, kObjIsRunning, noTraps),
- SObj(671, 461, kTypeHay, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps),
- SObj(780, 508, kTypeBeam, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps)};
+ SObj(145, 138, kTypeGold, kNoFrame, kObjIsRunning, noTraps),
+ SObj(671, 461, kTypeHay, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps),
+ SObj(780, 508, kTypeBeam, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps)};
CArray2D<SObj> objects{o5, o7, o8, o9, noObj, noObj, oE, noObj};
_stories[0]._objects = objects;
@@ -341,15 +341,19 @@ void ImmortalEngine::initStoryDynamic() {
Common::Array<SMonster> noMonst{};
Common::Array<SMonster> m5{SMonster(448, 344, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow),
- SMonster(590, 381, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow)};
+ SMonster(590, 381, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow)};
Common::Array<SMonster> m9{SMonster(1106, 258, 3, kMonstPlayer, kMonstA + kMonstIsEngage, progEasy, kGoblin0),
SMonster(832, 364, 10, kMonstA, kMonstB + kMonstIsPoss, progUlindor, kUlindor3),
- SMonster(838, 370, 15, kMonstPlayer, kMonstA + kMonstIsEngage, progGoblin5, kGoblin7)};
+ SMonster(838, 370, 15, kMonstPlayer, kMonstA + kMonstIsEngage, progGoblin5, kGoblin7)};
Common::Array<SMonster> mE{SMonster(1136, 464, 15, kMonstMonster, kMonstPlayer + kMonstIsEngage, progPlayer, kWizard0)};
Common::Array<SMonster> mF{SMonster(1182, 116, 5, kMonstPlayer, kMonstA + kMonstIsEngage, progWill2, kGoblin5)};
CArray2D<SMonster> monsters{m5, noMonst, noMonst, m9, noMonst, noMonst, mE, mF};
_stories[0]._monsters = monsters;
+ /*
+ * ::: Level 0: Intro 2 :::
+ */
+
}
} // namespace Immortal
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 4b369dbeb3f..6bd87d1400f 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -97,33 +97,33 @@ enum SObjPickup {
};
struct Pickup {
- //pointer to function
int _param;
+ // This will be a pointer to function
};
-// Iirc damage is used by object types as well as enemy types
+// Damage is used by object types as well as enemy types
enum SDamage {
};
struct Damage {
};
-// Use is self explanitory, it defines the function and parameters for using an object
+// Use defines the function and parameters for using an object
enum SObjUse {
};
struct Use {
- //pointer to function
int _param;
+ // This will be a pointer to function
};
struct ObjType {
Str _str = kStrNull;
Str _desc = kStrNull;
int _size = 0;
- Pickup _pickup;
Use _use;
Use _run;
+ Pickup _pickup;
};
// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
@@ -138,43 +138,48 @@ struct Cycle {
* for the moment there's no need to replicate this particular bit of space saving.
*/
struct SCycle {
-SpriteName _sName;
- bool _repeat;
-Common::Array<int> _frames;
+ SpriteName _sName;
+ Common::Array<int> _frames;
+ bool _repeat;
SCycle() {}
SCycle(SpriteName s, bool r, Common::Array<int> f) {
- _sName = s;
- _repeat = r;
- _frames = f;
+ _sName = s;
+ _repeat = r;
+ _frames = f;
}
};
struct SRoom {
uint16 _x = 0;
uint16 _y = 0;
- RoomFlag _flags = kRoomFlag0;
- SRoom() {}
- SRoom(uint16 x, uint16 y, RoomFlag f) {
- _x = x;
- _y = y;
- _flags = f;
- }
+
+ RoomFlag _flags = kRoomFlag0;
+ SRoom() {}
+ SRoom(uint16 x, uint16 y, RoomFlag f) {
+ _x = x;
+ _y = y;
+ _flags = f;
+ }
};
struct SDoor {
- uint8 _dir = 0;
- uint16 _x = 0;
- uint16 _y = 0;
+ uint8 _dir = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
+
uint16 _fromRoom = 0;
uint16 _toRoom = 0;
- bool _isLocked = false;
- SDoor() {}
- SDoor(uint8 d, uint16 x, uint16 y, uint16 f, uint16 t, bool l) {
- _dir = d;
- _x = x;
- _y = y;
- _fromRoom = f;
- _toRoom = t;
+
+ bool _isLocked = false;
+ SDoor() {}
+ SDoor(uint8 d, uint16 x, uint16 y, uint16 f, uint16 t, bool l) {
+ _dir = d;
+ _x = x;
+ _y = y;
+
+ _fromRoom = f;
+ _toRoom = t;
+
_isLocked = l;
}
};
@@ -182,68 +187,71 @@ struct SDoor {
struct SFlame {
uint16 _x = 0;
uint16 _y = 0;
-FPattern _p = kFlameOff;
+
+ FPattern _p = kFlameOff;
SFlame() {}
- SFlame(uint16 x, uint16 y, FPattern p) {
- _x = x;
+ SFlame(uint16 x, uint16 y, FPattern p) {
+ _x = x;
_y = y;
- _p = p;
- }
+ _p = p;
+ }
};
struct SObj {
- uint16 _x = 0;
- uint16 _y = 0;
- SObjType _type = kTypeTrap;
- uint8 _flags = 0;
-SpriteFrame _frame = kNoFrame;
-Common::Array<uint8> _traps;
+ uint16 _x = 0;
+ uint16 _y = 0;
+ uint8 _flags = 0;
+
+ SObjType _type = kTypeTrap;
+ SpriteFrame _frame = kNoFrame;
+ Common::Array<uint8> _traps;
SObj() {}
SObj(uint16 x, uint16 y, SObjType t, SpriteFrame s, uint8 f, Common::Array<uint8> traps) {
- _x = x;
- _y = y;
- _type = t;
- _flags = f;
- _traps = traps;
- _frame = s;
+ _x = x;
+ _y = y;
+ _type = t;
+ _flags = f;
+ _traps = traps;
+ _frame = s;
}
};
struct SMonster {
- uint16 _x = 0;
- uint16 _y = 0;
- uint16 _hits = 0;
-MonsterFlag _madAt = kMonstIsNone;
- uint8 _flags = 0;
- SpriteName _sprite = kCandle;
-Common::Array<Motive> _program;
+ uint16 _x = 0;
+ uint16 _y = 0;
+ uint16 _hits = 0;
+ uint8 _flags = 0;
+
+ MonsterFlag _madAt = kMonstIsNone;
+ SpriteName _sprite = kCandle;
+ Common::Array<Motive> _program;
SMonster() {}
SMonster(uint16 x, uint16 y, uint16 h, MonsterFlag m, uint8 f, Common::Array<Motive> p, SpriteName s) {
- _x = x;
- _y = y;
- _hits = h;
- _madAt = m;
- _flags = f;
- _program = p;
- _sprite = s;
+ _x = x;
+ _y = y;
+ _hits = h;
+ _madAt = m;
+ _flags = f;
+ _program = p;
+ _sprite = s;
}
};
struct Story {
- int _level = 0;
- int _part = 1;
-
- uint16 _initialUnivX = 0;
- uint16 _initialUnivY = 0;
- uint16 _playerPointX = 0;
- uint16 _playerPointY = 0;
-
- Common::Array<int> _ladders;
-Common::Array<SRoom> _rooms;
-Common::Array<SDoor> _doors;
- CArray2D<SFlame> _flames;
- CArray2D<SObj> _objects;
- CArray2D<SMonster> _monsters;
+ int _level = 0;
+ int _part = 1;
+
+ uint16 _initialUnivX = 0;
+ uint16 _initialUnivY = 0;
+ uint16 _playerPointX = 0;
+ uint16 _playerPointY = 0;
+
+ Common::Array<int> _ladders;
+ Common::Array<SRoom> _rooms;
+ Common::Array<SDoor> _doors;
+ CArray2D<SFlame> _flames;
+ CArray2D<SObj> _objects;
+ CArray2D<SMonster> _monsters;
};
} // namespace immortal
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
index 50957dede5e..0825510193d 100644
--- a/engines/immortal/utilities.cpp
+++ b/engines/immortal/utilities.cpp
@@ -90,7 +90,9 @@ bool Utilities::insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 poi
* This is done by grabbing the delta X,Y and
* making sure it is not negative.
*/
- if ((w | h) == 0) {
+
+ // The source specifically checks only for w *and* h being 0, so you could give it a rect with a width or height or 0, just not both
+ if ((w == 0) && (h == 0)) {
return false;
}
Commit: 3feb762d90017a7e2046a54451ee700f4a3ff6f8
https://github.com/scummvm/scummvm/commit/3feb762d90017a7e2046a54451ee700f4a3ff6f8
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Implement loadUniv() and add stub for makeBlisters()
Changed paths:
engines/immortal/immortal.h
engines/immortal/kernal.cpp
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 7ff63fc7591..954963bde22 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -382,11 +382,29 @@ public:
CArray2D<Motive> _programPtrs;
Common::Array<ObjType> _objTypePtrs;
- // Screen members
- byte *_screenBuff; // The final buffer that will transfer to the screen
+ // Universe members
+ uint16 _univRectX = 0;
+ uint16 _univRectY = 0;
+ uint16 _numAnims = 0;
+ uint16 _numCols = 0;
+ uint16 _numRows = 0;
+ uint16 _numChrs = 0;
+ uint16 _num2Cols = 0;
+ uint16 _num2Rows = 0;
+ uint16 _num2Cells = 0;
+ uint16 _num2Chrs = 0;
+ Common::Array<uint16> _univ; // This doesn't really need to exist in the way that it did in the source, but for now it will be utilized essentially the same way
+ uint16 *_CNM;
+ Common::Array<uint16> _LCNM;
+ uint16 *_modCNM;
+ uint16 *_modLCNM;
+
uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
uint16 _myModLCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
+
+ // Screen members
+ byte *_screenBuff; // The final buffer that will transfer to the screen
uint16 _columnX[kViewPortCW + 1];
uint16 _columnTop[kViewPortCW + 1];
uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
@@ -490,8 +508,9 @@ GenericSprite _genSprites[6];
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
void initStoryStatic(); // Sets up all of the global static story elements
- void loadUniv(char mazeNum);
- void loadMazeGraphics(int m); // Creates a universe with a maze
+ int loadUniv(char mazeNum); // Unpacks the .CNM and .UNV files into all the CNM stuff, returns the total length of everything
+ void loadMazeGraphics(int m); // Creates a universe with a maze
+ void makeBlisters(int povX, int povY); // Turns the unmodified CNM/CBM/LCNM etc into the modified ones to actually be used for drawing the game
void loadFont(); // Gets the font.spr file, and centers the sprite
void clearSprites(); // Clears all sprites before drawing the current frame
void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 97a898771db..741ae1af6e4 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -48,12 +48,12 @@ void ImmortalEngine::drawUniv() {
_myUnivPointX = !(_myViewPortX & (kChrW - 1)) + kViewPortSpX;
_myUnivPointY = !(_myViewPortY & (kChrH - 1)) + kViewPortSpY;
- makeMyCNM();
- drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
- addRows(); // Add rows to drawitem array
- addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
- sortDrawItems(); // Sort said items
- drawItems(); // Draw the items over the background
+ //makeMyCNM();
+ //drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
+ //addRows(); // Add rows to drawitem array
+ //addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
+ //sortDrawItems(); // Sort said items
+ //drawItems(); // Draw the items over the background
}
void ImmortalEngine::copyToScreen() {
@@ -466,17 +466,96 @@ void ImmortalEngine::cycleFreeAll() {
void ImmortalEngine::loadMazeGraphics(int m) {
char mazeNum = m + '0';
loadUniv(mazeNum);
- setColors(_palUniv);
+ //setColors(_palUniv);
}
-void ImmortalEngine::loadUniv(char mazeNum) {
+int ImmortalEngine::loadUniv(char mazeNum) {
+ int lData = 0;
+ int lStuff = 0x26;
+
+ // We start by loading the mazeN.CNM file with loadIFF (a little silly since we know this isn't compressed)
Common::String sCNM = "MAZE" + Common::String(mazeNum) + ".CNM";
Common::SeekableReadStream *mazeCNM = loadIFF(sCNM);
+ if (!mazeCNM) {
+ debug("Error, couldn't load maze %d.CNM", mazeNum);
+ return -1;
+ }
debug("Size of maze CNM: %ld", mazeCNM->size());
+ // The logical CNM contains the contents of mazeN.CNM, with every entry being multiplied by two (why are these byte indexes instead of word indexes in the file?)
+ mazeCNM->seek(0);
+ for (int i = 0; i < mazeCNM->size(); i++) {
+ _LCNM.push_back(mazeCNM->readUint16LE() << 1);
+ }
+
+ // Next we load the mazeN.UNV file, which contains the compressed data but is not itself compressed (again, a little silly)
Common::String sUNV = "MAZE" + Common::String(mazeNum) + ".UNV";
Common::SeekableReadStream *mazeUNV = loadIFF(sUNV);
+ if (!mazeUNV) {
+ debug("Error, couldn't load maze %d.UNV", mazeNum);
+ return -1;
+ }
debug("Size of maze UNV: %ld", mazeUNV->size());
+
+ lData = mazeUNV->size();
+
+ mazeUNV->seek(0x20);
+ _univRectX = mazeUNV->readUint16LE() << 1;
+ _numCols = _univRectX >> 6;
+ _num2Cols = _numCols << 1;
+
+ // univRectY is mazeUNV[22]
+ _univRectY = mazeUNV->readUint16LE();
+ _numRows = _univRectY >> 5;
+ _num2Rows = _numRows << 1;
+
+ // If there are animations (are there ever?), the univ data is expanded from 26 to include them
+ if (mazeUNV->readUint16LE() != 0) {
+ mazeUNV->seek(0x2C);
+ lStuff += mazeUNV->readUint16LE();
+ }
+
+ // This is probably not how _univ is actually populated, but just to make sure I don't forget about, let's make sure it has the data
+ mazeUNV->seek(0);
+ for (int i = 0; i < lStuff; i++) {
+ _univ.push_back(mazeUNV->readUint16LE());
+ }
+
+ // lData is everything from the .UNV file after the universe variables
+ lData -= lStuff;
+
+ // The data to uncompress is after the universe data in the file
+ mazeUNV->seek(lStuff);
+ Common::SeekableReadStream *pCNM = unCompress((Common::File *) mazeUNV, lData);
+
+ // Now we move the data of the uncompressed CNM into it's actual location
+ // This data type will likely change, it's just unclear how the munge functions work currently
+ _CNM = (uint16 *)malloc(pCNM->size());
+ pCNM->seek(0);
+ pCNM->read(_CNM, pCNM->size());
+
+ _num2Cells = _num2Cols * _numRows;
+ _numChrs = 0;
+
+ // Check every entry in the CNM, with the highest number being the highest number of chrs?
+ for (int i = 0; i < _num2Cells; i++) {
+ if (_CNM[i] >= _numChrs) {
+ _numChrs = _CNM[i];
+ }
+ }
+
+ // Inc one more time being 0 counts
+ _numChrs++;
+ _num2Chrs = _numChrs << 1;
+
+ int lCNMCBM = mungeCBM(_num2Chrs);
+
+ // We don't actually want to blister any rooms yet, so we give it a POV of (0,0)
+ makeBlisters(0, 0);
+ return _LCNM.size() + /*_modCNM.size()*/ + /*_modLCNM.size()*/ + _univ.size() + lCNMCBM;
+}
+
+void ImmortalEngine::makeBlisters(int povX, int povY) {
}
void ImmortalEngine::loadSprites() {
@@ -599,6 +678,15 @@ void ImmortalEngine::loadFont() {
}
Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
+ /* Technically the way this works in the source is that it loads the file
+ * to a destination address, and then checks the start of that address, and
+ * if it needs to uncompress, it gives that same address to the uncompress
+ * routine, overwriting the file in it's place. This is of course slightly
+ * different here, for simplicity we are not overwriting the original file
+ * pointer, instead just returning either a compressed or uncompressed
+ * file pointer.
+ */
+
Common::File f;
if (!f.open(fileName)) {
debug("*surprised pikachu face*");
Commit: 8232bfd367f8145a1f35f5f8f9c63e1e1fd0874b
https://github.com/scummvm/scummvm/commit/8232bfd367f8145a1f35f5f8f9c63e1e1fd0874b
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Update disk.cpp to use strncpy() instead of strcpy()
Changed paths:
engines/immortal/disk.cpp
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index be6103e6c30..dac2d02260d 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -34,7 +34,7 @@ ProDOSFile::ProDOSFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint1
, _eof(eof)
, _blockPtr(bPtr)
, _disk(disk) {
- strcpy(_name, name);
+ strncpy(_name, name, 15);
}
/* For debugging purposes, this prints the meta data of a file */
Commit: e16f087c544ae5d121244f61466c5a9b5d08f8c5
https://github.com/scummvm/scummvm/commit/e16f087c544ae5d121244f61466c5a9b5d08f8c5
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Rewrite loadUniv() based on new understanding of CNM
Changed paths:
engines/immortal/drawChr.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
diff --git a/engines/immortal/drawChr.cpp b/engines/immortal/drawChr.cpp
index 9a8b8e711cc..6f0cfe87a17 100644
--- a/engines/immortal/drawChr.cpp
+++ b/engines/immortal/drawChr.cpp
@@ -23,12 +23,15 @@
namespace Immortal {
-// These functions are not yet implemented
-int ImmortalEngine::mungeCBM(int numChrs) {
- return 0;
+int ImmortalEngine::mungeCBM() {
+
+ return 0;//_CNM - pDraw;
+}
+
+void ImmortalEngine::storeAddr() {
+
}
-void ImmortalEngine::storeAddr() {}
void ImmortalEngine::mungeSolid() {}
void ImmortalEngine::mungeLRHC() {}
void ImmortalEngine::mungeLLHC() {}
@@ -40,4 +43,17 @@ void ImmortalEngine::drawURHC(int chr, int x, int y) {}
void ImmortalEngine::drawLLHC(int chr, int x, int y) {}
void ImmortalEngine::drawLRHC(int chr, int x, int y) {}
+
+
+
+
+
+
+
+
+
+
+
+
+
} // namespace immortal
\ No newline at end of file
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 954963bde22..47b0207876f 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -162,6 +162,20 @@ struct Door {
uint8 _on = 0;
};
+// Universe is a set of properties for the entire level, nor just the room
+struct Univ {
+ uint16 _rectX = 0;
+ uint16 _rectY = 0;
+ uint16 _numAnims = 0;
+ uint16 _numCols = 0;
+ uint16 _numRows = 0;
+ uint16 _numChrs = 0;
+ uint16 _num2Cols = 0;
+ uint16 _num2Rows = 0;
+ uint16 _num2Cells = 0;
+ uint16 _num2Chrs = 0;
+};
+
struct ImmortalGameDescription;
// Forward declaration because we will need the Disk and Room classes
@@ -224,6 +238,7 @@ public:
const uint16 kChrLen = (kChrW / 2) * kChrH;
const uint16 kChrBMW = kChrW / 2;
const uint16 kLCutaway = 4;
+ const uint16 kLDrawSolid = 32 * ((3 * 16) + 5);
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
kChrH2, kChrH, kChrH2, kChrH2, kChr0,
@@ -243,6 +258,14 @@ public:
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0};
+ const uint16 kTBlisterCorners[60] = {7, 1, 1, 1, 1, 1, 5, 3, 1, 1, 1, 1, 1, 3, 5, 3, 5, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 16, 16, 16, 16, 8,
+ 8, 8, 8, 16, 16, 16, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+
+ const uint16 kTLogicalCorners[19] = {1, 1, 1, 1, 16, 8, 1, 8,
+ 16, 1, 1, 8, 1, 16, 8, 16,
+ 1, 16, 8};
+
// Disk offsets
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
@@ -382,22 +405,15 @@ public:
CArray2D<Motive> _programPtrs;
Common::Array<ObjType> _objTypePtrs;
- // Universe members
- uint16 _univRectX = 0;
- uint16 _univRectY = 0;
- uint16 _numAnims = 0;
- uint16 _numCols = 0;
- uint16 _numRows = 0;
- uint16 _numChrs = 0;
- uint16 _num2Cols = 0;
- uint16 _num2Rows = 0;
- uint16 _num2Cells = 0;
- uint16 _num2Chrs = 0;
- Common::Array<uint16> _univ; // This doesn't really need to exist in the way that it did in the source, but for now it will be utilized essentially the same way
- uint16 *_CNM;
- Common::Array<uint16> _LCNM;
+ // Universe members in order of their original memory layout
+ uint16 *_logicalCNM; // As confusing as this is, we get Logical CNM from the .CNM file, and we get the CNM from the .UNV file
uint16 *_modCNM;
- uint16 *_modLCNM;
+ uint16 *_modLogicalCNM;
+ Univ *_univ; // Pointer to the struct that contains the universe properties
+ Common::SeekableReadStream *_dataBuffer; // This contains the CNM and the CBM
+ uint16 *_CNM; // Stands for CHARACTER NUMBER MAP
+ byte *_CBM; // Stands for CHARACTER BIT MAP (?)
+ byte *_oldCBM;
uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
@@ -534,7 +550,7 @@ GenericSprite _genSprites[6];
*/
// Main
- int mungeCBM(int numChrs);
+ int mungeCBM();
void storeAddr();
void mungeSolid();
void mungeLRHC();
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 741ae1af6e4..9a83108d65f 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -482,13 +482,19 @@ int ImmortalEngine::loadUniv(char mazeNum) {
}
debug("Size of maze CNM: %ld", mazeCNM->size());
- // The logical CNM contains the contents of mazeN.CNM, with every entry being multiplied by two (why are these byte indexes instead of word indexes in the file?)
+ // The logical CNM contains the contents of mazeN.CNM, with every entry being bitshifted left once
+ _logicalCNM = (uint16 *) malloc(mazeCNM->size());
mazeCNM->seek(0);
- for (int i = 0; i < mazeCNM->size(); i++) {
- _LCNM.push_back(mazeCNM->readUint16LE() << 1);
+ mazeCNM->read(_logicalCNM, mazeCNM->size());
+ for (int i = 0; i < (mazeCNM->size()); i++) {
+ _logicalCNM[i] <<= 1;
}
- // Next we load the mazeN.UNV file, which contains the compressed data but is not itself compressed (again, a little silly)
+ // This is where the source defines the location of the pointers for modCNM, lModCNM, and then the universe properties
+ // So in similar fasion, here we will create the struct for universe
+ _univ = new Univ();
+
+ // Next we load the mazeN.UNV file, which contains the compressed data for multiple things
Common::String sUNV = "MAZE" + Common::String(mazeNum) + ".UNV";
Common::SeekableReadStream *mazeUNV = loadIFF(sUNV);
if (!mazeUNV) {
@@ -497,17 +503,29 @@ int ImmortalEngine::loadUniv(char mazeNum) {
}
debug("Size of maze UNV: %ld", mazeUNV->size());
+ // This is also where the pointer to CNM is defined, because it is 26 bytes after the pointer to Univ. However for our purposes
+ // These are separate
+
+ // After which, we set data length to be the total size of the file
lData = mazeUNV->size();
+ // The first data we need is found at index 20
mazeUNV->seek(0x20);
- _univRectX = mazeUNV->readUint16LE() << 1;
- _numCols = _univRectX >> 6;
- _num2Cols = _numCols << 1;
+
+ // The view port of the level is longer than it is wide, so there are more columns than rows
+ // numCols = rectX / 64 (charW)
+ _univ->_rectX = mazeUNV->readUint16LE() << 1;
+ _univ->_numCols = _univ->_rectX >> 6;
+ _univ->_num2Cols = _univ->_numCols << 1;
// univRectY is mazeUNV[22]
- _univRectY = mazeUNV->readUint16LE();
- _numRows = _univRectY >> 5;
- _num2Rows = _numRows << 1;
+ // numRows = rectY / 32 (charH)
+ _univ->_rectY = mazeUNV->readUint16LE();
+ _univ->_numRows = _univ->_rectY >> 5;
+ _univ->_num2Rows = _univ->_numRows << 1;
+
+ // Technically this is done right after decompressing the data, but it is more relevant here for now
+ _univ->_num2Cells = _univ->_num2Cols * _univ->_numRows;
// If there are animations (are there ever?), the univ data is expanded from 26 to include them
if (mazeUNV->readUint16LE() != 0) {
@@ -515,44 +533,40 @@ int ImmortalEngine::loadUniv(char mazeNum) {
lStuff += mazeUNV->readUint16LE();
}
- // This is probably not how _univ is actually populated, but just to make sure I don't forget about, let's make sure it has the data
- mazeUNV->seek(0);
- for (int i = 0; i < lStuff; i++) {
- _univ.push_back(mazeUNV->readUint16LE());
- }
-
- // lData is everything from the .UNV file after the universe variables
+ // lData is everything from the .UNV file after the universe properties
lData -= lStuff;
- // The data to uncompress is after the universe data in the file
- mazeUNV->seek(lStuff);
- Common::SeekableReadStream *pCNM = unCompress((Common::File *) mazeUNV, lData);
-
- // Now we move the data of the uncompressed CNM into it's actual location
- // This data type will likely change, it's just unclear how the munge functions work currently
- _CNM = (uint16 *)malloc(pCNM->size());
- pCNM->seek(0);
- pCNM->read(_CNM, pCNM->size());
+ // At this point in the source, the data after universe properties is moved to the end of the heap
- _num2Cells = _num2Cols * _numRows;
- _numChrs = 0;
+ // We then uncompress all of that data, into the place in the heap where the CNM is supposed to be (the Maze Heap)
+ mazeUNV->seek(lStuff);
+ _dataBuffer = unCompress((Common::File *) mazeUNV, lData);
+ debug("size of uncompressed CNM/CBM data %ld", _dataBuffer->size());
// Check every entry in the CNM, with the highest number being the highest number of chrs?
- for (int i = 0; i < _num2Cells; i++) {
- if (_CNM[i] >= _numChrs) {
- _numChrs = _CNM[i];
+ _univ->_numChrs = 0;
+ _dataBuffer->seek(0);
+ for (int i = 0; i < _univ->_num2Cells; i++) {
+ uint16 chr = _dataBuffer->readUint16LE();
+ if (chr >= _univ->_numChrs) {
+ _univ->_numChrs = chr;
}
}
- // Inc one more time being 0 counts
- _numChrs++;
- _num2Chrs = _numChrs << 1;
+ _dataBuffer->seek(0);
+ _univ->_numChrs++; // Inc one more time being 0 counts
+ _univ->_num2Chrs = _univ->_numChrs << 1;
- int lCNMCBM = mungeCBM(_num2Chrs);
+ //int lCNMCBM = mungeCBM(_univ->_num2Chrs);
+ int lCNMCBM = mungeCBM();
+
+ debug("nchrs %04X, n2cells %04X, univX %04X, univY %04X, cols %04X, rows %04X, lstuff %04X", _univ->_numChrs, _univ->_num2Cells, _univ->_rectX, _univ->_rectY, _univ->_numCols, _univ->_numRows, lStuff);
// We don't actually want to blister any rooms yet, so we give it a POV of (0,0)
makeBlisters(0, 0);
- return _LCNM.size() + /*_modCNM.size()*/ + /*_modLCNM.size()*/ + _univ.size() + lCNMCBM;
+
+ // We return the final size of everything by adding logicalCNM + modCNM + modLogicalCNM + univ + length of expanded CNM/CBM
+ return mazeCNM->size() /*+ _modCNM.size() + _modLCNM.size()*/ + lStuff + lCNMCBM;
}
void ImmortalEngine::makeBlisters(int povX, int povY) {
Commit: 55590df288f0d00b7172383724a2c1bd8e2423ef
https://github.com/scummvm/scummvm/commit/55590df288f0d00b7172383724a2c1bd8e2423ef
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Fix whitespace at end of most files
Changed paths:
engines/immortal/bullet.cpp
engines/immortal/cycle.cpp
engines/immortal/definitions.h
engines/immortal/disk.cpp
engines/immortal/disk.h
engines/immortal/door.cpp
engines/immortal/drawChr.cpp
engines/immortal/flameSet.cpp
engines/immortal/kernal.cpp
engines/immortal/level.cpp
engines/immortal/logic.cpp
engines/immortal/misc.cpp
engines/immortal/room.cpp
engines/immortal/room.h
engines/immortal/sprite_list.h
engines/immortal/sprites.cpp
engines/immortal/story.cpp
engines/immortal/story.h
engines/immortal/univ.cpp
engines/immortal/utilities.cpp
engines/immortal/utilities.h
diff --git a/engines/immortal/bullet.cpp b/engines/immortal/bullet.cpp
index e8770ec881e..e17e84c29f1 100644
--- a/engines/immortal/bullet.cpp
+++ b/engines/immortal/bullet.cpp
@@ -29,4 +29,4 @@ namespace Immortal {
-} // namespace immortal
\ No newline at end of file
+} // namespace immortal
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index 7d505a7a0fc..83d5cda2177 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -118,22 +118,3 @@ void Room::cycleSetIndex(int c, int f) {
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/definitions.h b/engines/immortal/definitions.h
index de431f4c9e5..d19c3debad6 100644
--- a/engines/immortal/definitions.h
+++ b/engines/immortal/definitions.h
@@ -237,4 +237,4 @@ enum SObjType {
} // namespace immortal
-#endif
\ No newline at end of file
+#endif
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index dac2d02260d..e52a96c390c 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -429,41 +429,3 @@ Common::SeekableReadStream *ProDOSDisk::createReadStreamForMember(const Common::
}
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index 57c057a8eb2..41292574db5 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -213,36 +213,3 @@ Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>> _files; // Hashma
} // namespace Immortal
#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/door.cpp b/engines/immortal/door.cpp
index ee905d84e98..f93487620b4 100644
--- a/engines/immortal/door.cpp
+++ b/engines/immortal/door.cpp
@@ -102,4 +102,4 @@ void ImmortalEngine::doorCloseSecret() {}
void ImmortalEngine::doorInit() {}
void ImmortalEngine::doorClrLock() {}
-} // namespace immortal
\ No newline at end of file
+} // namespace immortal
diff --git a/engines/immortal/drawChr.cpp b/engines/immortal/drawChr.cpp
index 6f0cfe87a17..b898849b113 100644
--- a/engines/immortal/drawChr.cpp
+++ b/engines/immortal/drawChr.cpp
@@ -43,17 +43,4 @@ void ImmortalEngine::drawURHC(int chr, int x, int y) {}
void ImmortalEngine::drawLLHC(int chr, int x, int y) {}
void ImmortalEngine::drawLRHC(int chr, int x, int y) {}
-
-
-
-
-
-
-
-
-
-
-
-
-
-} // namespace immortal
\ No newline at end of file
+} // namespace immortal
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index 89732a91fde..e3c0ca2ba7f 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -147,24 +147,3 @@ int Room::flameGetCyc(Flame *f, int first) {
}
} // namespace immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 9a83108d65f..e03a47597db 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -1085,23 +1085,3 @@ void ImmortalEngine::carriageReturn() {
}
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 47822b23d18..5d297ba8e70 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -156,21 +156,3 @@ bool ImmortalEngine::levelIsShowRoom(int r) {
}
} // namespace immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 20106566dd4..6a7b5349d23 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -658,22 +658,3 @@ void ImmortalEngine::doGroan() {
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 38fe11f6cc1..6a10b596c46 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -459,27 +459,3 @@ void ImmortalEngine::standardBeep() {
}
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/room.cpp b/engines/immortal/room.cpp
index 5351375162d..47de77211fb 100644
--- a/engines/immortal/room.cpp
+++ b/engines/immortal/room.cpp
@@ -86,20 +86,3 @@ bool Room::getWallNormal(uint8 x, uint8 y, uint8 xPrev, uint8 yPrev, int id) {
}
} // namespace immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 97c25fba2f1..5811b589e4d 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -228,18 +228,3 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
} // namespace immortal
#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index 26d6fb5ab6e..3ef3e1307e1 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -297,16 +297,3 @@ enum SpriteName {
} // namespace immortal
#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index c8c61f8ade5..170c8131b1d 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -245,41 +245,3 @@ void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 poin
}
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index c0b39350a10..0d598f82b24 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -357,28 +357,3 @@ void ImmortalEngine::initStoryDynamic() {
}
} // namespace Immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index 6bd87d1400f..f201c8ae0e4 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -257,14 +257,3 @@ struct Story {
} // namespace immortal
#endif
-
-
-
-
-
-
-
-
-
-
-
diff --git a/engines/immortal/univ.cpp b/engines/immortal/univ.cpp
index 33a8861c846..9458d645f1e 100644
--- a/engines/immortal/univ.cpp
+++ b/engines/immortal/univ.cpp
@@ -28,4 +28,4 @@ void Room::univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s,
//g_immortal->addSprite(vX, vY, s, img, x, y, p);
}
-} // namespace immortal
\ No newline at end of file
+} // namespace immortal
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
index 0825510193d..46d098a2fb0 100644
--- a/engines/immortal/utilities.cpp
+++ b/engines/immortal/utilities.cpp
@@ -108,4 +108,4 @@ bool Utilities::insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 poi
return false;
}
-}; // namespace Immortal
\ No newline at end of file
+}; // namespace Immortal
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
index c40904d8aa6..d1a1fcd064f 100644
--- a/engines/immortal/utilities.h
+++ b/engines/immortal/utilities.h
@@ -88,4 +88,4 @@ bool insideRect(uint8 rectX, uint8 rectY, uint8 w, uint8 h, uint8 pointX, uint8
}; // namespace Immortal
-#endif
\ No newline at end of file
+#endif
Commit: 98247630c2c720008f693b95a17734e88553c60c
https://github.com/scummvm/scummvm/commit/98247630c2c720008f693b95a17734e88553c60c
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Remove unnecessary commented line
Changed paths:
engines/immortal/compression.cpp
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index 0f39f6ff806..696a635b7db 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -250,8 +250,7 @@ void ImmortalEngine::appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &fi
link = hash >> 1;
- ptk[prev] = (link << 8) | (ptk[prev] & kMaskLow);
- //start[prev] = ((link >> 4) & kMaskLast) | start[prev]; // Yikes this statement is gross
+ ptk[prev] = (link << 8) | (ptk[prev] & kMaskLow);
start[prev] |= (link >> 4) & kMaskLast;
found = true;
}
@@ -260,29 +259,3 @@ void ImmortalEngine::appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &fi
}
} // namespace immortal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Commit: 9cbe97d557da566cf6121c177e566a264a520b3e
https://github.com/scummvm/scummvm/commit/9cbe97d557da566cf6121c177e566a264a520b3e
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: IMMORTAL_IMMORTAL_H -> IMMORTAL_H
Changed paths:
engines/immortal/immortal.h
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 47b0207876f..7b40f084f6e 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -19,8 +19,8 @@
*
*/
-#ifndef IMMORTAL_IMMORTAL_H
-#define IMMORTAL_IMMORTAL_H
+#ifndef IMMORTAL_H
+#define IMMORTAL_H
// Audio is only handled in kernal, therefore it is only needed here
#include "audio/mixer.h"
Commit: 4103ebe6003f3c2f9b2d2a54caa61eee42b3985f
https://github.com/scummvm/scummvm/commit/4103ebe6003f3c2f9b2d2a54caa61eee42b3985f
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Fix formatting for switch statements
Changed paths:
engines/immortal/flameSet.cpp
engines/immortal/kernal.cpp
engines/immortal/logic.cpp
engines/immortal/misc.cpp
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index e3c0ca2ba7f..dcb4c9f4307 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -133,16 +133,16 @@ int Room::flameGetCyc(Flame *f, int first) {
// Why is this not indexed further? ie. LDA patternTable,x : STA $00 : LDA ($00),y instead of a branch tree?
// Pretty sure CPX 3 times is more than a single LDA (dp),y
switch (f->_p) {
- case 0:
- return cycleNew(flamePatA[r]);
- case 1:
- return cycleNew(flamePatB[r]);
- case 2:
- return cycleNew(flamePatC[r]);
- case 3:
- return cycleNew(flamePatD[r]);
- default:
- return 0;
+ case 0:
+ return cycleNew(flamePatA[r]);
+ case 1:
+ return cycleNew(flamePatB[r]);
+ case 2:
+ return cycleNew(flamePatC[r]);
+ case 3:
+ return cycleNew(flamePatD[r]);
+ default:
+ return 0;
}
}
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index e03a47597db..6c565fae9ff 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -389,13 +389,14 @@ void ImmortalEngine::printChr(char c) {
}
switch (c) {
- case 'm':
- case 'w':
- case 'M':
- case 'W':
- _penX += 8;
- default:
- break;
+ case 'm':
+ case 'w':
+ case 'M':
+ case 'W':
+ _penX += 8;
+ // fall through
+ default:
+ break;
}
if ((((c >= 'A') && (c <= 'Z'))) || ((c == kGaugeOn) || (c == kGaugeOff))) {
@@ -403,17 +404,19 @@ void ImmortalEngine::printChr(char c) {
}
switch (c) {
- case 'i':
- _penX -= 3;
- break;
- case 'j':
- case 't':
- _penX -= 2;
- break;
- case 'l':
- _penX -= 4;
- default:
- break;
+ case 'i':
+ _penX -= 3;
+ break;
+ case 'j':
+ // fall through
+ case 't':
+ _penX -= 2;
+ break;
+ case 'l':
+ _penX -= 4;
+ // fall through
+ default:
+ break;
}
uint16 x = _penX + kScreenLeft;
@@ -1003,27 +1006,30 @@ void ImmortalEngine::fixPause() {
// This is a nasty bit of code isn't it? It's accurate to the source though :D
switch (_playing) {
- case kSongText:
- case kSongMaze:
- if (_themePaused) {
- musicUnPause(_themeID);
- break;
- }
- default:
- musicPause(_themeID);
+ case kSongText:
+ // fall through
+ case kSongMaze:
+ if (_themePaused) {
+ musicUnPause(_themeID);
break;
+ }
+ // fall through
+ default:
+ musicPause(_themeID);
+ break;
}
// Strictly speaking this should probably be a single function called twice, but the source writes out both so I will too
switch (_playing) {
- case kSongCombat:
- if (_themePaused) {
- musicUnPause(_combatID);
- break;
- }
- default:
- musicPause(_combatID);
+ case kSongCombat:
+ if (_themePaused) {
+ musicUnPause(_combatID);
break;
+ }
+ // fall through
+ default:
+ musicPause(_combatID);
+ break;
}
}
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 6a7b5349d23..21d8f0a2fb0 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -150,16 +150,17 @@ void ImmortalEngine::trapKeys() {
*/
getInput();
switch (_pressedAction) {
- case kActionDBGStep:
- _singleStep = true;
- break;
- case kActionRestart:
- gameOver();
- break;
- case kActionSound:
- toggleSound();
- default:
- break;
+ case kActionDBGStep:
+ _singleStep = true;
+ break;
+ case kActionRestart:
+ gameOver();
+ break;
+ case kActionSound:
+ toggleSound();
+ // fall through
+ default:
+ break;
}
}
@@ -170,13 +171,16 @@ int ImmortalEngine::keyOrButton() {
while (button == 0) {
getInput();
switch (_pressedAction) {
- case kActionKey:
- button = _pressedAction;
- case kActionFire:
- case kActionButton:
- button = 13;
- default:
- break;
+ case kActionKey:
+ button = _pressedAction;
+ break;
+ case kActionFire:
+ // fall through
+ case kActionButton:
+ button = 13;
+ // fall through
+ default:
+ break;
}
}
return button;
@@ -348,43 +352,43 @@ bool ImmortalEngine::fromOldGame() {
// This would have been much more clean as a set of tables instead of a long branching tree
switch (_certificate[kCertLevel]) {
- case 1:
- if ((certInv & 2) != 0) {
- //room.makeObject(3, 0, kSporesFrame, sporesType);
- }
-
- if ((certInv & 4) != 0) {
- //room.makeObject(3, 0, kSporesFrame, wowCharmType);
- }
+ case 1:
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, sporesType);
+ }
- break;
- case 4:
- if ((certInv & 2) != 0) {
- //room.makeObject(3, kIsInvisible, kSporesFrame, coffeeType);
- }
+ if ((certInv & 4) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, wowCharmType);
+ }
- break;
- case 3:
- if ((certInv & 1) != 0) {
- //room.makeObject(3, kIsRunning, kRingFrame, faceRingType);
- }
+ break;
+ case 4:
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, kIsInvisible, kSporesFrame, coffeeType);
+ }
- break;
- case 7:
- if ((certInv & 1) != 0) {
- //room.makeObject(6, kUsesFireButton, kSporesFrame, bronzeType);
- }
+ break;
+ case 3:
+ if ((certInv & 1) != 0) {
+ //room.makeObject(3, kIsRunning, kRingFrame, faceRingType);
+ }
- if ((certInv & 2) != 0) {
- //room.makeObject(3, 0, kSporesFrame, tractorType);
- }
+ break;
+ case 7:
+ if ((certInv & 1) != 0) {
+ //room.makeObject(6, kUsesFireButton, kSporesFrame, bronzeType);
+ }
- if ((certInv & 4) != 0) {
- //room.makeObject(3, 0, kSporesFrame, antiType);
- }
+ if ((certInv & 2) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, tractorType);
+ }
- default:
- break;
+ if ((certInv & 4) != 0) {
+ //room.makeObject(3, 0, kSporesFrame, antiType);
+ }
+ // fall through
+ default:
+ break;
}
levelNew(_level);
return true;
@@ -434,46 +438,46 @@ void ImmortalEngine::makeCertificate() {
// The lo byte of the inventory is used for items that only exist on a specific level, and are removed after
switch (_certificate[kCertLevel]) {
- case 1:
- if (true/*room.monster[kPlayerID].hasObject(sporesType)*/) {
- _certificate[kCertInvLo] |= 2;
- }
-
- if (true/*room.monster[kPlayerID].hasObject(wowCharmType)*/) {
- _certificate[kCertInvLo] |= 4;
- }
-
- case 3:
- if (true/*room.monster[kPlayerID].hasObject(faceRingType)*/) {
- _certificate[kCertInvLo] |= 1;
- }
-
- case 4:
- if (true/*room.monster[kPlayerID].hasObject(coffeeType)*/) {
- _certificate[kCertInvLo] |= 2;
- }
-
- case 7:
- if (true/*room.monster[kPlayerID].hasObject(bronzeType)*/) {
- _certificate[kCertInvLo] |= 1;
- }
+ case 1:
+ if (true/*room.monster[kPlayerID].hasObject(sporesType)*/) {
+ _certificate[kCertInvLo] |= 2;
+ }
- if (true/*room.monster[kPlayerID].hasObject(tractorType)*/) {
- _certificate[kCertInvLo] |= 2;
- }
+ if (true/*room.monster[kPlayerID].hasObject(wowCharmType)*/) {
+ _certificate[kCertInvLo] |= 4;
+ }
+ // fall through
+ case 3:
+ if (true/*room.monster[kPlayerID].hasObject(faceRingType)*/) {
+ _certificate[kCertInvLo] |= 1;
+ }
+ // fall through
+ case 4:
+ if (true/*room.monster[kPlayerID].hasObject(coffeeType)*/) {
+ _certificate[kCertInvLo] |= 2;
+ }
+ // fall through
+ case 7:
+ if (true/*room.monster[kPlayerID].hasObject(bronzeType)*/) {
+ _certificate[kCertInvLo] |= 1;
+ }
- if (true/*room.monster[kPlayerID].hasObject(antiType)*/) {
- _certificate[kCertInvLo] |= 4;
- }
+ if (true/*room.monster[kPlayerID].hasObject(tractorType)*/) {
+ _certificate[kCertInvLo] |= 2;
+ }
- default:
- _lastCertLen = 13;
- uint8 checksum[4];
- calcCheckSum(_lastCertLen, checksum);
- _certificate[0] = checksum[0];
- _certificate[1] = checksum[1];
- _certificate[2] = checksum[2];
- _certificate[3] = checksum[3];
+ if (true/*room.monster[kPlayerID].hasObject(antiType)*/) {
+ _certificate[kCertInvLo] |= 4;
+ }
+ // fall through
+ default:
+ _lastCertLen = 13;
+ uint8 checksum[4];
+ calcCheckSum(_lastCertLen, checksum);
+ _certificate[0] = checksum[0];
+ _certificate[1] = checksum[1];
+ _certificate[2] = checksum[2];
+ _certificate[3] = checksum[3];
}
}
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 6a10b596c46..938488fb84f 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -93,104 +93,108 @@ bool ImmortalEngine::textSub(Str s, FadeType f, int n) {
while (done == false) {
switch (text[index]) {
- case '@':
- case '=':
- case char(0):
- done = true;
- // This is so the while loop can be a little cleaner
- index--;
- break;
- case '&':
- textCR();
- break;
- case '$':
- printByte(n);
+ case '@':
+ case '=':
+ case char(0):
+ done = true;
+ // This is so the while loop can be a little cleaner
+ index--;
+ break;
+ case '&':
+ textCR();
+ break;
+ case '$':
+ printByte(n);
+ copyToScreen();
+ break;
+ case '_':
+ myFadeIn();
+ _slowText = 1;
+ break;
+ case '<':
+ _slowText = 0;
+ break;
+ case '>':
+ _formatted = 0;
+ break;
+ case '\\':
+ normalFadeOut();
+ break;
+ case '/':
+ slowFadeOut();
+ break;
+ case '|':
+ normalFadeIn();
+ break;
+ case '}':
+ _formatted = 1;
+ break;
+ case ']':
+ myDelay(40);
+ break;
+ case '{':
+ index++;
+ myDelay(text[index]);
+ break;
+ case '*':
+ textPageBreak(text, index);
+ break;
+ case '[':
+ textAutoPageBreak();
+ break;
+ case '#':
+ index++;
+ drawIcon(text[index]);
+ break;
+ case '~':
+ text = _strPtrs[(int)text[index + 1]];
+ index = -1;
+ break;
+ case '^':
+ center();
+ break;
+ case '%':
+ return yesNo();
+ case '+':
+ chr = 0x27;
+ break;
+ case '(':
+ chr = 0x60;
+ break;
+ default:
+ chr = text[index];
+ _collumn++;
+ if (chr == ' ') {
+ if (text[index + 1] == '~') {
+ text = _strPtrs[(int)text[index + 2]];
+ index = -1;
+ }
+ textDoSpace(text, index);
+
+ } else {
+ printChr(chr);
+ // We need this to show up now, not when the frame ends, so we have to update the screen here
copyToScreen();
- break;
- case '_':
- myFadeIn();
- _slowText = 1;
- break;
- case '<':
- _slowText = 0;
- break;
- case '>':
- _formatted = 0;
- break;
- case '\\':
- normalFadeOut();
- break;
- case '/':
- slowFadeOut();
- break;
- case '|':
- normalFadeIn();
- break;
- case '}':
- _formatted = 1;
- break;
- case ']':
- myDelay(40);
- break;
- case '{':
- index++;
- myDelay(text[index]);
- break;
- case '*':
- textPageBreak(text, index);
- break;
- case '[':
- textAutoPageBreak();
- break;
- case '#':
- index++;
- drawIcon(text[index]);
- break;
- case '~':
- text = _strPtrs[(int)text[index + 1]];
- index = -1;
- break;
- case '^':
- center();
- break;
- case '%':
- return yesNo();
- case '+':
- chr = 0x27;
- break;
- case '(':
- chr = 0x60;
- break;
- default:
- chr = text[index];
- _collumn++;
- if (chr == ' ') {
- if (text[index + 1] == '~') {
- text = _strPtrs[(int)text[index + 2]];
- index = -1;
- }
- textDoSpace(text, index);
-
- } else {
- printChr(chr);
- // We need this to show up now, not when the frame ends, so we have to update the screen here
- copyToScreen();
- if (_slowText != 0) {
- myDelay(5);
- switch (chr) {
- case '?':
- case ':':
- myDelay(13);
- case '.':
- myDelay(13);
- case ',':
- myDelay(13);
- default:
- break;
- }
+ if (_slowText != 0) {
+ myDelay(5);
+ switch (chr) {
+ case '?':
+ // fall through
+ case ':':
+ myDelay(13);
+ // fall through
+ case '.':
+ myDelay(13);
+ // fall through
+ case ',':
+ myDelay(13);
+ // fall through
+ default:
+ break;
}
}
- break;
+ }
+ break;
}
if (index == 0xFF) {
debug("String too long!");
@@ -272,14 +276,19 @@ void ImmortalEngine::textDoSpace(Common::String s, int index) {
while (foundEnd == false) {
index++;
switch (s[index]) {
- case '=':
- case '@':
- case '%':
- case '[':
- case ' ':
- foundEnd = true;
- default:
- break;
+ case '=':
+ // fall through
+ case '@':
+ // fall through
+ case '%':
+ // fall through
+ case '[':
+ // fall through
+ case ' ':
+ foundEnd = true;
+ // fall through
+ default:
+ break;
}
}
if (((index - start) + _collumn) >= kMaxCollumns) {
@@ -383,14 +392,16 @@ void ImmortalEngine::myDelay(int j) {
// Otherwise, we delay by different amounts based on what's held down
switch (type) {
- case 1:
- Utilities::delay4(1);
- break;
- case 0:
- Utilities::delay(1);
- case 2:
- default:
- break;
+ case 1:
+ Utilities::delay4(1);
+ break;
+ case 0:
+ Utilities::delay(1);
+ // fall through
+ case 2:
+ // fall through
+ default:
+ break;
}
j--;
Commit: 7e0c5b39abc444aa88d9e32bdf655b32d7f7da9b
https://github.com/scummvm/scummvm/commit/7e0c5b39abc444aa88d9e32bdf655b32d7f7da9b
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Fix formatting for casting
Changed paths:
engines/immortal/kernal.cpp
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 6c565fae9ff..7677cafab60 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -486,7 +486,7 @@ int ImmortalEngine::loadUniv(char mazeNum) {
debug("Size of maze CNM: %ld", mazeCNM->size());
// The logical CNM contains the contents of mazeN.CNM, with every entry being bitshifted left once
- _logicalCNM = (uint16 *) malloc(mazeCNM->size());
+ _logicalCNM = (uint16 *)malloc(mazeCNM->size());
mazeCNM->seek(0);
mazeCNM->read(_logicalCNM, mazeCNM->size());
for (int i = 0; i < (mazeCNM->size()); i++) {
Commit: 8a1c1764afb84ec3bcd4960c9c4e70c9c5402a58
https://github.com/scummvm/scummvm/commit/8a1c1764afb84ec3bcd4960c9c4e70c9c5402a58
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Add TODO comments for unimplemented methods
Changed paths:
engines/immortal/door.cpp
diff --git a/engines/immortal/door.cpp b/engines/immortal/door.cpp
index f93487620b4..187268dab31 100644
--- a/engines/immortal/door.cpp
+++ b/engines/immortal/door.cpp
@@ -60,40 +60,65 @@ void ImmortalEngine::doorNew(SDoor door) {
_doors.push_back(d);
}
-
+/* TODO
+ * Not implemented yet
+ */
int ImmortalEngine::findDoorTop(int x, int y) {
return 0;
}
+/* TODO
+ * Not implemented yet
+ */
int ImmortalEngine::findDoor(int x, int y) {
return 0;
}
+/* TODO
+ * Not implemented yet
+ */
bool ImmortalEngine::doLockStuff(int d, MonsterID m, int top) {
return true;
}
+/* TODO
+ * Not implemented yet
+ */
bool ImmortalEngine::inDoorTop(int x, int y, MonsterID m) {
return true;
}
+/* TODO
+ * Not implemented yet
+ */
bool ImmortalEngine::inDoor(int x, int y, MonsterID m) {
return true;
}
+/* TODO
+ * Not implemented yet
+ */
int ImmortalEngine::doorDoStep(MonsterID m, int d, int index) {
return 0;
}
+/* TODO
+ * Not implemented yet
+ */
int ImmortalEngine::doorSetOn(int d) {
return 0;
}
+/* TODO
+ * Not implemented yet
+ */
int ImmortalEngine::doorComeOut(MonsterID m) {
return 0;
}
-// These functions are not yet implemented
+/* TODO
+ * These functions are not yet implemented
+ */
void ImmortalEngine::doorSetLadders(MonsterID m) {}
void ImmortalEngine::doorDrawAll() {}
void ImmortalEngine::doorOnDoorMat() {}
Commit: 9c2e1a55a8c00abdd14d8a42a1f7bf4f2d55c554
https://github.com/scummvm/scummvm/commit/9c2e1a55a8c00abdd14d8a42a1f7bf4f2d55c554
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Remove unused debug console channel method
Changed paths:
engines/immortal/detection.cpp
engines/immortal/detection.h
diff --git a/engines/immortal/detection.cpp b/engines/immortal/detection.cpp
index 3f3615cfe76..ce19d7f2b7d 100644
--- a/engines/immortal/detection.cpp
+++ b/engines/immortal/detection.cpp
@@ -33,11 +33,6 @@
#include "immortal/detection.h"
#include "immortal/detection_tables.h"
-const DebugChannelDef ImmortalMetaEngineDetection::debugFlagList[] = {
- { Immortal::kDebugTest, "Test", "Test debug channel" },
- DEBUG_CHANNEL_END
-};
-
ImmortalMetaEngineDetection::ImmortalMetaEngineDetection() : AdvancedMetaEngineDetection(Immortal::gameDescriptions,
sizeof(ADGameDescription), Immortal::immortalGames) {
}
diff --git a/engines/immortal/detection.h b/engines/immortal/detection.h
index fa4c3fcbf9a..69699995ce4 100644
--- a/engines/immortal/detection.h
+++ b/engines/immortal/detection.h
@@ -54,9 +54,6 @@ public:
return "(c)1990 Will Harvey & Electronic Arts";
}
- const DebugChannelDef *getDebugChannels() const override {
- return debugFlagList;
- }
};
#endif
Commit: deb379462670a49dd76d12d6fe8d5d31a821ef52
https://github.com/scummvm/scummvm/commit/deb379462670a49dd76d12d6fe8d5d31a821ef52
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Use AStyle to fix indentation and format issues across all files
Changed paths:
engines/immortal/compression.cpp
engines/immortal/cycle.cpp
engines/immortal/disk.cpp
engines/immortal/disk.h
engines/immortal/flameSet.cpp
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/kernal.cpp
engines/immortal/level.cpp
engines/immortal/logic.cpp
engines/immortal/metaengine.cpp
engines/immortal/misc.cpp
engines/immortal/room.h
engines/immortal/sprite_list.h
engines/immortal/sprites.cpp
engines/immortal/story.cpp
engines/immortal/story.h
engines/immortal/utilities.cpp
diff --git a/engines/immortal/compression.cpp b/engines/immortal/compression.cpp
index 696a635b7db..08d7055149e 100644
--- a/engines/immortal/compression.cpp
+++ b/engines/immortal/compression.cpp
@@ -48,8 +48,8 @@ Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int sr
// The 20k bytes of memory that compression gets allocated to work with for the dictionary and the stack of chars
uint16 start[0x4000]; // Really needs a better name, remember to do this future me
- uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
- byte stack[0x4000]; // Stack of chars to be stored
+ uint16 ptk[0x4000]; // Pointer To Keys? Also needs a better name
+ byte stack[0x4000]; // Stack of chars to be stored
// These are the main variables we'll need for this
uint16 findEmpty;
@@ -73,8 +73,8 @@ Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int sr
}
finalChar = code;
- oldCode = code;
- myCode = code;
+ oldCode = code;
+ myCode = code;
outByte = code & kMaskLow;
dstW.writeByte(outByte); // Take just the lower byte and write it the output
@@ -85,17 +85,17 @@ Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int sr
code = getInputCode(carry, src, srcLen, evenOdd); // Get the next code
if (carry == true) {
- index = code << 1;
+ index = code << 1;
inputCode = code;
- myCode = code;
-
+ myCode = code;
+
// Split up the conditional statement to be easier to follow
uint16 cond;
cond = start[index] & kMaskLast;
cond |= ptk[index];
if ((cond & kMaskHigh) == 0) { // Empty code
- index = topStack;
+ index = topStack;
outByte = finalChar & kMaskLow;
stack[index] = outByte;
topStack++;
@@ -105,9 +105,9 @@ Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int sr
// :nextsymbol
index = myCode << 1;
while (index >= 0x200) {
- myCode = start[index] & kMask12Bit;
+ myCode = start[index] & kMask12Bit;
outByte = ptk[index] & kMaskLow;
- index = topStack;
+ index = topStack;
stack[index] = outByte;
topStack++;
index = myCode << 1;
@@ -115,7 +115,7 @@ Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int sr
// :singlechar
finalChar = (myCode >> 1);
- outByte = finalChar & kMaskLow;
+ outByte = finalChar & kMaskLow;
dstW.writeByte(outByte);
// :dump
@@ -126,8 +126,8 @@ Common::SeekableReadStream *ImmortalEngine::unCompress(Common::File *src, int sr
}
topStack = 0;
- code = getMember(oldCode, finalChar, findEmpty, start, ptk);
- oldCode = inputCode;
+ code = getMember(oldCode, finalChar, findEmpty, start, ptk);
+ oldCode = inputCode;
}
}
@@ -142,12 +142,12 @@ void ImmortalEngine::setupDictionary(uint16 start[], uint16 ptk[], uint16 &findE
// Clear the whole dictionary
for (int i = 0x3FFF; i >= 0; i--) {
start[i] = 0;
- ptk[i] = 0;
+ ptk[i] = 0;
}
// Set the initial 256 bytes to be value 256, these are the characters without extensions
for (int i = 255; i >= 0; i--) {
- ptk[i] = 256;
+ ptk[i] = 256;
}
// This shouldn't really be done inside the function, but for the sake of consistency with the source, we will
@@ -177,7 +177,7 @@ int ImmortalEngine::getInputCode(bool &carry, Common::File *src, int &srcLen, ui
uint16 ImmortalEngine::getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint16 start[], uint16 ptk[]) {
// This function is effectively void, as the return value is only used in compression
-
+
// k and codeW are local variables with the value of oldCode and finalChar
uint16 hash;
@@ -194,7 +194,7 @@ uint16 ImmortalEngine::getMember(uint16 codeW, uint16 k, uint16 &findEmpty, uint
uint16 b = ptk[hash] & kMaskHigh;
if (a | b) {
start[hash] = codeW;
- ptk[hash] = k | 0x100;
+ ptk[hash] = k | 0x100;
return ptk[hash];
}
@@ -228,7 +228,7 @@ void ImmortalEngine::appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &fi
prev = hash;
if (hash >= 0x200) {
setupDictionary(start, ptk, findEmpty);
-
+
} else {
bool found = false;
while (found == false) {
@@ -244,9 +244,9 @@ void ImmortalEngine::appendList(uint16 codeW, uint16 k, uint16 &hash, uint16 &fi
cond |= ptk[hash];
if ((cond & kMaskHigh) == 0) {
- findEmpty = hash;
+ findEmpty = hash;
start[hash] = codeW;
- ptk[hash] = k | 0x100;
+ ptk[hash] = k | 0x100;
link = hash >> 1;
diff --git a/engines/immortal/cycle.cpp b/engines/immortal/cycle.cpp
index 83d5cda2177..8441e1cd7b7 100644
--- a/engines/immortal/cycle.cpp
+++ b/engines/immortal/cycle.cpp
@@ -34,9 +34,9 @@
* Usually used as an index into the frame array by subtracting the frame enum first.
* Here's the movement of data from ROM to RAM:
* list of Cycles on heap (cyc)
- * |
+ * |
* list of word pointers in wram to Cycles (cycPtrs)
- * |
+ * |
* list of lexical pointers as byte indexes into word pointers (cycID -> cyclist)
*/
@@ -89,7 +89,7 @@ int Room::cycleGetFrame(int c) {
* STA DP : LDA (DP)
*/
return g_immortal->_cycPtrs[g_immortal->_cycles[c]._cycList]._frames[g_immortal->_cycles[c]._index];
- }
+}
int Room::cycleGetNumFrames(int c) {
// For whatever reason, this is not a property of the cycle, so it has to be re-calculated each time
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index e52a96c390c..5767aefac6c 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -29,13 +29,13 @@ namespace Immortal {
// --- ProDOSFile methods ---
ProDOSFile::ProDOSFile(char name[15], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk)
- : _type(type)
- , _totalBlocks(tBlk)
- , _eof(eof)
- , _blockPtr(bPtr)
- , _disk(disk) {
- strncpy(_name, name, 15);
- }
+ : _type(type)
+ , _totalBlocks(tBlk)
+ , _eof(eof)
+ , _blockPtr(bPtr)
+ , _disk(disk) {
+ strncpy(_name, name, 15);
+}
/* For debugging purposes, this prints the meta data of a file */
@@ -81,7 +81,7 @@ int ProDOSFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
for (int i = 0; i < blockNum; i++) {
dataSize = (i == (blockNum - 1)) ? rem : ProDOSDisk::kBlockSize;
dataOffset = _disk->readByte(); // Low byte is first
-
+
/* The cursor needs to know where to get the next pointer from in the index block,
* but it also needs to jump to the offset of data to read it, so we need to preserve
* the position in the index block it was in before.
@@ -93,7 +93,7 @@ int ProDOSFile::parseIndexBlock(byte *memOffset, int blockNum, int rem) const {
getDataBlock(memOffset + readSize, dataOffset, dataSize);
readSize += dataSize;
-
+
// And now we resume the position before this call
_disk->seek(diskPos);
}
@@ -129,7 +129,7 @@ Common::SeekableReadStream *ProDOSFile::createReadStream() const {
} else if (_type == kFileTypeSapling) {
_disk->seek(indexBlock);
parseIndexBlock(finalData, _totalBlocks - 1, remainder);
-
+
} else {
// If it's not a seed and not a sapling, it's a tree.
_disk->seek(indexBlock);
@@ -222,7 +222,7 @@ void ProDOSDisk::getDirectoryHeader(DirHeader *h) {
getHeader(h);
h->_parentBlockPtr = _disk.readUint16LE();
h->_parentEntryIndex = _disk.readByte();
- h->_parentEntryLen = _disk.readUint16LE();
+ h->_parentEntryLen = _disk.readUint16LE();
}
/* This is a little sneaky, but since the bulk of the header is the same, we're just going to pretend the volume header
@@ -233,7 +233,7 @@ void ProDOSDisk::getVolumeHeader(VolHeader *h) {
getHeader((DirHeader *)h);
h->_bitmapPtr = _disk.readUint16LE();
h->_volBlocks = _disk.readUint16LE();
- _volBlocks = h->_volBlocks;
+ _volBlocks = h->_volBlocks;
}
/* Getting a file entry header is very similar to getting a header, but with different data. */
@@ -280,7 +280,7 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
for (int i = 0; i < h->_fileCount; i++) {
// When we have read all the files for a given block (_entriesPerBlock), we need to change to the next block of the directory
if (parsedFiles == h->_entriesPerBlock) {
- parsedFiles = 0;
+ parsedFiles = 0;
_disk.seek(n * kBlockSize);
p = _disk.readUint16LE();
n = _disk.readUint16LE();
@@ -300,8 +300,8 @@ void ProDOSDisk::searchDirectory(DirHeader *h, uint16 p, uint16 n, Common::Strin
_files.setVal(fileName, Common::SharedPtr<ProDOSFile>(currFile));
_disk.seek(currPos);
-
- // Otherwise, if it is a subdirectory, we want to explore that subdirectory
+
+ // Otherwise, if it is a subdirectory, we want to explore that subdirectory
} else if (fileEntry._type == kFileTypeSubDir) {
_disk.seek(fileEntry._blockPtr * kBlockSize);
@@ -371,7 +371,7 @@ bool ProDOSDisk::open(const Common::String filename) {
ProDOSDisk::ProDOSDisk(const Common::String filename) {
if (open(filename)) {
- debug ("%s has been loaded", filename.c_str());
+ debug("%s has been loaded", filename.c_str());
}
}
diff --git a/engines/immortal/disk.h b/engines/immortal/disk.h
index 41292574db5..284e0f794a0 100644
--- a/engines/immortal/disk.h
+++ b/engines/immortal/disk.h
@@ -83,7 +83,7 @@ enum FileExt {
class ProDOSFile : public Common::ArchiveMember {
public:
ProDOSFile(char name[16], uint8 type, uint16 tBlk, uint32 eof, uint16 bPtr, Common::File *disk);
- ~ProDOSFile() {}; // File does not need a destructor, because the file it reads from is a pointer to Disk, and Disk has a destructor
+ ~ProDOSFile() {}; // File does not need a destructor, because the file it reads from is a pointer to Disk, and Disk has a destructor
// -- These are the Common::ArchiveMember related functions --
Common::String getName() const override; // Returns _name
@@ -95,12 +95,12 @@ public:
void printInfo();
private:
- char _name[16];
- uint8 _type; // As defined by enum FileType
- uint16 _blockPtr; // Block index in volume of index block or data
+ char _name[16];
+ uint8 _type; // As defined by enum FileType
+ uint16 _blockPtr; // Block index in volume of index block or data
uint16 _totalBlocks;
- uint32 _eof; // End Of File, used generally as size (exception being sparse files)
- Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
+ uint32 _eof; // End Of File, used generally as size (exception being sparse files)
+ Common::File *_disk; // This is a pointer because it is the same _disk as in ProDosDisk, passed to the file object
};
/* This class defines the entire disk volume. Upon using the open() method,
@@ -126,76 +126,76 @@ public:
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
- byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
- byte _loader2[kBlockSize];
-Common::String _name; // Name of volume
- Common::File _disk; // The volume file itself
- int _volBlocks; // Total blocks in volume
- byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
-Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDOSFile
+ byte _loader1[kBlockSize]; // There's not much reason for these to be needed, but I included them just in case
+ byte _loader2[kBlockSize];
+ Common::String _name; // Name of volume
+ Common::File _disk; // The volume file itself
+ int _volBlocks; // Total blocks in volume
+ byte *_volBitmap; // This can determine if the volume is corrupt as it contains a bit for every block, where 0 = unused, 1 = used
+ Common::HashMap<Common::String, Common::SharedPtr<ProDOSFile>> _files; // Hashmap of files in the volume, where key=Path, Value=ProDOSFile
struct Date {
- uint8 _day;
- uint8 _month;
- uint8 _year;
+ uint8 _day;
+ uint8 _month;
+ uint8 _year;
};
struct Time {
- uint8 _hour;
- uint8 _minute;
+ uint8 _hour;
+ uint8 _minute;
};
struct VolHeader {
- uint8 _type; // Not really important for a volume header, as this will always be F
- uint8 _nameLen;
- char _name[16];
- byte _reserved[8]; // Extra space reserved for possible future uses, not important
- Date _date;
- Time _time;
- uint8 _ver;
- uint8 _minVer; // Should pretty much always be 0 as far as I know
- uint8 _access; // If this ends up useful, there should be an enum for the access values
- uint8 _entryLen; // Always 27 in ProDOS 1.0
- uint8 _entriesPerBlock; // Always 0D in ProDOS 1.0
+ uint8 _type; // Not really important for a volume header, as this will always be F
+ uint8 _nameLen;
+ char _name[16];
+ byte _reserved[8]; // Extra space reserved for possible future uses, not important
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer; // Should pretty much always be 0 as far as I know
+ uint8 _access; // If this ends up useful, there should be an enum for the access values
+ uint8 _entryLen; // Always 27 in ProDOS 1.0
+ uint8 _entriesPerBlock; // Always 0D in ProDOS 1.0
uint16 _fileCount; // Number of files across all data blocks in this directory
uint16 _bitmapPtr; // Block pointer to the keyblock of the bitmap for the entire volume
uint16 _volBlocks; // Blocks in entire volume
};
struct DirHeader {
- uint8 _type;
- uint8 _nameLen;
- char _name[16];
- byte _reserved[8];
- Date _date;
- Time _time;
- uint8 _ver;
- uint8 _minVer;
- uint8 _access;
- uint8 _entryLen;
- uint8 _entriesPerBlock;
+ uint8 _type;
+ uint8 _nameLen;
+ char _name[16];
+ byte _reserved[8];
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer;
+ uint8 _access;
+ uint8 _entryLen;
+ uint8 _entriesPerBlock;
uint16 _fileCount;
uint16 _parentBlockPtr; // These values allow ProDOS to navigate back out of a directory, but they aren't really needed by the class to navigate
- uint8 _parentEntryIndex; // Index in the current directory
- uint8 _parentEntryLen; // This is always 27 in ProDOS 1.0
+ uint8 _parentEntryIndex; // Index in the current directory
+ uint8 _parentEntryLen; // This is always 27 in ProDOS 1.0
};
struct FileEntry {
- uint8 _type; // 0 = inactive, 1-3 = file, 4 = pascal area, 14 = subdirectory, 15 = volume directory
- uint8 _nameLen;
- char _name[16];
- uint8 _ext; // File extension, uses the enum FileExt
+ uint8 _type; // 0 = inactive, 1-3 = file, 4 = pascal area, 14 = subdirectory, 15 = volume directory
+ uint8 _nameLen;
+ char _name[16];
+ uint8 _ext; // File extension, uses the enum FileExt
uint16 _blockPtr; // Block pointer to data for seedling, index block for sapling, or master block for tree
uint16 _totalBlocks; // Really important to remember this is the total *including* the index block
uint32 _eof; // This is a long (3 bytes, read low to high) value representing the total readable data in a file (unless it's a sparse file, be careful!)
- Date _date;
- Time _time;
- uint8 _ver;
- uint8 _minVer;
- uint8 _access;
+ Date _date;
+ Time _time;
+ uint8 _ver;
+ uint8 _minVer;
+ uint8 _access;
uint16 _varUse;
- Date _modDate;
- Time _modTime;
+ Date _modDate;
+ Time _modTime;
uint16 _dirHeadPtr; // Pointer to the key block of the directory that contains this file entry
};
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index dcb4c9f4307..f9a7964f8d3 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -111,12 +111,14 @@ int Room::flameGetCyc(Flame *f, int first) {
* This gives us a random entry within the array to start at.
*/
CycID flamePatA[] = {kCycFNormal0, kCycFNormal1, kCycFNormal2,
- kCycFNormal0, kCycFNormal1, kCycFNormal2,
- kCycFNormal0, kCycFNormal1, kCycFNormal2,
- kCycFNormal0, kCycFNormal1, kCycFNormal2};
+ kCycFNormal0, kCycFNormal1, kCycFNormal2,
+ kCycFNormal0, kCycFNormal1, kCycFNormal2,
+ kCycFNormal0, kCycFNormal1, kCycFNormal2
+ };
CycID flamePatB[] = {kCycFCandleBurst, kCycFCandleSway, kCycFCandleJump,
- kCycFCandleLeap, kCycFCandleFlicker,
- kCycFCandleFlicker, kCycFCandleFlicker, kCycFCandleFlicker};
+ kCycFCandleLeap, kCycFCandleFlicker,
+ kCycFCandleFlicker, kCycFCandleFlicker, kCycFCandleFlicker
+ };
CycID flamePatC[] = {kCycFOff};
CycID flamePatD[] = {kCycFFlicker0, kCycFFlicker1, kCycFFlicker2};
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index a47301a3cf2..e7c5ca7913b 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -89,7 +89,7 @@ uint16 ImmortalEngine::mult16(uint16 a, uint16 b) {
/* We aren't using the game's multiplication function (mult16), but we do want
* to retain the ability to drop the second word, without doing (uint16) every time
*/
- return (uint16) (a * b);
+ return (uint16)(a * b);
}
// -----------------------------------------------------
@@ -141,7 +141,7 @@ Common::Error ImmortalEngine::run() {
_mainSurface = new Graphics::Surface();
_mainSurface->create(kResH, kResV, Graphics::PixelFormat::createFormatCLUT8());
-
+
_screenBuff = new byte[kScreenSize];
if (initDisks() != Common::kNoError) {
@@ -156,33 +156,33 @@ Common::Error ImmortalEngine::run() {
_penY = 7;
_penX = 1;
- initStoryStatic(); // Init the arrays of static story elements (done at compile time in the source)
- loadPalette(); // We need to grab the palette from the disk first
+ initStoryStatic(); // Init the arrays of static story elements (done at compile time in the source)
+ loadPalette(); // We need to grab the palette from the disk first
// This is the equivalent of Main->InitGraphics->MyClearScreen in Driver
- useNormal(); // The first palette will be the default
-
- loadFont(); // Load the font sprites
- loadWindow(); // Load the window background
- loadSingles("Song A"); // Music
- loadSprites(); // Get all the sprite data into memory
+ useNormal(); // The first palette will be the default
+
+ loadFont(); // Load the font sprites
+ loadWindow(); // Load the window background
+ loadSingles("Song A"); // Music
+ loadSprites(); // Get all the sprite data into memory
_playing = kSongNothing;
_themePaused = 0;
- clearSprites(); // Clear the sprites before we start
+ clearSprites(); // Clear the sprites before we start
// This is where the request play disk would happen, but that's not needed here
- logicInit(); // Init the game logic
+ logicInit(); // Init the game logic
_err = Common::kNoError;
while (!shouldQuit()) {
- /* The game loop runs at 60fps, which is 16 milliseconds per frame.
- * This loop keeps that time by getting the time in milliseconds at the start of the loop,
- * then again at the end, and the difference between them is the remainder
- * of the frame budget. If that remainder is within the 16 millisecond budget,
- * then it delays ScummVM for the remainder. If it is 0 or negative, then it continues.
- */
+ /* The game loop runs at 60fps, which is 16 milliseconds per frame.
+ * This loop keeps that time by getting the time in milliseconds at the start of the loop,
+ * then again at the end, and the difference between them is the remainder
+ * of the frame budget. If that remainder is within the 16 millisecond budget,
+ * then it delays ScummVM for the remainder. If it is 0 or negative, then it continues.
+ */
int64 loopStart = g_system->getMillis();
// Main
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 7b40f084f6e..2e2b520b490 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -70,11 +70,11 @@ namespace Immortal {
enum InputAction {
kActionNothing,
kActionKey,
- kActionRestart, // Key "R" <-- Debug?
+ kActionRestart, // Key "R" <-- Debug?
kActionSound,
kActionFire,
- kActionButton, // Does this just refer to whatever is not the fire button?
- kActionDBGStep // Debug key for moving engine forward one frame at a time
+ kActionButton, // Does this just refer to whatever is not the fire button?
+ kActionDBGStep // Debug key for moving engine forward one frame at a time
};
enum ButtonHeldMask {
@@ -154,18 +154,18 @@ struct GenericSprite {
// Doors are a property of the level, not the room, they define the connections between rooms
struct Door {
- uint8 _x = 0;
- uint8 _y = 0;
- uint8 _fromRoom = 0;
- uint8 _toRoom = 0;
+ uint8 _x = 0;
+ uint8 _y = 0;
+ uint8 _fromRoom = 0;
+ uint8 _toRoom = 0;
uint8 _busyOnRight = 0;
- uint8 _on = 0;
+ uint8 _on = 0;
};
// Universe is a set of properties for the entire level, nor just the room
struct Univ {
- uint16 _rectX = 0;
- uint16 _rectY = 0;
+ uint16 _rectX = 0;
+ uint16 _rectY = 0;
uint16 _numAnims = 0;
uint16 _numCols = 0;
uint16 _numRows = 0;
@@ -199,10 +199,10 @@ public:
/* Terrible functions because C doesn't like
* bit manipulation enough
*/
- uint16 xba(uint16 ab); // This just replicates the XBA command from the 65816, because flipping the byte order is somehow not a common library function???
- uint16 rol(uint16 ab, int n); // Rotate bits left by n
- uint16 ror(uint16 ab, int n); // Rotate bits right by n
- uint16 mult16(uint16 a, uint16 b); // Just avoids using (uint16) everywhere, and is slightly closer to the original
+ uint16 xba(uint16 ab); // This just replicates the XBA command from the 65816, because flipping the byte order is somehow not a common library function???
+ uint16 rol(uint16 ab, int n); // Rotate bits left by n
+ uint16 ror(uint16 ab, int n); // Rotate bits right by n
+ uint16 mult16(uint16 a, uint16 b); // Just avoids using (uint16) everywhere, and is slightly closer to the original
/*
* --- Members ---
@@ -219,239 +219,244 @@ public:
const int kMaxCertificate = 16;
// Screen constants
- const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
- const int kScreenH__ = 128; // ???
+ const int kScreenW__ = 128; // ??? labeled in source as SCREENWIDTH
+ const int kScreenH__ = 128; // ???
const int kViewPortW = 256;
const int kViewPortH = 128;
- const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is (320x200) * 2 byte words
+ const int kScreenSize = (kResH *kResV) * 2; // The size of the screen buffer is (320x200) * 2 byte words
const uint16 kScreenLeft = 32;
const uint16 kScreenTop = 20;
const uint8 kTextLeft = 8;
const uint8 kTextTop = 4;
const uint8 kGaugeX = 0;
- const uint8 kGaugeY = -13; // ???
- const uint16 kScreenBMW = 160; // Screen BitMap Width?
- const uint16 kChrW = 64;
+ const uint8 kGaugeY = -13; // ???
+ const uint16 kScreenBMW = 160; // Screen BitMap Width?
+ const uint16 kChrW = 64;
const uint16 kChrH = 32;
const uint16 kChrH2 = kChrH * 2;
const uint16 kChrH3 = kChrH * 3;
- const uint16 kChrLen = (kChrW / 2) * kChrH;
- const uint16 kChrBMW = kChrW / 2;
+ const uint16 kChrLen = (kChrW / 2) * kChrH;
+ const uint16 kChrBMW = kChrW / 2;
const uint16 kLCutaway = 4;
const uint16 kLDrawSolid = 32 * ((3 * 16) + 5);
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
- kChrH2, kChrH, kChrH2, kChrH2, kChr0,
- kChr0, kChrH2, kChrH, kChrH2, kChrH2,
- kChrH2, kChrH, kChrH2, kChrH2};
+ kChrH2, kChrH, kChrH2, kChrH2, kChr0,
+ kChr0, kChrH2, kChrH, kChrH2, kChrH2,
+ kChrH2, kChrH, kChrH2, kChrH2
+ };
const uint16 kChrMask[19] = {kChr0, kChr0, kChr0, kChr0,
- kChrR, kChrL, kChr0, kChrL,
- kChrR, kChr0, kChr0, kChrLD,
- kChr0, kChrR, kChrLD, kChrRD,
- kChr0, kChrRD, kChrL};
+ kChrR, kChrL, kChr0, kChrL,
+ kChrR, kChr0, kChr0, kChrLD,
+ kChr0, kChrR, kChrLD, kChrRD,
+ kChr0, kChrRD, kChrL
+ };
const uint16 kIsBackground[36] = {1, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0};
+ 0, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+ };
const uint16 kTBlisterCorners[60] = {7, 1, 1, 1, 1, 1, 5, 3, 1, 1, 1, 1, 1, 3, 5, 3, 5, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 16, 16, 16, 16, 8,
- 8, 8, 8, 16, 16, 16, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 16, 16, 16, 16, 8,
+ 8, 8, 8, 16, 16, 16, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
const uint16 kTLogicalCorners[19] = {1, 1, 1, 1, 16, 8, 1, 8,
- 16, 1, 1, 8, 1, 16, 8, 16,
- 1, 16, 8};
+ 16, 1, 1, 8, 1, 16, 8, 16,
+ 1, 16, 8
+ };
// Disk offsets
- const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
+ const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
- const uint16 kMaxSpriteW = 64;
- const uint16 kMaxSpriteH = 64;
- const uint16 kSpriteDY = 32;
- const uint16 kVSX = kMaxSpriteW;
- const uint16 kVSY = kSpriteDY;
- const uint16 kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
- const uint16 kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
- const uint16 kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
+ const uint16 kMaxSpriteW = 64;
+ const uint16 kMaxSpriteH = 64;
+ const uint16 kSpriteDY = 32;
+ const uint16 kVSX = kMaxSpriteW;
+ const uint16 kVSY = kSpriteDY;
+ const uint16 kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
+ const uint16 kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
+ const uint16 kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
const uint16 kMySuperBottom = kVSDY + kViewPortH;
- const uint16 kSuperBottom = 200;
- const uint16 kMySuperTop = kVSDY;
- const uint16 kSuperTop = 0;
- const uint16 kViewPortSpX = 32;
- const uint16 kViewPortSpY = 0;
- const uint16 kWizardX = 28; // Common sprite center for some reason
- const uint16 kWizardY = 37;
- const uint16 kObjectY = 24;
- const uint16 kObjectX = 32;
+ const uint16 kSuperBottom = 200;
+ const uint16 kMySuperTop = kVSDY;
+ const uint16 kSuperTop = 0;
+ const uint16 kViewPortSpX = 32;
+ const uint16 kViewPortSpY = 0;
+ const uint16 kWizardX = 28; // Common sprite center for some reason
+ const uint16 kWizardY = 37;
+ const uint16 kObjectY = 24;
+ const uint16 kObjectX = 32;
const uint16 kObjectHeight = 48;
- const uint16 kObjectWidth = 64;
+ const uint16 kObjectWidth = 64;
// Text constants
- const uint8 kMaxRows = 5;
- const uint8 kMaxCollumns = 26;
+ const uint8 kMaxRows = 5;
+ const uint8 kMaxCollumns = 26;
- const uint16 kYesNoY = 88;
- const uint16 kYesNoX1 = 8;
- const uint16 kYesNoX2 = 182;
+ const uint16 kYesNoY = 88;
+ const uint16 kYesNoX1 = 8;
+ const uint16 kYesNoX2 = 182;
// Asset constants
- const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
- const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
- const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
- const char kGaugeStart = 1; // First kGaugeOn char to draw
+ const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
+ const char kGaugeOff = 0; // Off uses the sprite at index 0 of the font spriteset
+ const char kGaugeStop = 1; // Literally just means the final kGaugeOn char to draw
+ const char kGaugeStart = 1; // First kGaugeOn char to draw
// Level constants
- const int kStoryNull = 5;
+ const int kStoryNull = 5;
const int kMaxFilesPerLevel = 16;
const int kMaxPartInstances = 4;
- const int kLevelToMaze[8] = {0,0,1,1,2,2,2,3};
+ const int kLevelToMaze[8] = {0, 0, 1, 1, 2, 2, 2, 3};
- /*
+ /*
* 'global' members
*/
// Misc
- Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
- uint8 _certificate[16]; // The certificate (password) is basically the inventory/equipment array
- uint8 _lastCertLen = 0;
- bool _draw = 0; // Whether the screen should draw this frame
- int _zero = 0; // No idea what this is yet
- bool _gameOverFlag = false;
- uint8 _gameFlags = 0; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
- bool _themePaused = false; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
- int _titlesShown = 0;
- int _time = 0;
- int _promoting = 0; // I think promoting means the title stuff
- bool _restart = false;
+ Common::ErrorCode _err; // If this is not kNoError at any point, the engine will stop
+ uint8 _certificate[16]; // The certificate (password) is basically the inventory/equipment array
+ uint8 _lastCertLen = 0;
+ bool _draw = 0; // Whether the screen should draw this frame
+ int _zero = 0; // No idea what this is yet
+ bool _gameOverFlag = false;
+ uint8 _gameFlags = 0; // Bitflag array of event flags, but only two were used (saving ana and saving the king) <-- why is gameOverFlag not in this? Lol
+ bool _themePaused = false; // In the source, this is actually considered a bit flag array of 2 bits (b0 and b1). However, it only ever checks for non-zero, so it's effectively only 1 bit.
+ int _titlesShown = 0;
+ int _time = 0;
+ int _promoting = 0; // I think promoting means the title stuff
+ bool _restart = false;
// Story members
Story _stories[8];
// Level members
- int _maxLevels = 0; // This is determined when loading in story files
- int _level = 0;
- bool _levelOver = false;
- int _count = 0;
- int _lastLevelLoaded = 0;
- int _lastSongLoaded = 0;
- int _storyLevel = 0;
- int _storyX = 0;
- int _loadA = 0;
- int _loadY = 0;
- uint16 _initialX = 0;
- uint16 _initialY = 0;
- int _initialBX = 0;
- int _initialBY = 0;
- int _dRoomNum = 0;
- int _initialRoom = 0;
- int _currentRoom = 0;
- int _lastType = 0;
- int _roomCellX = 0;
- int _roomCellY = 0;
- Room *_rooms[kMaxRooms]; // Rooms within the level
- Common::Array<SFlame> _allFlames[kMaxRooms]; // The level needs it's own set of flames so that the flames can be turned on/off permenantly. This is technically more like a hashmap in the source, but it could also be seen as a 2d array, just hashed together in the source
+ int _maxLevels = 0; // This is determined when loading in story files
+ int _level = 0;
+ bool _levelOver = false;
+ int _count = 0;
+ int _lastLevelLoaded = 0;
+ int _lastSongLoaded = 0;
+ int _storyLevel = 0;
+ int _storyX = 0;
+ int _loadA = 0;
+ int _loadY = 0;
+ uint16 _initialX = 0;
+ uint16 _initialY = 0;
+ int _initialBX = 0;
+ int _initialBY = 0;
+ int _dRoomNum = 0;
+ int _initialRoom = 0;
+ int _currentRoom = 0;
+ int _lastType = 0;
+ int _roomCellX = 0;
+ int _roomCellY = 0;
+ Room *_rooms[kMaxRooms]; // Rooms within the level
+ Common::Array<SFlame> _allFlames[kMaxRooms]; // The level needs it's own set of flames so that the flames can be turned on/off permenantly. This is technically more like a hashmap in the source, but it could also be seen as a 2d array, just hashed together in the source
// Door members
Common::Array<Door> _doors;
- uint8 _numDoors = 0;
- uint8 _doorRoom = 0;
+ uint8 _numDoors = 0;
+ uint8 _doorRoom = 0;
uint8 _doorToNextLevel = 0;
uint8 _doorCameInFrom = 0;
- uint8 _ladders = 0;
- uint8 _numLadders = 0;
- uint8 _ladderInUse = 0;
+ uint8 _ladders = 0;
+ uint8 _numLadders = 0;
+ uint8 _ladderInUse = 0;
uint8 _secretLadder = 0;
uint8 _secretCount = 0;
uint8 _secretDelta = 0;
// Debug members
- bool _singleStep = false; // Flag for _singleStep mode
+ bool _singleStep = false; // Flag for _singleStep mode
// Input members
- int _pressedAction = 0;
- int _heldAction = 0;
+ int _pressedAction = 0;
+ int _heldAction = 0;
int _pressedDirection = 0;
- int _heldDirection = 0;
+ int _heldDirection = 0;
// Text printing members
uint8 _slowText = 0;
uint8 _formatted = 0;
- uint8 _collumn = 0;
- uint8 _row = 0;
- uint8 _myButton = 0;
- uint8 _lastYes = 0;
+ uint8 _collumn = 0;
+ uint8 _row = 0;
+ uint8 _myButton = 0;
+ uint8 _lastYes = 0;
// Music members
- Song _playing; // Currently playing song
- int _themeID = 0; // Not sure yet tbh
+ Song _playing; // Currently playing song
+ int _themeID = 0; // Not sure yet tbh
int _combatID = 0;
// Asset members
- int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
- DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteName
- Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
- Cycle _cycles[kMaxCycles];
- Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
+ int _numSprites = 0; // This is more accurately actually the index within the sprite array, so _numSprites + 1 is the current number of sprites
+ DataSprite _dataSprites[kFont + 1]; // All the sprite data, indexed by SpriteName
+ Sprite _sprites[kMaxSprites]; // All the sprites shown on screen
+ Cycle _cycles[kMaxCycles];
+ Common::Array<Common::String> _strPtrs; // Str should really be a char array, but inserting frame values will be stupid so it's just a string instead
Common::Array<Motive> _motivePtrs;
Common::Array<Damage> _damagePtrs;
- Common::Array<Use> _usePtrs;
+ Common::Array<Use> _usePtrs;
Common::Array<Pickup> _pickupPtrs;
- Common::Array<SCycle> _cycPtrs; // This is not actually a set of pointers, but it is serving the function of what was called cycPtrs in the source
- CArray2D<Motive> _programPtrs;
+ Common::Array<SCycle> _cycPtrs; // This is not actually a set of pointers, but it is serving the function of what was called cycPtrs in the source
+ CArray2D<Motive> _programPtrs;
Common::Array<ObjType> _objTypePtrs;
// Universe members in order of their original memory layout
- uint16 *_logicalCNM; // As confusing as this is, we get Logical CNM from the .CNM file, and we get the CNM from the .UNV file
+ uint16 *_logicalCNM; // As confusing as this is, we get Logical CNM from the .CNM file, and we get the CNM from the .UNV file
uint16 *_modCNM;
uint16 *_modLogicalCNM;
- Univ *_univ; // Pointer to the struct that contains the universe properties
- Common::SeekableReadStream *_dataBuffer; // This contains the CNM and the CBM
- uint16 *_CNM; // Stands for CHARACTER NUMBER MAP
- byte *_CBM; // Stands for CHARACTER BIT MAP (?)
- byte *_oldCBM;
+ Univ *_univ; // Pointer to the struct that contains the universe properties
+ Common::SeekableReadStream *_dataBuffer; // This contains the CNM and the CBM
+ uint16 *_CNM; // Stands for CHARACTER NUMBER MAP
+ byte *_CBM; // Stands for CHARACTER BIT MAP (?)
+ byte *_oldCBM;
uint16 _myCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
uint16 _myModCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
uint16 _myModLCNM[(kViewPortCW + 1)][(kViewPortCH + 1)];
// Screen members
- byte *_screenBuff; // The final buffer that will transfer to the screen
+ byte *_screenBuff; // The final buffer that will transfer to the screen
uint16 _columnX[kViewPortCW + 1];
uint16 _columnTop[kViewPortCW + 1];
- uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
+ uint16 _columnIndex[kViewPortCW + 1]; // Why the heck is this an entire array, when it's just an index that gets zeroed before it gets used anyway...
uint16 _tIndex[kMaxDrawItems];
uint16 _tPriority[kMaxDrawItems];
- uint16 _viewPortX = 0;
- uint16 _viewPortY = 0;
- uint16 _myViewPortX = 0; // Probably mirror of viewportX
+ uint16 _viewPortX = 0;
+ uint16 _viewPortY = 0;
+ uint16 _myViewPortX = 0; // Probably mirror of viewportX
uint16 _myViewPortY = 0;
- int _lastGauge = 0; // Mirror for player health, used to update health gauge display
- uint16 _lastBMW = 0; // Mirrors used to determine where bitmap width needs to be re-calculated
- uint16 _lastY = 0;
- uint16 _lastPoint = 0;
- uint16 _penX = 0; // Basically where in the screen we are currently drawing
- uint16 _penY = 0;
+ int _lastGauge = 0; // Mirror for player health, used to update health gauge display
+ uint16 _lastBMW = 0; // Mirrors used to determine where bitmap width needs to be re-calculated
+ uint16 _lastY = 0;
+ uint16 _lastPoint = 0;
+ uint16 _penX = 0; // Basically where in the screen we are currently drawing
+ uint16 _penY = 0;
uint16 _myUnivPointX = 0;
uint16 _myUnivPointY = 0;
- int _num2DrawItems = 0;
+ int _num2DrawItems = 0;
Graphics::Surface *_mainSurface;
-GenericSprite _genSprites[6];
+ GenericSprite _genSprites[6];
// Palette members
- int _dontResetColors = 0; // Not sure yet
- bool _usingNormal = 0; // Whether the palette is using normal
- bool _dim = 0; // Whether the palette is dim
+ int _dontResetColors = 0; // Not sure yet
+ bool _usingNormal = 0; // Whether the palette is using normal
+ bool _dim = 0; // Whether the palette is dim
uint16 _palUniv[16];
uint16 _palDefault[16];
uint16 _palWhite[16];
uint16 _palBlack[16];
uint16 _palDim[16];
- byte _palRGB[48]; // Palette that ScummVM actually uses, which is an RGB conversion of the original
+ byte _palRGB[48]; // Palette that ScummVM actually uses, which is an RGB conversion of the original
/*
@@ -464,34 +469,34 @@ GenericSprite _genSprites[6];
*/
// Screen
- void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
- void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
- void rect(int x, int y, int w, int h); // Draws a solid rectangle at x,y with size w,h. Also shadows for blit?
- void backspace(); // Moves draw position back and draws empty rect in place of char
+ void clearScreen(); // Draws a black rectangle on the screen buffer but only inside the frame
+ void whiteScreen(); // Draws a white rectanlge on the screen buffer (but does not do anything with resetColors)
+ void rect(int x, int y, int w, int h); // Draws a solid rectangle at x,y with size w,h. Also shadows for blit?
+ void backspace(); // Moves draw position back and draws empty rect in place of char
void printByte(int b);
void printChr(char c);
- void loadWindow(); // Gets the window.bm file
- void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
- void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
- void mungeBM(); // Put together final bitmap?
- void blit(); // Will probably want this to be it's own function
- void blit40(); // Uses macro blit 40 times
+ void loadWindow(); // Gets the window.bm file
+ void drawUniv(); // Draw the background, add the sprites, determine draw order, draw the sprites
+ void copyToScreen(); // If draw is 0, just check input, otherwise also copy the screen buffer to the scummvm surface and update screen
+ void mungeBM(); // Put together final bitmap?
+ void blit(); // Will probably want this to be it's own function
+ void blit40(); // Uses macro blit 40 times
void sBlit();
void scroll();
- void makeMyCNM(); // ?
- void drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
- void addRows(); // Add rows to drawitem array
+ void makeMyCNM(); // ?
+ void drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
+ void addRows(); // Add rows to drawitem array
void addSprite(uint16 vpX, uint16 vpY, SpriteName s, int img, uint16 x, uint16 y, uint16 p);
- void addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
- void sortDrawItems(); // Sort said items
- void drawItems(); // Draw the items over the background
+ void addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
+ void sortDrawItems(); // Sort said items
+ void drawItems(); // Draw the items over the background
void drawIcon(int img);
- void setPen(uint16 penX, uint16 penY); // Sets the 'pen' x and y positions, including making y negative if above a certain point
+ void setPen(uint16 penX, uint16 penY); // Sets the 'pen' x and y positions, including making y negative if above a certain point
void center();
void carriageReturn();
// Music
- void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
+ void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
void fixPause();
Song getPlaying();
void playMazeSong();
@@ -501,46 +506,46 @@ GenericSprite _genSprites[6];
void stopMusic();
void musicPause(int sID);
void musicUnPause(int sID);
- void loadSingles(Common::String songName); // Loads and then parse the maze song
+ void loadSingles(Common::String songName); // Loads and then parse the maze song
void standardBeep();
// Palette
- void loadPalette(); // Get the static palette data from the disk
- void setColors(uint16 pal[]); // Applies the current palette to the ScummVM surface palette
- void fixColors(); // Determine whether the screen should be dim or normal
+ void loadPalette(); // Get the static palette data from the disk
+ void setColors(uint16 pal[]); // Applies the current palette to the ScummVM surface palette
+ void fixColors(); // Determine whether the screen should be dim or normal
void useNormal();
void useDim();
void useBlack();
void useWhite();
- void pump(); // Alternates between white and black with delays in between (flashes screen)
+ void pump(); // Alternates between white and black with delays in between (flashes screen)
void fadePal(uint16 pal[], int count, uint16 target[]); // Fades the palette except the frame
- void fade(uint16 pal[], int dir, int delay); // Calls fadePal() by a given delay each iteration
- void fadeOut(int j); // Calls Fade with a delay of j jiffies and direction 1
- void fadeIn(int j); // || and direction 0
+ void fade(uint16 pal[], int dir, int delay); // Calls fadePal() by a given delay each iteration
+ void fadeOut(int j); // Calls Fade with a delay of j jiffies and direction 1
+ void fadeIn(int j); // || and direction 0
void normalFadeOut();
void slowFadeOut();
void normalFadeIn();
// Assets
Common::SeekableReadStream *loadIFF(Common::String fileName); // Loads a file and uncompresses if it is compressed
- void initStoryStatic(); // Sets up all of the global static story elements
- int loadUniv(char mazeNum); // Unpacks the .CNM and .UNV files into all the CNM stuff, returns the total length of everything
- void loadMazeGraphics(int m); // Creates a universe with a maze
- void makeBlisters(int povX, int povY); // Turns the unmodified CNM/CBM/LCNM etc into the modified ones to actually be used for drawing the game
- void loadFont(); // Gets the font.spr file, and centers the sprite
- void clearSprites(); // Clears all sprites before drawing the current frame
- void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
+ void initStoryStatic(); // Sets up all of the global static story elements
+ int loadUniv(char mazeNum); // Unpacks the .CNM and .UNV files into all the CNM stuff, returns the total length of everything
+ void loadMazeGraphics(int m); // Creates a universe with a maze
+ void makeBlisters(int povX, int povY); // Turns the unmodified CNM/CBM/LCNM etc into the modified ones to actually be used for drawing the game
+ void loadFont(); // Gets the font.spr file, and centers the sprite
+ void clearSprites(); // Clears all sprites before drawing the current frame
+ void loadSprites(); // Loads all the sprite files and centers their sprites (in spritelist, but called from kernal)
// Input
- void userIO(); // Get input
- void pollKeys(); // Buffer input
- void noNetwork(); // Setup input mirrors
- void waitKey(); // Waits until a key is pressed (until getInput() returns true)
- void waitClick(); // Waits until one of the two buttons is pressed
- void blit8(); // This is actually just input, but it is called blit because it does a 'paddle blit' 8 times
+ void userIO(); // Get input
+ void pollKeys(); // Buffer input
+ void noNetwork(); // Setup input mirrors
+ void waitKey(); // Waits until a key is pressed (until getInput() returns true)
+ void waitClick(); // Waits until one of the two buttons is pressed
+ void blit8(); // This is actually just input, but it is called blit because it does a 'paddle blit' 8 times
// These will replace the myriad of hardware input handling from the source
- bool getInput(); // True if there was input, false if not
+ bool getInput(); // True if there was input, false if not
void addKeyBuffer();
void clearKeyBuff();
@@ -564,24 +569,24 @@ GenericSprite _genSprites[6];
void drawLRHC(int chr, int x, int y);
- /*
+ /*
* [Logic.cpp] Functions from Logic.GS
*/
// Debug
- void doSingleStep(); // Let the user advance the engine one frame at a time
+ void doSingleStep(); // Let the user advance the engine one frame at a time
// Main
- void trapKeys(); // Poorly named, this checks if the player wants to restart/pause music/use debug step
- int keyOrButton(); // Returns value based on whether it was a keyboard key or a button press
+ void trapKeys(); // Poorly named, this checks if the player wants to restart/pause music/use debug step
+ int keyOrButton(); // Returns value based on whether it was a keyboard key or a button press
void logicInit();
- void logic(); // Keeps time, handles win and lose conditions, then general logic
- void restartLogic(); // This is the actual logic init
- int logicFreeze(); // Overcomplicated way to check if game over or level over
+ void logic(); // Keeps time, handles win and lose conditions, then general logic
+ void restartLogic(); // This is the actual logic init
+ int logicFreeze(); // Overcomplicated way to check if game over or level over
void updateHitGauge();
void drawGauge(int h);
void makeCertificate();
- void calcCheckSum(int l, uint8 checksum[]); // Checksum is one word, but the source called it CheckSum
+ void calcCheckSum(int l, uint8 checksum[]); // Checksum is one word, but the source called it CheckSum
bool getCertificate();
void printCertificate();
@@ -594,7 +599,7 @@ GenericSprite _genSprites[6];
bool isSavedKing();
void setSavedAna();
bool isSavedAna();
- int getLevel(); // Literally just return _level...
+ int getLevel(); // Literally just return _level...
void gameOverDisplay();
void gameOver();
void levelOver();
@@ -662,7 +667,7 @@ GenericSprite _genSprites[6];
*/
// Misc
- void cycleFreeAll(); // Delete all cycles
+ void cycleFreeAll(); // Delete all cycles
/*
@@ -678,7 +683,7 @@ GenericSprite _genSprites[6];
// Init
void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
-
+
// Main
void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom);
bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, uint16 superTop, uint16 superBottom);
@@ -702,7 +707,7 @@ GenericSprite _genSprites[6];
* [door.cpp] Functions from Door.GS
*/
- void roomTransfer(int r, int x, int y); // Transfers the player from the current room to a new room at x,y
+ void roomTransfer(int r, int x, int y); // Transfers the player from the current room to a new room at x,y
void doorOpenSecret();
void doorCloseSecret();
//void doorToNextLevel();
@@ -711,7 +716,7 @@ GenericSprite _genSprites[6];
void doorNew(SDoor door);
void doorDrawAll();
void doorOnDoorMat();
- //void doorEnter(); // <-- this is actually a method of Player Monster, should probably move it there later
+ //void doorEnter(); // <-- this is actually a method of Player Monster, should probably move it there later
int findDoorTop(int x, int y);
int findDoor(int x, int y);
bool doLockStuff(int d, MonsterID m, int top);
@@ -735,9 +740,9 @@ GenericSprite _genSprites[6];
*
*/
- Common::ErrorCode initDisks(); // Opens and parses IMMORTAL.dsk and IMMORTAL_GFX.dsk
- uint32 getFeatures() const; // Returns the game description flags
- Common::String getGameId() const; // Returns the game Id
+ Common::ErrorCode initDisks(); // Opens and parses IMMORTAL.dsk and IMMORTAL_GFX.dsk
+ uint32 getFeatures() const; // Returns the game description flags
+ Common::String getGameId() const; // Returns the game Id
/* Gets a random number
*/
@@ -747,9 +752,9 @@ GenericSprite _genSprites[6];
bool hasFeature(EngineFeature f) const override {
return
- (f == kSupportsLoadingDuringRuntime) ||
- (f == kSupportsSavingDuringRuntime) ||
- (f == kSupportsReturnToLauncher);
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime) ||
+ (f == kSupportsReturnToLauncher);
};
bool canLoadGameStateCurrently() override {
@@ -765,13 +770,13 @@ GenericSprite _genSprites[6];
Common::Error syncGame(Common::Serializer &s);
/* Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) {
- Common::Serializer s(nullptr, stream);
- return syncGame(s);
+ Common::Serializer s(nullptr, stream);
+ return syncGame(s);
}
Common::Error loadGameStream(Common::SeekableReadStream *stream) {
- Common::Serializer s(stream, nullptr);
- return syncGame(s);
+ Common::Serializer s(stream, nullptr);
+ return syncGame(s);
} */
};
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 7677cafab60..00f0d27ed3d 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -30,7 +30,7 @@
namespace Immortal {
-/*
+/*
*
* ----- -----
* ----- Screen Drawing Functions -----
@@ -49,11 +49,11 @@ void ImmortalEngine::drawUniv() {
_myUnivPointY = !(_myViewPortY & (kChrH - 1)) + kViewPortSpY;
//makeMyCNM();
- //drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
- //addRows(); // Add rows to drawitem array
- //addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
- //sortDrawItems(); // Sort said items
- //drawItems(); // Draw the items over the background
+ //drawBGRND(); // Draw floor parts of leftmask rightmask and maskers
+ //addRows(); // Add rows to drawitem array
+ //addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
+ //sortDrawItems(); // Sort said items
+ //drawItems(); // Draw the items over the background
}
void ImmortalEngine::copyToScreen() {
@@ -79,7 +79,7 @@ void ImmortalEngine::clearScreen() {
_screenBuff[((y + 20) * kResH) + (x + 32)] = 0;
}
}
-
+
_penX = kTextLeft;
_penY = kTextTop;
@@ -109,8 +109,8 @@ void ImmortalEngine::addRows() {
// I'm not really sure how this works yet
int i = _num2DrawItems;
_tPriority[i] = !(!(_myViewPortY & (kChrH - 1)) + _myViewPortY);
-
- for (int j = 0; j != kViewPortCH+4; j++, i++) {
+
+ for (int j = 0; j != kViewPortCH + 4; j++, i++) {
_tIndex[i] = (j << 5) | 0x8000;
_tPriority[i] = _tPriority[i] - kChrH;
}
@@ -123,13 +123,13 @@ void ImmortalEngine::addSprite(uint16 vpX, uint16 vpY, SpriteName s, int img, ui
if (x >= (kResH + kMaxSpriteLeft)) {
x |= kMaskHigh; // Make it negative
}
-
+
_sprites[_numSprites]._X = (x << 1) + vpX;
-
+
if (y >= (kMaxSpriteAbove + kResV)) {
y |= kMaskHigh;
}
-
+
_sprites[_numSprites]._Y = (y << 1) + vpY;
if (p >= 0x80) {
@@ -137,7 +137,7 @@ void ImmortalEngine::addSprite(uint16 vpX, uint16 vpY, SpriteName s, int img, ui
}
_sprites[_numSprites]._priority = ((p + y) ^ 0xFFFF) + 1;
-
+
_sprites[_numSprites]._image = img;
_sprites[_numSprites]._dSprite = &_dataSprites[s];
_sprites[_numSprites]._on = 1;
@@ -153,7 +153,9 @@ void ImmortalEngine::addSprites() {
int tmpNum = _num2DrawItems;
for (int i = 0; i < kMaxSprites; i++) {
// If the sprite is active
- // This is commented out for testing until the issue with the function is resolved
+ /* TODO
+ * This is commented out for testing until the issue with the function is resolved
+ */
if (/*_sprites[i]._on*/0 == 1) {
// If sprite X is an odd number???
if ((_sprites[i]._X & 1) != 0) {
@@ -185,7 +187,7 @@ void ImmortalEngine::addSprites() {
int sx = ((_sprites[i]._X + tempImg->_deltaX) - tempD->_cenX) - _myViewPortX;
int sy = ((_sprites[i]._Y + tempImg->_deltaY) - tempD->_cenY) - _myViewPortY;
- if (sx >= 0 ) {
+ if (sx >= 0) {
if (sx >= kViewPortW) {
continue;
}
@@ -193,7 +195,7 @@ void ImmortalEngine::addSprites() {
continue;
}
- if (sy >= 0 ) {
+ if (sy >= 0) {
if (sy >= kViewPortH) {
continue;
}
@@ -226,10 +228,10 @@ void ImmortalEngine::sortDrawItems() {
// Assume that the list is sorted
bailout = true;
for (int i = 1; i < top; i++) {
- if (_tPriority[i] > _tPriority[i-1]) {
+ if (_tPriority[i] > _tPriority[i - 1]) {
uint16 tmp = _tPriority[i];
- _tPriority[i] = _tPriority[i-1];
- _tPriority[i-1] = tmp;
+ _tPriority[i] = _tPriority[i - 1];
+ _tPriority[i - 1] = tmp;
// List was not sorted yet, therefor we need to check it again
bailout = false;
@@ -264,9 +266,9 @@ void ImmortalEngine::drawBGRND() {
// Left Mask, draw upper right hand corner (UPHC) of floor
drawURHC(_myCNM[y2][x], pointX, pointY);
}
- pointX += kChrW; // This (and the H version) could be added to the for loop iterator arugment
+ pointX += kChrW; // This (and the H version) could be added to the for loop iterator arugment
}
- pointX -= (kChrW * (kViewPortCW + 1)); // They could have also just done pointX = _myUnivPointX
+ pointX -= (kChrW * (kViewPortCW + 1)); // They could have also just done pointX = _myUnivPointX
pointY += kChrH;
}
}
@@ -290,7 +292,7 @@ void ImmortalEngine::drawItems() {
uint16 rowY = 0;
do {
uint16 index = _tIndex[n];
- if (index >= 0x8000) { // If negative, it's a row to draw
+ if (index >= 0x8000) { // If negative, it's a row to draw
// rowY is (I think) the position of the start of the scroll window within the tile data
rowY = (index & 0x7FFF) + _myUnivPointY;
@@ -376,10 +378,10 @@ void ImmortalEngine::printByte(int b) {
void ImmortalEngine::printChr(char c) {
// This draws a character from the font sprite table, indexed as an ascii char, using superSprite
- c &= kMaskASCII; // Grab just the non-extended ascii part
+ c &= kMaskASCII; // Grab just the non-extended ascii part
if (c == ' ') {
- _penX += 8; // A space just moves the position on the screen to draw ahead by the size of a space
+ _penX += 8; // A space just moves the position on the screen to draw ahead by the size of a space
return;
}
@@ -394,7 +396,7 @@ void ImmortalEngine::printChr(char c) {
case 'M':
case 'W':
_penX += 8;
- // fall through
+ // fall through
default:
break;
}
@@ -408,13 +410,13 @@ void ImmortalEngine::printChr(char c) {
_penX -= 3;
break;
case 'j':
- // fall through
+ // fall through
case 't':
_penX -= 2;
break;
case 'l':
_penX -= 4;
- // fall through
+ // fall through
default:
break;
}
@@ -517,13 +519,13 @@ int ImmortalEngine::loadUniv(char mazeNum) {
// The view port of the level is longer than it is wide, so there are more columns than rows
// numCols = rectX / 64 (charW)
- _univ->_rectX = mazeUNV->readUint16LE() << 1;
+ _univ->_rectX = mazeUNV->readUint16LE() << 1;
_univ->_numCols = _univ->_rectX >> 6;
_univ->_num2Cols = _univ->_numCols << 1;
// univRectY is mazeUNV[22]
// numRows = rectY / 32 (charH)
- _univ->_rectY = mazeUNV->readUint16LE();
+ _univ->_rectY = mazeUNV->readUint16LE();
_univ->_numRows = _univ->_rectY >> 5;
_univ->_num2Rows = _univ->_numRows << 1;
@@ -557,7 +559,7 @@ int ImmortalEngine::loadUniv(char mazeNum) {
}
_dataBuffer->seek(0);
- _univ->_numChrs++; // Inc one more time being 0 counts
+ _univ->_numChrs++; // Inc one more time being 0 counts
_univ->_num2Chrs = _univ->_numChrs << 1;
//int lCNMCBM = mungeCBM(_univ->_num2Chrs);
@@ -591,37 +593,39 @@ void ImmortalEngine::loadSprites() {
*/
Common::String spriteNames[] = {"MORESPRITES.SPR", "NORLAC.SPR", "POWWOW.SPR", "TURRETS.SPR",
- "WORM.SPR", "IANSPRITES.SPR", "LAST.SPR", "DOORSPRITES.SPR",
- "GENSPRITES.SPR", "DRAGON.SPR", "MORDAMIR.SPR", "FLAMES.SPR",
- "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "WIZARDA.SPR",
- "WIZARDB.SPR", "ULINDOR.SPR", "SPIDER.SPR", "DRAG.SPR"};
+ "WORM.SPR", "IANSPRITES.SPR", "LAST.SPR", "DOORSPRITES.SPR",
+ "GENSPRITES.SPR", "DRAGON.SPR", "MORDAMIR.SPR", "FLAMES.SPR",
+ "ROPE.SPR", "RESCUE.SPR", "TROLL.SPR", "GOBLIN.SPR", "WIZARDA.SPR",
+ "WIZARDB.SPR", "ULINDOR.SPR", "SPIDER.SPR", "DRAG.SPR"
+ };
// Number of sprites in each file
int spriteNum[] = {10, 5, 7, 10, 4, 6, 3, 10, 5, 3, 2, 1, 3, 2, 9, 10, 8, 3, 9, 10, 9};
// Pairs of (x,y) for each sprite
// Should probably have made this a 2d array, oops
- uint16 centerXY[] = {16,56, 16,32, 27,39, 16,16, 32,16, 34,83, 28,37, 8,12, 8,19, 24,37,
- /* Norlac */ 46,18, 40,0, 8,13, 32,48, 32,40,
- /* Powwow */ 53,43, 28,37, 27,37, 26,30, 26,30, 26,29, 28,25,
- /* Turrets */ 34,42, 28,37, 24,32, 32,56, 26,56, 8,48, 8,32, 8,14, 8,24, 32,44,
- /* Worm */ 20,65, 25,46, 9,56, 20,53,
- /* Iansprites */ 24,50, 32,52, 32,53, 32,52, 40,16, 40,16,
- /* Last */ 32,56, 24,32, 24,36,
- /* Doorsprites */ 0,64, 4,49, 18,49, 18,56, 24,32, 24,16, 24,56, 24,32, 24,32, 36,32,
- /* Gensprites */ 16,44, 16,28, 32,24, 34,45, 20,28,
- /* Dragon */ 24,93, 32,48, 0,64,
- /* Mordamir */ 104,104, 30,30,
- /* Flames */ 64,0,
- /* Rope */ 0,80, 32,52, 32,40,
- /* Rescue */ 0,112, 0,112,
- /* Troll */ 28,38, 28,37, 28,37, 31,38, 28,37, 25,39, 28,37, 28,37, 28,37,
- /* Goblin */ 28,38, 30,38, 26,37, 30,38, 26,37, 26,37, 26,37, 26,37, 26,36, 44,32,
- /* Wizarda */ 28,37, 28,37, 28,37, 28,37, 28,37, 28,37, 28,37, 28,37,
- /* Wizardb */ 28,37, 28,37, 28,37,
- /* Ulindor */ 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42, 42,42,
- /* Spider */ 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44, 64,44,
- /* Drag */ 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36, 19,36};
+ uint16 centerXY[] = {16, 56, 16, 32, 27, 39, 16, 16, 32, 16, 34, 83, 28, 37, 8, 12, 8, 19, 24, 37,
+ /* Norlac */ 46, 18, 40, 0, 8, 13, 32, 48, 32, 40,
+ /* Powwow */ 53, 43, 28, 37, 27, 37, 26, 30, 26, 30, 26, 29, 28, 25,
+ /* Turrets */ 34, 42, 28, 37, 24, 32, 32, 56, 26, 56, 8, 48, 8, 32, 8, 14, 8, 24, 32, 44,
+ /* Worm */ 20, 65, 25, 46, 9, 56, 20, 53,
+ /* Iansprites */ 24, 50, 32, 52, 32, 53, 32, 52, 40, 16, 40, 16,
+ /* Last */ 32, 56, 24, 32, 24, 36,
+ /* Doorsprites */ 0, 64, 4, 49, 18, 49, 18, 56, 24, 32, 24, 16, 24, 56, 24, 32, 24, 32, 36, 32,
+ /* Gensprites */ 16, 44, 16, 28, 32, 24, 34, 45, 20, 28,
+ /* Dragon */ 24, 93, 32, 48, 0, 64,
+ /* Mordamir */ 104, 104, 30, 30,
+ /* Flames */ 64, 0,
+ /* Rope */ 0, 80, 32, 52, 32, 40,
+ /* Rescue */ 0, 112, 0, 112,
+ /* Troll */ 28, 38, 28, 37, 28, 37, 31, 38, 28, 37, 25, 39, 28, 37, 28, 37, 28, 37,
+ /* Goblin */ 28, 38, 30, 38, 26, 37, 30, 38, 26, 37, 26, 37, 26, 37, 26, 37, 26, 36, 44, 32,
+ /* Wizarda */ 28, 37, 28, 37, 28, 37, 28, 37, 28, 37, 28, 37, 28, 37, 28, 37,
+ /* Wizardb */ 28, 37, 28, 37, 28, 37,
+ /* Ulindor */ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ /* Spider */ 64, 44, 64, 44, 64, 44, 64, 44, 64, 44, 64, 44, 64, 44, 64, 44, 64, 44, 64, 44,
+ /* Drag */ 19, 36, 19, 36, 19, 36, 19, 36, 19, 36, 19, 36, 19, 36, 19, 36, 19, 36
+ };
// s = current sprite index, f = current file index, n = current number of sprites for this file
int s = 0;
@@ -632,7 +636,7 @@ void ImmortalEngine::loadSprites() {
for (int n = 0; n < (spriteNum[f] * 2); n += 2, s++) {
// For every data sprite in the file, make a datasprite and initialize it
DataSprite d;
- initDataSprite(file, &d, n/2, centerXY[s * 2], centerXY[(s * 2) + 1]);
+ initDataSprite(file, &d, n / 2, centerXY[s * 2], centerXY[(s * 2) + 1]);
_dataSprites[s] = d;
}
}
@@ -716,7 +720,7 @@ Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
* allowing us to directly compare it with 'CMP0'.
*/
char compSig[] = "CMP0";
- char sig[] = "0000";
+ char sig[] = "0000";
f.seek(8);
@@ -726,7 +730,7 @@ Common::SeekableReadStream *ImmortalEngine::loadIFF(Common::String fileName) {
if (strcmp(sig, compSig) == 0) {
debug("compressed");
-
+
/* The size of the compressed data is stored in the header, but doesn't
* account for the FORM part?? Also, **technically** this is a uint32LE,
* but the engine itself actually /doesn't/ use it like that. It only
@@ -775,7 +779,7 @@ void ImmortalEngine::loadPalette() {
// The palettes are stored at a particular location in the disk, this just grabs them
Common::File d;
d.open("IMMORTAL.dsk");
-
+
d.seek(kPaletteOffset);
d.read(_palDefault, 32);
d.read(_palWhite, 32);
@@ -797,8 +801,8 @@ void ImmortalEngine::setColors(uint16 pal[]) {
// Blue is the first nyble of the first byte, so it needs to move left by 4 bits (000B -> 00B0)
// We also need to repeat the bits so that the colour is the same proportion of 255 as it is of 15
_palRGB[(i * 3)] = ((pal[i] & kMaskRed) >> 4) | ((pal[i] & kMaskRed) >> 8);
- _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4);
- _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
+ _palRGB[(i * 3) + 1] = (pal[i] & kMaskGreen) | ((pal[i] & kMaskGreen) >> 4);
+ _palRGB[(i * 3) + 2] = (pal[i] & kMaskBlue) | ((pal[i] & kMaskBlue) << 4);
}
}
// Palette index to update first is 0, and there are 16 colours to update
@@ -847,9 +851,10 @@ void ImmortalEngine::fadePal(uint16 pal[], int count, uint16 target[]) {
* kept, this is a direct translation of the bit manipulation sequence.
*/
uint16 maskPal[16] = {0xFFFF, 0x0000, 0x0000, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
- 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
- 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0x0000,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
+ };
uint16 result;
uint16 temp;
@@ -930,7 +935,7 @@ void ImmortalEngine::useWhite() {
void ImmortalEngine::useNormal() {
setColors(_palDefault);
- _usingNormal = 1;
+ _usingNormal = 1;
}
void ImmortalEngine::useDim() {
@@ -1007,13 +1012,13 @@ void ImmortalEngine::fixPause() {
// This is a nasty bit of code isn't it? It's accurate to the source though :D
switch (_playing) {
case kSongText:
- // fall through
+ // fall through
case kSongMaze:
if (_themePaused) {
musicUnPause(_themeID);
break;
}
- // fall through
+ // fall through
default:
musicPause(_themeID);
break;
@@ -1026,7 +1031,7 @@ void ImmortalEngine::fixPause() {
musicUnPause(_combatID);
break;
}
- // fall through
+ // fall through
default:
musicPause(_combatID);
break;
@@ -1087,7 +1092,7 @@ void ImmortalEngine::center() {
// Reset the X position and move the Y position down by 16 pixels
void ImmortalEngine::carriageReturn() {
_penY += 16;
- _penX = kTextLeft;
+ _penX = kTextLeft;
}
} // namespace Immortal
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 5d297ba8e70..2010da59ca3 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -129,7 +129,7 @@ void ImmortalEngine::levelDrawAll() {
void ImmortalEngine::levelShowRoom(int r, int bX, int bY) {
_currentRoom = r;
- cycleFreeAll(); // This may not be needed, or it may need to be changed slightly
+ cycleFreeAll(); // This may not be needed, or it may need to be changed slightly
_rooms[_currentRoom]->flameSetRoom(_allFlames[r]);
//univSetRoom(r, bX, bY);
//fset, spark, bullet, and door get set to the current room
diff --git a/engines/immortal/logic.cpp b/engines/immortal/logic.cpp
index 21d8f0a2fb0..7cf5d084979 100644
--- a/engines/immortal/logic.cpp
+++ b/engines/immortal/logic.cpp
@@ -48,14 +48,14 @@ void ImmortalEngine::restartLogic() {
miscInit();
cycleFreeAll();
levelInit();
- //roomInit(); <-- will be run in constructor of room
- //monstInit(); <-- room.initMonsters() \
- //objectInit(); <-- room.initObjects()
- //doorInit(); <-- room.initDoors() |- probably all get run from room constructor
- //sparkInit(); <-- room.initSparks()
- //bulletInit(); <-- room.initProjectiles() /
- //objectInit(); <-- again? Odd...
- //genericSpriteInit(); <-- room.initGenSprites()
+ //roomInit(); <-- will be run in constructor of room
+ //monstInit(); <-- room.initMonsters() \
+ //objectInit(); <-- room.initObjects()
+ //doorInit(); <-- room.initDoors() |- probably all get run from room constructor
+ //sparkInit(); <-- room.initSparks()
+ //bulletInit(); <-- room.initProjectiles() /
+ //objectInit(); <-- again? Odd...
+ //genericSpriteInit(); <-- room.initGenSprites()
if (fromOldGame() == false) {
_level = 0;
@@ -65,12 +65,12 @@ void ImmortalEngine::restartLogic() {
_rooms[_currentRoom]->flameInit();
if (_level != 7) {
- _themePaused = true; // and #-1-2 = set both flags for themePaused
+ _themePaused = true; // and #-1-2 = set both flags for themePaused
}
}
void ImmortalEngine::logic() {
- trapKeys(); // First thing in any gameloop is to check if we should restart/toggle sound
+ trapKeys(); // First thing in any gameloop is to check if we should restart/toggle sound
_time += 1;
/* This is actually the main game state loop. I think the best way to translate it
@@ -98,7 +98,7 @@ void ImmortalEngine::logic() {
_themePaused = true;
_levelOver = false;
- if (_level == (_maxLevels-1)) {
+ if (_level == (_maxLevels - 1)) {
textPrint(kStrYouWin, 0);
} else {
@@ -111,7 +111,7 @@ void ImmortalEngine::logic() {
} else {
// Here's where the gameplay sequence actually happens!
- doSingleStep(); // Debug step function
+ doSingleStep(); // Debug step function
//monstRunAll();
//objectRunAll();
//doInfiniteHallways();
@@ -158,7 +158,7 @@ void ImmortalEngine::trapKeys() {
break;
case kActionSound:
toggleSound();
- // fall through
+ // fall through
default:
break;
}
@@ -175,10 +175,10 @@ int ImmortalEngine::keyOrButton() {
button = _pressedAction;
break;
case kActionFire:
- // fall through
+ // fall through
case kActionButton:
button = 13;
- // fall through
+ // fall through
default:
break;
}
@@ -293,13 +293,13 @@ bool ImmortalEngine::fromOldGame() {
_dontResetColors = 0;
if (_promoting == 1) {
_promoting = 0;
-
+
} else {
do {
if (!textPrint(kStrOldGame, 0)) {
// They choose not to load an old game
- return false;
+ return false;
}
} while (getCertificate() == true);
@@ -318,7 +318,7 @@ bool ImmortalEngine::fromOldGame() {
//uint8 hits = _certificate[kCertHits];
//uint8 quick = _certificate[kCertQuickness];
//uint8 gold = (_certificate[kCertGoldHi] << 4) | _certificate[kCertGoldLo];
- // monstMakePlayer(hits, quick, gold); <- will become room.makePlayer();
+ // monstMakePlayer(hits, quick, gold); <- will become room.makePlayer();
// Create the inventory
// room.makeObject(3, kObjIsRunning, 0, goldType);
@@ -326,7 +326,7 @@ bool ImmortalEngine::fromOldGame() {
// Hi bits of inventory
int certInv = _certificate[kCertInvHi];
- if ((certInv & 1) != 0 ) {
+ if ((certInv & 1) != 0) {
if (_level < 2) {
//room.makeObject(3, 0, 0, waterType);
}
@@ -386,7 +386,7 @@ bool ImmortalEngine::fromOldGame() {
if ((certInv & 4) != 0) {
//room.makeObject(3, 0, kSporesFrame, antiType);
}
- // fall through
+ // fall through
default:
break;
}
@@ -446,17 +446,17 @@ void ImmortalEngine::makeCertificate() {
if (true/*room.monster[kPlayerID].hasObject(wowCharmType)*/) {
_certificate[kCertInvLo] |= 4;
}
- // fall through
+ // fall through
case 3:
if (true/*room.monster[kPlayerID].hasObject(faceRingType)*/) {
_certificate[kCertInvLo] |= 1;
}
- // fall through
+ // fall through
case 4:
if (true/*room.monster[kPlayerID].hasObject(coffeeType)*/) {
_certificate[kCertInvLo] |= 2;
}
- // fall through
+ // fall through
case 7:
if (true/*room.monster[kPlayerID].hasObject(bronzeType)*/) {
_certificate[kCertInvLo] |= 1;
@@ -469,7 +469,7 @@ void ImmortalEngine::makeCertificate() {
if (true/*room.monster[kPlayerID].hasObject(antiType)*/) {
_certificate[kCertInvLo] |= 4;
}
- // fall through
+ // fall through
default:
_lastCertLen = 13;
uint8 checksum[4];
@@ -515,11 +515,11 @@ bool ImmortalEngine::getCertificate() {
} else if (k == 0x7f) {
// The input was a backspace
if (certLen != 0) {
- certLen--; // Length is one smaller now
- backspace(); // move the drawing position back and reprint the '-' char
+ certLen--; // Length is one smaller now
+ backspace(); // move the drawing position back and reprint the '-' char
backspace();
printChr('-');
- }
+ }
} else {
// The input was a key
@@ -588,7 +588,7 @@ void ImmortalEngine::printCertificate() {
* but grabbing it from a table is faster and doesn't
* use a lot of space (especially if it's used anywhere else)
*/
- char toHex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ char toHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
textBeginning(kStrCert, 0);
for (int i = 0; i < _lastCertLen; i++) {
diff --git a/engines/immortal/metaengine.cpp b/engines/immortal/metaengine.cpp
index 95fcb9ece99..70d628b02b9 100644
--- a/engines/immortal/metaengine.cpp
+++ b/engines/immortal/metaengine.cpp
@@ -34,12 +34,12 @@ Common::Error ImmortalMetaEngine::createInstance(OSystem *syst, Engine **engine,
bool ImmortalMetaEngine::hasFeature(MetaEngineFeature f) const {
return (f == kSavesUseExtendedFormat) ||
- (f == kSimpleSavesNames) ||
- (f == kSupportsListSaves) ||
- (f == kSupportsDeleteSave) ||
- (f == kSavesSupportMetaInfo) ||
- (f == kSavesSupportThumbnail) ||
- (f == kSupportsLoadingDuringStartup);
+ (f == kSimpleSavesNames) ||
+ (f == kSupportsListSaves) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSupportsLoadingDuringStartup);
}
#if PLUGIN_ENABLED_DYNAMIC(IMMORTAL)
diff --git a/engines/immortal/misc.cpp b/engines/immortal/misc.cpp
index 938488fb84f..12465ac9ff8 100644
--- a/engines/immortal/misc.cpp
+++ b/engines/immortal/misc.cpp
@@ -23,7 +23,7 @@
namespace Immortal {
-/*
+/*
*
* ----- -----
* ----- Main Functions -----
@@ -39,7 +39,7 @@ void ImmortalEngine::miscInit() {
void ImmortalEngine::setRandomSeed() {}
void ImmortalEngine::getRandom() {}
-/*
+/*
*
* ----- -----
* ----- Text Printing -----
@@ -170,7 +170,7 @@ bool ImmortalEngine::textSub(Str s, FadeType f, int n) {
index = -1;
}
textDoSpace(text, index);
-
+
} else {
printChr(chr);
// We need this to show up now, not when the frame ends, so we have to update the screen here
@@ -179,16 +179,16 @@ bool ImmortalEngine::textSub(Str s, FadeType f, int n) {
myDelay(5);
switch (chr) {
case '?':
- // fall through
+ // fall through
case ':':
myDelay(13);
- // fall through
+ // fall through
case '.':
myDelay(13);
- // fall through
+ // fall through
case ',':
myDelay(13);
- // fall through
+ // fall through
default:
break;
}
@@ -202,7 +202,7 @@ bool ImmortalEngine::textSub(Str s, FadeType f, int n) {
}
index++;
}
-
+
chr = text[index];
if (f != kTextFadeIn) {
@@ -277,16 +277,16 @@ void ImmortalEngine::textDoSpace(Common::String s, int index) {
index++;
switch (s[index]) {
case '=':
- // fall through
+ // fall through
case '@':
- // fall through
+ // fall through
case '%':
- // fall through
+ // fall through
case '[':
- // fall through
+ // fall through
case ' ':
foundEnd = true;
- // fall through
+ // fall through
default:
break;
}
@@ -294,7 +294,7 @@ void ImmortalEngine::textDoSpace(Common::String s, int index) {
if (((index - start) + _collumn) >= kMaxCollumns) {
if (_row < kMaxRows) {
textCR();
-
+
} else {
textAutoPageBreak();
}
@@ -316,7 +316,7 @@ bool ImmortalEngine::yesNo() {
if (tyes[_heldDirection] == 0) {
noOn();
_lastYes = 0;
-
+
} else {
yesOn();
_lastYes = 1;
@@ -333,7 +333,7 @@ bool ImmortalEngine::yesNo() {
standardBeep();
if (_lastYes == 0) {
noOn();
-
+
} else {
yesOn();
}
@@ -397,9 +397,9 @@ void ImmortalEngine::myDelay(int j) {
break;
case 0:
Utilities::delay(1);
- // fall through
+ // fall through
case 2:
- // fall through
+ // fall through
default:
break;
}
@@ -408,7 +408,7 @@ void ImmortalEngine::myDelay(int j) {
} while (j != 0);
}
-/*
+/*
*
* ----- -----
* ----- Input Related -----
@@ -419,11 +419,11 @@ void ImmortalEngine::myDelay(int j) {
bool ImmortalEngine::buttonPressed() {
// Returns false if the button was pressed, but not held or up
getInput();
-
+
if (_heldAction == kActionButton) {
// Zero just the button0held bit
_myButton &= (0xFF - kButton0Held);
-
+
} else if ((_myButton & kButton0Held) == 0) {
_myButton |= kButton0Held;
return false;
@@ -435,10 +435,10 @@ bool ImmortalEngine::buttonPressed() {
bool ImmortalEngine::firePressed() {
// Returns false if the button was pressed, but not held or up
getInput();
-
+
if (_heldAction == kActionFire) {
_myButton &= (0xFF - kButton1Held);
-
+
} else if ((_myButton & kButton1Held) == 0) {
_myButton |= kButton1Held;
return false;
@@ -448,7 +448,7 @@ bool ImmortalEngine::firePressed() {
}
-/*
+/*
*
* ----- -----
* ----- Screen Related -----
@@ -459,9 +459,9 @@ bool ImmortalEngine::firePressed() {
/*
*
- * ----- -----
+ * ----- -----
* ----- Sound Related -----
- * ----- -----
+ * ----- -----
*
*/
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 5811b589e4d..df968d68c4e 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -90,10 +90,10 @@ struct Monster {
};
struct Flame {
-FPattern _p = kFlameOff;
+ FPattern _p = kFlameOff;
uint8 _x = 0;
uint8 _y = 0;
- int _c = 0;
+ int _c = 0;
};
struct Chest {
@@ -126,12 +126,12 @@ public:
RoomFlag _flags;
- uint8 _xPos = 0;
- uint8 _yPos = 0;
+ uint8 _xPos = 0;
+ uint8 _yPos = 0;
uint8 _holeRoom = 0;
uint8 _holeCellX = 0;
uint8 _holeCellY = 0;
- uint8 _candleTmp = 0; // Special case for candle in maze 0
+ uint8 _candleTmp = 0; // Special case for candle in maze 0
uint8 _numFlames = 0;
uint8 _numInRoom = 0;
@@ -150,7 +150,7 @@ public:
//void init();
//void inRoomNew();
- //void getTilePair(uint8 x, uint8 y); // Modifies a struct of the tile number, aboveTile number, and the cell coordinates of the tile
+ //void getTilePair(uint8 x, uint8 y); // Modifies a struct of the tile number, aboveTile number, and the cell coordinates of the tile
void setHole();
void drawContents(uint16 vX, uint16 vY);
@@ -172,14 +172,14 @@ public:
*/
// Init
- int cycleNew(CycID id); // Adds a cycle to the current list
+ int cycleNew(CycID id); // Adds a cycle to the current list
void cycleFree(int c);
// Getters
-DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + getNum
- int cycleGetIndex(int c);
- int cycleGetFrame(int c);
- int cycleGetNumFrames(int c);
+ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + getNum
+ int cycleGetIndex(int c);
+ int cycleGetFrame(int c);
+ int cycleGetNumFrames(int c);
// Setters
void cycleSetIndex(int c, int f);
@@ -205,7 +205,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
void lightTorch(uint8 x, uint8 y);
void flameFreeAll();
void flameSetRoom(Common::Array<SFlame>);
- int flameGetCyc(Flame *f, int first);
+ int flameGetCyc(Flame *f, int first);
/*
* [bullet.cpp] Functions from Bullet.GS
@@ -222,7 +222,7 @@ DataSprite *cycleGetDataSprite(int c); // This takes the place of getFile + ge
* [Univ.cpp] Functions from Univ.GS
*/
- void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s, int img, uint16 p);
+ void univAddSprite(uint16 vX, uint16 vY, uint16 x, uint16 y, SpriteName s, int img, uint16 p);
};
} // namespace immortal
diff --git a/engines/immortal/sprite_list.h b/engines/immortal/sprite_list.h
index 3ef3e1307e1..e301a5089ac 100644
--- a/engines/immortal/sprite_list.h
+++ b/engines/immortal/sprite_list.h
@@ -38,17 +38,17 @@ struct Image {
};
struct DataSprite {
- uint16 _cenX; // These are the base center positions
+ uint16 _cenX; // These are the base center positions
uint16 _cenY;
uint16 _numImages;
Common::Array<Image> _images;
};
struct Sprite {
- int _image; // Index of _dSprite._images[]
+ int _image; // Index of _dSprite._images[]
uint16 _X;
uint16 _Y;
- uint16 _on; // 1 = active
+ uint16 _on; // 1 = active
uint16 _priority;
DataSprite *_dSprite;
};
diff --git a/engines/immortal/sprites.cpp b/engines/immortal/sprites.cpp
index 170c8131b1d..d997fdf1db2 100644
--- a/engines/immortal/sprites.cpp
+++ b/engines/immortal/sprites.cpp
@@ -53,7 +53,7 @@
namespace Immortal {
-/*
+/*
*
* ----- -----
* ----- Main Functions -----
@@ -145,10 +145,10 @@ bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skip
} else if ((pointY + height) < superTop) {
return true;
- /* The actual clipping is pretty simple:
- * Lower height = stop drawing the sprite early. Higher SkipY = start drawing the sprite late
- * So we just determine the delta for each based on superTop and superBottom
- */
+ /* The actual clipping is pretty simple:
+ * Lower height = stop drawing the sprite early. Higher SkipY = start drawing the sprite late
+ * So we just determine the delta for each based on superTop and superBottom
+ */
} else {
// Starting with checking if any of the sprite is under the bottom of the screen
@@ -203,7 +203,7 @@ void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skip
// as that is the transparent colour
pixel1 = (img._bitmap[y][x] & kMask8High) >> 4;
pixel2 = (img._bitmap[y][x] & kMask8Low);
-
+
if (pixel1 != 0) {
_screenBuff[pointIndex] = pixel1;
}
@@ -229,7 +229,7 @@ void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 poin
uint16 height = dSprite->_images[img]._rectH;
uint16 skipY = 0;
- uint16 pointIndex = 0; // This is 'screen' and 'screen + 2' in the source
+ uint16 pointIndex = 0; // This is 'screen' and 'screen + 2' in the source
pointX -= cenX;
pointY -= cenY;
@@ -237,7 +237,7 @@ void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 poin
// Normally I would just make the return from clip be reversed, but the idea is that the return would be 'offscreen == true'
if (!(clipSprite(height, pointIndex, skipY, dSprite, pointX, pointY, img, bmw, superTop, superBottom))) {
-
+
// Alignment was a factor in the assembly because it was essentially 2 pixels per byte. However ScummVM is 1 pixel per byte
spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
}
diff --git a/engines/immortal/story.cpp b/engines/immortal/story.cpp
index 0d598f82b24..d86c6d345f5 100644
--- a/engines/immortal/story.cpp
+++ b/engines/immortal/story.cpp
@@ -32,16 +32,16 @@
* objects, and everything in the rooms.
*/
-/* These are the UNIVAT for each Story entry
- UNIVAT 1024,480, 1152, 464, \-1, -1, zip,level1Ladders, rooma, 704/64, 544/32\
- UNIVAT 304, 448, 472+32, 500+16, \-1, -1, zip,level12Ladders, -1, 0, 0\
- UNIVAT 600, 450, 560, 598, \-1, r2.b+(16*r2.a), zip,level3Ladders, r2.b, 640/64, 576/32\
- UNIVAT 120, 540, 188, 584, \-1, -1, zip,level4Ladders, -1, 0, 0\
- UNIVAT 64, 128, 128, 128+32, \-1, -1, zip,level5Ladders, -1, 1088/64, 928/32\
- UNIVAT 768, 224, 896, 288-16, \-1, -1, zip,level5Ladders, -1, 1088/64, 928/32\
- UNIVAT 896, 672+64, 960, 832-16, \-1, -1, zip,level6Ladders, -1, 0, 0\
- UNIVAT 688, 800, 912-64, 888-32, \-1, -1, zip,level7Ladders, -1, 1088/64, 928/32\
- UNIVAT 64, 704, 64+96, 704+64, \-1, -1, zip,level8Ladders, -1, 0, 0\
+/* These are the UNIVAT for each Story entry
+ UNIVAT 1024,480, 1152, 464, \-1, -1, zip,level1Ladders, rooma, 704/64, 544/32\
+ UNIVAT 304, 448, 472+32, 500+16, \-1, -1, zip,level12Ladders, -1, 0, 0\
+ UNIVAT 600, 450, 560, 598, \-1, r2.b+(16*r2.a), zip,level3Ladders, r2.b, 640/64, 576/32\
+ UNIVAT 120, 540, 188, 584, \-1, -1, zip,level4Ladders, -1, 0, 0\
+ UNIVAT 64, 128, 128, 128+32, \-1, -1, zip,level5Ladders, -1, 1088/64, 928/32\
+ UNIVAT 768, 224, 896, 288-16, \-1, -1, zip,level5Ladders, -1, 1088/64, 928/32\
+ UNIVAT 896, 672+64, 960, 832-16, \-1, -1, zip,level6Ladders, -1, 0, 0\
+ UNIVAT 688, 800, 912-64, 888-32, \-1, -1, zip,level7Ladders, -1, 1088/64, 928/32\
+ UNIVAT 64, 704, 64+96, 704+64, \-1, -1, zip,level8Ladders, -1, 0, 0\
*/
#include "immortal/immortal.h"
@@ -50,155 +50,155 @@ namespace Immortal {
void ImmortalEngine::initStoryStatic() {
Common::Array<Common::String> s{"#" + Common::String(kSwordBigFrame) + "sword@",
- "You find an Elven sword of&agility. Take it?@",
- "Search the bones?%",
- "}The sword permanently endows you with Elven agility and quickness in combat.@",
- "}You notice something that looks wet and green under the pile. Search further?%",
- "#" + Common::String(kBagBigFrame) + " dust@",
- "}You find a bag containing Dust of Complaisance.&@",
- "}Drop the bait on the ground here?%",
- "}To use this dust, you throw it in the air. Do that here?%",
- "_}Don+t bother me, I+m cutting a gem. Yes, you need it. No, you can+t have it. I wouldn+t give it to anyone, least of all you. Go away. ]]]]=",
- "_}Let me help you. Please take this gem. No, really, I insist. Take it and go with my blessings. Good luck. ]]]]=",
- "#" + Common::String(kCarpetBigFrame) + "carpet@",
- "#" + Common::String(kBombBigFrame) + " bomb@",
- "A gas bomb that goblins&use to paralyze trolls.&@",
- "Take it?<>@",
- "%",
- " other@",
- "#" + Common::String(kKeyBigFrame) + " key@",
- "#" + Common::String(kKeyBigFrame) + " key@",
- "A key to a chest.&@",
- "The chest is open. Examine&contents?%",
- "Put it on?%",
- "Drop it?%",
- "It+s unlocked. Open it?%",
- "It+s locked but you have&the key. Open it?%",
- "It+s locked and you don+t&have the key.@",
- "The lock, triggered by a&complicated set of latches,&is unfamiliar to you.@",
- "#" + Common::String(kGoldBigFrame) + "$0 gold@",
- "You find $0 gold pieces.&&^#" + Common::String(kPileFrame) + "@",
- "@",
- "You can+t plant them on&stone tiles.@",
- "It+s locked but you are&able to unlock it with&the key.@",
- "_}The king is not dead, but the poison is taking effect. When he sees you, he attempts to speak:[(Give me water... the fountain... I give you... information... peace...+[Give him water?%",
- "_}You dont have any water to give him. He mumbles something. Then silence... You find a key on his body.]]]]=",
- "_}He mumbles something. Then silence... You find a key on his body.]]]]=",
- "_}I+ll tell you how to... next level... past slime... three jewels... slime... rock becomes... floor... right, left, center of the... [Then silence. His hand opens, releasing a key.]]]]=",
- "You find a door key.&@",
- "You find a note.&@",
- "#" + Common::String(kNoteBigFrame) + "note@",
- "He+s dead.&Look for possessions?%",
- "You don+t have it. Check&your inventory.@",
- "Game Over&&Play again?@",
- "Congratulations!&&Play again?@",
- "You find a bag of bait.&@",
- "#" + Common::String(kBagBigFrame) + " bait@",
- "You find a stone. @",
- "#" + Common::String(kStoneBigFrame) + " stone@",
- "You find a red gem.&@",
- "#" + Common::String(kGemBigFrame) + " gem@",
- "You find a scroll with&fireball spells.&@",
- "#" + Common::String(kScrollBigFrame) + "$ shots@",
- "You find a map warning&you about pit traps.&@",
- "#" + Common::String(kMapBigFrame) + " map@",
- "#" + Common::String(kVaseBigFrame) + " oil@",
- "You apply the oil but notice&as you walk that the leather&is drying out quickly.@",
- "}You discover a scroll with a charm spell to use on will o+ the wisps.&@",
- "#" + Common::String(kScrollBigFrame) + " charm@",
- "}This charms the will o+ the wisps to follow you. Read the spell again to turn them against your enemies.@",
- "}It looks like water. Drink it?%",
- "Drink it?%",
- "}It works! You are much stronger.]]]=",
- "}It looks like it has green stuff inside. Open it?%",
- "Now this will take&effect when you press the&fire button.@",
- "You find a potion,&Magic Muscle.&@",
- "#" + Common::String(kVaseBigFrame) + " potion@",
- "You find a bottle.&@",
- "#" + Common::String(kVaseBigFrame) + " bottle@",
- "#" + Common::String(kRingBigFrame) + "Protean@",
- "You find a Protean Ring.&@",
- "You find a troll ritual knife,&used to declare a fight to&the death. @",
- "#" + Common::String(kKnifeBigFrame) + " knife@",
- "_}It is a fine woman+s garment. Folded inside is a ring with the words,[`To Ana, so harm will never find you. -Your loving father, Dunric.+&@",
- "You find a small, well&crafted ring. @",
- "#" + Common::String(kRingBigFrame) + " gift@",
- "#" + Common::String(kRingBigFrame) + " Ana+s@",
- "_}She is hurt and upset when she finds you don+t have her ring or won+t give it to her. She scurries back into the hole. The hole is too small for you to follow.&@",
- "_}`Sir, can you help me,+ the girl pleads. `I was kidnapped and dragged down here. All the man would say is `Mordamir+s orders.+[I ~" + Common::String(kStrGive2),
- "escaped using a ring my father gave me, but now I+ve lost it. Did you find it?+%",
- "_}We have met before, old man. Do you remember? Because you helped me, you may pass. But I warn you, we are at war with the trolls.[Over this ladder, across the spikes, is troll territory. Very dangerous.@",
- "_}You are an impostor!]]]]=",
- "_}Old man, do you remember me? I am king of the goblins. You didn+t give me the water. You left me to die after you took the key from me. Now you will pay.]]]]=",
- "_}You quickly fall into a deep, healing sleep...[Vivid images of a beautiful enchanted city pass by. All the city people are young and glowing. Fountains fill the city, and the splash and ~" + Common::String(kStrDream1P2),
- "sparkle of water is everywhere...[Suddenly the images go black. A face appears... Mordamir!]][ ~" + Common::String(kStrDream1P3),
- "He is different from how you remember him. His gentle features are now withered. His kind eyes, now cold and sunken, seem to look through you with a dark, penetrating stare. You wake rejuvenated, but disturbed.]]]]]=",
- "_}Here, take this ring in return. [I don+t know if it will help, but I heard the unpleasant little dwarf say, (Clockwise, three rings around the triangle.+[Could that be a clue to his exit puzzle? I must go. Goodbye.]]]]=",
- "#" + Common::String(kSackBigFrame) + " spores@",
- "You find a sack of bad&smelling spores.&@",
- "Please insert play disk.@",
- "New game?%",
- "Enter certificate:&-=",
- "Invalid certificate.@",
- "End of level!&Here is your certificate:&&=",
- "&@",
- "\\ Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]\\]=",
- " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
- "_}Greetings, friend! Come, I+ve got something you need. These parts are plagued with slime.[You can+t venture safely without my slime oil for boots, a bargain at only 80 gold pieces.%",
- "_}All right, 60 gold pieces for my oil. Rub it on your boots and slime won+t touch you. 60, friend.%",
- "This room doesn+t resemble&any part of the map.@",
- "This room resembles part&of the map.@"};
+ "You find an Elven sword of&agility. Take it?@",
+ "Search the bones?%",
+ "}The sword permanently endows you with Elven agility and quickness in combat.@",
+ "}You notice something that looks wet and green under the pile. Search further?%",
+ "#" + Common::String(kBagBigFrame) + " dust@",
+ "}You find a bag containing Dust of Complaisance.&@",
+ "}Drop the bait on the ground here?%",
+ "}To use this dust, you throw it in the air. Do that here?%",
+ "_}Don+t bother me, I+m cutting a gem. Yes, you need it. No, you can+t have it. I wouldn+t give it to anyone, least of all you. Go away. ]]]]=",
+ "_}Let me help you. Please take this gem. No, really, I insist. Take it and go with my blessings. Good luck. ]]]]=",
+ "#" + Common::String(kCarpetBigFrame) + "carpet@",
+ "#" + Common::String(kBombBigFrame) + " bomb@",
+ "A gas bomb that goblins&use to paralyze trolls.&@",
+ "Take it?<>@",
+ "%",
+ " other@",
+ "#" + Common::String(kKeyBigFrame) + " key@",
+ "#" + Common::String(kKeyBigFrame) + " key@",
+ "A key to a chest.&@",
+ "The chest is open. Examine&contents?%",
+ "Put it on?%",
+ "Drop it?%",
+ "It+s unlocked. Open it?%",
+ "It+s locked but you have&the key. Open it?%",
+ "It+s locked and you don+t&have the key.@",
+ "The lock, triggered by a&complicated set of latches,&is unfamiliar to you.@",
+ "#" + Common::String(kGoldBigFrame) + "$0 gold@",
+ "You find $0 gold pieces.&&^#" + Common::String(kPileFrame) + "@",
+ "@",
+ "You can+t plant them on&stone tiles.@",
+ "It+s locked but you are&able to unlock it with&the key.@",
+ "_}The king is not dead, but the poison is taking effect. When he sees you, he attempts to speak:[(Give me water... the fountain... I give you... information... peace...+[Give him water?%",
+ "_}You dont have any water to give him. He mumbles something. Then silence... You find a key on his body.]]]]=",
+ "_}He mumbles something. Then silence... You find a key on his body.]]]]=",
+ "_}I+ll tell you how to... next level... past slime... three jewels... slime... rock becomes... floor... right, left, center of the... [Then silence. His hand opens, releasing a key.]]]]=",
+ "You find a door key.&@",
+ "You find a note.&@",
+ "#" + Common::String(kNoteBigFrame) + "note@",
+ "He+s dead.&Look for possessions?%",
+ "You don+t have it. Check&your inventory.@",
+ "Game Over&&Play again?@",
+ "Congratulations!&&Play again?@",
+ "You find a bag of bait.&@",
+ "#" + Common::String(kBagBigFrame) + " bait@",
+ "You find a stone. @",
+ "#" + Common::String(kStoneBigFrame) + " stone@",
+ "You find a red gem.&@",
+ "#" + Common::String(kGemBigFrame) + " gem@",
+ "You find a scroll with&fireball spells.&@",
+ "#" + Common::String(kScrollBigFrame) + "$ shots@",
+ "You find a map warning&you about pit traps.&@",
+ "#" + Common::String(kMapBigFrame) + " map@",
+ "#" + Common::String(kVaseBigFrame) + " oil@",
+ "You apply the oil but notice&as you walk that the leather&is drying out quickly.@",
+ "}You discover a scroll with a charm spell to use on will o+ the wisps.&@",
+ "#" + Common::String(kScrollBigFrame) + " charm@",
+ "}This charms the will o+ the wisps to follow you. Read the spell again to turn them against your enemies.@",
+ "}It looks like water. Drink it?%",
+ "Drink it?%",
+ "}It works! You are much stronger.]]]=",
+ "}It looks like it has green stuff inside. Open it?%",
+ "Now this will take&effect when you press the&fire button.@",
+ "You find a potion,&Magic Muscle.&@",
+ "#" + Common::String(kVaseBigFrame) + " potion@",
+ "You find a bottle.&@",
+ "#" + Common::String(kVaseBigFrame) + " bottle@",
+ "#" + Common::String(kRingBigFrame) + "Protean@",
+ "You find a Protean Ring.&@",
+ "You find a troll ritual knife,&used to declare a fight to&the death. @",
+ "#" + Common::String(kKnifeBigFrame) + " knife@",
+ "_}It is a fine woman+s garment. Folded inside is a ring with the words,[`To Ana, so harm will never find you. -Your loving father, Dunric.+&@",
+ "You find a small, well&crafted ring. @",
+ "#" + Common::String(kRingBigFrame) + " gift@",
+ "#" + Common::String(kRingBigFrame) + " Ana+s@",
+ "_}She is hurt and upset when she finds you don+t have her ring or won+t give it to her. She scurries back into the hole. The hole is too small for you to follow.&@",
+ "_}`Sir, can you help me,+ the girl pleads. `I was kidnapped and dragged down here. All the man would say is `Mordamir+s orders.+[I ~" + Common::String(kStrGive2),
+ "escaped using a ring my father gave me, but now I+ve lost it. Did you find it?+%",
+ "_}We have met before, old man. Do you remember? Because you helped me, you may pass. But I warn you, we are at war with the trolls.[Over this ladder, across the spikes, is troll territory. Very dangerous.@",
+ "_}You are an impostor!]]]]=",
+ "_}Old man, do you remember me? I am king of the goblins. You didn+t give me the water. You left me to die after you took the key from me. Now you will pay.]]]]=",
+ "_}You quickly fall into a deep, healing sleep...[Vivid images of a beautiful enchanted city pass by. All the city people are young and glowing. Fountains fill the city, and the splash and ~" + Common::String(kStrDream1P2),
+ "sparkle of water is everywhere...[Suddenly the images go black. A face appears... Mordamir!]][ ~" + Common::String(kStrDream1P3),
+ "He is different from how you remember him. His gentle features are now withered. His kind eyes, now cold and sunken, seem to look through you with a dark, penetrating stare. You wake rejuvenated, but disturbed.]]]]]=",
+ "_}Here, take this ring in return. [I don+t know if it will help, but I heard the unpleasant little dwarf say, (Clockwise, three rings around the triangle.+[Could that be a clue to his exit puzzle? I must go. Goodbye.]]]]=",
+ "#" + Common::String(kSackBigFrame) + " spores@",
+ "You find a sack of bad&smelling spores.&@",
+ "Please insert play disk.@",
+ "New game?%",
+ "Enter certificate:&-=",
+ "Invalid certificate.@",
+ "End of level!&Here is your certificate:&&=",
+ "&@",
+ "\\ Electronic Arts presents&& The Immortal&&&& 1990 Will Harvey|]]]]]]]]\\]=",
+ " written by&& Will Harvey& Ian Gooding& Michael Marcantel& Brett G. Durrett& Douglas Fulton|]]]]]]]/=",
+ "_}Greetings, friend! Come, I+ve got something you need. These parts are plagued with slime.[You can+t venture safely without my slime oil for boots, a bargain at only 80 gold pieces.%",
+ "_}All right, 60 gold pieces for my oil. Rub it on your boots and slime won+t touch you. 60, friend.%",
+ "This room doesn+t resemble&any part of the map.@",
+ "This room resembles part&of the map.@"};
_strPtrs = s;
- Common::Array<int> cyc0{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
- Common::Array<int> cyc1{15,16,17,18,19,20,21,22,-1};
- Common::Array<int> cyc2{0,1,2,-1};
- Common::Array<int> cyc3{3,4,5,-1};
- Common::Array<int> cyc4{6,7,8,9,10,-1};
- Common::Array<int> cyc5{11,12,13,14,15,-1};
- Common::Array<int> cyc6{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,-1};
- Common::Array<int> cyc7{0,1,2,3,4,-1};
- Common::Array<int> cyc8{5,1 + 5,2 + 5,3 + 5,4 + 5,-1};
- Common::Array<int> cyc9{10,1 + 10,2 + 10,3 + 10,4 + 10,-1};
- Common::Array<int> cyc10{15,1 + 15,2 + 15,3 + 15,4 + 15,-1};
- Common::Array<int> cyc11{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
- Common::Array<int> cyc12{0,1,2,3,4,5,6,7,8,9,-1};
- Common::Array<int> cyc13{0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, -1};
- Common::Array<int> cyc14{31,32,33,32, 34,35,36,35, 37,38,39,38, 40,41,42,41, 43,44,45,44, 46,47,48,47, 49,50,51,50, 52,53,54,53, -1};
+ Common::Array<int> cyc0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1};
+ Common::Array<int> cyc1{15, 16, 17, 18, 19, 20, 21, 22, -1};
+ Common::Array<int> cyc2{0, 1, 2, -1};
+ Common::Array<int> cyc3{3, 4, 5, -1};
+ Common::Array<int> cyc4{6, 7, 8, 9, 10, -1};
+ Common::Array<int> cyc5{11, 12, 13, 14, 15, -1};
+ Common::Array<int> cyc6{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1};
+ Common::Array<int> cyc7{0, 1, 2, 3, 4, -1};
+ Common::Array<int> cyc8{5, 1 + 5, 2 + 5, 3 + 5, 4 + 5, -1};
+ Common::Array<int> cyc9{10, 1 + 10, 2 + 10, 3 + 10, 4 + 10, -1};
+ Common::Array<int> cyc10{15, 1 + 15, 2 + 15, 3 + 15, 4 + 15, -1};
+ Common::Array<int> cyc11{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, -1};
+ Common::Array<int> cyc12{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1};
+ Common::Array<int> cyc13{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, -1};
+ Common::Array<int> cyc14{31, 32, 33, 32, 34, 35, 36, 35, 37, 38, 39, 38, 40, 41, 42, 41, 43, 44, 45, 44, 46, 47, 48, 47, 49, 50, 51, 50, 52, 53, 54, 53, -1};
Common::Array<int> cyc15{55, -1};
- Common::Array<int> cyc16{63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66, 63,64,65,66,-1};
- Common::Array<int> cyc17{0,1,0,-1};
- Common::Array<int> cyc18{0,1,2,4,5,6,7,8,9,10,11,12,2,1,-1};
- Common::Array<int> cyc19{0,0,1,2,13,14,15,16,4,2,3,-1};
- Common::Array<int> cyc20{0,1,2,3,20,21,22,23,24,25,26,27,5,4,3,-1};
- Common::Array<int> cyc21{0,1,2,3,-1};
- Common::Array<int> cyc22{0,17,18,19,3,-1};
- Common::Array<int> cyc23{0,1,-1};
- Common::Array<int> cyc24{28,28,28,28,-1};
- Common::Array<int> cyc25{15,16,15,16,15,1 + 15,1 + 15,-1};
- Common::Array<int> cyc26{10 + 15,11+ 15,12 + 15,13 + 15,14 + 15,15 + 15,16 + 15,-1};
- Common::Array<int> cyc27{2 + 15,3 + 15,4 + 15,5 + 15,-1};
- Common::Array<int> cyc28{6 + 15,7 + 15,8 + 15,9 + 15,-1};
- Common::Array<int> cyc29{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
- Common::Array<int> cyc30{0,1,2,3,3,3,3,4,5,6,-1};
- Common::Array<int> cyc31{0,1,2,3,4,5,6,7,8,-1};
+ Common::Array<int> cyc16{63, 64, 65, 66, 63, 64, 65, 66, 63, 64, 65, 66, 63, 64, 65, 66, 63, 64, 65, 66, 63, 64, 65, 66, 63, 64, 65, 66, 63, 64, 65, 66, -1};
+ Common::Array<int> cyc17{0, 1, 0, -1};
+ Common::Array<int> cyc18{0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 2, 1, -1};
+ Common::Array<int> cyc19{0, 0, 1, 2, 13, 14, 15, 16, 4, 2, 3, -1};
+ Common::Array<int> cyc20{0, 1, 2, 3, 20, 21, 22, 23, 24, 25, 26, 27, 5, 4, 3, -1};
+ Common::Array<int> cyc21{0, 1, 2, 3, -1};
+ Common::Array<int> cyc22{0, 17, 18, 19, 3, -1};
+ Common::Array<int> cyc23{0, 1, -1};
+ Common::Array<int> cyc24{28, 28, 28, 28, -1};
+ Common::Array<int> cyc25{15, 16, 15, 16, 15, 1 + 15, 1 + 15, -1};
+ Common::Array<int> cyc26{10 + 15, 11 + 15, 12 + 15, 13 + 15, 14 + 15, 15 + 15, 16 + 15, -1};
+ Common::Array<int> cyc27{2 + 15, 3 + 15, 4 + 15, 5 + 15, -1};
+ Common::Array<int> cyc28{6 + 15, 7 + 15, 8 + 15, 9 + 15, -1};
+ Common::Array<int> cyc29{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1};
+ Common::Array<int> cyc30{0, 1, 2, 3, 3, 3, 3, 4, 5, 6, -1};
+ Common::Array<int> cyc31{0, 1, 2, 3, 4, 5, 6, 7, 8, -1};
- Common::Array<SCycle> c{SCycle(kBubble, false, cyc0), SCycle(kBubble, false, cyc1),
- SCycle(kSpark, false, cyc2), SCycle(kSpark, false, cyc3),
- SCycle(kSpark, false, cyc4), SCycle(kSpark, false, cyc5), SCycle(kSpark, false, cyc6),
- SCycle(kPipe, false, cyc7), SCycle(kPipe, false, cyc8),
- SCycle(kPipe, false, cyc9), SCycle(kPipe, false, cyc10),
- SCycle(kAnaVanish, false, cyc11), SCycle(kAnaGlimpse, false, cyc12),
- SCycle(kKnife, true, cyc13),
- SCycle(kSpark, true, cyc14), SCycle(kSpark, true, cyc15), SCycle(kSpark, true, cyc16),
- SCycle(kBigBurst, false, cyc17),
- SCycle(kFlame, false, cyc18), SCycle(kFlame, false, cyc19), SCycle(kFlame, false, cyc20),
- SCycle(kFlame, false, cyc21), SCycle(kFlame, false, cyc22), SCycle(kFlame, false, cyc23),
- SCycle(kFlame, false, cyc24),
- SCycle(kCandle, false, cyc25), SCycle(kCandle, false, cyc26), SCycle(kCandle, false, cyc27),
- SCycle(kCandle, false, cyc28), SCycle(kCandle, false, cyc29),
- SCycle(kSink, false, cyc30),
- SCycle(kNorlacDown, false, cyc31)};
+ Common::Array<SCycle> c{SCycle(kBubble, false, cyc0), SCycle(kBubble, false, cyc1),
+ SCycle(kSpark, false, cyc2), SCycle(kSpark, false, cyc3),
+ SCycle(kSpark, false, cyc4), SCycle(kSpark, false, cyc5), SCycle(kSpark, false, cyc6),
+ SCycle(kPipe, false, cyc7), SCycle(kPipe, false, cyc8),
+ SCycle(kPipe, false, cyc9), SCycle(kPipe, false, cyc10),
+ SCycle(kAnaVanish, false, cyc11), SCycle(kAnaGlimpse, false, cyc12),
+ SCycle(kKnife, true, cyc13),
+ SCycle(kSpark, true, cyc14), SCycle(kSpark, true, cyc15), SCycle(kSpark, true, cyc16),
+ SCycle(kBigBurst, false, cyc17),
+ SCycle(kFlame, false, cyc18), SCycle(kFlame, false, cyc19), SCycle(kFlame, false, cyc20),
+ SCycle(kFlame, false, cyc21), SCycle(kFlame, false, cyc22), SCycle(kFlame, false, cyc23),
+ SCycle(kFlame, false, cyc24),
+ SCycle(kCandle, false, cyc25), SCycle(kCandle, false, cyc26), SCycle(kCandle, false, cyc27),
+ SCycle(kCandle, false, cyc28), SCycle(kCandle, false, cyc29),
+ SCycle(kSink, false, cyc30),
+ SCycle(kNorlacDown, false, cyc31)};
_cycPtrs = c;
Common::Array<Motive> m{};
@@ -251,7 +251,7 @@ void ImmortalEngine::initStoryDynamic() {
/* Universe related properties
* including spawn point and entry/exit points
- */
+ */
int univRoom = 4; // The room the player starts in when beginning this level
uint16 univRoomX = 512;
uint16 univRoomY = 416;
@@ -263,39 +263,39 @@ void ImmortalEngine::initStoryDynamic() {
_stories[0]._playerPointX = (1152 - univRoomX) / 2;
_stories[0]._playerPointY = 464 - univRoomY;
- Common::Array<int> ladders{-1, -1, kStoryNull, 2, 0, univRoom, (704 / 64),(544 / 32)};
+ Common::Array<int> ladders{-1, -1, kStoryNull, 2, 0, univRoom, (704 / 64), (544 / 32)};
_stories[0]._ladders = ladders;
/* All of the rooms
*/
Common::Array<SRoom> rooms{SRoom(384, 256, kRoomFlag0), SRoom(512, 64, kRoomFlag0),
- SRoom(640, 160, kRoomFlag0), SRoom(768, 224, kRoomFlag0),
- SRoom(univRoomX, univRoomY, kRoomFlag0), SRoom(960, 512, kRoomFlag0),
- SRoom(1024, 352, kRoomFlag0), SRoom(896, 64, kRoomFlag0)};
+ SRoom(640, 160, kRoomFlag0), SRoom(768, 224, kRoomFlag0),
+ SRoom(univRoomX, univRoomY, kRoomFlag0), SRoom(960, 512, kRoomFlag0),
+ SRoom(1024, 352, kRoomFlag0), SRoom(896, 64, kRoomFlag0)};
_stories[0]._rooms = rooms;
/* All of the doors
*/
Common::Array<SDoor> doors{SDoor(0, 704, 224, 0, 2, false), SDoor(1, 576, 352, 4, 0, true),
- SDoor(1, 704, 96, 2, 1, false), SDoor(1, 960, 128, 7, 2, false),
- SDoor(1, 1088,160, 3, 7, false), SDoor(1, 1088,320, 6, 3, false),
- SDoor(1, 896, 416, 4, 3, false)};
+ SDoor(1, 704, 96, 2, 1, false), SDoor(1, 960, 128, 7, 2, false),
+ SDoor(1, 1088, 160, 3, 7, false), SDoor(1, 1088, 320, 6, 3, false),
+ SDoor(1, 896, 416, 4, 3, false)};
_stories[0]._doors = doors;
/* All of the flames
* Macro for flames is (x - roomx), (y - roomy), pattern number
*/
- Common::Array<SFlame> f5{SFlame(512 - 384, (240 + 32) - 256, kFlameOff), SFlame(672 - 384, (240 + 32) - 256, kFlameOff)};
- Common::Array<SFlame> f7{SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(576 - 384, (112 + 32) - 256, kFlameNormal),
- SFlame(928 - 384, (48 + 32) - 256, kFlameNormal)};
- Common::Array<SFlame> f8{SFlame(800 - 640, (144 + 32) - 160, kFlameNormal)};
- Common::Array<SFlame> f9{SFlame(768 - 768, (304 + 32) - 224, kFlameNormal), SFlame((928 - 768), (304 + 32) - 224, kFlameNormal),
- SFlame(1024 - 768, (240 + 32) - 224, kFlameNormal)};
- Common::Array<SFlame> fA{SFlame(672 - 512, (400 + 32) - 416, kFlameNormal), SFlame((800 - 64) - 512, (496 - 32) - 416, kFlameNormal),
- SFlame(576 - 512, (528 + 32) - 416, kFlameNormal)};
- Common::Array<SFlame> fD{SFlame(1024 - 960, (496 + 32) - 512, kFlameNormal)};
- Common::Array<SFlame> fE{SFlame(1184 - 1024, 432 - 352, kFlameCandle)};
- Common::Array<SFlame> fF{SFlame(1024 - 896, (144 + 32) - 64, kFlameNormal)};
+ Common::Array<SFlame> f5{SFlame(512 - 384, (240 + 32) - 256, kFlameOff), SFlame(672 - 384, (240 + 32) - 256, kFlameOff)};
+ Common::Array<SFlame> f7{SFlame(576 - 384, (112 + 32) - 256, kFlameNormal), SFlame(576 - 384, (112 + 32) - 256, kFlameNormal),
+ SFlame(928 - 384, (48 + 32) - 256, kFlameNormal)};
+ Common::Array<SFlame> f8{SFlame(800 - 640, (144 + 32) - 160, kFlameNormal)};
+ Common::Array<SFlame> f9{SFlame(768 - 768, (304 + 32) - 224, kFlameNormal), SFlame((928 - 768), (304 + 32) - 224, kFlameNormal),
+ SFlame(1024 - 768, (240 + 32) - 224, kFlameNormal)};
+ Common::Array<SFlame> fA{SFlame(672 - 512, (400 + 32) - 416, kFlameNormal), SFlame((800 - 64) - 512, (496 - 32) - 416, kFlameNormal),
+ SFlame(576 - 512, (528 + 32) - 416, kFlameNormal)};
+ Common::Array<SFlame> fD{SFlame(1024 - 960, (496 + 32) - 512, kFlameNormal)};
+ Common::Array<SFlame> fE{SFlame(1184 - 1024, 432 - 352, kFlameCandle)};
+ Common::Array<SFlame> fF{SFlame(1024 - 896, (144 + 32) - 64, kFlameNormal)};
CArray2D<SFlame> flames{f5, f7, f8, f9, fA, fD, fE, fF};
_stories[0]._flames = flames;
@@ -303,29 +303,29 @@ void ImmortalEngine::initStoryDynamic() {
* Macro for traps is arrowType,freq,#sinkTraps,#1(going toward 5),#3,#5,#7,#trapdoors
*/
Common::Array<uint8> noTraps{};
- Common::Array<uint8> o5Traps{0,0x80,0,0,0,0,0,5};
- Common::Array<uint8> o7Traps{0,0x80,15,5,3,0,0,0};
- Common::Array<uint8> o8Traps{0,0x80,0,0,0,0,0,3};
+ Common::Array<uint8> o5Traps{0, 0x80, 0, 0, 0, 0, 0, 5};
+ Common::Array<uint8> o7Traps{0, 0x80, 15, 5, 3, 0, 0, 0};
+ Common::Array<uint8> o8Traps{0, 0x80, 0, 0, 0, 0, 0, 3};
Common::Array<SObj> noObj{};
Common::Array<SObj> o5{SObj(kZip, kZip, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o5Traps),
- SObj(459, 379, kTypeCoin, kRingFrame, kObjNone, noTraps),
- SObj(446, 327, kTypeWowCharm, kScrollFrame, kObjNone, noTraps)};
+ SObj(459, 379, kTypeCoin, kRingFrame, kObjNone, noTraps),
+ SObj(446, 327, kTypeWowCharm, kScrollFrame, kObjNone, noTraps)};
Common::Array<SObj> o7{SObj(145, 138, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o7Traps)};
Common::Array<SObj> o8{SObj(kZip, kZip, kTypeTrap, kNoFrame, kObjIsRunning + kObjIsInvisible, o8Traps)};
Common::Array<SObj> o9{SObj(1052, 309, kTypeDead, kDeadGoblinFrame, kObjIsChest + kObjIsOnGround, noTraps),
- SObj(kZip, kZip, kTypeFireBall, kScrollFrame, kObjUsesFireButton, noTraps),
- SObj(128, 464, kTypeDunRing, kRingFrame, 0, noTraps),
- SObj(837, 421, kTypeChest, kChest0Frame, kObjIsChest, noTraps),
- SObj(kZip, kZip, kTypeDeathMap, kScrollFrame, 0, noTraps),
- SObj(597, 457, kTypeWater, kVaseFrame, 0, noTraps),
- SObj(kZip, kZip, kTypeSpores, kSporesFrame, 0, noTraps),
- SObj(kZip, kZip, kTypeWormFood, kNoFrame, 0, noTraps),
- SObj(205, 158, kTypeChestKey, kKeyFrame, 0, noTraps)};
+ SObj(kZip, kZip, kTypeFireBall, kScrollFrame, kObjUsesFireButton, noTraps),
+ SObj(128, 464, kTypeDunRing, kRingFrame, 0, noTraps),
+ SObj(837, 421, kTypeChest, kChest0Frame, kObjIsChest, noTraps),
+ SObj(kZip, kZip, kTypeDeathMap, kScrollFrame, 0, noTraps),
+ SObj(597, 457, kTypeWater, kVaseFrame, 0, noTraps),
+ SObj(kZip, kZip, kTypeSpores, kSporesFrame, 0, noTraps),
+ SObj(kZip, kZip, kTypeWormFood, kNoFrame, 0, noTraps),
+ SObj(205, 158, kTypeChestKey, kKeyFrame, 0, noTraps)};
Common::Array<SObj> oE{SObj(1184, 426, kTypePhant, kAltarFrame, 0, noTraps),
- SObj(145, 138, kTypeGold, kNoFrame, kObjIsRunning, noTraps),
- SObj(671, 461, kTypeHay, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps),
- SObj(780, 508, kTypeBeam, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps)};
+ SObj(145, 138, kTypeGold, kNoFrame, kObjIsRunning, noTraps),
+ SObj(671, 461, kTypeHay, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps),
+ SObj(780, 508, kTypeBeam, kNoFrame, kObjIsRunning + kObjIsInvisible, noTraps)};
CArray2D<SObj> objects{o5, o7, o8, o9, noObj, noObj, oE, noObj};
_stories[0]._objects = objects;
@@ -341,10 +341,10 @@ void ImmortalEngine::initStoryDynamic() {
Common::Array<SMonster> noMonst{};
Common::Array<SMonster> m5{SMonster(448, 344, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow),
- SMonster(590, 381, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow)};
+ SMonster(590, 381, 12, kMonstPlayer, kMonstA + kMonstIsEngage + kMonstIsTough, progShade, kShadow)};
Common::Array<SMonster> m9{SMonster(1106, 258, 3, kMonstPlayer, kMonstA + kMonstIsEngage, progEasy, kGoblin0),
- SMonster(832, 364, 10, kMonstA, kMonstB + kMonstIsPoss, progUlindor, kUlindor3),
- SMonster(838, 370, 15, kMonstPlayer, kMonstA + kMonstIsEngage, progGoblin5, kGoblin7)};
+ SMonster(832, 364, 10, kMonstA, kMonstB + kMonstIsPoss, progUlindor, kUlindor3),
+ SMonster(838, 370, 15, kMonstPlayer, kMonstA + kMonstIsEngage, progGoblin5, kGoblin7)};
Common::Array<SMonster> mE{SMonster(1136, 464, 15, kMonstMonster, kMonstPlayer + kMonstIsEngage, progPlayer, kWizard0)};
Common::Array<SMonster> mF{SMonster(1182, 116, 5, kMonstPlayer, kMonstA + kMonstIsEngage, progWill2, kGoblin5)};
CArray2D<SMonster> monsters{m5, noMonst, noMonst, m9, noMonst, noMonst, mE, mF};
diff --git a/engines/immortal/story.h b/engines/immortal/story.h
index f201c8ae0e4..b3cfd1732aa 100644
--- a/engines/immortal/story.h
+++ b/engines/immortal/story.h
@@ -32,25 +32,25 @@ namespace Immortal {
// These maximum numbers aren't really needed, because most of these are vectors and have .size()
enum StoryMaxes {
- kMaxRooms = 16,
- kMaxDoors = 10,
- kMaxFlames = 32,
+ kMaxRooms = 16,
+ kMaxDoors = 10,
+ kMaxFlames = 32,
kMaxFlamesInRoom = 5,
- kMaxObjects = 42,
- kMaxMonsters = 20,
- kMaxGenSprites = 6,
- kMaxCycles = 32
+ kMaxObjects = 42,
+ kMaxMonsters = 20,
+ kMaxGenSprites = 6,
+ kMaxCycles = 32
};
// These are flags that are relevant to their specific story data structures
-enum RoomFlag : uint8 { // Generic properties available to each room
+enum RoomFlag : uint8 { // Generic properties available to each room
kRoomFlag0 = 0x01,
kRoomFlag1 = 0x02,
kRoomFlag2 = 0x04,
kRoomFlag3 = 0x08
};
-enum ObjFlag : uint8 { // Properties of the object essentially
+enum ObjFlag : uint8 { // Properties of the object essentially
kObjUsesFireButton = 0x40,
kObjIsInvisible = 0x20,
kObjIsRunning = 0x10,
@@ -58,16 +58,16 @@ enum ObjFlag : uint8 { // Properties of the object essentially
kObjIsOnGround = 0x04,
kObjIsF1 = 0x02,
kObjIsF2 = 0x01,
- kObjNone = 0x0
+ kObjNone = 0x0
};
-enum IsA : uint8 { // To be completely honest, I'm not really sure what this is. It seems to be more object flags, but they act a little strangely
- kIsAF1 = 0x20,
- kIsAF2 = 0x40,
+enum IsA : uint8 { // To be completely honest, I'm not really sure what this is. It seems to be more object flags, but they act a little strangely
+ kIsAF1 = 0x20,
+ kIsAF2 = 0x40,
kIsANone = 0x0,
};
-enum MonsterFlag : uint8 { // Mostly properties of the AI for a given monster, *including the player*
+enum MonsterFlag : uint8 { // Mostly properties of the AI for a given monster, *including the player*
kMonstIsNone = 0x00,
kMonstIsTough = 0x10,
kMonstIsDead = 0x20,
@@ -78,14 +78,14 @@ enum MonsterFlag : uint8 { // Mostly properties of the AI for a given monst
kMonstMonster = 0x01,
kMonstAnybody = 0x02,
kMonstNobody = 0x03,
- kMonstA = 0x04,
- kMonstB = 0x05,
- kMonstC = 0x06,
- kMonstD = 0x07
+ kMonstA = 0x04,
+ kMonstB = 0x05,
+ kMonstC = 0x06,
+ kMonstD = 0x07
};
// Flame pattern is used by the story data, in-room data, *and* the level based total flame data. So it needs to be in story.h to be used by immortal.h and room.h
-enum FPattern : uint8 { // This defines which Cyc animation it uses
+enum FPattern : uint8 { // This defines which Cyc animation it uses
kFlameNormal,
kFlameCandle,
kFlameOff,
@@ -128,7 +128,7 @@ struct ObjType {
// Cycles define the animation of sprites within a level. There is a fixed total of cycles available, and they are not room dependant
struct Cycle {
- int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
+ int _index; // In source this is actually the position within the *instruction list*, but since cycle's are structs, it's just the index of frames now
CycID _cycList;
};
@@ -141,12 +141,12 @@ struct SCycle {
SpriteName _sName;
Common::Array<int> _frames;
bool _repeat;
- SCycle() {}
- SCycle(SpriteName s, bool r, Common::Array<int> f) {
- _sName = s;
- _repeat = r;
- _frames = f;
- }
+ SCycle() {}
+ SCycle(SpriteName s, bool r, Common::Array<int> f) {
+ _sName = s;
+ _repeat = r;
+ _frames = f;
+ }
};
struct SRoom {
@@ -156,8 +156,8 @@ struct SRoom {
RoomFlag _flags = kRoomFlag0;
SRoom() {}
SRoom(uint16 x, uint16 y, RoomFlag f) {
- _x = x;
- _y = y;
+ _x = x;
+ _y = y;
_flags = f;
}
};
@@ -174,19 +174,19 @@ struct SDoor {
SDoor() {}
SDoor(uint8 d, uint16 x, uint16 y, uint16 f, uint16 t, bool l) {
_dir = d;
- _x = x;
- _y = y;
+ _x = x;
+ _y = y;
_fromRoom = f;
- _toRoom = t;
+ _toRoom = t;
_isLocked = l;
- }
+ }
};
struct SFlame {
- uint16 _x = 0;
- uint16 _y = 0;
+ uint16 _x = 0;
+ uint16 _y = 0;
FPattern _p = kFlameOff;
SFlame() {}
@@ -207,9 +207,9 @@ struct SObj {
Common::Array<uint8> _traps;
SObj() {}
SObj(uint16 x, uint16 y, SObjType t, SpriteFrame s, uint8 f, Common::Array<uint8> traps) {
- _x = x;
- _y = y;
- _type = t;
+ _x = x;
+ _y = y;
+ _type = t;
_flags = f;
_traps = traps;
_frame = s;
@@ -227,13 +227,13 @@ struct SMonster {
Common::Array<Motive> _program;
SMonster() {}
SMonster(uint16 x, uint16 y, uint16 h, MonsterFlag m, uint8 f, Common::Array<Motive> p, SpriteName s) {
- _x = x;
- _y = y;
- _hits = h;
+ _x = x;
+ _y = y;
+ _hits = h;
_madAt = m;
_flags = f;
- _program = p;
- _sprite = s;
+ _program = p;
+ _sprite = s;
}
};
@@ -246,12 +246,12 @@ struct Story {
uint16 _playerPointX = 0;
uint16 _playerPointY = 0;
- Common::Array<int> _ladders;
+ Common::Array<int> _ladders;
Common::Array<SRoom> _rooms;
Common::Array<SDoor> _doors;
- CArray2D<SFlame> _flames;
- CArray2D<SObj> _objects;
- CArray2D<SMonster> _monsters;
+ CArray2D<SFlame> _flames;
+ CArray2D<SObj> _objects;
+ CArray2D<SMonster> _monsters;
};
} // namespace immortal
diff --git a/engines/immortal/utilities.cpp b/engines/immortal/utilities.cpp
index 46d098a2fb0..03e5107fc4e 100644
--- a/engines/immortal/utilities.cpp
+++ b/engines/immortal/utilities.cpp
@@ -23,7 +23,7 @@
namespace Immortal {
-/*
+/*
*
* ----- -----
* ----- General Use -----
Commit: ffb7ffc6eb71431d0a79a34837b232ce40c99d71
https://github.com/scummvm/scummvm/commit/ffb7ffc6eb71431d0a79a34837b232ce40c99d71
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Pass flameSet list by reference instead of copy
Changed paths:
engines/immortal/flameSet.cpp
engines/immortal/room.h
diff --git a/engines/immortal/flameSet.cpp b/engines/immortal/flameSet.cpp
index f9a7964f8d3..a5f48744881 100644
--- a/engines/immortal/flameSet.cpp
+++ b/engines/immortal/flameSet.cpp
@@ -88,7 +88,7 @@ void Room::lightTorch(uint8 x, uint8 y) {
}
}
-void Room::flameSetRoom(Common::Array<SFlame> allFlames) {
+void Room::flameSetRoom(Common::Array<SFlame> &allFlames) {
for (int i = 0; i < allFlames.size(); i++) {
Flame f;
f._p = allFlames[i]._p;
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index df968d68c4e..0f26e364f2c 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -204,7 +204,7 @@ public:
bool roomLighted();
void lightTorch(uint8 x, uint8 y);
void flameFreeAll();
- void flameSetRoom(Common::Array<SFlame>);
+ void flameSetRoom(Common::Array<SFlame> &allFlames);
int flameGetCyc(Flame *f, int first);
/*
Commit: 4116c881e628a2e9b405f959cb9b4353be6d27d5
https://github.com/scummvm/scummvm/commit/4116c881e628a2e9b405f959cb9b4353be6d27d5
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Change loadPalette to use readUint16LE() instead of read()
Changed paths:
engines/immortal/kernal.cpp
diff --git a/engines/immortal/kernal.cpp b/engines/immortal/kernal.cpp
index 00f0d27ed3d..2db5382ed75 100644
--- a/engines/immortal/kernal.cpp
+++ b/engines/immortal/kernal.cpp
@@ -779,13 +779,19 @@ void ImmortalEngine::loadPalette() {
// The palettes are stored at a particular location in the disk, this just grabs them
Common::File d;
d.open("IMMORTAL.dsk");
-
d.seek(kPaletteOffset);
- d.read(_palDefault, 32);
- d.read(_palWhite, 32);
- d.read(_palBlack, 32);
- d.read(_palDim, 32);
+
+ // Each palette is stored after each other at this kPaletteOffset in the disk
+ uint16 *pals[4] = {_palDefault, _palWhite, _palBlack, _palDim};
+
+ // So we can just grab 16 colours at a time and store them to the appropriate palette
+ for (int p = 0; p < 4; p++) {
+ for (int i = 0; i < 16; i++) {
+ pals[p][i] = d.readUint16LE();
+ }
+ }
+ // And now we are done with the file
d.close();
}
Commit: 878fbe404ca5d8d56a559909a760c1b503305aa2
https://github.com/scummvm/scummvm/commit/878fbe404ca5d8d56a559909a760c1b503305aa2
Author: Quote58 (michael.hayman54 at gmail.com)
Date: 2023-02-05T21:59:12+01:00
Commit Message:
IMMORTAL: Clean up some of the header dependancies
Changed paths:
engines/immortal/disk.cpp
engines/immortal/immortal.cpp
engines/immortal/immortal.h
engines/immortal/level.cpp
engines/immortal/room.h
engines/immortal/utilities.h
diff --git a/engines/immortal/disk.cpp b/engines/immortal/disk.cpp
index 5767aefac6c..1e706f56f5d 100644
--- a/engines/immortal/disk.cpp
+++ b/engines/immortal/disk.cpp
@@ -19,9 +19,6 @@
*
*/
-
-#include "common/debug.h"
-#include "common/file.h"
#include "immortal/disk.h"
namespace Immortal {
diff --git a/engines/immortal/immortal.cpp b/engines/immortal/immortal.cpp
index e7c5ca7913b..90b19ca8d3a 100644
--- a/engines/immortal/immortal.cpp
+++ b/engines/immortal/immortal.cpp
@@ -19,11 +19,11 @@
*
*/
+// common/config-manager is needed for the search manager
#include "common/config-manager.h"
-#include "common/system.h"
+// engines/util is needed for initGraphics()
#include "engines/util.h"
-
#include "immortal/immortal.h"
namespace Immortal {
@@ -108,7 +108,7 @@ Common::ErrorCode ImmortalEngine::initDisks() {
if (SearchMan.hasFile("IMMORTAL.dsk")) {
// Instantiate the disk as an object. The disk will then open and parse itself
- ProDOSDisk *diskBoot = new ProDOSDisk("IMMORTAL.dsk");
+ ProDOSDisk *diskBoot = new ProDOSDisk("IMMORTAL.dsk");
if (diskBoot) {
// With the disk successfully parsed, it can be added to the search manager
@@ -122,7 +122,7 @@ Common::ErrorCode ImmortalEngine::initDisks() {
// Check for the gfx disk
if (SearchMan.hasFile("IMMORTAL_GFX.dsk")) {
- ProDOSDisk *diskGFX = new ProDOSDisk("IMMORTAL_GFX.dsk");
+ ProDOSDisk *diskGFX = new ProDOSDisk("IMMORTAL_GFX.dsk");
if (diskGFX) {
debug("Gfx disk found");
SearchMan.add("IMMORTAL_GFX.dsk", diskGFX, 0, true);
diff --git a/engines/immortal/immortal.h b/engines/immortal/immortal.h
index 2e2b520b490..d56dfe8875b 100644
--- a/engines/immortal/immortal.h
+++ b/engines/immortal/immortal.h
@@ -40,28 +40,21 @@
// Disk is only used by immortal.cpp
#include "immortal/disk.h"
-// Common is needed by immortal.h, room.h, and monster.h
-#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/scummsys.h"
#include "common/system.h"
-#include "common/error.h"
#include "common/fs.h"
-#include "common/file.h"
-#include "common/memstream.h"
#include "common/hash-str.h"
#include "common/random.h"
#include "common/serializer.h"
#include "common/util.h"
#include "common/platform.h"
-// Story is needed by both immortal.h and room.h
-#include "immortal/story.h"
-
// Utilities.h contains many things used by all objects, not just immortal
#include "immortal/utilities.h"
+// Room also includes story.h
#include "immortal/room.h"
namespace Immortal {
diff --git a/engines/immortal/level.cpp b/engines/immortal/level.cpp
index 2010da59ca3..67a92b702d9 100644
--- a/engines/immortal/level.cpp
+++ b/engines/immortal/level.cpp
@@ -24,8 +24,6 @@
namespace Immortal {
-struct Flame;
-
void ImmortalEngine::levelInitAtStartOfGameOnly() {
initStoryDynamic();
_lastLevelLoaded = -1;
diff --git a/engines/immortal/room.h b/engines/immortal/room.h
index 0f26e364f2c..b6bf67833e7 100644
--- a/engines/immortal/room.h
+++ b/engines/immortal/room.h
@@ -23,28 +23,14 @@
*
*/
-// Common is needed by immortal.h, room.h, and monster.h
-#include "common/debug.h"
-#include "common/debug-channels.h"
-#include "common/events.h"
-#include "common/scummsys.h"
+// Common/system includes basic things like Array
#include "common/system.h"
-#include "common/error.h"
-#include "common/fs.h"
-#include "common/file.h"
-#include "common/memstream.h"
-#include "common/hash-str.h"
-#include "common/random.h"
-#include "common/serializer.h"
-#include "common/util.h"
-#include "common/platform.h"
// Story is needed by both immortal.h and room.h
#include "immortal/story.h"
// Utilities.h contains many things used by all objects, not just immortal
#include "immortal/utilities.h"
-
#include "immortal/immortal.h"
#ifndef IMMORTAL_ROOM_H
diff --git a/engines/immortal/utilities.h b/engines/immortal/utilities.h
index d1a1fcd064f..79e76efebf1 100644
--- a/engines/immortal/utilities.h
+++ b/engines/immortal/utilities.h
@@ -22,13 +22,8 @@
#ifndef IMMORTAL_UTIL_H
#define IMMORTAL_UTIL_H
-#include "common/debug.h"
-#include "common/debug-channels.h"
#include "common/system.h"
-#include "immortal/sprite_list.h"
-#include "immortal/definitions.h"
-
namespace Immortal {
enum BitMask16 : uint16 {
More information about the Scummvm-git-logs
mailing list