# Universal Asset Creation Workflow

This workflow creates ANY asset type in UE5 — Materials, SoundCues, Niagara Systems, AnimBPs, DataTables, BehaviorTrees, StateTrees, Foliage Types, or any future asset class. One methodology, not per-system recipes.

## The 5-Step Process

### Step 1: Check if asset already exists

```python
import unreal

ar = unreal.AssetRegistryHelpers.get_asset_registry()
asset_path = '/Game/Materials/M_Rock'

# Check existence BEFORE creating
asset_data = ar.get_asset_by_object_path(asset_path)
if asset_data.is_valid():
    print(f"Asset already exists: {asset_path}")
    asset = unreal.load_asset(asset_path)
else:
    print("Asset does not exist — proceed to create")
```

CRITICAL: ALWAYS check first. Creating a duplicate produces `M_Rock_1` (silent rename, hard to debug).

### Step 2: Discover the factory class

Use `query_ue_api` tool with keyword search:

```
query_ue_api(mode="keyword", query="MaterialFactory")
query_ue_api(mode="keyword", query="SoundCueFactory")
query_ue_api(mode="keyword", query="DataTableFactory")
```

If you don't know the factory name, search by system name:
```
query_ue_api(mode="keyword", query="Factory Blueprint")
query_ue_api(mode="keyword", query="Factory Niagara")
```

The factory class path follows the pattern: `/Script/ModuleName.FactoryClassName`
Examples:
- `/Script/UnrealEd.BlueprintFactory`
- `/Script/UnrealEd.MaterialFactoryNew`
- `/Script/AudioEditor.SoundCueFactoryNew`
- `/Script/NiagaraEditor.NiagaraSystemFactoryNew`
- `/Script/UnrealEd.DataTableFactory`
- `/Script/InputEditor.InputAction_Factory` (NOT EnhancedInput!)
- `/Script/InputEditor.InputMappingContext_Factory` (NOT EnhancedInput!)

### Step 3: Discover factory properties (if needed)

Some factories require properties set BEFORE asset creation. Discover them:

```python
# Option A: Python introspection on a temporary factory instance
factory = unreal.MaterialFactoryNew()  # only works if class is in unreal namespace
print(dir(factory))
print(factory.get_editor_property_names())

# Option B: query_ue_api for factory class properties
# query_ue_api(mode="class_info", query="BlueprintFactory")
# query_ue_api(mode="class_info", query="DataTableFactory")
```

Common factory properties:
- `BlueprintFactory` → `parent_class` (e.g., Actor, Character, ActorComponent)
- `DataTableFactory` → `struct` (row struct type — MUST be set)
- `StateTreeFactory` → `StateTreeSchemaClass` (protected, use CreateAssetWithFactory)

### Step 4: Create the asset

**Method A — Python namespace available** (factory class is UFUNCTION-accessible):
```python
import unreal

factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.Character)

asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
asset = asset_tools.create_asset('BP_MyCharacter', '/Game/Blueprints', None, factory)
```

**Method B — Factory NOT in Python namespace** (use universal helper):
```python
import unreal

helpers = unreal.UNDBlueprintHelpers
asset = helpers.create_asset_with_factory(
    '/Game/AI/ST_Patrol',                                    # asset path
    '/Script/StateTreeEditorModule.StateTreeFactory',        # factory class path (full)
    '{"StateTreeSchemaClass": "/Script/GameplayStateTreeModule.StateTreeComponentSchema"}'  # properties JSON
)
```

`CreateAssetWithFactory` uses runtime reflection — works with ANY factory, no Build.cs dependencies needed.

**Supported property types in JSON**: UClass paths (string → StaticLoadClass), UObject references, bool, int, float, string, FName.

### Step 5: Configure, compile, and save

```python
# Set properties on the created asset
asset.set_editor_property('SomeProperty', some_value)

# For Blueprints: set CDO defaults
gen_class = asset.generated_class()
cdo = unreal.get_default_object(gen_class)
cdo.set_editor_property('MaxHealth', 100.0)

# For complex properties: use UniversalBridge
bridge = unreal.UNDUniversalBridge
bridge.set_property_from_json(asset, 'ComplexStruct.SubField', '"value"')

# Compile (Blueprints only)
unreal.BlueprintEditorLibrary.compile_blueprint(asset)

# Save
unreal.EditorAssetLibrary.save_asset('/Game/Blueprints/BP_MyCharacter')

# Print confirmation
print(f"Created and saved: {asset.get_path_name()}")
```

## When Python namespace is NOT available

Many editor module classes use `UCLASS(MinimalAPI)` or lack UFUNCTION bindings. Signs:
- `unreal.SomeFactory()` → `AttributeError: module 'unreal' has no attribute 'SomeFactory'`
- `factory.set_schema_class()` → `AttributeError` (method exists in C++ but not exposed to Python)
- `factory.get_editor_property('SomeField')` → fails on `protected` + `Transient` properties

**Solution**: Always use `CreateAssetWithFactory` for factories outside `UnrealEd` module. It uses C++ runtime reflection (`FProperty`, `StaticLoadClass`) to bypass Python binding limitations.

## Decision Tree

```
Need to create an asset?
├── Know the factory class? → Step 4
├── Don't know the factory? → Step 2 (query_ue_api)
├── Factory in unreal.* namespace? → Method A (Python direct)
├── Factory NOT in namespace? → Method B (CreateAssetWithFactory)
├── Need to set protected properties? → Method B (runtime reflection)
└── Need to build graph/data AFTER creation? → See universal_bridge.md
```

## CRITICAL RULES

1. **ALWAYS check existence** before creating (Step 1) — prevents silent `_1` renames
2. **NEVER call `open_editor_for_asset()`** — crashes editor (FMRUList null dereference)
3. **Content paths** (`/Game/...`) for Python `unreal` API, **filesystem paths** (`Content/...`) for file tools
4. **NEVER guess factory class names** — always discover via `query_ue_api`
5. **NEVER assume Python namespace** — if `unreal.SomeFactory()` fails, use `CreateAssetWithFactory`
6. **ALWAYS print results** to confirm success — silent failures are common
7. **ALWAYS save** after creation and modification
