[Scummvm-git-logs] scummvm master -> 17bd61d4132332572859255f526a2c2c2fa40c3c
npjg
noreply at scummvm.org
Sun Feb 2 16:52:15 UTC 2025
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
73e5a7bbe3 MEDIASTATION: Move to dirty rectangles-based drawing
85e8bfa7a3 MEDIASTATION: Allow stopping sounds once started
b4b4568dec MEDIASTATION: Make movies & sprites render more properly
a2a06746ca MEDIASTATION: Store variable bools internally as ints
17bd61d413 MEDIASTATION: Properly deactivate hotspots when mouse is moved outside any hotspot
Commit: 73e5a7bbe30527f125885173a022e00557825c0b
https://github.com/scummvm/scummvm/commit/73e5a7bbe30527f125885173a022e00557825c0b
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-02T11:52:05-05:00
Commit Message:
MEDIASTATION: Move to dirty rectangles-based drawing
Changed paths:
engines/mediastation/asset.cpp
engines/mediastation/asset.h
engines/mediastation/assets/image.cpp
engines/mediastation/assets/image.h
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/sprite.h
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index f28e39d94c2..9254eadab1e 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -45,6 +45,11 @@ int Asset::zIndex() const {
return _header->_zIndex;
}
+Common::Rect *Asset::getBbox() {
+ return _header->_boundingBox;
+}
+
+
void Asset::processTimeEventHandlers() {
if (!_isActive) {
warning("Asset::processTimeEventHandlers(): Attempted to process time event handlers while asset %d is not playing", _header->_id);
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index f746618d666..36a4380b360 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -44,6 +44,12 @@ public:
virtual void process() {
return;
}
+
+ // For spatial assets, actually redraws the dirty area.
+ virtual void redraw(Common::Rect &rect) {
+ return;
+ }
+
// Runs built-in bytecode methods.
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) = 0;
// Called to have the asset do any processing, like drawing new frames,
@@ -67,6 +73,7 @@ public:
AssetHeader *getHeader() const {
return _header;
}
+ Common::Rect *getBbox();
protected:
AssetHeader *_header = nullptr;
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index b0d16c4a272..352fb35a768 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -40,15 +40,14 @@ Operand Image::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
switch (methodId) {
case kSpatialShowMethod: {
assert(args.empty());
- _isActive = true;
- g_engine->addPlayingAsset(this);
+ spatialShow();
return Operand();
break;
}
case kSpatialHideMethod: {
assert(args.empty());
- _isActive = false;
+ spatialHide();
return Operand();
break;
}
@@ -66,6 +65,38 @@ Operand Image::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
}
}
+void Image::redraw(Common::Rect &rect) {
+ if (!_isActive) {
+ return;
+ }
+
+ Common::Point leftTop = getLeftTop();
+ Common::Rect bbox(leftTop, _bitmap->width(), _bitmap->height());
+ Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
+ if (!areaToRedraw.isEmpty()) {
+ Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
+ areaToRedraw.translate(-leftTop.x, -leftTop.y);
+ g_engine->_screen->simpleBlitFrom(_bitmap->_surface, areaToRedraw, originOnScreen);
+ }
+}
+
+void Image::spatialShow() {
+ _isActive = true;
+ g_engine->addPlayingAsset(this);
+ Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
+ g_engine->_dirtyRects.push_back(bbox);
+}
+
+void Image::spatialHide() {
+ _isActive = false;
+ Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
+ g_engine->_dirtyRects.push_back(bbox);
+}
+
+Common::Point Image::getLeftTop() {
+ return Common::Point(_header->_x + _header->_boundingBox->left, _header->_y + _header->_boundingBox->top);
+}
+
void Image::readChunk(Chunk &chunk) {
BitmapHeader *bitmapHeader = new BitmapHeader(chunk);
_bitmap = new Bitmap(chunk, bitmapHeader);
diff --git a/engines/mediastation/assets/image.h b/engines/mediastation/assets/image.h
index a950e207b88..ddcc1966966 100644
--- a/engines/mediastation/assets/image.h
+++ b/engines/mediastation/assets/image.h
@@ -38,10 +38,18 @@ public:
virtual void readChunk(Chunk &chunk) override;
+ virtual void redraw(Common::Rect &rect) override;
+
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) override;
private:
Bitmap *_bitmap = nullptr;
+
+ // Script method implementations.
+ void spatialShow();
+ void spatialHide();
+
+ Common::Point getLeftTop();
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 2da51c9964c..47ca95e944b 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -215,23 +215,15 @@ void Movie::timePlay() {
// TODO: Play movies one chunk at a time, which more directly approximates
// the original's reading from the CD one chunk at a time.
if (_isActive) {
- error("Movie::play(): Attempted to play a movie that is already playing");
+ warning("Movie::timePlay(): Attempted to play a movie that is already playing");
return;
}
- // SET ANIMATION VARIABLES.
_isActive = true;
_startTime = g_system->getMillis();
_lastProcessedTime = 0;
g_engine->addPlayingAsset(this);
-
- // GET THE DURATION OF THE MOVIE.
- _duration = 0;
- for (MovieFrame *frame : _frames) {
- if (frame->endInMilliseconds() > _duration) {
- _duration = frame->endInMilliseconds();
- }
- }
+ _framesNotYetShown = _frames;
// START PLAYING SOUND.
// TODO: This won't work when we have some chunks that don't have audio.
@@ -249,59 +241,103 @@ void Movie::timePlay() {
}
void Movie::timeStop() {
- // RESET ANIMATION VARIABLES.
+ if (!_isActive) {
+ warning("Movie::timePlay(): Attempted to stop a movie that isn't playing");
+ return;
+ }
_isActive = false;
_startTime = 0;
_lastProcessedTime = 0;
-
+ _framesNotYetShown.clear();
runEventHandlerIfExists(kMovieStoppedEvent);
}
void Movie::process() {
processTimeEventHandlers();
- drawNextFrame();
+ updateFrameState();
}
-bool Movie::drawNextFrame() {
+void Movie::updateFrameState() {
// TODO: We'll need to support persistent frames in movies too. Do movies
// have the same distinction between spatialShow and timePlay that sprites
// do?
uint currentTime = g_system->getMillis();
uint movieTime = currentTime - _startTime;
- debugC(5, kDebugGraphics, "GRAPHICS (Movie %d): Starting blitting (movie time: %d)", _header->_id, movieTime);
- bool donePlaying = movieTime > _duration;
- if (donePlaying) {
+ debugC(5, kDebugGraphics, "Movie::updateFrameState (%d): Starting update (movie time: %d)", _header->_id, movieTime);
+ if (_framesNotYetShown.empty()) {
_isActive = false;
_startTime = 0;
_lastProcessedTime = 0;
-
runEventHandlerIfExists(kMovieEndEvent);
- return false;
+ return;
+ }
+
+ // This complexity is necessary becuase movies can have more than one frame
+ // showing at the same time - for instance, a movie background and an
+ // animation on that background are a part of the saem movie and are on
+ // screen at the same time, it's just the starting and ending times of one
+ // can be different from the starting and ending times of another.
+ //
+ // We can rely on the frames being ordered in order of their start. First,
+ // see if there are any new frames to show.
+ for (auto it = _framesNotYetShown.begin(); it != _framesNotYetShown.end();) {
+ MovieFrame *frame = *it;
+ bool isAfterStart = movieTime >= frame->startInMilliseconds();
+ if (isAfterStart) {
+ _framesOnScreen.push_back(frame);
+ g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+
+ // We don't need ++it because we will either have another frame
+ // that needs to be drawn, or we have reached the end of the new
+ // frames.
+ it = _framesNotYetShown.erase(it);
+ } else {
+ // We've hit a frame that shouldn't yet be shown.
+ // Rely on the ordering to not bother with any further frames.
+ break;
+ }
}
- Common::Array<MovieFrame *> framesToDraw;
- for (MovieFrame *frame : _frames) {
- bool isAfterStart = _startTime + frame->startInMilliseconds() <= currentTime;
- bool isBeforeEnd = _startTime + frame->endInMilliseconds() >= currentTime;
- if (!isAfterStart || (isAfterStart && !isBeforeEnd)) {
- continue;
+ // Now see if there are any old frames that no longer need to be shown.
+ for (auto it = _framesOnScreen.begin(); it != _framesOnScreen.end();) {
+ MovieFrame *frame = *it;
+ bool isAfterEnd = movieTime >= frame->endInMilliseconds();
+ if (isAfterEnd) {
+ g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+ it = _framesOnScreen.erase(it);
+ } else {
+ ++it;
}
- debugC(5, kDebugGraphics, " (time: %d ms) Must re-draw frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d", movieTime, frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
- framesToDraw.push_back(frame);
}
- Common::sort(framesToDraw.begin(), framesToDraw.end(), [](MovieFrame * a, MovieFrame * b) {
+ // Show the frames that are currently active, for debugging purposes.
+ for (MovieFrame *frame : _framesOnScreen) {
+ debugC(5, kDebugGraphics, " (time: %d ms) Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d", movieTime, frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
+ }
+}
+
+void Movie::redraw(Common::Rect &rect) {
+ // Make sure the frames are ordered properly before we attempt to draw them.
+ Common::sort(_framesOnScreen.begin(), _framesOnScreen.end(), [](MovieFrame * a, MovieFrame * b) {
return a->zCoordinate() > b->zCoordinate();
});
- for (MovieFrame *frame : framesToDraw) {
- g_engine->_screen->simpleBlitFrom(frame->_surface, Common::Point(frame->left(), frame->top()));
+
+ for (MovieFrame *frame : _framesOnScreen) {
+ Common::Rect bbox = getFrameBoundingBox(frame);
+ Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
+ if (!areaToRedraw.isEmpty()) {
+ Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
+ areaToRedraw.translate(-frame->left() - _header->_boundingBox->left, -frame->top() - _header->_boundingBox->top);
+ g_engine->_screen->simpleBlitFrom(frame->_surface, areaToRedraw, originOnScreen);
+ }
}
+}
- uint blitEnd = g_system->getMillis() - _startTime;
- uint elapsedTime = blitEnd - movieTime;
- debugC(5, kDebugGraphics, "GRAPHICS (Movie %d): Finished blitting in %d ms (movie time: %d ms)", _header->_id, elapsedTime, blitEnd);
- return true;
+Common::Rect Movie::getFrameBoundingBox(MovieFrame *frame) {
+ Common::Rect bbox = frame->boundingBox();
+ bbox.translate(_header->_boundingBox->left, _header->_boundingBox->top);
+ return bbox;
}
void Movie::readChunk(Chunk &chunk) {
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index f81f40b44c2..0e7c435f1d2 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -103,18 +103,24 @@ public:
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) override;
virtual void process() override;
+ virtual void redraw(Common::Rect &rect) override;
+
private:
Common::Array<MovieFrame *> _frames;
Common::Array<MovieFrame *> _stills;
Common::Array<MovieFrameFooter *> _footers;
Common::Array<Audio::SeekableAudioStream *> _audioStreams;
+ Common::Array<MovieFrame *> _framesNotYetShown;
+ Common::Array<MovieFrame *> _framesOnScreen;
+
// Method implementations. These should be called from callMethod.
void timePlay();
void timeStop();
- // Internal helper functions.
- bool drawNextFrame();
+ void updateFrameState();
+
+ Common::Rect getFrameBoundingBox(MovieFrame *frame);
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index f3aae059dae..350e96f4088 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -122,21 +122,15 @@ void Sprite::spatialShow() {
_isActive = true;
g_engine->addPlayingAsset(this);
- // Persist the first frame.
- // TODO: Is there anything that says what the persisted frame should be?
- SpriteFrame *firstFrame = _frames[0];
- for (SpriteFrame *frame : _frames) {
- if (frame->index() < firstFrame->index()) {
- firstFrame = frame;
- }
+ _isPaused = true;
+ if (_activeFrame == nullptr) {
+ showFrame(0);
}
- _persistFrame = firstFrame;
}
void Sprite::timePlay() {
- debugC(5, kDebugScript, "Called Sprite::timePlay");
_isActive = true;
- _persistFrame = nullptr;
+ _isPaused = false;
_startTime = g_system->getMillis();
_lastProcessedTime = 0;
_nextFrameTime = 0;
@@ -152,9 +146,9 @@ void Sprite::timePlay() {
}
void Sprite::movieReset() {
- debugC(5, kDebugScript, "Called Sprite::movieReset");
_isActive = true;
// We do NOT reset the persisting frame, because it should keep showing!
+ _isPaused = true;
_startTime = 0;
_currentFrameIndex = 0;
_nextFrameTime = 0;
@@ -162,10 +156,7 @@ void Sprite::movieReset() {
}
void Sprite::process() {
- drawNextFrame();
-
- // TODO: I don't think sprites support time-based event handlers. Because we
- // have a separate timer for restarting the sprite when it expires.
+ updateFrameState();
}
void Sprite::readChunk(Chunk &chunk) {
@@ -182,42 +173,31 @@ void Sprite::readChunk(Chunk &chunk) {
});
}
-void Sprite::drawNextFrame() {
- // TODO: With a dirty rect-based system, we would only need to redraw the frame
- // when it NEEDS to be redrawn. But since the whole screen is currently redrawn
- // every time, the persisting frame needs to be redrawn too.
- bool redrawPersistentFrame = _persistFrame != nullptr;
- if (redrawPersistentFrame) {
- debugC(5, kDebugGraphics, "GRAPHICS (Sprite %d): Drawing persistent frame %d", _header->_id, _persistFrame->index());
- drawFrame(_persistFrame);
+void Sprite::updateFrameState() {
+ if (_isPaused) {
return;
}
uint currentTime = g_system->getMillis() - _startTime;
- bool redrawCurrentFrame = currentTime <= _nextFrameTime;
- if (redrawCurrentFrame) {
- // Just redraw the current frame in case it was covered over.
- // See TODO above.
- SpriteFrame *currentFrame = _frames[_currentFrameIndex];
- debugC(5, kDebugGraphics, "GRAPHICS (Sprite %d): Re-drawing current frame %d", _header->_id, currentFrame->index());
- drawFrame(currentFrame);
+ bool drawNextFrame = currentTime >= _nextFrameTime;
+ if (!drawNextFrame) {
return;
}
- SpriteFrame *nextFrame = _frames[_currentFrameIndex];
- debugC(5, kDebugGraphics, "GRAPHICS (Sprite %d): Drawing next frame %d (@%d)", _header->_id, nextFrame->index(), _nextFrameTime);
+ showFrame(_currentFrameIndex);
+
uint frameDuration = 1000 / _header->_frameRate;
- _nextFrameTime = _currentFrameIndex * frameDuration;
- drawFrame(nextFrame);
+ _nextFrameTime = ++_currentFrameIndex * frameDuration;
- bool spriteFinishedPlaying = (++_currentFrameIndex == _frames.size());
+ bool spriteFinishedPlaying = (_currentFrameIndex == _frames.size());
if (spriteFinishedPlaying) {
// Sprites always keep their last frame showing until they are hidden
// with spatialHide.
- _persistFrame = _frames[_currentFrameIndex - 1];
- _isActive = true;
+ showFrame(_currentFrameIndex - 1);
+ _isPaused = true;
// But otherwise, the sprite's params should be reset.
+ _isActive = true;
_startTime = 0;
_lastProcessedTime = 0;
_currentFrameIndex = 0;
@@ -227,11 +207,37 @@ void Sprite::drawNextFrame() {
}
}
-void Sprite::drawFrame(SpriteFrame *frame) {
- uint frameLeft = frame->left() + _header->_boundingBox->left;
- uint frameTop = frame->top() + _header->_boundingBox->top;
- debugC(5, kDebugGraphics, " Sprite frame %d (%d x %d) @ (%d, %d)", frame->index(), frame->width(), frame->height(), frameLeft, frameTop);
- g_engine->_screen->simpleBlitFrom(frame->_surface, Common::Point(frameLeft, frameTop));
+void Sprite::redraw(Common::Rect &rect) {
+ if (_activeFrame == nullptr) {
+ return;
+ }
+
+ Common::Rect bbox = getActiveFrameBoundingBox();
+ Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
+ if (!areaToRedraw.isEmpty()) {
+ Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
+ areaToRedraw.translate(-_activeFrame->left() - _header->_boundingBox->left, -_activeFrame->top() - _header->_boundingBox->top);
+ g_engine->_screen->simpleBlitFrom(_activeFrame->_surface, areaToRedraw, originOnScreen);
+ }
+}
+
+void Sprite::showFrame(uint frameIndex) {
+ // Erase the previous frame.
+ if (_activeFrame != nullptr) {
+ g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
+ }
+
+ // Show the next frame.
+ _activeFrame = _frames[frameIndex];
+ g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
+}
+
+Common::Rect Sprite::getActiveFrameBoundingBox() {
+ // The frame dimensions are relative to those of the sprite movie.
+ // So we must get the absolute coordinates.
+ Common::Rect bbox = _activeFrame->boundingBox();
+ bbox.translate(_header->_boundingBox->left, _header->_boundingBox->top);
+ return bbox;
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index c1a3114fffb..32f9bf9e7fa 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -65,12 +65,14 @@ public:
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) override;
virtual void process() override;
+ virtual void redraw(Common::Rect &rect) override;
virtual void readChunk(Chunk &chunk) override;
private:
Common::Array<SpriteFrame *> _frames;
- SpriteFrame *_persistFrame = nullptr;
+ SpriteFrame *_activeFrame = nullptr;
+ bool _isPaused = false;
uint _currentFrameIndex = 0;
uint _nextFrameTime = 0;
@@ -79,9 +81,9 @@ private:
void timePlay();
void movieReset();
- // Helper functions.
- void drawNextFrame();
- void drawFrame(SpriteFrame *frame);
+ void updateFrameState();
+ void showFrame(uint frameIndex);
+ Common::Rect getActiveFrameBoundingBox();
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 966cb1b1756..62ee2e2c386 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -145,17 +145,7 @@ Common::Error MediaStationEngine::run() {
break;
}
- // PROCESS ANY ASSETS CURRENTLY PLAYING.
- // TODO: Implement a dirty-rect based rendering system rather than
- // redrawing the screen each time. This will require keeping track of
- // all the images on screen at any given time, rather than just letting
- // the movies handle their own drawing.
- //
- // First, they all need to be sorted by z-coordinate.
- debugC(5, kDebugGraphics, "***** START RENDERING ***");
- Common::sort(_assetsPlaying.begin(), _assetsPlaying.end(), [](Asset * a, Asset * b) {
- return a->zIndex() > b->zIndex();
- });
+ debugC(5, kDebugGraphics, "***** START SCREEN UPDATE ***");
for (auto it = _assetsPlaying.begin(); it != _assetsPlaying.end();) {
(*it)->process();
if (!(*it)->isActive()) {
@@ -164,10 +154,10 @@ Common::Error MediaStationEngine::run() {
++it;
}
}
- debugC(5, kDebugGraphics, "***** END RENDERING ***");
+ redraw();
+ debugC(5, kDebugGraphics, "***** END SCREEN UPDATE ***");
- // UPDATE THE SCREEN.
- g_engine->_screen->update();
+ _screen->update();
g_system->delayMillis(10);
}
@@ -244,6 +234,30 @@ void MediaStationEngine::processEvents() {
}
}
+void MediaStationEngine::redraw() {
+ if (_dirtyRects.empty()) {
+ return;
+ }
+
+ Common::sort(_assetsPlaying.begin(), _assetsPlaying.end(), [](Asset * a, Asset * b) {
+ return a->zIndex() > b->zIndex();
+ });
+
+ for (Common::Rect dirtyRect : _dirtyRects) {
+ for (Asset *asset : _assetsPlaying) {
+ Common::Rect *bbox = asset->getBbox();
+ if (bbox != nullptr) {
+ if (dirtyRect.intersects(*bbox)) {
+ asset->redraw(dirtyRect);
+ }
+ }
+ }
+ }
+
+ _screen->update();
+ _dirtyRects.clear();
+}
+
Context *MediaStationEngine::loadContext(uint32 contextId) {
if (_boot == nullptr) {
error("Cannot load contexts before BOOT.STM is read");
@@ -360,6 +374,7 @@ void MediaStationEngine::branchToScreen(uint32 contextId) {
Context *context = loadContext(contextId);
_currentContext = context;
+ _dirtyRects.push_back(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT));
if (context->_screenAsset != nullptr) {
// TODO: Make the screen an asset just like everything else so we can
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index c4a1ae2abc0..d06d72101a3 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -72,6 +72,7 @@ public:
};
bool isFirstGenerationEngine();
void processEvents();
+ void redraw();
void setPalette(Asset *palette);
void addPlayingAsset(Asset *assetToAdd);
@@ -88,6 +89,8 @@ public:
Audio::Mixer *_mixer = nullptr;
Context *_currentContext = nullptr;
+ Common::Array<Common::Rect> _dirtyRects;
+
// All Media Station titles run at 640x480.
const uint16 SCREEN_WIDTH = 640;
const uint16 SCREEN_HEIGHT = 480;
Commit: 85e8bfa7a3a8e6af7aa1e08962ae00aa32f25922
https://github.com/scummvm/scummvm/commit/85e8bfa7a3a8e6af7aa1e08962ae00aa32f25922
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-02T11:52:05-05:00
Commit Message:
MEDIASTATION: Allow stopping sounds once started
Changed paths:
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/sound.cpp
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 47ca95e944b..8791f749139 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -157,6 +157,8 @@ MovieFrame::~MovieFrame() {
}
Movie::~Movie() {
+ g_engine->_mixer->stopHandle(_soundHandle);
+
for (MovieFrame *frame : _frames) {
delete frame;
}
@@ -230,11 +232,12 @@ void Movie::timePlay() {
if (!_audioStreams.empty()) {
Audio::QueuingAudioStream *audio = Audio::makeQueuingAudioStream(22050, false);
for (Audio::SeekableAudioStream *stream : _audioStreams) {
+ stream->rewind();
audio->queueAudioStream(stream, DisposeAfterUse::NO);
}
// Then play the audio!
- Audio::SoundHandle handle;
- g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, audio, -1, Audio::Mixer::kMaxChannelVolume);
+ g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, audio, -1, Audio::Mixer::kMaxChannelVolume);
+ audio->finish();
}
runEventHandlerIfExists(kMovieBeginEvent);
@@ -249,6 +252,10 @@ void Movie::timeStop() {
_startTime = 0;
_lastProcessedTime = 0;
_framesNotYetShown.clear();
+
+ g_engine->_mixer->stopHandle(_soundHandle);
+ _soundHandle = Audio::SoundHandle();
+
runEventHandlerIfExists(kMovieStoppedEvent);
}
@@ -425,7 +432,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
// READ THE AUDIO.
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) Reading audio chunk... (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
- bool isAudioChunk = (chunk._id = _header->_audioChunkReference);
+ bool isAudioChunk = (chunk._id == _header->_audioChunkReference);
if (isAudioChunk) {
byte *buffer = (byte *)malloc(chunk._length);
chunk.read((void *)buffer, chunk._length);
@@ -436,12 +443,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
break;
case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050:
- // TODO: The interface here is different. We can't pass in the
- // buffers directly. We have to make a stream first.
- // stream = Audio::makeADPCMStream(buffer, chunk.length,
- // DisposeAfterUse::NO, Audio::ADPCMType::kADPCMMSIma, 22050, 1,
- // 4);
- error("Movie::readSubfile(): ADPCM decoding not implemented yet");
+ error("Movie::readSubfile(): ADPCM decoding not supported for movies");
break;
default:
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index 0e7c435f1d2..acb8cac62e7 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -110,6 +110,7 @@ private:
Common::Array<MovieFrame *> _stills;
Common::Array<MovieFrameFooter *> _footers;
Common::Array<Audio::SeekableAudioStream *> _audioStreams;
+ Audio::SoundHandle _soundHandle;
Common::Array<MovieFrame *> _framesNotYetShown;
Common::Array<MovieFrame *> _framesOnScreen;
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index f7170b01b82..ae42081897d 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -35,6 +35,8 @@ Sound::Sound(AssetHeader *header) : Asset(header) {
}
Sound::~Sound() {
+ g_engine->_mixer->stopHandle(_handle);
+
for (Audio::SeekableAudioStream *stream : _streams) {
delete stream;
}
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 62ee2e2c386..0c21021fac1 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -51,8 +51,6 @@ MediaStationEngine::MediaStationEngine(OSystem *syst, const ADGameDescription *g
}
MediaStationEngine::~MediaStationEngine() {
- _mixer = nullptr;
-
delete _screen;
_screen = nullptr;
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index d06d72101a3..991908c37e9 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -86,7 +86,6 @@ public:
Common::HashMap<uint32, Variable *> _variables;
Graphics::Screen *_screen = nullptr;
- Audio::Mixer *_mixer = nullptr;
Context *_currentContext = nullptr;
Common::Array<Common::Rect> _dirtyRects;
Commit: b4b4568dec96f42d5e26973aa4d04894115df1a9
https://github.com/scummvm/scummvm/commit/b4b4568dec96f42d5e26973aa4d04894115df1a9
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-02T11:52:05-05:00
Commit Message:
MEDIASTATION: Make movies & sprites render more properly
Changed paths:
engines/mediastation/asset.cpp
engines/mediastation/asset.h
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/sprite.h
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index 9254eadab1e..74f2670a2a4 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -49,6 +49,18 @@ Common::Rect *Asset::getBbox() {
return _header->_boundingBox;
}
+void Asset::setActive() {
+ _isActive = true;
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
+ g_engine->addPlayingAsset(this);
+}
+
+void Asset::setInactive() {
+ _isActive = false;
+ _startTime = 0;
+ _lastProcessedTime = 0;
+}
void Asset::processTimeEventHandlers() {
if (!_isActive) {
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 36a4380b360..db318ceeba1 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -64,6 +64,8 @@ public:
virtual void readChunk(Chunk &chunk);
virtual void readSubfile(Subfile &subfile, Chunk &chunk);
+ void setInactive();
+ void setActive();
void processTimeEventHandlers();
void runEventHandlerIfExists(EventType eventType);
void runKeyDownEventHandlerIfExists(Common::KeyState keyState);
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 8791f749139..5e0443437d6 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -73,8 +73,7 @@ MovieFrameFooter::MovieFrameFooter(Chunk &chunk) {
MovieFrame::MovieFrame(Chunk &chunk, MovieFrameHeader *header) :
Bitmap(chunk, header),
- _footer(nullptr),
- _showing(false) {
+ _footer(nullptr) {
_bitmapHeader = header;
}
@@ -156,6 +155,13 @@ MovieFrame::~MovieFrame() {
_footer = nullptr;
}
+Movie::Movie(AssetHeader *header) : Asset(header) {
+ if (header->_startup == kAssetStartupActive) {
+ setActive();
+ _showByDefault = true;
+ }
+}
+
Movie::~Movie() {
g_engine->_mixer->stopHandle(_soundHandle);
@@ -190,7 +196,7 @@ Operand Movie::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
case kSpatialShowMethod: {
assert(args.empty());
- warning("Movie::callMethod(): spatialShow not implemented");
+ spatialShow();
return Operand();
}
@@ -202,31 +208,77 @@ Operand Movie::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
case kSpatialHideMethod: {
assert(args.empty());
- warning("Movie::callMethod(): spatialHide not implemented");
+ spatialHide();
return Operand();
}
+ case kIsPlayingMethod: {
+ assert(args.empty());
+ Operand returnValue(kOperandTypeLiteral1);
+ returnValue.putInteger(_isPlaying);
+ return returnValue;
+ }
+
default: {
- error("Got unimplemented method ID %d", methodId);
+ error("Movie::callMethod(): Got unimplemented method ID %d", methodId);
+ }
+ }
+}
+
+void Movie::spatialShow() {
+ if (_isPlaying) {
+ warning("Movie::spatialShow(): (%d) Attempted to spatialShow movie that is already showing", _header->_id);
+ return;
+ } else if (_isShowing) {
+ warning("Movie::spatialShow(): (%d) Attempted to spatialShow movie that is already showing", _header->_id);
+ return;
+ } else if (_stills.empty()) {
+ warning("Movie::spatialShow(): (%d) No still frame to show", _header->_id);
+ return;
}
+
+ // TODO: For movies with keyframes, there is more than one still and they
+ // must be composited. All other movies should have just one still.)
+ _framesNotYetShown.clear();
+ _framesOnScreen.clear();
+ for (MovieFrame *still : _stills) {
+ _framesOnScreen.push_back(still);
+ g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
}
+
+ setActive();
+ _isShowing = true;
+ _isPlaying = false;
+}
+
+void Movie::spatialHide() {
+ if (_isPlaying) {
+ warning("Movie::spatialShow(): (%d) Attempted to spatialHide movie that is playing", _header->_id);
+ return;
+ } else if (!_isShowing) {
+ warning("Movie::spatialHide(): (%d) Attempted to spatialHide movie that is not showing", _header->_id);
+ return;
+ }
+
+ for (MovieFrame *frame : _framesOnScreen) {
+ g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+ }
+ _framesOnScreen.clear();
+ _framesNotYetShown.clear();
+
+ _isShowing = false;
+ _isPlaying = false;
+ setInactive();
}
void Movie::timePlay() {
- debugC(5, kDebugScript, "Called Movie::timePlay()");
// TODO: Play movies one chunk at a time, which more directly approximates
// the original's reading from the CD one chunk at a time.
- if (_isActive) {
- warning("Movie::timePlay(): Attempted to play a movie that is already playing");
+ if (_isPlaying) {
+ warning("Movie::timePlay(): (%d) Attempted to play a movie that is already playing", _header->_id);
return;
}
- _isActive = true;
- _startTime = g_system->getMillis();
- _lastProcessedTime = 0;
- g_engine->addPlayingAsset(this);
- _framesNotYetShown = _frames;
-
// START PLAYING SOUND.
// TODO: This won't work when we have some chunks that don't have audio.
if (!_audioStreams.empty()) {
@@ -240,22 +292,42 @@ void Movie::timePlay() {
audio->finish();
}
+ _framesNotYetShown = _frames;
+ _isShowing = true;
+ _isPlaying = true;
+ setActive();
runEventHandlerIfExists(kMovieBeginEvent);
}
void Movie::timeStop() {
- if (!_isActive) {
- warning("Movie::timePlay(): Attempted to stop a movie that isn't playing");
+ if (!_isShowing) {
+ warning("Movie::timeStop(): (%d) Attempted to stop a movie that isn't showing", _header->_id);
+ return;
+ } else if (!_isPlaying) {
+ warning("Movie::timePlay(): (%d) Attempted to stop a movie that isn't playing", _header->_id);
return;
}
- _isActive = false;
- _startTime = 0;
- _lastProcessedTime = 0;
+
+ for (MovieFrame *frame : _framesOnScreen) {
+ g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+ }
+ _framesOnScreen.clear();
_framesNotYetShown.clear();
-
+
g_engine->_mixer->stopHandle(_soundHandle);
_soundHandle = Audio::SoundHandle();
+ // Show the persistent frames.
+ _isPlaying = false;
+ if (!_stills.empty()) {
+ for (MovieFrame *still : _stills) {
+ _framesOnScreen.push_back(still);
+ g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
+ }
+ } else {
+ setInactive();
+ }
+
runEventHandlerIfExists(kMovieStoppedEvent);
}
@@ -265,17 +337,27 @@ void Movie::process() {
}
void Movie::updateFrameState() {
- // TODO: We'll need to support persistent frames in movies too. Do movies
- // have the same distinction between spatialShow and timePlay that sprites
- // do?
+ if (_showByDefault) {
+ spatialShow();
+ _showByDefault = false;
+ }
+
+ if (!_isPlaying) {
+ debugC(6, kDebugGraphics, "Movie::updateFrameState (%d): Not playing", _header->_id);
+ for (MovieFrame *frame : _framesOnScreen) {
+ debugC(6, kDebugGraphics, " PERSIST: Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d",
+ frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
+ }
+ return;
+ }
uint currentTime = g_system->getMillis();
uint movieTime = currentTime - _startTime;
debugC(5, kDebugGraphics, "Movie::updateFrameState (%d): Starting update (movie time: %d)", _header->_id, movieTime);
if (_framesNotYetShown.empty()) {
- _isActive = false;
- _startTime = 0;
- _lastProcessedTime = 0;
+ _isPlaying = false;
+ setInactive();
+ _framesOnScreen.clear();
runEventHandlerIfExists(kMovieEndEvent);
return;
}
@@ -469,6 +551,14 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
}
// SET THE MOVIE FRAME FOOTERS.
+ for (MovieFrame *frame : _stills) {
+ for (MovieFrameFooter *footer : _footers) {
+ if (frame->index() == footer->_index) {
+ frame->setFooter(footer);
+ }
+ }
+ }
+
for (MovieFrame *frame : _frames) {
for (MovieFrameFooter *footer : _footers) {
if (frame->index() == footer->_index) {
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index acb8cac62e7..a0ee439c1bb 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -79,8 +79,6 @@ public:
// that could be confusing.
uint32 zCoordinate();
- bool _showing = false;
-
private:
MovieFrameHeader *_bitmapHeader = nullptr;
MovieFrameFooter *_footer = nullptr;
@@ -94,7 +92,7 @@ enum MovieSectionType {
class Movie : public Asset {
public:
- Movie(AssetHeader *header) : Asset(header) {};
+ Movie(AssetHeader *header);
virtual ~Movie() override;
virtual void readChunk(Chunk &chunk) override;
@@ -106,6 +104,10 @@ public:
virtual void redraw(Common::Rect &rect) override;
private:
+ bool _showByDefault = false;
+ bool _isShowing = false;
+ bool _isPlaying = false;
+
Common::Array<MovieFrame *> _frames;
Common::Array<MovieFrame *> _stills;
Common::Array<MovieFrameFooter *> _footers;
@@ -115,9 +117,11 @@ private:
Common::Array<MovieFrame *> _framesNotYetShown;
Common::Array<MovieFrame *> _framesOnScreen;
- // Method implementations. These should be called from callMethod.
+ // Script method implementations.
void timePlay();
void timeStop();
+ void spatialShow();
+ void spatialHide();
void updateFrameState();
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 350e96f4088..e29c3aa2252 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -68,7 +68,14 @@ uint32 SpriteFrame::index() {
Sprite::Sprite(AssetHeader *header) : Asset(header) {
if (header->_startup == kAssetStartupActive) {
- _isActive = true;
+ setActive();
+ _isShowing = true;
+ }
+
+ if (_header->_frameRate == 0) {
+ // It seems that the frame rate is 10 if it's not set in the asset
+ // header, or even if it's set to zero.
+ _header->_frameRate = 10;
}
}
@@ -82,35 +89,47 @@ Sprite::~Sprite() {
Operand Sprite::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) {
switch (methodId) {
case kSpatialShowMethod: {
- assert(args.size() == 0);
+ assert(args.empty());
spatialShow();
return Operand();
}
case kSpatialHideMethod: {
assert(args.empty());
- _isActive = false;
+ spatialHide();
return Operand();
}
- case kTimeStopMethod: {
+ case kTimePlayMethod: {
assert(args.empty());
- _isActive = false;
+ timePlay();
return Operand();
}
- case kTimePlayMethod: {
- assert(args.size() == 0);
- timePlay();
+ case kTimeStopMethod: {
+ assert(args.empty());
+ timeStop();
return Operand();
}
case kMovieResetMethod: {
- assert(args.size() == 0);
+ assert(args.empty());
movieReset();
return Operand();
}
+ case kSetCurrentClipMethod: {
+ assert(args.empty());
+ setCurrentClip();
+ return Operand();
+ }
+
+ case kIsPlayingMethod: {
+ Operand returnValue(kOperandTypeLiteral1);
+ returnValue.putInteger(static_cast<int>(_isPlaying));
+ return returnValue;
+ }
+
default: {
error("Sprite::callMethod(): Got unimplemented method ID %d", methodId);
}
@@ -118,45 +137,83 @@ Operand Sprite::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
}
void Sprite::spatialShow() {
- debugC(5, kDebugScript, "Called Sprite::spatialShow");
- _isActive = true;
- g_engine->addPlayingAsset(this);
+ if (_isShowing) {
+ warning("Sprite::spatialShow(): (%d) Attempted to spatialShow when already showing", _header->_id);
+ return;
+ }
+ showFrame(_frames[0]);
- _isPaused = true;
- if (_activeFrame == nullptr) {
- showFrame(0);
+ setActive();
+ _isShowing = true;
+ _isPlaying = false;
+}
+
+void Sprite::spatialHide() {
+ if (!_isShowing) {
+ warning("Sprite::spatialHide(): (%d) Attempted to spatialHide when not showing", _header->_id);
+ return;
}
+ showFrame(nullptr);
+
+ setInactive();
+ _isShowing = false;
+ _isPlaying = false;
}
void Sprite::timePlay() {
- _isActive = true;
- _isPaused = false;
- _startTime = g_system->getMillis();
- _lastProcessedTime = 0;
+ if (!_isShowing) {
+ warning("Sprite::timePlay(): (%d) Attempted to timePlay when not showing", _header->_id);
+ return;
+ } else if (_isPlaying) {
+ warning("Sprite::timePlay(): (%d) Attempted to timePlay when already playing", _header->_id);
+ return;
+ }
+
+ setActive();
+ _isPlaying = true;
_nextFrameTime = 0;
- g_engine->addPlayingAsset(this);
- if (_header->_frameRate == 0) {
- // It seems that the frame rate is 10 if it's not set in the asset
- // header, or even if it's set to zero.
- _header->_frameRate = 10;
+ runEventHandlerIfExists(kMovieBeginEvent);
+}
+
+void Sprite::timeStop() {
+ if (!_isShowing) {
+ warning("Sprite::timeStop(): (%d) Attempted to timeStop when not showing", _header->_id);
+ return;
+ } else if (!_isPlaying) {
+ warning("Sprite::timeStop(): (%d) Attempted to timeStop when not playing", _header->_id);
+ return;
}
- runEventHandlerIfExists(kMovieBeginEvent);
+ _isPlaying = false;
+ // TODO: Find the right event handler to run here.
}
void Sprite::movieReset() {
- _isActive = true;
- // We do NOT reset the persisting frame, because it should keep showing!
- _isPaused = true;
+ setActive();
+ if (_isShowing) {
+ showFrame(_frames[0]);
+ } else {
+ showFrame(nullptr);
+ }
+ _isPlaying = false;
_startTime = 0;
_currentFrameIndex = 0;
_nextFrameTime = 0;
_lastProcessedTime = 0;
}
+void Sprite::setCurrentClip() {
+ if (_currentFrameIndex < _frames.size()) {
+ showFrame(_frames[_currentFrameIndex++]);
+ } else {
+ warning("Sprite::setCurrentClip(): (%d) Attempted to increment past number of frames", _header->_id);
+ }
+}
+
void Sprite::process() {
updateFrameState();
+ // Sprites don't have time event handlers, separate timers do time handling.
}
void Sprite::readChunk(Chunk &chunk) {
@@ -174,17 +231,30 @@ void Sprite::readChunk(Chunk &chunk) {
}
void Sprite::updateFrameState() {
- if (_isPaused) {
+ if (!_isActive) {
return;
}
+ if (!_isPlaying) {
+ if (_activeFrame != nullptr) {
+ debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing. Persistent frame %d (%d x %d) @ (%d, %d)",
+ _header->_id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
+ } else {
+ debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing, no persistent frame", _header->_id);
+ }
+ return;
+ }
+
+ debugC(5, kDebugGraphics, "Sprite::updateFrameState(): (%d) Frame %d (%d x %d) @ (%d, %d)",
+ _header->_id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
+
uint currentTime = g_system->getMillis() - _startTime;
bool drawNextFrame = currentTime >= _nextFrameTime;
if (!drawNextFrame) {
return;
}
- showFrame(_currentFrameIndex);
+ showFrame(_frames[_currentFrameIndex]);
uint frameDuration = 1000 / _header->_frameRate;
_nextFrameTime = ++_currentFrameIndex * frameDuration;
@@ -193,8 +263,8 @@ void Sprite::updateFrameState() {
if (spriteFinishedPlaying) {
// Sprites always keep their last frame showing until they are hidden
// with spatialHide.
- showFrame(_currentFrameIndex - 1);
- _isPaused = true;
+ showFrame(_frames[_currentFrameIndex - 1]);
+ _isPlaying = false;
// But otherwise, the sprite's params should be reset.
_isActive = true;
@@ -208,7 +278,7 @@ void Sprite::updateFrameState() {
}
void Sprite::redraw(Common::Rect &rect) {
- if (_activeFrame == nullptr) {
+ if (_activeFrame == nullptr || !_isShowing) {
return;
}
@@ -221,15 +291,17 @@ void Sprite::redraw(Common::Rect &rect) {
}
}
-void Sprite::showFrame(uint frameIndex) {
+void Sprite::showFrame(SpriteFrame *frame) {
// Erase the previous frame.
if (_activeFrame != nullptr) {
g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
}
// Show the next frame.
- _activeFrame = _frames[frameIndex];
- g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
+ _activeFrame = frame;
+ if (frame != nullptr) {
+ g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
+ }
}
Common::Rect Sprite::getActiveFrameBoundingBox() {
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index 32f9bf9e7fa..8e2119c89f0 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -58,6 +58,8 @@ private:
SpriteFrameHeader *_bitmapHeader = nullptr;
};
+// Sprites are somewhat like movies, but they strictly show one frame at a time
+// and don't have sound. They are intended for background/recurrent animations.
class Sprite : public Asset {
public:
Sprite(AssetHeader *header);
@@ -72,17 +74,21 @@ public:
private:
Common::Array<SpriteFrame *> _frames;
SpriteFrame *_activeFrame = nullptr;
- bool _isPaused = false;
+ bool _isShowing = false;
+ bool _isPlaying = false;
uint _currentFrameIndex = 0;
uint _nextFrameTime = 0;
// Method implementations.
void spatialShow();
+ void spatialHide();
void timePlay();
+ void timeStop();
void movieReset();
+ void setCurrentClip();
void updateFrameState();
- void showFrame(uint frameIndex);
+ void showFrame(SpriteFrame *frame);
Common::Rect getActiveFrameBoundingBox();
};
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 1aaee142b51..b4a11d78a00 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -161,6 +161,8 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "IsVisible";
case kMovieResetMethod:
return "MovieReset";
+ case kSetCurrentClipMethod:
+ return "SetCurrentClip";
case kSetWorldSpaceExtentMethod:
return "SetWorldSpaceExtent";
case kSetBoundsMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 3e9d03547be..0228a2f047f 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -113,6 +113,7 @@ enum BuiltInMethod {
// SPRITE METHODS.
kMovieResetMethod = 219, // PARAMS: 0
+ kSetCurrentClipMethod = 221, // PARAMS: 0-1
// STAGE METHODS.
kSetWorldSpaceExtentMethod = 363, // PARAMS: 2
Commit: a2a06746ca5e8c43ddd477fe9108708e4e44b0e7
https://github.com/scummvm/scummvm/commit/a2a06746ca5e8c43ddd477fe9108708e4e44b0e7
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-02T11:52:05-05:00
Commit Message:
MEDIASTATION: Store variable bools internally as ints
Since the Operand doesn't have a separate type for bools anyway.
Changed paths:
engines/mediastation/mediascript/variable.cpp
engines/mediastation/mediascript/variable.h
diff --git a/engines/mediastation/mediascript/variable.cpp b/engines/mediastation/mediascript/variable.cpp
index bc9be29cc47..ceeca43aeb2 100644
--- a/engines/mediastation/mediascript/variable.cpp
+++ b/engines/mediastation/mediascript/variable.cpp
@@ -96,7 +96,7 @@ Variable::Variable(Chunk &chunk, bool readId) {
case kVariableTypeBoolean: {
uint rawValue = Datum(chunk, kDatumTypeUint8).u.i;
debugC(7, kDebugLoading, " Variable::Variable(): %s: %d", variableTypeToStr(_type), rawValue);
- _value.b = (rawValue == 1);
+ _value.i = static_cast<int>(rawValue == 1);
break;
}
@@ -169,7 +169,7 @@ Operand Variable::getValue() {
// TODO: Is this value type correct?
// Shouldn't matter too much, though, since it's still an integer type.
Operand returnValue(kOperandTypeLiteral1);
- returnValue.putInteger(_value.b);
+ returnValue.putInteger(_value.i);
return returnValue;
}
diff --git a/engines/mediastation/mediascript/variable.h b/engines/mediastation/mediascript/variable.h
index 691f011f70b..5cc3bcbe373 100644
--- a/engines/mediastation/mediascript/variable.h
+++ b/engines/mediastation/mediascript/variable.h
@@ -45,7 +45,6 @@ public:
union {
Common::String *string;
Collection *collection;
- bool b;
int i;
double d;
uint assetId;
Commit: 17bd61d4132332572859255f526a2c2c2fa40c3c
https://github.com/scummvm/scummvm/commit/17bd61d4132332572859255f526a2c2c2fa40c3c
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-02T11:52:05-05:00
Commit Message:
MEDIASTATION: Properly deactivate hotspots when mouse is moved outside any hotspot
Changed paths:
engines/mediastation/mediastation.cpp
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 0c21021fac1..ecc1f5b25ff 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -193,7 +193,11 @@ void MediaStationEngine::processEvents() {
debugC(5, kDebugEvents, "EVENT_MOUSEMOVE (%d, %d): Sent to hotspot %d", e.mouse.x, e.mouse.y, hotspot->getHeader()->_id);
hotspot->runEventHandlerIfExists(kMouseMovedEvent);
} else {
- _currentHotspot = nullptr;
+ if (_currentHotspot != nullptr) {
+ _currentHotspot->runEventHandlerIfExists(kMouseExitedEvent);
+ debugC(5, kDebugEvents, "EVENT_MOUSEMOVE (%d, %d): Exited hotspot %d", e.mouse.x, e.mouse.y, _currentHotspot->getHeader()->_id);
+ _currentHotspot = nullptr;
+ }
}
break;
}
@@ -373,6 +377,7 @@ void MediaStationEngine::branchToScreen(uint32 contextId) {
Context *context = loadContext(contextId);
_currentContext = context;
_dirtyRects.push_back(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT));
+ _currentHotspot = nullptr;
if (context->_screenAsset != nullptr) {
// TODO: Make the screen an asset just like everything else so we can
More information about the Scummvm-git-logs
mailing list