add blueprints

This commit is contained in:
Lefteris Notas
2026-05-19 13:22:27 +03:00
parent f71bc678b2
commit 411edea8ce
138 changed files with 23330 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
# 58 — BP_EnemyBase
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`Character`
### Dependencies
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — AI brain
- [`BPC_HealthComponent`](../02-player/08_BPC_HealthComponent.md) — Health & damage
- [`BPC_StaminaComponent`](../02-player/09_BPC_StaminaComponent.md) — Sprint/action energy
- [`BPC_DamageHandlerComponent`](../08-weapons/53_BPC_DamageHandlerComponent.md) — Damage reception
- [`I_Damageable`](../01-core/03_I_Damageable.md) — Interface
- [`I_Persistable`](../05-save/29_I_Persistable.md) — Save state
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Configuration
- [`BP_PatrolPath`](59_BP_PatrolPath.md) — Waypoint navigation
### Purpose
Base enemy character blueprint. Provides movement, animation, damage reaction, ragdoll, and interaction with the AI controller system. Designed to be extended for specific enemy types (humanoid, creature, drone). All AI logic is in the controller; the pawn provides physical representation and animation state.
### Variables
| Name | Type | Description |
|------|------|-------------|
| `AIProfile` | DA_AIProfile | Enemy configuration asset |
| `HealthComponent` | BPC_HealthComponent | Health pool |
| `StaminaComponent` | BPC_StaminaComponent | Stamina for actions |
| `PatrolPathRef` | BP_PatrolPath | Assigned patrol route |
| `SkeletalMesh` | USkeletalMeshComponent | Visual mesh |
| `AnimationBP` | UAnimInstance | Animation blueprint instance |
| `WeakSpotComponents` | Array<UChildActorComponent> | Weak point references |
| `bIsStaggered` | Bool | Hit reaction state |
| `bIsRagdoll` | Bool | Death ragdoll state |
| `RagdollLifetime` | Float | Seconds before ragdoll cleanup |
| `LootTable` | TArray<FLootEntry> | Dropped items on death |
| `AlertColor` | FLinearColor | VFX color matching alert level |
| `FootstepSounds` | TMap<UPhysicalMaterial, USoundBase> | Surface-based footsteps |
| `EnemyTypeTag` | FGameplayTag | e.g. Tag.Enemy.Humanoid |
### Structs
| Struct | Fields | Description |
|--------|--------|-------------|
| `FLootEntry` | ItemTag: FGameplayTag, DropChance: Float, MinCount: Int, MaxCount: Int | Loot definition |
| `FWeakSpot` | BoneName: FName, DamageMultiplier: Float, bDestroyed: Bool, HitVFX: UParticleSystem | Weak point data |
### Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `InitializeEnemy` | Profile: DA_AIProfile | — | Set stats, components, attachment |
| `OnTakeDamage` | DamageResult: FDamageResult | — | React to damage (stagger, VFX) |
| `OnDeath` | — | — | Play death anim or ragdoll |
| `StartRagdoll` | HitLocation: FVector, Impulse: FVector | — | Physics death |
| `EndRagdoll` | — | — | Cleanup and destroy |
| `SpawnLoot` | — | — | Drop items at death location |
| `GetWeakSpotMultiplier` | BoneName: FName | Float | Return damage multiplier |
| `SetEnemyState` | NewState: EAIState | — | Update animation state |
| `PlayFootstep` | Surface: UPhysicalMaterial | — | Footstep sound |
| `GetEyeLocation` | — | FVector | Perception reference point |
| `CanBeStaggered` | — | Bool | Stagger immunity check |
| `PlayStaggerAnimation` | — | — | Hit reaction montage |
| `OnStaggerEnd` | — | — | Return to normal state |
| `SetVisibilityBasedAnimTickOption` | Option: EVisibilityBasedAnimTickOption | — | Performance culling |
### Enums
| Enum | Values | Description |
|------|--------|-------------|
| `EEnemyArchetype` | Humanoid, Creature, Drone, Turret, Boss | Enemy structural type |
| `EHitReactionType` | None, Flinch, Stagger, Knockback, Launch | Reaction severity |
### Blueprint Flow
```
[OnTakeDamage]
└─► ApplyDamage to HealthComponent
└─► If HealthComponent.Health <= 0 → OnDeath
└─► Else:
HitReaction = DetermineReaction(DamageResult)
If HitReaction >= Stagger && CanBeStaggered:
bIsStaggered = true
PlayStaggerAnimation(DamageResult.HitDirection)
SpawnHitVFX(DamageResult.HitLocation)
Notify AIController of damage via dispatcher
[OnDeath]
└─► Set AIState = Disabled on controller
└─► Stop all AI logic
└─► If AIProfile.bUseRagdoll:
StartRagdoll(DamageResult.HitLocation, DamageResult.Impulse)
SetLifeSpan(RagdollLifetime)
└─► Else:
PlayDeathAnimation()
SetLifeSpan(DeathAnimTime)
└─► SpawnLoot()
└─► Broadcast OnEnemyKilled
└─► Unregister from AlertSystem
[SpawnLoot]
└─► For each FLootEntry:
If Random < DropChance:
Count = RandomRange(MinCount, MaxCount)
Spawn pickup actor with ItemTag and Count
```
### Animation State Mapping
| EAIState | Animation State | Notes |
|----------|----------------|-------|
| Idle | Idle | Standing, breathing |
| Patrol | Walk | Speed from profile |
| Suspicious | Walk (slower) | Looking around |
| Alerted | Walk (fast) | Ready stance |
| Combat | Run / Jog | Sprint toward target |
| Searching | Walk (scanning) | Head-turning |
| Fleeing | Sprint | Panic movement |
| Disabled | Stagger / Stun | Hit reaction or downed |
### Communications With
| Target | Method | Why |
|--------|--------|-----|
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Possessing controller | AI state and behavior |
| [`BPC_HealthComponent`](../02-player/08_BPC_HealthComponent.md) | Get Component | Health modify and death |
| [`BPC_DamageHandlerComponent`](../08-weapons/53_BPC_DamageHandlerComponent.md) | Get Component | Process incoming damage |
| [`BPC_CombatFeedbackComponent`](../08-weapons/54_BPC_CombatFeedbackComponent.md) | Get Component | Blood, hit FX |
| Animation Blueprint | Direct | State machine query |
| [`BP_PatrolPath`](59_BP_PatrolPath.md) | Direct | Waypoint navigation |
| World Spawning System | Direct | Loot spawning |
### Reuse Notes
- Subclass for different enemy visuals, hitbox setups, and animation sets
- Weak spots defined per blueprint (head, back weak point, glowing core)
- Loot table data-driven: design new enemies by swapping DA_AIProfile
- Ragdoll vs death animation toggle per enemy type
- AnimBP queries AIState for state-driven animation

View File

