.meta visibility="all" .meta key="EnemyDamageSync" .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" .meta client_flag="0x0000001000000000" .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU entry_ptr: reloc0: .offsetof start write_call_to_code_multi: .include WriteCallToCodeMulti start: call write_static_patches call write_incr_hp_with_sync call write_6x0A_patch call write_6xE4_handler ret call_write_call_to_code: call write_call_to_code_multi ret write_6xE4_handler: push 0 # Absolute address, not call opcode push push 1 call +4 .deltaof handle_6xE4, handle_6xE4_end pop eax push dword [eax] call call_write_call_to_code handle_6xE4: # [std] (G_6xE4* cmd @ [esp + 4]) -> void push ebx push esi push edi test byte [], 0x80 jz handle_6xE4_return mov ebx, [esp + 0x10] # cmd movzx eax, word [ebx + 2] cmp eax, 0x1000 jl handle_6xE4_return cmp eax, 0x1B50 jge handle_6xE4_return mov edi, eax mov eax, call eax # TObjEnemy* ene = get_enemy_entity(cmd->header.entity_id); push eax movzx eax, word [ebx + 2] and eax, 0x0FFF imul eax, eax, 0x0C add eax, [] # eax = state_for_enemy(cmd->header.entity_id) cmp dword [ebx + 0x0C], 0 jl handle_6xE4_not_proportional mov cx, [ebx + 0x0A] # cmd->max_hp sub cx, [eax + 0x06] # st.total_damage movzx ecx, cx xor edx, edx cmp ecx, edx cmovl ecx, edx push ecx fild st0, dword [esp] # current_hp = static_cast(std::max(cmd->max_hp - st.total_damage, 0)) fld st0, dword [ebx + 0x0C] fmulp st1, st0 fistp dword [esp], st0 mov ecx, dword [esp] # adjusted_hit_amount = static_cast(current_hp * cmd->factor) add esp, 4 xor edx, edx inc edx cmp ecx, edx cmovl ecx, edx mov [ebx + 0x04], cx # cmd->hit_amount = std::min(1, adjusted_hit_amount) handle_6xE4_not_proportional: movzx edx, word [eax + 0x06] # st.total_damage movsx esi, word [ebx + 0x04] # cmd->hit_amount movzx edi, word [ebx + 0x0A] # cmd->max_hp add edx, esi # st.total_damage + cmd->hit_amount cmp edx, edi jl handle_6xE4_damage_less_than_max_hp mov [eax + 0x06], di # st.total_damage = cmd->max_hp; mov edx, [eax] test edx, 0x800 jnz handle_6xE4_return_pop_ene or edx, 0x800 mov [eax], edx cmp dword [esp], 0 je handle_6xE4_return_pop_ene push edx # out_cmd.flags sub esp, 8 mov word [esp], 0x030A # out_cmd.header.{subcommand,size} mov si, [ebx + 2] mov [esp + 2], si # out_cmd.header.entity_id and si, 0x0FFF mov [esp + 4], si # out_cmd.entity_index mov [esp + 6], di # out_cmd.total_damage mov ecx, esp push ecx # For handle_60 later mov ebx, [] # root_protocol test ebx, ebx jz handle_6xE4_root_protocol_missing mov eax, 0x0C mov edx, call edx # send_60(root_protocol, &out_cmd, sizeof(out_cmd)) handle_6xE4_root_protocol_missing: mov dword [], 1 mov eax, call eax # handle_60(&out_cmd) mov dword [], 0 add esp, 0x14 jmp handle_6xE4_return handle_6xE4_damage_less_than_max_hp: xor edi, edi cmp edx, edx cmovl edx, edi mov [eax + 0x06], dx # st.total_damage = std::max(st.total_damage + cmd->hit_amount, 0); mov esi, eax # esi = ene_st mov eax, [esp] # eax = ene test eax, eax jz handle_6xE4_return_pop_ene mov ecx, eax push esi mov edx, [ecx] call [edx + 0x138] # ene->vtable[0x4E](ene, &st); handle_6xE4_return_pop_ene: add esp, 4 handle_6xE4_return: pop edi pop esi pop ebx ret handle_6xE4_end: write_6x0A_patch: push 5 push push 1 call +4 .deltaof on_6x0A_patch_start, on_6x0A_patch_end pop eax push dword [eax] call call_write_call_to_code on_6x0A_patch_start: # (TObjectV004434c8* this @ eax, int16_t amount @ cx) -> bool @ eax test byte [], 0x80 jnz on_6x0A_patch_skip_write mov [esp + 0x16], ax on_6x0A_patch_skip_write: ret on_6x0A_patch_end: # Write TObjectV004434c8::incr_hp_with_sync write_incr_hp_with_sync: push 5 push # v17 inlined callsite + 5 push 5 push # TObjectV004434c8::subtract_hp_if_not_in_state_2 + D push 5 push # TObjectV004434c8::v18_accept_hit (presumably Resta) - this is add_hp, not subtract_hp! push 5 push # TObjectV004434c8::v18_accept_hit cases 0 and 4 push 5 push # TObjectV004434c8::v18_accept_hit case 1 push 5 push # TObjectV004434c8::v18_accept_hit case 2 push 5 push # TObjectV004434c8::v18_accept_hit case 3 push 5 push # TObjectV004434c8::v18_accept_hit case 0x13 push 5 push # TObjectV004434c8::v18_accept_hit case 0x15 push 5 push # TObjectV004434c8::v19_handle_hit_special_effects case 1 push 5 push # TObjectV004434c8::v19_handle_hit_special_effects case 1 push 5 push # TObjectV004434c8::v19_handle_hit_special_effects case 6 push 5 push # TObjectV004434c8::v19_handle_hit_special_effects case 9 push 5 push # TObjectV004434c8::v19_handle_hit_special_effects case 0x0A push 5 push # TObjectV004434c8::v19_handle_hit_special_effects case 0x0D push 15 call +4 .deltaof on_add_or_subtract_hp_start, on_add_or_subtract_hp_end pop eax push dword [eax] call call_write_call_to_code on_add_or_subtract_hp_start: # (TObjectV004434c8* this @ eax, int16_t amount @ cx) -> bool @ eax # Check if callsite is subtract_hp_if_not_in_state_2 push eax push ecx push ebx test byte [], 0x80 jz on_add_or_subtract_hp_skip_send movzx edx, word [eax + 0x1C] # ene->entity_id cmp edx, 0x1000 jl on_add_or_subtract_hp_skip_send cmp edx, 0x1B50 jge on_add_or_subtract_hp_skip_send and edx, 0x0FFF imul edx, edx, 0x0C add edx, [] # eax = state_for_enemy(cmd->header.entity_id) sub esp, 0x10 mov word [esp], 0x04E4 mov bx, [eax + 0x1C] mov [esp + 0x02], bx # cmd.entity_id cmp dword [esp + 0x1C], # Check if callsite is add_hp jne on_add_or_subtract_hp_skip_negate_amount neg cx on_add_or_subtract_hp_skip_negate_amount: mov [esp + 0x04], cx # cmd.hit_amount mov bx, [edx + 6] mov [esp + 0x06], bx # cmd.total_damage_before_hit mov bx, [eax + 0x0330] mov [esp + 0x08], bx # cmd.current_hp mov bx, [eax + 0x02BC] mov [esp + 0x0A], bx # cmd.max_hp mov dword [esp + 0x0C], 0xBF800000 # cmd.factor cmp dword [esp + 0x1C], # Check if callsite is Devil's/Demon's jne on_add_or_subtract_hp_not_proportional # esp is 0x20 down from where it is in caller's context mov cx, 100 sub cx, [esp + 0x34] # cx = (100 - special_amount) movsx ecx, cx push ecx fild st0, dword [esp] # current_hp_factor = static_cast(100 - special_amount) fmul st0, dword [esp + 0x3C] # *= weapon_reduction_factor mov dword [esp], 0x42C80000 # 100.0f fdiv st0, dword [esp] add esp, 4 fstp dword [esp + 0x0C], st0 # cmd.factor = ((100 - special_amount) * weapon_reduction_factor) / 100 on_add_or_subtract_hp_not_proportional: mov ecx, esp mov ebx, [] # root_protocol test ebx, ebx jz on_add_or_subtract_hp_skip_send mov eax, 0x10 # Can't just `call ` here because this code is relocated at apply time mov edx, call edx # send_60(root_protocol, &out_cmd, sizeof(out_cmd)) add esp, 0x10 on_add_or_subtract_hp_skip_send: mov edx, # subtract_hp mov eax, # add_hp cmp dword [esp + 0x0C], # Check if callsite is add_hp cmove edx, eax pop ebx pop ecx pop eax jmp edx on_add_or_subtract_hp_end: write_static_patches: .include WriteCodeBlocks .data .data 9 flag_check_start: test byte [], 0x01 jz +0x38 flag_check_end: # Replace 6x09 handler with 6xE4 .data .data 4 .data 0x000600E4 # subcommand=0xE4, flags=6 # Handler address written by write_6xE4_handler # Rewrite TObjectV004434c8::subtract_hp_if_not_in_state_2 .data .deltaof on_subtract_hp_if_not_in_state_2_start, on_subtract_hp_if_not_in_state_2_end .address on_subtract_hp_if_not_in_state_2_start: # (TObjectV004434c8* this @ eax, int16_t amount @ cx) -> bool @ eax cmp word [eax + 0x328], 2 jne on_subtract_hp_if_not_in_state_2_do_subtract xor eax, eax ret on_subtract_hp_if_not_in_state_2_do_subtract: call -1 # Overwritten by write_call_to_code_multi later ret on_subtract_hp_if_not_in_state_2_end: # Inlined callsite of subtract_hp in TObjectV004434c8::v17 .data .deltaof v17_subtract_hp_inlined_callsite_start, v17_subtract_hp_inlined_callsite_end .address v17_subtract_hp_inlined_callsite_start: # This must assemble to exactly 0x1A bytes. There is a vfn call shortly after this, and fortunately it appears eax, # ecx, and edx are not used before then, so we don't have to save any registers here; we just have to move the args # into the right places. mov cx, ax mov eax, edi call -1 # Overwritten by write_call_to_code_multi later jmp v17_subtract_hp_inlined_callsite_end int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 int 3 v17_subtract_hp_inlined_callsite_end: .data 0x00000000 .data 0x00000000