[Scummvm-git-logs] scummvm master -> 0e192ba542f818f4551aa5f6f2b0dee11e74d68e

fracturehill noreply at scummvm.org
Sun Sep 24 14:44:10 UTC 2023


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

Summary:
c20f765032 NANCY: Add console command to display hotspots
4bceadfb6a NANCY: Fix movies in The Vampire Diaries
4e20ddd030 NANCY: Do not add a single item multiple times
82c3d2ae0b NANCY: Implement table indexing record types
9dd73a5edb NANCY: Only set minimum volume in nancy3 and up
0e192ba542 NANCY: Describe all ActionRecords


Commit: c20f7650320c65a9eb51758cce7904ed5fc2aca7
    https://github.com/scummvm/scummvm/commit/c20f7650320c65a9eb51758cce7904ed5fc2aca7
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-24T17:43:28+03:00

Commit Message:
NANCY: Add console command to display hotspots

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionmanager.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    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 1534cff080a..abc284a9909 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -27,6 +27,8 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/sound.h"
+#include "engines/nancy/font.h"
+#include "engines/nancy/graphics.h"
 
 #include "engines/nancy/action/actionmanager.h"
 #include "engines/nancy/action/actionrecord.h"
@@ -72,6 +74,8 @@ void ActionManager::handleInput(NancyInput &input) {
 				} else {
 					rec->_state = ActionRecord::ExecutionState::kActionTrigger;
 
+					input.eatMouseInput();
+
 					if (rec->_cursorDependency) {
 						int16 item = rec->_cursorDependency->label;
 						if (item > 100 && item <= (100 + g_nancy->getStaticData().numItems)) {
@@ -245,6 +249,8 @@ void ActionManager::processActionRecords() {
 			record->execute();
 		}
 	}
+
+	debugDrawHotspots();
 }
 
 void ActionManager::processDependency(DependencyRecord &dep, ActionRecord &record, bool doNotCheckCursor) {
@@ -529,5 +535,39 @@ void ActionManager::synchronize(Common::Serializer &ser) {
 	}
 }
 
+void ActionManager::debugDrawHotspots() {
+	// Draws a rectangle around (non-puzzle) hotspots as well as the id
+	// and type of the owning ActionRecord. Hardcoded to font 0 since that's
+	// the smallest one available in the engine.
+	RenderObject &obj = NancySceneState._hotspotDebug;
+	if (ConfMan.getBool("debug_hotspots", ConfMan.kTransientDomain)) {
+		const Font *font = g_nancy->_graphicsManager->getFont(0);
+		assert(font);
+		uint16 yOffset = NancySceneState.getViewport().getCurVerticalScroll();
+		obj.setVisible(true);
+		obj._drawSurface.clear(obj._drawSurface.getTransparentColor());
+
+		for (uint i = 0; i < _records.size(); ++i) {
+			ActionRecord *rec = _records[i];
+			if (rec->_hasHotspot) {
+				Common::Rect hotspot = rec->_hotspot;
+				hotspot.translate(0, -yOffset);
+				hotspot.clip(obj._drawSurface.getBounds());
+
+				if (!hotspot.isEmpty()) {
+					font->drawString(&obj._drawSurface, Common::String::format("%u, %s", i, rec->getRecordTypeName().c_str()),
+					hotspot.left, hotspot.bottom - font->getFontHeight() - 2, hotspot.width(), 0,
+					Graphics::kTextAlignCenter, 0, true);
+					obj._drawSurface.frameRect(hotspot, 0xFFFFFF);
+				}
+			}
+		}
+	} else {
+		if (obj.isVisible()) {
+			obj.setVisible(false);
+		}
+	}
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index dd293560958..1bfad205dfa 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -75,6 +75,8 @@ protected:
 	static ActionRecord *createActionRecord(uint16 type);
 	static ActionRecord *createAndLoadNewRecord(Common::SeekableReadStream &inputData);
 
+	void debugDrawHotspots();
+
 	Common::Array<ActionRecord *> _records;
 };
 
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 3d16ceb7e89..59fc9b85d91 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -21,6 +21,7 @@
 
 #include "common/system.h"
 #include "common/events.h"
+#include "common/config-manager.h"
 
 #include "audio/audiostream.h"
 
@@ -66,6 +67,7 @@ NancyConsole::NancyConsole() : GUI::Debugger() {
 	registerCmd("get_difficulty", WRAP_METHOD(NancyConsole, Cmd_getDifficulty));
 	registerCmd("set_difficulty", WRAP_METHOD(NancyConsole, Cmd_setDifficulty));
 	registerCmd("sound_info", WRAP_METHOD(NancyConsole, Cmd_soundInfo));
+	registerCmd("debug_hotspots", WRAP_METHOD(NancyConsole, Cmd_showHotspots));
 
 }
 
@@ -1013,4 +1015,11 @@ bool NancyConsole::Cmd_soundInfo(int argc, const char **argv) {
 	return true;
 }
 
+bool NancyConsole::Cmd_showHotspots(int argc, const char **argv) {
+	ConfMan.setBool("debug_hotspots", !ConfMan.getBool("debug_hotspots", ConfMan.kTransientDomain), ConfMan.kTransientDomain);
+
+	return cmdExit(0, nullptr);
+}
+
+
 } // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index d4dca79d872..48b00d13e3d 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -66,6 +66,7 @@ private:
 	bool Cmd_getDifficulty(int argc, const char **argv);
 	bool Cmd_setDifficulty(int argc, const char **argv);
 	bool Cmd_soundInfo(int argc, const char **argv);
