diff --git a/DLL Source/J42_ARR_plugin.vcxproj b/DLL Source/J42_ARR_plugin.vcxproj new file mode 100644 index 0000000..246044d --- /dev/null +++ b/DLL Source/J42_ARR_plugin.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {0091BF4A-C88C-4BB2-A429-3DF4F43C8232} + Win32Proj + J42_ARR_plugin + 10.0.17763.0 + + + + DynamicLibrary + true + v141 + MultiByte + No + false + + + DynamicLibrary + false + v141 + false + MultiByte + No + false + + + + + + + + + + + + + + + true + + + false + + + + Level3 + Disabled + _DEBUG;_USRDLL;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) + common/IPrefix.h + MultiThreadedDebug + + + true + exports.def + + + + + Level3 + MaxSpeed + true + true + NDEBUG;_USRDLL;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories); + common/IPrefix.h + MultiThreaded + + + true + true + true + exports.def + + + + + + + + + + + + + + {472e19ab-def0-42df-819b-18722e8dc822} + + + {7028b79c-06e3-4d9a-b38c-1dc3680b1bdb} + + + {5fd1c08d-db80-480c-a1c6-f0920005cd13} + + + + + + \ No newline at end of file diff --git a/DLL Source/exports.def b/DLL Source/exports.def new file mode 100644 index 0000000..2b3667f --- /dev/null +++ b/DLL Source/exports.def @@ -0,0 +1,4 @@ +LIBRARY "J42_ARR_plugin" +EXPORTS +SKSEPlugin_Query +SKSEPlugin_Load \ No newline at end of file diff --git a/DLL Source/main.cpp b/DLL Source/main.cpp new file mode 100644 index 0000000..6c7464f --- /dev/null +++ b/DLL Source/main.cpp @@ -0,0 +1,452 @@ +#include "main.h" + +static PluginHandle g_pluginHandle = kPluginHandle_Invalid; +static SKSEPapyrusInterface * g_papyrus = NULL; + + +///// Duplicating old ARR functionality here + + +// variables. cache here for easy access + +const SInt32 LINEAR = 0; +const SInt32 HYPERBOLIC = 1; +const SInt32 EXPONENTIAL = 2; + +SInt32 CurrentMode = HYPERBOLIC; + +float (*convertDamageResist)(float) = &convertDamageResistToHyperbolic; + +float LinearMult = 1.0; +float LinearCeil = 0.8000; +float HyperDenomShift = 0.2; +float ExpBase = 0.1337; + +bool UseCeilExpHyp = false; +bool UseMultExpHyp = false; + +float CeilExpHyp = 1.0; +float MultExpHyp = 1.00; + +const float kVanillaArmorScalingFactor = 0.0012; + +// float setter functions +void setLinearMult(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setLinearMult", arg); + LinearMult = arg / kVanillaArmorScalingFactor; +} + +void setLinearFloor(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setLinearFloor", arg); + LinearCeil = 1.0 - arg; +} + +void setLinearCeil(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setLinearCeil", arg); + LinearCeil = arg; +} + +void setHyperDiv(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setHyperDiv", arg); + HyperDenomShift = kVanillaArmorScalingFactor / arg; +} + +/* +void setHyperSurviveSlope(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setHyperSurviveSlope", arg); + HyperDenomShift = kVanillaArmorScalingFactor / arg; +} +*/ + +void setExpBase(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setExpBase", arg); + ExpBase = pow(arg, 1/kVanillaArmorScalingFactor); +} + +void setFloorExpHyp(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setFloorExpHyp", arg); + CeilExpHyp = 1.0 - arg; +} + +/* +void setCeilExpHyp(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setCeilExpHyp", arg); + CeilExpHyp = arg; +} +*/ + +void setMultExpHyp(StaticFunctionTag *base, float arg) { + _MESSAGE("Called %s with value %f.", "setMultExpHyp", arg); + MultExpHyp = arg; +} + + +// bool setter functions + +void setUseFloorExpHyp(StaticFunctionTag *base, bool arg) { + _MESSAGE("Called %s with value %s.", "setUseFloorExpHyp", arg ? "true" : "false"); + UseCeilExpHyp = arg; +} + +/* +void setUseCeilExpHyp(StaticFunctionTag *base, bool arg) { + _MESSAGE("Called %s with value %s.", "setUseCeilExpHyp", arg ? "true" : "false"); + UseCeilExpHyp = arg; +} +*/ + +void setUseMultExpHyp(StaticFunctionTag *base, bool arg) { + _MESSAGE("Called %s with value %s.", "setUseMultExpHyp", arg ? "true" : "false"); + UseMultExpHyp = arg; +} + +// int setter functions +void setMode(StaticFunctionTag *base, SInt32 arg) { + _MESSAGE("Called %s with value %s.", "setMode", + arg == LINEAR ? "linear" : + arg == HYPERBOLIC ? "hyperbolic" : + arg == EXPONENTIAL ? "exponential" : + "UNKNOWN" + ); + switch (arg) { + case LINEAR: { + convertDamageResist = convertDamageResistToLinear; + break; + } + case HYPERBOLIC: { + convertDamageResist = convertDamageResistToHyperbolic; + break; + } + case EXPONENTIAL: { + convertDamageResist = convertDamageResistToExponential; + break; + } + } +} + +float convertDamageResistToHyperbolic(float vanillaResist) { + float newResist = vanillaResist > 0 ? vanillaResist / (vanillaResist + HyperDenomShift) : vanillaResist / HyperDenomShift; + if (UseMultExpHyp) { + newResist *= MultExpHyp; + } + if (UseCeilExpHyp && newResist > CeilExpHyp) { + newResist = CeilExpHyp; + } +#ifdef _DEBUG + _DMESSAGE("Converting from vanilla resist of %f to hyperbolic resist of %f.", vanillaResist, newResist); +#endif + return newResist; +} + +float convertDamageResistToExponential(float vanillaResist) { + float newResist = 1.0 - pow(ExpBase, vanillaResist); + if (UseMultExpHyp) { + newResist *= MultExpHyp; + } + if (UseCeilExpHyp && newResist > CeilExpHyp) { + newResist = CeilExpHyp; + } +#ifdef _DEBUG + _DMESSAGE("Converting from vanilla resist of %f to exponential resist of %f.", vanillaResist, newResist); +#endif + return newResist; +} + +float convertDamageResistToLinear(float vanillaResist) { + float newResist = vanillaResist * LinearMult; + if (newResist > LinearCeil) { + newResist = LinearCeil; + } +#ifdef _DEBUG + _DMESSAGE("Converting from vanilla resist of %f to linear resist of %f.", vanillaResist, newResist); +#endif + return newResist; +} + +// function registry handling +bool RegisterFuncs(VMClassRegistry* registry) { + registry->RegisterFunction( + new NativeFunction1("setLinearMult", "J42_ARR_SKSE_Bridge", setLinearMult, registry)); + registry->RegisterFunction( + new NativeFunction1("setLinearFloor", "J42_ARR_SKSE_Bridge", setLinearFloor, registry)); + registry->RegisterFunction( + new NativeFunction1("setHyperDiv", "J42_ARR_SKSE_Bridge", setHyperDiv, registry)); + //registry->RegisterFunction( + // new NativeFunction1("setHyperSurviveSlope", "J42_ARR_SKSE_Bridge", setHyperSurviveSlope, registry)); + registry->RegisterFunction( + new NativeFunction1("setExpBase", "J42_ARR_SKSE_Bridge", setExpBase, registry)); + registry->RegisterFunction( + new NativeFunction1("setFloorExpHyp", "J42_ARR_SKSE_Bridge", setFloorExpHyp, registry)); + //registry->RegisterFunction( + // new NativeFunction1("setCeilExpHyp", "J42_ARR_SKSE_Bridge", setCeilExpHyp, registry)); + registry->RegisterFunction( + new NativeFunction1("setMultExpHyp", "J42_ARR_SKSE_Bridge", setMultExpHyp, registry)); + registry->RegisterFunction( + new NativeFunction1("setUseFloorExpHyp", "J42_ARR_SKSE_Bridge", setUseFloorExpHyp, registry)); + //registry->RegisterFunction( + // new NativeFunction1("setUseCeilExpHyp", "J42_ARR_SKSE_Bridge", setUseCeilExpHyp, registry)); + registry->RegisterFunction( + new NativeFunction1("setUseMultExpHyp", "J42_ARR_SKSE_Bridge", setUseMultExpHyp, registry)); + registry->RegisterFunction( + new NativeFunction1("setMode", "J42_ARR_SKSE_Bridge", setMode, registry)); + + return true; +} + +// code injection handling +//const RelocAddr kDamageResistInject1Address(0x00743ac7); +//const RelocAddr kDamageResistInject1Address(0x00743807); +const RelocAddr kDamageResistInject1Address(0x00743617); +const UInt8 kDamageResistInject1Bytes[] = { + 0xf3, 0x0f, 0x10, 0x4d, 0x77, 0xf3, 0x0f, 0x58, 0xc8, 0xf3, 0x0f, 0x11, 0x4d, 0x77 +}; +const size_t kDamageResist1NeededBytes = 34; + + +//const RelocAddr kDamageResistInject2Address(0x00625452); +//const UInt8 kDamageResistInject2Bytes[] = { +// 0xf3, 0x0f, 0x10, 0x0d, 0xc2, 0x6b, 0xf3, 0x00, 0xf3, 0x44, 0x0f, 0x58, +// 0xc0, 0x44, 0x0f, 0x2f, 0xc1, 0x72, 0x04, 0x44, 0x0f, 0x28, 0xc1 +//}; +/* +const RelocAddr kDamageResistInject2Address(0x00625192); +const UInt8 kDamageResistInject2Bytes[] = { + 0xf3, 0x0f, 0x10, 0x0d, 0x92, 0x6e, 0xf3, 0x00, 0xf3, 0x44, 0x0f, 0x58, + 0xc0, 0x44, 0x0f, 0x2f, 0xc1, 0x72, 0x04, 0x44, 0x0f, 0x28, 0xc1 +}; +*/ +const RelocAddr kDamageResistInject2Address(0x00624FA2); +const UInt8 kDamageResistInject2Bytes[] = { + 0xf3, 0x0f, 0x10, 0x0d, 0x32, 0xd0, 0xf1, 0x00, 0xf3, 0x44, 0x0f, 0x58, + 0xc0, 0x44, 0x0f, 0x2f, 0xc1, 0x72, 0x04, 0x44, 0x0f, 0x28, 0xc1 +}; + +const UInt8 kAsm8Nop = 0x90; +const UInt16 kAsm16Nop = 0x9090; +const UInt32 kAsm32Nop = 0x90909090; +const UInt64 kAsm64Nop = 0x9090909090909090; + +const size_t kNumBranchTrampolines = 0; +const size_t kSizeOfTrampolineCode = 14; +const size_t kSizeOfCodeGen = kDamageResist1NeededBytes; + +// following code from https://stackoverflow.com/questions/1065774/initialization-of-a-normal-array-with-one-default-value +// we use it to initialize arrays with Nops +template +constexpr std::array, size> make_array_impl(T && value, std::index_sequence) { + return std::array, size>{ (static_cast(indexes), value)..., std::forward(value) }; +} + +template +constexpr std::array, 0> make_array(std::integral_constant, T &&) { + return std::array, 0>{}; +} + +template +constexpr std::array, size> make_array(std::integral_constant, T && value) { + return make_array_impl(std::forward(value), std::make_index_sequence{}); +} + +template +constexpr std::array, size> make_array(T && value) { + return make_array(std::integral_constant{}, std::forward(value)); +} + + +#pragma pack(push, 1) +struct LongJump { + UInt8 mov_prefix; // 0x48 -- 64 bit instruction + UInt8 mov_opcode; // 0xb8 -- mov rax, + UInt64 dst; + UInt8 jmp_opcode; // 0xff + UInt8 jmp_fieldmode; // 0xe0 -- 64 bit mode + use register rax + + LongJump(uintptr_t _dst): mov_prefix(0x48), mov_opcode(0xb8), dst(_dst), jmp_opcode(0xff), jmp_fieldmode(0xe0) {} +}; +#pragma pack(pop) + +void WriteLongJump(uintptr_t src, uintptr_t dst) +{ + STATIC_ASSERT(sizeof(uintptr_t) == sizeof(UInt64)); + + + LongJump jumpCode(dst); + + SafeWriteBuf(src, &jumpCode, sizeof(jumpCode)); +} + +void Hooks_FixDamageResist_Commit() { + { + struct DamageResistInjection1 : Xbyak::CodeGenerator { + DamageResistInjection1(void * buf) : Xbyak::CodeGenerator(kDamageResist1NeededBytes, buf) + { + // xmm0: hiddenResist + // rbp+0x77: vanillaResist + + // setup call to our function + movss(xmm0, ptr[rbp + 0x77]); + mov(rax, ptr[(uintptr_t)&convertDamageResist]); + call(rax); + + // store the resulting resist + movss(ptr[rbp + 0x77], xmm0); + + // return to original code + mov(rax, kDamageResistInject1Address.GetUIntPtr() + sizeof(kDamageResistInject1Bytes)); + jmp(rax); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + DamageResistInjection1 code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + // overwrite the three instructions: + // movss xmm1, ptr [rbp+0x77] + // addss xmm1, xmm0 + // movss ptr [rbp+0x77], xmm1 + WriteLongJump(kDamageResistInject1Address.GetUIntPtr(), uintptr_t(code.getCode())); + SafeWrite16(kDamageResistInject1Address.GetUIntPtr() + sizeof(LongJump), kAsm16Nop); + STATIC_ASSERT(sizeof(kDamageResistInject1Bytes) == sizeof(LongJump) + sizeof(UInt16)); + } + + { + struct DamageResistInjection2 : Xbyak::CodeGenerator { + DamageResistInjection2(void * buf) : Xbyak::CodeGenerator(sizeof(kDamageResistInject2Bytes), buf) + { + // xmm0: hiddenResist + // xmm8: vanillaResist + + // setup call to our function + movss(xmm0, xmm8); + mov(rax, ptr[(uintptr_t)&convertDamageResist]); + call(rax); + + // store the resulting resist + movss(xmm8, xmm0); + } + }; + + std::array codeBuf = make_array(kAsm8Nop); + DamageResistInjection2 code(&codeBuf.front()); + + // overwrite the five instructions: + // movss xmm1, ptr [...] ;address is that of max damage resist + // addss xmm8, xmm0 + // comiss xmm8, xmm0 + // jb 0x04 ;short jb past the next instruction + // movaps xmm8, xmm1 + SafeWriteBuf(kDamageResistInject2Address.GetUIntPtr(), &codeBuf.front(), sizeof(kDamageResistInject2Bytes)); + } +} + +template +bool TestMemoryData(uintptr_t address, T expectedValue) { + T memoryValue = *reinterpret_cast(address); + if (memoryValue == expectedValue) { + return true; + } + _MESSAGE("Unexpected memory value at %016I64X: 0x%02x, expected: 0x%02x", address, memoryValue, expectedValue); + return false; +}; + +bool IsBinaryCompatible() { + bool ok = true; + uintptr_t address = kDamageResistInject1Address.GetUIntPtr(); + for (const UInt8 entry : kDamageResistInject1Bytes) { + ok &= TestMemoryData(address, entry); + address += 1; + } + address = kDamageResistInject2Address.GetUIntPtr(); + for (const UInt8 entry : kDamageResistInject2Bytes) { + ok &= TestMemoryData(address, entry); + address += 1; + } + return ok; +} + + +/// Standard SKSE Stuff + +extern "C" { + + bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info) { // Called by SKSE to learn about this plugin and check that it's safe to load it + gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Skyrim Special Edition\\SKSE\\J42_ARR_plugin.log"); + + _MESSAGE("J42_ARR_plugin"); + _MESSAGE("Base Address is: %016I64X", RelocationManager::s_baseAddr); + // populate info structure + info->infoVersion = PluginInfo::kInfoVersion; + info->name = "J42_ARR_plugin"; + info->version = 20005; + + // store plugin handle so we can identify ourselves later + g_pluginHandle = skse->GetPluginHandle(); + + if(skse->isEditor) + { + _MESSAGE("Loaded in editor, marking as incompatible."); + + return false; + } + //else if(skse->runtimeVersion != RUNTIME_VERSION_1_5_39) + //else if (skse->runtimeVersion != RUNTIME_VERSION_1_5_50) + //else if (skse->runtimeVersion != RUNTIME_VERSION_1_5_53) + //else if (skse->runtimeVersion != RUNTIME_VERSION_1_5_62) + else if (skse->runtimeVersion != RUNTIME_VERSION_1_5_73) + { + _ERROR("Unsupported runtime version %08X.", skse->runtimeVersion); + + return false; + } + + // ### do not do anything else in this callback + // ### only fill out PluginInfo and return true/false + + // supported runtime version + return true; + } + + bool SKSEPlugin_Load(const SKSEInterface * skse) { // Called by SKSE to load this plugin + if (!IsBinaryCompatible()) { + _ERROR("Incompatible SKSE plugin loaded. Skipping remainder of init process."); + + return false; + } + + if (kNumBranchTrampolines > 0) { + if (!g_branchTrampoline.Create(kSizeOfTrampolineCode * kNumBranchTrampolines)) + { + _ERROR("Couldn't create branch trampoline. This is fatal. Skipping remainder of init process."); + return false; + } + } + + if (kSizeOfCodeGen > 0) { + if (!g_localTrampoline.Create(kSizeOfCodeGen, nullptr)) + { + _ERROR("Couldn't create codegen buffer. This is fatal. Skipping remainder of init process."); + return false; + } + } + + _MESSAGE("Preparing injection."); + + Hooks_FixDamageResist_Commit(); + + _MESSAGE("Injection completed."); + + // Do Papyrus stuff + g_papyrus = (SKSEPapyrusInterface *)skse->QueryInterface(kInterface_Papyrus); + + // Check if the function registration was a success... + bool btest = g_papyrus->Register(RegisterFuncs); + + if (btest) { + _MESSAGE("Papyrus registration complete."); + } + + return true; + } + +}; \ No newline at end of file diff --git a/DLL Source/main.h b/DLL Source/main.h new file mode 100644 index 0000000..75d029a --- /dev/null +++ b/DLL Source/main.h @@ -0,0 +1,44 @@ +#include "skse64/PluginAPI.h" // super +#include "skse64/PapyrusNativeFunctions.h" // for exporting to Papyrus +#include "skse64_common/skse_version.h" // What version of SKSE is running? +#include "skse64_common/Relocation.h" +#include "skse64_common/SafeWrite.h" // for binary injections +#include "skse64_common/BranchTrampoline.h" +#include "xbyak/xbyak.h" // for CodeGenerator +#include // CSIDL_MYCODUMENTS +#include + +// float setter functions +void setLinearMult(StaticFunctionTag *base, float arg); +void setLinearFloor(StaticFunctionTag *base, float arg); +void setHyperDiv(StaticFunctionTag *base, float arg); +//void setHyperSurviveSlope(StaticFunctionTag *base, float arg); +void setExpBase(StaticFunctionTag *base, float arg); +void setFloorExpHyp(StaticFunctionTag *base, float arg); +//void setCeilExpHyp(StaticFunctionTag *base, float arg); +void setMultExpHyp(StaticFunctionTag *base, float arg); + +// bool setter functions +void setUseFloorExpHyp(StaticFunctionTag *base, bool arg); +//void setUseCeilExpHyp(StaticFunctionTag *base, bool arg); +void setUseMultExpHyp(StaticFunctionTag *base, bool arg); + +// int setter functions +void setMode(StaticFunctionTag *base, SInt32 arg); + +// function registry handling +bool RegisterFuncs(VMClassRegistry* registry); + +// conversion functions +float convertDamageResistToHyperbolic(float vanillaResist); +float convertDamageResistToExponential(float vanillaResist); +float convertDamageResistToLinear(float vanillaResist); + +//float calcDamageTaken(float armorRating); + +//float RescaleArmor(float vanillaResist); + +template +bool TestMemoryData(uintptr_t adress, T expectedValue); + +bool IsBinaryCompatible(); \ No newline at end of file diff --git a/J42_ArmorRatingRedux.esp b/J42_ArmorRatingRedux.esp new file mode 100644 index 0000000..e7fa743 Binary files /dev/null and b/J42_ArmorRatingRedux.esp differ diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..eec3fc8 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,265 @@ +Armor Rating Redux 2.0.5 SE +by DaJay42 on NexusMods.com + +=============================================================================== + +Armor Rating Redux - No Cap, Better Formula, Morrowind-like + +Fixes the ridiculously broken armor rating system in Skyrim by implementing a +formula closer to what Morrowind had. Fully configurable! +Now for SE! + +=============================================================================== + +------------ +The Problem: +------------ +The vanilla armor system is utterly, utterly broken. +Having an armor rating of 0 - 300 is pretty much pointless, as you still take +almost full damage. +Having an armor rating of 300 - 600 suddenly means every point is valuable, +as your survivability increases hyperbolically. +Having an armor rating of over 667 (567 if in full armor) means any further +increase is pointless, as your damage resistance simply goes flat. + +For a more detailed discussion, see this six years old(!) forum thread: +http://forums.nexusmods.com/index.php?/topic/481577-skyrims-armor-system-is-broken/ + +=============================================================================== + +------------- +The Solution: +------------- +Replace the broken system with one like in Morrowind, where every point of +armor counts. +This mod attempts to do just that. + +It replaces Skyrim's Damage Resistance calculation with one of 3 formulas, +as described below. All of them can be fine-tuned in the mod's MCM, which +also has some advanced options for that really specific armor formula you +never knew you needed. +All changes apply to both the player and any NPCs. +Additionally, the mod provides two (optional) ways of informing the player of +their current resistance: by notification or with a (fake) magic effect. + +=============================================================================== + +----------- +Definitions: +----------- +To make sure we're talking about the same thing, let's first define some terms: + +Armor Rating: The current armor value displayed in your inventory, as + modified by your skills, perks, etc. + +Hidden Armor Rating: On top of its visible armor rating, every piece of + armor you wear grants an additional 25 points of fixed, invisible armor + rating. This seems to be the result of a halfhearted attempt by Bethesda + to balance their broken system. This completely messes up the balance when + using armors with more than the standard amount of pieces. + +Damage Resist(ance): The fraction of incoming damage that is negated by your + armor. + +Damage Taken: The fraction of incoming damage that passes through your armor. + +Survivability: By how much your armor increases your ability to survive damage. + e.g. with +400% survivability and 100 health, you could take 500 damage + before dying. + +Level-Adjusted Survivability: By how much your armor increases your ability + to survive damage, relative to how much damage your enemies of the same + level can deal. In Skyrim, weapon damage increases linearly with skill, + so you need to have at least a linear increase in survivability to stay + ahead of the curve. + +=============================================================================== + +--------- +Formulas: +--------- +The mod currently supports 3 computation modes: + +----------- +Hyperbolic: +----------- +Morrowind-like. Every point of armor rating increases your survivability by a +fixed percentage. This means every point of armor is worth the same. Compared +to vanilla, your early armor will protect you a lot more, while armor past +the cap will still be useful, but it is difficult to reach crazy high +protection values. +HypDiv controls the increase per point. The default is 0.6%, which means you +will reach 80% protection (+400% survivability) at 667 armor rating, just like +vanilla. + +Your survivability will increase linearly, damage taken decrease hyperbolically. +Twice the armor rating means twice the survivability. +Damage Taken = Input damage / (Armor Rating * HypDiv + 1) + + +------------ +Exponential: +------------ +A compromise between Morrowind-style survivability and vanilla "balancing". +Every point of armor will reduce damage taken by a fixed multiplier. +Your early armor will be worth less than with "Hyperbolic", but your late game +armor will let you come closer to 100% resistance. +ExpBase controls the damage multiplier per point. The default is 99.76%, which +means you will reach 80% protection (+400% survivability) at approximately 667 +armor rating, just like vanilla. + +Your survivability will increase exponentially, damage taken decrease +exponentially. Every 'x' points of Armor Rating, your survivability doubles. +Damage Taken = Input damage * ExpBase^Armor Rating + + +------- +Linear: +------- +Vanilla-like, but without the hidden armor rating. Just because. +Every point of armor reduces damage taken by subtracting a fixed value. +Results as described above. +LinearMult controls the reduction per point. The default is 0.12%. +LinearFloor controls the lowest possible multiplier. The default is 0.2 +The default values let you reach 80% protection (+400% survivability) at +667 armor rating, just like vanilla. + +Your survivability will increase hyperbolically, damage taken decrease +linearly. +Damage Taken = Input damage * max(LinearFloor, 1 - LinearMult * Armor Rating) + + + +=============================================================================== + +----------------- +Advanced Options: +----------------- + +As of 1.1.0, these advanced options are available: + +FloorExpHyp: If activated, imposes a hard limit on your resistance in Hyperbolic +or Exponential modes, equivalent to what linearFloor does in Linear mode. + +MultExpHyp: If enabled, reduces your resistance in Hyperbolic or Exponential +modes by a multiplier, acting as a soft ceiling. +Thanks Neker07 for the ideas! + + +=============================================================================== + +------------ +Compability: +------------ + - Should be loaded after other mods that change Game Settings related to + Armor. + - Not compatible with any mod that attempts to do the same thing (duh). + + Compatible with everything else. + ...probably. + + +=============================================================================== + +-------- +Caveats: +-------- + - The magic effect description will round resistance to the next integer + value. To know your precise resistance, use the "notify" option. + +=============================================================================== + +Requirements: + - SKSE 2.0.15 + - SkyUI SE + +=============================================================================== + +-------- +Changes: +-------- +2.0.5 (SE only) + - Updated for SKSE 2.0.15 / Runtime 1.5.73 +2.0.4 (SE only) + - Updated for SKSE 2.0.11 / Runtime 1.5.62 + +2.0.3 (SE only) + - Updated for SKSE 2.0.9 + +2.0.2 (SE only) + - Updated for SKSE 2.0.8 + +2.0.1 (SE only) + - Fixed an oversight in the SE plugin that could cause actors with negative + armor rating to take infinite or negative damage if hyperbolic mode is + active. + +2.0.0 + - Complete overhaul of the mod's internals. Now does its computation in a + SKSE plugin, rather than Papyrus code. (A HUGE thank you to + underthesky for letting me make use of his SKSE code!) + Due to this being a huge change to the mods workings, a clean save + is absolutely recommended. + This change has some neat effects: + - Significantly increased performance. + - Player Only version no longer required, as calculations for NPCs are no + longer a potential performance drain. + - Changes in armor rating always have immediate effect. + - No more ActorValue or AVI Skill Mult caused incompatibilities. + - High Damage Resist values no longer cause weird stamina consumption + on blocking NPCs. + - NPC Damage Resist is no longer hard capped. + - On a different note, made the mod translation-friendly. Note that all + included translation files are dummy files and contain English text. + - Added a German translation to serve as an example. Translators for other + languages welcome! + +1.2.1 + - Fixed an issue that could cause the player to take excessive amounts of + damage when exceeding vanilla resistance limits. + +1.2.0 + - Changed the used actor value from "Fame" to "Variable07". This should + hopefully create less conflicts. + - Changed the way NPCs are treated. The new method has a hard cap at 99% + resistance, and might be slightly slower to update, but should be a lot + more reliable and cause less "spikes" in script activity. + - Added a "player only" version, for those who value performance over + fairness. A clean save is recommended when switching between + "Everyone" and "Player Only" versions. + +1.1.0 + - Added advanced options: If activated, FloorExpHyp imposes a hard limit on + your resistance in Hyperbolic or Exponential modes, equivalent to what + linearFloor does in Linear mode. MultExpHyp, if enabled, reduces your + resistance by a multiplier, and acts as a soft ceiling. + Thanks Neker07 for the ideas! + +1.0.1 + - Fixed minor issue that caused incorrect debug output. + +1.0.0 + - Initial release. + + +=============================================================================== + +---------------------------- +Permissions/License/Credits: +---------------------------- +The SE version of the mod contains code by locerra, without whom this port +would not have been possible. Many thanks! +Additional assistance was provided by hotemup. Thanks to you, too! + +The original mod contains code from Armor Rating Rescaled by underthesky: +http://www.nexusmods.com/skyrim/mods/85358/ +Used under permission as stated on that mod's page. +(Once again, a huge thank you for that!) + +Written by DaJay42, nexusmods.com, 2016-2018. +Software provided as-is, with no warranty. +For personal use only. +Re-uploads, uploads to other sites or uploads of derivative works are allowed +only after advance written permission by the author, or failure of the author +to respond within an month of a written request for such permission. + diff --git a/SKSE/Plugins/J42_ARR_plugin.dll b/SKSE/Plugins/J42_ARR_plugin.dll new file mode 100644 index 0000000..7ddb277 Binary files /dev/null and b/SKSE/Plugins/J42_ARR_plugin.dll differ diff --git a/Seq/J42_ArmorRatingRedux.seq b/Seq/J42_ArmorRatingRedux.seq new file mode 100644 index 0000000..94ea9d2 Binary files /dev/null and b/Seq/J42_ArmorRatingRedux.seq differ diff --git a/interface/translations/J42_ArmorRatingRedux_CZECH.txt b/interface/translations/J42_ArmorRatingRedux_CZECH.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_CZECH.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_ENGLISH.txt b/interface/translations/J42_ArmorRatingRedux_ENGLISH.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_ENGLISH.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_FRENCH.txt b/interface/translations/J42_ArmorRatingRedux_FRENCH.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_FRENCH.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_GERMAN.txt b/interface/translations/J42_ArmorRatingRedux_GERMAN.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_GERMAN.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_ITALIAN.txt b/interface/translations/J42_ArmorRatingRedux_ITALIAN.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_ITALIAN.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_JAPANESE.txt b/interface/translations/J42_ArmorRatingRedux_JAPANESE.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_JAPANESE.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_POLISH.txt b/interface/translations/J42_ArmorRatingRedux_POLISH.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_POLISH.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_RUSSIAN.txt b/interface/translations/J42_ArmorRatingRedux_RUSSIAN.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_RUSSIAN.txt differ diff --git a/interface/translations/J42_ArmorRatingRedux_SPANISH.txt b/interface/translations/J42_ArmorRatingRedux_SPANISH.txt new file mode 100644 index 0000000..ca97d8f Binary files /dev/null and b/interface/translations/J42_ArmorRatingRedux_SPANISH.txt differ diff --git a/scripts/J42_ARR_AliasScript.pex b/scripts/J42_ARR_AliasScript.pex new file mode 100644 index 0000000..6dbf2ca Binary files /dev/null and b/scripts/J42_ARR_AliasScript.pex differ diff --git a/scripts/J42_ARR_MenuScript.pex b/scripts/J42_ARR_MenuScript.pex new file mode 100644 index 0000000..f3e8912 Binary files /dev/null and b/scripts/J42_ARR_MenuScript.pex differ diff --git a/scripts/J42_ARR_QuestScript.pex b/scripts/J42_ARR_QuestScript.pex new file mode 100644 index 0000000..a128e2a Binary files /dev/null and b/scripts/J42_ARR_QuestScript.pex differ diff --git a/scripts/J42_ARR_SKSE_Bridge.pex b/scripts/J42_ARR_SKSE_Bridge.pex new file mode 100644 index 0000000..5012683 Binary files /dev/null and b/scripts/J42_ARR_SKSE_Bridge.pex differ diff --git a/source/scripts/J42_ARR_AliasScript.psc b/source/scripts/J42_ARR_AliasScript.psc new file mode 100644 index 0000000..338a318 --- /dev/null +++ b/source/scripts/J42_ARR_AliasScript.psc @@ -0,0 +1,8 @@ +ScriptName J42_ARR_AliasScript extends ReferenceAlias + + +J42_ARR_QuestScript Property J42_ARR_MainQuest Auto + +Event OnPlayerLoadGame() + J42_ARR_MainQuest.Maintenance() +EndEvent diff --git a/source/scripts/J42_ARR_MenuScript.psc b/source/scripts/J42_ARR_MenuScript.psc new file mode 100644 index 0000000..fd74503 --- /dev/null +++ b/source/scripts/J42_ARR_MenuScript.psc @@ -0,0 +1,315 @@ +ScriptName J42_ARR_MenuScript extends SKI_ConfigBase + + +J42_ARR_QuestScript Property J42_ARR_MainQuest Auto + +int oidMode +int oidDebugging +int oidNotify +int oidUseSpell + +int oidLinearMult +int oidLinearFloor +int oidHyperDiv +int oidexpBase + +int oidUseFloorExpHyp +int oidUseMultExpHyp +int oidFloorExpHyp +int oidMultExpHyp + + +string version +string[] modes +string[] formulas + +Int Function GetVersion() + ;99999 == 9.99.99 + return 20005 +EndFunction + +Event OnVersionUpdate(Int ver) + version = ver/10000+"."+(ver/100)%100+"."+ver%100 + debug.Notification(ModName+": Version "+version) + debug.Trace(self+": Version "+version) + + J42_ARR_MainQuest.stop() + + Utility.Wait(1) + + J42_ARR_MainQuest.start() + +EndEvent + + +Event OnConfigInit() + debug.Notification(ModName+": LOADING...") + debug.Trace(self+": LOADING...") +EndEvent + +Event OnConfigRegister() + debug.Notification(ModName+": OK!") + debug.Trace(self+": OK!") + + pages = new string[1] + pages[0] = "$J42_ARR_Title_General" + + modes = new string[3] + modes[0] = "$J42_ARR_Name_Linear" + modes[1] = "$J42_ARR_Name_Hyperbolic" + modes[2] = "$J42_ARR_Name_Exponential" + + formulas = new string[3] + formulas[0] ="$J42_ARR_Formula_Linear" + formulas[1] ="$J42_ARR_Formula_Hyperbolic" + formulas[2] ="$J42_ARR_Formula_Exponential" +EndEvent + + +Event OnConfigOpen() + pages = new string[1] + pages[0] = "$J42_ARR_Title_General" + + + modes = new string[3] + modes[0] = "$J42_ARR_Name_Linear" + modes[1] = "$J42_ARR_Name_Hyperbolic" + modes[2] = "$J42_ARR_Name_Exponential" + + formulas = new string[3] + formulas[0] ="$J42_ARR_Formula_Linear" + formulas[1] ="$J42_ARR_Formula_Hyperbolic" + formulas[2] ="$J42_ARR_Formula_Exponential" +EndEvent + +Event OnConfigClose() + ;Utility.Wait(0.1) + J42_ARR_MainQuest.ResetCache() + J42_ARR_MainQuest.RegisterForSingleUpdate(J42_ARR_MainQuest.timer) +EndEvent + + +Event OnPageReset(string page) + + SetTitleText(ModName) + SetCursorFillMode(TOP_TO_BOTTOM) + + If page == pages[0] + ;left side + AddHeaderOption("$J42_ARR_Title_Settings") + oidMode = AddTextOption("$J42_ARR_Title_Formula", modes[J42_ARR_MainQuest.Mode]) + AddTextOption("", formulas[J42_ARR_MainQuest.Mode], OPTION_FLAG_DISABLED) + + AddEmptyOption() + oidLinearMult = AddSliderOption("$J42_ARR_Param_LinearMult",J42_ARR_MainQuest.LinearMult ,"{4}") + oidLinearFloor = AddSliderOption("$J42_ARR_Param_LinearFloor",J42_ARR_MainQuest.LinearFloor ,"{4}") + oidHyperDiv = AddSliderOption("$J42_ARR_Param_HyperDiv",J42_ARR_MainQuest.HyperDiv ,"{4}") + oidExpBase = AddSliderOption("$J42_ARR_Param_ExpBase",J42_ARR_MainQuest.ExpBase ,"{4}") + + AddHeaderOption("") + oidNotify = AddToggleOption("$J42_ARR_Option_Notify", J42_ARR_MainQuest.Notify) + oidUseSpell = AddToggleOption("$J42_ARR_Option_UseSpell", J42_ARR_MainQuest.UseSpell) + oidDebugging = AddToggleOption("$J42_ARR_Option_Log_Debug", J42_ARR_MainQuest.Debugging) + + AddHeaderOption("$J42_ARR_Title_Advanced") + oidUseFloorExpHyp = AddToggleOption("$J42_ARR_Option_UseFloorExpHyp", J42_ARR_MainQuest.UseFloorExpHyp) + oidFloorExpHyp = AddSliderOption("$J42_ARR_Param_FloorExpHyp",J42_ARR_MainQuest.FloorExpHyp ,"{2}") + oidUseMultExpHyp = AddToggleOption("$J42_ARR_Option_UseMultExpHyp", J42_ARR_MainQuest.UseMultExpHyp) + oidMultExpHyp = AddSliderOption("$J42_ARR_Param_MultExpHyp",J42_ARR_MainQuest.MultExpHyp ,"{2}") + + ;right side + SetCursorPosition(1) + AddHeaderOption("$J42_ARR_Title_DamageTaken") + AddSliderOption("$J42_ARR_Info_At_X_AR{"+50+"}",J42_ARR_MainQuest.Calculate(50), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+100+"}",J42_ARR_MainQuest.Calculate(100), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+200+"}",J42_ARR_MainQuest.Calculate(200), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+300+"}",J42_ARR_MainQuest.Calculate(300), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+400+"}",J42_ARR_MainQuest.Calculate(400), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+500+"}",J42_ARR_MainQuest.Calculate(500), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+667+"}",J42_ARR_MainQuest.Calculate(667), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+800+"}",J42_ARR_MainQuest.Calculate(800), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+1000+"}",J42_ARR_MainQuest.Calculate(1000), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+1500+"}",J42_ARR_MainQuest.Calculate(1500), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+2000+"}",J42_ARR_MainQuest.Calculate(2000), "{5}", OPTION_FLAG_DISABLED) + AddSliderOption("$J42_ARR_Info_At_X_AR{"+5000+"}",J42_ARR_MainQuest.Calculate(5000), "{5}", OPTION_FLAG_DISABLED) + Endif + +EndEvent + +Event OnOptionSelect(int option) + If option == oidDebugging + J42_ARR_MainQuest.debugging = !J42_ARR_MainQuest.debugging + SetToggleOptionValue(option,J42_ARR_MainQuest.debugging) + + ElseIf option == oidNotify + J42_ARR_MainQuest.Notify = !J42_ARR_MainQuest.Notify + SetToggleOptionValue(option,J42_ARR_MainQuest.Notify) + + ElseIf option == oidUseSpell + J42_ARR_MainQuest.UseSpell = !J42_ARR_MainQuest.UseSpell + SetToggleOptionValue(option,J42_ARR_MainQuest.UseSpell) + + ElseIf option == oidUseFloorExpHyp + J42_ARR_MainQuest.useFloorExpHyp = !J42_ARR_MainQuest.useFloorExpHyp + SetToggleOptionValue(option,J42_ARR_MainQuest.useFloorExpHyp) + + ElseIf option == oidUseMultExpHyp + J42_ARR_MainQuest.UseMultExpHyp = !J42_ARR_MainQuest.UseMultExpHyp + SetToggleOptionValue(option,J42_ARR_MainQuest.UseMultExpHyp) + + ElseIf option == oidMode + J42_ARR_MainQuest.Mode = (J42_ARR_MainQuest.Mode+1)%3 + SetTextOptionValue(option,modes[J42_ARR_MainQuest.Mode]) + EndIf + ForcePageReset() +EndEvent + + +Event OnOptionDefault(int option) + If option == oidDebugging + J42_ARR_MainQuest.debugging = false + SetToggleOptionValue(option,J42_ARR_MainQuest.debugging) + + ElseIf option == oidNotify + J42_ARR_MainQuest.notify = false + SetToggleOptionValue(option,J42_ARR_MainQuest.notify) + + ElseIf option == oidUseSpell + J42_ARR_MainQuest.UseSpell = false + SetToggleOptionValue(option,J42_ARR_MainQuest.UseSpell) + + ElseIf option == oidUseFloorExpHyp + J42_ARR_MainQuest.UseFloorExpHyp = false + SetToggleOptionValue(option,J42_ARR_MainQuest.UseFloorExpHyp) + + ElseIf option == oidUseMultExpHyp + J42_ARR_MainQuest.UseMultExpHyp = false + SetToggleOptionValue(option,J42_ARR_MainQuest.UseMultExpHyp) + + ElseIf option == oidMode + J42_ARR_MainQuest.Mode = 1 + SetTextOptionValue(option,modes[J42_ARR_MainQuest.Mode]) + + ElseIf option == oidLinearMult + J42_ARR_MainQuest.linearMult = 0.0012 + SetSliderOptionValue(option,J42_ARR_MainQuest.linearMult,"{4}") + + ElseIf option == oidLinearFloor + J42_ARR_MainQuest.linearFloor = 0.2000 + SetSliderOptionValue(option,J42_ARR_MainQuest.linearFloor,"{4}") + + ElseIf option == oidHyperDiv + J42_ARR_MainQuest.hyperDiv = 0.0060 + SetSliderOptionValue(option,J42_ARR_MainQuest.hyperDiv,"{4}") + + ElseIf option == oidExpBase + J42_ARR_MainQuest.expBase = 0.9976 + SetSliderOptionValue(option,J42_ARR_MainQuest.expBase,"{4}") + + ElseIf option == oidFloorExpHyp + J42_ARR_MainQuest.FloorExpHyp = 0.00 + SetSliderOptionValue(option,J42_ARR_MainQuest.FloorExpHyp,"{2}") + + ElseIf option == oidMultExpHyp + J42_ARR_MainQuest.MultExpHyp = 1.00 + SetSliderOptionValue(option,J42_ARR_MainQuest.MultExpHyp,"{2}") + + EndIf + ForcePageReset() +endEvent + +Event OnOptionSliderOpen(Int option) + + SetSliderDialogInterval(0.0001) + + If option == oidLinearMult + SetSliderDialogStartValue(J42_ARR_MainQuest.linearMult) + SetSliderDialogDefaultValue(0.0012) + SetSliderDialogRange(0.0001, 0.0100) + + ElseIf option == oidLinearFloor + SetSliderDialogStartValue(J42_ARR_MainQuest.linearFloor) + SetSliderDialogDefaultValue(0.2000) + SetSliderDialogRange(0.0, 1.0) + + ElseIf option == oidHyperDiv + SetSliderDialogStartValue(J42_ARR_MainQuest.hyperDiv) + SetSliderDialogDefaultValue(0.0067) + SetSliderDialogRange(0.0001, 0.01) + + + ElseIf option == oidExpBase + SetSliderDialogStartValue(J42_ARR_MainQuest.expBase) + SetSliderDialogDefaultValue(0.9975) + SetSliderDialogRange(0.9900, 0.9999) + + ElseIf option == oidFloorExpHyp + SetSliderDialogInterval(0.01) + SetSliderDialogStartValue(J42_ARR_MainQuest.FloorExpHyp) + SetSliderDialogDefaultValue(0.00) + SetSliderDialogRange(0.00, 1.00) + + ElseIf option == oidMultExpHyp + SetSliderDialogInterval(0.01) + SetSliderDialogStartValue(J42_ARR_MainQuest.MultExpHyp) + SetSliderDialogDefaultValue(1.00) + SetSliderDialogRange(0.00, 1.00) + + EndIf + +EndEvent + +Event OnOptionSliderAccept(Int option, Float value) + + If option == oidLinearMult + J42_ARR_MainQuest.linearMult = value + SetSliderOptionValue(option, J42_ARR_MainQuest.linearMult, "{4}") + + ElseIf option == oidLinearFloor + J42_ARR_MainQuest.linearFloor = value + SetSliderOptionValue(option, J42_ARR_MainQuest.linearFloor, "{4}") + + ElseIf option == oidHyperDiv + J42_ARR_MainQuest.hyperDiv = value + SetSliderOptionValue(option,J42_ARR_MainQuest.hyperDiv,"{4}") + + ElseIf option == oidExpBase + J42_ARR_MainQuest.expBase = value + SetSliderOptionValue(option,J42_ARR_MainQuest.expBase,"{4}") + + ElseIf option == oidFloorExpHyp + J42_ARR_MainQuest.FloorExpHyp = value + SetSliderOptionValue(option,J42_ARR_MainQuest.FloorExpHyp,"{2}") + + ElseIf option == oidMultExpHyp + J42_ARR_MainQuest.MultExpHyp = value + SetSliderOptionValue(option,J42_ARR_MainQuest.MultExpHyp,"{2}") + + Endif + + ForcePageReset() +EndEvent + +Event OnOptionHighlight(Int option) + If option == oidMode + SetInfoText("$J42_ARR_Hint_Mode") + + ElseIf option == oidDebugging + SetInfoText("$J42_ARR_Hint_Debug") + + ElseIf option == oidNotify + SetInfoText("$J42_ARR_Hint_Notify") + + ElseIf option == oidUseSpell + SetInfoText("$J42_ARR_Hint_UseSpell") + + ElseIf option == oidUseFloorExpHyp + SetInfoText("$J42_ARR_Hint_UseFloorExpHyp") + + ElseIf option == oidUseMultExpHyp + SetInfoText("$J42_ARR_Hint_UseMultExpHyp") + + Else + SetInfoText("$J42_ARR_Hint_Default{"+ModName+"}{"+version+"}") + EndIf +EndEvent diff --git a/source/scripts/J42_ARR_QuestScript.psc b/source/scripts/J42_ARR_QuestScript.psc new file mode 100644 index 0000000..a5d5e82 --- /dev/null +++ b/source/scripts/J42_ARR_QuestScript.psc @@ -0,0 +1,282 @@ +Scriptname J42_ARR_QuestScript extends Quest + + +Spell Property J42_ARR_ARDisplaySpell Auto +MagicEffect Property J42_ARR_ARDisplayEffect Auto +Message Property J42_ARR_ErrorBox Auto +Actor Property PlayerRef Auto + +bool Property Debugging = false Auto Hidden +bool Property Notify = false Auto Hidden +bool Property UseSpell = false Auto Hidden + +int Mode_var = 1 +int Property Mode Hidden +{ 0 := linear + 1 := hyperbolic + 2 := exponential} + Function Set(int arg) + Mode_var = arg + J42_ARR_SKSE_Bridge.setMode(Mode_var) + EndFunction + int Function Get() + return Mode_var + EndFunction +EndProperty + +float LinearMult_var = 0.0012 +float Property LinearMult Hidden + Function Set(float arg) + LinearMult_var = arg + J42_ARR_SKSE_Bridge.setLinearMult(LinearMult_var) + EndFunction + float Function Get() + return LinearMult_var + EndFunction +EndProperty + +float LinearFloor_var = 0.2000 +float Property LinearFloor Hidden + Function Set(float arg) + LinearFloor_var = arg + J42_ARR_SKSE_Bridge.setLinearFloor(LinearFloor_var) + EndFunction + float Function Get() + return LinearFloor_var + EndFunction +EndProperty + +float HyperDiv_var = 0.0060 +float Property HyperDiv Hidden + Function Set(float arg) + HyperDiv_var = arg + J42_ARR_SKSE_Bridge.setHyperDiv(HyperDiv_var) + EndFunction + float Function Get() + return HyperDiv_var + EndFunction +EndProperty + +float ExpBase_var = 0.9976 +float Property ExpBase Hidden + Function Set(float arg) + ExpBase_var = arg + J42_ARR_SKSE_Bridge.setExpBase(ExpBase_var) + EndFunction + float Function Get() + return ExpBase_var + EndFunction +EndProperty + +bool UseFloorExpHyp_var = false +bool Property UseFloorExpHyp Hidden + Function Set(bool arg) + UseFloorExpHyp_var = arg + J42_ARR_SKSE_Bridge.setUseFloorExpHyp(UseFloorExpHyp_var) + EndFunction + bool Function Get() + return UseFloorExpHyp_var + EndFunction +EndProperty + +bool UseMultExpHyp_var = false +bool Property UseMultExpHyp Hidden + Function Set(bool arg) + UseMultExpHyp_var = arg + J42_ARR_SKSE_Bridge.setUseMultExpHyp(UseMultExpHyp_var) + EndFunction + bool Function Get() + return UseMultExpHyp_var + EndFunction +EndProperty + +float FloorExpHyp_var = 0.00 +float Property FloorExpHyp Hidden + Function Set(float arg) + FloorExpHyp_var = arg + J42_ARR_SKSE_Bridge.setFloorExpHyp(FloorExpHyp_var) + EndFunction + float Function Get() + return FloorExpHyp_var + EndFunction +EndProperty + +float MultExpHyp_var = 1.00 +float Property MultExpHyp Hidden + Function Set(float arg) + MultExpHyp_var = arg + J42_ARR_SKSE_Bridge.setMultExpHyp(MultExpHyp_var) + EndFunction + float Function Get() + return MultExpHyp_var + EndFunction +EndProperty + +float Property timer = 1.0 AutoReadOnly Hidden + +String Property dmgMult = "fArmorScalingFactor" AutoReadOnly Hidden +String Property dmgFloor = "fMaxArmorRating" AutoReadOnly Hidden + +String Property hiddenRating = "fArmorBaseFactor" AutoReadOnly Hidden + +float Property defaultMult = 0.0012 AutoReadOnly Hidden +float Property defaultFloor = 0.0000 AutoReadOnly Hidden + +String Property strArmorRating = "My Armor Rating" Auto Hidden +String Property strDamageResist = "My Damage Resistance" Auto Hidden + +float cachedValue = -1.0 +Function ResetCache() + cachedValue = -1.0 +EndFunction + +Event OnInit() + Maintenance() +EndEvent + +Function Maintenance() + UnregisterForUpdate() + + PlayerRef.removeSpell(J42_ARR_ARDisplaySpell) + + If SKSE.GetScriptVersionRelease() >= 58 + Game.SetGameSettingFloat(hiddenRating, 0.0) + Game.SetGameSettingFloat(dmgMult, defaultMult*100.0) + Game.SetGameSettingFloat(dmgFloor, 9223372013568.0) + Game.SetGameSettingFloat("fArmorRatingMax", 2.5) + + strArmorRating = J42_ARR_ARDisplaySpell.getName() + strDamageResist = J42_ARR_ARDisplayEffect.getName() + + ResetCache() + + J42_ARR_SKSE_Bridge.setMode(Mode_var) + J42_ARR_SKSE_Bridge.setLinearMult(LinearMult_var) + J42_ARR_SKSE_Bridge.setLinearFloor(LinearFloor_var) + J42_ARR_SKSE_Bridge.setHyperDiv(HyperDiv_var) + J42_ARR_SKSE_Bridge.setExpBase(ExpBase_var) + J42_ARR_SKSE_Bridge.setFloorExpHyp(FloorExpHyp_var) + J42_ARR_SKSE_Bridge.setMultExpHyp(MultExpHyp_var) + J42_ARR_SKSE_Bridge.setUseFloorExpHyp(UseFloorExpHyp_var) + J42_ARR_SKSE_Bridge.setUseMultExpHyp(UseMultExpHyp_var) + + If Debugging + StatusReport() + EndIf + + If Notify || UseSpell + RegisterForSingleUpdate(timer) + EndIf + Else + printf("FATAL ERROR - Bad SKSE Version!", true) + J42_ARR_ErrorBox.show() + EndIf +EndFunction + +Event OnUpdate() + If Notify || UseSpell + float armorRating + float damageTaken + float damageMult + float damageResist + + armorRating = PlayerRef.getActorValue("DamageResist") + If armorRating != cachedValue + cachedValue = armorRating + PlayerRef.removeSpell(J42_ARR_ARDisplaySpell) + + If armorRating > 0.0 + damageTaken = Calculate(armorRating) + Else + damageTaken = 1.0 + EndIf + + damageResist = 100.0 - damageTaken*100.0 + + If UseSpell + UpdateDisplaySpell(damageResist) + EndIf + + If Debugging + printf("ArmorRating: "+armorRating) + Debug.Notification(strArmorRating +": "+armorRating) + printf("DamageResist: "+damageResist+"%") + EndIf + If Notify || Debugging + Debug.Notification(strDamageResist+": "+damageResist+"%") + EndIf + EndIf + + RegisterForSingleUpdate(timer) + Else + PlayerRef.removeSpell(J42_ARR_ARDisplaySpell) + EndIf +EndEvent + +Function UpdateDisplaySpell(float z) + J42_ARR_ARDisplaySpell.SetNthEffectMagnitude(0, z) + Utility.WaitMenuMode(0.1) + PlayerRef.addSpell(J42_ARR_ARDisplaySpell, false) +EndFunction + +float Function Calculate(float armorRating) + float damageTaken = 1.0 + + If Mode_var == 0 + damageTaken = 1.0 - (armorRating * linearMult_var) + If damageTaken < linearFloor_var + damageTaken = linearFloor_var + EndIf + ElseIf Mode_var == 1 + damageTaken = 1.0/(hyperDiv_var * armorRating + 1.0) + If UseMultExpHyp_var + damageTaken = 1.0-((1.0-damageTaken) * MultExpHyp_var) + EndIf + If UseFloorExpHyp_var && damageTaken < FloorExpHyp_var + damageTaken = FloorExpHyp_var + EndIf + ElseIf Mode_var == 2 + damageTaken = Math.pow(expBase_var, armorRating) + If UseMultExpHyp_var + damageTaken = 1.0-((1.0-damageTaken) * MultExpHyp_var) + EndIf + If UseFloorExpHyp_var && damageTaken < FloorExpHyp_var + damageTaken = FloorExpHyp_var + EndIf + EndIf + return damageTaken +EndFunction + +Function printf(string asMsg, bool abNotif = false) + If abNotif + debug.Notification("Armor Rating Redux: " + asMsg) + EndIf + debug.Trace(self +": "+ asMsg) +EndFunction + +Function StatusReport() + printf("=== STATUS REPORT START ===") + printf("Debugging: "+ Debugging) + printf("Notify: "+ Notify) + printf("UseSpell: "+ UseSpell) + printf("Mode: "+ Mode) + printf("linearMult: "+ linearMult) + printf("linearFloor: "+ linearFloor) + printf("hyperDiv: "+ hyperDiv) + printf("expBase: "+ expBase) + printf("UseFloorExpHyp: "+ UseFloorExpHyp) + printf("UseMultExpHyp: "+ UseMultExpHyp) + printf("FloorExpHyp: "+ FloorExpHyp) + printf("MultExpHyp: "+ MultExpHyp) + printf("timer: "+ timer) + printf("cachedValue: "+ cachedValue) + printf("strArmorRating: "+ strArmorRating) + printf("strDamageResist: "+ strDamageResist) + printf("J42_ARR_ARDisplaySpell: "+ J42_ARR_ARDisplaySpell) + printf("J42_ARR_ErrorBox: "+ J42_ARR_ErrorBox) + printf("PlayerRef: "+ PlayerRef) + printf("GS hiddenRating: "+ Game.GetGameSettingFloat(hiddenRating)) + printf("GS dmgMult: "+ Game.GetGameSettingFloat(dmgMult)) + printf("GS dmgFloor: "+ Game.GetGameSettingFloat(dmgFloor)) + printf("=== STATUS REPORT END ===") +EndFunction diff --git a/source/scripts/J42_ARR_SKSE_Bridge.psc b/source/scripts/J42_ARR_SKSE_Bridge.psc new file mode 100644 index 0000000..0577efe --- /dev/null +++ b/source/scripts/J42_ARR_SKSE_Bridge.psc @@ -0,0 +1,19 @@ +Scriptname J42_ARR_SKSE_Bridge + +;calling any of these without argument resets to default values + +Function setMode(int aInt = 1) Global Native +{ 0 := linear + 1 := hyperbolic + 2 := exponential} + +Function setLinearMult(float aFloat = 0.0012) Global Native +Function setLinearFloor(float aFloat = 0.2000) Global Native +Function setHyperDiv(float aFloat = 0.0060) Global Native +Function setExpBase(float aFloat = 0.9976) Global Native + +Function setUseFloorExpHyp(bool aBool = false) Global Native +Function setUseMultExpHyp(bool aBool = false) Global Native + +Function setFloorExpHyp(float aFloat = 0.00) Global Native +Function setMultExpHyp(float aFloat = 1.00) Global Native