> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/scripting/player-model.md).

# Adding Player Logic

In All Out, the player is a first-class gameplay object. Your `Player :: class : Player_Base` is where you put **per-player state** (health, loadouts, cooldowns, UI toggles, progression, etc).

{% hint style="warning" %}
Assume multiple players are connected. Avoid global state that would break when there’s more than one player.
{% endhint %}

## Per-player state

Store gameplay state on the player or objects owned by the player.

```go
Player :: class : Player_Base {
    health: int;
    inventory_open: bool;
}
```

## Server + client: what runs where?

All Out automatically syncs gameplay state from the server to clients. Gameplay methods run on both the predicting client and the server, so do not guard them with `Game.is_server()`.

Two common checks:

* `is_local_or_server()`: all player UI and the input it produces
* `is_local()`: player-specific visual overrides only

```go
Player :: class : Player_Base {
    ao_late_update :: method(dt: float) {
        if is_local_or_server() {
            // Draw all player UI and handle its input here.
        }

        if is_local() {
            // Apply player-specific visibility here, but do not change fields.
        }
    }
}
```

{% hint style="info" %}
Draw all player UI from `Player.ao_late_update` under `is_local_or_server()`. Fields changed only inside `is_local()` are replaced during reconciliation.
{% endhint %}

## Player identity and profile data

`Player_Base` exposes identity fields you’ll use often:

* `p.get_username() -> string`
* `p.get_user_id() -> string`
* `p.avatar_color`
* `p.device_kind` (`.PHONE`, `.TABLET`, `.PC`)
* `p.is_game_owner()`, `p.is_game_editor()`, `p.is_owner_or_editor()` (the game's owner and their team), `p.is_vip()`, `p.is_moderator()`, `p.is_youtuber()`
* `p.is_chat_open()` and UI rect helpers such as `p.get_chat_rect()`

```go
Player :: class : Player_Base {
    ao_start :: method() {
        log_info("player joined: % (%)", {this.get_username(), this.get_user_id()});
    }
}
```

## Temporary state reasons

Player states such as freezing, invisibility, name hiding, ghosting, movement-input blocking, and joystick blocking use named reasons. Reasons are counted: every call to an `add_*_reason` method requires one matching `remove_*_reason` call.

Use the corresponding `has_*_reason` method when you need set-like behavior or need to avoid adding the same reason repeatedly:

```go
if !player.has_freeze_reason("cutscene") {
    player.add_freeze_reason("cutscene");
}

if player.has_freeze_reason("cutscene") {
    player.remove_freeze_reason("cutscene");
}
```

The same pattern is available for `invisibility`, `name_invisibility`, `name_offset`, `ghost`, `disable_movement_input`, and `joystick_disable` reasons. Each family also has `has_any_*_reason()` when only the presence of any reason matters. Do not call an add method every frame: matching by name does not make it idempotent.

## Persistence: where to store player progress

* **Economy**: currencies (coins/gems/xp) with automatic persistence + creator portal editing\
  See [Economy](/data-and-persistence/economy.md).
* **Save**: general key/value persistence (settings, quest state, unlock lists, etc)\
  See [Save System](/data-and-persistence/save.md).
* **Inventory**: item stacks/instances in a player inventory (optionally auto-saved)\
  See [Inventory](/core-engine-concepts/inventory.md).

## Common pattern: load saved values in `ao_start`

```go
Player :: class : Player_Base {
    xp: s64;
    selected_skin: string;

    ao_start :: method() {
        xp = Save.get_int(this, "xp", 0);
        selected_skin = Save.get_string(this, "selected_skin", "default");
    }
}
```

## Best practices

* **Keep per-player state on `Player`.** Avoid globals for anything player-specific.
* **Run gameplay in the shared predicted path.** Draw player UI under `is_local_or_server()` and reserve `is_local()` for player-specific visual overrides.
* **Prefer the built-in persistence APIs** instead of rolling your own (Economy/Save/Inventory).

## Player\_Base reference

See `api_references/core/ao/core.csl_engine` in the project folder for the complete `Player_Base` API.
