# 游戏设计器 UI 优化（布局撑满 + 元素复选框 + 双语标签）— 设计

- 日期：2026-07-22
- 状态：设计已确认，待实现
- 范围：编辑器工具层 `addons/game_designer/`，**游戏运行时代码与数据文件格式零改动**

---

## 1. 背景与问题

用户反馈游戏设计器三点待优化：

1. **输入框无法完整显示内容** —— `designer_ui.gd` 的 `line()`/`opt()`/`spin()` 只设固定最小宽度、无 `SIZE_EXPAND_FILL`，控件不随面板拉伸，长文本（显示名/描述/VFX/JSON/长中文选项）被截断。
2. **法术元素标签用逗号文本易错** —— 应改可视化控件。元素标签是**多值**（如 `plasma_storm` = `["tag:water","tag:lightning"]`）。
3. **部分显示仍为纯英文** —— `spell_tab.TYPE_NAMES`/`LOGIC_OPS`、`wave_tab.TYPE_NAMES`、`resonance_tab.MATCH` 等为纯英文。要求统一**双语（中文 English）**显示，含 spell_tab（其 `DMG_LABELS`/`ST_LABELS` 现为纯中文，也改双语）。

### 词表事实

- 元素标签词表（全数据仅这 5 个）：`tag:fire 火` / `tag:ice 冰` / `tag:lightning 雷` / `tag:water 水` / `tag:poison 毒`。
- 枚举整数来源：`DamageType`（0物理/1火/2冰/3雷/4毒/5奥术）、`StatusID`（0无/1燃烧/2冻结/3中毒/4潮湿/5沾油/6眩晕/7连击/8易伤）。

---

## 2. 布局修复（全 7 页，中央改动）

`addons/game_designer/designer_ui.gd`：`line()` / `opt()` / `spin()` 三个工厂各追加：
```gdscript
ctrl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
```
效果：行内 `cell_label`（70px，autosize，不变）之后，输入控件吃掉剩余宽度；表格页（敌人/波次）各列随之填满面板宽度。零数据改动，全 7 页受益。

---

## 3. 共享元素词表（`designer_ui.gd` 新增常量）

```gdscript
const ELEM_LABELS := ["火 Fire", "冰 Ice", "雷 Lightning", "水 Water", "毒 Poison"]  # 显示（双语）
const ELEM_TAGS   := ["tag:fire", "tag:ice", "tag:lightning", "tag:water", "tag:poison"]  # 存储，index 对齐
```
spell_tab（复选框）与 resonance_tab（元素下拉）共用，单一事实源。通过 `UI.ELEM_LABELS` / `UI.ELEM_TAGS` 访问（`const UI = preload(designer_ui.gd)`，常量可跨脚本访问）。

---

## 4. spell_tab —— 元素复选框 + 双语标签

### 4.1 元素标签复选框（替代 `_tags` 单行文本）
- UI：一行「元素标签」标签 + 5 个 CheckBox（文本取 `UI.ELEM_LABELS`）；下方一行「自定义标签」+ 小 LineEdit（收纳非元素 tag，防丢）。
- 新增成员：`var _elem_checks: Array = []`（5 个 CheckBox）、`var _custom_tags: LineEdit`。
- 加载（`_on_select`）：
  ```gdscript
  var etags: Array = d.get("element_tags", [])
  for i in _elem_checks.size():
      _elem_checks[i].button_pressed = UI.ELEM_TAGS[i] in etags
  var custom := []
  for t in etags:
      if not (t in UI.ELEM_TAGS): custom.append(String(t))
  _custom_tags.text = ",".join(custom)
  ```
- 应用（`_on_apply`，替换原 `_tags` 解析块）：
  ```gdscript
  var tags: Array = []
  for i in _elem_checks.size():
      if _elem_checks[i].button_pressed: tags.append(UI.ELEM_TAGS[i])
  for t in _custom_tags.text.split(",", false):
      var s := t.strip_edges()
      if s != "" and not (s in tags): tags.append(s)
  ```
