feedback5 WE1: 秘术师 stores its energy at the attack check, not one interval later (GitHub #181)
PRTS 分支特性信息 秘术师: "能量储存与攻击占用相同的攻击间隔:在进行攻击判定时…若无有效目标且能量储存数未满,则改为储存一份 攻击能量(属于攻击行为);仅没有有效目标,且能量储存数已满的情况下才进入待机状态", "攻击被打断且弹道未能成功生成的情况下,储存的 能量不会被消耗"; PRTS 异常效果 缴械 "秘术师储存能量同样无法进行". - ai.js updateAlly: a ready unit (able to act, not disarmed) whose attack check finds no valid target — no target, or its profile's canAttack false (a 秘术师 kit's target rule: 深靛's bound enemies, 维伊's marked ones) — calls the profile's storeEnergy; a stored energy restarts the attack interval, a full store idles with the attack ready. A DEFAULT cast at the check that leaves no target stores in the same check. - professions.js mystic: storeEnergy (up to bb times), releaseEnergy (performAttack, after beforeAttack: a cancelled shot keeps them) and hitsFn(b, u, info) — the energies leave with the attack and land with its main hit; the old separate store timer (it started only once the attack was ready and never restarted the interval) is gone. [ASSUMED] a redeployment holds no energy. - 维伊 / 黑键 kits: their own tick stores become the kit's storeEnergy on the shared check; 深靛 / 维伊 no longer write hadTarget themselves. Golden: roster-003, roster-021, roster-022, bond-preciShip-high (深靛 stores at her check); matches solo-FUNNY-1, solo-NORMAL-2, solo-HARD-2, solo-ABYSS-1, solo-ABYSS-2, coop2-NORMAL-3, coop2-HARD-4, coop4-FUNNY-6, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, solo-HARD-9-boosted, coop2-ABYSS-10-boosted, coop2-NORMAL-14-standins (each fields 深靛); diy-032 (维伊) and diy-114 (黑键): the first store after a redeployment comes at the next tick's attack check instead of the end of the redeploy tick (with the old tick store added back as a fallback the diy family matches the old digests). fields, standins unchanged. Tests: test/sim/feedback5-mystic-store.test.js (new, 4); op_veen, op_ebnhlz, kits_t1t2, kits_alt_t1 pass unchanged. Docs: SIM.md §Professions (mystic row, profile hooks).
This commit is contained in:
1 parent
a009d77d71
commit
8f62f3e360
11 files changed
+354
-214
No files matched your search
+17
-5
@@ -79,20 +79,30 @@ export function updateAlly(b, u, dt) {
|
||||
if (prof.noAttackUnlessSkill && !(sk && sk.active)) return;
|
||||
if (u.s.flags.disarm) return;
|
||||
if (u.atkCd > 0) return;
|
||||
if (prof.canAttack && !prof.canAttack(b, u)) return;
|
||||
// (a 秘术师's `canAttack` is its own target rule: false = no valid target — 深靛's bound enemies, 维伊's marked ones)
|
||||
if (prof.canAttack && !prof.canAttack(b, u)) { if (prof.storeEnergy) { u.trait.hadTarget = false; storeEnergy(b, u, prof); } return; }
|
||||
let targets = acquireTargets(b, u, prof);
|
||||
if (!targets.length) { u.trait.hadTarget = false; return; }
|
||||
if (!targets.length) { u.trait.hadTarget = false; storeEnergy(b, u, prof); return; }
|
||||
u.trait.hadTarget = true;
|
||||
if (sk && sk.onAboutToAttack()) {
|
||||
prof = effectiveProfile(u);
|
||||
if (prof.noAttack || !u.alive) return;
|
||||
targets = acquireTargets(b, u, prof);
|
||||
if (!targets.length) return;
|
||||
if (!targets.length) { storeEnergy(b, u, prof); return; } // the cast left none (a range change): still this check
|
||||
}
|
||||
performAttack(b, u, prof, targets);
|
||||
u.atkCd = Math.max(u.atkCd, u.s.interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* The attack check of a ready unit found no valid target: a 秘术师 profile stores one energy instead (`storeEnergy`,
|
||||
* professions.js installMystic — PRTS 分支特性信息 秘术师 "若无有效目标且能量储存数未满,则改为储存一份攻击能量(属于攻击
|
||||
* 行为)": the attack interval starts again); with a full store it idles, ready to attack.
|
||||
*/
|
||||
function storeEnergy(b, u, prof) {
|
||||
if (prof.storeEnergy && prof.storeEnergy(b, u)) u.atkCd = Math.max(u.atkCd, u.s.interval);
|
||||
}
|
||||
|
||||
/** Release blocked enemies beyond the current block capacity (latest blocked first). */
|
||||
export function enforceBlockCapacity(b, u) {
|
||||
const cap = u.alive && u.deployed ? u.s.blockCnt : 0;
|
||||
@@ -174,11 +184,13 @@ export function performAttack(b, u, prof, targets, opts = null) {
|
||||
}
|
||||
const ranged = !prof._fortressMelee && prof.attack === 'ranged' && prof.projectile && prof.projectile !== 'none' && prof.projectile !== 'beam';
|
||||
const vis = prof._fortressMelee ? 'none' : (prof.projectile || 'none');
|
||||
// 秘术师: the stored energies leave with this attack, at its main target (professions.js installMystic)
|
||||
const energy = !isHeal && prof.releaseEnergy ? prof.releaseEnergy(b, u) : 0;
|
||||
for (let i = 0; i < targets.length; i++) {
|
||||
const t = targets[i];
|
||||
b._ev(['atk', u.id, t.id, vis]);
|
||||
if (isHeal) { doHeal(b, u, prof, t); continue; }
|
||||
const info = { isSkill, index: i, attackId };
|
||||
const info = { isSkill, index: i, attackId, energy: i === 0 ? energy : 0 };
|
||||
const foe = t.side === 'enemy' || b.isAllyTarget(t);
|
||||
if (ranged && foe && prof.projectile === 'boomerang') {
|
||||
throwBoomerang(b, u, prof, t, info);
|
||||
@@ -260,7 +272,7 @@ export function resolveHit(b, u, prof, target, info, x, y) {
|
||||
if (target && target.alive) {
|
||||
let mulT = skillMul;
|
||||
if (prof.dmgMul) { const m = typeof prof.dmgMul === 'function' ? prof.dmgMul(b, u, target) : prof.dmgMul; if (Number.isFinite(m)) mulT *= m; }
|
||||
const hits = prof.hitsFn ? prof.hitsFn(b, u) : Math.max(1, prof.hits || 1);
|
||||
const hits = prof.hitsFn ? prof.hitsFn(b, u, info) : Math.max(1, prof.hits || 1);
|
||||
let dealtMain = 0;
|
||||
for (let h = 0; h < hits && target.alive; h++) {
|
||||
dealtMain += b.dealDamage(u, target, { amount: atk * scale * mulT, type: baseType, isAttack: true, isSkill: info.isSkill, tags: prof.tags || [], attackId });
|
||||
|
||||
Reference in new issue
Block a user