Skip to content

Commit da34aec

Browse files
committed
ExtraSource for takedmg callbacks (the laser!)
1 parent 4059491 commit da34aec

File tree

8 files changed

+139
-28
lines changed

8 files changed

+139
-28
lines changed

changelog.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ Modified:
117117
* ModCallbacks:
118118
- MC_POST_PLAYER_DROP_TRINKET
119119
Added DroppedTrinket param.
120+
- MC_(POST_)ENTITY_TAKE_DMG
121+
Added ExtraSource param. This is an additional EntityRef provided in specific situations where the "Source" of the damage may be indirect.
122+
For example, lasers and melee hitboxes provide their parent (ie, the player) instead of themselves as the damage Source. The laser/knife will now be passed as the ExtraSource!
123+
Currently supported: lasers, melee hitbox knives, gello, and brimstone balls.
124+
Note that ExtraSource is nil if not available.
120125
/newline/
121126
Fixes:
122127
* ModCallbacks:

libzhl/functions/ASM.zhl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ asm SplitTears_PreAngelicPrism "8b06ff50??c785????????00000000";
107107
asm SplitTears_PostAngelicPrism "0f8c????????8bbd????????8bb5????????8b8d";
108108
asm SplitTears_AngelicPrismOriginalTearOffset "8bb5????????81ff";
109109

110+
// ExtraSource for TakeDamage
111+
asm UpdateBoneSwingDamage "6a1e51ffb5????????8b40??ffb5????????518bcff30f110424ffd084c00f84????????6a008bcf";
112+
asm UpdateBoneSwingDamageFireplace "6a1e516a006a00518bcfc7042400007a44ff50??c685????????01";
113+
asm LaserDoDamagePushEbxA "0f1083????????5383ec14"; // 0x7
114+
asm LaserDoDamagePushEbxB "8b57??a1????????83fa3e"; // 0x8
115+
asm LaserDoDamageA "6a1e526a008b01680808000051c704240000803f"; // 0x14
116+
asm LaserDoDamageB "8b076a1e51535651f30f1104248bcf"; // 0xF
117+
asm LaserDoDamageExit "8b4d??5f5e33cd5be8????????8be55dc20400??????????8d81";
118+
asm LaserDoDamagePlayer "6a1e516a006a08518bc8c704240000803fff52??5f"; // 0x11 + 0x3
119+
asm GelloDamageEbpOffset "8b85????????8d8d????????ffb0????????e8????????8b068d8d????????6a1e"; // +0x2, int
120+
asm GelloDamage "6a1e51ffb5????????8b40??ffb5????????518bce"; // 0x1A + 0x2
121+
asm GelloDamageFireplace "6a1e516a006a00518bcec7042400007a44ff50??0f57c9"; // 0x11 + 0x3
122+
asm BrimstoneBallDamageEbpOffset "8b8d????????8b91????????528d8d????????e8????????6a1e8d85????????508b8d????????518b95????????5251f30f1085????????f30f1104248b85????????8b108b8d????????8b42??ffd0e9"; // +0x2, int
123+
asm BrimstoneBallDamage "6a1e8d85????????508b8d????????518b95????????5251f30f1085????????f30f1104248b85????????8b108b8d????????8b42??ffd0e9"; // 0x36 + 0x2
124+
110125
asm InlinedGetStatusEffectTarget_Baited "e8????????84c074??8b8f????????85c974??6690";
111126
asm InlinedGetStatusEffectTarget_Bleeding "e8????????84c074??8b8f????????85c974??8bf18b8e????????85c975??85d20f84????????8b86????????83e00183c8000f85????????3986????????0f8f????????8b8e????????85d27e??8d04??b978000000";
112127
asm InlinedGetStatusEffectTarget_BrimstoneMark "e8????????84c074??8b8e????????85c974??660f1f84??00000000";
@@ -143,7 +158,7 @@ asm TryAddToBagOfCraftingBelialEffectEspOffset "c64424??0083fb1e";
143158
asm TryAddToBagOfCraftingAccessArray "8b54????85d274??83f808";
144159
asm TryAddToBagOfCraftingArrayLength "83f90272??894424";
145160

146-
asm PostMeleeHitboxInit "8b078bcfff50??8b4d??85c9";
161+
asm PostMeleeHitboxInit "ffb5????????8bcfe8????????0fb786";
147162

148163
asm PreBombApplyTearFlagEffects "8b43??0b43??75??8b43??0b43??0f84";
149164

