<!-- SUMMARY -->
# Delegates & Events — Quick Reference
- **Event Dispatchers** = Blueprint multicast delegates for actor-to-actor communication (loose coupling)
- **When to use**: state changed, event occurred, threshold crossed — ALWAYS dispatcher, NEVER plain function call
- **Create**: `helpers.add_event_dispatcher(bp, 'OnSomething')` + `helpers.add_dispatcher_parameter(bp, 'OnSomething', 'Param', type)` + compile
- **Fire/Bind/Unbind in graphs**: use `build_graph_from_spec` with node types `call_delegate`, `bind_delegate`, `unbind_delegate`, `clear_delegate`
- **Compile order**: add_event_dispatcher → compile → build_graph_from_spec (delegates must exist on generated class)
- **Check first**: `helpers.has_member_variable(bp, 'OnSomething')` — dispatchers ARE member variables
- **Signature must match**: delegate params and custom event params must have same types/names
<!-- END SUMMARY -->

# Event Dispatchers and Delegates — Complete Guide

> **API names below are illustrative.** Use `query_ue_api('multicast delegate')` or `query_ue_api('event dispatcher Blueprint')` to discover exact delegate-related APIs before calling.

Event Dispatchers (Blueprint multicast delegates) are the **primary mechanism for actor-to-actor communication** in UE5. They decouple sender from receiver: the sender fires the event, any number of receivers can bind and react — without the sender knowing who's listening.

**CRITICAL DECISION RULE:** When one Blueprint needs to notify another about a state change or event, **ALWAYS use an Event Dispatcher — NEVER a plain function call**. Plain functions create tight coupling and require the caller to hold a direct reference to the exact class. Dispatchers enable loose coupling, multiple listeners, and cleaner architecture.

## When to use Event Dispatchers (NOT functions)

| Scenario | Use Dispatcher | Use Function |
|----------|---------------|--------------|
| Actor A notifies "something happened" | YES — `OnSomethingChanged` | NO |
| UI listens to value changes | YES — `OnValueChanged` | NO |
| System broadcasts state transition | YES — `OnStateChanged` | NO |
| Internal calculation within same actor | NO | YES — private function |
| Pure data getter (GetValue, GetCount) | NO | YES — pure function |
| Command pattern (DoAction, Execute) | NO | YES — callable function |

**Rule of thumb:** If the communication is "event" / "notification" / "something happened" → Dispatcher. If it's "do this" or "return this value" → Function.

## Create Event Dispatcher via Python (no parameters)

```python
import unreal
helpers = unreal.UNDBlueprintHelpers

bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')

# Create a simple event dispatcher (no parameters)
result = helpers.add_event_dispatcher(bp, 'OnSomethingHappened')
print(f'Dispatcher created: {result}')

# Compile the blueprint — find method via query_ue_api('compile blueprint')
# Save the asset — find method via query_ue_api('save package')
```

## Create Event Dispatcher WITH parameters

Dispatchers with parameters require TWO steps:
1. Create the dispatcher
2. Add input parameters to its signature

```python
import unreal
helpers = unreal.UNDBlueprintHelpers
lib = unreal.BlueprintEditorLibrary

bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')

# Step 1: Create the dispatcher
helpers.add_event_dispatcher(bp, 'OnValueChanged')

# Step 2: Add parameters using pin type helpers
float_type = lib.get_basic_type_by_name('real')  # 'real' = float/double in UE5
helpers.add_dispatcher_parameter(bp, 'OnValueChanged', 'NewValue', float_type)
helpers.add_dispatcher_parameter(bp, 'OnValueChanged', 'MaxValue', float_type)

# Compile the blueprint — find method via query_ue_api('compile blueprint')
# Save the asset — find method via query_ue_api('save package')
print('Created OnValueChanged dispatcher with NewValue and MaxValue params')
```

## Create Event Dispatcher with object parameter

