> 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/all-out-docs/docs-es/programacion/syntax.md).

# Primeros pasos con CSL

CSL es el lenguaje de programación personalizado de All Out, creado para que escribir juegos multijugador sea tan fácil como crear juegos para un solo jugador.

CSL es el lenguaje de scripting personalizado de All Out. Está tipado estáticamente y se parece más a Go/Odin — excepto **el estado de juego se sincroniza automáticamente desde el servidor a los clientes**.

{% hint style="info" %}
No necesitas escribir RPCs, SyncVars ni replicación personalizada. ¡Nosotros nos encargamos de eso por ti!
{% endhint %}

## Tu primer script (`main.csl`)

Cuando creas un nuevo proyecto, All Out genera un `main.csl` en la carpeta de tu proyecto `scripts/` carpeta.

```go
import "core:ao"

// ============================================================================
// Ciclo de vida global
// ============================================================================

ao_before_scene_load :: proc() {
    // Registra definiciones de objetos, monedas, etc.
    // Se ejecuta después de que existe la Scene vacía, antes de que se cargue el contenido de la escena.
}

ao_start :: proc() {
    // Se llama una vez cuando comienza la escena.
}

ao_update :: proc(dt: float) {
    // Se llama en cada frame.
}

ao_late_update :: proc(dt: float) {
    // Se llama en cada frame después de ao_update.
}

ao_draw :: proc(dt: float) {
    // Solo visual. La UI interactiva pertenece a ao_update/ao_late_update.
}

// ============================================================================
// Ciclo de vida del jugador
// ============================================================================

Player :: class : Player_Base {
    ao_start :: method() {
    }

    ao_update :: method(dt: float) {
    }

    ao_late_update :: method(dt: float) {
    }

    ao_draw :: method(dt: float) {
        // Solo visual. Nunca UI interactiva.
    }

    ao_end :: method() {
    }
}
```

Por ejemplo, registra un mensaje cuando se une cada jugador:

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

Si quieres una explicación más profunda de cuándo se ejecutan estas funciones, consulta [Ciclo de vida de juego/frame](/all-out-docs/docs-es/programacion/game-frame-lifecycle.md).

## Importaciones

Tu `main.csl` debe importar `core:ao` y cualquier carpeta que crees (como `ui/`, `abilities/`, etc.).

```go
import "core:ao"
import "ui"
```

Las importaciones apuntan a carpetas, no a archivos individuales. Una importación de carpeta incluye los `.csl` archivos de esa carpeta. Las importaciones dentro de esa carpeta se resuelven de forma relativa a ella:

```go
// main.csl
import "core:ao"
import "abilities"

// abilities/projectiles.csl
import "helpers"
```

{% hint style="warning" %}
En la mayoría de los proyectos, **importa en `main.csl` solo**. No esparzas importaciones por muchos archivos — acabarás teniendo problemas confusos de orden/visibilidad.
{% endhint %}

## Declaraciones (variables y constantes)

Las declaraciones asocian un nombre a un valor.

### Variables

```go
// Forma general
<name>: <type> = <expression>;
```

Se puede omitir `<type>` o `<expression>` uno de los dos:

```go
// Tipo explícito
my_value: int = 42;

// Inferencia de tipo
my_value := 42; // inferido como int

// Inicialización cero (igual que my_value := 0;)
my_value: int;
```

### Constantes

Las constantes usan `::` y deben ser constantes en tiempo de compilación. Pueden ser escalares, cadenas, tipos, valores de procedimiento, arreglos o literales compuestos:

```go
MAX_PLAYERS :: 12;

Spawn_Desc :: struct {
    name: string;
    pos: v2;
}

DEFAULT_SPAWNS: []Spawn_Desc : {
    {"Azul", {-4, 0}},
    {"Rojo", {4, 0}},
};
```

Esto no es válido (porque `a` no es una constante en tiempo de compilación):

```go
a := 123;
b :: a; // error de compilación
```

Los inicializadores de variables globales también deben ser constantes en tiempo de compilación. Usa `ao_before_scene_load` o `ao_start` para la inicialización en tiempo de ejecución.

### Variables globales

Los globales usan la misma sintaxis de declaración que los locales. Pueden dejarse con inicialización cero o inicializarse con constantes en tiempo de compilación, incluidos structs, arreglos, valores de procedimiento y `typeid` valores:

```go
score_total: int;
spawn_names: []string = {"Azul", "Rojo"};
default_type: typeid = int;

start_round :: proc() {
}

on_match_start: proc() = start_round;
```

