mirror of
https://github.com/ApfelTeeSaft/Perfectium.git
synced 2026-08-26 19:33:36 +00:00
2097 lines
60 KiB
NASM
2097 lines
60 KiB
NASM
INCLUDE asm\include\master.inc
|
|
|
|
; AFortPlayerController offsets
|
|
PC_WorldInventory EQU 1ED8h
|
|
PC_QuickBars EQU 1C38h
|
|
|
|
; AFortInventory offsets
|
|
AFI_Inventory EQU 0328h
|
|
AFI_bRequiresLocalUpdate EQU 0498h
|
|
|
|
; Within AFortInventory: FFortItemList field absolutes
|
|
AFI_ReplicatedEntries EQU 03D8h ; AFI+0x0328+0x00B0
|
|
AFI_ItemInstances EQU 0438h ; AFI+0x0328+0x0110
|
|
|
|
; AFortQuickBars offsets
|
|
QB_PrimarySlots EQU 0328h ; PrimaryQuickBar(+0x0318)+Slots(+0x10)
|
|
QB_SecondarySlots EQU 0448h ; SecondaryQuickBar(+0x0438)+Slots(+0x10)
|
|
QB_PriFocusedSlot EQU 0318h ; PrimaryQuickBar.CurrentFocusedSlot (int32)
|
|
|
|
; FQuickBarSlot size
|
|
QBSLOT_SIZE EQU 018h
|
|
QBSLOT_Items EQU 000h ; TArray<FGuid> within slot
|
|
|
|
; UFortWorldItem offsets
|
|
UWI_OwnerInventory EQU 01D8h
|
|
UWI_ItemEntry EQU 01E0h ; FFortItemEntry base
|
|
UWI_ItemEntry_Count EQU 01ECh ; +0x0C
|
|
UWI_ItemEntry_ItemDef EQU 01F8h ; +0x18
|
|
UWI_ItemEntry_LoadedAmmo EQU 0208h ; +0x28
|
|
UWI_ItemEntry_ItemGuid EQU 0230h ; +0x50
|
|
|
|
; FFortItemEntry size
|
|
FITEM_SIZEOF EQU 0C8h
|
|
|
|
; FGuid size
|
|
FGUID_SIZE EQU 010h
|
|
|
|
; AFortWeapon offsets
|
|
FW_WeaponData EQU 0368h
|
|
FW_ItemEntryGuid EQU 0988h
|
|
FW_AmmoCount EQU 099Ch
|
|
|
|
; EFortQuickBars enum values
|
|
QB_PRIMARY EQU 0
|
|
QB_SECONDARY EQU 1
|
|
|
|
; AController::PlayerState offset (for controller chain access)
|
|
ACTRL_Pawn EQU 0260h
|
|
|
|
.const
|
|
|
|
; UFunction paths
|
|
sz_HandleInvLocalUpdate BYTE "Function FortniteGame.FortInventory.HandleInventoryLocalUpdate",0
|
|
sz_ForceNetUpdate BYTE "Function Engine.Actor.ForceNetUpdate",0
|
|
sz_HandleWorldInvLocalUpdate BYTE "Function FortniteGame.FortPlayerController.HandleWorldInventoryLocalUpdate",0
|
|
sz_OnRep_PrimaryQB BYTE "Function FortniteGame.FortQuickBars.OnRep_PrimaryQuickBar",0
|
|
sz_OnRep_SecondaryQB BYTE "Function FortniteGame.FortQuickBars.OnRep_SecondaryQuickBar",0
|
|
sz_ForceUpdateQB BYTE "Function FortniteGame.FortPlayerController.ForceUpdateQuickbar",0
|
|
sz_ServerAddItemInternal BYTE "Function FortniteGame.FortQuickBars.ServerAddItemInternal",0
|
|
sz_ServerRemoveItemInternal BYTE "Function FortniteGame.FortQuickBars.ServerRemoveItemInternal",0
|
|
sz_EmptySlot BYTE "Function FortniteGame.FortQuickBars.EmptySlot",0
|
|
sz_ServerActivateSlotInt BYTE "Function FortniteGame.FortQuickBars.ServerActivateSlotInternal",0
|
|
sz_OnRep_QuickBar BYTE "Function FortniteGame.FortPlayerController.OnRep_QuickBar",0
|
|
sz_CreateTempItemBP BYTE "Function FortniteGame.FortItemDefinition.CreateTemporaryItemInstanceBP",0
|
|
sz_SetOwningController BYTE "Function FortniteGame.FortItem.SetOwningControllerForTemporaryItem",0
|
|
sz_EquipWeaponDef BYTE "Function FortniteGame.FortPawn.EquipWeaponDefinition",0
|
|
sz_OnRep_WeaponData BYTE "Function FortniteGame.FortWeapon.OnRep_ReplicatedWeaponData",0
|
|
sz_OnRep_AmmoCount BYTE "Function FortniteGame.FortWeapon.OnRep_AmmoCount",0
|
|
sz_ClientGivenTo BYTE "Function FortniteGame.FortWeapon.ClientGivenTo",0
|
|
sz_ClientInternalEquipWeapon BYTE "Function FortniteGame.FortPawn.ClientInternalEquipWeapon",0
|
|
sz_OnRep_CurrentWeapon BYTE "Function FortniteGame.FortPawn.OnRep_CurrentWeapon",0
|
|
sz_SetOwner BYTE "Function Engine.Actor.SetOwner",0
|
|
|
|
; Class names for Init
|
|
sz_FortQuickBarsClass BYTE "Class FortniteGame.FortQuickBars",0
|
|
sz_WallDef BYTE "FortBuildingItemDefinition BuildingItemData_Wall.BuildingItemData_Wall",0
|
|
sz_FloorDef BYTE "FortBuildingItemDefinition BuildingItemData_Floor.BuildingItemData_Floor",0
|
|
sz_StairDef BYTE "FortBuildingItemDefinition BuildingItemData_Stair_W.BuildingItemData_Stair_W",0
|
|
sz_RoofDef BYTE "FortBuildingItemDefinition BuildingItemData_RoofS.BuildingItemData_RoofS",0
|
|
sz_LaunchPad BYTE "FortTrapItemDefinition TID_Floor_Player_Launch_Pad_Athena.TID_Floor_Player_Launch_Pad_Athena",0
|
|
sz_WallElectric BYTE "FortTrapItemDefinition TID_Wall_Electric_Athena_R_T03.TID_Wall_Electric_Athena_R_T03",0
|
|
sz_FloorSpikes BYTE "FortTrapItemDefinition TID_Floor_Spikes_Athena_R_T03.TID_Floor_Spikes_Athena_R_T03",0
|
|
sz_Campfire BYTE "FortTrapItemDefinition TID_Floor_Player_Campfire_Athena.TID_Floor_Player_Campfire_Athena",0
|
|
sz_Wood BYTE "FortResourceItemDefinition WoodItemData.WoodItemData",0
|
|
sz_Stone BYTE "FortResourceItemDefinition StoneItemData.StoneItemData",0
|
|
sz_Metal BYTE "FortResourceItemDefinition MetalItemData.MetalItemData",0
|
|
sz_AmmoShells BYTE "FortAmmoItemDefinition AthenaAmmoDataShells.AthenaAmmoDataShells",0
|
|
sz_AmmoEnergy BYTE "FortAmmoItemDefinition AthenaAmmoDataEnergyCell.AthenaAmmoDataEnergyCell",0
|
|
sz_AmmoBulletsMedium BYTE "FortAmmoItemDefinition AthenaAmmoDataBulletsMedium.AthenaAmmoDataBulletsMedium",0
|
|
sz_AmmoBulletsLight BYTE "FortAmmoItemDefinition AthenaAmmoDataBulletsLight.AthenaAmmoDataBulletsLight",0
|
|
sz_AmmoBulletsHeavy BYTE "FortAmmoItemDefinition AthenaAmmoDataBulletsHeavy.AthenaAmmoDataBulletsHeavy",0
|
|
sz_AmmoRockets BYTE "FortAmmoItemDefinition AmmoDataRockets.AmmoDataRockets",0
|
|
sz_EditTool BYTE "FortEditToolItemDefinition EditTool.EditTool",0
|
|
|
|
.data?
|
|
|
|
; Cached UFunction pointers
|
|
fn_HandleInvLocalUpdate QWORD ?
|
|
fn_ForceNetUpdate QWORD ?
|
|
fn_HandleWorldInvLocalUpdate QWORD ?
|
|
fn_OnRep_PrimaryQB QWORD ?
|
|
fn_OnRep_SecondaryQB QWORD ?
|
|
fn_ForceUpdateQB QWORD ?
|
|
fn_ServerAddItemInternal QWORD ?
|
|
fn_ServerRemoveItemInternal QWORD ?
|
|
fn_EmptySlot QWORD ?
|
|
fn_ServerActivateSlotInt QWORD ?
|
|
fn_OnRep_QuickBar QWORD ?
|
|
fn_CreateTempItemBP QWORD ?
|
|
fn_SetOwningController QWORD ?
|
|
fn_EquipWeaponDef QWORD ?
|
|
fn_OnRep_WeaponData QWORD ?
|
|
fn_OnRep_AmmoCount QWORD ?
|
|
fn_ClientGivenTo QWORD ?
|
|
fn_ClientInternalEquipWeapon QWORD ?
|
|
fn_OnRep_CurrentWeapon QWORD ?
|
|
fn_SetOwner QWORD ?
|
|
|
|
; Cached item definitions for Init
|
|
def_Wall QWORD ?
|
|
def_Floor QWORD ?
|
|
def_Stair QWORD ?
|
|
def_Roof QWORD ?
|
|
def_LaunchPad QWORD ?
|
|
def_WallElectric QWORD ?
|
|
def_FloorSpikes QWORD ?
|
|
def_Campfire QWORD ?
|
|
def_Wood QWORD ?
|
|
def_Stone QWORD ?
|
|
def_Metal QWORD ?
|
|
def_AmmoShells QWORD ?
|
|
def_AmmoEnergy QWORD ?
|
|
def_AmmoBullMedium QWORD ?
|
|
def_AmmoBullLight QWORD ?
|
|
def_AmmoBullHeavy QWORD ?
|
|
def_AmmoRockets QWORD ?
|
|
def_EditTool QWORD ?
|
|
|
|
.code
|
|
|
|
; Internal helper: Inventory__LoadFn
|
|
; In : RSI = &cache QWORD
|
|
; RDI = szPath (BYTE*)
|
|
; Out: RAX = UFunction* (may be NULL on lookup failure)
|
|
; Allocates shadow for SDK_FindObject call (entry RSP=8 -> CALL->0 ).
|
|
Inventory__LoadFn PROC
|
|
mov rax, QWORD PTR [rsi]
|
|
test rax, rax
|
|
jnz @ILF_done
|
|
sub rsp, 20h
|
|
mov rcx, rdi
|
|
call SDK_FindObject
|
|
add rsp, 20h
|
|
mov QWORD PTR [rsi], rax
|
|
@ILF_done:
|
|
ret
|
|
Inventory__LoadFn ENDP
|
|
|
|
|
|
; Inventory_Update
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; EDX = Dirty (int, 0 = no mark-item-dirty)
|
|
; R8B = bRemovedItem (bool)
|
|
;
|
|
; Fires replication notifications on inventory and quickbar.
|
|
;
|
|
; Stack: 5 pushes (40) + sub 80h (128) = 168; 8-168=-160=0
|
|
;
|
|
; Frame:
|
|
; [+00..+1F] shadow
|
|
; [+20..+27] arg5 slot
|
|
; [+28..+2F] zero-param scratch (empty ProcessEvent params)
|
|
; [+30..+33] ForceUpdateQuickbar params (EFortQuickBars, BYTE->padded)
|
|
Inventory_Update PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 80h
|
|
|
|
; Save parameters in stable callee-saved registers
|
|
mov r12, rcx ; R12 = PC (callee-saved)
|
|
mov ebx, edx ; EBX = Dirty
|
|
movzx ebp, r8b ; EBP = bRemovedItem
|
|
|
|
; RSI/RDI will be used transiently for Inventory__LoadFn args;
|
|
; R12 persists across calls since r12 is callee-saved.
|
|
|
|
mov rdi, QWORD PTR [r12 + PC_WorldInventory] ; WorldInventory
|
|
test rdi, rdi
|
|
jz @IU2_done
|
|
|
|
; bRequiresLocalUpdate = true
|
|
mov BYTE PTR [rdi + AFI_bRequiresLocalUpdate], 1
|
|
|
|
; HandleInventoryLocalUpdate
|
|
lea rsi, [fn_HandleInvLocalUpdate]
|
|
lea rdi, [sz_HandleInvLocalUpdate]
|
|
call Inventory__LoadFn
|
|
mov rdi, QWORD PTR [r12 + PC_WorldInventory] ; reload after call
|
|
test rax, rax
|
|
jz @IU2_fnu
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 28h], r8
|
|
mov rcx, rdi
|
|
mov rdx, rax
|
|
lea r8, [rsp + 28h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_fnu:
|
|
; ForceNetUpdate on WorldInventory
|
|
lea rsi, [fn_ForceNetUpdate]
|
|
lea rdi, [sz_ForceNetUpdate]
|
|
call Inventory__LoadFn
|
|
mov rdi, QWORD PTR [r12 + PC_WorldInventory]
|
|
test rax, rax
|
|
jz @IU2_hwilu
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 28h], r8
|
|
mov rcx, rdi
|
|
mov rdx, rax
|
|
lea r8, [rsp + 28h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_hwilu:
|
|
; HandleWorldInventoryLocalUpdate on PC
|
|
lea rsi, [fn_HandleWorldInvLocalUpdate]
|
|
lea rdi, [sz_HandleWorldInvLocalUpdate]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IU2_priqb
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 28h], r8
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 28h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_priqb:
|
|
; Get QuickBars
|
|
mov rdi, QWORD PTR [r12 + PC_QuickBars]
|
|
test rdi, rdi
|
|
jz @IU2_done
|
|
|
|
; OnRep_PrimaryQuickBar
|
|
lea rsi, [fn_OnRep_PrimaryQB]
|
|
lea rdi, [sz_OnRep_PrimaryQB]
|
|
call Inventory__LoadFn
|
|
mov rdi, QWORD PTR [r12 + PC_QuickBars]
|
|
test rax, rax
|
|
jz @IU2_secqb
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 28h], r8
|
|
mov rcx, rdi
|
|
mov rdx, rax
|
|
lea r8, [rsp + 28h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_secqb:
|
|
; OnRep_SecondaryQuickBar
|
|
lea rsi, [fn_OnRep_SecondaryQB]
|
|
lea rdi, [sz_OnRep_SecondaryQB]
|
|
call Inventory__LoadFn
|
|
mov rdi, QWORD PTR [r12 + PC_QuickBars]
|
|
test rax, rax
|
|
jz @IU2_fnu_qb
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 28h], r8
|
|
mov rcx, rdi
|
|
mov rdx, rax
|
|
lea r8, [rsp + 28h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_fnu_qb:
|
|
; ForceNetUpdate on QuickBars
|
|
lea rsi, [fn_ForceNetUpdate]
|
|
lea rdi, [sz_ForceNetUpdate]
|
|
call Inventory__LoadFn
|
|
mov rdi, QWORD PTR [r12 + PC_QuickBars]
|
|
test rax, rax
|
|
jz @IU2_fuqb_pri
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 28h], r8
|
|
mov rcx, rdi
|
|
mov rdx, rax
|
|
lea r8, [rsp + 28h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_fuqb_pri:
|
|
; ForceUpdateQuickbar(Primary=0)
|
|
lea rsi, [fn_ForceUpdateQB]
|
|
lea rdi, [sz_ForceUpdateQB]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IU2_fuqb_sec
|
|
xor r8d, r8d
|
|
mov DWORD PTR [rsp + 30h], r8d ; EFortQuickBars = Primary = 0
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 30h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_fuqb_sec:
|
|
; ForceUpdateQuickbar(Secondary=1)
|
|
lea rsi, [fn_ForceUpdateQB]
|
|
lea rdi, [sz_ForceUpdateQB]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IU2_done
|
|
mov DWORD PTR [rsp + 30h], 1 ; EFortQuickBars = Secondary = 1
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 30h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IU2_done:
|
|
add rsp, 80h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_Update ENDP
|
|
|
|
|
|
; Inventory_IsGuidInInventory
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = FGuid* (16-byte guid to search for)
|
|
; Out: AL = 1 if found, 0 if not
|
|
;
|
|
; Searches PrimaryQuickBar.Slots for a matching guid.
|
|
;
|
|
; Stack: push rbx push rbp push rsi push rdi = 4 pushes (no CALL sites)
|
|
Inventory_IsGuidInInventory PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
|
|
mov rbx, rcx ; RBX = PC
|
|
mov rsi, rdx ; RSI = &Guid to find
|
|
|
|
; QuickBars
|
|
mov rcx, QWORD PTR [rbx + PC_QuickBars]
|
|
test rcx, rcx
|
|
jz @IGII_false
|
|
|
|
; PrimaryQuickBar.Slots TArray: Data at [QB+QB_PrimarySlots], Num at [QB+QB_PrimarySlots+8]
|
|
mov rbp, QWORD PTR [rcx + QB_PrimarySlots] ; Slots.Data
|
|
mov edi, DWORD PTR [rcx + QB_PrimarySlots + 8] ; Slots.Num
|
|
test rbp, rbp
|
|
jz @IGII_false
|
|
test edi, edi
|
|
jz @IGII_false
|
|
|
|
xor ebx, ebx ; slot index = 0
|
|
@IGII_slot_loop:
|
|
cmp ebx, edi
|
|
jge @IGII_false
|
|
|
|
; slot = Slots.Data + ebx * QBSLOT_SIZE
|
|
imul rax, rbx, QBSLOT_SIZE
|
|
add rax, rbp ; &Slots[slot]
|
|
|
|
; Items.Data at [rax + QBSLOT_Items]
|
|
mov rcx, QWORD PTR [rax + QBSLOT_Items] ; Items.Data
|
|
mov edx, DWORD PTR [rax + QBSLOT_Items + 8] ; Items.Num
|
|
test rcx, rcx
|
|
jz @IGII_slot_next
|
|
test edx, edx
|
|
jz @IGII_slot_next
|
|
|
|
xor r8d, r8d ; item index = 0
|
|
@IGII_item_loop:
|
|
cmp r8d, edx
|
|
jge @IGII_slot_next
|
|
|
|
; &Items[i] = rcx + i*16
|
|
imul r9, r8, FGUID_SIZE
|
|
add r9, rcx ; &Guid[i]
|
|
|
|
; Compare 16-byte GUIDs (two QWORD comparisons)
|
|
mov r10, QWORD PTR [r9]
|
|
mov r11, QWORD PTR [rsi]
|
|
cmp r10, r11
|
|
jne @IGII_item_next
|
|
mov r10, QWORD PTR [r9 + 8]
|
|
mov r11, QWORD PTR [rsi + 8]
|
|
cmp r10, r11
|
|
je @IGII_true
|
|
|
|
@IGII_item_next:
|
|
inc r8d
|
|
jmp @IGII_item_loop
|
|
|
|
@IGII_slot_next:
|
|
inc ebx
|
|
jmp @IGII_slot_loop
|
|
|
|
@IGII_false:
|
|
xor eax, eax
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
|
|
@IGII_true:
|
|
mov eax, 1
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_IsGuidInInventory ENDP
|
|
|
|
; IsValidGuid is the same logic
|
|
Inventory_IsValidGuid PROC
|
|
jmp Inventory_IsGuidInInventory
|
|
Inventory_IsValidGuid ENDP
|
|
|
|
|
|
; Inventory_GetEntryInSlot
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; EDX = Slot (int)
|
|
; R8D = Item (int, index within slot)
|
|
; R9D = Bars (EFortQuickBars: 0=Primary, 1=Secondary)
|
|
; Out: RAX = &FFortItemEntry (within UFortWorldItem) or NULL
|
|
;
|
|
; Stack: push rbx push rbp push rsi push rdi push r12 = 5 (no CALL sites)
|
|
Inventory_GetEntryInSlot PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
|
|
mov rbx, rcx ; RBX = PC
|
|
mov ebp, edx ; EBP = Slot
|
|
mov esi, r8d ; ESI = Item
|
|
mov r12d, r9d ; R12D = Bars
|
|
|
|
; Get QuickBars
|
|
mov rcx, QWORD PTR [rbx + PC_QuickBars]
|
|
test rcx, rcx
|
|
jz @IGETS_null
|
|
|
|
; Get Slots TArray based on Bars
|
|
test r12d, r12d
|
|
jnz @IGETS_secondary
|
|
mov rdi, QWORD PTR [rcx + QB_PrimarySlots] ; Primary.Slots.Data
|
|
mov edx, DWORD PTR [rcx + QB_PrimarySlots + 8]
|
|
jmp @IGETS_slots_ok
|
|
@IGETS_secondary:
|
|
mov rdi, QWORD PTR [rcx + QB_SecondarySlots]
|
|
mov edx, DWORD PTR [rcx + QB_SecondarySlots + 8]
|
|
|
|
@IGETS_slots_ok:
|
|
test rdi, rdi
|
|
jz @IGETS_null
|
|
cmp ebp, edx
|
|
jge @IGETS_null ; slot out of range
|
|
|
|
; &Slots[Slot]
|
|
imul rax, rbp, QBSLOT_SIZE
|
|
add rax, rdi
|
|
; Items.Data
|
|
mov rcx, QWORD PTR [rax + QBSLOT_Items]
|
|
mov edx, DWORD PTR [rax + QBSLOT_Items + 8]
|
|
test rcx, rcx
|
|
jz @IGETS_null
|
|
cmp esi, edx
|
|
jge @IGETS_null
|
|
; ToFindGuid = &Items[Item]
|
|
imul rax, rsi, FGUID_SIZE
|
|
add rax, rcx ; RAX = &FGuid to find
|
|
|
|
; Search ItemInstances for matching guid
|
|
mov rdi, QWORD PTR [rbx + PC_WorldInventory]
|
|
test rdi, rdi
|
|
jz @IGETS_null
|
|
mov rcx, QWORD PTR [rdi + AFI_ItemInstances] ; ItemInstances.Data
|
|
mov r8d, DWORD PTR [rdi + AFI_ItemInstances + 8] ; ItemInstances.Num
|
|
test rcx, rcx
|
|
jz @IGETS_null
|
|
|
|
xor r9d, r9d ; j = 0
|
|
@IGETS_inst_loop:
|
|
cmp r9d, r8d
|
|
jge @IGETS_null
|
|
mov r10, QWORD PTR [rcx + r9*8] ; ItemInstances[j] (UFortWorldItem*)
|
|
test r10, r10
|
|
jz @IGETS_inst_next
|
|
; Compare guid at [r10 + UWI_ItemEntry_ItemGuid] with [rax]
|
|
mov r11, QWORD PTR [r10 + UWI_ItemEntry_ItemGuid]
|
|
mov rbp, QWORD PTR [rax]
|
|
cmp r11, rbp
|
|
jne @IGETS_inst_next
|
|
mov r11, QWORD PTR [r10 + UWI_ItemEntry_ItemGuid + 8]
|
|
mov rbp, QWORD PTR [rax + 8]
|
|
cmp r11, rbp
|
|
je @IGETS_found
|
|
@IGETS_inst_next:
|
|
inc r9d
|
|
jmp @IGETS_inst_loop
|
|
|
|
@IGETS_found:
|
|
; Return &ItemInstance->ItemEntry
|
|
lea rax, [r10 + UWI_ItemEntry]
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
|
|
@IGETS_null:
|
|
xor eax, eax
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_GetEntryInSlot ENDP
|
|
|
|
|
|
; Inventory_AddItemToSlot
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = UFortWorldItemDefinition* (Definition)
|
|
; R8D = Slot (int)
|
|
; R9D = Bars (EFortQuickBars)
|
|
; [rsp+40] = Count (int, arg5)
|
|
; Out: RAX = &FFortItemEntry (within world item) or NULL
|
|
;
|
|
; Stack: 5 pushes (40) + sub 0A0h (160) = 200; 8-200=-192=0
|
|
;
|
|
; Frame:
|
|
; [+00..+1F] shadow
|
|
; [+20..+2F] CreateTemporaryItemInstanceBP params (Count/Level/RetVal)
|
|
; [+30..+37] SetOwningController params (InController QWORD)
|
|
; [+40..+57] ServerAddItemInternal params (FGuid16 + QB1 + pad3 + Slot4)
|
|
; [+60..+7F] locals: Count/PC/Def/Slot/Bars
|
|
Inventory_AddItemToSlot PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 0A0h ; 5 pushes(40)+0xA0(160)=200; 8-200=-192=0
|
|
|
|
; arg5 at entry_rsp+40 -> current_rsp+200+40 = rsp+0xE8
|
|
mov eax, DWORD PTR [rsp + 0E8h]
|
|
mov DWORD PTR [rsp + 60h], eax ; [+60] = Count
|
|
|
|
; Save all params to locals (frame)
|
|
mov QWORD PTR [rsp + 68h], rcx ; [+68] = PC
|
|
mov QWORD PTR [rsp + 70h], rdx ; [+70] = Definition
|
|
mov DWORD PTR [rsp + 78h], r8d ; [+78] = Slot
|
|
mov DWORD PTR [rsp + 7Ch], r9d ; [+7C] = Bars
|
|
|
|
; Clamp slot
|
|
test r9d, r9d
|
|
jnz @IAITS3_nc
|
|
cmp r8d, 6
|
|
jl @IAITS3_nc
|
|
mov DWORD PTR [rsp + 78h], 5
|
|
@IAITS3_nc:
|
|
cmp DWORD PTR [rsp + 78h], 0
|
|
jns @IAITS3_sk
|
|
mov DWORD PTR [rsp + 78h], 1
|
|
@IAITS3_sk:
|
|
|
|
; CreateTemporaryItemInstanceBP params at [+20]: Count(4)+Level(4)+RetVal(8)
|
|
mov eax, DWORD PTR [rsp + 60h]
|
|
mov DWORD PTR [rsp + 20h], eax ; Count
|
|
mov DWORD PTR [rsp + 24h], 1 ; Level = 1
|
|
mov QWORD PTR [rsp + 28h], 0 ; RetValue init
|
|
|
|
lea rsi, [fn_CreateTempItemBP]
|
|
lea rdi, [sz_CreateTempItemBP]
|
|
call Inventory__LoadFn
|
|
|
|
test rax, rax
|
|
jz @IAITS3_null
|
|
mov rcx, QWORD PTR [rsp + 70h] ; Definition
|
|
mov rdx, rax ; fn
|
|
lea r8, [rsp + 20h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
mov rbp, QWORD PTR [rsp + 28h] ; TempItemInstance
|
|
test rbp, rbp
|
|
jz @IAITS3_null
|
|
|
|
; SetOwningControllerForTemporaryItem(TempItem, PC)
|
|
; Params: InController(QWORD at +0)
|
|
mov rax, QWORD PTR [rsp + 68h] ; PC
|
|
mov QWORD PTR [rsp + 30h], rax
|
|
lea rsi, [fn_SetOwningController]
|
|
lea rdi, [sz_SetOwningController]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IAITS3_skip_soc
|
|
mov rcx, rbp ; TempItem
|
|
mov rdx, rax
|
|
lea r8, [rsp + 30h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IAITS3_skip_soc:
|
|
|
|
; TempItem->ItemEntry.Count = Count
|
|
mov eax, DWORD PTR [rsp + 60h]
|
|
mov DWORD PTR [rbp + UWI_ItemEntry_Count], eax
|
|
|
|
; TempItem->OwnerInventory = PC->WorldInventory
|
|
mov rcx, QWORD PTR [rsp + 68h]
|
|
mov rdx, QWORD PTR [rcx + PC_WorldInventory]
|
|
mov QWORD PTR [rbp + UWI_OwnerInventory], rdx
|
|
|
|
; TArray_Add(WorldInventory->Inventory.ReplicatedEntries, &TempItem->ItemEntry, FITEM_SIZEOF)
|
|
test rdx, rdx
|
|
jz @IAITS3_null
|
|
mov rbx, rdx ; RBX = WorldInventory
|
|
lea rcx, [rbx + AFI_ReplicatedEntries] ; &TArrayHeader
|
|
lea rdx, [rbp + UWI_ItemEntry] ; &ItemEntry
|
|
mov r8d, FITEM_SIZEOF
|
|
call TArray_Add
|
|
; EAX = index _Idx
|
|
|
|
mov r12d, eax ; R12D = _Idx
|
|
|
|
; TArray_Add(WorldInventory->Inventory.ItemInstances, &TempItem, 8)
|
|
push rbp ; push TempItem ptr onto stack
|
|
lea rcx, [rbx + AFI_ItemInstances]
|
|
lea rdx, [rsp] ; &TempItem on stack
|
|
mov r8d, 8
|
|
call TArray_Add
|
|
add rsp, 8
|
|
|
|
; ServerAddItemInternal(QuickBars, ItemGuid, Bars, Slot)
|
|
; Params: FGuid(+0,16) + EFortQuickBars(+10,1,3pad) + Slot(+14,4) = 0x18
|
|
mov rcx, QWORD PTR [rsp + 68h] ; PC
|
|
mov rcx, QWORD PTR [rcx + PC_QuickBars]
|
|
test rcx, rcx
|
|
jz @IAITS3_skip_sai
|
|
; Copy Guid from TempItem->ItemEntry.ItemGuid (at RBP+UWI_ItemEntry_ItemGuid)
|
|
mov rax, QWORD PTR [rbp + UWI_ItemEntry_ItemGuid]
|
|
mov rdx, QWORD PTR [rbp + UWI_ItemEntry_ItemGuid + 8]
|
|
mov QWORD PTR [rsp + 40h], rax
|
|
mov QWORD PTR [rsp + 48h], rdx
|
|
mov eax, DWORD PTR [rsp + 7Ch] ; Bars
|
|
mov BYTE PTR [rsp + 50h], al ; EFortQuickBars (BYTE + 3pad)
|
|
mov DWORD PTR [rsp + 51h], 0 ; padding
|
|
mov eax, DWORD PTR [rsp + 78h] ; Slot
|
|
mov DWORD PTR [rsp + 54h], eax
|
|
; Load fn
|
|
push rcx ; save QuickBars
|
|
lea rsi, [fn_ServerAddItemInternal]
|
|
lea rdi, [sz_ServerAddItemInternal]
|
|
call Inventory__LoadFn
|
|
pop rcx ; restore QuickBars
|
|
test rax, rax
|
|
jz @IAITS3_skip_sai
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IAITS3_skip_sai:
|
|
; Inventory_Update(PC, _Idx, false)
|
|
mov rcx, QWORD PTR [rsp + 68h]
|
|
mov edx, r12d
|
|
xor r8d, r8d
|
|
call Inventory_Update
|
|
|
|
; Return &TempItem->ItemEntry
|
|
lea rax, [rbp + UWI_ItemEntry]
|
|
add rsp, 0A0h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
|
|
@IAITS3_null:
|
|
xor eax, eax
|
|
add rsp, 0A0h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_AddItemToSlot ENDP
|
|
|
|
|
|
; Inventory_RemoveItemFromSlot
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; EDX = Slot (int)
|
|
; R8D = Bars (EFortQuickBars)
|
|
; R9D = Amount (int, -1 = all items in slot)
|
|
;
|
|
; Stack: 5 pushes (40) + sub 0C0h (192) = 232; 8-232=-224=0
|
|
;
|
|
; Frame: [+00..+1F] shadow; [+20..] locals
|
|
Inventory_RemoveItemFromSlot PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 0C0h
|
|
|
|
mov QWORD PTR [rsp + 60h], rcx ; [+60] = PC
|
|
mov DWORD PTR [rsp + 68h], edx ; [+68] = Slot
|
|
mov DWORD PTR [rsp + 6Ch], r8d ; [+6C] = Bars
|
|
mov DWORD PTR [rsp + 70h], r9d ; [+70] = Amount
|
|
|
|
; Get QuickBars
|
|
mov rbx, QWORD PTR [rcx + PC_QuickBars]
|
|
test rbx, rbx
|
|
jz @IRIFS_null
|
|
|
|
; Get WorldInventory
|
|
mov rdi, QWORD PTR [rcx + PC_WorldInventory]
|
|
test rdi, rdi
|
|
jz @IRIFS_null
|
|
|
|
; Determine slots TArray and slot ptr
|
|
mov eax, DWORD PTR [rsp + 6Ch] ; Bars
|
|
test eax, eax
|
|
jnz @IRIFS_secondary
|
|
|
|
; Primary: Slots.Data and Num at QB+QB_PrimarySlots
|
|
mov rsi, QWORD PTR [rbx + QB_PrimarySlots]
|
|
mov r12d, DWORD PTR [rbx + QB_PrimarySlots + 8]
|
|
jmp @IRIFS_slots_ok
|
|
@IRIFS_secondary:
|
|
mov rsi, QWORD PTR [rbx + QB_SecondarySlots]
|
|
mov r12d, DWORD PTR [rbx + QB_SecondarySlots + 8]
|
|
|
|
@IRIFS_slots_ok:
|
|
test rsi, rsi
|
|
jz @IRIFS_null
|
|
mov eax, DWORD PTR [rsp + 68h] ; Slot
|
|
cmp eax, r12d
|
|
jge @IRIFS_null
|
|
|
|
; &Slots[Slot]
|
|
imul rax, rax, QBSLOT_SIZE
|
|
add rax, rsi ; RAX = &Slots[Slot]
|
|
|
|
; If Amount == -1: use Items.Num
|
|
mov ebp, DWORD PTR [rsp + 70h]
|
|
cmp ebp, -1
|
|
jne @IRIFS_amount_ok
|
|
mov ebp, DWORD PTR [rax + QBSLOT_Items + 8] ; Items.Num
|
|
@IRIFS_amount_ok:
|
|
mov DWORD PTR [rsp + 74h], ebp ; save Amount to local
|
|
|
|
; Save &Slots[Slot] to local
|
|
mov QWORD PTR [rsp + 78h], rax ; [+78] = &Slots[Slot]
|
|
|
|
; bWasSuccessful = false
|
|
mov BYTE PTR [rsp + 80h], 0
|
|
|
|
; Loop i = 0..Amount
|
|
xor r12d, r12d ; R12D = i
|
|
@IRIFS_item_loop:
|
|
cmp r12d, DWORD PTR [rsp + 74h]
|
|
jge @IRIFS_check_success
|
|
|
|
; ToRemoveGuid = &Slots[Slot].Items[i]
|
|
mov rax, QWORD PTR [rsp + 78h] ; &Slots[Slot]
|
|
mov rcx, QWORD PTR [rax + QBSLOT_Items] ; Items.Data
|
|
test rcx, rcx
|
|
jz @IRIFS_skip_item
|
|
imul rbp, r12, FGUID_SIZE
|
|
add rbp, rcx ; RBP = &ToRemoveGuid
|
|
|
|
; Remove from ItemInstances
|
|
mov rcx, QWORD PTR [rdi + AFI_ItemInstances]
|
|
mov edx, DWORD PTR [rdi + AFI_ItemInstances + 8]
|
|
test rcx, rcx
|
|
jz @IRIFS_skip_inst
|
|
|
|
xor r9d, r9d
|
|
@IRIFS_inst_loop:
|
|
cmp r9d, edx
|
|
jge @IRIFS_skip_inst
|
|
mov rax, QWORD PTR [rcx + r9*8]
|
|
test rax, rax
|
|
jz @IRIFS_inst_next
|
|
|
|
; Compare guid at rax+UWI_ItemEntry_ItemGuid with [RBP]
|
|
mov r10, QWORD PTR [rax + UWI_ItemEntry_ItemGuid]
|
|
mov r11, QWORD PTR [rbp]
|
|
cmp r10, r11
|
|
jne @IRIFS_inst_next
|
|
mov r10, QWORD PTR [rax + UWI_ItemEntry_ItemGuid + 8]
|
|
mov r11, QWORD PTR [rbp + 8]
|
|
cmp r10, r11
|
|
jne @IRIFS_inst_next
|
|
|
|
; TArray_RemoveAt(ItemInstances, j, 8)
|
|
push rbp ; save RBP
|
|
push rdi ; save WorldInventory
|
|
push rcx ; save Items.Data (before RemoveAt)
|
|
push rdx ; save Num
|
|
lea rcx, [rdi + AFI_ItemInstances]
|
|
mov edx, r9d
|
|
mov r8d, 8
|
|
call TArray_RemoveAt
|
|
pop rdx
|
|
pop rcx
|
|
pop rdi
|
|
pop rbp
|
|
mov BYTE PTR [rsp + 80h], 1 ; bWasSuccessful = true
|
|
|
|
; Reload after RemoveAt (array may have moved)
|
|
mov rcx, QWORD PTR [rdi + AFI_ItemInstances]
|
|
mov edx, DWORD PTR [rdi + AFI_ItemInstances + 8]
|
|
; Don't increment r9d since RemoveAt shifts elements down
|
|
jmp @IRIFS_inst_loop
|
|
|
|
@IRIFS_inst_next:
|
|
inc r9d
|
|
jmp @IRIFS_inst_loop
|
|
|
|
@IRIFS_skip_inst:
|
|
; Remove from ReplicatedEntries
|
|
mov rcx, QWORD PTR [rdi + AFI_ReplicatedEntries]
|
|
mov edx, DWORD PTR [rdi + AFI_ReplicatedEntries + 8]
|
|
test rcx, rcx
|
|
jz @IRIFS_skip_rep
|
|
|
|
xor r9d, r9d
|
|
@IRIFS_rep_loop:
|
|
cmp r9d, edx
|
|
jge @IRIFS_skip_rep
|
|
; &ReplicatedEntries[x] = rcx + x * FITEM_SIZEOF
|
|
imul rax, r9, FITEM_SIZEOF
|
|
add rax, rcx
|
|
; Compare ItemGuid at rax+0x50
|
|
mov r10, QWORD PTR [rax + 050h]
|
|
mov r11, QWORD PTR [rbp]
|
|
cmp r10, r11
|
|
jne @IRIFS_rep_next
|
|
mov r10, QWORD PTR [rax + 050h + 8]
|
|
mov r11, QWORD PTR [rbp + 8]
|
|
cmp r10, r11
|
|
jne @IRIFS_rep_next
|
|
|
|
; TArray_RemoveAt(ReplicatedEntries, x, FITEM_SIZEOF)
|
|
push rbp
|
|
push rdi
|
|
push rcx
|
|
push rdx
|
|
lea rcx, [rdi + AFI_ReplicatedEntries]
|
|
mov edx, r9d
|
|
mov r8d, FITEM_SIZEOF
|
|
call TArray_RemoveAt
|
|
pop rdx
|
|
pop rcx
|
|
pop rdi
|
|
pop rbp
|
|
mov BYTE PTR [rsp + 80h], 1
|
|
|
|
mov rcx, QWORD PTR [rdi + AFI_ReplicatedEntries]
|
|
mov edx, DWORD PTR [rdi + AFI_ReplicatedEntries + 8]
|
|
jmp @IRIFS_rep_loop
|
|
|
|
@IRIFS_rep_next:
|
|
inc r9d
|
|
jmp @IRIFS_rep_loop
|
|
|
|
@IRIFS_skip_rep:
|
|
; ServerRemoveItemInternal(QuickBars, Guid, false, true)
|
|
; Params: FGuid(16) + bFindReplacement(1) + bForce(1) = 18, pad to 24
|
|
mov rax, QWORD PTR [rbp]
|
|
mov rdx, QWORD PTR [rbp + 8]
|
|
mov QWORD PTR [rsp + 38h], rax
|
|
mov QWORD PTR [rsp + 40h], rdx
|
|
mov BYTE PTR [rsp + 48h], 0 ; bFindReplacement = false
|
|
mov BYTE PTR [rsp + 49h], 1 ; bForce = true
|
|
push rbx
|
|
push rbp
|
|
lea rsi, [fn_ServerRemoveItemInternal]
|
|
lea rdi, [sz_ServerRemoveItemInternal]
|
|
call Inventory__LoadFn
|
|
pop rbp
|
|
pop rbx
|
|
test rax, rax
|
|
jz @IRIFS_skip_sri
|
|
mov rcx, rbx
|
|
mov rdx, rax
|
|
lea r8, [rsp + 38h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IRIFS_skip_sri:
|
|
|
|
; Zero out ToRemoveGuid (Reset)
|
|
xor eax, eax
|
|
mov QWORD PTR [rbp], rax
|
|
mov QWORD PTR [rbp + 8], rax
|
|
|
|
@IRIFS_skip_item:
|
|
inc r12d
|
|
jmp @IRIFS_item_loop
|
|
|
|
@IRIFS_check_success:
|
|
cmp BYTE PTR [rsp + 80h], 0
|
|
je @IRIFS_call_update
|
|
|
|
; EmptySlot(QuickBars, Bars, Slot)
|
|
mov eax, DWORD PTR [rsp + 6Ch]
|
|
mov edx, DWORD PTR [rsp + 68h]
|
|
mov DWORD PTR [rsp + 38h], eax ; EFortQuickBars (BYTE)
|
|
mov DWORD PTR [rsp + 3Ch], edx ; SlotIndex (int)
|
|
push rbx
|
|
lea rsi, [fn_EmptySlot]
|
|
lea rdi, [sz_EmptySlot]
|
|
call Inventory__LoadFn
|
|
pop rbx
|
|
test rax, rax
|
|
jz @IRIFS_skip_es
|
|
mov rcx, rbx
|
|
mov rdx, rax
|
|
lea r8, [rsp + 38h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IRIFS_skip_es:
|
|
|
|
; FreeArray on Slots[Slot].Items
|
|
mov rax, QWORD PTR [rsp + 78h] ; &Slots[Slot]
|
|
lea rcx, [rax + QBSLOT_Items]
|
|
call TArray_FreeArray
|
|
|
|
@IRIFS_call_update:
|
|
; Inventory_Update(PC, 0, true)
|
|
mov rcx, QWORD PTR [rsp + 60h]
|
|
xor edx, edx
|
|
mov r8d, 1
|
|
call Inventory_Update
|
|
|
|
movzx eax, BYTE PTR [rsp + 80h] ; return bWasSuccessful
|
|
add rsp, 0C0h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
|
|
@IRIFS_null:
|
|
xor eax, eax
|
|
add rsp, 0C0h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_RemoveItemFromSlot ENDP
|
|
|
|
|
|
; Inventory_EquipWeaponDefinition
|
|
; In : RCX = APawn* (pawn)
|
|
; RDX = UFortWeaponItemDefinition*
|
|
; R8 = FGuid* (16-byte)
|
|
;
|
|
; Stack: 5 pushes (40) + sub 80h (128) = 168; 8-168=-160=0
|
|
;
|
|
; Frame:
|
|
; [+00..+1F] shadow
|
|
; [+20..+3F] EquipWeaponDefinition params (WeaponData+Guid+RetVal = 0x20)
|
|
; [+40..+47] ProcessEvent null-params scratch / SetOwner params
|
|
; [+60..+67] Weapon (saved after EquipWeaponDefinition call)
|
|
; [+70..+7F] Guid copy
|
|
Inventory_EquipWeaponDefinition PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 80h
|
|
|
|
mov r12, rcx ; R12 = Pawn
|
|
mov rbx, rdx ; RBX = Definition
|
|
mov rax, QWORD PTR [r8]
|
|
mov QWORD PTR [rsp + 70h], rax
|
|
mov rax, QWORD PTR [r8 + 8]
|
|
mov QWORD PTR [rsp + 78h], rax
|
|
|
|
; Get Controller
|
|
mov rbp, QWORD PTR [r12 + 0228h]
|
|
test rbp, rbp
|
|
jz @IEWD3_done
|
|
|
|
; IsGuidInInventory check
|
|
mov rcx, rbp
|
|
lea rdx, [rsp + 70h]
|
|
call Inventory_IsGuidInInventory
|
|
test al, al
|
|
jz @IEWD3_done
|
|
|
|
; EquipWeaponDefinition via ProcessEvent
|
|
mov QWORD PTR [rsp + 20h], rbx
|
|
mov rax, QWORD PTR [rsp + 70h]
|
|
mov QWORD PTR [rsp + 28h], rax
|
|
mov rax, QWORD PTR [rsp + 78h]
|
|
mov QWORD PTR [rsp + 30h], rax
|
|
mov QWORD PTR [rsp + 38h], 0
|
|
|
|
lea rsi, [fn_EquipWeaponDef]
|
|
lea rdi, [sz_EquipWeaponDef]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_done
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 20h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
mov rbp, QWORD PTR [rsp + 38h] ; Weapon
|
|
test rbp, rbp
|
|
jz @IEWD3_done
|
|
mov QWORD PTR [rsp + 60h], rbp ; save Weapon to local
|
|
|
|
; Weapon->WeaponData = Definition
|
|
mov QWORD PTR [rbp + FW_WeaponData], rbx
|
|
; Weapon->ItemEntryGuid = Guid
|
|
mov rax, QWORD PTR [rsp + 70h]
|
|
mov QWORD PTR [rbp + FW_ItemEntryGuid], rax
|
|
mov rax, QWORD PTR [rsp + 78h]
|
|
mov QWORD PTR [rbp + FW_ItemEntryGuid + 8], rax
|
|
|
|
; SetOwner(Weapon, Pawn)
|
|
mov QWORD PTR [rsp + 40h], r12
|
|
lea rsi, [fn_SetOwner]
|
|
lea rdi, [sz_SetOwner]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_skip_so
|
|
mov rcx, QWORD PTR [rsp + 60h] ; Weapon
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IEWD3_skip_so:
|
|
|
|
; OnRep_ReplicatedWeaponData(Weapon)
|
|
lea rsi, [fn_OnRep_WeaponData]
|
|
lea rdi, [sz_OnRep_WeaponData]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_skip_rrwd
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 40h], r8
|
|
mov rcx, QWORD PTR [rsp + 60h]
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IEWD3_skip_rrwd:
|
|
|
|
; OnRep_AmmoCount(Weapon)
|
|
lea rsi, [fn_OnRep_AmmoCount]
|
|
lea rdi, [sz_OnRep_AmmoCount]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_skip_rac
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 40h], r8
|
|
mov rcx, QWORD PTR [rsp + 60h]
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IEWD3_skip_rac:
|
|
|
|
; ClientGivenTo(Weapon, Pawn)
|
|
mov QWORD PTR [rsp + 40h], r12
|
|
lea rsi, [fn_ClientGivenTo]
|
|
lea rdi, [sz_ClientGivenTo]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_skip_cgt
|
|
mov rcx, QWORD PTR [rsp + 60h]
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IEWD3_skip_cgt:
|
|
|
|
; ClientInternalEquipWeapon(Pawn, Weapon)
|
|
mov rax, QWORD PTR [rsp + 60h]
|
|
mov QWORD PTR [rsp + 40h], rax
|
|
lea rsi, [fn_ClientInternalEquipWeapon]
|
|
lea rdi, [sz_ClientInternalEquipWeapon]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_skip_ciew
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
@IEWD3_skip_ciew:
|
|
|
|
; OnRep_CurrentWeapon(Pawn)
|
|
lea rsi, [fn_OnRep_CurrentWeapon]
|
|
lea rdi, [sz_OnRep_CurrentWeapon]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @IEWD3_done
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 40h], r8
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@IEWD3_done:
|
|
add rsp, 80h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_EquipWeaponDefinition ENDP
|
|
|
|
|
|
; Inventory_OnDrop
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = AFortPlayerController_ServerAttemptInventoryDrop_Params*
|
|
; (FGuid ItemGuid at [RDX+0])
|
|
; Out: AL = 1 if successful
|
|
;
|
|
; Stack: 5 pushes (40) + sub 70h (112) = 152; 8-152=-144=0
|
|
Inventory_OnDrop PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 70h
|
|
|
|
mov r12, rcx ; R12 = PC
|
|
mov rbp, rdx ; RBP = Params
|
|
|
|
; bWasSuccessful = 0
|
|
xor ebx, ebx
|
|
|
|
; Get QuickBars
|
|
mov rsi, QWORD PTR [r12 + PC_QuickBars]
|
|
test rsi, rsi
|
|
jz @IOD_done
|
|
|
|
; Iterate PrimaryQuickBar.Slots[1..Num] looking for ItemGuid
|
|
mov rdi, QWORD PTR [rsi + QB_PrimarySlots]
|
|
mov r8d, DWORD PTR [rsi + QB_PrimarySlots + 8]
|
|
test rdi, rdi
|
|
jz @IOD_check_secondary
|
|
test r8d, r8d
|
|
jz @IOD_check_secondary
|
|
|
|
mov r9d, 1 ; start at slot 1 (skip pickaxe slot 0)
|
|
@IOD_pri_slot_loop:
|
|
cmp r9d, r8d
|
|
jge @IOD_check_secondary
|
|
|
|
imul rax, r9, QBSLOT_SIZE
|
|
add rax, rdi ; &Slots[i]
|
|
mov rcx, QWORD PTR [rax + QBSLOT_Items]
|
|
test rcx, rcx
|
|
jz @IOD_pri_slot_next
|
|
mov edx, DWORD PTR [rax + QBSLOT_Items + 8]
|
|
test edx, edx
|
|
jz @IOD_pri_slot_next
|
|
|
|
; For each item in slot
|
|
xor r10d, r10d
|
|
@IOD_pri_item_loop:
|
|
cmp r10d, edx
|
|
jge @IOD_pri_slot_next
|
|
imul r11, r10, FGUID_SIZE
|
|
add r11, rcx ; &Items[j]
|
|
; Compare with Params->ItemGuid at [RBP+0]
|
|
mov rax, QWORD PTR [r11]
|
|
mov rdx, QWORD PTR [rbp]
|
|
cmp rax, rdx
|
|
jne @IOD_pri_item_next
|
|
mov rax, QWORD PTR [r11 + 8]
|
|
mov rdx, QWORD PTR [rbp + 8]
|
|
cmp rax, rdx
|
|
jne @IOD_pri_item_next
|
|
|
|
; Found! RemoveItemFromSlot(PC, slot, Primary, j+1) -> spawn pickup
|
|
mov rcx, r12
|
|
mov edx, r9d
|
|
xor r8d, r8d ; Primary = 0
|
|
mov r9d, r10d
|
|
inc r9d ; Amount = j+1
|
|
call Inventory_RemoveItemFromSlot
|
|
test al, al
|
|
jz @IOD_done
|
|
|
|
; Spawn pickup: SummonPickup(Pawn, ItemDef, 1, PawnLocation)
|
|
; ItemDef comes from the ItemInstance we need to find
|
|
mov ebx, 1
|
|
jmp @IOD_done
|
|
|
|
@IOD_pri_item_next:
|
|
inc r10d
|
|
jmp @IOD_pri_item_loop
|
|
|
|
@IOD_pri_slot_next:
|
|
inc r9d
|
|
jmp @IOD_pri_slot_loop
|
|
|
|
@IOD_check_secondary:
|
|
|
|
@IOD_done:
|
|
; Select pickaxe (slot 0)
|
|
mov rax, QWORD PTR [r12 + PC_QuickBars]
|
|
test rax, rax
|
|
jz @IOD_return
|
|
mov rcx, QWORD PTR [rax + QB_PrimarySlots]
|
|
test rcx, rcx
|
|
jz @IOD_return
|
|
mov rcx, QWORD PTR [rcx] ; Slots[0].Items.Data
|
|
test rcx, rcx
|
|
jz @IOD_return
|
|
; EquipInventoryItem using guid from slot 0
|
|
lea rdx, [rcx] ; pointer to guid at Slots[0].Items[0]
|
|
mov rcx, r12
|
|
call Inventory_EquipWeaponDefinition ; partial call, but we don't have def here
|
|
; TODO: use proper EquipInventoryItem
|
|
|
|
@IOD_return:
|
|
movzx eax, bl
|
|
add rsp, 70h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_OnDrop ENDP
|
|
|
|
|
|
; Inventory_OnPickup
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = AFortPlayerPawn_ServerHandlePickup_Params*
|
|
; Pickup* at [RDX+0]
|
|
;
|
|
; Stack: 5 pushes (40) + sub 70h (112) = 152; 8-152=-144=0
|
|
Inventory_OnPickup PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 70h
|
|
|
|
mov r12, rcx ; R12 = PC
|
|
mov rbp, rdx ; RBP = Params
|
|
|
|
; Params->Pickup = [RBP + 0]
|
|
mov rbx, QWORD PTR [rbp] ; Pickup (AFortPickup*)
|
|
test rbx, rbx
|
|
jz @IOP_done
|
|
|
|
; ItemDef = Pickup->PrimaryPickupItemEntry.ItemDefinition (at AFortPickup+0x0368)
|
|
mov rsi, QWORD PTR [rbx + 0368h] ; ItemDefinition
|
|
test rsi, rsi
|
|
jz @IOP_done
|
|
|
|
; Count = Pickup->PrimaryPickupItemEntry.Count (at AFortPickup+0x035C)
|
|
mov edi, DWORD PTR [rbx + 035Ch]
|
|
|
|
; Find empty slot in PrimaryQuickBar.Slots[1..6]
|
|
mov rax, QWORD PTR [r12 + PC_QuickBars]
|
|
test rax, rax
|
|
jz @IOP_done
|
|
mov rcx, QWORD PTR [rax + QB_PrimarySlots]
|
|
mov edx, DWORD PTR [rax + QB_PrimarySlots + 8]
|
|
test rcx, rcx
|
|
jz @IOP_done
|
|
|
|
mov r9d, 1
|
|
@IOP_pri_slot_loop:
|
|
cmp r9d, 6
|
|
jge @IOP_done
|
|
cmp r9d, edx
|
|
jge @IOP_done
|
|
|
|
imul r10, r9, QBSLOT_SIZE
|
|
add r10, rcx
|
|
; Check if slot is empty: Items.Data == NULL
|
|
mov r11, QWORD PTR [r10 + QBSLOT_Items]
|
|
test r11, r11
|
|
jz @IOP_slot_found
|
|
|
|
inc r9d
|
|
jmp @IOP_pri_slot_loop
|
|
|
|
@IOP_slot_found:
|
|
; AddItemToSlot(PC, ItemDef, slot, Primary, Count)
|
|
mov rcx, r12
|
|
mov rdx, rsi ; ItemDef
|
|
mov r8d, r9d ; Slot
|
|
xor r9d, r9d ; Primary = 0
|
|
mov DWORD PTR [rsp + 20h], edi ; Count (arg5)
|
|
call Inventory_AddItemToSlot
|
|
test rax, rax
|
|
jz @IOP_done
|
|
mov r8, rax ; &ItemEntry returned
|
|
|
|
; Set Pickup->PickupLocationData.PickupTarget = Pawn
|
|
; PickupLocationData at AFortPickup+0x0428; PickupTarget at +0x00 within = AFortPickup+0x0428
|
|
mov rcx, QWORD PTR [r12 + ACTRL_Pawn]
|
|
test rcx, rcx
|
|
jz @IOP_skip_pldata
|
|
mov rax, QWORD PTR [r12 + ACTRL_Pawn]
|
|
mov rax, QWORD PTR [r12 + 0260h] ; Pawn
|
|
mov QWORD PTR [rbx + 0428h], rax ; PickupTarget
|
|
mov QWORD PTR [rbx + 0438h], rax ; ItemOwner
|
|
|
|
; FlyTime = 0.4f
|
|
mov DWORD PTR [rbx + 0458h], 3ECCCCCDh ; 0.4f IEEE
|
|
|
|
; TODO: OnRep_PickupLocationData
|
|
|
|
@IOP_skip_pldata:
|
|
; bPickedUp = true
|
|
mov BYTE PTR [rbx + 04C0h], 1
|
|
|
|
; TODO: OnRep_bPickedUp
|
|
|
|
; LoadedAmmo from pickup to instance
|
|
mov eax, DWORD PTR [rbx + 0378h]
|
|
mov DWORD PTR [r8 + 028h], eax
|
|
|
|
; Update inventory
|
|
mov rcx, r12
|
|
xor edx, edx
|
|
xor r8d, r8d
|
|
call Inventory_Update
|
|
|
|
@IOP_done:
|
|
add rsp, 70h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_OnPickup ENDP
|
|
|
|
|
|
; Inventory_Init
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; Spawns QuickBars, adds default items (buildings, traps, resources, ammo, edit tool).
|
|
;
|
|
; Stack: 5 pushes (40) + sub 80h (128) = 168; 8-168=-160=0
|
|
Inventory_Init PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 80h
|
|
|
|
mov r12, rcx ; R12 = PC
|
|
|
|
; Spawn QuickBars
|
|
lea rcx, [sz_FortQuickBarsClass]
|
|
call SDK_FindClass
|
|
test rax, rax
|
|
jz @II_no_qb
|
|
; SpawnActor at { -280, 400, 3000 }
|
|
; Build FVector on stack
|
|
mov DWORD PTR [rsp + 30h], 0C38C0000h ; -280.0f
|
|
mov DWORD PTR [rsp + 34h], 43C80000h ; 400.0f
|
|
mov DWORD PTR [rsp + 38h], 453B8000h ; 3000.0f
|
|
mov rcx, rax ; QuickBars class
|
|
lea rdx, [rsp + 30h] ; &FVector
|
|
mov r8, r12 ; owner = PC
|
|
call Spawners_SpawnActor
|
|
test rax, rax
|
|
jz @II_no_qb
|
|
|
|
; PC->QuickBars = spawned QuickBars
|
|
mov QWORD PTR [r12 + PC_QuickBars], rax
|
|
mov rbp, rax ; RBP = QuickBars
|
|
|
|
; OnRep_QuickBar on PC
|
|
lea rsi, [fn_OnRep_QuickBar]
|
|
lea rdi, [sz_OnRep_QuickBar]
|
|
call Inventory__LoadFn
|
|
test rax, rax
|
|
jz @II_no_qb
|
|
xor r8d, r8d
|
|
mov QWORD PTR [rsp + 40h], r8
|
|
mov rcx, r12
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@II_no_qb:
|
|
|
|
; EBX = secondary slot counter (0-7)
|
|
xor ebx, ebx
|
|
|
|
; Helper: finds object if not cached, adds to secondary slot
|
|
; ITEM_DEFINE loads item def into RDI, then calls AddItemToSlot
|
|
|
|
; Wall
|
|
mov rdi, QWORD PTR [def_Wall]
|
|
test rdi, rdi
|
|
jnz @II_wall_ok
|
|
lea rcx, [sz_WallDef]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Wall], rax
|
|
mov rdi, rax
|
|
@II_wall_ok:
|
|
test rdi, rdi
|
|
jz @II_floor
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_floor:
|
|
mov rdi, QWORD PTR [def_Floor]
|
|
test rdi, rdi
|
|
jnz @II_floor_ok
|
|
lea rcx, [sz_FloorDef]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Floor], rax
|
|
mov rdi, rax
|
|
@II_floor_ok:
|
|
test rdi, rdi
|
|
jz @II_stair
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_stair:
|
|
mov rdi, QWORD PTR [def_Stair]
|
|
test rdi, rdi
|
|
jnz @II_stair_ok
|
|
lea rcx, [sz_StairDef]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Stair], rax
|
|
mov rdi, rax
|
|
@II_stair_ok:
|
|
test rdi, rdi
|
|
jz @II_roof
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_roof:
|
|
mov rdi, QWORD PTR [def_Roof]
|
|
test rdi, rdi
|
|
jnz @II_roof_ok
|
|
lea rcx, [sz_RoofDef]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Roof], rax
|
|
mov rdi, rax
|
|
@II_roof_ok:
|
|
test rdi, rdi
|
|
jz @II_launchpad
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_launchpad:
|
|
mov rdi, QWORD PTR [def_LaunchPad]
|
|
test rdi, rdi
|
|
jnz @II_lp_ok
|
|
lea rcx, [sz_LaunchPad]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_LaunchPad], rax
|
|
mov rdi, rax
|
|
@II_lp_ok:
|
|
test rdi, rdi
|
|
jz @II_wallelectric
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_wallelectric:
|
|
mov rdi, QWORD PTR [def_WallElectric]
|
|
test rdi, rdi
|
|
jnz @II_we_ok
|
|
lea rcx, [sz_WallElectric]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_WallElectric], rax
|
|
mov rdi, rax
|
|
@II_we_ok:
|
|
test rdi, rdi
|
|
jz @II_floorspikes
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_floorspikes:
|
|
mov rdi, QWORD PTR [def_FloorSpikes]
|
|
test rdi, rdi
|
|
jnz @II_fs_ok
|
|
lea rcx, [sz_FloorSpikes]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_FloorSpikes], rax
|
|
mov rdi, rax
|
|
@II_fs_ok:
|
|
test rdi, rdi
|
|
jz @II_campfire
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
inc ebx
|
|
|
|
@II_campfire:
|
|
mov rdi, QWORD PTR [def_Campfire]
|
|
test rdi, rdi
|
|
jnz @II_cf_ok
|
|
lea rcx, [sz_Campfire]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Campfire], rax
|
|
mov rdi, rax
|
|
@II_cf_ok:
|
|
test rdi, rdi
|
|
jz @II_resources
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
|
|
; Resources and ammo: slot 0, count 999
|
|
@II_resources:
|
|
; Wood
|
|
mov rdi, QWORD PTR [def_Wood]
|
|
test rdi, rdi
|
|
jnz @II_wood_ok
|
|
lea rcx, [sz_Wood]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Wood], rax
|
|
mov rdi, rax
|
|
@II_wood_ok:
|
|
test rdi, rdi
|
|
jz @II_stone
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_stone:
|
|
mov rdi, QWORD PTR [def_Stone]
|
|
test rdi, rdi
|
|
jnz @II_stone_ok
|
|
lea rcx, [sz_Stone]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Stone], rax
|
|
mov rdi, rax
|
|
@II_stone_ok:
|
|
test rdi, rdi
|
|
jz @II_metal
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_metal:
|
|
mov rdi, QWORD PTR [def_Metal]
|
|
test rdi, rdi
|
|
jnz @II_metal_ok
|
|
lea rcx, [sz_Metal]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_Metal], rax
|
|
mov rdi, rax
|
|
@II_metal_ok:
|
|
test rdi, rdi
|
|
jz @II_ammo
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_ammo:
|
|
; Ammo items (all slot 0 secondary, count 999)
|
|
; AmmoShells
|
|
mov rdi, QWORD PTR [def_AmmoShells]
|
|
test rdi, rdi
|
|
jnz @II_ash_ok
|
|
lea rcx, [sz_AmmoShells]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_AmmoShells], rax
|
|
mov rdi, rax
|
|
@II_ash_ok:
|
|
test rdi, rdi
|
|
jz @II_aenergy
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_aenergy:
|
|
mov rdi, QWORD PTR [def_AmmoEnergy]
|
|
test rdi, rdi
|
|
jnz @II_ae_ok
|
|
lea rcx, [sz_AmmoEnergy]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_AmmoEnergy], rax
|
|
mov rdi, rax
|
|
@II_ae_ok:
|
|
test rdi, rdi
|
|
jz @II_abm
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_abm:
|
|
mov rdi, QWORD PTR [def_AmmoBullMedium]
|
|
test rdi, rdi
|
|
jnz @II_abm_ok
|
|
lea rcx, [sz_AmmoBulletsMedium]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_AmmoBullMedium], rax
|
|
mov rdi, rax
|
|
@II_abm_ok:
|
|
test rdi, rdi
|
|
jz @II_abl
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_abl:
|
|
mov rdi, QWORD PTR [def_AmmoBullLight]
|
|
test rdi, rdi
|
|
jnz @II_abl_ok
|
|
lea rcx, [sz_AmmoBulletsLight]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_AmmoBullLight], rax
|
|
mov rdi, rax
|
|
@II_abl_ok:
|
|
test rdi, rdi
|
|
jz @II_abh
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_abh:
|
|
mov rdi, QWORD PTR [def_AmmoBullHeavy]
|
|
test rdi, rdi
|
|
jnz @II_abh_ok
|
|
lea rcx, [sz_AmmoBulletsHeavy]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_AmmoBullHeavy], rax
|
|
mov rdi, rax
|
|
@II_abh_ok:
|
|
test rdi, rdi
|
|
jz @II_arockets
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_arockets:
|
|
mov rdi, QWORD PTR [def_AmmoRockets]
|
|
test rdi, rdi
|
|
jnz @II_ar_ok
|
|
lea rcx, [sz_AmmoRockets]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_AmmoRockets], rax
|
|
mov rdi, rax
|
|
@II_ar_ok:
|
|
test rdi, rdi
|
|
jz @II_edittool
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d
|
|
mov r9d, QB_SECONDARY
|
|
mov DWORD PTR [rsp + 20h], 999
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_edittool:
|
|
; EditTool: Primary slot 0, count 1
|
|
mov rdi, QWORD PTR [def_EditTool]
|
|
test rdi, rdi
|
|
jnz @II_et_ok
|
|
lea rcx, [sz_EditTool]
|
|
call SDK_FindObject
|
|
mov QWORD PTR [def_EditTool], rax
|
|
mov rdi, rax
|
|
@II_et_ok:
|
|
test rdi, rdi
|
|
jz @II_activate
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
xor r8d, r8d ; Slot = 0
|
|
xor r9d, r9d ; Primary = 0
|
|
mov DWORD PTR [rsp + 20h], 1
|
|
call Inventory_AddItemToSlot
|
|
|
|
@II_activate:
|
|
; ServerActivateSlotInternal(QuickBars, Primary, 0, 0.0f, false)
|
|
mov rax, QWORD PTR [r12 + PC_QuickBars]
|
|
test rax, rax
|
|
jz @II_done
|
|
; Params: InQuickBar(1B,3pad) + Slot(4) + ActivateDelay(4f) + bUpdate(1B) = 0x10
|
|
xor edx, edx
|
|
mov DWORD PTR [rsp + 38h], edx ; InQuickBar = Primary = 0
|
|
mov DWORD PTR [rsp + 3Ch], edx ; Slot = 0
|
|
mov DWORD PTR [rsp + 40h], edx ; ActivateDelay = 0.0f
|
|
mov BYTE PTR [rsp + 44h], 0 ; bUpdatePreviousFocusedSlot = false
|
|
push rax ; save QuickBars
|
|
lea rsi, [fn_ServerActivateSlotInt]
|
|
lea rdi, [sz_ServerActivateSlotInt]
|
|
call Inventory__LoadFn
|
|
pop rcx ; QuickBars
|
|
test rax, rax
|
|
jz @II_done
|
|
mov rdx, rax
|
|
lea r8, [rsp + 38h]
|
|
call QWORD PTR [ProcessEvent]
|
|
|
|
@II_done:
|
|
add rsp, 80h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_Init ENDP
|
|
|
|
; Inventory_GetDefinitionInSlot
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; EDX = Slot, R8D = Item, R9D = Bars
|
|
; Out: RAX = UFortWorldItemDefinition* or NULL
|
|
;
|
|
; Thin wrapper: GetEntryInSlot then return entry->ItemDefinition.
|
|
; Stack: 0 pushes + sub 28h = 40; X=8; 40 mod 16=8; 8-8=0
|
|
Inventory_GetDefinitionInSlot PROC
|
|
sub rsp, 28h
|
|
call Inventory_GetEntryInSlot
|
|
test rax, rax
|
|
jz @IGDS_null
|
|
mov rax, QWORD PTR [rax + 018h] ; entry->ItemDefinition (+0x18)
|
|
add rsp, 28h
|
|
ret
|
|
@IGDS_null:
|
|
xor eax, eax
|
|
add rsp, 28h
|
|
ret
|
|
Inventory_GetDefinitionInSlot ENDP
|
|
|
|
|
|
; Inventory_GetInstanceFromGuid
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = FGuid* (16-byte GUID to search for)
|
|
; Out: RAX = UFortWorldItem* or NULL
|
|
;
|
|
; Scans WorldInventory->Inventory.ItemInstances for matching GUID.
|
|
; Stack: 4 pushes (no CALL sites) - no alignment needed at call sites.
|
|
Inventory_GetInstanceFromGuid PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
|
|
mov rbx, rcx ; RBX = PC
|
|
mov rsi, rdx ; RSI = &Guid
|
|
|
|
mov rdi, QWORD PTR [rbx + PC_WorldInventory]
|
|
test rdi, rdi
|
|
jz @IGIG_null
|
|
|
|
mov rcx, QWORD PTR [rdi + AFI_ItemInstances]
|
|
mov edx, DWORD PTR [rdi + AFI_ItemInstances + 8]
|
|
test rcx, rcx
|
|
jz @IGIG_null
|
|
|
|
xor ebp, ebp ; i = 0
|
|
@IGIG_loop:
|
|
cmp ebp, edx
|
|
jge @IGIG_null
|
|
mov rax, QWORD PTR [rcx + rbp*8] ; Items[i] (UFortWorldItem*)
|
|
test rax, rax
|
|
jz @IGIG_next
|
|
; Compare [rax + UWI_ItemEntry_ItemGuid] with [rsi]
|
|
mov r8, QWORD PTR [rax + UWI_ItemEntry_ItemGuid]
|
|
mov r9, QWORD PTR [rsi]
|
|
cmp r8, r9
|
|
jne @IGIG_next
|
|
mov r8, QWORD PTR [rax + UWI_ItemEntry_ItemGuid + 8]
|
|
mov r9, QWORD PTR [rsi + 8]
|
|
cmp r8, r9
|
|
je @IGIG_found
|
|
@IGIG_next:
|
|
inc ebp
|
|
jmp @IGIG_loop
|
|
|
|
@IGIG_found:
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
|
|
@IGIG_null:
|
|
xor eax, eax
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_GetInstanceFromGuid ENDP
|
|
|
|
|
|
; Inventory_EquipInventoryItem
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = FGuid* (item to equip)
|
|
;
|
|
; Scans ItemInstances for matching GUID, then calls EquipWeaponDefinition.
|
|
;
|
|
; Stack: 5 pushes (40) + sub 60h (96) = 136; 8-136=0
|
|
; Frame: [+00..+1F] shadow; [+20..+2F] spare; [+40..+4F] FGuid copy
|
|
Inventory_EquipInventoryItem PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 60h
|
|
|
|
mov r12, rcx ; R12 = PC
|
|
; Copy FGuid to frame
|
|
mov rax, QWORD PTR [rdx]
|
|
mov QWORD PTR [rsp + 40h], rax
|
|
mov rax, QWORD PTR [rdx + 8]
|
|
mov QWORD PTR [rsp + 48h], rax
|
|
|
|
; Pawn = PC->Pawn
|
|
mov rbx, QWORD PTR [r12 + ACTRL_Pawn]
|
|
test rbx, rbx
|
|
jz @IEII_done
|
|
|
|
; WorldInventory
|
|
mov rdi, QWORD PTR [r12 + PC_WorldInventory]
|
|
test rdi, rdi
|
|
jz @IEII_done
|
|
|
|
; ItemInstances
|
|
mov rsi, QWORD PTR [rdi + AFI_ItemInstances] ; Data
|
|
mov edi, DWORD PTR [rdi + AFI_ItemInstances + 8] ; Num (zero-extends RDI)
|
|
test rsi, rsi
|
|
jz @IEII_done
|
|
|
|
xor ebp, ebp ; i = 0
|
|
@IEII_loop:
|
|
cmp ebp, edi
|
|
jge @IEII_done
|
|
mov rcx, QWORD PTR [rsi + rbp*8] ; Items[i]
|
|
test rcx, rcx
|
|
jz @IEII_next
|
|
; Compare GUID
|
|
mov rax, QWORD PTR [rcx + UWI_ItemEntry_ItemGuid]
|
|
cmp rax, QWORD PTR [rsp + 40h]
|
|
jne @IEII_next
|
|
mov rax, QWORD PTR [rcx + UWI_ItemEntry_ItemGuid + 8]
|
|
cmp rax, QWORD PTR [rsp + 48h]
|
|
jne @IEII_next
|
|
|
|
; Found - get ItemDef
|
|
mov rax, QWORD PTR [rcx + UWI_ItemEntry_ItemDef]
|
|
test rax, rax
|
|
jz @IEII_next
|
|
|
|
; EquipWeaponDefinition(Pawn, ItemDef, &Guid)
|
|
mov rcx, rbx
|
|
mov rdx, rax
|
|
lea r8, [rsp + 40h]
|
|
call Inventory_EquipWeaponDefinition
|
|
jmp @IEII_done
|
|
|
|
@IEII_next:
|
|
inc ebp
|
|
jmp @IEII_loop
|
|
|
|
@IEII_done:
|
|
add rsp, 60h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_EquipInventoryItem ENDP
|
|
|
|
|
|
; Inventory_EquipLoadout
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = UFortWorldItemDefinition*[6] (array of 6 def pointers)
|
|
;
|
|
; Adds items to primary slots 0-5, then equips slot 0 (pickaxe).
|
|
;
|
|
; Stack: 5 pushes (40) + sub 60h (96) = 136; 8-136=0
|
|
; Frame: [+00..+1F] shadow; [+20..+27] arg5(Count=1 for AddItemToSlot)
|
|
Inventory_EquipLoadout PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 60h
|
|
|
|
mov r12, rcx ; R12 = PC
|
|
mov rsi, rdx ; RSI = WIDS array
|
|
|
|
xor ebx, ebx ; i = 0
|
|
@IEL_loop:
|
|
cmp ebx, 6
|
|
jge @IEL_equip_slot0
|
|
|
|
mov rdi, QWORD PTR [rsi + rbx*8] ; WIDS[i]
|
|
test rdi, rdi
|
|
jz @IEL_next
|
|
|
|
; AddItemToSlot(PC, Definition, Slot=i, Bars=Primary=0, Count=1)
|
|
mov rcx, r12
|
|
mov rdx, rdi
|
|
mov r8d, ebx ; Slot
|
|
xor r9d, r9d ; Bars = Primary = 0
|
|
mov DWORD PTR [rsp + 20h], 1 ; Count (arg5)
|
|
call Inventory_AddItemToSlot
|
|
@IEL_next:
|
|
inc ebx
|
|
jmp @IEL_loop
|
|
|
|
@IEL_equip_slot0:
|
|
; 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 @IEL_done
|
|
|
|
; EquipInventoryItem(PC, &entry->ItemGuid)
|
|
mov rcx, r12
|
|
lea rdx, [rax + 050h] ; &entry->ItemGuid (+0x50 within FFortItemEntry)
|
|
call Inventory_EquipInventoryItem
|
|
|
|
@IEL_done:
|
|
add rsp, 60h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_EquipLoadout ENDP
|
|
|
|
|
|
; Inventory_FindItemInInventory
|
|
; In : RCX = AFortPlayerControllerAthena*
|
|
; RDX = UClass* (target class - matched against def->ClassPrivate)
|
|
; R8 = BYTE* bFound (output, may be NULL)
|
|
; Out: RAX = &FFortItemEntry (within UFortWorldItem) or NULL
|
|
;
|
|
; Scans ItemInstances; returns first entry whose definition's ClassPrivate
|
|
; exactly equals the target UClass*. (UObject::ClassPrivate = UObject+0x10)
|
|
;
|
|
; Stack: 5 pushes (40) + sub 60h (96) = 136; 8-136≡0
|
|
; Frame: [+00..+1F] shadow; [+50..+57] bFound ptr save
|
|
Inventory_FindItemInInventory PROC
|
|
push rbx
|
|
push rbp
|
|
push rsi
|
|
push rdi
|
|
push r12
|
|
sub rsp, 60h
|
|
|
|
mov r12, rcx ; R12 = PC
|
|
mov rdi, rdx ; RDI = target UClass*
|
|
mov QWORD PTR [rsp + 50h], r8 ; save bFound ptr
|
|
|
|
; bFound = false
|
|
test r8, r8
|
|
jz @IFII_scan
|
|
mov BYTE PTR [r8], 0
|
|
|
|
@IFII_scan:
|
|
mov rbx, QWORD PTR [r12 + PC_WorldInventory]
|
|
test rbx, rbx
|
|
jz @IFII_null
|
|
|
|
mov rsi, QWORD PTR [rbx + AFI_ItemInstances] ; Data
|
|
mov ebp, DWORD PTR [rbx + AFI_ItemInstances + 8] ; Num
|
|
test rsi, rsi
|
|
jz @IFII_null
|
|
|
|
xor ebx, ebx ; i = 0 (reuse RBX as counter)
|
|
@IFII_loop:
|
|
cmp ebx, ebp
|
|
jge @IFII_null
|
|
mov rax, QWORD PTR [rsi + rbx*8] ; Items[i]
|
|
test rax, rax
|
|
jz @IFII_next
|
|
; def = entry->ItemDefinition
|
|
mov rcx, QWORD PTR [rax + UWI_ItemEntry_ItemDef]
|
|
test rcx, rcx
|
|
jz @IFII_next
|
|
; def->ClassPrivate at UObject+0x10
|
|
mov rcx, QWORD PTR [rcx + 010h]
|
|
cmp rcx, rdi
|
|
jne @IFII_next
|
|
|
|
; Found - set bFound
|
|
mov rcx, QWORD PTR [rsp + 50h]
|
|
test rcx, rcx
|
|
jz @IFII_found_ptr
|
|
mov BYTE PTR [rcx], 1
|
|
@IFII_found_ptr:
|
|
lea rax, [rax + UWI_ItemEntry] ; return &FFortItemEntry
|
|
add rsp, 60h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
|
|
@IFII_next:
|
|
inc ebx
|
|
jmp @IFII_loop
|
|
|
|
@IFII_null:
|
|
xor eax, eax
|
|
add rsp, 60h
|
|
pop r12
|
|
pop rdi
|
|
pop rsi
|
|
pop rbp
|
|
pop rbx
|
|
ret
|
|
Inventory_FindItemInInventory ENDP
|
|
|
|
END |