成就
基本信息
| 属性 | 值 |
|---|---|
| 成就条目数 | 24 |
| 成就页数 | 1(Blood Magic) |
| 注册方法 | ModAchievements.init()(common/achievements/ModAchievements.java:40-73) |
| 调用点 | AlchemicalWizardry.preInit → ModAchievements.init()(AlchemicalWizardry.java:692) |
| 成就类 | common/achievements/AchievementsMod.java(继承原版 Achievement) |
| 成就页类 | net.minecraftforge.common.AchievementPage(Forge 提供) |
| 语言键前缀 | achievement.alchemicalwizardry:(统计名)/ alchemicalwizardry:(展示名) |
| 道具触发 | AchievementsRegistry.getAchievementForItem(Item) / getAchievementForBlock(Block) |
| 事件触发 | AchievementTrigger(注册于 :72) |
粒度选择
24 条成就在字段结构上完全同构(id / 网格 x,y / 图标 / 父成就),共用同一个类与同一个注册方法,彼此无独立行为代码。按惯例合并为一页表格,不拆成 24 个文件。
AchievementsMod 的构造函数把 this 追加进静态列表 AchievementsMod.achievements 并调用 registerStat(),因此每 new 一个就自动完成一次注册 —— 不存在「构造了但没注册」的可能。
成就表
「父」列为源码中传入的父成就字段;— 表示该字段是 null,即根成就。
| 网格 x,y | id | 图标 | 父 | 特殊 |
|---|---|---|---|---|
| 0, 0 | firstPrick |
ModItems.sacrificialDagger |
—(根) | ✅ setSpecial() |
| 3, 0 | weakOrb |
ModItems.weakBloodOrb |
firstPrick |
|
| 3, 2 | bloodLettersPack |
ModItems.itemBloodPack |
weakOrb |
|
| 6, 2 | waterSigil |
ModItems.waterSigil |
weakOrb |
|
| 4, -2 | blankRunes |
ModBlocks.bloodRune |
weakOrb |
|
| 4, -4 | apprenticeOrb |
ModItems.apprenticeBloodOrb |
blankRunes |
|
| 6, 1 | airSigil |
ModItems.airSigil |
apprenticeOrb |
|
| 4, -5 | daggerSacrifice |
ModItems.daggerOfSacrifice |
apprenticeOrb |
|
| 6, -3 | brewingPotions |
ModBlocks.blockWritingTable |
apprenticeOrb |
|
| 2, -2 | magicianOrb |
ModItems.magicianBloodOrb |
apprenticeOrb |
|
| 6, 0 | sigilHolding |
ModItems.sigilOfHolding |
magicianOrb |
|
| 0, -2 | boundBlade |
ModItems.energySword |
magicianOrb |
|
| 1, -1 | boundArmor |
ModItems.boundPlate |
magicianOrb |
|
| 1, -4 | complexSpells |
ModItems.itemComplexSpellCrystal |
magicianOrb |
|
| 1, -3 | ritualDiviner |
ModItems.itemRitualDiviner |
magicianOrb |
|
| -2, -1 | masterOrb |
ModItems.masterBloodOrb |
boundBlade |
|
| -3, -2 | demonSpawn |
ModItems.demonPlacer |
masterOrb |
|
| 6, -1 | phantomBridgeSigil |
ModItems.sigilOfTheBridge |
masterOrb |
|
| -4, -1 | teleposer |
ModBlocks.blockTeleposer |
masterOrb |
|
| 6, -2 | suppressionSigil |
ModItems.itemSigilOfSupression |
masterOrb |
|
| -1, 2 | archmageOrb |
ModItems.archmageBloodOrb |
masterOrb |
|
| -3, 2 | energyBazooka |
ModItems.energyBazooka |
archmageOrb |
|
| 0, 3 | demons |
ModItems.baseItems meta 29 |
archmageOrb |
✅ setSpecial() |
| 0, 5 | transcendentOrb |
ModItems.transcendentBloodOrb |
demons |
⚠️ 字段名与 id 不一致(源码既有):daggerSacrifice 成就的字段叫 daggerOfSacrifice(ModAchievements.java:22),而传入的 id 字符串是 "daggerSacrifice"(:48)。语言键因此是 achievement.alchemicalwizardry:daggerSacrifice,不是字段名。AchievementsRegistry(:72)按字段 ModItems.daggerOfSacrifice 查找,两侧指向同一个物品,不影响功能。
成就页
alchemicalWizardryPage = new AchievementPage(
"Blood Magic",
AchievementsMod.achievements.toArray(new Achievement[AchievementsMod.achievements.size()]));
AchievementPage.registerAchievementPage(alchemicalWizardryPage);
AchievementPage 的列表不是硬编码的 24 个字段,而是把静态列表 AchievementsMod.achievements 整体转成数组 —— 该列表在 AchievementsMod 构造函数里累加,因此页内条目数与 new AchievementsMod( 的调用数恒等(24)。页面标题 "Blood Magic" 与成就页内部键由 Forge 自行管理。
触发机制
成就全部通过 AchievementTrigger(common/achievements/AchievementTrigger.java,注册于 ModAchievements.java:72)的两个 Forge 事件触发,没有第三条路径:
| 事件 | 遍历列表 | 判定函数 | 列表长度 |
|---|---|---|---|
PlayerEvent.ItemPickupEvent |
AchievementsRegistry.pickupList |
getAchievementForItem(Item) |
12 |
PlayerEvent.ItemCraftedEvent |
AchievementsRegistry.craftinglist |
getAchievementForItem(Item) |
9 |
getAchievementForItem(AchievementsRegistry.java:65-88)是一串 if (item == ModItems.x) return ModAchievements.y;,19 个分支,含两条类型判定而非等值判定:
| 判定 | 成就 | 依据 |
|---|---|---|
item instanceof BoundArmour |
boundArmor |
AchievementsRegistry.java:76 |
item instanceof ItemRitualDiviner |
ritualDiviner |
AchievementsRegistry.java:78 |
方块成就走 getAchievementForBlock(Block)(:89-95,3 个分支:bloodRune、blockWritingTable、blockTeleposer),由 onItemCrafted 里的 event.crafting.getItem() instanceof ItemBlock 分支调用,把 ItemBlock.field_150939_a 反查回方块。
⚠️ 2 条成就不可达
pickupList(12)与 craftinglist(9)均不含 ModItems.demonPlacer 与 ModItems.baseItems,因此以下 2 条成就在正常游戏流程中无法解锁:
| id | 图标 | 说明 |
|---|---|---|
demonSpawn |
ModItems.demonPlacer |
图标物品从未被加入任何触发列表 |
demons |
ModItems.baseItems meta 29 |
同上;且它是 setSpecial() 成就 |
即 24 条成就中 22 条可达、2 条不可达。两条的字段、统计名与父子关系都已正确注册并显示在成就页上,只是缺少触发入口 —— 属于未完成而非注册错误。
⚠️ blockCraftingList 是死列表
AchievementsRegistry 用 addBlocksToCraftingList()(:41-45)向 blockCraftingList 填了 3 个方块,但全仓无任何读取点 —— 该标识符只在声明处与 3 次 .add() 出现。
方块成就之所以仍能触发,靠的是 onItemCrafted 中与 craftinglist 循环并列的 instanceof ItemBlock 分支,它按实际合成物的方块反查,与 blockCraftingList 无关。该列表是重构残留:早期版本大概是靠遍历它判定方块成就的。
数值
| 数值名 | 值 |
|---|---|
| 成就条目数 | 24 |
| 成就页数 | 1 |
根成就数(父为 null) |
1(firstPrick) |
标记 setSpecial() 的成就 |
2(firstPrick、demons) |
| 以物品为图标的成就 | 21 |
| 以方块为图标的成就 | 3(blankRunes bloodRune、brewingPotions blockWritingTable、teleposer blockTeleposer) |
以 ItemStack(带 meta)为图标的成就 |
1(demons = ModItems.baseItems meta 29) |
| 网格 x 范围 | -4 – 6 |
| 网格 y 范围 | -5 – 5 |
| 最深成就链 | firstPrick → weakOrb → blankRunes → apprenticeOrb → magicianOrb → boundBlade → masterOrb → archmageOrb → demons → transcendentOrb(10 层) |
| 叶子成就(无子成就) | 15 |
| 成就树形态 | 单根树(仅 firstPrick 无父),24 条全部挂在这一棵树下 |
| 由道具判定解锁的条目 | 22(19 个道具分支 + 3 个方块分支,减去未进入触发列表的 2 条) |
| 不可达的成就 | 2(demonSpawn、demons) |
死列表 blockCraftingList |
1(填 3 项,无读取点) |