> 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-zh/jiao-ben-bian-xie/types-and-structures.md).

# 语言基础

本页介绍 CSL 的“数据建模”一侧：原始类型、结构体/类，以及你将用来清晰表示游戏状态的模式。

{% hint style="info" %}
如果你是新手，请先看 [CSL 入门](/all-out-docs/docs-zh/jiao-ben-bian-xie/syntax.md). 本页假定你已经知道如何声明变量并编写 `proc`/`method`.
{% endhint %}

## 原始类型

### 数字和布尔值

* 有符号整数： `s8`, `s16`, `s32`, `s64` （别名： `int` == `s64`)
* 无符号整数： `u8`, `u16`, `u32`, `u64` （别名： `uint` == `u64`)
* 浮点数： `f32` （别名： `float`), `f64`
* 布尔值： `bool`

```go
health: int = 100;
speed: float = 6.5;
is_dead: bool = false;
```

CSL 在安全时会扩大数字类型：较小的有符号整数可以变为更大的有符号整数，较小的无符号整数可以变为更大的无符号整数，当目标类型能够容纳完整范围时，无符号整数可以变为更大的有符号整数，并且 `f32` 可以变为 `f64`.

### 字符串

`string` 经常用于 ID、名称和格式化。

```go
item_id := "sword";
log_info("picked up %", {item_id});
```

### 向量

`v2`, `v3`, `v4` 是内置的数学/数据类型：

```go
pos: v2 = {10, 20};
color: v4 = {1, 0, 0, 1};
```

## 枚举

枚举非常适合用于可读的“模式/状态”字段：

```go
Game_Phase :: enum {
    LOBBY;
    RUNNING;
    POST_GAME;
}

phase := Game_Phase.LOBBY;

if phase == .RUNNING {
    // ...
}
```

{% hint style="info" %}
当编译器能够推断枚举类型时，枚举值通常会用简写 `.VALUE` 来引用。
{% endhint %}

## 结构体（值类型）

当你想要以较低成本到处拷贝的“纯数据”时，使用结构体（配置、小型状态块、描述符）。

```go
Damage_Desc :: struct {
    amount: int;
    knockback: v2;
}

hit := Damage_Desc{amount=10, knockback={2, 1}};
```

结构体可以继承其他结构体。复合字面量会先包含继承来的字段：

```go
Base_Stats :: struct {
    health: int;
}

Enemy_Stats :: struct : Base_Stats {
    speed: float;
}

stats: Enemy_Stats = {100, 4.5};
```

### 何时使用结构体

* 描述符/配置对象（`*_Desc`)
* 小型、独立的状态，你希望按值复制
* 你想嵌入到另一种类型中的数据

### 何时不要使用结构体

* 在多个系统之间共享的大型可变状态（用类）
* “身份”对象（玩家、物品、长期存在的对象）（用类）

## 类（引用类型）

类是引用类型。使用 `new(...)`.

```go
Enemy :: class {
    health: int = 50;
    target: v2 = {0, 0};
}

e := new(Enemy);
```

### 继承

你可以继承其他结构体/类：

```go
Weapon_Definition :: class {
    id: string;
    name: string;
}

Gun_Definition :: class : Weapon_Definition {
    fire_rate: float;
}
```

派生类会继承默认字段值。它们可以在类体中用 `field = value` 覆盖继承来的默认值：

```go
Weapon_Definition :: class {
    damage: int = 10;
}

Rocket_Definition :: class : Weapon_Definition {
    damage = 100;
    splash_radius: float = 3.0;
}
```

{% hint style="warning" %}
当存在真正的“is-a（是一个）”关系时使用继承。对于大多数游戏玩法数据来说，组合（字段）更简单，也更容易维护。
{% endhint %}

## 数组、切片和动态数组

你会看到三种常见形式：

* **固定数组**: `[N]T` （编译时大小）
* **切片 / 托管数组**: `[]T` （数组的视图）
* **动态数组**: `[..]T` （可调整大小的列表）

示例：

```go
spawn_points: [4]v2 = {{0,0}, {5,0}, {0,5}, {5,5}};

alive_enemy_ids: [..]int;
alive_enemy_ids.append(123);
alive_enemy_ids.append(456);
```

完整指南请参见 [数组和集合](/all-out-docs/docs-zh/jiao-ben-bian-xie/arrays-and-collections.md).

## 按引用传递（`ref`)

如果某个过程需要修改值，请使用 `ref`:

```go
apply_damage :: proc(health: ref int, amount: int) {
    health -= amount;
}

hp := 100;
apply_damage(ref hp, 10);
```

这对于那种会逐步裁切/缩小矩形的“布局风格” API 特别有用：

```go
draw_panel :: proc(rect: ref Rect) {
    header := rect.cut_top(50);
    draw_header(header);
    // rect 现在是内容的剩余区域
}
```

## 类型转换

使用 `expr.(T)` 或 `cast(T)expr`:

```go
f := 12.7;
i := f.(int);       // 12
f2 := cast(float)i; // 12.0
```

当目标类型已经已知时， `expr.()` 和 `cast expr` 会推断目标类型。

## 类型多态（类似泛型的过程）

CSL 支持使用 `$` 在调用点推导类型：

```go
min :: proc(a: $T, b: T) -> T {
    if a < b return a;
    return b;
}

a := min(10, 20);       // T == int
b := min(1.25, 3.5);    // T == float
```

## 类型作为值（`typeid`)

`typeid` 是一个在运行时和/或用于多态调用时表示某个类型的值：

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

zero := default_of(int);
empty := default_of(string);
```

`value.#type` 会返回类值的运行时类型。当你从基类向下转换时，如果精确的派生类型很重要，请先使用它。

## 实用建模指南

* **每个玩家的状态保存在 `Player`.** 如果它在不同玩家之间应该不同，就不应该是全局的。
* **描述符优先使用小型结构体** 并将运行时状态存放在类/组件中。
* **在共享的预测路径中运行游戏逻辑。** 不要只在 `is_local()` 下计算游戏状态，也不要用 `Game.is_server()`.

对于玩家特定模式，请参见 [添加玩家逻辑](/all-out-docs/docs-zh/jiao-ben-bian-xie/player-model.md). 关于世界的 ECS 侧，请参见 [实体与组件](/all-out-docs/docs-zh/jiao-ben-bian-xie/entities-and-components.md).
