Forestry 集成

基本信息

属性 值
类型 4 个 NEI 配方页 + 搜索索引注入(Forestry addon)
@Mod 类 net.bdew.neiaddons.forestry.AddonForestry,modid NEIAddons|Forestry
目标 mod Forestry,版本要求 Forestry@[4.0.8.36,)
侧 双端 init(),但全部实际注册在 loadClient()(客户端)
事件 客户端 MinecraftForge.EVENT_BUS.register(this) 订阅 NEIRegisterHandlerInfosEvent
编译期依赖 implementation('com.github.GTNewHorizons:ForestryMC:4.11.31:dev')
开关 12 个,见 配置文件

AddonForestry 覆写了 preInit(打 @Mod.EventHandler)但没有 init 的 @EventHandler 注解——init(Side) 由主类在 FMLInitializationEvent 里手动调。

4 个配方处理器

全部在 loadClient() → BeeHelper.setup() / TreeHelper.setup() / ButterflyHelper.setup() 中按开关注册,且每个都成对调 API.registerRecipeHandler + API.registerUsageHandler。

Handler 开关 物种 root 配方 ID 图标栈
BeeBreedingHandler Show Bee Mutations rootBees beebreeding Forestry:beeQueenGE
BeeProduceHandler Show Bee Products rootBees beeproduce Forestry:beeQueenGE
TreeBreedingHandler Show Tree Mutations rootTrees treebreeding Forestry:saplingGE
TreeProduceHandler Show Tree Products rootTrees treeproduce Forestry:saplingGE
ButterflyBreedingHandler Show Butterfly Mutations rootButterflies butterflybreeding Forestry:butterflyGE

前四个是 Forestry 专属;ButterflyBreedingHandler 属 Lepidopterology(蝶类)子系统,与蜂/树同属 Forestry 但独立 root。

HandlerInfo 与隐藏按钮

三个 registerHandlerInfo 方法(BeeHelper / TreeHelper / ButterflyHelper 各一)通过 NEIRegisterHandlerInfosEvent.registerHandlerInfo 注册,构造方式是 new HandlerInfo.Builder(<HandlerClass>.class, Constants.MOD, Constants.ID),即借 Forestry 自己的 modid 与内部 ID注册自己的 handler 身份。

每个 HandlerInfo 都设了 .setShowOverlayButton(false).setShowFavoritesButton(false)——叠加层按钮与收藏按钮全部关闭,因为这些 handler 依赖 3D 基因组数据,塞进背包或做 2D 叠加都没有意义。

育种页(BaseBreedingRecipeHandler)

三个育种 handler 共用同一套基类。GUI 贴图统一 neiaddons:textures/gui/breeding.png(附带同名 .psd 源文件)。

每条配方由一个 IMutation 构造,三个带标签的槽位:

槽位 坐标 标签 RecipePosition
parrent1 (22, 19) mutation.getAllele0().getName() Parent1
parrent2 (75, 19) mutation.getAllele1().getName() Parent2
result (129, 19) mutation.getTemplate()[0].getName() Offspring

GeneticsUtils.RecipePosition 是 Parent1 / Parent2 / Offspring / Producer 四个枚举值,各自映射到 EnumBeeType 或 EnumGermlingType 的 ordinal:

位置 蜂 树
Parent1 PRINCESS SAPLING
Parent2 DRONE (静态块内定义)
Offspring QUEEN —
Producer QUEEN —

配方载入走 speciesRoot.getMutations(false)(不过滤 secret),随后由 if (!mutation.isSecret() || AddonForestry.showSecret) 决定是否收录——所以 Show Secret Mutations 是唯一的 secret 开关。

转移矩形两个:(49, 26, 15, 15) 父代区与 (98, 26, 21, 18) 结果区。

drawExtras 画三处标签 + 一处概率数字,位置固定 (108, 15),配色分支:

条件 文本 颜色
rec.derp 为真(getSpecialConditions 抛异常) DERP neiChanceTextRed
requirements 非空且 showReqs [<概率>] neiChanceTextRed
其它 <概率> neiChanceTextNormal

概率文本格式:chance < 1 时写死 "<1%",否则 String.format("%.0f%%", chance)。也就是说 0.5% 与 0.99% 显示完全一样。

handleTooltip 只在鼠标悬停于 (108-24, 15-2) 尺寸 48×12 的矩形内时追加:先是 rec.requirements 全部内容,再是 bdew.neiaddons.breeding.mutationchance(“Precise Mutation Chance”)+ rec.chance + "%"(这里显示的是原始浮点值,不是四舍五入后的文本)。条件非空、开关打开、tooltip 当前为空(currenttip.isEmpty())三者同时满足才触发。