Los globales son mutables y persisten durante la vida útil de la instancia del script. Una recarga en caliente conserva sus valores actuales, así que un inicializador editado solo se aplica después de reiniciar el juego; la recarga en caliente sí aplica los cambios a `::` las constantes, así que declara las tablas de datos de solo lectura como constantes. Evita usar globales para el estado de juego por jugador.

## Tipos

### Tipos primitivos

* Enteros con signo: `s8`, `s16`, `s32`, `s64`
* Enteros sin signo: `u8`, `u16`, `u32`, `u64`
* Booleanos: `bool`
* Flotantes: `f32`, `f64`
* Alias:
  * `int` == `s64`
  * `uint` == `u64`
  * `float` == `f32`
* Vectores: `v2`, `v3`, `v4`
* `string`
* `typeid`
* `any`

### Literales numéricos

Los literales fraccionarios pueden incluir u omitir el cero antes del punto decimal. Por ejemplo, `.52` y `-.52` son equivalentes a `0.52` y `-0.52`:

```go
opacity := .75;
offset := v2{-.52, 1.22};
```

### Tipos vectoriales

`v2` tiene `.x`, `.y`; `v3` añade `.z`; `v4` añade `.w` — todos campos float:

```go
pos: v2 = {10, 20};        // x=10, y=20
color: v4 = {1, 0, 0, 1};  // rojo con alpha=1
offset := v3{1, 4, 9};     // inferencia de tipo
```

## Structs y clases

Los structs son **tipos por valor** (se copian al asignarse). Las clases son **tipos por referencia** (se crean con `new`).

### Structs (tipos por valor)

```go
Food_Definition :: struct {
    name: string;
    food_value: int;
}

food: Food_Definition;
food.name = "Manzana";
food.food_value = 10;
```

### Clases (tipos por referencia)

```go
Foo :: class {
    value: int = 10;
    position: v2 = {12, 34};
}

foo := new(Foo);
```

Los campos de clase pueden tener valores predeterminados. Una clase derivada puede sobrescribir los valores heredados sin redeclarar el campo:

```go
Enemy :: class {
    health: int = 100;
    speed: float = 3.0;
}

Boss :: class : Enemy {
    health = 500;
    speed = 1.5;
}
```

### Herencia

Los structs/clases pueden heredar de otros structs/clases:

```go
Animal :: class {
    name: string;
    age: int;
}

Dog :: class : Animal {
    breed: string;
}

dog := new(Dog);
dog.name = "Buddy";
dog.age = 5;
dog.breed = "Labrador";
```

Los métodos derivados con el mismo nombre ocultan los métodos heredados. Las llamadas no son virtuales: la búsqueda de métodos usa el tipo estático del receptor, así que llamar a través de un valor tipado como base nunca selecciona un método con el mismo nombre del tipo derivado.

Las clases derivadas exponen un campo `base` de solo lectura que ve el mismo objeto como su tipo padre inmediato. Permite que la búsqueda de métodos comience explícitamente desde el tipo padre:

```go
Animal :: class {
    speak :: method() {
        log_info("animal");
    }
}

Dog :: class : Animal {
    speak :: method() {
        base.speak();
        log_info("perro");
    }
}

dog := new(Dog);
dog.speak();        // Dog.speak
dog.base.speak();   // Animal.speak

animal: Animal = dog;
animal.speak();     // Animal.speak, no Dog.speak
```

`base` no es una palabra reservada. Una declaración local llamada `base` oculta normalmente el campo heredado.

## Procedimientos y métodos

### Procedimientos (`proc`)

```go
add :: proc(a: int, b: int) -> int {
    return a + b;
}

result := add(2, 4); // 6
```

Los procedimientos que devuelven varios valores deben encerrar los tipos de retorno entre paréntesis:

```go
get_thing :: proc() -> (Thing, bool) {
    return g_thing, true;
}

thing, ok := get_thing();
```

Usa la misma forma separada por comas con `=` = para asignar resultados a destinos existentes. Una asignación puede consumir una sola expresión de múltiples retornos o una expresión por destino:

```go
thing: Thing;
ok := false;
thing, ok = get_thing();
a, b = b, a;
```

El número de destinos debe coincidir con el número de valores. Las expresiones del lado derecho se evalúan de izquierda a derecha antes de que se resuelva o escriba cualquier destino. Esto hace que los intercambios sean seguros, y `_` puede ignorar un valor:

```go
a, b = make_pair(b, a);
_, ok = get_thing();
```

Las expresiones del lado derecho separadas por comas son compatibles específicamente con las instrucciones de asignación; no crean un valor tupla que pueda usarse en otra expresión.

Los procedimientos son valores normales y pueden asignarse/almacenarse como cualquier otro valor:

```go
op := proc "op_add" (a: int, b: int) -> int { return a + b; };
op = proc "op_mul" (a: int, b: int) -> int { return a * b; };
```

