feedback7: merge fb7-fields — PR #337 boss-field portals teleport (route-end portals: hidden 3 s, out of [5,10], leak at the side's door), PR #347 碎铳之簧 chases the full 5 s, #370 耀骑士临光's redeploy reduction floored at 0, #375 the water-map patrol takes the official upper road

- Grok review: MERGE AFTER FIXES — the end-portal wait (0 s → 3 s, the data's modal crossing) fixed on the branch
  (1205336a). solo-NORMAL-1 still fails the Hidden Core (explained in the branch commits).
- Golden union OK; broad node --test 5517 (5516 pass, 0 fail, 1 skipped); lint 0 errors.
This commit is contained in:
sganggs committed 2026-10-08 21:20:37 +08:00
commit 31fee55c6c
18 files changed
+819 -336

No files matched your search

+1 -1
View File
@@ -445,7 +445,7 @@ as official level data, and sim tests use them as a plain two-halves road.
| `special` | `{"mire":{"intervalSec":1,"aspdPerStack":-0.05,"moveMulPerStack":-0.05,"maxStacks":10,"heavyWeight":3,…}}`, `{"infection":{"bb":{"damage":70,"atk":0.2,"attack_speed":20,"duration":300}}}`, `{"deepsea":{"skillId":"sktok_tidectrl_3","bb":{…}}}`, `{"smog":{"rule":"operatorsNotTargetableByEnemyRanged"}}`, `{"blower":{…}}` | special-terrain parameters |
| `runes[]`, `globalBuffs[]` | `{"key":"map_tile_blackb_assign","bb":{…},"bbStr":{…}}` | level runes (act2 m03 reed rune has no reed tiles — leftover) |
| `deployTiles` | `{"normal":{"melee":[[9,3],…],"rangedOnly":[[10,4],…],"changedByDevices":[…]},"bossLeft":{…},"bossRight":{…}}` | deployable tiles at match start: `melee` = LOW (effective) buildable ALL/MELEE (ranged may use them too) — and a 深水区 under an active 特制水上平台 (`waterPlatform`, "在水上建立可以部署任意单位的平台"; act1 m05 only — the prep's deploy map only: the sim does not model the canoes, so its Grid keeps those tiles NONE, automatic placements avoid them and the path tie-break counts them as non-blockable [ASSUMED, weight 0 this season]) —, `rangedOnly` = high ground / active platforms; tiles under active crates/mounds and the 深水区 excluded. `bossLeft` / `bossRight` (field coordinates, boss rows 1–5) are what a player may deploy on in a boss round's prep: board (r, c) = boss (r − 7, c) / the right half mirrored (r − 7, 20 − c) (server/match/board.js `field`, user playtest #5 item 7) |
| `groundPaths` / `groundPathsWithDevices` | `{"12,10->9,2":[[12,10],[12,9],…,[12,3],[11,3],[10,3],[9,3],[9,2]]}` | the ground route of the sim's own `server/sim/grid.js` flow field (research 08 §3.4: 4-direction SPFA from the goal, crates cost 1000, Bresenham line-of-sight smoothing — official route lengths — kept unless the remake's (0.1.0's) road-over-floor preference route crosses strictly fewer non-blockable floor / gate / 深水区 tiles — the fewest among equal-length chains, smoothing that never cuts across floor its grid route does not walk; a corner touch does not count as a crossing — e.g. 战场#01's lower gate takes the col-8 road, not the col-9 floor lane, while 战场#04's keeps the official diagonal from row 9 into row 10; DESIGN §14 corrections, test/sim/pathing-official.test.js) as the tiles an enemy crosses, over the whole map, without / with the match-start devices (crates cost 1000, platforms / mounds blocking [ASSUMED]); the battle re-reads its flow fields live |
| `groundPaths` / `groundPathsWithDevices` | `{"12,10->9,2":[[12,10],[12,9],…,[12,3],[11,3],[10,3],[9,3],[9,2]]}` | the ground route of the sim's own `server/sim/grid.js` flow field (research 08 §3.4: 4-direction SPFA from the goal, crates cost 1000, Bresenham line-of-sight smoothing — official route lengths — kept unless the remake's (0.1.0's) road-over-floor preference route crosses strictly fewer non-blockable floor / gate / 深水区 tiles and no more 深水区 (GitHub #375) — the fewest among equal-length chains, smoothing that never cuts across floor its grid route does not walk; a corner touch does not count as a crossing — e.g. 战场#01's lower gate takes the col-8 road, not the col-9 floor lane, while 战场#04's keeps the official diagonal from row 9 into row 10; DESIGN §14 corrections, test/sim/pathing-official.test.js) as the tiles an enemy crosses, over the whole map, without / with the match-start devices (crates cost 1000, platforms / mounds blocking [ASSUMED]); the battle re-reads its flow fields live |
| `options`, `config` | `{"characterLimit":8,"moveMultiplier":0.5}`, `{…configBlackBoard strings…}` | |
Glyph legend (`rows`):
+16 -3
View File
@@ -190,7 +190,14 @@ a part) is not, even when it counts for LP (`counted`).
`RouteSpec` accepts data/waves.json routes (`{motion, start, end, checkpoints:[[r,c]…], steps:[{t:'move',p},{t:'wait',s},{t:'disappear'},{t:'appear',p}]}`)
and research routes (`{m, s, e, cp:[['MOVE',r,c]…]}`). `spawnsFromTemplate(waveEntry, {mods})` (simdata.js) converts a
template into `{ routes, spawns, maxPlayTime, overrides, extraRoutes }` (non-spawn `action` entries are skipped; `unharmful`
and `tag:'part'` spawns don't count in `total`).
and `tag:'part'` spawns don't count in `total`). The boss templates' 传送门 reads as "reappear on the far side" (GitHub
#336, PR #337): four official routes write the crossing as `disappear → wait → move` to the exit with no `appear` (their
twins spell the `appear` out), so normalizeRoute re-inserts the exit `appear` ahead of that move — otherwise the enemy
walks the rest of its route hidden and leaks unseen (test/sim/portal-appear.test.js). A route that ENDS on an entrance
(`tile_telin`; the boss / Hidden Core circuits end on [1,3] / [1,17]) does not leak there: the enemy vanishes, comes
out of the far exit (`tile_telout`; both entrances feed [5,10], the pairing of every explicit `disappear`/`appear` pair
of the data) after 3 s hidden inside — the wait of the explicit crossings (85 of 95) — and walks on to the blue door
on the entrance's side, where it leaks (ai.js portalPickup; [ASSUMED] the pairing and the 3 s).
A `bounty` pays `coins` once, when the enemy really dies (not a knock-out it survives; a leak pays nothing), to
`Battle._bountyPayee`: the player of the operator or summon that dealt the blow, if that player is in the battle; any
@@ -201,13 +208,19 @@ Leader parts (`tag:'part'`) pass damage to their leader with `loseHp(leader, sha
(无来源, credited to the attacker's `bossDamage`): `PART_TRANSFER` 1 for 斩胄之剑 / 破胄之锤 (`BLADE_TRANSFER`, the same
constant) and 碎铳之簧 (PRTS "受到伤害时令假想敌:胄/铳受到等量的无来源生命流失"; DESIGN §20.10, §20.13). Content may replace a part mid-battle: every 剑/锤 sortie (content/bosses.js `kitBlade`)
ends by spawning a new 初始模式 copy on its level branch route (`left_hand_origin` / `right_hand_origin`) with the old
HP, then `kill(old, null)` — uncounted, no bounty; the client sees a `die` and a `spawn`.
HP, then `kill(old, null)` — uncounted, no bounty; the client sees a `die` and a `spawn`. The Hidden Core 铳's 【末日布道】
puts every 碎铳之簧 into 追逐模式 for its whole 5 s gain (`dog_duration`; invulnerable, no ordinary attacks): the spring
keeps following the casting gun after reaching it (PR #347; until 0.2.1 arrival or 1 tile ended the chase), and each
gun casts on its own data timer (20 s after it spawns, then every 45 s), so on a pair field the later call retargets
every spring to that gun for a new 5 s (PRTS: 「持续召唤场上所有“碎铳之簧”向自身移动」).
WALK legs pathfind on the stage grid inside the rect with the official flow field (grid.js: 4-direction SPFA from the
destination, crates cost 1000, then Bresenham line-of-sight smoothing — research 08 §3.4). The official route stays
unless 0.1.0's road-over-floor preference route (the fewest non-blockable tiles among equal-length chains, a line of
sight that never covers floor — diagonal-step corners included — its grid route does not walk; user playtest #2 item 2)
crosses strictly fewer non-blockable tiles (floor / gate lanes); "crosses" = passes through the tile's interior, a
crosses strictly fewer non-blockable tiles (floor / gate lanes) and no more 深水区 (GitHub #375: on 战场#08(下半) the
patrolling 铳 / 卢西恩 waded through two water tiles to dodge one floor tile; the official upper road crosses one);
"crosses" = passes through the tile's interior, a
corner touch does not count (`grid.js` `segmentTiles`; community report D5 after 0.1.0: 战场#04's lower-gate enemies cut
diagonally from row 9 into row 10 as officially), and on equal counts the official route stays.
test/sim/pathing-official.test.js lists the 21 of 154 stage routes that still differ from the official ones (战场#01's
+1 -1
View File
@@ -110,7 +110,7 @@ Piece = { uid, kind: 'chess'|'item'|'token', id, golden: bool, tier, items: [ {
**Corrections from research 08/09 (normative):**
- Waves: official generator (research 08 §2): 3 of 6 types, each owns exactly 3 of the 15 round slots, SPECIAL the rest (shuffled per match); per round one type + one weighted special entry (half by round); each template SPAWN **action** becomes `clamp(roundHalfEven(n·P(t)/P(new)), 1, 5)` units over the same window; placeholder actions of the other movement class are skipped. Stage per match from the mode's list (标准 = 战场#01 only, §6.5). No custom balance multipliers (`data/tuning.json` keeps only titles); co-op boss pool = `bloodPoint[difficulty]` unscaled.
- Ground pathing: official SPFA-style 4-direction search from the goal with crates as cost-1000 tiles, then straight-line smoothing (research 08 §3.4). act1 m02 crates #001–#004 inactive in 下半. Remake refinement (user playtest #2, narrowed by §21.15): route lengths and routes stay the official ones unless 0.1.0's road-over-floor preference route crosses strictly fewer non-blockable (floor / gate / 深水区) tiles — a corner touch is not a crossing; ties keep the official route. E.g. 战场#01's lower-gate enemies take the col-8 road, while 战场#04's keep the official diagonal from row 9 into row 10 (server/sim/grid.js header; test/sim/pathing-official.test.js, pathing-blockable.test.js).
- Ground pathing: official SPFA-style 4-direction search from the goal with crates as cost-1000 tiles, then straight-line smoothing (research 08 §3.4). act1 m02 crates #001–#004 inactive in 下半. Remake refinement (user playtest #2, narrowed by §21.15): route lengths and routes stay the official ones unless 0.1.0's road-over-floor preference route crosses strictly fewer non-blockable (floor / gate / 深水区) tiles and no more 深水区 (GitHub #375) — a corner touch is not a crossing; ties keep the official route. E.g. 战场#01's lower-gate enemies take the col-8 road, while 战场#04's keep the official diagonal from row 9 into row 10 (server/sim/grid.js header; test/sim/pathing-official.test.js, pathing-blockable.test.js).
- Observing: no watching while your own normal battle runs; after it ends (and in prep) tap a teammate → 前往查看 (local replica, fast-forwarded). ‹ › only inside 联防/FA fields; FA: own pair only; eliminated: free.
- Prep interactions: tap a unit → underframe with 出售 +1 (and 撤退 on board); items/Arts → 销毁; shop and upgrade are two-tap (select → confirm; the remake's 机变 cards too, §18.2); strategy draft forbids a strategy already taken by a teammate (队友已选).
- Emotes: 6 themes × 6 battle emotes (research 09 §4.2), image-only bubbles beside the sender avatar, 1 s cooldown, 3 s bubble; `g.emote {id}` with the official emoji id.
+3 -2
View File
@@ -352,8 +352,9 @@ reported the 战场#01 lower-gate enemies walking up the col-9 floor lane, where
`server/sim/grid.js` keeps every official route LENGTH and the official route itself unless the 2026-09-29
road-over-floor preference route (equal-length ties to the chain with the fewest non-blockable floor / gate tiles,
smoothing that never cuts across floor its grid route does not walk, the corner tiles of a diagonal step included)
crosses strictly fewer non-blockable tiles; in that comparison a segment that only touches a floor tile's corner does
not cross it. Used alone (0.1.0) the preference also bent official diagonals into L shapes — the D5 report after
crosses strictly fewer non-blockable tiles and no more 深水区 (GitHub #375, 0.2.2: the 深水区 is non-blockable too, and
on 战场#08(下半) the patrolling leaders had waded through two water tiles to dodge one floor tile); in that comparison a
segment that only touches a floor tile's corner does not cross it. Used alone (0.1.0) the preference also bent official diagonals into L shapes — the D5 report after
0.1.0: on 战场#04 the lower-gate enemies walked (9,10) → (9,8) → (10,8) instead of the official diagonal (9,10) →
(10,7), which only brushes the corner of the floor (10,9). Lanes that still differ from the table above (21 of the 154
stage × gate × field routes, test/sim/pathing-official.test.js):
+45 -6
View File
@@ -28,10 +28,11 @@
// (PRTS 状态机: an enemy's COMBAT state ends with its attack, not with its blocker); the candidates pass the
// enemy's own rule (`e.profile.canTarget`) and are ordered
// blocker → taunt → latest deployed (targeting.js sortAllyTargets). An enemy's damage type is its data's unless content
// arms it (`e.profile.dmgType`: 转译基底's forms, whose data never attacks). Reaching the final leg's end = leak. A `fear` (恐惧) status suspends the route: the
// enemy runs between random checkpoints away from the fear's source (fear.js moveFeared; a self-inflicted fear
// flutters inside its own tile); an `attract` (诱导) status walks it to the status point instead (moveAttracted);
// both re-plan the route when released (恐惧 outranks 诱导).
// arms it (`e.profile.dmgType`: 转译基底's forms, whose data never attacks). Reaching the final leg's end = leak — on a
// portal entrance it is a teleport to the far exit and a walk to that side's blue door instead (portalPickup). A `fear`
// (恐惧) status suspends the route: the enemy runs between random checkpoints away from the fear's source (fear.js
// moveFeared; a self-inflicted fear flutters inside its own tile); an `attract` (诱导) status walks it to the status
// point instead (moveAttracted); both re-plan the route when released (恐惧 outranks 诱导).
import { ATTACK_PAUSE, ALLY_COLLIDER_RADIUS, MOVE_SCALE, PROJECTILE_SPEEDS, PROJECTILE_SPEED, BOOMERANG_RETURN_SPEED, COLS, CHAIN_RADIUS } from './constants.js';
import { sortEnemyTargets, sortAllyTargets, canTargetEnemy, canTargetAlly, tileKeyOf } from './targeting.js';
@@ -634,7 +635,10 @@ function advanceRoute(b, e, dt, R, standing = false) {
let guard = 16;
while (budget > 1e-9 && guard-- > 0 && e.alive) {
const leg = R.legs[R.legIdx];
if (!leg) { b.leak(e); return; }
if (!leg) {
if (!portalPickup(b, e)) { b.leak(e); return; }
continue;
}
if (leg.t === 'wait') {
if (R.waitLeft == null) R.waitLeft = leg.time;
const use = Math.min(budget, R.waitLeft);
@@ -684,13 +688,48 @@ function advanceRoute(b, e, dt, R, standing = false) {
}
budget = dist / speed;
if (R.pts && R.ptIdx >= R.pts.length) {
if (leg.final) { b.leak(e); return; }
if (leg.final) {
if (!portalPickup(b, e)) { b.leak(e); return; }
}
R.legIdx++;
R.pts = null;
}
}
}
/**
* Boss-field portal pickup (tile_telin / tile_telout; GitHub #336, PR #337 by @2321Robin, both with a recording of the
* official game): a route that ENDS on a portal entrance does not leak there — the enemy is teleported out of the far
* exit and walks on to the blue door on the entrance's side, where it leaks. The boss / Hidden Core circuits end on
* an entrance ([1,3] / [1,17]; 137 routes of data/waves.json); their mid-route crossings are the routes' own DISAPPEAR /
* WAIT / APPEAR steps, every MOVE onto an entrance being followed by a DISAPPEAR, so only the route's end needs this.
* Appends vanish → wait → reappear → walk-to-door legs to the live route and returns true (false: not on an entrance,
* or no exit in the field). The exit is the telout farthest from the entrance, the door the end tile nearest to it:
* every explicit pair of the data (95, routes and extra routes) reads [1,3] / [1,17] → [5,10] — the farther of the two
* telouts [5,10] / [2,10] of the battle stages — and the doors [2,2] / [2,18] stand beside the entrances. Inside the
* portal the enemy stays hidden PORTAL_WAIT s, the wait of the explicit crossings of the same tiles (WAIT 3 s in 85 of
* the 95; 5 s in 9, 1 s in 1). [ASSUMED] the pairing (no tile carries a link) and the 3 s (no source times this one).
*/
const PORTAL_WAIT = 3;
function portalPickup(b, e) {
const r = Math.round(e.y), c = Math.round(e.x);
if (!b.grid.inBounds(r, c) || b.grid.tile(r, c).special !== 'telin') return false;
const outs = b.grid.specialTiles('telout');
if (!outs.length) return false;
const dist = (p) => hypot(p[0] - r, p[1] - c);
const out = outs.reduce((a, x) => (dist(x) > dist(a) ? x : a));
const R = e.route;
R.legs.splice(R.legIdx + 1, 0, { t: 'disappear' }, { t: 'wait', time: PORTAL_WAIT }, { t: 'appear', r: out[0], c: out[1] });
const ends = b.grid.specialTiles('end');
if (ends.length) {
const door = ends.reduce((a, x) => (dist(x) < dist(a) ? x : a));
R.legs.push({ t: 'move', r: door[0], c: door[1], final: true });
}
R.pts = null;
R.tailVersion = -1; // the appended tail invalidates the cached remaining-distance suffixes
return true;
}
/**
* How long an enemy stands for one attack: an unblocked ranged enemy for its whole clip (GitHub #58 — the owner's
* decision of 2026-10-04 from first-hand memory of the official game: a ranged enemy stops for each attack's animation
+12 -4
View File
@@ -640,8 +640,11 @@ function kitGun(ab, e, b) {
});
if (s2) list.push({
cd: s2.cd, icd: s2.icd, cond: (b2) => b2.enemies.some((o) => o.alive && isSpring(o)),
fire(b2, e2) { // 【末日布道】 springs dash to 铳, invulnerable, trampling operators
// PRTS “碎铳之簧” 追逐模式 "不进行普通攻击": `disarm` for the dash (until 0.1.3 it kept shooting while it ran)
fire(b2, e2) { // 【末日布道】 springs chase 铳 for the whole 5 s gain, invulnerable, trampling operators (kitSpring)
// PRTS “碎铳之簧” 追逐模式 "不进行普通攻击": `disarm` for the dash (until 0.1.3 it kept shooting while it ran).
// Each gun casts on its own data timer, and a cast takes every spring — on a pair field the later of the two
// calls retargets them all to its gun (handbook 「持续召唤场上所有“碎铳之簧”向自身移动」; no stagger, no lock while
// a chase runs: PR #347's pair coordination is not taken, the data has neither)
for (const sp of b2.enemies) {
if (!sp.alive || !isSpring(sp) || !sp.mem.ab) continue;
sp.mem.ab.dash = { until: b2.time + (s2.bb.dog_duration ?? 0), mul: 1 + (s2.bb.move_speed ?? 0), gun: e2, hit: new Set() };
@@ -709,11 +712,16 @@ function kitSpring(ab, e) {
if (!P.up && P.downAt != null && b.time - P.downAt >= regen) raise(b);
const d = e2.mem.ab.dash;
if (!d) return;
// 追逐模式 lasts the whole gain (PRTS 末日布道 "令全场的“碎铳之簧”获得5秒增益…增益期间切换为追逐模式"; “碎铳之簧”
// 追逐模式 "持续追踪令其进入该形态的场上的假想敌:铳移动" — no arrival clause, unlike the 铳's own 冲锋模式): reaching
// the gun does not end it, the spring keeps following the gun until the 5 s are over (PR #347 by @CXUtk; until
// 0.2.1 it ended on arrival or within 1 tile). The casting gun dead: the nearest other gun; none left on the
// field: it holds still, the gain running on.
const g = d.gun && d.gun.alive ? d.gun : gun(b);
const arrived = !g || stepToward(e2, g.x, g.y, e2.s.moveSpeed * d.mul * MOVE_SCALE * dt) || hypot(g.x - e2.x, g.y - e2.y) < 1;
if (g) stepToward(e2, g.x, g.y, e2.s.moveSpeed * d.mul * MOVE_SCALE * dt);
// 追逐模式 "对进入自身0.35半径范围内的我方单位(包括飞行单位)造成一次攻击力100%的物理普通伤害" (until 0.1.3: radius 0.5)
for (const u of areaAllies(b, e2, e2.x, e2.y, CHARGE_RADIUS)) if (!d.hit.has(u)) { d.hit.add(u); hurt(b, e2, u, e2.s.atk, 'phys'); }
if (arrived || b.time >= d.until) { e2.mem.ab.dash = null; b.removeBuff(e2, 'boss:dash'); if (e2.route) e2.route.pts = null; }
if (b.time >= d.until) { e2.mem.ab.dash = null; b.removeBuff(e2, 'boss:dash'); if (e2.route) e2.route.pts = null; }
},
},
// while shielded, every (spCost+1)-th attack (enemy SP +1 per attack, as 粉碎攻坚手's official text) adds the shield's
+4 -1
View File
@@ -400,7 +400,10 @@ function applyReader(battle, it) {
else battle.removeBuff(u, key);
break;
case 'act1autochess_gar_eff_respawnTimeByBond':
if (k > 0) S.passiveBuff(battle, u, key, { redeployMul: Math.max(0.05, 1 + S.num(it.bb.respawn_time, 0) * k) });
// 耀骑士临光's 144: −1.5 % / −3 % per 3 卡西米尔 layers with no minimum in the blackboard (divide_num, respawn_time
// only) — clamped at 0 like every other redeploy multiplier, so a knock-out timer can reach 0 s (GitHub #370;
// until 0.2.1 a 0.05 floor kept it at 5 % of the base time from 192 layers, 96 for the elite)
if (k > 0) S.passiveBuff(battle, u, key, { redeployMul: Math.max(0, 1 + S.num(it.bb.respawn_time, 0) * k) });
else battle.removeBuff(u, key);
break;
case 'act2autochess_gar_eff_attrByBond_add_onstart': {
+22 -11
View File
@@ -26,8 +26,7 @@
// 0.1.0: on 战场#04 活性源石 the lower-gate enemies must cross diagonally from row 9 into row 10 as officially, not
// walk two tiles left of the gate and then straight up). A tile is non-blockable when it is walkable but not LOW
// ground buildable for melee (floor / gate / goal / teleport / 深水区 tiles — `blockable()` false; no gate route of 战场#08
// changed when its 深水区 became non-buildable, a few off-route smoothed steps of its boss field, rows 4–5, go around the
// water instead of cutting across it). Each field holds two candidate
// changed when its 深水区 became non-buildable). Each field holds two candidate
// pointers per tile, both with the official `dist` (so the crate cost 1000 and every grid route length stay official):
// * official: the plain algorithm above;
// * preference (0.1.0's): the SPFA relaxes on (dist, pen) lexicographically, `pen` = non-blockable tiles on the chain
@@ -36,7 +35,10 @@
// only on the tile's own raw chain between its two ends, so it never slips past a road tile beside floor its grid
// route does not walk.
// `next[tile]` is the official pointer unless the preference pointer leads to a route CROSSING strictly fewer
// non-blockable tiles (`cost`, counted to the destination in increasing `dist` order). A segment crosses the tiles
// non-blockable tiles (`cost`, counted to the destination in increasing `dist` order) and no more 深水区 (`deep`): 深水区
// is non-blockable itself, so without that test the preference waded into the pool to dodge floor (GitHub #375: on
// 战场#08(下半) the patrolling 铳 / 卢西恩 took the lower road (3,9) → (2,3) through two water tiles, one floor tile fewer
// than the official upper road with one). A segment crosses the tiles
// whose interior it passes through (`crossTiles`, exact geometry); one it only touches at a corner point is not crossed
// — the first version counted the corner tiles there too and so bent official diagonals into L shapes (D5). On equal
// counts the official pointer stays, except where the preference one merely skips it (the official waypoint lies on
@@ -166,9 +168,12 @@ export class Grid {
}
/** 1 = walkable terrain an operator cannot block on (floor, gate, goal, teleport…): the path tie-break penalty */
this.unblockable = new Uint8Array(ROWS * COLS);
/** 1 = walkable 深水区 (terrain 'deepsea': tile_deepsea / tile_deepwater): the tie-break never walks into more of it */
this.deepWater = new Uint8Array(ROWS * COLS);
for (let k = 0; k < ROWS * COLS; k++) {
const t = this.tiles[k];
this.unblockable[k] = t.pass === 'ALL' && !isBlockableTile(t) ? 1 : 0;
this.deepWater[k] = t.pass === 'ALL' && t.terrain === 'deepsea' ? 1 : 0;
}
/** obstacle bits per tile (OB_BLOCK | OB_CRATE) */
this.obstacle = new Uint8Array(ROWS * COLS);
@@ -272,10 +277,10 @@ export class Grid {
_buildField(er, ec, allowDiagonal, ignore) {
const N = ROWS * COLS;
const f = { dest: -1, dist: null, parent: null, pen: null, next: null, official: null, cost: null, len: null, version: this.version, allowDiagonal, ignore };
const f = { dest: -1, dist: null, parent: null, pen: null, next: null, official: null, cost: null, deep: null, len: null, version: this.version, allowDiagonal, ignore };
if (!this.inBounds(er, ec)) {
const none = new Int32Array(N).fill(-1);
return Object.assign(f, { dist: none, parent: none, next: none, official: none, pen: new Int32Array(N), cost: new Int32Array(N) });
return Object.assign(f, { dist: none, parent: none, next: none, official: none, pen: new Int32Array(N), cost: new Int32Array(N), deep: new Int32Array(N) });
}
const dest = er * COLS + ec;
f.dest = dest;
@@ -300,25 +305,30 @@ export class Grid {
(n) => { for (let x = n, g = N; x >= 0 && g-- > 0; x = pref.parent[x]) onChain[x] = n; });
// per tile, in increasing distance: the official pointer unless the preference one crosses strictly fewer
// non-blockable tiles on the way to the destination — or as few while only skipping the official waypoint o, which
// then lies on the straight line to it and leads there (the same walk, one waypoint fewer)
// then lies on the straight line to it and leads there (the same walk, one waypoint fewer) — and no more 深水区
// than the official one (GitHub #375)
const order = [];
for (let k = 0; k < N; k++) if (dist[k] >= 0) order.push(k);
order.sort((a, b) => dist[a] - dist[b] || a - b);
const through = (a, b) => { let n = -unb[a]; crossTiles(a, b, (r, c) => { n += unb[r * COLS + c]; }); return n; };
const deepW = this.deepWater;
const wet = (a, b) => { let n = -deepW[a]; crossTiles(a, b, (r, c) => { n += deepW[r * COLS + c]; }); return n; };
const next = new Int32Array(N).fill(-1);
const cost = new Int32Array(N);
const deep = new Int32Array(N);
for (const k of order) {
const o = nextO[k], p = nextP[k];
if (o < 0) continue; // the destination
let use = o, best = through(k, o) + cost[o];
let use = o, best = through(k, o) + cost[o], bestWet = wet(k, o) + deep[o];
if (p >= 0 && p !== o) {
const cp = through(k, p) + cost[p];
if (cp < best || (cp === best && next[o] === p && onSegment(k, o, p))) { use = p; best = cp; }
const cp = through(k, p) + cost[p], wp = wet(k, p) + deep[p];
if (wp <= bestWet && (cp < best || (cp === best && next[o] === p && onSegment(k, o, p)))) { use = p; best = cp; bestWet = wp; }
}
next[k] = use;
cost[k] = best;
deep[k] = bestWet;
}
return Object.assign(f, { dist, parent: pref.parent, pen: pref.pen, next, official: nextO, cost });
return Object.assign(f, { dist, parent: pref.parent, pen: pref.pen, next, official: nextO, cost, deep });
}
/**
@@ -434,8 +444,9 @@ export class Grid {
* @property {Int32Array} pen non-blockable tiles on that raw chain (tile included, destination excluded)
* @property {Int32Array} official smoothed parent of the pure official algorithm
* @property {Int32Array} next the tile to walk straight toward (official, or the preference pointer when it crosses fewer
* non-blockable tiles — see the module header)
* non-blockable tiles and no more 深水区 — see the module header)
* @property {Int32Array} cost non-blockable tiles the route of `next` crosses from the tile (excluded) to the destination
* @property {Int32Array} deep 深水区 tiles the route of `next` crosses from the tile (excluded) to the destination
* @property {Float64Array|null} len memoised smoothed length (fieldLength)
*/
+21
View File
@@ -394,6 +394,7 @@ export function normalizeRoute(r) {
const t = String(st.t ?? st.type ?? 'move').toUpperCase();
cps.push({ type: normCpType(t), pos: pairOf(st.p ?? st.pos) ?? [0, 0], time: num(st.s ?? st.time, 0) });
}
portalAppear(cps);
return { motion, start, end, checkpoints: cps };
}
for (const cp of r.checkpoints ?? r.cp ?? []) {
@@ -406,9 +407,29 @@ export function normalizeRoute(r) {
cps.push({ type: normCpType(cp.type), pos: pairOf(cp.pos ?? cp.position ?? [cp.row, cp.col]) ?? [0, 0], time: num(cp.time ?? cp.t, 0) });
}
}
portalAppear(cps);
return { motion, start, end, checkpoints: cps };
}
/**
* The boss / Hidden Core templates' 传送门 (GitHub #336, PR #337 by @2321Robin): the twin routes spell a crossing as
* DISAPPEAR → WAIT → APPEAR[exit], but four official routes leave the APPEAR out and go straight to MOVE-to-the-exit
* (act1autochess_h07_02 #4, act1autochess_h08_02 #5 / #9, act2autochess_h07_05 #6; the level files themselves have no
* APPEAR_AT_POS there). Taken literally, such a route walks the enemy's whole remaining run hidden (untargetable,
* unblockable) and it leaks unseen — the crossing must read "reappear on the far side". Re-insert the exit APPEAR
* ahead of that MOVE, at the MOVE's own target, where the twins appear ([5,10]; the leftover MOVE becomes a
* zero-length hop, and the hop no longer counts as walking distance for the targeting order). A DISAPPEAR followed by
* an APPEAR, or by no MOVE, stays as it is; idempotent.
*/
function portalAppear(cps) {
for (let i = 0; i < cps.length; i++) {
if (cps[i].type !== 'DISAPPEAR') continue;
let j = i + 1;
while (j < cps.length && cps[j].type === 'WAIT') j++;
if (cps[j] && cps[j].type === 'MOVE') cps.splice(j, 0, { type: 'APPEAR', pos: cps[j].pos.slice(), time: 0 });
}
}
function normCpType(t) {
const s = String(t ?? 'MOVE').toUpperCase();
if (s.includes('DISAPPEAR')) return 'DISAPPEAR';
+27
View File
@@ -465,6 +465,33 @@ test('ADD_BOND 耀骑士临光 145 / 160 (real chess): self + front get 144 (red
}
});
test('respawnTimeByBond 144 has no 5% floor (#370): redeployMul is max(0, 1 + respawn_time * steps)', () => {
for (const [gid, rt] of [['garrison_144_a', -0.015], ['garrison_144_b', -0.03]]) {
assert.equal(GR(gid).bb.respawn_time, rt);
assert.deepEqual(Object.keys(GR(gid).bb).sort(), ['divide_num', 'respawn_time']);
// 96 / 192 layers: the old 0.05 floor bound the elite / the normal form (raw 0.04); 300: below zero
for (const layers of [9, 96, 192, 300]) {
const k = Math.floor(layers / 3);
const want = Math.max(0, 1 + rt * k);
const h = battle({
units: [{ id: 'op', g: [gid], row: 10, col: 4, o: { stats: { respawnTime: 100 } } }],
bonds: { kazimierzShip: B(layers) },
});
const u = h.unit('op');
approx(u.findBuff(`gar:${gid}`).mods.redeployMul, want, `${gid} @ ${layers}`);
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
assert.equal(u.alive, false);
approx(u.respawnAt - u.deathAt, 100 * want, `${gid} timer @ ${layers}`);
if (want === 0) {
h.step(2);
assert.ok(u.alive && u.deployed, `${gid} @ ${layers}: a 0 s timer brings it straight back`);
assert.equal(h.b.errorCount, 0);
}
}
cover(gid);
}
});
// ---------------------------------------------------------------------------------------------------------------------
// 魔王
+156
View File
@@ -0,0 +1,156 @@
// 隐秘核心铳's 【末日布道】 (PR #347 by @CXUtk): every 碎铳之簧 chases the casting gun for the gain's whole 5 s — PRTS 末日布道
// "令全场的“碎铳之簧”获得5秒增益:移动速度+800%,无敌,增益期间切换为追逐模式", “碎铳之簧” 追逐模式 "持续追踪令其进入该形态的场上的
// 假想敌:铳移动" (no arrival clause). Until 0.2.1 reaching the gun (or 1 tile from it) ended the chase. Each gun casts on its
// own data timer (initCooldown 20, cooldown 45 — no stagger between the two guns of a pair field, no lock while a chase
// runs: the data has neither, and the handbook's 「向自身」 means the later call takes the springs).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { makeBattle, chessRec, checkInvariants } from '../helpers/battleHarness.js';
import * as enemies from '../../server/sim/content/enemies.js';
import * as bosses from '../../server/sim/content/bosses.js';
const W = JSON.parse(fs.readFileSync(new URL('../../data/waves.json', import.meta.url), 'utf8'));
const E = JSON.parse(fs.readFileSync(new URL('../../data/enemies.json', import.meta.url), 'utf8'));
const GUN = 'enemy_9017_achunt_2';
const SPRING = 'enemy_9020_actrpc';
const wall = chessRec({ id: 'pursuit_wall', profession: 'TANK', stats: { maxHp: 1e9, atk: 0, def: 1000, blockCnt: 3 }, rangeGrid: [[0, 0]], skill: null });
/** The real h08_02(_s) template (leader + parts only, or no spawns with `manual`), one non-attacking wall per side. */
function arena({ solo = false, manual = false, cooldown = null } = {}) {
const tpl = structuredClone(W[solo ? 'act1autochess_h08_02_s' : 'act1autochess_h08_02']);
tpl.spawns = manual ? [] : tpl.spawns.filter((s) => s.tag === 'boss' || s.tag === 'part');
if (cooldown != null) {
tpl.overrides[GUN] ??= { skills: structuredClone(E[GUN].skills) };
const skill = tpl.overrides[GUN].skills.find((s) => s.prefabKey === '2');
skill.initCooldown = 0;
skill.cooldown = cooldown;
}
return makeBattle({
kind: 'hidden', waveTemplate: tpl, content: 'generic', extraContent: [enemies, bosses], seed: 7,
autoFinish: false, timeLimit: 300, recordEvents: false, hooks: [],
defs: { chess: { pursuit_wall: wall } }, kits: { pursuit_wall: () => ({ trait: { noAttack: true } }) },
sharedBoss: { hp: 1e12, maxHp: 1e12, damage(pid, n) { this.hp -= n; } },
players: [
{ playerId: 'p1', seat: 0, side: 'L', colOffset: 0, units: [{ uid: 1, chessId: 'pursuit_wall', row: 3, col: 5, abs: true }] },
...solo ? [] : [{ playerId: 'p2', seat: 1, side: 'R', colOffset: 8, units: [{ uid: 2, chessId: 'pursuit_wall', row: 3, col: 15, abs: true }] }],
],
setup(b) { b.enemyOverrides = tpl.overrides; b.opts.waveTemplate = tpl; b.opts.bossMirror = !manual; },
});
}
/** Every 【末日布道】 call (a new chase object on the spring): { time, gun }. */
function recordCalls(h) {
const calls = [];
let last = null;
h.b.on('tick', () => {
const d = h.enemies().find((e) => e.defId === SPRING)?.mem.ab.dash;
if (!d || d === last) return;
last = d;
calls.push({ time: h.b.time, gun: d.gun });
});
return calls;
}
test('隐秘核心铳 pair: each gun calls on its own data timer (20 s after it spawns, then every 45 s); the later call takes the springs', () => {
const h = arena();
const spawned = new Map();
h.b.on('enemySpawn', (c) => { if (c.enemy.defId === GUN) spawned.set(c.enemy, h.b.time); });
const calls = recordCalls(h);
h.run(150);
assert.equal(spawned.size, 2, 'the pair field has two guns (the mirrored one)');
const skill = W.act1autochess_h08_02.overrides[GUN].skills.find((s) => s.prefabKey === '2');
for (const [gun, t0] of spawned) {
const own = calls.filter((c) => c.gun === gun).map((c) => c.time);
assert.equal(own.length, 3, 'three calls each in 150 s');
assert.ok(Math.abs(own[0] - t0 - skill.initCooldown) < h.TICK * 2, `first call ${skill.initCooldown} s after its spawn`);
for (let i = 1; i < own.length; i++) assert.ok(Math.abs(own[i] - own[i - 1] - skill.cooldown) < h.TICK * 2, `then every ${skill.cooldown} s`);
}
// the two mirrored guns call within a tick or two of each other (no half-cooldown stagger), the later one wins
for (let i = 1; i < calls.length; i += 2) assert.ok(calls[i].time - calls[i - 1].time < h.TICK * 3, 'the two calls come together');
assert.equal(h.b.errorCount, 0);
checkInvariants(h.b);
});
test('隐秘核心铳: reaching a moving gun keeps pursuit, invulnerability and disarm for the full 5 s', () => {
const h = arena({ solo: true, manual: true, cooldown: 45 });
h.step();
const gun = h.spawn(GUN, { pos: [3, 5], tag: 'boss', mods: { speedMul: 0 } });
const spring = h.spawn(SPRING, { pos: [3, 4.8], tag: 'part' });
h.step(2);
const d = spring.mem.ab.dash;
assert.ok(d, 'arriving next to the gun does not end the chase');
h.run(1);
gun.x = 6;
const oldX = spring.x;
h.run(1);
assert.ok(spring.x > oldX, 'it keeps following the gun when the gun moves');
assert.equal(spring.mem.ab.dash, d);
assert.ok(spring.s.flags.invulnerable && spring.s.flags.disarm);
h.run(d.until - h.b.time - h.TICK * 2);
assert.equal(spring.mem.ab.dash, d, 'still chasing just before the 5 s are over');
assert.ok(spring.s.flags.invulnerable && spring.s.flags.disarm);
h.run(h.TICK * 4);
assert.equal(spring.mem.ab.dash, null, 'over once the gain ends');
assert.ok(!spring.s.flags.invulnerable && !spring.s.flags.disarm);
assert.equal(h.b.errorCount, 0);
});
test('隐秘核心铳: the other gun\'s call during a chase retargets every spring to that gun for a new 5 s', () => {
const h = arena({ manual: true, cooldown: 45 });
h.step();
const left = h.spawn(GUN, { pos: [3, 5], tag: 'boss', mods: { speedMul: 0 } });
const right = h.spawn(GUN, { pos: [3, 15], tag: 'boss', mods: { speedMul: 0 } });
const call = right.mem.ab.list.find((a) => a.cd === 45 && a.fire);
call.left = 999; // the right gun's call is not ready yet
const spring = h.spawn(SPRING, { pos: [3, 10], tag: 'part' });
h.step(2);
const first = spring.mem.ab.dash;
assert.ok(first && first.gun === left, 'the first call takes the spring to the left gun');
h.run(1);
assert.equal(spring.mem.ab.dash, first, 'chasing the left gun');
call.left = 0; // the right gun's call comes ready mid-chase
h.step(2);
const second = spring.mem.ab.dash;
assert.ok(second && second !== first && second.gun === right, 'the later call takes the spring at once');
assert.ok(Math.abs(second.until - h.b.time - 5) < h.TICK * 3, 'for a new 5 s');
const oldX = spring.x;
h.run(1);
assert.ok(spring.x > oldX, 'it now runs to the right gun');
assert.ok(spring.s.flags.invulnerable && spring.s.flags.disarm);
assert.equal(h.b.errorCount, 0);
});
test('隐秘核心铳: with no gun left on the field the spring holds still, invulnerable and disarmed, until the gain ends', () => {
const h = arena({ solo: true, manual: true, cooldown: 45 });
h.step();
const gun = h.spawn(GUN, { pos: [3, 5], tag: 'boss', mods: { speedMul: 0 } });
const spring = h.spawn(SPRING, { pos: [3, 1], tag: 'part' });
h.step(2);
const d = spring.mem.ab.dash;
assert.ok(d, 'chasing');
h.b.kill(gun, null);
h.step();
const at = [spring.x, spring.y];
h.run(1);
assert.deepEqual([spring.x, spring.y], at, 'no gun to follow: it stands');
assert.equal(spring.mem.ab.dash, d, 'the chase is not over');
assert.ok(spring.s.flags.invulnerable && spring.s.flags.disarm);
h.run(d.until - h.b.time + h.TICK * 2);
assert.equal(spring.mem.ab.dash, null);
assert.ok(!spring.s.flags.invulnerable);
assert.equal(h.b.errorCount, 0);
});
test('隐秘核心铳: solo keeps the template initial cooldown and repeated call timing', () => {
const h = arena({ solo: true });
const calls = recordCalls(h);
h.run(100);
assert.equal(calls.length, 2);
const tpl = W.act1autochess_h08_02_s;
const spawnTime = tpl.spawns.find((s) => s.tag === 'boss').time;
const skill = (tpl.overrides[GUN]?.skills ?? E[GUN].skills).find((s) => s.prefabKey === '2');
assert.ok(Math.abs(calls[0].time - spawnTime - skill.initCooldown) < h.TICK * 2);
assert.ok(Math.abs(calls[1].time - calls[0].time - skill.cooldown) < h.TICK * 2);
assert.equal(h.b.errorCount, 0);
});
+281 -283
View File
@@ -142,75 +142,75 @@
"killed": 8,
"total": 8,
"unspawned": 0,
"bossHpLeft": 5311560,
"rngDraws": 839,
"bossHpLeft": 5301010,
"rngDraws": 783,
"errors": {"count":0,"kinds":[]},
"players": {
"p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":612448,"bossDmg":313008,"heal":16689,"deaths":0,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":937710,"bossDmg":372608,"heal":6945,"deaths":2,"layerGains":{}}
"p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":741043,"bossDmg":366117,"heal":19827,"deaths":1,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":819707,"bossDmg":330056,"heal":4781,"deaths":3,"layerGains":{}}
},
"units": [
["p1",1,"op","chess_char_5_19_b",2,"uniequip_002_mlynar",3907,1,9,0,183438,1132,168,53,3],
["p1",2,"op","chess_char_4_01_b",2,"uniequip_002_rmixer",5482.35,1,11,0,3903,11319,311,35,1],
["p1",3,"op","chess_char_5_14_b",1,"none",2174,1,7,0,30509,953,294,58,17],
["p1",4,"op","chess_char_5_17_b",1,"uniequip_002_f12yin",2738,1,13,0,10026,0,0,66,1],
["p1",5,"op","chess_char_5_15_b",1,"uniequip_002_thorn2",1170,1,1,0,79315,0,0,32,8],
["p1",6,"op","chess_char_5_20_b",2,"uniequip_002_aglina",2418.5,1,3,0,78933,0,0,44,4],
["p1",7,"op","chess_char_5_21_b",1,"uniequip_002_sntlla",2769.3,1,17,1,26033,0,0,43,2],
["p1",8,"op","chess_char_5_22_b",1,"uniequip_002_nymph",1611,1,19,2,168011,1748,1748,55,18],
["p1",9,"op","chess_char_5_23_b",1,"uniequip_002_reckpr",1610,1,5,0,0,0,14114,21,3],
["p1",10,"op","chess_char_4_02_b",2,"uniequip_002_mostma",1695,1,15,0,32280,1383,55,35,2],
["p2",11,"op","chess_char_4_11_b",1,"uniequip_002_cathy",836.62,1,12,0,0,3919,1500,0,2],
["p2",12,"op","chess_char_4_04_b",1,"uniequip_002_ines",3545.85,1,51,2,253580,496,458,287,8],
["p2",13,"op","chess_char_4_07_b",1,"uniequip_002_bpipe",2099.31,1,8,0,4500,1149,865,24,8],
["p2",14,"op","chess_char_4_16_b",2,"uniequip_002_texas2",2671.35,1,52,1,112288,6746,1602,147,13],
["p2",15,"op","chess_char_4_09_b",1,"uniequip_002_mizuki",2777.6,1,10,0,62072,0,0,54,3],
["p2",16,"op","chess_char_4_06_b",1,"uniequip_002_kroos2",1561,1,2,1,303642,0,0,181,3],
["p1",1,"op","chess_char_5_19_b",2,"uniequip_002_mlynar",3907,1,9,0,235262,2487,437,74,3],
["p1",2,"op","chess_char_4_01_b",2,"uniequip_002_rmixer",5482.35,1,11,0,12166,13761,3921,33,1],
["p1",3,"op","chess_char_5_14_b",1,"none",2174,1,7,0,24813,953,294,16,12],
["p1",4,"op","chess_char_5_17_b",1,"uniequip_002_f12yin",2738,1,13,0,5871,0,0,20,1],
["p1",5,"op","chess_char_5_15_b",1,"uniequip_002_thorn2",1170,1,1,0,123408,0,0,77,10],
["p1",6,"op","chess_char_5_20_b",2,"uniequip_002_aglina",2418.5,1,3,0,81805,0,0,44,4],
["p1",7,"op","chess_char_5_21_b",1,"uniequip_002_sntlla",2769.3,1,17,2,56882,1768,1768,100,3],
["p1",8,"op","chess_char_5_22_b",1,"uniequip_002_nymph",748.12,1,43,1,153989,3270,796,46,16],
["p1",9,"op","chess_char_5_23_b",1,"uniequip_002_reckpr",1610,1,5,0,0,0,12611,20,3],
["p1",10,"op","chess_char_4_02_b",2,"uniequip_002_mostma",1695,1,15,1,46847,0,0,34,2],
["p2",11,"op","chess_char_4_11_b",1,"uniequip_002_cathy",2172.64,1,50,0,0,5191,1078,0,1],
["p2",12,"op","chess_char_4_04_b",1,"uniequip_002_ines",3545.85,1,51,2,259014,0,0,279,8],
["p2",13,"op","chess_char_4_07_b",1,"uniequip_002_bpipe",2383,1,52,0,3678,2726,343,18,7],
["p2",14,"op","chess_char_4_16_b",2,"uniequip_002_texas2",2671.35,1,49,1,97836,2233,1258,153,10],
["p2",15,"op","chess_char_4_09_b",1,"uniequip_002_mizuki",2777.6,1,10,0,82406,0,0,73,4],
["p2",16,"op","chess_char_4_06_b",1,"uniequip_002_kroos2",1561,1,2,0,179138,0,0,231,3],
["p2",17,"op","chess_char_4_10_b",1,"uniequip_002_aroma",1664,1,4,0,0,0,0,0,0],
["p2",18,"op","chess_char_4_12_b",2,"uniequip_002_glady",1418.27,1,18,1,69028,3331,2517,58,3],
["p2",19,"op","chess_char_4_13_b",1,"uniequip_002_gnosis",1989,1,40,0,44456,1991,2,63,18],
["p2",20,"op","chess_char_4_14_b",1,"uniequip_002_lionhd",1626,1,6,0,88144,0,0,57,14],
["p2",18,"op","chess_char_4_12_b",2,"uniequip_002_glady",1003.26,1,18,0,72377,3328,2099,61,4],
["p2",19,"op","chess_char_4_13_b",1,"uniequip_002_gnosis",1989,1,40,0,62819,1991,2,81,22],
["p2",20,"op","chess_char_4_14_b",1,"uniequip_002_lionhd",1626,1,6,1,62440,0,0,47,13],
["p2",21,"token","token_10041_cathy_catsld",null,null,100,1,21,0,0,0,0,0,null],
["p2",22,"token","token_10041_cathy_catsld",null,null,100,1,22,0,0,0,0,0,null]
],
"enemies": {"enemy_1005_yokai_2":[6,6,0,3179,62190],"enemy_1430_lrrook":[2,2,0,0,802449],"enemy_9017_achunt":[2,0,0,30987,688440]},
"hooks": [1,50,6000,1312,1325,2789,2834,4861,11,26,131,125,21,35636,978,0,10,0,1,11,0,13,1],
"events": {"atk":1342,"deploy":40,"die":26,"dmg":2826,"engage":17,"fx":602,"heal":41,"skill":256,"spawn":32,"status":489},
"enemies": {"enemy_1005_yokai_2":[6,6,0,3956,62190],"enemy_1430_lrrook":[2,2,0,0,802449],"enemy_9017_achunt":[2,0,0,33752,698990]},
"hooks": [1,50,6000,1372,1410,2907,2944,3787,13,26,127,123,21,38164,1047,0,10,0,1,13,0,12,1],
"events": {"atk":1411,"deploy":40,"die":26,"dmg":2934,"engage":17,"fx":696,"heal":41,"skill":250,"spawn":32,"status":549},
"fx": {
"aoe": 38,
"aoe": 33,
"appear": 2,
"blink": 16,
"burst": 13,
"blink": 14,
"burst": 12,
"catShield": 1,
"charge": 8,
"charge": 7,
"counter": 21,
"crit": 88,
"dilemma": 3,
"crit": 89,
"dilemma": 4,
"disappear": 2,
"dodge": 1,
"explosion": 14,
"frostNova": 18,
"explosion": 13,
"frostNova": 22,
"garrisonGrant": 1,
"iceSpike": 39,
"overclock": 2,
"pull": 3,
"iceSpike": 89,
"overclock": 1,
"pull": 4,
"quadShot": 2,
"ripple": 13,
"ripple": 20,
"sentry": 6,
"sermon": 1,
"shadowWeave": 11,
"shield": 6,
"soul": 5,
"sp": 11,
"shadowWeave": 10,
"shield": 5,
"soul": 4,
"sp": 7,
"stealth": 8,
"swordRain": 96,
"swordStorm": 13,
"tornado": 3,
"tornadoPulse": 15,
"swordRain": 82,
"swordStorm": 10,
"tornado": 4,
"tornadoPulse": 20,
"weightless": 4,
"zone": 138
"zone": 197
},
"snaps": "7917cfe4 ec53e8ac 09d8b201 823345e1 5d4f2a95 9efdd7ee 56e0addd 78c7726c 4ee3695b 20aee5b5 ba3311c0 d4d127c9 0c500cac d842272b 4cbf4a87 2525db60 3291ce13 ecb45e94 c2e5c2d0 a4ac7943"
"snaps": "7917cfe4 719c3d1e 5fce9836 dcc6d1db c6372862 d28f9510 24fc36dd 451a8b51 a84bc802 17264489 87aca75c 241abfe9 0bdd4e23 94a753a7 f66cf2d6 27f37155 8c831d53 112e1ac2 a0065d70 7e66122d"
},
"boss-boss_2-solo": {
"about": "假想敌:铳 solo pool 3000000: chess_char_4_17_b chess_char_4_18_b chess_char_4_23_b chess_char_4_19_b chess_char_4_22_b chess_char_4_20_b chess_char_4_21_b chess_char_4_24_b chess_char_4_25_b chess_char_4_26_b",
@@ -227,27 +227,27 @@
"killed": 5,
"total": 7,
"unspawned": 0,
"bossHpLeft": 2761255,
"rngDraws": 127,
"bossHpLeft": 2757148,
"rngDraws": 130,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":5,"total":7,"perfect":false,"leaks":2,"leaksUncounted":0,"coins":0,"dmg":401784,"bossDmg":232315,"heal":40581,"deaths":1,"layerGains":{}}},
"players": {"p1":{"killed":5,"total":7,"perfect":false,"leaks":2,"leaksUncounted":0,"coins":0,"dmg":451604,"bossDmg":236418,"heal":39582,"deaths":1,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",2383.55,1,18,0,4821,19443,15985,82,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",6104,1,4,0,86351,0,0,79,4],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",2811,1,6,0,77745,6115,3711,91,4],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",2118,1,7,1,23391,7108,1936,80,7],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",2569,1,8,4,131322,8759,0,69,2],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",1419,1,9,0,16118,0,0,11,0],
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",2383.55,1,18,0,5154,18917,15557,82,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",6104,1,4,0,89239,0,0,82,4],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",2811,1,6,0,89910,6115,3639,104,4],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",2118,1,7,1,23474,7108,1915,82,7],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",2569,1,8,4,162219,8759,0,75,3],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",1419,1,9,0,23155,0,0,12,2],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",1584,1,10,0,0,1263,566,2,1],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",2178,1,1,0,62036,0,1129,85,6],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",695.86,1,3,0,0,1448,1057,0,3],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1522,1,5,0,0,0,16195,34,3]
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",2178,1,1,0,58453,0,1107,75,5],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1671,1,3,0,0,0,574,0,3],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1522,1,5,0,0,0,16224,34,3]
],
"enemies": {"enemy_1249_lysdb_2":[5,5,0,8138,107495],"enemy_1430_lrrook":[2,0,2,0,66943],"enemy_9017_achunt":[1,0,0,23716,238745]},
"hooks": [1,19,6000,580,580,1299,1898,5497,6,8,30,29,0,13846,99,5,8,2,1,8,0,6,2],
"events": {"atk":615,"deploy":11,"die":6,"dmg":1888,"engage":7,"fx":277,"heal":37,"leak":2,"skill":62,"spawn":18,"status":481},
"fx": {"bandage":3,"block":9,"burst":6,"charge":7,"dodge":2,"flame":7,"garrisonGrant":1,"healField":1,"jungleSoul":4,"mote":102,"redistribute":45,"reweave":3,"riposte":2,"rockslide":4,"sandChains":5,"sandChainsCharged":1,"shieldBreak":15,"slash":32,"spGift":5,"thorns":21,"truesilver":2},
"snaps": "a42f06d2 4a40e022 e95a4901 abb693b9 1c238b4b afb89449 d6c6b3df c846d6b3 fa04e7ce c4d85f2e 4a6790c9 effed23a 72e8e203 8146bf98 e8dfb61c 69d1b960 6d949949 0310d38f a1429900 b8610bee"
"enemies": {"enemy_1249_lysdb_2":[5,5,0,8138,107495],"enemy_1430_lrrook":[2,0,2,0,112690],"enemy_9017_achunt":[1,0,0,21742,242852]},
"hooks": [1,19,6000,595,595,1368,1966,5211,6,8,32,32,0,15401,91,5,8,2,1,8,0,6,2],
"events": {"atk":638,"deploy":11,"die":6,"dmg":1956,"engage":7,"fx":289,"heal":37,"leak":2,"skill":67,"spawn":18,"status":465},
"fx": {"appear":2,"bandage":3,"block":10,"burst":6,"charge":7,"disappear":2,"dodge":2,"featherArrow":2,"flame":7,"garrisonGrant":1,"healField":1,"jungleSoul":4,"mote":102,"redistribute":45,"reweave":3,"riposte":2,"rockslide":4,"sandChains":5,"shieldBreak":15,"slash":38,"spGift":5,"thorns":20,"truesilver":3},
"snaps": "a42f06d2 4a40e022 e95a4901 abb693b9 1c238b4b afb89449 d6c6b3df c846d6b3 7c93c566 120f2baa 5eba2341 475bd0e1 a82180cf 2ec0f97d b5acfe2a 942750af b2587ba1 f8bb84fe 0dee9c0d c305f7f7"
},
"boss-boss_3-pair": {
"about": "假想敌:管 pair pool 4000000: 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 | 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",
@@ -516,29 +516,29 @@
"killed": 5,
"total": 7,
"unspawned": 0,
"bossHpLeft": 3759559,
"rngDraws": 84,
"bossHpLeft": 3757752,
"rngDraws": 87,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":5,"total":7,"perfect":false,"leaks":2,"leaksUncounted":0,"coins":0,"dmg":713408,"bossDmg":440441,"heal":95170,"deaths":6,"layerGains":{}}},
"players": {"p1":{"killed":5,"total":7,"perfect":false,"leaks":2,"leaksUncounted":0,"coins":0,"dmg":731117,"bossDmg":442248,"heal":91813,"deaths":7,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",2497.84,1,23,2,19548,11293,12704,21,0],
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",2762.76,1,23,2,19548,11760,12983,21,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",5981.62,1,22,0,42568,20827,14636,11,5],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",2811,1,21,3,202794,41516,12031,43,1],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",3350.14,1,9,0,0,7920,0,0,0],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",2541.58,1,10,0,162116,8875,0,135,3],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",0,0,24,0,234028,2838,0,104,5],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",1584,1,11,0,0,4444,24160,29,2],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",2178,1,25,0,52353,7314,487,76,5],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1394.71,1,3,0,0,5589,5880,0,3],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1522,1,5,0,0,4444,25271,28,4],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",2811,1,21,3,202794,41516,12121,43,1],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",2350.14,1,9,0,785,9979,0,1,1],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",2061.88,1,24,0,177744,6029,0,120,3],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",0,0,25,0,234028,2838,0,104,5],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",1545.34,1,11,0,0,4444,18376,28,2],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",1748.63,1,26,0,53649,7314,487,76,5],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1394.71,1,3,0,0,6735,7186,0,3],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1522,1,5,0,0,4444,26024,27,5],
["-","~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,5940,107495],"enemy_1438_dspred":[5,3,2,31950,165472],"enemy_1521_dslily":[1,0,0,71001,440441]},
"hooks": [1,25,6000,565,565,958,1133,7303,11,13,28,28,0,13157,125,16,8,2,1,16,0,29,1],
"events": {"atk":770,"deploy":15,"die":11,"dmg":1115,"engage":6,"fx":335,"heal":105,"leak":2,"skill":59,"spawn":20,"status":479},
"fx": {"bandage":4,"block":15,"burst":29,"camouflage":1,"dodge":1,"featherArrow":5,"garrisonGrant":1,"grow":1,"healField":2,"jungleSoul":1,"mote":61,"redistribute":45,"reweave":3,"rockfall":18,"rockslide":5,"sandChains":5,"shieldBreak":18,"slash":53,"spGift":4,"tentacle":6,"thorns":48,"tide":5,"truesilver":3,"undying":1},
"snaps": "b3cecd10 d3d68301 c10d8554 c91066bb 2ac0bbdd cb65c8b1 e357c87a 1cfd4db1 1a8bd8d8 e639cb94 69f98f4a 92de5c02 51bb18c2 a8b02c45 54b09843 f60538cb 94407072 8c567dae 4c15135e a957f119"
"enemies": {"enemy_1010_demon_2":[2,2,0,5940,107495],"enemy_1438_dspred":[5,3,2,34519,181373],"enemy_1521_dslily":[1,0,0,69256,442248]},
"hooks": [1,26,6000,551,551,949,1120,8051,12,14,30,30,0,13414,126,18,8,2,1,17,0,28,2],
"events": {"atk":754,"deploy":16,"die":12,"dmg":1102,"engage":7,"fx":339,"heal":91,"leak":2,"skill":63,"spawn":20,"status":479},
"fx": {"appear":2,"bandage":5,"block":15,"burst":28,"camouflage":1,"disappear":2,"dodge":2,"featherArrow":5,"flame":1,"garrisonGrant":1,"grow":1,"healField":2,"jungleSoul":1,"mote":61,"redistribute":45,"reweave":3,"riposte":1,"rockfall":18,"rockslide":5,"sandChains":5,"shieldBreak":18,"slash":50,"spGift":4,"tentacle":6,"thorns":48,"tide":5,"truesilver":3,"undying":1},
"snaps": "b3cecd10 d3d68301 c10d8554 c91066bb 2ac0bbdd cb65c8b1 e357c87a 1cfd4db1 842efc1f 73ccb5e1 7cbe283e 83895aff b84dfc8e 65315bfe 89187e2c 11947687 dc8072f3 31f16379 3c0d78cb cf38d801"
},
"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",
@@ -703,35 +703,35 @@
"time": 200,
"ticks": 6000,
"reason": "forced",
"killed": 4,
"killed": 5,
"total": 6,
"unspawned": 0,
"bossHpLeft": 7017881,
"rngDraws": 570,
"bossHpLeft": 6932482,
"rngDraws": 708,
"errors": {"count":0,"kinds":[]},
"players": {
"p1": {"killed":4,"total":6,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":0,"dmg":869760,"bossDmg":436648,"heal":2146,"deaths":5,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":0,"dmg":1033990,"bossDmg":505471,"heal":1246,"deaths":19,"layerGains":{}}
"p1": {"killed":5,"total":6,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1096623,"bossDmg":528737,"heal":2146,"deaths":5,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1199975,"bossDmg":498782,"heal":1246,"deaths":19,"layerGains":{}}
},
"units": [
["p1",1,"op","chess_char_5_19_b",2,"uniequip_002_mlynar",3907,1,3,1,147578,0,0,30,2],
["p1",1,"op","chess_char_5_19_b",2,"uniequip_002_mlynar",3907,1,3,1,211527,0,0,46,3],
["p1",2,"op","chess_char_4_01_b",2,"uniequip_002_rmixer",3537,1,1,0,0,0,0,0,0],
["p1",3,"op","chess_char_5_14_b",1,"none",2174,1,5,1,25423,0,0,0,10],
["p1",3,"op","chess_char_5_14_b",1,"none",2174,1,5,1,39624,0,0,0,16],
["p1",4,"op","chess_char_5_17_b",1,"uniequip_002_f12yin",4654.6,1,11,1,4568,0,0,6,1],
["p1",5,"op","chess_char_5_15_b",1,"uniequip_002_thorn2",1170,1,7,1,303339,0,0,64,13],
["p1",6,"op","chess_char_5_20_b",2,"uniequip_002_aglina",1382,1,13,0,179149,0,0,76,6],
["p1",7,"op","chess_char_5_21_b",1,"uniequip_002_sntlla",1629,1,66,0,19506,1629,0,43,2],
["p1",8,"op","chess_char_5_22_b",1,"uniequip_002_nymph",1611,1,67,0,135206,3222,0,46,14],
["p1",9,"op","chess_char_5_23_b",1,"uniequip_002_reckpr",1610,1,65,0,2158,3220,2146,3,1],
["p1",10,"op","chess_char_4_02_b",2,"uniequip_002_mostma",1695,1,9,0,52833,0,0,38,2],
["p1",5,"op","chess_char_5_15_b",1,"uniequip_002_thorn2",1170,1,7,1,407448,0,0,83,18],
["p1",6,"op","chess_char_5_20_b",2,"uniequip_002_aglina",1382,1,13,0,198029,0,0,73,6],
["p1",7,"op","chess_char_5_21_b",1,"uniequip_002_sntlla",1629,1,66,0,24193,1629,0,43,2],
["p1",8,"op","chess_char_5_22_b",1,"uniequip_002_nymph",1611,1,67,0,140802,3222,0,46,15],
["p1",9,"op","chess_char_5_23_b",1,"uniequip_002_reckpr",1610,1,65,0,4316,3220,2146,3,1],
["p1",10,"op","chess_char_4_02_b",2,"uniequip_002_mostma",1695,1,9,0,66114,0,0,47,3],
["p2",11,"op","chess_char_4_11_b",1,"uniequip_002_cathy",2644,1,49,0,4798,9646,260,10,1],
["p2",12,"op","chess_char_4_04_b",1,"uniequip_002_ines",190.91,1,63,0,118842,15240,280,209,6],
["p2",12,"op","chess_char_4_04_b",1,"uniequip_002_ines",190.91,1,63,0,134122,15240,280,209,6],
["p2",13,"op","chess_char_4_07_b",1,"uniequip_002_bpipe",2383,1,6,0,0,0,0,0,0],
["p2",14,"op","chess_char_4_16_b",2,"uniequip_002_texas2",0,0,61,0,58463,13618,706,128,7],
["p2",15,"op","chess_char_4_09_b",1,"uniequip_002_mizuki",2777.6,1,14,0,205534,0,0,117,6],
["p2",16,"op","chess_char_4_06_b",1,"uniequip_002_kroos2",1561,1,8,0,493990,0,0,140,2],
["p2",14,"op","chess_char_4_16_b",2,"uniequip_002_texas2",0,0,61,0,60730,13618,706,128,7],
["p2",15,"op","chess_char_4_09_b",1,"uniequip_002_mizuki",2777.6,1,14,0,201953,0,0,114,6],
["p2",16,"op","chess_char_4_06_b",1,"uniequip_002_kroos2",1561,1,8,1,634150,0,0,182,3],
["p2",17,"op","chess_char_4_10_b",1,"uniequip_002_aroma",1664,1,12,0,0,0,0,0,0],
["p2",18,"op","chess_char_4_12_b",2,"uniequip_002_glady",2232,1,47,0,28737,2232,0,110,5],
["p2",18,"op","chess_char_4_12_b",2,"uniequip_002_glady",2232,1,47,0,40597,2232,0,106,5],
["p2",19,"op","chess_char_4_13_b",1,"uniequip_002_gnosis",0,0,58,0,47100,5967,0,39,9],
["p2",20,"op","chess_char_4_14_b",1,"uniequip_002_lionhd",0,0,59,0,76526,4878,0,22,4],
["p2",21,"token","token_10041_cathy_catsld",null,null,100,1,21,0,0,0,0,0,null],
@@ -741,20 +741,20 @@
"enemy_10028_vtswd": [3,3,0,0,0],
"enemy_10029_vtshld": [5,5,0,0,0],
"enemy_10030_vtwand": [5,5,0,0,0],
"enemy_10085_hllevi_2": [2,0,2,0,761019],
"enemy_10085_hllevi_2": [2,1,0,0,1068467],
"enemy_1010_demon": [4,4,0,12402,200612],
"enemy_9032_aclionk": [1,0,0,47250,942119]
"enemy_9032_aclionk": [1,0,0,47250,1027518]
},
"hooks": [1,65,6000,1074,1088,2031,2044,431,41,45,91,88,0,30474,660,8,20,2,1,29,0,2,0],
"events": {"atk":1139,"deploy":45,"die":43,"dmg":1988,"engage":15,"fx":618,"heal":7,"leak":2,"skill":179,"spawn":42,"status":942},
"hooks": [1,65,6000,1150,1164,2296,2310,431,42,44,106,101,0,32028,744,8,20,0,1,30,0,2,0],
"events": {"atk":1232,"deploy":45,"die":44,"dmg":2254,"engage":15,"fx":761,"heal":7,"skill":207,"spawn":42,"status":1102},
"fx": {
"aoe": 30,
"appear": 4,
"aoe": 44,
"appear": 6,
"blink": 2,
"burst": 2,
"crit": 81,
"crit": 109,
"dilemma": 6,
"disappear": 4,
"disappear": 6,
"displace": 19,
"equip": 18,
"explosion": 4,
@@ -763,26 +763,26 @@
"iceSpike": 33,
"overclock": 1,
"pull": 4,
"quadShot": 2,
"quadShot": 3,
"revive": 2,
"ripple": 18,
"ripple": 25,
"sentry": 4,
"shadowWeave": 4,
"shadowWeave": 5,
"shield": 1,
"soul": 2,
"sp": 13,
"sp": 19,
"stealth": 6,
"sword": 1,
"swordRain": 25,
"swordRain": 26,
"swordStorm": 7,
"telegraph": 19,
"telegraph": 21,
"tornado": 5,
"tornadoPulse": 20,
"vest": 5,
"weightless": 6,
"zone": 260
"zone": 339
},
"snaps": "8ef2441b ee06d28e 0f318fc5 8e4156fd 466da928 e81f42d7 7687b463 1723e66d 7f419938 8e9ddd0f 9f00d62e 2aa9f2dd bca0bec4 7ba9a9bc a548f671 7a235ae3 7fc35fab 2bf5becc eb0dbe2a ee2661d9"
"snaps": "8ef2441b ee06d28e 0f318fc5 8e4156fd 466da928 e81f42d7 7687b463 1723e66d 7f419938 8e9ddd0f 9f00d62e 2aa9f2dd bca0bec4 90e786de 4f33ccda b4b8971e b9cab80b b4845fa4 1fdd7096 42eef665"
},
"boss-boss_6-solo": {
"about": "阿利斯泰尔,帝国余晖 solo pool 3980000: chess_char_4_17_b chess_char_4_18_b chess_char_4_23_b chess_char_4_19_b chess_char_4_22_b chess_char_4_20_b chess_char_4_21_b chess_char_4_24_b chess_char_4_25_b chess_char_4_26_b",
@@ -796,23 +796,23 @@
"time": 200,
"ticks": 6000,
"reason": "forced",
"killed": 5,
"killed": 8,
"total": 4,
"unspawned": 0,
"bossHpLeft": 3874806,
"rngDraws": 45,
"bossHpLeft": 3907393,
"rngDraws": 54,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":5,"total":4,"perfect":false,"leaks":5,"leaksUncounted":0,"coins":0,"dmg":152625,"bossDmg":105194,"heal":1783,"deaths":16,"layerGains":{}}},
"players": {"p1":{"killed":8,"total":4,"perfect":false,"leaks":2,"leaksUncounted":0,"coins":0,"dmg":164010,"bossDmg":72607,"heal":1088,"deaths":18,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",3513,1,10,2,8,0,0,8,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",6104,1,11,0,6,0,0,6,1],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",2811,1,12,1,8984,35,35,15,2],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",2118,1,48,0,1033,8472,0,40,3],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",0,0,46,0,52228,18906,1436,38,4],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",1419,1,7,2,88481,0,0,50,7],
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",3513,1,10,2,10,0,0,10,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",6104,1,11,1,8,0,0,8,1],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",2811,1,12,2,8987,35,35,17,2],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",0,0,48,1,790,10590,0,30,2],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",0,0,46,1,87664,18412,943,31,4],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",1419,1,7,1,59955,0,0,53,7],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",2772,1,8,0,0,0,0,0,0],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",2178,1,49,0,1884,8712,0,10,3],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",0,0,44,0,0,6995,311,0,0],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",0,0,43,0,6596,8712,0,7,2],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",0,0,49,0,0,8465,110,0,0],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1522,1,9,0,0,0,0,0,0],
["-","~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],
@@ -822,18 +822,18 @@
["-","~6","device","trap_1105_accrate",null,null,100,1,6,0,0,0,0,0,null]
],
"enemies": {
"enemy_10028_vtswd": [4,4,0,0,0],
"enemy_10029_vtshld": [3,3,0,0,0],
"enemy_10030_vtwand": [1,1,0,0,0],
"enemy_10085_hllevi_2": [2,0,2,0,29468],
"enemy_1197_sfshu": [2,2,0,17,17916],
"enemy_1198_msfshu": [6,3,3,0,47],
"enemy_9032_aclionk": [1,0,0,43085,105194]
"enemy_10028_vtswd": [3,3,0,0,0],
"enemy_10029_vtshld": [4,4,0,0,0],
"enemy_10030_vtwand": [2,2,0,0,0],
"enemy_10085_hllevi_2": [2,0,2,0,73421],
"enemy_1197_sfshu": [2,2,0,383,17916],
"enemy_1198_msfshu": [6,6,0,0,66],
"enemy_9032_aclionk": [1,0,0,45814,72607]
},
"hooks": [1,49,6000,179,179,275,472,1334,29,34,20,20,0,6463,12,1,19,5,1,21,0,0,3],
"events": {"atk":205,"deploy":24,"die":29,"dmg":201,"engage":7,"fx":91,"leak":5,"skill":42,"spawn":35,"status":73},
"fx": {"dodge":3,"equip":9,"featherArrow":7,"flame":3,"garrisonGrant":1,"jungleSoul":2,"mote":8,"riposte":3,"rockslide":1,"sandChains":3,"slash":29,"summon":2,"sword":3,"telegraph":10,"truesilver":4,"vest":3},
"snaps": "3e08ed3d b0f5c6bf 12616b1d 9cce9eed afcfc076 fb56bae7 617f9500 b046bb87 398d9618 80f1089f 99802228 823c06f9 a478439e 71b3d152 452c5be2 683a7bbf 1f49e639 2d0e0575 72ea261c 8fc27088"
"hooks": [1,50,6000,169,169,290,488,922,35,37,18,18,0,5924,10,2,20,2,1,27,0,0,2],
"events": {"atk":212,"deploy":24,"die":35,"dmg":238,"engage":7,"fx":94,"leak":2,"skill":38,"spawn":36,"status":77},
"fx": {"appear":5,"disappear":5,"dodge":2,"equip":11,"featherArrow":7,"flame":2,"garrisonGrant":1,"jungleSoul":2,"mote":6,"riposte":1,"rockslide":1,"sandChains":2,"slash":22,"spGift":1,"summon":2,"sword":3,"telegraph":13,"truesilver":4,"vest":4},
"snaps": "3e08ed3d b0f5c6bf 12616b1d 26cd4bb1 3ac9a7a9 dd6d6849 99b9989f 8d641dd4 9437b015 26f59cb1 cd9f4a7b d484bda0 a0fbf26e bb4ffa9d 8bd4554c d61db91c 9810e989 2fda8eab d003c4fc bad20758"
},
"boss-boss_7-pair": {
"about": "“萨米的意志” pair pool 4000000: 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",
@@ -964,7 +964,7 @@
"errors": {"count":0,"kinds":[]},
"players": {
"p1": {"killed":16,"total":14,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2677042,"bossDmg":1550396,"heal":58431,"deaths":17,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":0,"dmg":560253,"bossDmg":262771,"heal":20725,"deaths":25,"layerGains":{}}
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":560253,"bossDmg":262771,"heal":20725,"deaths":25,"layerGains":{}}
},
"units": [
["p1",1,"op","chess_char_5_19_b",2,"uniequip_002_mlynar",7814,1,90,1,335665,24097,655,20,3],
@@ -991,7 +991,7 @@
["p2",22,"token","token_10041_cathy_catsld",null,null,100,1,22,0,0,0,0,0,null]
],
"enemies": {
"enemy_10027_vtsk": [6,4,1,137764,1019681],
"enemy_10027_vtsk": [6,4,0,137764,1019681],
"enemy_1005_yokai": [4,4,0,0,10699],
"enemy_1229_darmy_2": [8,8,0,6104,294231],
"enemy_9013_acstmk_2": [1,0,0,63941,1814230],
@@ -999,8 +999,8 @@
"enemy_9015_acstmb": [3,2,0,9835,83569],
"enemy_9016_acstmr": [8,7,0,0,150]
},
"hooks": [1,92,6000,904,910,2960,3146,8374,72,75,107,101,6,25061,1027,17,36,1,1,61,0,5,9],
"events": {"atk":1083,"deploy":56,"die":74,"dmg":3143,"engage":18,"fx":651,"heal":62,"leak":1,"skill":208,"spawn":58,"status":990},
"hooks": [1,92,6000,904,910,2960,3146,8374,72,74,107,101,6,25061,1027,17,36,0,1,61,0,5,9],
"events": {"atk":1083,"deploy":56,"die":74,"dmg":3143,"engage":18,"fx":652,"heal":62,"skill":208,"spawn":58,"status":990},
"fx": {
"aoe": 42,
"appear": 7,
@@ -1011,7 +1011,7 @@
"counter": 6,
"crit": 59,
"dilemma": 3,
"disappear": 7,
"disappear": 8,
"displace": 1,
"dodge": 9,
"explode": 10,
@@ -1044,7 +1044,7 @@
"weightless": 3,
"zone": 180
},
"snaps": "0499777c 2a8a8958 27548bdc 4d50b14c 2d952e29 e8bac176 f62bddd6 c54603c8 eec4acbb 0e35cf5c f32cd036 b1a89b99 601ad31e 90860daf 260abb25 982d94cf 1e000364 bf1ae288 42ae578f 90a8c025"
"snaps": "0499777c 2a8a8958 27548bdc 4d50b14c 2d952e29 e8bac176 f62bddd6 c54603c8 eec4acbb 0e35cf5c f32cd036 b1a89b99 601ad31e 90860daf 260abb25 982d94cf 1e000364 bf1ae288 42ae578f 9af424ea"
},
"hidden-boss_8-solo": {
"about": "假想敌:胄 solo pool 7200000: chess_char_4_17_b chess_char_4_18_b chess_char_4_23_b chess_char_4_19_b chess_char_4_22_b chess_char_4_20_b chess_char_4_21_b chess_char_4_24_b chess_char_4_25_b chess_char_4_26_b",
@@ -1058,43 +1058,43 @@
"time": 200,
"ticks": 6000,
"reason": "forced",
"killed": 3,
"killed": 6,
"total": 6,
"unspawned": 0,
"bossHpLeft": 6797394,
"rngDraws": 227,
"bossHpLeft": 7007993,
"rngDraws": 122,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":3,"total":6,"perfect":false,"leaks":10,"leaksUncounted":0,"coins":0,"dmg":450453,"bossDmg":402606,"heal":28293,"deaths":23,"layerGains":{}}},
"players": {"p1":{"killed":6,"total":6,"perfect":false,"leaks":12,"leaksUncounted":0,"coins":0,"dmg":236889,"bossDmg":192007,"heal":29302,"deaths":24,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",0,0,6,0,264,7875,0,4,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",0,0,32,1,13954,18678,6470,10,5],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",0,0,7,0,0,2811,0,0,0],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",0,0,53,0,0,10739,0,0,0],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",2569,1,11,2,397515,0,140,154,3],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",0,0,29,0,5558,2838,0,6,0],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",0,0,55,0,0,8993,10992,37,6],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",0,0,35,1,12200,18678,6470,12,5],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",0,0,7,0,10,2811,0,8,1],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",0,0,59,2,97,11660,0,13,1],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",0,0,11,3,187439,3191,140,63,1],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",0,0,29,0,3718,2838,0,6,0],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",0,0,60,0,0,8933,12476,37,6],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",0,0,13,0,33161,6074,939,21,1],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",0,0,52,0,0,12130,3641,0,1],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",0,0,50,0,0,5960,6111,15,1],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",0,0,57,0,0,11657,3168,0,1],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",0,0,55,0,0,5960,6111,15,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],
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null],
["-","~4","device","trap_1105_accrate",null,null,100,1,4,0,0,0,0,0,null]
],
"enemies": {
"enemy_10027_vtsk": [3,0,3,29903,18989],
"enemy_1005_yokai": [4,2,2,0,2867],
"enemy_1197_sfshu": [3,1,2,27,17625],
"enemy_1198_msfshu": [3,0,3,0,7],
"enemy_9013_acstmk_2": [1,0,0,37240,402606],
"enemy_9014_acstma": [3,2,0,0,7972],
"enemy_9015_acstmb": [4,3,0,6587,340],
"enemy_9016_acstmr": [7,6,0,0,47]
"enemy_10027_vtsk": [3,0,3,34423,16521],
"enemy_1005_yokai": [4,1,2,0,1433],
"enemy_1197_sfshu": [3,3,0,649,26874],
"enemy_1198_msfshu": [9,2,7,0,43],
"enemy_9013_acstmk_2": [1,0,0,36456,192007],
"enemy_9014_acstma": [4,3,0,0,0],
"enemy_9015_acstmb": [4,3,0,5808,0],
"enemy_9016_acstmr": [7,6,0,0,11]
},
"hooks": [1,56,6000,303,303,564,576,1204,37,47,17,17,0,5895,50,3,28,10,1,29,0,0,0],
"events": {"atk":360,"deploy":24,"die":37,"dmg":569,"engage":5,"fx":188,"heal":76,"leak":10,"skill":35,"spawn":42,"status":149},
"fx": {"bandage":1,"beam":34,"blink":11,"block":6,"charge":6,"explode":11,"garrisonGrant":1,"healField":6,"mote":4,"redistribute":5,"reweave":1,"rockslide":5,"sandChains":1,"shell":7,"shieldBreak":7,"slash":58,"spGift":2,"summon":5,"telegraph":3,"thorns":11,"truesilver":3},
"snaps": "9b5e58cf c4e06fea a40166a3 0e30f917 42214d72 ebc20f66 7258ad62 49f2a70f 0b93ecfb 8a8ebc4e 4bf3c623 15ee0c07 41d58e70 7b48444f 6d3425f8 2d1f93c0 a755c2ad 76e23872 1016f3ce e5423b09"
"hooks": [1,63,6000,241,241,391,386,909,42,54,17,18,0,3782,53,7,35,12,1,36,0,0,0],
"events": {"atk":287,"deploy":24,"die":42,"dmg":379,"engage":7,"fx":174,"heal":78,"leak":12,"skill":36,"spawn":49,"status":159},
"fx": {"appear":11,"bandage":1,"beam":32,"blink":12,"block":6,"charge":6,"disappear":11,"explode":12,"flame":1,"garrisonGrant":1,"healField":6,"jungleSoul":1,"mote":4,"redistribute":5,"reweave":1,"rockslide":5,"sandChains":1,"shell":7,"shieldBreak":7,"slash":20,"spGift":2,"summon":7,"telegraph":3,"thorns":11,"truesilver":1},
"snaps": "9b5e58cf c4e06fea a40166a3 0e30f917 34fdd11e 47c00c11 fcd7b05e 1cc31311 8cab7ffd b87cca35 51d24d5e 732c5c43 e37369d0 c87fb776 672f8ced c7d655a3 119d0939 7599310d 196c7089 f76ddd4f"
},
"hidden-boss_9-pair": {
"about": "假想敌:铳 pair pool 5600000: 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",
@@ -1111,106 +1111,106 @@
"killed": 8,
"total": 8,
"unspawned": 0,
"bossHpLeft": 3570058,
"rngDraws": 1579,
"bossHpLeft": 3747749,
"rngDraws": 1619,
"errors": {"count":0,"kinds":[]},
"players": {
"p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1989162,"bossDmg":803960,"heal":23383,"deaths":4,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":3240431,"bossDmg":1176195,"heal":126751,"deaths":10,"layerGains":{}}
"p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1664353,"bossDmg":722232,"heal":20932,"deaths":5,"layerGains":{}},
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":3117516,"bossDmg":1090251,"heal":121649,"deaths":12,"layerGains":{}}
},
"units": [
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",5343.08,1,15,0,1906,10482,12401,28,3],
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",2910.14,1,11,0,19692,205,0,21,1],
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",1761.93,1,17,0,50071,1601,0,31,2],
["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",0,0,7,2,1548110,3561,0,55,8],
["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",1074.65,1,9,0,33675,10123,0,46,2],
["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",463.12,1,55,0,25244,10860,954,0,4],
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",2451.4,1,13,1,90440,0,0,0,11],
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,19,1,42621,0,7847,77,2],
["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",0,0,21,1,144190,13392,0,29,29],
["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,63,1,24647,9390,349,66,2],
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",5902.25,1,15,0,2317,10287,12324,32,3],
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",2893.32,1,11,0,20047,281,0,22,1],
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",1834.75,1,17,0,42326,1612,0,24,2],
["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",430.19,1,7,1,1229734,2143,0,58,8],
["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",1329.31,1,9,0,34472,14154,0,46,2],
["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",0,0,66,0,35830,9923,233,0,3],
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",2451.4,1,13,0,69036,0,0,0,11],
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,19,0,26444,0,7415,77,2],
["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",0,0,21,1,174620,13925,0,30,30],
["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,60,0,22306,9359,349,73,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,1640,318,4,1],
["p2",13,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",0,0,62,1,459553,38345,0,197,3],
["p2",14,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",0,0,60,0,497307,16632,0,66,5],
["p2",15,"op","chess_char_5_01_b",1,"uniequip_002_excu2",0,0,66,0,13781,7737,2820,42,2],
["p2",16,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",1302.59,1,61,0,512511,25088,0,68,3],
["p2",17,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",765.29,1,8,0,477111,13038,0,78,3],
["p2",18,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2215,1,14,0,969378,5435,3408,291,6],
["p2",19,"op","chess_char_6_14_b",2,"uniequip_002_lumen",3038.4,1,20,0,0,0,24080,78,1],
["p2",20,"op","chess_char_6_16_b",2,"uniequip_002_halo2",1439,1,10,0,98857,0,0,131,4],
["p2",21,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1695.1,1,59,1,183585,13999,0,2,5],
["p2",22,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1592.1,1,25,0,0,14000,96443,80,2],
["p2",13,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",2052.07,1,68,4,561677,30058,0,292,3],
["p2",14,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",0,0,59,0,341242,18065,0,56,4],
["p2",15,"op","chess_char_5_01_b",1,"uniequip_002_excu2",0,0,65,0,3198,6795,1515,24,1],
["p2",16,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",0,0,55,1,514814,17168,0,62,3],
["p2",17,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",942.17,1,8,0,674333,8720,0,73,3],
["p2",18,"op","chess_char_6_13_b",1,"uniequip_002_angel2",0,0,14,0,769354,8634,2644,263,5],
["p2",19,"op","chess_char_6_14_b",2,"uniequip_002_lumen",3038.4,1,20,0,0,0,15762,80,1],
["p2",20,"op","chess_char_6_16_b",2,"uniequip_002_halo2",0,0,10,0,97174,1439,0,119,3],
["p2",21,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",0,0,62,0,132239,19665,0,24,3],
["p2",22,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1183.78,1,25,0,0,15377,101728,82,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],
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null],
["-","~4","device","trap_1105_accrate",null,null,100,1,4,0,0,0,0,0,null],
["-","~5","device","trap_1105_accrate",null,null,100,1,5,0,0,0,0,0,null],
["-","~6","device","trap_1105_accrate",null,null,100,1,6,0,0,0,0,0,null],
["p2","~1","token","token_10019_nearl2_sword",null,null,2280.4,0,47,0,6744,6000,0,5,null],
["p1","~1","token","token_10030_mlyss_wtrman",null,null,1322,1,48,0,1766,2370,1514,30,1],
["p1","~2","token","token_10030_mlyss_wtrman",null,null,1102.65,0,49,0,595,675,0,11,null],
["p1","~3","token","token_10030_mlyss_wtrman",null,null,0,0,51,0,661,1322,0,10,null],
["p1","~4","token","token_10030_mlyss_wtrman",null,null,0,0,53,0,794,1322,0,12,null],
["p1","~1","token","token_10040_siege2_vlion",null,null,3730,0,54,0,4750,270,0,10,null],
["p1","~1","token","token_10017_skadi2_dedant",null,null,138,0,56,0,0,0,0,0,null],
["p2","~2","token","token_10019_nearl2_sword",null,null,0,0,57,0,21605,6600,0,12,null],
["p1","~2","token","token_10017_skadi2_dedant",null,null,100,0,64,0,0,0,0,0,null],
["p1","~2","token","token_10040_siege2_vlion",null,null,3926.59,0,65,0,0,97,0,0,null],
["p1","~3","token","token_10017_skadi2_dedant",null,null,100,1,67,0,0,0,0,0,null]
["p2","~1","token","token_10019_nearl2_sword",null,null,2916.93,0,47,0,6778,5363,0,21,null],
["p1","~1","token","token_10030_mlyss_wtrman",null,null,0,0,48,1,1801,1538,0,22,1],
["p1","~2","token","token_10030_mlyss_wtrman",null,null,0,0,49,0,207,3142,0,9,null],
["p1","~3","token","token_10030_mlyss_wtrman",null,null,0,0,50,0,463,1322,0,7,null],
["p1","~1","token","token_10040_siege2_vlion",null,null,3730,0,52,0,4750,270,0,10,null],
["p1","~1","token","token_10017_skadi2_dedant",null,null,138,0,57,0,0,0,0,0,null],
["p2","~2","token","token_10019_nearl2_sword",null,null,6950.53,0,58,0,0,963,0,0,null],
["p1","~4","token","token_10030_mlyss_wtrman",null,null,1322,1,61,0,0,385,293,0,1],
["p1","~2","token","token_10040_siege2_vlion",null,null,3897.9,0,63,0,0,137,0,0,null],
["p2","~3","token","token_10019_nearl2_sword",null,null,133.02,0,64,0,16707,6165,0,9,null],
["p1","~2","token","token_10017_skadi2_dedant",null,null,100,0,67,0,0,0,0,0,null]
],
"enemies": {
"enemy_1267_nhpbr_2": [6,6,0,5992,141894],
"enemy_9007_acelem": [2,2,0,22960,309587],
"enemy_9017_achunt_2": [2,0,0,57558,2029942],
"enemy_9018_actrpa": [1,0,0,42972,765851],
"enemy_9019_actrpb": [1,0,0,26761,756394],
"enemy_9020_actrpc": [1,0,0,18223,1328263]
"enemy_1267_nhpbr_2": [6,6,0,1400,141894],
"enemy_9007_acelem": [2,2,0,17143,309587],
"enemy_9017_achunt_2": [2,0,0,58691,1852251],
"enemy_9018_actrpa": [1,0,0,44364,722451],
"enemy_9019_actrpb": [1,0,0,28255,660498],
"enemy_9020_actrpc": [1,0,0,23012,1168947]
},
"hooks": [1,65,6000,1653,1653,7414,13693,3721,26,37,100,94,326,46845,2814,18,13,0,1,27,0,18,0],
"events": {"atk":2008,"deploy":46,"die":37,"dmg":13203,"engage":24,"fx":2168,"heal":883,"skill":194,"spawn":52,"status":833},
"hooks": [1,66,6000,1682,1682,6586,12669,2825,29,42,95,91,286,44763,2551,19,13,0,1,30,0,16,0],
"events": {"atk":2097,"deploy":47,"die":42,"dmg":12154,"engage":23,"fx":2024,"heal":885,"skill":186,"spawn":52,"status":769},
"fx": {
"airstrike": 117,
"beam": 31,
"airstrike": 88,
"beam": 27,
"blessing": 1,
"blink": 4,
"bombard": 68,
"bombardShell": 68,
"burst": 18,
"blink": 5,
"bombard": 72,
"bombardShell": 72,
"burst": 16,
"camouflage": 1,
"copy": 2,
"copy": 3,
"dash": 24,
"displace": 24,
"displace": 20,
"dp": 2,
"drone": 275,
"droneLock": 15,
"drones": 5,
"extraAttack": 12,
"finale": 5,
"drone": 190,
"droneLock": 9,
"drones": 3,
"extraAttack": 7,
"finale": 4,
"firewall": 3,
"garrisonGrant": 1,
"lightning": 119,
"lightning": 117,
"link": 1141,
"lock": 72,
"lure": 2,
"phase": 11,
"radiantSword": 2,
"radiantSword": 3,
"revive": 3,
"scorchBurst": 3,
"scorchBurst": 1,
"shieldBreak": 9,
"slash": 55,
"snow": 25,
"split": 3,
"steal": 6,
"slash": 44,
"snow": 26,
"split": 2,
"steal": 5,
"storm": 11,
"summon": 4,
"summon": 5,
"sunBurst": 3,
"talent": 7,
"tide": 4,
"talent": 6,
"tide": 3,
"wanted": 6,
"zone": 6
},
"snaps": "2a921421 d160972c 4e5b9396 7b13edd2 689bae2b f9a15be0 67150ecd 3a4286df 13119f08 b1b42c22 b5c0b449 f4bddab7 22e104fd fd2f838a b7964c3e 83e4b794 62994daa 16b9850d 3f3a5054 498cda94"
"snaps": "2a921421 57801ce9 151fd5ae 991b7440 79722413 1faab651 85db54ec 88d6f82e 6d938e5f fc881a30 9c294f55 4373e468 af475c5f df3e9b70 b0474d4c 538602b8 4e66de79 e70dcab9 481e5d84 e8e351b1"
},
"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",
@@ -1227,37 +1227,35 @@
"killed": 3,
"total": 3,
"unspawned": 0,
"bossHpLeft": 1932856,
"rngDraws": 128,
"bossHpLeft": 2011732,
"rngDraws": 207,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":3,"total":3,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2753803,"bossDmg":867144,"heal":73158,"deaths":5,"layerGains":{}}},
"players": {"p1":{"killed":3,"total":3,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2574046,"bossDmg":788268,"heal":101441,"deaths":5,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_5_08_b",2,"uniequip_002_horn",2775,1,1,1,368359,8434,0,127,4],
["p1",2,"op","chess_char_5_11_b",1,"uniequip_002_demkni",4804.8,1,3,0,1265,3442,23540,46,35],
["p1",3,"op","chess_char_5_05_b",2,"uniequip_002_ulpia",11263.65,1,33,0,413261,29216,17173,39,5],
["p1",4,"op","chess_char_5_06_b",2,"uniequip_002_etlchi",3747,1,7,1,1380629,16819,16926,201,5],
["p1",5,"op","chess_char_5_13_b",1,"uniequip_002_ghost2",3448.92,1,9,0,128392,14229,3714,76,3],
["p1",6,"op","chess_char_5_07_b",2,"uniequip_002_surtr",7680,1,35,0,42625,19991,4759,13,3],
["p1",7,"op","chess_char_5_02_b",2,"uniequip_002_titi",1439,1,5,0,0,0,0,1,1],
["p1",8,"op","chess_char_5_03_b",2,"uniequip_002_blaze2",1148.18,1,6,0,207074,1471,1147,186,6],
["p1",9,"op","chess_char_5_10_b",2,"uniequip_002_lisa",1480,1,32,0,14018,13299,5899,28,6],
["p1",10,"op","chess_char_5_12_b",0,"uniequip_002_dusk",1787,1,2,1,198179,0,0,53,41],
["p1","~1","token","token_10039_ulpia_block",null,null,3290,0,20,0,0,210,0,0,null],
["p1","~2","token","token_10039_ulpia_block",null,null,3335,0,28,0,0,165,0,0,null]
["p1",1,"op","chess_char_5_08_b",2,"uniequip_002_horn",2757.97,1,1,1,328544,10883,0,121,4],
["p1",2,"op","chess_char_5_11_b",1,"uniequip_002_demkni",4784.8,1,3,0,2559,11172,34206,59,38],
["p1",3,"op","chess_char_5_05_b",2,"uniequip_002_ulpia",10680.88,1,4,0,391098,51057,37953,64,5],
["p1",4,"op","chess_char_5_06_b",2,"uniequip_002_etlchi",1801.92,1,7,0,1462149,23079,21225,197,5],
["p1",5,"op","chess_char_5_13_b",1,"uniequip_002_ghost2",3448.92,1,19,1,146526,21512,5080,102,4],
["p1",6,"op","chess_char_5_07_b",2,"uniequip_002_surtr",2680,1,23,0,4444,7780,113,7,1],
["p1",7,"op","chess_char_5_02_b",2,"uniequip_002_titi",1439,1,5,0,0,0,0,0,0],
["p1",8,"op","chess_char_5_03_b",2,"uniequip_002_blaze2",1207.04,1,6,0,32056,265,0,25,1],
["p1",9,"op","chess_char_5_10_b",2,"uniequip_002_lisa",1480,1,28,0,0,8783,2863,1,5],
["p1",10,"op","chess_char_5_12_b",0,"uniequip_002_dusk",1787,1,2,1,206669,0,0,57,43]
],
"enemies": {
"enemy_10067_ftsjc": [1,1,0,1161,21565],
"enemy_5601_entlec": [6,5,0,0,160038],
"enemy_9007_acelem": [2,2,0,632,119439],
"enemy_9017_achunt_2": [1,0,0,32010,867144],
"enemy_9018_actrpa": [1,0,0,14160,440951],
"enemy_9019_actrpb": [1,0,0,19148,445768],
"enemy_9020_actrpc": [1,0,0,13881,698896]
"enemy_5601_entlec": [8,7,0,0,221752],
"enemy_9007_acelem": [2,2,0,1897,119439],
"enemy_9017_achunt_2": [1,0,0,61863,788268],
"enemy_9018_actrpa": [1,0,0,22333,431138],
"enemy_9019_actrpb": [1,0,0,16840,403557],
"enemy_9020_actrpc": [1,0,0,15204,588326]
},
"hooks": [1,36,6000,876,876,3337,7978,7359,13,17,109,104,118,25510,1863,10,13,0,1,120,0,2,0],
"events": {"atk":1327,"deploy":23,"die":17,"dmg":7399,"engage":10,"fx":851,"heal":385,"skill":213,"spawn":25,"status":317},
"fx": {"anchor":5,"aoe":3,"beam":22,"burst":2,"candle":6,"charge":7,"dash":12,"downed":1,"ember":2,"explode":1,"healAoe":35,"link":597,"overload":4,"palsy":3,"phase":12,"reborn":1,"shieldBreak":10,"splash":118,"substitute":3,"swap":3,"teleport":4},
"snaps": "9f379cb4 794e9645 d2f1f9f3 6f045130 3ba3cbda c2dffcd7 002617f9 8eb229f1 44fa10f5 02a4f054 024d05f4 4f2b850e 264e7810 e232b25c fb23ac72 ffe30a9a 966d17a8 f21eca66 c335c5c3 dadd437f"
"hooks": [1,31,6000,758,758,3651,7974,3912,15,16,106,103,21,22194,1289,14,15,0,1,70,0,8,0],
"events": {"atk":1320,"deploy":16,"die":16,"dmg":7575,"engage":9,"fx":765,"heal":611,"skill":209,"spawn":25,"status":228},
"fx": {"anchor":5,"aoe":1,"beam":30,"burst":8,"candle":8,"charge":7,"dash":12,"dollEnd":1,"ember":1,"explode":1,"healAoe":38,"link":597,"overload":4,"palsy":3,"phase":11,"reborn":1,"revive":1,"shieldBreak":9,"splash":21,"substitute":4,"swap":2},
"snaps": "9f379cb4 794e9645 1eab82b0 9bd21a64 94bcd685 ea068a42 11f6e172 f1d86469 8dae333a a7b0bf9e 854de036 d7f516e9 946585b3 06953c95 f04d0312 832df1bd 4f3fdc1c eec59b6e 4c10a52c 9c930ebb"
},
"hidden-boss_10-pair": {
"about": "假想敌:管 pair pool 15200000: chess_char_5_19_b chess_char_4_01_b chess_char_5_14_b chess_char_5_17_b chess_char_5_15_b chess_char_5_20_b chess_char_5_21_b chess_char_5_22_b chess_char_5_23_b chess_char_4_02_b | chess_char_4_11_b chess_char_4_04_b chess_char_4_07_b chess_char_4_16_b chess_char_4_09_b chess_char_4_06_b chess_char_4_10_b chess_char_4_12_b chess_char_4_13_b chess_char_4_14_b token_10041_cathy_catsld token_10041_cathy_catsld",
@@ -1372,36 +1370,36 @@
"time": 200,
"ticks": 6000,
"reason": "forced",
"killed": 11,
"killed": 10,
"total": 5,
"unspawned": 0,
"bossHpLeft": 7323878,
"rngDraws": 102,
"bossHpLeft": 7333760,
"rngDraws": 139,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":11,"total":5,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":0,"dmg":430741,"bossDmg":276122,"heal":148595,"deaths":10,"layerGains":{}}},
"players": {"p1":{"killed":10,"total":5,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":0,"dmg":448196,"bossDmg":266240,"heal":121244,"deaths":16,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",3123.26,1,29,5,45,3903,0,19,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",1092.31,1,24,0,36,24428,13312,31,6],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",0,0,28,0,140999,24118,1514,143,3],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",3600.6,1,31,0,13,10454,0,12,0],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",0,0,22,3,176153,57886,6009,118,2],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",1419,1,30,1,87204,1419,0,56,6],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",1584,1,25,0,0,20837,92999,101,2],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",2178,1,2,2,26292,3463,0,38,4],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1671,1,23,0,0,33782,4919,0,1],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",1522,1,3,0,0,2358,29841,56,9]
["p1",1,"op","chess_char_4_17_b",1,"uniequip_002_hsguma",0,0,29,6,69,7026,0,24,0],
["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",0,0,25,0,30,21486,9278,25,5],
["p1",3,"op","chess_char_4_23_b",2,"uniequip_002_gvial2",0,0,26,1,135024,23127,1337,137,3],
["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",0,0,34,1,24,20162,0,21,1],
["p1",5,"op","chess_char_4_22_b",2,"uniequip_002_svrash",3905.8,1,35,1,190029,35166,2983,92,3],
["p1",6,"op","chess_char_4_20_b",2,"uniequip_002_fartth",0,0,30,0,76234,2838,0,53,6],
["p1",7,"op","chess_char_4_21_b",1,"uniequip_002_plosis",2920.8,1,24,0,0,20473,74171,95,2],
["p1",8,"op","chess_char_4_24_b",1,"uniequip_002_billro",343.79,1,31,1,46787,6392,468,62,4],
["p1",9,"op","chess_char_4_25_b",2,"uniequip_002_cetsyr",1671,1,23,0,0,26222,3713,0,2],
["p1",10,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",0,0,3,0,0,7174,29294,42,11]
],
"enemies": {
"enemy_1005_yokai_3": [1,0,1,11629,68252],
"enemy_1005_yokai_3": [1,0,1,18962,95601],
"enemy_9010_acpupp": [2,2,0,19404,86026],
"enemy_9021_acduml_2": [1,0,0,6962,276122],
"enemy_9022_acdumm": [1,0,0,3379,0],
"enemy_9023_acdums": [10,9,0,138776,342]
"enemy_9021_acduml_2": [1,0,0,4772,266240],
"enemy_9022_acdumm": [1,0,0,2812,0],
"enemy_9023_acdums": [10,8,0,122384,328]
},
"hooks": [1,33,6000,650,650,2402,2852,7246,21,22,33,33,0,9568,105,16,15,1,1,65,0,27,2],
"events": {"atk":973,"deploy":18,"die":21,"dmg":2838,"engage":7,"fx":844,"heal":298,"leak":1,"skill":69,"spawn":25,"status":303},
"fx": {"bandage":9,"beam":21,"block":14,"burst":27,"dodge":2,"ember":2,"explode":500,"featherArrow":6,"garrisonGrant":1,"healField":2,"jungleSoul":3,"mote":52,"phase":55,"redistribute":15,"reweave":1,"riposte":1,"rockslide":6,"sandChains":1,"sandChainsCharged":3,"shieldBreak":21,"slash":40,"spGift":2,"summon":8,"telegraph":2,"thorns":47,"truesilver":2,"undying":1},
"snaps": "65ab7720 4d0f9bc6 b175a7ad decfc6d9 d1ad8895 b6add8db 8b187de4 1339864b 84136f5a 963ae966 244b6531 f0d249a1 55dc7a49 f44509fa 5ba1fdbd c65a6ccf 00d4edb3 c6669080 45cab92a 414911cc"
"hooks": [1,35,6000,629,629,2222,2669,4675,26,27,37,36,0,8979,144,19,15,1,1,63,0,28,5],
"events": {"atk":938,"deploy":20,"die":26,"dmg":2659,"engage":7,"fx":895,"heal":257,"leak":1,"skill":75,"spawn":25,"status":361},
"fx": {"appear":2,"bandage":11,"beam":21,"block":26,"burst":28,"disappear":2,"dodge":5,"ember":2,"explode":497,"featherArrow":6,"flame":1,"garrisonGrant":1,"healField":2,"jungleSoul":3,"mote":48,"phase":56,"redistribute":29,"reweave":2,"riposte":3,"rockslide":5,"sandChains":1,"sandChainsCharged":3,"shieldBreak":17,"slash":51,"spGift":1,"summon":8,"telegraph":2,"thorns":58,"truesilver":3,"undying":1},
"snaps": "65ab7720 4d0f9bc6 b175a7ad decfc6d9 d1ad8895 b6add8db d001bf54 dc66077c e9917391 7dcdab2e 165041d1 65a94aa3 35d72c2e 6ee3e9c7 a7174d70 93c52780 189aaca7 d0965cc7 aedfc5b2 1b3f4a01"
},
"unite-1": {
"about": "联防 1 helper(s), 11 leaked: 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 token_10017_skadi2_dedant",
+8 -8
View File
@@ -1314,7 +1314,7 @@
"combat": "client",
"boost": {"lp":400,"layers":300},
"setup": {"stageId":"act1autochess_m02","bossId":"boss_5","hiddenBossId":"boss_10","factions":["TIMES","ELEMENT","DOT"]},
"end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":395,"bossPoolLeft":0,"virtualMs":113700},
"end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":397,"bossPoolLeft":0,"virtualMs":113700},
"rng": {"setup":25,"shop":346,"waves":0,"draft":42,"bots":10350,"meta":3},
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
"rounds": [
@@ -1377,12 +1377,12 @@
"victory": true,
"roundsPassed": 15,
"eliminatedRound": null,
"lp": 395,
"lp": 397,
"bandId": "band_paganini",
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
"trophies": 0,
"reward": 425,
"stats": {"activatedLayers":2281,"bossDamage":4580000,"dmgDealt":6374743,"gold":175,"itemsEquipped":25,"kills":376,"leaks":0,"lpLost":0,"merges":5,"perfectRounds":13,"refreshes":34},
"stats": {"activatedLayers":2281,"bossDamage":4580000,"dmgDealt":6374751,"gold":175,"itemsEquipped":25,"kills":376,"leaks":0,"lpLost":0,"merges":5,"perfectRounds":13,"refreshes":34},
"lineup": ["4_02_b@12,7[3_08_e_a+1_02_e_b]","6_01_a@12,8[3_03_e_a+3_02_e_a]","6_16_a@11,5","3_18_a@11,8[4_11_e_a+4_05_e_b]","4_10_a@10,4[1_05_e_b]","6_05_a@10,5","4_01_b@10,6[4_06_e_a+4_05_e_a]","1_08_b@9,9[4_10_e_a]"],
"bonds": ["arcaneShip 2/389","deputShip 0/300","egirShip 0/300","emptyShip 0/300","indomShip 0/308","investShip 0/300","kazimierzShip 0/308","kjeragShip 0/300","lateranoShip 1/322","maniShip 0/300","miraShip 0/300","preciShip 1/302","raidShip 0/300","sargonShip 0/300","siracusaShip 1/345","skillfulShip 0/335","soloShip 1/307","steadShip 0/300","suntShip 1/300","swiftShip 1/316","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"]
}
@@ -1398,7 +1398,7 @@
"combat": "client",
"boost": {"lp":400,"layers":300},
"setup": {"stageId":"act2autochess_m04","bossId":"boss_5","hiddenBossId":"boss_9","factions":["FLY","ELEMENT","INVISIBLE"]},
"end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":800,"bossPoolLeft":0,"virtualMs":124650},
"end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":698,"bossPoolLeft":0,"virtualMs":124650},
"rng": {"setup":25,"shop":750,"waves":0,"draft":61,"bots":22165,"meta":198},
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
"rounds": [
@@ -1474,12 +1474,12 @@
"victory": true,
"roundsPassed": 15,
"eliminatedRound": null,
"lp": 400,
"lp": 349,
"bandId": "band_sarkazb",
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
"trophies": 8,
"reward": 425,
"stats": {"activatedLayers":3346,"bossDamage":7735817,"dmgDealt":25492475,"gold":154,"itemsEquipped":21,"kills":285,"leaks":8,"lpLost":0,"merges":7,"perfectRounds":12,"refreshes":21},
"stats": {"activatedLayers":3346,"bossDamage":8584552,"dmgDealt":21757593,"gold":154,"itemsEquipped":21,"kills":287,"leaks":8,"lpLost":0,"merges":7,"perfectRounds":12,"refreshes":21},
"lineup": ["4_09_b@11,5[3_11_e_a+3_11_e_b]","6_11_a@11,8","3_13_b@10,3","3_05_b@10,8[1_01_e_a+6_09_e_a]","6_16_a@9,6","6_05_a@9,7[3_02_e_a]","4_14_b@9,8[2_01_e_a+1_02_e_b]","5_07_a@9,9[4_10_e_b]"],
"bonds": ["arcaneShip 1/365","deputShip 0/304","egirShip 1/364","emptyShip 0/300","indomShip 0/300","investShip 0/300","kazimierzShip 0/302","kjeragShip 0/308","lateranoShip 0/300","maniShip 1/300","miraShip 0/300","preciShip 1/345","raidShip 1/405","sargonShip 1/300","siracusaShip 0/300","skillfulShip 1/338","soloShip 1/307","steadShip 0/338","suntShip 1/300","swiftShip 1/322","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"]
},
@@ -1488,12 +1488,12 @@
"victory": true,
"roundsPassed": 15,
"eliminatedRound": null,
"lp": 400,
"lp": 349,
"bandId": "band_doberm",
"title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"},
"trophies": 8,
"reward": 425,
"stats": {"activatedLayers":2801,"bossDamage":6160343,"dmgDealt":21332968,"gold":191,"itemsEquipped":32,"kills":287,"leaks":1,"lpLost":0,"merges":5,"perfectRounds":12,"refreshes":55},
"stats": {"activatedLayers":2801,"bossDamage":5304736,"dmgDealt":21479720,"gold":191,"itemsEquipped":32,"kills":287,"leaks":1,"lpLost":0,"merges":5,"perfectRounds":12,"refreshes":55},
"lineup": ["4_10_a@11,5[3_05_e_a+4_03_e_a]","6_05_a@11,8[4_08_e_a+6_09_e_a]","6_16_a@10,3[3_11_e_a+1_05_e_b]","2_13_a@10,7[4_04_e_a+4_05_e_a]","1_08_b@10,8[4_07_e_b+5_07_e_a]","6_08_a@9,6[2_01_e_b+3_01_e_b]","5_20_a@9,7[1_05_e_a+3_04_e_a]","6_08_a@9,8[3_03_e_a+1_01_e_a]","3_19_b@9,9[2_04_e_a+3_07_e_b]"],
"bonds": ["arcaneShip 1/371","deputShip 0/332","egirShip 0/300","emptyShip 0/308","indomShip 0/300","investShip 0/300","kazimierzShip 0/300","kjeragShip 0/300","lateranoShip 0/300","maniShip 0/300","miraShip 1/334","preciShip 1/311","raidShip 0/300","sargonShip 0/300","siracusaShip 1/465","skillfulShip 1/356","soloShip 1/322","steadShip 0/312","suntShip 1/308","swiftShip 1/334","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"]
}
+3 -1
View File
@@ -247,7 +247,9 @@ test('E2 audit: healers on the lane tiles of every boss / hidden field deal no d
const lead = (e) => e.isBoss || e.tag === 'boss' || e.tag === 'part' || LUCIEN.test(e.defId);
let healsOnLead = 0;
h.b.on('heal', (c) => { if (healers.includes(c.source) && c.amount > 0 && c.source.blocking.some(lead)) healsOnLead++; });
for (let i = 0; i < 120 * 30; i++) {
// 200 s on the fields whose leaders / parts a healer must block (the solo 铳 field's springs first meet a healer at
// 145 s since their 末日布道 chase lasts its whole 5 s — PR #347), 120 s elsewhere
for (let i = 0; i < (EXPECT[key] ? 200 : 120) * 30; i++) {
h.step();
// operators break the 碎铳之簧 shields (unblockable while shielded): a hit of the kind each shield yields to
for (const e of h.b.enemies) {
+32 -11
View File
@@ -1,7 +1,8 @@
// Blockable-ground preference of the ground pathing (grid.js header; user playtest #2: on 战场#01 the lower-gate enemies
// walked up the col-9 floor lane, where no operator can stand, instead of the col-8 road). The flow field keeps the
// official route unless the preference route — the fewest non-blockable tiles among equal-length chains, smoothing
// that never cuts across floor its grid route does not walk — crosses fewer non-blockable tiles. "Crosses" = the
// that never cuts across floor its grid route does not walk — crosses fewer non-blockable tiles and no more 深水区
// (GitHub #375). "Crosses" = the
// segment passes through the tile's interior (grid.js segmentTiles; brushing a corner is not crossing — the D5 report
// after 0.1.0, test/sim/pathing-official.test.js). Audit: every active stage × gate × field (normal, 联防 both halves,
// boss both halves incl. the solo `_s` templates) — the non-blockable tiles an enemy still crosses are listed and
@@ -69,27 +70,36 @@ function nbCrossed(g, s, e) {
/**
* Fewest non-blockable tiles (start and goal excluded) over all 4-connected crate-free routes s → e of length ≤ the
* shortest + `slack`; { best, min } (min = the minimum over those lengths).
* shortest + `slack` that cross at most `maxWet` 深水区 tiles (grid.js: the tie-break never walks into more 深水区 than
* the official route — GitHub #375); { best, min } (min = the minimum over those lengths).
*/
function fewestNb(g, s, e, slack = 2) {
function fewestNb(g, s, e, slack = 2, maxWet = Infinity) {
const f = g.flowField(e[0], e[1]);
const best = f.dist[s[0] * COLS + s[1]];
if (best < 0 || best >= 1000) return null;
const ek = e[0] * COLS + e[1];
const cost = (k) => (k === ek ? 0 : g.unblockable[k]);
let cur = new Map([[s[0] * COLS + s[1], 0]]);
const wetOf = Number.isFinite(maxWet) ? (k) => (k === ek ? 0 : g.deepWater[k]) : () => 0;
// tile → (深水区 so far → fewest non-blockable so far)
let cur = new Map([[s[0] * COLS + s[1], new Map([[0, 0]])]]);
let min = Infinity;
for (let L = 0; L <= best + slack; L++) {
if (cur.has(ek)) min = Math.min(min, cur.get(ek));
if (cur.has(ek)) for (const p of cur.get(ek).values()) min = Math.min(min, p);
const nx = new Map();
for (const [k, p] of cur) {
for (const [k, byWet] of cur) {
if (k === ek) continue;
const r = (k / COLS) | 0, c = k % COLS;
for (const [dr, dc] of [[1, 0], [0, 1], [-1, 0], [0, -1]]) {
const nr = r + dr, nc = c + dc;
if (!g.walkable(nr, nc) || g.isCrate(nr, nc)) continue;
const nk = nr * COLS + nc, np = p + cost(nk);
if (!nx.has(nk) || nx.get(nk) > np) nx.set(nk, np);
const nk = nr * COLS + nc;
let m = nx.get(nk);
for (const [w, p] of byWet) {
const nw = w + wetOf(nk), np = p + cost(nk);
if (nw > maxWet) continue;
if (!m) nx.set(nk, m = new Map());
if (!m.has(nw) || m.get(nw) > np) m.set(nw, np);
}
}
}
cur = nx;
@@ -97,6 +107,14 @@ function fewestNb(g, s, e, slack = 2) {
return { best, min };
}
/** 深水区 tiles crossed from s to e (start and goal excluded). */
function wetCrossed(g, s, e) {
const wp = g.waypoints(s[0], s[1], e[0], e[1]);
const seen = new Set();
for (const [r, c] of crossed(wp)) if (!(r === s[0] && c === s[1]) && !(r === e[0] && c === e[1]) && g.deepWater[r * COLS + c]) seen.add(`${r},${c}`);
return seen.size;
}
/** Tiles an enemy of `route` visits (rounded position per tick) and whether `u` blocked it. */
function walk(h) {
const seen = [];
@@ -293,12 +311,15 @@ test('audit: every WALK leg of every wave template (normal / 联防 / boss / hid
if (!grids.has(key)) grids.set(key, stageGrid(sid, rect));
const g = grids.get(key);
if (!g.walkable(from[0], from[1]) || !g.walkable(to[0], to[1])) continue;
const eq = fewestNb(g, from, to, 0);
if (!eq) continue;
if (!fewestNb(g, from, to, 0)) continue;
const got = nbCrossed(g, from, to);
// compared with the routes that wade through no more 深水区 than ours (#375: 战场#08(下半)'s patrol legs to (2,3) /
// (2,17) keep the official upper road, not the lower one through two water tiles that crosses one floor tile less)
const wet = wetCrossed(g, from, to);
const eq = fewestNb(g, from, to, 0, wet);
const what = `${sid} ${id} (${from}) → (${to}) crosses ${got.join(' ')}`;
assert.ok(got.length <= eq.min, `${what}, an equal-length route only ${eq.min}`);
const near = fewestNb(g, from, to, 2);
const near = fewestNb(g, from, to, 2, wet);
if (near.min < got.length) {
assert.ok(rect === GEO.BOSS_RECT && got.every(arena), `${what}: a route of length ≤ ${near.best}+2 crosses only ${near.min}`);
accepted.push(what);
+11 -3
View File
@@ -5,8 +5,9 @@
// preference field = 0.1.0's — equal-length ties to the chain with the fewest non-blockable tiles, a line of sight that
// covers non-blockable tiles, diagonal-step corners included, only on the own raw chain — whose pointer replaces the
// official one where its route crosses fewer non-blockable tiles, or as few while only skipping the official waypoint
// (on the straight line to it, leading there); "crosses" = positive-length intersection with the tile, found here by
// clipping the segment against each tile, independently of grid.js crossTiles):
// (on the straight line to it, leading there), and no more 深水区 than the official route (GitHub #375); "crosses" =
// positive-length intersection with the tile, found here by clipping the segment against each tile, independently of
// grid.js crossTiles):
// * the 8 active stages with their match-start devices: the smoothed chain from EVERY walkable tile of the normal,
// 联防 and boss rects to every goal equals the full-map reference one (the sim's rect limit changes nothing);
// * 400 random crate / block layouts: identical chains to the same algorithm limited to the rect;
@@ -56,6 +57,8 @@ function official(sid, { crates: extraCrates = [], blocks: extraBlocks = [], rec
if (t.tileKey === 'tile_deepsea') return true;
return !(t.heightType === 'LOWLAND' && (t.buildableType === 'ALL' || t.buildableType === 'MELEE'));
};
/** 深水区 — the preference may not cross more of it than the official route (#375) */
const deep = (r, c) => ['tile_deepsea', 'tile_deepwater'].includes(tile(r, c).tileKey);
const bres = ([r0, c0], [r1, c1], clear) => {
const dr = Math.abs(r1 - r0), dc = Math.abs(c1 - c0), sr = r1 > r0 ? 1 : -1, sc = c1 > c0 ? 1 : -1;
let err = dc - dr, r = r0, c = c0;
@@ -140,12 +143,15 @@ function official(sid, { crates: extraCrates = [], blocks: extraBlocks = [], rec
const off = spfa(false);
const dist = off.dist;
const segNb = (a, b) => through(a, b).filter(([y, x]) => !(y === a[0] && x === a[1]) && nb(y, x)).length;
const segWet = (a, b) => through(a, b).filter(([y, x]) => !(y === a[0] && x === a[1]) && deep(y, x)).length;
let nxt = smooth(off, false);
if (prefer) {
// per tile in increasing distance: the preference pointer when its route crosses fewer non-blockable tiles (or as
// few while it only skips the official waypoint: o strictly inside the segment a → p, and o's chosen pointer is p)
// and no more 深水区 than the official pointer's route
const nxtP = smooth(spfa(true), true);
const cost = new Map([[D, 0]]);
const wet = new Map([[D, 0]]);
const chosen = new Map();
const skips = (a, o, p) => {
const t = through(a, p);
@@ -155,9 +161,11 @@ function official(sid, { crates: extraCrates = [], blocks: extraBlocks = [], rec
if (k === D) continue;
const a = unkey(k), o = nxt.get(k), p = nxtP.get(k);
const co = segNb(a, o) + cost.get(key(o)), cp = segNb(a, p) + cost.get(key(p));
const useP = cp < co || (cp === co && key(o) !== key(p) && key(chosen.get(key(o)) ?? []) === key(p) && skips(a, o, p));
const wo = segWet(a, o) + wet.get(key(o)), wp = segWet(a, p) + wet.get(key(p));
const useP = wp <= wo && (cp < co || (cp === co && key(o) !== key(p) && key(chosen.get(key(o)) ?? []) === key(p) && skips(a, o, p)));
chosen.set(k, useP ? p : o);
cost.set(k, useP ? cp : co);
wet.set(k, useP ? wp : wo);
}
nxt = chosen;
}
+39 -1
View File
@@ -1,7 +1,8 @@
// Official route shapes (community report after 0.1.0, D5: on 战场#04 活性源石 the lower-gate enemies walked two tiles
// straight left of the red gate and then straight up, instead of crossing diagonally from the 4th row to the 3rd as in
// the official game). grid.js keeps the official flow field (research 08 §3.1) unless the blockable-ground preference
// route of user playtest #2 (0.1.0's) crosses strictly fewer non-blockable tiles (floor, gates) — a segment that only
// route of user playtest #2 (0.1.0's) crosses strictly fewer non-blockable tiles (floor, gates) and no more 深水区
// (GitHub #375) — a segment that only
// brushes a floor tile's corner does not cross it. Audit: every stage (active or not) × gate × field against the pure
// official algorithm; the routes that still differ are listed, each avoiding floor the official route walks over.
import { test } from 'node:test';
@@ -127,6 +128,43 @@ test('audit: every route of every stage (11, active or not) × gate × field is
assert.equal(official, stages.length * legs.length - seen.size);
});
// GitHub #375 (FrogThai: 「水图boss会从下面这条路往蓝门走,实际应该从上路走」): on 战场#08(下半) 涨潮控制 the patrolling leaders
// (铳 — level_act1autochess_h07_02 route 6 —, 卢西恩 — act2autochess_h07_05 route 8: PATROL_MOVE (3,9) → (2,3) → (5,9))
// took the lower road (3,9) → (2,3) through the two 深水区 tiles (3,6) / (4,6) — one floor tile fewer than the official
// upper road along row 5, which crosses one water tile. The tie-break may not wade into more 深水区 than the official
// route (grid.js), so these legs are official again.
test('#375 战场#08(下半): the (3,9) → (2,3) patrol leg stays on the official upper road', REAL, () => {
const sid = 'act2autochess_m04';
const g = stageGrid(sid, GEO.BOSS_RECT), pure = stageGrid(sid, GEO.BOSS_RECT, true);
for (const [s, e, wp] of [
[[3, 9], [2, 3], '3,9 5,9 5,4 2,4 2,3'],
[[3, 11], [2, 17], '3,11 5,11 5,16 2,16 2,17'],
[[3, 10], [2, 3], '3,10 5,9 5,4 2,4 2,3'],
[[4, 10], [3, 9], '4,10 3,9'],
]) {
assert.deepEqual(g.waypoints(s[0], s[1], e[0], e[1]), pure.waypoints(s[0], s[1], e[0], e[1]), `(${s}) → (${e})`);
assert.equal(str(g.waypoints(s[0], s[1], e[0], e[1])), wp);
}
});
test('#375 real battle (act2 m04, 铳 on act1autochess_h07_02): the patrol walks row 5 to (2,3) and steps on one 深水区 only', REAL, () => {
const h = makeBattle({ kind: 'boss', stageId: 'act2autochess_m04', waveTemplate: 'act1autochess_h07_02', units: [], autoFinish: false, timeLimit: 120,
sharedBoss: { hp: 1e12, maxHp: 1e12, damage(pid, n) { this.hp -= n; } } });
const rows = getDefaultSource().getStage('act2autochess_m04').raw.rows;
const tiles = [];
h.b.on('tick', () => {
const e = h.b.enemies.find((u) => u.alive && u.defId === 'enemy_9017_achunt');
if (!e) return;
const k = `${Math.round(e.y)},${Math.round(e.x)}`;
if (tiles[tiles.length - 1] !== k) tiles.push(k);
});
assert.ok(h.runUntil(() => tiles.includes('2,4'), 60), `reaches (2,4): ${tiles.join(' ')}`);
const upTo = tiles.slice(0, tiles.indexOf('2,4') + 1);
assert.deepEqual(upTo.slice(0, 4), ['4,10', '3,9', '4,9', '5,9'], `up to row 5 first: ${upTo.join(' ')}`);
const water = upTo.filter((k) => { const [r, c] = k.split(',').map(Number); return rows[r][c] === 'd'; });
assert.deepEqual(water, ['5,6'], `深水区 stepped on: ${water.join(' ')} (route ${upTo.join(' ')})`);
});
test('D5: an operator on (9,9), (9,8) or (10,8) blocks the lower-gate enemy on the official diagonal', REAL, () => {
const guard = chessRec({ id: 't_guard', profession: 'WARRIOR', stats: { atk: 0, blockCnt: 3, maxHp: 1e6 }, skill: null });
const walker = enemyRec({ key: 'enemy_walker', hp: 1e6, speed: 1 });
+137
View File
@@ -0,0 +1,137 @@
// The 传送门 (portal) of the boss / hidden-core templates (GitHub #336, PR #337 by @2321Robin): an enemy flies or walks
// into the portal and reappears on the far side. Four official routes spell the crossing as DISAPPEAR → WAIT → MOVE-to-the-exit with no APPEAR step
// (act1autochess_h07_02 route #4, act1autochess_h08_02 routes #5/#9, act2autochess_h07_05 route #6), while their twin
// routes in the same templates write it out as DISAPPEAR → WAIT → APPEAR[exit]. Taken literally, an appear-less route
// made the enemy walk its whole remaining route invisible and then leak unseen — the blue door takes the leak and the
// team never sees who or how (user report: 打 boss 时小怪进传送门后被判定进蓝门,全程隐身). normalizeRoute re-inserts
// the exit APPEAR ahead of that MOVE — at the MOVE's own target, which is exactly where the twins appear.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { normalizeRoute, hasGeneratedData } from '../../server/sim/simdata.js';
import { makeBattle, enemyRec } from '../helpers/battleHarness.js';
const REAL = { skip: !hasGeneratedData() && 'no generated data' };
/** Leg signature of a normalized route: `MOVE[r,c]` / `APPEAR[r,c]` / `DISAPPEAR` / `WAIT`. */
const sig = (r) => r.checkpoints.map((c) => c.type + (c.type === 'MOVE' || c.type === 'APPEAR' ? `[${c.pos[0]},${c.pos[1]}]` : ''));
/** The test enemy's leaks (the templates' own spawns leak too). */
const leaksOf = (h) => h.hooksOf('enemyLeak').filter((l) => l.enemy.defId === 'enemy_test_portal');
/** The test enemy's hidden spans: [{ from: [r,c], to: [r,c], dt }] (vanish tile, reappear tile, seconds hidden). */
function hiddenSpans(h) {
const spans = [];
let open = null;
const set = h.b._setHidden.bind(h.b);
h.b._setHidden = (e, on) => {
if (e.defId === 'enemy_test_portal' && on !== !!e.hidden) {
const at = [Math.round(e.y), Math.round(e.x)];
if (on) open = { at, t: h.b.time };
else if (open) { spans.push({ from: open.at, to: at, dt: h.b.time - open.t }); open = null; }
}
return set(e, on);
};
return spans;
}
test('normalizeRoute: DISAPPEAR → WAIT → MOVE gains the twin APPEAR at the move target', () => {
const r = normalizeRoute({
motion: 'FLY', start: [2, 10], end: [1, 3],
steps: [
{ t: 'move', p: [2, 11] }, { t: 'move', p: [1, 17] }, { t: 'disappear' }, { t: 'wait', s: 3 },
{ t: 'move', p: [5, 10] }, { t: 'move', p: [5, 9] },
],
});
assert.deepEqual(sig(r), ['MOVE[2,11]', 'MOVE[1,17]', 'DISAPPEAR', 'WAIT', 'APPEAR[5,10]', 'MOVE[5,10]', 'MOVE[5,9]']);
});
test('normalizeRoute: a DISAPPEAR the data does spell out with an APPEAR is kept as the only one', () => {
const r = normalizeRoute({
motion: 'FLY', start: [2, 10], end: [1, 17],
steps: [
{ t: 'move', p: [1, 3] }, { t: 'disappear' }, { t: 'wait', s: 3 }, { t: 'appear', p: [5, 10] }, { t: 'move', p: [5, 13] },
],
});
assert.equal(r.checkpoints.filter((c) => c.type === 'APPEAR').length, 1);
assert.deepEqual(sig(r), ['MOVE[1,3]', 'DISAPPEAR', 'WAIT', 'APPEAR[5,10]', 'MOVE[5,13]']);
});
test('normalizeRoute: idempotent — a second pass over a fixed route changes nothing', () => {
const once = normalizeRoute({
motion: 'FLY', start: [2, 10], end: [1, 3],
steps: [{ t: 'move', p: [1, 17] }, { t: 'disappear' }, { t: 'wait', s: 3 }, { t: 'move', p: [5, 10] }],
});
assert.deepEqual(normalizeRoute(once), once);
});
// The four official appear-less routes on their real templates: after the portal the enemy must be back in the open
// at the exit [5,10] (where the twins appear) and the route's leak at the blue door must happen VISIBLE.
const PORTAL_EXIT = [5, 10];
const CASES = [
['act1autochess_h07_02', 4, 'FLY'],
['act1autochess_h08_02', 5, 'WALK'],
['act1autochess_h08_02', 9, 'WALK'],
['act2autochess_h07_05', 6, 'FLY'],
];
for (const [tplId, routeIdx, motion] of CASES) {
test(`${tplId} route #${routeIdx}: portal crossing reappears at [${PORTAL_EXIT}] and leaks visible`, REAL, () => {
const h = makeBattle({
kind: 'boss', waveTemplate: tplId, content: 'none',
defs: { enemies: { enemy_test_portal: enemyRec({ key: 'enemy_test_portal', hp: 1e6, speed: 3, motion }) } },
enemies: [{ key: 'enemy_test_portal', route: routeIdx }],
autoFinish: true, timeLimit: 400, hooks: ['enemyLeak'],
});
const e = () => h.enemy('enemy_test_portal');
assert.ok(h.runUntil(() => e() && e().hidden, 400), 'reaches the portal (disappears)');
assert.ok(h.runUntil(() => e() && !e().hidden, 400), 'reappears after the portal');
assert.deepEqual([Math.round(e().y), Math.round(e().x)], PORTAL_EXIT, 'reappears at the portal exit');
h.runToEnd(400);
const [leak, ...more] = leaksOf(h);
assert.ok(leak, 'the route still ends in its leak');
assert.equal(more.length, 0, 'one leak');
assert.equal(leak.enemy.hidden, false, 'the leak is visible');
});
}
// A route that ENDS on a portal entrance does not leak there: the entrance teleports the enemy out of the far exit
// ([5,10] — the same pairing every explicit DISAPPEAR / APPEAR pair in the data reads) and it walks on to the blue
// door on that side of the field, where the leak happens (GitHub #336: 打 boss 小怪第一次进传送门就被判定进蓝门,
// 官方是传送后走向蓝门). The solo boss routes are exactly this shape: straight from the centre into the corner portal.
// Inside the portal it stays hidden for 3 s, as the explicit crossings of the same tiles (WAIT 3 in 85 of the 95).
test('solo boss: a route ending on the left portal entrance leaks at the left blue door, not the portal', REAL, () => {
const h = makeBattle({
kind: 'boss', stageId: 'act1autochess_m01', rect: { r0: 0, r1: 5, c0: 0, c1: 20 },
waveTemplate: 'act1autochess_h07_02_s', content: 'none',
defs: { enemies: { enemy_test_portal: enemyRec({ key: 'enemy_test_portal', hp: 1e6, speed: 3 }) } },
enemies: [{ key: 'enemy_test_portal', route: 0 }],
autoFinish: true, timeLimit: 300, hooks: ['enemyLeak'],
});
const spans = hiddenSpans(h);
const e = () => h.enemy('enemy_test_portal');
assert.ok(h.runUntil(() => e() && Math.round(e().y) === 1 && Math.round(e().x) === 3, 300), 'reaches the portal entrance [1,3]');
assert.ok(h.runUntil(() => e() && Math.round(e().y) === 5 && Math.round(e().x) === 10, 300), 'teleports out of the exit [5,10]');
h.runToEnd(400);
const [leak, ...more] = leaksOf(h);
assert.ok(leak, 'leaks eventually');
assert.deepEqual([Math.round(leak.enemy.y), Math.round(leak.enemy.x)], [2, 2], 'the leak is at the left blue door [2,2]');
assert.equal(more.length, 0, 'stepping into the portal entrance is no leak of its own');
assert.equal(spans.length, 1, 'one portal crossing');
assert.deepEqual([spans[0].from, spans[0].to], [[1, 3], [5, 10]], 'hidden from the entrance to the exit');
assert.ok(Math.abs(spans[0].dt - 3) < h.TICK * 1.5, `hidden 3 s inside the portal (${spans[0].dt.toFixed(3)} s)`);
});
test('a route ending on the right portal entrance mirrors: leak at the right blue door', REAL, () => {
const h = makeBattle({
kind: 'boss', stageId: 'act1autochess_m01', rect: { r0: 0, r1: 5, c0: 0, c1: 20 }, content: 'none',
defs: { enemies: { enemy_test_portal: enemyRec({ key: 'enemy_test_portal', hp: 1e6, speed: 3 }) } },
enemies: [{ key: 'enemy_test_portal', route: { motion: 'WALK', start: [2, 10], end: [1, 17], checkpoints: [] } }],
autoFinish: true, timeLimit: 300, hooks: ['enemyLeak'],
});
const spans = hiddenSpans(h);
h.runToEnd(400);
const [leak, ...more] = leaksOf(h);
assert.ok(leak, 'leaks eventually');
assert.deepEqual([Math.round(leak.enemy.y), Math.round(leak.enemy.x)], [2, 18], 'the leak is at the right blue door [2,18]');
assert.equal(more.length, 0, 'stepping into the portal entrance is no leak of its own');
assert.deepEqual(spans.map((x) => [x.from, x.to]), [[[1, 17], [5, 10]]], 'one crossing, entrance to exit');
assert.ok(Math.abs(spans[0].dt - 3) < h.TICK * 1.5, `hidden 3 s inside the portal (${spans[0].dt.toFixed(3)} s)`);
});