[Scummvm-git-logs] scummvm master -> 63b12c3149e39e6709de9a90342f1f3f0ca92efa
bluegr
noreply at scummvm.org
Tue Jul 21 23:46:34 UTC 2026
This automated email contains information about 8 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
54608cee24 NANCY: NANCY10: Read the frame image name from the TASK chunk
54b087a8fc NANCY: NANCY13: Show subtitles for Nancy's messages
8d46b754bd NANCY: NANCY12: Add detection entry for the French version
4d66c4f005 NANCY: Filter invalid file name characters when dumping action records
1da8ef9083 NANCY: NANCY12: Implement new functionality in CollisionPuzzle/TileMove
9a2b2685fe NANCY: NANCY10: Fix display of mini vs full overlay in ScrollTextBox
7df1a23fbb NANCY: Fix a bug with cell width calculation in CollisionPuzzle
63b12c3149 NANCY: NANCY12: More work on the ResourceUse AR
Commit: 54608cee24a8495a8c79392d8308585c60953f6a
https://github.com/scummvm/scummvm/commit/54608cee24a8495a8c79392d8308585c60953f6a
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:13+03:00
Commit Message:
NANCY: NANCY10: Read the frame image name from the TASK chunk
Fixes the frame image in Nancy13 (where it was renamed)
Changed paths:
engines/nancy/state/scene.cpp
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 9e66d491184..3ed6fcee6d3 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1641,12 +1641,16 @@ void Scene::initStaticData() {
auto *bootSummary = GetEngineData(BSUM);
assert(bootSummary);
- Common::Path imageName = "FRAME";
+ Common::Path imageName;
if (g_nancy->getGameType() <= kGameTypeNancy9) {
const ImageChunk *fr0 = (const ImageChunk *)g_nancy->getEngineData("FR0");
assert(fr0);
imageName = fr0->imageName;
+ } else {
+ auto *taskData = GetEngineData(TASK);
+ assert(taskData);
+ imageName = taskData->imageName;
}
auto *mapData = GetEngineData(MAP);
Commit: 54b087a8fce98eaa620512b8aa34823be753797b
https://github.com/scummvm/scummvm/commit/54b087a8fce98eaa620512b8aa34823be753797b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:14+03:00
Commit Message:
NANCY: NANCY13: Show subtitles for Nancy's messages
In Nancy 13, most ScrollTextBox subtitles were missing (chiefly Nancy's
narration, observations, and tutorial lines); conversation subtitles
were fine as they render through a separate ConversationPopup widget.
Nancy13 dropped the explicit closed-caption record (PlaySoundCC) used
by earlier games: instead, when any sound plays, the engine looks its
name up in the CVTX text chunks and shows any match in the game
textbox as a subtitle.
Changed paths:
engines/nancy/action/soundrecords.cpp
engines/nancy/action/soundrecords.h
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index 1b0609006ed..1814f86a04d 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -48,6 +48,31 @@ static uint selectRandomSound(Common::Array<Common::String> &soundNames) {
return g_nancy->_randomSource->getRandomNumber(soundNames.size() - 1);
}
+// Nancy13+ subtitles are no longer stored inside the sound record. Instead, the
+// engine looks the played sound's name up in the CVTX text chunks when the sound
+// starts and, if a matching entry exists, shows it in the game textbox. The
+// autotext chunk (narration/observations) is searched first, then the convo chunk.
+static Common::String resolveSoundSubtitle(const Common::String &soundName) {
+ if (soundName.empty() || soundName.equalsIgnoreCase("NO SOUND")) {
+ return Common::String();
+ }
+
+ const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
+ if (autotext) {
+ Common::String text = autotext->texts.getValOrDefault(soundName, "");
+ if (!text.empty()) {
+ return text;
+ }
+ }
+
+ const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+ if (convo) {
+ return convo->texts.getValOrDefault(soundName, "");
+ }
+
+ return Common::String();
+}
+
void SetVolume::readData(Common::SeekableReadStream &stream) {
channel = stream.readUint16LE();
volume = stream.readByte();
@@ -163,6 +188,9 @@ void PlaySound::readDataNancy13(Common::SeekableReadStream &stream) {
_sound.volume = stream.readUint16LE();
_sound.name = names[selectRandomSound(names)];
+
+ // Subtitles are keyed by the played sound's name in the CVTX chunks.
+ _ccText = resolveSoundSubtitle(_sound.name);
}
// No inline SoundEffectDescription anymore, and the scene change is just a
@@ -186,6 +214,14 @@ void PlaySound::execute() {
g_nancy->_sound->loadSound(_sound, &_soundEffect);
g_nancy->_sound->playSound(_sound);
+ // Nancy13+ shows the sound's subtitle (resolved from its name) in the
+ // game textbox. Earlier games use the explicit PlaySoundCC records instead.
+ if (g_nancy->getGameType() >= kGameTypeNancy13 && !_ccText.empty() &&
+ ConfMan.getBool("subtitles", ConfMan.getActiveDomainName())) {
+ NancySceneState.getTextbox().clear();
+ NancySceneState.getTextbox().addTextLine(_ccText);
+ }
+
if (g_nancy->getGameType() >= kGameTypeNancy13) {
// Nancy13 sets a list of event flags.
for (const FlagDescription &flag : _flags) {
@@ -257,7 +293,9 @@ void PlaySoundCC::readData(Common::SeekableReadStream &stream) {
}
void PlaySoundCC::execute() {
- if (_state == kBegin && _ccText.size() && ConfMan.getBool("subtitles", ConfMan.getActiveDomainName())) {
+ // Nancy13+ resolves the subtitle from the sound name in PlaySound::execute.
+ if (g_nancy->getGameType() < kGameTypeNancy13 &&
+ _state == kBegin && _ccText.size() && ConfMan.getBool("subtitles", ConfMan.getActiveDomainName())) {
NancySceneState.getTextbox().clear();
NancySceneState.getTextbox().addTextLine(_ccText);
}
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index ef60b81c1d2..bf69d95bd39 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -114,6 +114,11 @@ public:
Common::Array<FlagDescription> _flags;
byte _afterSoundAction = 0; // Nancy13+: 1 dismisses the text box overlay
+ // Subtitle shown in the game textbox while the sound plays. In Nancy13+ this
+ // is resolved from the sound name (see readDataNancy13); earlier games store
+ // it explicitly in the closed-caption records below.
+ Common::String _ccText;
+
Common::String getRecordExtraInfo() const override { return Common::String::format("Scene %d", _sceneChange.sceneID); }
protected:
@@ -132,8 +137,6 @@ public:
void readCCText(Common::SeekableReadStream &stream, Common::String &out);
- Common::String _ccText;
-
protected:
Common::String getRecordTypeName() const override;
};
Commit: 8d46b754bdca54f6323333d6c2975ff75e53086b
https://github.com/scummvm/scummvm/commit/8d46b754bdca54f6323333d6c2975ff75e53086b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:15+03:00
Commit Message:
NANCY: NANCY12: Add detection entry for the French version
Fix #17000
Changed paths:
engines/nancy/detection_tables.h
diff --git a/engines/nancy/detection_tables.h b/engines/nancy/detection_tables.h
index 013840abf84..82462e15bfb 100644
--- a/engines/nancy/detection_tables.h
+++ b/engines/nancy/detection_tables.h
@@ -803,6 +803,17 @@ static const NancyGameDescription gameDescriptions[] = {
},
kGameTypeNancy12
},
+ { // MD5 by tunnelsociety from bug #17000
+ {
+ "nancy12", nullptr,
+ AD_ENTRY1s("ciftree.dat", "816db22b5c4d5211336b742fee8ea080", 45822011),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_UNSTABLE | ADGF_DROPPLATFORM,
+ NANCY8_GUIOPTIONS
+ },
+ kGameTypeNancy12
+ },
{ // MD5 by bluegr
{
"nancy13", nullptr,
Commit: 4d66c4f005a47109019cf999fdeb0b5907db1ae7
https://github.com/scummvm/scummvm/commit/4d66c4f005a47109019cf999fdeb0b5907db1ae7
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:16+03:00
Commit Message:
NANCY: Filter invalid file name characters when dumping action records
Changed paths:
engines/nancy/console.cpp
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index ce75446f40d..9f076c414e2 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -704,11 +704,14 @@ bool NancyConsole::Cmd_actionRecordExport(int argc, const char **argv) {
char descBuf[48];
chunk->read(descBuf, 48);
descBuf[47] = '\0';
+ Common::String desc(descBuf);
+ desc.replace('/', '-');
+ desc.replace('\\', '-');
byte ARType = chunk->readByte();
chunk->skip(1); // execType
Common::DumpFile f;
- Common::String filename = Common::String::format("%s_ar_%d_scene_%d_%d_%s.dat", g_nancy->getGameId(), ARType, sceneId, recordId, descBuf);
+ Common::String filename = Common::String::format("%s_ar_%d_scene_%d_%d_%s.dat", g_nancy->getGameId(), ARType, sceneId, recordId, desc.c_str());
f.open(Common::Path(filename));
f.writeStream(chunk, chunk->size() - 50);
f.close();
Commit: 1da8ef908320a4cf352fe060a2a5d20ed23fc729
https://github.com/scummvm/scummvm/commit/1da8ef908320a4cf352fe060a2a5d20ed23fc729
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:17+03:00
Commit Message:
NANCY: NANCY12: Implement new functionality in CollisionPuzzle/TileMove
Fixes the Parakeet tile move puzzle
Changed paths:
engines/nancy/action/puzzle/collisionpuzzle.cpp
engines/nancy/action/puzzle/collisionpuzzle.h
diff --git a/engines/nancy/action/puzzle/collisionpuzzle.cpp b/engines/nancy/action/puzzle/collisionpuzzle.cpp
index 41b918aa207..15f6a7e4d16 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.cpp
+++ b/engines/nancy/action/puzzle/collisionpuzzle.cpp
@@ -91,15 +91,21 @@ void CollisionPuzzle::init() {
continue;
}
+ // Start the solve piece in the slot two cells left of the grid
+ int pieceX = (int)x;
+ if (id == 6 && _tileMoveSolveStartsInSlot) {
+ pieceX -= 2;
+ }
+
newPiece._drawSurface.create(_image, _pieceSrcs[id - 1]);
- Common::Rect pos = getScreenPosition(Common::Point(x, y));
+ Common::Rect pos = getScreenPosition(Common::Point(pieceX, y));
if (_lineWidth == 6) {
pos.translate(-1, 0); // Improvement
}
pos.setWidth(newPiece._drawSurface.w);
pos.setHeight(newPiece._drawSurface.h);
newPiece.moveTo(pos);
- newPiece._gridPos = Common::Point(x, y);
+ newPiece._gridPos = Common::Point(pieceX, y);
newPiece.setVisible(true);
newPiece.setTransparent(true);
@@ -210,6 +216,11 @@ void CollisionPuzzle::readData(Common::SeekableReadStream &stream) {
_tileMoveExitPos.x = stream.readUint16LE();
_tileMoveExitIndex = stream.readUint16LE();
numPieces = 6;
+
+ // Nancy 12: when set, the solve piece starts in the slot left of the grid
+ if (g_nancy->getGameType() >= kGameTypeNancy12) {
+ _tileMoveSolveStartsInSlot = stream.readByte() == 1;
+ }
}
// Nancy 12 enlarged the Collision grid from 8x8 to 12x12; TileMove went 8 to 11 in Nancy 10
@@ -364,6 +375,12 @@ void CollisionPuzzle::execute() {
int16 w = g_nancy->getGameType() <= kGameTypeNancy9 ? _grid.size() : _grid[0].size();
int16 h = g_nancy->getGameType() <= kGameTypeNancy9 ? _grid[0].size() : _grid.size();
Common::Rect gridRect(w, h);
+
+ // The left start slot is outside the grid too, but isn't the exit
+ if (_tileMoveSolveStartsInSlot && pos.x < 0) {
+ return;
+ }
+
if (!posRect.contains(_tileMoveExitPos) && gridRect.contains(pos)) {
return;
}
diff --git a/engines/nancy/action/puzzle/collisionpuzzle.h b/engines/nancy/action/puzzle/collisionpuzzle.h
index de69f379c50..c92068b7188 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.h
+++ b/engines/nancy/action/puzzle/collisionpuzzle.h
@@ -90,6 +90,7 @@ protected:
Common::Point _tileMoveExitPos = Common::Point(-1, -1);
uint _tileMoveExitIndex = 0;
+ bool _tileMoveSolveStartsInSlot = false;
bool _usesExitButton = false;
Common::Rect _exitButtonSrc;
Commit: 9a2b2685feeac063e8ec9b00638f300ce85a8a67
https://github.com/scummvm/scummvm/commit/9a2b2685feeac063e8ec9b00638f300ce85a8a67
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:19+03:00
Commit Message:
NANCY: NANCY10: Fix display of mini vs full overlay in ScrollTextBox
Fix #16888
Changed paths:
engines/nancy/ui/scrolltextbox.cpp
engines/nancy/ui/scrolltextbox.h
diff --git a/engines/nancy/ui/scrolltextbox.cpp b/engines/nancy/ui/scrolltextbox.cpp
index 1a46e4cd0dd..9a858580149 100644
--- a/engines/nancy/ui/scrolltextbox.cpp
+++ b/engines/nancy/ui/scrolltextbox.cpp
@@ -141,14 +141,13 @@ void ScrollTextBox::drawContent() {
const uint16 fontID = _fontIDOverride != -1 ? (uint16)_fontIDOverride : _tboxData->highlightConversationFontID;
drawAllText(textBounds, 0, fontID, _tboxData->highlightConversationFontID);
- // Two discrete states: "mini" (<= 2 lines) is a short strip above the
- // taskbar with no scrollbar; "expanded" (> 2 lines) is the full overlay
- // covering the taskbar, with a scrollbar.
+ // Mini strip vs full overlay with scrollbar, decided by whether the text
+ // overflows the viewport (not by line count): a caption that fits stays mini.
const Common::Rect viewport = textViewportLocal();
const int fullHeight = _fullPopupRect.height();
const int contentHeight = getInnerHeight();
- _expanded = _numDrawnLines > 2;
+ _expanded = contentHeight > viewport.height();
Common::Rect boxRect;
int visibleTextHeight;
@@ -161,8 +160,8 @@ void ScrollTextBox::drawContent() {
scrollY = (int)(_scrollPos * (contentHeight - visibleTextHeight));
}
} else {
- // Fixed two-line strip at the top of the overlay. Drop the chrome's
- // bottom margin so it's just tall enough for two lines.
+ // A strip at the top of the overlay, sized to the text with a two-line
+ // minimum. Drop the chrome's bottom margin so it hugs the content.
const Font *font = g_nancy->_graphics->getFont(fontID);
const int lineStep = font->getLineHeight() + font->getLineHeight() / 9;
const int twoLineContent = _tboxData->scrollbarDefaultPos.y + 2 * lineStep;
diff --git a/engines/nancy/ui/scrolltextbox.h b/engines/nancy/ui/scrolltextbox.h
index ffffb4d8e5d..9b798014a1b 100644
--- a/engines/nancy/ui/scrolltextbox.h
+++ b/engines/nancy/ui/scrolltextbox.h
@@ -95,8 +95,8 @@ private:
bool _scrollbarHovered;
int _scrollbarGrabOffset;
- // True in the expanded state (> 2 lines): full overlay with scrollbar.
- // False in the mini state (<= 2 lines): strip above the taskbar, no scrollbar.
+ // True when text overflows the viewport: full overlay with scrollbar.
+ // False when it fits: mini strip above the taskbar, sized to content.
bool _expanded;
int _fontIDOverride;
Commit: 7df1a23fbba5ff504dfad1e8fe40ff0fe6040f63
https://github.com/scummvm/scummvm/commit/7df1a23fbba5ff504dfad1e8fe40ff0fe6040f63
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:21+03:00
Commit Message:
NANCY: Fix a bug with cell width calculation in CollisionPuzzle
Fixes pieces overflowing off the grid
Changed paths:
engines/nancy/action/puzzle/collisionpuzzle.cpp
diff --git a/engines/nancy/action/puzzle/collisionpuzzle.cpp b/engines/nancy/action/puzzle/collisionpuzzle.cpp
index 15f6a7e4d16..a030bce221a 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.cpp
+++ b/engines/nancy/action/puzzle/collisionpuzzle.cpp
@@ -551,7 +551,8 @@ Common::Rect CollisionPuzzle::getScreenPosition(Common::Point gridPos) {
dest.bottom -= 1;
if (_puzzleType == kTileMove) {
- dest.setWidth(dest.width() / 2);
+ // The cell width is that of a 1-wide piece; halving the 2-wide sprite rounds up
+ dest.setWidth(_pieceSrcs[1].width() - 1);
}
dest.moveTo(_gridPos);
Commit: 63b12c3149e39e6709de9a90342f1f3f0ca92efa
https://github.com/scummvm/scummvm/commit/63b12c3149e39e6709de9a90342f1f3f0ca92efa
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-22T02:46:22+03:00
Commit Message:
NANCY: NANCY12: More work on the ResourceUse AR
Implements the payment screens and payment system in Nancy12
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 8b5f3fc3920..4aac737c170 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -22,6 +22,10 @@
#include "engines/nancy/nancy.h"
#include "engines/nancy/sound.h"
#include "engines/nancy/resource.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/font.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/input.h"
#include "engines/nancy/util.h"
#include "engines/nancy/action/miscrecords.h"
@@ -741,33 +745,165 @@ void HintSystem::selectHint() {
}
void ResourceUse::readData(Common::SeekableReadStream &stream) {
- _resourceIndex = stream.readSint16LE(); // which UIRC resource to change
- _amount = stream.readSint16LE(); // value / delta
- _mode = stream.readByte(); // 0 = set, non-zero = add
- _flag.label = stream.readSint16LE(); // event flag set on success
+ _resourceIndex = stream.readSint16LE(); // which UIRC resource to change
+ _amount = stream.readSint16LE(); // value / delta
+ _mode = stream.readByte(); // 0 = set, non-zero = add
+ _flag.label = stream.readSint16LE(); // event flag set on success
_flag.flag = stream.readByte();
- // The rest of the 113-byte block (success/fail sounds, an optional scene
- // change at +0x5b, and a transient "UIResource_Overlay" sprite) isn't
- // handled yet.
- stream.skip(0x71 - 8);
+
+ readFilename(stream, _failSoundName); // played when the change can't be applied
+ readFilename(stream, _successSoundName); // played when it is applied
+
+ readRect(stream, _paymentHotspot);
+ _useResourceCursor = stream.readByte();
+
+ _sceneID = stream.readUint16LE();
+ _continueSceneSound = stream.readUint16LE();
+
+ _drawResourceOverlay = stream.readByte() != 0;
+ _overlayDest.x = stream.readSint32LE();
+ _overlayDest.y = stream.readSint32LE();
+
+ _drawResourceValue = stream.readByte() != 0;
+ _valueDest.x = stream.readSint32LE();
+ _valueDest.y = stream.readSint32LE();
}
-void ResourceUse::execute() {
+void ResourceUse::init() {
+ Common::Rect screenBounds = NancySceneState.getViewport().getBounds();
+ _drawSurface.create(screenBounds.width(), screenBounds.height(), g_nancy->_graphics->getInputPixelFormat());
+ _drawSurface.clear(g_nancy->_graphics->getTransColor());
+ setTransparent(true);
+ moveTo(screenBounds);
+
+ const UIRC *uirc = GetEngineData(UIRC);
+ const bool haveItem = uirc && _resourceIndex >= 0 && (uint)_resourceIndex < uirc->items.size();
+
+ if (haveItem && _drawResourceOverlay) {
+ const UIRC::ItemRecord &item = uirc->items[_resourceIndex];
+ Graphics::ManagedSurface image;
+ g_nancy->_resource->loadImage(item.overlayName, image);
+ image.setTransparentColor(_drawSurface.getTransparentColor());
+
+ Common::Rect src = item.rect;
+ Common::Rect dest(_overlayDest.x, _overlayDest.y,
+ _overlayDest.x + src.width(), _overlayDest.y + src.height());
+ _drawSurface.blitFrom(image, src, dest);
+ }
+
+ if (haveItem && _drawResourceValue) {
+ // The value is rendered with a '$' prefix and `unknown2` decimal places
+ // (Old Clock tracks cents), using the font selected by `unknown1`.
+ const UIRC::ItemRecord &item = uirc->items[_resourceIndex];
+ const Font *font = g_nancy->_graphics->getFont(item.unknown1);
+ if (font && item.unknown2 > 0) {
+ const int32 value = NancySceneState.getUIResource(_resourceIndex);
+ const Common::String text = Common::String::format("$%d.%02d", value / 100, value % 100);
+ font->drawString(&_drawSurface, text, _valueDest.x, _valueDest.y, screenBounds.width() - _valueDest.x, 0);
+ }
+ }
+
+ setVisible(_drawResourceOverlay || _drawResourceValue);
+ registerGraphics();
+}
+
+void ResourceUse::applyChange() {
if (_mode == 0) {
// Set the resource outright.
NancySceneState.setUIResource(_resourceIndex, _amount);
NancySceneState.setEventFlag(_flag);
+ _paymentApplied = true;
} else {
// Add the (signed) amount, but never let the resource go negative â
// the original skips the change (e.g. when Nancy can't afford it).
const int32 result = NancySceneState.getUIResource(_resourceIndex) + _amount;
- if (result >= 0) {
+ _paymentApplied = result >= 0;
+ if (_paymentApplied) {
NancySceneState.setUIResource(_resourceIndex, result);
NancySceneState.setEventFlag(_flag);
}
}
- _isDone = true;
+ // Start the sound matching the outcome, if there is one.
+ const Common::String &soundName = _paymentApplied ? _successSoundName : _failSoundName;
+ if (!soundName.empty() && !soundName.equalsIgnoreCase("NO SOUND") && !soundName.equalsIgnoreCase("NO SOUND PLAY")) {
+ _sound.name = soundName;
+ _sound.numLoops = 1;
+
+ const UIRC *uirc = GetEngineData(UIRC);
+ if (uirc && _resourceIndex >= 0 && (uint)_resourceIndex < uirc->items.size()) {
+ _sound.channelID = uirc->items[_resourceIndex].soundChannel;
+ _sound.volume = uirc->items[_resourceIndex].soundVolume;
+ }
+
+ g_nancy->_sound->loadSound(_sound);
+ g_nancy->_sound->playSound(_sound);
+ _hasSound = true;
+ }
+
+ _paymentResolved = true;
+}
+
+void ResourceUse::handleInput(NancyInput &input) {
+ if (!_interactive || _state != kRun || _paymentResolved) {
+ return;
+ }
+
+ if (NancySceneState.getViewport().convertViewportToScreen(_paymentHotspot).contains(input.mousePos)) {
+ g_nancy->_cursor->setCursorType(_useResourceCursor ?
+ (CursorManager::CursorType)(_resourceIndex + 0x1f) : CursorManager::kNormal, true);
+
+ if (input.input & NancyInput::kLeftMouseButtonUp) {
+ applyChange();
+ }
+ }
+}
+
+void ResourceUse::execute() {
+ switch (_state) {
+ case kBegin:
+ init();
+
+ // A non-degenerate hotspot means the player has to click it (e.g. a coin
+ // slot) to trigger the change; otherwise it happens immediately.
+ _interactive = _paymentHotspot.top != _paymentHotspot.bottom;
+ if (!_interactive) {
+ applyChange();
+ }
+
+ _state = kRun;
+ break;
+ case kRun:
+ // Interactive changes wait for the player to click the hotspot.
+ if (_interactive && !_paymentResolved) {
+ return;
+ }
+
+ // Keep the overlay up until the outcome sound has finished.
+ if (_hasSound && g_nancy->_sound->isSoundPlaying(_sound)) {
+ return;
+ }
+
+ _state = kActionTrigger;
+ break;
+ case kActionTrigger:
+ if (_hasSound) {
+ g_nancy->_sound->stopSound(_sound);
+ }
+
+ setVisible(false);
+
+ // Only a successful change advances the scene.
+ if (_paymentApplied && _sceneID != kNoScene) {
+ SceneChangeDescription sceneChange;
+ sceneChange.sceneID = _sceneID;
+ sceneChange.continueSceneSound = _continueSceneSound;
+ NancySceneState.changeScene(sceneChange);
+ }
+
+ finishExecution();
+ break;
+ }
}
} // End of namespace Action
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 6391919dad2..9dfeac38e59 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -443,19 +443,53 @@ protected:
};
// Added in Nancy12 (AR 132). Adjusts a UI overlay resource (from the UIRC boot
-// chunk) at runtime.
-class ResourceUse : public ActionRecord {
+// chunk) at runtime -- e.g. paying coins from the purse (resource 0). Applying
+// the change plays a sound, optionally shows a transient overlay (a sprite and/or
+// the resource's numeric value) and can change scene on success.
+class ResourceUse : public RenderActionRecord {
public:
+ ResourceUse() : RenderActionRecord(7) {}
+
+ void init() override;
void readData(Common::SeekableReadStream &stream) override;
void execute() override;
+ void handleInput(NancyInput &input) override;
+
+ bool isViewportRelative() const override { return true; }
protected:
Common::String getRecordTypeName() const override { return "ResourceUse"; }
+ // Applies the resource change (respecting affordability), sets the event
+ // flag and starts the matching outcome sound.
+ void applyChange();
+
int16 _resourceIndex = 0;
int16 _amount = 0;
byte _mode = 0; // 0 = set the resource, non-zero = add (clamped to >= 0)
FlagDescription _flag; // event flag set when the change is applied
+
+ Common::String _failSoundName; // played when the change can't be applied
+ Common::String _successSoundName; // played when it is applied
+
+ // When this rect is non-degenerate the change is interactive: the player
+ // clicks it (e.g. a coin slot) to pay. A degenerate rect applies at once.
+ Common::Rect _paymentHotspot;
+ byte _useResourceCursor = 0; // 0 = normal cursor, else the resource's own hover cursor
+
+ uint16 _sceneID = kNoScene; // scene entered on success (9999 = none)
+ uint16 _continueSceneSound = 0;
+
+ bool _drawResourceOverlay = false; // blit the resource's UIRC sprite
+ Common::Point _overlayDest;
+ bool _drawResourceValue = false; // draw the resource's numeric value
+ Common::Point _valueDest;
+
+ SoundDescription _sound;
+ bool _hasSound = false;
+ bool _interactive = false;
+ bool _paymentResolved = false;
+ bool _paymentApplied = false;
};
} // End of namespace Action
More information about the Scummvm-git-logs
mailing list