> 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 的基本类型、枚举、结构体、类、引用和多态。

本页是 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;
opacity := .75; // 与 0.75 相同
```

当安全时，CSL 会自动提升数字：较小的有符号整数可以变成更宽的有符号整数，较小的无符号整数可以变成更宽的无符号整数，当目标能够容纳完整范围时，无符号整数可以变成有符号整数，并且 `f32` 可以变成 `f64`.

### 字符串

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

```go
item_id := "sword";
log_info("拾取了 %", {item_id});

first_name := "Ada";
last_name := "Lovelace";
full_name := first_name + " " + last_name;
full_name += "!";
```

字符串拼接既可用于运行时表达式，也可用于编译期常量。

### 向量

`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;
}
```

每个派生类都有一个只读的 `base` 字段，它将同一对象视为其直接父类型。当派生方法覆盖了同名父方法，并且需要显式选择父方法时使用它：

```go
Weapon :: class {
    fire :: method() {
        // 基础行为。
    }
}

Burst_Weapon :: class : Weapon {
    fire :: method() {
        base.fire();
        // 额外的连发行为。
    }
}
```

CSL 的方法不是虚函数。接收者的静态类型决定方法查找从哪里开始；基类型的值永远不会调用仅在其派生运行时类型上声明的同名方法。

{% 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 = {123, 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).