+	bool Cmd_showHotspots(int argc, const char **argv);
 
 	void printActionRecord(const Nancy::Action::ActionRecord *record, bool noDependencies = false);
 	void recursePrintDependencies(const Nancy::Action::DependencyRecord &record);
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 7a900e9ae0e..f888055ea75 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -119,7 +119,8 @@ Scene::Scene() :
 		_difficulty(0),
 		_activeConversation(nullptr),
 		_lightning(nullptr),
-		_destroyOnExit(false) {}
+		_destroyOnExit(false),
+		_hotspotDebug(50) {}
 
 Scene::~Scene()  {
 	delete _helpButton;
@@ -497,6 +498,7 @@ void Scene::registerGraphics() {
 	_viewport.registerGraphics();
 	_textbox.registerGraphics();
 	_inventoryBox.registerGraphics();
+	_hotspotDebug.registerGraphics();
 
 	if (_menuButton) {
 		_menuButton->registerGraphics();
@@ -718,6 +720,11 @@ void Scene::init() {
 		_lightning = new Misc::Lightning();
 	}
 
+	Common::Rect vpPos = _viewport.getScreenPosition();
+	_hotspotDebug._drawSurface.create(vpPos.width(), vpPos.height(), g_nancy->_graphicsManager->getScreenPixelFormat());
+	_hotspotDebug.moveTo(vpPos);
+	_hotspotDebug.setTransparent(true);
+
 	registerGraphics();
 	g_nancy->_graphicsManager->redrawAll();
 }
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 81592c22e6a..363b6dec8b2 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -290,6 +290,8 @@ private:
 	// Contains a screenshot of the Scene state from the last time it was exited
 	Graphics::ManagedSurface _lastScreenshot;
 
+	RenderObject _hotspotDebug;
+
 	bool _destroyOnExit;
 
 	State _state;


Commit: 4bceadfb6afc9ada770675f19240602a5cc2d025
    https://github.com/scummvm/scummvm/commit/4bceadfb6afc9ada770675f19240602a5cc2d025
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-24T17:43:28+03:00

Commit Message:
NANCY: Fix movies in The Vampire Diaries

This fixes a regression that would cause small-size movies
(like the ones in TVD) to not display correctly.

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


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 6637652c449..ae4d7d9d073 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -50,7 +50,7 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	readFilename(ser, _bitmapOverlayName);
 
 	ser.skip(2); // videoPlaySource
-	ser.skip(2); // smallSize
+	ser.syncAsUint16LE(_videoFormat);
 	ser.skip(4, kGameTypeVampire, kGameTypeVampire); // paletteStart, paletteSize
 	ser.skip(2); // hasBitmapOverlaySurface
 	ser.skip(2); // VIDEO_STOP_RENDERING, VIDEO_CONTINUE_RENDERING
@@ -173,8 +173,8 @@ void PlaySecondaryMovie::execute() {
 				}
 			}
 
-			GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _fullFrame, _paletteName.size() > 0);
-			_drawSurface.create(_fullFrame, _videoDescs[_curViewportFrame].srcRect);
+			GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _fullFrame, g_nancy->getGameType() == kGameTypeVampire, _videoFormat == kSmallVideoFormat);
+			_drawSurface.create(_fullFrame, _videoDescs[descID].srcRect);
 			moveTo(_videoDescs[descID].destRect);
 
 			_needsRedraw = true;
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index c412e6134a9..72831cf9578 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -57,6 +57,7 @@ public:
 	Common::String _paletteName;
 	Common::String _bitmapOverlayName;
 
+	uint16 _videoFormat = kLargeVideoFormat;
 	uint16 _videoSceneChange = kMovieNoSceneChange;
 	byte _playerCursorAllowed = kPlayerCursorAllowed;
 	byte _playDirection = kPlayMovieForward;


Commit: 4e20ddd03015471320d22d06f84d78aae54f28be
    https://github.com/scummvm/scummvm/commit/4e20ddd03015471320d22d06f84d78aae54f28be
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-24T17:43:28+03:00

Commit Message:
NANCY: Do not add a single item multiple times

