diff --git a/Project Reboot 3.0/BGA.h b/Project Reboot 3.0/BGA.h index 23e09f3..eb306c0 100644 --- a/Project Reboot 3.0/BGA.h +++ b/Project Reboot 3.0/BGA.h @@ -43,7 +43,7 @@ static inline void SpawnBGAs() // hahah not "proper", there's a function that we static auto SpawnLootTierGroupOffset = BGAConsumableSpawner->GetOffset("SpawnLootTierGroup"); auto& SpawnLootTierGroup = BGAConsumableSpawner->Get(SpawnLootTierGroupOffset); - auto LootDrops = PickLootDrops(SpawnLootTierGroup); + auto LootDrops = PickLootDrops(SpawnLootTierGroup, GameState->GetWorldLevel()); for (int z = 0; z < LootDrops.size(); z++) { diff --git a/Project Reboot 3.0/BuildingContainer.cpp b/Project Reboot 3.0/BuildingContainer.cpp index 53deb19..e228c97 100644 --- a/Project Reboot 3.0/BuildingContainer.cpp +++ b/Project Reboot 3.0/BuildingContainer.cpp @@ -7,6 +7,8 @@ bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn) { auto GameMode = Cast(GetWorld()->GetGameMode()); + auto GameState = Cast(GetWorld()->GetGameState()); + FVector LocationToSpawnLoot = this->GetActorLocation() + this->GetActorRightVector() * 70.f + FVector{ 0, 0, 50 }; static auto SearchLootTierGroupOffset = this->GetOffset("SearchLootTierGroup"); @@ -14,18 +16,16 @@ bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn) // LOG_INFO(LogInteraction, "RedirectedLootTier: {}", RedirectedLootTier.ToString()); - auto LootDrops = PickLootDrops(RedirectedLootTier, -1, bDebugPrintLooting); + auto LootDrops = PickLootDrops(RedirectedLootTier, GameState->GetWorldLevel(), -1, bDebugPrintLooting); // LOG_INFO(LogInteraction, "LootDrops.size(): {}", LootDrops.size()); - for (int i = 0; i < LootDrops.size(); i++) + for (auto& LootDrop : LootDrops) { - auto& lootDrop = LootDrops.at(i); - PickupCreateData CreateData{}; CreateData.bToss = true; // CreateData.PawnOwner = Pawn; - CreateData.ItemEntry = FFortItemEntry::MakeItemEntry(lootDrop->GetItemDefinition(), lootDrop->GetCount(), lootDrop->GetLoadedAmmo()); + CreateData.ItemEntry = LootDrop.ItemEntry; CreateData.SpawnLocation = LocationToSpawnLoot; CreateData.SourceType = EFortPickupSourceTypeFlag::GetContainerValue(); CreateData.bRandomRotation = true; @@ -34,5 +34,17 @@ bool ABuildingContainer::SpawnLoot(AFortPawn* Pawn) auto NewPickup = AFortPickup::SpawnPickup(CreateData); } + static auto SearchAnimationCountOffset = FindOffsetStruct("/Script/FortniteGame.FortSearchBounceData", "SearchAnimationCount"); + static auto SearchBounceDataOffset = this->GetOffset("SearchBounceData"); + + auto SearchBounceData = this->GetPtr(SearchBounceDataOffset); + + (*(int*)(__int64(SearchBounceData) + SearchAnimationCountOffset))++; + + static auto OnRep_bAlreadySearchedFn = FindObject(L"/Script/FortniteGame.BuildingContainer.OnRep_bAlreadySearched"); + this->ProcessEvent(OnRep_bAlreadySearchedFn); + + // Now there is some function called here but idk what it is, it calls OnLoot though. + return true; } \ No newline at end of file diff --git a/Project Reboot 3.0/DataTable.h b/Project Reboot 3.0/DataTable.h index 3f612e6..53ba8c6 100644 --- a/Project Reboot 3.0/DataTable.h +++ b/Project Reboot 3.0/DataTable.h @@ -1,9 +1,15 @@ #pragma once #include "Object.h" +#include "reboot.h" #include "Map.h" +struct FTableRowBase +{ + unsigned char UnknownData00[0x8]; // this is actually structural padding +}; + class UDataTable : public UObject { public: @@ -40,4 +46,63 @@ 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) + + template + void GetRows(std::vector& OutRows, const FString& ContextString) const + { + OutRows.clear(); + + if (DataTable == nullptr) + { + if (RowContents.ComparisonIndex.Value != 0) + { + // UE_LOG(LogDataTable, Warning, TEXT("FDataTableCategoryHandle::FindRow : No DataTable for row %s (%s)."), *RowContents.ToString(), *ContextString); + } + + return; + } + + if (ColumnName.ComparisonIndex.Value == 0) + { + if (RowContents.ComparisonIndex.Value != 0) + { + // UE_LOG(LogDataTable, Warning, TEXT("FDataTableCategoryHandle::FindRow : No Column selected for row %s (%s)."), *RowContents.ToString(), *ContextString); + } + + return; + } + + return; + + // unreal trippin + + /* + // Find the property that matches the desired column (ColumnName) + UProperty* Property = DataTable->FindTableProperty(ColumnName); + if (Property == nullptr) + { + return; + } + + // check each row to see if the value in the Property element is the one we're looking for (RowContents). If it is, add the row to OutRows + FString RowContentsAsString = RowContents.ToString(); + + for (auto RowIt = DataTable->RowMap.CreateConstIterator(); RowIt; ++RowIt) + { + uint8* RowData = RowIt.Value(); + + FString PropertyValue(TEXT("")); + + Property->ExportText_InContainer(0, PropertyValue, RowData, RowData, nullptr, PPF_None); + + if (RowContentsAsString == PropertyValue) + { + OutRows.Add((T*)RowData); + } + } + */ + + return; + } + }; diff --git a/Project Reboot 3.0/FortAthenaMutator_ItemDropOnDeath.h b/Project Reboot 3.0/FortAthenaMutator_ItemDropOnDeath.h index f946e33..a3a6c64 100644 --- a/Project Reboot 3.0/FortAthenaMutator_ItemDropOnDeath.h +++ b/Project Reboot 3.0/FortAthenaMutator_ItemDropOnDeath.h @@ -6,18 +6,49 @@ #include "FortWorldItemDefinition.h" #include "FortInventory.h" +enum class ERespawnRequirements : uint8_t +{ + RespawnOnly = 0, + NoRespawnOnly = 1, + Both = 2, + ERespawnRequirements_MAX = 3 +}; + struct FItemsToDropOnDeath { - UFortWorldItemDefinition* ItemToDrop; // 0x0000(0x0008) (Edit, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) - FScalableFloat NumberToDrop; // 0x0008(0x0020) (Edit, NativeAccessSpecifierPublic) + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.ItemsToDropOnDeath"); + return Struct; + } + + static int GetStructSize() { return GetStruct()->GetPropertiesSize(); } + + UFortWorldItemDefinition*& GetItemToDrop() + { + static auto ItemToDropOffset = FindOffsetStruct("/Script/FortniteGame.ItemsToDropOnDeath", "ItemToDrop"); + return *(UFortWorldItemDefinition**)(__int64(this) + ItemToDropOffset); + } + + FScalableFloat* GetNumberToDrop() + { + static auto NumberToDropOffset = FindOffsetStruct("/Script/FortniteGame.ItemsToDropOnDeath", "NumberToDrop"); + return (FScalableFloat*)(__int64(this) + NumberToDropOffset); + } }; class AFortAthenaMutator_ItemDropOnDeath : public AFortAthenaMutator { public: + ERespawnRequirements/*&*/ GetRespawnRequirements() + { + static auto RespawnRequirementsOffset = GetOffset("RespawnRequirements"); + return Get(RespawnRequirementsOffset); + } + TArray& GetItemsToDrop() { static auto ItemsToDropOffset = GetOffset("ItemsToDrop"); - return Get>(ItemsToDropoOffset); + return Get>(ItemsToDropOffset); } }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameMode.cpp b/Project Reboot 3.0/FortGameMode.cpp index 69cbf7e..cb4286e 100644 --- a/Project Reboot 3.0/FortGameMode.cpp +++ b/Project Reboot 3.0/FortGameMode.cpp @@ -1,6 +1,6 @@ #include "FortGameMode.h" -void AFortGameMode::SetCurrentPlaylistName(UObject* Playlist) // Techinally it takes in a fname +void AFortGameMode::SetCurrentPlaylistName(UFortPlaylist* Playlist) // Techinally it takes in a fname { if (!Playlist) { @@ -9,11 +9,10 @@ void AFortGameMode::SetCurrentPlaylistName(UObject* Playlist) // Techinally it t } static auto PlaylistNameOffset = Playlist->GetOffset("PlaylistName"); - static auto PlaylistIdOffset = Playlist->GetOffset("PlaylistId"); static auto CurrentPlaylistNameOffset = GetOffset("CurrentPlaylistName"); static auto CurrentPlaylistIdOffset = GetOffset("CurrentPlaylistId"); Get(CurrentPlaylistNameOffset) = Playlist->Get(PlaylistNameOffset); - Get(CurrentPlaylistIdOffset) = Playlist->Get(PlaylistIdOffset); + Get(CurrentPlaylistIdOffset) = Playlist->GetPlaylistId(); } \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameMode.h b/Project Reboot 3.0/FortGameMode.h index 6cf34b5..60739a4 100644 --- a/Project Reboot 3.0/FortGameMode.h +++ b/Project Reboot 3.0/FortGameMode.h @@ -1,9 +1,10 @@ #pragma once #include "GameMode.h" +#include "FortPlaylist.h" class AFortGameMode : public AGameMode { public: - void SetCurrentPlaylistName(UObject* Playlist); // Techinally it takes in a fname + void SetCurrentPlaylistName(UFortPlaylist* Playlist); // Techinally it takes in a fname }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 9c38f29..d3e11b8 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -161,21 +161,12 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game (*(int*)(__int64(CurrentPlaylistInfo) + PlaylistReplicationKeyOffset))++; CurrentPlaylistInfo->MarkArrayDirty(); - auto aeuh = *(UObject**)(__int64(CurrentPlaylistInfo) + BasePlaylistOffset); + auto currentBasePlaylist = *(UFortPlaylist**)(__int64(CurrentPlaylistInfo) + BasePlaylistOffset); - if (aeuh) + if (currentBasePlaylist) { - GameMode->SetCurrentPlaylistName(aeuh); - - /* if (Fortnite_Version >= 13) - { - static auto LastSafeZoneIndexOffset = aeuh->GetOffset("LastSafeZoneIndex"); - - if (LastSafeZoneIndexOffset != -1) - { - *(int*)(__int64(aeuh) + LastSafeZoneIndexOffset) = 0; - } - } */ + GameMode->SetCurrentPlaylistName(currentBasePlaylist); + GameState->SetPlaylistId(currentBasePlaylist); } } else @@ -183,7 +174,15 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game static auto CurrentPlaylistDataOffset = GameState->GetOffset("CurrentPlaylistData", false); if (CurrentPlaylistDataOffset != -1) + { GameState->Get(CurrentPlaylistDataOffset) = Playlist; + + if (GameState->Get(CurrentPlaylistDataOffset)) + { + GameMode->SetCurrentPlaylistName(GameState->Get(CurrentPlaylistDataOffset)); + GameState->SetPlaylistId(GameState->Get(CurrentPlaylistDataOffset)); + } + } } if (bOnRep) @@ -550,13 +549,23 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game LastNum3 = AmountOfRestarts; ++Globals::AmountOfListens; - LOG_INFO(LogNet, "Attempting to listen!"); + // LOG_INFO(LogNet, "Attempting to listen!"); GetWorld()->Listen(); + LOG_INFO(LogDev, "WorldLevel: {}", GameState->GetWorldLevel()); + SetupAIDirector(); SetupServerBotManager(); + bool bPrintCommonObjectPaths = true; + + if (bPrintCommonObjectPaths) + { + LOG_INFO(LogGame, "GameState PathName: {}", GetWorld()->GetGameState()->GetPathName()); + LOG_INFO(LogGame, "GameMode PathName: {}", GetWorld()->GetGameMode()->GetPathName()); + } + if (AmountOfBotsToSpawn != 0) { Bots::SpawnBotsAtPlayerStarts(AmountOfBotsToSpawn); @@ -968,13 +977,13 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena auto Location = CurrentActor->GetActorLocation(); Location.Z += UpZ; - std::vector LootDrops = PickLootDrops(SpawnIslandTierGroup, -1, bPrintWarmup); + std::vector LootDrops = PickLootDrops(SpawnIslandTierGroup, GameState->GetWorldLevel(), -1, bPrintWarmup); for (auto& LootDrop : LootDrops) { PickupCreateData CreateData; CreateData.bToss = true; - CreateData.ItemEntry = FFortItemEntry::MakeItemEntry(LootDrop->GetItemDefinition(), LootDrop->GetCount(), LootDrop->GetLoadedAmmo()); + CreateData.ItemEntry = LootDrop.ItemEntry; CreateData.SpawnLocation = Location; CreateData.SourceType = SpawnFlag; CreateData.bRandomRotation = true; @@ -998,13 +1007,13 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena auto Location = CurrentActor->GetActorLocation(); Location.Z += UpZ; - std::vector LootDrops = PickLootDrops(BRIslandTierGroup, -1, bPrint); + std::vector LootDrops = PickLootDrops(BRIslandTierGroup, GameState->GetWorldLevel(), -1, bPrint); for (auto& LootDrop : LootDrops) { PickupCreateData CreateData; CreateData.bToss = true; - CreateData.ItemEntry = FFortItemEntry::MakeItemEntry(LootDrop->GetItemDefinition(), LootDrop->GetCount(), LootDrop->GetLoadedAmmo()); + CreateData.ItemEntry = LootDrop.ItemEntry; CreateData.SpawnLocation = Location; CreateData.SourceType = SpawnFlag; CreateData.bRandomRotation = true; diff --git a/Project Reboot 3.0/FortGameModeAthena.h b/Project Reboot 3.0/FortGameModeAthena.h index a637fd7..5808a56 100644 --- a/Project Reboot 3.0/FortGameModeAthena.h +++ b/Project Reboot 3.0/FortGameModeAthena.h @@ -81,7 +81,7 @@ static void SetFoundationTransform(AActor* BuildingFoundation, const FTransform& static inline UFortAbilitySet* GetPlayerAbilitySet() { - // There are some variables that contain this but it changes through versions soo.. + // There are some variables that contain this but it changes through versions soo.. // GenericPlayerAbilitySet and PlayerAbilitySetBR static auto GameplayAbilitySet = (UFortAbilitySet*)(Fortnite_Version >= 8.30 ? LoadObject(L"/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer", UFortAbilitySet::StaticClass()) diff --git a/Project Reboot 3.0/FortGameStateAthena.cpp b/Project Reboot 3.0/FortGameStateAthena.cpp index a95b7eb..5cccd51 100644 --- a/Project Reboot 3.0/FortGameStateAthena.cpp +++ b/Project Reboot 3.0/FortGameStateAthena.cpp @@ -80,6 +80,12 @@ UFortPlaylist*& AFortGameStateAthena::GetCurrentPlaylist() return *(UFortPlaylist**)(__int64(CurrentPlaylistInfo) + BasePlaylistOffset); } +void AFortGameStateAthena::SetPlaylistId(UFortPlaylist* Playlist) +{ + static auto CurrentPlaylistIdOffset = GetOffset("CurrentPlaylistId"); + this->Get(CurrentPlaylistIdOffset) = Playlist->GetPlaylistId(); +} + int AFortGameStateAthena::GetAircraftIndex(AFortPlayerState* PlayerState) { // The function has a string in it but we can just remake lol diff --git a/Project Reboot 3.0/FortGameStateAthena.h b/Project Reboot 3.0/FortGameStateAthena.h index a20e06c..7a2e331 100644 --- a/Project Reboot 3.0/FortGameStateAthena.h +++ b/Project Reboot 3.0/FortGameStateAthena.h @@ -68,6 +68,12 @@ public: return Get(GamePhaseOffset); } + int& GetWorldLevel() // Actually in AFortGameState + { + static auto WorldLevelOffset = GetOffset("WorldLevel"); + return Get(WorldLevelOffset); + } + UBuildingStructuralSupportSystem* GetStructuralSupportSystem() // actually in FortGameModeZone { static auto StructuralSupportSystemOffset = GetOffset("StructuralSupportSystem"); @@ -99,6 +105,7 @@ public: // void AddPlayerStateToGameMemberInfo(class AFortPlayerStateAthena* PlayerState); + void SetPlaylistId(UFortPlaylist* Playlist); int GetAircraftIndex(AFortPlayerState* PlayerState); bool IsRespawningAllowed(AFortPlayerState* PlayerState); // actually in zone bool IsPlayerBuildableClass(UClass* Class); diff --git a/Project Reboot 3.0/FortItem.cpp b/Project Reboot 3.0/FortItem.cpp index c2b34fa..28a21b8 100644 --- a/Project Reboot 3.0/FortItem.cpp +++ b/Project Reboot 3.0/FortItem.cpp @@ -25,7 +25,7 @@ void FFortItemEntry::SetStateValue(EFortItemEntryState StateType, int IntValue) // ItemEntry->bIsDirty = true; } -FFortItemEntry* FFortItemEntry::MakeItemEntry(UFortItemDefinition* ItemDefinition, int Count, int LoadedAmmo, float Durability) +FFortItemEntry* FFortItemEntry::MakeItemEntry(UFortItemDefinition* ItemDefinition, int Count, int LoadedAmmo, float Durability, int Level) { auto Entry = Alloc(GetStructSize(), bUseFMemoryRealloc); @@ -40,7 +40,7 @@ FFortItemEntry* FFortItemEntry::MakeItemEntry(UFortItemDefinition* ItemDefinitio LoadedAmmo = 0; } - Entry->MostRecentArrayReplicationKey = -1; // idk if we need to set this + Entry->MostRecentArrayReplicationKey = -1; // idk if we need to set this one Entry->ReplicationID = -1; Entry->ReplicationKey = -1; @@ -50,6 +50,7 @@ FFortItemEntry* FFortItemEntry::MakeItemEntry(UFortItemDefinition* ItemDefinitio Entry->GetDurability() = Durability; Entry->GetGameplayAbilitySpecHandle() = FGameplayAbilitySpecHandle(-1); Entry->GetParentInventory().ObjectIndex = -1; + Entry->GetLevel() = Level; // We want to add StateValues.Add(DurabilityInitialized); orwnatefc erwgearf yk // CoCreateGuid((GUID*)&Entry->GetItemGuid()); // Entry->DoesUpdateStatsOnCollection() = true; // I think fortnite does this? diff --git a/Project Reboot 3.0/FortItem.h b/Project Reboot 3.0/FortItem.h index 718ea79..a4d783e 100644 --- a/Project Reboot 3.0/FortItem.h +++ b/Project Reboot 3.0/FortItem.h @@ -143,18 +143,12 @@ struct FFortItemEntry : FFastArraySerializerItem FGuid OldGuid = this->GetItemGuid(); - if (false) - { - CopyStruct(this, OtherItemEntry, FFortItemEntry::GetStructSize(), FFortItemEntry::GetStruct()); - } - else - { - this->GetItemDefinition() = OtherItemEntry->GetItemDefinition(); - this->GetCount() = OtherItemEntry->GetCount(); - this->GetLoadedAmmo() = OtherItemEntry->GetLoadedAmmo(); - this->GetItemGuid() = OtherItemEntry->GetItemGuid(); - this->GetLevel() = OtherItemEntry->GetLevel(); - } + this->GetCount() = OtherItemEntry->GetCount(); + this->GetItemDefinition() = OtherItemEntry->GetItemDefinition(); + this->GetDurability() = OtherItemEntry->GetDurability(); + this->GetLevel() = OtherItemEntry->GetLevel(); + this->GetLoadedAmmo() = OtherItemEntry->GetLoadedAmmo(); + this->GetItemGuid() = OtherItemEntry->GetItemGuid(); if (!bCopyGuid) this->GetItemGuid() = OldGuid; @@ -189,7 +183,7 @@ struct FFortItemEntry : FFastArraySerializerItem return StructSize; } - static FFortItemEntry* MakeItemEntry(UFortItemDefinition* ItemDefinition, int Count = 1, int LoadedAmmo = 0, float Durability = 0x3F800000); + static FFortItemEntry* MakeItemEntry(UFortItemDefinition* ItemDefinition, int Count = 1, int LoadedAmmo = 0, float Durability = 0x3F800000, int Level = 0); // We need to find a better way for below... Especially since we can't do either method for season 5 or 6. diff --git a/Project Reboot 3.0/FortKismetLibrary.cpp b/Project Reboot 3.0/FortKismetLibrary.cpp index 57603b3..6748da5 100644 --- a/Project Reboot 3.0/FortKismetLibrary.cpp +++ b/Project Reboot 3.0/FortKismetLibrary.cpp @@ -3,6 +3,7 @@ #include "FortPickup.h" #include "FortLootPackage.h" #include "AbilitySystemComponent.h" +#include "FortGameStateAthena.h" UFortResourceItemDefinition* UFortKismetLibrary::K2_GetResourceItemDefinition(EFortResourceType ResourceType) { @@ -601,13 +602,15 @@ bool UFortKismetLibrary::PickLootDropsHook(UObject* Context, FFrame& Stack, bool LOG_INFO(LogDev, "Picking loot for {}.", TierGroupName.ComparisonIndex.Value ? TierGroupName.ToString() : "InvalidName"); - auto LootDrops = PickLootDrops(TierGroupName, -1, true); + auto GameState = Cast(GetWorld()->GetGameState()); + + auto LootDrops = PickLootDrops(TierGroupName, GameState->GetWorldLevel(), -1, true); for (int i = 0; i < LootDrops.size(); i++) { auto& LootDrop = LootDrops.at(i); - auto NewEntry = FFortItemEntry::MakeItemEntry(LootDrop->GetItemDefinition(), LootDrop->GetCount(), LootDrop->GetLoadedAmmo()); + auto NewEntry = LootDrop.ItemEntry; OutLootToDrop.AddPtr(NewEntry, FFortItemEntry::GetStructSize()); } diff --git a/Project Reboot 3.0/FortLootLevel.cpp b/Project Reboot 3.0/FortLootLevel.cpp new file mode 100644 index 0000000..4c51c5b --- /dev/null +++ b/Project Reboot 3.0/FortLootLevel.cpp @@ -0,0 +1,71 @@ +#include "FortLootLevel.h" +#include "FortWorldItemDefinition.h" + +int UFortLootLevel::GetItemLevel(const FDataTableCategoryHandle& LootLevelData, int WorldLevel) +{ + // OMG IM GONNA DIE + + // we should use GetRows but L + + auto DataTable = LootLevelData.DataTable; + + if (!DataTable) + return 0; + + if (!LootLevelData.ColumnName.ComparisonIndex.Value) + return 0; + + if (!LootLevelData.RowContents.ComparisonIndex.Value) + return 0; + + std::vector OurLootLevelDatas; + + for (auto& LootLevelDataPair : LootLevelData.DataTable->GetRowMap()) + { + if (LootLevelDataPair.Second->Category != LootLevelData.RowContents) + continue; + + OurLootLevelDatas.push_back(LootLevelDataPair.Second); + } + + if (OurLootLevelDatas.size() > 0) + { + int PickedIndex = -1; + int PickedLootLevel = 0; + + for (int i = 0; i < OurLootLevelDatas.size(); i++) + { + auto CurrentLootLevelData = OurLootLevelDatas.at(i); + + if (CurrentLootLevelData->LootLevel <= WorldLevel && CurrentLootLevelData->LootLevel > PickedLootLevel) + { + PickedLootLevel = CurrentLootLevelData->LootLevel; + PickedIndex = i; + } + } + + if (PickedIndex != -1) + { + auto PickedLootLevelData = OurLootLevelDatas.at(PickedIndex); + + const auto PickedMinItemLevel = PickedLootLevelData->MinItemLevel; + const auto PickedMaxItemLevel = PickedLootLevelData->MaxItemLevel; + auto v15 = PickedMaxItemLevel - PickedMinItemLevel; + + if (v15 + 1 <= 0) + { + v15 = 0; + } + else + { + auto v16 = (int)(float)((float)((float)rand() * 0.000030518509) * (float)(v15 + 1)); + if (v16 <= v15) + v15 = v16; + } + + return v15 + PickedMinItemLevel; + } + } + + return 0; +} diff --git a/Project Reboot 3.0/FortLootLevel.h b/Project Reboot 3.0/FortLootLevel.h index 09a3d74..77b807e 100644 --- a/Project Reboot 3.0/FortLootLevel.h +++ b/Project Reboot 3.0/FortLootLevel.h @@ -4,8 +4,6 @@ class UFortLootLevel { - int GetItemLevel(FDataTableCategoryHandle LootLevelData, int WorldLevel) - { - return 0; - } +public: + static int GetItemLevel(const FDataTableCategoryHandle& LootLevelData, int WorldLevel); }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp index 6bb1324..432a6a9 100644 --- a/Project Reboot 3.0/FortLootPackage.cpp +++ b/Project Reboot 3.0/FortLootPackage.cpp @@ -6,6 +6,7 @@ #include "UObjectArray.h" #include "GameplayTagContainer.h" #include "FortGameModeAthena.h" +#include "FortLootLevel.h" struct FFortGameFeatureLootTableData { @@ -51,11 +52,6 @@ void CollectDataTablesRows(std::vector DataTables, std::mapGetLootPackageCategoryMinArray().Num() != LootTierData->GetLootPackageCategoryWeightArray().Num() @@ -183,7 +179,7 @@ FFortLootTierData* PickLootTierData(const std::vector& LTDTables, F return ChosenRowLootTierData; } -void PickLootDropsFromLootPackage(const std::vector& LPTables, const FName& LootPackageName, std::vector* OutEntries, int LootPackageCategory = -1, bool bPrint = false) +void PickLootDropsFromLootPackage(const std::vector& LPTables, const FName& LootPackageName, std::vector* OutEntries, int LootPackageCategory = -1, int WorldLevel = 0, bool bPrint = false) { if (!OutEntries) return; @@ -201,14 +197,16 @@ void PickLootDropsFromLootPackage(const std::vector& LPTables, cons return false; } - /* if (WorldLevel >= 0) + // todo add required tag? + + if (WorldLevel >= 0) { - if (LootPackage->MaxWorldLevel >= 0 && WorldLevel > LootPackage->MaxWorldLevel) + if (LootPackage->GetMaxWorldLevel() >= 0 && WorldLevel > LootPackage->GetMaxWorldLevel()) return 0; - if (LootPackage->MinWorldLevel >= 0 && WorldLevel < LootPackage->MinWorldLevel) + if (LootPackage->GetMinWorldLevel() >= 0 && WorldLevel < LootPackage->GetMinWorldLevel()) return 0; - } */ + } return true; }); @@ -242,7 +240,7 @@ void PickLootDropsFromLootPackage(const std::vector& LPTables, cons PickLootDropsFromLootPackage(LPTables, PickedPackage->GetLootPackageCall().Data.Data ? UKismetStringLibrary::Conv_StringToName(PickedPackage->GetLootPackageCall()) : FName(0), - OutEntries, LootPackageCategoryToUseForLPCall, bPrint + OutEntries, LootPackageCategoryToUseForLPCall, WorldLevel, bPrint ); v9++; @@ -260,15 +258,15 @@ void PickLootDropsFromLootPackage(const std::vector& LPTables, cons 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); - } + auto WorldItemDefinition = Cast(ItemDefinition); + + if (!WorldItemDefinition) // hahahah not proper!! + return; + + int ItemLevel = UFortLootLevel::GetItemLevel(WorldItemDefinition->GetLootLevelData(), WorldLevel); int CountMultiplier = 1; int FinalCount = CountMultiplier * PickedPackage->GetCount(); @@ -282,15 +280,30 @@ void PickLootDropsFromLootPackage(const std::vector& LPTables, cons while (FinalCount > 0) { - int CurrentCountForEntry = PickedPackage->GetCount(); // Idk calls some itemdefinition vfunc + int MaxStackSize = ItemDefinition->GetMaxStackSize(); - OutEntries->push_back(LootDrop(FFortItemEntry::MakeItemEntry(ItemDefinition, CurrentCountForEntry, LoadedAmmo))); + int CurrentCountForEntry = MaxStackSize; + + if (FinalCount <= MaxStackSize) + CurrentCountForEntry = FinalCount; + + if (CurrentCountForEntry <= 0) + CurrentCountForEntry = 0; + + auto ActualItemLevel = WorldItemDefinition->PickLevel(FinalItemLevel); + + OutEntries->push_back(LootDrop(FFortItemEntry::MakeItemEntry(ItemDefinition, CurrentCountForEntry, LoadedAmmo, 0x3F800000, ActualItemLevel))); + + /* if (bPrint) + { + LOG_INFO(LogDev, "ActualItemLevel: {} FinalItemLevel: {} ItemLevel: {}", ActualItemLevel, FinalItemLevel, ItemLevel); + } */ 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 on chapter two and above (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. @@ -306,7 +319,7 @@ void PickLootDropsFromLootPackage(const std::vector& LPTables, cons if (AmmoData) { - int AmmoCount = AmmoData->GetDropCount(); // idk about this one + int AmmoCount = AmmoData->GetDropCount(); // uhh??? OutEntries->push_back(LootDrop(FFortItemEntry::MakeItemEntry(AmmoData, AmmoCount))); } @@ -323,7 +336,7 @@ void PickLootDropsFromLootPackage(const std::vector& LPTables, cons } } -std::vector PickLootDrops(FName TierGroupName, int ForcedLootTier, bool bPrint, int recursive) +std::vector PickLootDrops(FName TierGroupName, int WorldLevel, int ForcedLootTier, bool bPrint, int recursive) { std::vector LootDrops; @@ -659,7 +672,7 @@ std::vector PickLootDrops(FName TierGroupName, int ForcedLootTier, boo int LootPackageCategory = i; - PickLootDropsFromLootPackage(LPTables, ChosenRowLootTierData->GetLootPackage(), &LootDrops, LootPackageCategory, bPrint); + PickLootDropsFromLootPackage(LPTables, ChosenRowLootTierData->GetLootPackage(), &LootDrops, LootPackageCategory, WorldLevel, bPrint); } } } diff --git a/Project Reboot 3.0/FortLootPackage.h b/Project Reboot 3.0/FortLootPackage.h index 6e0d8df..4bc8e9a 100644 --- a/Project Reboot 3.0/FortLootPackage.h +++ b/Project Reboot 3.0/FortLootPackage.h @@ -45,6 +45,18 @@ public: return *(int*)(__int64(this) + CountOffset); } + int& GetMinWorldLevel() + { + static auto MinWorldLevelOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "MinWorldLevel"); + return *(int*)(__int64(this) + MinWorldLevelOffset); + } + + int& GetMaxWorldLevel() + { + static auto MaxWorldLevelOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "MaxWorldLevel"); + return *(int*)(__int64(this) + MaxWorldLevelOffset); + } + int& GetLootPackageCategory() { static auto LootPackageCategoryOffset = FindOffsetStruct("/Script/FortniteGame.FortLootPackageData", "LootPackageCategory"); @@ -183,4 +195,4 @@ FORCEINLINE static ValueType PickWeightedElement(const std::map PickLootDrops(FName TierGroupName, int ForcedLootTier = -1, bool bPrint = false, int recursive = 0); \ No newline at end of file +std::vector PickLootDrops(FName TierGroupName, int WorldLevel, int ForcedLootTier = -1, 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 d20aa05..1f3b98b 100644 --- a/Project Reboot 3.0/FortPickup.cpp +++ b/Project Reboot 3.0/FortPickup.cpp @@ -34,7 +34,7 @@ AFortPickup* AFortPickup::SpawnPickup(PickupCreateData& PickupData) if (PickupData.Source == -1) PickupData.Source = 0; if (PickupData.SourceType == -1) - PickupData.SourceType = -1; + PickupData.SourceType = 0; /* if (PickupData.bToss) { diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 2650e4b..e307bd7 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -430,15 +430,11 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* FVector LocationToSpawnLoot = ReceivingActor->GetActorLocation() + ReceivingActor->GetActorRightVector() * 70.f + FVector{ 0, 0, 50 }; static auto FortAthenaVehicleClass = FindObject(L"/Script/FortniteGame.FortAthenaVehicle"); - static auto SearchAnimationCountOffset = FindOffsetStruct("/Script/FortniteGame.FortSearchBounceData", "SearchAnimationCount"); if (auto BuildingContainer = Cast(ReceivingActor)) { static auto bAlreadySearchedOffset = BuildingContainer->GetOffset("bAlreadySearched"); - static auto SearchBounceDataOffset = BuildingContainer->GetOffset("SearchBounceData"); static auto bAlreadySearchedFieldMask = GetFieldMask(BuildingContainer->GetProperty("bAlreadySearched")); - - auto SearchBounceData = BuildingContainer->GetPtr(SearchBounceDataOffset); if (BuildingContainer->ReadBitfieldValue(bAlreadySearchedOffset, bAlreadySearchedFieldMask)) return; @@ -446,10 +442,6 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* // LOG_INFO(LogInteraction, "bAlreadySearchedFieldMask: {}", bAlreadySearchedFieldMask); BuildingContainer->SetBitfieldValue(bAlreadySearchedOffset, bAlreadySearchedFieldMask, true); - (*(int*)(__int64(SearchBounceData) + SearchAnimationCountOffset))++; - - static auto OnRep_bAlreadySearchedFn = FindObject(L"/Script/FortniteGame.BuildingContainer.OnRep_bAlreadySearched"); - BuildingContainer->ProcessEvent(OnRep_bAlreadySearchedFn); BuildingContainer->SpawnLoot(PlayerController->GetMyFortPawn()); diff --git a/Project Reboot 3.0/FortPlayerPawn.cpp b/Project Reboot 3.0/FortPlayerPawn.cpp index 3f8c1ed..a3e42a8 100644 --- a/Project Reboot 3.0/FortPlayerPawn.cpp +++ b/Project Reboot 3.0/FortPlayerPawn.cpp @@ -259,7 +259,7 @@ void AFortPlayerPawn::ServerHandlePickupHook(AFortPlayerPawn* Pawn, AFortPickup* static auto bPickedUpOffset = Pickup->GetOffset("bPickedUp"); - LOG_INFO(LogDev, "InFlyTime: {}", InFlyTime); + // LOG_INFO(LogDev, "InFlyTime: {}", InFlyTime); if (Pickup->Get(bPickedUpOffset)) { diff --git a/Project Reboot 3.0/FortPlaylist.h b/Project Reboot 3.0/FortPlaylist.h index 55f05aa..2b09c1b 100644 --- a/Project Reboot 3.0/FortPlaylist.h +++ b/Project Reboot 3.0/FortPlaylist.h @@ -9,6 +9,7 @@ #include "BuildingActor.h" #include "FortPlayerPawnAthena.h" #include "GameplayAbilityTypes.h" +#include "FortPlayerState.h" struct FGameplayTagRequirements { @@ -275,6 +276,12 @@ struct FWinConditionScoreData class UFortPlaylist : public UObject { public: + int& GetPlaylistId() + { + static auto PlaylistIdOffset = GetOffset("PlaylistId"); + return Get(PlaylistIdOffset); + } + TArray>& GetModifierList() { static auto ModifierListOffset = this->GetOffset("ModifierList"); diff --git a/Project Reboot 3.0/FortWorldItemDefinition.h b/Project Reboot 3.0/FortWorldItemDefinition.h index 131fa40..5bca1b1 100644 --- a/Project Reboot 3.0/FortWorldItemDefinition.h +++ b/Project Reboot 3.0/FortWorldItemDefinition.h @@ -1,6 +1,7 @@ #pragma once #include "FortItemDefinition.h" +#include "DataTable.h" enum class EWorldItemDropBehavior : uint8_t { @@ -10,6 +11,16 @@ enum class EWorldItemDropBehavior : uint8_t EWorldItemDropBehavior_MAX = 3 }; +struct FFortLootLevelData : public FTableRowBase +{ +public: + FName Category; // 0x8(0x8)(Edit, BlueprintVisible, BlueprintReadOnly, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + int32 LootLevel; // 0x10(0x4)(Edit, BlueprintVisible, BlueprintReadOnly, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + int32 MinItemLevel; // 0x14(0x4)(Edit, BlueprintVisible, BlueprintReadOnly, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + int32 MaxItemLevel; // 0x18(0x4)(Edit, BlueprintVisible, BlueprintReadOnly, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + uint8 Pad_B94[0x4]; // Fixing Size Of Struct [ Dumper-7 ] +}; + class UFortWorldItemDefinition : public UFortItemDefinition { public: @@ -20,6 +31,35 @@ public: return ReadBitfieldValue(bCanBeDroppedOffset, bCanBeDroppedFieldMask); } + int PickLevel(int PreferredLevel) // well min level and maxlevel is sometimes in ufortowrlditemdeifnit9 then on older versions ufortitemdefinitoj so idk wher tyo put this + { + static auto MinLevelOffset = GetOffset("MinLevel"); + static auto MaxLevelOffset = GetOffset("MaxLevel"); + + const int MinLevel = Get(MinLevelOffset); + const int MaxLevel = Get(MaxLevelOffset); + + int PickedLevel = 0; + + if (PreferredLevel >= MinLevel) + PickedLevel = PreferredLevel; + + if (MaxLevel >= 0) + { + if (PickedLevel <= MaxLevel) + return PickedLevel; + return MaxLevel; + } + + return PickedLevel; + } + + FDataTableCategoryHandle& GetLootLevelData() + { + static auto LootLevelDataOffset = GetOffset("LootLevelData"); + return Get(LootLevelDataOffset); + } + int& GetDropCount() { static auto DropCountOffset = GetOffset("DropCount"); diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index 0b0bbb0..97bc21f 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -214,6 +214,7 @@ + @@ -287,6 +288,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 79b3686..fdd245a 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -271,6 +271,9 @@ Reboot\Private + + FortniteGame\Source\FortniteGame\Private\Items + @@ -859,6 +862,9 @@ Reboot\Public + + Reboot\Public + diff --git a/Project Reboot 3.0/World.cpp b/Project Reboot 3.0/World.cpp index 106085d..283b37f 100644 --- a/Project Reboot 3.0/World.cpp +++ b/Project Reboot 3.0/World.cpp @@ -85,7 +85,7 @@ void UWorld::Listen() *(UNetDriver**)(__int64(LevelCollections.AtPtr(0, LevelCollectionSize)) + 0x10) = NewNetDriver; *(UNetDriver**)(__int64(LevelCollections.AtPtr(1, LevelCollectionSize)) + 0x10) = NewNetDriver; - LOG_INFO(LogNet, "Listening on port {}!", Port + Globals::AmountOfListens - 1); + LOG_INFO(LogNet, "Listening on port {}{}!", Port + Globals::AmountOfListens - 1, Engine_Version <= 419 ? " (Wait like 10 seconds before joining though)" : ""); } AWorldSettings* UWorld::GetWorldSettings(const bool bCheckStreamingPersistent, const bool bChecked) const diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index 10efbc4..ce2009b 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -386,6 +386,9 @@ void Offsets::FindAll() Engine_Version == 424 ? (Fortnite_Version >= 11.00 && Fortnite_Version <= 11.10 ? 0x57 : (Fortnite_Version == 11.30 || Fortnite_Version == 11.31 ? 0x59 : 0x5A)) : 0x56; + if (Engine_Version == 421) + Offsets::PropertyClass = 0x70; + // ^ I know this makes no sense, 7.40-8.40 is 0x57, other 7-10 is 0x56, 11.00-11.10 = 0x57, 11.30-11.31 = 0x59, other S11 is 0x5A else if (std::floor(Fortnite_Version) == 12 || std::floor(Fortnite_Version) == 13) @@ -437,18 +440,23 @@ void Offsets::FindAll() } Offsets::IsNetRelevantFor = FindIsNetRelevantForOffset(); + Offsets::UnderlyingType = Offsets::PropertyClass; } void Offsets::Print() { - LOG_INFO(LogDev, "Offset_Internal: 0x{:x}", Offset_Internal); - LOG_INFO(LogDev, "SuperStruct: 0x{:x}", SuperStruct); - LOG_INFO(LogDev, "Children: 0x{:x}", Children); - LOG_INFO(LogDev, "PropertiesSize: 0x{:x}", PropertiesSize); LOG_INFO(LogDev, "Func: 0x{:x}", Func); + LOG_INFO(LogDev, "PropertiesSize: 0x{:x}", PropertiesSize); + LOG_INFO(LogDev, "Children: 0x{:x}", Children); + LOG_INFO(LogDev, "SuperStruct: 0x{:x}", SuperStruct); + LOG_INFO(LogDev, "PropertyClass: 0x{:x}", PropertyClass); + LOG_INFO(LogDev, "UnderlyingType: 0x{:x}", UnderlyingType); + LOG_INFO(LogDev, "Offset_Internal: 0x{:x}", Offset_Internal); LOG_INFO(LogDev, "ServerReplicateActors: 0x{:x}", ServerReplicateActors); LOG_INFO(LogDev, "ReplicationFrame: 0x{:x}", ReplicationFrame); LOG_INFO(LogDev, "IsNetRelevantFor: 0x{:x}", IsNetRelevantFor); + LOG_INFO(LogDev, "NetworkObjectList: 0x{:x}", NetworkObjectList); + LOG_INFO(LogDev, "ClientWorldPackageName: 0x{:x}", ClientWorldPackageName); } void Addresses::Init() diff --git a/Project Reboot 3.0/addresses.h b/Project Reboot 3.0/addresses.h index abe7fd8..85a2e24 100644 --- a/Project Reboot 3.0/addresses.h +++ b/Project Reboot 3.0/addresses.h @@ -84,6 +84,8 @@ namespace Offsets extern inline uint64 PropertiesSize = 0; extern inline uint64 Children = 0; extern inline uint64 SuperStruct = 0; + extern inline uint64 PropertyClass = 0; + extern inline uint64 UnderlyingType = 0; extern inline uint64 Offset_Internal = 0; extern inline uint64 ServerReplicateActors = 0; extern inline uint64 ReplicationFrame = 0; diff --git a/Project Reboot 3.0/commands.h b/Project Reboot 3.0/commands.h index 5988b03..f37b578 100644 --- a/Project Reboot 3.0/commands.h +++ b/Project Reboot 3.0/commands.h @@ -9,6 +9,7 @@ #include "builder.h" #include "FortLootPackage.h" #include "bots.h" +#include "FortLootLevel.h" bool IsOperator(APlayerState* PlayerState, AFortPlayerController* PlayerController) { @@ -200,6 +201,12 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg) if (bShouldUpdate) WorldInventory->Update(); + auto GameState = Cast(GetWorld()->GetGameState()); + + auto ItemLevel = UFortLootLevel::GetItemLevel(WID->GetLootLevelData(), GameState->GetWorldLevel()); + + LOG_INFO(LogDev, "ItemLevel: {}", ItemLevel); + LOG_INFO(LogDev, "PickLevel: {}", WID->PickLevel(ItemLevel)); SendMessageToConsole(PlayerController, L"Granted item!"); } else if (Command == "printsimulatelootdrops") @@ -211,8 +218,9 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg) } auto& lootTierGroup = Arguments[1]; + auto GameState = Cast(GetWorld()->GetGameState()); - auto LootDrops = PickLootDrops(UKismetStringLibrary::Conv_StringToName(std::wstring(lootTierGroup.begin(), lootTierGroup.end()).c_str()), -1, true); + auto LootDrops = PickLootDrops(UKismetStringLibrary::Conv_StringToName(std::wstring(lootTierGroup.begin(), lootTierGroup.end()).c_str()), GameState->GetWorldLevel(), -1, true); for (int i = 0; i < LootDrops.size(); i++) { diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index ceaa341..b8901e9 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -81,6 +81,20 @@ static __int64 DispatchRequestHook(__int64 a1, __int64* a2, int a3) return DispatchRequestOriginal(a1, a2, 3); } +static inline bool (*CanCreateInCurrentContextOriginal)(UObject* Template); + +bool CanCreateInCurrentContextHook(UObject* Template) +{ + auto Original = CanCreateInCurrentContextOriginal(Template); + + if (!Original) + { + LOG_INFO(LogDev, "CanCreateInCurrentContext returned false, but we will return true."); + } + + return true; +} + void (*ApplyHomebaseEffectsOnPlayerSetupOriginal)( __int64* GameState, __int64 a2, @@ -426,6 +440,8 @@ DWORD WINAPI Main(LPVOID) nullptr, false); } + // Hooking::MinHook::Hook(FortPlayerControllerZoneDefault->VFTable[0xD0 / 8], CanCreateInCurrentContextHook, (PVOID*)&CanCreateInCurrentContextOriginal); + HookInstruction(Addresses::UpdateTrackedAttributesLea, (PVOID)UFortGadgetItemDefinition::UpdateTrackedAttributesHook, "/Script/FortniteGame.FortPlayerController.Suicide", ERelativeOffsets::LEA, FortPlayerControllerAthenaDefault); HookInstruction(Addresses::CombinePickupLea, (PVOID)AFortPickup::CombinePickupHook, "/Script/Engine.PlayerController.SetVirtualJoystickVisibility", ERelativeOffsets::LEA, FortPlayerControllerAthenaDefault); diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index a4aab88..624ae19 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -144,6 +144,8 @@ static inline uint64 FindObjectArray() static inline uint64 FindPickupInitialize() { + if (Engine_Version == 419) + return Memcury::Scanner::FindPattern("48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 80 B9 ? ? ? ? ? 41 0F B6 E9").Get(); // 1.11 if (Engine_Version == 420) return Memcury::Scanner::FindPattern("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 41 56 48 83 EC 20 80 B9 ? ? ? ? ? 45 0F B6 F1 49 8B E8").Get(); // 4.1 if (Engine_Version == 421) diff --git a/Project Reboot 3.0/gui.h b/Project Reboot 3.0/gui.h index 184588f..6b58ad2 100644 --- a/Project Reboot 3.0/gui.h +++ b/Project Reboot 3.0/gui.h @@ -39,6 +39,7 @@ #include "events.h" #include "FortAthenaMutator_Heist.h" #include "BGA.h" +#include "objectviewer.h" #define GAME_TAB 1 #define PLAYERS_TAB 2 @@ -283,7 +284,7 @@ static inline void StaticUI() #ifndef PROD ImGui::Checkbox("Log ProcessEvent", &Globals::bLogProcessEvent); - ImGui::InputInt("Amount of bots to spawn", &AmountOfBotsToSpawn); + // ImGui::InputInt("Amount of bots to spawn", &AmountOfBotsToSpawn); #endif ImGui::Checkbox("Infinite Ammo", &Globals::bInfiniteAmmo); @@ -480,40 +481,6 @@ static inline void MainUI() SpawnBGAs(); } - /* - if (ImGui::Button("New")) - { - static auto NextFn = FindObject("/Game/Athena/Prototype/Blueprints/Cube/CUBE.CUBE_C.Next"); - static auto NewFn = FindObject("/Game/Athena/Prototype/Blueprints/Cube/CUBE.CUBE_C.New"); - auto Loader = GetEventLoader("/Game/Athena/Prototype/Blueprints/Cube/CUBE.CUBE_C"); - - LOG_INFO(LogDev, "Loader: {}", __int64(Loader)); - - if (Loader) - { - int32 NewParam = 1; - // Loader->ProcessEvent(NextFn, &NewParam); - Loader->ProcessEvent(NewFn, &NewParam); - } - } - - if (ImGui::Button("Next")) - { - static auto NextFn = FindObject("/Game/Athena/Prototype/Blueprints/Cube/CUBE.CUBE_C.Next"); - static auto NewFn = FindObject("/Game/Athena/Prototype/Blueprints/Cube/CUBE.CUBE_C.New"); - auto Loader = GetEventLoader("/Game/Athena/Prototype/Blueprints/Cube/CUBE.CUBE_C"); - - LOG_INFO(LogDev, "Loader: {}", __int64(Loader)); - - if (Loader) - { - int32 NewParam = 1; - Loader->ProcessEvent(NextFn, &NewParam); - // Loader->ProcessEvent(NewFn, &NewParam); - } - } - */ - if (!bIsInAutoRestart && (Engine_Version < 424 && ImGui::Button("Restart"))) { if (Engine_Version < 424) @@ -526,45 +493,6 @@ static inline void MainUI() LOG_ERROR(LogGame, "Restarting is not supported on chapter 2 and above!"); } } - /* - if (ImGui::Button("TEST")) - { - auto GameMode = (AFortGameMode*)GetWorld()->GetGameMode(); - auto GameState = GameMode->GetGameState(); - - static auto mutatorClass = FindObject("/Script/FortniteGame.FortAthenaMutator"); - auto AllMutators = UGameplayStatics::GetAllActorsOfClass(GetWorld(), mutatorClass); - - for (int i = 0; i < AllMutators.Num(); i++) - { - auto Mutator = AllMutators.at(i); - - LOG_INFO(LogDev, "[{}] Mutator: {}", i, Mutator->GetFullName()); - - if (auto DiscoMutator = Cast(Mutator)) - { - auto& ControlPointSpawnData = DiscoMutator->GetControlPointSpawnData(); - - LOG_INFO(LogDev, "ControlPointSpawnData.Num(): {}", ControlPointSpawnData.Num()); - } - else if (auto HeistMutator = Cast(Mutator)) - { - auto& HeistExitCraftSpawnData = HeistMutator->GetHeistExitCraftSpawnData(); - - LOG_INFO(LogDev, "HeistExitCraftSpawnData.Num(): {}", HeistExitCraftSpawnData.Num()); - - for (int j = 0; j < HeistExitCraftSpawnData.Num(); j++) - { - auto& CurrentHeistExitCraftSpawnData = HeistExitCraftSpawnData.at(j); - auto CurveTable = CurrentHeistExitCraftSpawnData.SpawnDelayTime.GetCurve().CurveTable; - - // LOG_INFO(LogDev, "{} {}", CurveTable ? CurveTable->GetFullName() : "InvalidTable", - // CurrentHeistExitCraftSpawnData.SpawnDelayTime.GetCurve().RowName.IsValid() ? CurrentHeistExitCraftSpawnData.SpawnDelayTime.GetCurve().RowName.ToString() : "InvalidName"); - } - } - } - } - */ if (!bStartedBus) { @@ -905,13 +833,29 @@ static inline void MainUI() { static std::string ClassNameToDump; static std::string FunctionNameToDump; + static std::string ObjectToDump; + static std::string FileNameToDumpTo; + static bool bExcludeUnhandled; ImGui::Checkbox("Fill Vending Machines", &Globals::bFillVendingMachines); ImGui::Checkbox("Enable Bot Tick", &bEnableBotTick); ImGui::Checkbox("Enable Combine Pickup", &bEnableCombinePickup); ImGui::InputText("Class Name to mess with", &ClassNameToDump); - + ImGui::InputText("Object to dump", &ObjectToDump); ImGui::InputText("Function Name to mess with", &FunctionNameToDump); + ImGui::InputText("File name to dump to", &FileNameToDumpTo); + ImGui::Checkbox("Exclude unhandled", &bExcludeUnhandled); + + if (ImGui::Button("Dump Object Info")) + { + auto Object = FindObject(ObjectToDump); + + LOG_INFO(LogDev, "r: {}", __int64(Object)); + + // null check is already handled + + ObjectViewer::DumpContentsToFile(Object, FileNameToDumpTo, bExcludeUnhandled); + } if (ImGui::Button("Print Class VFT")) { diff --git a/Project Reboot 3.0/log.h b/Project Reboot 3.0/log.h index 44176d2..9698a89 100644 --- a/Project Reboot 3.0/log.h +++ b/Project Reboot 3.0/log.h @@ -83,6 +83,7 @@ inline void InitLogger() MakeLogger("LogVehicles"); MakeLogger("LogBots"); MakeLogger("LogCosmetics"); + MakeLogger("LogObjectViewer"); } #define LOG_DEBUG(loggerName, ...) \ diff --git a/Project Reboot 3.0/objectviewer.h b/Project Reboot 3.0/objectviewer.h new file mode 100644 index 0000000..07d1307 --- /dev/null +++ b/Project Reboot 3.0/objectviewer.h @@ -0,0 +1,193 @@ +#pragma once + +#include "reboot.h" +#include + +static inline FName* GetNamePrivateOfProperty(void* Property) +{ + FName* NamePrivate = nullptr; + + if (Engine_Version >= 425) + NamePrivate = (FName*)(__int64(Property) + 0x28); + else + NamePrivate = &((UField*)Property)->NamePrivate; + + return NamePrivate; +}; + +static inline bool IsPropertyA(void* Property, UClass* Class) +{ + if (Engine_Version < 425) + { + if (((UField*)Property)->IsA(Class)) + return true; + } + else + { + // TODO + } + + return false; +} + +namespace ObjectViewer +{ + static inline void DumpContentsToFile(UObject* Object, const std::string& FileName, bool bExcludeUnhandled = false) + { + if (!Object/*->IsValidLowLevel()*/) + { + LOG_ERROR(LogObjectViewer, "Invalid object passed into DumpContentsToFile!"); + return; + } + + static auto ClassClass = FindObject(L"/Script/CoreUObject.Class"); + + if (Object->IsA(ClassClass)) + { + LOG_ERROR(LogObjectViewer, "Object passed into DumpContentsToFile was a class!"); + return; + } + + static auto BytePropertyClass = FindObject(L"/Script/CoreUObject.ByteProperty"); + static auto ObjectPropertyClass = FindObject(L"/Script/CoreUObject.ObjectProperty"); + static auto ClassPropertyClass = FindObject(L"/Script/CoreUObject.ClassProperty"); + static auto DoublePropertyClass = FindObject(L"/Script/CoreUObject.DoubleProperty"); + static auto FloatPropertyClass = FindObject(L"/Script/CoreUObject.FloatProperty"); + static auto Int8PropertyClass = FindObject(L"/Script/CoreUObject.Int8Property"); + static auto EnumPropertyClass = FindObject(L"/Script/CoreUObject.EnumProperty"); + static auto ArrayPropertyClass = FindObject(L"/Script/CoreUObject.ArrayProperty"); + static auto Int64PropertyClass = FindObject(L"/Script/CoreUObject.Int64Property"); + static auto UInt16PropertyClass = FindObject(L"/Script/CoreUObject.UInt16Property"); + static auto BoolPropertyClass = FindObject(L"/Script/CoreUObject.BoolProperty"); + static auto NamePropertyClass = FindObject(L"/Script/CoreUObject.NameProperty"); + static auto UInt32PropertyClass = FindObject(L"/Script/CoreUObject.UInt32Property"); + static auto FunctionClass = FindObject(L"/Script/CoreUObject.Function"); + static auto IntPropertyClass = FindObject(L"/Script/CoreUObject.IntProperty"); + static auto UInt64PropertyClass = FindObject(L"/Script/CoreUObject.UInt64Property"); + static auto StrPropertyClass = FindObject(L"/Script/CoreUObject.StrProperty"); + static auto SoftObjectPropertyClass = FindObject(L"/Script/CoreUObject.SoftObjectProperty"); + + std::ofstream Stream(FileName); + + if (!Stream.is_open()) + { + LOG_ERROR(LogObjectViewer, "Failed to open file {}!", FileName); + return; + } + + for (auto CurrentClass = Object->ClassPrivate; CurrentClass; CurrentClass = CurrentClass->GetSuperStruct()) + { + void* Property = *(void**)(__int64(CurrentClass) + Offsets::Children); + + while (Property) + { + std::string PropertyName = GetNamePrivateOfProperty(Property)->ToString(); + int Offset = *(int*)(__int64(Property) + Offsets::Offset_Internal); + + if (Offsets::PropertyClass) + { + if (IsPropertyA(Property, ObjectPropertyClass)) + { + auto PropertyClass = *(UClass**)(__int64(Property) + Offsets::PropertyClass); + + if (PropertyClass->IsValidLowLevel()) + Stream << std::format("{} Object: {}\n", PropertyName, PropertyClass->GetPathName()); + } + + /* + else if (IsPropertyA(Property, SoftObjectPropertyClass)) + { + auto PropertyClass = *(UClass**)(__int64(Property) + Offsets::PropertyClass); + + if (PropertyClass->IsValidLowLevel()) + { + auto SoftObjectPtr = *(TSoftObjectPtr*)(__int64(Object) + Offset); + auto SoftObjectPtrObject = SoftObjectPtr.Get(PropertyClass); + Stream << std::format("{} SoftObjectPtr (type: {}): {}\n", PropertyName, PropertyClass->GetName(), SoftObjectPtrObject ? SoftObjectPtrObject->GetPathName() : "BadRead"); + } + } + */ + } + + if (IsPropertyA(Property, BytePropertyClass)) + { + Stream << std::format("{} Byte: {}\n", PropertyName, *(uint8*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, DoublePropertyClass)) + { + Stream << std::format("{} Double: {}\n", PropertyName, *(double*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, UInt16PropertyClass)) + { + Stream << std::format("{} UInt16: {}\n", PropertyName, *(uint16*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, Int8PropertyClass)) + { + Stream << std::format("{} Int8: {}\n", PropertyName, *(int8*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, NamePropertyClass)) + { + Stream << std::format("{} Name: {}\n", PropertyName, (*(FName*)(__int64(Object) + Offset)).ToString()); + } + else if (IsPropertyA(Property, StrPropertyClass)) + { + Stream << std::format("{} String: {}\n", PropertyName, (*(FString*)(__int64(Object) + Offset)).ToString()); + } + else if (IsPropertyA(Property, FloatPropertyClass)) + { + Stream << std::format("{} Float: {}\n", PropertyName, *(float*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, BoolPropertyClass)) + { + auto FieldMask = GetFieldMask(Property); + + Stream << std::format("{} Bool: {}\n", PropertyName, ReadBitfield((PlaceholderBitfield*)(__int64(Object) + Offset), FieldMask)); + } + else if (IsPropertyA(Property, IntPropertyClass)) + { + Stream << std::format("{} Int32: {}\n", PropertyName, *(int*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, UInt32PropertyClass)) + { + Stream << std::format("{} UInt32: {}\n", PropertyName, *(uint32*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, UInt64PropertyClass)) + { + Stream << std::format("{} UInt64: {}\n", PropertyName, *(uint64*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, Int64PropertyClass)) + { + Stream << std::format("{} Int64: {}\n", PropertyName, *(int64*)(__int64(Object) + Offset)); + } + else if (IsPropertyA(Property, ArrayPropertyClass)) + { + Stream << std::format("{} Array\n", PropertyName); + } + else if (IsPropertyA(Property, EnumPropertyClass)) + { + using UNumericProperty = UObject; + + auto EnumValueIg = *(uint8*)(__int64(Property) + Offset); + auto UnderlyingType = *(UNumericProperty**)(__int64(Property) + Offsets::UnderlyingType); + // Stream << std::format("{} Enum: {}\n", PropertyName, (int)EnumValueIg); + Stream << std::format("{} Enum\n", PropertyName); + } + else if (IsPropertyA(Property, FunctionClass)) + { + + } + else if (!bExcludeUnhandled) + { + // Stream << std::format("{}: {}\n", PropertyName, "UNHANDLED"); + Stream << std::format("{}: {} {}\n", PropertyName, "UNHANDLED", ""/*, ((UObject*)Property)->GetName()*/); + } + + Property = Engine_Version >= 425 ? *(void**)(__int64(Property) + 0x20) : ((UField*)Property)->Next; + } + } + + return; + } + + static inline void DumpContentsToFile(const std::string& ObjectName, const std::string& FileName, bool bExcludeUnhandled = false) { return DumpContentsToFile(FindObject(ObjectName), FileName, bExcludeUnhandled); } +} diff --git a/Project Reboot 3.0/vendingmachine.h b/Project Reboot 3.0/vendingmachine.h index 8ed30f6..b3fbf55 100644 --- a/Project Reboot 3.0/vendingmachine.h +++ b/Project Reboot 3.0/vendingmachine.h @@ -124,7 +124,7 @@ static inline void FillItemCollector(ABuildingItemCollectorActor* ItemCollector, constexpr bool bPrint = false; - std::vector LootDrops = PickLootDrops(LootTierGroup, LootTier, bPrint); + std::vector LootDrops = PickLootDrops(LootTierGroup, GameState->GetWorldLevel(), LootTier, bPrint); if (LootDrops.size() == 0) continue;