UE5.7+ Modular RPG Framework · FAB Marketplace · Designer-first · GAS-centric · Multiplayer-ready
No hardcoded values. Extensive Project Settings. Everything Blueprint-exposed and configurable from Data Assets.
Gameplay Ability System is the foundation of all mechanics. Everything flows through GAS. Never bypass it.
Items and abilities use composable fragment modules. No deep inheritance chains. Data Asset + fragments = any type.
All systems server-authoritative from day one. No TMaps on replicated variables. RPCs for authority.
161 tags drive state, events, and cross-module communication. All tags live in one file. Never create tags elsewhere.
100% of framework code in Plugins/DynamicRPGWorlds/. Source/ contains only sample variants.
// UE base ACharacter (UE) └── AWCCharacterBase (Abstract — state machine, tag system, death, GAS) │ Components: WCCharacterMovementComponent WCInteractionComponent WCGASAttributeInitializer ├── AWCPlayerCharacter — UGameplayCameraComponent (tag-driven rigs), ASC → PlayerState └── AWCAICharacter — owns UWCCombatAbilitySystemComponent directly
// Player — ASC on PlayerState AWCPlayerState → owns UWCCombatAbilitySystemComponent AWCPlayerCharacter::GetAbilitySystemComponent() → routes to PlayerState // AI — ASC on character AWCAICharacter → owns UWCCombatAbilitySystemComponent // Utility resolver UWCCoreLibrary::GetWCAbilitySystemComponent(Actor) Pawn IAbilitySystemInterface → Actor's PlayerState → null
Enhanced Input → WCInputManagerComponent (PlayerController) → Priority-sorted Handler array → WCInputBuffer (timing-sensitive queuing) → WCComboDetector (tree-based pattern matching) → Ability activation via ASC
Same input repeated → different montage sections. Configured on MeleeCombat/RangedCombat fragment. ASC->PlayMontage() only. Never JumpToSection.
Different inputs → navigate combo tree → fire abilities at nodes. UWCComboDefinition Data Asset. Detected by UWCComboDetector in WCInput.
// 4 approved cross-module channels Gameplay Tags → event-driven state/events (tag in WCGameplayTags.h only) Interfaces → type-safe calls across boundaries Subsystems → singleton-pattern services Delegates → Blueprint-accessible callbacks // Planned module example: WCAudioSuite hears combat state WC.State.Gameplay.Combat added to ASC → UWCMusicDirectorComponent registered via RegisterGameplayTagEvent() → transitions to CombatMusicState → NO direct import of WCCombat headers needed
| Class | Layer | Purpose |
|---|---|---|
| AWCCharacterBase | Integration | Abstract base — state machine, tag system, death, GAS |
| AWCPlayerCharacter | Integration | Player — camera component, routes ASC to PlayerState |
| AWCAICharacter | Integration | AI — owns CombatASC directly |
| AWCPlayerState | Integration | Server-authoritative — owns CombatASC + CraftingComponent + new planned components |
| AWCPlayerController | Integration | Has WCInputManagerComponent, DefaultInputSetup asset |
| AWCHUD | Integration | HUD — widget mgmt, crosshair, hit markers + WCMusicDirectorComponent (planned) |
| AWCDummy | Integration | Debug target — Capsule + Mesh + CombatASC. Logs all hits for testing. |
// Components added to AWCPlayerState across phases AWCPlayerState UWCCombatAbilitySystemComponent ← WCGASCore (existing) UWCCraftingComponent ← WCInventory (existing) UWCStoriesComponent ← WCStories (Phase 4) UWCExplorationComponent ← WCRealms (Phase 2) UWCKnowledgeComponent ← WCKnowledgeSuite (Phase 3) // Each component defined in its feature module, CREATED here in DynamicRPGWorlds
UWCMusicDirectorComponent (on AWCHUD) TArray<FWCMusicStateMapping> MusicStateMappings // ordered by priority FWCMusicStateMapping: ActivationTag + MusicState + Priority // Example config Priority 300: WC.State.Gameplay.Combat → CombatMusicState Priority 200: WC.Character.State.Cinematic → CinematicMusicState Priority 100: WC.Realms.Location.Type.Dungeon → DungeonMusicState Default: (no match) → ExplorationMusicState
WCTags::Audio_State_MusicTransitioning WCTags::Audio_Event_MusicStateChanged WCTags::Audio_Event_StingerPlayed WCTags::Audio_Layer_BaseMusicLayer WCTags::Audio_Layer_CombatPercussion WCTags::Audio_Layer_Melody WCTags::Audio_Layer_Ambient WCTags::Audio_Layer_UI
UWCMusicDirectorComponent created on AWCHUD in DynamicRPGWorlds. AWCHUD::SetupAudioDirector() called after ASC resolves.WCDynamicSociety CalendarSubsystem for time-of-day to switch day/night ambient variants.WC.State.Gameplay.Combat → combat start stinger, WC.Combat.Event.Kill → kill stinger, WC.Realms.Event.LocationDiscovered → discovery stinger (cross-module via tags only).
AWCLocationVolume overlap (or UWCViewpointComponent radius scan)
→ UWCDiscoverySubsystem::ReportLocationDiscovered(PlayerController, LocationDef)
→ UWCExplorationComponent::SetLocationDiscovered(LocationTag)
→ GAS event WCTags::Realms_Event_LocationDiscovered on player ASC
→ WCKnowledgeSuite listens → unlocks codex/achievement (cross-module via tag)
WCTags::Realms_Event_LocationDiscovered WCTags::Realms_Event_FastTravelCompleted WCTags::Realms_Event_RealmTransitionStarted WCTags::Realms_Event_RealmTransitionCompleted WCTags::Realms_State_FastTraveling WCTags::Realms_State_Transitioning WCTags::Realms_Location_Type_Town WCTags::Realms_Location_Type_Dungeon WCTags::Realms_Location_Type_Landmark WCTags::Realms_Location_Type_FastTravel
UWCExplorationComponent added to AWCPlayerState constructor in DynamicRPGWorlds.AWCGameModeBase calls UWCLevelStreamingSubsystem for initial realm load.UWCWorldStateSubsystem::CaptureWorldSnapshot() before save, RestoreWorldSnapshot() after load.WCTags::Realms_Event_LocationDiscovered consumed by WCKnowledgeSuite achievement tasks and codex unlock conditions.
UWCKnowledgeSubsystem::InitializePlayerKnowledge(KnowledgeComp)
→ starts UWCAchievementTask instances for incomplete achievements
→ subscribes to AddGameplayEventTagContainerDelegate() for relevant event tags
→ UWCAchievementTask::AddProgress() on matching event
→ CompleteTask() → all tasks done → unlock achievement
→ OnAchievementUnlocked → WCAudioSuite plays stinger
WCTags::Knowledge_Achievement_Unlocked WCTags::Knowledge_Achievement_Progress WCTags::Knowledge_Codex_Unlocked WCTags::Knowledge_Codex_Category_Combat WCTags::Knowledge_Codex_Category_World WCTags::Knowledge_Bestiary_Encountered WCTags::Knowledge_Bestiary_Killed WCTags::Knowledge_Bestiary_FullyRevealed WCTags::Knowledge_Tutorial_Active WCTags::Knowledge_Tutorial_Dismissed WCTags::Knowledge_Achievement_Category_Combat WCTags::Knowledge_Achievement_Category_Exploration
UWCKnowledgeComponent added to AWCPlayerState constructor in DynamicRPGWorlds.UWCKnowledgeSubsystem::InitializePlayerKnowledge() called from AWCPlayerState::BeginPlay().UWCKnowledgeComponent arrays as part of player data.WCTags::Knowledge_Achievement_Unlocked fires achievement stinger.Realms_Event_LocationDiscovered consumed by WCAchievementTask_DiscoverLocation and codex unlock conditions.
UWCQuest (Blueprintable UObject)
TArray<UWCQuestState*> States
TArray<UWCQuestBranch*> Branches
UWCQuestState* CurrentState
EWCQuestCompletion: NotStarted | Started | Succeeded | Failed
└── UWCQuestState — a node in the SM
└── UWCQuestBranch — transition between states, holds tasks
└── UWCQuestTask (abstract Blueprintable, EditInlineNew)
UWCDialogue (Blueprintable UObject)
TArray<UWCDialogueNode_NPC*> RootNodes (entry points)
TArray<FSpeakerSelector> Speakers
└── UWCDialogueNodeBase
TArray<UWCStoryCondition*> Conditions (Instanced — all must pass)
TArray<UWCStoryEvent*> Events (Instanced — fire on reach)
TArray<UWCDialogueNode_NPC*> NPCReplies
TArray<UWCDialogueNode_Player*> PlayerReplies
WCTags::Quest_State_Active / Succeeded / Failed WCTags::Quest_Event_Started / Updated / Succeeded / Failed WCTags::Dialogue_State_Active WCTags::Dialogue_Event_Started / Finished / NodeReached
UWCStoriesComponent added to AWCPlayerState in DynamicRPGWorlds.WCStoriesEditor: custom graph editor for UWCQuest (state nodes + branch edges) and UWCDialogue (NPC/Player node graph).WCTags::Save_Event_SaveStarted WCTags::Save_Event_SaveFinished WCTags::Save_Event_LoadStarted WCTags::Save_Event_LoadFinished WCTags::Save_State_SavingDisabled
UWCSaveSubsystem registers automatically as GameInstanceSubsystem — no explicit wiring needed.AWCGameInstance hooks: OnPreLevelTransition → Save("autosave",0) and OnPostLevelTransition → Load("autosave",0) for seamless world transition saves.UWCSaveSubsystem::SetSavingDisabled(true) called during combat/cutscenes to prevent mid-action saves.WCSaveSystem.Build.cs lists WCStories as a private dep — this means Phase 6 can't ship until Phase 4 (WCStories) is complete.AWCNPCController watches NPC's ASC via RegisterGameplayTagEvent(): WC.State.Gameplay.Combat ADDED → EnterCombatState() → SuspendRoutine() saves checkpoint → WCGASAlly's WCGASAICombatComp takes over (already on NPC) WC.State.Gameplay.Combat REMOVED → ExitCombatState() → ResumeRoutine() restores checkpoint → WCAI routine resumes // AWCNPCController NEVER reimplements combat logic — it yields to the existing stack
UWCRoutineGoal (Abstract, Blueprintable, EditInlineNew) FGameplayTag GoalTag // e.g., WC.AI.Routine.Working float MaxDuration // 0 = until schedule changes BeginGoal(AWCNPCController*) // BlueprintNativeEvent TickGoal(float DeltaTime) // BlueprintNativeEvent EndGoal() // BlueprintNativeEvent IsGoalComplete() // BlueprintNativeEvent
WC.AI.Routine.Sleeping → GoToLocation (bed) + Wait
WC.AI.Routine.Working → GoToLocation (worksite) + Wait
WC.AI.Routine.Patrolling → FollowRoute
WC.AI.Routine.Socializing → Socialize
WC.AI.Routine.Shopping → GoToLocation (market)
WC.AI.Routine.Eating → GoToLocation (tavern) + Wait
WC.AI.Routine.Idle → Wait (wander)
Root → Selector ├── [Decorator: IsInCombat] → Exit (WCGASAlly handles) ├── [Decorator: IsIncapacitated] → WCBTTask_Wait └── Sequence (Routine) ├── WCBTService_UpdateRoutine // re-evaluates schedule each tick ├── WCBTService_UpdatePerception // updates blackboard └── WCBTTask_ExecuteRoutineGoal // runs CurrentGoal
WCTags::AI_State_Idle / InRoutine / Alerted / Fleeing / Suspended WCTags::AI_Routine_Sleeping / Working / Patrolling WCTags::AI_Routine_Socializing / Shopping / Eating / Idle WCTags::AI_Personality_Timid / Aggressive / Friendly / Neutral WCTags::AI_Event_RoutineGoalCompleted / AlertedToPlayer WCTags::AI_Event_CombatEntered / CombatExited
AWCNPCCharacter extends AWCAICharacter (DynamicRPGWorlds) — already in the hierarchy. No new PlayerState component needed.AWCGameModeBase optionally references UWCAISettings::DefaultNPCControllerClass for auto-possessed NPCs.UWCNPCRoutineComponent queries WCDynamicSociety CalendarSubsystem for current time slot and NPCSocialComponent for schedule asset.UWCRoutineGoal_StartDialogue (private dep on WCStories) calls UWCStoriesComponent::BeginDialogue() on nearest player's PlayerState.UWCAISettings::FullAIRadius (5000 units) run reduced RoutineUpdateInterval (5.0s default). Beyond MaxActiveFullAINPCs (20), fallback to minimal state.| Phase | Module | Depends On (Private) | Adds to PlayerState | New Tags |
|---|---|---|---|---|
| 2 | 🎵 WCAudioSuite | WCDynamicSociety, MetasoundEngine | — | 8 (Audio.*) |
| 2 | 🌍 WCRealms | WCUIFramework, LevelSequence | UWCExplorationComponent | 10 (Realms.*) |
| 3 | 📖 WCKnowledgeSuite | WCGASAlly, WCUIFramework | UWCKnowledgeComponent | 16 (Knowledge.*) |
| 4 | 📜 WCStories | WCGASAlly, WCUIFramework | UWCStoriesComponent | 11 (Quest.* Dialogue.*) |
| 4 | 🤖 WCAI | WCGASAlly, WCStories, GameplayStateTree | — | 21 (AI.*) |
| 6 | 💾 WCSaveSystem | WCInventory, WCGASAlly, WCStories, WCDynamicSociety | — | 5 (Save.*) |
// Which modules consume tags from other planned modules WCAudioSuite → listens: WC.Realms.Event.LocationDiscovered (discovery stinger) → listens: WC.Knowledge.Achievement_Unlocked (achievement stinger) → listens: WC.State.Gameplay.Combat (combat music — existing) WCKnowledgeSuite → listens: WC.Realms.Event.LocationDiscovered (codex/achievement unlock) → listens: WC.Combat.Event.Kill (kill-count achievements — existing) WCStories → condition checks: WCInventory, WCGASAlly, WCDynamicSociety (via interfaces) → task tasks: listens to WCInventory::OnItemAdded, WC.Combat.Event.Kill WCAI → watches: WC.State.Gameplay.Combat on NPC ASC → suspend/resume → reads: WCDynamicSociety CalendarSubsystem + NPCSocialComponent (schedule) → private: WCStories for WCRoutineGoal_StartDialogue WCSaveSystem → calls serialization on: WCGASCore, WCInventory, WCGASAlly, WCStories, WCDynamicSociety, WCRealms, WCKnowledgeSuite // All ~75 new planned tags still go to WCGASCore/Public/WCGameplayTags.h
WCGASCore/Public/WCGameplayTags.h in the WCTags namespace. Exception: UE_DEFINE_GAMEPLAY_TAG_STATIC for handler-local tags.TArray or FastArray. This applies even to variables that aren't yet replicated — plan for it. Affects UWCSaveGame, UWCExplorationComponent, UWCKnowledgeComponent, etc.Plugins/DynamicRPGWorlds/. Source/ is for sample variants only. Every new module gets a folder in Plugins/DynamicRPGWorlds/Source/WC[Name]/.ASC->PlayMontage() only. JumpToSection() and Montage_SetNextSection() are local-only — never replicated to simulated proxy clients.
UWCItemDefinition (Primary DA) + TArray<UWCItemFragment*> = any item type (no C++ subclassing)
UWCAbilityDefinition (Primary DA) + TArray<UWCAbilityFragment*> = any ability type
// Planned modules follow same pattern:
UWCAchievementDefinition + TArray<UWCAchievementTask*> = any achievement type
UWCQuest + TArray<UWCQuestState*> + TArray<UWCQuestTask*> = any quest structure
// Cross-module channels (in priority order) Gameplay Tags → event-driven state/events (preferred) Interfaces → type-safe calls (IWCSaveableInterface, IWCWorldStateSaveable) Subsystems → singleton services (UWCDiscoverySubsystem, UWCAudioSubsystem) Delegates → Blueprint callbacks // NEVER: feature module #includes another feature module without Build.cs declaration // NEVER: planned module includes DynamicRPGWorlds headers
// Component defined in feature module, CREATED in DynamicRPGWorlds
WCRealms defines UWCExplorationComponent
→ DynamicRPGWorlds AWCPlayerState constructor creates it
WCKnowledgeSuite defines UWCKnowledgeComponent
→ DynamicRPGWorlds AWCPlayerState constructor creates it
WCStories defines UWCStoriesComponent
→ DynamicRPGWorlds AWCPlayerState constructor creates it
WCAudioSuite defines UWCMusicDirectorComponent
→ DynamicRPGWorlds AWCHUD::BeginPlay creates it (after ASC resolves)
| Type | Pattern |
|---|---|
| Components | WC[Module]Component |
| Data Assets | WC[Type]Definition / Data |
| Settings | WC[Module]Settings |
| Subsystems | WC[Purpose]Subsystem |
| Interfaces | IWC[Purpose]Interface |
| Libraries | WC[Module]Library |
| Item Fragments | WCFragment_[Name] |
| Ability Fragments | WCAbilityFragment_[Name] |
| Input Handlers | WCInputHandler_[Name] |
WC.[Module].[Category].[Subcategory] // Existing WC.Combat.State.Aiming WC.Combat.Event.HitConfirmed WC.State.Gameplay.Combat WC.State.Gameplay.Exploring // Planned (50 new tags across 5 modules) WC.Audio.State.MusicTransitioning WC.Realms.Event.LocationDiscovered WC.Knowledge.Achievement.Unlocked WC.Quest.State.Active WC.Dialogue.State.Active WC.Save.Event.SaveStarted
| File | Path |
|---|---|
| Master Reference | Plugins/DynamicRPGWorlds/CLAUDE.md |
| All Gameplay Tags | WCGASCore/Public/WCGameplayTags.h |
| Tag Definitions | WCGASCore/Private/WCGameplayTags.cpp |
| Base Ability | WCGASCore/Public/GAS/WCGameplayAbility.h |
| Base Item Fragment | WCInventory/Public/Fragments/WCItemFragment.h |
| Base Ability Fragment | WCGASCore/Public/GAS/WCAbilityFragment.h |
| Combat Ability Base | WCCombat/Public/Abilities/WCGameplayAbility_Combat.h |
| Player Character | DynamicRPGWorlds/Public/Character/WCPlayerCharacter.h |
| Build.cs template | WCInventory/WCInventory.Build.cs |
| Project Root | U:\UE Projects\Diversion\WC_RPG_Worlds |
showdebug abilitysystem // GAS state, attributes, effects showdebug gameplaytags // Active tags on actors showdebug input // Enhanced Input debug AWCDummy // Drop in level — logs all GAS hits UWCAbilityFragment_Debug // Add to any ability // Per-module debug flags in Project Settings: UWCInputSettings → bEnableInputLogging UWCInteractionSettings → bEnableDebugVisualization UWCCombatSettings → bEnableTraceDebug // Add bDebugX to each new module's Settings class
"<UE5_ROOT>/Engine/Build/BatchFiles/Build.bat"
WC_RPG_Worlds Win64 Development
-Project="U:/UE Projects/Diversion/
WC_RPG_Worlds/WC_RPG_Worlds.uproject"
"<UE5_ROOT>/Engine/Build/BatchFiles/
GenerateProjectFiles.bat"
"U:/UE Projects/Diversion/
WC_RPG_Worlds/WC_RPG_Worlds.uproject"
// Build config:
IWYU: ON · Warnings-as-errors: WCGASCore
Unity builds: OFF (complex modules)
Plugins/DynamicRPGWorlds/Source/WC[Name]/Public/ and Private/. Follow the existing layout pattern from WCInteraction or WCInput.WCGASCore. NEVER add DynamicRPGWorlds. Set PCHUsage = ExplicitOrSharedPCHs. Copy the WCInventory.Build.cs as your template."Type": "Runtime" and "LoadingPhase": "Default".UWC[Name]Settings in Public/Settings/. Register under Project Settings > Dynamic RPG Worlds > [Name]. Always include bDebug[Name] flag.WCTags namespace, format WC.[ModuleName].[Category].[Name]. All 50 planned tags from the 5 specs must go here.UFUNCTION(BlueprintCallable).AWCPlayerState constructor. HUD components go in AWCHUD::BeginPlay(). Never let the feature module create things on framework classes.WCStoriesEditor for the graph editor. WCRealms may want WCRealmsEditor for volume visualization. Register in .uplugin with "Type": "Editor".Build in this sequence so you always have something compilable:
1. WCAudioSuite.h + WCAudioSuite.Build.cs + WCAudioSettings.h // module compiles 2. WCMusicTypes.h + WCMusicStateDefinition.h + WCMusicLayerDefinition.h // data assets 3. UWCAudioSubsystem.h/.cpp // world subsystem, stub implementations 4. UWCMusicDirectorComponent.h/.cpp // tag watching, calls subsystem 5. Wire into DynamicRPGWorlds: AWCHUD creates MusicDirectorComponent // playable 6. UWCAmbientZoneDefinition + AWCAmbientZoneVolume // ambient zones 7. UWCCombatAudioDefinition + UWCStingerDefinition // combat audio 8. WCDynamicSociety calendar integration // day/night ambient switch