WCDynamicSociety
Complete beginner-to-expert documentation — Calendar, NPC Social Simulation, and Crime & Justice for Unreal Engine 5.7+
📖 Overview
WCDynamicSociety is a Layer 2 feature module in the Wonderscape Creations RPG Framework that brings your game world's social fabric to life. It is a self-contained Unreal Engine 5.7+ plugin with minimal engine-only dependencies, making it one of the most portable modules in the framework.
The plugin is built around three fully independent yet deeply integrated systems:
Calendar System
Fully configurable in-game time with time periods, seasons, celestial cycles, calendar events, time skip, and save/load support.
Social Simulation
Deep NPC relationship engine with factions, propagation, memories, moods, personality, regional reputation, social clusters, and lifecycle management.
Crime & Justice
Full criminal economy — crime recording, witness detection, guard response, bounties, sentencing, pardons, and social consequence propagation.
Design Philosophy
- Designer-First: Every system is configured through Blueprint-friendly Data Assets and Project Settings — no hardcoded values, no required C++ knowledge to get started.
- Multiplayer-Ready: All three subsystems are
UGameInstanceSubsystemwith server-authoritative operations, delta-compressed replication, and range-based filtering. - Performance at Scale: Frame-budgeted propagation queues, spatial dormancy, NPC priority tiers, and connection hard limits allow hundreds of social NPCs without performance spikes.
- Phased Feature Depth: The social system has 18+ implementation phases you can enable incrementally — start simple, add complexity as your game grows.
- Fully Blueprint Exposed: Every public function, delegate, and data struct is
UFUNCTION(BlueprintCallable)orBlueprintAssignable, so your designers never need to open C++.
WCDynamicSociety depends only on Core, CoreUObject, GameplayTags, Engine, and DeveloperSettings. It does NOT require WCGASCore or any other WC module, making it usable as a standalone social layer in any project.✅ Prerequisites
Engine & Platform Requirements
| Requirement | Value | Notes |
|---|---|---|
| Unreal Engine | 5.7.0+ | Plugin targets UE 5.7 APIs exclusively. Earlier versions are NOT supported. |
| Platform | Win64 | Mac, Linux, Android, iOS, TVOS are on the platform deny list in the .uplugin. |
| C++ Compiler | MSVC (VS 2022) | Required to compile the plugin modules. Blueprint-only projects must be converted first. |
| Visual Studio | 2022 | Install the "Game development with C++" workload. |
| Project Type | C++ Project | Blueprint-only projects must add a C++ class first to generate the solution. |
Module Dependencies
The WCDynamicSociety.Build.cs declares the following dependencies. These are all built-in Unreal Engine modules — you do not need to install anything extra:
| Module | Visibility | Purpose |
|---|---|---|
Core | Public | Fundamental Unreal types |
GameplayTags | Public | Gameplay tag containers and queries |
CoreUObject | Public | UObject reflection system |
Engine | Private | Actor, component, subsystem base classes |
Slate / SlateCore | Private | Editor UI (editor module only) |
DeveloperSettings | Private | Project Settings panel integration |
NetCore | Private | Networking and replication support |
AIModule | Private | AI perception hooks for witness detection |
Recommended Background Knowledge
You don't need to know everything before starting. The systems are designed for all skill levels. However, the following knowledge will help you unlock the full power of the plugin:
| Topic | Why It Helps | Beginner Needed? |
|---|---|---|
| Gameplay Tags | Every crime, faction, region, time period, and event is identified by a Gameplay Tag. | Yes — learn the basics |
| Data Assets | All configuration is done through Blueprint Data Assets. Understanding how to create and reference them is essential. | Yes — essential |
| Actor Components | UNPCSocialComponent and UPlayerSocialComponent are Actor Components you attach to your pawns. | Yes — essential |
| Game Instance Subsystems | All three core systems are Game Instance Subsystems — they persist across level changes. | Helpful |
| Replication / Multiplayer | Understanding server/client architecture helps configure the multiplayer settings correctly. | Only for multiplayer projects |
| Blueprint Async Nodes | Async Actions let you react to social events without polling every frame. | Helpful for event-driven design |
🔧 Installation
-
Ensure Your Project Has C++ Support
If you have a Blueprint-only project, go to Tools → New C++ Class in the Unreal Editor, create any basic class (e.g.,
Actor), and click Create Class. This converts your project to C++ and generates the requiredSourcefolder. -
Copy the Plugin to Your Project
Place the
WCDynamicSocietyfolder inside your project'sPluginsdirectory:YourProject/ └── Plugins/ └── WCDynamicSociety/ ← place the entire folder here ├── WCDynamicSociety.uplugin ├── Source/ └── Documentation/If a
Pluginsfolder doesn't exist at your project root, create it manually. -
Enable the Plugin in the Editor
Open your project in Unreal Editor. Go to Edit → Plugins, search for "WCDynamicSociety", check the Enabled checkbox, and click Restart Now when prompted.
-
Regenerate Visual Studio Project Files
Right-click your
.uprojectfile in Windows Explorer and select "Generate Visual Studio project files". This adds the plugin's source to your Visual Studio solution. -
Build the Project
Open the
.slnfile in Visual Studio 2022. Select Development Editor configuration and build (Ctrl+Shift+B). Alternatively, launch the editor directly from the.uprojectwhich will trigger an automatic compile.The first compile will take several minutes. Subsequent incremental builds are much faster. -
Verify the Installation
After the editor launches successfully:
- Go to Edit → Project Settings and look for three new entries under Plugins: WC Dynamic Society, WC Dynamic Society Calendar, and WC Crime & Justice.
- In the Content Browser, enable Settings → Show Plugin Content to confirm content folder is accessible.
- Open the Output Log — you should see no red errors related to WCDynamicSociety on startup.
🚀 Quick Start Guide
Follow this guide to get a working Calendar, a social NPC, and basic crime tracking in your level in under 20 minutes.
Part 1 — Set Up the Calendar
-
Create Calendar Settings Data Asset
In the Content Browser, right-click and select Miscellaneous → Data Asset. Search for
CalendarSettingsas the class. Name itDA_Calendar_Settings. Open it and configure:- Hours Per Day: 24
- Minutes Per Hour: 60
- Real Seconds Per Game Hour: 120 (1 game hour = 2 real seconds)
-
Create Year Definition Data Asset
Right-click → Data Asset → YearDefinition. Name it
DA_Calendar_Year. Add your months in the Months array, each with a name, gameplay tag, and day count. -
Create Week Definition Data Asset
Right-click → Data Asset → WeekDefinition. Name it
DA_Calendar_Week. Add your days of the week, each with a name and gameplay tag. -
Initialize the Calendar in Your Game Mode
In your Game Mode's Begin Play Blueprint event:
// Get the Calendar Subsystem UCalendarSubsystem* Calendar = GetGameInstance()->GetSubsystem<UCalendarSubsystem>(); // Initialize with your data assets Calendar->InitializeCalendar(CalendarSettings, YearDef, WeekDef); // Set starting time (Year 1, Month 1, Day 1, Hour 8, Minute 0) FGameDateTime StartTime; StartTime.Year = 1; StartTime.Month = 1; StartTime.Day = 1; StartTime.Hour = 8; StartTime.Minute = 0; Calendar->SetCurrentDateTime(StartTime);The calendar does NOT auto-tick. You must callTickCalendar(DeltaTime)every frame from your Game Mode or Player Controller'sTickfunction. Without this, time will not advance. -
Call TickCalendar Every Frame
In your Game Mode Blueprint, add a Tick event and connect it to:
GetGameInstance()->GetSubsystem<UCalendarSubsystem>()->TickCalendar(DeltaTime);
Part 2 — Create a Social NPC
-
Create an NPC Social Data Asset
Right-click → Data Asset → NPCSocialDataAsset. Name it
DA_NPC_Blacksmith. Set:- Display Name: "Aldric the Blacksmith"
- Social Tier: A gameplay tag like
Society.Tier.Commoner - Relationship Priority: Medium
-
Add UNPCSocialComponent to Your NPC
Open your NPC Blueprint. In the Components panel, click Add Component and search for
NPCSocialComponent. Assign theDA_NPC_Blacksmithdata asset to its Social Data Asset property.The component automatically registers with the
UDynamicSocietySubsystemonBeginPlayand unregisters onEndPlay. -
Query the NPC's Relationship in Blueprint
From any Blueprint, you can check how the NPC feels about the player:
// On the NPC's Social Component: int32 Score = NPCSocialComponent->GetRelationshipWithPlayer(); // Score: -100 (hatred) to +100 (devoted) // Get a human-readable label: FText Label = Subsystem->GetSentimentLabel(Score); // e.g., "Friendly", "Neutral", "Hostile" -
Fire a Relationship Event
When the player does something good, call
ProcessRelationshipEventon the subsystem. First, create aRelationshipRulesDataAssetthat defines event tags and their impact values. Then:// In Blueprint or C++: FRelationshipEvent Event; Event.EventTag = FGameplayTag::RequestGameplayTag("Society.Event.HelpedWithWork"); Event.SourceID = PlayerSocialComponent->PlayerSocialID; Event.TargetID = NPCSocialComponent->GetNPCID(); Subsystem->ProcessRelationshipEvent(Event);
Part 3 — Record a Crime
-
Create a Crime Data Asset
Right-click → Data Asset → CrimeDataAsset. Name it
DA_CrimeTypes. Add your crime definitions, e.g.:- Tag:
Crime.Theft.Pickpocket, Type: Theft, Severity: Minor, BaseBounty: 50 - Tag:
Crime.Murder.Civilian, Type: Murder, Severity: Capital, BaseBounty: 1000
- Tag:
-
Register the Crime Asset in Project Settings
Go to Edit → Project Settings → Plugins → WC Crime & Justice and add your
DA_CrimeTypesasset to the Crime Data Assets array. -
Record a Crime When It Happens
UCrimeSubsystem* CrimeSys = GetGameInstance()->GetSubsystem<UCrimeSubsystem>(); FCrimeRecord Record; Record.CrimeTag = FGameplayTag::RequestGameplayTag("Crime.Theft.Pickpocket"); Record.CriminalID = PlayerSocialID; Record.VictimID = NPCSocialComponent->GetNPCID(); Record.Location = Player->GetActorLocation(); CrimeSys->RecordCrime(Record);
⚙️ Project Settings Reference
All settings live under Edit → Project Settings → Plugins. There are three settings panels, one per system.
WC Dynamic Society (UDynamicSocietySettings)
Relationships — Rules
| Property | Default | Description |
|---|---|---|
DefaultRulesAsset | None | The global RelationshipRulesDataAsset defining all event tags and their relationship impact values. Auto-loaded on subsystem init. |
CalculationProfile | None | An ImpactCalculationProfile asset defining how base impact, alignment, factions, and archetypes mathematically interact. Leave null for default hardcoded behavior. |
Relationships — Score Range
| Property | Default | Description |
|---|---|---|
MinRelationshipScore | -100 | Absolute minimum relationship score (maximum hatred). |
MaxRelationshipScore | +100 | Absolute maximum relationship score (maximum devotion). |
SentimentThresholds | 7 entries | Array of FReputationRank structs mapping score ranges to display labels (e.g., "Friendly", "Neutral", "Hostile"). Used in UI and dialogue selection. |
Social Systems — Tiers
| Property | Default | Description |
|---|---|---|
SocialTierConfig | None | A SocialTierConfigDataAsset defining 1–12 social tiers (hierarchy levels, bounty multipliers, reputation multipliers, event filtering thresholds). |
Factions
| Property | Default | Description |
|---|---|---|
Factions | Empty | Array of FactionDataAsset references. All factions in this array are registered automatically on subsystem init. |
GlobalBountyFactionID | Special GUID | Faction ID used when a crime victim has no faction membership. Allows crime system to work without factions. Set to all-zeros to disable. |
DefaultRegionalReputationCap | 1000 | Maximum regional reputation score. 10x the individual relationship max by default (harder to build regional fame). |
DefaultRegionalReputationFloor | -1000 | Minimum regional reputation score. |
RegionalSentimentThresholds | 7 entries | Labels for regional reputation levels (e.g., "Legendary", "Famous", "Known", "Neutral"). Uses the -1000 to +1000 scale, NOT the individual relationship scale. |
Regional Reputation
| Property | Default | Description |
|---|---|---|
Regions | Empty | Array of RegionalReputationDataAsset references. Each region defines connections to other regions, faction presence, and reputation caps. Auto-registered on init. |
Performance — Connections
| Property | Default | Description |
|---|---|---|
ConnectionSoftLimit | 12 | Logs a warning when an NPC exceeds this many personal connections. Does not block creation. |
ConnectionHardLimit | 20 | Rejects new connections beyond this limit. Prevents runaway memory growth. |
MaxEventsPerNPC | 20 | Maximum relationship history events stored per NPC. Oldest are removed when exceeded. |
MaxMemoryCapacity | 1000 | Maximum contested memories (Phase 6) stored system-wide. Non-permanent memories purged when exceeded. |
Propagation
| Property | Default | Description |
|---|---|---|
MinimumImpactThreshold | 3 | Propagation stops on a branch when the cascading impact drops below this absolute value. |
MinimumConnectionWeight | 0.1 | Connections weaker than this fraction are skipped during propagation (0.0–1.0). |
MaxAffectedNPCs | 100 | Maximum NPCs whose relationships are modified per propagation event. |
MaxVisitedNPCs | 500 | Hard visited-node limit per propagation pass. Must be greater than MaxAffectedNPCs. |
bPropagateToRivals | true | If true, events propagate to rivals with inverted impact (helping someone's rival makes them dislike you). If false, rivals are skipped. |
Performance — Frame Budget
| Property | Default | Description |
|---|---|---|
FrameBudgetMs | 0.5ms | Maximum milliseconds per frame spent processing the propagation queue. Raise for faster propagation, lower for smoother frame rate. |
QueueSizeWarningThreshold | 5000 | Queue size that triggers a performance warning in the log. |
StaleUpdateTimeoutSeconds | 60.0s | Time before queued updates are flagged as stale in the log. |
Multiplayer
| Property | Default | Description |
|---|---|---|
DefaultReplicationRange | 0 (disabled) | Distance (cm) within which NPC relationships are replicated to clients. 0 = replicate all. Set to ~500000 (5km) for open-world optimization. |
RelationshipUpdateThrottle | 0.0s | Minimum seconds between relationship update broadcasts to clients. 0 = immediate. |
MaxRelationshipChangePerRPC | 200 | Anti-cheat: maximum relationship change allowed in a single client RPC. |
bReplicateNPCToNPCRelationships | false | If true, clients can query NPC-to-NPC relationship data. More immersive but costs bandwidth. |
bEnableDeltaCompression | true | Only send changed relationships to clients (recommended). |
Social Systems — Advanced
| Property | Default | Description |
|---|---|---|
SpatialRelevanceConfig | None | Controls distance-based NPC dormancy and processing priority. Recommended for open-world games with 100+ NPCs. |
TemporaryMoodConfig | None | Configuration for temporary emotional state modifiers. Recommended for narrative-heavy games. |
RelationshipDynamicsConfig | None | Defines emotional inertia, velocity tracking, and stability thresholds. |
ForgivenessConfig | None | Controls apology effectiveness, redemption quest mechanics, and witness-based forgiveness. |
AlignmentReactionAsset | None | Maps alignment + moral tag combinations to approval modifiers. |
bAutoGenerateFamilyClusters | true | Automatically creates family social clusters from Family-type connections. Disable only if you need manual cluster control. |
SocialClusterDefinitions | Empty | Pre-defined cluster assets (gangs, squads, families) auto-registered on init. |
Lifecycle — Cleanup
| Property | Default | Description |
|---|---|---|
DeceasedPurgeThresholdHours | 24h | In-game hours after death before a deceased NPC becomes eligible for system cleanup. Allows resurrection mechanics. |
DormantPurgeThresholdHours | 48h | In-game hours of dormancy before a dormant NPC becomes eligible for cleanup. |
InteractionRecencyThresholdHours | 72h | Both dormancy AND recency thresholds must be exceeded before an NPC is purged. |
WC Dynamic Society Calendar (UCalendarProjectSettings)
Located at Project Settings → Plugins → WC Dynamic Society Calendar.
| Property | Description |
|---|---|
| Default Calendar asset | The default CalendarSettings data asset to load automatically. |
| Debug Flags | Enable verbose calendar logging per subsystem feature (time transitions, event triggers, celestial changes). |
WC Crime & Justice (UCrimeJusticeSettings)
Located at Project Settings → Plugins → WC Crime & Justice.
| Property | Description |
|---|---|
| Crime Data Assets | Array of CrimeDataAsset references — all crime definitions auto-registered on init. |
| Justice Data Asset | A JusticeDataAsset defining sentencing rules, bounty thresholds, and pardon conditions. |
| Pardon Data Asset | A PardonDataAsset defining time-based forgiveness, community service, and pardon conditions. |
| Propagation Rules | A CrimePropagationRulesDataAsset defining how crime rumors spread between NPCs. |
| Vigilante Rules | A VigilanteReactionRulesDataAsset defining which NPCs react to crime as vigilantes. |
| Arrest Bounty Threshold | Bounty amount that triggers guard arrest attempts (default: 500). |
| Kill-On-Sight Threshold | Bounty amount that triggers kill-on-sight (default: 2000). |
| Max Bounty Per Faction | Hard cap on bounty accumulation per faction to prevent infinite escalation. |
🏗️ System Architecture
WCDynamicSociety contains two Unreal plugin modules and three core Game Instance Subsystems. Understanding this structure is key to integrating the plugin correctly.
The Three Subsystems
| Subsystem | Type | Purpose |
|---|---|---|
UCalendarSubsystem | UGameInstanceSubsystem | Manages all in-game time — progression, time periods, seasons, celestial cycles, calendar events, and time skip. Must be manually ticked. |
UDynamicSocietySubsystem | UGameInstanceSubsystem + FTickableGameObject | Central hub for all NPC relationships, social propagation, factions, regional reputation, memories, moods, lifecycle, and clusters. Self-ticking via FTickableGameObject. |
UCrimeSubsystem | UGameInstanceSubsystem | Tracks all crimes, witnesses, bounties, guard responses, sentencing, and pardons. Integrates with UDynamicSocietySubsystem for social consequences. |
UGameInstanceSubsystem types. Access them with:GetWorld()->GetGameInstance()->GetSubsystem<UCalendarSubsystem>()GetWorld()->GetGameInstance()->GetSubsystem<UDynamicSocietySubsystem>()GetWorld()->GetGameInstance()->GetSubsystem<UCrimeSubsystem>()Do NOT try to access them as
UWorldSubsystem — they are Game Instance scoped and persist across level changes.Editor Module
The WCDynamicSocietyEditor module (Editor type, loads at Default phase) adds asset factory classes to the Unreal Editor's content creation menus, making it easy to right-click and create all WCDynamicSociety data asset types without knowing their class names.
🔩 Subsystems In Detail
UCalendarSubsystem
Manages the game's flow of time. Initialized explicitly by you with data assets, and advanced by explicit TickCalendar calls. Does not start running on its own.
Key characteristics:
- Persists across level changes (Game Instance scope).
- Replication-aware — current date/time and pause state are replicated to clients via Multicast RPCs.
- Broadcasts strongly-typed delegates for every time transition: minute, hour, day, time period, season, celestial phase, event start/end, and time skip.
- Provides
SaveCalendarState()/LoadCalendarState()for save game integration. - Features a
GetSmoothedTimeOfDay()output in UDS format (0–2400) to eliminate jittery sun/moon movement when connected to a sky system like Ultra Dynamic Sky.
UDynamicSocietySubsystem
The central social engine. Self-ticking via FTickableGameObject — you don't need to call a Tick function, but you do need to initialize it with data assets registered through Project Settings or manual RegisterXxx calls.
Key characteristics:
- Maintains a social graph of all registered NPCs and their bilateral relationships.
- Frame-budgeted propagation queue (
FrameBudgetMs) ensures social ripple effects don't spike your frame time. - Priority-tier-based dormancy: Critical NPCs always process; Negligible NPCs go dormant immediately when far from the player.
- Broadcasts 10+ delegate types for relationship changes, threshold crossings, propagation completion, NPC registration, death, resurrection, and lifecycle state changes.
- Exposes full save/load serialization via
FSocietySaveData.
UCrimeSubsystem
Manages the criminal justice system. Integrates with UDynamicSocietySubsystem for social consequences of crimes (reputation loss, faction standing changes).
Key characteristics:
- Fully quest-system-aware via
IQuestSystemInterface— integrates with any quest system that implements the interface. - Supports crime propagation rules for rumor spreading between NPCs.
- Provides time-based pardon decay, community service pardons, and witness-based forgiveness.
- Exposes
FOnCrimeReported,FOnSentenceExecuted, andFOnCrimeDataLoadeddelegates.
🧩 Components
UNPCSocialComponent
Add this to any Actor that should participate in the social system as an NPC. It is a standard UActorComponent with BlueprintSpawnableComponent metadata, so it appears in Unreal's Add Component list in Blueprint editors.
What it does:
- Registers the NPC with
UDynamicSocietySubsystemonBeginPlay(automatically). - Unregisters on
EndPlay(automatically). - Loads identity data from the assigned
UNPCSocialDataAsset: display name, social tier, relationship priority, and pre-defined connections. - Provides convenient Blueprint-callable relationship query functions:
GetRelationshipWithPlayer(),GetRelationshipWith(OtherNPCID),GetRelationshipWithPlayerEx(PlayerComponent). - Supports per-NPC rules override via
PersonalRulesOverride— useful for pacifist NPCs, crime lords, etc.
Key Properties
| Property | Type | Description |
|---|---|---|
SocialDataAsset | UNPCSocialDataAsset* | The data asset defining this NPC's social identity. If set, all configuration is loaded from the asset. If null, set SocialProfile values manually. |
SocialProfile | FNPCSocialProfile | Runtime state. Contains the NPC's GUID, display name, social tier, and priority. Do not edit manually when using a data asset. |
PersonalRulesOverride | URelationshipRulesDataAsset* | If set, this NPC reacts to events using these rules instead of the global default. Ideal for unique character personalities. |
UPlayerSocialComponent
Attach this to your PlayerState (not the Pawn) to enable per-player relationship tracking. This is essential for multiplayer games where different players may have different reputations with the same NPC.
What it does:
- Stores a unique
FGuid PlayerSocialIDidentifying this player in the social system. - Replicates to the owning client only (privacy and bandwidth optimization).
- Registers with
UDynamicSocietySubsystemon serverBeginPlay. - Exposes
FOnPlayerRelationshipChangeddelegate for UI binding. - Supports save/load via
RestorePlayerSocialID(SavedID)— call this beforeBeginPlayto restore a saved social identity.
UPlayerSocialComponent must live on the APlayerState because pawns can be destroyed and respawned. PlayerState persists for the player's entire session. Attaching to a Pawn will cause relationship data loss on death/respawn.UWitnessComponent
Add this to NPC Actors that should be able to observe and report crimes. The component hooks into the AI perception system to detect when crimes occur within its observation radius.
UGuardComponent
Add this to law enforcement NPCs. Works with UCrimeSubsystem to handle bounty pursuit, arrest attempts, and kill-on-sight responses based on the criminal's bounty level and the crime severity.
UCalendarTickComponent
Optional convenience component. Attach to any Actor to automatically call UCalendarSubsystem::TickCalendar(DeltaTime) every frame. Useful as an alternative to manually ticking from the Game Mode. Only one instance of this component should be active at a time.
📦 Data Assets Reference
WCDynamicSociety is configured entirely through Data Assets. Create them by right-clicking in the Content Browser and selecting Miscellaneous → Data Asset, then searching for the class name.
Calendar Data Assets
| Asset Class | Purpose |
|---|---|
UCalendarSettings | Core time parameters: hours per day, minutes per hour, real seconds per in-game hour, sunrise/sunset times, dynamic time scale settings. |
UYearDefinition | Year structure: list of months (each with a name, gameplay tag, and day count). Defines the total days in a year. |
UWeekDefinition | Week structure: list of days of the week, each with a name and gameplay tag. Used for GetCurrentDayOfWeek() queries. |
UTimePeriodDefinition | Time-of-day periods: array of named time ranges (e.g., Morning: 6–11, Afternoon: 11–17, Evening: 17–21, Night: 21–6), each with a gameplay tag. Optional. |
USeasonDefinition | Seasons: array of seasons with names, gameplay tags, and day-of-year ranges. Optional. |
UCelestialDefinition | Celestial bodies: array of bodies (moons, suns, stars) each with a name, gameplay tag, and phase count/cycle length. Optional. |
UCalendarEvent | A calendar event: gameplay tag ID, start/end date ranges, recurrence settings, and chain events to trigger. Optional. |
Social System Data Assets
| Asset Class | Purpose |
|---|---|
UNPCSocialDataAsset | NPC social identity: display name, social tier tag, relationship priority, default connections, and optional persistent NPCID GUID. |
UCharacterSocialDataAsset | Extended NPC data including personality traits, alignment, and faction memberships. |
UPlayerSocialDataAsset | Player social profile: personality preset, alignment tag, starting faction memberships. |
URelationshipRulesDataAsset | Maps event gameplay tags to impact values (e.g., "Society.Event.Murder" → -50). Defines the "rulebook" for all relationship-changing interactions. |
UFactionDataAsset | Faction definition: unique GUID, faction name, allied/hostile factions, membership rules, and reputation tier definitions. |
URegionalReputationDataAsset | Region definition: gameplay tag, connections to neighboring regions (bleeding paths), faction presence, custom reputation caps/floors. |
URegionalEventDefinition | A region-specific event trigger definition for events that affect regional reputation. |
USocialTierConfigDataAsset | 1–12 tier hierarchy with bounty multipliers, event filtering thresholds, and reputation multipliers per tier. |
USocialClusterDefinitionDataAsset | Pre-defined social group (family, gang, squad): list of NPC GUIDs, cluster type, and internal relationship rules. |
UPersonalityPresetDataAsset | Reusable personality template: volatility, empathy, loyalty bias, forgiveness speed. Assign to multiple NPCs for consistent personality archetypes. |
USpatialRelevanceConfigDataAsset | Distance thresholds and dormancy rules for performance optimization based on NPC distance from the player. |
UTemporaryMoodConfigDataAsset | Mood type definitions, stacking rules, decay curves, and maximum mood stacks per relationship. |
URelationshipDynamicsConfigDataAsset | Lock types, velocity tracking window (days), stability thresholds, and emotional inertia multipliers. |
UForgivenessConfigDataAsset | Apology effectiveness curves, redemption quest hooks, witness forgiveness conditions, and forgiveness decay rates. |
UAlignmentReactionDataAsset | Maps alignment + moral tag combinations to relationship impact modifiers. |
UImpactCalculationProfile | Formula configuration: how base impact, alignment modifiers, faction bonuses, and propagation decay combine mathematically. |
Crime & Justice Data Assets
| Asset Class | Purpose |
|---|---|
UCrimeDataAsset | Array of FCrimeDefinition structs — all registerable crime types with their tags, types, severities, and base bounty values. |
UJusticeDataAsset | Sentencing rules: maps crime severity to justice outcomes (warning, fine, jail time, exile, execution), plus bounty thresholds for guard escalation. |
UPardonDataAsset | Pardon conditions: time-based forgiveness rates, community service quest completion, faction-specific pardon rules. |
UCrimePropagationRulesDataAsset | How crime knowledge spreads: rumor decay rates, social distance limits, faction-based filtering. |
UVigilanteReactionRulesDataAsset | Defines which NPC archetypes react as vigilantes to which crime types and severities. |
🗓️ Calendar System — Overview
UCalendarSubsystem
A flexible, fully configurable in-game time system. Supports any fantasy or sci-fi calendar structure — custom months, custom week lengths, custom time periods, multiple moons, and recurring calendar events.
The calendar system consists of layered, optional features. You can use only core time tracking and ignore celestial cycles entirely, or build a full astronomical calendar with moon phases, seasonal festivals, and in-game calendar event chains.
Calendar Feature Layers
- Core Time — Hours, minutes, days with configurable speed (all games)
- Time Periods — Morning, Day, Evening, Night transitions (optional)
- Seasons — Day-of-year based season tracking (optional)
- Celestial Cycles — Moon phases and other celestial body cycles (optional)
- Calendar Events — Recurring or one-off scripted events (optional)
- Event History & Chains — Track occurrence count, trigger chained events (optional)
- Time Skip — Instant or animated time advancement (optional)
- Save/Load — Full state serialization (recommended for all games)
- Debug & Analytics — Performance metrics and validation tools (dev only)
🗓️ Calendar — Setup & Initialization
Initialization Flow
The calendar requires explicit initialization before it tracks time. Call InitializeCalendar() once — typically in your Game Mode's BeginPlay — before you start calling TickCalendar().
// Blueprint node: "Initialize Calendar"
// C++ equivalent:
UCalendarSubsystem* Cal = GetWorld()->GetGameInstance()->GetSubsystem<UCalendarSubsystem>();
Cal->InitializeCalendar(MyCalendarSettings, MyYearDef, MyWeekDef);
// Optional: Add time periods, seasons, celestial bodies
Cal->SetTimePeriodDefinition(MyTimePeriodDef); // Phase 2
Cal->SetSeasonDefinition(MySeasonDef); // Phase 3
Cal->SetCelestialDefinition(MyCelestialDef); // Phase 4
// Set the starting date/time
FGameDateTime Start;
Start.Year = 1000; Start.Month = 3; Start.Day = 15;
Start.Hour = 7; Start.Minute = 30;
Cal->SetCurrentDateTime(Start);
Ticking the Calendar
Without a tick call, time does not advance. Add this to your Game Mode's Tick function:
// In AMyGameMode::Tick(float DeltaTime):
if (UCalendarSubsystem* Cal = GetWorld()->GetGameInstance()->GetSubsystem<UCalendarSubsystem>())
{
Cal->TickCalendar(DeltaTime);
}
// Or in Blueprint: Connect to Event Tick → Get Calendar Subsystem → Tick Calendar (DeltaTime)
TickCalendar every frame without any Blueprint wiring.Validating Your Setup
Call ValidateCalendarSetup() after initialization to get an array of warning/error strings describing any common configuration mistakes:
TArray<FString> Warnings = Cal->ValidateCalendarSetup();
for (const FString& W : Warnings)
{
UE_LOG(LogCalendar, Warning, TEXT("%s"), *W);
}
🗓️ Calendar — Time & Time Scale
Querying Current Time
// Blueprint: "Get Current Date Time"
FGameDateTime Now = Cal->GetCurrentDateTime();
int32 Hour = Now.Hour;
int32 Minute = Now.Minute;
int32 Day = Now.Day;
int32 Month = Now.Month;
int32 Year = Now.Year;
// Convenience helpers
FString MonthName = Cal->GetCurrentMonthName();
FString DayName = Cal->GetCurrentDayOfWeekName();
bool bIsDaytime = Cal->IsCurrentlyDaytime();
bool bIsNight = Cal->IsCurrentlyNighttime();
Controlling Time Speed
The time scale multiplier determines how fast in-game time passes relative to real time. The base speed is set in UCalendarSettings (real seconds per in-game hour). The scale multiplier then applies on top of that:
Cal->SetTimeScale(1.0f); // Normal speed (as defined in CalendarSettings)
Cal->SetTimeScale(2.0f); // Double speed
Cal->SetTimeScale(0.5f); // Half speed
Cal->SetTimeScale(0.0f); // Effectively paused (but use PauseTime() instead)
// Pause / Resume
Cal->PauseTime();
Cal->ResumeTime();
bool bPaused = Cal->IsTimePaused();
Sunrise, Sunset & Day/Night
int32 SunriseHour, SunriseMinute, SunsetHour, SunsetMinute;
Cal->GetSunriseTime(SunriseHour, SunriseMinute);
Cal->GetSunsetTime(SunsetHour, SunsetMinute);
// UDS Integration (Ultra Dynamic Sky compatible output)
float UDSTimeOfDay = Cal->GetSmoothedTimeOfDay(); // Returns 0.0 – 2400.0, smoothed
Time Delegates
Bind to these delegates to react to time changes without polling every frame:
| Delegate | When Fired | Parameters |
|---|---|---|
OnMinuteChanged | Every in-game minute | FGameDateTime NewDateTime |
OnHourChanged | Every in-game hour | FGameDateTime NewDateTime |
OnDayChanged | Every in-game day (midnight rollover) | FGameDateTime NewDateTime |
OnTimePeriodChanged | When time period transitions (e.g., Morning → Afternoon) | FTimePeriod NewPeriod, FTimePeriod OldPeriod |
OnSeasonChanged | When the season changes | FSeason NewSeason, FSeason OldSeason |
OnCelestialPhaseChanged | When any celestial body changes phase | int32 BodyIndex, int32 NewPhase, int32 OldPhase |
OnEventStarted | When a calendar event begins | FActiveEvent Event |
OnEventEnded | When a calendar event ends | FActiveEvent Event |
OnTimeSkipStarted | When a time skip begins | (none) |
OnTimeSkipCompleted | When a time skip finishes | (none) |
OnTimeSkipCancelled | When a cancellable time skip is aborted | (none) |
🗓️ Calendar — Time Periods
Time periods divide the day into named segments (e.g., Dawn, Morning, Afternoon, Evening, Night). Each period has a gameplay tag you can use for dialogue filtering, event scheduling, and gameplay logic.
Setup
Create a UTimePeriodDefinition data asset. Add FTimePeriod entries, each with:
- Name: Display name (e.g., "Morning")
- Tag: Gameplay tag (e.g.,
Calendar.Period.Morning) - StartHour / StartMinute: When this period begins
Then pass it to the subsystem:
Cal->SetTimePeriodDefinition(MyTimePeriodDef);
Querying Time Periods
// Get current period
FTimePeriod CurrentPeriod = Cal->GetCurrentTimePeriod();
FString PeriodName = Cal->GetCurrentPeriodName();
FGameplayTag PeriodTag = Cal->GetCurrentTimePeriodTag();
// Check for a specific period
bool bIsMorning = Cal->HasTimePeriodWithTag(FGameplayTag::RequestGameplayTag("Calendar.Period.Morning"));
// Lookup a period by tag
FTimePeriod Period;
if (Cal->GetTimePeriodByTag(SomeTag, Period))
{
// use Period data
}
🗓️ Calendar — Seasons
Seasons are day-of-year based ranges. Each season has a name, gameplay tag, and determines the current season by comparing the current day-of-year to each season's range.
Setup
Create a USeasonDefinition data asset. Add FSeason entries, each with a name, gameplay tag, and start/end day of year. Then:
Cal->SetSeasonDefinition(MySeasonDef);
Querying Seasons
FSeason Current = Cal->GetCurrentSeason();
FGameplayTag SeasonTag = Cal->GetCurrentSeasonTag();
int32 DayOfYear = Cal->GetDayOfYear(); // 1-indexed
// Lookup
FSeason Spring;
Cal->GetSeasonByTag(FGameplayTag::RequestGameplayTag("Calendar.Season.Spring"), Spring);
🗓️ Calendar — Celestial Cycles
Track moon phases, twin moons, secondary suns, or any other cyclical celestial body. Each body has a configurable cycle length (in game days) and a number of distinct phases.
Setup
Create a UCelestialDefinition data asset. Add FCelestialBody entries. The first body is treated as the "primary moon" for the convenience GetPrimaryMoonPhase() function. Then:
Cal->SetCelestialDefinition(MyCelestialDef);
Querying Celestial Bodies
// Primary moon phase (enum: NewMoon, WaxingCrescent, FirstQuarter, ...)
EMoonPhase MoonPhase = Cal->GetPrimaryMoonPhase();
// Any body by index
int32 Phase = Cal->GetCelestialPhase(0); // Phase index (0 to N-1)
float Progress = Cal->GetCelestialProgress(0); // 0.0 to 1.0 through cycle
// By gameplay tag
int32 Phase2 = Cal->GetCelestialPhaseByTag(SecondMoonTag);
float Prog2 = Cal->GetCelestialProgressByTag(SecondMoonTag);
// Get full body data
FCelestialBody Body;
Cal->GetCelestialBodyByTag(SecondMoonTag, Body);
🗓️ Calendar — Calendar Events
Calendar events are data-asset-driven recurring or one-off date-range events (festivals, eclipses, hunting seasons, etc.). The subsystem checks for event start/end on every time update and fires delegates when events begin or end.
Creating a Calendar Event
Create a UCalendarEvent data asset and configure:
- EventID: Unique gameplay tag (e.g.,
Calendar.Event.HarvestFestival) - Start/End Date Range: Month, day, and time when the event begins and ends each occurrence
- Recurrence: Annual, every N years, or one-time
- Chain Events: Optional list of other event gameplay tags to trigger when this event starts
Registering and Querying Events
// Register
Cal->RegisterEvent(MyHarvestFestivalEvent);
// Check if active
bool bFestival = Cal->IsEventActive(FGameplayTag::RequestGameplayTag("Calendar.Event.HarvestFestival"));
// Get all currently active events
TArray<FActiveEvent> Active = Cal->GetActiveEvents();
// Event history
FEventHistory History = Cal->GetEventHistory(FestivalTag);
int32 TimesFestivalOccurred = Cal->GetEventOccurrenceCount(FestivalTag);
bool bHasOccurredBefore = Cal->HasEventOccurred(FestivalTag);
// Manually trigger (for testing or scripted narrative)
Cal->TriggerEvent(FestivalTag);
GetEventOccurrenceCount() to make NPCs comment differently after an event has happened multiple times — "Another Harvest Festival... I remember when they used to be special."🗓️ Calendar — Time Skip
The time skip system allows you to jump the game clock forward by an amount or to a specific date, with optional animated (accelerated) transition modes for cinematic resting screens.
Skip Modes (ETimeSkipMode)
| Mode | Behavior |
|---|---|
Instant | Time jumps immediately with no visual transition. All events that would have occurred during the skip are fired at once. |
Accelerated | Time runs at very high speed (configurable multiplier) until the target is reached. Events fire normally at their scheduled times. Good for "rest until morning" screen. |
Cinematic | Instant skip but fires OnTimeSkipStarted and OnTimeSkipCompleted for your UI to play a fade/transition animation. |
Performing a Time Skip
// Skip forward by amount
Cal->SkipTime(
/*Days=*/ 1,
/*Hours=*/ 6,
/*Minutes=*/ 0,
ETimeSkipMode::Accelerated
);
// Skip to a specific date
FTimeSkipRequest Request;
Request.TargetDateTime.Year = 1; Request.TargetDateTime.Month = 4; Request.TargetDateTime.Day = 1;
Request.TargetDateTime.Hour = 8; Request.TargetDateTime.Minute = 0;
Request.SkipMode = ETimeSkipMode::Cinematic;
Request.bCanBeCancelled = true;
Cal->SkipToDateTime(Request);
// Skip to the next occurrence of a calendar event
Cal->SkipToNextEvent(FGameplayTag::RequestGameplayTag("Calendar.Event.HarvestFestival"), ETimeSkipMode::Instant);
// Skip to next "Morning" time period
Cal->SkipToTimePeriod("Morning", ETimeSkipMode::Accelerated);
// Cancel if allowed
Cal->CancelTimeSkip();
// Check progress (0.0 - 1.0) for loading bar
float Progress = Cal->GetTimeSkipProgress();
🗓️ Calendar — Save & Load
The calendar fully supports save/load. The FCalendarSaveData struct captures the complete calendar state including current date/time, all event history, and active event states.
// SAVING — call in your save game logic
FCalendarSaveData SaveData = Cal->SaveCalendarState();
// Store SaveData in your USaveGame object or serialize however you prefer
// LOADING — call during game load, before ticking begins
Cal->LoadCalendarState(SaveData);
// RESETTING — wipes all state back to initial conditions
Cal->ResetCalendar();
InitializeCalendar() before LoadCalendarState(). The calendar must know which data assets define the year/week structure before it can restore a saved state correctly.🗓️ Calendar — Debug Tools
Debug Mode & Console Commands
// Enable verbose logging
Cal->EnableDebugMode(true);
// Log current state to Output Log
Cal->LogCurrentState();
// Get analytics struct (tick counts, time spent per period/season, etc.)
FCalendarAnalytics Analytics = Cal->GetAnalytics();
Cal->ResetAnalytics(); // Reset counters to zero
The plugin also exposes calendar console commands (via UCalendarConsoleCommands). In the Unreal Editor console, type Calendar. to autocomplete available commands for manually setting time, triggering events, and inspecting state.
⚖️ Crime & Justice — Overview
UCrimeSubsystem
A full criminal economy — crime recording, social witness detection, guard pursuit, bounty accumulation, sentencing, pardons, and crime-knowledge propagation through the social graph.
The crime system works in concert with the social system. Committing crimes affects:
- Individual NPC relationships — victims, witnesses, and their social circle all react.
- Faction standing — crimes against faction members reduce your standing with that faction.
- Regional reputation — becoming a known criminal in a region affects how all locals perceive you.
- Active bounties — accumulating bounties triggers escalating guard responses.
- Justice outcomes — getting caught means fines, jail, exile, or execution depending on severity.
⚖️ Crime — Types & Severity
Crime Types (ECrimeType)
| Type | Description | Design Note |
|---|---|---|
Trespassing | Entering restricted areas without permission. | Often a low-stakes crime; can be used for stealth gameplay. |
Theft | Taking property that doesn't belong to you. | Thieves Guild NPCs can be configured to ignore this type. |
Assault | Physical violence without killing. | Witnesses respond based on social relationship to victim. |
Murder | Killing another NPC. | Capital severity by default; highest bounty multipliers. |
Treason | Betraying your faction or nation. | Often triggers faction-wide consequences and exile. |
Crime Severity (ECrimeSeverity)
| Severity | Guard Response | Justice Outcome | Always Reported? |
|---|---|---|---|
Petty | Ignored by most. Verbal warning only. | No outcome unless bounty accumulates. | No |
Minor | Arrest attempt if bounty ≥ 500. | Small fine or warning. | No |
Moderate | Arrest attempt at 500 bounty; guards pursue. | Arrest, moderate fine. | No |
Serious | Immediate arrest attempt regardless of bounty. | Jail time. | No |
Capital | Kill-on-sight if bounty ≥ 2000. | Execution if captured. | Yes — always |
Bounty Multipliers by Social Tier
The victim's social tier scales the base bounty value at the time the crime is recorded. This is configured in USocialTierConfigDataAsset. Default example values:
| Tier | Example | Multiplier | Murder (Base 1000g) Becomes |
|---|---|---|---|
| Tier 1 | Nobility, Royalty | 5× | 5,000 gold |
| Tier 2 | Wealthy Merchants | 2× | 2,000 gold |
| Tier 3 | Commoners | 1× | 1,000 gold |
| Tier 4 | Beggars, Slaves | 0.25× | 250 gold |
⚖️ Crime — Recording Crimes
The FCrimeRecord Struct
FCrimeRecord Record;
Record.CrimeTag = FGameplayTag::RequestGameplayTag("Crime.Murder.Civilian");
Record.CriminalID = PlayerSocialID; // Who committed the crime
Record.VictimID = NPCSocialComponent->GetNPCID(); // Who was victimized
Record.Location = CrimeLocation; // World location for guard routing
Record.Timestamp = FDateTime::Now();
// Record it
UCrimeSubsystem* Crime = GetWorld()->GetGameInstance()->GetSubsystem<UCrimeSubsystem>();
FGuid CrimeID = Crime->RecordCrime(Record);
Querying Crime Records
// Get all crimes by a criminal
TArray<FCrimeRecord> MyCrimes = Crime->GetCrimesByCriminal(PlayerSocialID);
// Get all unreported crimes (witnessed but not yet reported to guards)
TArray<FCrimeRecord> Unreported = Crime->GetUnreportedCrimes(PlayerSocialID);
// Get active bounty for a criminal per faction
int32 Bounty = Crime->GetBountyForFaction(PlayerSocialID, FactionID);
// Get total bounty across all factions
int32 Total = Crime->GetTotalBounty(PlayerSocialID);
// Check if player has any active bounty
bool bWanted = Crime->IsWanted(PlayerSocialID);
⚖️ Crime — Witnesses
Witnesses are NPCs that can observe crimes and subsequently report them to authorities. Whether a witness actually reports depends on their relationship with the criminal, the crime severity, and configurable factors.
Adding Witness Capability to an NPC
-
Add UWitnessComponent
Open your NPC Blueprint → Components → Add → WitnessComponent. Configure the observation radius and witness behavior settings.
-
Configure Reporting Behavior
In the component's properties, set:
- Minimum Severity to Report: Witnesses below this severity threshold stay silent (default: Minor)
- Relationship Threshold for Silence: If the criminal's relationship score exceeds this value, the witness may choose not to report (unless the crime is Capital)
- Report Delay: Seconds before the witness attempts to report (gives player time to flee or intimidate)
The Reporting Flow
- A crime is recorded via
RecordCrime(). - Nearby
UWitnessComponents within their observation radius detect the crime event. - Each witness evaluates: Is the severity high enough? Is my relationship with the criminal below the silence threshold?
- Witnesses who decide to report call
Crime->ReportCrime(CrimeID, WitnessNPCID). FOnCrimeReportedfires — guard AI can listen and begin pursuit.- The bounty is added to the criminal's record for the victim's faction.
// Listen for crime reports in your guard AI
Crime->OnCrimeReported.AddDynamic(this, &AGuardController::OnCrimeReported);
// Fires: (FGuid CrimeID, FGuid ReporterNPCID)
⚖️ Crime — Guards
Guard Component Setup
Add UGuardComponent to law enforcement NPCs. The component hooks into FOnCrimeReported to automatically begin pursuit when a crime is reported in the guard's jurisdiction (faction ID match).
Guard Response by Bounty Level
| Bounty Range | Default Thresholds | Guard Behavior |
|---|---|---|
| Below arrest threshold | < 500 | Verbal warning, request to stop committing crimes. |
| Arrest zone | 500–1999 | Demand surrender. If refused, initiate arrest combat. If caught, execute sentence. |
| Kill-on-sight | ≥ 2000 | Attack on sight with no surrender offer. |
Surrender & Sentencing
// When player surrenders or is subdued by guards
FJusticeSentence Sentence = Crime->CalculateSentence(PlayerSocialID, FactionID);
Crime->ExecuteSentence(PlayerSocialID, FactionID, Sentence);
// Listen for sentence execution
Crime->OnSentenceExecuted.AddDynamic(this, &UMyClass::OnSentenceExecuted);
// Fires: (FGuid CriminalID, FGuid FactionID, FJusticeSentence Sentence)
// FJusticeSentence contains:
// - SentenceType (Warning, Fine, Jail, Exile, Execution)
// - FineAmount (gold removed from player)
// - JailDuration (in-game hours)
// - bClearsBounty (does serving the sentence wipe the slate?)
⚖️ Crime — Bounties & Justice
Bounty Accumulation
Bounties accumulate per faction — killing multiple city guard NPCs adds to the Imperial Guard faction bounty. The bounty value is: BaseBounty × SocialTierMultiplier per crime, capped by MaxBountyPerFaction in Project Settings.
Bounty Decay (Pardons)
The UPardonDataAsset defines how bounties decrease over time or through actions:
| Pardon Method | Description |
|---|---|
| Time-Based Decay | Bounty slowly reduces over in-game time when the criminal lies low (no new crimes). Rate configurable per crime severity. |
| Pay Fine | Player pays the active bounty amount to guards, clearing bounty and avoiding jail. |
| Serve Jail Time | Player spends configured in-game hours in jail, fully clearing the slate. |
| Community Service Quest | Via IQuestSystemInterface, completing a designated quest reduces or clears bounty. |
| Faction Pardon | High enough positive standing with the faction leader can grant a formal pardon. |
// Pay fine (removes bounty for the faction)
Crime->PayFine(PlayerSocialID, FactionID, FineAmount);
// Check pardon eligibility
bool bEligible = Crime->IsEligibleForPardon(PlayerSocialID, FactionID);
// Grant pardon (e.g., from quest completion or faction favor)
Crime->GrantPardon(PlayerSocialID, FactionID);
⚖️ Crime — Crime Propagation
Crime knowledge spreads through the social graph as rumor. An NPC who witnessed a crime can tell connected NPCs about it, which affects how those NPCs perceive the criminal — even if they didn't witness the crime personally.
How Crime Rumor Works
- A crime is witnessed and reported by a
UWitnessComponent. - The crime report enters the crime propagation queue (governed by
UCrimePropagationRulesDataAsset). - Connected NPCs are informed — their relationship with the criminal decreases based on crime severity and social distance.
- Each hop reduces the impact (similar to social propagation decay).
- Factions filter propagation: faction members are more likely to share crime knowledge within their faction.
// Async action: Wait for crime propagation to complete
// (See Async Actions section for Blueprint usage)
// Listen for propagation delegate
Crime->OnCrimePropagationComplete.AddDynamic(this, &UMyClass::OnPropComplete);
⚖️ Crime — Save & Load
// SAVING
FCrimeSaveData SaveData = Crime->SaveCrimeState();
// LOADING
Crime->LoadCrimeState(SaveData);
// Listen for load completion
Crime->OnCrimeDataLoaded.AddDynamic(this, &UMyClass::OnCrimeLoaded);
// Fires: (int32 CrimeCount, int32 BountyCount, bool bSuccess)
⚡ Async Actions
Blueprint Async Actions let you pause a Blueprint execution flow and wait for a specific social or calendar event without polling every tick. All async actions are UCLASS() subclasses of UBlueprintAsyncActionBase and appear as latent nodes in Blueprint.
Social Async Actions
| Async Action Class | What It Waits For |
|---|---|
USocietyAsyncAction_WaitForRelationshipChange | Any change to a specific NPC's relationship score. |
USocietyAsyncAction_WaitForRelationshipThreshold | A relationship score crossing a specific threshold value. |
USocietyAsyncAction_WaitForRelationshipStable | A relationship's velocity drops to near-zero (relationship stabilizes). |
USocietyAsyncAction_WaitForSentimentChange | A relationship's sentiment label changes (e.g., "Neutral" → "Friendly"). |
USocietyAsyncAction_WaitForPropagationComplete | All pending propagation from a specific event finishes. |
USocietyAsyncAction_WaitForEventProcessed | A specific relationship event finishes processing. |
USocietyAsyncAction_WaitForNPCRegistered | A specific NPC (by GUID or display name) registers with the subsystem. |
USocietyAsyncAction_WaitForNPCDeath | An NPC transitions to the Deceased lifecycle state. |
USocietyAsyncAction_WaitForLifecycleState | Any NPC reaches a specific lifecycle state. |
USocietyAsyncAction_WaitForRankChange | A relationship's social rank/tier classification changes. |
USocietyAsyncAction_WaitForConnectionDiscovered | An NPC discovers a connection to another NPC. |
USocietyAsyncAction_WaitForRegionalReputationThreshold | Regional reputation crosses a specified threshold. |
Calendar Async Actions
| Async Action Class | What It Waits For |
|---|---|
UAsyncAction_WaitForTimePeriod | Calendar enters a specific time period (e.g., wait for "Night"). |
UAsyncAction_WaitForDateTime | Calendar reaches a specific date and time. |
UAsyncAction_WaitForEvent | A specific calendar event starts or ends. |
Crime Async Actions
| Async Action Class | What It Waits For |
|---|---|
UCrimeAsyncAction_WaitForCrimeReported | Any crime is reported to authorities. |
UCrimeAsyncAction_WaitForWantedStatusChange | Player's wanted status changes (becomes wanted or bounty cleared). |
UCrimeAsyncAction_WaitForBountyThreshold | Bounty crosses a specific gold value. |
UCrimeAsyncAction_WaitForSentenceExecuted | A justice sentence is carried out. |
UCrimeAsyncAction_WaitForPropagation | Crime propagation finishes spreading through the social graph. |
Blueprint Usage Example
In Blueprint, these nodes appear as latent (async) nodes with output execution pins:
// Example: Wait until the player is "Friendly" with the Blacksmith
// Blueprint Node: "Wait For Relationship Threshold"
// Target NPC ID: BlacksmithNPCID
// Instigator ID: PlayerSocialID
// Threshold: 50 (Friendly threshold)
// Direction: Upward (crossing from below to above)
// → On Threshold Crossed (fires once, then node completes)
// → Play "blacksmith friend" dialogue
📚 Class Reference
Subsystems
| Class | Header | Description |
|---|---|---|
UCalendarSubsystem | Calendar/Subsystems/CalendarSubsystem.h | In-game time, events, seasons, celestial bodies. Manual tick required. |
UDynamicSocietySubsystem | Core/Subsystems/DynamicSocietySubsystem.h | Central social engine. Auto-ticks. Manages all relationships, factions, reputation. |
UCrimeSubsystem | CrimeJustice/Subsystems/CrimeSubsystem.h | Crime recording, bounties, witnesses, justice. Integrates with social subsystem. |
Components
| Class | Attach To | Description |
|---|---|---|
UNPCSocialComponent | Any NPC Actor | Gives an NPC a social identity. Auto-registers/unregisters with subsystem. |
UPlayerSocialComponent | APlayerState | Per-player social identity. Replicated to owning client. Use for multiplayer. |
UWitnessComponent | Civilian NPC Actors | Enables an NPC to observe and report crimes. |
UGuardComponent | Law Enforcement NPC Actors | Enables guard behavior — pursuit, arrest, kill-on-sight based on bounty. |
UCalendarTickComponent | Any persistent Actor | Convenience component that calls TickCalendar(DeltaTime) automatically every frame. |
Settings Classes
| Class | Project Settings Path |
|---|---|
UDynamicSocietySettings | Plugins → WC Dynamic Society |
UCalendarProjectSettings | Plugins → WC Dynamic Society Calendar |
UCrimeJusticeSettings | Plugins → WC Crime & Justice |
Key Enumerations
| Enum | Values | Purpose |
|---|---|---|
ENPCLifecycleState | Active, Dormant, Resurrected, Deceased, Purged | Current participation state of an NPC in the social system. |
ERelationshipPriority | Critical, High, Medium, Low, Negligible | Processing priority and dormancy aggressiveness for performance scaling. |
ECrimeType | Trespassing, Theft, Assault, Murder, Treason | Category of crime for faction filtering and quest objective tracking. |
ECrimeSeverity | Petty, Minor, Moderate, Serious, Capital | Determines guard response escalation and justice outcomes. |
ETimeSkipMode | Instant, Accelerated, Cinematic | How a time skip is presented to the player. |
EMoonPhase | NewMoon, WaxingCrescent, FirstQuarter… | Standard 8-phase moon cycle enum for the primary moon. |
EEventImpact | Minor, Moderate, Major, Massive | Severity classification of social events. Determines decay curves. |
EClusterType | Family, Gang, WorkCrew, MilitarySquad, Custom | Type classification of social clusters. |
Key Structs
| Struct | Purpose |
|---|---|
FRelationshipData | Full relationship state between two entities: score, history, moods, memories, velocity, stability. |
FRelationshipEvent | Input struct for ProcessRelationshipEvent(): event tag, source ID, target ID, optional modifiers. |
FEventProcessingResult | Output of event processing: NPCs affected, propagation steps taken, impact values applied. |
FPropagationResults | Full results of a propagation pass: all affected NPC IDs and their delta values. |
FNPCSocialProfile | NPC runtime identity: GUID, display name, social tier tag, priority, connections. |
FCrimeRecord | A recorded crime: crime tag, criminal ID, victim ID, location, timestamp, reported status. |
FJusticeSentence | A calculated justice outcome: sentence type, fine amount, jail duration, bounty cleared flag. |
FGameDateTime | In-game date and time: Year, Month, Day, Hour, Minute. |
FCalendarSaveData | Complete calendar state for save/load serialization. |
FTemporaryMoodModifier | A temporary mood: tag, initial modifier, duration in game days, current decay state. |
FReputationRank | A threshold→label mapping for displaying scores as human-readable sentiment strings. |
FSocietySaveData | Complete social system state for save/load serialization. |
🔷 Blueprint API Quick Reference
All functions below are BlueprintCallable or BlueprintPure. Access subsystems from any Blueprint via Get Game Instance → Get Subsystem nodes.
Calendar Subsystem — Most Used Functions
| Function | Type | Description |
|---|---|---|
InitializeCalendar | Callable | Initialize with Settings, YearDef, WeekDef data assets. |
TickCalendar | Callable | Advance time by DeltaTime. Call every frame. |
GetCurrentDateTime | Pure | Returns current FGameDateTime. |
SetCurrentDateTime | Callable | Jump to a specific date/time. |
SetTimeScale | Callable | Change time speed multiplier. |
PauseTime / ResumeTime | Callable | Pause/unpause time progression. |
GetCurrentTimePeriod | Pure | Returns current FTimePeriod struct. |
GetCurrentSeason | Pure | Returns current FSeason struct. |
GetPrimaryMoonPhase | Pure | Returns EMoonPhase enum for primary moon. |
IsEventActive | Pure | Check if a calendar event is currently active by tag. |
SkipTime | Callable | Skip forward by days/hours/minutes. |
SaveCalendarState | Callable | Returns FCalendarSaveData for serialization. |
LoadCalendarState | Callable | Restores calendar from FCalendarSaveData. |
GetSmoothedTimeOfDay | Pure | Returns 0–2400 float for UDS sky integration. |
Dynamic Society Subsystem — Most Used Functions
| Function | Type | Description |
|---|---|---|
ProcessRelationshipEvent | Callable | The primary way to change relationships. Fires propagation, delegates, history. |
GetRelationship | Pure | Get raw score between two entity GUIDs. |
GetEffectiveRelationshipWithMoods | Pure | Score + all active mood modifiers. Use for UI display. |
SetRelationship | Callable | Directly set a relationship score. Use sparingly (bypasses event system). |
ModifyRelationship | Callable | Add/subtract from a score directly. Bypasses event system. |
GetSentimentLabel | Pure | Convert score to human-readable label (e.g., "Friendly"). |
GetFactionStanding | Pure | Get player's faction standing score. |
GetRegionalReputation | Pure | Get player's reputation score in a region. |
GetRegionalSentimentLabel | Pure | Convert regional reputation to label (e.g., "Famous"). |
ApplyTemporaryMood | Callable | Apply a temporary mood modifier to a relationship. |
GetRelationshipVelocity | Pure | Rolling average score change per day. |
TransitionNPCToDeceased | Callable | Mark an NPC as dead (freezes relationships). |
ResurrectNPC | Callable | Bring a deceased NPC back to Resurrected state. |
RegisterSocialCluster | Callable | Register a social group at runtime. |
AutoDetectClusters | Callable | Trigger auto-detection of family clusters from connection data. |
SaveSocietyState | Callable | Returns FSocietySaveData for serialization. |
LoadSocietyState | Callable | Restores social system from FSocietySaveData. |
Crime Subsystem — Most Used Functions
| Function | Type | Description |
|---|---|---|
RecordCrime | Callable | Record a committed crime. Returns FGuid crime ID. |
ReportCrime | Callable | A witness reports a crime to authorities. Triggers bounty and guard response. |
GetBountyForFaction | Pure | Get bounty amount for a criminal/faction pair. |
GetTotalBounty | Pure | Total bounty across all factions. |
IsWanted | Pure | Check if entity has any active bounty. |
CalculateSentence | Callable | Determine justice outcome based on crimes and bounty. |
ExecuteSentence | Callable | Apply the sentence (fine, jail, etc.). |
PayFine | Callable | Pay gold to clear bounty without jail. |
GrantPardon | Callable | Clear bounty via pardon (faction favor, quest, etc.). |
SaveCrimeState | Callable | Returns crime save data for serialization. |
LoadCrimeState | Callable | Restores crime system from save data. |
🌐 Multiplayer Configuration
All three subsystems are designed with multiplayer in mind from the ground up. Operations that modify game state are server-authoritative.
Architecture Principles
- Server-Authoritative: All relationship modifications, crime recording, and sentence execution run on the server. Clients request changes via validated RPCs.
- Delta-Compressed Replication: Only changed relationships are broadcast to clients (controlled by
bEnableDeltaCompression). This dramatically reduces bandwidth in large NPC worlds. - Per-Player Isolation:
UPlayerSocialComponentreplicates only to its owning client — other players cannot inspect your exact relationship scores without server queries. - Range-Based Filtering: Set
DefaultReplicationRangeto only replicate NPC relationships within N centimeters of each client, critical for open-world performance. - Anti-Cheat RPC Validation:
MaxRelationshipChangePerRPClimits how much a client can modify a relationship in a single call. Server validates all incoming changes. - Calendar Replication: Current date/time and pause state are replicated via Multicast RPCs from the server when major changes occur.
Recommended Settings by Project Type
| Setting | Single-Player | Co-Op (Small) | Open World MMO-Style |
|---|---|---|---|
DefaultReplicationRange | 0 (disabled) | 0 (disabled) | 500000 (5km) |
RelationshipUpdateThrottle | 0.0s | 0.5s | 1.0s |
bReplicateNPCToNPCRelationships | N/A | false | false (unless UI requires it) |
bEnableDeltaCompression | N/A | true | true |
FrameBudgetMs | 1.0ms | 0.5ms | 0.2ms |
Per-Player Relationships in Multiplayer
With UPlayerSocialComponent on each player's PlayerState, every player maintains their own NPC relationship data. This means:
- Player A can be Hostile with the Blacksmith while Player B is Friendly.
- Player A committing a crime only affects Player A's bounty (unless you design shared-bounty mechanics).
- Each player's calendar interactions (waiting until morning) affect their own experience, while the shared game clock is server-authoritative.
⚡ Performance Guide
Profiling the Social System
Use Unreal's built-in profiling tools alongside the plugin's debug settings:
- Enable
bEnableVerboseLoggingin Project Settings to log every relationship change (disable before shipping). - Enable
bLogEventProcessingto track which events are consuming the most propagation budget. - Watch
QueueSizeWarningThresholdlog warnings — if you see these frequently, raiseFrameBudgetMsor reduce propagation scope. - Use
stat DynamicSocietyconsole command to see per-frame timing for the subsystem tick.
Spatial Relevance System
For open-world games with 100+ social NPCs, configure USpatialRelevanceConfigDataAsset in Project Settings to enable distance-based dormancy:
| Range | NPC Priority | Processing |
|---|---|---|
| < 50m | Any | Full processing regardless of priority |
| 50–200m | Medium+ | Normal processing; Low/Negligible go dormant |
| 200–500m | High+ | High priority processes; Medium+ go dormant |
| > 500m | Critical only | Only Critical NPCs process at distance |
Connection Limits
Keep the connection counts realistic to avoid exponential propagation growth:
- Soft limit: 12 connections per NPC — this is realistic for a social human (family of 4 + a few close friends + work colleagues).
- Hard limit: 20 connections per NPC — prevents any single NPC from being a propagation "hub" that fans events out to hundreds of NPCs instantly.
- A faction leader who "knows everyone" should use faction-level standing changes rather than personal connections to hundreds of NPCs.
Memory Management Tips
- Set
MaxMemoryCapacityto a value appropriate for your game's depth. 1000 memories is sufficient for most games. - Mark only truly important story memories as "permanent" — everything else should be purgeable.
- Use lifecycle state management aggressively: set NPCs to Dormant when players leave their region, and configure
DormantPurgeThresholdHoursto clean up truly forgotten NPCs.
🔍 Troubleshooting
Common Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| Calendar time is not advancing | TickCalendar() is not being called | Add TickCalendar(DeltaTime) to your Game Mode's Tick function, or add a UCalendarTickComponent to a persistent actor. |
| Calendar is not initialized error | InitializeCalendar() not called before first tick | Call InitializeCalendar() with your three data assets before calling TickCalendar() or querying any calendar state. |
| NPC relationships not changing after events | No RelationshipRulesDataAsset assigned | Create a RelationshipRulesDataAsset, add event tag → impact mappings, and assign it to Project Settings → Default Rules Asset. Without rules, the subsystem has no reference for impact values. |
| NPC not registering with subsystem | UNPCSocialComponent not on the Actor, or BeginPlay not firing | Verify the component is added in the Blueprint/C++ class. Check that the Actor's BeginPlay fires correctly. Check the Output Log for registration messages. |
| Propagation is slow / falling behind | FrameBudgetMs too low, or too many events per second | Raise FrameBudgetMs to 1.0–2.0ms. Reduce how frequently you fire relationship events. Raise MinimumImpactThreshold to cut propagation shorter. |
| Factions not recognized by subsystem | Faction assets not in Project Settings array | Open Project Settings → WC Dynamic Society → Factions and add your FactionDataAsset references to the array. |
| Crimes not triggering guard response | No CrimeDataAsset registered, or crime tag mismatch | Verify your CrimeDataAsset is in Project Settings → WC Crime & Justice. Ensure the gameplay tag used in RecordCrime() exactly matches a tag defined in the asset. |
| Bounty not accumulating | Crime not reported (no witness or witness chose silence) | Crimes must be reported to add bounty. Either call ReportCrime() directly, ensure a UWitnessComponent NPC is within range and meets the reporting conditions, or lower the relationship threshold for silence on the WitnessComponent. |
PlayerSocialComponent ID changes on respawn | Component attached to Pawn, not PlayerState | Move the component to APlayerState. Pawns are destroyed on death; PlayerState persists for the session. |
| Relationship data lost after save/load | PlayerSocialID not restored before BeginPlay | Call PlayerSocialComponent->RestorePlayerSocialID(SavedID) before the component's BeginPlay fires. If called after, relationships loaded from save won't match the new random ID. |
| Social tier multiplier not applying to bounties | No SocialTierConfigDataAsset assigned | Create a SocialTierConfigDataAsset, define tier hierarchy and multipliers, and assign it in Project Settings → Social Systems → Tiers. |
| Context tags not appearing on relationships | Contested Memories Phase 6 not configured | Context tags derive from memories. Ensure memories are being created when events are processed (requires memory subsystem configuration in Phase 6 setup). |
| Calendar time skips cause frame stutter | Using Instant mode, which fires all events at once | Switch to ETimeSkipMode::Cinematic (instant jump but with delegate notifications for fade transition) or Accelerated (time runs fast but events fire naturally). |
Debug Console Commands
Available in Development/Debug builds by typing in the Unreal console (~):
Calendar.PrintState— Logs complete calendar state to Output Log.Calendar.SetTime [Hour] [Minute]— Jump to a specific time of day.Calendar.SetDate [Year] [Month] [Day]— Jump to a specific date.Society.PrintRelationship [NPCID] [EntityID]— Print full relationship data between two entities.Society.PrintNPC [NPCID]— Print an NPC's complete social profile.Crime.PrintBounties [CriminalID]— List all active bounties for a criminal.Crime.ClearBounties [CriminalID]— Clear all bounties (dev only).
Enabling Additional Logging
In Project Settings → WC Dynamic Society → Debug:
bEnableVerboseLogging— Log every relationship Set/Modify call with old and new values.bLogEventProcessing— Log each ProcessRelationshipEvent call with all impact values.bLogThresholdCrossings— Log when a relationship crosses a significant threshold.bWarnOnUnknownEvents— Log a warning when an event tag has no rule defined (helps catch typos in event tags).
🤝 Social System — Overview
UDynamicSocietySubsystem
A deep NPC relationship engine managing opinions, social propagation, factions, regional reputation, memory, mood, personality, lifecycle, and social clusters. The world's social fabric.
The social system uses a bilateral social graph: every NPC has a score representing how they feel about every other entity they have a relationship with. Scores range from -100 (maximum hatred) to +100 (maximum devotion) by default, configurable in Project Settings.
Core Concepts
Relationship Score
A signed integer (-100 to +100) representing an entity's opinion of another. 0 = neutral stranger.
Propagation
When the player does something, the effect ripples through the social graph — friends of friends hear about it.
Factions
Named groups of NPCs with shared interests. Actions against faction members affect standing with the whole group.
Regional Reputation
How famous (or infamous) the player is in a geographic region. Uses a wider ±1000 scale for longer-term progression.
Contested Memories
NPCs remember specific events. Memories can be contested, propagated, or suppressed — affecting how they remember history.
Temporary Moods
Short-term emotional modifiers on top of base scores. Angry moods decay, grateful moods fade — adding natural emotional ebb and flow.