Commit Graph
11 Commits
Author SHA1 Message Date
xcdoge b4bc89ad4e feedback7 UIBACK: the top-bar capsule counts like the official — resolved = killed + leaked among the enemies the round scheduled (opening 0/3, a leak 1/3, a kill 2/3, the splitter 3/3); a split child, a summon and a boss summon are in neither number but still cost LP and break 完美作战 (PR #308 by @xcdoge, the capsule half of PR #157 — authored by them: the sim / protocol / HUD hunks applied as reviewed): inTotal / killedInTotal / leakedInTotal / resolved, b.snap / BattleResult / b.progress (optional field) / m.public progress carry resolved, killed may exceed total (validation bounds it by the content-spawn allowance); dropped the PR's stray design-doc line — it had replaced the S→C b.end bullet of docs/design/network.md, which is back; the 联防 live-counter change mark follows the enemy list now that a spawn no longer moves total (uniteLeftMark + test); golden: only total / players.*.total / snaps move, outcomes bit-identical — roster 23 (roster-002 003 007 008 009 010 011 012 013 015 017 018 020 023 024 025 026 027 037 045 047 048 049), bonds 2 (bond-maniShip-low, bond-soloShip-high), fields 11 (boss-boss_1 3 5 pair+solo, boss-boss_6-solo, hidden-boss_8 10 pair+solo), diy 11 (diy-036 040 056 062 068 073 083 091 095 114 115), matches 12 (solo-FUNNY-1 NORMAL-1 HARD-1 ABYSS-1 ABYSS-2, coop2-NORMAL-3 coop2-HARD-4 coop3-ABYSS-5 coop4-ABYSS-7 coop2-NORMAL-8-serverrun solo-HARD-9-boosted coop2-NORMAL-70-diy: the total column of 36 round rows), standins unchanged 2026-10-08 19:30:36 +08:00
sganggs d7c665dbf6 feedback7 UIBACK: the match's top bar lists the spectator seats — a 观战席 capsule (eye + count) beside the latency opens the roster, the host removes a spectator with its ✕ while the match runs (PR #120 by @salt-fishes, the part the owner accepted: the cap stays 2, no per-room spectator option); the server already took room.removeSpectator at any time — test/match/lobby-integration.test.js now covers the host removing one mid-match (room.closed kicked, the other spectator and the host told, no frame after it, the seat refilled), test/ui/spectator-roster.e2e.test.js the capsule; one new UI string in the four packs; the dev mock seats spectators on ?variant=spectators; golden: unchanged (client only) 2026-10-08 19:19:09 +08:00
sganggs d84b961c80 feedback6 R21G: a b.start is registered as pending before the sim loads, so a b.end (takeover / forced, the first end counts), a second b.start of the same battle, b.pool and the solo pause that arrive during the load or the silent catch-up are applied to it in order once the Battle exists (they were dropped); a superseded authority is still built, the next prep's clear stops the preparation (PR #266 by @siruimei07, re-implemented on the current runner); docs/design/network.md §14 Client runner + b.end; golden: unchanged 2026-10-07 17:51:26 +08:00
sganggs f661a47773 feedback5 R19C: the official 「作战结束」 result box and the three battle-over sounds at settlement (GitHub #235; PR #112 by @Convey123, approved and never merged, ported by hand onto the 0.2.0 modules) — the box says only the viewer's own LP, 「全员无伤!」 or 「生命值减少 −N」; after a 联防 the number is the server's own per-player charge (m.public.uniteResult in the SETTLE view) and 「全员无伤!」 only when nothing got through; no box for spectator seats, eliminated players or the boss rounds; golden: unchanged 2026-10-07 11:21:24 +08:00
sganggs 47079456be feedback5: merge CB2 — community reports (healers / summons): 刺玫's S2 heals the most injured ally again (the counter's heal alone goes to her protégé), 琳琅诗怀雅 heals herself and her 破财消灾 cost restarts at 5 DP with every deployment, 赫默's S2 casts at full SP so the drone takes the field at once, the countdown summons (医疗探机, 诅咒娃娃, 海嗣 and four 自选 ones) take no damage, are never healed and show the life left on their bar, stacked ground areas stay readable; §25.16 after CB5's §25.19 (renumbered with the later merges); golden: vs feedback5 CB2's own rules — roster-001/002/003/005/008/016/018/027/035/037/045/046/049, bond-deputShip-low/high, bond-suntShip-high, bond-victoriaShip-low/high, bond-visiShip-low/high, boss-boss_1/3/5/7-pair, hidden-boss_9-pair, unite-1, matches solo-FUNNY-2, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-ABYSS-10-boosted, coop2-NORMAL-14-standins; vs CB2 exactly CB5's 18 bot matches
Merge fixes (the three tests the trial merge failed):
- test/content/summon_loadout_conflicts.test.js (赫默's drone lifetime per module variant): it asserted "no cast yet"
  after the drone's redeploy time and then cast S2 by hand — with nobody hurt the old DEFAULT rule never cast. S2 now
  casts by itself the tick its SP is full (22 s), which is the official rule (client skill skchr_silent_2: no
  `_trigger`, `_allowNoTarget` 1, `_checkHasTargetBeforeDoCast` 0; CB2 64f8bf9), so the test waits for that cast and
  checks both drones' lifetimes from it. Failed on fb5-cb2 itself (the stream did not run the file).
- test/sim/feedback1f-downed-tile.test.js (F5 rule 3): its "killed" case killed 浊心斯卡蒂's 海嗣 with 1e9 true damage;
  the 海嗣 is a countdown summon now — 无敌 + 禁疗, the official state (PRTS 海嗣 备注 「持有禁疗、无敌」) — so it only
  expires. The killed case takes 缪尔赛思's 流形 (a placed board summon that is attacked and killed and also comes back on
  its tile 25 s later); both cases now also check the summon's return. Failed on fb5-cb2 itself.
- test/golden-standins.test.js (the 补位 match fields at least 3 different stand-ins): already failing on feedback5
  37558f0 — CB5's 调度中心 upgrade card changed the human seat's shop in coop2-NORMAL-14-standins, which then fielded
  only 2 of its 7 not-owned chess (忍冬 → Sharp, 安洁莉娜 → Raidian). tools/golden.mjs MATCH_NOT_OWNED adds 缄默德克萨斯
  and 铃兰 (chess its AI fields now): 4 stand-ins (Sharp from round 4, Misery from 7, 预备干员-辅助 from 9, Raidian in 12);
  the test is unchanged, test/golden/README.md says 9 chess. This moves coop2-NORMAL-14-standins only.
- docs/history/0.2.0.md: the §25 intro's two stream lines joined into one sentence (the merge had cut it).

Golden moves outside CB2's commit list are CB2's rules on the paths CB5 gave the bots: coop2-HARD-4 fields 琳琅诗怀雅
from round 6, coop2-NORMAL-8-serverrun 刺玫 from round 7, coop2-ABYSS-10-boosted 刺玫 from round 2,
coop2-NORMAL-14-standins 刺玫 from round 3 and 琳琅诗怀雅 in rounds 6-7 (instrumented); coop2-NORMAL-3 and coop4-FUNNY-6
(in CB2's list) still field 刺玫 and her S2 heals now reach other allies (36 / 205 heals), but no kill, leak, LP or
end stat changes, so their digests stay.

Gate: non-browser suites 4949 / 4949, npm run golden ok (283 scenarios), test/golden.test.js 9 / 9, lint 0 errors,
check:imports ok, typecheck ok.
2026-10-07 03:10:22 +08:00
sganggs 97105cbfad feedback5 CB2: DESIGN §25.16 — the community reports of 2026-10-06 of stream CB2 (healers, summons, timers): 12, 18, 33, 34, 26, 14 fixed; 11, 13, 31, 32 answered with the evidence; design/network.md notes the countdown summons' snapshot hp 2026-10-07 02:36:06 +08:00
sganggs ef10164773 feedback5 CB5: the boss round's prep shows both players of a pair together — the partner's pieces on the other half, as the battle places them — and frames the pair's two avatars in the official green (community report of 2026-10-06, item 51)
The own half was already the official one (the right-hand player on the right half with the
right_boss_*_camera_param camera, its layout mirrored col c -> 20 - c with RIGHT <-> LEFT, so its operators
face the leader as before: research 09 §1.2, DESIGN §15); the partner's half stood empty and nothing framed
the pair.
Server: in a boss round's prep m.private.bossMate { playerId, side, units } = the partner's board as UnitInfo
on its half (Match.bossMateView, finalAssault.js bossFieldPlacement); markPrivate marks the partner's
m.private with the mover's, so it follows live; a scout of either player (Match.prepFieldMeta) carries both
halves (`mate`), and its signature covers the partner's board.
Client: render/app.js syncMates draws the partner's units read-only ('m:<uid>' views: never picked, dragged
or swept with the own pieces) and the whole boss field is lit; the team panel frames the pair's avatars
(gameLogic teamFrameIds: the players still in by seat, two by two, as prepCamera — the boss field in the
fight) from the round start through the Final Assault / Hidden Core, with the official ui/battle
bg_team_border tinted the autochess green (#4ed8af; a plain ring without the local art); none for a lone
player, an eliminated player, a spectator seat, or 联防.
Overlap with PR #191 (same-field frames, issue #190; not merged): it published m.public bossPairing and
framed the partner alone in gold, 联防 included, plus a banner naming the partner; here the pairing is read
as the prep camera already does, both avatars get the official green frame, boss rounds only.
[ASSUMED] the partner's bench is not shown; one frame per avatar.

Golden unchanged. Tests: test/match/feedback5-boss-pair-prep.test.js (new, 4), the item 55 test now checks
the scouted player's own pieces (the partner's stand on the other half); SP_E2E
test/ui/feedback5-boss-pair.e2e.test.js (new, 2: both halves and the frames in prep and fight; item 55's
eliminated guest on the boss field with the leader). Docs: design/client.md §10 / §15, design/network.md,
PLAYING.md.
2026-10-07 02:29:49 +08:00
sganggs f14f03a59f feedback5 CB5: in a boss round's prep a watcher sees the boss field — the scouted player's half (mirrored on the right) with the leader standing at its spawn — instead of the normal board without a boss (community report of 2026-10-06, item 55)
Reproduced in the browser (notes i55-repro, an eliminated guest at the boss round): in the Final Assault fight
the watched boss field shows the leader (enemy_9013_acstmk at 10,3), but in the boss round's prep the
eliminated player saw its own empty normal board (camera 'prep', no leader), and a scout of a teammate was the
normal board too, while the players prepare on their half of the boss field with the leader standing there.
Match.prepFieldMeta: in a boss round's prep the scout is built on the boss field (kind 'boss', BOSS_RECT,
units through finalAssault.js bossFieldPlacement: rows − 7, the right half mirrored with RIGHT ↔ LEFT, the player's side;
nextEnemies keeps the leader's spawn tile); game.js frames it with the boss-field prep camera of that side,
which shows the leader (render/app.js leaderShown). With item 56 an eliminated viewer gets it at the round
start without a tap. After the fix the same run: guest field kind 'boss', camera bossPrep, leader shown.

Golden unchanged. Tests: test/match/feedback5-boss-prep-scout.test.js (new, 2). Docs: design/network.md,
design/client.md (§15).
2026-10-07 02:11:44 +08:00
sganggs e8d2b45ad4 feedback5 CB5: an eliminated player (and a spectator seat) follows a player through every phase reset — the one it last watched, else the first player still in — instead of being sent back to its own empty board each round (community report of 2026-10-06, item 56; the idea of PR #189)
Official: research 09 §3.1 — after elimination the client keeps observing (self_dead_dialog KEEP_WATCH,
DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget). Until 0.2.0 every phase reset cleared
an eliminated player's watch: in 回合开始 / 机变 / 休整期 it saw its own empty board, in battle the first field.

Server (server/match/match/watch.js): Match.watchPref (viewer → the player of its last manual g.watch: a prep
scout, a single-player field, or the new optional g.watch playerId on a shared 联防 / boss pair field — PR
#189's review point); _watchTargetOf = that player while still in, else the first player still in (seat
order, AI seats included); startRound scouts its board (_followScout, kept through 机变 and 休整期), both
combat modes and the boss fields start on its field, a resync / a spectator joining in prep gets the board,
a followed player who quits hands over at once. Client: the screen adopts the pushed board like a 前往查看 tap
(battle/observe.js followedScout; the stale-field fix of PR #189), keeps it through the prep phases (no camera
flight home and back), a spectator no longer asks for the first player itself; g.watch sends the tapped row.
Overlap with PR #189 (not merged): the same preference chain; differences — the fallback includes AI seats
(a lone human with AI teammates had nobody to follow), shared fields record the tapped playerId.

Golden unchanged. Tests: test/match/feedback5-watch-follow.test.js (new, 9), spectator.test.js updated (the
spectator follows the player it scouted). Docs: design/network.md, PLAYING.md.
2026-10-07 02:06:10 +08:00
sganggs 4e58481706 feedback5 WY: the Final Assault / Hidden Core leader pool is bloodPoint × the players alive when the fight starts (solo × 1), replacing 「保持固定血量」; 胄's drone share passes 限伤 (from PR #209 by @qingjingshenghuo; the owner's decision of 2026-10-06)
Ported by hand from PR #209 by @qingjingshenghuo (its one-line data/config.json was not taken):
- server/match/gamedata.js bossPoolShareOf (used by GameData.bossPoolShare and by finalAssault.bossPoolHp for a plain
  game-data object): solo × `solo` (1); co-op with `perPlayer` true × `coop` (1) × the players alive at the boss
  round's start (bots and AI 托管 seats count, eliminated / departed seats do not; at most aliveFull 4; a count left
  out = a full team). The fixed pool stays restorable through config: bossHpScale perPlayer false + solo 0.25 (and its
  optional aliveScaling).
