任务数据模型 (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 个具体任务类型(破坏方块、合成、猎杀、到达地点等)由**同仓库内另一个 modid bq_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(...) 调用

相关条目