[Scummvm-git-logs] scummvm master -> 5b2929ce4d9e821446898d1200a37415321cd6c2

fracturehill noreply at scummvm.org
Tue Feb 6 19:32:40 UTC 2024


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

Summary:
482da55d33 NANCY: Fix drawing of MouseLightPuzzle
716041603d NANCY: Carry over unshown text to next scene
66a7fdb58e NANCY: Implement SpigotPuzzle
d468406f25 Revert "NANCY: Fix fades in nancy7 dog scare sequence"
d6016dfbc6 NANCY: Proper fix for nancy7 movie fades
dd749d6042 NANCY: Add Bink support to play_video command
a2f6f38105 NANCY: Fix Overlay crash in nancy7
5b2929ce4d NANCY: Fix kThroughBlack special effects


Commit: 482da55d33cd602b482d3152723f86a1ac61bd99
    https://github.com/scummvm/scummvm/commit/482da55d33cd602b482d3152723f86a1ac61bd99
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:27+01:00

Commit Message:
NANCY: Fix drawing of MouseLightPuzzle

Previously, the record worked based on the assumption that
its background will be completely black. This is not the
case in several nancy7 scenes. To support it, an additional
4-byte ARGB pixel format is now allowed in the engine, since
we need to be able to set an alpha value for the surface.

Changed paths:
    engines/nancy/action/puzzle/mouselightpuzzle.cpp
    engines/nancy/action/puzzle/mouselightpuzzle.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h


diff --git a/engines/nancy/action/puzzle/mouselightpuzzle.cpp b/engines/nancy/action/puzzle/mouselightpuzzle.cpp
index e8853784946..89f9b9e88dd 100644
--- a/engines/nancy/action/puzzle/mouselightpuzzle.cpp
+++ b/engines/nancy/action/puzzle/mouselightpuzzle.cpp
@@ -27,7 +27,6 @@
 #include "engines/nancy/util.h"
 
 #include "engines/nancy/state/scene.h"
