[Scummvm-git-logs] scummvm master -> 07a95628bbc17af75afab7d06c2ea40d64d0ea6c

fracturehill noreply at scummvm.org
Sat Feb 10 12:28:15 UTC 2024


This automated email contains information about 10 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
5683e5f6e6 NANCY: Implement even terser Conversation records
4558690ec9 NANCY: Implement GoInvViewScene record type
f5aef15d69 NANCY: Allow invalid item ids in some Scene functions
477006c8a9 NANCY: Add PopScene() failsafe
b508ea4740 NANCY: Add sensible sound defaults
c0a529c763 NANCY: Add sensible default listener on new scene
0f4ce35613 NANCY: Add support for nancy8 PianoPuzzle
b85ede6c9a NANCY: Correctly evaluate Conversation orFlags
afbeb4ab8c NANCY: Support Conversation response add rules
07a95628bb NANCY: Fix nancy1 crash


Commit: 5683e5f6e687ca3654bec9f9de5851a7d1d0c11c
    https://github.com/scummvm/scummvm/commit/5683e5f6e687ca3654bec9f9de5851a7d1d0c11c
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:11+01:00

Commit Message:
NANCY: Implement even terser Conversation records

Implemented the ConversationSoundTerse and
ConversationCelTerse action records, which are even
shorter variants of the corresponding Conversation types.
Made changes to the base ConversationSound to reduce
code duplication.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/conversation.cpp
    engines/nancy/action/conversation.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 403898feb5e..dd7a98d6b21 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -174,7 +174,13 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 			return new Autotext();
 		}
 	case 62:
-		return new MapCallHotMultiframe();
+		if (g_nancy->getGameType() <= kGameTypeNancy7) {
+			return new MapCallHotMultiframe(); // TVD/nancy1 only
+		} else {
+			return new ConversationCelTerse(); // nancy8 and up
+		}
+	case 63:
+		return new ConversationSoundTerse();		
 	case 65:
 		return new TableIndexOverlay();
 	case 66:
diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 7db7a081637..c0f3ce77645 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -39,8 +39,13 @@ namespace Nancy {
 namespace Action {
 
 ConversationSound::ConversationSound() :
-	RenderActionRecord(8),
-	_noResponse(g_nancy->getGameType() <= kGameTypeNancy2 ? 10 : 20) {}
+		RenderActionRecord(8),
+		_noResponse(g_nancy->getGameType() <= kGameTypeNancy2 ? 10 : 20),
+		_hasDrawnTextbox(false),
+		_pickedResponse(-1) {
+	_conditionalResponseCharacterID = _noResponse;
+	_goodbyeResponseCharacterID = _noResponse;
+}
 
 ConversationSound::~ConversationSound() {
 	if (NancySceneState.getActiveConversation() == this) {
@@ -113,6 +118,83 @@ void ConversationSound::readData(Common::SeekableReadStream &stream) {
 	}
 }
 
+void ConversationSound::readTerseData(Common::SeekableReadStream &stream) {
+	readFilename(stream, _sound.name);
+	_sound.volume = stream.readUint16LE();
+	_sound.channelID = 12; // hardcoded
+	_sound.numLoops = 1;
+
+	_responseGenericSound.volume = _sound.volume;
+	_responseGenericSound.numLoops = 1;
+	_responseGenericSound.channelID = 13; // hardcoded
+
+	readTerseCaptionText(stream);
+
+	_conditionalResponseCharacterID = stream.readByte();
+	_goodbyeResponseCharacterID = stream.readByte();
+
+	_defaultNextScene = stream.readByte();
+
+	_sceneChange.sceneID = stream.readUint16LE();
+	_sceneChange.continueSceneSound = kContinueSceneSound;
+
+	uint16 numResponses = stream.readUint16LE();
+	_responses.resize(numResponses);
+	for (uint i = 0; i < numResponses; ++i) {
+		ResponseStruct &response = _responses[i];
+		response.conditionFlags.read(stream);
+		readTerseResponseText(stream, response);
+		readFilename(stream, response.soundName);
+		response.sceneChange.sceneID = stream.readUint16LE();
+		response.sceneChange.continueSceneSound = kContinueSceneSound;
+	}
+
+	// No scene branches
+	uint16 numFlagsStructs = stream.readUint16LE();
+	_flagsStructs.resize(numFlagsStructs);
+	for (uint i = 0; i < numFlagsStructs; ++i) {
+		FlagsStruct &flagsStruct = _flagsStructs[i];
+		flagsStruct.conditions.read(stream);
+		flagsStruct.flagToSet.type = stream.readByte();
+		flagsStruct.flagToSet.flag.label = stream.readSint16LE();
+		flagsStruct.flagToSet.flag.flag = stream.readByte();
+	}
+}
+
+void ConversationSound::readCaptionText(Common::SeekableReadStream &stream) {
+	char *rawText = new char[1500];
+	stream.read(rawText, 1500);
+	assembleTextLine(rawText, _text, 1500);
+	delete[] rawText;
+}
+
+void ConversationSound::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
+	char *rawText = new char[400];
+	stream.read(rawText, 400);
+	assembleTextLine(rawText, response.text, 400);
+	delete[] rawText;
+}
+
+void ConversationSound::readTerseCaptionText(Common::SeekableReadStream &stream) {
+	Common::String key;
+	readFilename(stream, key);
+
+	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+	assert(convo);
+
+	_text = convo->texts[key];
+}
+
+void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
+	Common::String key;
+	readFilename(stream, key);
+
+	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+	assert(convo);
+
+	response.text = convo->texts[key];
+}
+
 void ConversationSound::execute() {
 	ConversationSound *activeConversation = NancySceneState.getActiveConversation();
 	if (activeConversation != this && activeConversation != nullptr) {
@@ -289,20 +371,6 @@ void ConversationSound::execute() {
 	}
 }
 
-void ConversationSound::readCaptionText(Common::SeekableReadStream &stream) {
-	char *rawText = new char[1500];
-	stream.read(rawText, 1500);
-	assembleTextLine(rawText, _text, 1500);
-	delete[] rawText;
-}
-
-void ConversationSound::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
-	char *rawText = new char[400];
-	stream.read(rawText, 400);
-	assembleTextLine(rawText, response.text, 400);
-	delete[] rawText;
-}
-
 void ConversationSound::addConditionalDialogue() {
 	for (const auto &res : g_nancy->getStaticData().conditionalDialogue[_conditionalResponseCharacterID]) {
 		bool isSatisfied = true;
@@ -678,7 +746,35 @@ void ConversationCel::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, xsheetName);
 	
 	readFilenameArray(stream, _treeNames, 4);
+	readXSheet(stream, xsheetName);
+
+	// Continue reading the AR stream
+
+	// Something related to quality
+	stream.skip(3);
+
+	_firstFrame = stream.readUint16LE();
+	_lastFrame = stream.readUint16LE();
+
+	stream.skip(6);
+
+	_drawingOrder.resize(4);
+	for (uint i = 0; i < 4; ++i) {
+		_drawingOrder[i] = stream.readByte();
+	}
+
+	_overrideTreeRects.resize(4);
+	for (uint i = 0; i < 4; ++i) {
+		_overrideTreeRects[i] = stream.readByte();
+	}
+
+	readRectArray(stream, _overrideRectSrcs, 4);
+	readRectArray(stream, _overrideRectDests, 4);
+
+	ConversationSound::readData(stream);
+}
 
+void ConversationCel::readXSheet(Common::SeekableReadStream &stream, const Common::String &xsheetName) {
 	Common::SeekableReadStream *xsheet = SearchMan.createReadStreamForMember(Common::Path(xsheetName));
 
 	// Read the xsheet and load all images into the arrays
@@ -705,31 +801,6 @@ void ConversationCel::readData(Common::SeekableReadStream &stream) {
 		// 4 unknown values
 		xsheet->skip(8);
 	}
-
-	// Continue reading the AR stream
-
-	// Something related to quality
-	stream.skip(3);
-
-	_firstFrame = stream.readUint16LE();
-	_lastFrame = stream.readUint16LE();
-
-	stream.skip(6);
-
-	_drawingOrder.resize(4);
-	for (uint i = 0; i < 4; ++i) {
-		_drawingOrder[i] = stream.readByte();
-	}
-
-	_overrideTreeRects.resize(4);
-	for (uint i = 0; i < 4; ++i) {
-		_overrideTreeRects[i] = stream.readByte();
-	}
-
-	readRectArray(stream, _overrideRectSrcs, 4);
-	readRectArray(stream, _overrideRectDests, 4);
-
-	ConversationSound::readData(stream);
 }
 
 bool ConversationCel::isVideoDonePlaying() {
@@ -747,44 +818,22 @@ ConversationCel::Cel &ConversationCel::loadCel(const Common::Path &name, const C
 	return _celCache[name];
 }
 
-void ConversationSoundT::readCaptionText(Common::SeekableReadStream &stream) {
-	Common::String key;
-	readFilename(stream, key);
-
-	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
-	assert(convo);
-
-	_text = convo->texts[key];
-}
-
-void ConversationSoundT::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
-	Common::String key;
-	readFilename(stream, key);
-
-	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
-	assert(convo);
-
-	response.text = convo->texts[key];
+void ConversationSoundTerse::readData(Common::SeekableReadStream &stream) {
+	readTerseData(stream);
 }
 
-void ConversationCelT::readCaptionText(Common::SeekableReadStream &stream) {
-	Common::String key;
-	readFilename(stream, key);
-
-	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
-	assert(convo);
-
-	_text = convo->texts[key];
-}
+void ConversationCelTerse::readData(Common::SeekableReadStream &stream) {
+	Common::String xsheetName;
+	readFilename(stream, xsheetName);
 
-void ConversationCelT::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
-	Common::String key;
-	readFilename(stream, key);
+	readFilenameArray(stream, _treeNames, 2); // Only 2
+	readXSheet(stream, xsheetName);
 
-	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
-	assert(convo);
+	_lastFrame = stream.readUint16LE();
+	_drawingOrder = { 1, 0, 2, 3 };
+	_overrideTreeRects.resize(4, kCelOverrideTreeRectsOff);
 
-	response.text = convo->texts[key];
+	readTerseData(stream);
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/conversation.h b/engines/nancy/action/conversation.h
index 44fb8e0dbe6..3349dc98155 100644
--- a/engines/nancy/action/conversation.h
+++ b/engines/nancy/action/conversation.h
@@ -100,6 +100,11 @@ protected:
 	// Functions for reading captions are virtual to allow easier support for the terse Conversation variants
 	virtual void readCaptionText(Common::SeekableReadStream &stream);
 	virtual void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response);
+	
+	// Used in subclasses
+	void readTerseData(Common::SeekableReadStream &stream);
+	void readTerseCaptionText(Common::SeekableReadStream &stream);
+	void readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response);
 
 	// Functions for handling the built-in dialogue responses found in the executable
 	void addConditionalDialogue();
@@ -110,8 +115,8 @@ protected:
 	SoundDescription _sound;
 	SoundDescription _responseGenericSound;
 
-	byte _conditionalResponseCharacterID = 0;
-	byte _goodbyeResponseCharacterID = 0;
+	byte _conditionalResponseCharacterID;
+	byte _goodbyeResponseCharacterID;
 	byte _defaultNextScene = kDefaultNextSceneEnabled;
 	byte _popNextScene = kNoPopNextScene;
 	SceneChangeDescription _sceneChange;
@@ -120,8 +125,8 @@ protected:
 	Common::Array<FlagsStruct> _flagsStructs;
 	Common::Array<SceneBranchStruct> _sceneBranchStructs;
 
-	bool _hasDrawnTextbox = false;
-	int16 _pickedResponse = -1;
+	bool _hasDrawnTextbox;
+	int16 _pickedResponse;
 
 	const byte _noResponse;
 };