Fixed a silly bug in TVD where a cutscene can give the player
multiple copies of the same item.

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


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index f888055ea75..ffacc9df2b3 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -273,28 +273,32 @@ byte Scene::getPlayerTOD() const {
 }
 
 void Scene::addItemToInventory(uint16 id) {
-	_flags.items[id] = g_nancy->_true;
-	if (_flags.heldItem == id) {
-		setHeldItem(-1);
-	}
-	
-	g_nancy->_sound->playSound("BUOK");
+	if (_flags.items[id] == g_nancy->_false) {
+		_flags.items[id] = g_nancy->_true;
+		if (_flags.heldItem == id) {
+			setHeldItem(-1);
+		}
+		
+		g_nancy->_sound->playSound("BUOK");
 
-	_inventoryBox.addItem(id);
+		_inventoryBox.addItem(id);
+	}
 }
 
 void Scene::removeItemFromInventory(uint16 id, bool pickUp) {
-	_flags.items[id] = g_nancy->_false;
+	if (_flags.items[id] == g_nancy->_true || getHeldItem() == id) {
+		_flags.items[id] = g_nancy->_false;
 
-	if (pickUp) {
-		setHeldItem(id);
-	} else if (getHeldItem() == id) {
-		setHeldItem(-1);
-	}
-	
-	g_nancy->_sound->playSound("BUOK");
+		if (pickUp) {
+			setHeldItem(id);
+		} else if (getHeldItem() == id) {
+			setHeldItem(-1);
+		}
+		
+		g_nancy->_sound->playSound("BUOK");
 
-	_inventoryBox.removeItem(id);
+		_inventoryBox.removeItem(id);
+	}
 }
 
 void Scene::setHeldItem(int16 id)  {


Commit: 82c3d2ae0b6360662c8846c8dc01784f9d75feff
    https://github.com/scummvm/scummvm/commit/82c3d2ae0b6360662c8846c8dc01784f9d75feff
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-24T17:43:28+03:00

Commit Message:
NANCY: Implement table indexing record types

Added support for three different action records types
(TableIndexOverlay, TableIndexPlaySound, and
TableIndexSetValueHS), all of which use nancy6's newly-
introduced TABL chunk, which describes a table of values
that can be changed through interaction. This is used
in nancy6's museum narrations puzzle.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/miscrecords.h
    engines/nancy/action/overlay.cpp
    engines/nancy/action/overlay.h
    engines/nancy/action/soundrecords.cpp
    engines/nancy/action/soundrecords.h
    engines/nancy/commontypes.h
    engines/nancy/enginedata.cpp
    engines/nancy/enginedata.h
    engines/nancy/nancy.cpp
    engines/nancy/puzzledata.cpp
    engines/nancy/puzzledata.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 225f2345f4e..05b5b3814f6 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -113,7 +113,10 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 	case 53:
 		return new PlaySecondaryMovie();
 	case 54:
-		return new Overlay(false); // PlayStaticBitmapAnimation
+		if (g_nancy->getGameType() <= kGameTypeNancy1) {
+			return new Overlay(false); // PlayStaticBitmapAnimation
+		}
+		// fall through
 	case 55:
 		return new Overlay(true); // PlayIntStaticBitmapAnimation
 	case 56:
@@ -140,6 +143,12 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 		}
 	case 62:
 		return new MapCallHotMultiframe();
+	case 65:
+		return new TableIndexOverlay();
+	case 66:
+		return new TableIndexPlaySound();
+	case 67:
+		return new TableIndexSetValueHS();
 	case 75:
 		return new TextBoxWrite();
 	case 76:
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 0031134b6f5..fbca13c8b77 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -89,6 +89,90 @@ void SpecialEffect::execute() {
 	_isDone = true;
 }
 
+void TableIndexSetValueHS::readData(Common::SeekableReadStream &stream) {
+	_tableIndex = stream.readUint16LE();
+	_valueChangeType = stream.readByte();
+	_entryCorrectFlagID = stream.readSint16LE();
+	_allEntriesCorrectFlagID = stream.readSint16LE();
+
+	_flags.readData(stream);
+	_cursorType = stream.readUint16LE();
+	uint16 numHotspots = stream.readUint16LE();
+	_hotspots.resize(numHotspots);
+	for (uint i = 0; i < numHotspots; ++i) {
+		_hotspots[i].readData(stream);
+	}
+}
+
+void TableIndexSetValueHS::execute() {
+	switch (_state) {
+	case kBegin:
+		_state = kRun;
+		// fall through
+	case kRun:
+		_hasHotspot = false;
+		for (uint i = 0; i < _hotspots.size(); ++i) {
+			if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+				_hasHotspot = true;
+				_hotspot = _hotspots[i].coords;
+			}
+		}
+		break;
+	case kActionTrigger: {
+		TableData *playerTable = (TableData *)NancySceneState.getPuzzleData(TableData::getTag());
+		assert(playerTable);
+		const TABL *tabl = (const TABL *)g_nancy->getEngineData("TABL");
+		assert(tabl);
+
+		// Edit table. Values start from 1!
+		switch (_valueChangeType) {
+		case kNoChangeTableValue:
+			break;
+		case kIncrementTableValue:
+			++playerTable->currentIDs[_tableIndex - 1];
+			if (playerTable->currentIDs[_tableIndex - 1] >= playerTable->currentIDs.size() + 1) {
+				playerTable->currentIDs[_tableIndex - 1] = 1;
+			}
+			break;
+		case kDecrementTableValue:
+			--playerTable->currentIDs[_tableIndex - 1];
+			if (playerTable->currentIDs[_tableIndex - 1] == 0) {
+				playerTable->currentIDs[_tableIndex - 1] = playerTable->currentIDs.size();
+			}
+			
+			break;
+		}
+
+		// Check for correctness...
+
+		// ...of current index only...
+		if (playerTable->currentIDs[_tableIndex] == tabl->correctIDs[_tableIndex]) {
+			NancySceneState.setEventFlag(_entryCorrectFlagID, g_nancy->_true);
+		} else {
+			NancySceneState.setEventFlag(_entryCorrectFlagID, g_nancy->_false);
+		}
+
+		// ..and of all indices
+		bool allCorrect = true;
+		for (uint i = 0; i < tabl->correctIDs.size(); ++i) {
+			if (playerTable->currentIDs[i] != tabl->correctIDs[i]) {
+				allCorrect = false;
+				break;
+			}
+		}
+
+		if (allCorrect) {
+			NancySceneState.setEventFlag(_allEntriesCorrectFlagID, g_nancy->_true);
+		} else {
+			NancySceneState.setEventFlag(_allEntriesCorrectFlagID, g_nancy->_false);
+		}
+
+		_flags.execute();
+		finishExecution();
+	}
+	}
+}
+
 void LightningOn::execute() {
 	NancySceneState.beginLightning(_distance, _pulseTime, _rgbPercent);
 	_isDone = true;
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 9d5fa5bf9a2..ce5782a2192 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -85,6 +85,26 @@ protected:
 	Common::String getRecordTypeName() const override { return "SpecialEffect"; }
 };
 
