mirror of
https://github.com/ApfelTeeSaft/Perfectium.git
synced 2026-08-26 19:33:36 +00:00
game logic — abilities, teams, inventory, spawners, building hooks
This commit is contained in:
@@ -162,11 +162,20 @@ EXTERNDEF Game_GetDeathCause :PROC ; (report:PTR FFortPlayerDeathRe
|
||||
; logic/inventory.asm
|
||||
EXTERNDEF Inventory_Update :PROC ; (pc:QWORD, dirty:DWORD, bRemovedItem:BYTE)
|
||||
EXTERNDEF Inventory_IsValidGuid :PROC ; (pc:QWORD, guid:PTR FGuid) -> BYTE
|
||||
EXTERNDEF Inventory_IsGuidInInventory :PROC ; (pc:QWORD, guid:PTR FGuid) -> BYTE
|
||||
EXTERNDEF Inventory_GetEntryInSlot :PROC ; (pc:QWORD, slot:DWORD, item:DWORD, bars:DWORD) -> PTR FFortItemEntry
|
||||
EXTERNDEF Inventory_GetDefinitionInSlot :PROC ; (pc:QWORD, slot:DWORD, item:DWORD, bars:DWORD) -> PTR UFortWorldItemDefinition
|
||||
EXTERNDEF Inventory_GetInstanceFromGuid :PROC ; (pc:QWORD, guid:PTR FGuid) -> PTR UFortWorldItem
|
||||
EXTERNDEF Inventory_FindItemInInventory :PROC ; (pc:QWORD, class:QWORD, bFound:PTR BYTE) -> PTR FFortItemEntry
|
||||
EXTERNDEF Inventory_AddItemToSlot :PROC ; (pc:QWORD, def:QWORD, slot:DWORD, bars:DWORD, count:DWORD) -> PTR FFortItemEntry
|
||||
EXTERNDEF Inventory_RemoveItemFromSlot :PROC ; (pc:QWORD, slot:DWORD, bars:DWORD, amount:DWORD)
|
||||
EXTERNDEF Inventory_IsGuidInInventory :PROC ; (pc:QWORD, guid:PTR FGuid) -> BYTE
|
||||
EXTERNDEF Inventory_EquipWeaponDefinition :PROC ; (pawn:QWORD, def:QWORD, guid:PTR FGuid)
|
||||
EXTERNDEF Inventory_EquipInventoryItem :PROC ; (pc:QWORD, guid:PTR FGuid)
|
||||
EXTERNDEF Inventory_EquipLoadout :PROC ; (pc:QWORD, wids:PTR QWORD[6])
|
||||
EXTERNDEF Inventory_OnDrop :PROC ; (pc:QWORD, params:PTR) -> BYTE
|
||||
EXTERNDEF Inventory_OnPickup :PROC ; (pc:QWORD, params:PTR)
|
||||
EXTERNDEF Inventory_Init :PROC ; (pc:QWORD)
|
||||
|
||||
; logic/abilities.asm
|
||||
EXTERNDEF Abilities_TryActivateAbility :PROC
|
||||
@@ -176,6 +185,7 @@ EXTERNDEF Abilities_ConsumeAllReplicatedData :PROC
|
||||
|
||||
; logic/spawners.asm
|
||||
EXTERNDEF Spawners_SpawnActor :PROC ; (actorClass:QWORD, location:PTR FVector, owner:QWORD) -> QWORD
|
||||
EXTERNDEF Spawners_SpawnActor_ByLocation :PROC ; (actorClass:QWORD, location:PTR FVector, rotation:PTR FRotator, owner:QWORD) -> QWORD
|
||||
EXTERNDEF Spawners_SummonPickup :PROC ; (pawn:QWORD, itemDef:QWORD, count:DWORD, loc:PTR FVector) -> QWORD
|
||||
EXTERNDEF Spawners_SummonPickupFromChest :PROC ; (itemDef:QWORD, count:DWORD, loc:PTR FVector)
|
||||
EXTERNDEF Spawners_SpawnPickupFromFloor :PROC ; (itemDef:QWORD, count:DWORD, loc:PTR FVector)
|
||||
@@ -247,3 +257,4 @@ EXTRN memcpy :PROC
|
||||
EXTRN memmove :PROC
|
||||
EXTRN memset :PROC
|
||||
EXTRN memcmp :PROC
|
||||
EXTRN strcmp :PROC
|
||||
@@ -0,0 +1,478 @@
|
||||
INCLUDE asm\include\master.inc
|
||||
|
||||
; UAbilitySystemComponent: ActivatableAbilities(+0x470) + Items TArray (+0xB0)
|
||||
ASC_ITEMS_DATA EQU 0520h
|
||||
ASC_ITEMS_NUM EQU 0528h
|
||||
|
||||
; FGameplayAbilitySpec field offsets (from spec base)
|
||||
SPEC_HANDLE EQU 00Ch ; Handle.Handle (DWORD)
|
||||
SPEC_ABILITY EQU 010h ; Ability (QWORD)
|
||||
SPEC_LEVEL EQU 018h ; Level (DWORD)
|
||||
SPEC_INPUTID EQU 01Ch ; InputID (DWORD)
|
||||
SPEC_INPRESSED EQU 029h ; InputPressed (BYTE bit 0)
|
||||
SPEC_SIZEOF EQU 0C8h
|
||||
|
||||
; FPredictionKey.Current = int16 at +0
|
||||
FPREDKEY_CURRENT EQU 000h
|
||||
|
||||
; UClass::vtable[101] = CreateDefaultObject
|
||||
VFUNC_CDO_OFS EQU 0328h ; 101 * 8
|
||||
|
||||
.const
|
||||
|
||||
sz_ClientActFailed BYTE "Function GameplayAbilities.AbilitySystemComponent.ClientActivateAbilityFailed",0
|
||||
sz_Sprint BYTE "Class FortniteGame.FortGameplayAbility_Sprint",0
|
||||
sz_Reload BYTE "Class FortniteGame.FortGameplayAbility_Reload",0
|
||||
sz_Ranged BYTE "Class FortniteGame.FortGameplayAbility_RangedWeapon",0
|
||||
sz_Jump BYTE "Class FortniteGame.FortGameplayAbility_Jump",0
|
||||
sz_Death BYTE "BlueprintGeneratedClass GA_DefaultPlayer_Death.GA_DefaultPlayer_Death_C",0
|
||||
sz_InteractUse BYTE "BlueprintGeneratedClass GA_DefaultPlayer_InteractUse.GA_DefaultPlayer_InteractUse_C",0
|
||||
sz_InteractSearch BYTE "BlueprintGeneratedClass GA_DefaultPlayer_InteractSearch.GA_DefaultPlayer_InteractSearch_C",0
|
||||
sz_Emote BYTE "BlueprintGeneratedClass GAB_Emote_Generic.GAB_Emote_Generic_C",0
|
||||
sz_Trap BYTE "BlueprintGeneratedClass GA_TrapBuildGeneric.GA_TrapBuildGeneric_C",0
|
||||
sz_DanceGrenade BYTE "BlueprintGeneratedClass GA_DanceGrenade_Stun.GA_DanceGrenade_Stun_C",0
|
||||
|
||||
.data?
|
||||
|
||||
fn_ClientActFailed QWORD ?
|
||||
|
||||
AbilityClass_Sprint QWORD ?
|
||||
AbilityClass_Reload QWORD ?
|
||||
AbilityClass_Ranged QWORD ?
|
||||
AbilityClass_Jump QWORD ?
|
||||
AbilityClass_Death QWORD ?
|
||||
AbilityClass_InteractUse QWORD ?
|
||||
AbilityClass_InteractSearch QWORD ?
|
||||
AbilityClass_Emote QWORD ?
|
||||
AbilityClass_Trap QWORD ?
|
||||
AbilityClass_DanceGrenade QWORD ?
|
||||
|
||||
.code
|
||||
|
||||
; Abilities_FindAbilitySpecFromHandle
|
||||
; In : RCX = UAbilitySystemComponent*
|
||||
; RDX = FGameplayAbilitySpecHandle* (int Handle at [RDX+0])
|
||||
; Out: RAX = &FGameplayAbilitySpec or NULL
|
||||
; No CALL sites -> no shadow/alignment needed; push/pop only.
|
||||
Abilities_FindAbilitySpecFromHandle PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
|
||||
mov rsi, rcx
|
||||
mov ebx, DWORD PTR [rdx] ; target Handle.Handle value
|
||||
|
||||
mov rcx, QWORD PTR [rsi + ASC_ITEMS_DATA]
|
||||
mov edx, DWORD PTR [rsi + ASC_ITEMS_NUM]
|
||||
test rcx, rcx
|
||||
jz @FASFH_miss
|
||||
test edx, edx
|
||||
jz @FASFH_miss
|
||||
|
||||
xor ebp, ebp ; i = 0
|
||||
@FASFH_loop:
|
||||
cmp ebp, edx
|
||||
jge @FASFH_miss
|
||||
imul rax, rbp, SPEC_SIZEOF
|
||||
add rax, rcx
|
||||
cmp DWORD PTR [rax + SPEC_HANDLE], ebx
|
||||
je @FASFH_found
|
||||
inc ebp
|
||||
jmp @FASFH_loop
|
||||
|
||||
@FASFH_miss:
|
||||
xor eax, eax
|
||||
@FASFH_found:
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
Abilities_FindAbilitySpecFromHandle ENDP
|
||||
|
||||
|
||||
; Abilities_ConsumeAllReplicatedData
|
||||
Abilities_ConsumeAllReplicatedData PROC
|
||||
ret
|
||||
Abilities_ConsumeAllReplicatedData ENDP
|
||||
|
||||
|
||||
; Abilities_TryActivateAbility
|
||||
; In : RCX = UAbilitySystemComponent*
|
||||
; RDX = FGameplayAbilitySpecHandle* (Handle.Handle at [RDX+0])
|
||||
; R8B = InputPressed (bool)
|
||||
; R9 = FPredictionKey*
|
||||
; [caller rsp+32+8] = arg5: FGameplayEventData* TriggerEventData
|
||||
;
|
||||
; Stack: 5 callee-saved pushes (40) + sub 70h (112) = 152
|
||||
; Entry RSP=8; 5 pushes: 8-40=-32=0; sub 70h: 0-112=0
|
||||
;
|
||||
; Frame slots (after prolog, relative to current RSP):
|
||||
; [+00..+1F] shadow (32 B)
|
||||
; [+20..+27] arg5 slot for 6-arg calls
|
||||
; [+28..+2F] arg6 slot
|
||||
; [+30..+37] OutInstancedAbility QWORD
|
||||
; [+38..+3B] handle DWORD copy (for FindAbilitySpec pointer arg)
|
||||
; [+40..+47] CAF params: Handle(4) + PredKey(2) + pad(2)
|
||||
Abilities_TryActivateAbility PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 70h
|
||||
|
||||
mov rsi, rcx
|
||||
mov ebx, DWORD PTR [rdx] ; EBX = Handle.Handle
|
||||
movzx r12d, r8b ; R12D = InputPressed (unused directly)
|
||||
mov rdi, r9 ; RDI = FPredictionKey*
|
||||
; arg5 (TriggerEventData*) at entry_rsp+40 = current_rsp+152+40 = rsp+0xC0
|
||||
mov rbp, QWORD PTR [rsp + 0C0h]
|
||||
|
||||
; Find spec
|
||||
mov DWORD PTR [rsp + 38h], ebx
|
||||
mov rcx, rsi
|
||||
lea rdx, [rsp + 38h]
|
||||
call Abilities_FindAbilitySpecFromHandle
|
||||
test rax, rax
|
||||
jz @TAA_do_caf
|
||||
|
||||
; Set InputPressed = 1
|
||||
or BYTE PTR [rax + SPEC_INPRESSED], 1
|
||||
|
||||
; InternalTryActivateAbility(ASC, Handle, PredKey*, &OutGA, NULL, TriggerEventData)
|
||||
xor eax, eax
|
||||
mov QWORD PTR [rsp + 30h], rax
|
||||
mov rcx, rsi
|
||||
mov edx, ebx
|
||||
mov r8, rdi
|
||||
lea r9, [rsp + 30h]
|
||||
mov QWORD PTR [rsp + 20h], rax
|
||||
mov QWORD PTR [rsp + 28h], rbp
|
||||
call QWORD PTR [Native_AbilitySystemComponent_InternalTryActivateAbility]
|
||||
test al, al
|
||||
jz @TAA_activate_failed
|
||||
|
||||
; Success: MarkAbilitySpecDirty
|
||||
mov DWORD PTR [rsp + 38h], ebx
|
||||
mov rcx, rsi
|
||||
lea rdx, [rsp + 38h]
|
||||
call Abilities_FindAbilitySpecFromHandle
|
||||
test rax, rax
|
||||
jz @TAA_done
|
||||
mov rcx, rsi
|
||||
mov rdx, rax
|
||||
call QWORD PTR [Native_AbilitySystemComponent_MarkAbilitySpecDirty]
|
||||
jmp @TAA_done
|
||||
|
||||
@TAA_activate_failed:
|
||||
; Clear InputPressed
|
||||
mov DWORD PTR [rsp + 38h], ebx
|
||||
mov rcx, rsi
|
||||
lea rdx, [rsp + 38h]
|
||||
call Abilities_FindAbilitySpecFromHandle
|
||||
test rax, rax
|
||||
jz @TAA_do_caf
|
||||
and BYTE PTR [rax + SPEC_INPRESSED], 0FEh
|
||||
|
||||
@TAA_do_caf:
|
||||
; Lazy-load ClientActivateAbilityFailed fn
|
||||
mov rax, QWORD PTR [fn_ClientActFailed]
|
||||
test rax, rax
|
||||
jnz @TAA_caf_call
|
||||
lea rcx, [sz_ClientActFailed]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [fn_ClientActFailed], rax
|
||||
@TAA_caf_call:
|
||||
test rax, rax
|
||||
jz @TAA_done
|
||||
; Params: Handle(4) at +0, PredictionKey.Current(int16) at +4
|
||||
mov DWORD PTR [rsp + 40h], ebx
|
||||
movsx ecx, WORD PTR [rdi + FPREDKEY_CURRENT]
|
||||
mov WORD PTR [rsp + 44h], cx
|
||||
mov rcx, rsi
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 40h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@TAA_done:
|
||||
add rsp, 70h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
Abilities_TryActivateAbility ENDP
|
||||
|
||||
|
||||
; Abilities_GrantGameplayAbility
|
||||
; In : RCX = APlayerPawn_Athena_C* (pawn)
|
||||
; RDX = UClass* (abilityClass)
|
||||
;
|
||||
; Stack: 5 callee-saved pushes (40) + sub 100h (256) = 296
|
||||
; Entry RSP=8; 5 pushes: 8-40=0; sub 100h: 0=0
|
||||
;
|
||||
; Frame:
|
||||
; [+00..+1F] shadow
|
||||
; [+20..+27] arg5 slot
|
||||
; [+28..+2F] OutHandle QWORD (for GiveAbility result)
|
||||
; [+30..+F7] FGameplayAbilitySpec copy (0xC8 bytes)
|
||||
Abilities_GrantGameplayAbility PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 100h
|
||||
|
||||
mov rbp, rcx ; RBP = pawn
|
||||
mov rsi, rdx ; RSI = abilityClass
|
||||
|
||||
; ASC = FortHelper_GetAbilitySystemComponent(pawn)
|
||||
mov rcx, rbp
|
||||
call FortHelper_GetAbilitySystemComponent
|
||||
test rax, rax
|
||||
jz @GAA_done
|
||||
mov rbx, rax ; RBX = ASC
|
||||
|
||||
; CDO = abilityClass->vtable[101](abilityClass)
|
||||
mov rcx, rsi
|
||||
mov rax, QWORD PTR [rcx]
|
||||
call QWORD PTR [rax + VFUNC_CDO_OFS]
|
||||
test rax, rax
|
||||
jz @GAA_done
|
||||
mov rdi, rax ; RDI = CDO
|
||||
|
||||
; Duplicate check: scan ActivatableAbilities.Items for same Ability ptr
|
||||
mov r12, QWORD PTR [rbx + ASC_ITEMS_DATA]
|
||||
mov ebp, DWORD PTR [rbx + ASC_ITEMS_NUM]
|
||||
test ebp, ebp
|
||||
jz @GAA_add
|
||||
test r12, r12
|
||||
jz @GAA_add
|
||||
xor ecx, ecx ; i = 0
|
||||
@GAA_dup_loop:
|
||||
cmp ecx, ebp
|
||||
jge @GAA_add
|
||||
imul rax, rcx, SPEC_SIZEOF
|
||||
add rax, r12
|
||||
cmp QWORD PTR [rax + SPEC_ABILITY], rdi
|
||||
je @GAA_done ; duplicate, skip
|
||||
inc ecx
|
||||
jmp @GAA_dup_loop
|
||||
|
||||
@GAA_add:
|
||||
; Zero out spec buffer at [rsp+30h]
|
||||
lea rcx, [rsp + 30h]
|
||||
xor edx, edx
|
||||
mov r8d, SPEC_SIZEOF
|
||||
call memset
|
||||
|
||||
; FFastArraySerializerItem: ReplicationID/Key/MostRecentKey = -1
|
||||
mov DWORD PTR [rsp + 30h + 000h], 0FFFFFFFFh
|
||||
mov DWORD PTR [rsp + 30h + 004h], 0FFFFFFFFh
|
||||
mov DWORD PTR [rsp + 30h + 008h], 0FFFFFFFFh
|
||||
|
||||
; Handle.Handle = lower 32 bits of CDO ptr | 1 (ensure non-zero and pseudo-unique)
|
||||
mov eax, edi
|
||||
or eax, 1
|
||||
mov DWORD PTR [rsp + 30h + SPEC_HANDLE], eax
|
||||
|
||||
; Ability = CDO
|
||||
mov QWORD PTR [rsp + 30h + SPEC_ABILITY], rdi
|
||||
|
||||
; Level = 1
|
||||
mov DWORD PTR [rsp + 30h + SPEC_LEVEL], 1
|
||||
|
||||
; InputID = -1
|
||||
mov DWORD PTR [rsp + 30h + SPEC_INPUTID], 0FFFFFFFFh
|
||||
|
||||
; GiveAbility(ASC, &OutHandle, &Spec)
|
||||
mov rcx, rbx
|
||||
lea rdx, [rsp + 28h]
|
||||
lea r8, [rsp + 30h]
|
||||
call QWORD PTR [Native_AbilitySystemComponent_GiveAbility]
|
||||
|
||||
@GAA_done:
|
||||
add rsp, 100h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
Abilities_GrantGameplayAbility ENDP
|
||||
|
||||
|
||||
; Abilities_ApplyAbilities
|
||||
; In : RCX = APlayerPawn_Athena_C* (pawn)
|
||||
; Grants 10 standard gameplay abilities. Lazily caches class ptrs.
|
||||
;
|
||||
; Stack: 2 pushes (16) + sub 28h (40) = 56
|
||||
; Entry RSP=8; 2 pushes: 8-16=8; sub 28h: 8-40=0
|
||||
Abilities_ApplyAbilities PROC
|
||||
push rbx
|
||||
push rdi
|
||||
sub rsp, 28h
|
||||
|
||||
mov rbx, rcx ; RBX = pawn
|
||||
|
||||
; Macro-like: load class pointer (lazily) then call GrantGameplayAbility
|
||||
; Sprint
|
||||
mov rdi, QWORD PTR [AbilityClass_Sprint]
|
||||
test rdi, rdi
|
||||
jnz @AAA_sprint_ok
|
||||
lea rcx, [sz_Sprint]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Sprint], rax
|
||||
mov rdi, rax
|
||||
@AAA_sprint_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_reload
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_reload:
|
||||
mov rdi, QWORD PTR [AbilityClass_Reload]
|
||||
test rdi, rdi
|
||||
jnz @AAA_reload_ok
|
||||
lea rcx, [sz_Reload]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Reload], rax
|
||||
mov rdi, rax
|
||||
@AAA_reload_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_ranged
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_ranged:
|
||||
mov rdi, QWORD PTR [AbilityClass_Ranged]
|
||||
test rdi, rdi
|
||||
jnz @AAA_ranged_ok
|
||||
lea rcx, [sz_Ranged]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Ranged], rax
|
||||
mov rdi, rax
|
||||
@AAA_ranged_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_jump
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_jump:
|
||||
mov rdi, QWORD PTR [AbilityClass_Jump]
|
||||
test rdi, rdi
|
||||
jnz @AAA_jump_ok
|
||||
lea rcx, [sz_Jump]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Jump], rax
|
||||
mov rdi, rax
|
||||
@AAA_jump_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_death
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_death:
|
||||
mov rdi, QWORD PTR [AbilityClass_Death]
|
||||
test rdi, rdi
|
||||
jnz @AAA_death_ok
|
||||
lea rcx, [sz_Death]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Death], rax
|
||||
mov rdi, rax
|
||||
@AAA_death_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_iuse
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_iuse:
|
||||
mov rdi, QWORD PTR [AbilityClass_InteractUse]
|
||||
test rdi, rdi
|
||||
jnz @AAA_iuse_ok
|
||||
lea rcx, [sz_InteractUse]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_InteractUse], rax
|
||||
mov rdi, rax
|
||||
@AAA_iuse_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_isearch
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_isearch:
|
||||
mov rdi, QWORD PTR [AbilityClass_InteractSearch]
|
||||
test rdi, rdi
|
||||
jnz @AAA_isearch_ok
|
||||
lea rcx, [sz_InteractSearch]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_InteractSearch], rax
|
||||
mov rdi, rax
|
||||
@AAA_isearch_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_emote
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_emote:
|
||||
mov rdi, QWORD PTR [AbilityClass_Emote]
|
||||
test rdi, rdi
|
||||
jnz @AAA_emote_ok
|
||||
lea rcx, [sz_Emote]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Emote], rax
|
||||
mov rdi, rax
|
||||
@AAA_emote_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_trap
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_trap:
|
||||
mov rdi, QWORD PTR [AbilityClass_Trap]
|
||||
test rdi, rdi
|
||||
jnz @AAA_trap_ok
|
||||
lea rcx, [sz_Trap]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_Trap], rax
|
||||
mov rdi, rax
|
||||
@AAA_trap_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_dance
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_dance:
|
||||
mov rdi, QWORD PTR [AbilityClass_DanceGrenade]
|
||||
test rdi, rdi
|
||||
jnz @AAA_dance_ok
|
||||
lea rcx, [sz_DanceGrenade]
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [AbilityClass_DanceGrenade], rax
|
||||
mov rdi, rax
|
||||
@AAA_dance_ok:
|
||||
test rdi, rdi
|
||||
jz @AAA_done
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call Abilities_GrantGameplayAbility
|
||||
|
||||
@AAA_done:
|
||||
add rsp, 28h
|
||||
pop rdi
|
||||
pop rbx
|
||||
ret
|
||||
Abilities_ApplyAbilities ENDP
|
||||
|
||||
END
|
||||
+613
@@ -0,0 +1,613 @@
|
||||
INCLUDE ..\include\master.inc
|
||||
|
||||
; UWorld field offsets
|
||||
UWORLD_NetDriver EQU 038h
|
||||
UWORLD_AuthorityGameMode EQU 140h
|
||||
UWORLD_GameState EQU 148h
|
||||
UWORLD_OwningGameInstance EQU 190h
|
||||
|
||||
UGAMEINST_LocalPlayers EQU 038h ; TArray<ULocalPlayer*> Data ptr
|
||||
UPLAYER_PlayerController EQU 030h ; UPlayer::PlayerController
|
||||
|
||||
; AGameMode / AFortGameModeAthena offsets
|
||||
AGAMEMODE_MatchState EQU 03B8h
|
||||
AGAMEMODE_MinRespawnDelay EQU 03D0h
|
||||
AFGM_bEnableReplicationGraph EQU 0491h
|
||||
AFGMA_bDisableGCOnServer EQU 08E8h
|
||||
AFGMA_bAllowSpectateAfterDeath EQU 0B68h
|
||||
|
||||
; AFortGameStateAthena offsets
|
||||
AFGSA_WarmupCntdwnEnd EQU 14F4h
|
||||
AFGSA_AircraftStartTime EQU 14F8h
|
||||
AFGSA_GamePhase EQU 1B98h
|
||||
AFGSA_bSkipAircraft EQU 1BA8h
|
||||
|
||||
; AGameStateBase
|
||||
AGSB_bReplHasBegunPlay EQU 0340h
|
||||
|
||||
; EDeathCause values
|
||||
EDC_Unspecified EQU 0
|
||||
EDC_Shotgun EQU 1
|
||||
EDC_Rifle EQU 2
|
||||
EDC_Sniper EQU 3
|
||||
EDC_Pistol EQU 4
|
||||
EDC_Grenade EQU 5
|
||||
EDC_SMG EQU 6
|
||||
EDC_Melee EQU 7
|
||||
EDC_FallDamage EQU 8
|
||||
EDC_RocketLauncher EQU 9
|
||||
EDC_GrenadeLauncher EQU 10
|
||||
EDC_Trap EQU 11
|
||||
EDC_OutsideSafeZone EQU 12
|
||||
EDC_Minigun EQU 13
|
||||
EDC_Bow EQU 14
|
||||
|
||||
.const
|
||||
|
||||
; Athena terrain URL (wide string)
|
||||
szAthenaUrl DW 'A','t','h','e','n','a','_','T','e','r','r','a','i','n','?'
|
||||
DW 'g','a','m','e','=','/','G','a','m','e','/','A','t','h','e'
|
||||
DW 'n','a','/','A','t','h','e','n','a','_','G','a','m','e','M'
|
||||
DW 'o','d','e','.','A','t','h','e','n','a','_','G','a','m','e'
|
||||
DW 'M','o','d','e','_','C', 0
|
||||
|
||||
szInProgress DW 'I','n','P','r','o','g','r','e','s','s', 0
|
||||
|
||||
; UFunction paths
|
||||
szFn_SwitchLevel DB "Function Engine.PlayerController.SwitchLevel", 0
|
||||
szFn_ConvToName DB "Function Engine.KismetStringLibrary.Conv_StringToName", 0
|
||||
szFn_K2SetMatchState DB "Function Engine.GameMode.K2_OnSetMatchState", 0
|
||||
szFn_StartPlay DB "Function Engine.GameMode.StartPlay", 0
|
||||
szFn_StartMatch DB "Function Engine.GameMode.StartMatch", 0
|
||||
szFn_OnRepHasBegun DB "Function Engine.GameStateBase.OnRep_ReplicatedHasBegunPlay", 0
|
||||
szFn_OnRepGamePhase DB "Function FortniteGame.FortGameStateAthena.OnRep_GamePhase", 0
|
||||
szClass_KismetStr DB "Class Engine.KismetStringLibrary", 0
|
||||
|
||||
; Death cause tags
|
||||
szTag_Shotgun DB "weapon.ranged.shotgun", 0
|
||||
szTag_Rifle DB "weapon.ranged.assault", 0
|
||||
szTag_Fall DB "Gameplay.Damage.Environment.Falling", 0
|
||||
szTag_Sniper DB "weapon.ranged.sniper", 0
|
||||
szTag_SMG DB "Weapon.Ranged.SMG", 0
|
||||
szTag_Rocket DB "weapon.ranged.heavy.rocket_launcher", 0
|
||||
szTag_GrenadeL DB "weapon.ranged.heavy.grenade_launcher", 0
|
||||
szTag_Grenade DB "Weapon.ranged.heavy.grenade", 0
|
||||
szTag_Minigun DB "Weapon.Ranged.Heavy.Minigun", 0
|
||||
szTag_Bow DB "Weapon.Ranged.Crossbow", 0
|
||||
szTag_Trap DB "trap.floor", 0
|
||||
szTag_Pistol DB "weapon.ranged.pistol", 0
|
||||
szTag_SafeZone DB "Gameplay.Damage.OutsideSafeZone", 0
|
||||
szTag_Melee DB "Weapon.Melee.Impact.Pickaxe", 0
|
||||
|
||||
szLogMatch DB "[GAME] Initializing match!", 0
|
||||
szLogMode DB "[GAME] Solos game mode active.", 0
|
||||
szLogStart DB "[GAME] Game::Start - traveling to Athena_Terrain.", 0
|
||||
|
||||
.data?
|
||||
|
||||
pFn_SwitchLevel QWORD ?
|
||||
pFn_ConvToName QWORD ?
|
||||
pFn_K2SetMatchState QWORD ?
|
||||
pFn_StartPlay QWORD ?
|
||||
pFn_StartMatch QWORD ?
|
||||
pFn_OnRepHasBegun QWORD ?
|
||||
pFn_OnRepGamePhase QWORD ?
|
||||
pClass_KismetStr QWORD ?
|
||||
|
||||
Game_Mode QWORD ? ; current game mode ptr (Solos = AuthorityGameMode)
|
||||
|
||||
.code
|
||||
|
||||
; Internal helper: load cached UFunction*.
|
||||
; Inputs: RSI = &cache_qword, RDI = &szFunctionPath
|
||||
; Output: RAX = UFunction* (0 if not found)
|
||||
; Trashes: RCX
|
||||
Game__LoadFn PROC
|
||||
mov rax, QWORD PTR [rsi]
|
||||
test rax, rax
|
||||
jnz @@done
|
||||
mov rcx, rdi
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [rsi], rax
|
||||
@@done:
|
||||
ret
|
||||
Game__LoadFn ENDP
|
||||
|
||||
; QWORD Game__GetWorld_AuthMode() -> RAX
|
||||
Game__GetWorld_AuthMode PROC
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rax, QWORD PTR [rax + UWORLD_AuthorityGameMode]
|
||||
ret
|
||||
@@null:
|
||||
xor eax, eax
|
||||
ret
|
||||
Game__GetWorld_AuthMode ENDP
|
||||
|
||||
; QWORD Game__GetWorld_GameState() -> RAX
|
||||
Game__GetWorld_GameState PROC
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rax, QWORD PTR [rax + UWORLD_GameState]
|
||||
ret
|
||||
@@null:
|
||||
xor eax, eax
|
||||
ret
|
||||
Game__GetWorld_GameState ENDP
|
||||
|
||||
; QWORD Game_GetPlayerController() -> RAX
|
||||
; Exported so ufunctionhooks can use it.
|
||||
Game_GetPlayerController PROC
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rax, QWORD PTR [rax + UWORLD_OwningGameInstance]
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rax, QWORD PTR [rax + UGAMEINST_LocalPlayers] ; LocalPlayers.Data
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rax, QWORD PTR [rax] ; [0] = ULocalPlayer*
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rax, QWORD PTR [rax + UPLAYER_PlayerController]
|
||||
ret
|
||||
@@null:
|
||||
xor eax, eax
|
||||
ret
|
||||
Game_GetPlayerController ENDP
|
||||
|
||||
; Internal: call ProcessEvent with no meaningful params.
|
||||
; RCX = UObject*, RDX = UFunction*, R8 = scratch buf on caller stack.
|
||||
Game__CallPE_NoParams PROC
|
||||
call QWORD PTR [ProcessEvent]
|
||||
ret
|
||||
Game__CallPE_NoParams ENDP
|
||||
|
||||
; void Game_Start()
|
||||
; Frame: 4 pushes (rbp,rbx,rsi,rdi) + sub 58h
|
||||
; 4 pushes from base 8 -> RSP=8; sub 58h(88)=8 -> 0
|
||||
; [rsp+32..+47] = SwitchLevel FString params (16 bytes)
|
||||
Game_Start PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
sub rsp, 58h
|
||||
|
||||
lea rcx, szLogStart
|
||||
call Logger_LogInfo
|
||||
|
||||
; Get player controller
|
||||
call Game_GetPlayerController
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rbx, rax ; rbx = PlayerController*
|
||||
|
||||
; Build FString URL at [rsp+32]
|
||||
lea rcx, [rsp+32]
|
||||
lea rdx, szAthenaUrl
|
||||
call FString_FromWideChar
|
||||
|
||||
; Load SwitchLevel UFunction*
|
||||
lea rsi, pFn_SwitchLevel
|
||||
lea rdi, szFn_SwitchLevel
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@free
|
||||
|
||||
; ProcessEvent(PC, fn, ¶ms)
|
||||
mov rcx, rbx
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@@free:
|
||||
lea rcx, [rsp+32]
|
||||
call FString_Free
|
||||
|
||||
; bTraveled = true
|
||||
mov BYTE PTR [bTraveled], 1
|
||||
|
||||
@@done:
|
||||
add rsp, 58h
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Game_Start ENDP
|
||||
|
||||
; void Game_OnReadyToStartMatch()
|
||||
; Frame: 8 pushes (rbp,rbx,rsi,rdi,r12,r13,r14,r15) + sub 88h
|
||||
; 8 pushes from base 8 -> RSP=8; sub 88h(136)=8 -> 0
|
||||
; [rsp+32..+47] = FString scratch / Conv_StringToName input FString
|
||||
; [rsp+48..+55] = FName output from Conv_StringToName
|
||||
; [rsp+56..+63] = OnRep_GamePhase params (BYTE OldPhase + 7 pad)
|
||||
; [rsp+64..+71] = K2_OnSetMatchState params (FName, 8 bytes)
|
||||
Game_OnReadyToStartMatch PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
sub rsp, 88h
|
||||
|
||||
lea rcx, szLogMatch
|
||||
call Logger_LogInfo
|
||||
|
||||
; r12 = GameState, r13 = AuthorityGameMode
|
||||
call Game__GetWorld_GameState
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov r12, rax
|
||||
|
||||
call Game__GetWorld_AuthMode
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov r13, rax
|
||||
|
||||
; GameState fields
|
||||
mov BYTE PTR [r12 + AFGSA_bSkipAircraft], 1
|
||||
mov DWORD PTR [r12 + AFGSA_AircraftStartTime], 461C7EB8h ; 9999.9f
|
||||
mov DWORD PTR [r12 + AFGSA_WarmupCntdwnEnd], 47C34F80h ; 99999.9f
|
||||
mov BYTE PTR [r12 + AFGSA_GamePhase], EAthenaGamePhase_Warmup
|
||||
|
||||
; OnRep_GamePhase(EAthenaGamePhase::None)
|
||||
lea rsi, pFn_OnRepGamePhase
|
||||
lea rdi, szFn_OnRepGamePhase
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@skip_gp
|
||||
mov BYTE PTR [rsp+56], EAthenaGamePhase_None
|
||||
mov rcx, r12
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+56]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_gp:
|
||||
|
||||
; GameMode fields
|
||||
mov BYTE PTR [r13 + AFGMA_bDisableGCOnServer], 1
|
||||
mov BYTE PTR [r13 + AFGMA_bAllowSpectateAfterDeath], 1
|
||||
mov BYTE PTR [r13 + AFGM_bEnableReplicationGraph],1
|
||||
|
||||
; Get FName("InProgress") via Conv_StringToName
|
||||
; Build FString "InProgress" at [rsp+32]
|
||||
lea rcx, [rsp+32]
|
||||
lea rdx, szInProgress
|
||||
call FString_FromWideChar
|
||||
|
||||
; Get/cache KismetStringLibrary class
|
||||
mov r14, QWORD PTR [pClass_KismetStr]
|
||||
test r14, r14
|
||||
jnz @@have_ksl
|
||||
lea rcx, szClass_KismetStr
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [pClass_KismetStr], rax
|
||||
mov r14, rax
|
||||
@@have_ksl:
|
||||
|
||||
; Get Conv_StringToName fn
|
||||
lea rsi, pFn_ConvToName
|
||||
lea rdi, szFn_ConvToName
|
||||
call Game__LoadFn
|
||||
mov r15, rax ; r15 = fn (may be 0)
|
||||
|
||||
; Zero FName output slot
|
||||
xor eax, eax
|
||||
mov QWORD PTR [rsp+48], rax
|
||||
|
||||
; Call: GameplayStatics.Conv_StringToName(FString, FName*)
|
||||
; Params layout: [rsp+32]=FString input, [rsp+48]=FName output
|
||||
test r15, r15
|
||||
jz @@skip_conv
|
||||
test r14, r14
|
||||
jz @@skip_conv
|
||||
mov rcx, r14
|
||||
mov rdx, r15
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_conv:
|
||||
|
||||
; Free FString temp
|
||||
lea rcx, [rsp+32]
|
||||
call FString_Free
|
||||
|
||||
; MatchState = InProgress (FName at [rsp+48])
|
||||
mov rax, QWORD PTR [rsp+48]
|
||||
mov QWORD PTR [r13 + AGAMEMODE_MatchState], rax
|
||||
|
||||
; K2_OnSetMatchState(InProgress) - params: {FName NewState}
|
||||
lea rsi, pFn_K2SetMatchState
|
||||
lea rdi, szFn_K2SetMatchState
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@skip_k2
|
||||
mov rbx, QWORD PTR [rsp+48]
|
||||
mov QWORD PTR [rsp+64], rbx ; params.NewState
|
||||
mov rcx, r13
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+64]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_k2:
|
||||
|
||||
; Store current game mode
|
||||
mov QWORD PTR [Game_Mode], r13
|
||||
|
||||
lea rcx, szLogMode
|
||||
call Logger_LogInfo
|
||||
|
||||
; MinRespawnDelay = 5.0f
|
||||
mov DWORD PTR [r13 + AGAMEMODE_MinRespawnDelay], 40A00000h ; 5.0f
|
||||
|
||||
; StartPlay()
|
||||
lea rsi, pFn_StartPlay
|
||||
lea rdi, szFn_StartPlay
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@skip_sp
|
||||
mov rcx, r13
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_sp:
|
||||
|
||||
; GameState->bReplicatedHasBegunPlay = true
|
||||
mov BYTE PTR [r12 + AGSB_bReplHasBegunPlay], 1
|
||||
|
||||
; OnRep_ReplicatedHasBegunPlay()
|
||||
lea rsi, pFn_OnRepHasBegun
|
||||
lea rdi, szFn_OnRepHasBegun
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@skip_rb
|
||||
mov rcx, r12
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_rb:
|
||||
|
||||
; StartMatch()
|
||||
lea rsi, pFn_StartMatch
|
||||
lea rdi, szFn_StartMatch
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rcx, r13
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@@done:
|
||||
add rsp, 88h
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Game_OnReadyToStartMatch ENDP
|
||||
|
||||
; DWORD Game_GetDeathCause(FFortPlayerDeathReport* pReport)
|
||||
; RCX = pReport. Returns EDeathCause in EAX.
|
||||
;
|
||||
; FFortPlayerDeathReport::Tags (FGameplayTagContainer) at +0x30.
|
||||
; FGameplayTagContainer::GameplayTags (TArray<FGameplayTag>) at +0x00.
|
||||
; FGameplayTag::TagName (FName, 8 bytes).
|
||||
; We FNameToString each tag name, narrow-convert, strcmp vs table.
|
||||
;
|
||||
; Frame: 5 pushes (rbp,rbx,rsi,rdi,r12) + sub 48h
|
||||
; 5 pushes from base 8 -> RSP=8+5*8=48=0 mod16... wait:
|
||||
; entry 8 mod16, push->0, push->8, push->0, push->8, push->0 after 5 pushes = 0 mod16
|
||||
; sub 48h(72): 72 mod16=8 -> 0-8=-8=8 mod16. NOT right.
|
||||
;
|
||||
; stack grows DOWN. Entry RSP=X where X=8 mod16.
|
||||
; push subtracts 8: each push makes RSP lower by 8.
|
||||
; After 5 pushes: RSP = X-40. (X-40) mod16 = (X mod16) - (40 mod16) = 8 - 8 = 0 mod16.
|
||||
; So after 5 pushes, RSP = 0 mod 16.
|
||||
; sub N: need RSP=0 at CALL sites. Current RSP=0. sub N must keep 0 mod16.
|
||||
; N must be = 0 mod16. N=0x40(64) or N=0x50 etc.
|
||||
; Use sub 50h (80): [rsp+0..+31]=shadow, [rsp+32..+47]=FString scratch,
|
||||
; [rsp+48..+79]=narrow buf (32 bytes for tag string)
|
||||
AFPDR_Tags_Data EQU 030h
|
||||
AFPDR_Tags_Num EQU 038h
|
||||
|
||||
Game_GetDeathCause PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 50h
|
||||
|
||||
; Default = Unspecified
|
||||
xor r12d, r12d
|
||||
|
||||
test rcx, rcx
|
||||
jz @@done
|
||||
mov rbp, rcx ; rbp = pReport
|
||||
|
||||
; Load GameplayTags array
|
||||
mov rsi, QWORD PTR [rbp + AFPDR_Tags_Data] ; Tags.GameplayTags.Data
|
||||
mov edi, DWORD PTR [rbp + AFPDR_Tags_Num] ; Tags.GameplayTags.Num
|
||||
test rsi, rsi
|
||||
jz @@done
|
||||
test edi, edi
|
||||
jz @@done
|
||||
|
||||
xor ebx, ebx ; i = 0
|
||||
@@loop:
|
||||
cmp ebx, edi
|
||||
jge @@done
|
||||
|
||||
; FName at rsi + i*8
|
||||
lea rcx, [rsi + rbx*8] ; FName* in
|
||||
; Zero FString at [rsp+32]
|
||||
xor eax, eax
|
||||
mov QWORD PTR [rsp+32], rax
|
||||
mov QWORD PTR [rsp+40], rax
|
||||
lea rdx, [rsp+32] ; FString& out
|
||||
call QWORD PTR [FNameToString]
|
||||
|
||||
; Load FString.Data (wchar_t*)
|
||||
mov rdi, QWORD PTR [rsp+32]
|
||||
test rdi, rdi
|
||||
jz @@next
|
||||
|
||||
; Narrow-convert wchar_t -> char in-place
|
||||
xor ecx, ecx
|
||||
@@narrow:
|
||||
movzx eax, WORD PTR [rdi + rcx*2]
|
||||
test eax, eax
|
||||
jz @@narrow_done
|
||||
mov BYTE PTR [rdi + rcx], al
|
||||
inc ecx
|
||||
jmp @@narrow
|
||||
@@narrow_done:
|
||||
mov BYTE PTR [rdi + rcx], 0
|
||||
|
||||
; Compare against tag table; first arg = rdi (our string), second = table entry
|
||||
; strcmp(s1=rdi, s2=tag) in x64: RCX=s1, RDX=s2
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Shotgun
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c2
|
||||
mov r12d, EDC_Shotgun
|
||||
jmp @@found
|
||||
@@c2:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Rifle
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c3
|
||||
mov r12d, EDC_Rifle
|
||||
jmp @@found
|
||||
@@c3:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Fall
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c4
|
||||
mov r12d, EDC_FallDamage
|
||||
jmp @@found
|
||||
@@c4:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Sniper
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c5
|
||||
mov r12d, EDC_Sniper
|
||||
jmp @@found
|
||||
@@c5:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_SMG
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c6
|
||||
mov r12d, EDC_SMG
|
||||
jmp @@found
|
||||
@@c6:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Rocket
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c7
|
||||
mov r12d, EDC_RocketLauncher
|
||||
jmp @@found
|
||||
@@c7:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_GrenadeL
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c8
|
||||
mov r12d, EDC_GrenadeLauncher
|
||||
jmp @@found
|
||||
@@c8:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Grenade
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c9
|
||||
mov r12d, EDC_Grenade
|
||||
jmp @@found
|
||||
@@c9:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Minigun
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c10
|
||||
mov r12d, EDC_Minigun
|
||||
jmp @@found
|
||||
@@c10:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Bow
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c11
|
||||
mov r12d, EDC_Bow
|
||||
jmp @@found
|
||||
@@c11:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Trap
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c12
|
||||
mov r12d, EDC_Trap
|
||||
jmp @@found
|
||||
@@c12:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Pistol
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c13
|
||||
mov r12d, EDC_Pistol
|
||||
jmp @@found
|
||||
@@c13:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_SafeZone
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@c14
|
||||
mov r12d, EDC_OutsideSafeZone
|
||||
jmp @@found
|
||||
@@c14:
|
||||
mov rcx, rdi
|
||||
lea rdx, szTag_Melee
|
||||
call strcmp
|
||||
test eax, eax
|
||||
jnz @@next
|
||||
mov r12d, EDC_Melee
|
||||
jmp @@found
|
||||
|
||||
@@found:
|
||||
lea rcx, [rsp+32]
|
||||
call FString_Free
|
||||
jmp @@done
|
||||
|
||||
@@next:
|
||||
lea rcx, [rsp+32]
|
||||
call FString_Free
|
||||
inc ebx
|
||||
jmp @@loop
|
||||
|
||||
@@done:
|
||||
mov eax, r12d
|
||||
add rsp, 50h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Game_GetDeathCause ENDP
|
||||
|
||||
END
|
||||
+2097
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,830 @@
|
||||
INCLUDE ..\include\master.inc
|
||||
|
||||
; AFortPickup field offsets
|
||||
AFPICKUP_bReplicates EQU 0082h ; AActor::bReplicates bitfield (byte bit6)
|
||||
AFPICKUP_PrimaryPickupItemEntry EQU 0350h ; FFortItemEntry base
|
||||
FITE_Count EQU 000Ch ; +0C within FFortItemEntry
|
||||
FITE_ItemDefinition EQU 0018h ; +18
|
||||
FITE_LoadedAmmo EQU 0028h ; +28
|
||||
AFPICKUP_PickupLocationData EQU 0428h ; FFortPickupLocationData
|
||||
FPLD_PickupTarget EQU 0000h ; +00 within PickupLocationData
|
||||
FPLD_ItemOwner EQU 0010h ; +10
|
||||
FPLD_FlyTime EQU 0030h ; +30 (float)
|
||||
AFPICKUP_bPickedUp EQU 04C0h ; bool (Net)
|
||||
AFPICKUP_bTossedFromContainer EQU 0349h ; bool (net, after bRandomRotation)
|
||||
|
||||
; ABuildingTrap offsets
|
||||
ABTRAP_TrapData EQU 0580h ; UFortTrapItemDefinition*
|
||||
ABTRAP_AttachedTo EQU 0570h ; ABuildingActor*
|
||||
ABTRAP_Team EQU 04C5h ; EFortTeam (1 byte) - from ABuildingActor
|
||||
|
||||
; UFortAbilitySet::GameplayAbilities TArray at +0x38
|
||||
UFAS_GameplayAbilities EQU 0038h
|
||||
|
||||
.const
|
||||
|
||||
szFn_BeginDeferredSpawn DB "Function Engine.GameplayStatics.BeginDeferredActorSpawnFromClass", 0
|
||||
szFn_FinishSpawning DB "Function Engine.GameplayStatics.FinishSpawningActor", 0
|
||||
szFn_SpawnBuildingGA DB "Function FortniteGame.FortKismetLibrary.STATIC_SpawnBuildingGameplayActor", 0
|
||||
szFn_TossPickup DB "Function FortniteGame.FortPickup.TossPickup", 0
|
||||
szFn_OnRep_PrimaryPE DB "Function FortniteGame.FortPickup.OnRep_PrimaryPickupItemEntry", 0
|
||||
szFn_OnRep_bPickedUp DB "Function FortniteGame.FortPickup.OnRep_bPickedUp", 0
|
||||
szFn_OnRep_TossedFrCont DB "Function FortniteGame.FortPickup.OnRep_TossedFromContainer", 0
|
||||
szFn_OnRep_PickupLocDat DB "Function FortniteGame.FortPickup.OnRep_PickupLocationData", 0
|
||||
szFn_InitKismetBuilding DB "Function FortniteGame.BuildingActor.InitializeKismetSpawnedBuildingActor", 0
|
||||
szFn_OnRep_AttachedTo DB "Function FortniteGame.BuildingActor.OnRep_AttachedTo", 0
|
||||
szFn_GetBlueprintClass DB "Function FortniteGame.FortTrapItemDefinition.GetBlueprintClass", 0
|
||||
|
||||
szClass_GameplayStatics DB "Class Engine.GameplayStatics", 0
|
||||
szClass_FortKismet DB "Class FortniteGame.FortKismetLibrary", 0
|
||||
szClass_AFortPickup DB "Class FortniteGame.FortPickup", 0
|
||||
|
||||
szLogSpawnNull DB "[SPAWNERS] SpawnActor returned null!", 0
|
||||
szLogDecoNull DB "[SPAWNERS] SpawnDeco: null tool or params.", 0
|
||||
|
||||
.data?
|
||||
|
||||
pFn_BeginDeferredSpawn QWORD ?
|
||||
pFn_FinishSpawning QWORD ?
|
||||
pFn_SpawnBuildingGA QWORD ?
|
||||
pFn_TossPickup QWORD ?
|
||||
pFn_OnRep_PrimaryPE QWORD ?
|
||||
pFn_OnRep_bPickedUp QWORD ?
|
||||
pFn_OnRep_TossedFrCont QWORD ?
|
||||
pFn_OnRep_PickupLocDat QWORD ?
|
||||
pFn_InitKismetBuilding QWORD ?
|
||||
pFn_OnRep_AttachedTo QWORD ?
|
||||
pFn_GetBlueprintClass QWORD ?
|
||||
|
||||
pClass_GameplayStatics QWORD ?
|
||||
pClass_FortKismet QWORD ?
|
||||
pClass_AFortPickup_C QWORD ?
|
||||
|
||||
.code
|
||||
|
||||
; Internal: lazy-load cached UFunction*.
|
||||
; RCX = &cache_var (QWORD*), RDX = szFunctionPath (PTR BYTE)
|
||||
; Returns UFunction* in RAX.
|
||||
Spawners__LoadFn PROC
|
||||
mov rax, QWORD PTR [rcx]
|
||||
test rax, rax
|
||||
jnz @@done
|
||||
push rcx ; save cache ptr
|
||||
sub rsp, 32
|
||||
; rcx already is the cache ptr - need path in rcx for SDK_FindObject
|
||||
mov rcx, rdx
|
||||
call SDK_FindObject
|
||||
add rsp, 32
|
||||
pop rcx
|
||||
mov QWORD PTR [rcx], rax
|
||||
@@done:
|
||||
ret
|
||||
Spawners__LoadFn ENDP
|
||||
|
||||
; Internal: lazy-load cached UClass*.
|
||||
; RCX = &cache_var, RDX = szClassName
|
||||
Spawners__LoadClass PROC
|
||||
mov rax, QWORD PTR [rcx]
|
||||
test rax, rax
|
||||
jnz @@done
|
||||
push rcx
|
||||
sub rsp, 32
|
||||
mov rcx, rdx
|
||||
call SDK_FindClass
|
||||
add rsp, 32
|
||||
pop rcx
|
||||
mov QWORD PTR [rcx], rax
|
||||
@@done:
|
||||
ret
|
||||
Spawners__LoadClass ENDP
|
||||
|
||||
; AActor* Spawners_SpawnActor(UClass* ActorClass,
|
||||
; FVector* pLocation,
|
||||
; AActor* Owner)
|
||||
; RCX = ActorClass, RDX = FVector* location (may be NULL -> {0,0,0}),
|
||||
; R8 = AActor* Owner (may be NULL)
|
||||
;
|
||||
; Calls STATIC_BeginDeferredActorSpawnFromClass then FinishSpawningActor.
|
||||
;
|
||||
; BeginDeferredActorSpawnFromClass params (at [rsp+X]):
|
||||
; +00: UObject* WorldContextObject (8)
|
||||
; +08: UClass* ActorClass (8)
|
||||
; +10: FTransform SpawnTransform (0x30 = 48 bytes)
|
||||
; +40: BYTE CollisionHandlingOverride (ESpawnActorCollisionHandlingMethod)
|
||||
; +41..+47: 7 pad bytes
|
||||
; +48: AActor* Owner (8)
|
||||
; +50: AActor* ReturnValue (8 OUT)
|
||||
; Total params = 0x58 bytes.
|
||||
;
|
||||
; FinishSpawningActor params:
|
||||
; +00: AActor* Actor (8)
|
||||
; +08: FTransform (0x30)
|
||||
; +38: AActor* ReturnValue (8 OUT)
|
||||
; Total = 0x40 bytes.
|
||||
|
||||
; Offsets within BeginDeferred params (relative to rsp+32)
|
||||
BDS_WorldCtx EQU 000h
|
||||
BDS_Class EQU 008h
|
||||
BDS_Transform EQU 010h ; FTransform (0x30 bytes)
|
||||
BDS_Collision EQU 040h ; BYTE (collision method)
|
||||
BDS_Owner EQU 048h
|
||||
BDS_RetVal EQU 050h ; OUT AActor*
|
||||
|
||||
; Offsets within FinishSpawning params (relative to rsp+120)
|
||||
FS_Actor EQU 000h
|
||||
FS_Transform EQU 008h
|
||||
FS_RetVal EQU 038h
|
||||
|
||||
; FTransform layout (0x30 bytes):
|
||||
; +00: FQuat Rotation (16 bytes: X,Y,Z,W)
|
||||
; +10: FVector Translation (12 bytes: X,Y,Z)
|
||||
; +1C: pad (4)
|
||||
; +20: FVector Scale3D (12 bytes: X,Y,Z)
|
||||
; +2C: pad (4)
|
||||
; Identity FTransform: Rotation=(0,0,0,1), Translation=(0,0,0), Scale=(1,1,1)
|
||||
fIdentityQuat_W REAL4 1.0
|
||||
fScale_One REAL4 1.0
|
||||
|
||||
Spawners_SpawnActor PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 0C0h
|
||||
|
||||
mov rbx, rcx ; rbx = ActorClass
|
||||
mov rsi, rdx ; rsi = FVector* location (may be 0)
|
||||
mov rdi, r8 ; rdi = Owner
|
||||
|
||||
test rbx, rbx
|
||||
jz @@done_null
|
||||
|
||||
; Get/cache GameplayStatics class
|
||||
lea rcx, pClass_GameplayStatics
|
||||
lea rdx, szClass_GameplayStatics
|
||||
call Spawners__LoadClass
|
||||
mov r12, rax ; r12 = GameplayStatics class
|
||||
test r12, r12
|
||||
jz @@done_null
|
||||
|
||||
; Get/cache BeginDeferredActorSpawnFromClass fn
|
||||
lea rcx, pFn_BeginDeferredSpawn
|
||||
lea rdx, szFn_BeginDeferredSpawn
|
||||
call Spawners__LoadFn
|
||||
test rax, rax
|
||||
jz @@done_null
|
||||
|
||||
; Zero params block
|
||||
lea rcx, [rsp+32]
|
||||
xor edx, edx
|
||||
mov r8d, 058h ; sizeof params
|
||||
call memset
|
||||
|
||||
; Fill BeginDeferred params
|
||||
call SDK_GetWorld
|
||||
mov QWORD PTR [rsp + 32 + BDS_WorldCtx], rax
|
||||
mov QWORD PTR [rsp + 32 + BDS_Class], rbx
|
||||
|
||||
; FTransform at [rsp+32+BDS_Transform]:
|
||||
; Rotation.W = 1.0 (identity quat)
|
||||
mov eax, 3F800000h ; 1.0f
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 0Ch], eax ; Rotation.W
|
||||
; Scale3D = (1,1,1)
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 20h], eax ; Scale3D.X
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 24h], eax ; Scale3D.Y
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 28h], eax ; Scale3D.Z
|
||||
|
||||
; If location provided, copy XYZ into Translation
|
||||
test rsi, rsi
|
||||
jz @@default_loc
|
||||
mov eax, DWORD PTR [rsi]
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 10h], eax ; X
|
||||
mov eax, DWORD PTR [rsi + 4]
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 14h], eax ; Y
|
||||
mov eax, DWORD PTR [rsi + 8]
|
||||
mov DWORD PTR [rsp + 32 + BDS_Transform + 18h], eax ; Z
|
||||
@@default_loc:
|
||||
|
||||
; Collision = ESpawnCollision_AdjustIfPossibleButAlwaysSpawn (2)
|
||||
mov BYTE PTR [rsp + 32 + BDS_Collision], ESpawnCollision_AdjustIfPossibleButAlwaysSpawn
|
||||
mov QWORD PTR [rsp + 32 + BDS_Owner], rdi
|
||||
|
||||
; Load BeginDeferred fn (already in rax from LoadFn call above - lost)
|
||||
; Need to reload from cache
|
||||
mov rax, QWORD PTR [pFn_BeginDeferredSpawn]
|
||||
mov rcx, r12
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
; FirstActor = params.ReturnValue
|
||||
mov rbp, QWORD PTR [rsp + 32 + BDS_RetVal]
|
||||
test rbp, rbp
|
||||
jz @@done_null
|
||||
|
||||
; Get FinishSpawningActor fn
|
||||
lea rcx, pFn_FinishSpawning
|
||||
lea rdx, szFn_FinishSpawning
|
||||
call Spawners__LoadFn
|
||||
test rax, rax
|
||||
jz @@done_null
|
||||
|
||||
; Fill FinishSpawning params at [rsp+120]
|
||||
lea rcx, [rsp+120]
|
||||
xor edx, edx
|
||||
mov r8d, 040h
|
||||
call memset
|
||||
|
||||
mov QWORD PTR [rsp + 120 + FS_Actor], rbp
|
||||
|
||||
; Copy FTransform from BeginDeferred params
|
||||
lea rcx, [rsp + 120 + FS_Transform]
|
||||
lea rdx, [rsp + 32 + BDS_Transform]
|
||||
mov r8d, 030h
|
||||
call memcpy
|
||||
|
||||
mov rax, QWORD PTR [pFn_FinishSpawning]
|
||||
mov rcx, r12
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+120]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
mov rax, QWORD PTR [rsp + 120 + FS_RetVal]
|
||||
jmp @@done
|
||||
|
||||
@@done_null:
|
||||
xor eax, eax
|
||||
@@done:
|
||||
add rsp, 0C0h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Spawners_SpawnActor ENDP
|
||||
|
||||
; AActor* Spawners_SpawnBuilding(UClass* BGAClass,
|
||||
; FTransform* pTransform,
|
||||
; APlayerPawn_Athena_C* Pawn)
|
||||
; RCX = BGAClass, RDX = FTransform*, R8 = Pawn
|
||||
;
|
||||
; Calls STATIC_SpawnBuildingGameplayActor via FortKismetLibrary.
|
||||
;
|
||||
; Params layout:
|
||||
; +00: UClass* BGAClass (8)
|
||||
; +08: FTransform (0x30 bytes) (48)
|
||||
; +38: AFortPawn* Instigator (8)
|
||||
; +40: ABuildingGameplayActor* ReturnValue (8 OUT)
|
||||
; Total = 0x48
|
||||
;
|
||||
; Frame: 4 pushes (rbp,rbx,rsi,rdi) + sub 58h
|
||||
; 4 pushes: RSP=8; sub 58h(88)=8 -> 0
|
||||
; [rsp+32..+31+0x48=79] = params (0x48 = 72 bytes)
|
||||
Spawners_SpawnBuilding PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
sub rsp, 58h
|
||||
|
||||
mov rbx, rcx ; rbx = BGAClass
|
||||
mov rsi, rdx ; rsi = FTransform*
|
||||
mov rdi, r8 ; rdi = Pawn
|
||||
|
||||
test rbx, rbx
|
||||
jz @@null
|
||||
test rsi, rsi
|
||||
jz @@null
|
||||
|
||||
; Get FortKismetLibrary class
|
||||
lea rcx, pClass_FortKismet
|
||||
lea rdx, szClass_FortKismet
|
||||
call Spawners__LoadClass
|
||||
mov rbp, rax
|
||||
test rbp, rbp
|
||||
jz @@null
|
||||
|
||||
; Get STATIC_SpawnBuildingGameplayActor fn
|
||||
lea rcx, pFn_SpawnBuildingGA
|
||||
lea rdx, szFn_SpawnBuildingGA
|
||||
call Spawners__LoadFn
|
||||
test rax, rax
|
||||
jz @@null
|
||||
|
||||
; Zero params at [rsp+32]
|
||||
lea rcx, [rsp+32]
|
||||
xor edx, edx
|
||||
mov r8d, 048h
|
||||
call memset
|
||||
|
||||
; Fill params
|
||||
mov QWORD PTR [rsp+32], rbx ; BGAClass
|
||||
lea rcx, [rsp + 32 + 8] ; dest = ¶ms.Transform
|
||||
mov rdx, rsi ; src = FTransform*
|
||||
mov r8d, 030h
|
||||
call memcpy
|
||||
mov QWORD PTR [rsp + 32 + 38h], rdi ; Instigator = Pawn
|
||||
|
||||
mov rax, QWORD PTR [pFn_SpawnBuildingGA]
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
mov rax, QWORD PTR [rsp + 32 + 40h]
|
||||
jmp @@done
|
||||
@@null:
|
||||
xor eax, eax
|
||||
@@done:
|
||||
add rsp, 58h
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Spawners_SpawnBuilding ENDP
|
||||
|
||||
; AFortPickup* Spawners_SummonPickup(AFortPlayerPawn* Pawn,
|
||||
; UFortWorldItemDefinition* ItemDef,
|
||||
; DWORD Count,
|
||||
; FVector* pLocation)
|
||||
; RCX = Pawn, RDX = ItemDef, R8D = Count, R9 = FVector*
|
||||
;
|
||||
; Frame: 5 pushes (rbp,rbx,rsi,rdi,r12) + sub 50h
|
||||
; 5 pushes -> RSP=0 mod16; sub 50h(80) mod16=0
|
||||
; [rsp+32..+47] = scratch / TossPickup params setup
|
||||
|
||||
; TossPickup params:
|
||||
; +00: FVector FinalLocation (0x0C)
|
||||
; +0C: 4 pad (for AActor* alignment)
|
||||
; +10: AFortPawn* ItemOwner (8)
|
||||
; +18: int OverrideMaxStackCount (4)
|
||||
; +1C: bool bToss (1)
|
||||
; +1D: 3 pad
|
||||
; Total: 0x20
|
||||
|
||||
Spawners_SummonPickup PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 50h
|
||||
|
||||
mov rbx, rcx ; rbx = Pawn
|
||||
mov rsi, rdx ; rsi = ItemDef
|
||||
mov edi, r8d ; edi = Count
|
||||
mov r12, r9 ; r12 = FVector* location
|
||||
|
||||
; SpawnActor<AFortPickup>(Location, Pawn)
|
||||
; Get AFortPickup class
|
||||
mov rax, QWORD PTR [pClass_AFortPickup_C]
|
||||
test rax, rax
|
||||
jnz @@have_cls
|
||||
lea rcx, szClass_AFortPickup
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [pClass_AFortPickup_C], rax
|
||||
@@have_cls:
|
||||
test rax, rax
|
||||
jz @@null
|
||||
|
||||
mov rcx, rax ; ActorClass = AFortPickup
|
||||
mov rdx, r12 ; pLocation
|
||||
mov r8, rbx ; Owner = Pawn
|
||||
call Spawners_SpawnActor
|
||||
test rax, rax
|
||||
jz @@null
|
||||
mov rbp, rax ; rbp = FortPickup*
|
||||
|
||||
; Set bReplicates = true (bitfield: byte at 0x82, bit 6 = 0x40)
|
||||
or BYTE PTR [rbp + 082h], 040h
|
||||
|
||||
; PrimaryPickupItemEntry.Count = Count
|
||||
mov DWORD PTR [rbp + AFPICKUP_PrimaryPickupItemEntry + FITE_Count], edi
|
||||
|
||||
; PrimaryPickupItemEntry.ItemDefinition = ItemDef
|
||||
mov QWORD PTR [rbp + AFPICKUP_PrimaryPickupItemEntry + FITE_ItemDefinition], rsi
|
||||
|
||||
; OnRep_PrimaryPickupItemEntry()
|
||||
mov rax, QWORD PTR [pFn_OnRep_PrimaryPE]
|
||||
test rax, rax
|
||||
jnz @@have_onrep_pe
|
||||
lea rcx, pFn_OnRep_PrimaryPE
|
||||
lea rdx, szFn_OnRep_PrimaryPE
|
||||
call Spawners__LoadFn
|
||||
@@have_onrep_pe:
|
||||
test rax, rax
|
||||
jz @@skip_onrep_pe
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_onrep_pe:
|
||||
|
||||
; TossPickup(Location, Pawn, 6, true)
|
||||
mov rax, QWORD PTR [pFn_TossPickup]
|
||||
test rax, rax
|
||||
jnz @@have_toss
|
||||
lea rcx, pFn_TossPickup
|
||||
lea rdx, szFn_TossPickup
|
||||
call Spawners__LoadFn
|
||||
@@have_toss:
|
||||
test rax, rax
|
||||
jz @@skip_toss
|
||||
|
||||
; Build TossPickup params on stack at [rsp+32] (0x20 bytes)
|
||||
xor ecx, ecx
|
||||
mov QWORD PTR [rsp+32], rcx
|
||||
mov QWORD PTR [rsp+40], rcx
|
||||
mov QWORD PTR [rsp+48], rcx
|
||||
|
||||
; FVector FinalLocation = *pLocation (or 0,0,0 if null)
|
||||
test r12, r12
|
||||
jz @@toss_zero_loc
|
||||
mov ecx, DWORD PTR [r12]
|
||||
mov DWORD PTR [rsp+32], ecx ; X
|
||||
mov ecx, DWORD PTR [r12+4]
|
||||
mov DWORD PTR [rsp+36], ecx ; Y
|
||||
mov ecx, DWORD PTR [r12+8]
|
||||
mov DWORD PTR [rsp+40], ecx ; Z
|
||||
@@toss_zero_loc:
|
||||
; Pad at [rsp+44] = 0 already
|
||||
mov QWORD PTR [rsp+48], rbx ; ItemOwner = Pawn (at +0x10)
|
||||
; +0x0C = 4 pad bytes = rsp+44..rsp+47
|
||||
; +0x10 = AFortPawn* ItemOwner = rsp+48..rsp+55 <- stored above
|
||||
mov DWORD PTR [rsp+56], 6 ; OverrideMaxStackCount = 6 (at +0x18)
|
||||
mov BYTE PTR [rsp+60], 1 ; bToss = true (at +0x1C)
|
||||
|
||||
mov rdx, rax ; fn
|
||||
mov rcx, rbp ; this = FortPickup
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_toss:
|
||||
|
||||
mov rax, rbp
|
||||
jmp @@done
|
||||
@@null:
|
||||
xor eax, eax
|
||||
@@done:
|
||||
add rsp, 50h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Spawners_SummonPickup ENDP
|
||||
|
||||
; void Spawners_SummonPickupFromChest(UFortWorldItemDefinition* ItemDef,
|
||||
; DWORD Count, FVector* pLocation)
|
||||
; RCX = ItemDef, RDX = Count, R8 = FVector* location
|
||||
; Frame: 4 pushes + sub 38h -> 8 mod16
|
||||
Spawners_SummonPickupFromChest PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
sub rsp, 38h
|
||||
|
||||
mov rbx, rcx ; ItemDef
|
||||
mov esi, edx ; Count
|
||||
mov rdi, r8 ; FVector*
|
||||
|
||||
; Get AFortPickup class
|
||||
mov rax, QWORD PTR [pClass_AFortPickup_C]
|
||||
test rax, rax
|
||||
jnz @@have_cls
|
||||
lea rcx, szClass_AFortPickup
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [pClass_AFortPickup_C], rax
|
||||
@@have_cls:
|
||||
test rax, rax
|
||||
jz @@done
|
||||
|
||||
mov rcx, rax
|
||||
mov rdx, rdi ; location
|
||||
xor r8, r8 ; Owner = null
|
||||
call Spawners_SpawnActor
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rbp, rax ; FortPickup*
|
||||
|
||||
; bReplicates = true
|
||||
or BYTE PTR [rbp + 082h], 040h
|
||||
|
||||
; bTossedFromContainer = true
|
||||
mov BYTE PTR [rbp + AFPICKUP_bTossedFromContainer], 1
|
||||
|
||||
; PrimaryPickupItemEntry.Count, ItemDefinition
|
||||
mov DWORD PTR [rbp + AFPICKUP_PrimaryPickupItemEntry + FITE_Count], esi
|
||||
mov QWORD PTR [rbp + AFPICKUP_PrimaryPickupItemEntry + FITE_ItemDefinition], rbx
|
||||
|
||||
; OnRep_PrimaryPickupItemEntry()
|
||||
mov rax, QWORD PTR [pFn_OnRep_PrimaryPE]
|
||||
test rax, rax
|
||||
jz @@skip_pe
|
||||
lea rcx, [rsp+32]
|
||||
; rcx should be FortPickup
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_pe:
|
||||
|
||||
; OnRep_TossedFromContainer()
|
||||
mov rax, QWORD PTR [pFn_OnRep_TossedFrCont]
|
||||
test rax, rax
|
||||
jnz @@have_tc
|
||||
lea rcx, pFn_OnRep_TossedFrCont
|
||||
lea rdx, szFn_OnRep_TossedFrCont
|
||||
call Spawners__LoadFn
|
||||
@@have_tc:
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@@done:
|
||||
add rsp, 38h
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Spawners_SummonPickupFromChest ENDP
|
||||
|
||||
; void Spawners_SpawnPickupFromFloor(UFortWorldItemDefinition* ItemDef,
|
||||
; DWORD Count, FVector* pLocation)
|
||||
; Frame: 4 pushes + sub 38h
|
||||
Spawners_SpawnPickupFromFloor PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
sub rsp, 38h
|
||||
|
||||
mov rbx, rcx
|
||||
mov esi, edx
|
||||
mov rdi, r8
|
||||
|
||||
mov rax, QWORD PTR [pClass_AFortPickup_C]
|
||||
test rax, rax
|
||||
jnz @@have_cls
|
||||
lea rcx, szClass_AFortPickup
|
||||
call SDK_FindClass
|
||||
mov QWORD PTR [pClass_AFortPickup_C], rax
|
||||
@@have_cls:
|
||||
test rax, rax
|
||||
jz @@done
|
||||
|
||||
mov rcx, rax
|
||||
mov rdx, rdi
|
||||
xor r8, r8
|
||||
call Spawners_SpawnActor
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rbp, rax
|
||||
|
||||
or BYTE PTR [rbp + 082h], 040h
|
||||
mov DWORD PTR [rbp + AFPICKUP_PrimaryPickupItemEntry + FITE_Count], esi
|
||||
mov QWORD PTR [rbp + AFPICKUP_PrimaryPickupItemEntry + FITE_ItemDefinition], rbx
|
||||
|
||||
mov rax, QWORD PTR [pFn_OnRep_PrimaryPE]
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@@done:
|
||||
add rsp, 38h
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Spawners_SpawnPickupFromFloor ENDP
|
||||
|
||||
; void Spawners_SpawnDeco(AFortDecoTool* Tool,
|
||||
; AFortDecoTool_ServerSpawnDeco_Params* pParams)
|
||||
; RCX = Tool, RDX = pParams
|
||||
;
|
||||
; Source: Spawners.h::SpawnDeco()
|
||||
;
|
||||
; pParams layout (AFortDecoTool_ServerSpawnDeco_Params):
|
||||
; +00: FRotator Rotation (12 bytes)
|
||||
; +0C: 4 pad
|
||||
; +10: FVector Location (12 bytes)
|
||||
; +1C: 4 pad
|
||||
; +20: ABuildingActor* AttachedActor (8 bytes)
|
||||
|
||||
; AFortDecoTool_ServerSpawnDeco_Params offsets
|
||||
DECO_Rotation EQU 000h ; FRotator (12 bytes)
|
||||
DECO_Location EQU 010h ; FVector (12 bytes) - after 4 pad
|
||||
DECO_AttachedActor EQU 020h ; ABuildingActor*
|
||||
|
||||
; AFortDecoTool::ItemDefinition offset
|
||||
ADECOTOOL_ItemDef EQU 04E8h
|
||||
|
||||
; UFortTrapItemDefinition::GetBlueprintClass params: {UClass* ReturnValue}
|
||||
; GetBlueprintClass params size = 8
|
||||
|
||||
; ABuildingTrap fields
|
||||
ABTRAP_AbilitySet_offset EQU 0E20h ; from Phase 2 findings (FN_FortniteGame_classes.hpp)
|
||||
; Note: The C++ calls Trap->AbilitySet->GameplayAbilities[i]
|
||||
; ABuildingActor::AbilitySet at 0x0E20, UFortAbilitySet::GameplayAbilities TArray at +0x38
|
||||
|
||||
Spawners_SpawnDeco PROC
|
||||
push rbp
|
||||
push rbx
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
sub rsp, 70h
|
||||
|
||||
test rcx, rcx
|
||||
jz @@done
|
||||
test rdx, rdx
|
||||
jz @@done
|
||||
|
||||
mov r12, rcx ; r12 = Tool (AFortDecoTool*)
|
||||
mov r13, rdx ; r13 = pParams
|
||||
|
||||
; ItemDef = Tool->ItemDefinition (UFortTrapItemDefinition*)
|
||||
mov r14, QWORD PTR [r12 + ADECOTOOL_ItemDef]
|
||||
test r14, r14
|
||||
jz @@done
|
||||
|
||||
; Build FTransform from Params:
|
||||
; Rotation (FQuat via RotToQuat) + Translation (Location) + Scale (1,1,1)
|
||||
; FTransform at [rsp+32] (0x30 bytes):
|
||||
lea rcx, [rsp+32] ; &FQuat output (first 16 bytes of transform)
|
||||
lea rdx, [r13 + DECO_Rotation] ; &FRotator input
|
||||
call Utils_RotToQuat
|
||||
|
||||
; Translation = Location
|
||||
mov eax, DWORD PTR [r13 + DECO_Location]
|
||||
mov DWORD PTR [rsp + 32 + 10h], eax ; X
|
||||
mov eax, DWORD PTR [r13 + DECO_Location + 4]
|
||||
mov DWORD PTR [rsp + 32 + 14h], eax ; Y
|
||||
mov eax, DWORD PTR [r13 + DECO_Location + 8]
|
||||
mov DWORD PTR [rsp + 32 + 18h], eax ; Z
|
||||
|
||||
; Scale3D = (1,1,1)
|
||||
mov eax, 3F800000h
|
||||
mov DWORD PTR [rsp + 32 + 20h], eax
|
||||
mov DWORD PTR [rsp + 32 + 24h], eax
|
||||
mov DWORD PTR [rsp + 32 + 28h], eax
|
||||
|
||||
; Get blueprint class from TrapItemDefinition
|
||||
; Call GetBlueprintClass via ProcessEvent
|
||||
mov rax, QWORD PTR [pFn_GetBlueprintClass]
|
||||
test rax, rax
|
||||
jnz @@have_gbc
|
||||
lea rcx, pFn_GetBlueprintClass
|
||||
lea rdx, szFn_GetBlueprintClass
|
||||
call Spawners__LoadFn
|
||||
@@have_gbc:
|
||||
test rax, rax
|
||||
jz @@done
|
||||
|
||||
; GetBlueprintClass params: {UClass* ReturnValue} (8 bytes at [rsp+80])
|
||||
xor rbp, rbp
|
||||
mov QWORD PTR [rsp+80], rbp ; ReturnValue = null
|
||||
mov rcx, r14 ; this = TrapItemDef
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+80]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
mov rbx, QWORD PTR [rsp+80] ; rbx = Trap UClass*
|
||||
test rbx, rbx
|
||||
jz @@done
|
||||
|
||||
; SpawnActor(TrapClass, FTransform{rsp+32})
|
||||
mov rcx, rbx ; ActorClass
|
||||
lea rdx, [rsp + 32 + 10h] ; Location = Translation part of transform
|
||||
xor r8, r8
|
||||
call Spawners_SpawnActor
|
||||
test rax, rax
|
||||
jz @@done
|
||||
mov rbp, rax ; rbp = Trap (ABuildingTrap*)
|
||||
|
||||
; Trap->TrapData = TrapDef
|
||||
mov QWORD PTR [rbp + ABTRAP_TrapData], r14
|
||||
|
||||
; Get Pawn = Tool->Owner
|
||||
mov rbx, QWORD PTR [r12 + 0108h] ; AActor::Owner at 0x108
|
||||
|
||||
; Trap->InitializeKismetSpawnedBuildingActor(Trap, PC)
|
||||
; PC = Pawn->Controller
|
||||
mov rsi, QWORD PTR [rbx + 0228h] ; Pawn->Controller (AController*)
|
||||
; PC = cast to AFortPlayerController
|
||||
|
||||
mov rax, QWORD PTR [pFn_InitKismetBuilding]
|
||||
test rax, rax
|
||||
jnz @@have_ikb
|
||||
lea rcx, pFn_InitKismetBuilding
|
||||
lea rdx, szFn_InitKismetBuilding
|
||||
call Spawners__LoadFn
|
||||
@@have_ikb:
|
||||
test rax, rax
|
||||
jz @@skip_init
|
||||
|
||||
; Params: {ABuildingActor* BuildingOwner, AFortPlayerController* SpawningController}
|
||||
mov QWORD PTR [rsp+32], rbp ; BuildingOwner = Trap itself
|
||||
mov QWORD PTR [rsp+40], rsi ; SpawningController
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_init:
|
||||
|
||||
; Trap->AttachedTo = pParams->AttachedActor
|
||||
mov rax, QWORD PTR [r13 + DECO_AttachedActor]
|
||||
mov QWORD PTR [rbp + ABTRAP_AttachedTo], rax
|
||||
|
||||
; OnRep_AttachedTo()
|
||||
mov rax, QWORD PTR [pFn_OnRep_AttachedTo]
|
||||
test rax, rax
|
||||
jnz @@have_rat
|
||||
lea rcx, pFn_OnRep_AttachedTo
|
||||
lea rdx, szFn_OnRep_AttachedTo
|
||||
call Spawners__LoadFn
|
||||
@@have_rat:
|
||||
test rax, rax
|
||||
jz @@skip_rat
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
@@skip_rat:
|
||||
|
||||
; Trap->Team = PlayerState->TeamIndex
|
||||
; PlayerState at Controller + 0x248 (ACONTROLLER_PlayerState)
|
||||
mov rdi, QWORD PTR [rsi + 0248h] ; PlayerState
|
||||
test rdi, rdi
|
||||
jz @@skip_team
|
||||
movzx eax, BYTE PTR [rdi + 0F60h] ; AFortPlayerStateAthena::TeamIndex
|
||||
mov BYTE PTR [rbp + ABTRAP_Team], al
|
||||
@@skip_team:
|
||||
|
||||
; Apply abilities from AbilitySet
|
||||
; Trap->AbilitySet at ABTRAP_AbilitySet_offset = 0x0E20
|
||||
mov r14, QWORD PTR [rbp + ABTRAP_AbilitySet_offset]
|
||||
test r14, r14
|
||||
jz @@done
|
||||
|
||||
; AbilitySet->GameplayAbilities (TArray<UClass*>) at +0x38
|
||||
mov rsi, QWORD PTR [r14 + UFAS_GameplayAbilities] ; Data
|
||||
mov edi, DWORD PTR [r14 + UFAS_GameplayAbilities + 8] ; Num
|
||||
test rsi, rsi
|
||||
jz @@done
|
||||
xor r13d, r13d ; i = 0
|
||||
@@ability_loop:
|
||||
cmp r13d, edi
|
||||
jge @@done
|
||||
mov rax, QWORD PTR [rsi + r13*8]
|
||||
test rax, rax
|
||||
jz @@next_ability
|
||||
; Pawn = rbx
|
||||
mov rcx, rbx
|
||||
mov rdx, rax
|
||||
call Abilities_GrantGameplayAbility
|
||||
@@next_ability:
|
||||
inc r13d
|
||||
jmp @@ability_loop
|
||||
|
||||
@@done:
|
||||
add rsp, 70h
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbx
|
||||
pop rbp
|
||||
ret
|
||||
Spawners_SpawnDeco ENDP
|
||||
|
||||
; Spawners_SpawnActor_ByLocation
|
||||
; In : RCX = UClass* (actor class)
|
||||
; RDX = FVector* (location)
|
||||
; R8 = FRotator* (rotation - ignored; identity used)
|
||||
; R9 = AActor* (owner)
|
||||
; Out: RAX = spawned AActor* or NULL
|
||||
;
|
||||
; Thin wrapper: moves Owner from R9 to R8, calls Spawners_SpawnActor.
|
||||
Spawners_SpawnActor_ByLocation PROC
|
||||
mov r8, r9
|
||||
jmp Spawners_SpawnActor
|
||||
Spawners_SpawnActor_ByLocation ENDP
|
||||
|
||||
|
||||
END
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
INCLUDE asm\include\master.inc
|
||||
|
||||
; Team struct offsets
|
||||
TEAM_maxTeamSize EQU 000h
|
||||
TEAM_TeamPosition EQU 004h
|
||||
TEAM_Members_Data EQU 008h
|
||||
TEAM_Members_Num EQU 010h
|
||||
TEAM_Members_Max EQU 014h
|
||||
TEAM_SIZEOF EQU 018h
|
||||
|
||||
; PlayerTeams struct offsets
|
||||
PT_maxTeamSize EQU 000h
|
||||
PT_lastAssigned EQU 004h
|
||||
PT_Teams_Data EQU 008h
|
||||
PT_Teams_Num EQU 010h
|
||||
PT_Teams_Max EQU 014h
|
||||
PT_SIZEOF EQU 018h
|
||||
|
||||
; AController::PlayerState
|
||||
ACTRL_PlayerState EQU 0248h
|
||||
|
||||
; AFortPlayerStateAthena field offsets
|
||||
PS_PlayerTeam EQU 08F0h ; AFortTeamInfo*
|
||||
PS_TeamIndex EQU 0F60h ; byte (EFortTeam)
|
||||
PS_SquadId EQU 10F0h ; byte
|
||||
|
||||
; AFortTeamInfo field offsets
|
||||
FTI_TeamMembers EQU 0320h ; TArray<AController*>
|
||||
FTI_Team EQU 0330h ; byte (EFortTeam)
|
||||
|
||||
.const
|
||||
|
||||
sz_OnRep_SquadId BYTE "Function FortniteGame.FortPlayerStateAthena.OnRep_SquadId",0
|
||||
sz_OnRep_PlayerTeam BYTE "Function FortniteGame.FortPlayerState.OnRep_PlayerTeam",0
|
||||
|
||||
.data?
|
||||
|
||||
fn_OnRep_SquadId QWORD ?
|
||||
fn_OnRep_PlayerTeam QWORD ?
|
||||
|
||||
.code
|
||||
|
||||
; Team_New
|
||||
; In : ECX = TeamPosition (EFortTeam, DWORD)
|
||||
; EDX = maxTeamSize (DWORD)
|
||||
; Out: RAX = Team* (heap-allocated)
|
||||
;
|
||||
; Stack: push rbx push rbp + sub 28h -> 2+2*8=32; 8-32=8; sub28h=40; 8-40=0
|
||||
Team_New PROC
|
||||
push rbx
|
||||
push rbp
|
||||
sub rsp, 28h
|
||||
|
||||
mov ebx, ecx ; EBX = TeamPosition
|
||||
mov ebp, edx ; EBP = maxTeamSize
|
||||
|
||||
mov ecx, TEAM_SIZEOF
|
||||
mov edx, 8 ; alignment = 8
|
||||
call QWORD PTR [FMemory_Malloc]
|
||||
test rax, rax
|
||||
jz @TN_done
|
||||
|
||||
; Zero the allocation
|
||||
push rax
|
||||
mov rcx, rax
|
||||
xor edx, edx
|
||||
mov r8d, TEAM_SIZEOF
|
||||
call memset
|
||||
pop rax
|
||||
|
||||
mov DWORD PTR [rax + TEAM_maxTeamSize], ebp
|
||||
mov DWORD PTR [rax + TEAM_TeamPosition], ebx
|
||||
|
||||
@TN_done:
|
||||
add rsp, 28h
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
Team_New ENDP
|
||||
|
||||
|
||||
; Team_Num
|
||||
; In : RCX = Team*
|
||||
; Out: EAX = member count
|
||||
Team_Num PROC
|
||||
mov eax, DWORD PTR [rcx + TEAM_Members_Num]
|
||||
ret
|
||||
Team_Num ENDP
|
||||
|
||||
|
||||
; Team_AddPlayer
|
||||
; In : RCX = Team*
|
||||
; RDX = AFortPlayerController* (pc)
|
||||
;
|
||||
; Stack: push rbx push rbp + sub 28h -> 0 (same as Team_New)
|
||||
Team_AddPlayer PROC
|
||||
push rbx
|
||||
push rbp
|
||||
sub rsp, 28h
|
||||
|
||||
mov rbx, rcx ; RBX = Team*
|
||||
mov rbp, rdx ; RBP = pc
|
||||
|
||||
; if Num() == maxTeamSize, return
|
||||
mov eax, DWORD PTR [rbx + TEAM_Members_Num]
|
||||
cmp eax, DWORD PTR [rbx + TEAM_maxTeamSize]
|
||||
jge @TAP_done
|
||||
|
||||
; TArray_Add(&Members, &pc, 8)
|
||||
lea rcx, [rbx + TEAM_Members_Data] ; ptr to TArrayHeader (Data QWORD at start)
|
||||
lea rdx, QWORD PTR [rbp]
|
||||
push rbp
|
||||
mov r8d, 8
|
||||
; TArray_Add expects (TArrayHeader*, elem*, elem_size)
|
||||
; need to pass address of pc on stack
|
||||
lea rdx, [rsp] ; &pc on stack (rbp was pushed to [rsp])
|
||||
call TArray_Add
|
||||
add rsp, 8 ; pop the pushed rbp value
|
||||
|
||||
; InitializePlayer(Team*, pc)
|
||||
mov rcx, rbx
|
||||
mov rdx, rbp
|
||||
call Team_InitializePlayer
|
||||
|
||||
@TAP_done:
|
||||
add rsp, 28h
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
Team_AddPlayer ENDP
|
||||
|
||||
|
||||
; Team_Kick
|
||||
; In : RCX = Team*
|
||||
Team_Kick PROC
|
||||
ret
|
||||
Team_Kick ENDP
|
||||
|
||||
|
||||
; Team_InitializePlayer
|
||||
; In : RCX = Team*
|
||||
; RDX = AFortPlayerController* (pc)
|
||||
;
|
||||
; Sets TeamIndex, PlayerTeam->Team, SquadId on the player state,
|
||||
; appends pc to PlayerTeam->TeamMembers, fires OnRep_SquadId and OnRep_PlayerTeam.
|
||||
;
|
||||
; Stack: 5 pushes (40) + sub 60h (96) = 136; 8-136=-128=0
|
||||
;
|
||||
; Frame:
|
||||
; [+00..+1F] shadow
|
||||
; [+20..+27] arg5 slot
|
||||
; [+28..+2F] zero params scratch (for zero-arg ProcessEvent calls)
|
||||
Team_InitializePlayer PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 60h
|
||||
|
||||
mov rbx, rcx ; RBX = Team*
|
||||
mov rdi, rdx ; RDI = pc
|
||||
|
||||
; PlayerState = pc->PlayerState (AController+0x248)
|
||||
mov rsi, QWORD PTR [rdi + ACTRL_PlayerState]
|
||||
test rsi, rsi
|
||||
jz @TIP_done
|
||||
|
||||
; EBP = TeamPosition
|
||||
mov ebp, DWORD PTR [rbx + TEAM_TeamPosition]
|
||||
|
||||
; PlayerState->TeamIndex = TeamPosition
|
||||
mov BYTE PTR [rsi + PS_TeamIndex], bpl
|
||||
|
||||
; PlayerState->PlayerTeam->Team = TeamPosition
|
||||
mov r12, QWORD PTR [rsi + PS_PlayerTeam]
|
||||
test r12, r12
|
||||
jz @TIP_skip_team_set
|
||||
mov BYTE PTR [r12 + FTI_Team], bpl
|
||||
|
||||
; PlayerState->PlayerTeam->TeamMembers.Add(pc)
|
||||
; TArray_Add(&TeamMembers, &pc, 8)
|
||||
push rdi ; push pc value onto stack
|
||||
lea rcx, [r12 + FTI_TeamMembers] ; &TArrayHeader
|
||||
lea rdx, [rsp] ; &pc value on stack
|
||||
mov r8d, 8
|
||||
call TArray_Add
|
||||
add rsp, 8 ; pop pc value
|
||||
|
||||
@TIP_skip_team_set:
|
||||
; PlayerState->SquadId = (TeamPosition - 2) + 1 = TeamPosition - 1
|
||||
mov eax, ebp
|
||||
sub eax, 1
|
||||
mov BYTE PTR [rsi + PS_SquadId], al
|
||||
|
||||
; Lazy-load OnRep_SquadId
|
||||
mov rax, QWORD PTR [fn_OnRep_SquadId]
|
||||
test rax, rax
|
||||
jnz @TIP_sq_ok
|
||||
lea rcx, [sz_OnRep_SquadId]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [fn_OnRep_SquadId], rax
|
||||
@TIP_sq_ok:
|
||||
test rax, rax
|
||||
jz @TIP_skip_sq
|
||||
; ProcessEvent(PlayerState, fn, NULL_params)
|
||||
xor r8d, r8d
|
||||
mov QWORD PTR [rsp + 28h], 0 ; zero params scratch
|
||||
mov rcx, rsi
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 28h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@TIP_skip_sq:
|
||||
; Lazy-load OnRep_PlayerTeam
|
||||
mov rax, QWORD PTR [fn_OnRep_PlayerTeam]
|
||||
test rax, rax
|
||||
jnz @TIP_pt_ok
|
||||
lea rcx, [sz_OnRep_PlayerTeam]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [fn_OnRep_PlayerTeam], rax
|
||||
@TIP_pt_ok:
|
||||
test rax, rax
|
||||
jz @TIP_done
|
||||
xor r8d, r8d
|
||||
mov QWORD PTR [rsp + 28h], 0
|
||||
mov rcx, rsi
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 28h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@TIP_done:
|
||||
add rsp, 60h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
Team_InitializePlayer ENDP
|
||||
|
||||
|
||||
; PlayerTeams_New
|
||||
; In : ECX = maxTeamSize (DWORD)
|
||||
; Out: RAX = PlayerTeams* (heap-allocated)
|
||||
;
|
||||
; Stack: push rbx + sub 28h -> 1+8=16; 8-16=8; sub28h: 8-40=0
|
||||
PlayerTeams_New PROC
|
||||
push rbx
|
||||
sub rsp, 28h
|
||||
|
||||
mov ebx, ecx ; EBX = maxTeamSize
|
||||
|
||||
mov ecx, PT_SIZEOF
|
||||
mov edx, 8
|
||||
call QWORD PTR [FMemory_Malloc]
|
||||
test rax, rax
|
||||
jz @PTN_done
|
||||
|
||||
push rax
|
||||
mov rcx, rax
|
||||
xor edx, edx
|
||||
mov r8d, PT_SIZEOF
|
||||
call memset
|
||||
pop rax
|
||||
|
||||
mov DWORD PTR [rax + PT_maxTeamSize], ebx
|
||||
; lastAssignedIndex = 0, Teams array = zero (already zeroed)
|
||||
|
||||
@PTN_done:
|
||||
add rsp, 28h
|
||||
pop rbx
|
||||
ret
|
||||
PlayerTeams_New ENDP
|
||||
|
||||
|
||||
; PlayerTeams_GetNextSlot
|
||||
; In : RCX = PlayerTeams*
|
||||
; Out: EAX = EFortTeam value = Teams.Num + 2
|
||||
PlayerTeams_GetNextSlot PROC
|
||||
mov eax, DWORD PTR [rcx + PT_Teams_Num]
|
||||
add eax, 2
|
||||
ret
|
||||
PlayerTeams_GetNextSlot ENDP
|
||||
|
||||
|
||||
; PlayerTeams_FindAvailableSpot
|
||||
; In : RCX = PlayerTeams*
|
||||
; Out: RAX = Team* or NULL
|
||||
;
|
||||
; If maxTeamSize > 0: find first team with Num < maxTeamSize.
|
||||
; If maxTeamSize < 0 (round-robin): abs(maxTeamSize) teams must exist;
|
||||
; returns Teams[lastAssigned] and increments lastAssigned.
|
||||
;
|
||||
; Stack: push rbx push rbp + sub 28h -> 0
|
||||
PlayerTeams_FindAvailableSpot PROC
|
||||
push rbx
|
||||
push rbp
|
||||
sub rsp, 28h
|
||||
|
||||
mov rbx, rcx ; RBX = PlayerTeams*
|
||||
|
||||
mov ebp, DWORD PTR [rbx + PT_maxTeamSize]
|
||||
test ebp, ebp
|
||||
js @PFas_roundrobin
|
||||
|
||||
; maxTeamSize >= 0: linear scan for available slot
|
||||
mov ecx, DWORD PTR [rbx + PT_Teams_Num]
|
||||
test ecx, ecx
|
||||
jz @PFas_null
|
||||
mov rdx, QWORD PTR [rbx + PT_Teams_Data]
|
||||
test rdx, rdx
|
||||
jz @PFas_null
|
||||
|
||||
xor eax, eax ; i = 0
|
||||
@PFas_linear:
|
||||
cmp eax, ecx
|
||||
jge @PFas_null
|
||||
mov r8, QWORD PTR [rdx + rax*8] ; Teams[i] (Team*)
|
||||
test r8, r8
|
||||
jz @PFas_linear_next
|
||||
mov r9d, DWORD PTR [r8 + TEAM_Members_Num]
|
||||
cmp r9d, ebp
|
||||
jl @PFas_found ; Num < maxTeamSize -> available
|
||||
@PFas_linear_next:
|
||||
inc eax
|
||||
jmp @PFas_linear
|
||||
|
||||
@PFas_roundrobin:
|
||||
; abs(maxTeamSize) teams must exist
|
||||
neg ebp
|
||||
mov ecx, DWORD PTR [rbx + PT_Teams_Num]
|
||||
cmp ecx, ebp
|
||||
jl @PFas_null ; not enough teams yet
|
||||
|
||||
mov eax, DWORD PTR [rbx + PT_lastAssigned]
|
||||
mov rdx, QWORD PTR [rbx + PT_Teams_Data]
|
||||
test rdx, rdx
|
||||
jz @PFas_null
|
||||
mov rax, QWORD PTR [rdx + rax*8] ; Teams[lastAssigned]
|
||||
|
||||
; Increment lastAssigned (wrap at Teams.Num-1)
|
||||
mov ecx, DWORD PTR [rbx + PT_lastAssigned]
|
||||
inc ecx
|
||||
mov edx, DWORD PTR [rbx + PT_Teams_Num]
|
||||
cmp ecx, edx
|
||||
jl @PFas_rr_nowrap
|
||||
xor ecx, ecx
|
||||
@PFas_rr_nowrap:
|
||||
mov DWORD PTR [rbx + PT_lastAssigned], ecx
|
||||
jmp @PFas_found
|
||||
|
||||
@PFas_found:
|
||||
add rsp, 28h
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
@PFas_null:
|
||||
xor eax, eax
|
||||
add rsp, 28h
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
PlayerTeams_FindAvailableSpot ENDP
|
||||
|
||||
|
||||
; PlayerTeams_AddPlayerToRandomTeam
|
||||
; In : RCX = PlayerTeams*
|
||||
; RDX = AFortPlayerController* (pc)
|
||||
;
|
||||
; Finds or creates a team with space, then adds the player.
|
||||
;
|
||||
; Stack: 4 pushes (32) + sub 38h (56) = 88; 8-88=-80=0
|
||||
PlayerTeams_AddPlayerToRandomTeam PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
sub rsp, 38h
|
||||
|
||||
mov rbx, rcx ; RBX = PlayerTeams*
|
||||
mov rdi, rdx ; RDI = pc
|
||||
|
||||
; Try FindAvailableSpot
|
||||
mov rcx, rbx
|
||||
call PlayerTeams_FindAvailableSpot
|
||||
test rax, rax
|
||||
jz @PTAPART_new_team
|
||||
|
||||
; Available: add player to existing team
|
||||
mov rcx, rax
|
||||
mov rdx, rdi
|
||||
call Team_AddPlayer
|
||||
jmp @PTAPART_done
|
||||
|
||||
@PTAPART_new_team:
|
||||
; No spot: create a new Team and add to PlayerTeams.Teams array
|
||||
mov rcx, rbx
|
||||
call PlayerTeams_GetNextSlot ; EAX = new TeamPosition
|
||||
mov esi, eax ; ESI = TeamPosition
|
||||
|
||||
; Team_New(TeamPosition, maxTeamSize)
|
||||
mov ecx, esi
|
||||
mov edx, DWORD PTR [rbx + PT_maxTeamSize]
|
||||
call Team_New
|
||||
test rax, rax
|
||||
jz @PTAPART_done
|
||||
mov rbp, rax ; RBP = new Team*
|
||||
|
||||
; TArray_Add(&Teams, &newTeam, 8)
|
||||
push rbp ; push Team* onto stack
|
||||
lea rcx, [rbx + PT_Teams_Data] ; &TArrayHeader for Teams
|
||||
lea rdx, [rsp] ; &(Team* on stack)
|
||||
mov r8d, 8
|
||||
call TArray_Add
|
||||
add rsp, 8
|
||||
|
||||
; Team_AddPlayer(newTeam, pc)
|
||||
mov rcx, rbp
|
||||
mov rdx, rdi
|
||||
call Team_AddPlayer
|
||||
|
||||
@PTAPART_done:
|
||||
add rsp, 38h
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
ret
|
||||
PlayerTeams_AddPlayerToRandomTeam ENDP
|
||||
|
||||
|
||||
; PlayerTeams_AddPlayerToTeam - alias for AddPlayerToRandomTeam
|
||||
PlayerTeams_AddPlayerToTeam PROC
|
||||
jmp PlayerTeams_AddPlayerToRandomTeam
|
||||
PlayerTeams_AddPlayerToTeam ENDP
|
||||
|
||||
END
|
||||
+326
-5
@@ -8,8 +8,8 @@ AFPCA_bIsDisconnecting EQU 1674h ; AFortPlayerControllerAthena::bIsDiscon
|
||||
AOBH_ListenPort EQU 0208h ; AOnlineBeaconHost::ListenPort (int32)
|
||||
AOBH_NetDriver EQU 0220h ; AOnlineBeaconHost::Driver (UNetDriver*)
|
||||
ABSMA_EditingPlayer EQU 0480h ; ABuildingSMActor::EditingPlayer
|
||||
UWORLD_NetDriver EQU 0E8h ; UWorld::NetDriver (UNetDriver*)
|
||||
UWORLD_AuthorityGameMode EQU 0118h ; UWorld::AuthorityGameMode (AGameMode*)
|
||||
UWORLD_NetDriver EQU 038h ; UWorld::NetDriver (UNetDriver*)
|
||||
UWORLD_AuthorityGameMode EQU 0140h ; UWorld::AuthorityGameMode (AGameMode*)
|
||||
FURL_Port EQU 020h ; FURL::Port (DWORD, per structs.inc)
|
||||
VTABLE_ServerReplicateActors EQU (083h * 8) ; vtable byte offset for slot 0x53
|
||||
|
||||
@@ -32,6 +32,11 @@ pFn_K2_DestroyActor QWORD ?
|
||||
pFn_ClientOnPawnRevived QWORD ?
|
||||
pFn_ForceNetUpdate QWORD ?
|
||||
pFn_OnRep_ReplicatedAnimMontage QWORD ?
|
||||
pFn_InitKismetBuildingActor QWORD ?
|
||||
pFn_SilentDie QWORD ?
|
||||
pFn_OnRep_EditingPlayer QWORD ?
|
||||
pFn_OnRep_EditActor QWORD ?
|
||||
pFn_K2_GetActorLocation QWORD ?
|
||||
|
||||
; Cached class pointers (used by ReadyToStartMatch)
|
||||
pClass_FortOnlineBeaconHost QWORD ?
|
||||
@@ -70,6 +75,11 @@ szFn_K2_DestroyActor DB "Function Engine.Actor.K2_DestroyActor",
|
||||
szFn_ClientOnPawnRevived DB "Function FortniteGame.FortPlayerPawn.ClientOnPawnRevived", 0
|
||||
szFn_ForceNetUpdate DB "Function Engine.Actor.ForceNetUpdate", 0
|
||||
szFn_OnRepRepAnimMontage DB "Function GameplayAbilities.AbilitySystemComponent.OnRep_ReplicatedAnimMontageForMesh", 0
|
||||
szFn_InitKismetBuildingActor DB "Function FortniteGame.BuildingSMActor.InitializeKismetSpawnedBuildingActor", 0
|
||||
szFn_SilentDie DB "Function FortniteGame.BuildingActor.SilentDie", 0
|
||||
szFn_OnRep_EditingPlayer DB "Function FortniteGame.BuildingSMActor.OnRep_EditingPlayer", 0
|
||||
szFn_OnRep_EditActor DB "Function FortniteGame.FortWeap_EditingTool.OnRep_EditActor", 0
|
||||
szFn_K2_GetActorLocation DB "Function Engine.Actor.K2_GetActorLocation", 0
|
||||
|
||||
szClass_FortOnlineBeaconHost DB "Class FortniteGame.FortOnlineBeaconHost", 0
|
||||
|
||||
@@ -194,7 +204,7 @@ PEHOOK_ServerAttemptInventoryDrop PROC
|
||||
|
||||
mov rcx, rax
|
||||
mov rdx, rsi
|
||||
call Inventory_Update ; TODO: replace with Inventory_OnDrop(PC, Params)
|
||||
call Inventory_OnDrop
|
||||
|
||||
@@done:
|
||||
add rsp, 32
|
||||
@@ -225,7 +235,7 @@ PEHOOK_ServerHandlePickup PROC
|
||||
|
||||
mov rcx, rax ; PC
|
||||
mov rdx, rsi ; Params
|
||||
call Inventory_Update ; TODO: Inventory_OnPickup(PC, Params)
|
||||
call Inventory_OnPickup
|
||||
|
||||
@@done:
|
||||
add rsp, 32
|
||||
@@ -718,25 +728,336 @@ PEHOOK_ServerAttemptAircraftJump ENDP
|
||||
; RCX = AFortAthenaAircraft* (the aircraft)
|
||||
; Iterates passengers and forces ServerAttemptAircraftJump on each.
|
||||
PEHOOK_OnAircraftExitedDropZone PROC
|
||||
; TODO: iterate aircraft->Passengers TArray, call jump for each
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
sub rsp, 20h
|
||||
|
||||
; HostBeacon->NetDriver (AOBH_NetDriver = 0x220)
|
||||
mov rbx, QWORD PTR [HostBeacon]
|
||||
test rbx, rbx
|
||||
jz @OAEDZ_done
|
||||
mov rbx, QWORD PTR [rbx + AOBH_NetDriver]
|
||||
test rbx, rbx
|
||||
jz @OAEDZ_done
|
||||
|
||||
; ClientConnections TArray at UNetDriver+0x80
|
||||
mov rsi, QWORD PTR [rbx + 080h] ; Connections.Data
|
||||
test rsi, rsi
|
||||
jz @OAEDZ_done
|
||||
mov ebp, DWORD PTR [rbx + 088h] ; Connections.Num
|
||||
test ebp, ebp
|
||||
jz @OAEDZ_done
|
||||
|
||||
xor ebx, ebx ; i = 0
|
||||
@OAEDZ_loop:
|
||||
cmp ebx, ebp
|
||||
jge @OAEDZ_done
|
||||
mov rcx, QWORD PTR [rsi + rbx*8] ; Connections[i] (UNetConnection*)
|
||||
test rcx, rcx
|
||||
jz @OAEDZ_next
|
||||
mov rcx, QWORD PTR [rcx + 030h] ; PlayerController
|
||||
test rcx, rcx
|
||||
jz @OAEDZ_next
|
||||
call GameModeBase_InitPawn ; (PC in RCX)
|
||||
@OAEDZ_next:
|
||||
inc ebx
|
||||
jmp @OAEDZ_loop
|
||||
|
||||
@OAEDZ_done:
|
||||
add rsp, 20h
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
xor al, al
|
||||
ret
|
||||
PEHOOK_OnAircraftExitedDropZone ENDP
|
||||
|
||||
; PEHOOK_ServerCreateBuildingActor - spawn a building piece
|
||||
PEHOOK_ServerCreateBuildingActor PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
sub rsp, 48h
|
||||
|
||||
mov rdi, rcx ; RDI = PC
|
||||
mov rsi, rdx ; RSI = Params
|
||||
|
||||
test rdi, rdi
|
||||
jz @SCBA_done
|
||||
test rsi, rsi
|
||||
jz @SCBA_done
|
||||
|
||||
; BuildingClass = Params->BuildingClassData.BuildingClass
|
||||
mov rbx, QWORD PTR [rsi]
|
||||
test rbx, rbx
|
||||
jz @SCBA_done
|
||||
|
||||
; SpawnActor(BuildingClass, &BuildLoc, PC)
|
||||
mov rcx, rbx
|
||||
lea rdx, [rsi + 010h] ; &BuildLoc
|
||||
mov r8, rdi
|
||||
call Spawners_SpawnActor
|
||||
mov rbp, rax ; RBP = BuildingActor
|
||||
test rbp, rbp
|
||||
jz @SCBA_done
|
||||
|
||||
; DynamicBuildingPlacementType = 2 (DestroyAnythingThatCollides)
|
||||
mov BYTE PTR [rbp + 04B0h], 2
|
||||
|
||||
; BuildingActor->Team = PC->PlayerState->TeamIndex
|
||||
mov rax, QWORD PTR [rdi + 0248h] ; PlayerState
|
||||
test rax, rax
|
||||
jz @SCBA_init
|
||||
movzx ecx, BYTE PTR [rax + 0F60h] ; TeamIndex
|
||||
mov BYTE PTR [rbp + 04C5h], cl
|
||||
|
||||
@SCBA_init:
|
||||
; InitializeKismetSpawnedBuildingActor(BuildingActor, BuildingOwner=self, PC)
|
||||
; Params: BuildingOwner(8) + SpawningController(8) = 0x10 at [rsp+20h]
|
||||
mov rax, QWORD PTR [pFn_InitKismetBuildingActor]
|
||||
test rax, rax
|
||||
jnz @SCBA_have_init
|
||||
lea rcx, [szFn_InitKismetBuildingActor]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [pFn_InitKismetBuildingActor], rax
|
||||
@SCBA_have_init:
|
||||
test rax, rax
|
||||
jz @SCBA_done
|
||||
mov QWORD PTR [rsp + 20h], rbp ; BuildingOwner = BuildingActor
|
||||
mov QWORD PTR [rsp + 28h], rdi ; SpawningController = PC
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 20h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@SCBA_done:
|
||||
add rsp, 48h
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
xor al, al
|
||||
ret
|
||||
PEHOOK_ServerCreateBuildingActor ENDP
|
||||
|
||||
; PEHOOK_ServerBeginEditingBuildingActor - start building edit mode
|
||||
PEHOOK_ServerBeginEditingBuildingActor PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 60h
|
||||
|
||||
mov r12, rcx ; R12 = PC
|
||||
; BuildingActorToEdit = Params[+0]
|
||||
mov rax, QWORD PTR [rdx]
|
||||
mov QWORD PTR [rsp + 30h], rax ; save BuildingActorToEdit
|
||||
|
||||
test r12, r12
|
||||
jz @SBEBA_done
|
||||
test rax, rax
|
||||
jz @SBEBA_done
|
||||
|
||||
; Pawn = PC->Pawn
|
||||
mov rbx, QWORD PTR [r12 + ACONTROLLER_Pawn]
|
||||
test rbx, rbx
|
||||
jz @SBEBA_done
|
||||
|
||||
; GetEntryInSlot(PC, Slot=0, Item=0, Bars=Primary=0)
|
||||
mov rcx, r12
|
||||
xor edx, edx
|
||||
xor r8d, r8d
|
||||
xor r9d, r9d
|
||||
call Inventory_GetEntryInSlot
|
||||
test rax, rax
|
||||
jz @SBEBA_done
|
||||
mov rbp, rax ; RBP = &FFortItemEntry for edit tool
|
||||
|
||||
; ItemDef = entry[+0x18]
|
||||
mov rdi, QWORD PTR [rbp + 018h]
|
||||
test rdi, rdi
|
||||
jz @SBEBA_done
|
||||
|
||||
; Copy FGuid from entry[+0x50]
|
||||
mov rax, QWORD PTR [rbp + 050h]
|
||||
mov QWORD PTR [rsp + 40h], rax
|
||||
mov rax, QWORD PTR [rbp + 058h]
|
||||
mov QWORD PTR [rsp + 48h], rax
|
||||
|
||||
; EquipWeaponDefinition(Pawn, ItemDef, &Guid)
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
lea r8, [rsp + 40h]
|
||||
call Inventory_EquipWeaponDefinition
|
||||
|
||||
; EditTool = Pawn->CurrentWeapon
|
||||
mov rsi, QWORD PTR [rbx + 07D0h] ; AFortPawn::CurrentWeapon
|
||||
test rsi, rsi
|
||||
jz @SBEBA_done
|
||||
|
||||
; EditTool->EditActor = BuildingActorToEdit
|
||||
mov rax, QWORD PTR [rsp + 30h]
|
||||
mov QWORD PTR [rsi + 0AB0h], rax ; AFortWeap_EditingTool::EditActor
|
||||
|
||||
; OnRep_EditActor(EditTool)
|
||||
mov rax, QWORD PTR [pFn_OnRep_EditActor]
|
||||
test rax, rax
|
||||
jnz @SBEBA_have_ea
|
||||
lea rcx, [szFn_OnRep_EditActor]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [pFn_OnRep_EditActor], rax
|
||||
@SBEBA_have_ea:
|
||||
test rax, rax
|
||||
jz @SBEBA_editing_player
|
||||
xor r8d, r8d
|
||||
mov QWORD PTR [rsp + 20h], r8
|
||||
mov rcx, rsi
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 20h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@SBEBA_editing_player:
|
||||
; BuildingActorToEdit->EditingPlayer = Pawn->PlayerState
|
||||
mov rcx, QWORD PTR [rsp + 30h] ; BuildingActorToEdit
|
||||
test rcx, rcx
|
||||
jz @SBEBA_done
|
||||
mov rax, QWORD PTR [rbx + APAWN_PlayerState]
|
||||
mov QWORD PTR [rcx + 0C70h], rax ; ABuildingSMActor::EditingPlayer
|
||||
|
||||
; OnRep_EditingPlayer(BuildingActorToEdit)
|
||||
mov rax, QWORD PTR [pFn_OnRep_EditingPlayer]
|
||||
test rax, rax
|
||||
jnz @SBEBA_have_ep
|
||||
lea rcx, [szFn_OnRep_EditingPlayer]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [pFn_OnRep_EditingPlayer], rax
|
||||
@SBEBA_have_ep:
|
||||
test rax, rax
|
||||
jz @SBEBA_done
|
||||
mov rcx, QWORD PTR [rsp + 30h]
|
||||
xor r8d, r8d
|
||||
mov QWORD PTR [rsp + 20h], r8
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 20h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@SBEBA_done:
|
||||
add rsp, 60h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
xor al, al
|
||||
ret
|
||||
PEHOOK_ServerBeginEditingBuildingActor ENDP
|
||||
|
||||
; PEHOOK_ServerEditBuildingActor - apply edit and re-spawn building
|
||||
PEHOOK_ServerEditBuildingActor PROC
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r12
|
||||
sub rsp, 70h
|
||||
|
||||
mov r12, rcx ; R12 = PC
|
||||
mov rbx, QWORD PTR [rdx] ; RBX = BuildingActorToEdit
|
||||
mov rdi, QWORD PTR [rdx + 008h] ; RDI = NewBuildingClass
|
||||
|
||||
test r12, r12
|
||||
jz @SEBA_done
|
||||
test rbx, rbx
|
||||
jz @SEBA_done
|
||||
test rdi, rdi
|
||||
jz @SEBA_done
|
||||
|
||||
; K2_GetActorLocation(BuildingActor) -> RetValue FVector at [rsp+20h]
|
||||
mov rax, QWORD PTR [pFn_K2_GetActorLocation]
|
||||
test rax, rax
|
||||
jnz @SEBA_have_loc
|
||||
lea rcx, [szFn_K2_GetActorLocation]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [pFn_K2_GetActorLocation], rax
|
||||
@SEBA_have_loc:
|
||||
xor esi, esi
|
||||
test rax, rax
|
||||
jz @SEBA_no_loc
|
||||
xor esi, esi
|
||||
mov DWORD PTR [rsp + 20h], esi ; X = 0
|
||||
mov DWORD PTR [rsp + 24h], esi ; Y = 0
|
||||
mov DWORD PTR [rsp + 28h], esi ; Z = 0
|
||||
mov rcx, rbx
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 20h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@SEBA_no_loc:
|
||||
; SilentDie(BuildingActor)
|
||||
mov rax, QWORD PTR [pFn_SilentDie]
|
||||
test rax, rax
|
||||
jnz @SEBA_have_sd
|
||||
lea rcx, [szFn_SilentDie]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [pFn_SilentDie], rax
|
||||
@SEBA_have_sd:
|
||||
test rax, rax
|
||||
jz @SEBA_spawn
|
||||
xor esi, esi
|
||||
mov QWORD PTR [rsp + 30h], rsi
|
||||
mov rcx, rbx
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 30h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@SEBA_spawn:
|
||||
; SpawnActor(NewBuildingClass, &Location, PC)
|
||||
mov rcx, rdi ; NewBuildingClass
|
||||
lea rdx, [rsp + 20h] ; &FVector (location)
|
||||
mov r8, r12 ; PC
|
||||
call Spawners_SpawnActor
|
||||
mov rbp, rax ; RBP = NewBuildingActor
|
||||
test rbp, rbp
|
||||
jz @SEBA_done
|
||||
|
||||
; DynamicBuildingPlacementType = 2
|
||||
mov BYTE PTR [rbp + 04B0h], 2
|
||||
|
||||
; NewBuilding->Team = PC->PlayerState->TeamIndex
|
||||
mov rax, QWORD PTR [r12 + 0248h]
|
||||
test rax, rax
|
||||
jz @SEBA_init
|
||||
movzx ecx, BYTE PTR [rax + 0F60h]
|
||||
mov BYTE PTR [rbp + 04C5h], cl
|
||||
|
||||
@SEBA_init:
|
||||
; InitializeKismetSpawnedBuildingActor
|
||||
mov rax, QWORD PTR [pFn_InitKismetBuildingActor]
|
||||
test rax, rax
|
||||
jnz @SEBA_have_init
|
||||
lea rcx, [szFn_InitKismetBuildingActor]
|
||||
call SDK_FindObject
|
||||
mov QWORD PTR [pFn_InitKismetBuildingActor], rax
|
||||
@SEBA_have_init:
|
||||
test rax, rax
|
||||
jz @SEBA_done
|
||||
mov QWORD PTR [rsp + 38h], rbp ; BuildingOwner = NewBuilding
|
||||
mov QWORD PTR [rsp + 40h], r12 ; SpawningController = PC
|
||||
mov rcx, rbp
|
||||
mov rdx, rax
|
||||
lea r8, [rsp + 38h]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
@SEBA_done:
|
||||
add rsp, 70h
|
||||
pop r12
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
xor al, al
|
||||
ret
|
||||
PEHOOK_ServerEditBuildingActor ENDP
|
||||
|
||||
Reference in New Issue
Block a user