<!-- SUMMARY -->
# Blueprint Graphs — Quick Reference
- **Two libs**: `helpers = unreal.UNDBlueprintHelpers` (build_graph_from_spec, validation), `lib = unreal.BlueprintEditorLibrary` (variables, compile, types)
- **build_graph_from_spec(bp, json_spec)** — use for ALL graphs. graph_type: "function", "event_graph", or "construction_script".
- **exec_order** — list impure nodes in execution order. System auto-generates ALL exec wiring. Pure nodes skipped automatically.
  - Linear: `"exec_order": ["set_v", "fire"]`
  - Branch: `"exec_order": [{"node": "branch", "then": ["set_f"], "else": ["fire2"]}]`
- **connections** — write ONLY data connections (colored wires). NO exec connections needed when using exec_order.
- `query_ue_api('FunctionName')` BEFORE using function_call — verify exact name + pin names first
- **Float type is 'real'**: `lib.get_basic_type_by_name('real')` — not 'float'
- **UE5 naming**: Math uses `_DoubleDouble` suffix (not `_FloatFloat`)
- **Validation**: compile → validate_blueprint_graphs → remove_orphaned_nodes → describe_graph_logic → save
<!-- END SUMMARY -->

# Blueprint Graph Construction via Python

All graph operations use `execute_python` with `import unreal`.
**NEVER call `open_editor_for_asset()`** — crashes the editor.

## Two libraries

| Library | Handles |
|---------|---------|
| `helpers = unreal.UNDBlueprintHelpers` | `build_graph_from_spec`, validation, graph management, dispatchers |
| `lib = unreal.BlueprintEditorLibrary` | `add_member_variable`, `compile_blueprint`, type helpers |

## UNDBlueprintHelpers API

| Method | What it does |
|--------|-------------|
| `build_graph_from_spec(bp, json_spec)` | **THE method** — creates graph atomically from JSON spec |
| `add_event_dispatcher(bp, name)` | Create multicast delegate (call BEFORE graph building) |
| `add_dispatcher_parameter(bp, name, param, type)` | Add param to delegate |
| `has_member_variable(bp, name)` | Check if variable/dispatcher exists (ALWAYS check before creating) |
| `validate_blueprint_graphs(bp)` | **MANDATORY** after graph building — returns errors list |
| `describe_graph_logic(bp)` | **MANDATORY** — trace exec flow as text |
| `remove_orphaned_nodes(bp)` | Auto-remove disconnected impure nodes |
| `add_component_bound_event_node(graph, bp, comp, delegate, x, y)` | Bind C++ component delegate in EventGraph |

## Pin type helpers
```python
lib = unreal.BlueprintEditorLibrary
float_type = lib.get_basic_type_by_name('real')   # NEVER 'float' — UE5 uses 'real'
bool_type  = lib.get_basic_type_by_name('bool')
int_type   = lib.get_basic_type_by_name('int')
str_type   = lib.get_basic_type_by_name('string')
name_type  = lib.get_basic_type_by_name('name')
text_type  = lib.get_basic_type_by_name('text')
vec_type   = lib.get_struct_type(unreal.find_object(None, '/Script/CoreUObject.Vector'))
rot_type   = lib.get_struct_type(unreal.find_object(None, '/Script/CoreUObject.Rotator'))
actor_type = lib.get_object_reference_type(unreal.Actor)
```
Invalid type names silently default to `int` and break pin connections!

## exec_order — Automatic Execution Flow

**ALWAYS use `exec_order` instead of writing exec connections manually.** The system auto-generates all exec wiring (white wires). You write ONLY data connections (colored wires).

`exec_order` is a JSON array of impure node IDs in the order they should execute:

```json
"exec_order": ["set_v", "fire_delegate", "branch_node"]
```

- **entry** is auto-wired as start (do NOT include in list)
- **result** is auto-wired as end (do NOT include in list)
- **Pure nodes** (math, getters) are silently skipped if accidentally included
- Impure nodes: VariableSet, Branch, CallDelegate, Sequence, PrintString, SpawnActor, self_call

**Branch** — use an object with `then`/`else` sub-paths:
```json
"exec_order": ["set_v", {"node": "br", "then": ["set_f", "fire2"], "else": ["log_err"]}]
```

**Sequence** — use `paths` array:
```json
"exec_order": [{"node": "seq", "paths": [["a", "b"], ["c", "d"]]}]
```

## Data pin names (for connections array)

| Node | Pin | Direction | Notes |
|------|-----|-----------|-------|
| FunctionEntry | param names | Output | One data pin per function parameter |
| Branch | `Condition` | Input (bool) | Capital C. Must be bool — use comparison node for numbers |
| Variable Get | variable name | Output | Pure node — data source |
| Variable Set | variable name | **Input** | Receives data INTO the variable. Cannot read from it. |
| Function Call | `A`, `B`, `ReturnValue`, etc. | varies | `query_ue_api('FunctionName')` to get exact pin names |
| FunctionResult | return value names | Input | Wire data to return pins |

**CRITICAL: VariableSet's data pin is INPUT, not OUTPUT.** You cannot read from a VariableSet. Connect from the upstream source to both the SET and any other consumer.

