[Scummvm-git-logs] scummvm master -> d520b0c8dbc50b4f69d42117502db77488c08fc5
fracturehill
noreply at scummvm.org
Sat Sep 2 14:26:34 UTC 2023
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
a62f7cd7d7 NANCY: Implement TangramPuzzle
e11ec7c8cd DEVTOOLS: Fix broken strings in nancy3 data
13f95f1781 NANCY: Implement SafeLockPuzzle
d520b0c8db NANCY: Fix arrow cursor hotspot
Commit: a62f7cd7d7e840eca041831750c00f0d7e2cb6f5
https://github.com/scummvm/scummvm/commit/a62f7cd7d7e840eca041831750c00f0d7e2cb6f5
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-02T17:24:39+03:00
Commit Message:
NANCY: Implement TangramPuzzle
Implemented the puzzle type responsible for nancy3's tile
fitting puzzle.
Changed paths:
A engines/nancy/action/tangrampuzzle.cpp
A engines/nancy/action/tangrampuzzle.h
engines/nancy/action/arfactory.cpp
engines/nancy/graphics.cpp
engines/nancy/module.mk
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index e1625dd3183..2ff96a7a24c 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -39,6 +39,7 @@
#include "engines/nancy/action/setplayerclock.h"
#include "engines/nancy/action/raycastpuzzle.h"
#include "engines/nancy/action/turningpuzzle.h"
+#include "engines/nancy/action/tangrampuzzle.h"
#include "engines/nancy/state/scene.h"
@@ -200,6 +201,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
return new RiddlePuzzle();
case 206:
return new RaycastPuzzle();
+ case 207:
+ return new TangramPuzzle();
case 208:
return new OrderingPuzzle(OrderingPuzzle::PuzzleType::kPiano);
case 209:
diff --git a/engines/nancy/action/tangrampuzzle.cpp b/engines/nancy/action/tangrampuzzle.cpp
new file mode 100644
index 00000000000..b1395ba7791
--- /dev/null
+++ b/engines/nancy/action/tangrampuzzle.cpp
@@ -0,0 +1,481 @@
+/* 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/tangrampuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+TangramPuzzle::~TangramPuzzle() {
+ delete[] _zBuffer;
+}
+
+void TangramPuzzle::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(_tileImageName, _tileImage);
+ g_nancy->_resource->loadImage(_maskImageName, _maskImage);
+
+ _zBuffer = new byte[_drawSurface.w * _drawSurface.h];
+ memset(_zBuffer, -1, _drawSurface.w * _drawSurface.h);
+
+ _tiles.resize(_tileSrcs.size() + 1);
+
+ // First, add the mask as its own tile for easier handling
+ Tile *curTile = &_tiles[0];
+ curTile->_srcImage.create(_maskImage, _maskImage.getBounds());
+ curTile->_drawSurface.copyFrom(_tiles[0]._srcImage);
+ curTile->_id = 0;
+ curTile->drawMask();
+ curTile->moveTo(_maskImage.getBounds());
+ curTile->setTransparent(true);
+ curTile->setVisible(true);
+ drawToBuffer(*curTile);
+ curTile->setZ(_z + 1);
+
+ // Then, add the actual tiles
+ for (uint i = 0; i < _tileSrcs.size(); ++i) {
+ curTile = &_tiles[i + 1];
+ curTile->_srcImage.create(_tileImage, _tileSrcs[i]);
+ curTile->_drawSurface.copyFrom(curTile->_srcImage);
+ curTile->_id = i + 1;
+ curTile->moveTo(_tileDests[i]);
+ curTile->setTransparent(true);
+ curTile->setVisible(true);
+ curTile->setZ(_z + curTile->_id + 1);
+ curTile->drawMask();
+ drawToBuffer(*curTile);
+
+ // Draw the highlighted tile
+ curTile->_highlightedSrcImage.copyFrom(curTile->_srcImage);
+
+ Graphics::PixelFormat format = curTile->_highlightedSrcImage.format;
+ for (int y = 0; y < curTile->_highlightedSrcImage.h; ++y) {
+ uint16 *p = (uint16 *)curTile->_highlightedSrcImage.getBasePtr(0, y);
+ for (int x = 0; x < curTile->_highlightedSrcImage.w; ++x) {
+ if (*p != g_nancy->_graphicsManager->getTransColor()) {
+ // I'm not sure *3/2 is the exact formula but it's close enough
+ byte r, g, b;
+ format.colorToRGB(*p, r, g, b);
+ r = (byte)((((uint16)r) * 3) >> 1);
+ g = (byte)((((uint16)g) * 3) >> 1);
+ b = (byte)((((uint16)b) * 3) >> 1);
+ *p = (uint16)format.RGBToColor((byte)r, (byte)g, (byte)b);
+ }
+
+ ++p;
+ }
+ }
+ }
+
+ registerGraphics();
+}
+
+void TangramPuzzle::registerGraphics() {
+ for (Tile &tile : _tiles) {
+ tile.registerGraphics();
+ }
+
+ RenderActionRecord::registerGraphics();
+}
+
+void TangramPuzzle::readData(Common::SeekableReadStream &stream) {
+ readFilename(stream, _tileImageName);
+ readFilename(stream, _maskImageName);
+
+ stream.skip(2); // Supposedly number of tiles, actually useless
+
+ for (uint i = 0; i < 15; ++i) {
+ Common::Rect src, dest;
+ readRect(stream, src);
+ readRect(stream, dest);
+
+ if ((src.width() == 1 && src.height() == 1) || (dest.width() == 1 && dest.height() == 1)) {
+ continue;
+ }
+
+ _tileSrcs.push_back(src);
+ _tileDests.push_back(dest);
+ }
+
+ readRect(stream, _maskSolveBounds);
+
+ _pickUpSound.readNormal(stream);
+ _putDownSound.readNormal(stream);
+ _rotateSound.readNormal(stream);
+
+ _solveScene.readData(stream);
+ _solveSound.readNormal(stream);
+
+ _exitScene.readData(stream);
+ readRect(stream, _exitHotspot);
+}
+
+void TangramPuzzle::execute() {
+ switch (_state) {
+ case kBegin :
+ init();
+ g_nancy->_sound->loadSound(_pickUpSound);
+ g_nancy->_sound->loadSound(_putDownSound);
+ g_nancy->_sound->loadSound(_rotateSound);
+ _state = kRun;
+ // fall through
+ case kRun :
+ if (_pickedUpTile == -1 && _shouldCheck) {
+ for (int y = 0; y < _maskSolveBounds.height(); ++y) {
+ byte *p = &_zBuffer[(y + _maskSolveBounds.top) * _drawSurface.w + _maskSolveBounds.left];
+ for (int x = 0; x < _maskSolveBounds.width(); ++x) {
+ if (*p == 0) {
+ _shouldCheck = false;
+ return;
+ }
+ ++p;
+ }
+ }
+
+ g_nancy->_sound->loadSound(_solveSound);
+ g_nancy->_sound->playSound(_solveSound);
+ _solved = true;
+ _state = kActionTrigger;
+ }
+
+ break;
+ case kActionTrigger :
+ if (_solved) {
+ if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+ break;
+ }
+
+ _solveScene.execute();
+ } else {
+ _exitScene.execute();
+ }
+
+ g_nancy->_sound->stopSound(_solveSound);
+ g_nancy->_sound->stopSound(_pickUpSound);
+ g_nancy->_sound->stopSound(_putDownSound);
+ g_nancy->_sound->stopSound(_rotateSound);
+
+ finishExecution();
+
+ break;
+ }
+}
+
+void TangramPuzzle::handleInput(NancyInput &input) {
+ if (_state != kRun) {
+ return;
+ }
+
+ Common::Rect viewport = NancySceneState.getViewport().getScreenPosition();
+
+ if (!viewport.contains(input.mousePos)) {
+ return;
+ }
+
+ Common::Point mousePos = input.mousePos;
+ mousePos.x -= viewport.left;
+ mousePos.y -= viewport.top;
+
+ viewport.moveTo(Common::Point(0, 0));
+
+ if (_pickedUpTile == -1) {
+ // Not holding a tile, check what's under the cursor
+ byte idUnderMouse = _zBuffer[mousePos.y * _drawSurface.w + mousePos.x];
+
+ if (idUnderMouse != 0 && idUnderMouse != (byte)-1) {
+ // A tile is under the cursor
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ pickUpTile(idUnderMouse);
+ g_nancy->_sound->playSound(_pickUpSound);
+ } else if (input.input & NancyInput::kRightMouseButtonUp) {
+ rotateTile(idUnderMouse);
+ g_nancy->_sound->playSound(_rotateSound);
+ }
+
+ return;
+ }
+
+ // No tile under cursor, check exit hotspot
+ if (_exitHotspot.contains(mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kExit);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _state = kActionTrigger;
+ }
+
+ return;
+ }
+ } else {
+ // Currently holding a tile
+ Tile &tileHolding = _tiles[_pickedUpTile];
+
+ // Check if we need to place it back down
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ putDownTile(_pickedUpTile);
+ g_nancy->_sound->playSound(_putDownSound);
+ return;
+ }
+
+ // Move the tile under the cursor
+ Common::Rect newScreenPos = tileHolding._screenPosition;
+ newScreenPos.moveTo(mousePos);
+ newScreenPos.translate(-newScreenPos.width() / 2, -newScreenPos.height() / 2);
+
+ // Clip movement so the ring stays entirely inside the viewport
+ if (newScreenPos.left < viewport.left) {
+ newScreenPos.translate(viewport.left - newScreenPos.left, 0);
+ } else if (newScreenPos.right > viewport.right) {
+ newScreenPos.translate(viewport.right - newScreenPos.right, 0);
+ }
+
+ if (newScreenPos.top < viewport.top) {
+ newScreenPos.translate(0, viewport.top - newScreenPos.top);
+ } else if (newScreenPos.bottom > viewport.bottom) {
+ newScreenPos.translate(0, viewport.bottom - newScreenPos.bottom);
+ }
+
+ if (newScreenPos != tileHolding._screenPosition) {
+ tileHolding.moveTo(newScreenPos);
+ }
+
+ bool rotated = false;
+
+ // Check if we need to rotate it
+ if (input.input & NancyInput::kRightMouseButtonUp) {
+ rotateTile(_pickedUpTile);
+ g_nancy->_sound->playSound(_rotateSound);
+ rotated = true;
+ }
+
+ if (!rotated) {
+ // Check if we need to highlight, but only if we haven't rotated,
+ // since rotateTile() already checks as well
+ if (checkBuffer(tileHolding) != tileHolding._isHighlighted) {
+ tileHolding.setHighlighted(!tileHolding._isHighlighted);
+ }
+ }
+ }
+}
+
+void TangramPuzzle::drawToBuffer(const Tile &tile, Common::Rect subRect) {
+ if (subRect.isEmpty()) {
+ subRect = tile._screenPosition;
+ }
+
+ uint16 xDiff = subRect.left - tile._screenPosition.left;
+ uint16 yDiff = subRect.top - tile._screenPosition.top;
+
+ for (int y = 0; y < subRect.height(); ++y) {
+ byte *src = &tile._mask[(y + yDiff) * tile._drawSurface.w + xDiff];
+ byte *dest = &_zBuffer[(subRect.top + y) * _drawSurface.w + subRect.left];
+ for (int x = 0; x < subRect.width(); ++x) {
+ if (*src != (byte)-1) {
+ *dest = *src;
+ }
+
+ ++src;
+ ++dest;
+ }
+ }
+}
+
+void TangramPuzzle::pickUpTile(uint id) {
+ assert(id < _tiles.size() && id != 0);
+
+ Tile &tileToPickUp = _tiles[id];
+
+ moveToTop(id);
+
+ _pickedUpTile = id;
+
+ redrawBuffer(tileToPickUp._screenPosition);
+
+ // Make sure we don't have a frame with the correct zOrder, but wrong position
+ // This is not done when we're calling from rotate()
+ NancyInput input = g_nancy->_input->getInput();
+ input.input = 0;
+ handleInput(input);
+}
+
+void TangramPuzzle::putDownTile(uint id) {
+ Tile &tile = _tiles[id];
+ _pickedUpTile = -1;
+
+ drawToBuffer(tile);
+
+ if (tile._isHighlighted) {
+ tile.setHighlighted(false);
+ }
+
+ _shouldCheck = true;
+}
+
+void TangramPuzzle::rotateTile(uint id) {
+ assert(id < _tiles.size() && id != 0);
+
+ Tile &tileToRotate = _tiles[id];
+
+ if (tileToRotate._rotation == 3) {
+ tileToRotate._rotation = 0;
+ } else {
+ ++tileToRotate._rotation;
+ }
+
+ moveToTop(id);
+
+ Common::Rect oldPos = tileToRotate._screenPosition;
+
+ if (_pickedUpTile != -1 && checkBuffer(tileToRotate)) {
+ tileToRotate.setHighlighted(true);
+ } else {
+ tileToRotate.setHighlighted(false);
+ }
+
+ Common::Rect newPos = tileToRotate._drawSurface.getBounds();
+ newPos.moveTo(oldPos.left + oldPos.width() / 2 - newPos.width() / 2, oldPos.top + oldPos.height() / 2 - newPos.height() / 2);
+
+ // Do NOT use moveTo()!
+ // If moved and rotated in the same frame, we need to make sure the last position isn't overwritten
+ tileToRotate._screenPosition = newPos;
+ _needsRedraw = true;
+
+
+ tileToRotate.drawMask();
+ tileToRotate._needsRedraw = true;
+
+ if (_pickedUpTile == -1) {
+ redrawBuffer(oldPos);
+ drawToBuffer(tileToRotate);
+ _shouldCheck = true;
+ }
+}
+
+void TangramPuzzle::moveToTop(uint id) {
+ for (uint i = 1; i < _tiles.size(); ++i) {
+ Tile &tile = _tiles[i];
+ if (tile._z > _tiles[id]._z) {
+ tile.setZ(tile._z - 1);
+ tile.registerGraphics();
+ }
+ }
+
+ _tiles[id].setZ(_z + _tiles.size());
+ _tiles[id].registerGraphics();
+}
+
+void TangramPuzzle::redrawBuffer(const Common::Rect &rect) {
+ // Redraw the zBuffer for all intersecting pixels, except for the topmost tile
+ for (int y = 0; y < rect.height(); ++y) {
+ byte *dest = &_zBuffer[(y + rect.top) * _drawSurface.w + rect.left];
+ memset(dest, -1, rect.width());
+ }
+
+ for (uint z = _z + 1; z < _z + _tiles.size(); ++z) {
+ for (uint i = 0; i < _tiles.size() - 1; ++i) {
+ Tile &tile = _tiles[i];
+ if (tile._z == z) {
+ if (tile._screenPosition.intersects(rect)) {
+ drawToBuffer(tile, tile._screenPosition.findIntersectingRect(rect));
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+bool TangramPuzzle::checkBuffer(const Tile &tile) const {
+ // Check if the provided tile has any pixel overlapping with a non-zero in the zBuffer
+ // In other words, this checks if we're placing on a valid empty spot
+ for (int y = 0; y < tile._drawSurface.h; ++y) {
+ const byte *tilePtr = &tile._mask[y * tile._drawSurface.w];
+ const byte *bufPtr = &_zBuffer[(y + tile._screenPosition.top) * _drawSurface.w + tile._screenPosition.left];
+ for (int x = 0; x < tile._drawSurface.w; ++x) {
+ if (*tilePtr != (byte)-1 && *bufPtr != 0) {
+ return false;
+ }
+
+ ++tilePtr;
+ ++bufPtr;
+ }
+ }
+
+ return true;
+}
+
+TangramPuzzle::Tile::Tile() : RenderObject(1), _mask(nullptr), _id(0), _rotation(0), _isHighlighted(false) {}
+
+TangramPuzzle::Tile::~Tile() {
+ delete _mask;
+}
+
+void TangramPuzzle::Tile::drawMask() {
+ if (!_mask) {
+ _mask = new byte[_drawSurface.w * _drawSurface.h];
+ }
+
+ uint16 transColor = g_nancy->_graphicsManager->getTransColor();
+ for (int y = 0; y < _drawSurface.h; ++y) {
+ uint16 *src = (uint16 *)_drawSurface.getBasePtr(0, y);
+ for (int x = 0; x < _drawSurface.w; ++x) {
+ if (*src == transColor) {
+ _mask[y * _drawSurface.w + x] = -1;
+ } else {
+ _mask[y * _drawSurface.w + x] = _id;
+ }
+ ++src;
+ }
+ }
+}
+
+void TangramPuzzle::Tile::setZ(uint z) {
+ _z = z;
+ _needsRedraw = true;
+}
+
+void TangramPuzzle::Tile::setHighlighted(bool highlighted) {
+ _isHighlighted = highlighted;
+ GraphicsManager::rotateBlit(_isHighlighted ? _highlightedSrcImage : _srcImage,
+ _drawSurface,
+ _rotation);
+ setTransparent(true);
+ _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/tangrampuzzle.h b/engines/nancy/action/tangrampuzzle.h
new file mode 100644
index 00000000000..218533601b2
--- /dev/null
+++ b/engines/nancy/action/tangrampuzzle.h
@@ -0,0 +1,115 @@
+/* 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_TANGRAMPUZZLE_H
+#define NANCY_ACTION_TANGRAMPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+
+namespace Nancy {
+namespace Action {
+
+// Handles a specific type of puzzle where clicking an object rotates it,
+// as well as several other objects linked to it. Examples are the sun/moon
+// and staircase spindle puzzles in nancy3
+class TangramPuzzle : public RenderActionRecord {
+public:
+ TangramPuzzle() : RenderActionRecord(7) {}
+ virtual ~TangramPuzzle();
+
+ void init() override;
+ void registerGraphics() override;
+
+ void readData(Common::SeekableReadStream &stream) override;
+ void execute() override;
+ void handleInput(NancyInput &input) override;
+
+protected:
+ Common::String getRecordTypeName() const override { return "TangramPuzzle"; }
+ bool isViewportRelative() const override { return true; }
+
+ class Tile : public RenderObject {
+ friend class TangramPuzzle;
+ public:
+ Tile();
+ virtual ~Tile();
+
+ void drawMask();
+ void setZ(uint z);
+ void setHighlighted(bool highlighted);
+
+ Graphics::ManagedSurface _srcImage;
+ Graphics::ManagedSurface _highlightedSrcImage;
+ byte *_mask;
+ byte _id;
+ byte _rotation;
+ bool _isHighlighted;
+
+ protected:
+ bool isViewportRelative() const override { return true; }
+ };
+
+ void drawToBuffer(const Tile &tile, Common::Rect subRect = Common::Rect());
+
+ void pickUpTile(uint id);
+ void putDownTile(uint id);
+ void rotateTile(uint id);
+
+ void moveToTop(uint id);
+ void redrawBuffer(const Common::Rect &rect);
+
+ bool checkBuffer(const Tile &tile) const;
+
+ Common::String _tileImageName;
+ Common::String _maskImageName;
+
+ Common::Array<Common::Rect> _tileSrcs;
+ Common::Array<Common::Rect> _tileDests;
+
+ Common::Rect _maskSolveBounds;
+
+ SoundDescription _pickUpSound;
+ SoundDescription _putDownSound;
+ SoundDescription _rotateSound;
+
+ SceneChangeWithFlag _solveScene;
+ SoundDescription _solveSound;
+
+ SceneChangeWithFlag _exitScene;
+ Common::Rect _exitHotspot;
+
+ Graphics::ManagedSurface _tileImage;
+ Graphics::ManagedSurface _maskImage;
+ byte *_zBuffer = nullptr;
+
+ Common::Array<Tile> _tiles;
+
+ int16 _pickedUpTile = -1;
+ bool _shouldCheck = false;
+ bool _solved = false;
+
+ uint _pixelAdjustment = 5;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_TANGRAMPUZZLE_H
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 583c6731e78..bd75e629e72 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -130,13 +130,11 @@ void GraphicsManager::loadFonts(Common::SeekableReadStream *chunkStream) {
}
void GraphicsManager::addObject(RenderObject *object) {
- for (const auto &r : _objects) {
+ for (auto &r : _objects) {
if (r == object) {
- return;
- }
-
- if (r->getZOrder() > object->getZOrder()) {
- break;
+ // Erase and re-add objects already in the array to make sure
+ // any changes in the z depth are reflected correctly
+ _objects.erase(&r);
}
}
@@ -269,17 +267,29 @@ void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, ui
}
// Custom rotation code since Surface::rotoscale() produces incorrect results
-// Only works on 16 bit square surfaces with the same size, and ignores transparency
+// Only works on 16 bit surfaces and ignores transparency
// Rotation is a value between 0 and 3, corresponding to 0, 90, 180, or 270 degrees clockwise
void GraphicsManager::rotateBlit(const Graphics::ManagedSurface &src, Graphics::ManagedSurface &dest, byte rotation) {
assert(!src.empty() && !dest.empty());
- assert(src.w == src.h && src.h == dest.w && dest.w == dest.h);
assert(rotation <= 3);
assert(src.format.bytesPerPixel == 2 && dest.format.bytesPerPixel == 2);
- uint size = src.w;
+ uint srcW = src.w;
+ uint srcH = src.h;
const uint16 *s, *e;
+ if (rotation % 2) {
+ if (src.h != dest.w || src.w != dest.h) {
+ // Dest surface is wrong size, destroy it and create an appropriate one
+ dest.create(src.h, src.w, src.format);
+ }
+ } else {
+ if (src.w != dest.w || src.h != dest.h) {
+ // Dest surface is wrong size, destroy it and create an appropriate one
+ dest.create(src.w, src.h, src.format);
+ }
+ }
+
switch (rotation) {
case 0 :
// No rotation, just blit
@@ -288,10 +298,10 @@ void GraphicsManager::rotateBlit(const Graphics::ManagedSurface &src, Graphics::
case 2 : {
// 180 degrees
uint16 *d;
- for (uint y = 0; y < size; ++y) {
+ for (uint y = 0; y < srcH; ++y) {
s = (const uint16 *)src.getBasePtr(0, y);
- e = (const uint16 *)src.getBasePtr(size - 1, y);
- d = (uint16 *)dest.getBasePtr(size - 1, size - y - 1);
+ e = (const uint16 *)src.getBasePtr(srcW, y);
+ d = (uint16 *)dest.getBasePtr(srcW - 1, srcH - y - 1);
for (; s < e; ++s, --d) {
*d = *s;
}
@@ -301,22 +311,20 @@ void GraphicsManager::rotateBlit(const Graphics::ManagedSurface &src, Graphics::
}
case 1 :
// 90 degrees
- for (uint y = 0; y < size; ++y) {
+ for (uint y = 0; y < srcH; ++y) {
s = (const uint16 *)src.getBasePtr(0, y);
- e = (const uint16 *)src.getBasePtr(size - 1, y);
- for (uint x = 0; x < size; ++x, ++s) {
- *((uint16 *)dest.getBasePtr(size - y - 1, x)) = *s;
+ for (uint x = 0; x < srcW; ++x, ++s) {
+ *((uint16 *)dest.getBasePtr(srcH - y - 1, x)) = *s;
}
}
break;
case 3 :
// 270 degrees
- for (uint y = 0; y < size; ++y) {
+ for (uint y = 0; y < srcH; ++y) {
s = (const uint16 *)src.getBasePtr(0, y);
- e = (const uint16 *)src.getBasePtr(size - 1, y);
- for (uint x = 0; x < size; ++x, ++s) {
- *((uint16 *)dest.getBasePtr(y, size - x - 1)) = *s;
+ for (uint x = 0; x < srcW; ++x, ++s) {
+ *((uint16 *)dest.getBasePtr(y, srcW - x - 1)) = *s;
}
}
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 9ce5418defd..adb4c4b572a 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS = \
action/setplayerclock.o \
action/sliderpuzzle.o \
action/soundequalizerpuzzle.o \
+ action/tangrampuzzle.o \
action/towerpuzzle.o \
action/turningpuzzle.o \
action/telephone.o \
Commit: e11ec7c8cd378235ab9d3e66b631e11ebdb9014f
https://github.com/scummvm/scummvm/commit/e11ec7c8cd378235ab9d3e66b631e11ebdb9014f
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-02T17:24:39+03:00
Commit Message:
DEVTOOLS: Fix broken strings in nancy3 data
Added a bunch of missing commas that were causing
strings to get merged.
Changed paths:
devtools/create_nancy/nancy3_data.h
diff --git a/devtools/create_nancy/nancy3_data.h b/devtools/create_nancy/nancy3_data.h
index 8c81cfc418d..8a6666fbbae 100644
--- a/devtools/create_nancy/nancy3_data.h
+++ b/devtools/create_nancy/nancy3_data.h
@@ -307,7 +307,7 @@ const Common::Array<Common::Array<const char *>> _nancy3ConditionalDialogueTexts
"Have you seen the poem in the Chinese room?<h><n>", // NCM73
"What is that small closet in the hallway for?<h><n>", // NCM74
"Can you tell me about the Chinese writing system? I seem to come across a lot of Chinese symbols.<h><n>", // NEF30
- "I found some old papers in the house, plus a page from a phone directory dated 1894.<h><n>" // NEF31
+ "I found some old papers in the house, plus a page from a phone directory dated 1894.<h><n>", // NEF31
// 40
"Have you ever heard of the Ladies Protection Society?<h><n>", // NEF32
"What do you know about 'The Bandit's Treasure'?<h><n>", // NEF33
@@ -317,41 +317,41 @@ const Common::Array<Common::Array<const char *>> _nancy3ConditionalDialogueTexts
// 45
"Do you know what the words, 'gum bo fu' mean?<h><n>", // NEF40
"Do you know where Yerba Buena town is?<h><n>", // NEF41
- "Do you know anything about 'Valdez'?<h><n>" // NEF44
+ "Do you know anything about 'Valdez'?<h><n>", // NEF44
"Abby is very strange. She really is convinced there's a ghost somewhere in the house.<h><n>", // NHG20
"I met Rose's handyman, Charlie. He seems nice.<h><n>", // NHG23
// 50
"Did you know Rose has a resident expert on Victorians?<h><n>", // NHG26
- "Have you heard about the seance Abby hosted for Rose and me?<h><n>" // NHG29
+ "Have you heard about the seance Abby hosted for Rose and me?<h><n>", // NHG29
"Abby faked the seance. She rigged a table with a projector.<h><n>", // NHG33
- "I just found a hidden attic. I wonder if it has anything to do with all these accidents.<h><n>" // NHG34
- "There was a fire in the parlor but I put it out in time.<h><n>" // NHG37
+ "I just found a hidden attic. I wonder if it has anything to do with all these accidents.<h><n>", // NHG34
+ "There was a fire in the parlor but I put it out in time.<h><n>", // NHG37
// 55
- "Hannah, do you think Rose could have started the fire to collect the insurance on the house?<h><n>" // NHG38
+ "Hannah, do you think Rose could have started the fire to collect the insurance on the house?<h><n>", // NHG38
"This house is full of surprises. I found a secret room in the basement where someone's been living.<h><n>", // NHG39
"Louis is up to something. I saw him take a book from the library.<h><n>", // NHG40
"Louis's book mentioned that this house was once called 'gum bo fu' in the 1800's.<h><n>", // NHG41
"It turns out Charlie is the one living in the basement.<h><n>", // NHG42
// 60
"I was wondering whether you knew anything about someone named 'E. Valdez'?<h><n>", // NLC50
- "Have you ever heard of the Great Christmas gold robbery?<h><n>" // NLC51
+ "Have you ever heard of the Great Christmas gold robbery?<h><n>", // NLC51
"What kind of antique store do you own?<h><n>", // NLC52
- "Was this house once a hotel?<h><n>" // NLC53
+ "Was this house once a hotel?<h><n>", // NLC53
"Have you seen a paint scraper anywhere?<h><n>", // NLC54
// 65
"Do you know what a phoenix is?<h><n>", // NLC55
"Do you know why the fireplace in the parlor didn't have a screen?<h><n>", // NLC57
"Have you found any secret passage ways in this house?<h><n>", // NLC58
- "Do you think Rose should sell the house?<h><n>" // NLC59
+ "Do you think Rose should sell the house?<h><n>", // NLC59
"Do you know what 'gum bo fu' means?<h><n>", // NLC60
// 70
"Do you know who Lizzie Applegate was?<h><n>", // NLC62
"What was the Ladies Protection Society?<h><n>", // NLC65
"Have you seen the poem in my room?<h><n>", // NAS54
"Do you think Charlie is doing a good job?<h><n>", // NLC67
- "Did Lizzie ever wear men's clothing?<h><n>" // NLC68
+ "Did Lizzie ever wear men's clothing?<h><n>", // NLC68
// 75
- "Where did Abby find those papers that are in the parlor?<h><n>" // NRM17
+ "Where did Abby find those papers that are in the parlor?<h><n>", // NRM17
"How did you find Charlie?<h><n>", // NRM18
"How do you know Louis?<h><n>", // NRM19
"How did you meet Abby?<h><n>", // NRM20
@@ -360,12 +360,12 @@ const Common::Array<Common::Array<const char *>> _nancy3ConditionalDialogueTexts
"Are you missing any papers?<h><n>", // NRM55
"Have you heard of someone named Lizzie Applegate?<h><n>", // NRM22
"What is Abby planning for tonight?<h><n>", // NRM23
- "Have you found any rainbow designs in the house?<h><n>" // NRM24
- "Do you think Charlie is responsible for these accidents?<h><n>" // NRM25
+ "Have you found any rainbow designs in the house?<h><n>", // NRM24
+ "Do you think Charlie is responsible for these accidents?<h><n>", // NRM25
// 85
"Does Charlie live around here?<h><n>", // NAS67
"Do you know why there's a speaker in the air vent?<h><n>", // NRM27
- "Whose laptop is that in the library?<h><n>" // NRM28
+ "Whose laptop is that in the library?<h><n>", // NRM28
"Why are there dead roses in the parlor?<h><n>", // NRM29
"Do you know what 'gum bo fu' means?<h><n>", // NLC60
},
@@ -486,8 +486,8 @@ const Common::Array<Common::Array<const char *>> _nancy3GoodbyeTexts = {
"I'll let you get back to what you were doing.<h>", // NAS90
"I should get going. Talk to you later.<h>", // NBG90
"I'll let you get back to your renovation.<h>", // NCM90
- "I should get going. Goodbye, Emily.<h>" // NEF90
- "I should get back to work. Goodbye.<h>" // NHG90
+ "I should get going. Goodbye, Emily.<h>", // NEF90
+ "I should get back to work. Goodbye.<h>", // NHG90
"I won't keep you any longer.<h>", // NLC90
"Goodbye, Ned.<h>", // NNN90
"I can see you're busy - I'll let you go.<h>" // NRG90
Commit: 13f95f178138e54cda26ff42ff7ae71186d4add2
https://github.com/scummvm/scummvm/commit/13f95f178138e54cda26ff42ff7ae71186d4add2
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-02T17:24:39+03:00
Commit Message:
NANCY: Implement SafeLockPuzzle
Implemented the puzzle type responsible for safe locks
with a single dial. First used in nancy3.
Changed paths:
A engines/nancy/action/safelockpuzzle.cpp
A engines/nancy/action/safelockpuzzle.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 2ff96a7a24c..f0cd65855e2 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -40,6 +40,7 @@
#include "engines/nancy/action/raycastpuzzle.h"
#include "engines/nancy/action/turningpuzzle.h"
#include "engines/nancy/action/tangrampuzzle.h"
+#include "engines/nancy/action/safelockpuzzle.h"
#include "engines/nancy/state/scene.h"
@@ -207,6 +208,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
return new OrderingPuzzle(OrderingPuzzle::PuzzleType::kPiano);
case 209:
return new TurningPuzzle();
+ case 210:
+ return new SafeLockPuzzle();
default:
error("Action Record type %i is invalid!", type);
return nullptr;
diff --git a/engines/nancy/action/safelockpuzzle.cpp b/engines/nancy/action/safelockpuzzle.cpp
new file mode 100644
index 00000000000..43740a5a6ec
--- /dev/null
+++ b/engines/nancy/action/safelockpuzzle.cpp
@@ -0,0 +1,283 @@
+/* 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 "common/random.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/action/safelockpuzzle.h"
+
+#include "engines/nancy/state/scene.h"
+
+namespace Nancy {
+namespace Action {
+
+void SafeLockPuzzle::init() {
+ g_nancy->_resource->loadImage(_imageName1, _image1);
+ g_nancy->_resource->loadImage(_imageName2, _image2);
+ g_nancy->_resource->loadImage(_resetImageName, _resetImage);
+
+ 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);
+
+ registerGraphics();
+}
+
+void SafeLockPuzzle::updateGraphics() {
+ if (_animState == kSelect && (_state == kActionTrigger ? _nextAnim - 500 : _nextAnim) < g_nancy->getTotalPlayTime()) {
+ _drawSurface.fillRect(_arrowDest, _drawSurface.getTransparentColor());
+ _animState = kNone;
+ _needsRedraw = true;
+ }
+
+ if (_animState == kSpin && _nextAnim < g_nancy->getTotalPlayTime()) {
+ drawDialFrame(_current * 2);
+ _animState = kNone;
+ }
+
+ if (_animState == kReset && _nextAnim < g_nancy->getTotalPlayTime()) {
+ _animState = kResetAnim;
+ g_nancy->_sound->playSound(_resetSound);
+ }
+
+ if (_animState == kResetAnim) {
+ // Framerate-dependent animation. We're restricting the engine to ~60fps so it shouldn't be too fast
+ _drawSurface.blitFrom(_resetImage, _resetDialSrcs[_current % _resetDialSrcs.size()], _dialDest);
+ ++_current;
+ if (_current >= _resetDialSrcs.size() * _resetTurns) {
+ _animState = kNone;
+ _current = 0;
+ drawDialFrame(_current * 2);
+ }
+ _needsRedraw = true;
+ }
+}
+
+void SafeLockPuzzle::readData(Common::SeekableReadStream &stream) {
+ readFilename(stream, _imageName1);
+ readFilename(stream, _imageName2);
+ readFilename(stream, _resetImageName);
+
+ readRect(stream, _dialDest);
+ readRectArray(stream, _dialSrcs, 20);
+
+ readRect(stream, _resetDest);
+ readRect(stream, _resetSrc);
+ readRect(stream, _arrowDest);
+ readRect(stream, _arrowSrc);
+
+ readRectArray(stream, _resetDialSrcs, 10);
+
+ _resetTurns = stream.readUint16LE();
+
+ uint16 solveSize = stream.readUint16LE();
+ _correctSequence.resize(solveSize);
+ for (uint i = 0; i < solveSize; ++i) {
+ _correctSequence[i] = stream.readUint16LE();
+ }
+ stream.skip((10 - solveSize) * 2);
+
+ readRect(stream, _ccwHotspot);
+ readRect(stream, _cwHotspot);
+
+ _spinSound.readNormal(stream);
+ _selectSound.readNormal(stream);
+ _resetSound.readNormal(stream);
+
+ _solveScene.readData(stream);
+ _solveSoundDelay = stream.readUint16LE();
+ _solveSound.readNormal(stream);
+
+ _exitScene.readData(stream);
+ readRect(stream, _exitHotspot);
+}
+
+void SafeLockPuzzle::execute() {
+ switch (_state) {
+ case kBegin :
+ init();
+ g_nancy->_sound->loadSound(_spinSound);
+ g_nancy->_sound->loadSound(_selectSound);
+ g_nancy->_sound->loadSound(_resetSound);
+ _state = kRun;
+ // fall through
+ case kRun :
+ if (!g_nancy->_sound->isSoundPlaying(_selectSound) && g_nancy->getTotalPlayTime() > _nextAnim) {
+ if (_playerSequence == _correctSequence) {
+ _solved = true;
+ _state = kActionTrigger;
+ _nextAnim = g_nancy->getTotalPlayTime() + 1000 * _solveSoundDelay;
+ }
+ }
+
+ break;
+ case kActionTrigger :
+ if (_solved) {
+ if (_nextAnim == 0) {
+ if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+ break;
+ }
+ } else {
+ if (_nextAnim < g_nancy->getTotalPlayTime()) {
+ g_nancy->_sound->loadSound(_solveSound);
+ g_nancy->_sound->playSound(_solveSound);
+ _nextAnim = 0;
+ }
+ break;
+ }
+
+ _solveScene.execute();
+ } else {
+ _exitScene.execute();
+ }
+
+ g_nancy->_sound->stopSound(_solveSound);
+ g_nancy->_sound->stopSound(_spinSound);
+ g_nancy->_sound->stopSound(_selectSound);
+ g_nancy->_sound->stopSound(_resetSound);
+
+ finishExecution();
+
+ break;
+ }
+}
+
+void SafeLockPuzzle::handleInput(NancyInput &input) {
+ if (_state != kRun || _playerSequence == _correctSequence) {
+ return;
+ }
+
+ if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kExit);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _state = kActionTrigger;
+ }
+
+ return;
+ } else if (NancySceneState.getViewport().convertViewportToScreen(_ccwHotspot).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCCW);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp && _nextAnim < g_nancy->getTotalPlayTime() &&
+ _animState != kReset && _animState != kResetAnim) {
+ if (_current == 0) {
+ _current = _dialSrcs.size() / 2 - 1;
+ } else {
+ --_current;
+ }
+
+ drawDialFrame(_current * 2 + 1);
+ _nextAnim = g_nancy->getTotalPlayTime() + 250; // hardcoded
+
+ g_nancy->_sound->playSound(_spinSound);
+ _animState = kSpin;
+ }
+
+ return;
+ } else if (NancySceneState.getViewport().convertViewportToScreen(_cwHotspot).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCW);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp && _nextAnim < g_nancy->getTotalPlayTime() &&
+ _animState != kReset && _animState != kResetAnim) {
+ drawDialFrame(_current * 2 + 1);
+ _nextAnim = g_nancy->getTotalPlayTime() + 250; // hardcoded
+
+ if (_current == (_dialSrcs.size() / 2) - 1) {
+ _current = 0;
+ } else {
+ ++_current;
+ }
+
+ g_nancy->_sound->playSound(_spinSound);
+ _animState = kSpin;
+ }
+
+ return;
+ }
+
+ if (g_nancy->_sound->isSoundPlaying(_selectSound) || _animState == kReset || _animState == kResetAnim || _nextAnim > g_nancy->getTotalPlayTime()) {
+ return;
+ }
+
+ if (NancySceneState.getViewport().convertViewportToScreen(_arrowDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ g_nancy->_sound->playSound(_selectSound);
+ pushSequence(_current);
+ _drawSurface.blitFrom(_image1, _arrowSrc, _arrowDest);
+ _animState = kSelect;
+ _nextAnim = g_nancy->getTotalPlayTime() + 500; // hardcoded
+ _needsRedraw = true;
+ }
+
+ return;
+ } else if (NancySceneState.getViewport().convertViewportToScreen(_resetDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _drawSurface.blitFrom(_image1, _resetSrc, _resetDest);
+ g_nancy->_sound->playSound(_selectSound);
+ _animState = kReset;
+ _nextAnim = g_nancy->getTotalPlayTime() + 500; // hardcoded
+ _current = 0;
+ _playerSequence.clear();
+ _needsRedraw = true;
+ }
+
+ return;
+ }
+}
+
+void SafeLockPuzzle::drawDialFrame(uint frame) {
+ debug("%u", frame);
+ if (frame >= _dialSrcs.size() / 2) {
+ _drawSurface.blitFrom(_image2, _dialSrcs[frame], _dialDest);
+ } else {
+ _drawSurface.blitFrom(_image1, _dialSrcs[frame], _dialDest);
+ }
+
+ _needsRedraw = true;
+}
+
+void SafeLockPuzzle::pushSequence(uint id) {
+ if (id != 0) {
+ // The ids in the correct sequence are in reverse order
+ id = (_dialSrcs.size() / 2) - id;
+ }
+
+ _playerSequence.push_back(id);
+ if (_playerSequence.size() > _correctSequence.size()) {
+ _playerSequence.erase(_playerSequence.begin());
+ }
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/safelockpuzzle.h b/engines/nancy/action/safelockpuzzle.h
new file mode 100644
index 00000000000..9696e3bd969
--- /dev/null
+++ b/engines/nancy/action/safelockpuzzle.h
@@ -0,0 +1,98 @@
+/* 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_SAFELOCKPUZZLE_H
+#define NANCY_ACTION_SAFELOCKPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+
+namespace Nancy {
+namespace Action {
+
+// Handles a specific type of puzzle where clicking an object rotates it,
+// as well as several other objects linked to it. Examples are the sun/moon
+// and staircase spindle puzzles in nancy3
+class SafeLockPuzzle : public RenderActionRecord {
+public:
+ SafeLockPuzzle() : RenderActionRecord(7) {}
+ virtual ~SafeLockPuzzle() {}
+
+ void init() override;
+ void updateGraphics() override;
+
+ void readData(Common::SeekableReadStream &stream) override;
+ void execute() override;
+ void handleInput(NancyInput &input) override;
+
+protected:
+ enum AnimState { kNone, kSpin, kSelect, kReset, kResetAnim };
+ Common::String getRecordTypeName() const override { return "SafeLockPuzzle"; }
+ bool isViewportRelative() const override { return true; }
+
+ void drawDialFrame(uint frame);
+ void pushSequence(uint id);
+
+ Common::String _imageName1;
+ Common::String _imageName2;
+ Common::String _resetImageName;
+
+ Common::Rect _dialDest;
+
+ Common::Array<Common::Rect> _dialSrcs;
+
+ Common::Rect _resetDest;
+ Common::Rect _resetSrc;
+ Common::Rect _arrowDest;
+ Common::Rect _arrowSrc;
+
+ Common::Array<Common::Rect> _resetDialSrcs;
+
+ uint16 _resetTurns;
+
+ Common::Array<uint16> _correctSequence;
+
+ Common::Rect _ccwHotspot;
+ Common::Rect _cwHotspot;
+
+ SoundDescription _spinSound;
+ SoundDescription _selectSound;
+ SoundDescription _resetSound;
+
+ SceneChangeWithFlag _solveScene;
+ uint _solveSoundDelay;
+ SoundDescription _solveSound;
+
+ SceneChangeWithFlag _exitScene;
+ Common::Rect _exitHotspot;
+
+ Graphics::ManagedSurface _image1, _image2, _resetImage;
+
+ Common::Array<uint16> _playerSequence;
+ bool _solved = false;
+ AnimState _animState = kNone;
+ uint32 _nextAnim = 0;
+ uint16 _current = 0;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_SAFELOCKPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index adb4c4b572a..c89d91a64e2 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS = \
action/riddlepuzzle.o \
action/rippedletterpuzzle.o \
action/rotatinglockpuzzle.o \
+ action/safelockpuzzle.o \
action/secondarymovie.o \
action/secondaryvideo.o \
action/setplayerclock.o \
Commit: d520b0c8dbc50b4f69d42117502db77488c08fc5
https://github.com/scummvm/scummvm/commit/d520b0c8dbc50b4f69d42117502db77488c08fc5
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-02T17:24:39+03:00
Commit Message:
NANCY: Fix arrow cursor hotspot
Added a manual fix for the Nancy games' awful hotspot
position for the arrow cursor.
Changed paths:
engines/nancy/cursor.cpp
engines/nancy/cursor.h
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index adc3acf3b1a..884be28ab30 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -76,6 +76,8 @@ void CursorManager::init(Common::SeekableReadStream *chunkStream) {
_isInitialized = true;
+ adjustCursorHotspot();
+
delete chunkStream;
}
@@ -239,4 +241,27 @@ void CursorManager::showCursor(bool shouldShow) {
CursorMan.showMouse(shouldShow);
}
+void CursorManager::adjustCursorHotspot() {
+ if (g_nancy->getGameType() == kGameTypeVampire) {
+ return;
+ }
+
+ // Improvement: the arrow cursor in the Nancy games has an atrocious hotspot that's
+ // right in the middle of the graphic, instead of in the top left where
+ // it would make sense to be. This function fixes that.
+ // The hotspot is still a few pixels lower than it should be to account
+ // for the different graphic when hovering UI elements
+
+ // TODO: Make this optional?
+
+ uint startID = _curCursorID;
+
+ setCursorType(kNormalArrow);
+ _cursors[_curCursorID].hotspot = {3, 4};
+ setCursorType(kHotspotArrow);
+ _cursors[_curCursorID].hotspot = {3, 4};
+
+ _curCursorID = startID;
+}
+
} // End of namespace Nancy
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index 1a22475af3e..f73ed10c225 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -76,6 +76,8 @@ public:
private:
void showCursor(bool shouldShow);
+ void adjustCursorHotspot();
+
struct Cursor {
Common::Rect bounds;
Common::Point hotspot;
More information about the Scummvm-git-logs
mailing list