- tools/build-data.mjs: the global bossHpScale carries the rule (perPlayer true, solo 1); the modes' entries keep only
  bloodPointKey / unaffectedByEnemyScale. data/config.json regenerated offline — JSON compare: only bossHpScale (global
  and the 9 mode entries) changed.
- The problem #209 documented: a 胄 drone death is 2 % of the pool and 限伤 cancels a hit of ceil ≥ 300000, so the
  hidden 胄 终极 pool at 3 / 4 players (21.6M / 28.8M) would cancel every drone (432000 / 576000). The drone link is
  a share of the pool, no hit: Battle.loseHp takes noHitLimit and the link passes the limit [ASSUMED: the official
  client checks every damage modifier and PRTS calls it 真实伤害, but which max HP it reads is undocumented]; a part's
  传递 and every hit stay under the limit.

Golden (regenerated; why: the pool size):
- fields: boss-boss_1..7-pair/-solo and hidden-boss_8..10-pair/-solo (all 20 boss / hidden scenarios) — a pair
  field's pool × 2 (two players), a solo template's × 4 (solo × 1, was × 0.25); boss-boss_1-pair, boss-boss_1-solo,
  boss-boss_3-pair, boss-boss_3-solo, boss-boss_5-solo, boss-boss_7-pair and hidden-boss_9-solo now end at the
  200 s cap instead of clearing; the two 联防 scenarios are unchanged.