@@ -186,6 +191,8 @@ protected:
 	bool isVideoDonePlaying() override;
 	Cel &loadCel(const Common::Path &name, const Common::String &treeName);
 
+	void readXSheet(Common::SeekableReadStream &stream, const Common::String &xsheetName);
+
 	Common::Array<Common::Array<Common::Path>> _celNames;
 	Common::Array<Common::String> _treeNames;
 
@@ -209,20 +216,40 @@ protected:
 	Common::SharedPtr<ConversationCelLoader> _loaderPtr;
 };
 
+// A ConversationSound without embedded text; uses the CONVO chunk instead
 class ConversationSoundT : public ConversationSound {
 protected:
 	Common::String getRecordTypeName() const override { return "ConversationSoundT"; }
 
-	void readCaptionText(Common::SeekableReadStream &stream) override;
-	void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override;
+	void readCaptionText(Common::SeekableReadStream &stream) override { readTerseCaptionText(stream); }
+	void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override { readTerseResponseText(stream, response); }
 };
 
+// A ConversationCel without embedded text; uses the CONVO chunk instead
 class ConversationCelT : public ConversationCel {
 protected:
 	Common::String getRecordTypeName() const override { return "ConversationCelT"; }
 
-	void readCaptionText(Common::SeekableReadStream &stream) override;
-	void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override;
+	void readCaptionText(Common::SeekableReadStream &stream) override { readTerseCaptionText(stream); }
+	void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override { readTerseResponseText(stream, response); }
+};
+
+// A ConversationSound with a much smaller data footprint
+class ConversationSoundTerse : public ConversationSound {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	
+protected:
+	Common::String getRecordTypeName() const override { return "ConversationSoundTerse"; }
+};
+
+// A ConversationCel with a much smaller data footprint
+class ConversationCelTerse : public ConversationCel {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	
+protected:
+	Common::String getRecordTypeName() const override { return "ConversationCelTerse"; }
 };
 
 } // End of namespace Action


