sapdon 3.2.2 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +255 -121
  2. package/doc/dev/architecture.md +418 -0
  3. package/doc/dev/cli.md +467 -0
  4. package/doc/dev/core.md +751 -0
  5. package/doc/dev/lr-paradigm.md +85 -0
  6. package/doc/dev/oc.md +582 -0
  7. package/doc/dev/workflow.md +257 -0
  8. package/doc/hello_sapdon/hello_sapdon.md +3 -3
  9. package/doc/user/api/biome.md +558 -0
  10. package/doc/user/api/block.md +1530 -0
  11. package/doc/user/api/entity.md +685 -0
  12. package/doc/user/api/extra.md +231 -0
  13. package/doc/user/api/item.md +1125 -0
  14. package/doc/user/api/neo-guidebook.md +409 -0
  15. package/doc/user/api/recipe.md +427 -0
  16. package/doc/user/api/sapdon-ui.md +185 -0
  17. package/doc/user/api/texture.md +181 -0
  18. package/doc/user/config/build-config.md +83 -0
  19. package/doc/user/config/mod-info.md +33 -0
  20. package/doc/user/faq.md +160 -0
  21. package/doc/user/quick-start.md +122 -0
  22. package/doc/user/tutorials/block.md +474 -0
  23. package/doc/user/tutorials/entity.md +356 -0
  24. package/doc/user/tutorials/item.md +449 -0
  25. package/doc/user/tutorials/neo-guidebook-experience.md +381 -0
  26. package/doc/user/tutorials/neo-guidebook.md +640 -0
  27. package/doc/user/tutorials/recipe.md +278 -0
  28. package/doc/user/tutorials/sapdon-ui.md +207 -0
  29. package/package.json +8 -3
  30. package/prod/cli/index.js +1 -1
  31. package/prod/cli/start.js +1 -1458
  32. package/prod/core/index.d.ts +4564 -2166
  33. package/prod/core/index.js +1 -59015
  34. package/prod/core/package.json +7 -0
  35. package/prod/oc/index.d.ts +350 -108
  36. package/prod/oc/index.js +1 -1
  37. package/prod/oc/package.json +7 -0
  38. package/prod/utils/index.d.ts +20 -9
  39. package/prod/utils/index.js +1 -1
  40. package/prod/utils/package.json +7 -0
  41. package/doc/BlockAPI.md +0 -145
  42. package/doc/api.md +0 -128
  43. package/doc/oc/index.md +0 -0
  44. package/doc/sapdon-ts.md +0 -64
  45. package/src/templates/js_sapdon/build.config +0 -23
  46. package/src/templates/js_sapdon/main.mjs +0 -4
  47. package/src/templates/js_sapdon/mod.info +0 -7
  48. package/src/templates/js_sapdon/pack_icon.png +0 -0
  49. package/src/templates/js_sapdon/package.json +0 -20
  50. package/src/templates/js_sapdon/res/animations/animation_item.animation.json +0 -34
  51. package/src/templates/js_sapdon/res/animations/large_item.animation.json +0 -27
  52. package/src/templates/js_sapdon/res/models/blocks/crop.geo.json +0 -48
  53. package/src/templates/js_sapdon/res/models/entity/animation/animation_item.geo.json +0 -26
  54. package/src/templates/js_sapdon/res/models/entity/animation/large_item.geo.json +0 -28
  55. package/src/templates/js_sapdon/res/textures/blocks/none.png +0 -0
  56. package/src/templates/js_sapdon/res/textures/blocks/test_log_oak.png +0 -0
  57. package/src/templates/js_sapdon/res/textures/blocks/test_log_top.png +0 -0
  58. package/src/templates/js_sapdon/res/textures/items/masterball.png +0 -0
  59. package/src/templates/js_sapdon/scripts/custom_components/cropComponent.js +0 -50
  60. package/src/templates/js_sapdon/scripts/custom_components/items/gui_book.js +0 -37
  61. package/src/templates/js_sapdon/scripts/custom_components/registry.js +0 -25
  62. package/src/templates/js_sapdon/scripts/index.js +0 -0
  63. package/src/templates/ts_sapdon/build.config +0 -23
  64. package/src/templates/ts_sapdon/main.ts +0 -11
  65. package/src/templates/ts_sapdon/mod.info +0 -7
  66. package/src/templates/ts_sapdon/pack_icon.png +0 -0
  67. package/src/templates/ts_sapdon/package.json +0 -20
  68. package/src/templates/ts_sapdon/res/models/blocks/crop.geo.json +0 -48
  69. package/src/templates/ts_sapdon/res/textures/blocks/test_log_oak.png +0 -0
  70. package/src/templates/ts_sapdon/res/textures/blocks/test_log_top.png +0 -0
  71. package/src/templates/ts_sapdon/res/textures/items/masterball.png +0 -0
  72. package/src/templates/ts_sapdon/scripts/components/cropComponent.ts +0 -44
  73. package/src/templates/ts_sapdon/scripts/components/items/guiBook.ts +0 -36
  74. package/src/templates/ts_sapdon/scripts/components/registry.ts +0 -24
  75. package/src/templates/ts_sapdon/scripts/index.ts +0 -7
  76. package/src/templates/ts_sapdon/tsconfig.json +0 -117
