Playtest #6 fixes: summons placed by hand, boss pool stall, bounties, 联防 leak counter, distinct offers, 机变 card text, phone buy tap, skill triggers, blocking, push force, 孤立, enemies, element bar

- #1 #2 skill / device summons (赫默's drone, 巫恋's doll, 凯瑟琳's devices) are hand cards placed by the player;
  placed summons deploy once at battle start, then a skill summon re-appears at its tile on each cast (the user's call)
- #3 叙拉古 隐匿 ends on time (32 + 0.4 × layers s); 忍冬 S3 迷彩 lasts until her next cast (official), kept apart from 隐匿
- #4 multi-round bounty cards last two battles (the user's call; MULTI_ROUND_BOUNTY_BATTLES), blue / red card text
- #5 the shared boss pool's float dust (≈3.6e-12 HP) stalled Final Assault leaders at 0 %; < 1 HP now clears the pool,
  and the team LP reaching 0 first is a defeat (PRTS)
- #6 #10 机变 cards show their official effect text; an invisible 详情 button no longer swallows the confirming tap on phones
- #7 live ×N counter of a leaker's enemies still on the 联防 field (LP loss stays capped at 10)
- #8 #15 #16 skill triggers per 技能策略: skill-range triggers, injured-ally heals (古米, 塞雷娅), tank take-damage
  triggers with a 3 s cooldown; 新约能天使 S2 shields the highest-aggro operator in range
- #9 official per-enemy model scales (威龙)
- #11 element bar under the unit's HP bar (white = 元素值 left, element-coloured refill after a burst), floor rounding
- #12 枯朽战车 kit per PRTS; every ranged enemy reaches its target's 0.25 collider
- #13 恐惧 wander (萨科塔之翼 at half HP, operator fears make enemies flee) with re-planning after pushes
- #14 push / pull distance by 力度 − 重量 (PRTS table); #17 every blocker attacks what it blocks; ASPD floor 20
- #18 炎佑 is 孤立: no heals or ally buffs
- #19 every free pick-one chess offer (promotion reward, 信标, 寻呼模块) shows different operators
- DESIGN §20 and the normative lines, SIM / META / DATA / PLAYING updated; 2747 unit tests and all 27 browser suites
  green; dev recordings regenerated
This commit is contained in:
sganggs committed 2026-10-01 19:08:23 +08:00
1 parent e6628f0cfe
commit 6464e4b46a
138 files changed
+7946 -1379

No files matched your search

+11 -5
View File
@@ -164,7 +164,9 @@ test('6_01 蕾缪安 S2: a low-HP wanted target is sniped at once (ATK × scale
test('6_02 圣聆初雪 S1 铃音吹雪: 2 charges (cast with enemies in range); atk_scale × ATK arts + cold on every enemy in range, pushed along her direction, snow spreads 5 tiles forward', () => {
for (const id of both('chess_char_6_02')) {
const sid = 'skchr_sbell2_1', bb = bbOf(id, sid);
const h = run({ defs: { enemies: { e: dummy('e') } }, units: [U(id, sid, 10, 4, { carryState: READY })], enemies: [{ key: 'e', pos: [10, 5] }, { key: 'e', pos: [11, 5] }] });
// weight-2 dummies: 中力 (1) − 2 = 受力等级 −1 ⇒ the official 0.44-tile push (PRTS 游戏数据基础 §重量公式), so they stay
// in her range for the second charge
const h = run({ defs: { enemies: { e: dummy('e', { mass: 2 }) } }, units: [U(id, sid, 10, 4, { carryState: READY })], enemies: [{ key: 'e', pos: [10, 5] }, { key: 'e', pos: [11, 5] }] });
const u = h.unit(id);
usesSkill(u, sid);
assert.equal(u.skill.maxCharges, rec(id, sid).maxChargeTime);
@@ -179,11 +181,15 @@ test('6_02 圣聆初雪 S1 铃音吹雪: 2 charges (cast with enemies in range);
assert.equal(colds.length, 2);
for (const c of colds) approx(c.duration, bb.cold, 'cold');
const [e1] = h.enemies();
assert.ok(e1.x > 5.5, `pushed forward (${e1.x})`);
approx(e1.x, 5 + 0.44 * started(h, u).length, 'pushed forward along her direction by the official 0.44 tiles per cast');
approx(e1.y, 10, 'no sideways push');
// (the pushed enemies clear the snow of the tiles they leave: "首个敌人离开该地块时积雪消失")
for (let d = 3; d <= bb.trig_cnt; d++) assert.ok((u.mem.snow.get(10 * COLS + 4 + d) ?? 0) >= 1, `snow ${d} tiles ahead`);
h.run(2.5);
assert.equal(started(h, u).length, 1, 'the next automatic cast waits the 3 s operation cooldown ("自动操作具有3s冷却")');
h.run(1);
assert.equal(started(h, u).length, 2, 'the second charge fires too');
assert.ok(started(h, u)[1].t - t0 >= 3 - 1e-6);
done(h);
}
});
@@ -255,11 +261,11 @@ test('6_03 余 S1 今日做东: taunt +1 while carried; TAKE_DAMAGE cast, HP/DEF
assert.equal(d.unit('chess_char_6_03_a').s.taunt, 0);
});
test('6_03 余 S2 厚礼上宾: atk_scale × ATK arts around him, reachable ground enemies teleported onto his tile; block +2, HP/ATK +, arts attacks', () => {
test('6_03 余 S2 厚礼上宾: cast when hit (重装 TAKE_DAMAGE); atk_scale × ATK arts around him, reachable ground enemies teleported onto his tile; block +2, HP/ATK +, arts attacks', () => {
for (const id of both('chess_char_6_03')) {
const sid = 'skchr_yu_2', bb = bbOf(id, sid);
const h = run({
defs: { enemies: { e: dummy('e') } },
defs: { enemies: { e: dummy('e', { atk: 100, bat: 1 }) } },
units: [U(id, sid, 10, 4, { carryState: READY })],
enemies: [{ key: 'e', pos: [10, 4] }, { key: 'e', pos: [10, 6] }, { key: 'e', pos: [11, 5] }],
});
@@ -1153,7 +1159,7 @@ test('default-skill-only hooks do not run under an alternate skill (余 / 维娜
// 余 S2 active: 闲云隐市 is NOT given to every operator (S3 only)
{
const h = run({
defs: { chess: { o1: plain('o1'), o2: plain('o2'), o3: plain('o3') }, enemies: { e: dummy('e') } },
defs: { chess: { o1: plain('o1'), o2: plain('o2'), o3: plain('o3') }, enemies: { e: dummy('e', { atk: 100, bat: 1 }) } }, // (重装: TAKE_DAMAGE)
units: [U('chess_char_6_03_a', 'skchr_yu_2', 10, 4, { carryState: READY }), { chessId: 'o1', row: 9, col: 6 }, { chessId: 'o2', row: 11, col: 6 }, { chessId: 'o3', row: 12, col: 6 }],
enemies: [{ key: 'e', pos: [10, 4] }],
});