Todo procedimiento necesita una identidad única para que la recarga en caliente pueda emparejar el código viejo y el nuevo. Las declaraciones con nombre (`add :: proc(...)`) lo obtienen automáticamente. Un `:=`/`=` valor proc permanece anónimo — un proc anónimo por ámbito está bien, pero si un ámbito contiene más de uno, dale a cada uno un nombre de enlace único con `proc "name" (...)` o el compilador informa una colisión de ID de enlace.

### Métodos (`método`)

Usa `method()` dentro de un struct/clase. Los métodos tienen un parámetro implícito de referencia `this` .

```go
Dog :: class {
    name: string;

    bark :: method() {
        log_info("% dice guau!");
    }
}

dog := new(Dog);
dog.name = "Buddy";
dog.bark();
```

### Acceso a campos vs llamadas a métodos

Usa `.` para campos y métodos:

```go
hp := player.health;        // acceso a campo
player.respawn();          // llamada a método
entity.set_local_scale({2, 2});
```

{% hint style="info" %}
Cualquier procedimiento puede llamarse como un “método” si su primer parámetro coincide con el tipo del receptor. Los métodos reales y los campos con valores de procedimiento del tipo tienen prioridad antes de que CSL recurra a un procedimiento libre coincidente.
{% endhint %}

## Arreglos

CSL tiene unos pocos tipos “similares a arreglos” que usarás constantemente:

* **Arreglos fijos**: `[4]int`
* **Slices / arreglos administrados**: `[]T` (a menudo usados como “vista de solo lectura” de un arreglo)
* **Arreglos dinámicos**: `[..]T` (lista redimensionable)
* **Arreglos no administrados**: `[^]T` (usados en firmas de API integradas como `format_string`, `log_info`, etc. — pasa valores como `{a, b, c}`)

Los arreglos dinámicos pueden inicializarse con un literal compuesto. Exponen `.data`, `.count`, y `.capacity`, y usan sintaxis de llamada a método para las operaciones:

```go
numbers: [..]int = {10, 20};
numbers.append(30);

log_info("count: %", {numbers.count});
log_info("first: %", {numbers[0]});
```

Para una guía completa (incluidos los patrones de eliminación), consulta [Arreglos y colecciones](/all-out-docs/docs-es/programacion/arrays-and-collections.md).

## Flujo de control

### Si / sino

```go
if hp <= 0 {
    die();
} else if hp < 25 {
    Notifier.notify(player, "¡Salud baja!");
} else {
    // todo bien
}
```

### Switch

Usa `default:` para la cláusula predeterminada. Los casos admiten **múltiples valores** (separados por comas) y **rangos**. No hay caída al estilo C.

```go
Item_Tier :: enum {
    COMMON;
    UNCOMMON;
    RARE;
    EPIC;
    LEGENDARY;
}

get_tier_color :: proc(tier: Item_Tier) -> v4 {
    switch tier {
        case .COMMON:              return {0.7, 0.7, 0.7, 1.0};
        case .UNCOMMON:            return {0.3, 0.8, 0.3, 1.0};
        case .RARE, .EPIC:         return {0.3, 0.5, 1.0, 1.0};
        case .LEGENDARY:           return {1.0, 0.8, 0.2, 1.0};
        default:                   return {1, 1, 1, 1};
    }
}

// Casos por rango
get_difficulty :: proc(level: int) -> string {
    switch level {
        case 1..5:          return "Fácil";
        case 6..<11:        return "Medio";
        case 11..20, 25:    return "Difícil";
        default:            return "Desconocido";
    }
}
```

`..` incluye ambos extremos. `..<` excluye el extremo superior.

### Mientras / para

```go
while condition {
    // ...
}

// Rango inclusivo: 0..9 incluye 9
for i: 0..9 {
    log_info("i=%", {i});
}

// Rango semiabierto: 0..<count excluye count
for i: 0..<items.count {
    log_info("elemento %", {items[i]});
}

// Iteración inversa
for i: 0..<items.count #reverse {
    log_info("elemento invertido %", {items[i]});
}

// Iterar elementos de array/slice
for item: my_items {
    // ...
}

// Con variable de índice
for item, i: my_items {
    log_info("elemento % en el índice %", {item, i});
}

// Iterar iteradores personalizados (común para entidades/componentes)
for enemy: component_iterator(Enemy) {
    enemy.update_ai();
}
```

Basado en iteradores personalizados `for` los bucles requieren un `next :: method() -> bool` y un `current` campo.

## Conversión

Usa `expr.(T)` o `cast(T)expr` para convertir:

```go
a := 123.4;
b := a.(int);   // 123
c := cast(float)b; // 123.0
```

