fixed looting

This commit is contained in:
Milxnor
2023-06-28 11:58:20 -04:00
parent feea0e20f3
commit 26abeab332
14 changed files with 244 additions and 161 deletions

View File

@@ -175,6 +175,19 @@ void AFortGameModeAthena::HandleSpawnRateForActorClass(UClass* ActorClass, float
} }
} }
void AFortGameModeAthena::StartAircraftPhase()
{
if (Addresses::StartAircraftPhase)
{
static void (*StartAircraftPhaseOriginal)(AFortGameModeAthena*, bool bDoNotSpawnAircraft) = decltype(StartAircraftPhaseOriginal)(Addresses::StartAircraftPhase);
StartAircraftPhaseOriginal(this, false); // love the double negative fortnite
}
else
{
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"startaircraft", nullptr);
}
}
void AFortGameModeAthena::PauseSafeZone(bool bPaused) void AFortGameModeAthena::PauseSafeZone(bool bPaused)
{ {
auto GameState = GetGameStateAthena(); auto GameState = GetGameStateAthena();

View File

@@ -263,6 +263,7 @@ public:
UClass* GetVehicleClassOverride(UClass* DefaultClass); UClass* GetVehicleClassOverride(UClass* DefaultClass);
void SkipAircraft(); void SkipAircraft();
void PauseSafeZone(bool bPaused = true); void PauseSafeZone(bool bPaused = true);
void StartAircraftPhase();
static void HandleSpawnRateForActorClass(UClass* ActorClass, float SpawnPercentage); // idk where to put static void HandleSpawnRateForActorClass(UClass* ActorClass, float SpawnPercentage); // idk where to put

View File

@@ -249,7 +249,7 @@ std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AFortInventory::AddI
return Ret; return Ret;
} }
bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int Count, bool bForceRemoval) bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int Count, bool bForceRemoval, bool bIgnoreVariables)
{ {
if (bShouldUpdate) if (bShouldUpdate)
*bShouldUpdate = false; *bShouldUpdate = false;
@@ -280,7 +280,7 @@ bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int
bool bOverrideChangeStackSize = false; bool bOverrideChangeStackSize = false;
if (ItemDefinition->ShouldPersistWhenFinalStackEmpty()) if (!bIgnoreVariables && ItemDefinition->ShouldPersistWhenFinalStackEmpty())
{ {
bool bIsFinalStack = true; bool bIsFinalStack = true;

View File

@@ -101,7 +101,7 @@ public:
std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AddItem(FFortItemEntry* ItemEntry, bool* bShouldUpdate, bool bShowItemToast = false, int OverrideCount = -1); std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AddItem(FFortItemEntry* ItemEntry, bool* bShouldUpdate, bool bShowItemToast = false, int OverrideCount = -1);
std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AddItem(UFortItemDefinition* ItemDefinition, bool* bShouldUpdate, int Count = 1, int LoadedAmmo = -1, bool bShowItemToast = false); std::pair<std::vector<UFortItem*>, std::vector<UFortItem*>> AddItem(UFortItemDefinition* ItemDefinition, bool* bShouldUpdate, int Count = 1, int LoadedAmmo = -1, bool bShowItemToast = false);
bool RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int Count, bool bForceRemoval = false); bool RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int Count, bool bForceRemoval = false, bool bIgnoreVariables = false);
void SwapItem(const FGuid& ItemGuid, FFortItemEntry* NewItemEntry, int OverrideNewCount = -1, std::pair<FFortItemEntry*, FFortItemEntry*>* outEntries = nullptr); void SwapItem(const FGuid& ItemGuid, FFortItemEntry* NewItemEntry, int OverrideNewCount = -1, std::pair<FFortItemEntry*, FFortItemEntry*>* outEntries = nullptr);
void ModifyCount(UFortItem* ItemInstance, int New, bool bRemove = false, std::pair<FFortItemEntry*, FFortItemEntry*>* outEntries = nullptr, bool bUpdate = true, bool bShowItemToast = false); void ModifyCount(UFortItem* ItemInstance, int New, bool bRemove = false, std::pair<FFortItemEntry*, FFortItemEntry*>* outEntries = nullptr, bool bUpdate = true, bool bShowItemToast = false);

View File

@@ -121,11 +121,11 @@ float GetAmountOfLootPackagesToDrop(FFortLootTierData* LootTierData, int Origina
{ {
// HONESTLY IDEK WHAT FORTNITE DOES HERE // HONESTLY IDEK WHAT FORTNITE DOES HERE
float v29 = (float)rand() * 0.000030518509; float v29 = (float)rand() * 0.000030518509f;
float v35 = (int)(float)((float)((float)((float)SumLootPackageCategoryWeightArray * v29) float v35 = (int)(float)((float)((float)((float)SumLootPackageCategoryWeightArray * v29)
+ (float)((float)SumLootPackageCategoryWeightArray * v29)) + (float)((float)SumLootPackageCategoryWeightArray * v29))
+ 0.5) >> 1; + 0.5f) >> 1;
// OutLootTierInfo->Hello++; // OutLootTierInfo->Hello++;
MinimumLootDrops++; MinimumLootDrops++;
@@ -153,27 +153,30 @@ float GetAmountOfLootPackagesToDrop(FFortLootTierData* LootTierData, int Origina
FFortLootTierData* PickLootTierData(const std::vector<UDataTable*>& LTDTables, FName LootTierGroup, 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. FFortLootTierData* PickLootTierData(const std::vector<UDataTable*>& LTDTables, FName LootTierGroup, 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.
{ {
// This like isn't right, at all.
float LootTier = ForcedLootTier; float LootTier = ForcedLootTier;
float IdkForcedWeightorsomething = -1;
if (LootTier == -1) if (LootTier == -1)
{ {
// IdkForcedWeightorsomething = weightofAlltherowsithink // we don't ened to do this since the pickweightedeleent already does that // LootTier = ??
} }
else else
{ {
// ITS SO MUCH TO REVERSE AAAAA // buncha code im too lazy to reverse
} }
// if (fabs(LootTier) <= 0.0000000099999999) // if (fabs(LootTier) <= 0.0000000099999999f)
// return 0; // return 0;
int Multiplier = LootTier == -1 ? 1 : LootTier; // Idk i think we need to fill out the code above for this to work properly maybe
LOOTING_MAP_TYPE<FName, FFortLootTierData*> TierGroupLTDs; LOOTING_MAP_TYPE<FName, FFortLootTierData*> TierGroupLTDs;
CollectDataTablesRows<FFortLootTierData>(LTDTables, &TierGroupLTDs, [&](FName RowName, FFortLootTierData* TierData) -> bool { CollectDataTablesRows<FFortLootTierData>(LTDTables, &TierGroupLTDs, [&](FName RowName, FFortLootTierData* TierData) -> bool {
if (LootTierGroup == TierData->GetTierGroup()) if (LootTierGroup == TierData->GetTierGroup())
{ {
if ((LootTier == -1 ? true : LootTier == TierData->GetLootTier())) // idek if this is proper if ((LootTier == -1 ? true : LootTier == TierData->GetLootTier()))
{ {
return true; return true;
} }
@@ -186,20 +189,18 @@ FFortLootTierData* PickLootTierData(const std::vector<UDataTable*>& LTDTables, F
FFortLootTierData* ChosenRowLootTierData = PickWeightedElement<FName, FFortLootTierData*>(TierGroupLTDs, FFortLootTierData* ChosenRowLootTierData = PickWeightedElement<FName, FFortLootTierData*>(TierGroupLTDs,
[](FFortLootTierData* LootTierData) -> float { return LootTierData->GetWeight(); }, RandomFloatForLoot, -1, [](FFortLootTierData* LootTierData) -> float { return LootTierData->GetWeight(); }, RandomFloatForLoot, -1,
true, 1, OutRowName, false, false, IdkForcedWeightorsomething); true, Multiplier, OutRowName);
return ChosenRowLootTierData; return ChosenRowLootTierData;
} }
void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FName LootPackageName, std::vector<LootDrop>* OutEntries, int LootPackageCategory = -1, int WorldLevel = 0, bool bPrint = false, bool bCombineDrops = true) void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, const FName& LootPackageName, std::vector<LootDrop>* OutEntries, int LootPackageCategory = -1, int WorldLevel = 0, bool bPrint = false, bool bCombineDrops = true)
{ {
if (!OutEntries) if (!OutEntries)
return; return;
LOOTING_MAP_TYPE<FName, FFortLootPackageData*> LootPackageIDMap; LOOTING_MAP_TYPE<FName, FFortLootPackageData*> LootPackageIDMap;
float TotalWeight = 0;
CollectDataTablesRows<FFortLootPackageData>(LPTables, &LootPackageIDMap, [&](FName RowName, FFortLootPackageData* LootPackage) -> bool { CollectDataTablesRows<FFortLootPackageData>(LPTables, &LootPackageIDMap, [&](FName RowName, FFortLootPackageData* LootPackage) -> bool {
if (LootPackage->GetLootPackageID() != LootPackageName) if (LootPackage->GetLootPackageID() != LootPackageName)
{ {
@@ -214,18 +215,16 @@ void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FNam
if (WorldLevel >= 0) if (WorldLevel >= 0)
{ {
if (LootPackage->GetMaxWorldLevel() >= 0 && WorldLevel > LootPackage->GetMaxWorldLevel()) if (LootPackage->GetMaxWorldLevel() >= 0 && WorldLevel > LootPackage->GetMaxWorldLevel())
return false; return 0;
if (LootPackage->GetMinWorldLevel() >= 0 && WorldLevel < LootPackage->GetMinWorldLevel()) if (LootPackage->GetMinWorldLevel() >= 0 && WorldLevel < LootPackage->GetMinWorldLevel())
return false; return 0;
} }
TotalWeight += LootPackage->GetWeight();
return true; return true;
}); });
if (TotalWeight == 0) if (LootPackageIDMap.size() == 0)
{ {
// std::cout << std::format("Loot Package {} has no valid weights.\n", LootPackageName.ToString()); // std::cout << std::format("Loot Package {} has no valid weights.\n", LootPackageName.ToString());
return; return;
@@ -233,7 +232,8 @@ void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FNam
FName PickedPackageRowName; FName PickedPackageRowName;
FFortLootPackageData* PickedPackage = PickWeightedElement<FName, FFortLootPackageData*>(LootPackageIDMap, FFortLootPackageData* PickedPackage = PickWeightedElement<FName, FFortLootPackageData*>(LootPackageIDMap,
[](FFortLootPackageData* LootPackageData) -> float { return LootPackageData->GetWeight(); }, RandomFloatForLoot, -1, true, 1, &PickedPackageRowName, bPrint); [](FFortLootPackageData* LootPackageData) -> float { return LootPackageData->GetWeight(); }, RandomFloatForLoot,
-1, true, 1, &PickedPackageRowName, bPrint);
if (!PickedPackage) if (!PickedPackage)
return; return;
@@ -250,7 +250,7 @@ void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FNam
while (v9 < PickedPackage->GetCount()) while (v9 < PickedPackage->GetCount())
{ {
int LootPackageCategoryToUseForLPCall = 0; // hmm int LootPackageCategoryToUseForLPCall = 0; // hmm
PickLootDropsFromLootPackage(LPTables, PickLootDropsFromLootPackage(LPTables,
PickedPackage->GetLootPackageCall().Data.Data ? UKismetStringLibrary::Conv_StringToName(PickedPackage->GetLootPackageCall()) : FName(0), PickedPackage->GetLootPackageCall().Data.Data ? UKismetStringLibrary::Conv_StringToName(PickedPackage->GetLootPackageCall()) : FName(0),
OutEntries, LootPackageCategoryToUseForLPCall, WorldLevel, bPrint OutEntries, LootPackageCategoryToUseForLPCall, WorldLevel, bPrint
@@ -270,7 +270,7 @@ void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FNam
LOG_INFO(LogLoot, "Loot Package {} does not contain a LootPackageCall or ItemDefinition.", PickedPackage->GetLootPackageID().ToString()); LOG_INFO(LogLoot, "Loot Package {} does not contain a LootPackageCall or ItemDefinition.", PickedPackage->GetLootPackageID().ToString());
return; return;
} }
auto WeaponItemDefinition = Cast<UFortWeaponItemDefinition>(ItemDefinition); auto WeaponItemDefinition = Cast<UFortWeaponItemDefinition>(ItemDefinition);
int LoadedAmmo = WeaponItemDefinition ? WeaponItemDefinition->GetClipSize() : 0; // we shouldnt set loaded ammo here techinally int LoadedAmmo = WeaponItemDefinition ? WeaponItemDefinition->GetClipSize() : 0; // we shouldnt set loaded ammo here techinally
@@ -332,14 +332,14 @@ void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FNam
if (Engine_Version >= 424) if (Engine_Version >= 424)
{ {
/* /*
Alright, so Fortnite literally doesn't reference the first loot package category for chests and floor loot (didnt check rest). 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. 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. 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. 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? bool IsWeapon = PickedPackage->GetLootPackageID().ToString().contains(".Weapon.") && WeaponItemDefinition; // ONG?
@@ -366,6 +366,8 @@ void PickLootDropsFromLootPackage(const std::vector<UDataTable*>& LPTables, FNam
} }
} }
// #define brudda
std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int ForcedLootTier, bool bPrint, int recursive, bool bCombineDrops) std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int ForcedLootTier, bool bPrint, int recursive, bool bCombineDrops)
{ {
std::vector<LootDrop> LootDrops; std::vector<LootDrop> LootDrops;
@@ -509,6 +511,23 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int For
if (ptr) 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); LPTables.push_back(ptr);
} }
} }
@@ -552,7 +571,7 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int For
auto ParentTables = ptr->GetPtr<TArray<UDataTable*>>(ParentTablesOffset); auto ParentTables = ptr->GetPtr<TArray<UDataTable*>>(ParentTablesOffset);
for (int z = 0; z < ParentTables->size(); ++z) for (int z = 0; z < ParentTables->size(); z++)
{ {
auto ParentTable = ParentTables->At(z); auto ParentTable = ParentTables->At(z);
@@ -598,7 +617,7 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int For
Table->AddToRoot(); Table->AddToRoot();
LOG_INFO(LogDev, "[{}] LP {}", i, Table->GetFullName()); LOG_INFO(LogDev, "[{}] LP {}", i, Table->GetFullName());
} }
} }
if (!Addresses::LoadAsset) if (!Addresses::LoadAsset)
@@ -649,13 +668,13 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int For
} }
} }
if (LTDTables.size() <= 0 || LPTables.size() <= 0) if (LTDTables.size() <= 0 || LPTables.size() <= 0)
{ {
LOG_WARN(LogLoot, "Empty tables! ({} {})", LTDTables.size(), LPTables.size()); LOG_WARN(LogLoot, "Empty tables! ({} {})", LTDTables.size(), LPTables.size());
return LootDrops; return LootDrops;
} }
FName LootTierRowName; FName LootTierRowName;
auto ChosenRowLootTierData = PickLootTierData(LTDTables, TierGroupName, ForcedLootTier, &LootTierRowName); auto ChosenRowLootTierData = PickLootTierData(LTDTables, TierGroupName, ForcedLootTier, &LootTierRowName);
@@ -685,11 +704,11 @@ std::vector<LootDrop> PickLootDrops(FName TierGroupName, int WorldLevel, int For
} }
else else
{ {
NumberLootDrops = (int)(float)((float)(NumLootPackageDrops + NumLootPackageDrops) - 0.5) >> 1; NumberLootDrops = (int)(float)((float)(NumLootPackageDrops + NumLootPackageDrops) - 0.5f) >> 1;
float v20 = NumLootPackageDrops - NumberLootDrops; float v20 = NumLootPackageDrops - NumberLootDrops;
if (v20 > 0.0000099999997) if (v20 > 0.0000099999997f)
{ {
NumberLootDrops += v20 >= (rand() * 0.000030518509); NumberLootDrops += v20 >= (rand() * 0.000030518509f);
} }
} }
} }

View File

@@ -141,10 +141,10 @@ static inline float RandomFloatForLoot(float AllWeightsSum)
} }
template <typename KeyType, typename ValueType> template <typename KeyType, typename ValueType>
FORCEINLINE static ValueType PickWeightedElement(const std::map<KeyType, ValueType>& Elements, FORCEINLINE static ValueType PickWeightedElement(const std::map<KeyType, ValueType>& Elements,
std::function<float(ValueType)> GetWeightFn, std::function<float(ValueType)> GetWeightFn,
std::function<float(float)> RandomFloatGenerator = RandomFloatForLoot, std::function<float(float)> RandomFloatGenerator = RandomFloatForLoot,
float TotalWeightParam = -1, bool bCheckIfWeightIsZero = false, int RandMultiplier = 1, KeyType* OutName = nullptr, bool bPrint = false, bool bKeepGoingUntilWeGetValue = false, float AHHH = 1) float TotalWeightParam = -1, bool bCheckIfWeightIsZero = false, int RandMultiplier = 1, KeyType* OutName = nullptr, bool bPrint = false, bool bKeepGoingUntilWeGetValue = false)
{ {
float TotalWeight = TotalWeightParam; float TotalWeight = TotalWeightParam;
@@ -158,14 +158,15 @@ FORCEINLINE static ValueType PickWeightedElement(const std::map<KeyType, ValueTy
// if (Weight != 0) // if (Weight != 0)
{ {
LOG_INFO(LogLoot, "Adding weight {}", Weight); LOG_INFO(LogLoot, "Adding weight {}", Weight);
} }
} }
return acc + Weight; return acc + Weight;
}); });
} }
float RandomNumber = RandMultiplier * RandomFloatGenerator(AHHH == -1 ? TotalWeight : AHHH); float RandomNumber = // UKismetMathLibrary::RandomFloatInRange(0, TotalWeight);
RandMultiplier * RandomFloatGenerator(TotalWeight);
if (bPrint) if (bPrint)
{ {

View File

@@ -37,7 +37,9 @@ void AFortPlayerController::ClientReportDamagedResourceBuilding(ABuildingSMActor
void AFortPlayerController::ClientEquipItem(const FGuid& ItemGuid, bool bForceExecution) void AFortPlayerController::ClientEquipItem(const FGuid& ItemGuid, bool bForceExecution)
{ {
static auto ClientEquipItemFn = FindObject<UFunction>("/Script/FortniteGame.FortPlayerControllerAthena.ClientEquipItem") ? FindObject<UFunction>("/Script/FortniteGame.FortPlayerControllerAthena.ClientEquipItem") : FindObject<UFunction>("/Script/FortniteGame.FortPlayerController.ClientEquipItem"); static auto ClientEquipItemFn = FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerAthena.ClientEquipItem")
? FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerAthena.ClientEquipItem")
: FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerController.ClientEquipItem");
if (ClientEquipItemFn) if (ClientEquipItemFn)
{ {
@@ -920,7 +922,9 @@ AActor* AFortPlayerController::SpawnToyInstanceHook(UObject* Context, FFrame* St
if (!ToyClass) if (!ToyClass)
return nullptr; return nullptr;
auto NewToy = GetWorld()->SpawnActor<AActor>(ToyClass, SpawnPosition, CreateSpawnParameters(ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn, false, PlayerController)); auto Params = CreateSpawnParameters(ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn, false, PlayerController);
auto NewToy = GetWorld()->SpawnActor<AActor>(ToyClass, SpawnPosition, Params);
// free(Params); // ?
static auto ActiveToyInstancesOffset = PlayerController->GetOffset("ActiveToyInstances"); static auto ActiveToyInstancesOffset = PlayerController->GetOffset("ActiveToyInstances");
auto& ActiveToyInstances = PlayerController->Get<TArray<AActor*>>(ActiveToyInstancesOffset); auto& ActiveToyInstances = PlayerController->Get<TArray<AActor*>>(ActiveToyInstancesOffset);
@@ -993,7 +997,9 @@ void AFortPlayerController::ServerAttemptInventoryDropHook(AFortPlayerController
static auto DropBehaviorOffset = ItemDefinition->GetOffset("DropBehavior", false); static auto DropBehaviorOffset = ItemDefinition->GetOffset("DropBehavior", false);
if (!ItemDefinition->ShouldIgnoreRespawningOnDrop() && (DropBehaviorOffset != -1 ? ItemDefinition->GetDropBehavior() != EWorldItemDropBehavior::DestroyOnDrop : true)) EWorldItemDropBehavior DropBehavior = DropBehaviorOffset != -1 ? ItemDefinition->GetDropBehavior() : EWorldItemDropBehavior::EWorldItemDropBehavior_MAX;
if (!ItemDefinition->ShouldIgnoreRespawningOnDrop() && DropBehavior != EWorldItemDropBehavior::DestroyOnDrop)
{ {
PickupCreateData CreateData; PickupCreateData CreateData;
CreateData.ItemEntry = ReplicatedEntry; CreateData.ItemEntry = ReplicatedEntry;
@@ -1013,7 +1019,7 @@ void AFortPlayerController::ServerAttemptInventoryDropHook(AFortPlayerController
bool bShouldUpdate = false; bool bShouldUpdate = false;
if (!WorldInventory->RemoveItem(ItemGuid, &bShouldUpdate, Count, true)) if (!WorldInventory->RemoveItem(ItemGuid, &bShouldUpdate, Count, true, DropBehavior == EWorldItemDropBehavior::DropAsPickupDestroyOnEmpty))
return; return;
if (bShouldUpdate) if (bShouldUpdate)
@@ -1171,28 +1177,23 @@ uint8 ToDeathCause(const FGameplayTagContainer& TagContainer, bool bWasDBNO = fa
return sub_7FF7AB499410(TagContainer, bWasDBNO); return sub_7FF7AB499410(TagContainer, bWasDBNO);
} }
std::vector<APlayerController*> PlayerControllersDead; // make atomic? DWORD WINAPI SpectateThread(LPVOID PC)
// std::array<std::atomic<APlayerController*>, 100> PlayerControllersDead;
std::atomic<int> numValidElements(0);
DWORD WINAPI SpectateThread(LPVOID)
{ {
while (1) auto PlayerController = (UObject*)PC;
{
for (auto PC : PlayerControllersDead)
// for (int i = 0; i < PlayerControllersDead.size(); ++i)
{
// auto PC = PlayerControllersDead.at(i).load();
static auto SpectateOnDeathFn = FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerZone.SpectateOnDeath") ? if (!PlayerController->IsValidLowLevel())
FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerZone.SpectateOnDeath") : return 0;
FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerAthena.SpectateOnDeath");
PC->ProcessEvent(SpectateOnDeathFn); auto SpectatingPC = Cast<AFortPlayerControllerAthena>(PlayerController);
}
Sleep(4000); if (!SpectatingPC)
} return 0;
Sleep(3000);
LOG_INFO(LogDev, "bugha!");
SpectatingPC->SpectateOnDeath();
return 0; return 0;
} }
@@ -1434,110 +1435,98 @@ void AFortPlayerController::ClientOnPawnDiedHook(AFortPlayerController* PlayerCo
LOG_INFO(LogDev, "PlayersLeft: {} IsDBNO: {}", GameState->GetPlayersLeft(), DeadPawn->IsDBNO()); LOG_INFO(LogDev, "PlayersLeft: {} IsDBNO: {}", GameState->GetPlayersLeft(), DeadPawn->IsDBNO());
if (bHandleDeath && !DeadPawn->IsDBNO()) if (!DeadPawn->IsDBNO())
{ {
if (Fortnite_Version > 1.8 || Fortnite_Version == 1.11) if (bHandleDeath)
{ {
static void (*RemoveFromAlivePlayers)(AFortGameModeAthena * GameMode, AFortPlayerController * PlayerController, APlayerState * PlayerState, APawn * FinisherPawn, if (Fortnite_Version > 1.8 || Fortnite_Version == 1.11)
UFortWeaponItemDefinition * FinishingWeapon, uint8_t DeathCause, char a7)
= decltype(RemoveFromAlivePlayers)(Addresses::RemoveFromAlivePlayers);
AActor* DamageCauser = *(AActor**)(__int64(DeathReport) + MemberOffsets::DeathReport::DamageCauser);
UFortWeaponItemDefinition* KillerWeaponDef = nullptr;
static auto FortProjectileBaseClass = FindObject<UClass>(L"/Script/FortniteGame.FortProjectileBase");
if (DamageCauser)
{ {
if (DamageCauser->IsA(FortProjectileBaseClass)) static void (*RemoveFromAlivePlayers)(AFortGameModeAthena * GameMode, AFortPlayerController * PlayerController, APlayerState * PlayerState, APawn * FinisherPawn,
UFortWeaponItemDefinition * FinishingWeapon, uint8_t DeathCause, char a7)
= decltype(RemoveFromAlivePlayers)(Addresses::RemoveFromAlivePlayers);
AActor* DamageCauser = *(AActor**)(__int64(DeathReport) + MemberOffsets::DeathReport::DamageCauser);
UFortWeaponItemDefinition* KillerWeaponDef = nullptr;
static auto FortProjectileBaseClass = FindObject<UClass>(L"/Script/FortniteGame.FortProjectileBase");
if (DamageCauser)
{ {
auto Owner = Cast<AFortWeapon>(DamageCauser->GetOwner()); if (DamageCauser->IsA(FortProjectileBaseClass))
KillerWeaponDef = Owner->IsValidLowLevel() ? Owner->GetWeaponData() : nullptr; // I just added the IsValidLowLevel check because what if the weapon destroys?
}
if (auto Weapon = Cast<AFortWeapon>(DamageCauser))
{
KillerWeaponDef = Weapon->GetWeaponData();
}
}
RemoveFromAlivePlayers(GameMode, PlayerController, KillerPlayerState == DeadPlayerState ? nullptr : KillerPlayerState, KillerPawn, KillerWeaponDef, DeathCause, 0);
/*
STATS:
Note: This isn't the exact order relative to other functions.
ClientSendMatchStatsForPlayer
ClientSendTeamStatsForPlayer
ClientSendEndBattleRoyaleMatchForPlayer
*/
// FAthenaMatchStats.Stats[ERewardSource] // hmm
/*
// We need to check if their entire team is dead then I think we send it????
auto DeadControllerAthena = Cast<AFortPlayerControllerAthena>(PlayerController);
if (DeadControllerAthena && FAthenaMatchTeamStats::GetStruct())
{
auto MatchReport = DeadControllerAthena->GetMatchReport();
LOG_INFO(LogDev, "MatchReport: {}", __int64(MatchReport));
if (MatchReport)
{
MatchReport->GetTeamStats()->GetPlace() = DeadPlayerState->GetPlace();
MatchReport->GetTeamStats()->GetTotalPlayers() = AmountOfPlayersWhenBusStart; // hmm
MatchReport->HasTeamStats() = true;
DeadControllerAthena->ClientSendTeamStatsForPlayer(MatchReport->GetTeamStats());
}
}
*/
LOG_INFO(LogDev, "Removed!");
if (Fortnite_Version < 6) // Spectating
{
static auto bAllowSpectateAfterDeathOffset = GameMode->GetOffset("bAllowSpectateAfterDeath");
bool bAllowSpectate = false; // GameMode->Get<bool>(bAllowSpectateAfterDeathOffset);
LOG_INFO(LogDev, "bAllowSpectate: {}", bAllowSpectate);
if (bAllowSpectate)
{
LOG_INFO(LogDev, "Starting Spectating!");
static auto PlayerToSpectateOnDeathOffset = PlayerController->GetOffset("PlayerToSpectateOnDeath");
PlayerController->Get<APawn*>(PlayerToSpectateOnDeathOffset) = KillerPawn;
PlayerControllersDead.push_back(PlayerController);
/* if (numValidElements < PlayerControllersDead.size())
{ {
PlayerControllersDead[numValidElements].store(PlayerController); auto Owner = Cast<AFortWeapon>(DamageCauser->GetOwner());
numValidElements.fetch_add(1); KillerWeaponDef = Owner->IsValidLowLevel() ? Owner->GetWeaponData() : nullptr; // I just added the IsValidLowLevel check because what if the weapon destroys (idk)?
} */ }
if (auto Weapon = Cast<AFortWeapon>(DamageCauser))
static bool bCreatedThread = false;
if (!bCreatedThread)
{ {
bCreatedThread = true; KillerWeaponDef = Weapon->GetWeaponData();
CreateThread(0, 0, SpectateThread, 0, 0, 0);
} }
} }
RemoveFromAlivePlayers(GameMode, PlayerController, KillerPlayerState == DeadPlayerState ? nullptr : KillerPlayerState, KillerPawn, KillerWeaponDef, DeathCause, 0);
/*
STATS:
Note: This isn't the exact order relative to other functions.
ClientSendMatchStatsForPlayer
ClientSendTeamStatsForPlayer
ClientSendEndBattleRoyaleMatchForPlayer
*/
// FAthenaMatchStats.Stats[ERewardSource] // hmm
/*
// We need to check if their entire team is dead then I think we send it????
auto DeadControllerAthena = Cast<AFortPlayerControllerAthena>(PlayerController);
if (DeadControllerAthena && FAthenaMatchTeamStats::GetStruct())
{
auto MatchReport = DeadControllerAthena->GetMatchReport();
LOG_INFO(LogDev, "MatchReport: {}", __int64(MatchReport));
if (MatchReport)
{
MatchReport->GetTeamStats()->GetPlace() = DeadPlayerState->GetPlace();
MatchReport->GetTeamStats()->GetTotalPlayers() = AmountOfPlayersWhenBusStart; // hmm
MatchReport->HasTeamStats() = true;
DeadControllerAthena->ClientSendTeamStatsForPlayer(MatchReport->GetTeamStats());
}
}
*/
LOG_INFO(LogDev, "Removed!");
} }
// LOG_INFO(LogDev, "KillerPlayerState->Place: {}", KillerPlayerState ? KillerPlayerState->GetPlace() : -1);
} }
// LOG_INFO(LogDev, "KillerPlayerState->Place: {}", KillerPlayerState ? KillerPlayerState->GetPlace() : -1); if (Fortnite_Version < 6) // Spectating (is this the actual build or is it like 6.10 when they added it auto).
{
static auto bAllowSpectateAfterDeathOffset = GameMode->GetOffset("bAllowSpectateAfterDeath");
bool bAllowSpectate = GameMode->Get<bool>(bAllowSpectateAfterDeathOffset);
LOG_INFO(LogDev, "bAllowSpectate: {}", bAllowSpectate);
if (bAllowSpectate)
{
LOG_INFO(LogDev, "Starting Spectating!");
static auto PlayerToSpectateOnDeathOffset = PlayerController->GetOffset("PlayerToSpectateOnDeath");
PlayerController->Get<APawn*>(PlayerToSpectateOnDeathOffset) = KillerPawn;
CreateThread(0, 0, SpectateThread, (LPVOID)PlayerController, 0, 0);
}
}
} }
if (IsRestartingSupported() && Globals::bAutoRestart && !bIsInAutoRestart) if (IsRestartingSupported() && Globals::bAutoRestart && !bIsInAutoRestart)

View File

@@ -141,6 +141,15 @@ public:
static inline void (*StartGhostModeOriginal)(UObject* Context, FFrame* Stack, void* Ret); static inline void (*StartGhostModeOriginal)(UObject* Context, FFrame* Stack, void* Ret);
static inline void (*EndGhostModeOriginal)(AFortPlayerControllerAthena* PlayerController); static inline void (*EndGhostModeOriginal)(AFortPlayerControllerAthena* PlayerController);
void SpectateOnDeath() // actually in zone
{
static auto SpectateOnDeathFn = FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerZone.SpectateOnDeath") ?
FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerZone.SpectateOnDeath") :
FindObject<UFunction>(L"/Script/FortniteGame.FortPlayerControllerAthena.SpectateOnDeath");
this->ProcessEvent(SpectateOnDeathFn);
}
class UAthenaResurrectionComponent*& GetResurrectionComponent() class UAthenaResurrectionComponent*& GetResurrectionComponent()
{ {
static auto ResurrectionComponentOffset = GetOffset("ResurrectionComponent"); static auto ResurrectionComponentOffset = GetOffset("ResurrectionComponent");

View File

@@ -312,6 +312,9 @@ void Addresses::FindAll()
LOG_INFO(LogDev, "Finding AddToAlivePlayers"); LOG_INFO(LogDev, "Finding AddToAlivePlayers");
Addresses::AddToAlivePlayers = FindAddToAlivePlayers(); Addresses::AddToAlivePlayers = FindAddToAlivePlayers();
LOG_INFO(LogDev, "Finding StartAircraftPhase");
Addresses::StartAircraftPhase = FindStartAircraftPhase();
// LOG_INFO(LogDev, "Finding GetSessionInterface"); // LOG_INFO(LogDev, "Finding GetSessionInterface");
// Addresses::GetSessionInterface = FindGetSessionInterface(); // Addresses::GetSessionInterface = FindGetSessionInterface();
@@ -392,6 +395,7 @@ void Addresses::Print()
LOG_INFO(LogDev, "FinishResurrection: 0x{:x}", FinishResurrection - Base); LOG_INFO(LogDev, "FinishResurrection: 0x{:x}", FinishResurrection - Base);
LOG_INFO(LogDev, "AddToAlivePlayers: 0x{:x}", AddToAlivePlayers - Base); LOG_INFO(LogDev, "AddToAlivePlayers: 0x{:x}", AddToAlivePlayers - Base);
LOG_INFO(LogDev, "GetSessionInterface: 0x{:x}", GetSessionInterface - Base); LOG_INFO(LogDev, "GetSessionInterface: 0x{:x}", GetSessionInterface - Base);
LOG_INFO(LogDev, "StartAircraftPhase: 0x{:x}", StartAircraftPhase - Base);
} }
void Offsets::FindAll() void Offsets::FindAll()

View File

@@ -76,7 +76,8 @@ namespace Addresses
extern inline uint64 AddToAlivePlayers = 0; extern inline uint64 AddToAlivePlayers = 0;
extern inline uint64 GameSessionPatch = 0; extern inline uint64 GameSessionPatch = 0;
extern inline uint64 GetSessionInterface = 0; // Matchmaking extern inline uint64 GetSessionInterface = 0; // Matchmaking
extern inline uint64 StartAircraftPhase = 0;
void SetupVersion(); // Finds Engine Version void SetupVersion(); // Finds Engine Version
void FindAll(); void FindAll();
void Print(); void Print();

View File

@@ -3,6 +3,47 @@
#include "reboot.h" #include "reboot.h"
#include "FortPlayerControllerAthena.h" #include "FortPlayerControllerAthena.h"
uint64 FindStartAircraftPhase()
{
if (Engine_Version < 427) // they scuf it
{
auto strRef = Memcury::Scanner::FindStringRef(L"STARTAIRCRAFT").Get();
if (!strRef)
return 0;
int NumCalls = 0;
for (int i = 0; i < 150; i++)
{
if (*(uint8_t*)(strRef + i) == 0xE8)
{
LOG_INFO(LogDev, "Found call 0x{:x}", __int64(strRef + i) - __int64(GetModuleHandleW(0)));
NumCalls++;
if (NumCalls == 2) // First is the str compare ig
{
return Memcury::Scanner(strRef + i).RelativeOffset(1).Get();
}
}
}
}
else
{
auto StatAddress = Memcury::Scanner::FindStringRef(L"STAT_StartAircraftPhase").Get();
for (int i = 0; i < 1000; i++)
{
if (*(uint8_t*)(uint8_t*)(StatAddress - i) == 0x48 && *(uint8_t*)(uint8_t*)(StatAddress - i + 1) == 0x8B && *(uint8_t*)(uint8_t*)(StatAddress - i + 2) == 0xC4)
{
return StatAddress - i;
}
}
}
return 0;
}
uint64 FindGetSessionInterface() uint64 FindGetSessionInterface()
{ {
auto strRef = Memcury::Scanner::FindStringRef(L"OnDestroyReservedSessionComplete %s bSuccess: %d", true, 0, Fortnite_Version >= 19).Get(); auto strRef = Memcury::Scanner::FindStringRef(L"OnDestroyReservedSessionComplete %s bSuccess: %d", true, 0, Fortnite_Version >= 19).Get();

View File

@@ -495,6 +495,7 @@ static inline uint64 FindGetMaxTickRate() // UEngine::getmaxtickrate
// return FindBytes(stringRef, Fortnite_Version <= 4.1 ? std::vector<uint8_t>{ 0x40, 0x53 } : std::vector<uint8_t>{ 0x48, 0x89, 0x5C }, 1000, 0, true); // return FindBytes(stringRef, Fortnite_Version <= 4.1 ? std::vector<uint8_t>{ 0x40, 0x53 } : std::vector<uint8_t>{ 0x48, 0x89, 0x5C }, 1000, 0, true);
} }
uint64 FindStartAircraftPhase();
uint64 FindGetSessionInterface(); uint64 FindGetSessionInterface();
uint64 FindGetPlayerViewpoint(); uint64 FindGetPlayerViewpoint();
uint64 ApplyGameSessionPatch(); uint64 ApplyGameSessionPatch();

View File

@@ -104,7 +104,7 @@ static inline LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM
static inline void SetIsLategame(bool Value) static inline void SetIsLategame(bool Value)
{ {
Globals::bLateGame.store(Value); Globals::bLateGame.store(Value);
StartingShield = 100; StartingShield = Value ? 100 : 0;
} }
static inline void Restart() // todo move? static inline void Restart() // todo move?
@@ -528,7 +528,7 @@ static inline DWORD WINAPI LateGameThread(LPVOID)
return Aircrafts; return Aircrafts;
}; };
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"startaircraft", nullptr); GameMode->StartAircraftPhase();
while (GetAircrafts().size() <= 0) while (GetAircrafts().size() <= 0)
{ {
@@ -795,7 +795,7 @@ static inline void MainUI()
} }
else else
{ {
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"startaircraft", nullptr); GameMode->StartAircraftPhase();
} }
} }
} }
@@ -1086,9 +1086,10 @@ static inline void MainUI()
{ {
auto CurrentBuildingSMActor = (ABuildingSMActor*)AllBuildingSMActors.at(i); auto CurrentBuildingSMActor = (ABuildingSMActor*)AllBuildingSMActors.at(i);
if (!CurrentBuildingSMActor->IsPlayerPlaced()) continue; if (CurrentBuildingSMActor->IsDestroyed() || CurrentBuildingSMActor->IsActorBeingDestroyed() || !CurrentBuildingSMActor->IsPlayerPlaced()) continue;
CurrentBuildingSMActor->K2_DestroyActor(); CurrentBuildingSMActor->SilentDie();
// CurrentBuildingSMActor->K2_DestroyActor();
} }
AllBuildingSMActors.Free(); AllBuildingSMActors.Free();
@@ -1137,9 +1138,12 @@ static inline void MainUI()
static auto DefaultGliderRedeployCanRedeployOffset = FindOffsetStruct("/Script/FortniteGame.FortGameStateAthena", "DefaultGliderRedeployCanRedeploy", false); static auto DefaultGliderRedeployCanRedeployOffset = FindOffsetStruct("/Script/FortniteGame.FortGameStateAthena", "DefaultGliderRedeployCanRedeploy", false);
static auto DefaultParachuteDeployTraceForGroundDistanceOffset = GameState->GetOffset("DefaultParachuteDeployTraceForGroundDistance", false); static auto DefaultParachuteDeployTraceForGroundDistanceOffset = GameState->GetOffset("DefaultParachuteDeployTraceForGroundDistance", false);
if (DefaultParachuteDeployTraceForGroundDistanceOffset != -1) if (Globals::bStartedListening) // it resets accordingly to ProHenis b4 this
{ {
ImGui::InputFloat("Automatic Parachute Pullout Distance", GameState->GetPtr<float>(DefaultParachuteDeployTraceForGroundDistanceOffset)); if (DefaultParachuteDeployTraceForGroundDistanceOffset != -1)
{
ImGui::InputFloat("Automatic Parachute Pullout Distance", GameState->GetPtr<float>(DefaultParachuteDeployTraceForGroundDistanceOffset));
}
} }
if (DefaultGliderRedeployCanRedeployOffset != -1) if (DefaultGliderRedeployCanRedeployOffset != -1)

View File

@@ -20,7 +20,7 @@ extern inline int Engine_Version = 0; // For example, 420, 421, etc. // Prevent
extern inline double Fortnite_Version = 0; // For example, 4.1, 6.21, etc. // Prevent using this when possible. extern inline double Fortnite_Version = 0; // For example, 4.1, 6.21, etc. // Prevent using this when possible.
extern inline int Fortnite_CL = 0; extern inline int Fortnite_CL = 0;
#define PROD // this doesnt do anything besides remove processeventhook and some assert stuff // #define PROD // this doesnt do anything besides remove processeventhook and some assert stuff
struct PlaceholderBitfield struct PlaceholderBitfield
{ {