abundant update

complete looting rewrite, improve combining pickups, add debug printing logs, fix some agids, fix cheat summon, fix issue with vehicle spawning.
This commit is contained in:
Milxnor
2023-05-06 19:01:56 -04:00
parent a4ed589aab
commit 5e92f2e90b
31 changed files with 1113 additions and 201 deletions

View File

@@ -35,11 +35,6 @@ void SpawnBGAs() // hahah not "proper", there's a function that we can hook and
auto MapInfo = GameState->GetMapInfo(); auto MapInfo = GameState->GetMapInfo();
} }
else
{
// SpawnLocation.Z += 100;
// SpawnLocation.Z -= 50; // proper frfr
}
static auto SpawnLootTierGroupOffset = BGAConsumableSpawner->GetOffset("SpawnLootTierGroup"); static auto SpawnLootTierGroupOffset = BGAConsumableSpawner->GetOffset("SpawnLootTierGroup");
auto& SpawnLootTierGroup = BGAConsumableSpawner->Get<FName>(SpawnLootTierGroupOffset); auto& SpawnLootTierGroup = BGAConsumableSpawner->Get<FName>(SpawnLootTierGroupOffset);
@@ -48,8 +43,8 @@ void SpawnBGAs() // hahah not "proper", there's a function that we can hook and
for (auto& LootDrop : LootDrops) for (auto& LootDrop : LootDrops)
{ {
static auto ConsumableClassOffset = LootDrop.ItemDefinition->GetOffset("ConsumableClass"); static auto ConsumableClassOffset = LootDrop->GetItemDefinition()->GetOffset("ConsumableClass");
auto ConsumableClassSoft = LootDrop.ItemDefinition->GetPtr<TSoftObjectPtr<UClass>>(ConsumableClassOffset); auto ConsumableClassSoft = LootDrop->GetItemDefinition()->GetPtr<TSoftObjectPtr<UClass>>(ConsumableClassOffset);
static auto BlueprintGeneratedClassClass = FindObject<UClass>(L"/Script/Engine.BlueprintGeneratedClass"); static auto BlueprintGeneratedClassClass = FindObject<UClass>(L"/Script/Engine.BlueprintGeneratedClass");
auto StrongConsumableClass = ConsumableClassSoft->Get(BlueprintGeneratedClassClass, true); auto StrongConsumableClass = ConsumableClassSoft->Get(BlueprintGeneratedClassClass, true);

View File

@@ -2,6 +2,7 @@
#include "FortPickup.h" #include "FortPickup.h"
#include "FortLootPackage.h" #include "FortLootPackage.h"
#include "FortGameModeAthena.h" #include "FortGameModeAthena.h"
#include "gui.h"
bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn) bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn)
{ {
@@ -12,14 +13,14 @@ bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn)
// LOG_INFO(LogInteraction, "RedirectedLootTier: {}", RedirectedLootTier.ToString()); // LOG_INFO(LogInteraction, "RedirectedLootTier: {}", RedirectedLootTier.ToString());
auto LootDrops = PickLootDrops(RedirectedLootTier, true); auto LootDrops = PickLootDrops(RedirectedLootTier, bDebugPrintLooting);
// LOG_INFO(LogInteraction, "LootDrops.size(): {}", LootDrops.size()); // LOG_INFO(LogInteraction, "LootDrops.size(): {}", LootDrops.size());
for (int i = 0; i < LootDrops.size(); i++) for (int i = 0; i < LootDrops.size(); i++)
{ {
auto& lootDrop = LootDrops.at(i); auto& lootDrop = LootDrops.at(i);
AFortPickup::SpawnPickup(lootDrop.ItemDefinition, LocationToSpawnLoot, lootDrop.Count, EFortPickupSourceTypeFlag::Container, EFortPickupSpawnSource::Unset, lootDrop.LoadedAmmo); AFortPickup::SpawnPickup(lootDrop->GetItemDefinition(), LocationToSpawnLoot, lootDrop->GetCount(), EFortPickupSourceTypeFlag::Container, EFortPickupSpawnSource::Unset, lootDrop->GetLoadedAmmo());
} }
return true; return true;

View File

@@ -34,3 +34,10 @@ struct RowNameAndRowData
FName RowName; FName RowName;
StructType* RowData; StructType* RowData;
}; };
struct FDataTableCategoryHandle
{
UDataTable* DataTable; // 0x0000(0x0008) (Edit, BlueprintVisible, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
FName ColumnName; // 0x0008(0x0008) (Edit, BlueprintVisible, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
FName RowContents; // 0x0010(0x0008) (Edit, BlueprintVisible, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
};

View File

@@ -40,6 +40,7 @@ void AFortAthenaMapInfo::SpawnLlamas()
return; return;
} }
int AmountOfLlamasSpawned = 0;
auto AmountOfLlamasToSpawn = CalcuateCurveMinAndMax(GetLlamaQuantityMin(), GetLlamaQuantityMax(), 1); auto AmountOfLlamasToSpawn = CalcuateCurveMinAndMax(GetLlamaQuantityMin(), GetLlamaQuantityMax(), 1);
LOG_INFO(LogDev, "Attempting to spawn {} llamas.", AmountOfLlamasToSpawn); LOG_INFO(LogDev, "Attempting to spawn {} llamas.", AmountOfLlamasToSpawn);
@@ -67,7 +68,7 @@ void AFortAthenaMapInfo::SpawnLlamas()
auto LlamaStart = GetWorld()->SpawnActor<AFortAthenaSupplyDrop>(GetLlamaClass(), InitialSpawnTransform, SpawnParameters); auto LlamaStart = GetWorld()->SpawnActor<AFortAthenaSupplyDrop>(GetLlamaClass(), InitialSpawnTransform, SpawnParameters);
LOG_INFO(LogDev, "LlamaStart: {}", __int64(LlamaStart)); // LOG_INFO(LogDev, "LlamaStart: {}", __int64(LlamaStart));
if (!LlamaStart) if (!LlamaStart)
continue; continue;
@@ -80,5 +81,8 @@ void AFortAthenaMapInfo::SpawnLlamas()
LOG_INFO(LogDev, "Spawning Llama at {} {} {}", GroundLocation.X, GroundLocation.Y, GroundLocation.Z); LOG_INFO(LogDev, "Spawning Llama at {} {} {}", GroundLocation.X, GroundLocation.Y, GroundLocation.Z);
UGameplayStatics::FinishSpawningActor(LlamaStart, FinalSpawnTransform); UGameplayStatics::FinishSpawningActor(LlamaStart, FinalSpawnTransform);
AmountOfLlamasSpawned++;
} }
LOG_INFO(LogGame, "Spawned {} llamas.", AmountOfLlamasSpawned);
} }

View File

@@ -17,6 +17,15 @@ public:
return GetPawnAtSeat_Params.ReturnValue; return GetPawnAtSeat_Params.ReturnValue;
} }
int FindSeatIndex(class AFortPlayerPawn* PlayerPawn)
{
static auto FindSeatIndexFn = FindObject<UFunction>("/Script/FortniteGame.FortAthenaVehicle.FindSeatIndex");
struct { AFortPlayerPawn* PlayerPawn; int ReturnValue; } AFortAthenaVehicle_FindSeatIndex_Params{ PlayerPawn };
this->ProcessEvent(FindSeatIndexFn, &AFortAthenaVehicle_FindSeatIndex_Params);
return AFortAthenaVehicle_FindSeatIndex_Params.ReturnValue;
}
UFortWeaponItemDefinition* GetVehicleWeaponForSeat(int SeatIdx); UFortWeaponItemDefinition* GetVehicleWeaponForSeat(int SeatIdx);
static UClass* StaticClass() static UClass* StaticClass()

View File

@@ -963,7 +963,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena
{ {
for (auto& LootDrop : LootDrops) for (auto& LootDrop : LootDrops)
{ {
auto Pickup = AFortPickup::SpawnPickup(LootDrop.ItemDefinition, Location, LootDrop.Count, SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop.LoadedAmmo); auto Pickup = AFortPickup::SpawnPickup(LootDrop->GetItemDefinition(), Location, LootDrop->GetCount(), SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop->GetLoadedAmmo());
} }
} }
} }
@@ -996,7 +996,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena
{ {
for (auto& LootDrop : LootDrops) for (auto& LootDrop : LootDrops)
{ {
auto Pickup = AFortPickup::SpawnPickup(LootDrop.ItemDefinition, Location, LootDrop.Count, SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop.LoadedAmmo); auto Pickup = AFortPickup::SpawnPickup(LootDrop->GetItemDefinition(), Location, LootDrop->GetCount(), SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop->GetLoadedAmmo());
} }
} }
@@ -1013,9 +1013,9 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena
if (Engine_Version >= 423 && Fortnite_Version <= 12.61) // 423+ we need to spawn manually and vehicle sync doesn't work on >S13. if (Engine_Version >= 423 && Fortnite_Version <= 12.61) // 423+ we need to spawn manually and vehicle sync doesn't work on >S13.
{ {
static int LastNum420 = 1; static int LastNum420 = 114;
if (Globals::AmountOfListens != LastNum420) if (LastNum420 != Globals::AmountOfListens)
{ {
LastNum420 = Globals::AmountOfListens; LastNum420 = Globals::AmountOfListens;

View File

@@ -154,7 +154,7 @@ std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AFortInventory::AddI
{ {
if (GadgetItemDefinition->ShouldDropAllItemsOnEquip()) // idk shouldnt this be auto? if (GadgetItemDefinition->ShouldDropAllItemsOnEquip()) // idk shouldnt this be auto?
{ {
FortPlayerController->DropAllItems({ GadgetItemDefinition }); FortPlayerController->DropAllItems({ GadgetItemDefinition }, false, false, Fortnite_Version < 7);
} }
bool (*ApplyGadgetData)(UFortGadgetItemDefinition* a1, __int64 a2, UFortItem* a3, unsigned __int8 a4) = decltype(ApplyGadgetData)(Addresses::ApplyGadgetData); bool (*ApplyGadgetData)(UFortGadgetItemDefinition* a1, __int64 a2, UFortItem* a3, unsigned __int8 a4) = decltype(ApplyGadgetData)(Addresses::ApplyGadgetData);
@@ -165,17 +165,6 @@ std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AFortInventory::AddI
bool DidApplyingGadgetSucceed = ApplyGadgetData(GadgetItemDefinition, Interface, NewItemInstance, idktbh); bool DidApplyingGadgetSucceed = ApplyGadgetData(GadgetItemDefinition, Interface, NewItemInstance, idktbh);
LOG_INFO(LogDev, "DidApplyingGadgetSucceed: {}", DidApplyingGadgetSucceed); LOG_INFO(LogDev, "DidApplyingGadgetSucceed: {}", DidApplyingGadgetSucceed);
bWasGadget = true; bWasGadget = true;
if (Fortnite_Version < 7)
{
auto PickaxeInstance = GetPickaxeInstance();
if (PickaxeInstance)
{
// RemoveItem(PickaxeInstance->GetItemEntry()->GetItemGuid(), nullptr, PickaxeInstance->GetItemEntry()->GetCount(), true);
Update();
}
}
} }
} }
@@ -185,14 +174,6 @@ std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AFortInventory::AddI
FortPlayerController->ServerExecuteInventoryItemHook(FortPlayerController, NewItemInstance->GetItemEntry()->GetItemGuid()); FortPlayerController->ServerExecuteInventoryItemHook(FortPlayerController, NewItemInstance->GetItemEntry()->GetItemGuid());
FortPlayerController->ClientEquipItem(NewItemInstance->GetItemEntry()->GetItemGuid(), true); FortPlayerController->ClientEquipItem(NewItemInstance->GetItemEntry()->GetItemGuid(), true);
} }
if (bWasGadget)
{
if (Fortnite_Version < 7)
{
// FortPlayerController->AddPickaxeToInventory();
}
}
} }
else else
{ {
@@ -357,9 +338,12 @@ bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int
bWasGadget = true; bWasGadget = true;
if (Fortnite_Version < 7) if (bWasGadget)
{ {
// FortPlayerController->AddPickaxeToInventory(); if (Fortnite_Version < 7 && GadgetItemDefinition->ShouldDropAllItemsOnEquip())
{
FortPlayerController->AddPickaxeToInventory();
}
} }
} }
} }

