game logic — abilities, teams, inventory, spawners, building hooks

This commit is contained in:
ApfelTeeSaft
2026-05-05 15:10:14 +02:00
parent 240dba4a89
commit 4ba1aa9c60
7 changed files with 4794 additions and 5 deletions
+11
View File
@@ -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
+478
View File
@@ -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
View File
@@ -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, &params)
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
View File
File diff suppressed because it is too large Load Diff
+830
View File
@@ -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 = &params.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
View File
@@ -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
View File
@@ -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