# Game Framework — Complete Setup Guide

All framework setup uses `execute_python` with `import unreal`. Discover exact method names via `query_ue_api('keyword')` before calling.

## Core Class Chain

```
GameInstance (persistent across levels — save/load, global settings)
  └→ GameMode (server-only, one per level)
       ├── DefaultPawnClass → Character or Pawn
       ├── PlayerControllerClass → PlayerController
       ├── GameStateClass → GameState
       ├── PlayerStateClass → PlayerState
       └── HUDClass → HUD (legacy, prefer UMG widgets)
```

**Ownership flow**: Engine → GameMode.Login() → spawns PlayerController → PlayerController.Possess(DefaultPawnClass) → Player is in the game

## Existing Framework (auto-detected)

The `[Project Framework]` section in system prompt (above) shows what ALREADY EXISTS:
- Which GameMode is configured and what classes it uses
- What components the Character has (mesh, camera, etc.)
- What actors are in the level (PlayerStart, lights, etc.)
- What Input assets exist (IA_, IMC_)

**ALWAYS read `[Project Framework]` before creating anything.** If it shows existing classes — MODIFY them, don't create duplicates.

### Decision Table

| `[Project Framework]` shows | Action |
|------------------------------|--------|
| `No game framework detected` | Create from scratch (Steps 1-9 below) |
| GameMode exists, Pawn=(default) | Create Character + wire to existing GameMode |
| GameMode + Character exist | **DO NOT recreate.** Modify existing (see "Working with Existing" below) |
| `PlayerStart(MISSING!)` | Spawn PlayerStart on level |
| Input assets listed | Reuse existing, add new only if needed |
| Character has no Camera/SpringArm | Add camera setup to existing Character |
| Character already has SpringArm+Camera | **DO NOT add duplicates** |

### Refreshing After Changes

`[Project Framework]` is generated at editor startup. After creating new framework BPs mid-session, re-discover manually:
```python
import unreal
# Quick check: what does the GameMode point to now?
gm = unreal.load_asset('/Game/Framework/GM_MyGame')
cdo = unreal.get_default_object(gm.generated_class())
for prop in ['default_pawn_class', 'player_controller_class']:
    val = cdo.get_editor_property(prop)
    print(f'  {prop} = {val.get_name() if val else "None"}')
```

## Setup Order (new projects only)

ONLY when `[Project Framework]` shows "No game framework detected".

### Step 1: Create GameMode Blueprint
```python
import unreal
factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.GameModeBase)
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
gm = asset_tools.create_asset('GM_MyGame', '/Game/Framework', None, factory)
unreal.BlueprintEditorLibrary.compile_blueprint(gm)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/GM_MyGame')
```

### Step 2: Create Character Blueprint
```python
factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.Character)
char_bp = asset_tools.create_asset('BP_PlayerCharacter', '/Game/Framework', None, factory)
```

ACharacter ALREADY HAS (do NOT add duplicates):
- `CapsuleComponent` (root, collision)
- `ArrowComponent` (forward direction)
- `CharacterMovement` (walk, jump, fly, swim)
- `Mesh` (SkeletalMeshComponent — character body)

### Step 3: Assign Mesh + AnimBP to Character

Check `[Project Assets]` section for existing SkeletalMesh and AnimBlueprint assets.

```python
helpers = unreal.UNDBlueprintHelpers
char_bp = unreal.load_asset('/Game/Framework/BP_PlayerCharacter')
# List existing components to find the SkeletalMeshComponent
for c in helpers.get_blueprint_components(char_bp):
    print(c)  # Find the mesh component name (usually "Mesh")
# Set mesh on existing component (ACharacter has "Mesh" by default)
helpers.set_component_property(char_bp, 'Mesh', 'SkeletalMeshAsset', '"/Game/Characters/SK_MyCharacter"')
# Set AnimBP if exists
helpers.set_component_property(char_bp, 'Mesh', 'AnimClass', '"/Game/Characters/ABP_MyCharacter.ABP_MyCharacter_C"')
```

