feedback1f WT: 起飞 review 2 — 暴虐兵长's 暴击 splash and the ticks of debuffs already on her reach an airborne 蒂比
PRTS 萨卡兹悖谬暴虐兵长 技能0 暴击 "对目标和周围4格的我方单位造成攻击力150%的物理普通伤害(无视无法选择,无视迷彩)": the splash passes ignoreSelect. The ticks of a debuff a ground enemy put on her before she took off (出血, 沙狱, the burning DoT, 淤困, 【自然涌动】) select nobody (PRTS 异常效果: 无法选择 effects only act on selection), so they pass ignoreSelect too; a shot already in flight is still refused [ASSUMED]. Comments in tier2.js and SIM.md no longer say that nothing of a ground enemy damages her.
This commit is contained in:
1 parent
2ec51158d0
commit
a973bbe35f
7 files changed
+120
-36
No files matched your search
+12
-8
@@ -209,10 +209,11 @@ an airborne ally (起飞, flag `liftoff`: 蒂比's skills) never for a ground en
|
||||
PRTS 术语释义 起飞 "无法被不同阵营行动方式为地面的单位选中"; flyers, 近地悬浮 and 浮空 enemies still pick it; content picks
|
||||
and one-shot areas that bypass `canTargetAlly` — 控潮术师 / 腐败骑士's 周围四格, 陷落雪祀's chain, the 碎铳之簧 bounce, the
|
||||
自行炮 shells, “帝国的甲胄”'s barrage, 鼠王's 沙狱, 烹泉's death blast and steam — filter it too; not selections, so they
|
||||
still reach it (`ignoreSelect`): abilities PRTS marks "无视无法选择" (【污染秽蚀】, 假想敌:铳's 【盲信之誓】), direct picks
|
||||
(碎铳之簧's 法术护盾 counter on its attacker — PRTS 异常效果 "'直接选中'的能力…不受这些仅在选择时生效的异常效果制约") and
|
||||
the blasts of flying units credited to the ground 胄 (刺胄之弹, 斩胄之剑 / 破胄之锤 — whose 掷剑 / 掷锤 pick "(无视无法选择)");
|
||||
auras of ground enemies still apply [ASSUMED]);
|
||||
still reach it (`ignoreSelect`): abilities PRTS marks "无视无法选择" (【污染秽蚀】, 假想敌:铳's 【盲信之誓】, 萨卡兹悖谬
|
||||
暴虐兵长's 暴击 splash), direct picks (碎铳之簧's 法术护盾 counter on its attacker — PRTS 异常效果 "'直接选中'的能力…不受这些
|
||||
仅在选择时生效的异常效果制约"), the blasts of flying units credited to the ground 胄 (刺胄之弹, 斩胄之剑 / 破胄之锤 — whose
|
||||
掷剑 / 掷锤 pick "(无视无法选择)") and the ticks of a debuff a ground enemy put on it before it took off (出血, 沙狱,
|
||||
burning DoTs, 淤困, 【自然涌动】 — a tick selects nobody); auras of ground enemies still apply [ASSUMED]);
|
||||
a stealthed ally (隐匿, 排气格栅) only for the enemy it blocks — our operators keep 隐匿 while blocking (PRTS 作战机制
|
||||
§隐匿; 索敌的概念: a blocked enemy "强行无视对方可选性" attacks its blocker); a camouflaged one (迷彩, flag `camou`: ba.camou
|
||||
"不阻挡时不成为敌方普通攻击的目标") likewise (PRTS 异常效果: neither anomaly is "阻挡时解除") [ASSUMED: enemy skills and
|
||||
@@ -384,7 +385,9 @@ A `rangeExtend` on a `persist` never-expiring buff is **permanent**: it also wid
|
||||
untargetable blockFly noMove noSp burstLock hidden attract float noDisplace isolated camou liftoff` (`float` = 近地悬浮 (an air
|
||||
unit, `Unit.isFlying`), `liftoff` = an ally's 起飞 (蒂比's skills; gamedata_const ba.liftoff "不阻挡地面敌人且不会被地面敌人攻击,
|
||||
可以阻挡飞行敌人"): it blocks no ground enemy (`Battle._blockerFor`) and has 对地规避 — no ground enemy (not `isFlying`)
|
||||
selects it, damages it or puts a status on it (`targeting.js evadesGround`); flyers, 近地悬浮 and 浮空 enemies still do,
|
||||
selects it, so no selected damage or status of one lands on it (`targeting.js evadesGround`), while what selects nobody
|
||||
still does (`ignoreSelect` / 无来源: 无视无法选择 abilities, direct picks, flying units' blasts, a debuff's ticks); flyers,
|
||||
近地悬浮 and 浮空 enemies still select it,
|
||||
with no 对空 check, since it stays a ground unit on its tile (`unit.ground` unchanged; PRTS 行动方式 "起飞的干员仍然是地面单位"),
|
||||
`noDisplace` = 失衡免疫 (`displace()` moves nothing), `isolated` = 孤立 ("无法被同阵营选中": no ally
|
||||
ability selects it — no heal, buff, aura or talent pick from another ally (`battle.allySelectable` / `alliesFor`,
|
||||
@@ -490,7 +493,7 @@ HP left is refused (damage.js `hasHp`: a lethal hit's `damaged` hook runs before
|
||||
element?, defIgnoreFlat, defIgnorePct, resIgnoreFlat, resIgnorePct, mul=1, canDodge (phys/arts), isSkill, isSplash,
|
||||
isAttack, attackId, ignoreSleep, ignoreSelect, sourceless, tags[], cancel }` (`ignoreSelect`: no selection 无法选择
|
||||
effects stop — an ability "无视无法选择", a direct pick such as a counter on the attacker, a flying unit's blast credited to
|
||||
a ground leader — it reaches an airborne 起飞 ally whatever its source; `sourceless`: 无来源 damage — the source's stats add
|
||||
a ground leader, the tick of a debuff already on the unit — it reaches an airborne 起飞 ally whatever its source; `sourceless`: 无来源 damage — the source's stats add
|
||||
nothing and the hooks get `source: null` plus `credit` = the source, which keeps the stats and the kill; a `loseHp` whose
|
||||
`from` is 无来源 is 无来源 too; element bursts, leader-part transfers and 坚守 thorns use it — content damage that has a
|
||||
responsible unit should pass it as `source` with `sourceless: true` rather than `source: null`, which credits nobody)
|
||||
@@ -500,8 +503,9 @@ for "本次攻击" procs that must roll once per attack. **Dodge** from several
|
||||
`s.dodgePhys` = 1 − Π(1 − pᵢ) (a single source keeps its exact value).
|
||||
Order: invulnerable / asleep / 对地规避? (a ground enemy's damage or element fill on an airborne 起飞 ally — flag `liftoff`,
|
||||
`targeting.js evadesGround` — is refused unless `ignoreSelect` or 无来源: it cannot select her, so its splash and area
|
||||
abilities skip her and a shot in flight or a DoT ticking when she took off lands on nothing [ASSUMED for the last two:
|
||||
PRTS 伤害流程 7 "取消掉隐匿/无敌状态下的攻击" read for 对地规避]; checked before `hit` only — 蒂比 S2 takes off inside the
|
||||
abilities skip her and a shot in flight when she took off lands on nothing [ASSUMED: PRTS 伤害流程 7 "取消掉隐匿/无敌状态
|
||||
下的攻击" read for 对地规避]; the ticks of a debuff already on her select nobody and land (content passes `ignoreSelect`;
|
||||
PRTS 异常效果: 无法选择 effects "仅在选择时生效"); checked before `hit` only — 蒂比 S2 takes off inside the
|
||||
`hit` of the attack that set it off, which resolves as usual) → **`hit`** (mutate `dmg`, set `dmg.cancel`) → dodge
|
||||
(`rng()`) → mitigation (phys
|
||||
`max(A − max(0, D×(1−defIgnorePct) − defIgnoreFlat), 5 %A)`, arts `max(A×(1 − R′/100), 5 %A)`, elemental
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
// Every leader (tag boss) ignores 侵蚀 gauge damage ("最终攻势中,敌方领袖不会受到侵蚀损伤").
|
||||
// An airborne (起飞) operator is no selection of a ground leader or part (对地规避: canTargetAlly, the damage pipeline,
|
||||
// the 碎铳之簧 bounce); still reach it (`ignoreSelect`): the 刺胄之弹 / 剑 / 锤 blasts (flying units, 无来源 DoT; 掷剑 /
|
||||
// 掷锤 pick their operator "(无视无法选择)"), the 盲信之誓 chains ("无视无法选择") and the 法术护盾 counter on its attacker
|
||||
// (a direct pick).
|
||||
// 掷锤 pick their operator "(无视无法选择)"), the 盲信之誓 chains ("无视无法选择"), the 法术护盾 counter on its attacker
|
||||
// (a direct pick) and the ticks of a debuff already on it (【自然涌动】: a tick selects nobody).
|
||||
// LP effects ('lpLoss' hook + result.lpLoss) must be applied by the match (see the report of this module's owner).
|
||||
// fx kinds: 'beam' 'shell' 'explode' 'telegraph' 'charge' 'link' 'dash' 'column' 'tide' 'rockfall' 'tentacle' 'equip'
|
||||
// 'sword' 'vest' 'blink' 'summon' 'grow' 'phase' 'lpLoss' (x, y + extra {id, r, tiles, kind, tx, ty …}).
|
||||
@@ -1076,7 +1076,7 @@ function kitDeer(ab, e) {
|
||||
b.fx('beam', { x: e2.x, y: e2.y, from: e2.id, to: t.id, kind: 'naturalSurge', dur });
|
||||
b.applyStatus(t, 'stun', { duration: dur, source: e2 });
|
||||
b.addBuff(t, { key: 'boss:surge', duration: dur, interval: 1, visible: true,
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e2, unit, { amount: e2.s.atk * (lasso.bb.atk_scale ?? 0), type: 'arts', canDodge: false, tags: ['enemyAbility'] }) });
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e2, unit, { amount: e2.s.atk * (lasso.bb.atk_scale ?? 0), type: 'arts', canDodge: false, ignoreSelect: true, tags: ['enemyAbility'] }) });
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
// ground enemy (对地规避, targeting.js evadesGround): targetsNear / allTargets skip it through canTargetAlly, the picks
|
||||
// and one-shot areas that bypass it (周围四格 additions, chain and bounce jumps, shells, barrages, 沙狱, death blasts)
|
||||
// filter it, and the engine refuses a ground enemy's damage and statuses on it. Not selections, so they still reach
|
||||
// it (`ignoreSelect`): abilities PRTS marks "无视无法选择" (【污染秽蚀】, 【盲信之誓】), direct picks of the attacker
|
||||
// (碎铳之簧's counter — PRTS 异常效果 "'直接选中'的能力…不受这些仅在选择时生效的异常效果制约") and the blasts of flying
|
||||
// units credited to a ground leader (刺胄之弹, 斩胄之剑 / 破胄之锤). Auras of ground enemies still apply [ASSUMED].
|
||||
// it (`ignoreSelect`): abilities PRTS marks "无视无法选择" (【污染秽蚀】, 【盲信之誓】, 萨卡兹悖谬暴虐兵长's 暴击 splash),
|
||||
// direct picks of the attacker (碎铳之簧's counter — PRTS 异常效果 "'直接选中'的能力…不受这些仅在选择时生效的异常效果
|
||||
// 制约"), the blasts of flying units credited to a ground leader (刺胄之弹, 斩胄之剑 / 破胄之锤) and the ticks of a debuff
|
||||
// already on it (出血, 沙狱, “庞贝”'s burning, 淤困, 【自然涌动】 — a tick selects nobody). Auras of ground enemies still
|
||||
// apply [ASSUMED].
|
||||
//
|
||||
// Special types (factions.json):
|
||||
// FLY — engine (FLY motion, ranged-only targeting). Flyer kits below (御4, 护障, 寒霜, 萨科塔之翼/眼, 黑云 …).
|
||||
@@ -889,7 +891,10 @@ function statue(ab) {
|
||||
};
|
||||
}
|
||||
|
||||
/** Bleeding on hit (逐腐兽): arts damage per second, removed by healing. */
|
||||
/**
|
||||
* Bleeding on hit (逐腐兽): arts damage per second, removed by healing. A tick selects nobody (`ignoreSelect`): it keeps
|
||||
* hurting an ally that took off (起飞) after the bleed landed; so do the other enemy debuff DoTs (沙狱, burnDot, 淤困).
|
||||
*/
|
||||
function bleed(ab) {
|
||||
const dmg = T(ab, 'Bleeding.attack@bleeding_damage') ?? 0, dur = T(ab, 'Bleeding.attack@duration') ?? 0;
|
||||
return {
|
||||
@@ -897,7 +902,7 @@ function bleed(ab) {
|
||||
if (!(dmg > 0 && dur > 0)) return;
|
||||
b.addBuff(c.target, {
|
||||
key: 'ab:bleed', duration: dur, refresh: 'replace', interval: 1, visible: true,
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e, unit, { amount: dmg, type: 'arts', canDodge: false, tags: ['enemyAbility', 'bleed'] }),
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e, unit, { amount: dmg, type: 'arts', canDodge: false, ignoreSelect: true, tags: ['enemyAbility', 'bleed'] }),
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1321,6 +1326,11 @@ function kitRush(ab) {
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* 萨卡兹悖谬暴虐兵长: blocked only by a blocker with ≥ 3 free block; its first hit also strikes the units around its target
|
||||
* (PRTS 技能0 暴击 "对目标和周围4格的我方单位造成攻击力150%的物理普通伤害(无视无法选择,无视迷彩)※此技能仅能触发一次"):
|
||||
* the splash ignores 无法选择, so it reaches an airborne 起飞 ally too (`ignoreSelect`).
|
||||
*/
|
||||
function kitFirstAoe(ab) {
|
||||
const scale = T(ab, 'AOEAttack.atk_scale') ?? 0;
|
||||
return [blockWeight(3), {
|
||||
@@ -1328,7 +1338,9 @@ function kitFirstAoe(ab) {
|
||||
if (a.done) return;
|
||||
a.done = true;
|
||||
b.fx('explode', { x: c.target.x, y: c.target.y, r: 1, kind: 'aoeAttack' });
|
||||
for (const u of b.alliesInRadius(c.target.x, c.target.y, 1)) if (u !== c.target) hurt(b, e, u, e.s.atk * scale, 'phys');
|
||||
for (const u of b.alliesInRadius(c.target.x, c.target.y, 1)) {
|
||||
if (u !== c.target) hurt(b, e, u, e.s.atk * scale, 'phys', { ignoreSelect: true, tags: ['aoeAttack'] });
|
||||
}
|
||||
},
|
||||
}];
|
||||
}
|
||||
@@ -1382,7 +1394,7 @@ function kitParasite(ab) {
|
||||
b.addBuff(host, {
|
||||
// "受到的元素损伤提高至130%": a 元素损伤 multiplier on the element hit (ensureParasiteHook), not 元素伤害
|
||||
key: 'ab:parasite', visible: true, interval: 1, data: { src: e, spread: PARASITE_SPREAD, epMul: elMul },
|
||||
onTick: ({ battle, unit }) => { if (e.alive) battle.dealDamage(e, unit, { amount: e.s.atk * scale, type: 'arts', canDodge: false, tags: ['enemyAbility', 'parasite'] }); },
|
||||
onTick: ({ battle, unit }) => { if (e.alive) battle.dealDamage(e, unit, { amount: e.s.atk * scale, type: 'arts', canDodge: false, ignoreSelect: true, tags: ['enemyAbility', 'parasite'] }); },
|
||||
});
|
||||
},
|
||||
death(c, b, e, a) { if (a.host) b.removeBuff(a.host, 'ab:parasite'); },
|
||||
@@ -1618,7 +1630,7 @@ function kitLeaderMisc(key, ab, e) {
|
||||
dealt(c, b, e2) {
|
||||
if (!(dot.dur > 0)) return;
|
||||
b.addBuff(c.target, { key: 'ab:burnDot', duration: dot.dur, refresh: 'replace', interval: dot.iv, visible: true,
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e2, unit, { amount: dot.dmg, type: 'arts', canDodge: false, tags: ['enemyAbility'] }) });
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e2, unit, { amount: dot.dmg, type: 'arts', canDodge: false, ignoreSelect: true, tags: ['enemyAbility'] }) });
|
||||
},
|
||||
}, {
|
||||
iv: T(ab, 'rangedamage.interval') ?? 10,
|
||||
@@ -1677,7 +1689,7 @@ function kitLeaderMisc(key, ab, e) {
|
||||
for (const u of b.alliesInRadius(x, y, SANDSTORM_RADIUS)) {
|
||||
if (evadesGround(e2, u)) continue;
|
||||
b.addBuff(u, { key: 'ab:sandStorm', duration: dur, refresh: 'replace', interval: 1, visible: true, mods: { atkPct: ss.bb.atk ?? 0 },
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e2, unit, { amount: ss.bb.damage ?? 0, type: 'arts', canDodge: false, tags: ['enemyAbility', 'sandStorm'] }) });
|
||||
onTick: ({ battle, unit }) => battle.dealDamage(e2, unit, { amount: ss.bb.damage ?? 0, type: 'arts', canDodge: false, ignoreSelect: true, tags: ['enemyAbility', 'sandStorm'] }) });
|
||||
}
|
||||
}, { cond: (b, e2) => allTargets(b, e2).length > 0 }),
|
||||
];
|
||||
|
||||
@@ -37,8 +37,9 @@ function onDefaultSkill(chess) {
|
||||
|
||||
/**
|
||||
* 起飞 of 蒂比's skills (gamedata_const ba.liftoff "不阻挡地面敌人且不会被地面敌人攻击,可以阻挡飞行敌人"): the skill's flags
|
||||
* `liftoff` (no ground enemy blocked — Battle._blockerFor; 对地规避 — targeting.js evadesGround: no ground enemy selects or
|
||||
* damages her) and `blockFly` (blocks flyers at the air radius). She stays a ground unit on her tile (PRTS 行动方式 "起飞的
|
||||
* `liftoff` (no ground enemy blocked — Battle._blockerFor; 对地规避 — targeting.js evadesGround: no ground enemy selects
|
||||
* her, while what selects nobody still lands — 无视无法选择 abilities, direct picks, flying units' blasts, 无来源 damage, the
|
||||
* ticks of a debuff already on her) and `blockFly` (blocks flyers at the air radius). She stays a ground unit on her tile (PRTS 行动方式 "起飞的
|
||||
* 干员仍然是地面单位": `unit.ground` unchanged — 隐德来希's 血镰, 地面干员 bonds / items still count her).
|
||||
*/
|
||||
const LIFTOFF_FLAGS = Object.freeze({ blockFly: true, liftoff: true });
|
||||
@@ -597,7 +598,7 @@ export default {
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 2_13 蒂比 紧急赶场通知 (AUTO): "受到攻击后触发" — any incoming enemy attack sets it off (the kit is the only
|
||||
// trigger: the engine rule is disabled so non-attack damage never fires it) and a physical/arts one is dodged; takes
|
||||
// off for the duration (起飞, LIFTOFF_FLAGS: she blocks no ground enemy and none attacks or damages her): skill range,
|
||||
// off for the duration (起飞, LIFTOFF_FLAGS: she blocks no ground enemy and none selects her — 对地规避): skill range,
|
||||
// ATK +atk, attacks become 3 shots, blocks flying (not ground) enemies. Trait "起飞后能够阻挡2个飞行敌人": flying enemies
|
||||
// are blocked only while airborne. 片场工作指南: if not attacked for stack_time s, the next physical/arts attack is
|
||||
// dodged (prob); every attack restarts that timer.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// server/sim/damage.js — damage & heal pipeline, shields, dodge, element gauges (DESIGN §5.5).
|
||||
//
|
||||
// dealDamage order: (element → gauge path) | invulnerable? / 对地规避 (a ground enemy's damage to an airborne 起飞 ally —
|
||||
// targeting.js evadesGround; not `ignoreSelect` damage) → 'hit' hook (mutable DamageInfo, may set cancel)
|
||||
// targeting.js evadesGround; not `ignoreSelect` damage: no selection, e.g. a debuff's tick) → 'hit' hook (mutable DamageInfo, may set cancel)
|
||||
// → dodge (phys/arts, canDodge) → mitigation (phys: DEF, arts: RES, true: none)
|
||||
// → × source dmgDealtMul (× phys/artsDealtMul) × target dmgTakenMul (not for 元素伤害) × type-taken mul × dmg.mul
|
||||
// → 限伤 (leaders in boss / hidden battles: a hit of ceil(final) ≥ BOSS_HIT_LIMIT is cancelled, see leaderHitCancelled)
|
||||
@@ -85,7 +85,8 @@ export function makeDamageInfo(d = {}) {
|
||||
noSp: !!d.noSp,
|
||||
ignoreSleep: !!d.ignoreSleep,
|
||||
// no selection 无法选择 effects stop (an ability that "无视无法选择" such as PRTS 【污染秽蚀】, a direct pick, a flying
|
||||
// unit's blast credited to a ground leader): reaches an airborne 起飞 ally whatever the source's 行动方式
|
||||
// unit's blast credited to a ground leader, the tick of a debuff already on the unit): reaches an airborne 起飞 ally
|
||||
// whatever the source's 行动方式
|
||||
ignoreSelect: !!d.ignoreSelect,
|
||||
sourceless: !!d.sourceless,
|
||||
attackId: d.attackId ?? 0,
|
||||
@@ -100,10 +101,11 @@ function sleepBlocks(target, source, dmg) {
|
||||
/**
|
||||
* 起飞 (flag `liftoff`): a ground enemy's damage never reaches the airborne ally — it cannot select it (对地规避, PRTS 作战机制
|
||||
* "AOE的判定是对攻击范围内的每个可以被选中的敌人进行判定"), so its splash, area abilities and element fills skip it, and a shot
|
||||
* already in flight or a DoT ticking when it took off lands on nothing [ASSUMED: PRTS 伤害流程 7 "取消掉隐匿/无敌状态下的攻击"
|
||||
* read for 对地规避]. Checked before the `hit` hook only: 蒂比 S2 takes off inside the hook of the hit that set it off, which
|
||||
* then resolves as usual (dodged if physical / arts — PRTS 备注). Sourceless damage and `ignoreSelect` damage (no
|
||||
* selection: 无视无法选择, direct picks, flying units' blasts — targeting.js evadesGround) still land.
|
||||
* already in flight when it took off lands on nothing [ASSUMED: PRTS 伤害流程 7 "取消掉隐匿/无敌状态下的攻击" read for
|
||||
* 对地规避]. Checked before the `hit` hook only: 蒂比 S2 takes off inside the hook of the hit that set it off, which then
|
||||
* resolves as usual (dodged if physical / arts — PRTS 备注). Sourceless damage and `ignoreSelect` damage still land: no
|
||||
* selection (targeting.js evadesGround) — 无视无法选择 abilities, direct picks, flying units' blasts, and the ticks of a
|
||||
* debuff already on it (PRTS 异常效果: 无法选择 effects "仅在选择时生效"; the debuff's mods stay too).
|
||||
*/
|
||||
function liftoffEvades(target, source, dmg) {
|
||||
return !!target.s.flags.liftoff && !dmg.ignoreSelect && evadesGround(source, target);
|
||||
|
||||
@@ -90,9 +90,11 @@ export function canTargetAlly(e, a, ranged) {
|
||||
* pipeline and Battle.applyStatus (a ground enemy's area damage, statuses and hits under way skip it — PRTS 作战机制 "AOE
|
||||
* 的判定是对攻击范围内的每个可以被选中的敌人进行判定"), and the content picks and one-shot areas that bypass canTargetAlly
|
||||
* (chain / bounce jumps, 周围四格 additions, shells, barrages, 沙狱, death blasts). Not selections, so they still reach it:
|
||||
* sourceless damage and DamageInfo / applyStatus `ignoreSelect` — abilities that "无视无法选择" (【污染秽蚀】, 【盲信之誓】),
|
||||
* direct picks (碎铳之簧's counter on its attacker: PRTS 异常效果 "'直接选中'的能力…不受这些仅在选择时生效的异常效果制约")
|
||||
* and the blasts of flying units credited to a ground leader (刺胄之弹, 斩胄之剑 / 破胄之锤).
|
||||
* sourceless damage and DamageInfo / applyStatus `ignoreSelect` — abilities that "无视无法选择" (【污染秽蚀】, 【盲信之誓】,
|
||||
* 萨卡兹悖谬暴虐兵长's 暴击 splash), direct picks (碎铳之簧's counter on its attacker: PRTS 异常效果 "'直接选中'的能力…不受这些
|
||||
* 仅在选择时生效的异常效果制约"), the blasts of flying units credited to a ground leader (刺胄之弹, 斩胄之剑 / 破胄之锤) and
|
||||
* the ticks of a debuff a ground enemy put on it before it took off (出血, 沙狱, burning DoTs, 【自然涌动】: a tick selects
|
||||
* nobody). A shot already in flight is refused [ASSUMED] (damage.js).
|
||||
*/
|
||||
export function evadesGround(src, a) {
|
||||
return !!(a && a.s && a.s.flags.liftoff && src && src.side === 'enemy' && !src.isFlying);
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
// the targets its abilities add (控潮术师's 周围四格, chain / bounce jumps) and its area damage and statuses skip her;
|
||||
// - flyers, 近地悬浮 and 浮空 enemies still select her (she stays a ground unit: no 对空 check);
|
||||
// - what is no selection still reaches her: sourceless damage, abilities that "无视无法选择" (【污染秽蚀】 at the
|
||||
// low-ground rate — she is still on her low tile; 【盲信之誓】), direct picks (碎铳之簧's counter on its attacker —
|
||||
// PRTS 异常效果 "'直接选中'的能力…不受这些仅在选择时生效的异常效果制约") and flying units' blasts (刺胄之弹, 斩胄之剑);
|
||||
// low-ground rate — she is still on her low tile; 【盲信之誓】; 萨卡兹悖谬暴虐兵长's 暴击 splash), direct picks
|
||||
// (碎铳之簧's counter on its attacker — PRTS 异常效果 "'直接选中'的能力…不受这些仅在选择时生效的异常效果制约"), flying
|
||||
// units' blasts (刺胄之弹, 斩胄之剑) and the ticks of a debuff put on her before she took off (出血, 沙狱, burning);
|
||||
// a ground enemy's zone does not (集团军重型火炮 【燃烧区域】 "碰撞不受迷彩制约,不可对空": 迷彩 only);
|
||||
// - she stays a 地面单位 for ally rules (隐德来希 S2 puts a 血镰 on her; PRTS 备注 "被添加血镰的单位处于起飞时,血镰可对空").
|
||||
// Cause: 起飞 was `unit.ground = false` (an operator on a high tile): melee enemies could not reach her (she released them),
|
||||
// but every ranged ground enemy kept shooting her and the AoE / skills of ground enemies hit her.
|
||||
// [ASSUMED] a ground enemy's damage already under way (a shot in flight, a DoT ticking) is cancelled too (PRTS 作战机制 伤害
|
||||
// 流程 7 "取消掉隐匿/无敌状态下的攻击", read for 对地规避); auras of ground enemies (光环, field-wide debuffs) still apply.
|
||||
// [ASSUMED] a ground enemy's shot already in flight is cancelled too (PRTS 作战机制 伤害流程 7 "取消掉隐匿/无敌状态下的攻击",
|
||||
// read for 对地规避); auras of ground enemies (光环, field-wide debuffs) still apply.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
@@ -358,3 +359,65 @@ test('F3 review: one-shot area abilities of ground enemies skip an airborne 蒂
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// Second review round: 萨卡兹悖谬暴虐兵长's 暴击 splash ("无视无法选择") and the ticks of debuffs already on her.
|
||||
|
||||
const WDRRL = 'enemy_1320_wdrrl_2'; // 萨卡兹悖谬暴虐兵长 (ground, block ≥ 3): 暴击 "对目标和周围4格…(无视无法选择,无视迷彩)"
|
||||
const NHSTLK = 'enemy_1270_nhstlk'; // 逐腐兽 (ground melee): 出血 on hit — arts per second
|
||||
const LSLIME = 'enemy_1050_lslime'; // “庞贝” (ground): its hits leave a burning DoT
|
||||
|
||||
test('F3 review 2: 萨卡兹悖谬暴虐兵长\'s 暴击 splash ("无视无法选择") reaches an airborne 蒂比 next to its blocker', REAL, () => {
|
||||
const h = makeBattle({
|
||||
stageId: 'act2autochess_m02', seed: 3, autoFinish: false, timeLimit: 400, hooks: ['damaged'], captureNoisy: true,
|
||||
units: [{ chessId: TIPPI[0], row: 9, col: 5, carryState: { sp: 999 }, skillIndex: skillIndex(TIPPI[0], S1) }, { chessId: BAIT, row: 9, col: 4 }],
|
||||
enemies: [{ key: WDRRL, route: { motion: 'WALK', start: [9, 8], end: [9, 1], checkpoints: [] }, mods: { hpMul: 1e3, atkMul: 0.3 } }],
|
||||
});
|
||||
const u = h.unit(TIPPI[0]), bait = h.unit(BAIT);
|
||||
let e = null, airAtBlock = null;
|
||||
for (let i = 0; i < 14 * 30; i++) { // it reaches 角峰 at ≈ 9 s, S1 lasts 32 s
|
||||
h.step();
|
||||
e ??= h.b.enemies.find((x) => x.defId === WDRRL) ?? null;
|
||||
if (e && airAtBlock == null && e.blockedBy) airAtBlock = !!u.s.flags.liftoff;
|
||||
}
|
||||
assert.ok(e && e.blockedBy === bait, '角峰 (block 3) blocks it; she cannot (block < 3)');
|
||||
assert.equal(airAtBlock, true, 'she is airborne when it reaches 角峰');
|
||||
const splash = h.hooksOf('damaged').filter((c) => c.target === u && c.source === e);
|
||||
assert.equal(splash.length, 1, `its one-off splash lands on her: ${splash.map((c) => Math.round(c.amount))}`);
|
||||
assert.ok(splash[0].amount > 0 && !splash[0].dmg.isAttack && splash[0].dmg.ignoreSelect);
|
||||
assert.ok(u.s.flags.liftoff, 'while airborne');
|
||||
assert.ok(h.hooksOf('damaged').some((c) => c.target === bait && c.source === e && c.dmg.isAttack), 'its attack itself is on 角峰');
|
||||
done(h);
|
||||
});
|
||||
|
||||
test('F3 review 2: a debuff a ground enemy put on 蒂比 before she took off keeps ticking (no selection); its new attacks still skip her', REAL, () => {
|
||||
// 鼠王 【沙狱】 (centred near 角峰 and her), 逐腐兽's 出血, “庞贝”'s burning DoT: attached on the ground, then S1
|
||||
const cases = [
|
||||
{ key: MOUSEK, pos: [9, 8], buff: 'ab:sandStorm', bait: true, fire: (h, m) => { m.mem.ab.list.find((a) => a && a.cd === m.mem.ab.sk.SandStorm.cd && a.fire).left = 0.05; } },
|
||||
{ key: NHSTLK, pos: [9, 5.3], buff: 'ab:bleed' },
|
||||
{ key: LSLIME, pos: [9, 7], buff: 'ab:burnDot' },
|
||||
];
|
||||
for (const k of cases) {
|
||||
const units = [{ chessId: TIPPI[0], row: 9, col: 5, carryState: { sp: 0 }, skillIndex: skillIndex(TIPPI[0], S1) }];
|
||||
if (k.bait) units.push({ chessId: BAIT, row: 10, col: 5 });
|
||||
const h = makeBattle({
|
||||
stageId: 'act2autochess_m02', seed: 3, autoFinish: false, timeLimit: 400, hooks: ['damaged'], captureNoisy: true, units,
|
||||
enemies: [{ key: k.key, pos: k.pos, mods: { hpMul: 1e4, speedMul: 0, atkMul: k.bait ? 0.01 : 0.05 } }, { key: LAZERD, pos: [9, 7.2], mods: { hpMul: 1e4, speedMul: 0, atkMul: 0 } }],
|
||||
});
|
||||
const u = h.unit(TIPPI[0]);
|
||||
h.step();
|
||||
const e = h.b.enemies.find((x) => x.defId === k.key);
|
||||
k.fire?.(h, e);
|
||||
assert.ok(h.runUntil(() => u.findBuff(k.buff), 10), `${k.key}: ${k.buff} on her`);
|
||||
assert.ok(!u.s.flags.liftoff, `${k.key}: before she takes off`);
|
||||
u.skill.gainSp(999, 'init');
|
||||
assert.ok(h.runUntil(() => u.s.flags.liftoff, 3), `${k.key}: takes off`);
|
||||
const up = h.b.time;
|
||||
h.run(4);
|
||||
assert.ok(u.s.flags.liftoff && u.findBuff(k.buff), `${k.key}: still airborne with the debuff`);
|
||||
const air = h.hooksOf('damaged').filter((c) => c.target === u && c.source === e && tAt(c) > up + 1e-6);
|
||||
assert.ok(air.filter((c) => !c.dmg.isAttack).length >= 3, `${k.key}: its ticks land while airborne: ${air.length}`);
|
||||
assert.deepEqual(air.filter((c) => c.dmg.isAttack).map((c) => Math.round(c.amount)), [], `${k.key}: no new attack on her`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user