[Scummvm-git-logs] scummvm master -> 0e69b6057e957dc852dc4ecf0ab7037afd8eed88
sev-
noreply at scummvm.org
Sat May 16 18:46:16 UTC 2026
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
2e09711bc0 DIRECTOR: LINGO: Move currentChannelId into LingoState
10006de014 DIRECTOR: Fix regression with duplicate startMovie event
c8ace0d5fa DIRECTOR: Rewrite how channels are drawn to screen
0e69b6057e DIRECTOR: Force a whole-screen redraw after a movie switch
Commit: 2e09711bc0fb89ad1137220c5ac1a72d625fed45
https://github.com/scummvm/scummvm/commit/2e09711bc0fb89ad1137220c5ac1a72d625fed45
Author: Scott Percival (code at moral.net.au)
Date: 2026-05-16T20:46:09+02:00
Commit Message:
DIRECTOR: LINGO: Move currentChannelId into LingoState
This property is determined by the top level script that is being run,
and has to be preserved across state freezes since it affects various
features (e.g. "play done" will increment the frame by 1 if the original
"play" call was a frame script, but not do so if it was a sprite
script).
Fixes selecting one of the three choice buttons in Crime Scouts.
Changed paths:
engines/director/lingo/lingo-builtins.cpp
engines/director/lingo/lingo-events.cpp
engines/director/lingo/lingo-funcs.cpp
engines/director/lingo/lingo.cpp
engines/director/lingo/lingo.h
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 46159199448..d2cafba388c 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -2727,12 +2727,12 @@ void LB::b_editableText(int nargs) {
} else if (nargs == 0) {
g_lingo->dropStack(nargs);
- if (g_lingo->_currentChannelId == -1) {
+ if (g_lingo->_state->currentChannelId == -1) {
warning("b_editableText: channel Id is missing");
return;
}
- sc->getSpriteById(g_lingo->_currentChannelId)->_editable = true;
- sc->getOriginalSpriteById(g_lingo->_currentChannelId)->_editable = true;
+ sc->getSpriteById(g_lingo->_state->currentChannelId)->_editable = true;
+ sc->getOriginalSpriteById(g_lingo->_state->currentChannelId)->_editable = true;
} else {
warning("b_editableText: unexpectedly received %d arguments", nargs);
g_lingo->dropStack(nargs);
@@ -3056,16 +3056,16 @@ void LB::b_moveableSprite(int nargs) {
Score *score = movie->getScore();
Frame *frame = score->_currentFrame;
- if (g_lingo->_currentChannelId == -1) {
+ if (g_lingo->_state->currentChannelId == -1) {
warning("b_moveableSprite: channel Id is missing");
assert(0);
return;
}
// since we are using value copying, in order to make it taking effect immediately. we modify the sprites in channel
- if (score->_channels[g_lingo->_currentChannelId])
- score->_channels[g_lingo->_currentChannelId]->_sprite->_moveable = true;
- frame->_sprites[g_lingo->_currentChannelId]->_moveable = true;
+ if (score->_channels[g_lingo->_state->currentChannelId])
+ score->_channels[g_lingo->_state->currentChannelId]->_sprite->_moveable = true;
+ frame->_sprites[g_lingo->_state->currentChannelId]->_moveable = true;
}
void LB::b_pasteClipBoardInto(int nargs) {
@@ -3275,11 +3275,11 @@ void LB::b_immediateSprite(int nargs) {
} else if (nargs == 0 && g_director->getVersion() < 400) {
g_lingo->dropStack(nargs);
- if (g_lingo->_currentChannelId == -1) {
+ if (g_lingo->_state->currentChannelId == -1) {
warning("b_immediateSprite: channel Id is missing");
return;
}
- sc->getSpriteById(g_lingo->_currentChannelId)->_immediate = true;
+ sc->getSpriteById(g_lingo->_state->currentChannelId)->_immediate = true;
} else {
warning("b_immediateSprite: unexpectedly received %d arguments", nargs);
g_lingo->dropStack(nargs);
@@ -3319,11 +3319,11 @@ void LB::b_puppetSprite(int nargs) {
} else if (nargs == 0 && g_director->getVersion() < 400) {
g_lingo->dropStack(nargs);
- if (g_lingo->_currentChannelId == -1) {
+ if (g_lingo->_state->currentChannelId == -1) {
warning("b_puppetSprite: channel Id is missing");
return;
}
- sc->getSpriteById(g_lingo->_currentChannelId)->_puppet = true;
+ sc->getSpriteById(g_lingo->_state->currentChannelId)->_puppet = true;
} else {
warning("b_puppetSprite: unexpectedly received %d arguments", nargs);
g_lingo->dropStack(nargs);
diff --git a/engines/director/lingo/lingo-events.cpp b/engines/director/lingo/lingo-events.cpp
index d1f6326719f..83f59827434 100644
--- a/engines/director/lingo/lingo-events.cpp
+++ b/engines/director/lingo/lingo-events.cpp
@@ -699,7 +699,7 @@ void Lingo::processEvents(Common::Queue<LingoEvent> &queue, bool isInputEvent) {
}
bool Lingo::processEvent(LEvent event, ScriptType st, CastMemberID scriptId, int channelId, AbstractObject *obj) {
- _currentChannelId = channelId;
+ _state->currentChannelId = channelId;
if (!_eventHandlerTypes.contains(event))
error("processEvent: Unknown event %d", event);
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
index 7ef6238f822..d905703e130 100644
--- a/engines/director/lingo/lingo-funcs.cpp
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -62,8 +62,8 @@ void Lingo::func_goto(Datum &frame, Datum &movie, bool calledfromgo) {
// freeze this script context. We'll return to it after entering the next frame.
// Returning from a script with "play done" does not freeze the state. Instead it obliterates it.
- if (!g_lingo->_playDone)
- g_lingo->_freezeState = true;
+ if (!_playDone)
+ _freezeState = true;
if (movie.type != VOID) {
Common::String movieFilenameRaw = movie.asString();
@@ -74,9 +74,9 @@ void Lingo::func_goto(Datum &frame, Datum &movie, bool calledfromgo) {
// If we reached here from b_go, and the movie is getting swapped out,
// reset all of the custom event handlers.
if (calledfromgo)
- g_lingo->resetLingoGo();
+ resetLingoGo();
- if (g_lingo->_updateMovieEnabled) {
+ if (_updateMovieEnabled) {
// Save the movie when branching to another movie.
LB::b_saveMovie(0);
}
@@ -204,7 +204,7 @@ void Lingo::func_play(Datum &frame, Datum &movie) {
ref.frameI = _vm->getCurrentMovie()->getScore()->getCurrentFrameNum();
// if we are issuing play command from script channel script. then play done should return to next frame
- if (g_lingo->_currentChannelId == 0)
+ if (_state->currentChannelId == 0)
ref.frameI++;
stage->_movieStack.push_back(ref);
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 6ef78f733c0..b6327fc6b09 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -174,7 +174,6 @@ Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
g_lingo = this;
_state = nullptr;
- _currentChannelId = -1;
_globalCounter = 0;
_freezeState = false;
_freezePlay = false;
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 53efcf298a1..e140baedd48 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -345,6 +345,7 @@ struct LingoState {
DatumHash *localVars = nullptr; // current local variables
Datum me; // current me object
StackData stack;
+ int currentChannelId = 0;
~LingoState();
};
@@ -514,8 +515,6 @@ public:
LingoCompiler *_compiler;
LingoState *_state;
- int _currentChannelId;
-
bool _freezeState;
bool _freezePlay;
bool _playDone;
Commit: 10006de014ea41f40e6b1175e275524be05117ae
https://github.com/scummvm/scummvm/commit/10006de014ea41f40e6b1175e275524be05117ae
Author: Scott Percival (code at moral.net.au)
Date: 2026-05-16T20:46:09+02:00
Commit Message:
DIRECTOR: Fix regression with duplicate startMovie event
Fixes changing floors in the lift in Journeyman Project.
Changed paths:
engines/director/score.cpp
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index ffbcb37803e..3d8374052af 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -305,9 +305,6 @@ void Score::startPlay() {
return;
}
- if (_haveInteractivity && _version >= kFileVer300)
- _movie->processEvent(kEventStartMovie);
-
// load first frame (either 1 or _nextFrame)
updateCurrentFrame();
@@ -319,7 +316,7 @@ void Score::startPlay() {
updateSprites(kRenderForceUpdate, true);
// Stage has been set up, run the StartMovie script
- if (_version >= kFileVer300)
+ if (_haveInteractivity && _version >= kFileVer300)
_movie->processEvent(kEventStartMovie);
}
Commit: c8ace0d5fa33b50c97f58cac3ca295e32561026f
https://github.com/scummvm/scummvm/commit/c8ace0d5fa33b50c97f58cac3ca295e32561026f
Author: Scott Percival (code at moral.net.au)
Date: 2026-05-16T20:46:09+02:00
Commit Message:
DIRECTOR: Rewrite how channels are drawn to screen
Previously, the code was using the window's dirty rect list as a sort of
staging area, which could be reverse-engineed back into a list of
channels to move around. The trails feature requires us to keep tabs on
two bounding boxes per channel; the current one, and the one used the
last time updateStage() was called. Given this, we may as well stop
using the window bounding box list, and instead generate it from the
channels at render time.
Fixes a lot of games which use/abuse the trails flag for painting/strobing
sprites as a brush.
Lightly tested against:
- Eastern Mind: The Lost Souls of Tong Nou
- Rodney's Funscreen
- The Saboten Man
- The 7 Colors
- Virtual Nightclub
- PAWS: Personal Automated Wagging System
- The Journeyman Project
- Hell Cab
- Mission to Planet X
- The Dark Eye
Changed paths:
engines/director/channel.cpp
engines/director/channel.h
engines/director/debugger.cpp
engines/director/events.cpp
engines/director/lingo/lingo-builtins.cpp
engines/director/lingo/lingo-events.cpp
engines/director/lingo/lingo-the.cpp
engines/director/score.cpp
engines/director/score.h
engines/director/transitions.cpp
engines/director/window.cpp
engines/director/window.h
diff --git a/engines/director/channel.cpp b/engines/director/channel.cpp
index 277e5e5ac82..6e4bf08d985 100644
--- a/engines/director/channel.cpp
+++ b/engines/director/channel.cpp
@@ -61,8 +61,10 @@ Channel::Channel(Score *sc, Sprite *sp, int priority) {
_filmLoopFrame = 0;
_visible = true;
- _dirty = true;
+ _widgetDirty = true;
+ _needsDraw = false;
_hideFromStage = false;
+ _lastTrail = false;
if (sp) {
_startFrame = sp->_spriteInfo.startFrame;
@@ -95,7 +97,7 @@ Channel& Channel::operator=(const Channel &channel) {
_filmLoopFrame = channel._filmLoopFrame;
_visible = channel._visible;
- _dirty = channel._dirty;
+ _widgetDirty = channel._widgetDirty;
_hideFromStage = channel._hideFromStage;
_startFrame = channel._startFrame;
@@ -281,7 +283,7 @@ bool Channel::isDirty(Sprite *nextSprite) {
if (!nextSprite)
return false;
- bool isDirtyFlag = _dirty ||
+ bool isDirtyFlag = _widgetDirty ||
(_sprite->_cast && _sprite->_cast->isModified());
if (_sprite && !_sprite->_puppet && !_sprite->_autoPuppet) {
@@ -483,6 +485,7 @@ void Channel::setCast(CastMemberID memberID) {
// Based on Director in a Nutshell, page 15
_sprite->setAutoPuppet(kAPCast, true);
+ setNeedsDraw();
}
void Channel::setClean(Sprite *nextSprite, bool partial) {
@@ -527,7 +530,7 @@ void Channel::setClean(Sprite *nextSprite, bool partial) {
if (_stopTime && (!_sprite->_cast || (_sprite->_cast && _sprite->_cast->_type != kCastDigitalVideo)))
_stopTime = 0;
- _dirty = false;
+ _widgetDirty = false;
}
void Channel::setStretch(bool enabled) {
@@ -535,8 +538,7 @@ void Channel::setStretch(bool enabled) {
// when the stretch flag is manually disabled,
// revert whatever dimensions the sprite has to
// the default in the cast
- g_director->getCurrentWindow()->addDirtyRect(getBbox());
- _dirty = true;
+ setDirty();
if (_sprite->_cast) {
Common::Rect bbox = _sprite->_cast->getBbox();
@@ -561,7 +563,7 @@ void Channel::updateTextCast() {
if (!textWidget->getFixDims() && (_sprite->_width != _widget->_dims.width() || _sprite->_height != _widget->_dims.height())) {
_sprite->_width = _widget->_dims.width();
_sprite->_height = _widget->_dims.height();
- g_director->getCurrentWindow()->addDirtyRect(_widget->_dims);
+ setDirty();
}
}
}
@@ -657,6 +659,10 @@ void Channel::replaceSprite(Sprite *nextSprite) {
}
}
+void Channel::setDirty() {
+ _widgetDirty = true;
+}
+
void Channel::setPosition(int x, int y, bool force) {
Common::Point newPos(x, y);
if (_constraint > 0 && _score && _constraint <= _score->_channels.size()) {
diff --git a/engines/director/channel.h b/engines/director/channel.h
index c32f4e766c2..a55138280e0 100644
--- a/engines/director/channel.h
+++ b/engines/director/channel.h
@@ -65,9 +65,11 @@ public:
bool isActiveVideo();
bool isVideoDirectToStage();
- inline void setWidth(int w) { _sprite->setWidth(w); replaceWidget(); _dirty = true; };
- inline void setHeight(int h) { _sprite->setHeight(h); replaceWidget(); _dirty = true; };
- inline void setBbox(int l, int t, int r, int b) { _sprite->setBbox(l, t, r, b); replaceWidget(); _dirty = true; };
+ inline void setWidth(int w) { _sprite->setWidth(w); replaceWidget(); setNeedsDraw(); };
+ inline void setHeight(int h) { _sprite->setHeight(h); replaceWidget(); setNeedsDraw(); };
+ inline void setBbox(int l, int t, int r, int b) { _sprite->setBbox(l, t, r, b); replaceWidget(); setNeedsDraw(); };
+ void setDirty();
+ void setNeedsDraw() { _needsDraw = true; }
void setPosition(int x, int y, bool force = false);
void setCast(CastMemberID memberID);
void setClean(Sprite *nextSprite, bool partial = false);
@@ -106,7 +108,8 @@ public:
Cursor _cursor;
Graphics::MacWidget *_widget;
- bool _dirty;
+ bool _widgetDirty;
+ bool _needsDraw;
bool _visible;
bool _hideFromStage; // Used in DT for hiding the channel from rendering
uint _constraint;
@@ -124,6 +127,8 @@ public:
uint _filmLoopFrame;
Common::Rect _rollOverBbox;
+ Common::Rect _lastRenderedBbox;
+ bool _lastTrail;
int _startFrame;
int _endFrame;
diff --git a/engines/director/debugger.cpp b/engines/director/debugger.cpp
index 0fa0ea4ebb1..48223247e48 100644
--- a/engines/director/debugger.cpp
+++ b/engines/director/debugger.cpp
@@ -1055,7 +1055,7 @@ static void forceWindowRedraw(Window *window) {
return;
for (uint16 c = 0; c < score->_channels.size(); c++)
- score->_channels[c]->_dirty = true;
+ score->_channels[c]->setDirty();
}
bool Debugger::cmdForceRedraw(int argc, const char **argv) {
diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index 21b1d987bda..f0146900278 100644
--- a/engines/director/events.cpp
+++ b/engines/director/events.cpp
@@ -187,7 +187,7 @@ bool Movie::processEvent(Common::Event &event) {
// if we are moving out of bounds, then we don't hilite it anymore
if (_currentHiliteChannelId && (sc->_channels[_currentHiliteChannelId]->isMouseIn(pos) != kCollisionYes)) {
g_director->getCurrentWindow()->setDirty(true);
- g_director->getCurrentWindow()->addDirtyRect(sc->_channels[_currentHiliteChannelId]->getBbox());
+ sc->_channels[_currentHiliteChannelId]->setDirty();
_currentHiliteChannelId = 0;
}
@@ -196,19 +196,15 @@ bool Movie::processEvent(Common::Event &event) {
if (spriteId > 0 && sc->_channels[spriteId]->_sprite->shouldHilite()) {
_currentHiliteChannelId = spriteId;
g_director->getCurrentWindow()->setDirty(true);
- g_director->getCurrentWindow()->addDirtyRect(sc->_channels[_currentHiliteChannelId]->getBbox());
+ sc->_channels[_currentHiliteChannelId]->setDirty();
}
}
if (_currentDraggedChannel) {
if (_currentDraggedChannel->_sprite->_moveable) {
pos = _draggingSpriteOffset + event.mouse;
- if (!_currentDraggedChannel->_sprite->_trails) {
- g_director->getCurrentMovie()->getWindow()->addDirtyRect(_currentDraggedChannel->getBbox());
- }
_currentDraggedChannel->setPosition(pos.x, pos.y, true);
- _currentDraggedChannel->_dirty = true;
- g_director->getCurrentMovie()->getWindow()->addDirtyRect(_currentDraggedChannel->getBbox());
+ _currentDraggedChannel->setDirty();
} else {
_currentDraggedChannel = nullptr;
}
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index d2cafba388c..de210df178c 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -2750,7 +2750,7 @@ void LB::b_erase(int nargs) {
for (uint i = 0; i < channels.size(); i++) {
if (channels[i]->_sprite->_castId == d.asMemberID()) {
- channels[i]->_dirty = true;
+ channels[i]->setDirty();
}
}
}
@@ -3308,10 +3308,8 @@ void LB::b_puppetSprite(int nargs) {
if (refresh) {
// puppetSprite set to FALSE, copy back sprite data from frame cache
Channel *chan = sc->getChannelById(spriteId);
- movie->getWindow()->addDirtyRect(chan->getBbox());
- chan->_dirty = true;
chan->setClean(sc->_currentFrame->_sprites[spriteId]);
- chan->_dirty = true;
+ chan->setDirty();
}
} else {
warning("b_puppetSprite: sprite index out of bounds");
@@ -3468,7 +3466,7 @@ void LB::b_spriteBox(int nargs) {
// This automatically sets the stretch mode
channel->_sprite->_stretch = true;
- g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
+ channel->setDirty();
channel->setBbox(
l < r ? l : r,
t < b ? t : b,
@@ -3477,7 +3475,6 @@ void LB::b_spriteBox(int nargs) {
);
if (channel->_sprite->_cast)
channel->_sprite->_cast->setModified(true);
- channel->_dirty = true;
}
void LB::b_unLoad(int nargs) {
diff --git a/engines/director/lingo/lingo-events.cpp b/engines/director/lingo/lingo-events.cpp
index 83f59827434..a412dff3397 100644
--- a/engines/director/lingo/lingo-events.cpp
+++ b/engines/director/lingo/lingo-events.cpp
@@ -156,7 +156,7 @@ void Movie::resolveScriptEvent(LingoEvent &event) {
_currentHiliteChannelId = event.channelId;
g_director->_wm->_hilitingWidget = true;
g_director->getCurrentWindow()->setDirty(true);
- g_director->getCurrentWindow()->addDirtyRect(_score->_channels[_currentHiliteChannelId]->getBbox());
+ _score->_channels[_currentHiliteChannelId]->setDirty();
}
CastMember *cast = getCastMember(_score->_channels[event.channelId]->_sprite->_castId);
@@ -187,7 +187,7 @@ void Movie::resolveScriptEvent(LingoEvent &event) {
} else if ((event.event == kEventMouseUp) || (event.event == kEventRightMouseUp)) {
if (_currentHiliteChannelId && _score->_channels[_currentHiliteChannelId]) {
g_director->getCurrentWindow()->setDirty(true);
- g_director->getCurrentWindow()->addDirtyRect(_score->_channels[_currentHiliteChannelId]->getBbox());
+ _score->_channels[_currentHiliteChannelId]->setDirty();
}
g_director->_wm->_hilitingWidget = false;
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 3e128d824de..c1ac922028f 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -1843,7 +1843,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
uint32 newColor = g_director->transformColor(d.asInt());
if (newColor != sprite->_backColor) {
sprite->_backColor = newColor;
- channel->_dirty = true;
+ channel->setDirty();
// Based on Director in a Nutshell, page 15
sprite->setAutoPuppet(kAPBackColor, true);
@@ -1856,7 +1856,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
int blend = (100 - CLIP(d.asInt(), 0, 100)) * 255 / 100;
if (blend != sprite->_blendAmount) {
sprite->_blendAmount = blend;
- channel->_dirty = true;
+ channel->setDirty();
}
if (d.asInt() == 0)
@@ -1874,11 +1874,8 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
CastMemberID targetMember = d.asMemberID();
if (targetMember != sprite->_castId) {
- movie->getWindow()->addDirtyRect(channel->getBbox());
channel->setCast(targetMember);
// Ensure the new sprite, whether larger or smaller, appears correctly on the screen
- movie->getWindow()->addDirtyRect(channel->getBbox());
- channel->_dirty = true;
}
}
break;
@@ -1912,12 +1909,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
// Since Digital Video dimensions get clarified after loading,
// we enforce them here
if (castId != sprite->_castId || (castMember && castMember->_type == kCastDigitalVideo)) {
- if (!sprite->_trails) {
- movie->getWindow()->addDirtyRect(channel->getBbox());
- channel->_dirty = true;
- }
channel->setCast(castId);
- channel->_dirty = true;
}
}
break;
@@ -1939,7 +1931,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
}
if (channelId != -1 && channelId != (int)channel->_constraint) {
channel->_constraint = d.u.i;
- channel->_dirty = true;
+ channel->setDirty();
}
}
break;
@@ -1956,7 +1948,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kTheFlipH: // D7
sprite->_thickness = (sprite->_thickness & ~kTFlipH) | ((d.asInt() ? kTFlipH : 0));
- channel->_dirty = true;
+ channel->setDirty();
sprite->setAutoPuppet(kAPThickness, true);
@@ -1964,7 +1956,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kTheFlipV: // D7
sprite->_thickness = (sprite->_thickness & ~kTFlipV) | ((d.asInt() ? kTFlipV : 0));
- channel->_dirty = true;
+ channel->setDirty();
sprite->setAutoPuppet(kAPThickness, true);
@@ -1975,7 +1967,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
uint32 newColor = g_director->transformColor(d.asInt());
if (newColor != sprite->_foreColor) {
sprite->_foreColor = newColor;
- channel->_dirty = true;
+ channel->setDirty();
}
// Based on Director in a Nutshell, page 15
@@ -1984,9 +1976,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kTheHeight:
if (d.asInt() != channel->getHeight()) {
- g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
channel->setHeight(d.asInt());
- channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
@@ -1999,7 +1989,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
case kTheInk:
if (d.asInt() != sprite->_ink) {
sprite->_ink = static_cast<InkType>(d.asInt());
- channel->_dirty = true;
+ channel->setDirty();
}
// Based on Director in a Nutshell, page 15
@@ -2008,26 +1998,20 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kTheLineSize:
sprite->_thickness = (sprite->_thickness & ~kTThickness) | ((d.asInt() + 1) & kTThickness);
- channel->_dirty = true;
+ channel->setDirty();
sprite->setAutoPuppet(kAPThickness, true);
break;
case kTheLoc:
if (channel->getPosition() != d.asPoint()) {
- movie->getWindow()->addDirtyRect(channel->getBbox());
- channel->_dirty = true;
+ channel->setNeedsDraw();
}
channel->setPosition(d.asPoint().x, d.asPoint().y);
break;
case kTheLocH:
if (d.asInt() != channel->getPosition().x) {
- // Only add a dirty rectangle for the original position if we're not rendering in trails mode.
- // Otherwise, it will erase the trail.
- if (!channel->_sprite->_trails) {
- movie->getWindow()->addDirtyRect(channel->getBbox());
- }
- channel->_dirty = true;
channel->setPosition(d.asInt(), channel->getPosition().y);
+ channel->setNeedsDraw();
}
// Based on Director in a Nutshell, page 15
@@ -2036,11 +2020,8 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kTheLocV:
if (d.asInt() != channel->getPosition().y) {
- if (!channel->_sprite->_trails) {
- movie->getWindow()->addDirtyRect(channel->getBbox());
- }
- channel->_dirty = true;
channel->setPosition(channel->getPosition().x, d.asInt());
+ channel->setNeedsDraw();
}
// Based on Director in a Nutshell, page 15
@@ -2071,7 +2052,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
case kThePattern:
if (d.asInt() != sprite->getPattern()) {
sprite->setPattern(d.asInt());
- channel->_dirty = true;
+ channel->setDirty();
}
break;
case kThePuppet:
@@ -2088,7 +2069,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
d.u.farr->arr[0].u.i, d.u.farr->arr[1].u.i,
d.u.farr->arr[2].u.i, d.u.farr->arr[3].u.i
);
- channel->_dirty = true;
+ channel->setDirty();
}
// Based on Director in a Nutshell, page 15
@@ -2121,12 +2102,12 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
case kTheType:
if (d.asInt() != sprite->_spriteType) {
sprite->_spriteType = static_cast<SpriteType>(d.asInt());
- channel->_dirty = true;
+ channel->setDirty();
}
break;
case kTheTweened: // D6
sprite->_thickness = (sprite->_thickness & ~kTTweened) | ((d.asInt() ? kTTweened : 0));
- channel->_dirty = true;
+ channel->setDirty();
sprite->setAutoPuppet(kAPThickness, true);
@@ -2136,7 +2117,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
case kTheVisible:
if ((bool)d.asInt() != channel->_visible) {
channel->_visible = (bool)d.asInt();
- channel->_dirty = true;
+ channel->setNeedsDraw();
}
break;
case kTheVolume:
@@ -2145,9 +2126,7 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kTheWidth:
if (d.asInt() != channel->getWidth()) {
- g_director->getCurrentWindow()->addDirtyRect(channel->getBbox());
channel->setWidth(d.asInt());
- channel->_dirty = true;
}
// Based on Director in a Nutshell, page 15
@@ -2158,8 +2137,6 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
warning("Lingo::setTheSprite(): Unprocessed setting field \"%s\" of sprite", field2str(field));
}
- if (channel->_dirty)
- movie->getWindow()->addDirtyRect(channel->getBbox());
}
Datum Lingo::getTheCast(Datum &id1, int field) {
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 3d8374052af..759b217a32e 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -939,9 +939,6 @@ void Score::updateSprites(RenderMode mode, bool withClean) {
}
if (channel->isDirty(nextSprite) || widgetRedrawn || mode == kRenderForceUpdate) {
- bool invalidCastMember = currentSprite && currentSprite->_spriteType == kCastMemberSprite && currentSprite->_cast == nullptr;
- if (currentSprite && !invalidCastMember && !currentSprite->_trails)
- _window->addDirtyRect(channel->getBbox());
if (currentSprite && currentSprite->_cast && currentSprite->_cast->_erase) {
currentSprite->_cast->_erase = false;
@@ -954,13 +951,13 @@ void Score::updateSprites(RenderMode mode, bool withClean) {
// Only clean out the channel if we're moving to a different frame
if (withClean)
channel->setClean(nextSprite);
- invalidCastMember = currentSprite ? (currentSprite->_spriteType == kCastMemberSprite && currentSprite->_cast == nullptr) : false;
+ bool invalidCastMember = currentSprite ? (currentSprite->_spriteType == kCastMemberSprite && currentSprite->_cast == nullptr) : false;
// Check again to see if a video has just been started by setClean.
if (channel->isActiveVideo())
_movie->_videoPlayback = true;
- if (!invalidCastMember)
- _window->addDirtyRect(channel->getBbox());
+ // flag channel for drawing
+ channel->setNeedsDraw();
if (currentSprite) {
Common::Rect bbox = channel->getBbox();
@@ -1456,7 +1453,7 @@ void Score::updateWidgets(bool hasVideoPlayback) {
channel->updateVideoTime();
if (cast && (cast->_type != kCastDigitalVideo || hasVideoPlayback) && cast->isModified()) {
channel->replaceWidget();
- _window->addDirtyRect(channel->getBbox());
+ channel->setNeedsDraw();
}
}
}
@@ -1465,7 +1462,7 @@ void Score::invalidateRectsForMember(CastMember *member) {
for (uint16 i = 0; i < _channels.size(); i++) {
Channel *channel = _channels[i];
if (channel->_sprite->_cast == member) {
- _window->addDirtyRect(channel->getBbox());
+ channel->setDirty();
}
}
}
@@ -1673,9 +1670,9 @@ uint16 Score::getRollOverSpriteIDFromPos(Common::Point pos) {
}
-Common::List<Channel *> Score::getSpriteIntersections(const Common::Rect &r) {
- Common::List<Channel *> intersections;
- Common::List<Channel *> appendix;
+Common::Array<Channel *> Score::getSpriteIntersections(const Common::Rect &r) {
+ Common::Array<Channel *> intersections;
+ Common::Array<Channel *> appendix;
for (uint i = 0; i < _channels.size(); i++) {
if (!_channels[i]->isEmpty() && !r.findIntersectingRect(_channels[i]->getBbox()).isEmpty()) {
@@ -1703,6 +1700,28 @@ uint16 Score::getSpriteIdByMemberId(CastMemberID id) {
return 0;
}
+Common::Rect Score::getChannelDirtyRectBounds() {
+ Common::Array<Common::Rect> dirtyRects;
+ for (auto &it : _channels) {
+ if (it->_needsDraw) {
+ if (!it->_lastRenderedBbox.isEmpty())
+ dirtyRects.push_back(it->_lastRenderedBbox);
+ Common::Rect bbox = it->getBbox();
+ if (!bbox.isEmpty())
+ dirtyRects.push_back(bbox);
+ }
+ }
+
+ Common::Rect result;
+ if (dirtyRects.size() == 0)
+ return result;
+ result = Common::Rect(dirtyRects.front());
+ for (auto &r : dirtyRects) {
+ result.extend(r);
+ }
+ return result;
+}
+
bool Score::refreshPointersForCastMemberID(CastMemberID id) {
// FIXME: This can be removed once Sprite is refactored to not
// keep a pointer to a CastMember.
@@ -1711,7 +1730,6 @@ bool Score::refreshPointersForCastMemberID(CastMemberID id) {
if (it->_sprite->_castId == id) {
it->_sprite->_cast = nullptr;
it->setCast(id);
- it->_dirty = true;
hit = true;
}
}
@@ -1734,7 +1752,6 @@ bool Score::refreshPointersForCastLib(uint16 castLib) {
if (it->_sprite->_castId.castLib == castLib) {
it->_sprite->_cast = nullptr;
it->setCast(it->_sprite->_castId);
- it->_dirty = true;
hit = true;
}
}
diff --git a/engines/director/score.h b/engines/director/score.h
index 56d3800998b..24b5503424e 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -121,8 +121,9 @@ public:
uint16 getActiveSpriteIDFromPos(Common::Point pos);
bool checkSpriteRollOver(uint16 spriteId, Common::Point pos);
uint16 getRollOverSpriteIDFromPos(Common::Point pos);
- Common::List<Channel *> getSpriteIntersections(const Common::Rect &r);
+ Common::Array<Channel *> getSpriteIntersections(const Common::Rect &r);
uint16 getSpriteIdByMemberId(CastMemberID id);
+ Common::Rect getChannelDirtyRectBounds();
bool refreshPointersForCastMemberID(CastMemberID id);
bool refreshPointersForCastLib(uint16 castLib);
diff --git a/engines/director/transitions.cpp b/engines/director/transitions.cpp
index 7e0d6bbe87e..46894c6a869 100644
--- a/engines/director/transitions.cpp
+++ b/engines/director/transitions.cpp
@@ -205,10 +205,7 @@ void Window::playTransition(uint frame, RenderMode mode, uint16 transDuration, u
// Changed area transition
score->updateSprites(mode);
- clipRect = _window->getDirtyRectBounds();
-
- // Ensure we redraw any other sprites intersecting the non-clip area.
- _window->clearDirtyRects();
+ clipRect = score->getChannelDirtyRectBounds();
// Some transitions depend upon an even clipRect size
if (clipRect.width() % 2 == 1)
@@ -218,9 +215,8 @@ void Window::playTransition(uint frame, RenderMode mode, uint16 transDuration, u
clipRect.bottom += 1;
clipRect.clip(Common::Rect(innerDims.width(), innerDims.height()));
- _window->addDirtyRect(clipRect);
- render(false, &nextFrame);
+ render(true, &nextFrame);
} else {
// Full stage transition
score->updateSprites(mode);
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 3e5199fd776..7f5aff55869 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -69,6 +69,7 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
_windowType = -1;
_isModal = false;
_skipFrameAdvance = false;
+ _resetScreen = false;
// Owned by the window manager
_window = new Graphics::MacWindow(id, scrollable, resizable, editable, wm);
@@ -157,6 +158,62 @@ void Window::drawChannelBox(Director::Movie *currentMovie, Graphics::ManagedSurf
}
}
+void Window::renderChannel(Channel *channel, const Common::Rect &rect, Graphics::ManagedSurface *blitTo, bool invert) {
+ Score *score = _currentMovie->getScore();
+ if (rect.isEmpty())
+ return;
+ Common::Array<Channel *> dirtyChannels = score->getSpriteIntersections(rect);
+ Common::Array<Channel *> appendix;
+ Common::Rect r = rect;
+ r.clip(blitTo->getBounds());
+ size_t idx = 0;
+ // move direct-to-stage layers to the end of the list
+ while (idx < dirtyChannels.size()) {
+ Channel *ch = dirtyChannels[idx];
+ if (ch->isActiveVideo() && ch->isVideoDirectToStage()) {
+ appendix.push_back(ch);
+ dirtyChannels.remove_at(idx);
+ continue;
+ }
+ idx++;
+ }
+ for (auto &it : appendix) {
+ dirtyChannels.push_back(it);
+ }
+
+ size_t startIdx = 0;
+ if (channel->isTrail()) {
+ // trails mode, don't redraw anything stationary below the target
+ for (size_t i = 0; i < dirtyChannels.size(); i++) {
+ if (dirtyChannels[i] == channel) {
+ startIdx = i;
+ break;
+ }
+ }
+ } else {
+ blitTo->fillRect(r, _stageColor);
+ }
+ debugC(7, kDebugImages, "Window::renderChannel(): rect (%d, %d, %d, %d), %d overlapping channels", r.left, r.top, r.right, r.bottom, dirtyChannels.size());
+
+ for (size_t i = startIdx; i < dirtyChannels.size(); i++) {
+ Channel *ch = dirtyChannels[i];
+
+ if (ch->_visible && !ch->_hideFromStage) {
+ if (ch->hasSubChannels()) {
+ Common::Array<Channel> *list = ch->getSubChannels();
+ for (auto &k : *list) {
+ inkBlitFrom(&k, r, blitTo);
+ }
+ } else {
+ inkBlitFrom(ch, r, blitTo);
+ if ((ch == channel) && invert)
+ invertChannel(ch, r);
+ }
+ }
+ }
+ addDirtyRect(r);
+}
+
bool Window::render(bool forceRedraw, Graphics::ManagedSurface *blitTo) {
if (!_currentMovie)
return false;
@@ -164,89 +221,62 @@ bool Window::render(bool forceRedraw, Graphics::ManagedSurface *blitTo) {
if (!blitTo)
blitTo = _window->getSurface();
- Common::List<Common::Rect> &dirtyRects = _window->getDirtyRectList();
+ Score *score = _currentMovie->getScore();
+
+ Channel *hiliteChannel = score->getChannelById(_currentMovie->_currentHiliteChannelId);
+
+ if (_resetScreen) {
+ _resetScreen = false;
+ forceRedraw = true;
+ }
if (forceRedraw) {
blitTo->clear(_stageColor);
+ }
+
+ // for each channel
+ // - check to see if it needs a redraw
+ // - if it does:
+ // - if trails, just draw the sprite again
+ // - if not:
+ // - draw every sprite underneath, then the sprite
+ // - disable redraw flag
+ debugC(7, kDebugImages, "Window::render(): starting draw cycle for frame %d", score->getCurrentFrameNum());
+ uint32 renderStartTime = g_system->getMillis();
+
+ for (size_t i = 0; i < score->_channels.size(); i++) {
+ Channel *chan = score->_channels[i];
+ if (!chan->_needsDraw && !forceRedraw)
+ continue;
+ Common::Rect bbox = chan->getBbox();
+
+ debugC(7, kDebugImages, "Window::render(): drawing channel %d", (int)i);
+
+ if (!chan->_lastTrail) {
+ renderChannel(chan, chan->_lastRenderedBbox, blitTo, chan == hiliteChannel);
+ }
+ renderChannel(chan, bbox, blitTo, chan == hiliteChannel);
+ chan->_needsDraw = false;
+ chan->_lastRenderedBbox = bbox;
+ chan->_lastTrail = chan->isTrail();
+ }
+
+ Common::List<Common::Rect> &dirtyRects = _window->getDirtyRectList();
+
+ if (forceRedraw) {
_window->markAllDirty();
} else {
if (dirtyRects.size() == 0 && _currentMovie->_videoPlayback == false) {
if (g_director->_debugDraw & kDebugDrawFrame) {
drawFrameCounter(blitTo);
-
_window->setContentDirty(true);
}
-
return false;
}
_window->mergeDirtyRects();
}
- Channel *hiliteChannel = _currentMovie->getScore()->getChannelById(_currentMovie->_currentHiliteChannelId);
-
- uint32 renderStartTime = g_system->getMillis();
- debugC(7, kDebugImages, "Window::render(): Updating %d rects", dirtyRects.size());
-
- for (auto &i : dirtyRects) {
- Common::Rect r = i;
- // The inner dimensions are relative to the virtual desktop while
- // r isn't, so we need to move the window to be relative to the
- // same sapce.
- Common::Rect windowRect = _window->getInnerDimensions();
- windowRect.moveTo(r.left, r.top);
- r.clip(windowRect);
-
- _dirtyChannels = _currentMovie->getScore()->getSpriteIntersections(r);
-
- bool shouldClear = true;
- Channel *trailChannel = nullptr;
- for (auto &j : _dirtyChannels) {
- bool isHidden = false;
- isHidden = j->_hideFromStage;
- if (j->_visible && !isHidden && r == j->getBbox() && j->isTrail()) {
- shouldClear = false;
- trailChannel = j;
- break;
- }
- }
-
- if (shouldClear) {
- blitTo->fillRect(r, _stageColor);
- } else if (trailChannel) {
- // Trail rendering mode; do not re-render the background and sprites underneath.
- _dirtyChannels.clear();
- _dirtyChannels.push_back(trailChannel);
- }
-
- for (int pass = 0; pass < 2; pass++) {
- for (auto &j : _dirtyChannels) {
- if (j->isActiveVideo() && j->isVideoDirectToStage()) {
- if (pass == 0)
- continue;
- } else {
- if (pass == 1)
- continue;
- }
-
- if (j->_hideFromStage)
- continue;
-
- if (j->_visible) {
- if (j->hasSubChannels()) {
- Common::Array<Channel> *list = j->getSubChannels();
- for (auto &k : *list) {
- inkBlitFrom(&k, r, blitTo);
- }
- } else {
- inkBlitFrom(j, r, blitTo);
- if (j == hiliteChannel)
- invertChannel(hiliteChannel, r);
- }
- }
- }
- }
- }
#ifdef USE_IMGUI
int selectedChannel = DT::getSelectedChannel();
@@ -272,9 +302,10 @@ bool Window::render(bool forceRedraw, Graphics::ManagedSurface *blitTo) {
if (g_director->_debugDraw & kDebugDrawFrame)
drawFrameCounter(blitTo);
+ debugC(7, kDebugImages, "Window::render(): Draw cycle finished in %d ms, %d dirty rects", g_system->getMillis() - renderStartTime, dirtyRects.size());
+
dirtyRects.clear();
_window->setContentDirty(true);
- debugC(7, kDebugImages, "Window::render(): Draw finished in %d ms", g_system->getMillis() - renderStartTime);
return true;
}
@@ -378,6 +409,7 @@ void Window::reset() {
Graphics::ManagedSurface *composeSurface = _window->getSurface();
resizeInner(composeSurface->w, composeSurface->h);
_window->setContentDirty(true);
+ _resetScreen = true;
}
void Window::inkBlitFrom(Channel *channel, Common::Rect destRect, Graphics::ManagedSurface *blitTo) {
diff --git a/engines/director/window.h b/engines/director/window.h
index 6d2b025a8b5..e3caba765d4 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -109,6 +109,7 @@ public:
~Window();
bool render(bool forceRedraw = false, Graphics::ManagedSurface *blitTo = nullptr);
+ void renderChannel(Channel *channel, const Common::Rect &rect, Graphics::ManagedSurface *blitTo = nullptr, bool invert = false);
void invertChannel(Channel *channel, const Common::Rect &destRect);
bool needsAppliedColor(DirectorPlotData *pd);
@@ -220,13 +221,13 @@ public:
Graphics::MacWindow *_window;
Graphics::MacWindowManager *_wm;
- Common::List<Channel *> _dirtyChannels;
TransParams *_puppetTransition;
MovieReference _nextMovie;
Common::List<MovieReference> _movieStack;
bool _newMovieStarted;
bool _skipFrameAdvance;
+ bool _resetScreen;
private:
uint32 _stageColor;
Commit: 0e69b6057e957dc852dc4ecf0ab7037afd8eed88
https://github.com/scummvm/scummvm/commit/0e69b6057e957dc852dc4ecf0ab7037afd8eed88
Author: Scott Percival (code at moral.net.au)
Date: 2026-05-16T20:46:09+02:00
Commit Message:
DIRECTOR: Force a whole-screen redraw after a movie switch
Fixes transitions between movies in Eastern Mind.
Changed paths:
engines/director/score.cpp
engines/director/window.cpp
engines/director/window.h
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 759b217a32e..9ff70d97ff0 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -1701,6 +1701,10 @@ uint16 Score::getSpriteIdByMemberId(CastMemberID id) {
}
Common::Rect Score::getChannelDirtyRectBounds() {
+ // if we've just started a new movie, we need to redraw everything.
+ if (_window->_newMovieFirstDraw) {
+ return _window->_window->getInnerDimensions();
+ }
Common::Array<Common::Rect> dirtyRects;
for (auto &it : _channels) {
if (it->_needsDraw) {
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 7f5aff55869..c6af79c18f9 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -62,6 +62,7 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
_currentMovie = nullptr;
_nextMovie.frameI = -1;
_newMovieStarted = true;
+ _newMovieFirstDraw = true;
_objType = kWindowObj;
_startFrame = _vm->getStartMovie().startFrame;
@@ -229,9 +230,14 @@ bool Window::render(bool forceRedraw, Graphics::ManagedSurface *blitTo) {
_resetScreen = false;
forceRedraw = true;
}
+ if (_newMovieFirstDraw) {
+ _newMovieFirstDraw = false;
+ forceRedraw = true;
+ }
if (forceRedraw) {
blitTo->clear(_stageColor);
+ _window->markAllDirty();
}
// for each channel
@@ -570,6 +576,7 @@ void Window::loadNewSharedCast(Cast *previousSharedCast) {
bool Window::loadNextMovie() {
_soundManager->changingMovie();
_newMovieStarted = true;
+ _newMovieFirstDraw = true;
_currentPath = Common::firstPathComponents(_nextMovie.movie, g_director->_dirSeparator);
Common::Path archivePath = Common::Path(_currentPath, g_director->_dirSeparator);
diff --git a/engines/director/window.h b/engines/director/window.h
index e3caba765d4..5c0bc63a505 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -226,6 +226,7 @@ public:
MovieReference _nextMovie;
Common::List<MovieReference> _movieStack;
bool _newMovieStarted;
+ bool _newMovieFirstDraw;
bool _skipFrameAdvance;
bool _resetScreen;
More information about the Scummvm-git-logs
mailing list