If NO mesh exists, use default mannequin: `list_files` on `/Game/Characters/Mannequins/` to find SKM_Manny or SKM_Quinn.

### Step 4: Camera Setup

Choose ONE based on game type:

**Third-Person:**
```python
helpers = unreal.UNDBlueprintHelpers
helpers.add_component_to_blueprint(char_bp, 'SpringArmComponent', 'CameraBoom')
helpers.set_component_property(char_bp, 'CameraBoom', 'TargetArmLength', '300.0')
helpers.set_component_property(char_bp, 'CameraBoom', 'bUsePawnControlRotation', 'true')
helpers.add_component_to_blueprint(char_bp, 'CameraComponent', 'FollowCamera', 'CameraBoom')
```

**First-Person:**
```python
helpers = unreal.UNDBlueprintHelpers
helpers.add_component_to_blueprint(char_bp, 'CameraComponent', 'FirstPersonCamera')
helpers.set_component_property(char_bp, 'FirstPersonCamera', 'bUsePawnControlRotation', 'true')
```

**Top-Down:**
```python
helpers = unreal.UNDBlueprintHelpers
helpers.add_component_to_blueprint(char_bp, 'SpringArmComponent', 'CameraBoom')
helpers.set_component_property(char_bp, 'CameraBoom', 'TargetArmLength', '800.0')
helpers.set_component_property(char_bp, 'CameraBoom', 'bUsePawnControlRotation', 'false')
helpers.set_component_property(char_bp, 'CameraBoom', 'RelativeRotation', '{"Pitch": -60.0, "Yaw": 0.0, "Roll": 0.0}')
helpers.set_component_property(char_bp, 'CameraBoom', 'bDoCollisionTest', 'false')
helpers.add_component_to_blueprint(char_bp, 'CameraComponent', 'TopDownCamera', 'CameraBoom')
```

ALWAYS compile + save after adding components:
```python
unreal.BlueprintEditorLibrary.compile_blueprint(char_bp)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/BP_PlayerCharacter')
```

### Step 5: Add Gameplay Variables

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

# ALWAYS check before adding — prevents _0 duplicates
if not helpers.has_member_variable(bp, 'MyVar'):
    pin_type = lib.get_basic_type_by_name('real')  # see type table below
    lib.add_member_variable(bp, 'MyVar', pin_type)

# MUST compile BEFORE setting defaults or using in graphs
lib.compile_blueprint(bp)

# Set defaults via CDO
cdo = unreal.get_default_object(bp.generated_class())
cdo.set_editor_property('MyVar', 100.0)
lib.compile_blueprint(bp)
```

`get_basic_type_by_name()` valid names: `'real'`(float), `'bool'`, `'int'`, `'int64'`, `'string'`, `'name'`, `'text'`, `'byte'`. NEVER `'float'`/`'double'`.
Object refs: `lib.get_object_reference_type(unreal.Actor)`. Structs: `lib.get_struct_type(...)`.

### Step 6: Create PlayerController
```python
factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.PlayerController)
pc = asset_tools.create_asset('BP_PlayerController', '/Game/Framework', None, factory)
unreal.BlueprintEditorLibrary.compile_blueprint(pc)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/BP_PlayerController')
```

### Step 7: Enhanced Input Assets

See `enhanced_input.md` for full details. Minimum viable:
```python
# Discover factory via query_ue_api('InputAction')
ia_move = asset_tools.create_asset('IA_Move', '/Game/Input', None, unreal.InputActionFactory())
ia_move.set_editor_property('value_type', unreal.EInputActionValueType.AXIS2D)
unreal.EditorAssetLibrary.save_asset('/Game/Input/IA_Move')

ia_jump = asset_tools.create_asset('IA_Jump', '/Game/Input', None, unreal.InputActionFactory())
unreal.EditorAssetLibrary.save_asset('/Game/Input/IA_Jump')