@@ -0,0 +1,109 @@
# 59 — BP_PatrolPath
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`Actor`
### Dependencies
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — AI pawn using the path
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Waypoint consumer
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Patrol behavior config
### Purpose
Waypoint-based patrol route for AI characters. Defines a sequence of points (spline or actor array) that AI follows during patrol state. Supports looping, ping-pong, random selection, and conditional stops. Visualized in-editor with spline or point indicators for level designers.
### Variables
| Name | Type | Description |
|------|------|-------------|
| `PatrolPoints` | Array<FVector> | World-space waypoints |
| `PatrolMode` | EPatrolMode | Sequence mode |
| `bIsLooping` | Bool | Restart from first after last |
| `bIsPingPong` | Bool | Reverse direction at end |
| `PointRadius` | Float | Acceptance radius for reaching a point |
| `WaitTimeAtPoint` | Float | Pause at each waypoint (seconds) |
| `bRandomizeOrder` | Bool | Shuffle points on each loop |
| `bUseSpline` | Bool | Draw spline between points |
| `SplineComponent` | USplineComponent | Optional spline path |
| `bDrawDebug` | Bool | Editor visualization toggle |
| `DebugPointColor` | FColor | Point color in editor |
| `ConnectedAI` | Array<BPC_AIControllerBase> | AI using this path |
### Enums
| Enum | Values | Description |
|------|--------|-------------|
| `EPatrolMode` | Sequential, Random, PingPong, Reverse | Point selection order |
### Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `GetNextPatrolPoint` | CurrentIndex: Int | FVector, NewIndex: Int | Next waypoint based on mode |
| `GetPatrolStartPoint` | — | FVector, StartIndex: Int | First point |
| `GetPatrolPointCount` | — | Int | Total points |
| `GetPatrolPointByIndex` | Index: Int | FVector | Specific point |
| `AddPatrolPoint` | Location: FVector | — | Add waypoint |
| `RemovePatrolPoint` | Index: Int | — | Remove waypoint |
| `ClearPatrolPoints` | — | — | Reset path |
| `SetPatrolMode` | Mode: EPatrolMode | — | Change movement pattern |
| `IsPatrolComplete` | CurrentIndex: Int | Bool | End of path |
| `RegisterAIUser` | AIController: BPC_AIControllerBase | — | Track who uses this path |
| `UnregisterAIUser` | AIController: BPC_AIControllerBase | — | Remove usage |
### Blueprint Flow
```
[GetNextPatrolPoint(CurrentIndex)]
└─► If PatrolMode == Sequential:
NewIndex = CurrentIndex + 1
If NewIndex >= PatrolPoints.Num():
If bIsLooping: NewIndex = 0
Else: return Last point and IsPatrolComplete = true
└─► If PatrolMode == Random:
NewIndex = Random integer in range [0, Points.Num - 1]
Exclude CurrentIndex to avoid standing still
└─► If PatrolMode == PingPong:
If going forward:
NewIndex = CurrentIndex + 1
If NewIndex >= PatrolPoints.Num(): reverse direction
If going backward:
NewIndex = CurrentIndex - 1
If NewIndex < 0: reverse direction
└─► If PatrolMode == Reverse:
NewIndex = CurrentIndex - 1
If NewIndex < 0:
If bIsLooping: NewIndex = Points.Num - 1
Else: IsPatrolComplete = true
└─► Return PatrolPoints[NewIndex], NewIndex
```
### Editor Visualization
```
[OnConstruction]
└─► If bDrawDebug:
For each point:
DrawDebugSphere(Point, PointRadius, 12, DebugPointColor)
If bUseSpline:
SplineComponent.SetSplinePoints(PatrolPoints, ESplineCoordinateSpace::World)
Else:
Draw lines between consecutive points
```
### Communications With
| Target | Method | Why |
|--------|--------|-----|
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Direct call | AI requests next waypoint |
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Direct (via controller) | Move-to destination |
| Level Designer | Editor visualization | Place and configure in world |
### Reuse Notes
- World-placed actor, assignable to any AI via DA_AIProfile or direct reference
- Multiple AI can share one patrol path (squad patrol)
- Spline mode allows smooth curved paths (for flying/gliding enemies)
- Works with any AI that reads patrol index from blackboard

View File

