Add core gameplay systems and data assets for player mechanics

- Implemented DA_EquipmentConfig for managing equipment resistances, durability, and weight.
- Created DA_ItemData to serve as a base item data asset with various item types and properties.
- Introduced BPC_HealthSystem for managing player health and death events.
- Added BPC_MovementStateSystem to handle player movement modes with event delegation.
- Developed BPC_StaminaSystem to track player stamina and exhaustion states.
- Established BPC_StateManager as a central authority for managing player action states and gating.
- Created BPC_StressSystem to monitor and respond to player stress levels.
- Implemented PC_CoreController and PS_CorePlayerState for player controller and state management.
- Developed SS_SaveManager for save/load functionality with slot management and serialization.
- Introduced DA_StateGatingTable for defining action gating rules based on gameplay tags.
- Added BPC_DamageReceptionSystem to process incoming damage and apply resistance calculations.
- Implemented BPC_HitReactionSystem for managing hit reactions based on damage received.
- Created BPC_ShieldDefenseSystem to manage shield health and blocking mechanics.
- Added PG_FrameworkEditor.Target.cs for editor build configuration.
This commit is contained in:
Lefteris Notas
2026-05-21 14:38:30 +03:00
parent a145ae9373
commit f986343325
50 changed files with 86 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — DA_GameTagRegistry (01)
// Central gameplay tag registry Data Asset. Eliminates the 3 C++-only API workarounds
// (RequestAllGameplayTags, RequestGameplayTag, UGameplayTagsManager singleton access).
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "Engine/DataTable.h"
#include "DA_GameTagRegistry.generated.h"
/**
* DA_GameTagRegistry — Central GameplayTag namespace registry.
*
* In C++, this directly wraps UGameplayTagsManager APIs instead of using the
* Data Table proxy workaround required in Blueprint. Maintains the Data Table
* references for the Blueprint implementation guide, but the C++ functions
* bypass them entirely for performance and correctness.
*/
UCLASS(BlueprintType, Blueprintable, meta = (DisplayName = "DA_GameTagRegistry"))
class FRAMEWORK_API UDA_GameTagRegistry : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UDA_GameTagRegistry();
// ========================================================================
// Configuration
// ========================================================================
/** Human-readable description of the tag namespace (e.g. "Player.State"). */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Documentation")
FText TagNamespace;
/** True for framework-defined tags, false for project-specific overrides. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Documentation")
bool bIsFrameworkTag = true;
/**
* Array of 11 per-category Data Tables used by Blueprint implementations.
* C++ functions use UGameplayTagsManager directly and ignore this array.
* Maintained for the Blueprint Manual Implementation Guide.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Config")
TArray<TObjectPtr<UDataTable>> TagDataTables;
// ========================================================================
// Query Functions
// ========================================================================
/**
* Returns ALL registered gameplay tags from the engine's tag manager.
* C++ implementation: single call to UGameplayTagsManager.
* Blueprint equivalent: nested ForEachLoop over 11 Data Tables (workaround).
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Tags|Query")
TArray<FGameplayTag> GetAllRegisteredTags() const;
/**
* Returns the human-readable display name of a gameplay tag.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Tags|Query")
FText GetTagDisplayName(const FGameplayTag& Tag) const;
/**
* Validates whether a gameplay tag is registered in the engine's tag table.
* Returns false + logs warning for unregistered tags.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Tags|Validation")
bool ValidateTag(const FGameplayTag& Tag) const;
/**
* Validates that a tag exists AND returns it. Fails gracefully with warning.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Tags|Validation")
FGameplayTag RequestTag(FName TagName, bool bLogWarning = true) const;
// ========================================================================
// Debug / Tooling
// ========================================================================
/** Prints all registered tags to the output log. Editor-only. */
UFUNCTION(BlueprintCallable, Category = "Framework|Tags|Debug")
void LogAllTags() const;
/**
* Exports all tags matching a namespace prefix as a formatted string.
* Useful for auditing discrepancies between Data Tables and DefaultGameplayTags.ini.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Tags|Tooling")
FString ExportTagNamespace(const FString& NamespacePrefix) const;
// ========================================================================
// Overrides
// ========================================================================
virtual void PostLoad() override;
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};

View File

@@ -0,0 +1,169 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — FL_GameUtilities (02)
// Shared Blueprint Function Library. In C++, all functions are static template/inline
// with proper null-safety — no BP workarounds needed.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "GameplayTagContainer.h"
#include "FL_GameUtilities.generated.h"
class UGI_GameFramework;
class APC_CoreController;
/**
* FL_GameUtilities — Static utility functions available from any Blueprint or C++.
*
* In C++, these are proper static functions with template type-safety.
* The Blueprint version requires macro wrappers for subsystem access —
* here we get clean UFUNCTION(BlueprintCallable) with fast native paths.
*/
UCLASS()
class FRAMEWORK_API UFL_GameUtilities : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
// ========================================================================
// Subsystem Access (Null-Safe)
// ========================================================================
/**
* Safe subsystem retrieval — returns nullptr instead of crashing.
* Use this instead of raw GetSubsystem() everywhere.
*/
template<typename T>
static T* GetSubsystemSafe(const UObject* WorldContextObject)
{
if (!WorldContextObject)
{
UE_LOG(LogTemp, Warning, TEXT("FL_GameUtilities::GetSubsystemSafe — Invalid WorldContextObject"));
return nullptr;
}
const UGameInstance* GameInstance = WorldContextObject->GetWorld()->GetGameInstance();
if (!GameInstance)
{
UE_LOG(LogTemp, Warning, TEXT("FL_GameUtilities::GetSubsystemSafe — No GameInstance found"));
return nullptr;
}
T* Subsystem = GameInstance->GetSubsystem<T>();
if (!Subsystem)
{
UE_LOG(LogTemp, Warning, TEXT("FL_GameUtilities::GetSubsystemSafe — Subsystem '%s' not found"),
*T::StaticClass()->GetName());
}
return Subsystem;
}
// Blueprint-accessible subsystem getters (UFUNCTION wrappers for the template)
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Utilities",
meta = (WorldContext = "WorldContextObject"))
static UGI_GameFramework* GetGameFramework(const UObject* WorldContextObject);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Utilities",
meta = (WorldContext = "WorldContextObject", DeterminesOutputType = "SubsystemClass"))
static UGameInstanceSubsystem* GetSubsystemByClass(const UObject* WorldContextObject,
TSubclassOf<UGameInstanceSubsystem> SubsystemClass);
// ========================================================================
// Actor Utilities
// ========================================================================
/**
* Finds the first component on an actor that implements a given interface.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Utilities")
static UActorComponent* FindComponentByInterface(AActor* Actor,
TSubclassOf<UInterface> InterfaceClass);
/**
* Finds the nearest actor within a radius that has a specific gameplay tag.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Utilities",
meta = (WorldContext = "WorldContextObject"))
static AActor* FindNearestActorWithTag(const UObject* WorldContextObject,
FVector Origin, float Radius, FGameplayTag RequiredTag);
// ========================================================================
// Math Utilities
// ========================================================================
/** Remap a value from [InMin, InMax] to [OutMin, OutMax]. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Math")
static float RemapFloat(float Value, float InMin, float InMax, float OutMin, float OutMax);
/** Linear interpolation clamped to [0, 1]. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Math")
static float LerpClamped(float A, float B, float Alpha);
/** Convert a direction vector to a 2D angle in degrees. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Math")
static float VectorToAngle2D(FVector2D Direction);
/** Shortest signed angle difference between two angles in degrees. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Math")
static float AngleDifference(float A, float B);
// ========================================================================
// GameplayTag Utilities
// ========================================================================
/** Safe gameplay tag check — returns false if actor has no tag container. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Tags")
static bool HasGameplayTag(AActor* Actor, FGameplayTag Tag);
/**
* Creates a gameplay tag from a string with validation.
* Returns EmptyTag and logs warning if the tag isn't registered.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Tags")
static FGameplayTag MakeTagFromString(const FString& TagString, bool bLogWarning = true);
// ========================================================================
// Text Utilities
// ========================================================================
/** Format seconds into HH:MM:SS string. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Text")
static FText FormatTime(float TotalSeconds);
/** Returns singular or plural form based on count. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Text")
static FText Pluralise(const FText& Singular, const FText& Plural, int32 Count);
/** Truncates text to MaxLength with ellipsis. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Text")
static FText TruncateText(const FText& Text, int32 MaxLength);
// ========================================================================
// Screen / Projection Utilities
// ========================================================================
/**
* Projects a world position to screen space.
* bIsOnScreen is false if the point is behind the camera.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Screen",
meta = (WorldContext = "WorldContextObject"))
static bool WorldToScreenSafe(const UObject* WorldContextObject, FVector WorldPosition,
FVector2D& OutScreenPosition, bool& bIsOnScreen);
// ========================================================================
// Debug (Shipping-safe)
// ========================================================================
/** Debug log — stripped from shipping builds. */
UFUNCTION(BlueprintCallable, Category = "Framework|Debug")
static void DebugLog(const FString& Message, bool bPrintToScreen = false,
float ScreenDuration = 5.0f, FColor ScreenColor = FColor::Cyan);
/** Draw debug sphere in world — stripped from shipping builds. */
UFUNCTION(BlueprintCallable, Category = "Framework|Debug")
static void DebugSphere(const UObject* WorldContextObject, FVector Location,
float Radius = 50.0f, FColor Color = FColor::Green, float Duration = 5.0f);
};