imc = asset_tools.create_asset('IMC_Default', '/Game/Input', None, unreal.InputMappingContextFactory())
unreal.EditorAssetLibrary.save_asset('/Game/Input/IMC_Default')
```

Key mappings configured via Blueprint graph — see `enhanced_input.md` and `blueprint_graphs.md`.

### Step 8: Wire GameMode

CRITICAL: `generated_class()` returns None if Blueprint is not compiled. Always compile first.

```python
gm = unreal.load_asset('/Game/Framework/GM_MyGame')
char_bp = unreal.load_asset('/Game/Framework/BP_PlayerCharacter')
pc = unreal.load_asset('/Game/Framework/BP_PlayerController')

# Compile all BPs FIRST — generated_class() needs compilation
lib = unreal.BlueprintEditorLibrary
lib.compile_blueprint(gm)
lib.compile_blueprint(char_bp)
lib.compile_blueprint(pc)

cdo = unreal.get_default_object(gm.generated_class())
cdo.set_editor_property('default_pawn_class', char_bp.generated_class())
cdo.set_editor_property('player_controller_class', pc.generated_class())

lib.compile_blueprint(gm)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/GM_MyGame')
```

### Step 9: Set GameMode in Config

Use `edit_file` tool on `Config/DefaultEngine.ini`:
```ini
[/Script/EngineSettings.GameMapsSettings]
GlobalDefaultGameMode=/Game/Framework/GM_MyGame.GM_MyGame_C
GameDefaultMap=/Game/Maps/MainLevel
```
IMPORTANT: `_C` suffix required — references generated class, not Blueprint asset.

Also set via World Settings:
```python
world = unreal.EditorLevelLibrary.get_editor_world()
ws = world.get_world_settings()
ws.set_editor_property('default_game_mode', gm.generated_class())
```

### Step 10: Level Setup

Check `[Project Framework]` for what's MISSING, then add only what's needed:
```python
eas = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

# Only spawn if [Project Framework] shows MISSING
eas.spawn_actor_from_class(unreal.PlayerStart, unreal.Vector(0, 0, 100))

# Floor
floor = eas.spawn_actor_from_class(unreal.StaticMeshActor, unreal.Vector(0, 0, 0))
floor_mesh = unreal.load_asset('/Engine/BasicShapes/Plane')
floor.static_mesh_component.set_editor_property('static_mesh', floor_mesh)
floor.set_actor_scale3d(unreal.Vector(50, 50, 1))

# Lighting
eas.spawn_actor_from_class(unreal.DirectionalLight, unreal.Vector(0, 0, 500))
eas.spawn_actor_from_class(unreal.SkyLight, unreal.Vector(0, 0, 500))
eas.spawn_actor_from_class(unreal.SkyAtmosphere, unreal.Vector(0, 0, 0))

unreal.EditorLevelLibrary.save_current_level()
```

### Step 11: Optional — GameState, PlayerState

```python
# GameState
factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.GameStateBase)
gs = asset_tools.create_asset('GS_MyGame', '/Game/Framework', None, factory)
unreal.BlueprintEditorLibrary.compile_blueprint(gs)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/GS_MyGame')

# PlayerState
factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.PlayerState)
ps = asset_tools.create_asset('PS_MyGame', '/Game/Framework', None, factory)
unreal.BlueprintEditorLibrary.compile_blueprint(ps)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/PS_MyGame')