## component_event — binding C++ component delegates

Use in EventGraph to bind overlap, hit, click, or custom C++ delegates from components:
```json
{"id": "overlap", "type": "component_event",
 "component": "MyCollision",
 "delegate": "OnComponentBeginOverlap",
 "x": 0, "y": 0}
```
- `component` = **member variable name** (the BP variable holding the component)
- `delegate` = **delegate property name** on the C++ component class
- Blueprint MUST be compiled before binding (auto-compiled if needed)
- Output pins = delegate parameters (OtherActor, OtherComp, etc.) — wire directly, do NOT use variable_get
- Common delegates: `OnComponentBeginOverlap`, `OnComponentEndOverlap`, `OnComponentHit`, `OnClicked`

## Discovering function names (MANDATORY)

NEVER guess UE function names. ALWAYS `query_ue_api('Clamp')` or `query_ue_api('Subtract')` to find exact function names and pin signatures before using them in `build_graph_from_spec`.

## Complete example — function with math, delegates, branching

```python
import unreal, json
helpers = unreal.UNDBlueprintHelpers
bp = unreal.load_asset('/Game/Components/BP_MyComponent')

spec = {
    "graph_type": "function", "function_name": "ApplyDelta",
    "params": [{"name": "Amount", "type": "real"}],
    "nodes": [
        {"id": "get_v",   "type": "variable_get",  "variable": "MyVar",   "x": 50,  "y": 100},
        {"id": "get_max", "type": "variable_get",  "variable": "MaxVar",  "x": 50,  "y": 200},
        {"id": "sub",     "type": "function_call",  "library": "KismetMathLibrary", "function": "Subtract_DoubleDouble", "x": 250, "y": 0},
        {"id": "clamp",   "type": "function_call",  "library": "KismetMathLibrary", "function": "FClamp",               "x": 450, "y": 0},
        {"id": "cmp",     "type": "function_call",  "library": "KismetMathLibrary", "function": "LessEqual_DoubleDouble","x": 450, "y": 200},
        {"id": "set_v",   "type": "variable_set",  "variable": "MyVar",   "x": 650, "y": 0},
        {"id": "fire",    "type": "call_delegate",  "delegate": "OnValueChanged", "x": 850, "y": 0},
        {"id": "branch",  "type": "branch",                                "x": 1050, "y": 0},
        {"id": "set_f",   "type": "variable_set",  "variable": "bFlag",   "x": 1250, "y": -100},
        {"id": "fire2",   "type": "call_delegate",  "delegate": "OnZero", "x": 1250, "y": 100}
    ],
    "exec_order": [
        "set_v", "fire",
        {"node": "branch", "then": ["set_f"], "else": ["fire2"]}
    ],
    "connections": [
        {"from": "get_v",   "from_pin": "MyVar",        "to": "sub",    "to_pin": "A"},
        {"from": "entry",   "from_pin": "Amount",       "to": "sub",    "to_pin": "B"},
        {"from": "sub",     "from_pin": "ReturnValue",  "to": "clamp",  "to_pin": "Value"},
        {"from": "get_max", "from_pin": "MaxVar",       "to": "clamp",  "to_pin": "Max"},
        {"from": "clamp",   "from_pin": "ReturnValue",  "to": "set_v",  "to_pin": "MyVar"},
        {"from": "clamp",   "from_pin": "ReturnValue",  "to": "cmp",    "to_pin": "A"},
        {"from": "cmp",     "from_pin": "ReturnValue",  "to": "branch", "to_pin": "Condition"},
        {"from": "clamp",   "from_pin": "ReturnValue",  "to": "fire",   "to_pin": "NewValue"}
    ],
    "pin_defaults": [
        {"node": "clamp", "pin": "Min", "value": "0.0"},
        {"node": "cmp",   "pin": "B",   "value": "0.0"},
        {"node": "set_f", "pin": "bFlag", "value": "true"}
    ]
}
r = json.loads(helpers.build_graph_from_spec(bp, json.dumps(spec)))
print(f"Success: {r['success']}, Nodes: {r['nodes_created']}, Errors: {r['errors']}")
```

**Key points in this example:**
- `exec_order` lists ONLY impure nodes: set_v, fire, branch (with sub-paths)
- Pure nodes (get_v, get_max, sub, clamp, cmp) are NOT in exec_order — they evaluate lazily
- `connections` has ONLY data wires — no `execute`/`then` connections needed
- entry→set_v and branch paths are auto-wired by exec_order

## Simple getter function (pure logic only)

```python
spec = {
    "graph_type": "function", "function_name": "IsFlag",
    "return_values": [{"name": "ReturnValue", "type": "bool"}],
    "nodes": [
        {"id": "get_f", "type": "variable_get", "variable": "bFlag", "x": 150, "y": 0}
    ],
    "exec_order": [],
    "connections": [
        {"from": "get_f", "from_pin": "bFlag", "to": "result", "to_pin": "ReturnValue"}
    ]
}
```
Empty exec_order = entry auto-wired directly to result.

## Event nodes vs Custom events

