diff --git a/CHANGELOG.md b/CHANGELOG.md index a222ca1fb1..f804d04a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Bug #6993: Shooting your last round of ammunition causes the attack animation to cancel Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData Feature #6979: Add support of loading and displaying LOD assets purely based on their filename extension + Feature #6983: PCVisionBonus script functions 0.48.0 ------ diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index fc2ed4bbc5..af71ed416a 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -1036,6 +1036,19 @@ void removeMagicEffect(const MWWorld::Ptr& target, ActiveSpells::ActiveSpellPara case ESM::MagicEffect::DemoralizeHumanoid: modifyAiSetting(target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude, invalid); break; + case ESM::MagicEffect::NightEye: + { + const MWMechanics::EffectParam nightEye = magnitudes.get(effect.mEffectId); + if(nightEye.getMagnitude() < 0.f && nightEye.getBase() < 0) + { + // The PCVisionBonus functions are different from every other magic effect function in that they clamp the value to [0, 1]. + // Morrowind.exe applies the same clamping to the night-eye effect, which can create situations where an effect is still active + // (i.e. shown in the menu) but the screen is no longer bright. Modifying the base value here should prevent that while preserving their function. + float delta = std::clamp(-nightEye.getMagnitude(), 0.f, -static_cast(nightEye.getBase())); + magnitudes.modifyBase(effect.mEffectId, static_cast(delta)); + } + } + break; case ESM::MagicEffect::RallyCreature: case ESM::MagicEffect::RallyHumanoid: modifyAiSetting(target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, effect.mMagnitude, invalid); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index aaba5e5986..7197be48ba 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -481,5 +481,8 @@ op 0x200031e: GetDistance op 0x200031f: GetDistance, explicit op 0x2000320: Help op 0x2000321: ReloadLua +op 0x2000322: GetPCVisionBonus +op 0x2000323: SetPCVisionBonus +op 0x2000324: ModPCVisionBonus -opcodes 0x2000322-0x3ffffff unused +opcodes 0x2000325-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 7ce266111f..72f7304803 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1296,6 +1296,48 @@ namespace MWScript } }; + class OpGetPCVisionBonus : public Interpreter::Opcode0 + { + public: + void execute(Interpreter::Runtime& runtime) override + { + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::EffectParam nightEye = player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye); + runtime.push(std::clamp(nightEye.getMagnitude() / 100.f, 0.f, 1.f)); + } + }; + + class OpSetPCVisionBonus : public Interpreter::Opcode0 + { + public: + void execute(Interpreter::Runtime& runtime) override + { + float arg = runtime[0].mFloat; + runtime.pop(); + MWWorld::Ptr player = MWMechanics::getPlayer(); + auto& effects = player.getClass().getCreatureStats(player).getMagicEffects(); + float delta = std::clamp(arg * 100.f, 0.f, 100.f) - effects.get(ESM::MagicEffect::NightEye).getMagnitude(); + effects.modifyBase(ESM::MagicEffect::NightEye, static_cast(delta)); + } + }; + + class OpModPCVisionBonus : public Interpreter::Opcode0 + { + public: + void execute(Interpreter::Runtime& runtime) override + { + float arg = runtime[0].mFloat; + runtime.pop(); + MWWorld::Ptr player = MWMechanics::getPlayer(); + auto& effects = player.getClass().getCreatureStats(player).getMagicEffects(); + const MWMechanics::EffectParam nightEye = effects.get(ESM::MagicEffect::NightEye); + float newBase = std::clamp(nightEye.getMagnitude() + arg * 100.f, 0.f, 100.f); + newBase -= nightEye.getModifier(); + float delta = std::clamp(newBase, 0.f, 100.f) - nightEye.getMagnitude(); + effects.modifyBase(ESM::MagicEffect::NightEye, static_cast(delta)); + } + }; + struct MagicEffect { int mPositiveEffect; @@ -1474,6 +1516,10 @@ namespace MWScript interpreter.installSegment5>(Compiler::Stats::opcodeModMagicEffect + i, positive, negative); interpreter.installSegment5>(Compiler::Stats::opcodeModMagicEffectExplicit + i, positive, negative); } + + interpreter.installSegment5(Compiler::Stats::opcodeGetPCVisionBonus); + interpreter.installSegment5(Compiler::Stats::opcodeSetPCVisionBonus); + interpreter.installSegment5(Compiler::Stats::opcodeModPCVisionBonus); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 358683a397..af2a287c48 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -538,6 +538,10 @@ namespace Compiler extensions.registerInstruction("becomewerewolf", "", opcodeBecomeWerewolf, opcodeBecomeWerewolfExplicit); extensions.registerInstruction("undowerewolf", "", opcodeUndoWerewolf, opcodeUndoWerewolfExplicit); extensions.registerInstruction("setwerewolfacrobatics", "", opcodeSetWerewolfAcrobatics, opcodeSetWerewolfAcrobaticsExplicit); + + extensions.registerFunction("getpcvisionbonus", 'f', "", opcodeGetPCVisionBonus); + extensions.registerInstruction("setpcvisionbonus", "f", opcodeSetPCVisionBonus); + extensions.registerInstruction("modpcvisionbonus", "f", opcodeModPCVisionBonus); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 49adfbe209..32e9aa8790 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -484,6 +484,10 @@ namespace Compiler const int opcodeGetStat = 0x200024e; const int opcodeGetStatExplicit = 0x200024f; + + const int opcodeGetPCVisionBonus = 0x2000322; + const int opcodeSetPCVisionBonus = 0x2000323; + const int opcodeModPCVisionBonus = 0x2000324; } namespace Transformation