Enhance documentation with Manual Implementation Guides and Build Checklists

- Updated `CONTEXT.md` to reference the new Manual Implementation Guide and Build Checklist in blueprint spec files.
- Added a detailed Manual Implementation Guide to `01_GI_GameTagRegistry.md`, including class setup, variable initialization, function implementations, and a build checklist.
- Introduced a Manual Implementation Guide section in `22_BPC_PhysicsDragSystem.md` with step-by-step instructions for setup and function logic.
- Expanded `28_BPC_ConsumableSystem.md` with a comprehensive Manual Implementation Guide detailing class setup, variable initialization, function implementations, and networking.
- Enhanced `69_BP_WeaponBase.md` with a Manual Implementation Guide covering class setup, variable defaults, function implementations, and networking.
- Added a Manual Implementation Guide to `84_AI_BaseAgentController.md`, outlining class setup, variable initialization, function implementations, and networking.
- Updated `TEMPLATE.md` to version 2.0, incorporating the Manual Implementation Guide and Build Checklist for human implementers.
- Revised `INDEX.md` to reflect the new purpose and content of blueprint specifications, emphasizing the inclusion of the Manual Implementation Guide.
This commit is contained in:
Lefteris Notas
2026-05-19 17:51:03 +03:00
parent 8bc731e5ae
commit fef25a3363
8 changed files with 882 additions and 19 deletions

View File

@@ -181,7 +181,7 @@ docs/
- **Input Mode Coordination:** `SS_EnhancedInputManager` syncs cursor visibility and input blocking with `SS_UIManager` on menu open/close. - **Input Mode Coordination:** `SS_EnhancedInputManager` syncs cursor visibility and input blocking with `SS_UIManager` on menu open/close.
## Conventions for Blueprint Spec Files ## Conventions for Blueprint Spec Files
Each file in `docs/blueprints/` follows the template defined in `docs/blueprints/TEMPLATE.md`. Files define: Enums, Structs, Variables, Functions, Event Dispatchers, Flow Diagram, Communication Matrix, and Reuse Notes. Each file in `docs/blueprints/` follows the template defined in [`docs/blueprints/TEMPLATE.md`](docs/blueprints/TEMPLATE.md) (v2.0). Files define: Enums, Structs, Variables, Functions, Event Dispatchers, Flow Diagram, Communication Matrix, Reuse Notes, **Manual Implementation Guide** (node-by-node logic for human implementers), and Build Checklist.
## Documentation Update Protocol (CRITICAL RULE) ## Documentation Update Protocol (CRITICAL RULE)
**Any change to the framework MUST also update these files in the same commit:** **Any change to the framework MUST also update these files in the same commit:**

View File

@@ -177,16 +177,106 @@ This asset does not talk to other systems directly. All communication is passive
--- ---
## 14. Blueprint Implementation Steps (for Blueprint Agent) ## 14. Manual Implementation Guide
1. Create a new **Data Asset** class. > **For human implementer:** Follow these steps to build `DA_GameTagRegistry` in UE5.
2. Parent: `UPrimaryDataAsset`
### 14.1 Class Setup
1. Right-click in Content Browser → **Miscellaneous → Data Asset**
2. Select parent class: `PrimaryDataAsset`
3. Name: `DA_GameTagRegistry` 3. Name: `DA_GameTagRegistry`
4. Save to: `/Game/Framework/Core/DA_GameTagRegistry` 4. Save to: `Content/Framework/Core/`
5. Add variables `TagNamespace` (FText) and `bIsFrameworkTag` (bool).
6. Implement the functions listed in Section 6 as Blueprint Callable / Pure nodes. ### 14.2 Variables
7. Document all tag namespaces from Section 10 in the asset's description.
8. Save and run the validation test. Add to Class Defaults:
| Variable | Type | Instance Editable | Default |
|----------|------|------------------|---------|
| `TagNamespace` | `Text` | ✓ | *Framework tag namespace description* |
| `bIsFrameworkTag` | `Boolean` | ✓ | `true` |
There are NO tag entries stored in this asset. Tags live in `Project Settings → GameplayTags` or `DefaultGameplayTags.ini`.
### 14.3 Function Implementations
#### `GetAllRegisteredTags` → `Array of GameplayTag`
**Purpose:** Returns every tag registered in the project.
**Nodes to Use (Blueprint Implementable Function):**
```
[Function: GetAllRegisteredTags]
└─ Get All Gameplay Tags (from GameplayTags subsystem)
└─ Return the array
```
**Node Search:** Right-click → "Get All Gameplay Tags"
#### `GetTagDisplayName(Tag)` → `Text`
**Nodes to Use (Blueprint Pure):**
```
[Function: GetTagDisplayName]
Input Tag → Get Tag Display Name (from GameplayTags)
└─ Return the text
```
**Node Search:** Right-click → "Get Tag Display Name"
#### `ValidateTag(Tag)` → `Boolean`
**Nodes to Use (Blueprint Pure / Callable):**
```
[Function: ValidateTag]
Input Tag → Is Valid Tag (from GameplayTags)
Branch on result:
True → Return true
False → Print Warning: "Invalid Tag: {Tag}" → Return false
```
**Node Search:** Right-click → "Is Gameplay Tag Valid"
#### `LogAllTags` → *(void)*
**Blueprint Callable — Editor Only:**
```
[Function: LogAllTags]
├─ Get All Gameplay Tags → ForEachLoop
│ └─ Print String: Tag.ToString()
└─ Print String: "Total tags: {Array Length}"
```
**Node Search:** Right-click → "ForEachLoop", "Print String"
#### `ExportTagNamespace(NamespacePrefix)` → `String`
**Blueprint Callable:**
```
[Function: ExportTagNamespace]
├─ Get All Gameplay Tags
├─ ForEachLoop:
│ └─ Get Tag Name → ToString
│ └─ Does string start with NamespacePrefix?
│ True → Append to output string with newline
└─ Return output string
```
### 14.4 Networking
No replication needed. This is a read-only Data Asset. All clients load identical copies from disk.
### 14.5 Blueprint Build Checklist
- [ ] Create Data Asset: `DA_GameTagRegistry` (Parent: `PrimaryDataAsset`)
- [ ] Add `TagNamespace` (Text) and `bIsFrameworkTag` (Bool) variables
- [ ] Implement `GetAllRegisteredTags` function (Pure)
- [ ] Implement `GetTagDisplayName` function (Pure)
- [ ] Implement `ValidateTag` function (Pure)
- [ ] Implement `LogAllTags` (Editor-only)
- [ ] Implement `ExportTagNamespace` (Tooling)
- [ ] Document all tag namespaces in the asset's Description field (use Section 10 as reference)
- [ ] Verify: all tag namespaces from Section 10 exist in `DefaultGameplayTags.ini`
--- ---

