# StateTree AI System

StateTree is UE5's modern replacement for Behavior Trees — a hierarchical state machine with data-driven conditions and tasks. Prefer StateTree over BT for new UE5.4+ projects.

## CRITICAL: Complete Python API for StateTree

### Step 1: Create StateTree asset

Use `create_asset_with_factory` — it sets the schema via reflection (required for valid assets).

```python
import unreal
helpers = unreal.UNDBlueprintHelpers
bridge = unreal.UNDUniversalBridge

# For actors with StateTreeComponent:
asset = helpers.create_asset_with_factory(
    '/Game/AI/ST_Patrol',
    '/Script/StateTreeEditorModule.StateTreeFactory',
    '{"StateTreeSchemaClass": "/Script/GameplayStateTreeModule.StateTreeComponentSchema"}'
)

# For AI controllers (AIController context):
asset = helpers.create_asset_with_factory(
    '/Game/AI/ST_EnemyAI',
    '/Script/StateTreeEditorModule.StateTreeFactory',
    '{"StateTreeSchemaClass": "/Script/GameplayStateTreeModule.StateTreeAIComponentSchema"}'
)
```

### Step 2: Access EditorData and root state

`EditorData` is PROTECTED — use `get_sub_object`:

```python
editor_data = bridge.get_sub_object(asset, 'EditorData')
root_state = bridge.get_sub_object(editor_data, 'SubTrees[0]')
print(f'Root: {root_state.get_editor_property("Name")}')
```

### Step 3: Add child states

```python
patrol = bridge.add_object_to_array(
    root_state, 'Children', 'StateTreeState', '{"Name":"Patrol"}'
)
wait_state = bridge.add_object_to_array(
    root_state, 'Children', 'StateTreeState', '{"Name":"Wait"}'
)
```

### Step 4: Add tasks to states

Use `add_state_tree_node()` — it creates `FStateTreeEditorNode` with proper Node/Instance initialization:

```python
# Add a MoveTo task to the Patrol state
result = bridge.add_state_tree_node(
    patrol,           # UStateTreeState
    'Tasks',          # array: Tasks, EnterConditions, or Considerations
    'StateTreeMoveToTask',  # struct type name (F prefix optional)
    '{"AcceptableRadius": 50.0, "bAllowPartialPath": true}'  # applied to BOTH node and instance
)
print(result)  # {"success":true, "node_id":"...", "node_type":"StateTreeMoveToTask", "instance_type":"StateTreeMoveToTaskInstanceData"}
```

**CRITICAL**: Properties JSON is applied to BOTH the Node struct AND its Instance data. Most task properties (like AcceptableRadius, Speed, Duration) live on the Instance data — the bridge handles this automatically.

### Step 5: Add transitions

Use `add_state_tree_transition()`:

```python
# When Patrol completes → go to next sibling state (Wait)
result = bridge.add_state_tree_transition(
    patrol,               # state to add transition to
    'OnStateCompleted',   # trigger: OnStateCompleted, OnStateSucceeded, OnStateFailed, OnTick, OnEvent
    'NextState',          # target: Succeeded, Failed, NextState, NextSelectableState, GotoState
    '',                   # target state name (only for GotoState)
    None                  # StateTree asset (only for GotoState)
)

# When Wait completes → go back to Patrol (GotoState)
result = bridge.add_state_tree_transition(
    wait_state,
    'OnStateCompleted',
    'GotoState',
    'Patrol',        # target state name
    asset            # StateTree asset (REQUIRED for GotoState to resolve name → ID)
)
```

### Step 6: Compile and save

```python
# Compile the StateTree (bakes editor data into runtime format)
compile_result = bridge.compile_state_tree(asset)
print(compile_result)  # {"success":true, "errors":[]}

# Save
unreal.EditorAssetLibrary.save_asset(asset.get_path_name())
```

## Complete working example: Simple patrol loop

```python
import unreal, json

helpers = unreal.UNDBlueprintHelpers
bridge = unreal.UNDUniversalBridge

# 1. Create asset
asset = helpers.create_asset_with_factory(
    '/Game/AI/ST_PatrolLoop',
    '/Script/StateTreeEditorModule.StateTreeFactory',
    '{"StateTreeSchemaClass": "/Script/GameplayStateTreeModule.StateTreeComponentSchema"}'
)
if not asset:
    print('ERROR: failed to create StateTree')

# 2. Get root state
editor_data = bridge.get_sub_object(asset, 'EditorData')
root = bridge.get_sub_object(editor_data, 'SubTrees[0]')
root.set_editor_property('SelectionBehavior', unreal.StateTreeStateSelectionBehavior.TRY_SELECT_CHILDREN_IN_ORDER)

# 3. Create child states
move_state = bridge.add_object_to_array(root, 'Children', 'StateTreeState', '{"Name":"MoveTo"}')
wait_state = bridge.add_object_to_array(root, 'Children', 'StateTreeState', '{"Name":"Wait"}')

# 4. Add MoveTo task
bridge.add_state_tree_node(
    move_state, 'Tasks', 'StateTreeMoveToTask',
    '{"AcceptableRadius": 100.0, "bAllowPartialPath": true}'
)

# 5. Add transitions: MoveTo → Wait → MoveTo (loop)
bridge.add_state_tree_transition(move_state, 'OnStateCompleted', 'NextState')
bridge.add_state_tree_transition(wait_state, 'OnStateCompleted', 'GotoState', 'MoveTo', asset)

# 6. Compile + save
result = json.loads(bridge.compile_state_tree(asset))
if result['success']:
    unreal.EditorAssetLibrary.save_asset(asset.get_path_name())
    print(f'StateTree created and compiled: {asset.get_path_name()}')
else:
    print(f'Compile errors: {result["errors"]}')
```