+class TableIndexSetValueHS : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	CursorManager::CursorType getHoverCursor() const override { return (CursorManager::CursorType)_cursorType; }
+
+protected:
+	Common::String getRecordTypeName() const override { return "TableIndexSetValueHS"; }
+
+	uint16 _tableIndex = 0;
+	byte _valueChangeType = kNoChangeTableValue;
+	int16 _entryCorrectFlagID = -1;
+	int16 _allEntriesCorrectFlagID = -1;
+
+	MultiEventFlagDescription _flags;
+	uint16 _cursorType = 1;
+	Common::Array<HotspotDescription> _hotspots;
+};
+
 class TextBoxWrite : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index c6915e66c71..77544017f95 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -104,13 +104,9 @@ void Overlay::readData(Common::SeekableReadStream &stream) {
 
 	ser.syncAsUint16LE(_z, kGameTypeNancy1, kGameTypeNancy1);
 
-	if (ser.getVersion() > kGameTypeNancy1) {
-		_isInterruptible = true;
-		
-		if (ser.getVersion() > kGameTypeNancy2) {
-			if (_overlayType == kPlayOverlayStatic) {
-				_enableHotspot = (_hasSceneChange == kPlayOverlaySceneChange) ? kPlayOverlayWithHotspot : kPlayOverlayNoHotspot;
-			}
+	if (ser.getVersion() > kGameTypeNancy2) {
+		if (_overlayType == kPlayOverlayStatic) {
+			_enableHotspot = (_hasSceneChange == kPlayOverlaySceneChange) ? kPlayOverlayWithHotspot : kPlayOverlayNoHotspot;
 		}
 	}
 
@@ -351,5 +347,32 @@ void Overlay::setFrame(uint frame) {
 	_needsRedraw = true;
 }
 
+void TableIndexOverlay::readData(Common::SeekableReadStream &stream) {
+	_tableIndex = stream.readUint16LE();
+	Overlay::readData(stream);
+}
+
+void TableIndexOverlay::execute() {
+	if (_state == kBegin) {
+		Overlay::execute();
+	}
+
+	TableData *playerTable = (TableData *)NancySceneState.getPuzzleData(TableData::getTag());
+	assert(playerTable);
+	const TABL *tabl = (const TABL *)g_nancy->getEngineData("TABL");
+	assert(tabl);
+
+	if (_lastIndexVal != playerTable->currentIDs[_tableIndex - 1]) {
+		_lastIndexVal = playerTable->currentIDs[_tableIndex - 1];
+		_srcRects.clear();
+		_srcRects.push_back(tabl->srcRects[_lastIndexVal - 1]);
+		_currentViewportFrame = -1; // Force redraw 
+	}
+
+	if (_state != kBegin) {
+		Overlay::execute();
+	}
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/overlay.h b/engines/nancy/action/overlay.h
index b71a7df15f6..f2748932069 100644
--- a/engines/nancy/action/overlay.h
+++ b/engines/nancy/action/overlay.h
@@ -85,6 +85,21 @@ protected:
 	Graphics::ManagedSurface _staticModeIntermediate;
 };
 
+class TableIndexOverlay : public Overlay {
+public:
+	TableIndexOverlay() : Overlay(true) {}
+	virtual ~TableIndexOverlay() {}
+
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "TableIndexOverlay"; }
+
+	uint16 _tableIndex = 0;
+	int16 _lastIndexVal = -1;
+};
+
 } // End of namespace Action
 } // End of namespace Nancy
 
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index ec6ae23e42a..443c87cd7bc 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -196,5 +196,27 @@ void PlayRandomSound::execute() {
 	PlayDigiSound::execute();
 }
 
+void TableIndexPlaySound::readData(Common::SeekableReadStream &stream) {
+	_tableIndex = stream.readUint16LE();
+	PlayDigiSound::readData(stream); // Data does NOT contain captions, so we call the PlayDigiSound version
+}
+
+void TableIndexPlaySound::execute() {
+	TableData *playerTable = (TableData *)NancySceneState.getPuzzleData(TableData::getTag());
+	assert(playerTable);
+	const TABL *tabl = (const TABL *)g_nancy->getEngineData("TABL");
+	assert(tabl);
+
+	if (_lastIndexVal != playerTable->currentIDs[_tableIndex - 1]) {
+		g_nancy->_sound->stopSound(_sound);
+		NancySceneState.getTextbox().clear();
+		_lastIndexVal = playerTable->currentIDs[_tableIndex - 1];
+		_sound.name = Common::String::format("%s%u", tabl->soundBaseName.c_str(), playerTable->currentIDs[_tableIndex - 1]);
+		_ccText = tabl->strings[playerTable->currentIDs[_tableIndex - 1] - 1];
+	}
+
+	PlayDigiSoundCC::execute();
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index 2fb4be28a83..09d8ea64144 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -106,6 +106,18 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlayRandomSound"; }
 };
 
+class TableIndexPlaySound : public PlayDigiSoundCC {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "TableIndexPlaySound"; }
+
+	uint16 _tableIndex = 0;
+	int16 _lastIndexVal = -1;
+};
+
 } // End of namespace Action
 } // End of namespace Nancy
 
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 32b2aa57d10..d145b5d79c3 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -103,6 +103,11 @@ static const byte kPlayOverlayReverse				= 2;
 static const byte kPlayOverlayWithHotspot			= 1;
 static const byte kPlayOverlayNoHotspot				= 2;
 
