Site
Wonderscape Creations
UE 5.7+ · v1.0.1 · Win64

WCDynamicSociety

Complete beginner-to-expert documentation — Calendar, NPC Social Simulation, and Crime & Justice for Unreal Engine 5.7+

Unreal Engine 5.7+ Win64 Multiplayer Ready Blueprint Exposed 3 Subsystems

📖 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 UGameInstanceSubsystem with 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) or BlueprintAssignable, so your designers never need to open C++.
ℹ️
Minimal Dependencies: 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

RequirementValueNotes
Unreal Engine5.7.0+Plugin targets UE 5.7 APIs exclusively. Earlier versions are NOT supported.
PlatformWin64Mac, Linux, Android, iOS, TVOS are on the platform deny list in the .uplugin.
C++ CompilerMSVC (VS 2022)Required to compile the plugin modules. Blueprint-only projects must be converted first.
Visual Studio2022Install the "Game development with C++" workload.
Project TypeC++ ProjectBlueprint-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:

ModuleVisibilityPurpose
CorePublicFundamental Unreal types
GameplayTagsPublicGameplay tag containers and queries
CoreUObjectPublicUObject reflection system
EnginePrivateActor, component, subsystem base classes
Slate / SlateCorePrivateEditor UI (editor module only)
DeveloperSettingsPrivateProject Settings panel integration
NetCorePrivateNetworking and replication support
AIModulePrivateAI 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:

TopicWhy It HelpsBeginner Needed?
Gameplay TagsEvery crime, faction, region, time period, and event is identified by a Gameplay Tag.Yes — learn the basics
Data AssetsAll configuration is done through Blueprint Data Assets. Understanding how to create and reference them is essential.Yes — essential
Actor ComponentsUNPCSocialComponent and UPlayerSocialComponent are Actor Components you attach to your pawns.Yes — essential
Game Instance SubsystemsAll three core systems are Game Instance Subsystems — they persist across level changes.Helpful
Replication / MultiplayerUnderstanding server/client architecture helps configure the multiplayer settings correctly.Only for multiplayer projects
Blueprint Async NodesAsync Actions let you react to social events without polling every frame.Helpful for event-driven design

🔧 Installation

  1. 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 required Source folder.

  2. Copy the Plugin to Your Project

    Place the WCDynamicSociety folder inside your project's Plugins directory:

    YourProject/
    └── Plugins/
        └── WCDynamicSociety/          ← place the entire folder here
            ├── WCDynamicSociety.uplugin
            ├── Source/
            └── Documentation/

    If a Plugins folder doesn't exist at your project root, create it manually.

  3. 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.

  4. Regenerate Visual Studio Project Files

    Right-click your .uproject file in Windows Explorer and select "Generate Visual Studio project files". This adds the plugin's source to your Visual Studio solution.

  5. Build the Project

    Open the .sln file in Visual Studio 2022. Select Development Editor configuration and build (Ctrl+Shift+B). Alternatively, launch the editor directly from the .uproject which will trigger an automatic compile.

    ⚠️
    The first compile will take several minutes. Subsequent incremental builds are much faster.
  6. 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

  1. Create Calendar Settings Data Asset

    In the Content Browser, right-click and select Miscellaneous → Data Asset. Search for CalendarSettings as the class. Name it DA_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)
  2. 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.

  3. 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.

  4. 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 call TickCalendar(DeltaTime) every frame from your Game Mode or Player Controller's Tick function. Without this, time will not advance.
  5. 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

  1. 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
  2. Add UNPCSocialComponent to Your NPC

    Open your NPC Blueprint. In the Components panel, click Add Component and search for NPCSocialComponent. Assign the DA_NPC_Blacksmith data asset to its Social Data Asset property.

    The component automatically registers with the UDynamicSocietySubsystem on BeginPlay and unregisters on EndPlay.

  3. 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"
  4. Fire a Relationship Event

    When the player does something good, call ProcessRelationshipEvent on the subsystem. First, create a RelationshipRulesDataAsset that 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

  1. 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
  2. Register the Crime Asset in Project Settings

    Go to Edit → Project Settings → Plugins → WC Crime & Justice and add your DA_CrimeTypes asset to the Crime Data Assets array.

  3. 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);
💡
All three systems — Calendar, Social, and Crime — can be used completely independently. You don't need to set up all three to use one. Start with whichever is most relevant to your game, then add the others as needed.

⚙️ 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

PropertyDefaultDescription
DefaultRulesAssetNoneThe global RelationshipRulesDataAsset defining all event tags and their relationship impact values. Auto-loaded on subsystem init.
CalculationProfileNoneAn ImpactCalculationProfile asset defining how base impact, alignment, factions, and archetypes mathematically interact. Leave null for default hardcoded behavior.

Relationships — Score Range

PropertyDefaultDescription
MinRelationshipScore-100Absolute minimum relationship score (maximum hatred).
MaxRelationshipScore+100Absolute maximum relationship score (maximum devotion).
SentimentThresholds7 entriesArray of FReputationRank structs mapping score ranges to display labels (e.g., "Friendly", "Neutral", "Hostile"). Used in UI and dialogue selection.

Social Systems — Tiers

PropertyDefaultDescription
SocialTierConfigNoneA SocialTierConfigDataAsset defining 1–12 social tiers (hierarchy levels, bounty multipliers, reputation multipliers, event filtering thresholds).

Factions

PropertyDefaultDescription
FactionsEmptyArray of FactionDataAsset references. All factions in this array are registered automatically on subsystem init.
GlobalBountyFactionIDSpecial GUIDFaction ID used when a crime victim has no faction membership. Allows crime system to work without factions. Set to all-zeros to disable.
DefaultRegionalReputationCap1000Maximum regional reputation score. 10x the individual relationship max by default (harder to build regional fame).
DefaultRegionalReputationFloor-1000Minimum regional reputation score.
RegionalSentimentThresholds7 entriesLabels for regional reputation levels (e.g., "Legendary", "Famous", "Known", "Neutral"). Uses the -1000 to +1000 scale, NOT the individual relationship scale.

Regional Reputation

PropertyDefaultDescription
RegionsEmptyArray of RegionalReputationDataAsset references. Each region defines connections to other regions, faction presence, and reputation caps. Auto-registered on init.

Performance — Connections

PropertyDefaultDescription
ConnectionSoftLimit12Logs a warning when an NPC exceeds this many personal connections. Does not block creation.
ConnectionHardLimit20Rejects new connections beyond this limit. Prevents runaway memory growth.
MaxEventsPerNPC20Maximum relationship history events stored per NPC. Oldest are removed when exceeded.
MaxMemoryCapacity1000Maximum contested memories (Phase 6) stored system-wide. Non-permanent memories purged when exceeded.

Propagation

