#include "FortLootPackage.h" #include "DataTable.h" #include "KismetMathLibrary.h" static FFortLootTierData* GetLootTierData(std::vector& LootTierData, bool bPrint) { float TotalWeight = 0; for (auto Item : LootTierData) { TotalWeight += Item->GetWeight(); } float RandomNumber = UKismetMathLibrary::RandomFloatInRange(0, TotalWeight); // is -1 needed? FFortLootTierData* SelectedItem = nullptr; if (bPrint) { std::cout << std::format("TotalWeight: {}\n", TotalWeight); } for (auto Item : LootTierData) { if (bPrint) { std::cout << std::format("Rand: {} Weight: {}\n", RandomNumber, Item->GetWeight()); } if (RandomNumber <= Item->GetWeight()) { SelectedItem = Item; break; } RandomNumber -= Item->GetWeight(); } if (!SelectedItem) return GetLootTierData(LootTierData, bPrint); return SelectedItem; } static FFortLootPackageData* GetLootPackage(std::vector& LootPackages) { float TotalWeight = 0; for (auto Item : LootPackages) { TotalWeight += Item->GetWeight(); } float RandomNumber = UKismetMathLibrary::RandomFloatInRange(0, TotalWeight); // is -1 needed? FFortLootPackageData* SelectedItem = nullptr; for (auto Item : LootPackages) { if (RandomNumber <= Item->GetWeight()) { SelectedItem = Item; break; } RandomNumber -= Item->GetWeight(); } if (!SelectedItem) return GetLootPackage(LootPackages); return SelectedItem; } std::vector> PickLootDrops(FName TierGroupName, bool bPrint, int recursive) { std::vector> LootDrops; static std::vector LTDTables; static std::vector LPTables; static bool bHasFoundTables = false; if (!bHasFoundTables) { bHasFoundTables = true; LTDTables.push_back(LoadObject(L"/Game/Items/Datatables/AthenaLootTierData_Client.AthenaLootTierData_Client")); LPTables.push_back(LoadObject(L"/Game/Items/Datatables/AthenaLootPackages_Client.AthenaLootPackages_Client")); } std::vector TierGroupLTDs; for (int p = 0; p < LTDTables.size(); p++) { auto LTD = LTDTables[p]; if (!LTD) continue; auto& LTDRowMap = LTD->GetRowMap(); auto LTDRowMapNum = LTDRowMap.Pairs.Elements.Num(); // auto TierGroupNameStr = TierGroupName.ToString(); // LOG_INFO(LogLoot, "LTDRowMapNum: {}", LTDRowMapNum); for (int i = 0; i < LTDRowMapNum; i++) { auto& CurrentLTD = LTDRowMap.Pairs.Elements[i].ElementData.Value; auto TierData = (FFortLootTierData*)CurrentLTD.Value(); if (IsBadReadPtr(TierData, 8)) continue; // auto TierDataGroupStr = TierData->TierGroup.ToString(); // std::cout << "TierData->TierGroup.ToString(): " << TierDataGroupStr << '\n'; if (TierGroupName == TierData->GetTierGroup() /* TierDataGroupStr == TierGroupNameStr */ && TierData->GetWeight() != 0) { TierGroupLTDs.push_back(TierData); } } } if (TierGroupLTDs.size() == 0) { LOG_WARN(LogLoot, "Failed to find any LTD for: {}", TierGroupName.ToString()); return LootDrops; } if (bPrint) { std::cout << "TierGroupLTDs.size(): " << TierGroupLTDs.size() << '\n'; } FFortLootTierData* ChosenRowLootTierData = GetLootTierData(TierGroupLTDs, bPrint); if (!ChosenRowLootTierData) // Should NEVER happen return LootDrops; if (ChosenRowLootTierData->GetNumLootPackageDrops() <= 0) return PickLootDrops(TierGroupName, bPrint, ++recursive); // hm auto& LootPackageCategoryMinArray = ChosenRowLootTierData->GetLootPackageCategoryMinArray(); auto& LootPackageCategoryWeightArray = ChosenRowLootTierData->GetLootPackageCategoryWeightArray(); auto& LootPackageCategoryMaxArray = ChosenRowLootTierData->GetLootPackageCategoryMaxArray(); if (LootPackageCategoryMinArray.ArrayNum != LootPackageCategoryWeightArray.ArrayNum || LootPackageCategoryMinArray.ArrayNum != LootPackageCategoryMaxArray.ArrayNum) return PickLootDrops(TierGroupName, bPrint, ++recursive); // hm int MinimumLootDrops = 0; static int AmountToAdd = Engine_Version >= 424 ? 1 : 0; // fr float NumLootPackageDrops = std::floor(ChosenRowLootTierData->GetNumLootPackageDrops() + AmountToAdd); if (LootPackageCategoryMinArray.ArrayNum) { for (int i = 0; i < LootPackageCategoryMinArray.ArrayNum; i++) { if (LootPackageCategoryMinArray.at(i) > 0) { MinimumLootDrops += LootPackageCategoryMinArray.at(i); } } } if (MinimumLootDrops > NumLootPackageDrops) { } int SumLootPackageCategoryWeightArray = 0; for (int i = 0; i < LootPackageCategoryWeightArray.Num(); i++) { auto CategoryWeight = LootPackageCategoryWeightArray.at(i); if (CategoryWeight > 0) { auto CategoryMaxArray = LootPackageCategoryMaxArray.at(i); if (CategoryMaxArray < 0) { SumLootPackageCategoryWeightArray += CategoryWeight; } } } int SumLootPackageCategoryMinArray = 0; for (int i = 0; i < LootPackageCategoryMinArray.Num(); i++) { auto CategoryWeight = LootPackageCategoryMinArray.at(i); if (CategoryWeight > 0) { auto CategoryMaxArray = LootPackageCategoryMaxArray.at(i); if (CategoryMaxArray < 0) { SumLootPackageCategoryMinArray += CategoryWeight; } } } if (SumLootPackageCategoryWeightArray > SumLootPackageCategoryMinArray) return PickLootDrops(TierGroupName, bPrint, ++recursive); // hm std::vector TierGroupLPs; for (int p = 0; p < LPTables.size(); p++) { auto LP = LPTables[p]; auto& LPRowMap = LP->GetRowMap(); for (int i = 0; i < LPRowMap.Pairs.Elements.Num(); i++) { auto& CurrentLP = LPRowMap.Pairs.Elements[i].ElementData.Value; auto LootPackage = (FFortLootPackageData*)CurrentLP.Value(); if (!LootPackage) continue; if (LootPackage->GetLootPackageID() == ChosenRowLootTierData->GetLootPackage() && LootPackage->GetWeight() != 0) { TierGroupLPs.push_back(LootPackage); } } } auto ChosenLootPackageName = ChosenRowLootTierData->GetLootPackage().ToString(); if (ChosenLootPackageName.contains(".Empty")) { return PickLootDrops(TierGroupName, bPrint); // return LootDrops; } bool bIsWorldList = ChosenLootPackageName.contains("WorldList"); if (bPrint) { std::cout << "NumLootPackageDrops Floored: " << NumLootPackageDrops << '\n'; std::cout << "NumLootPackageDrops Original: " << ChosenRowLootTierData->GetNumLootPackageDrops() << '\n'; std::cout << "TierGroupLPs.size(): " << TierGroupLPs.size() << '\n'; std::cout << "ChosenLootPackageName: " << ChosenLootPackageName << '\n'; /* float t = ChosenRowLootTierData->NumLootPackageDrops; int b = (int)((t + t) - 0.5) >> 1; auto c = ChosenRowLootTierData->NumLootPackageDrops - b; b += c >= (rand() * 0.000030518509); std::cout << "b: " << b << '\n'; */ } LootDrops.reserve(NumLootPackageDrops); for (float i = 0; i < NumLootPackageDrops; i++) { if (i >= TierGroupLPs.size()) break; auto TierGroupLP = TierGroupLPs.at(i); auto& LootPackageCallFStr = TierGroupLP->GetLootPackageCall(); auto TierGroupLPStr = LootPackageCallFStr.IsValid() ? LootPackageCallFStr.ToString() : ".Empty"; if (TierGroupLPStr.contains(".Empty")) { NumLootPackageDrops++; continue; } std::vector lootPackageCalls; if (bIsWorldList) { for (int j = 0; j < TierGroupLPs.size(); j++) { auto& CurrentLP = TierGroupLPs.at(j); if (CurrentLP->GetWeight() != 0) lootPackageCalls.push_back(CurrentLP); } } else { for (int p = 0; p < LPTables.size(); p++) { auto LPRowMap = LPTables[p]->GetRowMap(); for (int j = 0; j < LPRowMap.Pairs.Elements.Num(); j++) { auto& CurrentLP = LPRowMap.Pairs.Elements[j].ElementData.Value; auto LootPackage = (FFortLootPackageData*)CurrentLP.Value(); if (LootPackage->GetLootPackageID().ToString() == TierGroupLPStr && LootPackage->GetWeight() != 0) { lootPackageCalls.push_back(LootPackage); } } } } if (lootPackageCalls.size() == 0) { // std::cout << "lootPackageCalls.size() == 0!\n"; NumLootPackageDrops++; // ?? continue; } FFortLootPackageData* LootPackageCall = GetLootPackage(lootPackageCalls); if (!LootPackageCall) // Should NEVER happen continue; auto ItemDef = LootPackageCall->GetItemDefinition().Get(); if (!ItemDef) { NumLootPackageDrops++; // ?? continue; } if (bPrint) { std::cout << std::format("[{}] {} {} {}\n", i, lootPackageCalls.size(), TierGroupLPStr, ItemDef->GetName()); } LootDrops.push_back({ ItemDef, LootPackageCall->GetCount() }); } return LootDrops; }