feedback7 R22-GHSIM: three or more overlapping 同名效果取最高 applications resume strongest first — the middle one no longer drops out when the strongest ends (GitHub #342, PR #348 by @siruimei07); golden: diy-059, diy-122, diy-123, diy-124, diy-133, diy-138 move
The contributor's code (status.js _applyValuedStatus and both test files, applied as written from PR #348), so they are the author of this commit; the maintainer side added the 同名效果取最高 label back to the JSDoc and a docs/SIM.md line. buff.data.tail held ONE waiting application (the later-ending one): a third, weaker but longer one replaced a middle one that was still running, so when the strongest ended the effect jumped to the weakest (#342: 30 % / 25 % / 20 % 减防 — DEF 1000 → 700 → 800 instead of 750 → 800 → 1000). The tail is now a chain ordered by strength (decreasing) and end (increasing): a weaker application is inserted in it, a stronger one takes over with the old value and its chain behind it, an equal refresh trims waiters that end before it, and an expiry resumes the next live waiter with the rest of the chain. removeStatus / removeBuff still drop the whole chain; two-application data shapes are unchanged. Golden: six 自选 battles that stack three same-name applications on one unit — diy-059 / diy-122 / diy-123 / diy-124 (庇护 from several operators), diy-133 (Mon3tr's ASPD talent buff talent:monstr:aspd), diy-138 (脆弱) — the middle application now resumes.
This commit is contained in:
1 parent
ae22335cfe
commit
3356b340a7
5 files changed
+261
-62
No files matched your search
@@ -274,23 +274,39 @@ export class BattleStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
* "同名效果取最高": keep the strongest value; a weaker one that outlasts it resumes afterwards (buff.data.tail). A
|
||||
* "同名效果取最高": keep the strongest value, then resume weaker effects in order of strength at their original expiry times.
|
||||
* `buff.data.tail` links the waiting effects, with decreasing strength and increasing expiry times. A
|
||||
* `plain` template (applyStrongest) is an ordinary invisible buff, not a status. `stackAs` (finite): the strength this
|
||||
* application competes with instead of its value (applyStatus opts.stackAs); kept in `data.stackAs` / the tail.
|
||||
*/
|
||||
_applyValuedStatus(target, key, tpl, duration, value, source, stackAs = null) {
|
||||
const as = Number.isFinite(stackAs) ? stackAs : null;
|
||||
const strength = (v, s = null) => Math.abs(Number.isFinite(s) ? s : Number.isFinite(v) ? v : tpl.valued);
|
||||
// (no stackAs: the very objects of before — data { value, tail }, tails { value, until })
|
||||
const entry = (v, s, extra) => (Number.isFinite(s) ? { value: v, stackAs: s, ...extra } : { value: v, ...extra });
|
||||
const after = (tail, until) => {
|
||||
while (tail && tail.until <= until) tail = tail.tail;
|
||||
return tail || null;
|
||||
};
|
||||
const insertTail = (tail, incoming) => {
|
||||
if (!tail) return incoming;
|
||||
const diff = strength(incoming.value, incoming.stackAs) - strength(tail.value, tail.stackAs);
|
||||
if (diff > 1e-12) return { ...incoming, tail: after(tail, incoming.until) };
|
||||
if (diff < -1e-12) {
|
||||
if (incoming.until <= tail.until) return tail;
|
||||
return { ...tail, tail: insertTail(tail.tail, incoming) };
|
||||
}
|
||||
// Equal-priority waiting effects keep the longest-lived application's value, including its stackAs.
|
||||
const kept = incoming.until > tail.until ? incoming : tail;
|
||||
return { ...kept, tail: after(tail.tail, kept.until) };
|
||||
};
|
||||
const make = (v, dur, tail, s = null) => ({
|
||||
...(tpl.buff || null), // extra buff fields of the status (抵抗: the 麻痹 decay tick)
|
||||
key, duration: dur, refresh: 'replace', mods: tpl.mods(v), flags: tpl.flags || null, status: tpl.plain ? null : key,
|
||||
visible: !tpl.plain, source,
|
||||
data: entry(v, s, { tail }),
|
||||
onExpire: ({ battle, unit, buff }) => {
|
||||
const t = buff.data.tail;
|
||||
if (t && t.until - battle.time > 1e-6 && unit.alive) battle.addBuff(unit, make(t.value, t.until - battle.time, null, t.stackAs));
|
||||
const t = after(buff.data.tail, battle.time + 1e-6);
|
||||
if (t && unit.alive) battle.addBuff(unit, make(t.value, t.until - battle.time, t.tail || null, t.stackAs));
|
||||
},
|
||||
});
|
||||
const old = target.buffs.find((b) => b.key === key && (tpl.plain || b.status === key));
|
||||
@@ -299,17 +315,17 @@ export class BattleStatus {
|
||||
const oldAs = old.data && Number.isFinite(old.data.stackAs) ? old.data.stackAs : null;
|
||||
const oldEnd = this.time + old.timeLeft, newEnd = this.time + duration;
|
||||
const oldTail = old.data && old.data.tail;
|
||||
const longerTail = (a, b) => (!a ? b : !b ? a : (b.until > a.until ? b : a));
|
||||
if (strength(value, as) > strength(oldV, oldAs) + 1e-12) {
|
||||
// stronger: takes over now; the weaker old one (or its tail) resumes if it lasts longer
|
||||
const tail = longerTail(oldEnd > newEnd ? entry(oldV, oldAs, { until: oldEnd }) : null, oldTail && oldTail.until > newEnd ? oldTail : null);
|
||||
// Stronger: take over now, retaining every old effect that can still become active afterwards.
|
||||
const tail = after(entry(oldV, oldAs, { until: oldEnd, tail: oldTail }), newEnd);
|
||||
this.addBuff(target, make(value, duration, tail, as));
|
||||
} else if (strength(value, as) < strength(oldV, oldAs) - 1e-12) {
|
||||
// weaker: never overrides; remembered as the tail when it outlasts the running one
|
||||
if (newEnd > oldEnd && (!oldTail || newEnd > oldTail.until)) old.data = { ...old.data, value: oldV, tail: entry(value, as, { until: newEnd }) };
|
||||
// Weaker: insert among the waiting effects instead of replacing the one with the latest expiry.
|
||||
if (newEnd > oldEnd) old.data = { ...old.data, value: oldV, tail: insertTail(oldTail, entry(value, as, { until: newEnd })) };
|
||||
} else if (duration > old.timeLeft) {
|
||||
old.timeLeft = duration;
|
||||
old.duration = Math.max(old.duration, duration);
|
||||
old.data.tail = after(oldTail, newEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user