PropertyDefaultDescription
MinimumImpactThreshold3Propagation stops on a branch when the cascading impact drops below this absolute value.
MinimumConnectionWeight0.1Connections weaker than this fraction are skipped during propagation (0.0–1.0).
MaxAffectedNPCs100Maximum NPCs whose relationships are modified per propagation event.
MaxVisitedNPCs500Hard visited-node limit per propagation pass. Must be greater than MaxAffectedNPCs.
bPropagateToRivalstrueIf true, events propagate to rivals with inverted impact (helping someone's rival makes them dislike you). If false, rivals are skipped.

Performance — Frame Budget

PropertyDefaultDescription
FrameBudgetMs0.5msMaximum milliseconds per frame spent processing the propagation queue. Raise for faster propagation, lower for smoother frame rate.
QueueSizeWarningThreshold5000Queue size that triggers a performance warning in the log.
StaleUpdateTimeoutSeconds60.0sTime before queued updates are flagged as stale in the log.

Multiplayer

PropertyDefaultDescription
DefaultReplicationRange0 (disabled)Distance (cm) within which NPC relationships are replicated to clients. 0 = replicate all. Set to ~500000 (5km) for open-world optimization.
RelationshipUpdateThrottle0.0sMinimum seconds between relationship update broadcasts to clients. 0 = immediate.
MaxRelationshipChangePerRPC200Anti-cheat: maximum relationship change allowed in a single client RPC.
bReplicateNPCToNPCRelationshipsfalseIf true, clients can query NPC-to-NPC relationship data. More immersive but costs bandwidth.
bEnableDeltaCompressiontrueOnly send changed relationships to clients (recommended).

Social Systems — Advanced

PropertyDefaultDescription
SpatialRelevanceConfigNoneControls distance-based NPC dormancy and processing priority. Recommended for open-world games with 100+ NPCs.
TemporaryMoodConfigNoneConfiguration for temporary emotional state modifiers. Recommended for narrative-heavy games.
RelationshipDynamicsConfigNoneDefines emotional inertia, velocity tracking, and stability thresholds.
ForgivenessConfigNoneControls apology effectiveness, redemption quest mechanics, and witness-based forgiveness.
AlignmentReactionAssetNoneMaps alignment + moral tag combinations to approval modifiers.
bAutoGenerateFamilyClusterstrueAutomatically creates family social clusters from Family-type connections. Disable only if you need manual cluster control.
SocialClusterDefinitionsEmptyPre-defined cluster assets (gangs, squads, families) auto-registered on init.

Lifecycle — Cleanup

PropertyDefaultDescription
DeceasedPurgeThresholdHours24hIn-game hours after death before a deceased NPC becomes eligible for system cleanup. Allows resurrection mechanics.
DormantPurgeThresholdHours48hIn-game hours of dormancy before a dormant NPC becomes eligible for cleanup.
InteractionRecencyThresholdHours72hBoth 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.

PropertyDescription
Default Calendar assetThe default CalendarSettings data asset to load automatically.
Debug FlagsEnable verbose calendar logging per subsystem feature (time transitions, event triggers, celestial changes).

WC Crime & Justice (UCrimeJusticeSettings)

Located at Project Settings → Plugins → WC Crime & Justice.

PropertyDescription
Crime Data AssetsArray of CrimeDataAsset references — all crime definitions auto-registered on init.
Justice Data AssetA JusticeDataAsset defining sentencing rules, bounty thresholds, and pardon conditions.
Pardon Data AssetA PardonDataAsset defining time-based forgiveness, community service, and pardon conditions.
Propagation RulesA CrimePropagationRulesDataAsset defining how crime rumors spread between NPCs.
Vigilante RulesA VigilanteReactionRulesDataAsset defining which NPCs react to crime as vigilantes.
Arrest Bounty ThresholdBounty amount that triggers guard arrest attempts (default: 500).
Kill-On-Sight ThresholdBounty amount that triggers kill-on-sight (default: 2000).
Max Bounty Per FactionHard 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.

Editor Module
WCDynamicSocietyEditor
Runtime Subsystems
UCalendarSubsystem UDynamicSocietySubsystem UCrimeSubsystem
Runtime Module
WCDynamicSociety

The Three Subsystems

SubsystemTypePurpose
UCalendarSubsystemUGameInstanceSubsystemManages all in-game time — progression, time periods, seasons, celestial cycles, calendar events, and time skip. Must be manually ticked.
UDynamicSocietySubsystemUGameInstanceSubsystem + FTickableGameObjectCentral hub for all NPC relationships, social propagation, factions, regional reputation, memories, moods, lifecycle, and clusters. Self-ticking via FTickableGameObject.
UCrimeSubsystemUGameInstanceSubsystemTracks all crimes, witnesses, bounties, guard responses, sentencing, and pardons. Integrates with UDynamicSocietySubsystem for social consequences.
🚫
CRITICAL — Subsystem Access Pattern: All three are 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, and FOnCrimeDataLoaded delegates.

🧩 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 UDynamicSocietySubsystem on BeginPlay (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

PropertyTypeDescription
SocialDataAssetUNPCSocialDataAsset*The data asset defining this NPC's social identity. If set, all configuration is loaded from the asset. If null, set SocialProfile values manually.
SocialProfileFNPCSocialProfileRuntime state. Contains the NPC's GUID, display name, social tier, and priority. Do not edit manually when using a data asset.
PersonalRulesOverrideURelationshipRulesDataAsset*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 PlayerSocialID identifying this player in the social system.
  • Replicates to the owning client only (privacy and bandwidth optimization).
  • Registers with UDynamicSocietySubsystem on server BeginPlay.
  • Exposes FOnPlayerRelationshipChanged delegate for UI binding.
  • Supports save/load via RestorePlayerSocialID(SavedID) — call this before BeginPlay to restore a saved social identity.
⚠️
Attach to PlayerState, not Pawn. The 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 ClassPurpose
UCalendarSettingsCore time parameters: hours per day, minutes per hour, real seconds per in-game hour, sunrise/sunset times, dynamic time scale settings.
UYearDefinitionYear structure: list of months (each with a name, gameplay tag, and day count). Defines the total days in a year.
UWeekDefinitionWeek structure: list of days of the week, each with a name and gameplay tag. Used for GetCurrentDayOfWeek() queries.
UTimePeriodDefinitionTime-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.
USeasonDefinitionSeasons: array of seasons with names, gameplay tags, and day-of-year ranges. Optional.
UCelestialDefinitionCelestial bodies: array of bodies (moons, suns, stars) each with a name, gameplay tag, and phase count/cycle length. Optional.
UCalendarEventA calendar event: gameplay tag ID, start/end date ranges, recurrence settings, and chain events to trigger. Optional.

Social System Data Assets

Asset ClassPurpose
UNPCSocialDataAssetNPC social identity: display name, social tier tag, relationship priority, default connections, and optional persistent NPCID GUID.
UCharacterSocialDataAssetExtended NPC data including personality traits, alignment, and faction memberships.
UPlayerSocialDataAssetPlayer social profile: personality preset, alignment tag, starting faction memberships.
URelationshipRulesDataAssetMaps event gameplay tags to impact values (e.g., "Society.Event.Murder" → -50). Defines the "rulebook" for all relationship-changing interactions.
UFactionDataAssetFaction definition: unique GUID, faction name, allied/hostile factions, membership rules, and reputation tier definitions.
URegionalReputationDataAssetRegion definition: gameplay tag, connections to neighboring regions (bleeding paths), faction presence, custom reputation caps/floors.
URegionalEventDefinitionA region-specific event trigger definition for events that affect regional reputation.
USocialTierConfigDataAsset1–12 tier hierarchy with bounty multipliers, event filtering thresholds, and reputation multipliers per tier.
USocialClusterDefinitionDataAssetPre-defined social group (family, gang, squad): list of NPC GUIDs, cluster type, and internal relationship rules.
UPersonalityPresetDataAssetReusable personality template: volatility, empathy, loyalty bias, forgiveness speed. Assign to multiple NPCs for consistent personality archetypes.
USpatialRelevanceConfigDataAssetDistance thresholds and dormancy rules for performance optimization based on NPC distance from the player.
UTemporaryMoodConfigDataAssetMood type definitions, stacking rules, decay curves, and maximum mood stacks per relationship.
URelationshipDynamicsConfigDataAssetLock types, velocity tracking window (days), stability thresholds, and emotional inertia multipliers.
UForgivenessConfigDataAssetApology effectiveness curves, redemption quest hooks, witness forgiveness conditions, and forgiveness decay rates.
UAlignmentReactionDataAssetMaps alignment + moral tag combinations to relationship impact modifiers.
UImpactCalculationProfileFormula configuration: how base impact, alignment modifiers, faction bonuses, and propagation decay combine mathematically.

Crime & Justice Data Assets

Asset ClassPurpose
UCrimeDataAssetArray of FCrimeDefinition structs — all registerable crime types with their tags, types, severities, and base bounty values.
UJusticeDataAssetSentencing rules: maps crime severity to justice outcomes (warning, fine, jail time, exile, execution), plus bounty thresholds for guard escalation.
UPardonDataAssetPardon conditions: time-based forgiveness rates, community service quest completion, faction-specific pardon rules.
UCrimePropagationRulesDataAssetHow crime knowledge spreads: rumor decay rates, social distance limits, faction-based filtering.
UVigilanteReactionRulesDataAssetDefines 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.

UGameInstanceSubsystem Must be ticked manually Replication supported

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

  1. Core Time — Hours, minutes, days with configurable speed (all games)
  2. Time Periods — Morning, Day, Evening, Night transitions (optional)
  3. Seasons — Day-of-year based season tracking (optional)
  4. Celestial Cycles — Moon phases and other celestial body cycles (optional)
  5. Calendar Events — Recurring or one-off scripted events (optional)
  6. Event History & Chains — Track occurrence count, trigger chained events (optional)
  7. Time Skip — Instant or animated time advancement (optional)
  8. Save/Load — Full state serialization (recommended for all games)
  9. 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)
💡
For Blueprint-only setups, add a UCalendarTickComponent to any persistent actor (your Game Mode, a dedicated TimeManager actor, etc.) and it will automatically call 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:

DelegateWhen FiredParameters
OnMinuteChangedEvery in-game minuteFGameDateTime NewDateTime
OnHourChangedEvery in-game hourFGameDateTime NewDateTime
OnDayChangedEvery in-game day (midnight rollover)FGameDateTime NewDateTime
OnTimePeriodChangedWhen time period transitions (e.g., Morning → Afternoon)FTimePeriod NewPeriod, FTimePeriod OldPeriod
OnSeasonChangedWhen the season changesFSeason NewSeason, FSeason OldSeason
OnCelestialPhaseChangedWhen any celestial body changes phaseint32 BodyIndex, int32 NewPhase, int32 OldPhase
OnEventStartedWhen a calendar event beginsFActiveEvent Event
OnEventEndedWhen a calendar event endsFActiveEvent Event
OnTimeSkipStartedWhen a time skip begins(none)
OnTimeSkipCompletedWhen a time skip finishes(none)
OnTimeSkipCancelledWhen 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);
💡
Use 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)