View File

@@ -101,4 +101,132 @@ ReleaseObject:
## 8. Reuse Notes ## 8. Reuse Notes
- Physics drag uses UE5 physics constraint system for realistic object handling - Physics drag uses UE5 physics constraint system for realistic object handling
- Objects must have `Simulate Physics = true` and `Mass` configured - Objects must have `Simulate Physics = true` and `Mass` configured
---
## 9. Manual Implementation Guide
### 9.1 Class Setup
1. Create Blueprint Class: Parent = `ActorComponent`, Name = `BPC_PhysicsDragSystem`
2. Save to: `Content/Framework/Interaction/`
3. Add to your Player Character.
### 9.2 Variable Initialization (BeginPlay)
```
Event BeginPlay
├─ Set bIsGrabbing = false
├─ Set GrabbedObject = None
├─ Get Owner → Get Component by Class (BPC_InteractionDetector) → Store reference
└─ Get Owner → Get PlayerCameraManager → Store for throw direction
```
### 9.3 Function Implementations
#### `GrabObject(TargetActor: Actor)` → `Boolean`
**Node-by-Node Logic:**
```
[Function: GrabObject]
Step 1: Branch on bIsGrabbing → If true, ReleaseObject first
Step 2: Branch on IsValid(TargetActor) → If not, return false
Step 3: Get TargetActor → Get Component by Class (StaticMeshComponent)
└─ Check: Is Simulate Physics Enabled? → If not, Fire OnGrabFailed("No physics"), return false
Step 4: Check mass: StaticMeshComponent.GetMass() → If > MaxGrabMass, fail
Step 5: Switch on DragMode:
Case PhysicsConstraint:
- Create Physics Constraint component (spawn dynamically)
- ConstraintComp.SetConstrainedComponents(StaticMeshComponent, None, PlayerHandBone)
- Set Linear limits: X=Locked, Y=Locked, Z=Locked (or Free for dangling)
- Set Angular limits: Free (can rotate), or limited
Case Kinematic:
- StaticMeshComponent.SetSimulatePhysics(false)
- StaticMeshComponent.AttachToComponent(PlayerHandSocket)
Case Magnetic:
- Store target; handle in Tick with Lerp toward hand position
Step 6: Set GrabbedObject = TargetActor
Step 7: Set bIsGrabbing = true
Step 8: Fire OnObjectGrabbed(TargetActor)
Step 9: Return true
```
**Nodes Used:** `IsValid`, `Get Component by Class (StaticMeshComponent)`, `Is Simulate Physics Enabled`, `Get Mass`, `Create Constraint`, `SetConstrainedComponents`, `AttachToComponent`, `Branch`
#### `ReleaseObject(bThrow: Boolean)` → `void`
```
[Function: ReleaseObject]
Step 1: Branch on bIsGrabbing → If false, return
Step 2: Branch on bThrow AND bCanThrow:
True:
- Get PlayerCameraManager → Get Forward Vector
- Multiply forward vector by ThrowForce
- GrabbedObject.StaticMeshComponent.AddImpulse(throw vector)
- OR: if ConstraintComp exists → BreakConstraint, then AddImpulse
False:
- Just release without force
Step 3: Switch on DragMode:
- PhysicsConstraint: Destroy ConstraintComp
- Kinematic: DetachFromComponent, SetSimulatePhysics(true)
- Magnetic: Clear target reference
Step 4: Set GrabbedObject = None
Step 5: Set bIsGrabbing = false
Step 6: Fire OnObjectReleased(GrabbedObject, bThrow)
```
**Nodes Used:** `Get Forward Vector`, `Multiply (Vector * Float)`, `Add Impulse`, `Break Constraint`, `Destroy Component`, `Detach From Component`, `Set Simulate Physics`
#### `RotateObject(YawInput: Float, PitchInput: Float, RollInput: Float)` → `void`
```
[Function: RotateObject]
Step 1: Branch on bIsGrabbing → If false, return
Step 2: Create Rotator from inputs (use RotationSpeed multiplier)
- NewRotation = Make Rotator(PitchInput * RotationSpeed * DeltaTime, YawInput * RotationSpeed * DeltaTime, RollInput * RotationSpeed * DeltaTime)
Step 3: Switch on DragMode:
- PhysicsConstraint: GrabbedObject.AddLocalRotation(NewRotation)
- Kinematic: GrabbedObject.AddRelativeRotation(NewRotation)
- Magnetic: Rotate the lerp target
```
#### `SetHoldDistance(NewDistance: Float)` → `void`
```
[Function: SetHoldDistance]
Step 1: Clamp NewDistance between MinHoldDistance and MaxHoldDistance
Step 2: Set HoldDistance = NewDistance
Step 3: If DragMode == PhysicsConstraint:
- ConstraintComp.SetLinearPositionTarget(PlayerCamera.ForwardVector * HoldDistance)
Step 4: If DragMode == Kinematic:
- Set Relative Location (X = HoldDistance, Y = 0, Z = 0) on attached component
```
### 9.4 Event Dispatcher Bindings
| Bind to Dispatcher | Custom Event | Logic |
|-------------------|-------------|-------|
| Input Action: `IA_Grab` (Pressed) | `OnGrabPressed` | Get InteractionDetector.CurrentTarget → Call GrabObject |
| Input Action: `IA_Grab` (Released) | `OnGrabReleased` | Call ReleaseObject(true) |
| `BPC_InteractionDetector.OnTargetFound` | `OnGrabTargetFound(Target)` | Highlight grabbable object outline |
### 9.5 Networking
| Variable | Replication |
|----------|-------------|
| `bIsGrabbing` | `Replicated` |
| `GrabbedObject` | `Replicated` (Actor reference) |
**Server RPC:** `Server_GrabObject(TargetActor)` — server validates range, spawns constraint. Physics is server-authoritative; client predicts locally.
### 9.6 Blueprint Build Checklist
- [ ] Create BPC_PhysicsDragSystem component, add to Player Character
- [ ] Add all variables: DragMode, GrabbedObject, ConstraintComp, GrabDistance, HoldDistance, ThrowForce, RotationSpeed, bCanThrow, bIsGrabbing
- [ ] Implement GrabObject with three drag modes (PhysicsConstraint, Kinematic, Magnetic)
- [ ] Implement ReleaseObject with throw mechanic
- [ ] Implement RotateObject with input axis mapping
- [ ] Implement SetHoldDistance with scroll wheel input
- [ ] Bind to IA_Grab input action (pressed/released)
- [ ] Add MaxGrabMass config to prevent lifting impossibly heavy objects
- [ ] Test: grab crate, rotate, throw — physics should feel natural