- ⚠️ 多元素法术会被规整为**固定元素顺序**（火冰雷水毒），`element_tags` 语义为集合、顺序无意义——属预期简化（回读比较按集合）。

### 4.2 双语标签（仅显示；存储 index/key 不变）
```gdscript
const TYPE_NAMES := ["动作 ACTION", "修饰 MODIFIER", "触发 TRIGGER", "逻辑 LOGIC"]
const ACTION_KINDS := ["弹道 Projectile", "毒池 Zone", "召唤 Summon"]
const LOGIC_OPS := ["every_n_shots", "if_hp_below", "if_enemy_nearby", "loop"]  # 存储 key，不变
const LOGIC_OP_LABELS := ["每 N 次 every_n_shots", "生命低于 if_hp_below", "附近有敌 if_enemy_nearby", "循环 loop"]  # 新增，供下拉显示
const DMG_LABELS := ["物理 Physical", "火 Fire", "冰 Ice", "雷 Lightning", "毒 Poison", "奥术 Arcane"]
const ST_LABELS := ["无 None", "燃烧 Burn", "冻结 Freeze", "中毒 Poison", "潮湿 Wet", "沾油 Oily", "眩晕 Stun", "连击 Combo", "易伤 Vuln"]
```
- `_lop` 下拉改用 `UI.opt(LOGIC_OP_LABELS, 0)`；`_read_meta` 仍 `meta["logic_op"] = LOGIC_OPS[_lop.selected]`，`_on_select` 仍 `_lop.selected = LOGIC_OPS.find(...)`（index↔key 逻辑不变，只换显示数组）。
- 列表前缀 `"[%s] %s" % [TYPE_NAMES[...], sid]` 变为 `"[动作 ACTION] action_spark_bolt"`（可接受）。

---

## 5. wave_tab —— 双语标签

```gdscript
const TYPE_NAMES = ["杂兵 Basic", "快速 Fast", "护甲 Armored", "精英 Elite", "首领 MiniBoss", "终焉 Boss"]
const BOSS_NAMES = ["无 None", "首领 MiniBoss", "终焉 Boss"]  # 存储走 BOSS_VALS，不变
```
（存储均为 index / BOSS_VALS，改标签零数据影响。）

---

## 6. resonance_tab —— 双语标签 + 元素下拉

- 匹配方式：`MATCH`（存储 key `adjacent`/`anywhere_in_deck`）不变；新增 `const MATCH_LABELS = ["相邻 adjacent", "卡组内任意 anywhere_in_deck"]` 供下拉显示。`_match = _row("匹配方式", UI.opt(MATCH_LABELS, 0))`；`_on_apply` 仍 `"match": MATCH[_match.selected]`；`_on_select` 仍 `_match.selected = max(0, MATCH.find(...))`。
- 元素 A / B：`_p0`/`_p1` 由 LineEdit → `UI.opt(UI.ELEM_LABELS, 0)`。
  - 加载：`_p0.selected = max(0, UI.ELEM_TAGS.find(String(p0)))`（非元素值回退第 0 项）。
  - 应用：`"pattern": [UI.ELEM_TAGS[_p0.selected], UI.ELEM_TAGS[_p1.selected]]`。
  - 列表显示 `_refresh` 用 pattern 原值（tag 串），不变。
  - ⚠️ 若某配方 pattern 含非元素值（现数据无），会被下拉回退丢失——现 `resonance.json` 全为元素，风险为零；如未来需要非元素 pattern 再扩展。

---

## 7. 既有双语标签归一为「中文 English」序

统一所有枚举下拉显示为「中文 English」序（存储均为 index，改文案零数据影响）：

