[Scummvm-git-logs] scummvm master -> 1fd05cea96680bd707a00daef1a02f8df480eeb5
bluegr
noreply at scummvm.org
Mon Jun 1 23:18:27 UTC 2026
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
2a05646354 NANCY: More work on the Nancy10+ taskbar buttons
d68decbb6d NANCY: Add workaround for hotspots in the Feeding Frenzy game in Nancy9
10d1caf9d7 NANCY: Fix item cursor when hovering over some hotspots in Nancy10+
24dda66e77 NANCY: Disable global font kerning, as the engine has its own kerning
1fd05cea96 NANCY: More work on cellphone search link records for Nancy10+
Commit: 2a05646354759539765b4679131601fc02a89467
https://github.com/scummvm/scummvm/commit/2a05646354759539765b4679131601fc02a89467
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:10+03:00
Commit Message:
NANCY: More work on the Nancy10+ taskbar buttons
- Buttons are now enabled/disabled correctly
- Added (still unused) functionality for button notifications
- More work on the ControUIItems AR, which toggles the taskbar buttons
Changed paths:
engines/nancy/action/miscrecords.cpp
engines/nancy/action/miscrecords.h
engines/nancy/state/scene.cpp
engines/nancy/ui/taskbar.cpp
engines/nancy/ui/taskbar.h
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 5c373c50fb5..b7bab4de98f 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -191,17 +191,65 @@ void FrameTextBox::execute() {
void ControlUIItems::readData(Common::SeekableReadStream &stream) {
_uiButton = stream.readUint16LE();
- _flagA = stream.readByte();
+ _autoOpenOrBadgeSound = stream.readByte();
_flagB = stream.readByte();
- _scene1 = stream.readSint16LE();
- _scene2 = stream.readSint16LE();
+ _startScene = stream.readSint16LE();
+ _endScene = stream.readSint16LE();
}
void ControlUIItems::execute() {
- // TODO: finish this
-
- NancySceneState.getTaskbar()->toggleButton(_uiButton, _flagA != 0);
- debug("ControlUIItems: UIButton=%d, flagA=%d, flagB=%d, scene1=%d, scene2=%d", _uiButton, _flagA, _flagB, _scene1, _scene2);
+ // Value 1 auto-opens the popup selected by _uiButton. For the cell
+ // phone, _startScene (when set) is the scene to jump to once it opens,
+ // which places a call that starts a conversation there.
+ if (_autoOpenOrBadgeSound == 1) {
+ switch (_uiButton) {
+ case kUITypeInventory:
+ NancySceneState.getInventoryPopup().open();
+ break;
+ case kUITypeNotebook:
+ NancySceneState.getNotebookPopup().open();
+ break;
+ case kUITypeCellphone:
+ NancySceneState.getCellPhonePopup().open();
+
+ if (_startScene != (int16)kNoScene) {
+ SceneChangeDescription scene;
+ scene.sceneID = _startScene;
+ scene.frameID = 0;
+ scene.verticalOffset = 0;
+ // The destination scene's sound carries the conversation audio.
+ scene.continueSceneSound = kLoadSceneSound;
+
+ // Pushed so AR 128 in the conversation scene can return here.
+ NancySceneState.pushScene();
+ NancySceneState.changeScene(scene);
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ // Otherwise this AR toggles whether the button is disabled while the
+ // player is in scene range [_startScene, _endScene]. _flagB != 0 sets
+ // the toggle (a _startScene of 9997 means "from scene 0", with the
+ // range capped at 9997); _flagB == 0 clears it once a bound is 9999
+ // (kNoScene). The _autoOpenOrBadgeSound value selects the button's
+ // click sound in the original (not yet implemented).
+ UI::Taskbar *taskbar = NancySceneState.getTaskbar();
+ if (taskbar) {
+ if (_flagB != 0) {
+ int16 start = _startScene;
+ int16 end = _endScene;
+ if (_startScene == 9997) {
+ start = 0;
+ end = 9997;
+ }
+ taskbar->setDisabledRange(_uiButton, start, end);
+ } else if (_startScene == (int16)kNoScene || _endScene == (int16)kNoScene) {
+ taskbar->clearButtonOverride(_uiButton);
+ }
+ }
+ }
finishExecution();
}
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index ed6cd179032..427bb7e5b1c 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -148,10 +148,10 @@ public:
void execute() override;
uint16 _uiButton = 0;
- byte _flagA = 0; // 0, 1 or 10
+ byte _autoOpenOrBadgeSound = 0; // 1 = auto-open popup; 0/10 = notification-badge click-sound selector
byte _flagB = 0; // 0 = clear, 1 = enable+remember scene
- int16 _scene1 = 0; // start scene id (9999 = none)
- int16 _scene2 = 0; // end scene id (9999 = none)
+ int16 _startScene = 0; // start scene id (9999 = none); also the auto-open cell phone's call target
+ int16 _endScene = 0; // end scene id (9999 = none)
protected:
Common::String getRecordTypeName() const override { return "ControlUIItems"; }
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 3cf4d222c41..02cefa63196 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1059,11 +1059,9 @@ void Scene::load(bool fromSaveFile) {
_flags.sceneCounts.getOrCreateVal(_sceneState.currentScene.sceneID)++;
}
- // Enable all task buttons
- // TODO: This should be done elsewhere
+ // Re-evaluate taskbar notification states against the new scene.
if (g_nancy->getGameType() >= kGameTypeNancy10) {
- for (int i = 0; i < 5; ++i)
- _taskbar->toggleButton(i, true);
+ _taskbar->updateNotificationStates(_sceneState.currentScene.sceneID);
}
delete sceneIFF;
diff --git a/engines/nancy/ui/taskbar.cpp b/engines/nancy/ui/taskbar.cpp
index 995c1d0798e..d266c2a62bd 100644
--- a/engines/nancy/ui/taskbar.cpp
+++ b/engines/nancy/ui/taskbar.cpp
@@ -34,9 +34,11 @@ namespace UI {
Taskbar::Taskbar() :
RenderObject(7),
_hoveredButton(-1),
- _clickedButton(-1) {
+ _clickedButton(-1),
+ _currentScene(-1) {
for (uint i = 0; i < TASK::kNumButtons; ++i) {
_buttonStates[i] = kButtonIdle;
+ _enabled[i] = true;
}
}
@@ -72,9 +74,14 @@ void Taskbar::drawButton(uint index, ButtonState state) {
auto *taskData = GetEngineData(TASK);
assert(taskData);
- const UIButtonRecord &btn = taskData->buttons[index].button;
+ const TASK::ButtonRecord &rec = taskData->buttons[index];
+ const UIButtonRecord &btn = rec.button;
- Common::Rect src = btn.sourceRects[state];
+ // The notification sprite lives outside the standard sourceRects
+ // array, in its own per-button rect.
+ Common::Rect src = (state == kButtonNotification)
+ ? rec.notificationSrcRect
+ : btn.sourceRects[state];
if (src.isEmpty())
src = btn.sourceRects[kButtonIdle];
if (src.isEmpty())
@@ -96,8 +103,86 @@ void Taskbar::drawButton(uint index, ButtonState state) {
_needsRedraw = true;
}
+Taskbar::ButtonState Taskbar::restingState(uint index) const {
+ if (index >= TASK::kNumButtons) {
+ return kButtonIdle;
+ }
+ if (!_enabled[index]) {
+ return kButtonDisabled;
+ }
+ const ButtonOverride &o = _overrides[index];
+ if (o.active && _currentScene >= o.startScene && _currentScene <= o.endScene) {
+ return o.state;
+ }
+ return kButtonIdle;
+}
+
+bool Taskbar::isButtonActive(uint index) const {
+ return _enabled[index] && restingState(index) != kButtonDisabled;
+}
+
void Taskbar::toggleButton(uint index, bool enabled) {
- drawButton(index, enabled ? kButtonIdle : kButtonDisabled);
+ if (index >= TASK::kNumButtons) {
+ return;
+ }
+ _enabled[index] = enabled;
+ if ((int)index != _hoveredButton) {
+ drawButton(index, restingState(index));
+ }
+}
+
+void Taskbar::setNotification(uint buttonIndex, int16 startScene, int16 endScene) {
+ if (buttonIndex >= TASK::kNumButtons) {
+ return;
+ }
+ _overrides[buttonIndex].active = true;
+ _overrides[buttonIndex].state = kButtonNotification;
+ _overrides[buttonIndex].startScene = startScene;
+ _overrides[buttonIndex].endScene = endScene;
+
+ // Re-render this button immediately unless the player is currently
+ // hovering it (hover takes priority over the override sprite).
+ if ((int)buttonIndex != _hoveredButton) {
+ drawButton(buttonIndex, restingState(buttonIndex));
+ }
+}
+
+void Taskbar::setDisabledRange(uint buttonIndex, int16 startScene, int16 endScene) {
+ if (buttonIndex >= TASK::kNumButtons) {
+ return;
+ }
+ _overrides[buttonIndex].active = true;
+ _overrides[buttonIndex].state = kButtonDisabled;
+ _overrides[buttonIndex].startScene = startScene;
+ _overrides[buttonIndex].endScene = endScene;
+
+ if ((int)buttonIndex != _hoveredButton) {
+ drawButton(buttonIndex, restingState(buttonIndex));
+ }
+}
+
+void Taskbar::clearButtonOverride(uint buttonIndex) {
+ if (buttonIndex >= TASK::kNumButtons) {
+ return;
+ }
+ _overrides[buttonIndex].active = false;
+
+ if ((int)buttonIndex != _hoveredButton) {
+ drawButton(buttonIndex, restingState(buttonIndex));
+ }
+}
+
+void Taskbar::updateNotificationStates(int16 currentSceneID) {
+ _currentScene = currentSceneID;
+ for (uint i = 0; i < TASK::kNumButtons; ++i) {
+ if ((int)i == _hoveredButton) {
+ continue;
+ }
+ const ButtonState desired = restingState(i);
+ if (_buttonStates[i] != desired) {
+ drawButton(i, desired);
+ }
+ }
}
void Taskbar::handleInput(NancyInput &input) {
@@ -108,18 +193,18 @@ void Taskbar::handleInput(NancyInput &input) {
int newHovered = -1;
for (uint i = 0; i < TASK::kNumButtons; ++i) {
- if (taskData->buttons[i].button.destRect.contains(input.mousePos) && _buttonStates[i] != kButtonDisabled) {
+ if (isButtonActive(i) && taskData->buttons[i].button.destRect.contains(input.mousePos)) {
newHovered = i;
break;
}
}
- // Update hover graphic on enter/exit. Always revert the previously-
- // hovered button (even from kButtonPressed) so it never gets stuck
- // after the cursor leaves.
+ // Update hover graphic on enter/exit. The previously-hovered button
+ // returns to its resting sprite (idle or notification) so it doesn't
+ // get stuck in hover/pressed after the cursor leaves.
if (newHovered != _hoveredButton) {
if (_hoveredButton != -1) {
- drawButton(_hoveredButton, kButtonIdle);
+ drawButton(_hoveredButton, restingState(_hoveredButton));
}
if (newHovered != -1) {
drawButton(newHovered, kButtonHover);
@@ -141,6 +226,12 @@ void Taskbar::handleInput(NancyInput &input) {
} else if (input.input & NancyInput::kLeftMouseButtonUp) {
// Mouse released over the button: trigger the click action and
// snap the sprite back to hover (the cursor is still over it).
+ // Acknowledging the click also clears a pending notification
+ // for this button, so re-opening the popup doesn't keep blinking.
+ // A scene-ranged disable override is left intact.
+ if (_overrides[newHovered].state == kButtonNotification) {
+ _overrides[newHovered].active = false;
+ }
drawButton(newHovered, kButtonHover);
_clickedButton = newHovered;
diff --git a/engines/nancy/ui/taskbar.h b/engines/nancy/ui/taskbar.h
index d3cfcf7cb65..f8b1d92dd22 100644
--- a/engines/nancy/ui/taskbar.h
+++ b/engines/nancy/ui/taskbar.h
@@ -42,27 +42,61 @@ public:
void registerGraphics() override;
void handleInput(NancyInput &input);
+ // Enable / disable a taskbar button. A disabled button is rendered
+ // in its disabled sprite and ignores clicks.
void toggleButton(uint index, bool enabled);
+ // Configure a per-button override that is active only while the player
+ // is in a scene whose ID falls in [startScene, endScene]. The button
+ // renders in its notification (badge) sprite, or its disabled sprite,
+ // for that range; outside it reverts to idle.
+ // setDisabledRange is driven by AR 29 (ControlUIItems, _flagB != 0).
+ // setNotification renders the badge sprite; the source that should drive
+ // it has not been identified yet (it is NOT ControlUIItems).
+ void setNotification(uint buttonIndex, int16 startScene, int16 endScene);
+ void setDisabledRange(uint buttonIndex, int16 startScene, int16 endScene);
+ void clearButtonOverride(uint buttonIndex);
+
+ // Re-evaluate which buttons should currently show their override
+ // sprite. Call after a scene change so the range check kicks in.
+ void updateNotificationStates(int16 currentSceneID);
+
// Returns the index of the button that was clicked this frame, or -1
// if none. Cleared on the next call to handleInput().
int getClickedButton() const { return _clickedButton; }
private:
enum ButtonState {
- kButtonIdle = 0,
- kButtonHover = 1,
- kButtonPressed = 2,
- kButtonDisabled = 3
+ kButtonIdle = 0,
+ kButtonHover = 1,
+ kButtonPressed = 2,
+ kButtonDisabled = 3, // not clickable
+ kButtonNotification = 4 // popup has new content (badge sprite)
+ };
+
+ // A scene-ranged sprite override for one button. While active and the
+ // current scene is within [startScene, endScene] the button renders in
+ // `state` (kButtonNotification or kButtonDisabled).
+ struct ButtonOverride {
+ bool active = false;
+ ButtonState state = kButtonIdle;
+ int16 startScene = -1;
+ int16 endScene = -1;
};
void drawButton(uint index, ButtonState state);
+ ButtonState restingState(uint index) const;
+ // True when the button currently accepts hover/click (not disabled).
+ bool isButtonActive(uint index) const;
Graphics::ManagedSurface _backgroundImage; // TASK::imageName (e.g. "Frame")
Graphics::ManagedSurface _buttonImage; // buttons' primaryImageName (e.g. "UIShared_OVL")
int _hoveredButton;
int _clickedButton;
+ int16 _currentScene;
+ bool _enabled[5];
ButtonState _buttonStates[5];
+ ButtonOverride _overrides[5];
};
} // End of namespace UI
Commit: d68decbb6d65b724185b3bf988edb5d121194f05
https://github.com/scummvm/scummvm/commit/d68decbb6d65b724185b3bf988edb5d121194f05
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:12+03:00
Commit Message:
NANCY: Add workaround for hotspots in the Feeding Frenzy game in Nancy9
Fix #16792
Changed paths:
engines/nancy/action/interactivevideo.cpp
diff --git a/engines/nancy/action/interactivevideo.cpp b/engines/nancy/action/interactivevideo.cpp
index 3ee0cd42bd4..b50851bf8e8 100644
--- a/engines/nancy/action/interactivevideo.cpp
+++ b/engines/nancy/action/interactivevideo.cpp
@@ -55,6 +55,19 @@ void InteractiveVideo::readData(Common::SeekableReadStream &stream) {
readFilename(*ivFile, _videoName);
+ // WORKAROUND: In Nancy 9, the Feeding Frenzy mini-game plays 6 videos (the whales that pop up)
+ // with a normal arrow cursor. In such cases, the cursor manager reverts to the default arrow
+ // cursor, if an item is held (which, in this case is a fish). We replace these cursors with a
+ // normal one, which allows for the held item to be visible. Making this a workaround for that
+ // scene, since a change in that part of the cursor code would require a full regression test in
+ // all supported games. Fixes bug #16792.
+ const uint16 sceneId = NancySceneState.getSceneInfo().sceneID;
+ if (g_nancy->getGameType() == kGameTypeNancy9 && (sceneId == 2992 || sceneId == 2995 || sceneId == 2996)) {
+ if (_videoName.toString().contains("WhaleFeed") && _cursors[0] == CursorManager::kNormalArrow) {
+ _cursors[0] = CursorManager::kNormal;
+ }
+ }
+
uint32 numFrames = ivFile->readUint32LE();
_frames.resize(numFrames);
for (uint i = 0; i < numFrames; ++i) {
Commit: 10d1caf9d70dd6ae8210cebd2817c154616fb93e
https://github.com/scummvm/scummvm/commit/10d1caf9d70dd6ae8210cebd2817c154616fb93e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:13+03:00
Commit Message:
NANCY: Fix item cursor when hovering over some hotspots in Nancy10+
Fixes the cursor when hovering the key over the rolltop in Nancy10
Changed paths:
engines/nancy/action/datarecords.cpp
engines/nancy/action/datarecords.h
diff --git a/engines/nancy/action/datarecords.cpp b/engines/nancy/action/datarecords.cpp
index 65f2009ce91..6610f500be0 100644
--- a/engines/nancy/action/datarecords.cpp
+++ b/engines/nancy/action/datarecords.cpp
@@ -361,6 +361,12 @@ void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
}
}
+CursorManager::CursorType EventFlagsMultiHS::getHoverCursor() const {
+ if (g_nancy->getGameType() >= kGameTypeNancy10 && NancySceneState.getHeldItem() >= 0)
+ return CursorManager::kHotspot;
+ return _hoverCursor;
+}
+
void EventFlagsMultiHS::execute() {
switch (_state) {
case kBegin:
diff --git a/engines/nancy/action/datarecords.h b/engines/nancy/action/datarecords.h
index d8a5700ab42..d200227d2f6 100644
--- a/engines/nancy/action/datarecords.h
+++ b/engines/nancy/action/datarecords.h
@@ -120,7 +120,7 @@ public:
void readData(Common::SeekableReadStream &stream) override;
void execute() override;
- CursorManager::CursorType getHoverCursor() const override { return _hoverCursor; }
+ CursorManager::CursorType getHoverCursor() const override;
CursorManager::CursorType _hoverCursor = CursorManager::kHotspot;
Common::Array<HotspotDescription> _hotspots;
Commit: 24dda66e7783fad9d73b5f71dbfe73cea2e0b05b
https://github.com/scummvm/scummvm/commit/24dda66e7783fad9d73b5f71dbfe73cea2e0b05b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:14+03:00
Commit Message:
NANCY: Disable global font kerning, as the engine has its own kerning
Changed paths:
engines/nancy/font.cpp
engines/nancy/font.h
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 13ce11d4ace..2235f9cc837 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -51,7 +51,7 @@ void Font::read(Common::SeekableReadStream &stream) {
_color1CoordsOffset.y = stream.readUint32LE();
_spaceWidth = stream.readUint16LE();
- _charSpace = stream.readSint16LE() - 1; // Account for the added pixel in readRect
+ _charSpace = stream.readSint16LE();
_uppercaseOffset = stream.readUint16LE();
_lowercaseOffset = stream.readUint16LE();
diff --git a/engines/nancy/font.h b/engines/nancy/font.h
index 9ebab817312..c3d73477efe 100644
--- a/engines/nancy/font.h
+++ b/engines/nancy/font.h
@@ -46,7 +46,7 @@ public:
int getFontHeight() const override { return _fontHeight - 1; }
int getMaxCharWidth() const override { return _maxCharWidth; }
int getCharWidth(uint32 chr) const override;
- int getKerningOffset(uint32 left, uint32 right) const override { return 1; }
+ int getKerningOffset(uint32 left, uint32 right) const override { return 0; }
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
Commit: 1fd05cea96680bd707a00daef1a02f8df480eeb5
https://github.com/scummvm/scummvm/commit/1fd05cea96680bd707a00daef1a02f8df480eeb5
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:16+03:00
Commit Message:
NANCY: More work on cellphone search link records for Nancy10+
Changed paths:
engines/nancy/action/miscrecords.cpp
engines/nancy/action/miscrecords.h
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index b7bab4de98f..1c45bbd347f 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -275,14 +275,12 @@ void AddSearchLink::readData(Common::SeekableReadStream &stream) {
_extra = stream.readSint16LE();
_flag = stream.readSint16LE();
- _scene = stream.readSint16LE();
+ _eventFlag = stream.readSint16LE();
}
void AddSearchLink::execute() {
- // TODO: finish this
-
- debug("AddSearchLink: mode=%d, key=%s, value=%s, extra=%d, scene1=%d, scene2=%d", _mode, _key.c_str(), _value.c_str(), _extra, _flag, _scene);
-
+ //NancySceneState.getCellPhonePopup().addSearchLink(
+ // _mode, _key, _value, _extra, _flag, _eventFlag);
finishExecution();
}
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 427bb7e5b1c..b5dd38cc958 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -178,11 +178,11 @@ public:
void execute() override;
int16 _mode = 0;
- Common::String _key;
- Common::String _value;
- int16 _extra = 0;
- int16 _flag = 0;
- int16 _scene = 0;
+ Common::String _key; // CVTX key for the list row text (both modes)
+ Common::String _value; // body CVTX key (mode 0/email); unused for mode 1
+ int16 _extra = 0; // page index (mode 1); unused for mode 0
+ int16 _flag = 0; // stored but unused by the original; reserved
+ int16 _eventFlag = 0; // event-flag index set when the entry is opened
protected:
Common::String getRecordTypeName() const override { return "AddSearchLink"; }
More information about the Scummvm-git-logs
mailing list