View File

@@ -0,0 +1,226 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — GI_GameFramework (04)
// Application kernel GameInstance. Owns all SS_ subsystems, manages game phases,
// platform initialization, save slot ownership, and service resolution.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "GameplayTagContainer.h"
#include "GI_GameFramework.generated.h"
// Forward declarations
class UDA_GameTagRegistry;
class USS_SaveManager;
class USS_UIManager;
class USS_SettingsSystem;
class USS_EnhancedInputManager;
class USS_AchievementSystem;
class USS_AudioManager;
/**
* Game Phase — top-level application state.
* Transitions are server-authoritative; clients receive via OnRep_GamePhase.
*/
UENUM(BlueprintType)
enum class EGamePhase : uint8
{
MainMenu UMETA(DisplayName = "Main Menu"),
Loading UMETA(DisplayName = "Loading"),
InGame UMETA(DisplayName = "In Game"),
Paused UMETA(DisplayName = "Paused"),
Cutscene UMETA(DisplayName = "Cutscene"),
DeathLoop UMETA(DisplayName = "Death Loop"),
AltDeathSpace UMETA(DisplayName = "Alt Death Space"),
Credits UMETA(DisplayName = "Credits"),
PostGame UMETA(DisplayName = "Post Game"),
};
/** Platform type for platform-specific initialization routing. */
UENUM(BlueprintType)
enum class EPlatformType : uint8
{
Generic UMETA(DisplayName = "Generic (PC)"),
Steam UMETA(DisplayName = "Steam"),
PS5 UMETA(DisplayName = "PlayStation 5"),
Xbox UMETA(DisplayName = "Xbox Series X|S"),
Switch UMETA(DisplayName = "Nintendo Switch"),
};
// ============================================================================
// Delegates (Event Dispatchers)
// ============================================================================
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGamePhaseChanged, EGamePhase, NewPhase);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPlatformReady);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnFrameworkReady);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFrameworkInitFailed, const FString&, ErrorReason);
/**
* GI_GameFramework — Application Kernel.
*
* The single persistent object that lives for the entire application session.
* Owns all SS_ GameInstanceSubsystems, manages save slot ownership, provides
* the canonical service resolver (GetService()), and tracks the top-level
* game phase state machine.
*
* In C++, this replaces the Blueprint "Get Game Instance → Cast → Get Subsystem"
* pattern with clean template access via GetService<T>().
*/
UCLASS(Blueprintable)
class FRAMEWORK_API UGI_GameFramework : public UGameInstance
{
GENERATED_BODY()
public:
UGI_GameFramework();
// ========================================================================
// Lifecycle
// ========================================================================
virtual void Init() override;
virtual void Shutdown() override;
// ========================================================================
// Configuration
// ========================================================================
/** Hard reference to the tag registry Data Asset. Loaded during Init. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Config")
TObjectPtr<UDA_GameTagRegistry> TagRegistry;
/** If true, validates all tags during Init. Recommended: true for development. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Config")
bool bValidateTagsOnInit = true;
/** If true, logs all registered tags to the output log during Init. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Debug")
bool bLogTagsOnInit = false;
/** Platform override. Determined automatically; can be overridden via command-line: -Platform=Steam */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Platform")
EPlatformType PlatformType = EPlatformType::Generic;
// ========================================================================
// Game Phase State Machine
// ========================================================================
/** Current top-level game phase. Server-authoritative; replicated to clients. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|State")
EGamePhase CurrentGamePhase = EGamePhase::MainMenu;
/**
* Sets the game phase and broadcasts OnGamePhaseChanged.
* Server-authoritative. No-ops if phase is unchanged.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|State")
void SetGamePhase(EGamePhase NewPhase);
/** Returns whether the framework has completed initialization. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|State")
bool IsFrameworkReady() const { return bFrameworkInitialized; }
// ========================================================================
// Session Flags (Transient, Non-Persisted)
// ========================================================================
/** Gets a session flag value. Returns false if the flag doesn't exist. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Session")
bool GetSessionFlag(FGameplayTag FlagTag) const;
/** Sets a session flag. Creates it if it doesn't exist. */
UFUNCTION(BlueprintCallable, Category = "Framework|Session")
void SetSessionFlag(FGameplayTag FlagTag, bool bValue);
/** Clears all session flags. Called on new game start. */
UFUNCTION(BlueprintCallable, Category = "Framework|Session")
void ClearAllSessionFlags();
// ========================================================================
// Save Slot Management
// ========================================================================
/** Currently active save slot index (-1 = none). */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Save")
int32 ActiveSlotIndex = -1;
/** Designates the active save slot. Does NOT trigger a load. */
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
void SetActiveSlot(int32 SlotIndex);
// ========================================================================
// Service Resolution
// ========================================================================
/**
* Canonical subsystem accessor. Maps a GameplayTag to a subsystem class.
* Returns nullptr and logs warning if tag isn't mapped or subsystem unavailable.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Services")
UGameInstanceSubsystem* GetService(FGameplayTag ServiceTag) const;
/** Template accessor for type-safe subsystem retrieval. */
template<typename T>
T* GetService() const
{
return GetSubsystem<T>();
}
// ========================================================================
// First-Launch State
// ========================================================================
/** True until onboarding/intro sequence clears it. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|State")
bool bFirstLaunch = true;
// ========================================================================
// Event Dispatchers
// ========================================================================
/** Broadcast when game phase changes. All systems react to this — never poll CurrentGamePhase. */
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnGamePhaseChanged OnGamePhaseChanged;
/** Broadcast when platform-specific initialization is complete. */
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnPlatformReady OnPlatformReady;
/** Broadcast when framework initialization is complete and tag registry is validated. */
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnFrameworkReady OnFrameworkReady;
/** Broadcast if framework initialization fails (missing tag registry, zero tags, etc.). */
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnFrameworkInitFailed OnFrameworkInitFailed;
protected:
// ========================================================================
// Internal State
// ========================================================================
bool bFrameworkInitialized = false;
/** Map of GameplayTag → bool for session-scoped flags. */
UPROPERTY()
TMap<FGameplayTag, bool> SessionFlags;
/** Maps service GameplayTags to subsystem classes. Populated during Init. */
UPROPERTY()
TMap<FGameplayTag, TSubclassOf<UGameInstanceSubsystem>> ServiceRegistry;
// ========================================================================
// Internal Methods
// ========================================================================
/** Platform-specific initialization routing. */
void InitPlatformServices();
/** Validates the tag registry and logs results. */
void ValidateFrameworkTags();
/** Builds the ServiceRegistry map of GameplayTag → SubsystemClass. */
void RegisterServices();
};

