变异树布局与渲染
基本信息
| 属性 | 值 |
|---|---|
| 类 | hellfirepvp.beebetteratbees.client.gui.CachedBeeMutationTree |
| 基类 | CachedRecipe(供 NEI 读取结果 / 原料堆栈) |
| 树结构 | SimpleBinaryTree<IAllele>,构造深度上限 4 |
| 渲染迭代深度 | iterationDepth = 3 |
| 蜜蜂类型常量 | BEE_TYPE_PRINCESS = 1、BEE_TYPE_DRONE = 0 |
从 mutation 到树
构造器接收一个 IBeeMutation,以其模板主位(getTemplate()[0],即结果物种)
为根,用 SimpleBinaryTree.RootProvider 递归向下展开(:153-169):
- 展开规则是"取第一个能以该物种为结果的 mutation",用它的
getAllele0()/getAllele1()作为左右子节点;没有可用 mutation 时返回null,SimpleBinaryTree据此把节点降级为Leaf。 SimpleBinaryTree的buildSubTree要么同时建左右子节点,要么建成Leaf,不存在单边节点。showDuplicateTrees = false时,removeAndReplaceDuplicates()先把已出现过的物种 从树上摘掉(:171-174、:307-322),命中且该物种确实还有 mutation 时才摘。
坐标与层高
| 常量 | 值 | 含义 |
|---|---|---|
MIN_X / MAX_X |
15 / 135 |
横向渲染区间,跨度 120 |
Y_OFFSET |
0 |
根节点纵坐标 |
X_SEPERATION_THRESHOLD |
7 |
过窄判定阈值 |
OFFSET_CORRECTION |
8 |
图标中心修正 |
POSSIBLE_CHILD_OFFSET |
16 |
叶子节点"可能有子代"分叉的纵向长度 |
层高在构造器里算出(:178-187):
int iterationDepth = 3;
int maxTotalDepth = Math.min(iterationDepth, mutationTree.getRoot().getMaxFollowingDepth());
int yStep = 110 / maxTotalDepth;
maxTotalDepth <= 0(根是叶子)时构造器直接 return,此时只有根、不渲染任何节点——
所以"查不到下级"的表现是完全空白而不是报错。
yStep 是整数除法,除不尽时向下取整(110 / 3 = 36)。
横向按二分中点递归:父节点在 [minX, maxX] 的中点,左子树占 [minX, center],
右子树占 [center, maxX]。
蜜蜂类型:根是公主蜂,其余全是雄蜂
根节点用 BEE_TYPE_PRINCESS(:227),而 placeInRenderBuffer 里所有
下级节点——无论叶子还是内部节点——都用 BEE_TYPE_DRONE(:348、:374)。
这是因为父代在 Forestry 的杂交公式里本来就该是雄蜂;结果个体(根)才需要公主蜂。
概率标签
每个节点的 baseChance 来自"第一个能产出该物种的 mutation"的 getBaseChance();
没有这种 mutation 时为哨兵值 -1。标签只在 baseChance > 0 时生成(:251、:281),
所以 -1 与 0 都不会显示文字。
显示规则(ChanceInfoNode 构造器,:107-111):
| 条件 | 显示文本 |
|---|---|
chance < 1 |
加粗的 <1% |
chance >= 1 |
加粗的 ((int) chance) + % |
chance 是 float,(int) 直接截断,1.9 会显示成 1%。
悬浮命中判定是 x ∈ [x, x+16] 且 y ∈ [y+1, y+6](:115-117),
且只在 currenttip.isEmpty() 时才返回(见
NEI 配方树处理器),即自己会吞掉原版 tooltip。
连线与配色
颜色取自 ColorUtils(GTNHLib ColorResource,资源域 beebetteratbees),
全部只有两档:
| 用途 | 颜色 | 触发条件 |
|---|---|---|
| 连线 / 标签 默认 | 0x000000 黑 |
该节点 requirements 为空或 null |
| 连线 / 标签 警示 | 0xA9000A 红 |
requirements 非空(该变异带特殊条件) |
连线以 GL_LINE_STRIP 绘制,glLineWidth(3.0F)、开启 GL_LINE_SMOOTH、
alpha 固定 127(LineNode#renderNode,:62-83),绘制完把线宽复位为 2.0F。
源码缺陷
oversized 字段算了但从不读。 checkSeparationWidth()(:324-328,
判据 120 / 2^(depth+1) < 7)的结果写进 public final boolean oversized
(:184、:189),但全仓库对该字段只有这两处赋值、零处读取——
grep -rn "oversized" 只命中声明和这两行。也就是说"树太宽挤不下"的判定
被完整计算后直接丢弃,不会触发任何降级或提示。
每层只画一条变异路径。 展开时固定取 mutations.get(0)(:162、:216、:364),
同一物种若有多个不同父代组合的 mutation,配方树只会展示注册顺序里的第一个。
这不是渲染 bug,是设计取舍,但会让人以为某条杂交路径不存在。
注释声明的画布高度与实际面板不符。 文件头注释写
Y = 10 to 100 (Size: 90)(:130),但 drawBackground 只画
166 x 65 的 neiBlank.png(AbstractTreeGUIHandler.java:132),
而 yStep = 110 / maxTotalDepth 在 maxTotalDepth = 1 时是 110,远超 65px 面板。
相关条目
- NEI 配方树处理器 - mutation 的收集与 secret 过滤
- 配置项 -
showDuplicateTrees控制上面的去重步骤