diff --git a/Project Reboot 3.0/BGA.h b/Project Reboot 3.0/BGA.h index fab858c..940740b 100644 --- a/Project Reboot 3.0/BGA.h +++ b/Project Reboot 3.0/BGA.h @@ -35,11 +35,6 @@ void SpawnBGAs() // hahah not "proper", there's a function that we can hook and auto MapInfo = GameState->GetMapInfo(); } - else - { - // SpawnLocation.Z += 100; - // SpawnLocation.Z -= 50; // proper frfr - } static auto SpawnLootTierGroupOffset = BGAConsumableSpawner->GetOffset("SpawnLootTierGroup"); auto& SpawnLootTierGroup = BGAConsumableSpawner->Get(SpawnLootTierGroupOffset); @@ -48,8 +43,8 @@ void SpawnBGAs() // hahah not "proper", there's a function that we can hook and for (auto& LootDrop : LootDrops) { - static auto ConsumableClassOffset = LootDrop.ItemDefinition->GetOffset("ConsumableClass"); - auto ConsumableClassSoft = LootDrop.ItemDefinition->GetPtr>(ConsumableClassOffset); + static auto ConsumableClassOffset = LootDrop->GetItemDefinition()->GetOffset("ConsumableClass"); + auto ConsumableClassSoft = LootDrop->GetItemDefinition()->GetPtr>(ConsumableClassOffset); static auto BlueprintGeneratedClassClass = FindObject(L"/Script/Engine.BlueprintGeneratedClass"); auto StrongConsumableClass = ConsumableClassSoft->Get(BlueprintGeneratedClassClass, true); diff --git a/Project Reboot 3.0/BuildingContainer.cpp b/Project Reboot 3.0/BuildingContainer.cpp index f267a0d..10978be 100644 --- a/Project Reboot 3.0/BuildingContainer.cpp +++ b/Project Reboot 3.0/BuildingContainer.cpp @@ -2,6 +2,7 @@ #include "FortPickup.h" #include "FortLootPackage.h" #include "FortGameModeAthena.h" +#include "gui.h" bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn) { @@ -12,14 +13,14 @@ bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn) // LOG_INFO(LogInteraction, "RedirectedLootTier: {}", RedirectedLootTier.ToString()); - auto LootDrops = PickLootDrops(RedirectedLootTier, true); + auto LootDrops = PickLootDrops(RedirectedLootTier, bDebugPrintLooting); // LOG_INFO(LogInteraction, "LootDrops.size(): {}", LootDrops.size()); for (int i = 0; i < LootDrops.size(); 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; diff --git a/Project Reboot 3.0/DataTable.h b/Project Reboot 3.0/DataTable.h index 796418f..3f612e6 100644 --- a/Project Reboot 3.0/DataTable.h +++ b/Project Reboot 3.0/DataTable.h @@ -33,4 +33,11 @@ struct RowNameAndRowData { FName RowName; StructType* RowData; -}; \ No newline at end of file +}; + +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) +}; diff --git a/Project Reboot 3.0/FortAthenaMapInfo.cpp b/Project Reboot 3.0/FortAthenaMapInfo.cpp index 2a8b509..759342e 100644 --- a/Project Reboot 3.0/FortAthenaMapInfo.cpp +++ b/Project Reboot 3.0/FortAthenaMapInfo.cpp @@ -40,6 +40,7 @@ void AFortAthenaMapInfo::SpawnLlamas() return; } + int AmountOfLlamasSpawned = 0; auto AmountOfLlamasToSpawn = CalcuateCurveMinAndMax(GetLlamaQuantityMin(), GetLlamaQuantityMax(), 1); LOG_INFO(LogDev, "Attempting to spawn {} llamas.", AmountOfLlamasToSpawn); @@ -67,7 +68,7 @@ void AFortAthenaMapInfo::SpawnLlamas() auto LlamaStart = GetWorld()->SpawnActor(GetLlamaClass(), InitialSpawnTransform, SpawnParameters); - LOG_INFO(LogDev, "LlamaStart: {}", __int64(LlamaStart)); + // LOG_INFO(LogDev, "LlamaStart: {}", __int64(LlamaStart)); if (!LlamaStart) continue; @@ -80,5 +81,8 @@ void AFortAthenaMapInfo::SpawnLlamas() LOG_INFO(LogDev, "Spawning Llama at {} {} {}", GroundLocation.X, GroundLocation.Y, GroundLocation.Z); UGameplayStatics::FinishSpawningActor(LlamaStart, FinalSpawnTransform); + AmountOfLlamasSpawned++; } + + LOG_INFO(LogGame, "Spawned {} llamas.", AmountOfLlamasSpawned); } \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaVehicle.h b/Project Reboot 3.0/FortAthenaVehicle.h index 41ead43..6fd53d9 100644 --- a/Project Reboot 3.0/FortAthenaVehicle.h +++ b/Project Reboot 3.0/FortAthenaVehicle.h @@ -17,6 +17,15 @@ public: return GetPawnAtSeat_Params.ReturnValue; } + int FindSeatIndex(class AFortPlayerPawn* PlayerPawn) + { + static auto FindSeatIndexFn = FindObject("/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); static UClass* StaticClass() diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 00b2dcc..0299ef4 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -963,7 +963,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena { 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) { - 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. { - static int LastNum420 = 1; + static int LastNum420 = 114; - if (Globals::AmountOfListens != LastNum420) + if (LastNum420 != Globals::AmountOfListens) { LastNum420 = Globals::AmountOfListens; diff --git a/Project Reboot 3.0/FortInventory.cpp b/Project Reboot 3.0/FortInventory.cpp index e657bf8..b41b968 100644 --- a/Project Reboot 3.0/FortInventory.cpp +++ b/Project Reboot 3.0/FortInventory.cpp @@ -154,7 +154,7 @@ std::pair, std::vector> AFortInventory::AddI { 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); @@ -165,17 +165,6 @@ std::pair, std::vector> AFortInventory::AddI bool DidApplyingGadgetSucceed = ApplyGadgetData(GadgetItemDefinition, Interface, NewItemInstance, idktbh); LOG_INFO(LogDev, "DidApplyingGadgetSucceed: {}", DidApplyingGadgetSucceed); 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> AFortInventory::AddI FortPlayerController->ServerExecuteInventoryItemHook(FortPlayerController, NewItemInstance->GetItemEntry()->GetItemGuid()); FortPlayerController->ClientEquipItem(NewItemInstance->GetItemEntry()->GetItemGuid(), true); } - - if (bWasGadget) - { - if (Fortnite_Version < 7) - { - // FortPlayerController->AddPickaxeToInventory(); - } - } } else { @@ -357,9 +338,12 @@ bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int bWasGadget = true; - if (Fortnite_Version < 7) + if (bWasGadget) { - // FortPlayerController->AddPickaxeToInventory(); + if (Fortnite_Version < 7 && GadgetItemDefinition->ShouldDropAllItemsOnEquip()) + { + FortPlayerController->AddPickaxeToInventory(); + } } } } diff --git a/Project Reboot 3.0/FortItem.h b/Project Reboot 3.0/FortItem.h index bd17b51..9a8d90c 100644 --- a/Project Reboot 3.0/FortItem.h +++ b/Project Reboot 3.0/FortItem.h @@ -1,7 +1,10 @@ #pragma once +#include + #include "NetSerialization.h" #include "Class.h" +#include "GameplayAbilitySpec.h" #include "reboot.h" @@ -101,6 +104,24 @@ struct FFortItemEntry : FFastArraySerializerItem 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& GetParentInventory() + { + static auto ParentInventoryOffset = FindOffsetStruct("/Script/FortniteGame.FortItemEntry", "ParentInventory"); + return *(TWeakObjectPtr*)(__int64(this) + ParentInventoryOffset); + } + void CopyFromAnotherItemEntry(FFortItemEntry* OtherItemEntry, bool bCopyGuid = false) { // We can use FortItemEntryStruct->CopyScriptStruct @@ -142,7 +163,7 @@ struct FFortItemEntry : FFastArraySerializerItem 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); Alloc(GetStructSize()); @@ -150,14 +171,18 @@ struct FFortItemEntry : FFastArraySerializerItem if (!Entry) return nullptr; - Entry->MostRecentArrayReplicationKey = -1; + Entry->MostRecentArrayReplicationKey = -1; // idk if we need to set this Entry->ReplicationID = -1; Entry->ReplicationKey = -1; Entry->GetItemDefinition() = ItemDefinition; Entry->GetCount() = Count; 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; } diff --git a/Project Reboot 3.0/FortKismetLibrary.cpp b/Project Reboot 3.0/FortKismetLibrary.cpp index 5933086..67ccfc8 100644 --- a/Project Reboot 3.0/FortKismetLibrary.cpp +++ b/Project Reboot 3.0/FortKismetLibrary.cpp @@ -35,6 +35,27 @@ UFortResourceItemDefinition* UFortKismetLibrary::K2_GetResourceItemDefinition(EF return params.ret; } +FVector UFortKismetLibrary::FindGroundLocationAt(UWorld* World, AActor* IgnoreActor, FVector InLocation, float TraceStartZ, float TraceEndZ, FName TraceName) +{ + static auto FindGroundLocationAtFn = FindObject("/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& CharacterParts, UObject* PlayerState, bool* bSuccess) { static auto fn = FindObject("/Script/FortniteGame.FortKismetLibrary.ApplyCharacterCosmetics"); @@ -550,7 +571,7 @@ bool UFortKismetLibrary::PickLootDropsHook(UObject* Context, FFrame& Stack, bool { 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()); } diff --git a/Project Reboot 3.0/FortKismetLibrary.h b/Project Reboot 3.0/FortKismetLibrary.h index dfe1863..59ac013 100644 --- a/Project Reboot 3.0/FortKismetLibrary.h +++ b/Project Reboot 3.0/FortKismetLibrary.h @@ -84,6 +84,7 @@ public: static UFortResourceItemDefinition* K2_GetResourceItemDefinition(EFortResourceType ResourceType); static void ApplyCharacterCosmetics(UObject* WorldContextObject, const TArray& 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 SpawnItemVariantPickupInWorldHook(UObject* Context, FFrame& Stack, void* Ret); diff --git a/Project Reboot 3.0/FortLootLevel.h b/Project Reboot 3.0/FortLootLevel.h new file mode 100644 index 0000000..09a3d74 --- /dev/null +++ b/Project Reboot 3.0/FortLootLevel.h @@ -0,0 +1,11 @@ +#pragma once + +#include "DataTable.h" + +class UFortLootLevel +{ + int GetItemLevel(FDataTableCategoryHandle LootLevelData, int WorldLevel) + { + return 0; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp index fa6a753..0c60f29 100644 --- a/Project Reboot 3.0/FortLootPackage.cpp +++ b/Project Reboot 3.0/FortLootPackage.cpp @@ -8,76 +8,367 @@ #include "FortGameModeAthena.h" #include - -float GetRandomFloatForLooting(float min, float max) -{ - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> dis(min, max); - float random_number = dis(gen); - - return random_number; - - return UKismetMathLibrary::RandomFloatInRange(min, max); -} - -static FFortLootTierData* GetLootTierData(std::vector& LootTierData, float TotalWeight) -{ - FFortLootTierData* SelectedItem = nullptr; - - for (auto Item : LootTierData) - { - TotalWeight += Item->GetWeight(); - } - - float RandomNumber = GetRandomFloatForLooting(0, TotalWeight); // is -1 needed? - - for (auto Item : LootTierData) - { - if (RandomNumber <= Item->GetWeight()) - { - SelectedItem = Item; - break; - } - - RandomNumber -= Item->GetWeight(); - } - - if (!SelectedItem) - return GetLootTierData(LootTierData, TotalWeight); - - return SelectedItem; -} - -static FFortLootPackageData* GetLootPackage(std::vector& LootPackages, float TotalWeight) -{ - FFortLootPackageData* SelectedItem = nullptr; - - float RandomNumber = GetRandomFloatForLooting(0, TotalWeight); // is -1 needed? - - for (auto Item : LootPackages) - { - if (RandomNumber <= Item->GetWeight()) - { - SelectedItem = Item; - break; - } - - RandomNumber -= Item->GetWeight(); - } - - if (!SelectedItem) - return GetLootPackage(LootPackages, TotalWeight); - - return SelectedItem; -} +#include +#include struct FFortGameFeatureLootTableData { TSoftObjectPtr LootTierData; - TSoftObjectPtr LootPackageData; + TSoftObjectPtr LootPackageData; }; +#ifdef EXPERIMENTAL_LOOTING +float RandomFloatForLoot(float AllWeightsSum) +{ + return (rand() * 0.000030518509) * AllWeightsSum; +} + +template +void CollectDataTablesRows(std::vector DataTables, std::map* OutMap, std::function Check = []() { return true; }) +{ + std::vector DataTablesToIterate; + + static auto CompositeDataTableClass = FindObject("/Script/Engine.CompositeDataTable"); + + for (auto DataTable : DataTables) + { + // if (auto CompositeDataTable = Cast(DataTable)) + if (DataTable->IsA(CompositeDataTableClass)) + { + auto CompositeDataTable = DataTable; + + static auto ParentTablesOffset = DataTable->GetOffset("ParentTables"); + auto& ParentTables = DataTable->Get>(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 +static T* PickWeightedElement(const std::map& Elements, std::function 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& 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 ItemEntries; +}; */ + +FFortLootTierData* PickLootTierData(const std::vector& 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 TierGroupLTDs; + + CollectDataTablesRows(LTDTables, &TierGroupLTDs, [&](FName RowName, FFortLootTierData* TierData) -> bool { + if (LootTierGroup == TierData->GetTierGroup()) + { + return true; + } + + return false; + }); + + FFortLootTierData* ChosenRowLootTierData = PickWeightedElement(TierGroupLTDs, + [](FFortLootTierData* LootTierData) -> float { return LootTierData->GetWeight(); }, -1, + true, LootTier, OutRowName); + + if (!ChosenRowLootTierData) + return nullptr; + + return ChosenRowLootTierData; +} + +void PickLootDropsFromLootPackage(const std::vector& LPTables, const FName& LootPackageName, std::vector* OutEntries, int LootPackageCategory = -1, bool bPrint = false) +{ + if (!OutEntries) + return; + + std::map LootPackageIDMap; + + CollectDataTablesRows(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(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(ItemDefinition); + int LoadedAmmo = WeaponItemDefinition ? WeaponItemDefinition->GetClipSize() : 0; // we shouldnt set loaded ammo here techinally + + if (auto WorldItemDefinition = Cast(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 PickLootDrops(FName TierGroupName, bool bPrint, int recursive) { std::vector LootDrops; @@ -353,6 +644,414 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs } } + 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) +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dis(min, max); + float random_number = dis(gen); + + return random_number; + + return UKismetMathLibrary::RandomFloatInRange(min, max); +} + +static FFortLootTierData* GetLootTierData(std::vector& LootTierData, float TotalWeight) +{ + FFortLootTierData* SelectedItem = nullptr; + + for (auto Item : LootTierData) + { + TotalWeight += Item->GetWeight(); + } + + float RandomNumber = GetRandomFloatForLooting(0, TotalWeight); // is -1 needed? + + for (auto Item : LootTierData) + { + if (RandomNumber <= Item->GetWeight()) + { + SelectedItem = Item; + break; + } + + RandomNumber -= Item->GetWeight(); + } + + if (!SelectedItem) + return GetLootTierData(LootTierData, TotalWeight); + + return SelectedItem; +} + +static FFortLootPackageData* GetLootPackage(std::vector& LootPackages, float TotalWeight) +{ + FFortLootPackageData* SelectedItem = nullptr; + + float RandomNumber = GetRandomFloatForLooting(0, TotalWeight); // is -1 needed? + + for (auto Item : LootPackages) + { + if (RandomNumber <= Item->GetWeight()) + { + SelectedItem = Item; + break; + } + + RandomNumber -= Item->GetWeight(); + } + + if (!SelectedItem) + return GetLootPackage(LootPackages, TotalWeight); + + return SelectedItem; +} + +std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recursive) +{ + std::vector LootDrops; + + if (recursive > 6) + return LootDrops; + + auto GameState = ((AFortGameModeAthena*)GetWorld()->GetGameMode())->GetGameStateAthena(); + static auto CurrentPlaylistDataOffset = GameState->GetOffset("CurrentPlaylistData", false); + + static std::vector LTDTables; + static std::vector LPTables; + + static auto CompositeDataTableClass = FindObject(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>(LootTierDataOffset); + + static auto LootPackagesOffset = CurrentPlaylist->GetOffset("LootPackages"); + auto& LootPackagesSoft = CurrentPlaylist->Get>(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(L"/Game/Items/Datatables/AthenaLootTierData_Client.AthenaLootTierData_Client")); + LPTables.push_back(LoadObject(L"/Game/Items/Datatables/AthenaLootPackages_Client.AthenaLootPackages_Client")); + } + + // LTDTables.push_back(LoadObject(L"/Game/Athena/Playlists/Playground/AthenaLootTierData_Client.AthenaLootTierData_Client")); + // LPTables.push_back(LoadObject(L"/Game/Athena/Playlists/Playground/AthenaLootPackages_Client.AthenaLootPackages_Client")); + + static auto FortGameFeatureDataClass = FindObject("/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(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>(PlaylistOverrideLootTableDataOffset); + + static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer"); + auto GameplayTagContainer = CurrentPlaylist->GetPtr(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>(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>(PlaylistOverrideLootTableDataOffset); + + static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer"); + auto GameplayTagContainer = CurrentPlaylist->GetPtr(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>(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>(LootTierDataOffset); + + static auto LootPackagesOffset = CurrentPlaylist->GetOffset("LootPackages"); + auto& LootPackagesSoft = CurrentPlaylist->Get>(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(L"/Game/Items/Datatables/AthenaLootTierData_Client.AthenaLootTierData_Client")); + LPTables.push_back(LoadObject(L"/Game/Items/Datatables/AthenaLootPackages_Client.AthenaLootPackages_Client")); + } + } + if (LTDTables.size() <= 0 || LPTables.size() <= 0) { LOG_WARN(LogLoot, "Empty tables! ({} {})", LTDTables.size(), LPTables.size()); @@ -360,7 +1059,7 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs } std::vector TierGroupLTDs; - + float TotalTierGroupLTDsWeight = 0; for (int p = 0; p < LTDTables.size(); p++) @@ -373,7 +1072,7 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs if (!LTD->IsValidLowLevel()) { // if (bPrint) - LOG_INFO(LogLoot, "BadRead!"); + LOG_INFO(LogLoot, "BadRead!"); continue; } @@ -639,18 +1338,14 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs if (bPrint) { - LOG_INFO(LogLoot, "[{}] {} {} {}", i, lootPackageCalls.size(), TierGroupLPStr, ItemDef->GetName()); + LOG_INFO(LogLoot, "[{}] {} {} {}", i, lootPackageCalls.size(), TierGroupLPStr, ItemDef->GetName()); } auto WeaponDef = Cast(ItemDef); - LootDrop lootDrop{}; - lootDrop.ItemDefinition = ItemDef; - lootDrop.LoadedAmmo = WeaponDef ? WeaponDef->GetClipSize() : 0; - lootDrop.Count = LootPackageCall->GetCount(); - - LootDrops.push_back(lootDrop); + LootDrops.push_back(LootDrop(FFortItemEntry::MakeItemEntry(ItemDef, LootPackageCall->GetCount(), WeaponDef ? WeaponDef->GetClipSize() : 0)); } return LootDrops; -} \ No newline at end of file +} +#endif diff --git a/Project Reboot 3.0/FortLootPackage.h b/Project Reboot 3.0/FortLootPackage.h index 977c7f7..e32aaff 100644 --- a/Project Reboot 3.0/FortLootPackage.h +++ b/Project Reboot 3.0/FortLootPackage.h @@ -5,13 +5,9 @@ #include "Array.h" #include "FortWorldItemDefinition.h" #include "SoftObjectPtr.h" +#include "FortItem.h" -struct LootDrop -{ - UFortItemDefinition* ItemDefinition; - int Count; - int LoadedAmmo; -}; +#define EXPERIMENTAL_LOOTING struct FFortLootPackageData { @@ -46,6 +42,12 @@ public: return *(int*)(__int64(this) + CountOffset); } + int& GetLootPackageCategory() + { + static auto LootPackageCategoryOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "LootPackageCategory"); + return *(int*)(__int64(this) + LootPackageCategoryOffset); + } + FString& GetAnnotation() { static auto AnnotationOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "Annotation"); @@ -99,4 +101,18 @@ public: } }; +struct LootDrop +{ + FFortItemEntry* ItemEntry; + + FFortItemEntry* operator->() { + return ItemEntry; + } + + ~LootDrop() + { + + } +}; + std::vector PickLootDrops(FName TierGroupName, bool bPrint = false, int recursive = 0); \ No newline at end of file diff --git a/Project Reboot 3.0/FortPickup.cpp b/Project Reboot 3.0/FortPickup.cpp index 7f5c143..09db872 100644 --- a/Project Reboot 3.0/FortPickup.cpp +++ b/Project Reboot 3.0/FortPickup.cpp @@ -8,6 +8,7 @@ #include "FortPlayerController.h" #include #include "GameplayStatics.h" +#include "gui.h" 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, 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) { @@ -85,14 +86,17 @@ AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Locatio { auto OtherPickup = (AFortPickup*)OtherPickupActor; - if (OtherPickup->GetPickupLocationData()->GetCombineTarget()) + if (OtherPickup == IgnoreCombinePickup || OtherPickup->GetPickupLocationData()->GetCombineTarget()) return false; 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 true; @@ -106,21 +110,29 @@ AFortPickup* AFortPickup::SpawnPickup(FFortItemEntry* ItemEntry, FVector Locatio PickupLocationData->GetCombineTarget() = (AFortPickup*)Pickup->GetClosestActor(AFortPickup::StaticClass(), 4, CanCombineWithPickup); } - // our little remake of tosspickup - - 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. + if (!PickupLocationData->GetCombineTarget()) // I don't think we should call TossPickup for every pickup anyways. { Pickup->TossPickup(Location, Pawn, 0, bToss, PickupSource, SpawnSource); } 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->ForceNetUpdate(); } 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, - int LoadedAmmo, AFortPawn* Pawn, UClass* OverrideClass, bool bToss) + int LoadedAmmo, AFortPawn* Pawn, UClass* OverrideClass, bool bToss, AFortPickup* IgnoreCombinePickup) { if (LoadedAmmo == -1) { @@ -153,7 +165,7 @@ AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Loca } 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); return Pickup; } @@ -164,27 +176,40 @@ void AFortPickup::CombinePickupHook(AFortPickup* Pickup) 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(); - PickupToCombineInto->OnRep_PrimaryPickupItemEntry(); + auto ItemOwner = Pickup->GetPickupLocationData()->GetItemOwner(); - PickupToCombineInto->ForceNetUpdate(); - PickupToCombineInto->FlushNetDormancy(); - - Pickup->K2_DestroyActor(); + auto NewOverStackPickup = AFortPickup::SpawnPickup(ItemDefinition, PickupToCombineInto->GetActorLocation(), OverStackCount, + EFortPickupSourceTypeFlag::Player, EFortPickupSpawnSource::Unset, -1, ItemOwner, nullptr, false, PickupToCombineInto); } + + PickupToCombineInto->GetPrimaryPickupItemEntry()->GetCount() += CountToAdd; + PickupToCombineInto->OnRep_PrimaryPickupItemEntry(); + + PickupToCombineInto->ForceNetUpdate(); + PickupToCombineInto->FlushNetDormancy(); + + Pickup->K2_DestroyActor(); } 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(Pickup->GetPickupLocationData()->GetPickupTarget()); if (!Pawn) @@ -234,7 +259,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) std::vector> PairsToMarkDirty; // vector of sets or something so no duplicates?? - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "Start cpyCount: {}", cpyCount); bool bWasHoldingSameItemWhenSwap = false; @@ -300,7 +325,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) bHasSwapped = true; - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "[{}] Swapping: {}", i, ItemDefinitionToSwap->GetFullName()); // bForceDontAddItem = true; @@ -311,7 +336,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) if (CurrentItemEntry->GetItemDefinition() == PickupItemDefinition) { - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "[{}] Found stack of item!", i); if (CurrentItemEntry->GetCount() < PickupItemDefinition->GetMaxStackSize()) @@ -327,7 +352,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) bEverStacked = true; - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "[{}] We are stacking {}.", i, AmountToStack); // if (cpyCount > 0) @@ -339,7 +364,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) if ((bIsInventoryFull || bForceOverflow) && cpyCount > 0) // overflow { - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "[{}] Overflow", i); UFortWorldItemDefinition* ItemDefinitionToSpawn = PickupItemDefinition; @@ -359,7 +384,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) if (cpyCount > 0 && !bIsInventoryFull && !bForceDontAddItem) { - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "Attempting to add to inventory."); if (bDoesStackExist ? PickupItemDefinition->DoesAllowMultipleStacks() : true) @@ -375,7 +400,7 @@ char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) else cpyCount -= NewItemCount; - if constexpr (bTestPrinting) + if (bDebugPrintSwapping) LOG_INFO(LogDev, "Added item with count {} to inventory.", NewItemCount); if (bHasSwapped && NewSwappedItem == FGuid(-1, -1, -1, -1)) diff --git a/Project Reboot 3.0/FortPickup.h b/Project Reboot 3.0/FortPickup.h index ff9d11f..2813fae 100644 --- a/Project Reboot 3.0/FortPickup.h +++ b/Project Reboot 3.0/FortPickup.h @@ -120,11 +120,11 @@ public: static AFortPickup* SpawnPickup(FFortItemEntry* ItemEntry, FVector Location, 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, - EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset, - int LoadedAmmo = -1, class AFortPawn* Pawn = nullptr, UClass* OverrideClass = nullptr, bool bToss = true); + static AFortPickup* SpawnPickup(class UFortItemDefinition* ItemDef, FVector Location, int Count, + EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset, + int LoadedAmmo = -1, class AFortPawn* Pawn = nullptr, UClass* OverrideClass = nullptr, bool bToss = true, AFortPickup* IgnoreCombinePickup = nullptr); static void CombinePickupHook(AFortPickup* Pickup); static char CompletePickupAnimationHook(AFortPickup* Pickup); diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 74deb50..83cbf22 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -61,7 +61,7 @@ bool AFortPlayerController::DoesBuildFree() return ReadBitfieldValue(bBuildFreeOffset, bBuildFreeFieldMask); } -void AFortPlayerController::DropAllItems(const std::vector& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar, bool bRemoveIfNotDroppable) +void AFortPlayerController::DropAllItems(const std::vector& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar, bool bRemoveIfNotDroppable, bool RemovePickaxe) { auto Pawn = this->GetMyFortPawn(); @@ -78,6 +78,8 @@ void AFortPlayerController::DropAllItems(const std::vector std::vector> GuidAndCountsToRemove; + auto PickaxeInstance = WorldInventory->GetPickaxeInstance(); + for (int i = 0; i < ItemInstances.Num(); i++) { auto ItemInstance = ItemInstances.at(i); @@ -86,6 +88,13 @@ void AFortPlayerController::DropAllItems(const std::vector continue; auto ItemEntry = ItemInstance->GetItemEntry(); + + if (RemovePickaxe && ItemInstance == PickaxeInstance) + { + GuidAndCountsToRemove.push_back({ ItemEntry->GetItemGuid(), ItemEntry->GetCount() }); + continue; + } + auto WorldItemDefinition = Cast(ItemEntry->GetItemDefinition()); 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()); // PlayerController->ServerExecuteInventoryItemHook(PlayerController, newitem->GetItemEntry()->GetItemGuid()); - /* if (WeaponComponent) + /* static auto GetSeatWeaponComponentFn = FindObject("/Script/FortniteGame.FortAthenaVehicle.GetSeatWeaponComponent"); + + if (GetSeatWeaponComponentFn) { - static auto bWeaponEquippedOffset = WeaponComponent->GetOffset("bWeaponEquipped"); - WeaponComponent->Get(bWeaponEquippedOffset) = true; + struct { int SeatIndex; UObject* ReturnValue; } AFortAthenaVehicle_GetSeatWeaponComponent_Params{}; - static auto CachedWeaponOffset = WeaponComponent->GetOffset("CachedWeapon"); - WeaponComponent->Get(CachedWeaponOffset) = VehicleWeapon; + Vehicle->ProcessEvent(GetSeatWeaponComponentFn, &AFortAthenaVehicle_GetSeatWeaponComponent_Params); - static auto CachedWeaponDefOffset = WeaponComponent->GetOffset("CachedWeaponDef"); - WeaponComponent->Get(CachedWeaponDefOffset) = VehicleWeaponDefinition; + UObject* WeaponComponent = AFortAthenaVehicle_GetSeatWeaponComponent_Params.ReturnValue; + + if (!WeaponComponent) + return; + + static auto WeaponSeatDefinitionStructSize = FindObject("/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>(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*)(__int64(WeaponSeat) + 0x20); + + VehicleGrantedWeaponItem->ObjectIndex = NewVehicleInstance->InternalIndex; + VehicleGrantedWeaponItem->ObjectSerialNumber = GetItemByIndex(NewVehicleInstance->InternalIndex)->SerialNumber; + + static auto bWeaponEquippedOffset = WeaponComponent->GetOffset("bWeaponEquipped"); + WeaponComponent->Get(bWeaponEquippedOffset) = true; + + break; + } } */ return; diff --git a/Project Reboot 3.0/FortPlayerController.h b/Project Reboot 3.0/FortPlayerController.h index 1e24d61..adb2697 100644 --- a/Project Reboot 3.0/FortPlayerController.h +++ b/Project Reboot 3.0/FortPlayerController.h @@ -87,7 +87,7 @@ public: return CosmeticLoadout; } - void AddPickaxeToInventory() + UFortItem* AddPickaxeToInventory() { auto CosmeticLoadout = GetCosmeticLoadout(); auto CosmeticLoadoutPickaxe = CosmeticLoadout ? CosmeticLoadout->GetPickaxe() : nullptr; @@ -100,10 +100,12 @@ public: auto WorldInventory = GetWorldInventory(); if (!WorldInventory || WorldInventory->GetPickaxeInstance()) - return; + return nullptr; - WorldInventory->AddItem(PickaxeDefinition, nullptr); + auto NewAndModifiedInstances = WorldInventory->AddItem(PickaxeDefinition, nullptr); WorldInventory->Update(); + + return NewAndModifiedInstances.first.size() > 0 ? NewAndModifiedInstances.first[0] : nullptr; } bool& ShouldTryPickupSwap() @@ -121,7 +123,7 @@ public: void ClientEquipItem(const FGuid& ItemGuid, bool bForceExecution); bool DoesBuildFree(); - void DropAllItems(const std::vector& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar = false, bool bRemoveIfNotDroppable = false); + void DropAllItems(const std::vector& IgnoreItemDefs, bool bIgnoreSecondaryQuickbar = false, bool bRemoveIfNotDroppable = false, bool RemovePickaxe = false); void ApplyCosmeticLoadout(); static void ServerLoadingScreenDroppedHook(UObject* Context, FFrame* Stack, void* Ret); diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.cpp b/Project Reboot 3.0/FortPlayerControllerAthena.cpp index 86e0bbd..30ed309 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.cpp +++ b/Project Reboot 3.0/FortPlayerControllerAthena.cpp @@ -44,6 +44,13 @@ void AFortPlayerControllerAthena::StartGhostModeHook(UObject* Context, FFrame* S if (!WorldInventory) return StartGhostModeOriginal(Context, Stack, Ret); + auto PickaxeInstance = WorldInventory->GetPickaxeInstance(); + + if (PickaxeInstance) + { + WorldInventory->RemoveItem(PickaxeInstance->GetItemEntry()->GetItemGuid(), nullptr, PickaxeInstance->GetItemEntry()->GetCount(), true); + } + bool bShouldUpdate = false; auto NewAndModifiedInstances = WorldInventory->AddItem(ItemProvidingGhostMode, &bShouldUpdate, 1); auto GhostModeItemInstance = NewAndModifiedInstances.first[0]; @@ -51,7 +58,7 @@ void AFortPlayerControllerAthena::StartGhostModeHook(UObject* Context, FFrame* S if (!GhostModeItemInstance) return StartGhostModeOriginal(Context, Stack, Ret); - if (bShouldUpdate) + // if (bShouldUpdate) WorldInventory->Update(); PlayerController->ServerExecuteInventoryItemHook(PlayerController, GhostModeItemInstance->GetItemEntry()->GetItemGuid()); @@ -95,12 +102,20 @@ void AFortPlayerControllerAthena::EndGhostModeHook(AFortPlayerControllerAthena* if (!GhostModeItemInstance) return EndGhostModeOriginal(PlayerController); + auto PickaxeInstance = PlayerController->AddPickaxeToInventory(); + WorldInventory->Update(); + + if (PickaxeInstance) + { + PlayerController->ClientEquipItem(PickaxeInstance->GetItemEntry()->GetItemGuid(), true); + } + bool bShouldUpdate = false; int Count = GhostModeItemInstance->GetItemEntry()->GetCount(); // 1 bool bForceRemoval = true; // false WorldInventory->RemoveItem(GhostModeItemInstance->GetItemEntry()->GetItemGuid(), &bShouldUpdate, Count, bForceRemoval); - if (bShouldUpdate) + // if (bShouldUpdate) WorldInventory->Update(); return EndGhostModeOriginal(PlayerController); diff --git a/Project Reboot 3.0/FortPlayerPawn.cpp b/Project Reboot 3.0/FortPlayerPawn.cpp index 8302142..cf30935 100644 --- a/Project Reboot 3.0/FortPlayerPawn.cpp +++ b/Project Reboot 3.0/FortPlayerPawn.cpp @@ -140,17 +140,7 @@ UFortWeaponItemDefinition* AFortPlayerPawn::GetVehicleWeaponDefinition(AFortAthe if (!Vehicle) return nullptr; - static auto FindSeatIndexFn = FindObject("/Script/FortniteGame.FortAthenaVehicle.FindSeatIndex"); - /* 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); + return Vehicle->GetVehicleWeaponForSeat(Vehicle->FindSeatIndex(this)); } void AFortPlayerPawn::UnEquipVehicleWeaponDefinition(UFortWeaponItemDefinition* VehicleWeaponDefinition) diff --git a/Project Reboot 3.0/FortWeaponItemDefinition.cpp b/Project Reboot 3.0/FortWeaponItemDefinition.cpp index 806a7d6..8eb4926 100644 --- a/Project Reboot 3.0/FortWeaponItemDefinition.cpp +++ b/Project Reboot 3.0/FortWeaponItemDefinition.cpp @@ -38,8 +38,8 @@ int UFortWeaponItemDefinition::GetClipSize() UFortWorldItemDefinition* UFortWeaponItemDefinition::GetAmmoData() { static auto AmmoDataOffset = GetOffset("AmmoData"); - auto AmmoData = Get>(AmmoDataOffset); - return AmmoData.Get(); + auto AmmoData = GetPtr>(AmmoDataOffset); + return AmmoData->Get(); } UClass* UFortWeaponItemDefinition::StaticClass() diff --git a/Project Reboot 3.0/FortWorldItemDefinition.h b/Project Reboot 3.0/FortWorldItemDefinition.h index 7d3c744..131fa40 100644 --- a/Project Reboot 3.0/FortWorldItemDefinition.h +++ b/Project Reboot 3.0/FortWorldItemDefinition.h @@ -20,6 +20,12 @@ public: return ReadBitfieldValue(bCanBeDroppedOffset, bCanBeDroppedFieldMask); } + int& GetDropCount() + { + static auto DropCountOffset = GetOffset("DropCount"); + return Get(DropCountOffset); + } + EWorldItemDropBehavior& GetDropBehavior() { static auto DropBehaviorOffset = GetOffset("DropBehavior"); diff --git a/Project Reboot 3.0/NameTypes.h b/Project Reboot 3.0/NameTypes.h index 9f0cef2..389c76a 100644 --- a/Project Reboot 3.0/NameTypes.h +++ b/Project Reboot 3.0/NameTypes.h @@ -29,4 +29,9 @@ struct FName { return ComparisonIndex.Value == other.ComparisonIndex.Value; } + + bool operator<(FName Other) const + { + return this->ComparisonIndex.Value < Other.ComparisonIndex.Value; + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index 7d76d7b..ce5b571 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -335,6 +335,7 @@ + diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters index c05dbd0..a33f410 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -694,9 +694,6 @@ Engine\Source\Runtime\Core\Public\Templates - - Engine\Source\Runtime\Core\Public\Templates - Engine\Source\Runtime\Core\Public\Templates @@ -848,6 +845,12 @@ Engine\Source\Runtime\Core\Public\Math + + Engine\Source\Runtime\Core\Public\Templates + + + FortniteGame\Source\FortniteGame\Public\Items + diff --git a/Project Reboot 3.0/commands.h b/Project Reboot 3.0/commands.h index e4ff69c..4af139b 100644 --- a/Project Reboot 3.0/commands.h +++ b/Project Reboot 3.0/commands.h @@ -490,7 +490,7 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg) return; } - auto Pawn = ReceivingController->GetMyFortPawn(); + auto Pawn = ReceivingController->GetPawn(); if (!Pawn) { @@ -519,11 +519,25 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg) if (ClassObj) { + int AmountSpawned = 0; + for (int i = 0; i < Count; i++) { + FActorSpawnParameters SpawnParameters{}; + // SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + auto Loc = Pawn->GetActorLocation(); - // Loc.Z += 1000; - GetWorld()->SpawnActor(ClassObj, Loc, FQuat()); + Loc.Z += 1000; + auto NewActor = GetWorld()->SpawnActor(ClassObj, Loc, FQuat(), FVector(1, 1, 1), SpawnParameters); + + if (!NewActor) + { + SendMessageToConsole(PlayerController, L"Failed to spawn an actor!"); + } + else + { + AmountSpawned++; + } } SendMessageToConsole(PlayerController, L"Summoned!"); diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index e365710..a54eec8 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -627,12 +627,26 @@ static inline uint64 FindUpdateTrackedAttributesLea() // kill me static inline uint64 FindCombinePickupLea() // kill me { + return 0; + /* uint64 OnRep_PickupLocationDataAddr = 0; // TODO (Idea: Find SetupCombinePickupDelegates from this). if (!OnRep_PickupLocationDataAddr) 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) return 0; @@ -644,7 +658,7 @@ static inline uint64 FindCombinePickupLea() // kill me { auto loadAddress = Memcury::Scanner(SetupCombinePickupDelegatesAddr + i).RelativeOffset(3).Get(); - if (IsNullSub(loadAddress)) // Safety + if (!bShouldCheckSafety || IsNullSub(loadAddress)) return SetupCombinePickupDelegatesAddr + i; } } diff --git a/Project Reboot 3.0/gui.h b/Project Reboot 3.0/gui.h index d192b86..956b885 100644 --- a/Project Reboot 3.0/gui.h +++ b/Project Reboot 3.0/gui.h @@ -48,8 +48,9 @@ #define DUMP_TAB 7 #define UNBAN_TAB 8 #define DEVELOPER_TAB 9 -#define SETTINGS_TAB 10 -#define CREDITS_TAB 11 +#define DEBUGLOG_TAB 10 +#define SETTINGS_TAB 11 +#define CREDITS_TAB 12 #define MAIN_PLAYERTAB 1 #define INVENTORY_PLAYERTAB 2 @@ -61,6 +62,8 @@ extern inline bool bSwitchedInitialLevel = false; extern inline bool bIsInAutoRestart = false; extern inline float AutoBusStartSeconds = 60; extern inline int NumRequiredPlayersToStart = 2; +extern inline bool bDebugPrintLooting = false; +extern inline bool bDebugPrintSwapping = false; // THE BASE CODE IS FROM IMGUI GITHUB @@ -376,6 +379,14 @@ static inline void MainTabs() bInformationTab = false; ImGui::EndTabItem(); } + + if (ImGui::BeginTabItem("Debug Logs")) + { + Tab = DEBUGLOG_TAB; + PlayerTab = -1; + bInformationTab = false; + ImGui::EndTabItem(); + } #endif 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) { // ImGui::Checkbox("Use custom lootpool (from Win64/lootpool.txt)", &Defines::bCustomLootpool); diff --git a/Project Reboot 3.0/inc.h b/Project Reboot 3.0/inc.h index cf097a8..5844254 100644 --- a/Project Reboot 3.0/inc.h +++ b/Project Reboot 3.0/inc.h @@ -49,7 +49,7 @@ struct PlaceholderBitfield inline bool AreVehicleWeaponsEnabled() { - return Fortnite_Version < 9; + return Fortnite_Version > 6; } inline bool IsRestartingSupported() diff --git a/Project Reboot 3.0/vehicles.h b/Project Reboot 3.0/vehicles.h index fed8ba1..41088f2 100644 --- a/Project Reboot 3.0/vehicles.h +++ b/Project Reboot 3.0/vehicles.h @@ -184,11 +184,22 @@ static inline void SpawnVehicles2() static auto FortAthenaVehicleSpawnerClass = FindObject("/Script/FortniteGame.FortAthenaVehicleSpawner"); TArray AllVehicleSpawners = UGameplayStatics::GetAllActorsOfClass(GetWorld(), FortAthenaVehicleSpawnerClass); + int AmountOfVehiclesSpawned = 0; + for (int i = 0; i < AllVehicleSpawners.Num(); i++) { auto VehicleSpawner = AllVehicleSpawners.at(i); auto Vehicle = SpawnVehicleFromSpawner(VehicleSpawner); + + if (Vehicle) + { + AmountOfVehiclesSpawned++; + } } + auto AllVehicleSpawnersNum = AllVehicleSpawners.Num(); + AllVehicleSpawners.Free(); + + LOG_INFO(LogGame, "Spawned {}/{} vehicles.", AmountOfVehiclesSpawned, AllVehicleSpawnersNum); } \ No newline at end of file diff --git a/Project Reboot 3.0/vendingmachine.h b/Project Reboot 3.0/vendingmachine.h index aac8a00..948ecec 100644 --- a/Project Reboot 3.0/vendingmachine.h +++ b/Project Reboot 3.0/vendingmachine.h @@ -134,7 +134,7 @@ static inline void FillItemCollector(ABuildingItemCollectorActor* ItemCollector, for (int LootDropIt = 0; LootDropIt < LootDrops.size(); LootDropIt++) { - auto WorldItemDefinition = Cast(LootDrops[LootDropIt].ItemDefinition); + UFortWorldItemDefinition* WorldItemDefinition = Cast(LootDrops[LootDropIt]->GetItemDefinition()); if (!WorldItemDefinition) continue; @@ -182,7 +182,7 @@ static inline void FillItemCollector(ABuildingItemCollectorActor* ItemCollector, 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) continue; diff --git a/vendor/memcury.h b/vendor/memcury.h index 877f8ad..1727ec4 100644 --- a/vendor/memcury.h +++ b/vendor/memcury.h @@ -1431,4 +1431,10 @@ return PtrRef.ScanFor(Bytes, false).Get(); } - inline bool IsNullSub(uint64 Addr) { return *(uint8_t*)(Addr) == 0xC3 || *(uint8_t*)(Addr) == 0xC2; } \ No newline at end of file + 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; + } \ No newline at end of file