feedback6 R21B: 联防 plays on the round's battlefield again — its terrain, crates, water, devices and runes, as in 0.1.4 (the owner's decision of 2026-10-07, 「官服保留地形,改回去」; GitHub #244). 0.2.0's escaped-level map (8a436249, §25.6.4) is withdrawn: that map is the placeholder grid all 39 wave template levels carry, tile for tile; only the 11 stage levels have terrain. match/unitePhase.js _uniteOpts fields this.stageId in both combat modes, unite.js uniteStageId is removed, the client draws the 联防 on the board's stage again (screens/game.js: 0.1.4's ownStage / baseStage, no map switch, nothing to restore on leaving), the dev mock likewise. Kept from 0.2.0: own-board 突袭 / 乌尔比安 S3 landings (onOwnBoard, raidTile untouched), the bounty payee, the 联防 BGM, the SETTLE result box. data/stages.json keeps the two kind 'unite' records (no field uses them; engine tests use them as a plain two-halves road); tools/build-data.mjs comments only, an offline rebuild reproduces data/ byte for byte. The 联防 battle input is byte-identical to v0.1.4's in 20 cases (5 stages × 1 / 2 helpers × both combat modes); GitHub #41 item 3 (野鬃 pushing walkers back across the gate onto the other half) is back with v0.1.4's numbers, none off the map. Tests: test/match/feedback5-unite-map.test.js rewritten (6, all fail on the parent), test/e2e/coop.e2e.mjs checks the 联防 field's drawn stage; docs: DATA §12, META §4, PLAYING §5, design/geometry-time, I18N, the §25.6.4 pointer; golden: fields unite-1 (escaped_single → act1autochess_m02) and unite-2 (escaped_multi → act1autochess_m07), the stages of 0.1.4, and the co-op matches whose 联防 rounds come out differently on the round's map — coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins (now a victory)

This commit is contained in:
sganggs committed 2026-10-07 17:40:03 +08:00
1 parent ac6de8a12d
commit 75202776cc
25 files changed
+333 -281

No files matched your search

+6 -5
View File
@@ -5,7 +5,7 @@
import { PHASE, GEO } from '../../../shared/constants.js';
import { deriveSeed } from '../../sim/rng.js';
import { uniteBattleOpts, uniteSurvivors, uniteStageId } from '../unite.js';
import { uniteBattleOpts, uniteSurvivors } from '../unite.js';
import { FieldRunner, timelineAt, uniteBillBounds } from '../fields.js';
import { uniteLeft } from '../../sim/spec.js';
import { FLOW_TICKER_PRIORITY, DELAYS } from './common.js';
@@ -40,9 +40,10 @@ export class MatchUnite {
}
/**
* Battle options of the 联防 field (helpers' carried end state, the leakers' enemies) on its own map — the escaped
* template's (unite.js uniteStageId; GitHub #41), the round's stage only when the data lacks it. The field meta and the
* client-run spec carry that stageId, so every viewer draws the 联防 map.
* Battle options of the 联防 field (helpers' carried end state, the leakers' enemies) on the round's battlefield, its
* terrain, crates, water, devices and runes included (unite.js header; the owner's decision of 2026-10-07 — 0.2.0's
* escaped-level map is withdrawn). The field meta and the client-run spec carry the match stageId, so every viewer
* draws the battlefield the boards stand on.
*/
_uniteOpts(plan, limit) {
const { wave, players } = uniteBattleOpts(this, plan, limit);
@@ -51,7 +52,7 @@ export class MatchUnite {
kind: 'unite',
modeId: this.modeId,
round: this.round,
stageId: uniteStageId(this.gd, plan.helpers.length) ?? this.stageId,
stageId: this.stageId,
rect: { ...GEO.UNITE_RECT },
timeLimit: limit,
players,
+7 -21
View File
@@ -5,11 +5,13 @@
// players chosen by most units on the field (downed included) > has an active bond (存疑) > most undowned units, then
// seat; with 2 helpers the one ranked first by most units > active bond > Σ active bond layers (存疑) > most undowned
// units "率先迎敌" on the RIGHT-hand field (colOffset +8, where the escaped_multi routes enter), the other keeps the
// left half (colOffset 0); a lone helper plays escaped_single on its own field. The field is the template's own map
// (uniteStageId: data/stages.json act1autochess_escaped_single / _multi, kind 'unite' — two road halves joined at col 10,
// the right half's gates at col 18; GitHub #41: until 0.1.3 it was the round's stage, its water, crates and devices
// included), every helper's pieces on their prep tiles ("按休整期位置部署在场"), the right-hand one shifted 8 columns
// (the official maps' halves, = the stage config's player_map_lr_offset 8). Their operators keep the HP ratio and
// left half (colOffset 0); a lone helper plays escaped_single on its own field. The field is the round's battlefield —
// the match stage with its terrain, crates, water, devices and runes — opened to both halves (GEO.UNITE_RECT, cols
// 0–20; the stage's right half is its left half + 8 columns), every helper's pieces on their prep tiles
// ("按休整期位置部署在场"), the right-hand one shifted 8 columns (= the stage config's player_map_lr_offset 8): the
// owner's knowledge of the official mode, 2026-10-07 「官服保留地形」. 0.2.0 fielded it on the escaped levels' own map,
// an empty road (GitHub #41) — withdrawn in 0.2.1; data/stages.json keeps those two records (kind 'unite'), which no
// match fields. Their operators keep the HP ratio and
// the SP (技力, stored charges included) from the end of their own combat, nothing else — a skill still running then
// enters switched off (BattleResult.unitsEnd → PlayerBattleInput.units[].carryState `{ hpPct, sp }`, "阵地以其当前状态";
// community report #34 / GitHub #82: it used to restart for free). An operator knocked out at the end of its own combat (alive false) is fielded with
@@ -120,22 +122,6 @@ export function helperOrder(m, perfects, results) {
return select.sort((a, b) => S(b).units - S(a).units || (S(b).active - S(a).active) || S(b).layers - S(a).layers || S(b).standing - S(a).standing || a.seat - b.seat);
}
/**
* The map of the 联防 field (GitHub #41): act2autochess constData escapedBattleTemplateMapSinglePlayer (1 helper) /
* MultiPlayer (2 helpers) name one level for both the 联防 wave and its map — config.unite.templates[n], built into
* data/stages.json as the stage of the same id (kind 'unite'). null when the data has no such stage (degraded data:
* the caller keeps the round's stage).
* @param {import('./gamedata.js').GameData} gd
* @param {number} helperCount
* @returns {string|null}
*/
export function uniteStageId(gd, helperCount) {
const templates = gd.unite.templates || {};
const id = templates[String(helperCount)] ?? templates[helperCount] ?? null;
const st = id ? gd.stage(id) : null;
return st && st.kind === 'unite' ? id : null;
}
/** Battle options for the unite field (without data/logger, added by the match). */
export function uniteBattleOpts(m, plan, timeLimit) {
const wave = buildUniteWave(m.gd, plan.leaked, plan.helpers.length, timeLimit);