# Wire into GameMode CDO
cdo = unreal.get_default_object(gm.generated_class())
cdo.set_editor_property('game_state_class', gs.generated_class())
cdo.set_editor_property('player_state_class', ps.generated_class())
unreal.BlueprintEditorLibrary.compile_blueprint(gm)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/GM_MyGame')
```

### Step 12: Optional — GameInstance
```python
factory = unreal.BlueprintFactory()
factory.set_editor_property('parent_class', unreal.GameInstance)
gi = asset_tools.create_asset('GI_MyGame', '/Game/Framework', None, factory)
unreal.BlueprintEditorLibrary.compile_blueprint(gi)
unreal.EditorAssetLibrary.save_asset('/Game/Framework/GI_MyGame')
```
Set in `Config/DefaultEngine.ini`: `GameInstanceClass=/Game/Framework/GI_MyGame.GI_MyGame_C`

## Working with Existing Framework

When `[Project Framework]` shows existing classes — MODIFY, don't recreate.

### Adding Components (with duplicate check)
```python
helpers = unreal.UNDBlueprintHelpers
bp = unreal.load_asset('/Game/Blueprints/BP_ExistingCharacter')
# List existing to check before adding
existing = helpers.get_blueprint_components(bp)
print(f'Existing: {existing}')
# add_component_to_blueprint auto-skips if name already exists
helpers.add_component_to_blueprint(bp, 'CameraComponent', 'FollowCamera')
```

### Modifying GameMode Wiring
```python
gm = unreal.load_asset('/Game/Blueprints/GM_Existing')
cdo = unreal.get_default_object(gm.generated_class())
# Read current
print(f'Current pawn: {cdo.get_editor_property("default_pawn_class")}')
# Change
new_char = unreal.load_asset('/Game/Blueprints/BP_NewCharacter')
cdo.set_editor_property('default_pawn_class', new_char.generated_class())
unreal.BlueprintEditorLibrary.compile_blueprint(gm)
unreal.EditorAssetLibrary.save_asset(str(gm.get_path_name()))
```

### Level Actors (no duplicates)
```python
eas = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
actors = eas.get_all_level_actors()
has_ps = any(a.get_class().get_name() == 'PlayerStart' for a in actors)
if not has_ps:
    eas.spawn_actor_from_class(unreal.PlayerStart, unreal.Vector(0, 0, 100))
```

### Integrating New System into Existing Game
1. Read `[Project Framework]` — understand what exists
2. Add components with duplicate check (see above)
3. Add variables — `BlueprintEditorLibrary` (see `blueprint_variables.md`)
4. Add functions — `BuildGraphFromSpec` (see `blueprint_graphs.md`)
5. Create supporting assets only if not in `[Project Assets]`
6. Always compile + save modified BPs

## Character Movement

```python
helpers = unreal.UNDBlueprintHelpers
# CharacterMovement is auto-created by ACharacter — find its name first
for c in helpers.get_blueprint_components(char_bp):
    print(c)  # Look for "CharacterMovement (CharacterMovementComponent)"
# Set properties on the existing CharacterMovement component
helpers.set_component_property(char_bp, 'CharacterMovement', 'MaxWalkSpeed', '600.0')
helpers.set_component_property(char_bp, 'CharacterMovement', 'JumpZVelocity', '420.0')
helpers.set_component_property(char_bp, 'CharacterMovement', 'AirControl', '0.2')
helpers.set_component_property(char_bp, 'CharacterMovement', 'GravityScale', '1.0')
```

## Multiplayer

- **GameMode** — server ONLY. Never access on clients.
- **GameState/PlayerState** — auto-replicated to all clients.
- **PlayerController** — server + owning client only.
- **Character** — replicated (movement auto-replicated by CharacterMovementComponent).

## CRITICAL RULES

1. **ALWAYS read `[Project Framework]`** in system prompt before creating anything
2. **NEVER duplicate** — if GM/Character/PC/Input exist, MODIFY them
3. **Setup order** (new only) — GM → Character → PC → Input → Wire → Level
4. **ACharacter already has** CapsuleComponent, CharacterMovement, Mesh — NEVER add duplicates
5. **Check level** before spawning — `[Project Framework]` shows what's missing
6. **`_C` suffix** in config paths: `GM_MyGame.GM_MyGame_C`
7. **PlayerStart required** — game won't spawn player without it
8. **Always compile + save** after modifying BPs
9. **Check `[Project Assets]`** for existing meshes, AnimBPs, materials
10. **Enhanced Input** is mandatory in UE5
11. Discover APIs via `query_ue_api('keyword')` — don't hardcode names
