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

fracturehill noreply at scummvm.org
Tue Oct 3 14:59:04 UTC 2023


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

Summary:
824ba46990 NANCY: Implement terse EventFlags variants
8b473098aa NANCY: Implement HotSceneChangeTerse
ddc5378d80 NANCY: Simplify cursor logic and explain CURS structure
634b1eb260 NANCY: Rename sound ARs
dbdb30e5cc NANCY: Implement terse PlaySound variants
cb526a5eb3 NANCY: Fix kSceneCount dependency
b0bd3f1c3f NANCY: Implement viewable items in nancy7
57c9f5f203 NANCY: Implement EnableDisableInventory
d9e0bc954a NANCY: Move inventory-related records


Commit: 824ba469904ff1ed6aa3f16542deb05285dd8867
    https://github.com/scummvm/scummvm/commit/824ba469904ff1ed6aa3f16542deb05285dd8867
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:27+03:00

Commit Message:
NANCY: Implement terse EventFlags variants

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


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index e5ca8641ff7..7d86380d04d 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -155,6 +155,10 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 		return new TextBoxWrite();
 	case 76:
 		return new TextboxClear();
+	case 97:
+		return new EventFlags(true);
+	case 98:
+		return new EventFlagsMultiHS(true, true);
 	case 99:
 		return new EventFlagsMultiHS(true);
 	case 100:
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index d5df596b3ab..5b5c0aa22bc 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -258,7 +258,15 @@ void StopTimer::execute() {
 }
 
 void EventFlags::readData(Common::SeekableReadStream &stream) {
-	_flags.readData(stream);
+	if (!_isTerse) {
+		_flags.readData(stream);
+	} else {
+		// Terse version only has 2 flags
+		_flags.descs[0].label = stream.readSint16LE();
+		_flags.descs[0].flag = stream.readUint16LE();
+		_flags.descs[1].label = stream.readSint16LE();
+		_flags.descs[1].flag = stream.readUint16LE();
+	}
 }
 
 void EventFlags::execute() {
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index cd0dc23f0db..3d572f2afff 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -201,19 +201,23 @@ protected:
 // Sets up to 10 flags at once.
 class EventFlags : public ActionRecord {
 public:
+	EventFlags(bool terse = false) : _isTerse(terse) {}
+	virtual ~EventFlags() {}
+
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
 
 	MultiEventFlagDescription _flags;
+	bool _isTerse;
 
 protected:
-	Common::String getRecordTypeName() const override { return "EventFlags"; }
+	Common::String getRecordTypeName() const override { return _isTerse ? "EventFlagsTerse" : "EventFlags"; }
 };
 
 // Sets up to 10 flags when clicked. Hotspot can move alongside background frame.
 class EventFlagsMultiHS : public EventFlags {
 public:
-	EventFlagsMultiHS(bool isCursor) : _isCursor(isCursor) {}
+	EventFlagsMultiHS(bool isCursor, bool terse = false) : EventFlags(terse), _isCursor(isCursor) {}
 	virtual ~EventFlagsMultiHS() {}
 
 	void readData(Common::SeekableReadStream &stream) override;
@@ -228,7 +232,7 @@ public:
 
 protected:
 	bool canHaveHotspot() const override { return true; }
-	Common::String getRecordTypeName() const override { return _isCursor ? "EventFlagsCursorHS" : "EventFlagsMultiHS"; }
+	Common::String getRecordTypeName() const override { return _isCursor ? (_isTerse ? "EventFlagsHSTerse" : "EventFlagsCursorHS") : "EventFlagsMultiHS"; }
 };
 
 // Returns the player back to the main menu


Commit: 8b473098aadd27dfc6d92586bba7b616e3bae36b
    https://github.com/scummvm/scummvm/commit/8b473098aadd27dfc6d92586bba7b616e3bae36b
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Implement HotSceneChangeTerse

Added support for a short variant of Hot1FrSceneChange,
which combines all the directional scene changes into one.
Due to the weird way the data is combined, significant
changes to the way ActionRecords are created had to be made.

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


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index a0d67d3e6f8..02c72226031 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -134,42 +134,42 @@ void ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 }
 
 ActionRecord *ActionManager::createAndLoadNewRecord(Common::SeekableReadStream &inputData) {
-	inputData.seek(0x30);
-	byte ARType = inputData.readByte();
-	ActionRecord *newRecord = createActionRecord(ARType);
-
-	if (!newRecord) {
-		return nullptr;
-	}
-
 	inputData.seek(0);
 	char descBuf[0x30];
 	inputData.read(descBuf, 0x30);
 	descBuf[0x2F] = '\0';
-	newRecord->_description = descBuf;
+	byte ARType = inputData.readByte();
+	byte execType = inputData.readByte();
+	ActionRecord *newRecord = createActionRecord(ARType, &inputData);
 
-	newRecord->_type = inputData.readByte(); // redundant
-	newRecord->_execType = (ActionRecord::ExecutionType)inputData.readByte();
+	if (!newRecord) {
+		newRecord = new Unimplemented();
+	}
+	
+	newRecord->_description = descBuf;
+	newRecord->_type = ARType;
+	newRecord->_execType = (ActionRecord::ExecutionType)execType;
 
-	uint16 localChunkSize = inputData.pos();
 	newRecord->readData(inputData);
-	localChunkSize = inputData.pos() - localChunkSize;
-	localChunkSize += 0x32;
 
-	// 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) {
+	// If the remaining data is less than the total data, there must be dependencies at the end of the chunk
+	int64 dataRemaining = inputData.size() - inputData.pos();
+	if (dataRemaining > 0 && newRecord->getRecordTypeName() != "Unimplemented") {
 		// 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) {
+		uint numDependencies = dataRemaining / singleDepSize;
+		if (dataRemaining % singleDepSize) {
 			warning("Action record type %u, %s has incorrect read size!\ndescription:\n%s",
 				newRecord->_type,
 				newRecord->getRecordTypeName().c_str(),
 				newRecord->_description.c_str());
 
 				delete newRecord;
-				return nullptr;
+				
+				newRecord = new Unimplemented();
+				newRecord->_description = descBuf;
+				newRecord->_type = ARType;
+				newRecord->_execType = (ActionRecord::ExecutionType)execType;
 		}
 
 		if (numDependencies == 0) {
@@ -180,7 +180,6 @@ ActionRecord *ActionManager::createAndLoadNewRecord(Common::SeekableReadStream &
 		depStack.push(&newRecord->_dependencies);
 
 		// Initialize the dependencies data
-		inputData.seek(localChunkSize);
 		for (uint16 i = 0; i < numDependencies; ++i) {
 			depStack.top()->children.push_back(DependencyRecord());
 			DependencyRecord &dep = depStack.top()->children.back();
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index 8f410cc13dd..da11ad68614 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -72,7 +72,7 @@ public:
 	void synchronize(Common::Serializer &serializer);
 
 protected:
-	static ActionRecord *createActionRecord(uint16 type);
+	static ActionRecord *createActionRecord(uint16 type, Common::SeekableReadStream *recordStream = nullptr);
 	static ActionRecord *createAndLoadNewRecord(Common::SeekableReadStream &inputData);
 
 	void synchronizeMovieWithSound();
diff --git a/engines/nancy/action/actionrecord.cpp b/engines/nancy/action/actionrecord.cpp
index 8cb1ff40c64..763bb517538 100644
--- a/engines/nancy/action/actionrecord.cpp
+++ b/engines/nancy/action/actionrecord.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/stack.h"
+#include "engines/nancy/detection.h"
 #include "engines/nancy/action/actionrecord.h"
 
 namespace Nancy {
@@ -53,5 +54,10 @@ void ActionRecord::finishExecution() {
 	}
 }
 
+void Unimplemented::execute() {
+	debugC(Nancy::kDebugActionRecord, "Unimplemented or changed ActionRecord type %u", _type);
+	_isDone = true;
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 9ed7c21c653..5e79c5cbbb8 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -155,6 +155,13 @@ public:
 	void onPause(bool pause) override { if (!pause) registerGraphics(); }
 };
 
+// Dummy AR for classes that haven't been implemented/don't work in the current game version
+class Unimplemented : public ActionRecord {
+	void execute() override;
+	void readData(Common::SeekableReadStream &stream) override {}
+	Common::String getRecordTypeName() const override { return "Unimplemented"; }
+};
+
 } // End of namespace Action
 } // End of namespace Nancy
 
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 7d86380d04d..cbeffaf507a 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -59,7 +59,7 @@
 namespace Nancy {
 namespace Action {
 
-ActionRecord *ActionManager::createActionRecord(uint16 type) {
+ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableReadStream *recordStream) {
 	switch (type) {
 	case 10:
 		return new Hot1FrSceneChange(CursorManager::kHotspot);
@@ -99,6 +99,18 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 		return new Hot1FrSceneChange(CursorManager::kMoveRight);
 	case 24:
 		return new HotMultiframeMultisceneCursorTypeSceneChange();
+	case 25: {
+		// Weird case; instead of storing the cursor id, they instead chose to store
+		// an AR id corresponding to one of the directional Hot1FrSceneChange variants.
+		// Thus, we need to scan the incoming chunk and make another call to createActionRecord().
+		// This is not the most elegant solution, but it works :)
+		assert(recordStream);
+		uint16 innerID = recordStream->readUint16LE();
+		Hot1FrSceneChange *newRec = dynamic_cast<Hot1FrSceneChange *>(createActionRecord(innerID));
+		assert(newRec);
+		newRec->_isTerse = true;
+		return newRec;
+	}
 	case 40:
 		if (g_nancy->getGameType() < kGameTypeNancy2) {
 			// Only used in TVD
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 5b5c0aa22bc..0488e1d4496 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -34,11 +34,6 @@
 namespace Nancy {
 namespace Action {
 
-void Unimplemented::execute() {
-	debugC(Nancy::kDebugActionRecord, "Unimplemented Action Record type %s", getRecordTypeName().c_str());
-	_isDone = true;
-}
-
 void PaletteThisScene::readData(Common::SeekableReadStream &stream) {
 	_paletteID = stream.readByte();
 	_unknownEnum = stream.readByte();
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 3d572f2afff..f30a9fe9ea4 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -30,10 +30,6 @@ class NancyEngine;
 
 namespace Action {
 
-class Unimplemented : public ActionRecord {
-	void execute() override;
-};
-
 // Changes the palette for the current scene's background. TVD only.
 class PaletteThisScene : public ActionRecord {
 public:
diff --git a/engines/nancy/action/navigationrecords.cpp b/engines/nancy/action/navigationrecords.cpp
index db009a0d3f1..5b086b84743 100644
--- a/engines/nancy/action/navigationrecords.cpp
+++ b/engines/nancy/action/navigationrecords.cpp
@@ -72,8 +72,15 @@ void HotMultiframeSceneChange::execute() {
 }
 
 void Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
-	SceneChange::readData(stream);
-	_hotspotDesc.readData(stream);
+	if (!_isTerse) {
+		SceneChange::readData(stream);
+		_hotspotDesc.readData(stream);
+	} else {
+		_sceneChange.sceneID = stream.readUint16LE();
+		_sceneChange.continueSceneSound = kContinueSceneSound;
+		_sceneChange.listenerFrontVector.set(0, 0, 1);
+		readRect(stream, _hotspotDesc.coords);
+	}
 }
 
 void Hot1FrSceneChange::execute() {
diff --git a/engines/nancy/action/navigationrecords.h b/engines/nancy/action/navigationrecords.h
index c88f38922a5..85c06131a57 100644
--- a/engines/nancy/action/navigationrecords.h
+++ b/engines/nancy/action/navigationrecords.h
@@ -86,10 +86,15 @@ public:
 	CursorManager::CursorType getHoverCursor() const override { return _hoverCursor; }
 
 	HotspotDescription _hotspotDesc;
+	bool _isTerse = false;
 
 protected:
 	bool canHaveHotspot() const override { return true; }
 	Common::String getRecordTypeName() const override {
+		if (_isTerse) {
+			return "HotSceneChangeTerse";
+		}
+
 		switch (_hoverCursor) {
 		case CursorManager::kExit:
 			return "Hot1FrExitSceneChange";


Commit: ddc5378d80943c36ac4869836197d904e02cef20
    https://github.com/scummvm/scummvm/commit/ddc5378d80943c36ac4869836197d904e02cef20
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Simplify cursor logic and explain CURS structure

Changed paths:
    engines/nancy/cursor.cpp


diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 80a9fb33e01..81c2e2a4dc8 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -40,15 +40,42 @@ CursorManager::CursorManager()  :
 
 void CursorManager::init(Common::SeekableReadStream *chunkStream) {
 	assert(chunkStream);
-
 	chunkStream->seek(0);
 
+	// First, we need to figure out the number of possible CursorTypes in the current game
+	// These grew as the engine got more complicated, so we use nancy.dat to store a related property
+	// TODO: Change nancy.dat so it just directly stores the number of cursor types
 	if (g_nancy->getGameType() == kGameTypeVampire) {
 		_numCursorTypes = g_nancy->getStaticData().numNonItemCursors / 2;
 	} else {
 		_numCursorTypes = g_nancy->getStaticData().numNonItemCursors / 3;
 	}
 
+	// The structure of CURS is weird:
+
+	// The data is divided in half: first half is source rectangles, second half is hotspots (all of which are identical...)
+	// However, each of those halves are divided into a number of arrays, each one of size _numCursorTypes.
+
+	// The first few arrays are the following:
+	// - an array of cursors used when the mouse is in the VIEWPORT (hourglass, directional arrows, etc.)
+	// - an array of cursors used in the FRAME
+	// - an array of cursors used in MENUS (not present in TVD)
+	// The only frame cursors used are the first two: the classic arrow cursor, and its hotspot variant, which is slightly shorter
+	// The same applies to the menu cursors; however, we completely ignore those (technically the arrow cursor has sliiiiightly
+	// different shading from the one in the frame array, but I don't care enough to implement it).
+	
+	// Following those are the ITEM arrays; these cursors are used to indicate that the player is holding an item.
+	// Their number is the same as the number of items described in INV, and their size is also _numCursorTypes.
+	// Out of those arrays, the only cursors that get used are the kNormal and kHotspot ones. The first few games also
+	// had kMove item cursors, but the Move cursors quickly fell out of use.
+
+	// Due to the logic in setCursor(), directional arrow cursors found in the VIEWPORT array take precedence over
+	// the ones in the item arrays. As a result, most of the CURS data is effectively junk that never gets used.
+
+	// Perhaps in the future the class could be modified so we no longer have to store or care about all of the junk cursors;
+	// however, this cannot happen until the engine is more mature and I'm more aware of what changes they made to the
+	// cursor code in later games.
+
 	uint numCursors = g_nancy->getStaticData().numNonItemCursors + g_nancy->getStaticData().numItems * _numCursorTypes;
 	_cursors.resize(numCursors);
 
@@ -103,32 +130,10 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 	// value of the CursorType enum.
 	switch (type) {
 	case kNormalArrow:
-		if (gameType <= kGameTypeNancy1) {
-			_curCursorID = 4;
-		} else if (gameType == kGameTypeNancy2) {
-			_curCursorID = 5;
-		} else if (gameType ==  kGameTypeNancy3) {
-			_curCursorID = 8;
-		} else if (gameType <= kGameTypeNancy5) {
-			_curCursorID = 12;
-		} else {
-			_curCursorID = 16;
-		}
-
+		_curCursorID = _numCursorTypes;
 		return;
 	case kHotspotArrow:
-		if (gameType <= kGameTypeNancy1) {
-			_curCursorID = 5;
-		} else if (gameType == kGameTypeNancy2) {
-			_curCursorID = 6;
-		} else if (gameType == kGameTypeNancy3) {
-			_curCursorID = 9;
-		} else if (gameType <= kGameTypeNancy5) {
-			_curCursorID = 13;
-		} else {
-			_curCursorID = 17;
-		}
-
+		_curCursorID = _numCursorTypes + 1;
 		return;
 	case kInvertedRotateLeft:
 		// Only valid for nancy6 and up


Commit: 634b1eb2604eb3ca45f7433015c7ed2a39b5ddb4
    https://github.com/scummvm/scummvm/commit/634b1eb2604eb3ca45f7433015c7ed2a39b5ddb4
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Rename sound ARs

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/soundrecords.cpp
    engines/nancy/action/soundrecords.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 02c72226031..ebff6fba5d2 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -581,7 +581,7 @@ void ActionManager::synchronizeMovieWithSound() {
 	// The heuristic for catching these cases relies on the scene having a movie and a sound
 	// record start at the same frame, and have a (valid) scene change to the same scene.
 	PlaySecondaryMovie *movie = nullptr;
-	PlayDigiSound *sound = nullptr;
+	PlaySound *sound = nullptr;
 
 	for (uint i = 0; i < _activatedRecordsThisFrame.size(); ++i) {
 		byte type = _activatedRecordsThisFrame[i]->_type;
@@ -589,7 +589,7 @@ void ActionManager::synchronizeMovieWithSound() {
 		if (type == 53) {
 			movie = (PlaySecondaryMovie *)_activatedRecordsThisFrame[i];
 		} else if (type == 150 || type == 151 || type == 157) {
-			sound = (PlayDigiSound *)_activatedRecordsThisFrame[i];
+			sound = (PlaySound *)_activatedRecordsThisFrame[i];
 		}
 
 		if (movie && sound) {
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index cbeffaf507a..1197fdacd58 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -230,11 +230,11 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 123:
 		return new InventorySoundOverride();
 	case 150:
-		return new PlayDigiSound();
+		return new PlaySound();
 	case 151:
-		return new PlayDigiSound();
+		return new PlaySound();
 	case 152:
-		return new PlaySoundPanFrameAnchorAndDie();
+		return new PlaySoundFrameAnchor();
 	case 153:
 		return new PlaySoundMultiHS();
 	case 154:
@@ -242,7 +242,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 155:
 		return new StopSound(); // StopAndUnloadSound, but we always unload
 	case 157:
-		return new PlayDigiSoundCC();
+		return new PlaySoundCC();
 	case 158:
 		return new PlayRandomSound();
 	case 160:
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index 443c87cd7bc..ba073a1cc67 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -32,7 +32,7 @@
 namespace Nancy {
 namespace Action {
 
-void PlayDigiSound::readData(Common::SeekableReadStream &stream) {
+void PlaySound::readData(Common::SeekableReadStream &stream) {
 	_sound.readDIGI(stream);
 
 	if (g_nancy->getGameType() >= kGameTypeNancy3) {
@@ -51,7 +51,7 @@ void PlayDigiSound::readData(Common::SeekableReadStream &stream) {
 	stream.skip(2); // VIDEO_STOP_RENDERING, VIDEO_CONTINUE_RENDERING
 }
 
-void PlayDigiSound::execute() {
+void PlaySound::execute() {
 	switch (_state) {
 	case kBegin:
 		g_nancy->_sound->loadSound(_sound, &_soundEffect);
@@ -82,12 +82,18 @@ void PlayDigiSound::execute() {
 	}
 }
 
-Common::String PlayDigiSound::getRecordTypeName() const {
-	return g_nancy->getGameType() <= kGameTypeNancy2 ? "PlayDigiSoundAndDie" : "PlayDigiSound";
+Common::String PlaySound::getRecordTypeName() const {
+	if (g_nancy->getGameType() <= kGameTypeNancy2) {
+		return "PlayDigiSoundAndDie";
+	} else if (g_nancy->getGameType() <= kGameTypeNancy5) {
+		return "PlayDigiSound";
+	} else {
+		return "PlaySound";
+	}
 }
 
-void PlayDigiSoundCC::readData(Common::SeekableReadStream &stream) {
-	PlayDigiSound::readData(stream);
+void PlaySoundCC::readData(Common::SeekableReadStream &stream) {
+	PlaySound::readData(stream);
 
 	uint16 textSize = stream.readUint16LE();
 	if (textSize) {
@@ -98,26 +104,42 @@ void PlayDigiSoundCC::readData(Common::SeekableReadStream &stream) {
 	}
 }
 
-void PlayDigiSoundCC::execute() {
+void PlaySoundCC::execute() {
 	if (_state == kBegin) {
 		NancySceneState.getTextbox().clear();
 		NancySceneState.getTextbox().addTextLine(_ccText);
 	}
-	PlayDigiSound::execute();
+	PlaySound::execute();
+}
+
+Common::String PlaySoundCC::getRecordTypeName() const {
+	if (g_nancy->getGameType() <= kGameTypeNancy5) {
+		return "PlayDigiSoundCC";
+	} else {
+		return "PlaySoundCC";
+	}
 }
 
-void PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) {
+void PlaySoundFrameAnchor::readData(Common::SeekableReadStream &stream) {
 	_sound.readDIGI(stream);
 	stream.skip(2);
 	_sound.isPanning = true;
 }
 
-void PlaySoundPanFrameAnchorAndDie::execute() {
+void PlaySoundFrameAnchor::execute() {
 	g_nancy->_sound->loadSound(_sound);
 	g_nancy->_sound->playSound(_sound);
 	_isDone = true;
 }
 
+Common::String PlaySoundFrameAnchor::getRecordTypeName() const {
+	if (g_nancy->getGameType() <= kGameTypeNancy2) {
+		return "PlaySoundPanFrameAnchorAndDie";
+	} else {
+		return "PlaySoundFrameAnchor";
+	}
+}
+
 void PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
 	_sound.readNormal(stream);
 
@@ -184,7 +206,7 @@ void PlayRandomSound::readData(Common::SeekableReadStream &stream) {
 	uint16 numSounds = stream.readUint16LE();
 	readFilenameArray(stream, _soundNames, numSounds - 1);
 
-	PlayDigiSound::readData(stream);
+	PlaySound::readData(stream);
 	_soundNames.push_back(_sound.name);
 }
 
@@ -193,12 +215,12 @@ void PlayRandomSound::execute() {
 		_sound.name = _soundNames[g_nancy->_randomSource->getRandomNumber(_soundNames.size() - 1)];
 	}
 
-	PlayDigiSound::execute();
+	PlaySound::execute();
 }
 
 void TableIndexPlaySound::readData(Common::SeekableReadStream &stream) {
 	_tableIndex = stream.readUint16LE();
-	PlayDigiSound::readData(stream); // Data does NOT contain captions, so we call the PlayDigiSound version
+	PlaySound::readData(stream); // Data does NOT contain captions, so we call the PlaySound version
 }
 
 void TableIndexPlaySound::execute() {
@@ -215,7 +237,7 @@ void TableIndexPlaySound::execute() {
 		_ccText = tabl->strings[playerTable->currentIDs[_tableIndex - 1] - 1];
 	}
 
-	PlayDigiSoundCC::execute();
+	PlaySoundCC::execute();
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index b7d9ddbe431..69d13552a53 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -30,10 +30,10 @@ namespace Action {
 // Used for sound effects. From nancy3 up it includes 3D sound data, which lets
 // the sound move in 3D space as the player rotates/changes scenes. Also supports
 // changing the scene and/or setting a flag
-class PlayDigiSound : public ActionRecord {
+class PlaySound : public ActionRecord {
 public:
-	PlayDigiSound() {}
-	~PlayDigiSound() { delete _soundEffect; }
+	PlaySound() {}
+	~PlaySound() { delete _soundEffect; }
 	
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
@@ -48,9 +48,9 @@ protected:
 	Common::String getRecordTypeName() const override;
 };
 
-// The same as PlayDigiSound, but with the addition of captioning text,
+// The same as PlaySound, but with the addition of captioning text,
 // which gets displayed inside the Textbox.
-class PlayDigiSoundCC : public PlayDigiSound {
+class PlaySoundCC : public PlaySound {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
@@ -58,12 +58,12 @@ public:
 	Common::String _ccText;
 
 protected:
-	Common::String getRecordTypeName() const override { return "PlayDigiSoundCC"; }
+	Common::String getRecordTypeName() const override;
 };
 
 // Used for sounds that pan left-right depending on the scene background frame.
-// Only used in The Vampire Diaries; later games use PlayDigiSound's 3D sound capabilities instead
-class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
+// Only used in The Vampire Diaries; later games use PlaySound's 3D sound capabilities instead
+class PlaySoundFrameAnchor : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
@@ -71,7 +71,7 @@ public:
 	SoundDescription _sound;
 
 protected:
-	Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; }
+	Common::String getRecordTypeName() const override;
 };
 
 // Plays a sound effect; has multiple hotspots, one per scene background frame.
@@ -105,9 +105,9 @@ protected:
 	Common::String getRecordTypeName() const override { return "StopSound"; }
 };
 
-// Same as PlayDigiSound, except it randomly picks between one of several
+// Same as PlaySound, except it randomly picks between one of several
 // provided sound files; all other settings for the sound are shared.
-class PlayRandomSound : public PlayDigiSound {
+class PlayRandomSound : public PlaySound {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
@@ -120,11 +120,11 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlayRandomSound"; }
 };
 
-// Same as PlayDigiSound, except it discards the filename provided in the data.
+// Same as PlaySound, except it discards the filename provided in the data.
 // Instead, it takes the current value of an item in TableData, and appends that
 // value to the end of the base filename provided in the TABL chunk. Does not contain
 // any CC text inside the record data; instead, that also gets copied over from TABL.
-class TableIndexPlaySound : public PlayDigiSoundCC {
+class TableIndexPlaySound : public PlaySoundCC {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;


Commit: dbdb30e5cc833877fa9da8a9004ccc26b9d3f3fe
    https://github.com/scummvm/scummvm/commit/dbdb30e5cc833877fa9da8a9004ccc26b9d3f3fe
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Implement terse PlaySound variants

Added support for PlaySoundTerse and
PlayRandomSoundTerse, as introduced in nancy7. Also
implemented the new functionality for reading closed
captioning text from the Autotext chunk.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/soundrecords.cpp
    engines/nancy/action/soundrecords.h
    engines/nancy/commontypes.cpp
    engines/nancy/commontypes.h
    engines/nancy/sound.h
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 1197fdacd58..90e38ff3a13 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -232,7 +232,11 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 150:
 		return new PlaySound();
 	case 151:
-		return new PlaySound();
+		if (g_nancy->getGameType() <= kGameTypeNancy6)  {
+			return new PlaySound(); // PlayStreamSound
+		} else {
+			return new PlayRandomSoundTerse();
+		}		
 	case 152:
 		return new PlaySoundFrameAnchor();
 	case 153:
@@ -245,6 +249,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new PlaySoundCC();
 	case 158:
 		return new PlayRandomSound();
+	case 159:
+		return new PlaySoundTerse();
 	case 160:
 		return new HintSystem();
 	case 170:
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index ba073a1cc67..138013e7988 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/random.h"
+#include "common/config-manager.h"
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/sound.h"
@@ -94,24 +95,36 @@ Common::String PlaySound::getRecordTypeName() const {
 
 void PlaySoundCC::readData(Common::SeekableReadStream &stream) {
 	PlaySound::readData(stream);
-
-	uint16 textSize = stream.readUint16LE();
-	if (textSize) {
-		char *strBuf = new char[textSize];
-		stream.read(strBuf, textSize);
-		assembleTextLine(strBuf, _ccText, textSize);
-		delete[] strBuf;
-	}
+	readCCText(stream, _ccText);
 }
 
 void PlaySoundCC::execute() {
-	if (_state == kBegin) {
+	if (_state == kBegin && _ccText.size() && ConfMan.getBool("subtitles", ConfMan.getActiveDomainName())) {
 		NancySceneState.getTextbox().clear();
 		NancySceneState.getTextbox().addTextLine(_ccText);
 	}
 	PlaySound::execute();
 }
 
+void PlaySoundCC::readCCText(Common::SeekableReadStream &stream, Common::String &out) {
+	int16 textSize = stream.readUint16LE();
+
+	if (textSize > 0) {
+		char *strBuf = new char[textSize];
+		stream.read(strBuf, textSize);
+		assembleTextLine(strBuf, out, textSize);
+		delete[] strBuf;
+	} else if (textSize == -1) {
+		// Text is in Autotext chunk
+		Common::String key;
+		readFilename(stream, key);
+		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
+		assert(autotext);
+
+		out = autotext->texts[key];
+	}
+}
+
 Common::String PlaySoundCC::getRecordTypeName() const {
 	if (g_nancy->getGameType() <= kGameTypeNancy5) {
 		return "PlayDigiSoundCC";
@@ -120,6 +133,17 @@ Common::String PlaySoundCC::getRecordTypeName() const {
 	}
 }
 
+void PlaySoundTerse::readData(Common::SeekableReadStream &stream) {
+	_sound.readTerse(stream);
+	_changeSceneImmediately = stream.readByte();
+	_sceneChange.sceneID = stream.readUint16LE();
+
+	_sceneChange.continueSceneSound = kContinueSceneSound;
+	_soundEffect = new SoundEffectDescription;
+
+	readCCText(stream, _ccText);
+}
+
 void PlaySoundFrameAnchor::readData(Common::SeekableReadStream &stream) {
 	_sound.readDIGI(stream);
 	stream.skip(2);
@@ -218,6 +242,32 @@ void PlayRandomSound::execute() {
 	PlaySound::execute();
 }
 
+void PlayRandomSoundTerse::readData(Common::SeekableReadStream &stream) {
+	uint16 numSounds = stream.readUint16LE();
+	readFilenameArray(stream, _soundNames, numSounds - 1);
+
+	PlaySoundTerse::readData(stream);
+
+	// The call above will have read the last sound name and the first cc text
+	_soundNames.push_back(_sound.name);
+	_ccTexts.push_back(_ccText);
+
+	for (int i = 0; i < numSounds - 1; ++i) {
+		_ccTexts.push_back(Common::String());
+		readCCText(stream, _ccTexts.back());
+	}
+}
+
+void PlayRandomSoundTerse::execute() {
+	if (_state == kBegin) {
+		uint16 randomID = g_nancy->_randomSource->getRandomNumber(_soundNames.size() - 1);
+		_sound.name = _soundNames[randomID];;
+		_ccText = _ccTexts[randomID];
+	}
+
+	PlaySoundCC::execute();
+}
+
 void TableIndexPlaySound::readData(Common::SeekableReadStream &stream) {
 	_tableIndex = stream.readUint16LE();
 	PlaySound::readData(stream); // Data does NOT contain captions, so we call the PlaySound version
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index 69d13552a53..cdddf1b1862 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -55,12 +55,23 @@ public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
 
+	void readCCText(Common::SeekableReadStream &stream, Common::String &out);
+
 	Common::String _ccText;
 
 protected:
 	Common::String getRecordTypeName() const override;
 };
 
+// Short version of PlaySoundCC
+class PlaySoundTerse : public PlaySoundCC {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "PlaySoundTerse"; }
+};
+
 // Used for sounds that pan left-right depending on the scene background frame.
 // Only used in The Vampire Diaries; later games use PlaySound's 3D sound capabilities instead
 class PlaySoundFrameAnchor : public ActionRecord {
@@ -120,6 +131,21 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlayRandomSound"; }
 };
 
+// Short version of PlayRandomSound, but ALSO supports closed captioning text
+class PlayRandomSoundTerse : public PlaySoundTerse {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	Common::Array<Common::String> _soundNames;
+	Common::Array<Common::String> _ccTexts;
+
+	uint _selectedSound = 0;
+
+protected:
+	Common::String getRecordTypeName() const override { return "PlayRandomSoundTerse"; }	
+};
+
 // Same as PlaySound, except it discards the filename provided in the data.
 // Instead, it takes the current value of an item in TableData, and appends that
 // value to the end of the base filename provided in the TABL chunk. Does not contain
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 20d48912296..ed1703c5bf3 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -255,6 +255,14 @@ void SoundDescription::readScene(Common::SeekableReadStream &stream) {
 	s.syncAsUint32LE(samplesPerSec, kGameTypeVampire, kGameTypeNancy2);
 }
 
+void SoundDescription::readTerse(Common::SeekableReadStream &stream) {
+	readFilename(stream, name);
+	channelID = stream.readUint16LE();
+	numLoops = stream.readUint32LE();
+	volume = stream.readUint16LE();
+	stream.skip(2);
+}
+
 void ConditionalDialogue::readData(Common::SeekableReadStream &stream) {
 	textID = stream.readByte();
 	sceneID = stream.readUint16LE();
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 8f204114ae4..bebf9e42038 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -113,6 +113,11 @@ static const byte kNoChangeTableValue				= 0;
 static const byte kIncrementTableValue				= 1;
 static const byte kDecrementTableValue				= 2;
 
+// 3D sound rotation
+static const byte kRotateAroundX					= 0;
+static const byte kRotateAroundY					= 1;
+static const byte kRotateAroundZ					= 2;
+
 enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
 
 // Separate namespace to remove possible clashes
@@ -204,9 +209,10 @@ struct SecondaryVideoDescription {
 };
 
 // Describes set of effects that can be applied to sounds.
+// Defaults are set according to the values used by PlaySoundTerse
 struct SoundEffectDescription {
-	uint32 minTimeDelay = 0;
-	uint32 maxTimeDelay = 0;
+	uint32 minTimeDelay = 500;
+	uint32 maxTimeDelay = 2000;
 
 	int32 randomMoveMinX = 0;
 	int32 randomMoveMaxX = 0;
@@ -219,8 +225,8 @@ struct SoundEffectDescription {
 	int32 fixedPosY = 0;
 	int32 fixedPosZ = 0;
 
-	uint32 moveStepTime = 0;
-	int32 numMoveSteps = 0;
+	uint32 moveStepTime = 1000;
+	int32 numMoveSteps = 10;
 
 	int32 linearMoveStartX = 0;
 	int32 linearMoveEndX = 0;
@@ -232,7 +238,7 @@ struct SoundEffectDescription {
 	int32 rotateMoveStartX = 0;
 	int32 rotateMoveStartY = 0;
 	int32 rotateMoveStartZ = 0;
-	byte rotateMoveAxis = 0;
+	byte rotateMoveAxis = kRotateAroundY;
 
 	uint32 minDistance = 0;
 	uint32 maxDistance = 0;
@@ -255,6 +261,7 @@ struct SoundDescription {
 	void readDIGI(Common::SeekableReadStream &stream);
 	void readMenu(Common::SeekableReadStream &stream);
 	void readScene(Common::SeekableReadStream &stream);
+	void readTerse(Common::SeekableReadStream &stream);
 };
 
 // Structs inside nancy.dat, which contains all the data that was
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index ea9a32f18e9..f51d7084a03 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -59,12 +59,6 @@ public:
 		kPlayMoveCircular			= 0x0300,	// Move sound position in a circular direction (see SoundRotationAxis)
 		kPlayRandomMove				= 0x0500	// Move along random vector. Does not combine with kPlayMoveCircular
 	};
-
-	enum SoundRotationAxis {
-		kRotateAroundX = 0,
-		kRotateAroundY = 1,
-		kRotateAroundZ = 2
-	};
 	
 	SoundManager();
 	~SoundManager();
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 9ff039d7be1..6b4c792edba 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -105,11 +105,7 @@ void Scene::SceneSummary::readTerse(Common::SeekableReadStream &stream) {
 	stream.read(buf, 0x32);
 	description = buf;
 	readFilename(stream, videoFile);
-	readFilename(stream, sound.name);
-	sound.channelID = stream.readUint16LE();
-	sound.numLoops = stream.readUint32LE();
-	sound.volume = stream.readUint16LE();
-	stream.skip(2);
+	sound.readTerse(stream);
 }
 
 Scene::Scene() :


Commit: cb526a5eb3245553a1d3c8b078ff0677bd3045c6
    https://github.com/scummvm/scummvm/commit/cb526a5eb3245553a1d3c8b078ff0677bd3045c6
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Fix kSceneCount dependency

It seems the kSceneCount dependency type has been
broken pretty much always, and the last attempt at fixing it
was a fluke. Also added checks for nancy7, since the devs
decided that, for whatever reason, the conditions need to
be flipped, and what was before a less-than check is now a
greater-than one, and vice versa.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/console.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index ebff6fba5d2..f1f7c4b2807 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -392,23 +392,26 @@ void ActionManager::processDependency(DependencyRecord &dep, ActionRecord &recor
 		case DependencyType::kSceneCount: {
 			// Check how many times a scene has been visited.
 			// This dependency type keeps its data in the time variables
+			// Note: nancy7 completely flipped the meaning of 1 and 2
 			int count = NancySceneState._flags.sceneCounts.contains(dep.hours) ?
 				NancySceneState._flags.sceneCounts[dep.hours] : 0;
 			switch (dep.milliseconds) {
 			case 1:
-				if (dep.seconds < count) {
+				if (	(dep.minutes < count && g_nancy->getGameType() <= kGameTypeNancy6) ||
+						(dep.minutes > count && g_nancy->getGameType() >= kGameTypeNancy7)) {
 					dep.satisfied = true;
 				}
 
 				break;
 			case 2:
-				if (dep.seconds > count) {
+				if (	(dep.minutes > count && g_nancy->getGameType() <= kGameTypeNancy6) ||
+						(dep.minutes < count && g_nancy->getGameType() >= kGameTypeNancy7)) {
 					dep.satisfied = true;
 				}
 
 				break;
 			case 3:
-				if (dep.seconds == count) {
+				if (dep.minutes == count) {
 					dep.satisfied = true;
 				}
 
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 9defb007242..3db52724682 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -530,7 +530,7 @@ void NancyConsole::recursePrintDependencies(const Nancy::Action::DependencyRecor
 			debugPrintf("kSceneCount, scene ID %i, hit count %s %i",
 				dep.hours,
 				dep.milliseconds == 1 ? ">" : dep.milliseconds == 2 ? "<" : "==",
-				dep.seconds);
+				dep.minutes);
 			break;
 		case DependencyType::kElapsedPlayerDay :
 			debugPrintf("kElapsedPlayerDay");
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 6b4c792edba..0ee5fe7ac1f 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -232,11 +232,6 @@ void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
 		return;
 	}
 
-	// Increment scene count on scene exit in order to:
-	// - keep values needed for the kSceneCount dependency correct
-	// - make sure we don't increment when loading a save
-	_flags.sceneCounts.getOrCreateVal(_sceneState.currentScene.sceneID)++;
-
 	_sceneState.nextScene = sceneDescription;
 	_state = kLoad;
 }
@@ -579,7 +574,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 
 		g_nancy->_sound->stopAllSounds();
 
-		load();
+		load(true);
 	}
 
 	ser.syncAsUint16LE(_sceneState.pushedScene.sceneID);
@@ -803,7 +798,7 @@ PuzzleData *Scene::getPuzzleData(const uint32 tag) {
 	}
 }
 
-void Scene::load() {
+void Scene::load(bool fromSaveFile) {
 	if (_specialEffects.size()) {
 		_specialEffects.front().onSceneChange();
 	}
@@ -900,6 +895,12 @@ void Scene::load() {
 	_timers.sceneTime = 0;
 	g_nancy->_sound->recalculateSoundEffects();
 
+	// Increment the number of times we've visited this scene, unless we're
+	// loading from a save
+	if (!fromSaveFile) {
+		_flags.sceneCounts.getOrCreateVal(_sceneState.currentScene.sceneID)++;
+	}
+
 	_state = kStartSound;
 }
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 4c911dda345..74891e27955 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -200,7 +200,7 @@ public:
 
 private:
 	void init();
-	void load();
+	void load(bool fromSaveFile = false);
 	void run();
 	void handleInput();
 


Commit: b0bd3f1c3fe793ee97d46727bc1ed5b77981be1c
    https://github.com/scummvm/scummvm/commit/b0bd3f1c3fe793ee97d46727bc1ed5b77981be1c
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Implement viewable items in nancy7

Added support for the new type of item introduced in nancy7,
which, instead of getting picked up, transports the player to
a new scene with a close-up view of the item.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/miscrecords.h
    engines/nancy/commontypes.h
    engines/nancy/enginedata.cpp
    engines/nancy/enginedata.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/inventorybox.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 90e38ff3a13..60fae5747e2 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -229,6 +229,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new ShowInventoryItem();
 	case 123:
 		return new InventorySoundOverride();
+	case 125:
+		return new PopInvViewPriorScene();
 	case 150:
 		return new PlaySound();
 	case 151:
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 0488e1d4496..e4cbf6d2ce1 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -522,6 +522,15 @@ void InventorySoundOverride::execute() {
 	_isDone = true;
 }
 
+void PopInvViewPriorScene::readData(Common::SeekableReadStream &stream) {
+	stream.skip(1);
+}
+
+void PopInvViewPriorScene::execute() {
+	NancySceneState.popScene(true);
+	_isDone = true;
+}
+
 void HintSystem::readData(Common::SeekableReadStream &stream) {
 	_characterID = stream.readByte();
 	_genericSound.readNormal(stream);
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index f30a9fe9ea4..a3fbda66e75 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -372,6 +372,16 @@ protected:
 	Common::String getRecordTypeName() const override { return "InventorySoundOverride"; }
 };
 
+// Pops the scene and item that get pushed when a player clicks a kInvItemNewSceneView item
+class PopInvViewPriorScene : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "PopInvViewPriorScene"; }
+};
+
 // Checks how many hints the player is allowed to get. If they are still allowed hints,
 // it selects an appropriate one and plays its sound/displays its caption in the Textbox.
 // The hint system was _only_ used in nancy1, since it's pretty limited and overly punishing.
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index bebf9e42038..d92a3ac6ec9 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -51,6 +51,7 @@ static const int8 kFrNoFrame						= -1;
 static const byte kInvItemUseThenLose				= 0;
 static const byte kInvItemKeepAlways				= 1;
 static const byte kInvItemReturn					= 2;
+static const byte kInvItemNewSceneView				= 3;
 
 // Inventory item sound override commands
 static const byte kInvSoundOverrideCommandNoSound	= 0;
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 6cfcb3ce6ab..4937982283d 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -181,6 +181,8 @@ INV::INV(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 		item.name = (char *)textBuf;
 
 		s.syncAsUint16LE(item.keepItem);
+		s.syncAsUint16LE(item.sceneID, kGameTypeNancy7);
+		s.syncAsUint16LE(item.sceneSoundFlag, kGameTypeNancy7);
 		readRect(s, item.sourceRect);
 		readRect(s, item.highlightedSourceRect, kGameTypeNancy2);
 
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index 84b255b87c3..b2caec2d536 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -105,7 +105,9 @@ struct PCAL : public EngineData {
 struct INV : public EngineData {
 	struct ItemDescription {
 		Common::String name;
-		byte keepItem;
+		byte keepItem = kInvItemKeepAlways;
+		uint16 sceneID = 9999;
+		uint16 sceneSoundFlag = kContinueSceneSound;
 		Common::Rect sourceRect;
 		Common::Rect highlightedSourceRect;
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 0ee5fe7ac1f..6ed3cc73dd7 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -236,15 +236,28 @@ void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
 	_state = kLoad;
 }
 
-void Scene::pushScene() {
-	_sceneState.pushedScene = _sceneState.currentScene;
-	_sceneState.isScenePushed = true;
+void Scene::pushScene(int16 itemID) {
+	if (itemID == -1) {
+		_sceneState.pushedScene = _sceneState.currentScene;
+		_sceneState.isScenePushed = true;
+	} else {
+		_sceneState.pushedInvScene = _sceneState.currentScene;
+		_sceneState.isInvScenePushed = true;
+		_sceneState.pushedInvItemID = itemID;
+	}
 }
 
-void Scene::popScene() {
-	_sceneState.pushedScene.continueSceneSound = true;
-	changeScene(_sceneState.pushedScene);
-	_sceneState.isScenePushed = false;
+void Scene::popScene(bool inventory) {
+	if (!inventory) {
+		_sceneState.pushedScene.continueSceneSound = true;
+		changeScene(_sceneState.pushedScene);
+		_sceneState.isScenePushed = false;
+	} else {
+		_sceneState.pushedInvScene.continueSceneSound = true;
+		changeScene(_sceneState.pushedInvScene);
+		_sceneState.isInvScenePushed = false;
+		addItemToInventory(_sceneState.pushedInvItemID);
+	}
 }
 
 void Scene::setPlayerTime(Time time, byte relative) {
@@ -582,6 +595,14 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsUint16LE(_sceneState.pushedScene.verticalOffset);
 	ser.syncAsByte(_sceneState.isScenePushed);
 
+	// Inventory scene "stack" was introduced in nancy7
+	if (g_nancy->getGameType() >= kGameTypeNancy7) {
+		ser.syncAsUint16LE(_sceneState.pushedInvScene.sceneID);
+		ser.syncAsUint16LE(_sceneState.pushedInvScene.frameID);
+		ser.syncAsUint16LE(_sceneState.pushedInvScene.verticalOffset);
+		ser.syncAsByte(_sceneState.isInvScenePushed);
+	}
+
 	// hardcoded number of logic conditions, check if there can ever be more/less
 	for (uint i = 0; i < 30; ++i) {
 		ser.syncAsUint32LE(_flags.logicConditions[i].flag);
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 74891e27955..4dda7ac1e58 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -127,8 +127,8 @@ public:
 	bool isRunningAd() { return _isRunningAd; }
 
 	void changeScene(const SceneChangeDescription &sceneDescription);
-	void pushScene();
-	void popScene();
+	void pushScene(int16 itemID = -1);
+	void popScene(bool inventory = false);
 	
 	void setPlayerTime(Time time, byte relative);
 	Time getPlayerTime() const { return _timers.playerTime; }
@@ -222,6 +222,9 @@ private:
 		SceneChangeDescription nextScene;
 		SceneChangeDescription pushedScene;
 		bool isScenePushed = false;
+		SceneChangeDescription pushedInvScene;
+		int16 pushedInvItemID = -1;
+		bool isInvScenePushed = false;
 	};
 
 	struct Timers {
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 240678a017a..877b5bdac43 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -40,7 +40,8 @@ InventoryBox::InventoryBox() :
 		RenderObject(6),
 		_scrollbar(nullptr),
 		_scrollbarPos(0),
-		_highlightedHotspot(-1) {}
+		_highlightedHotspot(-1),
+		_inventoryData(nullptr) {}
 
 InventoryBox::~InventoryBox() {
 	_fullInventorySurface.free();
@@ -51,13 +52,13 @@ void InventoryBox::init() {
 	const BSUM *bootSummary = (const BSUM *)g_nancy->getEngineData("BSUM");
 	assert(bootSummary);
 
-	const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-	assert(inventoryData);
+	_inventoryData = (const INV *)g_nancy->getEngineData("INV");
+	assert(_inventoryData);
 
 	_order.clear();
 
 	moveTo(bootSummary->inventoryBoxScreenPosition);
-	g_nancy->_resource->loadImage(inventoryData->inventoryBoxIconsImageName, _iconsSurface);
+	g_nancy->_resource->loadImage(_inventoryData->inventoryBoxIconsImageName, _iconsSurface);
 
 	_fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((g_nancy->getStaticData().numItems / 4) + 1), g_nancy->_graphicsManager->getScreenPixelFormat());
 	Common::Rect sourceRect = _screenPosition;
@@ -75,9 +76,9 @@ void InventoryBox::init() {
 	RenderObject::init();
 
 	_scrollbar = new Scrollbar(	9,
-								inventoryData->scrollbarSrcBounds,
-								inventoryData->scrollbarDefaultPos,
-								inventoryData->scrollbarMaxScroll - inventoryData->scrollbarDefaultPos.y);
+								_inventoryData->scrollbarSrcBounds,
+								_inventoryData->scrollbarDefaultPos,
+								_inventoryData->scrollbarMaxScroll - _inventoryData->scrollbarDefaultPos.y);
 	_scrollbar->init();
 	_curtains.init();
 }
@@ -122,9 +123,20 @@ void InventoryBox::handleInput(NancyInput &input) {
 				hoveredHotspot = i;
 
 				if (input.input & NancyInput::kLeftMouseButtonUp) {
-					NancySceneState.removeItemFromInventory(_itemHotspots[i].itemID);
+					uint16 itemID = _itemHotspots[i].itemID;
+					INV::ItemDescription item = _inventoryData->itemDescriptions[itemID];
+
+					NancySceneState.removeItemFromInventory(itemID, item.keepItem != kInvItemNewSceneView);
 					_highlightedHotspot = -1;
 					hoveredHotspot = -1;
+
+					if (item.keepItem == kInvItemNewSceneView) {
+						NancySceneState.pushScene(itemID);
+						SceneChangeDescription sceneChange;
+						sceneChange.sceneID = item.sceneID;
+						sceneChange.continueSceneSound = item.sceneSoundFlag;
+						NancySceneState.changeScene(sceneChange);
+					}
 				}
 			}
 			break;
@@ -199,10 +211,7 @@ void InventoryBox::setHotspots(const uint pageNr) {
 }
 
 void InventoryBox::drawItemInSlot(const uint itemID, const uint slotID, const bool highlighted) {
-	const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-	assert(inventoryData);
-
-	auto &item = inventoryData->itemDescriptions[itemID];
+	auto &item = _inventoryData->itemDescriptions[itemID];
 	Common::Rect dest;
 
 	dest.setWidth(_screenPosition.width() / 2);
@@ -231,10 +240,10 @@ void InventoryBox::onScrollbarMove() {
 }
 
 void InventoryBox::Curtains::init() {
-	const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-	assert(inventoryData);
+	const INV *_inventoryData = (const INV *)g_nancy->getEngineData("INV");
+	assert(_inventoryData);
 
-	moveTo(inventoryData->curtainsScreenPosition);
+	moveTo(_inventoryData->curtainsScreenPosition);
 	Common::Rect bounds = _screenPosition;
 	bounds.moveTo(0, 0);
 	_drawSurface.create(bounds.width(), bounds.height(), g_nancy->_graphicsManager->getInputPixelFormat());
@@ -257,11 +266,11 @@ void InventoryBox::Curtains::updateGraphics() {
 	Time time = g_nancy->getTotalPlayTime();
 	if (_areOpen) {
 		if (_curFrame < g_nancy->getStaticData().numCurtainAnimationFrames && time > _nextFrameTime) {
-			const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-			assert(inventoryData);
+			const INV *_inventoryData = (const INV *)g_nancy->getEngineData("INV");
+			assert(_inventoryData);
 
 			setAnimationFrame(++_curFrame);
-			_nextFrameTime = time + inventoryData->curtainsFrameTime;
+			_nextFrameTime = time + _inventoryData->curtainsFrameTime;
 
 			if (!_soundTriggered) {
 				_soundTriggered = true;
@@ -270,11 +279,11 @@ void InventoryBox::Curtains::updateGraphics() {
 		}
 	} else {
 		if (_curFrame > 0 && time > _nextFrameTime) {
-			const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-			assert(inventoryData);
+			const INV *_inventoryData = (const INV *)g_nancy->getEngineData("INV");
+			assert(_inventoryData);
 
 			setAnimationFrame(--_curFrame);
-			_nextFrameTime = time + inventoryData->curtainsFrameTime;
+			_nextFrameTime = time + _inventoryData->curtainsFrameTime;
 
 			if (!_soundTriggered) {
 				_soundTriggered = true;
@@ -304,17 +313,17 @@ void InventoryBox::Curtains::setAnimationFrame(uint frame) {
 		setVisible(true);
 	}
 
-	const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-	assert(inventoryData);
+	const INV *_inventoryData = (const INV *)g_nancy->getEngineData("INV");
+	assert(_inventoryData);
 
 	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	// Draw left curtain
-	srcRect = inventoryData->curtainAnimationSrcs[frame * 2];
+	srcRect = _inventoryData->curtainAnimationSrcs[frame * 2];
 	_drawSurface.blitFrom(_object0, srcRect, destPoint);
 
 	// Draw right curtain
-	srcRect = inventoryData->curtainAnimationSrcs[frame * 2 + 1];
+	srcRect = _inventoryData->curtainAnimationSrcs[frame * 2 + 1];
 	destPoint.x = getBounds().width() - srcRect.width();
 	_drawSurface.blitFrom(_object0, srcRect, destPoint);
 
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index ffca1b2313e..d76572457d3 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -107,6 +107,8 @@ private:
 	Common::Array<int16> _order;
 	ItemHotspot _itemHotspots[4];
 	int _highlightedHotspot;
+
+	const struct INV *_inventoryData;
 };
 
 } // End of namespace UI


Commit: 57c9f5f203f3c14570177edb011b14495d126337
    https://github.com/scummvm/scummvm/commit/57c9f5f203f3c14570177edb011b14495d126337
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Implement EnableDisableInventory

Implemented an action record type responsible for making
a specific item temporarily unusable.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/miscrecords.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/inventorybox.cpp


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 60fae5747e2..b85ca57ec89 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -229,6 +229,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new ShowInventoryItem();
 	case 123:
 		return new InventorySoundOverride();
+	case 124:
+		return new EnableDisableInventory();
 	case 125:
 		return new PopInvViewPriorScene();
 	case 150:
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index e4cbf6d2ce1..f0c8bbb832d 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -522,6 +522,24 @@ void InventorySoundOverride::execute() {
 	_isDone = true;
 }
 
+void EnableDisableInventory::readData(Common::SeekableReadStream &stream) {
+	_itemID = stream.readUint16LE();
+	bool disabled = stream.readUint16LE();
+	bool playSound = stream.readUint16LE();
+
+	if (disabled) {
+		++_disabledState;
+		if (playSound) {
+			++_disabledState;
+		}
+	}
+}
+
+void EnableDisableInventory::execute() {
+	NancySceneState.setItemDisabledState(_itemID, _disabledState);
+	_isDone = true;
+}
+
 void PopInvViewPriorScene::readData(Common::SeekableReadStream &stream) {
 	stream.skip(1);
 }
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index a3fbda66e75..08ac74af426 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -372,6 +372,18 @@ protected:
 	Common::String getRecordTypeName() const override { return "InventorySoundOverride"; }
 };
 
+class EnableDisableInventory : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	uint16 _itemID = 0;
+	byte _disabledState = 0;
+
+protected:
+	Common::String getRecordTypeName() const override { return "EnableDisableInventory"; }
+};
+
 // Pops the scene and item that get pushed when a player clicks a kInvItemNewSceneView item
 class PopInvViewPriorScene : public ActionRecord {
 public:
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 6ed3cc73dd7..25417f367f3 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -637,6 +637,10 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsSint16LE(_flags.heldItem);
 	g_nancy->_cursorManager->setCursorItemID(_flags.heldItem);
 
+	if (g_nancy->getGameType() >= kGameTypeNancy7) {
+		ser.syncArray(_flags.disabledItems.data(), g_nancy->getStaticData().numItems, Common::Serializer::Byte);
+	}
+
 	ser.syncAsUint32LE((uint32 &)_timers.lastTotalTime);
 	ser.syncAsUint32LE((uint32 &)_timers.sceneTime);
 	ser.syncAsUint32LE((uint32 &)_timers.playerTime);
@@ -732,6 +736,7 @@ void Scene::init() {
 	_flags.sceneCounts.clear();
 
 	_flags.items.resize(g_nancy->getStaticData().numItems, g_nancy->_false);
+	_flags.disabledItems.resize(_flags.items.size(), 0);
 
 	_timers.lastTotalTime = 0;
 	_timers.playerTime = bootSummary->startTimeHours * 3600000;
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 4dda7ac1e58..2f7f1773420 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -141,6 +141,8 @@ public:
 	void setHeldItem(int16 id);
 	void setNoHeldItem();
 	byte hasItem(int16 id) const { return _flags.items[id] || getHeldItem() == id; }
+	byte getItemDisabledState(int16 id) const { return _flags.disabledItems[id]; }
+	void setItemDisabledState(int16 id, byte state) { _flags.disabledItems[id] = state; }
 
 	void installInventorySoundOverride(byte command, const SoundDescription &sound, const Common::String &caption, uint16 itemID);
 	void playItemCantSound(int16 itemID = -1);
@@ -248,6 +250,7 @@ private:
 		Common::Array<byte> eventFlags;
 		Common::HashMap<uint16, uint16> sceneCounts;
 		Common::Array<byte> items;
+		Common::Array<byte> disabledItems;
 		int16 heldItem = -1;
 		int16 primaryVideoResponsePicked = -1;
 	};
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 877b5bdac43..f5400d15d9e 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -125,17 +125,28 @@ void InventoryBox::handleInput(NancyInput &input) {
 				if (input.input & NancyInput::kLeftMouseButtonUp) {
 					uint16 itemID = _itemHotspots[i].itemID;
 					INV::ItemDescription item = _inventoryData->itemDescriptions[itemID];
-
-					NancySceneState.removeItemFromInventory(itemID, item.keepItem != kInvItemNewSceneView);
-					_highlightedHotspot = -1;
-					hoveredHotspot = -1;
-
-					if (item.keepItem == kInvItemNewSceneView) {
-						NancySceneState.pushScene(itemID);
-						SceneChangeDescription sceneChange;
-						sceneChange.sceneID = item.sceneID;
-						sceneChange.continueSceneSound = item.sceneSoundFlag;
-						NancySceneState.changeScene(sceneChange);
+					byte disabled = NancySceneState.getItemDisabledState(itemID);
+
+					if (!disabled) {
+						// Item is not disabled
+						NancySceneState.removeItemFromInventory(itemID, item.keepItem != kInvItemNewSceneView);
+						_highlightedHotspot = -1;
+						hoveredHotspot = -1;
+
+						if (item.keepItem == kInvItemNewSceneView) {
+							// Transport the player to a close-up scene, temporarily remove the item from the inventory
+							NancySceneState.pushScene(itemID);
+							SceneChangeDescription sceneChange;
+							sceneChange.sceneID = item.sceneID;
+							sceneChange.continueSceneSound = item.sceneSoundFlag;
+							NancySceneState.changeScene(sceneChange);
+						}
+					} else {
+						// Item is disabled
+						if (disabled == 2) {
+							// ...and set so it plays the "can't" sound when you click it
+							NancySceneState.playItemCantSound(itemID);
+						}
 					}
 				}
 			}


Commit: d9e0bc954a27f13871a4e3dbad785ab061b0311e
    https://github.com/scummvm/scummvm/commit/d9e0bc954a27f13871a4e3dbad785ab061b0311e
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-03T17:58:28+03:00

Commit Message:
NANCY: Move inventory-related records

Moved all ActionRecords related to the inventory to their
own source file.

Changed paths:
  A engines/nancy/action/inventoryrecords.cpp
  A engines/nancy/action/inventoryrecords.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/miscrecords.h
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index b85ca57ec89..9e2773464c4 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "engines/nancy/action/inventoryrecords.h"
 #include "engines/nancy/action/navigationrecords.h"
 #include "engines/nancy/action/soundrecords.h"
 #include "engines/nancy/action/miscrecords.h"
diff --git a/engines/nancy/action/inventoryrecords.cpp b/engines/nancy/action/inventoryrecords.cpp
new file mode 100644
index 00000000000..003db016df8
--- /dev/null
+++ b/engines/nancy/action/inventoryrecords.cpp
@@ -0,0 +1,195 @@
+/* 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/util.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+
+#include "engines/nancy/action/inventoryrecords.h"
+
+#include "engines/nancy/state/scene.h"
+
+namespace Nancy {
+namespace Action {
+
+void AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
+	_itemID = stream.readUint16LE();
+
+	if (g_nancy->getGameType() >= kGameTypeNancy6) {
+		_setCursor = stream.readUint16LE();
+		_forceCursor = stream.readUint16LE();
+	}
+}
+
+void AddInventoryNoHS::execute() {
+	if (_setCursor) {
+		if (NancySceneState.getHeldItem() != -1) {
+			// Currently holding another item
+			if (_forceCursor) {
+				NancySceneState.addItemToInventory(NancySceneState.getHeldItem());
+				NancySceneState.setHeldItem(_itemID);
+			} else {
+				NancySceneState.addItemToInventory(_itemID);
+			}
+		} else {
+			NancySceneState.setHeldItem(_itemID);
+		}
+	} else {
+		if (NancySceneState.hasItem(_itemID) == g_nancy->_false) {
+			NancySceneState.addItemToInventory(_itemID);
+		}
+	}
+
+	_isDone = true;
+}
+
+void RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
+	_itemID = stream.readUint16LE();
+}
+
+void RemoveInventoryNoHS::execute() {
+	if (NancySceneState.hasItem(_itemID) == g_nancy->_true) {
+		NancySceneState.removeItemFromInventory(_itemID, false);
+	}
+
+	_isDone = true;
+}
+
+void ShowInventoryItem::init() {
+	g_nancy->_resource->loadImage(_imageName, _fullSurface);
+
+	_drawSurface.create(_fullSurface, _blitDescriptions[0].src);
+
+	RenderObject::init();
+}
+
+void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
+	GameType gameType = g_nancy->getGameType();
+	_objectID = stream.readUint16LE();
+	readFilename(stream, _imageName);
+
+	uint16 numFrames = stream.readUint16LE();
+	if (gameType >= kGameTypeNancy3) {
+		stream.skip(2);
+	}
+
+	_blitDescriptions.resize(numFrames);
+	for (uint i = 0; i < numFrames; ++i) {
+		if (gameType <= kGameTypeNancy2) {
+			_blitDescriptions[i].readData(stream);
+		} else {
+			_blitDescriptions[i].frameID = i;
+			readRect(stream, _blitDescriptions[i].src);
+			readRect(stream, _blitDescriptions[i].dest);
+		}
+	}
+}
+
+void ShowInventoryItem::execute() {
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		_state = kRun;
+		// fall through
+	case kRun: {
+		int newFrame = -1;
+
+		for (uint i = 0; i < _blitDescriptions.size(); ++i) {
+			if (_blitDescriptions[i].frameID == NancySceneState.getSceneInfo().frameID) {
+				newFrame = i;
+				break;
+			}
+		}
+
+		if (newFrame != _drawnFrameID) {
+			_drawnFrameID = newFrame;
+
+			if (newFrame != -1) {
+				_hasHotspot = true;
+				_hotspot = _blitDescriptions[newFrame].dest;
+				_drawSurface.create(_fullSurface, _blitDescriptions[newFrame].src);
+				_screenPosition = _blitDescriptions[newFrame].dest;
+				setVisible(true);
+			} else {
+				_hasHotspot = false;
+				setVisible(false);
+			}
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		g_nancy->_sound->playSound("BUOK");
+		NancySceneState.addItemToInventory(_objectID);
+		setVisible(false);
+		_hasHotspot = false;
+		finishExecution();
+		break;
+	}
+}
+
+void InventorySoundOverride::readData(Common::SeekableReadStream &stream) {
+	_command = stream.readByte();
+	_itemID = stream.readUint16LE();
+	stream.skip(2);
+	char buf[61];
+	stream.read(buf, 60);
+	buf[60] = '\0';
+	_caption = buf;
+	_sound.readNormal(stream);
+}
+
+void InventorySoundOverride::execute() {
+	NancySceneState.installInventorySoundOverride(_command, _sound, _caption, _itemID);
+	_isDone = true;
+}
+
+void EnableDisableInventory::readData(Common::SeekableReadStream &stream) {
+	_itemID = stream.readUint16LE();
+	bool disabled = stream.readUint16LE();
+	bool playSound = stream.readUint16LE();
+
+	if (disabled) {
+		++_disabledState;
+		if (playSound) {
+			++_disabledState;
+		}
+	}
+}
+
+void EnableDisableInventory::execute() {
+	NancySceneState.setItemDisabledState(_itemID, _disabledState);
+	_isDone = true;
+}
+
+void PopInvViewPriorScene::readData(Common::SeekableReadStream &stream) {
+	stream.skip(1);
+}
+
+void PopInvViewPriorScene::execute() {
+	NancySceneState.popScene(true);
+	_isDone = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/inventoryrecords.h b/engines/nancy/action/inventoryrecords.h
new file mode 100644
index 00000000000..015fc65b9c1
--- /dev/null
+++ b/engines/nancy/action/inventoryrecords.h
@@ -0,0 +1,126 @@
+/* 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_INVENTORYRECORDS_H
+#define NANCY_ACTION_INVENTORYRECORDS_H
+
+#include "engines/nancy/action/actionrecord.h"
+
+namespace Nancy {
+namespace Action {
+
+// Simply adds an item to the player's inventory.
+class AddInventoryNoHS : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	uint16 _itemID = 0;
+	bool _setCursor = false;
+	bool _forceCursor = false;
+
+protected:
+	Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }
+};
+
+// Simply removes an item from the player's inventory.
+class RemoveInventoryNoHS : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	uint _itemID;
+
+protected:
+	Common::String getRecordTypeName() const override { return "RemoveInventoryNoHS"; }
+};
+
+// Displays a static image inside the viewport. The static image corresponds to an
+// inventory item, and is only displayed if the item is not in the player's possesion.
+// On click, it hides the image and adds the item to the inventory.
+class ShowInventoryItem : public RenderActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	ShowInventoryItem() : RenderActionRecord(9) {}
+	virtual ~ShowInventoryItem() { _fullSurface.free(); }
+
+	void init() override;
+
+	uint16 _objectID = 0;
+	Common::String _imageName;
+	Common::Array<FrameBlitDescription> _blitDescriptions;
+
+	int16 _drawnFrameID = -1;
+	Graphics::ManagedSurface _fullSurface;
+
+protected:
+	bool canHaveHotspot() const override { return true; }
+	Common::String getRecordTypeName() const override { return "ShowInventoryItem"; }
+	bool isViewportRelative() const override { return true; }
+};
+
+// When clicking an ActionRecord hotspot with a kItem dependency, the engine
+// checks if the required item is currently being held; when it isn't, it plays
+// a specific sound to inform the player they need some item. This AR changes that
+// sound and its related caption (or stops it from playing entirely).
+class InventorySoundOverride : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	byte _command = 0;
+	uint16 _itemID = 0;
+	SoundDescription _sound;
+	Common::String _caption;
+
+protected:
+	Common::String getRecordTypeName() const override { return "InventorySoundOverride"; }
+};
+
+// Temporarily disable (or re-enable) clicking on a specific item in the inventory box
+class EnableDisableInventory : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	uint16 _itemID = 0;
+	byte _disabledState = 0;
+
+protected:
+	Common::String getRecordTypeName() const override { return "EnableDisableInventory"; }
+};
+
+// Pops the scene and item that get pushed when a player clicks a kInvItemNewSceneView item
+class PopInvViewPriorScene : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "PopInvViewPriorScene"; }
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_INVENTORYRECORDS_H
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index f0c8bbb832d..920bfe07d86 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -377,49 +377,6 @@ void WinGame::execute() {
 	_isDone = true;
 }
 
-void AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-	_itemID = stream.readUint16LE();
-
-	if (g_nancy->getGameType() >= kGameTypeNancy6) {
-		_setCursor = stream.readUint16LE();
-		_forceCursor = stream.readUint16LE();
-	}
-}
-
-void AddInventoryNoHS::execute() {
-	if (_setCursor) {
-		if (NancySceneState.getHeldItem() != -1) {
-			// Currently holding another item
-			if (_forceCursor) {
-				NancySceneState.addItemToInventory(NancySceneState.getHeldItem());
-				NancySceneState.setHeldItem(_itemID);
-			} else {
-				NancySceneState.addItemToInventory(_itemID);
-			}
-		} else {
-			NancySceneState.setHeldItem(_itemID);
-		}
-	} else {
-		if (NancySceneState.hasItem(_itemID) == g_nancy->_false) {
-			NancySceneState.addItemToInventory(_itemID);
-		}
-	}
-
-	_isDone = true;
-}
-
-void RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-	_itemID = stream.readUint16LE();
-}
-
-void RemoveInventoryNoHS::execute() {
-	if (NancySceneState.hasItem(_itemID) == g_nancy->_true) {
-		NancySceneState.removeItemFromInventory(_itemID, false);
-	}
-
-	_isDone = true;
-}
-
 void DifficultyLevel::readData(Common::SeekableReadStream &stream) {
 	_difficulty = stream.readUint16LE();
 	_flag.label = stream.readSint16LE();
@@ -432,123 +389,6 @@ void DifficultyLevel::execute() {
 	_isDone = true;
 }
 
-void ShowInventoryItem::init() {
-	g_nancy->_resource->loadImage(_imageName, _fullSurface);
-
-	_drawSurface.create(_fullSurface, _blitDescriptions[0].src);
-
-	RenderObject::init();
-}
-
-void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
-	GameType gameType = g_nancy->getGameType();
-	_objectID = stream.readUint16LE();
-	readFilename(stream, _imageName);
-
-	uint16 numFrames = stream.readUint16LE();
-	if (gameType >= kGameTypeNancy3) {
-		stream.skip(2);
-	}
-
-	_blitDescriptions.resize(numFrames);
-	for (uint i = 0; i < numFrames; ++i) {
-		if (gameType <= kGameTypeNancy2) {
-			_blitDescriptions[i].readData(stream);
-		} else {
-			_blitDescriptions[i].frameID = i;
-			readRect(stream, _blitDescriptions[i].src);
-			readRect(stream, _blitDescriptions[i].dest);
-		}
-	}
-}
-
-void ShowInventoryItem::execute() {
-	switch (_state) {
-	case kBegin:
-		init();
-		registerGraphics();
-		_state = kRun;
-		// fall through
-	case kRun: {
-		int newFrame = -1;
-
-		for (uint i = 0; i < _blitDescriptions.size(); ++i) {
-			if (_blitDescriptions[i].frameID == NancySceneState.getSceneInfo().frameID) {
-				newFrame = i;
-				break;
-			}
-		}
-
-		if (newFrame != _drawnFrameID) {
-			_drawnFrameID = newFrame;
-
-			if (newFrame != -1) {
-				_hasHotspot = true;
-				_hotspot = _blitDescriptions[newFrame].dest;
-				_drawSurface.create(_fullSurface, _blitDescriptions[newFrame].src);
-				_screenPosition = _blitDescriptions[newFrame].dest;
-				setVisible(true);
-			} else {
-				_hasHotspot = false;
-				setVisible(false);
-			}
-		}
-
-		break;
-	}
-	case kActionTrigger:
-		g_nancy->_sound->playSound("BUOK");
-		NancySceneState.addItemToInventory(_objectID);
-		setVisible(false);
-		_hasHotspot = false;
-		finishExecution();
-		break;
-	}
-}
-
-void InventorySoundOverride::readData(Common::SeekableReadStream &stream) {
-	_command = stream.readByte();
-	_itemID = stream.readUint16LE();
-	stream.skip(2);
-	char buf[61];
-	stream.read(buf, 60);
-	buf[60] = '\0';
-	_caption = buf;
-	_sound.readNormal(stream);
-}
-
-void InventorySoundOverride::execute() {
-	NancySceneState.installInventorySoundOverride(_command, _sound, _caption, _itemID);
-	_isDone = true;
-}
-
-void EnableDisableInventory::readData(Common::SeekableReadStream &stream) {
-	_itemID = stream.readUint16LE();
-	bool disabled = stream.readUint16LE();
-	bool playSound = stream.readUint16LE();
-
-	if (disabled) {
-		++_disabledState;
-		if (playSound) {
-			++_disabledState;
-		}
-	}
-}
-
-void EnableDisableInventory::execute() {
-	NancySceneState.setItemDisabledState(_itemID, _disabledState);
-	_isDone = true;
-}
-
-void PopInvViewPriorScene::readData(Common::SeekableReadStream &stream) {
-	stream.skip(1);
-}
-
-void PopInvViewPriorScene::execute() {
-	NancySceneState.popScene(true);
-	_isDone = true;
-}
-
 void HintSystem::readData(Common::SeekableReadStream &stream) {
 	_characterID = stream.readByte();
 	_genericSound.readNormal(stream);
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 08ac74af426..9aa531c7165 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -287,31 +287,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "WinGame"; }
 };
 
-// Simply adds an item to the player's inventory.
-class AddInventoryNoHS : public ActionRecord {
-public:
-	void readData(Common::SeekableReadStream &stream) override;
-	void execute() override;
 
-	uint16 _itemID = 0;
-	bool _setCursor = false;
-	bool _forceCursor = false;
-
-protected:
-	Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }
-};
-
-// Simply removes an item from the player's inventory.
-class RemoveInventoryNoHS : public ActionRecord {
-public:
-	void readData(Common::SeekableReadStream &stream) override;
-	void execute() override;
-
-	uint _itemID;
-
-protected:
-	Common::String getRecordTypeName() const override { return "RemoveInventoryNoHS"; }
-};
 
 // Sets the difficulty level for the current save. Only appears at the start of the game.
 // First appears in nancy1. Nancy1 and nancy2 have three difficulty values, while later games
@@ -328,72 +304,6 @@ protected:
 	Common::String getRecordTypeName() const override { return "DifficultyLevel"; }
 };
 
-// Displays a static image inside the viewport. The static image corresponds to an
-// inventory item, and is only displayed if the item is not in the player's possesion.
-// On click, it hides the image and adds the item to the inventory.
-class ShowInventoryItem : public RenderActionRecord {
-public:
-	void readData(Common::SeekableReadStream &stream) override;
-	void execute() override;
-
-	ShowInventoryItem() : RenderActionRecord(9) {}
-	virtual ~ShowInventoryItem() { _fullSurface.free(); }
-
-	void init() override;
-
-	uint16 _objectID = 0;
-	Common::String _imageName;
-	Common::Array<FrameBlitDescription> _blitDescriptions;
-
-	int16 _drawnFrameID = -1;
-	Graphics::ManagedSurface _fullSurface;
-
-protected:
-	bool canHaveHotspot() const override { return true; }
-	Common::String getRecordTypeName() const override { return "ShowInventoryItem"; }
-	bool isViewportRelative() const override { return true; }
-};
-
-// When clicking an ActionRecord hotspot with a kItem dependency, the engine
-// checks if the required item is currently being held; when it isn't, it plays
-// a specific sound to inform the player they need some item. This AR changes that
-// sound and its related caption (or stops it from playing entirely).
-class InventorySoundOverride : public ActionRecord {
-public:
-	void readData(Common::SeekableReadStream &stream) override;
-	void execute() override;
-
-	byte _command = 0;
-	uint16 _itemID = 0;
-	SoundDescription _sound;
-	Common::String _caption;
-
-protected:
-	Common::String getRecordTypeName() const override { return "InventorySoundOverride"; }
-};
-
-class EnableDisableInventory : public ActionRecord {
-public:
-	void readData(Common::SeekableReadStream &stream) override;
-	void execute() override;
-
-	uint16 _itemID = 0;
-	byte _disabledState = 0;
-
-protected:
-	Common::String getRecordTypeName() const override { return "EnableDisableInventory"; }
-};
-
-// Pops the scene and item that get pushed when a player clicks a kInvItemNewSceneView item
-class PopInvViewPriorScene : public ActionRecord {
-public:
-	void readData(Common::SeekableReadStream &stream) override;
-	void execute() override;
-
-protected:
-	Common::String getRecordTypeName() const override { return "PopInvViewPriorScene"; }
-};
-
 // Checks how many hints the player is allowed to get. If they are still allowed hints,
 // it selects an appropriate one and plays its sound/displays its caption in the Textbox.
 // The hint system was _only_ used in nancy1, since it's pretty limited and overly punishing.
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 4206126e7d6..e958e65daa7 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS = \
   action/actionrecord.o \
   action/arfactory.o \
   action/autotext.o \
+  action/inventoryrecords.o \
   action/navigationrecords.o \
   action/soundrecords.o \
   action/miscrecords.o \




More information about the Scummvm-git-logs mailing list