diff --git a/docs/PLAYING.md b/docs/PLAYING.md index 45bb2b7c..0a9ab9cf 100644 --- a/docs/PLAYING.md +++ b/docs/PLAYING.md @@ -73,7 +73,7 @@ - 拖动时模型一直在指针 / 手指下(脚在指针下方约半格,鼠标和触屏一样),**指针所在的格子**就是落点并高亮;装备拖到哪个格子,就配发给那个格子上的干员;在商店栏上松手则放回原位。 - 点击干员显示攻击范围和底部按钮(「撤退」回到整备区、「出售 +1」);右键 / 长按查看详细信息:头像旁是盟约,下面第一项就是干员的**特质**(如「休整期结束时 …」),之后是特性、属性、攻击范围、技能、模组、装备、天赋。属性是**实时数值**:作战中每秒刷新 4 次(当前生命、攻击、防御、法抗、攻击间隔、阻挡),休整期显示棋盘上的干员**开战时**的数值(已算入装备、盟约层数、特质、策略与机变效果);高于基础值为绿色、低于为红色并标出差值(攻击间隔变短算增益),鼠标悬停可看基础值。 - **召唤物手动摆放**:把带可部署召唤物的干员放上棋盘后,它的召唤物会进入整备区(赫默的医疗探机、巫恋的诅咒娃娃、凯瑟琳的 2 个支援装置、浊心斯卡蒂的海嗣、伺夜的狼群、缪尔赛思的流形;同一干员的召唤物叠在一格,不占部署位),像干员一样拖到棋盘上并选方向。凯瑟琳的支援装置给**它面朝的那一格**上的干员加屏障,所以要放在干员旁边、朝向干员。**战术家的援军**(伺夜的狼群、缪尔赛思的流形,也就是选战术点)和官方一样**只能放在召唤者的攻击范围内**:拖动时只有这些格子会亮起;之后在原地给战术家换朝向时,不在新攻击范围里的援军会退回整备区(还在范围里的保持不动;整备区和临时整备区都满、援军无处可退时,这次换朝向不生效并提示「整备区已满」)。撤退或出售干员时它的召唤物一起移除;**移动干员到别的格子时(包括把召唤物拖到干员身上交换位置),它已摆放的召唤物会退回整备区**,需要重新摆放。整备区满时退回的召唤物进入临时整备区,休整期结束还没处理的会在下一回合重新回到整备区。 -- 作战中:海嗣、狼群、流形、支援装置开战时就在摆放的位置部署;医疗探机、诅咒娃娃开战时也在摆放的位置免费部署一次(持续 10 / 15 秒,和官方一样不需要先开技能),之后所属干员每次发动技能时再次在原位出现;所属干员被击倒、再部署回场时,技能给的那一个如果还没出现,会立刻出现。巫恋退场时诅咒娃娃随之撤退,赫默退场时已出现的医疗探机不受影响。医疗探机、诅咒娃娃、支援装置、海嗣没有摆放就不会出现;狼群、流形(战术家的援军)没有摆放时会在战术点出现。狼群最后一只狼影被击倒后进入战术点形态:暂时离开战斗(不阻挡、不攻击、不会被攻击,格子仍然占着),经过狼影的恢复时间(25 秒)后在原地满血、带着 1 只狼影回来,之后重新开始每 25 秒加 1 只狼影;伺夜退场时狼群随之撤退,伺夜再部署回场时狼群重新带着 2 只狼影出现。 +- 作战中:海嗣、狼群、流形、支援装置开战时就在摆放的位置部署;医疗探机、诅咒娃娃开战时也在摆放的位置免费部署一次(持续 10 / 15 秒,和官方一样不需要先开技能),之后所属干员每次发动技能时再次在原位出现;所属干员被击倒、再部署回场时,技能给的那一个如果还没出现,会立刻出现。巫恋退场时诅咒娃娃随之撤退,赫默退场时已出现的医疗探机不受影响。医疗探机、诅咒娃娃、海嗣(以及自选干员的工程蓄水炮、沙地兽、打字机、本能的召唤)这类不会受到攻击、到时间就离场的召唤物不受任何伤害(活性源石也一样)、也不会被治疗,它们的血条就是倒计时:随时间减少,空了就离场。医疗探机、诅咒娃娃、支援装置、海嗣没有摆放就不会出现;狼群、流形(战术家的援军)没有摆放时会在战术点出现。狼群最后一只狼影被击倒后进入战术点形态:暂时离开战斗(不阻挡、不攻击、不会被攻击,格子仍然占着),经过狼影的恢复时间(25 秒)后在原地满血、带着 1 只狼影回来,之后重新开始每 25 秒加 1 只狼影;伺夜退场时狼群随之撤退,伺夜再部署回场时狼群重新带着 2 只狼影出现。 - **阻挡**看接触:干员阻挡中心距离约 0.7 格以内、与它接触的敌人(地面干员挡不住飞行和近地悬浮的敌人)。敌人卡在两名干员之间时,阻挡它的干员倒下、撤退或被晕眩后,另一名仍与它接触、还有空余阻挡数的干员会**立刻接替阻挡**;都没有空位时敌人才会走掉。迎面走来的敌人会停在干员格子前面。**阻挡了就一定能打到**:任何干员(包括站在近战位的远程干员和召唤物)总能攻击自己阻挡的敌人,不管它在不在攻击范围内、在干员哪一侧,并且优先攻击它(医疗干员除外:咒愈师以外的医疗挡住敌人时照常治疗队友,不会攻击它;「停止攻击」类技能期间也除外;技能原文另有限制的也按原文:普罗旺斯二技能开启时不以生命值高于 80% 的敌人为目标,寒檀二技能的冰凌随机落在攻击范围内的地块上)。 - 开战时干员按**逐列从左到右、每列从上到下**的顺序部署(最左一列最先,与阿戈尔吞噬「更靠左和靠上」的顺序相同),召唤物在所有干员之后。被阻挡的敌人先打阻挡它的干员;没被阻挡的远程敌人先打嘲讽等级高的,同级时打最后部署的(召唤物、最右一列靠下的干员和刚再部署的干员更容易挨打)。隐匿的干员只会被它阻挡的敌人攻击,没被它挡住的敌人的溅射、爆炸、范围技能、留下的伤害区域(包括假想敌:蚀裂被击倒后喷出的毒雾)以及深池伙友卫队、扎罗的减攻速光环也落不到它身上(和官方一样;写明「无视无法选择」的效果如污染秽蚀照常能打到,寒霜的减攻速光环和“墓碑”这类地图效果照常生效);**迷彩**(如忍冬三技能期间击倒敌人后、寒芒克洛丝一技能)同样只会被它阻挡的敌人攻击,也显示为半透明,但躲不开溅射、爆炸和范围伤害(同时打范围内所有单位的普通攻击——如“斩胄之剑”“破胄之锤”“灵幛”——打不到它),也不算隐匿(不触发叙拉古等「隐匿时」效果)。叙拉古的隐匿按时(32 秒 + 每层 0.4 秒)结束;忍冬的迷彩按官方持续到她下一次开技能。**蒂比起飞**期间只挡飞行敌人,地面敌人不会选她为目标(普通攻击、溅射、技能范围、燃烧区域和要选中目标的减益都落不到她身上);飞行和悬浮敌人、写明「无视无法选择」的效果(如污染秽蚀)照常能打到她;地面敌人的光环和全场效果(如“墓碑”让全场攻击力、防御力减半)、她攻击敌人时招来的反制(如重装侦察兵的暴露)照常生效,起飞前已挂上的持续伤害也照常跳。 - **技能自动释放**按官方「技能策略」:一般的手动技能在技能就绪、即将攻击 / 治疗且初始攻击范围内有敌人(治疗技能:有受伤的队友)时释放;**带独立技能范围**的技能(如德克萨斯「剑雨」、凛御银灰「御敌的锋锐」、锏「归于宁静」,不含「攻击范围扩大」类)在**技能范围内出现敌人**时就释放,不必先攻击;**开启后攻击范围比平时大**的手动技能(如莫斯提马「序时之匙」、银灰「真银斩」、史尔特尔「黄昏」、白面鸮「脑啡肽」)在**开启后的攻击范围内出现敌人**(治疗技能:有受伤的队友)时就释放,不必等敌人走进平时的攻击范围,这是本作按玩家反馈有意与官方不同的地方;**重装干员**的手动技能在受到伤害时释放(例外:深巡、雷蛇的二技能,号角的二、三技能,灰毫的一、二技能按玩家反馈改为攻击范围内有敌人时就释放(深巡二技能按开启后更远的攻击范围判断),余的二技能「厚礼上宾」改为周围两格(技能范围)内有敌人时就释放,这是本作有意与官方不同的地方;深巡一技能仍要受到伤害);执旗手 / 战术家 / 吟游者技能就绪即释放,解放者 / 阵法术师在初始攻击范围内有敌人时释放(开启后攻击范围扩大的技能——薄绿「风语」、蜜蜡「沙暴扩散」、卡涅利安「食噬之印」、玛恩纳「未宽解的悲哀」,以及安洁莉娜「秘杖·反重力模式」——同样按开启后的攻击范围判断)。**自动触发**的技能按其本身规则:只作用于队友的(如引星棘刺「度算浪波」、浊心斯卡蒂「同葬无光之愿」、纯烬艾雅法拉「无声润物」)技力满就释放,不用等敌人或伤员;伺夜「领袖的呼唤」「领袖的馈赠」只在场上有狼群时释放:狼群处于战术点形态时,「领袖的呼唤」也会释放并让它立刻带着 1 只狼影回到场上(狼影未满时改为加 1 只狼影,已满时把狼群的生命值回满),「领袖的馈赠」也会释放(立即获得费用,强化留给回来的狼群的下一次攻击);又如古米「备用军粮」在身边(技能范围内)有受伤的队友时就会发动,下一次攻击改为治疗该队友(不用先接敌;她的二技能「食粮烹制」是手动技能,按重装规则在受到伤害时释放);塞雷娅「急救」在她即将攻击时、身边有生命不高于一半的队友才发动,这次攻击直接改为治疗;新约能天使二技能的护盾按官方给攻击范围内**仇恨最高**的队友(嘲讽等级高的优先,同级时给最后部署的),不一定是站在最前面挨打的那个。手动技能的自动释放有 3 秒冷却:开战部署后先等 3 秒,之后同一干员两次自动释放至少间隔 3 秒(可充能的技能不会同时放完)。 diff --git a/docs/SIM.md b/docs/SIM.md index 41662235..586f7e7f 100644 --- a/docs/SIM.md +++ b/docs/SIM.md @@ -126,7 +126,10 @@ owner's decision of 2026-10-06, kits/ops/op-wang.js); a skill's summon (赫默 S and regardless of the holding (PRTS §作战阶段 "所有手动部署的召唤物,无视所属干员的持有状态…作战开始时立即部署一次"), then waits on its tile and takes the field there each time the skill gives one (stock ≤ 1, after the token's redeploy time once it left, free; never while its owner is off the field [ASSUMED] — a stocked one deploys as soon as the owner is -back; the doll also leaves when 巫恋 leaves, the drone stays when 赫默 leaves: PRTS token 备注). A skill's summon, a +back; the doll also leaves when 巫恋 leaves, the drone stays when 赫默 leaves: PRTS token 备注). The countdown summons — +those that "不会受到攻击" and leave after a fixed time (医疗探机, 诅咒娃娃, 海嗣; 自选: 工程蓄水炮, 沙地兽, “打字机”, 本能的召唤; +content/tokens.js `COUNTDOWN_SUMMONS`) — hold 无敌 + 禁疗 from each deployment (`startCountdown`) and their snapshot hp is +maxHp × the share of their life left (`unit.countdown`; community report of 2026-10-06, 0.2.0). A skill's summon, a device or 海嗣 that was not placed never appears (the hidden 待部署区 deploys nothing by itself); the tacticians' 狼群 / 流形 still come as their 援军 on a tactical point without a piece (content/tokens.js `tacticalPoint`). Their piece stands inside the tactician's attack range — the prep enforces "只能部署在召唤者攻击范围内" (tokens.json `ownerRange`, player diff --git a/docs/design/network.md b/docs/design/network.md index 0d8a2748..250c8681 100644 --- a/docs/design/network.md +++ b/docs/design/network.md @@ -23,7 +23,7 @@ S→C: - NOTE: game time in `b.snap`/`b.ev` is carried as **`gt`** (the frame's `t` is the message type). - Active finite zero-SP timed skills display `sp=timeLeft`, `spMax=duration` and `UF.SKILL`; after end they display `0/0`. Snapshot display capacity does not change runtime SP/cost. -- `b.snap` — `{ fieldId, gt, units: [[id, x, y, hp, maxHp, sp, spMax, flags, anim]] , dp, killed, total, down?, elem? }` 20 Hz. Optional, only when non-empty (§18.3): `down: [[id, respawnAt (game s), respawnTime (s), state, row, col]]` — knocked-out operators waiting to redeploy, `state` 0 counting | 1 timer done, waiting for DP | 2 timer done, its tile taken — a safeguard, no ally deploys there (sim/constants.js `DOWN_STATE`); `row, col` = the tile it lies on and comes back on (§21.24); `elem: [[id, element, fill, cooldownEnd (game s), cooldown (s)]]` — the one gauge a unit shows: the fullest (ties by the official element id 神经 < 侵蚀 < 灼燃 < 凋亡), fill with 2 decimals rounded DOWN and kept at 0.01–0.99 outside a burst (§20.8); during a 爆发冷却 the bursting element with fill 1 and the cooldown numbers (else 0, 0) +- `b.snap` — `{ fieldId, gt, units: [[id, x, y, hp, maxHp, sp, spMax, flags, anim]] , dp, killed, total, down?, elem? }` 20 Hz (a countdown summon's `hp` is maxHp × the share of its life left, §25.16.5). Optional, only when non-empty (§18.3): `down: [[id, respawnAt (game s), respawnTime (s), state, row, col]]` — knocked-out operators waiting to redeploy, `state` 0 counting | 1 timer done, waiting for DP | 2 timer done, its tile taken — a safeguard, no ally deploys there (sim/constants.js `DOWN_STATE`); `row, col` = the tile it lies on and comes back on (§21.24); `elem: [[id, element, fill, cooldownEnd (game s), cooldown (s)]]` — the one gauge a unit shows: the fullest (ties by the official element id 神经 < 侵蚀 < 灼燃 < 凋亡), fill with 2 decimals rounded DOWN and kept at 0.01–0.99 outside a burst (§20.8); during a 爆发冷却 the bursting element with fill 1 and the cooldown numbers (else 0, 0) - `b.ev` — `{ fieldId, ev: [ ... ] }` event tuples: `['spawn', UnitInfo]`, `['atk', srcId, tgtId, projKind]`, `['dmg', tgtId, amount, type]`, `['heal', tgtId, amount]`, `['skill', id, on:0|1]`, `['die', id, reason]` (reason `'killed'`, `'retreat'`, `'expired'` …; only `'killed'` plays an operator's knock-down sound), `['status', id, key, on]`, `['fx', kind, x, y, extra]` (`'hitCap' {id, n}` = a cancelled 限伤 hit, drawn as nothing, §20.12; `extra.form` = the unit's model form from then on (an enemy's mode, a 傀儡师's 替身 §22.11) — `shared/protocol.js fxForm`, state like a spawn: never dropped by the client, §21.4), `['layer', playerId, bondId, n]`, `['bounty', playerId, coins]`, `['deploy', id]` - `m.toast {kind, text}` · `m.ticker {text, id, type, priority, playerId}` (the strip plays the highest priority first, §21.10) · `m.emote {playerId, id}` · `m.result {...}` - `m.unitStats {seq, round, units: [unitStatsEntry]}` — the answer to `g.unitStats` (`seq` echoed): every own board operator / summon by uid with `{id, uid, defId, hp, alive, maxHp, atk, def, res, interval, blockCnt, moveSpeed, range?, base: {…same}}` (shared/protocol.js `unitStatsEntry`; `range` = an ally's live range grid, facing RIGHT, never a target-selection grid, §21.16) as its next battle starts (§18.5) diff --git a/docs/history/0.2.0.md b/docs/history/0.2.0.md index 51510f18..5439f6fb 100644 --- a/docs/history/0.2.0.md +++ b/docs/history/0.2.0.md @@ -29,7 +29,9 @@ bounty burned to death by the 活性源石, 枯朽之种 paying a bounty) → § 2026-10-06, stream CB5 (match flow and UI: a module's 特性追加 line after the class trait, the new shop slot filled at the upgrade, an eliminated player following a player through every round, the boss field and its leader in a watcher's boss prep, the pair together in the boss prep with the official green frames; PRs #189 / #191 reused, not merged; the -battle-history and hotkey requests left to the owner) → §25.19. +battle-history and hotkey requests left to the owner) → §25.19; the community reports of 2026-10-06, stream CB2 (healers, summons, timers: 刺玫's S2 heals, 琳琅诗怀雅 healing herself and her 破财消灾 cost after a +redeploy, 赫默's drone at full SP, the countdown summons' 无敌 / 禁疗 / timer bar, stacked alchemy zones; the chain heal, +DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16. Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1); 2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4); @@ -1211,3 +1213,98 @@ one commit with its tests; two open community PRs were reused without merging th a sanitised map in the settings (`sanitizeSettings`), `shortcutFor` reading it, every `` hint and tooltip reading it too, i18n and tests. Decision: whether to allow it at all (the official PC client's keys are fixed as far as the data shows: `pc_key_holder` hints). +### 25.16 Community reports of 2026-10-06, stream CB2: healers, summons, timers — `kits/ops/chess_char_1_06-vendla.js`, `chess_char_3_04-swire2.js`, `chess_char_2_02-silent.js`, `content/tokens.js COUNTDOWN_SUMMONS / startCountdown`, `sim/snapshot.js unitTuple`, `render/fx/zones.js zoneAlpha` + +The owner's second collection of community reports (2026-10-06/07) was split over five streams; CB2 took the healers, +the summons and the timers (items 11, 12, 13, 14, 18, 26, 31, 32, 33, 34 of the list). Each was reproduced on the 0.2.0 +tree with a script first (on the v0.1.4 sources too where the report could be older), then checked against the official +data — the local client's battle prefabs (charpack, token and skill prefabs, projectiles; abnormal flag ids per PRTS 异常 +效果: 5 无敌, 7 禁疗), ArknightsGameData's buff templates, skill_table / character_table, PRTS (备注 included) — and the +earlier decisions. Six were real and are fixed, one commit each with its golden scenarios named; four are answered with +the evidence. + +#### 25.16.1 「刺玫开技能(装备源石药剂)之后不能治疗」 (item 12): only the S2 counter's trait heal goes to her protégé — `kits/ops/chess_char_1_06-vendla.js` + +- **Seen**: during 荆藤庇荫 every trait heal went to the protégé, usually at full HP — a 源石溶剂 carrier drained from 1017 to + 297 HP over the skill with all eight heals spent on a 9000 / 9000 ally. +- **Official**: 「该角色受到攻击时刺玫对目标造成攻击力20%的法术伤害并仅对该角色触发刺玫特性」 (en "…and activates her Trait on the + ally"); the client buff `vendla_tr` heals through the selector S2TraitHealRange (the `vendla_s_2` holder) only for a + damage whose modifier carries the key `vendla_s_2` — the counter, `vendla_s_2[extra_damage]` — else through + TraitHealRange (`_excludeOwner` 0, 禁疗 excluded). +- **Now**: the counter's damage names the protégé (`traitAlly`); every other damage she deals heals the lowest-HP ally of + her range, herself included, skill running or not. +- Golden: roster-001 / 002, bond-deputShip-low / -high, bond-suntShip-high, bond-victoriaShip-low / -high, the bot matches + solo-FUNNY-2, coop2-NORMAL-3, coop3-ABYSS-5, coop4-FUNNY-6, coop4-ABYSS-7. Tests: + `test/content/feedback5-vendla-heal.test.js`. + +#### 25.16.2 「琳琅诗怀雅不会治疗自己」 (item 18): S1 仗义疏财 heals her own tile too — `kits/ops/chess_char_3_04-swire2.js` + +- **Official**: the client charpack (char_1033_swire2, mode S1, ability HealAlly) selects with range x-4 — the 3×3 with her + own tile —, ally side, `_excludeOwner` 0, max HP ratio 0.7, one target. **Cause**: the kit skipped her and kept + Chebyshev distance 1 only. **Now**: the lowest HP ratio below 70 % of the 3×3, herself included (her own 禁疗 still + stops it); the owner's cadence of 2026-10-04 is unchanged. +- Golden: roster-027. Tests: `test/content/feedback5-swire2-self-heal.test.js`. + +#### 25.16.3 「进入联防阶段琳琅诗怀雅所需的复活费用应该重置,正常对局中死亡之后再部署复活费用也应该重置」 (item 33): 破财消灾 restarts at 5 DP with every deployment — `kits/ops/chess_char_3_04-swire2.js` + +- **Official**: PRTS 破财消灾 备注 「再部署时重置本天赋费用消耗」; the client buff `swire2_t_2` doubles the `cost` of its own + blackboard, rebuilt when she is deployed. **Cause**: the kit kept its save count for the whole battle (5 → 10 → 20 across + a redeploy). **Now**: reset at each of her deployments (battle start, redeploy, 突袭 再部署); a 联防 field is a battle of + its own, so it always started at 5 (pinned). +- Golden: roster-008 (a cheaper save after a redeploy), roster-049 (snapshots only). Tests: + `test/content/feedback5-swire2-revive-cost.test.js`. + +#### 25.16.4 「赫默的无人机在2技能好了之后应该秒放」 (item 34): S2 医疗无人机 casts at full SP — `kits/ops/chess_char_2_02-silent.js` + +- **Seen**: S2 full at 26 s and no cast while nobody was hurt; the drone came only once an ally in her range was injured. +- **Official**: the client skill `skchr_silent_2` (AUTO) has no `_trigger`, `_allowNoTarget` 1, `_checkHasTargetBeforeDoCast` + 0 and a RechargeToken action — no cast condition; `_stopSpWhenTokenIsFull` 1 = PRTS S2 备注 「库存达到上限后赫默获得阻回, + 直到库存被消耗为止」. +- **Now**: trigger SP_FULL — the placed drone takes its tile the moment S2 is ready; 阻回 (`noSp`) while her stock is full. +- Golden: roster-003 / 005, bond-visiShip-low / -high. Tests: `test/content/feedback5-silent-drone.test.js`. + +#### 25.16.5 「赫默的无人机、浊心斯卡蒂的海嗣等这种召唤物不会受到外来伤害(包括源石地板),也不会被干员治疗,血量随时间减少」 (item 26): the countdown summons — `content/tokens.js COUNTDOWN_SUMMONS / startCountdown`, `sim/snapshot.js unitTuple`, `sim/units.js` (`countdown`), the kits of 浊心斯卡蒂, 温蒂, 莱伊, 鸿雪, 酒神 + +- **Seen**: a 海嗣 on 活性源石 lost 70 of its 100 HP every second; a hurt drone drew every medic's heals and the 吟游者 + regeneration; their bars were plain HP. +- **Official / sources**: PRTS 斯卡蒂的海嗣 备注 「持有禁疗、无敌」; the token prefabs give 海嗣 and 沙地兽 the abnormal flags 5 + + 7, 工程蓄水炮 7; their lives are suicide buffs (`suicide_after_buff`, the drone's `skcom_withdraw`). The report describes + the official mode: no outside damage, no heal, a bar that runs down like a timer and the summon gone when it is empty. +- **Now**: the summons that "不会受到攻击" and leave after a fixed time — 医疗探机 (10 s), 诅咒娃娃 (15 s), 斯卡蒂的海嗣 (25 / 30 + s) and the 自选 工程蓄水炮 (20 s), 沙地兽 (25 s), “打字机” (25 s), 本能的召唤 (10 s) — hold 无敌 + 禁疗 from each deployment + (`startCountdown`, called where each lifetime is scheduled) and the snapshot's hp is maxHp × the share of the life left + (`unit.countdown`). The sim HP stays full, so no selector sees a low-HP summon. Summons that block and are attacked + (沙之碑, 战术装备, “小自在”, 结构性原理, 狼群 …) keep real HP. +- [ASSUMED]: 无敌 + 禁疗 for the four whose base prefabs carry no flag (医疗探机, 诅咒娃娃, “打字机”, 本能的召唤), and the bar as + a display of the time left — from the report; no HP-drain node exists in the client data. +- Golden: snapshot hashes only (the bar): roster-003 / 016 / 018 / 035 / 045 / 046, bond-visiShip-low / -high, + boss-boss_1 / 3 / 5 / 7-pair, unite-1; gameplay too: roster-005 (medics no longer heal the drone), roster-037 and + hidden-boss_9-pair (a 海嗣 / drone under damage stays up). Tests: `test/sim/feedback5-countdown-summons.test.js`. Docs: + docs/SIM.md (token pieces), docs/PLAYING.md. + +#### 25.16.6 「炼金师分支干员的炼金单元等范围持续性技能特效太亮(尤其是多层叠加以后)」 (item 14): stacked areas stay readable — `render/fx/zones.js zone / zoneAlpha`, `render/fx/simfx.js`, `kits/ops/chess_char_5_15-thorn2.js` + +- **Cause**: 引星棘刺 S2's drifting unit was re-sent every second and drawn as a new additive zone for the rest of its life — + up to twelve layers on one spot; overlapping units (锡人's charges, several alchemists) added up the same way. +- **Now**: the fx carries `key` and the client moves / resizes that one zone; a persistent area (> 3 real s) is drawn at + 0.16 / 0.5 (disc / edge) and ÷ √n among the n persistent areas overlapping it; short bursts and telegraphs unchanged. +- Golden: unchanged. Tests: `test/render/feedback5-zone-stack.test.js` (node; no browser suite draws zones). + +#### 25.16.7 Answered with the evidence: items 11, 13, 31, 32 — no change + +- **11 「链愈师…只能从周身8格里的干员开始,最远不会跳出攻击范围,…即使后续跳跃干员满血也会按照规则顺序跳跃」**: the x-4 jump + around the last one healed and full-HP allies jumped in order are 0.2.0's WE1 (§25.7.6; on v0.1.4 a full-HP ally was + never jumped). The main target is the lowest-HP ally of the whole attack range y-2 (PRTS 分支特性信息), and the client's + chain selector (`projectile_chr_papyrs`: `_rangeId` x-4 around the hit, `_maxTarget` 3) has no attack-range bound, so a + jump may leave the range — not implemented. +- **13 「有的持续造成伤害类的技能无伤害(如炼金师分支干员的炼金单元)」**: not reproduced — every DoT skill of the pool and of + the 自选 operators deals damage once its condition is met (a sweep with two layouts; on two real stages 锡人 S1 / S2 and + 引星棘刺 S2 / S3 deal 13 000–134 000, the same on v0.1.4). The only zero is the hidden 锡人 record chess_char_1_16 (never + in the shop) whose kit lacks S1. The stacked, over-bright zones (item 14) are the likely cause of the impression. +- **31 「伺夜的召唤物随时间增长的狼影最高能叠三只(即在新的狼影恢复前能受到三次致命伤害才死亡)」**: what the sim does (kit pack + and placed piece, this tree and v0.1.4): 2 狼影 at deployment, 3 after 25 s, lethal hits 3 → 2 → 1 at full HP, the third + into the 战术点形态 — PRTS 狼群 备注. Pinned by `test/content/tokens_devices.test.js`, `kits_alt_t3.test.js`. +- **32 「吟游者的特性能恢复战术家的召唤物、不屈者分支干员的血量…(但赫默的无人机、浊心斯卡蒂的海嗣这种的血量不可以恢复)」**: the + trait is 生命回复速度, which 禁疗 and 无法被友方治疗 do not stop (§25.6.8): 狼群 440 → 532 and 斥罪 1480 → 1618 HP in 3 s + under 魔王 (pinned by `test/content/feedback5-regen.test.js`); 孤立 units stay out (PRTS 孤立). The countdown summons + cannot be refilled since §25.16.5. diff --git a/public/js/render/fx/simfx.js b/public/js/render/fx/simfx.js index 4eaa406d..1c474b5e 100644 --- a/public/js/render/fx/simfx.js +++ b/public/js/render/fx/simfx.js @@ -84,7 +84,8 @@ export class FxSim { this.mortar(this._viewOf(ex.id ?? ex.src), at.x, at.y, r, flight); break; } - case 'zone': this.zone(at.x, at.y, at.z, r, col, Math.max(0.6, dur || 1.5), spec.tex); break; + // `key`: one zone the sim re-sends as it moves / grows (an update in place, never a second layer — zones.js) + case 'zone': this.zone(at.x, at.y, at.z, r, col, Math.max(0.6, dur || 1.5), spec.tex, false, ex.key != null ? String(ex.key) : null); break; case 'wall': { // a line of burning tiles through the anchor tile along `axis` ('col' | 'row', from the sim event) const rect = this.ctx.fieldRect ? this.ctx.fieldRect() : null; diff --git a/public/js/render/fx/zones.js b/public/js/render/fx/zones.js index bb9fc917..3dcad273 100644 --- a/public/js/render/fx/zones.js +++ b/public/js/render/fx/zones.js @@ -3,6 +3,29 @@ import { NO_OPTS, clamp, easeOut } from './limits.js'; +/** A zone lasting longer than this (real s) is a persistent area (炼金单元, fields): drawn dimmer than a skill's short burst. */ +export const ZONE_PERSIST = 3; +/** Disc / edge alpha: a short zone, a persistent one (before the overlap damping), a telegraph. */ +export const ZONE_ALPHA = Object.freeze({ short: [0.28, 0.75], persist: [0.16, 0.5], warn: [0.35, 0.75] }); + +/** + * [disc, edge] alpha of zone `zn` among `zones` (additive blending). A persistent area is dimmer and shares its light with + * the persistent areas overlapping it — ÷ √n for n of them, centres closer than ¾ of their radii summed —, so stacked + * alchemy units read as one brighter area, not a white blot (community report of 2026-10-06 「炼金单元等范围持续性技能特效太亮 + * (尤其是多层叠加以后)」). Short bursts and telegraphs keep their full strength. + */ +export function zoneAlpha(zn, zones) { + if (zn.warn) return ZONE_ALPHA.warn; + if (!(zn.dur > ZONE_PERSIST)) return ZONE_ALPHA.short; + let n = 1; + for (const o of zones) { + if (o === zn || o.warn || !(o.dur > ZONE_PERSIST)) continue; + if (Math.hypot(o.x - zn.x, o.y - zn.y) < 0.75 * (o.r + zn.r)) n++; + } + const k = 1 / Math.sqrt(n); + return [ZONE_ALPHA.persist[0] * k, ZONE_ALPHA.persist[1] * k]; +} + export class FxZones { /** * An explosion on the ground at (x, y, z) of radius r tiles: fireball in `col`, a white flash, a shockwave and a @@ -65,15 +88,28 @@ export class FxZones { this.burst(g.x, g.y - g.s * 0.2, g.s, 6, tint, { speed: 2.4, life: 0.35 }); } - /** Persistent ground area: soft disc + pulsing edge ring for `dur` real seconds (telegraphs pulse faster). */ - zone(x, y, z, r, tint, dur, tex = 'soft', warn = false) { + /** + * Persistent ground area: soft disc + pulsing edge ring for `dur` real seconds (telegraphs pulse faster). `key` (the + * sim fx's `key`): a zone already up under that key is updated in place — moved, resized, its end `dur` from now — + * instead of a second one stacked on it (引星棘刺 S2's drifting alchemy unit is re-sent every second: twelve layers of the + * same zone used to pile up, community report of 2026-10-06 「炼金单元等范围持续性技能特效太亮(尤其是多层叠加以后)」). + */ + zone(x, y, z, r, tint, dur, tex = 'soft', warn = false, key = null) { + if (key != null) { + const zn = this.zones.find((q) => q.key === key && q.t < q.dur); + if (zn) { + zn.x = x; zn.y = y; zn.z = z; zn.r = r; zn.tint = tint; zn.dur = zn.t + dur; + this._onGround(zn.disc, y, z); this._onGround(zn.edge, y, z); + return; + } + } const P = this.P; const disc = new P.Sprite(this.tex[tex === 'ring' ? 'soft' : tex] || this.tex.soft); disc.anchor.set(0.5); disc.blendMode = P.BLEND_MODES.ADD; disc.tint = tint; const edge = new P.Sprite(this.tex.ring); edge.anchor.set(0.5); edge.blendMode = P.BLEND_MODES.ADD; edge.tint = tint; this._onGround(disc, y, z); this._onGround(edge, y, z); - this.zones.push({ disc, edge, x, y, z, r, t: 0, dur, warn }); + this.zones.push({ disc, edge, x, y, z, r, t: 0, dur, warn, key, tint }); if (this.zones.length > 24) this._freeZone(this.zones.shift()); } @@ -86,6 +122,10 @@ export class FxZones { for (const zn of this.zones) { zn.t += dt; if (zn.t >= zn.dur) { this._freeZone(zn); continue; } + this.zones[w++] = zn; + } + this.zones.length = w; + for (const zn of this.zones) { const k = zn.t / zn.dur; const grow = Math.min(1, zn.t / 0.25); const rad = zn.r * (0.35 + 0.65 * easeOut(grow)); @@ -94,11 +134,11 @@ export class FxZones { const rx = p.s * rad, ry = Math.max(1, p.y - q.y); const fade = k > 0.8 ? (1 - k) / 0.2 : 1; const pulse = zn.warn ? 0.55 + 0.45 * Math.abs(Math.sin(zn.t * 7)) : 0.8 + 0.2 * Math.sin(zn.t * 3); - zn.disc.position.set(p.x, p.y); zn.disc.scale.set((rx * 2) / 128, (ry * 2) / 128); zn.disc.alpha = (zn.warn ? 0.35 : 0.28) * fade * pulse; - zn.edge.position.set(p.x, p.y); zn.edge.scale.set((rx * 2.1) / 128, (ry * 2.1) / 128); zn.edge.alpha = 0.75 * fade * pulse; - this.zones[w++] = zn; + const [discA, edgeA] = zoneAlpha(zn, this.zones); + zn.disc.tint = zn.edge.tint = zn.tint ?? zn.disc.tint; + zn.disc.position.set(p.x, p.y); zn.disc.scale.set((rx * 2) / 128, (ry * 2) / 128); zn.disc.alpha = discA * fade * pulse; + zn.edge.position.set(p.x, p.y); zn.edge.scale.set((rx * 2.1) / 128, (ry * 2.1) / 128); zn.edge.alpha = edgeA * fade * pulse; } - this.zones.length = w; } /** Flash a set of tiles ([[r,c]]) on the ground (telegraphed boxes, blast tiles). */ diff --git a/server/sim/battle/deploy.js b/server/sim/battle/deploy.js index d5c23965..06fc44cf 100644 --- a/server/sim/battle/deploy.js +++ b/server/sim/battle/deploy.js @@ -32,6 +32,7 @@ export class BattleDeploy { u.removed = false; u.hidden = false; u.body = null; + u.countdown = null; // a countdown summon's content starts its new life in the deploy hook (content/tokens.js startCountdown) u.x = C0; u.y = R0; u.tileR = R0; u.tileC = C0; u.blocking = []; u.deploySeq = ++this._deploySeq; diff --git a/server/sim/content/kits/ops/chess_char_1_06-vendla.js b/server/sim/content/kits/ops/chess_char_1_06-vendla.js index c4ac4513..7dae0997 100644 --- a/server/sim/content/kits/ops/chess_char_1_06-vendla.js +++ b/server/sim/content/kits/ops/chess_char_1_06-vendla.js @@ -6,7 +6,13 @@ import { num, talentBb, up, byEnemyAttack, alliesInGridOf, skillBbOf } from '../ export default { // --------------------------------------------------------------------------------------------------------------- // 1_06 刺玫 荆藤庇荫: ATK +atk; the ally with the highest max HP in range gets taunt +taunt_level; whenever that ally is - // attacked, 刺玫 deals atk_scale × ATK arts to the attacker, and her trait heal (scale × damage) only goes to that ally. + // attacked, 刺玫 deals atk_scale × ATK arts to the attacker, and the trait heal of THAT damage (scale × damage) goes to + // that ally ("并仅对该角色触发刺玫特性"; en "…and activates her Trait on the ally"). Her own attacks keep healing the + // lowest-HP ally in range — herself included — while the skill runs: client buff vendla_tr heals through the + // S2TraitHealRange selector (the vendla_s_2 holder) only for a damage whose modifier carries the key vendla_s_2 (the + // counter, vendla_s_2[extra_damage]), else through TraitHealRange (_excludeOwner 0, 禁疗 excluded). Until 0.2.0 every + // damage she dealt during the skill healed the protégé, usually at full HP, so she healed nobody (community report of + // 2026-10-06 「刺玫开技能(装备源石药剂)之后不能治疗」 — her 源石溶剂 drain went unhealed). // 土壤基肥改良: the highest-max-HP ally in range receives heal_scale × healing ("受到的治疗效果提升") — never its natural / // skill HP regeneration (the 生命回复速度 attribute is "不受治疗加成影响": 宴 分神, 角峰 体能强化; PRTS). // Alternate S1 战术咏唱·γ型: ASPD +attack_speed (the trait heal keeps going to the most injured ally in range). @@ -20,14 +26,14 @@ export default { skills: { 'skcom_magic_rage[3]': { kind: 'duration', mods: { aspd: num(skillBbOf(chess, 'skcom_magic_rage[3]').attack_speed) } } }, trait: { // 咒愈师 trait: EVERY damage she deals heals an ally for 50 % of it (professions.js `installIncantation`, - // buff_template_data `vendla_tr` = ON_AFTER_OUTPUT_DAMAGE) — while 荆藤庇荫 runs her S2 says "仅对该角色触发刺玫 - // 特性", so her protégé is the target then; otherwise it is the lowest-HP ally in range. + // buff_template_data `vendla_tr` = ON_AFTER_OUTPUT_DAMAGE): the lowest-HP ally in range (herself included) — the + // S2 counter's damage names her protégé instead (`traitAlly`, talent install below), skill running or not install(battle, u) { battle.on('damaged', (c) => { const t = c.target; if (c.source !== u || !u.alive || !t || t.side !== 'enemy' || !(c.amount > 0)) return; if (c.type === 'element' || c.type === 'elemental') return; - const ally = (c.dmg && c.dmg.traitAlly) || protege(u) || battle.lowestHpAllyInRange(u); + const ally = (c.dmg && c.dmg.traitAlly) || battle.lowestHpAllyInRange(u); if (ally) battle.heal(u, ally, c.amount * (u.profile.healRatio ?? 0.5), { tags: ['incantation'] }); }, { owner: u }); }, diff --git a/server/sim/content/kits/ops/chess_char_2_02-silent.js b/server/sim/content/kits/ops/chess_char_2_02-silent.js index 16396ef2..bfa215d3 100644 --- a/server/sim/content/kits/ops/chess_char_2_02-silent.js +++ b/server/sim/content/kits/ops/chess_char_2_02-silent.js @@ -12,6 +12,11 @@ export default { // battle start — user playtest #4; not placed ⇒ no drone); it heals around itself and self-destructs after 10 s (token // kit). 强化注射: every 【医疗】 operator on the field ASPD +attack_speed. Elite module (PHY-Y): heals on ground units // ×heal_scale (module 'none': no bonus). + // The cast: as soon as the SP is full — the AUTO skill has no cast condition (client skill skchr_silent_2: no + // `_trigger`, `_allowNoTarget` 1, `_checkHasTargetBeforeDoCast` 0, a RechargeToken action), so the placed drone takes the + // field the moment S2 is ready (community report of 2026-10-06 「赫默的无人机在2技能好了之后应该秒放」; until 0.2.0 the data's + // DEFAULT rule with `heal` waited for an injured ally in her range). While the stock is full her SP stops (阻回) until + // one is spent: `_stopSpWhenTokenIsFull` 1, PRTS 备注 「医疗探机库存达到上限后赫默获得阻回,直到库存被消耗为止」. // S1 治疗强化·γ型 (alt): ATK +atk for its duration (no drone). chess_char_2_02_a: (bb, chess) => { const tokId = chess?.skill?.overrideTokenKey ?? (chess?.tokens ?? [])[0] ?? 'token_10000_silent_healrb'; @@ -21,8 +26,17 @@ export default { return { skill: { kind: 'instant', heal: true, + trigger: { rule: 'SP_FULL' }, onStart({ battle, unit }) { releaseSkillSummon(battle, unit, tokId, { cap: cnt }); }, }, + install(battle, unit) { + if (unit.def?.skill?.id !== 'skchr_silent_2') return; + // 阻回 while the stock is full (refreshed every tick; gone a tick after a drone takes the field) + battle.on('tick', () => { + if (!unit.alive || !unit.deployed || !((unit.mem.summonStock?.[tokId] ?? 0) >= cnt)) return; + battle.addBuff(unit, { key: 'silent:stockFull', duration: 2 * battle.dt, refresh: 'extend', flags: { noSp: true } }); + }, { owner: unit }); + }, skills: { 'skcom_heal_up[3]': { kind: 'duration', heal: true, mods: { atkPct: num(bb.atk) } } }, talents: [{ install(battle, unit) { const v = num(t.attack_speed); diff --git a/server/sim/content/kits/ops/chess_char_3_04-swire2.js b/server/sim/content/kits/ops/chess_char_3_04-swire2.js index 784f79f4..5004c3b5 100644 --- a/server/sim/content/kits/ops/chess_char_3_04-swire2.js +++ b/server/sim/content/kits/ops/chess_char_3_04-swire2.js @@ -40,8 +40,8 @@ export default { // ---- 3_04 琳琅诗怀雅 · 行商 — S2 “见面礼” (passive): with a coin and a free tile of range x-6 her attack's turn // throws a champagne bomb instead (onto an enemy's tile when one stands there; installS2); // 大买家: coin at skill start + coin & ATK stack per trait payment; 破财消灾: DP-paid revive (cost doubles) - // S1 仗义疏财 (passive, 2 coins): a coin heals the most injured ally (< 70 % HP) of the 8 surrounding tiles for - // attack@heal_scale × ATK — on her attack, or with no enemy to attack on her own attack timer (owner's decision + // S1 仗义疏财 (passive, 2 coins): a coin heals the most injured ally (< 70 % HP) of the 3×3 around her — herself + // included — for attack@heal_scale × ATK — on her attack, or with no enemy to attack on her own attack timer (owner's decision // 2026-10-04, against the official 「下一次攻击会为…」; installS1). S3 千金一掷 (持续时间无限): attacks hit twice, kills give a coin; // closing it spends every coin on random ground enemies of range 2-4 in front and those she blocks (atk_scale phys + // a small push, radial despite the text's 向前 — PRTS 备注 "推开效果为径向推动"; client charpack char_1033_swire2: @@ -96,10 +96,13 @@ export default { * 仗义疏财 — official text 「消耗一枚金币,下一次攻击会为周围八格内血量不足70%的一名友方单位恢复相当于攻击力40%的生命」: the heal rides on an * attack, so with no enemy around she never healed (community report #5 「琳琅诗怀雅1技能不会主动奶身边受伤的干员」). Owner's * decision 2026-10-04 (a deliberate deviation, like §21.29's 重装 casts): she heals an injured ally beside her whether - * she attacks or not. Same target (the lowest HP ratio below 70 % of the 8 surrounding tiles, no 禁疗 / 孤立 unit, no + * she attacks or not. Same target (the lowest HP ratio below 70 % of the 3×3 around her, no 禁疗 / 孤立 unit, no * device), coin and heal_scale × ATK. Cadence [ASSUMED]: at most one heal per attack cycle (her attack interval, ASPD * included) — while she attacks, on the attack as before; when her last attack attempt found no target, on her own * timer (the 'tick' hook runs after the attacks, so an attack due in the same tick takes it). + * 「周围八格」 includes herself: the client charpack (char_1033_swire2, mode S1, ability HealAlly) selects with range + * x-4 — the 3×3 with her own tile — ally side, `_excludeOwner` 0, max HP ratio 0.7, one target. Until 0.2.0 she was + * left out and never healed herself (community report of 2026-10-06 「琳琅诗怀雅不会治疗自己」). */ const installS1 = (battle, unit) => { const hs = num(bb['attack@heal_scale'], num(bb.heal_scale, 0)); @@ -107,9 +110,10 @@ export default { const healTarget = () => { let best = null; for (const a of battle.allyUnits) { - if (a === unit || !alive(a) || a.hidden || a.kind === 'device' || a.hpRatio >= healRatio) continue; - if (a.s.flags.noHeal || a.profile?.noHeal) continue; // 禁疗 / 孤立: never a heal target - if (Math.max(Math.abs(a.tileR - unit.tileR), Math.abs(a.tileC - unit.tileC)) !== 1) continue; // 周围八格 + if (!alive(a) || a.hidden || a.kind === 'device' || a.hpRatio >= healRatio) continue; + if (a !== unit && (a.s.flags.noHeal || a.profile?.noHeal)) continue; // 禁疗 / 孤立: never a heal target + if (a === unit && a.s.flags.healFree) continue; // her own 禁疗 (HEAL_FREE) stops a heal of herself too + if (Math.max(Math.abs(a.tileR - unit.tileR), Math.abs(a.tileC - unit.tileC)) > 1) continue; // x-4: the 3×3, her tile too if (!best || a.hpRatio < best.hpRatio || (a.hpRatio === best.hpRatio && a.deploySeq < best.deploySeq)) best = a; } return best; @@ -255,6 +259,11 @@ export default { battle.on('skillStart', (ctx) => { if (ctx.unit === unit && unit.skill?.kind !== 'passive') addCoins(battle, unit, num(t0.sp, 1)); }, { owner: unit }); } }, { install(battle, unit) { // 破财消灾 + // the doubling cost restarts at 5 with every deployment: PRTS 备注 「再部署时重置本天赋费用消耗」 — the client buff + // swire2_t_2 keeps the doubled `cost` in its own blackboard, built anew when she is deployed (a battle start, 联防 + // included — a new battle —, a redeploy, a 突袭 再部署). Until 0.2.0 a redeploy kept the doubled cost (community + // report of 2026-10-06 「正常对局中死亡之后再部署复活费用也应该重置」) + battle.on('deploy', (ctx) => { if (ctx.unit === unit) unit.mem.saveCount = 0; }, { owner: unit }); battle.on('fatal', (ctx) => { if (ctx.unit !== unit || ctx.prevented) return; const n = unit.mem.saveCount ?? 0; diff --git a/server/sim/content/kits/ops/chess_char_5_15-thorn2.js b/server/sim/content/kits/ops/chess_char_5_15-thorn2.js index 59cd281b..b0312bcd 100644 --- a/server/sim/content/kits/ops/chess_char_5_15-thorn2.js +++ b/server/sim/content/kits/ops/chess_char_5_15-thorn2.js @@ -135,9 +135,12 @@ export default { const extra = battle.alliesInGrid(unit).some((a) => a !== unit && isOp(a)) ? Math.min(num(t0.projectile_extend), num(t0.projectile_extend_max, Infinity)) : 0; // it drifts away from her deployment tile ("移动方向始终为远离棘刺部署位置中心的方向") const dx = t.x - unit.x, dy = t.y - unit.y, len = Math.hypot(dx, dy) || 1; - const z = { x: t.x, y: t.y, vx: (dx / len) * speed, vy: (dy / len) * speed, t: 0, acc: 0, dur: baseDur + extra }; + // `key`: the fx re-sent every second as the unit drifts and grows is this one zone — the client updates it in + // place instead of stacking a new layer each second (render/fx/zones.js; community report of 2026-10-06) + const key = `thorn2:${unit.id}:${unit.mem.zoneSeq = (unit.mem.zoneSeq ?? 0) + 1}`; + const z = { x: t.x, y: t.y, vx: (dx / len) * speed, vy: (dy / len) * speed, t: 0, acc: 0, dur: baseDur + extra, key }; (unit.mem.zones ??= []).push(z); - battle.fx('zone', { x: z.x, y: z.y, id: unit.id, r: r0, duration: z.dur }); + battle.fx('zone', { x: z.x, y: z.y, id: unit.id, r: r0, duration: z.dur, key }); }, }, talents: [ @@ -184,7 +187,7 @@ export default { for (const e of foes) battle.dealDamage(unit, e, { amount: unit.s.atk * num(bb.atk_scale), type: 'arts', isSkill: true, tags: ['skill', 'alchemy'] }); const heal = unit.s.atk * num(bb.hp_recovery_per_sec_ratio_chr); if (heal > 0) for (const a of battle.alliesInRadius(z.x, z.y, r, null)) if (a.hp < a.s.maxHp) battle.heal(unit, a, heal, { aura: true }); - battle.fx('zone', { x: z.x, y: z.y, id: unit.id, r, duration: Math.max(0, z.dur - z.t) }); + battle.fx('zone', { x: z.x, y: z.y, id: unit.id, r, duration: Math.max(0, z.dur - z.t), key: z.key }); } } for (const [e, steps] of seaHits) { // S3 debuff: 不叠加 — the strongest zone wins diff --git a/server/sim/content/kits/ops/chess_char_6_04-skadi2.js b/server/sim/content/kits/ops/chess_char_6_04-skadi2.js index 2f537b03..4c249cf5 100644 --- a/server/sim/content/kits/ops/chess_char_6_04-skadi2.js +++ b/server/sim/content/kits/ops/chess_char_6_04-skadi2.js @@ -4,6 +4,7 @@ import { aggregateMods } from '../../../buffs.js'; import { COLS } from '../../../constants.js'; import { bardRegen } from '../../../professions.js'; +import { startCountdown } from '../../tokens.js'; import { num, bv, tbb, tdesc, moduleBb, parseN, live, enemiesIn, isTok, onDefaultSkill, selectedSkill, aura, } from '../shared/tier6.js'; @@ -148,6 +149,7 @@ function skadi2(bb, chess, def) { if (t.profile) t.profile.noAttack = true; const dur = num(t.def?.talents?.[0]?.bb?.duration, parseN(tdesc(def, 0), /持续(\d+(?:\.\d+)?)秒/, 25)); const seq = t.deploySeq, r = t.tileR, c = t.tileC; + startCountdown(battle, t, dur); // a countdown summon: 无敌, 禁疗, its bar = the life left (content/tokens.js) battle.after(dur, () => { if (!t.alive || t.deploySeq !== seq) return; battle.retreat(t, { reason: 'expired', permanent: true }); diff --git a/server/sim/content/kits/ops/op-bgsnow.js b/server/sim/content/kits/ops/op-bgsnow.js index b0336b0b..c20c1e91 100644 --- a/server/sim/content/kits/ops/op-bgsnow.js +++ b/server/sim/content/kits/ops/op-bgsnow.js @@ -44,6 +44,7 @@ import { bodyKeys, bodyInKeys } from '../../../body.js'; import { toLocal } from '../../../dir.js'; import { hasHp } from '../../../damage.js'; import { COLS } from '../../../constants.js'; +import { startCountdown } from '../../tokens.js'; const S1 = 'skchr_bgsnow_1'; const S2 = 'skchr_bgsnow_2'; @@ -141,9 +142,11 @@ function typewriterKit(t, owner, chess) { } }], install(battle, unit) { if (sk?.id === TS1) installIamb(battle, unit, num(tb.prob), num(tb.atk_scale, 1), skillOn); - // 持续25秒: leaves `life` s after each deployment + // 持续25秒: leaves `life` s after each deployment — a countdown summon (content/tokens.js startCountdown): 无敌, + // 禁疗 [ASSUMED], its bar = the life left battle.on('deploy', (ctx) => { if (ctx.unit !== unit || !(life > 0)) return; + startCountdown(battle, unit, life); const seq = unit.deploySeq; battle.after(life, () => { if (unit.alive && unit.deploySeq === seq) battle.retreat(unit, { reason: 'expired', permanent: true }); }, { owner: unit }); }, { owner: unit }); diff --git a/server/sim/content/kits/ops/op-phatm2.js b/server/sim/content/kits/ops/op-phatm2.js index 9635920d..7c489ab8 100644 --- a/server/sim/content/kits/ops/op-phatm2.js +++ b/server/sim/content/kits/ops/op-phatm2.js @@ -63,7 +63,7 @@ import { num, up, traitBb, talentBb, skillRec, once } from '../shared/tier1.js'; import { isElite, onElementHit, elementDmg } from '../shared/tier6.js'; -import { releaseSkillSummon } from '../../tokens.js'; +import { releaseSkillSummon, startCountdown } from '../../tokens.js'; import { absoluteRangeKeys } from '../../../targeting.js'; import { bodyInKeys } from '../../../body.js'; import { hasHp, burstLocked } from '../../../damage.js'; @@ -124,6 +124,7 @@ function callKit(t, owner, b2) { lured = []; endAt = battle.time + life; atk = owner.s.atk; // 缓存攻击力: her ATK at the deployment (PRTS 备注) + startCountdown(battle, tok, life); // a countdown summon (content/tokens.js): 无敌, 禁疗 [ASSUMED], its bar = the life left const seq = tok.deploySeq; battle.after(life, () => { if (tok.alive && tok.deploySeq === seq) battle.retreat(tok, { reason: 'expired', permanent: true }); }, { owner: tok }); }, { owner: tok }); diff --git a/server/sim/content/kits/ops/op-ray.js b/server/sim/content/kits/ops/op-ray.js index 7c709665..2cf11db2 100644 --- a/server/sim/content/kits/ops/op-ray.js +++ b/server/sim/content/kits/ops/op-ray.js @@ -62,6 +62,7 @@ import { acquireTargets, effectiveProfile } from '../../../ai.js'; import { sortEnemyTargets } from '../../../targeting.js'; import { bodyInKeys } from '../../../body.js'; import { PUSH_DIRECTIONAL_MIN_DIST } from '../../../constants.js'; +import { startCountdown } from '../../tokens.js'; const S1 = 'skchr_ray_1'; const S2 = 'skchr_ray_2'; @@ -225,6 +226,7 @@ export default { m.rayBullets = 0; m.rayCounted = 0; battle.setExtraRange(unit, m.rayAreaKeys); + startCountdown(battle, t, life); // a countdown summon (content/tokens.js): its bar = the life left const seq = t.deploySeq; battle.after(life, () => { if (t.alive && t.deploySeq === seq) battle.retreat(t, { reason: 'expired', permanent: true }); }, { owner: unit }); }, { owner: unit }); diff --git a/server/sim/content/kits/ops/op-weedy.js b/server/sim/content/kits/ops/op-weedy.js index 8d61cb70..961a14be 100644 --- a/server/sim/content/kits/ops/op-weedy.js +++ b/server/sim/content/kits/ops/op-weedy.js @@ -48,6 +48,7 @@ import { num, traitBb, skillRec, up, giveSp } from '../shared/tier1.js'; import { absoluteRangeKeys } from '../../../targeting.js'; import { COLS } from '../../../constants.js'; +import { startCountdown } from '../../tokens.js'; const S1 = 'skchr_weedy_1'; const S2 = 'skchr_weedy_2'; @@ -174,9 +175,10 @@ function cannonKit(t, owner) { }, install(battle, unit) { battle.addBuff(unit, { key: 'trait:weedyCannon', flags: { noHeal: true }, persist: true, allowDead: true }); // 持有禁疗 - // 部署后20秒自动撤退 + // 部署后20秒自动撤退 — a countdown summon (content/tokens.js startCountdown): 无敌 [ASSUMED], 禁疗, its bar = the life left battle.on('deploy', (ctx) => { if (ctx.unit !== unit || !(life > 0)) return; + startCountdown(battle, unit, life); const seq = unit.deploySeq; battle.after(life, () => { if (unit.alive && unit.deploySeq === seq) battle.retreat(unit, { reason: 'expired', permanent: true }); }, { owner: unit }); }, { owner: unit }); diff --git a/server/sim/content/tokens.js b/server/sim/content/tokens.js index d9234261..ddb23029 100644 --- a/server/sim/content/tokens.js +++ b/server/sim/content/tokens.js @@ -5,8 +5,10 @@ // stats / talents / trait / skill at the owner's phase — a golden owner gets the `_b` variant). // Every tokenId of data/tokens.json has a kit, so `battle.spawnToken(owner, tokenId, r, c)` works with data defaults // even when the summoner's kit passes no options: -// 医疗探机 heal profile (data), untargetable, self-destructs after its withdraw skill time (10 s) -// 诅咒娃娃 no attack, aura: enemies in its range ATK/DEF + bb.atk/bb.def (−25 %/−30 %), 15 s (skill duration); +// 医疗探机 heal profile (data), untargetable, self-destructs after its withdraw skill time (10 s); a countdown +// summon (COUNTDOWN_SUMMONS: 无敌, 禁疗, its bar = the life left) +// 诅咒娃娃 no attack, aura: enemies in its range ATK/DEF + bb.atk/bb.def (−25 %/−30 %), 15 s (skill duration; a +// countdown summon); // leaves when 巫恋 leaves (PRTS 备注) — the drone stays when 赫默 leaves (PRTS 医疗探机 备注) // 沙之碑 on appear: owner ATK × atk_scale arts + stun (skill range 3×3), blocks 3 (no attack), talent duration 20 s // 战术装备 on appear: stun around (bb.stun), blocked enemies DEF + talent def (−160), talent duration 25 s @@ -14,7 +16,7 @@ // 斯卡蒂的海嗣 untargetable range extension of 浊心斯卡蒂: her trait's 生命回复速度 on the allies in its range (owner // ATK × trait ratio /s); // while the owner's skill runs: owner ATK × atk_scale true dmg/s to enemies + 鼓舞 owner ATK × bb.atk; -// talent duration 25/30 s, then redeploys after respawnTime (30/25 s, DP cost from data) +// talent duration 25/30 s (a countdown summon), then redeploys after respawnTime (30/25 s, DP cost from data) // “耀阳” on appear: owner ATK × atk_scale true + stun in the skill grid (+1 hit if the previously deployed // operator is 卡西米尔); golden trait ×atk_scale vs blocked enemies; lasts while the owner's skill runs // 纸偶 on appear: token ATK × damage_scale arts to the 8 surrounding tiles; does not block (data) @@ -76,6 +78,7 @@ // makes the token (Battle.spawnToken also refuses summons the owner's loadout does not produce). // // Exports for other content: spawnYanyou, spawnMapChar, findSummonTile, summonToken, tacticalPoint, wolfShadows, +// COUNTDOWN_SUMMONS / startCountdown (the countdown summons' 无敌 + 禁疗 and timer bar, shared with the kits that time them), // wolfShadowInterval, installWolfTacticalPoint, wolfTacticalPoint, wolfReturnNow (the 狼群's 战术点形态, shared with the // 伺夜 kit's own pack, kits/ops/chess_char_3_19-vigil.js), releaseSkillSummon, SKILL_SUMMON_START_DEPLOY, CAT_SHIELD_KEY, // TOKEN_IDS; mapCharTalents and touchGospel (Touch's 攫升 / 超脱 and 恳切福音, shared with the Touch 补位 stand-in kit, @@ -192,10 +195,42 @@ function untargetable(battle, unit) { battle.addBuff(unit, { key: 'trait:untargetable', flags: { untargetable: true }, persist: true, allowDead: true }); } -/** Withdraw the token `seconds` after this deployment (the same deployment only). */ +/** + * Countdown summons — the summons that "不会受到攻击" and leave after a fixed time: 赫默's 医疗探机 (10 s, skcom_withdraw), + * 巫恋's 诅咒娃娃 (15 s, its skill), 浊心斯卡蒂's 斯卡蒂的海嗣 (talent 远古血亲 25 / 30 s) and, among the 自选 operators, 温蒂's + * 工程蓄水炮 (20 s), 莱伊's 沙地兽 (25 s), 鸿雪's “打字机” (25 s) and 酒神's 本能的召唤 (10 s). Community report of 2026-10-06 + * (the official mode): they take no outside damage — 活性源石 included —, no operator heals them and their bar runs down + * like a timer, the summon leaving when it is empty. PRTS / client data: 海嗣 and 沙地兽 hold 无敌 + 禁疗 (PRTS 海嗣 备注 + * 「持有禁疗、无敌」; token prefabs: abnormal flags 5 + 7), 工程蓄水炮 禁疗 (PRTS 备注); the others have no abnormal flag in the + * base-game prefabs — [ASSUMED] the report's 无敌 + 禁疗 for every one of them. startCountdown gives them both (their HP never + * moves) and the bar (Unit.countdown, shown by snapshot.js as the share of the life left). The other timed summons (沙之碑, + * 战术装备, “小自在”, 结构性原理 …) block and are attacked: their HP stays HP. Until 0.2.0 a 海嗣 on 活性源石 lost 70 HP/s (of + * 100) and a hurt drone drew every medic's heals. + */ +export const COUNTDOWN_SUMMONS = Object.freeze(new Set([ + TOKEN_IDS.healDrone, TOKEN_IDS.curseDoll, TOKEN_IDS.seaborn, + 'token_10009_weedy_cannon', 'token_10034_ray_sndbst', 'token_10026_bgsnow_subbow', 'token_10054_phatm2_encdool', +])); +const COUNTDOWN_KEY = 'token:countdown'; + +/** + * A countdown summon's life on the field starts (COUNTDOWN_SUMMONS; call it from the `deploy` hook of each deployment, + * where its kit schedules its withdrawal): `seconds` from now its bar is empty. It holds 无敌 and 禁疗 from then on. A + * summon that is not a countdown one is left alone (false). + */ +export function startCountdown(battle, unit, seconds) { + const s = num(seconds, 0); + if (!unit || !COUNTDOWN_SUMMONS.has(unit.defId) || !(s > 0)) return false; + unit.countdown = { from: battle.time, until: battle.time + s }; + if (!unit.findBuff(COUNTDOWN_KEY)) battle.addBuff(unit, { key: COUNTDOWN_KEY, flags: { invulnerable: true, noHeal: true, healFree: true }, persist: true, allowDead: true }); + return true; +} + +/** Withdraw the token `seconds` after this deployment (the same deployment only); a countdown summon's bar runs with it. */ function scheduleLifetime(battle, unit, seconds) { const s = num(seconds, 0); if (!(s > 0)) return; + startCountdown(battle, unit, s); const seq = unit.deploySeq; unit.mem.expiresAt = battle.time + s; battle.after(s, () => { if (unit.alive && unit.deploySeq === seq) battle.retreat(unit, { reason: 'expired', permanent: true }); }, { owner: unit }); diff --git a/server/sim/snapshot.js b/server/sim/snapshot.js index 9236d662..8c1794bb 100644 --- a/server/sim/snapshot.js +++ b/server/sim/snapshot.js @@ -1,6 +1,7 @@ // server/sim/snapshot.js — compact serialization for clients (DESIGN §8.2). // // b.snap = { fieldId, t, units: [[id, x, y, hp, maxHp, sp, spMax, flags, anim]], dp, killed, total } +// (hp of a countdown summon — unit.countdown, content/tokens.js startCountdown — is maxHp × the share of its life left) // UnitInfo = { id, kind, side, ownerId, defId, name, tier, golden, spine, avatar, x, y, facing, dir, maxHp, motion?, boss?, uid?, // form?, skillIndex?, moduleId?, items?, standInFor?, diy? } (standInFor = the replaced operator's charId of a 补位 // stand-in; diy = a 自选 piece's pick { charId, skillIndex, uniEquipId } — defId is its slot, spine / avatar the operator's; @@ -107,9 +108,13 @@ export function unitTuple(u, t) { sp = spMax * (sk.timeLeft / sk.duration); } } - // hp is rounded up (a living unit never shows 0) but never above the rounded max HP + // hp is rounded up (a living unit never shows 0) but never above the rounded max HP. A countdown summon (医疗探机, 海嗣 …: + // content/tokens.js startCountdown) shows the share of its life left instead — its bar runs down like a timer and it + // leaves when it is empty (community report of 2026-10-06; its HP itself never moves: 无敌, 禁疗) const maxHp = Math.max(1, Math.round(u.s.maxHp)); - const hp = u.alive ? Math.min(Math.max(1, Math.ceil(u.hp)), maxHp) : 0; + const cd = u.alive ? u.countdown : null; + const left = cd ? Math.max(0, Math.min(1, (cd.until - t) / Math.max(1e-9, cd.until - cd.from))) : 1; + const hp = u.alive ? Math.min(Math.max(1, Math.ceil(cd ? maxHp * left : u.hp)), maxHp) : 0; return [u.id, r2(u.x), r2(u.y), hp, maxHp, r1(sp), spMax, flagsOf(u), animOf(u, t)]; } diff --git a/server/sim/units.js b/server/sim/units.js index 1a1258ab..d2431fae 100644 --- a/server/sim/units.js +++ b/server/sim/units.js @@ -81,6 +81,9 @@ export class Unit { this.burstPending = null; // { [element]: true } while that element's burst resolves (damage.js burstLocked) this.tags = new Set(init.tags || []); this.mem = {}; // free scratch space for content (per unit) + // a countdown summon's life on the field ({ from, until } battle times; content/tokens.js startCountdown, cleared by + // every deployment): its bar shows the time left (snapshot.js unitTuple), its HP never moves (无敌 + 禁疗) + this.countdown = null; this.trait = {}; // profession runtime state this.stats = { dmg: 0, kills: 0, heal: 0, taken: 0, attacks: 0 }; this.hidden = false; // enemies inside a DISAPPEAR segment diff --git a/test/content/feedback5-silent-drone.test.js b/test/content/feedback5-silent-drone.test.js new file mode 100644 index 00000000..da95b601 --- /dev/null +++ b/test/content/feedback5-silent-drone.test.js @@ -0,0 +1,52 @@ +// test/content/feedback5-silent-drone.test.js — 赫默 S2 医疗无人机 casts as soon as its SP is full (community report of +// 2026-10-06 「赫默的无人机在2技能好了之后应该秒放」): the AUTO skill has no cast condition — client skill skchr_silent_2: no +// `_trigger`, `_allowNoTarget` 1, `_checkHasTargetBeforeDoCast` 0, a RechargeToken action —, so the placed 医疗探机 takes the +// field the moment S2 is ready, injured ally or not. While the stock is full her SP stops (阻回) until it is spent +// (`_stopSpWhenTokenIsFull` 1; PRTS 赫默 S2 备注). Until 0.2.0 the data's DEFAULT rule (+ `heal`) waited for an injured ally. +// Run: node --test test/content/feedback5-silent-drone.test.js + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { makeBattle, checkInvariants } from '../helpers/battleHarness.js'; + +const SILENT = 'chess_char_2_02_a', DRONE = 'token_10000_silent_healrb'; + +function field() { + const h = makeBattle({ + units: [{ chessId: SILENT, row: 10, col: 3 }, { kind: 'token', tokenId: DRONE, row: 11, col: 3, ownerUid: 1 }, { chessId: 'chess_char_1_02_a', row: 10, col: 4 }], + autoFinish: false, timeLimit: 120, seed: 3, + }); + const drones = [], casts = []; + h.b.on('deploy', (c) => { if (c.unit.defId === DRONE) drones.push(h.b.time); }); + h.b.on('skillStart', (c) => { if (c.unit.defId === SILENT) casts.push(h.b.time); }); + return { h, s: h.unit(SILENT), drones, casts }; +} + +test('S2 casts the tick its SP is full with nobody injured, and the drone takes the field at once', () => { + const { h, s, drones, casts } = field(); + h.run(0.1); + assert.deepEqual(drones.length, 1, 'the placed drone deploys with the board (SKILL_SUMMON_START_DEPLOY)'); + // (the cast comes in the very tick the SP fills: from 0 at the deployment, spCost / spRecovery seconds later) + const fullAt = s.skill.spCost / s.s.spRecovery; + h.run(40); + assert.ok(h.b.allyUnits.every((a) => !a.alive || a.hp >= a.s.maxHp), 'nobody was injured'); + assert.ok(casts.length >= 1, 'it casts with no injured ally anywhere'); + assert.ok(Math.abs(casts[0] - fullAt) < 0.1, `at full SP (${fullAt.toFixed(2)} s; cast ${casts[0].toFixed(2)} s)`); + assert.ok(drones.some((t) => Math.abs(t - casts[0]) < 0.1), 'the drone deploys at the cast'); + checkInvariants(h.b); + assert.equal(h.b.errors.length, 0); +}); + +test('while the stock is full (the previous drone still up) her SP stops; it runs again once the drone deployed', () => { + const { h, s, drones, casts } = field(); + h.run(5); + s.skill.sp = s.skill.spCost; + h.step(2); + assert.equal(casts.length, 1, 'cast at once'); + assert.equal(s.mem.summonStock[DRONE], 1, 'the drone is still up (10 s): one in stock'); + h.run(6); + assert.equal(s.skill.sp, 0, '阻回: no SP while the stock is full'); + assert.ok(h.runUntil(() => drones.length >= 2, 10), 'the stocked drone deploys once the first is gone and ready again'); + h.run(3); + assert.ok(s.skill.sp > 2, `SP runs again (${s.skill.sp.toFixed(2)})`); +}); diff --git a/test/content/feedback5-swire2-revive-cost.test.js b/test/content/feedback5-swire2-revive-cost.test.js new file mode 100644 index 00000000..62ddb79d --- /dev/null +++ b/test/content/feedback5-swire2-revive-cost.test.js @@ -0,0 +1,44 @@ +// test/content/feedback5-swire2-revive-cost.test.js — 琳琅诗怀雅's 破财消灾 (「受到致命伤害时,若费用足够则消耗5点部署费用使生命恢复到 +// 70%,每次触发该天赋时消耗的费用翻倍」): the doubled cost restarts at 5 with every deployment — PRTS 备注 「再部署时重置本天赋费用 +// 消耗」 (community report of 2026-10-06 「进入联防阶段琳琅诗怀雅所需的复活费用应该重置,正常对局中死亡之后再部署复活费用也应该 +// 重置」). A 联防 field is a battle of its own, so it always started at 5; a redeploy used to keep the doubled cost. +// Run: node --test test/content/feedback5-swire2-revive-cost.test.js + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { makeBattle, checkInvariants } from '../helpers/battleHarness.js'; + +const ID = 'chess_char_3_04_a'; + +function field(kind = 'normal') { + const h = makeBattle({ kind, units: [{ chessId: ID, row: 10, col: 5 }], timeLimit: 300, autoFinish: false, flags: { dpInit: 60, dpMax: 99 } }); + h.step(); + return { h, u: h.unit(ID), ps: h.b.getPlayer('p1') }; +} + +/** A lethal hit with `dp` DP in the pool: returns the DP the talent took (0 when she fell). */ +function lethal(h, u, ps, dp = 90) { + ps.dp = dp; + h.b.dealDamage(null, u, { amount: u.s.maxHp * 5, type: 'true', canDodge: false }); + return u.alive ? dp - ps.dp : 0; +} + +test('破财消灾 doubles within one deployment and restarts at 5 DP after a redeploy', () => { + const { h, u, ps } = field(); + assert.equal(lethal(h, u, ps), 5, 'first save: 5 DP'); + assert.ok(u.alive && Math.abs(u.hp - u.s.maxHp * 0.7) < 1e-6, 'back to 70 % HP'); + assert.equal(lethal(h, u, ps), 10, 'second save of the same deployment: doubled'); + h.b.retreat(u); + assert.ok(!u.alive); + ps.dp = 90; + assert.ok(h.runUntil(() => u.alive && u.deployed, 120), 'redeployed'); + assert.equal(lethal(h, u, ps), 5, 'after the redeploy: 5 DP again (it used to cost 20)'); + checkInvariants(h.b); + assert.equal(h.b.errors.length, 0); +}); + +test('a 联防 field starts at 5 DP: a battle of its own', () => { + const { h, u, ps } = field('unite'); + assert.equal(lethal(h, u, ps), 5); + assert.equal(lethal(h, u, ps), 10); +}); diff --git a/test/content/feedback5-swire2-self-heal.test.js b/test/content/feedback5-swire2-self-heal.test.js new file mode 100644 index 00000000..c235a70c --- /dev/null +++ b/test/content/feedback5-swire2-self-heal.test.js @@ -0,0 +1,48 @@ +// test/content/feedback5-swire2-self-heal.test.js — 琳琅诗怀雅 S1 仗义疏财 heals herself too (community report of 2026-10-06 +// 「干员琳琅诗怀雅不会治疗自己」): 「下一次攻击会为周围八格内血量不足70%的一名友方单位恢复…」 — the client charpack +// (char_1033_swire2, mode S1, ability HealAlly) selects with range x-4 (the 3×3 with her own tile), ally side, +// `_excludeOwner` 0, max HP ratio 0.7, one target. kits/ops/chess_char_3_04-swire2.js used to skip her own tile. +// Run: node --test test/content/feedback5-swire2-self-heal.test.js + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js'; +import { getDefaultSource } from '../../server/sim/simdata.js'; + +const ds = getDefaultSource(); +const ID = 'chess_char_3_04_a', S1 = 'skchr_swire2_1'; +const S1_INDEX = ds.rawChess(ID).skills.find((s) => s.skillId === S1).index; +const NEIGHBOUR = 'chess_char_3_16_a'; + +/** 琳琅诗怀雅 (S1) on (9, 5), an ally on (10, 5); HP ratios set after the deployment; coins to spare. */ +function field(herRatio, allyRatio) { + const h = makeBattle({ + defs: { enemies: { enemy_d: enemyRec({ key: 'enemy_d', hp: 1e7, speed: 0 }) } }, + units: [{ chessId: ID, row: 9, col: 5, skillIndex: S1_INDEX }, { chessId: NEIGHBOUR, row: 10, col: 5 }], + timeLimit: 60, autoFinish: false, hooks: ['heal'], captureNoisy: true, flags: { dpInit: 60, dpMax: 99 }, + }); + h.step(); + const u = h.unit(ID), ally = h.unit(NEIGHBOUR); + u.mem.coins = 2; + u.hp = u.s.maxHp * herRatio; + ally.hp = ally.s.maxHp * allyRatio; + h.run(1); + return { h, u, ally, heals: h.hooksOf('heal').filter((c) => c.source === u) }; +} + +test('alone below 70 %: a coin heals herself (her own tile is in the x-4 selector)', () => { + const { h, u, heals } = field(0.5, 1); + assert.equal(heals.length, 1, 'one heal'); + assert.equal(heals[0].target, u, 'on herself'); + assert.equal(u.mem.coins, 1, 'one coin spent'); + checkInvariants(h.b); + assert.equal(h.b.errors.length, 0); +}); + +test('the lowest HP ratio of the 3×3 wins: herself below the neighbour, the neighbour below her; 70 % or more is never healed', () => { + assert.equal(field(0.4, 0.6).heals[0].target.defId, ID, 'she is lower: herself'); + assert.equal(field(0.6, 0.4).heals[0].target.defId, NEIGHBOUR, 'the neighbour is lower: the neighbour'); + const none = field(0.75, 1); + assert.equal(none.heals.length, 0, 'not below 70 %: no heal, the coin is kept'); + assert.equal(none.u.mem.coins, 2); +}); diff --git a/test/content/feedback5-vendla-heal.test.js b/test/content/feedback5-vendla-heal.test.js new file mode 100644 index 00000000..b9b95833 --- /dev/null +++ b/test/content/feedback5-vendla-heal.test.js @@ -0,0 +1,58 @@ +// test/content/feedback5-vendla-heal.test.js — 刺玫 S2 荆藤庇荫 and her 咒愈师 trait (community report of 2026-10-06 +// 「干员刺玫开技能(装备源石药剂)之后不能治疗」): the protégé takes only the trait heal of the S2 counter ("该角色受到攻击时刺玫 +// 对目标造成攻击力20%的法术伤害并仅对该角色触发刺玫特性"; en "…and activates her Trait on the ally"; client buff vendla_tr: the +// S2TraitHealRange selector only for a damage whose modifier carries vendla_s_2). Her own attacks keep healing the lowest-HP +// ally in range — herself too (TraitHealRange: _excludeOwner 0). Until 0.2.0 every damage she dealt during the skill healed +// the protégé, so with 源石溶剂 draining her she healed nobody while it ran. +// Run: node --test test/content/feedback5-vendla-heal.test.js + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { makeBattle, enemyRec, chessRec, checkInvariants } from '../helpers/battleHarness.js'; + +const VENDLA = 'chess_char_1_06_a', SOLVENT = 'chess_item_1_05_e_a'; +const tank = chessRec({ id: 't_tank', stats: { maxHp: 9000, atk: 0, def: 0 }, skill: null }); +const still = (r, c) => ({ motion: 'WALK', start: [r, c], end: [r, c], checkpoints: [] }); + +/** 刺玫 (+ 源石溶剂) on (10, 4), a high-max-HP ally on (11, 4) — her protégé —, one dummy enemy on (10, 6) hitting `hits`. */ +function field({ enemyAtk = 0, hits = null } = {}) { + const h = makeBattle({ + defs: { chess: { t_tank: tank }, enemies: { e_dummy: enemyRec({ key: 'e_dummy', hp: 1e8, atk: enemyAtk, speed: 0, range: enemyAtk ? 5 : 0, bat: 1 }) } }, + units: [{ chessId: VENDLA, row: 10, col: 4, items: [SOLVENT] }, { chessId: 't_tank', row: 11, col: 4 }], + enemies: [{ key: 'e_dummy', time: 0, pos: [10, 6], route: still(10, 6) }], + autoFinish: false, timeLimit: 60, seed: 3, captureNoisy: true, hooks: ['heal', 'damaged', 'skillStart'], + setup: hits ? (b) => b.on('beforeAttack', (ctx) => { if (ctx.attacker.side === 'enemy') ctx.targets = [b.allyUnits.find((u) => u.defId === hits)]; }, { priority: 50 }) : null, + }); + const v = h.unit(VENDLA), tk = h.unit('t_tank'); + h.run(8); // the 源石溶剂 drain has her below full HP + const sk = v.skill; + sk.sp = sk.spCost; sk.charges = Math.max(1, sk.charges); + assert.ok(sk.activate('manual'), 'S2 starts'); + assert.equal(v.mem.protege, tk, 'the protégé is the highest-max-HP ally in range'); + return { h, v, tk, t0: h.b.time }; +} + +test('during 荆藤庇荫 her attacks heal the most injured ally in range (herself, drained by 源石溶剂), not the full-HP protégé', () => { + const { h, v, tk, t0 } = field(); + h.run(10); + assert.ok(v.skill.active, 'still running'); + const heals = h.hooksOf('heal').filter((c) => c.source === v && c.t >= t0 && c.amount > 0); + assert.ok(heals.length >= 4, `she keeps healing: ${heals.length} heals in 10 s`); + assert.ok(heals.every((c) => c.target === v), 'every attack heal goes to her — the only injured ally — never to the protégé'); + assert.equal(tk.hp, tk.s.maxHp); + checkInvariants(h.b); + assert.equal(h.b.errors.length, 0); +}); + +test('the S2 counter (a hit on the protégé) heals the protégé; her attacks still heal the lowest-HP ally', () => { + const { h, v, tk, t0 } = field({ enemyAtk: 300, hits: 't_tank' }); + h.run(8); + const counters = h.hooksOf('damaged').filter((c) => c.source === v && c.t >= t0 && (c.dmg?.tags || []).includes('counter')); + assert.ok(counters.length >= 3, 'the enemy hitting the protégé draws counters'); + const heals = h.hooksOf('heal').filter((c) => c.source === v && c.t >= t0); + const toTank = heals.filter((c) => c.target === tk), toHer = heals.filter((c) => c.target === v); + assert.ok(toTank.length >= counters.length, `each counter heals the protégé (${toTank.length} heals for ${counters.length} counters)`); + assert.ok(toHer.length > 0, 'her attacks heal the most injured ally — herself when she is lower than the protégé'); + checkInvariants(h.b); + assert.equal(h.b.errors.length, 0); +}); diff --git a/test/content/kits_t1t2.test.js b/test/content/kits_t1t2.test.js index 6df93b61..5c1dca3d 100644 --- a/test/content/kits_t1t2.test.js +++ b/test/content/kits_t1t2.test.js @@ -219,7 +219,9 @@ test('1_06 刺玫: 荆藤庇荫 taunts the highest-HP ally in range and counters const counters = dealt(h, u, tagged('counter')); assert.ok(counters.length >= 2); for (const c of counters) approx(c.amount, u.s.atk * bb.atk_scale, 'counter = 20 % ATK arts'); - assert.ok(heals(h, u).every((c) => c.target === yak), 'trait heal only on the protected ally during the skill'); + // the counters' trait heal goes to the protected ally; her attacks heal the most injured ally in range — the protected + // one here, the only ally hit (test/content/feedback5-vendla-heal.test.js: herself when she is the most injured) + assert.ok(heals(h, u).every((c) => c.target === yak), 'trait heals on the protected ally (counters; the most injured)'); // talent: the highest max-HP ally in range receives more healing yak.hp = yak.s.maxHp * 0.3; approx(h.b.heal(null, yak, 100), 100 * t.heal_scale, 'heal ×1.08'); diff --git a/test/content/summon_loadout_conflicts.test.js b/test/content/summon_loadout_conflicts.test.js index c309317e..28a55df6 100644 --- a/test/content/summon_loadout_conflicts.test.js +++ b/test/content/summon_loadout_conflicts.test.js @@ -86,9 +86,13 @@ test('赫默: the medical drone lives its owner\'s withdraw duration (module var const start = ops.map((u) => summons(h, u, DRONE)[0]); assert.ok(start.every((d) => d && d.alive), 'both start deploys'); assertLifetimes(h, start, lo.map(life)); - h.run(6); // past the token's redeploy time - assert.ok(ops.every((u) => u.skill.activations === 0), 'no cast yet'); - const drones = castAndCollect(h, ops, DRONE); + // S2 casts by itself the tick its SP is full (22 s), injured ally or not — client skill skchr_silent_2: no `_trigger`, + // `_allowNoTarget` 1 (community report of 2026-10-06, DESIGN §25.16; until then it waited for an injured ally and this + // test cast it by hand): past the token's redeploy time, so each drone comes back with its owner's cast + assert.ok(ops.every((u) => u.skill.activations === 0), 'no cast before the SP is full'); + assert.ok(h.runUntil(() => ops.every((u) => u.skill.activations === 1), 10), 'both cast at full SP'); + const drones = ops.map((u) => summons(h, u, DRONE)[0]); + assert.ok(drones.every((d) => d.alive), 'both drones back on the field'); assertLifetimes(h, drones, lo.map(life)); done(h); } diff --git a/test/golden/README.md b/test/golden/README.md index 6fac31e4..2e9cef61 100644 --- a/test/golden/README.md +++ b/test/golden/README.md @@ -17,7 +17,7 @@ through. | `roster` | 49 battles: every visible chess record (normal and elite) with every selectable skill and module (DESIGN §16), 12 operators per battle on a real stage (all 11 in turn) against the round's real wave three times over, every non-leader enemy kind of `data/enemies.json` as extra spawns (half of them bounties), placeable summons on the board, every equipment item, band, battle-side 机变 card and stage map card in turn, bonds from the board | | `bonds` | 46 battles: every bond at its activation threshold (1 layer) and at its top tier (999 layers) | | `fields` | 22 battles: every Final Assault / Hidden Core leader on a pair and a solo template (shared pool, 200 s cap) and the 联防 field with 1 and 2 helpers on its own map (carried HP / SP, a knocked-out operator, two leakers' enemies) | -| `matches` | 18 matches to the end in virtual time: 16 bot-only (solo 标准 / 险境 / 绝境 / 终极 × 2 seeds, co-op 2 / 3 / 4, one server-run combat match, two runs boosted to the Hidden Core), one co-op match whose human seat (AI 托管, offline) does not own 7 NORMAL chess — they fight as their 补位 stand-ins (its digest lists them per round, `standIns`) — and one whose human seat slots 自选 picks (推进之王 and prototypes) with its 调度中心 at level 5 from the first prep: its own shop draws them and its AI fields 推进之王 (its digest lists per round the 自选 shop draws and the fielded pieces, `diy`) | +| `matches` | 18 matches to the end in virtual time: 16 bot-only (solo 标准 / 险境 / 绝境 / 终极 × 2 seeds, co-op 2 / 3 / 4, one server-run combat match, two runs boosted to the Hidden Core), one co-op match whose human seat (AI 托管, offline) does not own 9 NORMAL chess — they fight as their 补位 stand-ins (its digest lists them per round, `standIns`) — and one whose human seat slots 自选 picks (推进之王 and prototypes) with its 调度中心 at level 5 from the first prep: its own shop draws them and its AI fields 推进之王 (its digest lists per round the 自选 shop draws and the fielded pieces, `diy`) | | `standins` | 10 battles: every NORMAL chess record (normal and elite, 110) fielded as its 补位 stand-in (`standIn: true`: the stand-in's body, backup skill / module and kit — all 17 stand-ins, every skill a chess names for them), 12 per battle by strength band, laid out by the stand-in's position on a real stage, against the round's real wave three times over plus 8 ground enemy kinds (melee stand-ins always meet an enemy), an item each, bonds from the board | | `diy` | battles of 自选 pieces (a DIY slot with its `diy` pick, `shared/diy.js`): every owned 6★ with an operator kit (`kits/index.js OPERATOR_KITS`) in each form of tiers 5 and 6 (normal; elite with no module and with each module) under each skill, then every prototype pick with a kit at its locked selection, normal and elite, 12 per battle against the round's real wave; a new operator kit adds its scenarios (the battle count grows with the kits) | diff --git a/test/golden/bonds.json b/test/golden/bonds.json index 95378549..ae57ca87 100644 --- a/test/golden/bonds.json +++ b/test/golden/bonds.json @@ -116,7 +116,7 @@ ["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null] ], "enemies": {"enemy_1249_lysdb_2":[5,3,2,358,25821],"enemy_1251_lysyta":[15,13,2,36,35396],"enemy_1328_cbjedi":[1,1,0,1940,9000]}, - "hooks": [1,31,1829,179,179,228,223,4,18,22,15,15,0,9876,15,9,21,4,1,18,0,0,0], + "hooks": [1,31,1829,179,179,228,223,0,18,22,15,15,0,9876,15,9,21,4,1,18,0,0,0], "events": {"atk":196,"deploy":8,"die":18,"dmg":223,"engage":8,"fx":36,"leak":4,"skill":30,"spawn":31,"status":25}, "fx": {"aoe":1,"displace":11,"pull":11,"shieldBreak":5,"strike":3,"taunt":1,"volley":3,"zone":1}, "snaps": "b677776a 05df3ce3 0909a76f 5ebbf5d3 c7c03797 29c796e5" @@ -139,10 +139,10 @@ "bossHpLeft": null, "rngDraws": 1, "errors": {"count":0,"kinds":[]}, - "players": {"p1":{"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":129773,"bossDmg":0,"heal":7260,"deaths":0,"layerGains":{}}}, + "players": {"p1":{"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":129773,"bossDmg":0,"heal":7606,"deaths":0,"layerGains":{}}}, "units": [ ["p1",1,"op","chess_char_4_11_b",1,"uniequip_002_cathy",2644,1,5,0,0,0,0,0,0], - ["p1",2,"op","chess_char_1_06_b",1,"uniequip_002_vendla",997.09,1,7,1,43307,6000,5702,41,3], + ["p1",2,"op","chess_char_1_06_b",1,"uniequip_002_vendla",1343.46,1,7,1,43307,6000,6048,41,3], ["p1",3,"op","chess_char_1_13_b",1,"uniequip_002_podego",815.5,1,9,2,34608,0,0,36,5], ["p1",4,"op","chess_char_2_14_b",1,"uniequip_002_flower",1544,1,10,0,0,0,464,21,2], ["p1",5,"op","chess_char_1_06_a",1,null,1137,1,11,1,21655,1569,1062,40,1], @@ -156,10 +156,10 @@ ["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null] ], "enemies": {"enemy_1005_yokai_2":[4,4,0,85,5357],"enemy_1005_yokai_3":[2,2,0,894,69120],"enemy_9009_acfort":[2,2,0,685,55296]}, - "hooks": [1,21,3002,220,220,359,359,2184,8,8,15,11,0,11276,107,0,8,0,1,7,0,0,0], - "events": {"atk":220,"deploy":10,"die":8,"dmg":359,"engage":5,"fx":37,"heal":71,"skill":26,"spawn":21,"status":81}, + "hooks": [1,21,3002,220,220,359,359,2029,8,8,15,11,0,11276,107,0,8,0,1,7,0,0,0], + "events": {"atk":220,"deploy":10,"die":8,"dmg":359,"engage":5,"fx":37,"heal":88,"skill":26,"spawn":21,"status":81}, "fx": {"beam":2,"counter":16,"pull":1,"slippery":2,"taunt":4,"tornado":2,"tornadoPulse":5,"zone":5}, - "snaps": "67ec1cca fe9a3af9 50ebdf47 032ae628 27253169 48bf856f c188e510 b9d5ddd2 e14ce64e 4337816b" + "snaps": "67ec1cca f59d2b34 78cf536b facbaa19 69f2cd48 e1654b75 034aaa70 f8616db2 356f8b2e 9c5af2d5" }, "bond-egirShip-low": { "about": "阿戈尔 low: tier 1 layers 1; chess_char_1_04_a chess_char_3_16_a chess_char_2_07_a chess_char_3_18_a chess_char_3_05_a chess_char_3_14_a chess_char_3_17_a chess_char_3_19_a token_10028_vigil_wolf", @@ -1486,31 +1486,31 @@ "round": 10, "seed": 2806272209, "waveId": "act1autochess_h03", - "time": 138.43, - "ticks": 4153, + "time": 140.27, + "ticks": 4208, "reason": "cleared", - "killed": 10, + "killed": 9, "total": 24, "unspawned": 0, "bossHpLeft": null, "rngDraws": 5, "errors": {"count":0,"kinds":[]}, - "players": {"p1":{"killed":10,"total":24,"perfect":false,"leaks":14,"leaksUncounted":0,"coins":0,"dmg":225273,"bossDmg":0,"heal":10727,"deaths":7,"layerGains":{}}}, + "players": {"p1":{"killed":9,"total":24,"perfect":false,"leaks":15,"leaksUncounted":0,"coins":0,"dmg":230713,"bossDmg":0,"heal":15790,"deaths":4,"layerGains":{}}}, "units": [ - ["p1",1,"op","chess_char_1_02_b",1,"uniequip_002_yak",0,0,2,2,8663,5885,0,83,2], - ["p1",2,"op","chess_char_1_04_b",1,"uniequip_002_udflow",2638.94,1,1,1,32844,265,0,98,3], - ["p1",3,"op","chess_char_4_19_a",2,null,1560,1,36,0,6981,1560,0,62,3], - ["p1",4,"op","chess_char_1_01_b",1,"uniequip_002_inside",0,0,4,2,54929,2969,0,129,5], - ["p1",5,"op","chess_char_1_03_b",1,"uniequip_002_leizi",1391,1,35,4,75671,2100,0,30,1], - ["p1",6,"op","chess_char_1_06_b",1,"uniequip_002_vendla",0,0,5,1,14320,1402,1433,52,3], + ["p1",1,"op","chess_char_1_02_b",1,"uniequip_002_yak",0,0,2,1,5187,5876,0,84,2], + ["p1",2,"op","chess_char_1_04_b",1,"uniequip_002_udflow",2771.47,1,1,1,32730,133,0,97,3], + ["p1",3,"op","chess_char_4_19_a",2,null,1560,1,35,0,6981,1560,0,62,3], + ["p1",4,"op","chess_char_1_01_b",1,"uniequip_002_inside",1421,1,4,2,54217,2657,0,127,5], + ["p1",5,"op","chess_char_1_03_b",1,"uniequip_002_leizi",1391,1,7,4,79051,4252,0,33,2], + ["p1",6,"op","chess_char_1_06_b",1,"uniequip_002_vendla",1281.19,1,5,1,20681,1417,6655,76,4], ["p1",7,"op","chess_char_4_20_a",2,null,1217,1,34,0,31866,1466,0,26,2], - ["p1",8,"op","chess_char_4_21_a",1,null,1271,1,33,0,0,9493,9294,35,1] + ["p1",8,"op","chess_char_4_21_a",1,null,1271,1,33,0,0,8607,9135,35,1] ], - "enemies": {"enemy_1166_dusbr_2":[14,9,5,4744,117237],"enemy_1169_duphlx_2":[7,1,6,2029,29391],"enemy_1172_dugago":[3,0,3,18365,78645]}, - "hooks": [1,36,4153,676,676,839,838,75,17,31,20,20,82,13690,266,12,24,14,1,19,0,0,1], - "events": {"atk":760,"deploy":12,"die":17,"dmg":838,"engage":7,"fx":17,"heal":48,"leak":14,"skill":40,"spawn":32,"status":233}, - "fx": {"counter":1,"dodge":1,"featherArrow":2,"flame":3,"garrisonGrant":1,"healField":1,"revive":2,"riposte":1,"stone":2,"taunt":3}, - "snaps": "e8a58cae cc93867c 9aa85377 7ca07670 8057af22 e3c22593 ad0835c7 7ff82d39 9bbd966f 9a60dafe ba1e12ae 00940850 7a448d34 472c2ca1" + "enemies": {"enemy_1166_dusbr_2":[14,8,6,5050,114778],"enemy_1169_duphlx_2":[7,1,6,2029,28344],"enemy_1172_dugago":[3,0,3,18888,87591]}, + "hooks": [1,35,4208,712,712,876,875,82,13,28,22,22,82,15310,268,12,24,15,1,15,0,0,1], + "events": {"atk":797,"deploy":11,"die":13,"dmg":875,"engage":7,"fx":20,"heal":82,"leak":15,"skill":44,"spawn":32,"status":239}, + "fx": {"counter":3,"dodge":1,"featherArrow":2,"flame":3,"garrisonGrant":1,"healField":1,"revive":2,"riposte":1,"stone":2,"taunt":4}, + "snaps": "e8a58cae cc93867c 9aa85377 7ca07670 8057af22 e3c22593 ad0835c7 7ff82d39 5fe27d3c 6e667836 542993ce 0116e8e4 8ce69c48 ece782e2" }, "bond-swiftShip-low": { "about": "迅捷 low: tier 1 layers 1; chess_char_4_23_a chess_char_4_22_a chess_char_1_19_a chess_char_1_01_a chess_char_4_24_a chess_char_4_25_a chess_char_4_26_a chess_char_3_01_a", @@ -1609,8 +1609,8 @@ ["p1",3,"op","chess_char_3_03_a",1,null,1378,1,5,0,0,0,0,0,0], ["p1",4,"op","chess_char_3_04_a",1,null,1864,1,8,1,21821,0,0,0,0], ["p1",5,"op","chess_char_3_05_a",2,null,5435.3,1,9,5,20785,1731,0,21,1], - ["p1",6,"op","chess_char_1_06_a",1,null,1137,1,3,2,17074,0,2701,36,1], - ["p1",7,"op","chess_char_2_05_a",1,null,1411.2,1,4,0,0,0,3996,22,1], + ["p1",6,"op","chess_char_1_06_a",1,null,1137,1,3,2,17074,0,2760,36,1], + ["p1",7,"op","chess_char_2_05_a",1,null,1411.2,1,4,0,0,0,3937,21,1], ["p1",8,"op","chess_char_2_10_a",1,null,1078,1,10,0,4419,0,0,29,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], @@ -1633,10 +1633,10 @@ ["p1","~17","token","token_10031_swire2_gdtrap",null,null,1000,1,44,0,0,0,0,0,null] ], "enemies": {"enemy_1116_liprr":[11,11,0,312,69696],"enemy_1116_liprr_2":[4,4,0,429,34560],"enemy_1118_lidbox_2":[2,2,0,10463,21888]}, - "hooks": [1,44,1992,210,210,367,394,51,18,34,6,3,0,6986,15,16,17,0,1,18,0,1,0], - "events": {"atk":210,"deploy":25,"die":34,"dmg":394,"engage":5,"fx":123,"heal":42,"skill":11,"spawn":44,"status":48}, + "hooks": [1,44,1992,209,209,367,394,45,18,34,6,3,0,6986,15,16,17,0,1,18,0,1,0], + "events": {"atk":209,"deploy":25,"die":34,"dmg":394,"engage":5,"fx":123,"heal":45,"skill":11,"spawn":44,"status":48}, "fx": {"aoe":10,"burst":1,"coin":20,"counter":5,"explode":16,"liberate":3,"overload":1,"phase":9,"strike":40,"summon":17,"taunt":1}, - "snaps": "3a6154e2 8441cbea 22a9f920 52b9435f e1527c0b db3d00cc 19eea9b6" + "snaps": "3a6154e2 8441cbea 22a9f920 2943e417 a30c9b83 8b94283f a2bde0a9" }, "bond-victoriaShip-high": { "about": "维多利亚 high: tier 2 layers 999; chess_char_4_11_b chess_char_4_07_b chess_char_1_06_b chess_char_2_05_b chess_char_2_10_b chess_char_3_08_b chess_char_4_13_b chess_char_4_14_b token_10041_cathy_catsld token_10041_cathy_catsld", @@ -1673,7 +1673,7 @@ ["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null] ], "enemies": {"enemy_1005_yokai_2":[4,4,0,1618,15428],"enemy_1041_lazerd_2":[3,3,0,5218,44790],"enemy_1042_frostd":[5,5,0,0,80870]}, - "hooks": [1,25,1933,76,76,78,78,17,12,12,10,9,0,5149,274,0,12,0,1,12,0,0,0], + "hooks": [1,25,1933,76,76,78,78,12,12,12,10,9,0,5149,274,0,12,0,1,12,0,0,0], "events": {"atk":76,"deploy":10,"die":12,"dmg":78,"engage":3,"fx":12,"heal":12,"skill":19,"spawn":25,"status":19}, "fx": {"counter":3,"explosion":6,"overload":1,"taunt":2}, "snaps": "54ecd262 4cb3843a 6dc96721 240806fe ea24b00b ec19662f cbe42cad" @@ -1709,10 +1709,10 @@ ["p1",9,"token","token_10000_silent_healrb",null,null,1000,1,29,0,0,0,1372,18,null] ], "enemies": {"enemy_1116_liprr":[11,11,0,2221,54450],"enemy_1116_liprr_2":[4,4,0,932,27000],"enemy_1118_lidbox_2":[2,1,1,5466,16462]}, - "hooks": [1,28,1790,191,191,208,208,32,18,21,16,16,0,8079,3,27,17,1,1,18,0,0,0], + "hooks": [1,28,1790,191,191,208,208,32,18,21,16,16,0,8141,3,27,17,1,1,18,0,0,0], "events": {"atk":226,"deploy":11,"die":20,"dmg":208,"engage":6,"fx":54,"heal":32,"leak":1,"skill":32,"spawn":26,"status":26}, "fx": {"aoe":1,"displace":19,"liberate":3,"phase":4,"pull":19,"strike":3,"summon":2,"volley":3}, - "snaps": "4f49e250 3e34819c 91d6de66 5985f2e5 1e121271 ece611fe" + "snaps": "351c8009 987b22dd 7d790a0c fadc11f1 fb446f70 8805598c" }, "bond-visiShip-high": { "about": "远见 high: tier 1 layers 999; chess_char_4_17_b chess_char_4_18_b chess_char_3_03_b chess_char_4_19_b chess_char_4_16_b chess_char_2_02_b chess_char_4_20_b chess_char_4_21_b token_10000_silent_healrb", @@ -1738,19 +1738,19 @@ ["p1",2,"op","chess_char_4_18_b",1,"uniequip_002_mudrok",6104,1,4,0,0,0,0,0,0], ["p1",3,"op","chess_char_3_03_b",1,"uniequip_002_swire",1898,1,5,7,23102,0,0,23,1], ["p1",4,"op","chess_char_4_19_b",2,"uniequip_002_flamtl",157.8,1,8,2,31781,1960,0,27,3], - ["p1",5,"op","chess_char_4_16_b",2,"uniequip_002_texas2",0,0,31,6,46796,3266,28,41,4], - ["p1",6,"op","chess_char_2_02_b",1,"uniequip_002_silent",1614,1,6,0,0,1084,3098,8,2], + ["p1",5,"op","chess_char_4_16_b",2,"uniequip_002_texas2",0,0,32,6,46796,3266,28,41,4], + ["p1",6,"op","chess_char_2_02_b",1,"uniequip_002_silent",1614,1,6,0,0,1084,3098,8,5], ["p1",7,"op","chess_char_4_20_b",2,"uniequip_002_fartth",1419,1,7,3,35639,1704,0,22,1], ["p1",8,"op","chess_char_4_21_b",1,"uniequip_002_plosis",1584,1,10,0,0,2478,2168,14,1], - ["p1",9,"token","token_10000_silent_healrb",null,null,1000,0,25,0,0,0,0,0,null], + ["p1",9,"token","token_10000_silent_healrb",null,null,1000,0,35,0,0,0,0,0,null], ["-","~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_1234_dsubrl_2":[2,2,0,9884,32256],"enemy_1433_dsbasi_2":[16,16,0,608,105062]}, - "hooks": [1,32,2169,158,158,244,298,40,22,23,12,11,0,2963,17,18,18,0,1,22,0,0,9], - "events": {"atk":162,"deploy":12,"die":23,"dmg":298,"engage":4,"fx":90,"heal":23,"skill":25,"spawn":29,"status":31}, - "fx": {"dodge":9,"featherArrow":1,"flame":3,"garrisonGrant":1,"healField":1,"pulse":43,"riposte":9,"summon":2,"swordRain":17,"swordStorm":4}, - "snaps": "8108fcf3 f23c307f e1e3d738 bfea4fc4 5bc63ae6 522694d6 ea7f43e4 25faa0a1" + "hooks": [1,34,2169,158,158,244,298,40,22,25,15,14,0,3657,17,18,18,0,1,22,0,0,9], + "events": {"atk":162,"deploy":14,"die":25,"dmg":298,"engage":4,"fx":92,"heal":23,"skill":31,"spawn":29,"status":31}, + "fx": {"dodge":9,"featherArrow":1,"flame":3,"garrisonGrant":1,"healField":1,"pulse":43,"riposte":9,"summon":4,"swordRain":17,"swordStorm":4}, + "snaps": "f2695385 772aaaa3 406ef0c7 3308eca7 65753010 432ff52b 07e6e68b 2aca82b1" }, "bond-yanShip-low": { "about": "炎 low: tier 1 layers 1; chess_char_3_16_a chess_char_3_03_a chess_char_3_18_a chess_char_1_03_a chess_char_2_04_a chess_char_3_14_a chess_char_3_17_a chess_char_3_19_a token_10028_vigil_wolf", diff --git a/test/golden/fields.json b/test/golden/fields.json index 35fc2c3b..b096f4ea 100644 --- a/test/golden/fields.json +++ b/test/golden/fields.json @@ -81,7 +81,7 @@ "hooks": [1,70,6000,1208,1208,2984,3373,4986,32,41,94,88,9,39706,2798,15,29,0,1,32,0,21,0], "events": {"atk":1512,"deploy":35,"die":41,"dmg":3089,"engage":17,"fx":630,"heal":489,"skill":182,"spawn":68,"status":684}, "fx": {"beam":54,"blink":1,"bombard":9,"bombardShell":9,"burst":21,"copy":3,"dp":4,"drone":335,"droneLock":17,"drones":3,"firewall":1,"garrisonGrant":1,"lightning":108,"link":1,"lock":9,"radiantSword":4,"shell":8,"snow":5,"split":2,"storm":10,"summon":8,"talent":4,"tide":5,"wanted":8}, - "snaps": "d9836110 12e5b436 35c19c21 8b602c4b 63a29594 e92667ad 11995740 f731d336 2bcec2e1 d8cbc3bd 9017d64b 51d18505 e6ef48bf 94c31471 ab8f2380 1b98c997 13c68f78 a6b5eaa0 e318ae23 fd4a1367" + "snaps": "72dc81cb e1b819af f6f48b33 12da5c05 71ab4302 b8094e94 48f11686 168adccb 8baeac29 a827fcb5 f2c51503 f6ac9a0e 26f4f8c7 1e115b43 3689b856 f9c504d5 6a28e830 a1160c66 1e744db9 78024c8a" }, "boss-boss_1-solo": { "about": "假想敌:胄 solo pool 1800000: 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", @@ -325,7 +325,7 @@ "tide": 5, "wanted": 3 }, - "snaps": "b5e578c7 08019f4b 2414497e e8ccf404 0a757964 27f19ffa dc979221 9a9cddad 37887448 9597c3ad 3331f98b 22dca952 01026ee5 afe32a6d 65ca8fec ed617f42 3ed37aad 2975ad94 80313e62 c870870c" + "snaps": "87a11c35 cc0e1f70 2de88ef7 a5a4efc1 36878065 d2f20d65 a5481200 93371ab3 fa830c40 8d59e885 222cca53 040b3f01 dc7f3982 cca5f067 8a116542 f1f56dac c68f3562 6833a03c 12743320 0f665121" }, "boss-boss_3-solo": { "about": "假想敌:管 solo pool 2000000: 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", @@ -606,7 +606,7 @@ "tide": 3, "wanted": 8 }, - "snaps": "726b45e6 655ee79a 912e8d78 fe351355 d02de871 2de23192 90fc2124 37da201e 332c960d a6a1a21f 4df5e422 d780f15c" + "snaps": "eca813c6 f271e40a c26e5584 d73b51c9 a7c760e5 818a39be 850539df a8a1d78c 1c2b94fb 61f626e7 15b5d45a 89f18974" }, "boss-boss_5-solo": { "about": "卢西恩,“猩红血钻” solo pool 780000: 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", @@ -865,7 +865,7 @@ "hooks": [1,52,6000,1475,1475,3513,3867,3107,22,29,95,90,263,39065,1638,4,17,0,1,22,0,13,0], "events": {"atk":1666,"deploy":35,"die":29,"dmg":3557,"engage":19,"fx":565,"heal":514,"skill":185,"spawn":50,"status":588}, "fx": {"airstrike":92,"beam":4,"blink":2,"burst":13,"column":47,"copy":4,"dp":5,"drone":334,"droneLock":16,"drones":3,"explode":6,"garrisonGrant":1,"link":1,"phase":1,"shieldBreak":4,"snow":5,"split":5,"steal":7,"sunBurst":4,"talent":4,"tide":5,"wanted":2}, - "snaps": "aac21795 826f3a92 c07605ac 63ba2617 10d034dd f637231c 679eaf63 4484256d 932a1025 95a8f717 9f641051 49ed0ece cc247786 d907a3ce 4899d3c0 a603253f 2e8677a7 fc4cd6c6 ce276a0b 7cb6d808" + "snaps": "7ec7bc8a 80b84ac8 9ef4ab5e 2af0def9 18e8a357 b2a774b5 3da4fec2 83128f9e baca593e da6a4488 535145ce d7c8184c 6695103d 37c5ae18 9014d962 41b8cdf9 a33c7228 5e09f2b0 bd896428 bffa666e" }, "boss-boss_7-solo": { "about": "“萨米的意志” solo pool 2000000: 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", @@ -1077,36 +1077,36 @@ "killed": 8, "total": 8, "unspawned": 0, - "bossHpLeft": 4049210, + "bossHpLeft": 4046346, "rngDraws": 1680, "errors": {"count":0,"kinds":[]}, "players": { - "p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1426827,"bossDmg":597682,"heal":13214,"deaths":5,"layerGains":{}}, - "p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2554978,"bossDmg":913911,"heal":130784,"deaths":8,"layerGains":{}} + "p1": {"killed":8,"total":8,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1435418,"bossDmg":600545,"heal":13215,"deaths":5,"layerGains":{}}, + "p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2553890,"bossDmg":913911,"heal":130783,"deaths":8,"layerGains":{}} }, "units": [ - ["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",0,0,58,0,0,10883,4690,0,2], + ["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",0,0,60,0,0,10883,4690,0,2], ["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",2939,1,11,0,23455,2258,0,35,1], ["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",2990,1,17,0,52147,2547,0,50,2], ["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",99.06,1,7,1,1043569,3226,0,57,8], ["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",931.61,1,9,0,31824,10406,0,46,2], - ["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",745.66,1,52,0,8749,9134,562,0,4], + ["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",745.66,1,52,0,17203,9134,563,0,4], ["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",2451.4,1,13,1,81581,0,0,0,11], - ["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,19,1,32005,0,7161,70,2], - ["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",0,0,21,1,131535,12922,0,27,27], - ["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,56,0,14353,9097,349,78,2], - ["p1",11,"token","token_10017_skadi2_dedant",null,null,0,0,28,0,0,130,0,0,null], + ["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,19,0,31409,0,7161,70,2], + ["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",0,0,21,2,130635,12922,0,27,27], + ["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,57,0,15985,9097,349,78,2], + ["p1",11,"token","token_10017_skadi2_dedant",null,null,130.4,0,28,0,0,0,0,0,null], ["p1",12,"token","token_10030_mlyss_wtrman",null,null,0,0,24,0,0,1614,292,4,1], - ["p2",13,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",0,0,60,3,471039,50514,0,332,3], - ["p2",14,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",0,0,61,0,247360,13406,0,68,4], - ["p2",15,"op","chess_char_5_01_b",1,"uniequip_002_excu2",0,0,59,0,13275,9642,2640,45,2], - ["p2",16,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",2458.52,1,62,1,509240,22200,0,58,2], + ["p2",13,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",0,0,62,3,469951,50514,0,332,3], + ["p2",14,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",0,0,63,0,247360,13406,0,68,4], + ["p2",15,"op","chess_char_5_01_b",1,"uniequip_002_excu2",0,0,61,0,13275,9642,2640,45,2], + ["p2",16,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",2458.52,1,64,1,509240,22200,0,58,2], ["p2",17,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",2886.8,1,8,0,264522,6236,0,75,3], ["p2",18,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2215,1,14,0,662969,6421,4283,285,6], ["p2",19,"op","chess_char_6_14_b",2,"uniequip_002_lumen",2222.83,1,20,0,0,2781,19999,79,1], ["p2",20,"op","chess_char_6_16_b",2,"uniequip_002_halo2",0,0,10,0,80392,3176,881,101,3], ["p2",21,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1331,1,27,0,255595,5370,0,32,3], - ["p2",22,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",0,0,25,0,0,17059,102981,70,2], + ["p2",22,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",0,0,25,0,0,17059,102980,70,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], @@ -1118,20 +1118,23 @@ ["p1","~2","token","token_10030_mlyss_wtrman",null,null,131.08,0,48,0,1401,2025,0,19,null], ["p1","~3","token","token_10030_mlyss_wtrman",null,null,0,0,49,0,120,1322,0,2,null], ["p1","~1","token","token_10040_siege2_vlion",null,null,3722.5,0,50,0,4750,277,0,10,null], - ["p2","~2","token","token_10019_nearl2_sword",null,null,3093.17,0,53,0,44047,4069,0,25,null], - ["p1","~2","token","token_10040_siege2_vlion",null,null,3933.26,0,55,0,0,81,0,0,null], - ["p1","~4","token","token_10030_mlyss_wtrman",null,null,1322,1,57,0,0,180,160,0,1] + ["p1","~1","token","token_10017_skadi2_dedant",null,null,130.4,0,53,0,0,0,0,0,null], + ["p2","~2","token","token_10019_nearl2_sword",null,null,3093.17,0,54,0,44047,4069,0,25,null], + ["p1","~2","token","token_10040_siege2_vlion",null,null,3933.26,0,56,0,0,81,0,0,null], + ["p1","~4","token","token_10030_mlyss_wtrman",null,null,1322,1,58,0,0,180,160,0,1], + ["p1","~2","token","token_10017_skadi2_dedant",null,null,100,0,59,0,0,0,0,0,null], + ["p1","~3","token","token_10017_skadi2_dedant",null,null,100,1,65,0,0,0,0,0,null] ], "enemies": { "enemy_1267_nhpbr_2": [6,6,0,2895,141894], "enemy_9007_acelem": [2,2,0,23398,309587], - "enemy_9017_achunt_2": [2,0,0,59262,1550790], - "enemy_9018_actrpa": [1,0,0,43264,577004], - "enemy_9019_actrpb": [1,0,0,21943,535849], - "enemy_9020_actrpc": [1,0,0,24229,938579] + "enemy_9017_achunt_2": [2,0,0,59131,1553654], + "enemy_9018_actrpa": [1,0,0,43264,579324], + "enemy_9019_actrpb": [1,0,0,21943,537009], + "enemy_9020_actrpc": [1,0,0,24229,939739] }, - "hooks": [1,60,6000,1767,1767,7110,13865,2612,25,35,93,88,317,45858,2401,14,13,0,1,29,0,17,0], - "events": {"atk":2141,"deploy":41,"die":35,"dmg":13390,"engage":22,"fx":2180,"heal":905,"skill":181,"spawn":49,"status":750}, + "hooks": [1,63,6000,1767,1767,7114,13887,2610,24,37,93,88,317,45858,2398,14,13,0,1,28,0,17,0], + "events": {"atk":2140,"deploy":44,"die":37,"dmg":13412,"engage":22,"fx":2180,"heal":906,"skill":181,"spawn":52,"status":751}, "fx": { "airstrike": 99, "beam": 24, @@ -1173,7 +1176,7 @@ "wanted": 6, "zone": 6 }, - "snaps": "9797dd57 d3ba8d58 32d06887 bf42877c 1994874a e5491557 66d383f3 1312fd65 b74b9f4a 2f28845d ead771e6 c391a1a4 f923753b 23bee15f caa10968 93e7fa5f 8c6cfd06 4d79d333 2710458e 1a607645" + "snaps": "72f6af28 6f72c7d4 04c65e53 d68ea41c 34b0f4bf 38a30bbc b2d8c8dd 8d93cc3f 748b9cb6 4a9137e0 a01dbf1e ebdbc3fd a7ffe178 2c92480a 1e31807b 97b40094 e5feed1a 763383f6 f063bb7e d211d016" }, "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", @@ -1397,7 +1400,7 @@ "hooks": [1,25,5100,20,20,57,129,2358,12,15,6,5,7,9981,13,1,11,0,1,12,0,2,0], "events": {"atk":28,"bounty":1,"deploy":14,"die":15,"dmg":78,"engage":3,"fx":57,"skill":11,"spawn":22,"status":43}, "fx": {"bombard":7,"bombardShell":7,"burst":2,"camouflage":1,"lightning":14,"lock":7,"lure":1,"snow":12,"storm":1,"talent":1,"tide":3,"wanted":1}, - "snaps": "9845e2f6 5498943b a2c8865c 4947e8f1 7b75af29 70cf729f e3aec65c 1ab76abd 919d0c1a 8f2a907c 676cd0dc 4c514cee d56b2743 decf1034 44bec2d5 8b318871 593802f3" + "snaps": "c558a4df 5e12c668 02dad932 00fbc7af bc41cf43 ad41bea1 6e07fbe6 27dde52f 53b9a18c a640dd14 452ad2b3 46658458 8c81ee61 4c70f7fe c0fba143 3b8d81c6 aa0fc004" }, "unite-2": { "about": "联防 2 helper(s), 11 leaked: chess_char_6_15_b chess_char_6_17_b chess_char_6_09_b chess_char_6_11_b chess_char_6_12_b chess_char_6_13_b chess_char_6_14_b chess_char_6_16_b token_10030_mlyss_wtrman | chess_char_6_19_b chess_char_5_01_b chess_char_5_05_b chess_char_5_06_b chess_char_6_18_b chess_char_6_20_b chess_char_5_02_b chess_char_5_03_b", diff --git a/test/golden/matches.json b/test/golden/matches.json index 68148c80..2dd95ca9 100644 --- a/test/golden/matches.json +++ b/test/golden/matches.json @@ -59,7 +59,7 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":null,"factions":["ELEMENT","DOT","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":8,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":98765,"virtualMs":68100}, + "end": {"victory":false,"roundsPassed":8,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":97187,"virtualMs":68100}, "rng": {"setup":16,"shop":161,"waves":0,"draft":0,"bots":5205,"meta":43}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ @@ -70,7 +70,7 @@ {"round":5,"ai_0":[1,29,8,0,2,3,8,"6345271e",50,"deputShip 2/16, emptyShip 1/4, kjeragShip 1/10, steadShip 1/10, victoriaShip 1/10",17,17,0,29]}, {"round":6,"ai_0":[1,29,11,0,1,4,8,"c02dfe1b",69,"deputShip 2/22, emptyShip 0/4, kjeragShip 1/14, steadShip 2/18, victoriaShip 1/15",14,14,0,39]}, {"round":7,"ai_0":[1,29,11,0,2,4,8,"e7d70234",90,"deputShip 2/28, emptyShip 0/4, kjeragShip 1/18, steadShip 2/24, victoriaShip 1/20",35,35,0,50]}, - {"round":8,"ai_0":[1,26,13,0,2,4,8,"fbc70791",98,"deputShip 2/34, emptyShip 1/8, kjeragShip 1/22, steadShip 0/24, swiftShip 1/9, victoriaShip 1/25",21,24,3,63]} + {"round":8,"ai_0":[1,29,13,0,2,4,8,"76e9fa7b",98,"deputShip 2/34, emptyShip 1/8, kjeragShip 1/22, steadShip 0/24, swiftShip 1/9, victoriaShip 1/25",24,24,0,63]} ], "players": { "ai_0": { @@ -83,7 +83,7 @@ "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 0, "reward": 100, - "stats": {"activatedLayers":128,"bossDamage":101235,"dmgDealt":686257,"gold":78,"itemsEquipped":16,"kills":138,"leaks":3,"lpLost":3,"merges":0,"perfectRounds":7,"refreshes":18}, + "stats": {"activatedLayers":128,"bossDamage":102813,"dmgDealt":703977,"gold":78,"itemsEquipped":16,"kills":141,"leaks":0,"lpLost":0,"merges":0,"perfectRounds":8,"refreshes":18}, "lineup": ["3_20_a@12,3[4_07_e_a+3_04_e_b]","2_14_a@12,4","2_05_a@12,6","3_10_a@11,3[2_03_e_a]","4_14_a@11,8[4_02_e_a+4_08_e_a]","1_06_a@10,3[1_05_e_b+5_02_e_a]","1_02_a@10,4[3_08_e_a+2_01_e_b]","4_11_a@10,8[4_04_e_a+1_01_e_a]"], "bonds": ["deputShip 2/40","emptyShip 1/12","kjeragShip 1/28","steadShip 0/26","swiftShip 1/18","victoriaShip 1/30"] } @@ -726,7 +726,7 @@ "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, "trophies": 3, "reward": 298, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3581486,"gold":136,"itemsEquipped":11,"kills":249,"leaks":51,"lpLost":34,"merges":4,"perfectRounds":8,"refreshes":22}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3587885,"gold":136,"itemsEquipped":11,"kills":249,"leaks":51,"lpLost":34,"merges":4,"perfectRounds":8,"refreshes":22}, "lineup": [], "bonds": ["arcaneShip 0/40","kazimierzShip 0/2","preciShip 0/46","sargonShip 0/43","skillfulShip 0/14","swiftShip 0/10"] } @@ -766,57 +766,57 @@ }, { "round": 4, - "ai_0": [1,23,8,0,1,2,8,"8f7fa63d",12,"deputShip 1/8, kazimierzShip 0/2, swiftShip 1/4",12,14,2,23], + "ai_0": [1,21,8,0,1,2,8,"248ea0fd",12,"deputShip 1/8, kazimierzShip 0/2, swiftShip 1/4",12,14,2,23], "ai_1": [1,43,9,0,2,2,8,"4c004aa4",16,"deputShip 1/2, kazimierzShip 0/7, swiftShip 1/14",7,12,5,24], "ai_2": [1,27,8,0,1,2,8,"e66e483b",6,"indomShip 1/2, siracusaShip 1/0, soloShip 1/4, yanShip 0/1",12,12,0,23] }, { "round": 5, - "ai_0": [1,23,9,0,0,3,8,"61eb7d90",20,"deputShip 1/12, kazimierzShip 0/2, soloShip 1/2, swiftShip 1/6",53,53,0,32], + "ai_0": [1,21,9,0,0,3,8,"61eb7d90",20,"deputShip 1/12, kazimierzShip 0/2, soloShip 1/2, swiftShip 1/6",53,53,0,32], "ai_1": [1,43,10,0,0,3,8,"b4417f25",27,"deputShip 2/6, kazimierzShip 0/7, swiftShip 1/21",53,53,0,34], "ai_2": [1,27,9,0,2,3,8,"a15e1321",10,"indomShip 1/4, siracusaShip 1/0, soloShip 1/6, yanShip 0/1",53,53,0,31] }, { "round": 6, - "ai_0": [1,23,9,0,1,3,8,"4faaf967",44,"deputShip 2/20, kazimierzShip 0/2, lateranoShip 1/7, skillfulShip 1/7, soloShip 0/2, swiftShip 1/10",23,26,3,42], - "ai_1": [1,43,9,0,0,3,8,"85c81f35",40,"deputShip 1/8, kazimierzShip 0/7, siracusaShip 1/2, soloShip 1/2, suntShip 1/2, swiftShip 1/26",26,26,0,43], + "ai_0": [1,21,9,0,1,3,8,"4faaf967",44,"deputShip 2/20, kazimierzShip 0/2, lateranoShip 1/7, skillfulShip 1/7, soloShip 0/2, swiftShip 1/10",23,26,3,42], + "ai_1": [1,43,9,0,0,3,8,"85c81f35",46,"deputShip 1/8, kazimierzShip 0/7, siracusaShip 1/8, soloShip 1/2, suntShip 1/2, swiftShip 1/26",26,26,0,43], "ai_2": [1,27,11,0,2,3,8,"fa398791",18,"indomShip 1/8, miraShip 1/0, siracusaShip 1/4, soloShip 1/6, suntShip 1/0, yanShip 0/1",12,26,14,43] }, { "round": 7, - "ai_0": [1,23,11,0,0,4,8,"0ae8946b",78,"deputShip 2/28, kazimierzShip 0/4, lateranoShip 1/17, skillfulShip 1/17, soloShip 0/2, swiftShip 1/16",22,25,3,55], - "ai_1": [1,43,10,0,0,4,8,"bf120dea",59,"deputShip 1/10, kazimierzShip 0/7, siracusaShip 1/10, soloShip 1/4, suntShip 1/4, swiftShip 1/31",25,25,0,52], + "ai_0": [1,21,11,0,0,4,8,"0ae8946b",78,"deputShip 2/28, kazimierzShip 0/4, lateranoShip 1/17, skillfulShip 1/17, soloShip 0/2, swiftShip 1/16",22,25,3,55], + "ai_1": [1,43,10,0,0,4,8,"bf120dea",65,"deputShip 1/10, kazimierzShip 0/7, siracusaShip 1/16, soloShip 1/4, suntShip 1/4, swiftShip 1/31",25,25,0,52], "ai_2": [1,27,12,0,0,4,8,"162f421b",31,"deputShip 1/2, indomShip 0/8, miraShip 1/5, siracusaShip 1/12, soloShip 1/10, suntShip 1/2, yanShip 0/1",22,25,3,55] }, { "round": 8, - "ai_0": [1,15,11,0,0,4,8,"f1f06cea",104,"deputShip 2/39, kazimierzShip 0/4, lateranoShip 1/24, skillfulShip 1/27, soloShip 1/6, steadShip 1/8, swiftShip 0/16",12,20,8,66], - "ai_1": [1,33,11,0,0,4,8,"e84c71c4",78,"deputShip 1/12, kazimierzShip 0/7, raidShip 1/7, siracusaShip 1/12, soloShip 0/4, suntShip 1/6, swiftShip 1/41",8,20,12,63], - "ai_2": [1,17,11,0,4,4,8,"c8bb7116",68,"deputShip 1/4, indomShip 0/8, miraShip 1/7, siracusaShip 1/32, skillfulShip 1/2, soloShip 1/12, suntShip 1/4, swiftShip 1/7, yanShip 0/1",6,20,14,67] + "ai_0": [1,11,11,0,0,4,8,"f1f06cea",104,"deputShip 2/39, kazimierzShip 0/4, lateranoShip 1/24, skillfulShip 1/27, soloShip 1/6, steadShip 1/8, swiftShip 0/16",10,20,10,66], + "ai_1": [1,33,11,0,0,4,8,"2b26c1da",84,"deputShip 1/12, kazimierzShip 0/7, raidShip 1/7, siracusaShip 1/18, soloShip 0/4, suntShip 1/6, swiftShip 1/41",5,20,15,63], + "ai_2": [1,17,11,0,4,4,8,"3618b45d",68,"deputShip 1/4, indomShip 0/8, miraShip 1/7, siracusaShip 1/32, skillfulShip 1/2, soloShip 1/12, suntShip 1/4, swiftShip 1/7, yanShip 0/1",5,20,15,67] }, { "round": 9, - "ai_0": [1,10,12,0,1,4,8,"1ebef190",153,"deputShip 1/45, emptyShip 1/2, kazimierzShip 0/4, lateranoShip 1/29, skillfulShip 1/35, soloShip 1/8, steadShip 1/14, suntShip 1/2, swiftShip 1/18",52,57,5,78], - "ai_1": [1,25,12,0,1,4,8,"b3d8767c",107,"deputShip 1/14, kazimierzShip 0/7, raidShip 1/14, siracusaShip 1/20, soloShip 0/4, suntShip 1/8, swiftShip 1/51",51,59,8,76], + "ai_0": [1,2,12,0,1,4,8,"aac88c8e",153,"deputShip 1/45, emptyShip 1/2, kazimierzShip 0/4, lateranoShip 1/29, skillfulShip 1/35, soloShip 1/8, steadShip 1/14, suntShip 1/2, swiftShip 1/18",44,53,9,78], + "ai_1": [1,27,12,0,1,4,8,"b3d8767c",113,"deputShip 1/14, kazimierzShip 0/7, raidShip 1/14, siracusaShip 1/26, soloShip 0/4, suntShip 1/8, swiftShip 1/51",48,54,6,76], "ai_2": [1,7,16,0,0,4,8,"a9a38e4e",84,"deputShip 0/4, indomShip 0/8, miraShip 1/10, siracusaShip 2/58, skillfulShip 0/4, soloShip 1/12, suntShip 1/4, swiftShip 0/9, yanShip 0/1",40,50,10,82] }, { "round": 10, "ai_0": [0,0,13,0,0,5,0,"811c9dc5",0,"arcaneShip 0/4, deputShip 0/45, emptyShip 0/2, kazimierzShip 0/4, lateranoShip 0/36, skillfulShip 0/38, soloShip 0/8, steadShip 0/18, suntShip 0/2, swiftShip 0/23",8,18,10,91], - "ai_1": [1,15,13,0,0,5,8,"fc1d75ab",134,"deputShip 1/16, kazimierzShip 0/7, raidShip 1/21, siracusaShip 1/26, soloShip 0/4, suntShip 1/10, swiftShip 1/61",5,18,13,88], - "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/10, siracusaShip 0/60, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",10,18,8,93] + "ai_1": [1,17,13,0,0,5,8,"ddbe3d9a",140,"deputShip 1/16, kazimierzShip 0/7, raidShip 1/21, siracusaShip 1/32, soloShip 0/4, suntShip 1/10, swiftShip 1/61",8,18,10,88], + "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/13, siracusaShip 0/66, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",11,18,7,93] }, { "round": 11, "ai_0": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/4, deputShip 0/45, emptyShip 0/2, kazimierzShip 0/4, lateranoShip 0/36, skillfulShip 0/38, soloShip 0/8, steadShip 0/18, suntShip 0/2, swiftShip 0/23",null,null,null,91], - "ai_1": [1,5,12,0,1,5,8,"3e54e2ff",145,"deputShip 0/16, kazimierzShip 0/7, preciShip 1/5, raidShip 1/26, siracusaShip 1/38, soloShip 0/4, suntShip 0/10, swiftShip 1/67, visiShip 1/9",12,25,13,101], - "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/10, siracusaShip 0/60, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",null,null,null,93] + "ai_1": [1,7,12,0,1,5,8,"3e54e2ff",151,"deputShip 0/16, kazimierzShip 0/7, preciShip 1/5, raidShip 1/26, siracusaShip 1/44, soloShip 0/4, suntShip 0/10, swiftShip 1/67, visiShip 1/9",14,25,11,101], + "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/13, siracusaShip 0/66, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",null,null,null,93] }, { "round": 12, "ai_0": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/4, deputShip 0/45, emptyShip 0/2, kazimierzShip 0/4, lateranoShip 0/36, skillfulShip 0/38, soloShip 0/8, steadShip 0/18, suntShip 0/2, swiftShip 0/23",null,null,null,91], - "ai_1": [0,0,15,0,0,6,0,"811c9dc5",0,"arcaneShip 0/14, deputShip 0/16, kazimierzShip 0/7, preciShip 0/10, raidShip 0/31, siracusaShip 0/42, soloShip 0/4, suntShip 0/10, swiftShip 0/72, visiShip 0/22",13,57,44,118], - "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/10, siracusaShip 0/60, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",null,null,null,93] + "ai_1": [0,0,15,0,0,6,0,"811c9dc5",0,"arcaneShip 0/14, deputShip 0/16, kazimierzShip 0/7, preciShip 0/10, raidShip 0/31, siracusaShip 0/48, soloShip 0/4, suntShip 0/10, swiftShip 0/72, visiShip 0/22",9,57,48,118], + "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/13, siracusaShip 0/66, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",null,null,null,93] } ], "players": { @@ -830,7 +830,7 @@ "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, "trophies": 2, "reward": 191, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1418962,"gold":91,"itemsEquipped":6,"kills":217,"leaks":31,"lpLost":23,"merges":2,"perfectRounds":4,"refreshes":11}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1387028,"gold":91,"itemsEquipped":6,"kills":207,"leaks":37,"lpLost":31,"merges":2,"perfectRounds":4,"refreshes":11}, "lineup": [], "bonds": ["arcaneShip 0/4","deputShip 0/45","emptyShip 0/2","kazimierzShip 0/4","lateranoShip 0/36","skillfulShip 0/38","soloShip 0/8","steadShip 0/18","suntShip 0/2","swiftShip 0/23"] }, @@ -844,9 +844,9 @@ "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, "trophies": 2, "reward": 255, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3289155,"gold":118,"itemsEquipped":13,"kills":279,"leaks":95,"lpLost":50,"merges":4,"perfectRounds":6,"refreshes":21}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3455640,"gold":118,"itemsEquipped":13,"kills":274,"leaks":95,"lpLost":48,"merges":4,"perfectRounds":6,"refreshes":21}, "lineup": [], - "bonds": ["arcaneShip 0/14","deputShip 0/16","kazimierzShip 0/7","preciShip 0/10","raidShip 0/31","siracusaShip 0/42","soloShip 0/4","suntShip 0/10","swiftShip 0/72","visiShip 0/22"] + "bonds": ["arcaneShip 0/14","deputShip 0/16","kazimierzShip 0/7","preciShip 0/10","raidShip 0/31","siracusaShip 0/48","soloShip 0/4","suntShip 0/10","swiftShip 0/72","visiShip 0/22"] }, "ai_2": { "alive": false, @@ -858,9 +858,9 @@ "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, "trophies": 2, "reward": 191, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1309827,"gold":93,"itemsEquipped":9,"kills":170,"leaks":60,"lpLost":28,"merges":4,"perfectRounds":3,"refreshes":10}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1358917,"gold":93,"itemsEquipped":9,"kills":168,"leaks":60,"lpLost":27,"merges":4,"perfectRounds":3,"refreshes":10}, "lineup": [], - "bonds": ["deputShip 0/4","indomShip 0/8","miraShip 0/10","siracusaShip 0/60","skillfulShip 0/4","soloShip 0/12","suntShip 0/4","swiftShip 0/14","yanShip 0/1"] + "bonds": ["deputShip 0/4","indomShip 0/8","miraShip 0/13","siracusaShip 0/66","skillfulShip 0/4","soloShip 0/12","suntShip 0/4","swiftShip 0/14","yanShip 0/1"] } } }, @@ -1075,49 +1075,49 @@ "round": 5, "ai_0": [1,39,9,0,0,3,8,"0e6eee6e",8,"arcaneShip 0/8, kazimierzShip 0/4, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/8",11,17,6,32], "ai_1": [1,18,9,0,0,3,8,"8d338f5b",14,"arcaneShip 1/4, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/4, sargonShip 0/4, soloShip 1/0, steadShip 1/2, swiftShip 1/8",9,17,8,32], - "ai_2": [1,20,9,0,0,3,8,"7b409933",22,"deputShip 1/10, kazimierzShip 0/2, kjeragShip 0/14, soloShip 1/4, steadShip 1/8, swiftShip 0/2, victoriaShip 0/12, yanShip 0/4",13,17,4,31], + "ai_2": [1,17,9,0,0,3,8,"87d5cdd2",22,"deputShip 1/10, kazimierzShip 0/2, kjeragShip 0/12, soloShip 1/4, steadShip 1/8, swiftShip 0/2, victoriaShip 0/12, yanShip 0/4",10,17,7,31], "ai_3": [1,14,9,0,0,3,8,"07fdf462",19,"arcaneShip 1/10, deputShip 1/4, preciShip 0/4, sargonShip 0/2, soloShip 1/5",4,17,13,33] }, { "round": 6, "ai_0": [1,39,9,0,0,3,8,"6f9e5953",31,"arcaneShip 1/11, indomShip 1/2, kazimierzShip 1/8, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",14,14,0,41], "ai_1": [1,18,9,0,0,3,8,"0d78d413",35,"arcaneShip 1/4, kazimierzShip 1/9, kjeragShip 0/2, preciShip 1/8, sargonShip 0/4, soloShip 1/0, steadShip 2/6, swiftShip 1/8",14,14,0,41], - "ai_2": [1,20,9,0,0,3,8,"d76ab210",59,"deputShip 1/12, kazimierzShip 0/4, kjeragShip 1/19, raidShip 1/14, soloShip 0/4, steadShip 2/14, swiftShip 0/2, victoriaShip 0/14, yanShip 0/4",14,14,0,41], + "ai_2": [1,17,9,0,0,3,8,"d76ab210",57,"deputShip 1/12, kazimierzShip 0/4, kjeragShip 1/17, raidShip 1/14, soloShip 0/4, steadShip 2/14, swiftShip 0/2, victoriaShip 0/14, yanShip 0/4",14,14,0,41], "ai_3": [1,14,10,0,0,3,8,"7a7888b4",50,"arcaneShip 1/13, deputShip 1/6, kjeragShip 1/9, preciShip 1/11, sargonShip 0/2, soloShip 1/7, steadShip 1/4",8,14,6,42] }, { "round": 7, "ai_0": [1,29,10,0,0,4,8,"3e2f54ac",46,"arcaneShip 1/16, indomShip 1/4, kazimierzShip 1/16, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",8,21,13,52], "ai_1": [1,8,10,0,0,4,8,"c08bbdb7",47,"arcaneShip 1/4, kazimierzShip 1/13, kjeragShip 0/2, preciShip 1/12, sargonShip 0/4, soloShip 1/0, steadShip 2/10, swiftShip 1/8",7,21,14,52], - "ai_2": [1,13,10,0,0,3,8,"910eaa54",102,"deputShip 2/16, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/31, raidShip 0/16, skillfulShip 0/2, soloShip 0/4, steadShip 1/27, suntShip 1/4, swiftShip 0/2, victoriaShip 1/24, yanShip 0/10",14,21,7,51], + "ai_2": [1,7,10,0,0,3,8,"2fd8f260",100,"deputShip 2/16, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/29, raidShip 0/16, skillfulShip 0/2, soloShip 0/4, steadShip 1/27, suntShip 1/4, swiftShip 0/2, victoriaShip 1/24, yanShip 0/10",10,21,11,51], "ai_3": [1,4,11,0,0,4,8,"849fa992",55,"arcaneShip 0/13, deputShip 1/8, kjeragShip 1/18, preciShip 1/13, sargonShip 0/2, skillfulShip 1/7, soloShip 1/9, steadShip 0/4",4,21,17,55] }, { "round": 8, "ai_0": [1,22,11,0,0,4,8,"7e077aff",46,"arcaneShip 2/16, indomShip 1/6, kazimierzShip 1/21, preciShip 0/8, soloShip 1/0, steadShip 1/3, suntShip 1/0, swiftShip 0/10",2,9,7,63], "ai_1": [0,0,11,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",1,9,8,64], - "ai_2": [1,6,11,0,0,4,8,"011c22b2",115,"deputShip 2/24, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/41, raidShip 0/16, skillfulShip 1/10, soloShip 0/4, steadShip 0/30, suntShip 1/8, swiftShip 0/4, victoriaShip 1/32, yanShip 0/10",2,9,7,63], + "ai_2": [1,2,11,0,0,4,8,"011c22b2",113,"deputShip 2/24, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/39, raidShip 0/16, skillfulShip 1/10, soloShip 0/4, steadShip 0/30, suntShip 1/8, swiftShip 0/4, victoriaShip 1/32, yanShip 0/10",4,9,5,63], "ai_3": [0,0,12,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",2,9,7,68] }, { "round": 9, "ai_0": [1,12,12,0,3,4,8,"26dd41f4",103,"arcaneShip 1/16, indomShip 1/18, kazimierzShip 1/31, preciShip 2/17, soloShip 1/0, steadShip 0/3, suntShip 1/0, swiftShip 1/21",7,19,12,78], "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",null,null,null,64], - "ai_2": [1,6,12,0,3,4,8,"4d786b3d",172,"deputShip 2/30, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/60, raidShip 0/16, skillfulShip 1/30, soloShip 0/4, steadShip 0/32, suntShip 1/12, swiftShip 0/6, victoriaShip 1/40, yanShip 0/12",19,19,0,75], + "ai_2": [1,2,12,0,3,4,8,"4d786b3d",170,"deputShip 2/30, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/58, raidShip 0/16, skillfulShip 1/30, soloShip 0/4, steadShip 0/32, suntShip 1/12, swiftShip 0/6, victoriaShip 1/40, yanShip 0/12",19,19,0,75], "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",null,null,null,68] }, { "round": 10, "ai_0": [1,2,15,0,0,5,8,"50e96914",136,"arcaneShip 1/16, indomShip 1/30, kazimierzShip 1/38, preciShip 2/26, soloShip 1/0, steadShip 0/3, suntShip 1/0, swiftShip 1/26",14,25,11,93], "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",null,null,null,64], - "ai_2": [1,6,15,0,0,5,8,"1e6aa30e",209,"deputShip 2/36, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/71, raidShip 0/16, skillfulShip 1/38, soloShip 0/4, steadShip 0/32, suntShip 1/16, swiftShip 0/6, victoriaShip 1/48, yanShip 0/14",25,25,0,89], + "ai_2": [1,2,15,0,0,5,8,"1e6aa30e",207,"deputShip 2/36, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/69, raidShip 0/16, skillfulShip 1/38, soloShip 0/4, steadShip 0/32, suntShip 1/16, swiftShip 0/6, victoriaShip 1/48, yanShip 0/14",25,25,0,89], "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",null,null,null,68] }, { "round": 11, "ai_0": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/22, indomShip 0/52, kazimierzShip 0/46, preciShip 0/39, steadShip 0/3, swiftShip 0/31",3,25,22,105], "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",null,null,null,64], - "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/40, egirShip 0/2, kazimierzShip 0/4, kjeragShip 0/83, raidShip 0/16, skillfulShip 0/50, soloShip 0/6, steadShip 0/40, suntShip 0/18, swiftShip 0/6, victoriaShip 0/56, yanShip 0/16",16,25,9,100], + "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/40, egirShip 0/2, kazimierzShip 0/4, kjeragShip 0/81, raidShip 0/16, skillfulShip 0/50, soloShip 0/6, steadShip 0/40, suntShip 0/18, swiftShip 0/6, victoriaShip 0/56, yanShip 0/16",16,25,9,100], "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",null,null,null,68] } ], @@ -1146,7 +1146,7 @@ "title": null, "trophies": 1, "reward": 149, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":915590,"gold":64,"itemsEquipped":5,"kills":67,"leaks":30,"lpLost":26,"merges":1,"perfectRounds":5,"refreshes":7}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":928888,"gold":64,"itemsEquipped":5,"kills":67,"leaks":30,"lpLost":26,"merges":1,"perfectRounds":5,"refreshes":7}, "lineup": [], "bonds": ["arcaneShip 0/4","kazimierzShip 0/17","kjeragShip 0/4","preciShip 0/21","sargonShip 0/4","steadShip 0/16","swiftShip 0/13"] }, @@ -1160,9 +1160,9 @@ "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, "trophies": 2, "reward": 213, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2885209,"gold":100,"itemsEquipped":12,"kills":137,"leaks":31,"lpLost":31,"merges":2,"perfectRounds":5,"refreshes":11}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2811692,"gold":100,"itemsEquipped":12,"kills":132,"leaks":36,"lpLost":35,"merges":2,"perfectRounds":5,"refreshes":11}, "lineup": [], - "bonds": ["deputShip 0/40","egirShip 0/2","kazimierzShip 0/4","kjeragShip 0/83","raidShip 0/16","skillfulShip 0/50","soloShip 0/6","steadShip 0/40","suntShip 0/18","swiftShip 0/6","victoriaShip 0/56","yanShip 0/16"] + "bonds": ["deputShip 0/40","egirShip 0/2","kazimierzShip 0/4","kjeragShip 0/81","raidShip 0/16","skillfulShip 0/50","soloShip 0/6","steadShip 0/40","suntShip 0/18","swiftShip 0/6","victoriaShip 0/56","yanShip 0/16"] }, "ai_3": { "alive": false, @@ -1251,7 +1251,7 @@ }, { "round": 12, - "ai_0": [1,19,12,0,0,6,8,"06501643",236,"arcaneShip 2/52, deputShip 1/18, indomShip 0/8, kjeragShip 0/2, miraShip 1/8, preciShip 0/4, siracusaShip 1/100, soloShip 1/30, steadShip 0/2, suntShip 1/8, victoriaShip 1/20, visiShip 0/8",29,47,18,119], + "ai_0": [1,19,12,0,0,6,8,"06501643",236,"arcaneShip 2/52, deputShip 1/18, indomShip 0/8, kjeragShip 0/2, miraShip 1/8, preciShip 0/4, siracusaShip 1/100, soloShip 1/30, steadShip 0/2, suntShip 1/8, victoriaShip 1/20, visiShip 0/8",28,47,19,119], "ai_1": [1,17,12,0,0,6,8,"de894d12",133,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/63, siracusaShip 1/42, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",46,46,0,108] }, { @@ -1271,7 +1271,7 @@ "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":285,"bossDamage":626538,"dmgDealt":3441841,"gold":142,"itemsEquipped":20,"kills":250,"leaks":43,"lpLost":21,"merges":4,"perfectRounds":10,"refreshes":23}, + "stats": {"activatedLayers":285,"bossDamage":626538,"dmgDealt":3452600,"gold":142,"itemsEquipped":20,"kills":249,"leaks":44,"lpLost":21,"merges":4,"perfectRounds":10,"refreshes":23}, "lineup": ["5_20_a@11,3[4_07_e_a+1_01_e_a]","5_12_a@11,6[6_09_e_a+3_08_e_b]","6_11_a@11,7","2_10_a@10,3[3_05_e_a+3_11_e_b]","4_10_a@10,7[4_09_e_a+1_05_e_a]","1_08_b@10,8[1_02_e_a+5_03_e_a]","4_11_b@9,8[4_08_e_a]","6_07_a@9,9[4_10_e_a+4_05_e_a]"], "bonds": ["arcaneShip 2/74","deputShip 0/18","indomShip 0/8","kjeragShip 0/2","maniShip 1/0","miraShip 1/12","preciShip 0/4","siracusaShip 1/132","soloShip 1/30","steadShip 0/2","suntShip 1/8","victoriaShip 1/29","visiShip 0/8"] }, @@ -1285,7 +1285,7 @@ "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":177,"bossDamage":723462,"dmgDealt":4024219,"gold":132,"itemsEquipped":21,"kills":257,"leaks":15,"lpLost":15,"merges":3,"perfectRounds":10,"refreshes":22}, + "stats": {"activatedLayers":177,"bossDamage":723462,"dmgDealt":4067908,"gold":132,"itemsEquipped":21,"kills":259,"leaks":15,"lpLost":15,"merges":3,"perfectRounds":10,"refreshes":22}, "lineup": ["3_20_a@12,3[4_03_e_a+5_01_e_a]","5_10_a@11,3[1_01_e_a+3_04_e_b]","5_02_a@11,6","1_17_a@11,7[5_07_e_a+1_02_e_b]","3_11_b@10,3[3_03_e_a+5_02_e_b]","1_08_b@10,8[3_06_e_a+4_11_e_a]","1_02_b@9,4[4_10_e_a+4_08_e_b]","6_18_a@9,9[3_11_e_a+5_02_e_a]"], "bonds": ["arcaneShip 0/4","indomShip 0/8","kjeragShip 1/28","preciShip 2/89","siracusaShip 1/60","soloShip 1/0","steadShip 0/8","suntShip 1/0","victoriaShip 0/8","visiShip 0/8"] } @@ -1385,8 +1385,8 @@ "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":641,"bossPoolLeft":0,"virtualMs":124650}, - "rng": {"setup":25,"shop":667,"waves":0,"draft":61,"bots":22075,"meta":140}, + "end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":408,"bossPoolLeft":0,"virtualMs":123150}, + "rng": {"setup":25,"shop":681,"waves":0,"draft":61,"bots":22075,"meta":140}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1447,12 +1447,12 @@ { "round": 12, "ai_0": [1,400,14,0,0,6,8,"9229fb84",2208,"arcaneShip 0/314, deputShip 1/308, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/321, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/319, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/317, soloShip 1/306, steadShip 1/331, suntShip 1/306, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",14,14,0,115], - "ai_1": [1,400,15,0,0,6,8,"ec840519",1917,"arcaneShip 1/310, deputShip 0/310, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/363, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 1/320, suntShip 0/300, swiftShip 0/300, victoriaShip 1/316, visiShip 0/300, yanShip 0/300",9,16,7,119] + "ai_1": [1,400,15,0,2,6,8,"305c3338",1917,"arcaneShip 1/310, deputShip 0/310, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/362, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 1/321, suntShip 0/300, swiftShip 0/300, victoriaShip 1/316, visiShip 0/300, yanShip 0/300",16,16,0,119] }, { "round": 13, "ai_0": [1,400,12,0,2,6,8,"31644d65",2842,"arcaneShip 0/314, deputShip 1/313, egirShip 0/300, emptyShip 1/302, indomShip 1/302, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/331, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/326, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/319, soloShip 0/306, steadShip 1/339, suntShip 1/308, swiftShip 0/302, victoriaShip 0/300, visiShip 1/302, yanShip 0/300",41,41,0,149], - "ai_1": [1,400,12,0,4,6,8,"10ed5f25",2256,"arcaneShip 1/312, deputShip 1/312, egirShip 0/300, emptyShip 0/300, indomShip 1/302, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/389, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/302, soloShip 1/310, steadShip 1/329, suntShip 0/300, swiftShip 0/300, victoriaShip 0/316, visiShip 0/300, yanShip 0/300",43,43,0,133] + "ai_1": [1,400,14,0,4,6,8,"10ed5f25",2256,"arcaneShip 1/312, deputShip 1/312, egirShip 0/300, emptyShip 0/300, indomShip 1/302, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/388, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/302, soloShip 1/310, steadShip 1/330, suntShip 0/300, swiftShip 0/300, victoriaShip 0/316, visiShip 0/300, yanShip 0/300",43,43,0,135] } ], "players": { @@ -1461,33 +1461,33 @@ "victory": true, "roundsPassed": 15, "eliminatedRound": null, - "lp": 321, + "lp": 204, "bandId": "band_sarkazb", "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 8, "reward": 425, - "stats": {"activatedLayers":2606,"bossDamage":6895804,"dmgDealt":22127730,"gold":175,"itemsEquipped":24,"kills":294,"leaks":0,"lpLost":0,"merges":6,"perfectRounds":13,"refreshes":31}, - "lineup": ["1_02_b@12,8[6_04_e_a+1_02_e_b]","3_16_b@11,7[4_02_e_b+5_01_e_a]","3_11_b@11,8[3_01_e_a+1_01_e_b]","5_11_a@10,4[3_08_e_b]","5_06_a@10,8[2_01_e_a+4_11_e_a]","4_21_b@9,7","6_12_a@9,8[1_05_e_a+3_04_e_a]","3_20_a@9,9[5_02_e_a+3_07_e_a]"], - "bonds": ["arcaneShip 0/314","deputShip 1/317","egirShip 0/300","emptyShip 0/304","indomShip 1/306","investShip 0/300","kazimierzShip 0/302","kjeragShip 1/345","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/340","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/323","soloShip 1/308","steadShip 2/355","suntShip 1/312","swiftShip 0/302","victoriaShip 0/300","visiShip 0/304","yanShip 0/300"] + "stats": {"activatedLayers":2908,"bossDamage":4593567,"dmgDealt":16196818,"gold":176,"itemsEquipped":24,"kills":287,"leaks":0,"lpLost":0,"merges":6,"perfectRounds":13,"refreshes":31}, + "lineup": ["3_16_b@12,8[4_02_e_b+5_01_e_a]","3_11_b@11,8[3_01_e_a+1_01_e_b]","3_20_a@10,3[5_02_e_a+3_07_e_a]","1_02_b@10,4[1_02_e_a+4_05_e_a]","5_06_a@10,8[4_04_e_a+3_04_e_b]","4_21_b@9,7","6_12_a@9,8[3_08_e_a+1_05_e_a]","5_10_a@9,9[2_01_e_a+4_06_e_a]"], + "bonds": ["arcaneShip 0/314","deputShip 1/317","egirShip 0/300","emptyShip 1/306","indomShip 1/306","investShip 0/300","kazimierzShip 0/302","kjeragShip 1/345","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/340","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/323","soloShip 0/306","steadShip 1/353","suntShip 1/312","swiftShip 0/302","victoriaShip 0/300","visiShip 1/306","yanShip 0/300"] }, "ai_1": { "alive": true, "victory": true, "roundsPassed": 15, "eliminatedRound": null, - "lp": 320, + "lp": 204, "bandId": "band_doberm", "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 8, "reward": 425, - "stats": {"activatedLayers":2575,"bossDamage":6983028,"dmgDealt":20341827,"gold":159,"itemsEquipped":35,"kills":270,"leaks":8,"lpLost":0,"merges":5,"perfectRounds":11,"refreshes":34}, - "lineup": ["4_21_a@12,3[5_03_e_a+3_06_e_a]","2_10_b@11,5[2_03_e_a+2_03_e_b]","5_06_a@11,8[3_09_e_b+3_09_e_b]","5_02_a@10,3[3_06_e_b+2_03_e_b]","2_17_a@10,4[5_09_e_a+1_05_e_a]","4_07_a@10,8[3_09_e_a+5_02_e_b]","1_17_a@9,8[4_09_e_b+3_10_e_b]","1_06_b@9,9[3_02_e_a+4_09_e_b]"], - "bonds": ["arcaneShip 1/316","deputShip 1/316","egirShip 0/300","emptyShip 0/300","indomShip 1/306","investShip 0/300","kazimierzShip 0/300","kjeragShip 0/300","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/393","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/306","soloShip 1/314","steadShip 0/337","suntShip 1/304","swiftShip 0/300","victoriaShip 1/320","visiShip 0/300","yanShip 0/300"] + "stats": {"activatedLayers":2599,"bossDamage":9288137,"dmgDealt":29429364,"gold":160,"itemsEquipped":32,"kills":277,"leaks":1,"lpLost":0,"merges":5,"perfectRounds":12,"refreshes":36}, + "lineup": ["3_16_a@12,8[3_03_e_b+4_04_e_b]","2_10_b@11,5[2_03_e_a+2_03_e_b]","1_06_b@11,8[3_02_e_a+4_09_e_b]","2_17_a@10,4[5_02_e_a+5_09_e_a]","5_06_a@10,8[3_09_e_b+3_09_e_b]","4_21_a@9,6[5_03_e_a+3_06_e_a]","5_02_a@9,8[3_06_e_b+2_03_e_b]","1_17_a@9,9[4_09_e_b+3_10_e_b]"], + "bonds": ["arcaneShip 1/316","deputShip 1/316","egirShip 0/300","emptyShip 0/300","indomShip 1/306","investShip 0/300","kazimierzShip 0/300","kjeragShip 0/300","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/392","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/306","soloShip 1/314","steadShip 1/345","suntShip 1/304","swiftShip 0/300","victoriaShip 0/316","visiShip 0/300","yanShip 0/300"] } } }, "coop2-NORMAL-14-standins": { - "about": "coop NORMAL, 1 bot seat(s) + 1 human seat (AI 托管) without 7 operators (补位 stand-ins), seed 14", + "about": "coop NORMAL, 1 bot seat(s) + 1 human seat (AI 托管) without 9 operators (补位 stand-ins), seed 14", "fast": true, "mode": "coop", "difficulty": "NORMAL", @@ -1496,8 +1496,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":"boss_10","factions":["DOT","INVISIBLE","REFLECTION"]}, - "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":18,"bossPoolLeft":0,"virtualMs":116900}, - "rng": {"setup":25,"shop":542,"waves":0,"draft":34,"bots":18059,"meta":26}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":9,"bossPoolLeft":0,"virtualMs":119550}, + "rng": {"setup":25,"shop":546,"waves":0,"draft":34,"bots":17800,"meta":29}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1542,62 +1542,62 @@ }, { "round": 9, - "h_0": [1,28,12,0,0,4,8,"f78a00e2",66,"arcaneShip 1/10, kazimierzShip 0/8, preciShip 1/14, siracusaShip 1/18, suntShip 1/0, swiftShip 1/24, yanShip 0/13",23,23,0,76], - "ai_0": [1,23,12,0,0,4,8,"ae419bd2",132,"arcaneShip 0/2, deputShip 1/14, kazimierzShip 0/8, lateranoShip 1/11, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/24, skillfulShip 1/34, suntShip 1/8, swiftShip 1/41",23,23,0,75] + "h_0": [1,23,12,0,0,4,8,"f78a00e2",66,"arcaneShip 1/10, kazimierzShip 0/8, preciShip 1/14, siracusaShip 1/18, suntShip 1/0, swiftShip 1/24, yanShip 0/13",18,23,5,76], + "ai_0": [1,23,12,0,0,4,8,"d94d3c79",142,"arcaneShip 0/2, deputShip 1/14, kazimierzShip 0/8, lateranoShip 1/21, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/24, skillfulShip 1/34, suntShip 1/8, swiftShip 1/41",23,23,0,75] }, { "round": 10, - "h_0": [1,28,12,0,0,5,8,"57b54089",79,"arcaneShip 1/13, kazimierzShip 0/8, preciShip 1/19, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/29, yanShip 0/13",31,31,0,88], - "ai_0": [1,23,12,0,1,5,8,"82fa7de0",166,"arcaneShip 0/2, deputShip 1/22, kazimierzShip 0/8, lateranoShip 1/19, miraShip 1/2, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/26, skillfulShip 1/42, suntShip 1/10, swiftShip 1/45",31,31,0,87] + "h_0": [1,22,12,0,0,5,8,"57b54089",79,"arcaneShip 1/13, kazimierzShip 0/8, preciShip 1/19, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/29, yanShip 0/13",30,31,1,88], + "ai_0": [1,23,12,0,1,5,8,"82fa7de0",176,"arcaneShip 0/2, deputShip 1/22, kazimierzShip 0/8, lateranoShip 1/29, miraShip 1/2, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/26, skillfulShip 1/42, suntShip 1/10, swiftShip 1/45",31,31,0,87] }, { "round": 11, - "h_0": [1,24,12,0,2,5,8,"def68119",93,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/26, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/34, yanShip 0/13",21,25,4,102], - "ai_0": [1,23,13,0,0,5,8,"c0b11e3b",214,"arcaneShip 0/2, deputShip 1/30, kazimierzShip 0/8, lateranoShip 1/29, miraShip 1/4, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/40, skillfulShip 1/52, suntShip 1/12, swiftShip 1/47",25,25,0,98] + "h_0": [1,18,12,0,2,5,8,"def68119",93,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/26, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/34, yanShip 0/13",21,25,4,102], + "ai_0": [1,23,13,0,0,5,8,"c0b11e3b",224,"arcaneShip 0/2, deputShip 1/30, kazimierzShip 0/8, lateranoShip 1/39, miraShip 1/4, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/40, skillfulShip 1/52, suntShip 1/12, swiftShip 1/47",25,25,0,98] }, { "round": 12, - "h_0": [1,16,14,0,0,6,8,"0dd59e58",103,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/31, siracusaShip 1/18, suntShip 1/0, swiftShip 1/39, yanShip 0/13",23,31,8,117], - "ai_0": [1,22,12,0,1,6,8,"defd2806",250,"arcaneShip 0/2, deputShip 1/32, kazimierzShip 0/8, lateranoShip 1/45, miraShip 1/6, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/42, skillfulShip 1/62, suntShip 1/14, swiftShip 1/49",30,31,1,112] + "h_0": [1,8,14,0,0,6,8,"13e7bef4",103,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/31, siracusaShip 1/18, suntShip 1/0, swiftShip 1/39, yanShip 0/13",18,31,13,117], + "ai_0": [1,22,12,0,1,6,8,"defd2806",262,"arcaneShip 0/2, deputShip 1/32, kazimierzShip 0/8, lateranoShip 1/57, miraShip 1/6, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/42, skillfulShip 1/62, suntShip 1/14, swiftShip 1/49",30,31,1,112] }, { "round": 13, - "h_0": [1,6,12,0,0,6,8,"66816c7c",157,"arcaneShip 1/19, emptyShip 1/0, kazimierzShip 0/8, miraShip 1/0, preciShip 2/40, raidShip 1/24, siracusaShip 1/30, suntShip 1/0, swiftShip 1/44, yanShip 0/13",18,57,39,129], - "ai_0": [1,12,13,0,2,6,8,"e3ba3267",252,"arcaneShip 0/2, deputShip 0/32, kazimierzShip 0/8, lateranoShip 1/57, miraShip 1/6, preciShip 0/2, raidShip 1/2, sargonShip 0/2, siracusaShip 1/54, skillfulShip 1/70, suntShip 1/14, swiftShip 1/49",17,57,40,126] + "h_0": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/21, kazimierzShip 0/8, preciShip 0/42, raidShip 0/16, siracusaShip 0/30, swiftShip 0/44, yanShip 0/13",17,57,40,129], + "ai_0": [1,12,13,0,2,6,8,"e3ba3267",264,"arcaneShip 0/2, deputShip 0/32, kazimierzShip 0/8, lateranoShip 1/71, miraShip 1/6, preciShip 0/2, raidShip 1/2, sargonShip 0/2, siracusaShip 1/54, skillfulShip 1/68, suntShip 1/14, swiftShip 1/49",17,57,40,126] } ], "players": { "h_0": { - "alive": true, - "victory": true, - "roundsPassed": 14, - "eliminatedRound": null, - "lp": 6, + "alive": false, + "victory": false, + "roundsPassed": 12, + "eliminatedRound": 13, + "lp": 0, "bandId": "band_amedic", - "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, - "trophies": 4, - "reward": 400, - "stats": {"activatedLayers":133,"bossDamage":471435,"dmgDealt":2876512,"gold":139,"itemsEquipped":18,"kills":270,"leaks":51,"lpLost":22,"merges":5,"perfectRounds":10,"refreshes":23}, - "lineup": ["6_16_a@12,3","5_10_a@12,7[1_02_e_b+6_01_e_a]","5_20_a@11,3[1_01_e_a+3_11_e_a]","4_14_b@11,8[3_05_e_a+1_02_e_b]","6_12_a@10,3[4_03_e_a+2_01_e_a]","5_07_a@10,7","4_16_a@10,8[4_03_e_b+3_07_e_a]","1_17_b@9,8"], - "bonds": ["arcaneShip 2/25","emptyShip 1/0","kazimierzShip 0/8","miraShip 1/0","preciShip 2/50","raidShip 1/24","siracusaShip 1/34","suntShip 1/0","swiftShip 0/44","yanShip 0/13"] + "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, + "trophies": 3, + "reward": 280, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2083752,"gold":129,"itemsEquipped":15,"kills":231,"leaks":63,"lpLost":30,"merges":4,"perfectRounds":8,"refreshes":22}, + "lineup": [], + "bonds": ["arcaneShip 0/21","kazimierzShip 0/8","preciShip 0/42","raidShip 0/16","siracusaShip 0/30","swiftShip 0/44","yanShip 0/13"] }, "ai_0": { "alive": true, "victory": true, "roundsPassed": 14, "eliminatedRound": null, - "lp": 12, + "lp": 9, "bandId": "band_chiave", - "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, + "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":299,"bossDamage":308565,"dmgDealt":2856973,"gold":139,"itemsEquipped":20,"kills":253,"leaks":41,"lpLost":11,"merges":8,"perfectRounds":11,"refreshes":24}, - "lineup": ["3_21_b@12,3[3_01_e_a+1_05_e_b]","5_20_a@12,7","3_01_b@11,3[1_01_e_a+6_05_e_a]","1_13_b@11,8","4_10_b@10,3[2_01_e_a+3_08_e_a]","4_16_a@10,7[4_11_e_a+4_02_e_a]","6_17_a@10,8[4_04_e_a]","6_01_a@9,8","4_21_a@9,9"], - "bonds": ["arcaneShip 0/2","deputShip 1/34","kazimierzShip 0/8","lateranoShip 1/71","miraShip 1/14","preciShip 0/2","raidShip 1/4","sargonShip 0/2","siracusaShip 1/80","skillfulShip 1/80","suntShip 1/16","swiftShip 0/49"] + "stats": {"activatedLayers":336,"bossDamage":390000,"dmgDealt":2919224,"gold":141,"itemsEquipped":23,"kills":258,"leaks":41,"lpLost":11,"merges":8,"perfectRounds":11,"refreshes":27}, + "lineup": ["4_21_a@12,3","5_20_a@12,7","3_21_b@11,3[1_05_e_b+3_01_e_b]","6_05_a@11,8","3_01_b@10,3[1_01_e_a+6_05_e_a]","3_18_b@10,7[4_02_e_a+4_11_e_a]","4_16_b@10,8[4_10_e_a+4_04_e_a]","6_01_a@9,8[2_01_e_a+1_02_e_a]"], + "bonds": ["arcaneShip 0/2","deputShip 0/32","kazimierzShip 0/8","lateranoShip 1/83","miraShip 1/12","preciShip 1/2","raidShip 0/2","sargonShip 0/2","siracusaShip 1/84","skillfulShip 1/74","suntShip 1/14","swiftShip 1/67"] } }, "standIns": { - "notOwned": ["chess_char_3_12_a","chess_char_3_18_a","chess_char_5_20_a","chess_char_6_05_a","chess_char_6_06_a","chess_char_6_17_a","chess_char_6_19_a"], + "notOwned": ["chess_char_3_12_a","chess_char_3_18_a","chess_char_4_16_a","chess_char_5_10_a","chess_char_5_20_a","chess_char_6_05_a","chess_char_6_06_a","chess_char_6_17_a","chess_char_6_19_a"], "rounds": [ [1,""], [2,""], @@ -1605,13 +1605,13 @@ [4,"3_18_a→char_609_acguad"], [5,"3_18_a→char_609_acguad"], [6,"3_18_a→char_609_acguad"], - [7,"3_18_a→char_609_acguad"], - [8,"3_18_a→char_609_acguad"], - [9,"3_18_a→char_609_acguad"], - [10,""], - [11,""], - [12,"5_20_a→char_614_acsupo"], - [13,"5_20_a→char_614_acsupo"] + [7,"3_18_a→char_609_acguad 4_16_a→char_615_acspec"], + [8,"3_18_a→char_609_acguad 4_16_a→char_615_acspec"], + [9,"3_18_a→char_609_acguad 4_16_a→char_615_acspec 5_10_a→char_606_csuppo"], + [10,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo"], + [11,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo"], + [12,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo 5_20_a→char_614_acsupo"], + [13,""] ] } }, diff --git a/test/golden/roster.json b/test/golden/roster.json index c76666f8..8d0a089a 100644 --- a/test/golden/roster.json +++ b/test/golden/roster.json @@ -24,47 +24,47 @@ "total": 31, "unspawned": 0, "bossHpLeft": null, - "rngDraws": 62, + "rngDraws": 72, "errors": {"count":0,"kinds":[]}, - "players": {"p1":{"killed":26,"total":31,"perfect":false,"leaks":5,"leaksUncounted":0,"coins":8,"dmg":176632,"bossDmg":0,"heal":10701,"deaths":6,"layerGains":{}}}, + "players": {"p1":{"killed":26,"total":31,"perfect":false,"leaks":5,"leaksUncounted":0,"coins":8,"dmg":181403,"bossDmg":0,"heal":10137,"deaths":7,"layerGains":{}}}, "units": [ - ["p1",1,"op","chess_char_1_02_a",1,null,2612.09,1,7,1,1297,1013,0,5,1], - ["p1",2,"op","chess_char_1_04_a",1,null,1510.93,1,9,4,21666,2739,0,47,2], - ["p1",3,"op","chess_char_1_10_a",0,null,694.51,1,48,0,3770,2556,2554,26,13], - ["p1",4,"op","chess_char_1_08_a",1,null,0,0,12,4,28089,10533,0,27,2], - ["p1",5,"op","chess_char_1_12_a",1,null,0,0,15,3,16534,3912,966,22,1], - ["p1",6,"op","chess_char_1_01_a",1,null,1403.75,1,4,3,13930,0,0,33,1], + ["p1",1,"op","chess_char_1_02_a",1,null,2390.18,1,7,0,1839,1385,0,7,1], + ["p1",2,"op","chess_char_1_04_a",1,null,1344.28,1,9,4,20279,3055,0,46,2], + ["p1",3,"op","chess_char_1_10_a",0,null,0,0,48,1,3521,3821,2270,26,12], + ["p1",4,"op","chess_char_1_08_a",1,null,0,0,12,3,12740,6944,0,19,1], + ["p1",5,"op","chess_char_1_12_a",1,null,0,0,15,3,17546,4866,966,23,1], + ["p1",6,"op","chess_char_1_01_a",1,null,1403.75,1,4,4,14393,0,0,38,1], ["p1",7,"op","chess_char_1_03_a",1,null,964.63,1,5,0,0,898,0,0,0], - ["p1",8,"op","chess_char_1_06_a",1,null,0,0,11,1,13752,2346,4326,34,1], - ["p1",9,"op","chess_char_1_07_a",0,null,1518.75,1,50,3,12942,2010,0,21,0], - ["p1",10,"op","chess_char_1_09_a",1,null,2152.5,1,6,0,26727,5400,0,24,1], - ["p1",11,"op","chess_char_1_13_a",1,null,1052.15,1,49,1,8761,2284,0,19,1], - ["p1",12,"op","chess_char_1_14_a",1,null,1058.43,1,8,6,29164,2127,0,15,1], + ["p1",8,"op","chess_char_1_06_a",1,null,0,0,11,0,10358,1862,3851,26,1], + ["p1",9,"op","chess_char_1_07_a",0,null,0,0,13,4,15215,3447,0,26,0], + ["p1",10,"op","chess_char_1_09_a",1,null,2092.5,1,6,0,30700,5400,0,27,1], + ["p1",11,"op","chess_char_1_13_a",1,null,224.95,1,49,1,8761,3112,0,19,1], + ["p1",12,"op","chess_char_1_14_a",1,null,1431.25,1,8,6,46053,1959,0,17,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], - ["p1","~1","token","char_605_cmedic",null,null,1202,1,16,0,0,0,2855,32,2] + ["p1","~1","token","char_605_cmedic",null,null,1202,1,16,0,0,0,3050,32,2] ], "enemies": { "enemy_10001_trslim": [1,1,0,9,4320], - "enemy_1005_yokai_3": [1,1,0,5292,28800], - "enemy_1010_demon_2": [1,1,0,5814,14400], + "enemy_1005_yokai_3": [1,1,0,5096,28800], + "enemy_1010_demon_2": [1,1,0,4103,14400], "enemy_1026_aghost": [1,1,0,0,3648], - "enemy_1112_emppnt_2": [1,0,0,0,6643], - "enemy_1116_liprr": [18,17,0,296,91776], - "enemy_1168_dumage": [1,1,0,1808,4320], + "enemy_1112_emppnt_2": [1,0,0,0,9687], + "enemy_1116_liprr": [18,17,0,394,92338], + "enemy_1168_dumage": [1,1,0,2257,4320], "enemy_1198_msfshu_2": [1,1,0,0,4], - "enemy_1229_darmy_2": [1,1,0,561,5280], - "enemy_1288_duskls": [1,1,0,396,4805], - "enemy_1381_winman": [1,1,0,13,5568], - "enemy_1500_skulsr": [1,0,0,845,2870], - "enemy_2004_balloon": [1,0,0,18,3544], + "enemy_1229_darmy_2": [1,1,0,1003,5280], + "enemy_1288_duskls": [1,1,0,320,4805], + "enemy_1381_winman": [1,1,0,26,5568], + "enemy_1500_skulsr": [1,0,0,967,3069], + "enemy_2004_balloon": [1,0,0,0,4510], "enemy_9010_acpupp": [1,0,0,0,654] }, - "hooks": [1,50,2700,396,396,681,682,329,32,32,26,23,16,15196,105,33,31,0,1,33,0,0,0], - "events": {"atk":440,"bounty":4,"deploy":16,"die":32,"dmg":682,"engage":11,"fx":69,"heal":321,"skill":52,"spawn":47,"status":123}, - "fx": {"aoe":2,"bombard":8,"bombardShell":9,"counter":6,"crit":19,"dp":2,"ember":1,"explode":4,"heal":12,"phase":2,"summon":1,"taunt":1,"telegraph":1,"zone":1}, - "snaps": "15544384 8d3c7635 48c925ab 4c3fe49b cd3cfaa1 c157b224 2c9ae13b 3a55280f 20b3165f" + "hooks": [1,49,2700,400,400,686,689,317,33,33,24,22,16,14640,96,34,31,0,1,34,0,0,0], + "events": {"atk":445,"bounty":4,"deploy":15,"die":33,"dmg":689,"engage":11,"fx":72,"heal":313,"skill":47,"spawn":47,"status":114}, + "fx": {"aoe":1,"bombard":9,"bombardShell":9,"counter":6,"crit":21,"dp":1,"ember":1,"explode":3,"heal":12,"liberate":1,"phase":4,"summon":1,"taunt":1,"telegraph":1,"zone":1}, + "snaps": "15544384 8d3c7635 63b43dc6 a69603f8 1d386a8e 71ff06ab db0d25d7 bb18b412 736e5fc7" }, "roster-002": { "about": "pass 0 band 2: chess_char_1_02_b chess_char_1_20_a chess_char_1_04_b chess_char_1_08_b chess_char_1_18_a chess_char_1_19_a chess_char_1_17_a chess_char_1_01_b chess_char_1_03_b chess_char_1_06_b chess_char_1_07_b chess_char_1_09_b", @@ -78,50 +78,50 @@ "time": 110, "ticks": 3300, "reason": "timeout", - "killed": 28, + "killed": 29, "total": 44, "unspawned": 0, "bossHpLeft": null, - "rngDraws": 79, + "rngDraws": 88, "errors": {"count":0,"kinds":[]}, - "players": {"p1":{"killed":28,"total":44,"perfect":false,"leaks":16,"leaksUncounted":0,"coins":8,"dmg":224475,"bossDmg":0,"heal":1929,"deaths":15,"layerGains":{}}}, + "players": {"p1":{"killed":29,"total":44,"perfect":false,"leaks":15,"leaksUncounted":0,"coins":8,"dmg":244258,"bossDmg":0,"heal":5475,"deaths":15,"layerGains":{}}}, "units": [ - ["p1",1,"op","chess_char_1_02_b",1,"uniequip_002_yak",0,0,3,2,7577,4874,0,22,1], + ["p1",1,"op","chess_char_1_02_b",1,"uniequip_002_yak",0,0,3,2,7539,4874,0,22,1], ["p1",2,"op","chess_char_1_20_a",1,null,2857.4,1,1,0,0,0,0,0,0], - ["p1",3,"op","chess_char_1_04_b",1,"uniequip_002_udflow",1414.45,1,4,1,6366,2361,0,8,1], - ["p1",4,"op","chess_char_1_08_b",1,"uniequip_002_texas",0,0,7,0,6483,3473,0,2,2], - ["p1",5,"op","chess_char_1_18_a",1,null,0,0,61,2,3473,6928,350,7,2], + ["p1",3,"op","chess_char_1_04_b",1,"uniequip_002_udflow",2356.31,1,4,1,4860,1419,0,7,1], + ["p1",4,"op","chess_char_1_08_b",1,"uniequip_002_texas",0,0,7,1,10182,3473,0,1,3], + ["p1",5,"op","chess_char_1_18_a",1,null,0,0,60,2,3473,6928,350,7,2], ["p1",6,"op","chess_char_1_19_a",1,null,0,0,57,0,7018,3853,0,10,1], ["p1",7,"op","chess_char_1_17_a",1,null,0,0,5,0,0,1481,0,0,0], - ["p1",8,"op","chess_char_1_01_b",1,"uniequip_002_inside",0,0,8,4,51848,2235,0,55,3], - ["p1",9,"op","chess_char_1_03_b",1,"uniequip_002_leizi",770.4,1,62,4,30306,2846,0,14,1], - ["p1",10,"op","chess_char_1_06_b",1,"uniequip_002_vendla",0,0,60,5,22230,4837,1579,25,2], - ["p1",11,"op","chess_char_1_07_b",0,"uniequip_002_prove",0,0,59,5,36636,5955,0,25,0], - ["p1",12,"op","chess_char_1_09_b",1,"uniequip_002_caper",0,0,2,5,52537,2880,0,41,2] + ["p1",8,"op","chess_char_1_01_b",1,"uniequip_002_inside",0,0,8,5,59193,4079,0,62,3], + ["p1",9,"op","chess_char_1_03_b",1,"uniequip_002_leizi",0,0,62,4,28255,3617,0,14,1], + ["p1",10,"op","chess_char_1_06_b",1,"uniequip_002_vendla",0,0,61,6,24730,6539,5125,31,2], + ["p1",11,"op","chess_char_1_07_b",0,"uniequip_002_prove",0,0,59,5,35730,5955,0,27,0], + ["p1",12,"op","chess_char_1_09_b",1,"uniequip_002_caper",801.74,1,2,3,63277,2078,0,44,2] ], "enemies": { "enemy_1000_gopro_2": [1,1,0,0,2938], "enemy_10065_ftzlc": [1,1,0,0,6566], "enemy_10112_ymgds": [1,1,0,0,8640], - "enemy_1019_jshoot": [9,3,5,13597,11049], + "enemy_1019_jshoot": [9,3,5,13909,12884], "enemy_1040_bombd": [1,1,0,0,6912], - "enemy_1043_zomsbr": [18,16,2,262,82375], + "enemy_1043_zomsbr": [18,17,1,482,83466], "enemy_1116_liprr": [1,1,0,0,9504], "enemy_1168_dumage_2": [1,1,0,2185,11232], "enemy_1199_sfjin": [1,1,0,0,11232], - "enemy_1200_msfjin": [1,0,1,0,13], - "enemy_1234_dsubrl": [1,0,1,4676,11087], + "enemy_1200_msfjin": [1,0,1,0,18], + "enemy_1234_dsubrl": [1,0,1,6225,12848], "enemy_1288_duskls_2": [1,0,1,0,10368], "enemy_1299_ymkilr": [3,1,2,3672,14688], "enemy_1381_winman_2": [1,1,0,0,13478], - "enemy_1501_demonk": [1,0,0,8061,12259], - "enemy_2005_axetro": [1,0,0,5980,10350], - "enemy_9011_acrefr": [1,0,0,0,1783] + "enemy_1501_demonk": [1,0,0,6100,12225], + "enemy_2005_axetro": [1,0,0,8433,23361], + "enemy_9011_acrefr": [1,0,0,0,3898] }, - "hooks": [1,62,3300,343,343,409,439,3216,43,55,15,18,41,6999,32,10,44,12,1,44,0,0,0], - "events": {"atk":364,"bounty":4,"deploy":18,"die":43,"dmg":439,"engage":10,"fx":64,"heal":17,"leak":12,"skill":36,"spawn":56,"status":64}, - "fx": {"aoe":4,"counter":3,"crit":20,"displace":10,"dp":2,"ember":1,"pulse":19,"revive":1,"sp":1,"summon":1,"taunt":2}, - "snaps": "ccf62e58 0169998c b859efed 32758178 83e51a60 50ce6b41 f75af7ee 51da4e45 7cbeae65 6cf12cc4 68ebbf32" + "hooks": [1,62,3300,365,365,452,493,3061,44,55,16,18,48,7405,32,11,44,11,1,45,0,0,0], + "events": {"atk":384,"bounty":4,"deploy":18,"die":44,"dmg":493,"engage":10,"fx":79,"heal":32,"leak":11,"skill":37,"spawn":56,"status":64}, + "fx": {"aoe":5,"counter":9,"crit":24,"displace":10,"dp":3,"ember":1,"pulse":22,"revive":1,"sp":1,"summon":1,"taunt":2}, + "snaps": "ccf62e58 0169998c b859efed 32758178 83e51a60 5ea49804 e568e9f3 ccd82ab0 6deb6218 87da45df 9c7b415f" }, "roster-003": { "about": "pass 0 band 2: chess_char_1_10_b chess_char_1_12_b chess_char_1_20_b chess_char_1_18_b chess_char_1_19_b chess_char_1_13_b chess_char_1_14_b chess_char_1_17_b chess_char_2_01_a chess_char_2_02_a chess_char_2_04_a chess_char_2_05_a token_10000_silent_healrb", @@ -142,22 +142,22 @@ "rngDraws": 61, "errors": {"count":0,"kinds":[]}, "players": { - "p1": {"killed":82,"total":85,"perfect":false,"leaks":3,"leaksUncounted":0,"coins":12,"dmg":120882,"bossDmg":0,"heal":19617,"deaths":4,"layerGains":{"preciShip":3}} + "p1": {"killed":82,"total":85,"perfect":false,"leaks":3,"leaksUncounted":0,"coins":12,"dmg":120882,"bossDmg":0,"heal":19883,"deaths":4,"layerGains":{"preciShip":3}} }, "units": [ ["p1",1,"op","chess_char_1_10_b",0,"uniequip_002_sunbr",2493,1,5,0,0,0,974,5,5], ["p1",2,"op","chess_char_1_12_b",1,"uniequip_002_estell",2389,1,7,7,2525,974,0,25,1], - ["p1",3,"op","chess_char_1_20_b",1,"uniequip_002_liskam",1341.87,1,10,13,22082,1550,0,71,1], + ["p1",3,"op","chess_char_1_20_b",1,"uniequip_002_liskam",1526.37,1,10,13,22082,1550,0,71,1], ["p1",4,"op","chess_char_1_18_b",1,"uniequip_002_utage",0,0,11,9,22181,9241,5887,85,1], - ["p1",5,"op","chess_char_1_19_b",1,"uniequip_002_wildmn",1173.24,1,14,7,45433,9423,0,88,2], + ["p1",5,"op","chess_char_1_19_b",1,"uniequip_002_wildmn",1116.24,1,14,7,45433,9668,0,88,2], ["p1",6,"op","chess_char_1_13_b",1,"uniequip_002_podego",2446.5,1,8,5,1001,0,0,8,3], ["p1",7,"op","chess_char_1_14_b",1,"uniequip_002_greyy",0,0,12,8,2627,1505,0,14,1], ["p1",8,"op","chess_char_1_17_b",1,"uniequip_002_indigo",0,0,15,7,9634,1613,0,9,1], ["p1",9,"op","chess_char_2_01_a",1,null,1694,1,4,11,5662,0,0,22,1], - ["p1",10,"op","chess_char_2_02_a",1,null,1400,1,6,0,0,0,7356,38,4], + ["p1",10,"op","chess_char_2_02_a",1,null,1400,1,6,0,0,0,7016,38,4], ["p1",11,"op","chess_char_2_04_a",1,null,977,1,9,15,9737,0,0,57,3], - ["p1",12,"op","chess_char_2_05_a",1,null,0,0,13,0,0,5284,2522,22,1], - ["p1",13,"token","token_10000_silent_healrb",null,null,1000,1,106,0,0,0,2877,48,null], + ["p1",12,"op","chess_char_2_05_a",1,null,0,0,13,0,0,5179,2449,22,1], + ["p1",13,"token","token_10000_silent_healrb",null,null,1000,1,106,0,0,0,3557,59,null], ["-","~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] @@ -174,17 +174,17 @@ "enemy_1197_sfshu": [9,9,0,430,20250], "enemy_1198_msfshu": [27,27,0,0,81], "enemy_1200_msfjin": [1,1,0,0,30], - "enemy_1234_dsubrl_2": [1,1,0,9581,12600], + "enemy_1234_dsubrl_2": [1,1,0,9819,12600], "enemy_1292_duskld": [1,0,0,1504,10609], "enemy_1387_winshd": [1,0,0,540,9673], "enemy_1502_crowns": [1,1,0,144,5400], - "enemy_2008_flking": [1,0,0,11413,353], + "enemy_2008_flking": [1,0,0,11315,353], "enemy_9016_acstmr": [1,1,0,0,8] }, - "hooks": [1,105,3300,569,569,689,804,222,86,90,24,23,0,16353,226,29,85,0,1,94,1,0,0], - "events": {"atk":670,"bounty":6,"deploy":17,"die":90,"dmg":774,"engage":9,"fx":109,"heal":192,"layer":1,"skill":47,"spawn":101,"status":223}, + "hooks": [1,105,3300,580,580,689,804,233,86,90,24,23,0,16480,226,29,85,0,1,90,1,0,0], + "events": {"atk":681,"bounty":6,"deploy":17,"die":90,"dmg":774,"engage":9,"fx":109,"heal":203,"layer":1,"skill":47,"spawn":101,"status":221}, "fx": {"aoe":1,"blink":1,"displace":8,"ember":1,"explode":10,"garrison":1,"heal":5,"phase":1,"pulse":42,"reveal":1,"revive":1,"sleep":8,"summon":25,"undying":1,"zone":3}, - "snaps": "c29e3835 28b841b8 3ea22a73 67a6b520 35d0349a 97ef8a6d 86c3dbf5 9d20d292 f19b1a3f 70f1741b 20c76bb6" + "snaps": "e30955f8 499cf905 25671f69 6dfb7b8d de2f1106 a052f01c c47c18ad 9c42fbaa 3cf7b1f7 86643fea 37f1f719" }, "roster-004": { "about": "pass 0 band 2: chess_char_2_08_a chess_char_2_17_a chess_char_2_18_a chess_char_2_07_a chess_char_2_09_a chess_char_2_11_a chess_char_2_13_a chess_char_2_16_a chess_char_2_12_a chess_char_2_06_a chess_char_2_10_a chess_char_2_14_a", @@ -261,26 +261,26 @@ "total": 84, "unspawned": 0, "bossHpLeft": null, - "rngDraws": 333, + "rngDraws": 334, "errors": {"count":0,"kinds":[]}, - "players": {"p1":{"killed":83,"total":84,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":10,"dmg":306697,"bossDmg":0,"heal":36881,"deaths":0,"layerGains":{}}}, + "players": {"p1":{"killed":83,"total":84,"perfect":false,"leaks":1,"leaksUncounted":0,"coins":10,"dmg":306697,"bossDmg":0,"heal":37410,"deaths":0,"layerGains":{}}}, "units": [ ["p1",1,"op","chess_char_2_07_b",1,"uniequip_002_ghost",2664.2,1,2,0,0,1707,1318,0,0], ["p1",2,"op","chess_char_2_08_b",1,"uniequip_002_bubble",3373,1,4,0,49,1818,0,0,1], ["p1",3,"op","chess_char_2_09_b",1,"uniequip_002_humus",2133,1,6,28,84020,0,0,59,2], - ["p1",4,"op","chess_char_2_11_b",1,"uniequip_002_kazema",2214,1,8,12,91244,15780,0,92,3], - ["p1",5,"op","chess_char_2_12_b",1,"uniequip_002_gravel",1452,1,10,2,7180,8945,0,129,1], - ["p1",6,"op","chess_char_2_19_a",1,null,1021.2,1,11,1,14957,1523,407,26,3], - ["p1",7,"op","chess_char_2_01_b",1,"uniequip_002_excu",2146,1,12,20,36079,4183,0,22,1], - ["p1",8,"op","chess_char_2_02_b",1,"uniequip_002_silent",1614,1,1,0,0,0,9257,28,5], + ["p1",4,"op","chess_char_2_11_b",1,"uniequip_002_kazema",2214,1,8,12,91244,15658,0,92,3], + ["p1",5,"op","chess_char_2_12_b",1,"uniequip_002_gravel",1452,1,10,2,7180,9417,0,129,1], + ["p1",6,"op","chess_char_2_19_a",1,null,1021.2,1,11,1,14957,1433,314,26,3], + ["p1",7,"op","chess_char_2_01_b",1,"uniequip_002_excu",2146,1,12,20,36079,4363,0,22,1], + ["p1",8,"op","chess_char_2_02_b",1,"uniequip_002_silent",1614,1,1,0,0,0,9366,28,6], ["p1",9,"op","chess_char_2_04_b",1,"uniequip_002_grabds",1270,1,3,10,38233,2115,0,53,3], - ["p1",10,"op","chess_char_2_05_b",1,"uniequip_002_harold",1971.2,1,5,0,0,0,11159,39,2], - ["p1",11,"op","chess_char_2_06_b",0,"uniequip_002_papyrs",1639,1,7,0,0,0,12883,28,13], - ["p1",12,"op","chess_char_2_10_b",1,"uniequip_002_rockr",1406,1,9,5,34936,810,0,113,2], - ["p1",13,"token","token_10000_silent_healrb",null,null,1000,0,103,0,0,0,1858,18,null] + ["p1",10,"op","chess_char_2_05_b",1,"uniequip_002_harold",1971.2,1,5,0,0,0,11081,40,2], + ["p1",11,"op","chess_char_2_06_b",0,"uniequip_002_papyrs",1639,1,7,0,0,0,12196,27,13], + ["p1",12,"op","chess_char_2_10_b",1,"uniequip_002_rockr",1406,1,9,5,34936,900,0,113,2], + ["p1",13,"token","token_10000_silent_healrb",null,null,1000,1,104,0,0,0,3137,30,null] ], "enemies": { - "enemy_10027_vtsk": [1,0,0,10080,23138], + "enemy_10027_vtsk": [1,0,0,10552,23138], "enemy_1006_shield_2": [1,1,0,0,8000], "enemy_10122_uacann_2": [1,1,0,3476,8000], "enemy_1042_frostd": [1,1,0,0,5200], @@ -290,15 +290,15 @@ "enemy_1249_lysdb_2": [15,15,0,372,72000], "enemy_1251_lysyta": [52,52,0,50,114400], "enemy_1299_ymkilr_2": [1,1,0,1178,8800], - "enemy_1328_cbjedi": [6,6,0,8860,48000], - "enemy_1402_tgshd_2": [1,1,0,200,10400], - "enemy_1509_mousek": [1,1,0,7102,17600], - "enemy_2010_csdcr": [1,1,0,1896,9600] + "enemy_1328_cbjedi": [6,6,0,8950,48000], + "enemy_1402_tgshd_2": [1,1,0,20,10400], + "enemy_1509_mousek": [1,1,0,7192,17600], + "enemy_2010_csdcr": [1,1,0,1986,9600] }, - "hooks": [1,102,4200,702,702,1650,1643,1107,83,89,36,35,0,28577,142,53,84,0,1,83,0,0,0], - "events": {"atk":917,"bounty":5,"deploy":18,"die":89,"dmg":1602,"engage":7,"fx":66,"heal":122,"skill":71,"spawn":97,"status":441}, - "fx": {"counter":2,"explode":3,"expose":9,"overload":2,"phase":1,"shield":9,"shieldBreak":16,"sleep":7,"summon":5,"telegraph":6,"zone":6}, - "snaps": "de0d6fce aa9dbd18 e5f78211 369608f9 eaf0206b 8ca00225 879ade74 10585017 e8565d0c e354c612 a50e5370 07ad00b4 54071507 c18f3d28" + "hooks": [1,103,4200,714,714,1650,1643,1083,83,89,37,36,0,29028,143,53,84,0,1,83,0,0,0], + "events": {"atk":926,"bounty":5,"deploy":19,"die":89,"dmg":1603,"engage":7,"fx":66,"heal":134,"skill":73,"spawn":97,"status":433}, + "fx": {"counter":2,"explode":3,"expose":9,"overload":2,"phase":1,"shield":8,"shieldBreak":16,"sleep":7,"summon":6,"telegraph":6,"zone":6}, + "snaps": "84edc036 a46838b0 1d9bc714 1b570575 aab0920b 4bc6df97 dd7f1064 918ab11b 31267425 a4452600 f9a6edeb 9a33bdd8 c9a35f1c 7d3f9999" }, "roster-006": { "about": "pass 0 band 3: chess_char_2_17_b chess_char_2_18_b chess_char_3_06_a chess_char_2_13_b chess_char_2_16_b chess_char_3_02_a chess_char_3_03_a chess_char_3_04_a chess_char_3_05_a chess_char_2_14_b chess_char_2_19_b chess_char_3_01_a", @@ -452,7 +452,7 @@ "time": 170, "ticks": 5100, "reason": "timeout", - "killed": 51, + "killed": 53, "total": 87, "unspawned": 0, "bossHpLeft": null, @@ -460,13 +460,13 @@ "errors": {"count":0,"kinds":[]}, "players": { "p1": { - "killed": 51, + "killed": 53, "total": 87, "perfect": false, - "leaks": 36, + "leaks": 34, "leaksUncounted": 1, "coins": 8, - "dmg": 574049, + "dmg": 582803, "bossDmg": 0, "heal": 5204, "deaths": 16, @@ -478,12 +478,12 @@ ["p1",2,"op","chess_char_3_12_b",2,"uniequip_002_blemsh",0,0,20,7,44837,6002,5204,21,1], ["p1",3,"op","chess_char_3_02_b",1,"uniequip_002_ayer",0,0,10,0,28912,2711,0,43,1], ["p1",4,"op","chess_char_3_03_b",1,"uniequip_002_swire",0,0,7,1,16148,2438,0,18,1], - ["p1",5,"op","chess_char_3_04_b",1,"uniequip_002_swire2",1109.5,1,129,3,49374,12087,0,34,0], - ["p1",6,"op","chess_char_3_05_b",2,"uniequip_002_skadi",4331.14,1,128,14,88110,25803,0,46,2], - ["p1",7,"op","chess_char_3_21_a",2,null,0,0,2,0,21374,1464,0,35,1], + ["p1",5,"op","chess_char_3_04_b",1,"uniequip_002_swire2",616.59,1,130,5,59093,14705,0,44,0], + ["p1",6,"op","chess_char_3_05_b",2,"uniequip_002_skadi",5952.29,1,129,14,88619,24168,0,45,2], + ["p1",7,"op","chess_char_3_21_a",2,null,0,0,2,0,20351,1464,0,32,1], ["p1",8,"op","chess_char_3_01_b",2,"uniequip_002_angel",0,0,3,5,126129,1987,0,86,2], - ["p1",9,"op","chess_char_3_08_b",1,"uniequip_002_mint",0,0,4,9,75802,7586,0,10,1], - ["p1",10,"op","chess_char_3_09_b",1,"uniequip_002_haini",0,0,126,3,34783,2754,0,34,1], + ["p1",9,"op","chess_char_3_08_b",1,"uniequip_002_mint",0,0,4,9,75802,7686,0,10,1], + ["p1",10,"op","chess_char_3_09_b",1,"uniequip_002_haini",0,0,126,3,34331,2754,0,33,1], ["p1",11,"op","chess_char_3_10_b",1,"uniequip_002_pinecn",0,0,1,1,10694,2170,0,10,1], ["p1",12,"op","chess_char_3_11_b",1,"uniequip_002_snhunt",0,0,5,7,71515,2000,0,38,9], ["p1","~1","token","token_10031_swire2_gdtrap",null,null,1000,0,13,0,0,0,0,0,null], @@ -503,8 +503,9 @@ ["p1","~15","token","token_10031_swire2_gdtrap",null,null,1000,0,96,0,0,0,0,0,null], ["p1","~16","token","token_10031_swire2_gdtrap",null,null,1000,0,100,0,0,0,0,0,null], ["p1","~17","token","token_10031_swire2_gdtrap",null,null,1000,0,101,0,0,0,0,0,null], - ["p1","~18","token","token_10031_swire2_gdtrap",null,null,1000,0,122,0,0,0,0,0,null], - ["p1","~19","token","token_10031_swire2_gdtrap",null,null,1000,1,130,0,0,0,0,0,null] + ["p1","~18","token","token_10031_swire2_gdtrap",null,null,1000,0,105,0,0,0,0,0,null], + ["p1","~19","token","token_10031_swire2_gdtrap",null,null,1000,0,122,0,0,0,0,0,null], + ["p1","~20","token","token_10031_swire2_gdtrap",null,null,1000,1,131,0,0,0,0,0,null] ], "enemies": { "enemy_10030_vtwand": [1,0,0,0,0], @@ -512,20 +513,20 @@ "enemy_10138_xdsnow": [1,1,0,13,9504], "enemy_1045_hammer_2": [1,0,1,11134,26452], "enemy_1148_dssbr": [1,1,0,0,4838], - "enemy_1172_dugago_2": [1,0,1,3915,20736], + "enemy_1172_dugago_2": [1,0,1,4302,20736], "enemy_1203_sfhu_2": [1,1,0,3712,15120], "enemy_1204_msfhu_2": [4,4,0,0,24], "enemy_1254_lypa_2": [1,1,0,3974,17280], "enemy_1305_mhslim_2": [1,1,0,614,3110], "enemy_1415_mmkabi_2": [1,0,1,60,24007], "enemy_1513_dekght": [1,0,1,2017,13460], - "enemy_2021_syfish": [60,38,22,3714,287090], - "enemy_2025_syufo": [13,3,7,34317,151477] + "enemy_2021_syfish": [60,40,20,3912,296296], + "enemy_2025_syufo": [13,3,7,34695,151025] }, - "hooks": [1,130,5100,572,572,1142,1238,383,67,123,21,24,0,10102,74,47,88,33,1,72,14,13,0], - "events": {"atk":665,"bounty":4,"deploy":42,"die":90,"dmg":1182,"engage":12,"fx":188,"heal":382,"layer":14,"leak":33,"skill":49,"spawn":119,"status":129}, - "fx": {"aoe":1,"blink":4,"buff":1,"burst":13,"coin":21,"displace":31,"explode":25,"garrison":9,"layer":5,"pull":31,"revive":5,"shield":1,"shieldBreak":1,"stone":1,"strike":9,"summon":20,"telegraph":1,"volley":9}, - "snaps": "d491f937 8ffbd7b3 9e67e1b5 7e887872 27c243c0 3f150bd7 8e79d798 8c8aa383 1ace5379 4d15cb2e a4aee685 006dff51 e06c3bae 1719e77c cd00bd64 7ff011a8 21d3419f" + "hooks": [1,131,5100,586,586,1158,1255,394,69,124,21,24,0,10104,74,48,88,31,1,75,14,12,0], + "events": {"atk":679,"bounty":4,"deploy":43,"die":93,"dmg":1199,"engage":12,"fx":192,"heal":393,"layer":14,"leak":31,"skill":49,"spawn":120,"status":125}, + "fx": {"aoe":1,"blink":4,"buff":1,"burst":12,"coin":23,"displace":31,"explode":26,"garrison":9,"layer":5,"pull":31,"revive":6,"shield":1,"shieldBreak":1,"stone":1,"strike":9,"summon":21,"telegraph":1,"volley":9}, + "snaps": "d491f937 8ffbd7b3 9e67e1b5 7e887872 27c243c0 3f150bd7 8e79d798 8c8aa383 1ace5379 888f0b4b 72fc5e5e b0e185b5 44f8935f fe76c659 0db9aa25 e37eea06 638b6604" }, "roster-009": { "about": "pass 0 band 4: chess_char_3_16_b chess_char_4_01_a chess_char_3_18_b chess_char_4_04_a chess_char_3_13_b chess_char_3_14_b chess_char_3_17_b chess_char_3_19_b chess_char_3_20_b chess_char_3_21_b chess_char_4_02_a chess_char_4_06_a token_10028_vigil_wolf", @@ -1071,7 +1072,7 @@ "hooks": [1,154,6900,951,1017,7281,7430,78804,91,140,40,36,17,35845,1032,57,126,45,1,102,1,7,6], "events": {"atk":2283,"bounty":3,"deploy":28,"die":95,"dmg":7385,"engage":10,"fx":552,"heal":121,"layer":1,"leak":45,"skill":76,"spawn":142,"status":2410}, "fx": {"aoe":8,"bombard":17,"bombardShell":17,"burst":7,"crit":50,"dodge":6,"explode":40,"firewall":2,"garrison":1,"iceSpike":90,"lightning":148,"lock":17,"lure":3,"revive":1,"snow":82,"soul":1,"sp":1,"storm":5,"talent":2,"telegraph":8,"tide":3,"wanted":38,"weightless":5}, - "snaps": "ee17d257 d832b8ac 2ca210aa 3cd3fc22 022376cf a362140e 0ca09d66 1f446040 29245222 70bfb33a 2b4ce12f 51a65ad5 1216d2fa f2d3b899 86e068de 5a0bbda6 1bb53622 0ef45b3c a98167d6 5245239e eac46280 b7e2f8ef 905be587" + "snaps": "76864815 62849bad 979bf882 25b6b06a 5180c227 3ecd4ada 973ab3dc 34c4af0f c0357d8d 21dd059d b2e1cc4c 8a773c5e 69aa5545 f41c0b6e 34d38c11 fd5b4e85 3a8cd96d 541d94f7 b7150f71 e0eeba75 f3b17a4b 864eec43 64dedb33" }, "roster-017": { "about": "pass 0 band 6: chess_char_6_15_a chess_char_6_19_a chess_char_6_07_a chess_char_6_17_a chess_char_6_08_a chess_char_6_09_a chess_char_6_11_a chess_char_6_12_a chess_char_6_13_a chess_char_6_14_a chess_char_6_16_a chess_char_6_18_a", @@ -1226,7 +1227,7 @@ "hooks": [1,146,5863,543,543,1347,1516,3220,125,132,42,38,32,26310,394,7,122,0,1,125,11,5,0], "events": {"atk":634,"bounty":6,"deploy":22,"die":132,"dmg":1435,"engage":13,"fx":264,"heal":169,"layer":11,"skill":80,"spawn":146,"status":444}, "fx": {"beam":5,"bombard":32,"bombardShell":32,"burst":5,"camouflage":2,"copy":1,"dp":5,"garrison":8,"lightning":99,"link":1,"lock":32,"lure":3,"scorchBurst":9,"snow":7,"split":5,"storm":7,"summon":2,"talent":1,"tide":5,"wanted":3}, - "snaps": "d98f113c d0331366 be0cebae 241fe6f7 134c6492 39edc9da 96df2aaa b5e300fe 865fba99 4cc163f0 3748edd7 acc90429 29160015 97e57a26 cabd46be 14cde004 c11ac045 97b93efb 317c26e6 835a5563" + "snaps": "9cdb845f 1a3da81a b8f8fa18 18adb6a1 045ac790 92daeb67 e9b25891 6153ba1f 318ab774 dbcd5d11 bd86ccae 2804e1a9 0b939c36 d69ed2f9 d52ba02d c74b0ce7 bf1bc7e3 b8b19432 7a71afd1 478ab631" }, "roster-019": { "about": "pass 0 band 7: chess_char_6_15_b chess_char_6_19_b chess_char_5_01_b chess_char_5_05_b chess_char_6_17_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 chess_char_5_02_b chess_char_5_03_b", @@ -1692,25 +1693,25 @@ "time": 170, "ticks": 5100, "reason": "timeout", - "killed": 30, + "killed": 31, "total": 53, "unspawned": 0, "bossHpLeft": null, "rngDraws": 140, "errors": {"count":0,"kinds":[]}, "players": { - "p1": {"killed":30,"total":53,"perfect":false,"leaks":23,"leaksUncounted":0,"coins":4,"dmg":375198,"bossDmg":0,"heal":14642,"deaths":9,"layerGains":{"raidShip":18}} + "p1": {"killed":31,"total":53,"perfect":false,"leaks":22,"leaksUncounted":0,"coins":4,"dmg":381570,"bossDmg":0,"heal":20570,"deaths":8,"layerGains":{"raidShip":18}} }, "units": [ ["p1",1,"op","chess_char_3_06_b",0,"none",0,0,2,0,952,6276,0,10,1], ["p1",2,"op","chess_char_3_12_b",0,"uniequip_003_blemsh",0,0,14,5,31951,14072,11110,46,21], ["p1",3,"op","chess_char_3_02_b",0,"none",0,0,6,0,15577,2291,0,37,12], ["p1",4,"op","chess_char_3_03_b",0,"none",0,0,8,0,3718,1778,0,35,1], - ["p1",5,"op","chess_char_3_04_b",0,"uniequip_003_swire2",0,0,59,4,48389,6778,3531,66,0], + ["p1",5,"op","chess_char_3_04_b",0,"uniequip_003_swire2",1913.64,1,59,5,55982,7885,9460,73,0], ["p1",6,"op","chess_char_3_05_b",0,"uniequip_003_skadi",0,0,12,2,26200,12824,0,29,1], ["p1",7,"op","chess_char_3_21_a",0,null,506.83,1,10,0,8795,857,0,35,14], ["p1",8,"op","chess_char_3_01_b",0,"uniequip_003_angel",470.41,1,11,1,38052,1246,0,75,32], - ["p1",9,"op","chess_char_3_08_b",0,"none",233.39,1,1,7,80992,3015,0,23,4], + ["p1",9,"op","chess_char_3_08_b",0,"none",233.39,1,1,7,79772,3015,0,23,4], ["p1",10,"op","chess_char_3_09_b",0,"none",0,0,3,0,12044,1534,0,13,4], ["p1",11,"op","chess_char_3_10_b",0,"none",0,0,5,4,54938,2120,0,24,7], ["p1",12,"op","chess_char_3_11_b",0,"none",0,0,7,6,53591,1610,0,19,10] @@ -1726,12 +1727,12 @@ "enemy_1283_sgkill_2": [1,0,1,8304,8764], "enemy_1425_lrcmra": [1,0,1,1808,10651], "enemy_2008_flking": [1,0,1,7980,4818], - "enemy_9006_actoxi": [12,5,5,4229,136218] + "enemy_9006_actoxi": [12,6,5,5082,142471] }, - "hooks": [1,67,5100,528,528,1936,1936,28,39,60,107,108,0,7439,40,31,53,19,1,41,3,0,0], - "events": {"atk":733,"bounty":2,"deploy":14,"die":41,"dmg":1871,"engage":12,"fx":110,"heal":28,"layer":3,"leak":19,"skill":217,"spawn":65,"status":185}, - "fx": {"blink":1,"buff":1,"coin":18,"explode":25,"garrison":3,"revive":2,"shield":20,"shieldBreak":1,"strike":10,"volley":7,"zone":22}, - "snaps": "96282387 24613d95 49d36ca6 4e91020f f1d25080 9e767196 bdb45ea5 0f508e6f c1c13843 91b6d342 0d44ebac 29e98739 a430c9be c8c5ec6c 2fd74d54 48089465 456f6920" + "hooks": [1,67,5100,536,536,1944,1944,36,39,60,107,107,0,7439,40,31,53,19,1,39,3,0,0], + "events": {"atk":740,"bounty":2,"deploy":14,"die":41,"dmg":1879,"engage":12,"fx":125,"heal":36,"layer":3,"leak":19,"skill":217,"spawn":65,"status":185}, + "fx": {"blink":1,"buff":1,"coin":34,"explode":25,"garrison":3,"shield":20,"shieldBreak":1,"strike":10,"volley":7,"zone":23}, + "snaps": "96282387 24613d95 49d36ca6 4e91020f f1d25080 9e767196 bdb45ea5 0f508e6f c1c13843 91b6d342 0d44ebac 29e98739 5de387ca e01250d2 66bd1e5b 4aa99d1f 8cb0a6a5" }, "roster-028": { "about": "pass 1 band 4: chess_char_3_16_b chess_char_4_01_a chess_char_3_18_b chess_char_4_04_a chess_char_3_13_b chess_char_3_14_b chess_char_3_17_b chess_char_3_19_b chess_char_3_20_b chess_char_3_21_b chess_char_4_02_a chess_char_4_06_a token_10028_vigil_wolf", @@ -2194,7 +2195,7 @@ "hooks": [1,143,5589,311,311,508,779,843,126,129,71,69,0,13806,388,3,125,0,1,126,2,11,0], "events": {"atk":502,"bounty":4,"deploy":18,"die":129,"dmg":779,"engage":6,"fx":218,"heal":9,"layer":2,"skill":140,"spawn":142,"status":553}, "fx": {"bombard":3,"bombardShell":3,"burst":11,"copy":1,"expose":117,"frostNova":1,"garrison":2,"heal":3,"phase":1,"shieldBreak":1,"snow":14,"sp":58,"talent":1,"wanted":2}, - "snaps": "378db087 c01e4bfb 656d09bb 8ad40d0d 980adb80 0fbc6dfa 0affdeb4 5f9cb80b 96e5686c ad4f2604 0b08bcd0 e88e1b52 18910b94 829e4b19 4a00d92e 630d2588 f81f3544 df9493eb ce728565" + "snaps": "7d4b3aca f6affb06 724b3085 0ddf76eb d68a8dda a623e364 9c9d2e86 9998c540 c8bc041f 5e9130db 6dfbcc7b c6ec5c11 f21a16a2 e1fcd736 9bf63db1 696fbd1b 2c7e2d3c e2ef6c77 9b42d582" }, "roster-036": { "about": "pass 1 band 7: chess_char_6_03_b chess_char_6_15_a chess_char_6_19_a chess_char_6_17_a chess_char_6_12_a chess_char_6_13_a chess_char_6_14_a chess_char_6_16_a chess_char_6_18_a chess_char_6_20_a chess_char_6_01_b chess_char_6_02_b", @@ -2263,68 +2264,70 @@ "time": 217.2, "ticks": 6516, "reason": "cleared", - "killed": 38, + "killed": 45, "total": 131, "unspawned": 0, "bossHpLeft": null, - "rngDraws": 100, + "rngDraws": 90, "errors": {"count":0,"kinds":[]}, "players": { "p1": { - "killed": 38, + "killed": 45, "total": 131, "perfect": false, - "leaks": 93, + "leaks": 86, "leaksUncounted": 0, - "coins": 4, - "dmg": 994699, + "coins": 6, + "dmg": 1011569, "bossDmg": 0, - "heal": 30633, - "deaths": 25, - "layerGains": {"sargonShip":14} + "heal": 42323, + "deaths": 24, + "layerGains": {"sargonShip":16} } }, "units": [ - ["p1",1,"op","chess_char_6_06_b",0,"uniequip_003_pepe",0,0,89,1,39371,6491,513,13,3], - ["p1",2,"op","chess_char_6_15_b",0,"uniequip_003_qiubai",0,0,6,2,124903,6154,3570,27,6], - ["p1",3,"op","chess_char_6_07_b",0,"uniequip_003_siege2",0,0,9,2,110073,2661,0,15,5], - ["p1",4,"op","chess_char_6_04_b",0,"uniequip_003_skadi2",3523.2,1,169,0,0,10506,3936,0,3], - ["p1",5,"op","chess_char_6_05_b",0,"uniequip_003_pasngr",0,0,5,4,302318,1502,0,33,16], - ["p1",6,"op","chess_char_6_08_b",0,"uniequip_003_reed2",0,0,96,0,72197,8332,5006,52,1], - ["p1",7,"op","chess_char_6_09_b",1,"uniequip_003_cello",1352,1,170,0,57079,4352,296,20,2], - ["p1",8,"op","chess_char_6_11_b",0,"uniequip_003_mlyss",1873,1,171,3,23389,8393,901,58,2], - ["p1",9,"op","chess_char_6_12_b",0,"uniequip_003_rosmon",0,0,60,1,123922,5390,87,22,6], - ["p1",10,"op","chess_char_6_13_b",0,"none",1684.2,1,161,0,76484,11597,1724,50,5], - ["p1",11,"op","chess_char_6_14_b",0,"uniequip_003_lumen",0,0,15,0,0,12802,14600,13,5], - ["p1",12,"op","chess_char_6_16_b",0,"none",0,0,76,2,61875,2648,0,15,1], - ["p1",13,"token","token_10017_skadi2_dedant",null,null,0,0,17,0,0,100,0,0,null], - ["p1",14,"token","token_10030_mlyss_wtrman",null,null,0,0,11,0,201,1608,0,6,1], + ["p1",1,"op","chess_char_6_06_b",0,"uniequip_003_pepe",0,0,90,0,29221,6236,258,7,3], + ["p1",2,"op","chess_char_6_15_b",0,"uniequip_003_qiubai",0,0,6,1,115832,4600,2016,23,5], + ["p1",3,"op","chess_char_6_07_b",0,"uniequip_003_siege2",0,0,9,0,76591,2661,0,11,4], + ["p1",4,"op","chess_char_6_04_b",0,"uniequip_003_skadi2",3523.2,1,172,0,0,20406,11891,0,4], + ["p1",5,"op","chess_char_6_05_b",0,"uniequip_003_pasngr",0,0,5,10,297865,1502,0,34,17], + ["p1",6,"op","chess_char_6_08_b",0,"uniequip_003_reed2",0,0,98,1,68469,7787,4461,49,1], + ["p1",7,"op","chess_char_6_09_b",1,"uniequip_003_cello",1352,1,171,1,72879,4204,148,19,2], + ["p1",8,"op","chess_char_6_11_b",0,"uniequip_003_mlyss",1873,1,160,1,48208,7012,1393,94,2], + ["p1",9,"op","chess_char_6_12_b",0,"uniequip_003_rosmon",0,0,67,2,165357,7444,2140,26,8], + ["p1",10,"op","chess_char_6_13_b",0,"none",2005,1,169,0,71982,10725,2630,45,4], + ["p1",11,"op","chess_char_6_14_b",0,"uniequip_003_lumen",0,0,15,0,0,12802,13143,13,5], + ["p1",12,"op","chess_char_6_16_b",0,"none",0,0,81,0,62580,3399,751,17,1], + ["p1",13,"token","token_10017_skadi2_dedant",null,null,100,0,17,0,0,0,0,0,null], + ["p1",14,"token","token_10030_mlyss_wtrman",null,null,0,0,11,0,550,4736,3127,14,1], ["-","~1","device","trap_1105_accrate",null,null,100,0,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], - ["p1","~1","token","token_10030_mlyss_wtrman",null,null,0,0,43,0,522,1497,0,1,1], - ["p1","~2","token","token_10030_mlyss_wtrman",null,null,0,0,78,0,497,1192,0,1,1], - ["p1","~3","token","token_10030_mlyss_wtrman",null,null,0,0,125,0,1778,2145,0,4,1], - ["p1","~4","token","token_10030_mlyss_wtrman",null,null,0,0,153,0,90,1805,0,3,1], - ["p1","~5","token","token_10030_mlyss_wtrman",null,null,1804.5,1,172,0,0,0,0,0,1] + ["p1","~1","token","token_10030_mlyss_wtrman",null,null,0,0,32,0,104,1608,0,3,null], + ["p1","~1","token","token_10017_skadi2_dedant",null,null,100,0,54,0,0,0,0,0,null], + ["p1","~2","token","token_10030_mlyss_wtrman",null,null,0,0,80,0,0,1865,0,0,0], + ["p1","~2","token","token_10017_skadi2_dedant",null,null,100,0,109,0,0,0,0,0,null], + ["p1","~3","token","token_10030_mlyss_wtrman",null,null,0,0,125,0,1696,1684,363,5,1], + ["p1","~4","token","token_10030_mlyss_wtrman",null,null,0,0,161,0,233,1805,0,6,1], + ["p1","~3","token","token_10017_skadi2_dedant",null,null,100,1,173,0,0,0,0,0,null] ], "enemies": { - "enemy_10042_prtrop": [1,1,0,775,17199], - "enemy_10118_ymgprc": [1,0,1,12755,112019], - "enemy_1045_hammer_2": [18,3,15,3234,313913], - "enemy_1061_zomshd": [60,10,50,34663,543507], + "enemy_10042_prtrop": [1,1,0,2278,17199], + "enemy_10118_ymgprc": [1,0,1,4232,122751], + "enemy_1045_hammer_2": [18,3,15,15972,315355], + "enemy_1061_zomshd": [60,11,49,36815,558519], "enemy_1071_dftman": [1,1,0,0,8026], - "enemy_1172_dugago": [1,0,1,1428,38609], - "enemy_1210_msfden_2": [1,0,1,0,42], - "enemy_1269_nhfly": [45,23,22,19110,3747], + "enemy_1172_dugago": [1,0,1,2855,46471], + "enemy_1210_msfden_2": [1,1,0,0,45], + "enemy_1269_nhfly": [45,28,17,22220,0], "enemy_1321_wdarft": [1,0,1,0,0], - "enemy_1501_demonk": [1,0,1,13122,15956], - "enemy_2052_smgia": [1,0,1,0,43424] + "enemy_1501_demonk": [1,0,1,11941,20299], + "enemy_2052_smgia": [1,0,1,0,22008] }, - "hooks": [1,170,6516,408,408,2395,2585,24079,69,163,61,57,27,9225,1280,27,131,93,1,48,2,5,0], - "events": {"atk":518,"bounty":2,"deploy":36,"die":70,"dmg":2504,"engage":15,"fx":80,"heal":48,"layer":2,"leak":93,"skill":118,"spawn":153,"status":679}, - "fx": {"airstrike":6,"aoe":11,"burst":5,"copy":6,"explode":23,"garrison":2,"heal":3,"healField":5,"link":2,"revive":1,"stone":1,"summon":15}, - "snaps": "79552059 e5891b97 05581a54 4cf8b8b4 4c68e31e 6d0f79f9 326b9fac c54d2228 53ac3aee 4caa7e6f 63d4e63f 466be41c 29f5fd64 c6cb8659 fe3e5972 f93d955b 3456f391 0ca7b782 058d8c95 51f8fe78 675ec88f 7ffc0c56" + "hooks": [1,171,6516,450,450,2452,2627,24832,74,164,59,56,25,9642,1397,32,131,86,1,49,2,6,0], + "events": {"atk":560,"bounty":3,"deploy":37,"die":78,"dmg":2552,"engage":14,"fx":83,"heal":40,"layer":2,"leak":86,"skill":115,"spawn":155,"status":670}, + "fx": {"airstrike":5,"aoe":9,"burst":6,"cleanse":1,"copy":3,"explode":28,"garrison":2,"heal":4,"healField":5,"link":2,"revive":1,"split":1,"stone":1,"summon":15}, + "snaps": "aad8bee8 801856dd 3399a410 b92f8209 23973744 57bbfa1c 9ffd3055 be8deeb1 0bdabeab bda375ae 4afc7560 c160fb12 28f439d9 6584b7fd d1fd5246 16ea2222 4f142c9c dd788125 251f8a15 260c47ec 822d6d6e 1f8efd14" }, "roster-038": { "about": "pass 1 band 7: chess_char_5_08_b chess_char_6_19_b chess_char_5_01_b chess_char_5_05_b chess_char_5_06_b chess_char_6_17_b chess_char_5_07_b chess_char_6_18_b chess_char_6_20_b chess_char_5_02_b chess_char_5_03_b chess_char_5_10_b", @@ -2806,7 +2809,7 @@ "hooks": [1,91,5571,428,415,892,1381,11450,69,74,12,9,10,22992,758,17,68,0,1,69,2,51,0], "events": {"atk":524,"bounty":4,"deploy":20,"die":74,"dmg":1196,"engage":8,"fx":125,"layer":2,"skill":21,"spawn":90,"status":1451}, "fx": {"airstrike":5,"anchor":1,"burst":51,"dp":3,"expose":6,"garrison":2,"link":2,"snow":5,"summon":2,"summonStun":2,"talent":1,"wanted":45}, - "snaps": "cf9f9e00 4757b940 5f298e20 410a2d5d d84d51b4 7fc5ffdd 6924e4f0 afd930c6 a7d4ff63 35d5e664 a27fdae2 8dc50ed9 19f8fcbd 461d841e 4ffb9bc5 2cc4d1a8 729b0562 d4ce4f77 c940e5b3" + "snaps": "10fd15da 83cddf7f c846b231 d53dbd5c 231b2365 73a4cfd5 dbf3e959 b191323a 564195d7 3b4974b0 9b85bd56 82fc4a77 d312a559 0c646576 98456a8d a4735a50 8734e3fa 41a0eaf2 8280a082" }, "roster-046": { "about": "pass 2 band 7: chess_char_6_03_b chess_char_6_15_a chess_char_6_19_a chess_char_6_17_a chess_char_6_14_a chess_char_6_16_a chess_char_6_18_a chess_char_6_20_a chess_char_6_01_b chess_char_6_02_b chess_char_6_04_b chess_char_6_05_b token_10017_skadi2_dedant", @@ -2861,7 +2864,7 @@ "hooks": [1,165,6669,226,226,503,516,740,25,162,9,9,5,4004,335,15,134,133,1,26,1,1,0], "events": {"atk":332,"bounty":1,"deploy":31,"die":29,"dmg":507,"engage":8,"fx":167,"heal":18,"layer":1,"leak":133,"skill":18,"spawn":149,"status":425}, "fx": {"blessing":1,"blink":2,"burst":1,"camouflage":2,"displace":1,"explode":71,"garrison":1,"garrisonGrant":2,"link":1,"snow":7,"sunBurst":3,"talent":2,"telegraph":2,"zone":71}, - "snaps": "fd8ace00 3735d1a0 ac4c99f8 c5d4ca9a 8fded089 880882c7 e721adb9 0ae8c4ec e7907ddb 600d4aaf 8ba41e7f 29e57375 d2a53406 bc57a24b 0f0f5742 e95912b1 d765e411 ac479235 f1961017 bebbcde2 d00f5670 5d45684c ab83875e" + "snaps": "7d1bb456 b462eb12 665997c4 d3b0731e 733c887d 81c9ff7b d61bcd86 d87fe69c 579299cc 8f123aac dd0783cc 48859e06 0d12afe5 4eadb4fc b655b87d aa32fec6 c40fe17e e5480f12 c8fc10e0 5c4a7375 b8961ef3 38a3e52e 2342edcb" }, "roster-047": { "about": "pass 2 band 7: chess_char_6_06_b chess_char_6_15_b chess_char_6_07_b chess_char_6_17_b chess_char_6_08_b chess_char_6_09_b chess_char_6_11_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 token_10030_mlyss_wtrman", @@ -3025,7 +3028,7 @@ "hooks": [1,71,5700,396,396,1837,1837,0,35,66,31,32,0,3909,790,5,64,31,1,37,0,0,0], "events": {"atk":461,"bounty":3,"deploy":7,"die":35,"dmg":1836,"engage":5,"fx":46,"leak":31,"skill":66,"spawn":69,"status":292}, "fx": {"coin":8,"dilemma":2,"explode":2,"frostNova":25,"jungleSoul":1,"revive":2,"shield":1,"shieldBreak":1,"summon":1,"telegraph":3}, - "snaps": "3f2254f6 76b714eb 68b544c1 a849f2de a211d880 6cba6fce d113ba1d fc04f6f9 7682f0c2 4aba683d bde49088 1630bd13 e619d9ab 8bedd714 2669b6cc 475135ba da92f818 e0d13cba 9707c4aa" + "snaps": "3f2254f6 76b714eb 68b544c1 a849f2de a211d880 6cba6fce d113ba1d fc04f6f9 7682f0c2 4aba683d bde49088 1630bd13 e619d9ab 8bedd714 2669b6cc 475135ba e5876f3c 878f230c ced85923" } } } diff --git a/test/render/feedback5-zone-stack.test.js b/test/render/feedback5-zone-stack.test.js new file mode 100644 index 00000000..1b57a2ec --- /dev/null +++ b/test/render/feedback5-zone-stack.test.js @@ -0,0 +1,75 @@ +// test/render/feedback5-zone-stack.test.js — persistent ground areas (炼金单元 …) stay readable when they stack (community +// report of 2026-10-06 「炼金师分支干员的炼金单元等范围持续性技能特效太亮(尤其是多层叠加以后)」): a zone the sim re-sends under one +// `key` (引星棘刺 S2's drifting unit, every second) is updated in place instead of piling up a new additive layer each time, +// and a persistent area is drawn dimmer and shares its light with the persistent areas overlapping it (÷ √n) — +// render/fx/zones.js zone / zoneAlpha, render/fx/simfx.js (the event's `key`). +// Run: node --test test/render/feedback5-zone-stack.test.js + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { FxSystem } from '../../public/js/render/fx.js'; +import { zoneAlpha, ZONE_ALPHA, ZONE_PERSIST } from '../../public/js/render/fx/zones.js'; +import { presetCamera } from '../../public/js/render/projection.js'; + +/** An FxSystem shell with fake sprites: enough for zone() / _updateZones() / simFx('zone'). */ +function fxShell() { + const cam = presetCamera('normal', { width: 1280, height: 720 }); + class Sprite { + constructor() { this.anchor = { set() {} }; this.position = { set() {} }; this.scale = { set() {} }; this.alpha = 1; this.tint = 0; this.destroyed = false; } + destroy() { this.destroyed = true; } + } + const fx = Object.create(FxSystem.prototype); + Object.assign(fx, { + P: { Sprite, BLEND_MODES: { ADD: 1 } }, + tex: { soft: {}, ring: {} }, + zones: [], + ctx: { cam: () => cam, heightAt: () => 0, timeScale: () => 2, view: () => null }, + _p: { x: 0, y: 0, s: 0, depth: 0 }, _q: { x: 0, y: 0, s: 0, depth: 0 }, + _onGround() {}, + _groundZ: () => 0, + }); + return fx; +} + +test('a zone re-sent under the same key is moved / resized in place — one layer, not one per event', () => { + const fx = fxShell(); + fx.simFx('zone', 5, 10, { id: 1, r: 1.1, duration: 12, key: 'thorn2:7:1' }); + for (let i = 1; i <= 11; i++) { + fx._updateZones(0.5); + fx.simFx('zone', 5 + 0.1 * i, 10, { id: 1, r: 1.1 + 0.13 * i, duration: 12 - i, key: 'thorn2:7:1' }); + } + assert.equal(fx.zones.length, 1, 'twelve events, one zone'); + const zn = fx.zones[0]; + assert.ok(Math.abs(zn.x - 6.1) < 1e-9 && Math.abs(zn.r - (1.1 + 0.13 * 11)) < 1e-9, 'it follows the last event (drift, growth)'); + assert.ok(Math.abs(zn.dur - (zn.t + Math.max(0.6, 1 / 2))) < 1e-9, 'its end: the remaining game second at 2× (at least 0.6 s) from now'); + fx.simFx('zone', 8, 10, { id: 1, r: 1, duration: 12, key: 'thorn2:7:2' }); + fx.simFx('zone', 8, 11, { id: 2, r: 2, duration: 11 }); + fx.simFx('zone', 8, 11, { id: 2, r: 2, duration: 11 }); + assert.equal(fx.zones.length, 4, 'another key, and unkeyed zones (锡人\'s units, one event each), are zones of their own'); +}); + +test('persistent areas are dimmer and share their light when they overlap; short bursts and telegraphs keep theirs', () => { + const zone = (x, dur = 6, r = 2, warn = false) => ({ x, y: 10, r, dur, warn }); + const alone = zone(2); + assert.deepEqual(zoneAlpha(alone, [alone]), ZONE_ALPHA.persist); + const stack = [zone(5), zone(5.2), zone(5.4), zone(5.6)]; + const [disc, edge] = zoneAlpha(stack[0], stack); + assert.ok(Math.abs(disc - ZONE_ALPHA.persist[0] / 2) < 1e-12 && Math.abs(edge - ZONE_ALPHA.persist[1] / 2) < 1e-12, 'four on one spot: ÷ √4 each'); + assert.ok(4 * disc <= 2 * ZONE_ALPHA.short[0], `the four discs together (${(4 * disc).toFixed(2)}) stay within two short bursts`); + const far = [zone(2), zone(9)]; + assert.deepEqual(zoneAlpha(far[0], far), ZONE_ALPHA.persist, 'apart: no damping'); + const short = zone(5, ZONE_PERSIST - 0.5); + assert.deepEqual(zoneAlpha(short, [short, ...stack]), ZONE_ALPHA.short, 'a short burst keeps its strength'); + const warn = zone(5, 6, 2, true); + assert.deepEqual(zoneAlpha(warn, [warn, ...stack]), ZONE_ALPHA.warn, 'a telegraph keeps its strength'); +}); + +test('the renderer applies it: four alchemy units on one spot draw their discs at half the lone strength', () => { + const fx = fxShell(); + for (let i = 0; i < 4; i++) fx.simFx('zone', 5 + 0.1 * i, 10, { id: 9, r: 2, duration: 11 }); + fx.simFx('zone', 15, 10, { id: 9, r: 2, duration: 11 }); + fx._updateZones(1); // past the 0.25 s growth, before the fade + const lone = fx.zones[4], stacked = fx.zones.slice(0, 4); + const ratio = stacked[0].disc.alpha / lone.disc.alpha; + assert.ok(Math.abs(ratio - 0.5) < 1e-9, `÷ √4 (${ratio})`); +}); diff --git a/test/sim/feedback1f-downed-tile.test.js b/test/sim/feedback1f-downed-tile.test.js index 2056439a..a5b8d087 100644 --- a/test/sim/feedback1f-downed-tile.test.js +++ b/test/sim/feedback1f-downed-tile.test.js @@ -209,36 +209,41 @@ test('F5 "若干员被击倒的位置为其他干员或召唤物的初始位置 // expired or been killed — and the removed summon was not counted, so the clause never applied: a 突袭 member that // landed on the free tile of 浊心斯卡蒂's 海嗣 and fell there lay on it and kept the 海嗣 (远古血亲: it comes back on its // tile) off the field. Every board piece's 初始位置 counts now, on the field or not. +// Since 0.2.0 the 海嗣 is a countdown summon — 无敌 + 禁疗 (PRTS 海嗣 备注 「持有禁疗、无敌」; DESIGN §25.16): it only expires, +// no damage kills it. The killed case takes 缪尔赛思's 流形, a board summon that is attacked and killed and also comes back +// on its tile (「其被击败后会在25秒后自动刷新」). test('F5 rule 3: a body on the home of a summon that has expired or been killed goes back to its own; the summon comes back', () => { - for (const how of ['expired', 'killed']) { + const CASES = { + expired: { owner: 'chess_char_6_04_a', tokenId: 'token_10017_skadi2_dedant', name: '海嗣' }, + killed: { owner: 'chess_char_6_11_a', tokenId: 'token_10030_mlyss_wtrman', name: '流形' }, + }; + for (const [how, { owner, tokenId, name }] of Object.entries(CASES)) { const h = makeBattle({ stageId: 'flat', autoFinish: false, timeLimit: 400, bonds: RAID, units: [ - { chessId: 'chess_char_6_04_a', row: 10, col: 3, uid: 1 }, - { kind: 'token', tokenId: 'token_10017_skadi2_dedant', row: 10, col: 5, uid: 2, ownerUid: 1 }, + { chessId: owner, row: 10, col: 3, uid: 1 }, + { kind: 'token', tokenId, row: 10, col: 5, uid: 2, ownerUid: 1 }, { chessId: 'chess_char_1_18_a', row: 12, col: 3, uid: 3 }, ], }); h.step(); const b = h.b; - const sea = b.allyUnits.find((u) => u.kind === 'token' && u.uid === 2); + const sum = b.allyUnits.find((u) => u.kind === 'token' && u.uid === 2); const yan = h.unit(3); - assert.ok(sea.alive && sea.tileR === 10 && sea.tileC === 5, 'the 海嗣 on its placed tile'); - if (how === 'killed') kill(b, sea); - else h.runUntil(() => !sea.alive, 120); - assert.ok(!sea.alive && sea.removed, `${how}: the 海嗣 is gone`); + assert.ok(sum.alive && sum.tileR === 10 && sum.tileC === 5, `the ${name} on its placed tile`); + if (how === 'killed') kill(b, sum); + else h.runUntil(() => !sum.alive, 120); + assert.ok(!sum.alive && sum.removed, `${how}: the ${name} is gone`); assert.equal(b.isReservedTile(10, 5), false, 'its home is free for an automatic placement'); b.retreat(yan, { reason: 'raid' }); // the 突袭 landing (raidRedeploy: retreat + redeploy on a free tile) - assert.ok(b.redeploy(yan, { free: true, tile: [10, 5], keepSp: true }), `${how}: 宴 lands on the 海嗣's home`); + assert.ok(b.redeploy(yan, { free: true, tile: [10, 5], keepSp: true }), `${how}: 宴 lands on the ${name}'s home`); kill(b, yan); - assert.deepEqual(b.restTile(yan), [12, 3], `${how}: 宴 lies on her own home, not on the 海嗣's`); + assert.deepEqual(b.restTile(yan), [12, 3], `${how}: 宴 lies on her own home, not on the ${name}'s`); assert.deepEqual(bodyOf(b, yan), [12, 3], 'the client draws her there'); - assert.equal(b.isReservedTile(10, 5), false, 'the 海嗣\'s home stays free'); - if (how === 'expired') { - const back = () => b.allyUnits.find((u) => u.kind === 'token' && u.alive && u.defId === sea.defId) ?? null; - assert.ok(h.runUntil(() => back() != null, 60), 'the 海嗣 comes back (while 宴 still waits to redeploy)'); - assert.deepEqual(tileOf(back()), [10, 5], 'on its own tile'); - } + assert.equal(b.isReservedTile(10, 5), false, `the ${name}'s home stays free`); + const back = () => b.allyUnits.find((u) => u.kind === 'token' && u.alive && u.defId === tokenId) ?? null; + assert.ok(h.runUntil(() => back() != null, 60), `${how}: the ${name} comes back`); + assert.deepEqual(tileOf(back()), [10, 5], 'on its own tile'); checkInvariants(b); } }); diff --git a/test/sim/feedback5-countdown-summons.test.js b/test/sim/feedback5-countdown-summons.test.js new file mode 100644 index 00000000..68e9587b --- /dev/null +++ b/test/sim/feedback5-countdown-summons.test.js @@ -0,0 +1,72 @@ +// test/sim/feedback5-countdown-summons.test.js — the countdown summons (community report of 2026-10-06 「赫默的无人机、浊心 +// 斯卡蒂的海嗣等这种召唤物不会受到外来伤害(包括源石地板),也不会被干员治疗,血量随时间减少(血量就类似倒计时条,减少完召唤物就 +// 消失)」): the summons that "不会受到攻击" and leave after a fixed time — 医疗探机, 诅咒娃娃, 斯卡蒂的海嗣 and the 自选 ones +// (工程蓄水炮, 沙地兽, “打字机”, 本能的召唤) — hold 无敌 + 禁疗 (PRTS 海嗣 / 沙地兽 「持有禁疗、无敌」, 工程蓄水炮 禁疗; the others +// [ASSUMED] from the report) and their bar (the snapshot's hp) shows the share of their life left (content/tokens.js +// COUNTDOWN_SUMMONS / startCountdown). Until 0.2.0 a 海嗣 on 活性源石 lost 70 of its 100 HP every second and a hurt drone +// drew the medics' heals. The 吟游者 trait (item 32) cannot refill such a bar either: their HP never moves. +// Run: node --test test/sim/feedback5-countdown-summons.test.js + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { makeBattle, checkInvariants } from '../helpers/battleHarness.js'; +import { unitTuple } from '../../server/sim/snapshot.js'; +import { COUNTDOWN_SUMMONS } from '../../server/sim/content/tokens.js'; + +/** [label, owner entry (uid 1), token id, life in s] — every placed countdown summon of the pool and of the 自选 pool. */ +const CASES = [ + ['赫默 医疗探机', { chessId: 'chess_char_2_02_a' }, 'token_10000_silent_healrb', 10], + ['巫恋 诅咒娃娃', { chessId: 'chess_char_3_15_a' }, 'token_10006_vodfox_doll', null], + ['浊心斯卡蒂 海嗣', { chessId: 'chess_char_6_04_a' }, 'token_10017_skadi2_dedant', 25], + ['温蒂 工程蓄水炮', { diy: { slot: 5, charId: 'char_400_weedy', skillIndex: 0, uniEquipId: null } }, 'token_10009_weedy_cannon', 20], + ['莱伊 沙地兽', { diy: { slot: 5, charId: 'char_4117_ray', skillIndex: 0, uniEquipId: null } }, 'token_10034_ray_sndbst', 25], + ['鸿雪 打字机', { diy: { slot: 5, charId: 'char_4055_bgsnow', skillIndex: 0, uniEquipId: null } }, 'token_10026_bgsnow_subbow', 25], +]; + +function field(owner, tokenId) { + const h = makeBattle({ + units: [{ uid: 1, row: 10, col: 3, ...owner }, { uid: 2, kind: 'token', tokenId, ownerUid: 1, row: 10, col: 4 }], + autoFinish: false, timeLimit: 120, seed: 3, flags: { dpInit: 60, dpMax: 99 }, + }); + h.step(); + return { h, t: h.b.allyUnits.find((u) => u.kind === 'token' && u.defId === tokenId) }; +} +const bar = (h, t) => unitTuple(t, h.b.time)[3] / unitTuple(t, h.b.time)[4]; + +for (const [label, owner, tokenId, lifeWant] of CASES) { + test(`${label}: 无敌 + 禁疗, no outside damage (活性源石 tick), no heal, a bar that runs down with its life`, () => { + assert.ok(COUNTDOWN_SUMMONS.has(tokenId)); + const { h, t } = field(owner, tokenId); + assert.ok(t && t.alive, 'deployed from its piece'); + assert.ok(t.countdown, 'its life is counting down'); + const life = t.countdown.until - t.countdown.from; + if (lifeWant != null) assert.ok(Math.abs(life - lifeWant) < 1e-6, `life ${life} s`); + const f = t.s.flags; + assert.ok(f.invulnerable && f.noHeal && f.healFree, '无敌 + 禁疗'); + // a 活性源石 tick (true, 无来源, 环境) and an enemy-style hit change nothing + const hp0 = t.hp; + h.b.dealDamage(null, t, { amount: 70, type: 'true', canDodge: false, tags: ['terrain'] }); + h.b.dealDamage(null, t, { amount: t.s.maxHp * 3, type: 'phys' }); + assert.equal(t.hp, hp0, 'no damage reaches it'); + assert.equal(h.b.heal(h.unit(1), t, 500), 0, 'no heal reaches it'); + // the bar: full at the start, ~3/4 then ~1/4 of the way through, gone at the end + const from = t.countdown.from; + h.run(from + life * 0.25 - h.b.time); + assert.ok(Math.abs(bar(h, t) - 0.75) < 0.05, `a quarter in: ${bar(h, t).toFixed(3)}`); + h.run(life * 0.5); + assert.ok(Math.abs(bar(h, t) - 0.25) < 0.05, `three quarters in: ${bar(h, t).toFixed(3)}`); + assert.equal(t.hp, t.s.maxHp, 'its HP itself never moves'); + h.run(life * 0.25 + 0.2); + assert.ok(!t.alive, 'it leaves when the bar is empty'); + checkInvariants(h.b); + assert.equal(h.b.errors.length, 0, JSON.stringify(h.b.errors[0])); + }); +} + +test('a summon that is attacked keeps its HP as HP: 伺夜\'s 狼群 takes damage, is no countdown summon', () => { + const { h, t } = field({ chessId: 'chess_char_3_19_a' }, 'token_10028_vigil_wolf'); + assert.ok(!COUNTDOWN_SUMMONS.has(t.defId) && t.countdown == null && !t.s.flags.invulnerable); + h.b.dealDamage(null, t, { amount: 100, type: 'true', canDodge: false, tags: ['terrain'] }); + assert.ok(t.hp < t.s.maxHp); + assert.equal(unitTuple(t, h.b.time)[3], Math.ceil(t.hp), 'its bar is its HP'); +}); diff --git a/tools/golden.mjs b/tools/golden.mjs index f55caf3b..95ddf30b 100644 --- a/tools/golden.mjs +++ b/tools/golden.mjs @@ -739,11 +739,14 @@ const MATCH_DIY = Object.freeze({ const MATCH_DIY_SEED = 70; /** - * The NORMAL chess the 补位 match's human seat does not own: operators its AI fields with seed 14 (瑕光 → 郁金香 from - * round 4, 异客 → Stormeye, 耀骑士临光 → 郁金香 later; the digest's `standIns` lists them per round). + * The NORMAL chess the 补位 match's human seat does not own: operators its AI fields with seed 14 (the digest's `standIns` + * lists them per round; test/golden-standins.test.js wants at least 3 different ones). 缄默德克萨斯 and 铃兰 joined in 0.2.0 + * when the 调度中心 upgrade's new card (DESIGN §25.19) changed the seat's shop: 忍冬 → Sharp from round 4, 缄默德克萨斯 → + * Misery from round 7, 铃兰 → 预备干员-辅助 from round 9, 安洁莉娜 → Raidian in round 12. */ const MATCH_NOT_OWNED = Object.freeze([ - 'chess_char_3_12_a', 'chess_char_3_18_a', 'chess_char_5_20_a', 'chess_char_6_05_a', 'chess_char_6_06_a', 'chess_char_6_17_a', 'chess_char_6_19_a', + 'chess_char_3_12_a', 'chess_char_3_18_a', 'chess_char_4_16_a', 'chess_char_5_10_a', 'chess_char_5_20_a', 'chess_char_6_05_a', 'chess_char_6_06_a', + 'chess_char_6_17_a', 'chess_char_6_19_a', ]); const pieceStr = (p, r, c) => `${p.id.replace(/^chess_char_/, '')}@${r},${c}${p.dir && p.dir !== 'RIGHT' ? p.dir[0] : ''}${p.items && p.items.length ? `[${p.items.map((i) => i.id.replace(/^chess_item_/, '')).join('+')}]` : ''}`;