View File

@@ -59,4 +59,137 @@
## 7. Reuse Notes ## 7. Reuse Notes
- All consumable effects are data-driven via DA_ItemData - All consumable effects are data-driven via DA_ItemData
- Buffs support stacking (multiple buffs active simultaneously) - Buffs support stacking (multiple buffs active simultaneously)
---
## 8. Manual Implementation Guide
### 8.1 Class Setup
1. Create Blueprint Class: Parent = `ActorComponent`, Name = `BPC_ConsumableSystem`
2. Save to: `Content/Framework/Inventory/`
3. Add to your Player Character or Player Controller
### 8.2 Variable Initialization (BeginPlay)
```
Event BeginPlay
├─ Set ConsumableCooldown = 1.5 (or expose as config)
├─ Set bCanUseConsumables = true
├─ Set ActiveBuffs = empty array
├─ Get Owner → Find Component by Class (BPC_InventorySystem) → Store reference
│ └─ If found: Bind to BPC_InventorySystem.OnItemUsed → OnInventoryItemUsedHandler
└─ Start a looping timer (0.1s) for TickBuffs
```
### 8.3 Function Implementations
#### `UseConsumable(SlotIndex: Integer)` → `Boolean`
**Node-by-Node Logic:**
```
[Function: UseConsumable]
Step 1: Branch on bCanUseConsumables → If false, return false
Step 2: Get InventorySystem reference → Call GetItemAtSlot(SlotIndex) → returns S_InventoryEntry
Step 3: Branch on IsValid(ItemData) → If not valid, return false
Step 4: Check ItemData.ItemType == Consumable → If not, return false
Step 5: Read ItemData.ConsumableData → get effect values:
- HealthRestore, StaminaRestore, StressReduce, SpeedBuff, DamageBuff, BuffDuration
Step 6: Apply effects (branch on each value > 0):
- If HealthRestore > 0: Get BPC_HealthSystem → Call ApplyHealing(HealthRestore)
- If StaminaRestore > 0: Get BPC_StaminaSystem → Call RestoreStamina(StaminaRestore)
- If StressReduce > 0: Get BPC_StressSystem → Call RemoveStress(StressReduce)
- If SpeedBuff > 0 AND BuffDuration > 0: Call ApplyBuff(SpeedBuffTag, BuffDuration)
- If DamageBuff > 0 AND BuffDuration > 0: Call ApplyBuff(DamageBuffTag, BuffDuration)
Step 7: If any buff applied: add to ActiveBuffs array with RemainingDuration = BuffDuration
Step 8: Fire OnConsumableUsed (pass the InventoryEntry)
Step 9: Start ConsumableCooldown timer → set bCanUseConsumables = false
└─ On timer end → set bCanUseConsumables = true
Step 10: Return true
```
**Nodes Used:** `Branch`, `GetItemAtSlot`, `IsValid`, `Switch on E_ItemType`, `ApplyHealing`, `RestoreStamina`, `RemoveStress`, `Call ApplyBuff`, `Set Timer by Event`
#### `ApplyBuff(BuffTag: GameplayTag, Duration: Float, Stat: E_PlayerStat, SourceItem: DA_ItemData)` → `void`
```
[Function: ApplyBuff]
Step 1: Create S_ActiveBuff struct:
- BuffTag = input tag
- RemainingDuration = Duration
- BuffData = SourceItem
- AffectedStat = Stat
Step 2: Check if buff with same BuffTag already in ActiveBuffs:
- If yes: update RemainingDuration (refresh)
- If no: Add to ActiveBuffs array
Step 3: Apply stat modification:
- Switch on AffectedStat:
- Health: NOT here (buffs are temporary, not direct heal)
- Stamina: Get BPC_StaminaSystem → Call BlockRegen(0) or similar (buff handles in tick)
- Speed: Get BPC_MovementStateSystem → Call ApplyMovementPenalty(1.0 + buffAmount, Duration)
- Stress: Get BPC_StressSystem → Call AddStressSource(...)
- Damage: Store as damage multiplier (read during damage calc)
Step 4: Fire OnBuffApplied(BuffTag, Duration)
```
#### `RemoveBuff(BuffTag: GameplayTag)` → `void`
```
[Function: RemoveBuff]
Step 1: Find buff in ActiveBuffs by BuffTag → ForEachLoop with Break
Step 2: If found:
- Remove from array
- Reverse any active stat modification
- Fire OnBuffExpired(BuffTag)
```
#### `TickBuffs` → `void` *(Timer callback, runs every 0.1s)*
```
[Function: TickBuffs]
Step 1: ForEach ActiveBuffs (iterate backwards if removing):
- RemainingDuration -= 0.1
- Branch on RemainingDuration <= 0:
- True → Call RemoveBuff(BuffTag)
Step 2: If ActiveBuffs is empty after cleanup: stop the timer (optional)
```
#### `GetActiveBuffs` → `Array<S_ActiveBuff>`
```
[Function: GetActiveBuffs] (Pure)
└─ Return ActiveBuffs array
```
#### `HasBuff(BuffTag: GameplayTag)` → `Boolean`
```
[Function: HasBuff] (Pure)
Step 1: ForEach ActiveBuffs:
- If ActiveBuffs[i].BuffTag == BuffTag → Return true
Step 2: Return false
```
### 8.4 Event Dispatcher Bindings
| Bind to Dispatcher | Custom Event | Logic |
|-------------------|-------------|-------|
| `BPC_InventorySystem.OnItemUsed` | `OnInventoryItemUsedHandler(Item, Instigator)` | Check ItemData.ItemType == Consumable → Call UseConsumable(SlotIndex) |
### 8.5 Networking
| Variable | Replication | Notes |
|----------|-------------|-------|
| `ActiveBuffs` | `Replicated Using OnRep_ActiveBuffs` | OnRep reapplies visual effects |
| `bCanUseConsumables` | `Replicated` | Server controls cooldown |
**Server RPC:** `Server_UseConsumable(SlotIndex)` — Client calls, server validates item exists/cooldown, applies effects.
### 8.6 Blueprint Build Checklist
- [ ] Create BPC_ConsumableSystem component
- [ ] Add ConsumableCooldown, ActiveBuffs, bCanUseConsumables variables
- [ ] Define S_ActiveBuff struct in the component
- [ ] Implement UseConsumable with effect routing to Health/Stamina/Stress/Movement
- [ ] Implement ApplyBuff / RemoveBuff with ActiveBuffs array management
- [ ] Create TickBuffs timer (0.1s loop) for buff duration countdown
- [ ] Bind to BPC_InventorySystem.OnItemUsed
- [ ] Add networking: Replicated ActiveBuffs, Server_UseConsumable RPC

