任务数据模型 (Quest Data Model)
基本信息
| 属性 | 值 |
|---|---|
| 层级 | 任务线(QuestLine / 章节) → 任务(Quest) → 任务项(Task)/ 奖励(Reward) |
| 核心实现 | QuestLineDatabase(questing/QuestLineDatabase.java)、QuestDatabase(questing/QuestDatabase.java) |
| 任务实体 | QuestInstance(questing/QuestInstance.java),接口 IQuest |
| 任务项容器 | TaskStorage(questing/tasks/TaskStorage.java) |
| 奖励容器 | RewardStorage(questing/rewards/RewardStorage.java) |
| 底层容器 | api2/storage/UuidDatabase、SimpleDatabase、DBEntry |
| 任务项工厂 | TaskRegistry(questing/tasks/TaskRegistry.java) |
| 占位任务工厂 | api.placeholders.tasks.FactoryTaskPlaceholder |
功能
BetterQuesting 的世界数据分三层,全部以 UUID 为键的 UuidDatabase 组织,并全部实现 IDatabaseNBT<…, NBTTagList, NBTTagList> 以 NBT 列表形式存档。
第一层:任务线(QuestLine = 章节)
QuestLine extends UuidDatabase<IQuestLineEntry>,即任务线本身也是"一个装着若干条目的数据库"。每个条目 QuestLineEntry 只描述一个图上矩形:posX / posY / sizeX / sizeY(QuestLineEntry.java:12-15)。QuestLineEntry(int x, int y) 的双参构造等价于 24×24(QuestLineEntry.java:23-25);单参 (x, y, size) 的构造已被标注 @Deprecated。getSize() 取 max(sizeX, sizeY),同样 @Deprecated。
QuestLine 自身还持有一个 PropertyContainer info(QuestLine.java:31),其默认属性为 NAME = "New Quest Line"、DESC = "No Description"、ICON = BigItemStack(Items.book)、VISIBILITY = NORMAL(QuestLine.setupProps(),QuestLine.java:41-45)。
QuestLineDatabase 额外维护一个 lineOrder(List<UUID>)决定章节在界面中的排序。源码注释明确承认它与父类 BiMap 不完全同步(QuestLineDatabase.java:34-42)。
第二层:任务(Quest)
QuestDatabase.createNew(UUID) 返回 new QuestInstance() 并入表(QuestDatabase.java:26-31)。put() 被覆写:若传入 null 且 BQ_Settings.logNullQuests 为真,打印 A null quest was added with ID {} 警告(QuestDatabase.java:33-39)。remove() 覆写为只接受 UUID 键(QuestDatabase.java:41-44)。
IQuest 接口(api/questing/IQuest.java)暴露的核心状态机方法:
| 方法 | 含义 |
|---|---|
getState(EntityPlayer) |
返回 EnumQuestState |
isUnlocked(UUID) / isComplete(UUID) / hasClaimed(UUID) |
三态判定 |
isUnlockable(UUID) |
「在不靠 /bq_admin 或手改存档的前提下是否还有可能解锁」——任务逻辑用它判断死锁 |
canSubmit(EntityPlayer) / canClaim(EntityPlayer[, boolean forceChoice]) |
可否提交 / 可否领奖 |
canClaimBasically(EntityPlayer) |
「有没有奖励」,与 canClaim 区分(注释明说:奖励存在但选择型奖励未选时 canClaim 为假) |
getTasks() / getRewards() |
返回 IDatabaseNBT<ITask, NBTTagList, NBTTagList> / IDatabaseNBT<IReward, …> |
getRequirements() / setRequirements(Iterable<UUID>) |
前置依赖 UUID 集合,返回可变集合(注释明说改动会直接写回任务) |
getRequirementType(UUID) / setRequirementType(UUID, RequirementType) |
每个依赖有独立的三档可见性 |
resetUser(UUID, boolean fullReset) |
重置玩家进度 |
IQuest.RequirementType 枚举只有三个值,各带一个图标与一句按钮 tooltip(IQuest.java):
| 值 | 序号 (id()) |
语义 |
|---|---|---|
NORMAL |
0 | 普通前置 |
IMPLICIT |
1 | 隐式前置 |
HIDDEN |
2 | 隐藏前置 |
from(byte id) 对越界值返回 NORMAL 而非抛异常(id >= 0 && id < VALUES.length ? VALUES[id] : NORMAL);next() 在三个值间循环。
第三层:任务项与奖励
任务项(ITask)与奖励(IReward)都是工厂注册的内容,存放在任务各自的 TaskStorage / RewardStorage 里。它们在存档中带 ID 字符串字段(如 rewardID)用于重建,加载路径为 RewardRegistry.INSTANCE.createNew(loc)(RewardStorage.java:45)。
任务类型注册表
TaskRegistry implements IRegistry<IFactoryData<ITask, NBTTagCompound>, ITask>,单例 INSTANCE。它的 createNew(ResourceLocation) 有一段特殊的优先级分支(TaskRegistry.java:57-62):若请求的名字等于 FactoryTaskPlaceholder.INSTANCE.getRegistryName(),则无视注册表内容、直接用 FactoryTaskPlaceholder.INSTANCE 创建。这是让占位任务在任何附属包缺席时都能被重建的兜底路径(RewardRegistry.createNew 有对称实现)。
注册时的三重校验(TaskRegistry.register,TaskRegistry.java:28-40):拒绝 null 工厂、拒绝 getRegistryName() == null、拒绝重名或同一实例重复注册(containsKey || containsValue),均抛 IllegalArgumentException / NullPointerException。加载缺失类型时打印 Tried to load missing task type '…'! Are you missing an expansion pack? 并返回 null(TaskRegistry.java:65-69)。
核心模组自身不注册任何具体任务类型。
grep -rln 'implements .*ITask' src/main/java/betterquesting只命中注册表/存储与TaskPlaceholder。13 个具体任务类型(破坏方块、合成、猎杀、到达地点等)由**同仓库内另一个 modidbq_standard(Standard Expansion)**在bq_standard/core/proxies/CommonProxy.registerExpansion()中注册(CommonProxy.java:60-72),并声明dependencies = "required-after:betterquesting"。详见 扩展示例加载器。
数值
| 数值名 | 值 | 来源 |
|---|---|---|
QuestLineEntry 默认尺寸 |
24 × 24 | QuestLineEntry(int x, int y) → this(x, y, 24, 24)(QuestLineEntry.java:23-25) |
| 前置依赖可见性档位 | 3 | RequirementType.values().length |
| 任务状态数 | 5 | EnumQuestState:LOCKED / UNLOCKED / UNCLAIMED / COMPLETED / REPEATABLE |
| 任务可见性档数 | 7 | EnumQuestVisibility:HIDDEN / SECRET / UNLOCKED / NORMAL / COMPLETED / CHAIN / ALWAYS |
| 任务项逻辑运算种类 | 6 | EnumLogic:AND / NAND / OR / NOR / XOR / XNOR |
| 核心自带具体任务类型 | 0 | TaskRegistry 无任何 taskReg.register(...) 调用 |