ModeBehavior
InstantTime jumps immediately with no visual transition. All events that would have occurred during the skip are fired at once.
AcceleratedTime 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.
CinematicInstant 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();
⚠️
Call 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.

🤝 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.

UGameInstanceSubsystem FTickableGameObject (auto-ticks) 18+ feature phases

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.

🤝 Social — NPC Social Component

Adding to an NPC

  1. Create NPC Social Data Asset

    Right-click in Content Browser → Data Asset → NPCSocialDataAsset. Set the display name, social tier gameplay tag, and relationship priority.

  2. Add Component to NPC Blueprint

    Open your NPC Blueprint → Components panel → Add → NPCSocialComponent. In the Details panel, assign your data asset to Social Data Asset.

  3. Optional — Assign Personality Override

    If this NPC should react differently to events than the default rules specify, assign a URelationshipRulesDataAsset to Personal Rules Override. This NPC will use those rules instead of the global rules for all event processing.

Relationship Priority Tiers

The ERelationshipPriority enum controls how aggressively the subsystem processes this NPC's relationships — especially relevant for performance in large worlds:

PriorityProcessingDormancyIntended Usage (% of NPCs)
CriticalAlwaysNeverQuest-essential, faction leaders, story characters (5–10%)
HighFrequentRarelyKey merchants, guards, recurring NPCs (10–15%)
MediumNormalAt distanceGeneric townsfolk, traders (40–50%)
LowReducedQuicklyBackground NPCs, peasants, guards (20–30%)
NegligibleMinimalImmediatelyCrowd filler, decorative NPCs, one-time encounters (5–10%)

Querying Relationships from the Component

// Relation to player (single-player)
int32 Score = NPCComp->GetRelationshipWithPlayer();

// Multiplayer-safe version (pass the player's social component)
int32 Score = NPCComp->GetRelationshipWithPlayerEx(PlayerSocialComponent);

// Via player controller
int32 Score = NPCComp->GetRelationshipWithController(PlayerController);

// To another NPC
int32 Score = NPCComp->GetRelationshipWith(OtherNPCID);

// Get NPC's unique ID
FGuid NPCID = NPCComp->GetNPCID();

// Get social tier tag
FGameplayTag Tier = NPCComp->GetSocialTier();

// Get hierarchy level (1 = highest, 12 = lowest)
int32 Level = NPCComp->GetHierarchyLevel();

🤝 Social — Player Social Component