View File

@@ -1,7 +1,10 @@
#pragma once #pragma once
#include <Windows.h>
#include "NetSerialization.h" #include "NetSerialization.h"
#include "Class.h" #include "Class.h"
#include "GameplayAbilitySpec.h"
#include "reboot.h" #include "reboot.h"
@@ -101,6 +104,24 @@ struct FFortItemEntry : FFastArraySerializerItem
return *(int*)(__int64(this) + LoadedAmmoOffset); return *(int*)(__int64(this) + LoadedAmmoOffset);
} }
float& GetDurability()
{
static auto DurabilityOffset = FindOffsetStruct("/Script/FortniteGame.FortItemEntry", "Durability");
return *(float*)(__int64(this) + DurabilityOffset);
}
FGameplayAbilitySpecHandle& GetGameplayAbilitySpecHandle()
{
static auto GameplayAbilitySpecHandleOffset = FindOffsetStruct("/Script/FortniteGame.FortItemEntry", "GameplayAbilitySpecHandle");
return *(FGameplayAbilitySpecHandle*)(__int64(this) + GameplayAbilitySpecHandleOffset);
}
TWeakObjectPtr<class AFortInventory>& GetParentInventory()
{
static auto ParentInventoryOffset = FindOffsetStruct("/Script/FortniteGame.FortItemEntry", "ParentInventory");
return *(TWeakObjectPtr<class AFortInventory>*)(__int64(this) + ParentInventoryOffset);
}
void CopyFromAnotherItemEntry(FFortItemEntry* OtherItemEntry, bool bCopyGuid = false) void CopyFromAnotherItemEntry(FFortItemEntry* OtherItemEntry, bool bCopyGuid = false)
{ {
// We can use FortItemEntryStruct->CopyScriptStruct // We can use FortItemEntryStruct->CopyScriptStruct
@@ -142,7 +163,7 @@ struct FFortItemEntry : FFastArraySerializerItem
return StructSize; return StructSize;
} }
static FFortItemEntry* MakeItemEntry(UFortItemDefinition* ItemDefinition, int Count = 1, int LoadedAmmo = 0) static FFortItemEntry* MakeItemEntry(UFortItemDefinition* ItemDefinition, int Count = 1, int LoadedAmmo = 0, float Durability = 0x3F800000)
{ {
auto Entry = // (FFortItemEntry*)FMemory::Realloc(0, GetStructSize(), 0); auto Entry = // (FFortItemEntry*)FMemory::Realloc(0, GetStructSize(), 0);
Alloc<FFortItemEntry>(GetStructSize()); Alloc<FFortItemEntry>(GetStructSize());
@@ -150,14 +171,18 @@ struct FFortItemEntry : FFastArraySerializerItem
if (!Entry) if (!Entry)
return nullptr; return nullptr;
Entry->MostRecentArrayReplicationKey = -1; Entry->MostRecentArrayReplicationKey = -1; // idk if we need to set this
Entry->ReplicationID = -1; Entry->ReplicationID = -1;
Entry->ReplicationKey = -1; Entry->ReplicationKey = -1;
Entry->GetItemDefinition() = ItemDefinition; Entry->GetItemDefinition() = ItemDefinition;
Entry->GetCount() = Count; Entry->GetCount() = Count;
Entry->GetLoadedAmmo() = LoadedAmmo; Entry->GetLoadedAmmo() = LoadedAmmo;
// Entry->bUpdateStatsOnCollection = true; // Idk what this does but fortnite does it soo Entry->GetDurability() = Durability;
Entry->GetGameplayAbilitySpecHandle() = FGameplayAbilitySpecHandle(-1);
Entry->GetParentInventory().ObjectIndex = -1;
// CoCreateGuid((GUID*)&Entry->GetItemGuid());
// Entry->bUpdateStatsOnCollection = true; // Idk what this does but fortnite does it i think
return Entry; return Entry;
} }

View File

