# Blueprint Logic Patterns — Game Logic, Loops, Delegates, Error Fixing

## Pure vs Impure nodes
- **Pure** nodes (no exec pins): math, getters, comparisons — evaluate lazily when output is read
- **Impure** nodes (exec input "execute", exec output "then"): variable_set, print, spawn, branch — run in exec order
- Rule: pure nodes need NO exec wiring; impure nodes MUST be in exec chain or they are orphaned

## Control flow nodes in build_graph_from_spec

### Branch (if/else)
```json
{"id": "br", "type": "branch", "x": 400, "y": 0}
```
Pins: input `execute` + `Condition` (bool), outputs `then` (true), `else` (false).
ALWAYS wire Condition — unconnected = always True.

### Sequence (do A then B then C)
```json
{"id": "seq", "type": "sequence", "x": 400, "y": 0}
```
Outputs: `then_0`, `then_1`, `then_2` ... (all fire in order, NOT parallel).
Use when one event triggers multiple independent actions.

### ForEachLoop (iterate array)
```json
{"id": "loop", "type": "function_call", "library": "KismetArrayLibrary",
 "function": "Array_ForEachLoop", "x": 400, "y": 0}
```
**IMPORTANT**: ForEachLoop is a macro, not a simple function. For array iteration, use this pattern instead:
```json
{"id": "len",  "type": "function_call", "library": "KismetArrayLibrary", "function": "Array_Length", "x": 200, "y": 0},
{"id": "loop", "type": "self_call", "function": "ForLoopWithBreak", "x": 400, "y": 0},
{"id": "get",  "type": "function_call", "library": "KismetArrayLibrary", "function": "Array_Get", "x": 600, "y": 100}
```
Wire: len→ReturnValue to loop→LastIndex, loop→LoopBody exec to get, loop→Index to get→Index.

### Delay (timed action)
```json
{"id": "delay", "type": "function_call", "library": "KismetSystemLibrary",
 "function": "Delay", "x": 400, "y": 0}
```
Pins: input `execute` + `Duration` (real), output `Completed` (exec — fires after delay).
**Must be in EventGraph** (not function graphs — functions cannot contain latent nodes).

### DoOnce
```json
{"id": "once", "type": "function_call", "library": "KismetSystemLibrary",
 "function": "DoOnce", "x": 400, "y": 0}
```
NOT a library function — it's a macro node. Use self_call or create a bool variable:
```json
{"id": "get_done", "type": "variable_get", "variable": "bAlreadyDone", "x": 200, "y": 0},
{"id": "br", "type": "branch", "x": 400, "y": 0},
{"id": "set_done", "type": "variable_set", "variable": "bAlreadyDone", "x": 600, "y": 0}
```
Connections: get_done→ReturnValue to br→Condition, br→else (false path = not done yet) to set_done.
Pin defaults: set_done pin "bAlreadyDone" value "true".

### Gate / FlipFlop
These are macro nodes. Implement via bool variables:
- **Gate**: bool `bGateOpen`, branch on it before action
- **FlipFlop**: bool `bFlipped`, toggle with variable_set after each call

## Component delegate events (OnOverlap, OnHit, OnClicked, custom C++ delegates)

### Step 1: Create component variable
```python
import unreal
helpers = unreal.UNDBlueprintHelpers

# Add component as member variable
lib = unreal.UNDUniversalBridge
lib.add_member_variable(bp, 'TriggerZone', '/Script/Engine.BoxComponent')
helpers.compile_blueprint(bp)  # MUST compile before binding events
```

### Step 2: Bind component event in build_graph_from_spec
```json
{
  "graph_name": "EventGraph",
  "nodes": [
    {"id": "overlap", "type": "component_event",
     "component": "TriggerZone",
     "delegate": "OnComponentBeginOverlap",
     "x": 0, "y": 0},
    {"id": "print", "type": "function_call", "library": "KismetSystemLibrary",
     "function": "PrintString", "x": 400, "y": 0}
  ],
  "connections": [
    {"from": "overlap", "from_pin": "then", "to": "print", "to_pin": "execute"}
  ]
}
```