View File

@@ -0,0 +1,116 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — GM_CoreGameMode (05)
// Core Game Mode. Server-authoritative session rules, player spawning, chapter
// transitions, death routing. In C++, extends the replicated GameMode base for
// full networking support.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "GameplayTagContainer.h"
#include "GM_CoreGameMode.generated.h"
// Forward declarations
class UGI_GameFramework;
class AGS_CoreGameState;
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnChapterTransition, FGameplayTag, NewChapter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameOverTriggered, FGameplayTag, EndingTag);
/**
* GM_CoreGameMode — Core Game Mode.
*
* Sets the rules of the game session: which pawn, controller, player state,
* HUD, and game state classes to use. Manages chapter transitions, win/loss/
* death routing, and coordinates with the narrative system for story progression.
*
* Server-authoritative. Extends AGameModeBase for replication support.
*
* Note: PlayerControllerClass, PlayerStateClass, and GameStateClass are
* inherited from AGameModeBase — do not redeclare (UHT forbids shadowing).
* Set them in your BP child's Class Defaults.
*/
UCLASS(Blueprintable)
class FRAMEWORK_API AGM_CoreGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AGM_CoreGameMode();
// ========================================================================
// Chapter Management
// ========================================================================
/** The currently active chapter GameplayTag. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Narrative")
FGameplayTag CurrentChapterTag;
/**
* Transitions the game to a new chapter.
* Server-authoritative. Sets phase to Loading, opens the chapter level,
* then restores InGame phase on load complete.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void TransitionToChapter(FGameplayTag ChapterTag);
// ========================================================================
// Death Handling
// ========================================================================
/**
* Handles player death. Called by BPC_DeathHandlingSystem.
* Idempotent — safe to call multiple times.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Death")
void HandlePlayerDead(AController* DeadController);
// ========================================================================
// Ending / Game Over
// ========================================================================
/**
* Triggers a specific ending condition.
* Passes the ending tag to BPC_EndingAccumulator for accumulation logic.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void TriggerEnding(FGameplayTag EndingTag);
// ========================================================================
// Pause Control
// ========================================================================
/** Runtime flag — menu widgets check this before pausing. False during cutscenes/death. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|State")
bool bPauseAllowed = true;
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnChapterTransition OnChapterTransition;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnGameOverTriggered OnGameOverTriggered;
// ========================================================================
// Overrides
// ========================================================================
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
virtual void BeginPlay() override;
protected:
/** Cached reference to the framework GameInstance. */
UPROPERTY()
TObjectPtr<UGI_GameFramework> CachedFramework;
/** Server-side: performs the actual level transition for a chapter. */
void ServerTransitionToChapter(FGameplayTag ChapterTag);
/** Called when the chapter level finishes loading. */
void OnChapterLevelLoaded(FGameplayTag ChapterTag);
};

View File

@@ -0,0 +1,144 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — GS_CoreGameState (06)
// Shared session state. Fully replicated singleton visible to all players.
// Tracks chapter, narrative phase, encounter status, and active objectives.
// C++ gives us proper GetLifetimeReplicatedProps() and OnRep_ handlers.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "GameplayTagContainer.h"
#include "Net/UnrealNetwork.h"
#include "GS_CoreGameState.generated.h"
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnChapterChanged, FGameplayTag, NewChapter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnNarrativePhaseChanged, FGameplayTag, NewPhase);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEncounterStateChanged, bool, bActive);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnObjectiveTagsChanged);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayTimeUpdated, float, ElapsedSeconds);
/**
* GS_CoreGameState — Shared Session State.
*
* A replicated singleton data holder. All modification happens through
* dedicated setter functions — never direct variable writes.
*
* In C++, replication is handled via GetLifetimeReplicatedProps() with
* OnRep_ handlers that mirror broadcast dispatchers for both network
* clients and local single-player.
*/
UCLASS(Blueprintable)
class FRAMEWORK_API AGS_CoreGameState : public AGameStateBase
{
GENERATED_BODY()
public:
AGS_CoreGameState();
// ========================================================================
// Replicated State
// ========================================================================
/** Elapsed play time (accumulated only when GamePhase is InGame). */
UPROPERTY(ReplicatedUsing = OnRep_ElapsedPlayTime, BlueprintReadOnly, Category = "Framework|State")
float ElapsedPlayTime = 0.0f;
/** Current story chapter tag. */
UPROPERTY(ReplicatedUsing = OnRep_ActiveChapter, BlueprintReadOnly, Category = "Framework|Narrative")
FGameplayTag ActiveChapterTag;
/** Sub-chapter narrative phase. */
UPROPERTY(ReplicatedUsing = OnRep_NarrativePhase, BlueprintReadOnly, Category = "Framework|Narrative")
FGameplayTag ActiveNarrativePhase;
/** Whether an AI encounter is currently active. */
UPROPERTY(ReplicatedUsing = OnRep_EncounterActive, BlueprintReadOnly, Category = "Framework|Combat")
bool bEncounterActive = false;
/** Array of active objective tags. */
UPROPERTY(ReplicatedUsing = OnRep_ObjectiveTags, BlueprintReadOnly, Category = "Framework|Narrative")
TArray<FGameplayTag> ActiveObjectiveTags;
// ========================================================================
// Setters (Server-Authoritative)
// ========================================================================
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void SetChapter(FGameplayTag ChapterTag);
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void SetNarrativePhase(FGameplayTag PhaseTag);
UFUNCTION(BlueprintCallable, Category = "Framework|Combat")
void SetEncounterActive(bool bActive);
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void AddObjective(FGameplayTag ObjectiveTag);
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void RemoveObjective(FGameplayTag ObjectiveTag);
UFUNCTION(BlueprintCallable, Category = "Framework|Narrative")
void ClearAllObjectives();
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnPlayTimeUpdated OnElapsedPlayTimeUpdated;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnChapterChanged OnChapterChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnNarrativePhaseChanged OnNarrativePhaseChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnEncounterStateChanged OnEncounterActiveStateChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnObjectiveTagsChanged OnObjectiveTagsChanged;
// ========================================================================
// Overrides
// ========================================================================
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
protected:
// ========================================================================
// OnRep Handlers — Fire dispatchers for network clients AND local single-player
// ========================================================================
UFUNCTION()
void OnRep_ElapsedPlayTime();
UFUNCTION()
void OnRep_ActiveChapter();
UFUNCTION()
void OnRep_NarrativePhase();
UFUNCTION()
void OnRep_EncounterActive();
UFUNCTION()
void OnRep_ObjectiveTags();
// ========================================================================
// Internal
// ========================================================================
/** Cached GameInstance for phase checking during time accumulation. */
UPROPERTY()
TObjectPtr<class UGI_GameFramework> CachedFramework;
/** Throttle timer for play time updates (fires ~1/sec). */
float TimeUpdateAccumulator = 0.0f;
static constexpr float TimeUpdateInterval = 1.0f;
};

View File