+// Table access
+static const byte kNoChangeTableValue				= 0;
+static const byte kIncrementTableValue				= 1;
+static const byte kDecrementTableValue				= 2;
+
 enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
 
 // Separate namespace to remove possible clashes
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index c805cae6160..07c4ca6949b 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -734,4 +734,32 @@ CVTX::CVTX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	delete[] buf;
 }
 
+TABL::TABL(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
+	uint numEntries = chunkStream->readUint16LE();
+
+	readFilename(*chunkStream, soundBaseName);
+
+	startIDs.resize(numEntries);
+	for (uint i = 0; i < numEntries; ++i) {
+		startIDs[i] = chunkStream->readUint16LE();
+	}
+	chunkStream->skip((20 - numEntries) * 2);
+
+	correctIDs.resize(numEntries);
+	for (uint i = 0; i < numEntries; ++i) {
+		correctIDs[i] = chunkStream->readUint16LE();
+	}
+	chunkStream->skip((20 - numEntries) * 2);
+
+	readRectArray(*chunkStream, srcRects, numEntries, 20);
+
+	char buf[1000];
+	strings.resize(numEntries);
+	for (uint i = 0; i < numEntries; ++i) {
+		chunkStream->read(buf, 1000);
+		assembleTextLine(buf, strings[i], 1000);
+	}
+	chunkStream->skip((20 - numEntries) * 1000);
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index 76f0d67bfe9..7a54dedf738 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -430,6 +430,16 @@ struct CVTX : public EngineData {
 	Common::HashMap<Common::String, Common::String> texts;
 };
 
+struct TABL : public EngineData {
+	TABL(Common::SeekableReadStream *chunkStream);
+
+	Common::String soundBaseName;
+	Common::Array<uint16> startIDs;
+	Common::Array<uint16> correctIDs;
+	Common::Array<Common::Rect> srcRects;
+	Common::Array<Common::String> strings;
+};
+
 } // End of namespace Nancy
 
 #endif // NANCY_ENGINEDATA_H
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 44a7e0043bc..3e40ccb46bd 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -422,6 +422,7 @@ void NancyEngine::bootGameEngine() {
 	LOAD_BOOT(SPEC)
 	LOAD_BOOT(RCPR)
 	LOAD_BOOT(RCLB)
+	LOAD_BOOT(TABL)
 
 	LOAD_BOOT_L(ImageChunk, "OB0")
 	LOAD_BOOT_L(ImageChunk, "FR0")
diff --git a/engines/nancy/puzzledata.cpp b/engines/nancy/puzzledata.cpp
index 722cc9e5f68..7a0a6199784 100644
--- a/engines/nancy/puzzledata.cpp
+++ b/engines/nancy/puzzledata.cpp
@@ -20,6 +20,8 @@
  */
 
 #include "engines/nancy/puzzledata.h"
+#include "engines/nancy/enginedata.h"
+#include "engines/nancy/nancy.h"
 
 namespace Nancy {
 
@@ -134,6 +136,24 @@ void JournalData::synchronize(Common::Serializer &ser) {
 	}
 }
 
+TableData::TableData() {
+	const TABL *tabl = (const TABL *)g_nancy->getEngineData("TABL");
+	assert(tabl);
+
+	currentIDs = tabl->startIDs;
+}
+
+void TableData::synchronize(Common::Serializer &ser) {
+	byte num = currentIDs.size();
+	ser.syncAsByte(num);
+
+	if (ser.isLoading()) {
+		currentIDs.resize(num);
+	}
+
+	ser.syncArray(currentIDs.data(), num, Common::Serializer::Uint16LE);
+}
+
 PuzzleData *makePuzzleData(const uint32 tag) {
 	switch(tag) {
 	case SliderPuzzleData::getTag():
@@ -148,6 +168,8 @@ PuzzleData *makePuzzleData(const uint32 tag) {
 		return new SoundEqualizerPuzzleData();
 	case JournalData::getTag():
 		return new JournalData();
+	case TableData::getTag():
+		return new TableData();
 	default:
 		return nullptr;
 	}
diff --git a/engines/nancy/puzzledata.h b/engines/nancy/puzzledata.h
index 4d71012e082..95a89ee7230 100644
--- a/engines/nancy/puzzledata.h
+++ b/engines/nancy/puzzledata.h
@@ -40,6 +40,7 @@ struct PuzzleData {
 
 struct SliderPuzzleData : public PuzzleData {
 	SliderPuzzleData();
+	virtual ~SliderPuzzleData() {}
 
 	static constexpr uint32 getTag() { return MKTAG('S', 'L', 'I', 'D'); }
 	virtual void synchronize(Common::Serializer &ser);
@@ -50,6 +51,7 @@ struct SliderPuzzleData : public PuzzleData {
 
 struct RippedLetterPuzzleData : public PuzzleData {
 	RippedLetterPuzzleData();
+	virtual ~RippedLetterPuzzleData() {}
 
 	static constexpr uint32 getTag() { return MKTAG('R', 'I', 'P', 'L'); }
 	virtual void synchronize(Common::Serializer &ser);
@@ -61,6 +63,7 @@ struct RippedLetterPuzzleData : public PuzzleData {
 
 struct TowerPuzzleData : public PuzzleData {
 	TowerPuzzleData();
+	virtual ~TowerPuzzleData() {}
 
 	static constexpr uint32 getTag() { return MKTAG('T', 'O', 'W', 'R'); }
 	virtual void synchronize(Common::Serializer &ser);
@@ -71,6 +74,7 @@ struct TowerPuzzleData : public PuzzleData {
 
 struct RiddlePuzzleData : public PuzzleData {
 	RiddlePuzzleData();
+	virtual ~RiddlePuzzleData() {}
 
 	static constexpr uint32 getTag() { return MKTAG('R', 'I', 'D', 'L'); }
 	virtual void synchronize(Common::Serializer &ser);
@@ -81,6 +85,7 @@ struct RiddlePuzzleData : public PuzzleData {
 
 struct SoundEqualizerPuzzleData : public PuzzleData {
 	SoundEqualizerPuzzleData();
+	virtual ~SoundEqualizerPuzzleData() {}
 
 	static constexpr uint32 getTag() { return MKTAG('S', 'E', 'Q', 'L'); }
 	virtual void synchronize(Common::Serializer &ser);
@@ -98,6 +103,18 @@ struct JournalData : public PuzzleData {
 	Common::HashMap<uint16, Common::Array<Common::String>> journalEntries;
 };
 
+// Contains data related to nancy6's exhibit puzzle, which
+// spans multiple scenes and uses several special-purpose AR types
+struct TableData : public PuzzleData {
+	TableData();
+	virtual ~TableData() {}
+
+	static constexpr uint32 getTag() { return MKTAG('T', 'A', 'B', 'L'); }
+	virtual void synchronize(Common::Serializer &ser);
+
+	Common::Array<uint16> currentIDs;
+};
+
 PuzzleData *makePuzzleData(const uint32 tag);
 
 } // End of namespace Nancy


Commit: 9dd73a5edb14c2ee82478a0ad48f1adf0e5c2b02
    https://github.com/scummvm/scummvm/commit/9dd73a5edb14c2ee82478a0ad48f1adf0e5c2b02
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-24T17:43:29+03:00

Commit Message:
NANCY: Only set minimum volume in nancy3 and up

It seems the addition of a minimum volume for sounds only
got added when the sound system was revamped in nancy3.
This fixes an incorrect clock sound playing in the diner
in nancy1.

Changed paths:
    engines/nancy/sound.cpp


diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 16f73c6d005..fb0b9eb6a17 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -304,7 +304,9 @@ void SoundManager::playSound(uint16 channelID) {
 
 	// Set a minimum volume (10 percent was chosen arbitrarily, but sounds reasonably close)
 	// Fix for nancy3 scene 6112, but NOT a hack; the original engine also set a minimum volume for all sounds
-	chan.volume = 10 + ((int)chan.volume * 90) / 100;
+	if (g_nancy->getGameType() >= kGameTypeNancy3) {
+		chan.volume = 10 + ((int)chan.volume * 90) / 100;
+	}
 
 	// Init 3D sound
 	if (chan.playCommands & ~kPlaySequential && chan.effectData) {


Commit: 0e192ba542f818f4551aa5f6f2b0dee11e74d68e
    https://github.com/scummvm/scummvm/commit/0e192ba542f818f4551aa5f6f2b0dee11e74d68e
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-24T17:43:29+03:00

Commit Message:
NANCY: Describe all ActionRecords

Added comments describing the uses of all currently
implemented non-puzzle ActionRecords.

Changed paths:
    engines/nancy/action/autotext.h
    engines/nancy/action/conversation.h
    engines/nancy/action/miscrecords.h
    engines/nancy/action/navigationrecords.h
    engines/nancy/action/overlay.h
    engines/nancy/action/secondarymovie.h
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/soundrecords.h


diff --git a/engines/nancy/action/autotext.h b/engines/nancy/action/autotext.h
index 5eb0c794eec..4d3bd90e63f 100644
--- a/engines/nancy/action/autotext.h
+++ b/engines/nancy/action/autotext.h
@@ -28,8 +28,7 @@
 namespace Nancy {
 namespace Action {
 
-// Action record used for rendering text inside the game viewport
-// (before its introduction all text outside the textbox was prerendered)
+// Action record used for rendering text inside the game viewport.
 // Can be used in two ways: for single-use texts that get thrown away
 // after a scene change, or for permanent storage (used in nancy's journal)
 // Does not own or display any image data; it draws to a surface inside
diff --git a/engines/nancy/action/conversation.h b/engines/nancy/action/conversation.h
index bf8ec111603..535242fe8fb 100644
--- a/engines/nancy/action/conversation.h
+++ b/engines/nancy/action/conversation.h
@@ -28,7 +28,17 @@
 namespace Nancy {
 namespace Action {
 
-// The base class for conversations, with no video data
+// The base class for conversations, with no video data. Contains the following:
+// - a base sound for the NPC's speech and its caption (mandatory)
+// - a list of possible player responses, also with sounds and captions (optional)
+// Captions are displayed in the Textbox, and player responses are also selectable there.
+// Captions are hypertext; meaning, they contain extra data related to the text (see misc/hypertext.h)
+// A conversation will auto-advance to a next scene when no responses are available; the next scene
+// can either be described within the Conversation data, or can be whatever's pushed onto the scene "stack".
+// Also supports branching scenes depending on a condition, though that is only used in older games.
+// Player responses can also be conditional; the original engine had special-purpose "infocheck"
+// functions, two per character ID, which were used to evaluate those conditions. We replace that with
+// the data bundled inside nancy.dat (see devtools/create_nancy).
 class ConversationSound : public RenderActionRecord {
 public:
 	ConversationSound();
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index ce5782a2192..8be01a9f8f6 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -34,6 +34,7 @@ class Unimplemented : public ActionRecord {
 	void execute() override;
 };
 
+// Changes the palette for the current scene's background. TVD only.
 class PaletteThisScene : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -48,6 +49,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "PaletteThisScene"; }
 };
 
+// Changes the palette for the next scene's background. TVD only.
 class PaletteNextScene : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -59,6 +61,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "PaletteNextScene"; }
 };
 
+// Turns on (temporary) lightning effect. TVD Only.
 class LightningOn : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -72,6 +75,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "LightningOn"; }
 };
 
+// Requests either a fade between two scenes, or a fade to black; fade executes when scene is changed. Nancy2 and up.
 class SpecialEffect : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -85,6 +89,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "SpecialEffect"; }
 };
 
+// Changes the selected value inside the TableData. Value can be incremented, decremented, or not changed.
+// Also responsible for checking whether all values are correct (as described in the TABL chunk). Nancy6 and up.
 class TableIndexSetValueHS : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -105,6 +111,7 @@ protected:
 	Common::Array<HotspotDescription> _hotspots;
 };
 
+// Adds a caption to the textbox.
 class TextBoxWrite : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -116,6 +123,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "TextBoxWrite"; }
 };
 
+// Clears the textbox. Used very rarely.
 class TextboxClear : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -125,6 +133,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "TextboxClear"; }
 };
 
+// Changes the in-game time. Used prior to the introduction of SetPlayerClock.
 class BumpPlayerClock : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -138,6 +147,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "BumpPlayerClock"; }
 };
 
+// Creates a Second Chance save.
 class SaveContinueGame : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -147,6 +157,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "SaveContinueGame"; }
 };
 
+// Stops the screen from rendering. Our rendering system is different from the original engine's,
+// so we have no use for this.
 class TurnOffMainRendering : public Unimplemented {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -155,6 +167,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "TurnOffMainRendering"; }
 };
 
+// Restarts screen rendering. Our rendering system is different from the original engine's,
+// so we have no use for this.
 class TurnOnMainRendering : public Unimplemented {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -163,6 +177,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "TurnOnMainRendering"; }
 };
 