View File

@@ -146,4 +146,205 @@ Base class for all weapon actors (ranged and melee). Provides common weapon life
| Owner (Player Character) | Direct | Animation blueprint queries weapon state | | Owner (Player Character) | Direct | Animation blueprint queries weapon state |
### Reuse Notes ### Reuse Notes
Subclass for ranged or melee specific fire implementations. The state machine prevents firing during reload, equip, or holster sequences. Weapons are data-driven via `DA_WeaponData` — design changes never require blueprint edits. Subclass for ranged or melee specific fire implementations. The state machine prevents firing during reload, equip, or holster sequences. Weapons are data-driven via `DA_WeaponData` — design changes never require blueprint edits.
---
## Manual Implementation Guide
### Class Setup
1. Create Blueprint Class: Parent = `Actor`, Name = `BP_WeaponBase`
2. Save to: `Content/Framework/Weapons/`
3. Add components in Construction Script:
- `WeaponRoot` (SceneComponent) — root
- `SkeletalMesh` (SkeletalMeshComponent) — attached to root
4. Set `Replicates` = true in Class Defaults
### Variable Defaults (Class Defaults)
| Variable | Type | Default |
|----------|------|---------|
| `WeaponState` | `EWeaponState` | `Holstered` |
| `HolsterSocket` | `Name` | `weapon_holster` |
| `EquipSocket` | `Name` | `weapon_equip` |
| `AimDownSightsFOV` | `Float` | `55.0` |
| `DefaultFOV` | `Float` | `90.0` |
| `ADSInterpSpeed` | `Float` | `10.0` |
| `bIsAiming` | `Boolean` | `false` |
| `bCanFire` | `Boolean` | `true` |
| `EquipDuration` | `Float` | `0.5` |
| `HolsterDuration` | `Float` | `0.5` |
| `RecoilPitch` | `Float` | `2.0` |
| `RecoilYaw` | `Float` | `0.5` |
### Function Implementations
#### `Equip(CharacterOwner: Character)` → `void`
```
[Function: Equip]
Step 1: Branch on WeaponState == Holstered → If not, return (can't equip twice)
Step 2: SetWeaponState(Equipping)
Step 3: AttachToComponent(CharacterOwner.Mesh, EquipSocket)
- Set Relative Location = (0,0,0)
- Set Relative Rotation = (0,0,0)
Step 4: Set Actor Hidden In Game = false
Step 5: Play equip animation on SkeletalMesh (if montage exists)
Step 6: Start timer for EquipDuration:
└─ On timer complete:
- SetWeaponState(Ready)
- Set bCanFire = true
- Fire OnWeaponEquipped(Self)
```
**Nodes:** `Switch on EWeaponState`, `Attach Actor to Component`, `Set Actor Hidden In Game`, `Play Animation`, `Set Timer by Event`
#### `Holster()` → `void`
```
[Function: Holster]
Step 1: Branch on WeaponState == Ready OR Firing → If not, return
Step 2: If WeaponState == Firing: StopFire first
Step 3: SetWeaponState(Holstering)
Step 4: Play holster animation on SkeletalMesh
Step 5: Start timer for HolsterDuration:
└─ On timer complete:
- AttachToComponent(CharacterOwner.Mesh, HolsterSocket)
- SetWeaponState(Holstered)
- Fire OnWeaponHolstered(Self)
```
#### `StartFire()` → `void`
```
[Function: StartFire]
Step 1: Branch on WeaponState == Ready (only fire from Ready state):
False → Return
Step 2: Call CanFire() → Branch on result:
False → Return
Step 3: SetWeaponState(Firing)
Step 4: Fire OnFireStarted
Step 5: Call OnFire() — this is the subclass override point (virtual function)
Step 6: Switch on WeaponData.FireMode:
Case SemiAutomatic:
- Set bCanFire = false
- Set timer by WeaponData.FireRate → on complete: Set bCanFire = true
Case FullAutomatic:
- Start looping timer (interval = WeaponData.FireRate)
- Each tick: Call OnFire()
Case BurstFire:
- Loop 3 times with WeaponData.FireRate delay between each
- On burst complete: SetWeaponState(Ready)
```
**Important:** `OnFire()` is a **BlueprintImplementableEvent** — it has NO body in BP_WeaponBase. Subclasses (BP_RangedWeapon, BP_MeleeWeapon) override it with their own fire logic.
#### `StopFire()` → `void`
```
[Function: StopFire]
Step 1: Branch on WeaponState == Firing:
False → Return
Step 2: If FireMode == FullAutomatic OR BurstFire:
- Clear all FireRate timers
Step 3: SetWeaponState(Ready)
Step 4: Fire OnFireStopped
```
#### `CanFire()` → `Boolean`
```
[Function: CanFire] (Pure)
Step 1: Branch on WeaponState == Ready → If not, return false
Step 2: Branch on bCanFire → If not, return false (on cooldown)
Step 3: Get AmmoComponent from Owner → Call HasAmmo()
- If no ammo: return false (subclass may trigger auto-reload)
Step 4: Get Owner → Get BPC_StateManager → Call IsActionPermitted(GameplayTag: "Action.Fire")
- If not permitted: return false
Step 5: Return true
```
#### `StartReload()` → `void`
```
[Function: StartReload]
Step 1: Branch on WeaponState == Ready:
False → Return (can't reload while firing/holstered)
Step 2: Branch on AmmoComponent.IsMagazineFull():
True → Return (no need to reload)
Step 3: Branch on AmmoComponent.HasReserveAmmo():
False → Return (no ammo to load)
Step 4: SetWeaponState(Reloading)
Step 5: Fire OnReloadStarted
Step 6: Play reload animation montage:
- Use AnimNotify "OnReloadComplete" at the ammo-insert frame
- AnimNotify handler: call FinishReload()
```
#### `FinishReload()` → `void`
```
[Function: FinishReload]
Step 1: Get AmmoComponent → Call RefillAmmo(MagazineSize from WeaponData)
Step 2: SetWeaponState(Ready)
Step 3: Fire OnReloadCompleted
```
#### `StartAim()` → `void`
```
[Function: StartAim]
Step 1: Set bIsAiming = true
Step 2: Get Owner → Get PlayerController → Get PlayerCameraManager
Step 3: Set PlayerCameraManager Field of View to AimDownSightsFOV with interp speed
Step 4: Fire OnAimStarted
```
#### `StopAim()` → `void`
```
[Function: StopAim]
Step 1: Set bIsAiming = false
Step 2: Get PlayerCameraManager → Set FOV to DefaultFOV with interp
Step 3: Fire OnAimStopped
```
#### `SetWeaponState(NewState: EWeaponState)` → `void`
```
[Function: SetWeaponState]
Step 1: OldState = WeaponState
Step 2: If NewState == OldState → Return
Step 3: WeaponState = NewState
Step 4: Mark variable dirty (for replication)
Step 5: Fire OnWeaponStateChanged(NewState, OldState)
```
### Event Dispatcher Bindings
| Bind to Dispatcher | Custom Event | Logic |
|-------------------|-------------|-------|
| `IA_Fire` (Pressed) | `OnFirePressed` | Call StartFire() |
| `IA_Fire` (Released) | `OnFireReleased` | Call StopFire() |
| `IA_Reload` (Pressed) | `OnReloadPressed` | Call StartReload() |
| `IA_Aim` (Pressed) | `OnAimPressed` | Call StartAim() |
| `IA_Aim` (Released) | `OnAimReleased` | Call StopAim() |
### Networking
- **WeaponState** replicated with `OnRep_WeaponState` → plays matching animation on remote clients
- **All state transitions** are server-authoritative via `Server_StartFire`, `Server_StartReload`, etc.
- Client predicts: fire animation, muzzle flash, recoil (cosmetic only)
- Server validates: ammo count, fire rate cooldown, state machine transitions
### Blueprint Build Checklist
- [ ] Create BP_WeaponBase actor with WeaponRoot + SkeletalMesh components
- [ ] Add EquipmentSocket component on Player Character (weapon_equip, weapon_holster)
- [ ] Add all variables with correct types and defaults
- [ ] Implement Equip/Holster with attach/detach and animation timers
- [ ] Implement StartFire/StopFire with fire rate timer loop
- [ ] Create OnFire as BlueprintImplementableEvent (empty — subclasses override)
- [ ] Implement CanFire checks: state, cooldown, ammo, StateManager permission
- [ ] Implement StartReload/FinishReload with animation notify
- [ ] Implement StartAim/StopAim with FOV interpolation
- [ ] Implement SetWeaponState with old state tracking and dispatcher
- [ ] Bind all input actions (Fire, Reload, Aim)
- [ ] Add networking: replicated WeaponState, Server_ RPCs for all mutators

