[Scummvm-git-logs] scummvm master -> 24429f741792547049431d990bc93c19b3765e2f
elasota
noreply at scummvm.org
Tue May 16 00:47:54 UTC 2023
This automated email contains information about 8 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
07d03a6a26 VCRUISE: Implement character swap
b8ee841aad VCRUISE: Add comment explaining why defs are room-linked
5fe99ebc53 VCRUISE: Cut the volume of everything in half to fix integer overflows where one of the speaker volumes would be >255.
388b418b0b VCRUISE: Clear idle animations immediately when executing screen-transitioning ops to prevent them from playing during d
40649e3007 VCRUISE: Add heroGetPos, heroSetPos, BitAnd, BitOr, Mul, Div, and Mod opcodes
9ef7085bff VCRUISE: Fix ItemSelect! op, implement proper animChange op behavior.
8dd26f9daa VCRUISE: Fix up volume behavior, add some more sound ops for Schizm, fix parm op parsing.
24429f7417 VCRUISE: Rework a bunch of things to fix up Schizm's duplicate room behavior.
Commit: 07d03a6a26a17a50c0fa8cf51cc271a5e4dc77c8
https://github.com/scummvm/scummvm/commit/07d03a6a26a17a50c0fa8cf51cc271a5e4dc77c8
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Implement character swap
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
engines/vcruise/script.cpp
engines/vcruise/script.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 6f0b8bee881..296d69d20e1 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -385,11 +385,19 @@ void SfxData::load(Common::SeekableReadStream &stream, Audio::Mixer *mixer) {
c = '/';
}
+ size_t commentPos = sfxPath.find(';');
+ if (commentPos != Common::String::npos) {
+ sfxPath = sfxPath.substr(0, commentPos);
+ sfxPath.trim();
+ }
+
sfxPath = Common::String("Sfx/") + sfxPath;
Common::File f;
- if (!f.open(sfxPath))
+ if (!f.open(sfxPath)) {
warning("SfxData::load: Could not open sample file '%s'", sfxPath.c_str());
+ continue;
+ }
int64 size = f.size();
if (size <= 0 || size > 0x1fffffffu) {
@@ -2760,6 +2768,28 @@ void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
}
}
+void Runtime::changeHero() {
+ recordSaveGameSnapshot();
+
+ Common::SharedPtr<SaveGameSwappableState> currentState = _saveGame->states[0];
+ Common::SharedPtr<SaveGameSwappableState> alternateState = _saveGame->states[1];
+
+ if (_swapOutRoom && _swapOutScreen) {
+ // Some scripts may kick the player out to another location on swap back,
+ // such as the elevator in the first area on Hannah's quest
+ currentState->roomNumber = _swapOutRoom;
+ currentState->screenNumber = _swapOutScreen;
+ currentState->direction = _direction;
+ }
+
+ _saveGame->states[0] = alternateState;
+ _saveGame->states[1] = currentState;
+
+ _saveGame->hero ^= 1u;
+
+ restoreSaveGameSnapshot();
+}
+
void Runtime::triggerPreIdleActions() {
debug(1, "Triggering pre-idle actions in room %u screen 0%x facing direction %u", _roomNumber, _screenNumber, _direction);
@@ -2869,6 +2899,13 @@ bool Runtime::dischargeIdleMouseMove() {
}
}
+ if (_gameID == GID_SCHIZM && !isOnInteraction) {
+ if (_traySection.rect.contains(_mousePos) && (_traySection.rect.right - _mousePos.x) < 88u) {
+ isOnInteraction = true;
+ interactionID = kHeroChangeInteractionID;
+ }
+ }
+
if (_idleIsOnInteraction && (!isOnInteraction || interactionID != _idleInteractionID)) {
// Mouse left the previous interaction
_idleIsOnInteraction = false;
@@ -2882,12 +2919,17 @@ bool Runtime::dischargeIdleMouseMove() {
_idleIsOnInteraction = true;
_idleInteractionID = interactionID;
- // New interaction, is there a script?
- Common::SharedPtr<Script> script = findScriptForInteraction(interactionID);
+ if (interactionID == kHeroChangeInteractionID) {
+ changeToCursor(_cursors[16]);
+ _idleHaveClickInteraction = true;
+ } else {
+ // New interaction, is there a script?
+ Common::SharedPtr<Script> script = findScriptForInteraction(interactionID);
- if (script) {
- activateScript(script, ScriptEnvironmentVars());
- return true;
+ if (script) {
+ activateScript(script, ScriptEnvironmentVars());
+ return true;
+ }
}
}
@@ -2929,17 +2971,22 @@ bool Runtime::dischargeIdleMouseDown() {
bool Runtime::dischargeIdleClick() {
if (_idleIsOnInteraction && _idleHaveClickInteraction) {
- // Interaction, is there a script?
- Common::SharedPtr<Script> script = findScriptForInteraction(_idleInteractionID);
+ if (_gameID == GID_SCHIZM && _idleInteractionID == kHeroChangeInteractionID) {
+ changeHero();
+ return true;
+ } else {
+ // Interaction, is there a script?
+ Common::SharedPtr<Script> script = findScriptForInteraction(_idleInteractionID);
- _idleIsOnInteraction = false; // ?
+ _idleIsOnInteraction = false; // ?
- if (script) {
- ScriptEnvironmentVars vars;
- vars.lmb = true;
+ if (script) {
+ ScriptEnvironmentVars vars;
+ vars.lmb = true;
- activateScript(script, vars);
- return true;
+ activateScript(script, vars);
+ return true;
+ }
}
}
@@ -3787,13 +3834,12 @@ void Runtime::compileSchizmLogicSet(const uint *roomNumbers, uint numRooms) {
Common::SharedPtr<ScriptSet> scriptSet(new ScriptSet());
for (uint i = 0; i < numRooms; i++) {
-
Common::String logicFileName = Common::String::format("Log/Room%02u.log", roomNumbers[i]);
Common::File logicFile;
if (logicFile.open(logicFileName)) {
debug(1, "Compiling script %s...", logicFileName.c_str());
- compileSchizmLogicFile(*scriptSet, logicFile, static_cast<uint>(logicFile.size()), logicFileName, gs.get());
+ compileSchizmLogicFile(*scriptSet, roomNumbers[i], logicFile, static_cast<uint>(logicFile.size()), logicFileName, gs.get());
logicFile.close();
}
}
@@ -4703,7 +4749,7 @@ void Runtime::recordSaveGameSnapshot() {
snapshot->numStates = 1;
else if (_gameID == GID_SCHIZM) {
snapshot->numStates = 2;
- snapshot->states[1].reset(new SaveGameSwappableState());
+ snapshot->states[1] = _altState;
}
SaveGameSwappableState *mainState = snapshot->states[0].get();
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index eeb8f623334..f2ba28ecb81 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -786,6 +786,7 @@ private:
void resolveSoundByNameOrID(const StackValue &stackValue, bool load, StackInt_t &outSoundID, SoundInstance *&outWave);
void changeToScreen(uint roomNumber, uint screenNumber);
+ void changeHero();
void triggerPreIdleActions();
void returnToIdleState();
void changeToCursor(const Common::SharedPtr<Graphics::WinCursorGroup> &cursor);
@@ -1240,6 +1241,8 @@ private:
static const uint kSoundCacheSize = 16;
+ static const uint kHeroChangeInteractionID = 0xffffffffu;
+
Common::Pair<Common::String, Common::SharedPtr<SoundCache> > _soundCache[kSoundCacheSize];
uint _soundCacheIndex;
diff --git a/engines/vcruise/script.cpp b/engines/vcruise/script.cpp
index 6e6f031e128..1d3314c474c 100644
--- a/engines/vcruise/script.cpp
+++ b/engines/vcruise/script.cpp
@@ -149,7 +149,7 @@ struct ScriptNamedInstruction {
class ScriptCompiler {
public:
- ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, IScriptCompilerGlobalState *gs);
+ ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, uint roomNumber, IScriptCompilerGlobalState *gs);
void compileScriptSet(ScriptSet *ss);
@@ -181,6 +181,7 @@ private:
const Common::String _blamePath;
ScriptDialect _dialect;
+ uint _roomNumber;
const char *_scrToken;
const char *_eroomToken;
@@ -192,9 +193,9 @@ private:
class ScriptCompilerGlobalState : public IScriptCompilerGlobalState {
public:
- void define(const Common::String &key, const Common::String &value) override;
+ void define(const Common::String &key, uint roomNumber, int32 value) override;
- const Common::String *getTokenReplacement(const Common::String &str) const override;
+ bool getDefine(const Common::String &str, uint &outRoomNumber, int32 &outValue) const override;
uint getFunctionIndex(const Common::String &fnName) override;
void setFunction(uint fnIndex, const Common::SharedPtr<Script> &fn) override;
@@ -204,14 +205,21 @@ public:
Common::SharedPtr<Script> getFunction(uint fnIndex) const override;
private:
- Common::HashMap<Common::String, Common::String> _defs;
+ struct Def {
+ Def();
+
+ int32 _value;
+ uint _roomNumber;
+ };
+
+ Common::HashMap<Common::String, Def> _defs;
Common::HashMap<Common::String, uint> _functionNameToIndex;
Common::Array<Common::SharedPtr<Script> > _functions;
};
-ScriptCompiler::ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, IScriptCompilerGlobalState *gs)
- : _numberParsingMode(kNumberParsingHex), _parser(parser), _blamePath(blamePath), _dialect(dialect), _gs(gs),
+ScriptCompiler::ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, uint roomNumber, IScriptCompilerGlobalState *gs)
+ : _numberParsingMode(kNumberParsingHex), _parser(parser), _blamePath(blamePath), _dialect(dialect), _roomNumber(roomNumber), _gs(gs),
_scrToken(nullptr), _eroomToken(nullptr) {
}
@@ -418,7 +426,21 @@ void ScriptCompiler::compileRoomScriptSet(RoomScriptSet *rss) {
if (!_parser.parseToken(value, state))
error("Error compiling script at line %i col %i: Expected value", static_cast<int>(state._lineNum), static_cast<int>(state._col));
- _gs->define(key, value);
+ bool isNegative = false;
+ if (value[0] == '-') {
+ isNegative = true;
+ value = value.substr(1);
+ }
+
+ uint32 number = 0;
+ if (!parseNumber(value, number))
+ error("Error compiling script at line %i col %i: Expected number", static_cast<int>(state._lineNum), static_cast<int>(state._col));
+
+ int32 signedNumber = static_cast<int32>(number);
+ if (isNegative)
+ signedNumber = -signedNumber;
+
+ _gs->define(key, _roomNumber, signedNumber);
} else if (_dialect == kScriptDialectSchizm && token == "~Fun") {
Common::String fnName;
if (!_parser.parseToken(fnName, state))
@@ -782,17 +804,16 @@ static ScriptNamedInstruction g_schizmNamedInstructions[] = {
{"allowedSave", ProtoOp::kProtoOpNoop, ScriptOps::kInvalid},
};
-bool ScriptCompiler::compileInstructionToken(ProtoScript &script, const Common::String &tokenBase) {
- const Common::String *tokenPtr = &tokenBase;
-
+bool ScriptCompiler::compileInstructionToken(ProtoScript &script, const Common::String &token) {
if (_dialect == kScriptDialectSchizm) {
- const Common::String *ppToken = _gs->getTokenReplacement(tokenBase);
- if (ppToken)
- tokenPtr = ppToken;
+ uint roomNumber = 0;
+ int32 value = 0;
+ if (_gs->getDefine(token, roomNumber, value)) {
+ script.instrs.push_back(ProtoInstruction(ScriptOps::kNumber, value));
+ return true;
+ }
}
- const Common::String &token = *tokenPtr;
-
if (_dialect == kScriptDialectSchizm && token.hasPrefix("-")) {
uint32 unumber = 0;
if (parseNumber(token.substr(1), unumber)) {
@@ -889,11 +910,14 @@ bool ScriptCompiler::compileInstructionToken(ProtoScript &script, const Common::
}
if (token.size() >= 2 && token.hasSuffix("!")) {
- if (compileInstructionToken(script, token.substr(0, token.size() - 1))) {
- script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kVarGlobalStore, 0));
- return true;
- } else
+ uint roomNumber = 0;
+ int32 varNumber = 0;
+ if (!_gs->getDefine(token.substr(0, token.size() - 1), roomNumber, varNumber) || varNumber < 0)
return false;
+
+ script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kNumber, (roomNumber << 16) + static_cast<uint>(varNumber)));
+ script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kVarGlobalStore, 0));
+ return true;
}
// HACK: Work around bugged variable name in Room02.log
@@ -903,11 +927,14 @@ bool ScriptCompiler::compileInstructionToken(ProtoScript &script, const Common::
}
if (token.size() >= 2 && token.hasSuffix("@")) {
- if (compileInstructionToken(script, token.substr(0, token.size() - 1))) {
- script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kVarGlobalLoad, 0));
- return true;
- } else
+ uint roomNumber = 0;
+ int32 varNumber = 0;
+ if (!_gs->getDefine(token.substr(0, token.size() - 1), roomNumber, varNumber) || varNumber < 0)
return false;
+
+ script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kNumber, (roomNumber << 16) + static_cast<uint>(varNumber)));
+ script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kVarGlobalLoad, 0));
+ return true;
}
// Does this look like a screen name?
@@ -1252,16 +1279,24 @@ uint ScriptCompiler::indexString(const Common::String &str) {
return it->_value;
}
-void ScriptCompilerGlobalState::define(const Common::String &key, const Common::String &value) {
- _defs.setVal(key, value);
+ScriptCompilerGlobalState::Def::Def() : _value(0), _roomNumber(0) {
}
-const Common::String *ScriptCompilerGlobalState::getTokenReplacement(const Common::String &str) const {
- Common::HashMap<Common::String, Common::String>::const_iterator it = _defs.find(str);
+void ScriptCompilerGlobalState::define(const Common::String &key, uint roomNumber, int32 value) {
+ Def &def = _defs[key];
+
+ def._roomNumber = roomNumber;
+ def._value = value;
+}
+
+bool ScriptCompilerGlobalState::getDefine(const Common::String &str, uint &outRoomNumber, int32 &outValue) const {
+ Common::HashMap<Common::String, Def>::const_iterator it = _defs.find(str);
if (it == _defs.end())
- return nullptr;
+ return false;
- return &it->_value;
+ outRoomNumber = it->_value._roomNumber;
+ outValue = it->_value._value;
+ return true;
}
uint ScriptCompilerGlobalState::getFunctionIndex(const Common::String &fnName) {
@@ -1302,11 +1337,11 @@ Common::SharedPtr<Script> ScriptCompilerGlobalState::getFunction(uint fnIndex) c
IScriptCompilerGlobalState::~IScriptCompilerGlobalState() {
}
-static void compileLogicFile(ScriptSet &scriptSet, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, ScriptDialect dialect, IScriptCompilerGlobalState *gs) {
+static void compileLogicFile(ScriptSet &scriptSet, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, ScriptDialect dialect, uint roomNumber, IScriptCompilerGlobalState *gs) {
LogicUnscrambleStream unscrambleStream(&stream, streamSize);
TextParser parser(&unscrambleStream);
- ScriptCompiler compiler(parser, blamePath, dialect, gs);
+ ScriptCompiler compiler(parser, blamePath, dialect, roomNumber, gs);
compiler.compileScriptSet(&scriptSet);
}
@@ -1318,12 +1353,12 @@ Common::SharedPtr<IScriptCompilerGlobalState> createScriptCompilerGlobalState()
Common::SharedPtr<ScriptSet> compileReahLogicFile(Common::ReadStream &stream, uint streamSize, const Common::String &blamePath) {
Common::SharedPtr<ScriptSet> scriptSet(new ScriptSet());
- compileLogicFile(*scriptSet, stream, streamSize, blamePath, kScriptDialectReah, nullptr);
+ compileLogicFile(*scriptSet, stream, streamSize, blamePath, kScriptDialectReah, 0, nullptr);
return scriptSet;
}
-void compileSchizmLogicFile(ScriptSet &scriptSet, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs) {
- compileLogicFile(scriptSet, stream, streamSize, blamePath, kScriptDialectSchizm, gs);
+void compileSchizmLogicFile(ScriptSet &scriptSet, uint roomNumber, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs) {
+ compileLogicFile(scriptSet, stream, streamSize, blamePath, kScriptDialectSchizm, roomNumber, gs);
}
} // namespace VCruise
diff --git a/engines/vcruise/script.h b/engines/vcruise/script.h
index 40cac394e91..b05a0db7fd3 100644
--- a/engines/vcruise/script.h
+++ b/engines/vcruise/script.h
@@ -268,8 +268,8 @@ struct FunctionDef {
struct IScriptCompilerGlobalState {
virtual ~IScriptCompilerGlobalState();
- virtual void define(const Common::String &key, const Common::String &value) = 0;
- virtual const Common::String *getTokenReplacement(const Common::String &str) const = 0;
+ virtual void define(const Common::String &key, uint roomNumber, int32 value) = 0;
+ virtual bool getDefine(const Common::String &str, uint &outRoomNumber, int32 &outValue) const = 0;
virtual uint getFunctionIndex(const Common::String &fnName) = 0;
virtual void setFunction(uint fnIndex, const Common::SharedPtr<Script> &fn) = 0;
@@ -281,7 +281,7 @@ struct IScriptCompilerGlobalState {
Common::SharedPtr<IScriptCompilerGlobalState> createScriptCompilerGlobalState();
Common::SharedPtr<ScriptSet> compileReahLogicFile(Common::ReadStream &stream, uint streamSize, const Common::String &blamePath);
-void compileSchizmLogicFile(ScriptSet &scriptSet, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs);
+void compileSchizmLogicFile(ScriptSet &scriptSet, uint roomNumber, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs);
}
Commit: b8ee841aadce59f9437977330e8cd32565d1e480
https://github.com/scummvm/scummvm/commit/b8ee841aadce59f9437977330e8cd32565d1e480
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Add comment explaining why defs are room-linked
Changed paths:
engines/vcruise/script.cpp
diff --git a/engines/vcruise/script.cpp b/engines/vcruise/script.cpp
index 1d3314c474c..364098ecdbc 100644
--- a/engines/vcruise/script.cpp
+++ b/engines/vcruise/script.cpp
@@ -205,6 +205,12 @@ public:
Common::SharedPtr<Script> getFunction(uint fnIndex) const override;
private:
+ // Defs are linked to room numbers to deal with some weird variable sharing behavior. In Reah,
+ // variables are bound to room, but in Schizm they are clearly not. fnInitNewGame in Room01.log
+ // initializes a lot of things in various rooms, but some of those IDs also collide with other
+ // things. For example, dwNawigat2SZ is 13, and is read in Room20, but variable ID 13 is also
+ // used for dwStartSound47 in Room47 to determine if sounds have started, and it is expected
+ // to be initially 0 there.
struct Def {
Def();
Commit: 5fe99ebc532a906e8b61aeca9cb6a45cea820cd0
https://github.com/scummvm/scummvm/commit/5fe99ebc532a906e8b61aeca9cb6a45cea820cd0
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Cut the volume of everything in half to fix integer overflows where one of the speaker volumes would be >255.
Changed paths:
engines/vcruise/runtime.cpp
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 296d69d20e1..adf2a769e84 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -1029,7 +1029,7 @@ Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &roo
_menuInterface.reset(new RuntimeMenuInterface(this));
for (int32 i = 0; i < 49; i++)
- _dbToVolume[i] = decibelsToLinear(i - 49, Audio::Mixer::kMaxChannelVolume, Audio::Mixer::kMaxChannelVolume);
+ _dbToVolume[i] = decibelsToLinear(i - 49, Audio::Mixer::kMaxChannelVolume / 2, Audio::Mixer::kMaxChannelVolume / 2);
}
Runtime::~Runtime() {
@@ -3766,7 +3766,7 @@ uint Runtime::applyVolumeScale(int32 volume) const {
else if (volume < 0)
return 0;
- return volume * Audio::Mixer::kMaxChannelVolume / 100;
+ return volume * Audio::Mixer::kMaxChannelVolume / 200;
}
}
@@ -6419,9 +6419,9 @@ void Runtime::scriptOpSndPlay3D(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], true, soundID, cachedSound);
SoundParams3D sndParams;
- sndParams.unknownRange = sndParamArgs[2];
- sndParams.minRange = sndParamArgs[3];
- sndParams.maxRange = sndParamArgs[4];
+ sndParams.minRange = sndParamArgs[2];
+ sndParams.maxRange = sndParamArgs[3];
+ sndParams.unknownRange = sndParamArgs[4]; // Doesn't appear to be the same thing as Reah. Usually 1000, sometimes 2000 or 3000.
if (cachedSound) {
setSound3DParameters(*cachedSound, sndParamArgs[0], sndParamArgs[1], sndParams);
Commit: 388b418b0b6a541f3eca10e8d88283f33728841a
https://github.com/scummvm/scummvm/commit/388b418b0b6a541f3eca10e8d88283f33728841a
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Clear idle animations immediately when executing screen-transitioning ops to prevent them from playing during delays. This should still permit them if they're set after executing the op.
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index adf2a769e84..92f96db8125 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -2756,6 +2756,8 @@ void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
_havePanDownFromDirection[i] = false;
}
+ clearIdleAnimations();
+
for (uint i = 0; i < kNumDirections; i++)
_haveIdleAnimations[i] = false;
@@ -2768,6 +2770,16 @@ void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
}
}
+void Runtime::clearIdleAnimations() {
+ for (uint i = 0; i < kNumDirections; i++)
+ _haveIdleAnimations[i] = false;
+
+ _havePendingPreIdleActions = true;
+ _haveIdleStaticAnimation = false;
+ _idleCurrentStaticAnimation.clear();
+ _havePendingPlayAmbientSounds = true;
+}
+
void Runtime::changeHero() {
recordSaveGameSnapshot();
@@ -2776,7 +2788,8 @@ void Runtime::changeHero() {
if (_swapOutRoom && _swapOutScreen) {
// Some scripts may kick the player out to another location on swap back,
- // such as the elevator in the first area on Hannah's quest
+ // such as the elevator in the first area on Hannah's quest. This is done
+ // via the "heroOut" op.
currentState->roomNumber = _swapOutRoom;
currentState->screenNumber = _swapOutScreen;
currentState->direction = _direction;
@@ -2787,6 +2800,8 @@ void Runtime::changeHero() {
_saveGame->hero ^= 1u;
+ changeToCursor(_cursors[kCursorArrow]);
+
restoreSaveGameSnapshot();
}
@@ -3186,7 +3201,7 @@ void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bo
}
void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride, const Fraction &defaultFrameRate) {
- debug("changeAnimation: %u -> %u Initial %u", animDef.firstFrame, animDef.lastFrame, initialFrame);
+ debug("changeAnimation: Anim: %u Range: %u -> %u Initial %u", animDef.animNum, animDef.firstFrame, animDef.lastFrame, initialFrame);
_animPlaylist.reset();
@@ -5214,6 +5229,7 @@ void Runtime::scriptOpAnimF(ScriptArg_t arg) {
_screenNumber = stackArgs[kAnimDefStackArgs + 0];
_direction = stackArgs[kAnimDefStackArgs + 1];
_havePendingScreenChange = true;
+ clearIdleAnimations();
uint cursorID = kCursorArrow;
if (_scriptEnv.panInteractionID == kPanUpInteraction)
@@ -5288,6 +5304,8 @@ void Runtime::scriptOpAnim(ScriptArg_t arg) {
_direction = stackArgs[kAnimDefStackArgs + 1];
_havePendingScreenChange = true;
+ clearIdleAnimations();
+
if (_loadedAnimationHasSound)
changeToCursor(nullptr);
else {
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index f2ba28ecb81..e188acc8cf9 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -786,6 +786,7 @@ private:
void resolveSoundByNameOrID(const StackValue &stackValue, bool load, StackInt_t &outSoundID, SoundInstance *&outWave);
void changeToScreen(uint roomNumber, uint screenNumber);
+ void clearIdleAnimations();
void changeHero();
void triggerPreIdleActions();
void returnToIdleState();
Commit: 40649e30076a67e409ccf45b4ca66e3984b889d5
https://github.com/scummvm/scummvm/commit/40649e30076a67e409ccf45b4ca66e3984b889d5
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Add heroGetPos, heroSetPos, BitAnd, BitOr, Mul, Div, and Mod opcodes
Changed paths:
engines/vcruise/runtime.cpp
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 92f96db8125..b66837b4d37 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -6578,8 +6578,62 @@ void Runtime::scriptOpHeroOut(ScriptArg_t arg) {
_swapOutDirection = stackArgs[2];
}
-OPCODE_STUB(HeroGetPos)
-OPCODE_STUB(HeroSetPos)
+void Runtime::scriptOpHeroGetPos(ScriptArg_t arg) {
+ TAKE_STACK_INT(1);
+
+ bool thisHero = false;
+ switch (stackArgs[0]) {
+ case 0:
+ thisHero = (_hero == 0);
+ break;
+ case 1:
+ thisHero = (_hero == 1);
+ break;
+ case 2:
+ thisHero = false;
+ break;
+ default:
+ error("Unhandled heroGetPos argument %i", static_cast<int>(stackArgs[0]));
+ return;
+ }
+
+ uint roomNumber = thisHero ? _roomNumber : _altState->roomNumber;
+ uint screenNumber = thisHero ? _screenNumber : _altState->screenNumber;
+ uint direction = thisHero ? _direction : _altState->direction;
+
+ uint combined = (roomNumber << 16) | (screenNumber << 8) | direction;
+
+ _scriptStack.push_back(StackValue(static_cast<StackInt_t>(combined)));
+}
+
+void Runtime::scriptOpHeroSetPos(ScriptArg_t arg) {
+ TAKE_STACK_INT(2);
+
+ bool thisHero = false;
+ switch (stackArgs[0]) {
+ case 0:
+ thisHero = (_hero == 0);
+ break;
+ case 1:
+ thisHero = (_hero == 1);
+ break;
+ case 2:
+ thisHero = false;
+ break;
+ default:
+ error("Unhandled heroGetPos argument %i", static_cast<int>(stackArgs[0]));
+ return;
+ }
+
+ if (!thisHero) {
+ error("heroSetPos for the current hero isn't supported (and Schizm's game scripts shouldn't be doing it).");
+ return;
+ }
+
+ _altState->roomNumber = (stackArgs[1] >> 16) & 0xff;
+ _altState->screenNumber = (stackArgs[1] >> 8) & 0xff;
+ _altState->direction = stackArgs[1] & 0xff;
+}
void Runtime::scriptOpHeroGet(ScriptArg_t arg) {
_scriptStack.push_back(StackValue(_hero));
@@ -6587,8 +6641,18 @@ void Runtime::scriptOpHeroGet(ScriptArg_t arg) {
OPCODE_STUB(Garbage)
OPCODE_STUB(GetRoom)
-OPCODE_STUB(BitAnd)
-OPCODE_STUB(BitOr)
+
+void Runtime::scriptOpBitAnd(ScriptArg_t arg) {
+ TAKE_STACK_INT(2);
+
+ _scriptStack.push_back(StackValue(stackArgs[0] & stackArgs[1]));
+}
+
+void Runtime::scriptOpBitOr(ScriptArg_t arg) {
+ TAKE_STACK_INT(2);
+
+ _scriptStack.push_back(StackValue(stackArgs[0] | stackArgs[1]));
+}
void Runtime::scriptOpAngleGet(ScriptArg_t arg) {
_scriptStack.push_back(StackValue(_direction));
@@ -6605,9 +6669,35 @@ void Runtime::scriptOpIsCDVersion(ScriptArg_t arg) {
OPCODE_STUB(Disc)
OPCODE_STUB(HidePanel)
OPCODE_STUB(RotateUpdate)
-OPCODE_STUB(Mul)
-OPCODE_STUB(Div)
-OPCODE_STUB(Mod)
+
+void Runtime::scriptOpMul(ScriptArg_t arg) {
+ TAKE_STACK_INT(2);
+
+ _scriptStack.push_back(StackValue(stackArgs[0] * stackArgs[1]));
+}
+
+void Runtime::scriptOpDiv(ScriptArg_t arg) {
+ TAKE_STACK_INT(2);
+
+ if (stackArgs[1] == 0) {
+ error("Division by zero");
+ return;
+ }
+
+ _scriptStack.push_back(StackValue(stackArgs[0] / stackArgs[1]));
+}
+
+void Runtime::scriptOpMod(ScriptArg_t arg) {
+ TAKE_STACK_INT(2);
+
+ if (stackArgs[1] == 0) {
+ error("Division by zero");
+ return;
+ }
+
+ _scriptStack.push_back(StackValue(stackArgs[0] % stackArgs[1]));
+}
+
OPCODE_STUB(CyfraGet)
OPCODE_STUB(PuzzleInit)
OPCODE_STUB(PuzzleCanPress)
Commit: 9ef7085bff31e4abae53f87de5af9f7e2d24ba3a
https://github.com/scummvm/scummvm/commit/9ef7085bff31e4abae53f87de5af9f7e2d24ba3a
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Fix ItemSelect! op, implement proper animChange op behavior.
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
engines/vcruise/script.cpp
engines/vcruise/script.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index b66837b4d37..25a088518ca 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -186,7 +186,8 @@ const MapScreenDirectionDef *MapDef::getScreenDirection(uint screen, uint direct
return screenDirections[screen][direction].get();
}
-ScriptEnvironmentVars::ScriptEnvironmentVars() : lmb(false), lmbDrag(false), esc(false), exitToMenu(false), panInteractionID(0), fpsOverride(0), lastHighlightedItem(0) {
+ScriptEnvironmentVars::ScriptEnvironmentVars() : lmb(false), lmbDrag(false), esc(false), exitToMenu(false), animChangeSet(false),
+ panInteractionID(0), fpsOverride(0), lastHighlightedItem(0), animChangeFrameOffset(0), animChangeNumFrames(0) {
}
OSEvent::OSEvent() : type(kOSEventTypeInvalid), keyCode(static_cast<Common::KeyCode>(0)), keymappedEvent(kKeymappedEventNone), timestamp(0) {
@@ -2155,6 +2156,7 @@ bool Runtime::runScript() {
DISPATCH_OP(PuzzleDone);
DISPATCH_OP(PuzzleWhoWon);
DISPATCH_OP(Fn);
+ DISPATCH_OP(ItemHighlightSetTrue);
default:
error("Unimplemented opcode %i", static_cast<int>(instr.op));
@@ -2166,6 +2168,15 @@ bool Runtime::runScript() {
#undef DISPATCH_OP
+bool Runtime::requireAvailableStack(uint n) {
+ if (_scriptStack.size() < n) {
+ error("Script stack underflow");
+ return false;
+ }
+
+ return true;
+}
+
void Runtime::terminateScript() {
_scriptCallStack.clear();
@@ -3801,6 +3812,22 @@ AnimationDef Runtime::stackArgsToAnimDef(const StackInt_t *args) const {
return def;
}
+void Runtime::adjustUsingAnimChange(AnimationDef &animDef) const {
+ if (_scriptEnv.animChangeSet) {
+ uint origFirstFrame = animDef.firstFrame;
+ uint origLastFrame = animDef.lastFrame;
+
+ uint newFirstFrame = origFirstFrame + _scriptEnv.animChangeFrameOffset;
+ uint newLastFrame = newFirstFrame + _scriptEnv.animChangeNumFrames;
+
+ if (newLastFrame > origLastFrame || newFirstFrame > origLastFrame)
+ warning("animChange ops overran the original animation bounds");
+
+ animDef.firstFrame = newFirstFrame;
+ animDef.lastFrame = newLastFrame;
+ }
+}
+
void Runtime::pushAnimDef(const AnimationDef &animDef) {
_scriptStack.push_back(StackValue(animDef.animNum));
_scriptStack.push_back(StackValue(animDef.firstFrame));
@@ -5028,21 +5055,17 @@ LoadGameOutcome Runtime::loadGame(Common::ReadStream *stream) {
#endif
#define PEEK_STACK(n) \
- if (this->_scriptStack.size() < (n)) { \
- error("Script stack underflow"); \
+ if (!requireAvailableStack(n)) \
return; \
- } \
const StackValue *stackArgs = &this->_scriptStack[this->_scriptStack.size() - (n)]
#define TAKE_STACK_INT_NAMED(n, arrayName) \
StackInt_t arrayName[n]; \
do { \
- const uint stackSize = _scriptStack.size(); \
- if (stackSize < (n)) { \
- error("Script stack underflow"); \
+ if (!requireAvailableStack(n)) \
return; \
- } \
+ const uint stackSize = _scriptStack.size(); \
const StackValue *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
for (uint i = 0; i < (n); i++) { \
if (stackArgsPtr[i].type != StackValue::kNumber) \
@@ -5057,11 +5080,9 @@ LoadGameOutcome Runtime::loadGame(Common::ReadStream *stream) {
#define TAKE_STACK_STR_NAMED(n, arrayName) \
Common::String arrayName[n]; \
do { \
- const uint stackSize = _scriptStack.size(); \
- if (stackSize < (n)) { \
- error("Script stack underflow"); \
+ if (!requireAvailableStack(n)) \
return; \
- } \
+ const uint stackSize = _scriptStack.size(); \
const StackValue *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
for (uint i = 0; i < (n); i++) { \
if (stackArgsPtr[i].type != StackValue::kString) \
@@ -5076,11 +5097,9 @@ LoadGameOutcome Runtime::loadGame(Common::ReadStream *stream) {
#define TAKE_STACK_VAR_NAMED(n, arrayName) \
StackValue arrayName[n]; \
do { \
- const uint stackSize = _scriptStack.size(); \
- if (stackSize < (n)) { \
- error("Script stack underflow"); \
+ if (!requireAvailableStack(n)) \
return; \
- } \
+ const uint stackSize = _scriptStack.size(); \
const StackValue *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
for (uint i = 0; i < (n); i++) \
arrayName[i] = Common::move(stackArgsPtr[i]); \
@@ -5282,6 +5301,8 @@ void Runtime::scriptOpAnimS(ScriptArg_t arg) {
AnimationDef animDef = stackArgsToAnimDef(stackArgs + 0);
+ adjustUsingAnimChange(animDef);
+
// Static animations start on the last frame
changeAnimation(animDef, animDef.lastFrame, false);
@@ -5297,6 +5318,9 @@ void Runtime::scriptOpAnim(ScriptArg_t arg) {
TAKE_STACK_INT(kAnimDefStackArgs + 2);
AnimationDef animDef = stackArgsToAnimDef(stackArgs + 0);
+
+ adjustUsingAnimChange(animDef);
+
changeAnimation(animDef, animDef.firstFrame, true, _animSpeedDefault);
_gameState = kGameStateWaitingForAnimation;
@@ -5452,6 +5476,20 @@ void Runtime::scriptOpItemHighlightSet(ScriptArg_t arg) {
}
}
+void Runtime::scriptOpItemHighlightSetTrue(ScriptArg_t arg) {
+ TAKE_STACK_INT(1);
+
+ for (uint slot = 0; slot < kNumInventorySlots; slot++) {
+ InventoryItem &item = _inventory[slot];
+
+ if (item.itemID == static_cast<uint>(stackArgs[0])) {
+ item.highlighted = true;
+ drawInventory(slot);
+ break;
+ }
+ }
+}
+
void Runtime::scriptOpItemAdd(ScriptArg_t arg) {
TAKE_STACK_INT(1);
@@ -6487,10 +6525,12 @@ void Runtime::scriptOpAnimVolume(ScriptArg_t arg) {
void Runtime::scriptOpAnimChange(ScriptArg_t arg) {
TAKE_STACK_INT(2);
- (void)stackArgs;
+ if (stackArgs[1] == 0)
+ error("animChange frame count shouldn't be zero");
- // Not sure what this does yet. It is parameterized in some rooms.
- warning("animChange opcode isn't implemented yet");
+ _scriptEnv.animChangeSet = true;
+ _scriptEnv.animChangeFrameOffset = stackArgs[0];
+ _scriptEnv.animChangeNumFrames = stackArgs[1] - 1;
}
void Runtime::scriptOpScreenName(ScriptArg_t arg) {
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index e188acc8cf9..911034ac7ed 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -153,10 +153,13 @@ struct ScriptEnvironmentVars {
uint panInteractionID;
uint fpsOverride;
uint lastHighlightedItem;
+ uint animChangeFrameOffset;
+ uint animChangeNumFrames;
bool lmb;
bool lmbDrag;
bool esc;
bool exitToMenu;
+ bool animChangeSet;
};
struct SfxSound {
@@ -756,6 +759,7 @@ private:
bool runDelay();
bool runHorizontalPan(bool isRight);
bool runScript();
+ bool requireAvailableStack(uint n);
bool runWaitForAnimation();
bool runWaitForFacing();
bool runWaitForFacingToAnim();
@@ -824,6 +828,7 @@ private:
void stopSubtitles();
AnimationDef stackArgsToAnimDef(const StackInt_t *args) const;
+ void adjustUsingAnimChange(AnimationDef &animDef) const;
void pushAnimDef(const AnimationDef &animDef);
void activateScript(const Common::SharedPtr<Script> &script, const ScriptEnvironmentVars &envVars);
@@ -1047,6 +1052,7 @@ private:
void scriptOpPuzzleDone(ScriptArg_t arg);
void scriptOpPuzzleWhoWon(ScriptArg_t arg);
void scriptOpFn(ScriptArg_t arg);
+ void scriptOpItemHighlightSetTrue(ScriptArg_t arg);
Common::Array<Common::SharedPtr<Graphics::WinCursorGroup> > _cursors; // Cursors indexed as CURSOR_CUR_##
Common::Array<Common::SharedPtr<Graphics::WinCursorGroup> > _cursorsShort; // Cursors indexed as CURSOR_#
diff --git a/engines/vcruise/script.cpp b/engines/vcruise/script.cpp
index 364098ecdbc..9c4389dfc4f 100644
--- a/engines/vcruise/script.cpp
+++ b/engines/vcruise/script.cpp
@@ -761,7 +761,7 @@ static ScriptNamedInstruction g_schizmNamedInstructions[] = {
{"save0", ProtoOp::kProtoOpNoop, ScriptOps::kSave0},
{"hidePanel", ProtoOp::kProtoOpNoop, ScriptOps::kHidePanel},
{"ItemExist@", ProtoOp::kProtoOpScript, ScriptOps::kItemCheck},
- {"ItemSelect!", ProtoOp::kProtoOpScript, ScriptOps::kItemHighlightSet},
+ {"ItemSelect!", ProtoOp::kProtoOpScript, ScriptOps::kItemHighlightSetTrue},
{"ItemPlace@", ProtoOp::kProtoOpScript, ScriptOps::kItemHaveSpace},
{"ItemPutInto!", ProtoOp::kProtoOpScript, ScriptOps::kItemAdd},
{"ItemRemove!", ProtoOp::kProtoOpScript, ScriptOps::kItemRemove},
diff --git a/engines/vcruise/script.h b/engines/vcruise/script.h
index b05a0db7fd3..c94d10020c0 100644
--- a/engines/vcruise/script.h
+++ b/engines/vcruise/script.h
@@ -216,6 +216,7 @@ enum ScriptOp {
kPuzzleDone,
kPuzzleWhoWon,
kFn,
+ kItemHighlightSetTrue,
kNumOps,
};
Commit: 8dd26f9daacd17753c04bd8ec2275c11e9bf1b99
https://github.com/scummvm/scummvm/commit/8dd26f9daacd17753c04bd8ec2275c11e9bf1b99
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Fix up volume behavior, add some more sound ops for Schizm, fix parm op parsing.
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
engines/vcruise/script.cpp
engines/vcruise/script.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 25a088518ca..71d67fa3ff2 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -1896,11 +1896,11 @@ void Runtime::continuePlayingAnimation(bool loop, bool useStopFrame, bool &outAn
VCruise::AudioPlayer &audioPlayer = *playlistEntry.sample->audioPlayer;
if (playlistEntry.isUpdate) {
- audioPlayer.setVolumeAndBalance(applyVolumeScale(playlistEntry.volume), playlistEntry.balance);
+ audioPlayer.setVolumeAndBalance(applyVolumeScale(playlistEntry.volume), applyBalanceScale(playlistEntry.balance));
} else {
audioPlayer.stop();
playlistEntry.sample->audioStream->seek(0);
- audioPlayer.play(applyVolumeScale(playlistEntry.volume), playlistEntry.balance);
+ audioPlayer.play(applyVolumeScale(playlistEntry.volume), applyBalanceScale(playlistEntry.balance));
}
// No break, it's possible for there to be multiple sounds in the same frame
@@ -3648,7 +3648,7 @@ void Runtime::update3DSounds() {
bool Runtime::computeEffectiveVolumeAndBalance(SoundInstance &snd) {
uint effectiveVolume = applyVolumeScale(snd.volume);
- int32 effectiveBalance = snd.balance;
+ int32 effectiveBalance = applyBalanceScale(snd.balance);
double radians = Common::deg2rad<double>(_listenerAngle);
int32 cosAngle = static_cast<int32>(cos(radians) * (1 << 15));
@@ -3796,6 +3796,19 @@ uint Runtime::applyVolumeScale(int32 volume) const {
}
}
+int Runtime::applyBalanceScale(int32 balance) const {
+ if (balance < -100)
+ balance = -100;
+ else if (balance > 100)
+ balance = 100;
+
+ // Avoid undefined divide rounding behavior, round toward zero
+ if (balance < 0)
+ return -((-balance) * 127 / 100);
+ else
+ return balance * 127 / 100;
+}
+
AnimationDef Runtime::stackArgsToAnimDef(const StackInt_t *args) const {
AnimationDef def;
def.animNum = args[0];
@@ -6362,8 +6375,22 @@ void Runtime::scriptOpAnimName(ScriptArg_t arg) {
Common::HashMap<Common::String, AnimationDef>::const_iterator it = roomDef->animations.find(_scriptSet->strings[arg]);
- if (it == roomDef->animations.end())
- error("Can't resolve animation for room, couldn't find animation '%s'", _scriptSet->strings[arg].c_str());
+ if (it == roomDef->animations.end()) {
+ bool found = false;
+ for (const Common::SharedPtr<RoomDef> &altRoomDef : _roomDefs) {
+ it = altRoomDef->animations.find(_scriptSet->strings[arg]);
+
+ // Hack to fix PortR_Zwierz_morph being in the wrong room
+ if (it != altRoomDef->animations.end()) {
+ warning("Couldn't resolve animation '%s' in its normal room, but found it in another one, this may cause problems", _scriptSet->strings[arg].c_str());
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ error("Can't resolve animation for room, couldn't find animation '%s'", _scriptSet->strings[arg].c_str());
+ }
pushAnimDef(it->_value);
}
@@ -6464,7 +6491,17 @@ void Runtime::scriptOpSndPlay(ScriptArg_t arg) {
triggerSound(true, *cachedSound, getSilentSoundVolume(), 0, false, false);
}
-OPCODE_STUB(SndPlayEx)
+void Runtime::scriptOpSndPlayEx(ScriptArg_t arg) {
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ SoundInstance *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], true, soundID, cachedSound);
+
+ if (cachedSound)
+ triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], false, false);
+}
void Runtime::scriptOpSndPlay3D(ScriptArg_t arg) {
TAKE_STACK_INT_NAMED(5, sndParamArgs);
@@ -6499,7 +6536,16 @@ void Runtime::scriptOpSndWait(ScriptArg_t arg) {
OPCODE_STUB(SndHalt)
OPCODE_STUB(SndToBack)
-OPCODE_STUB(SndStop)
+
+void Runtime::scriptOpSndStop(ScriptArg_t arg) {
+ TAKE_STACK_INT(1);
+
+ SoundInstance *cachedSound = resolveSoundByID(stackArgs[0]);
+
+ if (cachedSound)
+ stopSound(*cachedSound);
+}
+
OPCODE_STUB(SndStopAll)
OPCODE_STUB(SndAddRandom)
OPCODE_STUB(SndClearRandom)
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 911034ac7ed..d311760de10 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -823,6 +823,7 @@ private:
int32 getSilentSoundVolume() const;
int32 getDefaultSoundVolume() const;
uint applyVolumeScale(int32 volume) const;
+ int applyBalanceScale(int32 balance) const;
void triggerWaveSubtitles(const SoundInstance &sound, const Common::String &id);
void stopSubtitles();
diff --git a/engines/vcruise/script.cpp b/engines/vcruise/script.cpp
index 9c4389dfc4f..ecb7b840675 100644
--- a/engines/vcruise/script.cpp
+++ b/engines/vcruise/script.cpp
@@ -370,8 +370,10 @@ void ScriptCompiler::compileScriptSet(ScriptSet *ss) {
uint32 roomNumber = 0;
if (_parser.parseToken(token, state)) {
- // Many Schizm rooms use 0xxh as the room number and are empty. In this case the room is discarded.
- if (_dialect != kScriptDialectSchizm || token != "0xxh") {
+ // Many Schizm rooms use 0xxh as the room number and are empty. In this case it's supposed to use a previous valid room.
+ if (_dialect == kScriptDialectSchizm && token == "0xxh") {
+ ss->isAutoGenFromPrevious = true;
+ } else {
if (!parseNumber(token, roomNumber))
error("Error compiling script at line %i col %i: Expected number but found '%s'", static_cast<int>(state._lineNum), static_cast<int>(state._col), token.c_str());
@@ -773,6 +775,10 @@ static ScriptNamedInstruction g_schizmNamedInstructions[] = {
{"puzzleDone", ProtoOp::kProtoOpScript, ScriptOps::kPuzzleDone},
{"puzzleWhoWon", ProtoOp::kProtoOpScript, ScriptOps::kPuzzleWhoWon},
{"fn", ProtoOp::kProtoOpScript, ScriptOps::kFn},
+ {"parm1", ProtoOp::kProtoOpScript, ScriptOps::kParm1},
+ {"parm2", ProtoOp::kProtoOpScript, ScriptOps::kParm2},
+ {"parm3", ProtoOp::kProtoOpScript, ScriptOps::kParm3},
+ {"parmG", ProtoOp::kProtoOpScript, ScriptOps::kParmG},
{"+", ProtoOp::kProtoOpScript, ScriptOps::kAdd},
{"-", ProtoOp::kProtoOpScript, ScriptOps::kSub},
@@ -965,12 +971,15 @@ bool ScriptCompiler::compileInstructionToken(ProtoScript &script, const Common::
return true;
}
+ // Disabled since Room02 is a cheat room and so not needed.
+#if 0
// HACK: Work around broken volume variable names in Room02. Some of these appear to have "par"
// where it should be "vol" but some are garbage. Figure this out later.
if (token.hasPrefix("par")) {
script.instrs.push_back(ProtoInstruction(kProtoOpScript, ScriptOps::kGarbage, indexString(token)));
return true;
}
+#endif
}
return false;
@@ -1340,6 +1349,9 @@ Common::SharedPtr<Script> ScriptCompilerGlobalState::getFunction(uint fnIndex) c
return _functions[fnIndex];
}
+ScriptSet::ScriptSet() : isAutoGenFromPrevious(false) {
+}
+
IScriptCompilerGlobalState::~IScriptCompilerGlobalState() {
}
diff --git a/engines/vcruise/script.h b/engines/vcruise/script.h
index c94d10020c0..b47498345c2 100644
--- a/engines/vcruise/script.h
+++ b/engines/vcruise/script.h
@@ -252,11 +252,15 @@ struct RoomScriptSet {
};
struct ScriptSet {
+ ScriptSet();
+
RoomScriptSetMap_t roomScripts;
Common::Array<Common::SharedPtr<Script> > functions;
Common::Array<Common::String> functionNames;
Common::Array<Common::String> strings;
+
+ bool isAutoGenFromPrevious;
};
struct FunctionDef {
Commit: 24429f741792547049431d990bc93c19b3765e2f
https://github.com/scummvm/scummvm/commit/24429f741792547049431d990bc93c19b3765e2f
Author: elasota (ejlasota at gmail.com)
Date: 2023-05-15T20:47:27-04:00
Commit Message:
VCRUISE: Rework a bunch of things to fix up Schizm's duplicate room behavior.
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
engines/vcruise/script.cpp
engines/vcruise/script.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 71d67fa3ff2..159e9c957bb 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -1209,6 +1209,15 @@ bool Runtime::bootGame(bool newGame) {
loadScore();
debug(1, "Score loaded OK");
+
+ // Duplicate rooms must be identified in advance because they can take effect before the room logic is loaded.
+ // For example, in room 37, when taking the hanging lift across, the room is changed to room 28 and then
+ // animation PortD_Zwierz_morph is used, is an animation mapped to room 25, but we can't know that room 28 is
+ // a duplicate of room 25 unless we check the logic file for rooms 26-28. Additionally, we can't just scan
+ // downward for missing animations elsewhere because PRZYCUMIE_KRZESELKO is mapped to animations 25 and 26,
+ // but the frame range for 27 and 28 is supposed to use room 25 (the root of the duplication), not 26.
+ loadDuplicateRooms();
+ debug(1, "Duplicated rooms identified OK");
} else {
StartConfigDef &startConfig = _startConfigs[kStartConfigInitial];
startConfig.disc = 1;
@@ -2551,6 +2560,38 @@ void Runtime::loadScore() {
warning("Couldn't load music score");
}
+void Runtime::loadDuplicateRooms() {
+ assert(_gameID == GID_SCHIZM);
+
+ Common::ArchiveMemberList logics;
+ SearchMan.listMatchingMembers(logics, "Log/Room##.log", true);
+
+ for (const Common::ArchiveMemberPtr &logic : logics) {
+ Common::String name = logic->getName();
+
+ char d10 = name[4];
+ char d1 = name[5];
+
+ uint roomNumber = (d10 - '0') * 10 + (d1 - '0');
+
+ Common::SharedPtr<Common::SeekableReadStream> stream(logic->createReadStream());
+ if (stream) {
+ if (checkSchizmLogicForDuplicatedRoom(*stream, stream->size())) {
+ if (_roomDuplicationOffsets.size() < roomNumber)
+ _roomDuplicationOffsets.resize(roomNumber + 1);
+ _roomDuplicationOffsets[roomNumber] = 1;
+ }
+ } else {
+ warning("Logic for room %u couldn't be checked for duplication");
+ }
+ }
+
+ for (uint i = 1; i < _roomDuplicationOffsets.size(); i++) {
+ if (_roomDuplicationOffsets[i])
+ _roomDuplicationOffsets[i] += _roomDuplicationOffsets[i - 1];
+ }
+}
+
Common::SharedPtr<SoundInstance> Runtime::loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr) {
for (const Common::SharedPtr<SoundInstance> &activeSound : _activeSounds) {
if (activeSound->name == soundName)
@@ -3889,12 +3930,18 @@ void Runtime::compileSchizmLogicSet(const uint *roomNumbers, uint numRooms) {
Common::SharedPtr<ScriptSet> scriptSet(new ScriptSet());
for (uint i = 0; i < numRooms; i++) {
- Common::String logicFileName = Common::String::format("Log/Room%02u.log", roomNumbers[i]);
+ uint roomNumber = roomNumbers[i];
+ uint roomFile = roomNumber;
+
+ if (roomNumber < _roomDuplicationOffsets.size())
+ roomFile -= _roomDuplicationOffsets[roomNumber];
+
+ Common::String logicFileName = Common::String::format("Log/Room%02u.log", roomFile);
Common::File logicFile;
if (logicFile.open(logicFileName)) {
debug(1, "Compiling script %s...", logicFileName.c_str());
- compileSchizmLogicFile(*scriptSet, roomNumbers[i], logicFile, static_cast<uint>(logicFile.size()), logicFileName, gs.get());
+ compileSchizmLogicFile(*scriptSet, roomNumber, roomFile, logicFile, static_cast<uint>(logicFile.size()), logicFileName, gs.get());
logicFile.close();
}
}
@@ -6369,30 +6416,52 @@ void Runtime::scriptOpAnimName(ScriptArg_t arg) {
if (_roomNumber >= _roomDefs.size())
error("Can't resolve animation for room, room number was invalid");
+ Common::String &animName = _scriptSet->strings[arg];
+
+ // In Reah, animations are mapped to rooms.
+ //
+ // In Schizm this can get very complicated: It supports overlapping room logics which in some cases
+ // have animation ranges mapped to a different animation.
+ //
+ // For example, in Schizm, rooms 25-28 all share one logic file and their corresponding animations are
+ // largely duplicates of each other with different skies.
+ //
+ // It appears that the animation to select is based on the remapped room if the animation can't be
+ // found in its primary room.
+ //
+ // For example, PRZYCUMIE_KRZESELKO is mapped to an animation range in room 25 and another range in
+ // room 26, and there is no mapping for room 28. In this case, the animation frame range from room 25
+ // is used, but it is remapped to animation 28.
Common::SharedPtr<RoomDef> roomDef = _roomDefs[_roomNumber];
- if (!roomDef)
- error("Can't resolve animation for room, room number was invalid");
+ if (roomDef) {
+ Common::HashMap<Common::String, AnimationDef>::const_iterator it = roomDef->animations.find(animName);
+ if (it != roomDef->animations.end()) {
+ pushAnimDef(it->_value);
+ return;
+ }
+ }
+ if (_roomNumber < _roomDuplicationOffsets.size() && _roomDuplicationOffsets[_roomNumber] != 0) {
+ int roomToUse = _roomNumber - _roomDuplicationOffsets[_roomNumber];
- Common::HashMap<Common::String, AnimationDef>::const_iterator it = roomDef->animations.find(_scriptSet->strings[arg]);
- if (it == roomDef->animations.end()) {
- bool found = false;
- for (const Common::SharedPtr<RoomDef> &altRoomDef : _roomDefs) {
- it = altRoomDef->animations.find(_scriptSet->strings[arg]);
+ roomDef = _roomDefs[roomToUse];
- // Hack to fix PortR_Zwierz_morph being in the wrong room
- if (it != altRoomDef->animations.end()) {
- warning("Couldn't resolve animation '%s' in its normal room, but found it in another one, this may cause problems", _scriptSet->strings[arg].c_str());
- found = true;
- break;
- }
- }
+ Common::HashMap<Common::String, AnimationDef>::const_iterator it = roomDef->animations.find(animName);
+ if (it != roomDef->animations.end()) {
+ AnimationDef animDef = it->_value;
- if (!found)
- error("Can't resolve animation for room, couldn't find animation '%s'", _scriptSet->strings[arg].c_str());
+ if (animDef.animNum == roomToUse)
+ animDef.animNum = _roomNumber;
+ else if (animDef.animNum == -roomToUse)
+ animDef.animNum = -static_cast<int>(_roomNumber);
+
+ pushAnimDef(animDef);
+ return;
+ }
}
- pushAnimDef(it->_value);
+
+ error("Can't resolve animation for room, couldn't find animation '%s'", animName.c_str());
}
void Runtime::scriptOpValueName(ScriptArg_t arg) {
@@ -6726,7 +6795,10 @@ void Runtime::scriptOpHeroGet(ScriptArg_t arg) {
}
OPCODE_STUB(Garbage)
-OPCODE_STUB(GetRoom)
+
+void Runtime::scriptOpGetRoom(ScriptArg_t arg) {
+ _scriptStack.push_back(StackValue(_roomNumber));
+}
void Runtime::scriptOpBitAnd(ScriptArg_t arg) {
TAKE_STACK_INT(2);
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index d311760de10..ca095520852 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -783,6 +783,7 @@ private:
void findWaves();
void loadConfig(const char *cfgPath);
void loadScore();
+ void loadDuplicateRooms();
Common::SharedPtr<SoundInstance> loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr);
SoundCache *loadCache(SoundInstance &sound);
void resolveSoundByName(const Common::String &soundName, bool load, StackInt_t &outSoundID, SoundInstance *&outWave);
@@ -1137,6 +1138,7 @@ private:
VCruiseGameID _gameID;
Common::Array<Common::SharedPtr<RoomDef> > _roomDefs;
+ Common::Array<uint> _roomDuplicationOffsets;
Common::SharedPtr<ScriptSet> _scriptSet;
Common::Array<CallStackFrame> _scriptCallStack;
diff --git a/engines/vcruise/script.cpp b/engines/vcruise/script.cpp
index ecb7b840675..339cf219bd0 100644
--- a/engines/vcruise/script.cpp
+++ b/engines/vcruise/script.cpp
@@ -149,7 +149,7 @@ struct ScriptNamedInstruction {
class ScriptCompiler {
public:
- ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, uint roomNumber, IScriptCompilerGlobalState *gs);
+ ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, uint loadAsRoom, uint fileRoom, IScriptCompilerGlobalState *gs);
void compileScriptSet(ScriptSet *ss);
@@ -181,7 +181,8 @@ private:
const Common::String _blamePath;
ScriptDialect _dialect;
- uint _roomNumber;
+ uint _loadAsRoom;
+ uint _fileRoom;
const char *_scrToken;
const char *_eroomToken;
@@ -224,8 +225,8 @@ private:
Common::Array<Common::SharedPtr<Script> > _functions;
};
-ScriptCompiler::ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, uint roomNumber, IScriptCompilerGlobalState *gs)
- : _numberParsingMode(kNumberParsingHex), _parser(parser), _blamePath(blamePath), _dialect(dialect), _roomNumber(roomNumber), _gs(gs),
+ScriptCompiler::ScriptCompiler(TextParser &parser, const Common::String &blamePath, ScriptDialect dialect, uint loadAsRoom, uint fileRoom, IScriptCompilerGlobalState *gs)
+ : _numberParsingMode(kNumberParsingHex), _parser(parser), _blamePath(blamePath), _dialect(dialect), _loadAsRoom(loadAsRoom), _fileRoom(fileRoom), _gs(gs),
_scrToken(nullptr), _eroomToken(nullptr) {
}
@@ -370,15 +371,13 @@ void ScriptCompiler::compileScriptSet(ScriptSet *ss) {
uint32 roomNumber = 0;
if (_parser.parseToken(token, state)) {
- // Many Schizm rooms use 0xxh as the room number and are empty. In this case it's supposed to use a previous valid room.
- if (_dialect == kScriptDialectSchizm && token == "0xxh") {
- ss->isAutoGenFromPrevious = true;
- } else {
- if (!parseNumber(token, roomNumber))
- error("Error compiling script at line %i col %i: Expected number but found '%s'", static_cast<int>(state._lineNum), static_cast<int>(state._col), token.c_str());
-
- ss->roomScripts[roomNumber] = roomScript;
- }
+ if (!parseNumber(token, roomNumber))
+ error("Error compiling script at line %i col %i: Expected number but found '%s'", static_cast<int>(state._lineNum), static_cast<int>(state._col), token.c_str());
+
+ if (_dialect == kScriptDialectSchizm && roomNumber == _fileRoom)
+ roomNumber = _loadAsRoom;
+
+ ss->roomScripts[roomNumber] = roomScript;
} else {
error("Error compiling script at line %i col %i: Expected number", static_cast<int>(state._lineNum), static_cast<int>(state._col));
}
@@ -448,7 +447,8 @@ void ScriptCompiler::compileRoomScriptSet(RoomScriptSet *rss) {
if (isNegative)
signedNumber = -signedNumber;
- _gs->define(key, _roomNumber, signedNumber);
+ // TODO: Figure out if the vars should be scoped in _fileRoom or _loadAsRoom in the case of duplicate rooms
+ _gs->define(key, _fileRoom, signedNumber);
} else if (_dialect == kScriptDialectSchizm && token == "~Fun") {
Common::String fnName;
if (!_parser.parseToken(fnName, state))
@@ -1349,17 +1349,17 @@ Common::SharedPtr<Script> ScriptCompilerGlobalState::getFunction(uint fnIndex) c
return _functions[fnIndex];
}
-ScriptSet::ScriptSet() : isAutoGenFromPrevious(false) {
+ScriptSet::ScriptSet() {
}
IScriptCompilerGlobalState::~IScriptCompilerGlobalState() {
}
-static void compileLogicFile(ScriptSet &scriptSet, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, ScriptDialect dialect, uint roomNumber, IScriptCompilerGlobalState *gs) {
+static void compileLogicFile(ScriptSet &scriptSet, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, ScriptDialect dialect, uint loadAsRoom, uint fileRoom, IScriptCompilerGlobalState *gs) {
LogicUnscrambleStream unscrambleStream(&stream, streamSize);
TextParser parser(&unscrambleStream);
- ScriptCompiler compiler(parser, blamePath, dialect, roomNumber, gs);
+ ScriptCompiler compiler(parser, blamePath, dialect, loadAsRoom, fileRoom, gs);
compiler.compileScriptSet(&scriptSet);
}
@@ -1371,12 +1371,36 @@ Common::SharedPtr<IScriptCompilerGlobalState> createScriptCompilerGlobalState()
Common::SharedPtr<ScriptSet> compileReahLogicFile(Common::ReadStream &stream, uint streamSize, const Common::String &blamePath) {
Common::SharedPtr<ScriptSet> scriptSet(new ScriptSet());
- compileLogicFile(*scriptSet, stream, streamSize, blamePath, kScriptDialectReah, 0, nullptr);
+ compileLogicFile(*scriptSet, stream, streamSize, blamePath, kScriptDialectReah, 0, 0, nullptr);
return scriptSet;
}
-void compileSchizmLogicFile(ScriptSet &scriptSet, uint roomNumber, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs) {
- compileLogicFile(scriptSet, stream, streamSize, blamePath, kScriptDialectSchizm, roomNumber, gs);
+void compileSchizmLogicFile(ScriptSet &scriptSet, uint loadAsRoom, uint fileRoom, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs) {
+ compileLogicFile(scriptSet, stream, streamSize, blamePath, kScriptDialectSchizm, loadAsRoom, fileRoom, gs);
+}
+
+bool checkSchizmLogicForDuplicatedRoom(Common::ReadStream &stream, uint streamSize) {
+ LogicUnscrambleStream unscrambleStream(&stream, streamSize);
+ TextParser parser(&unscrambleStream);
+
+ TextParserState state;
+ Common::String token;
+
+ const char *expectedTokenSequence[] = {"~Room", "0xxh", "~ERoom"};
+
+ for (const char *tokenExpected : expectedTokenSequence) {
+ if (!parser.parseToken(token, state))
+ return false;
+
+ if (token != tokenExpected)
+ return false;
+ }
+
+ if (parser.parseToken(token, state))
+ return false;
+
+ return true;
}
+
} // namespace VCruise
diff --git a/engines/vcruise/script.h b/engines/vcruise/script.h
index b47498345c2..e146dc77caf 100644
--- a/engines/vcruise/script.h
+++ b/engines/vcruise/script.h
@@ -259,8 +259,6 @@ struct ScriptSet {
Common::Array<Common::SharedPtr<Script> > functions;
Common::Array<Common::String> functionNames;
Common::Array<Common::String> strings;
-
- bool isAutoGenFromPrevious;
};
struct FunctionDef {
@@ -286,7 +284,8 @@ struct IScriptCompilerGlobalState {
Common::SharedPtr<IScriptCompilerGlobalState> createScriptCompilerGlobalState();
Common::SharedPtr<ScriptSet> compileReahLogicFile(Common::ReadStream &stream, uint streamSize, const Common::String &blamePath);
-void compileSchizmLogicFile(ScriptSet &scriptSet, uint roomNumber, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs);
+void compileSchizmLogicFile(ScriptSet &scriptSet, uint loadAsRoom, uint fileRoom, Common::ReadStream &stream, uint streamSize, const Common::String &blamePath, IScriptCompilerGlobalState *gs);
+bool checkSchizmLogicForDuplicatedRoom(Common::ReadStream &stream, uint streamSize);
}
More information about the Scummvm-git-logs
mailing list