+// Starts the timer. Used in combination with Dependency types that check for
+// how much time has passed since the timer was started.
 class ResetAndStartTimer : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -172,6 +188,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "ResetAndStartTimer"; }
 };
 
+// Stops the timer.
 class StopTimer : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -181,6 +198,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "StopTimer"; }
 };
 
+// Sets up to 10 flags at once.
 class EventFlags : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -192,6 +210,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "EventFlags"; }
 };
 
+// Sets up to 10 flags when clicked. Hotspot can move alongside background frame.
 class EventFlagsMultiHS : public EventFlags {
 public:
 	EventFlagsMultiHS(bool isCursor) : _isCursor(isCursor) {}
@@ -212,6 +231,9 @@ protected:
 	Common::String getRecordTypeName() const override { return _isCursor ? "EventFlagsCursorHS" : "EventFlagsMultiHS"; }
 };
 
+// Stops the game and boots the player back to the Menu screen, while also making sure
+// they can't Continue. The devs took care to add Second Chance saves before every one
+// of these, to make sure the player can return to a state just before the dangerous part.
 class LoseGame : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -221,6 +243,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "LoseGame"; }
 };
 
+// Adds a scene to the "stack" (which is just a single value). Used in combination with PopScene.
 class PushScene : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -230,6 +253,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "PushScene"; }
 };
 
