diff --git a/CONTEXT.md b/CONTEXT.md index 9eb98c9..905aa19 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -181,7 +181,7 @@ docs/ - **Input Mode Coordination:** `SS_EnhancedInputManager` syncs cursor visibility and input blocking with `SS_UIManager` on menu open/close. ## 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) **Any change to the framework MUST also update these files in the same commit:** diff --git a/docs/blueprints/01-core/01_GI_GameTagRegistry.md b/docs/blueprints/01-core/01_GI_GameTagRegistry.md index f7787d9..258d135 100644 --- a/docs/blueprints/01-core/01_GI_GameTagRegistry.md +++ b/docs/blueprints/01-core/01_GI_GameTagRegistry.md @@ -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. -2. Parent: `UPrimaryDataAsset` +> **For human implementer:** Follow these steps to build `DA_GameTagRegistry` in UE5. + +### 14.1 Class Setup + +1. Right-click in Content Browser → **Miscellaneous → Data Asset** +2. Select parent class: `PrimaryDataAsset` 3. Name: `DA_GameTagRegistry` -4. Save to: `/Game/Framework/Core/DA_GameTagRegistry` -5. Add variables `TagNamespace` (FText) and `bIsFrameworkTag` (bool). -6. Implement the functions listed in Section 6 as Blueprint Callable / Pure nodes. -7. Document all tag namespaces from Section 10 in the asset's description. -8. Save and run the validation test. +4. Save to: `Content/Framework/Core/` + +### 14.2 Variables + +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` --- diff --git a/docs/blueprints/03-interaction/22_BPC_PhysicsDragSystem.md b/docs/blueprints/03-interaction/22_BPC_PhysicsDragSystem.md index 1feeca6..f719dc2 100644 --- a/docs/blueprints/03-interaction/22_BPC_PhysicsDragSystem.md +++ b/docs/blueprints/03-interaction/22_BPC_PhysicsDragSystem.md @@ -101,4 +101,132 @@ ReleaseObject: ## 8. Reuse Notes - Physics drag uses UE5 physics constraint system for realistic object handling -- Objects must have `Simulate Physics = true` and `Mass` configured \ No newline at end of file +- 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 \ No newline at end of file diff --git a/docs/blueprints/04-inventory/28_BPC_ConsumableSystem.md b/docs/blueprints/04-inventory/28_BPC_ConsumableSystem.md index 0e4f814..8b7c6b0 100644 --- a/docs/blueprints/04-inventory/28_BPC_ConsumableSystem.md +++ b/docs/blueprints/04-inventory/28_BPC_ConsumableSystem.md @@ -59,4 +59,137 @@ ## 7. Reuse Notes - All consumable effects are data-driven via DA_ItemData -- Buffs support stacking (multiple buffs active simultaneously) \ No newline at end of file +- 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` +``` +[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 \ No newline at end of file diff --git a/docs/blueprints/08-weapons/69_BP_WeaponBase.md b/docs/blueprints/08-weapons/69_BP_WeaponBase.md index 851f59a..5d8e24d 100644 --- a/docs/blueprints/08-weapons/69_BP_WeaponBase.md +++ b/docs/blueprints/08-weapons/69_BP_WeaponBase.md @@ -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 | ### 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. \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/blueprints/09-ai/84_AI_BaseAgentController.md b/docs/blueprints/09-ai/84_AI_BaseAgentController.md index cf649cb..798730e 100644 --- a/docs/blueprints/09-ai/84_AI_BaseAgentController.md +++ b/docs/blueprints/09-ai/84_AI_BaseAgentController.md @@ -155,4 +155,208 @@ Central brain for enemy AI characters. Manages perception, decision-making via B - 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`. \ No newline at end of file +- 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 \ No newline at end of file diff --git a/docs/blueprints/TEMPLATE.md b/docs/blueprints/TEMPLATE.md index f41994c..6bd0cc2 100644 --- a/docs/blueprints/TEMPLATE.md +++ b/docs/blueprints/TEMPLATE.md @@ -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. +**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}}`) @@ -161,4 +163,109 @@ flowchart TD --- -*Template version 1.0 — Reference file for all docs/blueprints/ specifications.* \ No newline at end of file +## 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.* \ No newline at end of file diff --git a/docs/developer/INDEX.md b/docs/developer/INDEX.md index fe2f067..0455a47 100644 --- a/docs/developer/INDEX.md +++ b/docs/developer/INDEX.md @@ -184,10 +184,10 @@ docs/developer/ | Aspect | Blueprint Spec (`docs/blueprints/`) | Developer Reference (`docs/developer/`) | |--------|--------------------------------------|----------------------------------------| -| **Purpose** | Define what to build (contract) | Explain how it works (understanding) | -| **Audience** | Implementers following a spec | Anyone needing to understand internals | -| **Content** | Enums, structs, variables, functions | Data flow, state machines, design rationale | -| **Updates** | When design changes | When implementation details change | +| **Purpose** | Define what to build with node-by-node logic (v2.0) | Explain how it works (understanding) | +| **Audience** | Implementers building Blueprints manually | Anyone needing to understand internals | +| **Content** | Enums, structs, variables, functions, **Manual Implementation Guide** | Data flow, state machines, design rationale | +| **Format** | TEMPLATE.md v2.0 — includes Build Checklist | Per-category reference docs | ---