Cuando el tipo de destino ya es conocido, puedes dejar que CSL lo infiera:

```go
i: int = a.();
j: int = cast a;
```

Los valores de procedimiento no pueden convertirse a otra firma de procedimiento ni a/desde valores que no sean de procedimiento. Para una reinterpretación deliberada de bajo nivel de la firma, usa una declaración unsafe: `#unsafe retyped := (&callback).(*Other_Callback).*`.

Las conversiones de clase deben permanecer dentro de una sola jerarquía de herencia. Las conversiones ascendentes no hacen trabajo en tiempo de ejecución; las conversiones descendentes verifican el tipo concreto del objeto y lanzan un error si hay discordancia. Los valores nulos de clase permanecen nulos al convertir hacia abajo.

## Paso por referencia: `ref` (preferido)

Cuando necesites modificar un parámetro, **prefiere `ref`** sobre los punteros sin procesar. Tomar una dirección cruda con `&` requiere un `#unsafe` contexto. Una sola declaración unsafe sin llaves permanece en el ámbito circundante.

```go
update_position :: proc(pos: ref v2, velocity: v2, dt: float) {
    pos.x += velocity.x * dt;
    pos.y += velocity.y * dt;
}

my_pos := v2{0, 0};
update_position(ref my_pos, {10, 5}, 0.16);
```

## Clausuras y callbacks

Los literales de procedimiento pueden capturar parámetros y variables locales del entorno:

```go
invoke_twice :: proc(callback: proc()) {
    callback();
    callback();
}

count_twice :: proc(initial: int) -> int {
    value := initial;
    invoke_twice(proc() {
        value += 1;
    });
    return value;
}
```

Los literales de procedimiento con captura son expresiones de callback con ámbito. Solo pueden invocarse inmediatamente o pasarse directamente a un parámetro de procedimiento que acepte capturas. Ese parámetro puede invocar el callback o reenviarlo directamente a otro parámetro que acepte capturas. Los procedimientos con captura no pueden asignarse a variables, devolverse ni colocarse en campos, globales, colecciones u otro almacenamiento.

Un parámetro de procedimiento sin anotación podría recibir un procedimiento con captura, por lo que tiene la misma restricción de invocar o reenviar. Marca el parámetro `#no_captures` cuando el procedimiento necesite almacenarlo, devolverlo o manipularlo de otro modo. Entonces los llamadores solo podrán pasar procedimientos sin captura; conserva el estado persistente en userdata explícito:

```go
Callback_State :: class {
    value: int;
}

Callback :: proc(userdata: Object);
saved: Callback;
saved_userdata: Object;

install :: proc(userdata: Object, #no_captures callback: Callback) {
    saved_userdata = userdata;
    saved = callback;
}

increment :: proc(userdata: Object) {
    state := userdata.(Callback_State);
    state.value += 1;
}

state := new(Callback_State);
install(state, increment);
```

La anotación forma parte del contrato del procedimiento receptor y también puede aparecer en un tipo de procedimiento, como `Retainer :: proc(userdata: Object, #no_captures callback: Callback)`. Un procedimiento con captura y un procedimiento simple siguen teniendo la misma representación de procedimiento/entorno de dos palabras; el entorno es cero para un procedimiento simple. Las llamadas fallan si se usa reinterpretación de bajo nivel para pasar un procedimiento con captura a un `#no_captures` parámetro.

## Información de tipos (tipos como valores)

`typeid` los valores pueden pasarse a procedimientos polimórficos:

```go
default_of :: proc($T: typeid) -> T {
    t: T;
    return t;
}

a := default_of(int);      // 0
b := default_of(string);   // ""
c := default_of([4]int);   // {0, 0, 0, 0}
```

`#type_info(T)` devuelve datos de reflexión para un tipo. Usa su `id` campo al registrar o formatear el nombre del tipo:

```go
ti := #type_info(Thing);
log_info("tipo: %", {ti.id});
```

## Buenas prácticas (CSL en All Out)

* **Evita el estado global del gameplay.** Se conectan varios jugadores — almacena el estado por jugador en `Player` en su lugar.
* **Dibuja toda la interfaz de usuario del jugador desde `Player.ao_late_update`.** Envuélvelo en `is_local_or_server()`.
* **Usa `is_local()` solo para anulaciones visuales específicas del jugador.** No cambies dentro de él el estado del gameplay ni de la interfaz.
* **Valores predeterminados orientados a móvil.** No dependas de la entrada de teclado/ratón a menos que tu juego esté explícitamente enfocado en PC.
* **Si no estás seguro sobre la sintaxis o las API**, abre el `.csl_engine` archivo en `api_references/` en tu proyecto.