@@ -0,0 +1,147 @@
# 60 — BPC_AlertSystem
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`ActorComponent`
### Dependencies
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Owner controller
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn that receives commands
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Alert thresholds and response times
- [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) — Stimulus input source
- [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — State transition triggers
- [`I_Damageable`](../01-foundation/05_I_Damageable.md) — Damage stimulus source
### Purpose
Manages AI threat levels, alert escalation, and reinforcement propagation. Receives stimuli from perception and damage systems, evaluates threat severity, escalates through alert levels (Unaware → Suspicious → Alert → Combat), and notifies nearby AI units. Acts as the central threat-response coordinator for each AI.
### Enums
| Enum | Values | Description |
|------|--------|-------------|
| `EAlertLevel` | Unaware, Suspicious, Alert, Combat | Current alert tier |
| `EAlertCause` | SightStimulus, HearingStimulus, DamageTaken, AllyAlert, Environmental | What triggered the alert |
### Structs
| Struct | Fields | Description |
|--------|--------|-------------|
| `FAlertState` | AlertLevel: EAlertLevel, AlertCause: EAlertCause, LastAlertTime: Float, SourceLocation: FVector, KnownTarget: AActor, bHasValidTarget: Bool, SuspicionValue: Float, CombatTimer: Float | Snapshot of current alert status |
| `FAlertPropagationInfo` | AlertLevel: EAlertLevel, SourceLocation: FVector, Target: AActor, PropagationRadius: Float, Timestamp: Float | Data sent to nearby AI |
### Variables
| Name | Type | Description |
|------|------|-------------|
| `CurrentAlertState` | FAlertState | Live alert tracking |
| `AlertThresholds` | FVector2D | Suspicion threshold X, Combat threshold Y |
| `SuspicionDecayRate` | Float | Points per second lost when no stimulus |
| `SuspicionGrowthRate` | Float | Points per second gained on stimulus |
| `CombatExitDelay` | Float | Time without combat before dropping to Alert |
| `ReinforcementRadius` | Float | Max distance to notify allies |
| `bCanCallReinforcements` | Bool | Permission to propagate |
| `ReinforcementCooldown` | Float | Min seconds between calls |
| `LastReinforcementTime` | Float | Cooldown tracker |
| `bIsInCombat` | Bool | Fast check for combat state |
| `InvestigationTimer` | Float | Time spent investigating last known position |
| `MaxInvestigationTime` | Float | Give up after this long |
### Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `EvaluateStimulus` | StimulusType: EAlertCause, Intensity: Float, Location: FVector, Instigator: AActor | — | Process incoming stimulus |
| `CalculateThreatScore` | StimulusType: EAlertCause, Intensity: Float, Distance: Float | Float | Compute weighted threat value |
| `EscalateAlert` | NewLevel: EAlertLevel, Cause: EAlertCause | — | Raise alert level |
| `DeescalateAlert` | — | — | Lower alert level (decay timer) |
| `UpdateSuspicion` | DeltaTime: Float | — | Tick suspicion up/down |
| `CheckAlertTransition` | — | EAlertLevel | Determine if threshold crossed |
| `EnterCombat` | Target: AActor | — | Transition to combat state |
| `ExitCombat` | — | — | Transition out of combat |
| `CallReinforcements` | — | — | Broadcast alert to allies |
| `ReceiveReinforcementCall` | Info: FAlertPropagationInfo | — | Handle ally alert broadcast |
| `GetCurrentAlertLevel` | — | EAlertLevel | Getter for blackboard updates |
| `GetKnownTarget` | — | AActor | Current combat target |
| `IsInCombat` | — | Bool | Combat state check |
| `SetInvestigationPoint` | Location: FVector | — | Mark last known target position |
| `ClearInvestigationPoint` | — | — | Reset investigation |
| `ShouldGiveUpSearch` | — | Bool | Timeout check for investigation |
### Blueprint Flow
```
[EvaluateStimulus]
└─► Calculate Threat Score
└─► If Score > AlertThresholds.Y --> Escalate to Combat
└─► Else If Score > AlertThresholds.X --> Escalate to Suspicious
└─► Else --> Update SuspicionValue (accumulate)
└─► If AlertLevel >= Suspicious:
Set Investigation Point
If AlertLevel == Combat:
Set KnownTarget
EnterCombat
If bCanCallReinforcements:
CallReinforcements
[Event Tick]
└─► UpdateSuspicion(DeltaTime):
If no stimulus:
SuspicionValue -= SuspicionDecayRate * DeltaTime
If SuspicionValue <= 0:
DeescalateAlert (back to Unaware)
If AlertLevel == Combat:
CombatTimer += DeltaTime
If CombatTimer > CombatExitDelay && no damage/stimulus:
ExitCombat -> set AlertLevel = Alert
If AlertLevel == Suspicious:
InvestigationTimer += DeltaTime
If InvestigationTimer > MaxInvestigationTime:
ClearInvestigationPoint
DeescalateAlert
[CallReinforcements]
└─► If (Time - LastReinforcementTime) < ReinforcementCooldown: return
└─► Get all AI controllers within ReinforcementRadius
└─► For each:
Call ReceiveReinforcementCall with current alert info
└─► LastReinforcementTime = CurrentTime
[ReceiveReinforcementCall]
└─► If CurrentAlertLevel >= Info.AlertLevel: ignore (already higher)
└─► Else: EscalateAlert(Info.AlertLevel, Environmental)
Set InvestigationPoint = Info.SourceLocation
If Info.AlertLevel == Combat:
Set KnownTarget = Info.Target
```
### Event Dispatchers
| Name | Delegate Signature | Purpose |
|------|-------------------|---------|
| `OnAlertLevelChanged` | EAlertLevel, EAlertCause | UI, sound, state machine listeners |
| `OnCombatEntered` | AActor Target | Combat music, HUD, behavior tree |
| `OnCombatExited` | — | Combat cleanup |
| `OnReinforcementCalled` | FAlertPropagationInfo | Debug logging, analytics |
| `OnInvestigationStarted` | FVector Location | Sound/investigation music |
| `OnInvestigationEnded` | — | Return to patrol |
### Communications With
| Target | Method | Why |
|--------|--------|-----|
| [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Call | Request state change |
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Owner reference | Coordinate BB values |
| [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) | Direct call | Get stimulus data |
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Owner reference | Trigger combat animations |
| [`WBP_HUD`](../06-ui/36_WBP_HUD.md) | Event dispatcher | Show combat indicator |
| [`BPC_CombatFeedback`](../08-combat/54_BPC_CombatFeedbackComponent.md) | Event dispatcher | Combat music/vignette |
### Reuse Notes
- Attached to AIControllerBase; profiles tune thresholds per enemy type
- Works with any perception system that produces EAlertCause stimuli
- Reinforcement chain prevents infinite loops by comparing alert levels
- Investigation system handles "last seen" behavior without combat
- Decay system ensures AI eventually returns to patrol state

View File

@@ -0,0 +1,180 @@
# 61 — BPC_AIStateMachine
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`ActorComponent`
### Dependencies
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Owner controller, blackboard access
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Alert level triggers state transitions
- [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) — Stimulus input
- [`BPC_BehaviorTreeManager`](57_BPC_BehaviorTreeManager.md) — Tree lifecycle requests
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn animation state requests
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — State timings, cooldowns, transition rules
### Purpose
High-level finite state machine for AI behavior. Manages valid state transitions, cooldowns, priority overrides, and lifecycle events for each AI state (Patrol, Investigate, Search, Combat, Flee, Idle, Stunned). Interprets alert level changes from the alert system and resolves transitions with rule checking before delegating to the behavior tree manager.
### Enums
| Enum | Values | Description |
|------|--------|-------------|
| `EAIState` | Idle, Patrol, Investigate, Search, Combat, Flee, Stunned, Dead | All possible AI states |
| `EStateTransitionRule` | Allow, Block (Cooldown), Block (Priority), Block (Health), Block (Narrative) | Transition permission results |
| `EStatePriority` | None, Low, Medium, High, Critical | State override priority |
### Structs
| Struct | Fields | Description |
|--------|--------|-------------|
| `FStateConfig` | State: EAIState, bCanTransitionFrom: Array<EAIState>, MinTimeInState: Float, MaxTimeInState: Float, CooldownAfterExit: Float, Priority: EStatePriority, bAllowInterrupt: Bool, AssociatedTree: EBehaviorTreeType | Configuration per state |
| `FStateInstance` | State: EAIState, EnterTime: Float, LastExitTime: Float, bIsActive: Bool, CustomData: FName (tag), bCanInterrupt: Bool | Runtime state tracking |
| `FTransitionRequest` | RequestedState: EAIState, Priority: EStatePriority, Reason: FName, Instigator: AActor | Queued transition |
### Variables
| Name | Type | Description |
|------|------|-------------|
| `CurrentState` | EAIState | Active state |
| `PreviousState` | EAIState | State before last transition |
| `StateMap` | Map<EAIState, FStateConfig> | All state configs from profile |
| `StateInstances` | Map<EAIState, FStateInstance> | Runtime state trackers |
| `PendingTransitions` | Array<FTransitionRequest> | Queue of requested transitions |
| `bIsTransitioning` | Bool | Lock during transition processing |
| `MaxTransitionsPerTick` | Int | Safety limit (default 3) |
| `bLockedByNarrative` | Bool | Story script lock |
| `NarrativeLockReason` | FName | Why narrative locked AI |
| `StunnedDuration` | Float | Current stun timer remaining |
| `FleeThreshold` | Float | Health % below which AI flees |
| `bCanFlee` | Bool | Permission to flee |
### Functions — Core
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `RequestTransition` | NewState: EAIState, Priority: EStatePriority, Reason: FName, Instigator: AActor | Bool | Queue or execute state change |
| `ProcessPendingTransitions` | — | — | Evaluate queue on tick |
| `EvaluateTransitionRules` | FromState: EAIState, ToState: EAIState | EStateTransitionRule | Check if transition is allowed |
| `ForceTransition` | NewState: EAIState | — | Bypass rules (narrative/debug) |
| `GetCurrentState` | — | EAIState | BB getter |
| `GetTimeInCurrentState` | — | Float | Duration in current state |
| `CanTransitionTo` | NewState: EAIState | Bool | Public permission check |
| `IsStateActive` | State: EAIState | Bool | Check any state |
### Functions — State Lifecycle
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `EnterState` | NewState: EAIState, Instigator: AActor | — | State entry logic |
| `ExitState` | CurrentState: EAIState | — | State exit cleanup |
| `UpdateState` | DeltaTime: Float | — | Per-tick state behavior |
| `OnStateEnter_Patrol` | — | — | Setup patrol BB keys |
| `OnStateEnter_Investigate` | Location: FVector | — | Setup investigation |
| `OnStateEnter_Search` | LastKnownLocation: FVector | — | Setup search area |
| `OnStateEnter_Combat` | Target: AActor | — | Combat initialization |
| `OnStateEnter_Flee` | ThreatLocation: FVector | — | Flee setup |
| `OnStateEnter_Stunned` | Duration: Float | — | Stun timer |
| `OnStateEnter_Idle` | — | — | Idle setup |
| `OnStateEnter_Dead` | — | — | Death state |
| `OnStateExit_All` | — | — | Common exit behavior |
### Functions — Transition Rules
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `CheckCooldown` | State: EAIState | Bool | Has cooldown expired |
| `CheckHealthThreshold` | ToState: EAIState | Bool | Health gate check for flee/stun |
| `CheckNarrativeLock` | — | Bool | Is AI locked by script |
| `CheckPriorityOverride` | Request: FTransitionRequest | Bool | Higher priority can interrupt |
| `CheckMinStateTime` | — | Bool | Minimum time in current state met |
### Blueprint Flow — Transition Resolution
```
[RequestTransition]
└─► If bLockedByNarrative AND not ForceTransition: deny (return false)
└─► If bIsTransitioning: queue to PendingTransitions, return false
└─► EvaluateTransitionRules:
Call CheckNarrativeLock --> if blocked, deny
Call CheckCooldown(FromState) --> if active, deny
Call CheckMinStateTime --> if not met, queue or deny
Call CheckPriorityOverride --> if request priority >= current, allow
Else: deny or queue
└─► If Allowed:
bIsTransitioning = true
ExitState(CurrentState)
EnterState(NewState)
Update blackboard
Request BehaviorTree switch from BT Manager
bIsTransitioning = false
Return true
[ProcessPendingTransitions]
└─► Sort PendingTransitions by Priority (high first)
└─► For each (up to MaxTransitionsPerTick):
Try RequestTransition again
If success: remove from queue
If fails again: increment failure counter, remove if > 3 attempts
[Event Tick]
└─► UpdateState(DeltaTime):
Switch CurrentState:
Patrol: Check patrol timer, check patrol completion
Investigate: Update investigation timer, check timeout
Search: Update search timer, check coverage
Combat: Update combat timer, check health for flee
Flee: Update flee timer, check distance from threat
Stunned: StunnedDuration -= DeltaTime; if <= 0, transition
Idle: Update idle timer
Check external triggers:
If bCanFlee && Health < FleeThreshold: RequestTransition(Flee)
If AlertSystem.IsInCombat && CurrentState != Combat: RequestTransition(Combat)
```
### State Transition Map (Allowed)
| From \ To | Idle | Patrol | Investigate | Search | Combat | Flee | Stunned | Dead |
|-----------|------|--------|------------|--------|--------|------|---------|------|
| Idle | — | Yes | Yes | No | Yes | No | Yes | Yes |
| Patrol | Yes | — | Yes | No | Yes | No | Yes | Yes |
| Investigate | Yes | Yes | — | Yes | Yes | No | Yes | Yes |
| Search | Yes | Yes | No | — | Yes | Yes | Yes | Yes |
| Combat | No | No | No | No | — | Yes | Yes | Yes |
| Flee | Yes | Yes | No | No | No | — | Yes | Yes |
| Stunned | Yes | Yes | Yes | No | Yes | Yes | — | Yes |
| Dead | No | No | No | No | No | No | No | — |
### Event Dispatchers
| Name | Delegate Signature | Purpose |
|------|-------------------|---------|
| `OnAIStateChanged` | EAIState NewState, EAIState PreviousState, AActor Instigator | For all listeners |
| `OnStateTransitionBlocked` | EAIState Requested, FStateTransitionRule Reason | Debug logging |
| `OnAIDefeated` | AActor Killer | Death sequence |
| `OnAIFled` | — | Flee started |
| `OnAIStunned` | Float Duration | Stun entered |
| `OnAIStateTimerWarning` | EAIState, Float RemainingTime | State expiry warning |
### Communications With
| Target | Method | Why |
|--------|--------|-----|
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Event listener | Alert level changes trigger transitions |
| [`BPC_BehaviorTreeManager`](57_BPC_BehaviorTreeManager.md) | Direct call | Request tree switch on state enter |
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Owner reference | Update BB keys |
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Event dispatcher | Trigger state animations |
| [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) | Data lookup | Load state configs |
| [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) | Direct call | Get stimulus for investigate |
| Narrative system | Event dispatcher | Narrative lock/unlock |
| [`BPC_HealthComponent`](../01-player/01_BPC_HealthComponent.md) | Event listener | Health threshold checks |
### Reuse Notes
- One per AIController; state configs defined per AI type in DA_AIProfile
- Priority queue prevents infinite loops during rapid state changes
- Narrative lock allows scripted moments where AI cannot react
- Stun state can be triggered by weapons, environment, or narrative events
- Flee uses the same path system as patrol (inverted direction)
- Dead state is terminal — no transitions out (death is hard finality by design for this horror framework)

View File

@@ -0,0 +1,158 @@
# AI_BaseAgentController — AI Controller
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`AIController`
### Dependencies
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn
- [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Sensing
- [`BB_AgentBoard`](BB_AgentBoard.md) — Decision-making
- [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — High-level states
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Threat awareness
- [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Self health
- [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) — Damage reception
- `DA_AIProfile` — AI configuration
### Purpose
Central brain for enemy AI characters. Manages perception, decision-making via Behavior Trees, high-level state machine transitions, alert level propagation, and combat coordination. Possesses `BP_EnemyBase` pawns and controls their actions through Blackboard values and Behavior Tree execution.
### Variables
| Name | Type | Description |
|------|------|-------------|
| `AIProfile` | DA_AIProfile | Configuration asset |
| `PossessedEnemy` | BP_EnemyBase | Current pawn reference |
| `BlackboardComp` | UBlackboardComponent | Blackboard instance |
| `BehaviorTreeComp` | UBehaviorTreeComponent | Behavior tree runner |
| `PerceptionComp` | BPC_AIPerceptionSystem | Sensory input |
| `StateMachine` | BPC_AIStateMachine | State logic |
| `AlertSystem` | BPC_AlertSystem | Threat level |
| `bIsActive` | Bool | Enabled / disabled |
| `AggressionRange` | Float | Engage distance |
| `SuspicionRange` | Float | Investigate distance |
| `CombatRange` | Float | Preferred combat distance |
| `HomeLocation` | FVector | Spawn or patrol anchor |
| `LastKnownPlayerLocation` | FVector | Blackboard updated |
| `bHasLineOfSight` | Bool | LOS status |
| `LostPlayerTimer` | Float | Seconds since last sighting |
### Blackboard Keys
| Key Name | Type | Description |
|----------|------|-------------|
| `SelfActor` | Object | Self reference |
| `TargetActor` | Object | Current threat |
| `TargetLocation` | Vector | Last known threat location |
| `HomeLocation` | Vector | Patrol anchor |
| `AIState` | Enum (EAIState) | Current state |
| `AlertLevel` | Float | 0.01.0 awareness |
| `bHasLOS` | Bool | Line of sight |
| `bIsInvestigating` | Bool | Searching location |
| `PatrolIndex` | Int | Current waypoint |
| `CombatStance` | Enum | Aggressive / Defensive |
| `StimulusLocation` | Vector | Sound/sight trigger point |
### Enums
| Enum | Values | Description |
|------|--------|-------------|
| `EAIState` | Idle, Patrol, Suspicious, Alerted, Combat, Searching, Fleeing, Disabled | AI behavior state |
### Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `OnPossess` | Pawn: APawn | — | Bind blackboard, run BT |
| `OnUnPossess` | — | — | Cleanup |
| `InitializeAI` | Profile: DA_AIProfile | — | Set up components |
| `SetAIState` | NewState: EAIState | — | State machine transition |
| `UpdatePerception` | Stimulus: FAIStimulus | — | Process sensory input |
| `SetTarget` | Target: AActor | — | Update blackboard target |
| `ClearTarget` | — | — | Target lost |
| `StartInvestigation` | Location: FVector | — | Move to investigate |
| `RequestReinforcements` | — | — | Alert nearby allies |
| `GetCombatReadiness` | — | Float | Based on health, ammo, alert |
| `OnTakeDamage` | DamageResult: FDamageResult | — | React to being hit |
| `CanSeeTarget` | — | Bool | LOS check |
| `LostSightOfTarget` | — | — | Start search timer |
| `ReturnToPatrol` | — | — | Reset to patrol state |
| `GetHomeLocation` | — | FVector | Patrol anchor |
| `SetFocalPoint` | Location: FVector | — | Aim facing direction |
### Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|-----------|
| `OnAIStateChanged` | OldState: EAIState, NewState: EAIState | State transition |
| `OnTargetAcquired` | Target: AActor | New threat detected |
| `OnTargetLost` | — | Target out of range/perception |
| `OnAlertRaised` | AlertLevel: Float | Alert threshold crossed |
| `OnReinforcementRequested` | Location: FVector | Call for backup |
### Blueprint Flow
```
[OnPossess]
└─► PossessedEnemy = Cast<BP_EnemyBase>(Pawn)
└─► AIProfile = PossessedEnemy.AIProfile (or set from spawner)
└─► InitializeAI(AIProfile)
└─► RunBehaviorTree(AIProfile.BehaviorTree)
└─► SetHomeLocation(Pawn.GetActorLocation)
[InitializeAI]
└─► Create BPC_AIPerceptionSystem if not exists
└─► Create BPC_AIStateMachine
└─► Create BPC_AlertSystem
└─► PerceptionComp.Initialize(AIProfile.PerceptionConfig)
└─► StateMachine.Initialize(EAIState.Patrol)
└─► AlertSystem.Initialize()
└─► Set AIState = Patrol in Blackboard
[UpdatePerception — called by PerceptionComp dispatcher]
└─► If Stimulus.Type == Sight && Stimulus.bSuccess:
LastKnownPlayerLocation = Stimulus.StimulusLocation
AlertSystem.RaiseAlert(SightAlertValue)
If AlertSystem.AlertLevel >= AIProfile.CombatThreshold:
SetTarget(Stimulus.Instigator)
SetAIState(Combat)
Else:
SetAIState(Suspicious)
└─► If Stimulus.Type == Hearing && Stimulus.bSuccess:
AlertSystem.RaiseAlert(HearingAlertValue)
StartInvestigation(Stimulus.StimulusLocation)
SetAIState(Searching)
└─► If Stimulus.Type == Damage:
SetTarget(Stimulus.Instigator)
AlertSystem.RaiseAlert(DamageAlertValue)
SetAIState(Combat)
RequestReinforcements()
[OnTakeDamage]
└─► AlertSystem.RaiseAlert(MaxAlertValue)
└─► SetTarget(DamageResult.Instigator)
└─► SetAIState(Combat)
```
### Communications With
| Target | Method | Why |
|--------|--------|-----|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Possessed pawn | Movement, animation, abilities |
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Get Component | Sensory stimulus processing |
| [`BB_AgentBoard`](BB_AgentBoard.md) | Get Component | Blackboard updates |
| [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Get Component | State transitions |
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Get Component | Threat level queries |
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get on Pawn | Health events |
| Nearby AI Controllers | Direct / Event | Reinforcement calls |
### Reuse Notes
- Designed as parent for all enemy types (humanoid, creature, drone)
- Perception, alerts, and state machine are swappable components
- Behavior tree is data-driven via AIProfile
- All decision values (ranges, thresholds, speeds) come from data asset
- Renamed from `BPC_AIControllerBase` to `AI_BaseAgentController` per Master naming convention.
- Cross-references updated: `BPC_PerceptionComponent``BPC_AIPerceptionSystem`, `BPC_BehaviorTreeManager``BB_AgentBoard`, `BPC_HealthComponent``BPC_HealthSystem`.

View File

@@ -0,0 +1,123 @@
# BB_AgentBoard — AI Agent Blackboard Asset
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`UBlackboardData` (Blackboard Asset)
### Dependencies
- **Required By:** [`AI_BaseAgentController`](AI_BaseAgentController.md) — Uses this blackboard for all agent state
- **Required By:** [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Writes perception data to blackboard keys
- **Required By:** [`BPC_AIMemorySystem`](BPC_AIMemorySystem.md) — Reads/writes investigate and search locations
- **Required By:** [`BPC_BehaviourVariantSelector`](BPC_BehaviourVariantSelector.md) — Writes PlayerPlaystyleTag key
- **Required By:** Behaviour Tree (`BT_Agent`) — All BT nodes read/write these keys
- **Engine/Plugin Requirements:** AI Module, GameplayTags (for PlayerPlaystyleTag)
### Purpose
Standardised blackboard asset defining all keys used by AI agents. Acts as the shared data contract between perception, memory, behaviour variant selection, and the behaviour tree. Every AI agent using this framework must use this blackboard or a derivative.
---
## 1. Enums
*Uses `E_AIAlertState` from [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md).*
---
## 2. Structs
*No structs defined. Blackboard keys are primitive types.*
---
## 3. Variables — Blackboard Keys
### Standard Keys
| Key | Type | Description |
|-----|------|-------------|
| `TargetActor` | Object | Current pursuit target (player or other threat) |
| `LastKnownTargetLocation` | Vector | Last known position of the target |
| `AlertState` | E_AIAlertState | Current alert level (Unaware, Curious, Suspicious, Alerted, Engaged, Searching) |
| `PatrolTargetIndex` | Integer | Index of current patrol point in the patrol path |
| `bIsInvestigating` | Bool | Whether the agent is currently investigating |
| `InvestigateLocation` | Vector | Where the agent should go to investigate |
| `bTargetLost` | Bool | Whether the agent has lost sight/hearing of the target |
| `PlayerPlaystyleTag` | GameplayTag | Current classified playstyle of the player (Aggressive, Cautious, etc.) |
| `LastHeardLocation` | Vector | Most recent sound event location |
---
## 4. Functions
*Blackboard is data-only. No functions defined at the asset level. Keys are read/written by behaviour tree nodes and perception component.*
---
## 5. Event Dispatchers
*No event dispatchers. Blackboard is a passive data store.*
---
## 6. Overridden Events / Custom Events
*None. This is a data asset, not an executable Blueprint.*
---
## 7. Blueprint Graph Logic Flow
```mermaid
flowchart TD
A[BPC_AIPerceptionSystem sees player] --> B[Write TargetActor = Player]
B --> C[Write LastKnownTargetLocation = Player.Location]
A --> D[EscalateAlertState to Suspicious]
D --> E[Write AlertState = Suspicious]
E --> F[Behaviour Tree reads AlertState]
F --> G[Suspicious → Start investigating]
G --> H[Write bIsInvestigating = true]
H --> I[Write InvestigateLocation = LastKnownTargetLocation]
I --> J[BT MoveTo: InvestigateLocation]
```
---
## 8. Communication Matrix
| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Blackboard write | `TargetActor`, `LastKnownTargetLocation`, `LastHeardLocation`, `AlertState`, `bTargetLost` |
| [`BPC_AIMemorySystem`](BPC_AIMemorySystem.md) | Blackboard write | `InvestigateLocation`, `bIsInvestigating` |
| [`BPC_BehaviourVariantSelector`](BPC_BehaviourVariantSelector.md) | Blackboard write | `PlayerPlaystyleTag` |
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Blackboard ownership | Initialises and holds the blackboard component |
| Behaviour Tree (`BT_Agent`) | Blackboard read/write | All keys — drives BT flow decisions |
| Patrol System (`BP_PatrolPath`) | Blackboard read/write | `PatrolTargetIndex` |
---
## 9. Validation / Testing Checklist
- [ ] All 9 standard keys are present in the blackboard asset
- [ ] Key types match their expected usage (Object for TargetActor, Vector for locations, etc.)
- [ ] E_AIAlertState enum is properly referenced for the AlertState key
- [ ] GameplayTag type is configured for PlayerPlaystyleTag
- [ ] AI controller correctly assigns this blackboard on init
- [ ] Edge case: keys are present before perception system tries to write to them
- [ ] Edge case: behaviour tree graceful when TargetActor is None (cleared on target lost)
---
## 10. Reuse Notes
- This is a project-agnostic standard blackboard. Add project-specific keys in a child blackboard that inherits from this.
- All BT nodes should check key validity (`Is Set`) before reading to avoid errors
- Use `Blackboard Key Selector` pattern in BT tasks to allow designers to select which key to operate on
- New perception types (smell, damage) can write to `LastHeardLocation` or add new keys in a child blackboard
- `PatrolTargetIndex` is incremented by patrol tasks; reset to 0 on patrol path change
---
*Specification based on Master Section 10.2, line 2964.*

View File

@@ -0,0 +1,217 @@
# BPC_AIMemorySystem — AI Memory System
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`ActorComponent`
### Dependencies
- **Requires:** [`BB_AgentBoard`](BB_AgentBoard.md) — Writes `InvestigateLocation`, `bIsInvestigating` keys
- **Requires:** [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Receives sight/sound/damage stimuli
- **Required By:** [`AI_BaseAgentController`](AI_BaseAgentController.md) — Uses memory for investigation decisions
- **Engine/Plugin Requirements:** AI Module, GameplayTags
### Purpose
Gives AI agents persistent short-term memory: where they last saw the player, what sounds they heard, which areas they've searched. Prevents AI from "forgetting" the player immediately on breaking line of sight and avoids re-searching already investigated locations.
---
## 1. Enums
```text
Enum Name: E_MemoryType
(DisplayName = "Memory Type")
Values:
Sighting = 0 // Visual detection of target
Sound = 1 // Heard noise/audio event
Damage = 2 // Received damage
Smell = 3 // Olfactory detection (future/optional)
ScriptedHint = 4 // Designer-placed hint (scripted memory injection)
```
---
## 2. Structs
### `S_AIMemoryEntry`
| Field | Type | Description |
|-------|------|-------------|
| `Location` | Vector | World position of the memory |
| `MemoryType` | E_MemoryType | What triggered this memory |
| `Timestamp` | Float | Game time when memory was recorded |
| `Confidence` | Float | 0.01.0 confidence in this memory (sighting = 1.0, sound = variable) |
---
## 3. Variables
### Configuration (Instance Editable, Expose On Spawn)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `MemoryDecayTime` | Float | 30.0 | MemoryConfig | Seconds before a memory expires and is removed |
| `MaxMemoryEntries` | Integer | 20 | MemoryConfig | Maximum concurrent memory entries |
| `SearchRadius` | Float | 500.0 | MemoryConfig | Min distance between searched locations to consider them distinct |
### Internal (Private / Protected, No Expose)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `MemoryEntries` | Array of S_AIMemoryEntry | Empty | Internal | All active timestamped memory records |
| `SearchedLocations` | Array of Vector | Empty | Internal | World positions already investigated |
---
## 4. Functions
### Public Functions
#### `AddMemoryEntry` → `void`
- **Description:** Records a new memory entry from perception stimulus.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `Location` | Vector | World position of event |
| `MemoryType` | E_MemoryType | What kind of stimulus |
| `Confidence` | Float | How certain the memory is (0.01.0) |
- **Blueprint Authority:** Any
- **Flow:** Create S_AIMemoryEntry → populate fields → append to MemoryEntries → trim if over MaxMemoryEntries → broadcast OnMemoryAdded
#### `GetLatestMemoryOfType` → `S_AIMemoryEntry`
- **Description:** Returns the most recent memory entry of a specific type. Returns empty/invalid entry if none found.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `MemoryType` | E_MemoryType | Filter by type |
- **Blueprint Authority:** Any
#### `GetAllRecentMemories` → `Array of S_AIMemoryEntry`
- **Description:** Returns all non-expired memory entries sorted by timestamp (newest first).
- **Parameters:** None
- **Blueprint Authority:** Any
#### `HasSearchedLocation` → `Bool`
- **Description:** Checks if a location (within SearchRadius) has already been investigated.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `Location` | Vector | World position to check |
- **Blueprint Authority:** Any
#### `MarkLocationSearched` → `void`
- **Description:** Records a location as having been searched.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `Location` | Vector | World position searched |
- **Blueprint Authority:** Any
- **Flow:** Add Location to SearchedLocations → push InvestigateLocation to blackboard if more memories exist
#### `ClearMemories` → `void`
- **Description:** Wipes all memory entries and searched locations. Called on alert reset or agent death.
- **Parameters:** None
- **Blueprint Authority:** Any
#### `TickMemoryDecay` → `void`
- **Description:** Called on tick to remove expired memories based on MemoryDecayTime.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `DeltaTime` | Float | Frame delta time |
- **Flow:** Iterate MemoryEntries → remove those where `(CurrentTime - Timestamp) > MemoryDecayTime`
---
## 5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnMemoryAdded` | Entry: S_AIMemoryEntry | Public | New memory recorded |
| `OnMemoryExpired` | Entry: S_AIMemoryEntry | Public | Memory decayed and removed |
| `OnLocationSearched` | Location: Vector | Public | Location marked as searched |
| `OnAllLocationsSearched` | — | Public | No more unsearched memory locations remain |
---
## 6. Overridden Events / Custom Events
### Event: `ReceivePerceptionStimulus`
- **Description:** Subscribes to [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) dispatchers on BeginPlay. Routes sight, sound, and damage events to memory.
- **Flow:**
1. On sighting: AddMemoryEntry(Location, Sighting, 1.0)
2. On sound: AddMemoryEntry(Location, Sound, 0.50.9 based on distance)
3. On damage: AddMemoryEntry(InstigatorLocation, Damage, 1.0)
4. Write InvestigateLocation to blackboard if not already investigating
---
## 7. Blueprint Graph Logic Flow
```mermaid
flowchart TD
A[BeginPlay] --> B[Subscribe to Perception dispatchers]
B --> C[Idle]
C --> D{Perception event received?}
D --> E[Create S_AIMemoryEntry]
E --> F[Add to MemoryEntries]
F --> G{MemoryEntries count >= Max?}
G -->|Yes| H[Remove oldest entry]
G -->|No| I[Broadcast OnMemoryAdded]
H --> I
I --> J{Agent not investigating?}
J -->|Yes| K[Write InvestigateLocation to BB]
J -->|No| L[Skip — already busy]
K --> L
L --> C
subgraph Tick
M[TickMemoryDecay] --> N{Entry expired?}
N -->|Yes| O[Broadcast OnMemoryExpired]
N -->|No| P[Keep entry]
O --> Q[Remove from MemoryEntries]
end
```
---
## 8. Communication Matrix
| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Dispatcher | Sighting: Location+Confidence, Sound: Location+Volume, Damage: InstigatorLocation |
| [`BB_AgentBoard`](BB_AgentBoard.md) | Blackboard write | `InvestigateLocation`, `bIsInvestigating` |
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Direct (owns) | Tick calls for memory decay |
| Behaviour Tree (`BT_Investigate`) | Direct read | `GetLatestMemoryOfType`, `HasSearchedLocation` |
| [`BPC_EncounterDirector`](../10-adaptive/BPC_ProceduralEncounter.md) | Direct read | Memory entries for encounter escalation decisions |
---
## 9. Validation / Testing Checklist
- [ ] E_MemoryType enum has all 5 values defined
- [ ] S_AIMemoryEntry struct has 4 fields (Location, MemoryType, Timestamp, Confidence)
- [ ] AddMemoryEntry correctly appends and trims to MaxMemoryEntries
- [ ] GetLatestMemoryOfType returns newest entry filtered by type
- [ ] HasSearchedLocation returns true for locations within SearchRadius of a searched point
- [ ] TickMemoryDecay correctly removes entries older than MemoryDecayTime
- [ ] ClearMemories wipes both MemoryEntries and SearchedLocations
- [ ] Edge case: no memories of requested type returns empty struct
- [ ] Edge case: memory decay on empty array does nothing
- [ ] Edge case: rapid perception events don't overflow (MaxMemoryEntries cap works)
---
## 10. Reuse Notes
- MemoryDecayTime can be tuned per archetype (patrol guards forget quickly, stalker remembers longer)
- SearchedLocations prevents AI from repeatedly checking the same hiding spot
- Confidence parameter supports fuzzy memory: sounds at edge of range have lower confidence, AI may not act on low-confidence memories
- Memory system is independent of behaviour tree logic — BT reads from memory API to make decisions
- For co-op/multiplayer, add a shared memory blackboard for squad-level memory
---
*Specification based on Master Section 10.4, line 3012.*

View File

@@ -0,0 +1,138 @@
# BPC_AIPerceptionSystem — AI Perception System
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`ActorComponent`
### Dependencies
- [`AI_BaseAgentController`](AI_BaseAgentController.md) — Owner controller
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Alert level processing
- `DA_AIProfile` — Perception config
- `Engine.AIPerception` — UE5's `UAIPerceptionComponent` (or custom)
### Purpose
Encapsulates all sensory perception for AI: sight, hearing, smell, and damage detection. Processes stimulus data, performs line-of-sight checks, applies detection modifiers (lighting, noise, movement speed), and reports findings to the owner [`AI_BaseAgentController`](AI_BaseAgentController.md). Supports configurable sensor ranges, cone angles, and stimulus decay rates.
### Variables
| Name | Type | Description |
|------|------|-------------|
| `SightRange` | Float | Maximum sight distance (cm) |
| `SightHalfAngle` | Float | Cone half-angle (degrees) |
| `SightLossHalfAngle` | Float | Peripheral angle before losing target |
| `HearingRange` | Float | Maximum hearing distance (cm) |
| `bUseSight` | Bool | Enable sight perception |
| `bUseHearing` | Bool | Enable hearing perception |
| `bUseDamageSense` | Bool | React to being damaged |
| `DetectionMultiplier_Lighting` | Float | 0.52.0 based on ambient light |
| `DetectionMultiplier_Movement` | Float | 1.0 standing, 0.3 crouched, 0.1 still |
| `DetectionMultiplier_Noise` | Float | Noise volume factor |
| `MaxAge` | Float | Seconds before stimulus decays |
| `SightDelay` | Float | Time before initial detection |
| `bTargetInSight` | Bool | Current LOS status |
| `LastSightTime` | Float | Last successful sight check |
| `RegisteredStimuli` | Array<FStimulusInfo> | Current active stimuli |
### Structs
| Struct | Fields | Description |
|--------|--------|-------------|
| `FStimulusInfo` | Type: EStimulusType, Source: AActor, Location: FVector, Strength: Float, Age: Float, bCurrentlyValid: Bool | A perceived event |
### Enums
| Enum | Values | Description |
|------|--------|-------------|
| `EStimulusType` | Sight, Hearing, Damage, Smell, Vibration | Sensory channel |
### Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `InitializePerception` | Config: FPerceptionConfig | — | Set ranges, angles from profile |
| `OnSightStimulus` | Target: AActor | Bool | LOS check with modifiers |
| `OnHearingStimulus` | Location: FVector, NoiseTag: FGameplayTag, Loudness: Float | — | Sound detection |
| `OnDamageStimulus` | Instigator: AActor | — | Direct damage detection |
| `PerformLineOfSightCheck` | Target: AActor | Bool | Raycast with channel |
| `ApplyDetectionModifiers` | BaseChance: Float | Float | Apply darkness, movement, cover |
| `RegisterStimulus` | Info: FStimulusInfo | — | Add to active list |
| `ForgetStimulus` | Target: AActor | — | Clear specific source |
| `GetDominantStimulus` | — | FStimulusInfo | Highest priority active |
| `HasLineOfSightTo` | Target: AActor | Bool | Public wrapper |
| `GetSightStrength` | Target: AActor | Float | 0.01.0 detection confidence |
| `DecayStimuli` | DeltaTime: Float | — | Age all stimuli |
### Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|-----------|
| `OnTargetDetected` | Target: AActor, Strength: Float, Type: EStimulusType | New target detected |
| `OnTargetLost` | Target: AActor | Target out of perception |
| `OnLineOfSightChanged` | Target: AActor, bHasLOS: Bool | LOS state change |
| `OnSoundHeard` | Location: FVector, Loudness: Float | Sound detected |
### Blueprint Flow
```
[OnSightStimulus]
└─► If !bUseSight → return false
└─► If Distance > SightRange → return false
└─► If Angle > SightHalfAngle → return false (outside cone)
└─► // Modifier layer
ModChance = ApplyDetectionModifiers(1.0)
If Random < ModChance → return false (failed detection check)
└─► // LOS check
bHasLOS = PerformLineOfSightCheck(Target)
If !bHasLOS → return false
└─► // Confirmed
Strength = CalculateStrength(Distance, Angle, Modifiers)
FStimulusInfo = { Sight, Target, Location, Strength, 0, true }
RegisterStimulus(Info)
Broadcast OnTargetDetected(Target, Strength, Sight)
Return true
[ApplyDetectionModifiers]
└─► BaseChance *= DetectionMultiplier_Lighting (based on light channel)
└─► BaseChance *= DetectionMultiplier_Movement (based on target velocity)
└─► If target in cover → BaseChance *= 0.3 (partial cover) or 0.0 (full)
└─► Return FMath::Clamp(BaseChance, 0.0, 1.0)
[PerformLineOfSightCheck]
└─► Start = Owner Pawn EyeLocation
└─► End = Target.GetActorLocation + Target BaseEyeHeight
└─► Hit = LineTraceSingleByChannel(Channel = Visibility)
└─► Return Hit.Actor == Target || Hit.Actor == nullptr
```
### Stimulus Priority Table
| Stimulus Type | Base Priority | Decay Rate | Triggers |
|---------------|--------------|-----------|----------|
| Damage | 100 | Instant forget on lost | Combat state |
| Sight (Combat) | 90 | 3 sec | Combat state |
| Sight (Suspicious) | 60 | 8 sec | Suspicious state |
| Hearing (Loud) | 50 | 5 sec | Investigate |
| Hearing (Quiet) | 20 | 10 sec | Brief suspicion |
| Vibration | 30 | 2 sec | Alert nearby |
### Communications With
| Target | Method | Why |
|--------|--------|-----|
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Owner | Send stimulus results |
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Get from Owner | Raise alerts based on perception |
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Owner Pawn | Check eye location, movement speed |
| World | Direct | Line trace queries |
| Audio Manager | Event | Query noise levels |
### Reuse Notes
- Entirely data-driven via DA_AIProfile.PerceptionConfig
- Detection modifiers allow stealth mechanics (darkness, crouch, cover)
- Sight checks use UE5 AIPerception for stimulus system, but LOS logic is custom for accuracy
- Hearing range accepts any gameplay-tagged noise source (footsteps, doors, gunshots)
- Stimulus decay prevents infinite chase after target disappears
- Renamed from `BPC_PerceptionComponent` to `BPC_AIPerceptionSystem` per Master naming convention.
- Cross-references updated: `BPC_AIControllerBase``AI_BaseAgentController`.

View File

@@ -0,0 +1,185 @@
# BPC_BehaviourVariantSelector — Behaviour Variant Selector
## Blueprint Spec — UE 5.55.7
---
### Parent Class
`ActorComponent`
### Dependencies
- **Requires:** [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md) — Subscribes to `OnPlaystyleChanged` for variant selection
- **Requires:** [`BB_AgentBoard`](BB_AgentBoard.md) — Writes `PlayerPlaystyleTag` key
- **Requires:** [`DA_BehaviourVariant`](../14-data-assets/DA_BehaviourVariant.md) — Behaviour variant data assets
- **Required By:** [`AI_BaseAgentController`](AI_BaseAgentController.md) — Owns this component, uses variant for BT configuration
- **Required By:** Behaviour Tree nodes — Read `PlayerPlaystyleTag` from blackboard to adjust behaviour
- **Engine/Plugin Requirements:** GameplayTags
### Purpose
Selects which behaviour variant the AI agent uses based on the player's classified playstyle tag. Adapts AI difficulty, aggression, patrol patterns, investigation behaviour, and reaction times to match the player's approach. Enables dynamic difficulty adjustment without designer intervention.
---
## 1. Enums
*Uses `E_PlaystyleTag` from [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md) — Aggressive, Cautious, Explorer, Passive, Speedrunner, Completionist (used as Gameplay Tags).*
---
## 2. Structs
*No new structs defined. Uses `DA_BehaviourVariant` data asset rows for variant configuration.*
---
## 3. Variables
### Configuration (Instance Editable, Expose On Spawn)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `VariantMap` | Map (GameplayTag → DA_BehaviourVariant) | Empty | BehaviourConfig | Playstyle tag to behaviour variant data asset lookup |
| `DefaultVariant` | DA_BehaviourVariant | None | BehaviourConfig | Fallback variant used when no mapping exists for current playstyle |
### Internal (Private / Protected, No Expose)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `CurrentVariant` | DA_BehaviourVariant | None | Internal | Currently active behaviour variant |
| `bInitialised` | Bool | False | Internal | Whether initial variant has been applied |
---
## 4. Functions
### Public Functions
#### `SelectVariant` → `DA_BehaviourVariant`
- **Description:** Looks up the behaviour variant for a given playstyle tag. Returns DefaultVariant if no mapping exists.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `PlaystyleTag` | GameplayTag | Player's current playstyle classification |
- **Blueprint Authority:** Any
- **Flow:** Check VariantMap for PlaystyleTag → if found, return mapped DA_BehaviourVariant → else return DefaultVariant
#### `ApplyVariant` → `void`
- **Description:** Applies a behaviour variant by writing its properties to the blackboard and configuring agent parameters.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `Variant` | DA_BehaviourVariant | Variant to apply |
- **Blueprint Authority:** Any
- **Flow:** Write variant tag to BB `PlayerPlaystyleTag` → update perception config if variant changes it → update patrol speed if variant changes it → broadcast OnVariantApplied
#### `GetCurrentVariant` → `DA_BehaviourVariant`
- **Description:** Returns the currently active behaviour variant.
- **Parameters:** None
- **Blueprint Authority:** Any
#### `ResetToDefault` → `void`
- **Description:** Reverts to the DefaultVariant regardless of current playstyle. Used when AI is force-reset or despawned.
- **Parameters:** None
- **Blueprint Authority:** Any
---
## 5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnVariantChanged` | OldVariant: DA_BehaviourVariant, NewVariant: DA_BehaviourVariant | Public | Variant selection changed |
| `OnVariantApplied` | Variant: DA_BehaviourVariant | Public | Variant successfully written to blackboard and configured |
---
## 6. Overridden Events / Custom Events
### Event: `BeginPlay`
- **Description:** Subscribes to [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md)`OnPlaystyleChanged` dispatcher. Applies the initial variant.
- **Flow:**
1. Subscribe to BPC_PlaystyleClassifier.OnPlaystyleChanged
2. If BPC_PlaystyleClassifier exists, get CurrentPlaystyleTag
3. SelectVariant(CurrentPlaystyleTag) → ApplyVariant
4. Set bInitialised = true
### Event: `OnPlaystyleChanged`
- **Description:** Called when the player's playstyle classification changes. Re-evaluates variant selection.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `NewPlaystyleTag` | GameplayTag | Updated playstyle classification |
- **Flow:**
1. NewVariant = SelectVariant(NewPlaystyleTag)
2. If NewVariant != CurrentVariant → ApplyVariant(NewVariant)
3. Broadcast OnVariantChanged
---
## 7. Blueprint Graph Logic Flow
```mermaid
flowchart TD
A[BeginPlay] --> B[Subscribe to BPC_PlaystyleClassifier.OnPlaystyleChanged]
B --> C{PlaystyleClassifier exists?}
C -->|Yes| D[Get CurrentPlaystyleTag]
C -->|No| E[Apply DefaultVariant]
D --> F[SelectVariant from VariantMap]
E --> G[Write PlayerPlaystyleTag to BB]
F --> H{Variant found in map?}
H -->|Yes| I[CurrentVariant = mapped DA_BehaviourVariant]
H -->|No| J[CurrentVariant = DefaultVariant]
I --> G
J --> G
G --> K[Broadcast OnVariantApplied]
K --> L[bInitialised = true]
M[OnPlaystyleChanged fires] --> N[SelectVariant new tag]
N --> O{NewVariant != CurrentVariant?}
O -->|Yes| P[ApplyVariant]
O -->|No| Q[No change needed]
P --> R[Broadcast OnVariantChanged]
R --> L
```
---
## 8. Communication Matrix
| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md) | Dispatcher (`OnPlaystyleChanged`) | `NewPlaystyleTag: GameplayTag` |
| [`BB_AgentBoard`](BB_AgentBoard.md) | Blackboard write | `PlayerPlaystyleTag` |
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Direct (owns) | Tick calls, init |
| Behaviour Tree (`BT_Agent`) | Blackboard read | `PlayerPlaystyleTag` → BT switches behaviour subtree |
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Direct (on same agent) | Reconfigure perception radius, reaction time from variant data |
| [`DA_BehaviourVariant`](../14-data-assets/DA_BehaviourVariant.md) | Data asset read | Variant stats: AggressionLevel, PatrolSpeed, ReactionTime, SearchRadius, etc. |
---
## 9. Validation / Testing Checklist
- [ ] VariantMap is populated with entries for all E_PlaystyleTag values
- [ ] DefaultVariant is assigned as fallback
- [ ] SelectVariant returns DefaultVariant when playstyle tag is not in VariantMap
- [ ] ApplyVariant correctly writes PlayerPlaystyleTag to blackboard
- [ ] OnPlaystyleChanged triggers variant re-evaluation
- [ ] OnVariantChanged fires when variant actually changes (not on same variant)
- [ ] ResetToDefault reverts to DefaultVariant correctly
- [ ] Edge case: BPC_PlaystyleClassifier not present → DefaultVariant applied immediately
- [ ] Edge case: rapid playstyle changes → only last variant wins
- [ ] Edge case: invalid DA_BehaviourVariant in map → fall back to DefaultVariant with warning
---
## 10. Reuse Notes
- VariantMap is designer-configurable — each AI archetype can have different playstyle-to-variant mappings
- DefaultVariant ensures AI always has valid behaviour even without a playstyle classifier
- For non-adaptive games, set VariantMap to empty and use only DefaultVariant
- Behaviour tree uses PlayerPlaystyleTag to switch between behaviour subtrees (aggressive chase vs cautious patrol, etc.)
- Extend DA_BehaviourVariant with project-specific fields for combat style, dialogue behaviour, etc.
---
*Specification based on Master Section 10.5, line 3039.*