深色模式
战斗、治疗与资源 API
注册 Combat Plan
计划是不可变的伤害模板,必须在启动注册窗口内注册:
java
CombatPlan plan = new CombatPlan(
CombatPlanId.of("skillpack", "arcane_strike"),
0.0, 1.0, 1.0, // 固定物伤、物攻倍率、物防 reference floor
40.0, 0.8, 1.0, // 固定法伤、法攻倍率、法防 reference floor
0.0, // 真实伤害倍率
Set.of(
CombatCapability.CRITTABLE,
CombatCapability.AFFECTED_BY_DEFENSE,
CombatCapability.TRIGGERS_LIFESTEAL,
CombatCapability.TRIGGERS_REFLECTION
),
new CombatFeedback(true, true, 0.2)
);
RegistrationResult registered = api.combat().registerPlan(plan);
if (!registered.accepted()) {
throw new IllegalStateException("plan rejected: " + registered.getStatus());
}capability 是正向授权:没声明就不闪避、不暴击、不防御、不格挡、不吸血、不反伤。计划最多 256 个,每 provider 只能使用内置或自己命名空间的计划。
提交伤害
java
CombatOrigin origin = new CombatOrigin(
Optional.empty(),
actorSubject,
Optional.empty(),
CombatOriginType.SKILL,
plan.getCapabilityBits()
);
CombatResult result = api.combat().submit(
new CombatRequest(UUID.randomUUID(), plan.getId(), origin, targetSubject, 0)
);
if (result.getStatus() == CombatStatus.APPLIED) {
double actualLost = result.getActualLifeLost();
}第三方 origin 使用 SKILL 或 API。提交是同步确定性核心入口,但必须位于 Paper 认可的目标实体执行上下文;不要从任意异步线程调用 Bukkit 实体链。request UUID 用于幂等/诊断,不能重复复用到不同伤害。
Combat Gate
Gate 适合安全区、队伍免伤、剧情无敌:
java
api.combat().registerGate(new CombatGate() {
@Override public String id() { return "skillpack:safe_zone"; }
@Override public int priority() { return 0; }
@Override public GateDecision evaluate(CombatGateContext context) {
return protectedTarget(context) ? GateDecision.deny("protected") : GateDecision.pass();
}
});Gate 只做纯内存、确定性判断;禁止数据库、网络、文件、阻塞和重入。抛异常时 fail closed 为拒绝。最多 128 个 Gate。
Formula / Combat / Platform Extension
- FormulaProvider 最多 128,纯函数式把聚合基值转换为有效值;
- CombatExtension 最多 128,每次最多输出 16 个 modifier、16 条诊断;
- PlatformExtension 最多 128,每次最多 16 个投影 intent;
- priority 决定稳定排序,不应依赖未声明的注册偶然顺序;
- 回调不得访问可变全局状态、Bukkit 活对象、I/O 或重入写 API;异常被隔离,相关能力不会以猜测结果继续。
主动治疗
java
HealingResult result = api.healing().submit(new HealingRequest(
UUID.randomUUID(),
targetSubject,
new HealingOrigin(HealingOriginType.API, Optional.of(actorSubject), "skillpack:heal"),
80.0,
0
));在合法实体上下文同步提交。Core 应用施法者 healing_power、目标 received_healing_bonus(依 origin/请求语义)并夹取缺失生命。检查 HealingStatus,不要只看返回数值。完成事件不可取消。
资源模型
资源定义由启动扩展注册,例如 douluo:soul_power,绑定:最大值属性、可选固定恢复、百分比恢复、消耗减免。当前余额不是属性来源,不能用 ADD 表示。
java
ResourceId soulPower = ResourceId.of("douluo", "soul_power");
ResourceResult consumed = api.resources().tryConsume(subject, soulPower, 50.0);
switch (consumed.getStatus()) {
case APPLIED -> startSkill(consumed.getConsumedAmount());
case INSUFFICIENT -> showInsufficient();
default -> handleUnavailable(consumed.getStatus());
}tryConsume 应用消耗减免并原子检查余额;不足不部分扣。先完成权限、冷却、目标等前置检查,再扣除。扣除成功代表施法开始,后续落空/格挡不会自动退款;确需补偿,用返回的 consumedAmount 显式 restore。
公开方法只有:
java
query(subject, resource);
tryConsume(subject, resource, amount);
restore(subject, resource, amount);
restorePercent(subject, resource, maximumRatio);
fill(subject, resource);没有任意 set、负数 add、转账、预留事务或隐式回滚。状态为 APPLIED、INSUFFICIENT、NOT_READY、DEAD、UNKNOWN_RESOURCE、STALE_SUBJECT、STALE_OWNER、INVALID_AMOUNT、CLOSED。
资源操作是同步内存事务,不访问数据库;异步检查点由 SealAttributes 自己负责,玩法插件不要另存第二份权威余额。玩家离线不恢复;崩服最多回退一个 checkpoint 周期。上限升高不赠送余额,上限降低会夹取;死亡冻结消费/恢复,普通重生保留剩余值。
测试策略
- 单元测试构造 API 值对象,覆盖非法 ID、NaN、重复贡献、stale revision;
- 编译测试只依赖 API JAR,防止意外引用 Core/Paper;
- 用 fake/provider fixture 验证 status 分支和 CompletionStage 非阻塞;
- Paper 集成服验证线程上下文、owner disable、重登 subject epoch、Gate 异常、战斗事件;
- 真实依赖服再验证 MythicMobs/SealItems,不用 Mock 测试代替。
升级 API 时按 baseline 比较公共签名并重新编译。旧 MAX_CONTRIBUTIONS_PER_SOURCE 常量仍在 ABI 中但不再限制快照;不要用它截断新数据。