Commit: 4558690ec9cec09a8eff92f4caed849084c526b1
    https://github.com/scummvm/scummvm/commit/4558690ec9cec09a8eff92f4caed849084c526b1
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:11+01:00

Commit Message:
NANCY: Implement GoInvViewScene record type

This record goes to an item's associated scene, and
(optionally) adds it to the inventory if it's not already
present in it.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/inventoryrecords.cpp
    engines/nancy/action/inventoryrecords.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index dd7a98d6b21..aad9a948fba 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -273,6 +273,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new EnableDisableInventory();
 	case 125:
 		return new PopInvViewPriorScene();
+	case 126:
+		return new GoInvViewScene();
 	case 140:
 		return new SetVolume();
 	case 150:
diff --git a/engines/nancy/action/inventoryrecords.cpp b/engines/nancy/action/inventoryrecords.cpp
index 9bc1001ea1a..64ed1f693c5 100644
--- a/engines/nancy/action/inventoryrecords.cpp
+++ b/engines/nancy/action/inventoryrecords.cpp
@@ -199,5 +199,34 @@ void PopInvViewPriorScene::execute() {
 	_isDone = true;
 }
 
+void GoInvViewScene::readData(Common::SeekableReadStream &stream) {
+	_itemID = stream.readUint16LE();
+	_addToInventory = stream.readUint16LE();
+}
+
+void GoInvViewScene::execute() {
+	auto *inv = GetEngineData(INV);
+	assert(inv);
+
+	const INV::ItemDescription &item = inv->itemDescriptions[_itemID];
+	byte disabled = NancySceneState.getItemDisabledState(_itemID);
+
+	if (!disabled && item.keepItem == kInvItemNewSceneView) {
+		if (_addToInventory || NancySceneState.hasItem(_itemID)) {
+			NancySceneState.pushScene(_itemID);
+		} else {
+			// Do not add the item to the inventory, only go to its scene
+			NancySceneState.pushScene();
+		}
+
+		SceneChangeDescription sceneChange;
+		sceneChange.sceneID = item.sceneID;
+		sceneChange.continueSceneSound = item.sceneSoundFlag;
+		NancySceneState.changeScene(sceneChange);
+	}
+
+	_isDone = true;
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/inventoryrecords.h b/engines/nancy/action/inventoryrecords.h
index e9bbb4d52e9..4f0639d8bd0 100644
--- a/engines/nancy/action/inventoryrecords.h
+++ b/engines/nancy/action/inventoryrecords.h
@@ -120,6 +120,18 @@ protected:
 	Common::String getRecordTypeName() const override { return "PopInvViewPriorScene"; }
 };
 
