# 一键命令 (OOC) 专题 —— 原理、坑、正确姿势

> 这份文档是给 AI 用的。里面每一条都是**在真服务器上跑出来的**，
> 不是从 wiki 抄的。看完你应该能直接产出可用的 OOC 配置。
>
> ## ⚠️ 先记住：你的产物是「配置」，不是那条 `summon`
>
> | 模式 | 你产出 | 用户怎么用 |
> |---|---|---|
> | 单向自定义 | `cmd` 列表 + `types` 数组（或用 `json` 参数整段导入） | 粘进工具 → 点生成 |
> | 三维自定义 | 网格 JSON `{"x,y,z":{"id","tag","data"}}` | 粘进「三维网格 → 粘贴网格 JSON 导入」 |
>
> 那条一键 `summon …` 是**工具算出来的**。`/api/commands.json` 里两个 OOC 命令都带
> `oneCommand` 字段，能看到成品长什么样 —— 但**别自己拼**，你只要把配置写对。

## 1. OOC 是什么

把 N 条命令塞进一个**掉落方块载体**，粘进一个命令方块执行后：

1. 载体在空中生成 → **竖直下落** → 落到地上
2. 落地的瞬间，里面的命令方块矿车一条条执行 `setblock`
3. 一列（或一片）命令方块被搭出来，**自动开机**
4. 收尾把载体自己清理掉，只留命令方块继续跑

**载体结构**（从下往上）：

```
falling_block  ← BlockState: redstone_block
  └ armor_stand (Health:0s)
      └ falling_block  ← BlockState: activator_rail
          └ command_block_minecart × N   ← 每个的 Command 是一条 setblock
```

## 2. 两条铁律（错一条整柱就废）

### ★ 铁律一：第一块必须是「循环」

柱子里**第一块**命令方块必须是 `repeating_command_block`，其余全是 `chain_command_block`。

全是 `chain` 的话**没有任何触发源**，整柱一条都不会执行 —— 而且现场看起来"方块都在"，
特别容易误判成"命令写错了"。

每块都要带 `auto:1b`，落地自己就开机，不需要红石。

### ★ 铁律二：方块 NBT 的位置

| 版本 | 写法 |
|---|---|
| 1.13+ | `setblock ~ ~ ~ minecraft:chest{Items:[]} replace` ← **NBT 贴着方块名** |
| 1.12 | `setblock ~ ~ ~ minecraft:chest 0 replace {Items:[]}` ← **NBT 放最后** |

写成 `… replace {Items:[]}`（NBT 在模式后面）在 1.13+ 会报 `错误的命令参数`。

`BlockEntityTag` **只用于物品形式**（`give`）。`setblock` 要直接写 `{Items:[…]}` —— 套了会被当成无关标签丢掉。

## 3. 单向自定义（`ooc-custom`）

**什么时候用**：命令是一条链、要按顺序执行。菜单、流程、一次性布置。

### 参数

| 参数 | 说明 |
|---|---|
| `cmds` | 命令列表，**一行一条**。以 `#` 开头当注释，不执行。 |
| `types` | **JSON 数组**，和 `cmds` 的行**一一对应**：`["repeating","chain","chain"]`。数组短了后面自动补 `chain`。 |
| `layout` | `column` = 排成一列（柱子）；`line` = 排成一行（逐行部署） |
| `dir` | 柱子生长方向：`y+` `y-` `x+` `x-` `z+` `z-`（默认 `y+`） |
| `dir2` | 逐行模式的方向 |
| `pos` | 生成位置（**留空 = 头顶上方**，一般别填） |
| `start` | 逐行模式的起始坐标 |
| `clearGen` | 收走载体（矿车/盔甲架/掉落方块/柱根的红石块与铁轨），**柱子留着** |
| `clearExec` | 连柱子一起清掉 |

### 清理怎么走

收尾会补 5 条：`kill` × 3（矿车 / 盔甲架 / 掉落方块）+ 把柱根的红石块与铁轨 `fill air` +
一条 `fill air` **把自己连同前面几条清理块一起抹掉**。

> **坑**：红石块和铁轨落在柱根**下方 1~2 格**，而清理方块在柱子**末端** ——
> 写死 `~ ~-2 ~` 够不着，必须按"自己在柱子上的偏移量"往回数。

## 4. 三维自定义（`ooc3d`）

**什么时候用**：结构不是一条链、要按三维位置精确摆。小屋、字牌、立体装置。

### 网格格式

```json
{
  "1,0,0": { "id": "minecraft:repeating_command_block[facing=up]",
             "tag": "{Command:\"say hi\",auto:1b,TrackOutput:0b}", "data": "" },
  "2,0,0": { "id": "minecraft:stone", "tag": "", "data": "" }
}
```

- key 是 `"x,y,z"` —— 相对坐标，**字符串**，逗号分隔，不要空格
- `id` 可以带方块状态：`minecraft:oak_stairs[facing=east,half=top]`
- `tag` 是 NBT（1.13+ 直接写，不要套 BlockEntityTag）
- `data` 是 1.12 的数据值，1.13+ 留空

### 三个占用格（不能放）

| 格子 | 是什么 |
|---|---|
| `0,0,0` | **你原来放的那个命令方块**（粘贴 summon 的那块）—— 网格不会动它 |
| `0,1,0` | 机构：红石块 |
| `0,2,0` | 机构：铁轨 + 命令方块矿车 |

**所以你放不了 `x=0,z=0` 这根竖轴** —— 想摆竖直的一列，挪到 `x=1` 去。