- matches: solo-FUNNY-2, solo-NORMAL-1, solo-HARD-1, solo-HARD-2 now lose the Final Assault (solo leaders have 4× the
  HP: team LP 0 with 93088–176395 of the pool left); solo-FUNNY-1, solo-NORMAL-2, solo-HARD-9-boosted (its Hidden Core
  too), coop2-NORMAL-3, coop4-FUNNY-6, coop2-ABYSS-10-boosted, coop2-NORMAL-70-diy still win with more leader damage
  (pool × players alive; solo-FUNNY-1 / solo-HARD-9-boosted end with 6 / 1 team LP less). Unchanged:
  coop2-NORMAL-8-serverrun and coop2-NORMAL-14-standins (one player left at R14: × 1 as before) and the matches that
  end before R14; roster / bonds / standins / diy unchanged.

Tests: test/match/boss-hp-scaling.test.js (from PR #209, adapted — every leader and difficulty × 1–4 players and solo;
R14 / R15 leave out two departed players; AI 托管 and bots count, an eliminated player does not; config restores the
fixed pool, the opposite of the PR's test; the plain-object fallback agrees; only the leader's HP scales);
test/content/enemies_bosses.test.js (hidden 胄 终极 at 3 / 4 players: the drone share lands, a hit of the same size is
cancelled — fails without the change). Updated on purpose: finalAssault, balance, bosshp, connection, docs-consistency
(the §20.9 record points to §25.13.4; the data carries perPlayer true), sim/playtest6_limits (the drone share lands
past the line), match/playtest6-bosspool (the 5 % knob halved to keep the 2-player pool's size: that test is about the
pool's arithmetic). Docs: PLAYING §7, DATA, META, BALANCE, SIM, design engine.md / network.md, the §20.9 / §20.10
records, research 00 / 01 / 08, the code comments, DESIGN §25.13.4 and the §25.10 table.
2026-10-06 21:41:49 +08:00
sganggs da1f5830ca feedback5 WW: split DESIGN.md into docs/design/ (the current rules) and docs/history/ (the per-release revisions), section numbers kept — a pure move: every ## N. block (heading, text, the --- after it) moves verbatim, each part file only gains a title line and a pointer to the index; docs/DESIGN.md keeps its header and becomes the index (a § → file table). design/overview.md §0 §1 §2 §11 §12, geometry-time.md §3 §4, engine.md §5 §7, match.md §6 §16, network.md §8 §14, client.md §9 §10 §13 §15; history/v2.2-playtest3.md §17, v2.3-playtest4.md §18, v2.4-playtest5.md §19, v2.5-playtest6.md §20, 0.1.1.md §21, 0.1.2.md §22, 0.1.3.md §23, 0.1.4.md §24, 0.2.0.md §25. Round trip: the index header plus the blocks in § order reproduce the pre-split DESIGN.md byte for byte (813 785 bytes, sha256 fe81884c…). Tests: test/helpers/designDocs.js designText() (the split script's splitBlocks / designText) reassembles the text in § order; docs-consistency and feedback1-gaps read it instead of docs/DESIGN.md (every pin unchanged); docs-paths guards the split (each § exactly once in docs/design/ or docs/history/, the index row of every file names its sections, every "DESIGN §N(.M)" cited in the repository is a heading — 779 in 327 files today); package.test keeps docs/design/ and docs/history/ out of the zips; golden unchanged 2026-10-06 19:08:44 +08:00