Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions EEex/copy/EEex_scripts/EEex_Opcode_Patch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,120 @@
]]}
)

--[[
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Opcode #261 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| (special & 1) != 0 -> Process only m_effectAmount; do not continue the vanilla lower-level fallback loop |
| (special & 2) != 0 -> Replace the vanilla first eligible memorized spell at the current level with a random eligible spell from that same level |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| [EEex.dll] EEex::Opcode_Hook_Op261_SelectRandomSpell( |
| pEffect: CGameEffect*, pSprite: CGameSprite*, pVanillaSpell: CCreatureFileMemorizedSpell*, |
| pMemorizedList: CTypedPtrList<CPtrList, CCreatureFileMemorizedSpell*>*) -> CCreatureFileMemorizedSpell* |
| return -> pVanillaSpell unless bit1 applies and the current mage / priest list has a random eligible replacement |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| [EEex.dll] EEex::Opcode_Hook_Op261_ShouldStopAfterCurrentLevel(pEffect: CGameEffect*) -> bool |
| return -> true when bit0 is set |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
--]]

------------------------------------------------------------
-- [EEex.dll] EEex::Opcode_Hook_Op261_SelectRandomSpell() --
------------------------------------------------------------

-- Mage path: RBX is the first CCreatureFileMemorizedSpell* whose m_flags bit0 is clear.
-- R14 -> CGameEffect, RDI -> CGameSprite, and R12 is the vanilla per-level list offset.
-- The C++ helper preserves vanilla behavior for class 19 / 21 empty-resref special paths.
EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameEffectRememorizeSpell::ApplyEffect()-MageSelectedSpell"), 0, 6, 6, {
{"hook_integrity_watchdog_ignore_registers", {
EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RBX, EEex_HookIntegrityWatchdogRegister.RCX,
EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9,
EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11
}}},
{[[
#MAKE_SHADOW_SPACE(32)
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9

mov rcx, r14 ; pEffect
mov rdx, rdi ; pSprite
mov r8, rbx ; pVanillaSpell
lea r9, qword ptr ds:[r12+rdi+#OFFSET_OF(CGameSprite.m_memorizedSpellsMage)] ; pMemorizedList for current mage level
call #L(EEex::Opcode_Hook_Op261_SelectRandomSpell)
mov rbx, rax ; Use either vanilla or random selected spell

mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)]
mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)]
mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)]
mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)]
#DESTROY_SHADOW_SPACE
]]}
)

-- Priest path: same register ownership as the mage path, but the current list is m_memorizedSpellsPriest.
EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameEffectRememorizeSpell::ApplyEffect()-PriestSelectedSpell"), 0, 6, 6, {
{"hook_integrity_watchdog_ignore_registers", {
EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RBX, EEex_HookIntegrityWatchdogRegister.RCX,
EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9,
EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11
}}},
{[[
#MAKE_SHADOW_SPACE(32)
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9

mov rcx, r14 ; pEffect
mov rdx, rdi ; pSprite
mov r8, rbx ; pVanillaSpell
lea r9, qword ptr ds:[r12+rdi+#OFFSET_OF(CGameSprite.m_memorizedSpellsPriest)] ; pMemorizedList for current priest level
call #L(EEex::Opcode_Hook_Op261_SelectRandomSpell)
mov rbx, rax ; Use either vanilla or random selected spell

mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)]
mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)]
mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)]
mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)]
#DESTROY_SHADOW_SPACE
]]}
)

----------------------------------------------------------------------
-- [EEex.dll] EEex::Opcode_Hook_Op261_ShouldStopAfterCurrentLevel() --
----------------------------------------------------------------------

local op261StrictLevelLoopHook = function(labelName)
-- The hook site is after vanilla decrements ESI for the next lower level and before it adjusts R12/R13.
-- If bit0 is set, clear ESI so the untouched vanilla "test esi, esi / jg loop" falls through to completion.
EEex_HookBeforeRestoreWithLabels(EEex_Label(labelName), 0, 8, 8, {
{"hook_integrity_watchdog_ignore_registers", {
EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX,
EEex_HookIntegrityWatchdogRegister.RSI, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9,
EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11
}}},
{[[
#MAKE_SHADOW_SPACE(8)
mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx

mov rcx, r14 ; pEffect
call #L(EEex::Opcode_Hook_Op261_ShouldStopAfterCurrentLevel)
test al, al

mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)]
#DESTROY_SHADOW_SPACE
jz #L(return)

xor esi, esi ; Force vanilla loop condition to fail
]]}
)
end

op261StrictLevelLoopHook("Hook-CGameEffectRememorizeSpell::ApplyEffect()-MageAfterLevelDecrement")
op261StrictLevelLoopHook("Hook-CGameEffectRememorizeSpell::ApplyEffect()-PriestAfterLevelDecrement")

--[[
+------------------------------------------------------------------------------------------------------+
| Opcode #280 |
Expand Down
16 changes: 16 additions & 0 deletions EEex/loader/InfinityLoader.db
Original file line number Diff line number Diff line change
Expand Up @@ -2251,6 +2251,22 @@ Operations=ADD -43
Pattern=488D8FC81C0000
Operations=ADD 20

[Hook-CGameEffectRememorizeSpell::ApplyEffect()-MageAfterLevelDecrement]
Pattern=0F8FEEFEFFFFE95B010000
Operations=ADD -10

[Hook-CGameEffectRememorizeSpell::ApplyEffect()-MageSelectedSpell]
Pattern=0F8E8D020000
Operations=ADD 91

[Hook-CGameEffectRememorizeSpell::ApplyEffect()-PriestAfterLevelDecrement]
Pattern=41899E14010000
Operations=ADD -16

[Hook-CGameEffectRememorizeSpell::ApplyEffect()-PriestSelectedSpell]
Pattern=85C00F8E2E010000
Operations=ADD 94

[Hook-CGameEffectSaveVsDeath::ApplyEffect()-ImmediateSaveWriteOffset]
Pattern=0F852E0100000FB687AC050000
Operations=ADD -8
Expand Down