@@ -0,0 +1,317 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — Framework Interfaces (03)
// All 9 Blueprint Interfaces defined in C++ for clean default values and type safety.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "GameplayTagContainer.h"
#include "I_InterfaceLibrary.generated.h"
// ============================================================================
// Forward Declarations
// ============================================================================
class UDA_ItemData;
class UDA_InteractionData;
// ============================================================================
// I_Interactable — World objects the player can interact with
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UInteractable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IInteractable
{
GENERATED_BODY()
public:
/** Called when a player interacts with this object. Returns true if interaction succeeded. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool OnInteract(AActor* Interactor);
/** Called when crosshair/focus enters this object. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void OnFocusBegin(AActor* Interactor);
/** Called when crosshair/focus leaves this object. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void OnFocusEnd(AActor* Interactor);
/** Returns the interaction prompt text (e.g. "Open Door", "Pick Up"). */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
FText GetInteractionPrompt() const;
/** Returns whether interaction is currently possible. BlockReason explains why if false. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool CanInteract(AActor* Interactor, FText& OutBlockReason) const;
/** Returns the GameplayTag identifying the interaction type (e.g. Framework.Interaction.Type.Pickup). */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
FGameplayTag GetInteractionType() const;
};
// ============================================================================
// I_Inspectable — Objects that can be examined in 3D inspect mode
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UInspectable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IInspectable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void StartInspect(AActor* Inspector);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void EndInspect(AActor* Inspector);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void RotateInspect(FRotator RotationDelta);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool GetInspectData(FVector& OutAnchorPoint, FRotator& OutDefaultRotation, float& OutZoomDistance) const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool HasInspectInfo() const;
};
// ============================================================================
// I_Damageable — Anything that takes damage or can be healed
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UDamageable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IDamageable
{
GENERATED_BODY()
public:
/** Apply damage. Returns actual damage dealt after modifiers. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
float TakeDamage(float DamageAmount, AActor* DamageCauser, FGameplayTag DamageType, FVector HitLocation, FVector HitDirection);
/** Heal by amount. Returns actual health restored. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
float Heal(float HealAmount, AActor* Healer);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
bool IsAlive() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
float GetCurrentHealth() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
float GetMaxHealth() const;
/** Called when health reaches zero. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
void OnDeath(AActor* Killer, FGameplayTag DeathCause);
/** Returns multiplier for incoming damage (e.g. 1.5 = takes 50% more damage). */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Combat")
float GetDamageModifier(FGameplayTag DamageType) const;
};
// ============================================================================
// I_Holdable — Physics objects the player can grab and manipulate
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UHoldable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IHoldable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void OnPickup(AActor* Holder);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void OnDrop(AActor* Dropper);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
FTransform GetHoldTransform() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool IsHeld() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void OnReleasedFromHold();
};
// ============================================================================
// I_Lockable — Objects that can be locked/unlocked with key items
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class ULockable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API ILockable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool TryLock(AActor* Locker);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool TryUnlock(AActor* Unlocker, FGameplayTag KeyTag);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool IsLocked() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
FGameplayTag GetRequiredKeyTag() const;
/** Broadcast when lock state changes (implementor fires via delegate). */
virtual void OnLockStateChanged(bool bNewLocked) {}
};
// ============================================================================
// I_UsableItem — Items that can be used from inventory/quick-slots
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UUsableItem : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IUsableItem
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Inventory")
bool UseItem(AActor* User, AActor* Target);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Inventory")
bool CanUseItem(AActor* User, AActor* Target) const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Inventory")
float GetUseDuration() const;
/** Called after UseItem completes (for animations, effects). */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Inventory")
void OnItemUsed(AActor* User);
};
// ============================================================================
// I_Persistable — Actors that save/load their state to disk
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UPersistable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IPersistable
{
GENERATED_BODY()
public:
/** Serialize state to a byte array for saving. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Save")
TArray<uint8> OnSave();
/** Restore state from a previously saved byte array. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Save")
void OnLoad(const TArray<uint8>& Data);
/** Returns the unique GameplayTag identifier for this persistable actor. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Save")
FGameplayTag GetSaveTag() const;
/** Returns true if this actor has changed since last save. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Save")
bool NeedsSave() const;
};
// ============================================================================
// I_Toggleable — Objects with binary on/off states
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UToggleable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IToggleable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void Toggle(AActor* Toggler);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void SetState(bool bNewState, AActor* Setter);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
bool GetCurrentState() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
FText GetStateLabel() const;
/** Broadcast when state changes. */
virtual void OnStateChanged(bool bNewState, AActor* Changer) {}
};
// ============================================================================
// I_Adjustable — Objects with continuous value range (dials, sliders)
// ============================================================================
UINTERFACE(MinimalAPI, BlueprintType)
class UAdjustable : public UInterface
{
GENERATED_BODY()
};
class FRAMEWORK_API IAdjustable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void Adjust(float Delta, AActor* Adjuster);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
void SetValue(float NewValue, AActor* Setter);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
float GetCurrentValue() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
float GetMinValue() const;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Framework|Interaction")
float GetMaxValue() const;
/** Broadcast when value changes. */
virtual void OnValueChanged(float NewValue, AActor* Changer) {}
};

View File

@@ -0,0 +1,232 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — SS_EnhancedInputManager (128)
// Sole authority for Push/Pop input context, key rebinding, and input mode changes.
// In C++, directly wraps UEnhancedInputLocalPlayerSubsystem with priority-based
// context stack management.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "GameplayTagContainer.h"
#include "InputAction.h"
#include "SS_EnhancedInputManager.generated.h"
class UInputMappingContext;
class UInputAction;
class UEnhancedInputLocalPlayerSubsystem;
/**
* Input context priority ladder.
* Higher priority contexts override lower priority for conflicting inputs.
*/
UENUM(BlueprintType)
enum class EInputContextPriority : uint8
{
Default = 0 UMETA(DisplayName = "Default (0)"),
Hiding = 5 UMETA(DisplayName = "Hiding (5)"),
Wristwatch = 10 UMETA(DisplayName = "Wristwatch UI (10)"),
Inspection = 20 UMETA(DisplayName = "Inspection (20)"),
UI = 100 UMETA(DisplayName = "UI (100)"),
};
/**
* Mapping context entry in the stack.
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FInputContextEntry
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly)
TObjectPtr<UInputMappingContext> Context = nullptr;
UPROPERTY(BlueprintReadOnly)
EInputContextPriority Priority = EInputContextPriority::Default;
UPROPERTY(BlueprintReadOnly)
FGameplayTag ContextTag; // For identification: Framework.Input.Context.Default, etc.
bool operator==(const FInputContextEntry& Other) const
{
return Context == Other.Context;
}
};
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContextPushed, FGameplayTag, ContextTag, EInputContextPriority, Priority);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContextPopped, FGameplayTag, ContextTag, EInputContextPriority, Priority);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInputModeChanged, bool, bUIMode, bool, bShowCursor);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnKeyRebound, FGameplayTag, ActionTag, FKey, NewKey);
/**
* SS_EnhancedInputManager — Input Context Stack Authority.
*
* Manages all Enhanced Input Mapping Contexts with priority-based ordering.
* Systems call PushContext/PopContext instead of directly touching the
* Enhanced Input subsystem. Coordinates input mode (game/UI) with SS_UIManager.
*
* C++ gives us direct UEnhancedInputLocalPlayerSubsystem access — no
* "Get Enhanced Input Local Player Subsystem" node chains.
*/
UCLASS()
class FRAMEWORK_API USS_EnhancedInputManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
USS_EnhancedInputManager();
// ========================================================================
// Lifecycle
// ========================================================================
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// ========================================================================
// Context Stack Management
// ========================================================================
/**
* Push an input mapping context onto the stack.
* Higher priority contexts override lower for conflicting inputs.
* Duplicate protection — if the context is already active, it's moved to top.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void PushContext(UInputMappingContext* Context, EInputContextPriority Priority, FGameplayTag ContextTag);
/**
* Remove an input mapping context from the stack.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void PopContext(UInputMappingContext* Context);
/**
* Pop a context by its GameplayTag identifier.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void PopContextByTag(FGameplayTag ContextTag);
/**
* Clear ALL contexts from the stack (e.g., on level transition).
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void ClearAllContexts();
/**
* Returns whether a context is currently active on the stack.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
bool IsContextActive(FGameplayTag ContextTag) const;
/**
* Returns the currently highest-priority active context.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
FGameplayTag GetTopContext() const;
// ========================================================================
// Input Mode Coordination
// ========================================================================
/**
* Switch between game input mode and UI input mode.
* Coordinates cursor visibility and input blocking with SS_UIManager.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void SetInputMode(bool bUIMode, bool bShowCursor = true, bool bLockMouseToViewport = false);
/** Returns whether UI input mode is active. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
bool IsUIMode() const { return bCurrentUIMode; }
// ========================================================================
// Key Rebinding
// ========================================================================
/**
* Rebind a key for a specific input action.
* Persists via SS_SettingsSystem.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void RebindKey(UInputAction* Action, FKey NewKey, bool bSaveToDisk = true);
/**
* Reset all key bindings to defaults.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Input")
void ResetAllBindings();
/**
* Get the current key bound to an input action.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
FKey GetBoundKey(UInputAction* Action) const;
// ========================================================================
// Query
// ========================================================================
/**
* Check if a specific input action is currently being pressed.
* Use this instead of raw Enhanced Input queries.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
bool IsActionPressed(UInputAction* Action) const;
/**
* Get the current value of an input action (0.0 to 1.0 for digital, axis value for analog).
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
float GetActionValue(UInputAction* Action) const;
// ========================================================================
// Configuration
// ========================================================================
/** Default input mapping contexts (loaded on initialize). */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Input|Config")
TArray<TObjectPtr<UInputMappingContext>> DefaultContexts;
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
FOnContextPushed OnContextPushed;
UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
FOnContextPopped OnContextPopped;
UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
FOnInputModeChanged OnInputModeChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
FOnKeyRebound OnKeyRebound;
protected:
// ========================================================================
// Internal State
// ========================================================================
/** Ordered context stack (highest priority at the end/newest). */
TArray<FInputContextEntry> ContextStack;
/** Whether UI input mode is currently active. */
bool bCurrentUIMode = false;
/** Cached Enhanced Input subsystem. */
UPROPERTY()
TObjectPtr<UEnhancedInputLocalPlayerSubsystem> EnhancedInputSubsystem;
// ========================================================================
// Internal Methods
// ========================================================================
/** Re-sorts the context stack by priority and re-applies to the subsystem. */
void RebuildContextStack();
/** Gets the Enhanced Input subsystem, caching it if needed. */
UEnhancedInputLocalPlayerSubsystem* GetEnhancedInputSubsystem() const;
};