@@ -35,6 +35,27 @@ UFortResourceItemDefinition* UFortKismetLibrary::K2_GetResourceItemDefinition(EF
return params.ret; return params.ret;
} }
FVector UFortKismetLibrary::FindGroundLocationAt(UWorld* World, AActor* IgnoreActor, FVector InLocation, float TraceStartZ, float TraceEndZ, FName TraceName)
{
static auto FindGroundLocationAtFn = FindObject<UFunction>("/Script/FortniteGame.FortKismetLibrary.FindGroundLocationAt");
struct
{
UWorld* World; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
AActor* IgnoreActor; // (ConstParm, Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
FVector InLocation; // (ConstParm, Parm, OutParm, ZeroConstructor, ReferenceParm, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
float TraceStartZ; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
float TraceEndZ; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
FName TraceName; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
FVector ReturnValue; // (Parm, OutParm, ZeroConstructor, ReturnParm, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic)
} UFortKismetLibrary_FindGroundLocationAt_Params{ World, IgnoreActor, InLocation, TraceStartZ, TraceEndZ, TraceName };
static auto DefaultClass = StaticClass();
DefaultClass->ProcessEvent(FindGroundLocationAtFn, &UFortKismetLibrary_FindGroundLocationAt_Params);
return UFortKismetLibrary_FindGroundLocationAt_Params.ReturnValue;
}
void UFortKismetLibrary::ApplyCharacterCosmetics(UObject* WorldContextObject, const TArray<UObject*>& CharacterParts, UObject* PlayerState, bool* bSuccess) void UFortKismetLibrary::ApplyCharacterCosmetics(UObject* WorldContextObject, const TArray<UObject*>& CharacterParts, UObject* PlayerState, bool* bSuccess)
{ {
static auto fn = FindObject<UFunction>("/Script/FortniteGame.FortKismetLibrary.ApplyCharacterCosmetics"); static auto fn = FindObject<UFunction>("/Script/FortniteGame.FortKismetLibrary.ApplyCharacterCosmetics");
@@ -550,7 +571,7 @@ bool UFortKismetLibrary::PickLootDropsHook(UObject* Context, FFrame& Stack, bool
{ {
auto& LootDrop = LootDrops.at(i); auto& LootDrop = LootDrops.at(i);
auto NewEntry = FFortItemEntry::MakeItemEntry(LootDrop.ItemDefinition, LootDrop.Count, LootDrop.LoadedAmmo); auto NewEntry = FFortItemEntry::MakeItemEntry(LootDrop->GetItemDefinition(), LootDrop->GetCount(), LootDrop->GetLoadedAmmo());
OutLootToDrop.AddPtr(NewEntry, FFortItemEntry::GetStructSize()); OutLootToDrop.AddPtr(NewEntry, FFortItemEntry::GetStructSize());
} }

View File

@@ -84,6 +84,7 @@ public:
static UFortResourceItemDefinition* K2_GetResourceItemDefinition(EFortResourceType ResourceType); static UFortResourceItemDefinition* K2_GetResourceItemDefinition(EFortResourceType ResourceType);
static void ApplyCharacterCosmetics(UObject* WorldContextObject, const TArray<UObject*>& CharacterParts, UObject* PlayerState, bool* bSuccess); static void ApplyCharacterCosmetics(UObject* WorldContextObject, const TArray<UObject*>& CharacterParts, UObject* PlayerState, bool* bSuccess);
static FVector FindGroundLocationAt(UWorld* World, AActor* IgnoreActor, FVector InLocation, float TraceStartZ, float TraceEndZ, FName TraceName);
static void PickLootDropsWithNamedWeightsHook(UObject* Context, FFrame& Stack, void* Ret); static void PickLootDropsWithNamedWeightsHook(UObject* Context, FFrame& Stack, void* Ret);
static void SpawnItemVariantPickupInWorldHook(UObject* Context, FFrame& Stack, void* Ret); static void SpawnItemVariantPickupInWorldHook(UObject* Context, FFrame& Stack, void* Ret);

View File

@@ -0,0 +1,11 @@
#pragma once
#include "DataTable.h"
class UFortLootLevel
{
int GetItemLevel(FDataTableCategoryHandle LootLevelData, int WorldLevel)
{
return 0;
}
};

View File

@@ -8,6 +8,711 @@
#include "FortGameModeAthena.h" #include "FortGameModeAthena.h"
#include <random> #include <random>
#include <map>
#include <numeric>
struct FFortGameFeatureLootTableData
{
TSoftObjectPtr<UDataTable> LootTierData;
TSoftObjectPtr<UDataTable> LootPackageData;
};
#ifdef EXPERIMENTAL_LOOTING
float RandomFloatForLoot(float AllWeightsSum)
{
return (rand() * 0.000030518509) * AllWeightsSum;
}
template <typename RowStructType = uint8>
void CollectDataTablesRows(std::vector<UDataTable*> DataTables, std::map<FName, RowStructType*>* OutMap, std::function<bool(FName, RowStructType*)> Check = []() { return true; })
{
std::vector<UDataTable*> DataTablesToIterate;
static auto CompositeDataTableClass = FindObject<UClass>("/Script/Engine.CompositeDataTable");
for (auto DataTable : DataTables)
{
// if (auto CompositeDataTable = Cast<UCompositeDataTable>(DataTable))
if (DataTable->IsA(CompositeDataTableClass))
{
auto CompositeDataTable = DataTable;
static auto ParentTablesOffset = DataTable->GetOffset("ParentTables");
auto& ParentTables = DataTable->Get<TArray<UDataTable*>>(ParentTablesOffset);
for (int i = 0; i < ParentTables.Num(); i++)
{
DataTablesToIterate.push_back(ParentTables.at(i));
}
}
DataTablesToIterate.push_back(DataTable);
}
for (auto CurrentDataTable : DataTablesToIterate)
{
for (auto& CurrentPair : CurrentDataTable->GetRowMap())
{
if (Check(CurrentPair.Key(), (RowStructType*)CurrentPair.Value()))
(*OutMap)[CurrentPair.Key()] = (RowStructType*)CurrentPair.Value();
}
}
}
template <typename T>
static T* PickWeightedElement(const std::map<FName, T*>& Elements, std::function<float(T*)> GetWeightFn, float TotalWeightParam = -1, bool bCheckIfWeightIsZero = false, int RandMultiplier = 1, FName* OutName = nullptr, bool bPrint = false)
{
float TotalWeight = TotalWeightParam;
if (TotalWeight == -1)
{
TotalWeight = std::accumulate(Elements.begin(), Elements.end(), 0.0f, [&](float acc, const std::pair<FName, T*>& p) {
auto Weight = GetWeightFn(p.second);
if (bPrint)
{
LOG_INFO(LogLoot, "Adding weight: {}", Weight);
}
return acc + Weight;
});
}
float RandomNumber = // UKismetMathLibrary::RandomFloatInRange(0, TotalWeight);
RandMultiplier * RandomFloatForLoot(TotalWeight);
if (bPrint)
{
LOG_INFO(LogLoot, "RandomNumber: {} TotalWeight: {}", RandomNumber, TotalWeight);
}
for (auto& Element : Elements)
{
float Weight = GetWeightFn(Element.second);
if (bCheckIfWeightIsZero && Weight == 0)
continue;
if (RandomNumber <= Weight)
{
if (OutName)
*OutName = Element.first;
return Element.second;
}
RandomNumber -= Weight;
}
return nullptr;
}
int GetItemLevel(const FDataTableCategoryHandle& LootLevelData, int WorldLevel)
{
return 0;
}
float GetAmountOfLootPackagesToDrop(FFortLootTierData* LootTierData, int OriginalNumberLootDrops)
{
if (LootTierData->GetLootPackageCategoryMinArray().Num() != LootTierData->GetLootPackageCategoryWeightArray().Num()
|| LootTierData->GetLootPackageCategoryMinArray().Num() != LootTierData->GetLootPackageCategoryMaxArray().Num()
)
return 0;
// return OriginalNumberLootDrops;
float MinimumLootDrops = 0;
if (LootTierData->GetLootPackageCategoryMinArray().Num() > 0)
{
for (int i = 0; i < LootTierData->GetLootPackageCategoryMinArray().Num(); i++)
{
// Fortnite does more here, we need to figure it out.
MinimumLootDrops += LootTierData->GetLootPackageCategoryMinArray().at(i);
}
}
if (MinimumLootDrops > OriginalNumberLootDrops)
{
LOG_INFO(LogLoot, "Requested {} loot drops but minimum drops is {} for loot package {}", OriginalNumberLootDrops, MinimumLootDrops, LootTierData->GetLootPackage().ToString());
// Fortnite doesn't return here?
}
int SumLootPackageCategoryWeightArray = 0;
if (LootTierData->GetLootPackageCategoryWeightArray().Num() > 0)
{
for (int i = 0; i < LootTierData->GetLootPackageCategoryWeightArray().Num(); i++)
{
// Fortnite does more here, we need to figure it out.
if (LootTierData->GetLootPackageCategoryWeightArray().at(i) > 0)
{
auto LootPackageCategoryMaxArrayIt = LootTierData->GetLootPackageCategoryMaxArray().at(i);
float IDK = 0; // TODO
if (LootPackageCategoryMaxArrayIt < 0 || IDK < LootPackageCategoryMaxArrayIt)
{
SumLootPackageCategoryWeightArray += LootTierData->GetLootPackageCategoryWeightArray().at(i);
}
}
}
}
// if (MinimumLootDrops < OriginalNumberLootDrops) // real commeneted one to one
{
// IDK
while (SumLootPackageCategoryWeightArray > 0)
{
// HONESTLY IDEK WHAT FORTNITE DOES HERE
float v29 = (float)rand() * 0.000030518509;
float v35 = (int)(float)((float)((float)((float)SumLootPackageCategoryWeightArray * v29)
+ (float)((float)SumLootPackageCategoryWeightArray * v29))
+ 0.5) >> 1;
// OutLootTierInfo->Hello++;
MinimumLootDrops++;
if (MinimumLootDrops >= OriginalNumberLootDrops)
return MinimumLootDrops;
SumLootPackageCategoryWeightArray--;
}
/* if (MinimumLootDrops < OriginalNumberLootDrops)
{
std::cout << std::format("Requested {} loot drops but maximum drops is {} for loot package {}\n", OriginalNumberLootDrops, MinimumLootDrops, LootTierData->LootPackage.ToString());
} */
}
return MinimumLootDrops;
}
/*struct UFortLootPackage
{
int CurrentIdx = 0;
std::vector<FFortItemEntry> ItemEntries;
}; */
FFortLootTierData* PickLootTierData(const std::vector<UDataTable*>& LTDTables, FName LootTierGroup, int WorldLevel = 0, int ForcedLootTier = -1, FName* OutRowName = nullptr) // Fortnite returns the row name and then finds the tier data again, but I really don't see the point of this.
{
float LootTier = ForcedLootTier;
if (LootTier == -1)
{
LootTier = 0;
}
else
{
// buncha code im too lazy to reverse
}
LootTier = 1; // ONG PROPER
// if (fabs(LootTier) <= 0.0000000099999999)
// return 0;
std::map<FName, FFortLootTierData*> TierGroupLTDs;
CollectDataTablesRows<FFortLootTierData>(LTDTables, &TierGroupLTDs, [&](FName RowName, FFortLootTierData* TierData) -> bool {
if (LootTierGroup == TierData->GetTierGroup())
{
return true;
}
return false;
});
FFortLootTierData* ChosenRowLootTierData = PickWeightedElement<FFortLootTierData>(TierGroupLTDs,
[](FFortLootTierData* LootTierData) -> float { return LootTierData->GetWeight(); }, -1,
true, LootTier, OutRowName);
if (!ChosenRowLootTierData)
return nullptr;
return ChosenRowLootTierData;
}
void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, const FName& LootPackageName, std::vector<LootDrop>* OutEntries, int LootPackageCategory = -1, bool bPrint = false)
{
if (!OutEntries)
return;
std::map<FName, FFortLootPackageData*> LootPackageIDMap;
CollectDataTablesRows<FFortLootPackageData>(LPTables, &LootPackageIDMap, [&](FName RowName, FFortLootPackageData* LootPackage) -> bool {
if (LootPackage->GetLootPackageID() != LootPackageName)
{
return false;
}
if (LootPackageCategory != -1 && LootPackage->GetLootPackageCategory() != LootPackageCategory) // idk if proper
{
return false;
}
/* if (WorldLevel >= 0)
{
if (LootPackage->MaxWorldLevel >= 0 && WorldLevel > LootPackage->MaxWorldLevel)
return 0;
if (LootPackage->MinWorldLevel >= 0 && WorldLevel < LootPackage->MinWorldLevel)
return 0;
} */
return true;
});
if (LootPackageIDMap.size() == 0)
{
// std::cout << std::format("Loot Package {} has no valid weights.\n", LootPackageName.ToString());
return;
}
FName PickedPackageRowName;
FFortLootPackageData* PickedPackage = PickWeightedElement<FFortLootPackageData>(LootPackageIDMap,
[](FFortLootPackageData* LootPackageData) -> float { return LootPackageData->GetWeight(); },
-1, true, 1, &PickedPackageRowName, bPrint);
if (!PickedPackage)
return;
if (bPrint)
LOG_INFO(LogLoot, "PickLootDropsFromLootPackage selected package {} with loot package category {} from LootPackageIDMap of size: {}", PickedPackageRowName.ToString(), LootPackageCategory, LootPackageIDMap.size());
if (PickedPackage->GetLootPackageCall().Data.Num() > 1)
{
if (PickedPackage->GetCount() > 0)
{
int v9 = 0;
while (v9 < PickedPackage->GetCount())
{
int LootPackageCategoryToUseForLPCall = 0; // hmm
PickLootDropsFromLootPackage(LPTables,
PickedPackage->GetLootPackageCall().Data.Data ? UKismetStringLibrary::Conv_StringToName(PickedPackage->GetLootPackageCall()) : FName(0),
OutEntries, LootPackageCategoryToUseForLPCall, bPrint
);
v9++;
}
}
return;
}
auto ItemDefinition = PickedPackage->GetItemDefinition().Get(UFortItemDefinition::StaticClass(), true);
if (!ItemDefinition)
{
LOG_INFO(LogLoot, "Loot Package {} does not contain a LootPackageCall or ItemDefinition.", PickedPackage->GetLootPackageID().ToString());
return;
}
int ItemLevel = 0;
auto WeaponItemDefinition = Cast<UFortWeaponItemDefinition>(ItemDefinition);
int LoadedAmmo = WeaponItemDefinition ? WeaponItemDefinition->GetClipSize() : 0; // we shouldnt set loaded ammo here techinally
if (auto WorldItemDefinition = Cast<UFortWorldItemDefinition>(ItemDefinition))
{
ItemLevel = 0; // GetItemLevel(WorldItemDefinition->LootLevelData, 0);
}
int CountMultiplier = 1;
int FinalCount = CountMultiplier * PickedPackage->GetCount();
if (FinalCount > 0)
{
int FinalItemLevel = 0;
if (ItemLevel >= 0)
FinalItemLevel = ItemLevel;
while (FinalCount > 0)
{
int CurrentCountForEntry = PickedPackage->GetCount(); // Idk calls some itemdefinition vfunc
OutEntries->push_back(LootDrop(FFortItemEntry::MakeItemEntry(ItemDefinition, CurrentCountForEntry, LoadedAmmo)));
if (Engine_Version >= 424)
{
/*
Alright, so Fortnite literally doesn't reference the first loot package category for chests and floor loot (didnt check rest).
Usually the first loot package category in our case is ammo, so this is quite weird.
I have no clue how Fortnite would actually add the ammo.
Guess what, on the chapter 2 new loot tier groups, like FactionChests, they don't even have a package which has ammo as its loot package call.
*/
bool IsWeapon = PickedPackage->GetLootPackageID().ToString().contains(".Weapon.") && WeaponItemDefinition; // ONG?
if (IsWeapon)
{
auto AmmoData = WeaponItemDefinition->GetAmmoData();
int AmmoCount = AmmoData->GetDropCount(); // idk about this one
OutEntries->push_back(LootDrop(FFortItemEntry::MakeItemEntry(WeaponItemDefinition->GetAmmoData(), AmmoCount)));
}
}
FinalCount -= CurrentCountForEntry;
}
}
}
std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint, int recursive)
{
std::vector<LootDrop> LootDrops;
if (recursive > 6)
return LootDrops;
auto GameState = ((AFortGameModeAthena*)GetWorld()->GetGameMode())->GetGameStateAthena();
static auto CurrentPlaylistDataOffset = GameState->GetOffset("CurrentPlaylistData", false);
static std::vector<UDataTable*> LTDTables;
static std::vector<UDataTable*> LPTables;
static auto CompositeDataTableClass = FindObject<UClass>(L"/Script/Engine.CompositeDataTable");
static int LastNum1 = 14915;
auto CurrentPlaylist = CurrentPlaylistDataOffset == -1 && Fortnite_Version < 6 ? nullptr : GameState->GetCurrentPlaylist();
if (LastNum1 != Globals::AmountOfListens)
{
LastNum1 = Globals::AmountOfListens;
LTDTables.clear();
LPTables.clear();
bool bFoundPlaylistTable = false;
if (CurrentPlaylist)
{
static auto LootTierDataOffset = CurrentPlaylist->GetOffset("LootTierData");
auto& LootTierDataSoft = CurrentPlaylist->Get<TSoftObjectPtr<UDataTable>>(LootTierDataOffset);
static auto LootPackagesOffset = CurrentPlaylist->GetOffset("LootPackages");
auto& LootPackagesSoft = CurrentPlaylist->Get<TSoftObjectPtr<UDataTable>>(LootPackagesOffset);
if (LootTierDataSoft.IsValid() && LootPackagesSoft.IsValid())
{
auto LootTierDataStr = LootTierDataSoft.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto LootPackagesStr = LootPackagesSoft.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto LootTierDataTableIsComposite = LootTierDataStr.contains("Composite");
auto LootPackageTableIsComposite = LootPackagesStr.contains("Composite");
auto StrongLootTierData = LootTierDataSoft.Get(LootTierDataTableIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
auto StrongLootPackage = LootPackagesSoft.Get(LootPackageTableIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
if (StrongLootTierData && StrongLootPackage)
{
LTDTables.push_back(StrongLootTierData);
LPTables.push_back(StrongLootPackage);
bFoundPlaylistTable = true;
}
}
}
if (!bFoundPlaylistTable)
{
LTDTables.push_back(LoadObject<UDataTable>(L"/Game/Items/Datatables/AthenaLootTierData_Client.AthenaLootTierData_Client"));
LPTables.push_back(LoadObject<UDataTable>(L"/Game/Items/Datatables/AthenaLootPackages_Client.AthenaLootPackages_Client"));
}
// LTDTables.push_back(LoadObject<UDataTable>(L"/Game/Athena/Playlists/Playground/AthenaLootTierData_Client.AthenaLootTierData_Client"));
// LPTables.push_back(LoadObject<UDataTable>(L"/Game/Athena/Playlists/Playground/AthenaLootPackages_Client.AthenaLootPackages_Client"));
static auto FortGameFeatureDataClass = FindObject<UClass>("/Script/FortniteGame.FortGameFeatureData");
if (FortGameFeatureDataClass)
{
for (int i = 0; i < ChunkedObjects->Num(); i++)
{
auto Object = ChunkedObjects->GetObjectByIndex(i);
if (!Object)
continue;
if (Object->IsA(FortGameFeatureDataClass))
{
auto GameFeatureData = Object;
static auto DefaultLootTableDataOffset = GameFeatureData->GetOffset("DefaultLootTableData");
if (DefaultLootTableDataOffset != -1)
{
auto DefaultLootTableData = GameFeatureData->GetPtr<FFortGameFeatureLootTableData>(DefaultLootTableDataOffset);
auto LootTierDataTableStr = DefaultLootTableData->LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto LootTierDataTableIsComposite = LootTierDataTableStr.contains("Composite");
auto LootPackageTableStr = DefaultLootTableData->LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto LootPackageTableIsComposite = LootPackageTableStr.contains("Composite");
auto LootTierDataPtr = DefaultLootTableData->LootTierData.Get(LootTierDataTableIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
auto LootPackagePtr = DefaultLootTableData->LootPackageData.Get(LootPackageTableIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
if (LootPackagePtr)
{
LPTables.push_back(LootPackagePtr);
}
if (CurrentPlaylist)
{
static auto PlaylistOverrideLootTableDataOffset = GameFeatureData->GetOffset("PlaylistOverrideLootTableData");
auto& PlaylistOverrideLootTableData = GameFeatureData->Get<TMap<FGameplayTag, FFortGameFeatureLootTableData>>(PlaylistOverrideLootTableDataOffset);
static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer");
auto GameplayTagContainer = CurrentPlaylist->GetPtr<FGameplayTagContainer>(GameplayTagContainerOffset);
for (int i = 0; i < GameplayTagContainer->GameplayTags.Num(); i++)
{
auto& Tag = GameplayTagContainer->GameplayTags.At(i);
for (auto& Value : PlaylistOverrideLootTableData)
{
auto CurrentOverrideTag = Value.First;
if (Tag.TagName == CurrentOverrideTag.TagName)
{
auto OverrideLootPackageTableStr = Value.Second.LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto bOverrideIsComposite = OverrideLootPackageTableStr.contains("Composite");
auto ptr = Value.Second.LootPackageData.Get(bOverrideIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
if (ptr)
{
/* if (bOverrideIsComposite)
{
static auto ParentTablesOffset = ptr->GetOffset("ParentTables");
auto ParentTables = ptr->GetPtr<TArray<UDataTable*>>(ParentTablesOffset);
for (int z = 0; z < ParentTables->size(); z++)
{
auto ParentTable = ParentTables->At(z);
if (ParentTable)
{
LPTables.push_back(ParentTable);
}
}
} */
LPTables.push_back(ptr);
}
}
}
}
}
if (LootTierDataPtr)
{
LTDTables.push_back(LootTierDataPtr);
}
if (CurrentPlaylist)
{
static auto PlaylistOverrideLootTableDataOffset = GameFeatureData->GetOffset("PlaylistOverrideLootTableData");
auto& PlaylistOverrideLootTableData = GameFeatureData->Get<TMap<FGameplayTag, FFortGameFeatureLootTableData>>(PlaylistOverrideLootTableDataOffset);
static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer");
auto GameplayTagContainer = CurrentPlaylist->GetPtr<FGameplayTagContainer>(GameplayTagContainerOffset);
for (int i = 0; i < GameplayTagContainer->GameplayTags.Num(); i++)
{
auto& Tag = GameplayTagContainer->GameplayTags.At(i);
for (auto& Value : PlaylistOverrideLootTableData)
{
auto CurrentOverrideTag = Value.First;
if (Tag.TagName == CurrentOverrideTag.TagName)
{
auto OverrideLootTierDataStr = Value.Second.LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto bOverrideIsComposite = OverrideLootTierDataStr.contains("Composite");
auto ptr = Value.Second.LootTierData.Get(bOverrideIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
if (ptr)
{
/* if (bOverrideIsComposite)
{
static auto ParentTablesOffset = ptr->GetOffset("ParentTables");
auto ParentTables = ptr->GetPtr<TArray<UDataTable*>>(ParentTablesOffset);
for (int z = 0; z < ParentTables->size(); z++)
{
auto ParentTable = ParentTables->At(z);
if (ParentTable)
{
LTDTables.push_back(ParentTable);
}
}
} */
LTDTables.push_back(ptr);
}
}
}
}
}
}
}
}
}
for (int i = 0; i < LTDTables.size(); i++)
{
auto& Table = LTDTables.at(i);
if (!Table->IsValidLowLevel())
{
continue;
}
Table->AddToRoot();
LOG_INFO(LogDev, "[{}] LTD {}", i, Table->GetFullName());
}
for (int i = 0; i < LPTables.size(); i++)
{
auto& Table = LPTables.at(i);
if (!Table->IsValidLowLevel())
{
continue;
}
Table->AddToRoot();
LOG_INFO(LogDev, "[{}] LP {}", i, Table->GetFullName());
}
}
if (Fortnite_Version <= 6 || std::floor(Fortnite_Version) == 9) // ahhh
{
LTDTables.clear();
LPTables.clear();
bool bFoundPlaylistTable = false;
if (CurrentPlaylist)
{
static auto LootTierDataOffset = CurrentPlaylist->GetOffset("LootTierData");
auto& LootTierDataSoft = CurrentPlaylist->Get<TSoftObjectPtr<UDataTable>>(LootTierDataOffset);
static auto LootPackagesOffset = CurrentPlaylist->GetOffset("LootPackages");
auto& LootPackagesSoft = CurrentPlaylist->Get<TSoftObjectPtr<UDataTable>>(LootPackagesOffset);
if (LootTierDataSoft.IsValid() && LootPackagesSoft.IsValid())
{
auto LootTierDataStr = LootTierDataSoft.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto LootPackagesStr = LootPackagesSoft.SoftObjectPtr.ObjectID.AssetPathName.ToString();
auto LootTierDataTableIsComposite = LootTierDataStr.contains("Composite");
auto LootPackageTableIsComposite = LootPackagesStr.contains("Composite");
auto StrongLootTierData = LootTierDataSoft.Get(LootTierDataTableIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
auto StrongLootPackage = LootPackagesSoft.Get(LootPackageTableIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true);
if (StrongLootTierData && StrongLootPackage)
{
LTDTables.push_back(StrongLootTierData);
LPTables.push_back(StrongLootPackage);
bFoundPlaylistTable = true;
}
}
}
if (!bFoundPlaylistTable)
{
LTDTables.push_back(LoadObject<UDataTable>(L"/Game/Items/Datatables/AthenaLootTierData_Client.AthenaLootTierData_Client"));
LPTables.push_back(LoadObject<UDataTable>(L"/Game/Items/Datatables/AthenaLootPackages_Client.AthenaLootPackages_Client"));
}
}
if (LTDTables.size() <= 0 || LPTables.size() <= 0)
{
LOG_WARN(LogLoot, "Empty tables! ({} {})", LTDTables.size(), LPTables.size());
return LootDrops;
}
FName LootTierRowName;
auto ChosenRowLootTierData = PickLootTierData(LTDTables, TierGroupName, 0, -1, &LootTierRowName);
if (!ChosenRowLootTierData)
{
return LootDrops;
}
else if (bPrint)
{
LOG_INFO(LogLoot, "Picked loot tier data row {}", LootTierRowName.ToString());
}
// auto ChosenLootPackageName = ChosenRowLootTierData->GetLootPackage().ToString();
// if (ChosenLootPackageName.contains(".Empty")) { return PickLootDropsNew(TierGroupName, bPrint, ++recursive); }
float NumLootPackageDrops = ChosenRowLootTierData->GetNumLootPackageDrops();
float NumberLootDrops = 0;
if (NumLootPackageDrops > 0)
{
if (NumLootPackageDrops < 1)
{
NumberLootDrops = 1;
}
else
{
NumberLootDrops = (int)(float)((float)(NumLootPackageDrops + NumLootPackageDrops) - 0.5) >> 1;
float v20 = NumLootPackageDrops - NumberLootDrops;
if (v20 > 0.0000099999997)
{
NumberLootDrops += v20 >= (rand() * 0.000030518509);
}
}
}
float AmountOfLootPackageDrops = GetAmountOfLootPackagesToDrop(ChosenRowLootTierData, NumberLootDrops);
LootDrops.reserve(AmountOfLootPackageDrops);
if (AmountOfLootPackageDrops > 0)
{
for (int i = 0; i < AmountOfLootPackageDrops; i++)
{
if (i >= ChosenRowLootTierData->GetLootPackageCategoryMinArray().Num())
break;
for (int j = 0; j < ChosenRowLootTierData->GetLootPackageCategoryMinArray().at(i); j++)
{
if (ChosenRowLootTierData->GetLootPackageCategoryMinArray().at(i) < 1)
break;
int LootPackageCategory = i;
PickLootDropsFromLootPackage(LPTables, ChosenRowLootTierData->GetLootPackage(), &LootDrops, LootPackageCategory, bPrint);
}
}
}
return LootDrops;
}
#else
float GetRandomFloatForLooting(float min, float max) float GetRandomFloatForLooting(float min, float max)
{ {
@@ -72,12 +777,6 @@ static FFortLootPackageData* GetLootPackage(std::vector<FFortLootPackageData*>&
return SelectedItem; return SelectedItem;
} }
struct FFortGameFeatureLootTableData
{
TSoftObjectPtr<UDataTable> LootTierData;
TSoftObjectPtr<UDataTable> LootPackageData;
};
std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint, int recursive) std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint, int recursive)
{ {
std::vector<LootDrop> LootDrops; std::vector<LootDrop> LootDrops;
@@ -373,7 +1072,7 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint, int recurs
if (!LTD->IsValidLowLevel()) if (!LTD->IsValidLowLevel())
{ {
// if (bPrint) // if (bPrint)
LOG_INFO(LogLoot, "BadRead!"); LOG_INFO(LogLoot, "BadRead!");
continue; continue;
} }
@@ -639,18 +1338,14 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint, int recurs
if (bPrint) if (bPrint)
{ {
LOG_INFO(LogLoot, "[{}] {} {} {}", i, lootPackageCalls.size(), TierGroupLPStr, ItemDef->GetName()); LOG_INFO(LogLoot, "[{}] {} {} {}", i, lootPackageCalls.size(), TierGroupLPStr, ItemDef->GetName());
} }
auto WeaponDef = Cast<UFortWeaponItemDefinition>(ItemDef); auto WeaponDef = Cast<UFortWeaponItemDefinition>(ItemDef);
LootDrop lootDrop{}; LootDrops.push_back(LootDrop(FFortItemEntry::MakeItemEntry(ItemDef, LootPackageCall->GetCount(), WeaponDef ? WeaponDef->GetClipSize() : 0));
lootDrop.ItemDefinition = ItemDef;
lootDrop.LoadedAmmo = WeaponDef ? WeaponDef->GetClipSize() : 0;
lootDrop.Count = LootPackageCall->GetCount();
LootDrops.push_back(lootDrop);
} }
return LootDrops; return LootDrops;
} }
#endif

View File

@@ -5,13 +5,9 @@
#include "Array.h" #include "Array.h"
#include "FortWorldItemDefinition.h" #include "FortWorldItemDefinition.h"
#include "SoftObjectPtr.h" #include "SoftObjectPtr.h"
#include "FortItem.h"
struct LootDrop #define EXPERIMENTAL_LOOTING
{
UFortItemDefinition* ItemDefinition;
int Count;
int LoadedAmmo;
};
struct FFortLootPackageData struct FFortLootPackageData
{ {
@@ -46,6 +42,12 @@ public:
return *(int*)(__int64(this) + CountOffset); return *(int*)(__int64(this) + CountOffset);
} }
int& GetLootPackageCategory()
{
static auto LootPackageCategoryOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "LootPackageCategory");
return *(int*)(__int64(this) + LootPackageCategoryOffset);
}
FString& GetAnnotation() FString& GetAnnotation()
{ {
static auto AnnotationOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "Annotation"); static auto AnnotationOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "Annotation");
@@ -99,4 +101,18 @@ public:
} }
}; };
struct LootDrop
{
FFortItemEntry* ItemEntry;
FFortItemEntry* operator->() {
return ItemEntry;
}
~LootDrop()
{
}
};
std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint = false, int recursive = 0); std::vector<LootDrop> PickLootDrops(FName TierGroupName, bool bPrint = false, int recursive = 0);

