[Scummvm-git-logs] scummvm master -> 4adbfaa4ce2c678738c3fe448ccaf9225a9b1f5d

fracturehill noreply at scummvm.org
Sat Sep 16 14:05:37 UTC 2023


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

Summary:
ba5696786f NANCY: Implement InventorySoundOverride
b3c81e7074 NANCY: Auto-clear inventory item captions
4adbfaa4ce NANCY: Correctly render static Overlays


Commit: ba5696786f75860118fd83f04743940373151c3f
    https://github.com/scummvm/scummvm/commit/ba5696786f75860118fd83f04743940373151c3f
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T17:04:37+03:00

Commit Message:
NANCY: Implement InventorySoundOverride

Implemented the action record type responsible for
overriding the default "I can't" sound for a specific item.
Also implemented closed captioning for the same use
case, since it had previously been overlooked.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    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


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 2d0b30cc541..68e7f89a506 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -60,15 +60,10 @@ void ActionManager::handleInput(NancyInput &input) {
 				processDependency(rec->_dependencies, *rec, false);
 
 				if (!rec->_dependencies.satisfied) {
-					if (g_nancy->getGameType() >= kGameTypeNancy2 && rec->_cursorDependency != nullptr) {
-						const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
-						assert(inventoryData);
-
-						const SoundDescription &sound = inventoryData->itemDescriptions[rec->_cursorDependency->label].specificCantSound;
-						g_nancy->_sound->loadSound(sound);
-						g_nancy->_sound->playSound(sound);
+					if (rec->_cursorDependency != nullptr) {
+						NancySceneState.playItemCantSound(rec->_cursorDependency->label);
 					} else {
-						g_nancy->_sound->playSound("CANT");
+						NancySceneState.playItemCantSound();
 					}
 				} else {
 					rec->_state = ActionRecord::ExecutionState::kActionTrigger;
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index fdb1972dc36..95d30433ba7 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -183,6 +183,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 		return new RemoveInventoryNoHS();
 	case 122:
 		return new ShowInventoryItem();
+	case 123:
+		return new InventorySoundOverride();
 	case 150:
 		return new PlayDigiSoundAndDie();
 	case 151:
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 54f35363c2e..b1572eab303 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -385,6 +385,22 @@ void ShowInventoryItem::execute() {
 	}
 }
 
+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 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 e1e8d5daf4d..e00c3714255 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -285,6 +285,20 @@ protected:
 	bool isViewportRelative() const override { return true; }
 };
 
+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 HintSystem : public ActionRecord {
 public:
 	void readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 72712055fe7..660a60a22a9 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -43,36 +43,42 @@ class NancyEngine;
 // Other, more specific constants are declared within their related classes,
 // so as not to litter the namespace
 
-static const int8 kFlagNoLabel			= -1;
-static const int8 kEvNoEvent			= -1;
-static const int8 kFrNoFrame			= -1;
+static const int8 kFlagNoLabel						= -1;
+static const int8 kEvNoEvent						= -1;
+static const int8 kFrNoFrame						= -1;
 
 // Inventory items use types
-static const byte kInvItemUseThenLose	= 0;
-static const byte kInvItemKeepAlways	= 1;
-static const byte kInvItemReturn		= 2;
+static const byte kInvItemUseThenLose				= 0;
+static const byte kInvItemKeepAlways				= 1;
+static const byte kInvItemReturn					= 2;
+
+// Inventory item sound override commands
+static const byte kInvSoundOverrideCommandNoSound	= 0;
+static const byte kInvSoundOverrideCommandTurnOff	= 1;
+static const byte kInvSoundOverrideCommandNewSound	= 2;
+static const byte kInvSoundOverrideCommandICant		= 3;
 
 // Dependency types
-static const byte kFlagEvent			= 1;
-static const byte kFlagInventory		= 2;
-static const byte kFlagCursor			= 3;
+static const byte kFlagEvent						= 1;
+static const byte kFlagInventory					= 2;
+static const byte kFlagCursor						= 3;
 
 // Scene sound flags
-static const byte kContinueSceneSound	= 1;
-static const byte kLoadSceneSound		= 0;
+static const byte kContinueSceneSound				= 1;
+static const byte kLoadSceneSound					= 0;
 
 // Clock bump types
-static const byte kAbsoluteClockBump 	= 1;
-static const byte kRelativeClockBump 	= 2;
+static const byte kAbsoluteClockBump 				= 1;
+static const byte kRelativeClockBump 				= 2;
 
 // Time of day
-static const byte kPlayerDay			= 0;
-static const byte kPlayerNight			= 1;
-static const byte kPlayerDuskDawn		= 2;
+static const byte kPlayerDay						= 0;
+static const byte kPlayerNight						= 1;
+static const byte kPlayerDuskDawn					= 2;
 
 // Video
-static const byte kSmallVideoFormat		= 1;
-static const byte kLargeVideoFormat		= 2;
+static const byte kSmallVideoFormat					= 1;
+static const byte kLargeVideoFormat					= 2;
 
 enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
 
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 306024c333a..e66d52fd718 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -115,7 +115,7 @@ INV::INV(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	readRect(s, curtainsScreenPosition);
 	s.syncAsUint16LE(curtainsFrameTime);
 
-	s.skip(2, kGameTypeNancy3); // Unknown, 3000
+	s.syncAsUint16LE(captionAutoClearTime, kGameTypeNancy3);
 
 	readFilename(s, inventoryBoxIconsImageName);
 	readFilename(s, inventoryCursorsImageName);
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index 8eb7aceb21b..b58727bd502 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -115,6 +115,8 @@ struct INV : public EngineData {
 	Common::Rect curtainsScreenPosition;
 	uint16 curtainsFrameTime;
 
+	uint16 captionAutoClearTime = 3000;
+
 	Common::String inventoryBoxIconsImageName;
 	Common::String inventoryCursorsImageName;
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 13c03c1788a..64ac3eb6fb2 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -295,6 +295,119 @@ void Scene::setHeldItem(int16 id)  {
 	_flags.heldItem = id; g_nancy->_cursorManager->setCursorItemID(id);
 }
 
+void Scene::installInventorySoundOverride(byte command, const SoundDescription &sound, const Common::String &caption, uint16 itemID) {
+	InventorySoundOverride newOverride;
+
+	switch (command) {
+	case kInvSoundOverrideCommandNoSound :
+		// Make the sound silent
+		newOverride.sound = sound;
+		newOverride.sound.name = "NO SOUND";
+		newOverride.caption = caption; // Assumes the caption will be empty
+		_inventorySoundOverrides.setVal(itemID, newOverride);
+		break;
+	case kInvSoundOverrideCommandNewSound :
+		newOverride.sound = sound;
+		newOverride.caption = caption;
+		_inventorySoundOverrides.setVal(itemID, newOverride);
+		break;
+	case kInvSoundOverrideCommandICant :
+		// Make the sound the default "I can't use that here"
+		newOverride.isDefault = true;
+		_inventorySoundOverrides.setVal(itemID, newOverride);
+		break;
+	case kInvSoundOverrideCommandTurnOff :
+		// Remove any previous override
+		_inventorySoundOverrides.erase(itemID);
+		break;
+	default :
+		return;
+	}
+}
+
+void Scene::playItemCantSound(int16 itemID) {
+	// Improvement: nancy2 never shows the caption text, even though it exists in the data; we show it
+	const INV *inventoryData = (const INV *)g_nancy->getEngineData("INV");
+	assert(inventoryData);
+
+	if (itemID < 0) {
+		if (inventoryData->cantSound.name.size()) {
+			// Play default "can't" inside inventory data (if present)
+			g_nancy->_sound->loadSound(inventoryData->cantSound);
+			g_nancy->_sound->playSound(inventoryData->cantSound);
+
+			if (ConfMan.getBool("subtitles")) {
+				_textbox.addTextLine(inventoryData->cantText);
+			}
+		} else {
+			// TVD and nancy1 contain no sound data in INV, and have no captions
+			g_nancy->_sound->playSound("CANT");
+		}
+	} else if ((uint)itemID < _flags.items.size()) {
+		if (_inventorySoundOverrides.contains(itemID)) {
+			// We have an override installed
+			InventorySoundOverride &override = _inventorySoundOverrides[itemID];
+			if (!override.isDefault) {
+				// Not set to the default sound, play the override
+				g_nancy->_sound->loadSound(override.sound);
+				g_nancy->_sound->playSound(override.sound);
+
+				if (ConfMan.getBool("subtitles")) {
+					_textbox.addTextLine(override.caption);
+				}
+				return;
+			} else {
+				// Play the default "I can't" sound
+				const INV::ItemDescription item = inventoryData->itemDescriptions[itemID];
+
+				if (item.generalCantSound.name.size()) {
+					// This field only exists in nancy2
+					g_nancy->_sound->loadSound(item.generalCantSound);
+					g_nancy->_sound->playSound(item.generalCantSound);
+
+					if (ConfMan.getBool("subtitles")) {
+						_textbox.addTextLine(item.generalCantText);
+					}
+				} else if (inventoryData->cantSound.name.size()) {
+					g_nancy->_sound->loadSound(inventoryData->cantSound);
+					g_nancy->_sound->playSound(inventoryData->cantSound);
+
+					if (ConfMan.getBool("subtitles")) {
+						_textbox.addTextLine(inventoryData->cantText);
+					}
+				} else {
+					// Should be unreachable
+					g_nancy->_sound->playSound("CANT");
+				}
+			}
+		}
+
+		// No override installed
+		const INV::ItemDescription item = inventoryData->itemDescriptions[itemID];
+
+		if (item.specificCantSound.name.size()) {
+			// The inventory data contains a custom "can't" sound for this item
+			g_nancy->_sound->loadSound(item.specificCantSound);
+			g_nancy->_sound->playSound(item.specificCantSound);
+
+			if (ConfMan.getBool("subtitles")) {
+				_textbox.addTextLine(item.specificCantText);
+			}
+		} else if (inventoryData->cantSound.name.size()) {
+			// No custom sound, play default "can't" inside inventory data. Should (?) be unreachable
+			g_nancy->_sound->loadSound(inventoryData->cantSound);
+			g_nancy->_sound->playSound(inventoryData->cantSound);
+
+			if (ConfMan.getBool("subtitles")) {
+				_textbox.addTextLine(inventoryData->cantText);
+			}
+		} else {
+			// TVD and nancy1 contain no sound data in INV, and have no captions
+			g_nancy->_sound->playSound("CANT");
+		}
+	}
+}
+
 void Scene::setEventFlag(int16 label, byte flag) {
 	if (label >= 1000) {
 		// In nancy3 and onwards flags begin from 1000
@@ -709,6 +822,11 @@ void Scene::load() {
 		}
 	}
 
+	for (auto &override : _inventorySoundOverrides) {
+		g_nancy->_sound->stopSound(override._value.sound);
+	}
+	_inventorySoundOverrides.clear();
+
 	_timers.sceneTime = 0;
 
 	_flags.sceneCounts.getOrCreateVal(_sceneState.currentScene.sceneID)++;
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 68f8cb9d789..18f99dc889d 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -137,6 +137,9 @@ public:
 	void setHeldItem(int16 id);
 	byte hasItem(int16 id) const { return _flags.items[id]; }
 
+	void installInventorySoundOverride(byte command, const SoundDescription &sound, const Common::String &caption, uint16 itemID);
+	void playItemCantSound(int16 itemID = -1);
+
 	void setEventFlag(int16 label, byte flag);
 	void setEventFlag(FlagDescription eventFlag);
 	bool getEventFlag(int16 label, byte flag) const;
@@ -241,6 +244,12 @@ private:
 		int16 primaryVideoResponsePicked = -1;
 	};
 
+	struct InventorySoundOverride {
+		bool isDefault = false; // When true, other fields are ignored
+		SoundDescription sound;
+		Common::String caption;
+	};
+
 	// UI
 	UI::FullScreenImage _frame;
 	UI::Viewport _viewport;
@@ -267,6 +276,7 @@ private:
 	int16 _lastHintCharacter;
 	int16 _lastHintID;
 	NancyState::NancyState _gameStateRequested;
+	Common::HashMap<uint16, InventorySoundOverride> _inventorySoundOverrides;
 
 	Misc::Lightning *_lightning;
 	Common::Queue<Misc::SpecialEffect> _specialEffects;


Commit: b3c81e7074948e7f5deb2ec15f5d3cb11a772b14
    https://github.com/scummvm/scummvm/commit/b3c81e7074948e7f5deb2ec15f5d3cb11a772b14
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T17:04:37+03:00

Commit Message:
NANCY: Auto-clear inventory item captions

Item "can't" sounds' captions get automatically cleared after
a certain time delay, which is now implemented.

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


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 64ac3eb6fb2..ad9cce49e3b 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -337,7 +337,7 @@ void Scene::playItemCantSound(int16 itemID) {
 			g_nancy->_sound->playSound(inventoryData->cantSound);
 
 			if (ConfMan.getBool("subtitles")) {
-				_textbox.addTextLine(inventoryData->cantText);
+				_textbox.addTextLine(inventoryData->cantText, inventoryData->captionAutoClearTime);
 			}
 		} else {
 			// TVD and nancy1 contain no sound data in INV, and have no captions
@@ -353,7 +353,7 @@ void Scene::playItemCantSound(int16 itemID) {
 				g_nancy->_sound->playSound(override.sound);
 
 				if (ConfMan.getBool("subtitles")) {
-					_textbox.addTextLine(override.caption);
+					_textbox.addTextLine(override.caption, inventoryData->captionAutoClearTime);
 				}
 				return;
 			} else {
@@ -366,14 +366,14 @@ void Scene::playItemCantSound(int16 itemID) {
 					g_nancy->_sound->playSound(item.generalCantSound);
 
 					if (ConfMan.getBool("subtitles")) {
-						_textbox.addTextLine(item.generalCantText);
+						_textbox.addTextLine(item.generalCantText, inventoryData->captionAutoClearTime);
 					}
 				} else if (inventoryData->cantSound.name.size()) {
 					g_nancy->_sound->loadSound(inventoryData->cantSound);
 					g_nancy->_sound->playSound(inventoryData->cantSound);
 
 					if (ConfMan.getBool("subtitles")) {
-						_textbox.addTextLine(inventoryData->cantText);
+						_textbox.addTextLine(inventoryData->cantText, inventoryData->captionAutoClearTime);
 					}
 				} else {
 					// Should be unreachable
@@ -391,7 +391,7 @@ void Scene::playItemCantSound(int16 itemID) {
 			g_nancy->_sound->playSound(item.specificCantSound);
 
 			if (ConfMan.getBool("subtitles")) {
-				_textbox.addTextLine(item.specificCantText);
+				_textbox.addTextLine(item.specificCantText, inventoryData->captionAutoClearTime);
 			}
 		} else if (inventoryData->cantSound.name.size()) {
 			// No custom sound, play default "can't" inside inventory data. Should (?) be unreachable
@@ -399,7 +399,7 @@ void Scene::playItemCantSound(int16 itemID) {
 			g_nancy->_sound->playSound(inventoryData->cantSound);
 
 			if (ConfMan.getBool("subtitles")) {
-				_textbox.addTextLine(inventoryData->cantText);
+				_textbox.addTextLine(inventoryData->cantText, inventoryData->captionAutoClearTime);
 			}
 		} else {
 			// TVD and nancy1 contain no sound data in INV, and have no captions
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index f40e352feff..b7aac9c70f1 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -84,6 +84,10 @@ void Textbox::registerGraphics() {
 }
 
 void Textbox::updateGraphics() {
+	if (_autoClearTime && g_nancy->getTotalPlayTime() > _autoClearTime) {
+		clear();
+	}
+
 	if (_needsTextRedraw) {
 		drawTextbox();
 	}
@@ -352,14 +356,19 @@ void Textbox::clear() {
 		_fontIDOverride = -1;
 		onScrollbarMove();
 		_needsRedraw = true;
+		_autoClearTime = 0;
 	}
 }
 
-void Textbox::addTextLine(const Common::String &text) {
-	// Scan for the hotspot token and assume the text is the main text if not found
+void Textbox::addTextLine(const Common::String &text, uint32 autoClearTime) {
 	_textLines.push_back(text);
-
 	_needsTextRedraw = true;
+
+	if (autoClearTime != 0) {
+		// Start a timer, after which the textbox will automatically be cleared.
+		// Currently only used by inventory closed captions
+		_autoClearTime = g_nancy->getTotalPlayTime() + autoClearTime;
+	}
 }
 
 // A text line will often be broken up into chunks separated by nulls, use
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index 23bb982360a..5f4fcc6c3f2 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -47,7 +47,7 @@ public:
 	void drawTextbox();
 	void clear();
 
-	void addTextLine(const Common::String &text);
+	void addTextLine(const Common::String &text, uint32 autoClearTime = 0);
 	void overrideFontID(const uint fontID) { _fontIDOverride = fontID; };
 
 	static void assembleTextLine(char *rawCaption, Common::String &output, uint size);
@@ -77,6 +77,8 @@ private:
 	bool _needsTextRedraw;
 	float _scrollbarPos;
 
+	uint32 _autoClearTime = 0;
+
 	int _fontIDOverride;
 };
 


Commit: 4adbfaa4ce2c678738c3fe448ccaf9225a9b1f5d
    https://github.com/scummvm/scummvm/commit/4adbfaa4ce2c678738c3fe448ccaf9225a9b1f5d
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T17:04:37+03:00

Commit Message:
NANCY: Correctly render static Overlays

Static Overlays (may) use different source rects from animated
ones, which is now correctly implemented. This fixes the
out of service sign on the nancy5 key machine.

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


diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index 0fc78ed8308..ba24e53706a 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -237,16 +237,29 @@ Common::String Overlay::getRecordTypeName() const {
 void Overlay::setFrame(uint frame) {
 	_currentFrame = frame;
 
+	Common::Rect srcRect;
+
 	// Workaround for:
 	// - the fireplace in nancy2 scene 2491, where one of the rects is invalid.
 	// - the ball thing in nancy2 scene 1562, where one of the rects is twice as tall as it should be
 	// Assumes all rects in a single animation have the same dimensions
-	Common::Rect srcRect = _srcRects[frame];
+	srcRect = _srcRects[frame];
 	if (!srcRect.isValidRect() || srcRect.height() > _srcRects[0].height()) {
 		srcRect.setWidth(_srcRects[0].width());
 		srcRect.setHeight(_srcRects[0].height());
 	}
 
+	if (_overlayType == kPlayOverlayStatic && srcRect.isEmpty()) {
+		// In static mode the srcRect above may be empty (see "out of service" sign in nancy5 scenes 2056, 2075),
+		// in which case we need to take the rect from the bitmap struct instead. Note that this is a backup,
+		// since if we use the bitmap src rect in all cases rendering can be incorrect (see same sign in nancy5 scene 2000)
+		for (uint i = 0; i < _bitmaps.size(); ++i) {
+			if (_currentViewportFrame == _bitmaps[i].frameID) {
+				srcRect = _bitmaps[i].src;
+			}
+		}
+	}
+
 	_drawSurface.create(_fullSurface, srcRect);
 
 	setTransparent(_transparency == kPlayOverlayTransparent);




More information about the Scummvm-git-logs mailing list