From 07ac357ed4911d14b2a635ecda7a8549b8e856c7 Mon Sep 17 00:00:00 2001 From: robll-v1 <3922295484@qq.com> Date: Sun, 11 Oct 2026 16:16:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A1=B6=E6=A0=8F=E7=94=9F=E5=91=BD?= =?UTF-8?q?=E5=80=BC=E5=9C=A8=E7=BB=93=E7=AE=97=E5=89=8D=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E6=95=B0=E5=80=BC=EF=BC=8C=E4=BB=85=E4=BB=A5?= =?UTF-8?q?=20=E2=88=92N=20=E6=A0=87=E8=AE=B0=E5=BE=85=E6=89=A3=EF=BC=88Gi?= =?UTF-8?q?tHub=20#479=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/PLAYING.md | 2 +- docs/history/v2.2-playtest3.md | 2 +- public/js/ui/gameComponents.js | 7 ++++--- public/js/ui/hud.js | 14 ++++++++------ test/docs-consistency.test.js | 4 ++-- test/ui/playtest3.test.js | 12 ++++++------ 7 files changed, 23 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 24cfcdfc..20a86f40 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ English summary: [below](#english). - **最终攻势与隐秘核心**:两人共享一个战场,全队共同削减同一条领袖血条;10 个敌方领袖,巨型领袖约 5×3 格的受击范围,以及官方的限伤规则。 - **结算称号**:卫戍之星、不朽盟约、坚若磐石等 6 个称号。 - **断线重连**:同盟模拟断线后 10 分钟内重新打开页面即可回到原座位,掉线期间按原阵容自动作战,也可以「暂离」交给 AI 托管;独立模拟 24 小时内可以回来继续(同一个浏览器)。 -- **交互细节**:漏怪时顶栏的目标生命值实时减少(结算时确定);点选、拖放和配发装备都按地上的方格;购买、升级和机变选卡都需要点两次确认;只有一名玩家时除作战外不计时。 +- **交互细节**:漏怪时顶栏用红字 + 「−N」标记待扣的目标生命值(结算时才扣除);点选、拖放和配发装备都按地上的方格;购买、升级和机变选卡都需要点两次确认;只有一名玩家时除作战外不计时。 - **画面与声音**:真实 Spine 小人、官方 BGM 与音效、表情(6 套 × 6 个)、作战特效;可选的官方 3D 棋盘(需要从本机客户端提取贴图)。 - **手机与电脑**:触摸拖拽、长按查看详情,推荐横屏;设置里可以调低画质。 diff --git a/docs/PLAYING.md b/docs/PLAYING.md index c779af26..3be5fe06 100644 --- a/docs/PLAYING.md +++ b/docs/PLAYING.md @@ -24,7 +24,7 @@ | 休整期 | 购买、摆阵、配装备、冻结 / 刷新、升级调度中心;同盟有倒计时(65–215 秒,随回合增加);独立模拟、以及只有你一名玩家(只带 AI 队友)的同盟模拟不限时。所有人「准备就绪」即提前开战,全员就绪前可取消;点「准备就绪」时商店自动收起、棋盘放大到全场,取消准备时商店再展开(资金旁的「收起」和商店标签「展开商店」随时可以手动切换)。 | - **特殊地形点一下就有说明**:战场上的**活性源石**、**沼泽**、**排气格栅**、**深水区**,以及**红门 / 蓝门 / 传送点**,点一下就会弹出它自己的说明卡——名称、机制,以及**这一关自己的数值**(比如战场#04 的活性源石:每秒 70 点真实伤害、攻击力 +20%、攻击速度 +20、持续 300 秒)。休整期和作战期都能点。普通地板 / 道路 / 高台点了不会弹东西,所以点一下仍然只是「取消选中、关掉卡片」。 - **场地装置点一下也有说明**:**阻隔工事**(挡路:地面敌人绕着走,无路可走才撞碎它;作战中说明卡上有它的实时血量,被摧毁后点不出来)、**“双眼皮”炮台**(自动法术攻击、命中附加脆弱,按当前层数最高的盟约强化;还没被机械援助或机变卡启用的炮台不显示、点不出来)、**射击台**(远程位可部署的高台位)和**源石流发生装置**(吹风方向,以及对干员攻击力、敌人移动速度的加减)点一下同样弹出说明卡,数值都取自所在关卡的装置数据,装置名称按当前语言显示官方译名;被机变卡移除的箱子同样不显示。休整期和作战期都能点(GitHub #228,PR #229,感谢 @rickylxw)。 -| 作战 | 每人在自己的战场各自作战(战斗在你的浏览器里模拟,2 倍速)。干员按**逐列从左到右、每列从上到下**的顺序(官方「从上到下>从左到右」:最左一列最先)自动部署并消耗部署费用(初始 10,+1/秒)。时限 45–115 秒(实际秒数,即屏幕上的倒计时),时间到仍存活的敌人算漏怪。顶栏中间的胶囊是官方的「已解决 / 本回合敌人数」:打死一个、漏掉一个都算已解决(开局 0/3,漏一个 1/3,打死一个 2/3,打死会分裂的那只 3/3);分裂出来的小怪、召唤物和领袖召唤的敌人不进分子也不进分母,但它们漏掉时仍然各自扣目标生命值、也会破坏完美作战(4 只磨砻全被打死、分裂出的 8 只全漏进蓝门:胶囊 4/4,扣 8 点),所以会召唤的领袖关胶囊也能填满。有敌人进入蓝门时,顶栏的目标生命值会**立即**显示扣除后的数值(红字 + 「−N」,每回合至多 10 点),结算时确定;联防阶段标注「联防中」,队友拦下的部分最后不扣。左侧队伍栏同样显示队友的待扣生命值。被击倒的干员**留在倒下的格子上**(倒地姿势、略微变灰),之后也在这一格再部署(与官方一样;倒在其他干员或召唤物初始位置上的,会回到自己的初始位置躺下,召唤物已经消失也一样;唯一的例外是乌尔比安:三技能把他移到船锚处期间被击倒时,他回到自己的部署位躺下、再部署——这是项目维护者 2026-10-07 应社区反馈有意做出的改动,与官方「原地倒下」不同);因自身效果强制退场的干员(史尔特尔「余烬」结束、耀骑士临光 2 技能结束等)同样倒在原地、在原地再部署;倒地干员所在的格子**不会被占用**,突袭盟约的再部署、援军、召唤物都会避开它。头上的圆圈是再部署倒计时:绿色圆环 + 秒数;变成黄色「DP」表示部署费用不够。血条下方的一行是**元素损伤**(与原版一致:元素图标 + 白条):干员为带图标的彩色圆盘,敌人为小号纯色圆盘,右侧白条是剩余元素值,像血条一样从右往左减少,白条耗尽的那一下就是爆条(被大额元素损伤一次打掉一截时白条也会一次少一截,例如掠海漂移体每击约为 25%–50%);爆条后进入爆发冷却(灼燃 / 神经 / 侵蚀 10 秒,敌方侵蚀 8 秒,凋亡 15 秒),这时白条变成**元素颜色**(神经青绿、侵蚀蓝、灼燃橙红、凋亡灰紫)并在冷却期间慢慢回满、图标闪烁——彩色条表示已经爆条、正在承受爆发效果,不是还剩的元素值;期间所有元素都不会累积也无法被治疗,结束后清零。元素爆发造成的是**无来源伤害**,不吃干员的增伤、穿透和装备加成(例如深巡打海嗣时灼燃爆发固定 7000),击杀仍记在爆条的干员名下;元素脆弱只提高元素伤害,不加快元素条;脆弱不影响元素伤害。已被击杀的敌人不会再被附加元素损伤。 | +| 作战 | 每人在自己的战场各自作战(战斗在你的浏览器里模拟,2 倍速)。干员按**逐列从左到右、每列从上到下**的顺序(官方「从上到下>从左到右」:最左一列最先)自动部署并消耗部署费用(初始 10,+1/秒)。时限 45–115 秒(实际秒数,即屏幕上的倒计时),时间到仍存活的敌人算漏怪。顶栏中间的胶囊是官方的「已解决 / 本回合敌人数」:打死一个、漏掉一个都算已解决(开局 0/3,漏一个 1/3,打死一个 2/3,打死会分裂的那只 3/3);分裂出来的小怪、召唤物和领袖召唤的敌人不进分子也不进分母,但它们漏掉时仍然各自扣目标生命值、也会破坏完美作战(4 只磨砻全被打死、分裂出的 8 只全漏进蓝门:胶囊 4/4,扣 8 点),所以会召唤的领袖关胶囊也能填满。有敌人进入蓝门时,顶栏的目标生命值**先不变**(红字 + 「−N」标记待扣的点数,每回合至多 10 点),结算时才显示扣除后的数值(与官方一致,GitHub #479;0.2.4 及之前是立即显示扣除后的数值);联防阶段标注「联防中」,队友拦下的部分最后不扣。左侧队伍栏同样显示队友的待扣生命值。被击倒的干员**留在倒下的格子上**(倒地姿势、略微变灰),之后也在这一格再部署(与官方一样;倒在其他干员或召唤物初始位置上的,会回到自己的初始位置躺下,召唤物已经消失也一样;唯一的例外是乌尔比安:三技能把他移到船锚处期间被击倒时,他回到自己的部署位躺下、再部署——这是项目维护者 2026-10-07 应社区反馈有意做出的改动,与官方「原地倒下」不同);因自身效果强制退场的干员(史尔特尔「余烬」结束、耀骑士临光 2 技能结束等)同样倒在原地、在原地再部署;倒地干员所在的格子**不会被占用**,突袭盟约的再部署、援军、召唤物都会避开它。头上的圆圈是再部署倒计时:绿色圆环 + 秒数;变成黄色「DP」表示部署费用不够。血条下方的一行是**元素损伤**(与原版一致:元素图标 + 白条):干员为带图标的彩色圆盘,敌人为小号纯色圆盘,右侧白条是剩余元素值,像血条一样从右往左减少,白条耗尽的那一下就是爆条(被大额元素损伤一次打掉一截时白条也会一次少一截,例如掠海漂移体每击约为 25%–50%);爆条后进入爆发冷却(灼燃 / 神经 / 侵蚀 10 秒,敌方侵蚀 8 秒,凋亡 15 秒),这时白条变成**元素颜色**(神经青绿、侵蚀蓝、灼燃橙红、凋亡灰紫)并在冷却期间慢慢回满、图标闪烁——彩色条表示已经爆条、正在承受爆发效果,不是还剩的元素值;期间所有元素都不会累积也无法被治疗,结束后清零。元素爆发造成的是**无来源伤害**,不吃干员的增伤、穿透和装备加成(例如深巡打海嗣时灼燃爆发固定 7000),击杀仍记在爆条的干员名下;元素脆弱只提高元素伤害,不加快元素条;脆弱不影响元素伤害。已被击杀的敌人不会再被附加元素损伤。 | | 联防 | 见第 5 节。 | | 结算 | 扣 LP、发放悬赏资金、结算层数;LP ≤ 0 的玩家淘汰(其干员回到公共卡池)。结算开始时屏幕正中跳出官方的「**作战结束**」框(约 3 秒后自己消失,不挡操作),下面一行只写**你自己**这一回合被扣的目标生命值:没扣是「**全员无伤!**」,扣了是「**生命值减少 −N**」;同时播官方的战斗结束音效(扣了、联防里没扣、普通回合没扣各一种)。联防回合见第 5 节。最终攻势和隐秘核心没有这一框(之后直接是结算称号或隐秘核心);已淘汰的玩家和观战者不弹框。 | diff --git a/docs/history/v2.2-playtest3.md b/docs/history/v2.2-playtest3.md index da3a2057..b8b22941 100644 --- a/docs/history/v2.2-playtest3.md +++ b/docs/history/v2.2-playtest3.md @@ -30,7 +30,7 @@ v2.2 answered "a click selected the unit one row lower" with body picking: every ### 17.5 Game HUD (#2, #3, #8, #9) — `ui/hud.js`, `teamPanel.js`, `underframe.js`, `detailPanel.js`, `data.js` -- **Live LP (#2)**: in a normal round the top bar's LP tower and the own team row show `lp − min(lpCapPerRound, counted leaks)` as soon as the own battle's enemies enter the blue gate — the number in red followed by a −N plate that pops again on every leak, clamped at 0, with a tooltip (`ui/hud.js liveLp` / `pendingLoss` / `pendingTip`). The count is `ownLeaks(local, server)` = the larger of the local runner's `state().leaks[own field]` (§14) and `m.public players[].pendingLp` (§8.2; the authority's `b.progress` leaks, the field's result, the recorded result in 联防) — both only grow within a round, so a paused display replica never freezes the number. The pending part is dropped as soon as the settled `m.private` lands (its `lp` or `stats.leaks` changed; `Match.flush` sends it before the SETTLE `m.public`), the phase leaves COMBAT / 联防 or the round changes — never subtracted twice. 联防 (**revised by §20.6**, user playtest #6 item 7): a leaker's count is its enemies still standing — the local replica's while the 联防 field is on screen, else m.public `uniteLeft` — shown uncapped as ×N in the capsule and in the leakers' team rows, and the LP tower shows lp − min(10, N), tagged 联防中. Teammates' rows use their `pendingLp` (`teamPanel.js rowLp`); server-run fields (bots, taken-over fields) report none before their result is released. Boss rounds are unchanged (the team LP moves live through `b.pool`). +- **Live LP (#2)**: in a normal round the top bar's LP tower and the own team row mark `min(lpCapPerRound, counted leaks)` as soon as the own battle's enemies enter the blue gate — the settled LP in red followed by a −N plate that pops again on every leak, clamped at 0, with a tooltip (`ui/hud.js liveLp` / `pendingLoss` / `pendingTip`; **revised by GitHub #479**: the number keeps the settled LP until settlement, as in the official game — until 0.2.4 it showed `lp − min(...)` in place). The count is `ownLeaks(local, server)` = the larger of the local runner's `state().leaks[own field]` (§14) and `m.public players[].pendingLp` (§8.2; the authority's `b.progress` leaks, the field's result, the recorded result in 联防) — both only grow within a round, so a paused display replica never freezes the number. The pending part is dropped as soon as the settled `m.private` lands (its `lp` or `stats.leaks` changed; `Match.flush` sends it before the SETTLE `m.public`), the phase leaves COMBAT / 联防 or the round changes — never subtracted twice. 联防 (**revised by §20.6**, user playtest #6 item 7): a leaker's count is its enemies still standing — the local replica's while the 联防 field is on screen, else m.public `uniteLeft` — shown uncapped as ×N in the capsule and in the leakers' team rows, and the LP tower marks it as −min(10, N), tagged 联防中. Teammates' rows use their `pendingLp` (`teamPanel.js rowLp`); server-run fields (bots, taken-over fields) report none before their result is released. Boss rounds are unchanged (the team LP moves live through `b.pool`). - **Temp row (#3, UI)**: while `m.private temp` holds pieces during the own prep, a dashed red frame surrounds the 临时整备区 row on the board and a label says how many are waiting, what to do and when they go (`underframe.js TempRowNotice` / `tempRowFrame`, placed through `view.tileScreen` — it follows the camera, the mirrored Final Assault prep and the DOM fallback; no pointer events; the label hides while dragging or placing). Its wording follows the server rule (§6.2, `tempRowRule(ready)`): not ready — "放入整备区或战场、配发或使用后才能准备;休整期结束时仍在此处的将被销毁"; ready in PREP (so whatever lies there arrived after Ready) — "已准备就绪后进入的单位保留到下个休整期,届时仍在此处的将被销毁(取消准备则在本休整期结束时销毁)". The 准备就绪 button shows "临时整备区 N 个单位待处理" under it (not only on hover; the effects column moves down a line meanwhile, `.gm.has-readywhy`) and Space toasts the reason (`hud.js TEMP_RULE` / `tempReadyReason`). - **Detail card order (#8)** (`detailPanel.js CHESS_SECTIONS`): header (portrait, name, tier, class, bonds) → **特质** (the operator's own effect — garrison trigger chip such as 休整期结束时 + description, a compact gold block, visible without scrolling at 1920×1080, 1280×720, iPhone 14 and 640×360) → 特性 (class trait) → stats (a compact 4×2 grid) + attack range → 技能 → 模组 → 装备 → 天赋. On phones ≤ 460 px tall the portrait is shorter and the romanised name is hidden. A teammate's 策略 is not on the card — while scouting, the 本局信息 drawer shows the watched player's band in place of one's own (§14). - **Busy indicators and texts (#9)**: every text of the game (operators, skills, bonds, items, enemies, 特质 …) is static data, `/data/*.json` loaded once per page and never fetched during a match; the "spinners" the player saw were busy indicators of requests in flight next to texts — and the 特质 chip's type icon: it was picked by the trigger, so every <休整期开始时 / 结束时> 特质 showed the spoked 特异化 glyph (it reads like a loading spinner) before its text; the chip now shows the garrison's own official `eventTypeIcon` (`detailPanel.js garrisonTypeIconKey`: crossed swords 作战能力, gold 整备能力, chain link 持续 / 单次叠加, spokes only for 特异化). Now: no `cursor: wait / progress` anywhere; a loading `Button` keeps its icon and shows a thin sweeping bar along its bottom edge after 250 ms (no spinner ring); a 机变 card in flight shows a 选择中 strip until the pick appears in `m.public` (`choiceOverlay.js pickBusy`); the 作战结束,等待队友完成作战 hourglass is still; the hexagon `Spinner` is kept for genuine loading screens only (entering the match, the field view, connecting, guide images, the first 干员调配 load, which says the data loads once per page). `main.js warmGameData` downloads every in-match file (`gameComponents GAME_FILES`) in the background once the player is in a room, and the match screen waits for them; `data.js` retries a transient failure (network error, HTTP 5xx / 408 / 429) after 600 ms and 2 s (`RETRY_DELAYS_MS`) — a 404 or invalid JSON is final. diff --git a/public/js/ui/gameComponents.js b/public/js/ui/gameComponents.js index b531e986..df8650f8 100644 --- a/public/js/ui/gameComponents.js +++ b/public/js/ui/gameComponents.js @@ -201,8 +201,9 @@ export function GIcon({ name, class: cls, title }) { /** * Green LP tower + number. `pending` (> 0): the loss the running battle's leaks will cost at settlement (user - * playtest #3 item 2) — the number shows value − pending in red followed by a −N tick that pops again on every change - * (keyed); `note` a small tag after it (联防中); `tip` the title text. + * playtest #3 item 2) — the number keeps the settled LP (GitHub #479: the official shows the current value until + * settlement, not the deduction), shown in red with a −N tick that pops again on every change (keyed); `note` a + * small tag after it (联防中); `tip` the title text. * @param {{ value: any, size?: 'sm'|'md'|'lg', class?: string, tone?: string|null, pending?: number, note?: string|null, tip?: string|null }} props */ export function LpTower({ value, size = 'md', class: cls, tone, pending = 0, note = null, tip = null }) { @@ -211,7 +212,7 @@ export function LpTower({ value, size = 'md', class: cls, tone, pending = 0, not return html` 0 && 'is-pending', cls)} title=${tip || tr('目标生命值')} data-pending=${p > 0 ? p : null}> <${Sprite} k="hudPanel/icon_hp" class="lp__icon" fallback=${html`<${Icon} name="rook" class="lp__icon" />`} /> - ${ok ? Math.max(0, value - p) : '--'} + ${ok ? Math.max(0, value) : '--'} ${p > 0 ? html`−${p}` : null} ${note ? html`${note}` : null} `; diff --git a/public/js/ui/hud.js b/public/js/ui/hud.js index f72e72ea..bfd6fe16 100644 --- a/public/js/ui/hud.js +++ b/public/js/ui/hud.js @@ -12,13 +12,14 @@ // overtimeState): "NN 秒后全队生命值开始流失" once the level time ran out, then a live "生命值 −1/秒" indicator while the // merged team LP drains (the LP tower turns red). Solo battles: a pause / resume button beside the countdown (g.pause); // while m.public.paused every clock here is frozen at the pause moment (`frozenAt`). -// Normal rounds (user playtest #3 item 2): the LP tower drops live as the own battle's enemies enter the blue gate — -// lp − min(lpCapPerRound, counted leaks) in red with a −N tick, 联防中 while a 联防 may still save part of it (liveLp; +// Normal rounds (user playtest #3 item 2): the LP tower marks the own battle's leaks live as its enemies enter the blue +// gate — the settled lp stays until settlement, in red with a −min(lpCapPerRound, counted leaks) tick (GitHub #479: the +// official keeps the current value until settlement), 联防中 while a 联防 may still save part of it (liveLp; // the leaks come from the local battle runner, else m.public players[].pendingLp). 联防 (user playtest #6 item 7; PRTS // 卫戍协议/帮助 "防卫失败的玩家可通过上方信息栏确认自身所属敌人的剩余数量"): a leaker's phase capsule carries the official // runner tag ×N (research 09 `tag_miss`; art ui/battle bg_miss_enemy [ASSUMED]) = its enemies still standing on the 联防 // field, uncapped and live — falling as the helpers kill them, rising when one splits or summons (the local 联防 -// replica's runner state().uniteLeft, else m.public players[].uniteLeft); the LP tower shows lp − min(lpCapPerRound, N). +// replica's runner state().uniteLeft, else m.public players[].uniteLeft); the LP tower marks lp with −min(lpCapPerRound, N). // The same MissTag sits in the leakers' team rows (ui/teamPanel.js), its tooltip naming the teammate there (missTip). // 准备就绪 is refused while the temp overflow row (临时整备区) holds pieces: the reason shows under the button // (user playtest #3 item 3; the row's own label is ui/underframe.js TempRowNotice). @@ -183,9 +184,10 @@ export function uniteRemaining(local, server) { } /** - * The own LP while a normal round's battle runs: the loss its counted leaks will cost is shown at once (red, −N) - * instead of only at settlement. `base` — kept by the caller between renders — is the settled m.private state the - * pending loss applies to: { round, lp, statsLeaks } as first seen in the round's COMBAT / 联防. The pending part is + * The own LP while a normal round's battle runs: the loss its counted leaks will cost is marked at once (red, −N) instead + * of only at settlement — the number itself keeps the settled LP until settlement (GitHub #479: the official shows the + * current value, not the deduction, until the round settles). `base` — kept by the caller between renders — is the + * settled m.private state the pending loss applies to: { round, lp, statsLeaks } as first seen in the round's COMBAT / 联防. The pending part is * dropped as soon as the settlement lands — m.private lp or stats.leaks changed (Match.flush sends m.private before the * SETTLE m.public) — or the phase leaves COMBAT / UNITE or the round changes, so the loss is never subtracted twice. * 联防 (UNITE): a leaker finally loses min(cap, the survivors of the 联防 battle that came from them); `uniteLeft` (its diff --git a/test/docs-consistency.test.js b/test/docs-consistency.test.js index 21a4a5ac..3b245fab 100644 --- a/test/docs-consistency.test.js +++ b/test/docs-consistency.test.js @@ -290,8 +290,8 @@ test('lost models, live LP, detail card order, static game data (user playtest # assert.match(DESIGN, /official `eventTypeIcon` \(`detailPanel\.js garrisonTypeIconKey`/); assert.match(DESIGN, /pendingLp\? \/\* COMBAT \/ 联防 of a normal round/); assert.match(DESIGN, /`ownLeaks\(local, server\)`/); - assert.match(PLAYING, /顶栏的目标生命值会\*\*立即\*\*显示扣除后的数值/); - assert.match(README, /漏怪时顶栏的目标生命值实时减少/); + assert.match(PLAYING, /顶栏的目标生命值\*\*先不变\*\*.*结算时才显示扣除后的数值.*GitHub #479/); + assert.match(README, /漏怪时顶栏用红字 \+ 「−N」标记待扣的目标生命值/); }); test('user playtest #4 (DESIGN §18): picking by tile, timers, 机变 two taps, down / element state, content — code and every doc agree', () => { diff --git a/test/ui/playtest3.test.js b/test/ui/playtest3.test.js index c94e060b..7b465a4c 100644 --- a/test/ui/playtest3.test.js +++ b/test/ui/playtest3.test.js @@ -1,5 +1,5 @@ // Regression tests (Node) of user playtest #3, HUD / UI items: -// 2 the own LP drops live while the own battle's enemies enter the blue gate — pendingLoss / liveLp (the settle +// 2 the own LP's pending loss is marked live while the own battle's enemies enter the blue gate — pendingLoss / liveLp (the settle // rule min(lpCapPerRound, counted leaks), reset when the settled m.private lands, 联防中 during 联防), the team // panel rows (rowLp: own live value, teammates' m.public players[].pendingLp), the LP tower's −N tick, and the // server's pendingLp (server/match/match/views.js _pendingLpView) @@ -59,7 +59,7 @@ const hasClass = (v, c) => typeof v?.props?.class === 'string' && v.props.class. // ---- 2: live LP ---------------------------------------------------------------------------------------------------- -describe('2: the own LP drops live while the battle runs', () => { +describe("2: the own LP's pending loss is marked live while the battle runs", () => { test('pendingLoss is the settle rule: min(lpCapPerRound, counted leaks), never negative', () => { assert.equal(pendingLoss(0), 0); assert.equal(pendingLoss(3), 3); @@ -150,7 +150,7 @@ describe('2: the own LP drops live while the battle runs', () => { assert.deepEqual(rowLp({ lp: 30 }, { phase: PHASE.FINAL_ASSAULT }, { lp: 99, pending: 5 }), { lp: 30, pending: 0, unite: false, left: null }, 'boss rounds: the row keeps its own share'); }); - test('LP tower: lp − pending in red with a −N tick keyed by the pending value (it pops again on every leak), the 联防中 tag', () => { + test('LP tower: the settled LP in red with a −N tick keyed by the pending value (it pops again on every leak) — the official keeps the current value until settlement (GitHub #479), the 联防中 tag', () => { const plain = LpTower({ value: 24, size: 'lg' }); assert.ok(!hasClass(plain, 'is-pending')); assert.equal(textOf(plain), '24'); @@ -160,11 +160,11 @@ describe('2: the own LP drops live while the battle runs', () => { assert.equal(pend.key, 3, 'keyed: a new count re-mounts the tick (its pop animation plays again)'); assert.equal(textOf(pend), '−3'); const val = [...walk(v)].find((n) => hasClass(n, 'lp__val')); - assert.equal(textOf(val), '21'); + assert.equal(textOf(val), '24', 'the number keeps the settled LP; the −3 tick carries the pending loss (GitHub #479)'); assert.equal(v.props.title, 'x'); const unite = LpTower({ value: 5, pending: 9, note: '联防中' }); - assert.equal(textOf([...walk(unite)].find((n) => hasClass(n, 'lp__val'))), '0', 'clamped'); - assert.equal(textOf([...walk(unite)].find((n) => hasClass(n, 'lp__pend'))), '−5'); + assert.equal(textOf([...walk(unite)].find((n) => hasClass(n, 'lp__val'))), '5'); + assert.equal(textOf([...walk(unite)].find((n) => hasClass(n, 'lp__pend'))), '−5', 'the tick clamps at the LP left'); assert.equal(textOf([...walk(unite)].find((n) => hasClass(n, 'lp__note'))), '联防中'); assert.equal(textOf(LpTower({ value: null, pending: 3 })), '--'); });