View File

@@ -0,0 +1,210 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — BPC_InventorySystem (31)
// Core inventory grid. Add/remove/sort/stack/weight management.
// In C++, TArray operations with lambdas are natively fast — no BP array node overhead.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameplayTagContainer.h"
#include "BPC_InventorySystem.generated.h"
class UDA_ItemData;
/**
* Single inventory slot entry.
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FInventorySlot
{
GENERATED_BODY()
/** The item in this slot. nullptr = empty slot. */
UPROPERTY(BlueprintReadOnly)
TObjectPtr<UDA_ItemData> Item = nullptr;
/** How many of this item are stacked here. */
UPROPERTY(BlueprintReadOnly)
int32 Quantity = 0;
/** Grid position for UI layout. */
UPROPERTY(BlueprintReadOnly)
int32 GridX = 0;
/** Grid position for UI layout. */
UPROPERTY(BlueprintReadOnly)
int32 GridY = 0;
bool IsEmpty() const { return Item == nullptr || Quantity <= 0; }
void Clear()
{
Item = nullptr;
Quantity = 0;
}
bool operator==(const FInventorySlot& Other) const
{
return Item == Other.Item && Quantity == Other.Quantity && GridX == Other.GridX && GridY == Other.GridY;
}
};
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnInventoryChanged);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnItemAdded, UDA_ItemData*, Item, int32, Quantity);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnItemRemoved, UDA_ItemData*, Item, int32, Quantity);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnWeightChanged, float, CurrentWeight, float, MaxWeight);
/**
* BPC_InventorySystem — Core Inventory Grid.
*
* Manages the player's carried items: add, remove, sort, stack, weight tracking.
* C++ TArray operations (FindByPredicate, Sort, Filter) are natively compiled —
* no BP interpretive array node overhead.
*/
UCLASS(Blueprintable, ClassGroup = (Framework), meta = (BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_InventorySystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_InventorySystem();
// ========================================================================
// Configuration
// ========================================================================
/** Grid width (columns). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Inventory|Config")
int32 GridWidth = 8;
/** Grid height (rows). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Inventory|Config")
int32 GridHeight = 5;
/** Maximum carry weight. Items exceeding this cannot be picked up. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Inventory|Config")
float MaxWeight = 50.0f;
// ========================================================================
// Inventory State
// ========================================================================
/** All inventory slots (GridWidth × GridHeight). */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Inventory")
TArray<FInventorySlot> Slots;
/** Current total weight carried. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Inventory")
float CurrentWeight = 0.0f;
/** Whether the inventory has been modified since last save. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Inventory")
bool bDirty = false;
// ========================================================================
// Core Operations
// ========================================================================
/**
* Add an item to the inventory. Stacks if possible, finds empty slot otherwise.
* Returns the quantity actually added (may be less than requested if full).
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Inventory")
int32 AddItem(UDA_ItemData* Item, int32 Quantity = 1);
/**
* Remove an item from the inventory.
* Returns the quantity actually removed.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Inventory")
int32 RemoveItem(UDA_ItemData* Item, int32 Quantity = 1);
/**
* Remove an item from a specific slot.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Inventory")
int32 RemoveItemFromSlot(int32 SlotIndex, int32 Quantity = 1);
/**
* Check if an item can be added (enough space and weight capacity).
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
bool CanAddItem(UDA_ItemData* Item, int32 Quantity = 1) const;
// ========================================================================
// Query
// ========================================================================
/** Returns the total quantity of an item across all stacks. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
int32 GetItemCount(UDA_ItemData* Item) const;
/** Returns whether the inventory contains at least this many of an item. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
bool HasItem(UDA_ItemData* Item, int32 Quantity = 1) const;
/** Finds the first slot containing the given item. Returns -1 if not found. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
int32 FindItemSlot(UDA_ItemData* Item) const;
/** Returns all unique items currently in the inventory. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
TArray<UDA_ItemData*> GetAllItems() const;
/** Returns the number of empty slots available. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
int32 GetEmptySlotCount() const;
/** Returns the number of free weight units remaining. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Inventory")
float GetRemainingWeight() const;
// ========================================================================
// Organization
// ========================================================================
/** Sort inventory by ItemType, then by DisplayName. */
UFUNCTION(BlueprintCallable, Category = "Framework|Inventory")
void SortInventory();
/** Auto-merge all partial stacks of the same item. */
UFUNCTION(BlueprintCallable, Category = "Framework|Inventory")
void ConsolidateStacks();
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Inventory|Events")
FOnInventoryChanged OnInventoryChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Inventory|Events")
FOnItemAdded OnItemAdded;
UPROPERTY(BlueprintAssignable, Category = "Framework|Inventory|Events")
FOnItemRemoved OnItemRemoved;
UPROPERTY(BlueprintAssignable, Category = "Framework|Inventory|Events")
FOnWeightChanged OnWeightChanged;
// ========================================================================
// Overrides
// ========================================================================
virtual void BeginPlay() override;
protected:
/** Recalculates total weight from all slots. */
void RecalculateWeight();
/** Finds an existing stack for an item (not at max stack limit). Returns -1 if none found. */
int32 FindExistingStack(UDA_ItemData* Item) const;
/** Finds the first empty slot. Returns -1 if inventory is full. */
int32 FindEmptySlot() const;
/** Marks inventory as modified and broadcasts change dispatchers. */
void MarkDirty();
};

View File

@@ -0,0 +1,37 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "DA_EquipmentConfig.generated.h"
USTRUCT(BlueprintType)
struct FRAMEWORK_API FDamageTypeResistance
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FGameplayTag DamageType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Resistance = 0.0f;
};
UCLASS(BlueprintType)
class FRAMEWORK_API UDA_EquipmentConfig : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config")
TArray<FDamageTypeResistance> DamageTypeResistances;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config")
float Durability = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config")
float Weight = 1.0f;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Config")
float GetResistance(FGameplayTag DamageType) const;
};

View File