Setup

  1. Add UPlayerSocialComponent to PlayerState

    Open your APlayerState subclass Blueprint (or C++ class). Add UPlayerSocialComponent as a component. It auto-registers with the subsystem on server BeginPlay.

  2. Handle Save/Load

    When loading a saved game, call RestorePlayerSocialID() BEFORE BeginPlay completes. This ensures the subsystem loads saved relationship data keyed to the correct player GUID:

    // In your save game loading logic, before the player state's BeginPlay fires:
    PlayerSocialComponent->RestorePlayerSocialID(SaveData.PlayerSocialID);
  3. Optional — Assign Player Social Data Asset

    Assign a UPlayerSocialDataAsset to customize the player's personality traits, starting alignment, and faction memberships.

Listening for Relationship Changes

// Bind in BeginPlay
PlayerSocialComponent->OnRelationshipChanged.AddDynamic(this, &AMyHUD::OnNPCRelationshipChanged);

// Callback signature
void AMyHUD::OnNPCRelationshipChanged(FGuid NPCID, int32 OldScore, int32 NewScore)
{
    // Update your relationship UI
}

🤝 Social — Relationships

Relationship Data Structure (FRelationshipData)

Each bilateral relationship between two entities is stored as an FRelationshipData struct. This struct is rich with contextual information accumulated over time:

FieldTypeDescription
Scoreint32Base relationship score (-100 to +100). Does NOT include mood modifiers — use GetEffectiveRelationshipWithMoods() for the display value.
LastInteractionTimeFDateTimeWhen these two entities last interacted.
FirstInteractionTimeFDateTimeWhen the relationship was first established.
TotalInteractionCountint32Total number of direct interactions. Incremented on every relationship modification.
ContestedMemoryRefsTArray<FGuid>GUIDs of memories that shaped this relationship (Phase 6). References into the central memory store, not copies.
ContextTagsFGameplayTagContainerCached summary tags (e.g., Context.Violence, Context.Kindness) for fast dialogue filtering without loading full memories.
WorstOffenseSeverityEEventImpactThe most severe negative event ever recorded. Used to select which decay curve applies (Phase 8).
LastDecayProcessTimeFDateTimeWhen relationship decay was last calculated (Phase 8).
ActiveMoodsTArray<FTemporaryMoodModifier>Temporary mood modifiers decaying over time (Phase 13).
RecentChangeVelocityfloatRolling average score change per day over 7 days. Positive = improving, Negative = degrading (Phase 15).
StabilityModifierfloatMultiplier applied to all incoming impacts. <1.0 = resistant to change, >1.0 = more reactive (Phase 15).

Modifying Relationships

UDynamicSocietySubsystem* Society = GetWorld()->GetGameInstance()->GetSubsystem<UDynamicSocietySubsystem>();

// Direct set (use sparingly — bypasses event system and propagation)
Society->SetRelationship(NPCId, PlayerId, 50);

// Additive delta (also bypasses event system)
Society->ModifyRelationship(NPCId, PlayerId, +10);

// EVENT-BASED (RECOMMENDED — triggers propagation, delegates, threshold checks)
FRelationshipEvent Event;
Event.EventTag    = FGameplayTag::RequestGameplayTag("Society.Event.HelpedWithQuest");
Event.SourceID    = PlayerSocialComponent->PlayerSocialID;
Event.TargetID    = NPCComponent->GetNPCID();
Society->ProcessRelationshipEvent(Event);
💡
Always prefer ProcessRelationshipEvent() over direct SetRelationship() calls. Event processing triggers propagation to connected NPCs, fires threshold delegates, applies personality modifiers, and records history — all the things that make your world feel alive. Direct sets are only appropriate for initialization or save game loading.

Querying Relationships

// Raw score
int32 Score = Society->GetRelationship(NPCID, PlayerID);

// Score including mood modifiers (use for UI display)
int32 Effective = Society->GetEffectiveRelationshipWithMoods(NPCID, PlayerID);

// Human-readable sentiment label
FText Label = Society->GetSentimentLabel(Score);

// Full relationship data
FRelationshipData Data;
Society->GetRelationshipData(NPCID, PlayerID, Data);

// Relationship velocity (Phase 15)
float Velocity = Society->GetRelationshipVelocity(NPCID, PlayerID);
// +15.0 = rapidly improving, -20.0 = rapidly collapsing

Relationship Delegates

// Bind to global relationship change events
Society->OnRelationshipChanged.AddDynamic(this, &UMyClass::OnRelationshipChanged);
// Fires: (FGuid TargetNPCID, FGuid InstigatorID, int32 OldScore, int32 NewScore)

Society->OnRelationshipThresholdCrossed.AddDynamic(this, &UMyClass::OnThresholdCrossed);
// Fires: (FGuid NPCID, FGuid InstigatorID, int32 ThresholdCrossed, int32 Direction)
// ThresholdCrossed values: -75, -50, -25, 0, 25, 50, 75
// Direction: +1 = crossed upward (improving), -1 = crossed downward (worsening)

🤝 Social — Factions

Factions are named groups of NPCs. Actions against one faction member can affect standing with the entire faction. Factions can have pre-defined allied and hostile relationships with each other.

Creating a Faction

  1. Create FactionDataAsset

    Right-click → Data Asset → FactionDataAsset. Set the Faction GUID (unique per faction), Display Name, Allied Factions, and Hostile Factions arrays.

    ⚠️
    The Faction GUID is the permanent unique identifier. Once set and used in a shipped game, it should NEVER change — all save data references it by GUID.
  2. Register in Project Settings

    Go to Project Settings → Plugins → WC Dynamic Society → Factions and add your faction asset to the Factions array. It will be auto-registered when the subsystem initializes.

  3. Assign NPCs to Factions

    In your UNPCSocialDataAsset or UCharacterSocialDataAsset, add the faction GUID to the NPC's faction membership list. NPCs can belong to multiple factions.

Faction Queries

// Get player's standing with a faction
int32 Standing = Society->GetFactionStanding(PlayerSocialID, FactionID);

// Modify faction standing (without a specific NPC target)
Society->ModifyFactionStanding(PlayerSocialID, FactionID, +25);

// Get all factions an NPC belongs to
TArray<FGuid> Factions = Society->GetNPCFactions(NPCID);

// Check if two factions are allied/hostile
bool bAllied   = Society->AreFactionAllied(FactionA, FactionB);
bool bHostile  = Society->AreFactionHostile(FactionA, FactionB);

Social Tier & Faction Interaction

Social tier affects how crimes against NPCs are weighted. Murdering a Tier 1 noble may produce a 5x bounty compared to murdering a Tier 4 beggar, reflecting the asymmetry of social class in medieval societies. Configure these multipliers in USocialTierConfigDataAsset.

🤝 Social — Propagation

Propagation is the mechanism by which news of an event ripples outward through the social graph. When the player does something notable (helps a farmer, murders a merchant), connected NPCs hear about it and their relationships shift accordingly — with decreasing impact the further they are in the social network.

