合成与产出

下面这个九宫格是能点的:从材料盘拿东西放进 3×3,摆对配方产物格就会变绿。 它是纯演示工具 —— 材料无限、不消耗、也不写进任何背包,只是把游戏里的配方摆出来看。 匹配规则与游戏内 Items.matchGrid 完全一致 —— 无序匹配。

3×3 合成台(演示工具)

左:3×3 九宫格 · 中:产物格 · 下:材料盘。点格子里的物品可以取回,点产物格看结果 —— 怎么点都不会消耗东西。

九宫格是空的 —— 先从下面拿点材料吧。
演示工具 · 材料不消耗
这个工具只做试摆与匹配:不发请求、不扣材料、不写背包,刷新页面就回到空白。 游戏里真正的合成是另一套流程(九宫格暂存 → 点产物格发 CraftRequest → 服务端结算), 下面「一次合成在代码里发生什么」写的就是它。

配方

产物配方(无序)产出配方 id
木板 原木 ×1 ×4log_to_plank
火把 木板 ×1 + 煤炭 ×1 ×4plank_coal_to_torch
匹配是无序的:只看九宫格里物品的多重集必须与配方完全一致,摆哪个格子都算。 想做“有形状”的配方,在 Items.matchGrid 里按格子位置比对即可(配方表加一个 3×3 pattern 字段)。

产出链

从矿脉与树,到能拿在手里的东西。

煤矿石 → 煤炭

每区块 6 处脉,最常见的材料。

铁矿石 → 铁

每区块 4 处脉。

金矿石 → 金

每区块 2 处脉。

钻石矿石 → 钻石

每区块 1 处脉,最稀有。

原木 → 木板

1 个原木出 4 块木板,砍树就有。

木板 + 煤炭 → 火把

1 + 1 出 4 个火把。

一次合成在代码里发生什么

  1. 客户端点产物格 → ModNet.send(new CraftRequest(craftGrid)),把九个格子的物品名发过去(null 表示空)。
  2. 服务端判定配方Items.matchGrid(grid) 无序匹配;匹配不到就什么都不动,直接把背包重推一份。
  3. 服务端校验材料按配方逐项检查玩家背包够不够;不够就记一条日志并返回(不扣料)。
  4. 扣料 + 加产物逐项扣掉材料,加上产物(例如木板 ×4),累计合成计数 +1。
  5. 回推背包InventoryPacket(typeId 302)整份推给客户端,客户端在渲染线程更新背包 UI。

核心代码

// Items.java(两端同一份表,不会漂移)
public static String matchGrid(String[] grid) {
    Map<String,Integer> have = new LinkedHashMap<>();
    for (String s : grid) if (s != null && !s.isEmpty()) have.merge(s, 1, Integer::sum);
    if (have.isEmpty()) return null;
    for (String id : RECIPES) {                      // 无序:只比物品多重集
        Map<String,Integer> need = inputsOf(id);
        if (need.equals(have)) return id;
    }
    return null;
}

背包长什么样

游戏里按 I:背包 9 列 + 3×3 合成格 + 产物格,MC 风格的紧凑灰窗口。

背包区

  • 9 列槽位,物品图标右下角显示数量(×N,1 个时不显示)
  • 鼠标悬停高亮该格,页脚显示“物品名 ×数量”
  • 现在没有拖拽/分堆,点一下就是放 1 个进合成格

合成区

  • 3×3 九宫格 + 箭头 + 产物格,同游戏内排版
  • 产物格变绿 = 配方成立;点它才请求合成
  • 右侧小字显示产物数量