+// Changes to the scene pushed onto the "stack". Scenes can be pushed via PushScene, or Conversation types.
 class PopScene : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -239,6 +263,11 @@ protected:
 	Common::String getRecordTypeName() const override { return "PopScene"; }
 };
 
+// Ends the game and boots the player to the Credits screen.
+// TODO: The original engine also sets a config option called PlayerWonTheGame,
+// which in turn is used to trigger whichever event flag marks that the player
+// has beat the game at least once, which in turn allows easter eggs to be shown.
+// We currently support none of this.
 class WinGame : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -248,6 +277,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;
@@ -259,6 +289,7 @@ 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;
@@ -270,6 +301,9 @@ 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
+// only have two: 0 and 2.
 class DifficultyLevel : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -282,6 +316,9 @@ 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;
@@ -305,6 +342,10 @@ protected:
 	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;
@@ -319,6 +360,9 @@ protected:
 	Common::String getRecordTypeName() const override { return "InventorySoundOverride"; }
 };
 
+// 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.
 class HintSystem : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/action/navigationrecords.h b/engines/nancy/action/navigationrecords.h
index 90978e6ef6b..c88f38922a5 100644
--- a/engines/nancy/action/navigationrecords.h
+++ b/engines/nancy/action/navigationrecords.h
@@ -27,6 +27,7 @@
 namespace Nancy {
 namespace Action {
 
+// Simply changes the scene
 class SceneChange : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -38,6 +39,9 @@ protected:
 	Common::String getRecordTypeName() const override { return "SceneChange"; }
 };
 
+// Changes the scene when clicked. Hotspot can move along with scene background frame.
+// Nancy4 introduced several sub-types with a specific mouse cursor to show when
+// hovering; all of them are handled in this class as well.
 class HotMultiframeSceneChange : public SceneChange {
 public:
 	HotMultiframeSceneChange(CursorManager::CursorType hoverCursor) : _hoverCursor(hoverCursor) {}
@@ -68,6 +72,9 @@ protected:
 	CursorManager::CursorType _hoverCursor;
 };
 
+// Changes the scene when clicked; does _not_ move with scene background.
+// Nancy4 introduced several sub-types with a specific mouse cursor to show when
+// hovering; all of them are handled in this class as well.
 class Hot1FrSceneChange : public SceneChange {
 public:
 	Hot1FrSceneChange(CursorManager::CursorType hoverCursor) : _hoverCursor(hoverCursor) {}
@@ -106,6 +113,9 @@ protected:
 	CursorManager::CursorType _hoverCursor;
 };
 
+// Changes the scene when clicked. Hotspot can move along with scene background frame.
+// However, the scene it changes to can be one of two options, picked based on
+// a provided condition.
 class HotMultiframeMultisceneChange : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -123,6 +133,9 @@ protected:
 	Common::String getRecordTypeName() const override { return "HotMultiframeMultisceneChange"; }
 };
 