### 偏移公式（重要）

每格最终写成相对**矿车**的偏移：

```
偏移 = (x, y-2, z)
```

例：格子 `(1,0,0)` → `~1 ~-2 ~`；格子 `(1,4,0)` → `~1 ~2 ~`。
（因为机构占着 y=1 和 y=2，矿车在 y=2，所以往下数 2 格。）

生成时按 **(y, z, x)** 排序：先低后高，再按 z，最后按 x。

### 清理

- `clearMech`：`kill` 矿车/盔甲架/掉落方块 + `fill ~ ~-1 ~ ~ ~ ~ air`（把机构那两格抹平）
- `clearSrc`：删掉**源命令方块**。做法是从机构上方**往上扫** `srcScan` 格（默认 12），
  只对 `command_block` 生效。

> **为什么能这么扫**：掉落方块是**竖直下落**的，所以源方块一定在落点**正上方、同一 x/z**；
> 而只扫 `command_block`，就不会误伤刚摆的结构。
> 扫描命令排在**最前面**执行 —— 那会儿场上除了源方块没有别的命令方块。

## 5. 踩坑记录（都真出过）

### ① 几个内部数组必须一一对齐

三维那条路上，`offsets / faces / blocks / blockTags / types / runs` 必须**长度和顺序都一致**。

出过的 bug：普通格子忘了往 `runs` 里补 0，`runs` 就只剩收尾那几项，
输出层看到 `runs[0]` 是真值，就把**前面几格**当成"纯执行格"，
它们的 `setblock` 全被吞掉 —— 现象是"柱子里少了好几块，而且第一块没了"。

单向那条走 `column` 布局、**根本不用 `runs`**，所以这个 bug 一直没暴露。

### ② id 带着 `[方块状态]` 时，类型判定要先剥掉

`repeating_command_block[facing=up]` 用 `/repeating_command_block$/` 匹配会**失败**
（`$` 锚点在 `]` 后面）。要先剥掉 `[state]` 和 `{NBT}` 再比。

### ③ 文本类字段要包成 JSON 文本组件

书页尤其明显：`pages:[第一页]` 服务器报 **「需要值」**（裸文字不是合法 NBT）。
要写成 `pages:['{"text":"第一页"}']`。

### ④ 收尾会清掉附近的盔甲架

`kill @e[type=armor_stand,distance=..16]` 是 OOC 的固有行为。
落点 16 格内有装饰盔甲架会被一起清掉 —— 执行前挪开。

### ⑤ 多人的话要按人隔离

`unless entity @e[type=snowball]` 是**全局**判定。多人服上别人触发时你的状态也会被结算。
要隔离得给实体打 owner 标签。

## 6. 完整例子：雪球菜单

需求：扔雪球选菜单 —— 扔得越久（飞得越远）= 选项越靠后；落地那一瞬生效。

**原理**：雪球每飞一刻给玩家 +1 分，分数 = 飞了多久。

**7 条循环命令**（`cmds`）：

```
execute as @e[type=snowball] at @s positioned ~ ~ ~ run scoreboard players add @p snowball 1
execute as @a[scores={snowball=1..}] at @s positioned ~ ~ ~ run title @s actionbar [{"text":"▶ 雪球菜单  当前 ","color":"yellow"},{"score":{"name":"@s","objective":"snowball"},"color":"gold"},{"text":"  ：≤10 加速 · ≤25 回满血 · ≤∞ 一颗钻石","color":"yellow"}]
execute as @a[scores={snowball=1..10}] at @s positioned ~ ~ ~ unless entity @e[type=snowball] run effect give @s minecraft:speed 10 2 true
execute as @a[scores={snowball=11..25}] at @s positioned ~ ~ ~ unless entity @e[type=snowball] run effect give @s minecraft:instant_health 1 10 true
execute as @a[scores={snowball=26..}] at @s positioned ~ ~ ~ unless entity @e[type=snowball] run give @s minecraft:diamond
execute as @a[scores={snowball=1..}] at @s positioned ~ ~ ~ unless entity @e[type=snowball] run give @s minecraft:snowball
execute as @a[scores={snowball=1..}] at @s positioned ~ ~ ~ unless entity @e[type=snowball] run scoreboard players set @s snowball 0
```

外加**一次性**（别放进柱子，会每刻报"已存在"）：

```
scoreboard objectives add snowball dummy {"text":"雪球菜单","color":"aqua"}
```

**`types`**：

```json
["repeating","chain","chain","chain","chain","chain","chain"]
```

**关键点**：

- 判定时机是 `unless entity @e[type=snowball]`（场上没雪球了）= 刚落地的那一瞬
- 顺序不能换：先结算选项 → 补雪球 → 最后清零
- 分数区间要**排除 0**（写 `1..10` 不是 `..10`），否则没扔过雪球的人每刻都在触发

### 同一份菜单用三维版

把这一列挪到 `x=1`（`x=0,z=0` 被机构占了）：

```json
{
  "1,0,0": { "id": "minecraft:repeating_command_block[facing=up]", "tag": "{Command:\"<第1条>\",auto:1b,TrackOutput:0b}", "data": "" },
  "1,1,0": { "id": "minecraft:chain_command_block[facing=up]",    "tag": "{Command:\"<第2条>\",auto:1b,TrackOutput:0b}", "data": "" },
  "…": "类推到 1,6,0"
}
```

生成出来每块落在 `~1 ~-2 ~` 到 `~1 ~4 ~` —— 正好连成一根柱子。
