feedback6: merge R21B — 联防 plays on the round's battlefield again, its terrain, crates, water, devices and runes as in 0.1.4 (the owner's report 「联防阶段地形全消失了」 and knowledge of the official mode, GitHub #244's tournament video; 0.2.0's escaped level was the placeholder road every wave-template level carries); the 联防 battle input is byte-identical to 0.1.4's in 20 cases; 乌尔比安's comment keeps both streams' sentences; golden as R21B's commit (unite-1 / unite-2 back to their stages, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins), the merged tree differs from the branch only in R21A's boss pair fields
This commit is contained in:
commit
26c72a6d95
25 files changed
+334
-283
No files matched your search
+12
-9
@@ -418,19 +418,22 @@ hidden core `h08_0X`; 联防 `act1autochess_escaped_single|multi`; training `tr0
|
|||||||
Timing: templates have one wave with one fragment, so `time = wave.preDelay + fragment.preDelay + action.preDelay`
|
Timing: templates have one wave with one fragment, so `time = wave.preDelay + fragment.preDelay + action.preDelay`
|
||||||
exactly (the builder warns if a multi-fragment template ever appears).
|
exactly (the builder warns if a multi-fragment template ever appears).
|
||||||
|
|
||||||
## 12. `stages.json` — `{ [stageId]: Stage }` (11 terrains, 8 active, + the 2 联防 maps)
|
## 12. `stages.json` — `{ [stageId]: Stage }` (11 terrains, 8 active, + the 2 escaped levels' maps)
|
||||||
|
|
||||||
The 11 battle stages of `stageDatasDict`, then the two **联防 maps** (GitHub #41): act2autochess constData
|
The 11 battle stages of `stageDatasDict` — every field of a match is fought on its stage: the own boards, the boss fields
|
||||||
`escapedBattleTemplateMapSinglePlayer` / `MultiPlayer` name the level of the 联防 battle (`level_act1autochess_escaped_single`
|
and the 联防 field (both halves, `GEO.UNITE_RECT`; the owner's decision of 2026-10-07) — then the maps of the two escaped
|
||||||
/ `_multi`, the wave templates of the same id in `waves.json`), and its own map is the 联防 field — tile for tile the same
|
levels: act2autochess constData `escapedBattleTemplateMapSinglePlayer` / `MultiPlayer` name the level of the 联防 wave
|
||||||
on both: two road halves (cols 3–9 and 11–17, rows 9–12) joined at col 10, the objective at (9,2), no devices or special
|
(`level_act1autochess_escaped_single` / `_multi`, the wave templates of the same id in `waves.json`). Their map is the
|
||||||
terrain. `server/match/unite.js uniteStageId` fields the 联防 battle on the one of its helper count.
|
placeholder grid every wave template level carries, tile for tile (the round templates `01…07` and `h01…h08`, the
|
||||||
|
training `trXX` too): two road halves (cols 3–9 and 11–17, rows 9–12) joined at col 10, the objective at (9,2), no devices or
|
||||||
|
special terrain. 0.2.0 fought the 联防 battle on it (GitHub #41); no field uses these two records since 0.2.1 — they stay
|
||||||
|
as official level data, and sim tests use them as a plain two-halves road.
|
||||||
|
|
||||||
| Field | Example | Meaning |
|
| Field | Example | Meaning |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `id`, `name` | `"act2autochess_m01"`, `"战场#05(下半) 源石流发生装置"` | player-facing name from research 05 (the official tables carry none; falls back to id). The build drops bracketed segments containing Latin letters (research notes such as `战场#01 (upper half #01)` → `战场#01`), logs a warning, and fails validation if Latin text remains. The 联防 maps: `联防阵地(1名玩家)` / `联防阵地(2名玩家)`, the remake's own label [ASSUMED] (no table or PRTS page names them; no screen shows it) |
|
| `id`, `name` | `"act2autochess_m01"`, `"战场#05(下半) 源石流发生装置"` | player-facing name from research 05 (the official tables carry none; falls back to id). The build drops bracketed segments containing Latin letters (research notes such as `战场#01 (upper half #01)` → `战场#01`), logs a warning, and fails validation if Latin text remains. The escaped levels' maps: `联防阵地(1名玩家)` / `联防阵地(2名玩家)`, the remake's own label [ASSUMED] (no table or PRTS page names them; no screen shows it) |
|
||||||
| `weight`, `active`, `modes` | `50`, `true`, `["mode_single_normal",…]` | match-start pick weight (act1 m05–m07 weight 0); the 联防 maps: `0`, `false`, `[]` (never a match stage) |
|
| `weight`, `active`, `modes` | `50`, `true`, `["mode_single_normal",…]` | match-start pick weight (act1 m05–m07 weight 0); the escaped levels' maps: `0`, `false`, `[]` (never a match stage) |
|
||||||
| `kind`, `helpers` | `"unite"`, `1` | only on the two 联防 maps: the helper count whose 联防 field it is (= `config.unite.templates`; the build fails when they disagree) |
|
| `kind`, `helpers` | `"unite"`, `1` | only on the escaped levels' two maps: the helper count of their wave template (= `config.unite.templates`; the build fails when they disagree); no field is fought on them |
|
||||||
| `size` | `[19,21]` | |
|
| `size` | `[19,21]` | |
|
||||||
| `rows[]` | `rows[9] = "##Errr#rrrSrrr#rrrS##"` | 19 strings, **index = row (0 = bottom)**, one glyph per col |
|
| `rows[]` | `rows[9] = "##Errr#rrrSrrr#rrrS##"` | 19 strings, **index = row (0 = bottom)**, one glyph per col |
|
||||||
| `tiles[glyph]` | `{"tileKey":"tile_road","height":"LOW","buildable":"ALL","passable":"ALL","groundPassable":true,"flyPassable":true,"special":null,"bb":{}}` | actual tile properties of each glyph used. `buildable` is the **effective** deploy type: the level's buildableType, except a tile whose mechanism refuses deployment — 深水区 `tile_deepsea` (PRTS 深水区 地形信息 "部署类型 全部位 … 地形机制 拒绝部署(待补充)"; player report #3 after 0.1.0) — which is `NONE` and keeps the level's value in `buildableType` (`server/sim/grid.js DEPLOY_REFUSED_TILES`, shared with the builder) |
|
| `tiles[glyph]` | `{"tileKey":"tile_road","height":"LOW","buildable":"ALL","passable":"ALL","groundPassable":true,"flyPassable":true,"special":null,"bb":{}}` | actual tile properties of each glyph used. `buildable` is the **effective** deploy type: the level's buildableType, except a tile whose mechanism refuses deployment — 深水区 `tile_deepsea` (PRTS 深水区 地形信息 "部署类型 全部位 … 地形机制 拒绝部署(待补充)"; player report #3 after 0.1.0) — which is `NONE` and keeps the level's value in `buildableType` (`server/sim/grid.js DEPLOY_REFUSED_TILES`, shared with the builder) |
|
||||||
|
|||||||
+1
-1
@@ -157,7 +157,7 @@ rebuilt.
|
|||||||
same as in Chinese, so `ui/richText.js` renders the English unchanged (a few official EN texts leave a tag unclosed;
|
same as in Chinese, so `ui/richText.js` renders the English unchanged (a few official EN texts leave a tag unclosed;
|
||||||
the parser runs it to the end of the text, as the game does).
|
the parser runs it to the end of the text, as the game does).
|
||||||
- **Fallbacks**, in order: `tools/i18n/fallback-remake.json` (the remake's own strings: stage-name parts, item notes,
|
- **Fallbacks**, in order: `tools/i18n/fallback-remake.json` (the remake's own strings: stage-name parts, item notes,
|
||||||
the 联防 map names, the season name), `tools/i18n/fallback-pr70.json` (PR #70's translations of texts the EN client
|
the escaped levels' map names, the season name), `tools/i18n/fallback-pr70.json` (PR #70's translations of texts the EN client
|
||||||
lacks: modules of CN-only operators, the 甄选 slots, 结算称号 criteria, emote labels), then `public/i18n/en.json`;
|
lacks: modules of CN-only operators, the 甄选 slots, 结算称号 criteria, emote labels), then `public/i18n/en.json`;
|
||||||
anything else stays Chinese. Research notes (`bonds.*.spec`, `implFormula`, `assumed` …) are never translated.
|
anything else stays Chinese. Research notes (`bonds.*.spec`, `implFormula`, `assumed` …) are never translated.
|
||||||
- **Format** (`shared/i18nData.js`): `{ version, lang, meta: { source, coverage, totals }, names, files }`. `files[file][id]`
|
- **Format** (`shared/i18nData.js`): `{ version, lang, meta: { source, coverage, totals }, names, files }`. `files[file][id]`
|
||||||
|
|||||||
+4
-4
@@ -689,10 +689,10 @@ helpers = `unite.js helperOrder` (research 08 §5, PRTS 卫戍协议/帮助 §
|
|||||||
chosen by most units on the field (downed included) > an active bond > most standing units > seat; the pair ordered by
|
chosen by most units on the field (downed included) > an active bond > most standing units > seat; the pair ordered by
|
||||||
units > active bond > Σ active layers > standing > seat (LP plays no part), the first one on the right-hand field
|
units > active bond > Σ active layers > standing > seat (LP plays no part), the first one on the right-hand field
|
||||||
(colOffset +8, where escaped_multi enters), the other colOffset 0; the escaped template of that size routes the leaked
|
(colOffset +8, where escaped_multi enters), the other colOffset 0; the escaped template of that size routes the leaked
|
||||||
enemies by slot class, and its own map is the field (`unite.js uniteStageId`: stages.json `act1autochess_escaped_single` /
|
enemies by slot class on the round's battlefield — the match stage opened to both halves (`GEO.UNITE_RECT`, cols 0–20),
|
||||||
`_multi`, two road halves joined at col 10 — no water, crates or devices of the round's stage, no band map characters:
|
its terrain, crates, water, devices, runes and band map characters included, as in 0.1.x (the owner's decision of
|
||||||
the level has no predefined ones; the round's stage only with data that lacks them; GitHub #41), each helper's pieces
|
2026-10-07, 「官服保留地形」; 0.2.0 fought it on the escaped levels' own map, the placeholder road every wave template
|
||||||
on their prep tiles; helpers' operators carry
|
carries — GitHub #41, withdrawn), each helper's pieces on their prep tiles; helpers' operators carry
|
||||||
`{ hpPct, sp }` from `unitsEnd` ("阵地以其当前状态": the HP ratio and the 技力 only — a skill running at the end enters
|
`{ hpPct, sp }` from `unitsEnd` ("阵地以其当前状态": the HP ratio and the 技力 only — a skill running at the end enters
|
||||||
switched off; summon pieces `{ sp }`, "召唤物仅修改技力"); an operator knocked out at the end of the helper's own
|
switched off; summon pieces `{ sp }`, "召唤物仅修改技力"); an operator knocked out at the end of the helper's own
|
||||||
combat carries `{ down: true }` (PRTS 卫戍协议/帮助: "部署完成后…上一阶段为退场状态的干员强制退场"): deployed, then forced out
|
combat carries `{ down: true }` (PRTS 卫戍协议/帮助: "部署完成后…上一阶段为退场状态的干员强制退场"): deployed, then forced out
|
||||||
|
|||||||
+1
-1
@@ -90,7 +90,7 @@
|
|||||||
某回合**有人漏怪、又有人完美作战**(全歼且无漏怪)时触发:最多 2 名完美作战的队友带着自己的阵容(与官方相同,只保留作战结束时的生命比例和技力:作战结束时还开着的技能在联防开场是关着的,技力是开技能后剩下的值,通常为 0;召唤物只保留技力;作战结束时已被击倒的干员在原位倒地(阿戈尔的开场吞噬仍把它当作站着的干员计算标记与基础攻击力、阻挡数,之后它照常退场),按官方的「强制退场」以完整的再部署时间重新倒计时,时间到且部署费用足够后照常再部署——联防较短时可能来不及再部署;机变「征召」这类开战时生效的再部署时间减免同样计入),迎击其他玩家漏掉的全部敌人。盟约层数按本回合自己作战结束时达到的层数(即盟约栏显示的层数)计算,联防中不再叠加层数;这些层数在结算时只计入一次。
|
某回合**有人漏怪、又有人完美作战**(全歼且无漏怪)时触发:最多 2 名完美作战的队友带着自己的阵容(与官方相同,只保留作战结束时的生命比例和技力:作战结束时还开着的技能在联防开场是关着的,技力是开技能后剩下的值,通常为 0;召唤物只保留技力;作战结束时已被击倒的干员在原位倒地(阿戈尔的开场吞噬仍把它当作站着的干员计算标记与基础攻击力、阻挡数,之后它照常退场),按官方的「强制退场」以完整的再部署时间重新倒计时,时间到且部署费用足够后照常再部署——联防较短时可能来不及再部署;机变「征召」这类开战时生效的再部署时间减免同样计入),迎击其他玩家漏掉的全部敌人。盟约层数按本回合自己作战结束时达到的层数(即盟约栏显示的层数)计算,联防中不再叠加层数;这些层数在结算时只计入一次。
|
||||||
|
|
||||||
- 参加者按官方规则挑选:场上单位最多 > 有已激活的盟约 > 未倒下的单位最多 > 座位靠前(与 LP 无关);两人参加时,排在前面的一位(再比较激活盟约的层数总和)在右半场「率先迎敌」。
|
- 参加者按官方规则挑选:场上单位最多 > 有已激活的盟约 > 未倒下的单位最多 > 座位靠前(与 LP 无关);两人参加时,排在前面的一位(再比较激活盟约的层数总和)在右半场「率先迎敌」。
|
||||||
- 联防在官方的**联防专用地图**上进行(与官方相同,不是本回合的战场):一整片道路,没有水池、箱子、装置等地形;干员按休整期的位置站好。一人协防时敌人从中间的入口进入左半场;两人协防时敌人从最右侧进入,先经过右半场,再穿过中间进入左半场。
|
- 联防在**本回合的战场**上进行(与官方相同:地形、水池、箱子和装置都保留),左右两个半场都在场上;干员按休整期的位置站好,两人协防时排在前面的一位整体右移 8 格、站在右半场。一人协防时敌人从中间的入口进入左半场;两人协防时敌人从最右侧进入,先经过右半场,再穿过中间进入左半场。被推开的敌人可能越过中间的入口退到右半场,之后会沿路线走回来。(0.2.0 曾把联防改到一整片空道路上,0.2.1 起改回本回合的战场。)
|
||||||
|
|
||||||
- 只有完美作战的队友能参与联防;联防中**盟约不会叠加层数**。
|
- 只有完美作战的队友能参与联防;联防中**盟约不会叠加层数**。
|
||||||
- 联防的结果**只在结算的「作战结束」框里报**(与官方一样,没有「联防成功 / 联防失败」之类的字幕)。框里的数字是服务器结算时**真正扣到你头上**的点数:漏怪的玩家按联防后还剩下的敌人扣,不是自己那场漏了几只。**「全员无伤!」只在联防一只都没放过去时出现**(这一轮谁都没被扣);联防没守住、而你自己没被扣(你是去帮忙的队友,或你漏的敌人都被拦下了),框里只有「作战结束」这个标题——掉血的队友并不是无伤,官方对话框里也没有别的句子。框的颜色:青色=联防全部拦下,橙色=联防没守住但你没掉血,红色=你被扣了。漏了几只仍然看顶栏的橙色「×N」。
|
- 联防的结果**只在结算的「作战结束」框里报**(与官方一样,没有「联防成功 / 联防失败」之类的字幕)。框里的数字是服务器结算时**真正扣到你头上**的点数:漏怪的玩家按联防后还剩下的敌人扣,不是自己那场漏了几只。**「全员无伤!」只在联防一只都没放过去时出现**(这一轮谁都没被扣);联防没守住、而你自己没被扣(你是去帮忙的队友,或你漏的敌人都被拦下了),框里只有「作战结束」这个标题——掉血的队友并不是无伤,官方对话框里也没有别的句子。框的颜色:青色=联防全部拦下,橙色=联防没守住但你没掉血,红色=你被扣了。漏了几只仍然看顶栏的橙色「×N」。
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Part of [DESIGN.md](../DESIGN.md) (the index; section numbers are global).
|
|||||||
- Tile semantics come from `data/stages.json` legend: height (LOW/HIGH), buildable (ALL/MELEE/RANGED/NONE — the **effective** deploy type: the 深水区 `tile_deepsea` refuses deployment, NONE with the level's `buildableType` ALL kept, PRTS 深水区 地形信息 "拒绝部署(待补充)"; an active 特制水上平台 makes its tile deployable in the prep deploy map only, §21.3), ground-passable, fly-only, special terrain (mire, smog, deepsea, infection). The fenced tiles (围墙 `tile_fence_bound` / 围栏 `tile_fence`, legend `b`: LOW, buildable ALL, passable to flyers only) take units, which attack normally from there but block no ground enemy (PRTS 地形机制, §21.23). Melee chess may stand only on LOW tiles with buildable ALL/MELEE — except a melee chess whose trait reads 「可以放置于远程位」 (the 钩索师 / 推击手 branch trait: 歌蕾蒂娅, 崖心, 见行者, normal and elite, with any module or none), which may use the ranged tiles too (`shared/highGround.js`; the owner's decision of 2026-10-05, following PRTS — it reverses the 2026-10-04 one that allowed only elite 歌蕾蒂娅 with HOK-Y; on a 高台 it attacks and blocks nothing); ranged chess on ALL/RANGED **and** on LOW ALL/MELEE tiles ("所有行动内远程干员可部署在近战位"). Tokens follow their own `position`; a summon marked `ownerRange` (狼群, 流形: "只能部署在召唤者攻击范围内"; Mon3tr's 重构体 by her talent) goes only on a tile of its owner's attack range — the loadout grid rotated by the owner's facing around its tile (`PlayerState.summonRange`, §21.3) —, one marked `ownerRangeOutside` / `rangedTilesOnly` (凯尔希·思衡托's 战术锚点: "仅可以部署在凯尔希·思衡托攻击范围外的远程位") only on a 高台 outside it (`summonExcluded`, placement class `high`; 0.2.0).
|
- Tile semantics come from `data/stages.json` legend: height (LOW/HIGH), buildable (ALL/MELEE/RANGED/NONE — the **effective** deploy type: the 深水区 `tile_deepsea` refuses deployment, NONE with the level's `buildableType` ALL kept, PRTS 深水区 地形信息 "拒绝部署(待补充)"; an active 特制水上平台 makes its tile deployable in the prep deploy map only, §21.3), ground-passable, fly-only, special terrain (mire, smog, deepsea, infection). The fenced tiles (围墙 `tile_fence_bound` / 围栏 `tile_fence`, legend `b`: LOW, buildable ALL, passable to flyers only) take units, which attack normally from there but block no ground enemy (PRTS 地形机制, §21.23). Melee chess may stand only on LOW tiles with buildable ALL/MELEE — except a melee chess whose trait reads 「可以放置于远程位」 (the 钩索师 / 推击手 branch trait: 歌蕾蒂娅, 崖心, 见行者, normal and elite, with any module or none), which may use the ranged tiles too (`shared/highGround.js`; the owner's decision of 2026-10-05, following PRTS — it reverses the 2026-10-04 one that allowed only elite 歌蕾蒂娅 with HOK-Y; on a 高台 it attacks and blocks nothing); ranged chess on ALL/RANGED **and** on LOW ALL/MELEE tiles ("所有行动内远程干员可部署在近战位"). Tokens follow their own `position`; a summon marked `ownerRange` (狼群, 流形: "只能部署在召唤者攻击范围内"; Mon3tr's 重构体 by her talent) goes only on a tile of its owner's attack range — the loadout grid rotated by the owner's facing around its tile (`PlayerState.summonRange`, §21.3) —, one marked `ownerRangeOutside` / `rangedTilesOnly` (凯尔希·思衡托's 战术锚点: "仅可以部署在凯尔希·思衡托攻击范围外的远程位") only on a 高台 outside it (`summonExcluded`, placement class `high`; 0.2.0).
|
||||||
- **Facing (corrected, see research 09 §1.2):** like standard Arknights, every board piece has a direction `dir ∈ UP|RIGHT|DOWN|LEFT` chosen with the 4-direction deploy wheel after dropping it on a tile (also re-orientable in place); default `RIGHT`; persists across rounds. Range grid entries are `[dRow, dCol]` relative to facing RIGHT and are rotated: RIGHT `(dr,dc)`, UP `(dc,−dr)`, LEFT `(−dr,−dc)`, DOWN `(−dc,dr)` (row 0 = bottom); `rangeExtend` applies along +dCol before rotating — to a running skill's range too, unless that skill ignores 攻击距离 (`targeting.noRangeExtend`, 信仰搅拌机 S3, §21.16). The right-side Final Assault player is mirrored col `c→20−c` with RIGHT↔LEFT (UP/DOWN unchanged).
|
- **Facing (corrected, see research 09 §1.2):** like standard Arknights, every board piece has a direction `dir ∈ UP|RIGHT|DOWN|LEFT` chosen with the 4-direction deploy wheel after dropping it on a tile (also re-orientable in place); default `RIGHT`; persists across rounds. Range grid entries are `[dRow, dCol]` relative to facing RIGHT and are rotated: RIGHT `(dr,dc)`, UP `(dc,−dr)`, LEFT `(−dr,−dc)`, DOWN `(−dc,dr)` (row 0 = bottom); `rangeExtend` applies along +dCol before rotating — to a running skill's range too, unless that skill ignores 攻击距离 (`targeting.noRangeExtend`, 信仰搅拌机 S3, §21.16). The right-side Final Assault player is mirrored col `c→20−c` with RIGHT↔LEFT (UP/DOWN unchanged).
|
||||||
- **Range test:** an enemy is inside a grid range if the tile containing it (`round(y), round(x)`) is one of the range tiles; a huge enemy (data `hitArea`: 巨型单位, a 4.95 × 2.95 rectangle moved 1.0 up) if any tile its hit rectangle overlaps is one (PRTS 作战机制 "巨型BOSS单位的每一个占据的格子都可以让其本身通过格子判定"; `sim/body.js`, §19.4). Radius ranges (enemies, auras, AoE) use Euclidean distance in tiles — to a huge enemy's rectangle [ASSUMED] — except splash around a struck target, a 中点判定 on positions (PRTS 作战机制 "中点判定…案例:阻挡,酒神1天赋的1.3溅射半径"). A ranged enemy's normal attack takes an ally whose collider (radius 0.25, PRTS 作战机制 §碰撞体积 "我方干员碰撞体积基本均为以0.25格为半径的圆形") touches its range circle: centre distance ≤ `rangeRadius` + 0.25 (`constants.js ALLY_COLLIDER_RADIUS`, `ai.js enemyAttack`; §20.4).
|
- **Range test:** an enemy is inside a grid range if the tile containing it (`round(y), round(x)`) is one of the range tiles; a huge enemy (data `hitArea`: 巨型单位, a 4.95 × 2.95 rectangle moved 1.0 up) if any tile its hit rectangle overlaps is one (PRTS 作战机制 "巨型BOSS单位的每一个占据的格子都可以让其本身通过格子判定"; `sim/body.js`, §19.4). Radius ranges (enemies, auras, AoE) use Euclidean distance in tiles — to a huge enemy's rectangle [ASSUMED] — except splash around a struck target, a 中点判定 on positions (PRTS 作战机制 "中点判定…案例:阻挡,酒神1天赋的1.3溅射半径"). A ranged enemy's normal attack takes an ally whose collider (radius 0.25, PRTS 作战机制 §碰撞体积 "我方干员碰撞体积基本均为以0.25格为半径的圆形") touches its range circle: centre distance ≤ `rangeRadius` + 0.25 (`constants.js ALLY_COLLIDER_RADIUS`, `ai.js enemyAttack`; §20.4).
|
||||||
- **Fields at runtime:** a `Battle` receives a **field rect** (rows/cols subset) and the full stage grid. Normal battles use rows 9–12 × cols 0–10; unite uses rows 9–12 × cols 0–20 — on its own map, the escaped template's (stages.json `kind: 'unite'`, `unite.js uniteStageId`; GitHub #41), not the round's stage; boss uses rows 0–5 × cols 0–20 (solo boss uses the left half only). Pathfinding never leaves the rect.
|
- **Fields at runtime:** a `Battle` receives a **field rect** (rows/cols subset) and the full stage grid. Normal battles use rows 9–12 × cols 0–10; unite uses rows 9–12 × cols 0–20 of the round's stage, both halves with their terrain and devices (0.2.0's escaped-level map, GitHub #41, withdrawn in 0.2.1: the owner's decision of 2026-10-07); boss uses rows 0–5 × cols 0–20 (solo boss uses the left half only). Pathfinding never leaves the rect.
|
||||||
- **Speed:** `tilesPerSecond = moveSpeed × MOVE_SCALE` with `MOVE_SCALE = 0.5` (in `sim/constants.js`, tunable).
|
- **Speed:** `tilesPerSecond = moveSpeed × MOVE_SCALE` with `MOVE_SCALE = 0.5` (in `sim/constants.js`, tunable).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -419,6 +419,8 @@ machine-translated with the official terms, marked as such (§25.23).
|
|||||||
false`) carry the 联防 battle in both combat modes; the client draws that map and restores the board after. No water,
|
false`) carry the 联防 battle in both combat modes; the client draws that map and restores the board after. No water,
|
||||||
crates, devices or terrain in 联防; the BGM (PR #110) is unchanged. [ASSUMED]: the records' names 联防阵地(1名玩家) /
|
crates, devices or terrain in 联防; the BGM (PR #110) is unchanged. [ASSUMED]: the records' names 联防阵地(1名玩家) /
|
||||||
(2名玩家). Golden: unite-1, unite-2 and the co-op matches. Tests: `test/match/feedback5-unite-map.test.js`.
|
(2名玩家). Golden: unite-1, unite-2 and the co-op matches. Tests: `test/match/feedback5-unite-map.test.js`.
|
||||||
|
- **Withdrawn in 0.2.1**, see §26 — 联防 plays on the round's battlefield again (the owner's decision of 2026-10-07,
|
||||||
|
「官服保留地形」: the escaped levels' map is the placeholder road every wave template level carries).
|
||||||
|
|
||||||
#### 25.6.5 伺夜's 狼群 has the official 战术点形态; S1 acts by the pack's state (PRTS 备注; the owner's decision of 2026-10-05) — `content/tokens.js wolfPack / installWolfTacticalPoint / wolfReturnNow`, `kits/ops/chess_char_3_19-vigil.js`
|
#### 25.6.5 伺夜's 狼群 has the official 战术点形态; S1 acts by the pack's state (PRTS 备注; the owner's decision of 2026-10-05) — `content/tokens.js wolfPack / installWolfTacticalPoint / wolfReturnNow`, `kits/ops/chess_char_3_19-vigil.js`
|
||||||
|
|
||||||
|
|||||||
@@ -472,8 +472,8 @@ function startCombat(phase) {
|
|||||||
const e = data.lookup('enemies', bossRec.enemyKey);
|
const e = data.lookup('enemies', bossRec.enemyKey);
|
||||||
enemies.push({ id: id++, kind: 'enemy', side: 'enemy', ownerId: ME, defId: bossRec.enemyKey, name: bossRec.name, spine: bossRec.enemyKey, avatar: bossRec.enemyKey, x: 10, y: 3, facing: -1, maxHp: e?.stats?.maxHp || 1e5, hp: (e?.stats?.maxHp || 1e5) * 0.62, spawnAt: 0, dir: 0, boss: true, dead: false });
|
enemies.push({ id: id++, kind: 'enemy', side: 'enemy', ownerId: ME, defId: bossRec.enemyKey, name: bossRec.name, spine: bossRec.enemyKey, avatar: bossRec.enemyKey, x: 10, y: 3, facing: -1, maxHp: e?.stats?.maxHp || 1e5, hp: (e?.stats?.maxHp || 1e5) * 0.62, spawnAt: 0, dir: 0, boss: true, dead: false });
|
||||||
}
|
}
|
||||||
// the 联防 field fights on its own map (server unite.js uniteStageId: 2 helpers → escaped_multi; GitHub #41)
|
// every field — 联防 too — is fought on the round's battlefield (server unite.js; 0.2.0's escaped-level map withdrawn)
|
||||||
const field = { fieldId, kind, rect, stageId: kind === 'unite' ? 'act1autochess_escaped_multi' : pub.stageId, units: units.map((u) => ({ ...u })) };
|
const field = { fieldId, kind, rect, stageId: pub.stageId, units: units.map((u) => ({ ...u })) };
|
||||||
store.patch('match', { field });
|
store.patch('match', { field });
|
||||||
const allyState = units.map((u) => ({ ...u, hp: u.maxHp * (0.55 + rnd() * 0.45), sp: rnd() * 20, spMax: 20 }));
|
const allyState = units.map((u) => ({ ...u, hp: u.maxHp * (0.55 + rnd() * 0.45), sp: rnd() * 20, spMax: 20 }));
|
||||||
const t0 = performance.now();
|
const t0 = performance.now();
|
||||||
|
|||||||
+10
-11
@@ -356,20 +356,19 @@ function MatchScreen() {
|
|||||||
const shownMembers = shownField ? ((Array.isArray(pub?.fields) ? pub.fields : []).find((f) => f && f.fieldId === shownField.fieldId)?.players
|
const shownMembers = shownField ? ((Array.isArray(pub?.fields) ? pub.fields : []).find((f) => f && f.fieldId === shownField.fieldId)?.players
|
||||||
|| (Array.isArray(shownField.players) ? shownField.players : null)) : null;
|
|| (Array.isArray(shownField.players) ? shownField.players : null)) : null;
|
||||||
const ownView = showPrep || (shownField ? (shownMembers ? shownMembers.includes(myId) : shownField.fieldId === ownFieldId(myId)) : !watchingOther);
|
const ownView = showPrep || (shownField ? (shownMembers ? shownMembers.includes(myId) : shownField.fieldId === ownFieldId(myId)) : !watchingOther);
|
||||||
// a field on a map of its own (联防: the escaped template's map, GitHub #41) shows that map; leaving it restores the board's
|
// every field is fought on the round's battlefield — 联防 too, its terrain, crates and water included (0.2.1, the
|
||||||
const fieldStage = gd.ready && shownField && shownField.stageId && shownField.stageId !== pub?.stageId ? gd.stage(shownField.stageId) : null;
|
// owner's decision of 2026-10-07; 0.2.0 drew the escaped levels' empty road there and restored the board on leaving)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!view) return;
|
if (!view) return;
|
||||||
const st = fieldStage || (ownView ? ownStage : baseStage);
|
const st = ownView ? ownStage : baseStage;
|
||||||
if (st) view.setStage(st);
|
if (st) view.setStage(st);
|
||||||
}, [view, ownView, ownStage, baseStage, fieldStage]);
|
}, [view, ownView, ownStage, baseStage]);
|
||||||
// the stage behind the board ON SCREEN — the one view.setStage was just given: a field's own map (联防, GitHub #41),
|
// the stage behind the board ON SCREEN — the own one (机变 overrides applied) or, while watching a teammate, the plain
|
||||||
// else the own one (机变 overrides applied) or, while watching a teammate, the plain one — and how a tapped BOARD tile
|
// one — and how a tapped BOARD tile maps to it (GitHub issue #184: tileClick → gameLogic.terrainInfo). Everywhere but a
|
||||||
// maps to it (GitHub issue #184: tileClick → gameLogic.terrainInfo). Everywhere but a boss-prep board the two spaces
|
// boss-prep board the two spaces are the same: a 最终攻势 / 隐秘核心 battle renders the stage's own rows (GEO.BOSS_RECT),
|
||||||
// are the same: a 最终攻势 / 隐秘核心 battle renders the stage's own rows (GEO.BOSS_RECT), 联防 / normal rects are
|
// 联防 / normal rects are stage rows; the boss PREP draws the player's half (stage rows 2–5) as board rows 9–12
|
||||||
// stage rows; the boss PREP draws the player's half (stage rows 2–5) as board rows 9–12 (render/prepfield.js toDisp),
|
// (render/prepfield.js toDisp), which is exactly gameLogic.fieldTile.
|
||||||
// which is exactly gameLogic.fieldTile.
|
live.current.terrainStage = ownView ? ownStage : baseStage;
|
||||||
live.current.terrainStage = fieldStage || (ownView ? ownStage : baseStage);
|
|
||||||
live.current.terrainTile = showPrep && (deployField === 'bossL' || deployField === 'bossR')
|
live.current.terrainTile = showPrep && (deployField === 'bossL' || deployField === 'bossR')
|
||||||
? (row, col) => fieldTile(deployField, row, col)
|
? (row, col) => fieldTile(deployField, row, col)
|
||||||
: (row, col) => [row, col];
|
: (row, col) => [row, col];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { PHASE, GEO } from '../../../shared/constants.js';
|
import { PHASE, GEO } from '../../../shared/constants.js';
|
||||||
import { deriveSeed } from '../../sim/rng.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 { FieldRunner, timelineAt, uniteBillBounds } from '../fields.js';
|
||||||
import { uniteLeft } from '../../sim/spec.js';
|
import { uniteLeft } from '../../sim/spec.js';
|
||||||
import { FLOW_TICKER_PRIORITY, DELAYS } from './common.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
|
* Battle options of the 联防 field (helpers' carried end state, the leakers' enemies) on the round's battlefield, its
|
||||||
* template's (unite.js uniteStageId; GitHub #41), the round's stage only when the data lacks it. The field meta and the
|
* terrain, crates, water, devices and runes included (unite.js header; the owner's decision of 2026-10-07 — 0.2.0's
|
||||||
* client-run spec carry that stageId, so every viewer draws the 联防 map.
|
* 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) {
|
_uniteOpts(plan, limit) {
|
||||||
const { wave, players } = uniteBattleOpts(this, plan, limit);
|
const { wave, players } = uniteBattleOpts(this, plan, limit);
|
||||||
@@ -51,7 +52,7 @@ export class MatchUnite {
|
|||||||
kind: 'unite',
|
kind: 'unite',
|
||||||
modeId: this.modeId,
|
modeId: this.modeId,
|
||||||
round: this.round,
|
round: this.round,
|
||||||
stageId: uniteStageId(this.gd, plan.helpers.length) ?? this.stageId,
|
stageId: this.stageId,
|
||||||
rect: { ...GEO.UNITE_RECT },
|
rect: { ...GEO.UNITE_RECT },
|
||||||
timeLimit: limit,
|
timeLimit: limit,
|
||||||
players,
|
players,
|
||||||
|
|||||||
+7
-21
@@ -5,11 +5,13 @@
|
|||||||
// players chosen by most units on the field (downed included) > has an active bond (存疑) > most undowned units, then
|
// 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
|
// 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
|
// 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
|
// left half (colOffset 0); a lone helper plays escaped_single on its own field. The field is the round's battlefield —
|
||||||
// (uniteStageId: data/stages.json act1autochess_escaped_single / _multi, kind 'unite' — two road halves joined at col 10,
|
// the match stage with its terrain, crates, water, devices and runes — opened to both halves (GEO.UNITE_RECT, cols
|
||||||
// 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
|
// 0–20; the stage's right half is its left half + 8 columns), every helper's pieces on their prep tiles
|
||||||
// included), every helper's pieces on their prep tiles ("按休整期位置部署在场"), the right-hand one shifted 8 columns
|
// ("按休整期位置部署在场"), the right-hand one shifted 8 columns (= the stage config's player_map_lr_offset 8): the
|
||||||
// (the official maps' halves, = the stage config's player_map_lr_offset 8). Their operators keep the HP ratio and
|
// 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
|
// 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 }`, "阵地以其当前状态";
|
// 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
|
// 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);
|
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). */
|
/** Battle options for the unite field (without data/logger, added by the match). */
|
||||||
export function uniteBattleOpts(m, plan, timeLimit) {
|
export function uniteBattleOpts(m, plan, timeLimit) {
|
||||||
const wave = buildUniteWave(m.gd, plan.leaked, plan.helpers.length, timeLimit);
|
const wave = buildUniteWave(m.gd, plan.leaked, plan.helpers.length, timeLimit);
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ export class BattleEconomy {
|
|||||||
* whose half of the field it fell on: PRTS 卫戍协议:盟约 决策 "该敌人于对应玩家所属区域倒下时,使相应玩家获得额外资金"
|
* whose half of the field it fell on: PRTS 卫戍协议:盟约 决策 "该敌人于对应玩家所属区域倒下时,使相应玩家获得额外资金"
|
||||||
* [ASSUMED for 联防: no source names the payee of a death nobody caused there]. Until 0.2.0 that fallback was the
|
* [ASSUMED for 联防: no source names the payee of a death nobody caused there]. Until 0.2.0 that fallback was the
|
||||||
* leaker, who has no entry in a 联防 battle, and addCoins dropped the coins (community report of 2026-10-06
|
* leaker, who has no entry in a 联防 battle, and addCoins dropped the coins (community report of 2026-10-06
|
||||||
* 「被源石地板烫死的悬赏没给赏金」: until 0.1.3 the 联防 field was the round's map, 战场#04's 活性源石 included).
|
* 「被源石地板烫死的悬赏没给赏金」: in 0.1.x the 联防 field was the round's map, as it is again since 0.2.1, 战场#04's
|
||||||
|
* 活性源石 included).
|
||||||
*/
|
*/
|
||||||
_bountyPayee(unit, killer = null) {
|
_bountyPayee(unit, killer = null) {
|
||||||
if (killer && killer.side === 'ally' && killer.ownerId != null && this._pp(killer.ownerId)) return killer.ownerId;
|
if (killer && killer.side === 'ally' && killer.ownerId != null && this._pp(killer.ownerId)) return killer.ownerId;
|
||||||
|
|||||||
@@ -96,10 +96,9 @@ export default {
|
|||||||
// PRTS 备注: landing tile > the tile one beyond it > his own tile (a deployable, free, unreserved melee tile).
|
// PRTS 备注: landing tile > the tile one beyond it > his own tile (a deployable, free, unreserved melee tile).
|
||||||
// "可部署" is a tile his player may deploy on: his own board (Battle.onOwnBoard) — never the other half of a 联防 or
|
// "可部署" is a tile his player may deploy on: his own board (Battle.onOwnBoard) — never the other half of a 联防 or
|
||||||
// boss field nor a boss field's hand / 临时整备区 rows; a teammate's half stays closed to him even where the 突袭
|
// boss field nor a boss field's hand / 临时整备区 rows; a teammate's half stays closed to him even where the 突袭
|
||||||
// landing may take it (Battle.onFieldBoard, DESIGN §26.1). Community report of 2026-10-06 (item 27)
|
// landing may take it (Battle.onFieldBoard, DESIGN §26.1). Community report of 2026-10-06 (item 27) 「乌尔比安使用3技能会在
|
||||||
// 「乌尔比安使用3技能会在联防阶段跳到红门后」: on the one-helper 联防 map (escaped_single, enemies from the middle gate at
|
// 联防阶段跳到红门后」: in a one-helper 联防 (escaped_single's enemies come out of the middle gate at col 10) an anchor
|
||||||
// col 10) an anchor that met no enemy flew on to the right half, where no enemy ever walks, and he moved there for
|
// that met no enemy flew on to the right half, where no enemy ever walks, and he moved there for the whole skill
|
||||||
// the whole skill
|
|
||||||
const ok = ([r, c]) => (r !== unit.tileR || c !== unit.tileC) && battle.grid.inRect(r, c) && battle.onOwnBoard(unit.player, r, c) && battle.grid.canStand(r, c) && !battle.grid.isObstacle(r, c) && !battle.isReservedTile(r, c);
|
const ok = ([r, c]) => (r !== unit.tileR || c !== unit.tileC) && battle.grid.inRect(r, c) && battle.onOwnBoard(unit.player, r, c) && battle.grid.canStand(r, c) && !battle.grid.isObstacle(r, c) && !battle.isReservedTile(r, c);
|
||||||
const dest = [[sr, sc], frontOf(unit.tileR, unit.tileC, unit.dir, stop + 1)].find(ok);
|
const dest = [[sr, sc], frontOf(unit.tileR, unit.tileC, unit.dir, stop + 1)].find(ok);
|
||||||
if (dest == null) return;
|
if (dest == null) return;
|
||||||
|
|||||||
+8
-5
@@ -664,7 +664,7 @@ test('stages: player-facing names are clean Chinese "战场#NN…" labels (no re
|
|||||||
// Regression: research 05 named act1 m01 '战场#01 (upper half #01)'; the English note reached the
|
// Regression: research 05 named act1 m01 '战场#01 (upper half #01)'; the English note reached the
|
||||||
// briefing BATTLEFIELD row. Names come from research only, so the build must strip such notes.
|
// briefing BATTLEFIELD row. Names come from research only, so the build must strip such notes.
|
||||||
for (const s of Object.values(stages)) {
|
for (const s of Object.values(stages)) {
|
||||||
if (s.kind === 'unite') continue; // the two 联防 maps: the remake's own label, checked in the 联防 test below
|
if (s.kind === 'unite') continue; // the escaped levels' two maps: the remake's own label, checked in the test below
|
||||||
assert.match(s.name, /^战场#\d{2}(?:\((?:上半|下半)\))?(?: \S.*)?$/, `${s.id}: name ${JSON.stringify(s.name)}`);
|
assert.match(s.name, /^战场#\d{2}(?:\((?:上半|下半)\))?(?: \S.*)?$/, `${s.id}: name ${JSON.stringify(s.name)}`);
|
||||||
assert.doesNotMatch(s.name, /[A-Za-z]/, `${s.id}: Latin text in name ${JSON.stringify(s.name)}`);
|
assert.doesNotMatch(s.name, /[A-Za-z]/, `${s.id}: Latin text in name ${JSON.stringify(s.name)}`);
|
||||||
assert.equal(s.name, s.name.trim().replace(/\s+/g, ' '), `${s.id}: stray whitespace in name`);
|
assert.equal(s.name, s.name.trim().replace(/\s+/g, ' '), `${s.id}: stray whitespace in name`);
|
||||||
@@ -673,8 +673,10 @@ test('stages: player-facing names are clean Chinese "战场#NN…" labels (no re
|
|||||||
assert.equal(stages.act2autochess_m01.name, '战场#05(下半) 源石流发生装置');
|
assert.equal(stages.act2autochess_m01.name, '战场#05(下半) 源石流发生装置');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('stages: the 联防 maps (GitHub #41) — the escaped template levels, one per helper count, never a match stage', () => {
|
test('stages: the escaped levels\' maps — one per helper count, never a match stage (and since 0.2.1 never the 联防 field)', () => {
|
||||||
// act2autochess constData escapedBattleTemplateMapSinglePlayer / MultiPlayer → config.unite.templates { 1, 2 }
|
// act2autochess constData escapedBattleTemplateMapSinglePlayer / MultiPlayer → config.unite.templates { 1, 2 }. 0.2.0
|
||||||
|
// fought the 联防 battle on these maps (GitHub #41); 联防 plays on the round's battlefield again (the owner's decision of
|
||||||
|
// 2026-10-07, test/match/feedback5-unite-map.test.js) and the records stay as the official level data
|
||||||
assert.deepEqual(config.unite.templates, { 1: 'act1autochess_escaped_single', 2: 'act1autochess_escaped_multi' });
|
assert.deepEqual(config.unite.templates, { 1: 'act1autochess_escaped_single', 2: 'act1autochess_escaped_multi' });
|
||||||
for (const [n, id] of Object.entries(config.unite.templates)) {
|
for (const [n, id] of Object.entries(config.unite.templates)) {
|
||||||
const s = stages[id];
|
const s = stages[id];
|
||||||
@@ -688,13 +690,14 @@ test('stages: the 联防 maps (GitHub #41) — the escaped template levels, one
|
|||||||
assert.equal(s.name, `联防阵地(${n}名玩家)`);
|
assert.equal(s.name, `联防阵地(${n}名玩家)`);
|
||||||
assert.deepEqual(s.devices, [], 'no crates, platforms or other devices');
|
assert.deepEqual(s.devices, [], 'no crates, platforms or other devices');
|
||||||
assert.deepEqual(s.special, {}, 'no water, mire, smog or infection');
|
assert.deepEqual(s.special, {}, 'no water, mire, smog or infection');
|
||||||
// the official level map (level_act1autochess_escaped_*.json): two road halves joined at col 10, not the round's stage
|
// the official level map (level_act1autochess_escaped_*.json): two road halves joined at col 10
|
||||||
assert.deepEqual(s.rows.slice(9, 13), ['##ErrrrrrrSrrrrrrrS##', '###rrrrrrr#rrrrrrr###', '###rrrrrrr#rrrrrrr###', '###rrrrrrrSrrrrrrrS##']);
|
assert.deepEqual(s.rows.slice(9, 13), ['##ErrrrrrrSrrrrrrrS##', '###rrrrrrr#rrrrrrr###', '###rrrrrrr#rrrrrrr###', '###rrrrrrrSrrrrrrrS##']);
|
||||||
for (let r = 9; r <= 12; r++) for (const c of [19, 20]) assert.equal(s.tiles[s.rows[r][c]].groundPassable, false, `${id} (${r},${c}) is no ground`);
|
for (let r = 9; r <= 12; r++) for (const c of [19, 20]) assert.equal(s.tiles[s.rows[r][c]].groundPassable, false, `${id} (${r},${c}) is no ground`);
|
||||||
assert.ok(s.groundPaths['9,10->9,2'] && s.groundPaths['9,18->9,2'], 'walkable from both gates');
|
assert.ok(s.groundPaths['9,10->9,2'] && s.groundPaths['9,18->9,2'], 'walkable from both gates');
|
||||||
}
|
}
|
||||||
// the two maps are tile for tile the same: they differ in their routes (data/waves.json — 1 helper enters at col 10,
|
// the two maps are tile for tile the same: they differ in their routes (data/waves.json — 1 helper enters at col 10,
|
||||||
// 2 helpers at col 18 through the checkpoint (9,10))
|
// 2 helpers at col 18 through the checkpoint (9,10)), which the 联防 field follows on the round's stage (its gates
|
||||||
|
// (9,10) / (12,10) and (9,18) / (12,18))
|
||||||
assert.deepEqual(stages.act1autochess_escaped_single.rows, stages.act1autochess_escaped_multi.rows);
|
assert.deepEqual(stages.act1autochess_escaped_single.rows, stages.act1autochess_escaped_multi.rows);
|
||||||
assert.deepEqual(waves.act1autochess_escaped_single.routes.map((r) => r.start), [[9, 10], [9, 10], [12, 10], [9, 10], [9, 10], [9, 10], [9, 10], [9, 10]]);
|
assert.deepEqual(waves.act1autochess_escaped_single.routes.map((r) => r.start), [[9, 10], [9, 10], [12, 10], [9, 10], [9, 10], [9, 10], [9, 10], [9, 10]]);
|
||||||
assert.deepEqual(waves.act1autochess_escaped_multi.routes.map((r) => r.start), [[9, 18], [9, 18], [12, 18], [9, 18], [9, 18], [9, 18], [9, 18], [9, 18]]);
|
assert.deepEqual(waves.act1autochess_escaped_multi.routes.map((r) => r.start), [[9, 18], [9, 18], [12, 18], [9, 18], [9, 18], [9, 18], [9, 18], [9, 18]]);
|
||||||
|
|||||||
@@ -1198,6 +1198,16 @@ describe('browser E2E against the real server', { skip: !ENABLED && 'needs Chrom
|
|||||||
} else if (hs.phase === 'UNITE') {
|
} else if (hs.phase === 'UNITE') {
|
||||||
const rs = await runnerState(host);
|
const rs = await runnerState(host);
|
||||||
assert.ok(rs && rs.kind === 'unite', 'every human simulates the 联防 field locally');
|
assert.ok(rs && rs.kind === 'unite', 'every human simulates the 联防 field locally');
|
||||||
|
// 0.2.1 (the owner's decision of 2026-10-07): the 联防 field is the round's battlefield, terrain and crates
|
||||||
|
// included — the field meta names the match stage and the view draws it (0.2.0 drew the escaped levels' road)
|
||||||
|
for (const c of both) {
|
||||||
|
const drawn = await c.page.evaluate(() => {
|
||||||
|
const s = globalThis.__SP__.store.get();
|
||||||
|
return { match: s.match.public?.stageId ?? null, field: s.match.field?.stageId ?? null, shown: globalThis.__SP_VIEW__?.raw?.debug?.tiles?.stage?.id ?? null };
|
||||||
|
});
|
||||||
|
assert.ok(drawn.match && drawn.field === drawn.match && drawn.shown === drawn.match, `${c.label}: the 联防 field shows the round's battlefield (${JSON.stringify(drawn)})`);
|
||||||
|
c.note(`联防 round ${hs.round} drawn on ${drawn.shown}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (hs.round >= ROUNDS && hs.phase === 'COMBAT') { await sleep(2000); break; }
|
if (hs.round >= ROUNDS && hs.phase === 'COMBAT') { await sleep(2000); break; }
|
||||||
} else if (hs.phase === 'SETTLE' && !seen.has(`settle-${hs.round}`)) {
|
} else if (hs.phase === 'SETTLE' && !seen.has(`settle-${hs.round}`)) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ through.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `roster` | 49 battles: every visible chess record (normal and elite) with every selectable skill and module (DESIGN §16), 12 operators per battle on a real stage (all 11 in turn) against the round's real wave three times over, every non-leader enemy kind of `data/enemies.json` as extra spawns (half of them bounties), placeable summons on the board, every equipment item, band, battle-side 机变 card and stage map card in turn, bonds from the board |
|
| `roster` | 49 battles: every visible chess record (normal and elite) with every selectable skill and module (DESIGN §16), 12 operators per battle on a real stage (all 11 in turn) against the round's real wave three times over, every non-leader enemy kind of `data/enemies.json` as extra spawns (half of them bounties), placeable summons on the board, every equipment item, band, battle-side 机变 card and stage map card in turn, bonds from the board |
|
||||||
| `bonds` | 46 battles: every bond at its activation threshold (1 layer) and at its top tier (999 layers) |
|
| `bonds` | 46 battles: every bond at its activation threshold (1 layer) and at its top tier (999 layers) |
|
||||||
| `fields` | 22 battles: every Final Assault / Hidden Core leader on a pair and a solo template (shared pool, 200 s cap) and the 联防 field with 1 and 2 helpers on its own map (carried HP / SP, a knocked-out operator, two leakers' enemies) |
|
| `fields` | 22 battles: every Final Assault / Hidden Core leader on a pair and a solo template (shared pool, 200 s cap) and the 联防 field with 1 and 2 helpers on the round's stage, both halves (carried HP / SP, a knocked-out operator, two leakers' enemies) |
|
||||||
| `matches` | 18 matches to the end in virtual time: 16 bot-only (solo 标准 / 险境 / 绝境 / 终极 × 2 seeds, co-op 2 / 3 / 4, one server-run combat match, two runs boosted to the Hidden Core), one co-op match whose human seat (AI 托管, offline) does not own 9 NORMAL chess — they fight as their 补位 stand-ins (its digest lists them per round, `standIns`) — and one whose human seat slots 自选 picks (推进之王 and prototypes) with its 调度中心 at level 5 from the first prep: its own shop draws them and its AI fields 推进之王 (its digest lists per round the 自选 shop draws and the fielded pieces, `diy`) |
|
| `matches` | 18 matches to the end in virtual time: 16 bot-only (solo 标准 / 险境 / 绝境 / 终极 × 2 seeds, co-op 2 / 3 / 4, one server-run combat match, two runs boosted to the Hidden Core), one co-op match whose human seat (AI 托管, offline) does not own 9 NORMAL chess — they fight as their 补位 stand-ins (its digest lists them per round, `standIns`) — and one whose human seat slots 自选 picks (推进之王 and prototypes) with its 调度中心 at level 5 from the first prep: its own shop draws them and its AI fields 推进之王 (its digest lists per round the 自选 shop draws and the fielded pieces, `diy`) |
|
||||||
| `standins` | 10 battles: every NORMAL chess record (normal and elite, 110) fielded as its 补位 stand-in (`standIn: true`: the stand-in's body, backup skill / module and kit — all 17 stand-ins, every skill a chess names for them), 12 per battle by strength band, laid out by the stand-in's position on a real stage, against the round's real wave three times over plus 8 ground enemy kinds (melee stand-ins always meet an enemy), an item each, bonds from the board |
|
| `standins` | 10 battles: every NORMAL chess record (normal and elite, 110) fielded as its 补位 stand-in (`standIn: true`: the stand-in's body, backup skill / module and kit — all 17 stand-ins, every skill a chess names for them), 12 per battle by strength band, laid out by the stand-in's position on a real stage, against the round's real wave three times over plus 8 ground enemy kinds (melee stand-ins always meet an enemy), an item each, bonds from the board |
|
||||||
| `diy` | battles of 自选 pieces (a DIY slot with its `diy` pick, `shared/diy.js`): every owned 6★ with an operator kit (`kits/index.js OPERATOR_KITS`) in each form of tiers 5 and 6 (normal; elite with no module and with each module) under each skill, then every prototype pick with a kit at its locked selection, normal and elite, 12 per battle against the round's real wave; a new operator kit adds its scenarios (the battle count grows with the kits) |
|
| `diy` | battles of 自选 pieces (a DIY slot with its `diy` pick, `shared/diy.js`): every owned 6★ with an operator kit (`kits/index.js OPERATOR_KITS`) in each form of tiers 5 and 6 (normal; elite with no module and with each module) under each skill, then every prototype pick with a kit at its locked selection, normal and elite, 12 per battle against the round's real wave; a new operator kit adds its scenarios (the battle count grows with the kits) |
|
||||||
|
|||||||
+44
-39
@@ -1370,7 +1370,7 @@
|
|||||||
"about": "联防 1 helper(s), 11 leaked: chess_char_6_03_b chess_char_6_06_b chess_char_6_07_b chess_char_6_01_b chess_char_6_02_b chess_char_6_04_b chess_char_6_05_b chess_char_6_08_b token_10017_skadi2_dedant",
|
"about": "联防 1 helper(s), 11 leaked: chess_char_6_03_b chess_char_6_06_b chess_char_6_07_b chess_char_6_01_b chess_char_6_02_b chess_char_6_04_b chess_char_6_05_b chess_char_6_08_b token_10017_skadi2_dedant",
|
||||||
"fast": true,
|
"fast": true,
|
||||||
"kind": "unite",
|
"kind": "unite",
|
||||||
"stageId": "act1autochess_escaped_single",
|
"stageId": "act1autochess_m02",
|
||||||
"modeId": "mode_multi_normal",
|
"modeId": "mode_multi_normal",
|
||||||
"round": 7,
|
"round": 7,
|
||||||
"seed": 2621626757,
|
"seed": 2621626757,
|
||||||
@@ -1382,33 +1382,34 @@
|
|||||||
"total": 10,
|
"total": 10,
|
||||||
"unspawned": 0,
|
"unspawned": 0,
|
||||||
"bossHpLeft": null,
|
"bossHpLeft": null,
|
||||||
"rngDraws": 19,
|
"rngDraws": 24,
|
||||||
"errors": {"count":0,"kinds":[]},
|
"errors": {"count":0,"kinds":[]},
|
||||||
"players": {"p1":{"killed":10,"total":10,"perfect":true,"leaks":0,"leaksUncounted":1,"coins":3,"dmg":70502,"bossDmg":0,"heal":4439,"deaths":2,"layerGains":{}}},
|
"players": {"p1":{"killed":10,"total":10,"perfect":true,"leaks":0,"leaksUncounted":1,"coins":3,"dmg":70502,"bossDmg":0,"heal":7551,"deaths":0,"layerGains":{}}},
|
||||||
"units": [
|
"units": [
|
||||||
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",3430,1,2,1,912,0,0,1,0],
|
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",3430,1,2,0,0,0,0,0,0],
|
||||||
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",1616.45,1,1,0,0,0,0,0,0],
|
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",1616.45,1,1,0,0,0,0,0,0],
|
||||||
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",3101,1,22,0,0,0,0,0,0],
|
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",3101,1,23,0,0,0,0,0,0],
|
||||||
["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",2352,1,4,3,16632,0,1646,6,1],
|
["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",2352,1,4,3,17564,0,1646,4,1],
|
||||||
["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",1933,1,7,2,24053,126,512,3,1],
|
["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",1933,1,7,1,7345,0,387,1,1],
|
||||||
["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",333.86,1,23,0,2871,4463,1627,0,3],
|
["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",75.38,1,5,1,632,6392,4869,0,4],
|
||||||
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",1442,1,25,4,26035,1446,653,0,1],
|
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",1442,1,8,4,29637,0,649,0,1],
|
||||||
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,6,0,0,0,0,0,0],
|
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,6,1,15325,0,0,8,1],
|
||||||
["p1",9,"token","token_10017_skadi2_dedant",null,null,100,0,9,0,0,0,0,0,null],
|
["p1",9,"token","token_10017_skadi2_dedant",null,null,100,0,9,0,0,0,0,0,null],
|
||||||
["p1","~1","token","token_10017_skadi2_dedant",null,null,100,0,24,0,0,0,0,0,null],
|
["p1","~1","token","token_10017_skadi2_dedant",null,null,100,0,22,0,0,0,0,0,null],
|
||||||
["p1","~2","token","token_10017_skadi2_dedant",null,null,100,1,26,0,0,0,0,0,null]
|
["p1","~2","token","token_10017_skadi2_dedant",null,null,100,0,24,0,0,0,0,0,null],
|
||||||
|
["p1","~3","token","token_10017_skadi2_dedant",null,null,100,1,25,0,0,0,0,0,null]
|
||||||
],
|
],
|
||||||
"enemies": {"enemy_10028_vtswd":[1,0,0,0,0],"enemy_1158_divman":[4,4,0,322,15206],"enemy_1160_hvyslr":[3,3,0,0,34560],"enemy_1161_tidmag":[3,3,0,1639,20736]},
|
"enemies": {"enemy_10028_vtswd":[1,0,0,0,0],"enemy_1158_divman":[4,4,0,0,15206],"enemy_1160_hvyslr":[3,3,0,0,34560],"enemy_1161_tidmag":[3,3,0,0,20736]},
|
||||||
"hooks": [1,25,5100,20,20,57,129,2358,12,15,6,5,7,9981,13,1,11,0,1,12,0,2,0],
|
"hooks": [1,24,5100,13,13,48,140,3883,10,14,8,8,7,11668,21,0,11,0,1,10,0,1,0],
|
||||||
"events": {"atk":28,"bounty":1,"deploy":14,"die":15,"dmg":78,"engage":3,"fx":57,"skill":11,"spawn":22,"status":43},
|
"events": {"atk":15,"bounty":1,"deploy":13,"die":14,"dmg":60,"engage":3,"fx":58,"skill":16,"spawn":23,"status":65},
|
||||||
"fx": {"bombard":7,"bombardShell":7,"burst":2,"camouflage":1,"lightning":14,"lock":7,"lure":1,"snow":12,"storm":1,"talent":1,"tide":3,"wanted":1},
|
"fx": {"bombard":7,"bombardShell":7,"burst":1,"lightning":15,"lock":7,"lure":1,"scorchBurst":4,"snow":10,"storm":1,"talent":1,"tide":4},
|
||||||
"snaps": "c558a4df 5e12c668 02dad932 00fbc7af bc41cf43 ad41bea1 6e07fbe6 27dde52f 53b9a18c a640dd14 452ad2b3 46658458 8c81ee61 4c70f7fe c0fba143 3b8d81c6 aa0fc004"
|
"snaps": "24e18075 c08e2f71 f5edcfeb 0c324c6b fd360730 254c3c71 8375ccc6 24ac7d99 00239a36 da6ce132 05710646 d34c3d36 a02b720e 749b9c2b a2d13804 15645614 d947c90f"
|
||||||
},
|
},
|
||||||
"unite-2": {
|
"unite-2": {
|
||||||
"about": "联防 2 helper(s), 11 leaked: chess_char_6_15_b chess_char_6_17_b chess_char_6_09_b chess_char_6_11_b chess_char_6_12_b chess_char_6_13_b chess_char_6_14_b chess_char_6_16_b token_10030_mlyss_wtrman | chess_char_6_19_b chess_char_5_01_b chess_char_5_05_b chess_char_5_06_b chess_char_6_18_b chess_char_6_20_b chess_char_5_02_b chess_char_5_03_b",
|
"about": "联防 2 helper(s), 11 leaked: chess_char_6_15_b chess_char_6_17_b chess_char_6_09_b chess_char_6_11_b chess_char_6_12_b chess_char_6_13_b chess_char_6_14_b chess_char_6_16_b token_10030_mlyss_wtrman | chess_char_6_19_b chess_char_5_01_b chess_char_5_05_b chess_char_5_06_b chess_char_6_18_b chess_char_6_20_b chess_char_5_02_b chess_char_5_03_b",
|
||||||
"fast": true,
|
"fast": true,
|
||||||
"kind": "unite",
|
"kind": "unite",
|
||||||
"stageId": "act1autochess_escaped_multi",
|
"stageId": "act1autochess_m07",
|
||||||
"modeId": "mode_multi_hard",
|
"modeId": "mode_multi_hard",
|
||||||
"round": 11,
|
"round": 11,
|
||||||
"seed": 1995382690,
|
"seed": 1995382690,
|
||||||
@@ -1427,30 +1428,34 @@
|
|||||||
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":10528,"bossDmg":0,"heal":9524,"deaths":0,"layerGains":{}}
|
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":10528,"bossDmg":0,"heal":9524,"deaths":0,"layerGains":{}}
|
||||||
},
|
},
|
||||||
"units": [
|
"units": [
|
||||||
["p1",1,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",4608,1,32,2,33012,4080,0,21,1],
|
["p1",1,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",4608,1,36,2,33012,4080,0,21,1],
|
||||||
["p1",2,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",3593,1,35,0,27083,4928,0,17,1],
|
["p1",2,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",3593,1,39,0,27083,4928,0,17,1],
|
||||||
["p1",3,"op","chess_char_6_09_b",0,"uniequip_002_cello",1322,1,33,0,0,0,0,0,0],
|
["p1",3,"op","chess_char_6_09_b",0,"uniequip_002_cello",1322,1,37,0,0,0,0,0,0],
|
||||||
["p1",4,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,9,0,0,0,0,0,0],
|
["p1",4,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,13,0,0,0,0,0,0],
|
||||||
["p1",5,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",2944.54,1,12,6,72628,0,0,15,1],
|
["p1",5,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",2944.54,1,16,6,72628,0,0,15,1],
|
||||||
["p1",6,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2192.85,1,14,0,0,5746,0,0,0],
|
["p1",6,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2192.85,1,18,0,0,5746,0,0,0],
|
||||||
["p1",7,"op","chess_char_6_14_b",2,"uniequip_002_lumen",3323.25,1,16,0,0,0,5334,72,1],
|
["p1",7,"op","chess_char_6_14_b",2,"uniequip_002_lumen",3323.25,1,20,0,0,0,5334,72,1],
|
||||||
["p1",8,"op","chess_char_6_16_b",2,"uniequip_002_halo2",1439,1,7,0,4962,0,0,5,1],
|
["p1",8,"op","chess_char_6_16_b",2,"uniequip_002_halo2",1439,1,11,0,4962,0,0,5,1],
|
||||||
["p1",9,"token","token_10030_mlyss_wtrman",null,null,2062,1,10,0,0,0,0,0,1],
|
["p1",9,"token","token_10030_mlyss_wtrman",null,null,2062,1,14,0,0,0,0,0,1],
|
||||||
["p2",10,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",2767,1,2,0,0,0,0,0,0],
|
["p2",10,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",2767,1,6,0,0,0,0,0,0],
|
||||||
["p2",11,"op","chess_char_5_01_b",1,"uniequip_002_excu2",1272.7,1,6,0,0,0,0,0,0],
|
["p2",11,"op","chess_char_5_01_b",1,"uniequip_002_excu2",1272.7,1,10,0,0,0,0,0,0],
|
||||||
["p2",12,"op","chess_char_5_05_b",2,"uniequip_002_ulpia",5905,1,34,0,0,0,0,0,0],
|
["p2",12,"op","chess_char_5_05_b",2,"uniequip_002_ulpia",5905,1,38,0,0,0,0,0,0],
|
||||||
["p2",13,"op","chess_char_5_06_b",2,"uniequip_002_etlchi",776.63,1,15,0,0,0,0,0,0],
|
["p2",13,"op","chess_char_5_06_b",2,"uniequip_002_etlchi",776.63,1,19,0,0,0,0,0,0],
|
||||||
["p2",14,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1541,1,4,1,10528,0,0,0,1],
|
["p2",14,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1541,1,8,1,10528,0,0,0,1],
|
||||||
["p2",15,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1631.88,1,8,0,0,0,9524,25,2],
|
["p2",15,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1631.88,1,12,0,0,0,9524,25,2],
|
||||||
["p2",16,"op","chess_char_5_02_b",2,"uniequip_002_titi",1554.12,1,13,0,0,0,0,0,0],
|
["p2",16,"op","chess_char_5_02_b",2,"uniequip_002_titi",1554.12,1,17,0,0,0,0,0,0],
|
||||||
["p2",17,"op","chess_char_5_03_b",2,"uniequip_002_blaze2",1589.76,1,17,0,0,0,0,0,0],
|
["p2",17,"op","chess_char_5_03_b",2,"uniequip_002_blaze2",1589.76,1,21,0,0,0,0,0,0],
|
||||||
["p1","~1","token","token_10019_nearl2_sword",null,null,5441.59,0,31,1,27960,558,0,7,null]
|
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||||
|
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||||
|
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null],
|
||||||
|
["-","~4","device","trap_1105_accrate",null,null,100,1,4,0,0,0,0,0,null],
|
||||||
|
["p1","~1","token","token_10019_nearl2_sword",null,null,5441.59,0,35,1,27960,558,0,7,null]
|
||||||
],
|
],
|
||||||
"enemies": {"enemy_10029_vtshld":[1,0,0,0,0],"enemy_1249_lysdb_2":[3,3,0,1139,53748],"enemy_1251_lysyta":[4,4,0,0,32846],"enemy_1329_cbshld":[3,3,0,8428,89580]},
|
"enemies": {"enemy_10029_vtshld":[1,0,0,0,0],"enemy_1249_lysdb_2":[3,3,0,1139,53748],"enemy_1251_lysyta":[4,4,0,0,32846],"enemy_1329_cbshld":[3,3,0,8428,89580]},
|
||||||
"hooks": [1,34,6900,175,175,310,536,506,11,16,9,7,0,19291,418,5,11,0,1,11,0,0,0],
|
"hooks": [1,38,6900,175,175,310,536,506,11,16,9,7,0,19291,418,5,11,0,1,11,0,0,0],
|
||||||
"events": {"atk":275,"bounty":1,"deploy":23,"die":16,"dmg":307,"engage":5,"fx":166,"heal":197,"skill":16,"spawn":29,"status":87},
|
"events": {"atk":275,"bounty":1,"deploy":23,"die":16,"dmg":307,"engage":5,"fx":166,"heal":197,"skill":16,"spawn":33,"status":87},
|
||||||
"fx": {"blink":2,"copy":1,"drone":141,"droneLock":9,"drones":2,"garrisonGrant":1,"link":1,"radiantSword":1,"shieldBreak":3,"summon":1,"sunBurst":1,"talent":3},
|
"fx": {"blink":2,"copy":1,"drone":141,"droneLock":9,"drones":2,"garrisonGrant":1,"link":1,"radiantSword":1,"shieldBreak":3,"summon":1,"sunBurst":1,"talent":3},
|
||||||
"snaps": "87a560e8 8cf58762 c766f850 d32294f6 295e925c 44a2d0b0 80ea41cf 5b4cd7c2 993fdc2e 3171686a fab27835 978a7b48 e45c0811 5a5f2ed3 3d500d80 addffc1a f295f658 ae43f351 436ae4d0 214a93f0 1b8bc39d 44a2436b e448417b"
|
"snaps": "c4705ca6 7ac5e1f4 7b93c781 1a4d420a c1ddfa30 66889e17 58d20bb6 0a8d750d f2fed186 eada2980 a257e8a5 66c38c0d 3ab9a260 3462b6ee 9abc9798 5949be5e 691d70b6 ba404db0 9eb04453 328af32e a81e9442 4c690478 bcda8066"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+87
-87
@@ -516,7 +516,7 @@
|
|||||||
"combat": "client",
|
"combat": "client",
|
||||||
"boost": null,
|
"boost": null,
|
||||||
"setup": {"stageId":"act2autochess_m04","bossId":"boss_3","hiddenBossId":"boss_8","factions":["FLY","TIMES","DOT"]},
|
"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":46,"bossPoolLeft":0,"virtualMs":118400},
|
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":56,"bossPoolLeft":0,"virtualMs":118400},
|
||||||
"rng": {"setup":25,"shop":456,"waves":0,"draft":40,"bots":21608,"meta":4},
|
"rng": {"setup":25,"shop":456,"waves":0,"draft":40,"bots":21608,"meta":4},
|
||||||
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
||||||
"rounds": [
|
"rounds": [
|
||||||
@@ -582,7 +582,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 13,
|
"round": 13,
|
||||||
"ai_0": [1,27,12,0,0,6,8,"f3619a79",269,"arcaneShip 2/43, egirShip 1/124, preciShip 1/17, raidShip 0/7, sargonShip 0/10, siracusaShip 1/44, skillfulShip 0/8, soloShip 1/6, steadShip 1/0, suntShip 1/0, swiftShip 1/35",13,34,21,126],
|
"ai_0": [1,37,12,0,0,6,8,"f3619a79",269,"arcaneShip 2/43, egirShip 1/124, preciShip 1/17, raidShip 0/7, sargonShip 0/10, siracusaShip 1/44, skillfulShip 0/8, soloShip 1/6, steadShip 1/0, suntShip 1/0, swiftShip 1/35",13,34,21,126],
|
||||||
"ai_1": [1,19,12,0,0,6,8,"187f8019",183,"arcaneShip 2/28, indomShip 0/2, kazimierzShip 1/53, kjeragShip 0/2, preciShip 2/102, sargonShip 0/10, steadShip 0/6, suntShip 1/0, swiftShip 0/11",34,34,0,123]
|
"ai_1": [1,19,12,0,0,6,8,"187f8019",183,"arcaneShip 2/28, indomShip 0/2, kazimierzShip 1/53, kjeragShip 0/2, preciShip 2/102, sargonShip 0/10, steadShip 0/6, suntShip 1/0, swiftShip 0/11",34,34,0,123]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -592,12 +592,12 @@
|
|||||||
"victory": true,
|
"victory": true,
|
||||||
"roundsPassed": 14,
|
"roundsPassed": 14,
|
||||||
"eliminatedRound": null,
|
"eliminatedRound": null,
|
||||||
"lp": 27,
|
"lp": 37,
|
||||||
"bandId": "band_sarkazb",
|
"bandId": "band_sarkazb",
|
||||||
"title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"},
|
"title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"},
|
||||||
"trophies": 4,
|
"trophies": 4,
|
||||||
"reward": 400,
|
"reward": 400,
|
||||||
"stats": {"activatedLayers":303,"bossDamage":283279,"dmgDealt":2679730,"gold":138,"itemsEquipped":17,"kills":310,"leaks":32,"lpLost":18,"merges":3,"perfectRounds":9,"refreshes":23},
|
"stats": {"activatedLayers":303,"bossDamage":283279,"dmgDealt":2679730,"gold":138,"itemsEquipped":17,"kills":310,"leaks":32,"lpLost":8,"merges":3,"perfectRounds":9,"refreshes":23},
|
||||||
"lineup": ["1_17_a@11,7","4_14_a@11,8[3_01_e_a+4_08_e_a]","4_10_b@10,5[6_09_e_a+1_02_e_b]","3_18_a@10,7[4_05_e_a+3_05_e_a]","5_13_a@10,8[4_06_e_a]","3_05_a@9,4","3_09_a@9,8[1_05_e_a+4_07_e_a]","1_08_b@9,9[4_03_e_a+4_04_e_a]"],
|
"lineup": ["1_17_a@11,7","4_14_a@11,8[3_01_e_a+4_08_e_a]","4_10_b@10,5[6_09_e_a+1_02_e_b]","3_18_a@10,7[4_05_e_a+3_05_e_a]","5_13_a@10,8[4_06_e_a]","3_05_a@9,4","3_09_a@9,8[1_05_e_a+4_07_e_a]","1_08_b@9,9[4_03_e_a+4_04_e_a]"],
|
||||||
"bonds": ["arcaneShip 2/55","egirShip 1/124","preciShip 1/22","raidShip 0/7","sargonShip 0/10","siracusaShip 1/56","skillfulShip 0/8","soloShip 1/6","steadShip 1/0","suntShip 1/0","swiftShip 1/40"]
|
"bonds": ["arcaneShip 2/55","egirShip 1/124","preciShip 1/22","raidShip 0/7","sargonShip 0/10","siracusaShip 1/56","skillfulShip 0/8","soloShip 1/6","steadShip 1/0","suntShip 1/0","swiftShip 1/40"]
|
||||||
},
|
},
|
||||||
@@ -611,7 +611,7 @@
|
|||||||
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
||||||
"trophies": 4,
|
"trophies": 4,
|
||||||
"reward": 400,
|
"reward": 400,
|
||||||
"stats": {"activatedLayers":185,"bossDamage":1134221,"dmgDealt":4214714,"gold":135,"itemsEquipped":19,"kills":324,"leaks":9,"lpLost":9,"merges":4,"perfectRounds":11,"refreshes":14},
|
"stats": {"activatedLayers":185,"bossDamage":1134221,"dmgDealt":4858908,"gold":135,"itemsEquipped":19,"kills":344,"leaks":9,"lpLost":9,"merges":4,"perfectRounds":11,"refreshes":14},
|
||||||
"lineup": ["6_08_a@12,8","1_17_b@11,7[3_04_e_a+4_08_e_b]","4_20_a@11,8[5_02_e_a+4_04_e_a]","4_10_a@10,5[1_02_e_b+4_10_e_a]","6_17_a@10,8","6_08_a@9,7[4_07_e_a+3_01_e_a]","4_02_a@9,8","1_19_b@9,9[2_04_e_a+1_05_e_b]"],
|
"lineup": ["6_08_a@12,8","1_17_b@11,7[3_04_e_a+4_08_e_b]","4_20_a@11,8[5_02_e_a+4_04_e_a]","4_10_a@10,5[1_02_e_b+4_10_e_a]","6_17_a@10,8","6_08_a@9,7[4_07_e_a+3_01_e_a]","4_02_a@9,8","1_19_b@9,9[2_04_e_a+1_05_e_b]"],
|
||||||
"bonds": ["arcaneShip 2/30","indomShip 0/2","kazimierzShip 1/53","kjeragShip 0/2","preciShip 2/102","sargonShip 0/10","steadShip 0/6","suntShip 1/0","swiftShip 0/11"]
|
"bonds": ["arcaneShip 2/30","indomShip 0/2","kazimierzShip 1/53","kjeragShip 0/2","preciShip 2/102","sargonShip 0/10","steadShip 0/6","suntShip 1/0","swiftShip 0/11"]
|
||||||
}
|
}
|
||||||
@@ -708,7 +708,7 @@
|
|||||||
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
|
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
|
||||||
"trophies": 2,
|
"trophies": 2,
|
||||||
"reward": 255,
|
"reward": 255,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1458318,"gold":113,"itemsEquipped":10,"kills":161,"leaks":72,"lpLost":28,"merges":2,"perfectRounds":5,"refreshes":9},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1461472,"gold":113,"itemsEquipped":10,"kills":161,"leaks":72,"lpLost":28,"merges":2,"perfectRounds":5,"refreshes":9},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["deputShip 0/2","indomShip 0/8","kazimierzShip 0/2","kjeragShip 0/2","raidShip 0/2","siracusaShip 0/32","skillfulShip 0/12","soloShip 0/5","steadShip 0/14","suntShip 0/2","swiftShip 0/12"]
|
"bonds": ["deputShip 0/2","indomShip 0/8","kazimierzShip 0/2","kjeragShip 0/2","raidShip 0/2","siracusaShip 0/32","skillfulShip 0/12","soloShip 0/5","steadShip 0/14","suntShip 0/2","swiftShip 0/12"]
|
||||||
},
|
},
|
||||||
@@ -722,7 +722,7 @@
|
|||||||
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
||||||
"trophies": 3,
|
"trophies": 3,
|
||||||
"reward": 298,
|
"reward": 298,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3670137,"gold":136,"itemsEquipped":11,"kills":257,"leaks":40,"lpLost":31,"merges":4,"perfectRounds":8,"refreshes":22},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3663883,"gold":136,"itemsEquipped":11,"kills":257,"leaks":40,"lpLost":31,"merges":4,"perfectRounds":8,"refreshes":22},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["arcaneShip 0/40","kazimierzShip 0/2","preciShip 0/84","sargonShip 0/80","skillfulShip 0/14","swiftShip 0/10"]
|
"bonds": ["arcaneShip 0/40","kazimierzShip 0/2","preciShip 0/84","sargonShip 0/80","skillfulShip 0/14","swiftShip 0/10"]
|
||||||
}
|
}
|
||||||
@@ -762,37 +762,37 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 4,
|
"round": 4,
|
||||||
"ai_0": [1,22,8,0,1,2,8,"248ea0fd",12,"deputShip 1/8, kazimierzShip 0/2, swiftShip 1/4",13,14,1,23],
|
"ai_0": [1,23,8,0,1,2,8,"248ea0fd",12,"deputShip 1/8, kazimierzShip 0/2, swiftShip 1/4",13,14,1,23],
|
||||||
"ai_1": [1,43,9,0,2,2,8,"4c004aa4",16,"deputShip 1/2, kazimierzShip 0/7, swiftShip 1/14",7,12,5,24],
|
"ai_1": [1,43,9,0,2,2,8,"4c004aa4",16,"deputShip 1/2, kazimierzShip 0/7, swiftShip 1/14",7,12,5,24],
|
||||||
"ai_2": [1,27,8,0,1,2,8,"e66e483b",6,"indomShip 1/2, siracusaShip 1/0, soloShip 1/4, yanShip 0/1",12,12,0,23]
|
"ai_2": [1,27,8,0,1,2,8,"e66e483b",6,"indomShip 1/2, siracusaShip 1/0, soloShip 1/4, yanShip 0/1",12,12,0,23]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 5,
|
"round": 5,
|
||||||
"ai_0": [1,22,9,0,0,3,8,"61eb7d90",20,"deputShip 1/12, kazimierzShip 0/2, soloShip 1/2, swiftShip 1/6",53,53,0,32],
|
"ai_0": [1,23,9,0,0,3,8,"61eb7d90",20,"deputShip 1/12, kazimierzShip 0/2, soloShip 1/2, swiftShip 1/6",53,53,0,32],
|
||||||
"ai_1": [1,43,10,0,0,3,8,"b4417f25",27,"deputShip 2/6, kazimierzShip 0/7, swiftShip 1/21",53,53,0,34],
|
"ai_1": [1,43,10,0,0,3,8,"b4417f25",27,"deputShip 2/6, kazimierzShip 0/7, swiftShip 1/21",53,53,0,34],
|
||||||
"ai_2": [1,27,9,0,2,3,8,"a15e1321",10,"indomShip 1/4, siracusaShip 1/0, soloShip 1/6, yanShip 0/1",53,53,0,31]
|
"ai_2": [1,27,9,0,2,3,8,"a15e1321",10,"indomShip 1/4, siracusaShip 1/0, soloShip 1/6, yanShip 0/1",53,53,0,31]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 6,
|
"round": 6,
|
||||||
"ai_0": [1,22,9,0,1,3,8,"4faaf967",44,"deputShip 2/20, kazimierzShip 0/2, lateranoShip 1/7, skillfulShip 1/7, soloShip 0/2, swiftShip 1/10",23,26,3,42],
|
"ai_0": [1,23,9,0,1,3,8,"4faaf967",44,"deputShip 2/20, kazimierzShip 0/2, lateranoShip 1/7, skillfulShip 1/7, soloShip 0/2, swiftShip 1/10",23,26,3,42],
|
||||||
"ai_1": [1,43,9,0,0,3,8,"85c81f35",46,"deputShip 1/8, kazimierzShip 0/7, siracusaShip 1/8, soloShip 1/2, suntShip 1/2, swiftShip 1/26",26,26,0,43],
|
"ai_1": [1,43,9,0,0,3,8,"85c81f35",46,"deputShip 1/8, kazimierzShip 0/7, siracusaShip 1/8, soloShip 1/2, suntShip 1/2, swiftShip 1/26",26,26,0,43],
|
||||||
"ai_2": [1,27,11,0,2,3,8,"fa398791",18,"indomShip 1/8, miraShip 1/0, siracusaShip 1/4, soloShip 1/6, suntShip 1/0, yanShip 0/1",12,26,14,43]
|
"ai_2": [1,27,11,0,2,3,8,"fa398791",18,"indomShip 1/8, miraShip 1/0, siracusaShip 1/4, soloShip 1/6, suntShip 1/0, yanShip 0/1",12,26,14,43]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 7,
|
"round": 7,
|
||||||
"ai_0": [1,22,11,0,0,4,8,"0ae8946b",78,"deputShip 2/28, kazimierzShip 0/4, lateranoShip 1/17, skillfulShip 1/17, soloShip 0/2, swiftShip 1/16",22,25,3,55],
|
"ai_0": [1,23,11,0,0,4,8,"0ae8946b",78,"deputShip 2/28, kazimierzShip 0/4, lateranoShip 1/17, skillfulShip 1/17, soloShip 0/2, swiftShip 1/16",22,25,3,55],
|
||||||
"ai_1": [1,43,10,0,0,4,8,"bf120dea",65,"deputShip 1/10, kazimierzShip 0/7, siracusaShip 1/16, soloShip 1/4, suntShip 1/4, swiftShip 1/31",25,25,0,52],
|
"ai_1": [1,43,10,0,0,4,8,"bf120dea",65,"deputShip 1/10, kazimierzShip 0/7, siracusaShip 1/16, soloShip 1/4, suntShip 1/4, swiftShip 1/31",25,25,0,52],
|
||||||
"ai_2": [1,27,12,0,0,4,8,"162f421b",31,"deputShip 1/2, indomShip 0/8, miraShip 1/5, siracusaShip 1/12, soloShip 1/10, suntShip 1/2, yanShip 0/1",22,25,3,55]
|
"ai_2": [1,27,12,0,0,4,8,"162f421b",31,"deputShip 1/2, indomShip 0/8, miraShip 1/5, siracusaShip 1/12, soloShip 1/10, suntShip 1/2, yanShip 0/1",22,25,3,55]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 8,
|
"round": 8,
|
||||||
"ai_0": [1,12,11,0,0,4,8,"f1f06cea",104,"deputShip 2/39, kazimierzShip 0/4, lateranoShip 1/24, skillfulShip 1/27, soloShip 1/6, steadShip 1/8, swiftShip 0/16",10,20,10,66],
|
"ai_0": [1,13,11,0,0,4,8,"f1f06cea",104,"deputShip 2/39, kazimierzShip 0/4, lateranoShip 1/24, skillfulShip 1/27, soloShip 1/6, steadShip 1/8, swiftShip 0/16",10,20,10,66],
|
||||||
"ai_1": [1,33,11,0,0,4,8,"e84c71c4",84,"deputShip 1/12, kazimierzShip 0/7, raidShip 1/7, siracusaShip 1/18, soloShip 0/4, suntShip 1/6, swiftShip 1/41",6,20,14,63],
|
"ai_1": [1,33,11,0,0,4,8,"e84c71c4",84,"deputShip 1/12, kazimierzShip 0/7, raidShip 1/7, siracusaShip 1/18, soloShip 0/4, suntShip 1/6, swiftShip 1/41",6,20,14,63],
|
||||||
"ai_2": [1,17,11,0,4,4,8,"7904d229",68,"deputShip 1/4, indomShip 0/8, miraShip 1/7, siracusaShip 1/32, skillfulShip 1/2, soloShip 1/12, suntShip 1/4, swiftShip 1/7, yanShip 0/1",7,20,13,67]
|
"ai_2": [1,17,11,0,4,4,8,"7904d229",68,"deputShip 1/4, indomShip 0/8, miraShip 1/7, siracusaShip 1/32, skillfulShip 1/2, soloShip 1/12, suntShip 1/4, swiftShip 1/7, yanShip 0/1",7,20,13,67]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 9,
|
"round": 9,
|
||||||
"ai_0": [1,2,12,0,1,4,8,"7e93ff85",153,"deputShip 1/45, emptyShip 1/2, kazimierzShip 0/4, lateranoShip 1/29, skillfulShip 1/35, soloShip 1/8, steadShip 1/14, suntShip 1/2, swiftShip 1/18",48,58,10,78],
|
"ai_0": [1,3,12,0,1,4,8,"7e93ff85",153,"deputShip 1/45, emptyShip 1/2, kazimierzShip 0/4, lateranoShip 1/29, skillfulShip 1/35, soloShip 1/8, steadShip 1/14, suntShip 1/2, swiftShip 1/18",48,58,10,78],
|
||||||
"ai_1": [1,29,12,0,1,4,8,"1c039dc7",113,"deputShip 1/14, kazimierzShip 0/7, raidShip 1/14, siracusaShip 1/26, soloShip 0/4, suntShip 1/8, swiftShip 1/51",56,60,4,76],
|
"ai_1": [1,29,12,0,1,4,8,"1c039dc7",113,"deputShip 1/14, kazimierzShip 0/7, raidShip 1/14, siracusaShip 1/26, soloShip 0/4, suntShip 1/8, swiftShip 1/51",56,60,4,76],
|
||||||
"ai_2": [1,9,16,0,0,4,8,"a9a38e4e",84,"deputShip 0/4, indomShip 0/8, miraShip 1/10, siracusaShip 2/58, skillfulShip 0/4, soloShip 1/12, suntShip 1/4, swiftShip 0/9, yanShip 0/1",41,49,8,82]
|
"ai_2": [1,9,16,0,0,4,8,"a9a38e4e",84,"deputShip 0/4, indomShip 0/8, miraShip 1/10, siracusaShip 2/58, skillfulShip 0/4, soloShip 1/12, suntShip 1/4, swiftShip 0/9, yanShip 0/1",41,49,8,82]
|
||||||
},
|
},
|
||||||
@@ -826,7 +826,7 @@
|
|||||||
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
|
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
|
||||||
"trophies": 2,
|
"trophies": 2,
|
||||||
"reward": 191,
|
"reward": 191,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1395734,"gold":91,"itemsEquipped":6,"kills":212,"leaks":37,"lpLost":31,"merges":2,"perfectRounds":4,"refreshes":11},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1395734,"gold":91,"itemsEquipped":6,"kills":212,"leaks":37,"lpLost":30,"merges":2,"perfectRounds":4,"refreshes":11},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["arcaneShip 0/4","deputShip 0/48","emptyShip 0/5","kazimierzShip 0/4","lateranoShip 0/36","skillfulShip 0/38","soloShip 0/8","steadShip 0/18","suntShip 0/2","swiftShip 0/23"]
|
"bonds": ["arcaneShip 0/4","deputShip 0/48","emptyShip 0/5","kazimierzShip 0/4","lateranoShip 0/36","skillfulShip 0/38","soloShip 0/8","steadShip 0/18","suntShip 0/2","swiftShip 0/23"]
|
||||||
},
|
},
|
||||||
@@ -854,7 +854,7 @@
|
|||||||
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
||||||
"trophies": 2,
|
"trophies": 2,
|
||||||
"reward": 213,
|
"reward": 213,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1840422,"gold":105,"itemsEquipped":12,"kills":180,"leaks":72,"lpLost":36,"merges":4,"perfectRounds":3,"refreshes":14},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1845779,"gold":105,"itemsEquipped":12,"kills":181,"leaks":72,"lpLost":36,"merges":4,"perfectRounds":3,"refreshes":14},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["deputShip 0/4","indomShip 0/8","miraShip 0/12","siracusaShip 0/84","skillfulShip 0/4","soloShip 0/12","suntShip 0/4","swiftShip 0/16","yanShip 0/1"]
|
"bonds": ["deputShip 0/4","indomShip 0/8","miraShip 0/12","siracusaShip 0/84","skillfulShip 0/4","soloShip 0/12","suntShip 0/4","swiftShip 0/16","yanShip 0/1"]
|
||||||
}
|
}
|
||||||
@@ -1036,84 +1036,84 @@
|
|||||||
"boost": null,
|
"boost": null,
|
||||||
"setup": {"stageId":"act2autochess_m01","bossId":"boss_1","hiddenBossId":"boss_8","factions":["DOT","INVISIBLE","REFLECTION"]},
|
"setup": {"stageId":"act2autochess_m01","bossId":"boss_1","hiddenBossId":"boss_8","factions":["DOT","INVISIBLE","REFLECTION"]},
|
||||||
"end": {"victory":false,"roundsPassed":10,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":111300},
|
"end": {"victory":false,"roundsPassed":10,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":111300},
|
||||||
"rng": {"setup":25,"shop":495,"waves":0,"draft":56,"bots":18468,"meta":57},
|
"rng": {"setup":25,"shop":471,"waves":0,"draft":56,"bots":18450,"meta":59},
|
||||||
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
||||||
"rounds": [
|
"rounds": [
|
||||||
{
|
{
|
||||||
"round": 1,
|
"round": 1,
|
||||||
"ai_0": [1,45,4,0,0,1,2,"cf5de4b4",0,"kazimierzShip 0/2, swiftShip 0/2",5,5,0,4],
|
"ai_0": [1,45,4,0,0,1,2,"cf5de4b4",0,"kazimierzShip 0/2, swiftShip 0/2",5,5,0,4],
|
||||||
"ai_1": [1,26,4,0,0,1,2,"59374416",0,"kazimierzShip 0/2, sargonShip 0/2, swiftShip 0/2",5,5,0,4],
|
"ai_1": [1,26,4,0,0,1,2,"59374416",0,"kazimierzShip 0/2, sargonShip 0/2, swiftShip 0/2",5,5,0,4],
|
||||||
"ai_2": [1,25,4,0,0,1,2,"eb0c1458",0,"deputShip 0/2, victoriaShip 0/2",2,5,3,4],
|
"ai_2": [1,28,4,0,0,1,2,"eb0c1458",0,"deputShip 0/2, victoriaShip 0/2",2,5,3,4],
|
||||||
"ai_3": [1,26,4,0,0,1,2,"ba03322a",0,"soloShip 1/0",5,5,0,4]
|
"ai_3": [1,26,4,0,0,1,2,"ba03322a",0,"soloShip 1/0",5,5,0,4]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 2,
|
"round": 2,
|
||||||
"ai_0": [1,45,5,0,0,1,4,"62de86cd",4,"kazimierzShip 0/4, swiftShip 1/4",8,8,0,9],
|
"ai_0": [1,45,5,0,0,1,4,"62de86cd",4,"kazimierzShip 0/4, swiftShip 1/4",8,8,0,9],
|
||||||
"ai_1": [1,26,5,0,0,1,4,"a2ec5116",5,"kazimierzShip 0/5, sargonShip 0/2, soloShip 1/0, swiftShip 1/5",8,8,0,9],
|
"ai_1": [1,26,5,0,0,1,4,"a2ec5116",5,"kazimierzShip 0/5, sargonShip 0/2, soloShip 1/0, swiftShip 1/5",8,8,0,9],
|
||||||
"ai_2": [1,24,5,0,0,1,3,"70d74d61",0,"deputShip 0/4, kjeragShip 0/2, soloShip 1/0, victoriaShip 0/6",7,8,1,9],
|
"ai_2": [1,28,5,0,0,1,3,"70d74d61",0,"deputShip 0/4, kjeragShip 0/2, soloShip 1/0, victoriaShip 0/6",7,8,1,9],
|
||||||
"ai_3": [1,26,5,0,0,1,4,"9e98d014",0,"sargonShip 0/2, soloShip 1/0",8,8,0,9]
|
"ai_3": [1,26,5,0,0,1,4,"9e98d014",0,"sargonShip 0/2, soloShip 1/0",8,8,0,9]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 3,
|
"round": 3,
|
||||||
"ai_0": [1,45,6,0,1,1,7,"a7d37d87",4,"arcaneShip 0/2, kazimierzShip 0/4, preciShip 0/2, swiftShip 1/4",9,9,0,15],
|
"ai_0": [1,45,6,0,1,1,7,"a7d37d87",4,"arcaneShip 0/2, kazimierzShip 0/4, preciShip 0/2, swiftShip 1/4",9,9,0,15],
|
||||||
"ai_1": [1,26,6,0,1,1,7,"bf8932c2",5,"kazimierzShip 0/5, kjeragShip 0/2, sargonShip 0/4, soloShip 1/0, steadShip 0/2, swiftShip 1/5",11,11,0,15],
|
"ai_1": [1,26,6,0,1,1,7,"bf8932c2",5,"kazimierzShip 0/5, kjeragShip 0/2, sargonShip 0/4, soloShip 1/0, steadShip 0/2, swiftShip 1/5",11,11,0,15],
|
||||||
"ai_2": [1,24,6,0,1,1,6,"7363c3d8",0,"deputShip 0/4, kazimierzShip 0/2, kjeragShip 0/8, soloShip 1/0, steadShip 0/4, swiftShip 0/2, victoriaShip 0/8, yanShip 0/1",9,9,0,15],
|
"ai_2": [1,28,6,0,1,1,6,"7363c3d8",0,"deputShip 0/4, kazimierzShip 0/2, kjeragShip 0/8, soloShip 1/0, steadShip 0/4, swiftShip 0/2, victoriaShip 0/8, yanShip 0/1",9,9,0,15],
|
||||||
"ai_3": [1,26,6,0,2,1,7,"5051466d",1,"arcaneShip 0/2, preciShip 0/2, sargonShip 0/2, soloShip 1/1",9,9,0,15]
|
"ai_3": [1,26,6,0,2,1,7,"5051466d",1,"arcaneShip 0/2, preciShip 0/2, sargonShip 0/2, soloShip 1/1",9,9,0,15]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 4,
|
"round": 4,
|
||||||
"ai_0": [1,45,8,0,1,2,8,"822a03c7",6,"arcaneShip 0/8, kazimierzShip 0/4, preciShip 0/8, swiftShip 1/6",10,10,0,23],
|
"ai_0": [1,45,8,0,3,2,8,"822a03c7",6,"arcaneShip 0/8, kazimierzShip 0/4, preciShip 0/8, swiftShip 1/6",10,10,0,23],
|
||||||
"ai_1": [1,26,8,0,1,2,8,"c29eb727",12,"arcaneShip 1/2, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/2, sargonShip 0/4, soloShip 1/0, steadShip 1/2, swiftShip 1/8",12,12,0,23],
|
"ai_1": [1,26,8,0,1,2,8,"c29eb727",12,"arcaneShip 1/2, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/2, sargonShip 0/4, soloShip 1/0, steadShip 1/2, swiftShip 1/8",12,12,0,23],
|
||||||
"ai_2": [1,24,8,0,1,2,8,"c8d8c0f0",9,"deputShip 1/7, kazimierzShip 0/2, kjeragShip 0/10, soloShip 1/2, steadShip 0/4, swiftShip 0/2, victoriaShip 0/10, yanShip 0/4",10,10,0,22],
|
"ai_2": [1,28,8,0,1,2,8,"c8d8c0f0",9,"deputShip 1/7, kazimierzShip 0/2, kjeragShip 0/10, soloShip 1/2, steadShip 0/4, swiftShip 0/2, victoriaShip 0/10, yanShip 0/4",10,10,0,22],
|
||||||
"ai_3": [1,24,9,0,0,2,8,"3fd210ed",10,"arcaneShip 1/5, deputShip 1/2, preciShip 0/2, sargonShip 0/2, soloShip 1/3",8,10,2,24]
|
"ai_3": [1,26,9,0,0,2,8,"3fd210ed",10,"arcaneShip 1/5, deputShip 1/2, preciShip 0/2, sargonShip 0/2, soloShip 1/3",8,10,2,24]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 5,
|
"round": 5,
|
||||||
"ai_0": [1,36,9,0,0,3,8,"0e6eee6e",8,"arcaneShip 0/8, kazimierzShip 0/4, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/8",8,17,9,32],
|
"ai_0": [1,36,11,0,0,3,8,"7628b380",8,"arcaneShip 0/8, kazimierzShip 0/4, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/8",8,17,9,33],
|
||||||
"ai_1": [1,18,9,0,0,3,8,"8d338f5b",14,"arcaneShip 1/4, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/4, sargonShip 0/4, soloShip 1/0, steadShip 1/2, swiftShip 1/8",9,17,8,32],
|
"ai_1": [1,18,9,0,0,3,8,"8d338f5b",14,"arcaneShip 1/4, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/4, sargonShip 0/4, soloShip 1/0, steadShip 1/2, swiftShip 1/8",9,17,8,32],
|
||||||
"ai_2": [1,16,9,0,0,3,8,"8e449278",22,"deputShip 1/10, kazimierzShip 0/2, kjeragShip 0/14, soloShip 1/4, steadShip 1/8, swiftShip 0/2, victoriaShip 0/12, yanShip 0/4",9,17,8,31],
|
"ai_2": [1,20,9,0,0,3,8,"8e449278",22,"deputShip 1/10, kazimierzShip 0/2, kjeragShip 0/14, soloShip 1/4, steadShip 1/8, swiftShip 0/2, victoriaShip 0/12, yanShip 0/4",9,17,8,31],
|
||||||
"ai_3": [1,14,8,0,0,3,8,"46c3fdd0",17,"arcaneShip 1/8, deputShip 1/4, preciShip 0/2, sargonShip 0/2, soloShip 1/5",5,17,12,32]
|
"ai_3": [1,16,8,0,0,3,8,"46c3fdd0",17,"arcaneShip 1/8, deputShip 1/4, preciShip 0/2, sargonShip 0/2, soloShip 1/5",5,17,12,32]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 6,
|
"round": 6,
|
||||||
"ai_0": [1,36,9,0,0,3,8,"6f9e5953",31,"arcaneShip 1/11, indomShip 1/2, kazimierzShip 1/8, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",13,14,1,41],
|
"ai_0": [1,36,9,0,0,3,8,"f71e9e29",31,"arcaneShip 1/11, indomShip 1/2, kazimierzShip 1/8, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",13,14,1,42],
|
||||||
"ai_1": [1,18,9,0,0,3,8,"0d78d413",35,"arcaneShip 1/4, kazimierzShip 1/9, kjeragShip 0/2, preciShip 1/8, sargonShip 0/4, soloShip 1/0, steadShip 2/6, swiftShip 1/8",14,14,0,41],
|
"ai_1": [1,18,9,0,0,3,8,"0d78d413",35,"arcaneShip 1/4, kazimierzShip 1/9, kjeragShip 0/2, preciShip 1/8, sargonShip 0/4, soloShip 1/0, steadShip 2/6, swiftShip 1/8",14,14,0,41],
|
||||||
"ai_2": [1,16,9,0,0,3,8,"d76ab210",59,"deputShip 1/12, kazimierzShip 0/4, kjeragShip 1/19, raidShip 1/14, soloShip 0/4, steadShip 2/14, swiftShip 0/2, victoriaShip 0/14, yanShip 0/4",14,14,0,41],
|
"ai_2": [1,20,9,0,0,3,8,"d76ab210",59,"deputShip 1/12, kazimierzShip 0/4, kjeragShip 1/19, raidShip 1/14, soloShip 0/4, steadShip 2/14, swiftShip 0/2, victoriaShip 0/14, yanShip 0/4",14,14,0,41],
|
||||||
"ai_3": [1,14,9,0,0,3,8,"b07fd748",46,"arcaneShip 1/11, deputShip 1/6, kjeragShip 1/9, preciShip 1/9, sargonShip 0/2, soloShip 1/7, steadShip 1/4",12,14,2,41]
|
"ai_3": [1,16,9,0,0,3,8,"b07fd748",46,"arcaneShip 1/11, deputShip 1/6, kjeragShip 1/9, preciShip 1/9, sargonShip 0/2, soloShip 1/7, steadShip 1/4",12,14,2,41]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 7,
|
"round": 7,
|
||||||
"ai_0": [1,26,10,0,0,4,8,"3e2f54ac",43,"arcaneShip 1/13, indomShip 1/4, kazimierzShip 1/16, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",7,21,14,52],
|
"ai_0": [1,26,10,0,0,4,8,"9bcfb186",43,"arcaneShip 1/13, indomShip 1/4, kazimierzShip 1/16, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",7,21,14,53],
|
||||||
"ai_1": [1,8,10,0,0,4,8,"c08bbdb7",47,"arcaneShip 1/4, kazimierzShip 1/13, kjeragShip 0/2, preciShip 1/12, sargonShip 0/4, soloShip 1/0, steadShip 2/10, swiftShip 1/8",10,21,11,52],
|
"ai_1": [1,8,10,0,0,4,8,"c08bbdb7",47,"arcaneShip 1/4, kazimierzShip 1/13, kjeragShip 0/2, preciShip 1/12, sargonShip 0/4, soloShip 1/0, steadShip 2/10, swiftShip 1/8",10,21,11,52],
|
||||||
"ai_2": [1,7,10,0,0,3,8,"2fd8f260",101,"deputShip 2/20, kazimierzShip 0/4, kjeragShip 1/31, raidShip 0/14, skillfulShip 0/2, soloShip 0/4, steadShip 1/22, suntShip 1/4, swiftShip 0/2, victoriaShip 1/24, yanShip 0/10",12,21,9,51],
|
"ai_2": [1,11,10,0,0,3,8,"2fd8f260",101,"deputShip 2/20, kazimierzShip 0/4, kjeragShip 1/31, raidShip 0/14, skillfulShip 0/2, soloShip 0/4, steadShip 1/22, suntShip 1/4, swiftShip 0/2, victoriaShip 1/24, yanShip 0/10",12,21,9,51],
|
||||||
"ai_3": [1,4,10,0,0,4,8,"07f52be4",54,"arcaneShip 1/15, deputShip 1/8, kjeragShip 1/11, preciShip 1/11, sargonShip 0/2, soloShip 1/9, steadShip 0/4",4,21,17,52]
|
"ai_3": [1,6,10,0,0,4,8,"07f52be4",54,"arcaneShip 1/15, deputShip 1/8, kjeragShip 1/11, preciShip 1/11, sargonShip 0/2, soloShip 1/9, steadShip 0/4",4,21,17,52]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 8,
|
"round": 8,
|
||||||
"ai_0": [1,20,11,0,0,4,8,"76bea343",35,"arcaneShip 2/13, emptyShip 1/0, indomShip 0/4, kazimierzShip 1/22, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 0/14",3,9,6,63],
|
"ai_0": [1,20,11,0,0,4,8,"85689579",35,"arcaneShip 2/13, emptyShip 1/0, indomShip 0/4, kazimierzShip 1/22, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 0/14",3,9,6,64],
|
||||||
"ai_1": [1,1,11,0,0,4,8,"5be276b8",69,"arcaneShip 2/4, kazimierzShip 1/17, kjeragShip 0/2, preciShip 2/21, sargonShip 0/4, soloShip 1/0, steadShip 1/14, swiftShip 1/13",2,9,7,63],
|
"ai_1": [1,1,11,0,0,4,8,"5be276b8",69,"arcaneShip 2/4, kazimierzShip 1/17, kjeragShip 0/2, preciShip 2/21, sargonShip 0/4, soloShip 1/0, steadShip 1/14, swiftShip 1/13",2,9,7,63],
|
||||||
"ai_2": [1,1,11,0,0,4,8,"029d7c62",110,"deputShip 2/28, kazimierzShip 0/4, kjeragShip 1/40, preciShip 0/2, raidShip 0/14, skillfulShip 0/2, soloShip 0/4, steadShip 0/22, suntShip 1/8, swiftShip 0/4, victoriaShip 1/34, yanShip 0/10",3,9,6,62],
|
"ai_2": [1,5,11,0,0,4,8,"029d7c62",110,"deputShip 2/28, kazimierzShip 0/4, kjeragShip 1/40, preciShip 0/2, raidShip 0/14, skillfulShip 0/2, soloShip 0/4, steadShip 0/22, suntShip 1/8, swiftShip 0/4, victoriaShip 1/34, yanShip 0/10",3,9,6,62],
|
||||||
"ai_3": [0,0,11,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",1,9,8,63]
|
"ai_3": [0,0,11,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",1,9,8,63]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 9,
|
"round": 9,
|
||||||
"ai_0": [1,10,12,0,3,4,8,"a3659a4d",41,"arcaneShip 2/17, emptyShip 1/0, indomShip 0/4, kazimierzShip 1/24, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 0/14",4,19,15,73],
|
"ai_0": [1,10,12,0,0,4,8,"92351ce9",37,"arcaneShip 2/13, emptyShip 1/0, indomShip 0/4, kazimierzShip 1/24, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 0/14",4,19,15,75],
|
||||||
"ai_1": [0,0,12,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/21, kjeragShip 0/2, preciShip 0/30, sargonShip 0/4, steadShip 0/20, swiftShip 0/18",6,19,13,75],
|
"ai_1": [0,0,12,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/23, kjeragShip 0/2, preciShip 0/30, sargonShip 0/4, steadShip 0/18, swiftShip 0/20",6,19,13,76],
|
||||||
"ai_2": [1,1,12,0,1,4,8,"138cef2e",169,"deputShip 2/36, kazimierzShip 0/4, kjeragShip 1/54, preciShip 0/2, raidShip 0/14, skillfulShip 1/13, soloShip 1/8, steadShip 0/22, suntShip 1/12, swiftShip 0/4, victoriaShip 1/46, yanShip 0/10",19,19,0,73],
|
"ai_2": [1,5,12,0,1,4,8,"8e97ba4c",161,"deputShip 2/36, kazimierzShip 0/4, kjeragShip 1/54, preciShip 0/2, raidShip 0/14, skillfulShip 1/13, soloShip 0/4, steadShip 0/22, suntShip 1/12, swiftShip 0/4, victoriaShip 1/46, yanShip 0/10",19,19,0,75],
|
||||||
"ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",null,null,null,63]
|
"ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",null,null,null,63]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 10,
|
"round": 10,
|
||||||
"ai_0": [1,5,15,0,0,5,8,"a650b1b8",51,"arcaneShip 2/25, emptyShip 1/0, indomShip 0/4, kazimierzShip 1/26, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 0/14",16,25,9,87],
|
"ai_0": [1,7,12,0,0,5,8,"28ed99c4",42,"arcaneShip 2/16, emptyShip 1/0, indomShip 0/4, kazimierzShip 1/26, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 0/14",14,25,11,86],
|
||||||
"ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/21, kjeragShip 0/2, preciShip 0/30, sargonShip 0/4, steadShip 0/20, swiftShip 0/18",null,null,null,75],
|
"ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/23, kjeragShip 0/2, preciShip 0/30, sargonShip 0/4, steadShip 0/18, swiftShip 0/20",null,null,null,76],
|
||||||
"ai_2": [1,1,13,0,0,5,8,"4d632433",206,"deputShip 2/42, kazimierzShip 0/4, kjeragShip 1/63, preciShip 0/2, raidShip 0/14, skillfulShip 1/17, soloShip 1/14, steadShip 0/22, suntShip 1/16, swiftShip 0/4, victoriaShip 1/54, yanShip 0/12",25,25,0,86],
|
"ai_2": [1,5,13,0,0,5,8,"7348a417",233,"arcaneShip 0/2, deputShip 1/40, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/59, preciShip 0/2, raidShip 1/47, skillfulShip 1/17, soloShip 0/4, steadShip 0/24, suntShip 1/14, swiftShip 0/4, victoriaShip 1/56, yanShip 0/10",25,25,0,88],
|
||||||
"ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",null,null,null,63]
|
"ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",null,null,null,63]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 11,
|
"round": 11,
|
||||||
"ai_0": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/29, indomShip 0/4, kazimierzShip 0/26, preciShip 0/8, swiftShip 0/14",4,24,20,99],
|
"ai_0": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/22, indomShip 0/4, kazimierzShip 0/28, preciShip 0/8, swiftShip 0/14",5,24,19,98],
|
||||||
"ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/21, kjeragShip 0/2, preciShip 0/30, sargonShip 0/4, steadShip 0/20, swiftShip 0/18",null,null,null,75],
|
"ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/23, kjeragShip 0/2, preciShip 0/30, sargonShip 0/4, steadShip 0/18, swiftShip 0/20",null,null,null,76],
|
||||||
"ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/48, emptyShip 0/2, investShip 0/2, kazimierzShip 0/4, kjeragShip 0/72, preciShip 0/16, raidShip 0/14, skillfulShip 0/21, soloShip 0/14, steadShip 0/22, suntShip 0/18, swiftShip 0/18, victoriaShip 0/64, visiShip 0/4, yanShip 0/12",20,24,4,102],
|
"ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/12, deputShip 0/46, egirShip 0/2, kazimierzShip 0/4, kjeragShip 0/67, preciShip 0/2, raidShip 0/47, skillfulShip 0/21, soloShip 0/4, steadShip 0/24, suntShip 0/16, swiftShip 0/4, victoriaShip 0/64, yanShip 0/12",12,24,12,104],
|
||||||
"ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",null,null,null,63]
|
"ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/20, deputShip 0/10, kjeragShip 0/19, preciShip 0/11, sargonShip 0/2, skillfulShip 0/7, soloShip 0/11, steadShip 0/4",null,null,null,63]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1128,9 +1128,9 @@
|
|||||||
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
||||||
"trophies": 2,
|
"trophies": 2,
|
||||||
"reward": 213,
|
"reward": 213,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1585533,"gold":99,"itemsEquipped":10,"kills":87,"leaks":74,"lpLost":50,"merges":4,"perfectRounds":4,"refreshes":12},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1582681,"gold":98,"itemsEquipped":11,"kills":86,"leaks":75,"lpLost":48,"merges":3,"perfectRounds":4,"refreshes":13},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["arcaneShip 0/29","indomShip 0/4","kazimierzShip 0/26","preciShip 0/8","swiftShip 0/14"]
|
"bonds": ["arcaneShip 0/22","indomShip 0/4","kazimierzShip 0/28","preciShip 0/8","swiftShip 0/14"]
|
||||||
},
|
},
|
||||||
"ai_1": {
|
"ai_1": {
|
||||||
"alive": false,
|
"alive": false,
|
||||||
@@ -1142,9 +1142,9 @@
|
|||||||
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
|
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
|
||||||
"trophies": 1,
|
"trophies": 1,
|
||||||
"reward": 170,
|
"reward": 170,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1199483,"gold":75,"itemsEquipped":9,"kills":77,"leaks":39,"lpLost":35,"merges":1,"perfectRounds":5,"refreshes":12},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1254589,"gold":76,"itemsEquipped":9,"kills":80,"leaks":39,"lpLost":35,"merges":1,"perfectRounds":5,"refreshes":10},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["arcaneShip 0/4","kazimierzShip 0/21","kjeragShip 0/2","preciShip 0/30","sargonShip 0/4","steadShip 0/20","swiftShip 0/18"]
|
"bonds": ["arcaneShip 0/4","kazimierzShip 0/23","kjeragShip 0/2","preciShip 0/30","sargonShip 0/4","steadShip 0/18","swiftShip 0/20"]
|
||||||
},
|
},
|
||||||
"ai_2": {
|
"ai_2": {
|
||||||
"alive": false,
|
"alive": false,
|
||||||
@@ -1156,9 +1156,9 @@
|
|||||||
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
|
"title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"},
|
||||||
"trophies": 2,
|
"trophies": 2,
|
||||||
"reward": 213,
|
"reward": 213,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2779516,"gold":102,"itemsEquipped":11,"kills":137,"leaks":31,"lpLost":31,"merges":3,"perfectRounds":5,"refreshes":7},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2872888,"gold":104,"itemsEquipped":13,"kills":137,"leaks":39,"lpLost":33,"merges":3,"perfectRounds":5,"refreshes":5},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["deputShip 0/48","emptyShip 0/2","investShip 0/2","kazimierzShip 0/4","kjeragShip 0/72","preciShip 0/16","raidShip 0/14","skillfulShip 0/21","soloShip 0/14","steadShip 0/22","suntShip 0/18","swiftShip 0/18","victoriaShip 0/64","visiShip 0/4","yanShip 0/12"]
|
"bonds": ["arcaneShip 0/12","deputShip 0/46","egirShip 0/2","kazimierzShip 0/4","kjeragShip 0/67","preciShip 0/2","raidShip 0/47","skillfulShip 0/21","soloShip 0/4","steadShip 0/24","suntShip 0/16","swiftShip 0/4","victoriaShip 0/64","yanShip 0/12"]
|
||||||
},
|
},
|
||||||
"ai_3": {
|
"ai_3": {
|
||||||
"alive": false,
|
"alive": false,
|
||||||
@@ -1170,7 +1170,7 @@
|
|||||||
"title": null,
|
"title": null,
|
||||||
"trophies": 1,
|
"trophies": 1,
|
||||||
"reward": 149,
|
"reward": 149,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":768250,"gold":63,"itemsEquipped":8,"kills":52,"leaks":41,"lpLost":30,"merges":1,"perfectRounds":3,"refreshes":3},
|
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":777705,"gold":63,"itemsEquipped":8,"kills":55,"leaks":41,"lpLost":28,"merges":1,"perfectRounds":3,"refreshes":3},
|
||||||
"lineup": [],
|
"lineup": [],
|
||||||
"bonds": ["arcaneShip 0/20","deputShip 0/10","kjeragShip 0/19","preciShip 0/11","sargonShip 0/2","skillfulShip 0/7","soloShip 0/11","steadShip 0/4"]
|
"bonds": ["arcaneShip 0/20","deputShip 0/10","kjeragShip 0/19","preciShip 0/11","sargonShip 0/2","skillfulShip 0/7","soloShip 0/11","steadShip 0/4"]
|
||||||
}
|
}
|
||||||
@@ -1186,7 +1186,7 @@
|
|||||||
"combat": "server",
|
"combat": "server",
|
||||||
"boost": null,
|
"boost": null,
|
||||||
"setup": {"stageId":"act1autochess_m01","bossId":"boss_1","hiddenBossId":"boss_9","factions":["FLY","DOT","REFLECTION"]},
|
"setup": {"stageId":"act1autochess_m01","bossId":"boss_1","hiddenBossId":"boss_9","factions":["FLY","DOT","REFLECTION"]},
|
||||||
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":22,"bossPoolLeft":0,"virtualMs":119900},
|
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":32,"bossPoolLeft":0,"virtualMs":119900},
|
||||||
"rng": {"setup":25,"shop":511,"waves":0,"draft":34,"bots":18062,"meta":112},
|
"rng": {"setup":25,"shop":511,"waves":0,"draft":34,"bots":18062,"meta":112},
|
||||||
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
||||||
"rounds": [
|
"rounds": [
|
||||||
@@ -1223,37 +1223,37 @@
|
|||||||
{
|
{
|
||||||
"round": 7,
|
"round": 7,
|
||||||
"ai_0": [1,29,13,0,2,4,8,"ae8b19bc",59,"arcaneShip 1/8, deputShip 2/6, kjeragShip 0/2, preciShip 0/4, siracusaShip 1/27, soloShip 1/18, steadShip 0/2",27,27,0,53],
|
"ai_0": [1,29,13,0,2,4,8,"ae8b19bc",59,"arcaneShip 1/8, deputShip 2/6, kjeragShip 0/2, preciShip 0/4, siracusaShip 1/27, soloShip 1/18, steadShip 0/2",27,27,0,53],
|
||||||
"ai_1": [1,23,11,0,3,4,8,"c7d258e1",15,"arcaneShip 0/2, kjeragShip 0/13, preciShip 2/7, siracusaShip 1/0, soloShip 1/0, steadShip 1/8",22,27,5,49]
|
"ai_1": [1,28,11,0,3,4,8,"c7d258e1",15,"arcaneShip 0/2, kjeragShip 0/13, preciShip 2/7, siracusaShip 1/0, soloShip 1/0, steadShip 1/8",22,27,5,49]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 8,
|
"round": 8,
|
||||||
"ai_0": [1,29,13,0,1,4,8,"6bbcf02a",90,"arcaneShip 2/18, deputShip 2/10, kjeragShip 0/2, preciShip 0/4, siracusaShip 1/40, soloShip 1/22, steadShip 0/2",24,24,0,66],
|
"ai_0": [1,29,13,0,1,4,8,"6bbcf02a",90,"arcaneShip 2/18, deputShip 2/10, kjeragShip 0/2, preciShip 0/4, siracusaShip 1/40, soloShip 1/22, steadShip 0/2",24,24,0,66],
|
||||||
"ai_1": [1,17,14,0,0,4,8,"5589bffd",24,"arcaneShip 0/2, emptyShip 1/0, kjeragShip 1/13, preciShip 1/7, siracusaShip 1/4, soloShip 1/0, steadShip 0/8",17,23,6,63]
|
"ai_1": [1,27,14,0,0,4,8,"5589bffd",24,"arcaneShip 0/2, emptyShip 1/0, kjeragShip 1/13, preciShip 1/7, siracusaShip 1/4, soloShip 1/0, steadShip 0/8",17,23,6,63]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 9,
|
"round": 9,
|
||||||
"ai_0": [1,29,13,0,0,4,8,"5de9a4ca",153,"arcaneShip 2/38, deputShip 1/12, indomShip 0/8, kjeragShip 0/2, miraShip 1/2, preciShip 0/4, siracusaShip 1/64, soloShip 1/24, steadShip 0/2, suntShip 1/2, victoriaShip 1/11, visiShip 0/8",15,15,0,79],
|
"ai_0": [1,29,13,0,0,4,8,"5de9a4ca",153,"arcaneShip 2/38, deputShip 1/12, indomShip 0/8, kjeragShip 0/2, miraShip 1/2, preciShip 0/4, siracusaShip 1/64, soloShip 1/24, steadShip 0/2, suntShip 1/2, victoriaShip 1/11, visiShip 0/8",15,15,0,79],
|
||||||
"ai_1": [1,17,12,0,0,4,8,"90947485",62,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/26, siracusaShip 1/8, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",15,15,0,75]
|
"ai_1": [1,27,12,0,0,4,8,"90947485",62,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/26, siracusaShip 1/8, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",15,15,0,75]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 10,
|
"round": 10,
|
||||||
"ai_0": [1,29,12,0,2,5,8,"c0721c81",178,"arcaneShip 2/42, deputShip 1/14, indomShip 0/8, kjeragShip 0/2, miraShip 1/4, preciShip 0/4, siracusaShip 1/74, soloShip 1/26, steadShip 0/2, suntShip 1/4, victoriaShip 1/14, visiShip 0/8",24,24,0,92],
|
"ai_0": [1,29,12,0,2,5,8,"c0721c81",178,"arcaneShip 2/42, deputShip 1/14, indomShip 0/8, kjeragShip 0/2, miraShip 1/4, preciShip 0/4, siracusaShip 1/74, soloShip 1/26, steadShip 0/2, suntShip 1/4, victoriaShip 1/14, visiShip 0/8",24,24,0,92],
|
||||||
"ai_1": [1,17,12,0,0,5,8,"5b7d990c",72,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/36, siracusaShip 1/8, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",23,23,0,86]
|
"ai_1": [1,27,12,0,0,5,8,"5b7d990c",72,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/36, siracusaShip 1/8, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",23,23,0,86]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 11,
|
"round": 11,
|
||||||
"ai_0": [1,29,14,0,0,5,8,"1de29f06",219,"arcaneShip 2/50, deputShip 1/16, indomShip 0/8, kjeragShip 0/2, miraShip 1/6, preciShip 0/4, siracusaShip 1/96, soloShip 1/28, steadShip 0/2, suntShip 1/6, victoriaShip 1/17, visiShip 0/8",23,23,0,107],
|
"ai_0": [1,29,14,0,0,5,8,"1de29f06",219,"arcaneShip 2/50, deputShip 1/16, indomShip 0/8, kjeragShip 0/2, miraShip 1/6, preciShip 0/4, siracusaShip 1/96, soloShip 1/28, steadShip 0/2, suntShip 1/6, victoriaShip 1/17, visiShip 0/8",23,23,0,107],
|
||||||
"ai_1": [1,17,12,0,0,5,8,"c68c2f7d",103,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/49, siracusaShip 1/26, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",23,23,0,97]
|
"ai_1": [1,27,12,0,0,5,8,"c68c2f7d",103,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/49, siracusaShip 1/26, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",23,23,0,97]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 12,
|
"round": 12,
|
||||||
"ai_0": [1,19,12,0,0,6,8,"06501643",236,"arcaneShip 2/52, deputShip 1/18, indomShip 0/8, kjeragShip 0/2, miraShip 1/8, preciShip 0/4, siracusaShip 1/100, soloShip 1/30, steadShip 0/2, suntShip 1/8, victoriaShip 1/20, visiShip 0/8",28,47,19,119],
|
"ai_0": [1,19,12,0,0,6,8,"06501643",236,"arcaneShip 2/52, deputShip 1/18, indomShip 0/8, kjeragShip 0/2, miraShip 1/8, preciShip 0/4, siracusaShip 1/100, soloShip 1/30, steadShip 0/2, suntShip 1/8, victoriaShip 1/20, visiShip 0/8",28,47,19,119],
|
||||||
"ai_1": [1,16,12,0,0,6,8,"de894d12",130,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/62, siracusaShip 1/40, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",45,46,1,108]
|
"ai_1": [1,26,12,0,0,6,8,"de894d12",130,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/62, siracusaShip 1/40, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",45,46,1,108]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 13,
|
"round": 13,
|
||||||
"ai_0": [1,9,12,0,0,6,8,"e26e0bca",240,"arcaneShip 2/60, deputShip 0/18, indomShip 0/8, kjeragShip 0/2, maniShip 1/0, miraShip 1/8, preciShip 0/4, siracusaShip 1/114, soloShip 1/30, steadShip 0/2, suntShip 1/8, victoriaShip 1/20, visiShip 0/8",16,39,23,129],
|
"ai_0": [1,9,12,0,0,6,8,"e26e0bca",240,"arcaneShip 2/60, deputShip 0/18, indomShip 0/8, kjeragShip 0/2, maniShip 1/0, miraShip 1/8, preciShip 0/4, siracusaShip 1/114, soloShip 1/30, steadShip 0/2, suntShip 1/8, victoriaShip 1/20, visiShip 0/8",16,39,23,129],
|
||||||
"ai_1": [1,13,12,0,0,6,8,"95ed45eb",166,"arcaneShip 0/4, indomShip 0/8, kjeragShip 1/28, preciShip 2/88, siracusaShip 1/50, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",36,39,3,120]
|
"ai_1": [1,23,12,0,0,6,8,"95ed45eb",166,"arcaneShip 0/4, indomShip 0/8, kjeragShip 1/28, preciShip 2/88, siracusaShip 1/50, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",36,39,3,120]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"players": {
|
"players": {
|
||||||
@@ -1267,7 +1267,7 @@
|
|||||||
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
||||||
"trophies": 4,
|
"trophies": 4,
|
||||||
"reward": 400,
|
"reward": 400,
|
||||||
"stats": {"activatedLayers":285,"bossDamage":757732,"dmgDealt":3562183,"gold":142,"itemsEquipped":20,"kills":255,"leaks":43,"lpLost":21,"merges":4,"perfectRounds":10,"refreshes":23},
|
"stats": {"activatedLayers":285,"bossDamage":757732,"dmgDealt":3727425,"gold":142,"itemsEquipped":20,"kills":265,"leaks":43,"lpLost":21,"merges":4,"perfectRounds":10,"refreshes":23},
|
||||||
"lineup": ["5_20_a@11,3[4_07_e_a+1_01_e_a]","5_12_a@11,6[6_09_e_a+3_08_e_b]","6_11_a@11,7","2_10_a@10,3[3_05_e_a+3_11_e_b]","4_10_a@10,7[4_09_e_a+1_05_e_a]","1_08_b@10,8[1_02_e_a+5_03_e_a]","4_11_b@9,8[4_08_e_a]","6_07_a@9,9[4_10_e_a+4_05_e_a]"],
|
"lineup": ["5_20_a@11,3[4_07_e_a+1_01_e_a]","5_12_a@11,6[6_09_e_a+3_08_e_b]","6_11_a@11,7","2_10_a@10,3[3_05_e_a+3_11_e_b]","4_10_a@10,7[4_09_e_a+1_05_e_a]","1_08_b@10,8[1_02_e_a+5_03_e_a]","4_11_b@9,8[4_08_e_a]","6_07_a@9,9[4_10_e_a+4_05_e_a]"],
|
||||||
"bonds": ["arcaneShip 2/74","deputShip 0/18","indomShip 0/8","kjeragShip 0/2","maniShip 1/0","miraShip 1/12","preciShip 0/4","siracusaShip 1/132","soloShip 1/30","steadShip 0/2","suntShip 1/8","victoriaShip 1/29","visiShip 0/8"]
|
"bonds": ["arcaneShip 2/74","deputShip 0/18","indomShip 0/8","kjeragShip 0/2","maniShip 1/0","miraShip 1/12","preciShip 0/4","siracusaShip 1/132","soloShip 1/30","steadShip 0/2","suntShip 1/8","victoriaShip 1/29","visiShip 0/8"]
|
||||||
},
|
},
|
||||||
@@ -1276,12 +1276,12 @@
|
|||||||
"victory": true,
|
"victory": true,
|
||||||
"roundsPassed": 14,
|
"roundsPassed": 14,
|
||||||
"eliminatedRound": null,
|
"eliminatedRound": null,
|
||||||
"lp": 13,
|
"lp": 23,
|
||||||
"bandId": "band_malkie",
|
"bandId": "band_malkie",
|
||||||
"title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"},
|
"title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"},
|
||||||
"trophies": 4,
|
"trophies": 4,
|
||||||
"reward": 400,
|
"reward": 400,
|
||||||
"stats": {"activatedLayers":166,"bossDamage":592268,"dmgDealt":3911952,"gold":132,"itemsEquipped":21,"kills":253,"leaks":15,"lpLost":15,"merges":3,"perfectRounds":9,"refreshes":22},
|
"stats": {"activatedLayers":166,"bossDamage":592268,"dmgDealt":3922620,"gold":132,"itemsEquipped":21,"kills":253,"leaks":15,"lpLost":5,"merges":3,"perfectRounds":9,"refreshes":22},
|
||||||
"lineup": ["3_20_a@12,3[4_03_e_a+5_01_e_a]","5_10_a@11,3[1_01_e_a+3_04_e_b]","5_02_a@11,6","1_17_a@11,7[5_07_e_a+1_02_e_b]","3_11_b@10,3[3_03_e_a+5_02_e_b]","1_08_b@10,8[3_06_e_a+4_11_e_a]","1_02_b@9,4[4_10_e_a+4_08_e_b]","6_18_a@9,9[3_11_e_a+5_02_e_a]"],
|
"lineup": ["3_20_a@12,3[4_03_e_a+5_01_e_a]","5_10_a@11,3[1_01_e_a+3_04_e_b]","5_02_a@11,6","1_17_a@11,7[5_07_e_a+1_02_e_b]","3_11_b@10,3[3_03_e_a+5_02_e_b]","1_08_b@10,8[3_06_e_a+4_11_e_a]","1_02_b@9,4[4_10_e_a+4_08_e_b]","6_18_a@9,9[3_11_e_a+5_02_e_a]"],
|
||||||
"bonds": ["arcaneShip 0/4","indomShip 0/8","kjeragShip 1/28","preciShip 2/88","siracusaShip 1/50","soloShip 1/0","steadShip 0/8","suntShip 1/0","victoriaShip 0/8","visiShip 0/8"]
|
"bonds": ["arcaneShip 0/4","indomShip 0/8","kjeragShip 1/28","preciShip 2/88","siracusaShip 1/50","soloShip 1/0","steadShip 0/8","suntShip 1/0","victoriaShip 0/8","visiShip 0/8"]
|
||||||
}
|
}
|
||||||
@@ -1492,8 +1492,8 @@
|
|||||||
"combat": "client",
|
"combat": "client",
|
||||||
"boost": null,
|
"boost": null,
|
||||||
"setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":"boss_10","factions":["DOT","INVISIBLE","REFLECTION"]},
|
"setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":"boss_10","factions":["DOT","INVISIBLE","REFLECTION"]},
|
||||||
"end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":59326,"virtualMs":119550},
|
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":14,"bossPoolLeft":0,"virtualMs":119900},
|
||||||
"rng": {"setup":25,"shop":484,"waves":0,"draft":34,"bots":17816,"meta":19},
|
"rng": {"setup":25,"shop":525,"waves":0,"draft":34,"bots":18074,"meta":23},
|
||||||
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
||||||
"rounds": [
|
"rounds": [
|
||||||
{
|
{
|
||||||
@@ -1538,57 +1538,57 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 9,
|
"round": 9,
|
||||||
"h_0": [1,25,12,0,0,4,8,"f78a00e2",63,"arcaneShip 1/7, kazimierzShip 0/8, preciShip 1/14, siracusaShip 1/18, suntShip 1/0, swiftShip 1/24, yanShip 0/13",17,23,6,76],
|
"h_0": [1,28,12,0,0,4,8,"f78a00e2",63,"arcaneShip 1/7, kazimierzShip 0/8, preciShip 1/14, siracusaShip 1/18, suntShip 1/0, swiftShip 1/24, yanShip 0/13",17,23,6,76],
|
||||||
"ai_0": [1,23,12,0,0,4,8,"ae419bd2",132,"arcaneShip 0/2, deputShip 1/14, kazimierzShip 0/8, lateranoShip 1/11, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/24, skillfulShip 1/34, suntShip 1/8, swiftShip 1/41",23,23,0,75]
|
"ai_0": [1,23,12,0,0,4,8,"ae419bd2",132,"arcaneShip 0/2, deputShip 1/14, kazimierzShip 0/8, lateranoShip 1/11, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/24, skillfulShip 1/34, suntShip 1/8, swiftShip 1/41",23,23,0,75]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 10,
|
"round": 10,
|
||||||
"h_0": [1,25,12,0,0,5,8,"1a942a0c",66,"arcaneShip 0/7, kazimierzShip 0/8, preciShip 1/19, siracusaShip 1/18, skillfulShip 1/0, suntShip 1/0, swiftShip 1/29, yanShip 0/13",31,31,0,88],
|
"h_0": [1,28,12,0,0,5,8,"1a942a0c",66,"arcaneShip 0/7, kazimierzShip 0/8, preciShip 1/19, siracusaShip 1/18, skillfulShip 1/0, suntShip 1/0, swiftShip 1/29, yanShip 0/13",31,31,0,88],
|
||||||
"ai_0": [1,23,12,0,1,5,8,"82fa7de0",166,"arcaneShip 0/2, deputShip 1/22, kazimierzShip 0/8, lateranoShip 1/19, miraShip 1/2, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/26, skillfulShip 1/42, suntShip 1/10, swiftShip 1/45",31,31,0,87]
|
"ai_0": [1,23,12,0,1,5,8,"82fa7de0",166,"arcaneShip 0/2, deputShip 1/22, kazimierzShip 0/8, lateranoShip 1/19, miraShip 1/2, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/26, skillfulShip 1/42, suntShip 1/10, swiftShip 1/45",31,31,0,87]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 11,
|
"round": 11,
|
||||||
"h_0": [1,20,12,0,2,5,8,"a1840a82",60,"arcaneShip 0/7, kazimierzShip 0/8, miraShip 1/4, preciShip 0/19, siracusaShip 1/18, skillfulShip 1/0, soloShip 1/0, suntShip 1/0, swiftShip 1/38, yanShip 0/13",20,25,5,102],
|
"h_0": [1,25,12,0,2,5,8,"a1840a82",60,"arcaneShip 0/7, kazimierzShip 0/8, miraShip 1/4, preciShip 0/19, siracusaShip 1/18, skillfulShip 1/0, soloShip 1/0, suntShip 1/0, swiftShip 1/38, yanShip 0/13",20,25,5,102],
|
||||||
"ai_0": [1,23,13,0,0,5,8,"c0b11e3b",214,"arcaneShip 0/2, deputShip 1/30, kazimierzShip 0/8, lateranoShip 1/29, miraShip 1/4, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/40, skillfulShip 1/52, suntShip 1/12, swiftShip 1/47",25,25,0,98]
|
"ai_0": [1,23,13,0,0,5,8,"c0b11e3b",214,"arcaneShip 0/2, deputShip 1/30, kazimierzShip 0/8, lateranoShip 1/29, miraShip 1/4, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/40, skillfulShip 1/52, suntShip 1/12, swiftShip 1/47",25,25,0,98]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 12,
|
"round": 12,
|
||||||
"h_0": [1,10,14,0,0,6,8,"efcfaab5",123,"arcaneShip 0/7, kazimierzShip 0/8, miraShip 1/18, preciShip 1/24, siracusaShip 1/34, soloShip 1/0, suntShip 1/0, swiftShip 1/47, yanShip 0/13",15,31,16,116],
|
"h_0": [1,25,14,0,0,6,8,"efcfaab5",123,"arcaneShip 0/7, kazimierzShip 0/8, miraShip 1/18, preciShip 1/24, siracusaShip 1/34, soloShip 1/0, suntShip 1/0, swiftShip 1/47, yanShip 0/13",15,31,16,116],
|
||||||
"ai_0": [1,23,12,0,1,6,8,"97827397",254,"arcaneShip 0/2, deputShip 1/32, kazimierzShip 0/8, lateranoShip 1/49, miraShip 1/6, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/42, skillfulShip 1/62, suntShip 1/14, swiftShip 1/49",31,31,0,112]
|
"ai_0": [1,23,12,0,1,6,8,"97827397",254,"arcaneShip 0/2, deputShip 1/32, kazimierzShip 0/8, lateranoShip 1/49, miraShip 1/6, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/42, skillfulShip 1/62, suntShip 1/14, swiftShip 1/49",31,31,0,112]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"round": 13,
|
"round": 13,
|
||||||
"h_0": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/7, kazimierzShip 0/8, miraShip 0/30, preciShip 0/33, sargonShip 0/9, siracusaShip 0/46, swiftShip 0/51, yanShip 0/13",8,57,49,130],
|
"h_0": [1,15,12,0,2,6,8,"31530f99",169,"arcaneShip 0/7, kazimierzShip 0/8, miraShip 1/30, preciShip 1/33, raidShip 1/0, sargonShip 1/9, siracusaShip 1/46, suntShip 1/0, swiftShip 1/51, yanShip 0/13",8,57,49,130],
|
||||||
"ai_0": [1,13,13,0,2,6,8,"c4308d5d",194,"arcaneShip 0/2, deputShip 0/32, kazimierzShip 0/8, lateranoShip 1/63, miraShip 1/6, preciShip 1/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/54, skillfulShip 0/68, suntShip 1/14, swiftShip 1/55",10,57,47,127]
|
"ai_0": [1,13,13,0,2,6,8,"c4308d5d",194,"arcaneShip 0/2, deputShip 0/32, kazimierzShip 0/8, lateranoShip 1/63, miraShip 1/6, preciShip 1/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/54, skillfulShip 0/68, suntShip 1/14, swiftShip 1/55",10,57,47,127]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"players": {
|
"players": {
|
||||||
"h_0": {
|
"h_0": {
|
||||||
"alive": false,
|
"alive": true,
|
||||||
"victory": false,
|
"victory": true,
|
||||||
"roundsPassed": 12,
|
"roundsPassed": 14,
|
||||||
"eliminatedRound": 13,
|
"eliminatedRound": null,
|
||||||
"lp": 0,
|
"lp": 8,
|
||||||
"bandId": "band_amedic",
|
"bandId": "band_amedic",
|
||||||
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
|
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
||||||
"trophies": 3,
|
"trophies": 4,
|
||||||
"reward": 280,
|
"reward": 400,
|
||||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1950591,"gold":130,"itemsEquipped":14,"kills":218,"leaks":76,"lpLost":28,"merges":4,"perfectRounds":9,"refreshes":20},
|
"stats": {"activatedLayers":189,"bossDamage":281870,"dmgDealt":2422133,"gold":144,"itemsEquipped":20,"kills":241,"leaks":76,"lpLost":13,"merges":6,"perfectRounds":9,"refreshes":24},
|
||||||
"lineup": [],
|
"lineup": ["5_10_a@12,3[1_02_e_b]","5_15_a@11,3[3_11_e_a]","3_13_b@11,8[1_01_e_a]","3_19_b@10,3[4_07_e_a+3_01_e_a]","4_16_a@10,7[4_03_e_b+3_07_e_a]","1_08_b@10,8[4_11_e_b+4_02_e_a]","6_05_a@9,8[6_09_e_a+6_01_e_b]","5_02_a@9,9"],
|
||||||
"bonds": ["arcaneShip 0/7","kazimierzShip 0/8","miraShip 0/30","preciShip 0/33","sargonShip 0/9","siracusaShip 0/46","swiftShip 0/51","yanShip 0/13"]
|
"bonds": ["arcaneShip 0/11","kazimierzShip 0/16","kjeragShip 0/8","miraShip 1/34","preciShip 1/37","sargonShip 1/9","siracusaShip 1/46","soloShip 1/0","suntShip 1/0","swiftShip 1/63","yanShip 0/13"]
|
||||||
},
|
},
|
||||||
"ai_0": {
|
"ai_0": {
|
||||||
"alive": true,
|
"alive": true,
|
||||||
"victory": false,
|
"victory": true,
|
||||||
"roundsPassed": 13,
|
"roundsPassed": 14,
|
||||||
"eliminatedRound": null,
|
"eliminatedRound": null,
|
||||||
"lp": 0,
|
"lp": 6,
|
||||||
"bandId": "band_chiave",
|
"bandId": "band_chiave",
|
||||||
"title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"},
|
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
||||||
"trophies": 3,
|
"trophies": 4,
|
||||||
"reward": 320,
|
"reward": 400,
|
||||||
"stats": {"activatedLayers":280,"bossDamage":330674,"dmgDealt":3048187,"gold":142,"itemsEquipped":22,"kills":259,"leaks":47,"lpLost":10,"merges":6,"perfectRounds":12,"refreshes":21},
|
"stats": {"activatedLayers":280,"bossDamage":498130,"dmgDealt":3639604,"gold":140,"itemsEquipped":22,"kills":271,"leaks":47,"lpLost":10,"merges":6,"perfectRounds":12,"refreshes":21},
|
||||||
"lineup": ["1_01_b@12,3[2_01_e_a+1_02_e_a]","4_21_a@12,7","3_21_b@11,3[3_01_e_a+1_05_e_b]","5_20_a@11,8[3_07_e_b]","3_01_b@10,3[4_08_e_b+6_05_e_a]","3_18_a@10,7[4_11_e_b+4_02_e_b]","4_16_a@10,8","6_01_a@9,8[5_09_e_a+3_08_e_a]"],
|
"lineup": ["1_01_b@12,3[2_01_e_a+3_08_e_b]","4_21_a@12,7","3_21_b@11,3[3_01_e_a+1_05_e_b]","5_20_a@11,8[3_07_e_b]","3_01_b@10,3[4_08_e_b+6_05_e_a]","3_18_a@10,7","4_16_a@10,8[4_11_e_a+4_02_e_a]","6_01_a@9,8[5_09_e_a+3_07_e_a]"],
|
||||||
"bonds": ["arcaneShip 0/2","deputShip 0/32","kazimierzShip 0/8","lateranoShip 1/71","miraShip 1/6","preciShip 0/2","raidShip 0/2","sargonShip 0/2","siracusaShip 1/66","skillfulShip 1/68","suntShip 1/14","swiftShip 1/55"]
|
"bonds": ["arcaneShip 0/2","deputShip 0/32","kazimierzShip 0/8","lateranoShip 1/71","miraShip 1/6","preciShip 0/2","raidShip 0/2","sargonShip 0/2","siracusaShip 1/66","skillfulShip 1/68","suntShip 1/14","swiftShip 1/55"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1607,7 +1607,7 @@
|
|||||||
[10,"3_18_a→char_609_acguad 4_16_a→char_615_acspec 5_10_a→char_606_csuppo"],
|
[10,"3_18_a→char_609_acguad 4_16_a→char_615_acspec 5_10_a→char_606_csuppo"],
|
||||||
[11,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo"],
|
[11,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo"],
|
||||||
[12,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo 6_05_a→char_611_acnipe"],
|
[12,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo 6_05_a→char_611_acnipe"],
|
||||||
[13,""]
|
[13,"4_16_a→char_615_acspec 5_10_a→char_606_csuppo 6_05_a→char_611_acnipe"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
// GitHub #41 (item 3): the 联防 field was the round's own stage — its water, crates and devices included — opened to both
|
// 联防 plays on the round's battlefield again (0.2.1, the owner's decision of 2026-10-07 after the report 「联防阶段地形全消失
|
||||||
// halves. Official: act2autochess constData escapedBattleTemplateMapSinglePlayer / MultiPlayer = level_act1autochess_
|
// 了」: 「官服保留地形,改回去」). The 联防 field is the match stage opened to both halves (GEO.UNITE_RECT, cols 0–20) with its
|
||||||
// escaped_single / _multi, the level of the 联防 battle and its map (two road halves joined at col 10). One helper →
|
// terrain, crates, water, devices and runes, as in 0.1.x; the escaped wave templates route the leaked enemies (one helper:
|
||||||
// escaped_single (enemies enter at col 10), two helpers → escaped_multi (enemies enter at col 18 and pass (9,10)); the
|
// escaped_single, entering at the middle gate (9,10); two helpers: escaped_multi, entering at col 18 through (9,10));
|
||||||
// helpers' pieces stand on their prep tiles ("按休整期位置部署在场"), the first of two shifted 8 columns onto the right half
|
// every helper's pieces stand on their prep tiles, the first of two shifted 8 columns onto the right half ("率先迎敌(即位于右侧
|
||||||
// ("率先迎敌(即位于右侧阵地)"). data/stages.json holds both maps (kind 'unite'); server/match/unite.js uniteStageId.
|
// 阵地)"). 0.2.0 fought it on the escaped levels' own map, an empty road (GitHub #41 item 3, docs/history/0.2.0.md
|
||||||
|
// §25.6.4) — withdrawn; data/stages.json keeps those two records (kind 'unite'), which no field uses. Every test here
|
||||||
|
// fails on 0.2.0 (the field was act1autochess_escaped_single / _multi there).
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { GEO, PHASE } from '../../shared/constants.js';
|
import { GEO, PHASE } from '../../shared/constants.js';
|
||||||
import { Battle } from '../../server/sim/Battle.js';
|
import { Battle } from '../../server/sim/Battle.js';
|
||||||
import { createBattleFromSpec } from '../../server/sim/spec.js';
|
import { createBattleFromSpec } from '../../server/sim/spec.js';
|
||||||
import { DataSource } from '../../server/sim/simdata.js';
|
import { DataSource } from '../../server/sim/simdata.js';
|
||||||
import { uniteStageId } from '../../server/match/unite.js';
|
|
||||||
import { FakeBattle } from './fakeBattle.js';
|
import { FakeBattle } from './fakeBattle.js';
|
||||||
import { DATA, makeMatch, give, chessOfTier, legalTileFor, checkInvariants } from './harness.js';
|
import { DATA, makeMatch, give, chessOfTier, legalTileFor, checkInvariants } from './harness.js';
|
||||||
|
|
||||||
@@ -19,18 +20,23 @@ class NoFinish extends Battle {
|
|||||||
constructor(o) { super({ ...o, autoFinish: false }); }
|
constructor(o) { super({ ...o, autoFinish: false }); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CRATE = 'trap_1105_accrate';
|
||||||
|
/** 战场#01's active 阻隔工事 on the 联防 rows: three on the left half (#001–#003), three on the right half (#101–#103). */
|
||||||
|
const M01_CRATES = [[10, 5], [10, 6], [10, 13], [10, 14], [11, 5], [11, 13]];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Co-op on 战场#01 (its row 9 is fenced off at cols 5–7: "##Err###rrSrr###rrS##"): p_0 leaks 3 enemies, the other
|
* Co-op on `stageId` (战场#01's row 9 is walled off at cols 5–7: "##Err###rrSrr###rrS##"): p_0 leaks 3 enemies, the other
|
||||||
* players are perfect — 1 helper with 2 humans, 2 helpers with 3. Each helper fields one ranged operator in its corner.
|
* players are perfect — 1 helper with 2 humans, 2 helpers with 3. Each helper fields one ranged operator on the first
|
||||||
|
* legal tile in reading order (战场#01: the 围墙 (12,3)).
|
||||||
*/
|
*/
|
||||||
function scenario({ humans, clientCombat }) {
|
function scenario({ humans, clientCombat, stageId = 'act1autochess_m01', data = DATA, seed = 4101 + humans }) {
|
||||||
const h = makeMatch({
|
const h = makeMatch({
|
||||||
mode: 'coop', humans, seed: 4101 + humans, fake: true, clientCombat,
|
mode: 'coop', humans, seed, fake: true, clientCombat, data,
|
||||||
script: (b) => (b.kind === 'normal' ? { leaks: { p_0: 3 } } : {}),
|
script: (b) => (b.kind === 'normal' ? { leaks: { p_0: 3 } } : {}),
|
||||||
}).start();
|
}).start();
|
||||||
const m = h.m;
|
const m = h.m;
|
||||||
h.toPrep(1);
|
h.toPrep(1);
|
||||||
h.setStage('act1autochess_m01');
|
h.setStage(stageId);
|
||||||
const ranged = chessOfTier(1, (c) => c.position === 'RANGED').filter((x) => m.pool.has(x));
|
const ranged = chessOfTier(1, (c) => c.position === 'RANGED').filter((x) => m.pool.has(x));
|
||||||
const helpers = [];
|
const helpers = [];
|
||||||
for (let i = 1; i < humans; i++) {
|
for (let i = 1; i < humans; i++) {
|
||||||
@@ -52,78 +58,112 @@ function uniteField(m, clientCombat) {
|
|||||||
return { opts: u.opts, battle: new NoFinish({ ...u.opts, data: m.ds, logger: { warn() {}, error() {}, info() {}, debug() {} } }) };
|
return { opts: u.opts, battle: new NoFinish({ ...u.opts, data: m.ds, logger: { warn() {}, error() {}, info() {}, debug() {} } }) };
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const clientCombat of [true, false]) {
|
const tileOf = (ps, piece) => [...ps.board.entries()].find(([, p]) => p === piece)[0].split(',').map(Number);
|
||||||
test(`联防 with 1 helper (${clientCombat ? 'client-side combat' : 'server-run'}): escaped_single, not the round's stage — enemies enter at col 10 and walk row 9`, () => {
|
const startOf = (r) => r.start ?? [r.startPosition?.row, r.startPosition?.col];
|
||||||
|
/** The crates standing on the field (device units, sorted by tile). */
|
||||||
|
const crates = (b) => b.allyUnits.filter((u) => u.kind === 'device' && u.defId === CRATE && u.alive).map((u) => [u.tileR, u.tileC])
|
||||||
|
.sort((a, b2) => a[0] - b2[0] || a[1] - b2[1]);
|
||||||
|
const MODES = [[true, 'client-side combat'], [false, 'server-run']];
|
||||||
|
|
||||||
|
for (const [clientCombat, label] of MODES) {
|
||||||
|
test(`联防 with 1 helper (${label}): the round's battlefield — 战场#01's walls and crates on both halves, the helper on its 围墙, walkers from the middle gate go round the walls`, () => {
|
||||||
const { m, helpers } = scenario({ humans: 2, clientCombat });
|
const { m, helpers } = scenario({ humans: 2, clientCombat });
|
||||||
assert.deepEqual(m.unitePlan.helpers.map((p) => p.playerId), ['p_1']);
|
assert.deepEqual(m.unitePlan.helpers.map((p) => p.playerId), ['p_1']);
|
||||||
const { opts, battle: b } = uniteField(m, clientCombat);
|
const { opts, battle: b } = uniteField(m, clientCombat);
|
||||||
assert.equal(opts.stageId, 'act1autochess_escaped_single');
|
assert.equal(opts.stageId, 'act1autochess_m01', 'the match stage (0.2.0: act1autochess_escaped_single)');
|
||||||
assert.equal(uniteStageId(m.gd, 1), 'act1autochess_escaped_single');
|
assert.equal(opts.stageId, m.stageId);
|
||||||
assert.deepEqual(opts.rect, GEO.UNITE_RECT, 'the whole 19×21 map\'s field rows (both halves are road on it)');
|
assert.deepEqual(opts.rect, GEO.UNITE_RECT, 'both halves of the stage');
|
||||||
assert.equal(b.stage.id, 'act1autochess_escaped_single');
|
assert.ok(opts.routes.every((r) => startOf(r)[1] === 10), 'escaped_single\'s routes enter at the middle gate (col 10)');
|
||||||
assert.deepEqual(b.stage.devices, [], 'no crates or devices of 战场#01');
|
assert.equal(b.stage.id, 'act1autochess_m01');
|
||||||
// the map: row 9 is road from the gate (9,10) to the objective (9,2); on 战场#01 cols 5–7 are fenced off
|
assert.deepEqual(b.stage.rows, m.stage.rows, 'tile for tile the stage the boards stand on');
|
||||||
for (let c = 3; c <= 9; c++) assert.ok(b.grid.groundPassable(9, c), `(9,${c}) is road`);
|
for (const c of [5, 6, 7]) assert.ok(!b.grid.groundPassable(9, c), `(9,${c}) is 战场#01's wall, no road`);
|
||||||
assert.equal(m.stage.rows[9].slice(5, 8), '###', '战场#01 itself has no ground there');
|
|
||||||
for (const c of [19, 20]) assert.ok(!b.grid.groundPassable(9, c), `(9,${c}) is no ground`);
|
|
||||||
// the routes of escaped_single: every one starts at col 10
|
|
||||||
assert.ok(opts.routes.every((r) => (r.start ?? [r.startPosition?.row, r.startPosition?.col])[1] === 10));
|
|
||||||
// the helper's piece stands on its prep tile
|
|
||||||
const { ps, piece } = helpers[0];
|
|
||||||
const [r, c] = [...ps.board.entries()].find(([, p]) => p === piece)[0].split(',').map(Number);
|
|
||||||
b.step();
|
b.step();
|
||||||
|
assert.deepEqual(crates(b), M01_CRATES, 'the 阻隔工事 of both halves stand (0.2.0: none)');
|
||||||
|
// the helper's piece on its prep tile — the same 围墙 it stood on in prep
|
||||||
|
const { ps, piece } = helpers[0];
|
||||||
|
const [r, c] = tileOf(ps, piece);
|
||||||
|
assert.deepEqual([r, c], [12, 3]);
|
||||||
const u = b.allyUnits.find((x) => x.uid === piece.uid && x.ownerId === 'p_1');
|
const u = b.allyUnits.find((x) => x.uid === piece.uid && x.ownerId === 'p_1');
|
||||||
assert.deepEqual([u.tileR, u.tileC], [r, c]);
|
assert.deepEqual([u.tileR, u.tileC], [r, c]);
|
||||||
// a walker crosses the tiles 战场#01 fences off
|
assert.equal(b.grid.tile(r, c).key, 'tile_fence_bound', 'its tile is 战场#01\'s 围墙 (0.2.0: road)');
|
||||||
const crossed = new Set();
|
// walkers go round 战场#01's walls: never on row 9 cols 5–7, through the top row instead
|
||||||
|
const seen = new Set();
|
||||||
while (b.time < 60 && !b.finished) {
|
while (b.time < 60 && !b.finished) {
|
||||||
b.step();
|
b.step();
|
||||||
for (const e of b.enemies) if (e.alive && e.motion !== 'FLY') crossed.add(`${Math.round(e.y)},${Math.round(e.x)}`);
|
for (const e of b.enemies) if (e.alive && e.motion !== 'FLY') seen.add(`${Math.round(e.y)},${Math.round(e.x)}`);
|
||||||
}
|
}
|
||||||
assert.ok(['9,5', '9,6', '9,7'].some((k) => crossed.has(k)), `a walker on row 9 cols 5–7 (${[...crossed].sort().join(' ')})`);
|
assert.ok(!['9,5', '9,6', '9,7'].some((k) => seen.has(k)), `no walker on the wall (${[...seen].sort().join(' ')})`);
|
||||||
|
assert.ok([4, 5, 6, 7].some((cc) => seen.has(`12,${cc}`)), `a walker on the top row (${[...seen].sort().join(' ')})`);
|
||||||
assert.equal(b.errorCount || 0, 0);
|
assert.equal(b.errorCount || 0, 0);
|
||||||
checkInvariants(m);
|
checkInvariants(m);
|
||||||
m.dispose();
|
m.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(`联防 with 2 helpers (${label}): the round's battlefield — the first helper on the right half (+8 columns) on the same 围墙 as in prep, crates on both halves, every viewer gets the match stage`, () => {
|
||||||
|
const { h, m, helpers } = scenario({ humans: 3, clientCombat });
|
||||||
|
const order = m.unitePlan.helpers.map((p) => p.playerId);
|
||||||
|
assert.equal(order.length, 2);
|
||||||
|
const { opts, battle: b } = uniteField(m, clientCombat);
|
||||||
|
assert.equal(opts.stageId, 'act1autochess_m01', 'the match stage (0.2.0: act1autochess_escaped_multi)');
|
||||||
|
assert.deepEqual(opts.players.map((p) => [p.playerId, p.colOffset]), [[order[0], 8], [order[1], 0]]);
|
||||||
|
assert.ok(opts.routes.every((r) => startOf(r)[1] === 18), 'escaped_multi\'s routes enter at col 18');
|
||||||
|
assert.ok(opts.routes.filter((r) => r.motion === 'WALK').every((r) => r.checkpoints.some(([rr, cc]) => rr === 9 && cc === 10)), 'walkers pass (9,10)');
|
||||||
|
assert.deepEqual(b.stage.rows, m.stage.rows);
|
||||||
|
b.step();
|
||||||
|
assert.deepEqual(crates(b), M01_CRATES, 'the 阻隔工事 of both halves stand (0.2.0: none)');
|
||||||
|
for (const { ps, piece } of helpers) {
|
||||||
|
const [r, c] = tileOf(ps, piece);
|
||||||
|
const u = b.allyUnits.find((x) => x.uid === piece.uid && x.ownerId === ps.playerId);
|
||||||
|
const off = ps.playerId === order[0] ? 8 : 0;
|
||||||
|
assert.deepEqual([u.tileR, u.tileC], [r, c + off], `${ps.playerId}: its prep tile${off ? ' on the right half' : ''}`);
|
||||||
|
// the stage's right half is its left half + 8 columns: the shifted helper stands on the tile kind it prepared on
|
||||||
|
assert.equal(b.grid.tile(r, c + off).key, m.stage.tiles[m.stage.rows[r][c]].tileKey, `${ps.playerId}: the prep tile's kind`);
|
||||||
|
assert.equal(b.grid.tile(r, c + off).key, 'tile_fence_bound', `${ps.playerId}: 战场#01's 围墙 (0.2.0: road)`);
|
||||||
|
}
|
||||||
|
// what the other browsers are given: the 联防 field names the match stage, the one their boards are drawn on
|
||||||
|
if (clientCombat) {
|
||||||
|
const start = h.lastTo('p_0', 'b.start');
|
||||||
|
assert.equal(start && start.spec && start.spec.stageId, m.stageId, 'the leaker simulates the 联防 spec on the match stage');
|
||||||
|
} else {
|
||||||
|
m.handle('p_0', { t: 'g.watch', fieldId: 'u' });
|
||||||
|
const meta = h.lastTo('p_0', 'm.field');
|
||||||
|
assert.equal(meta && meta.stageId, m.stageId, 'a watcher\'s m.field names the match stage');
|
||||||
|
}
|
||||||
|
assert.equal(m.publicView().stageId, 'act1autochess_m01');
|
||||||
|
checkInvariants(m);
|
||||||
|
m.dispose();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test('联防 with 2 helpers: escaped_multi — the first helper on the right half (col + 8), enemies enter at col 18 through (9,10); the client draws the field\'s map', () => {
|
test('联防 on 战场#04: the round\'s special terrain is on the field — walkers pick up the 活性源石 on their way round the walls', () => {
|
||||||
const { h, m, helpers } = scenario({ humans: 3, clientCombat: false });
|
const { m } = scenario({ humans: 2, clientCombat: false, stageId: 'act1autochess_m04' });
|
||||||
const order = m.unitePlan.helpers.map((p) => p.playerId);
|
|
||||||
assert.equal(order.length, 2);
|
|
||||||
const { opts, battle: b } = uniteField(m, false);
|
const { opts, battle: b } = uniteField(m, false);
|
||||||
assert.equal(opts.stageId, 'act1autochess_escaped_multi');
|
assert.equal(opts.stageId, 'act1autochess_m04');
|
||||||
assert.equal(b.stage.id, 'act1autochess_escaped_multi');
|
assert.ok(b.stage.special && b.stage.special.infection, 'the stage\'s 活性源石 parameters (0.2.0: none)');
|
||||||
assert.deepEqual(opts.players.map((p) => [p.playerId, p.colOffset]), [[order[0], 8], [order[1], 0]]);
|
const infection = [];
|
||||||
assert.ok(opts.routes.every((r) => r.start[1] === 18), 'every route enters at col 18');
|
for (let r = GEO.UNITE_RECT.r0; r <= GEO.UNITE_RECT.r1; r++) for (let c = GEO.UNITE_RECT.c0; c <= GEO.UNITE_RECT.c1; c++) if (b.grid.tile(r, c).terrain === 'infection') infection.push([r, c]);
|
||||||
assert.ok(opts.routes.filter((r) => r.motion === 'WALK').every((r) => r.checkpoints.some(([rr, cc]) => rr === 9 && cc === 10)), 'walkers pass (9,10)');
|
assert.deepEqual(infection, [[10, 6], [10, 14], [11, 6], [11, 14]], '战场#04\'s 活性源石 tiles on both halves');
|
||||||
b.step();
|
let burnt = null;
|
||||||
for (const { ps, piece } of helpers) {
|
while (b.time < 60 && !b.finished && !burnt) {
|
||||||
const [r, c] = [...ps.board.entries()].find(([, p]) => p === piece)[0].split(',').map(Number);
|
b.step();
|
||||||
const u = b.allyUnits.find((x) => x.uid === piece.uid && x.ownerId === ps.playerId);
|
burnt = b.enemies.find((e) => e.alive && e.findBuff && e.findBuff('terrain:infection')) || null;
|
||||||
const off = ps.playerId === order[0] ? 8 : 0;
|
|
||||||
assert.deepEqual([u.tileR, u.tileC], [r, c + off], `${ps.playerId}: its prep tile${off ? ' on the right half' : ''}`);
|
|
||||||
assert.equal(b.stage.rows[r][c + off], 'r', 'a road tile of the 联防 map');
|
|
||||||
}
|
}
|
||||||
// what a watching browser receives: the m.field of the 联防 carries the map it is drawn on
|
assert.ok(burnt, 'a walker carries the 活性源石 effect');
|
||||||
m.handle('p_0', { t: 'g.watch', fieldId: 'u' });
|
assert.equal(b.errorCount || 0, 0);
|
||||||
const meta = h.lastTo('p_0', 'm.field');
|
|
||||||
assert.equal(meta && meta.stageId, 'act1autochess_escaped_multi');
|
|
||||||
assert.equal(m.stageId, 'act1autochess_m01', 'the match stage (m.public stageId, the boards) stays the round\'s');
|
|
||||||
checkInvariants(m);
|
|
||||||
m.dispose();
|
m.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('degraded data without the 联防 maps: the field keeps the round\'s stage', () => {
|
test('the 联防 field does not depend on the escaped levels\' map records: with or without them it is the same battlefield', () => {
|
||||||
const stages = Object.fromEntries(Object.entries(DATA.stages).filter(([, s]) => s.kind !== 'unite'));
|
const stages = Object.fromEntries(Object.entries(DATA.stages).filter(([, s]) => s.kind !== 'unite'));
|
||||||
const h = makeMatch({ mode: 'coop', humans: 2, seed: 4199, fake: true, data: { ...DATA, stages }, script: (b) => (b.kind === 'normal' ? { leaks: { p_0: 2 } } : {}) }).start();
|
const run = (data) => {
|
||||||
const m = h.m;
|
const { m } = scenario({ humans: 2, clientCombat: false, data, seed: 4199 });
|
||||||
h.toPrep(1);
|
const { opts } = uniteField(m, false);
|
||||||
assert.equal(uniteStageId(m.gd, 1), null);
|
const out = { stageId: opts.stageId, routes: opts.routes, offsets: opts.players.map((p) => [p.playerId, p.colOffset]) };
|
||||||
const ps = h.ps('p_1');
|
m.dispose();
|
||||||
const id = chessOfTier(1, (c) => c.position === 'RANGED').find((x) => m.pool.has(x));
|
return out;
|
||||||
give(m, ps, id, 'board', legalTileFor(m, ps, id));
|
};
|
||||||
h.drive(() => m.phase === PHASE.UNITE);
|
const full = run(DATA);
|
||||||
assert.equal(FakeBattle.instances.find((b) => b.kind === 'unite').opts.stageId, m.stageId);
|
const degraded = run({ ...DATA, stages });
|
||||||
m.dispose();
|
assert.equal(full.stageId, 'act1autochess_m01');
|
||||||
|
assert.deepEqual(full, degraded, 'the same stage, routes and helper offsets');
|
||||||
});
|
});
|
||||||
@@ -225,7 +225,7 @@ describe('#7 audit: every stage × deploy field (server = official level data =
|
|||||||
// buildableType (PRTS 深水区 地形信息 "地形机制:拒绝部署(待补充)" — player report #3 after 0.1.0, 战场#08's pool)
|
// buildableType (PRTS 深水区 地形信息 "地形机制:拒绝部署(待补充)" — player report #3 after 0.1.0, 战场#08's pool)
|
||||||
const ROLE = { trap_1105_accrate: 'block', trap_032_mound: 'block', trap_1106_achplat: 'platform', trap_040_canoe: 'water' };
|
const ROLE = { trap_1105_accrate: 'block', trap_032_mound: 'block', trap_1106_achplat: 'platform', trap_040_canoe: 'water' };
|
||||||
for (const [id, st] of Object.entries(DATA.stages)) {
|
for (const [id, st] of Object.entries(DATA.stages)) {
|
||||||
if (st.kind === 'unite') continue; // the 联防 maps: nobody deploys or prepares on them (no bench, no boss halves)
|
if (st.kind === 'unite') continue; // the escaped levels' maps: no field is fought on them (no bench, no boss halves)
|
||||||
const lv = JSON.parse(readFileSync(levelFile(id), 'utf8'));
|
const lv = JSON.parse(readFileSync(levelFile(id), 'utf8'));
|
||||||
const map = lv.mapData.map, tiles = lv.mapData.tiles, H = map.length;
|
const map = lv.mapData.map, tiles = lv.mapData.tiles, H = map.length;
|
||||||
const dev = new Map();
|
const dev = new Map();
|
||||||
@@ -262,7 +262,7 @@ describe('#7 audit: every stage × deploy field (server = official level data =
|
|||||||
|
|
||||||
test('carry-over: a tile legal on the normal board is legal (same class) on both boss halves of every active stage', () => {
|
test('carry-over: a tile legal on the normal board is legal (same class) on both boss halves of every active stage', () => {
|
||||||
for (const [id, st] of Object.entries(DATA.stages)) {
|
for (const [id, st] of Object.entries(DATA.stages)) {
|
||||||
if (st.kind === 'unite') continue; // the 联防 maps have no boss field
|
if (st.kind === 'unite') continue; // the escaped levels' maps have no boss field
|
||||||
const n = buildDeployMap(st);
|
const n = buildDeployMap(st);
|
||||||
for (const field of ['bossL', 'bossR']) {
|
for (const field of ['bossL', 'bossR']) {
|
||||||
const b = buildDeployMap(st, { field });
|
const b = buildDeployMap(st, { field });
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { bossFieldPlacement } from '../../server/match/finalAssault.js';
|
|||||||
|
|
||||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
||||||
const STAGES = JSON.parse(readFileSync(path.join(ROOT, 'data/stages.json'), 'utf8'));
|
const STAGES = JSON.parse(readFileSync(path.join(ROOT, 'data/stages.json'), 'utf8'));
|
||||||
// the battle stages (the two 联防 maps, kind 'unite', have no preview pen: nobody prepares on them)
|
// the battle stages (the escaped levels' two maps, kind 'unite', have no preview pen: nobody prepares on them)
|
||||||
const stageList = Object.values(STAGES.stages || STAGES).filter((s) => s.kind !== 'unite');
|
const stageList = Object.values(STAGES.stages || STAGES).filter((s) => s.kind !== 'unite');
|
||||||
const stage = (STAGES.stages || STAGES).act2autochess_m01;
|
const stage = (STAGES.stages || STAGES).act2autochess_m01;
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ describe('parseStage', () => {
|
|||||||
assert.ok(t.alpha > 0 && t.alpha <= 1);
|
assert.ok(t.alpha > 0 && t.alpha <= 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// the own board is always drawn and playable; hand slots are raised benches (the two 联防 maps, kind 'unite', have
|
// the own board is always drawn and playable; hand slots are raised benches (the escaped levels' two maps, kind
|
||||||
// no bench: nobody prepares on them)
|
// 'unite', have no bench: nobody prepares on them)
|
||||||
for (let r = 9; r <= 12; r++) for (let c = 3; c <= 8; c++) assert.ok(g[r][c].drawn, `${id} board ${r},${c}`);
|
for (let r = 9; r <= 12; r++) for (let c = 3; c <= 8; c++) assert.ok(g[r][c].drawn, `${id} board ${r},${c}`);
|
||||||
for (let c = 0; c < 10; c++) {
|
for (let c = 0; c < 10; c++) {
|
||||||
if (st.kind === 'unite') { assert.equal(g[7][c].glyph, '#', `${id} no hand ${c}`); continue; }
|
if (st.kind === 'unite') { assert.equal(g[7][c].glyph, '#', `${id} no hand ${c}`); continue; }
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
// death pays the card's owner when that player fights there — on its own field a 活性源石 death always paid (pinned) —
|
// death pays the card's owner when that player fights there — on its own field a 活性源石 death always paid (pinned) —
|
||||||
// and in 联防, where the owner is the leaker (unite.js planUnite) and fights elsewhere, the helper whose half it fell on.
|
// and in 联防, where the owner is the leaker (unite.js planUnite) and fights elsewhere, the helper whose half it fell on.
|
||||||
// Until 0.2.0 that last case fell back to the leaker, who has no per-player entry in the 联防 battle: the coins were
|
// Until 0.2.0 that last case fell back to the leaker, who has no per-player entry in the 联防 battle: the coins were
|
||||||
// dropped (until 0.1.3 the 联防 field was the round's map — 战场#04's 活性源石 burned leaked bounty enemies for nothing).
|
// dropped (in 0.1.x the 联防 field was the round's map, as it is again since 0.2.1, so 战场#04's 活性源石 burned leaked
|
||||||
|
// bounty enemies for nothing).
|
||||||
// Run: node --test test/sim/feedback5-bounty-payee.test.js
|
// Run: node --test test/sim/feedback5-bounty-payee.test.js
|
||||||
|
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// test/sim/feedback5-ulpia-own-board.test.js — community report of 2026-10-06 (item 27) 「乌尔比安使用3技能会在联防阶段跳到红门
|
// test/sim/feedback5-ulpia-own-board.test.js — community report of 2026-10-06 (item 27) 「乌尔比安使用3技能会在联防阶段跳到红门
|
||||||
// 后」. 乌尔比安 S3 必须开辟的通路 throws the anchor up to 6 tiles ahead (the nearest enemy of the line, else the farthest tile) and
|
// 后」. 乌尔比安 S3 必须开辟的通路 throws the anchor up to 6 tiles ahead (the nearest enemy of the line, else the farthest tile) and
|
||||||
// moves there when the tile "可以部署" (PRTS 备注 ③: the landing tile > the tile one beyond > his own tile). On the one-helper 联防
|
// moves there when the tile "可以部署" (PRTS 备注 ③: the landing tile > the tile one beyond > his own tile). In a one-helper 联防
|
||||||
// map (act1autochess_escaped_single: the helper's pieces on the left half, the enemies out of the middle gate at col 10) the
|
// (the helper's pieces on the left half, the enemies out of the middle gate at col 10; the fixture here is the escaped level's
|
||||||
// right half is no tile his player deploys on, yet an anchor that met no enemy flew there and he moved behind the red gate for
|
// plain road, act1autochess_escaped_single — the 联防 field itself is the round's battlefield since 0.2.1) the right half is no
|
||||||
// the whole skill. Now the landing must lie on his own board (Battle.onOwnBoard, the board region mapped onto the field).
|
// tile his player deploys on, yet an anchor that met no enemy flew there and he moved behind the red gate for the whole
|
||||||
|
// skill. Now the landing must lie on his own board (Battle.onOwnBoard, the board region mapped onto the field).
|
||||||
// Run: node --test test/sim/feedback5-ulpia-own-board.test.js
|
// Run: node --test test/sim/feedback5-ulpia-own-board.test.js
|
||||||
|
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
|
|||||||
+10
-7
@@ -2768,17 +2768,20 @@ function stageDisplayName(stageId, raw) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 联防 maps' names: the tables and PRTS name neither map, so the label is the remake's own [ASSUMED] (PRTS
|
* The escaped levels' map names: the tables and PRTS name neither map, so the label is the remake's own [ASSUMED] (PRTS
|
||||||
* 卫戍协议/帮助 §联防阶段 calls the joined field "一处阵地"); no screen shows it today.
|
* 卫戍协议/帮助 §联防阶段 calls the joined field "一处阵地"); no screen shows it.
|
||||||
*/
|
*/
|
||||||
const UNITE_STAGE_NAMES = { 1: '联防阵地(1名玩家)', 2: '联防阵地(2名玩家)' };
|
const UNITE_STAGE_NAMES = { 1: '联防阵地(1名玩家)', 2: '联防阵地(2名玩家)' };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build data/stages.json: 19×21 terrain grids (row 0 = bottom), legend, devices, special terrain
|
* Build data/stages.json: 19×21 terrain grids (row 0 = bottom), legend, devices, special terrain
|
||||||
* parameters, deployable tiles and helper ground paths — the 11 battle stages of stageDatasDict, then the two 联防
|
* parameters, deployable tiles and helper ground paths — the 11 battle stages of stageDatasDict, then the maps of the
|
||||||
* maps: act2autochess constData escapedBattleTemplateMapSinglePlayer / MultiPlayer name the level of the 联防 battle
|
* two escaped levels: act2autochess constData escapedBattleTemplateMapSinglePlayer / MultiPlayer name the level of the
|
||||||
* (level_act1autochess_escaped_single / _multi), whose own map is the 联防 field — a road, not the round's stage (GitHub
|
* 联防 wave (level_act1autochess_escaped_single / _multi), whose own map is an empty road — the placeholder grid every
|
||||||
* #41). They carry `kind: 'unite'` and `helpers` (1 / 2), weight 0, no modes, and are never drawn as a match stage.
|
* wave template level carries (01…07, h01…h08, tr…: tile for tile the same). They carry `kind: 'unite'`
|
||||||
|
* and `helpers` (1 / 2), weight 0, no modes, and are never a match stage. 0.2.0 fought the 联防 battle on them (GitHub
|
||||||
|
* #41); since 0.2.1 the 联防 field is the round's battlefield again (server/match/unite.js, the owner's decision of
|
||||||
|
* 2026-10-07), and the two records stay as the official level data — sim tests use them as a plain two-halves road.
|
||||||
*/
|
*/
|
||||||
function buildStages(ctx, modesById) {
|
function buildStages(ctx, modesById) {
|
||||||
const researchStages = ctx.research.maps?.stages || {};
|
const researchStages = ctx.research.maps?.stages || {};
|
||||||
@@ -3826,7 +3829,7 @@ function validateAll(f) {
|
|||||||
if (s.rows.some((r) => r.includes('?'))) err(`stage ${s.id}: unknown tile glyph`);
|
if (s.rows.some((r) => r.includes('?'))) err(`stage ${s.id}: unknown tile glyph`);
|
||||||
if (s.name !== s.id && /[A-Za-z]/.test(s.name)) err(`stage ${s.id}: Latin text in player-facing name "${s.name}"`);
|
if (s.name !== s.id && /[A-Za-z]/.test(s.name)) err(`stage ${s.id}: Latin text in player-facing name "${s.name}"`);
|
||||||
}
|
}
|
||||||
// the 联防 field of 1 / 2 helpers is the map of its template (server/match/unite.js uniteStageId)
|
// the escaped level of 1 / 2 helpers (config.unite.templates) has its map record (kind 'unite'; no field uses it)
|
||||||
for (const [n, id] of Object.entries(config.unite.templates)) {
|
for (const [n, id] of Object.entries(config.unite.templates)) {
|
||||||
if (stages[id]?.kind !== 'unite' || stages[id].helpers !== Number(n) || stages[id].active) err(`unite template ${id}: no inactive 联防 stage for ${n} helper(s)`);
|
if (stages[id]?.kind !== 'unite' || stages[id].helpers !== Number(n) || stages[id].active) err(`unite template ${id}: no inactive 联防 stage for ${n} helper(s)`);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-9
@@ -22,9 +22,8 @@
|
|||||||
// turn; bonds from the board (bondsMeta.computeBonds) with 0–120 layers
|
// turn; bonds from the board (bondsMeta.computeBonds) with 0–120 layers
|
||||||
// bonds 46 battles: every bond at its activation threshold (1 layer) and at its top tier (999 layers), 8 operators
|
// bonds 46 battles: every bond at its activation threshold (1 layer) and at its top tier (999 layers), 8 operators
|
||||||
// fields 22 battles: every Final Assault / Hidden Core leader on its pair and its solo template (a shared boss pool,
|
// fields 22 battles: every Final Assault / Hidden Core leader on its pair and its solo template (a shared boss pool,
|
||||||
// ended at 200 game s) and 联防 fields (1 and 2 helpers on the 联防 map of their count — boards laid out on a
|
// ended at 200 game s) and 联防 fields (1 and 2 helpers on a battle stage, both halves, carried HP / SP, a
|
||||||
// battle stage —, carried HP / SP, a knocked-out operator, two leakers' enemies with a summoned-only kind and
|
// knocked-out operator, two leakers' enemies with a summoned-only kind and a bounty)
|
||||||
// a bounty)
|
|
||||||
// matches 18 matches run to the end in virtual time with the match's default bot rehearsal: 16 bot-only (solo 标准 /
|
// matches 18 matches run to the end in virtual time with the match's default bot rehearsal: 16 bot-only (solo 标准 /
|
||||||
// 险境 / 绝境 / 终极 ×2 seeds, co-op 2 / 3 / 4, one server-run combat match, two with LP and layers raised at the
|
// 险境 / 绝境 / 终极 ×2 seeds, co-op 2 / 3 / 4, one server-run combat match, two with LP and layers raised at the
|
||||||
// first prep so they reach the Hidden Core), one co-op match whose human seat (AI 托管, offline: its
|
// first prep so they reach the Hidden Core), one co-op match whose human seat (AI 托管, offline: its
|
||||||
@@ -69,7 +68,6 @@ import { buildBattleSpec, createBattleFromSpec } from '../server/sim/spec.js';
|
|||||||
import { createRng, deriveSeed } from '../server/sim/rng.js';
|
import { createRng, deriveSeed } from '../server/sim/rng.js';
|
||||||
import { GameData } from '../server/match/gamedata.js';
|
import { GameData } from '../server/match/gamedata.js';
|
||||||
import { setupMatchWaves, buildNormalWave, buildBossWave, buildUniteWave, isFlyKey, routeByMotion } from '../server/match/waves.js';
|
import { setupMatchWaves, buildNormalWave, buildBossWave, buildUniteWave, isFlyKey, routeByMotion } from '../server/match/waves.js';
|
||||||
import { uniteStageId } from '../server/match/unite.js';
|
|
||||||
import { buildDeployMap, positionClass, canPlace, ownerRangeKeys, tileKey } from '../server/match/board.js';
|
import { buildDeployMap, positionClass, canPlace, ownerRangeKeys, tileKey } from '../server/match/board.js';
|
||||||
import { computeBonds, bondSnapshot } from '../server/match/bondsMeta.js';
|
import { computeBonds, bondSnapshot } from '../server/match/bondsMeta.js';
|
||||||
import { Match } from '../server/match/Match.js';
|
import { Match } from '../server/match/Match.js';
|
||||||
@@ -246,7 +244,7 @@ const ENEMY_COLS = Object.freeze(['spawned', 'killed', 'leaked', 'dmg', 'taken']
|
|||||||
|
|
||||||
const CHESS = Object.values(data.chess).sort((a, b) => byId(a.chessId, b.chessId));
|
const CHESS = Object.values(data.chess).sort((a, b) => byId(a.chessId, b.chessId));
|
||||||
const VISIBLE = CHESS.filter((c) => c.visible);
|
const VISIBLE = CHESS.filter((c) => c.visible);
|
||||||
// the battle stages (the two 联防 maps, stages.json kind 'unite', are the 联防 fields' own: server/match/unite.js uniteStageId)
|
// the battle stages (data/stages.json also keeps the escaped levels' two maps, kind 'unite', which no field uses)
|
||||||
const STAGES = Object.keys(data.stages).filter((id) => data.stages[id].kind !== 'unite').sort(byId);
|
const STAGES = Object.keys(data.stages).filter((id) => data.stages[id].kind !== 'unite').sort(byId);
|
||||||
const BANDS = Object.keys(data.bands).sort(byId);
|
const BANDS = Object.keys(data.bands).sort(byId);
|
||||||
const EQUIPS = Object.values(data.items).filter((i) => i.itemType === 'EQUIP').map((i) => i.id).sort(byId);
|
const EQUIPS = Object.values(data.items).filter((i) => i.itemType === 'EQUIP').map((i) => i.id).sort(byId);
|
||||||
@@ -607,9 +605,8 @@ export function fieldScenarios() {
|
|||||||
const modeId = helpers === 1 ? 'mode_multi_normal' : 'mode_multi_hard';
|
const modeId = helpers === 1 ? 'mode_multi_normal' : 'mode_multi_hard';
|
||||||
const gd = gdFor(modeId);
|
const gd = gdFor(modeId);
|
||||||
const round = helpers === 1 ? 7 : 11;
|
const round = helpers === 1 ? 7 : 11;
|
||||||
// the helpers' boards are laid out on the round's stage; the 联防 battle runs on its own map (GitHub #41)
|
// the 联防 battle runs on the round's stage, the helpers' boards on its two halves (0.2.0's escaped-level map withdrawn)
|
||||||
const boardStageId = STAGES[(i * 5) % STAGES.length];
|
const stageId = STAGES[(i * 5) % STAGES.length];
|
||||||
const stageId = uniteStageId(gd, helpers) ?? boardStageId;
|
|
||||||
const seed = deriveSeed(20261005, `unite:${helpers}`);
|
const seed = deriveSeed(20261005, `unite:${helpers}`);
|
||||||
const src = normalWave(gd, round, seed);
|
const src = normalWave(gd, round, seed);
|
||||||
const leakers = helpers === 1 ? ['p2', 'p3'] : ['p3', 'p4'];
|
const leakers = helpers === 1 ? ['p2', 'p3'] : ['p3', 'p4'];
|
||||||
@@ -624,7 +621,7 @@ export function fieldScenarios() {
|
|||||||
let uid = 1;
|
let uid = 1;
|
||||||
const players = [];
|
const players = [];
|
||||||
for (let h = 0; h < helpers; h++) {
|
for (let h = 0; h < helpers; h++) {
|
||||||
const t = team(gd, boardStageId, 'normal', uid, 8, helpers > 1 && h === 0 ? 8 : 0);
|
const t = team(gd, stageId, 'normal', uid, 8, helpers > 1 && h === 0 ? 8 : 0);
|
||||||
uid = t.nextUid;
|
uid = t.nextUid;
|
||||||
// carried end state of the helper's own battle: HP ratio + SP, one knocked out, summons with SP only
|
// carried end state of the helper's own battle: HP ratio + SP, one knocked out, summons with SP only
|
||||||
t.units.forEach((u, k) => {
|
t.units.forEach((u, k) => {
|
||||||
|
|||||||
Reference in new issue
Block a user