diff --git a/Project Reboot 3.0/AbilitySystemComponent.h b/Project Reboot 3.0/AbilitySystemComponent.h index 3b3a6ff..e01adbc 100644 --- a/Project Reboot 3.0/AbilitySystemComponent.h +++ b/Project Reboot 3.0/AbilitySystemComponent.h @@ -8,14 +8,18 @@ struct PadHex18 { char Pad[0x18]; }; struct PadHexA8 { char Pad[0xA8]; }; struct PadHexB0 { char Pad[0xB0]; }; -using FPredictionKey = PadHex18; -using FGameplayEventData = PadHexA8; +// using FPredictionKey = PadHex18; +// using FGameplayEventData = PadHexA8; + +using FPredictionKey = __int64; +using FGameplayEventData = __int64; class UAbilitySystemComponent : public UObject { public: static inline FGameplayAbilitySpecHandle* (*GiveAbilityOriginal)(UAbilitySystemComponent*, FGameplayAbilitySpecHandle*, __int64 inSpec); - static inline bool (*InternalTryActivateAbilityOriginal)(UAbilitySystemComponent*, FGameplayAbilitySpecHandle Handle, FPredictionKey InPredictionKey, UObject** OutInstancedAbility, void* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData); + static inline bool (*InternalTryActivateAbilityOriginal)(UAbilitySystemComponent*, FGameplayAbilitySpecHandle Handle, PadHex10 InPredictionKey, UObject** OutInstancedAbility, void* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData); + static inline bool (*InternalTryActivateAbilityOriginal2)(UAbilitySystemComponent*, FGameplayAbilitySpecHandle Handle, PadHex18 InPredictionKey, UObject** OutInstancedAbility, void* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData); void ClientActivateAbilityFailed(FGameplayAbilitySpecHandle AbilityToActivate, int16_t PredictionKey) { diff --git a/Project Reboot 3.0/AbilitySystemComponent_Abilities.cpp b/Project Reboot 3.0/AbilitySystemComponent_Abilities.cpp index 2012311..c4e1dd7 100644 --- a/Project Reboot 3.0/AbilitySystemComponent_Abilities.cpp +++ b/Project Reboot 3.0/AbilitySystemComponent_Abilities.cpp @@ -28,36 +28,60 @@ void InternalServerTryActivateAbility(UAbilitySystemComponent* AbilitySystemComp auto Spec = AbilitySystemComponent->FindAbilitySpecFromHandle(Handle); + static auto PredictionKeyStruct = FindObject("/Script/GameplayAbilities.PredictionKey"); + static auto PredictionKeySize = PredictionKeyStruct->GetPropertiesSize(); static auto CurrentOffset = FindOffsetStruct("/Script/GameplayAbilities.PredictionKey", "Current"); if (!Spec) { + LOG_INFO(LogAbilities, "InternalServerTryActivateAbility. Rejecting ClientActivation of ability with invalid SpecHandle!"); AbilitySystemComponent->ClientActivateAbilityFailed(Handle, *(int16_t*)(__int64(PredictionKey) + CurrentOffset)); return; } // ConsumeAllReplicatedData(Handle, PredictionKey); - const UGameplayAbility* AbilityToActivate = Spec->GetAbility(); + /* const */ UGameplayAbility * AbilityToActivate = Spec->GetAbility(); if (!AbilityToActivate) + { + LOG_ERROR(LogAbilities, "InternalServerTryActiveAbility. Rejecting ClientActivation of unconfigured spec ability!"); + AbilitySystemComponent->ClientActivateAbilityFailed(Handle, *(int16_t*)(__int64(PredictionKey) + CurrentOffset)); return; + } UGameplayAbility* InstancedAbility = nullptr; SetBitfield(Spec, 1, true); // InputPressed = true + + bool res = false; - if (!AbilitySystemComponent->InternalTryActivateAbilityOriginal(AbilitySystemComponent, Handle, *(FPredictionKey*)PredictionKey, &InstancedAbility, nullptr, TriggerEventData)) + if (PredictionKeySize == 0x10) + res = UAbilitySystemComponent::InternalTryActivateAbilityOriginal(AbilitySystemComponent, Handle, *(PadHex10*)PredictionKey, &InstancedAbility, nullptr, TriggerEventData); + else if (PredictionKeySize == 0x18) + res = UAbilitySystemComponent::InternalTryActivateAbilityOriginal2(AbilitySystemComponent, Handle, *(PadHex18*)PredictionKey, &InstancedAbility, nullptr, TriggerEventData); + else + LOG_ERROR(LogAbilities, "Prediction key size does not match with any of them!"); + + if (res) { + LOG_INFO(LogAbilities, "InternalServerTryActivateAbility. Rejecting ClientActivation of {}. InternalTryActivateAbility failed: ", AbilityToActivate->GetName()); + AbilitySystemComponent->ClientActivateAbilityFailed(Handle, *(int16_t*)(__int64(PredictionKey) + CurrentOffset)); SetBitfield(Spec, 1, false); // InputPressed = false static auto ActivatableAbilitiesOffset = AbilitySystemComponent->GetOffset("ActivatableAbilities"); AbilitySystemComponent->Get(ActivatableAbilitiesOffset).MarkItemDirty(Spec); } + else + { + LOG_INFO(LogAbilities, "InternalServerTryActivateAbility. Activated {}", AbilityToActivate->GetName()); + } } FGameplayAbilitySpecHandle UAbilitySystemComponent::GiveAbilityEasy(UClass* AbilityClass) { + // LOG_INFO(LogDev, "Making spec!"); + auto NewSpec = MakeNewSpec(AbilityClass); // LOG_INFO(LogDev, "Made spec!"); @@ -91,12 +115,14 @@ FGameplayAbilitySpec* UAbilitySystemComponent::FindAbilitySpecFromHandle(FGamepl void UAbilitySystemComponent::ServerTryActivateAbilityHook(UAbilitySystemComponent* AbilitySystemComponent, FGameplayAbilitySpecHandle Handle, bool InputPressed, FPredictionKey PredictionKey) { + LOG_INFO(LogAbilities, "ServerTryActivateAbility"); InternalServerTryActivateAbility(AbilitySystemComponent, Handle, /* InputPressed, */ &PredictionKey, nullptr); } void UAbilitySystemComponent::ServerTryActivateAbilityWithEventDataHook(UAbilitySystemComponent* AbilitySystemComponent, FGameplayAbilitySpecHandle Handle, bool InputPressed, FPredictionKey PredictionKey, FGameplayEventData TriggerEventData) { + LOG_INFO(LogAbilities, "ServerTryActivateAbilityWithEventData"); InternalServerTryActivateAbility(AbilitySystemComponent, Handle, /* InputPressed, */ &PredictionKey, &TriggerEventData); } diff --git a/Project Reboot 3.0/Actor.cpp b/Project Reboot 3.0/Actor.cpp index e8b6027..ded9732 100644 --- a/Project Reboot 3.0/Actor.cpp +++ b/Project Reboot 3.0/Actor.cpp @@ -49,5 +49,14 @@ FVector AActor::GetActorLocation() FVector ret; this->ProcessEvent(K2_GetActorLocationFn, &ret); + return ret; +} + +FVector AActor::GetActorRightVector() +{ + static auto GetActorRightVectorFn = FindObject("/Script/Engine.Actor.GetActorRightVector"); + FVector ret; + this->ProcessEvent(GetActorRightVectorFn, &ret); + return ret; } \ No newline at end of file diff --git a/Project Reboot 3.0/Actor.h b/Project Reboot 3.0/Actor.h index 6ff935e..be2fc5b 100644 --- a/Project Reboot 3.0/Actor.h +++ b/Project Reboot 3.0/Actor.h @@ -9,6 +9,7 @@ public: AActor* GetOwner(); struct FVector GetActorLocation(); + struct FVector GetActorRightVector(); void K2_DestroyActor(); class UActorComponent* GetComponentByClass(class UClass* ComponentClass); }; \ No newline at end of file diff --git a/Project Reboot 3.0/Array.h b/Project Reboot 3.0/Array.h index 50f3f7f..75d77d8 100644 --- a/Project Reboot 3.0/Array.h +++ b/Project Reboot 3.0/Array.h @@ -23,9 +23,9 @@ public: public: - inline InElementType& At(int i, int Size = sizeof(InElementType)) const { return *(InElementType*)(__int64(Data) + (static_cast(Size) * i)); } - inline InElementType& at(int i, int Size = sizeof(InElementType)) const { return *(InElementType*)(__int64(Data) + (static_cast(Size) * i)); } - inline InElementType* AtPtr(int i, int Size = sizeof(InElementType)) const { return (InElementType*)(__int64(Data) + (static_cast(Size) * i)); } + inline InElementType& At(int i, size_t Size = sizeof(InElementType)) const { return *(InElementType*)(__int64(Data) + (static_cast(Size) * i)); } + inline InElementType& at(int i, size_t Size = sizeof(InElementType)) const { return *(InElementType*)(__int64(Data) + (static_cast(Size) * i)); } + inline InElementType* AtPtr(int i, size_t Size = sizeof(InElementType)) const { return (InElementType*)(__int64(Data) + (static_cast(Size) * i)); } inline int Num() const { return ArrayNum; } inline int size() const { return ArrayNum; } @@ -35,14 +35,14 @@ public: return ArrayMax - NumElements; } - void Reserve(int Number, int Size = sizeof(InElementType)) + void Reserve(int Number, size_t Size = sizeof(InElementType)) { // LOG_INFO(LogDev, "ArrayNum {}", ArrayNum); // Data = (InElementType*)FMemory::Realloc(Data, (ArrayMax = ArrayNum + Number) * Size, 0); Data = /* (ArrayMax - ArrayNum) >= ArrayNum ? Data : */ (InElementType*)FMemory::Realloc(Data, (ArrayMax = Number + ArrayNum) * Size, 0); } - FORCENOINLINE void ResizeGrow(int32 OldNum, int Size = sizeof(InElementType)) + FORCENOINLINE void ResizeGrow(int32 OldNum, size_t Size = sizeof(InElementType)) { // LOG_INFO(LogMemory, "FMemory::Realloc: {}", __int64(FMemory::Realloc)); @@ -52,7 +52,7 @@ public: Data = (InElementType*)FMemory::Realloc(Data, ArrayMax * Size, 0); } - FORCEINLINE int32 AddUninitialized(int32 Count = 1, int Size = sizeof(InElementType)) + FORCEINLINE int32 AddUninitialized(int32 Count = 1, size_t Size = sizeof(InElementType)) { // CheckInvariants(); @@ -69,7 +69,7 @@ public: return OldNum; } - FORCEINLINE int32 Emplace(const InElementType& New, int Size = sizeof(InElementType)) + FORCEINLINE int32 Emplace(const InElementType& New, size_t Size = sizeof(InElementType)) { const int32 Index = AddUninitialized(1, Size); // resizes array memcpy_s((InElementType*)(__int64(Data) + (Index * Size)), Size, (void*)&New, Size); @@ -82,7 +82,7 @@ public: return Emplace(New, Size); } */ - int Add(const InElementType& New, int Size = sizeof(InElementType)) + int Add(const InElementType& New, size_t Size = sizeof(InElementType)) { // LOG_INFO(LogDev, "ArrayMax: {}", ArrayMax); @@ -113,14 +113,17 @@ public: ArrayMax = 0; } - bool Remove(const int Index) + bool Remove(const int Index, size_t Size = sizeof(InElementType)) { // return false; if (Index < ArrayNum) { if (Index != ArrayNum - 1) - Data[Index] = Data[ArrayNum - 1]; + { + memcpy_s(&at(Index, Size), Size, &at(ArrayNum - 1, Size), Size); + // Data[Index] = Data[ArrayNum - 1]; + } --ArrayNum; diff --git a/Project Reboot 3.0/AthenaMarkerComponent.h b/Project Reboot 3.0/AthenaMarkerComponent.h new file mode 100644 index 0000000..8e01f14 --- /dev/null +++ b/Project Reboot 3.0/AthenaMarkerComponent.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ActorComponent.h" + +class UAthenaMarkerComponent : public UActorComponent +{ +public: + +}; \ No newline at end of file diff --git a/Project Reboot 3.0/BuildingContainer.h b/Project Reboot 3.0/BuildingContainer.h index e954274..141175e 100644 --- a/Project Reboot 3.0/BuildingContainer.h +++ b/Project Reboot 3.0/BuildingContainer.h @@ -5,6 +5,13 @@ class ABuildingContainer : public ABuildingSMActor { public: + bool ShouldDestroyOnSearch() + { + static auto bDestroyContainerOnSearchOffset = GetOffset("bDestroyContainerOnSearch"); + static auto bDestroyContainerOnSearchFieldMask = GetFieldMask(GetProperty("bDestroyContainerOnSearch")); + return this->ReadBitfieldValue(bDestroyContainerOnSearchOffset, bDestroyContainerOnSearchFieldMask); + } + static UClass* StaticClass() { static auto Class = FindObject("/Script/FortniteGame.BuildingContainer"); diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 26bacc5..43f9e5d 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -76,7 +76,7 @@ static void StreamLevel(std::string LevelName, FVector Location = {}) FTransform Transform{}; Transform.Scale3D = { 1, 1, 1 }; Transform.Translation = Location; - auto BuildingFoundation = GetWorld()->SpawnActor(BuildingFoundation3x3Class, Transform); + auto BuildingFoundation = (GetWorld()->SpawnActor(BuildingFoundation3x3Class, Transform)); if (!BuildingFoundation) { @@ -84,7 +84,7 @@ static void StreamLevel(std::string LevelName, FVector Location = {}) return; } - BuildingFoundation->InitializeBuildingActor(BuildingFoundation, nullptr, false); + // BuildingFoundation->InitializeBuildingActor(BuildingFoundation, nullptr, false); static auto FoundationNameOffset = FindOffsetStruct("/Script/FortniteGame.BuildingFoundationStreamingData", "FoundationName"); static auto FoundationLocationOffset = FindOffsetStruct("/Script/FortniteGame.BuildingFoundationStreamingData", "FoundationLocation"); @@ -117,6 +117,9 @@ UObject* GetPlaylistToUse() Playlist = GetEventPlaylist(); } + // Playlist = FindObject("/MoleGame/Playlists/Playlist_MoleGame.Playlist_MoleGame"); + // Playlist = FindObject("/Game/Athena/Playlists/DADBRO/Playlist_DADBRO_Squads_8.Playlist_DADBRO_Squads_8"); + return Playlist; } @@ -147,7 +150,7 @@ FName AFortGameModeAthena::RedirectLootTier(const FName& LootTier) auto& Key = Pair.Key(); auto& Value = Pair.Value(); - LOG_INFO(LogDev, "[{}] {} {}", i, Key.ComparisonIndex.Value ? Key.ToString() : "NULL", Key.ComparisonIndex.Value ? Value.ToString() : "NULL"); + // LOG_INFO(LogDev, "[{}] {} {}", i, Key.ComparisonIndex.Value ? Key.ToString() : "NULL", Key.ComparisonIndex.Value ? Value.ToString() : "NULL"); if (Key == LootTier) return Value; @@ -658,7 +661,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena if (Globals::bAbilitiesEnabled) { - static auto GameplayAbilitySet = LoadObject(L"/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer") ? + static auto GameplayAbilitySet = Fortnite_Version >= 8.30 ? // LoadObject(L"/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer") ? LoadObject(L"/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer") : LoadObject(L"/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_DefaultPlayer.GAS_DefaultPlayer"); @@ -666,6 +669,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena if (GameplayAbilitySet) { + LOG_INFO(LogDev, "GameplayAbilitySet Name {}", GameplayAbilitySet->GetName()); static auto GameplayAbilitiesOffset = GameplayAbilitySet->GetOffset("GameplayAbilities"); auto GameplayAbilities = GameplayAbilitySet->GetPtr>(GameplayAbilitiesOffset); @@ -751,38 +755,67 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena static auto CreativePortalManagerOffset = GameState->GetOffset("CreativePortalManager"); auto CreativePortalManager = GameState->Get(CreativePortalManagerOffset); - static auto AvailablePortalsOffset = CreativePortalManager->GetOffset("AvailablePortals"); - auto& AvailablePortals = CreativePortalManager->Get>(AvailablePortalsOffset); - auto Portal = (AFortAthenaCreativePortal*)AvailablePortals.at(0); - AvailablePortals.Remove(0); + static auto AvailablePortalsOffset = CreativePortalManager->GetOffset("AvailablePortals", false); - static auto UsedPortalsOffset = CreativePortalManager->GetOffset("UsedPortals"); - auto& UsedPortals = CreativePortalManager->Get>(UsedPortalsOffset); - UsedPortals.Add(Portal); + AFortAthenaCreativePortal* Portal = nullptr; - // Portal->GetCreatorName() = PlayerStateAthena->GetPlayerName(); + if (AvailablePortalsOffset != 0) + { + auto& AvailablePortals = CreativePortalManager->Get>(AvailablePortalsOffset); + Portal = (AFortAthenaCreativePortal*)AvailablePortals.at(0); + AvailablePortals.Remove(0); - *(FUniqueNetIdReplExperimental*)Portal->GetOwningPlayer() = PlayerStateUniqueId; - Portal->GetPortalOpen() = true; + static auto UsedPortalsOffset = CreativePortalManager->GetOffset("UsedPortals"); + auto& UsedPortals = CreativePortalManager->Get>(UsedPortalsOffset); + UsedPortals.Add(Portal); + } + else + { + static auto AllPortalsOffset = CreativePortalManager->GetOffset("AllPortals"); + auto& AllPortals = CreativePortalManager->Get>(AllPortalsOffset); + + for (int i = 0; i < AllPortals.size(); i++) + { + auto CurrentPortal = AllPortals.at(i); - static auto PlayersReadyOffset = Portal->GetOffset("PlayersReady"); - auto& PlayersReady = Portal->Get>(PlayersReadyOffset); - PlayersReady.Add(PlayerStateUniqueId); + if (CurrentPortal->GetUserInitiatedLoad()) + continue; - Portal->GetUserInitiatedLoad() = true; - Portal->GetInErrorState() = false; + Portal = CurrentPortal; + break; + } + } - static auto OwnedPortalOffset = NewPlayer->GetOffset("OwnedPortal"); - NewPlayer->Get(OwnedPortalOffset) = Portal; + if (Portal) + { + // Portal->GetCreatorName() = PlayerStateAthena->GetPlayerName(); - static auto CreativePlotLinkedVolumeOffset = NewPlayer->GetOffset("CreativePlotLinkedVolume"); - NewPlayer->Get(CreativePlotLinkedVolumeOffset) = Portal->GetLinkedVolume(); + *(FUniqueNetIdReplExperimental*)Portal->GetOwningPlayer() = PlayerStateUniqueId; + Portal->GetPortalOpen() = true; - Portal->GetLinkedVolume()->GetVolumeState() = EVolumeState::Ready; + static auto PlayersReadyOffset = Portal->GetOffset("PlayersReady"); + auto& PlayersReady = Portal->Get>(PlayersReadyOffset); + PlayersReady.Add(PlayerStateUniqueId); - static auto IslandPlayset = FindObject("/Game/Playsets/PID_Playset_60x60_Composed.PID_Playset_60x60_Composed"); + Portal->GetUserInitiatedLoad() = true; + Portal->GetInErrorState() = false; - UFortPlaysetItemDefinition::ShowPlayset(IslandPlayset, Portal->GetLinkedVolume()); + static auto OwnedPortalOffset = NewPlayer->GetOffset("OwnedPortal"); + NewPlayer->Get(OwnedPortalOffset) = Portal; + + static auto CreativePlotLinkedVolumeOffset = NewPlayer->GetOffset("CreativePlotLinkedVolume"); + NewPlayer->Get(CreativePlotLinkedVolumeOffset) = Portal->GetLinkedVolume(); + + Portal->GetLinkedVolume()->GetVolumeState() = EVolumeState::Ready; + + static auto IslandPlayset = FindObject("/Game/Playsets/PID_Playset_60x60_Composed.PID_Playset_60x60_Composed"); + + UFortPlaysetItemDefinition::ShowPlayset(IslandPlayset, Portal->GetLinkedVolume()); + } + else + { + LOG_INFO(LogCreative, "Failed to find an open portal!"); + } } static auto SpawnActorDataListOffset = GameMode->GetOffset("SpawnActorDataList"); diff --git a/Project Reboot 3.0/FortInventory.cpp b/Project Reboot 3.0/FortInventory.cpp index 349ce4a..dce9dec 100644 --- a/Project Reboot 3.0/FortInventory.cpp +++ b/Project Reboot 3.0/FortInventory.cpp @@ -11,7 +11,7 @@ UFortItem* CreateItemInstance(AFortPlayerController* PlayerController, UFortItem return NewItemInstance; } -std::pair, std::vector> AFortInventory::AddItem(UFortItemDefinition* ItemDefinition, bool* bShouldUpdate, int Count) +std::pair, std::vector> AFortInventory::AddItem(UFortItemDefinition* ItemDefinition, bool* bShouldUpdate, int Count, int LoadedAmmo, bool bShouldAddToStateValues) { if (bShouldUpdate) *bShouldUpdate = false; @@ -45,6 +45,95 @@ std::pair, std::vector> AFortInventory::AddI return std::make_pair(NewItemInstances, ModifiedItemInstances); } +bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int Count) +{ + if (bShouldUpdate) + *bShouldUpdate = false; + + auto ItemInstance = FindItemInstance(ItemGuid); + auto ReplicatedEntry = FindReplicatedEntry(ItemGuid); + + if (!ItemInstance || !ReplicatedEntry) + return false; + + auto ItemDefinition = Cast(ReplicatedEntry->GetItemDefinition()); + + if (!ItemDefinition) + return false; + + auto NewCount = ReplicatedEntry->GetCount() - Count; + + if (NewCount > 0) + { + ItemInstance->GetItemEntry()->GetCount() = NewCount; + ReplicatedEntry->GetCount() = NewCount; + + GetItemList().MarkItemDirty(ItemInstance->GetItemEntry()); + GetItemList().MarkItemDirty(ReplicatedEntry); + + return true; + } + + static auto FortItemEntryStruct = FindObject(L"/Script/FortniteGame.FortItemEntry"); + static auto FortItemEntrySize = *(int*)(__int64(FortItemEntryStruct) + Offsets::PropertiesSize); + + auto& ItemInstances = GetItemList().GetItemInstances(); + auto& ReplicatedEntries = GetItemList().GetReplicatedEntries(); + + for (int i = 0; i < ItemInstances.Num(); i++) + { + if (ItemInstances.at(i)->GetItemEntry()->GetItemGuid() == ItemGuid) + { + ItemInstances.Remove(i); + break; + } + } + + for (int i = 0; i < ReplicatedEntries.Num(); i++) + { + if (ReplicatedEntries.at(i).GetItemGuid() == ItemGuid) + { + ReplicatedEntries.Remove(i, FortItemEntrySize); + break; + } + } + + // todo remove from weaponlist + + if (bShouldUpdate) + *bShouldUpdate = true; + + return true; +} + +void AFortInventory::ModifyCount(UFortItem* ItemInstance, int New, bool bRemove, std::pair* outEntries, bool bUpdate) +{ + auto ReplicatedEntry = FindReplicatedEntry(ItemInstance->GetItemEntry()->GetItemGuid()); + + if (!ReplicatedEntry) + return; + + if (!bRemove) + { + ItemInstance->GetItemEntry()->GetCount() += New; + ReplicatedEntry->GetCount() += New; + } + else + { + ItemInstance->GetItemEntry()->GetCount() -= New; + ReplicatedEntry->GetCount() -= New; + } + + if (outEntries) + *outEntries = { ItemInstance->GetItemEntry(), ReplicatedEntry}; + + if (bUpdate || !outEntries) + { + GetItemList().MarkItemDirty(ItemInstance->GetItemEntry()); + GetItemList().MarkItemDirty(ReplicatedEntry); + } +} + UFortItem* AFortInventory::FindItemInstance(const FGuid& Guid) { auto& ItemInstances = GetItemList().GetItemInstances(); @@ -57,5 +146,20 @@ UFortItem* AFortInventory::FindItemInstance(const FGuid& Guid) return ItemInstance; } + return nullptr; +} + +FFortItemEntry* AFortInventory::FindReplicatedEntry(const FGuid& Guid) +{ + auto& ReplicatedEntries = GetItemList().GetReplicatedEntries(); + + for (int i = 0; i < ReplicatedEntries.Num(); i++) + { + auto& ReplicatedEntry = ReplicatedEntries.At(i); + + if (ReplicatedEntry.GetItemGuid() == Guid) + return &ReplicatedEntry; + } + return nullptr; } \ No newline at end of file diff --git a/Project Reboot 3.0/FortInventory.h b/Project Reboot 3.0/FortInventory.h index 192ad60..ffdd2e0 100644 --- a/Project Reboot 3.0/FortInventory.h +++ b/Project Reboot 3.0/FortInventory.h @@ -9,6 +9,34 @@ #include "reboot.h" + +static bool IsPrimaryQuickbar(UFortItemDefinition* ItemDefinition) +{ + /* if (ItemDefinition->IsA(UFortDecoItemDefinition::StaticClass())) + { + if (ItemDefinition->IsA(UFortTrapItemDefinition::StaticClass())) + return false; + else + return true; + } + else if (ItemDefinition->IsA(UFortWeaponItemDefinition::StaticClass())) + return true; */ + + static auto FortWeaponMeleeItemDefinitionClass = FindObject("/Script/FortniteGame.FortWeaponMeleeItemDefinition"); + static auto FortEditToolItemDefinitionClass = FindObject("/Script/FortniteGame.FortEditToolItemDefinition"); + static auto FortBuildingItemDefinitionClass = FindObject("/Script/FortniteGame.FortBuildingItemDefinition"); + static auto FortAmmoItemDefinitionClass = FindObject("/Script/FortniteGame.FortAmmoItemDefinition"); + static auto FortResourceItemDefinitionClass = FindObject("/Script/FortniteGame.FortResourceItemDefinition"); + static auto FortTrapItemDefinitionClass = FindObject("/Script/FortniteGame.FortTrapItemDefinition"); + + if (!ItemDefinition->IsA(FortWeaponMeleeItemDefinitionClass) && !ItemDefinition->IsA(FortEditToolItemDefinitionClass) && + !ItemDefinition->IsA(FortBuildingItemDefinitionClass) && !ItemDefinition->IsA(FortAmmoItemDefinitionClass) + && !ItemDefinition->IsA(FortResourceItemDefinitionClass) && !ItemDefinition->IsA(FortTrapItemDefinitionClass)) + return true; + + return false; +} + enum class EFortInventoryType : unsigned char { World = 0, @@ -25,10 +53,10 @@ struct FFortItemList : public FFastArraySerializer return *(TArray*)(__int64(this) + ItemInstancesOffset); } - TArray& GetReplicatedEntries() + TArray& GetReplicatedEntries() { static auto ReplicatedEntriesOffset = FindOffsetStruct("/Script/FortniteGame.FortItemList", "ReplicatedEntries"); - return *(TArray*)(__int64(this) + ReplicatedEntriesOffset); + return *(TArray*)(__int64(this) + ReplicatedEntriesOffset); } }; @@ -63,7 +91,10 @@ public: } } - std::pair, std::vector> AddItem(UFortItemDefinition* ItemDefinition, bool* bShouldUpdate, int Count = 1); - + std::pair, std::vector> AddItem(UFortItemDefinition* ItemDefinition, bool* bShouldUpdate, int Count = 1, int LoadedAmmo = -1, bool bShouldAddToStateValues = false); + bool RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int Count); + void ModifyCount(UFortItem* ItemInstance, int New, bool bRemove = false, std::pair* outEntries = nullptr, bool bUpdate = true); + UFortItem* FindItemInstance(const FGuid& Guid); + FFortItemEntry* FindReplicatedEntry(const FGuid& Guid); }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortItem.h b/Project Reboot 3.0/FortItem.h index 2acdf6a..93f6a71 100644 --- a/Project Reboot 3.0/FortItem.h +++ b/Project Reboot 3.0/FortItem.h @@ -13,10 +13,10 @@ struct FFortItemEntry : FFastArraySerializerItem return *(FGuid*)(__int64(this) + ItemGuidOffset); } - UObject*& GetItemDefinition() + class UFortItemDefinition*& GetItemDefinition() { static auto ItemDefinitionOffset = FindOffsetStruct("/Script/FortniteGame.FortItemEntry", "ItemDefinition"); - return *(UObject**)(__int64(this) + ItemDefinitionOffset); + return *(class UFortItemDefinition**)(__int64(this) + ItemDefinitionOffset); } int& GetCount() diff --git a/Project Reboot 3.0/FortItemDefinition.h b/Project Reboot 3.0/FortItemDefinition.h index 21fb8a9..91bd7a8 100644 --- a/Project Reboot 3.0/FortItemDefinition.h +++ b/Project Reboot 3.0/FortItemDefinition.h @@ -11,4 +11,11 @@ class UFortItemDefinition : public UObject public: UFortItem* CreateTemporaryItemInstanceBP(int Count, int Level = 1); float GetMaxStackSize(); + + bool DoesAllowMultipleStacks() + { + static auto bAllowMultipleStacksOffset = GetOffset("bAllowMultipleStacks"); + static auto bAllowMultipleStacksFieldMask = GetFieldMask(GetProperty("bAllowMultipleStacks")); + return ReadBitfieldValue(bAllowMultipleStacksOffset, bAllowMultipleStacksFieldMask); + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp index 903b10d..392fad0 100644 --- a/Project Reboot 3.0/FortLootPackage.cpp +++ b/Project Reboot 3.0/FortLootPackage.cpp @@ -122,8 +122,7 @@ std::vector> PickLootDrops(FName TierGroupN if (TierGroupLTDs.size() == 0) { - // std::cout << "Failed to find LTD!\n"; - std::cout << "Failed to find any LTD for: " << TierGroupName.ToString() << '\n'; + LOG_WARN(LogLoot, "Failed to find any LTD for: {}", TierGroupName.ToString()); return LootDrops; } @@ -253,7 +252,9 @@ std::vector> PickLootDrops(FName TierGroupN std::cout << "b: " << b << '\n'; */ } - LootDrops.reserve(NumLootPackageDrops); + static int AmountToAdd = Engine_Version >= 424 ? 1 : 0; // fr + + LootDrops.reserve(NumLootPackageDrops + AmountToAdd); for (float i = 0; i < NumLootPackageDrops; i++) { @@ -261,7 +262,8 @@ std::vector> PickLootDrops(FName TierGroupN break; auto TierGroupLP = TierGroupLPs.at(i); - auto TierGroupLPStr = TierGroupLP->GetLootPackageCall().ToString(); + auto& LootPackageCallFStr = TierGroupLP->GetLootPackageCall(); + auto TierGroupLPStr = LootPackageCallFStr.IsValid() ? LootPackageCallFStr.ToString() : ".Empty"; if (TierGroupLPStr.contains(".Empty")) { diff --git a/Project Reboot 3.0/FortPickup.cpp b/Project Reboot 3.0/FortPickup.cpp index 592840e..0918d11 100644 --- a/Project Reboot 3.0/FortPickup.cpp +++ b/Project Reboot 3.0/FortPickup.cpp @@ -3,6 +3,8 @@ #include "FortPawn.h" #include "FortItemDefinition.h" #include "FortPlayerState.h" +#include "FortPlayerPawn.h" +#include "FortPlayerController.h" void AFortPickup::TossPickup(FVector FinalLocation, AFortPawn* ItemOwner, int OverrideMaxStackCount, bool bToss, EFortPickupSourceTypeFlag InPickupSourceTypeFlags, EFortPickupSpawnSource InPickupSpawnSource) { @@ -23,9 +25,8 @@ AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Loca if (auto Pickup = GetWorld()->SpawnActor(FortPickupClass, Location)) { static auto PawnWhoDroppedPickupOffset = Pickup->GetOffset("PawnWhoDroppedPickup"); - static auto PrimaryPickupItemEntryOffset = Pickup->GetOffset("PrimaryPickupItemEntry"); - auto PrimaryPickupItemEntry = Pickup->GetPtr(PrimaryPickupItemEntryOffset); + auto PrimaryPickupItemEntry = Pickup->GetPrimaryPickupItemEntry(); PrimaryPickupItemEntry->GetCount() = Count; PrimaryPickupItemEntry->GetItemDefinition() = ItemDef; @@ -33,7 +34,6 @@ AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Loca // Pickup->OptionalOwnerID = Pawn ? PlayerState->WorldPlayerId : -1; Pickup->Get(PawnWhoDroppedPickupOffset) = Pawn; - // Pickup->bCombinePickupsWhenTossCompletes = true; bool bToss = true; @@ -55,4 +55,156 @@ AFortPickup* AFortPickup::SpawnPickup(UFortItemDefinition* ItemDef, FVector Loca } return nullptr; +} + +char AFortPickup::CompletePickupAnimationHook(AFortPickup* Pickup) +{ + auto Pawn = Cast(Pickup->GetPickupLocationData()->GetPickupTarget()); + + if (!Pawn) + return CompletePickupAnimationOriginal(Pickup); + + auto PlayerController = Cast(Pawn->GetController()); + + if (!PlayerController) + return CompletePickupAnimationOriginal(Pickup); + + auto WorldInventory = PlayerController->GetWorldInventory(); + + if (!WorldInventory) + return CompletePickupAnimationOriginal(Pickup); + + auto PickupEntry = Pickup->GetPrimaryPickupItemEntry(); + + auto PickupItemDefinition = Cast(PickupEntry->GetItemDefinition()); + auto IncomingCount = PickupEntry->GetCount(); + + if (!PickupItemDefinition) + return CompletePickupAnimationOriginal(Pickup); + + auto& ItemInstances = WorldInventory->GetItemList().GetItemInstances(); + + auto& CurrentItemGuid = Pickup->GetPickupLocationData()->GetPickupGuid(); // Pawn->CurrentWeapon->ItemEntryGuid; + + auto ItemInstanceToSwap = WorldInventory->FindItemInstance(CurrentItemGuid); + auto ItemEntryToSwap = ItemInstanceToSwap->GetItemEntry(); + auto ItemDefinitionToSwap = ItemEntryToSwap ? Cast(ItemEntryToSwap->GetItemDefinition()) : nullptr; + bool bHasSwapped = false; + + int cpyCount = IncomingCount; + + auto PawnLoc = Pawn->GetActorLocation(); + auto ItemDefGoingInPrimary = IsPrimaryQuickbar(PickupItemDefinition); + + std::vector> PairsToMarkDirty; // vector of sets or something so no duplicates?? + + bool bForceOverflow = false; + + while (cpyCount > 0) + { + int PrimarySlotsFilled = 0; + bool bEverStacked = false; + bool bDoesStackExist = false; + + bool bIsInventoryFull = false; + + for (int i = 0; i < ItemInstances.Num(); i++) + { + auto ItemInstance = ItemInstances.at(i); + auto CurrentItemEntry = ItemInstance->GetItemEntry(); + + if (ItemDefGoingInPrimary && IsPrimaryQuickbar(CurrentItemEntry->GetItemDefinition())) + { + PrimarySlotsFilled++; + } + + bIsInventoryFull = (PrimarySlotsFilled /* - 6 */) >= 5; + + if (bIsInventoryFull) // probs shouldnt do in loop but alr + { + if (ItemInstanceToSwap && ItemDefinitionToSwap->CanBeDropped() && !bHasSwapped) + { + auto SwappedPickup = SpawnPickup(ItemDefinitionToSwap, PawnLoc, ItemEntryToSwap->GetCount(), + EFortPickupSourceTypeFlag::Player, EFortPickupSpawnSource::Unset, ItemEntryToSwap->GetLoadedAmmo(), Pawn); + + WorldInventory->RemoveItem(CurrentItemGuid, nullptr, ItemEntryToSwap->GetCount()); + + bHasSwapped = true; + + continue; // ??? + } + } + + if (CurrentItemEntry->GetItemDefinition() == PickupItemDefinition) + { + + if (CurrentItemEntry->GetCount() < PickupItemDefinition->GetMaxStackSize()) + { + int OverStack = CurrentItemEntry->GetCount() + cpyCount - PickupItemDefinition->GetMaxStackSize(); + int AmountToStack = OverStack > 0 ? cpyCount - OverStack : cpyCount; + + cpyCount -= AmountToStack; + + std::pair Pairs; + WorldInventory->ModifyCount(ItemInstance, AmountToStack, false, &Pairs, false); + PairsToMarkDirty.push_back(Pairs); + + bEverStacked = true; + + // if (cpyCount > 0) + // break; + } + + bDoesStackExist = true; + } + + if ((bIsInventoryFull || bForceOverflow) && cpyCount > 0) // overflow + { + UFortWorldItemDefinition* ItemDefinitionToSpawn = PickupItemDefinition; + int AmountToSpawn = cpyCount > PickupItemDefinition->GetMaxStackSize() ? PickupItemDefinition->GetMaxStackSize() : cpyCount; + + SpawnPickup(ItemDefinitionToSpawn, PawnLoc, AmountToSpawn, EFortPickupSourceTypeFlag::Player, EFortPickupSpawnSource::Unset, -1, Pawn); + cpyCount -= AmountToSpawn; + bForceOverflow = false; + } + + if (cpyCount <= 0) + break; + } + + if (cpyCount > 0 && !bIsInventoryFull) + { + if (bDoesStackExist ? PickupItemDefinition->DoesAllowMultipleStacks() : true) + { + auto NewItemCount = cpyCount > PickupItemDefinition->GetMaxStackSize() ? PickupItemDefinition->GetMaxStackSize() : cpyCount; + + auto NewItem = WorldInventory->AddItem(PickupItemDefinition, nullptr, + NewItemCount, PickupEntry->GetLoadedAmmo(), true); + + // if (NewItem) + cpyCount -= NewItemCount; + } + else + { + bForceOverflow = true; + } + } + } + + // auto Item = GiveItem(PlayerController, ItemDef, cpyCount, CurrentPickup->PrimaryPickupItemEntry.LoadedAmmo, true); + + /* for (int i = 0; i < Pawn->IncomingPickups.Num(); i++) + { + Pawn->IncomingPickups[i]->PickupLocationData.PickupGuid = Item->ItemEntry.ItemGuid; + } */ + + WorldInventory->Update(PairsToMarkDirty.size() == 0); + + for (auto& [key, value] : PairsToMarkDirty) + { + WorldInventory->GetItemList().MarkItemDirty(key); + WorldInventory->GetItemList().MarkItemDirty(value); + } + + return CompletePickupAnimationOriginal(Pickup); } \ No newline at end of file diff --git a/Project Reboot 3.0/FortPickup.h b/Project Reboot 3.0/FortPickup.h index 17d35e4..e559985 100644 --- a/Project Reboot 3.0/FortPickup.h +++ b/Project Reboot 3.0/FortPickup.h @@ -1,6 +1,7 @@ #pragma once #include "Actor.h" +#include "FortPawn.h" enum class EFortPickupSourceTypeFlag : uint8_t { @@ -28,12 +29,61 @@ enum class EFortPickupSpawnSource : uint8_t ENUM_CLASS_FLAGS(EFortPickupSourceTypeFlag) +struct FFortPickupLocationData +{ + AFortPawn*& GetPickupTarget() + { + static auto PickupTargetOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "PickupTarget"); + return *(AFortPawn**)(__int64(this) + PickupTargetOffset); + } + + float& GetFlyTime() + { + static auto FlyTimeOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "FlyTime"); + return *(float*)(__int64(this) + FlyTimeOffset); + } + + AFortPawn*& GetItemOwner() + { + static auto ItemOwnerOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "ItemOwner"); + return *(AFortPawn**)(__int64(this) + ItemOwnerOffset); + } + + FVector& GetStartDirection() + { + static auto StartDirectionOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "StartDirection"); + return *(FVector*)(__int64(this) + StartDirectionOffset); + } + + FGuid& GetPickupGuid() + { + static auto PickupGuidOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "PickupGuid"); + return *(FGuid*)(__int64(this) + PickupGuidOffset); + } +}; + class AFortPickup : public AActor { public: + static inline char (*CompletePickupAnimationOriginal)(AFortPickup* Pickup); + void TossPickup(FVector FinalLocation, class AFortPawn* ItemOwner, int OverrideMaxStackCount, bool bToss, EFortPickupSourceTypeFlag InPickupSourceTypeFlags, EFortPickupSpawnSource InPickupSpawnSource); + FFortPickupLocationData* GetPickupLocationData() + { + static auto PickupLocationDataOffset = this->GetOffset("PickupLocationData"); + return this->GetPtr(PickupLocationDataOffset); + } + + FFortItemEntry* GetPrimaryPickupItemEntry() + { + static auto PrimaryPickupItemEntryOffset = this->GetOffset("PrimaryPickupItemEntry"); + return this->GetPtr(PrimaryPickupItemEntryOffset); + } + static AFortPickup* SpawnPickup(class UFortItemDefinition* ItemDef, FVector Location, int Count, EFortPickupSourceTypeFlag PickupSource = EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource SpawnSource = EFortPickupSpawnSource::Unset, int LoadedAmmo = -1, class AFortPawn* Pawn = nullptr); + + static char CompletePickupAnimationHook(AFortPickup* Pickup); }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 57557a7..5a7bf05 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -91,15 +91,15 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* { static auto bAlreadySearchedOffset = BuildingContainer->GetOffset("bAlreadySearched"); static auto SearchBounceDataOffset = BuildingContainer->GetOffset("SearchBounceData"); + static auto bAlreadySearchedFieldMask = GetFieldMask(BuildingContainer->GetProperty("bAlreadySearched")); static auto SearchAnimationCountOffset = FindOffsetStruct("/Script/FortniteGame.FortSearchBounceData", "SearchAnimationCount"); - static auto bAlreadySearchedFieldMask = GetFieldMask(BuildingContainer->GetProperty(bAlreadySearchedOffset)); auto SearchBounceData = BuildingContainer->GetPtr(SearchBounceDataOffset); if (BuildingContainer->ReadBitfieldValue(bAlreadySearchedOffset, bAlreadySearchedFieldMask)) return; - LOG_INFO(LogInteraction, "bAlreadySearchedFieldMask: {}", bAlreadySearchedFieldMask); + // LOG_INFO(LogInteraction, "bAlreadySearchedFieldMask: {}", bAlreadySearchedFieldMask); BuildingContainer->SetBitfieldValue(bAlreadySearchedOffset, bAlreadySearchedFieldMask, true); (*(int*)(__int64(SearchBounceData) + SearchAnimationCountOffset))++; @@ -112,7 +112,9 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* auto LootDrops = PickLootDrops(RedirectedLootTier); - FVector LocationToSpawnLoot = BuildingContainer->GetActorLocation(); + LOG_INFO(LogInteraction, "LootDrops.size(): {}", LootDrops.size()); + + FVector LocationToSpawnLoot = BuildingContainer->GetActorLocation() + BuildingContainer->GetActorRightVector() * 70.f + FVector{0, 0, 50}; for (int i = 0; i < LootDrops.size(); i++) { @@ -120,6 +122,9 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* // , (AFortPawn*)PlayerController->GetPawn() // should we put this here? ); } + + // if (BuildingContainer->ShouldDestroyOnSearch()) + // BuildingContainer->K2_DestroyActor(); } return ServerAttemptInteractOriginal(Context, Stack, Ret); @@ -224,6 +229,41 @@ void AFortPlayerController::ServerCreateBuildingActorHook(AFortPlayerController* BuildingActor->InitializeBuildingActor(PlayerController, BuildingActor, true); } +void AFortPlayerController::ServerAttemptInventoryDropHook(AFortPlayerController* PlayerController, FGuid ItemGuid, int Count) +{ + LOG_INFO(LogDev, "ServerAttemptInventoryDropHook!"); + + auto Pawn = PlayerController->GetMyFortPawn(); + + if (Count < 0 || !Pawn) + return; + + auto WorldInventory = PlayerController->GetWorldInventory(); + auto ReplicatedEntry = WorldInventory->FindReplicatedEntry(ItemGuid); + + if (!ReplicatedEntry) + return; + + auto ItemDefinition = Cast(ReplicatedEntry->GetItemDefinition()); + + if (!ItemDefinition) + return; + + auto Pickup = AFortPickup::SpawnPickup(ItemDefinition, Pawn->GetActorLocation(), Count, + EFortPickupSourceTypeFlag::Player, EFortPickupSpawnSource::Unset, ReplicatedEntry->GetLoadedAmmo(), Pawn); + + if (!Pickup) + return; + + bool bShouldUpdate = false; + + if (!WorldInventory->RemoveItem(ItemGuid, &bShouldUpdate, Count)) + return; + + if (bShouldUpdate) + WorldInventory->Update(); +} + void AFortPlayerController::ServerPlayEmoteItemHook(AFortPlayerController* PlayerController, UObject* EmoteAsset) { auto PlayerState = (AFortPlayerStateAthena*)PlayerController->GetPlayerState(); diff --git a/Project Reboot 3.0/FortPlayerController.h b/Project Reboot 3.0/FortPlayerController.h index 247a7c0..34762b1 100644 --- a/Project Reboot 3.0/FortPlayerController.h +++ b/Project Reboot 3.0/FortPlayerController.h @@ -62,6 +62,7 @@ public: static void ServerAttemptAircraftJumpHook(AFortPlayerController* PC, FRotator ClientRotation); static void ServerCreateBuildingActorHook(AFortPlayerController* PlayerController, FCreateBuildingActorData CreateBuildingData); + static void ServerAttemptInventoryDropHook(AFortPlayerController* PlayerController, FGuid ItemGuid, int Count); static void ServerPlayEmoteItemHook(AFortPlayerController* PlayerController, UObject* EmoteAsset); static void ClientOnPawnDiedHook(AFortPlayerController* PlayerController, __int64 DeathReport); diff --git a/Project Reboot 3.0/FortPlayerPawn.cpp b/Project Reboot 3.0/FortPlayerPawn.cpp index 43c8f54..6ce13e4 100644 --- a/Project Reboot 3.0/FortPlayerPawn.cpp +++ b/Project Reboot 3.0/FortPlayerPawn.cpp @@ -73,22 +73,15 @@ void AFortPlayerPawn::ServerHandlePickupHook(AFortPlayerPawn* Pawn, AFortPickup* return; static auto IncomingPickupsOffset = Pawn->GetOffset("IncomingPickups"); - static auto PickupLocationDataOffset = Pickup->GetOffset("PickupLocationData"); - auto PickupLocationData = Pickup->GetPtr<__int64>(PickupLocationDataOffset); - Pawn->Get>(IncomingPickupsOffset).Add(Pickup); - static auto PickupTargetOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "PickupTarget"); - static auto FlyTimeOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "FlyTime"); - static auto ItemOwnerOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "ItemOwner"); - static auto StartDirectionOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "StartDirection"); - static auto PickupGuidOffset = FindOffsetStruct("/Script/FortniteGame.FortPickupLocationData", "PickupGuid"); + auto PickupLocationData = Pickup->GetPickupLocationData(); - *(AFortPawn**)(__int64(PickupLocationData) + PickupTargetOffset) = Pawn; - *(float*)(__int64(PickupLocationData) + FlyTimeOffset) = 0.40f; - *(AFortPawn**)(__int64(PickupLocationData) + ItemOwnerOffset) = Pawn; - *(FVector*)(__int64(PickupLocationData) + StartDirectionOffset) = InStartDirection; - *(FGuid*)(__int64(PickupLocationData) + PickupGuidOffset) = Pawn->GetCurrentWeapon() ? Pawn->GetCurrentWeapon()->GetItemEntryGuid() : FGuid(); + PickupLocationData->GetPickupTarget() = Pawn; + PickupLocationData->GetFlyTime() = 0.40f; + PickupLocationData->GetItemOwner() = Pawn; + PickupLocationData->GetStartDirection() = InStartDirection; + PickupLocationData->GetPickupGuid() = Pawn->GetCurrentWeapon() ? Pawn->GetCurrentWeapon()->GetItemEntryGuid() : FGuid(); static auto OnRep_PickupLocationDataFn = FindObject(L"/Script/FortniteGame.FortPickup.OnRep_PickupLocationData"); Pickup->ProcessEvent(OnRep_PickupLocationDataFn); diff --git a/Project Reboot 3.0/GameModeBase.cpp b/Project Reboot 3.0/GameModeBase.cpp index d90ea42..477882b 100644 --- a/Project Reboot 3.0/GameModeBase.cpp +++ b/Project Reboot 3.0/GameModeBase.cpp @@ -17,37 +17,41 @@ APawn* AGameModeBase::SpawnDefaultPawnForHook(AGameModeBase* GameMode, AControll GameMode->ProcessEvent(fn, &AGameModeBase_SpawnDefaultPawnAtTransform_Params); - auto NewPlayerAsAthena = Cast(NewPlayer); + bool bIsRespawning = false; - if (NewPlayerAsAthena) + if (!bIsRespawning) { - auto WorldInventory = NewPlayerAsAthena->GetWorldInventory(); + auto NewPlayerAsAthena = Cast(NewPlayer); - auto CosmeticLoadoutPickaxe = NewPlayerAsAthena->GetCosmeticLoadout()->GetPickaxe(); - static auto WeaponDefinitionOffset = FindOffsetStruct("/Script/FortniteGame.AthenaPickaxeItemDefinition", "WeaponDefinition"); + if (NewPlayerAsAthena) + { + auto WorldInventory = NewPlayerAsAthena->GetWorldInventory(); - auto Pickaxe = CosmeticLoadoutPickaxe ? CosmeticLoadoutPickaxe->Get(WeaponDefinitionOffset) - : FindObject(L"/Game/Athena/Items/Weapons/WID_Harvest_Pickaxe_Athena_C_T01.WID_Harvest_Pickaxe_Athena_C_T01"); + auto CosmeticLoadoutPickaxe = NewPlayerAsAthena->GetCosmeticLoadout()->GetPickaxe(); + static auto WeaponDefinitionOffset = FindOffsetStruct("/Script/FortniteGame.AthenaPickaxeItemDefinition", "WeaponDefinition"); - static UFortItemDefinition* EditToolItemDefinition = FindObject(L"/Game/Items/Weapons/BuildingTools/EditTool.EditTool"); - static UFortItemDefinition* PickaxeDefinition = Pickaxe; - static UFortItemDefinition* BuildingItemData_Wall = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_Wall.BuildingItemData_Wall"); - static UFortItemDefinition* BuildingItemData_Floor = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_Floor.BuildingItemData_Floor"); - static UFortItemDefinition* BuildingItemData_Stair_W = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_Stair_W.BuildingItemData_Stair_W"); - static UFortItemDefinition* BuildingItemData_RoofS = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_RoofS.BuildingItemData_RoofS"); - static UFortItemDefinition* WoodItemData = FindObject(L"/Game/Items/ResourcePickups/WoodItemData.WoodItemData"); + auto Pickaxe = CosmeticLoadoutPickaxe ? CosmeticLoadoutPickaxe->Get(WeaponDefinitionOffset) + : FindObject(L"/Game/Athena/Items/Weapons/WID_Harvest_Pickaxe_Athena_C_T01.WID_Harvest_Pickaxe_Athena_C_T01"); - WorldInventory->AddItem(EditToolItemDefinition, nullptr); - WorldInventory->AddItem(BuildingItemData_Wall, nullptr); - WorldInventory->AddItem(BuildingItemData_Floor, nullptr); - WorldInventory->AddItem(BuildingItemData_Stair_W, nullptr); - WorldInventory->AddItem(BuildingItemData_RoofS, nullptr); - WorldInventory->AddItem(PickaxeDefinition, nullptr); - WorldInventory->AddItem(WoodItemData, nullptr, 100); + static UFortItemDefinition* EditToolItemDefinition = FindObject(L"/Game/Items/Weapons/BuildingTools/EditTool.EditTool"); + static UFortItemDefinition* PickaxeDefinition = Pickaxe; + static UFortItemDefinition* BuildingItemData_Wall = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_Wall.BuildingItemData_Wall"); + static UFortItemDefinition* BuildingItemData_Floor = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_Floor.BuildingItemData_Floor"); + static UFortItemDefinition* BuildingItemData_Stair_W = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_Stair_W.BuildingItemData_Stair_W"); + static UFortItemDefinition* BuildingItemData_RoofS = FindObject(L"/Game/Items/Weapons/BuildingTools/BuildingItemData_RoofS.BuildingItemData_RoofS"); + static UFortItemDefinition* WoodItemData = FindObject(L"/Game/Items/ResourcePickups/WoodItemData.WoodItemData"); - WorldInventory->Update(true); + WorldInventory->AddItem(EditToolItemDefinition, nullptr); + WorldInventory->AddItem(BuildingItemData_Wall, nullptr); + WorldInventory->AddItem(BuildingItemData_Floor, nullptr); + WorldInventory->AddItem(BuildingItemData_Stair_W, nullptr); + WorldInventory->AddItem(BuildingItemData_RoofS, nullptr); + WorldInventory->AddItem(PickaxeDefinition, nullptr); + WorldInventory->AddItem(WoodItemData, nullptr, 100); + + WorldInventory->Update(true); + } } - return AGameModeBase_SpawnDefaultPawnAtTransform_Params.ReturnValue; } \ No newline at end of file diff --git a/Project Reboot 3.0/GameplayAbilitySpec.h b/Project Reboot 3.0/GameplayAbilitySpec.h index 68c7ee3..c035ab1 100644 --- a/Project Reboot 3.0/GameplayAbilitySpec.h +++ b/Project Reboot 3.0/GameplayAbilitySpec.h @@ -11,8 +11,15 @@ struct FGameplayAbilitySpecHandle void GenerateNewHandle() { - static int GHandle = 1; - Handle = ++GHandle; + if (true) + { + Handle = rand(); + } + else + { + static int GHandle = 1; + Handle = ++GHandle; + } } }; @@ -23,7 +30,7 @@ struct FGameplayAbilitySpec : FFastArraySerializerItem static auto GameplayAbilitySpecStruct = FindObject("/Script/GameplayAbilities.GameplayAbilitySpec"); static auto StructSize = GameplayAbilitySpecStruct->GetPropertiesSize(); // *(int*)(__int64(GameplayAbilitySpecStruct) + Offsets::PropertiesSize); - LOG_INFO(LogAbility, "StructSize: {}", StructSize); + // LOG_INFO(LogAbilities, "StructSize: {}", StructSize); return StructSize; } diff --git a/Project Reboot 3.0/Object.cpp b/Project Reboot 3.0/Object.cpp index 6063196..75b3359 100644 --- a/Project Reboot 3.0/Object.cpp +++ b/Project Reboot 3.0/Object.cpp @@ -17,37 +17,6 @@ FName* getFNameOfProp(void* Property) return NamePrivate; }; -void* UObject::GetProperty(int Offset, bool bWarnIfNotFound) -{ - for (auto CurrentClass = ClassPrivate; CurrentClass; CurrentClass = *(UClass**)(__int64(CurrentClass) + Offsets::SuperStruct)) - { - void* Property = *(void**)(__int64(CurrentClass) + Offsets::Children); - - if (Property) - { - if (*(int*)(__int64(Property) + Offsets::Offset_Internal) == Offset) - { - return Property; - } - - while (Property) - { - if (*(int*)(__int64(Property) + Offsets::Offset_Internal) == Offset) - { - return Property; - } - - Property = Engine_Version >= 425 ? *(void**)(__int64(Property) + 0x20) : ((UField*)Property)->Next; - } - } - } - - if (bWarnIfNotFound) - LOG_WARN(LogFinder, "Unable to find2{}", Offset); - - return 0; -} - void* UObject::GetProperty(const std::string& ChildName, bool bWarnIfNotFound) { for (auto CurrentClass = ClassPrivate; CurrentClass; CurrentClass = *(UClass**)(__int64(CurrentClass) + Offsets::SuperStruct)) diff --git a/Project Reboot 3.0/Object.h b/Project Reboot 3.0/Object.h index 30562ab..b8eb8cd 100644 --- a/Project Reboot 3.0/Object.h +++ b/Project Reboot 3.0/Object.h @@ -44,7 +44,6 @@ public: bool IsA(UClass* Other); - void* GetProperty(int Offset, bool bWarnIfNotFound = true); void* GetProperty(const std::string& ChildName, bool bWarnIfNotFound = true); int GetOffset(const std::string& ChildName, bool bWarnIfNotFound = true); diff --git a/Project Reboot 3.0/Pawn.h b/Project Reboot 3.0/Pawn.h index 4ce8c76..40eadf2 100644 --- a/Project Reboot 3.0/Pawn.h +++ b/Project Reboot 3.0/Pawn.h @@ -10,4 +10,10 @@ public: static auto PlayerStateOffset = GetOffset("PlayerState"); return Get(PlayerStateOffset); } + + class APlayerController* GetController() + { + static auto ControllerOffset = GetOffset("Controller"); + return Get(ControllerOffset); + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index 799ce02..f563441 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -213,6 +213,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 17d8857..f52385c 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -386,6 +386,9 @@ FortniteGame\Source\FortniteGame\Public\Items + + FortniteGame\Source\FortniteGame\Public + diff --git a/Project Reboot 3.0/Vector.h b/Project Reboot 3.0/Vector.h index 596a5d9..3b9092b 100644 --- a/Project Reboot 3.0/Vector.h +++ b/Project Reboot 3.0/Vector.h @@ -6,4 +6,34 @@ public: float X; float Y; float Z; + + FVector operator+(const FVector& A) + { + return FVector{ this->X + A.X, this->Y + A.Y, this->Z + A.Z }; + } + + FVector operator-(const FVector& A) + { + return FVector{ this->X - A.X, this->Y - A.Y, this->Z - A.Z }; + } + + FVector operator*(const float A) + { + return FVector{ this->X * A, this->Y * A, this->Z * A }; + } + + bool operator==(const FVector& A) + { + return X == A.X && Y == A.Y && Z == A.Z; + } + + void operator+=(const FVector& A) + { + *this = *this + A; + } + + void operator-=(const FVector& A) + { + *this = *this - A; + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index 20ab664..0b56445 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -194,6 +194,7 @@ void Addresses::FindAll() Addresses::NavSystemCleanUp = FindNavSystemCleanUp(); Addresses::LoadPlayset = FindLoadPlayset(); Addresses::SetZoneToIndex = FindSetZoneToIndex(); + Addresses::CompletePickupAnimation = FindCompletePickupAnimation(); } void Addresses::Print() @@ -231,6 +232,7 @@ void Addresses::Print() LOG_INFO(LogDev, "NavSystemCleanUp: 0x{:x}", NavSystemCleanUp - Base); LOG_INFO(LogDev, "LoadPlayset: 0x{:x}", LoadPlayset - Base); LOG_INFO(LogDev, "SetZoneToIndex: 0x{:x}", SetZoneToIndex - Base); + LOG_INFO(LogDev, "CompletePickupAnimation: 0x{:x}", CompletePickupAnimation - Base); } void Offsets::FindAll() @@ -292,6 +294,7 @@ void Addresses::Init() FMemory::Realloc = decltype(FMemory::Realloc)(Realloc); UAbilitySystemComponent::GiveAbilityOriginal = decltype(UAbilitySystemComponent::GiveAbilityOriginal)(GiveAbility); UAbilitySystemComponent::InternalTryActivateAbilityOriginal = decltype(UAbilitySystemComponent::InternalTryActivateAbilityOriginal)(InternalTryActivateAbility); + UAbilitySystemComponent::InternalTryActivateAbilityOriginal2 = decltype(UAbilitySystemComponent::InternalTryActivateAbilityOriginal2)(InternalTryActivateAbility); ABuildingActor::OnDamageServerOriginal = decltype(ABuildingActor::OnDamageServerOriginal)(OnDamageServer); StaticLoadObjectOriginal = decltype(StaticLoadObjectOriginal)(StaticLoadObject); @@ -350,6 +353,11 @@ std::vector Addresses::GetFunctionsToNull() toNull.push_back(Memcury::Scanner::FindPattern("48 8B C4 48 89 70 08 48 89 78 10 55 41 54 41 55 41 56 41 57 48 8D 68 A1 48 81 EC ? ? ? ? 45 33 ED").Get()); } + if (Fortnite_Version == 17.50) + { + toNull.push_back(Memcury::Scanner::FindPattern("48 8B C4 48 89 58 08 48 89 70 10 48 89 78 18 4C 89 60 20 55 41 56 41 57 48 8B EC 48 83 EC 60 49 8B D9 45 8A").Get()); // no reservation in game + } + toNull.push_back(Addresses::ChangeGameSessionId); return toNull; diff --git a/Project Reboot 3.0/addresses.h b/Project Reboot 3.0/addresses.h index 8fb4119..d0734ef 100644 --- a/Project Reboot 3.0/addresses.h +++ b/Project Reboot 3.0/addresses.h @@ -41,6 +41,7 @@ namespace Addresses extern inline uint64 NavSystemCleanUp = 0; extern inline uint64 LoadPlayset = 0; extern inline uint64 SetZoneToIndex = 0; + extern inline uint64 CompletePickupAnimation = 0; void SetupVersion(); // Finds Engine Version void FindAll(); diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index 65ba7b5..191f47c 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -80,18 +80,17 @@ DWORD WINAPI Main(LPVOID) { if (Hooking::MinHook::Hook((PVOID)Addresses::NoMCP, (PVOID)NoMCPHook, nullptr)) { - LOG_INFO(LogHook, "Hooking GetNetMode!"); Hooking::MinHook::Hook((PVOID)Addresses::GetNetMode, (PVOID)GetNetModeHook, nullptr); } } else { - Hooking::MinHook::Hook((PVOID)Addresses::KickPlayer, (PVOID)AGameSession::KickPlayerHook, (PVOID*)&AGameSession::KickPlayerOriginal); Hooking::MinHook::Hook((PVOID)Addresses::DispatchRequest, (PVOID)DispatchRequestHook, (PVOID*)&DispatchRequestOriginal); Hooking::MinHook::Hook((PVOID)Addresses::GetNetMode, (PVOID)GetNetModeHook, nullptr); - LOG_INFO(LogHook, "Hooking GetNetMode!"); } + Hooking::MinHook::Hook((PVOID)Addresses::KickPlayer, (PVOID)AGameSession::KickPlayerHook, (PVOID*)&AGameSession::KickPlayerOriginal); + LOG_INFO(LogDev, "Size: 0x{:x}", sizeof(TMap)); GetLocalPlayerController()->ProcessEvent(SwitchLevel, &Level); @@ -168,6 +167,10 @@ DWORD WINAPI Main(LPVOID) AFortPlayerController::ServerAttemptInteractHook, (PVOID*)&AFortPlayerController::ServerAttemptInteractOriginal, false, true); } + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/Engine.PlayerController.ServerAcknowledgePossession"), + AFortPlayerControllerAthena::ServerAcknowledgePossessionHook, nullptr, false); + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerAttemptInventoryDrop"), + AFortPlayerController::ServerAttemptInventoryDropHook, nullptr, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerExecuteInventoryItem"), AFortPlayerController::ServerExecuteInventoryItemHook, nullptr, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerPlayEmoteItem"), @@ -180,8 +183,6 @@ DWORD WINAPI Main(LPVOID) AFortPlayerController::ServerEditBuildingActorHook, nullptr, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerEndEditingBuildingActor"), AFortPlayerController::ServerEndEditingBuildingActorHook, nullptr, false); - Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/Engine.PlayerController.ServerAcknowledgePossession"), - AFortPlayerControllerAthena::ServerAcknowledgePossessionHook, nullptr, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerAthena.ServerPlaySquadQuickChatMessage"), AFortPlayerControllerAthena::ServerPlaySquadQuickChatMessage, nullptr, false); @@ -224,6 +225,7 @@ DWORD WINAPI Main(LPVOID) // Hooking::MinHook::Hook((PVOID)Addresses::CollectGarbage, (PVOID)CollectGarbageHook, nullptr); Hooking::MinHook::Hook((PVOID)Addresses::PickTeam, (PVOID)AFortGameModeAthena::Athena_PickTeamHook, nullptr); Hooking::MinHook::Hook((PVOID)Addresses::SetZoneToIndex, (PVOID)AFortGameModeAthena::SetZoneToIndexHook, (PVOID*)&AFortGameModeAthena::SetZoneToIndexOriginal); + Hooking::MinHook::Hook((PVOID)Addresses::CompletePickupAnimation, (PVOID)AFortPickup::CompletePickupAnimationHook, (PVOID*)&AFortPickup::CompletePickupAnimationOriginal); srand(time(0)); diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index dc65fea..22ef530 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -16,8 +16,8 @@ static inline uintptr_t FindBytes(Memcury::Scanner& Scanner, const std::vector("/Script/FortniteGame.FortKismetLibrary.IsRunningNoMCP"); @@ -568,22 +573,27 @@ static inline uint64 FindInternalTryActivateAbility() return FindBytes(Addr, { 0x4C, 0x89, 0x4C }, 1000, 0, true); } -static inline uint64 FindGiveAbility() -{ - auto Addr = Memcury::Scanner::FindStringRef(L"GiveAbilityAndActivateOnce called on ability %s on the client, not allowed!"); - auto /* callToGiveAbility */ realGiveAbility = Memcury::Scanner(FindBytes(Addr, { 0xE8 }, 1000, 0)).RelativeOffset(1).Get(); - // auto realGiveAbility = ((callToGiveAbility + 1 + 4) + *(int*)(callToGiveAbility + 1)); - return realGiveAbility; -} - static inline uint64 FindGiveAbilityAndActivateOnce() { + if (Engine_Version == 426) + return Memcury::Scanner::FindPattern("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 40 49 8B 40 10 49 8B D8 48 8B FA 48 8B F1").Get(); + auto Addr = Memcury::Scanner::FindStringRef(L"GiveAbilityAndActivateOnce called on ability %s on the client, not allowed!", true, 0, Engine_Version >= 500); auto res = FindBytes(Addr, { 0x48, 0x89, 0x5C }, 1000, 0, true); return res; } +static inline uint64 FindGiveAbility() +{ + // auto Addr = Memcury::Scanner::FindStringRef(L"GiveAbilityAndActivateOnce called on ability %s on the client, not allowed!"); // has 2 refs for some reason on some versions + // auto realGiveAbility = Memcury::Scanner(FindBytes(Addr, { 0xE8 }, 500, 0, false, 0, true)).RelativeOffset(1).Get(); + + Memcury::Scanner addr = Memcury::Scanner(FindGiveAbilityAndActivateOnce()); + + return Memcury::Scanner(FindBytes(addr, { 0xE8 }, 500, 0, false, 1)).RelativeOffset(1).Get(); +} + static inline uint64 FindCantBuild() { auto add = Memcury::Scanner::FindPattern("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 41 56 48 83 EC ? 49 8B E9 4D 8B F0", false).Get(); diff --git a/Project Reboot 3.0/globals.h b/Project Reboot 3.0/globals.h index fc00458..9c301c7 100644 --- a/Project Reboot 3.0/globals.h +++ b/Project Reboot 3.0/globals.h @@ -4,7 +4,7 @@ namespace Globals { extern inline bool bCreative = false; extern inline bool bGoingToPlayEvent = false; - extern inline bool bNoMCP = false; - extern inline bool bLateGame = false; + extern inline bool bNoMCP = true; + extern inline bool bLateGame = false; extern inline bool bAbilitiesEnabled = true; } \ No newline at end of file diff --git a/Project Reboot 3.0/log.h b/Project Reboot 3.0/log.h index 3c4de9d..d12315f 100644 --- a/Project Reboot 3.0/log.h +++ b/Project Reboot 3.0/log.h @@ -71,6 +71,7 @@ inline void InitLogger() MakeLogger("LogGame"); MakeLogger("LogAI"); MakeLogger("LogInteraction"); + MakeLogger("LogCreative"); } #define LOG_DEBUG(loggerName, ...) \ diff --git a/Project Reboot 3.0/reboot.h b/Project Reboot 3.0/reboot.h index e7f45a5..7a728c5 100644 --- a/Project Reboot 3.0/reboot.h +++ b/Project Reboot 3.0/reboot.h @@ -134,7 +134,7 @@ struct PlaceholderBitfield uint8_t Eighth : 1; }; -inline uint8_t GetFieldMask(void* Property) +inline uint8_t GetFieldMask(void* Property, int additional = 0) { if (!Property) return -1; @@ -142,11 +142,11 @@ inline uint8_t GetFieldMask(void* Property) // 3 = sizeof(FieldSize) + sizeof(ByteOffset) + sizeof(ByteMask) if (Engine_Version <= 420) - return *(uint8_t*)(__int64(Property) + (112 + 3)); + return *(uint8_t*)(__int64(Property) + (112 + 3 + additional)); else if (Engine_Version >= 421 && Engine_Version <= 424) - return *(uint8_t*)(__int64(Property) + (112 + 3)); + return *(uint8_t*)(__int64(Property) + (112 + 3 + additional)); else if (Engine_Version >= 425) - return *(uint8_t*)(__int64(Property) + (120 + 3)); + return *(uint8_t*)(__int64(Property) + (120 + 3 + additional)); return -1; }