feedback2 WJ: 蚀裂's 毒雾 and the buff auras of 深池伙友卫队 / 扎罗 keep a 隐匿 operator out (auraSelectable: 起飞 still reached); a locked target (死亡之眼's burst, C4, 爆炸箭, 碎骨's DEF down, 灼藤's burn) is hit as a direct pick

This commit is contained in:
sganggs committed 2026-10-04 00:36:32 +08:00
1 parent 20a433f2a9
commit 852c9d5bcf
2 files changed
+83 -40

No files matched your search

+53 -26
View File
@@ -37,17 +37,21 @@
// pulses, the zones an enemy leaves, chain / bounce jumps, 周围四格 additions, whole-column / whole-field skills —
// select with areaAllies / areaAlliesInTiles / fieldAllies (targeting.js areaSelectable; PRTS 作战机制 §AOE伤害判定
// "AOE的判定是对攻击范围内的每个可以被选中的敌人进行判定"): no 隐匿 ally that does not block the enemy, no untargetable
// or sleeping one, no airborne 起飞 one for a ground enemy — 迷彩 does not protect from them (ba.camou "无法躲避溅射类
// 攻击"). Until 0.1.2 they took every ally in the area (GitHub issue #32 item 6; DESIGN §22.12). An airborne ally
// or sleeping one, no airborne 起飞 one for a ground enemy; 迷彩 is not checked (splash-type, 中点判定 / 格子判定 and
// aura effects ignore it — ba.camou "无法躲避溅射类攻击", PRTS 异常效果 迷彩; the sites with no such reading are
// [ASSUMED] in DESIGN §22.12). Buff auras select with auraAllies (auraSelectable: 隐匿 kept out, 起飞 not). A locked
// target (an attack's, a channel's, a C4's) is hit as a direct pick, only the others around it as an area
// (targetAndArea). Until 0.1.2 they took every ally in the area (GitHub issue #32 item 6; DESIGN §22.12). An airborne ally
// (起飞, flag `liftoff`) is no target of a ground enemy (对地规避, targeting.js evadesGround): targetsNear / allTargets
// skip it through canTargetAlly, the area selectors through areaSelectable, and the engine refuses a ground enemy's
// damage and statuses on it. Not selections, so they still reach it (`ignoreSelect`): abilities PRTS marks "无视无法
// 选择 / 无视(目标)可选性" (【污染秽蚀】, 【盲信之誓】, 萨卡兹悖谬暴虐兵长's 暴击 splash, 假想敌:淤困's burst spread),
// 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). The buff auras of enemies
// (寒霜 — PRTS: its debuff ignores 隐匿 —, 深池伙友卫队, 扎罗's 远古威慑), map / tile effects (“墓碑”, 【国度】, the
// chimera's 源石污染区) and the sourceless 毒雾 of 假想敌:蚀裂 still reach every ally [ASSUMED for all but 寒霜 and “墓碑”].
// already on it (出血, 沙狱, “庞贝”'s burning, 淤困, 【自然涌动】 — a tick selects nobody). Ground enemies' buff auras
// (深池伙友卫队, 扎罗's 远古威慑) and the sourceless 毒雾 of 假想敌:蚀裂 still reach it [ASSUMED, §21.22] while keeping
// a 隐匿 ally out; 寒霜's aura (PRTS: its debuff "无视隐匿状态起作用") and map / tile effects (“墓碑” — PRTS: a map effect —,
// 【国度】, the chimera's 源石污染区 [ASSUMED]) reach every ally.
//
// Special types (factions.json):
// FLY — engine (FLY motion, ranged-only targeting). Flyer kits below (御4, 护障, 寒霜, 萨科塔之翼/眼, 黑云 …).
@@ -60,8 +64,8 @@
// DOT — damage zones (污染秽蚀: true damage, one tick per second however many cover a unit, "可对空,无视无法选择"
// — `ignoreSelect`, 起飞 / 隐匿 allies included; 燃烧区域: the enemy's area selection — PRTS 集团军重型火炮
// "碰撞不受迷彩制约,不可对空", no 无视无法选择 —, so no 起飞 or unblocking 隐匿 ally; 假想敌:蚀裂's sourceless
// 毒雾 reaches everyone inside), bleeding (removed by healing), pulsing damage around an enemy (area
// selection).
// 毒雾 skips a 隐匿 ally, not a 起飞 one), bleeding (removed by healing), pulsing damage around an enemy
// (area selection).
// INVISIBLE — permanent `stealth` flag (engine: untargetable unless blocked or revealed). An operator's radius area
// damage skips an unblocked one too (Battle.foesInRadius: profession splash around a struck target,
// skill circles — PRTS 作战机制 §AOE伤害判定 "对攻击范围内的每个可以被选中的敌人进行判定"; until 0.1.1 it
@@ -87,7 +91,7 @@
// Custom hook: 'lpLoss' {amount, reason, source} — leader "扣除目标生命" effects; also summed into result.lpLoss.
import { TICK, MOVE_SCALE, ELEMENT, ATTACK_PAUSE, PROJECTILE_SPEEDS, ALLY_COLLIDER_RADIUS } from '../constants.js';
import { canTargetAlly, sortAllyTargets, aggroCmp, areaSelectable } from '../targeting.js';
import { canTargetAlly, sortAllyTargets, aggroCmp, areaSelectable, auraSelectable } from '../targeting.js';
import { mitigate, periodicDamage } from '../damage.js';
// ---------------------------------------------------------------------------------------------------------------
@@ -440,13 +444,31 @@ export function fieldAllies(b, src) {
return b.allies().filter((a) => areaSelectable(src, a));
}
/**
* The allies within `r` of (x, y) a buff aura of enemy `src` takes (targeting.js auraSelectable: no unblocking 隐匿,
* untargetable or sleeping ally — PRTS "隐匿状态下的单位一般无法被敌方的…Buff选择器选中"; an airborne 起飞 ally is still
* taken [ASSUMED, §21.22]). 寒霜's aura, which PRTS says ignores 隐匿, keeps b.alliesInRadius (allyAura).
*/
export function auraAllies(b, src, x, y, r) {
return b.alliesInRadius(x, y, r).filter((a) => auraSelectable(src, a));
}
/**
* A locked target `t` and the allies an area takes around it: the target was picked directly when the ability started
* (an attack's target, a channel's or a C4's lock), so a 隐匿 it gains meanwhile does not save it — PRTS 异常效果 "'直接选中'
* 的能力不会进行具体的目标选择,故同样不受这些仅在选择时生效的异常效果制约"; only the others are an area selection (`area`).
*/
export function targetAndArea(t, area) {
return t && t.alive && t.deployed ? [t, ...area.filter((u) => u !== t)] : area;
}
/** Engine priority (blocker → taunt → latest deployed) on a copy. */
export function byPriority(e, list) { return sortAllyTargets(e, list.slice()); }
/**
* An HP-loss / damage zone. `tick(units)` runs every `iv` s for `life` s on the allies inside: `pick(x, y, r)` chooses
* them — by default every ally in the circle (【污染秽蚀】 "无视无法选择", a sourceless 毒雾); a zone an enemy's ability
* leaves passes its area selection (areaAllies).
* them — by default every ally in the circle (【污染秽蚀】 "无视无法选择"); the other zones (dmgZone, 烹泉's steam) pass
* their area selection (areaAllies).
*/
export function zone(b, { x, y, r, life, iv = 1, kind = 'zone', tick, pick = null }) {
b.fx('zone', { x, y, r, dur: life, kind });
@@ -1052,14 +1074,14 @@ function pollution(b, src, x, y, r, life, low, high) {
}
/**
* Damage zone (`amount` per tick, tagged `kind`). Unlike 【污染秽蚀】 it does not ignore 无法选择: an enemy's zone ticks on
* the allies its area selection takes (areaAllies: no unblocking 隐匿 ally, no airborne 起飞 one for a ground enemy —
* PRTS 集团军重型火炮 【燃烧区域】 "碰撞不受迷彩制约,不可对空": 迷彩 only). A sourceless zone (`src` null: 假想敌:蚀裂's
* 毒雾) reaches everyone inside [ASSUMED: PRTS 作战机制 "可以受到无来源的毒雾伤害" names the stage hazard; PRTS 假想敌:蚀裂
* gives its cloud no 无视可选性 either].
* Damage zone (`amount` per tick, tagged `kind`). Unlike 【污染秽蚀】 it does not ignore 无法选择: it ticks on the allies
* its area selection takes (areaAllies: no unblocking 隐匿, untargetable or sleeping ally; no airborne 起飞 one for a ground
* enemy — PRTS 集团军重型火炮 【燃烧区域】 "碰撞不受迷彩制约,不可对空": 迷彩 only). A sourceless zone (`src` null: 假想敌:蚀裂's
* 毒雾, an enemy's 死亡爆炸 with no 无视可选性 note on PRTS) selects as an effect with no selecting enemy: 隐匿 kept out, an
* airborne 起飞 ally reached (§21.22; until 0.1.2 it took everyone inside).
*/
function dmgZone(b, src, x, y, r, life, iv, amount, type = 'arts', kind = 'zone', el = null, elAmount = 0) {
zone(b, { x, y, r, life, iv, kind, pick: src ? (zx, zy, zr) => areaAllies(b, src, zx, zy, zr) : null, tick(units) {
zone(b, { x, y, r, life, iv, kind, pick: (zx, zy, zr) => areaAllies(b, src, zx, zy, zr), tick(units) {
for (const u of units) {
hurt(b, src, u, amount, type, { tags: [kind] });
if (el && elAmount > 0) elem(b, src, u, el, elAmount, { tags: [kind] });
@@ -1348,7 +1370,8 @@ function kitDeathEye(ab, e) {
if (++n >= dur) {
h.cancel();
b.fx('explode', { x: t.x, y: t.y, r: 1, kind: 'apoptosis' });
for (const u of areaAllies(b, e2, t.x, t.y, 1)) elem(b, e2, u, 'apoptosis', e2.s.atk * (s.bb.ep_damage_ratio ?? 0));
// "对目标及其周围4格的我方单位": the locked target (a 隐匿 it gained mid-channel does not save it) + an area around it
for (const u of targetAndArea(t, areaAllies(b, e2, t.x, t.y, 1))) elem(b, e2, u, 'apoptosis', e2.s.atk * (s.bb.ep_damage_ratio ?? 0));
}
}, { owner: e2 });
}, { sil: true, cond: (b, e2) => targetsNear(b, e2, r).length > 0 })];
@@ -1442,9 +1465,9 @@ function kitHolyGuard(ab) {
tick(b, e) {
const on = b.enemiesInRadius(e.x, e.y, r).some((o) => /enemy_1175_dushdo/.test(o.defId));
if (!on) return;
// a buff aura: every ally in it, 隐匿 / airborne ones included [ASSUMED — PRTS 作战机制 says a Buff selector skips
// 隐匿 unless it ignores it; kept as §21.22 kept ground-enemy auras for 起飞, DESIGN §22.12]
for (const u of b.alliesInRadius(e.x, e.y, r)) auraBuff(b, u, 'ab:forceField', 0.5, { aspd }, null, true);
// a buff aura (auraAllies): no 隐匿 operator that does not block it (PRTS 作战机制 §隐匿与Buff的关系 — no 无视 note on
// its page), but an airborne 起飞 one still [ASSUMED, as §21.22's ground-enemy auras]
for (const u of auraAllies(b, e, e.x, e.y, r)) auraBuff(b, u, 'ab:forceField', 0.5, { aspd }, null, true);
},
}];
}
@@ -1774,7 +1797,7 @@ function kitFlameVine(ab) {
// ends and later attacks add 20 %
a.first = false;
b.fx('explode', { x: t.x, y: t.y, r: 1.5, kind: 'flameVine' });
for (const u of areaAlliesInTiles(b, e, t.tileR, t.tileC, 'box', 1)) { if (u !== t) hurt(b, e, u, e.s.atk, 'arts'); elem(b, e, u, 'burn', e.s.atk * first); }
for (const u of targetAndArea(t, areaAlliesInTiles(b, e, t.tileR, t.tileC, 'box', 1))) { if (u !== t) hurt(b, e, u, e.s.atk, 'arts'); elem(b, e, u, 'burn', e.s.atk * first); }
b.removeBuff(e, 'ab:pow');
} else elem(b, e, t, 'burn', e.s.atk * normal);
},
@@ -1854,7 +1877,7 @@ function kitLeaderMisc(key, ab, e) {
dealt(c, b, e2) {
if (e2.blockedBy) return; // 未被阻挡时会发射榴弹
// the grenade's splash: an area selection — a 隐匿 ally next to the target is spared (GitHub issue #32 item 6)
for (const u of areaAllies(b, e2, c.target.x, c.target.y, 1)) {
for (const u of targetAndArea(c.target, areaAllies(b, e2, c.target.x, c.target.y, 1))) {
if (u !== c.target) hurt(b, e2, u, e2.s.atk, 'phys');
b.applyStatus(u, 'defDown', { duration: SKULSR_DEFDOWN_DUR, source: e2, value: -(T(ab, 'defdown.def') ?? 0) });
}
@@ -1872,7 +1895,8 @@ function kitLeaderMisc(key, ab, e) {
for (const t of l.slice(0, n)) {
const x = t.x, y = t.y;
b.fx('telegraph', { x, y, r: C4_RADIUS, dur: C4_FUSE, kind: 'c4' });
b.after(C4_FUSE, () => { b.fx('explode', { x, y, r: C4_RADIUS, kind: 'c4' }); for (const u of areaAllies(b, e2, x, y, C4_RADIUS)) hurt(b, e2, u, e2.s.atk * (s.bb.atk_scale ?? 1), 'phys'); });
// the C4 sits on its target (PRTS "在…1个非飞行的我方单位身上安装C4"): it is hit; the blast around it is an area
b.after(C4_FUSE, () => { b.fx('explode', { x, y, r: C4_RADIUS, kind: 'c4' }); for (const u of targetAndArea(t, areaAllies(b, e2, x, y, C4_RADIUS))) hurt(b, e2, u, e2.s.atk * (s.bb.atk_scale ?? 1), 'phys'); });
}
}, { cond: (b, e2) => targetsNear(b, e2, s.bb.range_radius ?? e2.base.rangeRadius).length > 0 })];
}
@@ -1943,7 +1967,8 @@ function kitLeaderMisc(key, ab, e) {
b.after(delay, () => {
if (!e2.alive) return;
b.fx('explode', { x: cc, y: r, r: 1, kind: 'blastArrow', tiles: 'plus' });
for (const u of areaAlliesInTiles(b, e2, r, cc, 'plus', 1)) hurt(b, e2, u, e2.s.atk * scale, 'arts');
// "对主目标造成法术普通伤害,对溅射目标造成法术溅射伤害": the arrow's target + the 周围四格 (an area)
for (const u of targetAndArea(t, areaAlliesInTiles(b, e2, r, cc, 'plus', 1))) hurt(b, e2, u, e2.s.atk * scale, 'arts');
}, { owner: e2 });
}
}, { cond: (b, e2) => cands(b, e2).length > 0 })];
@@ -2306,8 +2331,8 @@ function kitWolfLord(ab) {
during(b, e, a, t) { // 【远古威慑】: nearby operators slowed; its HP refills slowly
const dur = T(ab, 'Reborn.duration') ?? 1;
e.hp = Math.max(1, e.s.maxHp * Math.min(1, t / Math.max(TICK, dur)));
// a buff aura: every ally in it, 隐匿 / airborne ones included [ASSUMED, as 深池伙友卫队's — DESIGN §22.12]
for (const u of b.alliesInRadius(e.x, e.y, WOLF_AWE_RADIUS)) auraBuff(b, u, 'ab:ancientAwe', 0.2, { aspd: WOLF_AWE_ASPD }, null, true);
// a buff aura (auraAllies): no 隐匿 operator that does not block it, an airborne 起飞 one still — as 深池伙友卫队's
for (const u of auraAllies(b, e, e.x, e.y, WOLF_AWE_RADIUS)) auraBuff(b, u, 'ab:ancientAwe', 0.2, { aspd: WOLF_AWE_ASPD }, null, true);
},
onReborn(b, e) {
P.form2 = true;
@@ -2520,6 +2545,8 @@ export const KITS = Object.freeze({
if (c.reason !== 'killed') return;
const k = c.killer && c.killer.side === 'ally' && c.killer.alive ? c.killer : null;
const atk = e.s.atk;
// a sourceless zone (`src` null): its ticks skip a 隐匿 / untargetable / sleeping operator (PRTS 假想敌:蚀裂: no 无视
// 可选性 note — PRTS 作战机制's "无来源的毒雾" that reaches 隐匿 units is the stage hazard), not an airborne 起飞 one
const cloud = (x, y) => dmgZone(b, null, x, y, T(ab, '1.projectile_range') ?? 0.8, T(ab, '1.projectile_life_time') ?? 0, T(ab, '1.interval') ?? 1, atk * (T(ab, '1.damage_atk_scale') ?? 0), 'arts', 'poison');
if (k) b.addProjectile({ from: e, target: k, speed: 8, visual: 'lob', source: null, hitDead: true, onHit: (h) => cloud(h.x ?? k.x, h.y ?? k.y) });
else cloud(e.x, e.y);
+30 -14
View File
@@ -11,7 +11,8 @@
// (3) higher enemy taunt, (4) least remaining path distance to the goal, (5) earliest spawned. Air units
// (Unit.isFlying: FLY, 近地悬浮, 浮空) need a profile that can hit them (`canHitFly`, never `groundOnly`). Enemy
// priority: sortAllyTargets. 起飞 (an ally's flag `liftoff`) = 对地规避: no ground enemy selects it (evadesGround).
// An enemy's area effects select allies with areaSelectable (no unblocking 隐匿 ally; 迷彩 is hit — DESIGN §22.12).
// An enemy's area effects select allies with areaSelectable, its buff auras with auraSelectable (no unblocking 隐匿
// ally; 迷彩 is not checked — DESIGN §22.12).
import { COLS, ROWS } from './constants.js';
import { normDir, rotateOffset } from './dir.js';
@@ -74,8 +75,9 @@ export function canTargetEnemy(attacker, e, profile) {
* "隐匿与'阻挡时解除'没有直接关系"). Devices are never targets (阻隔工事: obstacles nobody can select; “双眼皮”: 迷彩 and
* off the enemy paths — PRTS). 迷彩 (flag `camou`, term ba.camou "不阻挡时不成为敌方普通攻击的目标") works the same way:
* PRTS 异常效果 gives both anomalies the note "与'阻挡时解除'没有直接关系" — for this target selection (an attack, a skill
* pick, a cast condition: PRTS 选择器 "所有触发选择器通常不无视迷彩"); an area effect selects with areaSelectable, where
* 迷彩 no longer protects. An airborne ally (起飞, flag `liftoff`) is never a target of a ground enemy (evadesGround).
* pick, a cast condition, a normal attack on every ally in range: PRTS 选择器 "所有触发选择器通常不无视迷彩"); an area
* effect selects with areaSelectable / auraSelectable, which do not check 迷彩. An airborne ally (起飞, flag `liftoff`) is
* never a target of a ground enemy (evadesGround).
*/
export function canTargetAlly(e, a, ranged) {
if (!a.alive || !a.deployed || a.hidden || a.kind === 'device') return false;
@@ -90,26 +92,40 @@ export function canTargetAlly(e, a, ranged) {
* May an AREA effect of enemy-side `src` select ally `a` — a splash, a blast, an area skill or status, a pulse, a zone it
* leaves, a chain / bounce jump, a 周围四格 addition (content/enemies.js areaAllies / areaAlliesInTiles / fieldAllies)?
* PRTS 作战机制 §AOE伤害判定 "AOE的判定是对攻击范围内的每个可以被选中的敌人进行判定"; §隐匿 "隐匿效果使得获得该效果的单位无法被
* 任何敌方的能力索敌选中"; PRTS 选择器 (可选判定): a selector checks 无法选择 by default — "站内所记载的大部分“无视可选性”指
* 的正是不进行这次可选判定"; PRTS 异常效果 §无法选择 = 隐匿, 不可选中, 无敌 (沉睡 is 无敌), 对地规避. So not:
* 任何敌方的能力索敌选中"; PRTS 异常效果 §无法选择: with 隐匿, 不可选中, 无敌, 塔不可选中 or 对地规避 "常见的、来自不同阵营的
* “选择”行为将无视这些单位进行(如同范围内不存在这个单位)", and the abilities PRTS marks "无视可选性" are those that skip
* that check. So not:
* - a 隐匿 ally (flag `stealth`: 伪装服, 叙拉古 6, 伊内丝 S2, the 排气格栅 tile) unless it blocks `src` — a blocked enemy
* attacks its blocker whatever its selectability (PRTS 作战机制 "因为“阻挡优先级最高”的效果,敌人会无视一切可选性对该干员
* 进行攻击"; read for its area abilities too [ASSUMED]); a dead `src` (death blasts) blocks nobody;
* - an untargetable (不可选中) or sleeping (沉睡: 无敌) ally;
* - an untargetable (不可选中) or sleeping (沉睡 = 无敌 + 无法行动) ally;
* - an airborne 起飞 ally when `src` walks (evadesGround — the same refusal the damage pipeline makes).
* 迷彩 (flag `camou`) does not protect: gamedata_const ba.camou "(无法躲避溅射类攻击)"; PRTS 异常效果 迷彩 "所有光环类能力、以及
* 涉及中点判定/格子判定的效果均不受迷彩制约"; PRTS 选择器 "非弹道类型的溅射攻击会自动无视迷彩". `src` null (an effect
* with no selecting enemy): 隐匿 still applies, 对地规避 not. Abilities PRTS marks "无视无法选择 / 无视(目标)可选性" never
* call this (【污染秽蚀】, 【盲信之誓】, 萨卡兹悖谬暴虐兵长's 暴击, 假想敌:淤困's burst spread, 远眺's 暴露 — DamageInfo
* `ignoreSelect`); neither do map / terrain effects nor the buff auras kept as they were (DESIGN §22.12).
* An invulnerable (`invulnerable`, 无敌) ally is still selected, as by canTargetAlly — a known deviation from 无法选择
* [ASSUMED, DESIGN §21.22] (its damage is 0 anyway). 迷彩 (flag `camou`) is not checked: the area effects that call this
* are splash-type, 中点判定 / 格子判定, auras or carry a PRTS "无视迷彩" note (gamedata_const ba.camou "(无法躲避溅射类
* 攻击)"; PRTS 异常效果 迷彩 "所有光环类能力、以及涉及中点判定/格子判定的效果均不受迷彩制约"; PRTS 选择器 "非弹道类型的溅射
* 攻击会自动无视迷彩") — the few without any of these are listed [ASSUMED] in DESIGN §22.12. `src` null (an effect with no
* selecting enemy): 隐匿 still applies, 对地规避 not. Abilities PRTS marks "无视无法选择 / 无视(目标)可选性" never call
* this (【污染秽蚀】, 【盲信之誓】, 萨卡兹悖谬暴虐兵长's 暴击, 假想敌:淤困's burst spread, 远眺's 暴露 — DamageInfo
* `ignoreSelect`); neither do map / terrain effects nor 寒霜's aura. Enemy buff auras use auraSelectable.
*/
export function areaSelectable(src, a) {
return auraSelectable(src, a) && !(a.s.flags.liftoff && evadesGround(src, a));
}
/**
* May a BUFF AURA of enemy-side `src` (a 光环 refreshed on whoever stands in it — 深池伙友卫队's force field, 扎罗's
* 远古威慑; content/enemies.js auraAllies) take ally `a`? PRTS 作战机制 §隐匿与Buff的关系 "隐匿状态下的单位一般无法被敌方的
* 索敌机制和Buff选择器选中为目标", "目前明日方舟中使用能选中隐匿状态单位的Buff效果一定是无视隐匿状态起作用的" (its example:
* 寒霜's 攻速下降 Debuff — content/enemies.js allyAura keeps that one on every ally): no 隐匿 ally unless it blocks `src`,
* no untargetable or sleeping one; 迷彩 does not protect ("所有光环类能力…均不受迷彩制约"). Unlike areaSelectable it does not
* apply 对地规避: a ground enemy's aura still reaches an airborne 起飞 ally [ASSUMED, DESIGN §21.20 / §21.22].
*/
export function auraSelectable(src, a) {
if (!a || !a.alive || !a.deployed || a.hidden || a.kind === 'device') return false;
const f = a.s.flags;
if (f.untargetable || f.sleep) return false;
if (f.stealth && !(src && src.alive && src.blockedBy === a)) return false;
if (f.liftoff && evadesGround(src, a)) return false;
return true;
return !(f.stealth && !(src && src.alive && src.blockedBy === a));
}
/**