View File

@@ -8,6 +8,7 @@
#include "FortPlayerController.h" #include "FortPlayerController.h"
#include <memcury.h> #include <memcury.h>
#include "GameplayStatics.h" #include "GameplayStatics.h"
#include "gui.h"
void AFortPickup::TossPickup(FVector FinalLocation, AFortPawn* ItemOwner, int OverrideMaxStackCount, bool bToss, EFortPickupSourceTypeFlag InPickupSourceTypeFlags, EFortPickupSpawnSource InPickupSpawnSource) void AFortPickup::TossPickup(FVector FinalLocation, AFortPawn* ItemOwner, int OverrideMaxStackCount, bool bToss, EFortPickupSourceTypeFlag InPickupSourceTypeFlags, EFortPickupSpawnSource InPickupSpawnSource)
{ {
@@ -30,7 +31,7 @@ void AFortPickup::SpawnMovementComponent()
AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Location, AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Location,
EFortPickupSourceTypeFlag PickupSource, EFortPickupSpawnSource SpawnSource, EFortPickupSourceTypeFlag PickupSource, EFortPickupSpawnSource SpawnSource,
class AFortPawn* Pawn, UClass* OverrideClass, bool bToss, int OverrideCount) class AFortPawn* Pawn, UClass* OverrideClass, bool bToss, int OverrideCount, AFortPickup* IgnoreCombinePickup)
{ {
if (bToss) if (bToss)
{ {
@@ -85,14 +86,17 @@ AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Locatio
{ {
auto OtherPickup = (AFortPickup*)OtherPickupActor; auto OtherPickup = (AFortPickup*)OtherPickupActor;
if (OtherPickup->GetPickupLocationData()->GetCombineTarget()) if (OtherPickup == IgnoreCombinePickup || OtherPickup->GetPickupLocationData()->GetCombineTarget())
return false; return false;
if (PrimaryPickupItemEntry->GetItemDefinition() == OtherPickup->GetPrimaryPickupItemEntry()->GetItemDefinition()) if (PrimaryPickupItemEntry->GetItemDefinition() == OtherPickup->GetPrimaryPickupItemEntry()->GetItemDefinition())
{ {
auto IncomingCount = OtherPickup->GetPrimaryPickupItemEntry()->GetCount(); // auto IncomingCount = OtherPickup->GetPrimaryPickupItemEntry()->GetCount();
if (PrimaryPickupItemEntry->GetCount() + IncomingCount > PrimaryPickupItemEntry->GetItemDefinition()->GetMaxStackSize()) // if (PrimaryPickupItemEntry->GetCount() + IncomingCount == PrimaryPickupItemEntry->GetItemDefinition()->GetMaxStackSize())
// return false;
if (OtherPickup->GetPrimaryPickupItemEntry()->GetCount() == PrimaryPickupItemEntry->GetItemDefinition()->GetMaxStackSize()) // Other pickup is already at the max size.
return false; return false;
return true; return true;
@@ -106,21 +110,29 @@ AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Locatio
PickupLocationData->GetCombineTarget() = (AFortPickup*)Pickup->GetClosestActor(AFortPickup::StaticClass(), 4, CanCombineWithPickup); PickupLocationData->GetCombineTarget() = (AFortPickup*)Pickup->GetClosestActor(AFortPickup::StaticClass(), 4, CanCombineWithPickup);
} }
// our little remake of tosspickup if (!PickupLocationData->GetCombineTarget()) // I don't think we should call TossPickup for every pickup anyways.
Pickup->GetPickupLocationData()->GetLootFinalPosition() = Location;
Pickup->GetPickupLocationData()->GetLootInitialPosition() = Pickup->GetActorLocation();
Pickup->GetPickupLocationData()->GetFlyTime() = 1.4f; // not right really
Pickup->GetPickupLocationData()->GetItemOwner() = Pawn;
Pickup->GetPickupLocationData()->GetFinalTossRestLocation() = Pickup->GetActorLocation(); // ong ong proper
if (!PickupLocationData->GetCombineTarget()) // I don't think we should call TossPickup for every pickup.
{ {
Pickup->TossPickup(Location, Pawn, 0, bToss, PickupSource, SpawnSource); Pickup->TossPickup(Location, Pawn, 0, bToss, PickupSource, SpawnSource);
} }
else else
{ {
auto ActorLocation = Pickup->GetActorLocation();
auto CurrentActorLocation = PickupLocationData->GetCombineTarget()->GetActorLocation();
int Dist = float(sqrtf(powf(CurrentActorLocation.X - ActorLocation.X, 2.0) + powf(CurrentActorLocation.Y - ActorLocation.Y, 2.0) + powf(CurrentActorLocation.Z - ActorLocation.Z, 2.0))) / 100.f;
// LOG_INFO(LogDev, "Distance: {}", Dist);
// our little remake of tosspickup
PickupLocationData->GetLootFinalPosition() = Location;
PickupLocationData->GetLootInitialPosition() = Pickup->GetActorLocation();
PickupLocationData->GetFlyTime() = 1.f / Dist; // Higher the dist quicker it should be. // not right
PickupLocationData->GetItemOwner() = Pawn;
PickupLocationData->GetFinalTossRestLocation() = PickupLocationData->GetCombineTarget()->GetActorLocation(); // Pickup->GetActorLocation() // ong ong proper
Pickup->OnRep_PickupLocationData(); Pickup->OnRep_PickupLocationData();
Pickup->ForceNetUpdate();
} }
if (PickupSource == EFortPickupSourceTypeFlag::Container) // crashes if we do this then tosspickup if (PickupSource == EFortPickupSourceTypeFlag::Container) // crashes if we do this then tosspickup
@@ -142,7 +154,7 @@ AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Locatio
} }
AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Location, int Count, EFortPickupSourceTypeFlag PickupSource, EFortPickupSpawnSource SpawnSource, AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Location, int Count, EFortPickupSourceTypeFlag PickupSource, EFortPickupSpawnSource SpawnSource,
int LoadedAmmo, AFortPawn* Pawn, UClass* OverrideClass, bool bToss) int LoadedAmmo, AFortPawn* Pawn, UClass* OverrideClass, bool bToss, AFortPickup* IgnoreCombinePickup)
{ {
if (LoadedAmmo == -1) if (LoadedAmmo == -1)
{ {
@@ -153,7 +165,7 @@ AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Loca
} }
auto ItemEntry = FFortItemEntry::MakeItemEntry(ItemDef, Count, LoadedAmmo); auto ItemEntry = FFortItemEntry::MakeItemEntry(ItemDef, Count, LoadedAmmo);
auto Pickup = SpawnPickup(ItemEntry, Location, PickupSource, SpawnSource, Pawn, OverrideClass, bToss); auto Pickup = SpawnPickup(ItemEntry, Location, PickupSource, SpawnSource, Pawn, OverrideClass, bToss, -1, IgnoreCombinePickup);
// VirtualFree(ItemEntry); // VirtualFree(ItemEntry);
return Pickup; return Pickup;
} }
@@ -164,27 +176,40 @@ void AFortPickup::CombinePickupHook(AFortPickup* Pickup)
auto PickupToCombineInto = (AFortPickup*)Pickup->GetPickupLocationData()->GetCombineTarget(); auto PickupToCombineInto = (AFortPickup*)Pickup->GetPickupLocationData()->GetCombineTarget();
if (!PickupToCombineInto->IsActorBeingDestroyed()) if (PickupToCombineInto->IsActorBeingDestroyed())
return;
const int IncomingCount = Pickup->GetPrimaryPickupItemEntry()->GetCount();
const int OriginalCount = PickupToCombineInto->GetPrimaryPickupItemEntry()->GetCount();
// add more checks?
auto ItemDefinition = PickupToCombineInto->GetPrimaryPickupItemEntry()->GetItemDefinition();
int CountToAdd = IncomingCount;
if (OriginalCount + CountToAdd > ItemDefinition->GetMaxStackSize())
{ {
// TODO Add more checks const int OverStackCount = OriginalCount + CountToAdd - ItemDefinition->GetMaxStackSize();
CountToAdd = ItemDefinition->GetMaxStackSize() - OriginalCount;
PickupToCombineInto->GetPrimaryPickupItemEntry()->GetCount() += Pickup->GetPrimaryPickupItemEntry()->GetCount(); auto ItemOwner = Pickup->GetPickupLocationData()->GetItemOwner();
PickupToCombineInto->OnRep_PrimaryPickupItemEntry();
PickupToCombineInto->ForceNetUpdate(); auto NewOverStackPickup = AFortPickup::SpawnPickup(ItemDefinition, PickupToCombineInto->GetActorLocation(), OverStackCount,
PickupToCombineInto->FlushNetDormancy(); EFortPickupSourceTypeFlag::Player, EFortPickupSpawnSource::Unset, -1, ItemOwner, nullptr, false, PickupToCombineInto);
Pickup->K2_DestroyActor();
} }
PickupToCombineInto->GetPrimaryPickupItemEntry()->GetCount() += CountToAdd;
PickupToCombineInto->OnRep_PrimaryPickupItemEntry();
PickupToCombineInto->ForceNetUpdate();
PickupToCombineInto->FlushNetDormancy();
Pickup->K2_DestroyActor();
} }
char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
{ {
constexpr bool bTestPrinting = false; // we could just use our own logger but eh
if constexpr (bTestPrinting)
LOG_INFO(LogDev, "CompletePickupAnimationHook!");
auto Pawn = Cast<AFortPlayerPawn>(Pickup->GetPickupLocationData()->GetPickupTarget()); auto Pawn = Cast<AFortPlayerPawn>(Pickup->GetPickupLocationData()->GetPickupTarget());
if (!Pawn) if (!Pawn)
@@ -234,7 +259,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
std::vector<std::pair<FFortItemEntry*, FFortItemEntry*>> PairsToMarkDirty; // vector of sets or something so no duplicates?? std::vector<std::pair<FFortItemEntry*, FFortItemEntry*>> PairsToMarkDirty; // vector of sets or something so no duplicates??
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "Start cpyCount: {}", cpyCount); LOG_INFO(LogDev, "Start cpyCount: {}", cpyCount);
bool bWasHoldingSameItemWhenSwap = false; bool bWasHoldingSameItemWhenSwap = false;
@@ -300,7 +325,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
bHasSwapped = true; bHasSwapped = true;
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "[{}] Swapping: {}", i, ItemDefinitionToSwap->GetFullName()); LOG_INFO(LogDev, "[{}] Swapping: {}", i, ItemDefinitionToSwap->GetFullName());
// bForceDontAddItem = true; // bForceDontAddItem = true;
@@ -311,7 +336,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
if (CurrentItemEntry->GetItemDefinition() == PickupItemDefinition) if (CurrentItemEntry->GetItemDefinition() == PickupItemDefinition)
{ {
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "[{}] Found stack of item!", i); LOG_INFO(LogDev, "[{}] Found stack of item!", i);
if (CurrentItemEntry->GetCount() < PickupItemDefinition->GetMaxStackSize()) if (CurrentItemEntry->GetCount() < PickupItemDefinition->GetMaxStackSize())
@@ -327,7 +352,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
bEverStacked = true; bEverStacked = true;
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "[{}] We are stacking {}.", i, AmountToStack); LOG_INFO(LogDev, "[{}] We are stacking {}.", i, AmountToStack);
// if (cpyCount > 0) // if (cpyCount > 0)
@@ -339,7 +364,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
if ((bIsInventoryFull || bForceOverflow) && cpyCount > 0) // overflow if ((bIsInventoryFull || bForceOverflow) && cpyCount > 0) // overflow
{ {
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "[{}] Overflow", i); LOG_INFO(LogDev, "[{}] Overflow", i);
UFortWorldItemDefinition* ItemDefinitionToSpawn = PickupItemDefinition; UFortWorldItemDefinition* ItemDefinitionToSpawn = PickupItemDefinition;
@@ -359,7 +384,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
if (cpyCount > 0 && !bIsInventoryFull && !bForceDontAddItem) if (cpyCount > 0 && !bIsInventoryFull && !bForceDontAddItem)
{ {
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "Attempting to add to inventory."); LOG_INFO(LogDev, "Attempting to add to inventory.");
if (bDoesStackExist ? PickupItemDefinition->DoesAllowMultipleStacks() : true) if (bDoesStackExist ? PickupItemDefinition->DoesAllowMultipleStacks() : true)
@@ -375,7 +400,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup)
else else
cpyCount -= NewItemCount; cpyCount -= NewItemCount;
if constexpr (bTestPrinting) if (bDebugPrintSwapping)
LOG_INFO(LogDev, "Added item with count {} to inventory.", NewItemCount); LOG_INFO(LogDev, "Added item with count {} to inventory.", NewItemCount);
if (bHasSwapped && NewSwappedItem == FGuid(-1, -1, -1, -1)) if (bHasSwapped && NewSwappedItem == FGuid(-1, -1, -1, -1))

View File

@@ -120,11 +120,11 @@ public:
static AFortPickup* SpawnPickup(FFortItemEntry* ItemEntry, FVector Location, static AFortPickup* SpawnPickup(FFortItemEntry* ItemEntry, FVector Location,
EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset, EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset,
class AFortPawn* Pawn = nullptr, UClass* OverrideClass = nullptr, bool bToss = true, int OverrideCount = -1); class AFortPawn* Pawn = nullptr, UClass* OverrideClass = nullptr, bool bToss = true, int OverrideCount = -1, AFortPickup* IgnoreCombinePickup = nullptr);
static AFortPickup* SpawnPickup(class UFortItemDefinition* ItemDef, FVector Location, int Count, static AFortPickup* SpawnPickup(class UFortItemDefinition* ItemDef, FVector Location, int Count,
EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset, EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset,
int LoadedAmmo = -1, class AFortPawn* Pawn = nullptr, UClass* OverrideClass = nullptr, bool bToss = true); int LoadedAmmo = -1, class AFortPawn* Pawn = nullptr, UClass* OverrideClass = nullptr, bool bToss = true, AFortPickup* IgnoreCombinePickup = nullptr);
static void CombinePickupHook(AFortPickup* Pickup); static void CombinePickupHook(AFortPickup* Pickup);
static char CompletePickupAnimationHook(AFortPickup* Pickup); static char CompletePickupAnimationHook(AFortPickup* Pickup);

View File

@@ -61,7 +61,7 @@ bool AFortPlayerController::DoesBuildFree()
return ReadBitfieldValue(bBuildFreeOffset, bBuildFreeFieldMask); return ReadBitfieldValue(bBuildFreeOffset, bBuildFreeFieldMask);
} }
void AFortPlayerController::DropAllItems(const std::vector<UFortItemDefinition*>& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar, bool bRemoveIfNotDroppable) void AFortPlayerController::DropAllItems(const std::vector<UFortItemDefinition*>& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar, bool bRemoveIfNotDroppable, bool RemovePickaxe)
{ {
auto Pawn = this->GetMyFortPawn(); auto Pawn = this->GetMyFortPawn();
@@ -78,6 +78,8 @@ void AFortPlayerController::DropAllItems(const std::vector<UFortItemDefinition*>
std::vector<std::pair<FGuid, int>> GuidAndCountsToRemove; std::vector<std::pair<FGuid, int>> GuidAndCountsToRemove;
auto PickaxeInstance = WorldInventory->GetPickaxeInstance();
for (int i = 0; i < ItemInstances.Num(); i++) for (int i = 0; i < ItemInstances.Num(); i++)
{ {
auto ItemInstance = ItemInstances.at(i); auto ItemInstance = ItemInstances.at(i);
@@ -86,6 +88,13 @@ void AFortPlayerController::DropAllItems(const std::vector<UFortItemDefinition*>
continue; continue;
auto ItemEntry = ItemInstance->GetItemEntry(); auto ItemEntry = ItemInstance->GetItemEntry();
if (RemovePickaxe && ItemInstance == PickaxeInstance)
{
GuidAndCountsToRemove.push_back({ ItemEntry->GetItemGuid(), ItemEntry->GetCount() });
continue;
}
auto WorldItemDefinition = Cast<UFortWorldItemDefinition>(ItemEntry->GetItemDefinition()); auto WorldItemDefinition = Cast<UFortWorldItemDefinition>(ItemEntry->GetItemDefinition());
if (!WorldItemDefinition || std::find(IgnoreItemDefs.begin(), IgnoreItemDefs.end(), WorldItemDefinition) != IgnoreItemDefs.end()) if (!WorldItemDefinition || std::find(IgnoreItemDefs.begin(), IgnoreItemDefs.end(), WorldItemDefinition) != IgnoreItemDefs.end())
@@ -482,16 +491,42 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame*
auto VehicleWeapon = Pawn->EquipWeaponDefinition(VehicleWeaponDefinition, NewVehicleInstance->GetItemEntry()->GetItemGuid()); auto VehicleWeapon = Pawn->EquipWeaponDefinition(VehicleWeaponDefinition, NewVehicleInstance->GetItemEntry()->GetItemGuid());
// PlayerController->ServerExecuteInventoryItemHook(PlayerController, newitem->GetItemEntry()->GetItemGuid()); // PlayerController->ServerExecuteInventoryItemHook(PlayerController, newitem->GetItemEntry()->GetItemGuid());
/* if (WeaponComponent) /* static auto GetSeatWeaponComponentFn = FindObject<UFunction>("/Script/FortniteGame.FortAthenaVehicle.GetSeatWeaponComponent");
if (GetSeatWeaponComponentFn)
{ {
static auto bWeaponEquippedOffset = WeaponComponent->GetOffset("bWeaponEquipped"); struct { int SeatIndex; UObject* ReturnValue; } AFortAthenaVehicle_GetSeatWeaponComponent_Params{};
WeaponComponent->Get<bool>(bWeaponEquippedOffset) = true;
static auto CachedWeaponOffset = WeaponComponent->GetOffset("CachedWeapon"); Vehicle->ProcessEvent(GetSeatWeaponComponentFn, &AFortAthenaVehicle_GetSeatWeaponComponent_Params);
WeaponComponent->Get<AFortWeapon*>(CachedWeaponOffset) = VehicleWeapon;
static auto CachedWeaponDefOffset = WeaponComponent->GetOffset("CachedWeaponDef"); UObject* WeaponComponent = AFortAthenaVehicle_GetSeatWeaponComponent_Params.ReturnValue;
WeaponComponent->Get<UFortWeaponItemDefinition*>(CachedWeaponDefOffset) = VehicleWeaponDefinition;
if (!WeaponComponent)
return;
static auto WeaponSeatDefinitionStructSize = FindObject<UClass>("/Script/FortniteGame.WeaponSeatDefinition")->GetPropertiesSize();
static auto VehicleWeaponOffset = FindOffsetStruct("/Script/FortniteGame.WeaponSeatDefinition", "VehicleWeapon");
static auto SeatIndexOffset = FindOffsetStruct("/Script/FortniteGame.WeaponSeatDefinition", "SeatIndex");
static auto WeaponSeatDefinitionsOffset = WeaponComponent->GetOffset("WeaponSeatDefinitions");
auto& WeaponSeatDefinitions = WeaponComponent->Get<TArray<__int64>>(WeaponSeatDefinitionsOffset);
for (int i = 0; i < WeaponSeatDefinitions.Num(); i++)
{
auto WeaponSeat = WeaponSeatDefinitions.AtPtr(i, WeaponSeatDefinitionStructSize);
if (*(int*)(__int64(WeaponSeat) + SeatIndexOffset) != Vehicle->FindSeatIndex(Pawn))
continue;
auto VehicleGrantedWeaponItem = (TWeakObjectPtr<UFortItem>*)(__int64(WeaponSeat) + 0x20);
VehicleGrantedWeaponItem->ObjectIndex = NewVehicleInstance->InternalIndex;
VehicleGrantedWeaponItem->ObjectSerialNumber = GetItemByIndex(NewVehicleInstance->InternalIndex)->SerialNumber;
static auto bWeaponEquippedOffset = WeaponComponent->GetOffset("bWeaponEquipped");
WeaponComponent->Get<bool>(bWeaponEquippedOffset) = true;
break;
}
} */ } */
return; return;

View File

@@ -87,7 +87,7 @@ public:
return CosmeticLoadout; return CosmeticLoadout;
} }
void AddPickaxeToInventory() UFortItem* AddPickaxeToInventory()
{ {
auto CosmeticLoadout = GetCosmeticLoadout(); auto CosmeticLoadout = GetCosmeticLoadout();
auto CosmeticLoadoutPickaxe = CosmeticLoadout ? CosmeticLoadout->GetPickaxe() : nullptr; auto CosmeticLoadoutPickaxe = CosmeticLoadout ? CosmeticLoadout->GetPickaxe() : nullptr;
@@ -100,10 +100,12 @@ public:
auto WorldInventory = GetWorldInventory(); auto WorldInventory = GetWorldInventory();
if (!WorldInventory || WorldInventory->GetPickaxeInstance()) if (!WorldInventory || WorldInventory->GetPickaxeInstance())
return; return nullptr;
WorldInventory->AddItem(PickaxeDefinition, nullptr); auto NewAndModifiedInstances = WorldInventory->AddItem(PickaxeDefinition, nullptr);
WorldInventory->Update(); WorldInventory->Update();
return NewAndModifiedInstances.first.size() > 0 ? NewAndModifiedInstances.first[0] : nullptr;
} }
bool& ShouldTryPickupSwap() bool& ShouldTryPickupSwap()
@@ -121,7 +123,7 @@ public:
void ClientEquipItem(const FGuid& ItemGuid, bool bForceExecution); void ClientEquipItem(const FGuid& ItemGuid, bool bForceExecution);
bool DoesBuildFree(); bool DoesBuildFree();
void DropAllItems(const std::vector<UFortItemDefinition*>& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar = false, bool bRemoveIfNotDroppable = false); void DropAllItems(const std::vector<UFortItemDefinition*>& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar = false, bool bRemoveIfNotDroppable = false, bool RemovePickaxe = false);
void ApplyCosmeticLoadout(); void ApplyCosmeticLoadout();
static void ServerLoadingScreenDroppedHook(UObject* Context, FFrame* Stack, void* Ret); static void ServerLoadingScreenDroppedHook(UObject* Context, FFrame* Stack, void* Ret);

View File

@@ -44,6 +44,13 @@ void AFortPlayerControllerAthena::StartGhostModeHook(UObject* Context, FFrame* S
if (!WorldInventory) if (!WorldInventory)
return StartGhostModeOriginal(Context, Stack, Ret); return StartGhostModeOriginal(Context, Stack, Ret);
auto PickaxeInstance = WorldInventory->GetPickaxeInstance();
if (PickaxeInstance)
{
WorldInventory->RemoveItem(PickaxeInstance->GetItemEntry()->GetItemGuid(), nullptr, PickaxeInstance->GetItemEntry()->GetCount(), true);
}
bool bShouldUpdate = false; bool bShouldUpdate = false;
auto NewAndModifiedInstances = WorldInventory->AddItem(ItemProvidingGhostMode, &bShouldUpdate, 1); auto NewAndModifiedInstances = WorldInventory->AddItem(ItemProvidingGhostMode, &bShouldUpdate, 1);
auto GhostModeItemInstance = NewAndModifiedInstances.first[0]; auto GhostModeItemInstance = NewAndModifiedInstances.first[0];
@@ -51,7 +58,7 @@ void AFortPlayerControllerAthena::StartGhostModeHook(UObject* Context, FFrame* S
if (!GhostModeItemInstance) if (!GhostModeItemInstance)
return StartGhostModeOriginal(Context, Stack, Ret); return StartGhostModeOriginal(Context, Stack, Ret);
if (bShouldUpdate) // if (bShouldUpdate)
WorldInventory->Update(); WorldInventory->Update();
PlayerController->ServerExecuteInventoryItemHook(PlayerController, GhostModeItemInstance->GetItemEntry()->GetItemGuid()); PlayerController->ServerExecuteInventoryItemHook(PlayerController, GhostModeItemInstance->GetItemEntry()->GetItemGuid());
@@ -95,12 +102,20 @@ void AFortPlayerControllerAthena::EndGhostModeHook(AFortPlayerControllerAthena*
if (!GhostModeItemInstance) if (!GhostModeItemInstance)
return EndGhostModeOriginal(PlayerController); return EndGhostModeOriginal(PlayerController);
auto PickaxeInstance = PlayerController->AddPickaxeToInventory();
WorldInventory->Update();
if (PickaxeInstance)
{
PlayerController->ClientEquipItem(PickaxeInstance->GetItemEntry()->GetItemGuid(), true);
}
bool bShouldUpdate = false; bool bShouldUpdate = false;
int Count = GhostModeItemInstance->GetItemEntry()->GetCount(); // 1 int Count = GhostModeItemInstance->GetItemEntry()->GetCount(); // 1
bool bForceRemoval = true; // false bool bForceRemoval = true; // false
WorldInventory->RemoveItem(GhostModeItemInstance->GetItemEntry()->GetItemGuid(), &bShouldUpdate, Count, bForceRemoval); WorldInventory->RemoveItem(GhostModeItemInstance->GetItemEntry()->GetItemGuid(), &bShouldUpdate, Count, bForceRemoval);
if (bShouldUpdate) // if (bShouldUpdate)
WorldInventory->Update(); WorldInventory->Update();
return EndGhostModeOriginal(PlayerController); return EndGhostModeOriginal(PlayerController);

View File

@@ -140,17 +140,7 @@ UFortWeaponItemDefinition* AFortPlayerPawn::GetVehicleWeaponDefinition(AFortAthe
if (!Vehicle) if (!Vehicle)
return nullptr; return nullptr;
static auto FindSeatIndexFn = FindObject<UFunction>("/Script/FortniteGame.FortAthenaVehicle.FindSeatIndex"); return Vehicle->GetVehicleWeaponForSeat(Vehicle->FindSeatIndex(this));
/* auto Vehicle = GetVehicle();
if (!Vehicle)
return nullptr; */
struct { AFortPlayerPawn* PlayerPawn; int ReturnValue; } AFortAthenaVehicle_FindSeatIndex_Params{ this };
Vehicle->ProcessEvent(FindSeatIndexFn, &AFortAthenaVehicle_FindSeatIndex_Params);
auto SeatIndex = AFortAthenaVehicle_FindSeatIndex_Params.ReturnValue;
return Vehicle->GetVehicleWeaponForSeat(SeatIndex);
} }
void AFortPlayerPawn::UnEquipVehicleWeaponDefinition(UFortWeaponItemDefinition* VehicleWeaponDefinition) void AFortPlayerPawn::UnEquipVehicleWeaponDefinition(UFortWeaponItemDefinition* VehicleWeaponDefinition)

View File

@@ -38,8 +38,8 @@ int UFortWeaponItemDefinition::GetClipSize()
UFortWorldItemDefinition* UFortWeaponItemDefinition::GetAmmoData() UFortWorldItemDefinition* UFortWeaponItemDefinition::GetAmmoData()
{ {
static auto AmmoDataOffset = GetOffset("AmmoData"); static auto AmmoDataOffset = GetOffset("AmmoData");
auto AmmoData = Get<TSoftObjectPtr<UFortWorldItemDefinition>>(AmmoDataOffset); auto AmmoData = GetPtr<TSoftObjectPtr<UFortWorldItemDefinition>>(AmmoDataOffset);
return AmmoData.Get(); return AmmoData->Get();
} }
UClass* UFortWeaponItemDefinition::StaticClass() UClass* UFortWeaponItemDefinition::StaticClass()

View File

@@ -20,6 +20,12 @@ public:
return ReadBitfieldValue(bCanBeDroppedOffset, bCanBeDroppedFieldMask); return ReadBitfieldValue(bCanBeDroppedOffset, bCanBeDroppedFieldMask);
} }
int& GetDropCount()
{
static auto DropCountOffset = GetOffset("DropCount");
return Get<int>(DropCountOffset);
}
EWorldItemDropBehavior& GetDropBehavior() EWorldItemDropBehavior& GetDropBehavior()
{ {
static auto DropBehaviorOffset = GetOffset("DropBehavior"); static auto DropBehaviorOffset = GetOffset("DropBehavior");

View File

@@ -29,4 +29,9 @@ struct FName
{ {
return ComparisonIndex.Value == other.ComparisonIndex.Value; return ComparisonIndex.Value == other.ComparisonIndex.Value;
} }
bool operator<(FName Other) const
{
return this->ComparisonIndex.Value < Other.ComparisonIndex.Value;
}
}; };

View File

@@ -335,6 +335,7 @@
<ClInclude Include="FortItem.h" /> <ClInclude Include="FortItem.h" />
<ClInclude Include="FortItemDefinition.h" /> <ClInclude Include="FortItemDefinition.h" />
<ClInclude Include="FortKismetLibrary.h" /> <ClInclude Include="FortKismetLibrary.h" />
<ClInclude Include="FortLootLevel.h" />
<ClInclude Include="FortLootPackage.h" /> <ClInclude Include="FortLootPackage.h" />
<ClInclude Include="FortMinigame.h" /> <ClInclude Include="FortMinigame.h" />
<ClInclude Include="FortMountedCannon.h" /> <ClInclude Include="FortMountedCannon.h" />

View File

@@ -694,9 +694,6 @@
<ClInclude Include="ReversePredicate.h"> <ClInclude Include="ReversePredicate.h">
<Filter>Engine\Source\Runtime\Core\Public\Templates</Filter> <Filter>Engine\Source\Runtime\Core\Public\Templates</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="UnrealTypeTraits.h">
<Filter>Engine\Source\Runtime\Core\Public\Templates</Filter>
</ClInclude>
<ClInclude Include="IsEnum.h"> <ClInclude Include="IsEnum.h">
<Filter>Engine\Source\Runtime\Core\Public\Templates</Filter> <Filter>Engine\Source\Runtime\Core\Public\Templates</Filter>
</ClInclude> </ClInclude>
@@ -848,6 +845,12 @@
<ClInclude Include="Vector2D.h"> <ClInclude Include="Vector2D.h">
<Filter>Engine\Source\Runtime\Core\Public\Math</Filter> <Filter>Engine\Source\Runtime\Core\Public\Math</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="UnrealTypeTraits.h">
<Filter>Engine\Source\Runtime\Core\Public\Templates</Filter>
</ClInclude>
<ClInclude Include="FortLootLevel.h">
<Filter>FortniteGame\Source\FortniteGame\Public\Items</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="Engine"> <Filter Include="Engine">

View File

@@ -490,7 +490,7 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg)
return; return;
} }
auto Pawn = ReceivingController->GetMyFortPawn(); auto Pawn = ReceivingController->GetPawn();
if (!Pawn) if (!Pawn)
{ {
@@ -519,11 +519,25 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg)
if (ClassObj) if (ClassObj)
{ {
int AmountSpawned = 0;
for (int i = 0; i < Count; i++) for (int i = 0; i < Count; i++)
{ {
FActorSpawnParameters SpawnParameters{};
// SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
auto Loc = Pawn->GetActorLocation(); auto Loc = Pawn->GetActorLocation();
// Loc.Z += 1000; Loc.Z += 1000;
GetWorld()->SpawnActor<AActor>(ClassObj, Loc, FQuat()); auto NewActor = GetWorld()->SpawnActor<AActor>(ClassObj, Loc, FQuat(), FVector(1, 1, 1), SpawnParameters);
if (!NewActor)
{
SendMessageToConsole(PlayerController, L"Failed to spawn an actor!");
}
else
{
AmountSpawned++;
}
} }
SendMessageToConsole(PlayerController, L"Summoned!"); SendMessageToConsole(PlayerController, L"Summoned!");

View File

@@ -627,12 +627,26 @@ static inline uint64 FindUpdateTrackedAttributesLea() // kill me
static inline uint64 FindCombinePickupLea() // kill me static inline uint64 FindCombinePickupLea() // kill me
{ {
return 0;
/* uint64 OnRep_PickupLocationDataAddr = 0; // TODO (Idea: Find SetupCombinePickupDelegates from this). /* uint64 OnRep_PickupLocationDataAddr = 0; // TODO (Idea: Find SetupCombinePickupDelegates from this).
if (!OnRep_PickupLocationDataAddr) if (!OnRep_PickupLocationDataAddr)
return 0; */ return 0; */
uint64 SetupCombinePickupDelegatesAddr = Memcury::Scanner::FindPattern("48 89 AC 24 ? ? ? ? 48 89 B4 24 ? ? ? ? 48 89 BC 24 ? ? ? ? 0F 29 B4 24 ? ? ? ? 75").Get(); // Haha so funny thing, this isn't actually the start its the middle because it's in function chunks yay! uint64 SetupCombinePickupDelegatesAddr = 0;
bool bShouldCheckSafety = true;
if (Engine_Version <= 420)
{
SetupCombinePickupDelegatesAddr = Memcury::Scanner::FindPattern("49 89 73 10 49 89 7B 18 4D 89 73 20 4D 89 7B E8 41 0F 29 73 ? 75").Get(); // found on 4.1
bShouldCheckSafety = false; // it's like "corrupted" and not just return
}
else if (Engine_Version >= 421)
{
SetupCombinePickupDelegatesAddr = Memcury::Scanner::FindPattern("48 89 AC 24 ? ? ? ? 48 89 B4 24 ? ? ? ? 48 89 BC 24 ? ? ? ? 0F 29 B4 24 ? ? ? ? 75").Get(); // Haha so funny thing, this isn't actually the start its the middle because it's in function chunks yay!
}
if (!SetupCombinePickupDelegatesAddr) if (!SetupCombinePickupDelegatesAddr)
return 0; return 0;
@@ -644,7 +658,7 @@ static inline uint64 FindCombinePickupLea() // kill me
{ {
auto loadAddress = Memcury::Scanner(SetupCombinePickupDelegatesAddr + i).RelativeOffset(3).Get(); auto loadAddress = Memcury::Scanner(SetupCombinePickupDelegatesAddr + i).RelativeOffset(3).Get();
if (IsNullSub(loadAddress)) // Safety if (!bShouldCheckSafety || IsNullSub(loadAddress))
return SetupCombinePickupDelegatesAddr + i; return SetupCombinePickupDelegatesAddr + i;
} }
} }

View File

@@ -48,8 +48,9 @@
#define DUMP_TAB 7 #define DUMP_TAB 7
#define UNBAN_TAB 8 #define UNBAN_TAB 8
#define DEVELOPER_TAB 9 #define DEVELOPER_TAB 9
#define SETTINGS_TAB 10 #define DEBUGLOG_TAB 10
#define CREDITS_TAB 11 #define SETTINGS_TAB 11
#define CREDITS_TAB 12
#define MAIN_PLAYERTAB 1 #define MAIN_PLAYERTAB 1
#define INVENTORY_PLAYERTAB 2 #define INVENTORY_PLAYERTAB 2
@@ -61,6 +62,8 @@ extern inline bool bSwitchedInitialLevel = false;
extern inline bool bIsInAutoRestart = false; extern inline bool bIsInAutoRestart = false;
extern inline float AutoBusStartSeconds = 60; extern inline float AutoBusStartSeconds = 60;
extern inline int NumRequiredPlayersToStart = 2; extern inline int NumRequiredPlayersToStart = 2;
extern inline bool bDebugPrintLooting = false;
extern inline bool bDebugPrintSwapping = false;
// THE BASE CODE IS FROM IMGUI GITHUB // THE BASE CODE IS FROM IMGUI GITHUB
@@ -376,6 +379,14 @@ static inline void MainTabs()
bInformationTab = false; bInformationTab = false;
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem("Debug Logs"))
{
Tab = DEBUGLOG_TAB;
PlayerTab = -1;
bInformationTab = false;
ImGui::EndTabItem();
}
#endif #endif
if (false && ImGui::BeginTabItem(("Credits"))) if (false && ImGui::BeginTabItem(("Credits")))
@@ -954,6 +965,11 @@ static inline void MainUI()
} }
*/ */
} }
else if (Tab == DEBUGLOG_TAB)
{
ImGui::Checkbox("Looting Debug Log", &bDebugPrintLooting);
ImGui::Checkbox("Swapping Debug Log", &bDebugPrintSwapping);
}
else if (Tab == SETTINGS_TAB) else if (Tab == SETTINGS_TAB)
{ {
// ImGui::Checkbox("Use custom lootpool (from Win64/lootpool.txt)", &Defines::bCustomLootpool); // ImGui::Checkbox("Use custom lootpool (from Win64/lootpool.txt)", &Defines::bCustomLootpool);

View File

@@ -49,7 +49,7 @@ struct PlaceholderBitfield
inline bool AreVehicleWeaponsEnabled() inline bool AreVehicleWeaponsEnabled()
{ {
return Fortnite_Version < 9; return Fortnite_Version > 6;
} }
inline bool IsRestartingSupported() inline bool IsRestartingSupported()

View File

@@ -184,11 +184,22 @@ static inline void SpawnVehicles2()
static auto FortAthenaVehicleSpawnerClass = FindObject<UClass>("/Script/FortniteGame.FortAthenaVehicleSpawner"); static auto FortAthenaVehicleSpawnerClass = FindObject<UClass>("/Script/FortniteGame.FortAthenaVehicleSpawner");
TArray<AActor*> AllVehicleSpawners = UGameplayStatics::GetAllActorsOfClass(GetWorld(), FortAthenaVehicleSpawnerClass); TArray<AActor*> AllVehicleSpawners = UGameplayStatics::GetAllActorsOfClass(GetWorld(), FortAthenaVehicleSpawnerClass);
int AmountOfVehiclesSpawned = 0;
for (int i = 0; i < AllVehicleSpawners.Num(); i++) for (int i = 0; i < AllVehicleSpawners.Num(); i++)
{ {
auto VehicleSpawner = AllVehicleSpawners.at(i); auto VehicleSpawner = AllVehicleSpawners.at(i);
auto Vehicle = SpawnVehicleFromSpawner(VehicleSpawner); auto Vehicle = SpawnVehicleFromSpawner(VehicleSpawner);
if (Vehicle)
{
AmountOfVehiclesSpawned++;
}
} }
auto AllVehicleSpawnersNum = AllVehicleSpawners.Num();
AllVehicleSpawners.Free(); AllVehicleSpawners.Free();
LOG_INFO(LogGame, "Spawned {}/{} vehicles.", AmountOfVehiclesSpawned, AllVehicleSpawnersNum);
} }