@@ -0,0 +1,427 @@
1
+ # 配方系统 API 参考
2
+
3
+ ---
4
+
5
+ ## RecipeAPI
6
+
7
+ 工厂函数集合,用于创建各类配方并自动注册到游戏系统。
8
+
9
+ ```typescript
10
+ import { RecipeAPI, registry } from '@sapdon/core'
11
+ ```
12
+
13
+ 所有注册器方法均返回配方实例,支持链式调用。完成后调用 `registry.submit()` 提交数据。
14
+
15
+ ---
16
+
17
+ ### registerSimpleFurnace
18
+
19
+ 快速创建熔炉配方(自动设置 `furnace` 标签)。
20
+
21
+ ```typescript
22
+ RecipeAPI.registerSimpleFurnace(
23
+ identifier: string,
24
+ output: string,
25
+ input: string
26
+ ): AddonRecipeFurnace
27
+ ```
28
+
29
+ **示例**
30
+
31
+ ```typescript
32
+ RecipeAPI.registerSimpleFurnace('sapdon:smelt_ore', 'minecraft:iron_ingot', 'minecraft:iron_ore')
33
+ ```
34
+
35
+ ---
36
+
37
+ ### registerFurnace
38
+
39
+ 创建熔炉配方实例,返回 `AddonRecipeFurnace` 用于链式配置。
40
+
41
+ ```typescript
42
+ RecipeAPI.registerFurnace(identifier: string): AddonRecipeFurnace
43
+ ```
44
+
45
+ **示例**
46
+
47
+ ```typescript
48
+ RecipeAPI.registerFurnace('sapdon:smelt_gold')
49
+ .input('minecraft:gold_ore', 0, 1)
50
+ .output('minecraft:gold_ingot')
51
+ .tags(['furnace'])
52
+ ```
53
+
54
+ ---
55
+
56
+ ### registerSimpleShaped
57
+
58
+ 快速创建有序工作台配方(自动设置 `crafting_table` 标签)。
59
+
60
+ ```typescript
61
+ RecipeAPI.registerSimpleShaped(
62
+ identifier: string,
63
+ output: string,
64
+ pattern: string[],
65
+ key: Record<string, any>
66
+ ): AddonRecipeShaped
67
+ ```
68
+
69
+ **示例**
70
+
71
+ ```typescript
72
+ RecipeAPI.registerSimpleShaped(
73
+ 'sapdon:sword',
74
+ 'sapdon:sword',
75
+ [' X ', ' X ', ' Y '],
76
+ { X: { item: 'minecraft:iron_ingot' }, Y: { item: 'minecraft:stick' } }
77
+ )
78
+ ```
79
+
80
+ ---
81
+
82
+ ### registerShaped
83
+
84
+ 创建有序配方实例,返回 `AddonRecipeShaped` 用于链式配置。
85
+
86
+ ```typescript
87
+ RecipeAPI.registerShaped(identifier: string): AddonRecipeShaped
88
+ ```
89
+
90
+ **示例**
91
+
92
+ ```typescript
93
+ RecipeAPI.registerShaped('sapdon:armor')
94
+ .key({ X: { item: 'minecraft:diamond' } })
95
+ .pattern(['XXX', 'X X', ' '])
96
+ .output({ item: 'sapdon:armor', count: 1 })
97
+ .tags(['crafting_table'])
98
+ ```
99
+
100
+ ---
101
+
102
+ ### registerSimpleShapeless
103
+
104
+ 快速创建无序工作台配方(自动设置 `crafting_table` 标签)。
105
+
106
+ ```typescript
107
+ RecipeAPI.registerSimpleShapeless(
108
+ identifier: string,
109
+ output: string,
110
+ ingredients: any[]
111
+ ): AddonRecipeShapeless
112
+ ```
113
+
114
+ **示例**
115
+
116
+ ```typescript
117
+ RecipeAPI.registerSimpleShapeless(
118
+ 'sapdon:alloy',
119
+ 'sapdon:alloy_ingot',
120
+ [{ item: 'minecraft:iron_ingot' }, { item: 'minecraft:gold_ingot' }]
121
+ )
122
+ ```
123
+
124
+ ---
125
+
126
+ ### registerShapeless
127
+
128
+ 创建无序配方实例,返回 `AddonRecipeShapeless` 用于链式配置。
129
+
130
+ ```typescript
131
+ RecipeAPI.registerShapeless(identifier: string): AddonRecipeShapeless
132
+ ```
133
+
134
+ **示例**
135
+
136
+ ```typescript
137
+ RecipeAPI.registerShapeless('sapdon:mixture')
138
+ .ingredients([{ item: 'minecraft:diamond' }, { item: 'minecraft:emerald' }])
139
+ .output({ item: 'sapdon:mixture', count: 2 })
140
+ .tags(['crafting_table'])
141
+ ```
142
+
143
+ ---
144
+
145
+ ## AddonRecipe 基类
146
+
147
+ 所有配方类型继承自 `AddonRecipe`。
148
+
149
+ ```typescript
150
+ import { AddonRecipe } from '@sapdon/core'
151
+ ```
152
+
153
+ ### 方法
154
+
155
+ | 方法 | 返回 | 说明 |
156
+ |------|------|------|
157
+ | `identifier(id: string)` | `this` | 设置配方标识符 |
158
+ | `tags(tags: string[])` | `this` | 设置配方标签数组(覆盖) |
159
+ | `getId(): string` | `string` | 获取配方标识符 |
160
+
161
+ ---
162
+
163
+ ## AddonRecipeShaped
164
+
165
+ 有序配方类,继承自 `AddonRecipe`。
166
+
167
+ ```typescript
168
+ import { AddonRecipeShaped } from '@sapdon/core'
169
+ ```
170
+
171
+ ### 方法
172
+
173
+ | 方法 | 返回 | 说明 |
174
+ |------|------|------|
175
+ | `assumeSymmetry(): void` | 无 | 启用对称假设,未定义键的字符自动视为相同 |
176
+ | `key(key: Record<string, any>)` | `this` | 设置字符到物品的键映射 |
177
+ | `pattern(pattern: string[])` | `this` | 设置 3 行图案数组 |
178
+ | `priority(priority: number)` | `this` | 设置优先级(数值越小优先级越高) |
179
+ | `output(item: string \| object)` | `this` | 设置输出,支持字符串或 `{ item, count }` |
180
+ | `tags(tags: string[])` | `this` | 继承自 `AddonRecipe`,设置配方标签 |
181
+
182
+ **示例**
183
+
184
+ ```typescript
185
+ const recipe = new AddonRecipeShaped_1_20()
186
+ recipe
187
+ .identifier('sapdon:example')
188
+ .key({ A: { item: 'minecraft:iron_ingot' } })
189
+ .pattern(['AAA', 'A A', 'AAA'])
190
+ .output({ item: 'sapdon:example', count: 8 })
191
+ .assumeSymmetry()
192
+ .tags(['crafting_table'])
193
+ ```
194
+
195
+ ### 版本化类
196
+
197
+ | 类 | format_version |
198
+ |------|------|
199
+ | `AddonRecipeShaped_1_12` | `1.12` |
200
+ | `AddonRecipeShaped_1_17` | `1.17` |
201
+ | `AddonRecipeShaped_1_19` | `1.19` |
202
+ | `AddonRecipeShaped_1_20` | `1.20` |
203
+
204
+ ---
205
+
206
+ ## AddonRecipeShapeless
207
+
208
+ 无序配方类,继承自 `AddonRecipe`。
209
+
210
+ ```typescript
211
+ import { AddonRecipeShapeless } from '@sapdon/core'
212
+ ```
213
+
214
+ ### 方法
215
+
216
+ | 方法 | 返回 | 说明 |
217
+ |------|------|------|
218
+ | `static create(ver, def)` | `AddonRecipeShapeless` | 静态工厂方法 |
219
+ | `priority(priority: number)` | `this` | 设置优先级 |
220
+ | `ingredients(ingredients: any[])` | `this` | 设置原料数组 |
221
+ | `output(item: string \| object)` | `this` | 设置输出,支持字符串或 `{ item, count }` |
222
+ | `tags(tags: string[])` | `this` | 继承自 `AddonRecipe`,设置配方标签 |
223
+
224
+ **示例**
225
+
226
+ ```typescript
227
+ const recipe = new AddonRecipeShapeless_1_17()
228
+ recipe
229
+ .identifier('sapdon:example')
230
+ .ingredients([
231
+ { item: 'minecraft:iron_ingot' },
232
+ { tag: 'minecraft:planks' }
233
+ ])
234
+ .output({ item: 'sapdon:example', count: 4 })
235
+ .tags(['crafting_table'])
236
+ ```
237
+
238
+ ### 版本化类
239
+
240
+ | 类 | format_version |
241
+ |------|------|
242
+ | `AddonRecipeShapeless_1_12` | `1.12` |
243
+ | `AddonRecipeShapeless_1_17` | `1.17` |
244
+
245
+ ---
246
+
247
+ ## AddonRecipeFurnace
248
+
249
+ 熔炉配方类,继承自 `AddonRecipe`。
250
+
251
+ ```typescript
252
+ import { AddonRecipeFurnace } from '@sapdon/core'
253
+ ```
254
+
255
+ ### 方法
256
+
257
+ | 方法 | 返回 | 说明 |
258
+ |------|------|------|
259
+ | `static create(ver, def)` | `AddonRecipeFurnace` | 静态工厂方法 |
260
+ | `input(item: string, data?, count?)` | `this` | 设置输入物品,可选副损伤值(data)和数量 |
261
+ | `output(item: string)` | `this` | 设置输出物品 |
262
+ | `tags(tags: string[])` | `this` | 继承自 `AddonRecipe`,设置配方标签 |
263
+
264
+ **示例**
265
+
266
+ ```typescript
267
+ const recipe = new AddonRecipeFurnace_1_17()
268
+ recipe
269
+ .identifier('sapdon:smelt_ore')
270
+ .input('minecraft:iron_ore', 0)
271
+ .output('minecraft:iron_ingot')
272
+ .tags(['furnace'])
273
+ ```
274
+
275
+ ### 版本化类
276
+
277
+ | 类 | format_version |
278
+ |------|------|
279
+ | `AddonRecipeFurnace_1_12` | `1.12` |
280
+ | `AddonRecipeFurnace_1_17` | `1.17` |
281
+
282
+ ---
283
+
284
+ ## RecipeTags
285
+
286
+ 配方标签常量,用于指定配方适用的方块。
287
+
288
+ ```typescript
289
+ import { RecipeTags } from '@sapdon/core'
290
+ ```
291
+
292
+ | 常量 | 值 | 说明 |
293
+ |------|-----|------|
294
+ | `RecipeTags.Furnace` | `"furnace"` | 熔炉配方 |
295
+ | `RecipeTags.smoker` | `"smoker"` | 烟熏炉配方 |
296
+ | `RecipeTags.Campfire` | `"campfire"` | 篝火烹饪配方 |
297
+ | `RecipeTags.SoulCampfire` | `"soul_campfire"` | 灵魂篝火烹饪配方 |
298
+ | `RecipeTags.CraftingTable` | `"crafting_table"` | 工作台合成配方 |
299
+
300
+ > 注意:`RecipeTags.smoker` 首字母小写,与其余常量命名不一致。
301
+
302
+ **示例**
303
+
304
+ ```typescript
305
+ // 同时适用于熔炉和烟熏炉
306
+ recipe.tags([RecipeTags.Furnace, RecipeTags.smoker])
307
+ ```
308
+
309
+ ---
310
+
311
+ ## RecipeInputTags
312
+
313
+ 物品输入标签常量,用于原料的 `tag` 字段实现宽匹配。
314
+
315
+ ```typescript
316
+ import { RecipeInputTags } from '@sapdon/core'
317
+ ```
318
+
319
+ | 常量 | 值 | 说明 |
320
+ |------|-----|------|
321
+ | `RecipeInputTags.Armor` | `"minecraft:is_armor"` | 盔甲类物品 |
322
+ | `RecipeInputTags.Arrows` | `"minecraft:arrow"` | 箭 |
323
+ | `RecipeInputTags.Banners` | `"minecraft:banner"` | 旗帜 |
324
+ | `RecipeInputTags.Boats` | `"minecraft:boats"` | 船 |
325
+ | `RecipeInputTags.BookshelfBooks` | `"minecraft:bookshelf_books"` | 书架用书 |
326
+ | `RecipeInputTags.ChainmailTier` | `"minecraft:chainmail_tier"` | 锁链级装备 |
327
+ | `RecipeInputTags.ChestBoat` | `"minecraft:chest_boat"` | 带箱子的船 |
328
+ | `RecipeInputTags.Coals` | `"minecraft:coals"` | 煤炭类 |
329
+ | `RecipeInputTags.Cooked` | `"minecraft:is_cooked"` | 已烹饪食物 |
330
+ | `RecipeInputTags.CrimsonStems` | `"minecraft:crimson_stems"` | 绯红菌柄 |
331
+ | `RecipeInputTags.DiamondTier` | `"minecraft:diamond_tier"` | 钻石级装备 |
332
+ | `RecipeInputTags.Digger` | `"minecraft:digger"` | 挖掘工具 |
333
+ | `RecipeInputTags.Door` | `"minecraft:door"` | 门 |
334
+ | `RecipeInputTags.Fish` | `"minecraft:is_fish"` | 鱼类 |
335
+ | `RecipeInputTags.Food` | `"minecraft:is_food"` | 食物类 |
336
+ | `RecipeInputTags.GoldenTier` | `"minecraft:golden_tier"` | 金质装备 |
337
+ | `RecipeInputTags.HangingActor` | `"minecraft:hanging_actor"` | 悬挂实体 |
338
+ | `RecipeInputTags.HangingSign` | `"minecraft:hanging_sign"` | 悬挂告示牌 |
339
+ | `RecipeInputTags.Hatchet` | `"minecraft:is_axe"` | 斧 |
340
+ | `RecipeInputTags.Hoe` | `"minecraft:is_hoe"` | 锄 |
341
+ | `RecipeInputTags.HorseArmor` | `"minecraft:horse_armor"` | 马铠 |
342
+ | `RecipeInputTags.IronTier` | `"minecraft:iron_tier"` | 铁质装备 |
343
+ | `RecipeInputTags.LeatherTier` | `"minecraft:leather_tier"` | 皮革装备 |
344
+ | `RecipeInputTags.LecternBooks` | `"minecraft:lectern_books"` | 讲台用书 |
345
+ | `RecipeInputTags.Logs` | `"minecraft:logs"` | 原木类 |
346
+ | `RecipeInputTags.LogsThatBurn` | `"minecraft:logs_that_burn"` | 可燃原木 |
347
+ | `RecipeInputTags.MangroveLogs` | `"minecraft:mangrove_logs"` | 红树木 |
348
+ | `RecipeInputTags.Meat` | `"minecraft:is_meat"` | 肉类 |
349
+ | `RecipeInputTags.Minecart` | `"minecraft:is_minecart"` | 矿车 |
350
+ | `RecipeInputTags.MusicDiscs` | `"minecraft:music_disc"` | 唱片 |
351
+ | `RecipeInputTags.NetheriteTier` | `"minecraft:netherite_tier"` | 下界合金级装备 |
352
+ | `RecipeInputTags.Pickaxe` | `"minecraft:is_pickaxe"` | 镐 |
353
+ | `RecipeInputTags.PiglinLoved` | `"minecraft:piglin_loved"` | 猪灵喜爱物品 |
354
+ | `RecipeInputTags.PiglinRepellents` | `"minecraft:piglin_repellents"` | 猪灵驱退物品 |
355
+ | `RecipeInputTags.Planks` | `"minecraft:planks"` | 木板 |
356
+ | `RecipeInputTags.Sand` | `"minecraft:sand"` | 沙子 |
357
+ | `RecipeInputTags.Shovel` | `"minecraft:is_shovel"` | 锹 |
358
+ | `RecipeInputTags.Sign` | `"minecraft:sign"` | 告示牌 |
359
+ | `RecipeInputTags.SoulFireBaseBlocks` | `"minecraft:soul_fire_base_blocks"` | 灵魂火基底 |
360
+ | `RecipeInputTags.SpawnEgg` | `"minecraft:spawn_egg"` | 刷怪蛋 |
361
+ | `RecipeInputTags.StoneBricks` | `"minecraft:stone_bricks"` | 石砖 |
362
+ | `RecipeInputTags.StoneCraftingMaterials` | `"minecraft:stone_crafting_materials"` | 石质合成材料 |
363
+ | `RecipeInputTags.StoneTier` | `"minecraft:stone_tier"` | 石质装备 |
364
+ | `RecipeInputTags.StoneToolMaterials` | `"minecraft:stone_tool_materials"` | 石质工具材料 |
365
+ | `RecipeInputTags.Sword` | `"minecraft:is_sword"` | 剑 |
366
+ | `RecipeInputTags.Tool` | `"minecraft:is_tool"` | 工具 |
367
+ | `RecipeInputTags.VibrationDamper` | `"minecraft:vibration_damper"` | 振动阻尼 |
368
+ | `RecipeInputTags.WarpedStems` | `"minecraft:warped_stems"` | 诡异菌柄 |
369
+ | `RecipeInputTags.WoodenSlabs` | `"minecraft:wooden_slabs"` | 木台阶 |
370
+ | `RecipeInputTags.WoodenTier` | `"minecraft:wooden_tier"` | 木质装备 |
371
+ | `RecipeInputTags.Wool` | `"minecraft:wool"` | 羊毛 |
372
+
373
+ **示例**
374
+
375
+ ```typescript
376
+ import { RecipeAPI, registry, RecipeInputTags } from '@sapdon/core'
377
+
378
+ // 使用标签匹配所有木板
379
+ RecipeAPI.registerShapeless('sapdon:framed_plank')
380
+ .ingredients([
381
+ { tag: RecipeInputTags.Planks },
382
+ { tag: RecipeInputTags.Planks },
383
+ { tag: RecipeInputTags.Planks }
384
+ ])
385
+ .output('sapdon:framed_plank')
386
+ .tags(['crafting_table'])
387
+ ```
388
+
389
+ ---
390
+
391
+ ## 类型汇总
392
+
393
+ ```typescript
394
+ // 工厂
395
+ RecipeAPI.registerSimpleFurnace(id, output, input)
396
+ RecipeAPI.registerFurnace(id)
397
+ RecipeAPI.registerSimpleShaped(id, output, pattern, key)
398
+ RecipeAPI.registerShaped(id)
399
+ RecipeAPI.registerSimpleShapeless(id, output, ingredients)
400
+ RecipeAPI.registerShapeless(id)
401
+
402
+ // 有序配方
403
+ class AddonRecipeShaped extends AddonRecipe
404
+ assumeSymmetry(): void
405
+ key(map): this
406
+ pattern(rows): this
407
+ priority(num): this
408
+ output(item): this
409
+ tags(tags[]): this // 继承
410
+
411
+ // 无序配方
412
+ class AddonRecipeShapeless extends AddonRecipe
413
+ priority(num): this
414
+ ingredients(items): this
415
+ output(item): this
416
+ tags(tags[]): this // 继承
417
+
418
+ // 熔炉配方
419
+ class AddonRecipeFurnace extends AddonRecipe
420
+ input(item, data?, count?): this
421
+ output(item): this
422
+ tags(tags[]): this // 继承
423
+
424
+ // 标签常量
425
+ RecipeTags = { Furnace, smoker, Campfire, SoulCampfire, CraftingTable }
426
+ RecipeInputTags = { Armor, Food, Logs, Planks, ... }
427
+ ```
@@ -0,0 +1,185 @@
1
+ # Sapdon UI 页面壳系统 API 参考
2
+
3
+ Sapdon UI 是一套「sapdon_ui: 前缀标题路由 + 页面壳」的自定义 Server Form UI 系统。它用 TypeScript 声明式生成 `server_form.json` 路由结构与每个页面的独立 UI 文件,运行时通过 `ActionFormData` 的 title 前缀自动分流:`sapdon_ui:` 开头的标题渲染自定义全屏 UI,其它标题走原版原生表单。
4
+
5
+ ---
6
+
7
+ ## 目录
8
+
9
+ 1. [架构总览](#1-架构总览)
10
+ 2. [SapdonServerUI 类](#2-sapdonserverui-类)
11
+ 3. [SapdonPanel 类](#3-sapdonpanel-类)
12
+ 4. [SapdonButtonPanel 类](#4-sapdonbuttonpanel-类)
13
+ 5. [SapdonButton 类](#5-sapdonbutton-类)
14
+ 6. [运行时触发](#6-运行时触发)
15
+ 7. [已知注意点](#7-已知注意点)
16
+
17
+ ---
18
+
19
+ ## 1. 架构总览
20
+
21
+ ### 生成的文件(`registry.submit()` 后)
22
+
23
+ | 文件 | 内容 |
24
+ |------|------|
25
+ | `ui/server_form.json` | 路由壳:屏幕、native/自定义分流、页面壳 `custom_panel_content`、按钮模板 `form_button` |
26
+ | `ui/sapdon_ui_xxx.json` | 每个页面一个独立文件(内容面板 + 按键面板) |
27
+ | `ui/_ui_defs.json` | 自动登记所有 UI 文件 |
28
+
29
+ ### 路由结构
30
+
31
+ ```
32
+ third_party_server_screen@common.base_screen (type: screen)
33
+ └─ $screen_content = custom_full_screen
34
+ ├─ native_form@main_screen_content
35
+ │ visible = title 不含 'sapdon_ui:'
36
+ └─ sapdon_custom_full@sapdon_screen_content
37
+ visible = title 含 'sapdon_ui:'
38
+ └─ (每个注册页) @custom_panel_content ← $panel_id 精确匹配 title
39
+ ├─ content@$user_content_panel (下)
40
+ └─ buttons@$user_buttons_panel (上, 后绘制覆盖)
41
+ ```
42
+
43
+ ### 脚本 ↔ JSON UI 绑定
44
+
45
+ | ActionForm API | JSON UI 变量 | 说明 |
46
+ |----------------|-------------|------|
47
+ | `.title("sapdon_ui:xxx")` | `#title_text` | 前缀 `sapdon_ui:` = 自定义;`$panel_id` 精确匹配页面 |
48
+ | `.button("text")` | `#form_button_*` | 集合 `form_buttons`,网格按放置顺序绑定各按钮数据 |
49
+
50
+ ---
51
+
52
+ ## 2. SapdonServerUI 类
53
+
54
+ 负责生成 `server_form.json` 的完整路由壳(屏幕、分流、页面壳、form_button 模板),并提供页面注册入口。
55
+
56
+ ```typescript
57
+ import { SapdonServerUI } from '@sapdon/core'
58
+
59
+ SapdonServerUI.registerPage({
60
+ panelId: "sapdon_ui:apple", // title 精确匹配值(必须 sapdon_ui: 前缀)
61
+ name: "apple", // 页面控件名(默认 pageN)
62
+ contentPanel: "sapdon_ui_apple.apple_content_panel", // 内容面板引用
63
+ buttonsPanel: "sapdon_ui_apple.apple_buttons_panel", // 按键面板引用(可选)
64
+ })
65
+ ```
66
+
67
+ ### 参数
68
+
69
+ | 参数 | 类型 | 必填 | 说明 |
70
+ |------|------|------|------|
71
+ | `panelId` | `string` | ✅ | 触发此页的 ActionForm title(须含 `sapdon_ui:` 前缀) |
72
+ | `contentPanel` | `UIElement \| string` | ✅ | 内容面板元素或 `ns.name` 引用 |
73
+ | `buttonsPanel` | `UIElement \| string` | ❌ | 按键面板元素或引用(壳固定渲染,缺省会报引用缺失,纯内容页请传空面板) |
74
+ | `name` | `string` | ❌ | 注册在 `sapdon_screen_content` 里的控件名 |
75
+
76
+ ### 自动生成的内容
77
+
78
+ - `third_party_server_screen@common.base_screen`:`$screen_content` 指向 `custom_full_screen`,附带退出动画抑制与 `menu_cancel → menu_exit` 映射。
79
+ - `custom_full_screen`:`native_form`(`((#title_text - 'sapdon_ui:') = #title_text)` → 非自定义显示)与 `sapdon_screen_content`(取反)分流。
80
+ - `sapdon_screen_content`:`sapdon_ui:` 前缀可见,承载所有注册页。
81
+ - `custom_panel_content`:通用页面壳,`(#title_text = $panel_id)` 精确匹配,内容(下)+按键(上)两块。
82
+ - `form_button@common_buttons.light_text_button`:框架固定提供的表单按钮模板(16×16,集合绑定 `form_buttons`)。
83
+
84
+ ---
85
+
86
+ ## 3. SapdonPanel 类
87
+
88
+ 每个页面一个独立 UI 文件。把「内容面板 + 按键面板」组装进该页面的命名空间。
89
+
90
+ ```typescript
91
+ import { SapdonPanel } from '@sapdon/core'
92
+
93
+ new SapdonPanel("sapdon_ui_apple") // 生成 ui/sapdon_ui_apple.json (ns: sapdon_ui_apple)
94
+ .setContent(contentPanel) // 内容面板 UIElement
95
+ .setButtons(buttonsPanel) // 按键面板 UIElement
96
+ .build()
97
+ ```
98
+
99
+ | 方法 | 说明 |
100
+ |------|------|
101
+ | `setContent(panel)` | 设置内容面板元素(必须是 UIElement,其 id 即 `ns.xxx` 引用名) |
102
+ | `setButtons(panel)` | 设置按键面板元素 |
103
+ | `build()` | 返回生成的 `UISystem` |
104
+
105
+ > 页面的内容/按键面板元素 id 会作为 `registerPage` 里 `contentPanel`/`buttonsPanel` 的引用名,例如 `new Panel("apple_content_panel")` → `"sapdon_ui_apple.apple_content_panel"`。
106
+
107
+ ---
108
+
109
+ ## 4. SapdonButtonPanel 类
110
+
111
+ 构建按键网格面板(grid)。网格通过 `collection_name: form_buttons` 提供每项数据上下文,`place()` 把按钮按 `grid_position` 摆到指定格(内部即 pos_wrap:面板包裹 + `grid_position`)。
112
+
113
+ ```typescript
114
+ import { SapdonButtonPanel, SapdonButton } from '@sapdon/core'
115
+
116
+ const buttons = new SapdonButtonPanel("apple_buttons_panel")
117
+ .setDimensions([2, 1]) // [列, 行]
118
+ .setCollection("form_buttons") // 注入表单按钮集合
119
+ .setSize(["100%", "100%"])
120
+ .place([0, 0], new SapdonButton("bt0").setAnchor("bottom_left"))
121
+ .place([1, 0], new SapdonButton("bt1").setAnchor("bottom_right"))
122
+ .build()
123
+ ```
124
+
125
+ | 方法 | 说明 |
126
+ |------|------|
127
+ | `setDimensions([cols, rows])` | 网格尺寸(**列 × 行**) |
128
+ | `setCollection(name)` | 注入集合(表单场景固定 `form_buttons`) |
129
+ | `setSize(size)` | 网格尺寸 |
130
+ | `place([col, row], element)` | 摆一个按钮/控件到指定格(自动包一层 pos_wrap) |
131
+ | `build()` | 返回 `Grid` |
132
+
133
+ > **布局技巧**:`grid_dimensions [2,1]` 把面板切成左右两份;格子内控件默认左上对齐,改锚点 `bottom_left` / `bottom_right` / `top_right` 即可把按钮贴到对应角。
134
+
135
+ ---
136
+
137
+ ## 5. SapdonButton 类
138
+
139
+ 表单按钮的封装:默认模板固定为 `server_form.form_button`(吃 `form_buttons` 集合数据),无需手写模板引用。
140
+
141
+ ```typescript
142
+ import { SapdonButton } from '@sapdon/core'
143
+
144
+ new SapdonButton("bt0") // → bt0@server_form.form_button
145
+ .setAnchor("bottom_left") // 锚点对齐,默认 32×32
146
+ ```
147
+
148
+ | 方法 | 说明 |
149
+ |------|------|
150
+ | `setAnchor(anchor)` | 设置 `anchor_from`/`anchor_to` 对齐(如 `bottom_left`、`bottom_right`、`top_right`),默认尺寸 32×32 |
151
+
152
+ > 右上角的「退出」这类非集合按钮,请用普通 `Button("exit", "common.button")` + `$pressed_button_name: "button.menu_exit"` 手写,不占用表单按钮集合。
153
+
154
+ ---
155
+
156
+ ## 6. 运行时触发
157
+
158
+ ```typescript
159
+ import { world } from "@minecraft/server"
160
+ import { ActionFormData } from "@minecraft/server-ui"
161
+
162
+ world.afterEvents.itemUse.subscribe((event) => {
163
+ if (event.itemStack.typeId == "minecraft:apple") {
164
+ new ActionFormData()
165
+ .title("sapdon_ui:apple") // 前缀路由 → 自定义页
166
+ .body("...")
167
+ .button("test1").button("test2") // 喂给集合 form_buttons
168
+ .show(event.source)
169
+ }
170
+ })
171
+ ```
172
+
173
+ - title 含 `sapdon_ui:` → 自定义全屏 UI;否则原版原生表单。
174
+ - 自定义页内的按钮点击通过 `button.form_button_click` 返回 `response.selection`(对应集合下标)。
175
+
176
+ ---
177
+
178
+ ## 7. 已知注意点
179
+
180
+ 1. **`grid_position` 顺序**:本项目约定为 `[列, 行]`(与 `grid_dimensions [列, 行]` 一致)。
181
+ 2. **grid 内 `offset` 无效**:网格接管子控件定位;要偏移请包一层 panel(pos_wrap),offset 放内层。
182
+ 3. **`collection_index` 不是合法 UI 属性**:引擎会报 `Unknown property [collection_index]`。每个按钮对应哪个表单按钮,靠 grid 的集合上下文按放置顺序确定,不要用 `collection_index`。
183
+ 4. **壳固定渲染 content+buttons 两块**:纯内容页也必须提供一个(空的)按键面板,否则 `$user_buttons_panel` 引用缺失报错。
184
+ 5. **grid 数量绑定**:`#maximum_grid_items` 需 int(用 `#form_button_length`);collection_panel 才用数组 `#form_button_contents` → `#collection_length`。
185
+ 6. **裸引用**:页面壳里的 `content@$user_content_panel` / `buttons@$user_buttons_panel` 必须是裸引用(不带 `type`),否则网格类型被覆盖导致 `grid_dimensions`/`grid_position` 报未知属性。