@@ -0,0 +1,232 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — DA_ItemData (07)
// Base item Data Asset. Single source of truth for every item.
// C++ gives us UPROPERTY metadata (EditCondition, EditConditionHides, ClampMin/Max)
// that make the Data Asset editor usable for designers — impossible in Blueprint.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "Engine/Texture2D.h"
#include "Engine/StaticMesh.h"
#include "DA_ItemData.generated.h"
/**
* Item type classification.
*/
UENUM(BlueprintType)
enum class EItemType : uint8
{
Weapon UMETA(DisplayName = "Weapon"),
Ammo UMETA(DisplayName = "Ammo"),
Consumable UMETA(DisplayName = "Consumable"),
KeyItem UMETA(DisplayName = "Key Item"),
Document UMETA(DisplayName = "Document"),
Collectible UMETA(DisplayName = "Collectible"),
Tool UMETA(DisplayName = "Tool"),
Resource UMETA(DisplayName = "Resource"),
Misc UMETA(DisplayName = "Misc"),
};
/**
* Equipment slot type.
*/
UENUM(BlueprintType)
enum class EEquipmentSlot : uint8
{
None UMETA(DisplayName = "None"),
PrimaryWeapon UMETA(DisplayName = "Primary Weapon"),
SecondaryWeapon UMETA(DisplayName = "Secondary Weapon"),
Melee UMETA(DisplayName = "Melee"),
Tool UMETA(DisplayName = "Tool"),
Armor UMETA(DisplayName = "Armor"),
Accessory UMETA(DisplayName = "Accessory"),
};
/**
* Equipment-specific data (shown when ItemType is Weapon or Tool).
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FItemEquipmentData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
EEquipmentSlot Slot = EEquipmentSlot::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Damage = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float FireRate = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Range = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MagazineSize = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float ReloadTime = 0.0f;
};
/**
* Consumable-specific data (shown when ItemType is Consumable).
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FItemConsumableData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0", ClampMax = "100"))
float HealthRestore = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0", ClampMax = "100"))
float StressReduce = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float UseDuration = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bConsumedOnUse = true;
};
/**
* Inspect-specific data (shown when bHasInspectMode is true).
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FItemInspectData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FVector AnchorPoint = FVector::ZeroVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FRotator DefaultRotation = FRotator::ZeroRotator;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float ZoomDistance = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bCanRotate = true;
};
/**
* DA_ItemData — Base Item Data Asset.
*
* Every item in the game is one DA_ItemData asset. No item data lives in
* Blueprint logic. C++ gives us EditCondition metadata so the editor only
* shows relevant sub-structs based on ItemType — a massive UX win for designers.
*/
UCLASS(BlueprintType, Blueprintable, meta = (DisplayName = "Item Data"))
class FRAMEWORK_API UDA_ItemData : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UDA_ItemData();
// ========================================================================
// Core Properties (Every Item Has These)
// ========================================================================
/** Unique GameplayTag identifier. Must be registered in DA_GameTagRegistry. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core")
FGameplayTag ItemTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core")
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core", meta = (MultiLine = true))
FText Description;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core")
TSoftObjectPtr<UTexture2D> Icon;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core")
TSoftObjectPtr<UStaticMesh> WorldMesh;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core", meta = (ClampMin = "0", ClampMax = "1000"))
float Weight = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core", meta = (ClampMin = "1", ClampMax = "999"))
int32 StackLimit = 1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Core")
EItemType ItemType = EItemType::Misc;
// ========================================================================
// Conditional Sub-Data (Shown Based on ItemType via EditCondition)
// ========================================================================
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Equipment",
meta = (EditCondition = "ItemType == EItemType::Weapon || ItemType == EItemType::Tool", EditConditionHides))
FItemEquipmentData EquipmentData;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Consumable",
meta = (EditCondition = "ItemType == EItemType::Consumable", EditConditionHides))
FItemConsumableData ConsumableData;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Inspect",
meta = (EditCondition = "bHasInspectMode", EditConditionHides))
FItemInspectData InspectData;
// ========================================================================
// Flags
// ========================================================================
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Flags")
bool bIsKeyItem = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Flags")
bool bCanBeDropped = true;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Flags")
bool bHasInspectMode = false;
// ========================================================================
// Combination / Crafting
// ========================================================================
/** Tags of items this can combine with. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Combination")
TArray<FGameplayTag> CombinesWith;
/** The resulting item tag when combined with CombinesWith item. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Combination")
FGameplayTag CombineResult;
// ========================================================================
// Extensibility
// ========================================================================
/** Custom per-project properties — no need to modify the base class. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Custom")
TMap<FName, FString> CustomProperties;
// ========================================================================
// Validation (Editor-Only)
// ========================================================================
#if WITH_EDITOR
/**
* Validates the item data asset for common errors.
* Called by editor utilities or pre-save validation.
*/
UFUNCTION(BlueprintCallable, Category = "Item|Validation")
bool ValidateItemData(FString& OutErrors) const;
#endif
// ========================================================================
// Overrides
// ========================================================================
virtual void PostLoad() override;
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BPC_HealthSystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChanged, float, CurrentHealth, float, MaxHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeath);
UCLASS(Blueprintable, ClassGroup=(Framework), meta=(BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_HealthSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_HealthSystem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Health")
float MaxHealth = 100.0f;
UPROPERTY(BlueprintReadOnly, Category = "Framework|Health")
float CurrentHealth = 100.0f;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnHealthChanged OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnDeath OnDeath;
};

View File

@@ -0,0 +1,23 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameplayTagContainer.h"
#include "BPC_MovementStateSystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMovementModeChanged, FGameplayTag, NewMode, FGameplayTag, OldMode);
UCLASS(Blueprintable, ClassGroup=(Framework), meta=(BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_MovementStateSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_MovementStateSystem();
UPROPERTY(BlueprintReadOnly, Category = "Framework|Movement")
FGameplayTag CurrentMovementMode;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnMovementModeChanged OnMovementModeChanged;
};

View File

@@ -0,0 +1,25 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BPC_StaminaSystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnExhaustionStateChanged, bool, bExhausted);
UCLASS(Blueprintable, ClassGroup=(Framework), meta=(BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_StaminaSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_StaminaSystem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Stamina")
float MaxStamina = 100.0f;
UPROPERTY(BlueprintReadOnly, Category = "Framework|Stamina")
float CurrentStamina = 100.0f;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnExhaustionStateChanged OnExhaustionStateChanged;
};

View File

@@ -0,0 +1,246 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — BPC_StateManager (130)
// Central State Authority. Single source of truth for "what can the player do right now?"
// Manages exclusive action states, upper-body overlay states, action gating,
// vital signs (heart rate), and the force-stack pattern for nested overrides.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameplayTagContainer.h"
#include "BPC_StateManager.generated.h"
// Forward declarations
class UDA_StateGatingTable;
class UBPC_HealthSystem;
class UBPC_StressSystem;
class UBPC_StaminaSystem;
class UBPC_MovementStateSystem;
// ============================================================================
// Delegates
// ============================================================================
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnActionStateChanged, FGameplayTag, NewState, FGameplayTag, OldState);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnOverlayStateChanged, FGameplayTag, NewOverlay, FGameplayTag, OldOverlay);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnVitalSignChanged, FGameplayTag, VitalTag, float, NewValue);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnForceStackPushed, FGameplayTag, ForceState);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnForceStackPopped, FGameplayTag, RestoredState);
/**
* Result codes for state change requests.
* Mirrors the Blueprint E_ActionRequestResult enum.
*/
UENUM(BlueprintType)
enum class EActionRequestResult : uint8
{
Granted UMETA(DisplayName = "Granted"),
Denied UMETA(DisplayName = "Denied — Gated"),
BlockedByForce UMETA(DisplayName = "Blocked — Force Stack Override"),
AlreadyActive UMETA(DisplayName = "Already Active"),
InvalidState UMETA(DisplayName = "Invalid State Tag"),
RequesterNotFound UMETA(DisplayName = "Requester Not Found"),
CooldownActive UMETA(DisplayName = "Cooldown Active"),
VitalThreshold UMETA(DisplayName = "Vital Threshold Not Met"),
};
/**
* Heart rate tier for vital sign tracking.
*/
UENUM(BlueprintType)
enum class EHeartRateTier : uint8
{
Resting UMETA(DisplayName = "Resting (60-80 BPM)"),
Elevated UMETA(DisplayName = "Elevated (80-100 BPM)"),
Stressed UMETA(DisplayName = "Stressed (100-130 BPM)"),
Panic UMETA(DisplayName = "Panic (130-160 BPM)"),
Critical UMETA(DisplayName = "Critical (160+ BPM)"),
};
/**
* BPC_StateManager — Central State Authority.
*
* Every system queries IsActionPermitted(Tag) instead of checking other systems
* directly. Gating rules are defined in DA_StateGatingTable (37 rules).
* In C++, the Chooser Table iteration is native-speed — no BP interpretive overhead.
*/
UCLASS(Blueprintable, ClassGroup = (Framework), meta = (BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_StateManager : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_StateManager();
// ========================================================================
// Configuration
// ========================================================================
/** The gating rules Data Asset. Contains all 37 action rules. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Config")
TObjectPtr<UDA_StateGatingTable> GatingTable;
/** Default action state on BeginPlay. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Config")
FGameplayTag DefaultActionState;
/** Default overlay state on BeginPlay. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Config")
FGameplayTag DefaultOverlayState;
// ========================================================================
// Core Query — Hot Path
// ========================================================================
/**
* Central query: "Can the player perform this action right now?"
* Called by EVERY gameplay system per-frame. C++ makes this fast.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|State")
bool IsActionPermitted(FGameplayTag ActionTag) const;
/**
* Request a state change. Returns the result code.
* Server-authoritative.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|State")
EActionRequestResult RequestStateChange(FGameplayTag NewState, AActor* Requester);
// ========================================================================
// Current State (Read-Only)
// ========================================================================
/** Currently active exclusive action state (only one at a time). */
UPROPERTY(BlueprintReadOnly, Category = "Framework|State")
FGameplayTag CurrentActionState;
/** Currently active upper-body overlay state. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|State")
FGameplayTag CurrentOverlayState;
// ========================================================================
// Force Stack Pattern (Death, Cutscenes, Void Space)
// ========================================================================
/**
* Pushes a forced state onto the stack. Overrides all gating.
* Example: death overrides everything.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|State")
void ForceStateChange(FGameplayTag ForceState, FString Reason);
/**
* Pops the top forced state and restores the previous state.
* Example: respawn restores the pre-death state.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|State")
void RestorePreviousState();
/** Returns the number of states currently on the force stack. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|State")
int32 GetForceStackDepth() const { return ForceStack.Num(); }
// ========================================================================
// Vital Signs — Heart Rate
// ========================================================================
/** Current heart rate in BPM (smoothed). */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Vitals")
float HeartRateBPM = 70.0f;
/** Current heart rate tier. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Vitals")
EHeartRateTier HeartRateTier = EHeartRateTier::Resting;
/** Target heart rate (set by stress, stamina, combat). Interpolated toward each tick. */
UPROPERTY(BlueprintReadOnly, Category = "Framework|Vitals")
float TargetHeartRate = 70.0f;
/** Smoothing speed for heart rate interpolation. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Vitals")
float HeartRateSmoothSpeed = 2.0f;
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnActionStateChanged OnActionStateChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnOverlayStateChanged OnOverlayStateChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnVitalSignChanged OnVitalSignChanged;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnForceStackPushed OnForceStackPushed;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnForceStackPopped OnForceStackPopped;
// ========================================================================
// Overrides
// ========================================================================
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
protected:
// ========================================================================
// Internal State
// ========================================================================
/** Force stack — array of (State, Reason) pairs. Most recent is active. */
struct FForceStackEntry
{
FGameplayTag State;
FString Reason;
};
TArray<FForceStackEntry> ForceStack;
/** Previous action state before force override (for restore). */
FGameplayTag PreForceActionState;
/** Previous overlay state before force override (for restore). */
FGameplayTag PreForceOverlayState;
// ========================================================================
// Gating Logic
// ========================================================================
/** Check gating rules for a tag against current state. */
bool EvaluateGatingRules(FGameplayTag ActionTag) const;
/** Check if any force stack entry blocks this action. */
bool IsBlockedByForceStack(FGameplayTag ActionTag) const;
// ========================================================================
// Vital Sign Calculation
// ========================================================================
/** Recalculates target heart rate based on stress tier + stamina exhaustion + combat. */
void RecalculateTargetHeartRate();
/** Determines heart rate tier from current BPM. */
static EHeartRateTier GetHeartRateTier(float BPM);
// ========================================================================
// Binding References (cached in BeginPlay)
// ========================================================================
UPROPERTY()
TObjectPtr<UBPC_HealthSystem> CachedHealthSystem;
UPROPERTY()
TObjectPtr<UBPC_StressSystem> CachedStressSystem;
UPROPERTY()
TObjectPtr<UBPC_StaminaSystem> CachedStaminaSystem;
UPROPERTY()
TObjectPtr<UBPC_MovementStateSystem> CachedMovementSystem;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BPC_StressSystem.generated.h"
UENUM(BlueprintType)
enum class EStressTier : uint8
{
Calm UMETA(DisplayName = "Calm"),
Tense UMETA(DisplayName = "Tense"),
Distressed UMETA(DisplayName = "Distressed"),
Panic UMETA(DisplayName = "Panic"),
Catatonic UMETA(DisplayName = "Catatonic"),
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStressTierChanged, EStressTier, NewTier);
UCLASS(Blueprintable, ClassGroup=(Framework), meta=(BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_StressSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_StressSystem();
UPROPERTY(BlueprintReadOnly, Category = "Framework|Stress")
EStressTier StressTier = EStressTier::Calm;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnStressTierChanged OnStressTierChanged;
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "PC_CoreController.generated.h"
UCLASS(Blueprintable)
class FRAMEWORK_API APC_CoreController : public APlayerController
{
GENERATED_BODY()
public:
APC_CoreController();
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "PS_CorePlayerState.generated.h"
UCLASS(Blueprintable)
class FRAMEWORK_API APS_CorePlayerState : public APlayerState
{
GENERATED_BODY()
public:
APS_CorePlayerState();
};

View File

@@ -0,0 +1,193 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — SS_SaveManager (35)
// Save/Load subsystem. Slot management, serialization, manifest tracking.
// In C++, uses FArchive for direct binary serialization — far more powerful
// than Blueprint "Save Game" / "Load Game" nodes.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "GameplayTagContainer.h"
#include "SS_SaveManager.generated.h"
/**
* Save slot metadata returned by GetSlotManifest().
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FSaveSlotInfo
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly)
int32 SlotIndex = -1;
UPROPERTY(BlueprintReadOnly)
FString SlotName;
UPROPERTY(BlueprintReadOnly)
FString ChapterName;
UPROPERTY(BlueprintReadOnly)
float PlayTimeHours = 0.0f;
UPROPERTY(BlueprintReadOnly)
FDateTime Timestamp;
UPROPERTY(BlueprintReadOnly)
FGameplayTag LastCheckpoint;
UPROPERTY(BlueprintReadOnly)
bool bIsEmpty = true;
};
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSaveComplete, int32, SlotIndex, bool, bSuccess);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnLoadComplete, int32, SlotIndex, bool, bSuccess);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSaveManifestUpdated, const TArray<FSaveSlotInfo>&, Slots);
/**
* SS_SaveManager — Save/Load Subsystem.
*
* Manages all save slots, serialization, and manifest tracking.
* C++ gives us direct FArchive-based serialization, proper error handling,
* and async save operations — impossible to match in Blueprint.
*/
UCLASS()
class FRAMEWORK_API USS_SaveManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
USS_SaveManager();
// ========================================================================
// Lifecycle
// ========================================================================
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// ========================================================================
// Configuration
// ========================================================================
/** Maximum number of save slots. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Save")
int32 MaxSlots = 10;
/** Save game file prefix for slot naming. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Save")
FString SavePrefix = TEXT("FrameworkSave_");
// ========================================================================
// Slot Manifest
// ========================================================================
/**
* Returns metadata for all save slots.
* Fast — reads header only, not full save data.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
TArray<FSaveSlotInfo> GetSlotManifest() const;
/**
* Checks if a slot has save data.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Save")
bool DoesSlotExist(int32 SlotIndex) const;
// ========================================================================
// Save / Load Operations
// ========================================================================
/**
* Save game to a slot. Returns true if successful.
* Server-authoritative in multiplayer.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool SaveGame(int32 SlotIndex, const FString& Description);
/**
* Load game from a slot. Returns true if successful.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool LoadGame(int32 SlotIndex);
/**
* Delete a save slot. Irreversible!
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool DeleteSlot(int32 SlotIndex);
/**
* Quick-save to the current active slot.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool QuickSave();
/**
* Quick-load from the current active slot.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool QuickLoad();
// ========================================================================
// Checkpoint Management
// ========================================================================
/** Loads the most recent checkpoint from a slot. */
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool LoadCheckpoint(int32 SlotIndex);
/**
* Creates a checkpoint within the current slot.
* Checkpoints are incremental saves within a single slot.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool CreateCheckpoint(FGameplayTag CheckpointTag);
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnSaveComplete OnSaveComplete;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnLoadComplete OnLoadComplete;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnSaveManifestUpdated OnSaveManifestUpdated;
// ========================================================================
// Utilities
// ========================================================================
/** Returns the total disk space used by all saves (in bytes). */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Save")
int64 GetTotalSaveSize() const;
/** Backs up all save slots to a Backup/ subdirectory. */
UFUNCTION(BlueprintCallable, Category = "Framework|Save")
bool BackupAllSaves(const FString& BackupLabel);
protected:
/** Builds the save slot name from prefix + index. */
FString GetSlotName(int32 SlotIndex) const;
/** Reads only the header/metadata from a save file. */
FSaveSlotInfo ReadSlotHeader(int32 SlotIndex) const;
/** Internal save implementation using FArchive serialization. */
bool SaveToFile(int32 SlotIndex, const TArray<uint8>& Data, const FSaveSlotInfo& Meta);
/** Internal load implementation. */
bool LoadFromFile(int32 SlotIndex, TArray<uint8>& OutData, FSaveSlotInfo& OutMeta);
/** Path to the save directory. */
FString GetSaveDirectory() const;
/** Currently active save slot (from GI_GameFramework). */
int32 GetActiveSlot() const;
};

View File

@@ -0,0 +1,34 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "DA_StateGatingTable.generated.h"
USTRUCT(BlueprintType)
struct FRAMEWORK_API FStateGatingRule
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FGameplayTag ActionTag;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FGameplayTag BlockedByState;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bIsBlocked = true;
};
UCLASS(BlueprintType)
class FRAMEWORK_API UDA_StateGatingTable : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gating")
TArray<FStateGatingRule> GatingRules;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Gating")
bool IsActionGated(FGameplayTag ActionTag, FGameplayTag CurrentState) const;
};

View File

@@ -0,0 +1,150 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — BPC_DamageReceptionSystem (72)
// Damage reception, resistance calculation, and damage application.
// Called potentially dozens of times per combat frame — C++ performance critical.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameplayTagContainer.h"
#include "BPC_DamageReceptionSystem.generated.h"
// Forward declarations
class UDA_EquipmentConfig;
class UBPC_HealthSystem;
class UBPC_ShieldDefenseSystem;
class UBPC_HitReactionSystem;
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FOnDamageReceived, float, RawDamage, float, FinalDamage,
AActor*, DamageCauser, FGameplayTag, DamageType, FVector, HitLocation);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDamageResisted, float, DamageResisted,
FGameplayTag, ResistanceType, FString, Reason);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStaggered, AActor*, StaggerCauser, float, StaggerForce);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnKnockedDown, AActor*, KnockdownCauser, float, KnockdownForce);
/**
* Damage modifier for a specific damage type.
*/
USTRUCT(BlueprintType)
struct FRAMEWORK_API FDamageModifier
{
GENERATED_BODY()
/** The damage type this modifier applies to. */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FGameplayTag DamageType;
/** Multiplier applied to incoming damage of this type. 0.5 = half damage, 2.0 = double. */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Multiplier = 1.0f;
/** If true, this is a flat reduction (subtract after multiplier). */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bFlatReduction = false;
/** Flat damage reduction amount (only used if bFlatReduction is true). */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float FlatReduction = 0.0f;
};
/**
* BPC_DamageReceptionSystem — Damage Reception & Resistance.
*
* Processes incoming damage: calculates resistance, applies armor/shield modifiers,
* triggers hit reactions (stagger, knockdown), and routes final damage to the
* health system. In C++, the damage pipeline is native-speed vectorized math.
*/
UCLASS(Blueprintable, ClassGroup = (Framework), meta = (BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_DamageReceptionSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_DamageReceptionSystem();
// ========================================================================
// Configuration
// ========================================================================
/** Equipment config for armor/damage modifiers. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Config")
TObjectPtr<UDA_EquipmentConfig> EquipmentConfig;
/** Base damage resistance (0.0 = no resistance, 1.0 = immune). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Combat")
float BaseResistance = 0.0f;
/** Damage multipliers per damage type. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Combat")
TArray<FDamageModifier> DamageModifiers;
// ========================================================================
// Damage Calculation — Hot Path
// ========================================================================
/**
* Calculate and apply damage.
* Full pipeline: raw damage → calculate resistance → apply armor → apply shield → apply health.
* Returns actual damage dealt.
*/
UFUNCTION(BlueprintCallable, Category = "Framework|Combat")
float ApplyDamage(float RawDamage, AActor* DamageCauser, FGameplayTag DamageType,
FVector HitLocation, FVector HitDirection);
/**
* Calculate effective resistance for a damage type.
* Used by UI/preview systems to show expected damage.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Combat")
float CalculateResistance(FGameplayTag DamageType) const;
/**
* Get the damage modifier for a specific damage type.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Combat")
float GetDamageMultiplier(FGameplayTag DamageType) const;
// ========================================================================
// Hit Reaction
// ========================================================================
/** Damage threshold to trigger a stagger reaction. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Combat|Reactions")
float StaggerThreshold = 20.0f;
/** Damage threshold to trigger a knockdown. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Combat|Reactions")
float KnockdownThreshold = 50.0f;
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnDamageReceived OnDamageReceived;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnDamageResisted OnDamageResisted;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnStaggered OnStaggered;
UPROPERTY(BlueprintAssignable, Category = "Framework|Events")
FOnKnockedDown OnKnockedDown;
protected:
/** Triggers hit reaction based on final damage amount. */
void EvaluateHitReaction(float FinalDamage, AActor* DamageCauser, FVector HitDirection);
/** Cached references to sibling components. */
UPROPERTY()
TObjectPtr<UBPC_HealthSystem> CachedHealthSystem;
UPROPERTY()
TObjectPtr<UBPC_ShieldDefenseSystem> CachedShieldSystem;
UPROPERTY()
TObjectPtr<UBPC_HitReactionSystem> CachedHitReactionSystem;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameplayTagContainer.h"
#include "BPC_HitReactionSystem.generated.h"
UCLASS(Blueprintable, ClassGroup=(Framework), meta=(BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_HitReactionSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_HitReactionSystem();
UFUNCTION(BlueprintCallable, Category = "Framework|Combat")
void PlayHitReaction(float DamageAmount, FVector HitDirection, AActor* DamageCauser);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|HitReaction")
float FlinchThreshold = 5.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|HitReaction")
float RagdollThreshold = 50.0f;
};

View File

@@ -0,0 +1,26 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BPC_ShieldDefenseSystem.generated.h"
UCLASS(Blueprintable, ClassGroup=(Framework), meta=(BlueprintSpawnableComponent))
class FRAMEWORK_API UBPC_ShieldDefenseSystem : public UActorComponent
{
GENERATED_BODY()
public:
UBPC_ShieldDefenseSystem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Shield")
float ShieldHealth = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Shield")
float MaxShieldHealth = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Framework|Shield")
float BlockAngle = 90.0f;
UPROPERTY(BlueprintReadOnly, Category = "Framework|Shield")
bool bShieldBroken = false;
};