[Scummvm-git-logs] scummvm master -> c6164fcf4a2063cced828d930df96e8e1dc6cc43

fracturehill noreply at scummvm.org
Sun May 7 08:52:51 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:
bd6023aba0 NANCY: Remove unnecessary forward declaration
932e93d714 NANCY: Implement BombPuzzle
13f61cf882 NANCY: Correct time of day for nancy2 and up
b7637ec92e NANCY: Respect ID offsets for nancy3 flags
ac81086665 NANCY: Correct dependency reading for nancy3
b1749a04e2 NANCY: Fix saving of riddle puzzle state
c6164fcf4a NANCY: Improve puzzle data storage and saving


Commit: bd6023aba00b710ea21f691f281e5e1c2b4113eb
    https://github.com/scummvm/scummvm/commit/bd6023aba00b710ea21f691f281e5e1c2b4113eb
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:29+03:00

Commit Message:
NANCY: Remove unnecessary forward declaration

Changed paths:
    engines/nancy/action/sliderpuzzle.h


diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 58115413c9f..0583903637c 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -24,10 +24,6 @@
 
 #include "engines/nancy/action/actionrecord.h"
 
-namespace Common {
-class Serializer;
-}
-
 namespace Nancy {
 
 struct SPUZ;


Commit: 932e93d7141fbea2a57a0c4013f63c11a5d5c287
    https://github.com/scummvm/scummvm/commit/932e93d7141fbea2a57a0c4013f63c11a5d5c287
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:32+03:00

Commit Message:
NANCY: Implement BombPuzzle

Changed paths:
  A engines/nancy/action/bombpuzzle.cpp
  A engines/nancy/action/bombpuzzle.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/module.mk
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index da76380e44d..0af8f4beb8f 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -34,6 +34,7 @@
 #include "engines/nancy/action/towerpuzzle.h"
 #include "engines/nancy/action/riddlepuzzle.h"
 #include "engines/nancy/action/overridelockpuzzle.h"
+#include "engines/nancy/action/bombpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -149,6 +150,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 		return new HintSystem();
 	case 201:
 		return new TowerPuzzle();
+	case 202:
+		return new BombPuzzle();
 	case 203:
 		return new RippedLetterPuzzle();
 	case 204:
diff --git a/engines/nancy/action/bombpuzzle.cpp b/engines/nancy/action/bombpuzzle.cpp
new file mode 100644
index 00000000000..7f27504ed0f
--- /dev/null
+++ b/engines/nancy/action/bombpuzzle.cpp
@@ -0,0 +1,275 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/action/bombpuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+void BombPuzzle::init() {
+	_screenPosition = _displayBounds;
+	for (Common::Rect &r : _wireDests) {
+		_screenPosition.extend(r);
+	}
+
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
+
+	setTransparent(true);
+
+	g_nancy->_resource->loadImage(_imageName, _image);
+	RenderActionRecord::init();
+}
+
+void BombPuzzle::readData(Common::SeekableReadStream &stream) {
+	readFilename(stream, _imageName);
+
+	readRectArray(stream, _wireSrcs, 4);
+	readRectArray(stream, _wireDests, 4);
+	readRectArray(stream, _digitSrcs, 10);
+	readRectArray(stream, _digitDests, 4);
+	readRect(stream, _colonSrc);
+	readRect(stream, _colonDest);
+	readRect(stream, _displayBounds);
+
+	_solveOrder.resize(4);
+	for (uint i = 0; i < 4; ++i) {
+		_solveOrder[i] = stream.readByte();
+	}
+
+	_snipSound.readData(stream, SoundDescription::kNormal);
+	_noToolSound.readData(stream, SoundDescription::kNormal);
+	_toolID = stream.readUint16LE();
+
+	_solveSceneChange.readData(stream);
+	stream.skip(2);
+	_solveSound.readData(stream, SoundDescription::kNormal);
+
+	_failSceneChange.readData(stream);
+	stream.skip(2);
+	_failSound.readData(stream, SoundDescription::kNormal);
+
+	switch (NancySceneState.getDifficulty()) {
+		case 0:
+			_timerTotalTime = 30 * 1000;
+			break;
+		case 1:
+			_timerTotalTime = 25 * 1000;
+			break;
+		case 2:
+			_timerTotalTime = 20 * 1000;
+			break;
+	}
+
+	_nextBlinkTime = _timerTotalTime;
+	_timerBlinkTime = 10 * 1000; // 10 seconds for all difficulties
+}
+
+void BombPuzzle::updateGraphics() {
+	if (_state != kRun) {
+		return;
+	}
+
+	Time timeRemaining = NancySceneState.getTimerTime();
+
+	if (timeRemaining == 0) {
+		return;
+	}
+
+	if (timeRemaining > _timerTotalTime) {
+		timeRemaining = 0;
+	} else {
+		timeRemaining = _timerTotalTime - timeRemaining;
+	}
+
+	bool toggleBlink = false;
+
+	if (timeRemaining < _nextBlinkTime) {
+		_nextBlinkTime = timeRemaining - 300; // hardcoded to 300 ms
+		toggleBlink = timeRemaining < _timerBlinkTime;
+	}
+
+	if (_lastDrawnTime == timeRemaining.getSeconds() && !toggleBlink) {
+		// State is the same as last call, do not redraw
+		return;
+	}
+
+	Common::Rect t = _displayBounds;
+	t.translate(-_screenPosition.left, -_screenPosition.top);
+
+	_lastDrawnTime = timeRemaining.getSeconds();
+
+	// Clear the display
+	_drawSurface.fillRect(t, _drawSurface.getTransparentColor());
+
+	if (toggleBlink) {
+		if (!_isBlinking) {
+			// Only clear the display
+			_isBlinking = true;
+			_needsRedraw = true;
+			return;
+		} else {
+			// Redraw the display
+			_isBlinking = false;
+		}
+	} else {
+		if (_isBlinking) {
+			// Only clear the display
+			_needsRedraw = true;
+			return;
+		}
+	}
+
+	// Add 1 second to timer so it starts at 30/25/20 seconds
+	timeRemaining += 1000;
+
+	// Draw 10s of minutes
+	t = _digitDests[0];
+	t.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawSurface.blitFrom(_image, _digitSrcs[timeRemaining.getMinutes() / 10], t);
+
+	// Draw 1s of minutes
+	t = _digitDests[1];
+	t.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawSurface.blitFrom(_image, _digitSrcs[timeRemaining.getMinutes() % 10], t);
+
+	// Draw 10s of seconds
+	t = _digitDests[2];
+	t.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawSurface.blitFrom(_image, _digitSrcs[timeRemaining.getSeconds() / 10], t);
+
+	// Draw 1s of seconds
+	t = _digitDests[3];
+	t.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawSurface.blitFrom(_image, _digitSrcs[timeRemaining.getSeconds() % 10], t);
+
+	// Draw colon
+	t = _colonDest;
+	t.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawSurface.blitFrom(_image, _colonSrc, t);
+
+	_needsRedraw = true;
+}
+
+void BombPuzzle::execute() {
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+
+		g_nancy->_sound->loadSound(_snipSound);
+		g_nancy->_sound->loadSound(_noToolSound);
+
+		_state = kRun;
+		break;
+	case kRun: {
+		bool fail = false;
+
+		for (uint i = 0; i < _playerOrder.size(); ++i) {
+			if (_playerOrder[i] != _solveOrder[i]) {
+				fail = true;
+				break;
+			}
+		}
+
+		if (fail) {
+			_failed = true;
+			_state = kActionTrigger;
+			g_nancy->_sound->loadSound(_failSound);
+			g_nancy->_sound->playSound(_failSound);
+
+			return;
+		}
+
+		if (_playerOrder.size() == _solveOrder.size()) {
+			_failed = false;
+			_state = kActionTrigger;
+			g_nancy->_sound->loadSound(_solveSound);
+			g_nancy->_sound->playSound(_solveSound);
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		if (_failed) {
+			if (g_nancy->_sound->isSoundPlaying(_failSound)) {
+				return;
+			}
+
+			g_nancy->_sound->stopSound(_failSound);
+			_failSceneChange.execute();
+		} else {
+			if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				return;
+			}
+
+			g_nancy->_sound->stopSound(_solveSound);
+			_solveSceneChange.execute();
+		}
+
+		g_nancy->_sound->stopSound(_snipSound);
+		g_nancy->_sound->stopSound(_noToolSound);
+
+		finishExecution();
+	}
+}
+
+void BombPuzzle::handleInput(NancyInput &input) {
+	for (uint i = 0 ; i < _wireDests.size(); ++i) {
+		if (NancySceneState.getViewport().convertViewportToScreen(_wireDests[i]).contains(input.mousePos)) {
+			for (byte j : _playerOrder) {
+				if (i == j) {
+					// Wire already snipped, do nothing
+					return;
+				}
+			}
+
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				if (NancySceneState.getHeldItem() == _toolID) {
+					_playerOrder.push_back(i);
+					g_nancy->_sound->playSound(_snipSound);
+					Common::Rect dest = _wireDests[i];
+					dest.translate(-_screenPosition.left, -_screenPosition.top);
+					_drawSurface.blitFrom(_image, _wireSrcs[i], dest);
+					_needsRedraw = true;
+				} else {
+					g_nancy->_sound->playSound(_noToolSound);
+				}
+			}
+
+			break;
+		}
+	}
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/bombpuzzle.h b/engines/nancy/action/bombpuzzle.h
new file mode 100644
index 00000000000..a0b2a6d52a9
--- /dev/null
+++ b/engines/nancy/action/bombpuzzle.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 NANCY_ACTION_BOMBPUZZLE_H
+#define NANCY_ACTION_BOMBPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+
+namespace Nancy {
+namespace Action {
+
+class BombPuzzle : public RenderActionRecord {
+public:
+	BombPuzzle() : RenderActionRecord(7) {}
+	virtual ~BombPuzzle() {}
+
+	void init() override;
+	void updateGraphics() override;
+
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+	void handleInput(NancyInput &input) override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "BombPuzzle"; }
+	bool isViewportRelative() const override { return true; }
+
+	Common::String _imageName;
+	Common::Array<Common::Rect> _wireSrcs;
+	Common::Array<Common::Rect> _wireDests;
+	Common::Array<Common::Rect> _digitSrcs;
+	Common::Array<Common::Rect> _digitDests;
+	Common::Rect _colonSrc;
+	Common::Rect _colonDest;
+	Common::Rect _displayBounds;
+	Common::Array<byte> _solveOrder;
+	SoundDescription _snipSound;
+	SoundDescription _noToolSound;
+	uint16 _toolID;
+	SceneChangeWithFlag _solveSceneChange;
+	SoundDescription _solveSound;
+	SceneChangeWithFlag _failSceneChange;
+	SoundDescription _failSound;
+
+	Graphics::ManagedSurface _image;
+	Common::Array<byte> _playerOrder;
+
+	Time _timerTotalTime;
+	Time _timerBlinkTime;
+	Time _nextBlinkTime;
+	bool _isBlinking = false;
+	uint _lastDrawnTime = 0;
+
+	bool _failed = false;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_BOMBPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 18bde304e50..dec6e74a51a 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS = \
   action/actionmanager.o \
   action/actionrecord.o \
   action/arfactory.o \
+  action/bombpuzzle.o \
   action/conversation.o \
   action/leverpuzzle.o \
   action/orderingpuzzle.o \
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 171675414ee..d6d1926b2e8 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -132,6 +132,7 @@ public:
 
 	void setPlayerTime(Time time, byte relative);
 	Time getPlayerTime() const { return _timers.playerTime; }
+	Time getTimerTime() const { return _timers.timerIsActive ? _timers.timerTime : 0; }
 	byte getPlayerTOD() const;
 
 	void addItemToInventory(uint16 id);


Commit: 13f61cf8823901d15af4f4925b519ee9ffe35185
    https://github.com/scummvm/scummvm/commit/13f61cf8823901d15af4f4925b519ee9ffe35185
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:33+03:00

Commit Message:
NANCY: Correct time of day for nancy2 and up

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index a4b18b2e806..fab951ccb41 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -280,12 +280,21 @@ void Scene::setPlayerTime(Time time, byte relative) {
 }
 
 byte Scene::getPlayerTOD() const {
-	if (_timers.playerTime.getHours() >= 7 && _timers.playerTime.getHours() < 18) {
-		return kPlayerDay;
-	} else if (_timers.playerTime.getHours() >= 19 || _timers.playerTime.getHours() < 6) {
-		return kPlayerNight;
+	if (g_nancy->getGameType() <= kGameTypeNancy1) {
+		if (_timers.playerTime.getHours() >= 7 && _timers.playerTime.getHours() < 18) {
+			return kPlayerDay;
+		} else if (_timers.playerTime.getHours() >= 19 || _timers.playerTime.getHours() < 6) {
+			return kPlayerNight;
+		} else {
+			return kPlayerDuskDawn;
+		}
 	} else {
-		return kPlayerDuskDawn;
+		// nancy2 and up removed dusk/dawn
+		if (_timers.playerTime.getHours() >= 6 && _timers.playerTime.getHours() < 18) {
+			return kPlayerDay;
+		} else {
+			return kPlayerNight;
+		}
 	}
 }
 


Commit: b7637ec92ec2178ef94f9a5768f4f979292090eb
    https://github.com/scummvm/scummvm/commit/b7637ec92ec2178ef94f9a5768f4f979292090eb
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:33+03:00

Commit Message:
NANCY: Respect ID offsets for nancy3 flags

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index fab951ccb41..0dc1d22ad2f 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -322,6 +322,11 @@ void Scene::setHeldItem(int16 id)  {
 }
 
 void Scene::setEventFlag(int16 label, byte flag) {
+	if (label > 1000) {
+		// In nancy3 and onwards flags begin from 1000
+		label -= 1000;
+	}
+
 	if (label > kEvNoEvent && (uint)label < g_nancy->getStaticData().numEventFlags) {
 		_flags.eventFlags[label] = flag;
 	}
@@ -332,6 +337,11 @@ void Scene::setEventFlag(FlagDescription eventFlag) {
 }
 
 bool Scene::getEventFlag(int16 label, byte flag) const {
+	if (label > 1000) {
+		// In nancy3 and onwards flags begin from 1000
+		label -= 1000;
+	}
+
 	if (label > kEvNoEvent && (uint)label < g_nancy->getStaticData().numEventFlags) {
 		return _flags.eventFlags[label] == flag;
 	} else {
@@ -345,6 +355,10 @@ bool Scene::getEventFlag(FlagDescription eventFlag) const {
 
 void Scene::setLogicCondition(int16 label, byte flag) {
 	if (label > kEvNoEvent) {
+		if (label > 2000) {
+			// In nancy3 and onwards logic conditions begin from 2000
+			label -= 2000;
+		}
 		_flags.logicConditions[label].flag = flag;
 		_flags.logicConditions[label].timestamp = g_nancy->getTotalPlayTime();
 	}


Commit: ac81086665766f14e67e93e2d3615c5ca6a3c819
    https://github.com/scummvm/scummvm/commit/ac81086665766f14e67e93e2d3615c5ca6a3c819
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:34+03:00

Commit Message:
NANCY: Correct dependency reading for nancy3

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 2551cc13484..84abe63d7b2 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -119,10 +119,11 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 	// If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
 	uint16 depsDataSize = (uint16)inputData.size() - localChunkSize;
 	if (depsDataSize > 0) {
-		// Each dependency is 0x0C bytes long (in v1)
-		uint numDependencies = depsDataSize / 0xC;
-		if (depsDataSize % 0xC) {
-			error("Action record type %s has incorrect read size", newRecord->getRecordTypeName().c_str());;
+		// Each dependency is 12 (up to nancy2) or 16 (nancy3 and up) bytes long 
+		uint singleDepSize = g_nancy->getGameType() <= kGameTypeNancy2 ? 12 : 16;
+		uint numDependencies = depsDataSize / singleDepSize;
+		if (depsDataSize % singleDepSize) {
+			error("Action record type %s has incorrect read size", newRecord->getRecordTypeName().c_str());
 		}
 
 		// Initialize the dependencies data
@@ -132,10 +133,18 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 			newRecord->_dependencies.push_back(DependencyRecord());
 			DependencyRecord &dep = newRecord->_dependencies.back();
 
-			dep.type = (DependencyType)inputData.readByte();
-			dep.label = inputData.readByte();
-			dep.condition = inputData.readByte();
-			dep.orFlag = inputData.readByte();
+			if (singleDepSize == 12) {
+				dep.type = (DependencyType)inputData.readByte();
+				dep.label = inputData.readByte();
+				dep.condition = inputData.readByte();
+				dep.orFlag = inputData.readByte();
+			} else if (singleDepSize == 16) {
+				dep.type = (DependencyType)inputData.readUint16LE();
+				dep.label = inputData.readUint16LE();
+				dep.condition = inputData.readUint16LE();
+				dep.orFlag = inputData.readUint16LE();
+			}
+
 			dep.hours = inputData.readSint16LE();
 			dep.minutes = inputData.readSint16LE();
 			dep.seconds = inputData.readSint16LE();
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 2029a777a20..c548bee75f1 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -39,7 +39,7 @@ struct NancyInput;
 
 namespace Action {
 
-enum struct DependencyType : byte {
+enum struct DependencyType : int16 {
 	kNone							= 0,
 	kInventory						= 1,
 	kEvent							= 2,
@@ -66,8 +66,8 @@ enum struct DependencyType : byte {
 // action record can be executed
 struct DependencyRecord {
 	DependencyType type;	// 0x00
-	byte label;				// 0x01
-	byte condition;			// 0x02
+	int16 label;			// 0x01
+	int16 condition;		// 0x02
 	bool orFlag;			// 0x03
 	int16 hours;			// 0x04
 	int16 minutes;			// 0x06


Commit: b1749a04e2bb7a996c6b0ce2429329726c13a8b9
    https://github.com/scummvm/scummvm/commit/b1749a04e2bb7a996c6b0ce2429329726c13a8b9
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:34+03:00

Commit Message:
NANCY: Fix saving of riddle puzzle state

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 0dc1d22ad2f..1964545a7ec 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -560,7 +560,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 			ser.syncArray(_towerPuzzleState->order[i].data(), 6, Common::Serializer::Byte);
 		}
 
-		byte numRiddles;
+		byte numRiddles = _riddlePuzzleState->solvedRiddleIDs.size();
 		ser.syncAsByte(numRiddles);
 
 		if (ser.isLoading()) {


Commit: c6164fcf4a2063cced828d930df96e8e1dc6cc43
    https://github.com/scummvm/scummvm/commit/c6164fcf4a2063cced828d930df96e8e1dc6cc43
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-05-07T11:46:35+03:00

Commit Message:
NANCY: Improve puzzle data storage and saving

Changed the way game-specific puzzle data is stored and
saved. Scene now holds a HashMap of lazily initialized
PuzzleData objects, each of which stores data for a specific
puzzle type. This helps avoid long switch statements in
the initialization and save/load code, and helps avoid
breaking savefiles every time a new puzzle type
is implemented.

Changed paths:
  A engines/nancy/puzzledata.cpp
  A engines/nancy/puzzledata.h
    engines/nancy/action/riddlepuzzle.cpp
    engines/nancy/action/riddlepuzzle.h
    engines/nancy/action/rippedletterpuzzle.cpp
    engines/nancy/action/rippedletterpuzzle.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/towerpuzzle.cpp
    engines/nancy/action/towerpuzzle.h
    engines/nancy/commontypes.h
    engines/nancy/module.mk
    engines/nancy/nancy.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/riddlepuzzle.cpp b/engines/nancy/action/riddlepuzzle.cpp
index 0c291facf6e..064f48b0db5 100644
--- a/engines/nancy/action/riddlepuzzle.cpp
+++ b/engines/nancy/action/riddlepuzzle.cpp
@@ -25,6 +25,7 @@
 #include "engines/nancy/util.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/puzzledata.h"
 #include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/action/riddlepuzzle.h"
@@ -45,7 +46,7 @@ void RiddlePuzzle::init() {
 }
 
 void RiddlePuzzle::readData(Common::SeekableReadStream &stream) {
-	_puzzleState = NancySceneState._riddlePuzzleState;
+	_puzzleState = (RiddlePuzzleData *)NancySceneState.getPuzzleData(RiddlePuzzleData::getTag());
 	assert(_puzzleState);
 
 	_viewportTextFontID = stream.readUint16LE();
diff --git a/engines/nancy/action/riddlepuzzle.h b/engines/nancy/action/riddlepuzzle.h
index 88b92a5c1f5..f347a3e16c7 100644
--- a/engines/nancy/action/riddlepuzzle.h
+++ b/engines/nancy/action/riddlepuzzle.h
@@ -25,6 +25,9 @@
 #include "engines/nancy/action/actionrecord.h"
 
 namespace Nancy {
+
+struct RiddlePuzzleData;
+
 namespace Action {
 
 class RiddlePuzzle : public RenderActionRecord {
@@ -73,7 +76,7 @@ protected:
 	bool _playerHasHitReturn = false;
 	Common::String _playerInput;
 	uint _riddleID = 0;
-	RiddlePuzzleState *_puzzleState = nullptr;
+	RiddlePuzzleData *_puzzleState = nullptr;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/rippedletterpuzzle.cpp b/engines/nancy/action/rippedletterpuzzle.cpp
index 655951bacca..640d6345ce8 100644
--- a/engines/nancy/action/rippedletterpuzzle.cpp
+++ b/engines/nancy/action/rippedletterpuzzle.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/cursor.h"
+#include "engines/nancy/puzzledata.h"
 
 #include "engines/nancy/state/scene.h"
 #include "engines/nancy/action/rippedletterpuzzle.h"
@@ -55,7 +56,7 @@ void RippedLetterPuzzle::registerGraphics() {
 }
 
 void RippedLetterPuzzle::readData(Common::SeekableReadStream &stream) {
-	_puzzleState = NancySceneState._rippedLetterPuzzleState;
+	_puzzleState = (RippedLetterPuzzleData *)NancySceneState.getPuzzleData(RippedLetterPuzzleData::getTag());
 	assert(_puzzleState);
 
 	readFilename(stream, _imageName);
diff --git a/engines/nancy/action/rippedletterpuzzle.h b/engines/nancy/action/rippedletterpuzzle.h
index 3b6152019c6..8d9036a0824 100644
--- a/engines/nancy/action/rippedletterpuzzle.h
+++ b/engines/nancy/action/rippedletterpuzzle.h
@@ -25,6 +25,9 @@
 #include "engines/nancy/action/actionrecord.h"
 
 namespace Nancy {
+
+struct RippedLetterPuzzleData;
+
 namespace Action {
 
 class RippedLetterPuzzle : public RenderActionRecord {
@@ -63,7 +66,7 @@ public:
 	
 	Graphics::ManagedSurface _image;
 	SolveState _solveState = kNotSolved;
-	RippedLetterPuzzleState *_puzzleState = nullptr;
+	RippedLetterPuzzleData *_puzzleState = nullptr;
 
 protected:
 	Common::String getRecordTypeName() const override { return "RippedLetterPuzzle"; }
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 6f84b14ad27..4715ae965ca 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -25,6 +25,7 @@
 #include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/util.h"
+#include "engines/nancy/puzzledata.h"
 
 #include "engines/nancy/action/sliderpuzzle.h"
 
@@ -46,7 +47,7 @@ void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
 	_spuzData = g_nancy->_sliderPuzzleData;
 	assert(_spuzData);
 
-	_puzzleState = NancySceneState._sliderPuzzleState;
+	_puzzleState = (SliderPuzzleData *)NancySceneState.getPuzzleData(SliderPuzzleData::getTag());
 	assert(_puzzleState);
 
 	readFilename(stream, _imageName);
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 0583903637c..c71242b0910 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -27,6 +27,7 @@
 namespace Nancy {
 
 struct SPUZ;
+struct SliderPuzzleData;
 
 namespace Action {
 
@@ -43,7 +44,7 @@ public:
 	void handleInput(NancyInput &input) override;
 
 	SPUZ *_spuzData = nullptr;
-	SliderPuzzleState *_puzzleState = nullptr;
+	SliderPuzzleData *_puzzleState = nullptr;
 
 	Common::String _imageName;
 	uint16 _width = 0;
diff --git a/engines/nancy/action/towerpuzzle.cpp b/engines/nancy/action/towerpuzzle.cpp
index 03d21a6757b..145cd289f69 100644
--- a/engines/nancy/action/towerpuzzle.cpp
+++ b/engines/nancy/action/towerpuzzle.cpp
@@ -25,6 +25,7 @@
 #include "engines/nancy/resource.h"
 #include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
+#include "engines/nancy/puzzledata.h"
 #include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/action/towerpuzzle.h"
@@ -50,7 +51,7 @@ void TowerPuzzle::registerGraphics() {
 }
 
 void TowerPuzzle::readData(Common::SeekableReadStream &stream) {
-	_puzzleState = NancySceneState._towerPuzzleState;
+	_puzzleState = (TowerPuzzleData *)NancySceneState.getPuzzleData(TowerPuzzleData::getTag());
 	assert(_puzzleState);
 
 	readFilename(stream, _imageName);
diff --git a/engines/nancy/action/towerpuzzle.h b/engines/nancy/action/towerpuzzle.h
index b698b4f00c8..1cdd51872dc 100644
--- a/engines/nancy/action/towerpuzzle.h
+++ b/engines/nancy/action/towerpuzzle.h
@@ -25,6 +25,9 @@
 #include "engines/nancy/action/actionrecord.h"
 
 namespace Nancy {
+	
+struct TowerPuzzleData;
+
 namespace Action {
 
 class TowerPuzzle : public RenderActionRecord {
@@ -68,7 +71,7 @@ protected:
 	int8 _heldRingID = -1;
 	int8 _heldRingPoleID = -1;
 	SolveState _solveState = kNotSolved;
-	TowerPuzzleState *_puzzleState;
+	TowerPuzzleData *_puzzleState = nullptr;
 	uint _numRings = 0;
 };
 
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 78710d725cd..c1f1c0db5b3 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -249,28 +249,6 @@ struct StaticData {
 	void readData(Common::SeekableReadStream &stream, Common::Language language);
 };
 
-// Structs for game-specific puzzle data that needs to be saved/loaded
-struct SliderPuzzleState {
-	Common::Array<Common::Array<int16>> playerTileOrder;
-	bool playerHasTriedPuzzle;
-};
-
-struct RippedLetterPuzzleState {
-	Common::Array<int8> order;
-	Common::Array<byte> rotations;
-	bool playerHasTriedPuzzle;
-};
-
-struct TowerPuzzleState {
-	Common::Array<Common::Array<int8>> order;
-	bool playerHasTriedPuzzle;
-};
-
-struct RiddlePuzzleState {
-	Common::Array<byte> solvedRiddleIDs;
-	int8 incorrectRiddleID;
-};
-
 } // End of namespace Nancy
 
 #endif // NANCY_COMMONYPES_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index dec6e74a51a..075bfd37c4b 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -48,6 +48,7 @@ MODULE_OBJS = \
   input.o \
   metaengine.o \
   nancy.o \
+  puzzledata.o \
   renderobject.o \
   resource.o \
   sound.o \
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 35c6ca20671..4e6856f587e 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -52,7 +52,7 @@ class Serializer;
  */
 namespace Nancy {
 
-static const int kSavegameVersion = 2;
+static const int kSavegameVersion = 3;
 
 struct NancyGameDescription;
 
diff --git a/engines/nancy/puzzledata.cpp b/engines/nancy/puzzledata.cpp
new file mode 100644
index 00000000000..5515e88f5da
--- /dev/null
+++ b/engines/nancy/puzzledata.cpp
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 "engines/nancy/puzzledata.h"
+
+namespace Nancy {
+
+SliderPuzzleData::SliderPuzzleData() : playerHasTriedPuzzle(false) {}
+
+void SliderPuzzleData::synchronize(Common::Serializer &ser) {
+	ser.syncAsByte(playerHasTriedPuzzle);
+
+	byte x = 0, y = 0;
+
+	if (ser.isSaving()) {
+		y = playerTileOrder.size();
+		if (y) {
+			x = playerTileOrder.back().size();
+		} else {
+			x = 0;
+		}
+	}
+
+	ser.syncAsByte(x);
+	ser.syncAsByte(y);
+
+	playerTileOrder.resize(y);
+
+	for (int i = 0; i < y; ++i) {
+		playerTileOrder[i].resize(x);
+		ser.syncArray(playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
+	}
+}
+
+RippedLetterPuzzleData::RippedLetterPuzzleData() :
+	order(24, 0),
+	rotations(24, 0),
+	playerHasTriedPuzzle(false) {}
+
+void RippedLetterPuzzleData::synchronize(Common::Serializer &ser) {
+	if (ser.isLoading()) {
+		order.resize(24);
+		rotations.resize(24);
+	}
+
+	ser.syncArray(order.data(), 24, Common::Serializer::Byte);
+	ser.syncArray(rotations.data(), 24, Common::Serializer::Byte);
+}
+
+TowerPuzzleData::TowerPuzzleData() {
+	order.resize(3, Common::Array<int8>(6, -1));
+	playerHasTriedPuzzle = false;
+}
+
+void TowerPuzzleData::synchronize(Common::Serializer &ser) {
+	ser.syncAsByte(playerHasTriedPuzzle);
+
+	if (ser.isLoading()) {
+		order.resize(3, Common::Array<int8>(6, -1));
+	}
+
+	for (uint i = 0; i < 3; ++i) {
+		ser.syncArray(order[i].data(), 6, Common::Serializer::Byte);
+	}
+}
+
+RiddlePuzzleData::RiddlePuzzleData() :
+	incorrectRiddleID(-1) {}
+
+void RiddlePuzzleData::synchronize(Common::Serializer &ser) {
+	byte numRiddles = solvedRiddleIDs.size();
+	ser.syncAsByte(numRiddles);
+
+	if (ser.isLoading()) {
+		solvedRiddleIDs.resize(numRiddles);
+	}
+
+	ser.syncArray(solvedRiddleIDs.data(), numRiddles, Common::Serializer::Byte);
+}
+
+PuzzleData *makePuzzleData(const uint32 tag) {
+	switch(tag) {
+	case SliderPuzzleData::getTag():
+		return new SliderPuzzleData();
+	case RippedLetterPuzzleData::getTag():
+		return new RippedLetterPuzzleData();
+	case TowerPuzzleData::getTag():
+		return new TowerPuzzleData();
+	case RiddlePuzzleData::getTag():
+		return new RiddlePuzzleData();
+	default:
+		return nullptr;
+	}
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/puzzledata.h b/engines/nancy/puzzledata.h
new file mode 100644
index 00000000000..bf9c90f4a07
--- /dev/null
+++ b/engines/nancy/puzzledata.h
@@ -0,0 +1,85 @@
+/* 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/serializer.h"
+#include "common/array.h"
+
+#ifndef NANCY_PUZZLEDATA_H
+#define NANCY_PUZZLEDATA_H
+
+namespace Nancy {
+
+// The following structs contain persistent data for specific
+// puzzle types, which is to be stored in savefiles
+
+struct PuzzleData {
+	PuzzleData() {}
+	virtual ~PuzzleData() {}
+
+	virtual void synchronize(Common::Serializer &ser) = 0;
+};
+
+struct SliderPuzzleData : public PuzzleData {
+	SliderPuzzleData();
+
+	static constexpr uint32 getTag() { return MKTAG('S', 'L', 'I', 'D'); }
+	virtual void synchronize(Common::Serializer &ser);
+
+	Common::Array<Common::Array<int16>> playerTileOrder;
+	bool playerHasTriedPuzzle;
+};
+
+struct RippedLetterPuzzleData : public PuzzleData {
+	RippedLetterPuzzleData();
+
+	static constexpr uint32 getTag() { return MKTAG('R', 'I', 'P', 'L'); }
+	virtual void synchronize(Common::Serializer &ser);
+
+	Common::Array<int8> order;
+	Common::Array<byte> rotations;
+	bool playerHasTriedPuzzle;
+};
+
+struct TowerPuzzleData : public PuzzleData {
+	TowerPuzzleData();
+
+	static constexpr uint32 getTag() { return MKTAG('T', 'O', 'W', 'R'); }
+	virtual void synchronize(Common::Serializer &ser);
+
+	Common::Array<Common::Array<int8>> order;
+	bool playerHasTriedPuzzle;
+};
+
+struct RiddlePuzzleData : public PuzzleData {
+	RiddlePuzzleData();
+
+	static constexpr uint32 getTag() { return MKTAG('R', 'I', 'D', 'L'); }
+	virtual void synchronize(Common::Serializer &ser);
+
+	Common::Array<byte> solvedRiddleIDs;
+	int8 incorrectRiddleID;
+};
+
+PuzzleData *makePuzzleData(const uint32 tag);
+
+} // End of namespace Nancy
+
+#endif // NANCY_PUZZLEDATA_H
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 1964545a7ec..f1ef5a129e7 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -115,11 +115,7 @@ Scene::Scene() :
 		_difficulty(0),
 		_activeConversation(nullptr),
 		_lightning(nullptr),
-		_specialEffect(nullptr),
-		_sliderPuzzleState(nullptr),
-		_rippedLetterPuzzleState(nullptr),
-		_towerPuzzleState(nullptr),
-		_riddlePuzzleState(nullptr) {}
+		_specialEffect(nullptr) {}
 
 Scene::~Scene()  {
 	delete _helpButton;
@@ -130,10 +126,8 @@ Scene::~Scene()  {
 	delete _clock;
 	delete _lightning;
 	delete _specialEffect;
-	delete _sliderPuzzleState;
-	delete _rippedLetterPuzzleState;
-	delete _towerPuzzleState;
-	delete _riddlePuzzleState;
+
+	clearPuzzleData();
 }
 
 void Scene::process() {
@@ -486,7 +480,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsUint32LE((uint32 &)_timers.pushedPlayTime);
 	ser.syncAsUint32LE((uint32 &)_timers.timerTime);
 	ser.syncAsByte(_timers.timerIsActive);
-	ser.skip(1); // timeOfDay; To be removed on next savefile version bump
+	ser.skip(1, 0, 2);
 
 	g_nancy->setTotalPlayTime((uint32)_timers.lastTotalTime);
 
@@ -500,79 +494,38 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsSint16LE(_lastHintCharacter);
 	ser.syncAsSint16LE(_lastHintID);
 
-	switch (g_nancy->getGameType()) {
-	case kGameTypeVampire:
-		// Fall through to avoid having to bump the savegame version
-		// fall through
-	case kGameTypeNancy1: {
-		// Synchronize SliderPuzzle static data
-		if (!_sliderPuzzleState) {
-			return;
-		}
-
-		ser.syncAsByte(_sliderPuzzleState->playerHasTriedPuzzle);
-
-		byte x = 0, y = 0;
-
-		if (ser.isSaving()) {
-			y = _sliderPuzzleState->playerTileOrder.size();
-			if (y) {
-				x = _sliderPuzzleState->playerTileOrder.back().size();
-			} else {
-				x = 0;
-			}
-		}
-
-		ser.syncAsByte(x);
-		ser.syncAsByte(y);
+	// Sync game-specific puzzle data
 
-		_sliderPuzzleState->playerTileOrder.resize(y);
-
-		for (int i = 0; i < y; ++i) {
-			_sliderPuzzleState->playerTileOrder[i].resize(x);
-			ser.syncArray(_sliderPuzzleState->playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
+	// Support for older savefiles
+	if (ser.getVersion() < 3 && g_nancy->getGameType() <= kGameTypeNancy1) {
+		PuzzleData *pd = getPuzzleData(SliderPuzzleData::getTag());
+		if (pd) {
+			pd->synchronize(ser);
 		}
 
-		break;
+		return;
 	}
-	case kGameTypeNancy2 : {
-		if (!_rippedLetterPuzzleState || !_towerPuzzleState || !_riddlePuzzleState) {
-			break;
-		}
-
-		ser.syncAsByte(_rippedLetterPuzzleState->playerHasTriedPuzzle);
-
-		if (ser.isLoading()) {
-			_rippedLetterPuzzleState->order.resize(24);
-			_rippedLetterPuzzleState->rotations.resize(24);
-		}
-
-		ser.syncArray(_rippedLetterPuzzleState->order.data(), 24, Common::Serializer::Byte);
-		ser.syncArray(_rippedLetterPuzzleState->rotations.data(), 24, Common::Serializer::Byte);
 
-		ser.syncAsByte(_towerPuzzleState->playerHasTriedPuzzle);
+	byte numPuzzleData = _puzzleData.size();
+	ser.syncAsByte(numPuzzleData);
 
-		if (ser.isLoading()) {
-			_towerPuzzleState->order.resize(3, Common::Array<int8>(6, -1));
-		}
-
-		for (uint i = 0; i < 3; ++i) {
-			ser.syncArray(_towerPuzzleState->order[i].data(), 6, Common::Serializer::Byte);
+	if (ser.isSaving()) {
+		for (auto pd : _puzzleData) {
+			uint32 tag = pd._key;
+			ser.syncAsUint32LE(tag);
+			pd._value->synchronize(ser);
 		}
-
-		byte numRiddles = _riddlePuzzleState->solvedRiddleIDs.size();
-		ser.syncAsByte(numRiddles);
-
-		if (ser.isLoading()) {
-			_riddlePuzzleState->solvedRiddleIDs.resize(numRiddles);
+	} else {
+		clearPuzzleData();
+
+		uint32 tag;
+		for (uint i = 0; i < numPuzzleData; ++i) {
+			ser.syncAsUint32LE(tag);
+			PuzzleData *pd = getPuzzleData(tag);
+			if (pd) {
+				pd->synchronize(ser);
+			}
 		}
-
-		ser.syncArray(_riddlePuzzleState->solvedRiddleIDs.data(), numRiddles, Common::Serializer::Byte);
-
-		break;
-	}
-	default:
-		break;
 	}
 }
 
@@ -604,38 +557,6 @@ void Scene::init() {
 		_lastHintCharacter = _lastHintID = -1;
 	}
 
-	// Initialize game-specific data
-	switch (g_nancy->getGameType()) {
-	case kGameTypeVampire:
-		// Fall through to avoid having to bump the savefile version
-		// fall through
-	case kGameTypeNancy1:
-		delete _sliderPuzzleState;
-		_sliderPuzzleState = new SliderPuzzleState();
-		_sliderPuzzleState->playerHasTriedPuzzle = false;
-		
-		break;
-	case kGameTypeNancy2:
-		delete _rippedLetterPuzzleState;
-		_rippedLetterPuzzleState = new RippedLetterPuzzleState();
-		_rippedLetterPuzzleState->playerHasTriedPuzzle = false;
-		_rippedLetterPuzzleState->order.resize(24, 0);
-		_rippedLetterPuzzleState->rotations.resize(24, 0);
-
-		delete _towerPuzzleState;
-		_towerPuzzleState = new TowerPuzzleState();
-		_towerPuzzleState->playerHasTriedPuzzle = false;
-		_towerPuzzleState->order.resize(3, Common::Array<int8>(6, -1));
-
-		delete _riddlePuzzleState;
-		_riddlePuzzleState = new RiddlePuzzleState();
-		_riddlePuzzleState->incorrectRiddleID = -1;
-		
-		break;
-	default:
-		break;
-	}
-
 	initStaticData();
 
 	if (ConfMan.hasKey("save_slot")) {
@@ -680,6 +601,22 @@ void Scene::specialEffect(byte type, uint16 fadeToBlackTime, uint16 frameTime) {
 	_specialEffect->init();
 }
 
+PuzzleData *Scene::getPuzzleData(const uint32 tag) {
+	// Lazy initialization ensures both init() and synchronize() will not need
+	// to care about which puzzles a specific game has
+
+	if (_puzzleData.contains(tag)) {
+		return _puzzleData[tag];
+	} else {
+		PuzzleData *newData = makePuzzleData(tag);
+		if (newData) {
+			_puzzleData.setVal(tag, newData);
+		}
+		
+		return newData;
+	}
+}
+
 void Scene::load() {
 	clearSceneData();
 
@@ -950,5 +887,11 @@ void Scene::clearSceneData() {
 	}
 }
 
+void Scene::clearPuzzleData() {
+	for (auto &pd : _puzzleData) {
+		delete pd._value;
+	}
+}
+
 } // End of namespace State
 } // End of namespace Nancy
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index d6d1926b2e8..da17783563b 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -25,6 +25,7 @@
 #include "common/singleton.h"
 
 #include "engines/nancy/commontypes.h"
+#include "engines/nancy/puzzledata.h"
 
 #include "engines/nancy/action/actionmanager.h"
 
@@ -190,11 +191,8 @@ public:
 	// Used from nancy2 onwards
 	void specialEffect(byte type, uint16 fadeToBlackTime, uint16 frameTime);
 
-	// Game-specific data that needs to be saved/loaded
-	SliderPuzzleState *_sliderPuzzleState;
-	RippedLetterPuzzleState *_rippedLetterPuzzleState;
-	TowerPuzzleState *_towerPuzzleState;
-	RiddlePuzzleState *_riddlePuzzleState;
+	// Get the persistent data for a given puzzle type
+	PuzzleData *getPuzzleData(const uint32 tag);
 
 private:
 	void init();
@@ -205,6 +203,7 @@ private:
 	void initStaticData();
 
 	void clearSceneData();
+	void clearPuzzleData();
 
 	enum State {
 		kInit,
@@ -262,6 +261,8 @@ private:
 	UI::InventoryBoxOrnaments *_inventoryBoxOrnaments;
 	UI::Clock *_clock;
 
+	Common::Rect _mapHotspot;
+
 	// General data
 	SceneState _sceneState;
 	PlayFlags _flags;
@@ -275,7 +276,7 @@ private:
 	Misc::Lightning *_lightning;
 	Misc::SpecialEffect *_specialEffect;
 
-	Common::Rect _mapHotspot;
+	Common::HashMap<uint32, PuzzleData *> _puzzleData;
 
 	Action::ActionManager _actionManager;
 	Action::ConversationSound *_activeConversation;




More information about the Scummvm-git-logs mailing list