+class GoInvViewScene : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "GoInvViewScene"; }
+
+	uint16 _itemID = 0;
+	bool _addToInventory = false;
+};
+
 } // End of namespace Action
 } // End of namespace Nancy
 


Commit: f5aef15d69c946dac843d7b830daff77ae2ffa16
    https://github.com/scummvm/scummvm/commit/f5aef15d69c946dac843d7b830daff77ae2ffa16
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:11+01:00

Commit Message:
NANCY: Allow invalid item ids in some Scene functions

The addItemToInventory() and removeItemFromInventory()
were still using uints, which meant that if they were
somehow called with an id of -1 (which marks an invalid
item), they'd crash the engine. This shouldn't matter for
regular play, but may occur when debugging.

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


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 05da02b0f2b..24780a8e2be 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -316,7 +316,11 @@ byte Scene::getPlayerTOD() const {
 	}
 }
 
-void Scene::addItemToInventory(uint16 id) {
+void Scene::addItemToInventory(int16 id) {
+	if (id == -1) {
+		return;
+	}
+
 	if (_flags.items[id] == g_nancy->_false) {
 		_flags.items[id] = g_nancy->_true;
 		if (_flags.heldItem == id) {
@@ -329,7 +333,11 @@ void Scene::addItemToInventory(uint16 id) {
 	}
 }
 
-void Scene::removeItemFromInventory(uint16 id, bool pickUp) {
+void Scene::removeItemFromInventory(int16 id, bool pickUp) {
+	if (id == -1) {
+		return;
+	}
+
 	if (_flags.items[id] == g_nancy->_true || getHeldItem() == id) {
 		_flags.items[id] = g_nancy->_false;
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index ee591cc32c0..9d2a8ea7076 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -125,8 +125,8 @@ public:
 	Time getTimerTime() const { return _timers.timerIsActive ? _timers.timerTime : 0; }
 	byte getPlayerTOD() const;
 
-	void addItemToInventory(uint16 id);
-	void removeItemFromInventory(uint16 id, bool pickUp = true);
+	void addItemToInventory(int16 id);
+	void removeItemFromInventory(int16 id, bool pickUp = true);
 	int16 getHeldItem() const { return _flags.heldItem; }
 	void setHeldItem(int16 id);
 	void setNoHeldItem();


Commit: 477006c8a93148e6df46b85551bb79f32227a3c7
    https://github.com/scummvm/scummvm/commit/477006c8a93148e6df46b85551bb79f32227a3c7
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Add PopScene() failsafe

When trying to pop an item scene with none pushed,
PopScene will now attempt to pop a regular pushed scene.

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


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 24780a8e2be..fdf20cf389c 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -256,7 +256,7 @@ void Scene::pushScene(int16 itemID) {
 }
 
 void Scene::popScene(bool inventory) {
-	if (!inventory) {
+	if (!inventory || _sceneState.pushedInvItemID == -1) {
 		_sceneState.pushedScene.continueSceneSound = true;
 		changeScene(_sceneState.pushedScene);
 		_sceneState.isScenePushed = false;


Commit: b508ea474004ee0f5900fb22736865c551774fa0
    https://github.com/scummvm/scummvm/commit/b508ea474004ee0f5900fb22736865c551774fa0
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Add sensible sound defaults

Changed paths:
    engines/nancy/commontypes.h


diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 1b783a6c775..1820cee6eb5 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -254,8 +254,8 @@ struct SoundDescription {
 	Common::String name = "NO SOUND";
 	uint16 channelID = 0;
 	uint16 playCommands = 1;
-	uint16 numLoops = 0;
-	uint16 volume = 0;
+	uint16 numLoops = 1;
+	uint16 volume = 50;
 	uint16 panAnchorFrame = 0;
 	uint32 samplesPerSec = 0;
 	bool isPanning = false;


Commit: c0a529c763d07d07b1e5d2f36369c62cc608930d
    https://github.com/scummvm/scummvm/commit/c0a529c763d07d07b1e5d2f36369c62cc608930d
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Add sensible default listener on new scene

Whenever a SceneChangeDescription is constructed
manually (e.g. in Conversation), it now has its
listenerFrontVector initialized with the default value
(pointing straight forward).

Changed paths:
    engines/nancy/commontypes.h


diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 1820cee6eb5..fdc5c0be905 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -155,7 +155,7 @@ struct SceneChangeDescription {
 
 	int8 paletteID = -1; // TVD only
 
-	Math::Vector3d listenerFrontVector;
+	Math::Vector3d listenerFrontVector = Math::Vector3d(0, 0, 1);
 	uint16 frontVectorFrameID = 0;
 
 	void readData(Common::SeekableReadStream &stream, bool longFormat = false);


Commit: 0f4ce356137479bc41c16e71e50fca8f7239d890
    https://github.com/scummvm/scummvm/commit/0f4ce356137479bc41c16e71e50fca8f7239d890
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Add support for nancy8 PianoPuzzle

The puzzle is used for the harmonica item, and adds
two special hover cursors.

Changed paths:
    engines/nancy/action/puzzle/orderingpuzzle.cpp
    engines/nancy/action/puzzle/orderingpuzzle.h


diff --git a/engines/nancy/action/puzzle/orderingpuzzle.cpp b/engines/nancy/action/puzzle/orderingpuzzle.cpp
index d7d03214e03..db372ed0530 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/orderingpuzzle.cpp
@@ -124,6 +124,13 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 		}
 	}
 
+	if (isPiano && g_nancy->getGameType() >= kGameTypeNancy8) {
+		_specialCursor1Id = stream.readUint16LE();
+		readRect(stream, _specialCursor1Dest);
+		_specialCursor2Id = stream.readUint16LE();
+		readRect(stream, _specialCursor2Dest);
+	}
+
 	uint sequenceLength = 5;
 	ser.syncAsUint16LE(sequenceLength, kGameTypeNancy1);
 
@@ -166,6 +173,9 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 
 		readRectArray(ser, _overlaySrcs, numOverlays);
 		readRectArray(ser, _overlayDests, numOverlays);
+	} else if (isPiano && g_nancy->getGameType() >= kGameTypeNancy8) {
+		readFilenameArray(stream, _pianoSoundNames, numElements);
+		stream.skip((maxNumElements - numElements) * 33);
 	}
 
 	if (ser.getVersion() > kGameTypeVampire) {
@@ -461,7 +471,14 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
 
 	for (int i = 0; i < (int)_hotspots.size(); ++i) {
 		if (NancySceneState.getViewport().convertViewportToScreen(_hotspots[i]).contains(input.mousePos)) {
-			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+			// Set the custom cursor for nancy8+ PianoPuzzle
+			if (NancySceneState.getViewport().convertViewportToScreen(_specialCursor1Dest).contains(input.mousePos)) {
+				g_nancy->_cursorManager->setCursorType((CursorManager::CursorType)_specialCursor1Id);
+			} else if (NancySceneState.getViewport().convertViewportToScreen(_specialCursor2Dest).contains(input.mousePos)) {
+				g_nancy->_cursorManager->setCursorType((CursorManager::CursorType)_specialCursor2Id);
+			} else {
+				g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+			}
 
 			if (canClick && input.input & NancyInput::kLeftMouseButtonUp) {
 				if (_puzzleType == kOrderItems) {
@@ -479,11 +496,18 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
 
 				if (_puzzleType == kPiano) {
 					// Set the correct sound name for every piano key
-					if (Common::isDigit(_pushDownSound.name.lastChar())) {
-						_pushDownSound.name.deleteLastChar();
-					}
+					if (g_nancy->getGameType() <= kGameTypeNancy7) {
+						// In earlier games, the sound name is the base sound + a number
+						if (Common::isDigit(_pushDownSound.name.lastChar())) {
+							_pushDownSound.name.deleteLastChar();
+						}
 
-					_pushDownSound.name.insertChar('0' + i, _pushDownSound.name.size());
+						_pushDownSound.name.insertChar('0' + i, _pushDownSound.name.size());
+					} else {
+						// Later games added an array of sound names
+						_pushDownSound.name = _pianoSoundNames[i];
+					}
+					
 					g_nancy->_sound->loadSound(_pushDownSound);
 				}
 
diff --git a/engines/nancy/action/puzzle/orderingpuzzle.h b/engines/nancy/action/puzzle/orderingpuzzle.h
index 8dcd6dfe222..7a44b51303a 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.h
+++ b/engines/nancy/action/puzzle/orderingpuzzle.h
@@ -72,6 +72,13 @@ protected:
 	Common::Array<Common::Rect> _hotspots;
 	Common::Array<uint16> _correctSequence;
 
+	uint16 _specialCursor1Id = CursorManager::kHotspot;
+	Common::Rect _specialCursor1Dest;
+	uint16 _specialCursor2Id = CursorManager::kHotspot;
+	Common::Rect _specialCursor2Dest;
+
+	Common::Array<Common::String> _pianoSoundNames; // nancy8 and up
+
 	uint16 _state2InvItem = 0;
 	Common::Array<Common::Rect> _overlaySrcs;
 	Common::Array<Common::Rect> _overlayDests;


Commit: b85ede6c9aaec6b0a92c9138b9d4a9a72fef89bb
    https://github.com/scummvm/scummvm/commit/b85ede6c9aaec6b0a92c9138b9d4a9a72fef89bb
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Correctly evaluate Conversation orFlags

The previous implementation of ConditionFlags' orFlag
checking did not account for chained orFlags. This struct
is seldom used, so it's unclear what, if anything, was
affected by this issue.

Changed paths:
    engines/nancy/action/conversation.cpp


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index c0f3ce77645..2a820dc7eb7 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -564,15 +564,31 @@ bool ConversationSound::ConversationFlags::isSatisfied() const {
 		if (conditionFlags[i].isSatisfied()) {
 			conditionsMet[i] = true;
 		}
+	}
+
+	for (uint i = 0; i < conditionsMet.size(); ++i) {
+		if (conditionFlags[i].orFlag) {
+			bool foundSatisfied = false;
+			for (uint j = 0; j < conditionFlags.size(); ++j) {
+				if (conditionsMet[j]) {
+					foundSatisfied = true;
+					break;
+				}
+
+				// Found end of orFlag chain
+				if (!conditionFlags[j].orFlag) {
+					break;
+				}
+			}
 
-		if (conditionFlags[i].orFlag && i < conditionFlags.size() - 1) {
-			if (conditionsMet[i] == true) {
-				conditionsMet[i + 1] = true;
-				++i;
-			} else if (conditionFlags[i + 1].isSatisfied()) {
-				conditionsMet[i] = true;
-				conditionsMet[i + 1] = true;
-				++i;
+			if (foundSatisfied) {
+				for (; i < conditionsMet.size(); ++i) {
+					conditionsMet[i] = true;
+					if (!conditionFlags[i].orFlag) {
+						// End of orFlag chain
+						break;
+					}
+				}
 			}
 		}
 	}


Commit: afbeb4ab8cdd822cd3f600219d14234b23b61388
    https://github.com/scummvm/scummvm/commit/afbeb4ab8cdd822cd3f600219d14234b23b61388
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Support Conversation response add rules

Conversation responses have rules for how they should
be added to the list (just add, add at end, or remove),
which until now have been ignored. It's unclear if these
have ever been used, since scanning for them is hard.
Also, conditional responses are now added after the
regular ones.

Changed paths:
    engines/nancy/action/conversation.cpp
    engines/nancy/action/conversation.h


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 2a820dc7eb7..357d3ce7ff4 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -90,7 +90,7 @@ void ConversationSound::readData(Common::SeekableReadStream &stream) {
 		response.conditionFlags.read(stream);
 		readResponseText(stream, response);
 		readFilename(stream, response.soundName);
-		ser.skip(1); // RESPONSE_ADD_IF_NOT_FOUND, RESPONSE_REMOVE_AND_ADD_TO_END, or RESPONSE_REMOVE
+		ser.syncAsByte(response.addRule);
 		response.sceneChange.readData(stream, ser.getVersion() == kGameTypeVampire);
 		ser.syncAsSint16LE(response.flagDesc.label);
 		ser.syncAsByte(response.flagDesc.flag);
@@ -262,6 +262,44 @@ void ConversationSound::execute() {
 				NancySceneState.getTextbox().addTextLine(_text);
 			}
 
+			Common::Array<uint> responsesToAdd;
+			for (uint i = 0; i < _responses.size(); ++i) {
+				auto &res = _responses[i];
+
+				if (res.conditionFlags.isSatisfied()) {
+					int foundIndex = -1;
+					for (uint j = 0; j < responsesToAdd.size(); ++j) {
+						if (_responses[responsesToAdd[j]].text.compareToIgnoreCase(res.text) == 0) {
+							foundIndex = j;
+							break;
+						}
+					}
+					switch(res.addRule) {
+					case ResponseStruct::kAddIfNotFound:
+						if (foundIndex == -1) {
+							responsesToAdd.push_back(i);
+						}
+
+						break;
+					case ResponseStruct::kRemoveAndAddToEnd:
+						if (foundIndex != -1) {
+							responsesToAdd.remove_at(foundIndex);
+						}
+
+						responsesToAdd.push_back(i);
+						break;
+					case ResponseStruct::kRemove:
+						if (foundIndex != -1) {
+							responsesToAdd.remove_at(foundIndex);
+						}
+
+						break;
+					}
+				}
+			}
+
+			uint numRegularResponses = _responses.size();
+
 			// Add responses when conditions have been satisfied
 			if (_conditionalResponseCharacterID != _noResponse) {
 				addConditionalDialogue();
@@ -271,13 +309,14 @@ void ConversationSound::execute() {
 				addGoodbye();
 			}
 
-			for (uint i = 0; i < _responses.size(); ++i) {
-				auto &res = _responses[i];
+			// If conditionals/goodbyes added, make sure to send them to Textbox
+			for (uint i = numRegularResponses; i < _responses.size(); ++i) {
+				responsesToAdd.push_back(i);
+			}
 
-				if (res.conditionFlags.isSatisfied()) {
-					NancySceneState.getTextbox().addTextLine(res.text);
-					res.isOnScreen = true;
-				}
+			for (uint i : responsesToAdd) {
+				NancySceneState.getTextbox().addTextLine(_responses[responsesToAdd[i]].text);
+				_responses[responsesToAdd[i]].isOnScreen = true;
 			}
 		}
 
diff --git a/engines/nancy/action/conversation.h b/engines/nancy/action/conversation.h
index 3349dc98155..5cc705f38f7 100644
--- a/engines/nancy/action/conversation.h
+++ b/engines/nancy/action/conversation.h
@@ -69,9 +69,12 @@ protected:
 	};
 
 	struct ResponseStruct {
+		enum AddRule { kAddIfNotFound, kRemoveAndAddToEnd, kRemove };
+
 		ConversationFlags conditionFlags;
 		Common::String text;
 		Common::String soundName;
+		byte addRule = kAddIfNotFound;
 		SceneChangeDescription sceneChange;
 		FlagDescription flagDesc;
 


Commit: 07a95628bbc17af75afab7d06c2ea40d64d0ea6c
    https://github.com/scummvm/scummvm/commit/07a95628bbc17af75afab7d06c2ea40d64d0ea6c
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-10T13:27:12+01:00

Commit Message:
NANCY: Fix nancy1 crash

Fixed a crash caused by the recent changes to the way
the clock is accessed.

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


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index fdf20cf389c..0f4dd8847e6 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -756,7 +756,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 
 UI::Clock *Scene::getClock() {
 	auto *clok = GetEngineData(CLOK);
-	if (clok->clockIsDisabled || clok->clockIsDay) {
+	if (!clok || clok->clockIsDisabled || clok->clockIsDay) {
 		return nullptr;
 	} else {
 		return (UI::Clock *)_clock;




More information about the Scummvm-git-logs mailing list