How Propagation Works

  1. An event is processed via ProcessRelationshipEvent().
  2. The direct target NPC's relationship changes immediately.
  3. The event enters the propagation queue and is processed over multiple frames (respecting FrameBudgetMs).
  4. Each connected NPC receives a scaled-down version of the original impact, reduced by connection weight and distance in the social graph.
  5. Propagation stops on any branch when the cascading impact drops below MinimumImpactThreshold (default: 3).
  6. Rivals receive inverted impact (if bPropagateToRivals = true) — helping someone makes their rivals dislike you.

Propagation Delegates

// When a specific event finishes propagating
Society->OnRelationshipEventProcessed.AddDynamic(this, &UMyClass::OnEventProcessed);
// Fires: (FRelationshipEvent Event, FEventProcessingResult Result)
// FEventProcessingResult contains: NPCsAffected, TotalPropagationSteps, etc.

// When the entire propagation queue is empty
Society->OnPropagationQueueEmpty.AddDynamic(this, &UMyClass::OnQueueEmpty);
// Fires: (int32 TotalUpdatesProcessed)
// Useful for waiting until all social ripples have settled before enabling player input.

// When a single propagation completes
Society->OnPropagationCompleted.AddDynamic(this, &UMyClass::OnPropComplete);
// Fires: (FGuid SourceNPCID, FPropagationResults Results)

Tuning Propagation Performance

For large open worlds with hundreds of social NPCs, tune these Project Settings values:

Tuning GoalSetting to AdjustDirection
Reduce CPU spike per frameFrameBudgetMsLower (e.g., 0.2ms). Propagation takes more frames but frames stay smooth.
Limit social reachMaxAffectedNPCsLower (e.g., 50). Events affect fewer NPCs per occurrence.
Sharper decay with distanceMinimumImpactThresholdHigher (e.g., 5–10). Propagation cuts off sooner in the graph.
Ignore weak relationshipsMinimumConnectionWeightHigher (e.g., 0.3). Only close relationships propagate news.

🤝 Social — Regional Reputation

Regional reputation tracks how the player is perceived by the population of a geographic area as a whole — distinct from individual NPC relationships. Where individual scores range from -100 to +100 (a single person's opinion), regional reputation uses a wider -1000 to +1000 scale, making regional fame a long-term achievement requiring sustained effort.

Creating a Region

  1. Create RegionalReputationDataAsset

    Right-click → Data Asset → RegionalReputationDataAsset. Set:

    • Region Tag: Gameplay tag identifying this region (e.g., Region.Ironhaven)
    • Display Name: "Ironhaven"
    • Connected Regions: Array of other region tags with "bleed weight" — fame/infamy in one region bleeds partially into adjacent regions
    • Faction Presence: Which factions operate here — faction reputation changes also affect regional reputation
    • Custom Rep Cap/Floor: Override the global default if this region has a different range
  2. Register in Project Settings

    Add your RegionalReputationDataAsset to Project Settings → Plugins → WC Dynamic Society → Regions array.

Querying Regional Reputation

// Get reputation in a region
int32 Rep = Society->GetRegionalReputation(PlayerSocialID, FGameplayTag::RequestGameplayTag("Region.Ironhaven"));

// Modify
Society->ModifyRegionalReputation(PlayerSocialID, RegionTag, +100);

// Get display label (uses RegionalSentimentThresholds from Project Settings)
FText Label = Society->GetRegionalSentimentLabel(Rep);
// e.g., "Legendary" (800+), "Famous" (500+), "Known" (0+), "Infamous" (-500+)

// Delegate
Society->OnRegionalReputationChanged.AddDynamic(this, &UMyClass::OnRepChanged);
// Fires: (FGameplayTag RegionTag, int32 OldRep, int32 NewRep)

🤝 Social — Contested Memories Phase 6

Contested memories are specific historical events stored in the social system. Rather than NPCs simply having a score, they have records of why they feel that way — and these memories can be contested, propagated as rumors, or suppressed entirely.

Memory Architecture

  • Memories are stored centrally in the subsystem — not per-NPC. FRelationshipData.ContestedMemoryRefs contains GUIDs pointing into the central memory store.
  • Each relationship can reference up to 10 memories (soft limit); 20 is the hard cap. Oldest non-permanent memories are purged when the cap is reached. Permanent memories (flagged as such) are always retained.
  • ContextTags on FRelationshipData are automatically derived from memory types — e.g., Context.Violence, Context.Betrayal, Context.Trade. Use these for fast dialogue filtering without loading full memory data.

Using Context Tags in Dialogue

// Fast check: Does this relationship have a violence memory?
FRelationshipData Data;
Society->GetRelationshipData(NPCID, PlayerID, Data);
bool bHasViolence = Data.ContextTags.HasTag(FGameplayTag::RequestGameplayTag("Context.Violence"));

// This enables dialogue like: "I haven't forgotten what you did to my brother..."
// Without loading all memory detail — just tag queries.
💡
The memory system pairs beautifully with dialogue systems. Check ContextTags to choose dialogue variants, then load the specific memory GUID if you need the precise "what happened, where, when" details for a more personal NPC response.

🤝 Social — Temporary Moods Phase 13

Temporary moods are short-duration emotional modifiers layered on top of the base relationship score. They decay linearly over time, creating natural emotional ebb and flow without permanently altering the underlying relationship.

Mood Behavior

  • Decay formula: CurrentImpact = InitialModifier × (1 − Elapsed / Duration). A -30 "Angry" mood over 7 days is -15 after 3.5 days and 0 after 7 days.
  • Fully decayed moods (impact = 0) are automatically removed from the relationship.
  • New moods replace existing moods of the same type by default (stacking can be enabled in UTemporaryMoodConfigDataAsset).
  • The base FRelationshipData.Score does NOT include mood modifiers. Always call GetEffectiveRelationshipWithMoods() for display values.

Example Moods

Mood TagModifierDurationTrigger
Mood.Angry-307 game daysNPC was recently insulted or harmed
Mood.Grateful+202 game daysNPC was recently helped
Mood.Grieving-1514 game daysNPC lost someone important
Mood.Elated+251 game daySomething wonderful just happened
// Apply a mood
FTemporaryMoodModifier Mood;
Mood.MoodTag        = FGameplayTag::RequestGameplayTag("Mood.Angry");
Mood.InitialModifier = -30;
Mood.DurationDays   = 7.0f;
Society->ApplyTemporaryMood(NPCID, PlayerID, Mood);

// Get effective score (base + all mood modifiers)
int32 Effective = Society->GetEffectiveRelationshipWithMoods(NPCID, PlayerID);

🤝 Social — Relationship Dynamics Phase 15

Relationship dynamics track the rate of change of a relationship — not just its current state. This enables richer narrative queries: "Is this relationship improving or collapsing?" and "Has this relationship become stable or volatile?"

Velocity

The RecentChangeVelocity field in FRelationshipData is a rolling 7-day average of score changes per day. Positive values mean the relationship is improving; negative means it's degrading.

float Velocity = Society->GetRelationshipVelocity(NPCID, PlayerID);
// +15.0 = rapidly improving
//  +2.5 = slowly improving
//   0.0 = stable
//  -5.0 = slowly deteriorating
// -20.0 = rapidly collapsing (betrayal aftermath)

Stability

The StabilityModifier field acts as a damper on incoming impacts. A stable long-term friend (stability > 1.0) won't be easily destabilized by a single insult. A new acquaintance (stability < 1.0) may be more easily swayed. Configure stability thresholds in URelationshipDynamicsConfigDataAsset.

Lock Types

Lock types allow designers to temporarily prevent a relationship from changing beyond a certain value — useful for story beats. Example: "This NPC must remain hostile until the player completes quest X."

🤝 Social — Social Clusters Phase 16

Social clusters are named groups of NPCs that share a collective social identity — families, gangs, work crews, military squads. Harming one member propagates a modified impact to all cluster members, creating "hurt one, hurt all" dynamics.

Auto-Generation (Families)

With bAutoGenerateFamilyClusters = true (default), the system automatically creates family clusters from NPCs connected with the Family connection type. No setup required.

Manual Cluster Definition

// Create via data asset: USocialClusterDefinitionDataAsset
// Add it to Project Settings → Social Cluster Definitions array for auto-loading

// Or register at runtime
FSocialClusterDefinition Cluster;
Cluster.ClusterType  = EClusterType::Gang;
Cluster.ClusterName  = "The Iron Brotherhood";
Cluster.MemberIDs.Add(BossNPCID);
Cluster.MemberIDs.Add(LieutenantNPCID);
// ...
Society->RegisterSocialCluster(Cluster);

🤝 Social — NPC Lifecycle Phase 11

The lifecycle system manages what state each NPC is in relative to the social system. This prevents invalid operations (like targeting a dead NPC) while allowing the world to persist meaningful social context about deceased characters.

Lifecycle States

StateDescriptionRelationship Processing
ActiveNPC is spawned and in the world. Normal operations.Full — can be direct event target.
DormantNPC exists in data but is not currently spawned (player left region).Preserved — can receive propagated reputation changes but cannot be direct event target.
ResurrectedNPC was dead and has been brought back. Transitional state.Unfrozen — allows resurrection narrative acknowledgment.
DeceasedNPC has died. Relationships are frozen.Frozen — still referenced in other NPCs' memories.
PurgedMarked for complete removal. Terminal state.Being cleaned up.

Valid State Transitions

// Active ↔ Dormant (reversible as player travels)
// Active → Deceased (NPC dies)
// Deceased → Resurrected (resurrection magic, quest)
// Resurrected → Active (full recovery)
// Resurrected → Deceased (died again)
// Deceased → Purged (after DeceasedPurgeThresholdHours)

// Transition an NPC to deceased:
Society->TransitionNPCToDeceased(NPCID, KillerID, DeathContextTag);

// Resurrection:
Society->ResurrectNPC(NPCID, ResurrectorID);

Lifecycle Delegates

Society->OnNPCDeceased.AddDynamic(this, &UMyClass::OnNPCDied);
// Fires: (FGuid NPCID, FGuid KillerID, FGameplayTag DeathContext)

Society->OnNPCResurrected.AddDynamic(this, &UMyClass::OnNPCResurrected);
// Fires: (FGuid NPCID, FGuid ResurrectorID, int32 DeathCount)

Society->OnLifecycleStateChanged.AddDynamic(this, &UMyClass::OnLifecycleChanged);
// Fires: (FGuid NPCID, ENPCLifecycleState OldState, ENPCLifecycleState NewState)

🤝 Social — Alignment System Phase 7

The alignment system maps the player's (and NPCs') moral alignment tags to approval or disapproval modifiers when specific event types are processed. A Lawful Good NPC will react much more negatively to murder than a Chaotic Evil NPC would.