| Spec type | When to use |
|-----------|-------------|
| `"event"` with `event_name` | Native events: `ReceiveBeginPlay`, `ReceiveTick`, `ReceiveEndPlay`, etc. |
| `"custom_event"` with `event_name` | Events you INVENT that don't exist in the parent class |

**NEVER use `custom_event` for `ReceiveBeginPlay`** — causes compile error.

**Events live in EventGraph**, not function graphs:
```python
spec = {
    "graph_type": "event_graph",
    "nodes": [
        {"id": "bp",   "type": "event",     "event_name": "ReceiveBeginPlay", "x": 0,   "y": 0},
        {"id": "init", "type": "self_call",  "function": "InitMySystem",      "x": 250, "y": 0}
    ],
    "exec_order": ["init"],
    "connections": []
}
```

| Blueprint UI name | `event_name` value |
|---|---|
| Event BeginPlay | `ReceiveBeginPlay` |
| Event Tick | `ReceiveTick` |
| Event EndPlay | `ReceiveEndPlay` |
| ActorBeginOverlap | `ReceiveActorBeginOverlap` |
| ActorEndOverlap | `ReceiveActorEndOverlap` |
| ActorHit | `ReceiveActorHit` |

## Construction Script

For Actor-derived Blueprints, the Construction Script runs when actor is spawned/moved in editor.
Use `graph_type: "construction_script"` — **NOT** `graph_type: "function"` with `function_name: "UserConstructionScript"`.

```python
spec = {
    "graph_type": "construction_script",
    "nodes": [
        {"id": "scene", "type": "function_call", "library": "KismetMathLibrary", "function": "RandomFloatInRange", "x": 0, "y": 100},
        {"id": "set_loc", "type": "self_call", "function": "K2_SetActorLocation", "x": 300, "y": 0}
    ],
    "exec_order": ["set_loc"],
    "connections": [
        {"from": "scene", "from_pin": "ReturnValue", "to": "set_loc", "to_pin": "NewLocation"}
    ]
}
```

The construction_script graph already exists — nodes are added to it (old nodes auto-cleared).

## Three critical rules

1. **Compile before graph building**: `add_member_variable()` → `compile_blueprint()` → THEN `build_graph_from_spec()`. Without compile, variable/delegate pins are empty.

2. **Compile before delegate nodes**: `add_event_dispatcher()` → `compile_blueprint()` → THEN `build_graph_from_spec()` with delegate node types.

3. **Validate + Describe before completion**: After all graph work → `compile_blueprint()` → `validate_blueprint_graphs(bp)` → `remove_orphaned_nodes(bp)` → `describe_graph_logic(bp)` → verify logic → `attempt_completion`.

## Node types

| type | Required fields | Creates |
|------|----------------|---------|
| `variable_get` | `variable` | VariableGet (pure, no exec) |
| `variable_set` | `variable` | VariableSet (impure, has exec) |
| `function_call` | `library`, `function` | CallFunction by class name |
| `self_call` | `function` | CallFunction on self blueprint |
| `branch` | — | If/Else (`Condition`, `then`, `else`) |
| `sequence` | — | Sequence (`then_0`, `then_1`, ...) |
| `event` | `event_name` | Built-in event (ReceiveBeginPlay, etc.) |
| `custom_event` | `event_name` | Custom event |
| `function_result` | — | Return node (reuses existing) |
| `call_delegate` | `delegate` | Fire dispatcher |
| `bind_delegate` | `delegate` | Bind to dispatcher |
| `component_event` | `component`, `delegate` | Widget/component delegate event (e.g. Button OnClicked) |

**Type formats for params/return_values:** `"real"`, `"bool"`, `"int"`, `"int64"`, `"string"`, `"name"`, `"text"`, `"byte"`, or `{"type":"object","object_class":"Actor"}`, or `{"type":"struct","struct_path":"/Script/CoreUObject.Vector"}`.

**Optional fields:** `"clear_existing": true` removes all non-structural nodes before building.

## Validation error fixes

| Error message | Fix |
|---|---|
| `Return node pin 'X' has NO connection` | Add data connection to result pin |
| `Return node has no exec input` | Add result node to exec_order (or it's auto-wired) |
| `SET 'X' node value pin NOT connected` | Add data connection to set node |
| `Node 'X' is ORPHANED` | Add node to exec_order |
| `Branch Condition NOT connected` | Wire bool source to branch Condition pin |
| `CallDelegate has parameter pin NOT connected` | Wire data to delegate pin |
| `Return node NOT reachable` | Rebuild with `"clear_existing": true` |

## Preventing duplicate variables

**ALWAYS check before creating:**
```python
if not helpers.has_member_variable(bp, 'MyVar'):
    lib.add_member_variable(bp, 'MyVar', float_type)
if not helpers.has_member_variable(bp, 'OnValueChanged'):
    helpers.add_event_dispatcher(bp, 'OnValueChanged')
    helpers.add_dispatcher_parameter(bp, 'OnValueChanged', 'NewValue', float_type)
```

## Fixing validation errors

When `validate_blueprint_graphs` reports errors, rebuild with `build_graph_from_spec` using `"clear_existing": true`. This is safer than patching individual connections.
