feedback7: merge fb7-gh-sim — PR #348 (take-the-highest chains), PR #293 (REA-Y in the engine), #329 ports (异客 13 tiles, 歌蕾蒂娅 net radius 1, 搅拌机 latest first, 大帝 uncapped), PR #365 (骨刺 forms + PRTS targeting), #298 (instant non-attack skills recast at most once per attack interval, attacks untouched), #364 (先兆者 shell 3×3), #220 (速射手 air first), 余 skips noStack bonds (owner)
- Conflict: docs/PLAYING.md 技能自动释放 bullet — both insertions kept (the 铁钳号 rule from fb7-owner-sim, the #298
pacing sentence).
- Grok review: MERGE AFTER FIXES — the HIGH #298 finding (the cast replaced the due attack) fixed on the branch
(db29acf9: per-skill nextCastAt; golden of #298 alone shrank from 84 scenarios to 2).
- Golden union: 4 matches differ from HEAD though the branch alone left them (coop2-NORMAL-3, coop4-ABYSS-7,
coop2-NORMAL-8-serverrun, solo-HARD-9-boosted): HEAD's side had moved all four (#333's shop RNG …) and on those new
bot paths the merged rules apply — the diffs are in-battle layer gains / damage (卡西米尔 50 → 45, 精准 80 → 76,
叙拉古 176 → 168, kills ±2, solo-HARD-9 LP 397 → 399), no outcome change.
- Broad node --test 5571 (5570 pass, 0 fail, 1 skipped); lint 0 errors; typecheck.
This commit is contained in:
commit
52f56319ac
41 files changed
+2065
-648
No files matched your search
+17
-2
@@ -108,6 +108,7 @@ export class SkillRuntime {
|
||||
// the official skill strategies automate the manual 开启: only MANUAL skills wait for the operation cooldown
|
||||
this.manual = String(d.skillType ?? 'MANUAL').toUpperCase() === 'MANUAL';
|
||||
this.opReadyAt = -Infinity; // no automatic cast before this battle time (AUTO_OP_COOLDOWN)
|
||||
this.nextCastAt = -Infinity; // instant / charges with no attack of its own: no cast from the tick before (GitHub #298)
|
||||
if (this.kind === 'passive') this.baseSpCost = 0;
|
||||
this._spCostMul = 1; // content may set spCostMul (e.g. 绝技 ×0.7) — see the accessor below
|
||||
this.sp = 0;
|
||||
@@ -269,6 +270,7 @@ export class SkillRuntime {
|
||||
this._trigKeys = null;
|
||||
this._trigSet = null;
|
||||
this.opReadyAt = -Infinity; // (Battle._deploy starts the operation cooldown of the battle-start deployment)
|
||||
this.nextCastAt = -Infinity;
|
||||
if (this.noSkill) return;
|
||||
if (this.kind === 'passive') {
|
||||
this._startPassive();
|
||||
@@ -389,6 +391,9 @@ export class SkillRuntime {
|
||||
if (this.active && this.isTimed) return;
|
||||
// a cast "next attack" still waits for its attack: another charge now would be spent on the same attack
|
||||
if (this.pending || this._opCooling()) return;
|
||||
// an instant / charge skill with no attack of its own is cast at most once per attack interval of its unit
|
||||
// (activate sets nextCastAt; GitHub #298): the casts checked here wait for it — the unit's attacks are not touched
|
||||
if (!this.isTimed && !this.spec.attack && this.battle.time < this.nextCastAt - 1e-9) return;
|
||||
if (TICK_RULES.has(this.rule)) {
|
||||
if (this._tickRuleSatisfied()) this.activate(this.rule);
|
||||
} else if (this.rule !== 'TAKE_DAMAGE' && this.rule !== 'NEVER') {
|
||||
@@ -493,7 +498,8 @@ export class SkillRuntime {
|
||||
if (this.pending || this._opCooling()) return false;
|
||||
if (!this._defaultCondition()) return false;
|
||||
if (this.triggerAllies && !this._allyTriggerSatisfied()) return false;
|
||||
return this.activate('DEFAULT');
|
||||
this._beforeAttack = true; // the attack follows this cast in the same check (ai.js updateAlly): attacks pace it
|
||||
try { return this.activate('DEFAULT'); } finally { this._beforeAttack = false; }
|
||||
}
|
||||
|
||||
/** TAKE_DAMAGE trigger + INCREASE_WHEN_TAKEN_DAMAGE SP. */
|
||||
@@ -541,7 +547,16 @@ export class SkillRuntime {
|
||||
// bullets added in skillStart (拉特兰's ×(1.05 + 0.015 × layers), 逃犯引渡手续, talents): the bar's full mark
|
||||
// (community report #35: the extra bullets sat above a full bar until fewer than the base count were left)
|
||||
if (this.active && this.ammoLeft > this.ammoMax) this.ammoMax = this.ammoLeft;
|
||||
if (!this.isTimed && !this.pending) this.end('instant');
|
||||
if (!this.isTimed && !this.pending) {
|
||||
this.end('instant');
|
||||
// an instant / charge skill with no attack of its own (no spec.attack: a throw, a heal, a buff, DP …) waits one
|
||||
// attack interval of its unit before it is cast again from the tick (GitHub #298: 引星棘刺 S1 度算浪波, an SP_FULL
|
||||
// cast, was recast every tick once 迅捷's refund refilled its bar at once, whatever the attack speed) — on the skill
|
||||
// only: the cast is no attack, the unit's attacks / heals keep their own rhythm. [ASSUMED] one attack interval: PRTS
|
||||
// prints no 前后摇 for such casts. A cast made right before an attack (DEFAULT) is paced by those attacks already;
|
||||
// a cast onEnd opened again owns its own gap.
|
||||
if (!this.active && !this.spec.attack && !this._beforeAttack && u.alive && u.s.interval > 0) this.nextCastAt = b.time + u.s.interval;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user