[Scummvm-git-logs] scummvm master -> d587f77071b19e910e9ea8c96dddd49f4fd06e2e
fracturehill
noreply at scummvm.org
Wed Mar 15 16:39:24 UTC 2023
This automated email contains information about 15 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
3f6cc5ec9c NANCY: Don't cut off text in The Vampire Diaries
2407996d35 NANCY: Add missing #include
cb29c7d1a3 NANCY: Enable map button in The Vampire Diaries
9988b3ba43 NANCY: Implement map in The Vampire Diaries
87c7cdb577 NANCY: Don't store time of day
4069c5e381 NANCY: Correctly implement changing palettes
85dcfb64d8 NANCY: Do not clear palette on viewport scroll
18e8ac14f5 NANCY: Fix secondary movie transparency
79f3bace94 NANCY: Fix player time speed in The Vampire Diaries
8470f4deac NANCY: Play correct sound on button press
318f84fb90 NANCY: Clear sound when loading game
fed8207725 NANCY: Map improvements
23462c8c83 NANCY: Implement clock
95e4967b6d NANCY: Fix flashing cursor during primary video
d587f77071 NANCY: Respect palette changes in secondary video
Commit: 3f6cc5ec9c5cea772382883ae8347143ba4d1a1d
https://github.com/scummvm/scummvm/commit/3f6cc5ec9c5cea772382883ae8347143ba4d1a1d
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:00+02:00
Commit Message:
NANCY: Don't cut off text in The Vampire Diaries
The calculations for getting the inner height of the textbox have been
tweaked to get results closer to the original engine. This fixes an issue
in The Vampire Diaries where longer text would get cut off at the end.
Changed paths:
engines/nancy/ui/textbox.cpp
engines/nancy/ui/textbox.h
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 290b211c115..8906e9f5fa3 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -50,7 +50,8 @@ Textbox::Textbox() :
_needsTextRedraw(false),
_scrollbar(nullptr),
_scrollbarPos(0),
- _numLines(0) {}
+ _numLines(0),
+ _lastResponseisMultiline(false) {}
Textbox::~Textbox() {
delete _scrollbar;
@@ -79,7 +80,7 @@ void Textbox::init() {
uint16 scrollbarMaxScroll = chunk->readUint16LE();
_firstLineOffset = chunk->readUint16LE() + 1;
- _lineHeight = chunk->readUint16LE();
+ _lineHeight = chunk->readUint16LE() + (g_nancy->getGameType() == Nancy::GameType::kGameTypeVampire ? 1 : 0);
_borderWidth = chunk->readUint16LE() - 1;
_maxWidthDifference = chunk->readUint16LE();
@@ -146,7 +147,7 @@ void Textbox::drawTextbox() {
const Font *font = g_nancy->_graphicsManager->getFont(_fontID);
uint maxWidth = _fullSurface.w - _maxWidthDifference - _borderWidth - 2;
- uint lineDist = _lineHeight + _lineHeight / 4 + (g_nancy->getGameType() == kGameTypeVampire ? 1 : 0);
+ uint lineDist = _lineHeight + _lineHeight / 4;
for (uint lineID = 0; lineID < _textLines.size(); ++lineID) {
Common::String currentLine = _textLines[lineID];
@@ -197,6 +198,7 @@ void Textbox::drawTextbox() {
}
String currentSubLine;
+ _lastResponseisMultiline = false;
uint32 nextTabPos = currentLine.find(_tabToken);
if (nextTabPos != String::npos) {
@@ -245,6 +247,7 @@ void Textbox::drawTextbox() {
// a single line gets a double newline afterwards
if (wrappedLines.size() > 1 && hasHotspot) {
++_numLines;
+ _lastResponseisMultiline = true;
}
horizontalOffset = 0;
@@ -321,8 +324,13 @@ void Textbox::onScrollbarMove() {
}
uint16 Textbox::getInnerHeight() const {
+ // These calculations are _almost_ correct, but off by a pixel sometimes
uint lineDist = _lineHeight + _lineHeight / 4;
- return _numLines * lineDist + _firstLineOffset + lineDist / 2 - 1;
+ if (g_nancy->getGameType() == kGameTypeVampire) {
+ return _numLines * lineDist + _firstLineOffset + (_lastResponseisMultiline ? - _lineHeight / 2 : 1);
+ } else {
+ return _numLines * lineDist + _firstLineOffset + lineDist / 2 - 1;
+ }
}
} // End of namespace UI
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index 690db4d5572..9d81c87423e 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -75,6 +75,8 @@ private:
uint16 _numLines;
uint16 _fontID;
+ bool _lastResponseisMultiline;
+
bool _needsTextRedraw;
float _scrollbarPos;
Commit: 2407996d350f4ef89cea07b899491a1d65fd1c97
https://github.com/scummvm/scummvm/commit/2407996d350f4ef89cea07b899491a1d65fd1c97
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:00+02:00
Commit Message:
NANCY: Add missing #include
Changed paths:
engines/nancy/input.h
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 3c08fd0e0a1..229d8d39065 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -22,6 +22,8 @@
#ifndef NANCY_INPUT_H
#define NANCY_INPUT_H
+#include "engines/nancy/commontypes.h"
+
#include "common/rect.h"
#include "common/keyboard.h"
Commit: cb29c7d1a3afeef3b660f06481f0cb5b168cfb98
https://github.com/scummvm/scummvm/commit/cb29c7d1a3afeef3b660f06481f0cb5b168cfb98
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:01+02:00
Commit Message:
NANCY: Enable map button in The Vampire Diaries
The map button hotspot in TVD is now read correctly.
Changed paths:
engines/nancy/state/scene.cpp
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 8c5621d0fef..53cd25d33a8 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -609,6 +609,25 @@ void Scene::run() {
// Update the UI elements and handle input
NancyInput input = g_nancy->_input->getInput();
+
+ // Handle invisible map button
+ // We do this first since TVD's map button overlaps the viewport's right hotspot
+ for (uint16 id : g_nancy->getStaticData().mapAccessSceneIDs) {
+ if ((int)_sceneState.currentScene.sceneID == id) {
+ if (_mapHotspot.contains(input.mousePos)) {
+ g_nancy->_cursorManager->setCursorType(g_nancy->getGameType() == kGameTypeVampire ? CursorManager::kHotspot : CursorManager::kHotspotArrow);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ requestStateChange(NancyState::kMap);
+ }
+
+ input.eatMouseInput();
+ }
+
+ break;
+ }
+ }
+
_viewport.handleInput(input);
_sceneState.currentScene.verticalOffset = _viewport.getCurVerticalScroll();
@@ -636,55 +655,59 @@ void Scene::run() {
requestStateChange(NancyState::kHelp);
}
- // Handle invisible map button
- for (uint16 id : g_nancy->getStaticData().mapAccessSceneIDs) {
- if ((int)_sceneState.currentScene.sceneID == id) {
- if (_mapHotspot.contains(input.mousePos)) {
- g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
-
- if (input.input & NancyInput::kLeftMouseButtonUp) {
- requestStateChange(NancyState::kMap);
- }
- }
-
- break;
- }
- }
-
_actionManager.processActionRecords();
}
void Scene::initStaticData() {
- Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
- chunk->seek(0x8A);
- readRect(*chunk, _mapHotspot);
+ Common::SeekableReadStream *chunk;
chunk = g_nancy->getBootChunkStream("FR0");
chunk->seek(0);
+ if (chunk) {
+ _frame.init(chunk->readString());
+ }
- _frame.init(chunk->readString());
_viewport.init();
_textbox.init();
_inventoryBox.init();
- // Init menu and help buttons
+ // Init buttons
chunk = g_nancy->getBootChunkStream("BSUM");
- chunk->seek(0);
- Common::Serializer ser(chunk, nullptr);
- ser.setVersion(g_nancy->getGameType());
- ser.skip(0x176, kGameTypeVampire, kGameTypeVampire);
- ser.skip(0x184, kGameTypeNancy1);
- Common::Rect menuSrc, helpSrc, menuDest, helpDest;
- readRect(*chunk, menuSrc);
- readRect(*chunk, helpSrc);
- readRect(*chunk, menuDest);
- readRect(*chunk, helpDest);
- _menuButton = new UI::Button(5, g_nancy->_graphicsManager->_object0, menuSrc, menuDest);
- _helpButton = new UI::Button(5, g_nancy->_graphicsManager->_object0, helpSrc, helpDest);
- _menuButton->init();
- _helpButton->init();
- g_nancy->_cursorManager->showCursor(true);
+ if (chunk) {
+ chunk->seek(0);
+ Common::Serializer ser(chunk, nullptr);
+ ser.setVersion(g_nancy->getGameType());
+
+ // TVD checks if the _entire_ cursor is within the bounds of the hotspot,
+ // which results in the actual hotspot being about a quarter of the size
+ // it should be. This is stupid so we sacrifice some accuracy and ignore it.
+ ser.skip(0x136, kGameTypeVampire, kGameTypeVampire);
+ if (ser.getVersion() == kGameTypeVampire) {
+ readRect(*chunk, _mapHotspot);
+ }
+ ser.skip(0x30, kGameTypeVampire, kGameTypeVampire);
+ ser.skip(0x184, kGameTypeNancy1);
+ Common::Rect menuSrc, helpSrc, menuDest, helpDest;
+ readRect(*chunk, menuSrc);
+ readRect(*chunk, helpSrc);
+ readRect(*chunk, menuDest);
+ readRect(*chunk, helpDest);
+ _menuButton = new UI::Button(5, g_nancy->_graphicsManager->_object0, menuSrc, menuDest);
+ _helpButton = new UI::Button(5, g_nancy->_graphicsManager->_object0, helpSrc, helpDest);
+ _menuButton->init();
+ _helpButton->init();
+ g_nancy->_cursorManager->showCursor(true);
+ }
+
+ if (g_nancy->getGameType() == kGameTypeNancy1) {
+ chunk = g_nancy->getBootChunkStream("MAP");
+ if (chunk) {
+ chunk->seek(0x8A);
+ readRect(*chunk, _mapHotspot);
+ }
+ }
+
// Init ornaments (TVD only)
if (g_nancy->getGameType() == kGameTypeVampire) {
_viewportOrnaments = new UI::ViewportOrnaments(9);
Commit: 9988b3ba43e10edfbac54f5172ae9dee571d9bbf
https://github.com/scummvm/scummvm/commit/9988b3ba43e10edfbac54f5172ae9dee571d9bbf
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:01+02:00
Commit Message:
NANCY: Implement map in The Vampire Diaries
Changed paths:
A engines/nancy/ui/animatedbutton.cpp
A engines/nancy/ui/animatedbutton.h
engines/nancy/commontypes.h
engines/nancy/module.mk
engines/nancy/state/map.cpp
engines/nancy/state/map.h
engines/nancy/state/scene.cpp
engines/nancy/state/scene.h
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 5ff51d7aefd..6996e231be6 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -75,6 +75,11 @@ static const byte kLoadSceneSound = 0;
static const byte kAbsoluteClockBump = 1;
static const byte kRelativeClockBump = 2;
+// Time of day
+static const byte kPlayerDay = 0;
+static const byte kPlayerNight = 1;
+static const byte kPlayerDuskDawn = 2;
+
enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
// Separate namespace to remove possible clashes
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index dbf85866dde..9fa007842f2 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -17,6 +17,7 @@ MODULE_OBJS = \
action/staticbitmapanim.o \
action/telephone.o \
ui/fullscreenimage.o \
+ ui/animatedbutton.o \
ui/button.o \
ui/inventorybox.o \
ui/ornaments.o \
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 1ec44180e40..41d7c7c6ec3 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -33,6 +33,16 @@
namespace Common {
DECLARE_SINGLETON(Nancy::State::Map);
+
+template<>
+Nancy::State::Map *Singleton<Nancy::State::Map>::makeInstance() {
+ if (Nancy::g_nancy->getGameType() == Nancy::kGameTypeVampire) {
+ return new Nancy::State::TVDMap();
+ } else {
+ return new Nancy::State::Nancy1Map();
+ }
+}
+
}
namespace Nancy {
@@ -40,16 +50,10 @@ namespace State {
Map::Map() : _state(kInit),
_mapID(0),
- _mapButtonClicked(false),
_pickedLocationID(-1),
- _viewport(),
_label(7),
_closedLabel(7),
- _button(nullptr) {}
-
-Map::~Map() {
- delete _button;
-}
+ _background(0) {}
void Map::process() {
switch (_state) {
@@ -59,113 +63,434 @@ void Map::process() {
case kRun:
run();
break;
+ case kExit:
+ g_nancy->setState(NancyState::kScene);
+ break;
}
}
+void Map::onStateExit() {
+ g_nancy->_sound->stopSound(_sound);
+
+ if (_pickedLocationID != -1) {
+ auto &loc = _locations[_pickedLocationID];
+ NancySceneState.changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
+
+ g_nancy->_sound->playSound("BUOK");
+ }
+
+ // The two sounds play at the same time if a location was picked
+ g_nancy->_sound->playSound("GLOB");
+ g_nancy->_graphicsManager->clearObjects();
+
+ destroy();
+}
+
void Map::init() {
- Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
- Common::Rect textboxScreenPosition = NancySceneState.getTextbox().getScreenPosition();
+ // Get a screenshot of the Scene state and set it as the background
+ // to allow the labels to clear when not hovered
+ const Graphics::ManagedSurface *screen = g_nancy->_graphicsManager->getScreen();
+ _background._drawSurface.create(screen->w, screen->h, screen->format);
+ _background._drawSurface.blitFrom(*screen);
+ _background.moveTo(_background._drawSurface.getBounds());
+ _background.setVisible(true);
+}
+
+void Map::registerGraphics() {
+ _background.registerGraphics();
+ _viewport.registerGraphics();
+ _label.registerGraphics();
+ _closedLabel.registerGraphics();
+}
+
+void Map::setLabel(int labelID) {
+ if (labelID == -1) {
+ _label.setVisible(false);
+ _closedLabel.setVisible(false);
+ } else {
+ _label.moveTo(_locations[labelID].labelDest);
+ _label._drawSurface.create(g_nancy->_graphicsManager->_object0, _locations[labelID].labelSrc);
+ _label.setVisible(true);
+ _label.setTransparent(true);
+
+ if (!_locations[labelID].isActive) {
+ _closedLabel.setVisible(true);
+ }
+ }
+}
+
+void Map::MapViewport::init() {
+ Common::SeekableReadStream *viewChunk = g_nancy->getBootChunkStream("VIEW");
+
+ if (viewChunk) {
+ viewChunk->seek(0);
+ Common::Rect dest;
+ readRect(*viewChunk, dest);
+ moveTo(dest);
+
+ _drawSurface.create(dest.width(), dest.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+ }
+
+ RenderObject::init();
+}
+
+void Map::MapViewport::updateGraphics() {
+ // TODO: One frame plays for longer than it should
+ if (_decoder.getFrameCount() > 1) {
+ if (_decoder.atEnd()) {
+ _decoder.rewind();
+ }
+
+ if (_decoder.needsUpdate()) {
+ GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, g_nancy->getGameType() == kGameTypeVampire);
+ _needsRedraw = true;
+ }
+ }
+}
+
+void Map::MapViewport::loadVideo(const Common::String &filename, const Common::String &palette) {
+ if (_decoder.isVideoLoaded()) {
+ _decoder.close();
+ }
+
+ if (!_decoder.loadFile(filename + ".avf")) {
+ error("Couldn't load video file %s", filename.c_str());
+ }
+
+ if (palette.size()) {
+ setPalette(palette);
+ }
+
+ GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, palette.size());
+ _needsRedraw = true;
+}
+
+TVDMap::TVDMap() : _ornaments(7), _globe(8, this) {}
+void TVDMap::init() {
+ Map::init();
_viewport.init();
_label.init();
+ _ornaments.init();
+ _globe.init();
- Common::Rect buttonSrc, buttonDest;
- chunk->seek(0x7A, SEEK_SET);
- readRect(*chunk, buttonSrc);
- readRect(*chunk, buttonDest);
+ Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
+ Common::Rect textboxScreenPosition = NancySceneState.getTextbox().getScreenPosition();
- chunk->seek(0xDA, SEEK_SET);
- Common::Rect closedLabelSrc;
- readRect(*chunk, closedLabelSrc);
+ if (chunk) {
+ chunk->seek(0x386);
+ _cursorPosition.x = chunk->readUint16LE();
+ _cursorPosition.y = chunk->readUint16LE();
- _closedLabel._drawSurface.create(g_nancy->_graphicsManager->_object0, closedLabelSrc);
+ // Determine which version of the map will be shown
+ if (NancySceneState.getEventFlag(52, kEvOccurred)) {
+ _mapID = 3; // Storm
+ //
+ } else {
+ // Determine map based on the in-game time
+ Time playerTime = NancySceneState.getPlayerTime();
+ uint hours = playerTime.getHours();
+ if (hours >= 7 && hours < 18) {
+ _mapID = 0; // Day
+ } else if (hours >= 19 || hours < 6) {
+ _mapID = 1; // Night
+ } else {
+ _mapID = 2; // Dusk/dawn
+ }
+ }
- Common::Rect newScreenRect;
+ chunk->seek(0x1E6);
+ Common::Rect closedLabelSrc;
+ readRect(*chunk, closedLabelSrc);
+
+ _closedLabel._drawSurface.create(g_nancy->_graphicsManager->_object0, closedLabelSrc);
+
+ Common::Rect newScreenRect;
+
+ newScreenRect.left = textboxScreenPosition.left + ((textboxScreenPosition.width() - closedLabelSrc.width()) / 2);
+ newScreenRect.right = newScreenRect.left + closedLabelSrc.width();
+ newScreenRect.bottom = textboxScreenPosition.bottom - 10;
+ newScreenRect.top = newScreenRect.bottom - closedLabelSrc.height();
+
+ _closedLabel.moveTo(newScreenRect);
+ _closedLabel.setTransparent(true);
+
+ // Load the video
+ chunk->seek(_mapID * 10);
+ Common::String videoName, paletteName;
+ readFilename(*chunk, videoName);
+ chunk->seek(40 + _mapID * 10);
+ readFilename(*chunk, paletteName);
+ _viewport.loadVideo(videoName, paletteName);
+
+ // Load the audio
+ chunk->seek(0x54 + _mapID * 0x20);
+ _sound.read(*chunk, SoundDescription::kMenu);
+ g_nancy->_sound->loadSound(_sound);
+ g_nancy->_sound->playSound("GLOB");
+
+ char buf[30];
+ _locations.resize(7);
+ for (uint i = 0; i < 7; ++i) {
+ Location &loc = _locations[i];
+
+ chunk->seek(0x226 + i * 30);
+ chunk->read(buf, 30);
+ buf[29] = '\0';
+ loc.description = buf;
+
+ chunk->seek(0x2F8 + i * 16);
+ readRect(*chunk, loc.hotspot);
+
+ loc.scenes.resize(2);
+ for (uint j = 0; j < 2; ++j) {
+ Location::SceneChange &sc = loc.scenes[j];
+ chunk->seek(0x38A + (8 * i) + (56 * j));
+ sc.sceneID = chunk->readUint16LE();
+ sc.frameID = chunk->readUint16LE();
+ sc.verticalOffset = chunk->readUint16LE();
+ sc.paletteID = chunk->readUint16LE();
+ }
- newScreenRect.left = textboxScreenPosition.left + ((textboxScreenPosition.width() - closedLabelSrc.width()) / 2);
- newScreenRect.right = newScreenRect.left + closedLabelSrc.width() - 1;
- newScreenRect.bottom = textboxScreenPosition.bottom - 11;
- newScreenRect.top = newScreenRect.bottom - closedLabelSrc.height() + 1;
+ chunk->seek(0x186 + i * 16);
+ readRect(*chunk, loc.labelSrc);
- _closedLabel.moveTo(newScreenRect);
+ Common::Rect closedScreenRect = _closedLabel.getScreenPosition();
- setLabel(-1);
+ loc.labelDest.left = textboxScreenPosition.left + ((textboxScreenPosition.width() - loc.labelSrc.width()) / 2);
+ loc.labelDest.right = loc.labelDest.left + loc.labelSrc.width();
+ loc.labelDest.bottom = closedScreenRect.bottom - ((closedScreenRect.bottom - loc.labelSrc.height() - textboxScreenPosition.top) / 2) - 10;
+ loc.labelDest.top = loc.labelDest.bottom - loc.labelSrc.height();
- _button = new UI::Button(9, g_nancy->_graphicsManager->_object0, buttonSrc, buttonDest);
- _button->init();
- _button->setVisible(true);
+ loc.isActive = true;
+ }
- if (NancySceneState.getEventFlag(40, kEvOccurred) && // Has set up sting
- NancySceneState.getEventFlag(95, kEvOccurred)) { // Connie chickens
- _mapID = 1;
- } else {
+ g_nancy->_cursorManager->setCursorItemID(-1);
+ }
+
+ _viewport.setVisible(false);
+ _globe.setOpen(true);
+ _globe.setVisible(true);
+
+ registerGraphics();
+ _state = kRun;
+}
+
+void TVDMap::onStateExit() {
+ g_nancy->_sound->stopSound(_sound);
+
+ if (NancySceneState.getPlayerTOD() == kPlayerDay) {
_mapID = 0;
+ } else {
+ _mapID = 1;
}
- // Load the video
- chunk->seek(_mapID * 10, SEEK_SET);
- Common::String videoName;
- readFilename(*chunk, videoName);
+ Map::onStateExit();
+}
- _viewport.loadVideo(videoName, 0, 0);
- _viewport.disableEdges(kLeft | kRight | kUp | kDown);
+void TVDMap::run() {
+ if (!g_nancy->_sound->isSoundPlaying("GLOB") && !g_nancy->_sound->isSoundPlaying(_sound)) {
+ g_nancy->_sound->playSound(_sound);
+ }
- // Load the audio
- chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
- _sound.read(*chunk, SoundDescription::kMenu);
- g_nancy->_sound->loadSound(_sound);
- g_nancy->_sound->playSound("GLOB");
+ setLabel(-1);
+ g_nancy->_cursorManager->setCursorType(CursorManager::kNormal);
- _locations.clear();
+ if (!_globe.isPlaying()) {
+ NancyInput input = g_nancy->_input->getInput();
- _locations.reserve(4);
- char buf[30];
- for (uint i = 0; i < 4; ++i) {
- _locations.push_back(Location());
- Location &loc = _locations.back();
+ _globe.handleInput(input);
- chunk->seek(0xEA + i * 16, SEEK_SET);
- chunk->read(buf, 30);
- buf[29] = '\0';
- loc.description = buf;
+ for (uint i = 0; i < _locations.size(); ++i) {
+ auto &loc = _locations[i];
+ if (_viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
+ setLabel(i);
- chunk->seek(0x162 + i * 16, SEEK_SET);
- readRect(*chunk, loc.hotspot);
+ if (loc.isActive){
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
- if (_mapID == 1 && (i % 2) != 0) {
- loc.isActive = false;
- } else {
- loc.isActive = true;
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ _pickedLocationID = i;
+ _globe.setOpen(false);
+ }
+ }
+
+ return;
+ }
}
+ }
+}
- loc.scenes.reserve(2);
- for (uint j = 0; j < 2; ++j) {
- loc.scenes.push_back(Location::SceneChange());
- Location::SceneChange &sc = loc.scenes[j];
- chunk->seek(0x1BE + 6 * i + j * 24, SEEK_SET);
- sc.sceneID = chunk->readUint16LE();
- sc.frameID = chunk->readUint16LE();
- sc.verticalOffset = chunk->readUint16LE();
+void TVDMap::registerGraphics() {
+ Map::registerGraphics();
+ _ornaments.registerGraphics();
+ _globe.registerGraphics();
+}
+
+void TVDMap::MapGlobe::init() {
+ Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
+
+ if (chunk) {
+ chunk->seek(0xf4);
+
+ _frameTime = chunk->readUint16LE();
+
+ _srcRects.resize(8);
+ for (uint i = 0; i < 8; ++i) {
+ readRect(*chunk, _srcRects[i]);
}
+
+ Common::Rect screenDest;
+ readRect(*chunk, screenDest);
+ moveTo(screenDest);
+
+ Common::Rect gargoyleSrc, gargoyleDest;
+ chunk->skip(0x80);
+ readRect(*chunk, gargoyleSrc);
+ readRect(*chunk, gargoyleDest);
+
+ _gargoyleEyes._drawSurface.create(g_nancy->_graphicsManager->_object0, gargoyleSrc);
+ _gargoyleEyes.moveTo(gargoyleDest);
+ _gargoyleEyes.setTransparent(true);
+ _gargoyleEyes.setVisible(false);
+
+ _alwaysHighlightCursor = false;
+ _sound = "GLOB";
+ _hotspot = _screenPosition;
+ }
+
+ AnimatedButton::init();
+}
- chunk->seek(0x9A + i * 16, SEEK_SET);
- readRect(*chunk, loc.labelSrc);
+void TVDMap::MapGlobe::registerGraphics() {
+ AnimatedButton::registerGraphics();
+ _gargoyleEyes.registerGraphics();
+}
- Common::Rect closedScreenRect = _closedLabel.getScreenPosition();
+void TVDMap::MapGlobe::onClick() {
+ _gargoyleEyes.setVisible(false);
+}
- loc.labelDest.left = textboxScreenPosition.left + ((textboxScreenPosition.width() - loc.labelSrc.width()) / 2);
- loc.labelDest.right = loc.labelDest.left + loc.labelSrc.width() - 1;
- loc.labelDest.bottom = closedScreenRect.bottom - ((closedScreenRect.bottom - loc.labelSrc.height() - textboxScreenPosition.top) / 2) - 11;
- loc.labelDest.top = loc.labelDest.bottom - loc.labelSrc.height() + 1;
+void TVDMap::MapGlobe::onTrigger() {
+ if (_isOpen) {
+ _gargoyleEyes.setVisible(true);
+ _owner->_viewport.setVisible(true);
+ _owner->_viewport.playVideo();
+ g_system->warpMouse(_owner->_cursorPosition.x, _owner->_cursorPosition.y);
+ g_nancy->_cursorManager->showCursor(true);
+ } else {
+ _owner->_state = kExit;
}
+}
- registerGraphics();
- g_nancy->_cursorManager->setCursorItemID(-1);
+Nancy1Map::Nancy1Map() : _button(nullptr), _mapButtonClicked(false) {}
+
+Nancy1Map::~Nancy1Map() {
+ delete _button;
+}
+
+void Nancy1Map::init() {
+ Map::init();
+ _viewport.init();
+ _label.init();
+
+ Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
+ Common::Rect textboxScreenPosition = NancySceneState.getTextbox().getScreenPosition();
+
+ if (chunk) {
+ Common::Rect buttonSrc, buttonDest;
+ chunk->seek(0x7A, SEEK_SET);
+ readRect(*chunk, buttonSrc);
+ readRect(*chunk, buttonDest);
+
+ chunk->seek(0xDA, SEEK_SET);
+ Common::Rect closedLabelSrc;
+ readRect(*chunk, closedLabelSrc);
+
+ _closedLabel._drawSurface.create(g_nancy->_graphicsManager->_object0, closedLabelSrc);
+
+ Common::Rect newScreenRect;
+
+ newScreenRect.left = textboxScreenPosition.left + ((textboxScreenPosition.width() - closedLabelSrc.width()) / 2);
+ newScreenRect.right = newScreenRect.left + closedLabelSrc.width() - 1;
+ newScreenRect.bottom = textboxScreenPosition.bottom - 11;
+ newScreenRect.top = newScreenRect.bottom - closedLabelSrc.height() + 1;
+
+ _closedLabel.moveTo(newScreenRect);
+
+ setLabel(-1);
+
+ _button = new UI::Button(9, g_nancy->_graphicsManager->_object0, buttonSrc, buttonDest);
+ _button->init();
+ _button->setVisible(true);
+
+ // Determine which version of the map will be shown
+ if (NancySceneState.getEventFlag(40, kEvOccurred) && // Has set up sting
+ NancySceneState.getEventFlag(95, kEvOccurred)) { // Connie chickens
+ _mapID = 1; // Day
+ } else {
+ _mapID = 0; // Night
+ }
+
+ // Load the video
+ chunk->seek(_mapID * 10);
+ Common::String videoName;
+ readFilename(*chunk, videoName);
+ _viewport.loadVideo(videoName);
+
+ // Load the audio
+ chunk->seek(0x18 + _mapID * 0x20);
+ _sound.read(*chunk, SoundDescription::kMenu);
+ g_nancy->_sound->loadSound(_sound);
+ g_nancy->_sound->playSound("GLOB");
+
+ char buf[30];
+ _locations.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ Location &loc = _locations[i];
+
+ chunk->seek(0xEA + i * 16);
+ chunk->read(buf, 30);
+ buf[29] = '\0';
+ loc.description = buf;
+
+ chunk->seek(0x162 + i * 16);
+ readRect(*chunk, loc.hotspot);
+
+ if (_mapID == 1 && (i % 2) != 0) {
+ loc.isActive = false;
+ } else {
+ loc.isActive = true;
+ }
+ loc.scenes.resize(2);
+ for (uint j = 0; j < 2; ++j) {
+ Location::SceneChange &sc = loc.scenes[j];
+ chunk->seek(0x1BE + (6 * i) + (24 * j));
+ sc.sceneID = chunk->readUint16LE();
+ sc.frameID = chunk->readUint16LE();
+ sc.verticalOffset = chunk->readUint16LE();
+ }
+
+ chunk->seek(0x9A + i * 16);
+ readRect(*chunk, loc.labelSrc);
+
+ Common::Rect closedScreenRect = _closedLabel.getScreenPosition();
+
+ loc.labelDest.left = textboxScreenPosition.left + ((textboxScreenPosition.width() - loc.labelSrc.width()) / 2);
+ loc.labelDest.right = loc.labelDest.left + loc.labelSrc.width() - 1;
+ loc.labelDest.bottom = closedScreenRect.bottom - ((closedScreenRect.bottom - loc.labelSrc.height() - textboxScreenPosition.top) / 2) - 11;
+ loc.labelDest.top = loc.labelDest.bottom - loc.labelSrc.height() + 1;
+ }
+
+ g_nancy->_cursorManager->setCursorItemID(-1);
+ }
+
+ registerGraphics();
_state = kRun;
}
-void Map::run() {
+void Nancy1Map::run() {
if (!g_nancy->_sound->isSoundPlaying("GLOB") && !g_nancy->_sound->isSoundPlaying(_sound)) {
g_nancy->_sound->playSound(_sound);
}
@@ -178,11 +503,11 @@ void Map::run() {
if (_button->_isClicked) {
_button->_isClicked = false;
- g_nancy->setState(NancyState::kScene);
+ _state = kExit;
return;
}
- for (uint i = 0; i < 4; ++i) {
+ for (uint i = 0; i < _locations.size(); ++i) {
auto &loc = _locations[i];
if (_viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
setLabel(i);
@@ -192,7 +517,7 @@ void Map::run() {
if (input.input & NancyInput::kLeftMouseButtonUp) {
_pickedLocationID = i;
- g_nancy->setState(NancyState::kScene);
+ _state = kExit;
}
}
@@ -201,46 +526,10 @@ void Map::run() {
}
}
-void Map::onStateExit() {
- g_nancy->_sound->stopSound(_sound);
-
- if (_pickedLocationID != -1) {
- auto &loc = _locations[_pickedLocationID];
- NancySceneState.changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
- _pickedLocationID = -1;
-
- g_nancy->_sound->playSound("BUOK");
- }
-
- // The two sounds play at the same time if a location was picked
- g_nancy->_sound->playSound("GLOB");
-
- _mapButtonClicked = false;
-
- destroy();
-}
-
-void Map::registerGraphics() {
- _viewport.registerGraphics();
- _label.registerGraphics();
- _closedLabel.registerGraphics();
+void Nancy1Map::registerGraphics() {
+ Map::registerGraphics();
_button->registerGraphics();
}
-void Map::setLabel(int labelID) {
- if (labelID == -1) {
- _label.setVisible(false);
- _closedLabel.setVisible(false);
- } else {
- _label.moveTo(_locations[labelID].labelDest);
- _label._drawSurface.create(g_nancy->_graphicsManager->_object0, _locations[labelID].labelSrc);
- _label.setVisible(true);
-
- if (!_locations[labelID].isActive) {
- _closedLabel.setVisible(true);
- }
- }
-}
-
} // End of namespace State
} // End of namespace Nancy
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 0b6b06d2e4d..0c68bbb43ef 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -24,9 +24,13 @@
#include "common/singleton.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/video.h"
+
#include "engines/nancy/state/state.h"
-#include "engines/nancy/ui/viewport.h"
+#include "engines/nancy/ui/animatedbutton.h"
+#include "engines/nancy/ui/ornaments.h"
namespace Nancy {
@@ -40,20 +44,35 @@ namespace State {
class Map : public State, public Common::Singleton<Map> {
public:
- enum State { kInit, kRun };
+ enum State { kInit, kRun, kExit };
Map();
- virtual ~Map();
+ virtual ~Map() = default;
- // State API
void process() override;
void onStateExit() override;
-private:
+protected:
+ class MapViewport : public Nancy::RenderObject {
+ public:
+ MapViewport() : RenderObject(6) {}
+ virtual ~MapViewport() = default;
+
+ void init() override;
+ void updateGraphics() override;
+
+ void loadVideo(const Common::String &filename, const Common::String &palette = Common::String());
+ void playVideo() { _decoder.start(); }
+
+ private:
+ AVFDecoder _decoder;
+ };
+
struct Location {
struct SceneChange {
uint16 sceneID = 0;
uint16 frameID = 0;
uint16 verticalOffset = 0;
+ int16 paletteID = -1;
};
Common::String description;
@@ -66,29 +85,83 @@ private:
Common::Rect labelDest;
};
- void init();
- void run();
-
- void registerGraphics();
+ virtual void init();
+ virtual void run() = 0;
+ virtual void registerGraphics();
void setLabel(int labelID);
- Nancy::UI::Viewport _viewport;
+ MapViewport _viewport;
RenderObject _label;
RenderObject _closedLabel;
- UI::Button *_button;
+ RenderObject _background;
SoundDescription _sound;
+ Common::Point _cursorPosition;
+
State _state;
uint16 _mapID;
- bool _mapButtonClicked;
int16 _pickedLocationID;
Common::Array<Location> _locations;
};
+class TVDMap : public Map {
+ friend class MapGlobe;
+
+public:
+ TVDMap();
+ virtual ~TVDMap() = default;
+
+private:
+ class MapGlobe : public Nancy::UI::AnimatedButton {
+ public:
+ MapGlobe(uint zOrder, TVDMap *owner) : AnimatedButton(zOrder), _gargoyleEyes(zOrder), _owner(owner) {}
+ virtual ~MapGlobe() = default;
+
+ void init() override;
+ void registerGraphics() override;
+ void onClick() override;
+ void onTrigger() override;
+
+ private:
+ TVDMap *_owner;
+ RenderObject _gargoyleEyes;
+ };
+
+ void init() override;
+ void run() override;
+ void registerGraphics() override;
+
+ void onStateExit() override;
+
+ MapGlobe _globe;
+ UI::ViewportOrnaments _ornaments;
+};
+
+class Nancy1Map : public Map {
+public:
+ Nancy1Map();
+ virtual ~Nancy1Map();
+
+private:
+ void init() override;
+ void run() override;
+ void registerGraphics() override;
+
+ UI::Button *_button;
+ bool _mapButtonClicked;
+};
+
#define NancyMapState Nancy::State::Map::instance()
} // End of namespace State
} // End of namespace Nancy
+namespace Common {
+
+template<>
+Nancy::State::Map *Singleton<Nancy::State::Map>::makeInstance();
+
+} // End of namespace Common
+
#endif // NANCY_STATE_MAP_H
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 53cd25d33a8..0cd0525fe20 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -619,6 +619,10 @@ void Scene::run() {
if (input.input & NancyInput::kLeftMouseButtonUp) {
requestStateChange(NancyState::kMap);
+
+ if (g_nancy->getGameType() == kGameTypeVampire) {
+ g_nancy->_cursorManager->showCursor(false);
+ }
}
input.eatMouseInput();
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 75c064622c1..add2203f26f 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -75,10 +75,6 @@ class Scene : public State, public Common::Singleton<Scene> {
friend class Nancy::NancyEngine;
public:
- static const byte kPlayerDay = 0;
- static const byte kPlayerNight = 1;
- static const byte kPlayerDuskDawn = 2;
-
enum GameStateChange : byte {
kHelpMenu = 1 << 0,
kMainMenu = 1 << 1,
@@ -130,6 +126,8 @@ public:
void unpauseSceneSpecificSounds();
void setPlayerTime(Time time, byte relative);
+ Time getPlayerTime() const { return _timers.playerTime; }
+ byte getPlayerTOD() const { return _timers.timeOfDay; }
void addItemToInventory(uint16 id);
void removeItemFromInventory(uint16 id, bool pickUp = true);
diff --git a/engines/nancy/ui/animatedbutton.cpp b/engines/nancy/ui/animatedbutton.cpp
new file mode 100644
index 00000000000..df402342147
--- /dev/null
+++ b/engines/nancy/ui/animatedbutton.cpp
@@ -0,0 +1,109 @@
+/* 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/cursor.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/ui/animatedbutton.h"
+
+namespace Nancy {
+namespace UI {
+
+AnimatedButton::AnimatedButton(uint zOrder) :
+ RenderObject(zOrder),
+ _frameTime(0),
+ _currentFrame(-1),
+ _nextFrameTime(0),
+ _isOpen(false),
+ _alwaysHighlightCursor(false),
+ _isActive(true) {}
+
+void AnimatedButton::init() {
+ setTransparent(true);
+}
+
+void AnimatedButton::updateGraphics() {
+ uint32 currentTime = g_nancy->getTotalPlayTime();
+ if (currentTime > _nextFrameTime) {
+ if (_isOpen && _currentFrame < (int)_srcRects.size()) {
+ setFrame(++_currentFrame);
+ _nextFrameTime = currentTime + _frameTime;
+ setVisible(true);
+ if (_currentFrame == (int)_srcRects.size()) {
+ onTrigger();
+ }
+ } else if (!_isOpen && _currentFrame > -1) {
+ setFrame(--_currentFrame);
+ _nextFrameTime = currentTime + _frameTime;
+ if (_currentFrame == -1) {
+ onTrigger();
+ setVisible(false);
+ }
+ }
+ }
+}
+
+void AnimatedButton::handleInput(NancyInput &input) {
+ if (!_isActive) {
+ return;
+ }
+
+ if (_hotspot.contains(input.mousePos)) {
+ if (_alwaysHighlightCursor || _currentFrame == -1 || _currentFrame == (int)_srcRects.size()) {
+ g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+ }
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ if (_currentFrame == -1) {
+ onClick();
+ setOpen(true);
+ g_nancy->_sound->playSound(_sound);
+ } else if (_currentFrame == (int)_srcRects.size()) {
+ onClick();
+ setOpen(false);
+ g_nancy->_sound->playSound(_sound);
+ }
+ }
+
+ input.eatMouseInput();
+ }
+}
+
+void AnimatedButton::setFrame(int frame) {
+ if (frame > -1 && frame < (int)_srcRects.size()) {
+ _drawSurface.create(g_nancy->_graphicsManager->_object0, _srcRects[frame]);
+ setTransparent(true);
+ _needsRedraw = true;
+ }
+}
+
+bool AnimatedButton::isPlaying() const {
+ return _isOpen ? _currentFrame < (int)_srcRects.size() : _currentFrame > -1;
+}
+
+} // End of namespace UI
+} // End of namespace Nancy
diff --git a/engines/nancy/ui/animatedbutton.h b/engines/nancy/ui/animatedbutton.h
new file mode 100644
index 00000000000..cc8f598635e
--- /dev/null
+++ b/engines/nancy/ui/animatedbutton.h
@@ -0,0 +1,65 @@
+/* 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_UI_ANIMATEDBUTTON_H
+#define NANCY_UI_ANIMATEDBUTTON_H
+
+#include "engines/nancy/renderobject.h"
+
+namespace Nancy {
+
+struct NancyInput;
+
+namespace UI {
+
+class AnimatedButton : public RenderObject {
+public:
+ AnimatedButton(uint zOrder);
+ virtual ~AnimatedButton() = default;
+
+ void init() override;
+ void updateGraphics() override;
+ void handleInput(NancyInput &input);
+
+ void setActive(bool active) { _isActive = active; }
+ void setOpen(bool open) { _isOpen = open; }
+ void setFrame(int frame);
+ bool isPlaying() const;
+ virtual void onClick() = 0;
+ virtual void onTrigger() = 0;
+
+protected:
+ Common::Array<Common::Rect> _srcRects;
+ uint32 _frameTime;
+ Common::String _sound;
+ bool _alwaysHighlightCursor;
+
+ bool _isActive;
+ int _currentFrame;
+ uint32 _nextFrameTime;
+ bool _isOpen;
+ Common::Rect _hotspot;
+};
+
+} // End of namespace UI
+} // End of namespace Nancy
+
+#endif // NANCY_UI_ANIMATEDBUTTON_H
Commit: 87c7cdb577657adc0691dbdfd5cdce7278e3811e
https://github.com/scummvm/scummvm/commit/87c7cdb577657adc0691dbdfd5cdce7278e3811e
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:01+02:00
Commit Message:
NANCY: Don't store time of day
Removed the timeOfDay property, since it is much simpler for
it to be calculated on the fly when needed.
Changed paths:
engines/nancy/action/actionmanager.cpp
engines/nancy/state/scene.cpp
engines/nancy/state/scene.h
engines/nancy/time.h
diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 5e74523b1de..e66e1e343ac 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -288,7 +288,7 @@ void ActionManager::processActionRecords() {
break;
}
case DependencyType::kPlayerTOD:
- if (dep.label == NancySceneState._timers.timeOfDay) {
+ if (dep.label == NancySceneState.getPlayerTOD()) {
dep.satisfied = true;
}
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 0cd0525fe20..854b233c8ce 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -223,6 +223,16 @@ void Scene::setPlayerTime(Time time, byte relative) {
_timers.playerTimeNextMinute = g_nancy->getTotalPlayTime() + g_nancy->_playerTimeMinuteLength;
}
+byte Scene::getPlayerTOD() const {
+ if (_timers.playerTime.getHours() >= 7 && _timers.playerTime.getHours() < 18) {
+ return kPlayerDay;
+ } else if (_timers.playerTime.getHours() >= 19 || _timers.playerTime.getHours() < 6) {
+ return kPlayerNight;
+ } else {
+ return kPlayerDuskDawn;
+ }
+}
+
void Scene::addItemToInventory(uint16 id) {
_flags.items[id] = kInvHolding;
if (_flags.heldItem == id) {
@@ -388,7 +398,7 @@ void Scene::synchronize(Common::Serializer &ser) {
ser.syncAsUint32LE((uint32 &)_timers.pushedPlayTime);
ser.syncAsUint32LE((uint32 &)_timers.timerTime);
ser.syncAsByte(_timers.timerIsActive);
- ser.syncAsByte(_timers.timeOfDay);
+ ser.skip(1); // timeOfDay; To be removed on next savefile version bump
g_nancy->setTotalPlayTime((uint32)_timers.lastTotalTime);
@@ -445,7 +455,6 @@ void Scene::init() {
_timers.timerIsActive = false;
_timers.playerTimeNextMinute = 0;
_timers.pushedPlayTime = 0;
- _timers.timeOfDay = kPlayerDay;
changeScene(g_nancy->_firstScene);
@@ -598,15 +607,6 @@ void Scene::run() {
_timers.playerTimeNextMinute = currentPlayTime + g_nancy->_playerTimeMinuteLength;
}
- // Set the time of day according to playerTime
- if (_timers.playerTime.getHours() >= 7 && _timers.playerTime.getHours() < 18) {
- _timers.timeOfDay = kPlayerDay;
- } else if (_timers.playerTime.getHours() >= 19 || _timers.playerTime.getHours() < 6) {
- _timers.timeOfDay = kPlayerNight;
- } else {
- _timers.timeOfDay = kPlayerDuskDawn;
- }
-
// Update the UI elements and handle input
NancyInput input = g_nancy->_input->getInput();
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index add2203f26f..fa90a7e8674 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -127,7 +127,7 @@ public:
void setPlayerTime(Time time, byte relative);
Time getPlayerTime() const { return _timers.playerTime; }
- byte getPlayerTOD() const { return _timers.timeOfDay; }
+ byte getPlayerTOD() const;
void addItemToInventory(uint16 id);
void removeItemFromInventory(uint16 id, bool pickUp = true);
@@ -212,7 +212,6 @@ private:
bool timerIsActive = false;
Time playerTime; // In-game time of day, adds a minute every 5 seconds
Time playerTimeNextMinute; // Stores the next tick count until we add a minute to playerTime
- byte timeOfDay = kPlayerDay;
};
struct PlayFlags {
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index bfd131e465a..8edbff5e845 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -67,12 +67,12 @@ public:
friend bool operator>= (const Time &l, const uint32 &r) { return !(l < r); }
friend bool operator>= (const uint32 &l, const Time &r) { return !(l < r); }
- uint16 getSeconds() { return (_milliseconds / 1000) % 60; }
- uint16 getMinutes() { return (_milliseconds / 60000) % 60; }
- uint16 getTotalHours() { return _milliseconds / 3600000; }
+ uint16 getSeconds() const { return (_milliseconds / 1000) % 60; }
+ uint16 getMinutes() const { return (_milliseconds / 60000) % 60; }
+ uint16 getTotalHours() const { return _milliseconds / 3600000; }
- uint16 getHours() { return (_milliseconds / 3600000) % 24; } // Used for player time
- uint16 getDays() { return _milliseconds / 86400000; } // up to 49.7 days
+ uint16 getHours() const { return (_milliseconds / 3600000) % 24; } // Used for player time
+ uint16 getDays() const { return _milliseconds / 86400000; } // up to 49.7 days
private:
uint32 _milliseconds;
Commit: 4069c5e38120eabe6e81df79aa07b9a002d11df0
https://github.com/scummvm/scummvm/commit/4069c5e38120eabe6e81df79aa07b9a002d11df0
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:02+02:00
Commit Message:
NANCY: Correctly implement changing palettes
Scenes with multiple palettes will now use the correct palette,
instead of defaulting to the first one.
Changed paths:
engines/nancy/commontypes.cpp
engines/nancy/commontypes.h
engines/nancy/state/map.cpp
engines/nancy/state/map.h
engines/nancy/state/scene.cpp
engines/nancy/state/scene.h
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 88c23344c0a..ec707706506 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -31,7 +31,8 @@ void SceneChangeDescription::readData(Common::SeekableReadStream &stream, bool l
frameID = stream.readUint16LE();
verticalOffset = stream.readUint16LE();
if (longFormat) {
- stream.skip(3);
+ paletteID = stream.readByte();
+ stream.skip(2);
}
continueSceneSound = stream.readUint16LE();
}
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 6996e231be6..40d62138890 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -111,7 +111,9 @@ struct SceneChangeDescription {
uint16 sceneID = 0;
uint16 frameID = 0;
uint16 verticalOffset = 0;
- uint16 continueSceneSound = 0;
+ uint16 continueSceneSound = kLoadSceneSound;
+
+ int8 paletteID = 0; // TVD only
void readData(Common::SeekableReadStream &stream, bool longFormat = false);
};
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 41d7c7c6ec3..ff98e667fb0 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -74,7 +74,7 @@ void Map::onStateExit() {
if (_pickedLocationID != -1) {
auto &loc = _locations[_pickedLocationID];
- NancySceneState.changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
+ NancySceneState.changeScene(loc.scenes[_mapID]);
g_nancy->_sound->playSound("BUOK");
}
@@ -244,7 +244,7 @@ void TVDMap::init() {
loc.scenes.resize(2);
for (uint j = 0; j < 2; ++j) {
- Location::SceneChange &sc = loc.scenes[j];
+ SceneChangeDescription &sc = loc.scenes[j];
chunk->seek(0x38A + (8 * i) + (56 * j));
sc.sceneID = chunk->readUint16LE();
sc.frameID = chunk->readUint16LE();
@@ -465,7 +465,7 @@ void Nancy1Map::init() {
loc.scenes.resize(2);
for (uint j = 0; j < 2; ++j) {
- Location::SceneChange &sc = loc.scenes[j];
+ SceneChangeDescription &sc = loc.scenes[j];
chunk->seek(0x1BE + (6 * i) + (24 * j));
sc.sceneID = chunk->readUint16LE();
sc.frameID = chunk->readUint16LE();
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 0c68bbb43ef..f5ea7a2f420 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -68,18 +68,11 @@ protected:
};
struct Location {
- struct SceneChange {
- uint16 sceneID = 0;
- uint16 frameID = 0;
- uint16 verticalOffset = 0;
- int16 paletteID = -1;
- };
-
Common::String description;
bool isActive = false;
Common::Rect hotspot;
- Common::Array<SceneChange> scenes;
+ Common::Array<SceneChangeDescription> scenes;
Common::Rect labelSrc;
Common::Rect labelDest;
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 854b233c8ce..bb489e749f1 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -171,7 +171,7 @@ void Scene::onStateExit() {
_gameStateRequested = NancyState::kNone;
}
-void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, byte continueSceneSound) {
+void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, byte continueSceneSound, byte paletteID) {
if (id == 9999) {
return;
}
@@ -179,12 +179,17 @@ void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, byte con
_sceneState.nextScene.sceneID = id;
_sceneState.nextScene.frameID = frame;
_sceneState.nextScene.verticalOffset = verticalOffset;
+ _sceneState.nextScene.paletteID = paletteID;
_sceneState.continueSceneSound = continueSceneSound;
_state = kLoad;
}
void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
- changeScene(sceneDescription.sceneID, sceneDescription.frameID, sceneDescription.verticalOffset, sceneDescription.continueSceneSound);
+ changeScene(sceneDescription.sceneID,
+ sceneDescription.frameID,
+ sceneDescription.verticalOffset,
+ sceneDescription.continueSceneSound,
+ sceneDescription.paletteID);
}
void Scene::pushScene() {
@@ -571,7 +576,6 @@ void Scene::load() {
}
_timers.sceneTime = 0;
- _sceneState.nextScene.paletteID = 0;
_state = kStartSound;
}
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index fa90a7e8674..b1e75ec735d 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -117,7 +117,7 @@ public:
void onStateEnter() override;
void onStateExit() override;
- void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, byte continueSceneSound);
+ void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, byte continueSceneSound, byte paletteID = 0);
void changeScene(const SceneChangeDescription &sceneDescription);
void pushScene();
void popScene();
Commit: 85dcfb64d8e1e7f3455893639fd2ea4d76e0574d
https://github.com/scummvm/scummvm/commit/85dcfb64d8e1e7f3455893639fd2ea4d76e0574d
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:03+02:00
Commit Message:
NANCY: Do not clear palette on viewport scroll
Fixed a bug where the background would go black when scrolling up
or down.
Changed paths:
engines/nancy/ui/viewport.cpp
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 5943d49220e..6cd7ae2a442 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -197,6 +197,7 @@ void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint vert
setVerticalScroll(verticalScroll);
if (palette.size()) {
+ GraphicsManager::loadSurfacePalette(_fullFrame, palette);
setPalette(palette);
}
Commit: 18e8ac14f5abe9c3172046d0fbe63670c8ae5d93
https://github.com/scummvm/scummvm/commit/18e8ac14f5abe9c3172046d0fbe63670c8ae5d93
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:03+02:00
Commit Message:
NANCY: Fix secondary movie transparency
Changed SecondaryMovie to always be transparent for The Vampire
Diaries, which fixes the dream sequence at the beginning of the game.
Changed paths:
engines/nancy/action/secondarymovie.cpp
engines/nancy/action/secondarymovie.h
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 57c375957e0..5e0d2d01c58 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -51,7 +51,7 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
ser.skip(0x2); // videoPlaySource
- ser.syncAsUint16LE(_transparency, kGameTypeVampire, kGameTypeVampire);
+ ser.skip(2, kGameTypeVampire, kGameTypeVampire); // smallSize
ser.skip(4, kGameTypeVampire, kGameTypeVampire); // paletteStart, paletteSize
ser.skip(2, kGameTypeVampire, kGameTypeVampire); // hasBitmapOverlaySurface
ser.skip(2, kGameTypeVampire, kGameTypeVampire); // unknown, probably related to playing a sfx
@@ -98,7 +98,7 @@ void PlaySecondaryMovie::init() {
GraphicsManager::loadSurfacePalette(_fullFrame, _paletteName);
}
- if (_transparency == kPlayMovieTransparent) {
+ if (g_nancy->getGameType() == kGameTypeVampire) {
setTransparent(true);
_fullFrame.setTransparentColor(_drawSurface.getTransparentColor());
}
@@ -136,6 +136,7 @@ void PlaySecondaryMovie::updateGraphics() {
GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _fullFrame, _paletteName.size() > 0);
_drawSurface.create(_fullFrame, _fullFrame.getBounds());
moveTo(_videoDescs[descID].destRect);
+
_needsRedraw = true;
for (auto f : _frameFlags) {
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 7c4cb5bded3..79e15f5d40a 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -32,9 +32,6 @@ namespace Action {
class PlaySecondaryMovie : public ActionRecord, public RenderObject {
public:
- static const byte kPlayMoviePlain = 1;
- static const byte kPlayMovieTransparent = 2;
-
static const byte kMovieSceneChange = 5;
static const byte kMovieNoSceneChange = 6;
@@ -61,8 +58,6 @@ public:
Common::String _videoName; // 0x00
Common::String _paletteName;
- //Common::String _bitmapOverlayName
- byte _transparency = kPlayMoviePlain;
uint16 _videoSceneChange = kMovieNoSceneChange; // 0x1C
byte _playerCursorAllowed = kPlayerCursorAllowed; // 0x1E
Commit: 79f3bace94b1004b9614967a840459dab6f3f9f0
https://github.com/scummvm/scummvm/commit/79f3bace94b1004b9614967a840459dab6f3f9f0
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:03+02:00
Commit Message:
NANCY: Fix player time speed in The Vampire Diaries
The value of the length of an in-game minute in The Vampire Diaries
is now read correctly, fixing an issue with time passing way too fast.
Changed paths:
engines/nancy/nancy.cpp
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 18a3604f326..5888b9e927f 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -470,11 +470,12 @@ void NancyEngine::readBootSummary(const IFF &boot) {
ser.skip(0x89, kGameTypeNancy2, kGameTypeNancy3);
ser.syncAsUint16LE(_horizontalEdgesSize);
ser.syncAsUint16LE(_verticalEdgesSize);
- ser.skip(0x1C);
+ ser.skip(0x1A, kGameTypeVampire, kGameTypeVampire);
+ ser.skip(0x1C, kGameTypeNancy1);
int16 time = 0;
ser.syncAsSint16LE(time);
_playerTimeMinuteLength = time;
- ser.skip(2, kGameTypeNancy1, kGameTypeNancy3);
+ ser.skip(2);
ser.syncAsByte(_overrideMovementTimeDeltas);
if (_overrideMovementTimeDeltas) {
Commit: 8470f4deac4d65195e67412d3113aaa9f1f80d16
https://github.com/scummvm/scummvm/commit/8470f4deac4d65195e67412d3113aaa9f1f80d16
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:03+02:00
Commit Message:
NANCY: Play correct sound on button press
Changed the sound that plays when the help and menu buttons are
pressed to the correct one.
Changed paths:
engines/nancy/state/scene.cpp
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index bb489e749f1..1d93bb0e3d9 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -653,13 +653,13 @@ void Scene::run() {
if (_menuButton->_isClicked) {
_menuButton->_isClicked = false;
- g_nancy->_sound->playSound("GLOB");
+ g_nancy->_sound->playSound("BUOK");
requestStateChange(NancyState::kMainMenu);
}
if (_helpButton->_isClicked) {
_helpButton->_isClicked = false;
- g_nancy->_sound->playSound("GLOB");
+ g_nancy->_sound->playSound("BUOK");
requestStateChange(NancyState::kHelp);
}
Commit: 318f84fb90a9e1d5d748a34fee47f7641bbe1c65
https://github.com/scummvm/scummvm/commit/318f84fb90a9e1d5d748a34fee47f7641bbe1c65
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:04+02:00
Commit Message:
NANCY: Clear sound when loading game
Fixed an issue where loading a saved game wouldn't clear all the
previously playing music.
Changed paths:
engines/nancy/state/scene.cpp
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 1d93bb0e3d9..9d526a39e4f 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -351,7 +351,9 @@ void Scene::synchronize(Common::Serializer &ser) {
ser.syncAsUint16LE(_sceneState.nextScene.sceneID);
ser.syncAsUint16LE(_sceneState.nextScene.frameID);
ser.syncAsUint16LE(_sceneState.nextScene.verticalOffset);
- _sceneState.continueSceneSound = kContinueSceneSound;
+ _sceneState.continueSceneSound = kLoadSceneSound;
+
+ g_nancy->_sound->stopAllSounds();
load();
}
Commit: fed8207725e16e5c5ff6f52c48cfa041dff3cf3a
https://github.com/scummvm/scummvm/commit/fed8207725e16e5c5ff6f52c48cfa041dff3cf3a
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:04+02:00
Commit Message:
NANCY: Map improvements
The Map state no longer reads its data from disk every time it needs to
be opened. The Vampire Diaries scenes where the map music keeps
playing are now respected.
Changed paths:
engines/nancy/sound.cpp
engines/nancy/state/map.cpp
engines/nancy/state/map.h
engines/nancy/ui/animatedbutton.cpp
engines/nancy/ui/animatedbutton.h
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 3b2e3d8c752..f835ba7a174 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -30,6 +30,7 @@
#include "engines/nancy/sound.h"
#include "engines/nancy/state/scene.h"
+#include "engines/nancy/state/map.h"
namespace Nancy {
@@ -476,13 +477,19 @@ void SoundManager::calculatePanForAllSounds() {
}
void SoundManager::stopAndUnloadSpecificSounds() {
- // TODO missing if
+ if (g_nancy->getGameType() == kGameTypeVampire && Nancy::State::Map::hasInstance()) {
+ // Don't stop the map sound in certain scenes
+ uint nextScene = NancySceneState.getNextSceneInfo().sceneID;
+ if (nextScene != 0 && (nextScene < 15 || nextScene > 27)) {
+ stopSound(NancyMapState.getSound());
+ }
+ }
for (uint i = 0; i < 10; ++i) {
stopSound(i);
}
- stopSound(_commonSounds["MSND"]);
+ stopSound("MSND");
}
void SoundManager::initSoundChannels() {
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index ff98e667fb0..cd0ad958004 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -60,6 +60,9 @@ void Map::process() {
case kInit:
init();
// fall through
+ case kLoad:
+ load();
+ // fall through
case kRun:
run();
break;
@@ -70,23 +73,12 @@ void Map::process() {
}
void Map::onStateExit() {
- g_nancy->_sound->stopSound(_sound);
-
- if (_pickedLocationID != -1) {
- auto &loc = _locations[_pickedLocationID];
- NancySceneState.changeScene(loc.scenes[_mapID]);
-
- g_nancy->_sound->playSound("BUOK");
- }
-
- // The two sounds play at the same time if a location was picked
- g_nancy->_sound->playSound("GLOB");
g_nancy->_graphicsManager->clearObjects();
-
- destroy();
+ _viewport.unloadVideo();
+ _state = kLoad;
}
-void Map::init() {
+void Map::load() {
// Get a screenshot of the Scene state and set it as the background
// to allow the labels to clear when not hovered
const Graphics::ManagedSurface *screen = g_nancy->_graphicsManager->getScreen();
@@ -168,7 +160,6 @@ void Map::MapViewport::loadVideo(const Common::String &filename, const Common::S
TVDMap::TVDMap() : _ornaments(7), _globe(8, this) {}
void TVDMap::init() {
- Map::init();
_viewport.init();
_label.init();
_ornaments.init();
@@ -178,25 +169,23 @@ void TVDMap::init() {
Common::Rect textboxScreenPosition = NancySceneState.getTextbox().getScreenPosition();
if (chunk) {
- chunk->seek(0x386);
- _cursorPosition.x = chunk->readUint16LE();
- _cursorPosition.y = chunk->readUint16LE();
+ chunk->seek(0);
- // Determine which version of the map will be shown
- if (NancySceneState.getEventFlag(52, kEvOccurred)) {
- _mapID = 3; // Storm
- //
- } else {
- // Determine map based on the in-game time
- Time playerTime = NancySceneState.getPlayerTime();
- uint hours = playerTime.getHours();
- if (hours >= 7 && hours < 18) {
- _mapID = 0; // Day
- } else if (hours >= 19 || hours < 6) {
- _mapID = 1; // Night
- } else {
- _mapID = 2; // Dusk/dawn
- }
+ _mapNames.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ readFilename(*chunk, _mapNames[i]);
+ }
+
+ _mapPalettes.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ readFilename(*chunk, _mapPalettes[i]);
+ }
+
+ chunk->skip(4);
+
+ _mapSounds.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ _mapSounds[i].read(*chunk, SoundDescription::kMenu);
}
chunk->seek(0x1E6);
@@ -214,20 +203,6 @@ void TVDMap::init() {
_closedLabel.moveTo(newScreenRect);
_closedLabel.setTransparent(true);
-
- // Load the video
- chunk->seek(_mapID * 10);
- Common::String videoName, paletteName;
- readFilename(*chunk, videoName);
- chunk->seek(40 + _mapID * 10);
- readFilename(*chunk, paletteName);
- _viewport.loadVideo(videoName, paletteName);
-
- // Load the audio
- chunk->seek(0x54 + _mapID * 0x20);
- _sound.read(*chunk, SoundDescription::kMenu);
- g_nancy->_sound->loadSound(_sound);
- g_nancy->_sound->playSound("GLOB");
char buf[30];
_locations.resize(7);
@@ -265,32 +240,67 @@ void TVDMap::init() {
loc.isActive = true;
}
- g_nancy->_cursorManager->setCursorItemID(-1);
+ chunk->seek(0x386);
+ _cursorPosition.x = chunk->readUint16LE();
+ _cursorPosition.y = chunk->readUint16LE();
+ }
+
+ _state = kLoad;
+}
+
+void TVDMap::load() {
+ Map::load();
+
+ // Determine which version of the map will be shown
+ if (NancySceneState.getEventFlag(52, kEvOccurred)) {
+ _mapID = 3; // Storm
+ //
+ } else {
+ // Determine map based on the in-game time
+ byte timeOfDay = NancySceneState.getPlayerTOD();
+ if (timeOfDay == kPlayerDay) {
+ _mapID = 0; // Day
+ } else if (timeOfDay == kPlayerNight) {
+ _mapID = 1; // Night
+ } else {
+ _mapID = 2; // Dusk/dawn
+ }
}
-
+
+ _viewport.loadVideo(_mapNames[_mapID], _mapPalettes[_mapID]);
+
+ g_nancy->_cursorManager->setCursorItemID(-1);
+
_viewport.setVisible(false);
_globe.setOpen(true);
_globe.setVisible(true);
-
+
+ if (!g_nancy->_sound->isSoundPlaying(getSound())) {
+ g_nancy->_sound->loadSound(getSound());
+ }
+
+ g_nancy->_sound->playSound("GLOB");
+
registerGraphics();
_state = kRun;
}
void TVDMap::onStateExit() {
- g_nancy->_sound->stopSound(_sound);
+ if (_pickedLocationID != -1) {
+ auto &loc = _locations[_pickedLocationID];
+ NancySceneState.changeScene(loc.scenes[NancySceneState.getPlayerTOD() == kPlayerDay ? 0 : 1]);
- if (NancySceneState.getPlayerTOD() == kPlayerDay) {
- _mapID = 0;
+ g_nancy->_sound->playSound("BUOK");
} else {
- _mapID = 1;
+ g_nancy->_sound->stopSound(getSound());
}
Map::onStateExit();
}
void TVDMap::run() {
- if (!g_nancy->_sound->isSoundPlaying("GLOB") && !g_nancy->_sound->isSoundPlaying(_sound)) {
- g_nancy->_sound->playSound(_sound);
+ if (!g_nancy->_sound->isSoundPlaying("GLOB") && !g_nancy->_sound->isSoundPlaying(getSound())) {
+ g_nancy->_sound->playSound(getSound());
}
setLabel(-1);
@@ -312,6 +322,7 @@ void TVDMap::run() {
if (input.input & NancyInput::kLeftMouseButtonUp) {
_pickedLocationID = i;
_globe.setOpen(false);
+ g_nancy->_sound->playSound("GLOB");
}
}
@@ -339,7 +350,7 @@ void TVDMap::MapGlobe::init() {
for (uint i = 0; i < 8; ++i) {
readRect(*chunk, _srcRects[i]);
}
-
+
Common::Rect screenDest;
readRect(*chunk, screenDest);
moveTo(screenDest);
@@ -355,10 +366,9 @@ void TVDMap::MapGlobe::init() {
_gargoyleEyes.setVisible(false);
_alwaysHighlightCursor = false;
- _sound = "GLOB";
_hotspot = _screenPosition;
}
-
+
AnimatedButton::init();
}
@@ -369,6 +379,7 @@ void TVDMap::MapGlobe::registerGraphics() {
void TVDMap::MapGlobe::onClick() {
_gargoyleEyes.setVisible(false);
+ g_nancy->_sound->playSound("GLOB");
}
void TVDMap::MapGlobe::onTrigger() {
@@ -380,6 +391,7 @@ void TVDMap::MapGlobe::onTrigger() {
g_nancy->_cursorManager->showCursor(true);
} else {
_owner->_state = kExit;
+ _nextFrameTime = 0;
}
}
@@ -390,14 +402,27 @@ Nancy1Map::~Nancy1Map() {
}
void Nancy1Map::init() {
- Map::init();
_viewport.init();
_label.init();
-
+
Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
Common::Rect textboxScreenPosition = NancySceneState.getTextbox().getScreenPosition();
if (chunk) {
+ chunk->seek(0);
+
+ _mapNames.resize(2);
+ for (uint i = 0; i < 2; ++i) {
+ readFilename(*chunk, _mapNames[i]);
+ }
+
+ chunk->skip(4);
+
+ _mapSounds.resize(2);
+ for (uint i = 0; i < 2; ++i) {
+ _mapSounds[i].read(*chunk, SoundDescription::kMenu);
+ }
+
Common::Rect buttonSrc, buttonDest;
chunk->seek(0x7A, SEEK_SET);
readRect(*chunk, buttonSrc);
@@ -418,32 +443,10 @@ void Nancy1Map::init() {
_closedLabel.moveTo(newScreenRect);
- setLabel(-1);
-
_button = new UI::Button(9, g_nancy->_graphicsManager->_object0, buttonSrc, buttonDest);
_button->init();
_button->setVisible(true);
- // Determine which version of the map will be shown
- if (NancySceneState.getEventFlag(40, kEvOccurred) && // Has set up sting
- NancySceneState.getEventFlag(95, kEvOccurred)) { // Connie chickens
- _mapID = 1; // Day
- } else {
- _mapID = 0; // Night
- }
-
- // Load the video
- chunk->seek(_mapID * 10);
- Common::String videoName;
- readFilename(*chunk, videoName);
- _viewport.loadVideo(videoName);
-
- // Load the audio
- chunk->seek(0x18 + _mapID * 0x20);
- _sound.read(*chunk, SoundDescription::kMenu);
- g_nancy->_sound->loadSound(_sound);
- g_nancy->_sound->playSound("GLOB");
-
char buf[30];
_locations.resize(4);
for (uint i = 0; i < 4; ++i) {
@@ -457,12 +460,6 @@ void Nancy1Map::init() {
chunk->seek(0x162 + i * 16);
readRect(*chunk, loc.hotspot);
- if (_mapID == 1 && (i % 2) != 0) {
- loc.isActive = false;
- } else {
- loc.isActive = true;
- }
-
loc.scenes.resize(2);
for (uint j = 0; j < 2; ++j) {
SceneChangeDescription &sc = loc.scenes[j];
@@ -481,18 +478,43 @@ void Nancy1Map::init() {
loc.labelDest.right = loc.labelDest.left + loc.labelSrc.width() - 1;
loc.labelDest.bottom = closedScreenRect.bottom - ((closedScreenRect.bottom - loc.labelSrc.height() - textboxScreenPosition.top) / 2) - 11;
loc.labelDest.top = loc.labelDest.bottom - loc.labelSrc.height() + 1;
+
+ loc.isActive = true;
}
+ }
- g_nancy->_cursorManager->setCursorItemID(-1);
+ _state = kLoad;
+}
+
+void Nancy1Map::load() {
+ Map::load();
+
+ // Determine which version of the map will be shown
+ if (NancySceneState.getEventFlag(40, kEvOccurred) && // Has set up sting
+ NancySceneState.getEventFlag(95, kEvOccurred)) { // Connie chickens
+ _mapID = 1; // Night
+
+ _locations[1].isActive = _locations[3].isActive = false;
+ } else {
+ _mapID = 0; // Day
}
-
+
+ _viewport.loadVideo(_mapNames[_mapID]);
+
+ setLabel(-1);
+ g_nancy->_cursorManager->setCursorItemID(-1);
+
+ if (!g_nancy->_sound->isSoundPlaying(getSound())) {
+ g_nancy->_sound->loadSound(getSound());
+ }
+
registerGraphics();
_state = kRun;
}
void Nancy1Map::run() {
- if (!g_nancy->_sound->isSoundPlaying("GLOB") && !g_nancy->_sound->isSoundPlaying(_sound)) {
- g_nancy->_sound->playSound(_sound);
+ if (!g_nancy->_sound->isSoundPlaying("GLOB") && !g_nancy->_sound->isSoundPlaying(getSound())) {
+ g_nancy->_sound->playSound(getSound());
}
NancyInput input = g_nancy->_input->getInput();
@@ -531,5 +553,19 @@ void Nancy1Map::registerGraphics() {
_button->registerGraphics();
}
+void Nancy1Map::onStateExit() {
+ if (_pickedLocationID != -1) {
+ auto &loc = _locations[_pickedLocationID];
+ NancySceneState.changeScene(loc.scenes[_mapID]);
+
+ g_nancy->_sound->playSound("BUOK");
+ }
+
+ g_nancy->_sound->stopSound(getSound());
+ g_nancy->_sound->playSound("GLOB");
+
+ Map::onStateExit();
+}
+
} // End of namespace State
} // End of namespace Nancy
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index f5ea7a2f420..810ac1d9dfb 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -44,12 +44,14 @@ namespace State {
class Map : public State, public Common::Singleton<Map> {
public:
- enum State { kInit, kRun, kExit };
+ enum State { kInit, kLoad, kRun, kExit };
Map();
virtual ~Map() = default;
void process() override;
void onStateExit() override;
+
+ const SoundDescription &getSound() { return _mapSounds[_mapID]; }
protected:
class MapViewport : public Nancy::RenderObject {
@@ -62,6 +64,7 @@ protected:
void loadVideo(const Common::String &filename, const Common::String &palette = Common::String());
void playVideo() { _decoder.start(); }
+ void unloadVideo() { _decoder.close(); }
private:
AVFDecoder _decoder;
@@ -78,19 +81,21 @@ protected:
Common::Rect labelDest;
};
- virtual void init();
+ virtual void init() = 0;
+ virtual void load();
virtual void run() = 0;
virtual void registerGraphics();
void setLabel(int labelID);
+ Common::Array<Common::String> _mapNames;
+ Common::Array<Common::String> _mapPalettes;
+ Common::Array<SoundDescription> _mapSounds;
+
MapViewport _viewport;
RenderObject _label;
RenderObject _closedLabel;
RenderObject _background;
- SoundDescription _sound;
-
- Common::Point _cursorPosition;
State _state;
uint16 _mapID;
@@ -122,6 +127,7 @@ private:
};
void init() override;
+ void load() override;
void run() override;
void registerGraphics() override;
@@ -129,6 +135,8 @@ private:
MapGlobe _globe;
UI::ViewportOrnaments _ornaments;
+
+ Common::Point _cursorPosition;
};
class Nancy1Map : public Map {
@@ -138,9 +146,12 @@ public:
private:
void init() override;
+ void load() override;
void run() override;
void registerGraphics() override;
+ void onStateExit() override;
+
UI::Button *_button;
bool _mapButtonClicked;
};
diff --git a/engines/nancy/ui/animatedbutton.cpp b/engines/nancy/ui/animatedbutton.cpp
index df402342147..7f893978401 100644
--- a/engines/nancy/ui/animatedbutton.cpp
+++ b/engines/nancy/ui/animatedbutton.cpp
@@ -81,11 +81,9 @@ void AnimatedButton::handleInput(NancyInput &input) {
if (_currentFrame == -1) {
onClick();
setOpen(true);
- g_nancy->_sound->playSound(_sound);
} else if (_currentFrame == (int)_srcRects.size()) {
onClick();
setOpen(false);
- g_nancy->_sound->playSound(_sound);
}
}
diff --git a/engines/nancy/ui/animatedbutton.h b/engines/nancy/ui/animatedbutton.h
index cc8f598635e..47c0ef01c20 100644
--- a/engines/nancy/ui/animatedbutton.h
+++ b/engines/nancy/ui/animatedbutton.h
@@ -49,7 +49,6 @@ public:
protected:
Common::Array<Common::Rect> _srcRects;
uint32 _frameTime;
- Common::String _sound;
bool _alwaysHighlightCursor;
bool _isActive;
Commit: 23462c8c83a8df88b52152c026edc56fc2654b77
https://github.com/scummvm/scummvm/commit/23462c8c83a8df88b52152c026edc56fc2654b77
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:05+02:00
Commit Message:
NANCY: Implement clock
Implemented the clock in The Vampire Diaries.
Changed paths:
A engines/nancy/ui/clock.cpp
A engines/nancy/ui/clock.h
engines/nancy/module.mk
engines/nancy/state/scene.cpp
engines/nancy/state/scene.h
engines/nancy/ui/animatedbutton.cpp
engines/nancy/ui/animatedbutton.h
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 9fa007842f2..cbe76bfc3fe 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -19,6 +19,7 @@ MODULE_OBJS = \
ui/fullscreenimage.o \
ui/animatedbutton.o \
ui/button.o \
+ ui/clock.o \
ui/inventorybox.o \
ui/ornaments.o \
ui/scrollbar.o \
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 9d526a39e4f..bbfb85318d9 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -34,6 +34,7 @@
#include "engines/nancy/ui/button.h"
#include "engines/nancy/ui/ornaments.h"
+#include "engines/nancy/ui/clock.h"
namespace Common {
DECLARE_SINGLETON(Nancy::State::Scene);
@@ -104,6 +105,7 @@ Scene::Scene() :
_helpButton(nullptr),
_viewportOrnaments(nullptr),
_textboxOrnaments(nullptr),
+ _clock(nullptr),
_actionManager(),
_difficulty(0),
_activePrimaryVideo(nullptr) {}
@@ -113,6 +115,7 @@ Scene::~Scene() {
delete _menuButton;
delete _viewportOrnaments;
delete _textboxOrnaments;
+ delete _clock;
}
void Scene::process() {
@@ -339,6 +342,10 @@ void Scene::registerGraphics() {
_textboxOrnaments->setVisible(true);
}
+ if (_clock) {
+ _clock->registerGraphics();
+ }
+
_textbox.setVisible(!_shouldClearTextbox);
}
@@ -637,6 +644,11 @@ void Scene::run() {
break;
}
}
+
+ // Handle clock before viewport since it overlaps the left hotspot in TVD
+ if (_clock) {
+ _clock->handleInput(input);
+ }
_viewport.handleInput(input);
@@ -648,21 +660,27 @@ void Scene::run() {
}
_actionManager.handleInput(input);
- _menuButton->handleInput(input);
- _helpButton->handleInput(input);
_textbox.handleInput(input);
_inventoryBox.handleInput(input);
- if (_menuButton->_isClicked) {
- _menuButton->_isClicked = false;
- g_nancy->_sound->playSound("BUOK");
- requestStateChange(NancyState::kMainMenu);
+ if (_menuButton) {
+ _menuButton->handleInput(input);
+
+ if (_menuButton->_isClicked) {
+ _menuButton->_isClicked = false;
+ g_nancy->_sound->playSound("BUOK");
+ requestStateChange(NancyState::kMainMenu);
+ }
}
+
+ if (_helpButton) {
+ _helpButton->handleInput(input);
- if (_helpButton->_isClicked) {
- _helpButton->_isClicked = false;
- g_nancy->_sound->playSound("BUOK");
- requestStateChange(NancyState::kHelp);
+ if (_helpButton->_isClicked) {
+ _helpButton->_isClicked = false;
+ g_nancy->_sound->playSound("BUOK");
+ requestStateChange(NancyState::kHelp);
+ }
}
_actionManager.processActionRecords();
@@ -718,13 +736,16 @@ void Scene::initStaticData() {
}
}
- // Init ornaments (TVD only)
+ // Init ornaments and clock (TVD only)
if (g_nancy->getGameType() == kGameTypeVampire) {
_viewportOrnaments = new UI::ViewportOrnaments(9);
_viewportOrnaments->init();
_textboxOrnaments = new UI::TextboxOrnaments(9);
_textboxOrnaments->init();
+
+ _clock = new UI::Clock();
+ _clock->init();
}
_state = kLoad;
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index b1e75ec735d..ddbb5b4ee86 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -55,6 +55,7 @@ namespace UI {
class Button;
class ViewportOrnaments;
class TextboxOrnaments;
+class Clock;
}
namespace State {
@@ -244,6 +245,7 @@ private:
UI::ViewportOrnaments *_viewportOrnaments;
UI::TextboxOrnaments *_textboxOrnaments;
+ UI::Clock *_clock;
// Data
SceneState _sceneState;
diff --git a/engines/nancy/ui/animatedbutton.cpp b/engines/nancy/ui/animatedbutton.cpp
index 7f893978401..115432daeaa 100644
--- a/engines/nancy/ui/animatedbutton.cpp
+++ b/engines/nancy/ui/animatedbutton.cpp
@@ -39,8 +39,7 @@ AnimatedButton::AnimatedButton(uint zOrder) :
_currentFrame(-1),
_nextFrameTime(0),
_isOpen(false),
- _alwaysHighlightCursor(false),
- _isActive(true) {}
+ _alwaysHighlightCursor(false) {}
void AnimatedButton::init() {
setTransparent(true);
@@ -68,15 +67,11 @@ void AnimatedButton::updateGraphics() {
}
void AnimatedButton::handleInput(NancyInput &input) {
- if (!_isActive) {
- return;
- }
-
if (_hotspot.contains(input.mousePos)) {
if (_alwaysHighlightCursor || _currentFrame == -1 || _currentFrame == (int)_srcRects.size()) {
g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
}
-
+
if (input.input & NancyInput::kLeftMouseButtonUp) {
if (_currentFrame == -1) {
onClick();
diff --git a/engines/nancy/ui/animatedbutton.h b/engines/nancy/ui/animatedbutton.h
index 47c0ef01c20..ff84c9c19ff 100644
--- a/engines/nancy/ui/animatedbutton.h
+++ b/engines/nancy/ui/animatedbutton.h
@@ -39,9 +39,9 @@ public:
void updateGraphics() override;
void handleInput(NancyInput &input);
- void setActive(bool active) { _isActive = active; }
void setOpen(bool open) { _isOpen = open; }
void setFrame(int frame);
+ int getCurrentFrame() const { return _currentFrame; }
bool isPlaying() const;
virtual void onClick() = 0;
virtual void onTrigger() = 0;
@@ -51,7 +51,6 @@ protected:
uint32 _frameTime;
bool _alwaysHighlightCursor;
- bool _isActive;
int _currentFrame;
uint32 _nextFrameTime;
bool _isOpen;
diff --git a/engines/nancy/ui/clock.cpp b/engines/nancy/ui/clock.cpp
new file mode 100644
index 00000000000..e0c0d31ddd4
--- /dev/null
+++ b/engines/nancy/ui/clock.cpp
@@ -0,0 +1,192 @@
+/* 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/cursor.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/time.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/ui/clock.h"
+
+namespace Nancy {
+namespace UI {
+
+Clock::Clock() : RenderObject(11),
+ _globe(10, this),
+ _gargoyleEyes(9) {}
+
+void Clock::init() {
+ Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("CLOK");
+ Graphics::ManagedSurface &object0 = g_nancy->_graphicsManager->_object0;
+
+ _globe.init();
+
+ if (chunk) {
+ chunk->seek(0x80);
+
+ _hoursHandSrcRects.resize(12);
+ for (uint i = 0; i < 12; ++i) {
+ readRect(*chunk, _hoursHandSrcRects[i]);
+ }
+
+ _minutesHandSrcRects.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ readRect(*chunk, _minutesHandSrcRects[i]);
+ }
+
+ Common::Rect dest;
+ readRect(*chunk, dest);
+ _drawSurface.create(dest.width(), dest.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+ moveTo(dest);
+
+ _hoursHandDestRects.resize(12);
+ for (uint i = 0; i < 12; ++i) {
+ readRect(*chunk, _hoursHandDestRects[i]);
+ }
+
+ _minutesHandDestRects.resize(4);
+ for (uint i = 0; i < 4; ++i) {
+ readRect(*chunk, _minutesHandDestRects[i]);
+ }
+
+ Common::Rect gargoyleEyesSrcRect;
+ Common::Rect gargoyleEyesDestRect;
+
+ readRect(*chunk, gargoyleEyesSrcRect);
+ readRect(*chunk, gargoyleEyesDestRect);
+ _gargoyleEyes._drawSurface.create(object0, gargoyleEyesSrcRect);
+ _gargoyleEyes.moveTo(gargoyleEyesDestRect);
+ _gargoyleEyes.setVisible(false);
+
+ _gargoyleEyes.setTransparent(true);
+ _globe.setTransparent(true);
+ GraphicsManager::loadSurfacePalette(_drawSurface, "OBJECT0");
+ setTransparent(true);
+ }
+}
+
+void Clock::registerGraphics() {
+ _gargoyleEyes.registerGraphics();
+ _globe.registerGraphics();
+ RenderObject::registerGraphics();
+}
+
+void Clock::updateGraphics() {
+ setVisible(_globe.getCurrentFrame() >= 5);
+
+ if (_isVisible) {
+ Time newPlayerTime = NancySceneState.getPlayerTime();
+
+ if (newPlayerTime == _playerTime ||
+ newPlayerTime.getMinutes() / 15 != _playerTime.getMinutes() / 15 ||
+ newPlayerTime.getHours() != _playerTime.getHours()) {
+
+ _playerTime = newPlayerTime;
+ drawClockHands();
+ }
+ }
+}
+
+void Clock::handleInput(NancyInput &input) {
+ if (!_globe.isPlaying()) {
+ _globe.handleInput(input);
+ }
+}
+
+void Clock::drawClockHands() {
+ Graphics::ManagedSurface &object0 = g_nancy->_graphicsManager->_object0;
+ uint hours = _playerTime.getHours();
+ if (hours >= 12) {
+ hours -= 12;
+ }
+ uint minutesHand = _playerTime.getMinutes() / 15;
+
+ Common::Rect hoursDest = _hoursHandDestRects[hours];
+ Common::Rect minutesDest = _minutesHandDestRects[minutesHand];
+
+ hoursDest.translate(-_screenPosition.left, -_screenPosition.top);
+ minutesDest.translate(-_screenPosition.left, -_screenPosition.top);
+
+ _drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
+ _drawSurface.blitFrom(object0, _hoursHandSrcRects[hours], hoursDest);
+ _drawSurface.blitFrom(object0, _minutesHandSrcRects[minutesHand], minutesDest);
+}
+
+void Clock::ClockGlobe::init() {
+ Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("CLOK");
+
+ if (chunk) {
+ chunk->seek(0);
+ _srcRects.resize(8);
+ for (uint i = 0; i < 8; ++i) {
+ readRect(*chunk, _srcRects[i]);
+ }
+
+ chunk->seek(0x180);
+ Common::Rect screenDest;
+ readRect(*chunk, screenDest);
+ moveTo(screenDest);
+
+ chunk->seek(0x2B0);
+ _timeToKeepOpen = chunk->readUint32LE();
+ _frameTime = chunk->readUint16LE();
+
+ _alwaysHighlightCursor = true;
+ _hotspot = _screenPosition;
+ }
+}
+
+void Clock::ClockGlobe::updateGraphics() {
+ AnimatedButton::updateGraphics();
+ if (_isOpen && !isPlaying() && g_nancy->getTotalPlayTime() > _closeTime) {
+ setOpen(false);
+ _owner->_gargoyleEyes.setVisible(false);
+ g_nancy->_sound->playSound("GLOB");
+ }
+}
+
+void Clock::ClockGlobe::onClick() {
+ if (!isPlaying()) {
+ setOpen(!_isOpen);
+ if (!_isOpen) {
+ _owner->_gargoyleEyes.setVisible(false);
+ }
+ _owner->_playerTime = NancySceneState.getPlayerTime();
+ g_nancy->_sound->playSound("GLOB");
+ }
+}
+
+void Clock::ClockGlobe::onTrigger() {
+ if (_isOpen) {
+ _closeTime = g_nancy->getTotalPlayTime() + _timeToKeepOpen;
+ _owner->_gargoyleEyes.setVisible(true);
+ } else {
+ _owner->setVisible(false);
+ }
+}
+
+} // End of namespace UI
+} // End of namespace Nancy
diff --git a/engines/nancy/ui/clock.h b/engines/nancy/ui/clock.h
new file mode 100644
index 00000000000..2041b7e5ebb
--- /dev/null
+++ b/engines/nancy/ui/clock.h
@@ -0,0 +1,81 @@
+/* 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_UI_CLOCK_H
+#define NANCY_UI_CLOCK_H
+
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/ui/animatedbutton.h"
+
+namespace Nancy {
+
+struct NancyInput;
+
+namespace UI {
+
+class Clock : public RenderObject {
+ friend class ClockGlobe;
+public:
+ Clock();
+ virtual ~Clock() = default;
+
+ void init() override;
+ void registerGraphics() override;
+ void updateGraphics() override;
+ void handleInput(NancyInput &input);
+
+ void drawClockHands();
+
+protected:
+ class ClockGlobe : public AnimatedButton {
+ public:
+ ClockGlobe(uint zOrder, Clock *owner) : AnimatedButton(zOrder), _owner(owner), _closeTime(0) {}
+ virtual ~ClockGlobe() = default;
+
+ void init() override;
+ void updateGraphics() override;
+ void onClick() override;
+ void onTrigger() override;
+
+ private:
+ Clock *_owner;
+
+ uint32 _closeTime;
+ uint32 _timeToKeepOpen;
+ };
+
+ RenderObject _gargoyleEyes;
+ ClockGlobe _globe;
+
+ Common::Array<Common::Rect> _globeSrcRects;
+ Common::Array<Common::Rect> _hoursHandSrcRects;
+ Common::Array<Common::Rect> _minutesHandSrcRects;
+ Common::Array<Common::Rect> _hoursHandDestRects;
+ Common::Array<Common::Rect> _minutesHandDestRects;
+
+ Time _playerTime;
+};
+
+} // End of namespace UI
+} // End of namespace Nancy
+
+#endif // NANCY_UI_CLOCK_H
Commit: 95e4967b6d7295c9db4a28a19e5b7dd214907d84
https://github.com/scummvm/scummvm/commit/95e4967b6d7295c9db4a28a19e5b7dd214907d84
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:05+02:00
Commit Message:
NANCY: Fix flashing cursor during primary video
Fixed an issue where moving the mouse up into the inactive zone
when a primary video is playing would result in the cursor rapidly
changing. Also separated all of the input handling in Scene into
a dedicated function.
Changed paths:
engines/nancy/action/primaryvideo.cpp
engines/nancy/action/primaryvideo.h
engines/nancy/state/scene.cpp
engines/nancy/state/scene.h
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 96ff392ce6a..660f0cad381 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -382,18 +382,6 @@ void PlayPrimaryVideoChan0::execute() {
}
}
-void PlayPrimaryVideoChan0::handleInput(NancyInput &input) {
- const Common::Rect &inactiveZone = g_nancy->_cursorManager->getPrimaryVideoInactiveZone();
- const Common::Point cursorHotspot = g_nancy->_cursorManager->getCurrentCursorHotspot();
- Common::Point adjustedMousePos = input.mousePos;
- adjustedMousePos.y -= cursorHotspot.y;
-
- if (inactiveZone.bottom > adjustedMousePos.y) {
- input.mousePos.y = inactiveZone.bottom + cursorHotspot.y;
- g_system->warpMouse(input.mousePos.x, input.mousePos.y);
- }
-}
-
void PlayPrimaryVideoChan0::addConditionalDialogue() {
for (const auto &res : g_nancy->getStaticData().conditionalDialogue[_conditionalResponseCharacterID]) {
bool isSatisfied = true;
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index ef2110042ad..0ae0532f1fc 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -84,7 +84,6 @@ public:
void readData(Common::SeekableReadStream &stream) override;
void execute() override;
- void handleInput(NancyInput &input) override;
// Functions for handling the built-in dialogue responses found in the executable
void addConditionalDialogue();
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index bbfb85318d9..60747578177 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -620,9 +620,27 @@ void Scene::run() {
_timers.playerTimeNextMinute = currentPlayTime + g_nancy->_playerTimeMinuteLength;
}
- // Update the UI elements and handle input
+ handleInput();
+
+ _actionManager.processActionRecords();
+}
+
+void Scene::handleInput() {
NancyInput input = g_nancy->_input->getInput();
+ // Warp the mouse below the inactive zone during dialogue scenes
+ if (_activePrimaryVideo != nullptr) {
+ const Common::Rect &inactiveZone = g_nancy->_cursorManager->getPrimaryVideoInactiveZone();
+ const Common::Point cursorHotspot = g_nancy->_cursorManager->getCurrentCursorHotspot();
+ Common::Point adjustedMousePos = input.mousePos;
+ adjustedMousePos.y -= cursorHotspot.y;
+
+ if (inactiveZone.bottom > adjustedMousePos.y) {
+ input.mousePos.y = inactiveZone.bottom + cursorHotspot.y;
+ g_system->warpMouse(input.mousePos.x, input.mousePos.y);
+ }
+ }
+
// Handle invisible map button
// We do this first since TVD's map button overlaps the viewport's right hotspot
for (uint16 id : g_nancy->getStaticData().mapAccessSceneIDs) {
@@ -682,8 +700,6 @@ void Scene::run() {
requestStateChange(NancyState::kHelp);
}
}
-
- _actionManager.processActionRecords();
}
void Scene::initStaticData() {
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index ddbb5b4ee86..4a0c9692c9c 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -183,6 +183,7 @@ private:
void init();
void load();
void run();
+ void handleInput();
void initStaticData();
Commit: d587f77071b19e910e9ea8c96dddd49f4fd06e2e
https://github.com/scummvm/scummvm/commit/d587f77071b19e910e9ea8c96dddd49f4fd06e2e
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-03-15T18:25:06+02:00
Commit Message:
NANCY: Respect palette changes in secondary video
The palette for the scene change in The Vampire Diaries' secondary
videos is now read correctly instead of being ignored.
Changed paths:
engines/nancy/action/secondaryvideo.cpp
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 8050c61e0d0..b0daf1a7b2d 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -28,6 +28,8 @@
#include "engines/nancy/state/scene.h"
+#include "common/serializer.h"
+
namespace Nancy {
namespace Action {
@@ -156,40 +158,30 @@ void PlaySecondaryVideo::handleInput(NancyInput &input) {
}
void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
+ Common::Serializer ser(&stream, nullptr);
+ ser.setVersion(g_nancy->getGameType());
+
readFilename(stream, _filename);
readFilename(stream, _paletteFilename);
- stream.skip(10); // video overlay bitmap filename
-
- if (_paletteFilename.size()) {
- stream.skip(12);
- // videoSource (HD, CD, cache)
- // ??
- // _paletteStart
- // _paletteSize
- // hasOverlayBitmap
- // ignoreHoverAnimation??
- _videoHotspots = stream.readUint16LE();
- }
+ ser.skip(10); // video overlay bitmap filename
- _loopFirstFrame = stream.readUint16LE();
- _loopLastFrame = stream.readUint16LE();
- _onHoverFirstFrame = stream.readUint16LE();
- _onHoverLastFrame = stream.readUint16LE();
- _onHoverEndFirstFrame = stream.readUint16LE();
- _onHoverEndLastFrame = stream.readUint16LE();
+ ser.skip(12, kGameTypeVampire, kGameTypeVampire);
+ ser.syncAsUint16LE(_videoHotspots, kGameTypeVampire, kGameTypeVampire);
- _sceneChange.readData(stream);
+ ser.syncAsUint16LE(_loopFirstFrame);
+ ser.syncAsUint16LE(_loopLastFrame);
+ ser.syncAsUint16LE(_onHoverFirstFrame);
+ ser.syncAsUint16LE(_onHoverLastFrame);
+ ser.syncAsUint16LE(_onHoverEndFirstFrame);
+ ser.syncAsUint16LE(_onHoverEndLastFrame);
- if (_paletteFilename.size()) {
- stream.skip(3);
- } else {
- stream.skip(1);
- }
+ _sceneChange.readData(stream, ser.getVersion() == kGameTypeVampire);
+ ser.skip(1, kGameTypeNancy1);
- uint16 numVideoDescs = stream.readUint16LE();
- _videoDescs.reserve(numVideoDescs);
+ uint16 numVideoDescs;
+ ser.syncAsUint16LE(numVideoDescs);
+ _videoDescs.resize(numVideoDescs);
for (uint i = 0; i < numVideoDescs; ++i) {
- _videoDescs.push_back(SecondaryVideoDescription());
_videoDescs[i].readData(stream);
}
}
More information about the Scummvm-git-logs
mailing list