Setup

Create a UAlignmentReactionDataAsset. Define rows mapping alignment tag + moral event tag → approval modifier. Assign it in Project Settings → WC Dynamic Society → Alignment Reaction Asset.

Alignment Tags

Alignment tags are standard Gameplay Tags applied to an NPC's or player's social profile (e.g., Alignment.LawfulGood, Alignment.ChaoticEvil, Alignment.TrueNeutral). Define your own tag hierarchy to match your game's moral framework.

🤝 Social — Forgiveness Phase 18

The forgiveness system defines how NPCs repair damaged relationships over time — through patience, apologies, redemption quests, and witness-mediated forgiveness.

Forgiveness Mechanisms

MechanismDescription
Time-Based DecayNegative relationships slowly heal over time. The rate and floor are determined by the WorstOffenseSeverity on the relationship — minor offenses heal faster than capital crimes.
ApologyPlayer can offer an apology. Effectiveness is configurable in UForgivenessConfigDataAsset — high-severity offenses may require witnessed apologies or gifts.
Redemption QuestsNPCs can set redemption quest conditions via the IQuestSystemInterface. Completing the quest grants forgiveness proportional to the quest's configured weight.
Witness ForgivenessA mutual trusted third party can vouch for the player, carrying their forgiveness weight into a hostile relationship.
ℹ️
Time-based decay always applies. The advanced forgiveness mechanisms (apology, redemption quest, witness) require a UForgivenessConfigDataAsset to be set in Project Settings. Without it, only base decay applies.

⚖️ 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.

UGameInstanceSubsystem Integrates with UDynamicSocietySubsystem Quest system interface

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)

TypeDescriptionDesign Note
TrespassingEntering restricted areas without permission.Often a low-stakes crime; can be used for stealth gameplay.
TheftTaking property that doesn't belong to you.Thieves Guild NPCs can be configured to ignore this type.
AssaultPhysical violence without killing.Witnesses respond based on social relationship to victim.
MurderKilling another NPC.Capital severity by default; highest bounty multipliers.
TreasonBetraying your faction or nation.Often triggers faction-wide consequences and exile.

Crime Severity (ECrimeSeverity)

SeverityGuard ResponseJustice OutcomeAlways Reported?
PettyIgnored by most. Verbal warning only.No outcome unless bounty accumulates.No
MinorArrest attempt if bounty ≥ 500.Small fine or warning.No
ModerateArrest attempt at 500 bounty; guards pursue.Arrest, moderate fine.No
SeriousImmediate arrest attempt regardless of bounty.Jail time.No
CapitalKill-on-sight if bounty ≥ 2000.Execution if captured.Yes — always
⚠️
Capital crimes are always reported regardless of the witness's relationship with the criminal. Even a criminal's best friend will report a witnessed murder to the authorities.

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:

TierExampleMultiplierMurder (Base 1000g) Becomes
Tier 1Nobility, Royalty5,000 gold
Tier 2Wealthy Merchants2,000 gold
Tier 3Commoners1,000 gold
Tier 4Beggars, Slaves0.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

  1. Add UWitnessComponent

    Open your NPC Blueprint → Components → Add → WitnessComponent. Configure the observation radius and witness behavior settings.

  2. 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

  1. A crime is recorded via RecordCrime().
  2. Nearby UWitnessComponents within their observation radius detect the crime event.
  3. Each witness evaluates: Is the severity high enough? Is my relationship with the criminal below the silence threshold?
  4. Witnesses who decide to report call Crime->ReportCrime(CrimeID, WitnessNPCID).
  5. FOnCrimeReported fires — guard AI can listen and begin pursuit.
  6. 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 RangeDefault ThresholdsGuard Behavior
Below arrest threshold< 500Verbal warning, request to stop committing crimes.
Arrest zone500–1999Demand surrender. If refused, initiate arrest combat. If caught, execute sentence.
Kill-on-sight≥ 2000Attack 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 MethodDescription
Time-Based DecayBounty slowly reduces over in-game time when the criminal lies low (no new crimes). Rate configurable per crime severity.
Pay FinePlayer pays the active bounty amount to guards, clearing bounty and avoiding jail.
Serve Jail TimePlayer spends configured in-game hours in jail, fully clearing the slate.
Community Service QuestVia IQuestSystemInterface, completing a designated quest reduces or clears bounty.
Faction PardonHigh 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

  1. A crime is witnessed and reported by a UWitnessComponent.
  2. The crime report enters the crime propagation queue (governed by UCrimePropagationRulesDataAsset).
  3. Connected NPCs are informed — their relationship with the criminal decreases based on crime severity and social distance.
  4. Each hop reduces the impact (similar to social propagation decay).
  5. 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 ClassWhat It Waits For
USocietyAsyncAction_WaitForRelationshipChangeAny change to a specific NPC's relationship score.
USocietyAsyncAction_WaitForRelationshipThresholdA relationship score crossing a specific threshold value.
USocietyAsyncAction_WaitForRelationshipStableA relationship's velocity drops to near-zero (relationship stabilizes).
USocietyAsyncAction_WaitForSentimentChangeA relationship's sentiment label changes (e.g., "Neutral" → "Friendly").
USocietyAsyncAction_WaitForPropagationCompleteAll pending propagation from a specific event finishes.
USocietyAsyncAction_WaitForEventProcessedA specific relationship event finishes processing.
USocietyAsyncAction_WaitForNPCRegisteredA specific NPC (by GUID or display name) registers with the subsystem.
USocietyAsyncAction_WaitForNPCDeathAn NPC transitions to the Deceased lifecycle state.
USocietyAsyncAction_WaitForLifecycleStateAny NPC reaches a specific lifecycle state.
USocietyAsyncAction_WaitForRankChangeA relationship's social rank/tier classification changes.
USocietyAsyncAction_WaitForConnectionDiscoveredAn NPC discovers a connection to another NPC.
USocietyAsyncAction_WaitForRegionalReputationThresholdRegional reputation crosses a specified threshold.

Calendar Async Actions

Async Action ClassWhat It Waits For
UAsyncAction_WaitForTimePeriodCalendar enters a specific time period (e.g., wait for "Night").
UAsyncAction_WaitForDateTimeCalendar reaches a specific date and time.
UAsyncAction_WaitForEventA specific calendar event starts or ends.

Crime Async Actions

Async Action ClassWhat It Waits For
UCrimeAsyncAction_WaitForCrimeReportedAny crime is reported to authorities.
UCrimeAsyncAction_WaitForWantedStatusChangePlayer's wanted status changes (becomes wanted or bounty cleared).
UCrimeAsyncAction_WaitForBountyThresholdBounty crosses a specific gold value.
UCrimeAsyncAction_WaitForSentenceExecutedA justice sentence is carried out.
UCrimeAsyncAction_WaitForPropagationCrime 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

ClassHeaderDescription
UCalendarSubsystemCalendar/Subsystems/CalendarSubsystem.hIn-game time, events, seasons, celestial bodies. Manual tick required.
UDynamicSocietySubsystemCore/Subsystems/DynamicSocietySubsystem.hCentral social engine. Auto-ticks. Manages all relationships, factions, reputation.
UCrimeSubsystemCrimeJustice/Subsystems/CrimeSubsystem.hCrime recording, bounties, witnesses, justice. Integrates with social subsystem.

Components

ClassAttach ToDescription
UNPCSocialComponentAny NPC ActorGives an NPC a social identity. Auto-registers/unregisters with subsystem.
UPlayerSocialComponentAPlayerStatePer-player social identity. Replicated to owning client. Use for multiplayer.
UWitnessComponentCivilian NPC ActorsEnables an NPC to observe and report crimes.
UGuardComponentLaw Enforcement NPC ActorsEnables guard behavior — pursuit, arrest, kill-on-sight based on bounty.
UCalendarTickComponentAny persistent ActorConvenience component that calls TickCalendar(DeltaTime) automatically every frame.

Settings Classes

ClassProject Settings Path
UDynamicSocietySettingsPlugins → WC Dynamic Society
UCalendarProjectSettingsPlugins → WC Dynamic Society Calendar
UCrimeJusticeSettingsPlugins → WC Crime & Justice

Key Enumerations

EnumValuesPurpose
ENPCLifecycleStateActive, Dormant, Resurrected, Deceased, PurgedCurrent participation state of an NPC in the social system.
ERelationshipPriorityCritical, High, Medium, Low, NegligibleProcessing priority and dormancy aggressiveness for performance scaling.
ECrimeTypeTrespassing, Theft, Assault, Murder, TreasonCategory of crime for faction filtering and quest objective tracking.
ECrimeSeverityPetty, Minor, Moderate, Serious, CapitalDetermines guard response escalation and justice outcomes.
ETimeSkipModeInstant, Accelerated, CinematicHow a time skip is presented to the player.
EMoonPhaseNewMoon, WaxingCrescent, FirstQuarter…Standard 8-phase moon cycle enum for the primary moon.
EEventImpactMinor, Moderate, Major, MassiveSeverity classification of social events. Determines decay curves.
EClusterTypeFamily, Gang, WorkCrew, MilitarySquad, CustomType classification of social clusters.

Key Structs

StructPurpose
FRelationshipDataFull relationship state between two entities: score, history, moods, memories, velocity, stability.
FRelationshipEventInput struct for ProcessRelationshipEvent(): event tag, source ID, target ID, optional modifiers.
FEventProcessingResultOutput of event processing: NPCs affected, propagation steps taken, impact values applied.
FPropagationResultsFull results of a propagation pass: all affected NPC IDs and their delta values.
FNPCSocialProfileNPC runtime identity: GUID, display name, social tier tag, priority, connections.
FCrimeRecordA recorded crime: crime tag, criminal ID, victim ID, location, timestamp, reported status.
FJusticeSentenceA calculated justice outcome: sentence type, fine amount, jail duration, bounty cleared flag.
FGameDateTimeIn-game date and time: Year, Month, Day, Hour, Minute.
FCalendarSaveDataComplete calendar state for save/load serialization.
FTemporaryMoodModifierA temporary mood: tag, initial modifier, duration in game days, current decay state.
FReputationRankA threshold→label mapping for displaying scores as human-readable sentiment strings.
FSocietySaveDataComplete 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

