[Scummvm-git-logs] scummvm master -> ac491547e4d1c385ee310a71399acd64a18a09d7
mduggan
noreply at scummvm.org
Sat Jan 4 05:15:17 UTC 2025
This automated email contains information about 21 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
f0c3ee3b4b DGDS: Fix fade op in TTM slightly
ce0439e01f DGDS: Fix Willy dialog rendering
408127ff35 DGDS: Select correct item from inventory page 2
ec271639bb DGDS: Slight compatibility fix for unknown sequences
5caaf6dfdd DGDS: More small fixes for TTM in Willy Beamish
7e2c29c88c DGDS: Fix potential crash on menu
1248fbdea2 DGDS: Small mouse fixes for look mode
0005ca1a96 DGDS: Show Willy's dollars correctly in inventory
6febe657ef DGDS: Don't run scripts in Willy Beamish inventory
e4d1813663 DGDS: Fix picking up coin in Willy Beamish
b47874294c DGDS: Fix skipping nested if ADS blocks using 0x13C1
60ff040f79 DGDS: Add another debug image dump point
5e1d3d69bc DGDS: Add workaround for disappearing cola vendor in Willy Beamish
216d1a34c6 DGDS: Add support for targeting mode in Willy Beamish
70609012c8 DGDS: Fix comment typo
4742182e34 DGDS: Fix Willy Beamish rendering at bar
9eb9b6f273 DGDS: Fix mixing inventory items in Willy Beamish
29d92ff5dd DGDS: Properly clip rectangle line edges
cbcee066c0 DGDS: Fix selection of partial line dialog actions
91908bbdc9 DGDS: Reset look mode on scene change
ac491547e4 DGDS: Switch to 15 game frames per second
Commit: f0c3ee3b4ba45f04c1cb9cedb3869ce6be3d99b4
https://github.com/scummvm/scummvm/commit/f0c3ee3b4ba45f04c1cb9cedb3869ce6be3d99b4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix fade op in TTM slightly
Changed paths:
engines/dgds/ttm.cpp
engines/dgds/ttm.h
diff --git a/engines/dgds/ttm.cpp b/engines/dgds/ttm.cpp
index 744f6addaf1..f2d07a32bb7 100644
--- a/engines/dgds/ttm.cpp
+++ b/engines/dgds/ttm.cpp
@@ -826,16 +826,23 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
g_system->delayMillis(5);
}
}
- if (ivals[1] == 256) {
- // Clear the background only if we faded everything??
+
+ // Logic here is different in Dragon + HOC. They clear all buffers after fade
+ if (_vm->getGameId() == GID_DRAGON || _vm->getGameId() == GID_HOC) {
+ _vm->_compositionBuffer.fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
_vm->getBackgroundBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ } else {
+ // In Willy Beamish, copy comp->screen and comp->back
+ g_system->copyRectToScreen(_vm->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ g_system->updateScreen();
+ _vm->getBackgroundBuffer().blitFrom(_vm->_compositionBuffer);
}
- // Other buffers are always cleared.
+ // Stored area is cleared in all games.
_vm->getStoredAreaBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
- _vm->_compositionBuffer.fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
- // Reset to previous palette - except in Willy Beamish?
- if (_vm->getGameId() != GID_WILLY)
- _vm->getGamePals()->setFade(ivals[0], ivals[1], ivals[2], 0);
+
+ // Reset to previous palette.
+ _vm->getGamePals()->setPalette();
+
break;
case 0x4120: { // FADE IN: colorno,ncolors,targetcol,speed:byte
if (seq._executed) // this is a one-shot op.
@@ -844,10 +851,10 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
if (ivals[3] == 0) {
_vm->getGamePals()->setPalette();
} else {
- for (int i = SCREEN_WIDTH; i > 0; i -= ivals[3]) {
+ for (int i = 320; i > 0; i -= ivals[3]) {
int fade = MAX(0, MIN(i / 5, 63));
_vm->getGamePals()->setFade(ivals[0], ivals[1], ivals[2], fade * 4);
- if (i == SCREEN_WIDTH) {
+ if (i == 320) {
// update screen first to make the initial fade-in work
g_system->copyRectToScreen(_vm->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
}
diff --git a/engines/dgds/ttm.h b/engines/dgds/ttm.h
index d0c6228002f..f4063c4898f 100644
--- a/engines/dgds/ttm.h
+++ b/engines/dgds/ttm.h
@@ -82,7 +82,8 @@ enum TTMRunType {
// Note: this object needs to be safely copy-able - ADS opcodes 0x4000 and 0x4010 require it.
struct TTMSeq {
- TTMSeq() : _enviro(0), _seqNum(0), _startFrame(0), _lastFrame(0), _timeCut(0) {
+ TTMSeq() : _enviro(0), _seqNum(0), _startFrame(0), _lastFrame(0), _timeCut(0),
+ _currentBmpId(0), _currentGetPutId(0) {
// Other members are initialized in the reset function.
reset();
}
Commit: ce0439e01f592672159cb3bb1d08182f548973ee
https://github.com/scummvm/scummvm/commit/ce0439e01f592672159cb3bb1d08182f548973ee
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix Willy dialog rendering
Changed paths:
engines/dgds/dialog.cpp
diff --git a/engines/dgds/dialog.cpp b/engines/dgds/dialog.cpp
index cc87f2cbdef..8fc73e2f685 100644
--- a/engines/dgds/dialog.cpp
+++ b/engines/dgds/dialog.cpp
@@ -144,16 +144,15 @@ void Dialog::drawType2BackgroundChina(Graphics::ManagedSurface *dst, const Commo
void Dialog::drawType2BackgroundBeamish(Graphics::ManagedSurface *dst, const Common::String &title) {
// TODO: This needs updating.
- _state->_loc = DgdsRect(_rect.x + 12, _rect.y + 10, _rect.width - 24, _rect.height - 20);
+ _state->_loc = DgdsRect(_rect.x + 11, _rect.y + 10, _rect.width - 22, _rect.height - 20);
if (title.empty()) {
dst->fillRect(Common::Rect(Common::Point(_rect.x + 2, _rect.y + 2), _rect.width - 4, _rect.height - 4), 0);
RequestData::drawCorners(dst, 54, _rect.x, _rect.y, _rect.width, _rect.height);
} else {
dst->fillRect(Common::Rect(Common::Point(_rect.x + 2, _rect.y + 2), _rect.width - 4, _rect.height - 4), 0);
RequestData::drawCorners(dst, 46, _rect.x, _rect.y, _rect.width, _rect.height);
- // TODO: Maybe should measure the font?
- _state->_loc.y += 11;
- _state->_loc.height -= 11;
+ _state->_loc.y += 15;
+ _state->_loc.height -= 15;
RequestData::drawHeader(dst, _rect.x, _rect.y + 5, _rect.width, 2, title, _fontColor, false, 0, 0);
}
}
@@ -685,9 +684,11 @@ Common::String Dialog::dump(const Common::String &indent) const {
"%sDialog<num %d %s bgcol %d fcol %d selbgcol %d selfontcol %d fntsz %d flags 0x%02x frame %d delay %d next %d:%d talkdata %d:%d",
indent.c_str(), _num, _rect.dump("").c_str(), _bgColor, _fontColor, _selectionBgCol, _selectonFontCol, _fontSize,
_flags, _frameType, _time, _nextDialogFileNum, _nextDialogDlgNum, _talkDataNum, _talkDataHeadNum);
- str += indent + "state=" + (_state ? _state->dump("") : "null");
- str += "\n";
- str += DebugUtil::dumpStructList(indent, "actions", _action);
+ str += indent + " state=" + (_state ? _state->dump("") : "null");
+ if (_action.size()) {
+ str += "\n";
+ str += DebugUtil::dumpStructList(indent, "actions", _action);
+ }
str += "\n";
str += indent + " str='" + _str + "'>";
return str;
Commit: 408127ff351cdb1cc52f6b4780b042b6223cf658
https://github.com/scummvm/scummvm/commit/408127ff351cdb1cc52f6b4780b042b6223cf658
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Select correct item from inventory page 2
Changed paths:
engines/dgds/inventory.cpp
diff --git a/engines/dgds/inventory.cpp b/engines/dgds/inventory.cpp
index 3a7fd433b99..22dd4bf9887 100644
--- a/engines/dgds/inventory.cpp
+++ b/engines/dgds/inventory.cpp
@@ -347,10 +347,10 @@ GameItem *Inventory::itemUnderMouse(const Common::Point &pt) {
if (_itemArea->containsPoint(pt)) {
const int imgAreaX = _itemArea->_parentX + _itemArea->_x;
const int imgAreaY = _itemArea->_parentY + _itemArea->_y;
- const int numacross = _itemArea->_width / _itemArea->_xStep;
+ const int numAcross = _itemArea->_width / _itemArea->_xStep;
const int itemrow = (pt.y - imgAreaY) / _itemArea->_yStep;
const int itemcol = (pt.x - imgAreaX) / _itemArea->_xStep;
- int itemnum = numacross * itemrow + itemcol;
+ int itemnum = numAcross * itemrow + itemcol + _itemOffset;
for (auto &item: items) {
if (!isItemInInventory(item))
Commit: ec271639bb9a491d2d5241fd8018a3f9e80628c7
https://github.com/scummvm/scummvm/commit/ec271639bb9a491d2d5241fd8018a3f9e80628c7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Slight compatibility fix for unknown sequences
It's possible nothing ever relies on this behavior, but it's closer to the
originals.
Changed paths:
engines/dgds/ads.cpp
diff --git a/engines/dgds/ads.cpp b/engines/dgds/ads.cpp
index a13d36a47e8..fb4d25d6d26 100644
--- a/engines/dgds/ads.cpp
+++ b/engines/dgds/ads.cpp
@@ -90,9 +90,13 @@ bool ADSInterpreter::load(const Common::String &filename) {
dgds.parse(_adsData, detailfile);
for (const auto &file : _adsData->_scriptNames) {
+ // Environments are numbered based on this list, so extend the list even if there
+ // is no file to load. Eg, Willy Beamish TVO.ADS uses env 5 but only loads 4
+ // files (and has 1 empty name)
+ _adsData->_scriptEnvs.resize(_adsData->_scriptEnvs.size() + 1);
if (file.empty())
continue;
- _adsData->_scriptEnvs.resize(_adsData->_scriptEnvs.size() + 1);
+
debug(1, " load TTM %s to env %d", file.c_str(), _adsData->_scriptEnvs.size());
TTMEnviro &data = _adsData->_scriptEnvs.back();
data._enviro = _adsData->_scriptEnvs.size();
@@ -353,42 +357,40 @@ bool ADSInterpreter::logicOpResult(uint16 code, const TTMEnviro *env, const TTMS
int16 seqNum = seq ? seq->_seqNum : 0;
const char *optype = (code < 0x1300 ? "while" : "if");
- assert(seq || (code & 0xFF) >= 0x80);
-
Globals *globals = DgdsEngine::getInstance()->getGameGlobals();
switch (code) {
case 0x1010: // WHILE paused
case 0x1310: // IF paused, 2 params
debugN(10, "ADS 0x%04x: %s paused env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
- return seq->_runFlag == kRunTypePaused;
+ return seq && seq->_runFlag == kRunTypePaused;
case 0x1020: // WHILE not paused
case 0x1320: // IF not paused, 2 params
debugN(10, "ADS 0x%04x: %s not paused env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
- return seq->_runFlag != kRunTypePaused;
+ return !seq || seq->_runFlag != kRunTypePaused;
case 0x1030: // WHILE NOT PLAYED
case 0x1330: // IF_NOT_PLAYED, 2 params
debugN(10, "ADS 0x%04x: %s not played env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
- return !seq->_runPlayed;
+ return !seq || !seq->_runPlayed;
case 0x1040: // WHILE PLAYED
case 0x1340: // IF_PLAYED, 2 params
debugN(10, "ADS 0x%04x: %s played env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
- return seq->_runPlayed;
+ return seq && seq->_runPlayed;
case 0x1050: // WHILE FINISHED
case 0x1350: // IF_FINISHED, 2 params
debugN(10, "ADS 0x%04x: %s finished env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
- return seq->_runFlag == kRunTypeFinished;
+ return seq && seq->_runFlag == kRunTypeFinished;
case 0x1060: // WHILE NOT RUNNING
case 0x1360: { // IF_NOT_RUNNING, 2 params
debugN(10, "ADS 0x%04x: %s not running env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
// Dragon only checks kRunTypeStopped, HoC onward also check for kRunTypeFinished
bool isDragon = _vm->getGameId() == GID_DRAGON;
- return seq->_runFlag == kRunTypeStopped || (!isDragon && seq->_runFlag == kRunTypeFinished);
+ return !seq || seq->_runFlag == kRunTypeStopped || (!isDragon && seq->_runFlag == kRunTypeFinished);
}
case 0x1070: // WHILE RUNNING
case 0x1370: // IF_RUNNING, 2 params
debugN(10, "ADS 0x%04x: %s running env %d seq %d (%s)", code, optype, envNum, seqNum, tag);
- return seq->_runFlag == kRunTypeKeepGoing || seq->_runFlag == kRunTypeMulti || seq->_runFlag == kRunTypeTimeLimited;
+ return seq && (seq->_runFlag == kRunTypeKeepGoing || seq->_runFlag == kRunTypeMulti || seq->_runFlag == kRunTypeTimeLimited);
case 0x1080:
case 0x1090:
warning("Unimplemented IF/WHILE operation 0x%x", code);
@@ -447,8 +449,11 @@ bool ADSInterpreter::handleLogicOp(uint16 code, Common::SeekableReadStream *scr)
seq = findTTMSeq(enviro, seqnum);
env = findTTMEnviro(enviro);
if (!seq) {
- warning("ADS if op referenced non-existent env %d seq %d", enviro, seqnum);
- return false;
+ warning("ADS if op 0x%04x referenced non-existent env %d seq %d", code, enviro, seqnum);
+ // Dragon always returns false for this. Others return result
+ // based on opcode - eg, "if stopped" is true for non-existent sequence.
+ if (DgdsEngine::getInstance()->getGameId() == GID_DRAGON)
+ return false;
}
} else {
// We load this into "enviro" but it's just the parameter of the op.
@@ -601,9 +606,8 @@ bool ADSInterpreter::handleOperation(uint16 code, Common::SeekableReadStream *sc
debug(10, "ADS 0x%04x: hit branch op endwhile", code);
_adsData->_hitBranchOp = true;
return false;
-
case 0x2000:
- case 0x2005: { // ADD sequence
+ case 0x2005: { // ADD sequence, set run count
enviro = scr->readUint16LE();
seqnum = scr->readUint16LE();
int16 runCount = scr->readSint16LE();
@@ -611,6 +615,10 @@ bool ADSInterpreter::handleOperation(uint16 code, Common::SeekableReadStream *sc
Common::SharedPtr<TTMSeq> seq = findTTMSeq(enviro, seqnum);
TTMEnviro *env = findTTMEnviro(enviro);
+
+ // Set this even if null, check for null below.
+ _currentTTMSeq = seq;
+
if (!seq || !env) {
// This happens in Willy Beamish FDD scene 24
warning("ADS op %04x invalid env + seq requested %d %d", code, enviro, seqnum);
@@ -623,7 +631,6 @@ bool ADSInterpreter::handleOperation(uint16 code, Common::SeekableReadStream *sc
if (code == 0x2000)
seq->_currentFrame = seq->_startFrame;
- _currentTTMSeq = seq;
if (runCount == 0) {
seq->_runFlag = kRunTypeKeepGoing;
} else if (runCount < 0) {
Commit: 5caaf6dfdd342c86cd58989f2dc7d961d367b236
https://github.com/scummvm/scummvm/commit/5caaf6dfdd342c86cd58989f2dc7d961d367b236
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: More small fixes for TTM in Willy Beamish
Changed paths:
engines/dgds/ttm.cpp
engines/dgds/ttm.h
diff --git a/engines/dgds/ttm.cpp b/engines/dgds/ttm.cpp
index f2d07a32bb7..2be330894b7 100644
--- a/engines/dgds/ttm.cpp
+++ b/engines/dgds/ttm.cpp
@@ -574,7 +574,7 @@ void TTMInterpreter::doDrawDialogForStrings(const TTMEnviro &env, const TTMSeq &
}
-bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts) {
+void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts) {
switch (op) {
case 0x0000: // FINISH: void
break;
@@ -594,6 +594,7 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
//
if (DgdsEngine::getInstance()->getGameId() == GID_WILLY) {
_vm->getStoredAreaBuffer().blitFrom(_vm->_compositionBuffer);
+ _vm->getBackgroundBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
} else {
_vm->getStoredAreaBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
_vm->getBackgroundBuffer().blitFrom(_vm->_compositionBuffer);
@@ -711,8 +712,8 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
_vm->_soundPlayer->stopSfxByNum(ivals[0]);
break;
case 0x2000: // SET (DRAW) COLORS: fgcol,bgcol:int [0..255]
- seq._drawColFG = static_cast<byte>(ivals[0]);
- seq._drawColBG = static_cast<byte>(ivals[1]);
+ seq._drawColFG = static_cast<byte>(ivals[0]); // aka Line Color
+ seq._drawColBG = static_cast<byte>(ivals[1]); // aka Fill Color
break;
case 0x2020: { // SET RANDOM SLEEP: min,max: int (eg, 60,300)
if (seq._executed) // this is a one-shot op.
@@ -912,12 +913,15 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
break;
}
case 0xa100: { // DRAW FILLED RECT x,y,w,h:int [0..320,0..200]
+ // Draw fill first
Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2], ivals[3]);
+ r.grow(-1);
r.clip(seq._drawWin);
- _vm->_compositionBuffer.fillRect(r, seq._drawColFG);
- break;
+ _vm->_compositionBuffer.fillRect(r, seq._drawColBG);
+ // FALL THROUGH to draw the border
}
case 0xa110: { // DRAW EMPTY RECT x1,y1,x2,y2:int
+ // FIXME: These lines should be clipped by seq._drawWin
const Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2] - 1, ivals[3] - 1);
_vm->_compositionBuffer.drawLine(r.left, r.top, r.right, r.top, seq._drawColFG);
_vm->_compositionBuffer.drawLine(r.left, r.bottom, r.right, r.bottom, seq._drawColFG);
@@ -956,12 +960,14 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
break;
case 0xa400: { // DRAW FILLED CIRCLE
+ // FIXME: This should honor seq._drawWin
int16 xr = ivals[2] / 2;
int16 yr = ivals[3] / 2;
Drawing::filledCircle(ivals[0] + xr, ivals[1] + yr, xr, yr, &_vm->_compositionBuffer, seq._drawColFG, seq._drawColBG);
break;
}
case 0xa420: { // DRAW EMPTY CIRCLE
+ // FIXME: This should honor seq._drawWin
int16 xr = ivals[2] / 2;
int16 yr = ivals[3] / 2;
Drawing::emptyCircle(ivals[0] + xr, ivals[1] + yr, xr, yr, &_vm->_compositionBuffer, seq._drawColFG);
@@ -1243,7 +1249,6 @@ bool TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
ttmOpName(op), sval.c_str());
break;
}
- return true;
}
Common::String TTMInterpreter::readTTMStringVal(Common::SeekableReadStream *scr) {
@@ -1312,9 +1317,7 @@ bool TTMInterpreter::run(TTMEnviro &env, TTMSeq &seq) {
}
debug(10, " (%s)", ttmOpName(op));
- bool opResult = handleOperation(env, seq, op, count, ivals, sval, pts);
- if (!opResult)
- break;
+ handleOperation(env, seq, op, count, ivals, sval, pts);
}
return true;
diff --git a/engines/dgds/ttm.h b/engines/dgds/ttm.h
index f4063c4898f..4436a297490 100644
--- a/engines/dgds/ttm.h
+++ b/engines/dgds/ttm.h
@@ -130,7 +130,7 @@ public:
static Common::String readTTMStringVal(Common::SeekableReadStream *scr);
protected:
- bool handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts);
+ void handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts);
int32 findGOTOTarget(const TTMEnviro &env, const TTMSeq &seq, int16 frame);
void doWipeOp(uint16 code, const TTMEnviro &env, const TTMSeq &seq, const Common::Rect &r);
int16 doOpInitCreditScroll(const Image *img);
Commit: 7e2c29c88cacfbf2bc555411454764bd1ab6275a
https://github.com/scummvm/scummvm/commit/7e2c29c88cacfbf2bc555411454764bd1ab6275a
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix potential crash on menu
Changed paths:
engines/dgds/menu.cpp
engines/dgds/menu.h
diff --git a/engines/dgds/menu.cpp b/engines/dgds/menu.cpp
index 757c5bc0267..ffb4bc76bdc 100644
--- a/engines/dgds/menu.cpp
+++ b/engines/dgds/menu.cpp
@@ -491,7 +491,7 @@ void Menu::onMouseLUp(const Common::Point &mouse) {
else if (_curMenu == kMenuSkipPlayIntro)
handleClickSkipPlayIntroMenu(mouse);
else
- handleClick(mouse);
+ isToggle &= handleClick(mouse);
if (isToggle)
drawMenu(_curMenu);
@@ -517,7 +517,7 @@ static void _toggleSoundType(Audio::Mixer::SoundType soundType) {
}
}
-void Menu::handleClick(const Common::Point &mouse) {
+bool Menu::handleClick(const Common::Point &mouse) {
DgdsEngine *engine = DgdsEngine::getInstance();
int currentScene = engine->getScene()->getNum();
Gadget *gadget = getClickedMenuItem(mouse);
@@ -608,10 +608,12 @@ void Menu::handleClick(const Common::Point &mouse) {
case kMenuGameOverRestore:
case kMenuIntroRestore:
case kMainMenuWillyLoad:
- if (g_engine->loadGameDialog())
+ if (g_engine->loadGameDialog()) {
hideMenu();
- else
+ return false;
+ } else {
drawMenu(_curMenu);
+ }
break;
case kMenuFilesRestart:
case kMainMenuWillyRestart:
@@ -638,10 +640,12 @@ void Menu::handleClick(const Common::Point &mouse) {
// break;
case kMenuQuitYes:
g_engine->quitGame();
- break;
+ return false;
case kMenuRestartYes:
+ hideMenu();
engine->restartGame();
- break;
+ // don't draw the button coming back up.
+ return false;
case kMenuGameOverQuit:
case kMainMenuWillyQuit:
drawMenu(kMenuReallyQuit);
@@ -734,6 +738,7 @@ void Menu::handleClick(const Common::Point &mouse) {
debug(1, "Clicked ID %d", clickedMenuItem);
break;
}
+ return true;
}
void Menu::handleClickOptionsMenu(const Common::Point &mouse) {
diff --git a/engines/dgds/menu.h b/engines/dgds/menu.h
index aaecb358a93..81e835e0370 100644
--- a/engines/dgds/menu.h
+++ b/engines/dgds/menu.h
@@ -103,7 +103,7 @@ private:
void toggleGadget(int16 gadgetId, bool enable);
void configureGadget(MenuId menu, Gadget *gadget);
bool updateOptionsGadget(Gadget *gadget);
- void handleClick(const Common::Point &mouse);
+ bool handleClick(const Common::Point &mouse);
void handleClickOptionsMenu(const Common::Point &mouse);
void handleClickSkipPlayIntroMenu(const Common::Point &mouse);
void loadCredits();
Commit: 1248fbdea2354c964ef0d606dd4225cfcf036c98
https://github.com/scummvm/scummvm/commit/1248fbdea2354c964ef0d606dd4225cfcf036c98
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Small mouse fixes for look mode
Changed paths:
engines/dgds/scene.cpp
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index 36544c09286..5b2a623cb84 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -1178,8 +1178,11 @@ void SDSScene::mouseMoved(const Common::Point &pt) {
const HotArea *area = findAreaUnderMouse(pt);
DgdsEngine *engine = DgdsEngine::getInstance();
- int16 cursorNum = _isLookMode ? kDgdsMouseLook : kDgdsMouseGameDefault;
+ int16 cursorNum = kDgdsMouseGameDefault;
if (!dlg) {
+ // Update mouse cursor if no dialog visible
+ if (_isLookMode)
+ cursorNum = kDgdsMouseLook;
if (area)
cursorNum = _isLookMode ? area->_cursorNum2 : area->_cursorNum;
}
@@ -1210,6 +1213,10 @@ void SDSScene::mouseLDown(const Common::Point &pt) {
return;
}
+ // Don't start drag in look mode.
+ if (_isLookMode)
+ return;
+
HotArea *area = findAreaUnderMouse(pt);
if (!area)
return;
Commit: 0005ca1a96d7be73841c804962182597c7613c8d
https://github.com/scummvm/scummvm/commit/0005ca1a96d7be73841c804962182597c7613c8d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Show Willy's dollars correctly in inventory
Changed paths:
engines/dgds/dialog.cpp
engines/dgds/globals.cpp
engines/dgds/globals.h
diff --git a/engines/dgds/dialog.cpp b/engines/dgds/dialog.cpp
index 8fc73e2f685..628b596dc39 100644
--- a/engines/dgds/dialog.cpp
+++ b/engines/dgds/dialog.cpp
@@ -182,8 +182,12 @@ void Dialog::drawType2(Graphics::ManagedSurface *dst, DialogDrawStage stage) {
txt = _str;
}
- // Special case for HoC to update the Shekel count in their description.
- // This is how the original game does it too.
+ //
+ // Special case for HoC to update the Shekel count in their description
+ // and Willy Beamish update dollars
+ //
+ // This is how the original games do it too.
+ //
DgdsEngine *engine = DgdsEngine::getInstance();
if (_fileNum == 0x5d && _num == 0x32 && engine->getGameId() == GID_HOC) {
int16 shekels = engine->getGDSScene()->getGlobal(44);
@@ -191,6 +195,12 @@ void Dialog::drawType2(Graphics::ManagedSurface *dst, DialogDrawStage stage) {
uint32 offset = txt.find("###");
if (offset != Common::String::npos)
txt.replace(offset, 3, numstr);
+ } else if (_fileNum == 67 && _num == 9 && engine->getGameId() == GID_WILLY) {
+ int16 cents = engine->getGDSScene()->getGlobal(3);
+ const Common::String numstr = Common::String::format("%3d.%02d", cents / 100, cents % 100);
+ uint32 offset = txt.find("###.##");
+ if (offset != Common::String::npos)
+ txt.replace(offset, 6, numstr);
}
if (stage == kDlgDrawStageBackground) {
@@ -245,8 +255,8 @@ void Dialog::drawType3(Graphics::ManagedSurface *dst, DialogDrawStage stage) {
}
xradius = (yradius * 5) / 4;
- const int16 circlesAcross = usablex / xradius - 1;
- const int16 circlesDown = usabley / yradius - 1;
+ const int16 circlesAcross = MAX(1, usablex / xradius - 1);
+ const int16 circlesDown = MAX(1, usabley / yradius - 1);
uint16 x = _rect.x + xradius;
uint16 y = _rect.y + yradius;
diff --git a/engines/dgds/globals.cpp b/engines/dgds/globals.cpp
index b710eb8f949..a2b495db0a3 100644
--- a/engines/dgds/globals.cpp
+++ b/engines/dgds/globals.cpp
@@ -365,7 +365,7 @@ public:
WillyGlobals::WillyGlobals(Clock &clock) : Globals(clock),
- _trouble(4), _unk3(0), _invDrawTimeSkipButtons(0), _hideMouseCursor(0), _unk74(0), _unk75(300),
+ _trouble(4), _money(0), _invDrawTimeSkipButtons(0), _hideMouseCursor(0), _unk74(0), _unk75(300),
_palFade(255), _droppedItemNum(0), _characterStance(0), _characterPos(0), _unk81(3),
_unk82(1) {
_globals.push_back(new DetailLevelROGlobal(0x53));
@@ -380,14 +380,14 @@ WillyGlobals::WillyGlobals(Clock &clock) : Globals(clock),
_globals.push_back(new RWI16Global(0x4A, &_unk74));
_globals.push_back(new RWI16Global(0x05, &_hideMouseCursor));
_globals.push_back(new RWI16Global(0x04, &_invDrawTimeSkipButtons));
- _globals.push_back(new RWI16Global(0x03, &_unk3));
+ _globals.push_back(new RWI16Global(0x03, &_money));
_globals.push_back(new WillyTroubleGlobal(0x02, &_trouble));
}
Common::Error WillyGlobals::syncState(Common::Serializer &s) {
Globals::syncState(s);
s.syncAsSint16LE(_trouble);
- s.syncAsSint16LE(_unk3);
+ s.syncAsSint16LE(_money);
s.syncAsSint16LE(_invDrawTimeSkipButtons);
s.syncAsSint16LE(_hideMouseCursor);
s.syncAsSint16LE(_unk74);
diff --git a/engines/dgds/globals.h b/engines/dgds/globals.h
index f025ae4144f..652732ffde6 100644
--- a/engines/dgds/globals.h
+++ b/engines/dgds/globals.h
@@ -209,7 +209,7 @@ public:
private:
// Willy-specific globals
int16 _trouble;
- int16 _unk3;
+ int16 _money;
int16 _invDrawTimeSkipButtons;
int16 _hideMouseCursor;
int16 _unk74;
Commit: 6febe657ef7b676ec6427ee4313a7efab9b139de
https://github.com/scummvm/scummvm/commit/6febe657ef7b676ec6427ee4313a7efab9b139de
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Don't run scripts in Willy Beamish inventory
Changed paths:
engines/dgds/dgds.cpp
diff --git a/engines/dgds/dgds.cpp b/engines/dgds/dgds.cpp
index 5e95c5b6341..b5d881f5c38 100644
--- a/engines/dgds/dgds.cpp
+++ b/engines/dgds/dgds.cpp
@@ -670,7 +670,7 @@ Common::Error DgdsEngine::run() {
//
//_scene->drawActiveDialogBgs(&_compositionBuffer);
- if (!_inventory->isOpen() || _inventory->isZoomVisible())
+ if (!_inventory->isOpen() || (_inventory->isZoomVisible() && getGameId() != GID_WILLY))
_adsInterp->run();
if (mouseEvent != Common::EVENT_INVALID) {
Commit: e4d18136637ff289839defc0525bfce74cab64f6
https://github.com/scummvm/scummvm/commit/e4d18136637ff289839defc0525bfce74cab64f6
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix picking up coin in Willy Beamish
Changed paths:
engines/dgds/dgds.cpp
engines/dgds/scene.cpp
diff --git a/engines/dgds/dgds.cpp b/engines/dgds/dgds.cpp
index b5d881f5c38..4d1ce2d4b66 100644
--- a/engines/dgds/dgds.cpp
+++ b/engines/dgds/dgds.cpp
@@ -171,7 +171,9 @@ bool DgdsEngine::changeScene(int sceneNum) {
debug(1, "CHANGE SCENE %d -> %d (clock %s)", _scene->getNum(), sceneNum, _clock.dump().c_str());
- if (sceneNum == _scene->getNum()) {
+ // Willy Beamish relies on this resetting the scene when picking up the
+ // coin in the fountain (scene 56)
+ if (sceneNum == _scene->getNum() && getGameId() != GID_WILLY) {
warning("Tried to change from scene %d to itself, doing nothing.", sceneNum);
return false;
}
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index 5b2a623cb84..f3c6d451fcc 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -502,7 +502,7 @@ bool Scene::runOps(const Common::Array<SceneOp> ops, int16 addMinuites /* = 0 */
// need to stop as pointers are no longer valid.
//
int16 endSceneNum = engine->getScene()->getNum();
- return startSceneNum == endSceneNum;
+ return (startSceneNum == endSceneNum) && !sceneChanged;
}
Commit: b47874294c69196f6059dc6c77bba5f3fc803ad1
https://github.com/scummvm/scummvm/commit/b47874294c69196f6059dc6c77bba5f3fc803ad1
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix skipping nested if ADS blocks using 0x13C1
This fixes rendering of Willy as he's running from the babysitter, and probably
other random graphical glitches I've seen in the house.
Changed paths:
engines/dgds/ads.cpp
diff --git a/engines/dgds/ads.cpp b/engines/dgds/ads.cpp
index fb4d25d6d26..2ee558a9464 100644
--- a/engines/dgds/ads.cpp
+++ b/engines/dgds/ads.cpp
@@ -179,7 +179,7 @@ void ADSInterpreter::findUsedSequencesForSegment(int idx) {
// HoC and Dragon call a sequence "used" if there is any dependency on
// the sequence (reordering, if conditions), but to simplify the use of
// getStateForSceneOp, later games only call it "used" if it is directly
- // started.
+ // started (0x2000 or 0x2005).
//
int n_ops_to_check = (gameId == GID_DRAGON || gameId == GID_HOC) ? ARRAYSIZE(ADS_USED_SEQ_OPCODES) : 2;
while (opcode != 0xffff && _adsData->scr->pos() < _adsData->scr->size()) {
@@ -246,8 +246,9 @@ bool ADSInterpreter::skipSceneLogicBranch() {
} else if (op == 0 || op == 0xffff) {
// end of segment
return false;
- } else if ((op & 0xff0f) == 0x1300) {
- // A nested IF (0x13x0) block. Skip to endif ignoring else.
+ } else if ((op & 0xff00) == 0x1300) {
+ // A nested IF (0x13xx) block. Skip to endif ignoring else.
+ scr->skip(numArgs(op) * 2);
result = skipToEndIf();
} else {
scr->skip(numArgs(op) * 2);
@@ -270,16 +271,25 @@ bool ADSInterpreter::skipToElseOrEndif() {
}
bool ADSInterpreter::skipToEndIf() {
+ // This is similar to skipSceneLogicBranch, but it does not stop for ELSE,
+ // only ENDIF.
+ // This is used to skip nested IF blocks inside another skipped block.
Common::SeekableReadStream *scr = _adsData->scr;
while (scr->pos() < scr->size()) {
uint16 op = scr->readUint16LE();
// don't rewind - the calls to this should always return after the last op.
- if (op == 0x1510) // ENDIF
+ if (op == 0x1510) { // ENDIF
return true;
- else if (op == 0 || op == 0xffff)
+ } else if (op == 0 || op == 0xffff) {
return false;
-
- scr->skip(numArgs(op) * 2);
+ } else if ((op & 0xff00) == 0x1300) {
+ // A nested IF (0x13xx) block. Skip to endif ignoring else.
+ scr->skip(numArgs(op) * 2);
+ if (!skipToEndIf())
+ return false;
+ } else {
+ scr->skip(numArgs(op) * 2);
+ }
}
return false;
}
@@ -595,7 +605,7 @@ bool ADSInterpreter::handleOperation(uint16 code, Common::SeekableReadStream *sc
return handleLogicOp(code, scr);
case 0x1500: // ELSE / Skip to end-if, 0 params
debug(10, "ADS 0x%04x: else (skip to end if)", code);
- skipToElseOrEndif();
+ skipToEndIf();
_adsData->_hitBranchOp = true;
return true;
case 0x1510: // END IF 0 params
Commit: 60ff040f79d5fc7c074c6f0811bef200e2ebb2c4
https://github.com/scummvm/scummvm/commit/60ff040f79d5fc7c074c6f0811bef200e2ebb2c4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Add another debug image dump point
Changed paths:
engines/dgds/dgds.cpp
diff --git a/engines/dgds/dgds.cpp b/engines/dgds/dgds.cpp
index 4d1ce2d4b66..f1853ce17bd 100644
--- a/engines/dgds/dgds.cpp
+++ b/engines/dgds/dgds.cpp
@@ -672,6 +672,8 @@ Common::Error DgdsEngine::run() {
//
//_scene->drawActiveDialogBgs(&_compositionBuffer);
+ _dumpFrame(_compositionBuffer, "comp-before-ads");
+
if (!_inventory->isOpen() || (_inventory->isZoomVisible() && getGameId() != GID_WILLY))
_adsInterp->run();
Commit: 5e1d3d69bc5abef1ca1f1f9bbff04b438af459aa
https://github.com/scummvm/scummvm/commit/5e1d3d69bc5abef1ca1f1f9bbff04b438af459aa
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Add workaround for disappearing cola vendor in Willy Beamish
Changed paths:
engines/dgds/ads.cpp
diff --git a/engines/dgds/ads.cpp b/engines/dgds/ads.cpp
index 2ee558a9464..9e2d693d587 100644
--- a/engines/dgds/ads.cpp
+++ b/engines/dgds/ads.cpp
@@ -630,7 +630,6 @@ bool ADSInterpreter::handleOperation(uint16 code, Common::SeekableReadStream *sc
_currentTTMSeq = seq;
if (!seq || !env) {
- // This happens in Willy Beamish FDD scene 24
warning("ADS op %04x invalid env + seq requested %d %d", code, enviro, seqnum);
break;
}
@@ -638,8 +637,16 @@ bool ADSInterpreter::handleOperation(uint16 code, Common::SeekableReadStream *sc
debug(10, "ADS 0x%04x: add scene - env %d seq %d (%s) runCount %d prop %d", code,
enviro, seqnum, env->_tags.getValOrDefault(seqnum).c_str(), runCount, unk);
- if (code == 0x2000)
+ if (code == 0x2000) {
+ //
+ // HACKY WORKAROUND? If we change the frame here we should also reset
+ // the `executed` flag surely? Without this, the cola vendor disappears
+ // after you buy cola from him in Willy Beamish scene 31 (park).
+ //
+ if (seq->_currentFrame != seq->_startFrame && _vm->getGameId() == GID_WILLY)
+ seq->_executed = false;
seq->_currentFrame = seq->_startFrame;
+ }
if (runCount == 0) {
seq->_runFlag = kRunTypeKeepGoing;
@@ -875,7 +882,7 @@ bool ADSInterpreter::run() {
int16 flag = _adsData->_state[idx] & 0xfff7;
for (auto seq : _adsData->_usedSeqs[idx]) {
if (flag == 3) {
- debug(10, "ADS: Segment idx %d, Reset seq %d", idx, seq->_seqNum);
+ debug(10, "ADS: Segment idx %d, Reset env %d seq %d", idx, seq->_enviro, seq->_seqNum);
seq->reset();
} else {
if (flag != seq->_scriptFlag) {
Commit: 216d1a34c6dc7fbd3192b0ff3dd8f5ece8f665e0
https://github.com/scummvm/scummvm/commit/216d1a34c6dc7fbd3192b0ff3dd8f5ece8f665e0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Add support for targeting mode in Willy Beamish
This should make the baby sitter scene possible to complete.
Changed paths:
engines/dgds/scene.cpp
engines/dgds/scene.h
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index f3c6d451fcc..e85bee018ca 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -510,7 +510,7 @@ bool SDSScene::_dlgWithFlagLo8IsClosing = false;
DialogFlags SDSScene::_sceneDialogFlags = kDlgFlagNone;
SDSScene::SDSScene() : _num(-1), _dragItem(nullptr), _shouldClearDlg(false), _ignoreMouseUp(false),
-_field6_0x14(0), _rbuttonDown(false), _lbuttonDown(false), _isLookMode(false) {
+_field6_0x14(0), _rbuttonDown(false), _lbuttonDown(false), _lookMode(0) {
}
bool SDSScene::load(const Common::String &filename, ResourceManager *resourceManager, Decompressor *decompressor) {
@@ -1180,13 +1180,16 @@ void SDSScene::mouseMoved(const Common::Point &pt) {
int16 cursorNum = kDgdsMouseGameDefault;
if (!dlg) {
- // Update mouse cursor if no dialog visible
- if (_isLookMode)
+ // Update mouse cursor if no dialog visible.
+ // If lookMode is target (2) then activeItem will change it below.
+ if (_lookMode)
cursorNum = kDgdsMouseLook;
if (area)
- cursorNum = _isLookMode ? area->_cursorNum2 : area->_cursorNum;
+ cursorNum = _lookMode ? area->_cursorNum2 : area->_cursorNum;
}
+ GameItem *activeItem = engine->getGDSScene()->getActiveItem();
+
if (_dragItem) {
if (area && area->_objInteractionRectNum == 1) {
// drag over Willy Beamish
@@ -1195,9 +1198,10 @@ void SDSScene::mouseMoved(const Common::Point &pt) {
}
cursorNum = _dragItem->_iconNum;
- } else if (_rbuttonDown) {
- GameItem *activeItem = engine->getGDSScene()->getActiveItem();
- if (activeItem)
+ } else if (activeItem) {
+ // In HOC or Dragon you need to hold down right button to get the
+ // target cursor. In Willy Beamish it is look mode 2 (target)
+ if (_rbuttonDown || _lookMode == 2)
cursorNum = activeItem->_altCursor;
}
@@ -1213,8 +1217,8 @@ void SDSScene::mouseLDown(const Common::Point &pt) {
return;
}
- // Don't start drag in look mode.
- if (_isLookMode)
+ // Don't start drag in look/target mode.
+ if (_lookMode)
return;
HotArea *area = findAreaUnderMouse(pt);
@@ -1252,6 +1256,7 @@ static const ObjectInteraction *_findInteraction(const Common::Array<ObjectInter
void SDSScene::mouseLUp(const Common::Point &pt) {
_lbuttonDown = false;
+ DgdsEngine *engine = DgdsEngine::getInstance();
if (_ignoreMouseUp) {
debug(9, "Ignoring mouseup at %d,%d as it was used to clear a dialog", pt.x, pt.y);
@@ -1264,7 +1269,7 @@ void SDSScene::mouseLUp(const Common::Point &pt) {
return;
}
- if (_isLookMode) {
+ if (_lookMode == 1) {
rightButtonAction(pt);
return;
}
@@ -1276,7 +1281,6 @@ void SDSScene::mouseLUp(const Common::Point &pt) {
debug(9, "Mouse LUp on area %d (%d,%d,%d,%d) cursor %d cursor2 %d", area->_num, area->_rect.x, area->_rect.y,
area->_rect.width, area->_rect.height, area->_cursorNum, area->_cursorNum2);
- DgdsEngine *engine = DgdsEngine::getInstance();
if (!_rbuttonDown)
engine->setMouseCursor(area->_cursorNum);
@@ -1296,26 +1300,24 @@ void SDSScene::mouseLUp(const Common::Point &pt) {
if (haveInvBtn)
addInvButtonToHotAreaList();
} else {
- if (_rbuttonDown) {
+ const GameItem *activeItem = engine->getGDSScene()->getActiveItem();
+ if (activeItem && (_rbuttonDown || _lookMode == 2)) {
debug(1, " --> exec both-button click ops for area %d", area->_num);
// A both-button-click event, find the interaction list.
- const GameItem *activeItem = engine->getGDSScene()->getActiveItem();
- if (activeItem) {
- if (!runOps(activeItem->onBothButtonsOps))
- return;
+ if (!runOps(activeItem->onBothButtonsOps))
+ return;
- const GameItem *destItem = dynamic_cast<const GameItem *>(area);
- const ObjectInteraction *i;
- if (destItem) {
- i =_findInteraction(gds->getObjInteractions2(), activeItem->_num, area->_num);
- } else {
- i = _findInteraction(_objInteractions2, activeItem->_num, area->_num);
- }
- if (i) {
- debug(1, " --> exec %d both-click ops for item combo %d", i->opList.size(), activeItem->_num);
- if (!runOps(i->opList, engine->getGameGlobals()->getGameMinsToAddOnObjInteraction()))
- return;
- }
+ const GameItem *destItem = dynamic_cast<const GameItem *>(area);
+ const ObjectInteraction *i;
+ if (destItem) {
+ i =_findInteraction(gds->getObjInteractions2(), activeItem->_num, area->_num);
+ } else {
+ i = _findInteraction(_objInteractions2, activeItem->_num, area->_num);
+ }
+ if (i) {
+ debug(1, " --> exec %d both-click ops for item combo %d", i->opList.size(), activeItem->_num);
+ if (!runOps(i->opList, engine->getGameGlobals()->getGameMinsToAddOnObjInteraction()))
+ return;
}
} else {
debug(1, " --> exec %d click ops for area %d", area->onLClickOps.size(), area->_num);
@@ -1416,9 +1418,16 @@ void SDSScene::mouseRUp(const Common::Point &pt) {
return;
}
- if (DgdsEngine::getInstance()->getGameId() == GID_WILLY) {
- // Willy toggles between look/act mode on right click
- _isLookMode = !_isLookMode;
+ DgdsEngine *engine = DgdsEngine::getInstance();
+ if (engine->getGameId() == GID_WILLY) {
+ // Willy toggles between look/act/target mode on right click
+ if (engine->getGDSScene()->getActiveItem()) {
+ _lookMode++;
+ if (_lookMode > 2)
+ _lookMode = 0;
+ } else {
+ _lookMode = !_lookMode;
+ }
mouseMoved(pt);
} else {
// Other games do right-button action straight away.
diff --git a/engines/dgds/scene.h b/engines/dgds/scene.h
index b2c55a0de03..05d0c7790f1 100644
--- a/engines/dgds/scene.h
+++ b/engines/dgds/scene.h
@@ -369,7 +369,9 @@ private:
bool _ignoreMouseUp;
bool _lbuttonDown;
bool _rbuttonDown;
- bool _isLookMode;
+
+ /// Only changes in beamish - toggle between use (0), look (1) and target (2)
+ int16 _lookMode;
static bool _dlgWithFlagLo8IsClosing;
static DialogFlags _sceneDialogFlags;
Commit: 70609012c858ccf9ff1c68676d4ae0c36715160e
https://github.com/scummvm/scummvm/commit/70609012c858ccf9ff1c68676d4ae0c36715160e
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix comment typo
Changed paths:
engines/dgds/clock.cpp
diff --git a/engines/dgds/clock.cpp b/engines/dgds/clock.cpp
index e9a4b8821ad..95a79a5dcff 100644
--- a/engines/dgds/clock.cpp
+++ b/engines/dgds/clock.cpp
@@ -156,7 +156,7 @@ void Clock::update(bool gameRunning) {
_gameTicksUp = playTimeNow / MILLIS_PER_TIMER_TICK;
// Is there any reason to make this variable anything other than negative the other one??
- // There seems to be no other way to set them as the are RO globals.
+ // There seems to be no other way to set them as they are RO globals.
_gameTicksDown = -_gameTicksUp;
uint32 lastLastPlayTime = _lastPlayTime;
Commit: 4742182e346bc87287d0d9ba5f723495623c35e1
https://github.com/scummvm/scummvm/commit/4742182e346bc87287d0d9ba5f723495623c35e1
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix Willy Beamish rendering at bar
More ADS skip logic fixes.
Also stop loading so many ADS scripts on game load - we only need the active
one.
Changed paths:
engines/dgds/ads.cpp
diff --git a/engines/dgds/ads.cpp b/engines/dgds/ads.cpp
index 9e2d693d587..e06eb6d168f 100644
--- a/engines/dgds/ads.cpp
+++ b/engines/dgds/ads.cpp
@@ -271,9 +271,13 @@ bool ADSInterpreter::skipToElseOrEndif() {
}
bool ADSInterpreter::skipToEndIf() {
+ //
// This is similar to skipSceneLogicBranch, but it does not stop for ELSE,
- // only ENDIF.
- // This is used to skip nested IF blocks inside another skipped block.
+ // only ENDIF. It's used to skip nested IF blocks inside another skipped
+ // block.
+ // It should be called with the pointer at the op after the IF, which can
+ // also be AND or OR ops.
+ //
Common::SeekableReadStream *scr = _adsData->scr;
while (scr->pos() < scr->size()) {
uint16 op = scr->readUint16LE();
@@ -282,6 +286,14 @@ bool ADSInterpreter::skipToEndIf() {
return true;
} else if (op == 0 || op == 0xffff) {
return false;
+ } else if (op == 0x1420 || op == 0x1430) {
+ // AND or OR. Skip the IF that follows like a nested block
+ // (this should work even for multiple AND/OR conditions)
+ uint16 op2 = scr->readUint16LE();
+ if ((op2 & 0xff00) != 0x1300)
+ error("AND/OR ADS op not followed by another IF (got 0x%04x)", op2);
+ scr->skip(numArgs(op) * 2);
+ return skipToEndIf();
} else if ((op & 0xff00) == 0x1300) {
// A nested IF (0x13xx) block. Skip to endif ignoring else.
scr->skip(numArgs(op) * 2);
@@ -1085,8 +1097,12 @@ Common::Error ADSInterpreter::syncState(Common::Serializer &s) {
for (uint32 i = 0; i < numTexts; i++) {
Common::String txtName;
s.syncString(txtName);
- load(txtName);
- scriptNames.push_back(txtName);
+ // We save all the names, but we only actually need to reload one script -
+ // the most recent one. Do that at the end of this function.
+ if (s.getVersion() < 3) {
+ load(txtName);
+ scriptNames.push_back(txtName);
+ }
}
} else {
for (const auto &node : _adsTexts) {
@@ -1110,6 +1126,12 @@ Common::Error ADSInterpreter::syncState(Common::Serializer &s) {
}
s.syncString(activeScript);
+
+ if (s.getVersion() >= 3 && !activeScript.empty()) {
+ load(activeScript);
+ scriptNames.push_back(activeScript);
+ }
+
assert(activeScript.empty() || _adsTexts.contains(activeScript));
_adsData = activeScript.empty() ? nullptr : &_adsTexts[activeScript];
Commit: 9eb9b6f273c485198a4b4b76f67cd97ec5c73619
https://github.com/scummvm/scummvm/commit/9eb9b6f273c485198a4b4b76f67cd97ec5c73619
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix mixing inventory items in Willy Beamish
Changed paths:
engines/dgds/scene.cpp
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index e85bee018ca..26af2b9cfe9 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -1339,9 +1339,12 @@ void SDSScene::onDragFinish(const Common::Point &pt) {
DgdsEngine *engine = DgdsEngine::getInstance();
Globals *globals = engine->getGameGlobals();
GDSScene *gdsScene = engine->getGDSScene();
+ int16 dropSceneNum = _num;
if (engine->getGameId() == GID_WILLY) {
static_cast<WillyGlobals *>(globals)->setDroppedItemNum(dragItem->_num);
+ if (engine->getInventory()->isOpen())
+ dropSceneNum = 2;
}
runOps(dragItem->onDragFinishedOps, globals->getGameMinsToAddOnDragFinished());
@@ -1349,7 +1352,7 @@ void SDSScene::onDragFinish(const Common::Point &pt) {
// TODO: Both these loops are very similar.. there should be a cleaner way.
for (const auto &item : gdsScene->getGameItems()) {
- if (item._inSceneNum == _num && _isInRect(pt, item._rect)) {
+ if (item._inSceneNum == dropSceneNum && _isInRect(pt, item._rect)) {
debug(1, "Dragged item %d onto item %d @ (%d, %d)", dragItem->_num, item._num, pt.x, pt.y);
const ObjectInteraction *i = _findInteraction(gdsScene->getObjInteractions1(), dragItem->_num, item._num);
if (i) {
Commit: 29d92ff5ddb6569e9103c5c4d2e33d8a89bc807d
https://github.com/scummvm/scummvm/commit/29d92ff5ddb6569e9103c5c4d2e33d8a89bc807d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Properly clip rectangle line edges
Changed paths:
engines/dgds/drawing.cpp
engines/dgds/drawing.h
engines/dgds/ttm.cpp
diff --git a/engines/dgds/drawing.cpp b/engines/dgds/drawing.cpp
index d7146aee758..4db4328bbb4 100644
--- a/engines/dgds/drawing.cpp
+++ b/engines/dgds/drawing.cpp
@@ -43,6 +43,20 @@ void emptyCircle(int x, int y, int xr, int yr, Graphics::ManagedSurface *dst, by
Graphics::drawEllipse(x - xr, y - yr, x + xr, y + yr, fgcol, false, drawPixel, dst);
}
+void rectClipped(const Common::Rect &r, const Common::Rect &clip, Graphics::ManagedSurface *dst, byte color) {
+ if (r.top >= clip.top && r.top < clip.bottom)
+ dst->hLine(MAX(r.left, clip.left), r.top, MIN(r.right - 1, clip.right - 1), color);
+
+ if (r.bottom - 1 >= clip.top && r.bottom - 1 < clip.bottom)
+ dst->hLine(MAX(r.left, clip.left), r.bottom - 1, MIN(r.right - 1, clip.right - 1), color);
+
+ if (r.left >= clip.left && r.left < clip.right)
+ dst->vLine(r.left, MAX(r.top, clip.top), MIN(r.bottom - 1, clip.bottom - 1), color);
+
+ if (r.right - 1 >= clip.left && r.right - 1 < clip.right)
+ dst->vLine(r.right - 1, MAX(r.top, clip.top), MIN(r.bottom - 1, clip.bottom - 1), color);
+}
+
}
} // end namespace Dgds
diff --git a/engines/dgds/drawing.h b/engines/dgds/drawing.h
index 3636da7c893..e62405fbd67 100644
--- a/engines/dgds/drawing.h
+++ b/engines/dgds/drawing.h
@@ -36,6 +36,13 @@ namespace Drawing {
void filledCircle(int x, int y, int xr, int yr, Graphics::ManagedSurface *dst, byte fgcol, byte bgcol);
void emptyCircle(int x, int y, int xr, int yr, Graphics::ManagedSurface *dst, byte fgcol);
+ /**
+ * A non-filled rectangle but don't draw outside clipWin.
+ *
+ * Applies regular rect rounding rules and draws lines at right-1 and bottom-1, so that
+ * clipRect == rect will draw a complete rectangle
+ */
+ void rectClipped(const Common::Rect &rect, const Common::Rect &clipWin, Graphics::ManagedSurface *dst, byte color);
}
} // end namespace Dgds
diff --git a/engines/dgds/ttm.cpp b/engines/dgds/ttm.cpp
index 2be330894b7..b5a62f1e39f 100644
--- a/engines/dgds/ttm.cpp
+++ b/engines/dgds/ttm.cpp
@@ -921,12 +921,8 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
// FALL THROUGH to draw the border
}
case 0xa110: { // DRAW EMPTY RECT x1,y1,x2,y2:int
- // FIXME: These lines should be clipped by seq._drawWin
- const Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2] - 1, ivals[3] - 1);
- _vm->_compositionBuffer.drawLine(r.left, r.top, r.right, r.top, seq._drawColFG);
- _vm->_compositionBuffer.drawLine(r.left, r.bottom, r.right, r.bottom, seq._drawColFG);
- _vm->_compositionBuffer.drawLine(r.left, r.top, r.left, r.bottom, seq._drawColFG);
- _vm->_compositionBuffer.drawLine(r.right, r.top, r.right, r.bottom, seq._drawColFG);
+ const Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2], ivals[3]);
+ Drawing::rectClipped(r, seq._drawWin, &_vm->_compositionBuffer, seq._drawColFG);
break;
}
case 0xa200: // 0xa2n0 DRAW STRING n: x,y,w,h:int - draw the nth string from the string table
Commit: cbcee066c0c7194c065b01e1cafa19aa260556f0
https://github.com/scummvm/scummvm/commit/cbcee066c0c7194c065b01e1cafa19aa260556f0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T12:30:15+11:00
Commit Message:
DGDS: Fix selection of partial line dialog actions
This is required when hypnotising the guard in Willy Beamish.
Changed paths:
engines/dgds/dialog.cpp
diff --git a/engines/dgds/dialog.cpp b/engines/dgds/dialog.cpp
index 628b596dc39..1daaf9013f1 100644
--- a/engines/dgds/dialog.cpp
+++ b/engines/dgds/dialog.cpp
@@ -573,14 +573,18 @@ void Dialog::drawForeground(Graphics::ManagedSurface *dst, uint16 fontcol, const
}
for (uint i = 0; i < lines.size(); i++) {
+ // Draw every line unhighlighted then highlight bits as needed
font->drawString(dst, lines[i], x, ystart + i * h, xwidth, fontcol, align);
if (highlightStart < lineOffs[i + 1] && highlightEnd > lineOffs[i]) {
- // highlight on this line
- // TODO: What if it's only a partial line??
- font->drawString(dst, lines[i], x, ystart + i * h, xwidth, _selectonFontCol, align);
+ // Highlight on this line. Redraw whatever part is highlighted.
+ int lineLen = (int)lines[i].size();
+ int lineHighlightStart = MAX(highlightStart - lineOffs[i], 0);
+ int lineHighlightEnd = MIN(highlightEnd - lineOffs[i], lineLen);
+ int highlightXOff = lineHighlightStart ? font->getStringWidth(lines[i].substr(0, lineHighlightStart)) : 0;
+ Common::String highlightString = lines[i].substr(lineHighlightStart, lineHighlightEnd - lineHighlightStart);
+ font->drawString(dst, highlightString, x + highlightXOff, ystart + i * h, xwidth, _selectonFontCol, align);
}
}
-
}
void Dialog::setFlag(DialogFlags flg) {
Commit: 91908bbdc99790b4ed18b55c5be93abf6395ae00
https://github.com/scummvm/scummvm/commit/91908bbdc99790b4ed18b55c5be93abf6395ae00
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T16:12:54+11:00
Commit Message:
DGDS: Reset look mode on scene change
Changed paths:
engines/dgds/scene.cpp
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index 26af2b9cfe9..548fe3b3c0c 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -578,6 +578,7 @@ bool SDSScene::parse(Common::SeekableReadStream *stream) {
void SDSScene::unload() {
_num = 0;
+ _lookMode = 0;
_enterSceneOps.clear();
_leaveSceneOps.clear();
_preTickOps.clear();
Commit: ac491547e4d1c385ee310a71399acd64a18a09d7
https://github.com/scummvm/scummvm/commit/ac491547e4d1c385ee310a71399acd64a18a09d7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-04T16:13:17+11:00
Commit Message:
DGDS: Switch to 15 game frames per second
But, pump mouse messages as fast as we can to make the game feel better.
This is more accurate to how the original games work. We stop pumping if a
mouse click event happens, but that should work petty well.
This fixes a number of bugs in the way Willy Beamish was playing.
Changed paths:
engines/dgds/clock.cpp
engines/dgds/dgds.cpp
engines/dgds/dgds.h
engines/dgds/minigames/china_train.cpp
engines/dgds/minigames/dragon_arcade.cpp
diff --git a/engines/dgds/clock.cpp b/engines/dgds/clock.cpp
index 95a79a5dcff..c9bf6309b76 100644
--- a/engines/dgds/clock.cpp
+++ b/engines/dgds/clock.cpp
@@ -146,9 +146,10 @@ void Clock::draw(Graphics::ManagedSurface &surf) {
font->drawString(&surf, clockStr, _drawPos.left + 3, _drawPos.top + 3, _drawPos.width(), 0);
}
-// TODO: This is approximate, work out the exact conversion factor here for better game accuracy.
+// Confirmed by timing the clock tick up in the inventory of Dragon and Willy Beamish
+// - 5 game minutes are exactly 25 real time seconds.
static const int MILLIS_PER_GAME_MIN = 5000;
-static const int MILLIS_PER_TIMER_TICK = 60;
+static const int MILLIS_PER_TIMER_TICK = 60; // ~16.667 ticks per second.
void Clock::update(bool gameRunning) {
uint32 playTimeNow = DgdsEngine::getInstance()->getThisFrameMs();
diff --git a/engines/dgds/dgds.cpp b/engines/dgds/dgds.cpp
index f1853ce17bd..c15fb2c2210 100644
--- a/engines/dgds/dgds.cpp
+++ b/engines/dgds/dgds.cpp
@@ -90,7 +90,7 @@ DgdsEngine::DgdsEngine(OSystem *syst, const ADGameDescription *gameDesc)
_rstFileName(nullptr), _difficulty(1), _menu(nullptr), _adsInterp(nullptr), _isDemo(false),
_dragonArcade(nullptr), _chinaTank(nullptr), _chinaTrain(nullptr), _skipNextFrame(false),
_gameId(GID_INVALID), _thisFrameMs(0), _lastGlobalFade(-1), _lastGlobalFadedPal(0),
- _debugShowHotAreas(false) {
+ _debugShowHotAreas(false), _lastMouseEvent(Common::EVENT_INVALID) {
_platform = gameDesc->platform;
_gameLang = gameDesc->language;
@@ -516,6 +516,86 @@ static void _dumpFrame(const Graphics::ManagedSurface &surf, const char *name) {
}
+void DgdsEngine::pumpMessages() {
+ Common::Event ev;
+ while (_eventMan->pollEvent(ev)) {
+ if (ev.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
+ switch ((DgdsKeyEvent)ev.customType) {
+ case kDgdsKeyToggleMenu:
+ _menuToTrigger = kMenuMain;
+ break;
+ case kDgdsKeySave:
+ saveGameDialog();
+ break;
+ case kDgdsKeyLoad:
+ loadGameDialog();
+ break;
+ case kDgdsKeyToggleClock:
+ _clock.toggleVisibleUser();
+ break;
+ case kDgdsKeyNextChoice:
+ if (_menu->menuShown())
+ _menu->nextChoice();
+ else if (_scene->hasVisibleDialog())
+ _scene->nextChoice();
+ break;
+ case kDgdsKeyPrevChoice:
+ if (_menu->menuShown())
+ _menu->prevChoice();
+ else if (_scene->hasVisibleDialog())
+ _scene->prevChoice();
+ break;
+ case kDgdsKeyNextItem:
+ warning("TODO: Implement kDgdsKeyNextItem");
+ break;
+ case kDgdsKeyPrevItem:
+ warning("TODO: Implement kDgdsKeyPrevItem");
+ break;
+ case kDgdsKeyPickUp:
+ if (_menu->menuShown())
+ _menu->activateChoice();
+ else if (_scene->hasVisibleDialog())
+ _scene->activateChoice();
+ else
+ warning("TODO: Implement kDgdsKeyPickUp");
+ break;
+ case kDgdsKeyLook:
+ if (_menu->menuShown())
+ _menu->activateChoice();
+ else if (_scene->hasVisibleDialog())
+ _scene->activateChoice();
+ else
+ warning("TODO: Implement kDgdsKeyLook");
+ break;
+ case kDgdsKeyActivate:
+ warning("TODO: Implement kDgdsKeyActivate");
+ break;
+ default:
+ break;
+ }
+ } else if (ev.type == Common::EVENT_LBUTTONDOWN || ev.type == Common::EVENT_LBUTTONUP
+ || ev.type == Common::EVENT_RBUTTONDOWN || ev.type == Common::EVENT_RBUTTONUP
+ || ev.type == Common::EVENT_MOUSEMOVE) {
+ _lastMouseEvent = ev.type;
+ _lastMouse = ev.mouse;
+ // We can keep going if there were multiple moves or a move then a button, but
+ // stop if there was a button event to process it now.
+ if (_lastMouseEvent != Common::EVENT_MOUSEMOVE)
+ return;
+ } else if (ev.type == Common::EVENT_KEYDOWN) {
+ if (_dragonArcade)
+ _dragonArcade->onKeyDown(ev.kbd);
+ if (_chinaTrain)
+ _chinaTrain->onKeyDown(ev.kbd);
+ } else if (ev.type == Common::EVENT_KEYUP) {
+ if (_dragonArcade)
+ _dragonArcade->onKeyUp(ev.kbd);
+ if (_chinaTrain)
+ _chinaTrain->onKeyUp(ev.kbd);
+ }
+ }
+}
+
Common::Error DgdsEngine::run() {
syncSoundSettings();
_isLoading = true;
@@ -527,9 +607,6 @@ Common::Error DgdsEngine::run() {
if (saveSlot != -1)
loadGameState(saveSlot);
- Common::EventManager *eventMan = g_system->getEventManager();
- Common::Event ev;
-
_isLoading = false;
uint32 startMillis = g_system->getMillis();
@@ -538,79 +615,8 @@ Common::Error DgdsEngine::run() {
while (!shouldQuit()) {
_thisFrameMs = getTotalPlayTime();
- Common::EventType mouseEvent = Common::EVENT_INVALID;
- while (eventMan->pollEvent(ev)) {
- if (ev.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
- switch ((DgdsKeyEvent)ev.customType) {
- case kDgdsKeyToggleMenu:
- _menuToTrigger = kMenuMain;
- break;
- case kDgdsKeySave:
- saveGameDialog();
- break;
- case kDgdsKeyLoad:
- loadGameDialog();
- break;
- case kDgdsKeyToggleClock:
- _clock.toggleVisibleUser();
- break;
- case kDgdsKeyNextChoice:
- if (_menu->menuShown())
- _menu->nextChoice();
- else if (_scene->hasVisibleDialog())
- _scene->nextChoice();
- break;
- case kDgdsKeyPrevChoice:
- if (_menu->menuShown())
- _menu->prevChoice();
- else if (_scene->hasVisibleDialog())
- _scene->prevChoice();
- break;
- case kDgdsKeyNextItem:
- warning("TODO: Implement kDgdsKeyNextItem");
- break;
- case kDgdsKeyPrevItem:
- warning("TODO: Implement kDgdsKeyPrevItem");
- break;
- case kDgdsKeyPickUp:
- if (_menu->menuShown())
- _menu->activateChoice();
- else if (_scene->hasVisibleDialog())
- _scene->activateChoice();
- else
- warning("TODO: Implement kDgdsKeyPickUp");
- break;
- case kDgdsKeyLook:
- if (_menu->menuShown())
- _menu->activateChoice();
- else if (_scene->hasVisibleDialog())
- _scene->activateChoice();
- else
- warning("TODO: Implement kDgdsKeyLook");
- break;
- case kDgdsKeyActivate:
- warning("TODO: Implement kDgdsKeyActivate");
- break;
- default:
- break;
- }
- } else if (ev.type == Common::EVENT_LBUTTONDOWN || ev.type == Common::EVENT_LBUTTONUP
- || ev.type == Common::EVENT_RBUTTONDOWN || ev.type == Common::EVENT_RBUTTONUP
- || ev.type == Common::EVENT_MOUSEMOVE) {
- mouseEvent = ev.type;
- _lastMouse = ev.mouse;
- } else if (ev.type == Common::EVENT_KEYDOWN) {
- if (_dragonArcade)
- _dragonArcade->onKeyDown(ev.kbd);
- if (_chinaTrain)
- _chinaTrain->onKeyDown(ev.kbd);
- } else if (ev.type == Common::EVENT_KEYUP) {
- if (_dragonArcade)
- _dragonArcade->onKeyUp(ev.kbd);
- if (_chinaTrain)
- _chinaTrain->onKeyUp(ev.kbd);
- }
- }
+ if (_lastMouseEvent == Common::EVENT_INVALID)
+ pumpMessages();
if (_menuToTrigger != kMenuNone) {
if (_inventory->isOpen()) {
@@ -629,7 +635,7 @@ Common::Error DgdsEngine::run() {
}
if (_menu->menuShown()) {
- switch (mouseEvent) {
+ switch (_lastMouseEvent) {
case Common::EVENT_LBUTTONUP:
_menu->onMouseLUp(_lastMouse);
break;
@@ -677,44 +683,42 @@ Common::Error DgdsEngine::run() {
if (!_inventory->isOpen() || (_inventory->isZoomVisible() && getGameId() != GID_WILLY))
_adsInterp->run();
- if (mouseEvent != Common::EVENT_INVALID) {
- if (_inventory->isOpen()) {
- switch (mouseEvent) {
- case Common::EVENT_MOUSEMOVE:
- _inventory->mouseMoved(_lastMouse);
- break;
- case Common::EVENT_LBUTTONDOWN:
- _inventory->mouseLDown(_lastMouse);
- break;
- case Common::EVENT_LBUTTONUP:
- _inventory->mouseLUp(_lastMouse);
- break;
- case Common::EVENT_RBUTTONUP:
- _inventory->mouseRUp(_lastMouse);
- break;
- default:
- break;
- }
- } else {
- switch (mouseEvent) {
- case Common::EVENT_MOUSEMOVE:
- _scene->mouseMoved(_lastMouse);
- break;
- case Common::EVENT_LBUTTONDOWN:
- _scene->mouseLDown(_lastMouse);
- break;
- case Common::EVENT_LBUTTONUP:
- _scene->mouseLUp(_lastMouse);
- break;
- case Common::EVENT_RBUTTONDOWN:
- _scene->mouseRDown(_lastMouse);
- break;
- case Common::EVENT_RBUTTONUP:
- _scene->mouseRUp(_lastMouse);
- break;
- default:
- break;
- }
+ if (_inventory->isOpen()) {
+ switch (_lastMouseEvent) {
+ case Common::EVENT_MOUSEMOVE:
+ _inventory->mouseMoved(_lastMouse);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ _inventory->mouseLDown(_lastMouse);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ _inventory->mouseLUp(_lastMouse);
+ break;
+ case Common::EVENT_RBUTTONUP:
+ _inventory->mouseRUp(_lastMouse);
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (_lastMouseEvent) {
+ case Common::EVENT_MOUSEMOVE:
+ _scene->mouseMoved(_lastMouse);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ _scene->mouseLDown(_lastMouse);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ _scene->mouseLUp(_lastMouse);
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ _scene->mouseRDown(_lastMouse);
+ break;
+ case Common::EVENT_RBUTTONUP:
+ _scene->mouseRUp(_lastMouse);
+ break;
+ default:
+ break;
}
}
@@ -762,6 +766,9 @@ Common::Error DgdsEngine::run() {
_justChangedScene2 = false;
}
+ // Mouse event is now handled.
+ _lastMouseEvent = Common::EVENT_INVALID;
+
// Willy Beamish dims the palette of the screen while dialogs are active
if (getGameId() == GID_WILLY) {
WillyGlobals *globals = static_cast<WillyGlobals *>(_gameGlobals);
@@ -791,19 +798,42 @@ Common::Error DgdsEngine::run() {
g_system->updateScreen();
- // Limit to 30 FPS
+ //
+ // Limit frame rate to 15 FPS
+ //
+ // Most game events are based on timed delays using eg TTM op 0x1020,
+ // but some game events are based on frames - eg the chef turning
+ // around in Willy Beamish kitchen (scene 46) uses global 190 to count
+ // down frames before the chef turns around. If they don't match, it's
+ // impossible to complete the needed actions (timed on ms) before the
+ // chef moves (timed on frames).
+ //
+ static const int framesPerSecond = 15;
+
frameCount++;
if (_skipNextFrame) {
frameCount++;
_skipNextFrame = false;
}
- const uint32 thisFrameEndMillis = g_system->getMillis();
- const uint32 elapsedMillis = thisFrameEndMillis - startMillis;
- const uint32 targetMillis = (frameCount * 1000 / 30);
+ uint32 thisFrameEndMillis = g_system->getMillis();
+ uint32 elapsedMillis = thisFrameEndMillis - startMillis;
+ const uint32 targetMillis = (frameCount * 1000 / framesPerSecond);
if (targetMillis > elapsedMillis) {
- // too fast, delay
- g_system->delayMillis(targetMillis - elapsedMillis);
+ //
+ // Too fast, delay.
+ //
+ // Pump messages and update the screen - moves will be accumulated and the
+ // last one will be processed in the next frame. This way the mouse moves
+ // at 60+ FPS even though the game is only 15 FPS.
+ //
+ while (targetMillis > elapsedMillis) {
+ if (_lastMouseEvent == Common::EVENT_INVALID || _lastMouseEvent == Common::EVENT_MOUSEMOVE)
+ pumpMessages();
+ g_system->updateScreen();
+ g_system->delayMillis(5);
+ elapsedMillis = g_system->getMillis() - startMillis;
+ }
} else if (targetMillis < elapsedMillis) {
// too slow.. adjust expectations? :)
startMillis = thisFrameEndMillis;
diff --git a/engines/dgds/dgds.h b/engines/dgds/dgds.h
index 36ec69c8a72..75031f9ce04 100644
--- a/engines/dgds/dgds.h
+++ b/engines/dgds/dgds.h
@@ -170,6 +170,7 @@ private:
Common::RandomSource _random;
Common::Point _lastMouse; // originals start mouse at 0,0.
+ Common::EventType _lastMouseEvent; // a pending mouse event to process.
int _currentCursor;
Common::Point _currentCursorHot;
@@ -292,6 +293,7 @@ private:
void init(bool restarting);
void loadGameFiles();
void loadRestartFile();
+ void pumpMessages();
};
} // End of namespace Dgds
diff --git a/engines/dgds/minigames/china_train.cpp b/engines/dgds/minigames/china_train.cpp
index e769cfabfee..e0770557556 100644
--- a/engines/dgds/minigames/china_train.cpp
+++ b/engines/dgds/minigames/china_train.cpp
@@ -685,7 +685,7 @@ int16 ChinaTrain::tick() { // aka arcadeLoop
drawFrame();
// Original has delay here to reduce the arcade speed to 15 FPS.
- DgdsEngine::getInstance()->setSkipNextFrame();
+ //DgdsEngine::getInstance()->setSkipNextFrame();
return 1;
}
diff --git a/engines/dgds/minigames/dragon_arcade.cpp b/engines/dgds/minigames/dragon_arcade.cpp
index e30c8eccb41..e28d911dfb6 100644
--- a/engines/dgds/minigames/dragon_arcade.cpp
+++ b/engines/dgds/minigames/dragon_arcade.cpp
@@ -138,7 +138,7 @@ bool DragonArcade::doTickUpdate() {
drawHealthBars();
// Original has delay here to reduce the arcade speed to 15 FPS.
- DgdsEngine::getInstance()->setSkipNextFrame();
+ //DgdsEngine::getInstance()->setSkipNextFrame();
_nTickUpdates++;
More information about the Scummvm-git-logs
mailing list