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