产物页(BaseProduceRecipeHandler)

两个产物 handler 共用。GUI 贴图 neiaddons:textures/gui/products_dynamic.png(高度随产物数量动态变化,另有 .psd 源文件)。ROW_HEIGHT = 27。

布局:左侧一个生产者是唯一 ingredient,右侧 3 列网格分「Prod:」与「Spec:」两段。

元素 坐标
生产者 (22, 19),标签 species.getName()
Prod 行 x = 96 + 22×(i%3),y = 8 + (i/3)×27
Spec 行 x = 96 + 22×(i%3),y = 36 + ((i/3) + (numProductRows-1))×27
段标题 bdew.neiaddons.produce.prod (65, 12),色 neiProd
段标题 bdew.neiaddons.produce.spec (65, 40 + (numProductRows-1)×27),色 neiSpec

getRecipeHeight() 返回 (numProductRows + numSpecRows) * 27 + 10,所以这一页能无限增高。

产物来源两路:

来源 蜂 树
Prod IAlleleBeeSpecies.getProductChances() ITree.getProduceList()(由 root.getTemplate(uid) → templateAsIndividual 取出模板实例)
Spec IAlleleBeeSpecies.getSpecialtyChances() ITree.getSpecialtyList()

两者都先过 Utils.mergeStacks:同 Item + 同概率值 + 同 NBT 的合并成一个并把 stackSize 累加,NBT 用 Utils.isSameItem 逐字段比较。概率文本用 new DecimalFormat("0.#") 格式化 值×100 + "%"(所以 33.33% 显示 33.3%)。

蜜蜂专属:当 species 实现 IAlleleBeeSpeciesCustom 且 getJubilanceProvider() 非 null 时,provider.getDescription() 被写成灰色 tooltip 挂在每一个 specialty 项上。树没有对应逻辑。

isNoOutput()(prod + spec 都空)时该 species 不生成任何配方条目。getOtherStacks() 返回除首个产物外的其余项,getResult() 恒为 products.get(0)。

搜索索引注入

三个 helper 各有一轮遍历 species 调 Utils.safeAddNBTItem(内部是 API.addItemListEntry),并对 null 做静默跳过:

注入物 开关 来源类
蜂种(QUEEN / DRONE / PRINCESS 三形态) Add Bees to Search BeeHelper
蝴蝶种(BUTTERFLY / CATERPILLAR / SERUM 三形态) Add Bees to Search ButterflyHelper(复用了蜂的开关)
树苗 SAPLING Add Saplings to Search TreeHelper
花粉 POLLEN Add Pollen to Search TreeHelper
蜂蜡子物品的全部 metadata 变体 Add Combs to Search BeeHelper

蝴蝶挂在 addBees 开关下是源码原样(ButterflyHelper 里写的判断条件是 AddonForestry.addBees),名字与作用不符。

蜂蜡子物品的 mod 探测

BeeHelper.getMobCombs() 用 Loader.isModLoaded 依次探测三个来源,全部通过反射取物品:

modid 取法 失败处理
Forestry GameRegistry.findItem("Forestry", "beeCombs") 为 null 时打 "Failed to get forestry bee comb item, something is messed up"
ExtraBees Class.forName("binnie.extrabees.ExtraBees").getField("comb") catch (Throwable) → "Error locating Extra Bees comb item";字段不是 Item → "Extra Bees comb is not Item subclass!"
MagicBees Class.forName("magicbees.main.Config").getField("combs") 同上

这三个是通过反射探测的第三方集成,不需要对应的 @Mod 容器、也不需要 getDependencies() 声明——getDependencies() 只有 Forestry@[4.0.8.36,) 一条。

Add Combs to Search 打开时,会把每个 comb 物品用 getSubItems(item, null, combs) 展开出的全部子物品逐个 API.addItemListEntry,并逐条打 "Registering comb variant for %s: %s"。

与 NEIPlugins 的 IMC 握手

AddonForestry.registerWithNEIPlugins(name, id) 每次注册 handler 后调一次:

FMLInterModComms.sendRuntimeMessage(
        this, "NEIPlugins", "register-crafting-handler",
        String.format("%s@%s@%s", I18n.format("bdew.neiaddons.genetics"), name, id));

频道 NEIPlugins,键 register-crafting-handler,值形如 Forestry Genetics@Bee Breeding@beebreeding。I18n.format 意味着发出去的已经是翻译后的本地化字符串,非英文环境下传给 NEIPlugins 的名字也会跟着变。

AddonBotany 有同名同格式的方法(见 Botany 集成)。

相关条目