```python
import unreal
helpers = unreal.UNDBlueprintHelpers
lib = unreal.BlueprintEditorLibrary

bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')

helpers.add_event_dispatcher(bp, 'OnActorEvent')

# Actor reference parameter
actor_type = lib.get_object_reference_type(unreal.Actor)
helpers.add_dispatcher_parameter(bp, 'OnActorEvent', 'TargetActor', actor_type)

# int parameter
int_type = lib.get_basic_type_by_name('int')
helpers.add_dispatcher_parameter(bp, 'OnActorEvent', 'EventIndex', int_type)

# Compile the blueprint — find method via query_ue_api('compile blueprint')
# Save the asset — find method via query_ue_api('save package')
print('Added TargetActor (Actor) and EventIndex (int) params')
```

### Pin type helpers for parameters

**CRITICAL:** NEVER use `get_basic_type_by_name('float')` — the correct name is `'real'`. NEVER use `get_basic_type_by_name('object')` — use `get_object_reference_type()` instead. Invalid names silently default to `int` and break everything.

```python
lib = unreal.BlueprintEditorLibrary
float_type = lib.get_basic_type_by_name('real')   # 'real' = float/double in UE5 (NEVER 'float'!)
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)  # NEVER get_basic_type_by_name('object')!
```

## List existing Event Dispatchers on a Blueprint

```python
import unreal

bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')
sig_graphs = bp.get_editor_property('delegate_signature_graphs')
print(f'Event Dispatchers ({len(sig_graphs)}):')
for g in sig_graphs:
    print(f'  - {g.get_name()}')
```

## Fire, Bind, Unbind dispatchers via build_graph_from_spec

All dispatcher graph operations use `build_graph_from_spec` with delegate node types. The Blueprint **MUST be compiled first** so the delegate property exists on the generated class.

### Fire a dispatcher from a function

```python
import unreal, json
helpers = unreal.UNDBlueprintHelpers
lib = unreal.BlueprintEditorLibrary

bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')
# Compile the blueprint — find method via query_ue_api('compile blueprint')

# Function that modifies a value and fires a dispatcher
spec = {
    "graph_type": "function", "function_name": "ModifyValue",
    "params": [{"name": "Amount", "type": "real"}],
    "nodes": [
        {"id": "get_v", "type": "variable_get",  "variable": "MyVar",          "x": 50,  "y": 100},
        {"id": "sub",   "type": "function_call",  "library": "KismetMathLibrary", "function": "Subtract_DoubleDouble", "x": 250, "y": 0},
        {"id": "set_v", "type": "variable_set",  "variable": "MyVar",          "x": 450, "y": 0},
        {"id": "fire",  "type": "call_delegate",  "delegate": "OnValueChanged", "x": 650, "y": 0}
    ],
    "connections": [
        {"from": "entry", "from_pin": "then",         "to": "set_v", "to_pin": "execute"},
        {"from": "set_v", "from_pin": "then",          "to": "fire",  "to_pin": "execute"},
        {"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": "set_v", "to_pin": "MyVar"},
        {"from": "sub",   "from_pin": "ReturnValue",   "to": "fire",  "to_pin": "NewValue"}
    ]
}
r = json.loads(helpers.build_graph_from_spec(bp, json.dumps(spec)))
print(f"Success: {r['success']}, errors: {r['errors']}")
```

### Bind to a dispatcher in EventGraph

```python
# Bind a custom event handler to a dispatcher in BeginPlay
spec = {
    "graph_type": "event_graph",
    "nodes": [
        {"id": "bp",      "type": "event",          "event_name": "ReceiveBeginPlay", "x": 0,   "y": 0},
        {"id": "handler", "type": "custom_event",    "event_name": "HandleValueChanged", "x": 0, "y": 200},
        {"id": "bind",    "type": "bind_delegate",   "delegate": "OnValueChanged",    "x": 250, "y": 0}
    ],
    "connections": [
        {"from": "bp",      "from_pin": "then", "to": "bind",    "to_pin": "execute"},
        {"from": "handler", "from_pin": "then", "to": "bind",    "to_pin": "Delegate"}
    ]
}
r = json.loads(helpers.build_graph_from_spec(bp, json.dumps(spec)))
```

