Playtest #5 fixes: hovering enemies, target order, blocking hand-over, 联防 knocked-out, elements, huge-boss hit area, maps, gear icon, phone HUD, 至简 price

- #1 近地悬浮 enemies count as air units (Unit.isFlying): ground-only attacks, splash, terrain and traps skip them;
  PRTS 不可对空 skill effects made ground-only; 掠海漂移体 / 吉兆飞鳞 drop modes; the bot plans them as air
- #2 an operator knocked out before 联防 is fielded and force-exited (PRTS 强制退场): it lies on its tile with the
  redeploy ring and redeploys (full timer [ASSUMED]); partners, observers and the server fallback included
- #3 element audit of every operator, item and enemy: 海嗣 numbers are official; bursts are 无来源, 元素脆弱 only
  raises 元素伤害, 元素抗性 applied, 盟约·辅助干员 fills all three gauges, 菲莱 / 哈洛德 / 纯烬 / enemy kits fixed
- #4 blocking by the official contact radius (0.7071): another operator in contact takes over at once when the
  blocker dies, withdraws or is stunned; melee blockers attack what they block
- #5 enemy target order per PRTS 索敌: blocker, special priority, taunt, last deployed; initial deploy 从上到下>从左到右,
  operators before summons; special priorities break ties by taunt then deploy order
- #6 act2 m01's normal-field blower machines (row 13) are drawn in the normal / 联防 views
- #7 Final Assault prep legality reads the player's half of the boss field (fence tiles deployable); all 11 stages
  audited against the official level files
- #8 regular settings gear; #9 the prep camera keeps the bench and temp rows clear of the shop bar on phones
- #10 huge bosses have the official 4.95 × 2.95 hit area (sim/body.js) and are 自缚 / unblockable
- #11 至简 costs 1: 购买价格为N's bb.price is a discount off the tier price, applied before other price effects
- DESIGN §19 and the normative §3 / §5 / §6 / §7 lines, SIM / META / DATA / PLAYING updated; 2595 unit tests and
  every browser suite green; dev recordings regenerated
This commit is contained in:
sganggs committed 2026-09-30 19:12:36 +08:00
1 parent d4f868cd19
commit 15291c2293
104 files changed
+5539 -662

No files matched your search

+30 -3
View File
@@ -441,7 +441,7 @@ test('1_14 格雷伊: 静电场 停顿 on the attacked target, ×talent_scale du
done(h1); done(h2);
});
test('1_15 盟约·辅助干员 (hidden): 迭代元素 18 % ATK 神经损伤 per hit; skill 2 targets + ASPD; elite boost vs ELITE', () => {
test('1_15 盟约·辅助干员 (hidden): 迭代元素 18 % ATK 神经 + 灼燃 + 凋亡 per damage; skill 2 targets + ASPD; elite boost vs ELITE', () => {
const id = 'chess_char_1_15_a', bb = bbOf(id), t = tal(id);
const h = run({ defs: { enemies: { e: dummy('e') } }, units: [{ chessId: id, row: 10, col: 4, carryState: READY }], enemies: [{ key: 'e', pos: [10, 5] }, { key: 'e', pos: [10, 6] }] });
const u = h.unit(id);
@@ -450,7 +450,9 @@ test('1_15 盟约·辅助干员 (hidden): 迭代元素 18 % ATK 神经损伤 per
approx(u.s.aspd - u.base.aspd, bb.attack_speed);
assert.equal(h.hooksOf('attack').find((c) => c.attacker === u).targets.length, bb['attack@max_target']);
const el = dealt(h, u, (c) => c.type === 'element');
assert.ok(el.length >= 2 && el.every((c) => c.dmg.element === 'neural'));
assert.ok(el.length >= 6);
// every damage she deals attaches all three, in the official order (神经 "(优先)", then 灼燃, then 凋亡)
for (let i = 0; i + 2 < el.length; i += 3) assert.deepEqual(el.slice(i, i + 3).map((c) => c.dmg.element), ['neural', 'burn', 'apoptosis']);
for (const c of el) approx(c.amount, u.s.atk * t.ep_damage_ratio);
done(h);
@@ -724,7 +726,32 @@ test('2_05 哈洛德: 重症优先 heals the heaviest element load first with ×
assert.equal(first.targets[0], elem, 'element-damaged ally first');
approx(elem.elem.neural, 900 - u.s.atk * tbOf(id).ep_heal_ratio * bb.trait_scale, 'recovery ×1.6');
h.run(0.3);
approx(elem.s.elemTakenMul, 1 - t.ep_damage_resistance, '我即军营');
// 我即军营 cuts the 元素损伤 (gauge fill) of an ally over half, on the hit itself — not elemTakenMul (元素伤害 / 元素脆弱)
assert.ok(elem.elem.neural > 500, 'still over half');
const b0 = elem.elem.burn;
h.b.dealDamage(null, elem, { type: 'element', element: 'burn', amount: 100 });
approx(elem.elem.burn - b0, 100 * (1 - t.ep_damage_resistance), '我即军营');
approx(elem.s.elemTakenMul, 1, 'no 元素伤害 / 元素脆弱 change');
// under half: no cut
const n0 = low.elem.neural;
h.b.dealDamage(null, low, { type: 'element', element: 'neural', amount: 100 });
approx(low.elem.neural - n0, 100, 'gauge under half: full fill');
done(h);
});
test('2_05 哈洛德 (user playtest #5 #3): 侵蚀 counts as element damage for 重症优先 and 我即军营', () => {
const id = 'chess_char_2_05_a', t = tal(id);
const h = run({ units: [{ chessId: id, row: 10, col: 4, carryState: READY }, { chessId: 'chess_char_1_02_a', row: 10, col: 5 }, { chessId: 'chess_char_1_10_a', row: 11, col: 5 }] });
const u = h.unit(id), low = h.unit('chess_char_1_02_a'), eroded = h.unit('chess_char_1_10_a');
h.step();
low.hp = low.s.maxHp * 0.3;
eroded.elem.erosion = 900;
h.step();
const first = h.hooksOf('attack').find((c) => c.attacker === u);
assert.equal(first.targets[0], eroded, 'the eroded ally is the heaviest element load');
const e0 = eroded.elem.erosion;
h.b.dealDamage(null, eroded, { type: 'element', element: 'erosion', amount: 100 });
approx(eroded.elem.erosion - e0, 100 * (1 - t.ep_damage_resistance), '我即军营 on an eroded ally over half');
done(h);
});