View File

@@ -155,4 +155,208 @@ Central brain for enemy AI characters. Manages perception, decision-making via B
- Behavior tree is data-driven via AIProfile - Behavior tree is data-driven via AIProfile
- All decision values (ranges, thresholds, speeds) come from data asset - All decision values (ranges, thresholds, speeds) come from data asset
- Renamed from `BPC_AIControllerBase` to `AI_BaseAgentController` per Master naming convention. - 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`. - Cross-references updated: `BPC_PerceptionComponent``BPC_AIPerceptionSystem`, `BPC_BehaviorTreeManager``BB_AgentBoard`, `BPC_HealthComponent``BPC_HealthSystem`.
---
## Manual Implementation Guide
### Class Setup
1. Create Blueprint Class: Parent = `AIController`, Name = `AI_BaseAgentController`
2. Save to: `Content/Framework/AI/`
3. In Class Defaults: set `Auto Possess AI` = `Placed in World or Spawned`
### Variables (Add to Class Defaults)
| Variable | Type | Default | Instance Editable |
|----------|------|---------|-------------------|
| `AIProfile` | `DA_AIProfile` | None | ✓ |
| `PossessedEnemy` | `BP_EnemyBase` (Object Ref) | None | |
| `BlackboardComp` | `Blackboard Component` (Object Ref) | None | |
| `BehaviorTreeComp` | `Behavior Tree Component` (Object Ref) | None | |
| `PerceptionComp` | `BPC_AIPerceptionSystem` (Object Ref) | None | |
| `StateMachine` | `BPC_AIStateMachine` (Object Ref) | None | |
| `AlertSystem` | `BPC_AlertSystem` (Object Ref) | None | |
| `bIsActive` | `Boolean` | `true` | ✓ |
| `AggressionRange` | `Float` | `1500.0` | ✓ |
| `SuspicionRange` | `Float` | `3000.0` | ✓ |
| `CombatRange` | `Float` | `800.0` | ✓ |
| `HomeLocation` | `Vector` | `(0,0,0)` | |
| `LastKnownPlayerLocation` | `Vector` | `(0,0,0)` | |
| `LastKnownPlayerLocation` | `Vector` | `(0,0,0)` | |
| `bHasLineOfSight` | `Boolean` | `false` | |
| `LostPlayerTimer` | `Float` | `0.0` | |
### Function Implementations
#### `Event OnPossess(Pawn: Pawn)`
```
[Event OnPossess]
Step 1: Call Parent OnPossess
Step 2: Cast Pawn to BP_EnemyBase → Set PossessedEnemy
Step 3: Get AIProfile from PossessedEnemy (or a spawner data)
Step 4: Get Blackboard Component → Set BlackboardComp
Step 5: Get Behavior Tree Component → Set BehaviorTreeComp
Step 6: Get Component by Class (BPC_AIPerceptionSystem) → Set PerceptionComp
└─ If not found on Pawn: Create and attach dynamically
Step 7: Get Component by Class (BPC_AIStateMachine) → Set StateMachine
Step 8: Get Component by Class (BPC_AlertSystem) → Set AlertSystem
Step 9: Set HomeLocation = Pawn.GetActorLocation()
Step 10: Set Blackboard value "SelfActor" to Self
Step 11: Set Blackboard value "HomeLocation" to HomeLocation
Step 12: Set Blackboard value "AIState" to EAIState::Patrol
Step 13: Run Behavior Tree (AIProfile.BehaviorTree)
Step 14: PerceptionComp.Initialize(AIProfile.PerceptionConfig)
Step 15: StateMachine.Initialize(EAIState::Patrol)
Step 16: AlertSystem.Initialize()
```
**Nodes:** `Cast To BP_EnemyBase`, `Get Blackboard`, `Set Value as Object/Vector/Enum`, `Run Behavior Tree`, `Create Component (if needed)`, `Get Actor Location`
#### `Event OnUnPossess(Pawn: Pawn)`
```
[Event OnUnPossess]
Step 1: Stop Behavior Tree
Step 2: Clear Blackboard values (TargetActor = None, AIState = Disabled)
Step 3: PerceptionComp.Deinitialize()
Step 4: Call Parent OnUnPossess
```
#### `UpdatePerception(Stimulus: AIStimulus)` → `void`
*This is called by BPC_AIPerceptionSystem dispatcher when any sense triggers.*
```
[Function: UpdatePerception]
Step 1: Branch on Stimulus.WasSuccessfullySensed()
False → Return (ignore failed perception)
Step 2: Switch on Stimulus.Type:
Case Sight:
Step 2a: Set LastKnownPlayerLocation = Stimulus.StimulusLocation
Step 2b: AlertSystem.RaiseAlert(SightAlertValue from AIProfile)
Step 2c: Branch on AlertSystem.AlertLevel >= AIProfile.CombatThreshold:
True:
- SetTarget(Stimulus.StimulusLocation → find closest Actor at that location)
- SetAIState(EAIState::Combat)
- Set Blackboard "bHasLOS" = true
False:
- SetAIState(EAIState::Suspicious)
- Set Blackboard "StimulusLocation" = Stimulus.StimulusLocation
- Set Blackboard "bIsInvestigating" = true
Case Hearing:
Step 3a: AlertSystem.RaiseAlert(HearingAlertValue from AIProfile)
Step 3b: StartInvestigation(Stimulus.StimulusLocation)
Step 3c: SetAIState(EAIState::Searching)
Step 3d: Set Blackboard "StimulusLocation" = Stimulus.StimulusLocation
Case Damage:
Step 4a: SetTarget(Stimulus.StimulusLocation → find instigator)
Step 4b: AlertSystem.RaiseAlert(MaxAlertValue)
Step 4c: SetAIState(EAIState::Combat)
Step 4d: Call RequestReinforcements() if team-aware
Step 5: Fire OnTargetAcquired(Stimulus.StimulusLocation nearest Actor) if target found
```
**Nodes:** `Switch on EAIPerceptionSense`, `RaiseAlert`, `Set Blackboard Value`, `Branch`, `Find Nearest Actor` or `Get Actor at Location`
#### `SetAIState(NewState: EAIState)` → `void`
```
[Function: SetAIState]
Step 1: Get old state from Blackboard "AIState"
Step 2: If NewState == OldState → Return
Step 3: Set Blackboard "AIState" = NewState
Step 4: Call StateMachine.SetState(NewState)
Step 5: Fire OnAIStateChanged(OldState, NewState)
Step 6: If NewState == Combat: Call RequestReinforcements()
```
#### `SetTarget(Target: Actor)` → `void`
```
[Function: SetTarget]
Step 1: Set Blackboard "TargetActor" = Target
Step 2: If Target is valid:
- Set Blackboard "TargetLocation" = Target.GetActorLocation()
- SetFocus(Target)
- Fire OnTargetAcquired(Target)
Else:
- ClearFocus()
- Fire OnTargetLost()
```
#### `StartInvestigation(Location: Vector)` → `void`
```
[Function: StartInvestigation]
Step 1: Set Blackboard "bIsInvestigating" = true
Step 2: Set Blackboard "TargetLocation" = Location
Step 3: Call AI MoveTo(Location)
Step 4: On Move Completed:
- If target not found at location:
- Set Blackboard "bIsInvestigating" = false
- ReturnToPatrol()
```
#### `RequestReinforcements()` → `void`
```
[Function: RequestReinforcements]
Step 1: Get all AI_BaseAgentController in radius (ReinforcementRange from AIProfile)
Step 2: ForEach nearby AI:
- If AI.StateMachine.CurrentState == Patrol OR Suspicious:
- Call AI.SetTarget(Self.GetBlackboard("TargetActor"))
- Call AI.SetAIState(Alerted)
Step 3: Fire OnReinforcementRequested(Self.GetActorLocation())
```
**Nodes:** `Get All Actors of Class (AI_BaseAgentController)`, `ForEachLoop`, `Get Distance To`, `Branch`, `Call SetTarget/SetAIState on other AI`
#### `LostSightOfTarget()` → `void`
```
[Function: LostSightOfTarget]
Step 1: Set Blackboard "bHasLOS" = false
Step 2: Start LostPlayerTimer (timer loops 0.1s)
└─ Each tick: LostPlayerTimer += 0.1
Step 3: Branch on LostPlayerTimer >= AIProfile.LostTargetTimeout:
True:
- ClearTarget()
- SetAIState(EAIState::Searching)
- StartInvestigation(LastKnownPlayerLocation)
- Clear LostPlayerTimer
False: continue waiting
```
#### `CanSeeTarget()` → `Boolean`
```
[Function: CanSeeTarget] (Pure)
Step 1: TargetActor = Get Blackboard "TargetActor"
Step 2: If TargetActor NOT valid → Return false
Step 3: Line Trace by Channel from Self location to TargetActor location
- Trace channel: Visibility
- Ignore Self
Step 4: Return (Hit Actor == TargetActor) — true if nothing blocks line of sight
```
### Blueprint Build Checklist
- [ ] Create AI_BaseAgentController (Parent: AIController)
- [ ] Add all variables from the table above
- [ ] Create Blackboard asset: BB_AgentBoard with all key names
- [ ] Configure Behavior Tree asset (or use placeholder)
- [ ] Implement OnPossess: cache components, init perception/state/alert, run BT
- [ ] Implement UpdatePerception: route Sight/Hearing/Damage to correct states
- [ ] Implement SetAIState with blackboard update and dispatcher
- [ ] Implement SetTarget/ClearTarget with blackboard sync
- [ ] Implement StartInvestigation with AI MoveTo
- [ ] Implement RequestReinforcements (nearby AI coordination)
- [ ] Implement LostSightOfTarget with timeout → search
- [ ] Implement CanSeeTarget with line trace
- [ ] Create BP_EnemyBase child classes that use this controller

View File

@@ -1,7 +1,9 @@
# Blueprint Spec Template # Blueprint Spec Template v2.0
Use this template for every system file in `docs/blueprints/`. Replace all `{{placeholders}}` with system-specific values. Use this template for every system file in `docs/blueprints/`. Replace all `{{placeholders}}` with system-specific values.
**v2.0 Changes:** Added Section 11 (Manual Implementation Guide) and Section 12 (Build Checklist) for human implementers building Blueprints manually in UE5. Each function now has node-by-node logic with specific UE5 Blueprint node names.
--- ---
# {{System Number}} — {{System Name}} (`{{AssetType}}_{{Name}}`) # {{System Number}} — {{System Name}} (`{{AssetType}}_{{Name}}`)
@@ -161,4 +163,109 @@ flowchart TD
--- ---
*Template version 1.0 — Reference file for all docs/blueprints/ specifications.* ## 11. Manual Implementation Guide
> **This section is for a human implementer building the Blueprint manually in UE5.**
>
> Follow these steps in order. Each function is broken down into specific UE5 Blueprint nodes.
> Variable names in `code font` refer to variables defined in Section 3.
> References to other systems (e.g., `BPC_HealthSystem`) mean "get the component and call its function."
### 11.1 Class Setup
1. Create a new Blueprint Class:
- Parent Class: `{{ParentClass}}`
- Name: `{{AssetType}}_{{Name}}`
- Path: `Content/Framework/{{Category}}/`
2. Add all variables from Section 3 to the Class Defaults.
- Configuration variables: set `Instance Editable` ✓ and `Expose on Spawn` ✓ (if marked).
- Internal variables: set to `Private` (no expose).
3. If the class implements interfaces, add them in `Class Settings → Implemented Interfaces`.
### 11.2 Variable Initialization (Construction Script / BeginPlay)
```
Event BeginPlay
├─ Set {{VarName}} = {{Default}} // For each internal variable with a default
├─ Get Owner → Cast to {{ExpectedOwnerType}} → Store as CachedOwner
├─ Get Component by Class ({{RequiredComponent}}) → Store reference
│ └─ If NOT valid → Print Warning: "{{Component}} missing on {{Owner}}"
├─ [If replicating] Check HasAuthority → if server, initialize replicated vars
└─ Fire initialization-related dispatchers
```
### 11.3 Function Implementations
#### `{{FunctionName}}`
**Input Pins:** `{{Param1}}` ({{Type1}}), `{{Param2}}` ({{Type2}})
**Output Pins:** `{{ReturnValue}}` ({{ReturnType}})
**Node-by-Node Logic:**
```
[Function: {{FunctionName}}]
Step 1: {{First thing to do — get a reference, check a condition}}
Step 2: Branch on {{Condition}}
True →
Step 2a: {{Action for true branch}}
Step 2b: {{Next action}}
False →
Step 2c: {{Action for false branch}}
Step 3: {{Continuation after branch}}
Step 4: Fire dispatcher: Call {{OnEventName}}({{Params}})
Step 5: Return {{ReturnValue}}
```
**Nodes Used:** {{List of UE5 Blueprint nodes the implementer should search for}}
**Things to Watch:**
- {{Gotcha 1}}
- {{Gotcha 2}}
#### `{{NextFunction}}`
*(Repeat the same detailed format for every public and protected function)*
### 11.4 Event Dispatcher Bindings
List every dispatcher this system binds TO (inbound listeners) and what to do:
| Bind to Dispatcher (on which system) | Custom Event to Create | What it Does |
|--------------------------------------|------------------------|--------------|
| `{{Component}}.On{{EventName}}` | `On{{EventName}}Handler` | {{Brief logic}} |
### 11.5 Networking Setup (if applicable)
- In `Class Settings`, enable `Replicates`
- For each replicated variable, set `Replication` to `Replicated` or `Replicated Using OnRep_{{VarName}}`
- Create `OnRep_{{VarName}}` functions that fire the same dispatchers as single-player
- Add `Switch HasAuthority` before all state-changing logic
- Add `Run on Server` RPC functions with `Server_` prefix
### 11.6 Quick Node Reference
Common UE5 Blueprint nodes used in this system:
| Node | Where to Find | Used For |
|------|---------------|----------|
| `{{NodeName}}` | Right-click → "{{search term}}" | {{Purpose}} |
---
## 12. Blueprint Build Checklist
Complete these steps in order when implementing this system:
- [ ] Create Blueprint class with correct parent
- [ ] Add all variables with correct types and defaults
- [ ] Implement interface functions (if any)
- [ ] Build `BeginPlay` / `OnPossess` / initialization event
- [ ] Implement each function from Section 4 following the step-by-step guide
- [ ] Create all event dispatchers from Section 5
- [ ] Bind to inbound dispatchers (Section 11.4)
- [ ] Add networking logic (HasAuthority gates, RPCs, RepNotify)
- [ ] Test with validation checklist (Section 9)
- [ ] Place in level or attach to character as appropriate
---
*Template version 2.0 — Includes Manual Implementation Guide for human implementers.*

View File

@@ -184,10 +184,10 @@ docs/developer/
| Aspect | Blueprint Spec (`docs/blueprints/`) | Developer Reference (`docs/developer/`) | | Aspect | Blueprint Spec (`docs/blueprints/`) | Developer Reference (`docs/developer/`) |
|--------|--------------------------------------|----------------------------------------| |--------|--------------------------------------|----------------------------------------|
| **Purpose** | Define what to build (contract) | Explain how it works (understanding) | | **Purpose** | Define what to build with node-by-node logic (v2.0) | Explain how it works (understanding) |
| **Audience** | Implementers following a spec | Anyone needing to understand internals | | **Audience** | Implementers building Blueprints manually | Anyone needing to understand internals |
| **Content** | Enums, structs, variables, functions | Data flow, state machines, design rationale | | **Content** | Enums, structs, variables, functions, **Manual Implementation Guide** | Data flow, state machines, design rationale |
| **Updates** | When design changes | When implementation details change | | **Format** | TEMPLATE.md v2.0 — includes Build Checklist | Per-category reference docs |
--- ---