### Complete workflow: variable + dispatcher + function

```python
import unreal, json
helpers = unreal.UNDBlueprintHelpers
lib = unreal.BlueprintEditorLibrary

bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')

# 1. Add variables + dispatcher (structure phase)
float_type = lib.get_basic_type_by_name('real')
if not helpers.has_member_variable(bp, 'MyVar'):
    lib.add_member_variable(bp, 'MyVar', float_type)
if not helpers.has_member_variable(bp, 'MaxVar'):
    lib.add_member_variable(bp, 'MaxVar', 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)

# CRITICAL: Compile NOW so variables + dispatcher exist on generated class
# Compile the blueprint — find method via query_ue_api('compile blueprint')

# 2. Build function: subtract, clamp, set, fire dispatcher
spec = {
    "graph_type": "function", "function_name": "ApplyDelta",
    "params": [{"name": "Delta", "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": "set_v",   "type": "variable_set",  "variable": "MyVar",  "x": 650, "y": 0},
        {"id": "fire",    "type": "call_delegate",  "delegate": "OnValueChanged", "x": 850, "y": 0}
    ],
    "connections": [
        {"from": "entry",   "from_pin": "then",         "to": "set_v",  "to_pin": "execute"},
        {"from": "set_v",   "from_pin": "then",          "to": "fire",   "to_pin": "execute"},
        {"from": "get_v",   "from_pin": "MyVar",         "to": "sub",    "to_pin": "A"},
        {"from": "entry",   "from_pin": "Delta",         "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": "fire",   "to_pin": "NewValue"}
    ],
    "pin_defaults": [
        {"node": "clamp", "pin": "Min", "value": "0.0"}
    ]
}
r = json.loads(helpers.build_graph_from_spec(bp, json.dumps(spec)))
print(f"ApplyDelta: success={r['success']}, errors={r['errors']}")

# Compile the blueprint — find method via query_ue_api('compile blueprint')
# Save the asset — find method via query_ue_api('save package')
```

## C++ Dynamic Multicast Delegates (PREFERRED for complex systems)

For reliable parameterized dispatchers, **declare them in C++** with `DECLARE_DYNAMIC_MULTICAST_DELEGATE`. The delegate automatically appears as an Event Dispatcher in any Blueprint subclass.

### Header file pattern
```cpp
// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

// Declare delegate signatures BEFORE the class
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnStateReached);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnValueChanged, float, NewValue);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnActionPerformed, float, Amount, AActor*, Instigator);

UCLASS(Blueprintable)
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()
public:
    // BlueprintAssignable = can Bind in Blueprint graphs
    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnStateReached OnStateReached;

    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnValueChanged OnValueChanged;

    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnActionPerformed OnActionPerformed;

    UFUNCTION(BlueprintCallable, Category = "Actions")
    void ApplyDelta(float Amount, AActor* Instigator);

protected:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
    float MyVar = 100.f;

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Data")
    float MaxVar = 100.f;
};
```

### Implementation file pattern
```cpp
// MyActor.cpp
#include "MyActor.h"

void AMyActor::ApplyDelta(float Amount, AActor* Instigator)
{
    MyVar = FMath::Clamp(MyVar - Amount, 0.f, MaxVar);
    OnActionPerformed.Broadcast(Amount, Instigator);
    OnValueChanged.Broadcast(MyVar);

    if (MyVar <= 0.f)
    {
        OnStateReached.Broadcast();
    }
}
```

### Delegate parameter count macros
```cpp
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyEvent);                                        // 0 params
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyEvent, float, Value);                  // 1 param
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMyEvent, float, Val, AActor*, Actor);   // 2 params
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FMyEvent, int32, A, float, B, FString, C); // 3
// Up to NineParams
```

