[Scummvm-git-logs] scummvm master -> f36970c8c32acbc4fb7ec07d5df7ce61483e92e5
bluegr
noreply at scummvm.org
Sat Sep 12 01:06:00 UTC 2026
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
e9b24e62a1 NANCY: NANCY14: Fix color matching and winning in PaintPuzzle
f36970c8c3 NANCY: NANCY13-14: Fixes for secondary movies
Commit: e9b24e62a1f59c93c79ba118bf7fe842ce5355a7
https://github.com/scummvm/scummvm/commit/e9b24e62a1f59c93c79ba118bf7fe842ce5355a7
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-12T04:05:45+03:00
Commit Message:
NANCY: NANCY14: Fix color matching and winning in PaintPuzzle
- Read the solve scene info correctly
- Read sound data correctly
- Check against the correct color indices
Marchant's paintings can now be solved correctly
Changed paths:
engines/nancy/action/puzzle/paintpuzzle.cpp
engines/nancy/action/puzzle/paintpuzzle.h
diff --git a/engines/nancy/action/puzzle/paintpuzzle.cpp b/engines/nancy/action/puzzle/paintpuzzle.cpp
index f39d5632900..326362ab368 100644
--- a/engines/nancy/action/puzzle/paintpuzzle.cpp
+++ b/engines/nancy/action/puzzle/paintpuzzle.cpp
@@ -19,11 +19,14 @@
*
*/
+#include "common/random.h"
+
#include "engines/nancy/nancy.h"
#include "engines/nancy/cursor.h"
#include "engines/nancy/graphics.h"
#include "engines/nancy/input.h"
#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
#include "engines/nancy/util.h"
#include "engines/nancy/action/puzzle/paintpuzzle.h"
@@ -64,12 +67,15 @@ void PaintPuzzle::readData(Common::SeekableReadStream &stream) {
_sounds[0].readData(stream); // 0xa4
_sounds[1].readData(stream); // 0xfa
- _field1a6 = stream.readSint16LE(); // 0x1a6
- _outcome.field0 = stream.readSint16LE(); // 0x1a8
- _outcome.sceneID = stream.readSint16LE();
- _outcome.flag = stream.readByte();
+ // Shorter than the SceneChangeWithFlag::readData() formats: no vertical
+ // offset or scene sound field
+ _solveScene._sceneChange.sceneID = stream.readUint16LE(); // 0x1a6
+ _solveScene._sceneChange.frameID = stream.readUint16LE();
+ _solveScene._sceneChange.continueSceneSound = kContinueSceneSound;
+ _solveScene._flag.label = stream.readSint16LE();
+ _solveScene._flag.flag = stream.readByte();
- _sounds[2].readData(stream);
+ _solveSound.readData(stream); // 0x150
// Trailing count-prefixed array of 23-byte give-up hotspots
// {Rect, uint16 cursorType, uint16 sceneID, int16 flagLabel, byte flagValue}.
@@ -101,7 +107,6 @@ void PaintPuzzle::init() {
_hoverRegion = -1;
_hoverColor = -1;
_solved = false;
- _outcomeApplied = false;
redraw();
}
@@ -152,7 +157,7 @@ int PaintPuzzle::regionAtCursor(const Common::Point &mousePos) const {
// Draws a painted region: its overlay shape recolored to the flat fill color.
void PaintPuzzle::drawRegion(uint regionIndex) {
const PaintRegion ®ion = _regions[regionIndex];
- int c = region.currentColor;
+ int c = region.currentColor - 1;
if (c < 0 || c >= (int)_colors.size() || regionIndex >= _regionImages.size()) {
return;
}
@@ -242,7 +247,7 @@ void PaintPuzzle::redraw() {
// Only painted regions are drawn on the overlay; the picture outline and
// palette come from the scene background.
for (uint i = 0; i < _regions.size(); ++i) {
- if (_regions[i].currentColor >= 0) {
+ if (_regions[i].currentColor > 0) {
drawRegion(i);
}
}
@@ -265,18 +270,37 @@ bool PaintPuzzle::isSolved() const {
}
void PaintPuzzle::paintRegion(uint regionIndex, int colorIndex) {
- _regions[regionIndex].currentColor = (int16)colorIndex;
+ // Region colors are 1-based palette indices; 0 means unpainted
+ _regions[regionIndex].currentColor = (int16)(colorIndex + 1);
if (isSolved()) {
_solved = true;
}
redraw();
}
-void PaintPuzzle::applyOutcome(const SceneOutcome &outcome) {
- SceneChangeDescription desc;
- desc.sceneID = outcome.sceneID;
- NancySceneState.changeScene(desc);
- NancySceneState.setEventFlag(outcome.field0, outcome.flag);
+void PaintPuzzle::playSoundBlock(const RandomSoundBlock &block) {
+ if (block.names.empty()) {
+ return;
+ }
+
+ uint idx = block.names.size() == 1 ? 0 : g_nancy->_randomSource->getRandomNumber(block.names.size() - 1);
+ const Common::String &name = block.names[idx];
+ if (name.empty() || name == "NO SOUND") {
+ return;
+ }
+
+ SoundDescription desc;
+ desc.name = name;
+ desc.channelID = block.channel;
+ desc.numLoops = block.numLoops > 0 ? block.numLoops : 1;
+ desc.volume = block.volume;
+
+ g_nancy->_sound->loadSound(desc);
+ g_nancy->_sound->playSound(desc);
+}
+
+bool PaintPuzzle::isSoundBlockPlaying(const RandomSoundBlock &block) const {
+ return !block.names.empty() && g_nancy->_sound->isSoundPlaying((uint16)block.channel);
}
void PaintPuzzle::handleInput(NancyInput &input) {
@@ -338,15 +362,27 @@ void PaintPuzzle::execute() {
_state = kRun;
break;
case kRun:
+ if (_exitRequested || _solved) {
+ if (_solved) {
+ playSoundBlock(_solveSound);
+ }
+ _state = kActionTrigger;
+ }
+ break;
+ case kActionTrigger:
+ // The solve sound gets to finish first
+ if (!_exitRequested && isSoundBlockPlaying(_solveSound)) {
+ break;
+ }
+
if (_exitRequested) {
NancySceneState.setEventFlag(_exitFlag);
NancySceneState.changeScene(_exitScene);
- break;
- }
- if (_solved && !_outcomeApplied) {
- _outcomeApplied = true;
- applyOutcome(_outcome);
+ } else {
+ _solveScene.execute();
}
+
+ finishExecution();
break;
default:
break;
diff --git a/engines/nancy/action/puzzle/paintpuzzle.h b/engines/nancy/action/puzzle/paintpuzzle.h
index 0cce3fd9f17..fbe7ad9c093 100644
--- a/engines/nancy/action/puzzle/paintpuzzle.h
+++ b/engines/nancy/action/puzzle/paintpuzzle.h
@@ -58,7 +58,8 @@ protected:
};
// A fillable region of the picture: an overlay shape drawn at a position,
- // its current color index, and the target it must hold to be solved.
+ // its current color, and the target it must hold to be solved. Colors are
+ // 1-based palette indices; 0 means unpainted.
struct PaintRegion {
Common::Path name;
Common::Rect rect;
@@ -66,12 +67,6 @@ protected:
int16 targetColor = -1;
};
- struct SceneOutcome {
- int16 field0 = 0;
- int16 sceneID = 0;
- byte flag = 0;
- };
-
int colorSwatchAtCursor(const Common::Point &mousePos) const;
int regionAtCursor(const Common::Point &mousePos) const;
// Alpha of the region overlay's shape at (x,y): the pixel's alpha channel, or
@@ -82,7 +77,8 @@ protected:
void drawBrush();
void redraw();
bool isSolved() const;
- void applyOutcome(const SceneOutcome &outcome);
+ void playSoundBlock(const RandomSoundBlock &block);
+ bool isSoundBlockPlaying(const RandomSoundBlock &block) const;
// -- File data --
Common::Path _imageName; // 0x3d
@@ -93,10 +89,10 @@ protected:
Common::Array<PaintColor> _colors; // 0x78
Common::Array<PaintRegion> _regions; // 0x94
- RandomSoundBlock _sounds[3]; // 0xa4/0xfa (before) + one after the outcome
+ RandomSoundBlock _sounds[2]; // 0xa4/0xfa
- int16 _field1a6 = 0; // 0x1a6
- SceneOutcome _outcome; // 0x1a8
+ SceneChangeWithFlag _solveScene; // 0x1a6
+ RandomSoundBlock _solveSound; // 0x150, plays before the solve scene change
// Give-up hotspot (count-prefixed 23-byte trailer): click to leave the puzzle.
Common::Rect _exitHotspot;
@@ -112,7 +108,6 @@ protected:
int _hoverRegion = -1;
int _hoverColor = -1;
bool _solved = false;
- bool _outcomeApplied = false;
bool _exitRequested = false;
};
Commit: f36970c8c32acbc4fb7ec07d5df7ce61483e92e5
https://github.com/scummvm/scummvm/commit/f36970c8c32acbc4fb7ec07d5df7ce61483e92e5
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-12T04:05:46+03:00
Commit Message:
NANCY: NANCY13-14: Fixes for secondary movies
- Fixes talking to some characters in Nancy14 (e.g. Dieter and Monique)
- Add handling for random pauses in turning animations
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 2c78e7a7159..7dd98d84934 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -130,17 +130,20 @@ void PlaySecondaryMovie::handleInput(NancyInput &input) {
return;
}
- // The character's box (set as the hotspot while it is on screen) is
- // clickable; clicking opens its conversation scene, and hovering drives the
- // recognition movie. The talk hover cursor is applied by ActionManager via
- // getHoverCursor().
- if (!_hasHotspot || _talkSceneID == kNoScene) {
- _isHovered = false;
+ // The mouse is hit-tested against the movie's own box whether or not the
+ // record is clickable, since the sequence chain reacts to hovering on its
+ // own (Nancy14 characters turn toward the player that way, Nancy13 ones
+ // play their recognition movie).
+ //
+ // A character that names a conversation scene is clickable on top of that;
+ // its hover cursor is applied by ActionManager via getHoverCursor().
+ _isHovered = _isVisible &&
+ NancySceneState.getViewport().convertViewportToScreen(_screenPosition).contains(input.mousePos);
+
+ if (_talkSceneID == kNoScene) {
return;
}
- _isHovered = NancySceneState.getViewport().convertViewportToScreen(_hotspot).contains(input.mousePos);
-
if (_isHovered && (input.input & NancyInput::kLeftMouseButtonUp)) {
input.eatMouseInput();
SceneChangeDescription desc;
@@ -150,9 +153,10 @@ void PlaySecondaryMovie::handleInput(NancyInput &input) {
}
CursorManager::CursorType PlaySecondaryMovie::getHoverCursor() const {
- // The character's own cursor type (a raw Nancy13 cursor id) comes from the
- // secondary record; cursorSetFromScript() routes it through the raw-slot path.
- return (CursorManager::CursorType)_talkCursorType;
+ // The character's own cursor type (a raw cursor id) comes from the chunk;
+ // cursorSetFromScript() routes it through the raw-slot path. Records that
+ // don't name one keep the generic hotspot cursor.
+ return _talkCursorType >= 0 ? (CursorManager::CursorType)_talkCursorType : CursorManager::kHotspot;
}
void PlaySecondaryMovie::readRandomSequence(Common::Serializer &ser, RandomSequence &seq) {
@@ -177,8 +181,35 @@ void PlaySecondaryMovie::readRandomSequence(Common::Serializer &ser, RandomSeque
seq.nextSequences.resize(nextCount);
for (uint i = 0; i < nextCount; ++i) {
- readFilename(ser, seq.nextSequences[i].name);
- ser.syncAsUint16LE(seq.nextSequences[i].weight);
+ NextSequenceRef &next = seq.nextSequences[i];
+ readFilename(ser, next.name);
+
+ // A negative weight isn't a weight at all, but one of the special flags
+ // that make this entry the one picked when its condition holds.
+ int16 weight = 0;
+ ser.syncAsSint16LE(weight);
+
+ switch (weight) {
+ case -1:
+ next.condition = kNextEqualChance;
+ seq.equalChanceNext = true;
+ break;
+ case -2:
+ next.condition = kNextIfHovered;
+ break;
+ case -3:
+ next.condition = kNextIfNotHovered;
+ break;
+ case -4:
+ next.condition = kNextIfChannel13Playing;
+ break;
+ case -5:
+ next.condition = kNextIfChannel12Playing;
+ break;
+ default:
+ next.weight = weight;
+ break;
+ }
}
}
@@ -192,7 +223,7 @@ void PlaySecondaryMovie::readSecondaryRandomMovie(Common::Serializer &ser, Rando
readFilename(ser, seq.name);
ser.syncAsUint16LE(seq.startFrame);
ser.syncAsUint16LE(seq.lastFrame);
- ser.syncAsUint16LE(_talkCursorType); // hover cursor for the character
+ ser.syncAsSint16LE(_talkCursorType); // hover cursor for the character
ser.syncAsUint16LE(_talkSceneID);
ser.skip(2); // conversation frameID (0 in known data)
@@ -255,9 +286,13 @@ void PlaySecondaryMovie::readRandomMovieDataNancy14(Common::Serializer &ser, Com
_videoFormat = kLargeVideoFormat;
ser.skip(2); // Visibility frame ID; ScummVM drives visibility from the videoDescs
ser.syncAsUint16LE(_randomPlayerCursorAllowed);
- ser.skip(4); // Two u16s (object offsets 0x8c / 0xe7); purpose not yet mapped
- ser.syncAsSint16LE(_sceneChange.sceneID);
- ser.syncAsUint16LE(_sceneChange.frameID);
+ ser.skip(2); // Event flag gating the roll for a next sequence; -1 = always roll
+ // Talkable character: the cursor shown while the mouse is over it, and the
+ // conversation scene a click opens. -1 / kNoScene mean the record carries
+ // neither, i.e. the character isn't clickable.
+ ser.syncAsSint16LE(_talkCursorType);
+ ser.syncAsUint16LE(_talkSceneID);
+ ser.skip(2); // Conversation frame ID (0 in known data)
ser.syncAsByte(_movieVolume);
_movieVolume = MIN<byte>(_movieVolume, 100);
@@ -447,18 +482,41 @@ void PlaySecondaryMovie::playRandomSequence() {
}
int PlaySecondaryMovie::beginRandomPause(const RandomSequence &seq) {
+ _randomChainState = kRandomPaused;
+
+ // Two of the pause values are sentinels: instead of a duration they hold
+ // the sequence on its last frame until the mouse enters or leaves the
+ // movie, which is how a character keeps looking at the player for as long
+ // as the mouse stays on them.
+ if (seq.minPauseMs == -2 || seq.minPauseMs == -3) {
+ _randomPauseMode = seq.minPauseMs == -2 ? kPauseUntilHovered : kPauseUntilNotHovered;
+ return -1;
+ }
+
int32 pauseMs = seq.minPauseMs;
if (seq.maxPauseMs > seq.minPauseMs) {
pauseMs += g_nancy->_randomSource->getRandomNumber(seq.maxPauseMs - seq.minPauseMs - 1);
}
+
+ _randomPauseMode = kPauseTimed;
_randomPauseEndTime = g_system->getMillis() + (uint32)MAX<int32>(0, pauseMs);
- _randomChainState = kRandomPaused;
setVisible(false);
_mask.setVisible(false);
_decoder.pauseVideo(true);
return -1;
}
+bool PlaySecondaryMovie::randomPauseElapsed() const {
+ switch (_randomPauseMode) {
+ case kPauseUntilHovered:
+ return _isHovered;
+ case kPauseUntilNotHovered:
+ return !_isHovered;
+ default:
+ return g_system->getMillis() >= _randomPauseEndTime;
+ }
+}
+
int PlaySecondaryMovie::lookupSequence(const Common::Path &name) const {
for (uint j = 0; j < _sequences.size(); ++j) {
if (_sequences[j].name == name) {
@@ -469,45 +527,93 @@ int PlaySecondaryMovie::lookupSequence(const Common::Path &name) const {
return -1;
}
-int PlaySecondaryMovie::rollNextSequence() {
+int PlaySecondaryMovie::pickNextSequence() {
if (_activeSequenceIndex < 0 || _activeSequenceIndex >= (int)_sequences.size()) {
return -1;
}
const RandomSequence &seq = _sequences[_activeSequenceIndex];
- if (g_nancy->getGameType() >= kGameTypeNancy13) {
- // Two independent rolls: first a percent chance to stay on this
- // sequence and pause, then a percent-weighted pick among the next
- // sequences (weights sum to 100, or all EQUAL_CHANCE for a uniform pick).
- if (seq.stayWeight != 0 && (uint)g_nancy->_randomSource->getRandomNumber(99) < seq.stayWeight) {
- return beginRandomPause(seq);
+ if (seq.nextSequences.empty()) {
+ _randomChainState = kRandomPaused;
+ _randomPauseMode = kPauseTimed;
+ _randomPauseEndTime = g_system->getMillis() + 1000; // re-check in 1s
+ return -1;
+ }
+
+ // The special-flag entries are tried first, in the order the original uses:
+ // the one matching the current hover state, then the sound-gated ones.
+ const NextCondition hoverCondition = _isHovered ? kNextIfHovered : kNextIfNotHovered;
+ for (const NextSequenceRef &next : seq.nextSequences) {
+ if (next.condition == hoverCondition) {
+ return lookupSequence(next.name);
}
- if (seq.nextSequences.empty()) {
- _randomChainState = kRandomPaused;
- _randomPauseEndTime = g_system->getMillis() + 1000; // re-check in 1s
- return -1;
+ if (next.condition == kNextIfChannel12Playing || next.condition == kNextIfChannel13Playing) {
+ warning("PlayRandomMovie: sound-gated next-sequence \"%s\" is not implemented",
+ next.name.toString().c_str());
}
+ }
- const bool equalChance = seq.nextSequences[0].weight == 0xFFFF;
- const uint step = 100 / seq.nextSequences.size();
- uint roll = g_nancy->_randomSource->getRandomNumber(99);
- uint cumulative = 0;
- for (uint i = 0; i < seq.nextSequences.size(); ++i) {
- if (i == seq.nextSequences.size() - 1) {
- cumulative = 100;
- } else {
- cumulative += equalChance ? step : seq.nextSequences[i].weight;
- }
- if (roll < cumulative) {
- return lookupSequence(seq.nextSequences[i].name);
- }
+ // Otherwise a percent-weighted pick among the weighted entries, whose
+ // weights sum to 100 (or take an equal share each).
+ uint numWeighted = 0;
+ for (const NextSequenceRef &next : seq.nextSequences) {
+ if (next.condition == kNextWeighted || next.condition == kNextEqualChance) {
+ ++numWeighted;
}
+ }
+ if (numWeighted == 0) {
+ // Nothing but conditions that don't hold right now. Hold the sequence
+ // on its last frame and re-check shortly, since moving the mouse can
+ // change the answer.
+ _randomChainState = kRandomPaused;
+ _randomPauseMode = kPauseTimed;
+ _randomPauseEndTime = g_system->getMillis() + 100;
return -1;
}
+ const uint step = 100 / numWeighted;
+ const uint roll = g_nancy->_randomSource->getRandomNumber(99);
+ uint cumulative = 0;
+ uint weightedIndex = 0;
+ for (const NextSequenceRef &next : seq.nextSequences) {
+ if (next.condition != kNextWeighted && next.condition != kNextEqualChance) {
+ continue;
+ }
+
+ if (++weightedIndex == numWeighted) {
+ cumulative = 100;
+ } else {
+ cumulative += seq.equalChanceNext ? step : next.weight;
+ }
+
+ if (roll < cumulative) {
+ return lookupSequence(next.name);
+ }
+ }
+
+ return -1;
+}
+
+int PlaySecondaryMovie::rollNextSequence() {
+ if (_activeSequenceIndex < 0 || _activeSequenceIndex >= (int)_sequences.size()) {
+ return -1;
+ }
+
+ const RandomSequence &seq = _sequences[_activeSequenceIndex];
+
+ if (g_nancy->getGameType() >= kGameTypeNancy13) {
+ // First a percent chance to stay on this sequence and pause; the pick
+ // among the next sequences happens once the pause is over.
+ if (seq.stayWeight != 0 && (uint)g_nancy->_randomSource->getRandomNumber(99) < seq.stayWeight) {
+ return beginRandomPause(seq);
+ }
+
+ return pickNextSequence();
+ }
+
uint32 totalWeight = seq.stayWeight;
for (const NextSequenceRef &ns : seq.nextSequences) {
totalWeight += ns.weight;
@@ -940,11 +1046,11 @@ void PlaySecondaryMovie::execute() {
_state = kActionTrigger;
break;
}
- if (g_system->getMillis() < _randomPauseEndTime) {
+ if (!randomPauseElapsed()) {
break;
}
_randomChainState = kRandomPlaying;
- int picked = rollNextSequence();
+ int picked = g_nancy->getGameType() >= kGameTypeNancy13 ? pickNextSequence() : rollNextSequence();
if (picked >= 0) {
activateRandomSequence(picked);
}
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index d9cb365d71b..f78dff85ad2 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -60,11 +60,25 @@ public:
FlagDescription flagDesc;
};
+ // What makes a next-sequence entry the one picked once the current sequence
+ // finishes. Entries carry a percent weight, unless the chunk tags them with
+ // one of the negative "special flag" values below; a tagged entry is picked
+ // whenever its condition holds, ahead of the weighted roll.
+ enum NextCondition {
+ kNextWeighted = 0, // ordinary percent weight
+ kNextEqualChance, // -1: uniform share among the entries
+ kNextIfHovered, // -2: the mouse is over the movie
+ kNextIfNotHovered, // -3: it isn't
+ kNextIfChannel13Playing, // -4
+ kNextIfChannel12Playing // -5
+ };
+
// Name of the next sequence to chain to once the current one finishes,
- // plus its selection weight in the weighted random pick.
+ // plus what makes it the one picked.
struct NextSequenceRef {
Common::Path name;
uint16 weight = 0;
+ NextCondition condition = kNextWeighted;
};
// `name` is both the sequence id and the movie filename.
@@ -76,9 +90,11 @@ public:
int32 maxPauseMs = 0;
// Weight assigned to "stay on this sequence" in the weighted random
// pick. A roll inside [0, stayWeight) means "don't transition";
- // instead pause for [minPauseMs, maxPauseMs] and re-roll.
+ // instead pause for [minPauseMs, maxPauseMs] before moving on.
uint16 stayWeight = 0;
Common::Array<NextSequenceRef> nextSequences;
+ // Every weighted entry takes an equal share of the pick.
+ bool equalChanceNext = false;
};
// Which of the action record types sharing this class is being played.
@@ -189,24 +205,31 @@ public:
Common::Path _maskName;
Common::Array<SecondaryVideoDescription> _maskDescs;
- // Nancy13 talkable characters: the scene to open when the character is
+ // Talkable characters (Nancy13+): the scene to open when the character is
// clicked (its conversation). kNoScene means the character isn't clickable.
uint16 _talkSceneID = kNoScene;
- // Hover cursor for the character (a raw Nancy13 cursor id from the chunk).
- uint16 _talkCursorType = 0;
+ // Hover cursor for the character (a raw cursor id from the chunk), or -1
+ // when the record doesn't name one.
+ int16 _talkCursorType = -1;
// Chain state. After a sequence's movie finishes the engine rolls a
// weighted pick: "stay" -> enter pause for a random duration and
// re-roll; valid next-sequence -> swap to that sequence's movie.
enum RandomChainState { kRandomPlaying, kRandomPaused };
+ // What ends the pause: its duration running out, or the mouse entering or
+ // leaving the movie (minPauseMs -2 / -3). A sequence waiting on the mouse
+ // holds its last frame on screen instead of hiding.
+ enum RandomPauseMode { kPauseTimed, kPauseUntilHovered, kPauseUntilNotHovered };
int _activeSequenceIndex = -1;
RandomChainState _randomChainState = kRandomPlaying;
+ RandomPauseMode _randomPauseMode = kPauseTimed;
uint32 _randomPauseEndTime = 0;
bool _randomStopRequested = false;
bool _randomPaused = false;
- // Talkable-character hover state: whether the mouse is over the character,
- // and whether the recognition (secondary) movie is currently playing.
+ // Whether the mouse is over the movie (which drives both the hover-based
+ // sequence chain and the click that opens a character's conversation), and
+ // whether the recognition (secondary) movie is currently playing.
bool _isHovered = false;
bool _playingSecondary = false;
@@ -240,7 +263,7 @@ public:
// hovering plays the recognition ("turn around") movie.
void handleInput(NancyInput &input) override;
CursorManager::CursorType getHoverCursor() const override;
- bool cursorSetFromScript() const override { return isRandom() && _talkSceneID != kNoScene; }
+ bool cursorSetFromScript() const override { return isRandom() && _talkSceneID != kNoScene && _talkCursorType >= 0; }
Common::String getRecordExtraInfo() const override {
return Common::String::format("Scene %d, file %s", _sceneChange.sceneID, _videoName.baseName().c_str());
@@ -307,10 +330,19 @@ protected:
// or the chosen sequence index otherwise.
int rollNextSequence();
+ // Pick the sequence to chain to, without rolling for "stay" first: the
+ // special-flag entries take priority over the weighted random pick.
+ // Returns the chosen sequence index, or -1 if nothing was picked.
+ int pickNextSequence();
+
// Enter the paused chain state for a random duration in the sequence's
- // [minPauseMs, maxPauseMs] range. Always returns -1.
+ // [minPauseMs, maxPauseMs] range, or until the mouse enters or leaves the
+ // movie. Always returns -1.
int beginRandomPause(const RandomSequence &seq);
+ // Whether whatever the current pause is waiting for has happened.
+ bool randomPauseElapsed() const;
+
// Find a sequence by name, warning and returning -1 if it isn't present.
int lookupSequence(const Common::Path &name) const;
More information about the Scummvm-git-logs
mailing list