libzhl/functions/EntityRef.zhl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ __thiscall void EntityRef::constructor(Entity* entity);
44
struct EntityRef depends (Vector)
55
{
66
{{
7+
EntityRef() { this->constructor(nullptr); }
78
EntityRef(Entity* entity) { this->constructor(entity); }
89
}}
910
int _type : 0x0; //TODO: EntityType enum

repentogon/Patches/ASMPatches.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@ void PerformASMPatches() {
173173
ASMCallbacks::detail::ApplyPatches();
174174
PatchPreSampleLaserCollision();
175175
PatchPreLaserCollision();
176-
PatchPreEntityTakeDamageCallbacks();
177-
PatchPostEntityTakeDamageCallbacks();
176+
PatchEntityTakeDamageCallbacks();
178177
ASMPatchPrePlayerUseBomb();
179178
ASMPatchPostPlayerUseBomb();
180179
ASMPatchPreMMorphActiveCallback();

repentogon/Patches/ASMPatches/ASMCallbacks.cpp

Lines changed: 111 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,88 @@ void PatchPreLaserCollision() {
183183

184184
// End of PRE_LASER_COLLISION patches
185185

186+
187+
// ---------- Beginning of MC_(POST_)ENTITY_TAKE_DMG related patches --------------------------------------------------
188+
189+
// Place to temporarily store "extra sources" to pass to the TakeDamage callbacks.
190+
// These refs are keyed by the address of the primary EntityRef created for, and passed to, the TakeDamage call.
191+
// An entry in this map is added right before the TakeDamage call, and removed immediately afterwards,
192+
// allowing access to the extra source for the duration of the TakeDamage call.
193+
// Should not be modified outside of `TakeDamageWithExtraSource`.
194+
static std::unordered_map<uintptr_t, EntityRef> _TakeDamageExtraSources;
195+
196+
// Fetch the extra source for a TakeDamage source, if any exists.
197+
static EntityRef* GetExtraTakeDamageSource(EntityRef* source) {
198+
if (source && _TakeDamageExtraSources.find((uintptr_t)source) != _TakeDamageExtraSources.end()) {
199+
EntityRef* extraSource = &_TakeDamageExtraSources[(uintptr_t)source];
200+
if (extraSource && extraSource->_entity != source->_entity) {
201+
return extraSource;
202+
}
203+
}
204+
return nullptr;
205+
}
206+
207+
// Call TakeDamage with an "extra source", available via the standard "source" for the TAKE_DMG callbacks.
208+
// Patches are used to overwrite relevant TakeDamage calls with this one that enables the extra source.
209+
bool __stdcall TakeDamageWithExtraSource(Entity* damagedEntity, float damage, uint64_t damageFlags, EntityRef* source, int damageCountdown, Entity* extraSourceEntity) {
210+
_TakeDamageExtraSources[(uintptr_t)source] = EntityRef(extraSourceEntity);
211+
bool result = damagedEntity->TakeDamage(damage, damageFlags, source, damageCountdown);
212+
_TakeDamageExtraSources.erase((uintptr_t)source);
213+
return result;
214+
}
215+
void PatchLaserDoDamageExtraSourcePushEbx(char* asmDef, int overriddenBytes) {
216+
void* addr = sASMDefinitionHolder->GetDefinition(asmDef);
217+
printf("[REPENTOGON] Patching Entity_Laser::DoDamage at %p\n", addr);
218+
ASMPatch patch;
219+
patch.Push(ASMPatch::Registers::EBX)
220+
.AddBytes(ByteBuffer().AddAny((char*)addr, overriddenBytes))
221+
.AddRelativeJump((char*)addr + overriddenBytes);
222+
sASMPatcher.PatchAt(addr, &patch);
223+
}
224+
void PatchLaserDoDamageExtraSource(char* asmDef, int bytesToCopy) {
225+
void* addr = sASMDefinitionHolder->GetDefinition(asmDef);
226+
void* exitAddr = sASMDefinitionHolder->GetDefinition(&AsmDefinitions::LaserDoDamageExit);
227+
228+
printf("[REPENTOGON] Patching Entity::TakeDamage call in Entity_Laser::DoDamage at %p\n", addr);
229+
230+
ASMPatch::SavedRegisters savedRegisters((ASMPatch::SavedRegisters::Registers::GP_REGISTERS_STACKLESS | ASMPatch::SavedRegisters::Registers::XMM_REGISTERS) & ~ASMPatch::SavedRegisters::Registers::EAX, true);
231+
ASMPatch patch;
232+
patch.Pop(ASMPatch::Registers::EAX)
233+
.PreserveRegisters(savedRegisters)
234+
.Push(ASMPatch::Registers::EAX)
235+
.AddBytes(ByteBuffer().AddAny((char*)addr, bytesToCopy))
236+
.Push(ASMPatch::Registers::ECX)
237+
.AddInternalCall(TakeDamageWithExtraSource)
238+
.RestoreRegisters(savedRegisters)
239+
.AddRelativeJump(exitAddr);
240+
sASMPatcher.PatchAt(addr, &patch);
241+
}
242+
void PatchTakeDamageWithExtraSource(char* desc, char* asmDef, ASMPatch::Registers extraSourceRegister, char* offsetAsmDef, int bytesToCopy, int addJump) {
243+
void* addr = sASMDefinitionHolder->GetDefinition(asmDef);
244+
245+
int offset = 0;
246+
if (offsetAsmDef) {
247+
offset = *(int*)((char*)sASMDefinitionHolder->GetDefinition(offsetAsmDef) + 0x2);
248+
}
249+
250+
printf("[REPENTOGON] Patching Entity::TakeDamage call for ExtraSource (%s) at %p\n", desc, addr);
251+
252+
ASMPatch::SavedRegisters savedRegisters((ASMPatch::SavedRegisters::Registers::GP_REGISTERS_STACKLESS | ASMPatch::SavedRegisters::Registers::XMM_REGISTERS) & ~ASMPatch::SavedRegisters::Registers::EAX, true);
253+
ASMPatch patch;
254+
patch.PreserveRegisters(savedRegisters);
255+
if (offsetAsmDef) {
256+
patch.Push(extraSourceRegister, offset);
257+
} else {
258+
patch.Push(extraSourceRegister);
259+
}
260+
patch.AddBytes(ByteBuffer().AddAny((char*)addr, bytesToCopy))
261+
.Push(ASMPatch::Registers::ECX)
262+
.AddInternalCall(TakeDamageWithExtraSource)
263+
.RestoreRegisters(savedRegisters)
264+
.AddRelativeJump((char*)addr + bytesToCopy + addJump);
265+
sASMPatcher.PatchAt(addr, &patch);
266+
}
267+
186268
/* * MC_ENTITY_TAKE_DMG REIMPLEMENTATION * *
187269
* This section patches in a new ENTITY_TAKE_DMG callback with table returns for overridding.
188270
* A patch is used because ENTITY_TAKE_DMG runs in the middle of the TakeDamage functions,
@@ -192,8 +274,9 @@ void PatchPreLaserCollision() {
192274
* We need to patch into both Entity::TakeDamage AND EntityPlayer::TakeDamage.
193275
*/
194276
bool __stdcall ProcessPreDamageCallback(Entity* entity, float* damage, int* damageHearts, uint64_t* damageFlags, EntityRef* source, int* damageCountdown, const bool isPlayer) {
195-
const int callbackid = 11;
277+
EntityRef* extraSource = GetExtraTakeDamageSource(source);
196278

279+
const int callbackid = 11;
197280
if (VanillaCallbackState.test(callbackid)) {
198281
lua_State* L = g_LuaEngine->_state;
199282
lua::LuaStackProtector protector(L);
@@ -212,7 +295,8 @@ bool __stdcall ProcessPreDamageCallback(Entity* entity, float* damage, int* dama
212295
}
213296
caller.push(*damageFlags)
214297
.push(source, lua::Metatables::ENTITY_REF)
215-
.push(*damageCountdown);
298+
.push(*damageCountdown)
299+
.push(extraSource, lua::Metatables::ENTITY_REF);
216300
lua::LuaResults lua_result = caller.call(1);
217301

218302
if (!lua_result) {
@@ -283,7 +367,6 @@ bool __stdcall ProcessPreDamageCallback(Entity* entity, float* damage, int* dama
283367

284368
return true;
285369
}
286-
287370
void InjectPreDamageCallback(void* addr, bool isPlayer) {
288371
printf("[REPENTOGON] Patching %s::TakeDamage for MC_ENTITY_TAKE_DMG at %p\n", isPlayer ? "Entity_Player" : "Entity", addr);
289372

@@ -337,26 +420,18 @@ void InjectPreDamageCallback(void* addr, bool isPlayer) {
337420
sASMPatcher.PatchAt(addr, &patch);
338421
}
339422

340-
// Patches overtop the existing calls to LuaEngine::Callback::EntityTakeDamage,
341-
// within the Entity::TakeDamage and EntityPlayer::TakeDamage functions respectively.
342-
void PatchPreEntityTakeDamageCallbacks() {
343-
InjectPreDamageCallback(sASMDefinitionHolder->GetDefinition(&AsmDefinitions::TakeDmg_PreEntityCallback), false);
344-
InjectPreDamageCallback(sASMDefinitionHolder->GetDefinition(&AsmDefinitions::TakeDmg_PrePlayerCallback), true);
345-
}
346-
347423
// Even though I patched overtop the calls to this callback, super kill it for good measure.
348424
HOOK_METHOD(LuaEngine, EntityTakeDamage, (Entity* entity, float damage, unsigned long long damageFlags, EntityRef* source, int damageCountdown) -> bool) { return true; }
349425

350-
// End of ENTITY_TAKE_DMG patches
351-
352426
/* * MC_POST_ENTITY_TAKE_DMG * *
353427
* A hook worked just fine for ""POST_TAKE_DMG"" initially, but now that we support overidding
354428
* values via ENTITY_TAKE_DMG (see above) we need the POST_TAKE_DMG callback to actually show
355429
* those modified values. That means another patch!!
356430
* Like before, we need to patch into both Entity::TakeDamage and EntityPlayer::TakeDamage.
357431
*/
358-
359432
void __stdcall ProcessPostDamageCallback(Entity* entity, const float damage, const uint64_t damageFlags, EntityRef* source, const int damageCountdown, const bool isPlayer) {
433+
EntityRef* extraSource = GetExtraTakeDamageSource(source);
434+
360435
const int callbackid = 1006;
361436
if (CallbackState.test(callbackid - 1000)) {
362437
if (isPlayer && source->_type == 33 && source->_variant == 4) {
@@ -381,10 +456,10 @@ void __stdcall ProcessPostDamageCallback(Entity* entity, const float damage, con
381456
caller.push(damageFlags)
382457
.push(source, lua::Metatables::ENTITY_REF)
383458
.push(damageCountdown)
459+
.push(extraSource, lua::Metatables::ENTITY_REF)
384460
.call(1);
385461
}
386462
};
387-
388463
void InjectPostDamageCallback(void* addr, bool isPlayer) {
389464
printf("[REPENTOGON] Patching %s::TakeDamage for MC_POST_ENTITY_TAKE_DMG at %p\n", isPlayer ? "Entity_Player" : "Entity", addr);
390465
const int numOverriddenBytes = (isPlayer ? 10 : 5);
@@ -426,15 +501,32 @@ void InjectPostDamageCallback(void* addr, bool isPlayer) {
426501
sASMPatcher.PatchAt(addr, &patch);
427502
}
428503

429-
// These patches overwrite suitably-sized commands near where the respective TakeDamage functions would return.
430-
// The overridden bytes are restored by the patch.
431-
void PatchPostEntityTakeDamageCallbacks() {
432-
// Sig is 6 bytes before where we actually want to patch to avoid bleeding past the end of the function.
504+
void PatchEntityTakeDamageCallbacks() {
505+
// Patches overtop the existing calls to LuaEngine::Callback::EntityTakeDamage,
506+
// within the Entity::TakeDamage and EntityPlayer::TakeDamage functions respectively.
507+
InjectPreDamageCallback(sASMDefinitionHolder->GetDefinition(&AsmDefinitions::TakeDmg_PreEntityCallback), false);
508+
InjectPreDamageCallback(sASMDefinitionHolder->GetDefinition(&AsmDefinitions::TakeDmg_PrePlayerCallback), true);
509+
510+
// These patches overwrite suitably-sized commands near where the respective TakeDamage functions would return.
511+
// The overridden bytes are restored by the patch.
512+
// First sig is 6 bytes before where we actually want to patch to avoid bleeding past the end of the function.
433513
InjectPostDamageCallback((char*)sASMDefinitionHolder->GetDefinition(&AsmDefinitions::TakeDmg_PostEntityCallback) + 0x6, false);
434514
InjectPostDamageCallback(sASMDefinitionHolder->GetDefinition(&AsmDefinitions::TakeDmg_PostPlayerCallback), true);
515+
516+
// Patches over TakeDamage calls to allow providing an ExtraSource.
517+
PatchLaserDoDamageExtraSourcePushEbx(&AsmDefinitions::LaserDoDamagePushEbxA, 0x7);
518+
PatchLaserDoDamageExtraSourcePushEbx(&AsmDefinitions::LaserDoDamagePushEbxB, 0x8);
519+
PatchLaserDoDamageExtraSource(&AsmDefinitions::LaserDoDamageA, 0x14);
520+
PatchLaserDoDamageExtraSource(&AsmDefinitions::LaserDoDamageB, 0xF);
521+
PatchTakeDamageWithExtraSource("Entity_Laser::do_damage, player", &AsmDefinitions::LaserDoDamagePlayer, ASMPatch::Registers::EBX, nullptr, 0x11, 0x3);
522+
PatchTakeDamageWithExtraSource("Entity_Knife::update_bone_swing", &AsmDefinitions::UpdateBoneSwingDamage, ASMPatch::Registers::ESI, nullptr, 0x1A, 0x2);
523+
PatchTakeDamageWithExtraSource("Entity_Knife::update_bone_swing, fireplace", &AsmDefinitions::UpdateBoneSwingDamageFireplace, ASMPatch::Registers::ESI, nullptr, 0x11, 0x3);
524+
PatchTakeDamageWithExtraSource("Entity_Familiar::ai_umbilical_baby", &AsmDefinitions::GelloDamage, ASMPatch::Registers::EBP, &AsmDefinitions::GelloDamageEbpOffset, 0x1A, 0x2);
525+
PatchTakeDamageWithExtraSource("Entity_Familiar::ai_umbilical_baby, fireplace", &AsmDefinitions::GelloDamageFireplace, ASMPatch::Registers::EBP, &AsmDefinitions::GelloDamageEbpOffset, 0x11, 0x3);
526+
PatchTakeDamageWithExtraSource("Entity_Effect::Update, Brimstone Ball", &AsmDefinitions::BrimstoneBallDamage, ASMPatch::Registers::EBP, &AsmDefinitions::BrimstoneBallDamageEbpOffset, 0x36, 0x2);
435527
}
436528

437-
// End of POST_ENTITY_TAKE_DMG patches
529+
// ---------- End of MC_(POST_)ENTITY_TAKE_DMG related patches --------------------------------------------------
438530

439531

440532
// MC_PRE_PLAYER_USE_BOMB

repentogon/Patches/ASMPatches/ASMCallbacks.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ bool RunCanSelectCharacterCallback(int playerType, bool isBeingSelected);
88

99
void PatchPreSampleLaserCollision();
1010
void PatchPreLaserCollision();
11-
void PatchPreEntityTakeDamageCallbacks();
12-
void PatchPostEntityTakeDamageCallbacks();
11+
void PatchEntityTakeDamageCallbacks();
1312
void ASMPatchPrePlayerUseBomb();
1413
void ASMPatchPostPlayerUseBomb();
1514
void ASMPatchBombPreApplyTearflagEffects();

repentogon/Patches/ASMPatches/ASMEntityKnife.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ void __stdcall RunMeleeHitboxCallback(Entity_Knife* hitboxKnife, Entity_Knife* m
1212
if (entityPlus) {
1313
entityPlus->hitboxSource.SetReference(mainKnife);
1414
}
15-
hitboxKnife->Update(); // Patch overwrites an update call.
1615
}
1716
void PatchMeleeHitboxInit() {
1817
void* addr = sASMDefinitionHolder->GetDefinition(&AsmDefinitions::PostMeleeHitboxInit);
@@ -26,6 +25,7 @@ void PatchMeleeHitboxInit() {
2625
.Push(ASMPatch::Registers::EDI) // "Hitbox" Knife
2726
.AddInternalCall(RunMeleeHitboxCallback)
2827
.RestoreRegisters(savedRegisters)
29-
.AddRelativeJump((char*)addr + 0x7);
28+
.AddBytes(ByteBuffer().AddAny((char*)addr, 0x6)) // Restore overwritten bytes
29+
.AddRelativeJump((char*)addr + 0x6);
3030
sASMPatcher.PatchAt(addr, &patch);
3131
}

repentogon/resources/scripts/main_ex.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,11 +1387,11 @@ end
13871387

13881388
-- Custom handling for the MC_ENTITY_TAKE_DMG rewrite, so if a mod changes the damage amount etc that doesn't terminate the callback
13891389
-- and the updated values are shown to later callbacks. The callback also now ONLY terminates early if FALSE is returned.
1390-
function _RunEntityTakeDmgCallback(callbackID, param, entity, damage, damageFlags, source, damageCountdown)
1390+
function _RunEntityTakeDmgCallback(callbackID, param, entity, damage, damageFlags, source, damageCountdown, extraSource)
13911391
local combinedRet
13921392

13931393
for callback in GetCallbackIterator(callbackID, param) do
1394-
local ret = RunCallbackInternal(callbackID, callback, entity, damage, damageFlags, source, damageCountdown)
1394+
local ret = RunCallbackInternal(callbackID, callback, entity, damage, damageFlags, source, damageCountdown, extraSource)
13951395
if ret ~= nil then
13961396
if type(ret) == "boolean" and ret == false then
13971397
-- Only terminate the callback early if someone returns FALSE.

0 commit comments

Comments
 (0)