-
 #include "engines/nancy/action/puzzle/mouselightpuzzle.h"
 
 namespace Nancy {
@@ -35,14 +34,17 @@ namespace Action {
 
 void MouseLightPuzzle::init() {
 	Common::Rect screenBounds = NancySceneState.getViewport().getBounds();
-	_drawSurface.create(screenBounds.width(), screenBounds.height(), g_nancy->_graphicsManager->getInputPixelFormat());
-	_drawSurface.clear();
+
+	Graphics::ManagedSurface baseImage;
+	g_nancy->_resource->loadImage(_imageName, baseImage);
+
+	_drawSurface.create(screenBounds.width(), screenBounds.height(), g_nancy->_graphicsManager->getTransparentPixelFormat());
+	_drawSurface.blitFrom(baseImage);
+	((Graphics::Surface)_drawSurface).setAlpha(0);
+
 	setVisible(true);
 	moveTo(screenBounds);
 
-	g_nancy->_resource->loadImage(_imageName, _baseImage);
-	
-	_mask.copyFrom(_drawSurface);
 	_maskCircle.create(_radius * 2, _radius * 2, g_nancy->_graphicsManager->getInputPixelFormat());
 	_maskCircle.clear();
 
@@ -87,7 +89,7 @@ void MouseLightPuzzle::handleInput(NancyInput &input) {
 	}
 
 	_lastMousePos = input.mousePos;
-	_drawSurface.clear();
+	((Graphics::Surface)_drawSurface).setAlpha(0);
 	_needsRedraw = true;
 
 	Common::Rect vpScreenPos = NancySceneState.getViewport().convertViewportToScreen(_screenPosition);
@@ -102,10 +104,16 @@ void MouseLightPuzzle::handleInput(NancyInput &input) {
 	Common::Rect srcRect = _maskCircle.getBounds();
 	Common::Rect::getBlitRect(blitDestPoint, srcRect, _drawSurface.getBounds());
 
-	_mask.clear();
-	_mask.copyRectToSurface(_maskCircle, blitDestPoint.x, blitDestPoint.y, srcRect);
-
-	_drawSurface.transBlitFrom(_baseImage, Common::Point(), _mask);
+	// Copy over the transparency to the draw surface
+	for (int y = srcRect.top; y < srcRect.bottom; ++y) {
+		uint32 *drawSurfPtr = (uint32 *)_drawSurface.getBasePtr(blitDestPoint.x, y + blitDestPoint.y - srcRect.top);
+		uint16 *circlePtr = (uint16 *)_maskCircle.getBasePtr(srcRect.left, y);
+		for (int x = srcRect.left; x < srcRect.right; ++x) {
+			*drawSurfPtr = (*drawSurfPtr & 0xFFFFFF00) | (byte)*circlePtr;
+			++drawSurfPtr;
+			++circlePtr;
+		}
+	}
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/puzzle/mouselightpuzzle.h b/engines/nancy/action/puzzle/mouselightpuzzle.h
index 99f1475bdda..198efe12d48 100644
--- a/engines/nancy/action/puzzle/mouselightpuzzle.h
+++ b/engines/nancy/action/puzzle/mouselightpuzzle.h
@@ -30,6 +30,9 @@ namespace Action {
 // Shows a single image over the entire frame, with most of it blackened;
 // a circle around that follows the cursor reveals parts of the image.
 // Circle can have smooth or hard edges. Not actually a puzzle.
+
+// TODO: Optimize blitting; currently, the whole screen is redrawn
+// TODO: Add noise to the circle mask; there are artifacts at low brightness
 class MouseLightPuzzle : public RenderActionRecord {
 public:
 	MouseLightPuzzle() : RenderActionRecord(7) {}
@@ -49,9 +52,7 @@ protected:
 	byte _radius = 0;
 	bool _smoothEdges = false;
 
-	Graphics::ManagedSurface _baseImage;
 	Graphics::ManagedSurface _maskCircle;
-	Graphics::ManagedSurface _mask;
 
 	Common::Point _lastMousePos;
 };
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index a9a4f4753c5..ab12ee24e30 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -37,6 +37,7 @@ GraphicsManager::GraphicsManager() :
 	_inputPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0),
 	_screenPixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),
 	_clut8Format(Graphics::PixelFormat::createFormatCLUT8()),
+	_transparentPixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0),
 	_isSuppressed(false) {}
 
 void GraphicsManager::init() {
@@ -136,7 +137,7 @@ void GraphicsManager::draw(bool updateScreen) {
 						// The entire area that would be drawn is obscured by another RenderObject.
 						// If the obscuring RenderObject is not transparent, we skip drawing current
 
-						if (!other._drawSurface.hasTransparentColor()) {
+						if (!other._drawSurface.hasTransparentColor() && other._drawSurface.format != _transparentPixelFormat) {
 							// No transparency, skip current
 							shouldSkip = true;
 							break;
@@ -404,6 +405,10 @@ const Graphics::PixelFormat &GraphicsManager::getScreenPixelFormat() {
 	return _screenPixelFormat;
 }
 
+const Graphics::PixelFormat &GraphicsManager::getTransparentPixelFormat() {
+	return _transparentPixelFormat;
+}
+
 void GraphicsManager::grabViewportObjects(Common::Array<RenderObject *> &inArray) {
 	// Add the viewport
 	inArray.push_back(&(RenderObject &)NancySceneState.getViewport());
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 0a263c1333b..0bb89559b34 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -54,6 +54,7 @@ public:
 
 	const Graphics::PixelFormat &getInputPixelFormat();
 	const Graphics::PixelFormat &getScreenPixelFormat();
+	const Graphics::PixelFormat &getTransparentPixelFormat();
 	uint32 getTransColor() { return _transColor; }
 
 	Graphics::ManagedSurface &getAutotextSurface(uint16 id) { return _autotextSurfaces.getOrCreateVal(id); }
@@ -74,7 +75,6 @@ public:
 
 	Graphics::ManagedSurface _object0;
 
-	Graphics::PixelFormat _screenPixelFormat;
 
 private:
 	void blitToScreen(const RenderObject &src, Common::Rect dest);
@@ -84,7 +84,9 @@ private:
 	Common::SortedArray<RenderObject *> _objects;
 
 	Graphics::PixelFormat _inputPixelFormat;
+	Graphics::PixelFormat _screenPixelFormat;
 	Graphics::PixelFormat _clut8Format;
+	Graphics::PixelFormat _transparentPixelFormat;
 
 	Graphics::Screen _screen;
 	Common::Array<Font> _fonts;


Commit: 716041603dbc567c6f29082713ddd7f272360077
    https://github.com/scummvm/scummvm/commit/716041603dbc567c6f29082713ddd7f272360077
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:27+01:00

Commit Message:
NANCY: Carry over unshown text to next scene

A couple of scenes in nancy7 draw to the textbox, then
immediately change the scene, which clears said text.
This commit adds a check for whether text was actually
drawn, and if it hasn't, makes sure it is shown in the next
scene. This is behavior not present in the original engine.

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


diff --git a/engines/nancy/misc/hypertext.h b/engines/nancy/misc/hypertext.h
index 795c2f76773..67459881ed8 100644
--- a/engines/nancy/misc/hypertext.h
+++ b/engines/nancy/misc/hypertext.h
@@ -41,6 +41,8 @@ public:
 		_imageVerticalOffset(0) {}
 	virtual ~HypertextParser() {};
 
+	bool hasBeenDrawn() const { return !_needsTextRedraw; }
+
 protected:
 	void initSurfaces(uint width, uint height, const struct Graphics::PixelFormat &format, uint32 backgroundColor, uint32 highlightBackgroundColor);
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 6536a2e49a3..7c99651557d 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1221,7 +1221,13 @@ void Scene::clearSceneData() {
 		_lightning->endLightning();
 	}
 
-	_textbox.clear();
+	if (_textbox.hasBeenDrawn()) {
+		// Improvement: the dog portrait scenes in nancy7 queue a piece of text,
+		// then immediately change the scene. This makes the text disappear instantly;
+		// instead, we check if the textbox has been drawn, and don't clear it if it hasn't.
+		// Hopefully this doesn't cause issues with earlier games.
+		_textbox.clear();
+	}
 }
 
 void Scene::clearPuzzleData() {


Commit: 66a7fdb58e58dc127b85012c8a8295fe54eeb841
    https://github.com/scummvm/scummvm/commit/66a7fdb58e58dc127b85012c8a8295fe54eeb841
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:27+01:00

Commit Message:
NANCY: Implement SpigotPuzzle

Implemented the AR responsible for nancy7's spigot puzzle.

Changed paths:
  A engines/nancy/action/puzzle/spigotpuzzle.cpp
  A engines/nancy/action/puzzle/spigotpuzzle.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index a924d42358a..4c47f0327b4 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -52,6 +52,7 @@
 #include "engines/nancy/action/puzzle/setplayerclock.h"
 #include "engines/nancy/action/puzzle/sliderpuzzle.h"
 #include "engines/nancy/action/puzzle/soundequalizerpuzzle.h"
+#include "engines/nancy/action/puzzle/spigotpuzzle.h"
 #include "engines/nancy/action/puzzle/tangrampuzzle.h"
 #include "engines/nancy/action/puzzle/telephone.h"
 #include "engines/nancy/action/puzzle/towerpuzzle.h"
@@ -329,6 +330,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new CubePuzzle();
 	case 224:
 		return new OrderingPuzzle(OrderingPuzzle::kKeypadTerse);
+	case 225:
+		return new SpigotPuzzle();
 	default:
 		return nullptr;
 	}
diff --git a/engines/nancy/action/puzzle/spigotpuzzle.cpp b/engines/nancy/action/puzzle/spigotpuzzle.cpp
new file mode 100644
index 00000000000..b3351c48fa3
--- /dev/null
+++ b/engines/nancy/action/puzzle/spigotpuzzle.cpp
@@ -0,0 +1,301 @@
+/* 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/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/puzzledata.h"
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/action/puzzle/spigotpuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+void SpigotPuzzle::init() {
+	Common::Rect screenBounds = NancySceneState.getViewport().getBounds();
+	_drawSurface.create(screenBounds.width(), screenBounds.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
+	setTransparent(true);
+	setVisible(true);
+	moveTo(screenBounds);
+
+	g_nancy->_resource->loadImage(_imageName, _image);
+	registerGraphics();
+}
+
+void SpigotPuzzle::updateGraphics() {
+	if (_pushedButtonID != -1) {
+		if (g_nancy->getTotalPlayTime() >= _nextAnimTime) {
+			_animatingLetterID = _pushedButtonID;
+			_drawSurface.fillRect(_buttonDests[_pushedButtonID], _drawSurface.getTransparentColor());
+			_pushedButtonID = -1;
+			_nextAnimTime = 0;
+
+			uint numSpins = _numSpins[_numPulls[_animatingLetterID] - 1];
+			_frameID = numSpins * _numInbetweens;
+		}
+	}
+
+	if (_animatingSpigotID != -1) {
+		uint32 curTime = g_nancy->getTotalPlayTime();
+		if (curTime >= _nextAnimTime) {
+			if (_nextAnimTime == 0) {
+				_nextAnimTime = curTime + 100;
+			} else {
+				_nextAnimTime += 100;
+			}
+		} else {
+			return;
+		}
+
+		if (_frameID != _spigotAnimSrcs[_animatingSpigotID].size()) {
+			_drawSurface.blitFrom(_image, _spigotAnimSrcs[_animatingSpigotID][_frameID], _spigotDests[_animatingSpigotID]);
+			_needsRedraw = true;
+
+			++_frameID;
+		} else {
+			// Increment the number and end the animation
+			_numPulls[_animatingSpigotID] = MIN<uint16>(_numPulls[_animatingSpigotID] + 1, 6);
+			_drawSurface.blitFrom(_image, _digitSrcs[_animatingSpigotID][_numPulls[_animatingSpigotID]], _digitDests[_animatingSpigotID]);
+
+			// Also, clear the last drawn spigot frame
+			_drawSurface.fillRect(_spigotDests[_animatingSpigotID], _drawSurface.getTransparentColor());
+
+			_needsRedraw = true;
+			_animatingSpigotID = -1;
+			_frameID = 0;
+			_nextAnimTime = 0;
+		}
+	} else if (_animatingLetterID != -1) {
+		uint32 curTime = g_nancy->getTotalPlayTime();
+		if (curTime >= _nextAnimTime) {
+			if (_nextAnimTime == 0) {
+				_nextAnimTime = curTime + _letterTime * 200;
+			} else {
+				_nextAnimTime += _letterTime * 200;
+			}
+		} else {
+			return;
+		}
+
+		if (_frameID != 0) {
+			if (++_currentAnimOrder[_animatingLetterID] >= _numLetters * _numInbetweens) {
+				_currentAnimOrder[_animatingLetterID] = 0;
+			}
+
+			g_nancy->_sound->playSound(_letterSound);
+			_drawSurface.blitFrom(_image, _letterSrcs[_animatingLetterID][_currentAnimOrder[_animatingLetterID]], _letterDests[_animatingLetterID]);
+			_needsRedraw = true;
+			
+			--_frameID;
+		} else {
+			// Clear the number
+			_numPulls[_animatingLetterID] = 0;
+			_drawSurface.fillRect(_digitDests[_animatingLetterID], _drawSurface.getTransparentColor());
+			_needsRedraw = true;
+
+			// Set the order at the end of the animation to avoid "solving" while animating
+			_currentOrder[_animatingLetterID] = _currentAnimOrder[_animatingLetterID] / _numInbetweens;
+			_animatingLetterID = -1;
+			_nextAnimTime = 0;
+		}
+	}
+}
+
+void SpigotPuzzle::readData(Common::SeekableReadStream &stream) {
+	readFilename(stream, _imageName);
+
+	_numSpigots = stream.readUint16LE();
+	_numLetters = stream.readUint16LE();
+	_numInbetweens = stream.readUint16LE();
+
+	_startOrder.resize(_numSpigots);
+	for (uint i = 0; i < _numSpigots; ++i) {
+		_startOrder[i] = stream.readUint16LE();
+	}
+	stream.skip((6 - _numSpigots) * 2);
+
+	_numSpins.resize(6);
+	for (uint i = 0; i < 6; ++i) {
+		_numSpins[i] = stream.readUint16LE();
+	}
+
+	readRectArray(stream, _spigotDests, _numSpigots, 6);
+	readRectArray(stream, _spigotHotspots, _numSpigots, 6);
+	readRectArray(stream, _letterDests, _numSpigots, 6);
+	readRectArray(stream, _digitDests, _numSpigots, 6);
+	readRectArray(stream, _buttonDests, _numSpigots, 6);
+
+	uint16 numSpigotAnimationFrames = stream.readUint16LE();
+	_spigotAnimSrcs.resize(_numSpigots);
+	for (uint i = 0; i < _numSpigots; ++i) {
+		_spigotAnimSrcs[i].resize(numSpigotAnimationFrames);
+
+		uint32 x = stream.readUint32LE();
+		uint32 y = stream.readUint32LE();
+		uint16 deltaX = stream.readUint16LE();
+		uint16 height = stream.readUint16LE() + 1;
+
+		for (uint j = 0; j < numSpigotAnimationFrames; ++j) {
+			_spigotAnimSrcs[i][j] = Common::Rect(x + j * deltaX, y, x + j * deltaX + deltaX, y + height);
+		}
+	}
+	stream.skip((6 - _numSpigots) * 12);
+
+	_digitSrcs.resize(_numSpigots);
+	for (uint i = 0; i < _numSpigots; ++i) {
+		readRectArray(stream, _digitSrcs[i], 7);
+	}
+	stream.skip((6 - _numSpigots) * 7 * 16);
+
+	readRectArray(stream, _buttonSrcs, _numSpigots, 6);
+
+	_letterSrcs.resize(_numSpigots);
+	for (uint i = 0; i < _numSpigots; ++i) {
+		readRectArray(stream, _letterSrcs[i], _numLetters * _numInbetweens, 7 * 2);
+	}
+	stream.skip((6 - _numSpigots) * 7 * 2 * 16);
+
+	_letterTime = stream.readUint16LE();
+
+	_correctOrder.resize(_numSpigots);
+	for (uint i = 0; i < _numSpigots; ++i) {
+		_correctOrder[i] = stream.readUint16LE();
+	}
+	stream.skip((6 - _numSpigots) * 2);
+
+	_buttonSound.readNormal(stream);
+	_letterSound.readNormal(stream);
+	_spigotSound.readNormal(stream);
+
+	_solveScene.readData(stream);
+	_solveSoundDelay = stream.readUint16LE();
+	_solveSound.readNormal(stream);
+
+	_exitScene.readData(stream);
+	readRect(stream, _exitHotspot);
+}
+
+void SpigotPuzzle::execute() {
+	switch(_state) {
+	case kBegin:
+		init();
+		g_nancy->_sound->loadSound(_buttonSound);
+		g_nancy->_sound->loadSound(_letterSound);
+		g_nancy->_sound->loadSound(_spigotSound);
+		
+		_currentOrder = _startOrder;
+		_currentAnimOrder.resize(_currentOrder.size());
+		for (uint i = 0; i < _currentAnimOrder.size(); ++i) {
+			_currentAnimOrder[i] = _currentOrder[i] * _numInbetweens;
+		}
+		_numPulls.resize(_numSpigots, 0);
+
+		// Draw the start letters, in case the background ones are different
+		for (uint i = 0; i < _numSpigots; ++i) {
+			_drawSurface.blitFrom(_image, _letterSrcs[i][_currentAnimOrder[i]], _letterDests[i]);
+		}
+		_needsRedraw = true;
+
+		NancySceneState.setNoHeldItem();
+
+		_state = kRun;
+		// fall through
+	case kRun:
+		if (_currentOrder == _correctOrder) {
+			g_nancy->_sound->loadSound(_solveSound);
+			g_nancy->_sound->playSound(_solveSound);
+			_solved = true;
+			_state = kActionTrigger;
+		}
+
+		break;
+	case kActionTrigger:
+		if (_solved) {
+			// Sound delay not used
+			if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				return;
+			}
+
+			_solveScene.execute();
+		} else {
+			_exitScene.execute();
+		}
+
+		break;
+	}
+}
+
+void SpigotPuzzle::handleInput(NancyInput &input) {
+	if (_state != kRun || _animatingSpigotID != -1 || _animatingLetterID != -1) {
+		return;
+	}
+
+	Common::Rect vpScreenPos = NancySceneState.getViewport().convertViewportToScreen(_screenPosition);
+	if (!vpScreenPos.contains(input.mousePos)) {
+		return;
+	}
+
+	Common::Point mousePos = input.mousePos;
+	mousePos -= { vpScreenPos.left, vpScreenPos.top };
+
+	if (_exitHotspot.contains(mousePos)) {
+		g_nancy->_cursorManager->setCursorType(g_nancy->_cursorManager->_puzzleExitCursor);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_state = kActionTrigger;
+		}
+
+		return;
+	}
+
+	for (uint i = 0; i < _numSpigots; ++i) {
+		if (_spigotHotspots[i].contains(mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				g_nancy->_sound->playSound(_spigotSound);
+				_animatingSpigotID = i;
+				return;
+			}
+		}
+
+		if (_numPulls[i] && _buttonDests[i].contains(mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				g_nancy->_sound->playSound(_buttonSound);
+				_drawSurface.blitFrom(_image, _buttonSrcs[i], _buttonDests[i]);
+				_needsRedraw = true;
+				_pushedButtonID = i;
+				_nextAnimTime = g_nancy->getTotalPlayTime() + 250;
+				return;
+			}
+		}
+	}
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/puzzle/spigotpuzzle.h b/engines/nancy/action/puzzle/spigotpuzzle.h
new file mode 100644
index 00000000000..12923c4efbe
--- /dev/null
+++ b/engines/nancy/action/puzzle/spigotpuzzle.h
@@ -0,0 +1,99 @@
+/* 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_SPIGOTPUZZLE_H
+#define NANCY_ACTION_SPIGOTPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+
+namespace Nancy {
+namespace Action {
+
+// A puzzle in nancy7 where you pull spigots to input a password
+class SpigotPuzzle : public RenderActionRecord {
+public:
+	SpigotPuzzle() : RenderActionRecord(7) {}
+	virtual ~SpigotPuzzle() {}
+
+	void init() override;
+	void updateGraphics() override;
+
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+	void handleInput(NancyInput &input) override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "SpigotPuzzle"; }
+	bool isViewportRelative() const override { return true; }
+
+	Common::Path _imageName;
+
+	uint _numSpigots = 0;
+	uint _numLetters = 0;
+	uint _numInbetweens = 0;
+
+	Common::Array<uint16> _startOrder;
+	Common::Array<uint16> _numSpins;
+
+	Common::Array<Common::Rect> _spigotDests;
+	Common::Array<Common::Rect> _spigotHotspots;
+	Common::Array<Common::Rect> _letterDests;
+	Common::Array<Common::Rect> _digitDests;
+	Common::Array<Common::Rect> _buttonDests;
+
+	Common::Array<Common::Array<Common::Rect>> _spigotAnimSrcs;
+	Common::Array<Common::Array<Common::Rect>> _digitSrcs;
+	Common::Array<Common::Rect> _buttonSrcs;
+	Common::Array<Common::Array<Common::Rect>> _letterSrcs;
+
+	uint16 _letterTime = 0; // 1 unit = 200ms
+	Common::Array<uint16> _correctOrder;
+
+	SoundDescription _buttonSound;
+	SoundDescription _letterSound;
+	SoundDescription _spigotSound;
+
+	SceneChangeWithFlag _solveScene;
+	uint16 _solveSoundDelay = 0;
+	SoundDescription _solveSound;
+
+	SceneChangeWithFlag _exitScene;
+	Common::Rect _exitHotspot;
+
+	Graphics::ManagedSurface _image;
+
+	Common::Array<uint16> _currentOrder;
+	Common::Array<uint16> _currentAnimOrder;
+	Common::Array<uint16> _numPulls;
+
+	int _animatingSpigotID = -1;
+	int _animatingLetterID = -1;
+	int _pushedButtonID = -1;
+	uint _frameID = 0;
+	uint32 _nextAnimTime = 0;
+
+	bool _solved = false;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_SPIGOTPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 74cecf79072..57ea5fb711e 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -35,6 +35,7 @@ MODULE_OBJS = \
   action/puzzle/setplayerclock.o \
   action/puzzle/sliderpuzzle.o \
   action/puzzle/soundequalizerpuzzle.o \
+  action/puzzle/spigotpuzzle.o \
   action/puzzle/tangrampuzzle.o \
   action/puzzle/telephone.o \
   action/puzzle/towerpuzzle.o \


Commit: d468406f250f2cffdfa2ade68d206d7747b8517a
    https://github.com/scummvm/scummvm/commit/d468406f250f2cffdfa2ade68d206d7747b8517a
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:28+01:00

Commit Message:
Revert "NANCY: Fix fades in nancy7 dog scare sequence"

This reverts commit 1f5a3c64cc89fca3e0d13afd24056756e65a2ff2.

The changes in this commit break other fades in nancy7.

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


diff --git a/engines/nancy/misc/specialeffect.cpp b/engines/nancy/misc/specialeffect.cpp
index d580e3a5c4f..d8a4cc90ddf 100644
--- a/engines/nancy/misc/specialeffect.cpp
+++ b/engines/nancy/misc/specialeffect.cpp
@@ -123,7 +123,6 @@ void SpecialEffect::updateGraphics() {
 void SpecialEffect::onSceneChange() {
 	g_nancy->_graphicsManager->screenshotScreen(_fadeFrom);
 	_drawSurface.rawBlitFrom(_fadeFrom, _rect, Common::Point());
-	_halfInitialized = true;
 }
 
 void SpecialEffect::afterSceneChange() {
diff --git a/engines/nancy/misc/specialeffect.h b/engines/nancy/misc/specialeffect.h
index c721eccbb6b..75ba696ee60 100644
--- a/engines/nancy/misc/specialeffect.h
+++ b/engines/nancy/misc/specialeffect.h
@@ -58,11 +58,9 @@ public:
 	void afterSceneChange();
 
 	bool isDone() const;
-	bool isHalfInitialized() const { return _halfInitialized; }
 	bool isInitialized() const { return _initialized; }
 
 protected:
-	bool _halfInitialized = false;
 	bool _initialized = false;
 
 	uint32 _nextFrameTime = 0;
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 7c99651557d..05da02b0f2b 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -227,10 +227,6 @@ bool Scene::onStateExit(const NancyState::NancyState nextState) {
 	return _destroyOnExit;
 }
 
-bool Scene::isRunningSpecialEffect() const {
-	return _specialEffects.size() && _specialEffects.front().isHalfInitialized() && !_specialEffects.front().isDone();
-}
-
 void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
 	if (sceneDescription.sceneID == kNoScene || _state == kLoad) {
 		return;
@@ -1007,20 +1003,13 @@ void Scene::run() {
 		return;
 	}
 
-	// Run action records. From nancy7 onward want to skip this if we're in the
-	// middle of a special effect. This way, the nancy7 dog scare sequence 
-	// (scene 2090 and onwards) gets the fades to black present in the original.
-	// We don't do the same for earlier games, since we _want_ records to execute
-	// there; an example is the text in the nancy3 intro.
-	// Note: if this causes any issues, move the check inside SecondaryMovie.
-	if (!isRunningSpecialEffect() || g_nancy->getGameType() <= kGameTypeNancy6) {
-		_actionManager.processActionRecords();
-	}
+	_actionManager.processActionRecords();
 
 	if (_lightning) {
 		_lightning->run();
 	}
 
+	// Do this after the first records are processed to fix the text in nancy3 intro
 	if (_specialEffects.size()) {
 		if (_specialEffects.front().isInitialized()) {
 			if (_specialEffects.front().isDone()) {
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 89f40a6a06a..ee591cc32c0 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -115,7 +115,6 @@ public:
 	void setDestroyOnExit() { _destroyOnExit = true; }
 
 	bool isRunningAd() const { return _isRunningAd; }
-	bool isRunningSpecialEffect() const;
 
 	void changeScene(const SceneChangeDescription &sceneDescription);
 	void pushScene(int16 itemID = -1);


Commit: d6016dfbc673f49f58f76c332fd1610933d8f031
    https://github.com/scummvm/scummvm/commit/d6016dfbc673f49f58f76c332fd1610933d8f031
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:28+01:00

Commit Message:
NANCY: Proper fix for nancy7 movie fades

This commit makes changes that ensure the nancy7
dog scare sequence will have the correct fades to black
without impacting other scene transitions. This is achieved
by delaying full-screen movies by exactly one frame.

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


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 9700d710d51..4dfa65d6f37 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -149,6 +149,12 @@ void PlaySecondaryMovie::execute() {
 		}
 
 		_state = kRun;
+
+		if (Common::Rect(_decoder->getWidth(), _decoder->getHeight()) == NancySceneState.getViewport().getBounds()) {
+			g_nancy->_graphicsManager->suppressNextDraw();
+			break;
+		}
+
 		// fall through
 	case kRun: {
 		int newFrame = NancySceneState.getSceneInfo().frameID;


Commit: dd749d6042e1893584bc7fee36ad026037f71c8e
    https://github.com/scummvm/scummvm/commit/dd749d6042e1893584bc7fee36ad026037f71c8e
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:28+01:00

Commit Message:
NANCY: Add Bink support to play_video command

Changed paths:
    engines/nancy/console.cpp


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index b76383e2f8b..d7172e4ca87 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -24,8 +24,8 @@
 #include "common/config-manager.h"
 
 #include "audio/audiostream.h"
-
 #include "image/bmp.h"
+#include "video/bink_decoder.h"
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/console.h"
@@ -76,9 +76,22 @@ NancyConsole::~NancyConsole() {}
 void NancyConsole::postEnter() {
 	GUI::Debugger::postEnter();
 	if (!_videoFile.empty()) {
-		Video::VideoDecoder *dec = new AVFDecoder;
+		Common::Path withExt = _videoFile;
+		Video::VideoDecoder *dec = new AVFDecoder();
+
+		if (!dec->loadFile(withExt.append(".avf"))) {
+			// No AVF found, try Bink
+			delete dec;
+			dec = new Video::BinkDecoder();
+
+			if (!dec->loadFile(withExt.append(".bik"))) {
+				debugPrintf("Failed to load video '%s'\n", _videoFile.toString(Common::Path::kNativeSeparator).c_str());
+				delete dec;
+				dec = nullptr;
+			}
+		}
 
-		if (dec->loadFile(_videoFile)) {
+		if (dec) {
 			Graphics::ManagedSurface surf;
 
 			if (!_paletteFile.empty()) {
@@ -107,8 +120,6 @@ void NancyConsole::postEnter() {
 			}
 
 			g_nancy->_graphicsManager->redrawAll();
-		} else {
-			debugPrintf("Failed to load '%s'\n", _videoFile.toString(Common::Path::kNativeSeparator).c_str());
 		}
 
 		_videoFile.clear();
@@ -381,7 +392,6 @@ bool NancyConsole::Cmd_playVideo(int argc, const char **argv) {
 		}
 
 		_videoFile = argv[1];
-		_videoFile.appendInPlace(".avf");
 		_paletteFile = argv[2];
 		return cmdExit(0, nullptr);
 	} else {
@@ -392,7 +402,6 @@ bool NancyConsole::Cmd_playVideo(int argc, const char **argv) {
 		}
 
 		_videoFile = argv[1];
-		_videoFile.appendInPlace(".avf");
 		return cmdExit(0, nullptr);
 	}
 }


Commit: a2f6f38105bce9721c00321f871f3a9268f5eb55
    https://github.com/scummvm/scummvm/commit/a2f6f38105bce9721c00321f871f3a9268f5eb55
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:28+01:00

Commit Message:
NANCY: Fix Overlay crash in nancy7

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


diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index 72cba2b52f8..33b16429053 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -303,6 +303,10 @@ void Overlay::execute() {
 							}
 						}
 
+						// Make sure the srcRect doesn't extend beyond the image.
+						// This fixes nancy7 scene 4228
+						srcRect.clip(_fullSurface.getBounds());
+
 						if (blitsForThisFrame.size() == 1) {
 							_drawSurface.create(_fullSurface, srcRect);
 							setTransparent(_transparency == kPlayOverlayTransparent);


Commit: 5b2929ce4d9e821446898d1200a37415321cd6c2
    https://github.com/scummvm/scummvm/commit/5b2929ce4d9e821446898d1200a37415321cd6c2
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2024-02-06T20:32:28+01:00

Commit Message:
NANCY: Fix kThroughBlack special effects

The transition type that fades to black, and then to the
next scene now no longer gets stuck on a black screen
forever.

Changed paths:
    engines/nancy/misc/specialeffect.cpp


diff --git a/engines/nancy/misc/specialeffect.cpp b/engines/nancy/misc/specialeffect.cpp
index d8a4cc90ddf..cc7a663a468 100644
--- a/engines/nancy/misc/specialeffect.cpp
+++ b/engines/nancy/misc/specialeffect.cpp
@@ -90,7 +90,7 @@ void SpecialEffect::updateGraphics() {
 			_fadeToBlackTime /= 2;
 		}
 
-		if (g_nancy->getTotalPlayTime() > _startTime + _totalTime) {
+		if (g_nancy->getTotalPlayTime() > _startTime + _totalTime && (_type != kThroughBlack || _throughBlackStarted2nd)) {
 			if (_currentFrame == 0) {
 				// Ensure at least one dissolve frame is shown
 				++_currentFrame;
@@ -98,21 +98,22 @@ void SpecialEffect::updateGraphics() {
 				setVisible(true);
 			}
 		} else {
-			// Use a Bezier curve for all fades. Not entirely accurate to the original engine,
+			// Use a curve for all fades. Not entirely accurate to the original engine,
 			// since that pre-calculated the number of frames and did some exponent magic on them
 			float alpha = (float)(g_nancy->getTotalPlayTime() - _startTime) / (float)_totalTime;
+			bool start2nd = alpha > 1;
 			alpha = alpha * alpha * (3.0 - 2.0 * alpha);
 			alpha *= 255;
 			GraphicsManager::crossDissolve(_fadeFrom, _fadeTo, alpha, _rect, _drawSurface);
 			setVisible(true);
 			++_currentFrame;
 
-			if (alpha > 255 && _type == kThroughBlack) {
+			if (start2nd && _type == kThroughBlack) {
 				_throughBlackStarted2nd = true;
-				void *temp = _fadeFrom.getPixels();
-				_fadeFrom.setPixels(_fadeTo.getPixels());
-				_fadeTo.setPixels(temp);
+				_fadeFrom.clear();
+				setVisible(false);
 				g_nancy->_graphicsManager->screenshotScreen(_fadeTo);
+				setVisible(true);
 				_startTime = g_nancy->getTotalPlayTime();
 				_currentFrame = 0;
 			}




More information about the Scummvm-git-logs mailing list