### CRITICAL: BlueprintAssignable vs BlueprintCallable
- `BlueprintAssignable` — Blueprint can BIND to this delegate (listen for events). **Always use this.**
- `BlueprintCallable` — Blueprint can CALL/FIRE this delegate. Use when Blueprint needs to fire it (rare).
- Both can be combined: `BlueprintAssignable, BlueprintCallable`

## Component-level event binding (Overlap, Hit) — C++

Components have built-in delegates. Bind in `BeginPlay`, unbind in `EndPlay`:
```cpp
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    CollisionComp->OnComponentBeginOverlap.AddDynamic(this, &AMyActor::HandleOverlapBegin);
    CollisionComp->OnComponentHit.AddDynamic(this, &AMyActor::HandleHit);
}
```

## Timer delegates — C++

```cpp
// Looping timer
GetWorldTimerManager().SetTimer(TimerHandle, this, &AMyActor::HandleTimerTick, 1.0f, true);

// One-shot with lambda
GetWorldTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda([this]()
{
    // action
}), 2.0f, false);

// Clear
GetWorldTimerManager().ClearTimer(TimerHandle);
```

## Common Dispatcher Architecture

- **1:N pattern**: One actor broadcasts, many listeners react — UI widgets, managers, subsystems all bind independently
- **Base class pattern**: Declare dispatcher in parent class, subclasses inherit and fire it with different context
- **Global event bus**: Use a `UGameInstanceSubsystem` with `BlueprintAssignable` delegates — any Blueprint can Get subsystem → Bind/Call

```cpp
UCLASS()
class UMyEventBusSubsystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()
public:
    UPROPERTY(BlueprintAssignable)
    FOnGlobalEvent OnGlobalEvent;
};
// Any Blueprint: GetSubsystem → Bind or Call
```

## Unbinding (cleanup)

```cpp
// Remove specific binding
OnValueChanged.RemoveDynamic(this, &AMyListener::HandleValueChanged);

// Remove all bindings from a specific object
OnValueChanged.RemoveAll(this);
```

In Blueprint via Python (using build_graph_from_spec):
```python
# Clear all bindings on a dispatcher in EventGraph
spec = {
    "graph_type": "event_graph",
    "nodes": [
        {"id": "clear", "type": "clear_delegate", "delegate": "OnValueChanged", "x": 0, "y": 0}
    ],
    "connections": []
}
# Wire exec from appropriate event/node to clear.execute
```

## Debugging dispatchers

```python
# Check if a Blueprint has specific dispatchers
import unreal
bp = unreal.load_asset('/Game/Blueprints/BP_MyActor')
sig_graphs = bp.get_editor_property('delegate_signature_graphs')
for g in sig_graphs:
    print(f'Dispatcher: {g.get_name()}')
```

## RULES
- **ALWAYS prefer Event Dispatchers over direct function calls** for actor-to-actor communication
- **ALWAYS prefer C++ delegates** (`DECLARE_DYNAMIC_MULTICAST_DELEGATE`) over Blueprint-only dispatchers when parameters are involved — C++ delegates are type-safe and compile-time checked
- Mark delegates `BlueprintAssignable` so Blueprint can bind to them
- Fire with `.Broadcast(...)` in C++ — all bound listeners execute
- Use `build_graph_from_spec` for ALL delegate node operations (call_delegate, bind_delegate, unbind_delegate, clear_delegate)
- ALWAYS `compile_blueprint()` after `add_event_dispatcher()` and BEFORE `build_graph_from_spec` with delegate nodes — the delegate property must exist on the generated class
- ALWAYS compile + save after ALL graph changes are done
- NEVER use `get_basic_type_by_name('float')` — the correct name is `'real'`
- NEVER use `get_basic_type_by_name('object')` — use `get_object_reference_type()` instead
- Use `AddDynamic` in `BeginPlay()` for component delegates (overlap, hit)
- Pair every `AddDynamic` with `RemoveDynamic` in `EndPlay()` for non-owned objects
- For global events accessible from any Blueprint, use a GameInstance Subsystem with `BlueprintAssignable` delegates
- Warnings are informational — do NOT loop on unfixable warnings
