feedback5 WJ: 伺夜's 狼群 takes its official 战术点形态 and S1 领袖的呼唤 acts by the pack's state (PRTS 狼群领袖 / S1 备注)

The fatal hit on the last 狼影, or a 撤退 while 伺夜 stands, no longer kills the pack. It enters its 战术点形态, the
remake's knocked-out piece: off the fight (no block, no attack, not targetable), kept, its tile reserved, 0 狼影. After
the 狼影 recovery time it comes back on its tile at full HP with 1 狼影 and a fresh 狼影 cycle. That time is the token
talent's interval, read from the data: 25 s on both chess. Until now it came back after the token's 10 s redeploy time
with 2 狼影.

Both packs share this (tokens.js installWolfTacticalPoint): the prep-placed 狼群 piece and the 伺夜 kit's own pack.
- A return timer carries the form's seq and a return cancels it, so a stale timer never revives the pack early.
- When 伺夜 leaves, a standing pack still leaves with him ('expired', no form). A pack in its form ends it with no
  return; his redeploy brings a fresh pack.

S1 casts when ready while the pack is on the field in either form, and always pays +cost DP:
① in its 战术点形态 the pack comes back at once with 1 狼影 and a fresh cycle;
② below the cap, +1 狼影, the pack's own cycle untouched;
③ at the cap, the pack's HP is reset to max.
S2 still needs a standing pack: its 备注 says nothing of the form [ASSUMED]. A held S2 gift stays on the pack through
the form [ASSUMED].

fx: the kit's pack keeps 'summon' / 'revive', the piece keeps 'wolfShadow' / 'wolfShadowLost'. Entering the form shows
'wolfShadowLost' with count 0. No new kinds, no client change. Data unchanged.

Docs: DESIGN §5.6 (SP_FULL note), PLAYING (作战中, 自动触发), SIM (token pieces), the tokens.js / kit comments.

Tests:
- kits_alt_t3, new: 3_19 战术点形态 (both chess × both packs), S1 ①②③ with the stale-timer guard, 撤退 / 伺夜 leaving.
- kits_alt_t3, rewritten: the S1 no-pack test, and the S2 auto-cast test (no cast in the form, a cast once the pack is
  back).
- kits_t3 3_19 wolf pack and tokens_devices 狼群 board piece: the form, then a return with 1 狼影.

Golden (each fields 伺夜 with his pack knocked out at its last 狼影 while he stands: 25 s out and 1 狼影 instead of
10 s and 2):
- roster-007 and roster-009: the fights change.
- roster-028 (elite S1): S1 brings the pack back at once.
- roster-026 and roster-040: 伺夜 falls before the old return; only the new 'wolfShadowLost' fx moves.
- bond-siracusaShip-high.
- matches solo-NORMAL-1, coop2-NORMAL-3, coop3-ABYSS-5, coop2-NORMAL-8-serverrun: the bots field 伺夜.
This commit is contained in:
sganggs committed 2026-10-05 19:47:12 +08:00
1 parent a98b02477d
commit 4304eb057c
11 files changed
+577 -222

No files matched your search