### Common component delegates
| Component | Delegate | Output pins |
|-----------|----------|-------------|
| BoxComponent / SphereComponent / CapsuleComponent | `OnComponentBeginOverlap` | OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult |
| Same | `OnComponentEndOverlap` | OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex |
| Same | `OnComponentHit` | HitComponent, OtherActor, OtherComp, NormalImpulse, Hit |
| Button (UMG) | `OnClicked` | (none) |
| Button (UMG) | `OnHovered` / `OnUnhovered` | (none) |

### Output pin names are the delegate parameters
After creating a `component_event` node, its output pins match the C++ delegate signature.
Wire them to your logic — do NOT create variable_get for these names.
Example: `OtherActor` is an OUTPUT PIN on the overlap event, not a Blueprint variable.

### Custom C++ multicast delegates
If a C++ component declares:
```cpp
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChanged, float, NewHealth);

UPROPERTY(BlueprintAssignable)
FOnHealthChanged OnHealthChanged;
```
Bind it the same way:
```json
{"type": "component_event", "component": "HealthComp", "delegate": "OnHealthChanged"}
```
Output pin: `NewHealth` (float).

## Game logic patterns

### Pattern: Health system with damage and death
```
EventGraph:
  Event AnyDamage → get Health → subtract Damage → clamp(0, MaxHealth) → set Health
    → branch (Health <= 0) → [true] call Die() → [false] call UpdateHealthUI()

Function Die():
  entry → set bIsDead = true → PlayAnimMontage(DeathMontage) → Delay(2.0) → DestroyActor
```

### Pattern: Pickup with trigger
```
EventGraph:
  component_event(TriggerZone, OnComponentBeginOverlap)
    → CastToCharacter(OtherActor) → [success] call PickUp()

Function PickUp():
  entry → AddItemToInventory(ItemClass) → PlaySound(PickupSound) → DestroyActor(self)
```

### Pattern: Timed spawner (spawn N enemies with delay)
```
EventGraph:
  Custom Event "SpawnWave" → ForLoop(0, WaveSize-1)
    → LoopBody: SpawnActor(EnemyClass, GetRandomPointInVolume) → Delay(SpawnInterval)
    → Completed: set WaveCount += 1 → branch(WaveCount < MaxWaves)
    → [true] Delay(WaveDelay) → call SpawnWave
```

### Pattern: Toggle door with trigger
```
EventGraph:
  component_event(DoorTrigger, OnComponentBeginOverlap)
    → branch(bDoorOpen)
      → [false] SetRelativeRotation(DoorMesh, (0,90,0)) → set bDoorOpen=true
      → [true]  SetRelativeRotation(DoorMesh, (0,0,0))  → set bDoorOpen=false
```

## Fixing validation errors

When `compile_and_check` reports "Logical Validation" problems:

| Error message | Cause | Fix |
|---------------|-------|-----|
| "Return node pin 'ReturnValue' has NO connection" | Function has return type but result not wired | Connect data to result node pin |
| "Branch Condition NOT connected" | Branch always takes True path | Wire bool source to branch→Condition |
| "SET node value NOT connected" | Variable set to default 0/false | Wire value source to SET→input |
| "Node is ORPHANED — will never execute" | Impure node not in exec chain | Add to exec_order or delete with remove_orphaned_nodes() |
| "FunctionResult NOT reachable" | Broken exec chain | Use describe_graph_logic() to find gap, then connect_pins |
| "Empty function — 0 nodes" | Function created but no logic | Build with build_graph_from_spec |

### Debugging workflow
1. `compile_and_check('/Game/BP_MyActor')` — see ALL errors
2. If validation problems: `describe_graph_logic(bp, 'FunctionName')` — see actual exec flow
3. Fix specific issues (connect_pins, build_graph_from_spec with clear_existing:true)
4. Re-run compile_and_check — repeat until "all graphs OK"
5. ONLY THEN call attempt_completion

### NEVER ignore validation errors — completion is blocked until all graphs pass validation.