View File

@@ -134,7 +134,7 @@ static inline void FillItemCollector(ABuildingItemCollectorActor* ItemCollector,
for (int LootDropIt = 0; LootDropIt < LootDrops.size(); LootDropIt++) for (int LootDropIt = 0; LootDropIt < LootDrops.size(); LootDropIt++)
{ {
auto WorldItemDefinition = Cast<UFortWorldItemDefinition>(LootDrops[LootDropIt].ItemDefinition); UFortWorldItemDefinition* WorldItemDefinition = Cast<UFortWorldItemDefinition>(LootDrops[LootDropIt]->GetItemDefinition());
if (!WorldItemDefinition) if (!WorldItemDefinition)
continue; continue;
@@ -182,7 +182,7 @@ static inline void FillItemCollector(ABuildingItemCollectorActor* ItemCollector,
for (int LootDropIt = 0; LootDropIt < LootDrops.size(); LootDropIt++) for (int LootDropIt = 0; LootDropIt < LootDrops.size(); LootDropIt++)
{ {
auto ItemEntry = FFortItemEntry::MakeItemEntry(LootDrops[LootDropIt].ItemDefinition, LootDrops[LootDropIt].Count, LootDrops[LootDropIt].LoadedAmmo); auto ItemEntry = FFortItemEntry::MakeItemEntry(LootDrops[LootDropIt]->GetItemDefinition(), LootDrops[LootDropIt]->GetCount(), LootDrops[LootDropIt]->GetLoadedAmmo());
if (!ItemEntry) if (!ItemEntry)
continue; continue;

8
vendor/memcury.h vendored
View File

@@ -1431,4 +1431,10 @@
return PtrRef.ScanFor(Bytes, false).Get(); return PtrRef.ScanFor(Bytes, false).Get();
} }
inline bool IsNullSub(uint64 Addr) { return *(uint8_t*)(Addr) == 0xC3 || *(uint8_t*)(Addr) == 0xC2; } inline bool IsNullSub(uint64 Addr)
{
// if (*(uint8_t*)(Addr) == 0xEB && *(uint8_t*)(Addr + 1) == 0xF7) // positive sp value has been detected, the output may be wrong!
// return true;
return *(uint8_t*)(Addr) == 0xC3 || *(uint8_t*)(Addr) == 0xC2;
}