- `core_tab.gd`（`addons/game_designer/core_tab.gd`）：
  ```gdscript
  const TOPO = ["线性 LINEAR", "矩阵 MATRIX", "电路 CIRCUIT"]
  const FEAT = ["无 None", "记忆 PERSISTENT_MEMORY", "双流 DUAL_STREAM", "末位施法 ALWAYS_CAST_LAST", "洗牌 SHUFFLE_DECK", "无限法术 INFINITE_SPELLS"]
  ```
  （`FEAT` 原有 3 项纯英文 `ALWAYS_CAST_LAST`/`SHUFFLE_DECK`/`INFINITE_SPELLS` 补中文；`topology`/`feature_tags` 存 index 不变。）
- `status_tab.gd`（`addons/game_designer/status_tab.gd`）：
  ```gdscript
  const STACK = ["刷新 REFRESH", "叠层 INTENSITY", "独立 INDEPENDENT"]
  ```
  （`stack_mode` 存 index 不变。）

**不改**：
- `enemy_tab.NAMES`（`["杂兵 Basic", ...]`）已是「中文 English」序，且 enemy `name` 是**持久化字段**（写入 enemies.json）——保持原样。
- `balance_tab` 标签本就中文（`敌人血量×`/`初学者`/`MiniBoss(W8) 血量` 等），无纯英文枚举下拉，无需改。

全 7 页均受益于第 2 点布局撑满。

---

## 8. 组件边界与改动清单

| 文件 | 改动 |
|---|---|
| `designer_ui.gd` | line/opt/spin 加 `SIZE_EXPAND_FILL`；新增 `ELEM_LABELS`/`ELEM_TAGS` 常量 |
| `spell_tab.gd` | 元素复选框 + 自定义框替换 `_tags`；`TYPE_NAMES`/`ACTION_KINDS`/`DMG_LABELS`/`ST_LABELS` 改双语；新增 `LOGIC_OP_LABELS` 并用于 `_lop` 下拉 |
| `wave_tab.gd` | `TYPE_NAMES`/`BOSS_NAMES` 改双语 |
| `resonance_tab.gd` | `MATCH_LABELS` 新增用于下拉；元素 A/B 改元素下拉 |
| `core_tab.gd` | `TOPO`/`FEAT` 归一为「中文 English」序（FEAT 补 3 项中文） |
| `status_tab.gd` | `STACK` 归一为「中文 English」序 |

其它运行时脚本、数据文件、其它 tab 逻辑不动。`enemy_tab`/`balance_tab` 仅受布局修复，文案不动。

---

## 9. 验证

1. `validate_script` 四个改动文件全绿；`reload_project` + `get_editor_errors` 无报错。
2. 回读往返测试（`execute_editor_script`）：
   - **spell**：逐条 `_on_select`→`_read_meta` meta 一致（同上一特性）；`element_tags` 作为**集合**与原一致；含非元素自定义 tag 的情形自定义框保留。
   - **resonance**：逐条 `_on_select`→ 重建 record，`pattern`（两元素）与 `match` 与原一致。
3. `get_editor_screenshot` 目测：各页输入框撑满面板宽、下拉显示双语、法术页元素为复选框。

## 10. 范围外（YAGNI）

- 不改游戏运行时代码 / 数据文件格式 / 数据内容。
- 不动 `enemy_tab.NAMES`（持久化）与 `balance_tab` 中文标签。
- 不做元素标签之外的 tag 下拉词表（自定义框已覆盖）。
- resonance pattern 仍限两元素（现结构），不扩展多元素配方。

## 11. 验收标准

1. 打开游戏设计器，各页输入框/下拉框随面板宽度撑满，长文本不再截断。
2. 法术页「元素标签」为 5 个元素复选框 + 自定义框；`plasma_storm` 正确勾选 水+雷；保存后 `element_tags` 集合不变。
3. 法术页 类型/动作种类/logic_op/伤害类型/施加状态 均双语显示，保存的 `type`/`logic_op`/`damage_type` 等值不变。
4. 波次页 类型/Boss、共鸣页 匹配方式 均双语；共鸣页 元素 A/B 为元素下拉，`resonance.json` 回读往返一致。
5. `validate_script`/`reload_project` 无报错；spell 与 resonance 回读往返测试 FAILS=0。
