[Scummvm-git-logs] scummvm master -> cf188ca8fb678080ef5b311b094ca787e0a2ff60
fracturehill
noreply at scummvm.org
Tue Oct 17 15:32:04 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:
3551db675d NANCY: Implement AssemblyPuzzle
489b9c696d NANCY: Fix Coverity issues
cf188ca8fb NANCY: Implement CubePuzzle
Commit: 3551db675d56da5c8c08b0597bd79d6892555a1f
https://github.com/scummvm/scummvm/commit/3551db675d56da5c8c08b0597bd79d6892555a1f
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-17T18:31:08+03:00
Commit Message:
NANCY: Implement AssemblyPuzzle
Implemented the record type responsible for nancy6's
broken pottery assembly minigame.
Changed paths:
A engines/nancy/action/puzzle/assemblypuzzle.cpp
A engines/nancy/action/puzzle/assemblypuzzle.h
engines/nancy/action/arfactory.cpp
engines/nancy/module.mk
engines/nancy/renderobject.h
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index a56b3d2358c..a090c786cb2 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -30,6 +30,7 @@
#include "engines/nancy/action/secondaryvideo.h"
#include "engines/nancy/action/secondarymovie.h"
+#include "engines/nancy/action/puzzle/assemblypuzzle.h"
#include "engines/nancy/action/puzzle/bombpuzzle.h"
#include "engines/nancy/action/puzzle/collisionpuzzle.h"
#include "engines/nancy/action/puzzle/leverpuzzle.h"
@@ -311,6 +312,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
return new MouseLightPuzzle();
case 220:
return new TwoDialPuzzle();
+ case 222:
+ return new AssemblyPuzzle();
case 224:
return new OrderingPuzzle(OrderingPuzzle::kKeypadTerse);
default:
diff --git a/engines/nancy/action/puzzle/assemblypuzzle.cpp b/engines/nancy/action/puzzle/assemblypuzzle.cpp
new file mode 100644
index 00000000000..94ed2b009ec
--- /dev/null
+++ b/engines/nancy/action/puzzle/assemblypuzzle.cpp
@@ -0,0 +1,320 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/action/puzzle/assemblypuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+void AssemblyPuzzle::init() {
+ g_nancy->_resource->loadImage(_imageName, _image);
+ _image.setTransparentColor(_drawSurface.getTransparentColor());
+
+ for (uint i = 0; i < _pieces.size(); ++i) {
+ Piece &piece = _pieces[i];
+ piece.curRotation = 0;
+ piece._drawSurface.create(_image, piece.srcRects[piece.curRotation]);
+ piece.setVisible(true);
+ piece.setTransparent(true);
+ piece.moveTo(piece.placed ? piece.destRects[0] : piece.startRect);
+ piece.setZ(_z + i + _pieces.size());
+ }
+}
+
+void AssemblyPuzzle::registerGraphics() {
+ for (uint i = 0; i < _pieces.size(); ++i) {
+ _pieces[i].registerGraphics();
+ }
+}
+
+void AssemblyPuzzle::readData(Common::SeekableReadStream &stream) {
+ readFilename(stream, _imageName);
+
+ uint16 numPieces = stream.readUint16LE();
+ _height = stream.readUint16LE();
+
+ readRect(stream, _cwCursorDest);
+ readRect(stream, _ccwCursorDest);
+
+ _pieces.resize(numPieces);
+ for (uint i = 0; i < numPieces; ++i) {
+ Piece &piece = _pieces[i];
+ readRectArray(stream, piece.srcRects, 4);
+ readRectArray(stream, piece.destRects, 4);
+
+ readRect(stream, piece.startRect);
+
+ piece.correctRotation = stream.readUint16LE();
+ piece.layer = stream.readUint16LE();
+ piece.placed = stream.readUint16LE();
+ }
+ stream.skip((12 - numPieces) * 150);
+
+ _rotateSound.readNormal(stream);
+ _pickUpSound.readNormal(stream);
+ _placeDownSound.readNormal(stream);
+
+ _allowWrongPieceHotspot = stream.readUint16LE();
+
+ _wrongPieceSounds.resize(4);
+ _wrongPieceTexts.resize(4);
+
+ for (uint i = 0; i < 4; ++i) {
+ _wrongPieceSounds[i].readNormal(stream);
+ }
+
+ char buf[200];
+ for (uint i = 0; i < 4; ++i) {
+ stream.read(buf, 200);
+ assembleTextLine(buf, _wrongPieceTexts[i], 200);
+ }
+
+ _solveScene.readData(stream);
+ _solveSound.readNormal(stream);
+ stream.read(buf, 200);
+ assembleTextLine(buf, _solveText, 200);
+
+ _exitScene.readData(stream);
+ readRect(stream, _exitHotspot);
+}
+
+void AssemblyPuzzle::execute() {
+ switch (_state) {
+ case kBegin:
+ init();
+ registerGraphics();
+
+ g_nancy->_sound->loadSound(_rotateSound);
+ g_nancy->_sound->loadSound(_pickUpSound);
+ g_nancy->_sound->loadSound(_placeDownSound);
+
+ _state = kRun;
+ // fall through
+ case kRun:
+ if (_layersAssembled != _height) {
+ return;
+ }
+
+ g_nancy->_sound->loadSound(_solveSound);
+ g_nancy->_sound->playSound(_solveSound);
+ NancySceneState.getTextbox().clear();
+ NancySceneState.getTextbox().addTextLine(_solveText);
+ NancySceneState.setEventFlag(_solveScene._flag);
+ _completed = true;
+
+ _state = kActionTrigger;
+ break;
+ case kActionTrigger:
+ if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+ return;
+ }
+
+ if (_completed) {
+ NancySceneState.changeScene(_solveScene._sceneChange);
+ } else {
+ _exitScene.execute();
+ }
+
+ break;
+ }
+}
+
+void AssemblyPuzzle::handleInput(NancyInput &input) {
+ if (_state == kActionTrigger && _completed && g_nancy->_sound->isSoundPlaying(_solveSound)) {
+ return;
+ }
+
+ if (_pickedUpPiece == -1 && NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(g_nancy->_cursorManager->_puzzleExitCursor);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _state = kActionTrigger;
+ _completed = false;
+ }
+ return;
+ }
+
+ if (_pickedUpPiece == -1 && NancySceneState.getViewport().convertViewportToScreen(_cwCursorDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCW);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp && !g_nancy->_sound->isSoundPlaying(_rotateSound)) {
+ g_nancy->_sound->playSound(_rotateSound);
+ rotateBase(false);
+ return;
+ }
+ }
+
+ if (_pickedUpPiece == -1 && NancySceneState.getViewport().convertViewportToScreen(_ccwCursorDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCCW);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp && !g_nancy->_sound->isSoundPlaying(_rotateSound)) {
+ g_nancy->_sound->playSound(_rotateSound);
+ rotateBase(true);
+ return;
+ }
+ }
+
+ if (_completed) {
+ return;
+ }
+
+ for (uint i = 0; i < _pieces.size(); ++i) {
+ if (NancySceneState.getViewport().convertViewportToScreen(_pieces[i].startRect).contains(input.mousePos)) {
+ if (_pickedUpPiece == -1 && _pieces[i].placed) {
+ return;
+ }
+
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ if (_pickedUpPiece != -1) {
+ _pieces[_pickedUpPiece].putDown();
+ _pieces[_pickedUpPiece].moveTo(_pieces[_pickedUpPiece].startRect);
+ g_nancy->_sound->playSound(_placeDownSound);
+ }
+
+ if (_pickedUpPiece != (int)i && !_pieces[i].placed) {
+ // Clicked on another piece while holding, swap them
+ _pickedUpPiece = i;
+ _pieces[i].pickUp();
+ g_nancy->_sound->playSound(_pickUpSound);
+
+ for (uint j = 1; j < _pieces.size(); ++j) {
+ Piece &piece = _pieces[j];
+ if (!piece.placed && piece.getZOrder() > _pieces[i].getZOrder()) {
+ piece.setZ(piece.getZOrder() - 1);
+ piece.registerGraphics();
+ }
+ }
+
+ _pieces[i].setZ(_z + _pieces.size() * 2);
+ _pieces[i].registerGraphics();
+ } else {
+ // Clicked the dest of the picked up piece, or an already placed one; simply put it down
+ _pickedUpPiece = -1;
+ }
+ }
+ }
+ }
+
+ if (_pickedUpPiece != -1) {
+ // Piece picked up
+ Piece &pickedUpPiece = _pieces[_pickedUpPiece];
+ pickedUpPiece.handleInput(input);
+
+ bool isWrong = _curRotation != pickedUpPiece.correctRotation;
+ bool otherIsPlaced = false;
+ for (uint i = 0; i < _pieces.size(); ++i) {
+ if (_pieces[i].placed && _pieces[i].curRotation == 0 && _pieces[i].layer - 1 == (int)_layersAssembled) {
+ otherIsPlaced = true;
+ break;
+ }
+ }
+
+ if (!otherIsPlaced && (!isWrong || (_allowWrongPieceHotspot && _layersAssembled + 1 == pickedUpPiece.layer))) {
+ if (NancySceneState.getViewport().convertViewportToScreen(pickedUpPiece.destRects[0]).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ if (!isWrong) {
+ pickedUpPiece.putDown();
+ pickedUpPiece.moveTo(pickedUpPiece.destRects[0]);
+ g_nancy->_sound->playSound(_placeDownSound);
+ pickedUpPiece.placed = true;
+ _pickedUpPiece = -1;
+
+ // Check for finished layer
+ uint placedOnLayer = 0;
+ for (uint i = 0; i < _pieces.size(); ++i) {
+ if (_pieces[i].layer == _layersAssembled + 1 && _pieces[i].placed) {
+ ++placedOnLayer;
+ }
+ }
+
+ if (placedOnLayer == 4) {
+ ++_layersAssembled;
+ }
+ } else if (_allowWrongPieceHotspot) {
+ // Wrong place, play a sound
+ g_nancy->_sound->loadSound(_wrongPieceSounds[_curRotation]);
+ g_nancy->_sound->playSound(_wrongPieceSounds[_curRotation]);
+ if (!_wrongPieceTexts[_curRotation].empty()) {
+ NancySceneState.getTextbox().addTextLine(_wrongPieceTexts[_curRotation], 4000); // check
+ }
+
+ // and put down the piece
+ pickedUpPiece.putDown();
+ pickedUpPiece.moveTo(pickedUpPiece.startRect);
+ _pickedUpPiece = -1;
+ }
+ }
+ }
+ }
+ }
+}
+
+void AssemblyPuzzle::rotateBase(bool ccw) {
+ // _curRotation moves in the opposite direction to pieces' rotations
+ _curRotation += ccw ? 1 : -1;
+ if (_curRotation < 0) {
+ _curRotation = 3;
+ } else if (_curRotation > 3) {
+ _curRotation = 0;
+ }
+
+ for (uint i = 0; i < _pieces.size(); ++i) {
+ Piece &piece = _pieces[i];
+ if (piece.placed) {
+ piece.curRotation += ccw ? -1 : 1;
+ if (piece.curRotation < 0) {
+ piece.curRotation = 3;
+ } else if (piece.curRotation > 3) {
+ piece.curRotation = 0;
+ }
+
+ uint base = 0;
+ if (piece.curRotation == 0) {
+ base = 2;
+ } else if (piece.curRotation == 1 || piece.curRotation == 3) {
+ base = 1;
+ }
+
+ piece.setZ(_z + base + 4 * (piece.layer - 1));
+ piece.registerGraphics();
+
+ piece.moveTo(piece.destRects[piece.curRotation]);
+ piece._drawSurface.create(_image, piece.srcRects[piece.curRotation]);
+ piece.setTransparent(true);
+ }
+ }
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/puzzle/assemblypuzzle.h b/engines/nancy/action/puzzle/assemblypuzzle.h
new file mode 100644
index 00000000000..c03d27c3771
--- /dev/null
+++ b/engines/nancy/action/puzzle/assemblypuzzle.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_ASSEMBLYPUZZLE_H
+#define NANCY_ACTION_ASSEMBLYPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/misc/mousefollow.h"
+
+namespace Nancy {
+namespace Action {
+
+// Minigame where the player is provided with the broken pieces of something
+// (a piece of pottery in nancy6), and has to assemble it.
+class AssemblyPuzzle : public RenderActionRecord {
+public:
+ AssemblyPuzzle() : RenderActionRecord(7) {}
+ virtual ~AssemblyPuzzle() {}
+
+ 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 "AssemblyPuzzle"; };
+ bool isViewportRelative() const override { return true; }
+
+ void rotateBase(bool ccw);
+
+ struct Piece : Misc::MouseFollowObject {
+ Common::Array<Common::Rect> srcRects;
+ Common::Array<Common::Rect> destRects;
+
+ Common::Rect startRect;
+ uint16 correctRotation = 0;
+ uint16 layer = 0;
+ bool placed = false;
+
+ int curRotation = 0;
+ };
+
+ Common::String _imageName;
+
+ uint16 _height = 0;
+
+ Common::Rect _cwCursorDest;
+ Common::Rect _ccwCursorDest;
+
+ Common::Array<Piece> _pieces;
+
+ SoundDescription _rotateSound;
+ SoundDescription _pickUpSound;
+ SoundDescription _placeDownSound;
+
+ bool _allowWrongPieceHotspot = false;
+
+ Common::Array<SoundDescription> _wrongPieceSounds;
+ Common::Array<Common::String> _wrongPieceTexts;
+
+ SceneChangeWithFlag _solveScene; // has 9999 in nancy6, so the puzzle doesn't auto-exit
+ SoundDescription _solveSound;
+ Common::String _solveText;
+
+ SceneChangeWithFlag _exitScene;
+ Common::Rect _exitHotspot;
+
+ Graphics::ManagedSurface _image;
+
+ int _pickedUpPiece = -1;
+ int _curRotation = 0;
+ uint _layersAssembled = 0;
+ bool _completed = false;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_ASSEMBLYPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 906dfc9786a..a5cdc91c4e8 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS = \
action/overlay.o \
action/secondarymovie.o \
action/secondaryvideo.o \
+ action/puzzle/assemblypuzzle.o \
action/puzzle/bombpuzzle.o \
action/puzzle/collisionpuzzle.o \
action/puzzle/leverpuzzle.o \
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
index f03a24b8d92..4ae27fe9fbc 100644
--- a/engines/nancy/renderobject.h
+++ b/engines/nancy/renderobject.h
@@ -65,14 +65,11 @@ public:
Common::Rect convertToScreen(const Common::Rect &rect) const;
Common::Rect getBounds() const { return Common::Rect(_screenPosition.width(), _screenPosition.height()); }
+ uint16 getZOrder() const { return _z; }
Graphics::ManagedSurface _drawSurface;
protected:
- // Z order and blit type are extracted directly from the corresponding
- // ZRenderStruct from the original engine
- uint16 getZOrder() const { return _z; }
-
// Needed for proper handling of objects inside the viewport
virtual bool isViewportRelative() const { return false; }
Commit: 489b9c696d7e09374a2cc7cb4cc253f9dfc3a6e5
https://github.com/scummvm/scummvm/commit/489b9c696d7e09374a2cc7cb4cc253f9dfc3a6e5
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-17T18:31:08+03:00
Commit Message:
NANCY: Fix Coverity issues
Changed paths:
engines/nancy/action/arfactory.cpp
engines/nancy/action/secondarymovie.cpp
engines/nancy/misc/specialeffect.cpp
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index a090c786cb2..03d835413c3 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -144,7 +144,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
case 56:
if (g_nancy->getGameType() <= kGameTypeNancy6) {
return new ConversationVideo();
- } else if (g_nancy->getGameType() >= kGameTypeNancy7) {
+ } else {
return new OverlayAnimTerse();
}
return nullptr;
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index b99c091c440..22f8ef1115f 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -95,7 +95,7 @@ void PlaySecondaryMovie::init() {
if (!_decoder) {
if (_videoType == kVideoPlaytypeAVF) {
_decoder = new AVFDecoder();
- } else if (_videoType == kVideoPlaytypeBink) {
+ } else {
_decoder = new Video::BinkDecoder();
}
}
diff --git a/engines/nancy/misc/specialeffect.cpp b/engines/nancy/misc/specialeffect.cpp
index b039824ba19..248106bb498 100644
--- a/engines/nancy/misc/specialeffect.cpp
+++ b/engines/nancy/misc/specialeffect.cpp
@@ -138,10 +138,10 @@ void SpecialEffect::afterSceneChange() {
}
bool SpecialEffect::isDone() const {
- if (_type != kCrossDissolve) {
+ if (_type == kBlackout) {
return g_nancy->getTotalPlayTime() > _fadeToBlackEndTime;
} else {
- bool canFinish = _type == kThroughBlack ? _throughBlackStarted2nd : true;
+ bool canFinish = (_type == kThroughBlack) ? _throughBlackStarted2nd : true;
return _totalTime ? ((g_nancy->getTotalPlayTime() > _startTime + _totalTime) && (_currentFrame != 0) && canFinish) : (_currentFrame >= (int)_numFrames);
}
}
Commit: cf188ca8fb678080ef5b311b094ca787e0a2ff60
https://github.com/scummvm/scummvm/commit/cf188ca8fb678080ef5b311b094ca787e0a2ff60
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-10-17T18:31:08+03:00
Commit Message:
NANCY: Implement CubePuzzle
Implemented the record type responsible for nancy6's
cube assembly minigame (not to be confused with the
pottery assembly one, which uses a different AR type)
Changed paths:
A engines/nancy/action/puzzle/cubepuzzle.cpp
A engines/nancy/action/puzzle/cubepuzzle.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 03d835413c3..6f2bf836768 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -33,6 +33,7 @@
#include "engines/nancy/action/puzzle/assemblypuzzle.h"
#include "engines/nancy/action/puzzle/bombpuzzle.h"
#include "engines/nancy/action/puzzle/collisionpuzzle.h"
+#include "engines/nancy/action/puzzle/cubepuzzle.h"
#include "engines/nancy/action/puzzle/leverpuzzle.h"
#include "engines/nancy/action/puzzle/mazechasepuzzle.h"
#include "engines/nancy/action/puzzle/mouselightpuzzle.h"
@@ -314,6 +315,8 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
return new TwoDialPuzzle();
case 222:
return new AssemblyPuzzle();
+ case 223:
+ return new CubePuzzle();
case 224:
return new OrderingPuzzle(OrderingPuzzle::kKeypadTerse);
default:
diff --git a/engines/nancy/action/puzzle/cubepuzzle.cpp b/engines/nancy/action/puzzle/cubepuzzle.cpp
new file mode 100644
index 00000000000..75c3dcd8d11
--- /dev/null
+++ b/engines/nancy/action/puzzle/cubepuzzle.cpp
@@ -0,0 +1,322 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/action/puzzle/cubepuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+void CubePuzzle::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);
+ _image.setTransparentColor(_drawSurface.getTransparentColor());
+
+ for (uint i = 0; i < 5; ++i) {
+ _drawSurface.blitFrom(_image, _pieceSrcs[i], _pieceDests[i]);
+ }
+
+ _placedPieces.resize(5, false);
+ _curRotation = _startRotation;
+ _drawSurface.blitFrom(_image, _placedSrcs[_curRotation][0], _placedDest);
+}
+
+void CubePuzzle::registerGraphics() {
+ _curPiece.registerGraphics();
+ RenderActionRecord::registerGraphics();
+}
+
+void CubePuzzle::readData(Common::SeekableReadStream &stream) {
+ readFilename(stream, _imageName);
+
+ readRect(stream, _cwCursorDest);
+ readRect(stream, _ccwCursorDest);
+
+ readRect(stream, _placedDest);
+
+ // four pieces on the side, 1 on top
+ _pieceSrcs.resize(5);
+ _pieceDests.resize(5);
+ for (uint i = 0; i < 5; ++i) {
+ readRect(stream, _pieceSrcs[i]);
+ readRect(stream, _pieceDests[i]);
+ }
+
+ _placedSrcs.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ readRectArray(stream, _placedSrcs[i], 9);
+ }
+
+ _startRotation = stream.readUint16LE();
+
+ _rotateSound.readNormal(stream);
+ _pickUpSound.readNormal(stream);
+ _placeDownSound.readNormal(stream);
+
+ _solveSceneIDs.resize(4);
+ for (uint i = 0; i < 3; ++i) {
+ _solveSceneIDs[i] = stream.readUint16LE();
+ }
+ _solveScene.readData(stream);
+ _solveSceneIDs[3] = _solveScene._sceneChange.sceneID;
+ _solveSound.readNormal(stream);
+
+ _exitScene.readData(stream);
+ readRect(stream, _exitHotspot);
+}
+
+void CubePuzzle::execute() {
+ switch (_state) {
+ case kBegin:
+ init();
+ registerGraphics();
+
+ g_nancy->_sound->loadSound(_rotateSound);
+ g_nancy->_sound->loadSound(_pickUpSound);
+ g_nancy->_sound->loadSound(_placeDownSound);
+
+ _state = kRun;
+ // fall through
+ case kRun:
+ for (uint i = 0; i < 5; ++i) {
+ if (!_placedPieces[i]) {
+ return;
+ }
+ }
+
+ g_nancy->_sound->loadSound(_solveSound);
+ g_nancy->_sound->playSound(_solveSound);
+ NancySceneState.setEventFlag(_solveScene._flag);
+ _completed = true;
+
+ _state = kActionTrigger;
+ break;
+ case kActionTrigger:
+ if (g_nancy->_sound->isSoundPlaying(_solveSound)) {
+ return;
+ }
+
+ if (_completed) {
+ _solveScene._sceneChange.sceneID = _solveSceneIDs[_curRotation];
+ NancySceneState.changeScene(_solveScene._sceneChange);
+ } else {
+ _exitScene.execute();
+ }
+
+ g_nancy->_sound->stopSound(_solveSound);
+ g_nancy->_sound->stopSound(_rotateSound);
+ g_nancy->_sound->stopSound(_pickUpSound);
+ g_nancy->_sound->stopSound(_placeDownSound);
+
+ break;
+ }
+}
+
+void CubePuzzle::handleInput(NancyInput &input) {
+ if (_state != kRun) {
+ return;
+ }
+
+ if (_pickedUpPiece == -1 && NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(g_nancy->_cursorManager->_puzzleExitCursor);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _state = kActionTrigger;
+ _completed = false;
+ }
+ return;
+ }
+
+ if (_pickedUpPiece == -1 && NancySceneState.getViewport().convertViewportToScreen(_cwCursorDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCW);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp && !g_nancy->_sound->isSoundPlaying(_rotateSound)) {
+ g_nancy->_sound->playSound(_rotateSound);
+ rotateBase(-1);
+ return;
+ }
+ }
+
+ if (_pickedUpPiece == -1 && NancySceneState.getViewport().convertViewportToScreen(_ccwCursorDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCCW);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp && !g_nancy->_sound->isSoundPlaying(_rotateSound)) {
+ g_nancy->_sound->playSound(_rotateSound);
+ rotateBase(1);
+ return;
+ }
+ }
+
+ for (uint i = 0; i < 5; ++i) {
+ if (NancySceneState.getViewport().convertViewportToScreen(_pieceDests[i]).contains(input.mousePos)) {
+ if (_pickedUpPiece == -1 && _placedPieces[i]) {
+ return;
+ }
+
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ if (_pickedUpPiece != -1) {
+ _curPiece.putDown();
+ _curPiece.setVisible(false);
+ g_nancy->_sound->playSound(_placeDownSound);
+
+ _drawSurface.fillRect(_pieceDests[_pickedUpPiece], _drawSurface.getTransparentColor());
+ _drawSurface.blitFrom(_image, _pieceSrcs[_pickedUpPiece], _pieceDests[_pickedUpPiece]);
+ _needsRedraw = true;
+ }
+
+ if (_pickedUpPiece != (int)i && !_placedPieces[i]) {
+ // Clicked on another piece while holding, swap them
+ _drawSurface.fillRect(_pieceDests[i], _drawSurface.getTransparentColor());
+
+ if (_pickedUpPiece != -1) {
+ _drawSurface.fillRect(_pieceDests[_pickedUpPiece], _drawSurface.getTransparentColor());
+ _drawSurface.blitFrom(_image, _pieceSrcs[_pickedUpPiece], _pieceDests[_pickedUpPiece]);
+ }
+
+ _needsRedraw = true;
+
+ _pickedUpPiece = i;
+ g_nancy->_sound->playSound(_pickUpSound);
+
+ _curPiece.pickUp();
+ _curPiece.setVisible(true);
+ _curPiece._drawSurface.create(_image, _pieceSrcs[i]);
+ _curPiece.setVisible(true);
+ } else {
+ // Clicked the dest of the picked up piece, or an already placed one; simply put it down
+ _pickedUpPiece = -1;
+ }
+ }
+ }
+ }
+
+ if (_pickedUpPiece != -1) {
+ // Piece picked up
+ _curPiece.handleInput(input);
+
+ bool canPlace = false;
+
+ if (_pickedUpPiece == 0) {
+ // Top piece can ble placed only in the correct rotation (which is 0)
+ canPlace = _curRotation == 0;
+ } else {
+ canPlace = _curRotation + 1 == _pickedUpPiece;
+ }
+
+ if (canPlace) {
+ if (NancySceneState.getViewport().convertViewportToScreen(_placedDest).contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _placedPieces[_pickedUpPiece] = true;
+ _curPiece.putDown();
+ _curPiece.setVisible(false);
+ _pickedUpPiece = -1;
+ g_nancy->_sound->playSound(_placeDownSound);
+ rotateBase(0);
+ }
+ }
+ }
+ }
+}
+
+void CubePuzzle::rotateBase(int dir) {
+ _drawSurface.fillRect(_placedDest, _drawSurface.getTransparentColor());
+
+ _curRotation += dir;
+ if (_curRotation < 0) {
+ _curRotation = 3;
+ } else if (_curRotation > 3) {
+ _curRotation = 0;
+ }
+
+ uint srcSelect = 0;
+
+ if (_placedPieces[0]) {
+ if (_placedPieces[_curRotation + 1]) {
+ // Front piece is in, other checks are unnecessary
+ srcSelect = 2;
+ } else {
+ int leftIndex = _curRotation + 1;
+ if (leftIndex > 3) {
+ leftIndex = 0;
+ }
+
+ int rightIndex = _curRotation - 1;
+ if (rightIndex < 0) {
+ rightIndex = 3;
+ }
+
+ int backIndex = (_curRotation + 2) % 4;
+
+ if (_placedPieces[leftIndex + 1]) {
+ // Left piece is in, check for right or back
+ if (_placedPieces[rightIndex + 1]) {
+ // Draw left & right piece
+ srcSelect = 8;
+ } else if (_placedPieces[backIndex]) {
+ // Draw left & back piece
+ srcSelect = 6;
+ } else {
+ // Draw left piece only
+ srcSelect = 3;
+ }
+ } else if (_placedPieces[rightIndex + 1]) {
+ // Right piece is in, check for back
+ if (_placedPieces[backIndex + 1]) {
+ // Draw right & back piece
+ srcSelect = 7;
+ } else {
+ // Draw right piece only
+ srcSelect = 4;
+ }
+ } else if (_placedPieces[backIndex + 1]) {
+ // Draw back piece only
+ srcSelect = 5;
+ } else {
+ // Draw top piece only
+ srcSelect = 1;
+ }
+ }
+ }
+
+ _drawSurface.blitFrom(_image, _placedSrcs[_curRotation][srcSelect], _placedDest);
+ _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/puzzle/cubepuzzle.h b/engines/nancy/action/puzzle/cubepuzzle.h
new file mode 100644
index 00000000000..1a00d0ea872
--- /dev/null
+++ b/engines/nancy/action/puzzle/cubepuzzle.h
@@ -0,0 +1,101 @@
+/* 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_CUBEPUZZLE_H
+#define NANCY_ACTION_CUBEPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/misc/mousefollow.h"
+
+namespace Nancy {
+namespace Action {
+
+// Similar idea to AssemblyPuzzle; the player is provided with broken pieces of
+// a cube, and has to assemble it back together. However, the data is completely
+// different to AssemblyPuzzle's, so we need separate implementations.
+class CubePuzzle : public RenderActionRecord {
+public:
+ CubePuzzle() : RenderActionRecord(7) {}
+ virtual ~CubePuzzle() {}
+
+ 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 "CubePuzzle"; };
+ bool isViewportRelative() const override { return true; }
+
+ void rotateBase(int dir);
+
+ Common::String _imageName;
+
+ Common::Rect _cwCursorDest;
+ Common::Rect _ccwCursorDest;
+
+ Common::Rect _placedDest;
+
+ Common::Array<Common::Rect> _pieceSrcs; // Used only when not placed
+ Common::Array<Common::Rect> _pieceDests; // Used only when not placed
+
+ // 4 arrays with 9 rects each; every rect corresponds to a specific permutation of placed pieces:
+ // - bottom piece only (start state)
+ // - bottom & top piece
+ // - front piece placed
+ // - left piece placed
+ // - back piece placed
+ // - right piece placed
+ // - left + back piece placed
+ // - right + back piece placed
+ // - left + right piece placed
+ Common::Array<Common::Array<Common::Rect>> _placedSrcs;
+
+ uint16 _startRotation = 0;
+
+ SoundDescription _rotateSound;
+ SoundDescription _pickUpSound;
+ SoundDescription _placeDownSound;
+
+ // Multiple solve scenes, one for each cube orientation
+ Common::Array<uint> _solveSceneIDs;
+ SceneChangeWithFlag _solveScene;
+ SoundDescription _solveSound;
+
+ SceneChangeWithFlag _exitScene;
+ Common::Rect _exitHotspot;
+
+ Graphics::ManagedSurface _image;
+ Misc::MouseFollowObject _curPiece;
+
+ Common::Array<bool> _placedPieces;
+ int _pickedUpPiece = -1;
+
+ int _curRotation = 0;
+ bool _completed = false;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_CUBEPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index a5cdc91c4e8..d305ab4f348 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS = \
action/puzzle/assemblypuzzle.o \
action/puzzle/bombpuzzle.o \
action/puzzle/collisionpuzzle.o \
+ action/puzzle/cubepuzzle.o \
action/puzzle/leverpuzzle.o \
action/puzzle/mazechasepuzzle.o \
action/puzzle/mouselightpuzzle.o \
More information about the Scummvm-git-logs
mailing list