From 02353e13f845768fb7761cb64bece6d11f54e261 Mon Sep 17 00:00:00 2001 From: sganggs Date: Wed, 7 Oct 2026 01:33:21 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feedback5=20CB5:=20a=20module's=20added=20t?= =?UTF-8?q?rait=20line=20(official=20additionalDescription,=20PRTS=20?= =?UTF-8?q?=E7=89=B9=E6=80=A7=E8=BF=BD=E5=8A=A0)=20shows=20after=20the=20c?= =?UTF-8?q?lass=20trait,=20not=20instead=20of=20it=20=E2=80=94=20the=20?= =?UTF-8?q?=E5=B9=B2=E5=91=98=E8=B0=83=E9=85=8D=20module=20card=20(?= =?UTF-8?q?=E7=89=B9=E6=80=A7=20+=20=E7=89=B9=E6=80=A7=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=20rows),=20its=20=E5=B1=80=E5=86=85=E6=95=B0=E5=80=BC,=20the?= =?UTF-8?q?=20detail=20card=20and=20the=20=E8=87=AA=E9=80=89=20module=20ro?= =?UTF-8?q?w=20(community=20report=20of=202026-10-06,=20item=2016.2:=20114?= =?UTF-8?q?=20of=20the=20164=20modules=20the=20screen=20offers=20showed=20?= =?UTF-8?q?the=20extra=20line=20alone)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Text only: data/chess.json keeps both lines (traitOverride.desc = the class trait or the module's rewrite, moduleDesc = the added line) and the battle unit keeps the class trait with the module on — the same trait text, targeting and attack shape as 不装备 for every added-line module (圣约送葬人 REA-Y also rewrites it). New helpers ui/loadoutModel.js traitLines / fullTraitText; traitText (ui/detailPanel.js) composes the two. Golden unchanged (no sim change). Tests: test/ui/feedback5-module-trait.test.js (new), loadout-stats updated. --- docs/DATA.md | 4 +- public/i18n/en.json | 1 + public/js/screens/diy.js | 5 +- public/js/screens/loadout.js | 12 +- public/js/ui/detailPanel.js | 12 +- public/js/ui/loadoutModel.js | 23 ++++ test/ui/feedback5-module-trait.test.js | 149 +++++++++++++++++++++++++ test/ui/loadout-stats.test.js | 10 +- 8 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 test/ui/feedback5-module-trait.test.js diff --git a/docs/DATA.md b/docs/DATA.md index b1fb47a4..511b34ce 100644 --- a/docs/DATA.md +++ b/docs/DATA.md @@ -169,7 +169,7 @@ Top level: `{ season, seasonName, modes, economy, lpCapPerRound, bossOvertimeAft | `projectile` | `"arrow"` | `arrow` (phys ranged) / `bolt` (arts ranged) / `orb` (heal) / `none` | | `canHitFly` | `true` | | | `targetPriority` | `"fly"` | from trait text: `fly` (优先攻击空中单位), `lowestDef` (防御力最低), else `null` | -| `trait` | `{"desc":"优先攻击空中单位","descRaw":"…","bb":{"atk_scale":1.1},"bbStr":{},"rangeGrid":null,"moduleDesc":"攻击空中单位时攻击力提升至110%","moduleDescRaw":"…"}` | profession trait (+ golden module trait upgrade merged into `bb`; `rangeGrid` = trait-effect area, e.g. 散射手 front row — **not** the attack range) | +| `trait` | `{"desc":"优先攻击空中单位","descRaw":"…","bb":{"atk_scale":1.1},"bbStr":{},"rangeGrid":null,"moduleDesc":"攻击空中单位时攻击力提升至110%","moduleDescRaw":"…"}` | profession trait (+ golden module trait upgrade merged into `bb`; `rangeGrid` = trait-effect area, e.g. 散射手 front row — **not** the attack range). `desc` = the class trait, or the module's rewrite of it (official `overrideDescripton`); `moduleDesc` = the module's added line (official `additionalDescription`, PRTS 「特性追加」), which the client shows after `desc`, never alone (0.2.0, community report item 16.2) | | `skill` | see below | default skill at `status.skillLevel` (normal 4, golden 7) | | `skills[]` | `[{…skill record…, "index":0, "isDefault":false}, {…, "index":1, "isDefault":true}]` | **loadout choices** (DESIGN §16): every skill unlocked at `status` (E1 ⇒ S1–S2, E2 ⇒ S1–S3; the default is always listed), same shape as `skill` + `isDefault`, at the chess skill level, `trigger` resolved **for that skill index** (§2.2). The `isDefault` entry equals `skill` | | `modules[]`, `statsBase`, `traitBase`, `talentsBase` | see §2.2 | golden chess with `equipLevel > 0` only: selectable modules + the no-module base they apply to | @@ -231,7 +231,7 @@ everything needed to resolve a unit for `(chessId, skillIndex, moduleId)` (`simd | `uniEquipId`, `name`, `typeName`, `typeIcon`, `icon` | `"uniequip_003_mlyss"`, `"落叶四季"`, `"TAC-Y"`, `"tac-y"`, `"uniequip_003_mlyss"` | | | `isDefault`, `level` | `false`, `3` | default = `defaultUniEquipId` (= `module.id`) | | `attr` | `{"maxHp":170,"atk":28,"def":28}` | flat stat additions (stat field names): `stats = statsBase[f] + attr[f]` (float-noise cleaned) | -| `traitOverride` | `{"desc":"…提升至165%","descRaw":…,"bb":{"atk_scale":1.65},"bbStr":{},"rangeGrid":null,"moduleDesc"?:…}` \| `null` | the full trait with this module; `null` ⇒ `traitBase` | +| `traitOverride` | `{"desc":"…提升至165%","descRaw":…,"bb":{"atk_scale":1.65},"bbStr":{},"rangeGrid":null,"moduleDesc"?:…}` \| `null` | the full trait with this module (`desc`, then the added line `moduleDesc` when present — see `trait` above); `null` ⇒ `traitBase` | | `talentChanges[]` | `{"talentIndex":1,"name":"开源节流","desc":…,"descRaw":…,"bb":{"cost":-2,"runtime_cost":-1},"bbStr":{},"rangeGrid":null,"tokenKey":null,"hidden":false}` | talent additions/overrides (`talentIndex` −1 = new hidden data-only talent); applied to `talentsBase` with the build's merge rule (`simdata composeTalents`: override of an existing index merges blackboards, module keys win) | - `statsBase` / `traitBase` / `talentsBase` — the golden record **without** any module (`stats` / `trait` / `talents` diff --git a/public/i18n/en.json b/public/i18n/en.json index bdf656b7..18112329 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -239,6 +239,7 @@ "充能": "Charges", "不装备模组:精锐干员以基础属性、特性与天赋作战。": "No Module: the Elite Operator fights with its base stats, Trait and Talents.", "特性": "Trait", + "特性追加": "Added Trait", "属性": "Stats", "无属性加成": "No stat bonus", "天赋": "Talent", diff --git a/public/js/screens/diy.js b/public/js/screens/diy.js index 00c8a7aa..59c35ef2 100644 --- a/public/js/screens/diy.js +++ b/public/js/screens/diy.js @@ -13,7 +13,7 @@ import { html, Icon, Button, TierChip } from '../ui/components.js'; import { Img, RichText, BondGlyph } from '../ui/gameComponents.js'; import { chessAvatarUrl, chessPortraitUrl, profIconUrl, skillRecordIconUrl, moduleTypeIconUrl } from '../ui/assetUrls.js'; import { data } from '../data.js'; -import { PROF_NAME, skillLabel, moduleBadge } from '../ui/loadoutModel.js'; +import { PROF_NAME, skillLabel, moduleBadge, fullTraitText } from '../ui/loadoutModel.js'; import { diySlotList, pickChoices, pickOptions, slotRecord, defaultPick } from '../ui/diyModel.js'; import { t } from '../../../shared/i18n.js'; @@ -117,7 +117,8 @@ function SkillRow({ m, s, on, locked, onPick }) { function ModuleRow({ id, rec, on, locked, onPick }) { const none = id == null; const talents = rec && Array.isArray(rec.talentChanges) ? rec.talentChanges.filter((x) => x && x.desc && !x.hidden) : []; - const trait = rec?.traitOverride ? (rec.traitOverride.moduleDescRaw || rec.traitOverride.moduleDesc || rec.traitOverride.descRaw || rec.traitOverride.desc) : null; + // the trait this module gives: the class trait (or the module's rewrite), then its extra line (item 16.2) + const trait = rec?.traitOverride ? fullTraitText(rec.traitOverride) || null : null; return html``; } -function ModuleInfo({ m, golden, opt }) { +export function ModuleInfo({ m, golden, opt }) { if (!golden) return null; const rec = opt.id === MODULE_NONE ? null : opt.rec; if (!rec) { @@ -209,8 +209,9 @@ function ModuleInfo({ m, golden, opt }) { `; } const rows = attrRows(rec.attr); - const trait = rec.traitOverride; - const traitText = trait ? (trait.moduleDescRaw || trait.moduleDesc || trait.descRaw || trait.desc) : null; + // 特性: the trait the elite fights with under this module (the class trait, or the module's rewrite of it), then the + // module's extra line under PRTS's own label 特性追加 — the line adds to the class trait (item 16.2) + const lines = traitLines(rec.traitOverride); const talents = (Array.isArray(rec.talentChanges) ? rec.talentChanges : []).filter((t) => t && (t.name || t.desc) && !t.hidden); return html`
<${Img} src=${moduleIconOf(m, rec)} class="lo-minfo__icon" />${rec.typeName || ''}${rec.name || rec.uniEquipId} @@ -219,7 +220,8 @@ function ModuleInfo({ m, golden, opt }) { ${t('属性')} ${rows.length ? rows.map((r) => html`${r.label}${r.text}`) : html`${t('无属性加成')}`}
- ${traitText ? html`
${t('特性')}<${RichText} class="lo-minfo__v" text=${traitText} />
` : null} + ${lines.base ? html`
${t('特性')}<${RichText} class="lo-minfo__v" text=${lines.base} />
` : null} + ${lines.added ? html`
${t('特性追加')}<${RichText} class="lo-minfo__v" text=${lines.added} />
` : null} ${talents.map((tal, i) => html`
${t('天赋')} ${tal.name ? html`${tal.name}` : null}<${RichText} text=${tal.descRaw || tal.desc || ''} />
`)}
`; diff --git a/public/js/ui/detailPanel.js b/public/js/ui/detailPanel.js index 18b826ef..32b887b2 100644 --- a/public/js/ui/detailPanel.js +++ b/public/js/ui/detailPanel.js @@ -40,7 +40,7 @@ import { abilityRows } from './abilityLines.js'; import { data } from '../data.js'; import { attackRangeGrid } from '../../../shared/loadoutRecord.js'; import { SKILL_SUMMON_START_DEPLOY } from '../../../shared/constants.js'; -import { moduleBadge } from './loadoutModel.js'; +import { moduleBadge, fullTraitText } from './loadoutModel.js'; import { t, tc, N_ } from '../../../shared/i18n.js'; import { audio } from '../audio.js'; @@ -277,11 +277,11 @@ export function BondChips({ bondIds, bonds = [], onBond = null, off = null, gran */ export function traitText(c, golden, lo) { const t = (lo?.record || c).trait || {}; - const base = t.descRaw || t.desc || ''; - if (!golden) return base; - // the record's own module line, also for a record cloned for another skill or module (a 不装备 record carries none) — - // until 0.1.2 a clone fell back to the class trait (Grok review of GitHub #64; the in-match card had it too) - return t.moduleDescRaw || base; + if (!golden) return t.descRaw || t.desc || ''; + // the class trait (or the module's rewrite of it), then the record's own module line — also for a record cloned for + // another skill or module (a 不装备 record carries none). Until 0.1.2 a clone fell back to the class trait (Grok review + // of GitHub #64); until 0.2.0 the module line replaced the class trait (community report of 2026-10-06, item 16.2). + return fullTraitText(t); } /** diff --git a/public/js/ui/loadoutModel.js b/public/js/ui/loadoutModel.js index ab6b1ab0..1981a711 100644 --- a/public/js/ui/loadoutModel.js +++ b/public/js/ui/loadoutModel.js @@ -325,6 +325,29 @@ export function moduleBadge(rec, id = null) { return m ? m[1] : t.slice(-1) || '?'; } +/** + * The two lines of a trait record (data `trait` / `traitBase`, ModuleRecord `traitOverride`, DATA.md §2): `base` = the + * 特性 the unit fights with — the class trait, or the module's own wording where the module rewrites it (official + * `overrideDescripton`) — and `added` = the module's extra line (official `additionalDescription`), or null. The extra + * line comes after the class trait, never instead of it: PRTS flags it 「特性追加」 on every such module, and the sim keeps + * the class trait with the module equipped (community report of 2026-10-06, item 16.2: until 0.2.0 the 干员调配 module + * card, its 局内数值 and the detail card showed the extra line alone on 114 of the 164 modules the screen offers). + * @param {any} trait + * @returns {{ base: string, added: string|null }} + */ +export function traitLines(trait) { + if (!isObj(trait)) return { base: '', added: null }; + const base = String(trait.descRaw || trait.desc || ''); + const added = trait.moduleDescRaw || trait.moduleDesc || null; + return { base, added: added ? String(added) : null }; +} + +/** The whole 特性 text of a trait record: its base line, then the module's extra line (`\n` between; RichText breaks it). */ +export function fullTraitText(trait) { + const { base, added } = traitLines(trait); + return base && added ? `${base}\n${added}` : base || added || ''; +} + /** * Module stat bonus as display rows (non-zero entries only). * @param {Record | null | undefined} attr diff --git a/test/ui/feedback5-module-trait.test.js b/test/ui/feedback5-module-trait.test.js new file mode 100644 index 00000000..9263368d --- /dev/null +++ b/test/ui/feedback5-module-trait.test.js @@ -0,0 +1,149 @@ +// Community report of 2026-10-06, item 16.2: 「干员调配界面很多干员的某个模组只有额外特性没有基础特性,不清楚是文本错误还是 +// 实际效果也有错」. A module's trait part either rewrites the class trait (official `overrideDescripton`) or adds a line +// after it (official `additionalDescription`, PRTS 「特性追加」). The data keeps both (`traitOverride.desc` = the class trait +// or its rewrite, `moduleDesc` = the added line) and the sim keeps the class trait with the module on; the text was wrong: +// the 干员调配 module card, its 局内数值, the detail card and the 自选 module row showed the added line alone (114 of the +// 164 modules the screen offers). Now the class trait comes first and the added line after it (the module card labels it 特性追加). +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); +globalThis.fetch = async (url) => { + const name = String(url).split('/').pop(); + try { + const body = readFileSync(path.join(ROOT, 'data', name), 'utf8'); + return { ok: true, status: 200, json: async () => JSON.parse(body) }; + } catch { + return { ok: false, status: 404, json: async () => ({}) }; + } +}; + +const { traitLines, fullTraitText } = await import('../../public/js/ui/loadoutModel.js'); +const { traitText } = await import('../../public/js/ui/detailPanel.js'); +const { ModuleInfo } = await import('../../public/js/screens/loadout.js'); +const { DiyPickerView } = await import('../../public/js/screens/diy.js'); +const { chessLoadout } = await import('../../public/js/ui/gameLogic.js'); +const { getDefaultSource } = await import('../../server/sim/simdata.js'); +const { resolveProfile } = await import('../../server/sim/professions.js'); +const { data } = await import('../../public/js/data.js'); +const { KITTED_CHARS } = await import('../../server/sim/content/kits/index.js'); +await data.loadAll('chess', 'bonds', 'assets', 'garrisons', 'items', 'backups'); + +const CHESS = JSON.parse(readFileSync(path.join(ROOT, 'data/chess.json'), 'utf8')); +const get = (id) => CHESS[id] ?? null; +const elites = Object.values(CHESS).filter((c) => c.visible !== false && c.isGolden && Array.isArray(c.modules)); +const modules = elites.flatMap((g) => g.modules.map((m) => ({ g, m }))); +const added = modules.filter(({ m }) => m.traitOverride?.moduleDesc); + +function* walk(v) { + if (Array.isArray(v)) { for (const x of v) yield* walk(x); return; } + if (!v || typeof v !== 'object') return; + yield v; + yield* walk(v.props?.children); +} +const hasClass = (v, c) => typeof v?.props?.class === 'string' && v.props.class.split(/\s+/).includes(c); +const textOf = (v) => { + if (v == null || typeof v === 'boolean') return ''; + if (typeof v === 'string' || typeof v === 'number') return String(v); + if (Array.isArray(v)) return v.map(textOf).join(''); + return typeof v === 'object' ? textOf(v.props?.children) : ''; +}; +const richText = (v) => String([...walk(v)].find((n) => typeof n.type === 'function' && n.type.name === 'RichText')?.props.text ?? ''); +/** The module card's rows: { k, text, kind } (kind = data-trait). */ +const cardRows = (tree) => [...walk(tree)].filter((n) => hasClass(n, 'lo-minfo__row')).map((row) => ({ + k: textOf([...walk(row)].find((n) => hasClass(n, 'lo-minfo__k'))), text: richText(row), kind: row.props['data-trait'] ?? null, +})); + +test('the data: 164 elite modules offered, 114 of them add a line after the class trait (the report\'s "only the extra trait")', () => { + assert.ok(modules.length >= 160, `${modules.length} elite module records`); + assert.ok(added.length >= 110, `${added.length} modules with an added line`); + for (const { g, m } of added) { + // the record keeps the class trait (or the module's rewrite of it) beside the added line + assert.ok(m.traitOverride.desc && m.traitOverride.desc !== m.traitOverride.moduleDesc, `${g.chessId} ${m.uniEquipId}`); + } + // 隐现's MAR-X: the official candidate is additionalDescription only, so the class trait stays + const inside = get('chess_char_1_01_b').modules.find((m) => m.typeName === 'MAR-X'); + assert.equal(inside.traitOverride.desc, '优先攻击空中单位'); + assert.equal(inside.traitOverride.moduleDesc, '攻击空中单位时攻击力提升至110%'); +}); + +test('traitLines / fullTraitText: the class trait, then the added line; a rewrite or a plain trait is one line', () => { + assert.deepEqual(traitLines({ desc: 'A', descRaw: '<@ba.kw>A', moduleDesc: 'B', moduleDescRaw: '<@ba.kw>B' }), { base: '<@ba.kw>A', added: '<@ba.kw>B' }); + assert.equal(fullTraitText({ desc: 'A', moduleDesc: 'B' }), 'A\nB'); + assert.equal(fullTraitText({ desc: 'A' }), 'A'); + assert.equal(fullTraitText({ moduleDesc: 'B' }), 'B'); + assert.equal(fullTraitText(null), ''); + assert.deepEqual(traitLines(undefined), { base: '', added: null }); +}); + +test('局内数值 and the detail card (traitText): every added-line module shows the class trait first, then its line', () => { + for (const { g, m } of added) { + const lo = { [g.baseId]: { module: m.uniEquipId } }; + const shown = traitText(g, true, chessLoadout(g, lo, get)); + const t = m.traitOverride; + assert.equal(shown, `${t.descRaw || t.desc}\n${t.moduleDescRaw || t.moduleDesc}`, `${g.chessId} ${m.typeName}`); + } + // 不装备 keeps the bare class trait; a normal chess its own + const g = get('chess_char_1_01_b'); + assert.equal(traitText(g, true, chessLoadout(g, { [g.baseId]: { module: 'none' } }, get)), g.traitBase.descRaw); + assert.equal(traitText(get('chess_char_1_01_a'), false, null), get('chess_char_1_01_a').trait.descRaw); +}); + +test('the 干员调配 module card: 特性 = the class trait (or the rewrite), 特性追加 = the added line', () => { + for (const { g, m } of modules) { + const rows = cardRows(ModuleInfo({ m: null, golden: g, opt: { id: m.uniEquipId, rec: m, isDefault: m.isDefault } })); + const t = m.traitOverride; + const trait = rows.filter((r) => r.kind); + if (t?.moduleDesc) { + assert.deepEqual(trait.map((r) => [r.k, r.text]), [['特性', t.descRaw || t.desc], ['特性追加', t.moduleDescRaw || t.moduleDesc]], `${g.chessId} ${m.typeName}`); + } else { + assert.deepEqual(trait.map((r) => [r.k, r.text]), [['特性', t.descRaw || t.desc]], `${g.chessId} ${m.typeName}: a rewrite is one line`); + } + } +}); + +test('the 自选 picker\'s module rows: the class trait, then the module\'s line', () => { + const pick = { charId: 'char_112_siege', skillIndex: 2, uniEquipId: 'uniequip_002_siege' }; + const slot = { slotId: 'chess_char_5_diy1_a', tier: 5, shopLevel: 5 }; + const tree = DiyPickerView({ m: null, slot, picks: { [slot.slotId]: pick }, kitted: [...KITTED_CHARS], onDone() {}, onClose() {}, + filter: 'all', query: '', draft: { ...pick }, onFilter() {}, onQuery() {}, onDraft() {} }); + const expand = (v, d = 6) => { + if (Array.isArray(v)) return v.map((x) => expand(x, d)); + if (!v || typeof v !== 'object') return v; + if (typeof v.type === 'function' && v.type.name !== 'RichText' && d > 0) { try { return expand(v.type(v.props || {}), d - 1); } catch { return v; } } + const c = v.props?.children; + return c === undefined ? v : { ...v, props: { ...v.props, children: expand(c, d) } }; + }; + const rows = [...walk(expand(tree))].filter((v) => v.props?.role === 'radio' && v.props['data-module'] && v.props['data-module'] !== 'none'); + assert.ok(rows.length >= 1); + const backups = data.get('backups'); + const mods = Object.values(backups.units.char_112_siege.forms).flatMap((f) => f.modules || []); + for (const row of rows) { + const rec = mods.find((x) => x.uniEquipId === row.props['data-module']); + const t = rec.traitOverride; + const first = [...walk(row)].find((n) => typeof n.type === 'function' && n.type.name === 'RichText'); + assert.equal(first.props.text, t.moduleDesc ? `${t.descRaw || t.desc}\n${t.moduleDescRaw || t.moduleDesc}` : (t.descRaw || t.desc)); + } +}); + +test('the sim: an added-line module keeps the class trait (the same text, targeting and attack shape as 不装备)', () => { + const ds = getDefaultSource(); + const keys = ['trait', 'targetPriority', 'attackKind', 'dmgType', 'canHitFly', 'splashRadius']; + const prof = ['attack', 'dmgType', 'canHitFly', 'maxTargets', 'hitAllBlocked', 'allInRange', 'priority', 'noAttack', 'noHeal', 'blockFly']; + let same = 0; + for (const { g, m } of added) { + const a = ds.getChess(g.chessId, { moduleId: m.uniEquipId }); + const b = ds.getChess(g.chessId, { moduleId: 'none' }); + assert.equal(a.trait, m.traitOverride.desc, `${g.chessId} ${m.typeName}: the battle's trait text = the card's`); + if (m.traitOverride.desc !== g.traitBase.desc) continue; // 圣约送葬人 REA-Y rewrites the class trait too + const pa = resolveProfile(a), pb = resolveProfile(b); + for (const k of keys) assert.deepEqual(a[k], b[k], `${g.chessId} ${m.typeName} ${k}`); + for (const k of prof) assert.deepEqual(pa[k], pb[k], `${g.chessId} ${m.typeName} profile.${k}`); + for (const k of Object.keys(b.traitBb || {})) assert.ok(k in a.traitBb, `${g.chessId} ${m.typeName}: keeps trait bb ${k}`); + same++; + } + assert.ok(same >= 110, `${same} added-line modules keep the class trait in battle`); +}); diff --git a/test/ui/loadout-stats.test.js b/test/ui/loadout-stats.test.js index 1f742c0f..6be6bf72 100644 --- a/test/ui/loadout-stats.test.js +++ b/test/ui/loadout-stats.test.js @@ -196,9 +196,9 @@ test('特性 and 天赋 follow the chosen module: 隐现\'s trait upgrade is MAR const rows = (id, level, entries) => kitRows(section(id, level, entries)); const { golden } = slot(INSIDE); const traitOf = (rs) => rs.find((r) => r.k === '特性'); - // default module: the module's upgraded trait text; 不装备: the plain class trait - assert.equal(traitOf(rows(INSIDE, 'elite', {})).text, golden.trait.moduleDescRaw); - assert.match(traitOf(rows(INSIDE, 'elite', {})).text, /110%/); + // default module: the class trait, then the module's added line (item 16.2 of 2026-10-06); 不装备: the plain class trait + assert.equal(traitOf(rows(INSIDE, 'elite', {})).text, `${golden.trait.descRaw}\n${golden.trait.moduleDescRaw}`); + assert.match(traitOf(rows(INSIDE, 'elite', {})).text, /^优先攻击空中单位\n[^\n]*110%/); assert.equal(traitOf(rows(INSIDE, 'elite', { [INSIDE]: { module: 'none' } })).text, golden.traitBase.descRaw); assert.equal(traitOf(rows(INSIDE, 'normal', {})).text, slot(INSIDE).base.trait.descRaw, '普通: its own trait'); // 天赋: named and not hidden, the record's list @@ -292,8 +292,8 @@ test('特性 follows the chosen module also when the skill or the module differs const chess = JSON.parse(fs.readFileSync(new URL('../../data/chess.json', import.meta.url), 'utf8')); const get = (id) => chess[id]; const shown = (id, lo) => traitText(get(id), true, chessLoadout(get(id), lo, get)); - // 隐现 (MAR-X): its other skill keeps the module's line, not the class trait 优先攻击空中单位 - assert.match(shown('chess_char_1_01_b', { chess_char_1_01_a: { skill: 0, module: null } }), /攻击空中单位时攻击力提升至/); + // 隐现 (MAR-X): its other skill keeps the module's line after the class trait 优先攻击空中单位 (item 16.2) + assert.match(shown('chess_char_1_01_b', { chess_char_1_01_a: { skill: 0, module: null } }), /^优先攻击空中单位\n攻击空中单位时攻击力提升至/); // 信仰搅拌机: SPT-Y shows its own line; 不装备 shows the class trait assert.match(shown('chess_char_4_01_b', { chess_char_4_01_a: { skill: get('chess_char_4_01_b').skill.index, module: 'uniequip_003_rmixer' } }), /攻击距离\+1/); assert.match(shown('chess_char_4_01_b', { chess_char_4_01_a: { skill: get('chess_char_4_01_b').skill.index, module: 'none' } }), /能够阻挡三个敌人/); From 464a823bc5f71a83baf384ceda5f657dd8bd5a88 Mon Sep 17 00:00:00 2001 From: sganggs Date: Wed, 7 Oct 2026 01:44:35 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feedback5=20CB5:=20a=20=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=20upgrade=20opens=20the=20new=20level's=20ex?= =?UTF-8?q?tra=20slot=20at=20once=20with=20a=20new=20card,=20the=20cards?= =?UTF-8?q?=20shown=20stay=20in=20place=20(community=20report=20of=202026-?= =?UTF-8?q?10-06,=20item=2019;=20the=20official=20tutorial's=20=E4=BC=91?= =?UTF-8?q?=E6=95=B4=E6=9C=9F=20page:=20=E3=80=8C=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=90=8E=E5=B0=86=E5=87=BA=E7=8E=B0=E6=9B=B4=E5=A4=9A=E7=9A=84?= =?UTF-8?q?=E5=95=86=E5=93=81=E6=A0=8F=E4=BD=8D=E3=80=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until 0.2.0 the shop kept its old slot count until the next roll (a refresh or the round start), so a level-up 1→2 (险境: 3→4 operator slots) or 3→4 (4→5; 标准: 5→6) showed no new card. PlayerState._openLevelSlots (player/economy.js): the extra slots of the new level are appended to the chess slots (the item slot stays after them), each drawn at the new level; [ASSUMED] the new card follows the freeze toggle as a manual refresh's cards do. audit.js checks the slot count and that every card shown before stays in place. Docs: META.md §3 / §7, PLAYING.md. Golden: matches — all 18 scenarios moved (solo-FUNNY-1/2, solo-NORMAL-1/2, solo-HARD-1/2, solo-ABYSS-1/2, coop2-NORMAL-3, coop2-HARD-4, coop3-ABYSS-5, coop4-FUNNY-6, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, solo-HARD-9-boosted, coop2-ABYSS-10-boosted, coop2-NORMAL-14-standins, coop2-NORMAL-70-diy): every bot levels its shop and now sees (and draws the shop RNG for) the new card at once, so purchases and boards part from there. roster / bonds / fields / standins / diy unchanged; no engine error stored. Tests: test/match/feedback5-shop-levelslots.test.js (new). --- docs/META.md | 7 +- docs/PLAYING.md | 2 +- server/match/audit.js | 11 + server/match/player/economy.js | 22 + test/golden/matches.json | 1006 +++++++++--------- test/match/feedback5-shop-levelslots.test.js | 94 ++ 6 files changed, 635 insertions(+), 507 deletions(-) create mode 100644 test/match/feedback5-shop-levelslots.test.js diff --git a/docs/META.md b/docs/META.md index 3db3e3f0..b5cd6288 100644 --- a/docs/META.md +++ b/docs/META.md @@ -505,7 +505,8 @@ Any `choice:` handler whose EffectRef reuses its own key must guard like this (o items by `price`, refresh 1 (free refreshes first), sell +1 (elite too; items cannot be sold, only destroyed). * **Shop**: `shopSlots[level]` chess slots + item slot(s), copy-weighted rolls over remaining pool copies of unbanned, visible chess with tier ≤ level; item slot: tier by the same shares, uniform item within the tier. Level-up price = - base per mode, −1 each round start (floor 0), reset to the next base after upgrading; `MAX_LEVEL` at 6. One freeze + base per mode, −1 each round start (floor 0), reset to the next base after upgrading; an upgrade opens the new level's + extra slots at once with new cards (the cards shown stay); `MAX_LEVEL` at 6. One freeze toggle freezes every unsold slot until the next round start; a manual refresh rerolls everything (new slots stay frozen). Unfrozen slots are cleared at combat start. Slot positions are stable (frozen slots keep their index). * **Pool**: copies 12/14/18/16/8/5 (缪尔赛思 4); a normal piece holds 1 copy, an elite 3; displays never reserve copies; @@ -823,7 +824,9 @@ round was over. The official 1 s `broadcastBeginDelay` is not modelled. units > active bond > Σ active layers > standing > seat; the ranks marked 存疑 in PRTS). Unite enemies re-enter with their original stats on the official 联防 spawn timing (waves.js `buildUniteWave`, DATA.md §15 #22). * A manual refresh while frozen keeps the new slots frozen until the next round start. -* Level-up does not add slots until the next roll (refresh / round start). +* A level-up opens the new level's extra slots at once, each with a new card drawn at the new level, and keeps the + cards shown (0.2.0, community report item 19; the official tutorial's 「升级后将出现更多的商品栏位」); the new card + follows the freeze toggle like a manual refresh's cards. * Buying a second copy of an equipped normal item merges into the golden item in the hand (not equipped). * Promotions by effects (升华, 博士投影) keep the equipment; merges return it; 突变细胞's transformation returns it (the cell included) before its new operator is gained into the 整备区 — the carrier's tile is left empty (official footage, diff --git a/docs/PLAYING.md b/docs/PLAYING.md index 19b12245..ed13b4d6 100644 --- a/docs/PLAYING.md +++ b/docs/PLAYING.md @@ -48,7 +48,7 @@ | 出售干员 | 任何干员(含精锐)都只返还 **1** 资金 | | 装备 / 法术 | 调度中心出售除特殊装备外的所有装备:4 把特殊维式重锤(战栗 / 坚固 / 加速 / 灼燃)只来自维多利亚盟约(每 25 层)和洛洛的定制品,突变细胞只来自昆图斯的策略。装备不能出售,整备区里的只能「销毁」(不返还)。已配发的装备锁定在干员身上:只能被第三件装备替换(弹出替换窗口,选择要替换的一件,它会被销毁),或在干员出售 / 合成精锐时退回整备区。**突变细胞**不会用完:战斗结束后装备者被销毁(部署在场上的话,原来的格子空出来,剩余可放置角色加 1),突变细胞和它的其他装备先退回整备区,可以再次配发给任意干员;随后获得一名高一阶(最高 VI)的随机普通干员(装备者是精锐也一样),和其他新获得的干员一样进入整备区(满了进入临时整备区),需要自己重新部署;如果它凑齐了合成所需的同名干员,照常合成精锐。**坚固维式重锤**首次受到致命伤害时锁血 8 秒,**每次部署一次**:被击倒再部署(或突袭跳跃再部署、被 M3茧甲 / 埃芒加德复活)后又能锁一次,娜仁图亚策略借来的锤子也一样;借用到期时正在锁血的会锁满 8 秒;站在阿戈尔干员身前被开场吞噬、自带源石溶剂掉血时也会用掉这一次 | | 冻结 | 免费,保留当前所有未买的格子到下一回合;冻结状态下仍可主动刷新 | -| 升级调度中心(1→2 … 5→6) | 险境及以上 5 / 8 / 11 / 12 / 13;同盟标准 5 / 8 / 10 / 11 / 11;独立标准 1 / 1 / 5 / 8 / 10。**每过一回合价格 −1**(最低 0),升级后按下一级的价格重新计算 | +| 升级调度中心(1→2 … 5→6) | 险境及以上 5 / 8 / 11 / 12 / 13;同盟标准 5 / 8 / 10 / 11 / 11;独立标准 1 / 1 / 5 / 8 / 10。**每过一回合价格 −1**(最低 0),升级后按下一级的价格重新计算。升级到干员栏位更多的等级(险境及以上 2 级 4 个、4 级 5 个;标准 2 级 4 个、6 级 5 个)时,**新栏位立刻刷出一名新干员**,已有的卡片不变 | | 其他收入 | 悬赏敌人、盟约「远见」「奇迹」、装备「骑士储蓄罐」、部分策略和特质 | 调度中心等级决定商店能出现的**最高阶级**和格子数(险境及以上:干员 3 / 4 / 4 / 5 / 5 / 5 格 + 装备 1 格)。 diff --git a/server/match/audit.js b/server/match/audit.js index ce191e1f..ee78a662 100644 --- a/server/match/audit.js +++ b/server/match/audit.js @@ -221,12 +221,23 @@ export function attachAudit(m, { invariants = true, limit = 200 } = {}) { const price = ps.shop.upgradePrice; const f0 = ps.funds; const fx = hasSpendEffects(m, ps); + const slots0 = ps.shop.slots.slice(); + const layout0 = ps.shop.layout || { chess: slots0.length, item: 0 }; const res = orig(); if (res && res.ok) check('levelUp', () => { if (ps.shop.level !== lv + 1) fail(`${ps.playerId}: level ${lv} → ${ps.shop.level}`); if (f0 - ps.funds !== price && !fx && !hasSpendEffects(m, ps)) fail(`${ps.playerId}: level-up paid ${f0 - ps.funds}, price ${price}`); const next = gd.upgradeBase(ps.shop.level) ?? 0; if (ps.shop.upgradePrice !== next) fail(`${ps.playerId}: upgrade price after level-up ${ps.shop.upgradePrice}, expected ${next}`); + // the new level's extra slots open at once (item 19 of 2026-10-06); the cards shown before stay in place + const { chess, item } = gd.shopSlots(ps.shop.level); + const want = Math.max(chess, layout0.chess) + Math.max(item, layout0.item); + if (ps.shop.slots.length !== want) fail(`${ps.playerId}: ${ps.shop.slots.length} shop slots after the level-up to ${ps.shop.level}, expected ${want}`); + const layout = ps.shop.layout || layout0; + slots0.forEach((s, i) => { + const at = i < layout0.chess ? i : layout.chess + (i - layout0.chess); + if (ps.shop.slots[at] !== s) fail(`${ps.playerId}: shop slot ${i} changed by the level-up`); + }); }); return res; }); diff --git a/server/match/player/economy.js b/server/match/player/economy.js index bc75d022..bd0686bd 100644 --- a/server/match/player/economy.js +++ b/server/match/player/economy.js @@ -92,6 +92,27 @@ export class PlayerEconomy { this.dirty(); } + /** + * The 调度中心's upgrade opens the new level's extra slots at once, each with a new card drawn at the new level; the + * cards already shown stay where they are (chess slots keep their index, the item slot stays after them). Official: the + * tutorial's 休整期 page 「升级:…升级后将出现更多的商品栏位、可调度干员以及新装备」; the community report of 2026-10-06 + * (item 19) 「升级商店获得新的商店位时用新卡补上,而不是空着」. Until 0.2.0 the extra slots waited for the next roll (a + * refresh or the round start). [ASSUMED] the new card follows the freeze toggle, as a manual refresh's cards do. + */ + _openLevelSlots() { + const { chess: nChess, item: nItem } = this.gd.shopSlots(this.shop.level); + const old = this.shop.slots; + const layout = this.shop.layout || { chess: old.length, item: 0 }; + if (nChess <= layout.chess && nItem <= layout.item) return; + const chess = old.slice(0, layout.chess); + const items = old.slice(layout.chess); + const fresh = (s) => { if (s) s.frozen = this.shop.frozen; return s; }; + while (chess.length < nChess) chess.push(fresh(this._rollChessSlot())); + while (items.length < nItem) items.push(fresh(this._rollItemSlot())); + this.shop.slots = [...chess, ...items]; + this.shop.layout = { chess: chess.length, item: items.length }; + } + /** Combat start: unfrozen slots are emptied (research 01 A1). */ clearUnfrozenShop() { this.shop.slots = this.shop.slots.map((s) => (s && s.frozen && !s.sold ? s : null)); @@ -173,6 +194,7 @@ export class PlayerEconomy { this.spend(price); this.shop.level++; this.shop.upgradePrice = this.gd.upgradeBase(this.shop.level) ?? 0; + this._openLevelSlots(); this.m.tickerFor('SHOP_LEVEL', [this.name, String(this.shop.level)], { playerId: this.playerId, param: String(this.shop.level) }); this.m.dispatch(this, 'onLevelUp', { level: this.shop.level, price }); this._afterSpend(price, 'levelUp'); diff --git a/test/golden/matches.json b/test/golden/matches.json index e2f8e487..68148c80 100644 --- a/test/golden/matches.json +++ b/test/golden/matches.json @@ -16,33 +16,36 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":null,"factions":["TIMES","DOT","INVISIBLE"]}, - "end": {"victory":true,"roundsPassed":9,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":21,"bossPoolLeft":0,"virtualMs":68100}, - "rng": {"setup":16,"shop":142,"waves":0,"draft":0,"bots":5205,"meta":0}, + "end": {"victory":false,"roundsPassed":8,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":80481,"virtualMs":68100}, + "rng": {"setup":16,"shop":81,"waves":0,"draft":0,"bots":5205,"meta":0}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,27,4,0,0,1,2,"20c00c02",0,"preciShip 0/2, sargonShip 0/2",4,4,0,4]}, {"round":2,"ai_0":[1,27,5,0,0,2,4,"8cdbb4d3",0,"preciShip 0/2, sargonShip 0/4",8,8,0,9]}, - {"round":3,"ai_0":[1,27,6,0,0,3,6,"24c9b010",1,"indomShip 1/1, preciShip 0/2, sargonShip 0/4",7,7,0,15]}, - {"round":4,"ai_0":[1,27,7,0,0,3,8,"2f6d1395",16,"deputShip 1/2, indomShip 1/4, preciShip 0/2, sargonShip 1/10",10,10,0,22]}, - {"round":5,"ai_0":[1,27,8,0,0,3,8,"beffa9be",29,"deputShip 1/4, indomShip 1/7, preciShip 0/2, sargonShip 1/12, steadShip 1/6",53,53,0,29]}, - {"round":6,"ai_0":[1,27,9,0,0,4,8,"522b51a4",40,"deputShip 2/8, indomShip 0/7, preciShip 0/2, sargonShip 1/18, steadShip 2/14",28,28,0,40]}, - {"round":7,"ai_0":[1,27,10,0,0,4,8,"84e59f1d",58,"deputShip 2/12, indomShip 0/7, preciShip 0/2, sargonShip 1/24, steadShip 2/22",27,27,0,51]}, - {"round":8,"ai_0":[1,27,11,0,0,4,8,"f9f2e5fd",76,"deputShip 2/16, indomShip 0/7, preciShip 0/2, sargonShip 1/28, steadShip 2/32",14,14,0,61]} + {"round":3,"ai_0":[1,27,6,0,0,3,6,"88b2c40f",1,"indomShip 1/1, preciShip 0/2, sargonShip 0/4",7,7,0,15]}, + {"round":4,"ai_0":[1,27,7,0,0,3,8,"12cbee9d",2,"indomShip 1/2, preciShip 0/2, sargonShip 0/4",10,10,0,21]}, + {"round":5,"ai_0":[1,27,8,0,0,3,8,"bea79fc5",3,"indomShip 2/3, kjeragShip 1/0, preciShip 0/2, sargonShip 0/4",53,53,0,29]}, + {"round":6,"ai_0":[1,27,9,0,0,4,8,"b6051456",15,"indomShip 2/4, kjeragShip 1/3, preciShip 0/2, sargonShip 0/4, steadShip 1/8",28,28,0,39]}, + {"round":7,"ai_0":[1,27,10,0,0,4,8,"bc01603b",32,"indomShip 2/5, kjeragShip 1/7, preciShip 0/2, sargonShip 0/4, steadShip 2/20",27,27,0,49]}, + { + "round": 8, + "ai_0": [1,27,11,0,0,4,8,"144e1f0b",45,"deputShip 1/2, emptyShip 1/2, indomShip 0/5, kjeragShip 1/10, preciShip 0/2, sargonShip 0/4, steadShip 2/29, swiftShip 1/2",14,14,0,58] + } ], "players": { "ai_0": { "alive": true, - "victory": true, - "roundsPassed": 9, + "victory": false, + "roundsPassed": 8, "eliminatedRound": null, - "lp": 21, + "lp": 0, "bandId": "band_ermengard", - "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, + "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 0, - "reward": 113, - "stats": {"activatedLayers":78,"bossDamage":200000,"dmgDealt":841708,"gold":72,"itemsEquipped":10,"kills":175,"leaks":0,"lpLost":0,"merges":2,"perfectRounds":8,"refreshes":14}, - "lineup": ["4_14_a@12,7","2_08_a@12,8[4_05_e_a+2_04_e_b]","2_14_a@11,8","3_10_a@10,3","2_17_a@10,4[1_01_e_b+4_02_e_a]","4_01_b@10,7","1_12_b@10,8[3_05_e_a+5_02_e_a]","5_02_a@9,8[4_08_e_a]"], - "bonds": ["deputShip 0/16","emptyShip 1/0","indomShip 0/7","preciShip 1/7","sargonShip 1/28","steadShip 2/38","swiftShip 1/5"] + "reward": 100, + "stats": {"activatedLayers":78,"bossDamage":119519,"dmgDealt":724068,"gold":71,"itemsEquipped":14,"kills":170,"leaks":0,"lpLost":0,"merges":2,"perfectRounds":8,"refreshes":4}, + "lineup": ["2_14_a@12,3","1_13_a@11,3","3_14_a@11,8[5_02_e_b+1_01_e_a]","3_10_a@10,3[2_01_e_a]","3_16_a@10,4[3_03_e_a+2_04_e_a]","5_14_a@10,7[4_08_e_a+4_05_e_a]","3_05_a@10,8","3_20_b@9,8[3_11_e_a+5_02_e_b]"], + "bonds": ["deputShip 2/9","emptyShip 1/6","indomShip 0/5","kjeragShip 1/17","preciShip 0/2","sargonShip 0/8","steadShip 1/40","swiftShip 1/6"] } } }, @@ -56,21 +59,18 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":null,"factions":["ELEMENT","DOT","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":8,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":124751,"virtualMs":68100}, - "rng": {"setup":16,"shop":123,"waves":0,"draft":0,"bots":5205,"meta":28}, + "end": {"victory":false,"roundsPassed":8,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":98765,"virtualMs":68100}, + "rng": {"setup":16,"shop":161,"waves":0,"draft":0,"bots":5205,"meta":43}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,29,4,0,0,1,2,"aa096cd9",0,"",5,5,0,4]}, {"round":2,"ai_0":[1,29,5,0,0,2,4,"640ad8c6",0,"kjeragShip 0/2, steadShip 0/2",8,8,0,9]}, - {"round":3,"ai_0":[1,29,6,0,0,3,6,"844de193",4,"deputShip 1/4, kjeragShip 0/2, steadShip 0/2",10,10,0,14]}, - {"round":4,"ai_0":[1,27,7,0,0,3,8,"1ee26f29",17,"deputShip 1/6, kjeragShip 0/2, steadShip 0/2, victoriaShip 1/11",6,8,2,20]}, - {"round":5,"ai_0":[1,27,8,0,0,3,8,"f4b101e7",44,"deputShip 2/10, emptyShip 1/4, kjeragShip 1/6, steadShip 0/2, victoriaShip 1/24",17,17,0,27]}, - {"round":6,"ai_0":[1,27,9,0,2,4,8,"9a76f3a9",74,"deputShip 2/14, emptyShip 1/8, kjeragShip 1/15, steadShip 0/2, victoriaShip 1/37",14,14,0,35]}, - {"round":7,"ai_0":[1,26,12,0,0,4,8,"8d24a157",111,"deputShip 2/18, emptyShip 1/12, kjeragShip 1/21, steadShip 1/10, victoriaShip 1/50",34,35,1,46]}, - { - "round": 8, - "ai_0": [1,23,11,0,0,4,8,"cc675060",138,"deputShip 1/20, emptyShip 0/12, indomShip 1/2, kjeragShip 1/32, steadShip 1/17, victoriaShip 1/67",21,24,3,56] - } + {"round":3,"ai_0":[1,29,6,0,0,3,6,"77b7c06e",4,"deputShip 1/4, kjeragShip 0/2, steadShip 0/2",10,10,0,15]}, + {"round":4,"ai_0":[1,29,7,0,0,3,8,"36539919",21,"deputShip 2/10, kjeragShip 1/6, steadShip 0/2, victoriaShip 1/5",8,8,0,22]}, + {"round":5,"ai_0":[1,29,8,0,2,3,8,"6345271e",50,"deputShip 2/16, emptyShip 1/4, kjeragShip 1/10, steadShip 1/10, victoriaShip 1/10",17,17,0,29]}, + {"round":6,"ai_0":[1,29,11,0,1,4,8,"c02dfe1b",69,"deputShip 2/22, emptyShip 0/4, kjeragShip 1/14, steadShip 2/18, victoriaShip 1/15",14,14,0,39]}, + {"round":7,"ai_0":[1,29,11,0,2,4,8,"e7d70234",90,"deputShip 2/28, emptyShip 0/4, kjeragShip 1/18, steadShip 2/24, victoriaShip 1/20",35,35,0,50]}, + {"round":8,"ai_0":[1,26,13,0,2,4,8,"fbc70791",98,"deputShip 2/34, emptyShip 1/8, kjeragShip 1/22, steadShip 0/24, swiftShip 1/9, victoriaShip 1/25",21,24,3,63]} ], "players": { "ai_0": { @@ -83,9 +83,9 @@ "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 0, "reward": 100, - "stats": {"activatedLayers":182,"bossDamage":75249,"dmgDealt":642303,"gold":68,"itemsEquipped":11,"kills":140,"leaks":6,"lpLost":6,"merges":3,"perfectRounds":5,"refreshes":11}, - "lineup": ["3_20_a@12,3[3_11_e_b+1_05_e_a]","1_02_b@12,8[3_10_e_b+4_10_e_a]","1_06_a@11,3[5_02_e_a+1_05_e_b]","3_08_b@11,8","4_14_a@10,3","3_16_a@10,4","5_22_a@9,8","2_05_a@9,9"], - "bonds": ["deputShip 1/22","emptyShip 0/12","indomShip 0/2","kjeragShip 1/34","steadShip 1/21","swiftShip 1/21","victoriaShip 1/84"] + "stats": {"activatedLayers":128,"bossDamage":101235,"dmgDealt":686257,"gold":78,"itemsEquipped":16,"kills":138,"leaks":3,"lpLost":3,"merges":0,"perfectRounds":7,"refreshes":18}, + "lineup": ["3_20_a@12,3[4_07_e_a+3_04_e_b]","2_14_a@12,4","2_05_a@12,6","3_10_a@11,3[2_03_e_a]","4_14_a@11,8[4_02_e_a+4_08_e_a]","1_06_a@10,3[1_05_e_b+5_02_e_a]","1_02_a@10,4[3_08_e_a+2_01_e_b]","4_11_a@10,8[4_04_e_a+1_01_e_a]"], + "bonds": ["deputShip 2/40","emptyShip 1/12","kjeragShip 1/28","steadShip 0/26","swiftShip 1/18","victoriaShip 1/30"] } } }, @@ -99,62 +99,65 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m03","bossId":"boss_5","hiddenBossId":"boss_8","factions":["TIMES","DOT","INVISIBLE"]}, - "end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":93088,"virtualMs":106900}, - "rng": {"setup":25,"shop":207,"waves":0,"draft":10,"bots":8483,"meta":14}, + "end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":26,"bossPoolLeft":0,"virtualMs":112800}, + "rng": {"setup":25,"shop":233,"waves":0,"draft":10,"bots":8923,"meta":6}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,27,4,0,0,1,2,"cc1bac8f",0,"sargonShip 0/2, soloShip 1/0",5,5,0,4]}, {"round":2,"ai_0":[1,27,5,0,0,1,4,"eee78fe4",0,"kazimierzShip 0/2, sargonShip 0/4, soloShip 1/0, swiftShip 0/2",10,10,0,9]}, {"round":3,"ai_0":[1,27,6,0,0,1,6,"a5dafe29",1,"kazimierzShip 0/2, sargonShip 0/8, soloShip 1/1, swiftShip 0/2",8,8,0,15]}, {"round":4,"ai_0":[1,27,7,0,0,2,8,"3fbb6d23",2,"kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, soloShip 1/2, steadShip 0/2, swiftShip 0/4",8,8,0,22]}, - {"round":5,"ai_0":[1,27,8,0,0,3,8,"e6160244",3,"kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, soloShip 1/3, steadShip 0/2, swiftShip 0/4",21,21,0,30]}, + { + "round": 5, + "ai_0": [1,27,8,0,2,2,8,"7df835b1",9,"kazimierzShip 0/8, kjeragShip 0/2, preciShip 1/0, sargonShip 0/8, siracusaShip 1/6, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 0/8",21,21,0,29] + }, { "round": 6, - "ai_0": [1,27,9,0,0,3,8,"83353941",23,"deputShip 1/2, kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/8, soloShip 1/9, steadShip 1/4, swiftShip 0/4",64,64,0,39] + "ai_0": [1,27,11,0,0,3,8,"bc2d0f8e",27,"kazimierzShip 0/8, kjeragShip 0/2, preciShip 1/3, sargonShip 0/8, siracusaShip 1/12, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/9",64,64,0,39] }, { "round": 7, - "ai_0": [1,27,10,0,0,4,8,"6fa9839b",38,"deputShip 1/4, kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/16, soloShip 1/12, steadShip 1/6, swiftShip 0/4",28,28,0,49] + "ai_0": [1,27,10,0,0,4,8,"3327405a",38,"kazimierzShip 0/8, kjeragShip 0/2, preciShip 1/6, sargonShip 0/8, siracusaShip 1/18, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/11",28,28,0,51] }, { "round": 8, - "ai_0": [1,27,11,0,0,4,8,"9642d3a4",62,"deputShip 1/6, emptyShip 1/2, kazimierzShip 0/8, kjeragShip 1/9, preciShip 0/5, sargonShip 0/8, siracusaShip 1/31, soloShip 1/14, steadShip 0/6, swiftShip 0/8",19,19,0,60] + "ai_0": [1,27,11,0,0,4,8,"d22ead42",61,"kazimierzShip 0/8, kjeragShip 0/2, preciShip 2/14, sargonShip 0/8, siracusaShip 1/26, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/18",19,19,0,61] }, { "round": 9, - "ai_0": [1,27,12,0,0,4,8,"765a4a23",99,"arcaneShip 0/2, deputShip 1/8, emptyShip 1/4, kazimierzShip 0/8, kjeragShip 0/14, preciShip 0/12, sargonShip 0/8, siracusaShip 1/53, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/16",18,18,0,73] + "ai_0": [1,27,12,0,0,4,8,"00aa8624",97,"arcaneShip 1/8, kazimierzShip 0/8, kjeragShip 0/2, preciShip 1/23, sargonShip 0/8, siracusaShip 1/40, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/23",18,18,0,73] }, { "round": 10, - "ai_0": [1,27,12,0,0,5,8,"44e95a01",112,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/3, preciShip 0/12, sargonShip 0/8, siracusaShip 2/69, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",24,24,0,87] + "ai_0": [1,27,12,0,0,5,8,"8c4162aa",117,"arcaneShip 1/8, kazimierzShip 0/8, kjeragShip 0/2, preciShip 2/32, sargonShip 0/8, siracusaShip 1/46, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/28",24,24,0,85] }, { "round": 11, - "ai_0": [1,27,12,0,0,5,8,"12000646",141,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/6, preciShip 0/12, sargonShip 0/8, siracusaShip 2/95, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",24,24,0,98] + "ai_0": [1,27,12,0,0,5,8,"051e59d5",173,"arcaneShip 1/14, kazimierzShip 0/10, kjeragShip 0/2, preciShip 2/43, sargonShip 0/8, siracusaShip 1/78, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/35",24,24,0,96] }, { "round": 12, - "ai_0": [1,17,12,0,0,6,8,"03cea672",154,"arcaneShip 0/2, deputShip 0/8, emptyShip 0/4, kazimierzShip 0/8, kjeragShip 0/14, miraShip 1/9, preciShip 0/12, sargonShip 0/8, siracusaShip 2/105, soloShip 1/16, steadShip 0/6, suntShip 1/2, swiftShip 1/22",20,31,11,109] + "ai_0": [1,26,12,0,0,6,8,"95e59cfc",213,"arcaneShip 1/14, kazimierzShip 0/10, kjeragShip 0/2, preciShip 2/54, sargonShip 0/8, siracusaShip 1/102, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/40",30,31,1,109] }, { "round": 13, - "ai_0": [1,17,12,0,0,6,8,"1cf42739",230,"arcaneShip 0/2, deputShip 2/12, emptyShip 1/8, kazimierzShip 0/16, kjeragShip 0/22, miraShip 1/13, preciShip 0/12, sargonShip 0/8, siracusaShip 1/129, soloShip 1/20, steadShip 0/6, suntShip 1/6, swiftShip 1/42",115,115,0,121] + "ai_0": [1,26,12,0,0,6,8,"168e12ee",335,"arcaneShip 2/18, kazimierzShip 0/10, kjeragShip 0/2, preciShip 2/59, sargonShip 0/8, siracusaShip 1/210, soloShip 1/3, steadShip 0/2, suntShip 1/0, swiftShip 1/45",115,115,0,121] } ], "players": { "ai_0": { "alive": true, - "victory": false, - "roundsPassed": 13, + "victory": true, + "roundsPassed": 15, "eliminatedRound": null, - "lp": 0, + "lp": 26, "bandId": "band_ermengard", - "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, + "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 0, - "reward": 320, - "stats": {"activatedLayers":290,"bossDamage":296912,"dmgDealt":2081142,"gold":131,"itemsEquipped":23,"kills":383,"leaks":11,"lpLost":10,"merges":5,"perfectRounds":12,"refreshes":15}, - "lineup": ["2_14_a@12,5","5_10_a@12,7[4_07_e_a+3_08_e_b]","5_20_b@11,5[6_09_e_a+5_02_e_b]","3_20_b@11,8[3_06_e_a+4_08_e_a]","6_11_a@10,3[4_12_e_a+5_02_e_b]","3_18_a@10,8[2_04_e_b+4_05_e_b]","6_19_a@9,4[4_04_e_a+4_10_e_a]","1_08_a@9,5[1_01_e_a+1_02_e_a]"], - "bonds": ["arcaneShip 0/2","deputShip 1/14","emptyShip 1/10","kazimierzShip 0/16","kjeragShip 1/24","maniShip 1/2","miraShip 1/15","preciShip 0/12","sargonShip 0/8","siracusaShip 1/151","soloShip 1/22","steadShip 0/6","suntShip 1/8","swiftShip 1/44"] + "reward": 400, + "stats": {"activatedLayers":398,"bossDamage":1327500,"dmgDealt":3282365,"gold":144,"itemsEquipped":16,"kills":384,"leaks":1,"lpLost":1,"merges":4,"perfectRounds":12,"refreshes":18}, + "lineup": ["6_08_a@12,8","1_17_b@11,7","4_14_a@11,8[5_03_e_a]","6_18_a@10,4[3_02_e_a+3_05_e_a]","4_10_a@10,5[3_02_e_b+3_08_e_b]","6_02_a@10,6","1_19_b@10,8[4_05_e_a]","1_08_a@9,9[1_01_e_a+1_02_e_b]"], + "bonds": ["arcaneShip 2/34","kazimierzShip 0/10","kjeragShip 0/2","miraShip 1/3","preciShip 2/73","sargonShip 0/8","siracusaShip 1/230","soloShip 1/3","steadShip 0/2","suntShip 1/0","swiftShip 1/55","yanShip 0/6"] } } }, @@ -168,8 +171,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m02","bossId":"boss_5","hiddenBossId":"boss_10","factions":["ELEMENT","DOT","REFLECTION"]}, - "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":25,"bossPoolLeft":0,"virtualMs":106900}, - "rng": {"setup":25,"shop":227,"waves":0,"draft":10,"bots":10157,"meta":82}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":2,"bossPoolLeft":0,"virtualMs":106900}, + "rng": {"setup":25,"shop":243,"waves":0,"draft":10,"bots":10157,"meta":82}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,29,4,0,0,1,2,"30936985",0,"",5,5,0,4]}, @@ -181,39 +184,39 @@ }, { "round": 5, - "ai_0": [1,26,8,0,0,3,8,"adfde65f",11,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/2, steadShip 1/4, swiftShip 1/5",16,19,3,30] + "ai_0": [1,25,8,0,0,3,8,"0b0944dc",11,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/2, steadShip 1/4, swiftShip 1/5",15,19,4,29] }, { "round": 6, - "ai_0": [1,26,9,0,1,3,8,"e3b6b0ab",16,"arcaneShip 0/4, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/4, sargonShip 0/2, steadShip 2/4, swiftShip 1/8",21,21,0,38] + "ai_0": [1,25,9,0,1,3,8,"d0c6b512",10,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, raidShip 1/3, sargonShip 0/2, soloShip 1/0, steadShip 0/4, swiftShip 1/5",21,21,0,37] }, { "round": 7, - "ai_0": [1,26,11,0,2,4,8,"e419f5a0",18,"arcaneShip 0/4, kazimierzShip 1/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/4, sargonShip 0/2, soloShip 1/0, steadShip 2/4, swiftShip 1/8",28,28,0,50] + "ai_0": [1,25,11,0,2,4,8,"a9801735",13,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, raidShip 1/6, sargonShip 0/2, soloShip 1/0, steadShip 0/4, swiftShip 1/5",28,28,0,49] }, { "round": 8, - "ai_0": [1,26,13,0,1,4,8,"2b5d9514",26,"arcaneShip 1/8, kazimierzShip 1/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 0/4, sargonShip 0/2, soloShip 1/0, steadShip 2/8, swiftShip 1/8",20,20,0,62] + "ai_0": [1,25,13,0,1,4,8,"ea5364fe",17,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 0/2, raidShip 0/6, sargonShip 0/2, soloShip 1/0, steadShip 2/12, suntShip 1/0, swiftShip 1/5",20,20,0,63] }, { "round": 9, - "ai_0": [1,26,13,0,0,4,8,"3198e188",72,"arcaneShip 1/18, kazimierzShip 0/2, kjeragShip 1/13, lateranoShip 0/3, preciShip 2/23, sargonShip 0/2, soloShip 1/0, steadShip 0/8, suntShip 1/0, swiftShip 1/18",16,16,0,76] + "ai_0": [1,25,13,0,0,4,8,"0b9ca3f1",34,"arcaneShip 0/2, emptyShip 1/0, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/18, raidShip 0/6, sargonShip 0/2, soloShip 1/0, steadShip 2/16, suntShip 1/0, swiftShip 0/5",16,16,0,77] }, { "round": 10, - "ai_0": [1,26,12,0,1,5,8,"7ed90b72",94,"arcaneShip 1/20, kazimierzShip 0/2, kjeragShip 1/13, lateranoShip 0/3, preciShip 2/33, sargonShip 0/2, soloShip 1/0, steadShip 0/8, suntShip 1/0, swiftShip 1/28, visiShip 1/0",31,31,0,89] + "ai_0": [1,25,12,0,1,5,8,"8faacf7c",62,"arcaneShip 0/2, emptyShip 1/0, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/42, raidShip 0/6, sargonShip 0/2, soloShip 1/0, steadShip 2/20, suntShip 1/0, swiftShip 0/5",31,31,0,89] }, { "round": 11, - "ai_0": [1,26,13,0,2,5,8,"3ce86f2f",144,"arcaneShip 2/26, kazimierzShip 0/2, kjeragShip 1/13, lateranoShip 0/3, preciShip 2/38, raidShip 1/29, sargonShip 0/2, steadShip 0/8, suntShip 1/0, swiftShip 1/33, visiShip 1/5",23,23,0,101] + "ai_0": [1,22,13,0,1,5,8,"759088cc",40,"arcaneShip 0/2, kazimierzShip 1/4, kjeragShip 0/8, lateranoShip 1/10, preciShip 0/42, raidShip 1/11, sargonShip 0/2, soloShip 1/0, steadShip 0/20, suntShip 1/0, swiftShip 1/7, visiShip 1/8",20,23,3,101] }, { "round": 12, - "ai_0": [1,25,14,0,2,6,8,"97ed3953",182,"arcaneShip 2/28, kazimierzShip 0/2, kjeragShip 1/13, lateranoShip 0/3, preciShip 2/43, raidShip 1/50, sargonShip 0/2, steadShip 0/8, suntShip 1/0, swiftShip 1/38, visiShip 1/10",44,45,1,114] + "ai_0": [1,12,13,0,2,6,8,"f88a55c8",86,"arcaneShip 0/2, kazimierzShip 1/4, kjeragShip 0/8, lateranoShip 1/24, preciShip 0/42, raidShip 1/19, sargonShip 0/2, soloShip 1/0, steadShip 1/20, suntShip 1/0, swiftShip 0/7, visiShip 1/19",34,45,11,115] }, { "round": 13, - "ai_0": [1,25,14,0,2,6,8,"37aee47d",232,"arcaneShip 2/34, kazimierzShip 0/2, kjeragShip 1/13, lateranoShip 0/3, preciShip 2/48, raidShip 1/79, sargonShip 0/2, steadShip 0/8, suntShip 1/0, swiftShip 1/43, visiShip 1/15",35,35,0,128] + "ai_0": [1,2,14,0,2,6,8,"bdd2cfd8",106,"arcaneShip 0/2, kazimierzShip 1/4, kjeragShip 0/8, lateranoShip 1/31, preciShip 0/42, raidShip 1/24, sargonShip 0/2, soloShip 1/0, steadShip 1/20, suntShip 1/0, swiftShip 0/7, visiShip 1/27",14,35,21,129] } ], "players": { @@ -222,14 +225,14 @@ "victory": true, "roundsPassed": 14, "eliminatedRound": null, - "lp": 25, + "lp": 2, "bandId": "band_ducklord", "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 0, "reward": 400, - "stats": {"activatedLayers":268,"bossDamage":390000,"dmgDealt":2637590,"gold":140,"itemsEquipped":13,"kills":278,"leaks":4,"lpLost":4,"merges":5,"perfectRounds":11,"refreshes":18}, - "lineup": ["1_17_b@12,5","4_14_b@11,4","3_11_a@11,5[1_05_e_a+3_08_e_a]","5_07_a@11,6[4_02_e_a+4_05_e_a]","4_10_a@10,3","1_02_b@10,4[4_12_e_a+4_06_e_a]","5_14_a@10,5[1_05_e_b+1_05_e_b]","4_04_a@10,6"], - "bonds": ["arcaneShip 2/40","kazimierzShip 0/2","kjeragShip 1/13","lateranoShip 0/3","preciShip 2/58","raidShip 1/84","sargonShip 0/2","steadShip 0/8","suntShip 1/0","swiftShip 1/53","visiShip 1/20"] + "stats": {"activatedLayers":106,"bossDamage":390000,"dmgDealt":2324575,"gold":142,"itemsEquipped":10,"kills":248,"leaks":39,"lpLost":27,"merges":3,"perfectRounds":9,"refreshes":21}, + "lineup": ["5_10_a@12,7[3_11_e_a]","1_01_b@11,4","3_17_b@11,5[2_01_e_a+4_08_e_a]","6_17_a@11,6","3_05_a@11,7","2_18_a@10,3","4_01_a@10,4[1_05_e_a+4_10_e_a]","5_01_a@10,6[4_04_e_a+4_06_e_a]"], + "bonds": ["arcaneShip 0/2","kazimierzShip 1/4","kjeragShip 0/8","lateranoShip 1/31","preciShip 0/42","raidShip 1/24","sargonShip 0/2","soloShip 1/0","steadShip 2/20","suntShip 1/0","swiftShip 0/7","visiShip 1/27"] } } }, @@ -243,62 +246,65 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m03","bossId":"boss_5","hiddenBossId":"boss_8","factions":["TIMES","DOT","INVISIBLE"]}, - "end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":94716,"virtualMs":107800}, - "rng": {"setup":25,"shop":201,"waves":0,"draft":42,"bots":8376,"meta":2}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":true,"hiddenCleared":false,"reason":"victory","teamLp":0,"bossPoolLeft":2633655,"virtualMs":113700}, + "rng": {"setup":25,"shop":254,"waves":0,"draft":42,"bots":8253,"meta":13}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,27,4,0,0,1,2,"cc1bac8f",0,"sargonShip 0/2, soloShip 1/0",5,5,0,4]}, {"round":2,"ai_0":[1,27,5,0,0,1,4,"eee78fe4",0,"kazimierzShip 0/2, sargonShip 0/4, soloShip 1/0, swiftShip 0/2",10,10,0,9]}, {"round":3,"ai_0":[1,27,6,0,1,1,6,"d5f9d956",1,"kazimierzShip 0/2, sargonShip 0/8, soloShip 1/1, swiftShip 0/2",11,11,0,15]}, {"round":4,"ai_0":[1,27,8,0,1,2,8,"247bff16",4,"kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, soloShip 1/1, steadShip 1/3, swiftShip 0/4",11,11,0,23]}, - {"round":5,"ai_0":[1,27,9,0,0,3,8,"8f1605f3",5,"kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, soloShip 1/1, steadShip 1/4, swiftShip 0/4",21,21,0,31]}, + { + "round": 5, + "ai_0": [1,27,9,0,0,3,8,"98f10fe1",11,"kazimierzShip 0/8, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/6, soloShip 1/1, steadShip 1/4, suntShip 1/0, swiftShip 0/8",21,21,0,32] + }, { "round": 6, - "ai_0": [1,27,9,0,0,3,8,"3bdfd652",23,"deputShip 1/2, kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/8, soloShip 1/3, steadShip 1/10, swiftShip 0/4",56,56,0,40] + "ai_0": [1,27,9,0,0,3,8,"86328c72",38,"kazimierzShip 0/8, kjeragShip 0/2, raidShip 1/12, sargonShip 0/8, siracusaShip 1/17, soloShip 1/1, steadShip 1/8, suntShip 1/0, swiftShip 0/8",56,56,0,41] }, { "round": 7, - "ai_0": [1,27,10,0,0,4,8,"1e743cfe",34,"deputShip 1/4, kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/12, soloShip 1/5, steadShip 1/13, swiftShip 0/4",28,28,0,50] + "ai_0": [1,27,10,0,0,4,8,"c990b80e",64,"kazimierzShip 0/8, kjeragShip 0/2, raidShip 1/28, sargonShip 0/8, siracusaShip 1/23, soloShip 1/1, steadShip 1/12, suntShip 1/0, swiftShip 0/8",28,28,0,52] }, { "round": 8, - "ai_0": [1,23,11,0,0,4,8,"6960d5f9",66,"deputShip 1/6, emptyShip 1/2, kazimierzShip 0/8, kjeragShip 1/9, preciShip 0/5, sargonShip 0/8, siracusaShip 1/27, soloShip 1/7, steadShip 1/15, swiftShip 0/8",15,19,4,61] + "ai_0": [1,27,11,0,0,4,8,"36478f07",87,"kazimierzShip 0/8, kjeragShip 0/2, raidShip 1/40, sargonShip 0/8, siracusaShip 1/33, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/13",19,19,0,62] }, { "round": 9, - "ai_0": [1,23,12,0,0,4,8,"ee973cc6",102,"arcaneShip 1/10, deputShip 1/8, emptyShip 1/4, kazimierzShip 0/8, kjeragShip 1/16, preciShip 1/14, sargonShip 0/8, siracusaShip 1/41, soloShip 1/9, steadShip 0/15, swiftShip 0/8",34,34,0,75] + "ai_0": [1,27,12,0,0,4,8,"0cd05d6c",147,"kazimierzShip 1/20, kjeragShip 0/2, preciShip 1/9, raidShip 1/54, sargonShip 0/8, siracusaShip 1/45, soloShip 1/1, steadShip 0/12, swiftShip 1/18",34,34,0,73] }, { "round": 10, - "ai_0": [1,23,12,0,0,5,8,"b4c89cd7",128,"arcaneShip 1/12, deputShip 1/10, emptyShip 1/6, kazimierzShip 0/8, kjeragShip 1/23, preciShip 1/21, sargonShip 0/8, siracusaShip 1/45, soloShip 1/11, steadShip 0/15, swiftShip 0/8",24,24,0,86] + "ai_0": [1,27,12,0,0,5,8,"e44f0961",194,"kazimierzShip 1/36, kjeragShip 0/2, preciShip 1/18, raidShip 1/67, sargonShip 0/8, siracusaShip 1/49, soloShip 1/1, steadShip 0/12, swiftShip 1/23",24,24,0,85] }, { "round": 11, - "ai_0": [1,20,12,0,2,5,8,"1d52aad9",172,"arcaneShip 1/20, deputShip 1/12, emptyShip 1/8, kazimierzShip 0/8, kjeragShip 1/32, preciShip 1/28, sargonShip 0/8, siracusaShip 1/59, soloShip 1/13, steadShip 0/17, swiftShip 0/8",31,34,3,97] + "ai_0": [1,27,12,0,1,5,8,"252aff38",242,"kazimierzShip 1/52, kjeragShip 0/2, preciShip 1/26, raidShip 1/77, sargonShip 0/8, siracusaShip 1/63, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/23",34,34,0,96] }, { "round": 12, - "ai_0": [1,10,14,0,0,6,8,"4dd4e915",196,"arcaneShip 0/20, deputShip 1/14, emptyShip 1/10, kazimierzShip 0/8, kjeragShip 1/39, preciShip 1/40, sargonShip 0/8, siracusaShip 1/63, soloShip 1/15, steadShip 0/17, swiftShip 1/15",12,31,19,111] + "ai_0": [1,27,13,0,0,6,8,"c9c17eab",296,"arcaneShip 1/0, kazimierzShip 1/68, kjeragShip 0/2, preciShip 1/34, raidShip 1/101, sargonShip 0/8, siracusaShip 1/69, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/23",31,31,0,111] }, { "round": 13, - "ai_0": [1,10,12,0,2,6,8,"42f17176",230,"arcaneShip 1/26, deputShip 0/14, emptyShip 1/10, kazimierzShip 0/8, kjeragShip 1/39, preciShip 2/45, sargonShip 0/8, siracusaShip 1/75, soloShip 1/15, steadShip 0/17, swiftShip 1/20",115,115,0,126] + "ai_0": [1,27,12,0,0,6,8,"b6fe7f54",393,"arcaneShip 1/6, kazimierzShip 1/88, kjeragShip 0/2, miraShip 1/3, preciShip 1/52, raidShip 1/121, sargonShip 0/8, siracusaShip 1/89, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/33",115,115,0,125] } ], "players": { "ai_0": { "alive": true, - "victory": false, - "roundsPassed": 13, + "victory": true, + "roundsPassed": 14, "eliminatedRound": null, "lp": 0, "bandId": "band_ermengard", - "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, + "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 0, - "reward": 340, - "stats": {"activatedLayers":268,"bossDamage":685284,"dmgDealt":2273515,"gold":140,"itemsEquipped":16,"kills":391,"leaks":26,"lpLost":17,"merges":3,"perfectRounds":10,"refreshes":15}, - "lineup": ["6_12_a@12,5[5_01_e_a+5_02_e_a]","5_10_a@12,7[4_07_e_a+5_02_e_b]","4_14_a@11,8","3_11_a@10,3","5_14_a@9,4[3_08_e_a+3_11_e_a]","1_08_a@9,5[1_01_e_a+4_05_e_a]","6_02_a@9,6","4_10_a@9,8[4_08_e_a+4_10_e_b]"], - "bonds": ["arcaneShip 1/30","deputShip 0/14","emptyShip 1/10","kazimierzShip 0/16","kjeragShip 1/51","preciShip 2/50","sargonShip 0/8","siracusaShip 1/79","soloShip 1/15","steadShip 0/21","swiftShip 1/33","visiShip 1/0"] + "reward": 425, + "stats": {"activatedLayers":473,"bossDamage":1746345,"dmgDealt":3598968,"gold":151,"itemsEquipped":18,"kills":422,"leaks":0,"lpLost":0,"merges":4,"perfectRounds":13,"refreshes":21}, + "lineup": ["4_14_b@12,8[5_01_e_a+2_01_e_b]","3_19_a@11,7[3_02_e_b]","4_20_b@11,8[3_11_e_b+3_06_e_a]","4_10_a@10,6","6_17_a@10,8[4_10_e_b+4_05_e_a]","1_08_a@9,5[1_01_e_a+1_02_e_a]","5_07_a@9,6","1_19_b@9,9[3_08_e_a+3_01_e_a]"], + "bonds": ["arcaneShip 1/18","kazimierzShip 1/88","kjeragShip 0/2","miraShip 1/3","preciShip 1/72","raidShip 1/121","sargonShip 0/8","siracusaShip 1/117","soloShip 1/1","steadShip 0/12","suntShip 1/0","swiftShip 1/53"] } } }, @@ -312,8 +318,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m03","bossId":"boss_5","hiddenBossId":"boss_10","factions":["ELEMENT","DOT","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":176395,"virtualMs":107800}, - "rng": {"setup":25,"shop":279,"waves":0,"draft":42,"bots":8378,"meta":87}, + "end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":74282,"virtualMs":107800}, + "rng": {"setup":25,"shop":259,"waves":0,"draft":42,"bots":8650,"meta":93}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,29,4,0,0,1,2,"e4d89f1f",0,"",5,5,0,4]}, @@ -325,39 +331,39 @@ }, { "round": 5, - "ai_0": [1,24,9,0,0,3,8,"f1511763",11,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/2, steadShip 1/4, swiftShip 1/5",14,19,5,30] + "ai_0": [1,25,9,0,0,3,8,"f5ae8f4c",11,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/4, steadShip 1/4, swiftShip 1/5",15,19,4,31] }, { "round": 6, - "ai_0": [1,24,9,0,2,3,8,"79c07df2",16,"arcaneShip 0/4, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/4, sargonShip 0/2, steadShip 2/4, swiftShip 1/8",21,21,0,38] + "ai_0": [1,22,9,0,1,3,8,"cbad681a",7,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/4, soloShip 1/0, steadShip 0/4, swiftShip 1/5",18,21,3,40] }, { "round": 7, - "ai_0": [1,24,12,0,2,4,8,"ac4f9622",18,"arcaneShip 0/4, kazimierzShip 1/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/4, sargonShip 0/2, soloShip 1/0, steadShip 2/4, swiftShip 1/8",28,28,0,50] + "ai_0": [1,22,11,0,2,4,8,"5324db4f",9,"arcaneShip 1/2, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/2, sargonShip 0/4, soloShip 1/0, steadShip 0/8, swiftShip 1/5",28,28,0,53] }, { "round": 8, - "ai_0": [1,24,13,0,1,4,8,"c470c9bb",26,"arcaneShip 1/8, kazimierzShip 1/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 0/4, sargonShip 0/2, soloShip 1/0, steadShip 2/8, swiftShip 1/8",20,20,0,62] + "ai_0": [1,22,13,0,1,4,8,"13cec89e",36,"arcaneShip 1/8, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/11, sargonShip 1/17, soloShip 1/0, steadShip 0/8, suntShip 1/0, swiftShip 0/5",20,20,0,65] }, { "round": 9, - "ai_0": [1,17,13,0,1,4,8,"6ea41206",62,"arcaneShip 1/18, kazimierzShip 1/2, kjeragShip 1/13, lateranoShip 0/3, preciShip 1/13, sargonShip 0/2, soloShip 1/0, steadShip 1/8, suntShip 1/0, swiftShip 1/8",25,32,7,75] + "ai_0": [1,19,13,0,1,4,8,"a57f7bd8",92,"arcaneShip 1/14, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/27, sargonShip 1/33, siracusaShip 1/18, soloShip 1/0, steadShip 0/8, swiftShip 0/5",29,32,3,77] }, { "round": 10, - "ai_0": [1,17,13,0,1,5,8,"65d67e3b",72,"arcaneShip 1/24, kazimierzShip 1/4, kjeragShip 1/13, lateranoShip 0/3, preciShip 1/13, sargonShip 0/2, soloShip 1/0, steadShip 1/8, suntShip 1/0, swiftShip 1/10",30,30,0,88] + "ai_0": [1,19,13,0,1,5,8,"95219185",114,"arcaneShip 1/14, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/38, sargonShip 1/44, siracusaShip 1/18, soloShip 1/0, steadShip 0/8, swiftShip 0/5",30,30,0,90] }, { "round": 11, - "ai_0": [1,9,13,0,1,5,8,"0c556348",74,"arcaneShip 1/28, kazimierzShip 1/8, kjeragShip 1/13, lateranoShip 0/3, preciShip 2/17, sargonShip 0/2, soloShip 1/0, steadShip 1/8, suntShip 1/0, swiftShip 0/10",16,24,8,101] + "ai_0": [1,19,13,0,1,5,8,"b7cde340",176,"arcaneShip 2/20, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/53, sargonShip 1/59, siracusaShip 1/44, steadShip 0/8, swiftShip 0/5",24,24,0,101] }, { "round": 12, - "ai_0": [1,9,13,0,2,6,8,"30f54d4c",162,"arcaneShip 2/34, kazimierzShip 1/36, kjeragShip 1/29, lateranoShip 0/3, preciShip 1/25, sargonShip 0/2, steadShip 1/8, suntShip 1/0, swiftShip 1/30",45,45,0,115] + "ai_0": [1,9,13,0,0,6,8,"ed0c6c9c",240,"arcaneShip 2/22, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/77, sargonShip 1/83, siracusaShip 1/58, steadShip 0/8, swiftShip 0/5",32,45,13,113] }, { "round": 13, - "ai_0": [1,3,14,0,0,6,8,"09807249",200,"arcaneShip 1/40, kazimierzShip 1/48, kjeragShip 1/37, lateranoShip 0/3, preciShip 2/29, sargonShip 0/2, steadShip 1/8, suntShip 1/0, swiftShip 1/38",23,29,6,129] + "ai_0": [1,3,12,0,0,6,8,"57991d13",319,"arcaneShip 2/28, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, miraShip 1/0, preciShip 1/96, sargonShip 1/111, siracusaShip 1/84, steadShip 0/8, swiftShip 0/5",23,29,6,124] } ], "players": { @@ -371,9 +377,9 @@ "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 0, "reward": 340, - "stats": {"activatedLayers":163,"bossDamage":603605,"dmgDealt":2769206,"gold":141,"itemsEquipped":11,"kills":273,"leaks":26,"lpLost":26,"merges":6,"perfectRounds":9,"refreshes":25}, - "lineup": ["1_17_b@12,5","3_17_a@12,6","4_20_a@12,7","4_10_b@10,3","2_18_a@10,5[4_11_e_a+1_05_e_b]","1_19_b@10,7","6_17_a@10,8[3_07_e_a+3_04_e_a]","6_19_a@9,5[4_02_e_b+5_03_e_a]"], - "bonds": ["arcaneShip 1/48","kazimierzShip 2/48","kjeragShip 0/37","lateranoShip 0/3","preciShip 1/29","sargonShip 0/2","soloShip 1/0","steadShip 0/8","suntShip 1/0","swiftShip 1/38"] + "stats": {"activatedLayers":363,"bossDamage":705718,"dmgDealt":2862087,"gold":137,"itemsEquipped":16,"kills":275,"leaks":29,"lpLost":26,"merges":4,"perfectRounds":8,"refreshes":23}, + "lineup": ["5_20_a@12,5[3_07_e_a]","5_02_a@12,6[2_04_e_a+3_08_e_b]","6_12_a@12,7","6_05_a@11,8","4_10_b@10,3[3_11_e_a]","4_16_a@10,7[4_02_e_a]","1_12_b@10,8[4_10_e_b+1_05_e_b]","5_07_a@9,8"], + "bonds": ["arcaneShip 1/40","kazimierzShip 0/2","kjeragShip 0/8","lateranoShip 0/3","miraShip 1/0","preciShip 2/96","raidShip 1/0","sargonShip 1/111","siracusaShip 1/116","steadShip 0/8","suntShip 1/0","swiftShip 0/5"] } } }, @@ -387,58 +393,65 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m03","bossId":"boss_5","hiddenBossId":"boss_8","factions":["TIMES","DOT","INVISIBLE"]}, - "end": {"victory":false,"roundsPassed":11,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":94500}, - "rng": {"setup":25,"shop":142,"waves":0,"draft":42,"bots":7385,"meta":1}, + "end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":2424900,"virtualMs":107800}, + "rng": {"setup":25,"shop":191,"waves":0,"draft":42,"bots":8021,"meta":6}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,27,4,0,0,1,2,"cc1bac8f",0,"sargonShip 0/2, soloShip 1/0",5,5,0,4]}, {"round":2,"ai_0":[1,27,5,0,0,1,4,"eee78fe4",0,"kazimierzShip 0/2, sargonShip 0/4, soloShip 1/0, swiftShip 0/2",10,10,0,9]}, {"round":3,"ai_0":[1,27,6,0,1,1,6,"d5f9d956",1,"kazimierzShip 0/2, sargonShip 0/8, soloShip 1/1, swiftShip 0/2",11,11,0,15]}, {"round":4,"ai_0":[1,27,8,0,1,2,8,"247bff16",4,"kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, soloShip 1/1, steadShip 1/3, swiftShip 0/4",11,11,0,23]}, - {"round":5,"ai_0":[1,26,9,0,0,3,8,"5fdaa5e6",5,"kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, soloShip 1/1, steadShip 1/4, swiftShip 0/4",20,21,1,31]}, + { + "round": 5, + "ai_0": [1,27,9,0,0,3,8,"1ca911b7",9,"kazimierzShip 0/8, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/4, soloShip 1/1, steadShip 1/4, suntShip 1/0, swiftShip 0/8",21,21,0,32] + }, { "round": 6, - "ai_0": [1,26,9,0,0,3,8,"3bdfd652",23,"deputShip 1/2, kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/8, soloShip 1/3, steadShip 1/10, swiftShip 0/4",56,56,0,40] + "ai_0": [1,27,9,0,0,3,8,"0e30e70c",34,"kazimierzShip 0/8, kjeragShip 0/2, raidShip 1/11, sargonShip 0/8, siracusaShip 1/15, soloShip 1/1, steadShip 1/7, suntShip 1/0, swiftShip 0/8",56,56,0,41] }, { "round": 7, - "ai_0": [1,26,10,0,0,4,8,"1e743cfe",38,"deputShip 1/4, kazimierzShip 0/4, kjeragShip 0/2, sargonShip 0/8, siracusaShip 1/16, soloShip 1/5, steadShip 1/13, swiftShip 0/4",28,28,0,50] + "ai_0": [1,27,10,0,0,4,8,"c990b80e",62,"kazimierzShip 0/8, kjeragShip 0/2, raidShip 1/28, sargonShip 0/8, siracusaShip 1/21, soloShip 1/1, steadShip 1/12, suntShip 1/0, swiftShip 0/8",28,28,0,52] }, { "round": 8, - "ai_0": [1,21,11,0,0,4,8,"214665a0",70,"deputShip 1/6, emptyShip 1/2, kazimierzShip 0/8, kjeragShip 1/9, preciShip 0/5, sargonShip 0/8, siracusaShip 1/31, soloShip 1/7, steadShip 1/15, swiftShip 0/8",14,19,5,61] + "ai_0": [1,25,11,0,0,4,8,"36478f07",84,"kazimierzShip 0/8, kjeragShip 0/2, raidShip 1/41, sargonShip 0/8, siracusaShip 1/29, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/13",17,19,2,62] }, { "round": 9, - "ai_0": [1,21,12,0,0,4,8,"b598a45c",106,"arcaneShip 1/10, deputShip 1/8, emptyShip 1/4, kazimierzShip 0/8, kjeragShip 1/16, preciShip 1/14, sargonShip 0/8, siracusaShip 1/45, soloShip 1/9, steadShip 0/15, swiftShip 0/8",34,34,0,75] + "ai_0": [1,25,12,0,0,4,8,"0cd05d6c",153,"kazimierzShip 1/24, kjeragShip 0/2, preciShip 1/9, raidShip 1/58, sargonShip 0/8, siracusaShip 1/43, soloShip 1/1, steadShip 0/12, swiftShip 1/18",34,34,0,73] }, { "round": 10, - "ai_0": [1,11,12,0,0,5,8,"317b28ba",132,"arcaneShip 1/12, deputShip 1/10, emptyShip 1/6, kazimierzShip 0/8, kjeragShip 1/23, preciShip 1/21, sargonShip 0/8, siracusaShip 1/49, soloShip 1/11, steadShip 0/15, swiftShip 0/8",11,24,13,86] + "ai_0": [1,25,12,0,0,5,8,"985bd5f5",181,"kazimierzShip 1/32, kjeragShip 0/2, preciShip 1/18, raidShip 1/64, sargonShip 0/8, siracusaShip 1/43, soloShip 1/1, steadShip 0/12, swiftShip 1/23",24,24,0,85] }, { "round": 11, - "ai_0": [1,1,12,0,0,5,8,"e69c468d",176,"arcaneShip 1/20, deputShip 1/12, emptyShip 1/8, kazimierzShip 0/8, kjeragShip 1/32, preciShip 1/28, sargonShip 0/8, siracusaShip 1/63, soloShip 1/13, steadShip 0/17, swiftShip 0/8",19,34,15,97] + "ai_0": [1,21,12,0,1,5,8,"e1c9f2cb",232,"kazimierzShip 1/48, kjeragShip 0/2, preciShip 1/26, raidShip 1/73, sargonShip 0/8, siracusaShip 1/61, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/23",30,34,4,96] }, { "round": 12, - "ai_0": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/22, deputShip 0/14, emptyShip 0/10, kazimierzShip 0/8, kjeragShip 0/39, preciShip 0/35, sargonShip 0/8, siracusaShip 0/67, soloShip 0/15, steadShip 0/17, swiftShip 0/8",9,31,22,109] + "ai_0": [1,12,13,0,0,6,8,"53598e5f",286,"arcaneShip 1/0, kazimierzShip 1/64, kjeragShip 0/2, preciShip 1/34, raidShip 1/97, sargonShip 0/8, siracusaShip 1/67, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/23",22,31,9,111] + }, + { + "round": 13, + "ai_0": [1,12,12,0,0,6,8,"065b4c96",375,"arcaneShip 1/6, kazimierzShip 1/80, kjeragShip 0/2, miraShip 1/3, preciShip 1/52, raidShip 1/113, sargonShip 0/8, siracusaShip 1/87, soloShip 1/1, steadShip 0/12, suntShip 1/0, swiftShip 1/33",115,115,0,125] } ], "players": { "ai_0": { - "alive": false, + "alive": true, "victory": false, - "roundsPassed": 11, - "eliminatedRound": 12, + "roundsPassed": 13, + "eliminatedRound": null, "lp": 0, "bandId": "band_ermengard", - "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, + "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 0, - "reward": 255, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1653111,"gold":109,"itemsEquipped":10,"kills":228,"leaks":56,"lpLost":36,"merges":2,"perfectRounds":7,"refreshes":9}, - "lineup": [], - "bonds": ["arcaneShip 0/22","deputShip 0/14","emptyShip 0/10","kazimierzShip 0/8","kjeragShip 0/39","preciShip 0/35","sargonShip 0/8","siracusaShip 0/67","soloShip 0/15","steadShip 0/17","swiftShip 0/8"] + "reward": 340, + "stats": {"activatedLayers":412,"bossDamage":575100,"dmgDealt":3381027,"gold":137,"itemsEquipped":16,"kills":404,"leaks":15,"lpLost":15,"merges":4,"perfectRounds":10,"refreshes":13}, + "lineup": ["4_14_b@12,5[2_01_e_a+5_01_e_a]","3_19_a@12,6[3_02_e_b]","4_20_b@11,8[3_11_e_b+3_06_e_a]","4_10_a@10,3","5_07_a@10,7","6_17_a@10,8[4_10_e_b]","1_08_a@9,5[1_01_e_a+1_02_e_a]","1_19_b@9,9[3_08_e_a+3_01_e_a]"], + "bonds": ["arcaneShip 1/12","kazimierzShip 1/80","kjeragShip 0/2","miraShip 0/3","preciShip 1/62","raidShip 1/113","sargonShip 0/8","siracusaShip 1/101","soloShip 1/1","steadShip 0/12","suntShip 1/0","swiftShip 1/43"] } } }, @@ -452,8 +465,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m03","bossId":"boss_5","hiddenBossId":"boss_10","factions":["ELEMENT","DOT","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":9,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":78800}, - "rng": {"setup":25,"shop":106,"waves":0,"draft":28,"bots":6031,"meta":53}, + "end": {"victory":false,"roundsPassed":7,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":63100}, + "rng": {"setup":25,"shop":88,"waves":0,"draft":14,"bots":4780,"meta":38}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"ai_0":[1,29,4,0,0,1,2,"e4d89f1f",0,"",5,5,0,4]}, @@ -465,43 +478,35 @@ }, { "round": 5, - "ai_0": [1,20,9,0,0,3,8,"f1511763",11,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/2, steadShip 1/4, swiftShip 1/5",12,19,7,30] + "ai_0": [1,19,9,0,0,3,8,"88d61dac",11,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/4, steadShip 1/4, swiftShip 1/5",11,19,8,31] }, { "round": 6, - "ai_0": [1,15,9,0,1,3,8,"12a1dcbb",16,"arcaneShip 0/4, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/4, sargonShip 0/2, steadShip 2/4, swiftShip 1/8",16,21,5,38] + "ai_0": [1,9,9,0,1,3,8,"cbad681a",7,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/2, sargonShip 0/4, soloShip 1/0, steadShip 0/4, swiftShip 1/5",9,21,12,40] }, { "round": 7, - "ai_0": [1,13,11,0,0,4,8,"932a36c9",18,"arcaneShip 0/4, kazimierzShip 1/2, kjeragShip 0/4, lateranoShip 0/3, preciShip 1/4, sargonShip 0/2, soloShip 1/0, steadShip 2/4, swiftShip 1/8",26,28,2,50] + "ai_0": [1,1,11,0,0,4,8,"3dc7e9d1",9,"arcaneShip 1/2, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 1/2, sargonShip 0/4, soloShip 1/0, steadShip 0/8, swiftShip 1/5",20,28,8,53] }, { "round": 8, - "ai_0": [1,12,11,0,0,4,8,"1d3e40c9",24,"arcaneShip 1/6, kazimierzShip 1/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 0/4, sargonShip 0/2, soloShip 1/0, steadShip 2/8, swiftShip 1/8",19,20,1,61] - }, - { - "round": 9, - "ai_0": [1,2,12,0,0,4,8,"7c48626c",61,"arcaneShip 1/14, kazimierzShip 1/2, kjeragShip 1/16, lateranoShip 0/3, preciShip 1/13, sargonShip 0/2, soloShip 1/0, steadShip 1/8, suntShip 1/0, swiftShip 1/8",17,32,15,74] - }, - { - "round": 10, - "ai_0": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/18, kazimierzShip 0/4, kjeragShip 0/16, lateranoShip 0/3, preciShip 0/13, sargonShip 0/2, steadShip 0/8, swiftShip 0/10",20,30,10,88] + "ai_0": [0,0,11,0,0,4,0,"811c9dc5",0,"arcaneShip 0/6, kazimierzShip 0/2, kjeragShip 0/8, lateranoShip 0/3, preciShip 0/20, sargonShip 0/26, steadShip 0/8, swiftShip 0/5",16,20,4,63] } ], "players": { "ai_0": { "alive": false, "victory": false, - "roundsPassed": 9, - "eliminatedRound": 10, + "roundsPassed": 7, + "eliminatedRound": 8, "lp": 0, "bandId": "band_ducklord", "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, "trophies": 0, - "reward": 191, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1256554,"gold":88,"itemsEquipped":8,"kills":143,"leaks":42,"lpLost":37,"merges":2,"perfectRounds":3,"refreshes":6}, + "reward": 149, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":677063,"gold":63,"itemsEquipped":6,"kills":89,"leaks":34,"lpLost":32,"merges":3,"perfectRounds":3,"refreshes":5}, "lineup": [], - "bonds": ["arcaneShip 0/18","kazimierzShip 0/4","kjeragShip 0/16","lateranoShip 0/3","preciShip 0/13","sargonShip 0/2","steadShip 0/8","swiftShip 0/10"] + "bonds": ["arcaneShip 0/6","kazimierzShip 0/2","kjeragShip 0/8","lateranoShip 0/3","preciShip 0/20","sargonShip 0/26","steadShip 0/8","swiftShip 0/5"] } } }, @@ -515,8 +520,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act2autochess_m04","bossId":"boss_3","hiddenBossId":"boss_8","factions":["FLY","TIMES","DOT"]}, - "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":58,"bossPoolLeft":0,"virtualMs":116900}, - "rng": {"setup":25,"shop":449,"waves":0,"draft":40,"bots":21634,"meta":8}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":46,"bossPoolLeft":0,"virtualMs":118400}, + "rng": {"setup":25,"shop":456,"waves":0,"draft":40,"bots":21608,"meta":4}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -537,52 +542,52 @@ { "round": 4, "ai_0": [1,45,7,0,0,2,8,"ace8bd51",8,"preciShip 0/10, sargonShip 0/10, siracusaShip 0/6, soloShip 1/6, suntShip 1/0, swiftShip 1/2",7,7,0,22], - "ai_1": [1,28,7,0,0,2,8,"8b1c5c6d",2,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/12, sargonShip 0/10, soloShip 1/0, swiftShip 1/2",7,7,0,22] + "ai_1": [1,28,7,0,0,2,8,"9ad12e68",4,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/2, preciShip 0/12, sargonShip 0/10, soloShip 1/0, steadShip 1/2, swiftShip 1/2",7,7,0,22] }, { "round": 5, - "ai_0": [1,45,8,0,0,3,8,"255bf6f9",10,"preciShip 0/10, sargonShip 0/10, siracusaShip 0/6, soloShip 1/6, suntShip 1/0, swiftShip 1/4",24,24,0,29], - "ai_1": [1,28,8,0,2,3,8,"92afa044",2,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/12, sargonShip 0/10, soloShip 1/0, swiftShip 1/2",24,24,0,30] + "ai_0": [1,45,8,0,0,3,8,"8b45e1cb",10,"preciShip 0/10, sargonShip 0/10, siracusaShip 0/6, soloShip 1/6, suntShip 1/0, swiftShip 1/4",24,24,0,30], + "ai_1": [1,28,8,0,0,3,8,"6b0e7ce6",4,"arcaneShip 0/2, kazimierzShip 0/2, kjeragShip 0/2, preciShip 0/12, sargonShip 0/10, soloShip 1/0, steadShip 1/2, swiftShip 1/2",24,24,0,30] }, { "round": 6, - "ai_0": [1,45,9,0,2,3,8,"91f7f108",36,"preciShip 0/10, sargonShip 0/10, siracusaShip 1/18, skillfulShip 1/0, soloShip 1/6, suntShip 1/0, swiftShip 1/12",28,28,0,37], - "ai_1": [1,28,11,0,2,3,8,"d8261a9b",12,"arcaneShip 1/7, kazimierzShip 0/5, preciShip 0/14, sargonShip 0/10, soloShip 1/0, swiftShip 1/5",28,28,0,41] + "ai_0": [1,45,9,0,2,3,8,"df405a06",36,"preciShip 0/10, sargonShip 0/10, siracusaShip 1/18, skillfulShip 1/0, soloShip 1/6, suntShip 1/0, swiftShip 1/12",28,28,0,38], + "ai_1": [1,28,9,0,1,3,8,"557c8e96",6,"arcaneShip 0/4, indomShip 1/0, kazimierzShip 0/2, kjeragShip 0/2, preciShip 0/14, sargonShip 0/10, soloShip 1/0, steadShip 2/4, swiftShip 1/2",28,28,0,39] }, { "round": 7, - "ai_0": [1,45,12,0,2,4,8,"f93a454e",46,"preciShip 0/10, sargonShip 0/10, siracusaShip 1/24, skillfulShip 1/2, soloShip 1/6, suntShip 1/0, swiftShip 1/14",34,34,0,50], - "ai_1": [1,28,12,0,1,4,8,"88383fad",18,"arcaneShip 1/7, kazimierzShip 1/6, preciShip 0/14, sargonShip 0/10, soloShip 1/0, swiftShip 1/5",34,34,0,53] + "ai_0": [1,45,12,0,2,4,8,"30049f83",45,"preciShip 0/10, raidShip 1/5, sargonShip 0/10, siracusaShip 1/18, skillfulShip 1/2, soloShip 1/6, suntShip 1/0, swiftShip 1/14",34,34,0,51], + "ai_1": [1,28,11,0,1,4,8,"663a7aa2",16,"arcaneShip 1/8, indomShip 1/0, kazimierzShip 0/2, kjeragShip 0/2, preciShip 0/18, sargonShip 0/10, soloShip 1/0, steadShip 1/6, suntShip 1/0, swiftShip 1/2",34,34,0,50] }, { "round": 8, - "ai_0": [1,45,13,0,0,4,8,"870d6315",68,"arcaneShip 2/14, preciShip 0/10, sargonShip 0/10, siracusaShip 1/28, skillfulShip 1/4, soloShip 1/6, suntShip 1/0, swiftShip 1/16",19,19,0,63], - "ai_1": [1,28,12,0,0,4,8,"5e8420e4",53,"arcaneShip 1/10, kazimierzShip 1/6, lateranoShip 1/8, preciShip 1/19, sargonShip 0/10, soloShip 1/0, swiftShip 1/10",19,19,0,64] + "ai_0": [1,45,13,0,0,4,8,"33b796a4",61,"arcaneShip 1/6, preciShip 0/10, raidShip 1/5, sargonShip 0/10, siracusaShip 1/24, skillfulShip 1/4, soloShip 1/6, suntShip 1/0, swiftShip 1/16",19,19,0,64], + "ai_1": [1,28,12,0,0,4,8,"c03c0a1a",51,"arcaneShip 1/8, indomShip 1/0, kazimierzShip 1/13, kjeragShip 0/2, preciShip 1/26, sargonShip 0/10, soloShip 1/0, steadShip 0/6, suntShip 1/0, swiftShip 1/4",19,19,0,61] }, { "round": 9, - "ai_0": [1,45,12,0,1,4,8,"b71d708f",86,"arcaneShip 2/22, preciShip 0/10, sargonShip 0/10, siracusaShip 1/34, skillfulShip 1/6, soloShip 1/6, suntShip 1/0, swiftShip 1/18",18,20,2,74], - "ai_1": [1,28,12,0,1,4,8,"b6ef2d78",59,"arcaneShip 2/14, kazimierzShip 1/6, lateranoShip 0/8, preciShip 1/24, sargonShip 0/10, soloShip 1/0, steadShip 1/0, swiftShip 1/15",20,20,0,76] + "ai_0": [1,41,12,0,1,4,8,"b05d4239",75,"arcaneShip 2/15, preciShip 0/10, raidShip 0/5, sargonShip 0/10, siracusaShip 1/30, skillfulShip 1/6, soloShip 1/6, suntShip 1/0, swiftShip 1/18",16,20,4,77], + "ai_1": [1,23,12,0,1,4,8,"ea817069",80,"arcaneShip 2/14, kazimierzShip 1/25, kjeragShip 0/2, preciShip 1/34, sargonShip 0/10, soloShip 1/0, steadShip 0/6, suntShip 1/0, swiftShip 1/7",15,20,5,73] }, { "round": 10, - "ai_0": [1,45,13,0,0,5,8,"e786a445",98,"arcaneShip 2/26, preciShip 0/10, sargonShip 0/10, siracusaShip 1/38, skillfulShip 1/8, soloShip 1/6, suntShip 1/0, swiftShip 1/20",8,8,0,87], - "ai_1": [1,28,13,0,0,5,8,"ed80e1d9",71,"arcaneShip 2/16, kazimierzShip 1/6, lateranoShip 0/8, preciShip 1/29, sargonShip 0/10, soloShip 1/0, steadShip 1/0, swiftShip 1/20",8,8,0,89] + "ai_0": [1,37,13,0,0,5,8,"29aea524",87,"arcaneShip 2/18, egirShip 1/28, preciShip 0/10, raidShip 1/5, sargonShip 0/10, siracusaShip 1/30, skillfulShip 0/8, soloShip 1/6, suntShip 1/0, swiftShip 0/20",4,8,4,89], + "ai_1": [1,19,13,0,0,5,8,"41242f43",104,"arcaneShip 2/18, kazimierzShip 1/37, kjeragShip 0/2, preciShip 1/42, sargonShip 0/10, soloShip 1/0, steadShip 0/6, suntShip 1/0, swiftShip 1/7",4,8,4,86] }, { "round": 11, - "ai_0": [1,45,12,0,2,5,8,"360613f3",118,"arcaneShip 2/34, miraShip 1/0, preciShip 0/10, sargonShip 0/10, siracusaShip 1/56, skillfulShip 0/10, soloShip 1/6, suntShip 1/0, swiftShip 1/22",23,23,0,99], - "ai_1": [1,28,12,0,0,5,8,"13f38ed9",131,"arcaneShip 2/28, kazimierzShip 1/22, kjeragShip 0/8, lateranoShip 0/8, preciShip 1/40, sargonShip 0/10, steadShip 1/0, suntShip 1/0, swiftShip 1/41",23,23,0,103] + "ai_0": [1,37,12,0,0,5,8,"c43f7697",147,"arcaneShip 1/23, egirShip 1/55, preciShip 0/10, raidShip 1/6, sargonShip 0/10, siracusaShip 1/32, skillfulShip 0/8, soloShip 1/6, suntShip 1/0, swiftShip 1/25",20,23,3,101], + "ai_1": [1,19,12,0,0,5,8,"812fd9eb",124,"arcaneShip 2/22, kazimierzShip 1/46, kjeragShip 0/2, preciShip 2/56, sargonShip 0/10, steadShip 0/6, suntShip 1/0, swiftShip 0/11",23,23,0,99] }, { "round": 12, - "ai_0": [1,45,14,0,0,6,8,"d434f9ef",168,"arcaneShip 2/46, miraShip 1/6, preciShip 0/10, sargonShip 0/10, siracusaShip 1/68, skillfulShip 1/18, soloShip 1/6, suntShip 1/0, swiftShip 1/24",105,105,0,113], - "ai_1": [1,28,12,0,0,6,8,"2e4f3094",157,"arcaneShip 2/28, kazimierzShip 1/30, kjeragShip 0/8, lateranoShip 0/8, preciShip 1/45, sargonShip 0/10, steadShip 1/0, suntShip 1/0, swiftShip 1/54",105,105,0,114] + "ai_0": [1,37,12,0,0,6,8,"e51a7992",179,"arcaneShip 1/26, egirShip 1/78, preciShip 0/10, raidShip 1/7, sargonShip 0/10, siracusaShip 1/32, skillfulShip 0/8, soloShip 1/6, suntShip 1/0, swiftShip 1/30",105,105,0,114], + "ai_1": [1,19,12,0,0,6,8,"c916c4e3",168,"arcaneShip 2/22, kazimierzShip 1/51, kjeragShip 0/2, preciShip 2/84, sargonShip 0/10, steadShip 0/6, suntShip 1/0, swiftShip 1/11",105,105,0,111] }, { "round": 13, - "ai_0": [1,36,12,0,2,6,8,"5deeb03a",214,"arcaneShip 2/70, miraShip 1/12, preciShip 0/10, sargonShip 0/10, siracusaShip 1/98, skillfulShip 1/28, soloShip 1/6, suntShip 1/0, swiftShip 0/32",25,34,9,125], - "ai_1": [1,22,12,0,0,6,8,"dc3e0431",189,"arcaneShip 2/34, kazimierzShip 1/38, kjeragShip 0/8, lateranoShip 0/8, preciShip 1/50, sargonShip 0/10, steadShip 1/0, suntShip 1/0, swiftShip 1/67",28,34,6,125] + "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",14,34,20,126], + "ai_1": [1,19,12,0,0,6,8,"187f8019",190,"arcaneShip 2/28, kazimierzShip 1/56, kjeragShip 0/2, preciShip 2/106, sargonShip 0/10, steadShip 0/6, suntShip 1/0, swiftShip 0/11",34,34,0,123] } ], "players": { @@ -591,28 +596,28 @@ "victory": true, "roundsPassed": 14, "eliminatedRound": null, - "lp": 36, + "lp": 27, "bandId": "band_sarkazb", "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":250,"bossDamage":490972,"dmgDealt":3069097,"gold":139,"itemsEquipped":12,"kills":331,"leaks":11,"lpLost":9,"merges":5,"perfectRounds":11,"refreshes":23}, - "lineup": ["3_19_b@11,7[2_04_e_b]","5_12_a@11,8","4_10_a@10,5[1_02_e_b+2_01_e_a]","6_06_a@9,4","1_08_b@9,5","6_16_a@9,7","5_20_a@9,8[3_05_e_a+3_02_e_b]","3_13_b@9,9"], - "bonds": ["arcaneShip 2/86","miraShip 1/12","preciShip 0/10","sargonShip 0/10","siracusaShip 1/110","skillfulShip 1/36","soloShip 1/6","suntShip 1/0","swiftShip 0/32"] + "stats": {"activatedLayers":303,"bossDamage":282517,"dmgDealt":2624584,"gold":138,"itemsEquipped":17,"kills":311,"leaks":31,"lpLost":18,"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]"], + "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"] }, "ai_1": { "alive": true, "victory": true, "roundsPassed": 14, "eliminatedRound": null, - "lp": 22, + "lp": 19, "bandId": "band_dusk", "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":199,"bossDamage":926528,"dmgDealt":3823020,"gold":137,"itemsEquipped":18,"kills":325,"leaks":6,"lpLost":6,"merges":5,"perfectRounds":12,"refreshes":13}, - "lineup": ["4_20_a@12,8[3_06_e_a]","1_17_b@11,7[3_08_e_a]","4_02_b@11,8[3_02_e_a+4_03_e_b]","4_01_b@10,7[4_04_e_a+1_02_e_a]","6_17_a@10,8[4_06_e_a+4_08_e_a]","6_19_a@9,7[1_05_e_a+1_01_e_b]","4_14_a@9,8","5_07_a@9,9"], - "bonds": ["arcaneShip 2/34","kazimierzShip 1/38","kjeragShip 0/8","lateranoShip 0/8","preciShip 2/55","raidShip 1/0","sargonShip 0/10","suntShip 1/0","swiftShip 1/72"] + "stats": {"activatedLayers":192,"bossDamage":1134983,"dmgDealt":4217825,"gold":135,"itemsEquipped":19,"kills":324,"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]"], + "bonds": ["arcaneShip 2/30","kazimierzShip 1/56","kjeragShip 0/2","preciShip 2/106","sargonShip 0/10","steadShip 0/6","suntShip 1/0","swiftShip 0/11"] } } }, @@ -626,8 +631,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m04","bossId":"boss_4","hiddenBossId":"boss_10","factions":["FLY","TIMES","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":12,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":114800}, - "rng": {"setup":25,"shop":391,"waves":0,"draft":73,"bots":13595,"meta":11}, + "end": {"victory":false,"roundsPassed":12,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":116300}, + "rng": {"setup":25,"shop":389,"waves":0,"draft":73,"bots":13595,"meta":8}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -648,82 +653,82 @@ { "round": 4, "ai_0": [1,25,8,0,1,2,8,"0c0d931c",5,"kazimierzShip 0/2, kjeragShip 0/2, soloShip 1/3, steadShip 2/2, swiftShip 0/2",12,12,0,23], - "ai_1": [1,24,8,0,1,2,8,"81a34b39",0,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/4, soloShip 1/0, swiftShip 0/2",10,12,2,23] + "ai_1": [1,24,8,0,1,2,8,"74a0e24c",2,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/2, skillfulShip 1/0, soloShip 1/0, swiftShip 1/2",10,12,2,23] }, { "round": 5, - "ai_0": [1,25,9,0,0,3,8,"db350784",7,"kazimierzShip 0/2, kjeragShip 0/2, soloShip 1/3, steadShip 2/2, swiftShip 1/2",18,18,0,32], - "ai_1": [1,24,9,0,0,3,8,"7a9c7584",0,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/4, skillfulShip 1/0, soloShip 1/0, swiftShip 0/2",15,18,3,32] + "ai_0": [1,25,9,0,0,3,8,"df888d16",5,"kazimierzShip 0/2, kjeragShip 0/2, soloShip 1/3, steadShip 2/2, swiftShip 0/2",14,18,4,32], + "ai_1": [1,24,9,0,0,3,8,"f6ecac03",6,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/4, skillfulShip 1/2, soloShip 1/0, swiftShip 1/4",18,18,0,32] }, { "round": 6, - "ai_0": [1,25,9,0,0,3,8,"a0e6e026",9,"kazimierzShip 1/2, kjeragShip 0/2, skillfulShip 1/0, soloShip 1/3, steadShip 2/2, swiftShip 1/2",24,24,0,41], - "ai_1": [1,24,9,0,0,3,8,"e1a93e5c",2,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/4, skillfulShip 1/0, soloShip 1/0, swiftShip 1/2",24,24,0,43] + "ai_0": [1,25,9,0,0,3,8,"0c494655",22,"kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/6, skillfulShip 1/3, soloShip 1/3, steadShip 1/2, swiftShip 1/8",24,24,0,39], + "ai_1": [1,24,9,0,1,3,8,"7eef3a52",15,"arcaneShip 1/5, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/4, skillfulShip 1/4, soloShip 1/0, swiftShip 1/6",24,24,0,42] }, { "round": 7, - "ai_0": [1,25,10,0,0,4,8,"7dbd16ef",20,"kazimierzShip 1/2, kjeragShip 0/2, raidShip 1/4, skillfulShip 1/3, soloShip 1/3, steadShip 1/6, swiftShip 1/2",28,28,0,51], - "ai_1": [1,24,10,0,2,4,8,"b6785496",2,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/2, sargonShip 0/4, skillfulShip 1/0, soloShip 1/0, swiftShip 1/2",28,28,0,53] + "ai_0": [1,25,10,0,0,4,8,"fbc6374d",17,"kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/6, skillfulShip 1/6, soloShip 1/3, steadShip 2/2, swiftShip 0/8",23,28,5,49], + "ai_1": [1,24,11,0,1,4,8,"e7fe4f2e",27,"arcaneShip 1/8, kazimierzShip 0/2, preciShip 1/5, sargonShip 0/8, skillfulShip 1/6, swiftShip 1/8",28,28,0,54] }, { "round": 8, - "ai_0": [1,25,11,0,0,4,8,"50b7d162",55,"kazimierzShip 1/16, kjeragShip 0/2, raidShip 1/20, skillfulShip 1/6, soloShip 1/3, steadShip 2/10, swiftShip 0/4",9,9,0,61], - "ai_1": [1,19,13,0,0,4,8,"bdd566ef",15,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 2/15, sargonShip 0/4, siracusaShip 1/0, skillfulShip 1/0, soloShip 1/0, swiftShip 0/2",4,9,5,66] + "ai_0": [1,25,11,0,0,4,8,"e5bac407",20,"kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/6, skillfulShip 1/6, soloShip 1/3, steadShip 2/5, swiftShip 0/8",4,9,5,60], + "ai_1": [1,24,12,0,0,4,8,"d585bb43",50,"arcaneShip 1/11, kazimierzShip 0/2, preciShip 1/14, sargonShip 1/17, skillfulShip 1/8, soloShip 1/0, swiftShip 0/10",9,9,0,65] }, { "round": 9, - "ai_0": [1,25,12,0,2,4,8,"d4f6e277",74,"kazimierzShip 1/19, kjeragShip 0/2, raidShip 1/32, skillfulShip 0/9, soloShip 1/3, steadShip 2/12, suntShip 1/0, swiftShip 1/8",23,23,0,73], - "ai_1": [1,15,12,0,1,4,8,"f01fb873",39,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 1/28, sargonShip 0/4, siracusaShip 1/4, skillfulShip 1/0, soloShip 1/0, swiftShip 1/7",17,23,6,78] + "ai_0": [1,16,12,0,2,4,8,"60860b3b",47,"deputShip 1/2, indomShip 1/8, kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/22, skillfulShip 1/8, soloShip 1/5, steadShip 0/5, suntShip 1/2, swiftShip 0/8",14,23,9,74], + "ai_1": [1,22,12,0,0,4,8,"7b53d3a1",80,"arcaneShip 2/19, kazimierzShip 0/2, preciShip 1/25, sargonShip 1/26, skillfulShip 1/10, soloShip 1/0, swiftShip 0/10",21,23,2,78] }, { "round": 10, - "ai_0": [1,23,14,0,1,5,8,"eb530a8d",86,"kazimierzShip 1/25, kjeragShip 0/2, raidShip 1/32, skillfulShip 0/9, soloShip 1/3, steadShip 2/12, suntShip 1/0, swiftShip 1/14",6,8,2,88], - "ai_1": [1,12,13,0,0,5,8,"518a37b6",61,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 1/41, sargonShip 0/4, siracusaShip 1/4, skillfulShip 1/2, soloShip 1/0, suntShip 1/0, swiftShip 1/14",5,8,3,91] + "ai_0": [1,16,14,0,0,5,8,"0090abcb",63,"deputShip 0/2, indomShip 1/8, kazimierzShip 0/2, kjeragShip 0/2, siracusaShip 1/24, skillfulShip 1/8, soloShip 1/5, steadShip 1/8, suntShip 1/2, swiftShip 1/8",4,8,4,87], + "ai_1": [1,22,12,0,2,5,8,"e97a6c43",103,"arcaneShip 2/22, kazimierzShip 0/2, preciShip 1/34, sargonShip 1/35, skillfulShip 1/12, suntShip 1/0, swiftShip 0/10",8,8,0,92] }, { "round": 11, - "ai_0": [1,13,13,0,1,5,8,"990bffda",113,"kazimierzShip 1/33, kjeragShip 0/2, raidShip 1/38, skillfulShip 0/9, soloShip 1/3, steadShip 2/17, suntShip 1/0, swiftShip 1/22",13,27,14,100], - "ai_1": [1,2,12,0,1,5,8,"60f9ee96",85,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 1/50, sargonShip 0/4, siracusaShip 1/10, skillfulShip 1/4, soloShip 1/0, suntShip 1/0, swiftShip 1/21",6,27,21,102] + "ai_0": [1,6,12,0,1,5,8,"93ecea9e",70,"deputShip 0/2, indomShip 0/8, kazimierzShip 0/2, kjeragShip 0/2, miraShip 1/0, raidShip 1/1, siracusaShip 1/32, skillfulShip 1/10, soloShip 1/5, steadShip 1/10, suntShip 1/2, swiftShip 1/10",10,27,17,100], + "ai_1": [1,12,14,0,3,5,8,"bc2a3dd4",119,"arcaneShip 2/28, kazimierzShip 0/2, preciShip 1/38, sargonShip 1/39, skillfulShip 1/14, suntShip 1/0, swiftShip 0/10",13,27,14,107] }, { "round": 12, - "ai_0": [1,3,13,0,0,6,8,"dc00c87b",133,"kazimierzShip 1/33, kjeragShip 0/2, raidShip 1/49, skillfulShip 0/9, soloShip 1/3, steadShip 2/22, suntShip 1/0, swiftShip 1/26, visiShip 1/0",21,50,29,114], - "ai_1": [0,0,13,0,0,6,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/59, sargonShip 0/4, siracusaShip 0/10, skillfulShip 0/6, swiftShip 0/23",21,50,29,114] + "ai_0": [0,0,13,0,0,6,0,"811c9dc5",0,"deputShip 0/2, indomShip 0/8, kazimierzShip 0/2, kjeragShip 0/2, raidShip 0/3, siracusaShip 0/32, skillfulShip 0/12, soloShip 0/5, steadShip 0/15, suntShip 0/2, swiftShip 0/12",13,50,37,113], + "ai_1": [1,2,15,0,2,6,8,"74bedaa1",129,"arcaneShip 2/30, kazimierzShip 0/2, preciShip 1/42, sargonShip 1/43, skillfulShip 1/14, suntShip 1/0, swiftShip 0/10",28,50,22,120] }, { "round": 13, - "ai_0": [0,0,12,0,0,6,0,"811c9dc5",0,"kazimierzShip 0/41, kjeragShip 0/2, miraShip 0/7, preciShip 0/4, raidShip 0/49, skillfulShip 0/9, soloShip 0/3, steadShip 0/22, swiftShip 0/34, yanShip 0/6",13,49,36,126], - "ai_1": [0,0,0,0,0,6,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/2, preciShip 0/59, sargonShip 0/4, siracusaShip 0/10, skillfulShip 0/6, swiftShip 0/23",null,null,null,114] + "ai_0": [0,0,0,0,0,6,0,"811c9dc5",0,"deputShip 0/2, indomShip 0/8, kazimierzShip 0/2, kjeragShip 0/2, raidShip 0/3, siracusaShip 0/32, skillfulShip 0/12, soloShip 0/5, steadShip 0/15, suntShip 0/2, swiftShip 0/12",null,null,null,113], + "ai_1": [0,0,14,0,0,6,0,"811c9dc5",0,"arcaneShip 0/40, kazimierzShip 0/2, preciShip 0/46, sargonShip 0/43, skillfulShip 0/14, swiftShip 0/10",38,49,11,136] } ], "players": { "ai_0": { "alive": false, "victory": false, - "roundsPassed": 12, - "eliminatedRound": 13, + "roundsPassed": 11, + "eliminatedRound": 12, "lp": 0, "bandId": "band_amiya", - "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, - "trophies": 3, - "reward": 298, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2574318,"gold":126,"itemsEquipped":8,"kills":206,"leaks":81,"lpLost":32,"merges":4,"perfectRounds":9,"refreshes":13}, + "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, + "trophies": 2, + "reward": 255, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1366383,"gold":113,"itemsEquipped":10,"kills":152,"leaks":81,"lpLost":29,"merges":2,"perfectRounds":5,"refreshes":9}, "lineup": [], - "bonds": ["kazimierzShip 0/41","kjeragShip 0/2","miraShip 0/7","preciShip 0/4","raidShip 0/49","skillfulShip 0/9","soloShip 0/3","steadShip 0/22","swiftShip 0/34","yanShip 0/6"] + "bonds": ["deputShip 0/2","indomShip 0/8","kazimierzShip 0/2","kjeragShip 0/2","raidShip 0/3","siracusaShip 0/32","skillfulShip 0/12","soloShip 0/5","steadShip 0/15","suntShip 0/2","swiftShip 0/12"] }, "ai_1": { "alive": false, "victory": false, - "roundsPassed": 11, - "eliminatedRound": 12, + "roundsPassed": 12, + "eliminatedRound": 13, "lp": 0, "bandId": "band_qalaisa", - "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, - "trophies": 2, - "reward": 255, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1426006,"gold":114,"itemsEquipped":11,"kills":164,"leaks":69,"lpLost":34,"merges":3,"perfectRounds":5,"refreshes":19}, + "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, + "trophies": 3, + "reward": 298, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3581486,"gold":136,"itemsEquipped":11,"kills":249,"leaks":51,"lpLost":34,"merges":4,"perfectRounds":8,"refreshes":22}, "lineup": [], - "bonds": ["arcaneShip 0/2","kazimierzShip 0/2","preciShip 0/59","sargonShip 0/4","siracusaShip 0/10","skillfulShip 0/6","swiftShip 0/23"] + "bonds": ["arcaneShip 0/40","kazimierzShip 0/2","preciShip 0/46","sargonShip 0/43","skillfulShip 0/14","swiftShip 0/10"] } } }, @@ -737,8 +742,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act2autochess_m03","bossId":"boss_4","hiddenBossId":"boss_9","factions":["FLY","TIMES","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":11,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":114200}, - "rng": {"setup":25,"shop":540,"waves":0,"draft":75,"bots":24787,"meta":17}, + "end": {"victory":false,"roundsPassed":11,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":112600}, + "rng": {"setup":25,"shop":507,"waves":0,"draft":74,"bots":23145,"meta":14}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -761,73 +766,73 @@ }, { "round": 4, - "ai_0": [1,23,8,0,3,2,8,"68de5356",12,"deputShip 1/8, kazimierzShip 0/2, swiftShip 1/4",11,14,3,22], - "ai_1": [1,45,9,0,2,2,8,"695e8a3a",16,"deputShip 1/2, kazimierzShip 0/7, swiftShip 1/14",7,12,5,23], - "ai_2": [1,27,8,0,1,2,8,"7d605cca",12,"deputShip 1/2, siracusaShip 1/2, soloShip 1/8, yanShip 0/1",12,12,0,23] + "ai_0": [1,23,8,0,1,2,8,"8f7fa63d",12,"deputShip 1/8, kazimierzShip 0/2, swiftShip 1/4",12,14,2,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_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, - "ai_0": [1,23,11,0,0,3,8,"59856422",20,"deputShip 1/12, kazimierzShip 0/2, skillfulShip 1/2, swiftShip 1/6",49,51,2,33], - "ai_1": [1,45,10,0,0,3,8,"a37755ee",27,"deputShip 2/6, kazimierzShip 0/7, swiftShip 1/21",53,53,0,32], - "ai_2": [1,27,9,0,2,3,8,"c25b67c7",22,"deputShip 1/4, siracusaShip 1/4, soloShip 1/14, yanShip 0/1",53,53,0,31] + "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_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, - "ai_0": [1,23,9,0,0,3,8,"f9fc483c",42,"deputShip 2/18, kazimierzShip 0/2, skillfulShip 1/8, soloShip 1/4, swiftShip 1/12",24,26,2,42], - "ai_1": [1,45,9,0,0,3,8,"50fac57a",25,"deputShip 2/13, kazimierzShip 0/7, skillfulShip 1/4, soloShip 1/4, suntShip 1/4, swiftShip 0/21",26,26,0,41], - "ai_2": [1,27,11,0,0,3,8,"973ac812",46,"deputShip 1/6, siracusaShip 1/20, soloShip 1/18, suntShip 1/2, yanShip 0/1",26,26,0,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",40,"deputShip 1/8, kazimierzShip 0/7, siracusaShip 1/2, 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] }, { "round": 7, - "ai_0": [1,21,10,0,3,4,8,"62ed25ce",67,"deputShip 2/24, kazimierzShip 0/2, skillfulShip 1/17, soloShip 1/8, swiftShip 1/18",23,25,2,55], - "ai_1": [1,43,10,0,2,4,8,"7a946d01",47,"deputShip 2/20, kazimierzShip 0/7, skillfulShip 1/11, soloShip 1/8, suntShip 1/8, swiftShip 0/21",23,25,2,50], - "ai_2": [1,26,10,0,0,4,8,"a5bc0482",70,"deputShip 1/8, siracusaShip 1/38, soloShip 1/20, suntShip 1/4, yanShip 0/1",24,25,1,52] + "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",59,"deputShip 1/10, kazimierzShip 0/7, siracusaShip 1/10, 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] }, { "round": 8, - "ai_0": [1,12,14,0,0,4,8,"42a1f973",86,"deputShip 2/28, kazimierzShip 0/2, kjeragShip 0/10, skillfulShip 1/35, soloShip 1/12, steadShip 1/11, swiftShip 0/20",11,20,9,69], - "ai_1": [1,33,13,0,0,4,8,"983995f0",83,"deputShip 2/30, kazimierzShip 0/7, skillfulShip 1/18, soloShip 1/12, suntShip 1/12, swiftShip 0/21, victoriaShip 1/11",10,20,10,61], - "ai_2": [1,16,11,0,0,4,8,"a6b5364f",107,"deputShip 1/10, siracusaShip 1/56, soloShip 1/22, suntShip 1/6, swiftShip 1/13, yanShip 0/1",10,20,10,63] + "ai_0": [1,15,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",12,20,8,66], + "ai_1": [1,33,11,0,0,4,8,"e84c71c4",78,"deputShip 1/12, kazimierzShip 0/7, raidShip 1/7, siracusaShip 1/12, soloShip 0/4, suntShip 1/6, swiftShip 1/41",8,20,12,63], + "ai_2": [1,17,11,0,4,4,8,"c8bb7116",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",6,20,14,67] }, { "round": 9, - "ai_0": [1,9,12,0,6,4,8,"6d558be7",161,"deputShip 2/32, indomShip 1/4, kazimierzShip 0/2, kjeragShip 1/32, skillfulShip 1/51, soloShip 1/16, steadShip 1/22, suntShip 1/4, swiftShip 0/20",66,69,3,81], - "ai_1": [1,23,12,0,0,4,8,"ad1959de",111,"deputShip 2/35, kazimierzShip 0/7, skillfulShip 1/22, soloShip 1/16, suntShip 1/16, swiftShip 0/21, victoriaShip 1/22",40,54,14,73], - "ai_2": [1,8,12,0,0,4,8,"e0957cd2",127,"deputShip 0/10, miraShip 1/3, siracusaShip 2/78, soloShip 1/22, suntShip 1/6, swiftShip 1/18, yanShip 0/1",51,59,8,75] + "ai_0": [1,10,12,0,1,4,8,"1ebef190",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",52,57,5,78], + "ai_1": [1,25,12,0,1,4,8,"b3d8767c",107,"deputShip 1/14, kazimierzShip 0/7, raidShip 1/14, siracusaShip 1/20, soloShip 0/4, suntShip 1/8, swiftShip 1/51",51,59,8,76], + "ai_2": [1,7,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",40,50,10,82] }, { "round": 10, - "ai_0": [1,6,18,0,0,5,8,"7365b613",209,"deputShip 2/36, indomShip 1/8, kazimierzShip 0/2, kjeragShip 1/43, miraShip 1/4, skillfulShip 1/57, soloShip 1/20, steadShip 1/33, suntShip 1/8, swiftShip 0/20",15,18,3,99], - "ai_1": [1,13,12,0,0,5,8,"8f07e1d3",124,"deputShip 2/40, indomShip 1/5, kazimierzShip 0/7, skillfulShip 1/26, soloShip 1/20, suntShip 0/16, swiftShip 0/21, victoriaShip 1/33",4,18,14,85], - "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/10, miraShip 0/6, siracusaShip 0/86, soloShip 0/22, suntShip 0/6, swiftShip 0/23, yanShip 0/1",6,18,12,86] + "ai_0": [0,0,13,0,0,5,0,"811c9dc5",0,"arcaneShip 0/4, deputShip 0/45, emptyShip 0/2, kazimierzShip 0/4, lateranoShip 0/36, skillfulShip 0/38, soloShip 0/8, steadShip 0/18, suntShip 0/2, swiftShip 0/23",8,18,10,91], + "ai_1": [1,15,13,0,0,5,8,"fc1d75ab",134,"deputShip 1/16, kazimierzShip 0/7, raidShip 1/21, siracusaShip 1/26, soloShip 0/4, suntShip 1/10, swiftShip 1/61",5,18,13,88], + "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/10, siracusaShip 0/60, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",10,18,8,93] }, { "round": 11, - "ai_0": [1,3,12,0,1,5,8,"3d2ea997",260,"deputShip 2/40, indomShip 0/8, kazimierzShip 0/2, kjeragShip 1/59, miraShip 1/8, siracusaShip 0/6, skillfulShip 1/69, soloShip 1/24, steadShip 1/44, suntShip 1/12, swiftShip 0/26, visiShip 1/4",22,25,3,111], - "ai_1": [1,3,12,0,0,5,8,"ec0a9e7a",133,"deputShip 1/46, indomShip 1/11, kazimierzShip 0/7, maniShip 1/2, skillfulShip 1/28, soloShip 0/20, suntShip 0/16, swiftShip 0/21, victoriaShip 2/46",8,25,17,98], - "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/10, miraShip 0/6, siracusaShip 0/86, soloShip 0/22, suntShip 0/6, swiftShip 0/23, yanShip 0/1",null,null,null,86] + "ai_0": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/4, deputShip 0/45, emptyShip 0/2, kazimierzShip 0/4, lateranoShip 0/36, skillfulShip 0/38, soloShip 0/8, steadShip 0/18, suntShip 0/2, swiftShip 0/23",null,null,null,91], + "ai_1": [1,5,12,0,1,5,8,"3e54e2ff",145,"deputShip 0/16, kazimierzShip 0/7, preciShip 1/5, raidShip 1/26, siracusaShip 1/38, soloShip 0/4, suntShip 0/10, swiftShip 1/67, visiShip 1/9",12,25,13,101], + "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/10, siracusaShip 0/60, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",null,null,null,93] }, { "round": 12, - "ai_0": [0,0,13,0,0,6,0,"811c9dc5",0,"deputShip 0/44, indomShip 0/8, kazimierzShip 0/2, kjeragShip 0/65, miraShip 0/12, siracusaShip 0/6, skillfulShip 0/81, soloShip 0/28, steadShip 0/55, suntShip 0/16, swiftShip 0/26, visiShip 0/8",14,57,43,125], - "ai_1": [0,0,12,0,0,6,0,"811c9dc5",0,"deputShip 0/48, indomShip 0/17, kazimierzShip 0/7, maniShip 0/4, skillfulShip 0/30, soloShip 0/20, suntShip 0/16, swiftShip 0/21, victoriaShip 0/67",11,57,46,109], - "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/10, miraShip 0/6, siracusaShip 0/86, soloShip 0/22, suntShip 0/6, swiftShip 0/23, yanShip 0/1",null,null,null,86] + "ai_0": [0,0,0,0,0,5,0,"811c9dc5",0,"arcaneShip 0/4, deputShip 0/45, emptyShip 0/2, kazimierzShip 0/4, lateranoShip 0/36, skillfulShip 0/38, soloShip 0/8, steadShip 0/18, suntShip 0/2, swiftShip 0/23",null,null,null,91], + "ai_1": [0,0,15,0,0,6,0,"811c9dc5",0,"arcaneShip 0/14, deputShip 0/16, kazimierzShip 0/7, preciShip 0/10, raidShip 0/31, siracusaShip 0/42, soloShip 0/4, suntShip 0/10, swiftShip 0/72, visiShip 0/22",13,57,44,118], + "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/4, indomShip 0/8, miraShip 0/10, siracusaShip 0/60, skillfulShip 0/4, soloShip 0/12, suntShip 0/4, swiftShip 0/14, yanShip 0/1",null,null,null,93] } ], "players": { "ai_0": { "alive": false, "victory": false, - "roundsPassed": 11, - "eliminatedRound": 12, + "roundsPassed": 9, + "eliminatedRound": 10, "lp": 0, "bandId": "band_chiave", - "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, + "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, "trophies": 2, - "reward": 255, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3504686,"gold":125,"itemsEquipped":16,"kills":270,"leaks":70,"lpLost":30,"merges":4,"perfectRounds":3,"refreshes":14}, + "reward": 191, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1418962,"gold":91,"itemsEquipped":6,"kills":217,"leaks":31,"lpLost":23,"merges":2,"perfectRounds":4,"refreshes":11}, "lineup": [], - "bonds": ["deputShip 0/44","indomShip 0/8","kazimierzShip 0/2","kjeragShip 0/65","miraShip 0/12","siracusaShip 0/6","skillfulShip 0/81","soloShip 0/28","steadShip 0/55","suntShip 0/16","swiftShip 0/26","visiShip 0/8"] + "bonds": ["arcaneShip 0/4","deputShip 0/45","emptyShip 0/2","kazimierzShip 0/4","lateranoShip 0/36","skillfulShip 0/38","soloShip 0/8","steadShip 0/18","suntShip 0/2","swiftShip 0/23"] }, "ai_1": { "alive": false, @@ -836,12 +841,12 @@ "eliminatedRound": 12, "lp": 0, "bandId": "band_sarkazb", - "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, + "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, "trophies": 2, "reward": 255, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2183297,"gold":109,"itemsEquipped":9,"kills":242,"leaks":108,"lpLost":52,"merges":3,"perfectRounds":5,"refreshes":18}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3289155,"gold":118,"itemsEquipped":13,"kills":279,"leaks":95,"lpLost":50,"merges":4,"perfectRounds":6,"refreshes":21}, "lineup": [], - "bonds": ["deputShip 0/48","indomShip 0/17","kazimierzShip 0/7","maniShip 0/4","skillfulShip 0/30","soloShip 0/20","suntShip 0/16","swiftShip 0/21","victoriaShip 0/67"] + "bonds": ["arcaneShip 0/14","deputShip 0/16","kazimierzShip 0/7","preciShip 0/10","raidShip 0/31","siracusaShip 0/42","soloShip 0/4","suntShip 0/10","swiftShip 0/72","visiShip 0/22"] }, "ai_2": { "alive": false, @@ -850,12 +855,12 @@ "eliminatedRound": 10, "lp": 0, "bandId": "band_lmlee", - "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, + "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, "trophies": 2, "reward": 191, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1540634,"gold":86,"itemsEquipped":5,"kills":202,"leaks":42,"lpLost":29,"merges":2,"perfectRounds":4,"refreshes":14}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1309827,"gold":93,"itemsEquipped":9,"kills":170,"leaks":60,"lpLost":28,"merges":4,"perfectRounds":3,"refreshes":10}, "lineup": [], - "bonds": ["deputShip 0/10","miraShip 0/6","siracusaShip 0/86","soloShip 0/22","suntShip 0/6","swiftShip 0/23","yanShip 0/1"] + "bonds": ["deputShip 0/4","indomShip 0/8","miraShip 0/10","siracusaShip 0/60","skillfulShip 0/4","soloShip 0/12","suntShip 0/4","swiftShip 0/14","yanShip 0/1"] } } }, @@ -869,8 +874,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_3","hiddenBossId":null,"factions":["TIMES","ELEMENT","REFLECTION"]}, - "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":130,"bossPoolLeft":0,"virtualMs":133000}, - "rng": {"setup":16,"shop":893,"waves":0,"draft":25,"bots":35892,"meta":29}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":130,"bossPoolLeft":0,"virtualMs":128500}, + "rng": {"setup":16,"shop":757,"waves":0,"draft":25,"bots":35892,"meta":28}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -904,65 +909,65 @@ { "round": 5, "ai_0": [1,26,8,0,2,3,8,"9781edeb",18,"deputShip 2/18, kjeragShip 0/2, sargonShip 0/15, steadShip 0/2",16,16,0,30], - "ai_1": [1,45,8,0,0,2,8,"b338489c",21,"deputShip 2/14, preciShip 0/2, sargonShip 0/2, siracusaShip 1/7",16,16,0,29], + "ai_1": [1,45,8,0,0,2,8,"9d1510c8",23,"deputShip 1/8, preciShip 0/4, sargonShip 0/2, siracusaShip 1/15, swiftShip 0/6",16,16,0,30], "ai_2": [1,28,8,0,0,3,8,"36b2b83c",6,"sargonShip 0/8, siracusaShip 1/6, yanShip 0/1",16,16,0,30], - "ai_3": [1,31,8,0,0,3,7,"7334b6cc",18,"deputShip 1/4, preciShip 1/14, siracusaShip 0/3, yanShip 0/1",15,16,1,30] + "ai_3": [1,31,8,0,2,3,7,"a758480c",18,"deputShip 1/4, preciShip 1/14, siracusaShip 0/3, yanShip 0/1",15,16,1,30] }, { "round": 6, - "ai_0": [1,26,11,0,0,3,8,"e8b0bf76",35,"deputShip 2/25, kjeragShip 0/2, sargonShip 0/15, steadShip 2/10",30,30,0,41], - "ai_1": [1,45,9,0,0,3,8,"6eaa3f94",33,"deputShip 2/22, preciShip 0/4, sargonShip 0/2, siracusaShip 1/11",30,30,0,38], - "ai_2": [1,28,9,0,0,3,8,"d135205d",8,"sargonShip 0/8, siracusaShip 1/8, visiShip 1/0, yanShip 0/1",30,30,0,39], - "ai_3": [1,31,9,0,0,3,7,"2c579203",43,"deputShip 2/8, emptyShip 1/4, kjeragShip 0/10, preciShip 1/31, siracusaShip 0/3, yanShip 0/1",30,30,0,39] + "ai_0": [1,26,11,0,0,3,8,"618b130f",35,"deputShip 2/25, kjeragShip 0/2, sargonShip 0/15, steadShip 2/10",30,30,0,42], + "ai_1": [1,45,9,0,0,3,8,"4b9a8330",31,"deputShip 1/10, preciShip 0/4, sargonShip 0/2, siracusaShip 1/21, swiftShip 0/6",30,30,0,38], + "ai_2": [1,28,9,0,0,3,8,"a6f6c116",18,"sargonShip 0/8, siracusaShip 1/14, steadShip 2/4, swiftShip 0/6, yanShip 0/1",30,30,0,39], + "ai_3": [1,31,11,0,1,3,7,"ff70b795",38,"deputShip 1/6, kjeragShip 0/5, preciShip 1/24, siracusaShip 1/8, yanShip 0/1",30,30,0,41] }, { "round": 7, - "ai_0": [1,26,10,0,0,4,8,"4d10b2d0",52,"deputShip 2/34, kjeragShip 0/2, sargonShip 0/15, steadShip 2/18",29,29,0,52], - "ai_1": [1,45,10,0,0,4,8,"74703cad",45,"deputShip 2/30, preciShip 0/4, sargonShip 0/2, siracusaShip 1/15",29,29,0,49], - "ai_2": [1,28,10,0,0,4,8,"15280eab",16,"sargonShip 0/8, siracusaShip 1/16, swiftShip 0/6, visiShip 1/0, yanShip 0/1",29,29,0,50], - "ai_3": [1,31,10,0,1,4,8,"53fd121b",82,"deputShip 2/12, emptyShip 1/8, kjeragShip 0/20, preciShip 1/53, siracusaShip 0/3, swiftShip 1/9, yanShip 0/1",29,29,0,50] + "ai_0": [1,26,10,0,0,4,8,"3b67af64",69,"deputShip 2/32, kjeragShip 0/2, sargonShip 1/19, steadShip 2/18",29,29,0,53], + "ai_1": [1,45,10,0,0,4,8,"11024c67",39,"deputShip 1/12, preciShip 0/4, sargonShip 0/2, siracusaShip 1/27, swiftShip 0/6",29,29,0,49], + "ai_2": [1,28,10,0,0,4,8,"7f56cec6",30,"deputShip 1/2, sargonShip 0/8, siracusaShip 1/18, steadShip 1/10, swiftShip 0/6, yanShip 0/1",29,29,0,50], + "ai_3": [1,31,11,0,0,4,8,"eb93ef9f",54,"deputShip 2/10, kjeragShip 0/5, preciShip 1/32, siracusaShip 1/12, yanShip 0/7",29,29,0,51] }, { "round": 8, - "ai_0": [1,26,11,0,0,4,8,"eba0dfca",120,"deputShip 1/41, kjeragShip 0/2, preciShip 2/21, sargonShip 1/30, steadShip 1/28",15,15,0,61], - "ai_1": [1,45,11,0,0,4,8,"cf25f7a3",65,"deputShip 2/38, preciShip 0/4, sargonShip 0/2, siracusaShip 1/23, steadShip 1/4",15,15,0,60], - "ai_2": [1,28,11,0,0,4,8,"dfed3d0a",48,"sargonShip 0/8, siracusaShip 1/36, swiftShip 1/12, visiShip 1/0, yanShip 0/1",15,15,0,59], - "ai_3": [1,31,12,0,1,4,7,"d59d4186",130,"deputShip 1/14, emptyShip 1/10, kjeragShip 1/27, preciShip 1/63, siracusaShip 0/3, swiftShip 1/16, yanShip 0/1",15,15,0,63] + "ai_0": [1,26,11,0,0,4,8,"388f6918",92,"deputShip 2/39, kjeragShip 0/7, sargonShip 1/23, steadShip 2/26, visiShip 1/4",15,15,0,62], + "ai_1": [1,45,11,0,0,4,8,"81f206ca",74,"deputShip 1/14, preciShip 1/11, sargonShip 0/2, siracusaShip 1/36, swiftShip 1/13",15,15,0,60], + "ai_2": [1,28,11,0,0,4,8,"731b7931",59,"deputShip 1/4, sargonShip 0/8, siracusaShip 1/26, steadShip 1/16, swiftShip 1/13, yanShip 0/1",15,15,0,60], + "ai_3": [1,31,11,0,0,4,7,"5ec63e46",77,"deputShip 1/12, kjeragShip 0/5, preciShip 1/43, siracusaShip 1/22, yanShip 0/7",15,15,0,61] }, { "round": 9, - "ai_0": [1,26,12,0,0,4,8,"fc454676",166,"deputShip 1/47, kjeragShip 0/2, preciShip 2/39, sargonShip 1/42, steadShip 1/38",30,30,0,74], - "ai_1": [1,45,12,0,0,4,8,"e1506142",83,"deputShip 2/48, emptyShip 1/4, preciShip 0/4, sargonShip 0/2, siracusaShip 1/31, steadShip 0/4",28,30,2,70], - "ai_2": [1,28,12,0,0,4,8,"6965081a",70,"sargonShip 0/8, siracusaShip 1/58, swiftShip 1/12, visiShip 1/0, yanShip 0/1",30,30,0,72], - "ai_3": [1,31,13,0,0,4,8,"5f784848",153,"deputShip 1/16, emptyShip 1/12, kjeragShip 1/30, preciShip 1/72, siracusaShip 0/3, swiftShip 1/23, yanShip 0/1",30,30,0,76] + "ai_0": [1,26,12,0,0,4,8,"dcbc34d9",117,"deputShip 2/46, kjeragShip 0/7, sargonShip 1/27, steadShip 2/36, visiShip 1/8",30,30,0,75], + "ai_1": [1,45,12,0,0,4,8,"7e29602e",116,"deputShip 2/18, emptyShip 1/4, preciShip 1/28, sargonShip 0/2, siracusaShip 1/44, swiftShip 1/22",30,30,0,71], + "ai_2": [1,28,12,0,0,4,8,"e97cc791",111,"deputShip 1/6, preciShip 1/31, sargonShip 0/8, siracusaShip 1/54, steadShip 0/16, swiftShip 1/20, yanShip 0/1",30,30,0,73], + "ai_3": [1,31,12,0,0,4,8,"db03c900",105,"deputShip 1/14, kjeragShip 0/5, preciShip 1/59, siracusaShip 1/32, yanShip 0/7",30,30,0,76] }, { "round": 10, - "ai_0": [1,26,12,0,0,5,8,"2b6a123a",256,"deputShip 1/53, kjeragShip 0/2, preciShip 2/79, sargonShip 1/76, steadShip 1/48",29,29,0,86], - "ai_1": [1,45,12,0,0,5,8,"5bcc6998",105,"deputShip 2/58, emptyShip 1/8, preciShip 0/4, sargonShip 0/2, siracusaShip 1/39, steadShip 0/4",29,29,0,82], - "ai_2": [1,28,12,0,0,5,8,"fe139ef2",72,"sargonShip 0/8, siracusaShip 2/60, swiftShip 1/12, visiShip 1/0, yanShip 0/1",29,29,0,83], - "ai_3": [1,31,12,0,0,5,8,"9ce217c6",163,"deputShip 1/18, emptyShip 0/12, kjeragShip 2/34, preciShip 1/81, siracusaShip 0/3, swiftShip 1/30, yanShip 0/1",29,29,0,88] + "ai_0": [1,26,12,0,0,5,8,"28ed8fda",129,"deputShip 2/53, kjeragShip 0/7, sargonShip 1/31, steadShip 2/45, visiShip 0/8",29,29,0,88], + "ai_1": [1,45,12,0,0,5,8,"ce4b0441",166,"deputShip 2/22, emptyShip 1/8, preciShip 1/41, sargonShip 0/2, siracusaShip 1/58, swiftShip 1/37",29,29,0,81], + "ai_2": [1,28,12,0,0,5,8,"ce1a56b2",135,"deputShip 1/8, preciShip 1/42, sargonShip 0/8, siracusaShip 1/58, steadShip 0/16, swiftShip 1/27, yanShip 0/1",29,29,0,84], + "ai_3": [1,31,12,0,0,5,7,"33b16596",115,"deputShip 0/16, kjeragShip 0/5, preciShip 1/75, siracusaShip 1/40, yanShip 0/7",29,29,0,89] }, { "round": 11, - "ai_0": [1,26,12,0,0,5,8,"5c1d4d08",288,"deputShip 1/55, kjeragShip 0/2, preciShip 1/87, sargonShip 1/88, steadShip 1/58",28,28,0,99], - "ai_1": [1,45,12,0,0,5,8,"6ceb3315",133,"deputShip 2/68, emptyShip 1/15, preciShip 0/4, sargonShip 0/2, siracusaShip 1/50, steadShip 0/4",10,28,18,94], - "ai_2": [1,28,12,0,0,5,8,"8321f307",124,"emptyShip 1/0, sargonShip 0/8, siracusaShip 2/100, swiftShip 1/24, yanShip 0/1",28,28,0,95], - "ai_3": [1,31,12,0,0,5,7,"5e2bfa81",195,"deputShip 0/18, emptyShip 0/12, kjeragShip 2/68, preciShip 1/92, siracusaShip 0/3, swiftShip 1/35, visiShip 1/0, yanShip 0/1",28,28,0,98] + "ai_0": [1,26,12,0,0,5,8,"dc9d71e9",152,"deputShip 1/55, indomShip 1/2, kjeragShip 0/7, sargonShip 1/33, steadShip 2/62, visiShip 0/8",28,28,0,102], + "ai_1": [1,45,12,0,0,5,8,"38f3210b",224,"deputShip 1/24, emptyShip 1/10, preciShip 2/82, sargonShip 0/2, siracusaShip 1/64, swiftShip 1/44",28,28,0,91], + "ai_2": [1,28,12,0,0,5,8,"d8242751",175,"deputShip 1/10, preciShip 1/49, sargonShip 0/8, siracusaShip 1/82, steadShip 0/16, swiftShip 1/34, yanShip 0/1",28,28,0,96], + "ai_3": [1,31,12,0,0,5,7,"9e061915",143,"deputShip 0/16, kjeragShip 0/5, preciShip 1/87, siracusaShip 1/56, swiftShip 0/10, yanShip 0/7",28,28,0,102] }, { "round": 12, - "ai_0": [1,26,12,0,0,6,8,"934bb983",406,"deputShip 1/57, emptyShip 1/2, kjeragShip 0/2, preciShip 1/137, sargonShip 1/142, steadShip 1/68",46,46,0,111], - "ai_1": [1,45,12,0,0,6,8,"d4b1958d",155,"deputShip 2/78, emptyShip 1/19, preciShip 0/4, sargonShip 0/2, siracusaShip 1/58, steadShip 0/4",20,46,26,104], - "ai_2": [1,28,12,0,0,6,8,"696f5ca3",164,"emptyShip 1/0, sargonShip 0/8, siracusaShip 2/140, swiftShip 1/24, yanShip 0/1",46,46,0,106], - "ai_3": [1,31,12,0,0,6,8,"2e3cd509",247,"deputShip 0/18, emptyShip 0/12, kjeragShip 2/108, preciShip 2/99, siracusaShip 0/3, swiftShip 1/40, visiShip 1/0, yanShip 0/1",46,46,0,110] + "ai_0": [1,26,12,0,0,6,8,"127f991f",165,"deputShip 1/57, indomShip 0/2, kjeragShip 0/7, sargonShip 1/35, steadShip 1/73, visiShip 0/8",46,46,0,114], + "ai_1": [1,45,12,0,0,6,8,"f1e90cde",282,"deputShip 2/28, emptyShip 1/14, preciShip 1/119, sargonShip 0/2, siracusaShip 1/68, swiftShip 1/53",46,46,0,103], + "ai_2": [1,28,12,0,0,6,8,"441ee35b",231,"deputShip 1/12, preciShip 2/80, sargonShip 0/8, siracusaShip 1/98, steadShip 0/16, swiftShip 1/41, yanShip 0/1",46,46,0,107], + "ai_3": [1,31,12,0,0,6,7,"1ee80c63",155,"deputShip 0/16, kjeragShip 0/5, preciShip 1/99, siracusaShip 1/56, swiftShip 0/10, yanShip 0/7",46,46,0,113] }, { "round": 13, - "ai_0": [1,26,12,0,0,6,8,"2df19066",460,"deputShip 1/59, emptyShip 1/4, kjeragShip 0/7, preciShip 2/155, sargonShip 1/164, steadShip 1/78",32,32,0,126], - "ai_1": [1,45,12,0,2,6,8,"9346fb5a",177,"deputShip 1/86, emptyShip 1/21, preciShip 1/6, sargonShip 0/2, siracusaShip 1/64, steadShip 0/4",32,32,0,115], - "ai_2": [1,28,12,0,0,6,8,"0236b2a3",238,"emptyShip 1/0, sargonShip 0/8, siracusaShip 2/214, swiftShip 1/24, yanShip 0/1",32,32,0,117], - "ai_3": [1,31,12,0,0,6,7,"ceba29b5",265,"deputShip 0/18, emptyShip 0/12, kjeragShip 1/116, preciShip 2/104, siracusaShip 0/3, swiftShip 1/45, yanShip 0/1",32,32,0,124] + "ai_0": [1,26,12,0,0,6,8,"626d042c",184,"deputShip 1/59, indomShip 1/4, kjeragShip 0/7, sargonShip 1/37, steadShip 2/84, visiShip 0/8",32,32,0,127], + "ai_1": [1,45,12,0,0,6,8,"d2a2642d",332,"deputShip 1/30, emptyShip 1/16, preciShip 2/144, sargonShip 0/2, siracusaShip 1/76, swiftShip 1/66",32,32,0,116], + "ai_2": [1,28,12,0,0,6,8,"ca7f3fdd",247,"deputShip 0/12, preciShip 2/85, sargonShip 0/8, siracusaShip 1/116, steadShip 0/16, swiftShip 1/46, yanShip 0/1",32,32,0,117], + "ai_3": [1,31,12,0,0,6,7,"6e7bba0f",207,"deputShip 0/16, kjeragShip 0/5, preciShip 1/115, siracusaShip 1/72, swiftShip 1/20, yanShip 0/7",32,32,0,126] } ], "players": { @@ -973,12 +978,12 @@ "eliminatedRound": null, "lp": 26, "bandId": "band_kirara", - "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, + "title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"}, "trophies": 3, "reward": 250, - "stats": {"activatedLayers":482,"bossDamage":190683,"dmgDealt":1852681,"gold":140,"itemsEquipped":25,"kills":319,"leaks":0,"lpLost":0,"merges":5,"perfectRounds":13,"refreshes":21}, - "lineup": ["6_08_a@11,6[3_03_e_a+3_02_e_a]","6_12_a@11,7[4_08_e_a+6_07_e_b]","2_14_b@10,3","2_17_b@10,4[4_02_e_b+5_01_e_a]","2_08_a@9,3[4_04_e_a+1_01_e_b]","6_06_a@9,4[3_08_e_a+3_01_e_b]","1_06_b@9,8","5_02_b@9,9[5_02_e_a+3_06_e_b]"], - "bonds": ["deputShip 1/61","emptyShip 1/6","kjeragShip 0/7","preciShip 2/157","sargonShip 1/170","steadShip 1/88"] + "stats": {"activatedLayers":203,"bossDamage":70064,"dmgDealt":1489868,"gold":140,"itemsEquipped":20,"kills":293,"leaks":0,"lpLost":0,"merges":4,"perfectRounds":13,"refreshes":20}, + "lineup": ["6_14_a@12,3","3_16_a@11,7[4_06_e_a+6_03_e_a]","4_24_a@10,3[6_07_e_a]","2_17_b@10,4[1_02_e_a]","2_08_b@9,3[4_04_e_a+5_01_e_a]","1_12_b@9,4[3_02_e_a+3_08_e_a]","3_13_b@9,8[2_04_e_a+1_05_e_a]","6_05_a@9,9[3_05_e_b+3_07_e_a]"], + "bonds": ["deputShip 1/63","indomShip 0/4","kjeragShip 0/12","sargonShip 1/45","steadShip 2/95","visiShip 0/8"] }, "ai_1": { "alive": true, @@ -987,12 +992,12 @@ "eliminatedRound": null, "lp": 45, "bandId": "band_sarkazb", - "title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"}, + "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 3, "reward": 250, - "stats": {"activatedLayers":207,"bossDamage":73033,"dmgDealt":1373511,"gold":129,"itemsEquipped":20,"kills":238,"leaks":46,"lpLost":0,"merges":6,"perfectRounds":10,"refreshes":20}, - "lineup": ["2_14_b@12,3","5_10_a@11,6[4_03_e_b+1_02_e_a]","6_01_a@11,7[3_05_e_a]","5_10_a@11,8[5_09_e_a+4_08_e_b]","6_14_a@10,3","4_16_a@10,8[4_11_e_a+4_04_e_b]","6_05_a@9,8[1_05_e_a+5_03_e_a]","2_16_a@9,9[5_02_e_a+4_05_e_b]"], - "bonds": ["deputShip 1/96","emptyShip 1/23","preciShip 1/14","sargonShip 0/2","siracusaShip 1/74","steadShip 0/4"] + "stats": {"activatedLayers":384,"bossDamage":420794,"dmgDealt":1945174,"gold":128,"itemsEquipped":20,"kills":284,"leaks":0,"lpLost":0,"merges":6,"perfectRounds":13,"refreshes":9}, + "lineup": ["5_10_a@12,8[4_03_e_b+3_04_e_b]","5_02_a@11,6","6_12_a@11,7","1_13_b@11,8[3_11_e_a]","6_20_a@10,3","3_18_b@10,8[2_01_e_b+3_01_e_a]","4_14_a@9,8[4_06_e_a+1_02_e_a]","1_08_b@9,9[1_01_e_b+4_12_e_a]"], + "bonds": ["deputShip 1/32","emptyShip 1/18","preciShip 2/159","sargonShip 0/2","siracusaShip 1/90","swiftShip 1/85"] }, "ai_2": { "alive": true, @@ -1001,12 +1006,12 @@ "eliminatedRound": null, "lp": 28, "bandId": "band_mlyss", - "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, + "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 3, "reward": 250, - "stats": {"activatedLayers":260,"bossDamage":308467,"dmgDealt":1912652,"gold":127,"itemsEquipped":20,"kills":291,"leaks":0,"lpLost":0,"merges":3,"perfectRounds":13,"refreshes":16}, - "lineup": ["4_25_a@12,3","1_07_b@11,7[1_01_e_a+1_05_e_a]","5_20_a@11,8[3_02_e_a+2_01_e_b]","6_18_a@10,3[2_01_e_b+4_03_e_b]","4_10_a@10,7[4_03_e_b]","3_18_b@9,4[3_07_e_b+3_11_e_a]","6_05_a@9,8","2_16_b@9,9[5_01_e_a+4_02_e_a]"], - "bonds": ["sargonShip 0/8","siracusaShip 2/236","swiftShip 1/24","yanShip 0/1"] + "stats": {"activatedLayers":307,"bossDamage":246847,"dmgDealt":1661325,"gold":129,"itemsEquipped":23,"kills":294,"leaks":0,"lpLost":0,"merges":4,"perfectRounds":13,"refreshes":11}, + "lineup": ["4_21_b@11,3[3_07_e_a]","5_02_a@11,6[6_03_e_a+4_03_e_a]","6_12_a@11,7[3_11_e_a+1_02_e_a]","5_20_a@11,8[4_02_e_a+5_02_e_b]","3_18_a@10,4[4_06_e_a+5_01_e_b]","2_16_b@9,4[4_04_e_a+4_05_e_a]","1_13_b@9,8[4_08_e_a+4_12_e_a]","4_14_a@9,9[3_06_e_a+2_01_e_b]"], + "bonds": ["deputShip 1/14","preciShip 2/92","sargonShip 0/8","siracusaShip 1/142","steadShip 0/16","swiftShip 1/59","yanShip 0/1"] }, "ai_3": { "alive": true, @@ -1015,12 +1020,12 @@ "eliminatedRound": null, "lp": 31, "bandId": "band_quintus", - "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, + "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, "trophies": 3, "reward": 250, - "stats": {"activatedLayers":311,"bossDamage":567817,"dmgDealt":2252972,"gold":138,"itemsEquipped":36,"kills":300,"leaks":1,"lpLost":0,"merges":3,"perfectRounds":12,"refreshes":23}, - "lineup": ["3_20_b@12,8[5_02_e_b+6_03_e_a]","6_14_a@11,3","4_13_b@11,6[5_02_e_b+4_07_e_a]","6_01_a@11,7[3_11_e_a+3_04_e_a]","4_14_a@11,8[4_03_e_a+1_02_e_a]","4_22_a@10,8[4_04_e_b+4_02_e_a]","3_11_b@9,8[4_09_e_a+3_07_e_a]","5_14_a@9,9[4_06_e_b+2_01_e_b]"], - "bonds": ["deputShip 1/20","emptyShip 0/12","kjeragShip 1/120","preciShip 2/117","siracusaShip 0/3","swiftShip 1/54","yanShip 0/1"] + "stats": {"activatedLayers":249,"bossDamage":402295,"dmgDealt":1838186,"gold":138,"itemsEquipped":31,"kills":282,"leaks":1,"lpLost":0,"merges":2,"perfectRounds":12,"refreshes":18}, + "lineup": ["6_12_a@11,7[4_03_e_a+2_01_e_b]","6_05_a@11,8","4_10_a@10,3[4_06_e_a]","4_10_a@10,4[5_03_e_a+3_01_e_b]","2_16_a@10,7[5_02_e_a+4_06_e_b]","4_16_a@10,8[4_05_e_a+4_07_e_a]","4_14_b@9,8[6_03_e_a+4_09_e_a]"], + "bonds": ["deputShip 0/16","kjeragShip 0/5","preciShip 2/131","siracusaShip 1/88","swiftShip 1/30","yanShip 0/7"] } } }, @@ -1034,8 +1039,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act2autochess_m01","bossId":"boss_1","hiddenBossId":"boss_8","factions":["DOT","INVISIBLE","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":11,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":118950}, - "rng": {"setup":25,"shop":453,"waves":0,"draft":65,"bots":18179,"meta":53}, + "end": {"victory":false,"roundsPassed":10,"hiddenReached":false,"hiddenCleared":false,"reason":"eliminated","teamLp":null,"bossPoolLeft":null,"virtualMs":110050}, + "rng": {"setup":25,"shop":495,"waves":0,"draft":65,"bots":17828,"meta":59}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1062,81 +1067,74 @@ { "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_1": [1,25,8,0,1,2,8,"f1865800",7,"arcaneShip 1/2, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/2, sargonShip 0/4, soloShip 1/0, steadShip 0/2, swiftShip 1/5",11,12,1,21], - "ai_2": [1,24,8,0,3,2,8,"38880204",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,26,9,0,0,2,8,"00097ea6",4,"arcaneShip 1/3, preciShip 0/2, sargonShip 0/2, siracusaShip 1/0, soloShip 1/1",6,10,4,24] + "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_3": [1,24,9,0,0,2,8,"e41df16d",10,"arcaneShip 1/5, deputShip 1/2, preciShip 0/2, sargonShip 0/2, soloShip 1/3",8,10,2,24] }, { "round": 5, - "ai_0": [1,35,9,0,0,3,8,"62c03eb5",8,"arcaneShip 0/8, kazimierzShip 0/4, preciShip 0/8, swiftShip 1/8",4,17,13,32], - "ai_1": [1,15,9,0,0,3,8,"0a827c34",8,"arcaneShip 1/2, kazimierzShip 0/5, kjeragShip 0/2, preciShip 0/2, sargonShip 0/4, soloShip 1/0, steadShip 0/2, swiftShip 1/6",4,17,13,30], - "ai_2": [1,14,11,0,0,3,8,"623f557a",22,"deputShip 1/10, kazimierzShip 0/2, kjeragShip 0/10, siracusaShip 0/2, skillfulShip 0/2, soloShip 1/6, steadShip 0/4, swiftShip 1/6, victoriaShip 0/10, yanShip 0/4",7,17,10,32], - "ai_3": [1,16,9,0,0,3,8,"af41c680",5,"arcaneShip 1/4, preciShip 0/2, sargonShip 0/2, siracusaShip 1/0, soloShip 1/1",4,17,13,33] + "ai_0": [1,39,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",11,17,6,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,20,9,0,0,3,8,"7b409933",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",13,17,4,31], + "ai_3": [1,14,9,0,0,3,8,"07fdf462",19,"arcaneShip 1/10, deputShip 1/4, preciShip 0/4, sargonShip 0/2, soloShip 1/5",4,17,13,33] }, { "round": 6, - "ai_0": [1,35,9,0,0,3,8,"844b219a",33,"arcaneShip 0/8, kazimierzShip 0/4, kjeragShip 0/5, preciShip 2/20, suntShip 1/0, swiftShip 1/13",14,14,0,41], - "ai_1": [1,14,9,0,1,3,8,"3e9da7ea",25,"arcaneShip 1/2, kazimierzShip 1/10, kjeragShip 0/2, preciShip 0/2, sargonShip 0/4, siracusaShip 0/6, soloShip 1/0, steadShip 0/2, swiftShip 1/13",11,14,3,40], - "ai_2": [1,14,9,0,0,3,8,"524a7cff",51,"deputShip 1/12, egirShip 0/2, kazimierzShip 0/2, kjeragShip 0/12, raidShip 0/2, siracusaShip 0/2, skillfulShip 0/4, soloShip 1/8, steadShip 2/18, swiftShip 1/13, victoriaShip 0/10, yanShip 0/4",14,14,0,41], - "ai_3": [1,15,10,0,0,3,8,"ae19c686",28,"arcaneShip 2/9, deputShip 1/2, preciShip 0/2, sargonShip 0/2, siracusaShip 1/11, soloShip 1/6",9,14,5,45] + "ai_0": [1,39,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",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,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,10,0,0,3,8,"7a7888b4",50,"arcaneShip 1/13, deputShip 1/6, kjeragShip 1/9, preciShip 1/11, sargonShip 0/2, soloShip 1/7, steadShip 1/4",8,14,6,42] }, { "round": 7, - "ai_0": [1,25,10,0,0,4,8,"fa19dc02",65,"arcaneShip 0/8, kazimierzShip 1/14, kjeragShip 0/5, preciShip 2/27, suntShip 1/0, swiftShip 1/24",6,21,15,52], - "ai_1": [1,4,11,0,0,4,8,"638fec4a",37,"arcaneShip 2/2, kazimierzShip 1/22, kjeragShip 0/2, preciShip 0/2, sargonShip 0/8, siracusaShip 0/6, steadShip 0/2, swiftShip 1/13",8,21,13,51], - "ai_2": [1,14,10,0,2,4,8,"a7ea2373",78,"deputShip 1/16, egirShip 0/4, kazimierzShip 0/2, kjeragShip 0/12, raidShip 0/4, siracusaShip 0/2, skillfulShip 0/6, soloShip 1/10, steadShip 2/33, swiftShip 1/19, victoriaShip 0/12, yanShip 0/4",21,21,0,51], - "ai_3": [1,5,11,0,0,4,8,"cad1e4b3",42,"arcaneShip 2/11, deputShip 1/4, preciShip 0/2, sargonShip 0/2, siracusaShip 1/19, soloShip 1/8",2,21,19,56] + "ai_0": [1,29,10,0,0,4,8,"3e2f54ac",46,"arcaneShip 1/16, indomShip 1/4, kazimierzShip 1/16, preciShip 0/8, soloShip 1/0, suntShip 1/0, swiftShip 1/10",8,21,13,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",7,21,14,52], + "ai_2": [1,13,10,0,0,3,8,"910eaa54",102,"deputShip 2/16, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/31, raidShip 0/16, skillfulShip 0/2, soloShip 0/4, steadShip 1/27, suntShip 1/4, swiftShip 0/2, victoriaShip 1/24, yanShip 0/10",14,21,7,51], + "ai_3": [1,4,11,0,0,4,8,"849fa992",55,"arcaneShip 0/13, deputShip 1/8, kjeragShip 1/18, preciShip 1/13, sargonShip 0/2, skillfulShip 1/7, soloShip 1/9, steadShip 0/4",4,21,17,55] }, { "round": 8, - "ai_0": [1,18,11,0,0,4,8,"78106ce5",93,"arcaneShip 2/8, kazimierzShip 1/22, kjeragShip 0/5, preciShip 2/34, suntShip 1/0, swiftShip 1/29",2,9,7,63], - "ai_1": [0,0,11,0,0,4,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/29, kjeragShip 0/7, preciShip 0/2, sargonShip 0/8, siracusaShip 0/12, skillfulShip 0/5, steadShip 0/2, swiftShip 0/19",0,9,9,62], - "ai_2": [1,10,13,0,0,4,8,"710470e0",113,"deputShip 1/20, egirShip 0/6, kazimierzShip 0/2, kjeragShip 0/12, preciShip 0/2, raidShip 0/6, siracusaShip 0/2, skillfulShip 0/6, soloShip 1/14, steadShip 2/44, swiftShip 1/35, victoriaShip 0/14, yanShip 0/4",5,9,4,64], - "ai_3": [0,0,12,0,0,4,0,"811c9dc5",0,"arcaneShip 0/13, deputShip 0/8, miraShip 0/10, preciShip 0/4, sargonShip 0/2, siracusaShip 0/32, skillfulShip 0/4, soloShip 0/12",2,9,7,68] + "ai_0": [1,22,11,0,0,4,8,"7e077aff",46,"arcaneShip 2/16, indomShip 1/6, kazimierzShip 1/21, preciShip 0/8, soloShip 1/0, steadShip 1/3, suntShip 1/0, swiftShip 0/10",2,9,7,63], + "ai_1": [0,0,11,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",1,9,8,64], + "ai_2": [1,6,11,0,0,4,8,"011c22b2",115,"deputShip 2/24, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/41, raidShip 0/16, skillfulShip 1/10, soloShip 0/4, steadShip 0/30, suntShip 1/8, swiftShip 0/4, victoriaShip 1/32, yanShip 0/10",2,9,7,63], + "ai_3": [0,0,12,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",2,9,7,68] }, { "round": 9, - "ai_0": [1,17,12,0,1,4,8,"a359f7ee",129,"arcaneShip 1/8, indomShip 1/2, kazimierzShip 1/38, kjeragShip 0/5, preciShip 2/47, suntShip 1/0, swiftShip 1/34",9,19,10,76], - "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/29, kjeragShip 0/7, preciShip 0/2, sargonShip 0/8, siracusaShip 0/12, skillfulShip 0/5, steadShip 0/2, swiftShip 0/19",null,null,null,62], - "ai_2": [1,10,12,0,1,4,8,"8df12b45",145,"deputShip 0/22, egirShip 0/6, kazimierzShip 0/2, kjeragShip 0/12, preciShip 0/4, raidShip 0/6, siracusaShip 0/2, skillfulShip 1/8, soloShip 1/14, steadShip 1/48, suntShip 1/0, swiftShip 1/50, victoriaShip 1/25, yanShip 0/13",19,19,0,76], - "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/13, deputShip 0/8, miraShip 0/10, preciShip 0/4, sargonShip 0/2, siracusaShip 0/32, skillfulShip 0/4, soloShip 0/12",null,null,null,68] + "ai_0": [1,12,12,0,3,4,8,"26dd41f4",103,"arcaneShip 1/16, indomShip 1/18, kazimierzShip 1/31, preciShip 2/17, soloShip 1/0, steadShip 0/3, suntShip 1/0, swiftShip 1/21",7,19,12,78], + "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",null,null,null,64], + "ai_2": [1,6,12,0,3,4,8,"4d786b3d",172,"deputShip 2/30, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/60, raidShip 0/16, skillfulShip 1/30, soloShip 0/4, steadShip 0/32, suntShip 1/12, swiftShip 0/6, victoriaShip 1/40, yanShip 0/12",19,19,0,75], + "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",null,null,null,68] }, { "round": 10, - "ai_0": [1,12,13,0,0,5,8,"1c690635",153,"arcaneShip 1/8, indomShip 1/4, kazimierzShip 1/46, kjeragShip 0/5, preciShip 2/56, suntShip 1/0, swiftShip 1/39",17,25,8,88], - "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/29, kjeragShip 0/7, preciShip 0/2, sargonShip 0/8, siracusaShip 0/12, skillfulShip 0/5, steadShip 0/2, swiftShip 0/19",null,null,null,62], - "ai_2": [1,10,13,0,0,5,8,"0e4a12ab",175,"deputShip 0/24, egirShip 0/6, kazimierzShip 0/2, kjeragShip 0/12, lateranoShip 0/2, preciShip 0/4, raidShip 0/6, siracusaShip 0/2, skillfulShip 1/12, soloShip 1/14, steadShip 1/54, suntShip 1/0, swiftShip 1/64, victoriaShip 1/31, yanShip 0/15",25,25,0,89], - "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/13, deputShip 0/8, miraShip 0/10, preciShip 0/4, sargonShip 0/2, siracusaShip 0/32, skillfulShip 0/4, soloShip 0/12",null,null,null,68] + "ai_0": [1,2,15,0,0,5,8,"50e96914",136,"arcaneShip 1/16, indomShip 1/30, kazimierzShip 1/38, preciShip 2/26, soloShip 1/0, steadShip 0/3, suntShip 1/0, swiftShip 1/26",14,25,11,93], + "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",null,null,null,64], + "ai_2": [1,6,15,0,0,5,8,"1e6aa30e",209,"deputShip 2/36, egirShip 0/2, kazimierzShip 0/4, kjeragShip 1/71, raidShip 0/16, skillfulShip 1/38, soloShip 0/4, steadShip 0/32, suntShip 1/16, swiftShip 0/6, victoriaShip 1/48, yanShip 0/14",25,25,0,89], + "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",null,null,null,68] }, { "round": 11, - "ai_0": [1,2,12,0,1,5,8,"1d31e5b7",213,"arcaneShip 1/8, indomShip 0/4, kazimierzShip 1/68, kjeragShip 0/5, preciShip 2/74, raidShip 1/12, suntShip 1/0, swiftShip 1/51",12,25,13,100], - "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/29, kjeragShip 0/7, preciShip 0/2, sargonShip 0/8, siracusaShip 0/12, skillfulShip 0/5, steadShip 0/2, swiftShip 0/19",null,null,null,62], - "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/24, egirShip 0/6, kazimierzShip 0/2, kjeragShip 0/14, lateranoShip 0/2, preciShip 0/6, raidShip 0/6, siracusaShip 0/2, skillfulShip 0/16, soloShip 0/14, steadShip 0/56, swiftShip 0/78, victoriaShip 0/35, yanShip 0/35",6,25,19,100], - "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/13, deputShip 0/8, miraShip 0/10, preciShip 0/4, sargonShip 0/2, siracusaShip 0/32, skillfulShip 0/4, soloShip 0/12",null,null,null,68] - }, - { - "round": 12, - "ai_0": [0,0,13,0,0,6,0,"811c9dc5",0,"arcaneShip 0/8, indomShip 0/4, kazimierzShip 0/88, kjeragShip 0/5, preciShip 0/87, raidShip 0/24, swiftShip 0/56",14,47,33,114], - "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/2, kazimierzShip 0/29, kjeragShip 0/7, preciShip 0/2, sargonShip 0/8, siracusaShip 0/12, skillfulShip 0/5, steadShip 0/2, swiftShip 0/19",null,null,null,62], - "ai_2": [0,0,0,0,0,5,0,"811c9dc5",0,"deputShip 0/24, egirShip 0/6, kazimierzShip 0/2, kjeragShip 0/14, lateranoShip 0/2, preciShip 0/6, raidShip 0/6, siracusaShip 0/2, skillfulShip 0/16, soloShip 0/14, steadShip 0/56, swiftShip 0/78, victoriaShip 0/35, yanShip 0/35",null,null,null,100], - "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/13, deputShip 0/8, miraShip 0/10, preciShip 0/4, sargonShip 0/2, siracusaShip 0/32, skillfulShip 0/4, soloShip 0/12",null,null,null,68] + "ai_0": [0,0,12,0,0,5,0,"811c9dc5",0,"arcaneShip 0/22, indomShip 0/52, kazimierzShip 0/46, preciShip 0/39, steadShip 0/3, swiftShip 0/31",3,25,22,105], + "ai_1": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/4, kazimierzShip 0/17, kjeragShip 0/4, preciShip 0/21, sargonShip 0/4, steadShip 0/16, swiftShip 0/13",null,null,null,64], + "ai_2": [0,0,12,0,0,5,0,"811c9dc5",0,"deputShip 0/40, egirShip 0/2, kazimierzShip 0/4, kjeragShip 0/83, raidShip 0/16, skillfulShip 0/50, soloShip 0/6, steadShip 0/40, suntShip 0/18, swiftShip 0/6, victoriaShip 0/56, yanShip 0/16",16,25,9,100], + "ai_3": [0,0,0,0,0,4,0,"811c9dc5",0,"arcaneShip 0/31, deputShip 0/10, kjeragShip 0/20, preciShip 0/17, sargonShip 0/2, skillfulShip 0/9, soloShip 0/11, steadShip 0/4, suntShip 0/2",null,null,null,68] } ], "players": { "ai_0": { "alive": false, "victory": false, - "roundsPassed": 11, - "eliminatedRound": 12, + "roundsPassed": 10, + "eliminatedRound": 11, "lp": 0, "bandId": "band_sarkazb", "title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"}, "trophies": 2, - "reward": 255, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":3052677,"gold":114,"itemsEquipped":8,"kills":110,"leaks":99,"lpLost":53,"merges":5,"perfectRounds":5,"refreshes":9}, + "reward": 213, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":1569829,"gold":105,"itemsEquipped":9,"kills":91,"leaks":71,"lpLost":53,"merges":3,"perfectRounds":5,"refreshes":12}, "lineup": [], - "bonds": ["arcaneShip 0/8","indomShip 0/4","kazimierzShip 0/88","kjeragShip 0/5","preciShip 0/87","raidShip 0/24","swiftShip 0/56"] + "bonds": ["arcaneShip 0/22","indomShip 0/52","kazimierzShip 0/46","preciShip 0/39","steadShip 0/3","swiftShip 0/31"] }, "ai_1": { "alive": false, @@ -1148,9 +1146,9 @@ "title": null, "trophies": 1, "reward": 149, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":699220,"gold":62,"itemsEquipped":2,"kills":58,"leaks":39,"lpLost":31,"merges":1,"perfectRounds":3,"refreshes":5}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":915590,"gold":64,"itemsEquipped":5,"kills":67,"leaks":30,"lpLost":26,"merges":1,"perfectRounds":5,"refreshes":7}, "lineup": [], - "bonds": ["arcaneShip 0/2","kazimierzShip 0/29","kjeragShip 0/7","preciShip 0/2","sargonShip 0/8","siracusaShip 0/12","skillfulShip 0/5","steadShip 0/2","swiftShip 0/19"] + "bonds": ["arcaneShip 0/4","kazimierzShip 0/17","kjeragShip 0/4","preciShip 0/21","sargonShip 0/4","steadShip 0/16","swiftShip 0/13"] }, "ai_2": { "alive": false, @@ -1162,9 +1160,9 @@ "title": {"id":"comment_5","name":"万事俱备","picId":"comment_icon_5","text":"装备配发数量最多"}, "trophies": 2, "reward": 213, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2969116,"gold":100,"itemsEquipped":10,"kills":147,"leaks":37,"lpLost":28,"merges":3,"perfectRounds":6,"refreshes":9}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2885209,"gold":100,"itemsEquipped":12,"kills":137,"leaks":31,"lpLost":31,"merges":2,"perfectRounds":5,"refreshes":11}, "lineup": [], - "bonds": ["deputShip 0/24","egirShip 0/6","kazimierzShip 0/2","kjeragShip 0/14","lateranoShip 0/2","preciShip 0/6","raidShip 0/6","siracusaShip 0/2","skillfulShip 0/16","soloShip 0/14","steadShip 0/56","swiftShip 0/78","victoriaShip 0/35","yanShip 0/35"] + "bonds": ["deputShip 0/40","egirShip 0/2","kazimierzShip 0/4","kjeragShip 0/83","raidShip 0/16","skillfulShip 0/50","soloShip 0/6","steadShip 0/40","suntShip 0/18","swiftShip 0/6","victoriaShip 0/56","yanShip 0/16"] }, "ai_3": { "alive": false, @@ -1176,9 +1174,9 @@ "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, "trophies": 1, "reward": 149, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":649139,"gold":68,"itemsEquipped":10,"kills":45,"leaks":48,"lpLost":28,"merges":1,"perfectRounds":3,"refreshes":6}, + "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":878906,"gold":68,"itemsEquipped":10,"kills":48,"leaks":45,"lpLost":29,"merges":3,"perfectRounds":3,"refreshes":5}, "lineup": [], - "bonds": ["arcaneShip 0/13","deputShip 0/8","miraShip 0/10","preciShip 0/4","sargonShip 0/2","siracusaShip 0/32","skillfulShip 0/4","soloShip 0/12"] + "bonds": ["arcaneShip 0/31","deputShip 0/10","kjeragShip 0/20","preciShip 0/17","sargonShip 0/2","skillfulShip 0/9","soloShip 0/11","steadShip 0/4","suntShip 0/2"] } } }, @@ -1192,8 +1190,8 @@ "combat": "server", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_1","hiddenBossId":"boss_9","factions":["FLY","DOT","REFLECTION"]}, - "end": {"victory":false,"roundsPassed":13,"hiddenReached":false,"hiddenCleared":false,"reason":"defeat","teamLp":0,"bossPoolLeft":465751,"virtualMs":124050}, - "rng": {"setup":25,"shop":419,"waves":0,"draft":34,"bots":17822,"meta":91}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":17,"bossPoolLeft":0,"virtualMs":121400}, + "rng": {"setup":25,"shop":511,"waves":0,"draft":34,"bots":18062,"meta":112}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1219,77 +1217,77 @@ { "round": 5, "ai_0": [1,29,9,0,0,3,8,"c1f02bc4",13,"arcaneShip 0/2, kjeragShip 0/2, preciShip 1/2, siracusaShip 0/6, soloShip 1/9, steadShip 1/2",15,15,0,31], - "ai_1": [1,28,8,0,2,3,8,"a3c40192",6,"arcaneShip 0/2, kjeragShip 0/4, preciShip 1/2, soloShip 1/0, steadShip 1/4",15,15,0,30] + "ai_1": [1,28,8,0,0,3,8,"a3c40192",6,"arcaneShip 0/2, kjeragShip 0/4, preciShip 1/2, soloShip 1/0, steadShip 1/4",15,15,0,29] }, { "round": 6, - "ai_0": [1,29,9,0,3,3,8,"5840167c",27,"arcaneShip 0/2, kjeragShip 0/2, preciShip 1/2, siracusaShip 1/12, soloShip 1/11, steadShip 2/2",25,25,0,39], - "ai_1": [1,28,11,0,1,3,8,"10bc7ef2",6,"arcaneShip 0/2, kjeragShip 0/4, preciShip 1/2, siracusaShip 1/0, soloShip 1/0, steadShip 2/4",22,22,0,41] + "ai_0": [1,29,9,0,3,3,8,"41bbfcb2",32,"arcaneShip 0/2, deputShip 1/2, kjeragShip 0/2, preciShip 1/4, siracusaShip 1/12, soloShip 1/14, steadShip 0/2",25,25,0,39], + "ai_1": [1,28,9,0,1,3,8,"5d508670",11,"arcaneShip 0/2, kjeragShip 0/9, preciShip 2/7, siracusaShip 1/0, soloShip 1/0, steadShip 1/4",22,22,0,37] }, { "round": 7, - "ai_0": [1,19,13,0,2,4,8,"2567b4e8",41,"arcaneShip 0/2, kjeragShip 0/2, preciShip 1/2, siracusaShip 1/22, soloShip 1/11, steadShip 0/2, swiftShip 1/6",13,27,14,52], - "ai_1": [1,19,11,0,1,4,8,"64998dac",6,"arcaneShip 0/2, kjeragShip 0/4, preciShip 1/2, siracusaShip 1/0, soloShip 1/0, steadShip 2/4",18,27,9,51] + "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] }, { "round": 8, - "ai_0": [1,16,13,0,0,4,8,"b1d5bd05",68,"arcaneShip 1/8, emptyShip 1/0, kjeragShip 0/7, preciShip 1/7, siracusaShip 1/36, soloShip 1/11, steadShip 0/2, swiftShip 1/6",20,23,3,66], - "ai_1": [1,19,12,0,0,4,8,"ff6ef047",44,"arcaneShip 1/8, kjeragShip 0/4, preciShip 2/26, siracusaShip 1/10, soloShip 1/0, steadShip 0/4",23,23,0,62] + "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] }, { "round": 9, - "ai_0": [1,16,12,0,0,4,8,"6a3c0df3",86,"arcaneShip 2/16, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 1/9, siracusaShip 1/44, soloShip 1/11, steadShip 0/2, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",15,15,0,80], - "ai_1": [1,19,12,0,0,4,8,"819efa17",66,"arcaneShip 1/14, emptyShip 1/0, indomShip 0/8, kjeragShip 0/4, preciShip 1/32, siracusaShip 1/20, soloShip 1/0, steadShip 0/4, victoriaShip 0/8, visiShip 0/8",15,15,0,75] + "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] }, { "round": 10, - "ai_0": [1,14,12,0,0,5,8,"aae57ad8",92,"arcaneShip 2/18, emptyShip 1/0, indomShip 0/8, kjeragShip 0/7, preciShip 1/9, siracusaShip 1/48, soloShip 1/11, steadShip 0/2, swiftShip 1/6, victoriaShip 0/8, visiShip 0/8",23,25,2,92], - "ai_1": [1,19,12,0,0,4,8,"edab8beb",93,"arcaneShip 1/16, indomShip 0/8, kjeragShip 0/8, miraShip 1/4, preciShip 1/39, siracusaShip 1/30, soloShip 1/0, steadShip 0/8, suntShip 1/0, swiftShip 1/4, victoriaShip 0/8, visiShip 0/8",23,23,0,87] + "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] }, { "round": 11, - "ai_0": [1,8,12,0,0,5,8,"c448c8be",131,"arcaneShip 2/24, indomShip 0/8, kazimierzShip 0/8, kjeragShip 1/31, preciShip 1/9, siracusaShip 1/56, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 0/14, victoriaShip 0/8, visiShip 0/8",17,23,6,104], - "ai_1": [1,19,12,0,2,5,8,"47df0cde",105,"arcaneShip 2/20, indomShip 0/8, kjeragShip 0/8, miraShip 1/8, preciShip 1/47, siracusaShip 1/30, soloShip 1/0, steadShip 0/8, suntShip 1/0, swiftShip 0/4, victoriaShip 0/8, visiShip 0/8",23,23,0,99] + "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] }, { "round": 12, - "ai_0": [1,8,12,0,0,6,8,"0e145f3c",139,"arcaneShip 2/24, indomShip 0/8, kazimierzShip 0/8, kjeragShip 1/39, preciShip 1/9, siracusaShip 1/56, soloShip 1/11, steadShip 0/2, suntShip 1/0, swiftShip 0/14, victoriaShip 0/8, visiShip 0/8",31,46,15,117], - "ai_1": [1,19,14,0,0,6,8,"2e091bea",143,"arcaneShip 2/20, indomShip 0/8, kjeragShip 0/8, miraShip 1/12, preciShip 1/81, siracusaShip 1/30, soloShip 1/0, steadShip 0/8, swiftShip 0/4, victoriaShip 0/8, visiShip 0/8",46,46,0,113] + "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",29,47,18,119], + "ai_1": [1,17,12,0,0,6,8,"de894d12",133,"arcaneShip 0/2, indomShip 0/8, kjeragShip 1/28, preciShip 2/63, siracusaShip 1/42, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",46,46,0,108] }, { "round": 13, - "ai_0": [0,0,12,0,0,6,0,"811c9dc5",0,"arcaneShip 0/30, indomShip 0/8, kazimierzShip 0/8, kjeragShip 0/55, preciShip 0/9, siracusaShip 0/62, soloShip 0/11, steadShip 0/2, swiftShip 0/30, victoriaShip 0/8, visiShip 0/8",16,39,23,128], - "ai_1": [1,19,12,0,0,6,8,"9fa05801",221,"arcaneShip 2/31, indomShip 0/8, kazimierzShip 0/8, kjeragShip 0/16, maniShip 1/0, miraShip 1/16, preciShip 1/107, siracusaShip 1/43, soloShip 1/0, steadShip 0/8, swiftShip 1/24, victoriaShip 0/8, visiShip 0/8",39,39,0,130] + "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",15,39,24,129], + "ai_1": [1,13,12,0,0,6,8,"ff65891d",177,"arcaneShip 0/4, indomShip 0/8, kjeragShip 1/28, preciShip 2/89, siracusaShip 1/60, soloShip 1/0, steadShip 0/8, suntShip 1/0, victoriaShip 0/8, visiShip 0/8",35,39,4,120] } ], "players": { "ai_0": { - "alive": false, - "victory": false, - "roundsPassed": 12, - "eliminatedRound": 13, - "lp": 0, + "alive": true, + "victory": true, + "roundsPassed": 14, + "eliminatedRound": null, + "lp": 7, "bandId": "band_doberm", - "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, - "trophies": 3, - "reward": 280, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2487063,"gold":128,"itemsEquipped":15,"kills":211,"leaks":64,"lpLost":32,"merges":2,"perfectRounds":6,"refreshes":18}, - "lineup": [], - "bonds": ["arcaneShip 0/30","indomShip 0/8","kazimierzShip 0/8","kjeragShip 0/55","preciShip 0/9","siracusaShip 0/62","soloShip 0/11","steadShip 0/2","swiftShip 0/30","victoriaShip 0/8","visiShip 0/8"] + "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, + "trophies": 4, + "reward": 400, + "stats": {"activatedLayers":285,"bossDamage":626538,"dmgDealt":3441841,"gold":142,"itemsEquipped":20,"kills":250,"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]"], + "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"] }, "ai_1": { "alive": true, - "victory": false, - "roundsPassed": 13, + "victory": true, + "roundsPassed": 14, "eliminatedRound": null, - "lp": 0, + "lp": 10, "bandId": "band_malkie", - "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, - "trophies": 3, - "reward": 320, - "stats": {"activatedLayers":229,"bossDamage":209249,"dmgDealt":3885078,"gold":143,"itemsEquipped":22,"kills":284,"leaks":9,"lpLost":9,"merges":4,"perfectRounds":12,"refreshes":16}, - "lineup": ["5_15_a@11,3[3_01_e_b+1_01_e_a]","1_17_b@11,6","6_11_a@11,7","6_19_a@11,8[4_04_e_a+4_10_e_a]","6_01_a@10,3[1_02_e_b+5_07_e_a]","4_10_a@10,7[3_08_e_a+3_01_e_b]","1_08_b@10,8[4_03_e_a+6_09_e_a]","4_02_a@9,9[3_11_e_a+1_05_e_a]"], - "bonds": ["arcaneShip 2/37","indomShip 0/8","kazimierzShip 0/16","kjeragShip 0/24","lateranoShip 1/0","maniShip 1/0","miraShip 0/16","preciShip 1/111","siracusaShip 1/45","soloShip 1/0","steadShip 0/8","suntShip 1/0","swiftShip 1/36","victoriaShip 0/8","visiShip 0/8"] + "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, + "trophies": 4, + "reward": 400, + "stats": {"activatedLayers":177,"bossDamage":723462,"dmgDealt":4024219,"gold":132,"itemsEquipped":21,"kills":257,"leaks":15,"lpLost":15,"merges":3,"perfectRounds":10,"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]"], + "bonds": ["arcaneShip 0/4","indomShip 0/8","kjeragShip 1/28","preciShip 2/89","siracusaShip 1/60","soloShip 1/0","steadShip 0/8","suntShip 1/0","victoriaShip 0/8","visiShip 0/8"] } } }, @@ -1304,7 +1302,7 @@ "boost": {"lp":400,"layers":300}, "setup": {"stageId":"act1autochess_m02","bossId":"boss_5","hiddenBossId":"boss_10","factions":["TIMES","ELEMENT","DOT"]}, "end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":398,"bossPoolLeft":0,"virtualMs":113700}, - "rng": {"setup":25,"shop":356,"waves":0,"draft":42,"bots":10350,"meta":3}, + "rng": {"setup":25,"shop":356,"waves":0,"draft":42,"bots":10350,"meta":2}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1325,39 +1323,39 @@ }, { "round": 5, - "ai_0": [1,400,9,0,0,3,8,"6add2b99",603,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/303, steadShip 1/300, suntShip 0/300, swiftShip 0/308, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,30] + "ai_0": [1,400,9,0,0,3,8,"99ba6d0e",603,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/303, steadShip 1/300, suntShip 0/300, swiftShip 0/308, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,30] }, { "round": 6, - "ai_0": [1,400,9,0,0,3,8,"01500129",605,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/305, steadShip 1/300, suntShip 0/300, swiftShip 0/308, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",23,23,0,39] + "ai_0": [1,400,9,0,0,3,8,"b18a2483",605,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/305, steadShip 1/300, suntShip 0/300, swiftShip 0/308, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",23,23,0,37] }, { "round": 7, - "ai_0": [1,400,10,0,0,4,8,"00e5b748",607,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/307, steadShip 1/300, suntShip 0/300, swiftShip 0/308, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",25,25,0,51] + "ai_0": [1,398,10,0,0,4,8,"edb5e276",607,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/307, steadShip 1/300, suntShip 0/300, swiftShip 0/308, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",23,25,2,47] }, { "round": 8, - "ai_0": [1,399,11,0,0,4,8,"6b809742",1841,"arcaneShip 1/307, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 1/311, skillfulShip 0/300, soloShip 1/307, steadShip 2/300, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",18,19,1,62] + "ai_0": [1,398,11,0,0,4,8,"1dc1f7ca",1535,"arcaneShip 1/304, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/305, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/304, raidShip 0/300, sargonShip 0/300, siracusaShip 0/306, skillfulShip 1/308, soloShip 1/307, steadShip 0/300, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",19,19,0,56] }, { "round": 9, - "ai_0": [1,399,12,0,0,4,8,"679aaaf0",2157,"arcaneShip 1/314, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/302, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 1/315, skillfulShip 1/303, soloShip 1/307, steadShip 0/300, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",21,21,0,75] + "ai_0": [1,398,12,0,2,4,8,"14393928",1840,"arcaneShip 1/310, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/305, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/304, raidShip 0/300, sargonShip 0/300, siracusaShip 0/306, skillfulShip 1/311, soloShip 0/307, steadShip 1/303, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",21,21,0,69] }, { "round": 10, - "ai_0": [1,399,12,0,0,5,8,"1e2c3fd7",2165,"arcaneShip 1/317, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/304, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 1/315, skillfulShip 1/306, soloShip 1/307, steadShip 0/300, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",26,26,0,86] + "ai_0": [1,398,14,0,0,5,8,"1bc668c4",1840,"arcaneShip 1/310, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 1/308, kjeragShip 0/310, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/304, raidShip 0/300, sargonShip 0/300, siracusaShip 0/306, skillfulShip 1/316, soloShip 0/307, steadShip 1/306, suntShip 1/300, swiftShip 0/319, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",26,26,0,84] }, { "round": 11, - "ai_0": [1,399,12,0,2,5,8,"da131f5d",2182,"arcaneShip 1/323, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/306, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 1/321, skillfulShip 1/309, soloShip 1/307, steadShip 0/300, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",34,34,0,97] + "ai_0": [1,398,12,0,2,5,8,"9dc663a0",2467,"arcaneShip 1/310, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 1/308, kjeragShip 0/310, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/309, raidShip 0/300, sargonShip 0/300, siracusaShip 0/306, skillfulShip 0/316, soloShip 1/307, steadShip 1/309, suntShip 1/300, swiftShip 1/324, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",34,34,0,98] }, { "round": 12, - "ai_0": [1,399,14,0,0,6,8,"63f60b1e",2194,"arcaneShip 1/328, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/308, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 1/326, skillfulShip 1/309, soloShip 1/307, steadShip 0/300, suntShip 1/300, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",115,115,0,111] + "ai_0": [1,398,14,0,0,6,8,"402ad3d6",2480,"arcaneShip 1/310, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 1/308, kjeragShip 0/310, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/314, raidShip 0/300, sargonShip 0/300, siracusaShip 0/306, skillfulShip 0/316, soloShip 1/307, steadShip 1/312, suntShip 1/300, swiftShip 1/329, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",114,114,0,112] }, { "round": 13, - "ai_0": [1,399,12,0,2,6,8,"5091cd4e",1940,"arcaneShip 2/360, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/308, investShip 0/300, kazimierzShip 0/308, kjeragShip 0/300, lateranoShip 1/306, maniShip 0/300, miraShip 0/300, preciShip 0/302, raidShip 0/300, sargonShip 0/300, siracusaShip 1/332, skillfulShip 1/335, soloShip 1/307, steadShip 0/300, suntShip 1/300, swiftShip 0/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",35,35,0,150] + "ai_0": [1,398,12,0,0,6,8,"2a88f27f",3163,"arcaneShip 1/334, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 1/308, kjeragShip 0/310, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/323, raidShip 0/300, sargonShip 0/300, siracusaShip 0/306, skillfulShip 1/336, soloShip 1/307, steadShip 1/315, suntShip 1/300, swiftShip 1/340, victoriaShip 0/300, visiShip 1/300, yanShip 0/300",35,35,0,142] } ], "players": { @@ -1371,9 +1369,9 @@ "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 0, "reward": 425, - "stats": {"activatedLayers":2279,"bossDamage":4580000,"dmgDealt":6590189,"gold":175,"itemsEquipped":26,"kills":376,"leaks":1,"lpLost":1,"merges":6,"perfectRounds":12,"refreshes":35}, - "lineup": ["4_02_b@12,7[3_08_e_a+3_03_e_b]","6_01_a@12,8[3_02_e_a]","6_16_a@11,5[1_05_e_b+3_03_e_a]","3_18_a@11,8[4_11_e_a+4_05_e_b]","4_10_b@10,4[4_07_e_a+1_02_e_b]","6_05_a@10,5","4_01_b@10,6[6_03_e_a+4_06_e_a]","1_08_b@9,9"], - "bonds": ["arcaneShip 2/395","deputShip 0/300","egirShip 0/300","emptyShip 0/300","indomShip 0/308","investShip 0/300","kazimierzShip 0/308","kjeragShip 0/300","lateranoShip 1/306","maniShip 0/300","miraShip 0/300","preciShip 1/302","raidShip 0/300","sargonShip 0/300","siracusaShip 1/353","skillfulShip 0/343","soloShip 1/307","steadShip 0/300","suntShip 1/300","swiftShip 1/316","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"] + "stats": {"activatedLayers":2931,"bossDamage":4580000,"dmgDealt":6350821,"gold":167,"itemsEquipped":19,"kills":371,"leaks":2,"lpLost":2,"merges":5,"perfectRounds":12,"refreshes":35}, + "lineup": ["1_17_b@12,7[1_05_e_a]","3_17_a@12,8[1_02_e_b+2_01_e_b]","6_16_a@11,5","5_06_a@11,8[5_02_e_a+4_04_e_a]","4_14_b@10,4[4_03_e_a+1_01_e_b]","2_18_b@10,5[4_05_e_a+1_02_e_a]","3_16_a@9,5[4_02_e_a]","1_19_b@9,9[1_05_e_b+2_01_e_a]"], + "bonds": ["arcaneShip 1/348","deputShip 0/300","egirShip 0/300","emptyShip 0/300","indomShip 1/300","investShip 0/300","kazimierzShip 1/311","kjeragShip 0/310","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/338","raidShip 0/300","sargonShip 0/300","siracusaShip 0/306","skillfulShip 1/350","soloShip 1/307","steadShip 1/322","suntShip 1/300","swiftShip 1/355","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"] } } }, @@ -1387,8 +1385,8 @@ "combat": "client", "boost": {"lp":400,"layers":300}, "setup": {"stageId":"act2autochess_m04","bossId":"boss_5","hiddenBossId":"boss_9","factions":["FLY","ELEMENT","INVISIBLE"]}, - "end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":800,"bossPoolLeft":0,"virtualMs":126150}, - "rng": {"setup":25,"shop":651,"waves":0,"draft":61,"bots":22075,"meta":162}, + "end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":641,"bossPoolLeft":0,"virtualMs":124650}, + "rng": {"setup":25,"shop":667,"waves":0,"draft":61,"bots":22075,"meta":140}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1408,53 +1406,53 @@ }, { "round": 4, - "ai_0": [1,400,8,0,1,2,8,"7a73d63d",1206,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/300, soloShip 1/300, steadShip 1/304, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",21,21,0,23], - "ai_1": [1,400,9,0,2,2,8,"a5d7e73a",909,"arcaneShip 0/302, deputShip 1/302, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/305, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/302, steadShip 0/300, suntShip 0/300, swiftShip 0/300, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",22,24,2,24] + "ai_0": [1,400,8,0,1,2,8,"6153ae71",906,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/300, steadShip 1/304, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",21,21,0,23], + "ai_1": [1,400,9,0,5,2,8,"c56b1a00",909,"arcaneShip 0/302, deputShip 1/302, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/305, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/302, steadShip 0/300, suntShip 0/300, swiftShip 0/300, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",22,22,0,25] }, { "round": 5, - "ai_0": [1,400,9,0,0,3,8,"e81795ce",1207,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/300, soloShip 1/300, steadShip 1/305, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,31], - "ai_1": [1,400,10,0,0,3,8,"7ef5eef5",1218,"arcaneShip 0/302, deputShip 1/304, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/308, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/302, suntShip 0/300, swiftShip 0/300, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,34] + "ai_0": [1,400,9,0,0,3,8,"74d81bd7",1210,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/302, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/303, soloShip 1/300, steadShip 1/305, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,32], + "ai_1": [1,400,13,0,0,3,8,"e04712f6",1523,"arcaneShip 1/304, deputShip 1/304, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/308, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 0/300, suntShip 0/300, swiftShip 0/300, victoriaShip 1/303, visiShip 0/300, yanShip 0/300",24,24,0,38] }, { "round": 6, - "ai_0": [1,400,9,0,2,3,8,"5e0551fe",1528,"arcaneShip 1/307, deputShip 1/302, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/304, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/302, steadShip 1/313, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",27,27,0,41], - "ai_1": [1,400,9,0,3,3,8,"55653a0f",1228,"arcaneShip 0/302, deputShip 1/306, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/311, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/306, steadShip 2/305, suntShip 0/300, swiftShip 0/300, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",28,28,0,42] + "ai_0": [1,400,9,0,0,3,8,"30588739",1220,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/307, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 2/308, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/306, soloShip 1/300, steadShip 1/306, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",27,27,0,42], + "ai_1": [1,400,9,0,0,3,8,"50b45347",1535,"arcaneShip 1/306, deputShip 1/306, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/311, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/306, steadShip 0/300, suntShip 0/300, swiftShip 0/300, victoriaShip 1/306, visiShip 0/300, yanShip 0/300",27,27,0,45] }, { "round": 7, - "ai_0": [1,400,12,0,1,4,8,"d26ea2d9",1544,"arcaneShip 1/312, deputShip 1/304, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/306, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/318, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",25,25,0,54], - "ai_1": [1,400,13,0,0,4,8,"5d59190b",1544,"arcaneShip 1/304, deputShip 1/308, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/314, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 1/310, suntShip 0/300, swiftShip 0/300, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",25,25,0,55] + "ai_0": [1,400,10,0,0,4,8,"8f4587cd",1228,"arcaneShip 0/302, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/307, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 2/311, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/309, soloShip 1/300, steadShip 1/308, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",25,25,0,54], + "ai_1": [1,400,10,0,0,4,8,"2daf0b74",1547,"arcaneShip 1/308, deputShip 1/308, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/314, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 0/300, suntShip 0/300, swiftShip 0/300, victoriaShip 1/309, visiShip 0/300, yanShip 0/300",25,25,0,54] }, { "round": 8, - "ai_0": [1,400,12,0,0,4,8,"d692537c",1558,"arcaneShip 1/315, deputShip 0/304, egirShip 1/307, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 2/310, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 2/322, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",18,18,0,64], - "ai_1": [1,400,11,0,2,4,8,"bf903083",1573,"arcaneShip 1/312, deputShip 1/310, egirShip 0/300, emptyShip 0/300, indomShip 1/312, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/331, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 0/308, steadShip 0/310, suntShip 0/300, swiftShip 0/300, victoriaShip 1/308, visiShip 0/300, yanShip 0/305",20,20,0,66] + "ai_0": [1,400,11,0,0,4,8,"e8308b42",1534,"arcaneShip 1/306, deputShip 0/300, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/307, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 2/311, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/309, soloShip 1/300, steadShip 1/310, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",18,18,0,64], + "ai_1": [1,400,11,0,3,4,8,"cbdb5901",1562,"arcaneShip 1/310, deputShip 1/310, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/326, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 0/308, steadShip 1/304, suntShip 0/300, swiftShip 0/300, victoriaShip 1/312, visiShip 0/300, yanShip 0/300",19,19,0,63] }, { "round": 9, - "ai_0": [1,400,12,0,2,4,8,"03c507e7",2199,"arcaneShip 1/326, deputShip 0/304, egirShip 1/316, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 1/306, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/314, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/333, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,77], - "ai_1": [1,400,14,0,1,4,8,"30c9a0fa",1605,"arcaneShip 1/314, deputShip 1/312, egirShip 0/300, emptyShip 0/300, indomShip 1/326, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/342, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 0/308, steadShip 0/310, suntShip 0/300, swiftShip 0/300, victoriaShip 1/311, visiShip 0/300, yanShip 0/305",25,26,1,79] + "ai_0": [1,400,12,0,2,4,8,"c3934d4d",1864,"arcaneShip 1/314, deputShip 1/302, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/309, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/313, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/311, soloShip 0/300, steadShip 1/315, suntShip 0/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,75], + "ai_1": [1,400,15,0,4,4,8,"eb426307",1574,"arcaneShip 1/310, deputShip 0/310, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/336, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 1/307, suntShip 0/300, swiftShip 0/300, victoriaShip 1/313, visiShip 0/300, yanShip 0/300",24,24,0,79] }, { "round": 10, - "ai_0": [1,400,14,0,0,5,8,"196c874a",2220,"arcaneShip 1/328, deputShip 0/304, egirShip 1/325, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 1/310, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/318, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/335, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",12,12,0,92], - "ai_1": [1,400,13,0,0,5,8,"a300807f",1921,"arcaneShip 1/314, deputShip 0/312, egirShip 0/300, emptyShip 0/300, indomShip 1/326, investShip 0/300, kazimierzShip 1/305, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/350, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 0/308, steadShip 1/314, suntShip 0/300, swiftShip 0/300, victoriaShip 1/312, visiShip 0/300, yanShip 0/305",12,12,0,91] + "ai_0": [1,400,14,0,0,5,8,"3aef9421",2166,"arcaneShip 0/314, deputShip 1/304, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/311, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/315, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/313, soloShip 1/302, steadShip 1/319, suntShip 1/302, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",12,12,0,89], + "ai_1": [1,400,16,0,0,5,8,"2a7d9f59",1576,"arcaneShip 1/310, deputShip 0/310, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/344, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 0/307, suntShip 0/300, swiftShip 0/300, victoriaShip 1/314, visiShip 0/300, yanShip 0/300",12,13,1,94] }, { "round": 11, - "ai_0": [1,400,12,0,2,5,8,"e69d16c5",2563,"arcaneShip 1/341, deputShip 0/304, egirShip 1/335, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 1/326, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/318, raidShip 1/313, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/308, soloShip 1/304, steadShip 1/336, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",34,34,0,105], - "ai_1": [1,400,12,0,4,5,8,"26f914c8",1924,"arcaneShip 1/316, deputShip 1/316, egirShip 0/300, emptyShip 0/300, indomShip 0/326, investShip 0/300, kazimierzShip 0/305, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/354, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 0/308, steadShip 1/319, suntShip 1/302, swiftShip 0/300, victoriaShip 1/317, visiShip 0/300, yanShip 0/305",35,35,0,103] + "ai_0": [1,400,12,0,2,5,8,"0665e01c",2194,"arcaneShip 0/314, deputShip 1/306, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/319, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/317, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/315, soloShip 1/304, steadShip 1/329, suntShip 1/304, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",34,34,0,101], + "ai_1": [1,400,12,0,3,5,8,"976cdb21",1907,"arcaneShip 1/310, deputShip 0/310, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/360, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 1/314, suntShip 0/300, swiftShip 0/300, victoriaShip 1/315, visiShip 0/300, yanShip 0/300",34,34,0,104] }, { "round": 12, - "ai_0": [1,400,14,0,0,6,8,"69e2bf2f",2613,"arcaneShip 1/354, deputShip 0/304, egirShip 1/346, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 1/337, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/318, raidShip 1/317, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/316, soloShip 1/304, steadShip 1/339, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",8,14,6,117], - "ai_1": [1,400,16,0,1,6,8,"7a68a955",1945,"arcaneShip 1/318, deputShip 1/320, egirShip 0/300, emptyShip 0/300, indomShip 0/326, investShip 0/300, kazimierzShip 0/305, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/358, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 0/308, steadShip 1/323, suntShip 1/304, swiftShip 0/300, victoriaShip 1/322, visiShip 0/300, yanShip 0/305",15,15,0,119] + "ai_0": [1,400,14,0,0,6,8,"9229fb84",2208,"arcaneShip 0/314, deputShip 1/308, egirShip 0/300, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/321, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/319, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/317, soloShip 1/306, steadShip 1/331, suntShip 1/306, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",14,14,0,115], + "ai_1": [1,400,15,0,0,6,8,"ec840519",1917,"arcaneShip 1/310, deputShip 0/310, egirShip 0/300, emptyShip 0/300, indomShip 1/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/363, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/308, steadShip 1/320, suntShip 0/300, swiftShip 0/300, victoriaShip 1/316, visiShip 0/300, yanShip 0/300",9,16,7,119] }, { "round": 13, - "ai_0": [1,400,12,0,2,6,8,"f10059d1",2677,"arcaneShip 1/367, deputShip 0/304, egirShip 1/353, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 1/356, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 0/318, raidShip 1/330, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/324, soloShip 1/304, steadShip 1/343, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",41,41,0,129], - "ai_1": [1,400,13,0,2,6,8,"db98f88d",2283,"arcaneShip 1/322, deputShip 1/322, egirShip 0/300, emptyShip 0/300, indomShip 0/326, investShip 0/300, kazimierzShip 0/305, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 2/362, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/310, steadShip 1/331, suntShip 1/306, swiftShip 0/300, victoriaShip 1/330, visiShip 0/300, yanShip 0/305",41,41,0,132] + "ai_0": [1,400,12,0,2,6,8,"31644d65",2842,"arcaneShip 0/314, deputShip 1/313, egirShip 0/300, emptyShip 1/302, indomShip 1/302, investShip 0/300, kazimierzShip 0/302, kjeragShip 1/331, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/326, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/319, soloShip 0/306, steadShip 1/339, suntShip 1/308, swiftShip 0/302, victoriaShip 0/300, visiShip 1/302, yanShip 0/300",41,41,0,149], + "ai_1": [1,400,12,0,4,6,8,"10ed5f25",2256,"arcaneShip 1/312, deputShip 1/312, egirShip 0/300, emptyShip 0/300, indomShip 1/302, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/389, raidShip 0/300, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/302, soloShip 1/310, steadShip 1/329, suntShip 0/300, swiftShip 0/300, victoriaShip 0/316, visiShip 0/300, yanShip 0/300",43,43,0,133] } ], "players": { @@ -1463,28 +1461,28 @@ "victory": true, "roundsPassed": 15, "eliminatedRound": null, - "lp": 400, + "lp": 321, "bandId": "band_sarkazb", "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, "trophies": 8, "reward": 425, - "stats": {"activatedLayers":2713,"bossDamage":1676605,"dmgDealt":10050362,"gold":153,"itemsEquipped":19,"kills":283,"leaks":6,"lpLost":0,"merges":4,"perfectRounds":12,"refreshes":33}, - "lineup": ["2_18_b@11,5[2_01_e_a+3_03_e_a]","3_13_b@11,8","4_09_a@10,3","3_05_a@10,8[1_01_e_b+4_05_e_b]","3_09_a@9,6","6_16_a@9,7[3_11_e_a+1_02_e_b]","4_20_a@9,8","3_12_a@9,9[4_03_e_a]"], - "bonds": ["arcaneShip 1/387","deputShip 0/304","egirShip 1/353","emptyShip 0/300","indomShip 0/300","investShip 0/300","kazimierzShip 1/356","kjeragShip 0/302","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 0/318","raidShip 1/330","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/340","soloShip 1/304","steadShip 1/343","suntShip 1/300","swiftShip 0/302","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"] + "stats": {"activatedLayers":2606,"bossDamage":6895804,"dmgDealt":22127730,"gold":175,"itemsEquipped":24,"kills":294,"leaks":0,"lpLost":0,"merges":6,"perfectRounds":13,"refreshes":31}, + "lineup": ["1_02_b@12,8[6_04_e_a+1_02_e_b]","3_16_b@11,7[4_02_e_b+5_01_e_a]","3_11_b@11,8[3_01_e_a+1_01_e_b]","5_11_a@10,4[3_08_e_b]","5_06_a@10,8[2_01_e_a+4_11_e_a]","4_21_b@9,7","6_12_a@9,8[1_05_e_a+3_04_e_a]","3_20_a@9,9[5_02_e_a+3_07_e_a]"], + "bonds": ["arcaneShip 0/314","deputShip 1/317","egirShip 0/300","emptyShip 0/304","indomShip 1/306","investShip 0/300","kazimierzShip 0/302","kjeragShip 1/345","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/340","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/323","soloShip 1/308","steadShip 2/355","suntShip 1/312","swiftShip 0/302","victoriaShip 0/300","visiShip 0/304","yanShip 0/300"] }, "ai_1": { "alive": true, "victory": true, "roundsPassed": 15, "eliminatedRound": null, - "lp": 400, + "lp": 320, "bandId": "band_doberm", "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 8, "reward": 425, - "stats": {"activatedLayers":2334,"bossDamage":12218936,"dmgDealt":34332028,"gold":157,"itemsEquipped":29,"kills":286,"leaks":3,"lpLost":0,"merges":4,"perfectRounds":11,"refreshes":30}, - "lineup": ["3_16_a@12,8[3_09_e_b+4_07_e_a]","2_10_a@11,5[3_11_e_a+2_03_e_b]","6_05_a@11,8[3_05_e_b+3_10_e_b]","6_08_a@10,3[3_01_e_b+1_01_e_b]","5_11_a@10,4[4_12_e_a+5_09_e_a]","6_14_a@9,7[4_09_e_a+4_04_e_b]","1_06_b@9,8[1_05_e_b+2_01_e_b]","1_17_b@9,9[2_03_e_b+2_03_e_a]"], - "bonds": ["arcaneShip 1/330","deputShip 1/326","egirShip 0/300","emptyShip 0/300","indomShip 0/326","investShip 0/300","kazimierzShip 0/305","kjeragShip 0/300","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 2/366","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 0/300","soloShip 1/316","steadShip 1/340","suntShip 1/310","swiftShip 0/300","victoriaShip 1/346","visiShip 0/300","yanShip 0/305"] + "stats": {"activatedLayers":2575,"bossDamage":6983028,"dmgDealt":20341827,"gold":159,"itemsEquipped":35,"kills":270,"leaks":8,"lpLost":0,"merges":5,"perfectRounds":11,"refreshes":34}, + "lineup": ["4_21_a@12,3[5_03_e_a+3_06_e_a]","2_10_b@11,5[2_03_e_a+2_03_e_b]","5_06_a@11,8[3_09_e_b+3_09_e_b]","5_02_a@10,3[3_06_e_b+2_03_e_b]","2_17_a@10,4[5_09_e_a+1_05_e_a]","4_07_a@10,8[3_09_e_a+5_02_e_b]","1_17_a@9,8[4_09_e_b+3_10_e_b]","1_06_b@9,9[3_02_e_a+4_09_e_b]"], + "bonds": ["arcaneShip 1/316","deputShip 1/316","egirShip 0/300","emptyShip 0/300","indomShip 1/306","investShip 0/300","kazimierzShip 0/300","kjeragShip 0/300","lateranoShip 0/300","maniShip 0/300","miraShip 0/300","preciShip 1/393","raidShip 0/300","sargonShip 0/300","siracusaShip 0/300","skillfulShip 1/306","soloShip 1/314","steadShip 0/337","suntShip 1/304","swiftShip 0/300","victoriaShip 1/320","visiShip 0/300","yanShip 0/300"] } } }, @@ -1498,8 +1496,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":"boss_10","factions":["DOT","INVISIBLE","REFLECTION"]}, - "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":1,"bossPoolLeft":0,"virtualMs":115050}, - "rng": {"setup":25,"shop":495,"waves":0,"draft":34,"bots":17791,"meta":11}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":18,"bossPoolLeft":0,"virtualMs":116900}, + "rng": {"setup":25,"shop":542,"waves":0,"draft":34,"bots":18059,"meta":26}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ { @@ -1519,53 +1517,53 @@ }, { "round": 4, - "h_0": [1,28,7,0,0,2,8,"7cb948b2",8,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, raidShip 1/8, soloShip 1/0, swiftShip 0/8, yanShip 0/1",9,9,0,22], - "ai_0": [1,23,7,0,0,1,8,"b679c297",10,"arcaneShip 0/2, deputShip 1/2, kazimierzShip 0/4, preciShip 0/2, sargonShip 0/2, swiftShip 1/8",9,9,0,22] + "h_0": [1,28,7,0,0,2,8,"513a21f2",14,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, siracusaShip 0/6, soloShip 1/0, swiftShip 1/14, yanShip 0/1",9,9,0,22], + "ai_0": [1,23,7,0,0,1,8,"c0623de1",4,"arcaneShip 0/2, kazimierzShip 0/4, preciShip 0/2, sargonShip 0/2, swiftShip 1/4",9,9,0,22] }, { "round": 5, - "h_0": [1,28,8,0,0,3,8,"d543ab44",16,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, raidShip 1/16, soloShip 1/0, swiftShip 0/8, yanShip 0/1",21,21,0,30], - "ai_0": [1,23,8,0,0,2,8,"b5d78234",34,"arcaneShip 0/2, deputShip 1/4, kazimierzShip 0/8, lateranoShip 1/5, preciShip 1/7, sargonShip 0/2, suntShip 1/2, swiftShip 1/16",21,21,0,30] + "h_0": [1,28,8,0,0,3,8,"c8c0f064",14,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, siracusaShip 0/6, soloShip 1/0, swiftShip 1/14, yanShip 0/1",21,21,0,30], + "ai_0": [1,23,8,0,0,2,8,"2c9d6c93",19,"arcaneShip 0/2, deputShip 1/2, kazimierzShip 0/8, preciShip 0/2, sargonShip 0/2, skillfulShip 1/2, suntShip 1/2, swiftShip 1/13",21,21,0,30] }, { "round": 6, - "h_0": [1,28,9,0,2,3,8,"9b4b0e34",32,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, raidShip 1/24, soloShip 1/0, swiftShip 1/8, yanShip 0/1",31,31,0,39], - "ai_0": [1,23,9,0,1,3,8,"c6b88ba7",55,"arcaneShip 0/2, deputShip 2/8, kazimierzShip 0/8, lateranoShip 1/18, preciShip 0/10, sargonShip 0/2, suntShip 1/6, swiftShip 1/23",31,31,0,39] + "h_0": [1,28,9,0,2,3,8,"6d8e12fd",26,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, siracusaShip 1/12, soloShip 1/0, swiftShip 1/14, yanShip 0/1",31,31,0,39], + "ai_0": [1,23,9,0,1,3,8,"1a987a83",44,"arcaneShip 0/2, deputShip 1/7, kazimierzShip 0/8, preciShip 0/2, sargonShip 0/2, siracusaShip 0/6, skillfulShip 1/9, suntShip 1/4, swiftShip 1/24",31,31,0,39] }, { "round": 7, - "h_0": [1,28,12,0,2,4,8,"f09ca8b6",44,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, raidShip 1/36, soloShip 1/0, swiftShip 1/8, yanShip 0/1",34,34,0,50], - "ai_0": [1,23,11,0,1,4,8,"f6f38045",80,"arcaneShip 0/2, deputShip 2/12, kazimierzShip 0/8, lateranoShip 1/28, preciShip 0/10, sargonShip 0/2, suntShip 1/10, swiftShip 1/30",34,34,0,50] + "h_0": [1,28,12,0,2,4,8,"9f2332ce",32,"arcaneShip 0/4, kazimierzShip 0/8, preciShip 0/4, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/14, yanShip 0/5",34,34,0,51], + "ai_0": [1,23,11,0,1,4,8,"36579fa0",65,"arcaneShip 0/2, deputShip 1/9, kazimierzShip 0/8, preciShip 0/2, raidShip 1/2, sargonShip 0/2, siracusaShip 0/6, skillfulShip 1/16, suntShip 1/6, swiftShip 1/32",34,34,0,52] }, { "round": 8, - "h_0": [1,28,13,0,2,4,8,"2bca9c35",60,"arcaneShip 1/4, kazimierzShip 0/8, preciShip 0/4, raidShip 1/48, skillfulShip 1/0, swiftShip 1/8, yanShip 0/1",15,15,0,62], - "ai_0": [1,23,12,0,0,4,8,"87633ca6",109,"arcaneShip 0/2, deputShip 2/16, kazimierzShip 0/8, lateranoShip 1/41, preciShip 0/10, sargonShip 0/2, skillfulShip 1/4, suntShip 1/14, swiftShip 1/34",15,15,0,62] + "h_0": [1,28,13,0,0,4,8,"905534ef",53,"arcaneShip 1/7, kazimierzShip 0/8, preciShip 1/9, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/19, yanShip 0/5",15,15,0,64], + "ai_0": [1,23,12,0,0,4,8,"01511dde",72,"arcaneShip 0/2, deputShip 0/12, kazimierzShip 0/8, lateranoShip 1/0, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/10, skillfulShip 1/21, suntShip 1/6, swiftShip 1/35",15,15,0,64] }, { "round": 9, - "h_0": [1,27,14,0,0,4,8,"70a00d2f",44,"arcaneShip 1/4, kazimierzShip 1/12, preciShip 1/9, raidShip 0/48, skillfulShip 1/3, soloShip 1/0, swiftShip 1/16, yanShip 0/1",22,23,1,76], - "ai_0": [1,20,12,0,0,4,8,"951b6406",164,"arcaneShip 0/2, deputShip 1/18, kazimierzShip 0/8, lateranoShip 1/56, preciShip 1/20, sargonShip 0/2, skillfulShip 1/11, suntShip 1/16, swiftShip 1/43",20,23,3,75] + "h_0": [1,28,12,0,0,4,8,"f78a00e2",66,"arcaneShip 1/10, kazimierzShip 0/8, preciShip 1/14, siracusaShip 1/18, suntShip 1/0, swiftShip 1/24, yanShip 0/13",23,23,0,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] }, { "round": 10, - "h_0": [1,27,12,0,0,5,8,"59b986b7",70,"arcaneShip 1/7, kazimierzShip 1/19, preciShip 1/14, raidShip 0/48, skillfulShip 1/6, soloShip 1/3, swiftShip 1/21, yanShip 0/1",31,31,0,87], - "ai_0": [1,20,12,0,0,5,8,"fa71d132",191,"arcaneShip 0/2, deputShip 0/18, kazimierzShip 0/8, lateranoShip 1/84, preciShip 1/25, sargonShip 0/2, skillfulShip 1/16, suntShip 1/16, swiftShip 1/50",31,31,0,88] + "h_0": [1,28,12,0,0,5,8,"57b54089",79,"arcaneShip 1/13, kazimierzShip 0/8, preciShip 1/19, siracusaShip 1/18, soloShip 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] }, { "round": 11, - "h_0": [1,19,12,0,0,5,8,"b7278144",146,"arcaneShip 0/7, kazimierzShip 1/34, preciShip 2/19, raidShip 1/61, skillfulShip 0/9, soloShip 1/6, suntShip 1/0, swiftShip 1/26, yanShip 0/1",17,25,8,99], - "ai_0": [1,18,12,0,0,5,8,"e240abda",229,"arcaneShip 0/2, deputShip 0/18, kazimierzShip 0/8, lateranoShip 1/92, preciShip 1/33, raidShip 1/5, sargonShip 0/2, skillfulShip 1/21, suntShip 1/16, swiftShip 1/57, visiShip 1/5",23,25,2,100] + "h_0": [1,24,12,0,2,5,8,"def68119",93,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/26, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/34, yanShip 0/13",21,25,4,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] }, { "round": 12, - "h_0": [1,11,12,0,0,6,8,"955db8af",172,"arcaneShip 0/7, kazimierzShip 1/42, preciShip 2/24, raidShip 1/69, skillfulShip 0/9, soloShip 1/6, suntShip 1/0, swiftShip 1/31, yanShip 0/1",23,31,8,111], - "ai_0": [1,8,12,0,2,6,8,"959a33fe",267,"arcaneShip 0/2, deputShip 0/18, kazimierzShip 0/8, lateranoShip 1/100, preciShip 1/41, raidShip 1/10, sargonShip 0/2, skillfulShip 1/26, suntShip 1/16, swiftShip 1/64, visiShip 1/10",13,31,18,113] + "h_0": [1,16,14,0,0,6,8,"0dd59e58",103,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/31, siracusaShip 1/18, suntShip 1/0, swiftShip 1/39, yanShip 0/13",23,31,8,117], + "ai_0": [1,22,12,0,1,6,8,"defd2806",250,"arcaneShip 0/2, deputShip 1/32, kazimierzShip 0/8, lateranoShip 1/45, miraShip 1/6, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/42, skillfulShip 1/62, suntShip 1/14, swiftShip 1/49",30,31,1,112] }, { "round": 13, - "h_0": [1,1,12,0,0,6,8,"4b1c1c8d",241,"arcaneShip 1/7, kazimierzShip 1/54, preciShip 2/34, raidShip 1/105, skillfulShip 0/9, soloShip 0/6, suntShip 1/0, swiftShip 1/41, yanShip 0/1",20,57,37,122], - "ai_0": [0,0,14,0,0,6,0,"811c9dc5",0,"arcaneShip 0/2, deputShip 0/18, kazimierzShip 0/8, lateranoShip 0/108, preciShip 0/46, raidShip 0/15, sargonShip 0/2, skillfulShip 0/31, suntShip 0/16, swiftShip 0/74, visiShip 0/15",18,57,39,126] + "h_0": [1,6,12,0,0,6,8,"66816c7c",157,"arcaneShip 1/19, emptyShip 1/0, kazimierzShip 0/8, miraShip 1/0, preciShip 2/40, raidShip 1/24, siracusaShip 1/30, suntShip 1/0, swiftShip 1/44, yanShip 0/13",18,57,39,129], + "ai_0": [1,12,13,0,2,6,8,"e3ba3267",252,"arcaneShip 0/2, deputShip 0/32, kazimierzShip 0/8, lateranoShip 1/57, miraShip 1/6, preciShip 0/2, raidShip 1/2, sargonShip 0/2, siracusaShip 1/54, skillfulShip 1/70, suntShip 1/14, swiftShip 1/49",17,57,40,126] } ], "players": { @@ -1574,28 +1572,28 @@ "victory": true, "roundsPassed": 14, "eliminatedRound": null, - "lp": 1, + "lp": 6, "bandId": "band_amedic", "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":276,"bossDamage":390000,"dmgDealt":2801259,"gold":135,"itemsEquipped":23,"kills":253,"leaks":54,"lpLost":27,"merges":5,"perfectRounds":9,"refreshes":21}, - "lineup": ["6_05_a@12,3[3_08_e_a+3_05_e_a]","4_14_b@11,3[3_11_e_b+6_05_e_a]","1_17_b@10,3","6_19_a@10,4","6_17_a@10,7[4_08_e_a+1_05_e_b]","6_06_a@10,8[1_01_e_a+4_04_e_a]","3_12_a@9,4[4_04_e_b+1_02_e_b]","3_13_b@9,8[2_01_e_a+4_03_e_a]"], - "bonds": ["arcaneShip 0/11","kazimierzShip 1/62","kjeragShip 0/8","preciShip 2/48","raidShip 1/105","sargonShip 1/2","skillfulShip 0/9","soloShip 0/6","suntShip 1/0","swiftShip 1/59","yanShip 0/1"] + "stats": {"activatedLayers":133,"bossDamage":471435,"dmgDealt":2876512,"gold":139,"itemsEquipped":18,"kills":270,"leaks":51,"lpLost":22,"merges":5,"perfectRounds":10,"refreshes":23}, + "lineup": ["6_16_a@12,3","5_10_a@12,7[1_02_e_b+6_01_e_a]","5_20_a@11,3[1_01_e_a+3_11_e_a]","4_14_b@11,8[3_05_e_a+1_02_e_b]","6_12_a@10,3[4_03_e_a+2_01_e_a]","5_07_a@10,7","4_16_a@10,8[4_03_e_b+3_07_e_a]","1_17_b@9,8"], + "bonds": ["arcaneShip 2/25","emptyShip 1/0","kazimierzShip 0/8","miraShip 1/0","preciShip 2/50","raidShip 1/24","siracusaShip 1/34","suntShip 1/0","swiftShip 0/44","yanShip 0/13"] }, "ai_0": { - "alive": false, - "victory": false, - "roundsPassed": 12, - "eliminatedRound": 13, - "lp": 0, + "alive": true, + "victory": true, + "roundsPassed": 14, + "eliminatedRound": null, + "lp": 12, "bandId": "band_chiave", - "title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"}, - "trophies": 3, - "reward": 280, - "stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2057565,"gold":126,"itemsEquipped":15,"kills":232,"leaks":62,"lpLost":25,"merges":3,"perfectRounds":9,"refreshes":23}, - "lineup": [], - "bonds": ["arcaneShip 0/2","deputShip 0/18","kazimierzShip 0/8","lateranoShip 0/108","preciShip 0/46","raidShip 0/15","sargonShip 0/2","skillfulShip 0/31","suntShip 0/16","swiftShip 0/74","visiShip 0/15"] + "title": {"id":"comment_2","name":"不朽盟约","picId":"comment_icon_2","text":"激活的盟约层数总和最高"}, + "trophies": 4, + "reward": 400, + "stats": {"activatedLayers":299,"bossDamage":308565,"dmgDealt":2856973,"gold":139,"itemsEquipped":20,"kills":253,"leaks":41,"lpLost":11,"merges":8,"perfectRounds":11,"refreshes":24}, + "lineup": ["3_21_b@12,3[3_01_e_a+1_05_e_b]","5_20_a@12,7","3_01_b@11,3[1_01_e_a+6_05_e_a]","1_13_b@11,8","4_10_b@10,3[2_01_e_a+3_08_e_a]","4_16_a@10,7[4_11_e_a+4_02_e_a]","6_17_a@10,8[4_04_e_a]","6_01_a@9,8","4_21_a@9,9"], + "bonds": ["arcaneShip 0/2","deputShip 1/34","kazimierzShip 0/8","lateranoShip 1/71","miraShip 1/14","preciShip 0/2","raidShip 1/4","sargonShip 0/2","siracusaShip 1/80","skillfulShip 1/80","suntShip 1/16","swiftShip 0/49"] } }, "standIns": { @@ -1604,16 +1602,16 @@ [1,""], [2,""], [3,""], - [4,"3_12_a→char_608_acpion"], - [5,"3_12_a→char_608_acpion"], - [6,"3_12_a→char_608_acpion"], - [7,"3_12_a→char_608_acpion"], - [8,"3_12_a→char_608_acpion"], - [9,"3_12_a→char_608_acpion"], - [10,"3_12_a→char_608_acpion"], - [11,"3_12_a→char_608_acpion 6_05_a→char_611_acnipe"], - [12,"3_12_a→char_608_acpion 6_05_a→char_611_acnipe"], - [13,"3_12_a→char_608_acpion 6_05_a→char_611_acnipe 6_17_a→char_608_acpion"] + [4,"3_18_a→char_609_acguad"], + [5,"3_18_a→char_609_acguad"], + [6,"3_18_a→char_609_acguad"], + [7,"3_18_a→char_609_acguad"], + [8,"3_18_a→char_609_acguad"], + [9,"3_18_a→char_609_acguad"], + [10,""], + [11,""], + [12,"5_20_a→char_614_acsupo"], + [13,"5_20_a→char_614_acsupo"] ] } }, @@ -1627,8 +1625,8 @@ "combat": "client", "boost": null, "setup": {"stageId":"act1autochess_m03","bossId":"boss_7","hiddenBossId":"boss_10","factions":["ELEMENT","INVISIBLE","REFLECTION"]}, - "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":55,"bossPoolLeft":0,"virtualMs":122900}, - "rng": {"setup":25,"shop":515,"waves":0,"draft":34,"bots":16491,"meta":18}, + "end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":55,"bossPoolLeft":0,"virtualMs":119900}, + "rng": {"setup":25,"shop":645,"waves":0,"draft":34,"bots":15763,"meta":14}, "errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0}, "rounds": [ {"round":1,"h_0":[1,27,4,0,0,5,1,"3e4697a0",0,"",5,5,0,3],"ai_0":[1,28,4,0,0,1,2,"a1734bc6",0,"sargonShip 0/2, soloShip 1/0",5,5,0,4]}, @@ -1637,52 +1635,52 @@ { "round": 4, "h_0": [1,27,7,0,0,5,6,"2d3d3501",10,"preciShip 1/2, sargonShip 1/8",20,20,0,20], - "ai_0": [1,28,7,0,0,2,8,"bf509782",0,"sargonShip 0/2, soloShip 1/0, yanShip 0/2",20,20,0,22] + "ai_0": [1,28,7,0,0,2,8,"622c2b29",0,"sargonShip 0/2, siracusaShip 1/0, soloShip 1/0, yanShip 0/2",20,20,0,22] }, { "round": 5, - "h_0": [1,27,8,0,0,5,8,"1a62176b",31,"indomShip 1/0, preciShip 0/2, sargonShip 1/29, steadShip 1/1, victoriaShip 1/1",27,27,0,28], - "ai_0": [1,28,8,0,1,2,8,"e0bfc937",0,"sargonShip 0/2, skillfulShip 1/0, soloShip 1/0, suntShip 1/0, yanShip 0/6",27,27,0,29] + "h_0": [1,27,8,0,0,5,8,"271d993b",29,"preciShip 1/2, raidShip 1/8, sargonShip 1/14, visiShip 1/5",27,27,0,26], + "ai_0": [1,28,8,0,0,3,8,"1bc9fd77",0,"sargonShip 0/2, siracusaShip 1/0, soloShip 1/0, yanShip 0/2",27,27,0,30] }, { "round": 6, - "h_0": [1,27,9,0,1,5,8,"7e9969de",79,"arcaneShip 0/2, indomShip 1/6, preciShip 1/14, sargonShip 1/39, steadShip 1/6, victoriaShip 1/8, visiShip 1/6",22,22,0,38], - "ai_0": [1,28,10,0,1,3,8,"7f20e163",0,"sargonShip 0/4, skillfulShip 1/0, soloShip 1/0, suntShip 1/0, yanShip 0/6",23,24,1,39] + "h_0": [1,27,9,0,3,5,8,"99290e8e",59,"preciShip 0/2, raidShip 1/15, sargonShip 1/26, steadShip 1/2, victoriaShip 1/6, visiShip 1/10",24,24,0,33], + "ai_0": [1,28,9,0,1,3,8,"546cd677",6,"kjeragShip 0/2, sargonShip 0/2, siracusaShip 1/4, soloShip 1/0, steadShip 1/2, suntShip 1/0, yanShip 0/8",22,22,0,38] }, { "round": 7, - "h_0": [1,27,11,0,1,5,8,"5702ef65",125,"arcaneShip 0/2, indomShip 1/6, preciShip 1/39, sargonShip 1/60, steadShip 1/11, victoriaShip 1/9, visiShip 0/6",26,26,0,50], - "ai_0": [1,28,11,0,1,4,8,"7f20e163",0,"sargonShip 0/4, skillfulShip 1/0, soloShip 1/0, suntShip 1/0, yanShip 0/6",28,28,0,49] + "h_0": [1,27,13,0,1,5,8,"4c92ca45",92,"preciShip 0/2, raidShip 1/24, sargonShip 1/35, steadShip 1/6, victoriaShip 1/12, visiShip 1/15",28,28,0,47], + "ai_0": [1,28,11,0,1,4,8,"483ff3a4",4,"kjeragShip 0/2, sargonShip 0/2, siracusaShip 1/4, skillfulShip 1/0, soloShip 1/0, steadShip 0/2, suntShip 1/0, yanShip 0/8",26,26,0,50] }, { "round": 8, - "h_0": [1,27,12,0,0,5,8,"b6d1cc82",215,"arcaneShip 0/2, indomShip 1/18, preciShip 2/72, sargonShip 1/93, soloShip 1/0, steadShip 2/32, victoriaShip 0/21, visiShip 0/6",18,18,0,63], - "ai_0": [1,28,12,0,1,4,8,"854ef8a8",16,"miraShip 1/0, sargonShip 0/4, skillfulShip 1/3, soloShip 1/0, steadShip 1/0, suntShip 1/0, yanShip 1/13",17,18,1,61] + "h_0": [1,27,14,0,2,5,8,"a47973b7",182,"preciShip 0/2, raidShip 1/41, sargonShip 1/61, steadShip 1/18, victoriaShip 1/30, visiShip 1/32",18,18,0,61], + "ai_0": [1,28,12,0,0,4,8,"770028df",13,"kjeragShip 0/2, raidShip 1/5, sargonShip 0/2, siracusaShip 1/8, skillfulShip 1/0, soloShip 1/0, steadShip 0/2, suntShip 1/0, yanShip 0/8",18,18,0,62] }, { "round": 9, - "h_0": [1,27,12,0,4,5,8,"6405e223",287,"arcaneShip 0/2, indomShip 1/18, preciShip 2/98, sargonShip 1/124, steadShip 1/37, victoriaShip 0/21, visiShip 1/10",23,23,0,77], - "ai_0": [1,28,13,0,2,4,8,"561a6f1b",30,"sargonShip 0/4, siracusaShip 1/8, skillfulShip 1/3, soloShip 1/0, suntShip 1/0, yanShip 1/19",16,23,7,74] + "h_0": [1,27,14,0,2,5,8,"b9bfdb46",237,"preciShip 0/2, raidShip 1/49, sargonShip 1/90, steadShip 1/21, victoriaShip 1/38, visiShip 1/39",23,23,0,74], + "ai_0": [1,28,12,0,1,4,8,"1df1a8d4",51,"kjeragShip 0/2, raidShip 1/10, sargonShip 0/2, siracusaShip 1/24, soloShip 1/0, steadShip 0/2, suntShip 1/0, visiShip 1/5, yanShip 1/12",20,23,3,75] }, { "round": 10, - "h_0": [1,27,16,0,2,5,8,"c899fa07",334,"arcaneShip 0/2, indomShip 1/18, preciShip 2/115, sargonShip 1/133, soloShip 1/0, steadShip 2/46, suntShip 1/0, victoriaShip 0/21, visiShip 1/22",31,31,0,91], - "ai_0": [1,28,14,0,0,5,8,"0d283e5f",35,"sargonShip 0/4, siracusaShip 1/8, skillfulShip 1/6, soloShip 1/0, suntShip 2/0, yanShip 1/21",31,31,0,87] + "h_0": [1,27,14,0,2,5,8,"c41d66b3",218,"preciShip 0/2, raidShip 1/56, sargonShip 1/91, soloShip 1/0, steadShip 2/27, victoriaShip 0/38, visiShip 1/44",31,31,0,88], + "ai_0": [1,28,13,0,2,5,8,"1127b509",57,"kjeragShip 0/2, miraShip 1/4, raidShip 1/15, sargonShip 0/2, siracusaShip 1/28, soloShip 1/0, steadShip 0/2, suntShip 1/0, visiShip 1/10, yanShip 0/12",31,31,0,87] }, { "round": 11, - "h_0": [1,27,16,0,0,6,8,"a5cfb056",385,"arcaneShip 0/2, indomShip 1/18, preciShip 2/134, sargonShip 1/139, steadShip 1/51, suntShip 1/0, swiftShip 1/5, victoriaShip 0/21, visiShip 1/38",19,19,0,106], - "ai_0": [1,28,12,0,0,5,8,"fe5ac1a4",53,"sargonShip 0/4, siracusaShip 1/12, skillfulShip 1/12, soloShip 1/0, suntShip 2/0, visiShip 1/0, yanShip 1/29",19,19,0,99] + "h_0": [1,27,14,2,0,6,8,"3ea6a635",296,"preciShip 2/8, raidShip 1/65, sargonShip 1/97, steadShip 1/31, victoriaShip 1/42, visiShip 1/53",19,19,0,102], + "ai_0": [1,28,14,0,0,5,8,"d2ce576d",120,"kjeragShip 0/2, miraShip 1/8, raidShip 1/20, sargonShip 0/2, siracusaShip 1/56, soloShip 1/0, steadShip 0/2, suntShip 1/0, visiShip 1/15, yanShip 1/21",19,19,0,103] }, { "round": 12, - "h_0": [1,27,14,2,2,6,8,"477e3f61",482,"arcaneShip 0/2, indomShip 1/30, preciShip 2/165, sargonShip 1/157, steadShip 1/54, suntShip 1/0, swiftShip 1/10, victoriaShip 0/21, visiShip 1/66",33,33,0,124], - "ai_0": [1,28,12,0,0,6,8,"d3c53bfd",63,"sargonShip 0/8, siracusaShip 1/12, skillfulShip 1/18, soloShip 1/0, suntShip 2/0, yanShip 1/33",15,33,18,112] + "h_0": [1,27,14,0,0,6,8,"7bcdc94b",358,"preciShip 2/30, raidShip 1/74, sargonShip 1/119, steadShip 1/35, victoriaShip 1/42, visiShip 1/58",33,33,0,115], + "ai_0": [1,28,12,0,2,6,8,"c4a4c577",117,"kjeragShip 0/2, miraShip 1/12, raidShip 1/25, sargonShip 0/2, siracusaShip 1/60, soloShip 1/0, steadShip 0/2, suntShip 1/0, visiShip 1/20, yanShip 0/21",19,33,14,114] }, { "round": 13, - "h_0": [1,27,18,0,2,6,8,"648327be",474,"arcaneShip 0/2, indomShip 0/30, preciShip 2/190, sargonShip 1/187, steadShip 0/54, suntShip 1/0, swiftShip 1/15, victoriaShip 0/21, visiShip 1/82",50,50,0,143], - "ai_0": [1,28,12,0,0,6,8,"f0c52b92",100,"miraShip 1/0, preciShip 1/24, sargonShip 1/32, siracusaShip 1/20, skillfulShip 1/24, soloShip 1/0, suntShip 1/0, yanShip 0/33",30,50,20,123] + "h_0": [1,27,12,0,2,6,8,"7b65a785",407,"preciShip 2/51, raidShip 1/81, sargonShip 1/158, steadShip 0/35, suntShip 1/0, victoriaShip 1/48, visiShip 1/69",50,50,0,127], + "ai_0": [1,28,14,0,0,6,8,"93a0a5a2",173,"kjeragShip 0/2, miraShip 1/16, preciShip 1/5, raidShip 1/30, sargonShip 0/2, siracusaShip 1/88, soloShip 1/0, steadShip 0/2, suntShip 1/0, swiftShip 1/9, visiShip 1/25, yanShip 0/21",31,50,19,126] } ], "players": { @@ -1696,9 +1694,9 @@ "title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":513,"bossDamage":1147951,"dmgDealt":5456360,"gold":160,"itemsEquipped":22,"kills":352,"leaks":0,"lpLost":0,"merges":5,"perfectRounds":13,"refreshes":31}, - "lineup": ["6_05_a@12,8[4_08_e_a+2_01_e_b]","5_02_a@11,6[6_01_e_a]","6_05_a@11,7[3_04_e_a+3_03_e_a]","4_20_b@11,8","4_14_b@10,6[3_11_e_a+5_01_e_b]","5_21_b@10,8[1_01_e_a+6_03_e_a]","4_07_b@9,8[4_04_e_b+4_11_e_b]","4_23_b@9,9[4_06_e_a]"], - "bonds": ["arcaneShip 0/2","indomShip 0/30","preciShip 2/203","sargonShip 1/187","steadShip 0/54","suntShip 2/0","swiftShip 1/25","victoriaShip 0/21","visiShip 1/98"] + "stats": {"activatedLayers":417,"bossDamage":1142107,"dmgDealt":5118920,"gold":143,"itemsEquipped":30,"kills":345,"leaks":0,"lpLost":0,"merges":4,"perfectRounds":13,"refreshes":42}, + "lineup": ["5_02_a@12,8[3_11_e_a+5_01_e_b]","6_05_a@11,8[2_01_e_b+3_08_e_b]","6_08_a@10,6[3_06_e_a+2_03_e_a]","4_04_a@10,7[4_11_e_a+3_07_e_a]","4_23_b@10,8[4_03_e_a+4_04_e_b]","4_07_a@9,7[1_01_e_a+1_02_e_a]","5_diy1_b@9,8[6_01_e_a+4_05_e_a]","3_05_b@9,9[4_02_e_b+4_04_e_a]"], + "bonds": ["preciShip 2/51","raidShip 1/86","sargonShip 1/158","steadShip 0/35","suntShip 1/0","victoriaShip 1/48","visiShip 1/74"] }, "ai_0": { "alive": true, @@ -1710,9 +1708,9 @@ "title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"}, "trophies": 4, "reward": 400, - "stats": {"activatedLayers":108,"bossDamage":427049,"dmgDealt":2882939,"gold":134,"itemsEquipped":19,"kills":246,"leaks":47,"lpLost":0,"merges":6,"perfectRounds":8,"refreshes":12}, - "lineup": ["5_20_a@12,8","5_10_a@11,7","6_05_a@11,8[5_03_e_a+1_01_e_a]","3_13_a@10,5","5_02_a@10,6[3_07_e_a+4_03_e_a]","3_03_b@10,8[3_11_e_b+2_04_e_a]","2_04_b@9,8","1_08_b@9,9[1_02_e_a+4_06_e_a]"], - "bonds": ["preciShip 1/24","sargonShip 1/32","siracusaShip 1/28","skillfulShip 1/24","soloShip 1/0","suntShip 1/0","yanShip 0/33"] + "stats": {"activatedLayers":221,"bossDamage":432893,"dmgDealt":3069369,"gold":137,"itemsEquipped":18,"kills":253,"leaks":36,"lpLost":0,"merges":3,"perfectRounds":10,"refreshes":20}, + "lineup": ["5_12_a@12,8[1_05_e_b+3_03_e_b]","4_14_a@11,7","6_12_a@11,8","5_20_b@10,5[1_01_e_a+3_11_e_a]","6_05_a@10,6[5_01_e_a]","4_04_a@10,8[4_11_e_a]","4_16_a@9,8[3_07_e_a+4_08_e_a]","1_08_b@9,9[1_02_e_a+4_06_e_b]"], + "bonds": ["kjeragShip 0/2","miraShip 1/16","preciShip 2/10","raidShip 1/35","sargonShip 0/2","siracusaShip 1/116","soloShip 1/0","steadShip 0/2","suntShip 1/0","swiftShip 1/14","visiShip 1/30","yanShip 0/21"] } }, "diy": { @@ -1731,14 +1729,14 @@ [5,"","5_diy1_a→char_112_siege"], [6,"","5_diy1_a→char_112_siege"], [7,"5_diy2_a","5_diy1_a→char_112_siege"], - [8,"",""], - [9,"",""], - [10,"",""], - [11,"",""], - [12,"",""], - [13,"",""] + [8,"5_diy2_a 5_diy2_a","5_diy1_a→char_112_siege"], + [9,"","5_diy1_a→char_112_siege"], + [10,"","5_diy1_a→char_112_siege"], + [11,"","5_diy1_a→char_112_siege"], + [12,"5_diy1_a 5_diy2_a 6_diy2_a","5_diy1_a→char_112_siege"], + [13,"5_diy2_a","5_diy1_a→char_112_siege"] ], - "stock": {"chess_char_5_diy1_a":8,"chess_char_5_diy2_a":8,"chess_char_6_diy1_a":5,"chess_char_6_diy2_a":5} + "stock": {"chess_char_5_diy1_a":5,"chess_char_5_diy2_a":8,"chess_char_6_diy1_a":5,"chess_char_6_diy2_a":5} } } } diff --git a/test/match/feedback5-shop-levelslots.test.js b/test/match/feedback5-shop-levelslots.test.js new file mode 100644 index 00000000..3c17dfba --- /dev/null +++ b/test/match/feedback5-shop-levelslots.test.js @@ -0,0 +1,94 @@ +// Community report of 2026-10-06, item 19: 「升级商店获得新的商店位时用新卡补上,而不是空着」. The official tutorial's 休整期 +// page says 「升级:消耗资金升级调度中心。升级后将出现更多的商品栏位、可调度干员以及新装备」: the upgrade itself opens the +// new level's extra slot. Until 0.2.0 the shop kept its old slot count until the next roll (a refresh or the round start), +// so a level-up from 1 to 2 (险境: 3 → 4 operator slots) or 3 to 4 (4 → 5) showed no new card. Now the upgrade adds the +// extra slots at once, each with a new card drawn at the new level, and leaves the cards already shown where they are. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { makeMatch, DATA } from './harness.js'; +import { GameData } from '../../server/match/gamedata.js'; + +const ids = (slots) => slots.map((s) => (s ? `${s.kind}:${s.id}${s.sold ? '(sold)' : ''}` : null)); +const tierOf = (id) => DATA.chess[id]?.tier ?? 0; + +test('a level-up opens the new level\'s operator slot at once with a new card; the cards shown stay in place', () => { + const h = makeMatch({ mode: 'solo', difficulty: 'NORMAL', seed: 7, fake: true }).start(); + h.toPrep(1); + const ps = h.ps('p_0'); + ps.funds = 200; + assert.deepEqual(h.m.gd.shopSlots(1), { chess: 3, item: 1 }); + const s1 = ps.shop.slots.slice(); + assert.equal(s1.length, 4); + assert.deepEqual(h.m.handle('p_0', { t: 'g.levelUp' }), { ok: true }); + // level 2: 4 operator slots + the item slot — the three cards and the item kept (the same objects), one new card + assert.equal(ps.shop.level, 2); + assert.deepEqual(ps.shop.layout, { chess: 4, item: 1 }); + assert.equal(ps.shop.slots.length, 5); + for (let i = 0; i < 3; i++) assert.equal(ps.shop.slots[i], s1[i], `operator card ${i} kept`); + assert.equal(ps.shop.slots[4], s1[3], 'the item card stays after the operator cards'); + const added = ps.shop.slots[3]; + assert.ok(added && added.kind === 'chess' && !added.sold, `a new operator card: ${JSON.stringify(added)}`); + assert.ok(tierOf(added.id) >= 1 && tierOf(added.id) <= 2, `drawn at the new level (tier ${tierOf(added.id)})`); + assert.equal(added.frozen, false); + // the player sees it right away (m.private shop) + assert.deepEqual(ids(ps.privateView().shop.slots), ids(ps.shop.slots)); + // 2 → 3 keeps 4 operator slots: nothing rerolled + const s2 = ps.shop.slots.slice(); + h.m.handle('p_0', { t: 'g.levelUp' }); + assert.equal(ps.shop.level, 3); + assert.deepEqual(ps.shop.slots, s2, 'no extra slot at level 3, no reroll'); + // 3 → 4: the fifth operator slot + h.m.handle('p_0', { t: 'g.levelUp' }); + assert.equal(ps.shop.level, 4); + assert.deepEqual(ps.shop.layout, { chess: 5, item: 1 }); + for (let i = 0; i < 4; i++) assert.equal(ps.shop.slots[i], s2[i]); + assert.equal(ps.shop.slots[5], s2[4]); + assert.ok(ps.shop.slots[4]?.kind === 'chess' && tierOf(ps.shop.slots[4].id) <= 4); + h.invariants(); + h.m.dispose(); +}); + +test('a bought card stays sold; a frozen shop freezes the new card too [ASSUMED], and the round start keeps it', () => { + const h = makeMatch({ mode: 'solo', difficulty: 'NORMAL', seed: 11, fake: true }).start(); + h.toPrep(1); + const ps = h.ps('p_0'); + ps.funds = 200; + assert.deepEqual(h.m.handle('p_0', { t: 'g.buy', slot: 0 }), { ok: true }); + assert.equal(ps.shop.slots[0].sold, true); + assert.deepEqual(h.m.handle('p_0', { t: 'g.freeze' }), { ok: true }); + h.m.handle('p_0', { t: 'g.levelUp' }); + assert.equal(ps.shop.slots[0].sold, true, 'the bought slot is not refilled'); + const added = ps.shop.slots[3]; + assert.ok(added && !added.sold && added.frozen === true, 'the new card follows the freeze toggle'); + const keep = ps.shop.slots.filter((s) => s && !s.sold && s.frozen).map((s) => s.id); + assert.equal(keep.length, 4, 'two operator cards, the new one and the item'); + // the next round start keeps every frozen card (in place) and rolls only the sold slot + h.drive(() => h.m.phase === 'PREP' && h.m.round === 2); + assert.deepEqual(ps.shop.slots.slice(1).map((s) => s.id), keep); + assert.equal(ps.shop.slots.length, 5); + h.invariants(); + h.m.dispose(); +}); + +test('every mode\'s slot table: a level-up adds exactly the slots the new level has more of (标准: 3,4,4,4,4,5)', () => { + for (const [mode, difficulty] of [['solo', 'FUNNY'], ['solo', 'NORMAL'], ['coop', 'FUNNY'], ['coop', 'ABYSS']]) { + const h = makeMatch({ mode, difficulty, seed: 3, fake: true, humans: 1, bots: mode === 'coop' ? 1 : 0 }).start(); + h.toPrep(1); + const ps = h.ps('p_0'); + const gd = h.m.gd; + ps.funds = 500; + for (let lv = 2; lv <= gd.maxShopLevel; lv++) { + const before = ps.shop.slots.slice(); + assert.deepEqual(h.m.handle('p_0', { t: 'g.levelUp' }), { ok: true }); + const { chess, item } = gd.shopSlots(lv); + assert.equal(ps.shop.slots.length, chess + item, `${mode} ${difficulty} L${lv}`); + const grew = chess - gd.shopSlots(lv - 1).chess; + const kept = ps.shop.slots.filter((s) => before.includes(s)).length; + assert.equal(kept, before.length, `${mode} ${difficulty} L${lv}: every shown card kept`); + assert.equal(ps.shop.slots.length - before.length, grew + (item - gd.shopSlots(lv - 1).item)); + } + h.invariants(); + h.m.dispose(); + } + assert.deepEqual([1, 2, 3, 4, 5, 6].map((l) => new GameData(DATA, 'mode_single_funny').shopSlots(l).chess), [3, 4, 4, 4, 4, 5]); +}); From e8d2b45ad4cfbb243a4632fd8184836534a7aa24 Mon Sep 17 00:00:00 2001 From: sganggs Date: Wed, 7 Oct 2026 02:06:10 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feedback5=20CB5:=20an=20eliminated=20player?= =?UTF-8?q?=20(and=20a=20spectator=20seat)=20follows=20a=20player=20throug?= =?UTF-8?q?h=20every=20phase=20reset=20=E2=80=94=20the=20one=20it=20last?= =?UTF-8?q?=20watched,=20else=20the=20first=20player=20still=20in=20?= =?UTF-8?q?=E2=80=94=20instead=20of=20being=20sent=20back=20to=20its=20own?= =?UTF-8?q?=20empty=20board=20each=20round=20(community=20report=20of=2020?= =?UTF-8?q?26-10-06,=20item=2056;=20the=20idea=20of=20PR=20#189)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Official: research 09 §3.1 — after elimination the client keeps observing (self_dead_dialog KEEP_WATCH, DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget). Until 0.2.0 every phase reset cleared an eliminated player's watch: in 回合开始 / 机变 / 休整期 it saw its own empty board, in battle the first field. Server (server/match/match/watch.js): Match.watchPref (viewer → the player of its last manual g.watch: a prep scout, a single-player field, or the new optional g.watch playerId on a shared 联防 / boss pair field — PR #189's review point); _watchTargetOf = that player while still in, else the first player still in (seat order, AI seats included); startRound scouts its board (_followScout, kept through 机变 and 休整期), both combat modes and the boss fields start on its field, a resync / a spectator joining in prep gets the board, a followed player who quits hands over at once. Client: the screen adopts the pushed board like a 前往查看 tap (battle/observe.js followedScout; the stale-field fix of PR #189), keeps it through the prep phases (no camera flight home and back), a spectator no longer asks for the first player itself; g.watch sends the tapped row. Overlap with PR #189 (not merged): the same preference chain; differences — the fallback includes AI seats (a lone human with AI teammates had nobody to follow), shared fields record the tapped playerId. Golden unchanged. Tests: test/match/feedback5-watch-follow.test.js (new, 9), spectator.test.js updated (the spectator follows the player it scouted). Docs: design/network.md, PLAYING.md. --- docs/PLAYING.md | 4 +- docs/design/network.md | 6 +- public/js/battle/observe.js | 20 ++- public/js/screens/game.js | 56 +++++--- public/js/ui/gameActions.js | 3 +- server/match/Match.js | 10 +- server/match/match/combat.js | 8 +- server/match/match/intents.js | 3 +- server/match/match/phases.js | 3 + server/match/match/platform.js | 8 +- server/match/match/watch.js | 86 ++++++++++-- shared/protocol.js | 4 +- test/match/feedback5-watch-follow.test.js | 156 ++++++++++++++++++++++ test/match/spectator.test.js | 10 +- 14 files changed, 330 insertions(+), 47 deletions(-) create mode 100644 test/match/feedback5-watch-follow.test.js diff --git a/docs/PLAYING.md b/docs/PLAYING.md index ed13b4d6..43f280c5 100644 --- a/docs/PLAYING.md +++ b/docs/PLAYING.md @@ -127,8 +127,8 @@ - 左下角「交流」打开表情面板:6 套主题 × 6 个表情,左右滑动(或方向键)换主题。表情以气泡形式显示在你头像旁 3 秒,发送冷却 1 秒。 - 休整期、或自己的作战结束后(显示「作战结束,等待队友完成作战」),点左侧队友头像 →「前往查看」观看他的战场;自己的作战进行中不能观战。联防和最终攻势中可以用 ‹ › 切换左半 / 全景 / 右半。 -- 被淘汰后可以自由观看任何战场。 -- **观战席**(本作新增,原版没有):每个同盟房间另有 2 个观战席。在大厅输入同盟密钥后点「观战」进入(模拟进行中也可以),不占博士席位、不算参战人数,也不能购买、部署、准备或发送表情;观看方式与被淘汰的玩家相同——作战时自动观看第一个战场,休整期自动显示第一位博士的阵地,点左侧成员头像可切换查看任何人。断线重连会回到观战席;创建者可以在等待室用观战者名字旁的 ✕ 将其移出;观战者在等待室有空位时可以点「入座」成为博士。 +- 被淘汰后可以自由观看任何战场,而且**不用每回合重选**:你上次点「前往查看」的是谁,之后每个阶段(回合开始、机变、休整期、作战、最终攻势)都会自动跟到他的阵地或战场;没选过或他也被淘汰时,跟随座位顺序第一位还在场的博士(包括 AI 队友)。阶段中途换人立即生效,之后就跟随新选的人。 +- **观战席**(本作新增,原版没有):每个同盟房间另有 2 个观战席。在大厅输入同盟密钥后点「观战」进入(模拟进行中也可以),不占博士席位、不算参战人数,也不能购买、部署、准备或发送表情;观看方式与被淘汰的玩家相同——自动跟随你上次查看的博士(没查看过则是座位顺序第一位还在场的博士):休整期显示他的阵地,作战时观看他的战场,点左侧成员头像可切换查看任何人。断线重连会回到观战席;创建者可以在等待室用观战者名字旁的 ✕ 将其移出;观战者在等待室有空位时可以点「入座」成为博士。 - **观看谁,盟约栏就显示谁的盟约**:查看队友的阵地或战场时(休整期、作战结束后、被淘汰后自动观战),左上的盟约栏换成**该队友的盟约、人数和层数**,前面有琥珀色的「👁 队友名」标签,点开的盟约详情也写着「队友名 的盟约」(成员一栏只标出他在场上的干员,整备区看不到);作战中层数实时增长(与他自己看到的一致)。联防和最终攻势里跟随 ‹ ›:镜头在队友的半场时显示他的盟约,在你自己的半场时显示你自己的;「全景」时,你在这个战场上作战就显示你自己的,你不在这个战场上(漏怪后观看联防、被淘汰后观战)则显示你点「前往查看」选中的那名队友,没选过就显示该战场的第一名玩家(1 号联防者 / 这一组的第一位),**不会显示你自己的**。在这样的战场上点某名队友的头像 →「前往查看」,镜头会直接移到他的半场,‹ › 上的「👁 队友名」与盟约栏的标签一致;‹ › 旁边有「返回战场」(被淘汰时没有)。盟约栏换人时,从盟约栏打开的盟约详情会自动关闭。点开场上某个干员的详情卡时,卡上的盟约(以及点它打开的盟约详情)属于**这个干员的主人**,与盟约栏当前显示谁无关——例如在「全景」点队友的干员,看到的是「队友名 的盟约」及他的人数和层数;在队友的半场点你自己的干员,看到的是你自己的。回到自己的阵地 / 战场后恢复成你自己的盟约。作战结束后观看队友时刷新页面 / 断线重连,仍会回到正在观看的队友战场,「👁 队友名」和「返回战场」都还在。已被淘汰的队友不再显示盟约(也无法被查看)。 ## 9. 结算称号 diff --git a/docs/design/network.md b/docs/design/network.md index a24aea7a..23af0a6c 100644 --- a/docs/design/network.md +++ b/docs/design/network.md @@ -15,7 +15,7 @@ Platform additions (implemented): `welcome.{resumed, version}`, `welcome.diyKitt **Spectator seats** (a remake feature — the official room has none; community report #26, owner's decision 2026-10-04; the spectator-seat subsection of §23): `room.spectate {code}` takes one of a co-op room's `MAX_SPECTATORS` (2) spectator seats, in its lobby or during its match; `room.removeSpectator {playerId}` (host) frees one (`room.closed {reason:'kicked'}` to it); `room.state.spectators: [{playerId, name, connected}]`. A spectator is never a player (not in `seats`, not counted for the start, never host, keeps no room alive — `room.closed {empty}` when the last human leaves), receives the room's broadcasts and watches like an eliminated player (`Match.addSpectator`: `b.start` watch / `m.field`, never `m.private`); it may only `g.watch`, leave, and store a `room.loadout` — anything else answers `error SPECTATOR`. Its seat is kept and given back on reconnect like a player's. ### 8.2 Match -C→S (all carry `rid`): `g.infoReady` · `g.band {bandId}` · `g.bandSkip` · `g.bandFocus {bandId?}` (the strategy highlighted in the draft screen — what a timed-out turn takes; absent / null clears it; `WRONG_PHASE` outside BAND_DRAFT, `ALREADY` after the pick, `BAD_TARGET` for a band not allowed; §18.2) · `g.buy {slot}` · `g.refresh` · `g.freeze` · `g.levelUp` · `g.sell {uid}` · `g.move {uid, to:{area:'board', row, col}|{area:'hand', idx}, dir?}` · `g.equip {itemUid, targetUid, replaceUid?}` (`replaceUid`: which equipped item a third one replaces, §6.2) · `g.art {itemUid, row, col, dir?}` (`dir` ∈ UP|RIGHT|DOWN|LEFT from the deploy wheel, §3; absent ⇒ `to.dir`, then RIGHT; `g.move` onto the piece's own tile re-orients it; board pieces in `m.private` / `m.field {prep:true}` carry `dir`) · `g.destroy {uid}` (hand / temp items only; equipped items are locked, §6.2) · `g.reward {idx}` · `g.choice {idx}` · `g.ready {ready}` · `g.emote {id}` · `g.watch {fieldId}` · `g.autoplay {on}` · `g.pause {on}` (solo battles only, §14 Solo pause) · `g.unitStats {seq?}` (ROUND_START / SP_DRAFT / PREP, else `WRONG_PHASE`; eliminated: `ELIMINATED`) → push `m.unitStats` (§18.5) · `g.leave` +C→S (all carry `rid`): `g.infoReady` · `g.band {bandId}` · `g.bandSkip` · `g.bandFocus {bandId?}` (the strategy highlighted in the draft screen — what a timed-out turn takes; absent / null clears it; `WRONG_PHASE` outside BAND_DRAFT, `ALREADY` after the pick, `BAD_TARGET` for a band not allowed; §18.2) · `g.buy {slot}` · `g.refresh` · `g.freeze` · `g.levelUp` · `g.sell {uid}` · `g.move {uid, to:{area:'board', row, col}|{area:'hand', idx}, dir?}` · `g.equip {itemUid, targetUid, replaceUid?}` (`replaceUid`: which equipped item a third one replaces, §6.2) · `g.art {itemUid, row, col, dir?}` (`dir` ∈ UP|RIGHT|DOWN|LEFT from the deploy wheel, §3; absent ⇒ `to.dir`, then RIGHT; `g.move` onto the piece's own tile re-orients it; board pieces in `m.private` / `m.field {prep:true}` carry `dir`) · `g.destroy {uid}` (hand / temp items only; equipped items are locked, §6.2) · `g.reward {idx}` · `g.choice {idx}` · `g.ready {ready}` · `g.emote {id}` · `g.watch {fieldId, playerId?}` (`playerId`: the player tapped — a shared 联防 / boss pair field shows two; what an eliminated viewer follows, §25) · `g.autoplay {on}` · `g.pause {on}` (solo battles only, §14 Solo pause) · `g.unitStats {seq?}` (ROUND_START / SP_DRAFT / PREP, else `WRONG_PHASE`; eliminated: `ELIMINATED`) → push `m.unitStats` (§18.5) · `g.leave` S→C: - `m.public` — full public state on every change (throttled ≤ 10/s): `{ phase, round, lastRound, deadline, serverNow, modeId, difficulty, stageId, factions, disabledBonds, bannedChess, bossId, hiddenBossId?, teamLp?, bossHp?:{hp,max}, overtimeAt? /* FA / HC: ms epoch the overtime drain starts, §4 */, paused /* solo pause, §14 */, draft?:{order,turn,picks,skipsLeft,turnDeadline,turnSeconds /* one turn's length, 0 untimed; deadline = turnDeadline (§18.2) */,untimed}, sp?:{family,cards /* bounty cards carry descRaw?, §20.6 */,turn,picks}, players:[{playerId, seat, name, isBot, connected, alive, lp, bandId, shopLevel, boardCount, ready, bonds:[{bondId,count,active,tier,layers,harmony? /* 调和's +1 is in count, §21.26 */}], fieldId, status, pendingLp? /* COMBAT / 联防 of a normal round: the LP the player's own battle will cost at settlement so far, min(lpCapPerRound, counted leaks) — in 联防 min(lpCapPerRound, uniteLeft) — omitted when 0 and in every other phase (§17.5 live LP) */, uniteLeft? /* 联防: the leaker's enemies still standing — not spawned yet + alive + counted leaks, uncapped, live (§20.6) */}] , fields:[{fieldId, kind, players:[playerId...]}] }` - `m.private` — the recipient's full PlayerState view (§6.2 minus server-only data) on every change. @@ -75,7 +75,7 @@ Piece = { uid, kind: 'chess'|'item'|'token', id, golden: bool, tier, items: [ { **Protocol (additions; `shared/protocol.js` is normative).** - `battleId` = `...` (`Match._ccField`; `` = the match seed in base 36 plus `-`, the room's match number the lobby passes as `opts.matchNo`; `` counts every battle of the match; the informational `.` is dropped when the id would exceed the protocol's 64 chars): unique for the whole match although field ids repeat (`n:` every round, `u`, `b1`/`b2` in both boss rounds), and across the matches of one room. The client runner keys its local battles by it (the same id again = the same battle, no rebuild) and the server matches reports by it, so a late `b.progress` / `b.result` of a finished battle — or one that crossed into the room's next match on the same socket — is ignored instead of being credited to another battle. -- S→C `b.start { battleId, fieldId, kind, spec, authoritative, startAt, serverNow, elapsed, speed, watch, done }` — sent at COMBAT/UNITE/FINAL_ASSAULT/HIDDEN start to every human in the field (联防: to every human; COMBAT and boss rounds: eliminated humans get the first field as a display replica — research 09 "keep-watching auto-observes the first available field"), on `g.watch`, and on reconnect/resync. `elapsed` = game seconds on the field clock (a finished field: its end time); the client fast-forwards to it. `speed` = game seconds per real second (2; tests/tools may speed up). +- S→C `b.start { battleId, fieldId, kind, spec, authoritative, startAt, serverNow, elapsed, speed, watch, done }` — sent at COMBAT/UNITE/FINAL_ASSAULT/HIDDEN start to every human in the field (联防: to every human; COMBAT and boss rounds: eliminated humans and spectator seats get the field of the player they follow as a display replica — the one they last watched with `g.watch`, else the first player still in; research 09 "keep-watching auto-observes the first available field", 0.2.0 item 56), on `g.watch`, and on reconnect/resync. `elapsed` = game seconds on the field clock (a finished field: its end time); the client fast-forwards to it. `speed` = game seconds per real second (2; tests/tools may speed up). - C→S `b.progress { battleId, gt, killed, total, leaks?, left?, bossDmg?, by?, done? }` — authoritative clients, ~1 Hz (boss fields 4 Hz). Normal/联防: `leaks` = counted leaks so far; 联防: `left` = per leaker its enemies still standing (`spec.js uniteLeft`, clamped by the server to `fields.js uniteBillBounds`; §20.6). Boss: `bossDmg` = cumulative damage of this field to the shared pool, `by` = the same per player, `leaks` = the field's cumulative LP cost (leaks × `lpr` + leader "扣除目标生命" effects; `spec.js attachLpMeter`). The server applies deltas (never twice). Stale reports (unknown battle, not the authority, after the match ended) are ignored, never answered with an error; the platform never answers a rid-less `b.progress` with an error frame (it would surface as a toast). - C→S `b.result { battleId, result }` — authoritative client at battle end; `result` = `compactResult(BattleResult)` (the fields the match consumes; ≤ 4 players, ≤ 400 leaks, ≤ 64 unitsEnd, ≤ 160 unitStats, ≤ 40 layer gains; `fitResult` keeps the frame ≤ 60 KB: unit statistics, then leak mods, then — boss only — leak entries go first). Structural checks in `isBattleResult`; `server/net.js` routes `b.*` frames to the match (lobby `routeGame`). - S→C `b.pool { hp, max, teamLp, acked: { [fieldId]: cumulative damage counted } }` (boss rounds, ≤ 4 Hz; exact numbers, never rounded) — clients reconcile their `LocalBossPool`: displayed pool = server hp − (local cumulative damage − acked). Pools never hold less than 1 HP (`BOSS_POOL_MIN_HP`: `SharedBossPool` and `LocalBossPool`; a browser pool reads any finite rest below 1 as 0 — §20.5); the 'cleared'-result dust rule (a boss field whose client result is `'cleared'` while < 1 HP is left on the server empties the pool) only catches noise at that boundary. @@ -89,7 +89,7 @@ Piece = { uid, kind: 'chess'|'item'|'token', id, golden: bool, tier, items: [ { **Solo pause (official `PauseUp` / `ResumeUp`; `Match.setPause`).** C→S `g.pause { on }`: solo matches only (co-op → `WRONG_PHASE 'co-op battles never pause'`), and `on: true` only while a battle runs (COMBAT / FINAL_ASSAULT / HIDDEN_CORE with a live field, not once the Final Assault is ending — else `WRONG_PHASE 'no battle running'`); `on: false` is always accepted. While `m.public.paused` is true everything of the battle is frozen: the field clock (`b.start elapsed`, the boss plausibility budgets and the overtime drain), the authority deadline and the server-run release timers, the boss clock, the HUD `deadline` / `overtimeAt` (shifted by the paused time on resume) and the server-run pacers (FieldRunner / HeadlessPacer skip their intervals); the browser's runner stops its local clock. In solo battles the top bar shows a 暂停 / 继续作战 button (`ui/hud.js PauseButton`, `ui/matchStatus.js pauseAvailable`; `Space` toggles it), a paused overlay offers 继续作战 / 放弃模拟, and the HUD clocks freeze at the pause moment. A disconnect or leave resumes (the server takes the field over — nobody is left to resume it); the battle phase ending drops the pause. -**Spectating (research 09 §3.1 / §6.3).** No live spectating while your own normal battle runs (`g.watch` → `WRONG_PHASE 'own battle running'`; the UI toasts "作战中无法查看队友"). After your own battle ends you see "⌛ 作战结束,等待队友完成作战" with each teammate's live progress (`m.public.fields[].progress`); tapping a teammate's avatar expands **前往查看** → `g.watch` → `b.start` (display replica fast-forwarded to the field clock); the own row shows **返回战场**. Prep: the same flow shows the teammate's board (`m.field` with `prep: true`) and sends it again when that board changes (GitHub #87). The scout is framed by the prep camera in its shop-folded form and renders the teammate’s hand (整备区) and 临时整备区 as units on their rows — the own prep bench’s exact look (user playtest #2 item 1, the GitHub #44 suggestion; the spectator seat's scout view is the same, §23.19's never-a-private-view staying the no-`m.private` rule). While scouting, the 本局信息 drawer shows the watched player's 策略 in place of one's own (`game.js scoutPid` → `enemyDrawer.js InfoTab bandId / bandOwner`, labelled "X 的策略"; user playtest #2 item 2), and the right effects column follows the watched field too: the scout meta carries the player's effects view, a display-replica battle derives it from the spec's `playerEffects` (`battle/observe.js spectateEffects`) — undefined (empty column, never one's own) for 联防 / boss pair fields and server-run watched fields. 联防: every human receives the unite spec (helpers + observers). Final Assault: each human sees its own pair field; the other pair → `BAD_TARGET 'other group hidden'`. In 联防/FA fields the ‹ › pill switches the camera LEFT half / 全景 / RIGHT half ("你自己" / "👁 name" / "全景" / "无人在家"); there is **no** view switcher during normal combat. Eliminated players start each combat phase on the first field (auto-observe) and may watch any field. +**Spectating (research 09 §3.1 / §6.3).** No live spectating while your own normal battle runs (`g.watch` → `WRONG_PHASE 'own battle running'`; the UI toasts "作战中无法查看队友"). After your own battle ends you see "⌛ 作战结束,等待队友完成作战" with each teammate's live progress (`m.public.fields[].progress`); tapping a teammate's avatar expands **前往查看** → `g.watch` → `b.start` (display replica fast-forwarded to the field clock); the own row shows **返回战场**. Prep: the same flow shows the teammate's board (`m.field` with `prep: true`) and sends it again when that board changes (GitHub #87). The scout is framed by the prep camera in its shop-folded form and renders the teammate’s hand (整备区) and 临时整备区 as units on their rows — the own prep bench’s exact look (user playtest #2 item 1, the GitHub #44 suggestion; the spectator seat's scout view is the same, §23.19's never-a-private-view staying the no-`m.private` rule). While scouting, the 本局信息 drawer shows the watched player's 策略 in place of one's own (`game.js scoutPid` → `enemyDrawer.js InfoTab bandId / bandOwner`, labelled "X 的策略"; user playtest #2 item 2), and the right effects column follows the watched field too: the scout meta carries the player's effects view, a display-replica battle derives it from the spec's `playerEffects` (`battle/observe.js spectateEffects`) — undefined (empty column, never one's own) for 联防 / boss pair fields and server-run watched fields. 联防: every human receives the unite spec (helpers + observers). Final Assault: each human sees its own pair field; the other pair → `BAD_TARGET 'other group hidden'`. In 联防/FA fields the ‹ › pill switches the camera LEFT half / 全景 / RIGHT half ("你自己" / "👁 name" / "全景" / "无人在家"); there is **no** view switcher during normal combat. Eliminated players and spectator seats follow a player through every phase reset (0.2.0, community report item 56; the official DeadAutoObDn { obIndex, state, preparation }; the idea of PR #189): the player they last watched with a manual `g.watch` (`Match.watchPref`; a shared field records the `playerId` tapped, else nobody), else the first player still in (seat order, AI seats included) — the round start pushes that player's prep board (`Match._followScout`, adopted by the screen like a 前往查看 tap: `battle/observe.js followedScout`; kept through 机变 and 休整期), each battle starts on that player's field, a resync / a spectator joining in prep gets the board again; they may watch any field. **Client runner.** `public/js/battle/runner.js` owns the local Battles: fixed-step clock at the battle speed (60 ticks per real second at 2×, at most `max(8, 4·speed)` ticks per frame, bounded fast-forward of ≤ 240 ticks per frame when far behind; a new battle is caught up silently before it is shown), feeds the existing renderer through the same `snapshot()`/`drainEvents()` wire formats every frame (`view.setLocalFeed` shrinks the interpolation buffer to ~2 frames), publishes the field meta (m.field shape + `local`, `battleId`, `players`, `sides`, `speed`) into `store.match.field` and its state into `store.match.battle` (with `leaks: { [fieldId]: counted leaks so far }` of every normal field it simulates and, while the 联防 field is on screen, `uniteLeft` per leaker (§20.6), authoritative or display replica — the settle rule's count, `/sim/spec.js battleProgress`; published in the frame a count changes: the live LP of §17.5), sends `b.progress`/`b.result` when authoritative (results retried once on timeout; a result lost with the socket — the request failed `DISCONNECTED` / `OFFLINE` or timed out twice — is kept and sent again when the session is back online and whenever a `b.start` still names that finished battle `authoritative: true`; the server answers a duplicate `ok` and changes nothing; a server refusal is final), follows `m.public.paused` (solo pause: every local clock stands still, resume shifts it by the paused time), handles `b.pool`/`b.end`, fast-forwards on reconnect/observe, and survives tab backgrounding (a 250 ms interval pump keeps authoritative battles on their clock while hidden; the server deadline + takeover cover extreme cases). Catch-up frames keep the state events and every enemy form fx (`keepsState`); while hidden, the battle on screen holds them for the first frame back (`hold`; past `HELD_MAX` the superseded status / skill toggles are dropped, `compactHeld`, and only a backlog still past it re-enters the view from the field meta), and a battle that ended while hidden delivers them when the tab is shown; a catch-up frame or a hidden step drains its events every `EV_SLICE` (15) ticks, each batch stamped with its own game time (`gt`), so a late form fx reaches the render engine as late as it is; the game screen's pre-entry buffer (`keepEarly`, also while a re-sent field meta re-enters the field on screen) and the render engine's event queue (`render/interp.js isCosmeticEvent`) never drop a form fx either (§21.4). Sim content errors are logged as console warnings (the server logs its own). The sim loader fetches the ten data files (one retry each) and fails when any is missing — a battle on partial data would report a plausible wrong result — so the next `b.start` retries and the server's deadline covers the field meanwhile. `public/js/battle/observe.js` holds the observing rules and labels. diff --git a/public/js/battle/observe.js b/public/js/battle/observe.js index f18708bd..99ab8602 100644 --- a/public/js/battle/observe.js +++ b/public/js/battle/observe.js @@ -6,7 +6,8 @@ // replica of their battle; 返回战场 goes back; // * 联防 / 最终攻势: the ‹ › pill switches the camera LEFT half / 全景 / RIGHT half of the own field; the other pair's // boss field is never shown to a fighting player; -// * eliminated: anything. +// * eliminated: anything — each phase reset starts on the player it follows (the server's prep scout / b.start, +// followedScout). import { PHASE } from '../../../shared/constants.js'; import { data } from '../data.js'; @@ -82,6 +83,23 @@ export function resumedWatch(b, { pub = null, myId = '', alive = true, watching return { seen: b.battleId, fieldId: b.fieldId }; } +/** + * The prep board the server pushed to a viewer that follows a player (an eliminated player or a spectator seat: + * Match._followScout — the player it last watched, else the first player still in; community report of 2026-10-06, + * item 56, the idea of PR #189), to adopt as the watched board like a 前往查看 tap: `field` is such a scout (m.field + * `prep`, an `n:` id other than the own) and the screen watches nothing (`watching` null — a 返回战场 this phase is + * not overridden: the caller adopts a board once per phase). Null for a living player: its phase resets keep its own + * board. + * @param {{ field?: any, watching?: string|null, alive?: boolean, spectator?: boolean, myId?: string }} o + * @returns {string|null} the fieldId to watch + */ +export function followedScout({ field = null, watching = null, alive = true, spectator = false, myId = '' } = {}) { + if (watching || (alive && !spectator) || !isObj(field) || !field.prep) return null; + const fid = field.fieldId; + if (typeof fid !== 'string' || !fid.startsWith('n:') || fid === `n:${myId}`) return null; + return fid; +} + /** Teammates' progress for the waiting pill: [{ playerId, name, killed, total, done, isBot }]. */ export function teammateProgress(pub, myId) { const out = []; diff --git a/public/js/screens/game.js b/public/js/screens/game.js index 43563c64..4450d41d 100644 --- a/public/js/screens/game.js +++ b/public/js/screens/game.js @@ -106,7 +106,7 @@ import { ResultScreen } from './result.js'; import { net } from '../net.js'; import { store, useStore, shallowEqual, serverNow, isSpectating } from '../store.js'; import { battleRunner } from '../battle/runner.js'; -import { isClientCombat, observeTarget, teammateProgress, cameraLayers, layerCamera, sidesOf, resumedWatch } from '../battle/observe.js'; +import { isClientCombat, observeTarget, teammateProgress, cameraLayers, layerCamera, sidesOf, resumedWatch, followedScout } from '../battle/observe.js'; import { screenStrip, playerBonds, playerLayer, detailBondOwner, toggleBond, popupView } from '../ui/watchBonds.js'; import { data, getMode } from '../data.js'; import { audio, resultSpeaker, resultVoiceSlot } from '../audio.js'; @@ -251,7 +251,7 @@ function MatchScreen() { getChess: (id) => { const c = gd.chess(id); return ownDiyRecord(c, priv, { chess: data.get('chess'), backups: data.get('backups') }) || c; }, getToken: gd.token, getItem: gd.item, getEffect: gd.effect, backups: gd.backups, }), [priv, pub?.stageId, editable, gd.ready, deployField]); - live.current = { pub, priv, field, editable, placeCtx, watching, watchWho, home, myId, detail, drawer, bondOpen, emoteOpen, settingsOpen, exitOpen, drag, facing, sel, selBusy, pen, collapsedNow: collapsed, localDone: false, canPause: false, paused }; + live.current = { pub, priv, field, editable, placeCtx, watching, watchWho, home, myId, alive, spectator, detail, drawer, bondOpen, emoteOpen, settingsOpen, exitOpen, drag, facing, sel, selBusy, pen, collapsedNow: collapsed, localDone: false, canPause: false, paused }; // ---- camera: every request goes through setCam, which remembers it for the pen's way back ----------------------- // the own prep board: the normal board, or — in the prep of a boss round — the player's half of the boss field @@ -582,8 +582,15 @@ function MatchScreen() { else if (phase === PHASE.FINAL_ASSAULT) audio.sfx(solo ? 'bossRoundSingle' : 'bossRoundTeam'); else if (phase === PHASE.HIDDEN_CORE) audio.sfx('bossRoundSecret'); else if (phase === PHASE.SP_DRAFT) audio.sfx('draft'); - setWatching(null); // the server resets every watcher to its own field on phase changes - setWatchWho(null); + // the server resets every watcher to its own field on phase changes — but an eliminated player / spectator seat + // keeps the prep board it follows from 回合开始 through 机变 and 休整期 (Match._followScout re-points it only at the + // round start): resetting here would fly the camera home and back at every prep phase (item 56) + const keepScout = (phase === PHASE.SP_DRAFT || phase === PHASE.PREP) && (!alive || spectator) + && typeof live.current.watching === 'string' && live.current.watching.startsWith('n:'); + if (!keepScout) { + setWatching(null); + setWatchWho(null); + } // the pen is a 休整期 view: leaving prep returns the camera (the next setCam would, too) if (phase !== PHASE.PREP && penRef.current.on) togglePenRef.current(false); // a battle unit's panel (live HP of a unit of the fight that just ended) never outlives its battle @@ -699,7 +706,7 @@ function MatchScreen() { const who = playerId ? { fieldId: fid, playerId } : null; setWatching(fid); setWatchWho(who); - const ok = await actions.watch(fid); + const ok = await actions.watch(fid, playerId); if (!ok) { setWatching((w) => (w === fid ? prev : w)); setWatchWho((w) => (w === who ? prevWho : w)); @@ -713,8 +720,11 @@ function MatchScreen() { if (L.watching && L.watching !== L.home) { const target = isCombatPhase(L.pub?.phase) ? L.home : ownFieldId(L.myId); // client-side combat without an own field to go back to (a 联防 leaker, an eliminated player): the screen keeps - // the field it shows — g.watch of a field that does not exist would only be refused (an error toast) - const exists = !isClientCombat(L.pub) || !isCombatPhase(L.pub?.phase) || (Array.isArray(L.pub?.fields) && L.pub.fields.some((f) => f && f.fieldId === target)); + // the field it shows — g.watch of a field that does not exist would only be refused (an error toast); an + // eliminated player / spectator seat has no prep board of its own either + const combatNow = isCombatPhase(L.pub?.phase); + const exists = combatNow ? (!isClientCombat(L.pub) || (Array.isArray(L.pub?.fields) && L.pub.fields.some((f) => f && f.fieldId === target))) + : (L.alive && !L.spectator); if (exists) actions.watch(target); } setWatching(null); @@ -733,7 +743,9 @@ function MatchScreen() { } const self = p.playerId === L.myId; if (self) { - if (L.watching && L.watching !== L.home) actions.watch(isCombatPhase(L.pub?.phase) ? L.home : ownFieldId(L.myId)); + // (an eliminated player / spectator seat has no prep board of its own to ask for: item 56) + const combatNow = isCombatPhase(L.pub?.phase); + if (L.watching && L.watching !== L.home && (combatNow || (L.alive && !L.spectator))) actions.watch(combatNow ? L.home : ownFieldId(L.myId)); setWatching(null); setWatchWho(null); return; @@ -745,17 +757,25 @@ function MatchScreen() { const watchField = useCallback((fid) => { requestWatch(fid); }, []); - // a spectator seat has no board of its own: in 休整期 / 机变 / round start it is shown the first player still in (as a - // tap on that row would — g.watch 'n:', the read-only board), once per phase; a row switches to another player - const scoutedRef = useRef(null); + // an eliminated player or a spectator seat follows a player through every phase reset (community report of + // 2026-10-06, item 56; the idea of PR #189): the server pushes that player's prep board (Match._followScout) — the one + // it last watched, else the first player still in — and the screen adopts it like a 前往查看 tap, once per phase and + // board (a 返回战场 this phase is not overridden; a row switches to another player, who is followed from then on). + // Until 0.2.0 every round's reset dropped an eliminated player onto its own empty board (a spectator seat asked for + // the first player itself). + const followRef = useRef(null); useEffect(() => { - if (!spectator || watching || !pub || scoutedRef.current === phaseKey) return; - if (phase !== PHASE.PREP && phase !== PHASE.SP_DRAFT && phase !== PHASE.ROUND_START) return; - const first = players.find((p) => p.alive !== false && p.status !== 'left'); - if (!first) return; - scoutedRef.current = phaseKey; - requestWatch(ownFieldId(first.playerId), first.playerId); - }, [spectator, phaseKey, watching]); + const fid = followedScout({ field, watching, alive, spectator, myId }); + if (!fid || !pub) return; + const key = `${phaseKey}:${fid}`; + if (followRef.current === key) return; + followRef.current = key; + // the phase reset's render (watching null) ran the own-prep branch with this scout meta in `field` and marked it + // stale: un-mark it, or the enter effect refuses it and the board stays blank until the player moves (PR #189) + if (staleFieldRef.current === field) staleFieldRef.current = null; + setWatching(fid); + setWatchWho({ fieldId: fid, playerId: fid.slice(2) }); + }, [field, pub, watching, alive, spectator, phaseKey, myId]); // ---- view events (drag & drop, clicks) ---------------------------------------------------------------------- useEffect(() => { diff --git a/public/js/ui/gameActions.js b/public/js/ui/gameActions.js index 08205f51..fc7ae50f 100644 --- a/public/js/ui/gameActions.js +++ b/public/js/ui/gameActions.js @@ -67,7 +67,8 @@ export const actions = { choice: (idx) => act('g.choice', { idx }), ready: (ready) => act('g.ready', { ready }, { sfx: ready ? 'ready' : 'back' }), emote: (id) => act('g.emote', { id }, { quiet: true }), - watch: (fieldId) => act('g.watch', { fieldId }, { sfx: 'tab' }), + // `playerId`: the player tapped in the team panel (a shared field shows two) — what an eliminated viewer follows + watch: (fieldId, playerId = null) => act('g.watch', typeof playerId === 'string' && playerId ? { fieldId, playerId } : { fieldId }, { sfx: 'tab' }), autoplay: (on) => act('g.autoplay', { on }), // solo battles only (ui/matchStatus.js pauseAvailable): m.public.paused follows pause: (on) => act('g.pause', { on: !!on }, { sfx: on ? 'click' : 'confirm' }), diff --git a/server/match/Match.js b/server/match/Match.js index 22a1c08c..570889c8 100644 --- a/server/match/Match.js +++ b/server/match/Match.js @@ -64,7 +64,9 @@ // addSpectator(id) (optional for the platform) A spectator seat joined during the match, came back or asked // for a resync: register it (idempotent) and resend what an ELIMINATED player watching sees — // m.public and, while a battle runs, the b.start (watch) of the field it watches (default: the -// first field; server-run mode: m.field + b.snap), or m.result once ended. A spectator gets +// field of the player it follows — the one it last watched, else the first player still in; +// server-run mode: m.field + b.snap), in a prep phase that player's board (m.field prep), or +// m.result once ended. A spectator gets // every broadcast through the platform, never an m.private / m.toast / m.unitStats, is never // a field's player or authority, and is shown fields like an eliminated player in every phase. // handle(id, msg) answers only its 'g.watch' (anything else → SPECTATOR; the platform routes @@ -358,6 +360,12 @@ export class Match { this.runner = null; /** playerId → fieldId */ this.watchers = new Map(); + /** + * Watch preference (community report of 2026-10-06, item 56; the idea of PR #189): viewer playerId → the player it + * last chose to watch with a manual g.watch. Every phase reset starts an eliminated human or a spectator seat on that + * player again (its prep board, its battle field) — else the first player still in — instead of its own empty board. + */ + this.watchPref = new Map(); this.lastResults = new Map(); this.unitePlan = null; /** server-run 联防: the leakers' counts last published (_uniteTick) */ diff --git a/server/match/match/combat.js b/server/match/match/combat.js index 2d0ee700..69d82b3f 100644 --- a/server/match/match/combat.js +++ b/server/match/match/combat.js @@ -92,7 +92,8 @@ export class MatchCombat { this.deadline = this.sched.instant ? 0 : this.sched.now() + Math.round((limit / this.gameSpeed) * 1000); this._launch(fields); // every fighting human runs its own field; eliminated humans (and spectator seats) keep watching (research 09 §3.1 - // "Keep-watching auto-observes the first available field", switching freely with 前往查看): a replica of the first field + // "Keep-watching auto-observes the first available field", switching freely with 前往查看): a replica of the field + // of the player they follow — the one they last watched, else the first player still in (item 56) — else the first for (const f of fields) for (const pid of f.players) { const ps = this.players.get(pid); if (!ps || ps.isBot || ps.left) continue; @@ -103,8 +104,9 @@ export class MatchCombat { if (first) { for (const ps of this._viewers()) { if (ps.alive || this.watchers.has(ps.playerId)) continue; - this.watchers.set(ps.playerId, first.fieldId); - this._sendStart(ps.playerId, first, { watch: true }); + const f = this._watchTargetField(ps, fields) || first; + this.watchers.set(ps.playerId, f.fieldId); + this._sendStart(ps.playerId, f, { watch: true }); } } this.markPublic(); diff --git a/server/match/match/intents.js b/server/match/match/intents.js index 499447ad..7a96823f 100644 --- a/server/match/match/intents.js +++ b/server/match/match/intents.js @@ -34,7 +34,8 @@ export class MatchIntents { case 'g.choice': return this.pickCard(ps, msg.idx); case 'g.ready': return ps.setReady(!!msg.ready); case 'g.emote': return this.emote(ps, msg.id); - case 'g.watch': return this.watch(ps, msg.fieldId); + // playerId: the player tapped (a shared field names two) — the watch preference (item 56) + case 'g.watch': return this.watch(ps, msg.fieldId, msg.playerId ?? null); case 'g.autoplay': return this.setAutoplay(ps, !!msg.on); case 'g.pause': return this.setPause(ps, !!msg.on); // the stats the board's units start their next battle with (the detail card in prep, user playtest #4 item 7) diff --git a/server/match/match/phases.js b/server/match/match/phases.js index 34c67800..408d868c 100644 --- a/server/match/match/phases.js +++ b/server/match/match/phases.js @@ -253,6 +253,9 @@ export class MatchPhases { for (const ps of alive) ps.recompute(); this.setDeadline(DELAYS.ROUND_START / 1000, () => this.afterRoundStart(), { silent: this.soloUntimed }); this.markPublic(); + // eliminated humans and spectator seats scout the board of the player they follow through the round's prep, not + // their own empty board (community report of 2026-10-06, item 56; MatchWatch._followScout) + for (const ps of this._viewers()) this._followScout(ps); } /** The boss round's fields (seat pairs of the alive players) and their templates, generated for the prep preview. */ diff --git a/server/match/match/platform.js b/server/match/match/platform.js index 64d608cf..5d20f2a3 100644 --- a/server/match/match/platform.js +++ b/server/match/match/platform.js @@ -100,7 +100,8 @@ export class MatchPlatform { /** * The full state of one human (a reconnect, a resync, a spectator seat): m.public, its m.private (players only), the - * field it is on / watches — a spectator, like an eliminated player, the first field — or the result once ended. + * field it is on / watches — a spectator, like an eliminated player, the field of the player it follows (item 56), + * else the first; in a prep phase that player's board — or the result once ended. */ _resync(ps) { const playerId = ps.playerId; @@ -111,9 +112,10 @@ export class MatchPlatform { this._sendPrivate(ps, true); } if (this.clientCombat) this._resendBattle(ps); + else if (!this.fields.length && this._follows(ps)) this._followScout(ps, { keep: true }); else { let fid = this.watchers.get(playerId); - if (!fid && ps.spectator && this.fields.length) { fid = this.fields[0].fieldId; this.watchers.set(playerId, fid); } + if (!fid && ps.spectator && this.fields.length) { fid = (this._watchTargetField(ps, this.fields) || this.fields[0]).fieldId; this.watchers.set(playerId, fid); } if (fid) this._sendField(playerId, fid); } } else if (this.lastResultMsg) { @@ -180,6 +182,8 @@ export class MatchPlatform { this._planBossWaves(); for (const p of this.alivePlayers()) p.recompute(); } + // whoever scouted the departed player's board follows the next player still in (item 56) + for (const v of this._viewers()) if (this.watchers.get(v.playerId) === `n:${ps.playerId}`) this._followScout(v); this.markPublic(); if (this.teamLp != null) this._syncTeamLp(); if (!this.alivePlayers().length) { diff --git a/server/match/match/watch.js b/server/match/match/watch.js index d73c7688..0fec7fc2 100644 --- a/server/match/match/watch.js +++ b/server/match/match/watch.js @@ -2,7 +2,9 @@ // #26: stand-ins shown fields like an eliminated player), g.watch in both combat modes (the boss-group rule, prep // scouting of a teammate's board), who is shown which field (the default watch, the boss fields, the resend on a // reconnect, the spectator's spec without the funds), a field's m.field + b.snap (server-run mode) and the prep-scout -// pushes (GitHub #87). +// pushes (GitHub #87). Eliminated humans and spectator seats follow a player through every phase reset (community +// report of 2026-10-06, item 56; the official DeadAutoObDn { obIndex, state, preparation }, research 09 §3.1; the idea +// of PR #189): the player they last watched with a manual g.watch (`watchPref`), else the first player still in. // Installed on Match.prototype by server/match/Match.js (a method container: never instantiated; `this` is the match). import { PHASE, ERR } from '../../../shared/constants.js'; @@ -44,9 +46,13 @@ export class MatchWatch { return out; } - watch(ps, fieldId) { + /** + * g.watch { fieldId, playerId? } — `playerId`: the player the viewer tapped (a shared 联防 / boss pair field shows two, + * its id does not say which; recorded as the watch preference when that player is on the field). + */ + watch(ps, fieldId, playerId = null) { if (typeof fieldId !== 'string') return fail(ERR.BAD_TARGET); - if (this.clientCombat && this.fields.length && this.fields.some((x) => x.cc)) return this._watchClient(ps, fieldId); + if (this.clientCombat && this.fields.length && this.fields.some((x) => x.cc)) return this._watchClient(ps, fieldId, playerId); const f = this.fields.find((x) => x.fieldId === fieldId); if (f) { // 最终攻势 / 隐秘核心: "两名参与者会处于同一个战场,但无法查看另一组队友的战场情况" — a fighting player sees its own @@ -54,6 +60,7 @@ export class MatchWatch { const own = this.fieldOf(ps); if ((f.kind === 'boss' || f.kind === 'hidden') && own && own !== f.fieldId) return fail(ERR.BAD_TARGET, 'other group hidden'); this.watchers.set(ps.playerId, fieldId); + this._watchPrefSet(ps, f, playerId); this._sendField(ps.playerId, fieldId); return OK; } @@ -66,18 +73,68 @@ export class MatchWatch { const target = this.players.get(fieldId.slice(2)); if (!target || !target.alive) return fail(ERR.BAD_TARGET); this.watchers.set(ps.playerId, fieldId); + if (target !== ps) this.watchPref.set(ps.playerId, target.playerId); this._notifyPrepScouts(target, { to: ps.playerId }); return OK; } return fail(ERR.BAD_TARGET); } + /** + * Record the player a manual watch of field `f` names: `playerId` when it is one of the field's players, else the + * field's only player — never the viewer itself; a shared field without `playerId` names nobody (PR #189's review). + * Only manual watches write it: the automatic assignments below read it. + */ + _watchPrefSet(ps, f, playerId = null) { + const players = Array.isArray(f?.players) ? f.players : []; + const pid = typeof playerId === 'string' && players.includes(playerId) ? playerId : players.length === 1 ? players[0] : null; + if (pid && pid !== ps.playerId) this.watchPref.set(ps.playerId, pid); + } + + /** + * The player a viewer follows (an eliminated human, a spectator seat): its watch preference while that player is + * still in, else the first player still in (seat order — the official FindFirstAvailObTarget, AI teammates included: + * the remake's AI seats are players), never the viewer itself; null when nobody is left. + */ + _watchTargetOf(ps) { + const ok = (pid) => { const t = typeof pid === 'string' && pid !== ps.playerId ? this.players.get(pid) : null; return !!t && t.alive && !t.left; }; + const pref = this.watchPref.get(ps.playerId); + if (ok(pref)) return pref; + return this.order.find((q) => ok(q.playerId))?.playerId ?? null; + } + + /** The field of the player `ps` follows among `fields` (null when that player has none this phase). */ + _watchTargetField(ps, fields) { + const pid = this._watchTargetOf(ps); + return pid ? fields.find((f) => Array.isArray(f.players) && f.players.includes(pid)) || null : null; + } + + /** Does `ps` follow a player (an eliminated human or a spectator seat, still in the room)? */ + _follows(ps) { + return !!ps && !ps.isBot && !ps.left && (ps.spectator || !ps.alive); + } + + /** + * A prep phase (回合开始 / 机变 / 休整期): an eliminated human / spectator seat scouts the board of the player it follows + * — `keep`: a scout it already has this phase stays (a manual pick, a resync). Until 0.2.0 a phase reset dropped it + * onto its own empty board every round (community report of 2026-10-06, item 56). + */ + _followScout(ps, { keep = false } = {}) { + if (!this._follows(ps) || this.fields.length) return; + const cur = keep ? this.watchers.get(ps.playerId) : null; + const kept = typeof cur === 'string' && cur.startsWith('n:') ? this.players.get(cur.slice(2)) : null; + const target = kept && kept.alive && !kept.left ? kept : this.players.get(this._watchTargetOf(ps)); + if (!target) return; + this.watchers.set(ps.playerId, `n:${target.playerId}`); + if (ps.connected) this._notifyPrepScouts(target, { to: ps.playerId }); + } + /** * g.watch under client-side combat: the watcher gets the field's spec (b.start, display only) and runs a local * replica fast-forwarded to the field's clock. No watching while the own normal battle runs; the other pair's boss * field is never shown to a fighting player; eliminated players may watch anything. */ - _watchClient(ps, fieldId) { + _watchClient(ps, fieldId, playerId = null) { const f = this.fields.find((x) => x.fieldId === fieldId) || null; if (!f) return fail(ERR.BAD_TARGET, 'no such field'); const own = this.fields.find((x) => x.players.includes(ps.playerId)) || null; @@ -86,17 +143,22 @@ export class MatchWatch { if (f.kind === 'normal' && own && own !== f && own.live) return fail(ERR.WRONG_PHASE, 'own battle running'); } this.watchers.set(ps.playerId, f.fieldId); + this._watchPrefSet(ps, f, playerId); this.sendTo(ps.playerId, this._startMsg(f, ps.playerId, { watch: !f.players.includes(ps.playerId) })); return OK; } - /** Reconnect / resync: the spec of the field the player is on (fast-forwarded by the client). */ + /** + * Reconnect / resync: the spec of the field the player is on (fast-forwarded by the client); an eliminated human / + * spectator seat the field it watches, else the one of the player it follows. With no client field up (a prep phase): + * the board of the player it follows (item 56; PR #189's review — a resync in prep used to show nothing). + */ _resendBattle(ps) { - if (!this.fields.some((f) => f.cc)) return; + if (!this.fields.some((f) => f.cc)) { this._followScout(ps, { keep: true }); return; } const fid = this.watchers.get(ps.playerId); let f = fid ? this.fields.find((x) => x.fieldId === fid) : null; if (!f) f = this.fields.find((x) => x.players.includes(ps.playerId)) || (this.phase === PHASE.UNITE ? this.fields[0] : null); - if (!f && !ps.alive) f = this.fields[0] || null; + if (!f && !ps.alive) f = this._watchTargetField(ps, this.fields) || this.fields[0] || null; if (!f) return; this.watchers.set(ps.playerId, f.fieldId); this.sendTo(ps.playerId, this._startMsg(f, ps.playerId, { watch: !f.players.includes(ps.playerId) })); @@ -106,18 +168,22 @@ export class MatchWatch { this.watchers.clear(); for (const ps of this._viewers()) { const own = this.fields.find((f) => f.players.includes(ps.playerId)); - const f = own || this.fields[0]; + // an eliminated human / spectator seat: the field of the player it follows (item 56), else the first + const f = own || this._watchTargetField(ps, this.fields) || this.fields[0]; if (!f) continue; this.watchers.set(ps.playerId, f.fieldId); if (ps.connected) this._sendField(ps.playerId, f.fieldId); } } - /** b.start of the boss fields: players get their own pair field, eliminated humans and spectator seats the first. */ + /** + * b.start of the boss fields: players get their own pair field, eliminated humans and spectator seats the field of + * the player they follow (item 56), else the first. + */ _watchBossFields(fields) { for (const ps of this._viewers()) { const own = fields.find((f) => f.players.includes(ps.playerId)) || null; - const f = own || fields[0]; + const f = own || this._watchTargetField(ps, fields) || fields[0]; if (!f) continue; this.watchers.set(ps.playerId, f.fieldId); this._sendStart(ps.playerId, f, { watch: !own }); diff --git a/shared/protocol.js b/shared/protocol.js index 6fd8ec00..19282a2a 100644 --- a/shared/protocol.js +++ b/shared/protocol.js @@ -368,7 +368,9 @@ export const C2S = { 'g.choice': { idx: (v) => isInt(v, 0, 5) }, 'g.ready': { ready: isBool }, 'g.emote': { id: (v) => EMOTES.includes(v) }, - 'g.watch': { fieldId: (v) => isStr(v, 32) }, + // playerId: the player tapped in the team panel (a 联防 / boss pair field shows two) — what an eliminated viewer or a + // spectator seat follows from then on (Match.watchPref; community report of 2026-10-06, item 56) + 'g.watch': { fieldId: (v) => isStr(v, 32), playerId: isId, $optional: ['playerId'] }, 'g.autoplay': { on: isBool }, // solo pause (official PauseUp / ResumeUp, DESIGN §14): freezes the running battle (field clock, deadlines, the // browser's local runner) — solo matches only (co-op ⇒ WRONG_PHASE), only while a battle runs; m.public.paused diff --git a/test/match/feedback5-watch-follow.test.js b/test/match/feedback5-watch-follow.test.js new file mode 100644 index 00000000..5466098f --- /dev/null +++ b/test/match/feedback5-watch-follow.test.js @@ -0,0 +1,156 @@ +// Community report of 2026-10-06, item 56: 「死亡观战时每一回合结束都会被转回自己家」. An eliminated human (and a spectator +// seat) now follows a player through every phase reset — the official keeps observing after death (research 09 §3.1: +// self_dead_dialog KEEP_WATCH, DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget): the player it last +// watched with a manual g.watch (Match.watchPref), else the first player still in (seat order, AI seats included — +// they are players of the remake). Round start: that player's prep board (m.field prep); 各自行动 (both combat modes), +// 最终攻势 / 隐秘核心: that player's field; a resync / a spectator joining in prep: the board again. The idea and most +// cases come from PR #189 by @2321Robin (watch preference), not merged; the shared-field rule of its review is solved +// with g.watch's optional `playerId` (the tapped player). The client side: battle/observe.js followedScout. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { PHASE } from '../../shared/constants.js'; +import { validateC2S } from '../../shared/protocol.js'; +import { makeMatch } from './harness.js'; +import { followedScout } from '../../public/js/battle/observe.js'; + +const S = 's_spec'; + +test('round start: an eliminated viewer scouts the board it last watched; a spectator without a pick the first player still in', () => { + const h = makeMatch({ mode: 'coop', humans: 2, seed: 1001, fake: true, spectators: [S] }).start(); + h.toPrep(1); + const m = h.m; + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:p_1' }), { ok: true }); + assert.equal(m.watchPref.get('p_0'), 'p_1', 'the manual scout is the preference'); + h.ps('p_0').eliminate(1); + h.toPrep(2); + assert.equal(m.watchers.get('p_0'), 'n:p_1', 'the eliminated viewer scouts its preference again'); + const meta = h.lastTo('p_0', 'm.field'); + assert.deepEqual([meta?.fieldId, meta?.prep], ['n:p_1', true], 'the prep board itself was pushed'); + assert.equal(m.watchPref.get('p_0'), 'p_1', 'the automatic assignment leaves the preference alone'); + assert.equal(m.watchers.get(S), 'n:p_1', 'the spectator (no pick) follows the first player still in: p_1 (p_0 is out)'); + assert.equal(h.lastTo(S, 'm.field')?.fieldId, 'n:p_1'); + m.dispose(); +}); + +test('the item: one human with AI teammates — eliminated, it follows an AI seat\'s board every round, never its own empty one', () => { + const h = makeMatch({ mode: 'coop', humans: 1, bots: 3, seed: 1010, fake: true }).start(); + h.toPrep(1); + const m = h.m; + h.ps('p_0').eliminate(1); + h.toPrep(2); + assert.equal(m.watchers.get('p_0'), 'n:ai_0', 'the first player still in'); + assert.equal(h.lastTo('p_0', 'm.field')?.fieldId, 'n:ai_0'); + // a row tapped: that AI seat from now on, in combat and at the next round start + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:ai_2' }), { ok: true }); + h.runToPhase(PHASE.COMBAT, 2); + assert.equal(m.watchers.get('p_0'), 'n:ai_2', 'its battle (server-run combat)'); + h.toPrep(3); + assert.equal(m.watchers.get('p_0'), 'n:ai_2', 'its board again at the round start'); + assert.equal(h.lastTo('p_0', 'm.field')?.fieldId, 'n:ai_2'); + m.dispose(); +}); + +test('a dead preference falls back to the first player still in; a followed player who quits mid-prep hands over at once', () => { + const h = makeMatch({ mode: 'coop', humans: 4, seed: 1002, fake: true }).start(); + h.toPrep(1); + const m = h.m; + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:p_3' }), { ok: true }); + h.ps('p_0').eliminate(1); + h.ps('p_3').eliminate(1); + h.toPrep(2); + assert.equal(m.watchers.get('p_0'), 'n:p_1', 'p_3 is gone → the first player still in (p_1)'); + // p_1 quits during the prep: whoever scouted it follows the next player still in at once + m.onLeave('p_1'); + assert.equal(m.watchers.get('p_0'), 'n:p_2'); + assert.deepEqual([h.lastTo('p_0', 'm.field')?.fieldId, h.lastTo('p_0', 'm.field')?.prep], ['n:p_2', true]); + m.dispose(); +}); + +test('server-run combat: the default watch starts an eliminated viewer on its preference; a mid-phase switch re-records it', () => { + const h = makeMatch({ mode: 'coop', humans: 3, seed: 1003, fake: true }).start(); + h.toPrep(1); + const m = h.m; + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:p_2' }), { ok: true }); + h.ps('p_0').eliminate(1); + h.runToPhase(PHASE.COMBAT, 1); + assert.equal(m.watchers.get('p_0'), 'n:p_2'); + assert.equal(m.watchers.get('p_1'), 'n:p_1', 'a fighting player stays on its own field'); + assert.equal(m.watchPref.get('p_0'), 'p_2', 'the combat assignment does not touch the preference'); + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:p_1' }), { ok: true }); + assert.equal(m.watchers.get('p_0'), 'n:p_1'); + assert.equal(m.watchPref.get('p_0'), 'p_1', 'the manual switch is the new preference'); + m.dispose(); +}); + +test('client combat: an eliminated human is started on the field of the player it last watched; a reconnect puts it back there', () => { + const h = makeMatch({ mode: 'coop', humans: 3, seed: 1004, fake: true, clientCombat: true }).start(); + h.toPrep(1); + const m = h.m; + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:p_2' }), { ok: true }); + h.ps('p_0').eliminate(1); + h.runToPhase(PHASE.COMBAT, 1); + let start = h.lastTo('p_0', 'b.start'); + assert.deepEqual([start?.watch, start?.fieldId], [true, 'n:p_2'], 'the preference\'s field, not the first seat\'s'); + m.onDisconnect('p_0'); + m.onReconnect('p_0'); + start = h.lastTo('p_0', 'b.start'); + assert.deepEqual([start?.watch, start?.fieldId], [true, 'n:p_2']); + m.dispose(); +}); + +test('Final Assault: an eliminated viewer is started on its preference\'s boss field; a shared field records the tapped player only', () => { + const h = makeMatch({ mode: 'coop', difficulty: 'FUNNY', humans: 4, seed: 54, fake: true, instant: false, script: (b) => (b.kind === 'boss' ? { bossDps: 1 } : {}) }).start(); + const m = h.m; + h.drive(() => m.phase === PHASE.PREP && m.round === 14); + h.ps('p_3').eliminate(13); + assert.deepEqual(m.handle('p_3', { t: 'g.watch', fieldId: 'n:p_2' }), { ok: true }, 'records the preference in prep'); + h.drive(() => m.phase === PHASE.FINAL_ASSAULT); + assert.deepEqual(m.fields.map((f) => [f.fieldId, f.players]), [['b1', ['p_0', 'p_1']], ['b2', ['p_2']]]); + assert.equal(m.watchers.get('p_3'), 'b2', 'follows p_2 into b2 without a tap'); + assert.equal(m.watchers.get('p_0'), 'b1', 'a fighting player stays on its own boss field'); + // PR #189's review: a shared field's id does not say who was tapped — without `playerId` nothing is recorded + assert.deepEqual(m.handle('p_3', { t: 'g.watch', fieldId: 'b1' }), { ok: true }); + assert.equal(m.watchPref.get('p_3'), 'p_2'); + // with it (the client sends the row tapped), that player is the preference; a player not on the field is not + assert.deepEqual(m.handle('p_3', { t: 'g.watch', fieldId: 'b1', playerId: 'p_1' }), { ok: true }); + assert.equal(m.watchPref.get('p_3'), 'p_1'); + assert.deepEqual(m.handle('p_3', { t: 'g.watch', fieldId: 'b1', playerId: 'p_2' }), { ok: true }); + assert.equal(m.watchPref.get('p_3'), 'p_1', 'p_2 is not on b1'); + m.dispose(); +}); + +test('a resync in prep sends the followed board: a spectator joining, an eliminated human reconnecting (client combat)', () => { + const h = makeMatch({ mode: 'coop', humans: 2, seed: 1006, fake: true, clientCombat: true }).start(); + h.toPrep(1); + const m = h.m; + m.addSpectator(S); + let meta = h.lastTo(S, 'm.field'); + assert.deepEqual([meta?.fieldId, meta?.prep], ['n:p_0', true], 'no pick → the first player still in'); + assert.equal(m.watchers.get(S), 'n:p_0', 'registered as a scout: later board changes push again'); + assert.deepEqual(m.handle('p_0', { t: 'g.watch', fieldId: 'n:p_1' }), { ok: true }); + h.ps('p_0').eliminate(1); + m.onDisconnect('p_0'); + const n = h.allTo('p_0', 'm.field').length; + m.onReconnect('p_0'); + assert.ok(h.allTo('p_0', 'm.field').length > n, 'the reconnect pushed a board'); + meta = h.lastTo('p_0', 'm.field'); + assert.deepEqual([meta?.fieldId, meta?.prep], ['n:p_1', true], 'the preference survived the disconnect'); + m.dispose(); +}); + +test('g.watch\'s optional playerId passes the protocol check; junk does not', () => { + assert.equal(validateC2S({ t: 'g.watch', fieldId: 'b1' }), null); + assert.equal(validateC2S({ t: 'g.watch', fieldId: 'b1', playerId: 'p_1' }), null); + assert.notEqual(validateC2S({ t: 'g.watch', fieldId: 'b1', playerId: 7 }), null); +}); + +test('client: followedScout adopts the server\'s prep board for an eliminated player / spectator seat only, from home', () => { + const scout = { t: 'm.field', fieldId: 'n:p_1', prep: true }; + assert.equal(followedScout({ field: scout, watching: null, alive: false, myId: 'p_0' }), 'n:p_1'); + assert.equal(followedScout({ field: scout, watching: null, alive: false, spectator: true, myId: 's_x' }), 'n:p_1'); + assert.equal(followedScout({ field: scout, watching: null, alive: true, myId: 'p_0' }), null, 'a living player keeps its own board'); + assert.equal(followedScout({ field: scout, watching: 'n:p_2', alive: false, myId: 'p_0' }), null, 'already watching'); + assert.equal(followedScout({ field: { ...scout, prep: false }, alive: false, myId: 'p_0' }), null, 'a battle field'); + assert.equal(followedScout({ field: { ...scout, fieldId: 'n:p_0' }, alive: false, myId: 'p_0' }), null, 'its own board'); + assert.equal(followedScout({ field: null, alive: false, myId: 'p_0' }), null); +}); diff --git a/test/match/spectator.test.js b/test/match/spectator.test.js index 2e417f88..8f3aa797 100644 --- a/test/match/spectator.test.js +++ b/test/match/spectator.test.js @@ -1,7 +1,8 @@ // Spectator seats, the match side (community report #26, owner's decision 2026-10-04 — a remake feature: the official // room has no spectator seat). A spectator (opts.spectators / addSpectator, server/lobby.js spectate) is shown fields like -// an ELIMINATED player in every phase — the first field of each battle (b.start watch), any field on g.watch, the 联防 -// spec, the first boss field, a prep board on g.watch 'n:' — and the settlement; it never receives an m.private +// an ELIMINATED player in every phase — the field of the player it follows in each battle (b.start watch: the one it last +// watched, else the first), any field on g.watch, the 联防 spec, that player's boss field, a prep board on g.watch +// 'n:' or of the player it follows — and the settlement; it never receives an m.private // (shop, hand, funds …), an m.toast or m.unitStats, is never a field's player or authority, and every intent but g.watch // is refused (SPECTATOR). The platform half (seats, cap, host removal, reconnect): test/lobby.test.js; over sockets with // the real Match: test/match/lobby-integration.test.js. @@ -65,8 +66,9 @@ test('a spectator seat watches a whole match (各自行动, 联防, 最终攻势 } const kinds = new Set(starts.map((x) => x.kind)); for (const k of ['normal', 'unite', 'boss']) assert.ok(kinds.has(k), `a ${k} field was shown (${[...kinds]})`); - // what it was shown in 各自行动 is what an eliminated player is shown: the first field of the phase - for (const st of starts.filter((x) => x.kind === 'normal')) assert.equal(st.fieldId, 'n:p_0'); + // what it was shown in 各自行动 is what an eliminated player is shown: the field of the player it follows — p_1, the one + // it scouted itself in prep (a manual watch is the preference: item 56 of 2026-10-06); with no pick the first field + for (const st of starts.filter((x) => x.kind === 'normal')) assert.equal(st.fieldId, 'n:p_1'); // the Final Assault's end reaches the boss field it watched, like every human shown that field const ends = h.allTo(S, 'b.end'); assert.ok(ends.some((e) => e.fieldId === starts.filter((x) => x.kind === 'boss').pop().fieldId), 'b.end of the watched boss field'); From f14f03a59f92ccdc55647ec3b4f0c9d923b61169 Mon Sep 17 00:00:00 2001 From: sganggs Date: Wed, 7 Oct 2026 02:11:25 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feedback5=20CB5:=20in=20a=20boss=20round's?= =?UTF-8?q?=20prep=20a=20watcher=20sees=20the=20boss=20field=20=E2=80=94?= =?UTF-8?q?=20the=20scouted=20player's=20half=20(mirrored=20on=20the=20rig?= =?UTF-8?q?ht)=20with=20the=20leader=20standing=20at=20its=20spawn=20?= =?UTF-8?q?=E2=80=94=20instead=20of=20the=20normal=20board=20without=20a?= =?UTF-8?q?=20boss=20(community=20report=20of=202026-10-06,=20item=2055)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reproduced in the browser (notes i55-repro, an eliminated guest at the boss round): in the Final Assault fight the watched boss field shows the leader (enemy_9013_acstmk at 10,3), but in the boss round's prep the eliminated player saw its own empty normal board (camera 'prep', no leader), and a scout of a teammate was the normal board too, while the players prepare on their half of the boss field with the leader standing there. Match.prepFieldMeta: in a boss round's prep the scout is built on the boss field (kind 'boss', BOSS_RECT, units through finalAssault.js bossFieldPlacement: rows − 7, the right half mirrored with RIGHT ↔ LEFT, the player's side; nextEnemies keeps the leader's spawn tile); game.js frames it with the boss-field prep camera of that side, which shows the leader (render/app.js leaderShown). With item 56 an eliminated viewer gets it at the round start without a tap. After the fix the same run: guest field kind 'boss', camera bossPrep, leader shown. Golden unchanged. Tests: test/match/feedback5-boss-prep-scout.test.js (new, 2). Docs: design/network.md, design/client.md (§15). --- docs/design/client.md | 2 +- docs/design/network.md | 2 +- public/js/screens/game.js | 5 +- server/match/match/views.js | 24 ++++++- test/match/feedback5-boss-prep-scout.test.js | 76 ++++++++++++++++++++ 5 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 test/match/feedback5-boss-prep-scout.test.js diff --git a/docs/design/client.md b/docs/design/client.md index e4587ed4..bad024d8 100644 --- a/docs/design/client.md +++ b/docs/design/client.md @@ -72,6 +72,6 @@ Target: a **real 3D board layer** rendered with three.js (vendored ESM, no CDN) - One camera model drives both layers: `public/js/render/projection.js` keeps its public API (`worldToScreen`, `screenToWorld`/tile picking, presets, transitions) but derives it from a three.js `PerspectiveCamera` initialised from the official configBlackBoard framing, so Spine units, FX, drag/drop and hit-testing stay aligned with the 3D board. - Must degrade gracefully: when local-client art or WebGL2 features are missing, fall back to the current 2D atlas board; keep 60 fps (static geometry merged into few draw calls; textures mipmapped; DPR-aware). - Implemented (`public/js/render/board3d/*`, `render/app.js`): three.js is downloaded only when the local-art manifest lists the board atlas; the built 3D area, the drawn 2D rows and the lit rect follow `viewKind(kind, {rect})` (the field the camera actually shows: a 'prep' camera on rows ≤ 6 = `bossPrep`); the background plane is extended with mirrored copies and the clear colour matches the fog (no visible edge at any aspect); battle crates live in their own group (survive scene rebuilds) and follow the board mode (`switchableBox`: 3D mesh ⇄ 2D box on a mode switch or a lost WebGL context); quality 'low' drops shadows; `?board=2d|3d` overrides. The direction wheel's range stripes sit between the board canvas and the unit canvas. -- Done: Final Assault / Hidden Core prep takes place on your own half of the boss field (research 09 §1.2): pieces shown at boss-field positions (row − 7; right-hand player mirrored col → 20 − col, RIGHT ↔ LEFT), drop targets and wheel directions mapped back to board coordinates (`prepCamera` → `bossPrep` in public/js/ui/gameLogic.js; covered by the playtest2 browser tests). +- Done: Final Assault / Hidden Core prep takes place on your own half of the boss field (research 09 §1.2): pieces shown at boss-field positions (row − 7; right-hand player mirrored col → 20 − col, RIGHT ↔ LEFT), drop targets and wheel directions mapped back to board coordinates (`prepCamera` → `bossPrep` in public/js/ui/gameLogic.js; covered by the playtest2 browser tests). A scout of such a board (`m.field` prep of a boss round) comes in boss-field coordinates with the player's `side` and is framed the same way, the leader standing at its spawn (0.2.0, community report item 55). --- diff --git a/docs/design/network.md b/docs/design/network.md index 23af0a6c..37fad38e 100644 --- a/docs/design/network.md +++ b/docs/design/network.md @@ -89,7 +89,7 @@ Piece = { uid, kind: 'chess'|'item'|'token', id, golden: bool, tier, items: [ { **Solo pause (official `PauseUp` / `ResumeUp`; `Match.setPause`).** C→S `g.pause { on }`: solo matches only (co-op → `WRONG_PHASE 'co-op battles never pause'`), and `on: true` only while a battle runs (COMBAT / FINAL_ASSAULT / HIDDEN_CORE with a live field, not once the Final Assault is ending — else `WRONG_PHASE 'no battle running'`); `on: false` is always accepted. While `m.public.paused` is true everything of the battle is frozen: the field clock (`b.start elapsed`, the boss plausibility budgets and the overtime drain), the authority deadline and the server-run release timers, the boss clock, the HUD `deadline` / `overtimeAt` (shifted by the paused time on resume) and the server-run pacers (FieldRunner / HeadlessPacer skip their intervals); the browser's runner stops its local clock. In solo battles the top bar shows a 暂停 / 继续作战 button (`ui/hud.js PauseButton`, `ui/matchStatus.js pauseAvailable`; `Space` toggles it), a paused overlay offers 继续作战 / 放弃模拟, and the HUD clocks freeze at the pause moment. A disconnect or leave resumes (the server takes the field over — nobody is left to resume it); the battle phase ending drops the pause. -**Spectating (research 09 §3.1 / §6.3).** No live spectating while your own normal battle runs (`g.watch` → `WRONG_PHASE 'own battle running'`; the UI toasts "作战中无法查看队友"). After your own battle ends you see "⌛ 作战结束,等待队友完成作战" with each teammate's live progress (`m.public.fields[].progress`); tapping a teammate's avatar expands **前往查看** → `g.watch` → `b.start` (display replica fast-forwarded to the field clock); the own row shows **返回战场**. Prep: the same flow shows the teammate's board (`m.field` with `prep: true`) and sends it again when that board changes (GitHub #87). The scout is framed by the prep camera in its shop-folded form and renders the teammate’s hand (整备区) and 临时整备区 as units on their rows — the own prep bench’s exact look (user playtest #2 item 1, the GitHub #44 suggestion; the spectator seat's scout view is the same, §23.19's never-a-private-view staying the no-`m.private` rule). While scouting, the 本局信息 drawer shows the watched player's 策略 in place of one's own (`game.js scoutPid` → `enemyDrawer.js InfoTab bandId / bandOwner`, labelled "X 的策略"; user playtest #2 item 2), and the right effects column follows the watched field too: the scout meta carries the player's effects view, a display-replica battle derives it from the spec's `playerEffects` (`battle/observe.js spectateEffects`) — undefined (empty column, never one's own) for 联防 / boss pair fields and server-run watched fields. 联防: every human receives the unite spec (helpers + observers). Final Assault: each human sees its own pair field; the other pair → `BAD_TARGET 'other group hidden'`. In 联防/FA fields the ‹ › pill switches the camera LEFT half / 全景 / RIGHT half ("你自己" / "👁 name" / "全景" / "无人在家"); there is **no** view switcher during normal combat. Eliminated players and spectator seats follow a player through every phase reset (0.2.0, community report item 56; the official DeadAutoObDn { obIndex, state, preparation }; the idea of PR #189): the player they last watched with a manual `g.watch` (`Match.watchPref`; a shared field records the `playerId` tapped, else nobody), else the first player still in (seat order, AI seats included) — the round start pushes that player's prep board (`Match._followScout`, adopted by the screen like a 前往查看 tap: `battle/observe.js followedScout`; kept through 机变 and 休整期), each battle starts on that player's field, a resync / a spectator joining in prep gets the board again; they may watch any field. +**Spectating (research 09 §3.1 / §6.3).** No live spectating while your own normal battle runs (`g.watch` → `WRONG_PHASE 'own battle running'`; the UI toasts "作战中无法查看队友"). After your own battle ends you see "⌛ 作战结束,等待队友完成作战" with each teammate's live progress (`m.public.fields[].progress`); tapping a teammate's avatar expands **前往查看** → `g.watch` → `b.start` (display replica fast-forwarded to the field clock); the own row shows **返回战场**. Prep: the same flow shows the teammate's board (`m.field` with `prep: true`) and sends it again when that board changes (GitHub #87). In a boss round's prep that board is built on the boss field — the player's half (rows − 7, the right half mirrored with RIGHT ↔ LEFT, `server/match/finalAssault.js bossFieldPlacement`), `kind: 'boss'`, `side`, the leader in `nextEnemies` at its spawn tile — and framed by the boss-field prep camera of that side, so a watcher sees the leader as the players do (0.2.0, community report item 55). The scout is framed by the prep camera in its shop-folded form and renders the teammate’s hand (整备区) and 临时整备区 as units on their rows — the own prep bench’s exact look (user playtest #2 item 1, the GitHub #44 suggestion; the spectator seat's scout view is the same, §23.19's never-a-private-view staying the no-`m.private` rule). While scouting, the 本局信息 drawer shows the watched player's 策略 in place of one's own (`game.js scoutPid` → `enemyDrawer.js InfoTab bandId / bandOwner`, labelled "X 的策略"; user playtest #2 item 2), and the right effects column follows the watched field too: the scout meta carries the player's effects view, a display-replica battle derives it from the spec's `playerEffects` (`battle/observe.js spectateEffects`) — undefined (empty column, never one's own) for 联防 / boss pair fields and server-run watched fields. 联防: every human receives the unite spec (helpers + observers). Final Assault: each human sees its own pair field; the other pair → `BAD_TARGET 'other group hidden'`. In 联防/FA fields the ‹ › pill switches the camera LEFT half / 全景 / RIGHT half ("你自己" / "👁 name" / "全景" / "无人在家"); there is **no** view switcher during normal combat. Eliminated players and spectator seats follow a player through every phase reset (0.2.0, community report item 56; the official DeadAutoObDn { obIndex, state, preparation }; the idea of PR #189): the player they last watched with a manual `g.watch` (`Match.watchPref`; a shared field records the `playerId` tapped, else nobody), else the first player still in (seat order, AI seats included) — the round start pushes that player's prep board (`Match._followScout`, adopted by the screen like a 前往查看 tap: `battle/observe.js followedScout`; kept through 机变 and 休整期), each battle starts on that player's field, a resync / a spectator joining in prep gets the board again; they may watch any field. **Client runner.** `public/js/battle/runner.js` owns the local Battles: fixed-step clock at the battle speed (60 ticks per real second at 2×, at most `max(8, 4·speed)` ticks per frame, bounded fast-forward of ≤ 240 ticks per frame when far behind; a new battle is caught up silently before it is shown), feeds the existing renderer through the same `snapshot()`/`drainEvents()` wire formats every frame (`view.setLocalFeed` shrinks the interpolation buffer to ~2 frames), publishes the field meta (m.field shape + `local`, `battleId`, `players`, `sides`, `speed`) into `store.match.field` and its state into `store.match.battle` (with `leaks: { [fieldId]: counted leaks so far }` of every normal field it simulates and, while the 联防 field is on screen, `uniteLeft` per leaker (§20.6), authoritative or display replica — the settle rule's count, `/sim/spec.js battleProgress`; published in the frame a count changes: the live LP of §17.5), sends `b.progress`/`b.result` when authoritative (results retried once on timeout; a result lost with the socket — the request failed `DISCONNECTED` / `OFFLINE` or timed out twice — is kept and sent again when the session is back online and whenever a `b.start` still names that finished battle `authoritative: true`; the server answers a duplicate `ok` and changes nothing; a server refusal is final), follows `m.public.paused` (solo pause: every local clock stands still, resume shifts it by the paused time), handles `b.pool`/`b.end`, fast-forwards on reconnect/observe, and survives tab backgrounding (a 250 ms interval pump keeps authoritative battles on their clock while hidden; the server deadline + takeover cover extreme cases). Catch-up frames keep the state events and every enemy form fx (`keepsState`); while hidden, the battle on screen holds them for the first frame back (`hold`; past `HELD_MAX` the superseded status / skill toggles are dropped, `compactHeld`, and only a backlog still past it re-enters the view from the field meta), and a battle that ended while hidden delivers them when the tab is shown; a catch-up frame or a hidden step drains its events every `EV_SLICE` (15) ticks, each batch stamped with its own game time (`gt`), so a late form fx reaches the render engine as late as it is; the game screen's pre-entry buffer (`keepEarly`, also while a re-sent field meta re-enters the field on screen) and the render engine's event queue (`render/interp.js isCosmeticEvent`) never drop a form fx either (§21.4). Sim content errors are logged as console warnings (the server logs its own). The sim loader fetches the ten data files (one retry each) and fails when any is missing — a battle on partial data would report a plausible wrong result — so the next `b.start` retries and the server's deadline covers the field meanwhile. `public/js/battle/observe.js` holds the observing rules and labels. diff --git a/public/js/screens/game.js b/public/js/screens/game.js index 4450d41d..a0526d2d 100644 --- a/public/js/screens/game.js +++ b/public/js/screens/game.js @@ -400,7 +400,10 @@ function MatchScreen() { const pf = (Array.isArray(pub?.fields) ? pub.fields : []).find((f) => f && f.fieldId === field.fieldId); const members = Array.isArray(pf?.players) ? pf.players : Array.isArray(field.players) ? field.players : []; const sides = field.sides && typeof field.sides === 'object' ? field.sides : null; - const side = sides && sides[myId] ? sides[myId] : members.length > 1 && members.indexOf(myId) === 1 ? 'R' : 'L'; + // a scouted board of a boss round's prep (Match.prepFieldMeta: the boss-field rows, `side` = the scouted player's + // half) is framed by the boss-field prep camera of that half — the leader standing at its spawn (item 55) + const side = field.prep && (field.side === 'L' || field.side === 'R') ? field.side + : sides && sides[myId] ? sides[myId] : members.length > 1 && members.indexOf(myId) === 1 ? 'R' : 'L'; // local simulation (client-side combat) feeds a frame per animation frame: no network jitter buffer view.raw?.setLocalFeed?.({ on: !!field.local, speed: field.speed }); setLayer('ALL'); diff --git a/server/match/match/views.js b/server/match/match/views.js index f14bf08d..f1e0b552 100644 --- a/server/match/match/views.js +++ b/server/match/match/views.js @@ -1,6 +1,7 @@ // server/match/match/views.js — Match methods: the state builders — m.public (publicView with statusOf / fieldOf, the // fields' progress and the teammates' live pendingLp / uniteLeft), the nextEnemies preview of m.private and the prep -// scout's m.field (prepFieldMeta: board, hand and temp as units, the scouted player's effects and coming enemies). +// scout's m.field (prepFieldMeta: board, hand and temp as units, the scouted player's effects and coming enemies; in a +// boss round's prep on the player's half of the boss field). // Installed on Match.prototype by server/match/Match.js (a method container: never instantiated; `this` is the match). import { PHASE, GEO } from '../../../shared/constants.js'; @@ -9,6 +10,7 @@ import { bondList, offBondCounts } from '../bondsMeta.js'; import { cardView } from '../choices.js'; import { bountySpawns, previewOf } from '../waves.js'; import { timelineAt } from '../fields.js'; +import { bossFieldPlacement } from '../finalAssault.js'; import { battleProgress } from '../../sim/spec.js'; export class MatchViews { @@ -253,6 +255,24 @@ export class MatchViews { try { nextEnemies = this.nextEnemiesFor(ps); } catch (e) { this.reportError('nextEnemies', e); } // the scouted player's effects column (策略 / 机变 / 悬赏 …), display-ready (user playtest #2: while scouting, the // right column shows the watched player's effects, not one's own) - return { t: 'm.field', fieldId: `n:${ps.playerId}`, kind: 'normal', rect: { ...GEO.NORMAL_RECT }, stageId: this.stageId, units, effects: ps.effectsView(), prep: true, nextEnemies }; + const meta = { t: 'm.field', fieldId: `n:${ps.playerId}`, kind: 'normal', rect: { ...GEO.NORMAL_RECT }, stageId: this.stageId, units, effects: ps.effectsView(), prep: true, nextEnemies }; + // 最终攻势 / 隐秘核心 prep: the pieces stand on the player's half of the boss field — the scout shows them there (rows + // − 7, the right half mirrored with RIGHT ↔ LEFT: finalAssault.js bossFieldPlacement, as the own prep view), with the + // round's leader at its spawn tile (nextEnemies `start`), framed by the boss-field prep camera of the player's side + // (`side`). Until 0.2.0 an eliminated player or a spectator seat scouting a player then saw the normal board and no + // leader at all (community report of 2026-10-06, item 55). + const g = this.fields.length ? null : this.bossGroupOf(ps); + if (g) return { ...meta, kind: 'boss', rect: { ...GEO.BOSS_RECT }, side: g.side, units: units.map((u) => this._onBossHalf(u, g.side)) }; + return meta; + } + + /** A prep UnitInfo (board, bench or temp row) placed on side 'L' | 'R' of the boss field (bossFieldPlacement). */ + _onBossHalf(u, side) { + const p = bossFieldPlacement(side, u.y, u.x, u.dir || 'RIGHT'); + const v = { ...u, x: p.col, y: p.row }; + // a board piece keeps facing the same way relative to the leader (mirrored on the right half); a bench piece has no + // stored facing (it faces right, as the own prep bench draws it) + if (u.dir) { v.dir = p.dir; v.facing = p.dir === 'LEFT' ? -1 : 1; } + return v; } } diff --git a/test/match/feedback5-boss-prep-scout.test.js b/test/match/feedback5-boss-prep-scout.test.js new file mode 100644 index 00000000..b9b60bc1 --- /dev/null +++ b/test/match/feedback5-boss-prep-scout.test.js @@ -0,0 +1,76 @@ +// Community report of 2026-10-06, item 55: 「死亡后观战视角看不到场上的关底boss」. Reproduced in the browser (an eliminated +// guest at the boss round, notes i55-repro): in the Final Assault fight the watched boss field shows the leader, but in +// the boss round's prep the eliminated player saw its own empty normal board — and a scout of a teammate (m.field +// prep) was the normal board too, while the players themselves prepare on their half of the boss field with the leader +// standing at its spawn (render/app.js setLeader, only on the boss-field prep camera). Now the scout of a boss round's +// prep is built on the boss field: the scouted player's pieces on its half (bossFieldPlacement: rows − 7, the right +// half mirrored, RIGHT ↔ LEFT), `side` for the camera, the leader in nextEnemies with its spawn tile; with item 56 an +// eliminated viewer is pushed that scout at the round start without a tap. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { GEO, PHASE } from '../../shared/constants.js'; +import { makeMatch, give, legalTileFor, chessOfTier } from './harness.js'; +import { bossFieldPlacement } from '../../server/match/finalAssault.js'; +import { viewKind, leaderShown } from '../../public/js/render/app/view.js'; +import { leaderStand } from '../../public/js/render/prepfield.js'; + +function bossPrep(seed) { + const h = makeMatch({ mode: 'coop', difficulty: 'FUNNY', humans: 4, seed, fake: true }).start(); + const m = h.m; + h.drive(() => m.phase === PHASE.PREP && m.round === m.gd.bossRound, { ready: true }); + return h; +} + +test('a scout of a boss round\'s prep is the boss field: the player\'s half (mirrored on the right), its side, the leader\'s spawn', () => { + const h = bossPrep(55); + const m = h.m; + assert.deepEqual(m.bossWaves.map((w) => w.players), [['p_0', 'p_1'], ['p_2', 'p_3']]); + const p1 = h.ps('p_1'); + // a board piece facing RIGHT and one in the hand + for (const k of [...p1.board.keys()]) p1.board.delete(k); + const id = chessOfTier(1)[0]; + const tile = legalTileFor(m, p1, id); + const onBoard = give(m, p1, id, 'board', tile); + onBoard.dir = 'RIGHT'; + give(m, p1, chessOfTier(1)[1], 'hand', 2); + h.ps('p_3').eliminate(13); + assert.deepEqual(m.handle('p_3', { t: 'g.watch', fieldId: 'n:p_1' }), { ok: true }); + const meta = h.lastTo('p_3', 'm.field'); + assert.deepEqual([meta.fieldId, meta.prep, meta.kind, meta.side], ['n:p_1', true, 'boss', 'R']); + assert.deepEqual(meta.rect, GEO.BOSS_RECT); + const u = meta.units.find((x) => x.uid === onBoard.uid); + const want = bossFieldPlacement('R', tile[0], tile[1], 'RIGHT'); + assert.deepEqual([u.y, u.x, u.dir, u.facing], [want.row, want.col, 'LEFT', -1], 'mirrored onto the right half, still facing the leader'); + const bench = meta.units.find((x) => x.y === 0); + assert.ok(bench && bench.x === 20 - 2, `the hand row on the half's bench row (boss row 0, mirrored): ${JSON.stringify(bench)}`); + for (const x of meta.units) assert.ok(x.y >= 0 && x.y <= 5 && x.x >= 10 && x.x <= 20, `on the right half: ${x.y},${x.x}`); + // the leader stands at its spawn tile: the client's leaderStand finds it in the scout's nextEnemies + const stand = leaderStand(meta.nextEnemies, () => null, (x, y) => [[y, x]]); + assert.ok(stand && stand.entry.boss && stand.row >= 0 && stand.row <= 5, `the leader with its spawn tile: ${JSON.stringify(stand?.entry)}`); + assert.deepEqual(meta.nextEnemies, m.nextEnemiesFor(p1), 'the scouted player\'s own preview (its pair\'s wave)'); + // the client frames a scout with that rect on the boss-field prep camera, which shows the leader + assert.equal(viewKind('prep', { rect: meta.rect, side: meta.side }), 'bossPrep'); + assert.equal(leaderShown('bossPrep'), true); + // the left player of the pair: its own half, unmirrored + assert.deepEqual(m.handle('p_3', { t: 'g.watch', fieldId: 'n:p_0' }), { ok: true }); + const left = h.lastTo('p_3', 'm.field'); + assert.deepEqual([left.kind, left.side], ['boss', 'L']); + for (const x of left.units) assert.ok(x.x <= 10, 'the left half'); + m.dispose(); +}); + +test('the eliminated viewer is pushed the boss-field scout at the boss round\'s start (item 56); a normal round\'s scout stays the normal board', () => { + const h = makeMatch({ mode: 'coop', difficulty: 'FUNNY', humans: 3, seed: 56, fake: true }).start(); + const m = h.m; + h.toPrep(1); + h.ps('p_2').eliminate(1); + h.toPrep(2); + const normal = h.lastTo('p_2', 'm.field'); + assert.deepEqual([normal.fieldId, normal.kind, normal.prep, normal.side], ['n:p_0', 'normal', true, undefined]); + assert.deepEqual(normal.rect, GEO.NORMAL_RECT); + h.drive(() => m.phase === PHASE.PREP && m.round === m.gd.bossRound, { ready: true }); + const meta = h.lastTo('p_2', 'm.field'); + assert.deepEqual([meta.fieldId, meta.kind, meta.prep, meta.side], ['n:p_0', 'boss', true, 'L']); + assert.ok(leaderStand(meta.nextEnemies, () => null, (x, y) => [[y, x]]), 'the leader at its spawn'); + m.dispose(); +}); From ef101647739978e3847f240213d798e1a9a35d30 Mon Sep 17 00:00:00 2001 From: sganggs Date: Wed, 7 Oct 2026 02:29:49 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feedback5=20CB5:=20the=20boss=20round's=20p?= =?UTF-8?q?rep=20shows=20both=20players=20of=20a=20pair=20together=20?= =?UTF-8?q?=E2=80=94=20the=20partner's=20pieces=20on=20the=20other=20half,?= =?UTF-8?q?=20as=20the=20battle=20places=20them=20=E2=80=94=20and=20frames?= =?UTF-8?q?=20the=20pair's=20two=20avatars=20in=20the=20official=20green?= =?UTF-8?q?=20(community=20report=20of=202026-10-06,=20item=2051)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The own half was already the official one (the right-hand player on the right half with the right_boss_*_camera_param camera, its layout mirrored col c -> 20 - c with RIGHT <-> LEFT, so its operators face the leader as before: research 09 §1.2, DESIGN §15); the partner's half stood empty and nothing framed the pair. Server: in a boss round's prep m.private.bossMate { playerId, side, units } = the partner's board as UnitInfo on its half (Match.bossMateView, finalAssault.js bossFieldPlacement); markPrivate marks the partner's m.private with the mover's, so it follows live; a scout of either player (Match.prepFieldMeta) carries both halves (`mate`), and its signature covers the partner's board. Client: render/app.js syncMates draws the partner's units read-only ('m:' views: never picked, dragged or swept with the own pieces) and the whole boss field is lit; the team panel frames the pair's avatars (gameLogic teamFrameIds: the players still in by seat, two by two, as prepCamera — the boss field in the fight) from the round start through the Final Assault / Hidden Core, with the official ui/battle bg_team_border tinted the autochess green (#4ed8af; a plain ring without the local art); none for a lone player, an eliminated player, a spectator seat, or 联防. Overlap with PR #191 (same-field frames, issue #190; not merged): it published m.public bossPairing and framed the partner alone in gold, 联防 included, plus a banner naming the partner; here the pairing is read as the prep camera already does, both avatars get the official green frame, boss rounds only. [ASSUMED] the partner's bench is not shown; one frame per avatar. Golden unchanged. Tests: test/match/feedback5-boss-pair-prep.test.js (new, 4), the item 55 test now checks the scouted player's own pieces (the partner's stand on the other half); SP_E2E test/ui/feedback5-boss-pair.e2e.test.js (new, 2: both halves and the frames in prep and fight; item 55's eliminated guest on the boss field with the leader). Docs: design/client.md §10 / §15, design/network.md, PLAYING.md. --- docs/PLAYING.md | 1 + docs/design/client.md | 4 +- docs/design/network.md | 2 +- public/css/screens/game.css | 9 ++ public/i18n/en.json | 1 + public/js/render/app.js | 38 ++++- public/js/ui/gameLogic.js | 2 +- public/js/ui/gameLogic/camera.js | 33 ++++- public/js/ui/teamPanel.js | 15 +- server/match/match/messaging.js | 13 +- server/match/match/views.js | 62 ++++++-- server/match/match/watch.js | 15 +- server/match/player/views.js | 6 + test/match/feedback5-boss-pair-prep.test.js | 116 +++++++++++++++ test/match/feedback5-boss-prep-scout.test.js | 6 +- test/ui/feedback5-boss-pair.e2e.test.js | 146 +++++++++++++++++++ 16 files changed, 436 insertions(+), 33 deletions(-) create mode 100644 test/match/feedback5-boss-pair-prep.test.js create mode 100644 test/ui/feedback5-boss-pair.e2e.test.js diff --git a/docs/PLAYING.md b/docs/PLAYING.md index 43f280c5..f97754e9 100644 --- a/docs/PLAYING.md +++ b/docs/PLAYING.md @@ -118,6 +118,7 @@ - **最终攻势**(第 14 回合;独立标准为第 9 回合):按座位两两配对,每对共享一个战场,一人守左半边、一人守右半边(右侧玩家的棋盘左右镜像)。所有人**共同削减同一条领袖血条**(血量 = 该难度的官方领袖生命值 × 开战时存活的玩家人数:领袖生命值取自当前版本的游戏数据,例如终极「假想敌:胄」3 600 000,4 人存活时为 14 400 000、2 人为 7 200 000;AI 队友和掉线托管的玩家都算,已被淘汰或中途退出的不算;独立模拟按 1 人计。这是作者 2026-10-06 采纳社区 PR #209 的决定,取代之前「保持固定血量」的做法),看不到另一组的战场。全队 LP 合并成一个池,不再有每回合 10 点上限。顶部的 120 秒倒计时结束后战斗**继续**(不会超时判负);从开战第 150 秒起(实际秒数)全队每秒扣 1 点 LP。领袖血量归零即胜利(血条显示 0 时领袖已倒下,战斗立即结束),LP 归零即失败——以先归零的一方为准,LP 耗尽后打出的伤害不再计入。此阶段盟约不叠层。 - **限伤**(最终攻势与隐秘核心,与官方相同):对领袖的**单次伤害达到 300000 或以上时整次作废**——这一下造成 0 点伤害,不计入领袖血条,也不显示伤害数字;不是截到 300000,而是一点都不算(299999 照常生效)。只针对领袖本体(含两人战场里的另一个领袖),领袖的部件、护卫、无人机和其他小怪不受影响,普通回合和联防也不受影响;部件把伤害等量转给领袖时,这一下也按一次伤害检查;无人机死亡令领袖失去的 2% 血条不算一次伤害,不受限伤影响(隐秘核心终极「假想敌:胄」3 或 4 人存活时每架为 43.2 万 / 57.6 万,按限伤会整次作废,本作仍然计入,属推断)。层数极高时领袖仍可能几秒内倒下(例如每名玩家的两个盟约都在 999 层,或所有激活的盟约都在 999 层),这来自官方按层数计算、没有上限的伤害倍率(谢拉格、奥术、精准等),官方社区也有「boss一秒死」的说法。 +- 最终攻势 / 隐秘核心的休整期和战斗中一样,**同组两人在同一个关底战场上**:你在自己的半场摆位(右侧玩家看到自己在右边,摆位左右镜像,干员相对领袖的朝向不变),队友当前的阵容显示在另一半场(只能看,随他调整实时更新);左侧成员栏里同组两人的头像带**绿框**,从回合开始一直到战斗结束。观战者查看其中一人时也会看到两人的阵容和站在关底的领袖。 - 最终攻势 / 隐秘核心的休整期按你在**关底战场上的半场**判定可部署地块(右侧玩家左右镜像),拖动时高亮的就是可放置的格子:例如「源石流发生装置」关底图中被 1×3 橙色管子围起的地块可以部署。在最终攻势放在这些格子上的干员进入隐秘核心时会留在原位。 - **领袖的部件**:「假想敌:胄」的无人机死亡时(不论是谁击落;漏过去不算),胄失去领袖血条总量的 2%(例如终极 4 人存活 14 400 000 → 每架 288 000;官方没写明这个「最大生命值」指哪一个,本作按血条总量处理,属推断);隐秘核心的「斩胄之剑 / 破胄之锤」「碎铳之簧」受到的每一点伤害都会等量扣在领袖血条上(剑 / 锤被击落后一定算;俯冲途中挨的打也算,这一点官方两处文字不一致,属推断)。剑 / 锤平时悬浮在胄身边、无敌;掷出后闪现到固定路线,冲向攻击力最低的干员,途中被打中 15 次就会【瘫痪】(晕眩 10 秒、落地、受到的伤害 ×1.3);落地或瘫痪结束后,会以剩余生命值换成一把新的回到原位。「灭顶之灾」每次只发射 1 枚刺胄之弹(低于 20% 生命也一样),只打攻击范围内攻击力最高的干员。 - **巨型领袖**(假想敌:胄 / 管、盐风主教昆图斯、阿利斯泰尔、“萨米的意志”)的受击判定是一片约 5×3 格的区域(整个管道区块):攻击范围碰到其中任意一格就能打到它;它们**自缚、无法被阻挡**,不会离开原地。溅射按敌人的中心判定,打在旁边小怪身上的溅射碰不到巨型领袖。 diff --git a/docs/design/client.md b/docs/design/client.md index bad024d8..1466b9be 100644 --- a/docs/design/client.md +++ b/docs/design/client.md @@ -37,7 +37,7 @@ Visuals: oblique perspective board (tilted ~30°, trapezoid tiles), raised highl Look & feel: research 06 §11–§12 (near-black green-grey panels, mint `#4ed8af` accents, gold prices, bracket frames, hexagon badges, bond discs, 7-segment countdown). Fonts: Noto Sans SC (Google Fonts with local fallback), Bender/Novecento self-hosted for numerals and Latin micro-labels. -Screens: **Title** (season art backdrop, 开始 → nickname), **Lobby** (独立模拟 / 同盟模拟 → create or join; recent rooms), **Room** (4 seats, invite code + copy link, difficulty picker (host), add/remove AI, ready, start), **Briefing** (INFO_CHECK: stage, factions, disabled bonds, banned chess, boss silhouette, ready x/4), **Band draft** (grid of 40 bands with icon/name/LP/effect; turn order list; skip; 查看禁用盟约与干员 under the order list opens the briefing's bond rows, legend and banned operators again in a read-only dialog — one component, `ui/matchInfo.js`, §21.28), **Game** (Pixi field + DOM HUD: top bar with round/phase capsule/LP (live during the own battle, §17.5)/timer/ready; bond strip + popups; left team panel with LP/status/emote bubbles, click to watch; bottom shop bar (level card, 3–5 chess cards, item card, funds, refresh, freeze, collapse); detail panel on click/hover (header with bonds, then 特质 — the operator's own effect — right under it, 特性, stats (live: the battle's own numbers / the start-of-battle ones in prep, §18.5), skill, module, equipped items, talents: §17.5); enemy preview drawer (next round enemies with counts, factions; its 本局信息 tab lists the briefing's disabled bonds and banned operators, §21.28); 机变 overlay (the official card: framed icon + name, the effect text — data `descRaw` — under it, the taker's round avatar at the top-right; two taps, §18.2, §20.7); merge reward overlay (the promotion reward, or a special offer — 凯瑟琳's 定向投放 … — as item / chess cards under its source's label, with the count of offers queued behind it, §21.2); phase banners; ticker (by broadcast priority, highest first; a leader-damage line plays only in its round and a player's newer one replaces the older, §21.10); toasts; emote wheel; settings (volume, quality, show damage numbers)), **Result** (victory/defeat, rounds, per-player lineup, stats, titles, back to room). +Screens: **Title** (season art backdrop, 开始 → nickname), **Lobby** (独立模拟 / 同盟模拟 → create or join; recent rooms), **Room** (4 seats, invite code + copy link, difficulty picker (host), add/remove AI, ready, start), **Briefing** (INFO_CHECK: stage, factions, disabled bonds, banned chess, boss silhouette, ready x/4), **Band draft** (grid of 40 bands with icon/name/LP/effect; turn order list; skip; 查看禁用盟约与干员 under the order list opens the briefing's bond rows, legend and banned operators again in a read-only dialog — one component, `ui/matchInfo.js`, §21.28), **Game** (Pixi field + DOM HUD: top bar with round/phase capsule/LP (live during the own battle, §17.5)/timer/ready; bond strip + popups; left team panel with LP/status/emote bubbles, click to watch (a boss round frames the viewer's pair in green, §15); bottom shop bar (level card, 3–5 chess cards, item card, funds, refresh, freeze, collapse); detail panel on click/hover (header with bonds, then 特质 — the operator's own effect — right under it, 特性, stats (live: the battle's own numbers / the start-of-battle ones in prep, §18.5), skill, module, equipped items, talents: §17.5); enemy preview drawer (next round enemies with counts, factions; its 本局信息 tab lists the briefing's disabled bonds and banned operators, §21.28); 机变 overlay (the official card: framed icon + name, the effect text — data `descRaw` — under it, the taker's round avatar at the top-right; two taps, §18.2, §20.7); merge reward overlay (the promotion reward, or a special offer — 凯瑟琳's 定向投放 … — as item / chess cards under its source's label, with the count of offers queued behind it, §21.2); phase banners; ticker (by broadcast priority, highest first; a leader-damage line plays only in its round and a player's newer one replaces the older, §21.10); toasts; emote wheel; settings (volume, quality, show damage numbers)), **Result** (victory/defeat, rounds, per-player lineup, stats, titles, back to room). 盟约栏支持「收起/展开盟约」(GitHub #142):收起时隐藏完整列表并关闭由列表打开的浮层,让原图标区域的指针事件到达棋盘,只保留至少 44×44 CSS px 的恢复按钮。折叠状态在本局阶段变化、观看队友时保留;展开后仍按当前观看对象显示最新盟约。按钮支持 Enter / Space,聚焦按钮时 Space 不触发准备或暂停。 @@ -72,6 +72,6 @@ Target: a **real 3D board layer** rendered with three.js (vendored ESM, no CDN) - One camera model drives both layers: `public/js/render/projection.js` keeps its public API (`worldToScreen`, `screenToWorld`/tile picking, presets, transitions) but derives it from a three.js `PerspectiveCamera` initialised from the official configBlackBoard framing, so Spine units, FX, drag/drop and hit-testing stay aligned with the 3D board. - Must degrade gracefully: when local-client art or WebGL2 features are missing, fall back to the current 2D atlas board; keep 60 fps (static geometry merged into few draw calls; textures mipmapped; DPR-aware). - Implemented (`public/js/render/board3d/*`, `render/app.js`): three.js is downloaded only when the local-art manifest lists the board atlas; the built 3D area, the drawn 2D rows and the lit rect follow `viewKind(kind, {rect})` (the field the camera actually shows: a 'prep' camera on rows ≤ 6 = `bossPrep`); the background plane is extended with mirrored copies and the clear colour matches the fog (no visible edge at any aspect); battle crates live in their own group (survive scene rebuilds) and follow the board mode (`switchableBox`: 3D mesh ⇄ 2D box on a mode switch or a lost WebGL context); quality 'low' drops shadows; `?board=2d|3d` overrides. The direction wheel's range stripes sit between the board canvas and the unit canvas. -- Done: Final Assault / Hidden Core prep takes place on your own half of the boss field (research 09 §1.2): pieces shown at boss-field positions (row − 7; right-hand player mirrored col → 20 − col, RIGHT ↔ LEFT), drop targets and wheel directions mapped back to board coordinates (`prepCamera` → `bossPrep` in public/js/ui/gameLogic.js; covered by the playtest2 browser tests). A scout of such a board (`m.field` prep of a boss round) comes in boss-field coordinates with the player's `side` and is framed the same way, the leader standing at its spawn (0.2.0, community report item 55). +- Done: Final Assault / Hidden Core prep takes place on your own half of the boss field (research 09 §1.2): pieces shown at boss-field positions (row − 7; right-hand player mirrored col → 20 − col, RIGHT ↔ LEFT), drop targets and wheel directions mapped back to board coordinates (`prepCamera` → `bossPrep` in public/js/ui/gameLogic.js; covered by the playtest2 browser tests). A scout of such a board (`m.field` prep of a boss round) comes in boss-field coordinates with the player's `side` and is framed the same way, the leader standing at its spawn (0.2.0, community report item 55). Both players of a pair are shown together, as in the battle (0.2.0, community report item 51): the partner's board stands on the other half (`m.private.bossMate` units, already in boss-field coordinates: mirrored on the right half, RIGHT ↔ LEFT; read-only `m:` views of `render/app.js syncMates`), the whole boss field is lit, a scout carries both halves (`mate`), and the team panel frames the pair's avatars in the official green (`ui/battle bg_team_border` tinted, a plain ring without the local art; `gameLogic teamFrameIds`) from the round's start through the fight. --- diff --git a/docs/design/network.md b/docs/design/network.md index 37fad38e..0d8a2748 100644 --- a/docs/design/network.md +++ b/docs/design/network.md @@ -18,7 +18,7 @@ Platform additions (implemented): `welcome.{resumed, version}`, `welcome.diyKitt C→S (all carry `rid`): `g.infoReady` · `g.band {bandId}` · `g.bandSkip` · `g.bandFocus {bandId?}` (the strategy highlighted in the draft screen — what a timed-out turn takes; absent / null clears it; `WRONG_PHASE` outside BAND_DRAFT, `ALREADY` after the pick, `BAD_TARGET` for a band not allowed; §18.2) · `g.buy {slot}` · `g.refresh` · `g.freeze` · `g.levelUp` · `g.sell {uid}` · `g.move {uid, to:{area:'board', row, col}|{area:'hand', idx}, dir?}` · `g.equip {itemUid, targetUid, replaceUid?}` (`replaceUid`: which equipped item a third one replaces, §6.2) · `g.art {itemUid, row, col, dir?}` (`dir` ∈ UP|RIGHT|DOWN|LEFT from the deploy wheel, §3; absent ⇒ `to.dir`, then RIGHT; `g.move` onto the piece's own tile re-orients it; board pieces in `m.private` / `m.field {prep:true}` carry `dir`) · `g.destroy {uid}` (hand / temp items only; equipped items are locked, §6.2) · `g.reward {idx}` · `g.choice {idx}` · `g.ready {ready}` · `g.emote {id}` · `g.watch {fieldId, playerId?}` (`playerId`: the player tapped — a shared 联防 / boss pair field shows two; what an eliminated viewer follows, §25) · `g.autoplay {on}` · `g.pause {on}` (solo battles only, §14 Solo pause) · `g.unitStats {seq?}` (ROUND_START / SP_DRAFT / PREP, else `WRONG_PHASE`; eliminated: `ELIMINATED`) → push `m.unitStats` (§18.5) · `g.leave` S→C: - `m.public` — full public state on every change (throttled ≤ 10/s): `{ phase, round, lastRound, deadline, serverNow, modeId, difficulty, stageId, factions, disabledBonds, bannedChess, bossId, hiddenBossId?, teamLp?, bossHp?:{hp,max}, overtimeAt? /* FA / HC: ms epoch the overtime drain starts, §4 */, paused /* solo pause, §14 */, draft?:{order,turn,picks,skipsLeft,turnDeadline,turnSeconds /* one turn's length, 0 untimed; deadline = turnDeadline (§18.2) */,untimed}, sp?:{family,cards /* bounty cards carry descRaw?, §20.6 */,turn,picks}, players:[{playerId, seat, name, isBot, connected, alive, lp, bandId, shopLevel, boardCount, ready, bonds:[{bondId,count,active,tier,layers,harmony? /* 调和's +1 is in count, §21.26 */}], fieldId, status, pendingLp? /* COMBAT / 联防 of a normal round: the LP the player's own battle will cost at settlement so far, min(lpCapPerRound, counted leaks) — in 联防 min(lpCapPerRound, uniteLeft) — omitted when 0 and in every other phase (§17.5 live LP) */, uniteLeft? /* 联防: the leaker's enemies still standing — not spawned yet + alive + counted leaks, uncapped, live (§20.6) */}] , fields:[{fieldId, kind, players:[playerId...]}] }` -- `m.private` — the recipient's full PlayerState view (§6.2 minus server-only data) on every change. +- `m.private` — the recipient's full PlayerState view (§6.2 minus server-only data) on every change; in a boss round's prep also `bossMate { playerId, side, units }` — the pair partner's board on its half of the boss field (UnitInfo, mirrored on the right half), re-sent when the partner moves (0.2.0, community report item 51). - `m.field` — field meta when watching starts: `{ fieldId, kind, rect, stageId, units: [UnitInfo], effects? }` (the units on the field, knocked-out operators waiting to redeploy included — `Battle.fieldMeta`, §18.3). The prep-scouting field (`Match.prepFieldMeta`) puts the scouted player's **hand (整备区) and 临时整备区 into `units` as well**: held pieces as units on the hand row (row 7, col = hand slot) and temp pieces on the temp row (row 8, cols 4..8 = the temp slots; PRTS 帮助 counts the temp area with the hand; a held item is a `kind: 'item'` unit the client draws as its floating plate) — the scout renders exactly like the own prep bench, for every watcher alike, the spectator seat's copy equal to a teammate's (§23.19's "never a private view" stays the no-`m.private` rule); the scout signature `_prepScoutSig` carries the hand / temp slots, so a piece moved to another slot is a change — and carries `effects`: the scouted player's effects view (策略 / 机变 / 悬赏 …), the watched player's effects column ready-made - NOTE: game time in `b.snap`/`b.ev` is carried as **`gt`** (the frame's `t` is the message type). - Active finite zero-SP timed skills display `sp=timeLeft`, `spMax=duration` and `UF.SKILL`; after end they display diff --git a/public/css/screens/game.css b/public/css/screens/game.css index 8f58f30b..6eee175a 100644 --- a/public/css/screens/game.css +++ b/public/css/screens/game.css @@ -307,6 +307,15 @@ .team__row.is-self .pavatar { box-shadow: 0 0 0 2px var(--mint-500), 0 0 .18rem rgba(23, 249, 183, .35); } .team__row.is-watched .pavatar { box-shadow: 0 0 0 2px var(--amber), 0 0 .18rem rgba(246, 163, 41, .45); } .team__row.is-dead .team__name { color: var(--text-dim); text-decoration: line-through; } +/* 最终攻势 / 隐秘核心: the viewer's pair framed in the official green from the round's start (item 51) — the official + ui/battle bg_team_border (a white frame with its glow) tinted through a mask; a plain green ring without the local art. + It replaces the own row's mint ring; a watched row keeps its amber one inside the frame. */ +.team__frame { + position: absolute; inset: -.07rem; pointer-events: none; z-index: 1; + background: var(--mint-500); -webkit-mask: var(--frame) center / 100% 100% no-repeat; mask: var(--frame) center / 100% 100% no-repeat; +} +.team__frame--plain { inset: -.04rem; background: none; -webkit-mask: none; mask: none; border: 2px solid var(--mint-500); box-shadow: 0 0 .14rem rgba(78, 216, 175, .65); } +.team__row.is-self.is-team .pavatar { box-shadow: none; } .team__bubble { position: absolute; left: calc(100% + .06rem); top: 50%; transform: translateY(-50%); width: max-content; max-width: 3.2rem; } /* 前往查看 / 返回战场 (research 09 §3.1: tapping a teammate's avatar expands a mint button under it) */ .team__row.is-open .pavatar { box-shadow: 0 0 0 2px var(--mint-400), 0 0 .2rem rgba(23, 249, 183, .45); } diff --git a/public/i18n/en.json b/public/i18n/en.json index 18112329..d37762c4 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -677,6 +677,7 @@ "刷新商店 · R": "Refresh Shop · R", "免费 ×{free}": "Free ×{free}", "查看 {name} 的战场": "View {name}'s Battlefield", + "与你在同一战场": "On your field", "查看自己的阵地": "View your Field", "查看 {name} 的阵地": "View {name}'s Field", "连接已断开": "Disconnected", diff --git a/public/js/render/app.js b/public/js/render/app.js index a1d4ad3c..2a3237e3 100644 --- a/public/js/render/app.js +++ b/public/js/render/app.js @@ -78,7 +78,10 @@ // boss rows 2–5, bench 7 → 0, temp 8 → 1, side 'R' mirrored col c → 20 − c with RIGHT ↔ LEFT. Every public // coordinate stays in BOARD space (pieceDrop targets, canPlace, highlightTiles, tileScreen, holdPiece, setPieceDir, // stored `dir`); `view.prepField()` → { kind, side, mirror } and `tileScreen(...).mirror` tell the direction wheel -// that a screen-right swipe means board LEFT on the mirrored half. +// that a screen-right swipe means board LEFT on the mirrored half. The pair partner's board stands on the other half +// (`priv.bossMate` units, already in boss-field coordinates — server/match/match/views.js bossMateView), read-only +// ('m:' views: never picked or dragged), and the lit rect is the whole boss field: both players of a pair are +// shown together, as in the battle (community report of 2026-10-06, item 51). // Lost WebGL context of the 3D board: the 2D board takes over at once and the 3D board is rebuilt on a fresh context // a moment later (up to 3 tries; a context lost again right away counts as a failure; `setBoardMode('2d')` stops it). // @@ -458,7 +461,8 @@ export async function createFieldView(host, options = {}) { if (k === 'pen') return { r0: 14, r1: 18, c0: 7, c1: 13 }; // prep lights the bench (hand row 7 / temp row 8) with the field, like the official prep view if (camOpts.rect) { const r = normRect(camOpts.rect); return k === 'prep' ? { ...r, r0: Math.min(r.r0, GEO.HAND_ROW) } : r; } - if (k === 'bossPrep') return camOpts.side === 'R' ? { r0: 0, r1: 5, c0: 10, c1: 20 } : { r0: 0, r1: 5, c0: 0, c1: 10 }; + // the boss round's prep lights the whole boss field: the pair partner's half is shown with its pieces (item 51) + if (k === 'bossPrep') return { ...GEO.BOSS_RECT }; return k === 'boss' ? { ...GEO.BOSS_RECT } : k === 'unite' ? { ...GEO.UNITE_RECT } : k === 'prep' ? { r0: 7, r1: 12, c0: 0, c1: 10 } : { ...GEO.NORMAL_RECT }; } @@ -768,12 +772,42 @@ export async function createFieldView(host, options = {}) { if (e.area === 'board' && info.dir && !held.has(e.uid) && typeof v.setDir === 'function') v.setDir(info.dir); } for (const k of [...views.keys()]) if (String(k).startsWith('p:') && !seen.has(Number(String(k).slice(2)))) dropView(k); + // the boss round's prep: the pair partner's board on its half of the boss field (item 51; display only) + syncMates(prepXf.kind === 'bossPrep' && src.bossMate && Array.isArray(src.bossMate.units) ? src.bossMate.units : []); prepPieces = list.filter((e) => e.key && views.has(e.key)); if (dragState && !views.has(dragState.key)) { drag.reset(); endDragVisual(false); } holdScene(false); // the prep pieces reference their models now (a battle's hold ends here) return true; } + /** + * The pair partner's pieces in the Final Assault / Hidden Core prep (m.private bossMate units: UnitInfo in boss-field + * coordinates, facing as the battle will place them): read-only 'm:' views — not prep pieces, so they are never + * picked, dragged or swept with the own ones; a view is rebuilt when its body, tile or facing changes. + */ + function syncMates(units) { + const keep = new Set(); + for (const u of units) { + const info = u && Number.isInteger(u.uid) ? renderInfo({ ...u, id: `m:${u.uid}` }) : null; + if (!info) continue; + keep.add(info.id); + const sig = `${info.defId}|${info.golden ? 1 : 0}|${info.spine || ''}|${info.x},${info.y}|${info.dir || ''}|${(info.items || []).join(',')}`; + let v = views.get(info.id); + if (v && v._sig !== sig) { dropView(info.id); v = null; } + if (v) continue; + v = new UnitView(ctx, { ...info, maxHp: 1 }, { prep: true }); + v._sig = sig; + v.setWorld(info.x, info.y, heightAt(info.y, info.x)); + if (info.dir && typeof v.setDir === 'function') v.setDir(info.dir); + v._showFacing = true; + if (v.setItems && Array.isArray(info.items) && info.items.length) { + v.setItems(info.items.map((it) => { const r = data.item(it); return assets.itemIcon ? assets.itemIcon(r ? { trapId: r.trapId, iconId: r.iconId } : it) : null; })); + } + views.set(info.id, v); + } + for (const k of [...views.keys()]) if (String(k).startsWith('m:') && !keep.has(k)) dropView(k); + } + /** * A summon stack of several copies (凯瑟琳's 2 devices, user playtest #6) dropped on a board tile stays in the hand * and one copy — a new piece of the same token — lands there: the state already shows the move, the stack goes home. diff --git a/public/js/ui/gameLogic.js b/public/js/ui/gameLogic.js index 86688c57..fd6043a8 100644 --- a/public/js/ui/gameLogic.js +++ b/public/js/ui/gameLogic.js @@ -26,7 +26,7 @@ export { clamp, tileKey } from './gameLogic/shared.js'; export { phaseMode, isCombatPhase, showDeadPill, isBossPhase } from './gameLogic/phases.js'; -export { prepCamera, prepCameraFor, foldCamera, deployFieldOf, fieldTile, boardTileOf, ownerBandId } from './gameLogic/camera.js'; +export { prepCamera, prepCameraFor, foldCamera, deployFieldOf, fieldTile, boardTileOf, ownerBandId, teamFrameIds } from './gameLogic/camera.js'; export { phaseBanner, prepCapsuleLabel, countdownState, phaseTotalSeconds } from './gameLogic/phases.js'; export { STATUS_META } from './gameLogic/format.js'; export { sortedPlayers } from './gameLogic/shared.js'; diff --git a/public/js/ui/gameLogic/camera.js b/public/js/ui/gameLogic/camera.js index 1d0d7f11..ce0a59ac 100644 --- a/public/js/ui/gameLogic/camera.js +++ b/public/js/ui/gameLogic/camera.js @@ -1,6 +1,6 @@ // ui/gameLogic/camera.js — prep camera, boss-field tile mapping, owner band. Re-exported from ../gameLogic.js. -import { GEO } from '../../../../shared/constants.js'; +import { GEO, PHASE } from '../../../../shared/constants.js'; import { BOSS_ROW_SHIFT, MAX_COL } from '../../render/prepfield.js'; import { int, isObj } from './shared.js'; @@ -26,6 +26,37 @@ export function prepCamera(pub, myId) { return { kind: 'bossPrep', opts: { side: idx % 2 === 1 ? 'R' : 'L' } }; } +const BOSS_PREP_PHASES = new Set([PHASE.ROUND_START, PHASE.SP_DRAFT, PHASE.PREP]); + +/** + * The players framed in green on the team panel (community report of 2026-10-06, item 51: 「同组两个玩家屏幕左边的头像也像原版 + * 那样用绿框框起来」 — the official co-op frame, ui/battle `bg_team_border`): in a boss round (最终攻势 / 隐秘核心) the viewer + * and its pair partner — from the round's start (回合开始 / 机变 / 休整期: the pairing `prepCamera` reads, the players still + * in by seat, two by two — server/match/finalAssault.js pairPlayers) through the fight (the boss field listing the + * viewer). Empty for a lone player, an eliminated player or a spectator seat (no pair of its own) and in every other + * round. PR #191 framed the partner alone, in gold (not merged). + * @param {any} pub m.public @param {string|null} myId + * @returns {Set} + */ +export function teamFrameIds(pub, myId) { + const none = new Set(); + if (!pub || !myId) return none; + const players = Array.isArray(pub.players) ? pub.players.filter(isObj) : []; + const me = players.find((p) => p.playerId === myId); + if (!me || me.alive === false) return none; + const r = pub.round; + const bossRound = Number.isInteger(r) && r > 0 && (r === pub.bossRound || r === pub.hiddenRound); + if (pub.phase === PHASE.FINAL_ASSAULT || pub.phase === PHASE.HIDDEN_CORE) { + const f = (Array.isArray(pub.fields) ? pub.fields : []).find((x) => isObj(x) && (x.kind === 'boss' || x.kind === 'hidden') && Array.isArray(x.players) && x.players.includes(myId)); + return f && f.players.length > 1 ? new Set(f.players) : none; + } + if (!bossRound || !BOSS_PREP_PHASES.has(pub.phase)) return none; + const alive = players.filter((p) => p.alive !== false).sort((a, b) => int(a.seat, 99) - int(b.seat, 99)).map((p) => p.playerId); + const idx = alive.indexOf(myId); + const pair = alive.slice(idx - (idx % 2), idx - (idx % 2) + 2); + return pair.length > 1 ? new Set(pair) : none; +} + /** * The own prep board's camera request with the shop bar's state (public issue #5 "准备阶段,收起商店界面时,界面并不会进行 * 缩放"): `prepCamera` plus `shop: !folded` — a folded shop (收起) takes the official shop-collapsed camera diff --git a/public/js/ui/teamPanel.js b/public/js/ui/teamPanel.js index 8ef00f23..54c0a396 100644 --- a/public/js/ui/teamPanel.js +++ b/public/js/ui/teamPanel.js @@ -1,6 +1,7 @@ // Left team panel (research 06 §11.1, research 09 §3.1): one row per seat — avatar (band icon once picked), name, LP // tower, status glyph (… acting / ✓ ready / ⌛ deciding / ⚔ combat / door left / ✕ dead), AI badge, "you" marker, the -// field being watched (eye badge), and emote bubbles. +// field being watched (eye badge), and emote bubbles. In a boss round the viewer's pair is framed in green from the +// round's start (gameLogic teamFrameIds; community report of 2026-10-06, item 51). // Observing (client-side combat, `observe` prop — the official flow): tapping a teammate's avatar expands a mint // "前往查看" button under the row (when that teammate can be observed now; otherwise the reason is toasted through // onWatch); while observing, the own row shows a "返回战场" button. Without `observe` (server-run combat) a click @@ -19,7 +20,7 @@ import { PHASE } from '../../../shared/constants.js'; import { html, Icon, Tooltip } from './components.js'; import { PlayerAvatar, LpTower, GIcon, LocalSprite } from './gameComponents.js'; import { EmoteBubble } from './emotes.js'; -import { STATUS_META, sortedPlayers } from './gameLogic.js'; +import { STATUS_META, sortedPlayers, teamFrameIds } from './gameLogic.js'; import { MissTag, uniteRemaining } from './hud.js'; import { localAsset } from '../data.js'; import { t } from '../../../shared/i18n.js'; @@ -79,6 +80,10 @@ export function TeamPanel({ pub, myId, watching, bubbles, onWatch, compact = fal useEffect(() => { setOpenPid(null); }, [phaseKey, watching, observe?.observing]); const players = sortedPlayers(pub); if (!players.length) return null; + // a boss round: the viewer's pair framed in green from the round's start (item 51 — the official bg_team_border, + // tinted like the official green; a plain green ring without the local art) + const team = teamFrameIds(pub, myId); + const frameArt = team.size ? localAsset('ui/battle', 'bg_team_border') : null; const click = (p, self) => { if (!observe) { onWatch(p); return; } if (self) { if (observe.observing) observe.onBack(); setOpenPid(null); return; } @@ -98,9 +103,11 @@ export function TeamPanel({ pub, myId, watching, bubbles, onWatch, compact = fal const back = !!observe && self && observe.observing; const title = observe ? (self ? (observe.observing ? t('返回战场') : t('你自己')) : t('查看 {name} 的战场', { name: p.name })) : (self ? t('查看自己的阵地') : t('查看 {name} 的阵地', { name: p.name })); const lp = rowLp(p, pub, self ? selfLive : null, { uniteLocal, cap }); - return html`
-