+1 -1
View File
@@ -305,7 +305,7 @@ Engine helpers (content must use these, never touch internals): `battle.dealDama
SP: `spType` `INCREASE_WITH_TIME` (+`spRecovery`/s), `INCREASE_WHEN_ATTACK` (+1 per attack), `INCREASE_WHEN_TAKEN_DAMAGE` (+1 per hit taken); starts at `initSp`; no SP gain while a duration skill is active. `maxChargeTime > 1` ⇒ charges.
Auto-trigger rules (`skillTrigger.rule` in data — the official 技能策略, PRTS 卫戍协议/帮助 §作战阶段 技能操作; §20.2): `DEFAULT` (SP ready AND about to attack/heal AND an enemy/injured ally inside the **initial** range, or an enemy the unit blocks; with `trigger.allies` + `hpAtMost` an ally of the skill grid at or below that HP ratio — 塞雷娅 S1 急救, whose cast replaces that attack and is withdrawn with its charge if the ally recovers first), `SKILL_RANGE` (a MANUAL skill with a 技能范围 of its own, not an attack-range change: any living enemy, stealthed ones included, on the skill range, checked every tick — "仅在技能范围内存在敌人(无视其不可选中)时释放技能"; 13 skills / 26 records by the official rule, 14 / 28 in the data with 余 S2's deviation, §22.10), `ACTIVE_RANGE` (the owner's deliberate deviation of 2026-10-05, 0.2.0: a MANUAL skill on the basic strategy — or, by the owner's further decisions of the same day, on the `SEARCH` row, and 深巡 S2 on top of its §21.29 DEFAULT — whose attack range while it runs strictly contains the operator's own range — its rangeId grid, or the own range grown by `ability_range_forward_extend` — checks the DEFAULT condition on that running range every tick, no attack needed; `customRangeGrid` = the running range, `rawRule` the official row (DEFAULT, SEARCH, 深巡's TAKE_DAMAGE); 39 skills / 78 records, of them 薄绿 S1, 蜜蜡 S1, 卡涅利安 S3, 玛恩纳 S2 and 安洁莉娜 S3 from SEARCH; 烛煌 S2 / S3, whose ranges do not contain her 3-1, keep DEFAULT), `TAKE_DAMAGE` (on taking damage — any damage, 无来源 included, never a 流失; every MANUAL 重装 skill, §21.12 — but the six of the deliberate deviation, §21.29: 深巡 / 雷蛇 S2, 号角 S2 / S3 and 灰毫 S1 / S2 are `DEFAULT`, `rawRule` keeping the official TAKE_DAMAGE — 深巡 S2 then `ACTIVE_RANGE` on its 3-2; and 余 S2, `SKILL_RANGE` on its x-1 (§22.10)), `SP_FULL` (immediately — 执旗手 / 战术家 / 吟游者 MANUAL skills, AUTO DP skills such as 焰尾 S1, 凛御银灰 S1 (伺夜 S1 / S2 too, but only while his pack is on the field — PRTS 备注 「仅场上存在狼群时可触发技能」, the kit's own cast; the owner's decision of 2026-10-05 for S1), and, by their kits, AUTO skills whose effects act on allies only: 引星棘刺 S1, 浊心斯卡蒂 S2, 纯烬艾雅法拉 S1 — the owner's decision of 2026-10-05 for the last), `CUSTOM_RANGE` (enemy inside the custom grid), `SEARCH` (an enemy inside the initial range, checked every tick — no attack needed; 解放者 / 阵法术师 MANUAL skills and 安洁莉娜 S2, those without a larger running range), `GDGLOW_SKILL_2` (a valid target anywhere on the field: 荒芜拉普兰德, 纯烬艾雅法拉 S3), special ids. The class rows cover **every MANUAL skill** of the class and never an AUTO skill (AUTO skills keep their own rule — an inference: PRTS names whole classes; the data's `skillIndex 0` / `-1` sentinel argues the other way, §20.2). **Operation cooldown**: the engine auto-casts a MANUAL skill no sooner than `AUTO_OP_COOLDOWN` (3 s) after its previous cast and after the unit's battle-start deployment ("自动操作具有3s冷却,在完成一次操作或作战开始时部署的单位将进入冷却"; `Battle._deploy` initial, `battle.flags.startOpCooldown`); kits with an automatic cast of their own check `skill.opCooling`; a pending "next attack" never takes a second charge. PASSIVE skills are always on.
Auto-trigger rules (`skillTrigger.rule` in data — the official 技能策略, PRTS 卫戍协议/帮助 §作战阶段 技能操作; §20.2): `DEFAULT` (SP ready AND about to attack/heal AND an enemy/injured ally inside the **initial** range, or an enemy the unit blocks; with `trigger.allies` + `hpAtMost` an ally of the skill grid at or below that HP ratio — 塞雷娅 S1 急救, whose cast replaces that attack and is withdrawn with its charge if the ally recovers first), `SKILL_RANGE` (a MANUAL skill with a 技能范围 of its own, not an attack-range change: any living enemy, stealthed ones included, on the skill range, checked every tick — "仅在技能范围内存在敌人(无视其不可选中)时释放技能"; 13 skills / 26 records by the official rule, 14 / 28 in the data with 余 S2's deviation, §22.10), `ACTIVE_RANGE` (the owner's deliberate deviation of 2026-10-05, 0.2.0: a MANUAL skill on the basic strategy — or, by the owner's further decisions of the same day, on the `SEARCH` row, and 深巡 S2 on top of its §21.29 DEFAULT — whose attack range while it runs strictly contains the operator's own range — its rangeId grid, or the own range grown by `ability_range_forward_extend` — checks the DEFAULT condition on that running range every tick, no attack needed; `customRangeGrid` = the running range, `rawRule` the official row (DEFAULT, SEARCH, 深巡's TAKE_DAMAGE); 39 skills / 78 records, of them 薄绿 S1, 蜜蜡 S1, 卡涅利安 S3, 玛恩纳 S2 and 安洁莉娜 S3 from SEARCH; 烛煌 S2 / S3, whose ranges do not contain her 3-1, keep DEFAULT), `TAKE_DAMAGE` (on taking damage — any damage, 无来源 included, never a 流失; every MANUAL 重装 skill, §21.12 — but the six of the deliberate deviation, §21.29: 深巡 / 雷蛇 S2, 号角 S2 / S3 and 灰毫 S1 / S2 are `DEFAULT`, `rawRule` keeping the official TAKE_DAMAGE — 深巡 S2 then `ACTIVE_RANGE` on its 3-2; and 余 S2, `SKILL_RANGE` on its x-1 (§22.10)), `SP_FULL` (immediately — 执旗手 / 战术家 / 吟游者 MANUAL skills, AUTO DP skills such as 焰尾 S1, 凛御银灰 S1 (伺夜 S1 / S2 too, but only while his pack is on the field — PRTS 备注 「仅场上存在狼群时可触发技能」, the kit's own cast; the owner's decision of 2026-10-05 for S1; S1 also while the pack sits in its 战术点形态, which the cast ends — S2 waits for it to come back [ASSUMED]), and, by their kits, AUTO skills whose effects act on allies only: 引星棘刺 S1, 浊心斯卡蒂 S2, 纯烬艾雅法拉 S1 — the owner's decision of 2026-10-05 for the last), `CUSTOM_RANGE` (enemy inside the custom grid), `SEARCH` (an enemy inside the initial range, checked every tick — no attack needed; 解放者 / 阵法术师 MANUAL skills and 安洁莉娜 S2, those without a larger running range), `GDGLOW_SKILL_2` (a valid target anywhere on the field: 荒芜拉普兰德, 纯烬艾雅法拉 S3), special ids. The class rows cover **every MANUAL skill** of the class and never an AUTO skill (AUTO skills keep their own rule — an inference: PRTS names whole classes; the data's `skillIndex 0` / `-1` sentinel argues the other way, §20.2). **Operation cooldown**: the engine auto-casts a MANUAL skill no sooner than `AUTO_OP_COOLDOWN` (3 s) after its previous cast and after the unit's battle-start deployment ("自动操作具有3s冷却,在完成一次操作或作战开始时部署的单位将进入冷却"; `Battle._deploy` initial, `battle.flags.startOpCooldown`); kits with an automatic cast of their own check `skill.opCooling`; a pending "next attack" never takes a second charge. PASSIVE skills are always on.
**SkillSpec** (authored per chess in `sim/content/kits/tierN.js`; values come from the chess's skill blackboard at its level — normal Lv4, elite Lv7 — so a spec is a *function of the blackboard*):
```js
+2 -2
View File
@@ -68,10 +68,10 @@
- 拖动时模型一直在指针 / 手指下(脚在指针下方约半格,鼠标和触屏一样),**指针所在的格子**就是落点并高亮;装备拖到哪个格子,就配发给那个格子上的干员;在商店栏上松手则放回原位。
- 点击干员显示攻击范围和底部按钮(「撤退」回到整备区、「出售 +1」);右键 / 长按查看详细信息:头像旁是盟约,下面第一项就是干员的**特质**(如「休整期结束时 …」),之后是特性、属性、攻击范围、技能、模组、装备、天赋。属性是**实时数值**:作战中每秒刷新 4 次(当前生命、攻击、防御、法抗、攻击间隔、阻挡),休整期显示棋盘上的干员**开战时**的数值(已算入装备、盟约层数、特质、策略与机变效果);高于基础值为绿色、低于为红色并标出差值(攻击间隔变短算增益),鼠标悬停可看基础值。
- **召唤物手动摆放**:把带可部署召唤物的干员放上棋盘后,它的召唤物会进入整备区(赫默的医疗探机、巫恋的诅咒娃娃、凯瑟琳的 2 个支援装置、浊心斯卡蒂的海嗣、伺夜的狼群、缪尔赛思的流形;同一干员的召唤物叠在一格,不占部署位),像干员一样拖到棋盘上并选方向。凯瑟琳的支援装置给**它面朝的那一格**上的干员加屏障,所以要放在干员旁边、朝向干员。**战术家的援军**(伺夜的狼群、缪尔赛思的流形,也就是选战术点)和官方一样**只能放在召唤者的攻击范围内**:拖动时只有这些格子会亮起;之后在原地给战术家换朝向时,不在新攻击范围里的援军会退回整备区(还在范围里的保持不动;整备区和临时整备区都满、援军无处可退时,这次换朝向不生效并提示「整备区已满」)。撤退或出售干员时它的召唤物一起移除;**移动干员到别的格子时(包括把召唤物拖到干员身上交换位置),它已摆放的召唤物会退回整备区**,需要重新摆放。整备区满时退回的召唤物进入临时整备区,休整期结束还没处理的会在下一回合重新回到整备区。
- 作战中:海嗣、狼群、流形、支援装置开战时就在摆放的位置部署;医疗探机、诅咒娃娃开战时也在摆放的位置免费部署一次(持续 10 / 15 秒,和官方一样不需要先开技能),之后所属干员每次发动技能时再次在原位出现;所属干员被击倒、再部署回场时,技能给的那一个如果还没出现,会立刻出现。巫恋退场时诅咒娃娃随之撤退,赫默退场时已出现的医疗探机不受影响。医疗探机、诅咒娃娃、支援装置、海嗣没有摆放就不会出现;狼群、流形(战术家的援军)没有摆放时会在战术点出现。
- 作战中:海嗣、狼群、流形、支援装置开战时就在摆放的位置部署;医疗探机、诅咒娃娃开战时也在摆放的位置免费部署一次(持续 10 / 15 秒,和官方一样不需要先开技能),之后所属干员每次发动技能时再次在原位出现;所属干员被击倒、再部署回场时,技能给的那一个如果还没出现,会立刻出现。巫恋退场时诅咒娃娃随之撤退,赫默退场时已出现的医疗探机不受影响。医疗探机、诅咒娃娃、支援装置、海嗣没有摆放就不会出现;狼群、流形(战术家的援军)没有摆放时会在战术点出现。狼群最后一只狼影被击倒后进入战术点形态:暂时离开战斗(不阻挡、不攻击、不会被攻击,格子仍然占着),经过狼影的恢复时间(25 秒)后在原地满血、带着 1 只狼影回来,之后重新开始每 25 秒加 1 只狼影;伺夜退场时狼群随之撤退,伺夜再部署回场时狼群重新带着 2 只狼影出现。
- **阻挡**看接触:干员阻挡中心距离约 0.7 格以内、与它接触的敌人(地面干员挡不住飞行和近地悬浮的敌人)。敌人卡在两名干员之间时,阻挡它的干员倒下、撤退或被晕眩后,另一名仍与它接触、还有空余阻挡数的干员会**立刻接替阻挡**;都没有空位时敌人才会走掉。迎面走来的敌人会停在干员格子前面。**阻挡了就一定能打到**:任何干员(包括站在近战位的远程干员和召唤物)总能攻击自己阻挡的敌人,不管它在不在攻击范围内、在干员哪一侧,并且优先攻击它(医疗干员除外:咒愈师以外的医疗挡住敌人时照常治疗队友,不会攻击它;「停止攻击」类技能期间也除外;技能原文另有限制的也按原文:普罗旺斯二技能开启时不以生命值高于 80% 的敌人为目标,寒檀二技能的冰凌随机落在攻击范围内的地块上)。
- 开战时干员按**逐列从左到右、每列从上到下**的顺序部署(最左一列最先,与阿戈尔吞噬「更靠左和靠上」的顺序相同),召唤物在所有干员之后。被阻挡的敌人先打阻挡它的干员;没被阻挡的远程敌人先打嘲讽等级高的,同级时打最后部署的(召唤物、最右一列靠下的干员和刚再部署的干员更容易挨打)。隐匿的干员只会被它阻挡的敌人攻击,没被它挡住的敌人的溅射、爆炸、范围技能、留下的伤害区域(包括假想敌:蚀裂被击倒后喷出的毒雾)以及深池伙友卫队、扎罗的减攻速光环也落不到它身上(和官方一样;写明「无视无法选择」的效果如污染秽蚀照常能打到,寒霜的减攻速光环和“墓碑”这类地图效果照常生效);**迷彩**(如忍冬三技能期间击倒敌人后、寒芒克洛丝一技能)同样只会被它阻挡的敌人攻击,也显示为半透明,但躲不开溅射、爆炸和范围伤害(同时打范围内所有单位的普通攻击——如“斩胄之剑”“破胄之锤”“灵幛”——打不到它),也不算隐匿(不触发叙拉古等「隐匿时」效果)。叙拉古的隐匿按时(32 秒 + 每层 0.4 秒)结束;忍冬的迷彩按官方持续到她下一次开技能。**蒂比起飞**期间只挡飞行敌人,地面敌人不会选她为目标(普通攻击、溅射、技能范围、燃烧区域和要选中目标的减益都落不到她身上);飞行和悬浮敌人、写明「无视无法选择」的效果(如污染秽蚀)照常能打到她;地面敌人的光环和全场效果(如“墓碑”让全场攻击力、防御力减半)、她攻击敌人时招来的反制(如重装侦察兵的暴露)照常生效,起飞前已挂上的持续伤害也照常跳。
- **技能自动释放**按官方「技能策略」:一般的手动技能在技能就绪、即将攻击 / 治疗且初始攻击范围内有敌人(治疗技能:有受伤的队友)时释放;**带独立技能范围**的技能(如德克萨斯「剑雨」、凛御银灰「御敌的锋锐」、锏「归于宁静」,不含「攻击范围扩大」类)在**技能范围内出现敌人**时就释放,不必先攻击;**开启后攻击范围比平时大**的手动技能(如莫斯提马「序时之匙」、银灰「真银斩」、史尔特尔「黄昏」、白面鸮「脑啡肽」)在**开启后的攻击范围内出现敌人**(治疗技能:有受伤的队友)时就释放,不必等敌人走进平时的攻击范围,这是本作按玩家反馈有意与官方不同的地方;**重装干员**的手动技能在受到伤害时释放(例外:深巡、雷蛇的二技能,号角的二、三技能,灰毫的一、二技能按玩家反馈改为攻击范围内有敌人时就释放(深巡二技能按开启后更远的攻击范围判断),余的二技能「厚礼上宾」改为周围两格(技能范围)内有敌人时就释放,这是本作有意与官方不同的地方;深巡一技能仍要受到伤害);执旗手 / 战术家 / 吟游者技能就绪即释放,解放者 / 阵法术师在初始攻击范围内有敌人时释放(开启后攻击范围扩大的技能——薄绿「风语」、蜜蜡「沙暴扩散」、卡涅利安「食噬之印」、玛恩纳「未宽解的悲哀」,以及安洁莉娜「秘杖·反重力模式」——同样按开启后的攻击范围判断)。**自动触发**的技能按其本身规则:只作用于队友的(如引星棘刺「度算浪波」、浊心斯卡蒂「同葬无光之愿」、纯烬艾雅法拉「无声润物」)技力满就释放,不用等敌人或伤员;伺夜「领袖的呼唤」「领袖的馈赠」只在场上有狼群时释放(狼群被击倒后要等它重新出现);又如古米「备用军粮」在身边(技能范围内)有受伤的队友时就会发动,下一次攻击改为治疗该队友(不用先接敌;她的二技能「食粮烹制」是手动技能,按重装规则在受到伤害时释放);塞雷娅「急救」在她即将攻击时、身边有生命不高于一半的队友才发动,这次攻击直接改为治疗;新约能天使二技能的护盾按官方给攻击范围内**仇恨最高**的队友(嘲讽等级高的优先,同级时给最后部署的),不一定是站在最前面挨打的那个。手动技能的自动释放有 3 秒冷却:开战部署后先等 3 秒,之后同一干员两次自动释放至少间隔 3 秒(可充能的技能不会同时放完)。
- **技能自动释放**按官方「技能策略」:一般的手动技能在技能就绪、即将攻击 / 治疗且初始攻击范围内有敌人(治疗技能:有受伤的队友)时释放;**带独立技能范围**的技能(如德克萨斯「剑雨」、凛御银灰「御敌的锋锐」、锏「归于宁静」,不含「攻击范围扩大」类)在**技能范围内出现敌人**时就释放,不必先攻击;**开启后攻击范围比平时大**的手动技能(如莫斯提马「序时之匙」、银灰「真银斩」、史尔特尔「黄昏」、白面鸮「脑啡肽」)在**开启后的攻击范围内出现敌人**(治疗技能:有受伤的队友)时就释放,不必等敌人走进平时的攻击范围,这是本作按玩家反馈有意与官方不同的地方;**重装干员**的手动技能在受到伤害时释放(例外:深巡、雷蛇的二技能,号角的二、三技能,灰毫的一、二技能按玩家反馈改为攻击范围内有敌人时就释放(深巡二技能按开启后更远的攻击范围判断),余的二技能「厚礼上宾」改为周围两格(技能范围)内有敌人时就释放,这是本作有意与官方不同的地方;深巡一技能仍要受到伤害);执旗手 / 战术家 / 吟游者技能就绪即释放,解放者 / 阵法术师在初始攻击范围内有敌人时释放(开启后攻击范围扩大的技能——薄绿「风语」、蜜蜡「沙暴扩散」、卡涅利安「食噬之印」、玛恩纳「未宽解的悲哀」,以及安洁莉娜「秘杖·反重力模式」——同样按开启后的攻击范围判断)。**自动触发**的技能按其本身规则:只作用于队友的(如引星棘刺「度算浪波」、浊心斯卡蒂「同葬无光之愿」、纯烬艾雅法拉「无声润物」)技力满就释放,不用等敌人或伤员;伺夜「领袖的呼唤」「领袖的馈赠」只在场上有狼群时释放:狼群处于战术点形态时,「领袖的呼唤」也会释放并让它立刻带着 1 只狼影回到场上(狼影未满时改为加 1 只狼影,已满时把狼群的生命值回满),「领袖的馈赠」则要等狼群回来;又如古米「备用军粮」在身边(技能范围内)有受伤的队友时就会发动,下一次攻击改为治疗该队友(不用先接敌;她的二技能「食粮烹制」是手动技能,按重装规则在受到伤害时释放);塞雷娅「急救」在她即将攻击时、身边有生命不高于一半的队友才发动,这次攻击直接改为治疗;新约能天使二技能的护盾按官方给攻击范围内**仇恨最高**的队友(嘲讽等级高的优先,同级时给最后部署的),不一定是站在最前面挨打的那个。手动技能的自动释放有 3 秒冷却:开战部署后先等 3 秒,之后同一干员两次自动释放至少间隔 3 秒(可充能的技能不会同时放完)。
- **推开 / 拉拽**按官方「力度 − 重量」决定距离:例如野鬃二技能(中等力度)把重量 0 / 1 / 2 / 3 的敌人推开约 2.1 / 1.7 / 0.4 / 0.1 格,重量 4 以上推不动——轻型敌人反而推得更远。野鬃二技能沿她的朝向推开:正前方 45° 以内按上面的距离,身后、侧面或贴身的敌人只被轻推一下(约 0.4 格以内);琳琅诗怀雅三技能的金币则从她身边向外推开,落在她前方 2–4 格的范围(官方「范围2-4」)内的地面敌人和她阻挡的敌人身上。攻击速度最低为 20(沼泽、寒霜等减速叠满时也不会更慢)。
- **元素损伤条**在单位自己的血条正下方:左边是元素图标,白条是剩余的元素值,只有真正爆发时才会耗尽;爆发后条变成元素的颜色、图标闪烁,在冷却期间慢慢回满(这段时间不会再积累元素损伤)。
- **敌人**:远程敌人的射程按官方从干员的碰撞体积(半径 0.25 格)算起,比数据上的攻击范围半径远 0.25 格——例如萨卡兹枯朽战车(射程 2.2)能打到中心 2.45 格以内、站在低地的干员。没被阻挡的远程敌人每次攻击都会站定、播完攻击动作,两次攻击之间才继续前进。它的第一下攻击就是「秽蚀轰击」(在目标处留下半径 1.7、持续 10 秒的污染区:低地每秒 50、高台 25 点真实伤害,同名不叠加),被阻挡时普通攻击伤害翻倍。“萨科塔之翼”等血量第一次低于一半时会在原地小范围乱飞 5 秒(官方叫「恐惧」);叙拉古、妮芙等干员造成的恐惧会让敌人朝远离干员的方向逃跑。隐匿的敌人只有被阻挡(或被揭示)时才能被选为攻击目标,溅射、技能的圆形范围伤害也打不到没被挡住的隐匿敌人(和官方一样);阻挡解除后 3 秒内它仍然可以被选中(官方页面注明 0 秒或 1 秒的敌人按页面),之后才重新隐匿:深池逐火战士每次被击倒后先重生 1 秒,然后变成继续往前走的隐匿「余烬」(需要 5 次伤害击倒,护卫的「火灰」10 次),一般要用干员挡住它才能选中(挡住时它不再半透明;被阻挡时被击倒的,重生结束后约 2 秒内也能用远程或范围伤害补刀),10 秒内没打掉就满血站起来;敌人重生时身上的减速、恐惧等状态会被清空、技能冷却从头开始,被恐惧的逐火战士变成余烬后也能被挡住;转译基底·α 原始形态受到的伤害全部无效,受到第 4 次物理伤害、第 4 次法术伤害或被阻挡时花 2 秒变成寻仇者、特战术师或幽灵,之后才能被击倒。
+4 -1
View File
@@ -128,7 +128,10 @@ device or 海嗣 that was not placed never appears (the hidden 待部署区 depl
流形 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
report #9 after 0.1.0) — and the kits re-use the piece's tile for a re-summoned pack only while it is a free, standable
tile of her initial range (kits/shared/tier3.js `tacticalPoint`, tokens.js `ensureReinforcement`). The start deploy
tile of her initial range (kits/shared/tier3.js `tacticalPoint`, tokens.js `ensureReinforcement`). A 狼群 whose last 狼影
falls (or that is retreated) is not re-summoned: it stays its tile's piece in its 战术点形态 — off the fight, the tile
reserved — for the 狼影 interval and comes back there with one 狼影 (tokens.js `installWolfTacticalPoint`, the piece and
the 伺夜 kit's own pack alike; 伺夜 S1 ① ends the form at once, 伺夜 leaving ends it without a return). The start deploy
is the user's call after playtest #6 (DESIGN §20); `shared/constants.js SKILL_SUMMON_START_DEPLOY = false` would bring
back the playtest #4 reading (only with the skill) in the sim and the summon card's hint (docs/PLAYING.md §4 and this
passage must follow; test/ui/playtest6_summons.test.js checks).
@@ -2,18 +2,23 @@
// Conventions of the tier-3 kits: ../shared/tier3.js; kit contract and rules: ../README.md.
import { num, defOf, talentBb, selectedId, altSkills, alive, fx, textNum, tacticalPoint } from '../shared/tier3.js';
import { installWolfTacticalPoint, wolfReturnNow, wolfShadowInterval, wolfTacticalPoint } from '../../tokens.js';
/** Tokens `tokenId` summoned by / placed for `owner` (board pieces included). */
const tokensOf = (battle, owner, tokenId) => battle.allyUnits.filter((t) => t.kind === 'token' && t.defId === tokenId && t.ownerUnit === owner && !t.mem.isClone);
export default {
// ---- 3_19 伺夜 · 战术家 — the tactical reinforcement is the wolf pack (狼群领袖: 2 wolves, +1 every 25 s up to 3, each
// wolf = +1 block and one more bite, a wolf is lost instead of the pack dying); 狼群天性: DEF ignore vs pack-blocked
// wolf = +1 block and one more bite, a wolf is lost instead of the pack dying; the fatal hit on the last one, or a
// 撤退, puts the pack in its 战术点形态 — out of the fight for the 狼影 interval, then back on its tile with one
// wolf: tokens.js installWolfTacticalPoint, PRTS 狼群领袖 备注); 狼群天性: DEF ignore vs pack-blocked
// enemies; S3 领袖的尊严: DP over time, 三连击, bonus arts vs pack-blocked enemies; 精锐 module: pack takes less
// damage from the enemies it blocks (token module talent). A 狼群 piece placed in the prep phase is the pack.
// S1 领袖的呼唤 (自动触发, only while the pack is on the field): +cost DP and one more “狼影” (≤ the talent's
// maximum); S2 领袖的馈赠: +cost DP, the pack recovers hp_ratio of its max HP and its next attack hits ×atk_scale —
// a kill by that attack gives +cost DP.
// S1 领袖的呼唤 (自动触发, only while the pack is on the field — standing or in its 战术点形态): +cost DP, and by the
// pack's state (PRTS 备注) ① 战术点形态: back at once with one “狼影” and a fresh growth cycle, ② below the
// talent's maximum: one more “狼影” (its own cycle untouched), ③ at the maximum: the pack's HP to max; S2 领袖的馈赠
// (only while the pack stands): +cost DP, the pack recovers hp_ratio of its max HP and its next attack hits
// ×atk_scale — a kill by that attack gives +cost DP.
// 精锐 module TAC-Y: trait ×165 % (profession layer) and "援军阻挡的敌人更容易受到我方的攻击": the pack's token module
// talent taunt_level (+1) goes to the enemies it blocks — the enemy-side 嘲讽等级 our operators target first
// (targeting.js; research 05: "更容易受到我方的攻击" = enemy taunt.taunt_level), never to the pack itself.
@@ -27,7 +32,13 @@ export default {
const hits = textNum(d.skill?.description, /(\d+|[一二两三四五])连击/, 3);
const bonus = num(bb['attack@vigil_s_3.atk_scale'], 0);
const pen = num(t1.def_penetrate_fixed, 0);
/** The standing pack (狼群天性, S3, the TAC-Y mark, S2). */
const wolfOf = (u) => (u.trait.reinforcement && u.trait.reinforcement.alive ? u.trait.reinforcement : null);
/** The pack on the field in either form — standing, or in its 战术点形态 (only while 伺夜 stands: tokens.js). */
const packOf = (u) => {
const w = u.trait.reinforcement;
return w && (w.alive || wolfTacticalPoint(w)) ? w : null;
};
const wolfKit = (vigil) => ({
skill: null,
trait: { hitsFn: (b, w) => Math.max(1, w.mem.wolves || 1) },
@@ -35,11 +46,10 @@ export default {
const tal = w.def.talents || [];
const wb = tal[0]?.bb ?? {};
const per = num(wb.block_cnt, num(wb['vigil_wolf_t_1_enhance[trigger].block_cnt'], 1));
const grow = num(wb.interval, num(wb['vigil_wolf_t_1_enhance[trigger].interval'], 25));
// the 狼影 interval from the data (tokens.js wolfShadowInterval): the growth cycle — and the 战术点形态's length
const grow = wolfShadowInterval(w.def);
// (mem.shadows mirrors the count for content reading tokens.js wolfShadows())
const apply = () => { w.mem.shadows = w.mem.wolves; battle.addBuff(w, { key: 'token:wolves', mods: { blockCnt: per * w.mem.wolves }, allowDead: true }); };
w.mem.wolves = Math.max(1, Math.min(maxWolves, initial));
apply();
w.mem.addWolf = () => {
if (!w.alive || w.mem.wolves >= maxWolves) return false;
w.mem.wolves++;
@@ -47,7 +57,23 @@ export default {
fx(battle, 'summon', w, { src: vigil.id, wolves: w.mem.wolves });
return true;
};
battle.every(grow, () => w.mem.addWolf(), { owner: w });
// every deployment: the initial count — or one “狼影” back from the 战术点形态 (tokens.js wolfReturnNow: "持续时间
// 结束后狼影层数变回1层", S1 ①) — and a fresh growth cycle (the previous one stops at its next tick)
battle.on('deploy', (ctx) => {
if (ctx.unit !== w) return;
const back = w.mem.wolfReturn;
w.mem.wolves = back ? 1 : Math.max(1, Math.min(maxWolves, initial));
apply();
if (back && back.src == null) fx(battle, 'summon', w, { wolves: w.mem.wolves }); // (S1 ① shows its own)
const seq = w.deploySeq;
if (grow > 0) {
battle.every(grow, (b, sched) => {
if (!w.alive || w.deploySeq !== seq) { sched.cancel(); return; }
w.mem.addWolf();
}, { owner: w });
}
}, { owner: w, priority: 20 });
// fatal with more than one wolf: one is lost, full HP; on the last one the knock-out goes through — 战术点形态
battle.on('fatal', (ctx) => {
if (ctx.unit !== w || ctx.prevented || w.mem.wolves <= 1) return;
w.mem.wolves--;
@@ -56,6 +82,13 @@ export default {
w.hp = w.s.maxHp;
fx(battle, 'revive', w, { wolves: w.mem.wolves });
}, { owner: w });
installWolfTacticalPoint(battle, w, {
onEnter: () => {
w.mem.wolves = 0;
apply();
fx(battle, 'wolfShadowLost', w, { wolves: 0 });
},
});
const mod = tal.find((t) => t && t.bb && t.bb.damage_scale != null);
if (mod) {
battle.on('hit', (ctx) => {
@@ -102,7 +135,10 @@ export default {
cur = next;
}, { owner: unit, immediate: true });
};
/** One more “狼影” (S1): this kit's pack, or a 狼群 board piece run by content/tokens.js (same 'wolf:shadows' buff). */
/**
* One more “狼影” (S1 ②) on a standing pack: this kit's pack, or a 狼群 board piece run by content/tokens.js (same
* 'wolf:shadows' buff). False at the maximum. Neither pack's own growth cycle is touched ("与狼群本身的刷新周期互相独立").
*/
const addShadow = (battle, vigil, w) => {
if (typeof w.mem.addWolf === 'function') return w.mem.addWolf();
const n = Math.max(1, w.mem.shadows ?? 1);
@@ -115,17 +151,20 @@ export default {
return true;
};
const dpGain = (battle, unit, n) => { if (n > 0) { battle.addDp(unit.ownerId, n); fx(battle, 'dp', unit, { n }); } };
// S1: the cast — ready and the pack on the field (PRTS 备注 「仅场上存在狼群时可触发技能」)
// S1: the cast — ready and the pack on the field in either form (PRTS 备注 「仅场上存在狼群时可触发技能」 with ① for its
// 战术点形态)
const installCall = (battle, unit) => {
battle.on('tick', () => {
const sk = unit.skill;
if (!alive(unit) || !sk || !sk.ready || sk.active || !unit.canAct || unit.s.flags.silence || !wolfOf(unit)) return;
if (!alive(unit) || !sk || !sk.ready || sk.active || !unit.canAct || unit.s.flags.silence || !packOf(unit)) return;
sk.activate('SP_FULL');
}, { owner: unit });
};
// S2: the pack's empowered next attack (armed by the cast; ×scale on its hits; a kill pays once)
const installGift = (battle, unit) => {
battle.on('tick', () => { // the cast: ready, the pack on the field, no unused gift on it (PRTS 备注)
// the cast: ready, the pack standing, no unused gift on it (PRTS 备注 「仅场上存在狼群,且狼群未获得此技能的充能时可触发
// 技能」; a pack in its 战术点形态 does not count — the 备注 does not say [ASSUMED])
battle.on('tick', () => {
const sk = unit.skill, w = wolfOf(unit);
if (!alive(unit) || !sk || !sk.ready || sk.active || !unit.canAct || unit.s.flags.silence || !w || w.mem.vigilGift) return;
sk.activate('SP_FULL');
@@ -152,16 +191,19 @@ export default {
trait: { install(battle, unit) {
// The pack is the tactician's 援军. The match also hands the player the 狼群 token to place in the prep phase
// (= choosing the tactical point): that board piece (tokens.js kit, owner-coupled effects left to this kit) is
// the pack when present — deployed early on its own tile if 伺夜 deploys first — never a second pack.
// the pack when present — deployed early on its own tile if 伺夜 deploys first — never a second pack. Summoned at
// each deployment of 伺夜; a knocked-out pack is not summoned again: it sits in its 战术点形态 and comes back by
// itself (tokens.js installWolfTacticalPoint — both packs; until 0.2.0 it came back after the token's 10 s redeploy
// time with its initial wolves).
const spawn = () => {
if (!alive(unit) || wolfOf(unit)) return;
if (!alive(unit) || packOf(unit)) return;
const pieces = tokensOf(battle, unit, wolfId);
const live = pieces.find((t) => alive(t));
if (live) { unit.trait.reinforcement = live; return; }
const waiting = pieces.find((t) => !t.alive && !t.removed);
if (waiting && battle.redeploy(waiting, { free: true })) {
unit.trait.reinforcement = waiting;
fx(battle, 'summon', waiting, { src: unit.id, token: wolfId });
fx(battle, 'summon', waiting, { src: unit.id, token: wolfId, wolves: waiting.mem.shadows });
return;
}
const board = pieces.find((t) => t.uid != null);
@@ -171,11 +213,11 @@ export default {
unit.trait.reinforcement = w;
if (!w) return;
fx(battle, 'summon', w, { src: unit.id, token: wolfId, wolves: w.mem.wolves });
const again = Math.max(0, w.base.respawnTime || 0);
battle.on('death', (c) => { if (c.unit === w && c.reason === 'killed') battle.after(again, spawn, { owner: unit }); }, { owner: w });
};
battle.on('deploy', (c) => { if (c.unit === unit) spawn(); }, { owner: unit });
installPackMark(battle, unit);
// "持有者离场后强制撤退场上的狼群(不触发上述效果)": a standing pack leaves with him ('expired': no 战术点形态); one in
// its 战术点形态 ends it there (tokens.js) and waits for his redeploy
battle.on('death', (c) => {
if (c.unit !== unit) return;
const w = wolfOf(unit);
@@ -205,23 +247,34 @@ export default {
},
skills: altSkills(chess, d, bb, {
// (自动触发: an AUTO skill takes no 技能策略 — the 战术家 row is for MANUAL skills. PRTS 备注 「仅场上存在狼群时可触发技能」
// (the owner's decision of 2026-10-05): the kit casts it as soon as it is ready while the pack stands
// (installCall); without a pack it waits at full SP — until 0.2.0 it fired at SP_FULL and paid its DP with no pack.
// The 备注 also counts the pack's 战术点形态 (after its last 狼影 is lost) as on the field — the cast brings it back
// at once with one 狼影 — and at the 狼影 cap resets the pack's HP; the kit has neither: a knocked-out pack comes
// back after its respawn time and S1 waits for it, and at the cap it only pays its DP [ASSUMED])
// (the owner's decision of 2026-10-05): the kit casts it as soon as it is ready while the pack is on the field —
// standing, or in its 战术点形态 while 伺夜 stands (installCall, packOf); without a pack it waits at full SP — until
// 0.2.0 it fired at SP_FULL and paid its DP with no pack. The cast always pays +cost DP; its effect follows the
// pack's state (备注 ①②③): ① in its 战术点形态 the pack comes back at once with one 狼影 and a fresh 狼影 cycle (its
// pending return is cancelled — tokens.js wolfReturnNow), ② standing below the maximum: one more 狼影 (the pack's
// own cycle untouched), ③ at the maximum: the pack's HP to max — a reset like the talent's, not a heal (the pack
// holds 禁疗). Until 0.2.0 a knocked-out pack was waited for and the cast at the maximum only paid its DP.)
skchr_vigil_1: (s) => ({
kind: 'instant',
trigger: 'NEVER',
onStart({ battle, unit }) {
dpGain(battle, unit, num(s.bb.cost, 0));
const w = wolfOf(unit);
if (w) addShadow(battle, unit, w);
const w = packOf(unit);
if (!w) return;
if (!w.alive) { // ①
if (wolfReturnNow(battle, w, unit.id)) fx(battle, 'summon', w, { src: unit.id, wolves: w.mem.shadows });
return;
}
if (addShadow(battle, unit, w)) return; // ②
w.hp = w.s.maxHp; // ③
fx(battle, 'revive', w, { src: unit.id, wolves: w.mem.shadows });
},
}),
// (自动触发, PRTS 备注 「仅场上存在狼群,且狼群未获得此技能的充能时可触发技能」: no enemy needed — the kit casts it as soon
// as it is ready while the pack stands and holds no unused gift (installGift); until 0.2.0 the data's DEFAULT made it
// wait for 伺夜's own next attack, so it never fired with no enemy in his range)
// wait for 伺夜's own next attack, so it never fired with no enemy in his range. A pack in its 战术点形态 does not
// count: S1's 备注 lists that form, S2's says nothing of it, so S2 waits for the pack to come back [ASSUMED]; a gift
// the pack holds stays on it through the form [ASSUMED])
skchr_vigil_2: (s) => ({
kind: 'instant',
trigger: 'NEVER',
+125 -11
View File
@@ -21,7 +21,8 @@
// full HP; its and 伺夜's attacks ignore def_penetrate_fixed DEF of enemies it blocks; while 伺夜's
// timed skill runs every attack damage instance (each bite) of the pack / 伺夜 on an enemy it blocks
// adds 伺夜 ATK × bb scale arts; module: ×damage_scale damage from enemies it blocks; is its owner's
// tactician 援军 (1.5× trait), leaves with it; respawns respawnTime s after being killed
// tactician 援军 (1.5× trait), leaves with it; the fatal hit on its last shadow (or a 撤退) ⇒ 战术点形态
// for the talent interval, then back on its tile with 1 shadow (installWolfTacticalPoint, both packs)
// 流形 copy skill (SP from data, golden +sp; starts only when an operator can be copied, else waits ready):
// copies scale × HP/ATK/DEF/RES, block, BAT/ASPD, range and the damage type of the nearest allied
// operator; no attack before its copy; ranged copy splits every N attacks (clone lasts the talent
@@ -74,10 +75,12 @@
// 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,
// releaseSkillSummon, SKILL_SUMMON_START_DEPLOY, CAT_SHIELD_KEY, TOKEN_IDS; mapCharTalents and touchGospel (Touch's 攫升 /
// 超脱 and 恳切福音, shared with the Touch 补位 stand-in kit, kits/ops/standin-acmedc.js).
// 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,
// kits/ops/standin-acmedc.js).
import { COLS, ROWS, MOVE_SCALE } from '../constants.js';
import { COLS, ROWS, MOVE_SCALE, FORCED_EXIT } from '../constants.js';
import { absoluteRangeKeys, sortEnemyTargets, canTargetEnemy } from '../targeting.js';
import { bodyInKeys, bodyOnTile } from '../body.js';
import { hasHp } from '../damage.js';
@@ -607,14 +610,109 @@ function paperDoll(bb, raw, def) {
};
}
/** Current “狼影” count of a 狼群 token (0 when not a wolf pack). */
/** Current “狼影” count of a 狼群 token (0 when not a wolf pack, and in its 战术点形态). */
export function wolfShadows(unit) { return unit && unit.defId === TOKEN_IDS.wolfPack ? (unit.mem.shadows ?? 0) : 0; }
// ---- 狼群 战术点形态 — both packs: the board piece (wolfPack below) and the 伺夜 kit's own pack
// (kits/ops/chess_char_3_19-vigil.js). PRTS 伺夜 天赋 狼群领袖 备注 and 狼群 召唤物信息 备注: "受到致命伤时,如果狼影层数>1则
// 消耗一层狼影并重设生命值至上限,为1则消耗一层狼影变为战术点形态;手动撤退、强制撤退时狼影层数归零并变为战术点形态;战术点形态的
// 持续时间等于“狼影”恢复时间,持续时间结束后狼影层数变回1层", "战术点形态期间:不进行普通攻击,持有无敌、强制缴械、不死…",
// "持有者离场后强制撤退场上的狼群(不触发上述效果)". The remake's 战术点形态 is its knocked-out piece: off the fight
// (`alive` false: no block, no attack, not targetable) but kept (`removed` false: its hooks live on and its tile stays
// reserved — Battle.isReservedTile — as the official device holds it), with 0 狼影, for the 狼影 recovery time
// (wolfShadowInterval: the talent's interval, data); then it is redeployed on its tile, free, at full HP with 1 狼影 and a
// fresh growth cycle (the pack's own `deploy` handler reads `mem.wolfReturn`). 伺夜 S1 领袖的呼唤 ① brings it back at
// once (wolfReturnNow); every return timer carries the form's `seq`, so a stale one never revives a pack that came back
// and fell again.
/**
* Removal reasons that put a 狼群 in its 战术点形态: the fatal hit on its last 狼影 (the engine's knock-out) and a 撤退 —
* manual (`battle.retreat`'s default reason) or forced. No sim path retreats the pack that way today; 'expired' (its
* owner leaving, the deploy limit) and 'raid' (an instant redeploy) never do.
*/
const WOLF_TAC_EXITS = new Set(['killed', 'retreat', FORCED_EXIT]);
/** Seconds between two return tries of a 狼群 whose tile is taken when its 战术点形态 ends (Battle.isReservedTile keeps it). */
const WOLF_RETURN_RETRY = 0.25;
/** The 狼群领袖 talent of a 狼群 def (its 狼影 stack talent), else its first talent. */
const wolfLeader = (def) => talentWith(def, 'vigil_wolf_t_1_enhance[trigger].max_stack_cnt') ?? def?.talents?.[0] ?? null;
/**
* “狼影” recovery time of a 狼群 def: its 狼群领袖 talent interval ("每25秒增加一只"; data — 25 s for both 伺夜 chess). The
* growth cycle and the length of the 战术点形态. 0 when the data has none.
*/
export function wolfShadowInterval(def) {
const lb = wolfLeader(def)?.bb ?? {};
return Math.max(0, num(lb['vigil_wolf_t_1_enhance[trigger].interval'] ?? lb.interval, 0));
}
/** The 战术点形态 of a 狼群 (`{ seq, since, until }`) while it is in it, else null. */
export function wolfTacticalPoint(unit) {
const tp = unit?.mem?.wolfTac;
return tp && !unit.alive && !unit.removed ? tp : null;
}
/** The pack's owner stands on the field (no owner unit: a test spawn). */
const wolfOwnerStands = (u) => !u.ownerUnit || (u.ownerUnit.alive && u.ownerUnit.deployed);
/**
* Bring a 狼群 back from its 战术点形态 at once (the end of the form; 伺夜 S1 ①: "立刻切换至拥有1只狼影的召唤物形态(会更新
* 狼群的狼影刷新周期)"): redeployed on its tile, free, at full HP with 1 狼影 and a fresh growth cycle (its `deploy`
* handler reads `mem.wolfReturn` = { src }); the pending return is cancelled. False when it is not in the form, its owner
* is off the field or its tile is taken.
*/
export function wolfReturnNow(battle, unit, src = null) {
const tp = wolfTacticalPoint(unit);
if (!tp || battle.finished || !wolfOwnerStands(unit)) return false;
unit.mem.wolfReturn = { src };
let ok;
try { ok = battle.redeploy(unit, { free: true }); } finally { unit.mem.wolfReturn = null; }
if (!ok) return false;
if (unit.mem.wolfTac === tp) unit.mem.wolfTac = null;
if (tp.timer) tp.timer.cancel();
return true;
}
/** The end of the 战术点形态 `seq` (its timer): the pack comes back — another try shortly when its tile is taken. */
function wolfTimerReturn(battle, unit, seq) {
const tp = wolfTacticalPoint(unit);
if (!tp || tp.seq !== seq || battle.finished) return;
if (wolfReturnNow(battle, unit)) return;
tp.timer = battle.after(WOLF_RETURN_RETRY, () => wolfTimerReturn(battle, unit, seq), { owner: unit });
}
/**
* The 战术点形态 of a 狼群 unit (see above). `onEnter(battle, unit)` sets its 狼影 to 0 — each pack keeps its own count,
* buff and fx.
*/
export function installWolfTacticalPoint(battle, unit, { onEnter = null } = {}) {
battle.on('death', (ctx) => {
if (ctx.unit !== unit || battle.finished || !WOLF_TAC_EXITS.has(ctx.reason) || !wolfOwnerStands(unit)) return;
unit.removed = false;
// the 狼影 recovery time from the data; a record without one falls back to the token's redeploy time (data)
const t = wolfShadowInterval(unit.def) || Math.max(0, num(unit.base.respawnTime, 0));
const seq = (unit.mem.wolfTacSeq ?? 0) + 1;
unit.mem.wolfTacSeq = seq;
const tp = { seq, since: battle.time, until: battle.time + t, timer: null };
unit.mem.wolfTac = tp;
if (onEnter) onEnter(battle, unit);
tp.timer = battle.after(t, () => wolfTimerReturn(battle, unit, seq), { owner: unit });
}, { owner: unit, priority: -10 });
// its owner leaving ends the form without a return ("不触发上述效果"): the piece waits for the owner's redeploy, which
// brings a fresh pack (its initial 狼影) — as for a standing pack, which leaves with its owner
battle.on('death', (ctx) => {
const tp = wolfTacticalPoint(unit);
if (!tp || !unit.ownerUnit || ctx.unit !== unit.ownerUnit) return;
unit.mem.wolfTac = null;
if (tp.timer) tp.timer.cancel();
}, { owner: unit });
}
/** 狼群 (伺夜 talent 狼群领袖/狼群天性; S3 bb on the token). */
function wolfPack(bb, raw, def) {
const leader = talentWith(def, 'vigil_wolf_t_1_enhance[trigger].max_stack_cnt') ?? def?.talents?.[0] ?? null;
const leader = wolfLeader(def);
const lb = leader?.bb ?? {};
const interval = num(lb['vigil_wolf_t_1_enhance[trigger].interval'] ?? lb.interval, 0);
const interval = wolfShadowInterval(def);
const perBlock = num(lb['vigil_wolf_t_1_enhance[trigger].block_cnt'] ?? lb.block_cnt, 1);
const mMax = String(leader?.description ?? '').match(/至多(\d+)只/);
const maxShadows = mMax ? +mMax[1] : 1 + num(lb['vigil_wolf_t_1_enhance[trigger].max_stack_cnt'], 0);
@@ -635,8 +733,13 @@ function wolfPack(bb, raw, def) {
const mi = ot.match(/初始(两|二|\d+)只/);
const initShadows = Math.max(1, Math.min(maxShadows, mi ? (/\d/.test(mi[1]) ? +mi[1] : 2) : maxShadows - 1));
onDeploy(battle, unit, () => {
setShadows(battle, unit, initShadows);
// back from its 战术点形态 (wolfReturnNow): 1 狼影 (PRTS 狼群 "从战术点形态转变为召唤物形态后拥有1只“狼影”") and a
// fresh growth cycle; any other deployment: the initial count
const back = unit.mem.wolfReturn;
setShadows(battle, unit, back ? 1 : initShadows);
unit.hp = unit.s.maxHp;
// (伺夜 S1 ① shows its own summon fx)
if (back && back.src == null) battle.fx('wolfShadow', { x: unit.x, y: unit.y, id: unit.id, n: 1 });
const seq = unit.deploySeq;
if (interval > 0) {
battle.every(interval, (b, sched) => {
@@ -650,6 +753,7 @@ function wolfPack(bb, raw, def) {
const o = ownerOf(unit);
if (o && o.profile?.sub === 'tactician') o.trait.reinforcement = unit;
}, 20);
// fatal with more than one 狼影: one is lost, full HP; on the last one the knock-out goes through — 战术点形态
battle.on('fatal', (ctx) => {
if (ctx.unit !== unit || ctx.prevented || !((unit.mem.shadows ?? 0) > 1)) return;
ctx.prevented = true;
@@ -657,6 +761,12 @@ function wolfPack(bb, raw, def) {
unit.hp = unit.s.maxHp;
battle.fx('wolfShadowLost', { x: unit.x, y: unit.y, id: unit.id, n: unit.mem.shadows });
}, { owner: unit, priority: -50 });
installWolfTacticalPoint(battle, unit, {
onEnter: (b, u) => {
setShadows(b, u, 0);
b.fx('wolfShadowLost', { x: u.x, y: u.y, id: u.id, n: 0 });
},
});
// 狼群天性 ("伺夜和狼群对其的攻击无视其175防御力") and the owner's S3 bonus ("狼群与伺夜攻击被狼群阻挡的单位造成伤害
// 时,额外造成相当于伺夜攻击力N%的法术伤害", one per damage instance = per bite) cover the pack's and 伺夜's own
// attacks; with a hand-authored 伺夜 kit (managed) that kit applies both. The module guard is intrinsic.
@@ -676,14 +786,14 @@ function wolfPack(bb, raw, def) {
battle.dealDamage(ctx.source, e, { amount: o.s.atk * s3Scale, type: 'arts', isSkill: true, canDodge: false, tags: ['vigil'] });
}, { owner: unit });
}
// the 援军 leaves with its tactician (a hand-authored 伺夜 kit does the same for its pack) and a killed pack
// does not come back while its tactician is down (the tactician's redeploy brings it back)
// the 援军 leaves with its tactician (a hand-authored 伺夜 kit does the same for its pack); a pack in its 战术点形态
// ends it then (installWolfTacticalPoint) and the tactician's redeploy brings it back fresh. No respawn timer of
// its own: the token's redeploy time (data 10 s) is not the 战术点形态's length (the 狼影 interval)
const tacticianOwner = () => ownerOf(unit)?.profile?.sub === 'tactician';
battle.on('death', (ctx) => {
if (ctx.unit !== unit.ownerUnit || !unit.alive || managed(unit) || !tacticianOwner()) return;
battle.retreat(unit, { reason: 'expired', permanent: true });
}, { owner: unit });
enableRespawn(battle, unit, { delay: (u) => u.base.respawnTime, requireOwner: tacticianOwner });
},
};
}
@@ -1464,6 +1574,10 @@ function ensureReinforcement(battle, owner, tokenId) {
const mine = battle.allyUnits.filter((t) => t.kind === 'token' && t.defId === tokenId && t.ownerUnit === owner && !t.mem.isClone && !t.mem.mlyssClone);
const live = mine.find((t) => t.alive);
if (live) { owner.trait.reinforcement = live; return live; }
// a 狼群 in its 战术点形态 is still the 援军 on the field: it comes back by its own timer (a 【移动】 of the owner fires
// `deploy` without ending the form)
const tac = mine.find((t) => wolfTacticalPoint(t));
if (tac) { owner.trait.reinforcement = tac; return tac; }
const waiting = mine.find((t) => !t.alive && !t.removed);
if (waiting && battle.redeploy(waiting, { free: true })) { owner.trait.reinforcement = waiting; return waiting; }
// the tactical point the player chose (the board piece's tile, else the last one's) when still usable, else the
+184 -20
View File
@@ -8,6 +8,7 @@ import { getDefaultSource } from '../../server/sim/simdata.js';
import { skillSpecSource } from '../../server/sim/content/index.js';
import { effectiveProfile } from '../../server/sim/ai.js';
import { kitCoverage } from '../../tools/kit-coverage.mjs';
import { wolfShadows, wolfTacticalPoint } from '../../server/sim/content/tokens.js';
import KITS from '../../server/sim/content/kits/tier3.js';
const ds = getDefaultSource();
@@ -786,28 +787,186 @@ test('3_19 伺夜 S1 领袖的呼唤 (自动触发, the pack on the field): +DP
done(g);
});
test('3_19 伺夜 S1 领袖的呼唤: PRTS 备注 「仅场上存在狼群时可触发技能」 — no pack, no cast (the SP waits full, no DP); cast once the pack stands again', () => {
test('3_19 伺夜 S1 领袖的呼唤: PRTS 备注 「仅场上存在狼群时可触发技能」 — no pack on the field in either form, no cast (the SP waits full, no DP)', () => {
for (const id of BOTH('chess_char_3_19_a')) {
const b = SB(id, 'skchr_vigil_1');
const h = makeBattle({ defs: { chess: noGarrison(id) }, timeLimit: 60, flags: { dpPerSec: 0 }, units: [U(id, 'skchr_vigil_1', 10, 3)] });
const u = h.unit(id), p = h.b.getPlayer('p1');
h.run(1);
const w = u.trait.reinforcement;
assert.ok(w && w.alive, 'the pack stands');
for (let i = 0; i < 10 && w.alive; i++) h.b.dealDamage(null, w, { amount: 1e9, type: 'true' }); // a wolf is lost per KO
assert.ok(!u.trait.reinforcement?.alive, 'no pack on the field');
// withdrawn by a rule ('expired', as when it leaves with 伺夜): gone, not in its 战术点形态
h.b.retreat(w, { reason: 'expired', permanent: true });
assert.equal(w.removed, true);
assert.equal(wolfTacticalPoint(w), null, 'no 战术点形态');
const dp0 = p.dp;
fill(u);
h.run(2);
assert.equal(u.skill.activations, 0, 'no pack, no cast');
assert.ok(u.skill.ready, 'the SP waits full');
approx(p.dp, dp0, 1e-9, 'no DP without the pack');
// the knocked-out pack comes back after its respawn time: cast at once — +DP and one more 狼影
assert.ok(h.runUntil(() => u.skill.activations === 1, 15), 'cast once the pack stands again');
done(h);
}
});
// 狼群 战术点形态 (PRTS 伺夜 天赋 狼群领袖 备注, 狼群 召唤物信息 备注) — both packs: the 伺夜 kit's own pack (no board piece) and
// the 狼群 piece placed in the prep phase (content/tokens.js wolfPack, adopted by the kit)
const WOLF = 'token_10028_vigil_wolf';
/** The 狼影 recovery time from the data: the token's 狼群领袖 talent interval. */
const WOLF_IV = (id) => ds.getToken(WOLF, id).talents.find((t) => t.bb['vigil_wolf_t_1_enhance[trigger].interval'] != null).bb['vigil_wolf_t_1_enhance[trigger].interval'];
/** 伺夜 `id` (skill `skillId`, default S3 when null) with the kit's pack, or with a prep-placed 狼群 piece (uid 2). */
const vigilUnits = (id, skillId, piece) => {
const sk = skillId ? { skillIndex: IDX(id, skillId) } : {};
return piece
? [{ chessId: id, row: 12, col: 3, uid: 1, ...sk }, { kind: 'token', tokenId: WOLF, ownerUid: 1, row: 11, col: 5, uid: 2 }]
: [{ chessId: id, row: 10, col: 3, uid: 1, ...sk }];
};
const close = (a, b, tol, msg) => assert.ok(Math.abs(a - b) <= tol, `${msg}: ${a} ≈ ${b}`);
const silence = (h, u, on) => (on ? h.b.addBuff(u, { key: 'test:silence', flags: { silence: true } }) : h.b.removeBuff(u, 'test:silence'));
test('3_19 伺夜 狼群 战术点形态: the fatal hit on the last 狼影 — out of the fight for the 狼影 interval (data), then back on its tile at full HP with one 狼影 and a fresh cycle (kit pack and prep piece)', () => {
for (const id of BOTH('chess_char_3_19_a')) for (const piece of [false, true]) {
const iv = WOLF_IV(id), tag = `${id} ${piece ? 'piece' : 'kit pack'}`;
assert.equal(iv, 25, '25 s on both chess (data)');
const h = makeBattle({ defs: { enemies: { enemy_d: dummy('enemy_d', { atk: 0 }) }, chess: noGarrison(id) }, timeLimit: 120, hooks: ['attack'], flags: { dpPerSec: 0 },
units: vigilUnits(id, null, piece), enemies: [{ key: 'enemy_d', pos: piece ? [11, 5] : [9, 3], time: 0.5 }] });
h.run(2);
const u = h.unit(1), w = u.trait.reinforcement, e = h.enemy('enemy_d');
if (piece) assert.equal(w, h.unit(2), `${tag}: the piece is the pack`);
else assert.ok(w && w.defId === WOLF && w.uid == null, `${tag}: the kit's own pack`);
assert.equal(e.blockedBy, w, `${tag}: blocks`);
const tile = [w.tileR, w.tileC];
h.b.dealDamage(null, w, { amount: 1e9, type: 'true' });
assert.ok(w.alive && wolfShadows(w) === 1, `${tag}: one 狼影 lost, still standing`);
h.b.dealDamage(null, w, { amount: 1e9, type: 'true' });
const t0 = h.b.time;
assert.equal(w.alive, false, `${tag}: out of the fight`);
assert.ok(wolfTacticalPoint(w), `${tag}: 战术点形态`);
assert.equal(w.removed, false, `${tag}: kept, not removed`);
assert.equal(wolfShadows(w), 0, `${tag}: 狼影 0`);
assert.equal(u.trait.reinforcement, w, `${tag}: still his 援军`);
assert.ok(h.b.isReservedTile(tile[0], tile[1]), `${tag}: its tile stays taken`);
assert.deepEqual(w.blocking, [], `${tag}: no block`);
assert.notEqual(e.blockedBy, w);
const bites0 = h.hooksOf('attack').filter((c) => c.attacker === w).length;
h.run(iv - 0.5);
assert.equal(w.alive, false, `${tag}: still in its 战术点形态 (not back after the token's 10 s redeploy time)`);
assert.equal(h.hooksOf('attack').filter((c) => c.attacker === w).length, bites0, `${tag}: no attack`);
assert.notEqual(e.blockedBy, w, `${tag}: blocks nothing`);
assert.ok(h.runUntil(() => w.alive, 1), `${tag}: back`);
close(h.b.time - t0, iv, 0.1, `${tag}: after the 狼影 interval`);
assert.equal(wolfTacticalPoint(w), null);
assert.deepEqual([w.tileR, w.tileC], tile, `${tag}: on its tile`);
assert.equal(w.hp, w.s.maxHp, `${tag}: full HP`);
assert.equal(wolfShadows(w), 1, `${tag}: one 狼影`);
assert.equal(w.s.blockCnt, 1);
assert.equal(u.trait.reinforcement, w, `${tag}: the same pack`);
// a fresh 狼影 cycle: the next one an interval after the return
const t1 = h.b.time;
h.run(iv - 0.5);
assert.equal(wolfShadows(w), 1, `${tag}: no 狼影 before a full interval`);
assert.ok(h.runUntil(() => wolfShadows(w) === 2, 1), `${tag}: the next 狼影`);
close(h.b.time - t1, iv, 0.1, `${tag}: one interval after the return`);
done(h);
}
});
test('3_19 伺夜 S1 领袖的呼唤 by the pack\'s state (PRTS 备注 ①②③): +cost DP each cast; ② +1 狼影 (own cycle untouched), ③ at the maximum HP to max, ① from the 战术点形态 back at once with one 狼影 and a fresh cycle — a stale return timer never revives it (kit pack and prep piece)', () => {
for (const id of BOTH('chess_char_3_19_a')) for (const piece of [false, true]) {
const b = SB(id, 'skchr_vigil_1'), iv = WOLF_IV(id), tag = `${id} ${piece ? 'piece' : 'kit pack'}`;
const h = makeBattle({ defs: { chess: noGarrison(id) }, timeLimit: 120, flags: { dpPerSec: 0 }, units: vigilUnits(id, 'skchr_vigil_1', piece) });
const u = h.unit(1), p = h.b.getPlayer('p1');
h.step();
const w = u.trait.reinforcement;
assert.ok(w && w.alive && (!piece || w === h.unit(2)), tag);
silence(h, u, true); // casts only when the test asks (the SP refills in 24–27 s)
const cast = () => {
const n0 = u.skill.activations, dp0 = p.dp;
silence(h, u, false);
fill(u);
h.step();
silence(h, u, true);
assert.equal(u.skill.activations, n0 + 1, `${tag}: cast`);
approx(p.dp, dp0 + b.cost, 1e-9, `${tag}: +cost DP`);
};
// ② below the maximum, 5 s into the pack's cycle: one more 狼影; its own next one still comes at deploy + interval
assert.equal(wolfShadows(w), 2);
h.run(5);
cast();
assert.equal(wolfShadows(w), 3, `${tag} ②: +1 狼影`);
assert.equal(w.s.blockCnt, 3);
h.b.dealDamage(null, w, { amount: 1e9, type: 'true' });
assert.equal(wolfShadows(w), 2);
assert.ok(h.runUntil(() => wolfShadows(w) === 3, iv), `${tag} ②: the pack's own 狼影`);
close(h.b.time - w.deployedAt, iv, 0.1, `${tag} ②: on the cycle from its deployment, not from the cast`);
// ③ at the maximum: the pack's HP back to max, the count unchanged
w.hp = w.s.maxHp * 0.3;
cast();
assert.equal(wolfShadows(w), 3, `${tag} ③: still at the maximum`);
assert.equal(w.hp, w.s.maxHp, `${tag} ③: HP to max`);
// ① in its 战术点形态: back at once, one 狼影, full HP, a fresh cycle
for (let i = 0; i < 3; i++) h.b.dealDamage(null, w, { amount: 1e9, type: 'true' });
assert.ok(!w.alive && wolfTacticalPoint(w), `${tag}: 战术点形态`);
h.run(3);
assert.equal(w.alive, false, `${tag}: waits in its 战术点形态 without a cast`);
cast();
assert.ok(w.alive, `${tag} ①: back at once`);
assert.equal(wolfTacticalPoint(w), null);
assert.equal(wolfShadows(w), 1, `${tag} ①: one 狼影`);
assert.equal(w.hp, w.s.maxHp, `${tag} ①: full HP`);
assert.equal(u.trait.reinforcement, w, `${tag} ①: the same pack`);
const tUp = h.b.time;
assert.ok(h.runUntil(() => wolfShadows(w) === 2, iv + 1), `${tag} ①: the next 狼影`);
close(h.b.time - tUp, iv, 0.1, `${tag} ①: a fresh cycle from the return`);
// a stale timer: down, back by S1 3 s later, down again 2 s after that — the first form's return time passes
// without reviving it; it comes back an interval after the second knock-out
for (let i = 0; i < 2; i++) h.b.dealDamage(null, w, { amount: 1e9, type: 'true' });
assert.ok(wolfTacticalPoint(w));
const tDown = h.b.time;
h.run(3);
cast();
assert.ok(w.alive);
h.run(2);
h.b.dealDamage(null, w, { amount: 1e9, type: 'true' });
assert.ok(wolfTacticalPoint(w), `${tag}: 战术点形态 again`);
const tDown2 = h.b.time;
h.run(tDown + iv + 0.5 - h.b.time);
assert.equal(w.alive, false, `${tag}: the cancelled return does not revive it`);
assert.ok(h.runUntil(() => w.alive, 6), `${tag}: back`);
close(h.b.time - tDown2, iv, 0.1, `${tag}: an interval after the second knock-out`);
done(h);
}
});
test('3_19 伺夜 狼群: a 撤退 also ends in the 战术点形态 (狼影 0); 伺夜 leaving ends the form without a return — his redeploy brings a fresh pack; a standing pack leaves with him, no 战术点形态 (kit pack and prep piece)', () => {
const id = 'chess_char_3_19_a', iv = WOLF_IV(id);
for (const piece of [false, true]) {
const tag = piece ? 'piece' : 'kit pack';
const h = makeBattle({ defs: { chess: noGarrison(id) }, timeLimit: 120, flags: { dpPerSec: 0 }, units: vigilUnits(id, null, piece) });
h.step();
const u = h.unit(1), w = u.trait.reinforcement;
assert.equal(wolfShadows(w), 2);
// a manual 撤退 (battle.retreat's default reason; no sim path withdraws the pack so today)
h.b.retreat(w);
assert.ok(!w.alive && wolfTacticalPoint(w), `${tag}: 撤退 ⇒ 战术点形态`);
assert.equal(wolfShadows(w), 0, `${tag}: 狼影 0`);
// 伺夜 leaves: the form ends there, no return
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
assert.equal(u.alive, false);
assert.equal(wolfTacticalPoint(w), null, `${tag}: the form ends with 伺夜`);
h.run(iv + 2);
assert.equal(w.alive, false, `${tag}: no return without 伺夜`);
// his redeploy: a fresh pack with the initial 狼影
assert.ok(h.b.redeploy(u, { free: true }));
h.step();
const w2 = u.trait.reinforcement;
assert.ok(w2 && w2.alive, 'the pack is back');
approx(p.dp, dp0 + b.cost, 1e-9, '+cost DP');
assert.equal(w2.mem.wolves, 3, 'one more 狼影 on the returned pack');
assert.ok(w2 && w2.alive, `${tag}: a pack with him`);
assert.equal(wolfShadows(w2), 2, `${tag}: the initial 狼影`);
assert.equal(h.b.allyUnits.filter((t) => t.alive && t.defId === WOLF).length, 1, `${tag}: one pack`);
// a standing pack leaves with him ('expired'): gone, no 战术点形态
h.b.dealDamage(null, u, { amount: 1e9, type: 'true' });
assert.equal(w2.alive, false);
assert.equal(wolfTacticalPoint(w2), null, `${tag}: no 战术点形态 when it leaves with 伺夜`);
assert.equal(w2.removed, true);
done(h);
}
});
@@ -857,28 +1016,33 @@ test('3_19 伺夜 S2 领袖的馈赠: +DP, the pack recovers HP, its next attack
}
});
test('3_19 伺夜 S2 领袖的馈赠 (自动触发): no enemy needed — cast at full SP while the pack stands and holds no unused gift (PRTS 备注), never without the pack', () => {
for (const id of BOTH('chess_char_3_19_a')) {
const b = SB(id, 'skchr_vigil_2');
const h = makeBattle({ defs: { chess: noGarrison(id) }, timeLimit: 60, flags: { dpPerSec: 0 }, units: [U(id, 'skchr_vigil_2', 10, 3)] });
const u = h.unit(id), p = h.b.getPlayer('p1');
test('3_19 伺夜 S2 领袖的馈赠 (自动触发): no enemy needed — cast at full SP while the pack stands and holds no unused gift (PRTS 备注), never while it is in its 战术点形态 [ASSUMED] — cast once it is back (kit pack and prep piece)', () => {
for (const id of BOTH('chess_char_3_19_a')) for (const piece of [false, true]) {
const b = SB(id, 'skchr_vigil_2'), tag = `${id} ${piece ? 'piece' : 'kit pack'}`;
const h = makeBattle({ defs: { chess: noGarrison(id) }, timeLimit: 60, flags: { dpPerSec: 0 }, units: vigilUnits(id, 'skchr_vigil_2', piece) });
const u = h.unit(1), p = h.b.getPlayer('p1');
h.run(1);
const w = u.trait.reinforcement;
assert.ok(w && w.alive, 'the pack stands');
assert.ok(w && w.alive, `${tag}: the pack stands`);
const dp0 = p.dp;
fill(u);
assert.ok(h.runUntil(() => u.skill.activations === 1, 1), 'cast with nobody on the field');
assert.ok(h.runUntil(() => u.skill.activations === 1, 1), `${tag}: cast with nobody on the field`);
approx(p.dp, dp0 + b.cost, 1e-6, '+cost DP at once');
assert.ok(w.mem.vigilGift, 'the pack holds the gift for its next attack');
fill(u);
h.run(3);
assert.equal(u.skill.activations, 1, '"狼群未获得此技能的充能时可触发": no second cast while the gift is unused');
// the gift spent (as by a bite), the pack gone: still no cast until a pack stands again
// the gift spent (as by a bite), the pack in its 战术点形态: no cast (the 备注 does not count that form for S2 —
// [ASSUMED]); once it is back, the waiting cast fires
w.mem.vigilGift = null;
for (let i = 0; i < 10 && w.alive; i++) h.b.dealDamage(null, w, { amount: 1e9, type: 'true' }); // a wolf is lost per KO
assert.ok(!u.trait.reinforcement?.alive, 'no pack on the field');
h.step(2);
assert.equal(u.skill.activations, 1, '"仅场上存在狼群…时可触发": no pack, no cast');
assert.ok(wolfTacticalPoint(w), `${tag}: 战术点形态`);
h.run(5);
assert.equal(u.skill.activations, 1, `${tag}: no cast while the pack is in its 战术点形态`);
assert.ok(u.skill.ready, 'the SP waits full');
assert.ok(h.runUntil(() => w.alive, WOLF_IV(id)), `${tag}: the pack is back`);
assert.ok(h.runUntil(() => u.skill.activations === 2, 0.5), `${tag}: cast once it stands again`);
assert.ok(w.mem.vigilGift, 'the returned pack holds the gift');
done(h);
}
});
+7 -1
View File
@@ -727,11 +727,17 @@ test('3_19 伺夜: wolf pack (2 → 3 wolves, block/bites, lose a wolf instead o
assert.ok(g.runUntil(() => v.skill.active, 5));
g.runUntil(() => !v.skill.active, 20);
approx(gp.dp, gdp + BB(gid).value, 1e-6, '精锐 DP over the full duration');
// the last wolf falls: 战术点形态 (PRTS 狼群领袖 备注) for the 狼影 interval, then the same pack back with one wolf —
// not a new pack after the token's redeploy time (until 0.2.0)
const iv = ds.getToken('token_10028_vigil_wolf', gid).talents[0].bb.interval;
g.b.dealDamage(null, gw, { type: 'true', amount: 1e7 });
g.b.dealDamage(null, gw, { type: 'true', amount: 1e7 });
assert.equal(gw.alive, false);
g.run(gw.base.respawnTime + 0.5);
assert.ok(v.trait.reinforcement !== gw && v.trait.reinforcement?.alive, 'pack re-summoned after its respawn time');
assert.equal(gw.alive, false, 'not back after the token’s redeploy time');
assert.ok(g.runUntil(() => gw.alive, iv), 'back after the 狼影 interval');
assert.equal(v.trait.reinforcement, gw, 'the same pack');
assert.equal(gw.mem.wolves, 1, 'with one wolf');
done(g);
});
+14 -6
View File
@@ -5,7 +5,7 @@ import assert from 'node:assert/strict';
import { makeBattle, chessRec, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
import { hasGeneratedData, getDefaultSource } from '../../server/sim/simdata.js';
import { genericKit } from '../../server/sim/content/generic.js';
import { spawnYanyou, spawnMapChar, TOKEN_IDS, wolfShadows, tileFree, findSummonTile, summonToken } from '../../server/sim/content/tokens.js';
import { spawnYanyou, spawnMapChar, TOKEN_IDS, wolfShadows, wolfShadowInterval, wolfTacticalPoint, tileFree, findSummonTile, summonToken } from '../../server/sim/content/tokens.js';
import { startColdWind, kjeragColdWind, activateTurrets, terrainAt, deviceOverridesOf } from '../../server/sim/content/devices.js';
import { HUSK_REBIRTH } from '../../server/sim/content/enemies.js';
@@ -202,7 +202,7 @@ test('纸偶: appear burst = its ATK × damage_scale arts on the 8 surrounding t
checkInvariants(h.b);
});
test('狼群: board piece becomes 伺夜\'s 援军; 2→3 狼影 (block & hits), fatal sheds a shadow, DEF ignore vs blocked, respawn', REAL, () => {
test('狼群: board piece becomes 伺夜\'s 援军; 2→3 狼影 (block & hits), fatal sheds a shadow, DEF ignore vs blocked, 战术点形态 after the last one', REAL, () => {
let wolfHits = 0;
const h = makeBattle({
defs: { enemies: { enemy_walker: walker({ def: 200, atk: 0 }) } },
@@ -238,11 +238,19 @@ test('狼群: board piece becomes 伺夜\'s 援军; 2→3 狼影 (block & hits),
assert.equal(wolf.hp, wolf.s.maxHp);
h.b.dealDamage(null, wolf, { amount: 1e6, type: 'true' });
h.b.dealDamage(null, wolf, { amount: 1e6, type: 'true' });
// the last shadow falls: 战术点形态 (PRTS 狼群 备注) — off the field, kept, 0 狼影, for the 狼影 interval (data), then
// back on its tile with one 狼影 (no longer a full pack after the token's redeploy time)
assert.equal(wolf.alive, false, 'last shadow falls');
const died = h.b.time;
assert.ok(h.runUntil(() => wolf.alive, 20), 'respawns');
approx(h.b.time - died, wolf.base.respawnTime, 0.3);
assert.equal(wolfShadows(wolf), 2, 'fresh pack');
assert.ok(wolfTacticalPoint(wolf), '战术点形态');
assert.equal(wolf.removed, false);
assert.equal(wolfShadows(wolf), 0);
const died = h.b.time, iv = wolfShadowInterval(wolf.def);
assert.equal(iv, tokDef(TOKEN_IDS.wolfPack, 'chess_char_3_19_b').talents[0].bb.interval, 'the talent interval');
assert.ok(h.runUntil(() => wolf.alive, iv + 1), 'back');
approx(h.b.time - died, iv, 0.004);
assert.deepEqual([wolf.tileR, wolf.tileC], [9, 6], 'on its tile');
assert.equal(wolfShadows(wolf), 1, 'one 狼影');
assert.equal(wolf.s.blockCnt, 1);
checkInvariants(h.b);
});
+15 -15
View File
@@ -1163,34 +1163,34 @@
"round": 10,
"seed": 1111632671,
"waveId": "act1autochess_h03",
"time": 76.4,
"ticks": 2292,
"time": 74.33,
"ticks": 2230,
"reason": "cleared",
"killed": 26,
"total": 26,
"unspawned": 0,
"bossHpLeft": null,
"rngDraws": 365,
"rngDraws": 380,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":26,"total":26,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":321718,"bossDmg":0,"heal":929,"deaths":1,"layerGains":{"miraShip":6}}},
"players": {"p1":{"killed":26,"total":26,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":323078,"bossDmg":0,"heal":929,"deaths":1,"layerGains":{"miraShip":6}}},
"units": [
["p1",1,"op","chess_char_1_08_b",1,"uniequip_002_texas",2924.8,1,7,1,16495,0,0,17,3],
["p1",2,"op","chess_char_2_16_b",1,"uniequip_002_whitew",2304,1,8,4,82108,0,0,50,1],
["p1",1,"op","chess_char_1_08_b",1,"uniequip_002_texas",2924.8,1,7,1,17028,0,0,19,3],
["p1",2,"op","chess_char_2_16_b",1,"uniequip_002_whitew",2304,1,8,4,94384,0,0,49,1],
["p1",3,"op","chess_char_3_18_b",2,"uniequip_002_vulpis",2020,1,6,0,0,0,0,0,0],
["p1",4,"op","chess_char_5_01_b",1,"uniequip_002_excu2",1428.96,1,9,0,0,885,0,0,0],
["p1",5,"op","chess_char_1_07_b",0,"uniequip_002_prove",1527,1,3,7,46217,0,0,40,0],
["p1",6,"op","chess_char_3_19_b",2,"uniequip_002_vigil",1798,1,4,14,174101,0,0,141,2],
["p1",4,"op","chess_char_5_01_b",1,"uniequip_002_excu2",619.57,1,9,0,0,1694,0,0,0],
["p1",5,"op","chess_char_1_07_b",0,"uniequip_002_prove",1527,1,3,9,55294,0,0,48,0],
["p1",6,"op","chess_char_3_19_b",2,"uniequip_002_vigil",1798,1,4,12,155534,0,0,143,2],
["p1",7,"op","chess_char_4_10_b",1,"uniequip_002_aroma",1664,1,10,0,0,0,0,0,0],
["p1",8,"op","chess_char_4_26_b",0,"uniequip_002_bldsk",0,0,11,0,0,2451,929,2,0],
["p1",9,"token","token_10028_vigil_wolf",null,null,0,0,37,0,2796,5814,0,4,null],
["p1",9,"token","token_10028_vigil_wolf",null,null,446.88,1,40,0,838,3477,0,2,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_1158_divman":[20,20,0,7809,164229],"enemy_1160_hvyslr_2":[3,3,0,0,104509],"enemy_1161_tidmag_2":[3,3,0,1341,59720]},
"hooks": [1,39,2292,311,311,481,484,2,30,30,6,5,0,3813,40,4,26,0,1,35,3,0,0],
"events": {"atk":312,"deploy":11,"die":30,"dmg":484,"fx":26,"heal":2,"layer":3,"skill":11,"spawn":37,"status":73},
"fx": {"aoe":3,"bondProc":8,"crit":5,"dp":4,"garrison":3,"wolfShadowLost":3},
"snaps": "d1ead0d3 cda7c00c 8843b429 bd54e255 1de8d432 e6f225a5 7cba25d9 d80a750d"
"enemies": {"enemy_1158_divman":[20,20,0,6116,164229],"enemy_1160_hvyslr_2":[3,3,0,0,104509],"enemy_1161_tidmag_2":[3,3,0,1507,59720]},
"hooks": [1,39,2230,313,313,469,473,2,29,29,6,5,0,3754,38,2,26,0,1,31,3,0,0],
"events": {"atk":314,"deploy":11,"die":29,"dmg":473,"fx":29,"heal":2,"layer":3,"skill":11,"spawn":37,"status":75},
"fx": {"aoe":3,"bondProc":9,"crit":5,"dp":4,"garrison":3,"wolfShadow":2,"wolfShadowLost":3},
"snaps": "d1ead0d3 cda7c00c ccc1aada c0883850 2ba76678 a36bad1d 085c7f88 f7baafc7"
},
"bond-skillfulShip-low": {
"about": "灵巧 low: tier 1 layers 1; chess_char_3_12_a chess_char_1_14_a chess_char_2_04_a chess_char_3_08_a chess_char_3_09_a chess_char_3_10_a chess_char_3_11_a chess_char_3_14_a",
+72 -66
View File
@@ -99,7 +99,7 @@
"combat": "client",
"boost": null,
"setup": {"stageId":"act1autochess_m03","bossId":"boss_5","hiddenBossId":"boss_8","factions":["TIMES","DOT","INVISIBLE"]},
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":11,"bossPoolLeft":0,"virtualMs":106900},
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":10,"bossPoolLeft":0,"virtualMs":106900},
"rng": {"setup":25,"shop":207,"waves":0,"draft":10,"bots":8483,"meta":14},
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
"rounds": [
@@ -118,27 +118,27 @@
},
{
"round": 8,
"ai_0": [1,21,11,0,0,4,8,"9642d3a4",62,"deputShip 1/6, emptyShip 1/2, kazimierzShip 0/8, kjeragShip 1/9, preciShip 0/5, sargonShip 0/8, siracusaShip 1/31, soloShip 1/14, steadShip 0/6, swiftShip 0/8",13,19,6,60]
"ai_0": [1,20,11,0,0,4,8,"9642d3a4",62,"deputShip 1/6, emptyShip 1/2, kazimierzShip 0/8, kjeragShip 1/9, preciShip 0/5, sargonShip 0/8, siracusaShip 1/31, soloShip 1/14, steadShip 0/6, swiftShip 0/8",12,19,7,60]
},
{
"round": 9,
"ai_0": [1,21,12,0,0,4,8,"765a4a23",99,"arcaneShip 0/2, deputShip 1/8, emptyShip 1/4, kazimierzShip 0/8, kjeragShip 0/14, preciShip 0/12, sargonShip 0/8, siracusaShip 1/53, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/16",18,18,0,73]
"ai_0": [1,20,12,0,0,4,8,"765a4a23",99,"arcaneShip 0/2, deputShip 1/8, emptyShip 1/4, kazimierzShip 0/8, kjeragShip 0/14, preciShip 0/12, sargonShip 0/8, siracusaShip 1/53, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/16",18,18,0,73]
},
{
"round": 10,
"ai_0": [1,21,12,0,0,5,8,"44e95a01",112,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/3, preciShip 0/12, sargonShip 0/8, siracusaShip 2/69, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",24,24,0,87]
"ai_0": [1,20,12,0,0,5,8,"44e95a01",112,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/3, preciShip 0/12, sargonShip 0/8, siracusaShip 2/69, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",24,24,0,87]
},
{
"round": 11,
"ai_0": [1,21,12,0,0,5,8,"12000646",141,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/6, preciShip 0/12, sargonShip 0/8, siracusaShip 2/95, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",24,24,0,98]
"ai_0": [1,20,12,0,0,5,8,"12000646",141,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/6, preciShip 0/12, sargonShip 0/8, siracusaShip 2/95, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",24,24,0,98]
},
{
"round": 12,
"ai_0": [1,11,12,0,0,6,8,"03cea672",154,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/9, preciShip 0/12, sargonShip 0/8, siracusaShip 2/105, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",16,31,15,109]
"ai_0": [1,10,12,0,0,6,8,"03cea672",154,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/9, preciShip 0/12, sargonShip 0/8, siracusaShip 2/105, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",17,31,14,109]
},
{
"round": 13,
"ai_0": [1,11,12,0,0,6,8,"1cf42739",230,"arcaneShip 0/2, deputShip 2/12, emptyShip 1/8, kazimierzShip 0/16, kjeragShip 0/22, miraShip 1/13, preciShip 0/12, sargonShip 0/8, siracusaShip 1/129, soloShip 1/20, steadShip 0/6, suntShip 1/6, swiftShip 1/42",115,115,0,121]
"ai_0": [1,10,12,0,0,6,8,"1cf42739",230,"arcaneShip 0/2, deputShip 2/12, emptyShip 1/8, kazimierzShip 0/16, kjeragShip 0/22, miraShip 1/13, preciShip 0/12, sargonShip 0/8, siracusaShip 1/129, soloShip 1/20, steadShip 0/6, suntShip 1/6, swiftShip 1/42",115,115,0,121]
}
],
"players": {
@@ -147,12 +147,12 @@
"victory": true,
"roundsPassed": 14,
"eliminatedRound": null,
"lp": 11,
"lp": 10,
"bandId": "band_ermengard",
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
"trophies": 0,
"reward": 400,
"stats": {"activatedLayers":290,"bossDamage":97500,"dmgDealt":1700976,"gold":131,"itemsEquipped":23,"kills":361,"leaks":21,"lpLost":16,"merges":5,"perfectRounds":11,"refreshes":15},
"stats": {"activatedLayers":290,"bossDamage":97500,"dmgDealt":1691540,"gold":131,"itemsEquipped":23,"kills":361,"leaks":21,"lpLost":17,"merges":5,"perfectRounds":11,"refreshes":15},
"lineup": ["2_14_a@12,5","5_10_a@12,7[4_07_e_a+3_08_e_b]","5_20_b@11,5[6_09_e_a+5_02_e_b]","3_20_b@11,8[3_06_e_a+4_08_e_a]","6_11_a@10,3[4_12_e_a+5_02_e_b]","3_18_a@10,8[2_04_e_b+4_05_e_b]","6_19_a@9,4[4_04_e_a+4_10_e_a]","1_08_a@9,5[1_01_e_a+1_02_e_a]"],
"bonds": ["arcaneShip 0/2","deputShip 1/14","emptyShip 1/10","kazimierzShip 0/16","kjeragShip 1/24","maniShip 1/2","miraShip 1/15","preciShip 0/12","sargonShip 0/8","siracusaShip 1/151","soloShip 1/22","steadShip 0/6","suntShip 1/8","swiftShip 1/44"]
}
@@ -511,7 +511,7 @@
"combat": "client",
"boost": null,
"setup": {"stageId":"act2autochess_m04","bossId":"boss_3","hiddenBossId":"boss_8","factions":["FLY","TIMES","DOT"]},
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":55,"bossPoolLeft":0,"virtualMs":121400},
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":54,"bossPoolLeft":0,"virtualMs":121400},
"rng": {"setup":25,"shop":449,"waves":0,"draft":40,"bots":21634,"meta":8},
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
"rounds": [
@@ -577,7 +577,7 @@
},
{
"round": 13,
"ai_0": [1,37,12,0,2,6,8,"5deeb03a",214,"arcaneShip 2/70, miraShip 1/12, preciShip 0/10, sargonShip 0/10, siracusaShip 1/98, skillfulShip 1/28, soloShip 1/6, suntShip 1/0, swiftShip 0/32",26,34,8,125],
"ai_0": [1,36,12,0,2,6,8,"5deeb03a",214,"arcaneShip 2/70, miraShip 1/12, preciShip 0/10, sargonShip 0/10, siracusaShip 1/98, skillfulShip 1/28, soloShip 1/6, suntShip 1/0, swiftShip 0/32",25,34,9,125],
"ai_1": [1,18,12,0,0,6,8,"49c3b995",195,"arcaneShip 2/36, kazimierzShip 1/40, kjeragShip 0/8, preciShip 1/50, sargonShip 0/10, steadShip 1/0, suntShip 1/0, swiftShip 1/69",22,34,12,127]
}
],
@@ -587,12 +587,12 @@
"victory": true,
"roundsPassed": 14,
"eliminatedRound": null,
"lp": 37,
"lp": 36,
"bandId": "band_sarkazb",
"title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"},
"trophies": 4,
"reward": 400,
"stats": {"activatedLayers":250,"bossDamage":287418,"dmgDealt":3020895,"gold":139,"itemsEquipped":12,"kills":334,"leaks":9,"lpLost":8,"merges":5,"perfectRounds":11,"refreshes":23},
"stats": {"activatedLayers":250,"bossDamage":287418,"dmgDealt":2992890,"gold":139,"itemsEquipped":12,"kills":333,"leaks":10,"lpLost":9,"merges":5,"perfectRounds":11,"refreshes":23},
"lineup": ["3_19_b@11,7[2_04_e_b]","5_12_a@11,8","4_10_a@10,5[1_02_e_b+2_01_e_a]","6_06_a@9,4","1_08_b@9,5","6_16_a@9,7","5_20_a@9,8[3_05_e_a+3_02_e_b]","3_13_b@9,9"],
"bonds": ["arcaneShip 2/86","miraShip 1/12","preciShip 0/10","sargonShip 0/10","siracusaShip 1/110","skillfulShip 1/36","soloShip 1/6","suntShip 1/0","swiftShip 0/32"]
},
@@ -733,8 +733,8 @@
"combat": "client",
"boost": null,
"setup": {"stageId":"act2autochess_m03","bossId":"boss_4","hiddenBossId":"boss_9","factions":["FLY","TIMES","REFLECTION"]},
"end": {"victory":false,"roundsPassed":11,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":115700},
"rng": {"setup":25,"shop":537,"waves":0,"draft":75,"bots":24773,"meta":19},
"end": {"victory":false,"roundsPassed":12,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":124350},
"rng": {"setup":25,"shop":606,"waves":0,"draft":76,"bots":26471,"meta":24},
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
"rounds": [
{
@@ -789,25 +789,31 @@
"round": 9,
"ai_0": [1,8,12,0,6,4,8,"6d558be7",157,"deputShip 2/32, indomShip 1/4, kazimierzShip 0/2, kjeragShip 1/28, skillfulShip 1/51, soloShip 1/16, steadShip 1/22, suntShip 1/4, swiftShip 0/20",65,68,3,81],
"ai_1": [1,24,12,0,1,4,8,"ad1959de",111,"deputShip 2/35, kazimierzShip 0/7, skillfulShip 1/22, soloShip 1/16, suntShip 1/16, swiftShip 0/21, victoriaShip 1/22",41,52,11,73],
"ai_2": [1,7,12,0,0,4,8,"e0957cd2",127,"deputShip 0/10, miraShip 1/3, siracusaShip 2/78, soloShip 1/22, suntShip 1/6, swiftShip 1/18, yanShip 0/1",51,60,9,75]
"ai_2": [1,13,12,0,0,4,8,"790ec77b",127,"deputShip 0/10, miraShip 1/3, siracusaShip 2/78, soloShip 1/22, suntShip 1/6, swiftShip 1/18, yanShip 0/1",59,62,3,75]
},
{
"round": 10,
"ai_0": [1,8,18,0,0,5,8,"7365b613",207,"deputShip 2/36, indomShip 1/8, kazimierzShip 0/2, kjeragShip 1/41, miraShip 1/4, skillfulShip 1/57, soloShip 1/20, steadShip 1/33, suntShip 1/8, swiftShip 0/20",18,18,0,99],
"ai_1": [1,15,13,0,0,5,8,"776e574d",124,"deputShip 2/40, indomShip 1/5, kazimierzShip 0/7, skillfulShip 1/26, soloShip 1/20, suntShip 0/16, swiftShip 0/21, victoriaShip 1/33",3,18,15,86],
"ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/10, miraShip 0/5, siracusaShip 0/84, soloShip 0/22, suntShip 0/6, swiftShip 0/23, yanShip 0/1",5,18,13,86]
"ai_0": [1,8,18,0,0,5,8,"0cdaabe2",207,"deputShip 2/36, indomShip 1/8, kazimierzShip 0/2, kjeragShip 1/41, miraShip 1/4, skillfulShip 1/57, soloShip 1/20, steadShip 1/33, suntShip 1/8, swiftShip 0/20",18,18,0,99],
"ai_1": [1,20,13,0,0,5,8,"5553a152",132,"deputShip 2/40, indomShip 1/9, kazimierzShip 0/7, skillfulShip 1/26, soloShip 1/20, suntShip 0/16, swiftShip 0/21, victoriaShip 1/37",9,18,9,86],
"ai_2": [1,7,12,0,2,5,8,"d71a0d82",137,"deputShip 0/10, miraShip 1/4, siracusaShip 2/82, soloShip 1/22, suntShip 1/6, swiftShip 1/23, yanShip 0/1",9,18,9,86]
},
{
"round": 11,
"ai_0": [1,3,12,0,1,5,8,"d2be236f",254,"deputShip 2/40, indomShip 1/12, kazimierzShip 0/2, kjeragShip 1/51, miraShip 1/8, siracusaShip 0/6, skillfulShip 1/63, soloShip 1/24, steadShip 1/44, suntShip 1/12, swiftShip 0/26",20,25,5,110],
"ai_1": [1,5,12,0,0,5,8,"3d8f81b8",150,"deputShip 1/42, indomShip 1/11, kazimierzShip 0/7, skillfulShip 1/28, soloShip 0/20, suntShip 0/16, swiftShip 0/21, victoriaShip 2/69, yanShip 0/5",7,25,18,98],
"ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/10, miraShip 0/5, siracusaShip 0/84, soloShip 0/22, suntShip 0/6, swiftShip 0/23, yanShip 0/1",null,null,null,86]
"ai_0": [1,3,12,0,1,5,8,"15e6bba0",255,"deputShip 2/40, indomShip 1/12, kazimierzShip 0/2, kjeragShip 1/52, miraShip 1/8, siracusaShip 0/6, skillfulShip 1/63, soloShip 1/24, steadShip 1/44, suntShip 1/12, swiftShip 0/26",20,25,5,110],
"ai_1": [1,14,12,0,1,5,8,"458bff99",175,"deputShip 2/44, indomShip 1/15, kazimierzShip 0/11, skillfulShip 1/30, soloShip 1/24, steadShip 1/8, suntShip 0/16, swiftShip 0/25, victoriaShip 1/54",19,25,6,100],
"ai_2": [0,0,14,0,0,5,0,"811c9dc5",0,"arcaneShip 0/16, deputShip 0/10, miraShip 0/5, siracusaShip 0/100, soloShip 0/22, suntShip 0/6, swiftShip 0/33, yanShip 0/1",5,25,20,101]
},
{
"round": 12,
"ai_0": [0,0,13,0,0,6,0,"811c9dc5",0,"deputShip 0/44, indomShip 0/16, kazimierzShip 0/2, kjeragShip 0/62, miraShip 0/12, siracusaShip 0/6, skillfulShip 0/69, soloShip 0/28, steadShip 0/55, suntShip 0/16, swiftShip 0/26",16,57,41,124],
"ai_1": [0,0,12,0,0,6,0,"811c9dc5",0,"deputShip 0/44, indomShip 0/19, kazimierzShip 0/7, skillfulShip 0/30, soloShip 0/20, suntShip 0/16, swiftShip 0/21, victoriaShip 0/102, yanShip 0/5",24,57,33,109],
"ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/10, miraShip 0/5, siracusaShip 0/84, soloShip 0/22, suntShip 0/6, swiftShip 0/23, yanShip 0/1",null,null,null,86]
"ai_0": [0,0,13,0,0,6,0,"811c9dc5",0,"deputShip 0/44, indomShip 0/16, kazimierzShip 0/2, kjeragShip 0/61, miraShip 0/12, siracusaShip 0/6, skillfulShip 0/69, soloShip 0/28, steadShip 0/55, suntShip 0/16, swiftShip 0/26",14,57,43,122],
"ai_1": [1,4,13,0,0,6,8,"fedd02aa",227,"deputShip 2/48, indomShip 1/23, kazimierzShip 0/11, kjeragShip 0/5, skillfulShip 1/39, soloShip 1/28, steadShip 1/16, suntShip 0/16, swiftShip 0/25, victoriaShip 1/73",17,57,40,113],
"ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/16, deputShip 0/10, miraShip 0/5, siracusaShip 0/100, soloShip 0/22, suntShip 0/6, swiftShip 0/33, yanShip 0/1",null,null,null,101]
},
{
"round": 13,
"ai_0": [0,0,0,0,0,6,0,"811c9dc5",0,"deputShip 0/44, indomShip 0/16, kazimierzShip 0/2, kjeragShip 0/61, miraShip 0/12, siracusaShip 0/6, skillfulShip 0/69, soloShip 0/28, steadShip 0/55, suntShip 0/16, swiftShip 0/26",null,null,null,122],
"ai_1": [0,0,12,0,0,6,0,"811c9dc5",0,"deputShip 0/52, indomShip 0/27, kazimierzShip 0/11, kjeragShip 0/5, skillfulShip 0/43, soloShip 0/32, steadShip 0/26, suntShip 0/16, swiftShip 0/25, victoriaShip 0/89",5,26,21,124],
"ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/16, deputShip 0/10, miraShip 0/5, siracusaShip 0/100, soloShip 0/22, suntShip 0/6, swiftShip 0/33, yanShip 0/1",null,null,null,101]
}
],
"players": {
@@ -818,40 +824,40 @@
"eliminatedRound": 12,
"lp": 0,
"bandId": "band_chiave",
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
"trophies": 2,
"reward": 255,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":4777308,"gold":124,"itemsEquipped":17,"kills":279,"leaks":70,"lpLost":30,"merges":3,"perfectRounds":4,"refreshes":14},
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":4492000,"gold":122,"itemsEquipped":17,"kills":275,"leaks":72,"lpLost":30,"merges":3,"perfectRounds":4,"refreshes":15},
"lineup": [],
"bonds": ["deputShip 0/44","indomShip 0/16","kazimierzShip 0/2","kjeragShip 0/62","miraShip 0/12","siracusaShip 0/6","skillfulShip 0/69","soloShip 0/28","steadShip 0/55","suntShip 0/16","swiftShip 0/26"]
"bonds": ["deputShip 0/44","indomShip 0/16","kazimierzShip 0/2","kjeragShip 0/61","miraShip 0/12","siracusaShip 0/6","skillfulShip 0/69","soloShip 0/28","steadShip 0/55","suntShip 0/16","swiftShip 0/26"]
},
"ai_1": {
"alive": false,
"victory": false,
"roundsPassed": 11,
"eliminatedRound": 12,
"roundsPassed": 12,
"eliminatedRound": 13,
"lp": 0,
"bandId": "band_sarkazb",
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
"trophies": 2,
"reward": 255,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2690412,"gold":109,"itemsEquipped":9,"kills":258,"leaks":92,"lpLost":50,"merges":3,"perfectRounds":5,"refreshes":18},
"trophies": 3,
"reward": 298,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3467151,"gold":124,"itemsEquipped":13,"kills":274,"leaks":102,"lpLost":51,"merges":3,"perfectRounds":5,"refreshes":20},
"lineup": [],
"bonds": ["deputShip 0/44","indomShip 0/19","kazimierzShip 0/7","skillfulShip 0/30","soloShip 0/20","suntShip 0/16","swiftShip 0/21","victoriaShip 0/102","yanShip 0/5"]
"bonds": ["deputShip 0/52","indomShip 0/27","kazimierzShip 0/11","kjeragShip 0/5","skillfulShip 0/43","soloShip 0/32","steadShip 0/26","suntShip 0/16","swiftShip 0/25","victoriaShip 0/89"]
},
"ai_2": {
"alive": false,
"victory": false,
"roundsPassed": 9,
"eliminatedRound": 10,
"roundsPassed": 10,
"eliminatedRound": 11,
"lp": 0,
"bandId": "band_lmlee",
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
"trophies": 2,
"reward": 191,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1500871,"gold":86,"itemsEquipped":5,"kills":199,"leaks":45,"lpLost":29,"merges":2,"perfectRounds":4,"refreshes":14},
"reward": 213,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1915977,"gold":101,"itemsEquipped":9,"kills":216,"leaks":55,"lpLost":30,"merges":4,"perfectRounds":4,"refreshes":18},
"lineup": [],
"bonds": ["deputShip 0/10","miraShip 0/5","siracusaShip 0/84","soloShip 0/22","suntShip 0/6","swiftShip 0/23","yanShip 0/1"]
"bonds": ["arcaneShip 0/16","deputShip 0/10","miraShip 0/5","siracusaShip 0/100","soloShip 0/22","suntShip 0/6","swiftShip 0/33","yanShip 0/1"]
}
}
},
@@ -1181,8 +1187,8 @@
"combat": "server",
"boost": null,
"setup": {"stageId":"act1autochess_m01","bossId":"boss_1","hiddenBossId":"boss_9","factions":["FLY","DOT","REFLECTION"]},
"end": {"victory":false,"roundsPassed":12,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":114800},
"rng": {"setup":25,"shop":383,"waves":0,"draft":34,"bots":16823,"meta":105},
"end": {"victory":false,"roundsPassed":12,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":113300},
"rng": {"setup":25,"shop":389,"waves":0,"draft":34,"bots":16823,"meta":81},
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
"rounds": [
{
@@ -1217,68 +1223,68 @@
},
{
"round": 7,
"ai_0": [1,19,13,0,0,4,8,"28ba7985",39,"arcaneShip 0/2, kjeragShip 0/2, preciShip 1/2, siracusaShip 1/20, soloShip 1/11, steadShip 0/2, swiftShip 1/6",11,27,16,52],
"ai_0": [1,19,13,0,0,4,8,"bfa893e9",41,"arcaneShip 0/2, kjeragShip 0/2, preciShip 1/2, siracusaShip 1/22, soloShip 1/11, steadShip 0/2, swiftShip 1/6",12,27,15,52],
"ai_1": [1,18,11,0,1,4,8,"eac0b2d5",6,"arcaneShip 0/2, kjeragShip 0/4, preciShip 1/2, siracusaShip 1/0, soloShip 1/0, steadShip 2/4",16,27,11,51]
},
{
"round": 8,
"ai_0": [1,19,11,0,0,4,8,"7c223b06",60,"arcaneShip 1/6, emptyShip 1/0, kjeragShip 0/7, preciShip 1/7, siracusaShip 1/30, soloShip 1/11, steadShip 0/2, swiftShip 1/6",23,23,0,64],
"ai_0": [1,19,11,0,0,4,8,"7c223b06",62,"arcaneShip 1/6, emptyShip 1/0, kjeragShip 0/7, preciShip 1/7, siracusaShip 1/32, soloShip 1/11, steadShip 0/2, swiftShip 1/6",23,23,0,64],
"ai_1": [1,16,12,0,0,4,8,"2243ec88",26,"arcaneShip 1/8, indomShip 1/1, kjeragShip 0/4, preciShip 0/2, siracusaShip 1/10, soloShip 1/0, steadShip 2/7",18,23,5,61]
},
{
"round": 9,
"ai_0": [1,19,12,0,1,4,8,"588666d3",72,"arcaneShip 1/12, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 2/7, siracusaShip 1/36, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",16,16,0,78],
"ai_1": [1,15,12,0,0,4,8,"0f3cf873",67,"arcaneShip 1/14, indomShip 1/10, kjeragShip 0/4, preciShip 0/2, siracusaShip 1/32, soloShip 1/0, steadShip 1/11, victoriaShip 0/8, visiShip 0/8",14,15,1,73]
"ai_0": [1,19,12,0,1,4,8,"6dfc43a6",74,"arcaneShip 2/12, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 1/7, siracusaShip 1/38, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",16,16,0,77],
"ai_1": [1,16,12,0,0,4,8,"bbe510e7",82,"arcaneShip 1/16, indomShip 1/10, kjeragShip 0/8, preciShip 1/22, siracusaShip 1/20, soloShip 1/0, steadShip 1/14, victoriaShip 0/8, visiShip 0/8",15,15,0,74]
},
{
"round": 10,
"ai_0": [1,17,13,0,0,5,8,"bb29d023",80,"arcaneShip 1/16, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 2/7, siracusaShip 1/40, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",22,24,2,91],
"ai_1": [1,14,12,0,0,5,8,"34e3de0f",83,"arcaneShip 1/14, indomShip 1/20, kjeragShip 0/8, preciShip 0/2, siracusaShip 1/32, soloShip 1/0, steadShip 1/17, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",22,23,1,85]
"ai_0": [1,18,13,0,0,5,8,"e24edc84",78,"arcaneShip 2/14, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 1/7, siracusaShip 1/40, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",23,24,1,89],
"ai_1": [1,16,12,0,0,5,8,"f2b57087",100,"arcaneShip 1/16, indomShip 1/12, kjeragShip 0/8, miraShip 0/3, preciShip 1/35, siracusaShip 1/20, soloShip 1/0, steadShip 1/17, suntShip 1/0, victoriaShip 0/8, visiShip 0/8, yanShip 0/6",23,23,0,86]
},
{
"round": 11,
"ai_0": [1,17,12,0,0,5,8,"f0ff6294",86,"arcaneShip 1/22, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 1/7, siracusaShip 2/46, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 0/6, victoriaShip 0/8, visiShip 0/8",23,23,0,104],
"ai_1": [1,8,12,0,0,5,8,"f020d91c",127,"arcaneShip 1/20, indomShip 1/30, kjeragShip 0/8, preciShip 0/2, siracusaShip 1/58, soloShip 1/0, steadShip 1/19, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",17,23,6,97]
"ai_0": [1,9,12,0,0,5,8,"586b372b",90,"arcaneShip 2/20, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 1/7, siracusaShip 1/46, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",14,23,9,101],
"ai_1": [1,12,12,0,0,5,8,"b5188f04",124,"arcaneShip 2/26, indomShip 1/14, kazimierzShip 0/8, kjeragShip 0/16, miraShip 0/3, preciShip 1/54, siracusaShip 1/30, soloShip 1/0, steadShip 0/17, suntShip 1/0, swiftShip 0/8, victoriaShip 0/8, visiShip 0/8, yanShip 0/6",19,23,4,99]
},
{
"round": 12,
"ai_0": [1,7,12,0,0,6,8,"09ffe6d0",86,"arcaneShip 1/22, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 2/7, siracusaShip 1/46, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 0/6, victoriaShip 0/8, visiShip 0/8",35,47,12,116],
"ai_1": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/20, indomShip 0/50, kjeragShip 0/8, preciShip 0/2, siracusaShip 0/58, steadShip 0/22, victoriaShip 0/8, visiShip 0/8",20,46,26,109]
"ai_0": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/24, indomShip 0/8, kjeragShip 0/7, preciShip 0/7, siracusaShip 0/50, soloShip 0/11, steadShip 0/2, swiftShip 0/6, victoriaShip 0/8, visiShip 0/8",27,46,19,114],
"ai_1": [1,2,12,0,0,6,8,"f5a033f5",153,"arcaneShip 2/26, indomShip 0/14, kazimierzShip 0/8, kjeragShip 0/16, miraShip 0/3, preciShip 1/78, siracusaShip 1/30, soloShip 1/0, steadShip 1/19, suntShip 1/0, swiftShip 0/8, victoriaShip 0/8, visiShip 0/8, yanShip 0/6",27,46,19,111]
},
{
"round": 13,
"ai_0": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/36, indomShip 0/8, kjeragShip 0/7, miraShip 0/3, preciShip 0/9, siracusaShip 0/58, soloShip 0/11, steadShip 0/2, swiftShip 0/6, victoriaShip 0/8, visiShip 0/8, yanShip 0/6",21,39,18,128],
"ai_1": [0,0,0,0,0,6,0,"811c9dc5",0,"arcaneShip 0/20, indomShip 0/50, kjeragShip 0/8, preciShip 0/2, siracusaShip 0/58, steadShip 0/22, victoriaShip 0/8, visiShip 0/8",null,null,null,109]
"ai_0": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/24, indomShip 0/8, kjeragShip 0/7, preciShip 0/7, siracusaShip 0/50, soloShip 0/11, steadShip 0/2, swiftShip 0/6, victoriaShip 0/8, visiShip 0/8",null,null,null,114],
"ai_1": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/32, indomShip 0/14, kazimierzShip 0/16, kjeragShip 0/24, miraShip 0/3, preciShip 0/102, siracusaShip 0/42, steadShip 0/19, swiftShip 0/16, victoriaShip 0/8, visiShip 0/8, yanShip 0/6",27,39,12,121]
}
],
"players": {
"ai_0": {
"alive": false,
"victory": false,
"roundsPassed": 12,
"eliminatedRound": 13,
"roundsPassed": 11,
"eliminatedRound": 12,
"lp": 0,
"bandId": "band_doberm",
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
"trophies": 3,
"reward": 280,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2828648,"gold":128,"itemsEquipped":14,"kills":230,"leaks":49,"lpLost":33,"merges":3,"perfectRounds":8,"refreshes":18},
"trophies": 2,
"reward": 240,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2035070,"gold":114,"itemsEquipped":13,"kills":194,"leaks":45,"lpLost":31,"merges":4,"perfectRounds":7,"refreshes":15},
"lineup": [],
"bonds": ["arcaneShip 0/36","indomShip 0/8","kjeragShip 0/7","miraShip 0/3","preciShip 0/9","siracusaShip 0/58","soloShip 0/11","steadShip 0/2","swiftShip 0/6","victoriaShip 0/8","visiShip 0/8","yanShip 0/6"]
"bonds": ["arcaneShip 0/24","indomShip 0/8","kjeragShip 0/7","preciShip 0/7","siracusaShip 0/50","soloShip 0/11","steadShip 0/2","swiftShip 0/6","victoriaShip 0/8","visiShip 0/8"]
},
"ai_1": {
"alive": false,
"victory": false,
"roundsPassed": 11,
"eliminatedRound": 12,
"roundsPassed": 12,
"eliminatedRound": 13,
"lp": 0,
"bandId": "band_malkie",
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
"trophies": 2,
"reward": 240,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1891141,"gold":109,"itemsEquipped":16,"kills":179,"leaks":50,"lpLost":30,"merges":2,"perfectRounds":6,"refreshes":14},
"trophies": 3,
"reward": 280,
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2655538,"gold":121,"itemsEquipped":18,"kills":217,"leaks":51,"lpLost":36,"merges":3,"perfectRounds":8,"refreshes":17},
"lineup": [],
"bonds": ["arcaneShip 0/20","indomShip 0/50","kjeragShip 0/8","preciShip 0/2","siracusaShip 0/58","steadShip 0/22","victoriaShip 0/8","visiShip 0/8"]
"bonds": ["arcaneShip 0/32","indomShip 0/14","kazimierzShip 0/16","kjeragShip 0/24","miraShip 0/3","preciShip 0/102","siracusaShip 0/42","steadShip 0/19","swiftShip 0/16","victoriaShip 0/8","visiShip 0/8","yanShip 0/6"]
}
}
},
+75 -74
View File
@@ -387,55 +387,56 @@
"time": 140,
"ticks": 4200,
"reason": "timeout",
"killed": 112,
"total": 162,
"killed": 103,
"total": 165,
"unspawned": 0,
"bossHpLeft": null,
"rngDraws": 64,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":112,"total":162,"perfect":false,"leaks":50,"leaksUncounted":1,"coins":2,"dmg":202293,"bossDmg":0,"heal":650,"deaths":11,"layerGains":{}}},
"players": {"p1":{"killed":103,"total":165,"perfect":false,"leaks":62,"leaksUncounted":1,"coins":4,"dmg":206927,"bossDmg":0,"heal":650,"deaths":11,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_3_12_a",2,null,2334,1,180,2,1060,2334,0,11,1],
["p1",2,"op","chess_char_3_16_a",1,null,0,0,6,0,3135,2823,650,18,1],
["p1",3,"op","chess_char_3_18_a",2,null,1481,1,186,6,10342,2962,0,29,1],
["p1",4,"op","chess_char_3_08_a",1,null,1326,1,4,12,21498,268,0,20,2],
["p1",5,"op","chess_char_3_09_a",1,null,739,1,7,18,20206,528,0,64,2],
["p1",6,"op","chess_char_3_10_a",1,null,0,0,10,13,7811,1650,0,21,2],
["p1",7,"op","chess_char_3_11_a",1,null,1316,1,187,14,11933,1316,0,22,3],
["p1",8,"op","chess_char_3_13_a",1,null,1064,1,185,3,5201,1064,0,22,10],
["p1",9,"op","chess_char_3_14_a",1,null,0,0,5,5,10307,1251,0,30,1],
["p1",10,"op","chess_char_3_17_a",1,null,0,0,8,13,53646,2246,0,78,4],
["p1",11,"op","chess_char_3_19_a",2,null,731,1,141,16,34429,2147,0,91,2],
["p1",12,"op","chess_char_3_20_a",1,null,1121,1,189,2,5255,1121,0,40,1],
["p1",13,"token","token_10028_vigil_wolf",null,null,880,1,188,8,17470,5205,0,47,null],
["p1",1,"op","chess_char_3_12_a",2,null,2277.37,1,183,4,2509,2391,0,17,1],
["p1",2,"op","chess_char_3_16_a",1,null,0,0,6,0,468,2823,650,29,1],
["p1",3,"op","chess_char_3_18_a",2,null,1481,1,189,5,10012,2962,0,27,1],
["p1",4,"op","chess_char_3_08_a",1,null,1594,1,4,12,28602,0,0,20,2],
["p1",5,"op","chess_char_3_09_a",1,null,927,1,7,14,27278,340,0,63,2],
["p1",6,"op","chess_char_3_10_a",1,null,0,0,10,14,14131,1650,0,25,2],
["p1",7,"op","chess_char_3_11_a",1,null,1316,1,190,12,10443,1316,0,19,3],
["p1",8,"op","chess_char_3_13_a",1,null,1064,1,186,3,4643,1064,0,21,9],
["p1",9,"op","chess_char_3_14_a",1,null,0,0,5,11,14855,1251,0,49,2],
["p1",10,"op","chess_char_3_17_a",1,null,0,0,8,8,43714,2246,0,62,4],
["p1",11,"op","chess_char_3_19_a",2,null,731,1,147,12,32311,2147,0,87,2],
["p1",12,"op","chess_char_3_20_a",1,null,1121,1,191,2,5153,1121,0,36,1],
["p1",13,"token","token_10028_vigil_wolf",null,null,599.46,0,102,4,4175,1361,0,13,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]
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
["p1","~1","token","token_10028_vigil_wolf",null,null,0,0,148,2,8632,1697,0,19,null]
],
"enemies": {
"enemy_10029_vtshld": [1,0,0,0,0],
"enemy_10073_mpcar": [1,1,0,0,2304],
"enemy_10127_rkmbst_2": [1,0,1,4291,19874],
"enemy_1045_hammer": [1,0,1,145,10120],
"enemy_1045_hammer": [1,0,1,145,8498],
"enemy_1121_lifbos_2": [1,0,1,453,2161],
"enemy_1172_dugago": [1,0,1,573,2706],
"enemy_1195_sfyin": [36,25,11,1028,67596],
"enemy_1195_sfyin_2": [15,8,7,2711,37686],
"enemy_1196_msfyin": [50,40,10,0,83],
"enemy_1196_msfyin_2": [16,15,1,0,47],
"enemy_1197_sfshu": [9,7,2,506,21044],
"enemy_1198_msfshu": [21,13,8,0,46],
"enemy_1203_sfhu": [1,1,0,4133,7488],
"enemy_1204_msfhu": [4,0,4,0,5],
"enemy_1252_lysytb_2": [1,1,0,1377,10368],
"enemy_1305_mhslim": [1,0,1,608,268],
"enemy_1407_hummbd": [1,1,0,347,5184],
"enemy_1512_mcmstr": [1,0,1,7851,11737],
"enemy_2021_syfish": [1,0,1,145,3577]
"enemy_1172_dugago": [1,0,1,346,7726],
"enemy_1195_sfyin": [36,29,7,906,76246],
"enemy_1195_sfyin_2": [15,7,8,1605,39934],
"enemy_1196_msfyin": [58,44,14,0,99],
"enemy_1196_msfyin_2": [14,3,11,0,18],
"enemy_1197_sfshu": [9,6,3,468,21922],
"enemy_1198_msfshu": [18,10,8,0,39],
"enemy_1203_sfhu": [1,1,0,4335,7488],
"enemy_1204_msfhu": [4,0,4,0,4],
"enemy_1252_lysytb_2": [1,0,1,1937,5600],
"enemy_1305_mhslim": [1,1,0,232,1382],
"enemy_1407_hummbd": [1,1,0,77,5184],
"enemy_1512_mcmstr": [1,0,1,6828,7272],
"enemy_2021_syfish": [1,0,1,0,1177]
},
"hooks": [1,188,4200,679,679,939,964,449,126,176,30,30,0,18036,13,43,163,50,1,137,0,0,0],
"events": {"atk":791,"bounty":1,"deploy":23,"die":126,"dmg":950,"fx":127,"leak":50,"skill":60,"spawn":178,"status":56},
"fx": {"aoe":2,"camouflage":1,"displace":20,"dp":3,"explode":20,"expose":1,"pull":20,"sp":1,"strike":3,"summon":42,"volley":7,"wolfShadow":2,"wolfShadowLost":5},
"snaps": "1e865453 bf5ab273 da567691 fe6968a3 5a5519a4 176a1866 672c29cb e491de64 2d441cf0 124654c7 edb7abad 5b41e1b9 bcd0ec37 376197fb"
"hooks": [1,190,4200,653,653,915,920,449,116,179,30,29,0,19129,13,48,166,62,1,123,0,0,0],
"events": {"atk":805,"bounty":2,"deploy":22,"die":117,"dmg":909,"fx":142,"leak":62,"skill":59,"spawn":182,"status":53},
"fx": {"aoe":2,"camouflage":1,"displace":28,"dp":2,"explode":19,"pull":28,"revive":1,"sp":1,"strike":3,"summon":44,"volley":7,"wolfShadow":2,"wolfShadowLost":4},
"snaps": "1e865453 bf5ab273 da567691 fe6968a3 154137f2 3b5be2d3 2bf6358c 68ab8b27 047d4e31 50a382a4 359d8bbe a898fdd8 7f9a5c3c 08b0e4de"
},
"roster-008": {
"about": "pass 0 band 4: chess_char_3_06_b chess_char_3_12_b chess_char_3_02_b chess_char_3_03_b chess_char_3_04_b chess_char_3_05_b chess_char_3_21_a chess_char_3_01_b chess_char_3_08_b chess_char_3_09_b chess_char_3_10_b chess_char_3_11_b",
@@ -542,23 +543,23 @@
"total": 171,
"unspawned": 0,
"bossHpLeft": null,
"rngDraws": 151,
"rngDraws": 164,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":171,"total":171,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":12,"dmg":279608,"bossDmg":0,"heal":534,"deaths":1,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_3_16_b",1,"uniequip_002_snakek",5363,1,3,0,0,0,0,0,0],
["p1",2,"op","chess_char_4_01_a",2,null,4563.74,1,6,0,0,0,0,0,0],
["p1",3,"op","chess_char_3_18_b",2,"uniequip_002_vulpis",3494.6,1,8,4,18685,534,534,22,2],
["p1",4,"op","chess_char_4_04_a",1,null,1977.6,1,12,9,19246,1067,0,152,4],
["p1",5,"op","chess_char_3_13_b",1,"uniequip_002_malist",2378.75,1,9,14,11985,0,0,67,31],
["p1",6,"op","chess_char_3_14_b",1,"uniequip_002_slbell",2793.95,1,10,6,9477,0,0,49,3],
["p1",7,"op","chess_char_3_17_b",1,"uniequip_002_shotst",3811.23,1,14,20,28968,1201,0,59,9],
["p1",8,"op","chess_char_3_19_b",2,"uniequip_002_vigil",3110.54,1,4,2,5707,0,0,12,4],
["p1",9,"op","chess_char_3_20_b",1,"uniequip_002_kjera",2508.5,1,7,3,1457,0,0,14,2],
["p1",10,"op","chess_char_3_21_b",2,"uniequip_002_archet",2750.7,1,11,67,114797,0,0,211,5],
["p1",3,"op","chess_char_3_18_b",2,"uniequip_002_vulpis",3494.6,1,8,4,19286,534,534,23,2],
["p1",4,"op","chess_char_4_04_a",1,null,1977.6,1,12,9,19051,1067,0,144,4],
["p1",5,"op","chess_char_3_13_b",1,"uniequip_002_malist",2378.75,1,9,17,12596,0,0,67,31],
["p1",6,"op","chess_char_3_14_b",1,"uniequip_002_slbell",2793.95,1,10,8,9525,0,0,50,3],
["p1",7,"op","chess_char_3_17_b",1,"uniequip_002_shotst",3811.23,1,14,20,29353,1201,0,58,9],
["p1",8,"op","chess_char_3_19_b",2,"uniequip_002_vigil",3110.54,1,4,3,6258,0,0,17,4],
["p1",9,"op","chess_char_3_20_b",1,"uniequip_002_kjera",2508.5,1,7,2,1598,0,0,21,2],
["p1",10,"op","chess_char_3_21_b",2,"uniequip_002_archet",2750.7,1,11,66,112976,0,0,207,5],
["p1",11,"op","chess_char_4_02_a",2,null,2470.44,1,164,18,57817,2470,0,38,2],
["p1",12,"op","chess_char_4_06_a",1,null,1479.3,1,13,27,10358,676,0,75,2],
["p1",13,"token","token_10028_vigil_wolf",null,null,828.67,1,136,1,1113,4061,0,21,null],
["p1",12,"op","chess_char_4_06_a",1,null,1338.48,1,13,23,10174,817,0,75,2],
["p1",13,"token","token_10028_vigil_wolf",null,null,1026,1,163,1,974,3863,0,17,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]
],
@@ -569,11 +570,11 @@
"enemy_1046_agent": [1,1,0,0,2700],
"enemy_1148_dssbr_2": [1,1,0,0,3888],
"enemy_1174_duholy": [1,1,0,1276,16200],
"enemy_1195_sfyin": [33,33,0,384,71280],
"enemy_1195_sfyin": [33,33,0,502,71280],
"enemy_1196_msfyin": [66,66,0,0,132],
"enemy_1197_sfshu": [12,12,0,315,32400],
"enemy_1198_msfshu": [36,36,0,0,108],
"enemy_1199_sfjin": [6,6,0,175,42120],
"enemy_1199_sfjin": [6,6,0,0,42120],
"enemy_1200_msfjin": [6,6,0,0,180],
"enemy_1204_msfhu": [1,1,0,0,4],
"enemy_1267_nhpbr": [1,1,0,750,4860],
@@ -582,10 +583,10 @@
"enemy_1513_dekght_2": [1,1,0,6913,43200],
"enemy_2034_sythef": [1,1,0,0,8640]
},
"hooks": [1,188,4746,743,743,1332,1316,115,173,173,64,62,0,21844,123,10,171,0,1,179,0,0,0],
"events": {"atk":918,"bounty":6,"deploy":15,"die":173,"dmg":1315,"fx":178,"skill":126,"spawn":186,"status":252},
"fx": {"camouflage":2,"crit":20,"displace":33,"dp":5,"explode":3,"phase":1,"ripple":10,"shadowWeave":27,"shield":2,"shieldBreak":1,"stealth":4,"summon":51,"telegraph":3,"volley":9,"wolfShadow":3,"wolfShadowLost":3,"zone":1},
"snaps": "666c3952 b094f1ea 38685430 9f090539 ee670c94 1074c7a5 93293922 a5617f28 5b7f9793 095e3e72 c46ed058 dbb905a8 02fb9aea d0e5cb5d a733910b 08ba7414"
"hooks": [1,188,4746,738,738,1313,1297,115,173,173,64,62,0,21844,132,9,171,0,1,179,0,0,0],
"events": {"atk":918,"bounty":6,"deploy":15,"die":173,"dmg":1296,"fx":184,"skill":126,"spawn":186,"status":261},
"fx": {"camouflage":2,"crit":24,"displace":33,"dp":5,"explode":3,"phase":1,"ripple":10,"shadowWeave":27,"shield":2,"shieldBreak":1,"stealth":4,"summon":51,"telegraph":3,"volley":9,"wolfShadow":4,"wolfShadowLost":4,"zone":1},
"snaps": "666c3952 b094f1ea 38685430 9f090539 ee670c94 1074c7a5 93293922 a5617f28 5b7f9793 3c0a7490 14387936 dcb74c1a a58e1beb 82ecbe91 64e48fa5 85eef6b4"
},
"roster-010": {
"about": "pass 0 band 4: chess_char_4_17_a chess_char_4_18_a chess_char_4_11_a chess_char_4_12_a chess_char_4_19_a chess_char_4_07_a chess_char_4_16_a chess_char_4_09_a chess_char_4_10_a chess_char_4_13_a chess_char_4_14_a chess_char_4_20_a token_10041_cathy_catsld token_10041_cathy_catsld",
@@ -1694,8 +1695,8 @@
"enemy_9010_acpupp": [1,0,1,0,1540]
},
"hooks": [1,98,4200,350,350,1399,1458,9,34,91,27,27,0,8299,26,36,73,56,1,38,0,0,0],
"events": {"atk":410,"bounty":3,"deploy":22,"die":35,"dmg":1457,"fx":19,"heal":9,"leak":56,"skill":54,"spawn":90,"status":52},
"fx": {"dp":3,"explode":3,"palsy":1,"revive":4,"shield":1,"sp":2,"strike":1,"summon":2,"telegraph":2},
"events": {"atk":410,"bounty":3,"deploy":22,"die":35,"dmg":1457,"fx":20,"heal":9,"leak":56,"skill":54,"spawn":90,"status":52},
"fx": {"dp":3,"explode":3,"palsy":1,"revive":4,"shield":1,"sp":2,"strike":1,"summon":2,"telegraph":2,"wolfShadowLost":1},
"snaps": "b8ad25ab 03f51213 f84a4bf7 9a29353d 19f6d9a9 5330fd8a f6dda9d3 43873b05 57a77a2a 94494c3a ade46bcb e1b4f57e 436600a0 c60c5a58"
},
"roster-027": {
@@ -1769,39 +1770,39 @@
"bossHpLeft": null,
"rngDraws": 84,
"errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":3,"total":62,"perfect":false,"leaks":59,"leaksUncounted":0,"coins":0,"dmg":157324,"bossDmg":0,"heal":105,"deaths":25,"layerGains":{}}},
"players": {"p1":{"killed":3,"total":62,"perfect":false,"leaks":59,"leaksUncounted":0,"coins":0,"dmg":153754,"bossDmg":0,"heal":105,"deaths":26,"layerGains":{}}},
"units": [
["p1",1,"op","chess_char_3_16_b",0,"none",0,0,4,0,70,4069,0,7,1],
["p1",2,"op","chess_char_4_01_a",0,null,0,0,7,0,0,3614,0,0,0],
["p1",3,"op","chess_char_3_18_b",0,"uniequip_003_vulpis",0,0,10,0,0,2743,105,0,0],
["p1",4,"op","chess_char_4_04_a",0,null,799.54,1,90,0,15633,20901,0,35,5],
["p1",4,"op","chess_char_4_04_a",0,null,0,0,91,0,13678,21701,0,34,4],
["p1",5,"op","chess_char_3_13_b",0,"none",0,0,1,0,15809,1802,0,35,1],
["p1",6,"op","chess_char_3_14_b",0,"none",0,0,83,1,25147,4151,0,29,1],
["p1",7,"op","chess_char_3_17_b",0,"none",0,0,89,1,31808,7884,0,21,7],
["p1",8,"op","chess_char_3_19_b",0,"uniequip_003_vigil",0,0,2,1,24320,2429,0,34,3],
["p1",6,"op","chess_char_3_14_b",0,"none",0,0,88,1,23224,4151,0,27,1],
["p1",7,"op","chess_char_3_17_b",0,"none",0,0,82,1,31808,7884,0,21,7],
["p1",8,"op","chess_char_3_19_b",0,"uniequip_003_vigil",0,0,2,1,24320,2429,0,34,4],
["p1",9,"op","chess_char_3_20_b",0,"none",0,0,9,0,4159,1897,0,11,1],
["p1",10,"op","chess_char_3_21_b",0,"uniequip_003_archet",0,0,60,0,17805,4603,0,22,8],
["p1",11,"op","chess_char_4_02_a",0,null,0,0,65,0,9310,5869,0,7,0],
["p1",12,"op","chess_char_4_06_a",0,null,0,0,74,0,7987,3414,0,17,1],
["p1",13,"token","token_10028_vigil_wolf",null,null,0,0,3,0,5276,3473,0,12,null]
["p1",10,"op","chess_char_3_21_b",0,"uniequip_003_archet",0,0,61,0,17805,4603,0,22,8],
["p1",11,"op","chess_char_4_02_a",0,null,0,0,66,0,9310,5869,0,7,0],
["p1",12,"op","chess_char_4_06_a",0,null,0,0,74,0,8278,3414,0,17,1],
["p1",13,"token","token_10028_vigil_wolf",null,null,0,0,39,0,5293,4499,0,13,null]
],
"enemies": {
"enemy_10044_wintun": [1,0,1,6689,14921],
"enemy_10127_rkmbst_2": [1,0,1,1878,0],
"enemy_1019_jshoot_2": [18,0,18,8026,19151],
"enemy_1092_mdgint": [1,0,1,0,374],
"enemy_1019_jshoot_2": [18,0,18,7757,16859],
"enemy_1092_mdgint": [1,0,1,1026,391],
"enemy_1174_duholy_2": [1,0,1,1445,892],
"enemy_1234_dsubrl": [1,0,1,4496,214],
"enemy_1328_cbjedi": [1,0,1,0,267],
"enemy_1381_winman": [21,3,18,7318,106180],
"enemy_1389_winbab_2": [15,0,12,34758,14615],
"enemy_1381_winman": [21,3,18,7653,104478],
"enemy_1389_winbab_2": [15,0,12,35491,15020],
"enemy_1509_mousek": [1,0,1,1686,0],
"enemy_9006_actoxi": [1,0,1,554,712]
},
"hooks": [1,89,5100,404,404,513,529,30,29,85,28,28,0,4321,18,29,62,56,1,32,0,0,18],
"events": {"atk":431,"deploy":27,"die":29,"dmg":505,"fx":84,"leak":56,"skill":56,"spawn":75,"status":83},
"fx": {"camouflage":1,"crit":6,"dodge":18,"dp":3,"phase":1,"pulse":17,"sentry":8,"shadowWeave":11,"shield":4,"shieldBreak":2,"summon":2,"telegraph":1,"volley":6,"wolfShadowLost":3,"zone":1},
"snaps": "7c092213 c094fdd0 61afa7ba 7ab796f5 181fca6b 71abad47 8bc283c6 0928fca3 2eb15bdf 35788dd1 8be98855 61cc5888 771b79b1 2212bef6 d09f87b0 fc2cc2ef d14c6547"
"hooks": [1,90,5100,401,401,504,520,30,31,87,28,28,0,4426,16,27,62,56,1,34,0,0,18],
"events": {"atk":426,"deploy":28,"die":31,"dmg":496,"fx":88,"leak":56,"skill":56,"spawn":75,"status":79},
"fx": {"camouflage":1,"crit":6,"dodge":18,"dp":4,"phase":1,"pulse":17,"revive":1,"sentry":9,"shadowWeave":9,"shield":4,"shieldBreak":2,"summon":3,"telegraph":1,"volley":6,"wolfShadowLost":5,"zone":1},
"snaps": "7c092213 c094fdd0 61afa7ba 7ab796f5 513e5d4e 4a180eed f5d75fff 20004d5b d4cf3e4e e860333f 2580eac0 7b4a044a 27c913a4 dd5637ac f12e3851 a9e14dc9 76318156"
},
"roster-029": {
"about": "pass 1 band 4: chess_char_4_17_a chess_char_4_18_a chess_char_4_11_a chess_char_4_12_a chess_char_4_19_a chess_char_4_07_a chess_char_4_16_a chess_char_4_09_a chess_char_4_10_a chess_char_4_13_a chess_char_4_14_a chess_char_4_20_a token_10041_cathy_catsld token_10041_cathy_catsld",
@@ -2510,8 +2511,8 @@
"enemy_9016_acstmr": [1,1,0,0,8]
},
"hooks": [1,95,5100,533,533,815,851,518,50,90,17,28,16,7820,316,21,65,40,1,60,0,1,0],
"events": {"atk":606,"bounty":1,"deploy":28,"die":50,"dmg":845,"fx":93,"leak":40,"skill":34,"spawn":80,"status":171},
"fx": {"beam":5,"block":3,"buff":2,"burst":1,"coldWind":1,"displace":15,"dp":1,"iceSpike":1,"liberate":1,"overclock":1,"overload":1,"palsy":3,"revive":3,"rockfall":1,"sentry":6,"shadowWeave":11,"shield":7,"shieldBreak":6,"stone":1,"swordStorm":7,"telegraph":2,"volley":9,"wake":1,"wolfShadow":1,"wolfShadowLost":2,"zone":1},
"events": {"atk":606,"bounty":1,"deploy":28,"die":50,"dmg":845,"fx":94,"leak":40,"skill":34,"spawn":80,"status":171},
"fx": {"beam":5,"block":3,"buff":2,"burst":1,"coldWind":1,"displace":15,"dp":1,"iceSpike":1,"liberate":1,"overclock":1,"overload":1,"palsy":3,"revive":3,"rockfall":1,"sentry":6,"shadowWeave":11,"shield":7,"shieldBreak":6,"stone":1,"swordStorm":7,"telegraph":2,"volley":9,"wake":1,"wolfShadow":1,"wolfShadowLost":3,"zone":1},
"snaps": "de3ca31c 182b136c 5c27b035 f8f15ac1 250e93d3 6fde9171 1273cfdd f1c0ebf4 8e50442c 8277b17d 3a6591ca 5a144122 2014c207 a4445fd0 3d27fbdc 8d62308c d9b05d02"
},
"roster-041": {