// Copyright ExByte Studios. Item Interaction Ecosystem. #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "ItemDefinitionRow.h" #include "ItemAuxiliaryRows.h" #include "ItemContentRows.h" #include "ItemInstanceData.h" #include "ItemDatabaseSubsystem.generated.h" /** One problem found by ValidateData (section 28). */ USTRUCT(BlueprintType) struct FItemValidationIssue { GENERATED_BODY() UPROPERTY(BlueprintReadOnly, Category = "Validation") FName ItemId; UPROPERTY(BlueprintReadOnly, Category = "Validation") FString Message; UPROPERTY(BlueprintReadOnly, Category = "Validation") bool bIsError = true; }; /** * Authoritative, read-only access point for all static item data (section 35 stage 1). * * Loads the configured data tables once, flattens them into fast lookup maps and * exposes definition/property/feature/interaction queries plus redirect resolution * and a data validator. Lives on the GameInstance so it is available on both * server and clients; the tables themselves are never replicated (section 32). */ UCLASS() class ITEMDATABASE_API UItemDatabaseSubsystem : public UGameInstanceSubsystem { GENERATED_BODY() public: //~ USubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; /** Convenience accessor from any world context. */ static UItemDatabaseSubsystem* Get(const UObject* WorldContext); /** (Re)load every configured table and rebuild the lookup caches. */ void RebuildCache(); // --- Definition lookup --- /** Returns the definition for an item (after redirects), or null if unknown. C++ fast path. */ const FItemDefinitionRow* GetItemDefinition(FName ItemId) const; UFUNCTION(BlueprintCallable, Category = "Item|Database") bool FindItemDefinition(FName ItemId, FItemDefinitionRow& OutDefinition) const; UFUNCTION(BlueprintPure, Category = "Item|Database") bool HasItem(FName ItemId) const; UFUNCTION(BlueprintPure, Category = "Item|Database") void GetAllItemIds(TArray& OutItemIds) const; /** Applies the redirect chain; returns the input unchanged if no redirect exists. */ UFUNCTION(BlueprintPure, Category = "Item|Database") FName ResolveRedirect(FName ItemId) const; // --- Instances --- /** * Builds a fresh instance of ItemId with a new GUID and its default runtime * properties applied. The single correct way to mint an item (server-side). */ UFUNCTION(BlueprintCallable, Category = "Item|Database") FItemInstanceData MakeItemInstance(FName ItemId, int32 Quantity = 1) const; /** Writes the item's default runtime properties into Out (does not clear first). */ void ApplyDefaultProperties(FName ItemId, FItemRuntimeProperties& Out) const; /** Adds only default properties that are missing (save migration, section 26.2). */ void ApplyMissingDefaultProperties(FName ItemId, FItemRuntimeProperties& Out) const; /** Current schema version stamped onto fresh/migrated instances. */ static constexpr int32 CurrentItemDataVersion = 1; /** Applies redirects + version migration to a loaded instance (section 26). */ UFUNCTION(BlueprintCallable, Category = "Item|Database") void MigrateLoadedItem(UPARAM(ref) FItemInstanceData& Item) const; UFUNCTION(BlueprintPure, Category = "Item|Database") int32 GetMaxStack(FName ItemId) const; // --- Auxiliary data --- void GetPropertyRows(FName ItemId, TArray& Out) const; void GetFeatureRows(FName ItemId, TArray& Out) const; /** Feature rows for one specific feature tag (e.g. all Rotting params of raw_meat). */ void GetFeatureRows(FName ItemId, const FGameplayTag& Feature, TArray& Out) const; void GetInteractionRows(FName ItemId, TArray& Out) const; /** Per-property replication policy default (section 22), or None if unspecified. */ EItemPropertyReplicationPolicy GetPropertyPolicy(FName ItemId, const FGameplayTag& PropertyTag) const; /** Property tags on an item that are flagged ServerOnly (never sent to clients). */ void GetServerOnlyProperties(FName ItemId, TArray& Out) const; // --- Containers / world slots / loot / recipes (section 6) --- UFUNCTION(BlueprintPure, Category = "Item|Database") bool FindContainerDef(FGameplayTag ContainerTag, FContainerDefRow& OutDef) const; UFUNCTION(BlueprintPure, Category = "Item|Database") bool FindWorldSlotDef(FGameplayTag SlotTag, FWorldSlotDefRow& OutDef) const; UFUNCTION(BlueprintPure, Category = "Item|Database") bool FindRecipe(FName RecipeId, FCraftingRecipeRow& OutRecipe) const; UFUNCTION(BlueprintPure, Category = "Item|Database") void GetAllRecipes(TArray& OutRecipes) const; void GetLootEntries(FName TableId, TArray& Out) const; /** Rolls a loot table into a list of fresh item instances (server). */ UFUNCTION(BlueprintCallable, Category = "Item|Database") void RollLoot(FName TableId, int32 RollCount, TArray& OutItems) const; // --- Validation (section 28) --- UFUNCTION(BlueprintCallable, Category = "Item|Database") void ValidateData(TArray& OutIssues) const; private: void LoadDefinitions(UDataTable* Table); void LoadProperties(UDataTable* Table); void LoadFeatures(UDataTable* Table); void LoadInteractions(UDataTable* Table); void LoadRedirects(UDataTable* Table); void LoadContainers(UDataTable* Table); void LoadWorldSlots(UDataTable* Table); void LoadLoot(UDataTable* Table); void LoadRecipes(UDataTable* Table); /** ItemId -> definition. */ TMap Definitions; /** ItemId -> default property rows. */ TMap> PropertiesByItem; /** ItemId -> feature parameter rows. */ TMap> FeaturesByItem; /** ItemId -> interaction rows. */ TMap> InteractionsByItem; /** Old -> new id. */ TMap Redirects; TMap ContainerDefs; TMap WorldSlotDefs; TMap> LootByTable; TMap RecipesById; bool bCacheBuilt = false; };