注意:本文仅给出合成表模块中一些比较重要的地方的使用方法,没有给出的地方自行查看代码注释,如果有不清楚的地方或者有新的想法、建议也非常欢迎在评论区提出(关于模块本身的想法可以发在仓库的 issues)。

  仓库地址:github,欢迎各位小伙伴来点 star ( ̄▽ ̄)"

  如果没有特别指出,下文中提到的所有“合成表”均指代我设计的合成表模块而非 MC 原版的合成表。

  同时如果没有给出完整类名,则类一定为 JDK/Kotlin 标准库、MC 自带的第三方库或top.kmar.mi.api.craft包下的类。

输入和输出

  合成表使用ElementList表示合成表的输入,最多支持二维输入。对象中的值一般直接从用户输入映射,不进行元素合并。

  由于ElementList一般仅用于模块内部使用,这里不再介绍具体内容,有兴趣的小伙伴可以查看源码

  合成表用CraftOutput表示合成表的输出,合成表输出仅支持一维数据。

  与原版自带的合成表不同的是,CraftOutput可以包含多个物品,同时能够包含非物品数据(StringIntBoolean)。

  CraftOutput内部代码很简单,这里不再说明,具体见代码注释

物品序列

  在合成表模块中,使用IShape表示一个不可变的(有序或无序的)物品列表。模块内置了两个实现:OrderlyShapeDisorderlyShape,前者为有序列表,后者为无序列表。

  IShape用来充当合成表的 key,CraftOutput为 value。

  IShape的结构也很简单,并且一般仅模块内部使用,这里不再详细介绍,有兴趣的小伙伴可以查看源码

JSON

  合成表模块支持使用 JSON 文件注册合成表,格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"group": "synthesizer",
"id": "craftId",
"output": "*",
"input": [
"#%%%#",
"#^^^#",
"=---=",
"#^^^#",
"#%%%#"
],
"key": {
"#": { "item": "mi:machine_shell" },
"^": { "item": "mi:blade" },
"=": { "item": "mi:electromagnet" },
"-": { "item": "mi:iron_stick" },
"%": { "item": "mi:circuit" },
"*": { "item": "mi:pulverizer" }
}
}

  其中各个项目的用途如下:

  • group - 合成表的分组(具体介绍见下文)。
  • id - 合成表的 ID(具体介绍见下文)。
  • output - 合成表的输出
    该项目允许两种值:
    • 字符串:表明输出的内容是一个纯物品序列。
    • 对象:对象中各个 key 的效果如下
      • stacks:要输出的物品序列
        值应当为字符串、对象或对象数组,字符串表明一个物品序列,对象的格式与MC 原版的格式相同。
      • 其它任意字符串:存储非物品数据
        值应当为布尔类型、整型或字符串,可以在CraftOutput中使用相应的get函数获取该值。
  • input - 合成表的输入
    该项目允许三种值:
    • 字符串:表明该合成表是一个无序合成表,字符串的内容是一个物品序列
    • 对象数组:表明该合成表是一个无序合成表,对象的格式与MC 原版的格式相同。
    • 字符串数组:表明该合成表是一个有序合成表,每个字符串的长度应当相同。
  • key - 键值表,与MC 原版中的 ingredients 格式相同

  合成表模组会根据input的内容推断是有序合成表还是无序合成表,使用字符串表示物品列表时强制使用空格表示空(当然你也可以手动定义一个新的 key 来表示空,但是不建议这么做)。

分组

  合成表是基于分组运行的,通过分组将不同的合成模块分割开。简单说就是假如有两个方块——压缩机和粉碎机,这两个方块肯定是不能共享合成表的。通过分组可以让两者的合成表完全独立,互不影响。

  分组的依据是用户传入的字符串类型的 KEY,为了避免冲突,建议在 KEY 的开头添加模组 ID 一类的唯一性标识。

  CraftGuide类就是用来处理分组信息的,可以通过其中提供的一系列函数进行查找操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
object CraftGuide {

/**
* 注册一个合成表
* @param group 合成表分组名称
* @param id 合成表 ID,同一个分组内 ID 不可重复
* @param shape 合成表输入
* @param output 合成表输出
* @throws IllegalArgumentException 如果 ID 重复
*/

@JvmStatic
fun registry(group: String, id: String, shape: IShape, output: CraftOutput)

/** 获取一个注册机 */
@JvmStatic
fun getRegedit(group: String): CraftGuide?

/** 获取指定合成表的输出 */
@JvmStatic
fun findOutput(group: String, id: ResourceLocation): CraftOutput?

/** 通过输入获取合成表输出 */
@JvmStatic
fun findOutput(group: String, input: ElementList): CraftOutput?

/** 通过输出查询无序合成表的输出 */
@JvmStatic
fun findDisorderlyOutput(group: String, input: ElementList): CraftOutput?

/** 判断是否存在与指定输入相匹配的合成表 */
@JvmStatic
fun has(group: String, input: ElementList): Boolean

}

注册机

  注册机是用来管理某一个分组内的合成表的,合成表内的每一个项都对应一个唯一的 ID,在一个 ID 被注册后,可以通过这个 ID 修改其对应的数据。

  通过 JSON 注册的合成表都会被分配一个默认的 ID,也可以在 JSON 内手动指定 ID,有关这方面的内容我们下文会详细说明。

  接着我们来介绍注册机(CraftRegedit)中一些比较重要的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class CraftRegedit {

/**
* 注册一个合成表
* @param id 合成表的键,用于后续合成表管理
* @param shape 合成规则
* @param output 产物
*/

fun registry(id: String, shape: IShape, output: CraftOutput)

/** 移除指定合成表 */
fun delete(id: String)

/** 删除满足指定条件的合成表 */
fun deleteIf(predicate: (String, LazyNode) -> Boolean)

/** 通过 ID 查找合成表输出,未查询到返回`null` */
fun findOutput(id: String)

/** 通过输入查询合成表输出,未查询到返回`null` */
fun findOutput(input: ElementList): CraftOutput?

/** 通过输入查询有序合成表的输出,未查询到返回`null` */
fun findOrderlyOutput(input: ElementList): CraftOutput?

/** 通过输入查询无序合成表的输出,未查询到返回`null` */
fun findDisorderlyOutput(input: ElementList): CraftOutput?

}

  可以看到注册机中提供了好几个find函数,我们说明一下这些函数的区别。

  前两个findOutput一个是根据项的 ID 来查找,另一个是根据输入的物品序列来查找。后面两个则是仅查找有序合成表或无序合成表。

  为什么要这样子分开呢?因为分组一样的合成表不论是有序还是无序都存储在同一个注册机(不同的Map)中,findOutput会同时查找有序和无序的合成表,优先查找有序合成表。

  如果一个注册机中有多个与输入相匹配的合成表,则仅会返回第一个,如果输入序列无法查找到相匹配的项,则返回null