成就系统
基本信息
| 属性 | 值 | 来源 |
|---|---|---|
| 注册类 | ModAchievements |
common/achievement/ModAchievements.java |
| 触发类 | AchievementMod extends Achievement |
common/achievement/AchievementMod.java:22 |
| 事件类 | AchievementTriggerer |
common/achievement/AchievementTriggerer.java |
| 成就总数 | 49 | 49 个 public static Achievement 字段、49 次 new AchievementMod、49 个 LibAchievementNames 常量,三者吻合 |
| 隐藏成就 | 12(.setSpecial()) |
ModAchievements.init() |
| 成就页 | AchievementPage(FML 原版机制) |
ModAchievements.java:156-158 |
| 侧 | 服务端 | ModAchievements.init() 由 CommonProxy.java:117 调用(客户端另见 ClientProxy) |
成就页的显示名用 LibMisc.MOD_NAME,pageIndex 记录该页在全局成就页列表中的下标(ModAchievements.java:156)。
注册机制
每个成就走 AchievementMod 的三参重载(AchievementMod.java:26-33):
super("achievement.botania:" + name, "botania:" + name, x, y, icon, parent);
achievements.add(this); // 加入静态列表
registerStat(); // Statistic.registerStat
因此产生两套本地化键,共 98 条 zh_CN 键(49 × 2):
| 键 | 含义 | 示例 |
|---|---|---|
achievement.botania:<name> |
成就标题 | achievement.botania:flowerPickup=Sky of Beginning |
achievement.botania:<name>.desc |
成就说明 | achievement.botania:flowerPickup.desc=拾取一朵神秘花 |
传给 Achievement 构造器的第二个参数(未本地化名)少了 achievement. 前缀,为 botania:<name>。achievements 列表在构造时累积,最后统一 new Achievement[achievements.size()] 传给 AchievementPage(ModAchievements.java:157)。
位置坐标 (x, y) 决定成就页上的格子位置,负数为左侧独立的分支(遗物 6 件 + 3 个彩蛋全在 x < 0 区域)。
触发方式
AchievementTriggerer 只监听 2 个原版事件(AchievementTriggerer.java:22-34):
| 事件 | 判定接口 | 行为 |
|---|---|---|
ItemPickupEvent |
拾取物 instanceof IPickupAchievement |
调 getAchievementOnPickup(stack, player, entity),非空则 player.addStat(achievement, 1) |
ItemCraftedEvent |
合成物 instanceof ICraftAchievement |
调 getAchievementOnCraft(crafting, player, matrix),非空则 addStat |
也就是说成就不靠监听方块放置 / 实体击杀 / 地块交互,而是靠物品自身实现 IPickupAchievement / ICraftAchievement(通常经 ItemLexicon、各 Item 子类)后借上述两个事件顺带解锁。遗物另有 IRelic.setBindAchievement 绑定(AchievementMod.java:31-32)。
AchievementTriggerer 自身也在 init 末尾注册到 FMLCommonHandler 总线(ModAchievements.java:160)。
全部 49 个成就
「隐藏」列为 是 表示调用了 .setSpecial(),在成就页上以问号显示直到解锁。
| 字段 | 名称 | 图标 | 坐标 | 父成就 | 隐藏 | 说明 |
|---|---|---|---|---|---|---|
flowerPickup |
Sky of Beginning | ModBlocks.flower meta 6 |
0, 4 |
无 | 拾取一朵神秘花 | |
lexiconUse |
现実的论理主义者 | ModItems.lexicon |
1, 5 |
flowerPickup |
打开一本植物魔法辞典并浏览目录 | |
daybloomPickup |
拂晓抵达 | subtile_daybloom |
3, 5 |
lexiconUse |
在花药台制作一朵太阳花 | |
cacophoniumCraft |
Noisy Lover Soul | ModItems.cacophonium |
-1, 2 |
flowerPickup |
制作一个不和谐之音来激怒你的敌人 | |
manaPoolPickup |
Vivid Wave | ModBlocks.pool |
3, 2 |
daybloomPickup |
将稀释魔力池升级为普通的魔力池 | |
endoflamePickup |
炉心融解 | subtile_endoflame |
2, 0 |
manaPoolPickup |
做出一朵火红莲来加速魔力生产 | |
tinyPotatoPet |
Just Be Friends | ModBlocks.tinyPotato |
2, -2 |
manaPoolPickup |
做出一个小土豆再爱抚它 | |
sparkCraft |
Live Drive | ModItems.spark |
4, -2 |
manaPoolPickup |
制作火花协助传输魔力 | |
baubleWear |
Inner Arts | ModItems.manaRing |
4, 0 |
manaPoolPickup |
做出一件饰品并佩戴 | |
manaCookieEat |
腐れ外道とチョコレゐト | ModItems.manaCookie |
2, -4 |
manaPoolPickup |
注魔一块魔力曲奇并吃掉 | |
manaweaveArmorCraft |
Imagination Forest | ModItems.manaweaveChest |
4, -4 |
manaPoolPickup |
制作一套魔力布匹制作的装甲来提高你的魔力效率 | |
craftingHaloCraft |
Segments Blue | ModItems.craftingHalo |
3, -6 |
manaPoolPickup |
制作一个合成环以方便合成 | |
manaCartCraft |
World Domination How-To | ModItems.poolMinecart |
5, 3 |
manaPoolPickup |
制作一个用魔力矿车进行的长距离魔力运输系统 | |
enchanterMake |
Odds and Ends | ModBlocks.enchanter |
1, 2 |
manaPoolPickup |
制作一个魔力附魔台来更好的控制附魔 | |
runePickup |
Lilium Doll | ModBlocks.runeAltar |
6, 2 |
manaPoolPickup |
用符文祭坛制作符文 | |
dirtRodCraft |
Rockbell | ModItems.dirtRod |
8, 3 |
runePickup |
制作一根泥棍子…哦不,是大地法杖 | |
terraformRodCraft |
World is Mine | ModItems.terraformRod |
10, 3 |
dirtRodCraft |
制作一把泰拉高并用他改变世界地形 | |
manaBlasterShoot |
M‘AIDER遭難ガール | ModItems.manaGun |
8, 1 |
runePickup |
合成魔力脉冲枪再发射脉冲 | |
pollidisiacPickup |
Romeo and Cinderella | subtile_pollidisiac |
8, 5 |
runePickup |
做一个畜牧花,在空气中制造爱情力场 | |
brewPickup |
Childhood Blues | ModBlocks.brewery |
6, 0 |
runePickup |
制作一个植物酿造台并用他酿造所有种类的药水 | |
terrasteelPickup |
Dancer in the Dark | ModItems.manaResource meta 4 |
6, 9 |
runePickup |
是 | 用泰拉凝聚板制作一块泰拉锭 |
terrasteelWeaponCraft |
Remote Paranoia | ModItems.terraSword |
8, 10 |
terrasteelPickup |
用泰拉锭制作的武器武装你自己 | |
elfPortalOpen |
World Calling | ModBlocks.alfPortal |
4, 9 |
terrasteelPickup |
是 | 打开通往精灵王国的传送门 |
kekimurusPickup |
放課後ストライド | subtile_kekimurus |
3, 11 |
elfPortalOpen |
制作一朵贪食花来提供大量魔力 | |
heiseiDreamPickup |
Heisei Cataclysm | subtile_heisei_dream |
5, 11 |
elfPortalOpen |
制作一朵平成之梦让怪物们窝里斗 | |
bubbellPickup |
深海少女 | subtile_bubbell |
6, 12 |
elfPortalOpen |
制作一株气泡铃来方便水下建筑 | |
enderAirMake |
Imitation Air | ModItems.manaResource meta 15 |
4, 14 |
elfPortalOpen |
收集一瓶末地空气 | |
corporeaCraft |
Drop Pop Candy | ModBlocks.corporeaFunnel |
2, 14 |
enderAirMake |
合成一个使用多媒体网络的方块 | |
luminizerRide |
World’s End Dancehall | ModBlocks.lightRelay |
6, 14 |
enderAirMake |
制作一些微光并用它来进行传输 | |
gaiaGuardianKill |
Dead and Seek | ModItems.manaResource meta 5 |
2, 9 |
elfPortalOpen |
是 | 在盖亚仪式中召唤并击杀一只盖亚守护者 |
spawnerMoverUse |
I can take off my (redacted) | ModItems.spawnerMover |
-1, 10 |
gaiaGuardianKill |
用一个生命聚合器来转移刷怪笼 | |
tiaraWings |
Heart Shooter | ModItems.flightTiara |
-1, 8 |
gaiaGuardianKill |
制作天翼族之冠并用翅膀装饰它 | |
manaBombIgnite |
Under of Fragments | ModBlocks.manaBomb |
0, 11 |
gaiaGuardianKill |
制作一个魔力风暴,并在宽阔的地方点燃它 | |
dandelifeonPickup |
Donut Hole | subtile_dandelifeon |
0, 7 |
gaiaGuardianKill |
制作一朵启命英来挑战你的心智 | |
signalFlareStun |
See the Lights | ModItems.signalFlare |
-3, 1 |
无 | 是 | 用一发信号弹让至少 100 只怪物眩晕 |
l20ShardUse |
Out of Step | ModItems.laputaShard |
-5, 3 |
无 | 是 | 用能量为 XX 的拉普达碎片将地面升至天空 |
gaiaGuardianNoArmor |
Mythologia’s End | Items.skull meta 3 |
-5, 1 |
无 | 是 | 战斗从头至尾不穿任何护甲击杀一只盖亚守护者 |
rankSSPick |
六兆年零一夜物语 | ModItems.terraPick |
-3, 3 |
无 | 是 | 用 SS 级的泰拉粉碎者挖穿世界 |
superCorporeaRequest |
Invader Girl | ModBlocks.corporeaIndex |
-3, -1 |
无 | 是 | 成功从多媒体索引处请求单种物品至少 50,000 个 |
pinkinator |
Decorator | ModItems.pinkinator |
-5, -1 |
无 | 是 | 使用粉色手炮 |
relicInfiniteFruit |
Innocentia | ModItems.infiniteFruit |
-9, 8 |
无 | 获得战利品:禁忌果实 | |
relicKingKey |
A Fake, Fake Psychotropic | ModItems.kingKey |
-7, 11 |
无 | 获得战利品:王之宝钥 | |
relicFlugelEye |
Angel of Death | ModItems.flugelEye |
-5, 8 |
无 | 获得战利品:天翼族之眼 | |
relicThorRing |
One Step Layered | ModItems.thorRing |
-7, 7 |
无 | 获得战利品:托尔之戒 | |
relicOdinRing |
回忆补时 | ModItems.odinRing |
-9, 10 |
无 | 获得战利品:奥丁之戒 | |
relicLokiRing |
Arithmetic | ModItems.lokiRing |
-5, 10 |
无 | 获得战利品:洛基之戒 | |
relicAesirRing |
神のまにまに | ModItems.aesirRing |
-7, 9 |
无 | 是 | 通过合成做出诸神之戒 |
nullFlower |
Take it Easy | ModBlocks.specialFlower |
-8, 0 |
无 | 是 | 发现一个 bug 导致你有了一朵未定义的植物魔法花。别忘了提交! |
desuGun |
裏表ラバーズ | ModItems.manaGun(显示名被改为 “desu gun”) |
-8, 2 |
无 | 是 | ASADA-SAN × 9 |
三个分组
按坐标与依赖关系,成就页自然分成三块:
| 组 | 成就 | 特征 |
|---|---|---|
| 主线 | flowerPickup → lexiconUse → daybloomPickup → manaPoolPickup → endoflamePickup / runePickup → 精灵分支 |
沿 x = 0 → 10 展开,是推荐的游玩顺序 |
| 遗物 6 件 | relicInfiniteFruit / relicKingKey / relicFlugelEye / relicThorRing / relicOdinRing / relicLokiRing(+ 合成的 relicAesirRing) |
全部 parent = null,且全部受 ConfigHandler.relicsEnabled 门控 |
| 彩蛋 6 个 | signalFlareStun / l20ShardUse / gaiaGuardianNoArmor / rankSSPick / superCorporeaRequest / pinkinator / nullFlower / desuGun |
全部 parent = null 且全部 .setSpecial(),散落在 x = −3 ~ −8 的空白区 |
遗物 6 件 + 诸神之戒 = 7 个,全部包在 if(ConfigHandler.relicsEnabled) 判定内(ModAchievements.java:140-148),所以 relics.enabled 关闭时实际只有 42 个成就注册。
已确认的细节与坑
42 + 7 的条件注册:关掉 relics.enabled 后 relic* 字段保持 null。但 AchievementPage 用的是 achievements 列表而非字段,列表同样不含这 7 个,所以不会出现 null 图标崩溃 —— 这一点由 achievements.add(this) 只在构造时调用保证。
desuGun 的图标被改过显示名:ItemStack desu = new ItemStack(ModItems.manaGun); desu.setStackDisplayName("desu gun");(ModAchievements.java:152-154)。这是 49 个成就里唯一一个自定义显示名的图标。
gaiaGuardianNoArmor 用原版头颅当图标:new ItemStack(Items.skull, 1, 3)(ModAchievements.java:135),meta 3 是玩家头颅,刻意回避了盖亚守护者自身的模型。
说明文本里的占位符:l20ShardUse.desc 写的是「用能量为 XX 的拉普达碎片」,XX 是未替换的占位符(原文本即如此)。
零星未汉化的标题:上表中有 20 余个成就标题回退到 en_US(例如 Noisy Lover Soul、Imagination Forest),但 .desc 全部有中文译文。已确认 zh_CN.lang 中 49 条 .desc 齐全,标题则部分缺失。