From cc1963a4c40c205ec9334d1d5e8bfd632723fc29 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:30:48 +0300 Subject: [PATCH 01/15] save game base --- .../G2I/Private/Game/G2IGameplaySaveGame.cpp | 5 ++ .../G2I/Private/Game/G2ISettingsSaveGame.cpp | 5 ++ .../SavingSystem/G2ISavableInterface.cpp | 6 ++ .../SavingSystem/G2ISaveGameplayInterface.cpp | 6 ++ .../SavingSystem/G2ISaveSettingsInterface.cpp | 6 ++ Source/G2I/Public/Game/G2IGameInstance.h | 3 +- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 17 ++++++ Source/G2I/Public/Game/G2ISettingsSaveGame.h | 17 ++++++ .../SavingSystem/G2ISavableInterface.h | 25 +++++++++ .../SavingSystem/G2ISaveGameplayInterface.h | 55 +++++++++++++++++++ .../SavingSystem/G2ISaveSettingsInterface.h | 25 +++++++++ 11 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 Source/G2I/Private/Game/G2IGameplaySaveGame.cpp create mode 100644 Source/G2I/Private/Game/G2ISettingsSaveGame.cpp create mode 100644 Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp create mode 100644 Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp create mode 100644 Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp create mode 100644 Source/G2I/Public/Game/G2IGameplaySaveGame.h create mode 100644 Source/G2I/Public/Game/G2ISettingsSaveGame.h create mode 100644 Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h create mode 100644 Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h create mode 100644 Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h diff --git a/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp b/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp new file mode 100644 index 00000000..51a17276 --- /dev/null +++ b/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp @@ -0,0 +1,5 @@ + + + +#include "Game/G2IGameplaySaveGame.h" + diff --git a/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp b/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp new file mode 100644 index 00000000..d651b736 --- /dev/null +++ b/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp @@ -0,0 +1,5 @@ + + + +#include "Game/G2ISettingsSaveGame.h" + diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp new file mode 100644 index 00000000..711ba3ae --- /dev/null +++ b/Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp @@ -0,0 +1,6 @@ + + + +#include "Interfaces/SavingSystem/G2ISavableInterface.h" + +// Add default functionality here for any IG2ISavableInterface functions that are not pure virtual. diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp new file mode 100644 index 00000000..1e2ac933 --- /dev/null +++ b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp @@ -0,0 +1,6 @@ + + + +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" + +// Add default functionality here for any IG2ISaveGameplayInterface functions that are not pure virtual. diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp new file mode 100644 index 00000000..d1fb6fc1 --- /dev/null +++ b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp @@ -0,0 +1,6 @@ + + + +#include "Interfaces/SavingSystem/G2ISaveSettingsInterface.h" + +// Add default functionality here for any IG2ISaveSettingsInterface functions that are not pure virtual. diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index db3c1aaa..922b3281 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -2,6 +2,8 @@ #include "CoreMinimal.h" #include "Engine/GameInstance.h" +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "Interfaces/SavingSystem/G2ISaveSettingsInterface.h" #include "G2IGameInstance.generated.h" class UG2IWidgetComponentParameters; @@ -34,5 +36,4 @@ class G2I_API UG2IGameInstance : public UGameInstance UG2IStringTablesCatalog *GetStringTablesCatalog(); UG2IWidgetComponentParameters *GetWidgetComponentParameters(); - }; diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h new file mode 100644 index 00000000..a3feb0c4 --- /dev/null +++ b/Source/G2I/Public/Game/G2IGameplaySaveGame.h @@ -0,0 +1,17 @@ + + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/SaveGame.h" +#include "G2IGameplaySaveGame.generated.h" + +/** + * SaveGame for gameplay data. + */ +UCLASS() +class G2I_API UG2IGameplaySaveGame : public USaveGame +{ + GENERATED_BODY() + +}; diff --git a/Source/G2I/Public/Game/G2ISettingsSaveGame.h b/Source/G2I/Public/Game/G2ISettingsSaveGame.h new file mode 100644 index 00000000..890366ff --- /dev/null +++ b/Source/G2I/Public/Game/G2ISettingsSaveGame.h @@ -0,0 +1,17 @@ + + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/SaveGame.h" +#include "G2ISettingsSaveGame.generated.h" + +/** + * SaveGame for settings data. + */ +UCLASS() +class G2I_API UG2ISettingsSaveGame : public USaveGame +{ + GENERATED_BODY() + +}; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h new file mode 100644 index 00000000..2445334d --- /dev/null +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h @@ -0,0 +1,25 @@ + + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "G2ISavableInterface.generated.h" + +// This class does not need to be modified. +UINTERFACE(MinimalAPI) +class UG2ISavableInterface : public UInterface +{ + GENERATED_BODY() +}; + +/** + * + */ +class G2I_API IG2ISavableInterface +{ + GENERATED_BODY() + + // Add interface functions to this class. This is the class that will be inherited to implement this interface. +public: +}; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h new file mode 100644 index 00000000..573b31a3 --- /dev/null +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h @@ -0,0 +1,55 @@ + + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "Delegates/Delegate.h" +#include "G2ISaveGameplayInterface.generated.h" + +DECLARE_MULTICAST_DELEGATE_OneParam(FOnGameplaySavedSignature, bool /* bSuccess */); +DECLARE_MULTICAST_DELEGATE_OneParam(FOnGameplayLoadedSignature, bool /* bSuccess */); + +// This class does not need to be modified. +UINTERFACE(MinimalAPI) +class UG2ISaveGameplayInterface : public UInterface +{ + GENERATED_BODY() +}; + +/* + Allows to save, load, sync data with SaveGame object that corresponds to storing gameplay data + (such as player's location, inventory, etc.) and to return delegates of that actions. + */ +class G2I_API IG2ISaveGameplayInterface +{ + GENERATED_BODY() + +public: + // Creates save game object + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void CreateGameplaySaveGameObject(); + + // Saves game + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void SaveGameplay(bool bAsync); + + // Loads game + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void LoadGameplay(bool bAsync); + + // Syncs game data + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void SyncGameplaySaveGameData(); + + // Syncs data & saves game + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void SuncAndSaveGameplay(bool bAsync); + + // Returns OnGameplaySaved Delegate. Not Blueprint compatible. + FOnGameplaySavedSignature GetGameplaySavedDelegate(); + + // Returns OnGameplayLoaded Delegate. Not Blueprint compatible. + FOnGameplayLoadedSignature GetGameplayLoadedDelegate(); + +}; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h new file mode 100644 index 00000000..17ff2769 --- /dev/null +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h @@ -0,0 +1,25 @@ + + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "G2ISaveSettingsInterface.generated.h" + +// This class does not need to be modified. +UINTERFACE(MinimalAPI) +class UG2ISaveSettingsInterface : public UInterface +{ + GENERATED_BODY() +}; + +/* + Allows to save, load, sync data with SaveGame object that corresponds to storing settings data + (such as music volume, key bindings, etc.) and to return delegates of that actions. + */ +class G2I_API IG2ISaveSettingsInterface +{ + GENERATED_BODY() + +public: +}; From 52a15cef9805ac0ad12b37dad7e6573024deb101 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Sun, 22 Feb 2026 13:34:43 +0300 Subject: [PATCH 02/15] feat: savable tag + interface --- Config/Tags/G2IGameplayTags.ini | 2 +- .../Interfaces/SavingSystem/G2ISavableInterface.h | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Config/Tags/G2IGameplayTags.ini b/Config/Tags/G2IGameplayTags.ini index e8462b40..07e3f1e2 100644 --- a/Config/Tags/G2IGameplayTags.ini +++ b/Config/Tags/G2IGameplayTags.ini @@ -1,4 +1,4 @@ [/Script/GameplayTags.GameplayTagsList] GameplayTagList=(Tag="Interaction.Pipes.TechnicalHoles",DevComment="Allows interacting with technical holes") GameplayTagList=(Tag="Interaction.Pipes.Valves",DevComment="Allows interacting with valves") - +GameplayTagList=(Tag="Savable",DevComment="Is required for saving/loading data") diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h index 2445334d..255a86d6 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h @@ -1,9 +1,9 @@ - #pragma once #include "CoreMinimal.h" #include "UObject/Interface.h" +#include "GameFramework/SaveGame.h" #include "G2ISavableInterface.generated.h" // This class does not need to be modified. @@ -14,12 +14,18 @@ class UG2ISavableInterface : public UInterface }; /** - * + * Interface that every object that needs to be saved have to implement */ class G2I_API IG2ISavableInterface { GENERATED_BODY() - // Add interface functions to this class. This is the class that will be inherited to implement this interface. public: + // Adds/updates needed data to the save game object + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Savable") + void SaveData(const USaveGame* SaveGameRef); + + // Loads needed data from the save game object + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Savable") + void LoadData(const USaveGame* SaveGameRef); }; From 681bb14c1c7f4be4cc8820df97669b27aaa8f62c Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Sun, 22 Feb 2026 13:35:16 +0300 Subject: [PATCH 03/15] feat: save gameplay save & load functions --- Source/G2I/Private/Game/G2IGameInstance.cpp | 242 ++++++++++++++++++ .../SavingSystem/G2ISaveGameplayInterface.cpp | 6 - Source/G2I/Public/Game/G2IGameInstance.h | 86 ++++++- .../SavingSystem/G2ISaveGameplayInterface.h | 65 +++-- 4 files changed, 377 insertions(+), 22 deletions(-) delete mode 100644 Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index e1b29542..4d3704a5 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -1,4 +1,9 @@ #include "G2IGameInstance.h" +#include "G2I.h" +#include "Kismet/GameplayStatics.h" +#include "GameplayTagContainer.h" +#include "BlueprintGameplayTagLibrary.h" +#include "Interfaces/SavingSystem/G2ISavableInterface.h" UG2IWidgetsCatalog* UG2IGameInstance::GetWidgetsCatalog() { @@ -14,3 +19,240 @@ UG2IWidgetComponentParameters* UG2IGameInstance::GetWidgetComponentParameters() { return WidgetComponentsParameters; } + + +void UG2IGameInstance::CreateNewGameplaySaveGameObject_Implementation() +{ + if (UGameplayStatics::DoesSaveGameExist(GameplaySaveSlotName, 0)) + UGameplayStatics::DeleteGameInSlot(GameplaySaveSlotName, 0); + + CreateGameplaySaveGame(); +} + +void UG2IGameInstance::Init() +{ + Super::Init(); + + CreateSaveGameplayDelegates(); + + OnGameplayAsyncSavedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncSaved); + OnGameplayAsyncLoadedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncLoaded); + + /*if (UGameplayStatics::DoesSaveGameExist(GameplaySaveSlotName, 0)) + { + // Load? + // Check if it's valid? + // idk? + } + else + { + CreateGameplaySaveGame(); + }*/ +} + +void UG2IGameInstance::CreateGameplaySaveGame() +{ + GameplaySaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UG2IGameplaySaveGame::StaticClass())); + if (!ensure(GameplaySaveGame)) + UE_LOG(LogG2I, Error, TEXT("Couldn't create GameplaySaveGame object.")); +} + +void UG2IGameInstance::CreateSaveGameplayDelegates() +{ + SaveGameplayDelegates = NewObject(this); + if (!ensure(SaveGameplayDelegates)) + UE_LOG(LogG2I, Error, TEXT("Couldn't create SaveGameplayDelegates object.")); +} + +void UG2IGameInstance::OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess) +{ + SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(bSuccess); +} + +void UG2IGameInstance::OnGameplayAsuncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData) +{ + if (LoadedGameData) + { + if (GameplaySaveGame = Cast(LoadedGameData)) + { + // Successfully loaded + } + else + { + // SaveGame file exists, but not valid + // TODO + } + + SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(true); + SyncGameplayLoadGameData(); + } + else + { + // Load failed + SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); + } +} + +void UG2IGameInstance::SaveGameplay_Implementation(bool bAsync) +{ + if (!ensure(GameplaySaveGame)) + CreateGameplaySaveGame(); + if (!ensure(SaveGameplayDelegates)) + CreateSaveGameplayDelegates(); + + if (bAsync) + { + // Asynchronous + SaveGameplayDelegates->OnGameplaySaveStartedDelegate.Broadcast(); + UGameplayStatics::AsyncSaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0, OnGameplayAsyncSavedDelegate); + } + else + { + // Synchronous + SaveGameplayDelegates->OnGameplaySaveStartedDelegate.Broadcast(); + if (ensure(UGameplayStatics::SaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0))) + { + // Successfully saved + SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(true); + UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); + } + else + { + // Failed to save + SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(false); + UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); + } + } +} + +void UG2IGameInstance::SetGameplaySaveSlotName_Implementation(const FString& NewSlotName) +{ + GameplaySaveSlotName = NewSlotName; +} + +const FString UG2IGameInstance::GetGameplaySaveSlotName_Implementation() +{ + return GameplaySaveSlotName; +} + +const UG2ISaveGameplayDelegates* UG2IGameInstance::GetGameplaySaveDelegates_Implementation() +{ + return SaveGameplayDelegates; +} + +void UG2IGameInstance::LoadGameplay_Implementation(bool bAsync) +{ + if (!ensure(SaveGameplayDelegates)) + CreateSaveGameplayDelegates(); + + if (bAsync) + { + // Asynchronous + SaveGameplayDelegates->OnGameplayLoadStartedDelegate.Broadcast(); + UGameplayStatics::AsyncLoadGameFromSlot(GameplaySaveSlotName, 0, OnGameplayAsyncLoadedDelegate); + } + else + { + // Synchronous + SaveGameplayDelegates->OnGameplayLoadStartedDelegate.Broadcast(); + if (USaveGame* LoadedGame = UGameplayStatics::LoadGameFromSlot(GameplaySaveSlotName, 0)) + { + if (GameplaySaveGame = Cast(LoadedGame)) + { + // Successfully loaded + SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(true); + SyncGameplayLoadGameData(); + UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); + } + else + { + // Loaded SaveGame object, but it's invalid + // TODO + } + } + else + { + // Failed to load + SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); + UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); + + // TODO maybe create new SaveGame object? + } + } +} + +void UG2IGameInstance::SyncGameplaySaveGameData_Implementation() +{ + // Getting all actors with tag 'Savable' + TArray FoundSavableActors; + GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); + + // Iterating on them & saving their data + for (auto* Actor : FoundSavableActors) + { + if (Actor->Implements()) + { + IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Error, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + } + + UE_LOG(LogG2I, Log, TEXT("Gameplay data synced & stored in GameplaySaveGame object.")); +} + +void UG2IGameInstance::SyncGameplayLoadGameData() +{ + // Getting all actors with tag 'Savable' + TArray FoundSavableActors; + GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); + + // Iterating on them & loading their data + for (auto* Actor : FoundSavableActors) + { + if (Actor->Implements()) + { + IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Error, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + } + + UE_LOG(LogG2I, Log, TEXT("Gameplay data synced & loaded in Savable objects.")); +} + +void UG2IGameInstance::GetAllActorsWithGameplayTag(TArray& FoundActors, FName TagName) +{ + if (!ensure(GameplaySaveGame)) + CreateGameplaySaveGame(); + + UWorld* World = GetWorld(); + if (!World) + return; + + TArray FoundSavableActors; + FGameplayTagQuery TagQuery = FGameplayTagQuery::MakeQuery_MatchTag(FGameplayTag::RequestGameplayTag(TagName)); + + UBlueprintGameplayTagLibrary::GetAllActorsOfClassMatchingTagQuery( + World, + AActor::StaticClass(), + TagQuery, + FoundActors + ); +} + +void UG2IGameInstance::SuncAndSaveGameplay_Implementation(bool bAsync) +{ + IG2ISaveGameplayInterface::Execute_SyncGameplaySaveData(this); + IG2ISaveGameplayInterface::Execute_SaveGameplay(this, bAsync); +} + +void UG2IGameInstance::CheckIfGameplaySaveGameExistsAndValid() +{ + +} + +void UG2IGameInstance::CheckIfSettingsSaveGameExistsAndValid() +{ + +} diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp deleted file mode 100644 index 1e2ac933..00000000 --- a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveGameplayInterface.cpp +++ /dev/null @@ -1,6 +0,0 @@ - - - -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" - -// Add default functionality here for any IG2ISaveGameplayInterface functions that are not pure virtual. diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index 922b3281..52739bed 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -2,8 +2,12 @@ #include "CoreMinimal.h" #include "Engine/GameInstance.h" +#include "Kismet/GameplayStatics.h" +#include "GameFramework/SaveGame.h" #include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" #include "Interfaces/SavingSystem/G2ISaveSettingsInterface.h" +#include "Game/G2IGameplaySaveGame.h" +#include "Game/G2ISettingsSaveGame.h" #include "G2IGameInstance.generated.h" class UG2IWidgetComponentParameters; @@ -12,9 +16,10 @@ class UG2IWidgetsCatalog; /** * Base game instance for G2I game + * saves and loads gameplay & settings data */ UCLASS() -class G2I_API UG2IGameInstance : public UGameInstance +class G2I_API UG2IGameInstance : public UGameInstance, public IG2ISaveGameplayInterface { GENERATED_BODY() @@ -36,4 +41,83 @@ class G2I_API UG2IGameInstance : public UGameInstance UG2IStringTablesCatalog *GetStringTablesCatalog(); UG2IWidgetComponentParameters *GetWidgetComponentParameters(); + + // Class that holds SaveGameplay-related delegates + UPROPERTY(BlueprintReadOnly, Category = "Gameplay Save Game") + TObjectPtr SaveGameplayDelegates; + +private: + // SaveGame w/ gameplay info + UPROPERTY() + TObjectPtr GameplaySaveGame; + + // SaveGame w/ settings + UPROPERTY() + TObjectPtr SettingsSaveGame; + + // Name of the current slot for the gameplay save game + UPROPERTY() + FString GameplaySaveSlotName = TEXT("GameplaySaveSlot0"); + + // Name of the current slot for the gameplay save game + UPROPERTY() + FString SettingsSaveSlotName = TEXT("SettingsSaveSlot0"); + + // C++-only delegate called from AsyncSaveGameToSlot + FAsyncSaveGameToSlotDelegate OnGameplayAsyncSavedDelegate; + + // C++-only delegate called from AsyncLoadGameToSlot + FAsyncLoadGameFromSlotDelegate OnGameplayAsyncLoadedDelegate; + +public: + + // === Implementation of the IG2ISaveGameplayInterface' functions === + + void CreateNewGameplaySaveGameObject_Implementation(); + + void SaveGameplay_Implementation(bool bAsync); + + void SetGameplaySaveSlotName_Implementation(const FString& NewSlotName); + + const FString GetGameplaySaveSlotName_Implementation(); + + void LoadGameplay_Implementation(bool bAsync); + + void SyncGameplaySaveGameData_Implementation(); + + void SuncAndSaveGameplay_Implementation(bool bAsync); + + const UG2ISaveGameplayDelegates* GetGameplaySaveDelegates_Implementation(); + + // === Other Functions === + + // This function not only checks, it creates new SaveGame if it doesn't exist yet. + // If SaveGame exists, but not valid, adds missing data, keeping everything else untouched. + UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") + void CheckIfGameplaySaveGameExistsAndValid(); + + // This function not only checks, it creates new SaveGame if it doesn't exist yet. + // If SaveGame exists, but not valid, adds missing data, keeping everything else untouched. + UFUNCTION(BlueprintCallable, Category = "Settings Save Game") + void CheckIfSettingsSaveGameExistsAndValid(); + +protected: + virtual void Init() override; + + UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") + void CreateGameplaySaveGame(); + + UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") + void CreateSaveGameplayDelegates(); + + UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") + void SyncGameplayLoadGameData(); + + // Finding all actors that have gameplay tag 'TagName' + UFUNCTION(BlueprintCallable) + void GetAllActorsWithGameplayTag(TArray& FoundActors, FName TagName); + + void OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess); + + void OnGameplayAsuncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData); }; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h index 573b31a3..60255871 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h @@ -1,14 +1,44 @@ - #pragma once #include "CoreMinimal.h" #include "UObject/Interface.h" +//#include "Kismet/GameplayStatics.h" #include "Delegates/Delegate.h" #include "G2ISaveGameplayInterface.generated.h" -DECLARE_MULTICAST_DELEGATE_OneParam(FOnGameplaySavedSignature, bool /* bSuccess */); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnGameplayLoadedSignature, bool /* bSuccess */); + +// === Delegates === + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplaySavedDelegate, bool, bSuccess); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplaySaveStartedDelegate); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplayLoadedDelegate, bool, bSuccess); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplayLoadStartedDelegate); + +// Wrapper class for save gameplay delegates so that we can return it in the interface function +UCLASS(Blueprintable) +class G2I_API UG2ISaveGameplayDelegates : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") + FOnGameplaySavedDelegate OnGameplaySavedDelegate; + + UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") + FOnGameplaySaveStartedDelegate OnGameplaySaveStartedDelegate; + + UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") + FOnGameplayLoadedDelegate OnGameplayLoadedDelegate; + + UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") + FOnGameplayLoadStartedDelegate OnGameplayLoadStartedDelegate; +}; + + + +// === Interface === + // This class does not need to be modified. UINTERFACE(MinimalAPI) @@ -26,30 +56,35 @@ class G2I_API IG2ISaveGameplayInterface GENERATED_BODY() public: - // Creates save game object + // Creates new save game object, sets everything as default + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void CreateNewGameplaySaveGameObject(); + + // Sets slot name UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void CreateGameplaySaveGameObject(); + void SetGameplaySaveSlotName(const FString& NewSlotName); + + // Returns slot name + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + const FString GetGameplaySaveSlotName(); // Saves game UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") void SaveGameplay(bool bAsync); - // Loads game + // Loads game & syncs loaded data with every object that has Savable interface UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") void LoadGameplay(bool bAsync); - // Syncs game data + // Updates & syncs data of every object with the Savable interface UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SyncGameplaySaveGameData(); + void SyncGameplaySaveData(); - // Syncs data & saves game + // Syncs all data & saves game UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") void SuncAndSaveGameplay(bool bAsync); - // Returns OnGameplaySaved Delegate. Not Blueprint compatible. - FOnGameplaySavedSignature GetGameplaySavedDelegate(); - - // Returns OnGameplayLoaded Delegate. Not Blueprint compatible. - FOnGameplayLoadedSignature GetGameplayLoadedDelegate(); - + // Returns class with all delegates + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + const UG2ISaveGameplayDelegates* GetGameplaySaveDelegates(); }; From 2ed2f1c29d62b74e36f8cf6b6d2bd69cd0713c06 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:50:01 +0300 Subject: [PATCH 04/15] refact: deleted spaces + player's locations for testing --- Source/G2I/Private/Game/G2IGameplaySaveGame.cpp | 3 --- Source/G2I/Private/Game/G2ISettingsSaveGame.cpp | 3 --- .../SavingSystem/G2ISaveSettingsInterface.cpp | 3 --- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 14 ++++++++++++-- Source/G2I/Public/Game/G2ISettingsSaveGame.h | 2 -- .../Interfaces/SavingSystem/G2ISavableInterface.h | 1 - .../SavingSystem/G2ISaveGameplayInterface.h | 1 - .../SavingSystem/G2ISaveSettingsInterface.h | 2 -- 8 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp b/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp index 51a17276..464201ce 100644 --- a/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp +++ b/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp @@ -1,5 +1,2 @@ - - - #include "Game/G2IGameplaySaveGame.h" diff --git a/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp b/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp index d651b736..27acc25c 100644 --- a/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp +++ b/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp @@ -1,5 +1,2 @@ - - - #include "Game/G2ISettingsSaveGame.h" diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp index d1fb6fc1..49915a60 100644 --- a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp +++ b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp @@ -1,6 +1,3 @@ - - - #include "Interfaces/SavingSystem/G2ISaveSettingsInterface.h" // Add default functionality here for any IG2ISaveSettingsInterface functions that are not pure virtual. diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h index a3feb0c4..71550e60 100644 --- a/Source/G2I/Public/Game/G2IGameplaySaveGame.h +++ b/Source/G2I/Public/Game/G2IGameplaySaveGame.h @@ -1,11 +1,18 @@ - - #pragma once #include "CoreMinimal.h" #include "GameFramework/SaveGame.h" #include "G2IGameplaySaveGame.generated.h" +USTRUCT(BlueprintType) +struct FPlayersSaveData +{ + GENERATED_BODY() + + UPROPERTY(SaveGame, BlueprintReadWrite) + TMap PlayersLocation; +}; + /** * SaveGame for gameplay data. */ @@ -14,4 +21,7 @@ class G2I_API UG2IGameplaySaveGame : public USaveGame { GENERATED_BODY() +public: + UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Player") + FPlayersSaveData PlayersSaveData; }; diff --git a/Source/G2I/Public/Game/G2ISettingsSaveGame.h b/Source/G2I/Public/Game/G2ISettingsSaveGame.h index 890366ff..5cc5a8b1 100644 --- a/Source/G2I/Public/Game/G2ISettingsSaveGame.h +++ b/Source/G2I/Public/Game/G2ISettingsSaveGame.h @@ -1,5 +1,3 @@ - - #pragma once #include "CoreMinimal.h" diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h index 255a86d6..e8ea2fba 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h @@ -1,4 +1,3 @@ - #pragma once #include "CoreMinimal.h" diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h index 60255871..c047c258 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h @@ -1,4 +1,3 @@ - #pragma once #include "CoreMinimal.h" diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h index 17ff2769..469bf27b 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h @@ -1,5 +1,3 @@ - - #pragma once #include "CoreMinimal.h" From b85ac98cbbd56112cded80ba875ba386cdad3caa Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Fri, 6 Mar 2026 13:36:16 +0300 Subject: [PATCH 05/15] refact: saving gameplay --- Source/G2I/Private/Game/G2IGameInstance.cpp | 115 +++++++++++------- Source/G2I/Public/Game/G2IGameInstance.h | 24 ++-- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 5 +- .../SavingSystem/G2ISaveGameplayInterface.h | 28 +++-- 4 files changed, 102 insertions(+), 70 deletions(-) diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index 4d3704a5..d98474c6 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -34,20 +34,6 @@ void UG2IGameInstance::Init() Super::Init(); CreateSaveGameplayDelegates(); - - OnGameplayAsyncSavedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncSaved); - OnGameplayAsyncLoadedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncLoaded); - - /*if (UGameplayStatics::DoesSaveGameExist(GameplaySaveSlotName, 0)) - { - // Load? - // Check if it's valid? - // idk? - } - else - { - CreateGameplaySaveGame(); - }*/ } void UG2IGameInstance::CreateGameplaySaveGame() @@ -61,7 +47,13 @@ void UG2IGameInstance::CreateSaveGameplayDelegates() { SaveGameplayDelegates = NewObject(this); if (!ensure(SaveGameplayDelegates)) + { UE_LOG(LogG2I, Error, TEXT("Couldn't create SaveGameplayDelegates object.")); + return; + } + + OnGameplayAsyncSavedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncSaved); + OnGameplayAsyncLoadedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncLoaded); } void UG2IGameInstance::OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess) @@ -75,29 +67,29 @@ void UG2IGameInstance::OnGameplayAsuncLoaded(const FString& SlotName, const int3 { if (GameplaySaveGame = Cast(LoadedGameData)) { - // Successfully loaded + UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); + SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(true); } else { // SaveGame file exists, but not valid - // TODO + UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); + SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); } - - SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(true); - SyncGameplayLoadGameData(); } else { // Load failed + UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); } } void UG2IGameInstance::SaveGameplay_Implementation(bool bAsync) { - if (!ensure(GameplaySaveGame)) + if (!GameplaySaveGame) CreateGameplaySaveGame(); - if (!ensure(SaveGameplayDelegates)) + if (!SaveGameplayDelegates) CreateSaveGameplayDelegates(); if (bAsync) @@ -110,7 +102,7 @@ void UG2IGameInstance::SaveGameplay_Implementation(bool bAsync) { // Synchronous SaveGameplayDelegates->OnGameplaySaveStartedDelegate.Broadcast(); - if (ensure(UGameplayStatics::SaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0))) + if (UGameplayStatics::SaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0)) { // Successfully saved SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(true); @@ -142,7 +134,7 @@ const UG2ISaveGameplayDelegates* UG2IGameInstance::GetGameplaySaveDelegates_Impl void UG2IGameInstance::LoadGameplay_Implementation(bool bAsync) { - if (!ensure(SaveGameplayDelegates)) + if (!SaveGameplayDelegates) CreateSaveGameplayDelegates(); if (bAsync) @@ -154,36 +146,30 @@ void UG2IGameInstance::LoadGameplay_Implementation(bool bAsync) else { // Synchronous - SaveGameplayDelegates->OnGameplayLoadStartedDelegate.Broadcast(); if (USaveGame* LoadedGame = UGameplayStatics::LoadGameFromSlot(GameplaySaveSlotName, 0)) { if (GameplaySaveGame = Cast(LoadedGame)) { // Successfully loaded - SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(true); - SyncGameplayLoadGameData(); - UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); + UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); } else { // Loaded SaveGame object, but it's invalid - // TODO + UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); } } else { // Failed to load - SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); - UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); - - // TODO maybe create new SaveGame object? + UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); } } } -void UG2IGameInstance::SyncGameplaySaveGameData_Implementation() +void UG2IGameInstance::SaveAllData_Implementation() { - // Getting all actors with tag 'Savable' + // Getting all actors with gameplay tag 'Savable' TArray FoundSavableActors; GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); @@ -195,10 +181,30 @@ void UG2IGameInstance::SyncGameplaySaveGameData_Implementation() IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); } else - UE_LOG(LogG2I, Error, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + UE_LOG(LogG2I, Warning, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + } + + UE_LOG(LogG2I, Log, TEXT("Gameplay data saved & stored in GameplaySaveGame object.")); +} + +void UG2IGameInstance::LoadAllData_Implementation() +{ + // Getting all actors with gameplay tag 'Savable' + TArray FoundSavableActors; + GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); + + // Iterating on them & loading their data + for (auto* Actor : FoundSavableActors) + { + if (Actor->Implements()) + { + IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Warning, TEXT("Actor %s doesn't implement Savable interface. It's data won't be loaded."), *Actor->GetActorNameOrLabel()); } - UE_LOG(LogG2I, Log, TEXT("Gameplay data synced & stored in GameplaySaveGame object.")); + UE_LOG(LogG2I, Log, TEXT("Gameplay data loaded from the GameplaySaveGame object.")); } void UG2IGameInstance::SyncGameplayLoadGameData() @@ -215,7 +221,7 @@ void UG2IGameInstance::SyncGameplayLoadGameData() IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); } else - UE_LOG(LogG2I, Error, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + UE_LOG(LogG2I, Warning, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); } UE_LOG(LogG2I, Log, TEXT("Gameplay data synced & loaded in Savable objects.")); @@ -223,14 +229,13 @@ void UG2IGameInstance::SyncGameplayLoadGameData() void UG2IGameInstance::GetAllActorsWithGameplayTag(TArray& FoundActors, FName TagName) { - if (!ensure(GameplaySaveGame)) - CreateGameplaySaveGame(); - UWorld* World = GetWorld(); if (!World) + { + UE_LOG(LogG2I, Error, TEXT("World doesn't exist in %s."), *GetName()); return; + } - TArray FoundSavableActors; FGameplayTagQuery TagQuery = FGameplayTagQuery::MakeQuery_MatchTag(FGameplayTag::RequestGameplayTag(TagName)); UBlueprintGameplayTagLibrary::GetAllActorsOfClassMatchingTagQuery( @@ -241,18 +246,34 @@ void UG2IGameInstance::GetAllActorsWithGameplayTag(TArray& FoundActors, ); } -void UG2IGameInstance::SuncAndSaveGameplay_Implementation(bool bAsync) +void UG2IGameInstance::SaveAllDataAndGameplay_Implementation(bool bAsync) { - IG2ISaveGameplayInterface::Execute_SyncGameplaySaveData(this); + IG2ISaveGameplayInterface::Execute_SaveAllData(this); IG2ISaveGameplayInterface::Execute_SaveGameplay(this, bAsync); } -void UG2IGameInstance::CheckIfGameplaySaveGameExistsAndValid() +void UG2IGameInstance::SaveRequestedData_Implementation(UObject* Requester) { - + if (Requester) + { + if (Requester->Implements()) + { + IG2ISavableInterface::Execute_SaveData(Requester, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement Savable interface. It's data will be lost."), *Requester->GetName()); + } } -void UG2IGameInstance::CheckIfSettingsSaveGameExistsAndValid() +void UG2IGameInstance::LoadRequestedData_Implementation(UObject* Requester) { - + if (Requester) + { + if (Requester->Implements()) + { + IG2ISavableInterface::Execute_LoadData(Requester, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement Savable interface. It's data won't be loaded."), *Requester->GetName()); + } } diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index 52739bed..71c76f75 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -71,35 +71,31 @@ class G2I_API UG2IGameInstance : public UGameInstance, public IG2ISaveGameplayIn public: - // === Implementation of the IG2ISaveGameplayInterface' functions === + // === Implementations of the IG2ISaveGameplayInterface' functions === void CreateNewGameplaySaveGameObject_Implementation(); void SaveGameplay_Implementation(bool bAsync); + void LoadGameplay_Implementation(bool bAsync); + void SetGameplaySaveSlotName_Implementation(const FString& NewSlotName); const FString GetGameplaySaveSlotName_Implementation(); - void LoadGameplay_Implementation(bool bAsync); + void SaveAllData_Implementation(); - void SyncGameplaySaveGameData_Implementation(); + void LoadAllData_Implementation(); - void SuncAndSaveGameplay_Implementation(bool bAsync); + void SaveAllDataAndGameplay_Implementation(bool bAsync); - const UG2ISaveGameplayDelegates* GetGameplaySaveDelegates_Implementation(); + void SaveRequestedData_Implementation(UObject* Requester); - // === Other Functions === + void LoadRequestedData_Implementation(UObject* Requester); - // This function not only checks, it creates new SaveGame if it doesn't exist yet. - // If SaveGame exists, but not valid, adds missing data, keeping everything else untouched. - UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") - void CheckIfGameplaySaveGameExistsAndValid(); + const UG2ISaveGameplayDelegates* GetGameplaySaveDelegates_Implementation(); - // This function not only checks, it creates new SaveGame if it doesn't exist yet. - // If SaveGame exists, but not valid, adds missing data, keeping everything else untouched. - UFUNCTION(BlueprintCallable, Category = "Settings Save Game") - void CheckIfSettingsSaveGameExistsAndValid(); + // === Other Functions === protected: virtual void Init() override; diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h index 71550e60..e321025f 100644 --- a/Source/G2I/Public/Game/G2IGameplaySaveGame.h +++ b/Source/G2I/Public/Game/G2IGameplaySaveGame.h @@ -10,7 +10,10 @@ struct FPlayersSaveData GENERATED_BODY() UPROPERTY(SaveGame, BlueprintReadWrite) - TMap PlayersLocation; + TMap CharactersLocation; + + UPROPERTY(SaveGame, BlueprintReadWrite) + FString CurrentCharacter; }; /** diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h index c047c258..52d079e2 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h @@ -2,11 +2,9 @@ #include "CoreMinimal.h" #include "UObject/Interface.h" -//#include "Kismet/GameplayStatics.h" #include "Delegates/Delegate.h" #include "G2ISaveGameplayInterface.generated.h" - // === Delegates === DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplaySavedDelegate, bool, bSuccess); @@ -67,21 +65,35 @@ class G2I_API IG2ISaveGameplayInterface UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") const FString GetGameplaySaveSlotName(); - // Saves game + // Saves SaveGame to slot UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") void SaveGameplay(bool bAsync); - // Loads game & syncs loaded data with every object that has Savable interface + // Loads SaveGame from slot UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") void LoadGameplay(bool bAsync); - // Updates & syncs data of every object with the Savable interface + // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame (not in the slot though!) + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void SaveAllData(); + + // Loads data of all ACTORS on the level with Savable tag/interface from the SaveGame + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void LoadAllData(); + + // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame & saves SaveGame to slot + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") + void SaveAllDataAndGameplay(bool bAsync); + + // Saves Requester's data in the SaveGame object (not in the slot though!) + // This will be called when Requester needs it (in it's OnDestroyed, etc.) UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SyncGameplaySaveData(); + void SaveRequestedData(UObject* Requester); - // Syncs all data & saves game + // Loads Requester's data from the SaveGame object (not from the slot though!) + // This will be called when Requester needs it (in it's BeginPlay, etc.) UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SuncAndSaveGameplay(bool bAsync); + void LoadRequestedData(UObject* Requester); // Returns class with all delegates UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") From 6cb6d13ea7ba018e397755d212bd50407dc763b5 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Sun, 8 Mar 2026 23:16:53 +0300 Subject: [PATCH 06/15] feat: save actions + player's data --- .../Core/Input/Actions/IA_Load.uasset | Bin 0 -> 1474 bytes .../Core/Input/Actions/IA_Save.uasset | Bin 0 -> 1474 bytes .../Private/Controls/G2IPlayerController.cpp | 53 +++++++++++++++++- Source/G2I/Private/Game/G2IGameInstance.cpp | 11 ++-- .../G2I/Public/Controls/G2IPlayerController.h | 31 +++++++++- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 2 +- .../SavingSystem/G2ISavableInterface.h | 6 +- 7 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 Content/G2I_Game/Core/Input/Actions/IA_Load.uasset create mode 100644 Content/G2I_Game/Core/Input/Actions/IA_Save.uasset diff --git a/Content/G2I_Game/Core/Input/Actions/IA_Load.uasset b/Content/G2I_Game/Core/Input/Actions/IA_Load.uasset new file mode 100644 index 0000000000000000000000000000000000000000..36b850e2294b6983a6fcdf9a95568f276172bd6e GIT binary patch literal 1474 zcmb7ET}TvB6h8S^`IE9(6x2h#ST649itA5*&g$;6V5^q;(2GvvZQWdFmYLC72`Utl zf*^@{>mk^O9ty#SiXKABmm;W#2n-7PgQTcvv3jWOoI5kyD5&+p<=%78ch5QZo^NJm ztn%vjOeWKp3s9H?@Dpc3<*n)4Wuu+5!j^Q^f#I)v?v#D$3*`f>LEoo=ska~em(oRZ zHP^(uLC;R&;}Am95EDY)a4%o9s2UT(N@Ca$MA?X`iY|o3-ZoY0M=n4KjCNczwPGe=rcN4%GUCjjh2(v8A!T))x@{0iT#9BOeqjm8xAJ0};vlJ5xa0?!kQ; zM{$(b&wWeYNVQ*n)s#7Pa(s;P({U;$9}4h#=~ybqg_hgh9jJ33WN{|xtg6oA<9>k8 z&6CUc@es%O;QS;l&SAa__+(DtOK0dAX?xP)+km#HX;QL7Qxi-xk`SJH6C*-IRT!jm zXG==aXg_j{$wrnVzL=ihhT9#IJSat3n<`83*r-Hr9l=)U2}x$)GLjSPk<_KdqEV*l z@Va^E`v0}7^!~z=att9HlC@aE5U8`=W-ag|T(xhL!xvBnBt>TZyz%ht=A(}V=xvEh zx(>$i6BpN^Wy`=DdH=kaY!Zp75pZqpE2dn0&#JjYW4g}z;qklu=jpt4U(iDn8`0#V zv~>G#GNO_Q6pZi-+fryQc`$26T04wFQ?_qP7&XjX$qn$!HjGs!qRR?jM zz8z@W!doORk5e}E5mSZ;s(*x;a@*@T>5#04dn;CrSpg)89w|P|x{?XTi}I@~{ty)Y z-?1BvBmlg?BG4_UCL3Hq_9o3)IE@+n8(&#So%Za0@RUw(&9cnJ$jgS#yodL8Ra^VS F{{tHFB3}Ri literal 0 HcmV?d00001 diff --git a/Content/G2I_Game/Core/Input/Actions/IA_Save.uasset b/Content/G2I_Game/Core/Input/Actions/IA_Save.uasset new file mode 100644 index 0000000000000000000000000000000000000000..e80276d857ade4092a3c257096a7af2e151d2f83 GIT binary patch literal 1474 zcmb7EPe>GD6o08}<(~q{5UN8$ST64Dx~~7Ca#nO%uyxHphb}seU%I)@EHmTQcCtDo z2|;31mkxnM7cn|ThcN9@bm&k)QP96bQFvLbDBIrqW@Z}&wSMsNzW3*Q?|t9<&CH&w zy#6hd$@Jv{6yyN>KuIX?UznearhiRGMt#@H#)sy9KEE7t0c=3u*TI<&pMsO=!k4}q z;=NYSUbI!>5<=226GGMDUcPBoH710W#E2n?vJq1iT?mW4ol=rvEI=`?^OO!TibQn!nwAR>8xB_*`=9<hP;8O_im1?6gF09l=rP2}x#9Vx-14V^EhC8yH}k z4)2BnF4(c2uC zbRCS7r!H+m&89*8bzx2s=|mEfBT!P-S46eA&FVR-FvomARZ}YAxlrqaK~3IwYwsQJ!#SoqA>zz*_a}d)TonCDmLxXeSYl;-^w)^+B;t zUpeYF@s^Rx`yY2ZU-DE9hCKOZ^gPfD}X4`BgIEpS1Q4HQ$bb5AA-XF zJNAGv2mmjz40H?X$p&YTZ6uk6Q<=rT@s-7?QP2MQr*wK}Hp<*2-_&>HJ-WZoYuPFO E4`FT}5&!@I literal 0 HcmV?d00001 diff --git a/Source/G2I/Private/Controls/G2IPlayerController.cpp b/Source/G2I/Private/Controls/G2IPlayerController.cpp index c76b3824..036bdb01 100644 --- a/Source/G2I/Private/Controls/G2IPlayerController.cpp +++ b/Source/G2I/Private/Controls/G2IPlayerController.cpp @@ -92,6 +92,9 @@ void AG2IPlayerController::SetupInputComponent() &ThisClass::SwitchCameraBehavior); // TODO: Add Pause Action after adding all UI systems //EnhancedInputComponent->BindAction(DebugPauseAction, ETriggerEvent::Started,this, &ThisClass::CallPause); +#if WITH_EDITOR + EnhancedInputComponent->BindAction(SaveAction, ETriggerEvent::Triggered, this, &ThisClass::SaveGameplay); + EnhancedInputComponent->BindAction(LoadAction, ETriggerEvent::Triggered, this, &ThisClass::LoadGameplay); #endif } else @@ -129,6 +132,13 @@ void AG2IPlayerController::SetupInputComponent() AG2IPlayerController::AG2IPlayerController() { PlayerCameraManagerClass = AG2IPlayerCameraManager::StaticClass(); + + FGameplayTag SavableTag = UGameplayTagsManager::Get().RequestGameplayTag(FName("Savable")); + + if (SavableTag.IsValid()) + { + GameplayTags.AddTag(SavableTag); + } } void AG2IPlayerController::OnPossess(APawn* NewPawn) @@ -248,6 +258,25 @@ TMap, FName>& AG2IPlayerController::GetActionToTagMap() return ActionToTagMap; } +void AG2IPlayerController::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) +{ + if (SaveGameRef && GetPawn()) + { + SaveGameRef->PlayersSaveData.CurrentCharacter = GetPawn()->GetName(); + UE_LOG(LogG2I, Log, TEXT("PlayerController: %s saved."), *SaveGameRef->PlayersSaveData.CurrentCharacter); + } +} + +void AG2IPlayerController::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) +{ + if (SaveGameRef) + if (const APawn* CurrentCharacter = GetPawn()) + { + if (!CurrentCharacter->GetName().Equals(SaveGameRef->PlayersSaveData.CurrentCharacter)) + SelectNextCharacter(FInputActionValue()); + } +} + void AG2IPlayerController::SetupCharacterActorComponents() { ThirdPersonCameraComponents.Empty(); @@ -636,4 +665,26 @@ void AG2IPlayerController::GlovePunchActivation(const FInputActionInstance& Inst { IG2IGlovePunchInterface::Execute_GlovePunchActivation(GlovePunchComponent); } -} \ No newline at end of file +} + +void AG2IPlayerController::SaveGameplay(const FInputActionValue& Value) +{ + if (auto* GameInstance = GetGameInstance()) + { + if (GameInstance->Implements()) + IG2ISaveGameplayInterface::Execute_SaveAllDataAndGameplay(GameInstance, true); + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface."), *GameInstance->GetName()); + } +} + +void AG2IPlayerController::LoadGameplay(const FInputActionValue& Value) +{ + if (auto* GameInstance = GetGameInstance()) + { + if (GameInstance->Implements()) + IG2ISaveGameplayInterface::Execute_LoadRequestedData(GameInstance, this); + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface."), *GameInstance->GetName()); + } +} diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index d98474c6..e9f63598 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -2,6 +2,7 @@ #include "G2I.h" #include "Kismet/GameplayStatics.h" #include "GameplayTagContainer.h" +#include "GameplayTagsManager.h" #include "BlueprintGameplayTagLibrary.h" #include "Interfaces/SavingSystem/G2ISavableInterface.h" @@ -176,7 +177,7 @@ void UG2IGameInstance::SaveAllData_Implementation() // Iterating on them & saving their data for (auto* Actor : FoundSavableActors) { - if (Actor->Implements()) + if (Actor->Implements()) { IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); } @@ -196,7 +197,7 @@ void UG2IGameInstance::LoadAllData_Implementation() // Iterating on them & loading their data for (auto* Actor : FoundSavableActors) { - if (Actor->Implements()) + if (Actor->Implements()) { IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); } @@ -216,7 +217,7 @@ void UG2IGameInstance::SyncGameplayLoadGameData() // Iterating on them & loading their data for (auto* Actor : FoundSavableActors) { - if (Actor->Implements()) + if (Actor->Implements()) { IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); } @@ -256,7 +257,7 @@ void UG2IGameInstance::SaveRequestedData_Implementation(UObject* Requester) { if (Requester) { - if (Requester->Implements()) + if (Requester->Implements()) { IG2ISavableInterface::Execute_SaveData(Requester, GameplaySaveGame); } @@ -269,7 +270,7 @@ void UG2IGameInstance::LoadRequestedData_Implementation(UObject* Requester) { if (Requester) { - if (Requester->Implements()) + if (Requester->Implements()) { IG2ISavableInterface::Execute_LoadData(Requester, GameplaySaveGame); } diff --git a/Source/G2I/Public/Controls/G2IPlayerController.h b/Source/G2I/Public/Controls/G2IPlayerController.h index 42f35c83..8b66087b 100644 --- a/Source/G2I/Public/Controls/G2IPlayerController.h +++ b/Source/G2I/Public/Controls/G2IPlayerController.h @@ -3,6 +3,10 @@ #include "CoreMinimal.h" #include "EnhancedActionKeyMapping.h" #include "GameFramework/PlayerController.h" +#include "GameplayTagContainer.h" +#include "GameplayTagsManager.h" +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "Interfaces/SavingSystem/G2ISavableInterface.h" #include "G2IPlayerController.generated.h" class UG2IUIManager; @@ -22,7 +26,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FToggleFollowAIBehindPlayerDelegate, * Manages input mappings */ UCLASS(abstract) -class G2I_API AG2IPlayerController : public APlayerController +class G2I_API AG2IPlayerController : public APlayerController, public IG2ISavableInterface { GENERATED_BODY() @@ -73,6 +77,15 @@ class G2I_API AG2IPlayerController : public APlayerController TMap, FName>& GetActionToTagMap(); +public: + /* Saving system */ + + UPROPERTY(EditAnywhere, BlueprintReadOnly) + FGameplayTagContainer GameplayTags; + + void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + + void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); protected: /** Setup Input */ @@ -211,4 +224,20 @@ class G2I_API AG2IPlayerController : public APlayerController TObjectPtr GlovePunchComponent; void GlovePunchActivation(const FInputActionInstance& Instance); + + /** Debug keys for testing saving system */ + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Category = "Input|Debug") + TObjectPtr SaveAction; + + UPROPERTY(EditAnywhere, Category = "Input|Debug") + TObjectPtr LoadAction; +#endif + +#if WITH_EDITOR + void SaveGameplay(const FInputActionValue& Value); + + void LoadGameplay(const FInputActionValue& Value); +#endif }; diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h index e321025f..4c1f2a59 100644 --- a/Source/G2I/Public/Game/G2IGameplaySaveGame.h +++ b/Source/G2I/Public/Game/G2IGameplaySaveGame.h @@ -13,7 +13,7 @@ struct FPlayersSaveData TMap CharactersLocation; UPROPERTY(SaveGame, BlueprintReadWrite) - FString CurrentCharacter; + FString CurrentCharacter = TEXT(""); }; /** diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h index e8ea2fba..c6bc4a6d 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "UObject/Interface.h" -#include "GameFramework/SaveGame.h" +#include "Game/G2IGameplaySaveGame.h" #include "G2ISavableInterface.generated.h" // This class does not need to be modified. @@ -22,9 +22,9 @@ class G2I_API IG2ISavableInterface public: // Adds/updates needed data to the save game object UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Savable") - void SaveData(const USaveGame* SaveGameRef); + void SaveData(UG2IGameplaySaveGame* SaveGameRef); // Loads needed data from the save game object UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Savable") - void LoadData(const USaveGame* SaveGameRef); + void LoadData(const UG2IGameplaySaveGame* SaveGameRef); }; From ab3c3a31b9136a9f012a0e47f83294b60f0aa3f1 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Mon, 9 Mar 2026 00:19:28 +0300 Subject: [PATCH 07/15] feat: added save/load actions to bps --- .../Controllers/BP_PlayerController.uasset | Bin 24663 -> 24169 bytes .../G2I_Game/Core/Input/IMC_Characters.uasset | Bin 12671 -> 14085 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Content/G2I_Game/Core/Controllers/BP_PlayerController.uasset b/Content/G2I_Game/Core/Controllers/BP_PlayerController.uasset index c47fd986746a4984987d7e57b4f606fa4b4e5c26..15aec29af8fad60df20b32b429d508f0094c560e 100644 GIT binary patch delta 4777 zcmbuCeNfaz7{~W_aE-huCvbPXoQi;OrwD}jf^P)m>m9ELg1p{^D2Q?dC|M|xW~KRp zTUludIq5i#8FMu?KdCG*71dl zFjqFz)Hn6B$3nM;h4X<3VS#|FA}l4gU*R+}EF{oH&Z5m>$*pbJ>kHVcXRvNW%M1&^ ziZi$0;UXh(uZ_&LlFwn_*GC3PH*lL|a;-ACCkc|nWx@vDDl=e*Os-RKVy3J;bg9|E zr2<6jj{6L_ZEm=Y=z9+Bm%u$WgyZ82Py}HCw+sPUvju&xqWuZD?O1nS!J*ubz&-Dt zbL1}q{s7<(_aOHQ?h&_E& z$E}gct##pGe0KrsTtN4nO(s|GiDUUYXZ)xqa0u5RlWQD-W8?GEgm$E*UMG`VFOO>_ z&GJC4?ezl;T(J&@gh1qp9OI(I|meH*7E#!180gmw7q#{bt^K7s_m0 zCa?5%$aj`;xesLa5g~fF<1!VMaM>1&wc7x$qbR_#uuI_8@`|y$% z4}udTBpV}{=)Fwcv^m&fhNs376~F0X@B2l6s8!R>tRTrm+jE1JtVjGno9@pFiCM^K zEbOb0u!!P>=uw!*I?p;(i*+JwvfNzpe17L{w0LAGtEy7 z{QJR+%bNWT5oQdm(TdB^wt9lEf|%+_>;-XwC-DY|bA?DPc{KL21X#m_vSVXF$*2Hpw8Y=RvvMfK|%Ogx&ly%C$aS!s8c+tzkyig zN&FSW98cnP5ZU2p#W^iLBQ|D%LsP&qbeV%Q<~y3M>@UEs@+7i}XA*iOcj-3_ZI%MOs83 zD6Us8M%98=)&!iS4l!tWQmO_;t{SVec0q>-+ zXWUaBHuzw(If=M9KSDyWW}>bh=y* LejpfWh$ZzO9U`fN delta 4242 zcmbW43s96*6oBttB?Vn@VRzX@kf%IEQ4sNg3W$p4E{i-A1VqFY@dbjEj})o2d}P{5 zdssfeM5~!}HpZ!OMx16Ue316g>|t1@nQ6YJY}D-ByZ6V%{qIcUo!S4~d%kndf6x8T z{r|g{579F%bd$+=@NV1viu#1$&wR%%yxDwoZ{3z1&DX34;=HK$K0?Si4Iy4Am+E1O zId;Si10g+e=od~%63WH^ykN_Tekk8HI>!`M+fd(#zr)e+9u1)T;8U6t78OHC_zb6m za(#g9nw5|RsEC^3NY?zS@no^b+Gp05Lwz?x(C3?0U)3p^tkT*Vds^Azipqt=cBIDu zM~FvQ7e`+A7V3!hnoCps&@i{MynMbry}GitY>qJ9T@lp9w*&Fmf?U1bduwP1fqI?Z zvA@S|N(aJqZ8UtKt)attZ-w_Hi0>Jr%`Kg0Ppeo!Dr3?PBq9k4c8OLqaAH~sA6qMD z*~#tXr3Z&Fn-EnSo(p+@d#gT$8F_Fcxab|?q3L#R>rkc(ebmu4H?p&9s}H33#M1~! z^$GRjwq%&5u+35B^EZvvVp^T%({Sa9W8N-G=LA~!>}!K^-BP%&OM)cdvb0`KlTNp) zX-``2!}QkllJq&H)um;N?bS7;iX6Jns)VVXw?8uIM6+YRuYVUh6nYus;4g!j_o`?( z)Ef&&^d6#xWX4}i8l|t=2n!=BMMyRe*w))Ou-%I`%G(S5DYS#VuM_q7FXBW zi6<&p$+-a=)R+^stA%|V7TS6UwfPyCF>9`*K}s4`$q9_MTa_F~@~o6RCne8I$vP?7 zBPC6Y=pW6L%?(0qdbcGwhF~KPR_|OJ@{*8WAX(FiY!rm~yvWHaq1h>9JCe<+Jr zsFHI?K$ZN6EDWOuLk&=2&@PNSE9wBdHRqbU2 zDnlbQ*Q{{3s1Gwx%_|uH=v3YI`f{=#mU#@aqlYvv->hUyQ>qo=W|>b zk!Wm0jpDso?3eMy93Mlx*6Bd-XhZsm{+a0?(M)`2|L8FMoT~!763c?rW%{r z5P$66m+;4$ZwdGA9XUbskF&cz^5gDJ(S(?gLS2SG%!=ym<3Pn}JUFq2(}(LZW=B)h z`iK;EJe%|2!LA4Cqj+pFXF{^LEg`~*k9fYfak<%HfwfVv$P(Dk&4iu(=5hl<*yoxx z7T5|cHocie8N^iB+$h|!m}my*ta_crY0|M!DUf6h#?#BfLVs9b^`w2_RG}XPWd=c$ z)kMRf#o8YaVyiOso{FbpF~pU2#2qZ+TQLbee3UtWCP6&L{x{PQt3==?MT()2Oz@>( zd0Jq!EmoT#5MMrC)@yBA_l3EIYd1`ox}|RZ8EJ1Bopm1RY(d>t{360q8N7hdE`z@y z3{)VTx9QVUm64E4@O1w`)A2HR1>tA~!muorvbc;UY%jAm%_&4#tjvlwrwI+)U9S0M zz~QXGx)Fk2!x+h;KT}9oYd+CSXjCYJzabnTgEtVCt3V5ndmZUy70Ti72oq(H-Lyi1 z&^}r3nddqJUACE8pgmhpt&p0nH%t<=bCFbJ51~U?)r(3!nr-+WUbyEVAt$Jl=kgqb zcc&VUBHg#lVBx= QkWBDL&;vUb=r=v(KlGeX?EnA( diff --git a/Content/G2I_Game/Core/Input/IMC_Characters.uasset b/Content/G2I_Game/Core/Input/IMC_Characters.uasset index 77164e6e7120ad0e826c369d49cdb4d8c1bdd828..500ef1b088f16bffac34d12e7a5eb936fefef91c 100644 GIT binary patch literal 14085 zcmeHO4RBP|6+R^aDG||VErO+hg&1P8Nl3zv{OxWwX&`^FfjHWk@UnUAE^PMg?7mG1 zw%S2Orkzfeq1vh9Oc|Mh{*4UII0`LBVW!ln19T8a2&F{%*R&EN%1?T}``%kN%Z7c{ zP0FS{GrRA;d%t_mz280eo_lXL?N3a5>GR&+-sNdTqlOWE!B0qfXYpH;r?$@xt^9NT zvfroY+jc$m-mY&GeS^zB_Pp}p8QaG0j1LMnTX&Y_-OqX1{IQfv&9bGuu%baE>`G9! zRQLm7)lyMqZ?Jo$pyX2JV2Gha%lNYn+E_jqMKp<@MP&sgGfM2UitL32#r7E`Gm315 z#f4VKbi2c0ca+#8;L)5qd^^z;evZiy+4S_?*Pc*lyzk_EeBR;D@yrhYp@Xm6w`^UM zaWQB22H1zLW&9aVBe-|n-Yvs2`7I}BsiQb&iR_c5kepkbS0c)Zx)yBcr<^MZ`SNn$ zRV1#my#pm$p7G5@_kzU-IO9-M8AHk9bOpTu)#C7bB)>~;63o$|KfH4u?J-J4E|{gd z>AipB0V6BD*<5=37NlINSMgIdNG)E49m5@-vT2?v=CZ4CYruiK+iMo+d6tr2eq z1FO@r_T6D*Mt8U2tv`K^p>fi&pVdANLSnpu=55~F4MLn~jZF&D>^ENAkIp#NU^uEX z`SjvtXsV+l9cRihR&`P%Z8)`kCTiCOy>2(iJF%d78y2^v0V;aAel2R$)38gooX+J` z|4WzVQz1)*wV}+{CZ@-u>t=m2+jJ$x=Tlm(73}0*f0M=~t$d^)uh^`H3O`RR$)##7 zDWJf}gK7U^&)TWO}bNe?pE~L$wmmHG25KMe|0S5*t}uJAU{qvqqd8FkEW2yjqQfJL-IQdX4E; zIz5V_(s_5+A5D9hv(>A*Ja(S9LCIK3>C&DFXH7eHowQ1}dYehfwr(#p+toV7?e@v` zpb~a@=ohp0Zu?4GfUWh9-Fg>H=}`|MZK<|O(Z!0~d01BLJ}DF;wRQEg2=P%9F#PL% zPmk5J;!Up&jYprqQ!9_1JXve&N5?HHq^mE#z8R}48xoG6y>qNze;00DSbg#^);BJ{ z=Cu1S)QcrqWU{xM^H017%MQP54!5!0^MbBLxRb9VV;JuM0Ep)-?+za~MX#^9!sOP+ z_vw9&JJ4xYE;x01jA4{++x|WlR5sCPkV3~x9>w|@vl|Q0t}6&qVr!v?(9E&R#qfwc zJt851v13!u%=w+zuxMp^a4MChriT%6e)Y(lpJQPAlvc}bNtJ2cQ;m-6%A6c~cyX`Z zYC4k#331qpKoaT8*I)^81txKbq>w~*r)#i;xFVA{>>B6KM7SB6x`g^>n#5r< z1xaKV8z4v`oQCR)W4G)z^6{+FUl1wV^oW3icz=S60em2_y0l#FJFM%v8f6dl5DV}` zD94@wQfv^ez4rMwY$5YT_Mi`PCB`0NN~}HG0NcZ1D!P9)18ff`yJ&kq7+`z2Qi-EO(H^V*+5$cz2ZWe2OPpiR?z@S$>LCDC<3Pl1M7(R+ym;p# zD8V_Zn(xtCWbN>ZJ!70BZk{O3GD?iwRiQPW?ksWB%|6ikT~-(ooC_hC$q=+)sz_b) z4kKUB77q)e77kn4dTz7=+zT3F(fRf4;t|?L^KU2 zb;x+_A--|W6LjEd>b%>H0*C6DNI{rZA0XT+YJ(R*z7}6n7YB9q!R^UDNR{ql48Lb zBu~SU29m;_7MY|7ZJ0TOL>+#307+r$5>L`|N!{-9p;*X}rmQQel-+;RQWjaRFadGi zhZr7(b5z`uZoiWgzY{c$>(SQ?MjkXqAGU9NgcptD8aJP(MQFptz}Kg(C#kgImwk|% zk2b@xl0ezVNu?}HV{B+sb|I;h;dg(KA?;=Vr;JDVtA&8`N%gYH!pDZVmwl2{%8b8R z59x_{F{zZ{4?B<{P1zSorR;y=6!5R4Qif|g$dLB3eK{10u<_iXub-h+D%;JT?#Q~AB+*v!T0f7zXF{Mrxre*-`OKokG~ literal 12671 zcmeHOeQZw0xFyC8e|_&Cc(Am>1*3 zZ^f~2v?Dq9-t*2q=ic8v_ndp5o$epZ-~H*pz`%yFM3cr4ea0o|?Ty`CzYjS)r>Ep? zJJ}Fl9T2Mv|9L0T*ZA3offrAobL{NP|99CQ`>VC34>7NZKejp{EZXYI-7RXxDaoSE z9f`&jo4elG;tU9~;8R37#-c6NI+On!eRn8?9&hoNK=c1}b6^`=C za=UAx)8%rys+X&#lS7 zT6~}j_Mr=d8BY^)sVuEhpA+@2>KfuXcT(x|Ytffb zxs%aaQ>O8ZzYO#MP@K<<2Gg^xgr4};uG6csU!b|br^pw9bDf34r+BI&;I3snW(XkMuoPPTEQne;))_gS{xD= zVcI|caK$%F5^KecN_h=ExZ%6I#xa{mPcJQhWM`gM_ply%!W$h2)F4W>?K>{qrZ=2Y zM3JQs`JdW(YO2nwm*O$qBu)FIWIy{T=`hCVcz1jw9JyX<3vLWzlgv35*aTZ@Hy+2h zs-GWGeU%L-klyxj-f9fMBSl2&8+&jvCeYCOxad<7GWK`(pZ_Ma8wKAc!7r|rd_pL= zMS!nh60s;FK?O?Z4!|?iipA)a?_K`O1eRDA^am8mzfu&MX0*&RYx(n^J_X`lI`PxS zt(be#n$wCs2m3I0FAO^bnW|rT`b~`6tH^QFxEIjV*PyA1-aUS<4olD^v{KiZeT&h% zNe=q`!0FVg@bd^->!VchZ1XnsXr?h=Ogvk{wV^|j<~cE&+ul+e>QvqSJKL9BsZNv(j`l3Lx7ezoMu9g8J$m?R?-HwByd0!`ibve6 zBvK6Zys@ysvQ2M5k`%h=@BO1?muV6mPiZ82p+1d1~Sh#l^oItD4pE5G_kaD$T6UF`bssiv~tMdpmh2cXkuyQjB80cedQMQ zEwm_CVNni8Bv88XX;Opb*gXaeK1!F~K!!Mp<1Z8LXG0P0i}-+&b!lAf?=fB1mnM7Y zhuDY`DGa$0^gCl=wC13{hAl9Mvj=^k{v>;dvB~yc8)17mi<{?nXoT(IVqms+bcF5U zo@ut%lRSbd$@3$@m!HXd&%qnRocKKabQk5!>70g9A()9dI~_- z6e8;NH`Uv^(p2{ZD8aqL!*5*LMeS0Cykor4GhN+E)u)BV6XT_usx_7FLiGhG`@mM# zr_pr1#4ZNG`dqbk-7^fn&Q^Ecsh>t-^2Xt!N_#8Kf%1f`RVU ziW|oczHtJ{S;(g9voj`=gOI7Kh2=zLNKd<*QBPC-(6s(J2%GBP*b#`_fGtbV&0$lZ z(cp0i8d-u3D+S77*o-uhEy!@p1LZJeiP(k|mL*SnNChpYVN1j|WP7+>mY~ByAC$u$ zm+eh@pN3d2LzdX22sl~tG+bvuIqYePO^Vcpos%W#rU5sm9EL9aCjC!FkGl_Qh~0va zCH5Y?>E`dF_#KpS_nfOw8dnR0hdMq64Sye12gjnfo~I>X!z<{UhwVy6VZ$$%K(`)j ziG6qTkX_0sWb-t{MmJ=A8HEgAv4cjnm;E0yUf~_5LoBx&K$t!!m eU%K?KhjFVlXC4f<{lwyj$NlX Date: Mon, 9 Mar 2026 13:54:18 +0300 Subject: [PATCH 08/15] feat: checkpoint + refact --- Source/G2I/Private/Game/G2IGameInstance.cpp | 8 ++- .../G2I/Private/Game/G2ISavingTriggerBox.cpp | 69 +++++++++++++++++++ Source/G2I/Public/Game/G2IGameInstance.h | 2 +- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 3 + Source/G2I/Public/Game/G2ISavingTriggerBox.h | 45 ++++++++++++ .../SavingSystem/G2ISaveGameplayInterface.h | 2 +- 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 Source/G2I/Private/Game/G2ISavingTriggerBox.cpp create mode 100644 Source/G2I/Public/Game/G2ISavingTriggerBox.h diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index e9f63598..f3f65d9d 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -60,6 +60,12 @@ void UG2IGameInstance::CreateSaveGameplayDelegates() void UG2IGameInstance::OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess) { SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(bSuccess); + if (bSuccess) + { + UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); + } + else + UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); } void UG2IGameInstance::OnGameplayAsuncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData) @@ -128,7 +134,7 @@ const FString UG2IGameInstance::GetGameplaySaveSlotName_Implementation() return GameplaySaveSlotName; } -const UG2ISaveGameplayDelegates* UG2IGameInstance::GetGameplaySaveDelegates_Implementation() +UG2ISaveGameplayDelegates* UG2IGameInstance::GetGameplaySaveDelegates_Implementation() { return SaveGameplayDelegates; } diff --git a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp new file mode 100644 index 00000000..86519dfe --- /dev/null +++ b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp @@ -0,0 +1,69 @@ +#include "Game/G2ISavingTriggerBox.h" +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "G2I.h" + +AG2ISavingTriggerBox::AG2ISavingTriggerBox() +{ + OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnOverlapBegin); + + FGameplayTag SavableTag = UGameplayTagsManager::Get().RequestGameplayTag(FName("Savable")); + if (SavableTag.IsValid()) + { + GameplayTags.AddTag(SavableTag); + } + +#if WITH_EDITOR + SetActorHiddenInGame(false); +#endif +} + +void AG2ISavingTriggerBox::BeginPlay() +{ + Super::BeginPlay(); + + GameInstance = GetGameInstance(); + if (GameInstance && GameInstance->Implements()) + { + IG2ISaveGameplayInterface::Execute_LoadRequestedData(GameInstance, this); + if (auto* Delegates = IG2ISaveGameplayInterface::Execute_GetGameplaySaveDelegates(GameInstance)) + Delegates->OnGameplaySavedDelegate.AddUniqueDynamic(this, &ThisClass::OnGameplaySaved); + } + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface or is NULL in %s."), *GameInstance->GetName(), *GetName()); +} + +void AG2ISavingTriggerBox::OnOverlapBegin(AActor* OverlappedActor, AActor* OtherActor) +{ + bActivated = true; + + if (GameInstance && GameInstance->Implements()) + { + IG2ISaveGameplayInterface::Execute_SaveAllDataAndGameplay(GameInstance, true); + } + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface or is NULL in %s."), *GameInstance->GetName(), *GetName()); +} + +void AG2ISavingTriggerBox::OnGameplaySaved(bool bSuccess) +{ + if (bActivated) + Destroy(); +} + +void AG2ISavingTriggerBox::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) +{ + if (SaveGameRef) + { + SaveGameRef->SaveTriggerBoxesSaveData.Add(GetName(), bActivated); + } +} + +void AG2ISavingTriggerBox::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) +{ + if (SaveGameRef) + { + if (auto* Key = SaveGameRef->SaveTriggerBoxesSaveData.Find(GetName())) + if (&Key) + Destroy(); + } +} diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index 71c76f75..7fea3eae 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -93,7 +93,7 @@ class G2I_API UG2IGameInstance : public UGameInstance, public IG2ISaveGameplayIn void LoadRequestedData_Implementation(UObject* Requester); - const UG2ISaveGameplayDelegates* GetGameplaySaveDelegates_Implementation(); + UG2ISaveGameplayDelegates* GetGameplaySaveDelegates_Implementation(); // === Other Functions === diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h index 4c1f2a59..fcf19cea 100644 --- a/Source/G2I/Public/Game/G2IGameplaySaveGame.h +++ b/Source/G2I/Public/Game/G2IGameplaySaveGame.h @@ -27,4 +27,7 @@ class G2I_API UG2IGameplaySaveGame : public USaveGame public: UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Player") FPlayersSaveData PlayersSaveData; + + UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Saving Trigger Boxes") + TMap SaveTriggerBoxesSaveData; }; diff --git a/Source/G2I/Public/Game/G2ISavingTriggerBox.h b/Source/G2I/Public/Game/G2ISavingTriggerBox.h new file mode 100644 index 00000000..5ae59486 --- /dev/null +++ b/Source/G2I/Public/Game/G2ISavingTriggerBox.h @@ -0,0 +1,45 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/TriggerBox.h" +#include "GameplayTagContainer.h" +#include "GameplayTagsManager.h" +#include "Interfaces/SavingSystem/G2ISavableInterface.h" +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "G2ISavingTriggerBox.generated.h" + +/** + * Trigger box that, when triggered by the player, saves gameplay & deletes itself + */ +UCLASS() +class G2I_API AG2ISavingTriggerBox : public ATriggerBox, public IG2ISavableInterface +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly) + FGameplayTagContainer GameplayTags; + +protected: + UPROPERTY(BlueprintReadOnly) + TObjectPtr GameInstance; + + UPROPERTY() + bool bActivated = false; + +public: + AG2ISavingTriggerBox(); + + UFUNCTION() + void OnOverlapBegin(AActor* OverlappedActor, AActor* OtherActor); + + UFUNCTION() + void OnGameplaySaved(bool bSuccess); + + void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + + void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); + +protected: + virtual void BeginPlay() override; +}; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h index 52d079e2..6704fa0f 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h @@ -97,5 +97,5 @@ class G2I_API IG2ISaveGameplayInterface // Returns class with all delegates UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - const UG2ISaveGameplayDelegates* GetGameplaySaveDelegates(); + UG2ISaveGameplayDelegates* GetGameplaySaveDelegates(); }; From 8d2d6856b0d0e3af963ac9bbf1916c411c1d6f11 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:28:31 +0300 Subject: [PATCH 09/15] refact: saving system + PC's jump logs --- .../G2I_Game/Core/Input/IMC_Characters.uasset | Bin 14085 -> 14064 bytes .../Characters/G2ICharacterDaughter.cpp | 26 ++++++ .../Characters/G2ICharacterEngineer.cpp | 27 +++++- .../Private/Controls/G2IPlayerController.cpp | 46 ++-------- Source/G2I/Private/Game/G2IGameInstance.cpp | 81 ++++++++---------- .../G2I/Private/Game/G2ISavingTriggerBox.cpp | 22 ++--- .../Public/Characters/G2ICharacterDaughter.h | 8 +- .../Public/Characters/G2ICharacterEngineer.h | 8 +- .../G2I/Public/Controls/G2IPlayerController.h | 14 +-- Source/G2I/Public/Game/G2IGameInstance.h | 7 +- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 7 +- Source/G2I/Public/Game/G2ISavingTriggerBox.h | 6 -- 12 files changed, 122 insertions(+), 130 deletions(-) diff --git a/Content/G2I_Game/Core/Input/IMC_Characters.uasset b/Content/G2I_Game/Core/Input/IMC_Characters.uasset index 500ef1b088f16bffac34d12e7a5eb936fefef91c..4812d9ce0037e397e2fb2937188215f9fbd3ffc1 100644 GIT binary patch literal 14064 zcmeHOdvH|M8UGp{QX)QrQUpsuMGR@OBqSk3UYpG(E+iqa!7%zqxY=BGS9bUA+`XHy z&}xTHk+#!mrKoLv%rqlbs?*Bgw4=}hYW}cJ9iU@%3ZaxJub^oq1_WaI{qAGgEF1Q= zY*IG#o7uhR@jKsl?(cl(eCM2O_I`Kf^Jn||`c{n~8k<4%1wSEn{{!24&c555@%`mH zZg1%ccJEsG_^-Z3^cAlA#PjM$C(E{UXZ|g})w;7H?>;We=8vUP@Qaqp0!M?Iut~CL zaRh>4#p0;8HP}3YEVvX=4l$JILH=xnHkP)rMAP_LSdm{mr`R^P&{mLNWSdhwr?9M` zsK9EUZL`~L_F`KUJdRUu-a<5;pXa$TM4oWz357=fMJ~tZcl_gXs{24`~`R%&BTQjmamy@%?UX-(3^oc@9%q_|*R_lqn7HuA&o*Suh{$lVo zBu=uuUBz0N@y$f{fyHlf!Je2phLXkUlD$F2Vh?zPfJDzNVycR6riq5d*~))Wm$;S%2H62l?XXr?eOAN%OZ*xEj(6}y9YYT z>E+SQw~RtcY6(!})QL$bw$q)H$G6;pl-?=L#1b)5Ed+z!fSaCr{{F(zDu73p#y!z_ z8b<7aus>>K*$2-px*LVHJ|QB?Xel)ML{{Py6@`_Qi5{WZE3sp^!_%KzUIis5 z*{kR}LKU*)*9nk+zjQLXUqNm2dp|^~lH{pB+dT;>cF~~F)KDR~#ImrWNP&hji64I2 zF>`<6%`u27akWxVPWP_*)>BZigeL5qN+XdHBlOz*m0QMW=^EBZk2-@RfvTeMoj)%D zk!Vhqjhi}e)H60IpvaPs+|O(|%C4&ds-z zF0K_^YXrAgCAkEjcbx$Dfv!-H*`X{YvI&$8G;ASy^}6{smS4&e$S$3FG5ot9c({Y ziNUB78fo+K?ImbkCwtv)@N{&Me;d}Z6+tR|p?(8e)KkWV8&2eMYT#8$bDxmKVQr}J zMbw$Fe&gIvOHDVDd_Jkg>R^BN2AVY5=#hu>^NP%Ba0GZ32`)wJNj?R~92oImHZGuT zdGlVGY1Z=Mus=w*6_4?nZc!x(O?2~HJ^L(XZKys`B}r?@pPkiZ+EBIBEYe>)W=+X4 zYp7P>X+;n1KWE;EQ(z;@YA27HYu1R9V}nbn5!Wiw$VMIS&0cD{mrjo)DRkQ1^#{`) z=4|mQE{~0;s4N((CS7=Y$|=*1T_>y&tzJJ#+1Bj^W?!{Va=U$^O_stg4?Q`zZQFko z?O&>Y^v2s@N)K>|UMsW(iOxB47hnak`Gio2l$NzmWO6QM0)~J2&f^pHqD0ecbK{|> z$7}WRlV`?;`f&-X3h2uhUT?)h%7#?O&)zvPpyyS$E~q*70Tx!SzvQ(0&eW@`uUg37 z(ixa~2bLYqYYw+%ujd(Ei|S6k_RI_p_5dVumiI2auB6q+7FaQVf<^L~th?N?hPx&=j~jo)juUsmVjFvE*`O{--kHWmHa@f)vO z=M$}Ky&?L6>v&PLvA(L;%3>%aHqoCyUid?htcNZNRc^YSv+Bn58%VI2+Dp#UuryzP z9^}2*Qaz2>XhynoOWCmFX; zPhSalf^myX;EprSY65qRab+fOA2Uun8C>~2K4M(C34I-mvzx#jWn6^`+&>ssX##hI zaSxcl{he_R6S%{STWkW?&bTEeaEBOIl>leVj}>|y9ys4XV*Mo%4q*$D440?DlH#xx zfh5y6OM@lF<(tGIl0uT%ovp!=;tEaTus4JxvuhkRli}uQ>XPa!F^R)w3X;q&Hb9VM zI1SYw$8OnYl;c^gXAmhPQ?v}u+ye>j0Pumt>(WZK@35}x%NToThgg6oLOJ#fkf{dY zTDIS}VGD&`2LvWvA>Imq^KdlGA}E`|0k#*H6h zef;mK#yo@jZsdx@`D%!u#^yppa8k7@^kJy4&IR1J{o1vh>sy^*D#!rJGF=U zjdPw#2cE{>;2vaH5Z@v2Rg48BRx9#YhYV4bMFX^6dRgC5ZdgiN%5Wn9FRzp7vQvJuL^Ev7|9=>O?SBZ_qYG)ZMKTm)6upJr0Y;5YdK_f~2v>8DV0mD8p6{ zlE#!pJ#8$%#}IiM_BW6;rY*X*A@alRhKM?B_91CZ-B3@`X&PhcOj&f2V!;_APvc{X zDrxL#(MgKXhM6-&)ZvE*kTj+)`6TU3>2}w+6CKu+b)}TD`!pxJ2E8o0TwwxU{Sh8N z2{exDSAT?8J9kY^&M0lT7`S@cdQwUoe#8eE))VxTlu|ZBV{CX+b|$5iP5wV++Qnh) z<>8gDB(Dp~PN&q%?z$el?9-G|hM$5#hV}A)E~S*=4?B=yP1zSIrR=&m1^g?el;PSA zGOWGqpDCp*OJi(!Q`VhQ%G6)A{!hHj@N0FGR&+-sNdTqlOWE!B0qfXYpH;r?$@xt^9NT zvfroY+jc$m-mY&GeS^zB_Pp}p8QaG0j1LMnTX&Y_-OqX1{IQfv&9bGuu%baE>`G9! zRQLm7)lyMqZ?Jo$pyX2JV2Gha%lNYn+E_jqMKp<@MP&sgGfM2UitL32#r7E`Gm315 z#f4VKbi2c0ca+#8;L)5qd^^z;evZiy+4S_?*Pc*lyzk_EeBR;D@yrhYp@Xm6w`^UM zaWQB22H1zLW&9aVBe-|n-Yvs2`7I}BsiQb&iR_c5kepkbS0c)Zx)yBcr<^MZ`SNn$ zRV1#my#pm$p7G5@_kzU-IO9-M8AHk9bOpTu)#C7bB)>~;63o$|KfH4u?J-J4E|{gd z>AipB0V6BD*<5=37NlINSMgIdNG)E49m5@-vT2?v=CZ4CYruiK+iMo+d6tr2eq z1FO@r_T6D*Mt8U2tv`K^p>fi&pVdANLSnpu=55~F4MLn~jZF&D>^ENAkIp#NU^uEX z`SjvtXsV+l9cRihR&`P%Z8)`kCTiCOy>2(iJF%d78y2^v0V;aAel2R$)38gooX+J` z|4WzVQz1)*wV}+{CZ@-u>t=m2+jJ$x=Tlm(73}0*f0M=~t$d^)uh^`H3O`RR$)##7 zDWJf}gK7U^&)TWO}bNe?pE~L$wmmHG25KMe|0S5*t}uJAU{qvqqd8FkEW2yjqQfJL-IQdX4E; zIz5V_(s_5+A5D9hv(>A*Ja(S9LCIK3>C&DFXH7eHowQ1}dYehfwr(#p+toV7?e@v` zpb~a@=ohp0Zu?4GfUWh9-Fg>H=}`|MZK<|O(Z!0~d01BLJ}DF;wRQEg2=P%9F#PL% zPmk5J;!Up&jYprqQ!9_1JXve&N5?HHq^mE#z8R}48xoG6y>qNze;00DSbg#^);BJ{ z=Cu1S)QcrqWU{xM^H017%MQP54!5!0^MbBLxRb9VV;JuM0Ep)-?+za~MX#^9!sOP+ z_vw9&JJ4xYE;x01jA4{++x|WlR5sCPkV3~x9>w|@vl|Q0t}6&qVr!v?(9E&R#qfwc zJt851v13!u%=w+zuxMp^a4MChriT%6e)Y(lpJQPAlvc}bNtJ2cQ;m-6%A6c~cyX`Z zYC4k#331qpKoaT8*I)^81txKbq>w~*r)#i;xFVA{>>B6KM7SB6x`g^>n#5r< z1xaKV8z4v`oQCR)W4G)z^6{+FUl1wV^oW3icz=S60em2_y0l#FJFM%v8f6dl5DV}` zD94@wQfv^ez4rMwY$5YT_Mi`PCB`0NN~}HG0NcZ1D!P9)18ff`yJ&kq7+`z2Qi-EO(H^V*+5$cz2ZWe2OPpiR?z@S$>LCDC<3Pl1M7(R+ym;p# zD8V_Zn(xtCWbN>ZJ!70BZk{O3GD?iwRiQPW?ksWB%|6ikT~-(ooC_hC$q=+)sz_b) z4kKUB77q)e77kn4dTz7=+zT3F(fRf4;t|?L^KU2 zb;x+_A--|W6LjEd>b%>H0*C6DNI{rZA0XT+YJ(R*z7}6n7YB9q!R^UDNR{ql48Lb zBu~SU29m;_7MY|7ZJ0TOL>+#307+r$5>L`|N!{-9p;*X}rmQQel-+;RQWjaRFadGi zhZr7(b5z`uZoiWgzY{c$>(SQ?MjkXqAGU9NgcptD8aJP(MQFptz}Kg(C#kgImwk|% zk2b@xl0ezVNu?}HV{B+sb|I;h;dg(KA?;=Vr;JDVtA&8`N%gYH!pDZVmwl2{%8b8R z59x_{F{zZ{4?B<{P1zSorR;y=6!5R4Qif|g$dLB3eK{10u<_iXub-h+D%;JT?#Q~AB+*v!T0f7zXF{Mrxre*-`OKokG~ diff --git a/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp b/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp index ab27a76d..6d4a7e0a 100644 --- a/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp +++ b/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp @@ -2,6 +2,7 @@ #include "G2I.h" #include "G2IFlightComponent.h" #include "Engine/LocalPlayer.h" +#include "Game/G2IPlayerState.h" #include "Components/Camera/G2IThirdPersonCameraComponent.h" #include "Components/G2ICharacterMovementComponent.h" #include "Components/G2IInteractionComponent.h" @@ -9,6 +10,7 @@ #include "Components/Camera/G2ICameraControllerComponent.h" #include "Components/Camera/G2IFixedCamerasComponent.h" #include "Components/G2IInventoryComponent.h" +#include AG2ICharacterDaughter::AG2ICharacterDaughter(const FObjectInitializer& ObjectInitializer) : ACharacter(ObjectInitializer.SetDefaultSubobjectClass( @@ -69,3 +71,27 @@ FUnPossessedDelegate& AG2ICharacterDaughter::GetUnPossessedDelegate() { return OnUnPossessedDelegate; } + +void AG2ICharacterDaughter::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) +{ + if (IsPlayerControlled()) + { + SaveGameRef->PlayersSaveData.CurrentCharacter = GetClass(); + } + + SaveGameRef->PlayersSaveData.CharactersTransform.Add(GetClass(), GetTransform()); +} + +void AG2ICharacterDaughter::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) +{ + if (IsPlayerControlled() && !this->IsA(SaveGameRef->PlayersSaveData.CurrentCharacter)) + { + if (auto* G2IPlayerState = Cast(GetPlayerState())) + { + G2IPlayerState->SelectNextCharacter(); + } + } + + if (auto* KeyTransform = SaveGameRef->PlayersSaveData.CharactersTransform.Find(GetClass())) + SetActorTransform(*KeyTransform); +} diff --git a/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp b/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp index 426073b8..71261ea5 100644 --- a/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp +++ b/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp @@ -7,6 +7,7 @@ #include "Components/SteamGlove/G2ISteamGloveComponent.h" #include "Components/Camera/G2ICameraControllerComponent.h" #include "Components/Camera/G2IFixedCamerasComponent.h" +#include "Game/G2IPlayerState.h" #include "GameFramework/Controller.h" #include "Engine/World.h" #include "G2I.h" @@ -59,4 +60,28 @@ FPossessedDelegate& AG2ICharacterEngineer::GetPossessedDelegate() FUnPossessedDelegate& AG2ICharacterEngineer::GetUnPossessedDelegate() { return OnUnPossessedDelegate; -} \ No newline at end of file +} + +void AG2ICharacterEngineer::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) +{ + if (IsPlayerControlled()) + { + SaveGameRef->PlayersSaveData.CurrentCharacter = GetClass(); + } + + SaveGameRef->PlayersSaveData.CharactersTransform.Add(GetClass(), GetTransform()); +} + +void AG2ICharacterEngineer::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) +{ + if (IsPlayerControlled() && !this->IsA(SaveGameRef->PlayersSaveData.CurrentCharacter)) + { + if (auto* G2IPlayerState = Cast(GetPlayerState())) + { + G2IPlayerState->SelectNextCharacter(); + } + } + + if (auto* KeyTransform = SaveGameRef->PlayersSaveData.CharactersTransform.Find(GetClass())) + SetActorTransform(*KeyTransform); +} diff --git a/Source/G2I/Private/Controls/G2IPlayerController.cpp b/Source/G2I/Private/Controls/G2IPlayerController.cpp index 036bdb01..5314ff8f 100644 --- a/Source/G2I/Private/Controls/G2IPlayerController.cpp +++ b/Source/G2I/Private/Controls/G2IPlayerController.cpp @@ -132,13 +132,6 @@ void AG2IPlayerController::SetupInputComponent() AG2IPlayerController::AG2IPlayerController() { PlayerCameraManagerClass = AG2IPlayerCameraManager::StaticClass(); - - FGameplayTag SavableTag = UGameplayTagsManager::Get().RequestGameplayTag(FName("Savable")); - - if (SavableTag.IsValid()) - { - GameplayTags.AddTag(SavableTag); - } } void AG2IPlayerController::OnPossess(APawn* NewPawn) @@ -258,25 +251,6 @@ TMap, FName>& AG2IPlayerController::GetActionToTagMap() return ActionToTagMap; } -void AG2IPlayerController::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) -{ - if (SaveGameRef && GetPawn()) - { - SaveGameRef->PlayersSaveData.CurrentCharacter = GetPawn()->GetName(); - UE_LOG(LogG2I, Log, TEXT("PlayerController: %s saved."), *SaveGameRef->PlayersSaveData.CurrentCharacter); - } -} - -void AG2IPlayerController::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) -{ - if (SaveGameRef) - if (const APawn* CurrentCharacter = GetPawn()) - { - if (!CurrentCharacter->GetName().Equals(SaveGameRef->PlayersSaveData.CurrentCharacter)) - SelectNextCharacter(FInputActionValue()); - } -} - void AG2IPlayerController::SetupCharacterActorComponents() { ThirdPersonCameraComponents.Empty(); @@ -464,27 +438,18 @@ void AG2IPlayerController::Fly(int Direction) { IG2IFlightInterface::Execute_Fly(FlightComponent, MovementComponent, Direction); } - else - { - UE_LOG(LogG2I, Log, TEXT("Pawn doesn't have component with fly interface in %s"), *GetName()); - UE_LOG(LogG2I, Log, TEXT("Pawn doesn't have component with movement interface in %s"), *GetName()); - } } void AG2IPlayerController::Jump(const FInputActionValue& Value) { - if (!FlightComponent) - { - UE_LOG(LogG2I, Log, TEXT("Pawn doesn't have component with fly interface in %s"), *GetName()); - } - else + if (FlightComponent) { return; } if (!ensure(MovementComponent)) { - UE_LOG(LogG2I, Warning, TEXT("Pawn doesn't have component with movement interface in %s"), *GetName()); + UE_LOG(LogG2I, Warning, TEXT("Pawn doesn't have movement component in %s"), *GetName()); return; } @@ -667,6 +632,7 @@ void AG2IPlayerController::GlovePunchActivation(const FInputActionInstance& Inst } } +#if WITH_EDITOR void AG2IPlayerController::SaveGameplay(const FInputActionValue& Value) { if (auto* GameInstance = GetGameInstance()) @@ -683,8 +649,12 @@ void AG2IPlayerController::LoadGameplay(const FInputActionValue& Value) if (auto* GameInstance = GetGameInstance()) { if (GameInstance->Implements()) - IG2ISaveGameplayInterface::Execute_LoadRequestedData(GameInstance, this); + { + IG2ISaveGameplayInterface::Execute_LoadGameplay(GameInstance, false); + IG2ISaveGameplayInterface::Execute_LoadAllData(GameInstance); + } else UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface."), *GameInstance->GetName()); } } +#endif diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index f3f65d9d..e144dff3 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -35,13 +35,14 @@ void UG2IGameInstance::Init() Super::Init(); CreateSaveGameplayDelegates(); + CreateGameplaySaveGame(); } void UG2IGameInstance::CreateGameplaySaveGame() { GameplaySaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UG2IGameplaySaveGame::StaticClass())); if (!ensure(GameplaySaveGame)) - UE_LOG(LogG2I, Error, TEXT("Couldn't create GameplaySaveGame object.")); + UE_LOG(LogG2I, Error, TEXT("Couldn't create GameplaySaveGame object in %s."), *GetName()); } void UG2IGameInstance::CreateSaveGameplayDelegates() @@ -49,7 +50,7 @@ void UG2IGameInstance::CreateSaveGameplayDelegates() SaveGameplayDelegates = NewObject(this); if (!ensure(SaveGameplayDelegates)) { - UE_LOG(LogG2I, Error, TEXT("Couldn't create SaveGameplayDelegates object.")); + UE_LOG(LogG2I, Error, TEXT("Couldn't create SaveGameplayDelegates object in %s."), *GetName()); return; } @@ -176,19 +177,20 @@ void UG2IGameInstance::LoadGameplay_Implementation(bool bAsync) void UG2IGameInstance::SaveAllData_Implementation() { - // Getting all actors with gameplay tag 'Savable' + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); + return; + } + + // Getting all actors with interface 'Savable' TArray FoundSavableActors; - GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); + GetAllActorsWithSavableIntetrface(FoundSavableActors); // Iterating on them & saving their data for (auto* Actor : FoundSavableActors) { - if (Actor->Implements()) - { - IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); - } - else - UE_LOG(LogG2I, Warning, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); } UE_LOG(LogG2I, Log, TEXT("Gameplay data saved & stored in GameplaySaveGame object.")); @@ -196,45 +198,26 @@ void UG2IGameInstance::SaveAllData_Implementation() void UG2IGameInstance::LoadAllData_Implementation() { - // Getting all actors with gameplay tag 'Savable' - TArray FoundSavableActors; - GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); - - // Iterating on them & loading their data - for (auto* Actor : FoundSavableActors) + if (!GameplaySaveGame) { - if (Actor->Implements()) - { - IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); - } - else - UE_LOG(LogG2I, Warning, TEXT("Actor %s doesn't implement Savable interface. It's data won't be loaded."), *Actor->GetActorNameOrLabel()); + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); + return; } - UE_LOG(LogG2I, Log, TEXT("Gameplay data loaded from the GameplaySaveGame object.")); -} - -void UG2IGameInstance::SyncGameplayLoadGameData() -{ - // Getting all actors with tag 'Savable' + // Getting all actors with interface 'Savable' TArray FoundSavableActors; - GetAllActorsWithGameplayTag(FoundSavableActors, FName(TEXT("Savable"))); + GetAllActorsWithSavableIntetrface(FoundSavableActors); // Iterating on them & loading their data for (auto* Actor : FoundSavableActors) { - if (Actor->Implements()) - { - IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); - } - else - UE_LOG(LogG2I, Warning, TEXT("Actor %s doesn't implement Savable interface. It's data will be lost."), *Actor->GetActorNameOrLabel()); + IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); } - UE_LOG(LogG2I, Log, TEXT("Gameplay data synced & loaded in Savable objects.")); + UE_LOG(LogG2I, Log, TEXT("Gameplay data loaded from the GameplaySaveGame object.")); } -void UG2IGameInstance::GetAllActorsWithGameplayTag(TArray& FoundActors, FName TagName) +void UG2IGameInstance::GetAllActorsWithSavableIntetrface(TArray& FoundActors) { UWorld* World = GetWorld(); if (!World) @@ -243,14 +226,10 @@ void UG2IGameInstance::GetAllActorsWithGameplayTag(TArray& FoundActors, return; } - FGameplayTagQuery TagQuery = FGameplayTagQuery::MakeQuery_MatchTag(FGameplayTag::RequestGameplayTag(TagName)); - - UBlueprintGameplayTagLibrary::GetAllActorsOfClassMatchingTagQuery( - World, - AActor::StaticClass(), - TagQuery, - FoundActors - ); + UGameplayStatics::UGameplayStatics::GetAllActorsWithInterface( + this, + UG2ISavableInterface::StaticClass(), + FoundActors); } void UG2IGameInstance::SaveAllDataAndGameplay_Implementation(bool bAsync) @@ -261,6 +240,12 @@ void UG2IGameInstance::SaveAllDataAndGameplay_Implementation(bool bAsync) void UG2IGameInstance::SaveRequestedData_Implementation(UObject* Requester) { + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); + return; + } + if (Requester) { if (Requester->Implements()) @@ -274,6 +259,12 @@ void UG2IGameInstance::SaveRequestedData_Implementation(UObject* Requester) void UG2IGameInstance::LoadRequestedData_Implementation(UObject* Requester) { + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); + return; + } + if (Requester) { if (Requester->Implements()) diff --git a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp index 86519dfe..32ec9052 100644 --- a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp +++ b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp @@ -6,12 +6,6 @@ AG2ISavingTriggerBox::AG2ISavingTriggerBox() { OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnOverlapBegin); - FGameplayTag SavableTag = UGameplayTagsManager::Get().RequestGameplayTag(FName("Savable")); - if (SavableTag.IsValid()) - { - GameplayTags.AddTag(SavableTag); - } - #if WITH_EDITOR SetActorHiddenInGame(false); #endif @@ -41,7 +35,7 @@ void AG2ISavingTriggerBox::OnOverlapBegin(AActor* OverlappedActor, AActor* Other IG2ISaveGameplayInterface::Execute_SaveAllDataAndGameplay(GameInstance, true); } else - UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface or is NULL in %s."), *GameInstance->GetName(), *GetName()); + UE_LOG(LogG2I, Warning, TEXT("GameInstance doesn't implement interface UG2ISaveGameplayInterface or is NULL in %s."), *GetName()); } void AG2ISavingTriggerBox::OnGameplaySaved(bool bSuccess) @@ -52,18 +46,12 @@ void AG2ISavingTriggerBox::OnGameplaySaved(bool bSuccess) void AG2ISavingTriggerBox::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) { - if (SaveGameRef) - { - SaveGameRef->SaveTriggerBoxesSaveData.Add(GetName(), bActivated); - } + SaveGameRef->SaveTriggerBoxesSaveData.Add(GetActorLocation(), bActivated); } void AG2ISavingTriggerBox::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) { - if (SaveGameRef) - { - if (auto* Key = SaveGameRef->SaveTriggerBoxesSaveData.Find(GetName())) - if (&Key) - Destroy(); - } + if (auto* Key = SaveGameRef->SaveTriggerBoxesSaveData.Find(GetActorLocation())) + if (*Key) + Destroy(); } diff --git a/Source/G2I/Public/Characters/G2ICharacterDaughter.h b/Source/G2I/Public/Characters/G2ICharacterDaughter.h index f157d459..6d0c311b 100644 --- a/Source/G2I/Public/Characters/G2ICharacterDaughter.h +++ b/Source/G2I/Public/Characters/G2ICharacterDaughter.h @@ -3,6 +3,8 @@ #include "CoreMinimal.h" #include "G2ICharacterInterface.h" #include "GameFramework/Character.h" +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "Interfaces/SavingSystem/G2ISavableInterface.h" #include "G2ICharacterDaughter.generated.h" class UG2IFlightComponent; @@ -21,7 +23,7 @@ class UG2IInventoryComponent; * Implements a controllable orbiting camera */ UCLASS(Blueprintable) -class AG2ICharacterDaughter : public ACharacter, public IG2ICharacterInterface +class AG2ICharacterDaughter : public ACharacter, public IG2ICharacterInterface, public IG2ISavableInterface { GENERATED_BODY() @@ -61,6 +63,10 @@ class AG2ICharacterDaughter : public ACharacter, public IG2ICharacterInterface explicit AG2ICharacterDaughter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + + void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); + virtual void PossessedBy(AController* NewController) override; virtual void UnPossessed() override; diff --git a/Source/G2I/Public/Characters/G2ICharacterEngineer.h b/Source/G2I/Public/Characters/G2ICharacterEngineer.h index 70c10701..ed3dd72c 100644 --- a/Source/G2I/Public/Characters/G2ICharacterEngineer.h +++ b/Source/G2I/Public/Characters/G2ICharacterEngineer.h @@ -3,6 +3,8 @@ #include "CoreMinimal.h" #include "G2ICharacterInterface.h" #include "GameFramework/Character.h" +#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "Interfaces/SavingSystem/G2ISavableInterface.h" #include "Components/G2IValveInteractionComponent.h" #include "Components/G2IHoleInteractionComponent.h" #include "G2ICharacterEngineer.generated.h" @@ -20,7 +22,7 @@ class UG2IInventoryComponent; * Implements a controllable orbiting camera */ UCLASS(Blueprintable) -class G2I_API AG2ICharacterEngineer : public ACharacter, public IG2ICharacterInterface +class G2I_API AG2ICharacterEngineer : public ACharacter, public IG2ICharacterInterface, public IG2ISavableInterface { GENERATED_BODY() @@ -67,6 +69,10 @@ class G2I_API AG2ICharacterEngineer : public ACharacter, public IG2ICharacterInt explicit AG2ICharacterEngineer(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + + void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); + virtual void PossessedBy(AController* NewController) override; virtual void UnPossessed() override; diff --git a/Source/G2I/Public/Controls/G2IPlayerController.h b/Source/G2I/Public/Controls/G2IPlayerController.h index 8b66087b..e25db393 100644 --- a/Source/G2I/Public/Controls/G2IPlayerController.h +++ b/Source/G2I/Public/Controls/G2IPlayerController.h @@ -3,10 +3,7 @@ #include "CoreMinimal.h" #include "EnhancedActionKeyMapping.h" #include "GameFramework/PlayerController.h" -#include "GameplayTagContainer.h" -#include "GameplayTagsManager.h" #include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" -#include "Interfaces/SavingSystem/G2ISavableInterface.h" #include "G2IPlayerController.generated.h" class UG2IUIManager; @@ -26,7 +23,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FToggleFollowAIBehindPlayerDelegate, * Manages input mappings */ UCLASS(abstract) -class G2I_API AG2IPlayerController : public APlayerController, public IG2ISavableInterface +class G2I_API AG2IPlayerController : public APlayerController { GENERATED_BODY() @@ -77,15 +74,6 @@ class G2I_API AG2IPlayerController : public APlayerController, public IG2ISavabl TMap, FName>& GetActionToTagMap(); -public: - /* Saving system */ - - UPROPERTY(EditAnywhere, BlueprintReadOnly) - FGameplayTagContainer GameplayTags; - - void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); - - void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); protected: /** Setup Input */ diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index 7fea3eae..199a045d 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -106,12 +106,9 @@ class G2I_API UG2IGameInstance : public UGameInstance, public IG2ISaveGameplayIn UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") void CreateSaveGameplayDelegates(); - UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") - void SyncGameplayLoadGameData(); - - // Finding all actors that have gameplay tag 'TagName' + // Finding all actors that have 'gameplay tag 'TagName''Savable' interface UFUNCTION(BlueprintCallable) - void GetAllActorsWithGameplayTag(TArray& FoundActors, FName TagName); + void GetAllActorsWithSavableIntetrface(TArray& FoundActors); void OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess); diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h index fcf19cea..6cc24579 100644 --- a/Source/G2I/Public/Game/G2IGameplaySaveGame.h +++ b/Source/G2I/Public/Game/G2IGameplaySaveGame.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "GameFramework/SaveGame.h" +#include "GameFramework/Character.h" #include "G2IGameplaySaveGame.generated.h" USTRUCT(BlueprintType) @@ -10,10 +11,10 @@ struct FPlayersSaveData GENERATED_BODY() UPROPERTY(SaveGame, BlueprintReadWrite) - TMap CharactersLocation; + TMap, FTransform> CharactersTransform; UPROPERTY(SaveGame, BlueprintReadWrite) - FString CurrentCharacter = TEXT(""); + TSubclassOf CurrentCharacter; }; /** @@ -29,5 +30,5 @@ class G2I_API UG2IGameplaySaveGame : public USaveGame FPlayersSaveData PlayersSaveData; UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Saving Trigger Boxes") - TMap SaveTriggerBoxesSaveData; + TMap SaveTriggerBoxesSaveData; }; diff --git a/Source/G2I/Public/Game/G2ISavingTriggerBox.h b/Source/G2I/Public/Game/G2ISavingTriggerBox.h index 5ae59486..bffead4b 100644 --- a/Source/G2I/Public/Game/G2ISavingTriggerBox.h +++ b/Source/G2I/Public/Game/G2ISavingTriggerBox.h @@ -2,8 +2,6 @@ #include "CoreMinimal.h" #include "Engine/TriggerBox.h" -#include "GameplayTagContainer.h" -#include "GameplayTagsManager.h" #include "Interfaces/SavingSystem/G2ISavableInterface.h" #include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" #include "G2ISavingTriggerBox.generated.h" @@ -16,10 +14,6 @@ class G2I_API AG2ISavingTriggerBox : public ATriggerBox, public IG2ISavableInter { GENERATED_BODY() -public: - UPROPERTY(EditAnywhere, BlueprintReadOnly) - FGameplayTagContainer GameplayTags; - protected: UPROPERTY(BlueprintReadOnly) TObjectPtr GameInstance; From b2ce8091fdbf55d871713c93cf1a7c9e29267cef Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:28:50 +0300 Subject: [PATCH 10/15] feat: added saving trigger boxes on test level --- .../4/76/6L66UZVID6WZS0H9AKAK77.uasset | Bin 0 -> 3819 bytes .../8/WQ/XBYSC668Y63NHQOBB7E3VE.uasset | Bin 0 -> 3813 bytes .../9/L7/JL34ZRUO6G1TEMXPIJ4LT3.uasset | Bin 5658 -> 6192 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Content/__ExternalActors__/G2I_Game/Maps/TestLevel/4/76/6L66UZVID6WZS0H9AKAK77.uasset create mode 100644 Content/__ExternalActors__/G2I_Game/Maps/TestLevel/8/WQ/XBYSC668Y63NHQOBB7E3VE.uasset diff --git a/Content/__ExternalActors__/G2I_Game/Maps/TestLevel/4/76/6L66UZVID6WZS0H9AKAK77.uasset b/Content/__ExternalActors__/G2I_Game/Maps/TestLevel/4/76/6L66UZVID6WZS0H9AKAK77.uasset new file mode 100644 index 0000000000000000000000000000000000000000..ba57eb4be78c64bda0d91fa3165ffd2b51fe42e3 GIT binary patch literal 3819 zcmcgvYj6|S6}}Kd91zSkvO3SpJwt2LWmNc&+({_dc0W%wjwm)sy z_RxlZ<)0O&0k%>eJ@T-9kL}pJO`8jg8W-FF7`x`Vx1GK>mh9YJx4yg}dCUUXXX!m3 ze8O%ydHuV(A2J7>Eq4KyPSk$X)6vwIZ)jV;Y0aJUZv@N-kH*Dic}*)W!hxTnS zMtQ$>qD6=F{UqS{pd5PVrE?$u>bU!<$eb6pp4xaDt`_>j!OS2t$ux2@^YZjMcx!GB zJB-aiUf{U`zueejQuKuU$NJP1dN5L?-g9gocFsV|eRC=p{e{gkhC87ifP+;!G|+?+ zQYJ5JY@)=6JLBv4giOwOmVa{==#SU z5}%Vv%h=8jaME#iCYj`OWMs&}LgP2z^`{~}7KE4SM zn{z_bGWf-=uv*|_f;wuVrCczctP@afl_x;xdn;NnyaD@Vq2L?Ccv@gglS^EdJ9+%S2_i~WTSPTM%xKbvUS|w#tDPDrj zL7F%B7BEGV>Fz$(^tWY`>tbQXV~?3zjV6Q50^MYIDgRx*?;@o0=I@#vVV zdDM1EB@{UW<7-m;)x7h7tFThlY0hd%4QWvoSbeGB#-I;k*rx)n#(hzYws)$X(JBt! zA~g3HZ@bDlT@{bo(>Sz6{BrT0Qw`5m#rp#KSX&->?dfmf*=barZN(xmg&>8-yj;9` zm4vr5^3+>;-0J~#PDm5sxomxF89$!)Ty&mWCaES`K+ zxri~>?e^xQ`lOKP^Oksq<%@~DCKze&4@z0PzfBu$5B2&gajyJpv1`(DsoPZOb+rdr zgWp*09Q3rVs~CpDPIjF|bz&drQicbwU5ti$w%DnjWl8~xewh~iYdWGSlQ-b*X86(& zpD#80l6|&>zBOoOgGG0Maa1|}s^e9KhU7VMt`5jgw?CcC3dN`{Smq+hT(rk)iyKm9 zWU~s#Sc+#c$6rQPtRVOyaKW~%-11j*I`Cwi#ha80RxcD(P^rNV9{uVPt0?h`MZpoE zU>#6CdDT8*RPdgwo%_4qIp4DutwW-p-xgxijrl zB^nFMf-z#+rcWW{J7|$kU0y$X>++W>w_n#cQoAks@_kpX zdgGOugj^21Z5vNkjQXZyB_dWObJYdyTAM87J)wb8yZd<94<&fL|!s1wzzFyDPx$BK)hs|a#3gbdQbQvKtpgjc2Nbi@Aw`h=# zj{}Y$(kCZgjVv4S1UBbp4F74vvj=ds&=v+Wl}sa-kmJHrQz~FoQ`71))r5JG=Q8|q zQ>|I$6Y_=q;Ys+Qq|%Frw(iExN$~m16$Om`!e$x6oiGQ0zm)JQVXTY9WFf8S9Fokp zHh162#}sn1Vfk&-fv2vvCo9B>=jnxhoVAKHaoxN~cFvQdvk+1gE|v^(GM`dN?bxml zanjvhAs**bWX)?0(@}_(BrzyUX*f z>8rD@gh63)MH%M3XFtOQXilkVp$fZfmbjuT}3Pn^dkw3^+&j*zt7whE` ze3KO8L}7?iAm9M((@dI+@uWh@ugC2RNd!(#l|l)!J~(F?E`C^&l=1mhKF2*a8_-Bv z78HIQKbD$y8d63gid+o&<^%F#3=6PqOew;8^7tt1O40_olVq}^x-5Pj3=3yxl7m9F zcbu+3TQvW!6wIF|>+EYA&@F{QoqXQ3bth(r6-2RH;^g>vlq3K9ep(^FgX7@sUz%Z4 zLpDV&lOf8$;3k~*8M!5^|HIC~rF8N=93(Ru1xFDsC!`WBvXDsdGHeb~Gt!g66ivii zdSubt%O>VUTbTxD)KYIU*TGq~um)2d>oDt$qAiNic8S&*Es?=ngytUoZP#SJ zF7Ze8sUO-Rem;L&G{x^r{Cx*~tSz5DUEPIer&=@i8!Q5o5QzbUb zeqm`)FCIUya86F)$P+MOmH}$;w)M34$)Q$VpT+HN>=y*1VlkPJr3`fW+jYst&U|tOXw`dB^^QU#dskk8j@bQ`&LQ>xADOLrOL zVyxGf;~6$(i^m-;euot9cbUx`pLBZa^94T_{ zDAMg@TdbN9=Ri;m4~9WTM=e{Ol+QB35Ji7V3-$-SO3_KL4@Dinjg$oH8l*+J!r@pkhs!Y6HkuU-zECG11khH2%7{D)H zapklB)pNzK?OFTK=;0k>59I8Rzc+l>)T&?E@x_C5(S~}L{;=kmuQu41%eQn?9NK6H z-G486dFsk@LZp+Ma64T79Xr<5I#nb&e6g(qSG#&307nz$}5ZrK||$< zZ{B8`UfSh;a>db|PtHskX?exJ9hB7P!DJPSUMcuLk&+rBy2E_ZJK*fKIRJlHAN}^J dduwlg<-h2aJ_e|f++k~|?4K{*q}KqH{}I1cO5Okf literal 0 HcmV?d00001 diff --git a/Content/__ExternalActors__/G2I_Game/Maps/TestLevel/9/L7/JL34ZRUO6G1TEMXPIJ4LT3.uasset b/Content/__ExternalActors__/G2I_Game/Maps/TestLevel/9/L7/JL34ZRUO6G1TEMXPIJ4LT3.uasset index d0f10a0a86e14ab96fe017864bc86eb0762ca772..2b7df57be1863ca4d9893d2bf04932f7fe519e93 100644 GIT binary patch delta 1916 zcmaJ?TS!z<6g}6uzGi&P!#lomG_4%d9P=?+QjL!qv<4+13W^w~D+)Tw%oXuVhNAwW zI6+hv_2EZkVMswlg-Boh2u9xpK_C6}FwGv=-shg9*GAnRcb&7>+UM-^+V^I~RMnLn zXLIOj=F8U;3zLmmi%0I=AMuCNzRI@8m6=k8pAgcX03R?{Dm^NddtIr7n4mjvA;b#U z1%0%V^fg3{=zGBLNvD&NOY+kQVP;FtYGcq`&Mzm2fG4E|hUm!TN>Z`jJi_w_em5~HQ$4FR> zkUOO*qtkEYY$=B=UJGh{p@16gRih9{E?8gOpq~?`4tgQL;|~s~9)ES)S=JCK9P8_j z1S6xK!#zD}NQ-&`z+;MfpVTgWN~X7xW*Tx?GJ4u=;iwi2ht!rq*3gLDXn&ZAg7v|7 zW8qnPI{8UbF>r+7f#EQu9zS3|V4s`yCUMajvqLd54gyWzgY9pt*ElU!gn5eT$$w#M^pSKHfuMcLFe;sphJ9re;&7c2h=V#YP+RaUDV( zZ*R1Q-JJUgYiQU)Cw3iAVBZ!xeg@6t8wgvs5FX*&66g+Zp!-WZEV*!@^Z2@C8=SX7 z=gf)pk=b2o=Wd8z;nCy|v|FI_-oD(C0$9y8F8gYz%i?p2tWty)sHZ0fWrQXZXg zIHW@AvlZ3Hr6=xdTzcPOcPjJ*Nagf(TUmlsgae&4D%sL;w||40b)cQhqXbyRxfsui zZ2?ANDMjquNg_OxN%E# tKiS1m94w}0!*Tbs>9aU3_V0h(!Fl7%`aL?OIFy~6umK5+i$Xst-hV4K9d`f# delta 1513 zcma)6%}*0i5Pz?&wM$!;?y|e3YOScWNG*s^6hk#0_22C~9zKJFhHlQ<%g4=HoZ>X5PmxjXcUeh}&z+ z3o|QoTGhP#^muo0Lp zU)|$Km=7+&oxN*tGqf!CLs}av4s(fLSm6}weX6Vny}YBl;QhpUC)@BO`^M^66yK3J z(SbP5u?^(q+VI#V@=8IZZ6Ikkh?h4cin}9b>$DvUs_AxG__(Ua>$|2(KElBoPCTNB zj0B3Oj*E`&LwNRORSf4sD;nx9M!E4q4_u2}u~jP}5fHZm_52$#kdXwKkxXKiU0k>U_x194^tFG0 z-p=OPhFgeU)-#N>+8}%~9C+b$!K;{QMLA+J(o8+@G3FGhJteLlW2@yL)n+CX9ntNS z-t=L1whi)cKzRGJ>j_XbY?wFVG7V3Sw51;RHYQGzl@2_yOqqa`nVM*6P2HQ8)qmhY z3~+1d@@;d_C-+gIelODacCJFl}sbks^m^ zv8Z>NehR{>t>raNZK-{L11Uy#LDZ>5ozf>=kHKNJ8_k&HxOerfTQTRD*1-;pyzGOj GbMZg75v^zd From c6228d4b0e82b8faf6e79bd1aa3bedaabc345db3 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:19:26 +0300 Subject: [PATCH 11/15] refact: deleted unused files and tag --- Config/Tags/G2IGameplayTags.ini | 2 +- Source/G2I/Private/Game/G2IGameplaySaveGame.cpp | 2 -- Source/G2I/Private/Game/G2ISettingsSaveGame.cpp | 2 -- .../Private/Interfaces/SavingSystem/G2ISavableInterface.cpp | 6 ------ .../Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp | 3 --- 5 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 Source/G2I/Private/Game/G2IGameplaySaveGame.cpp delete mode 100644 Source/G2I/Private/Game/G2ISettingsSaveGame.cpp delete mode 100644 Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp delete mode 100644 Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp diff --git a/Config/Tags/G2IGameplayTags.ini b/Config/Tags/G2IGameplayTags.ini index 07e3f1e2..e8462b40 100644 --- a/Config/Tags/G2IGameplayTags.ini +++ b/Config/Tags/G2IGameplayTags.ini @@ -1,4 +1,4 @@ [/Script/GameplayTags.GameplayTagsList] GameplayTagList=(Tag="Interaction.Pipes.TechnicalHoles",DevComment="Allows interacting with technical holes") GameplayTagList=(Tag="Interaction.Pipes.Valves",DevComment="Allows interacting with valves") -GameplayTagList=(Tag="Savable",DevComment="Is required for saving/loading data") + diff --git a/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp b/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp deleted file mode 100644 index 464201ce..00000000 --- a/Source/G2I/Private/Game/G2IGameplaySaveGame.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Game/G2IGameplaySaveGame.h" - diff --git a/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp b/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp deleted file mode 100644 index 27acc25c..00000000 --- a/Source/G2I/Private/Game/G2ISettingsSaveGame.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Game/G2ISettingsSaveGame.h" - diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp deleted file mode 100644 index 711ba3ae..00000000 --- a/Source/G2I/Private/Interfaces/SavingSystem/G2ISavableInterface.cpp +++ /dev/null @@ -1,6 +0,0 @@ - - - -#include "Interfaces/SavingSystem/G2ISavableInterface.h" - -// Add default functionality here for any IG2ISavableInterface functions that are not pure virtual. diff --git a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp b/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp deleted file mode 100644 index 49915a60..00000000 --- a/Source/G2I/Private/Interfaces/SavingSystem/G2ISaveSettingsInterface.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "Interfaces/SavingSystem/G2ISaveSettingsInterface.h" - -// Add default functionality here for any IG2ISaveSettingsInterface functions that are not pure virtual. From 396f81189c25b3bfcd8efd63ec88b2806cba0b27 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:22:32 +0300 Subject: [PATCH 12/15] refact: moved saving from interface to subsystem --- .../Private/Controls/G2IPlayerController.cpp | 24 +- Source/G2I/Private/Game/G2IGameInstance.cpp | 275 ------------------ .../Private/Game/G2ISavingGameplayManager.cpp | 235 +++++++++++++++ .../G2I/Private/Game/G2ISavingTriggerBox.cpp | 48 ++- Source/G2I/Public/Game/G2IGameInstance.h | 93 +----- .../Public/Game/G2ISavingGameplayManager.h | 110 +++++++ Source/G2I/Public/Game/G2ISavingTriggerBox.h | 10 +- .../SavingSystem/G2ISaveGameplayInterface.h | 73 ----- 8 files changed, 403 insertions(+), 465 deletions(-) create mode 100644 Source/G2I/Private/Game/G2ISavingGameplayManager.cpp create mode 100644 Source/G2I/Public/Game/G2ISavingGameplayManager.h diff --git a/Source/G2I/Private/Controls/G2IPlayerController.cpp b/Source/G2I/Private/Controls/G2IPlayerController.cpp index 5314ff8f..5e7b4423 100644 --- a/Source/G2I/Private/Controls/G2IPlayerController.cpp +++ b/Source/G2I/Private/Controls/G2IPlayerController.cpp @@ -19,6 +19,7 @@ #include "G2ISteamShotInputInterface.h" #include "G2IUIManager.h" #include "G2IWidgetNames.h" +#include "G2ISavingGameplayManager.h" #include "Kismet/KismetSystemLibrary.h" void AG2IPlayerController::SetupInputComponent() @@ -637,10 +638,16 @@ void AG2IPlayerController::SaveGameplay(const FInputActionValue& Value) { if (auto* GameInstance = GetGameInstance()) { - if (GameInstance->Implements()) - IG2ISaveGameplayInterface::Execute_SaveAllDataAndGameplay(GameInstance, true); + + if (UG2ISavingGameplayManager* SavingGameplayManager = GameInstance->GetSubsystem()) + { + SavingGameplayManager->SaveAllDataAndGameplay(true); + } else - UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface."), *GameInstance->GetName()); + { + UE_LOG(LogG2I, Warning, TEXT("Couldn't get SavingGameplayManager subsystem from GameInstance in %s."), *GetName()); + return; + } } } @@ -648,13 +655,16 @@ void AG2IPlayerController::LoadGameplay(const FInputActionValue& Value) { if (auto* GameInstance = GetGameInstance()) { - if (GameInstance->Implements()) + if (UG2ISavingGameplayManager* SavingGameplayManager = GameInstance->GetSubsystem()) { - IG2ISaveGameplayInterface::Execute_LoadGameplay(GameInstance, false); - IG2ISaveGameplayInterface::Execute_LoadAllData(GameInstance); + SavingGameplayManager->LoadGameplay(false); + SavingGameplayManager->LoadAllData(); } else - UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface."), *GameInstance->GetName()); + { + UE_LOG(LogG2I, Warning, TEXT("Couldn't get SavingGameplayManager subsystem from GameInstance in %s."), *GetName()); + return; + } } } #endif diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index e144dff3..b98661ac 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -1,277 +1,2 @@ #include "G2IGameInstance.h" #include "G2I.h" -#include "Kismet/GameplayStatics.h" -#include "GameplayTagContainer.h" -#include "GameplayTagsManager.h" -#include "BlueprintGameplayTagLibrary.h" -#include "Interfaces/SavingSystem/G2ISavableInterface.h" - -UG2IWidgetsCatalog* UG2IGameInstance::GetWidgetsCatalog() -{ - return WidgetsCatalog; -} - -UG2IStringTablesCatalog* UG2IGameInstance::GetStringTablesCatalog() -{ - return StringTablesCatalog; -} - -UG2IWidgetComponentParameters* UG2IGameInstance::GetWidgetComponentParameters() -{ - return WidgetComponentsParameters; -} - - -void UG2IGameInstance::CreateNewGameplaySaveGameObject_Implementation() -{ - if (UGameplayStatics::DoesSaveGameExist(GameplaySaveSlotName, 0)) - UGameplayStatics::DeleteGameInSlot(GameplaySaveSlotName, 0); - - CreateGameplaySaveGame(); -} - -void UG2IGameInstance::Init() -{ - Super::Init(); - - CreateSaveGameplayDelegates(); - CreateGameplaySaveGame(); -} - -void UG2IGameInstance::CreateGameplaySaveGame() -{ - GameplaySaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UG2IGameplaySaveGame::StaticClass())); - if (!ensure(GameplaySaveGame)) - UE_LOG(LogG2I, Error, TEXT("Couldn't create GameplaySaveGame object in %s."), *GetName()); -} - -void UG2IGameInstance::CreateSaveGameplayDelegates() -{ - SaveGameplayDelegates = NewObject(this); - if (!ensure(SaveGameplayDelegates)) - { - UE_LOG(LogG2I, Error, TEXT("Couldn't create SaveGameplayDelegates object in %s."), *GetName()); - return; - } - - OnGameplayAsyncSavedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncSaved); - OnGameplayAsyncLoadedDelegate.BindUObject(this, &UG2IGameInstance::OnGameplayAsuncLoaded); -} - -void UG2IGameInstance::OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess) -{ - SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(bSuccess); - if (bSuccess) - { - UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); - } - else - UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); -} - -void UG2IGameInstance::OnGameplayAsuncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData) -{ - if (LoadedGameData) - { - if (GameplaySaveGame = Cast(LoadedGameData)) - { - UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); - SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(true); - } - else - { - // SaveGame file exists, but not valid - UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); - SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); - } - } - else - { - // Load failed - UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); - SaveGameplayDelegates->OnGameplayLoadedDelegate.Broadcast(false); - } -} - -void UG2IGameInstance::SaveGameplay_Implementation(bool bAsync) -{ - if (!GameplaySaveGame) - CreateGameplaySaveGame(); - if (!SaveGameplayDelegates) - CreateSaveGameplayDelegates(); - - if (bAsync) - { - // Asynchronous - SaveGameplayDelegates->OnGameplaySaveStartedDelegate.Broadcast(); - UGameplayStatics::AsyncSaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0, OnGameplayAsyncSavedDelegate); - } - else - { - // Synchronous - SaveGameplayDelegates->OnGameplaySaveStartedDelegate.Broadcast(); - if (UGameplayStatics::SaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0)) - { - // Successfully saved - SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(true); - UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); - } - else - { - // Failed to save - SaveGameplayDelegates->OnGameplaySavedDelegate.Broadcast(false); - UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); - } - } -} - -void UG2IGameInstance::SetGameplaySaveSlotName_Implementation(const FString& NewSlotName) -{ - GameplaySaveSlotName = NewSlotName; -} - -const FString UG2IGameInstance::GetGameplaySaveSlotName_Implementation() -{ - return GameplaySaveSlotName; -} - -UG2ISaveGameplayDelegates* UG2IGameInstance::GetGameplaySaveDelegates_Implementation() -{ - return SaveGameplayDelegates; -} - -void UG2IGameInstance::LoadGameplay_Implementation(bool bAsync) -{ - if (!SaveGameplayDelegates) - CreateSaveGameplayDelegates(); - - if (bAsync) - { - // Asynchronous - SaveGameplayDelegates->OnGameplayLoadStartedDelegate.Broadcast(); - UGameplayStatics::AsyncLoadGameFromSlot(GameplaySaveSlotName, 0, OnGameplayAsyncLoadedDelegate); - } - else - { - // Synchronous - if (USaveGame* LoadedGame = UGameplayStatics::LoadGameFromSlot(GameplaySaveSlotName, 0)) - { - if (GameplaySaveGame = Cast(LoadedGame)) - { - // Successfully loaded - UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); - } - else - { - // Loaded SaveGame object, but it's invalid - UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); - } - } - else - { - // Failed to load - UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); - } - } -} - -void UG2IGameInstance::SaveAllData_Implementation() -{ - if (!GameplaySaveGame) - { - UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); - return; - } - - // Getting all actors with interface 'Savable' - TArray FoundSavableActors; - GetAllActorsWithSavableIntetrface(FoundSavableActors); - - // Iterating on them & saving their data - for (auto* Actor : FoundSavableActors) - { - IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); - } - - UE_LOG(LogG2I, Log, TEXT("Gameplay data saved & stored in GameplaySaveGame object.")); -} - -void UG2IGameInstance::LoadAllData_Implementation() -{ - if (!GameplaySaveGame) - { - UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); - return; - } - - // Getting all actors with interface 'Savable' - TArray FoundSavableActors; - GetAllActorsWithSavableIntetrface(FoundSavableActors); - - // Iterating on them & loading their data - for (auto* Actor : FoundSavableActors) - { - IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); - } - - UE_LOG(LogG2I, Log, TEXT("Gameplay data loaded from the GameplaySaveGame object.")); -} - -void UG2IGameInstance::GetAllActorsWithSavableIntetrface(TArray& FoundActors) -{ - UWorld* World = GetWorld(); - if (!World) - { - UE_LOG(LogG2I, Error, TEXT("World doesn't exist in %s."), *GetName()); - return; - } - - UGameplayStatics::UGameplayStatics::GetAllActorsWithInterface( - this, - UG2ISavableInterface::StaticClass(), - FoundActors); -} - -void UG2IGameInstance::SaveAllDataAndGameplay_Implementation(bool bAsync) -{ - IG2ISaveGameplayInterface::Execute_SaveAllData(this); - IG2ISaveGameplayInterface::Execute_SaveGameplay(this, bAsync); -} - -void UG2IGameInstance::SaveRequestedData_Implementation(UObject* Requester) -{ - if (!GameplaySaveGame) - { - UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); - return; - } - - if (Requester) - { - if (Requester->Implements()) - { - IG2ISavableInterface::Execute_SaveData(Requester, GameplaySaveGame); - } - else - UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement Savable interface. It's data will be lost."), *Requester->GetName()); - } -} - -void UG2IGameInstance::LoadRequestedData_Implementation(UObject* Requester) -{ - if (!GameplaySaveGame) - { - UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); - return; - } - - if (Requester) - { - if (Requester->Implements()) - { - IG2ISavableInterface::Execute_LoadData(Requester, GameplaySaveGame); - } - else - UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement Savable interface. It's data won't be loaded."), *Requester->GetName()); - } -} diff --git a/Source/G2I/Private/Game/G2ISavingGameplayManager.cpp b/Source/G2I/Private/Game/G2ISavingGameplayManager.cpp new file mode 100644 index 00000000..a912c775 --- /dev/null +++ b/Source/G2I/Private/Game/G2ISavingGameplayManager.cpp @@ -0,0 +1,235 @@ +#include "Game/G2ISavingGameplayManager.h" +#include "G2I.h" +#include "Interfaces/SavingSystem/G2ISavableInterface.h" +#include "Kismet/GameplayStatics.h" + + +void UG2ISavingGameplayManager::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + + OnGameplayAsyncSavedDelegate.BindUObject(this, &UG2ISavingGameplayManager::OnGameplayAsyncSaved); + OnGameplayAsyncLoadedDelegate.BindUObject(this, &UG2ISavingGameplayManager::OnGameplayAsyncLoaded); +} + +void UG2ISavingGameplayManager::CreateNewGameplaySaveGameObject() +{ + if (UGameplayStatics::DoesSaveGameExist(GameplaySaveSlotName, 0)) + UGameplayStatics::DeleteGameInSlot(GameplaySaveSlotName, 0); + + CreateGameplaySaveGame(); +} + +void UG2ISavingGameplayManager::CreateGameplaySaveGame() +{ + GameplaySaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UG2IGameplaySaveGame::StaticClass())); + if (!ensure(GameplaySaveGame)) + UE_LOG(LogG2I, Error, TEXT("Couldn't create GameplaySaveGame object in %s."), *GetName()); +} + +void UG2ISavingGameplayManager::OnGameplayAsyncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess) +{ + OnGameplaySavedDelegate.Broadcast(bSuccess); + if (bSuccess) + { + UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); + } + else + UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); +} + +void UG2ISavingGameplayManager::OnGameplayAsyncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData) +{ + if (!LoadedGameData) + { + // Load failed + UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); + OnGameplayLoadedDelegate.Broadcast(false); + return; + } + + if (GameplaySaveGame = Cast(LoadedGameData)) + { + UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); + OnGameplayLoadedDelegate.Broadcast(true); + } + else + { + // SaveGame file exists, but not valid + UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); + OnGameplayLoadedDelegate.Broadcast(false); + } +} + +void UG2ISavingGameplayManager::SaveGameplay(bool bAsync) +{ + if (!GameplaySaveGame) + CreateGameplaySaveGame(); + + if (bAsync) + { + // Asynchronous + OnGameplaySaveStartedDelegate.Broadcast(); + UGameplayStatics::AsyncSaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0, OnGameplayAsyncSavedDelegate); + } + else + { + // Synchronous + OnGameplaySaveStartedDelegate.Broadcast(); + if (UGameplayStatics::SaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0)) + { + // Successfully saved + OnGameplaySavedDelegate.Broadcast(true); + UE_LOG(LogG2I, Log, TEXT("Gameplay saved successfully in the slot %s."), *GameplaySaveSlotName); + } + else + { + // Failed to save + OnGameplaySavedDelegate.Broadcast(false); + UE_LOG(LogG2I, Error, TEXT("Gameplay saving in the slot %s failed."), *GameplaySaveSlotName); + } + } +} + +void UG2ISavingGameplayManager::SetGameplaySaveSlotName(const FString& NewSlotName) +{ + GameplaySaveSlotName = NewSlotName; +} + +const FString UG2ISavingGameplayManager::GetGameplaySaveSlotName() +{ + return GameplaySaveSlotName; +} + +void UG2ISavingGameplayManager::LoadGameplay(bool bAsync) +{ + if (bAsync) + { + // Asynchronous + OnGameplayLoadStartedDelegate.Broadcast(); + UGameplayStatics::AsyncLoadGameFromSlot(GameplaySaveSlotName, 0, OnGameplayAsyncLoadedDelegate); + } + else + { + // Synchronous + if (USaveGame* LoadedGame = UGameplayStatics::LoadGameFromSlot(GameplaySaveSlotName, 0)) + { + if (GameplaySaveGame = Cast(LoadedGame)) + { + // Successfully loaded + UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); + } + else + { + // Loaded SaveGame object, but it's invalid + UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); + } + } + else + { + // Failed to load + UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); + } + } +} + +void UG2ISavingGameplayManager::SaveAllData() +{ + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null in %s."), *GetName()); + return; + } + + // Getting all actors with interface 'Savable' + TArray FoundSavableActors; + GetAllActorsWithSavableIntetrface(FoundSavableActors); + + // Iterating on them & saving their data + for (auto* Actor : FoundSavableActors) + { + IG2ISavableInterface::Execute_SaveData(Actor, GameplaySaveGame); + } + + UE_LOG(LogG2I, Log, TEXT("Gameplay data saved & stored in GameplaySaveGame object.")); +} + +void UG2ISavingGameplayManager::LoadAllData() +{ + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null in %s."), *GetName()); + return; + } + + // Getting all actors with interface 'Savable' + TArray FoundSavableActors; + GetAllActorsWithSavableIntetrface(FoundSavableActors); + + // Iterating on them & loading their data + for (auto* Actor : FoundSavableActors) + { + IG2ISavableInterface::Execute_LoadData(Actor, GameplaySaveGame); + } + + UE_LOG(LogG2I, Log, TEXT("Gameplay data loaded from the GameplaySaveGame object.")); +} + +void UG2ISavingGameplayManager::GetAllActorsWithSavableIntetrface(TArray& FoundActors) +{ + UWorld* World = GetWorld(); + if (!World) + { + UE_LOG(LogG2I, Error, TEXT("World doesn't exist in %s."), *GetName()); + return; + } + + UGameplayStatics::UGameplayStatics::GetAllActorsWithInterface( + this, + UG2ISavableInterface::StaticClass(), + FoundActors); +} + +void UG2ISavingGameplayManager::SaveAllDataAndGameplay(bool bAsync) +{ + SaveAllData(); + SaveGameplay(bAsync); +} + +void UG2ISavingGameplayManager::SaveRequestedData(UObject* Requester) +{ + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); + return; + } + + if (Requester) + { + if (Requester->Implements()) + { + IG2ISavableInterface::Execute_SaveData(Requester, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement Savable interface. It's data will be lost."), *Requester->GetName()); + } +} + +void UG2ISavingGameplayManager::LoadRequestedData(UObject* Requester) +{ + if (!GameplaySaveGame) + { + UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); + return; + } + + if (Requester) + { + if (Requester->Implements()) + { + IG2ISavableInterface::Execute_LoadData(Requester, GameplaySaveGame); + } + else + UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement Savable interface. It's data won't be loaded."), *Requester->GetName()); + } +} diff --git a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp index 32ec9052..e97f1735 100644 --- a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp +++ b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp @@ -1,11 +1,8 @@ #include "Game/G2ISavingTriggerBox.h" -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" #include "G2I.h" AG2ISavingTriggerBox::AG2ISavingTriggerBox() { - OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnOverlapBegin); - #if WITH_EDITOR SetActorHiddenInGame(false); #endif @@ -15,27 +12,36 @@ void AG2ISavingTriggerBox::BeginPlay() { Super::BeginPlay(); - GameInstance = GetGameInstance(); - if (GameInstance && GameInstance->Implements()) + UGameInstance* GameInstance = GetGameInstance(); + if (!GameInstance) + { + UE_LOG(LogG2I, Warning, TEXT("GameInstance is NULL in %s."), *GameInstance->GetName(), *GetName()); + return; + } + + if (!ensure(SavingGameplayManager = GameInstance->GetSubsystem())) { - IG2ISaveGameplayInterface::Execute_LoadRequestedData(GameInstance, this); - if (auto* Delegates = IG2ISaveGameplayInterface::Execute_GetGameplaySaveDelegates(GameInstance)) - Delegates->OnGameplaySavedDelegate.AddUniqueDynamic(this, &ThisClass::OnGameplaySaved); + UE_LOG(LogG2I, Warning, TEXT("Couldn't get SavingGameplayManager subsystem from GameInstance in %s."), *GameInstance->GetName(), *GetName()); + return; } - else - UE_LOG(LogG2I, Warning, TEXT("%s doesn't implement interface UG2ISaveGameplayInterface or is NULL in %s."), *GameInstance->GetName(), *GetName()); + + SavingGameplayManager->LoadRequestedData(this); + SavingGameplayManager->OnGameplaySavedDelegate.AddUniqueDynamic(this, &ThisClass::OnGameplaySaved); } -void AG2ISavingTriggerBox::OnOverlapBegin(AActor* OverlappedActor, AActor* OtherActor) +void AG2ISavingTriggerBox::NotifyActorBeginOverlap(AActor* OtherActor) { + Super::NotifyActorBeginOverlap(OtherActor); + bActivated = true; - if (GameInstance && GameInstance->Implements()) + if (!SavingGameplayManager) { - IG2ISaveGameplayInterface::Execute_SaveAllDataAndGameplay(GameInstance, true); + UE_LOG(LogG2I, Error, TEXT("SavingGameplayManager is null in %s."), *GetName()); + return; } - else - UE_LOG(LogG2I, Warning, TEXT("GameInstance doesn't implement interface UG2ISaveGameplayInterface or is NULL in %s."), *GetName()); + + SavingGameplayManager->SaveAllDataAndGameplay(true); } void AG2ISavingTriggerBox::OnGameplaySaved(bool bSuccess) @@ -46,11 +52,23 @@ void AG2ISavingTriggerBox::OnGameplaySaved(bool bSuccess) void AG2ISavingTriggerBox::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) { + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); + return; + } + SaveGameRef->SaveTriggerBoxesSaveData.Add(GetActorLocation(), bActivated); } void AG2ISavingTriggerBox::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) { + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to load %s's data."), *GetName()); + return; + } + if (auto* Key = SaveGameRef->SaveTriggerBoxesSaveData.Find(GetActorLocation())) if (*Key) Destroy(); diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index 199a045d..5160b274 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -2,12 +2,7 @@ #include "CoreMinimal.h" #include "Engine/GameInstance.h" -#include "Kismet/GameplayStatics.h" -#include "GameFramework/SaveGame.h" -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" -#include "Interfaces/SavingSystem/G2ISaveSettingsInterface.h" -#include "Game/G2IGameplaySaveGame.h" -#include "Game/G2ISettingsSaveGame.h" +#include "Game/G2ISavingGameplayManager.h" #include "G2IGameInstance.generated.h" class UG2IWidgetComponentParameters; @@ -19,7 +14,7 @@ class UG2IWidgetsCatalog; * saves and loads gameplay & settings data */ UCLASS() -class G2I_API UG2IGameInstance : public UGameInstance, public IG2ISaveGameplayInterface +class G2I_API UG2IGameInstance : public UGameInstance { GENERATED_BODY() @@ -28,89 +23,7 @@ class G2I_API UG2IGameInstance : public UGameInstance, public IG2ISaveGameplayIn UPROPERTY(EditAnywhere) TObjectPtr WidgetsCatalog; - UPROPERTY(EditAnywhere) - TObjectPtr StringTablesCatalog; - - UPROPERTY(EditAnywhere) - TObjectPtr WidgetComponentsParameters; - -public: - - UG2IWidgetsCatalog *GetWidgetsCatalog(); - - UG2IStringTablesCatalog *GetStringTablesCatalog(); - - UG2IWidgetComponentParameters *GetWidgetComponentParameters(); - - // Class that holds SaveGameplay-related delegates - UPROPERTY(BlueprintReadOnly, Category = "Gameplay Save Game") - TObjectPtr SaveGameplayDelegates; - -private: - // SaveGame w/ gameplay info - UPROPERTY() - TObjectPtr GameplaySaveGame; - - // SaveGame w/ settings - UPROPERTY() - TObjectPtr SettingsSaveGame; - - // Name of the current slot for the gameplay save game - UPROPERTY() - FString GameplaySaveSlotName = TEXT("GameplaySaveSlot0"); - - // Name of the current slot for the gameplay save game UPROPERTY() - FString SettingsSaveSlotName = TEXT("SettingsSaveSlot0"); - - // C++-only delegate called from AsyncSaveGameToSlot - FAsyncSaveGameToSlotDelegate OnGameplayAsyncSavedDelegate; - - // C++-only delegate called from AsyncLoadGameToSlot - FAsyncLoadGameFromSlotDelegate OnGameplayAsyncLoadedDelegate; - -public: - - // === Implementations of the IG2ISaveGameplayInterface' functions === - - void CreateNewGameplaySaveGameObject_Implementation(); - - void SaveGameplay_Implementation(bool bAsync); - - void LoadGameplay_Implementation(bool bAsync); - - void SetGameplaySaveSlotName_Implementation(const FString& NewSlotName); - - const FString GetGameplaySaveSlotName_Implementation(); - - void SaveAllData_Implementation(); - - void LoadAllData_Implementation(); - - void SaveAllDataAndGameplay_Implementation(bool bAsync); - - void SaveRequestedData_Implementation(UObject* Requester); - - void LoadRequestedData_Implementation(UObject* Requester); - - UG2ISaveGameplayDelegates* GetGameplaySaveDelegates_Implementation(); - - // === Other Functions === - -protected: - virtual void Init() override; - - UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") - void CreateGameplaySaveGame(); - - UFUNCTION(BlueprintCallable, Category = "Gameplay Save Game") - void CreateSaveGameplayDelegates(); - - // Finding all actors that have 'gameplay tag 'TagName''Savable' interface - UFUNCTION(BlueprintCallable) - void GetAllActorsWithSavableIntetrface(TArray& FoundActors); - - void OnGameplayAsuncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess); + TObjectPtr SavingGameplayManager; - void OnGameplayAsuncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData); }; diff --git a/Source/G2I/Public/Game/G2ISavingGameplayManager.h b/Source/G2I/Public/Game/G2ISavingGameplayManager.h new file mode 100644 index 00000000..06c623db --- /dev/null +++ b/Source/G2I/Public/Game/G2ISavingGameplayManager.h @@ -0,0 +1,110 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "GameFramework/SaveGame.h" +#include "Kismet/GameplayStatics.h" +#include "Delegates/Delegate.h" +#include "Game/G2IGameplaySaveGame.h" +#include "G2ISavingGameplayManager.generated.h" + +// Delegates + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplaySavedDelegate, bool, bSuccess); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplaySaveStartedDelegate); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplayLoadedDelegate, bool, bSuccess); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplayLoadStartedDelegate); + +/** + * Responsible for saving, loading & storing gameplay data + */ +UCLASS() +class G2I_API UG2ISavingGameplayManager : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnGameplaySavedDelegate OnGameplaySavedDelegate; + + UPROPERTY(BlueprintAssignable) + FOnGameplaySaveStartedDelegate OnGameplaySaveStartedDelegate; + + UPROPERTY(BlueprintAssignable) + FOnGameplayLoadedDelegate OnGameplayLoadedDelegate; + + UPROPERTY(BlueprintAssignable) + FOnGameplayLoadStartedDelegate OnGameplayLoadStartedDelegate; + +protected: + + UPROPERTY() + FString GameplaySaveSlotName = TEXT("GameplaySaveSlot0"); + + // C++-only delegate called from AsyncSaveGameToSlot + FAsyncSaveGameToSlotDelegate OnGameplayAsyncSavedDelegate; + + // C++-only delegate called from AsyncLoadGameToSlot + FAsyncLoadGameFromSlotDelegate OnGameplayAsyncLoadedDelegate; + +private: + // SaveGame with gameplay info + UPROPERTY() + TObjectPtr GameplaySaveGame; + +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + + UFUNCTION(BlueprintCallable) + void CreateNewGameplaySaveGameObject(); + + UFUNCTION(BlueprintCallable) + void SetGameplaySaveSlotName(const FString& NewSlotName); + + UFUNCTION(BlueprintCallable) + const FString GetGameplaySaveSlotName(); + + // Saves SaveGame to slot + UFUNCTION(BlueprintCallable) + void SaveGameplay(bool bAsync); + + // Loads SaveGame from slot + UFUNCTION(BlueprintCallable) + void LoadGameplay(bool bAsync); + + // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame (not in the slot though!) + UFUNCTION(BlueprintCallable) + void SaveAllData(); + + // Loads data of all ACTORS on the level with Savable tag/interface from the SaveGame + UFUNCTION(BlueprintCallable) + void LoadAllData(); + + // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame & saves SaveGame to slot + UFUNCTION(BlueprintCallable) + void SaveAllDataAndGameplay(bool bAsync); + + // Saves Requester's data in the SaveGame object (not in the slot though!) + // This will be called when Requester needs it (in it's OnDestroyed, etc.) + UFUNCTION(BlueprintCallable) + void SaveRequestedData(UObject* Requester); + + // Loads Requester's data from the SaveGame object (not from the slot though!) + // This will be called when Requester needs it (in it's BeginPlay, etc.) + UFUNCTION(BlueprintCallable) + void LoadRequestedData(UObject* Requester); + +protected: + + UFUNCTION(BlueprintCallable) + void CreateGameplaySaveGame(); + + // Finding all actors that have 'Savable' interface + UFUNCTION(BlueprintCallable) + void GetAllActorsWithSavableIntetrface(TArray& FoundActors); + + void OnGameplayAsyncSaved(const FString& SlotName, const int32 UserIndex, bool bSuccess); + + void OnGameplayAsyncLoaded(const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData); +}; diff --git a/Source/G2I/Public/Game/G2ISavingTriggerBox.h b/Source/G2I/Public/Game/G2ISavingTriggerBox.h index bffead4b..11b2138c 100644 --- a/Source/G2I/Public/Game/G2ISavingTriggerBox.h +++ b/Source/G2I/Public/Game/G2ISavingTriggerBox.h @@ -3,7 +3,7 @@ #include "CoreMinimal.h" #include "Engine/TriggerBox.h" #include "Interfaces/SavingSystem/G2ISavableInterface.h" -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" +#include "Game/G2ISavingGameplayManager.h" #include "G2ISavingTriggerBox.generated.h" /** @@ -16,7 +16,7 @@ class G2I_API AG2ISavingTriggerBox : public ATriggerBox, public IG2ISavableInter protected: UPROPERTY(BlueprintReadOnly) - TObjectPtr GameInstance; + TObjectPtr SavingGameplayManager; UPROPERTY() bool bActivated = false; @@ -25,14 +25,14 @@ class G2I_API AG2ISavingTriggerBox : public ATriggerBox, public IG2ISavableInter AG2ISavingTriggerBox(); UFUNCTION() - void OnOverlapBegin(AActor* OverlappedActor, AActor* OtherActor); + virtual void NotifyActorBeginOverlap(AActor* OtherActor) override; UFUNCTION() void OnGameplaySaved(bool bSuccess); - void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + virtual void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) override; - void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); + virtual void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) override; protected: virtual void BeginPlay() override; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h index 6704fa0f..4bb56a24 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h +++ b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h @@ -2,35 +2,8 @@ #include "CoreMinimal.h" #include "UObject/Interface.h" -#include "Delegates/Delegate.h" #include "G2ISaveGameplayInterface.generated.h" -// === Delegates === - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplaySavedDelegate, bool, bSuccess); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplaySaveStartedDelegate); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameplayLoadedDelegate, bool, bSuccess); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameplayLoadStartedDelegate); - -// Wrapper class for save gameplay delegates so that we can return it in the interface function -UCLASS(Blueprintable) -class G2I_API UG2ISaveGameplayDelegates : public UObject -{ - GENERATED_BODY() - -public: - UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") - FOnGameplaySavedDelegate OnGameplaySavedDelegate; - - UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") - FOnGameplaySaveStartedDelegate OnGameplaySaveStartedDelegate; - - UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") - FOnGameplayLoadedDelegate OnGameplayLoadedDelegate; - - UPROPERTY(BlueprintAssignable, Category = "Save Gameplay Delegates") - FOnGameplayLoadStartedDelegate OnGameplayLoadStartedDelegate; -}; @@ -52,50 +25,4 @@ class G2I_API IG2ISaveGameplayInterface { GENERATED_BODY() -public: - // Creates new save game object, sets everything as default - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void CreateNewGameplaySaveGameObject(); - - // Sets slot name - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SetGameplaySaveSlotName(const FString& NewSlotName); - - // Returns slot name - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - const FString GetGameplaySaveSlotName(); - - // Saves SaveGame to slot - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SaveGameplay(bool bAsync); - - // Loads SaveGame from slot - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void LoadGameplay(bool bAsync); - - // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame (not in the slot though!) - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SaveAllData(); - - // Loads data of all ACTORS on the level with Savable tag/interface from the SaveGame - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void LoadAllData(); - - // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame & saves SaveGame to slot - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SaveAllDataAndGameplay(bool bAsync); - - // Saves Requester's data in the SaveGame object (not in the slot though!) - // This will be called when Requester needs it (in it's OnDestroyed, etc.) - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void SaveRequestedData(UObject* Requester); - - // Loads Requester's data from the SaveGame object (not from the slot though!) - // This will be called when Requester needs it (in it's BeginPlay, etc.) - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - void LoadRequestedData(UObject* Requester); - - // Returns class with all delegates - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Save Gameplay") - UG2ISaveGameplayDelegates* GetGameplaySaveDelegates(); }; From c5240a02edc28023da36d039f642689f4843ff47 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:29:44 +0300 Subject: [PATCH 13/15] refact: saving system * moved all saving system to the dedicated folder * renamed SavingTriggerBox to Checkpoint * changed checkpoints save data to struct (for checking the level name with actor location and not just actor location) * added function in PlayerState for switching to character by it's class * added check on character under player's control in checkbox * added checks on SaveGameRef for nullptr --- Source/G2I/G2I.Build.cs | 4 +- .../Characters/G2ICharacterDaughter.cpp | 21 +++- .../Characters/G2ICharacterEngineer.cpp | 21 +++- Source/G2I/Private/Game/G2IPlayerState.cpp | 67 ++++++++++- .../G2I/Private/Game/G2ISavingTriggerBox.cpp | 75 ------------- .../G2I/Private/SaveSystem/G2ICheckpoint.cpp | 106 ++++++++++++++++++ .../G2ISavingGameplayManager.cpp | 4 +- .../Public/Characters/G2ICharacterDaughter.h | 7 +- .../Public/Characters/G2ICharacterEngineer.h | 7 +- .../G2I/Public/Controls/G2IPlayerController.h | 1 - Source/G2I/Public/Game/G2IGameInstance.h | 2 +- Source/G2I/Public/Game/G2IGameplaySaveGame.h | 34 ------ Source/G2I/Public/Game/G2IPlayerState.h | 3 + .../SavingSystem/G2ISaveGameplayInterface.h | 28 ----- .../SavingSystem/G2ISaveSettingsInterface.h | 23 ---- .../G2ICheckpoint.h} | 10 +- .../Public/SaveSystem/G2IGameplaySaveGame.h | 82 ++++++++++++++ .../G2ISavableInterface.h | 2 +- .../G2ISavingGameplayManager.h | 13 +-- .../G2ISettingsSaveGame.h | 0 20 files changed, 317 insertions(+), 193 deletions(-) delete mode 100644 Source/G2I/Private/Game/G2ISavingTriggerBox.cpp create mode 100644 Source/G2I/Private/SaveSystem/G2ICheckpoint.cpp rename Source/G2I/Private/{Game => SaveSystem}/G2ISavingGameplayManager.cpp (98%) delete mode 100644 Source/G2I/Public/Game/G2IGameplaySaveGame.h delete mode 100644 Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h delete mode 100644 Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h rename Source/G2I/Public/{Game/G2ISavingTriggerBox.h => SaveSystem/G2ICheckpoint.h} (73%) create mode 100644 Source/G2I/Public/SaveSystem/G2IGameplaySaveGame.h rename Source/G2I/Public/{Interfaces/SavingSystem => SaveSystem}/G2ISavableInterface.h (95%) rename Source/G2I/Public/{Game => SaveSystem}/G2ISavingGameplayManager.h (87%) rename Source/G2I/Public/{Game => SaveSystem}/G2ISettingsSaveGame.h (100%) diff --git a/Source/G2I/G2I.Build.cs b/Source/G2I/G2I.Build.cs index 0d1cfcf6..ecf45610 100644 --- a/Source/G2I/G2I.Build.cs +++ b/Source/G2I/G2I.Build.cs @@ -20,7 +20,7 @@ public G2I(ReadOnlyTargetRules Target) : base(Target) "UMG", "Slate", "AIModule", - "GameplayTags", + "GameplayTags", "CinematicCamera", "NavigationSystem", "SlateCore" @@ -44,6 +44,7 @@ public G2I(ReadOnlyTargetRules Target) : base(Target) "G2I/Public/Interfaces", "G2I/Public/Interfaces/Camera", "G2I/Public/Interfaces/SteamGlove", + "G2I/Public/SaveSystem", "G2I/Public/UI", "G2I/Public/UI/Widgets", "G2I/Public/UI/WidgetComponents", @@ -64,6 +65,7 @@ public G2I(ReadOnlyTargetRules Target) : base(Target) "G2I/Private/Interfaces", "G2I/Private/Interfaces/Camera", "G2I/Private/Interfaces/SteamGlove", + "G2I/Private/SaveSystem", "G2I/Private/UI", "G2I/Private/UI/Widgets", "G2I/Private/UI/WidgetComponents", diff --git a/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp b/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp index 6d4a7e0a..6d2ac7e8 100644 --- a/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp +++ b/Source/G2I/Private/Characters/G2ICharacterDaughter.cpp @@ -74,6 +74,12 @@ FUnPossessedDelegate& AG2ICharacterDaughter::GetUnPossessedDelegate() void AG2ICharacterDaughter::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) { + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); + return; + } + if (IsPlayerControlled()) { SaveGameRef->PlayersSaveData.CurrentCharacter = GetClass(); @@ -84,14 +90,23 @@ void AG2ICharacterDaughter::SaveData_Implementation(UG2IGameplaySaveGame* SaveGa void AG2ICharacterDaughter::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) { - if (IsPlayerControlled() && !this->IsA(SaveGameRef->PlayersSaveData.CurrentCharacter)) + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); + return; + } + + const TSubclassOf CurrentCharacterClass = SaveGameRef->PlayersSaveData.CurrentCharacter; + if (IsPlayerControlled() && !IsA(CurrentCharacterClass)) { if (auto* G2IPlayerState = Cast(GetPlayerState())) { - G2IPlayerState->SelectNextCharacter(); + G2IPlayerState->SetCharacterByClass(CurrentCharacterClass); } } - if (auto* KeyTransform = SaveGameRef->PlayersSaveData.CharactersTransform.Find(GetClass())) + if (const FTransform* KeyTransform = SaveGameRef->PlayersSaveData.CharactersTransform.Find(GetClass())) + { SetActorTransform(*KeyTransform); + } } diff --git a/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp b/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp index 71261ea5..edcbce36 100644 --- a/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp +++ b/Source/G2I/Private/Characters/G2ICharacterEngineer.cpp @@ -64,6 +64,12 @@ FUnPossessedDelegate& AG2ICharacterEngineer::GetUnPossessedDelegate() void AG2ICharacterEngineer::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) { + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); + return; + } + if (IsPlayerControlled()) { SaveGameRef->PlayersSaveData.CurrentCharacter = GetClass(); @@ -74,14 +80,23 @@ void AG2ICharacterEngineer::SaveData_Implementation(UG2IGameplaySaveGame* SaveGa void AG2ICharacterEngineer::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) { - if (IsPlayerControlled() && !this->IsA(SaveGameRef->PlayersSaveData.CurrentCharacter)) + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); + return; + } + + const TSubclassOf CurrentCharacterClass = SaveGameRef->PlayersSaveData.CurrentCharacter; + if (IsPlayerControlled() && !IsA(CurrentCharacterClass)) { if (auto* G2IPlayerState = Cast(GetPlayerState())) { - G2IPlayerState->SelectNextCharacter(); + G2IPlayerState->SetCharacterByClass(CurrentCharacterClass); } } - if (auto* KeyTransform = SaveGameRef->PlayersSaveData.CharactersTransform.Find(GetClass())) + if (const FTransform* KeyTransform = SaveGameRef->PlayersSaveData.CharactersTransform.Find(GetClass())) + { SetActorTransform(*KeyTransform); + } } diff --git a/Source/G2I/Private/Game/G2IPlayerState.cpp b/Source/G2I/Private/Game/G2IPlayerState.cpp index d9552590..622cb733 100644 --- a/Source/G2I/Private/Game/G2IPlayerState.cpp +++ b/Source/G2I/Private/Game/G2IPlayerState.cpp @@ -6,6 +6,7 @@ #include "Engine/World.h" #include "GameFramework/Pawn.h" #include "Kismet/GameplayStatics.h" +#include "GameFramework/Character.h" void AG2IPlayerState::BeginPlay() { @@ -225,7 +226,7 @@ void AG2IPlayerState::SelectNextCharacter() PlayableCharactersRowNames.Num(); if (NewCharacterIndex == NumberCurrentCharacter) { - UE_LOG(LogG2I, Log, TEXT("Character doesn't switched")); + UE_LOG(LogG2I, Log, TEXT("Couldn't switch to the next character.")); return; } @@ -257,6 +258,70 @@ void AG2IPlayerState::SelectNextCharacter() } } +void AG2IPlayerState::SetCharacterByClass(const TSubclassOf TargetClass) +{ + if (!ensure(TargetClass)) + { + UE_LOG(LogG2I, Warning, TEXT("An attempt to set character with null class.")); + return; + } + + if (!ensure(!PlayableCharactersRowNames.IsEmpty())) + { + UE_LOG(LogG2I, Warning, TEXT("An attempt to set character when array of playable characters is empty.")); + return; + } + + for (int32 OffsetRowName = 1; OffsetRowName <= PlayableCharactersRowNames.Num(); ++OffsetRowName) + { + const int32 NewCharacterIndex = (NumberCurrentCharacter + OffsetRowName) % PlayableCharactersRowNames.Num(); + + if (NewCharacterIndex == NumberCurrentCharacter) + { + UE_LOG(LogG2I, Log, TEXT("Couldn't switch to %s character."), *TargetClass->GetName()); + return; + } + + const FG2IItemCharacter* Row = PlayableCharactersDataTable->FindRow( + PlayableCharactersRowNames[NewCharacterIndex], TEXT("Playable Character Context")); + if (!Row) + { + UE_LOG(LogG2I, Error, TEXT("Array of row names isn't synced with %s in %s."), + *PlayableCharactersDataTable.GetName(), *GetName()); + continue; + } + + if (Row->CharacterClass == TargetClass) + { + const int32 OldCharacterNumber = NumberCurrentCharacter; + NumberCurrentCharacter = NewCharacterIndex; + + if (!SetupControllerForPawn(OldCharacterNumber)) + { + UE_LOG(LogG2I, Warning, TEXT("Character %i couldn't switch to AI controller."), + OldCharacterNumber); + NumberCurrentCharacter = OldCharacterNumber; + continue; + } + + if (SetupControllerForPawn(NumberCurrentCharacter)) + { + OnNewControllerPossessDelegate.Broadcast(GetPawn(OldCharacterNumber)); + OnNewControllerPossessDelegate.Broadcast(GetPawn(NumberCurrentCharacter)); + break; + } + else + { + UE_LOG(LogG2I, Warning, TEXT("Character %i couldn't switch to player controller."), + NumberCurrentCharacter); + NumberCurrentCharacter = OldCharacterNumber; + SetupControllerForPawn(NumberCurrentCharacter); + continue; + } + } + } +} + APawn *AG2IPlayerState::GetPawn(const uint32 PawnNumber) { if (const FG2IItemCharacter *Row = PlayableCharactersDataTable->FindRow( diff --git a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp b/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp deleted file mode 100644 index e97f1735..00000000 --- a/Source/G2I/Private/Game/G2ISavingTriggerBox.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "Game/G2ISavingTriggerBox.h" -#include "G2I.h" - -AG2ISavingTriggerBox::AG2ISavingTriggerBox() -{ -#if WITH_EDITOR - SetActorHiddenInGame(false); -#endif -} - -void AG2ISavingTriggerBox::BeginPlay() -{ - Super::BeginPlay(); - - UGameInstance* GameInstance = GetGameInstance(); - if (!GameInstance) - { - UE_LOG(LogG2I, Warning, TEXT("GameInstance is NULL in %s."), *GameInstance->GetName(), *GetName()); - return; - } - - if (!ensure(SavingGameplayManager = GameInstance->GetSubsystem())) - { - UE_LOG(LogG2I, Warning, TEXT("Couldn't get SavingGameplayManager subsystem from GameInstance in %s."), *GameInstance->GetName(), *GetName()); - return; - } - - SavingGameplayManager->LoadRequestedData(this); - SavingGameplayManager->OnGameplaySavedDelegate.AddUniqueDynamic(this, &ThisClass::OnGameplaySaved); -} - -void AG2ISavingTriggerBox::NotifyActorBeginOverlap(AActor* OtherActor) -{ - Super::NotifyActorBeginOverlap(OtherActor); - - bActivated = true; - - if (!SavingGameplayManager) - { - UE_LOG(LogG2I, Error, TEXT("SavingGameplayManager is null in %s."), *GetName()); - return; - } - - SavingGameplayManager->SaveAllDataAndGameplay(true); -} - -void AG2ISavingTriggerBox::OnGameplaySaved(bool bSuccess) -{ - if (bActivated) - Destroy(); -} - -void AG2ISavingTriggerBox::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) -{ - if (!ensure(SaveGameRef)) - { - UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); - return; - } - - SaveGameRef->SaveTriggerBoxesSaveData.Add(GetActorLocation(), bActivated); -} - -void AG2ISavingTriggerBox::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) -{ - if (!ensure(SaveGameRef)) - { - UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to load %s's data."), *GetName()); - return; - } - - if (auto* Key = SaveGameRef->SaveTriggerBoxesSaveData.Find(GetActorLocation())) - if (*Key) - Destroy(); -} diff --git a/Source/G2I/Private/SaveSystem/G2ICheckpoint.cpp b/Source/G2I/Private/SaveSystem/G2ICheckpoint.cpp new file mode 100644 index 00000000..54799711 --- /dev/null +++ b/Source/G2I/Private/SaveSystem/G2ICheckpoint.cpp @@ -0,0 +1,106 @@ +#include "G2ICheckpoint.h" +#include "G2I.h" +#include "Kismet/GameplayStatics.h" + +AG2ICheckpoint::AG2ICheckpoint() +{ +#if WITH_EDITOR + SetActorHiddenInGame(false); +#endif +} + +void AG2ICheckpoint::BeginPlay() +{ + Super::BeginPlay(); + + UGameInstance* GameInstance = GetGameInstance(); + if (!GameInstance) + { + UE_LOG(LogG2I, Error, TEXT("GameInstance is NULL in %s."), *GameInstance->GetName(), *GetName()); + return; + } + + if (!ensure(SavingGameplayManager = GameInstance->GetSubsystem())) + { + UE_LOG(LogG2I, Error, TEXT("Couldn't get SavingGameplayManager subsystem from GameInstance in %s."), *GameInstance->GetName(), *GetName()); + return; + } + + SavingGameplayManager->LoadRequestedData(this); + SavingGameplayManager->OnGameplaySavedDelegate.AddUniqueDynamic(this, &ThisClass::OnGameplaySaved); +} + +void AG2ICheckpoint::NotifyActorBeginOverlap(AActor* OtherActor) +{ + Super::NotifyActorBeginOverlap(OtherActor); + + const ACharacter* OtherCharacter = Cast(OtherActor); + if (!OtherCharacter) + { + return; + } + if (!OtherCharacter->IsPlayerControlled()) + { + UE_LOG(LogG2I, Log, TEXT("AI controlled character overlapped %s. Saving isn't triggered."), *GetName()); + return; + } + + bActivated = true; + + if (!SavingGameplayManager) + { + UE_LOG(LogG2I, Error, TEXT("SavingGameplayManager is null in %s."), *GetName()); + return; + } + + SavingGameplayManager->SaveAllDataAndGameplay(true); +} + +void AG2ICheckpoint::OnGameplaySaved(bool bSuccess) +{ + if (bActivated) + { + Destroy(); + } +} + +void AG2ICheckpoint::SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) +{ + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to save %s's data."), *GetName()); + return; + } + + const UWorld* World = GetWorld(); + if (!World) + { + UE_LOG(LogG2I, Error, TEXT("World is null in %s."), *GetName()); + return; + } + + const FCheckpointSaveData CheckpointSavedData = FCheckpointSaveData(UGameplayStatics::GetCurrentLevelName(World), GetActorLocation()); + SaveGameRef->CheckpointsSaveData.Add(CheckpointSavedData, bActivated); +} + +void AG2ICheckpoint::LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) +{ + if (!ensure(SaveGameRef)) + { + UE_LOG(LogG2I, Warning, TEXT("Got null SaveGameRef while trying to load %s's data."), *GetName()); + return; + } + + const UWorld* World = GetWorld(); + if (!World) + { + UE_LOG(LogG2I, Error, TEXT("World is null in %s."), *GetName()); + return; + } + + const FCheckpointSaveData CheckpointData = FCheckpointSaveData(UGameplayStatics::GetCurrentLevelName(World), GetActorLocation()); + if (const bool* Key = SaveGameRef->CheckpointsSaveData.Find(CheckpointData)) + { + if (*Key) { Destroy(); } + } +} diff --git a/Source/G2I/Private/Game/G2ISavingGameplayManager.cpp b/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp similarity index 98% rename from Source/G2I/Private/Game/G2ISavingGameplayManager.cpp rename to Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp index a912c775..545d9cf5 100644 --- a/Source/G2I/Private/Game/G2ISavingGameplayManager.cpp +++ b/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp @@ -1,6 +1,6 @@ -#include "Game/G2ISavingGameplayManager.h" +#include "G2ISavingGameplayManager.h" +#include "G2ISavableInterface.h" #include "G2I.h" -#include "Interfaces/SavingSystem/G2ISavableInterface.h" #include "Kismet/GameplayStatics.h" diff --git a/Source/G2I/Public/Characters/G2ICharacterDaughter.h b/Source/G2I/Public/Characters/G2ICharacterDaughter.h index 6d0c311b..f872fd9e 100644 --- a/Source/G2I/Public/Characters/G2ICharacterDaughter.h +++ b/Source/G2I/Public/Characters/G2ICharacterDaughter.h @@ -3,8 +3,7 @@ #include "CoreMinimal.h" #include "G2ICharacterInterface.h" #include "GameFramework/Character.h" -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" -#include "Interfaces/SavingSystem/G2ISavableInterface.h" +#include "G2ISavableInterface.h" #include "G2ICharacterDaughter.generated.h" class UG2IFlightComponent; @@ -63,9 +62,9 @@ class AG2ICharacterDaughter : public ACharacter, public IG2ICharacterInterface, explicit AG2ICharacterDaughter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); - void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + virtual void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) override; - void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); + virtual void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) override; virtual void PossessedBy(AController* NewController) override; diff --git a/Source/G2I/Public/Characters/G2ICharacterEngineer.h b/Source/G2I/Public/Characters/G2ICharacterEngineer.h index ed3dd72c..0a5c2eb2 100644 --- a/Source/G2I/Public/Characters/G2ICharacterEngineer.h +++ b/Source/G2I/Public/Characters/G2ICharacterEngineer.h @@ -3,8 +3,7 @@ #include "CoreMinimal.h" #include "G2ICharacterInterface.h" #include "GameFramework/Character.h" -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" -#include "Interfaces/SavingSystem/G2ISavableInterface.h" +#include "G2ISavableInterface.h" #include "Components/G2IValveInteractionComponent.h" #include "Components/G2IHoleInteractionComponent.h" #include "G2ICharacterEngineer.generated.h" @@ -69,9 +68,9 @@ class G2I_API AG2ICharacterEngineer : public ACharacter, public IG2ICharacterInt explicit AG2ICharacterEngineer(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); - void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef); + virtual void SaveData_Implementation(UG2IGameplaySaveGame* SaveGameRef) override; - void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef); + virtual void LoadData_Implementation(const UG2IGameplaySaveGame* SaveGameRef) override; virtual void PossessedBy(AController* NewController) override; diff --git a/Source/G2I/Public/Controls/G2IPlayerController.h b/Source/G2I/Public/Controls/G2IPlayerController.h index e25db393..e574b895 100644 --- a/Source/G2I/Public/Controls/G2IPlayerController.h +++ b/Source/G2I/Public/Controls/G2IPlayerController.h @@ -3,7 +3,6 @@ #include "CoreMinimal.h" #include "EnhancedActionKeyMapping.h" #include "GameFramework/PlayerController.h" -#include "Interfaces/SavingSystem/G2ISaveGameplayInterface.h" #include "G2IPlayerController.generated.h" class UG2IUIManager; diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index 5160b274..cf90a432 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "Engine/GameInstance.h" -#include "Game/G2ISavingGameplayManager.h" +#include "G2ISavingGameplayManager.h" #include "G2IGameInstance.generated.h" class UG2IWidgetComponentParameters; diff --git a/Source/G2I/Public/Game/G2IGameplaySaveGame.h b/Source/G2I/Public/Game/G2IGameplaySaveGame.h deleted file mode 100644 index 6cc24579..00000000 --- a/Source/G2I/Public/Game/G2IGameplaySaveGame.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/SaveGame.h" -#include "GameFramework/Character.h" -#include "G2IGameplaySaveGame.generated.h" - -USTRUCT(BlueprintType) -struct FPlayersSaveData -{ - GENERATED_BODY() - - UPROPERTY(SaveGame, BlueprintReadWrite) - TMap, FTransform> CharactersTransform; - - UPROPERTY(SaveGame, BlueprintReadWrite) - TSubclassOf CurrentCharacter; -}; - -/** - * SaveGame for gameplay data. - */ -UCLASS() -class G2I_API UG2IGameplaySaveGame : public USaveGame -{ - GENERATED_BODY() - -public: - UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Player") - FPlayersSaveData PlayersSaveData; - - UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Saving Trigger Boxes") - TMap SaveTriggerBoxesSaveData; -}; diff --git a/Source/G2I/Public/Game/G2IPlayerState.h b/Source/G2I/Public/Game/G2IPlayerState.h index 53b3f3fa..bf91cfbf 100644 --- a/Source/G2I/Public/Game/G2IPlayerState.h +++ b/Source/G2I/Public/Game/G2IPlayerState.h @@ -37,6 +37,8 @@ class G2I_API AG2IPlayerState : public APlayerState void SelectNextCharacter(); + void SetCharacterByClass(const TSubclassOf TargetClass); + protected: APawn *GetPawn(const uint32 PawnNumber); @@ -59,4 +61,5 @@ class G2I_API AG2IPlayerState : public APlayerState AActor *SpawnActor(const TSubclassOf ActorClass) const; + void SwitchToCharacter(int32 NewCharacterIndex); }; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h deleted file mode 100644 index 4bb56a24..00000000 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveGameplayInterface.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "UObject/Interface.h" -#include "G2ISaveGameplayInterface.generated.h" - - - - -// === Interface === - - -// This class does not need to be modified. -UINTERFACE(MinimalAPI) -class UG2ISaveGameplayInterface : public UInterface -{ - GENERATED_BODY() -}; - -/* - Allows to save, load, sync data with SaveGame object that corresponds to storing gameplay data - (such as player's location, inventory, etc.) and to return delegates of that actions. - */ -class G2I_API IG2ISaveGameplayInterface -{ - GENERATED_BODY() - -}; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h b/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h deleted file mode 100644 index 469bf27b..00000000 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISaveSettingsInterface.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "UObject/Interface.h" -#include "G2ISaveSettingsInterface.generated.h" - -// This class does not need to be modified. -UINTERFACE(MinimalAPI) -class UG2ISaveSettingsInterface : public UInterface -{ - GENERATED_BODY() -}; - -/* - Allows to save, load, sync data with SaveGame object that corresponds to storing settings data - (such as music volume, key bindings, etc.) and to return delegates of that actions. - */ -class G2I_API IG2ISaveSettingsInterface -{ - GENERATED_BODY() - -public: -}; diff --git a/Source/G2I/Public/Game/G2ISavingTriggerBox.h b/Source/G2I/Public/SaveSystem/G2ICheckpoint.h similarity index 73% rename from Source/G2I/Public/Game/G2ISavingTriggerBox.h rename to Source/G2I/Public/SaveSystem/G2ICheckpoint.h index 11b2138c..886cbf86 100644 --- a/Source/G2I/Public/Game/G2ISavingTriggerBox.h +++ b/Source/G2I/Public/SaveSystem/G2ICheckpoint.h @@ -2,15 +2,15 @@ #include "CoreMinimal.h" #include "Engine/TriggerBox.h" -#include "Interfaces/SavingSystem/G2ISavableInterface.h" -#include "Game/G2ISavingGameplayManager.h" -#include "G2ISavingTriggerBox.generated.h" +#include "G2ISavableInterface.h" +#include "G2ISavingGameplayManager.h" +#include "G2ICheckpoint.generated.h" /** * Trigger box that, when triggered by the player, saves gameplay & deletes itself */ UCLASS() -class G2I_API AG2ISavingTriggerBox : public ATriggerBox, public IG2ISavableInterface +class G2I_API AG2ICheckpoint : public ATriggerBox, public IG2ISavableInterface { GENERATED_BODY() @@ -22,7 +22,7 @@ class G2I_API AG2ISavingTriggerBox : public ATriggerBox, public IG2ISavableInter bool bActivated = false; public: - AG2ISavingTriggerBox(); + AG2ICheckpoint(); UFUNCTION() virtual void NotifyActorBeginOverlap(AActor* OtherActor) override; diff --git a/Source/G2I/Public/SaveSystem/G2IGameplaySaveGame.h b/Source/G2I/Public/SaveSystem/G2IGameplaySaveGame.h new file mode 100644 index 00000000..880a0236 --- /dev/null +++ b/Source/G2I/Public/SaveSystem/G2IGameplaySaveGame.h @@ -0,0 +1,82 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/SaveGame.h" +#include "GameFramework/Character.h" +#include "Misc/Crc.h" +#include "G2IGameplaySaveGame.generated.h" + +USTRUCT(BlueprintType) +struct FPlayerSaveData +{ + GENERATED_BODY() + + UPROPERTY(SaveGame, BlueprintReadWrite) + TMap, FTransform> CharactersTransform; + + UPROPERTY(SaveGame, BlueprintReadWrite) + TSubclassOf CurrentCharacter; +}; + +USTRUCT(BlueprintType) +struct FCheckpointSaveData +{ + GENERATED_BODY() + + UPROPERTY(SaveGame, BlueprintReadWrite) + FString LevelName; + + UPROPERTY(SaveGame, BlueprintReadWrite) + FVector Location; + + // Extending the struct for using it in TMap as a key + + FCheckpointSaveData(FString NewLevelName, FVector NewLocation) + : LevelName(NewLevelName), Location(NewLocation) { + } + + FCheckpointSaveData() + : LevelName(TEXT("")), Location(FVector::ZeroVector) { + } + + FCheckpointSaveData(const FCheckpointSaveData& Other) + : LevelName(Other.LevelName), Location(Other.Location) { + } + + bool operator==(const FCheckpointSaveData& Other) const + { + return Equals(Other); + } + + bool operator!=(const FCheckpointSaveData& Other) const + { + return !Equals(Other); + } + + bool Equals(const FCheckpointSaveData& Other) const + { + return LevelName.Equals(Other.LevelName) && Location.Equals(Other.Location); + } + + friend uint32 GetTypeHash(const FCheckpointSaveData& Thing) + { + uint32 Hash = FCrc::MemCrc32(&Thing, sizeof(FCheckpointSaveData)); + return Hash; + } +}; + +/** + * SaveGame for gameplay data. + */ +UCLASS() +class G2I_API UG2IGameplaySaveGame : public USaveGame +{ + GENERATED_BODY() + +public: + UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Player") + FPlayerSaveData PlayersSaveData; + + UPROPERTY(SaveGame, BlueprintReadWrite, Category = "Save Gameplay Data|Checkpoints") + TMap CheckpointsSaveData; +}; diff --git a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h b/Source/G2I/Public/SaveSystem/G2ISavableInterface.h similarity index 95% rename from Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h rename to Source/G2I/Public/SaveSystem/G2ISavableInterface.h index c6bc4a6d..dd5bbd5d 100644 --- a/Source/G2I/Public/Interfaces/SavingSystem/G2ISavableInterface.h +++ b/Source/G2I/Public/SaveSystem/G2ISavableInterface.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "UObject/Interface.h" -#include "Game/G2IGameplaySaveGame.h" +#include "G2IGameplaySaveGame.h" #include "G2ISavableInterface.generated.h" // This class does not need to be modified. diff --git a/Source/G2I/Public/Game/G2ISavingGameplayManager.h b/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h similarity index 87% rename from Source/G2I/Public/Game/G2ISavingGameplayManager.h rename to Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h index 06c623db..3fe7a588 100644 --- a/Source/G2I/Public/Game/G2ISavingGameplayManager.h +++ b/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h @@ -5,7 +5,7 @@ #include "GameFramework/SaveGame.h" #include "Kismet/GameplayStatics.h" #include "Delegates/Delegate.h" -#include "Game/G2IGameplaySaveGame.h" +#include "G2IGameplaySaveGame.h" #include "G2ISavingGameplayManager.generated.h" // Delegates @@ -73,24 +73,24 @@ class G2I_API UG2ISavingGameplayManager : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable) void LoadGameplay(bool bAsync); - // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame (not in the slot though!) + // Saves data of all ACTORS on the level with Savable interface in the SaveGame (not in the slot!) UFUNCTION(BlueprintCallable) void SaveAllData(); - // Loads data of all ACTORS on the level with Savable tag/interface from the SaveGame + // Loads data of all ACTORS on the level with Savable interface from the SaveGame UFUNCTION(BlueprintCallable) void LoadAllData(); - // Saves data of all ACTORS on the level with Savable tag/interface in the SaveGame & saves SaveGame to slot + // Saves data of all ACTORS on the level with Savable interface in the SaveGame & saves SaveGame to slot UFUNCTION(BlueprintCallable) void SaveAllDataAndGameplay(bool bAsync); - // Saves Requester's data in the SaveGame object (not in the slot though!) + // Saves Requester's data in the SaveGame object (not in the slot!) // This will be called when Requester needs it (in it's OnDestroyed, etc.) UFUNCTION(BlueprintCallable) void SaveRequestedData(UObject* Requester); - // Loads Requester's data from the SaveGame object (not from the slot though!) + // Loads Requester's data from the SaveGame object (not from the slot!) // This will be called when Requester needs it (in it's BeginPlay, etc.) UFUNCTION(BlueprintCallable) void LoadRequestedData(UObject* Requester); @@ -100,7 +100,6 @@ class G2I_API UG2ISavingGameplayManager : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable) void CreateGameplaySaveGame(); - // Finding all actors that have 'Savable' interface UFUNCTION(BlueprintCallable) void GetAllActorsWithSavableIntetrface(TArray& FoundActors); diff --git a/Source/G2I/Public/Game/G2ISettingsSaveGame.h b/Source/G2I/Public/SaveSystem/G2ISettingsSaveGame.h similarity index 100% rename from Source/G2I/Public/Game/G2ISettingsSaveGame.h rename to Source/G2I/Public/SaveSystem/G2ISettingsSaveGame.h From e92e533b8ce781e4788c0a86578eec1019d6e0d8 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:42:53 +0300 Subject: [PATCH 14/15] refact: saving manager --- .../SaveSystem/G2ISavingGameplayManager.cpp | 17 ++++++++++------- .../SaveSystem/G2ISavingGameplayManager.h | 2 -- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp b/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp index 545d9cf5..8ea243fd 100644 --- a/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp +++ b/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp @@ -66,16 +66,16 @@ void UG2ISavingGameplayManager::SaveGameplay(bool bAsync) if (!GameplaySaveGame) CreateGameplaySaveGame(); + OnGameplaySaveStartedDelegate.Broadcast(); + if (bAsync) { // Asynchronous - OnGameplaySaveStartedDelegate.Broadcast(); UGameplayStatics::AsyncSaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0, OnGameplayAsyncSavedDelegate); } else { // Synchronous - OnGameplaySaveStartedDelegate.Broadcast(); if (UGameplayStatics::SaveGameToSlot(GameplaySaveGame, GameplaySaveSlotName, 0)) { // Successfully saved @@ -103,10 +103,10 @@ const FString UG2ISavingGameplayManager::GetGameplaySaveSlotName() void UG2ISavingGameplayManager::LoadGameplay(bool bAsync) { + OnGameplayLoadStartedDelegate.Broadcast(); if (bAsync) { // Asynchronous - OnGameplayLoadStartedDelegate.Broadcast(); UGameplayStatics::AsyncLoadGameFromSlot(GameplaySaveSlotName, 0, OnGameplayAsyncLoadedDelegate); } else @@ -117,17 +117,20 @@ void UG2ISavingGameplayManager::LoadGameplay(bool bAsync) if (GameplaySaveGame = Cast(LoadedGame)) { // Successfully loaded + OnGameplayLoadedDelegate.Broadcast(true); UE_LOG(LogG2I, Log, TEXT("Gameplay loaded successfully from the slot %s."), *GameplaySaveSlotName); } else { // Loaded SaveGame object, but it's invalid + OnGameplayLoadedDelegate.Broadcast(false); UE_LOG(LogG2I, Error, TEXT("Gameplay loaded from the slot %s but is invalid. Operation failed."), *GameplaySaveSlotName); } } else { // Failed to load + OnGameplayLoadedDelegate.Broadcast(false); UE_LOG(LogG2I, Error, TEXT("Gameplay load from the slot %s failed."), *GameplaySaveSlotName); } } @@ -135,7 +138,7 @@ void UG2ISavingGameplayManager::LoadGameplay(bool bAsync) void UG2ISavingGameplayManager::SaveAllData() { - if (!GameplaySaveGame) + if (!ensure(GameplaySaveGame)) { UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null in %s."), *GetName()); return; @@ -156,7 +159,7 @@ void UG2ISavingGameplayManager::SaveAllData() void UG2ISavingGameplayManager::LoadAllData() { - if (!GameplaySaveGame) + if (!ensure(GameplaySaveGame)) { UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null in %s."), *GetName()); return; @@ -198,7 +201,7 @@ void UG2ISavingGameplayManager::SaveAllDataAndGameplay(bool bAsync) void UG2ISavingGameplayManager::SaveRequestedData(UObject* Requester) { - if (!GameplaySaveGame) + if (!ensure(GameplaySaveGame)) { UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); return; @@ -217,7 +220,7 @@ void UG2ISavingGameplayManager::SaveRequestedData(UObject* Requester) void UG2ISavingGameplayManager::LoadRequestedData(UObject* Requester) { - if (!GameplaySaveGame) + if (!ensure(GameplaySaveGame)) { UE_LOG(LogG2I, Error, TEXT("GameplaySaveGame in null. You'll have to load it first or create new.")); return; diff --git a/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h b/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h index 3fe7a588..7b6a36f6 100644 --- a/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h +++ b/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h @@ -2,8 +2,6 @@ #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" -#include "GameFramework/SaveGame.h" -#include "Kismet/GameplayStatics.h" #include "Delegates/Delegate.h" #include "G2IGameplaySaveGame.h" #include "G2ISavingGameplayManager.generated.h" From 242869e3101e40002b6cc5098e6bb55deb1dfac9 Mon Sep 17 00:00:00 2001 From: Karaseva Irina <94855816+irazaurus@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:12:18 +0300 Subject: [PATCH 15/15] fix: rebase mismatches --- .../G2I/Private/Controls/G2IPlayerController.cpp | 1 - Source/G2I/Private/Game/G2IGameInstance.cpp | 15 +++++++++++++++ .../SaveSystem/G2ISavingGameplayManager.cpp | 1 - Source/G2I/Public/Game/G2IGameInstance.h | 15 ++++++++++++--- .../Public/SaveSystem/G2ISavingGameplayManager.h | 1 + 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Source/G2I/Private/Controls/G2IPlayerController.cpp b/Source/G2I/Private/Controls/G2IPlayerController.cpp index 5e7b4423..84bb9058 100644 --- a/Source/G2I/Private/Controls/G2IPlayerController.cpp +++ b/Source/G2I/Private/Controls/G2IPlayerController.cpp @@ -93,7 +93,6 @@ void AG2IPlayerController::SetupInputComponent() &ThisClass::SwitchCameraBehavior); // TODO: Add Pause Action after adding all UI systems //EnhancedInputComponent->BindAction(DebugPauseAction, ETriggerEvent::Started,this, &ThisClass::CallPause); -#if WITH_EDITOR EnhancedInputComponent->BindAction(SaveAction, ETriggerEvent::Triggered, this, &ThisClass::SaveGameplay); EnhancedInputComponent->BindAction(LoadAction, ETriggerEvent::Triggered, this, &ThisClass::LoadGameplay); #endif diff --git a/Source/G2I/Private/Game/G2IGameInstance.cpp b/Source/G2I/Private/Game/G2IGameInstance.cpp index b98661ac..1d9865f3 100644 --- a/Source/G2I/Private/Game/G2IGameInstance.cpp +++ b/Source/G2I/Private/Game/G2IGameInstance.cpp @@ -1,2 +1,17 @@ #include "G2IGameInstance.h" #include "G2I.h" + +UG2IWidgetsCatalog* UG2IGameInstance::GetWidgetsCatalog() +{ + return WidgetsCatalog; +} + +UG2IStringTablesCatalog* UG2IGameInstance::GetStringTablesCatalog() +{ + return StringTablesCatalog; +} + +UG2IWidgetComponentParameters* UG2IGameInstance::GetWidgetComponentParameters() +{ + return WidgetComponentsParameters; +} diff --git a/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp b/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp index 8ea243fd..3a841214 100644 --- a/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp +++ b/Source/G2I/Private/SaveSystem/G2ISavingGameplayManager.cpp @@ -1,7 +1,6 @@ #include "G2ISavingGameplayManager.h" #include "G2ISavableInterface.h" #include "G2I.h" -#include "Kismet/GameplayStatics.h" void UG2ISavingGameplayManager::Initialize(FSubsystemCollectionBase& Collection) diff --git a/Source/G2I/Public/Game/G2IGameInstance.h b/Source/G2I/Public/Game/G2IGameInstance.h index cf90a432..2661c391 100644 --- a/Source/G2I/Public/Game/G2IGameInstance.h +++ b/Source/G2I/Public/Game/G2IGameInstance.h @@ -2,7 +2,6 @@ #include "CoreMinimal.h" #include "Engine/GameInstance.h" -#include "G2ISavingGameplayManager.h" #include "G2IGameInstance.generated.h" class UG2IWidgetComponentParameters; @@ -23,7 +22,17 @@ class G2I_API UG2IGameInstance : public UGameInstance UPROPERTY(EditAnywhere) TObjectPtr WidgetsCatalog; - UPROPERTY() - TObjectPtr SavingGameplayManager; + UPROPERTY(EditAnywhere) + TObjectPtr StringTablesCatalog; + + UPROPERTY(EditAnywhere) + TObjectPtr WidgetComponentsParameters; + +public: + + UG2IWidgetsCatalog* GetWidgetsCatalog(); + + UG2IStringTablesCatalog* GetStringTablesCatalog(); + UG2IWidgetComponentParameters* GetWidgetComponentParameters(); }; diff --git a/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h b/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h index 7b6a36f6..9318570b 100644 --- a/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h +++ b/Source/G2I/Public/SaveSystem/G2ISavingGameplayManager.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "Delegates/Delegate.h" +#include "Kismet/GameplayStatics.h" #include "G2IGameplaySaveGame.h" #include "G2ISavingGameplayManager.generated.h"