[Scummvm-git-logs] scummvm master -> 78522c9122b2c823ffa765c02c655188bcc0dd9c
fracturehill
noreply at scummvm.org
Thu Sep 14 19:54:42 UTC 2023
This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
538e124959 NANCY: Make nancy6 bootable
6b719b4b44 NANCY: Read CVTX chunks
6c4621b8ee NANCY: Document engine data structs
c0b89cccbe NANCY: Fix PasswordPuzzle reading
edf03037a2 NANCY: Properly clear deleted RenderObjects from screen
53a20b9002 NANCY: Finish implementing RaycastPuzzle
78522c9122 NANCY: Silence compiler warning
Commit: 538e12495975b255c8d015a101c682dec6ca5022
https://github.com/scummvm/scummvm/commit/538e12495975b255c8d015a101c682dec6ca5022
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Make nancy6 bootable
Made changes to BOOT chunk reading code so the game
no longer crashes on startup.
Changed paths:
engines/nancy/enginedata.cpp
engines/nancy/enginedata.h
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 031355fdcb6..354b54baab0 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -43,6 +43,9 @@ BSUM::BSUM(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
s.skip(0x49, kGameTypeNancy1, kGameTypeNancy1);
s.skip(0x43, kGameTypeNancy2);
+ readFilename(s, conversationTextsFilename, kGameTypeNancy6);
+ readFilename(s, autotextFilename, kGameTypeNancy6);
+
s.syncAsUint16LE(firstScene.sceneID);
s.skip(0xC, kGameTypeVampire, kGameTypeVampire); // Palette name + unknown 2 bytes
s.syncAsUint16LE(firstScene.frameID);
@@ -359,7 +362,14 @@ SET::SET(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
chunkStream->skip(20); // image info
chunkStream->skip(16); // bounds for all scrollbars
- uint numButtons = g_nancy->getGameType() == kGameTypeVampire ? 5 : 4;
+ uint numButtons;
+ if (g_nancy->getGameType() == kGameTypeVampire) {
+ numButtons = 5;
+ } else if (g_nancy->getGameType() <= kGameTypeNancy5) {
+ numButtons = 4;
+ } else {
+ numButtons = 3;
+ }
readRectArray(*chunkStream, _scrollbarBounds, 3);
readRectArray(*chunkStream, _buttonDests, numButtons);
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index e4e7270c916..679ea6ed14e 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -38,6 +38,9 @@ struct BSUM : public EngineData {
byte header[90];
+ Common::String conversationTextsFilename;
+ Common::String autotextFilename;
+
// Game start section
SceneChangeDescription firstScene;
uint16 startTimeHours;
Commit: 6b719b4b44599411c2aa5c5837ce3447e80eadef
https://github.com/scummvm/scummvm/commit/6b719b4b44599411c2aa5c5837ce3447e80eadef
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Read CVTX chunks
Added code for reading CVTX chunks, which contain
an array of key-value pairs of strings (introduced in nancy6).
Changed paths:
engines/nancy/enginedata.cpp
engines/nancy/enginedata.h
engines/nancy/nancy.cpp
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 354b54baab0..fdf7d022936 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -92,7 +92,6 @@ VIEW::VIEW(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
}
PCAL::PCAL(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
- assert(chunkStream);
uint num = chunkStream->readUint16LE();
readFilenameArray(*chunkStream, calNames, num);
}
@@ -185,11 +184,8 @@ INV::INV(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
}
TBOX::TBOX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
- assert(chunkStream);
-
bool isVampire = g_nancy->getGameType() == Nancy::GameType::kGameTypeVampire;
- chunkStream->seek(0);
readRect(*chunkStream, scrollbarSrcBounds);
chunkStream->seek(0x20);
@@ -318,11 +314,7 @@ HELP::HELP(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
}
CRED::CRED(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
- assert(chunkStream);
-
bool isVampire = g_nancy->getGameType() == kGameTypeVampire;
- chunkStream->seek(0);
-
readFilename(*chunkStream, imageName);
textNames.resize(isVampire ? 7 : 1);
@@ -537,20 +529,12 @@ CLOK::CLOK(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
}
SPEC::SPEC(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
- assert(chunkStream);
-
- chunkStream->seek(0);
-
fadeToBlackNumFrames = chunkStream->readByte();
fadeToBlackFrameTime = chunkStream->readUint16LE();
crossDissolveNumFrames = chunkStream->readUint16LE();
}
RCLB::RCLB(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
- assert(chunkStream);
-
- chunkStream->seek(0);
-
lightSwitchID = chunkStream->readUint16LE();
unk2 = chunkStream->readUint16LE();
@@ -628,10 +612,6 @@ RCLB::RCLB(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
}
RCPR::RCPR(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
- assert(chunkStream);
-
- chunkStream->seek(0);
-
readRectArray(*chunkStream, screenViewportSizes, 6);
viewportSizeUsed = chunkStream->readUint16LE();
@@ -696,4 +676,28 @@ ImageChunk::ImageChunk(Common::SeekableReadStream *chunkStream) : EngineData(chu
height = chunkStream->readUint16LE();
}
+CVTX::CVTX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
+ uint16 numEntries = chunkStream->readUint16LE();
+
+ char *buf = nullptr;
+ uint bufSize = 0;
+ Common::String keyName;
+
+ for (uint i = 0; i < numEntries; ++i) {
+ readFilename(*chunkStream, keyName);
+ uint16 stringSize = chunkStream->readUint16LE();
+ if (stringSize > bufSize) {
+ delete buf;
+ buf = new char[stringSize * 2];
+ bufSize = stringSize * 2;
+
+ chunkStream->read(buf, stringSize);
+ buf[stringSize] = '\0';
+ texts.setVal(keyName, buf);
+ }
+ }
+
+ delete buf;
+}
+
} // End of namespace Nancy
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index 679ea6ed14e..b09522793e8 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -380,6 +380,12 @@ struct ImageChunk : public EngineData {
uint16 height;
};
+struct CVTX : public EngineData {
+ CVTX(Common::SeekableReadStream *chunkStream);
+
+ Common::HashMap<Common::String, Common::String> texts;
+};
+
} // End of namespace Nancy
#endif // NANCY_ENGINEDATA_H
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index b27f60cf13b..ec007f95d1c 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -369,13 +369,13 @@ void NancyEngine::bootGameEngine() {
// Setup mixer
syncSoundSettings();
- IFF *boot = new IFF("boot");
- if (!boot->load())
+ IFF *iff = new IFF("boot");
+ if (!iff->load())
error("Failed to load boot script");
// Load BOOT chunks data
Common::SeekableReadStream *chunkStream = nullptr;
- #define LOAD_BOOT_L(t, s) if (chunkStream = boot->getChunkStream(s), chunkStream) { \
+ #define LOAD_BOOT_L(t, s) if (chunkStream = iff->getChunkStream(s), chunkStream) { \
_engineData.setVal(s, new t(chunkStream)); \
delete chunkStream; \
}
@@ -404,29 +404,57 @@ void NancyEngine::bootGameEngine() {
LOAD_BOOT_L(ImageChunk, "FR0")
LOAD_BOOT_L(ImageChunk, "LG0")
- chunkStream = boot->getChunkStream("PLG0");
+ chunkStream = iff->getChunkStream("PLG0");
if (!chunkStream) {
- chunkStream = boot->getChunkStream("PLGO"); // nancy4 and above use an O instead of a zero
+ chunkStream = iff->getChunkStream("PLGO"); // nancy4 and above use an O instead of a zero
}
if (chunkStream) {
_engineData.setVal("PLG0", new ImageChunk(chunkStream));
}
- _cursorManager->init(boot->getChunkStream("CURS"));
+ _cursorManager->init(iff->getChunkStream("CURS"));
_graphicsManager->init();
- _graphicsManager->loadFonts(boot->getChunkStream("FONT"));
-
- #undef LOAD_BOOT_L
- #undef LOAD_BOOT
+ _graphicsManager->loadFonts(iff->getChunkStream("FONT"));
preloadCals();
_sound->initSoundChannels();
- _sound->loadCommonSounds(boot);
+ _sound->loadCommonSounds(iff);
+
+ delete iff;
+
+ // Load convo texts and autotext
+ const BSUM *bsum = (const BSUM *)getEngineData("BSUM");
+ if (bsum && bsum->conversationTextsFilename.size() && bsum->autotextFilename.size()) {
+ iff = new IFF(bsum->conversationTextsFilename);
+ if (!iff->load()) {
+ error("Could not load CONVO IFF");
+ }
+
+ if (chunkStream = iff->getChunkStream("CVTX"), chunkStream) {
+ _engineData.setVal("CONVO", new CVTX(chunkStream));
+ delete chunkStream;
+ }
+
+ delete iff;
- delete boot;
+ iff = new IFF(bsum->autotextFilename);
+ if (!iff->load()) {
+ error("Could not load AUTOTEXT IFF");
+ }
+
+ if (chunkStream = iff->getChunkStream("CVTX"), chunkStream) {
+ _engineData.setVal("AUTOTEXT", new CVTX(chunkStream));
+ delete chunkStream;
+ }
+
+ delete iff;
+ }
+
+ #undef LOAD_BOOT_L
+ #undef LOAD_BOOT
}
State::State *NancyEngine::getStateObject(NancyState::NancyState state) const {
Commit: 6c4621b8ee56a40d0d60cd5abece43e83ae3accc
https://github.com/scummvm/scummvm/commit/6c4621b8ee56a40d0d60cd5abece43e83ae3accc
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Document engine data structs
Added comments describing the structs inside enginedata.h
Changed paths:
engines/nancy/enginedata.h
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index b09522793e8..8eb7aceb21b 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -33,6 +33,7 @@ struct EngineData {
virtual ~EngineData() {}
};
+// Boot summary. Contains data for the UI, game clock, starting a new game.
struct BSUM : public EngineData {
BSUM(Common::SeekableReadStream *chunkStream);
@@ -69,6 +70,7 @@ struct BSUM : public EngineData {
uint16 fastMovementTimeDelta;
};
+// Contains rects defining the in-game viewport
struct VIEW : public EngineData {
VIEW(Common::SeekableReadStream *chunkStream);
@@ -76,12 +78,17 @@ struct VIEW : public EngineData {
Common::Rect bounds;
};
+// Contains a list of .cal filenames, which are to be loaded at startup.
+// .cal files themselves are just collections of image files used in dialogue.
+// First introduced in nancy2.
struct PCAL : public EngineData {
PCAL(Common::SeekableReadStream *chunkStream);
Common::Array<Common::String> calNames;
};
+// Contains definitions for all in-game items, as well as data for the
+// inventory box at the bottom right of the game screen.
struct INV : public EngineData {
struct ItemDescription {
Common::String name;
@@ -117,6 +124,7 @@ struct INV : public EngineData {
Common::Array<ItemDescription> itemDescriptions;
};
+// Contains data about the textbox at the bottom left of the game screen
struct TBOX : public EngineData {
TBOX(Common::SeekableReadStream *chunkStream);
@@ -138,6 +146,7 @@ struct TBOX : public EngineData {
uint16 highlightConversationFontID;
};
+// Contains data about the map state. Only used in TVD and nancy1
struct MAP : public EngineData {
struct Location {
Common::String description;
@@ -171,6 +180,7 @@ struct MAP : public EngineData {
Common::Point cursorPosition;
};
+// Contains data for the help screen.
struct HELP : public EngineData {
HELP(Common::SeekableReadStream *chunkStream);
@@ -180,6 +190,7 @@ struct HELP : public EngineData {
Common::Rect buttonHoverSrc;
};
+// Contains data for the credits screen.
struct CRED : public EngineData {
CRED(Common::SeekableReadStream *chunkStream);
@@ -191,6 +202,7 @@ struct CRED : public EngineData {
SoundDescription sound;
};
+// Contains data for the main menu.
struct MENU : public EngineData {
MENU(Common::SeekableReadStream *chunkStream);
@@ -201,6 +213,7 @@ struct MENU : public EngineData {
Common::Array<Common::Rect> _buttonDisabledSrcs;
};
+// Contains data for the Setup screen (a.k.a settings menu)
struct SET : public EngineData {
SET(Common::SeekableReadStream *chunkStream);
@@ -219,6 +232,7 @@ struct SET : public EngineData {
Common::Array<SoundDescription> _sounds;
};
+// Contains data for the Save/Load screen
struct LOAD : public EngineData {
LOAD(Common::SeekableReadStream *chunkStream);
@@ -258,6 +272,8 @@ struct LOAD : public EngineData {
// Common::Rect _gameSavedBounds
};
+// Contains data for the prompt that appears when exiting the game
+// without saving first. Introduced in nancy3.
struct SDLG : public EngineData {
SDLG(Common::SeekableReadStream *chunkStream);
@@ -276,18 +292,22 @@ struct SDLG : public EngineData {
Common::Rect _cancelDownSrc;
};
+// Contains data for the hint system. Only used in nancy1.
struct HINT : public EngineData {
HINT(Common::SeekableReadStream *chunkStream);
Common::Array<uint16> numHints;
};
+// Contains data for the slider puzzle. First used in nancy1
struct SPUZ : public EngineData {
SPUZ(Common::SeekableReadStream *chunkStream);
Common::Array<Common::Array<int16>> tileOrder;
};
+// Contains data for the clock UI that appears at the bottom left of the screen (top left in TVD)
+// Not used in nancy1 but still present in the data.
struct CLOK : public EngineData {
CLOK(Common::SeekableReadStream *chunkStream);
@@ -311,6 +331,8 @@ struct CLOK : public EngineData {
Common::Array<Common::Rect> nancy5CountdownSrcs;
};
+// Contains data for special effects (fades between scenes/fades to black).
+// Introduced in nancy2.
struct SPEC : public EngineData {
SPEC(Common::SeekableReadStream *chunkStream);
@@ -319,6 +341,8 @@ struct SPEC : public EngineData {
byte crossDissolveNumFrames;
};
+// Contains data for the raycast puzzle in nancy3. Specifically, this is the
+// data for the different "themes" that appear in the 3D space.
struct RCLB : public EngineData {
struct Theme {
Common::String themeName;
@@ -349,6 +373,8 @@ struct RCLB : public EngineData {
Common::Array<Theme> themes;
};
+// Contains data about the raycast puzzle in nancy3. Specifically, this is the
+// data for the debug map and the names of the textures to be used when rendering.
struct RCPR : public EngineData {
RCPR(Common::SeekableReadStream *chunkStream);
@@ -372,6 +398,7 @@ struct RCPR : public EngineData {
Common::Array<Common::String> floorNames;
};
+// Contains the name and dimensions of an image.
struct ImageChunk : public EngineData {
ImageChunk(Common::SeekableReadStream *chunkStream);
@@ -380,6 +407,10 @@ struct ImageChunk : public EngineData {
uint16 height;
};
+// Contains text data. Every string is tagged with a key via which
+// it can be accessed. Used to store dialogue and journal (autotext) strings.
+// NOT found inside BOOT; these are stored in their own cifs, the names of which
+// can be found inside BSUM. Introduced in nancy6.
struct CVTX : public EngineData {
CVTX(Common::SeekableReadStream *chunkStream);
Commit: c0b89cccbe69cd54ccd2857c9c9d9fce33d756b8
https://github.com/scummvm/scummvm/commit/c0b89cccbe69cd54ccd2857c9c9d9fce33d756b8
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Fix PasswordPuzzle reading
Added two missing version checks without which older
versions of the puzzle would crash the game.
Changed paths:
engines/nancy/action/puzzle/passwordpuzzle.cpp
diff --git a/engines/nancy/action/puzzle/passwordpuzzle.cpp b/engines/nancy/action/puzzle/passwordpuzzle.cpp
index 0cb65efb291..835c954fff3 100644
--- a/engines/nancy/action/puzzle/passwordpuzzle.cpp
+++ b/engines/nancy/action/puzzle/passwordpuzzle.cpp
@@ -55,7 +55,7 @@ void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
uint numPasswords = 1;
char buf[20];
- s.syncAsUint16LE(numNames);
+ s.syncAsUint16LE(numNames, kGameTypeNancy4);
_names.resize(numNames);
for (uint i = 0; i < numNames; ++i) {
stream.read(buf, 20);
@@ -64,9 +64,9 @@ void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
_maxNameLength = MAX(_maxNameLength, _names[i].size());
}
- s.skip((5 - numNames) * 20);
+ s.skip((5 - numNames) * 20, kGameTypeNancy4);
- s.syncAsUint16LE(numPasswords);
+ s.syncAsUint16LE(numPasswords, kGameTypeNancy4);
_passwords.resize(numPasswords);
for (uint i = 0; i < numPasswords; ++i) {
stream.read(buf, 20);
@@ -75,7 +75,7 @@ void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
_maxPasswordLength = MAX(_maxPasswordLength, _passwords[i].size());
}
- s.skip((5 - numPasswords) * 20);
+ s.skip((5 - numPasswords) * 20, kGameTypeNancy4);
_solveExitScene.readData(stream);
_solveSound.readNormal(stream);
Commit: edf03037a231037130725eb72dd23e35c9de6c0f
https://github.com/scummvm/scummvm/commit/edf03037a231037130725eb72dd23e35c9de6c0f
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Properly clear deleted RenderObjects from screen
Added code that makes sure that when a RenderObject is removed from GraphicsManager's internal list, its graphics
do not stay on screen.
Changed paths:
engines/nancy/graphics.cpp
engines/nancy/graphics.h
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index bd75e629e72..73d13d41a03 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -58,7 +58,6 @@ void GraphicsManager::draw(bool updateScreen) {
}
g_nancy->_cursorManager->applyCursor();
- Common::List<Common::Rect> dirtyRects;
// Update graphics for all RenderObjects and determine
// the areas of the screen that need to be redrawn
@@ -71,14 +70,14 @@ void GraphicsManager::draw(bool updateScreen) {
if (current._isVisible) {
if (current.hasMoved() && !current.getPreviousScreenPosition().isEmpty()) {
// Object moved to a new location on screen, update the previous one
- dirtyRects.push_back(current.getPreviousScreenPosition());
+ _dirtyRects.push_back(current.getPreviousScreenPosition());
}
// Redraw the current location
- dirtyRects.push_back(current.getScreenPosition());
+ _dirtyRects.push_back(current.getScreenPosition());
} else if (!current.getPreviousScreenPosition().isEmpty()) {
// Object just turned invisible, redraw the last location
- dirtyRects.push_back(current.getPreviousScreenPosition());
+ _dirtyRects.push_back(current.getPreviousScreenPosition());
}
}
@@ -87,10 +86,10 @@ void GraphicsManager::draw(bool updateScreen) {
}
// Filter out dirty rects that are completely inside others to reduce overdraw
- for (auto outer = dirtyRects.begin(); outer != dirtyRects.end(); ++outer) {
- for (auto inner = dirtyRects.begin(); inner != dirtyRects.end(); ++inner) {
+ for (auto outer = _dirtyRects.begin(); outer != _dirtyRects.end(); ++outer) {
+ for (auto inner = _dirtyRects.begin(); inner != _dirtyRects.end(); ++inner) {
if (inner != outer && (*outer).contains(*inner)) {
- dirtyRects.erase(inner);
+ _dirtyRects.erase(inner);
break;
}
}
@@ -104,7 +103,7 @@ void GraphicsManager::draw(bool updateScreen) {
continue;
}
- for (Common::Rect rect : dirtyRects) {
+ for (Common::Rect rect : _dirtyRects) {
if (rect.intersects(current.getScreenPosition())) {
blitToScreen(current, rect.findIntersectingRect(current.getScreenPosition()));
}
@@ -115,6 +114,9 @@ void GraphicsManager::draw(bool updateScreen) {
if (updateScreen) {
_screen.update();
}
+
+ // Remove all dirty rects for the next frame
+ _dirtyRects.clear();
}
void GraphicsManager::loadFonts(Common::SeekableReadStream *chunkStream) {
@@ -144,6 +146,8 @@ void GraphicsManager::addObject(RenderObject *object) {
void GraphicsManager::removeObject(RenderObject *object) {
for (auto &r : _objects) {
if (r == object) {
+ // Make sure the object gets properly cleared
+ _dirtyRects.push_back(r->getPreviousScreenPosition());
_objects.erase(&r);
break;
}
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index ddd8915d9a7..26b33419751 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -87,6 +87,8 @@ private:
Graphics::Screen _screen;
Common::Array<Font> _fonts;
+ Common::List<Common::Rect> _dirtyRects;
+
bool _isSuppressed;
};
Commit: 53a20b9002c5720242ac5f6140b8cdb416477bd9
https://github.com/scummvm/scummvm/commit/53a20b9002c5720242ac5f6140b8cdb416477bd9
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Finish implementing RaycastPuzzle
The raycasted maze puzzle in nancy3 is now completable
and nearly feature-complete. This includes proper lighting,
non-wonky movement, and sound FX.
Currently still missing are the debug features like showing
the map, enlarging the 3D viewport, moving up/down,
and ghosting through walls. Since they're not supposed to
be used by players, they were deemed nonessential and
will be implemented at a later point.
Changed paths:
engines/nancy/action/puzzle/raycastpuzzle.cpp
engines/nancy/action/puzzle/raycastpuzzle.h
diff --git a/engines/nancy/action/puzzle/raycastpuzzle.cpp b/engines/nancy/action/puzzle/raycastpuzzle.cpp
index 9e879d1fe36..769cfb498cc 100644
--- a/engines/nancy/action/puzzle/raycastpuzzle.cpp
+++ b/engines/nancy/action/puzzle/raycastpuzzle.cpp
@@ -86,7 +86,6 @@ public:
void writeDoors(uint startX, uint startY, uint themeID);
void writeLightSwitch(uint startX, uint startY, uint quadrant);
void writeExitFloorTexture(uint themeID);
- void validateMap();
Common::Array<uint32> _wallMap, _infoMap;
Common::Array<int16> _floorMap, _ceilingMap;
@@ -140,7 +139,6 @@ RaycastLevelBuilder::RaycastLevelBuilder(uint width, uint height, uint verticalH
fillWalls();
fillLocalWallAndInfo();
writeThemesAndExitFloor();
- validateMap();
}
void RaycastLevelBuilder::fillCells() {
@@ -762,6 +760,7 @@ void RaycastLevelBuilder::writeDoors(uint startX, uint startY, uint themeID) {
}
// Subtract 2 from all lightmap values when a door is added
+ // This looks extremely ugly but the original devs must've added it for a reason
byte lowWall, midWall, highWall;
lowWall = lightmapValue & 0xF;
midWall = (lightmapValue >> 4) & 0xF;
@@ -786,7 +785,7 @@ void RaycastLevelBuilder::writeDoors(uint startX, uint startY, uint themeID) {
}
}
-void RaycastLevelBuilder::writeLightSwitch(uint startX, uint startY, uint quadrant) {
+void RaycastLevelBuilder::writeLightSwitch(uint startX, uint startY, uint switchID) {
bool foundSwitchLocation = false;
for (uint checkedCells = 0; checkedCells < _fullNumCells && !foundSwitchLocation; ++checkedCells) {
@@ -798,7 +797,7 @@ void RaycastLevelBuilder::writeLightSwitch(uint startX, uint startY, uint quadra
}
if (foundSwitchLocation) {
- _infoMap[y * _fullWidth + x] = (quadrant << 8) | 2;
+ _infoMap[y * _fullWidth + x] = (switchID << 8) | 2;
uint lightmapValue = _floorCeilingLightMap[y * _fullWidth + x];
@@ -844,20 +843,27 @@ void RaycastLevelBuilder::writeExitFloorTexture(uint themeID) {
}
}
-void RaycastLevelBuilder::validateMap() {
- for (uint y = 0; y < _fullHeight; ++y) {
- for (uint x = 0; x < _fullWidth; ++x) {
- if (_wallMap[y * _fullWidth + x] == 1) {
+void RaycastPuzzle::validateMap() {
+ for (uint y = 0; y < _mapFullHeight; ++y) {
+ for (uint x = 0; x < _mapFullWidth; ++x) {
+ if (_wallMap[y * _mapFullWidth + x] == 1) {
error("wallMap not complete at coordinates x = %d, y = %d", x, y);
}
- if (_floorMap[y * _fullWidth + x] == -1) {
+ if (_floorMap[y * _mapFullWidth + x] == -1) {
error("floorMap not complete at coordinates x = %d, y = %d", x, y);
}
- if (_ceilingMap[y * _fullWidth + x] == -1) {
+ if (_ceilingMap[y * _mapFullWidth + x] == -1) {
error("wallMap not complete at coordinates x = %d, y = %d", x, y);
}
+
+ // Find light switches
+ if ((_infoMap[y * _mapFullWidth + x] & 0xFF) == 2) {
+ _lightSwitchIDs.push_back((_infoMap[y * _mapFullWidth + x] >> 8) & 0xFF);
+ _lightSwitchPositions.push_back(Common::Point(x, y));
+ _lightSwitchStates.push_back(false);
+ }
}
}
}
@@ -877,7 +883,7 @@ public:
private:
bool loadInner() override;
- enum State { kInitDrawSurface, kCopyData, kInitMap, kInitTables1, kInitTables2, kLoadTextures };
+ enum State { kInitDrawSurface, kInitPlayerLocationRotation, kCopyData, kInitMap, kInitTables1, kInitTables2, kLoadTextures };
State _loadState;
@@ -898,9 +904,31 @@ bool RaycastDeferredLoader::loadInner() {
_owner._drawSurface.create(viewport.width(), viewport.height(), g_nancy->_graphicsManager->getInputPixelFormat());
_owner.setTransparent(true);
- _loadState = kCopyData;
+ _loadState = kInitPlayerLocationRotation;
break;
}
+ case kInitPlayerLocationRotation :
+ if ( _builder._wallMap[_builder._startY * _builder._fullWidth + _builder._startX + 1] == 0 &&
+ _builder._wallMap[_builder._startY * _builder._fullWidth + _builder._startX + 2] == 0) {
+ _owner._playerRotation = 0;
+ } else if ( _builder._wallMap[(_builder._startY - 1) * _builder._fullWidth + _builder._startX] == 0 &&
+ _builder._wallMap[(_builder._startY - 2) * _builder._fullWidth + _builder._startX] == 0) {
+ _owner._playerRotation = 1024;
+ } else if ( _builder._wallMap[_builder._startY * _builder._fullWidth + _builder._startX - 1] == 0 &&
+ _builder._wallMap[_builder._startY * _builder._fullWidth + _builder._startX - 2] == 0) {
+ _owner._playerRotation = 2048;
+ } else if ( _builder._wallMap[(_builder._startY + 1) * _builder._fullWidth + _builder._startX] == 0 &&
+ _builder._wallMap[(_builder._startY + 2) * _builder._fullWidth + _builder._startX] == 0) {
+ _owner._playerRotation = 3072;
+ } else {
+ _owner._playerRotation = 512;
+ }
+
+ _owner._playerX = _builder._startX * 128 + 64;
+ _owner._playerY = _builder._startY * 128 + 64;
+
+ _loadState = kCopyData;
+ break;
case kCopyData :
_owner._wallMap.swap(_builder._wallMap);
_owner._infoMap.swap(_builder._infoMap);
@@ -909,24 +937,17 @@ bool RaycastDeferredLoader::loadInner() {
_owner._heightMap.swap(_builder._heightMap);
_owner._wallLightMap.swap(_builder._wallLightMap);
_owner._floorCeilingLightMap.swap(_builder._floorCeilingLightMap);
+ _owner._wallLightMapBackup = _owner._wallLightMap;
+ _owner._floorCeilingLightMapBackup = _owner._floorCeilingLightMap;
_owner._mapFullWidth = _builder._fullWidth;
_owner._mapFullHeight = _builder._fullHeight;
_loadState = kInitMap;
break;
case kInitMap : {
- // TODO map is a debug feature, make sure to hide it
- // Also, fix the fact that it's rendered upside-down
- const BSUM *bootSummary = (const BSUM *)g_nancy->getEngineData("BSUM");
- assert(bootSummary);
-
- _owner._map._drawSurface.create(_owner._mapFullWidth, _owner._mapFullHeight, g_nancy->_graphicsManager->getInputPixelFormat());
- Common::Rect mapPos(bootSummary->textboxScreenPosition);
- mapPos.setWidth(_owner._mapFullWidth * 2);
- mapPos.setHeight(_owner._mapFullHeight * 2);
- _owner._map.moveTo(mapPos);
- _owner._map.init();
_owner.drawMap();
+ // TODO: Add console command for setting debug features (map, player height, screen size, ghost mode, etc.)
+ _owner._map.setVisible(false);
_loadState = kInitTables1;
break;
@@ -1032,10 +1053,7 @@ bool RaycastDeferredLoader::loadInner() {
}
}
- // TODO these need to be set according to the start position in _infoMap
- _owner._playerRotation = 2048;
- _owner._playerX = _owner._playerY = 320;
-
+ _owner.validateMap();
_isDone = true;
}
@@ -1092,16 +1110,28 @@ void RaycastPuzzle::execute() {
init();
break;
case kRun:
- // TODO check light switches
- // TODO check exit
+ checkSwitch();
+ checkExit();
break;
case kActionTrigger:
+ if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+ return;
+ }
+
+ g_nancy->_sound->stopSound(_solveSound);
+ g_nancy->_sound->stopSound(_dummySound);
+ _solveScene.execute();
+ finishExecution();
break;
}
}
void RaycastPuzzle::handleInput(NancyInput &input) {
+ if (_state != kRun) {
+ return;
+ }
+
uint32 time = g_nancy->getTotalPlayTime();
uint32 deltaTime = time - _lastMovementTime;
_lastMovementTime = time;
@@ -1152,24 +1182,24 @@ void RaycastPuzzle::handleInput(NancyInput &input) {
clampRotation(_playerRotation);
- int32 newX = _playerX;
- int32 newY = _playerY;
+ float newX = _playerX;
+ float newY = _playerY;
bool hasMoved = false;
bool hasMovedSlowdown = false;
// Move forward/backwards
-
- // Invert the rotation to avoid some annoying precision errors
- int32 invertedRotation = 4095 - (_playerRotation + 2048);
- clampRotation(invertedRotation);
+ // Improvement: we do _not_ use the sin/cos tables since they produce wonky movement
+ float fRotation = (float)(4095 - _playerRotation) / 4096.0;
+ float dX = sin(fRotation * _pi * 2) * deltaPosition;
+ float dY = cos(fRotation * _pi * 2) * deltaPosition;
if (input.input & NancyInput::kMoveUp || (input.input & NancyInput::kLeftMouseButtonHeld && mouseIsInBounds)) {
if (input.input & NancyInput::kMoveFastModifier) {
- newX = _playerX - (int32)(_sinTable[invertedRotation] * deltaPosition) * 2;
- newY = _playerY - (int32)(_cosTable[invertedRotation] * deltaPosition) * 2;
+ newX = _playerX + dX * 2;
+ newY = _playerY + dY * 2;
} else {
- newX = _playerX - (int32)(_sinTable[invertedRotation] * deltaPosition);
- newY = _playerY - (int32)(_cosTable[invertedRotation] * deltaPosition);
+ newX = _playerX + dX;
+ newY = _playerY + dY;
}
hasMoved = true;
@@ -1177,17 +1207,19 @@ void RaycastPuzzle::handleInput(NancyInput &input) {
if (input.input & NancyInput::kMoveDown || (input.input & NancyInput::kRightMouseButtonHeld && mouseIsInBounds)) {
if (input.input & NancyInput::kMoveFastModifier) {
- newX = _playerX + (int32)(_sinTable[invertedRotation] * deltaPosition) * 2;
- newY = _playerY + (int32)(_cosTable[invertedRotation] * deltaPosition) * 2;
+ newX = _playerX - dX * 2;
+ newY = _playerY - dY * 2;
} else {
- newX = _playerX + (int32)(_sinTable[invertedRotation] * deltaPosition);
- newY = _playerY + (int32)(_cosTable[invertedRotation] * deltaPosition);
+ newX = _playerX - dX;
+ newY = _playerY - dY;
}
hasMoved = true;
}
// Perform slowdown
+ // Improvement: the original engine's slowdown is VERY buggy
+ // and almost never actually works
if (!hasMoved && _nextSlowdownMovementTime < time && _slowdownFramesLeft > 0) {
_slowdownDeltaX = (float)_slowdownDeltaX * 9.0 / 10.0;
_slowdownDeltaY = (float)_slowdownDeltaY * 9.0 / 10.0;
@@ -1201,8 +1233,13 @@ void RaycastPuzzle::handleInput(NancyInput &input) {
// Perform collision
if (hasMoved) {
- uint yGrid = newX >> 7;
- uint xGrid = newY >> 7;
+ uint yGrid = ((int32)newX) >> 7;
+ uint xGrid = ((int32)newY) >> 7;
+
+ int32 xCell = ((int32)newX) & 0x7F;
+ int32 yCell = ((int32)newY) & 0x7F;
+
+ int collisionSize = 48;
// Check neighboring cells
uint32 cellLeft = xGrid > 0 ? _wallMap[yGrid * _mapFullWidth + xGrid - 1] : 1;
@@ -1216,31 +1253,26 @@ void RaycastPuzzle::handleInput(NancyInput &input) {
cellRight = (cellRight & kDoor) ? 0 : cellRight;
cellBottom = (cellBottom & kDoor) ? 0 : cellBottom;
- int collisionSize = 48;
-
- int32 xCell = newX & 0x7F;
- int32 yCell = newY & 0x7F;
-
if (cellLeft && yCell < collisionSize) {
- newY = (newY & 0xFF80) + collisionSize;
+ newY = (((int32)newY) & 0xFF80) + collisionSize;
}
if (cellTop && xCell < collisionSize) {
- newX = (newX & 0xFF80) + collisionSize;
+ newX = (((int32)newX) & 0xFF80) + collisionSize;
}
if (cellRight && yCell > (128 - collisionSize)) {
- newY = (newY & 0xFF80) + (128 - collisionSize);
+ newY = (((int32)newY) & 0xFF80) + (128 - collisionSize);
}
if (cellBottom && xCell > (128 - collisionSize)) {
- newX = (newX & 0xFF80) + (128 - collisionSize);
+ newX = (((int32)newX) & 0xFF80) + (128 - collisionSize);
}
- yGrid = newX >> 7;
- xGrid = newY >> 7;
- yCell = newX & 0x7F;
- xCell = newY & 0x7F;
+ yGrid = ((int32)newX) >> 7;
+ xGrid = ((int32)newY) >> 7;
+ yCell = ((int32)newX) & 0x7F;
+ xCell = ((int32)newY) & 0x7F;
cellLeft = xGrid > 0 ? _wallMap[yGrid * _mapFullWidth + xGrid - 1] : 1;
cellTop = yGrid > 0 ? _wallMap[(yGrid - 1) * _mapFullWidth + xGrid] : 1;
@@ -1262,24 +1294,39 @@ void RaycastPuzzle::handleInput(NancyInput &input) {
cellBottomLeft = (cellBottomLeft & kDoor) ? 0 : cellBottomLeft;
cellBottomRight = (cellBottomRight & kDoor) ? 0 : cellBottomRight;
- collisionSize = 21;
-
// Make sure the player doesn't clip diagonally into a wall
- // TODO this is still wonky
+ // Improvement: in the original engine the player just gets stuck when hitting a corner;
+ // instead, we move along smoothly
if (cellTopLeft && !cellLeft && !cellTop && (yCell < collisionSize) && (xCell < collisionSize)) {
- return;
+ if (yCell > xCell) {
+ newX = (((int32)newX) & 0xFF80) + collisionSize;
+ } else {
+ newY = (((int32)newY) & 0xFF80) + collisionSize;
+ }
}
if (cellTopRight && !cellRight && !cellTop && (yCell < collisionSize) && (xCell > (128 - collisionSize))) {
- return;
+ if (yCell > (128 - xCell)) {
+ newX = (((int32)newX) & 0xFF80) + collisionSize;
+ } else {
+ newY = (((int32)newY) & 0xFF80) + (128 - collisionSize);
+ }
}
if (cellBottomLeft && !cellLeft && !cellBottom && (yCell > (128 - collisionSize)) && (xCell < collisionSize)) {
- return;
+ if (128 - yCell > xCell) {
+ newX = (((int32)newX) & 0xFF80) + (128 - collisionSize);
+ } else {
+ newY = (((int32)newY) & 0xFF80) + collisionSize;
+ }
}
if (cellBottomRight && !cellRight && !cellBottom && (yCell > (128 - collisionSize)) && (xCell > (128 - collisionSize))) {
- return;
+ if (128 - yCell > 128 - xCell) {
+ newX = (((int32)newX) & 0xFF80) + (128 - collisionSize);
+ } else {
+ newY = (((int32)newY) & 0xFF80) + (128 - collisionSize);
+ }
}
if (!hasMovedSlowdown) {
@@ -1291,22 +1338,31 @@ void RaycastPuzzle::handleInput(NancyInput &input) {
_playerX = newX;
_playerY = newY;
-
- debug("x = %u, y = %u", _playerX, _playerY);
}
}
void RaycastPuzzle::updateGraphics() {
if (_state == kRun) {
drawMaze();
+ updateMap();
}
}
void RaycastPuzzle::drawMap() {
- _map._drawSurface.clear();
+ // Improvement: the original map is drawn upside-down; ours isn't
+ const BSUM *bootSummary = (const BSUM *)g_nancy->getEngineData("BSUM");
+ assert(bootSummary);
+
+ _mapBaseSurface.create(_mapFullWidth, _mapFullHeight, g_nancy->_graphicsManager->getInputPixelFormat());
+ _map._drawSurface.create(_mapFullWidth, _mapFullHeight, g_nancy->_graphicsManager->getInputPixelFormat());
+ Common::Rect mapPos(bootSummary->textboxScreenPosition);
+ mapPos.setWidth(_mapFullWidth * 2);
+ mapPos.setHeight(_mapFullHeight * 2);
+ _map.moveTo(mapPos);
+ _map.init();
uint16 *pixelPtr;
- Graphics::PixelFormat &format = _map._drawSurface.format;
+ Graphics::PixelFormat &format = _mapBaseSurface.format;
uint16 wallColor = format.RGBToColor(_puzzleData->wallColor[0], _puzzleData->wallColor[1], _puzzleData->wallColor[2]);
uint16 uColor6 = format.RGBToColor(_puzzleData->uColor6[0], _puzzleData->uColor6[1], _puzzleData->uColor6[2]);
@@ -1319,7 +1375,7 @@ void RaycastPuzzle::drawMap() {
uint16 exitColor = format.RGBToColor(_puzzleData->exitColor[0], _puzzleData->exitColor[1], _puzzleData->exitColor[2]);
for (uint y = 0; y < _mapFullHeight; ++y) {
- pixelPtr = (uint16 *)_map._drawSurface.getBasePtr(0, y);
+ pixelPtr = (uint16 *)_mapBaseSurface.getBasePtr(0, _mapFullHeight - y - 1);
for (uint x = 0; x < _mapFullWidth; ++x) {
uint32 wallMapCell = _wallMap[y * _mapFullHeight + x];
uint32 infoMapCell = _infoMap[y * _mapFullHeight + x];
@@ -1362,19 +1418,25 @@ void RaycastPuzzle::drawMap() {
++pixelPtr;
}
}
-
- _map.setVisible(true);
}
-void RaycastPuzzle::loadTextures() {
- // TODO this is slow and freezes the engine for a few seconds
-
+void RaycastPuzzle::updateMap() {
+ if (_map.isVisible()) {
+ _map._drawSurface.blitFrom(_mapBaseSurface);
+ Graphics::PixelFormat &format = _mapBaseSurface.format;
+ uint16 playerColor = format.RGBToColor(_puzzleData->playerColor[0], _puzzleData->playerColor[1], _puzzleData->playerColor[2]);
+ _map._drawSurface.setPixel((((uint)_playerY) >> 7), _mapFullWidth - 1 - (((uint)_playerX) >> 7), playerColor);
+
+ _map.setVisible(true);
+ }
}
void RaycastPuzzle::createTextureLightSourcing(Common::Array<Graphics::ManagedSurface> *array, const Common::String &textureName) {
Graphics::PixelFormat format = g_nancy->_graphicsManager->getInputPixelFormat();
array->resize(8);
+ uint16 transColor = g_nancy->_graphicsManager->getTransColor();
+
g_nancy->_resource->loadImage(textureName, (*array)[0]);
uint width = (*array)[0].w;
@@ -1389,17 +1451,26 @@ void RaycastPuzzle::createTextureLightSourcing(Common::Array<Graphics::ManagedSu
for (uint y = 0; y < height; ++y) {
for (uint x = 0; x < width; ++x) {
uint offset = y * width + x;
- byte r, g, b, rStep, gStep, bStep;
- format.colorToRGB(((uint16 *)(*array)[0].getPixels())[offset], r, g, b);
- rStep = (float)r / 8.0 * 65536.0;
- gStep = (float)g / 8.0 * 65536.0;
- bStep = (float)b / 8.0 * 65536.0;
- for (uint i = 1; i < 8; ++i) {
- r -= rStep;
- g -= gStep;
- b -= bStep;
- ((uint16 *)(*array)[i].getPixels())[offset] = format.RGBToColor(r, g, b);
+ uint16 color = ((uint16 *)(*array)[0].getPixels())[offset];
+ if (color == transColor) {
+ for (uint i = 1; i < 8; ++i) {
+ // Do not darken transparent color
+ ((uint16 *)(*array)[i].getPixels())[offset] = color;
+ }
+ } else {
+ byte r, g, b, rStep, gStep, bStep;
+ format.colorToRGB(color, r, g, b);
+ rStep = (float)r / 8.0;
+ gStep = (float)g / 8.0;
+ bStep = (float)b / 8.0;
+ for (uint i = 1; i < 8; ++i) {
+ r -= rStep;
+ g -= gStep;
+ b -= bStep;
+ ((uint16 *)(*array)[i].getPixels())[offset] = format.RGBToColor(r, g, b);
+ }
}
+
}
}
}
@@ -1416,7 +1487,7 @@ void RaycastPuzzle::drawMaze() {
_drawSurface.clear(_drawSurface.getTransparentColor());
// Draw walls
- for (int x = viewBounds.left; x <= viewBounds.right; ++x) {
+ for (int x = viewBounds.left; x < viewBounds.right; ++x) {
int32 columnAngleForX = _wallCastColumnAngles[x];
int32 rotatedColumnAngleForX = columnAngleForX + _playerRotation;
clampRotation(rotatedColumnAngleForX);
@@ -1481,7 +1552,7 @@ void RaycastPuzzle::drawMaze() {
Common::Point yGrid(-1, -1);
if (xDist < _maxWorldDistance) {
- xGrid.y = (int)xRayX >> 7;
+ xGrid.y = ((int)xRayX) >> 7;
xGrid.x = (int)(xRayY / 128.0);
if (xGrid.x < _mapFullWidth && xGrid.y < _mapFullHeight && xGrid.x >= 0 && xGrid.y >= 0) {
@@ -1491,7 +1562,7 @@ void RaycastPuzzle::drawMaze() {
if (yDist < _maxWorldDistance) {
yGrid.y = (int)(yRayX / 128.0);
- yGrid.x = (int)yRayY >> 7;
+ yGrid.x = ((int)yRayY) >> 7;
if (yGrid.x < _mapFullWidth && yGrid.y < _mapFullHeight && yGrid.x >= 0 && yGrid.y >= 0) {
yGridTile = _wallMap[yGrid.y * _mapFullWidth + yGrid.x];
@@ -1685,7 +1756,7 @@ void RaycastPuzzle::drawMaze() {
if (drawnWallHeight > 1) {
uint16 *destPixel = (uint16 *)_drawSurface.getBasePtr(x, drawnWallBottom);
byte *zBufferDestPixel = &_zBuffer[drawnWallBottom * _drawSurface.w + x];
- byte baseLightVal = MIN<byte>(_depthBuffer[x] / 128, 7);
+ byte baseLightVal = MIN<byte>(_depthBuffer[x] / 768, 7);
uint srcYSubtractVal = (uint)(((float)numSrcPixelsToDraw / (float)drawnWallHeight) * 65536.0);
srcY <<= 16;
@@ -1844,7 +1915,6 @@ void RaycastPuzzle::drawMaze() {
}
// Draw floors and ceilings
- // TODO exit does not get rendered correctly
int32 leftAngle = _playerRotation + _leftmostAngle;
int32 rightAngle = _playerRotation + _rightmostAngle;
@@ -1890,7 +1960,7 @@ void RaycastPuzzle::drawMaze() {
for (int x = viewBounds.left; x < viewBounds.right; ++x) {
if (_zBuffer[floorY * _drawSurface.w + x] != curZBufferDepth) {
- byte offset = (floorSrcFracY >> 23) * _mapFullWidth + (floorSrcFracX >> 23);
+ uint16 offset = (floorSrcFracY >> 23) * _mapFullWidth + (floorSrcFracX >> 23);
*floorDest = *(int16 *)_floorTextures[_floorMap[offset]][_floorCeilingLightMap[offset] & 0xF].getBasePtr((floorSrcFracX >> 16) & 0x7F, (floorSrcFracY >> 16) & 0x7F);
}
@@ -1899,7 +1969,7 @@ void RaycastPuzzle::drawMaze() {
floorSrcFracY += floorSrcIncrementY;
if (_zBuffer[ceilingY * _drawSurface.w + x] != curZBufferDepth) {
- byte offset = (ceilingSrcFracY >> 23) * _mapFullWidth + (ceilingSrcFracX >> 23);
+ uint16 offset = (ceilingSrcFracY >> 23) * _mapFullWidth + (ceilingSrcFracX >> 23);
*ceilingDest = *(int16 *)_ceilingTextures[_ceilingMap[offset]][(_floorCeilingLightMap[offset] >> 4) & 0xF].getBasePtr((ceilingSrcFracX >> 16) & 0x7F, (ceilingSrcFracY >> 16) & 0x7F);
}
@@ -1926,7 +1996,73 @@ void RaycastPuzzle::clearZBuffer() {
}
void RaycastPuzzle::checkSwitch() {
+ // X/Y swapping intentional. The axes get mixed up somewhere between level generation
+ // and running and I'm not really sure where
+ Common::Point gridPos(((uint)_playerY) >> 7, ((uint)_playerX) >> 7);
+
+ for (int i = 0; i < (int)_lightSwitchPositions.size(); ++i) {
+ if (_lightSwitchPositions[i] == gridPos) {
+ if (_lightSwitchPlayerIsOn != i) {
+ // Player just stepped on light switch
+ _lightSwitchPlayerIsOn = i;
+
+ if (_lightSwitchStates[i] == false) {
+ // Switch was unpressed, press and turn light ON
+ for (uint y = 0; y < _mapFullHeight; ++y) {
+ for (uint x = 0; x < _mapFullWidth; ++x) {
+ if ((_wallLightMap[y * _mapFullWidth + x] >> 0xC) == _lightSwitchIDs[i]) {
+ _wallLightMap[y * _mapFullWidth + x] &= 0xF000;
+ }
+
+ if ((_floorCeilingLightMap[y * _mapFullWidth + x] >> 0xC) == _lightSwitchIDs[i]) {
+ _floorCeilingLightMap[y * _mapFullWidth + x] &= 0xF000;
+ }
+ }
+ }
+
+ _dummySound.name = _switchSoundName;
+ _dummySound.channelID = _switchSoundChannelID;
+ g_nancy->_sound->loadSound(_dummySound);
+ g_nancy->_sound->playSound(_dummySound);
+ _lightSwitchStates[i] = true;
+ } else {
+ // Switch was pressed, unpress and turn light OFF
+ for (uint y = 0; y < _mapFullHeight; ++y) {
+ for (uint x = 0; x < _mapFullWidth; ++x) {
+ if ((_wallLightMap[y * _mapFullWidth + x] >> 0xC) == _lightSwitchIDs[i]) {
+ _wallLightMap[y * _mapFullWidth + x] = _wallLightMapBackup[y * _mapFullWidth + x];
+ }
+
+ if ((_floorCeilingLightMap[y * _mapFullWidth + x] >> 0xC) == _lightSwitchIDs[i]) {
+ _floorCeilingLightMap[y * _mapFullWidth + x] = _floorCeilingLightMapBackup[y * _mapFullWidth + x];
+ }
+ }
+ }
+
+ _lightSwitchStates[i] = false;
+ }
+ }
+ } else {
+ if (_lightSwitchPlayerIsOn == i) {
+ // Player just stepped off light switch
+
+ _lightSwitchPlayerIsOn = -1;
+ }
+ }
+ }
+}
+
+void RaycastPuzzle::checkExit() {
+ // X/Y swapping intentional; see above
+ Common::Point gridPos(((uint)_playerY) >> 7, ((uint)_playerX) >> 7);
+
+ if (_infoMap[gridPos.y * _mapFullWidth + gridPos.x] == 1) {
+ g_nancy->_sound->loadSound(_solveSound);
+ g_nancy->_sound->playSound(_solveSound);
+
+ _state = kActionTrigger;
+ }
}
} // End of namespace Action
diff --git a/engines/nancy/action/puzzle/raycastpuzzle.h b/engines/nancy/action/puzzle/raycastpuzzle.h
index 6b6af1ccdba..9ab2bc67830 100644
--- a/engines/nancy/action/puzzle/raycastpuzzle.h
+++ b/engines/nancy/action/puzzle/raycastpuzzle.h
@@ -53,14 +53,17 @@ protected:
Common::String getRecordTypeName() const override { return "RaycastPuzzle"; }
bool isViewportRelative() const override { return true; }
- void loadTextures();
+ void validateMap();
+
void createTextureLightSourcing(Common::Array<Graphics::ManagedSurface> *array, const Common::String &textureName);
void drawMap();
+ void updateMap();
void drawMaze();
void clearZBuffer();
void checkSwitch();
+ void checkExit();
uint16 _mapWidth = 0;
uint16 _mapHeight = 0;
@@ -78,11 +81,13 @@ protected:
Common::Array<uint32> _wallMap, _infoMap;
Common::Array<int16> _floorMap, _ceilingMap;
Common::Array<uint16> _wallLightMap, _floorCeilingLightMap, _heightMap;
+ Common::Array<uint16> _wallLightMapBackup, _floorCeilingLightMapBackup;
uint16 _mapFullWidth = 0;
uint16 _mapFullHeight = 0;
RenderObject _map;
+ Graphics::ManagedSurface _mapBaseSurface;
double _pi = 3.141592653589793;
uint _fov = 192;
@@ -101,8 +106,9 @@ protected:
int32 _leftmostAngle = -1;
int32 _rightmostAngle = -1;
- int32 _playerX = -1; // Player position with precision 1/128th of cell width/height
- int32 _playerY = -1;
+ // Improvement: we store position as float for smoother movement
+ float _playerX = -1; // Player position with precision 1/128th of cell width/height
+ float _playerY = -1;
int32 _playerRotation = 0; // Rotation of player (0 - 4096)
uint32 _playerAltitude = 88; // Z position of "camera"; only modified in god mode
@@ -117,6 +123,11 @@ protected:
int32 _slowdownDeltaX = -1;
int32 _slowdownDeltaY = -1;
+ Common::Array<byte> _lightSwitchIDs;
+ Common::Array<Common::Point> _lightSwitchPositions;
+ Common::Array<bool> _lightSwitchStates;
+ int _lightSwitchPlayerIsOn = -1;
+
const RCPR *_puzzleData = nullptr;
Common::SharedPtr<RaycastDeferredLoader> _loaderPtr;
};
Commit: 78522c9122b2c823ffa765c02c655188bcc0dd9c
https://github.com/scummvm/scummvm/commit/78522c9122b2c823ffa765c02c655188bcc0dd9c
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-14T22:54:17+03:00
Commit Message:
NANCY: Silence compiler warning
Changed paths:
engines/nancy/action/puzzle/orderingpuzzle.cpp
diff --git a/engines/nancy/action/puzzle/orderingpuzzle.cpp b/engines/nancy/action/puzzle/orderingpuzzle.cpp
index 016869569b3..3bb6f021bd3 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/orderingpuzzle.cpp
@@ -75,7 +75,7 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
Common::Serializer ser(&stream, nullptr);
ser.setVersion(g_nancy->getGameType());
- uint16 numElements;
+ uint16 numElements = 5;
uint16 maxNumElements = 15;
if (ser.getVersion() == kGameTypeVampire) {
// Hardcoded in The Vampire Diaries
More information about the Scummvm-git-logs
mailing list