Initial commit
This commit is contained in:
637
engines/mediastation/actors/camera.cpp
Normal file
637
engines/mediastation/actors/camera.cpp
Normal file
@@ -0,0 +1,637 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/camera.h"
|
||||
#include "mediastation/actors/stage.h"
|
||||
#include "mediastation/actors/image.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
CameraActor::~CameraActor() {
|
||||
if (_parentStage != nullptr) {
|
||||
_parentStage->removeCamera(this);
|
||||
_parentStage->removeChildSpatialEntity(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderChannelIdent:
|
||||
_channelIdent = chunk.readTypedChannelIdent();
|
||||
registerWithStreamManager();
|
||||
_image = Common::SharedPtr<ImageAsset>(new ImageAsset);
|
||||
break;
|
||||
|
||||
case kActorHeaderStartup:
|
||||
_isVisible = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderX:
|
||||
_offset.x = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderY:
|
||||
_offset.y = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderCameraViewportOrigin: {
|
||||
Common::Point origin = chunk.readTypedPoint();
|
||||
setViewportOrigin(origin);
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderCameraLensOpen:
|
||||
_lensOpen = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderCameraImageActor: {
|
||||
uint actorReference = chunk.readTypedUint16();
|
||||
Actor *referencedActor = g_engine->getActorById(actorReference);
|
||||
if (referencedActor == nullptr) {
|
||||
error("%s: Referenced actor %d doesn't exist or has not been read yet in this title", __func__, actorReference);
|
||||
}
|
||||
if (referencedActor->type() != kActorTypeCamera) {
|
||||
error("%s: Type mismatch of referenced actor %d", __func__, actorReference);
|
||||
}
|
||||
CameraActor *referencedImage = static_cast<CameraActor *>(referencedActor);
|
||||
_image = referencedImage->_image;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::readChunk(Chunk &chunk) {
|
||||
BitmapHeader *bitmapHeader = new BitmapHeader(chunk);
|
||||
_image->bitmap = new Bitmap(chunk, bitmapHeader);
|
||||
}
|
||||
|
||||
ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
switch (methodId) {
|
||||
case kSpatialMoveToMethod:
|
||||
case kSpatialMoveToByOffsetMethod:
|
||||
case kSpatialCenterMoveToMethod:
|
||||
invalidateLocalBounds();
|
||||
returnValue = SpatialEntity::callMethod(methodId, args);
|
||||
invalidateLocalBounds();
|
||||
break;
|
||||
|
||||
case kAddToStageMethod:
|
||||
assert(args.empty());
|
||||
addToStage();
|
||||
break;
|
||||
|
||||
case kRemoveFromStageMethod: {
|
||||
bool stopPan = false;
|
||||
if (args.size() >= 1) {
|
||||
stopPan = args[0].asBool();
|
||||
}
|
||||
removeFromStage(stopPan);
|
||||
break;
|
||||
}
|
||||
|
||||
case kAddedToStageMethod:
|
||||
assert(args.empty());
|
||||
returnValue.setToBool(_addedToStage);
|
||||
break;
|
||||
|
||||
case kStartPanMethod: {
|
||||
assert(args.size() == 3);
|
||||
int16 deltaX = static_cast<uint16>(args[0].asFloat());
|
||||
int16 deltaY = static_cast<int16>(args[1].asFloat());
|
||||
double duration = args[2].asTime();
|
||||
_nextViewportOrigin = Common::Point(deltaX, deltaY) + _currentViewportOrigin;
|
||||
adjustCameraViewport(_nextViewportOrigin);
|
||||
startPan(deltaX, deltaY, duration);
|
||||
break;
|
||||
}
|
||||
|
||||
case kStopPanMethod:
|
||||
assert(args.empty());
|
||||
stopPan();
|
||||
break;
|
||||
|
||||
case kIsPanningMethod:
|
||||
assert(args.empty());
|
||||
returnValue.setToBool(_panState);
|
||||
break;
|
||||
|
||||
case kViewportMoveToMethod: {
|
||||
assert(args.size() == 2);
|
||||
int16 x = static_cast<int16>(args[0].asFloat());
|
||||
int16 y = static_cast<int16>(args[1].asFloat());
|
||||
_nextViewportOrigin = Common::Point(x, y);
|
||||
if (!_addedToStage) {
|
||||
_currentViewportOrigin = _nextViewportOrigin;
|
||||
} else {
|
||||
bool viewportMovedSuccessfully = processViewportMove();
|
||||
if (!viewportMovedSuccessfully) {
|
||||
startPan(0, 0, 0.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kAdjustCameraViewportMethod: {
|
||||
assert(args.size() == 2);
|
||||
int16 xDiff = static_cast<int16>(args[0].asFloat());
|
||||
int16 yDiff = static_cast<int16>(args[1].asFloat());
|
||||
Common::Point viewportDelta(xDiff, yDiff);
|
||||
_nextViewportOrigin = getViewportOrigin() + viewportDelta;
|
||||
adjustCameraViewport(_nextViewportOrigin);
|
||||
if (!_addedToStage) {
|
||||
_currentViewportOrigin = _nextViewportOrigin;
|
||||
} else {
|
||||
bool viewportMovedSuccessfully = processViewportMove();
|
||||
if (!viewportMovedSuccessfully) {
|
||||
startPan(0, 0, 0.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kAdjustCameraViewportSpatialCenterMethod: {
|
||||
assert(args.size() == 2);
|
||||
int16 xDiff = static_cast<int16>(args[0].asFloat());
|
||||
int16 yDiff = static_cast<int16>(args[1].asFloat());
|
||||
|
||||
// Apply centering adjustment, which is indeed based on the entire camera actor's
|
||||
// bounds, not just the current viewport bounds.
|
||||
int16 centeredXDiff = xDiff - (getBbox().width() / 2);
|
||||
int16 centeredYDiff = yDiff - (getBbox().height() / 2);
|
||||
Common::Point viewportDelta(centeredXDiff, centeredYDiff);
|
||||
_nextViewportOrigin = getViewportOrigin() + viewportDelta;
|
||||
adjustCameraViewport(_nextViewportOrigin);
|
||||
if (!_addedToStage) {
|
||||
_currentViewportOrigin = _nextViewportOrigin;
|
||||
} else {
|
||||
bool viewportMovedSuccessfully = processViewportMove();
|
||||
if (!viewportMovedSuccessfully) {
|
||||
startPan(0, 0, 0.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kSetCameraBoundsMethod: {
|
||||
assert(args.size() == 2);
|
||||
int16 width = static_cast<int16>(args[0].asFloat());
|
||||
int16 height = static_cast<int16>(args[1].asFloat());
|
||||
Common::Rect newBounds(_originalBoundingBox.origin(), width, height);
|
||||
|
||||
// invalidateLocalBounds is already called in the setBounds call, but these extra calls are
|
||||
// in the original, so they are kept.
|
||||
invalidateLocalBounds();
|
||||
setBounds(newBounds);
|
||||
invalidateLocalBounds();
|
||||
break;
|
||||
}
|
||||
|
||||
case kXViewportPositionMethod:
|
||||
assert(args.size() == 0);
|
||||
returnValue.setToFloat(getViewportOrigin().x);
|
||||
break;
|
||||
|
||||
case kYViewportPositionMethod:
|
||||
assert(args.size() == 0);
|
||||
returnValue.setToFloat(getViewportOrigin().y);
|
||||
break;
|
||||
|
||||
case kPanToMethod: {
|
||||
assert(args.size() >= 3);
|
||||
int16 x = static_cast<int16>(args[0].asFloat());
|
||||
int16 y = static_cast<int16>(args[1].asFloat());
|
||||
|
||||
if (args.size() == 4) {
|
||||
uint panSteps = static_cast<uint>(args[2].asFloat());
|
||||
double duration = args[3].asFloat();
|
||||
panToByStepCount(x, y, panSteps, duration);
|
||||
} else if (args.size() == 3) {
|
||||
double duration = args[2].asFloat();
|
||||
panToByTime(x, y, duration);
|
||||
} else {
|
||||
error("%s: Incorrect number of args for method %s", __func__, builtInMethodToStr(methodId));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
returnValue = SpatialEntity::callMethod(methodId, args);
|
||||
break;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void CameraActor::invalidateLocalBounds() {
|
||||
if (_parentStage != nullptr) {
|
||||
_parentStage->invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::loadIsComplete() {
|
||||
SpatialEntity::loadIsComplete();
|
||||
if (_lensOpen) {
|
||||
addToStage();
|
||||
}
|
||||
|
||||
if (_image != nullptr) {
|
||||
warning("%s: STUB: Camera image asset not handled yet", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::addToStage() {
|
||||
if (_parentStage != nullptr) {
|
||||
_parentStage->addCamera(this);
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::removeFromStage(bool shouldStopPan) {
|
||||
if (_parentStage != nullptr) {
|
||||
_parentStage->removeCamera(this);
|
||||
invalidateLocalBounds();
|
||||
_addedToStage = false;
|
||||
if (shouldStopPan && _panState != kCameraNotPanning) {
|
||||
stopPan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::setViewportOrigin(const Common::Point &newViewpointOrigin) {
|
||||
_currentViewportOrigin = newViewpointOrigin;
|
||||
}
|
||||
|
||||
Common::Point CameraActor::getViewportOrigin() {
|
||||
return _currentViewportOrigin;
|
||||
}
|
||||
|
||||
Common::Rect CameraActor::getViewportBounds() {
|
||||
Common::Rect viewportBounds(getBbox());
|
||||
viewportBounds.moveTo(_currentViewportOrigin);
|
||||
return viewportBounds;
|
||||
}
|
||||
|
||||
void CameraActor::drawUsingCamera(DisplayContext &displayContext, const Common::Array<SpatialEntity *> &entitiesToDraw) {
|
||||
Clip *currentClip = displayContext.currentClip();
|
||||
if (currentClip != nullptr) {
|
||||
Clip *previousClip = displayContext.previousClip();
|
||||
if (previousClip == nullptr) {
|
||||
currentClip->addToRegion(currentClip->_bounds);
|
||||
} else {
|
||||
*currentClip = *previousClip;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect cameraBounds = getBbox();
|
||||
displayContext.intersectClipWith(cameraBounds);
|
||||
displayContext.pushOrigin();
|
||||
|
||||
Common::Point viewportOrigin = getViewportOrigin();
|
||||
Common::Point viewportOffset(
|
||||
-viewportOrigin.x + cameraBounds.left,
|
||||
-viewportOrigin.y + cameraBounds.top
|
||||
);
|
||||
displayContext._origin.x += viewportOffset.x;
|
||||
displayContext._origin.y += viewportOffset.y;
|
||||
|
||||
if (_image != nullptr) {
|
||||
// TODO: Handle image asset stuff.
|
||||
warning("%s: Camera image asset not handled yet", __func__);
|
||||
}
|
||||
|
||||
for (SpatialEntity *entityToDraw : entitiesToDraw) {
|
||||
if (entityToDraw->isVisible()) {
|
||||
drawObject(displayContext, displayContext, entityToDraw);
|
||||
}
|
||||
}
|
||||
|
||||
displayContext.popOrigin();
|
||||
displayContext.emptyCurrentClip();
|
||||
}
|
||||
|
||||
void CameraActor::drawObject(DisplayContext &sourceContext, DisplayContext &destContext, SpatialEntity *objectToDraw) {
|
||||
if (_parentStage == nullptr) {
|
||||
warning("%s: No parent stage", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
objectToDraw->setAdjustedBounds(kWrapNone);
|
||||
Common::Rect visibleBounds = objectToDraw->getBbox();
|
||||
if (sourceContext.rectIsInClip(visibleBounds)) {
|
||||
objectToDraw->draw(destContext);
|
||||
}
|
||||
|
||||
if (_parentStage->cylindricalX()) {
|
||||
warning("%s: CylindricalX not handled yet", __func__);
|
||||
}
|
||||
|
||||
if (_parentStage->cylindricalY()) {
|
||||
warning("%s: CylindricalY not handled yet", __func__);
|
||||
}
|
||||
objectToDraw->setAdjustedBounds(kWrapNone);
|
||||
}
|
||||
|
||||
void CameraActor::setXYDelta(uint xDelta, uint yDelta) {
|
||||
_panDelta.x = xDelta;
|
||||
_panDelta.y = yDelta;
|
||||
debugC(6, kDebugCamera, "%s: (%d, %d)", __func__, _panDelta.x, _panDelta.y);
|
||||
}
|
||||
|
||||
void CameraActor::setXYDelta() {
|
||||
// If we have no parameters for setting the delta,
|
||||
// just set the delta to 1 in whatever direction we are going.
|
||||
if (_panStart.x < _panDest.x) {
|
||||
_panDelta.x = 1;
|
||||
} else if (_panDest.x < _panStart.x) {
|
||||
_panDelta.x = -1;
|
||||
}
|
||||
|
||||
if (_panStart.y < _panDest.y) {
|
||||
_panDelta.y = 1;
|
||||
} else if (_panDest.y < _panStart.y) {
|
||||
_panDelta.y = -1;
|
||||
}
|
||||
debugC(6, kDebugCamera, "%s: (%d, %d)", __func__, _panDelta.x, _panDelta.y);
|
||||
}
|
||||
|
||||
void CameraActor::panToByTime(int16 x, int16 y, double duration) {
|
||||
_panState = kCameraPanToByTime;
|
||||
_panStart = _currentViewportOrigin;
|
||||
_panDest = Common::Point(x, y);
|
||||
_panDuration = duration;
|
||||
_currentPanStep = 1;
|
||||
_startTime = g_system->getMillis();
|
||||
_nextPanStepTime = 0;
|
||||
debugC(6, kDebugCamera, "%s: panStart: (%d, %d); panDest: (%d, %d); panDuration: %f",
|
||||
__func__, _panStart.x, _panStart.y, _panDest.x, _panDest.y, _panDuration);
|
||||
setXYDelta();
|
||||
calcNewViewportOrigin();
|
||||
}
|
||||
|
||||
void CameraActor::panToByStepCount(int16 x, int16 y, uint panSteps, double duration) {
|
||||
_panState = kCameraPanByStepCount;
|
||||
_panStart = _currentViewportOrigin;
|
||||
_panDest = Common::Point(x, y);
|
||||
_panDuration = duration;
|
||||
_currentPanStep = 1;
|
||||
_maxPanStep = panSteps;
|
||||
_startTime = g_system->getMillis();
|
||||
_nextPanStepTime = 0;
|
||||
debugC(6, kDebugCamera, "%s: panStart: (%d, %d); panDest: (%d, %d); panDuration: %f; maxPanStep: %d",
|
||||
__func__, _panStart.x, _panStart.y, _panDest.x, _panDest.y, _panDuration, _maxPanStep);
|
||||
setXYDelta();
|
||||
calcNewViewportOrigin();
|
||||
}
|
||||
|
||||
void CameraActor::startPan(uint xOffset, uint yOffset, double duration) {
|
||||
_panState = kCameraPanningStarted;
|
||||
_panDuration = duration;
|
||||
_startTime = g_system->getMillis();
|
||||
_nextPanStepTime = 0;
|
||||
_currentPanStep = 0;
|
||||
_maxPanStep = 0;
|
||||
setXYDelta(xOffset, yOffset);
|
||||
debugC(6, kDebugCamera, "%s: xOffset: %u, yOffset: %u, duration: %f", __func__, xOffset, yOffset, duration);
|
||||
}
|
||||
|
||||
void CameraActor::stopPan() {
|
||||
_panState = kCameraNotPanning;
|
||||
_panDuration = 0.0;
|
||||
_startTime = 0;
|
||||
_nextPanStepTime = 0;
|
||||
_currentPanStep = 0;
|
||||
_maxPanStep = 0;
|
||||
debugC(6, kDebugCamera, "%s: nextViewportOrigin: (%d, %d); actualViewportOrigin: (%d, %d)",
|
||||
__func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _currentViewportOrigin.x, _currentViewportOrigin.y);
|
||||
}
|
||||
|
||||
bool CameraActor::continuePan() {
|
||||
bool panShouldContinue = true;
|
||||
if (_panState == kCameraPanningStarted) {
|
||||
if (_panDelta == Common::Point(0, 0)) {
|
||||
panShouldContinue = false;
|
||||
}
|
||||
} else {
|
||||
if (percentComplete() >= 1.0) {
|
||||
panShouldContinue = false;
|
||||
}
|
||||
}
|
||||
debugC(6, kDebugCamera, "%s: %s", __func__, panShouldContinue ? "true": "false");
|
||||
return panShouldContinue;
|
||||
}
|
||||
|
||||
void CameraActor::process() {
|
||||
// Only process panning if we're actively panning.
|
||||
if (_panState == kCameraNotPanning) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's time for the next pan step.
|
||||
uint currentTime = g_system->getMillis() - _startTime;
|
||||
if (currentTime < _nextPanStepTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(7, kDebugCamera, "*** START PAN STEP ***");
|
||||
timerEvent();
|
||||
debugC(7, kDebugCamera, "*** END PAN STEP ***");
|
||||
}
|
||||
|
||||
void CameraActor::timerEvent() {
|
||||
if (_parentStage != nullptr) {
|
||||
if (processViewportMove()) {
|
||||
processNextPanStep();
|
||||
if (continuePan()) {
|
||||
if (cameraWithinStage(_nextViewportOrigin)) {
|
||||
adjustCameraViewport(_nextViewportOrigin);
|
||||
|
||||
// The original had logic to pre-load the items that were going to be scrolled
|
||||
// into view next, but since we load actors more all-at-once, we don't actually need this.
|
||||
// The calls that would be made are kept commented out.
|
||||
// Common::Rect advanceRect = getAdvanceRect();
|
||||
// _parentStage->preload(advanceRect);
|
||||
} else {
|
||||
runEventHandlerIfExists(kCameraPanAbortEvent);
|
||||
stopPan();
|
||||
}
|
||||
} else {
|
||||
bool success = true;
|
||||
if (_panState == kCameraPanToByTime) {
|
||||
_nextViewportOrigin = _panDest;
|
||||
adjustCameraViewport(_nextViewportOrigin);
|
||||
success = processViewportMove();
|
||||
}
|
||||
if (success) {
|
||||
runEventHandlerIfExists(kCameraPanEndEvent);
|
||||
stopPan();
|
||||
} else {
|
||||
Common::Rect currentBounds = getBbox();
|
||||
Common::Rect preloadBounds(_nextViewportOrigin, currentBounds.width(), currentBounds.height());
|
||||
_parentStage->preload(preloadBounds);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Common::Rect currentBounds = getBbox();
|
||||
Common::Rect preloadBounds(_nextViewportOrigin, currentBounds.width(), currentBounds.height());
|
||||
_parentStage->preload(preloadBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraActor::processViewportMove() {
|
||||
bool isRectInMemory = true;
|
||||
if (_parentStage != nullptr) {
|
||||
Common::Rect boundsInViewport = getBbox();
|
||||
boundsInViewport.moveTo(_nextViewportOrigin);
|
||||
_parentStage->setCurrentCamera(this);
|
||||
isRectInMemory = _parentStage->isRectInMemory(boundsInViewport);
|
||||
if (isRectInMemory) {
|
||||
invalidateLocalBounds();
|
||||
setViewportOrigin(_nextViewportOrigin);
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
return isRectInMemory;
|
||||
}
|
||||
|
||||
void CameraActor::processNextPanStep() {
|
||||
// If pan type includes per-step updates (4-arg pan in original engine),
|
||||
// advance the pan step counter. Then compute the new viewport origin
|
||||
// and notify any script handlers registered for the pan-step event.
|
||||
if (_panState == kCameraPanByStepCount) {
|
||||
_currentPanStep += 1;
|
||||
}
|
||||
|
||||
calcNewViewportOrigin();
|
||||
runEventHandlerIfExists(kCameraPanStepEvent);
|
||||
|
||||
uint stepDurationInMilliseconds = 20; // Visually smooth.
|
||||
_nextPanStepTime += stepDurationInMilliseconds;
|
||||
}
|
||||
|
||||
void CameraActor::adjustCameraViewport(Common::Point &viewportToAdjust) {
|
||||
if (_parentStage == nullptr) {
|
||||
warning("%s: No parent stage", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_parentStage->cylindricalX()) {
|
||||
warning("%s: CylindricalX not handled yet", __func__);
|
||||
}
|
||||
|
||||
if (_parentStage->cylindricalY()) {
|
||||
warning("%s: CylindricalY not handled yet", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraActor::calcNewViewportOrigin() {
|
||||
if (_panState == kCameraPanningStarted) {
|
||||
_nextViewportOrigin = _currentViewportOrigin + _panDelta;
|
||||
debugC(6, kDebugCamera, "%s: (%d, %d) [panDelta: (%d, %d)]",
|
||||
__func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _panDelta.x, _panDelta.y);
|
||||
} else {
|
||||
// Interpolate from the start to the dest based on percent complete.
|
||||
double progress = percentComplete();
|
||||
double startX = static_cast<double>(_panStart.x);
|
||||
double endX = static_cast<double>(_panDest.x);
|
||||
double interpolatedX = startX + (endX - startX) * progress + 0.5;
|
||||
_nextViewportOrigin.x = static_cast<int16>(interpolatedX);
|
||||
|
||||
double startY = static_cast<double>(_panStart.y);
|
||||
double endY = static_cast<double>(_panDest.y);
|
||||
double interpolatedY = startY + (endY - startY) * progress + 0.5;
|
||||
_nextViewportOrigin.y = static_cast<int16>(interpolatedY);
|
||||
debugC(6, kDebugCamera, "%s: (%d, %d) [panStart: (%d, %d); panDest: (%d, %d); percentComplete: %f]",
|
||||
__func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _panStart.x, _panStart.y, _panDest.x, _panDest.y, progress);
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraActor::cameraWithinStage(const Common::Point &candidate) {
|
||||
if (_parentStage == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
// We can only be out of horizontal bounds if we have a requested delta and
|
||||
// are not doing X axis wrapping.
|
||||
bool canBeOutOfHorizontalBounds = !_parentStage->cylindricalX() && _panDelta.x != 0;
|
||||
if (canBeOutOfHorizontalBounds) {
|
||||
int16 candidateRightBoundary = getBbox().width() + candidate.x;
|
||||
bool cameraPastRightBoundary = _parentStage->extent().x < candidateRightBoundary;
|
||||
if (cameraPastRightBoundary) {
|
||||
result = false;
|
||||
} else if (candidate.x < 0) {
|
||||
result = false;
|
||||
}
|
||||
debugC(6, kDebugCamera, "%s: %s [rightBoundary: %d, extent: %d]", __func__, result ? "true" : "false", candidateRightBoundary, _parentStage->extent().x);
|
||||
}
|
||||
|
||||
// We can only be out of vertical bounds if we have a requested delta and
|
||||
// are not doing Y axis wrapping.
|
||||
bool canBeOutOfVerticalBounds = !_parentStage->cylindricalY() && _panDelta.y != 0;
|
||||
if (canBeOutOfVerticalBounds) {
|
||||
int16 candidateBottomBoundary = getBbox().height() + candidate.y;
|
||||
bool cameraPastBottomBoundary = _parentStage->extent().y < candidateBottomBoundary;
|
||||
if (cameraPastBottomBoundary) {
|
||||
result = false;
|
||||
} else if (candidate.y < 0) {
|
||||
result = false;
|
||||
}
|
||||
debugC(6, kDebugCamera, "%s: %s [bottomBoundary: %d, extent: %d]", __func__, result ? "true" : "false", candidateBottomBoundary, _parentStage->extent().y);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
double CameraActor::percentComplete() {
|
||||
double percentValue = 0.0;
|
||||
switch (_panState) {
|
||||
case kCameraPanByStepCount: {
|
||||
percentValue = static_cast<double>(_maxPanStep - _currentPanStep) / static_cast<double>(_maxPanStep);
|
||||
percentValue = 1.0 - percentValue;
|
||||
break;
|
||||
}
|
||||
|
||||
case kCameraPanToByTime: {
|
||||
const double MILLISECONDS_IN_ONE_SECOND = 1000.0;
|
||||
uint currentRuntime = g_system->getMillis();
|
||||
uint elapsedTime = currentRuntime - _startTime;
|
||||
double elapsedSeconds = elapsedTime / MILLISECONDS_IN_ONE_SECOND;
|
||||
percentValue = elapsedSeconds / _panDuration;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
percentValue = 0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
percentValue = CLIP<double>(percentValue, 0.0, 1.0);
|
||||
return percentValue;
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
103
engines/mediastation/actors/camera.h
Normal file
103
engines/mediastation/actors/camera.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_CAMERA_H
|
||||
#define MEDIASTATION_CAMERA_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/graphics.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
enum CameraPanState {
|
||||
kCameraNotPanning = 0,
|
||||
kCameraPanningStarted = 1,
|
||||
// We pan for a certain total amount of time.
|
||||
kCameraPanToByTime = 2,
|
||||
// We pan for a certain number of steps, waiting a given time between each step.
|
||||
kCameraPanByStepCount = 3
|
||||
};
|
||||
|
||||
struct ImageAsset;
|
||||
|
||||
// A Camera's main purpose is panning around a stage that is too large to fit on screen all at once.
|
||||
class CameraActor : public SpatialEntity, public ChannelClient {
|
||||
public:
|
||||
CameraActor() : SpatialEntity(kActorTypeCamera) {};
|
||||
~CameraActor();
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
virtual void loadIsComplete() override;
|
||||
virtual void process() override;
|
||||
|
||||
Common::Point getViewportOrigin();
|
||||
Common::Rect getViewportBounds();
|
||||
virtual void invalidateLocalBounds() override;
|
||||
|
||||
void drawUsingCamera(DisplayContext &displayContext, const Common::Array<SpatialEntity *> &entitiesToDraw);
|
||||
|
||||
private:
|
||||
bool _lensOpen = false;
|
||||
bool _addedToStage = false;
|
||||
double _panDuration = 0.0;
|
||||
uint _currentPanStep = 0;
|
||||
uint _maxPanStep = 0;
|
||||
uint _startTime = 0;
|
||||
uint _nextPanStepTime = 0;
|
||||
CameraPanState _panState = kCameraNotPanning;
|
||||
Common::Point _offset;
|
||||
Common::Point _currentViewportOrigin;
|
||||
Common::Point _nextViewportOrigin;
|
||||
Common::Point _panStart;
|
||||
Common::Point _panDest;
|
||||
Common::Point _panDelta;
|
||||
Common::SharedPtr<ImageAsset> _image;
|
||||
DisplayContext _displayContext;
|
||||
|
||||
void addToStage();
|
||||
void removeFromStage(bool stopPan);
|
||||
void setViewportOrigin(const Common::Point &newViewportOrigin);
|
||||
void drawObject(DisplayContext &sourceContext, DisplayContext &destContext, SpatialEntity *objectToDraw);
|
||||
void setXYDelta(uint xDelta, uint yDelta);
|
||||
void setXYDelta();
|
||||
bool cameraWithinStage(const Common::Point &candidate);
|
||||
|
||||
void panToByTime(int16 x, int16 y, double duration);
|
||||
void panToByStepCount(int16 x, int16 y, uint maxPanStep, double duration);
|
||||
void startPan(uint xOffset, uint yOffset, double duration);
|
||||
void stopPan();
|
||||
bool continuePan();
|
||||
|
||||
void timerEvent();
|
||||
bool processViewportMove();
|
||||
void processNextPanStep();
|
||||
void adjustCameraViewport(Common::Point &viewportToAdjust);
|
||||
void calcNewViewportOrigin();
|
||||
double percentComplete();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
48
engines/mediastation/actors/canvas.cpp
Normal file
48
engines/mediastation/actors/canvas.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/canvas.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
void CanvasActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderStartup:
|
||||
_isVisible = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue CanvasActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
switch (methodId) {
|
||||
case kClearToPaletteMethod: {
|
||||
error("%s: clearToPalette is not implemented yet", __func__);
|
||||
}
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
41
engines/mediastation/actors/canvas.h
Normal file
41
engines/mediastation/actors/canvas.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_CANVAS_H
|
||||
#define MEDIASTATION_CANVAS_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class CanvasActor : public SpatialEntity {
|
||||
public:
|
||||
CanvasActor() : SpatialEntity(kActorTypeCanvas) {};
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
100
engines/mediastation/actors/document.cpp
Normal file
100
engines/mediastation/actors/document.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/actors/document.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
const uint MediaStation::DocumentActor::DOCUMENT_ACTOR_ID;
|
||||
|
||||
ScriptValue DocumentActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
switch (methodId) {
|
||||
case kDocumentBranchToScreenMethod:
|
||||
processBranch(args);
|
||||
break;
|
||||
|
||||
case kDocumentQuitMethod:
|
||||
g_engine->quitGame();
|
||||
break;
|
||||
|
||||
case kDocumentContextLoadInProgressMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint contextId = args[0].asActorId();
|
||||
bool isLoading = g_engine->getDocument()->isContextLoadInProgress(contextId);
|
||||
returnValue.setToBool(isLoading);
|
||||
break;
|
||||
}
|
||||
|
||||
case kDocumentSetMultipleStreamsMethod:
|
||||
case kDocumentSetMultipleSoundsMethod: {
|
||||
assert(args.size() == 1);
|
||||
bool value = args[0].asBool();
|
||||
warning("%s: STUB: %s: %d", __func__, builtInMethodToStr(methodId), value);
|
||||
break;
|
||||
}
|
||||
|
||||
case kDocumentLoadContextMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint contextId = args[0].asActorId();
|
||||
g_engine->getDocument()->startContextLoad(contextId);
|
||||
break;
|
||||
}
|
||||
|
||||
case kDocumentReleaseContextMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint contextId = args[0].asActorId();
|
||||
g_engine->getDocument()->scheduleContextRelease(contextId);
|
||||
break;
|
||||
}
|
||||
|
||||
case kDocumentContextIsLoadedMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint contextId = args[0].asActorId();
|
||||
|
||||
// We are looking for the screen actor with the same ID as the context.
|
||||
Actor *screenActor = g_engine->getActorById(contextId);
|
||||
bool contextIsLoading = g_engine->getDocument()->isContextLoadInProgress(contextId);
|
||||
bool contextIsLoaded = (screenActor != nullptr) && !contextIsLoading;
|
||||
returnValue.setToBool(contextIsLoaded);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
returnValue = Actor::callMethod(methodId, args);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void DocumentActor::processBranch(Common::Array<ScriptValue> &args) {
|
||||
assert(args.size() >= 1);
|
||||
uint contextId = args[0].asActorId();
|
||||
if (args.size() > 1) {
|
||||
bool disableUpdates = static_cast<bool>(args[1].asParamToken());
|
||||
if (disableUpdates)
|
||||
warning("%s: disableUpdates parameter not handled yet", __func__);
|
||||
}
|
||||
|
||||
g_engine->getDocument()->scheduleScreenBranch(contextId);
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
44
engines/mediastation/actors/document.h
Normal file
44
engines/mediastation/actors/document.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_DOCUMENT_H
|
||||
#define MEDIASTATION_DOCUMENT_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class DocumentActor : public Actor {
|
||||
public:
|
||||
static const uint DOCUMENT_ACTOR_ID = 1;
|
||||
DocumentActor() : Actor(kActorTypeDocument) { _id = DOCUMENT_ACTOR_ID; };
|
||||
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
|
||||
private:
|
||||
void processBranch(Common::Array<ScriptValue> &args);
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
66
engines/mediastation/actors/font.cpp
Normal file
66
engines/mediastation/actors/font.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/actors/font.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
FontGlyph::FontGlyph(Chunk &chunk, uint asciiCode, uint unk1, uint unk2, BitmapHeader *header) : Bitmap(chunk, header) {
|
||||
_asciiCode = asciiCode;
|
||||
_unk1 = unk1;
|
||||
_unk2 = unk2;
|
||||
}
|
||||
|
||||
FontActor::~FontActor() {
|
||||
unregisterWithStreamManager();
|
||||
for (auto it = _glyphs.begin(); it != _glyphs.end(); ++it) {
|
||||
delete it->_value;
|
||||
}
|
||||
_glyphs.clear();
|
||||
}
|
||||
|
||||
void FontActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderChannelIdent:
|
||||
_channelIdent = chunk.readTypedChannelIdent();
|
||||
registerWithStreamManager();
|
||||
break;
|
||||
|
||||
default:
|
||||
Actor::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
void FontActor::readChunk(Chunk &chunk) {
|
||||
debugC(5, kDebugLoading, "FontActor::readChunk(): Reading font glyph (@0x%llx)", static_cast<long long int>(chunk.pos()));
|
||||
uint asciiCode = chunk.readTypedUint16();
|
||||
int unk1 = chunk.readTypedUint16();
|
||||
int unk2 = chunk.readTypedUint16();
|
||||
BitmapHeader *header = new BitmapHeader(chunk);
|
||||
FontGlyph *glyph = new FontGlyph(chunk, asciiCode, unk1, unk2, header);
|
||||
if (_glyphs.getValOrDefault(asciiCode) != nullptr) {
|
||||
error("%s: Glyph for ASCII code 0x%x already exists", __func__, asciiCode);
|
||||
}
|
||||
_glyphs.setVal(asciiCode, glyph);
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
57
engines/mediastation/actors/font.h
Normal file
57
engines/mediastation/actors/font.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_FONT_H
|
||||
#define MEDIASTATION_FONT_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/bitmap.h"
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class FontGlyph : public Bitmap {
|
||||
public:
|
||||
FontGlyph(Chunk &chunk, uint asciiCode, uint unk1, uint unk2, BitmapHeader *header);
|
||||
uint _asciiCode = 0;
|
||||
|
||||
private:
|
||||
int _unk1 = 0;
|
||||
int _unk2 = 0;
|
||||
};
|
||||
|
||||
class FontActor : public Actor, public ChannelClient {
|
||||
public:
|
||||
FontActor() : Actor(kActorTypeFont) {};
|
||||
~FontActor();
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
|
||||
private:
|
||||
Common::HashMap<uint, FontGlyph *> _glyphs;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
252
engines/mediastation/actors/hotspot.cpp
Normal file
252
engines/mediastation/actors/hotspot.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/actors/hotspot.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
void HotspotActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderMouseActiveArea: {
|
||||
uint16 total_points = chunk.readTypedUint16();
|
||||
for (int i = 0; i < total_points; i++) {
|
||||
Common::Point point = chunk.readTypedPoint();
|
||||
_mouseActiveArea.push_back(point);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderStartup:
|
||||
_isActive = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderCursorResourceId:
|
||||
_cursorResourceId = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderGetOffstageEvents:
|
||||
_getOffstageEvents = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
bool HotspotActor::isInside(const Common::Point &pointToCheck) {
|
||||
// No sense checking the polygon if we're not even in the bbox.
|
||||
if (!_boundingBox.contains(pointToCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're in the bbox, but there might not be a polygon to check.
|
||||
if (_mouseActiveArea.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Polygon intersection code adapted from HADESCH engine, might need more
|
||||
// refinement once more testing is possible.
|
||||
Common::Point point = pointToCheck - Common::Point(_boundingBox.left, _boundingBox.top);
|
||||
int rcross = 0; // Number of right-side overlaps
|
||||
|
||||
// Each edge is checked whether it cuts the outgoing stream from the point
|
||||
Common::Array<Common::Point> _polygon = _mouseActiveArea;
|
||||
for (unsigned i = 0; i < _polygon.size(); i++) {
|
||||
const Common::Point &edgeStart = _polygon[i];
|
||||
const Common::Point &edgeEnd = _polygon[(i + 1) % _polygon.size()];
|
||||
|
||||
// A vertex is a point? Then it lies on one edge of the polygon
|
||||
if (point == edgeStart)
|
||||
return true;
|
||||
|
||||
if ((edgeStart.y > point.y) != (edgeEnd.y > point.y)) {
|
||||
int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
|
||||
int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
|
||||
if ((term1 > 0) == (term2 >= 0))
|
||||
rcross++;
|
||||
}
|
||||
}
|
||||
|
||||
// The point is strictly inside the polygon if and only if the number of overlaps is odd
|
||||
return ((rcross % 2) == 1);
|
||||
}
|
||||
|
||||
ScriptValue HotspotActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kMouseActivateMethod: {
|
||||
assert(args.empty());
|
||||
activate();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kMouseDeactivateMethod: {
|
||||
assert(args.empty());
|
||||
deactivate();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kIsActiveMethod: {
|
||||
assert(args.empty());
|
||||
returnValue.setToBool(_isActive);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTriggerAbsXPositionMethod: {
|
||||
double mouseX = static_cast<double>(g_system->getEventManager()->getMousePos().x);
|
||||
returnValue.setToFloat(mouseX);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTriggerAbsYPositionMethod: {
|
||||
double mouseY = static_cast<double>(g_system->getEventManager()->getMousePos().y);
|
||||
returnValue.setToFloat(mouseY);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
uint16 HotspotActor::findActorToAcceptMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool clipMouseEvents) {
|
||||
|
||||
uint16 result = 0;
|
||||
if (isActive()) {
|
||||
if (isInside(point)) {
|
||||
if (eventMask & kMouseDownFlag) {
|
||||
state.mouseDown = this;
|
||||
result |= kMouseDownFlag;
|
||||
}
|
||||
|
||||
if (eventMask & kMouseEnterFlag) {
|
||||
state.mouseEnter = this;
|
||||
result |= kMouseEnterFlag;
|
||||
}
|
||||
|
||||
if (eventMask & kMouseMovedFlag) {
|
||||
state.mouseMoved = this;
|
||||
result |= kMouseMovedFlag;
|
||||
}
|
||||
}
|
||||
|
||||
if (this == g_engine->getMouseInsideHotspot() && (eventMask & kMouseExitFlag)) {
|
||||
state.mouseExit = this;
|
||||
result |= kMouseExitFlag;
|
||||
}
|
||||
|
||||
if (this == g_engine->getMouseDownHotspot() && (eventMask & kMouseUpFlag)) {
|
||||
state.mouseUp = this;
|
||||
result |= kMouseUpFlag;
|
||||
}
|
||||
} else {
|
||||
debugC(5, kDebugEvents, "%s: %d: Inactive", __func__, id());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HotspotActor::activate() {
|
||||
if (!_isActive) {
|
||||
_isActive = true;
|
||||
invalidateMouse();
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotActor::deactivate() {
|
||||
if (_isActive) {
|
||||
_isActive = false;
|
||||
if (g_engine->getMouseDownHotspot() == this) {
|
||||
g_engine->setMouseDownHotspot(nullptr);
|
||||
}
|
||||
if (g_engine->getMouseInsideHotspot() == this) {
|
||||
g_engine->setMouseDownHotspot(nullptr);
|
||||
}
|
||||
|
||||
invalidateMouse();
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotActor::mouseDownEvent(const Common::Event &event) {
|
||||
if (!_isActive) {
|
||||
warning("%s: Called on inactive hotspot", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
g_engine->setMouseDownHotspot(this);
|
||||
runEventHandlerIfExists(kMouseDownEvent);
|
||||
}
|
||||
|
||||
void HotspotActor::mouseUpEvent(const Common::Event &event) {
|
||||
if (!_isActive) {
|
||||
warning("%s: Called on inactive hotspot", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
g_engine->setMouseDownHotspot(nullptr);
|
||||
runEventHandlerIfExists(kMouseUpEvent);
|
||||
}
|
||||
|
||||
void HotspotActor::mouseEnteredEvent(const Common::Event &event) {
|
||||
if (!_isActive) {
|
||||
warning("%s: Called on inactive hotspot", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
g_engine->setMouseInsideHotspot(this);
|
||||
if (_cursorResourceId != 0) {
|
||||
debugC(5, kDebugEvents, "%s: Setting cursor %d for asset %d", __func__, _cursorResourceId, id());
|
||||
g_engine->getCursorManager()->setAsTemporary(_cursorResourceId);
|
||||
} else {
|
||||
debugC(5, kDebugEvents, "%s: Unsetting cursor for asset %d", __func__, id());
|
||||
g_engine->getCursorManager()->unsetTemporary();
|
||||
}
|
||||
|
||||
runEventHandlerIfExists(kMouseEnteredEvent);
|
||||
}
|
||||
|
||||
void HotspotActor::mouseMovedEvent(const Common::Event &event) {
|
||||
if (!_isActive) {
|
||||
warning("%s: Called on inactive hotspot", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
runEventHandlerIfExists(kMouseMovedEvent);
|
||||
}
|
||||
|
||||
void HotspotActor::mouseExitedEvent(const Common::Event &event) {
|
||||
if (!_isActive) {
|
||||
warning("%s: Called on inactive hotspot", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
g_engine->setMouseInsideHotspot(nullptr);
|
||||
runEventHandlerIfExists(kMouseExitedEvent);
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
68
engines/mediastation/actors/hotspot.h
Normal file
68
engines/mediastation/actors/hotspot.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_HOTSPOT_H
|
||||
#define MEDIASTATION_HOTSPOT_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class HotspotActor : public SpatialEntity {
|
||||
public:
|
||||
HotspotActor() : SpatialEntity(kActorTypeHotspot) {};
|
||||
virtual ~HotspotActor() { _mouseActiveArea.clear(); }
|
||||
|
||||
bool isInside(const Common::Point &pointToCheck);
|
||||
virtual bool isVisible() const override { return false; }
|
||||
bool isActive() const { return _isActive; }
|
||||
virtual bool interactsWithMouse() const override { return isActive(); }
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
|
||||
virtual uint16 findActorToAcceptMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) override;
|
||||
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
||||
virtual void mouseDownEvent(const Common::Event &event) override;
|
||||
virtual void mouseUpEvent(const Common::Event &event) override;
|
||||
virtual void mouseEnteredEvent(const Common::Event &event) override;
|
||||
virtual void mouseExitedEvent(const Common::Event &event) override;
|
||||
virtual void mouseMovedEvent(const Common::Event &event) override;
|
||||
|
||||
uint _cursorResourceId = 0;
|
||||
Common::Array<Common::Point> _mouseActiveArea;
|
||||
|
||||
private:
|
||||
bool _isActive = false;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
128
engines/mediastation/actors/image.cpp
Normal file
128
engines/mediastation/actors/image.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/actors/image.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
ImageAsset::~ImageAsset() {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
|
||||
ImageActor::~ImageActor() {
|
||||
unregisterWithStreamManager();
|
||||
}
|
||||
|
||||
void ImageActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderChannelIdent:
|
||||
_channelIdent = chunk.readTypedChannelIdent();
|
||||
registerWithStreamManager();
|
||||
_asset = Common::SharedPtr<ImageAsset>(new ImageAsset);
|
||||
break;
|
||||
|
||||
case kActorHeaderStartup:
|
||||
_isVisible = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderLoadType:
|
||||
_loadType = chunk.readTypedByte();
|
||||
break;
|
||||
|
||||
case kActorHeaderX:
|
||||
_xOffset = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderY:
|
||||
_yOffset = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderActorReference: {
|
||||
_actorReference = chunk.readTypedUint16();
|
||||
Actor *referencedActor = g_engine->getActorById(_actorReference);
|
||||
if (referencedActor == nullptr) {
|
||||
error("%s: Referenced actor %d doesn't exist or has not been read yet in this title", __func__, _actorReference);
|
||||
}
|
||||
if (referencedActor->type() != kActorTypeImage) {
|
||||
error("%s: Type mismatch of referenced actor %d", __func__, _actorReference);
|
||||
}
|
||||
ImageActor *referencedImage = static_cast<ImageActor *>(referencedActor);
|
||||
_asset = referencedImage->_asset;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue ImageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
switch (methodId) {
|
||||
case kSpatialShowMethod: {
|
||||
assert(args.empty());
|
||||
spatialShow();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSpatialHideMethod: {
|
||||
assert(args.empty());
|
||||
spatialHide();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageActor::draw(DisplayContext &displayContext) {
|
||||
if (_isVisible) {
|
||||
Common::Point origin = getBbox().origin();
|
||||
g_engine->getDisplayManager()->imageBlit(origin, _asset->bitmap, _dissolveFactor, &displayContext);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageActor::spatialShow() {
|
||||
_isVisible = true;
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
|
||||
void ImageActor::spatialHide() {
|
||||
_isVisible = false;
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
|
||||
Common::Rect ImageActor::getBbox() const {
|
||||
Common::Point origin(_xOffset + _boundingBox.left, _yOffset + _boundingBox.top);
|
||||
Common::Rect bbox(origin, _asset->bitmap->width(), _asset->bitmap->height());
|
||||
return bbox;
|
||||
}
|
||||
|
||||
void ImageActor::readChunk(Chunk &chunk) {
|
||||
BitmapHeader *bitmapHeader = new BitmapHeader(chunk);
|
||||
_asset->bitmap = new Bitmap(chunk, bitmapHeader);
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
68
engines/mediastation/actors/image.h
Normal file
68
engines/mediastation/actors/image.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_IMAGE_H
|
||||
#define MEDIASTATION_IMAGE_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/bitmap.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
// The original had a separate class that did reference counting,
|
||||
// for sharing an asset across actors, but we can just use a SharedPtr.
|
||||
struct ImageAsset {
|
||||
~ImageAsset();
|
||||
|
||||
Bitmap *bitmap = nullptr;
|
||||
};
|
||||
|
||||
class ImageActor : public SpatialEntity, public ChannelClient {
|
||||
public:
|
||||
ImageActor() : SpatialEntity(kActorTypeImage) {};
|
||||
virtual ~ImageActor() override;
|
||||
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
virtual void draw(DisplayContext &displayContext) override;
|
||||
virtual Common::Rect getBbox() const override;
|
||||
|
||||
private:
|
||||
Common::SharedPtr<ImageAsset> _asset;
|
||||
uint _loadType = 0;
|
||||
int _xOffset = 0;
|
||||
int _yOffset = 0;
|
||||
uint _actorReference = 0;
|
||||
|
||||
// Script method implementations.
|
||||
void spatialShow();
|
||||
void spatialHide();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
503
engines/mediastation/actors/movie.cpp
Normal file
503
engines/mediastation/actors/movie.cpp
Normal file
@@ -0,0 +1,503 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/movie.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
MovieFrameHeader::MovieFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
|
||||
_index = chunk.readTypedUint32();
|
||||
debugC(5, kDebugLoading, "MovieFrameHeader::MovieFrameHeader(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
|
||||
_keyframeEndInMilliseconds = chunk.readTypedUint32();
|
||||
}
|
||||
|
||||
MovieFrame::MovieFrame(Chunk &chunk) {
|
||||
if (g_engine->isFirstGenerationEngine()) {
|
||||
blitType = static_cast<MovieBlitType>(chunk.readTypedUint16());
|
||||
startInMilliseconds = chunk.readTypedUint32();
|
||||
endInMilliseconds = chunk.readTypedUint32();
|
||||
// These are unsigned in the data files but ScummVM expects signed.
|
||||
leftTop.x = static_cast<int16>(chunk.readTypedUint16());
|
||||
leftTop.y = static_cast<int16>(chunk.readTypedUint16());
|
||||
index = chunk.readTypedUint32();
|
||||
keyframeIndex = chunk.readTypedUint32();
|
||||
keepAfterEnd = chunk.readTypedByte();
|
||||
} else {
|
||||
layerId = chunk.readTypedUint32();
|
||||
blitType = static_cast<MovieBlitType>(chunk.readTypedUint16());
|
||||
startInMilliseconds = chunk.readTypedUint32();
|
||||
endInMilliseconds = chunk.readTypedUint32();
|
||||
// These are unsigned in the data files but ScummVM expects signed.
|
||||
leftTop.x = static_cast<int16>(chunk.readTypedUint16());
|
||||
leftTop.y = static_cast<int16>(chunk.readTypedUint16());
|
||||
zIndex = chunk.readTypedSint16();
|
||||
// This represents the difference between the left-top coordinate of the
|
||||
// keyframe (if applicable) and the left coordinate of this frame. Zero
|
||||
// if there is no keyframe.
|
||||
diffBetweenKeyframeAndFrame.x = chunk.readTypedSint16();
|
||||
diffBetweenKeyframeAndFrame.y = chunk.readTypedSint16();
|
||||
index = chunk.readTypedUint32();
|
||||
keyframeIndex = chunk.readTypedUint32();
|
||||
keepAfterEnd = chunk.readTypedByte();
|
||||
debugC(5, kDebugLoading, "MovieFrame::MovieFrame(): _blitType = %d, _startInMilliseconds = %d, \
|
||||
_endInMilliseconds = %d, _left = %d, _top = %d, _zIndex = %d, _diffBetweenKeyframeAndFrameX = %d, \
|
||||
_diffBetweenKeyframeAndFrameY = %d, _index = %d, _keyframeIndex = %d, _keepAfterEnd = %d (@0x%llx)",
|
||||
blitType, startInMilliseconds, endInMilliseconds, leftTop.x, leftTop.y, zIndex, diffBetweenKeyframeAndFrame.x, \
|
||||
diffBetweenKeyframeAndFrame.y, index, keyframeIndex, keepAfterEnd, static_cast<long long int>(chunk.pos()));
|
||||
}
|
||||
}
|
||||
|
||||
MovieFrameImage::MovieFrameImage(Chunk &chunk, MovieFrameHeader *header) : Bitmap(chunk, header) {
|
||||
_bitmapHeader = header;
|
||||
}
|
||||
|
||||
MovieFrameImage::~MovieFrameImage() {
|
||||
// The base class destructor takes care of deleting the bitmap header, so
|
||||
// we don't need to delete that here.
|
||||
}
|
||||
|
||||
StreamMovieActor::~StreamMovieActor() {
|
||||
unregisterWithStreamManager();
|
||||
if (_streamFeed != nullptr) {
|
||||
g_engine->getStreamFeedManager()->closeStreamFeed(_streamFeed);
|
||||
_streamFeed = nullptr;
|
||||
}
|
||||
|
||||
delete _streamFrames;
|
||||
_streamFrames = nullptr;
|
||||
|
||||
delete _streamSound;
|
||||
_streamSound = nullptr;
|
||||
}
|
||||
|
||||
void StreamMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderActorId: {
|
||||
// We already have this actor's ID, so we will just verify it is the same
|
||||
// as the ID we have already read.
|
||||
uint32 duplicateActorId = chunk.readTypedUint16();
|
||||
if (duplicateActorId != _id) {
|
||||
warning("%s: Duplicate actor ID %d does not match original ID %d", __func__, duplicateActorId, _id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderMovieLoadType:
|
||||
_loadType = chunk.readTypedByte();
|
||||
break;
|
||||
|
||||
case kActorHeaderChannelIdent:
|
||||
_channelIdent = chunk.readTypedChannelIdent();
|
||||
registerWithStreamManager();
|
||||
break;
|
||||
|
||||
case kActorHeaderHasOwnSubfile: {
|
||||
bool hasOwnSubfile = static_cast<bool>(chunk.readTypedByte());
|
||||
if (!hasOwnSubfile) {
|
||||
error("%s: StreamMovieActor doesn't have a subfile", __func__);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderStartup:
|
||||
_isVisible = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderMovieAudioChannelIdent: {
|
||||
ChannelIdent soundChannelIdent = chunk.readTypedChannelIdent();
|
||||
_streamSound->setChannelIdent(soundChannelIdent);
|
||||
_streamSound->registerWithStreamManager();
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderMovieAnimationChannelIdent: {
|
||||
ChannelIdent framesChannelIdent = chunk.readTypedChannelIdent();
|
||||
_streamFrames->setChannelIdent(framesChannelIdent);
|
||||
_streamFrames->registerWithStreamManager();
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderSoundInfo:
|
||||
_streamSound->_audioSequence.readParameters(chunk);
|
||||
break;
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kTimePlayMethod: {
|
||||
assert(args.empty());
|
||||
timePlay();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSpatialShowMethod: {
|
||||
assert(args.empty());
|
||||
setVisibility(true);
|
||||
updateFrameState();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTimeStopMethod: {
|
||||
assert(args.empty());
|
||||
timeStop();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSpatialHideMethod: {
|
||||
assert(args.empty());
|
||||
setVisibility(false);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kIsPlayingMethod: {
|
||||
assert(args.empty());
|
||||
returnValue.setToBool(_isPlaying);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kGetLeftXMethod: {
|
||||
assert(args.empty());
|
||||
double left = static_cast<double>(_boundingBox.left);
|
||||
returnValue.setToFloat(left);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kGetTopYMethod: {
|
||||
assert(args.empty());
|
||||
double top = static_cast<double>(_boundingBox.top);
|
||||
returnValue.setToFloat(top);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamMovieActor::timePlay() {
|
||||
if (_streamFeed == nullptr) {
|
||||
_streamFeed = g_engine->getStreamFeedManager()->openStreamFeed(_id);
|
||||
_streamFeed->readData();
|
||||
}
|
||||
|
||||
if (_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
_streamSound->_audioSequence.play();
|
||||
_framesNotYetShown = _streamFrames->_frames;
|
||||
_framesOnScreen.clear();
|
||||
_isPlaying = true;
|
||||
_startTime = g_system->getMillis();
|
||||
_lastProcessedTime = 0;
|
||||
runEventHandlerIfExists(kMovieBeginEvent);
|
||||
process();
|
||||
}
|
||||
|
||||
void StreamMovieActor::timeStop() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MovieFrame *frame : _framesOnScreen) {
|
||||
invalidateRect(getFrameBoundingBox(frame));
|
||||
}
|
||||
_streamSound->_audioSequence.stop();
|
||||
_framesNotYetShown.empty();
|
||||
if (_hasStill) {
|
||||
_framesNotYetShown = _streamFrames->_frames;
|
||||
}
|
||||
_framesOnScreen.clear();
|
||||
_startTime = 0;
|
||||
_lastProcessedTime = 0;
|
||||
_isPlaying = false;
|
||||
runEventHandlerIfExists(kMovieStoppedEvent);
|
||||
}
|
||||
|
||||
void StreamMovieActor::process() {
|
||||
if (_isVisible) {
|
||||
if (_isPlaying) {
|
||||
processTimeEventHandlers();
|
||||
}
|
||||
updateFrameState();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamMovieActor::setVisibility(bool visibility) {
|
||||
if (visibility != _isVisible) {
|
||||
_isVisible = visibility;
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamMovieActor::updateFrameState() {
|
||||
uint movieTime = 0;
|
||||
if (_isPlaying) {
|
||||
uint currentTime = g_system->getMillis();
|
||||
movieTime = currentTime - _startTime;
|
||||
}
|
||||
debugC(5, kDebugGraphics, "StreamMovieActor::updateFrameState (%d): Starting update (movie time: %d)", _id, movieTime);
|
||||
|
||||
// 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.insert(frame);
|
||||
invalidateRect(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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
invalidateRect(getFrameBoundingBox(frame));
|
||||
it = _framesOnScreen.erase(it);
|
||||
|
||||
if (_framesOnScreen.empty() && movieTime >= _fullTime) {
|
||||
_isPlaying = false;
|
||||
if (_hasStill) {
|
||||
_framesNotYetShown = _streamFrames->_frames;
|
||||
updateFrameState();
|
||||
}
|
||||
runEventHandlerIfExists(kMovieEndEvent);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// 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, zIndex = %d", \
|
||||
movieTime, frame->index, frame->image->width(), frame->image->height(), frame->leftTop.x, frame->leftTop.y, frame->startInMilliseconds, frame->endInMilliseconds, frame->zIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamMovieActor::draw(DisplayContext &displayContext) {
|
||||
for (MovieFrame *frame : _framesOnScreen) {
|
||||
Common::Rect bbox = getFrameBoundingBox(frame);
|
||||
|
||||
switch (frame->blitType) {
|
||||
case kUncompressedMovieBlit:
|
||||
g_engine->getDisplayManager()->imageBlit(bbox.origin(), frame->image, _dissolveFactor, &displayContext);
|
||||
break;
|
||||
|
||||
case kUncompressedDeltaMovieBlit:
|
||||
g_engine->getDisplayManager()->imageDeltaBlit(
|
||||
bbox.origin(), frame->diffBetweenKeyframeAndFrame,
|
||||
frame->image, frame->keyframeImage, _dissolveFactor, &displayContext);
|
||||
break;
|
||||
|
||||
case kCompressedDeltaMovieBlit:
|
||||
if (frame->keyframeImage->isCompressed()) {
|
||||
decompressIntoAuxImage(frame);
|
||||
}
|
||||
g_engine->getDisplayManager()->imageDeltaBlit(
|
||||
bbox.origin(), frame->diffBetweenKeyframeAndFrame,
|
||||
frame->image, frame->keyframeImage, _dissolveFactor, &displayContext);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Got unknown movie frame blit type: %d", __func__, frame->blitType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect StreamMovieActor::getFrameBoundingBox(MovieFrame *frame) {
|
||||
// Use _boundingBox directly (which may be temporarily offset by camera rendering)
|
||||
// The camera offset is already applied to _boundingBox by pushBoundingBoxOffset()
|
||||
Common::Point origin = _boundingBox.origin() + frame->leftTop;
|
||||
Common::Rect bbox = Common::Rect(origin, frame->image->width(), frame->image->height());
|
||||
return bbox;
|
||||
}
|
||||
|
||||
StreamMovieActorFrames::~StreamMovieActorFrames() {
|
||||
unregisterWithStreamManager();
|
||||
|
||||
for (MovieFrame *frame : _frames) {
|
||||
delete frame;
|
||||
}
|
||||
_frames.clear();
|
||||
|
||||
for (MovieFrameImage *image : _images) {
|
||||
delete image;
|
||||
}
|
||||
_images.clear();
|
||||
}
|
||||
|
||||
void StreamMovieActorFrames::readChunk(Chunk &chunk) {
|
||||
uint sectionType = chunk.readTypedUint16();
|
||||
switch ((MovieSectionType)sectionType) {
|
||||
case kMovieImageDataSection:
|
||||
readImageData(chunk);
|
||||
break;
|
||||
|
||||
case kMovieFrameDataSection:
|
||||
readFrameData(chunk);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Unknown movie still section type", __func__);
|
||||
}
|
||||
|
||||
for (MovieFrame *frame : _frames) {
|
||||
if (frame->endInMilliseconds > _parent->_fullTime) {
|
||||
_parent->_fullTime = frame->endInMilliseconds;
|
||||
}
|
||||
if (frame->keepAfterEnd) {
|
||||
_parent->_hasStill = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_parent->_hasStill) {
|
||||
_parent->_framesNotYetShown = _frames;
|
||||
}
|
||||
}
|
||||
|
||||
StreamMovieActorSound::~StreamMovieActorSound() {
|
||||
unregisterWithStreamManager();
|
||||
}
|
||||
|
||||
void StreamMovieActorSound::readChunk(Chunk &chunk) {
|
||||
_audioSequence.readChunk(chunk);
|
||||
}
|
||||
|
||||
StreamMovieActor::StreamMovieActor() : _framesOnScreen(StreamMovieActor::compareFramesByZIndex), SpatialEntity(kActorTypeMovie) {
|
||||
_streamFrames = new StreamMovieActorFrames(this);
|
||||
_streamSound = new StreamMovieActorSound();
|
||||
}
|
||||
|
||||
void StreamMovieActor::readChunk(Chunk &chunk) {
|
||||
MovieSectionType sectionType = static_cast<MovieSectionType>(chunk.readTypedUint16());
|
||||
if (sectionType == kMovieRootSection) {
|
||||
parseMovieHeader(chunk);
|
||||
} else if (sectionType == kMovieChunkMarkerSection) {
|
||||
parseMovieChunkMarker(chunk);
|
||||
} else {
|
||||
error("%s: Got unused movie chunk header section", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamMovieActor::parseMovieHeader(Chunk &chunk) {
|
||||
_chunkCount = chunk.readTypedUint16();
|
||||
_frameRate = chunk.readTypedDouble();
|
||||
debugC(5, kDebugLoading, "%s: chunkCount = 0x%x, frameRate = %f (@0x%llx)", __func__, _chunkCount, _frameRate, static_cast<long long int>(chunk.pos()));
|
||||
|
||||
Common::Array<uint> chunkLengths;
|
||||
for (uint i = 0; i < _chunkCount; i++) {
|
||||
uint chunkLength = chunk.readTypedUint32();
|
||||
debugC(5, kDebugLoading, "StreamMovieActor::readSubfile(): chunkLength = 0x%x (@0x%llx)", chunkLength, static_cast<long long int>(chunk.pos()));
|
||||
chunkLengths.push_back(chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamMovieActor::parseMovieChunkMarker(Chunk &chunk) {
|
||||
// TODO: There is no warning here because that would spam with thousands of warnings.
|
||||
// This takes care of scheduling stream load and such - it doesn't actually read from the
|
||||
// chunk that is passed in. Since we don't need that scheduling since we are currently reading
|
||||
// the whole movie at once rather than streaming it from the CD-ROM, we don't currently need
|
||||
// to do much here anyway.
|
||||
}
|
||||
|
||||
void StreamMovieActor::invalidateRect(const Common::Rect &rect) {
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
|
||||
void StreamMovieActor::decompressIntoAuxImage(MovieFrame *frame) {
|
||||
const Common::Point origin(0, 0);
|
||||
frame->keyframeImage->_image.create(frame->keyframeImage->width(), frame->keyframeImage->height(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
frame->keyframeImage->_image.setTransparentColor(0);
|
||||
g_engine->getDisplayManager()->imageBlit(origin, frame->keyframeImage, 1.0, nullptr, &frame->keyframeImage->_image);
|
||||
}
|
||||
|
||||
void StreamMovieActorFrames::readImageData(Chunk &chunk) {
|
||||
MovieFrameHeader *header = new MovieFrameHeader(chunk);
|
||||
MovieFrameImage *frame = new MovieFrameImage(chunk, header);
|
||||
_images.push_back(frame);
|
||||
}
|
||||
|
||||
void StreamMovieActorFrames::readFrameData(Chunk &chunk) {
|
||||
uint frameDataToRead = chunk.readTypedUint16();
|
||||
for (uint i = 0; i < frameDataToRead; i++) {
|
||||
MovieFrame *frame = new MovieFrame(chunk);
|
||||
|
||||
// We cannot use a hashmap here because multiple frames can have the
|
||||
// same index, and frames are not necessarily in index order. So we'll
|
||||
// do a linear search, which is how the original does it.
|
||||
for (MovieFrameImage *image : _images) {
|
||||
if (image->index() == frame->index) {
|
||||
frame->image = image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->keyframeIndex != 0) {
|
||||
for (MovieFrameImage *image : _images) {
|
||||
if (image->index() == frame->keyframeIndex) {
|
||||
frame->keyframeImage = image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_frames.push_back(frame);
|
||||
}
|
||||
}
|
||||
|
||||
int StreamMovieActor::compareFramesByZIndex(const MovieFrame *a, const MovieFrame *b) {
|
||||
if (b->zIndex > a->zIndex) {
|
||||
return 1;
|
||||
} else if (a->zIndex > b->zIndex) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
167
engines/mediastation/actors/movie.h
Normal file
167
engines/mediastation/actors/movie.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MOVIE_H
|
||||
#define MEDIASTATION_MOVIE_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/audio.h"
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/bitmap.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
enum MovieBlitType {
|
||||
kInvalidMovieBlit = 0,
|
||||
kUncompressedMovieBlit = 1,
|
||||
kUncompressedDeltaMovieBlit = 2,
|
||||
kCompressedDeltaMovieBlit = 3,
|
||||
};
|
||||
|
||||
class MovieFrameHeader : public BitmapHeader {
|
||||
public:
|
||||
MovieFrameHeader(Chunk &chunk);
|
||||
|
||||
uint _index = 0;
|
||||
uint _keyframeEndInMilliseconds = 0;
|
||||
};
|
||||
|
||||
class MovieFrameImage : public Bitmap {
|
||||
public:
|
||||
MovieFrameImage(Chunk &chunk, MovieFrameHeader *header);
|
||||
virtual ~MovieFrameImage() override;
|
||||
|
||||
uint32 index() { return _bitmapHeader->_index; }
|
||||
|
||||
private:
|
||||
MovieFrameHeader *_bitmapHeader = nullptr;
|
||||
};
|
||||
|
||||
enum MovieSectionType {
|
||||
kMovieRootSection = 0x06a8,
|
||||
kMovieImageDataSection = 0x06a9,
|
||||
kMovieFrameDataSection = 0x06aa,
|
||||
kMovieChunkMarkerSection = 0x06ab
|
||||
};
|
||||
|
||||
struct MovieFrame {
|
||||
MovieFrame(Chunk &chunk);
|
||||
uint unk3 = 0;
|
||||
uint unk4 = 0;
|
||||
uint layerId = 0;
|
||||
uint startInMilliseconds = 0;
|
||||
uint endInMilliseconds = 0;
|
||||
Common::Point leftTop;
|
||||
Common::Point diffBetweenKeyframeAndFrame;
|
||||
MovieBlitType blitType = kInvalidMovieBlit;
|
||||
int16 zIndex = 0;
|
||||
uint keyframeIndex = 0;
|
||||
bool keepAfterEnd = false;
|
||||
uint index = 0;
|
||||
MovieFrameImage *image = nullptr;
|
||||
MovieFrameImage *keyframeImage = nullptr;
|
||||
};
|
||||
|
||||
class StreamMovieActor;
|
||||
|
||||
// This is called `RT_stmvFrames` in the original.
|
||||
class StreamMovieActorFrames : public ChannelClient {
|
||||
public:
|
||||
StreamMovieActorFrames(StreamMovieActor *parent) : ChannelClient(), _parent(parent) {}
|
||||
~StreamMovieActorFrames();
|
||||
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
|
||||
Common::Array<MovieFrame *> _frames;
|
||||
Common::Array<MovieFrameImage *> _images;
|
||||
|
||||
private:
|
||||
StreamMovieActor *_parent = nullptr;
|
||||
|
||||
void readImageData(Chunk &chunk);
|
||||
void readFrameData(Chunk &chunk);
|
||||
};
|
||||
|
||||
// This is called `RT_stmvSound` in the original.
|
||||
class StreamMovieActorSound : public ChannelClient {
|
||||
public:
|
||||
~StreamMovieActorSound();
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
|
||||
AudioSequence _audioSequence;
|
||||
};
|
||||
|
||||
class StreamMovieActor : public SpatialEntity, public ChannelClient {
|
||||
friend class StreamMovieActorFrames;
|
||||
friend class StreamMovieActorSound;
|
||||
|
||||
public:
|
||||
StreamMovieActor();
|
||||
virtual ~StreamMovieActor() override;
|
||||
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
virtual void process() override;
|
||||
|
||||
virtual void draw(DisplayContext &displayContext) override;
|
||||
|
||||
virtual bool isVisible() const override { return _isVisible; }
|
||||
|
||||
private:
|
||||
ImtStreamFeed *_streamFeed = nullptr;
|
||||
uint _fullTime = 0;
|
||||
uint _chunkCount = 0;
|
||||
double _frameRate = 0;
|
||||
|
||||
uint _loadType = 0;
|
||||
bool _isPlaying = false;
|
||||
bool _hasStill = false;
|
||||
|
||||
StreamMovieActorFrames *_streamFrames = nullptr;
|
||||
StreamMovieActorSound *_streamSound = nullptr;
|
||||
|
||||
Common::Array<MovieFrame *> _framesNotYetShown;
|
||||
Common::SortedArray<MovieFrame *, const MovieFrame *> _framesOnScreen;
|
||||
|
||||
// Script method implementations.
|
||||
void timePlay();
|
||||
void timeStop();
|
||||
|
||||
void setVisibility(bool visibility);
|
||||
void updateFrameState();
|
||||
void invalidateRect(const Common::Rect &rect);
|
||||
void decompressIntoAuxImage(MovieFrame *frame);
|
||||
|
||||
void parseMovieHeader(Chunk &chunk);
|
||||
void parseMovieChunkMarker(Chunk &chunk);
|
||||
|
||||
Common::Rect getFrameBoundingBox(MovieFrame *frame);
|
||||
static int compareFramesByZIndex(const MovieFrame *a, const MovieFrame *b);
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
47
engines/mediastation/actors/palette.cpp
Normal file
47
engines/mediastation/actors/palette.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/actors/palette.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
PaletteActor::~PaletteActor() {
|
||||
delete _palette;
|
||||
_palette = nullptr;
|
||||
}
|
||||
|
||||
void PaletteActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderPalette: {
|
||||
byte *buffer = new byte[Graphics::PALETTE_SIZE];
|
||||
chunk.read(buffer, Graphics::PALETTE_SIZE);
|
||||
_palette = new Graphics::Palette(buffer, Graphics::PALETTE_COUNT, DisposeAfterUse::YES);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Actor::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
45
engines/mediastation/actors/palette.h
Normal file
45
engines/mediastation/actors/palette.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_PALETTE_H
|
||||
#define MEDIASTATION_PALETTE_H
|
||||
|
||||
#include "graphics/palette.h"
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class PaletteActor : public Actor {
|
||||
public:
|
||||
PaletteActor() : Actor(kActorTypePalette) {};
|
||||
virtual ~PaletteActor() override;
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
|
||||
Graphics::Palette *_palette = nullptr;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
165
engines/mediastation/actors/path.cpp
Normal file
165
engines/mediastation/actors/path.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/path.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
void PathActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderStartPoint:
|
||||
_startPoint = chunk.readTypedPoint();
|
||||
break;
|
||||
|
||||
case kActorHeaderEndPoint:
|
||||
_endPoint = chunk.readTypedPoint();
|
||||
break;
|
||||
|
||||
case kActorHeaderStepRate: {
|
||||
double _stepRateFloat = chunk.readTypedDouble();
|
||||
// This should always be an integer anyway,
|
||||
// so we'll cast away any fractional part.
|
||||
_stepRate = static_cast<uint32>(_stepRateFloat);
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderDuration:
|
||||
// These are stored in the file as fractional seconds,
|
||||
// but we want milliseconds.
|
||||
_duration = static_cast<uint32>(chunk.readTypedTime() * 1000);
|
||||
break;
|
||||
|
||||
case kActorHeaderPathTotalSteps:
|
||||
_totalSteps = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
default:
|
||||
Actor::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue PathActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kTimePlayMethod: {
|
||||
assert(args.size() == 0);
|
||||
timePlay();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSetDurationMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint durationInMilliseconds = static_cast<uint>(args[0].asTime() * 1000);
|
||||
setDuration(durationInMilliseconds);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kPercentCompleteMethod: {
|
||||
assert(args.size() == 0);
|
||||
returnValue.setToFloat(percentComplete());
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kIsPlayingMethod: {
|
||||
assert(args.empty());
|
||||
returnValue.setToBool(_isPlaying);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return Actor::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
void PathActor::timePlay() {
|
||||
if (_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_duration == 0) {
|
||||
warning("%s: Got zero duration", __func__);
|
||||
} else if (_stepRate == 0) {
|
||||
error("%s: Got zero step rate", __func__);
|
||||
}
|
||||
|
||||
_isPlaying = true;
|
||||
_startTime = g_system->getMillis();
|
||||
_lastProcessedTime = 0;
|
||||
_percentComplete = 0;
|
||||
_nextPathStepTime = 0;
|
||||
_currentStep = 0;
|
||||
_totalSteps = (_duration * _stepRate) / 1000;
|
||||
_stepDurationInMilliseconds = 1000 / _stepRate;
|
||||
|
||||
// TODO: Run the path start event. Haven't seen one the wild yet, don't know its ID.
|
||||
debugC(5, kDebugScript, "Path::timePlay(): No PathStart event handler");
|
||||
}
|
||||
|
||||
void PathActor::process() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint currentTime = g_system->getMillis();
|
||||
uint pathTime = currentTime - _startTime;
|
||||
|
||||
bool doNextStep = pathTime >= _nextPathStepTime;
|
||||
if (!doNextStep) {
|
||||
return;
|
||||
}
|
||||
|
||||
_percentComplete = static_cast<double>(_currentStep + 1) / _totalSteps;
|
||||
debugC(2, kDebugScript, "Path::timePlay(): Step %d of %d", _currentStep, _totalSteps);
|
||||
|
||||
if (_currentStep < _totalSteps) {
|
||||
// TODO: Actually step the path. It seems they mostly just use this for
|
||||
// palette animation in the On Step event handler, so nothing is actually drawn on the screen now.
|
||||
|
||||
// We don't run a step event for the last step.
|
||||
runEventHandlerIfExists(kPathStepEvent);
|
||||
_nextPathStepTime = ++_currentStep * _stepDurationInMilliseconds;
|
||||
} else {
|
||||
_isPlaying = false;
|
||||
_percentComplete = 0;
|
||||
_nextPathStepTime = 0;
|
||||
_currentStep = 0;
|
||||
_totalSteps = 0;
|
||||
_stepDurationInMilliseconds = 0;
|
||||
|
||||
runEventHandlerIfExists(kPathEndEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void PathActor::setDuration(uint durationInMilliseconds) {
|
||||
// TODO: Do we need to save the original duration?
|
||||
debugC(5, kDebugScript, "Path::setDuration(): Setting duration to %d ms", durationInMilliseconds);
|
||||
_duration = durationInMilliseconds;
|
||||
}
|
||||
|
||||
double PathActor::percentComplete() {
|
||||
debugC(5, kDebugScript, "Path::percentComplete(): Returning percent complete %f%%", _percentComplete * 100);
|
||||
return _percentComplete;
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
61
engines/mediastation/actors/path.h
Normal file
61
engines/mediastation/actors/path.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_PATH_H
|
||||
#define MEDIASTATION_PATH_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class PathActor : public Actor {
|
||||
public:
|
||||
PathActor() : Actor(kActorTypePath) {};
|
||||
|
||||
virtual void process() override;
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
|
||||
private:
|
||||
double _percentComplete = 0.0;
|
||||
uint _totalSteps = 0;
|
||||
uint _currentStep = 0;
|
||||
uint _nextPathStepTime = 0;
|
||||
uint _stepDurationInMilliseconds = 0;
|
||||
bool _isPlaying = false;
|
||||
|
||||
Common::Point _startPoint;
|
||||
Common::Point _endPoint;
|
||||
uint32 _stepRate = 0;
|
||||
uint32 _duration = 0;
|
||||
|
||||
// Method implementations.
|
||||
void timePlay();
|
||||
void setDuration(uint durationInMilliseconds);
|
||||
double percentComplete();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
42
engines/mediastation/actors/screen.cpp
Normal file
42
engines/mediastation/actors/screen.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/screen.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
void ScreenActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderCursorResourceId:
|
||||
_cursorResourceId = chunk.readTypedUint16();
|
||||
if (_cursorResourceId != 0) {
|
||||
g_engine->getCursorManager()->registerAsPermanent(_cursorResourceId);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Actor::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
45
engines/mediastation/actors/screen.h
Normal file
45
engines/mediastation/actors/screen.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_SCREEN_H
|
||||
#define MEDIASTATION_SCREEN_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
// A Screen holds actor data and processes event handlers for a Context.
|
||||
// The original separated them this way - there is a ContextParameters section,
|
||||
// then a Screen actor header.
|
||||
class ScreenActor : public Actor {
|
||||
public:
|
||||
ScreenActor() : Actor(kActorTypeScreen) {};
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
|
||||
uint _cursorResourceId = 0;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
149
engines/mediastation/actors/sound.cpp
Normal file
149
engines/mediastation/actors/sound.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/audio.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/actors/sound.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
SoundActor::~SoundActor() {
|
||||
unregisterWithStreamManager();
|
||||
if (_streamFeed != nullptr) {
|
||||
g_engine->getStreamFeedManager()->closeStreamFeed(_streamFeed);
|
||||
_streamFeed = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderActorId: {
|
||||
// We already have this actor's ID, so we will just verify it is the same
|
||||
// as the ID we have already read.
|
||||
uint32 duplicateActorId = chunk.readTypedUint16();
|
||||
if (duplicateActorId != _id) {
|
||||
warning("Duplicate actor ID %d does not match original ID %d", duplicateActorId, _id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderChannelIdent:
|
||||
_channelIdent = chunk.readTypedChannelIdent();
|
||||
registerWithStreamManager();
|
||||
break;
|
||||
|
||||
case kActorHeaderHasOwnSubfile:
|
||||
_hasOwnSubfile = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderSoundInfo:
|
||||
_sequence.readParameters(chunk);
|
||||
break;
|
||||
|
||||
case kActorHeaderMovieLoadType:
|
||||
_loadType = chunk.readTypedByte();
|
||||
break;
|
||||
|
||||
default:
|
||||
Actor::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundActor::process() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
processTimeEventHandlers();
|
||||
if (!_sequence.isActive()) {
|
||||
_isPlaying = false;
|
||||
_sequence.stop();
|
||||
runEventHandlerIfExists(kSoundEndEvent);
|
||||
}
|
||||
}
|
||||
void SoundActor::readChunk(Chunk &chunk) {
|
||||
_isLoadedFromChunk = true;
|
||||
_sequence.readChunk(chunk);
|
||||
}
|
||||
|
||||
ScriptValue SoundActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kSpatialShowMethod:
|
||||
// WORKAROUND: No-op to avoid triggering error on Dalmatians
|
||||
// timer_6c06_AnsweringMachine, which calls SpatialShow on a sound.
|
||||
// Since the engine is currently flagging errors on unimplemented
|
||||
// methods for easier debugging, a no-op is used here to avoid the error.
|
||||
assert(args.empty());
|
||||
return returnValue;
|
||||
|
||||
case kTimePlayMethod: {
|
||||
assert(args.empty());
|
||||
timePlay();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTimeStopMethod: {
|
||||
assert(args.empty());
|
||||
timeStop();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return Actor::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundActor::timePlay() {
|
||||
if (_streamFeed == nullptr && !_isLoadedFromChunk) {
|
||||
_streamFeed = g_engine->getStreamFeedManager()->openStreamFeed(_id);
|
||||
_streamFeed->readData();
|
||||
}
|
||||
|
||||
if (_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sequence.isEmpty()) {
|
||||
_isPlaying = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_isPlaying = true;
|
||||
_startTime = g_system->getMillis();
|
||||
_lastProcessedTime = 0;
|
||||
_sequence.play();
|
||||
runEventHandlerIfExists(kSoundBeginEvent);
|
||||
}
|
||||
|
||||
void SoundActor::timeStop() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isPlaying = false;
|
||||
_sequence.stop();
|
||||
runEventHandlerIfExists(kSoundStoppedEvent);
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
59
engines/mediastation/actors/sound.h
Normal file
59
engines/mediastation/actors/sound.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_ACTORS_SOUND_H
|
||||
#define MEDIASTATION_ACTORS_SOUND_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/audio.h"
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class SoundActor : public Actor, public ChannelClient {
|
||||
public:
|
||||
SoundActor() : Actor(kActorTypeSound) {};
|
||||
~SoundActor();
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
virtual void process() override;
|
||||
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
|
||||
private:
|
||||
ImtStreamFeed *_streamFeed = nullptr;
|
||||
bool _isLoadedFromChunk = false;
|
||||
uint _loadType = 0;
|
||||
bool _hasOwnSubfile = false;
|
||||
bool _isPlaying = false;
|
||||
AudioSequence _sequence;
|
||||
|
||||
// Script method implementations
|
||||
void timePlay();
|
||||
void timeStop();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
387
engines/mediastation/actors/sprite.cpp
Normal file
387
engines/mediastation/actors/sprite.cpp
Normal file
@@ -0,0 +1,387 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/sprite.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
SpriteFrameHeader::SpriteFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
|
||||
_index = chunk.readTypedUint16();
|
||||
_offset = chunk.readTypedPoint();
|
||||
}
|
||||
|
||||
SpriteFrame::SpriteFrame(Chunk &chunk, SpriteFrameHeader *header) : Bitmap(chunk, header) {
|
||||
_bitmapHeader = header;
|
||||
}
|
||||
|
||||
SpriteFrame::~SpriteFrame() {
|
||||
// The base class destructor takes care of deleting the bitmap header.
|
||||
}
|
||||
|
||||
uint32 SpriteFrame::left() {
|
||||
return _bitmapHeader->_offset.x;
|
||||
}
|
||||
|
||||
uint32 SpriteFrame::top() {
|
||||
return _bitmapHeader->_offset.y;
|
||||
}
|
||||
|
||||
Common::Point SpriteFrame::topLeft() {
|
||||
return Common::Point(left(), top());
|
||||
}
|
||||
|
||||
Common::Rect SpriteFrame::boundingBox() {
|
||||
return Common::Rect(topLeft(), width(), height());
|
||||
}
|
||||
|
||||
uint32 SpriteFrame::index() {
|
||||
return _bitmapHeader->_index;
|
||||
}
|
||||
|
||||
SpriteAsset::~SpriteAsset() {
|
||||
for (SpriteFrame *frame : frames) {
|
||||
delete frame;
|
||||
}
|
||||
}
|
||||
|
||||
SpriteMovieActor::~SpriteMovieActor() {
|
||||
unregisterWithStreamManager();
|
||||
}
|
||||
|
||||
void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderChannelIdent:
|
||||
_channelIdent = chunk.readTypedChannelIdent();
|
||||
registerWithStreamManager();
|
||||
_asset = Common::SharedPtr<SpriteAsset>(new SpriteAsset);
|
||||
break;
|
||||
|
||||
case kActorHeaderFrameRate:
|
||||
_frameRate = static_cast<uint32>(chunk.readTypedDouble());
|
||||
break;
|
||||
|
||||
case kActorHeaderLoadType:
|
||||
_loadType = chunk.readTypedByte();
|
||||
break;
|
||||
|
||||
case kActorHeaderStartup:
|
||||
_isVisible = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderSpriteChunkCount: {
|
||||
_frameCount = chunk.readTypedUint16();
|
||||
|
||||
// Set the default clip.
|
||||
SpriteClip clip;
|
||||
clip.id = DEFAULT_CLIP_ID;
|
||||
clip.firstFrameIndex = 0;
|
||||
clip.lastFrameIndex = _frameCount - 1;
|
||||
_clips.setVal(clip.id, clip);
|
||||
setCurrentClip(clip.id);
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderSpriteClip: {
|
||||
SpriteClip spriteClip;
|
||||
spriteClip.id = chunk.readTypedUint16();
|
||||
spriteClip.firstFrameIndex = chunk.readTypedUint16();
|
||||
spriteClip.lastFrameIndex = chunk.readTypedUint16();
|
||||
_clips.setVal(spriteClip.id, spriteClip);
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderCurrentSpriteClip: {
|
||||
uint clipId = chunk.readTypedUint16();
|
||||
setCurrentClip(clipId);
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderActorReference: {
|
||||
_actorReference = chunk.readTypedUint16();
|
||||
Actor *referencedActor = g_engine->getActorById(_actorReference);
|
||||
if (referencedActor == nullptr) {
|
||||
error("%s: Referenced actor %d doesn't exist or has not been read yet in this title", __func__, _actorReference);
|
||||
}
|
||||
if (referencedActor->type() != kActorTypeSprite) {
|
||||
error("%s: Type mismatch of referenced actor %d", __func__, _actorReference);
|
||||
}
|
||||
SpriteMovieActor *referencedSprite = static_cast<SpriteMovieActor *>(referencedActor);
|
||||
_asset = referencedSprite->_asset;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kSpatialShowMethod: {
|
||||
assert(args.empty());
|
||||
setVisibility(true);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSpatialHideMethod: {
|
||||
assert(args.empty());
|
||||
setVisibility(false);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTimePlayMethod: {
|
||||
assert(args.empty());
|
||||
play();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTimeStopMethod: {
|
||||
assert(args.empty());
|
||||
stop();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kMovieResetMethod: {
|
||||
assert(args.empty());
|
||||
setCurrentFrameToInitial();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSetCurrentClipMethod: {
|
||||
assert(args.size() <= 1);
|
||||
uint clipId = DEFAULT_CLIP_ID;
|
||||
if (args.size() == 1) {
|
||||
clipId = args[0].asParamToken();
|
||||
}
|
||||
setCurrentClip(clipId);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kIncrementFrameMethod: {
|
||||
assert(args.size() <= 1);
|
||||
bool loopAround = false;
|
||||
if (args.size() == 1) {
|
||||
loopAround = args[0].asBool();
|
||||
}
|
||||
|
||||
bool moreFrames = activateNextFrame();
|
||||
if (!moreFrames) {
|
||||
if (loopAround) {
|
||||
setCurrentFrameToInitial();
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kDecrementFrameMethod: {
|
||||
bool shouldSetCurrentFrameToFinal = false;
|
||||
if (args.size() == 1) {
|
||||
shouldSetCurrentFrameToFinal = args[0].asBool();
|
||||
}
|
||||
|
||||
bool moreFrames = activatePreviousFrame();
|
||||
if (!moreFrames) {
|
||||
if (shouldSetCurrentFrameToFinal) {
|
||||
setCurrentFrameToFinal();
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kGetCurrentClipIdMethod: {
|
||||
returnValue.setToParamToken(_activeClip.id);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kIsPlayingMethod: {
|
||||
returnValue.setToBool(_isPlaying);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
bool SpriteMovieActor::activateNextFrame() {
|
||||
if (_currentFrameIndex < _activeClip.lastFrameIndex) {
|
||||
_currentFrameIndex++;
|
||||
dirtyIfVisible();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SpriteMovieActor::activatePreviousFrame() {
|
||||
if (_currentFrameIndex > _activeClip.firstFrameIndex) {
|
||||
_currentFrameIndex--;
|
||||
dirtyIfVisible();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SpriteMovieActor::dirtyIfVisible() {
|
||||
if (_isVisible) {
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::setVisibility(bool visibility) {
|
||||
if (_isVisible != visibility) {
|
||||
_isVisible = visibility;
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::play() {
|
||||
_isPlaying = true;
|
||||
_startTime = g_system->getMillis();
|
||||
_lastProcessedTime = 0;
|
||||
_nextFrameTime = 0;
|
||||
|
||||
scheduleNextFrame();
|
||||
}
|
||||
|
||||
void SpriteMovieActor::stop() {
|
||||
_nextFrameTime = 0;
|
||||
_isPlaying = false;
|
||||
}
|
||||
|
||||
void SpriteMovieActor::setCurrentClip(uint clipId) {
|
||||
if (_activeClip.id != clipId) {
|
||||
if (_clips.contains(clipId)) {
|
||||
_activeClip = _clips.getVal(clipId);
|
||||
} else {
|
||||
_activeClip.id = clipId;
|
||||
warning("%s: Sprite clip %d not found in sprite %d", __func__, clipId, _id);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentFrameToInitial();
|
||||
}
|
||||
|
||||
void SpriteMovieActor::setCurrentFrameToInitial() {
|
||||
if (_currentFrameIndex != _activeClip.firstFrameIndex) {
|
||||
_currentFrameIndex = _activeClip.firstFrameIndex;
|
||||
dirtyIfVisible();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::setCurrentFrameToFinal() {
|
||||
if (_currentFrameIndex != _activeClip.lastFrameIndex) {
|
||||
_currentFrameIndex = _activeClip.lastFrameIndex;
|
||||
dirtyIfVisible();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::process() {
|
||||
updateFrameState();
|
||||
// Sprites don't have time event handlers, separate timers do time handling.
|
||||
}
|
||||
|
||||
void SpriteMovieActor::readChunk(Chunk &chunk) {
|
||||
// Reads one frame from the sprite.
|
||||
debugC(5, kDebugLoading, "Sprite::readFrame(): Reading sprite frame (@0x%llx)", static_cast<long long int>(chunk.pos()));
|
||||
SpriteFrameHeader *header = new SpriteFrameHeader(chunk);
|
||||
SpriteFrame *frame = new SpriteFrame(chunk, header);
|
||||
_asset->frames.push_back(frame);
|
||||
|
||||
// TODO: Are these in exactly reverse order? If we can just reverse the
|
||||
// whole thing once.
|
||||
Common::sort(_asset->frames.begin(), _asset->frames.end(), [](SpriteFrame *a, SpriteFrame *b) {
|
||||
return a->index() < b->index();
|
||||
});
|
||||
}
|
||||
|
||||
void SpriteMovieActor::scheduleNextFrame() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentFrameIndex < _activeClip.lastFrameIndex) {
|
||||
scheduleNextTimerEvent();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::scheduleNextTimerEvent() {
|
||||
uint frameDuration = 1000 / _frameRate;
|
||||
_nextFrameTime += frameDuration;
|
||||
}
|
||||
|
||||
void SpriteMovieActor::updateFrameState() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint currentTime = g_system->getMillis() - _startTime;
|
||||
bool drawNextFrame = currentTime >= _nextFrameTime;
|
||||
debugC(kDebugGraphics, "nextFrameTime: %d; startTime: %d, currentTime: %d", _nextFrameTime, _startTime, currentTime);
|
||||
if (drawNextFrame) {
|
||||
timerEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::timerEvent() {
|
||||
if (!_isPlaying) {
|
||||
error("%s: Attempt to activate sprite frame when sprite is not playing", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = activateNextFrame();
|
||||
if (!result) {
|
||||
stop();
|
||||
} else {
|
||||
postMovieEndEventIfNecessary();
|
||||
scheduleNextFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteMovieActor::postMovieEndEventIfNecessary() {
|
||||
if (_currentFrameIndex != _activeClip.lastFrameIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isPlaying = false;
|
||||
_startTime = 0;
|
||||
_nextFrameTime = 0;
|
||||
|
||||
ScriptValue value;
|
||||
value.setToParamToken(_activeClip.id);
|
||||
runEventHandlerIfExists(kSpriteMovieEndEvent, value);
|
||||
}
|
||||
|
||||
void SpriteMovieActor::draw(DisplayContext &displayContext) {
|
||||
SpriteFrame *activeFrame = _asset->frames[_currentFrameIndex];
|
||||
if (_isVisible) {
|
||||
Common::Rect frameBbox = activeFrame->boundingBox();
|
||||
frameBbox.translate(_boundingBox.left, _boundingBox.top);
|
||||
g_engine->getDisplayManager()->imageBlit(frameBbox.origin(), activeFrame, _dissolveFactor, &displayContext);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
126
engines/mediastation/actors/sprite.h
Normal file
126
engines/mediastation/actors/sprite.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_SPRITE_H
|
||||
#define MEDIASTATION_SPRITE_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/bitmap.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
struct SpriteClip {
|
||||
uint id = 0;
|
||||
uint firstFrameIndex = 0;
|
||||
uint lastFrameIndex = 0;
|
||||
};
|
||||
|
||||
class SpriteFrameHeader : public BitmapHeader {
|
||||
public:
|
||||
SpriteFrameHeader(Chunk &chunk);
|
||||
|
||||
uint _index;
|
||||
Common::Point _offset;
|
||||
};
|
||||
|
||||
class SpriteFrame : public Bitmap {
|
||||
public:
|
||||
SpriteFrame(Chunk &chunk, SpriteFrameHeader *header);
|
||||
virtual ~SpriteFrame() override;
|
||||
|
||||
uint32 left();
|
||||
uint32 top();
|
||||
Common::Point topLeft();
|
||||
Common::Rect boundingBox();
|
||||
uint32 index();
|
||||
|
||||
private:
|
||||
SpriteFrameHeader *_bitmapHeader = nullptr;
|
||||
};
|
||||
|
||||
// The original had a separate class that did reference counting,
|
||||
// for sharing an asset across actors, but we can just use a SharedPtr.
|
||||
struct SpriteAsset {
|
||||
~SpriteAsset();
|
||||
|
||||
Common::Array<SpriteFrame *> frames;
|
||||
};
|
||||
|
||||
// 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 SpriteMovieActor : public SpatialEntity, public ChannelClient {
|
||||
public:
|
||||
SpriteMovieActor() : SpatialEntity(kActorTypeSprite) {};
|
||||
~SpriteMovieActor();
|
||||
|
||||
virtual void process() override;
|
||||
virtual void draw(DisplayContext &displayContext) override;
|
||||
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
|
||||
virtual bool isVisible() const override { return _isVisible; }
|
||||
|
||||
virtual void readChunk(Chunk &chunk) override;
|
||||
|
||||
private:
|
||||
static const uint DEFAULT_CLIP_ID = 1200;
|
||||
uint _loadType = 0;
|
||||
uint _frameRate = 0;
|
||||
uint _frameCount = 0;
|
||||
uint _actorReference = 0;
|
||||
Common::HashMap<uint, SpriteClip> _clips;
|
||||
Common::SharedPtr<SpriteAsset> _asset;
|
||||
bool _isPlaying = false;
|
||||
uint _currentFrameIndex = 0;
|
||||
uint _nextFrameTime = 0;
|
||||
SpriteClip _activeClip;
|
||||
|
||||
void play();
|
||||
void stop();
|
||||
void setCurrentClip(uint clipId);
|
||||
|
||||
bool activateNextFrame();
|
||||
bool activatePreviousFrame();
|
||||
|
||||
void dirtyIfVisible();
|
||||
void setCurrentFrameToInitial();
|
||||
void setCurrentFrameToFinal();
|
||||
|
||||
void scheduleNextFrame();
|
||||
void scheduleNextTimerEvent();
|
||||
void postMovieEndEventIfNecessary();
|
||||
void setVisibility(bool visibility);
|
||||
|
||||
void updateFrameState();
|
||||
void timerEvent();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
817
engines/mediastation/actors/stage.cpp
Normal file
817
engines/mediastation/actors/stage.cpp
Normal file
@@ -0,0 +1,817 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/actors/camera.h"
|
||||
#include "mediastation/actors/stage.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
StageActor::StageActor() : SpatialEntity(kActorTypeStage),
|
||||
_children(StageActor::compareSpatialActorByZIndex),
|
||||
_cameras(StageActor::compareSpatialActorByZIndex) {
|
||||
}
|
||||
|
||||
StageActor::~StageActor() {
|
||||
removeAllChildren();
|
||||
if (_parentStage != nullptr) {
|
||||
_parentStage->removeChildSpatialEntity(this);
|
||||
}
|
||||
_currentCamera = nullptr;
|
||||
}
|
||||
|
||||
void StageActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderChildActorId: {
|
||||
// In stages, this basically has the oppose meaning it has outside of stages. Here,
|
||||
// it specifies an actor that is a parent of this stage.
|
||||
uint parentActorId = chunk.readTypedUint16();
|
||||
_pendingParent = g_engine->getSpatialEntityById(parentActorId);
|
||||
break;
|
||||
}
|
||||
|
||||
case kActorHeaderStageExtent:
|
||||
_extent = chunk.readTypedGraphicSize();
|
||||
break;
|
||||
|
||||
case kActorHeaderCylindricalX:
|
||||
_cylindricalX = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderCylindricalY:
|
||||
_cylindricalY = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::preload(const Common::Rect &rect) {
|
||||
if (cylindricalX()) {
|
||||
preloadTest(rect, kWrapLeft);
|
||||
}
|
||||
if (cylindricalY()) {
|
||||
preloadTest(rect, kWrapTop);
|
||||
}
|
||||
if (cylindricalX() && cylindricalY()) {
|
||||
preloadTest(rect, kWrapLeftTop);
|
||||
}
|
||||
preloadTest(rect, kWrapNone);
|
||||
}
|
||||
|
||||
void StageActor::preloadTest(const Common::Rect &rect, CylindricalWrapMode wrapMode) {
|
||||
for (SpatialEntity *entity : _children) {
|
||||
entity->setAdjustedBounds(wrapMode);
|
||||
if (!entity->isRectInMemory(rect) && !entity->isLoading()) {
|
||||
entity->preload(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StageActor::isRectInMemory(const Common::Rect &rect) {
|
||||
bool result = true;
|
||||
if (cylindricalX()) {
|
||||
result = isRectInMemoryTest(rect, kWrapLeft);
|
||||
}
|
||||
if (result && cylindricalY()) {
|
||||
result = isRectInMemoryTest(rect, kWrapTop);
|
||||
}
|
||||
if (result && cylindricalY() && cylindricalX()) {
|
||||
result = isRectInMemoryTest(rect, kWrapLeftTop);
|
||||
}
|
||||
if (result) {
|
||||
result = isRectInMemoryTest(rect, kWrapNone);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StageActor::isRectInMemoryTest(const Common::Rect &rect, CylindricalWrapMode wrapMode) {
|
||||
for (SpatialEntity *entity : _children) {
|
||||
entity->setAdjustedBounds(wrapMode);
|
||||
if (!entity->isRectInMemory(rect)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void StageActor::draw(DisplayContext &displayContext) {
|
||||
Clip *currentClip = displayContext.currentClip();
|
||||
if (currentClip != nullptr) {
|
||||
Clip *previousClip = displayContext.previousClip();
|
||||
if (previousClip == nullptr) {
|
||||
currentClip->addToRegion(currentClip->_bounds);
|
||||
} else {
|
||||
currentClip = previousClip;
|
||||
}
|
||||
}
|
||||
|
||||
displayContext.intersectClipWith(getBbox());
|
||||
if (displayContext.clipIsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool boundsNeedsAdjustment = false;
|
||||
Common::Rect bounds = getBbox();
|
||||
if (bounds.left != 0 || bounds.top != 0) {
|
||||
boundsNeedsAdjustment = true;
|
||||
}
|
||||
if (boundsNeedsAdjustment) {
|
||||
displayContext.pushOrigin();
|
||||
displayContext._origin.x = bounds.left + displayContext._origin.x;
|
||||
displayContext._origin.y = bounds.top + displayContext._origin.y;
|
||||
}
|
||||
|
||||
if (_cameras.empty()) {
|
||||
drawUsingStage(displayContext);
|
||||
} else {
|
||||
for (CameraActor *camera : _cameras) {
|
||||
setCurrentCamera(camera);
|
||||
camera->drawUsingCamera(displayContext, _children);
|
||||
}
|
||||
}
|
||||
|
||||
if (boundsNeedsAdjustment) {
|
||||
displayContext.popOrigin();
|
||||
}
|
||||
displayContext.emptyCurrentClip();
|
||||
}
|
||||
|
||||
void StageActor::drawUsingStage(DisplayContext &displayContext) {
|
||||
for (SpatialEntity *entity : _children) {
|
||||
entity->setAdjustedBounds(kWrapNone);
|
||||
if (entity->isVisible()) {
|
||||
if (displayContext.rectIsInClip(entity->getBbox())) {
|
||||
debugC(5, kDebugGraphics, "%s: Redrawing actor %d", __func__, entity->id());
|
||||
entity->draw(displayContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateRect(const Common::Rect &rect) {
|
||||
if (_parentStage != nullptr) {
|
||||
Common::Point origin = _boundingBox.origin();
|
||||
Common::Rect rectRelativeToParent = rect;
|
||||
rectRelativeToParent.translate(origin.x, origin.y);
|
||||
|
||||
if (_cameras.size() == 0) {
|
||||
_parentStage->invalidateRect(rectRelativeToParent);
|
||||
} else {
|
||||
invalidateUsingCameras(rectRelativeToParent);
|
||||
}
|
||||
} else {
|
||||
error("%s: Attempt to invalidate rect without a parent stage", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateUsingCameras(const Common::Rect &rect) {
|
||||
for (CameraActor *camera : _cameras) {
|
||||
Common::Rect adjustedRectToInvalidate = rect;
|
||||
Common::Rect cameraBounds = camera->getBbox();
|
||||
|
||||
Common::Rect cameraBoundsInStageCoordinates = cameraBounds;
|
||||
Common::Rect stageOrigin = getBbox();
|
||||
cameraBoundsInStageCoordinates.translate(stageOrigin.left, stageOrigin.top);
|
||||
|
||||
Common::Point cameraViewportOrigin = camera->getViewportOrigin();
|
||||
Common::Point viewportOffsetFromCameraBounds(
|
||||
cameraViewportOrigin.x - cameraBounds.left,
|
||||
cameraViewportOrigin.y - cameraBounds.top
|
||||
);
|
||||
|
||||
adjustedRectToInvalidate.translate(
|
||||
-viewportOffsetFromCameraBounds.x,
|
||||
-viewportOffsetFromCameraBounds.y
|
||||
);
|
||||
invalidateObject(adjustedRectToInvalidate, cameraBoundsInStageCoordinates);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateObject(const Common::Rect &rect, const Common::Rect &visibleRegion) {
|
||||
Common::Point xyAdjustment(0, 0);
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
|
||||
if (_cylindricalX) {
|
||||
xyAdjustment.x = _extent.x;
|
||||
xyAdjustment.y = 0;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
|
||||
xyAdjustment.x = -_extent.x;
|
||||
xyAdjustment.y = 0;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
}
|
||||
|
||||
if (_cylindricalY) {
|
||||
xyAdjustment.x = 0;
|
||||
xyAdjustment.y = _extent.y;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
|
||||
xyAdjustment.x = 0;
|
||||
xyAdjustment.y = -_extent.y;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
}
|
||||
|
||||
if (_cylindricalX && _cylindricalY) {
|
||||
xyAdjustment.x = _extent.x;
|
||||
xyAdjustment.y = _extent.y;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
|
||||
xyAdjustment.x = -_extent.x;
|
||||
xyAdjustment.y = -_extent.y;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
|
||||
xyAdjustment.x = -_extent.x;
|
||||
xyAdjustment.y = _extent.y;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
|
||||
xyAdjustment.x = _extent.x;
|
||||
xyAdjustment.y = -_extent.y;
|
||||
invalidateTest(rect, visibleRegion, xyAdjustment);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateTest(const Common::Rect &rect, const Common::Rect &visibleRegion, const Common::Point &originAdjustment) {
|
||||
Common::Rect rectToInvalidateRelative = rect;
|
||||
rectToInvalidateRelative.translate(-originAdjustment.x, -originAdjustment.y);
|
||||
rectToInvalidateRelative.clip(visibleRegion);
|
||||
_parentStage->invalidateRect(rectToInvalidateRelative);
|
||||
}
|
||||
|
||||
void StageActor::loadIsComplete() {
|
||||
// This is deliberately calling down to Actor, rather than calling
|
||||
// to SpatialEntity first.
|
||||
Actor::loadIsComplete();
|
||||
|
||||
if (_pendingParent != nullptr) {
|
||||
if (_pendingParent->type() != kActorTypeStage) {
|
||||
error("%s: Parent must be a stage", __func__);
|
||||
}
|
||||
StageActor *parentStage = static_cast<StageActor *>(_pendingParent);
|
||||
parentStage->addChildSpatialEntity(this);
|
||||
_pendingParent = nullptr;
|
||||
}
|
||||
|
||||
if (_extent.x == 0 || _extent.y == 0) {
|
||||
_extent.x = _boundingBox.width();
|
||||
_extent.y = _boundingBox.height();
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::addActorToStage(uint actorId) {
|
||||
// If actor has a current parent, remove it from that parent first.
|
||||
SpatialEntity *spatialEntity = g_engine->getSpatialEntityById(actorId);
|
||||
StageActor *currentParent = spatialEntity->getParentStage();
|
||||
if (currentParent != nullptr) {
|
||||
currentParent->removeChildSpatialEntity(spatialEntity);
|
||||
}
|
||||
addChildSpatialEntity(spatialEntity);
|
||||
}
|
||||
|
||||
void StageActor::removeActorFromStage(uint actorId) {
|
||||
SpatialEntity *spatialEntity = g_engine->getSpatialEntityById(actorId);
|
||||
StageActor *currentParent = spatialEntity->getParentStage();
|
||||
if (currentParent == this) {
|
||||
// Remove the actor from this stage, and add it back to the root stage.
|
||||
removeChildSpatialEntity(spatialEntity);
|
||||
RootStage *rootStage = g_engine->getRootStage();
|
||||
rootStage->addChildSpatialEntity(spatialEntity);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::addCamera(CameraActor *camera) {
|
||||
_cameras.insert(camera);
|
||||
}
|
||||
|
||||
void StageActor::removeCamera(CameraActor *camera) {
|
||||
for (auto it = _cameras.begin(); it != _cameras.end(); ++it) {
|
||||
if (*it == camera) {
|
||||
_cameras.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::setCurrentCamera(CameraActor *camera) {
|
||||
_currentCamera = camera;
|
||||
}
|
||||
|
||||
ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
switch (methodId) {
|
||||
case kAddActorToStageMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint actorId = args[0].asActorId();
|
||||
addActorToStage(actorId);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kRemoveActorFromStageMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint actorId = args[0].asActorId();
|
||||
removeActorFromStage(actorId);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSetBoundsMethod: {
|
||||
assert(args.size() == 4);
|
||||
int16 x = static_cast<int16>(args[0].asFloat());
|
||||
int16 y = static_cast<int16>(args[1].asFloat());
|
||||
int16 width = static_cast<int16>(args[2].asFloat());
|
||||
int16 height = static_cast<int16>(args[3].asFloat());
|
||||
Common::Rect newBounds(Common::Point(x, y), width, height);
|
||||
setBounds(newBounds);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kStageSetSizeMethod:
|
||||
assert(args.size() == 2);
|
||||
_boundingBox.setWidth(static_cast<int16>(args[0].asFloat()));
|
||||
_boundingBox.setHeight(static_cast<int16>(args[1].asFloat()));
|
||||
return returnValue;
|
||||
|
||||
case kStageGetWidthMethod:
|
||||
returnValue.setToFloat(_boundingBox.width());
|
||||
return returnValue;
|
||||
|
||||
case kStageGetHeightMethod:
|
||||
returnValue.setToFloat(_boundingBox.height());
|
||||
return returnValue;
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::addChildSpatialEntity(SpatialEntity *entity) {
|
||||
if (!assertHasNoParent(entity)) {
|
||||
error("%s: Attempt to add entity that already has a parent", __func__);
|
||||
}
|
||||
|
||||
entity->setParentStage(this);
|
||||
_children.insert(entity);
|
||||
|
||||
if (isVisible()) {
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::removeChildSpatialEntity(SpatialEntity *entity) {
|
||||
if (!assertIsMyChild(entity)) {
|
||||
error("%s: Attempt to remove entity that is not a child", __func__);
|
||||
}
|
||||
|
||||
if (isVisible()) {
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
|
||||
entity->setToNoParentStage();
|
||||
for (auto it = _children.begin(); it != _children.end(); ++it) {
|
||||
if (*it == entity) {
|
||||
_children.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16 StageActor::queryChildrenAboutMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
CylindricalWrapMode wrapMode) {
|
||||
|
||||
uint16 result = 0;
|
||||
Common::Point adjustedPoint = point - _boundingBox.origin();
|
||||
for (auto childIterator = _children.end(); childIterator != _children.begin();) {
|
||||
--childIterator; // Decrement first, then dereference
|
||||
SpatialEntity *child = *childIterator;
|
||||
debugC(7, kDebugEvents, " %s: Checking actor %d (z-index: %d) (eventMask: 0x%02x) (result: 0x%02x) (wrapMode: %d)", __func__, child->id(), child->zIndex(), eventMask, result, wrapMode);
|
||||
|
||||
child->setAdjustedBounds(wrapMode);
|
||||
uint16 handledEvents = child->findActorToAcceptMouseEvents(adjustedPoint, eventMask, state, true);
|
||||
child->setAdjustedBounds(kWrapNone);
|
||||
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16 StageActor::findActorToAcceptMouseEventsObject(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) {
|
||||
|
||||
uint16 result = 0;
|
||||
|
||||
uint16 handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapNone);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
|
||||
if ((eventMask != 0) && cylindricalX()) {
|
||||
handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapLeft);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
if ((eventMask != 0) && cylindricalX()) {
|
||||
handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapRight);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
if ((eventMask != 0) && cylindricalY()) {
|
||||
handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapTop);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
if ((eventMask != 0) && cylindricalY()) {
|
||||
handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapBottom);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
if ((eventMask != 0) && cylindricalY() && cylindricalX()) {
|
||||
handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapLeftTop);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
if ((eventMask != 0) && cylindricalY() && cylindricalX()) {
|
||||
handledEvents = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapRightBottom);
|
||||
if (handledEvents != 0) {
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16 StageActor::findActorToAcceptMouseEventsCamera(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) {
|
||||
|
||||
uint16 result = 0;
|
||||
for (CameraActor *camera : _cameras) {
|
||||
Common::Point mousePosRelativeToCamera = point;
|
||||
setCurrentCamera(camera);
|
||||
|
||||
Common::Point cameraViewportOrigin = camera->getViewportOrigin();
|
||||
mousePosRelativeToCamera.x += cameraViewportOrigin.x;
|
||||
mousePosRelativeToCamera.y += cameraViewportOrigin.y;
|
||||
|
||||
if (!inBounds) {
|
||||
Common::Rect viewportBounds = camera->getViewportBounds();
|
||||
if (!viewportBounds.contains(mousePosRelativeToCamera)) {
|
||||
inBounds = true;
|
||||
}
|
||||
}
|
||||
|
||||
result = findActorToAcceptMouseEventsObject(mousePosRelativeToCamera, eventMask, state, inBounds);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16 StageActor::findActorToAcceptMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) {
|
||||
|
||||
debugC(6, kDebugEvents, " --- %s ---", __func__);
|
||||
|
||||
Common::Point mousePosAdjustedByStageOrigin = point;
|
||||
mousePosAdjustedByStageOrigin.x -= _boundingBox.left;
|
||||
mousePosAdjustedByStageOrigin.y -= _boundingBox.top;
|
||||
|
||||
uint16 result;
|
||||
if (_cameras.empty()) {
|
||||
if (!inBounds) {
|
||||
if (!_boundingBox.contains(point)) {
|
||||
inBounds = true;
|
||||
}
|
||||
}
|
||||
result = queryChildrenAboutMouseEvents(mousePosAdjustedByStageOrigin, eventMask, state, kWrapNone);
|
||||
} else {
|
||||
result = findActorToAcceptMouseEventsCamera(mousePosAdjustedByStageOrigin, eventMask, state, inBounds);
|
||||
}
|
||||
|
||||
debugC(6, kDebugEvents, " --- END %s ---", __func__);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16 StageActor::findActorToAcceptKeyboardEvents(
|
||||
uint16 asciiCode,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state) {
|
||||
|
||||
uint16 result = 0;
|
||||
for (SpatialEntity *child : _children) {
|
||||
uint16 handledEvents = child->findActorToAcceptKeyboardEvents(asciiCode, eventMask, state);
|
||||
if (handledEvents != 0) {
|
||||
eventMask &= ~handledEvents;
|
||||
result |= handledEvents;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StageActor::assertHasNoParent(const SpatialEntity *entity) {
|
||||
// Make sure entity is not in our children.
|
||||
for (SpatialEntity *child : _children) {
|
||||
if (child == entity) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure entity doesn't have a parent.
|
||||
if (entity->getParentStage() != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StageActor::removeAllChildren() {
|
||||
for (SpatialEntity *child : _children) {
|
||||
child->setToNoParentStage();
|
||||
}
|
||||
_children.clear();
|
||||
}
|
||||
|
||||
void StageActor::setMousePosition(int16 x, int16 y) {
|
||||
if (_parentStage != nullptr) {
|
||||
x += _boundingBox.left;
|
||||
y += _boundingBox.top;
|
||||
_parentStage->setMousePosition(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateLocalBounds() {
|
||||
for (SpatialEntity *child : _children) {
|
||||
if (child->isVisible()) {
|
||||
child->invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateLocalZIndex() {
|
||||
if (_parentStage != nullptr) {
|
||||
_parentStage->invalidateZIndexOf(this);
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::invalidateZIndexOf(const SpatialEntity *entity) {
|
||||
if (!assertIsMyChild(entity)) {
|
||||
error("%s: Attempt to invalidate local z-index of non-child", __func__);
|
||||
}
|
||||
|
||||
// Remove the entity from the sorted array and re-insert it at the correct position.
|
||||
for (auto it = _children.begin(); it != _children.end(); ++it) {
|
||||
if (*it == entity) {
|
||||
_children.erase(it);
|
||||
_children.insert(const_cast<SpatialEntity *>(entity));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the whole stage dirty since z-order changed.
|
||||
if (isVisible()) {
|
||||
invalidateLocalBounds();
|
||||
}
|
||||
}
|
||||
|
||||
int StageActor::compareSpatialActorByZIndex(const SpatialEntity *a, const SpatialEntity *b) {
|
||||
int diff = b->zIndex() - a->zIndex();
|
||||
if (diff < 0)
|
||||
return -1; // a should come before b
|
||||
else if (diff > 0)
|
||||
return 1; // b should come before a
|
||||
else {
|
||||
// If z-indices are equal, compare pointers for stable sort
|
||||
return (a < b) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
void StageActor::currentMousePosition(Common::Point &point) {
|
||||
if (getParentStage() != nullptr) {
|
||||
getParentStage()->currentMousePosition(point);
|
||||
point -= getBbox().origin();
|
||||
}
|
||||
}
|
||||
|
||||
void RootStage::invalidateRect(const Common::Rect &rect) {
|
||||
Common::Rect rectToAdd = rect;
|
||||
rectToAdd.clip(_boundingBox);
|
||||
_dirtyRegion.addRect(rectToAdd);
|
||||
}
|
||||
|
||||
void RootStage::currentMousePosition(Common::Point &point) {
|
||||
point = g_engine->getEventManager()->getMousePos();
|
||||
point -= getBbox().origin();
|
||||
}
|
||||
|
||||
void RootStage::drawAll(DisplayContext &displayContext) {
|
||||
StageActor::draw(displayContext);
|
||||
}
|
||||
|
||||
void RootStage::drawDirtyRegion(DisplayContext &displayContext) {
|
||||
displayContext.setClipTo(_dirtyRegion);
|
||||
StageActor::draw(displayContext);
|
||||
displayContext.emptyCurrentClip();
|
||||
}
|
||||
|
||||
uint16 RootStage::findActorToAcceptMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) {
|
||||
|
||||
// Handle any mouse moved events.
|
||||
uint16 result = StageActor::findActorToAcceptMouseEvents(point, eventMask, state, inBounds);
|
||||
eventMask &= ~result;
|
||||
if (eventMask & kMouseEnterFlag) {
|
||||
state.mouseEnter = this;
|
||||
result |= kMouseEnterFlag;
|
||||
}
|
||||
if (eventMask & kMouseOutOfFocusFlag) {
|
||||
state.mouseOutOfFocus = this;
|
||||
result |= kMouseOutOfFocusFlag;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RootStage::mouseEnteredEvent(const Common::Event &event) {
|
||||
_isMouseInside = true;
|
||||
g_engine->getCursorManager()->unsetTemporary();
|
||||
}
|
||||
|
||||
void RootStage::mouseExitedEvent(const Common::Event &event) {
|
||||
_isMouseInside = false;
|
||||
}
|
||||
|
||||
void RootStage::mouseOutOfFocusEvent(const Common::Event &event) {
|
||||
_isMouseInside = true;
|
||||
g_engine->getCursorManager()->unsetTemporary();
|
||||
}
|
||||
|
||||
void RootStage::deleteChildrenFromContextId(uint contextId) {
|
||||
for (auto it = _children.begin(); it != _children.end();) {
|
||||
uint actorContextId = (*it)->contextId();
|
||||
if (actorContextId == contextId) {
|
||||
it = _children.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RootStage::setMousePosition(int16 x, int16 y) {
|
||||
x += _boundingBox.left;
|
||||
y += _boundingBox.top;
|
||||
warning("%s: STUB: (%d, %d)", __func__, x, y);
|
||||
}
|
||||
|
||||
StageDirector::StageDirector() {
|
||||
_rootStage = new RootStage;
|
||||
Common::Rect rootStageBounds(MediaStationEngine::SCREEN_WIDTH, MediaStationEngine::SCREEN_HEIGHT);
|
||||
_rootStage->setBounds(rootStageBounds);
|
||||
g_engine->registerActor(_rootStage);
|
||||
}
|
||||
|
||||
StageDirector::~StageDirector() {
|
||||
g_engine->destroyActor(RootStage::ROOT_STAGE_ACTOR_ID);
|
||||
_rootStage = nullptr;
|
||||
}
|
||||
|
||||
void StageDirector::drawAll() {
|
||||
_rootStage->drawAll(g_engine->getDisplayManager()->_displayContext);
|
||||
}
|
||||
|
||||
void StageDirector::drawDirtyRegion() {
|
||||
_rootStage->drawDirtyRegion(g_engine->getDisplayManager()->_displayContext);
|
||||
}
|
||||
|
||||
void StageDirector::clearDirtyRegion() {
|
||||
_rootStage->_dirtyRegion._rects.clear();
|
||||
_rootStage->_dirtyRegion._bounds = Common::Rect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void StageDirector::handleKeyboardEvent(const Common::Event &event) {
|
||||
MouseActorState state;
|
||||
uint16 flags = _rootStage->findActorToAcceptKeyboardEvents(event.kbd.ascii, kKeyDownFlag, state);
|
||||
if (flags & kKeyDownFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching to actor %d", __func__, state.keyDown->id());
|
||||
state.keyDown->keyboardEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void StageDirector::handleMouseDownEvent(const Common::Event &event) {
|
||||
MouseActorState state;
|
||||
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseDownFlag, state, false);
|
||||
if (flags & kMouseDownFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching to actor %d", __func__, state.mouseDown->id());
|
||||
state.mouseDown->mouseDownEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void StageDirector::handleMouseUpEvent(const Common::Event &event) {
|
||||
MouseActorState state;
|
||||
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseUpFlag, state, false);
|
||||
if (flags & kMouseUpFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching to actor %d", __func__, state.mouseUp->id());
|
||||
state.mouseUp->mouseUpEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void StageDirector::handleMouseMovedEvent(const Common::Event &event) {
|
||||
MouseActorState state;
|
||||
uint16 flags = _rootStage->findActorToAcceptMouseEvents(
|
||||
event.mouse,
|
||||
kMouseEnterFlag | kMouseExitFlag | kMouseMovedFlag,
|
||||
state, false);
|
||||
debugC(5, kDebugEvents, "%s: Calling sendMouseEnterExitEvent", __func__);
|
||||
|
||||
sendMouseEnterExitEvent(flags, state, event);
|
||||
if (flags & kMouseMovedFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching mouse moved to actor %d", __func__, state.mouseMoved->id());
|
||||
state.mouseMoved->mouseMovedEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void StageDirector::handleMouseEnterExitEvent(const Common::Event &event) {
|
||||
MouseActorState state;
|
||||
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseEnterFlag | kMouseExitFlag, state, false);
|
||||
sendMouseEnterExitEvent(flags, state, event);
|
||||
}
|
||||
|
||||
void StageDirector::handleMouseOutOfFocusEvent(const Common::Event &event) {
|
||||
MouseActorState state;
|
||||
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseExitFlag | kMouseOutOfFocusFlag, state, false);
|
||||
|
||||
if (flags & kMouseExitFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching mouse enter to actor %d", __func__, state.mouseExit->id());
|
||||
state.mouseExit->mouseExitedEvent(event);
|
||||
}
|
||||
|
||||
if (flags & kMouseOutOfFocusFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching mouse out of focus to actor %d", __func__, state.mouseOutOfFocus->id());
|
||||
state.mouseOutOfFocus->mouseOutOfFocusEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void StageDirector::sendMouseEnterExitEvent(uint16 flags, MouseActorState &state, const Common::Event &event) {
|
||||
if (state.mouseMoved != state.mouseEnter || state.mouseMoved != state.mouseExit) {
|
||||
if (flags & kMouseEnterFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching mouse enter to actor %d", __func__, state.mouseEnter->id());
|
||||
state.mouseEnter->mouseEnteredEvent(event);
|
||||
}
|
||||
|
||||
if (flags & kMouseExitFlag) {
|
||||
debugC(5, kDebugEvents, "%s: Dispatching mouse exit to actor %d", __func__, state.mouseExit->id());
|
||||
state.mouseExit->mouseExitedEvent(event);
|
||||
}
|
||||
} else {
|
||||
debugC(5, kDebugEvents, "%s: No hotspot to dispatch to", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
191
engines/mediastation/actors/stage.h
Normal file
191
engines/mediastation/actors/stage.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_STAGE_H
|
||||
#define MEDIASTATION_STAGE_H
|
||||
|
||||
#include "common/events.h"
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/graphics.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
// Cylindrical wrapping allows content on a stage to wrap around like a cylinder - for example, when you scroll past the
|
||||
// right edge, content from the left edge appears, creating the illusion of an infinite looping world.
|
||||
enum CylindricalWrapMode : int {
|
||||
kWrapNone = 0, // No offset (default)
|
||||
kWrapRight = 1, // Right wrap (X + extent.x)
|
||||
kWrapBottom = 2, // Bottom wrap (Y + extent.y)
|
||||
kWrapLeftTop = 3, // Left + Top wrap (X - extent.x, Y - extent.y)
|
||||
kWrapLeft = 4, // Left wrap (X - extent.x)
|
||||
kWrapRightBottom = 5, // Right + Bottom wrap (X + extent.x, Y + extent.y)
|
||||
kWrapTop = 6, // Top wrap (Y - extent.y)
|
||||
kWrapLeftBottom = 7, // Left + Bottom wrap (X - extent.x, Y + extent.y)
|
||||
kWrapRightTop = 8 // Right + Top wrap (X + extent.x, Y - extent.y)
|
||||
};
|
||||
|
||||
class CameraActor;
|
||||
class StageActor : public SpatialEntity {
|
||||
public:
|
||||
StageActor();
|
||||
virtual ~StageActor();
|
||||
|
||||
virtual void draw(DisplayContext &displayContext) override;
|
||||
void drawUsingStage(DisplayContext &displayContext);
|
||||
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual void preload(const Common::Rect &rect) override;
|
||||
virtual bool isVisible() const override { return _children.size() > 0; }
|
||||
virtual bool isRectInMemory(const Common::Rect &rect) override;
|
||||
|
||||
void addChildSpatialEntity(SpatialEntity *entity);
|
||||
void removeChildSpatialEntity(SpatialEntity *entity);
|
||||
void addCamera(CameraActor *camera);
|
||||
void removeCamera(CameraActor *camera);
|
||||
void setCurrentCamera(CameraActor *camera);
|
||||
CameraActor *getCurrentCamera() const { return _currentCamera; }
|
||||
|
||||
uint16 queryChildrenAboutMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
CylindricalWrapMode wrapMode = kWrapNone);
|
||||
uint16 findActorToAcceptMouseEventsObject(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds);
|
||||
uint16 findActorToAcceptMouseEventsCamera(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds);
|
||||
virtual uint16 findActorToAcceptMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) override;
|
||||
virtual uint16 findActorToAcceptKeyboardEvents(
|
||||
uint16 asciiCode,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state) override;
|
||||
virtual void currentMousePosition(Common::Point &point);
|
||||
virtual void setMousePosition(int16 x, int16 y) override;
|
||||
|
||||
void invalidateZIndexOf(const SpatialEntity *entity);
|
||||
virtual void invalidateLocalBounds() override;
|
||||
virtual void invalidateRect(const Common::Rect &rect);
|
||||
|
||||
bool cylindricalX() const { return _cylindricalX; }
|
||||
bool cylindricalY() const { return _cylindricalY; }
|
||||
Common::Point extent() const { return _extent; }
|
||||
|
||||
protected:
|
||||
// Whether or not cameras on this stage should wrap around this stage.
|
||||
bool _cylindricalX = false;
|
||||
bool _cylindricalY = false;
|
||||
Common::Point _extent;
|
||||
SpatialEntity *_pendingParent = nullptr;
|
||||
CameraActor *_currentCamera = nullptr;
|
||||
|
||||
void addActorToStage(uint actorId);
|
||||
void removeActorFromStage(uint actorId);
|
||||
bool isRectInMemoryTest(const Common::Rect &rect, CylindricalWrapMode wrapMode);
|
||||
void preloadTest(const Common::Rect &rect, CylindricalWrapMode wrapMode);
|
||||
|
||||
bool assertHasNoParent(const SpatialEntity *entity);
|
||||
bool assertHasParentThatIsNotMe(const SpatialEntity *entity) { return !assertIsMyChild(entity); }
|
||||
bool assertIsMyChild(const SpatialEntity *entity) { return this == entity->getParentStage();}
|
||||
void removeAllChildren();
|
||||
|
||||
virtual void invalidateLocalZIndex() override;
|
||||
void invalidateUsingCameras(const Common::Rect &rect);
|
||||
void invalidateObject(const Common::Rect &rect, const Common::Rect &visibleRegion);
|
||||
// The function is called this in the original; not sure why though.
|
||||
void invalidateTest(const Common::Rect &rect, const Common::Rect &clip, const Common::Point &originAdjustment);
|
||||
|
||||
virtual void loadIsComplete() override;
|
||||
|
||||
static int compareSpatialActorByZIndex(const SpatialEntity *a, const SpatialEntity *b);
|
||||
|
||||
Common::SortedArray<SpatialEntity *, const SpatialEntity *> _children;
|
||||
Common::SortedArray<CameraActor *, const SpatialEntity *> _cameras;
|
||||
};
|
||||
|
||||
class RootStage : public StageActor {
|
||||
public:
|
||||
friend class StageDirector;
|
||||
RootStage() : StageActor() { _id = ROOT_STAGE_ACTOR_ID; };
|
||||
|
||||
virtual uint16 findActorToAcceptMouseEvents(
|
||||
const Common::Point &point,
|
||||
uint16 eventMask,
|
||||
MouseActorState &state,
|
||||
bool inBounds) override;
|
||||
virtual void currentMousePosition(Common::Point &point) override;
|
||||
virtual void setMousePosition(int16 x, int16 y) override;
|
||||
virtual void invalidateRect(const Common::Rect &rect) override;
|
||||
virtual void deleteChildrenFromContextId(uint contextId);
|
||||
|
||||
virtual void mouseEnteredEvent(const Common::Event &event) override;
|
||||
virtual void mouseExitedEvent(const Common::Event &event) override;
|
||||
virtual void mouseOutOfFocusEvent(const Common::Event &event) override;
|
||||
|
||||
void drawAll(DisplayContext &displayContext);
|
||||
void drawDirtyRegion(DisplayContext &displayContext);
|
||||
|
||||
Region _dirtyRegion;
|
||||
|
||||
private:
|
||||
static const uint ROOT_STAGE_ACTOR_ID = 2;
|
||||
bool _isMouseInside = false;
|
||||
};
|
||||
|
||||
class StageDirector {
|
||||
public:
|
||||
StageDirector();
|
||||
~StageDirector();
|
||||
|
||||
RootStage *getRootStage() const { return _rootStage; }
|
||||
|
||||
void drawAll();
|
||||
void drawDirtyRegion();
|
||||
void clearDirtyRegion();
|
||||
|
||||
void handleKeyboardEvent(const Common::Event &event);
|
||||
void handleMouseDownEvent(const Common::Event &event);
|
||||
void handleMouseUpEvent(const Common::Event &event);
|
||||
void handleMouseMovedEvent(const Common::Event &event);
|
||||
void handleMouseEnterExitEvent(const Common::Event &event);
|
||||
void handleMouseOutOfFocusEvent(const Common::Event &event);
|
||||
void sendMouseEnterExitEvent(uint16 flags, MouseActorState &state, const Common::Event &event);
|
||||
|
||||
private:
|
||||
RootStage *_rootStage = nullptr;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
114
engines/mediastation/actors/text.cpp
Normal file
114
engines/mediastation/actors/text.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/actors/text.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
void TextActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
|
||||
switch (paramType) {
|
||||
case kActorHeaderStartup:
|
||||
_isVisible = static_cast<bool>(chunk.readTypedByte());
|
||||
break;
|
||||
|
||||
case kActorHeaderEditable:
|
||||
_editable = chunk.readTypedByte();
|
||||
break;
|
||||
|
||||
case kActorHeaderLoadType:
|
||||
_loadType = chunk.readTypedByte();
|
||||
break;
|
||||
|
||||
case kActorHeaderFontId:
|
||||
_fontActorId = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderTextMaxLength:
|
||||
_maxTextLength = chunk.readTypedUint16();
|
||||
break;
|
||||
|
||||
case kActorHeaderInitialText:
|
||||
_text = chunk.readTypedString();
|
||||
break;
|
||||
|
||||
case kActorHeaderTextJustification:
|
||||
_justification = static_cast<TextJustification>(chunk.readTypedUint16());
|
||||
break;
|
||||
|
||||
case kActorHeaderTextPosition:
|
||||
_position = static_cast<TextPosition>(chunk.readTypedUint16());
|
||||
break;
|
||||
|
||||
case kActorHeaderTextCharacterClass: {
|
||||
CharacterClass characterClass;
|
||||
characterClass.firstAsciiCode = chunk.readTypedUint16();
|
||||
characterClass.lastAsciiCode = chunk.readTypedUint16();
|
||||
_acceptedInput.push_back(characterClass);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SpatialEntity::readParameter(chunk, paramType);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue TextActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kTextMethod: {
|
||||
assert(args.empty());
|
||||
error("%s: Text() method not implemented yet", __func__);
|
||||
}
|
||||
|
||||
case kSetTextMethod: {
|
||||
assert(args.size() == 1);
|
||||
error("%s: getText() method not implemented yet", __func__);
|
||||
}
|
||||
|
||||
case kSpatialShowMethod: {
|
||||
assert(args.empty());
|
||||
_isVisible = true;
|
||||
warning("%s: spatialShow method not implemented yet", __func__);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kSpatialHideMethod: {
|
||||
assert(args.empty());
|
||||
_isVisible = false;
|
||||
warning("%s: spatialHide method not implemented yet", __func__);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return SpatialEntity::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
Common::String TextActor::text() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
void TextActor::setText(Common::String text) {
|
||||
error("%s: Setting text not implemented yet", __func__);
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
76
engines/mediastation/actors/text.h
Normal file
76
engines/mediastation/actors/text.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_TEXT_H
|
||||
#define MEDIASTATION_TEXT_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
enum TextJustification {
|
||||
kTextJustificationLeft = 0x25c,
|
||||
kTextJustificationRight = 0x25d,
|
||||
kTextJustificationCenter = 0x25e
|
||||
};
|
||||
|
||||
enum TextPosition {
|
||||
kTextPositionMiddle = 0x25e,
|
||||
kTextPositionTop = 0x260,
|
||||
kTextPositionBotom = 0x261
|
||||
};
|
||||
|
||||
struct CharacterClass {
|
||||
uint firstAsciiCode = 0;
|
||||
uint lastAsciiCode = 0;
|
||||
};
|
||||
|
||||
class TextActor : public SpatialEntity {
|
||||
public:
|
||||
TextActor() : SpatialEntity(kActorTypeText) {};
|
||||
|
||||
virtual bool isVisible() const override { return _isVisible; }
|
||||
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
|
||||
private:
|
||||
bool _editable = false;
|
||||
uint _loadType = 0;
|
||||
bool _isVisible = false;
|
||||
Common::String _text;
|
||||
uint _maxTextLength = 0;
|
||||
uint _fontActorId = 0;
|
||||
TextJustification _justification;
|
||||
TextPosition _position;
|
||||
Common::Array<CharacterClass> _acceptedInput;
|
||||
|
||||
// Method implementations.
|
||||
Common::String text() const;
|
||||
void setText(Common::String text);
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
94
engines/mediastation/actors/timer.cpp
Normal file
94
engines/mediastation/actors/timer.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
#include "mediastation/actors/timer.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
ScriptValue TimerActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
switch (methodId) {
|
||||
case kTimePlayMethod: {
|
||||
assert(args.size() == 0);
|
||||
timePlay();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kTimeStopMethod: {
|
||||
assert(args.size() == 0);
|
||||
timeStop();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kIsPlayingMethod: {
|
||||
assert(args.size() == 0);
|
||||
returnValue.setToBool(_isPlaying);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
return Actor::callMethod(methodId, args);
|
||||
}
|
||||
}
|
||||
|
||||
void TimerActor::timePlay() {
|
||||
_isPlaying = true;
|
||||
_startTime = g_system->getMillis();
|
||||
_lastProcessedTime = 0;
|
||||
|
||||
// Get the duration of the timer.
|
||||
// TODO: Is there a better way to find out what the max time is? Do we have to look
|
||||
// through each of the timer event handlers to figure it out?
|
||||
_duration = 0;
|
||||
const Common::Array<EventHandler *> &timeHandlers = _eventHandlers.getValOrDefault(kTimerEvent);
|
||||
for (EventHandler *timeEvent : timeHandlers) {
|
||||
// Indeed float, not time.
|
||||
double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
|
||||
uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
|
||||
if (timeEventInMilliseconds > _duration) {
|
||||
_duration = timeEventInMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
debugC(5, kDebugScript, "Timer::timePlay(): Now playing for %d ms", _duration);
|
||||
}
|
||||
|
||||
void TimerActor::timeStop() {
|
||||
if (!_isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isPlaying = false;
|
||||
_startTime = 0;
|
||||
_lastProcessedTime = 0;
|
||||
}
|
||||
|
||||
void TimerActor::process() {
|
||||
if (_isPlaying) {
|
||||
processTimeEventHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
47
engines/mediastation/actors/timer.h
Normal file
47
engines/mediastation/actors/timer.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_TIMER_H
|
||||
#define MEDIASTATION_TIMER_H
|
||||
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class TimerActor : public Actor {
|
||||
public:
|
||||
TimerActor() : Actor(kActorTypeTimer) {};
|
||||
|
||||
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
|
||||
virtual void process() override;
|
||||
|
||||
private:
|
||||
bool _isPlaying = false;
|
||||
|
||||
void timePlay();
|
||||
void timeStop();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user