[Scummvm-git-logs] scummvm master -> 7501868b3ae5addd4ed6d34401c6b46366ea77f2
elasota
noreply at scummvm.org
Sat Mar 11 18:16:10 UTC 2023
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
7501868b3a VCRUISE: Add sanimX opcode support
Commit: 7501868b3ae5addd4ed6d34401c6b46366ea77f2
https://github.com/scummvm/scummvm/commit/7501868b3ae5addd4ed6d34401c6b46366ea77f2
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-11T13:15:35-05:00
Commit Message:
VCRUISE: Add sanimX opcode support
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index a7fe14e6a0e..713728c5fc6 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -295,9 +295,26 @@ CachedSound::~CachedSound() {
this->stream.reset();
}
+TriggeredOneShot::TriggeredOneShot() : soundID(0), uniqueSlot(0) {
+}
+
+bool TriggeredOneShot::operator==(const TriggeredOneShot &other) const {
+ return soundID == other.soundID && uniqueSlot == other.uniqueSlot;
+}
+
+bool TriggeredOneShot::operator!=(const TriggeredOneShot &other) const {
+ return !((*this) == other);
+}
+
+StaticAnimParams::StaticAnimParams() : initialDelay(0), repeatDelay(0), lockInteractions(false) {
+}
+
+StaticAnimation::StaticAnimation() : currentAlternation(0), nextStartTime(0) {
+}
+
Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID)
: _system(system), _mixer(mixer), _roomNumber(1), _screenNumber(0), _direction(0), _havePanAnimations(0), _loadedRoomNumber(0), _activeScreenNumber(0),
- _gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _havePendingReturnToIdleState(false), _havePendingCompletionCheck(false),
+ _gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _forceScreenChange(false), _havePendingReturnToIdleState(false), _havePendingCompletionCheck(false),
_scriptNextInstruction(0), _escOn(false), _debugMode(false), _panoramaDirectionFlags(0),
_loadedAnimation(0), _animPendingDecodeFrame(0), _animDisplayingFrame(0), _animFirstFrame(0), _animLastFrame(0), _animStopFrame(0),
_animFrameRateLock(0), _animStartTime(0), _animFramesDecoded(0), _animDecoderState(kAnimDecoderStateStopped),
@@ -421,6 +438,9 @@ bool Runtime::runFrame() {
case kGameStateWaitingForFacing:
moreActions = runWaitForFacing();
break;
+ case kGameStateWaitingForFacingToAnim:
+ moreActions = runWaitForFacingToAnim();
+ break;
case kGameStateGyroIdle:
moreActions = runGyroIdle();
break;
@@ -485,15 +505,34 @@ bool Runtime::runIdle() {
return true;
}
+ uint32 timestamp = g_system->getMillis();
+
if (_animPlayWhileIdle) {
+ assert(_haveIdleAnimations[_direction]);
+
+ StaticAnimation &sanim = _idleAnimations[_direction];
+ bool looping = (sanim.params.repeatDelay == 0);
+
bool animEnded = false;
- continuePlayingAnimation(true, false, animEnded);
+ continuePlayingAnimation(looping, false, animEnded);
+
+ if (!looping && animEnded) {
+ _animPlayWhileIdle = false;
+ sanim.nextStartTime = timestamp + sanim.params.repeatDelay * 1000u;
+ sanim.currentAlternation = 1 - sanim.currentAlternation;
+ }
+ } else if (_haveIdleAnimations[_direction]) {
+ // Try to re-trigger
+ StaticAnimation &sanim = _idleAnimations[_direction];
+ if (sanim.nextStartTime <= timestamp) {
+ changeAnimation(sanim.animDefs[sanim.currentAlternation], false);
+ _animPlayWhileIdle = true;
+ }
}
if (_debugMode)
drawDebugOverlay();
- uint32 timestamp = g_system->getMillis();
detectPanoramaMouseMovement(timestamp);
OSEvent osEvent;
@@ -615,7 +654,7 @@ bool Runtime::runWaitForAnimation() {
return false;
}
-bool Runtime::runWaitForFacing() {
+bool Runtime::runWaitForFacingToAnim() {
bool animEnded = false;
continuePlayingAnimation(true, true, animEnded);
@@ -629,6 +668,19 @@ bool Runtime::runWaitForFacing() {
return false;
}
+bool Runtime::runWaitForFacing() {
+ bool animEnded = false;
+ continuePlayingAnimation(true, true, animEnded);
+
+ if (animEnded) {
+ _gameState = kGameStateScript;
+ return true;
+ }
+
+ // Yield
+ return false;
+}
+
bool Runtime::runGyroIdle() {
if (!_lmbDown) {
exitGyroIdle();
@@ -1235,9 +1287,11 @@ void Runtime::loadWave(uint soundID, const Common::String &soundName, const Comm
}
void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
- bool changedRoom = (roomNumber != _loadedRoomNumber);
+ bool changedRoom = (roomNumber != _loadedRoomNumber) || _forceScreenChange;
bool changedScreen = (screenNumber != _activeScreenNumber) || changedRoom;
+ _forceScreenChange = false;
+
_roomNumber = roomNumber;
_screenNumber = screenNumber;
@@ -1300,11 +1354,20 @@ void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
void Runtime::returnToIdleState() {
debug(1, "Returned to idle state in room %u screen 0%x facing direction %u", _roomNumber, _screenNumber, _direction);
+ uint32 timestamp = g_system->getMillis();
+
_animPlayWhileIdle = false;
if (_haveIdleAnimations[_direction]) {
- changeAnimation(_idleAnimations[_direction], false);
- _animPlayWhileIdle = true;
+ StaticAnimation &sanim = _idleAnimations[_direction];
+ sanim.currentAlternation = 0;
+ sanim.nextStartTime = timestamp + sanim.params.initialDelay * 1000u;
+
+ if (sanim.params.initialDelay == 0) {
+ changeAnimation(sanim.animDefs[0], false);
+ _animPlayWhileIdle = true;
+ sanim.currentAlternation = 1;
+ }
}
_idleIsOnInteraction = false;
@@ -1493,7 +1556,7 @@ void Runtime::changeMusicTrack(int track) {
Common::SharedPtr<Audio::AudioStream> loopingStream(Audio::makeLoopingAudioStream(audioStream, 0));
_musicPlayer.reset(new AudioPlayer(_mixer, loopingStream));
- _musicPlayer->play(255, 0);
+ _musicPlayer->play(100, 0);
}
} else {
warning("Music file '%s' is missing", wavFileName.c_str());
@@ -2147,18 +2210,22 @@ void Runtime::scriptOpSAnimL(ScriptArg_t arg) {
error("sanimL invalid direction");
_haveIdleAnimations[direction] = true;
- _idleAnimations[direction] = animDef;
+
+ StaticAnimation &outAnim = _idleAnimations[direction];
+
+ outAnim = StaticAnimation();
+ outAnim.animDefs[0] = animDef;
+ outAnim.animDefs[1] = animDef;
}
void Runtime::scriptOpChangeL(ScriptArg_t arg) {
TAKE_STACK(1);
- // Not actually sure what this does. It usually occurs coincident with rotation interactions and animR ops.
- // Might change the screen number? Usually seems to change the screen number to the current screen or to the
- // one being transitioned to. Need more investigation.
-
- warning("ChangeL opcode not implemented");
- (void)stackArgs;
+ // ChangeL changes the screen number, but it also forces screen entry scripts to replay, which is
+ // needed for things like the fountain.
+ _screenNumber = stackArgs[0];
+ _havePendingScreenChange = true;
+ _forceScreenChange = true;
}
void Runtime::scriptOpAnimR(ScriptArg_t arg) {
@@ -2230,7 +2297,7 @@ void Runtime::scriptOpAnimF(ScriptArg_t arg) {
_postFacingAnimDef = animDef;
_animStopFrame = stopFrame;
changeAnimation(*faceDirectionAnimDef, initialFrame, false);
- _gameState = kGameStateWaitingForFacing;
+ _gameState = kGameStateWaitingForFacingToAnim;
} else {
changeAnimation(animDef, true);
_gameState = kGameStateWaitingForAnimation;
@@ -2242,7 +2309,23 @@ void Runtime::scriptOpAnimF(ScriptArg_t arg) {
changeToCursor(_cursors[kCursorArrow]);
}
-OPCODE_STUB(AnimN)
+void Runtime::scriptOpAnimN(ScriptArg_t arg) {
+ TAKE_STACK(1);
+
+ const AnimationDef *faceDirectionAnimDef = nullptr;
+ uint initialFrame = 0;
+ uint stopFrame = 0;
+ if (computeFaceDirectionAnimation(stackArgs[0], faceDirectionAnimDef, initialFrame, stopFrame)) {
+ _animStopFrame = stopFrame;
+ changeAnimation(*faceDirectionAnimDef, initialFrame, false);
+ _gameState = kGameStateWaitingForFacing;
+ }
+
+ _direction = stackArgs[0];
+ _havePendingScreenChange = true;
+
+ changeToCursor(_cursors[kCursorArrow]);
+}
void Runtime::scriptOpAnimG(ScriptArg_t arg) {
TAKE_STACK(kAnimDefStackArgs * 2 + 1);
@@ -2526,8 +2609,37 @@ void Runtime::scriptOpParmG(ScriptArg_t arg) {
_gyros.maxValue = maxValue;
}
-OPCODE_STUB(SParmX)
-OPCODE_STUB(SAnimX)
+void Runtime::scriptOpSParmX(ScriptArg_t arg) {
+ TAKE_STACK(3);
+
+ _pendingStaticAnimParams.initialDelay = stackArgs[0];
+ _pendingStaticAnimParams.repeatDelay = stackArgs[1];
+ _pendingStaticAnimParams.lockInteractions = (stackArgs[2] != 0);
+
+ if (_pendingStaticAnimParams.lockInteractions)
+ error("Locking interactions for animation is not implemented yet");
+}
+
+void Runtime::scriptOpSAnimX(ScriptArg_t arg) {
+ TAKE_STACK(kAnimDefStackArgs * 2 + 1);
+
+ AnimationDef animDef1 = stackArgsToAnimDef(stackArgs + 0);
+ AnimationDef animDef2 = stackArgsToAnimDef(stackArgs + kAnimDefStackArgs);
+
+ uint direction = stackArgs[kAnimDefStackArgs * 2 + 0];
+
+ if (direction >= kNumDirections)
+ error("sanimX invalid direction");
+
+ _haveIdleAnimations[direction] = true;
+
+ StaticAnimation &outAnim = _idleAnimations[direction];
+
+ outAnim = StaticAnimation();
+ outAnim.animDefs[0] = animDef1;
+ outAnim.animDefs[1] = animDef2;
+ outAnim.params = _pendingStaticAnimParams;
+}
void Runtime::scriptOpVolumeUp3(ScriptArg_t arg) {
TAKE_STACK(3);
@@ -2565,17 +2677,37 @@ void Runtime::scriptOpDup(ScriptArg_t arg) {
void Runtime::scriptOpSay3(ScriptArg_t arg) {
TAKE_STACK(3);
- warning("Say3 opcode is not implemented yet");
- (void)stackArgs;
+ TriggeredOneShot oneShot;
+ oneShot.soundID = stackArgs[0];
+ oneShot.uniqueSlot = stackArgs[1];
+
+ // The third param seems to control sound interruption, but say3 is a Reah-only op and it's only ever 1.
+ if (stackArgs[2] != 1)
+ error("Invalid interrupt arg for say3, only 1 is supported.");
+
+ if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
+ triggerSound(false, oneShot.soundID, 100, 0);
+ _triggeredOneShots.push_back(oneShot);
+ }
}
void Runtime::scriptOpSay3Get(ScriptArg_t arg) {
TAKE_STACK(3);
- warning("Say3Get opcode is not implemented yet");
- (void)stackArgs;
+ TriggeredOneShot oneShot;
+ oneShot.soundID = stackArgs[0];
+ oneShot.uniqueSlot = stackArgs[1];
+
+ // The third param seems to control sound interruption, but say3 is a Reah-only op and it's only ever 1.
+ if (stackArgs[2] != 1)
+ error("Invalid interrupt arg for say3, only 1 is supported.");
- _scriptStack.push_back(0);
+ if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
+ triggerSound(false, oneShot.soundID, 100, 0);
+ _triggeredOneShots.push_back(oneShot);
+ _scriptStack.push_back(0);
+ } else
+ _scriptStack.push_back(oneShot.soundID);
}
void Runtime::scriptOpSetTimer(ScriptArg_t arg) {
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 4e217a0fd29..c69c7c548d4 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -70,14 +70,15 @@ struct Script;
struct Instruction;
enum GameState {
- kGameStateBoot, // Booting the game
- kGameStateWaitingForAnimation, // Waiting for a blocking animation to complete, then resuming script
- kGameStateWaitingForFacing, // Waiting for a blocking animation to complete, then playing _postFacingAnimDef and switching to kGameStateWaitingForAnimation
- kGameStateQuit, // Quitting
- kGameStateIdle, // Waiting for input events
- kGameStateScript, // Running a script
- kGameStateGyroIdle, // Waiting for mouse movement to run a gyro
- kGameStateGyroAnimation, // Animating a gyro
+ kGameStateBoot, // Booting the game
+ kGameStateWaitingForAnimation, // Waiting for a blocking animation with no stop frame complete, then resuming script
+ kGameStateWaitingForFacing, // Waiting for a blocking animation with a stop frame to complete, then resuming script
+ kGameStateWaitingForFacingToAnim, // Waiting for a blocking animation to complete, then playing _postFacingAnimDef and switching to kGameStateWaitingForAnimation
+ kGameStateQuit, // Quitting
+ kGameStateIdle, // Waiting for input events
+ kGameStateScript, // Running a script
+ kGameStateGyroIdle, // Waiting for mouse movement to run a gyro
+ kGameStateGyroAnimation, // Animating a gyro
kGameStatePanLeft,
kGameStatePanRight,
@@ -189,6 +190,34 @@ struct CachedSound {
int32 balance;
};
+struct TriggeredOneShot {
+ TriggeredOneShot();
+
+ bool operator==(const TriggeredOneShot &other) const;
+ bool operator!=(const TriggeredOneShot &other) const;
+
+ uint soundID;
+ uint uniqueSlot;
+};
+
+struct StaticAnimParams {
+ StaticAnimParams();
+
+ uint initialDelay;
+ uint repeatDelay;
+ bool lockInteractions;
+};
+
+struct StaticAnimation {
+ StaticAnimation();
+
+ AnimationDef animDefs[2];
+ StaticAnimParams params;
+
+ uint32 nextStartTime;
+ uint currentAlternation;
+};
+
class Runtime {
public:
Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID);
@@ -342,6 +371,7 @@ private:
bool runScript();
bool runWaitForAnimation();
bool runWaitForFacing();
+ bool runWaitForFacingToAnim();
bool runGyroIdle();
bool runGyroAnimation();
void exitGyroIdle();
@@ -516,8 +546,9 @@ private:
bool _havePanUpFromDirection[kNumDirections];
bool _havePanDownFromDirection[kNumDirections];
- AnimationDef _idleAnimations[kNumDirections];
+ StaticAnimation _idleAnimations[kNumDirections];
bool _haveIdleAnimations[kNumDirections];
+ StaticAnimParams _pendingStaticAnimParams;
AnimationDef _postFacingAnimDef;
@@ -540,6 +571,7 @@ private:
uint _loadedRoomNumber;
uint _activeScreenNumber;
bool _havePendingScreenChange;
+ bool _forceScreenChange;
bool _havePendingReturnToIdleState;
bool _havePendingCompletionCheck;
GameState _gameState;
@@ -613,6 +645,8 @@ private:
Common::HashMap<Common::String, Common::ArchiveMemberPtr> _waves;
Common::HashMap<uint, Common::SharedPtr<CachedSound> > _cachedSounds;
+ Common::Array<TriggeredOneShot> _triggeredOneShots;
+
static const uint kAnimDefStackArgs = 8;
static const uint kCursorArrow = 0;
More information about the Scummvm-git-logs
mailing list