FunctionTypeDescription
InitializeCalendarCallableInitialize with Settings, YearDef, WeekDef data assets.
TickCalendarCallableAdvance time by DeltaTime. Call every frame.
GetCurrentDateTimePureReturns current FGameDateTime.
SetCurrentDateTimeCallableJump to a specific date/time.
SetTimeScaleCallableChange time speed multiplier.
PauseTime / ResumeTimeCallablePause/unpause time progression.
GetCurrentTimePeriodPureReturns current FTimePeriod struct.
GetCurrentSeasonPureReturns current FSeason struct.
GetPrimaryMoonPhasePureReturns EMoonPhase enum for primary moon.
IsEventActivePureCheck if a calendar event is currently active by tag.
SkipTimeCallableSkip forward by days/hours/minutes.
SaveCalendarStateCallableReturns FCalendarSaveData for serialization.
LoadCalendarStateCallableRestores calendar from FCalendarSaveData.
GetSmoothedTimeOfDayPureReturns 0–2400 float for UDS sky integration.

Dynamic Society Subsystem — Most Used Functions

FunctionTypeDescription
ProcessRelationshipEventCallableThe primary way to change relationships. Fires propagation, delegates, history.
GetRelationshipPureGet raw score between two entity GUIDs.
GetEffectiveRelationshipWithMoodsPureScore + all active mood modifiers. Use for UI display.
SetRelationshipCallableDirectly set a relationship score. Use sparingly (bypasses event system).
ModifyRelationshipCallableAdd/subtract from a score directly. Bypasses event system.
GetSentimentLabelPureConvert score to human-readable label (e.g., "Friendly").
GetFactionStandingPureGet player's faction standing score.
GetRegionalReputationPureGet player's reputation score in a region.
GetRegionalSentimentLabelPureConvert regional reputation to label (e.g., "Famous").
ApplyTemporaryMoodCallableApply a temporary mood modifier to a relationship.
GetRelationshipVelocityPureRolling average score change per day.
TransitionNPCToDeceasedCallableMark an NPC as dead (freezes relationships).
ResurrectNPCCallableBring a deceased NPC back to Resurrected state.
RegisterSocialClusterCallableRegister a social group at runtime.
AutoDetectClustersCallableTrigger auto-detection of family clusters from connection data.
SaveSocietyStateCallableReturns FSocietySaveData for serialization.
LoadSocietyStateCallableRestores social system from FSocietySaveData.

Crime Subsystem — Most Used Functions

FunctionTypeDescription
RecordCrimeCallableRecord a committed crime. Returns FGuid crime ID.
ReportCrimeCallableA witness reports a crime to authorities. Triggers bounty and guard response.
GetBountyForFactionPureGet bounty amount for a criminal/faction pair.
GetTotalBountyPureTotal bounty across all factions.
IsWantedPureCheck if entity has any active bounty.
CalculateSentenceCallableDetermine justice outcome based on crimes and bounty.
ExecuteSentenceCallableApply the sentence (fine, jail, etc.).
PayFineCallablePay gold to clear bounty without jail.
GrantPardonCallableClear bounty via pardon (faction favor, quest, etc.).
SaveCrimeStateCallableReturns crime save data for serialization.
LoadCrimeStateCallableRestores 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: UPlayerSocialComponent replicates only to its owning client — other players cannot inspect your exact relationship scores without server queries.
  • Range-Based Filtering: Set DefaultReplicationRange to only replicate NPC relationships within N centimeters of each client, critical for open-world performance.
  • Anti-Cheat RPC Validation: MaxRelationshipChangePerRPC limits 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

SettingSingle-PlayerCo-Op (Small)Open World MMO-Style
DefaultReplicationRange0 (disabled)0 (disabled)500000 (5km)
RelationshipUpdateThrottle0.0s0.5s1.0s
bReplicateNPCToNPCRelationshipsN/Afalsefalse (unless UI requires it)
bEnableDeltaCompressionN/Atruetrue
FrameBudgetMs1.0ms0.5ms0.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 bEnableVerboseLogging in Project Settings to log every relationship change (disable before shipping).
  • Enable bLogEventProcessing to track which events are consuming the most propagation budget.
  • Watch QueueSizeWarningThreshold log warnings — if you see these frequently, raise FrameBudgetMs or reduce propagation scope.
  • Use stat DynamicSociety console 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:

RangeNPC PriorityProcessing
< 50mAnyFull processing regardless of priority
50–200mMedium+Normal processing; Low/Negligible go dormant
200–500mHigh+High priority processes; Medium+ go dormant
> 500mCritical onlyOnly 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 MaxMemoryCapacity to 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 DormantPurgeThresholdHours to clean up truly forgotten NPCs.

🔍 Troubleshooting

Common Issues

SymptomLikely CauseFix
Calendar time is not advancingTickCalendar() is not being calledAdd TickCalendar(DeltaTime) to your Game Mode's Tick function, or add a UCalendarTickComponent to a persistent actor.
Calendar is not initialized errorInitializeCalendar() not called before first tickCall InitializeCalendar() with your three data assets before calling TickCalendar() or querying any calendar state.
NPC relationships not changing after eventsNo RelationshipRulesDataAsset assignedCreate 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 subsystemUNPCSocialComponent not on the Actor, or BeginPlay not firingVerify 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 behindFrameBudgetMs too low, or too many events per secondRaise FrameBudgetMs to 1.0–2.0ms. Reduce how frequently you fire relationship events. Raise MinimumImpactThreshold to cut propagation shorter.
Factions not recognized by subsystemFaction assets not in Project Settings arrayOpen Project Settings → WC Dynamic Society → Factions and add your FactionDataAsset references to the array.
Crimes not triggering guard responseNo CrimeDataAsset registered, or crime tag mismatchVerify 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 accumulatingCrime 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 respawnComponent attached to Pawn, not PlayerStateMove the component to APlayerState. Pawns are destroyed on death; PlayerState persists for the session.
Relationship data lost after save/loadPlayerSocialID not restored before BeginPlayCall 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 bountiesNo SocialTierConfigDataAsset assignedCreate a SocialTierConfigDataAsset, define tier hierarchy and multipliers, and assign it in Project Settings → Social Systems → Tiers.
Context tags not appearing on relationshipsContested Memories Phase 6 not configuredContext 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 stutterUsing Instant mode, which fires all events at onceSwitch 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).