+// Changes the scene when clicked. Hotspot can move along with scene background frame.
+// However, the scene it changes to can be one of several options, picked based on
+// the item the player is currently holding.
 class HotMultiframeMultisceneCursorTypeSceneChange : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -138,6 +151,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "HotMultiframeMultisceneCursorTypeSceneChange"; }
 };
 
+// Simply switches to the Map state. TVD/nancy1 only.
 class MapCall : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -149,6 +163,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "MapCall"; }
 };
 
+// Switches to the Map state when clicked; does _not_ move with background frame. TVD/nancy1 only.
 class MapCallHot1Fr : public MapCall {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -161,6 +176,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "MapCallHot1Fr"; }
 };
 
+// Switches to the Map state when clicked. Hotspot can move along with scene background frame. TVD/nancy1 only.
 class MapCallHotMultiframe : public MapCall {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/action/overlay.h b/engines/nancy/action/overlay.h
index f2748932069..5a34b0221b3 100644
--- a/engines/nancy/action/overlay.h
+++ b/engines/nancy/action/overlay.h
@@ -27,13 +27,20 @@
 namespace Nancy {
 namespace Action {
 
-// ActionRecord describing an overlay on top of the viewport.
-// That overlay can be either a short animation, or a static bitmap
-// that changes depending on the current viewport frame.
-// This class covers three different ActionRecord types:
-// - PlayStaticBitmapAnimation: nancy1 only, does not support static mode
-// - PlayIntStaticBitmapAnimation: nancy1 only, same as above but supports being interrupted by an event flag
-// - Overlay: nancy2 and above, supports static mode
+// Places a static image or a looping animation on top of the background
+// Can move along with the scene's background frame, however:
+// - in animation mode, the animation is the same for every background frame
+// - in static mode, every background frame gets its own static image
+// Also supports:
+// - playing a sound;
+// - playing backwards;
+// - looping (non-looping animated overlays are very rare);
+// - getting interrupted by an event flag;
+// - changing the scene/setting flags when clicked/interrupted
+// Originally introduced in nancy1, where it was split into two different types:
+// PlayStaticBitmapAnimation and PlayIntStaticBitmapAnimation (the latter was interruptible)
+// In nancy2, the two got merged inside the newly-renamed Overlay;
+// that was also when static mode got introduced.
 class Overlay : public RenderActionRecord {
 public:
 	Overlay(bool interruptible) : RenderActionRecord(7), _isInterruptible(interruptible) {}
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 72831cf9578..856ec830f35 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -28,6 +28,14 @@
 namespace Nancy {
 namespace Action {
 
+// Plays an AVF video. Optionally supports:
+// - playing a sound;
+// - reverse playback;
+// - moving with the scene's background frame;
+// - hiding of player cursor (and thus, disabling input);
+// - setting event flags on a specific frame, as well as at the end of the video;
+// - changing the scene after playback ends
+// Mostly used for cinematics, with some occasional uses for background animations
 class PlaySecondaryMovie : public RenderActionRecord {
 public:
 	static const byte kMovieSceneChange			= 5;
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index a1d04066f6d..92edd6f320e 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -28,8 +28,10 @@
 namespace Nancy {
 namespace Action {
 
-// ActionRecord that shows NPC animations outside of dialogue. Supports
-// different animations depending on whether the NPC is hovered by the mouse
+// Shows an (optionally) looping AVF video, which can move around
+// the screen with the background frame. When hovered, the video can
+// play a special animation instead of the normal looping one.
+// Used for character animations _outside_ of conversations.
 class PlaySecondaryVideo : public RenderActionRecord {
 public:
 	static const byte kNoVideoHotspots	= 1;
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index 09d8ea64144..d579b8cc29e 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -27,6 +27,9 @@
 namespace Nancy {
 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 {
 public:
 	PlayDigiSound() {}
@@ -45,6 +48,8 @@ protected:
 	Common::String getRecordTypeName() const override;
 };
 
+// The same as PlayDigiSound, but with the addition of captioning text,
+// which gets displayed inside the Textbox.
 class PlayDigiSoundCC : public PlayDigiSound {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -56,6 +61,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlayDigiSoundCC"; }
 };
 
+// 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 {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -67,6 +74,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; }
 };
 
+// Plays a sound effect; has multiple hotspots, one per scene background frame.
+// Used in exactly two places; one scene in tvd, and one in nancy1
 class PlaySoundMultiHS : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -81,6 +90,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlaySoundMultiHS"; }
 };
 
+// Stops a sound if it's loaded and playing. Used very rarely, as sounds (usually)
+// get auto-stopped on a scene change
 class StopSound : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -93,6 +104,8 @@ protected:
 	Common::String getRecordTypeName() const override { return "StopSound"; }
 };
 
+// Same as PlayDigiSound, except it randomly picks between one of several
+// provided sound files; all other settings for the sound are shared.
 class PlayRandomSound : public PlayDigiSound {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
@@ -106,6 +119,10 @@ protected:
 	Common::String getRecordTypeName() const override { return "PlayRandomSound"; }
 };
 
+// Same as PlayDigiSound, 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 {
 public:
 	void readData(Common::SeekableReadStream &stream) override;




More information about the Scummvm-git-logs mailing list