## Available built-in task types

| Struct Name | Module | Description | Key Instance Properties |
|-------------|--------|-------------|------------------------|
| `StateTreeMoveToTask` | GameplayStateTree | AI MoveTo via AITask_MoveTo | Destination, TargetActor, AcceptableRadius, bAllowStrafe, bAllowPartialPath |
| `StateTreeRunEnvQueryTask` | GameplayStateTree | Run EQS query | QueryTemplate, RunMode, QueryOwner |
| `StateTreeRunParallelStateTreeTask` | StateTree | Run another StateTree in parallel | StateTree reference |

**For any other behavior**: write custom C++ task structs (see "C++ Task example" below).

## Transition triggers and targets

**Triggers** (`add_state_tree_transition` 2nd param):
- `OnStateCompleted` — when ALL tasks in state complete (succeed or fail)
- `OnStateSucceeded` — only when tasks succeed
- `OnStateFailed` — only when tasks fail
- `OnTick` — every tick (use with conditions)
- `OnEvent` — on gameplay event

**Targets** (`add_state_tree_transition` 3rd param):
- `Succeeded` — exit tree as succeeded
- `Failed` — exit tree as failed
- `NextState` — go to next sibling state
- `NextSelectableState` — go to next sibling that passes enter conditions
- `GotoState` — go to specific state by name (requires 4th and 5th params)

## State selection behaviors

Set on parent state via `set_editor_property('SelectionBehavior', ...)`:
- `TRY_ENTER_STATE` — enter this state directly (leaf state)
- `TRY_SELECT_CHILDREN_IN_ORDER` — try children top to bottom (default)
- `TRY_SELECT_CHILDREN_AT_RANDOM` — random child
- `TRY_SELECT_CHILDREN_WITH_HIGHEST_UTILITY` — utility-based selection

## Adding enter conditions

```python
# Add a condition to a state (checked before state can be entered)
bridge.add_state_tree_node(
    combat_state, 'EnterConditions', 'YourConditionStruct',
    '{"SomeThreshold": 0.5}'
)
```

## CRITICAL access rules

- **Do NOT use `get_editor_property('EditorData')`** — it is protected. Use `bridge.get_sub_object(asset, 'EditorData')`
- **Do NOT use `get_editor_property('SubTrees')`** — use `bridge.get_sub_object(editor_data, 'SubTrees[0]')`
- **Do NOT attempt `unreal.StateTreeFactory()` directly** — schema cannot be set from Python
- **UStateTreeState is a UObject**, NOT a struct — use `add_object_to_array` for children
- **Tasks/Conditions are FStateTreeEditorNode** — use `add_state_tree_node` (NOT `add_instanced_struct_to_array`)
- **After creating states and tasks, ALWAYS call `compile_state_tree(asset)`** before saving
- **ALWAYS complete the full workflow** — do NOT ask the user to configure manually

## C++ Task example

```cpp
// STTask_Patrol.h — custom C++ task
#pragma once
#include "StateTreeTaskBase.h"
#include "STTask_Patrol.generated.h"

USTRUCT()
struct FSTTask_PatrolInstanceData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, Category = "Patrol")
    TArray<FVector> PatrolPoints;

    UPROPERTY()
    int32 CurrentIndex = 0;
};

USTRUCT(meta = (DisplayName = "Patrol"))
struct FSTTask_Patrol : public FStateTreeTaskCommonBase
{
    GENERATED_BODY()

    using FInstanceDataType = FSTTask_PatrolInstanceData;

    virtual const UStruct* GetInstanceDataType() const override
    { return FSTTask_PatrolInstanceData::StaticStruct(); }

    virtual EStateTreeRunStatus EnterState(
        FStateTreeExecutionContext& Context,
        const FStateTreeTransitionResult& Transition) const override;

    virtual EStateTreeRunStatus Tick(
        FStateTreeExecutionContext& Context,
        const float DeltaTime) const override;
};
```

## C++ Condition example

```cpp
USTRUCT(meta = (DisplayName = "Check Threshold"))
struct FSTCondition_CheckThreshold : public FStateTreeConditionCommonBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere)
    float Threshold = 0.5f;

    virtual bool TestCondition(FStateTreeExecutionContext& Context) const override;
};
```

## Build.cs dependencies

```csharp
PublicDependencyModuleNames.AddRange(new string[] {
    "StateTreeModule",
    "GameplayStateTreeModule"
});
```

## Naming conventions
- ST_ prefix for assets
- STTask_ for custom task structs
- STCondition_ for custom condition structs

## StateTree vs Behavior Tree

| Feature | Behavior Tree | StateTree |
|---------|--------------|-----------|
| Flow | Top-down tree traversal | State machine with transitions |
| Concurrency | Parallel nodes (limited) | Multiple active states |
| Data binding | Blackboard (external) | Inline parameters (strongly typed) |
| Transitions | Implicit (decorator evaluation) | Explicit transition conditions |
| Best for | Simple sequential AI | Complex multi-state AI, game flow |

## Architecture: Editor Data vs Runtime Data

- **UStateTreeEditorData** (editor-only) — states, tasks, conditions as editable objects
- **UStateTree** (runtime) — compiled compact format, NOT directly editable
- **UStateTreeState** (editor-only) — single state with Tasks, EnterConditions, Transitions, Children
- **FStateTreeEditorNode** — wraps a task/condition as FInstancedStruct (Node + Instance data)
- **UStateTreeSchema** — defines allowed tasks/conditions. REQUIRED for valid assets
