fix: correct community-reported combat and strategy draft behavior
Guard lethal stuns against a replacement deployment; keep bard auras running through stun; require an active Aegir bond for Clementia's strategy; allow Compass SP to bypass recovery locks; and refresh Hidden Core gun mitigation against current gun/spring companions. Set multiplayer strategy picks to 50s and describe renderer fallback without diagnosing unsupported hardware. Golden moves (full 285-scenario regeneration): - roster-008: inactive Aegir no longer gains 18 strategy layers. - boss-boss_4-solo: bard regeneration continues through stun. - hidden-boss_9-pair: mitigation covers first hits/current companions. - standin-07: hammer stun cannot follow an immediate revival deployment. No ending-reason changes. Sources and report-by-report findings: DESIGN 28.25.
This commit is contained in:
1 parent
77cd29f07a
commit
708caf7d06
37 files changed
+277
-103
No files matched your search
@@ -572,18 +572,22 @@ test('埃芒加德 命结之秘: the first 3 knock-downs of the battle revive at
|
||||
cover('band_ermengard');
|
||||
});
|
||||
|
||||
test('克莱门莎 崇高牺牲: a <阿戈尔> operator knocked down ⇒ +its tier <阿戈尔> layers (no activation needed; none in 联防/boss)', () => {
|
||||
test('克莱门莎 崇高牺牲: a <阿戈尔> operator knocked down ⇒ +its tier <阿戈尔> layers (requires activation; none in 联防/boss)', () => {
|
||||
const ops = { t_eg: op('t_eg', { bonds: ['egirShip'], tier: 3 }), t_x: op('t_x', { bonds: ['yanShip'], tier: 5 }) };
|
||||
const h = fight({ band: 'band_clementia', ops, units: [{ chessId: 't_eg', row: 10, col: 4 }, { chessId: 't_x', row: 11, col: 4 }], foes: [[10, 9]] });
|
||||
const h = fight({ band: 'band_clementia', bonds: active('egirShip'), ops, units: [{ chessId: 't_eg', row: 10, col: 4 }, { chessId: 't_x', row: 11, col: 4 }], foes: [[10, 9]] });
|
||||
h.step(1);
|
||||
h.b.dealDamage(foe(h), h.unit('t_eg'), { amount: 1e6, type: 'true' });
|
||||
h.b.dealDamage(foe(h), h.unit('t_x'), { amount: 1e6, type: 'true' });
|
||||
h.b.retreat(h.unit('t_eg')); // not knocked down: nothing (already dead anyway)
|
||||
assert.deepEqual(h.b.result().perPlayer.p1.layerGains, { egirShip: 3 });
|
||||
const u = fight({ band: 'band_clementia', kind: 'unite', ops, units: [{ chessId: 't_eg', row: 10, col: 4 }], foes: [[10, 9]] });
|
||||
const u = fight({ band: 'band_clementia', bonds: active('egirShip'), kind: 'unite', ops, units: [{ chessId: 't_eg', row: 10, col: 4 }], foes: [[10, 9]] });
|
||||
u.step(1);
|
||||
u.b.dealDamage(foe(u), u.unit('t_eg'), { amount: 1e6, type: 'true' });
|
||||
assert.deepEqual(u.b.result().perPlayer.p1.layerGains, {});
|
||||
const inactive = fight({ band: 'band_clementia', ops, units: [{ chessId: 't_eg', row: 10, col: 4 }], foes: [[10, 9]] });
|
||||
inactive.step();
|
||||
inactive.b.dealDamage(foe(inactive), inactive.unit('t_eg'), { amount: 1e6, type: 'true' });
|
||||
assert.deepEqual(inactive.b.result().perPlayer.p1.layerGains, {}, 'an inactive 阿戈尔 bond gains no layers');
|
||||
cover('band_clementia');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Community reports of 2026-10-10: actual attacks / skills, including death and redeployment in the same tick.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, chessRec, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
|
||||
const bond = (layers = 0) => ({ count: 3, active: true, tier: 1, layers });
|
||||
const dummy = enemyRec({ key: 'dummy', hp: 1e9, atk: 0, speed: 0, mass: 0 });
|
||||
const wall = (id, opts = {}) => chessRec({ id, skill: null, stats: { maxHp: 1e7, atk: 0, def: 0, blockCnt: 3 }, ...opts });
|
||||
const finish = (h) => { checkInvariants(h.b); assert.deepEqual(h.b.errors, []); };
|
||||
const near = (actual, expected) => assert.ok(Math.abs(actual - expected) < 1e-6, `${actual} != ${expected}`);
|
||||
|
||||
for (const key of ['enemy_1045_hammer', 'enemy_1045_hammer_2', 'enemy_1160_hvyslr', 'enemy_1160_hvyslr_2']) {
|
||||
test(`${key}: a lethal stunning strike cannot stun the deployment created by 不屈`, () => {
|
||||
const h = makeBattle({ autoFinish: false, timeLimit: 100, defs: { chess: { wall: wall('wall') } },
|
||||
units: [{ chessId: 'wall', row: 10, col: 5 }], bonds: { indomShip: bond(300) } });
|
||||
const nth = key.includes('hvyslr') ? 4 : 3;
|
||||
h.step(); const u = h.unit('wall'); const e = h.spawn(key, { pos: [10, 5], mods: { speedMul: 0 } });
|
||||
assert.ok(h.runUntil(() => e.stats.attacks >= nth - 1, 40));
|
||||
const seq = u.deploySeq; u.hp = 1;
|
||||
assert.ok(h.runUntil(() => e.stats.attacks >= nth, 20));
|
||||
assert.ok(u.alive && u.deploySeq > seq, 'the lethal stunning strike immediately redeployed the operator');
|
||||
assert.ok(!u.s.flags.stun, 'the old attack cannot put stun on that new deployment');
|
||||
// The next stunning strike that does not kill must still stun normally.
|
||||
assert.ok(h.runUntil(() => e.stats.attacks >= nth * 2, 40));
|
||||
assert.ok(u.s.flags.stun); finish(h);
|
||||
});
|
||||
}
|
||||
|
||||
for (const skadi of [false, true]) {
|
||||
test(`${skadi ? '浊心斯卡蒂 custom' : 'default'} bard keeps its regeneration while stunned, stops after leaving`, () => {
|
||||
const bard = chessRec({ id: 'bard', profession: 'SUPPORT', subProfessionId: 'bard', attackKind: 'none', dmgType: 'heal',
|
||||
skill: null, rangeGrid: [[0, 0], [0, 1]], stats: { atk: 1000 } });
|
||||
const h = makeBattle({ autoFinish: false, defs: { chess: { bard, wall: wall('wall') } },
|
||||
units: [{ chessId: skadi ? 'chess_char_6_04_a' : 'bard', row: 10, col: 4, skillIndex: 1 }, { chessId: 'wall', row: 10, col: 5 }] });
|
||||
h.step(); const u = h.b.allyUnits[0], a = h.unit('wall'); a.hp = 100;
|
||||
if (skadi) { u.skill.gainSp(999, 'init'); assert.ok(u.skill.activate('test')); }
|
||||
h.run(1); const regen = a.s.hpRegen; assert.ok(regen > 0);
|
||||
h.b.applyStatus(u, 'stun', { duration: 10 }); h.run(2);
|
||||
near(a.s.hpRegen, regen); const hp = a.hp; h.run(1); assert.ok(a.hp > hp);
|
||||
h.b.kill(u, null); h.run(1); near(a.s.hpRegen, 0); finish(h);
|
||||
});
|
||||
}
|
||||
|
||||
test('黄沙罗盘 + 浓茶 bypass both the active skill lock and noSp, including the caster; ordinary SP still obeys them', () => {
|
||||
const op = (id, bonds) => chessRec({ id, bonds, skill: { spCost: 100, initSp: 0, duration: 20 } });
|
||||
const h = makeBattle({ autoFinish: false, defs: { chess: { a: op('a', ['sargonShip']), b: op('b', ['sargonShip']), c: op('c', []) } },
|
||||
units: [{ chessId: 'a', row: 10, col: 4, items: ['chess_item_6_07_e_a', 'chess_item_2_04_e_a'] },
|
||||
{ chessId: 'b', row: 11, col: 4 }, { chessId: 'c', row: 12, col: 4 }] });
|
||||
h.step(); const a = h.unit('a'), b = h.unit('b'), c = h.unit('c');
|
||||
for (const u of [a, b, c]) { u.skill.gainSp(100, 'init'); h.b.addBuff(u, { key: 'test:lock', flags: { noSp: true } }); }
|
||||
assert.ok(b.skill.activate('test')); assert.ok(c.skill.activate('test')); assert.ok(a.skill.activate('test'));
|
||||
near(a.skill.sp, 3); near(b.skill.sp, 3); near(c.skill.sp, 0);
|
||||
near(b.skill.gainSp(5, 'item'), 0); near(b.skill.sp, 3);
|
||||
a.skill.end('test'); near(a.skill.sp, 33); finish(h);
|
||||
});
|
||||
|
||||
for (const elite of [false, true]) {
|
||||
test(`蕾缪安 S3 real ammo consumption gains active bonds with three operators in the row (${elite ? 'elite' : 'normal'})`, () => {
|
||||
const id = `chess_char_6_01_${elite ? 'b' : 'a'}`;
|
||||
const h = makeBattle({ autoFinish: false, timeLimit: 120, hooks: ['ammoUsed'],
|
||||
defs: { chess: { a: wall('a'), b: wall('b') }, enemies: { dummy } },
|
||||
bonds: { lateranoShip: bond(), preciShip: bond() },
|
||||
units: [{ chessId: id, row: 10, col: 4 }, { chessId: 'a', row: 10, col: 2 }, { chessId: 'b', row: 10, col: 3 }] });
|
||||
h.step(); const u = h.unit(id); h.spawn('dummy', { pos: [10, 6] });
|
||||
for (let i = 0; i < 2; i++) { u.skill.gainSp(999, 'init'); assert.ok(u.skill.activate('test')); assert.ok(h.runUntil(() => !u.skill.active, 30)); }
|
||||
const spent = h.hooksOf('ammoUsed').filter((c) => c.unit === u).length;
|
||||
assert.ok(spent >= 10); const gains = h.result().perPlayer.p1.layerGains;
|
||||
for (const b of ['lateranoShip', 'preciShip']) assert.equal(gains[b], Math.floor(spent / 10) * (elite ? 4 : 2), b);
|
||||
finish(h);
|
||||
});
|
||||
}
|
||||
|
||||
for (const id of ['chess_char_4_23_a', 'chess_char_5_18_a']) {
|
||||
test(`${id} 百炼嘉维尔 S2 damages a final boss but cannot pull it`, () => {
|
||||
const sharedBoss = { hp: 1e9, maxHp: 1e9, damage(pid, n) { this.hp -= n; } };
|
||||
const h = makeBattle({ kind: 'boss', sharedBoss, autoFinish: false, hooks: ['damaged'], captureNoisy: true,
|
||||
units: [{ chessId: id, row: 10, col: 3, skillIndex: 1 }] });
|
||||
h.step(); const u = h.unit(id);
|
||||
const e = h.spawn('enemy_9017_achunt', { pos: [3, 5], tag: 'boss', mods: { speedMul: 0 } });
|
||||
e.profile.noAttack = true;
|
||||
u.skill.gainSp(999, 'init'); assert.ok(u.skill.activate('test')); h.run(3);
|
||||
assert.ok(h.hooksOf('damaged').some((c) => c.source === u && c.target === e), 'S2 actually hits the leader');
|
||||
near(e.x, 5); near(e.y, 3);
|
||||
// An excessive force isolates the leader exclusion from its ordinary high weight.
|
||||
near(h.b.pullToFront(e, u, 99), 0); near(e.x, 5); near(e.y, 3); finish(h);
|
||||
});
|
||||
}
|
||||
@@ -2713,6 +2713,23 @@ test('假想敌:铳 (h07_02 override) never uses 最终之罚 in the regular b
|
||||
assert.ok(!h.eventsOf('fx').some((f) => f[1] === 'charge'));
|
||||
});
|
||||
|
||||
test('隐秘核心 铳: another gun alone grants 80% reduction from the very first hit; no stale reduction after it leaves', () => {
|
||||
const h = bossArena({ units: [{ chessId: 't_wall', row: 10, col: 7 }] });
|
||||
h.step();
|
||||
const g = put(h, 'enemy_9017_achunt_2', [3, 10], { tag: 'boss' });
|
||||
const other = put(h, 'enemy_9017_achunt_2', [3, 4], { tag: 'boss' });
|
||||
const full = 1000 * (1 - g.s.res / 100);
|
||||
approx(h.b.dealDamage(null, g, { amount: 1000, type: 'arts' }), full * 0.2);
|
||||
// Leaving the field without killing the shared boss pool isolates the companion condition.
|
||||
other.alive = false; other.deployed = false;
|
||||
approx(h.b.dealDamage(null, g, { amount: 1000, type: 'arts' }), full);
|
||||
const spring = put(h, 'enemy_9020_actrpc', [3, 6], { tag: 'part' });
|
||||
approx(h.b.dealDamage(null, g, { amount: 1000, type: 'arts' }), full * 0.2);
|
||||
spring.alive = false; spring.deployed = false;
|
||||
approx(h.b.dealDamage(null, g, { amount: 1000, type: 'arts' }), full);
|
||||
checkInvariants(h.b); assert.deepEqual(h.b.errors, []);
|
||||
});
|
||||
|
||||
test('假想敌:铳 (隐秘核心): damage ×0.2 while springs live; 盲信之誓 lines hurt operators on them; 末日布道 dash', () => {
|
||||
const h = bossArena({ units: [{ chessId: 't_wall', row: 10, col: 7 }], setup(b) { b.enemyOverrides = W.act1autochess_h08_02.overrides; } });
|
||||
h.step();
|
||||
|
||||
@@ -617,7 +617,7 @@ test('黄沙罗盘: initial SP +30 / +50; (萨尔贡) first skill end +30 SP; +
|
||||
u.skill.sp = 0;
|
||||
u.skill.activate('test', { free: true });
|
||||
u.skill.end('test');
|
||||
close(u.skill.sp, 0, 'only the first skill end');
|
||||
close(u.skill.sp, 3, 'only the first skill end gives 30; this cast still gives its own 3 through the lock');
|
||||
cover(A('6_07'), B('6_07'), A('2_04'), B('2_04'));
|
||||
});
|
||||
|
||||
|
||||
@@ -323,11 +323,11 @@ test('user playtest #4 (DESIGN §18): picking by tile, timers, 机变 two taps,
|
||||
assert.match(META, /solo \/ single human untimed/);
|
||||
assert.match(PLAYING, /只有你一名玩家/);
|
||||
// #4 one countdown: BAND_TURN_SECONDS per turn (code = data = docs)
|
||||
assert.equal(BAND_TURN_SECONDS, 30);
|
||||
assert.equal(BAND_TURN_SECONDS, 50);
|
||||
assert.equal(DATA.config.timers.bandTurn, BAND_TURN_SECONDS);
|
||||
assert.match(DESIGN, /`BAND_TURN_SECONDS` 30 s per turn = m\.public\.deadline/);
|
||||
assert.match(META, /`Match\.BAND_TURN_SECONDS` 30/);
|
||||
assert.match(PLAYING, /\*\*每人 30 秒\*\*/);
|
||||
assert.match(DESIGN, /`BAND_TURN_SECONDS` 50 s per turn = m\.public\.deadline/);
|
||||
assert.match(META, /`Match\.BAND_TURN_SECONDS` 50/);
|
||||
assert.match(PLAYING, /\*\*每人 50 秒\*\*/);
|
||||
assert.ok(!/12 s\/turn/.test(DESIGN) && !/`bandTurn` 12/.test(META) && !/每人 12 秒/.test(PLAYING), 'the old 12 s turn is gone');
|
||||
assert.match(META, /`ev\.preview` true/, 'META: onBattleStart handlers must not change the match for the stats preview');
|
||||
// #2 机变 two taps
|
||||
|
||||
+14
-14
@@ -493,24 +493,24 @@
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":7,"total":7,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":851512,"bossDmg":507527,"heal":97350,"deaths":4,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",3574.36,1,4,0,34947,15344,19358,35,0],
|
||||
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",3574.36,1,4,0,34947,15344,18716,35,0],
|
||||
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",6421.77,1,6,2,47087,20583,21153,15,7],
|
||||
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",3092.1,1,21,4,222093,33954,10820,50,2],
|
||||
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",3092.1,1,21,4,222093,33954,11556,50,2],
|
||||
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",3727.68,1,9,0,14897,8313,0,32,2],
|
||||
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",2825.9,1,23,0,189827,5620,0,112,3],
|
||||
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",0,0,22,0,263410,3122,0,104,5],
|
||||
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",1742.4,1,11,0,0,4444,18833,22,2],
|
||||
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",2395.8,1,12,1,79251,5783,825,91,6],
|
||||
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1586.02,1,3,0,0,4444,5184,0,3],
|
||||
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1548.74,1,5,0,0,4444,21177,28,2],
|
||||
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1586.02,1,3,0,0,4444,5277,0,3],
|
||||
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1548.74,1,5,0,0,4444,20990,27,2],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {"enemy_1010_demon_2":[2,2,0,4541,107495],"enemy_1438_dspred":[5,5,0,35246,236490],"enemy_1521_dslily":[1,0,0,60130,507527]},
|
||||
"hooks": [1,23,6000,612,612,1020,1376,7619,11,11,32,31,0,13754,153,15,8,0,1,12,0,30,6],
|
||||
"events": {"atk":817,"deploy":13,"die":11,"dmg":1354,"engage":7,"fx":380,"heal":88,"skill":65,"spawn":20,"status":567},
|
||||
"hooks": [1,23,6000,611,611,1020,1376,7785,11,11,32,31,0,13754,153,15,8,0,1,12,0,30,6],
|
||||
"events": {"atk":816,"deploy":13,"die":11,"dmg":1354,"engage":7,"fx":380,"heal":86,"skill":65,"spawn":20,"status":567},
|
||||
"fx": {"appear":2,"bandage":2,"block":22,"burst":30,"camouflage":1,"disappear":2,"dodge":6,"featherArrow":5,"flame":2,"garrisonGrant":1,"grow":1,"healField":2,"jungleSoul":2,"mote":68,"redistribute":45,"reweave":3,"riposte":6,"rockfall":18,"rockslide":7,"sandChains":6,"shieldBreak":22,"slash":51,"spGift":6,"tentacle":6,"thorns":55,"tide":5,"truesilver":3,"undying":1},
|
||||
"snaps": "4357c1b6 34df6216 b3da85aa 3b7d368b 762f4ac8 daa91aef 832c4be2 95b21603 93538c1e b305aa21 42b77f2e f8296dc4 8fa97c9b 26ee953f b4ec0d70 46537e48 9cb7a666 1bbcbd3e 6cf0c396 02349ae3"
|
||||
"snaps": "4357c1b6 34df6216 b3da85aa 3b7d368b 762f4ac8 daa91aef 832c4be2 95b21603 93538c1e 6b29d2ca 77ccce4c cf06422e f2dccfed 789733a1 83ba65ee 81086826 06bf209c 7bdd56fd c413e565 757edd58"
|
||||
},
|
||||
"boss-boss_5-pair": {
|
||||
"about": "卢西恩,“猩红血钻” pair pool 1560000: chess_char_6_03_b chess_char_6_06_b chess_char_6_07_b chess_char_6_01_b chess_char_6_02_b chess_char_6_04_b chess_char_6_05_b chess_char_6_08_b chess_char_6_09_b chess_char_6_11_b token_10017_skadi2_dedant token_10030_mlyss_wtrman | chess_char_6_15_b chess_char_6_19_b chess_char_5_01_b chess_char_6_17_b chess_char_6_12_b chess_char_6_13_b chess_char_6_14_b chess_char_6_16_b chess_char_6_18_b chess_char_6_20_b",
|
||||
@@ -1076,12 +1076,12 @@
|
||||
"killed": 8,
|
||||
"total": 8,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": 2992115,
|
||||
"bossHpLeft": 2992511,
|
||||
"rngDraws": 1828,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {
|
||||
"p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2840858,"bossDmg":1130306,"heal":26454,"deaths":3,"layerGains":{}},
|
||||
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":3891982,"bossDmg":1427731,"heal":133966,"deaths":7,"layerGains":{}}
|
||||
"p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2840728,"bossDmg":1130176,"heal":26454,"deaths":3,"layerGains":{}},
|
||||
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":3891716,"bossDmg":1427465,"heal":133966,"deaths":7,"layerGains":{}}
|
||||
},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",7326,1,15,0,3237,9163,15665,31,3],
|
||||
@@ -1093,7 +1093,7 @@
|
||||
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",2696.54,1,13,1,71156,0,0,0,11],
|
||||
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1834.8,1,19,0,55032,0,9419,73,2],
|
||||
["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",0,0,21,1,160062,14044,0,27,27],
|
||||
["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",2082.3,1,59,0,30214,11261,306,74,2],
|
||||
["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",2082.3,1,59,0,30083,11261,306,74,2],
|
||||
["p1",11,"token","token_10017_skadi2_dedant",null,null,138,0,28,0,0,0,0,0,null],
|
||||
["p1",12,"token","token_10030_mlyss_wtrman",null,null,0,0,24,0,0,1670,348,4,1],
|
||||
["p2",13,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",903.1,1,68,4,817795,32754,0,248,4],
|
||||
@@ -1104,7 +1104,7 @@
|
||||
["p2",18,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2436.5,1,14,0,526339,8805,4488,259,5],
|
||||
["p2",19,"op","chess_char_6_14_b",2,"uniequip_002_lumen",3342.24,1,20,0,0,0,19549,78,1],
|
||||
["p2",20,"op","chess_char_6_16_b",2,"uniequip_002_halo2",1582.9,1,10,0,103786,0,0,127,3],
|
||||
["p2",21,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1859.61,1,27,0,346326,5155,0,25,3],
|
||||
["p2",21,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1859.61,1,27,0,346061,5155,0,25,3],
|
||||
["p2",22,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1758.31,1,25,0,0,14000,103507,78,2],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
@@ -1128,7 +1128,7 @@
|
||||
"enemies": {
|
||||
"enemy_1267_nhpbr_2": [6,6,0,1900,141894],
|
||||
"enemy_9007_acelem": [2,2,0,14390,309587],
|
||||
"enemy_9017_achunt_2": [2,0,0,57663,2607885],
|
||||
"enemy_9017_achunt_2": [2,0,0,57663,2607489],
|
||||
"enemy_9018_actrpa": [1,0,0,53759,1092472],
|
||||
"enemy_9019_actrpb": [1,0,0,24773,1015854],
|
||||
"enemy_9020_actrpc": [1,0,0,22601,1668902]
|
||||
@@ -1175,7 +1175,7 @@
|
||||
"wanted": 6,
|
||||
"zone": 6
|
||||
},
|
||||
"snaps": "8b102142 7109c20e 6cb62905 16f4d84e db38e8d1 fb736fc0 d644e65e bf490768 454f169f 723043e7 883b6557 609aa7bb bfa05477 7d38be83 76b95dc1 8ac15f69 7c25d190 03595be2 40606ec4 e87eb589"
|
||||
"snaps": "8b102142 0561d2e7 b88fff99 8716a28b 1badf400 91b895ee 78b15f27 974adf64 c118d65e 831cdc6f 0c7385df d5ab8494 2e6d06b7 f460f583 e7a7bafc 6ab1c142 80a01218 1de18f0f 84c4afee 5fb9b9bc"
|
||||
},
|
||||
"hidden-boss_9-solo": {
|
||||
"about": "假想敌:铳 solo pool 2800000: chess_char_5_08_b chess_char_5_11_b chess_char_5_05_b chess_char_5_06_b chess_char_5_13_b chess_char_5_07_b chess_char_5_02_b chess_char_5_03_b chess_char_5_10_b chess_char_5_12_b",
|
||||
|
||||
+4
-16
@@ -454,19 +454,7 @@
|
||||
"rngDraws": 43,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {
|
||||
"p1": {
|
||||
"killed": 58,
|
||||
"total": 83,
|
||||
"perfect": false,
|
||||
"leaks": 29,
|
||||
"leaksUncounted": 1,
|
||||
"coins": 8,
|
||||
"dmg": 584652,
|
||||
"bossDmg": 0,
|
||||
"heal": 5484,
|
||||
"deaths": 18,
|
||||
"layerGains": {"egirShip":18,"raidShip":36}
|
||||
}
|
||||
"p1": {"killed":58,"total":83,"perfect":false,"leaks":29,"leaksUncounted":1,"coins":8,"dmg":584652,"bossDmg":0,"heal":5484,"deaths":18,"layerGains":{"raidShip":36}}
|
||||
},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_3_06_b",1,"uniequip_002_philae",0,0,6,3,8669,8443,0,11,1],
|
||||
@@ -524,9 +512,9 @@
|
||||
"enemy_2021_syfish": [60,46,14,2435,336653],
|
||||
"enemy_2025_syufo": [13,3,7,30859,107202]
|
||||
},
|
||||
"hooks": [1,136,5100,622,622,1146,1244,412,76,130,23,25,0,8861,90,55,88,26,1,81,15,12,0],
|
||||
"events": {"atk":700,"bounty":4,"deploy":48,"die":104,"dmg":1157,"engage":12,"fx":224,"heal":412,"layer":15,"leak":26,"skill":50,"spawn":126,"status":151},
|
||||
"fx": {"aoe":21,"blink":2,"buff":1,"burst":12,"coin":26,"displace":30,"explode":31,"garrison":9,"layer":6,"pull":30,"revive":5,"shield":1,"shieldBreak":1,"stone":1,"strike":11,"summon":27,"telegraph":1,"volley":9},
|
||||
"hooks": [1,136,5100,622,622,1146,1244,412,76,130,23,25,0,8861,90,55,88,26,1,81,9,12,0],
|
||||
"events": {"atk":700,"bounty":4,"deploy":48,"die":104,"dmg":1157,"engage":12,"fx":218,"heal":412,"layer":9,"leak":26,"skill":50,"spawn":126,"status":151},
|
||||
"fx": {"aoe":21,"blink":2,"buff":1,"burst":12,"coin":26,"displace":30,"explode":31,"garrison":9,"pull":30,"revive":5,"shield":1,"shieldBreak":1,"stone":1,"strike":11,"summon":27,"telegraph":1,"volley":9},
|
||||
"snaps": "b19807c9 91093618 e23e7617 b1bc3a57 24c17dc9 e4263c69 ce35e0e8 34019a9b d63a9b7b 8f9d73c2 d3acc943 cbc3410b 6b14e481 7d8bf5fe 9a5b67ba 72b72b18 90c03fa8"
|
||||
},
|
||||
"roster-009": {
|
||||
|
||||
+23
-23
@@ -359,21 +359,21 @@
|
||||
"time": 223.2,
|
||||
"ticks": 6696,
|
||||
"reason": "cleared",
|
||||
"killed": 9,
|
||||
"killed": 10,
|
||||
"total": 80,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 81,
|
||||
"rngDraws": 84,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {
|
||||
"p1": {
|
||||
"killed": 9,
|
||||
"killed": 10,
|
||||
"total": 80,
|
||||
"perfect": false,
|
||||
"leaks": 71,
|
||||
"leaks": 70,
|
||||
"leaksUncounted": 0,
|
||||
"coins": 4,
|
||||
"dmg": 146478,
|
||||
"dmg": 144421,
|
||||
"bossDmg": 0,
|
||||
"heal": 0,
|
||||
"deaths": 25,
|
||||
@@ -384,35 +384,35 @@
|
||||
["p1",1,"op","chess_char_4_17_b",1,"none",0,0,3,0,189,3211,0,1,1],
|
||||
["p1",2,"op","chess_char_4_18_b",1,"none",0,0,86,0,60,9306,0,2,1],
|
||||
["p1",3,"op","chess_char_5_05_b",2,"none",0,0,100,0,572,5816,0,1,1],
|
||||
["p1",4,"op","chess_char_4_19_b",1,"none",1847.63,1,110,0,230,3864,0,7,1],
|
||||
["p1",5,"op","chess_char_5_01_b",2,"none",1036.53,1,108,1,2433,3985,0,6,3],
|
||||
["p1",6,"op","chess_char_4_22_b",1,"none",3191,1,112,0,959,14040,0,6,1],
|
||||
["p1",4,"op","chess_char_4_19_b",1,"none",1847.63,1,110,1,466,3864,0,8,1],
|
||||
["p1",5,"op","chess_char_5_01_b",2,"none",1036.53,1,108,0,2197,3985,0,5,3],
|
||||
["p1",6,"op","chess_char_4_22_b",1,"none",3191,1,112,0,1090,14040,0,8,1],
|
||||
["p1",7,"op","chess_char_4_23_b",1,"none",0,0,71,0,3484,6865,0,8,1],
|
||||
["p1",8,"op","chess_char_5_06_b",2,"uniequip_002_acguad",0,0,10,0,0,3820,0,0,0],
|
||||
["p1",9,"op","chess_char_4_20_b",1,"none",1255.06,1,109,2,17210,4785,0,32,1],
|
||||
["p1",10,"op","chess_char_4_24_b",1,"none",1007.59,1,111,2,5402,3072,0,19,1],
|
||||
["p1",9,"op","chess_char_4_20_b",1,"none",1255.06,1,109,2,15866,4785,0,30,1],
|
||||
["p1",10,"op","chess_char_4_24_b",1,"none",1007.59,1,111,1,5309,3072,0,18,1],
|
||||
["p1",11,"op","chess_char_5_02_b",2,"none",0,0,105,1,8684,4080,0,19,1],
|
||||
["p1",12,"op","chess_char_5_03_b",2,"uniequip_002_accast",0,0,4,3,107254,1749,0,10,1],
|
||||
["p1",12,"op","chess_char_5_03_b",2,"uniequip_002_accast",0,0,4,5,106504,1749,0,10,1],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {
|
||||
"enemy_1005_yokai_2": [18,3,15,3213,10312],
|
||||
"enemy_1041_lazerd_2": [24,3,21,43210,35712],
|
||||
"enemy_1042_frostd": [30,1,29,0,34310],
|
||||
"enemy_1045_hammer": [1,1,0,2029,15552],
|
||||
"enemy_1045_hammer_2": [1,0,1,4288,18162],
|
||||
"enemy_1046_agent": [1,1,0,72,3888],
|
||||
"enemy_1050_lslime": [1,0,1,9094,6601],
|
||||
"enemy_1005_yokai_2": [18,4,14,3323,11182],
|
||||
"enemy_1041_lazerd_2": [24,3,21,42917,34367],
|
||||
"enemy_1042_frostd": [30,1,29,0,33047],
|
||||
"enemy_1045_hammer": [1,1,0,2515,15552],
|
||||
"enemy_1045_hammer_2": [1,0,1,5062,16522],
|
||||
"enemy_1046_agent": [1,1,0,57,3888],
|
||||
"enemy_1050_lslime": [1,0,1,8323,7921],
|
||||
"enemy_1061_zomshd": [1,0,1,61,13892],
|
||||
"enemy_1062_rager_2": [1,0,1,2628,17050],
|
||||
"enemy_1062_rager_2": [1,0,1,2335,17050],
|
||||
"enemy_1067_snslime": [1,0,1,0,0],
|
||||
"enemy_1071_dftman": [1,0,1,0,0]
|
||||
},
|
||||
"hooks": [1,112,6696,299,299,578,571,566,34,105,13,13,0,9182,8,14,80,71,1,34,2,0,7],
|
||||
"events": {"atk":324,"bounty":2,"deploy":30,"die":34,"dmg":571,"engage":11,"fx":22,"layer":2,"leak":71,"skill":26,"spawn":94,"status":111},
|
||||
"fx": {"aoe":1,"dodge":7,"dp":6,"garrison":2,"garrisonGrant":1,"palsy":2,"revive":3},
|
||||
"snaps": "801e90ec c1cca6cb f2a5a38b 6dc62d98 cb792a41 23ec9de6 62a40e3b eb8791c8 5cf201e4 e0c50494 b5bd31db 490ef7be f2c93ce1 ee84f980 68266f8f d66f411d 6f00ab8c 98dfa057 343e0f1c 7a9470b3 c574bda9 8be7c32a dadff196"
|
||||
"hooks": [1,112,6696,302,302,560,551,566,35,105,13,13,0,9244,8,13,80,70,1,35,2,0,9],
|
||||
"events": {"atk":326,"bounty":2,"deploy":30,"die":35,"dmg":551,"engage":11,"fx":25,"layer":2,"leak":70,"skill":26,"spawn":94,"status":113},
|
||||
"fx": {"aoe":1,"dodge":9,"dp":7,"garrison":2,"garrisonGrant":1,"palsy":2,"revive":3},
|
||||
"snaps": "801e90ec c1cca6cb 81d33420 f867f133 edacd763 1628b012 6c63447d 731a52ce e4d1fee0 1aa30026 5afb0d3a 3292f917 f65a695f 7f9749da 215a493b 0a35d6fd 8300297d 71eb2c83 2ae65721 93a77bf5 f413d9ea 3b2f4b8c ef24d1bf"
|
||||
},
|
||||
"standin-08": {
|
||||
"about": "elite band 6 as stand-ins: chess_char_5_08_b→char_610_acfend chess_char_5_14_b→char_608_acpion chess_char_5_19_b→char_617_sharp2 chess_char_5_07_b→char_601_cguard chess_char_5_13_b→char_609_acguad chess_char_5_17_b→char_615_acspec chess_char_5_10_b→char_606_csuppo chess_char_5_11_b→char_613_acmedc chess_char_5_12_b→char_614_acsupo chess_char_5_15_b→char_603_csnipe chess_char_5_18_b→char_611_acnipe chess_char_5_20_b→char_614_acsupo",
|
||||
|
||||
@@ -113,6 +113,7 @@ test('band draft timers (user playtest #4 item 4): one countdown of BAND_TURN_SE
|
||||
assert.equal(new Set([...h2.m.players.values()].map((ps) => ps.bandId)).size, 4, 'the rest get distinct free strategies');
|
||||
h2.m.dispose();
|
||||
// data and code say the same: data/config.json timers.bandTurn (tools/build-data.mjs) = Match.BAND_TURN_SECONDS
|
||||
assert.equal(BAND_TURN_SECONDS, 50, 'owner official-play report: 50 seconds per multiplayer pick');
|
||||
assert.equal(DATA.config.timers.bandTurn, BAND_TURN_SECONDS);
|
||||
});
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ describe('server: automatic assignments never duplicate a teammate\'s strategy',
|
||||
test('every human idle: the turn timeouts assign distinct strategies (no separate step cap)', () => {
|
||||
const h = draftOf({ humans: 4, seed: 9 });
|
||||
const m = h.m;
|
||||
h.run(() => m.phase !== PHASE.BAND_DRAFT, { maxTime: 120000 });
|
||||
h.run(() => m.phase !== PHASE.BAND_DRAFT, { maxTime: 4 * TURN_MS + 1 });
|
||||
assert.equal(m.phase, PHASE.BATTLE_CHECK);
|
||||
const ids = distinct(m);
|
||||
assert.ok(ids.includes('band_bldsk'), 'the first idle player still gets the official default');
|
||||
@@ -194,7 +194,7 @@ describe('server: automatic assignments never duplicate a teammate\'s strategy',
|
||||
m.dispose();
|
||||
for (const seed of [1, 2, 3, 4]) {
|
||||
const h2 = draftOf({ humans: 2, bots: 2, seed });
|
||||
h2.run(() => h2.m.phase !== PHASE.BAND_DRAFT, { maxTime: 120000 });
|
||||
h2.run(() => h2.m.phase !== PHASE.BAND_DRAFT, { maxTime: 4 * TURN_MS + 1 });
|
||||
distinct(h2.m);
|
||||
h2.m.dispose();
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ describe('user playtest #2 item 10 — boss-round prep on the boss field (real s
|
||||
for (const c of [host, guest]) await c.waitFor((s) => s.phase === 'INFO_CHECK', 'briefing', 30000);
|
||||
for (const c of [host, guest]) await c.click('.brief__foot .btn--primary', '准备就绪');
|
||||
// the draft first (a client still in the briefing would count as done without picking: its turn then runs out —
|
||||
// Match.BAND_TURN_SECONDS, 30 s each since user playtest #4 item 4)
|
||||
// Match.BAND_TURN_SECONDS, 50 s each since the 2026-10-10 official-play report)
|
||||
for (const c of [host, guest]) await c.waitFor((s) => s.phase !== 'INFO_CHECK', 'band draft', 40000);
|
||||
// band draft: whoever's turn it is picks a free strategy
|
||||
const picked = new Set();
|
||||
|
||||
@@ -88,7 +88,7 @@ describe('DESIGN §16 — a teammate\'s unit shows its owner\'s loadout (real se
|
||||
assert.deepEqual(await loOf(host), {}, 'the host fights with the defaults');
|
||||
for (const c of [host, guest]) await c.click('.brief__foot .btn--primary', '准备就绪');
|
||||
// the draft first (a client still in the briefing would count as done without picking: its turn then runs out —
|
||||
// Match.BAND_TURN_SECONDS, 30 s each since user playtest #4 item 4)
|
||||
// Match.BAND_TURN_SECONDS, 50 s each since the 2026-10-10 official-play report)
|
||||
for (const c of [host, guest]) await c.waitFor((s) => s.phase !== 'INFO_CHECK', 'band draft', 40000);
|
||||
const picked = new Set();
|
||||
const t0 = Date.now();
|
||||
|
||||
Reference in new issue
Block a user