挑战系统
基本信息
| 属性 | 值 | 来源 |
|---|---|---|
| 注册类 | ModChallenges |
client/challenge/ModChallenges.java |
| 难度枚举 | EnumChallengeLevel:4 级 |
client/challenge/EnumChallengeLevel.java:13-18 |
| 挑战条目 | 20(6 + 6 + 4 + 4) | ModChallenges.init(),20 次 addChallenge |
| 数据类 | Challenge |
client/challenge/Challenge.java |
| 初始化调用 | ClientProxy.java:209 |
纯客户端 |
| 展示界面 | 辞典 GUI 的「挑战」页 | client/gui/lexicon/GuiLexiconChallengesList.java、GuiLexiconChallenge.java |
| 持久化 | 客户端本地文件 | PersistentVariableHelper → injectNBTToFile(cmp, getCacheFile()) |
⚠️ 整个系统位于 client/ 包,不产生任何服务端注册。服务端既不知道有哪些挑战,也不知道玩家完成了哪些挑战。
这是「挑战」而不是「宾果」
.desc 文本里反复出现「奖励积分」(bonus points),但 Botania 源码中没有任何积分 / 计分 / 物品限制代码:
- 全仓
grep -rlni 'bingo' src/main/java→ 无任何命中,模组里没有宾果(Bingo)玩法 Challenge只有 3 个字段 + 1 个布尔:unlocalizedName/icon/level/complete(Challenge.java:18-21)- 没有任何类读取「完成状态」来改变物品可获得性
所以 Botania 交付的是挑战定义 + 自查清单 UI,积分判定与奖励由外部整合包 / 宾果封装包负责。描述文本引用积分,只是因为这些挑战最初为宾果卡组设计。读源码时不要期待在本模组内找到计分逻辑。
完成状态如何被设置
Challenge.complete 是 public boolean(Challenge.java:21),唯一的写入点是玩家在辞典界面手动点击按钮:
challenge.complete = !challenge.complete; // GuiLexiconChallenge.java:113
即玩家自己勾选「这条我做到了」。没有自动检测、没有统计系统。GuiLexiconChallenge.java:171 通过 ModChallenges.challengeLookup.get(challengeName) 按未本地化名取回实例。
持久化
PersistentVariableHelper 把挑战状态与其他辞典数据一起写入本地缓存文件(PersistentVariableHelper.java:59-62):
NBTTagCompound challengesCmp = new NBTTagCompound();
for(Challenge c : ModChallenges.challengeLookup.values())
c.writeToNBT(challengesCmp); // cmp.setBoolean(unlocalizedName, complete)
cmp.setTag(TAG_CHALLENGES, challengesCmp);
读取在 PersistentVariableHelper.java:97-99。同一个 NBT 还保存书签(TAG_BOOKMARKS)、辞典笔记(TAG_LEXICON_NOTES)、首载标记与上次 Botania 版本号。
注意:因为存在客户端本地文件,换电脑 / 删 .minecraft 下的缓存目录会丢失所有勾选状态,且多人游戏中各客户端状态独立、不同步。
20 条挑战
难度键为 botania.challengelevel.<level>,条目键为 botania.challenge.<name>。四条难度:EASY / NORMAL / HARD / LUNATIC。
| 难度 | 键 | 名称 | 图标 |
|---|---|---|---|
| EASY | flowerFarm |
花卉农场 | ModBlocks.flower meta 6 |
| EASY | recordFarm |
唱片农场 | Items.record_13 |
| EASY | reedFarm |
甘蔗农场 | Items.reeds |
| EASY | cobbleGen |
圆石制造机 | Blocks.cobblestone |
| EASY | pureDaisy |
自动白雏菊 | subtile_puredaisy |
| EASY | battery |
魔力电池 | ModBlocks.pool |
| NORMAL | apothecaryRefill |
药剂台注水器 | ModBlocks.altar |
| NORMAL | treeFarm |
树场 | Blocks.sapling |
| NORMAL | fullCropFarm |
齐全的作物农场 | Items.wheat_seeds |
| NORMAL | animalFarm |
动物农场 | Items.leather |
| NORMAL | boneMealFarm |
骨粉农场 | Items.dye meta 15(骨粉) |
| NORMAL | orechid |
自动凝矿 | subtile_orechid |
| HARD | mobTower |
刷怪塔 | Items.bone |
| HARD | entropinnyumSetup |
热爆装置 | subtile_entropinnyum |
| HARD | spectrolusSetup |
斑斓装置 | subtile_spectrolus |
| HARD | potionBrewer |
自动酿造 | ModBlocks.brewery |
| LUNATIC | kekimurusSetup |
贪食装置 | subtile_kekimurus |
| LUNATIC | autoQuarry |
自动采矿 | Items.diamond_pickaxe |
| LUNATIC | runeCrafter |
符文合成 | ModItems.rune |
EASY / NORMAL 各 6 条,HARD / LUNATIC 各 4 条 —— 难度越高条目越少,但 LUNATIC 的描述明确写着「你必须非常疯狂才能尝试这个」。
20 条挑战中有 4 条直接考据功能花(pureDaisy、orechid、entropinnyumSetup、spectrolusSetup、kekimurusSetup 共 5 条),即「让某朵花完全自动化」。这与凝矿兰权重表那类内部数据配合阅读效果最好。
注册机制
public static void addChallenge(EnumChallengeLevel level, String name, ItemStack icon) {
Challenge c = new Challenge("botania.challenge." + name, icon, level);
challenges.get(level).add(c); // 按难度分桶
challengeLookup.put(c.unlocalizedName, c); // 另建按未本地化名的索引
}
init() 先按 EnumChallengeLevel.class.getEnumConstants() 为 4 个难度各建一个空 ArrayList(ModChallenges.java:32-33),所以 challenges 永远是 4 键的 EnumMap,即使某难度没有条目也不会缺键。
注意 challengeLookup 以 unlocalizedName(含 botania.challenge. 前缀)为键,不是裸名。