feedback5: merge O24 — 自选 kits for 凯尔希·思衡托 (with her 战术锚点) and Mon3tr (with her 重构体); engine (additive): mod blockRadiusScale widens the air-block radius (0 for every other unit); golden: only diy moved — all 71 自选 operators now have kits
This commit is contained in:
commit
a009d77d71
11 files changed
+1695
-177
No files matched your search
@@ -0,0 +1,360 @@
|
||||
// test/content/op_kalts2.test.js — the 自选 operator kit of 凯尔希·思衡托 (char_1052_kalts2, 6★ 守望者; kit
|
||||
// server/sim/content/kits/ops/op-kalts2.js) and her summon 战术锚点 (token_10068_kalts2_mtship), fielded the production way (a
|
||||
// DIY slot + its `diy` pick; the anchor as a token piece of hers) in every form: tiers 5 / 6, normal (E2 Lv1, skill rank 4)
|
||||
// and elite (E2 Lv60, rank 7) — she has no module. Every number is read back from data/backups.json (the form of that slot
|
||||
// status, the token's variant); the fidelity checklist of kits/README.md item by item.
|
||||
// Run: node --test test/content/op_kalts2.test.js
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
import { KITTED_CHARS, OPERATOR_KITS, KITS } from '../../server/sim/content/kits/index.js';
|
||||
import { diyPool, validateDiyPicks } from '../../shared/diy.js';
|
||||
import { COLS } from '../../server/sim/constants.js';
|
||||
|
||||
const load = (f) => JSON.parse(readFileSync(new URL(`../../data/${f}.json`, import.meta.url), 'utf8'));
|
||||
const CHESS = load('chess');
|
||||
const BACKUPS = load('backups');
|
||||
const KALTS2 = 'char_1052_kalts2';
|
||||
const ANCHOR = 'token_10068_kalts2_mtship';
|
||||
const FORMS = BACKUPS.units[KALTS2].forms;
|
||||
const TOKEN = BACKUPS.tokens[ANCHOR];
|
||||
const SLOT = { 5: 'chess_char_5_diy1_a', 6: 'chess_char_6_diy1_a' };
|
||||
const S1 = 'skchr_kalts2_1', S2 = 'skchr_kalts2_2', S3 = 'skchr_kalts2_3';
|
||||
const statusKey = (tier, elite) => (elite ? (tier === 5 ? '2/60/7/1' : '2/60/7/3') : '2/1/4/0');
|
||||
const formOf = (tier, elite) => FORMS[statusKey(tier, elite)];
|
||||
const skillOf = (tier, elite, id) => formOf(tier, elite).skills.find((s) => s.skillId === id);
|
||||
const talentOf = (tier, elite, i) => formOf(tier, elite).talents.find((t) => t.index === i).bb;
|
||||
const variantOf = (tier, elite) => TOKEN.variants[`${KALTS2}@${statusKey(tier, elite)}`];
|
||||
const approx = (a, b, msg, eps = 1e-6) => assert.ok(Math.abs(a - b) <= eps * Math.max(1, Math.abs(b)), `${msg}: ${a} vs ${b}`);
|
||||
const dummy = (key, o = {}) => enemyRec({ key, hp: 1e9, speed: 0, mass: 0, ...o });
|
||||
const ENEMIES = {
|
||||
enemy_dummy: dummy('enemy_dummy'), enemy_fly: dummy('enemy_fly', { motion: 'FLY' }),
|
||||
enemy_biter: dummy('enemy_biter', { atk: 400, range: 1.2, bat: 1 }),
|
||||
enemy_flybiter: dummy('enemy_flybiter', { atk: 300, range: 1.6, bat: 1, motion: 'FLY' }),
|
||||
};
|
||||
/** Every 自选 form: [tier, elite] (no module). */
|
||||
const FORMS_ALL = [[5, false], [6, false], [5, true], [6, true]];
|
||||
const label = ([tier, elite]) => `T${tier} ${elite ? 'elite' : 'normal'}`;
|
||||
const YAK = 'chess_char_1_02_a', TEXAS = 'chess_char_1_08_a', PERFUMER = 'chess_char_2_14_a';
|
||||
|
||||
/** A battle with 凯尔希·思衡托 as uid 1 at (10, 4) facing RIGHT (`anchor`: her piece as uid 2), plus `others`. */
|
||||
function field({ tier = 5, elite = false, skill = 0, anchor = null, others = [], seed = 5, dp = 99 } = {}) {
|
||||
const units = [{ uid: 1, diy: { slot: SLOT[tier], charId: KALTS2, skillIndex: skill }, elite, row: 10, col: 4 }];
|
||||
if (anchor) units.push({ uid: 2, kind: 'token', tokenId: ANCHOR, ownerUid: 1, row: anchor.row, col: anchor.col, dir: anchor.dir ?? 'RIGHT' });
|
||||
const log = []; // heal / damaged in the order they happen
|
||||
const h = makeBattle({
|
||||
defs: { enemies: ENEMIES }, timeLimit: 900, autoFinish: false, seed,
|
||||
flags: { dpInit: dp, dpPerSec: 0, dpMax: 999 },
|
||||
hooks: ['damaged', 'heal', 'skillStart', 'skillEnd', 'statusApplied', 'death', 'deploy', 'blocked'], captureNoisy: true,
|
||||
units: [...units, ...others],
|
||||
setup: (b) => {
|
||||
b.on('heal', (c) => { if (!c.opts?.regen) log.push({ name: 'heal', ...c }); }, { priority: -1000 });
|
||||
b.on('damaged', (c) => log.push({ name: 'damaged', ...c }), { priority: -1000 });
|
||||
},
|
||||
});
|
||||
h.step();
|
||||
return { h, u: h.unit(1), a: anchor ? h.unit(2) : null, log };
|
||||
}
|
||||
const heals = (h, u) => h.hooksOf('heal').filter((c) => c.source === u && !c.opts?.regen);
|
||||
const inRange = (u, x) => u.rangeKeySet.has(x.tileR * COLS + x.tileC);
|
||||
function done(h) {
|
||||
checkInvariants(h.b);
|
||||
assert.equal(h.b.errors.length, 0, JSON.stringify(h.b.errors[0]));
|
||||
}
|
||||
|
||||
test('凯尔希·思衡托 in every 自选 form: her operator kit (all three skills authored), the form\'s stats with 遗尘守望, y-6, one heal per attack, blocks 2, 起飞, 罗德岛, no module, no 特质; her 战术锚点: the form\'s token stats', () => {
|
||||
assert.equal(OPERATOR_KITS[KALTS2], KITS[KALTS2]);
|
||||
for (const f of FORMS_ALL) {
|
||||
const [tier, elite] = f;
|
||||
for (const skill of [0, 1, 2]) {
|
||||
const { h, u, a } = field({ tier, elite, skill, anchor: { row: 10, col: 9 } });
|
||||
const form = formOf(tier, elite), t0 = talentOf(tier, elite, 0);
|
||||
assert.deepEqual([u.def.charId, u.def.diyFor, u.skill.id, !!u.kit.generic, u.kit.skillSource], [KALTS2, SLOT[tier], form.skills[skill].skillId, false, 'skills'], label(f));
|
||||
assert.deepEqual([u.base.maxHp, u.base.atk, u.base.def, u.base.bat, u.base.cost], [form.stats.maxHp, form.stats.atk, form.stats.def, 2.85, 19], `${label(f)}: stats`);
|
||||
approx(u.s.maxHp, form.stats.maxHp * (1 + t0.max_hp), `${label(f)}: 生命上限 +25 %`);
|
||||
approx(u.s.def, form.stats.def * (1 + t0.def), `${label(f)}: 防御力 +25 %`);
|
||||
assert.deepEqual([u.s.blockCnt, u.s.blockRadiusScale, t0.block_cnt, t0.block_radius_scale], [2, 0.23, 1, 0.23], `${label(f)}: 阻挡数 +1, 阻挡半径倍率 +0.23`);
|
||||
assert.deepEqual([u.profile.dmgType, u.profile.heal, u.profile.attack, u.def.subProf], ['heal', { mode: 'single' }, 'ranged', 'watchman'], `${label(f)}: 守望者 heal`);
|
||||
assert.deepEqual([u.s.flags.liftoff, u.s.flags.blockFly], [true, true], `${label(f)}: 起飞`);
|
||||
assert.deepEqual(u.liveRangeGrid, form.rangeGrid, `${label(f)}: y-6`);
|
||||
assert.deepEqual([u.def.bonds, u.def.raw.garrisonIds, u.def.tokens, u.def.raw.nationId, !!u.def.raw.module?.active, form.modules?.length ?? 0],
|
||||
[BACKUPS.diy.operators[KALTS2].bonds, [], [ANCHOR], 'rhodes', false, 0], `${label(f)}: bonds / 特质 / summon / no module`);
|
||||
const ts = variantOf(tier, elite).stats;
|
||||
assert.deepEqual([a.base.maxHp, a.base.atk, a.base.cost, a.base.respawnTime, a.s.blockCnt], [ts.maxHp, 0, 3, 70, 0], `${label(f)}: 战术锚点 stats`);
|
||||
assert.deepEqual([a.s.flags.invulnerable, a.s.flags.untargetable, a.profile.noAttack, !!a.kit.generic], [true, true, true, false], `${label(f)}: 战术锚点 无敌, never targeted, no attack`);
|
||||
assert.equal(a.alive, skill === 2, `${label(f)}: the anchor piece takes the field only with S3 (a skill's summon)`);
|
||||
done(h);
|
||||
}
|
||||
}
|
||||
// the numbers of the forms (zh_CN): E2 Lv1 1393 / 408 / 220, E2 Lv60 1543 / 476 / 257; the anchor 2235 HP, 3 DP, 70 s
|
||||
assert.deepEqual([FORMS['2/1/4/0'].stats.maxHp, FORMS['2/1/4/0'].stats.atk, FORMS['2/60/7/1'].stats.maxHp, FORMS['2/60/7/3'].stats.atk], [1393, 408, 1543, 476]);
|
||||
assert.deepEqual([variantOf(5, false).stats.maxHp, variantOf(6, true).stats.cost, TOKEN.placeable], [2235, 3, true]);
|
||||
});
|
||||
|
||||
test('a 自选 pick: 凯尔希·思衡托 is offered at tiers 5 and 6 (she has a kit) and a roster with her passes validateDiyPicks', () => {
|
||||
const data = { chess: CHESS, backups: BACKUPS };
|
||||
assert.ok(KITTED_CHARS.includes(KALTS2));
|
||||
for (const t of [5, 6]) assert.ok(diyPool(t, { data, kitted: KITTED_CHARS }).includes(KALTS2), `tier ${t}`);
|
||||
assert.deepEqual(validateDiyPicks({ [SLOT[5]]: { charId: KALTS2, skillIndex: 2 } }, { data, kitted: KITTED_CHARS }),
|
||||
{ ok: true, picks: { [SLOT[5]]: { charId: KALTS2, skillIndex: 2, uniEquipId: null } } });
|
||||
});
|
||||
|
||||
test('trait: her heals take 凯尔希\'s Mon3tr through its 禁疗 (her selection too); another 禁疗 summon stays out', () => {
|
||||
const others = [{ uid: 3, diy: { slot: 6, charId: 'char_003_kalts', skillIndex: 0 }, row: 12, col: 2 },
|
||||
{ uid: 4, kind: 'token', tokenId: 'token_10002_kalts_mon3tr', ownerUid: 3, row: 10, col: 5 }];
|
||||
const { h, u } = field({ skill: 0, others });
|
||||
const m = h.unit(4);
|
||||
assert.ok(m.s.flags.noHeal && inRange(u, m), '凯尔希\'s Mon3tr: 禁疗, in her range');
|
||||
m.hp = m.s.maxHp * 0.4;
|
||||
const n0 = heals(h, u).length;
|
||||
assert.ok(h.runUntil(() => heals(h, u).length > n0, 4), 'a heal');
|
||||
assert.equal(heals(h, u)[n0].target, m, 'it is her target');
|
||||
approx(heals(h, u)[n0].amount, u.s.atk, 'a full heal through its 禁疗');
|
||||
done(h);
|
||||
});
|
||||
|
||||
test('T1 遗尘守望 起飞: no ground enemy targets her or is blocked by her; she blocks a flyer 1.0 tile away (air radius 0.8944 × 1.23), two at most; the mod blockRadiusScale on any blocker', () => {
|
||||
const { h, u } = field({ skill: 0, others: [{ uid: 3, chessId: YAK, row: 11, col: 7 }] });
|
||||
const yak = h.unit(3);
|
||||
const g = h.spawn('enemy_biter', { pos: [10, 5] });
|
||||
const f1 = h.spawn('enemy_fly', { pos: [10, 5] });
|
||||
const f2 = h.spawn('enemy_fly', { pos: [11, 4] });
|
||||
const f3 = h.spawn('enemy_fly', { pos: [9, 4] });
|
||||
h.run(3);
|
||||
assert.equal(g.blockedBy, null, 'a ground enemy on the next tile is not blocked');
|
||||
assert.equal(h.hooksOf('damaged').filter((c) => c.target === u).length, 0, 'nor does it attack her (对地规避)');
|
||||
assert.equal([f1, f2, f3].filter((e) => e.blockedBy === u).length, 2, 'two of the three flyers 1.0 away: block 2');
|
||||
assert.ok(f1.blockedBy === u, 'the first one in contact');
|
||||
// the engine rule on another unit: a blockFly unit blocks a flyer 1.0 away only with 阻挡半径倍率 ≥ 1.118
|
||||
const f4 = h.spawn('enemy_fly', { pos: [11, 8] });
|
||||
h.b.addBuff(yak, { key: 'test:air', flags: { blockFly: true } });
|
||||
h.run(0.5);
|
||||
assert.equal(f4.blockedBy, null, 'radius 0.8944: out of reach');
|
||||
h.b.addBuff(yak, { key: 'test:scale', mods: { blockRadiusScale: 0.23 } });
|
||||
h.run(0.5);
|
||||
assert.equal(f4.blockedBy, yak, 'radius 0.8944 × 1.23: blocked');
|
||||
assert.equal(yak.s.blockRadiusScale, 0.23);
|
||||
done(h);
|
||||
});
|
||||
|
||||
test('T2 医者丰碑: an operator entering her range gets 1 护盾 layer (one damage instance blocked) and 生命回复速度 +50 for 30 s (罗德岛 +100); summons and she get nothing; a new entry refreshes, never a 2nd layer', () => {
|
||||
for (const f of [[5, false], [6, true]]) {
|
||||
const [tier, elite] = f;
|
||||
const t1 = talentOf(tier, elite, 1);
|
||||
assert.deepEqual([t1.buff_duration, t1.hp_recovery_per_sec, t1.rhodes_bonus], [30, 50, 2], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }, { uid: 4, chessId: PERFUMER, row: 9, col: 5 },
|
||||
{ uid: 5, chessId: TEXAS, row: 12, col: 8 }, { uid: 6, diy: { slot: 6, charId: 'char_003_kalts', skillIndex: 0 }, row: 12, col: 2 },
|
||||
{ uid: 7, kind: 'token', tokenId: 'token_10002_kalts_mon3tr', ownerUid: 6, row: 11, col: 5 }];
|
||||
const { h, u } = field({ tier, elite, skill: 0, others });
|
||||
const yak = h.unit(3), pf = h.unit(4), tx = h.unit(5), m = h.unit(7);
|
||||
assert.equal(pf.def.raw.nationId, 'rhodes');
|
||||
assert.ok(yak.findBuff('talent:kalts2:shield') && yak.findBuff('talent:kalts2:shield').shieldHits === 1, `${label(f)}: 角峰 1 护盾 layer`);
|
||||
assert.deepEqual([yak.findBuff('talent:kalts2:regen')?.mods, pf.findBuff('talent:kalts2:regen')?.mods], [{ hpRegen: 50 }, { hpRegen: 100 }], `${label(f)}: 50 / 罗德岛 100 HP/s (生命回复速度)`);
|
||||
approx(yak.findBuff('talent:kalts2:regen').timeLeft, 30, `${label(f)}: 30 s`, 0.01);
|
||||
assert.ok(!m.findBuff('talent:kalts2:shield') && !m.findBuff('talent:kalts2:regen'), `${label(f)}: no summon`);
|
||||
assert.ok(!u.findBuff('talent:kalts2:shield') && !u.findBuff('talent:kalts2:regen'), `${label(f)}: not herself`);
|
||||
assert.ok(!tx.findBuff('talent:kalts2:regen'), `${label(f)}: 德克萨斯 outside her range`);
|
||||
// the 护盾 blocks one damage instance whole
|
||||
const hp0 = yak.hp;
|
||||
assert.equal(h.b.dealDamage(null, yak, { amount: 500, type: 'true', sourceless: true }), 0, `${label(f)}: blocked`);
|
||||
assert.equal(yak.hp, hp0);
|
||||
assert.equal(yak.findBuff('talent:kalts2:shield'), null, `${label(f)}: used up`);
|
||||
assert.ok(h.b.dealDamage(null, yak, { amount: 500, type: 'true', sourceless: true }) > 0, `${label(f)}: the next one lands`);
|
||||
// still inside: no new entry; out and back in: a new layer and a fresh 30 s
|
||||
h.run(5);
|
||||
assert.equal(yak.findBuff('talent:kalts2:shield'), null, `${label(f)}: no entry while it stays`);
|
||||
approx(yak.findBuff('talent:kalts2:regen').timeLeft, 25, `${label(f)}: ticking`, 0.05);
|
||||
h.b.relocate(yak, 10, 8);
|
||||
h.step();
|
||||
assert.ok(!inRange(u, yak));
|
||||
h.b.relocate(yak, 10, 5);
|
||||
h.step();
|
||||
assert.ok(yak.findBuff('talent:kalts2:shield'), `${label(f)}: re-entry: 护盾 again`);
|
||||
approx(yak.findBuff('talent:kalts2:regen').timeLeft, 30, `${label(f)}: re-entry: 30 s again`, 0.05);
|
||||
h.b.relocate(yak, 10, 8); h.step(); h.b.relocate(yak, 10, 5); h.step();
|
||||
assert.equal(yak.buffs.filter((b) => b.key === 'talent:kalts2:shield').length, 1, `${label(f)}: one layer at most`);
|
||||
// 30 s on: the regen ends (德克萨斯 entering later gets its own)
|
||||
const r0 = tx.s.hpRegen; // (调香师's own 熏衣草 regen on everyone)
|
||||
assert.ok(h.b.relocate(tx, 9, 3));
|
||||
h.step();
|
||||
approx(tx.s.hpRegen - r0, 50, `${label(f)}: 德克萨斯 enters`);
|
||||
assert.deepEqual(tx.findBuff('talent:kalts2:regen')?.mods, { hpRegen: 50 });
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S1 应急肃正防线 (MANUAL, data DEFAULT — as she is about to heal): 35 s, ATK +60 % / +75 %, ASPD +25 / +35; every other 起飞 operator 阻挡半径倍率 +0.23 meanwhile (not a grounded one, not her twice)', () => {
|
||||
for (const f of [[5, false], [6, true]]) {
|
||||
const [tier, elite] = f;
|
||||
const sk = skillOf(tier, elite, S1);
|
||||
assert.deepEqual([sk.duration, sk.bb.atk, sk.bb.attack_speed, sk.bb['attack@block_radius_scale'], sk.spCost, sk.initSp, sk.trigger.rule],
|
||||
[35, elite ? 0.75 : 0.6, elite ? 35 : 25, 0.23, 35, elite ? 20 : 18, 'DEFAULT'], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }, { uid: 4, chessId: TEXAS, row: 12, col: 9 }];
|
||||
const { h, u } = field({ tier, elite, skill: 0, others });
|
||||
const yak = h.unit(3), tx = h.unit(4);
|
||||
h.b.addBuff(tx, { key: 'test:liftoff', flags: { liftoff: true } });
|
||||
assert.equal(u.skill.rule, 'DEFAULT');
|
||||
u.skill.gainSp(999);
|
||||
h.run(2);
|
||||
assert.equal(u.skill.activations, 0, `${label(f)}: nobody injured: no cast`);
|
||||
yak.hp = yak.s.maxHp * 0.5;
|
||||
assert.ok(h.runUntil(() => u.skill.active, 4), `${label(f)}: cast as she heals`);
|
||||
approx(u.skill.timeLeft, 35, `${label(f)}: 35 s`, 0.05);
|
||||
approx(u.s.atk, u.base.atk * (1 + sk.bb.atk), `${label(f)}: ATK`);
|
||||
assert.equal(u.s.aspd, u.base.aspd + sk.bb.attack_speed, `${label(f)}: ASPD`);
|
||||
h.run(0.4);
|
||||
assert.equal(tx.s.blockRadiusScale, 0.23, `${label(f)}: the 起飞 德克萨斯 (anywhere on the field)`);
|
||||
assert.equal(yak.s.blockRadiusScale, 0, `${label(f)}: 角峰 is not 起飞`);
|
||||
assert.equal(u.s.blockRadiusScale, 0.23, `${label(f)}: hers stays 0.23 (同名效果取最高)`);
|
||||
h.runUntil(() => !u.skill.active, 40);
|
||||
h.run(0.5);
|
||||
assert.deepEqual([tx.s.blockRadiusScale, u.s.atk, u.s.aspd], [0, u.base.atk, u.base.aspd], `${label(f)}: all back`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S2 保护性拒止 (MANUAL, data ACTIVE_RANGE on its y-11: an injured ally there): 10 shots, range y-11, ATK +; at an enemy (air too): true damage + 5 s 停顿 within 1.5, then the heal there; at an ally (no enemy): heal first, no 停顿', () => {
|
||||
for (const f of [[5, false], [6, true]]) {
|
||||
const [tier, elite] = f;
|
||||
const sk = skillOf(tier, elite, S2);
|
||||
assert.deepEqual([sk.bb.atk, sk.bb['attack@atk_scale'], sk.bb['attack@heal_scale'], sk.bb['attack@sluggish'], sk.bb['attack@trigger_time'], sk.rangeId, sk.trigger.rule],
|
||||
[elite ? 1.25 : 1.1, elite ? 3.5 : 3.1, elite ? 1.7 : 1.35, 5, 10, 'y-11', 'ACTIVE_RANGE'], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 7 }, { uid: 4, chessId: TEXAS, row: 11, col: 4 }];
|
||||
const { h, u, log } = field({ tier, elite, skill: 1, others });
|
||||
const yak = h.unit(3);
|
||||
const mine = (from) => log.slice(from).filter((c) => c.source === u);
|
||||
assert.deepEqual([u.skill.rule, u.skill.kind], ['ACTIVE_RANGE', 'ammo'], label(f));
|
||||
assert.ok(!inRange(u, yak), `${label(f)}: (10,7) is outside her y-6`);
|
||||
u.skill.gainSp(999);
|
||||
h.run(1.5);
|
||||
assert.equal(u.skill.activations, 0, `${label(f)}: nobody injured: no cast`);
|
||||
// an ally shot first: 角峰 injured inside the y-11 only, no enemy she can select (the dummy is beyond it)
|
||||
const e = h.spawn('enemy_dummy', { pos: [11, 8] }); // √2 from 角峰, outside the y-11 (col +4)
|
||||
const n0 = log.length;
|
||||
yak.hp = yak.s.maxHp * 0.3;
|
||||
assert.ok(h.runUntil(() => u.skill.active, 0.5), `${label(f)}: cast for the y-11`);
|
||||
assert.deepEqual([u.liveRangeGrid, u.skill.ammoMax], [sk.rangeGrid, 10], `${label(f)}: y-11, 10 shots`);
|
||||
approx(u.s.atk, u.base.atk * (1 + sk.bb.atk), `${label(f)}: ATK`);
|
||||
assert.ok(h.runUntil(() => u.skill.ammoLeft === 9, 3), `${label(f)}: one shot (the cast's attack)`);
|
||||
const shot = mine(n0);
|
||||
assert.deepEqual(shot.slice(0, 2).map((c) => [c.name, c.target]), [['heal', yak], ['damaged', e]], `${label(f)}: at an ally — heal first, then the enemy within 1.5`);
|
||||
approx(shot[0].amount, u.s.atk * sk.bb['attack@heal_scale'], `${label(f)}: heal ${sk.bb['attack@heal_scale'] * 100} % ATK`);
|
||||
approx(shot[1].amount, u.s.atk * sk.bb['attack@atk_scale'], `${label(f)}: ${sk.bb['attack@atk_scale'] * 100} % ATK true`);
|
||||
assert.equal(h.hooksOf('statusApplied').filter((c) => c.source === u).length, 0, `${label(f)}: no 停顿 from an ally shot`);
|
||||
// an enemy inside the y-11 is the target from now on — a flyer
|
||||
const fly = h.spawn('enemy_fly', { pos: [9, 7] }); // 1.0 from 角峰
|
||||
const n1 = log.length;
|
||||
assert.ok(h.runUntil(() => mine(n1).some((c) => c.name === 'damaged'), 4), `${label(f)}: another shot lands (a 医疗单元 in flight)`);
|
||||
const burst = mine(n1);
|
||||
assert.equal(burst[0].name, 'damaged', `${label(f)}: at an enemy — damage first`);
|
||||
assert.equal(burst[0].target, fly, `${label(f)}: the flyer (可对空)`);
|
||||
for (const c of burst.filter((x) => x.name === 'damaged')) assert.deepEqual([c.type, c.dmg.isAttack, c.dmg.isSplash, c.dmg.isSkill], ['true', true, false, true], `${label(f)}: 真实普通伤害`);
|
||||
assert.ok(burst.some((c) => c.name === 'heal' && c.target === yak), `${label(f)}: then 角峰 (1.0 away) healed`);
|
||||
assert.ok(!burst.some((c) => c.target === e), `${label(f)}: the dummy (2.2 from the flyer) untouched`);
|
||||
const slugs = h.hooksOf('statusApplied').filter((c) => c.source === u && c.status === 'sluggish');
|
||||
assert.ok(slugs.length >= 1 && slugs.every((c) => c.duration === 5 && c.target === fly), `${label(f)}: 停顿 5 s on the enemy struck`);
|
||||
h.runUntil(() => !u.skill.active, 40);
|
||||
assert.deepEqual(u.liveRangeGrid, formOf(tier, elite).rangeGrid, `${label(f)}: back to y-6 after 10 shots`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S3 破梏重生 (MANUAL, data DEFAULT): 35 s, ATK +, attack interval 1.30 s (−1.55 flat), 2 heal targets; with no anchor she stays', () => {
|
||||
for (const f of [[5, false], [6, true]]) {
|
||||
const [tier, elite] = f;
|
||||
const sk = skillOf(tier, elite, S3);
|
||||
assert.deepEqual([sk.duration, sk.bb.atk, sk.bb.base_attack_time, sk.spCost, sk.initSp, sk.trigger.rule, sk.overrideTokenKey],
|
||||
[35, elite ? 1.25 : 1.1, -1.55, elite ? 60 : 65, elite ? 30 : 25, 'DEFAULT', ANCHOR], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }, { uid: 4, chessId: TEXAS, row: 11, col: 5 }];
|
||||
const { h, u } = field({ tier, elite, skill: 2, others });
|
||||
const yak = h.unit(3), tx = h.unit(4);
|
||||
u.skill.gainSp(999);
|
||||
yak.hp = yak.s.maxHp * 0.3; tx.hp = tx.s.maxHp * 0.4;
|
||||
const n0 = heals(h, u).length;
|
||||
assert.ok(h.runUntil(() => u.skill.active, 4), label(f));
|
||||
approx(u.s.atk, u.base.atk * (1 + sk.bb.atk), `${label(f)}: ATK`);
|
||||
approx(u.s.interval, 2.85 - 1.55, `${label(f)}: 1.30 s`);
|
||||
h.runUntil(() => heals(h, u).length >= n0 + 2, 3);
|
||||
const two = heals(h, u).slice(n0, n0 + 2); // the cast's heal (same tick) already heals two
|
||||
assert.deepEqual(two.map((c) => c.target.id).sort(), [yak.id, tx.id].sort(), `${label(f)}: two targets per heal`);
|
||||
assert.equal(two[0].t, two[1].t, `${label(f)}: in one attack`);
|
||||
h.run(2);
|
||||
assert.deepEqual([u.tileR, u.tileC, u.hidden], [10, 4, false], `${label(f)}: no anchor: no move`);
|
||||
h.runUntil(() => !u.skill.active, 40);
|
||||
approx(u.s.interval, 2.85, `${label(f)}: interval back`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S3 with her anchor: 0.1 s after the cast she flies to it (hidden, 无敌, the skill time paused, 3.5 tiles/s; operators within 2.2 of her flight enter T2), the two latest-deployed operators of her range are withdrawn 0.6 s on and back at once; she lands on its tile facing its way; the anchor is withdrawn for the rest of the skill', () => {
|
||||
for (const f of [[5, false], [6, true]]) {
|
||||
const [tier, elite] = f;
|
||||
// deployment order (left column first, top row first): (11,5) → (10,5) → (9,5); (12,7) beside her flight
|
||||
const others = [{ uid: 3, chessId: YAK, row: 11, col: 5 }, { uid: 4, chessId: TEXAS, row: 10, col: 5 }, { uid: 5, chessId: PERFUMER, row: 9, col: 5 },
|
||||
{ uid: 6, chessId: 'chess_char_2_02_a', row: 12, col: 7 }];
|
||||
const { h, u, a } = field({ tier, elite, skill: 2, anchor: { row: 10, col: 9, dir: 'UP' }, others });
|
||||
const yak = h.unit(3), tx = h.unit(4), pf = h.unit(5), sil = h.unit(6);
|
||||
assert.ok(a.alive && u.mem.k2link === a, `${label(f)}: the anchor deployed at the start, linked to her`);
|
||||
assert.ok(!inRange(u, a), `${label(f)}: outside her range`);
|
||||
assert.ok(!sil.findBuff('talent:kalts2:regen'), `${label(f)}: 赫默 (12,7) is outside her range`);
|
||||
const seq = [yak.deploySeq, tx.deploySeq, pf.deploySeq];
|
||||
assert.ok(seq[0] < seq[1] && seq[1] < seq[2], label(f));
|
||||
u.skill.gainSp(999);
|
||||
yak.hp = yak.s.maxHp * 0.3;
|
||||
assert.ok(h.runUntil(() => u.skill.active, 4), label(f));
|
||||
const t0 = h.b.time;
|
||||
h.runUntil(() => u.hidden, 0.2);
|
||||
assert.ok(u.hidden && u.mem.k2flight, `${label(f)}: flying`);
|
||||
approx(h.b.time - t0, 0.1, `${label(f)}: 0.1 s after the cast`, 0.4);
|
||||
const tl = u.skill.timeLeft;
|
||||
assert.equal(h.b.dealDamage(null, u, { amount: 5000, type: 'true', sourceless: true }), 0, `${label(f)}: 无敌 in flight`);
|
||||
const T = 5 / 3.5;
|
||||
h.run(0.7);
|
||||
approx(u.skill.timeLeft, tl, `${label(f)}: skill time paused`, 1e-6);
|
||||
// the two latest deployed (护理 (9,5) and 德克萨斯 (10,5)) withdrawn and back on their tiles; 角峰 stays
|
||||
for (const x of [pf, tx]) {
|
||||
assert.ok(h.hooksOf('death').some((c) => c.unit === x && c.reason === 'retreat'), `${label(f)}: ${x.defId} withdrawn`);
|
||||
assert.ok(x.alive && x.deploySeq > seq[2], `${label(f)}: ${x.defId} back at once`);
|
||||
}
|
||||
assert.ok(!h.hooksOf('death').some((c) => c.unit === yak), `${label(f)}: 角峰 (the first deployed) stays`);
|
||||
assert.deepEqual([tx.tileR, tx.tileC, pf.tileR, pf.tileC], [10, 5, 9, 5], `${label(f)}: on their tiles`);
|
||||
h.runUntil(() => !u.hidden, T);
|
||||
assert.ok(!u.hidden, `${label(f)}: landed`);
|
||||
approx(h.b.time - t0, 0.1 + T, `${label(f)}: after ${T.toFixed(2)} s of flight`, 0.1);
|
||||
assert.deepEqual([u.tileR, u.tileC, u.dir], [10, 9, 'UP'], `${label(f)}: on the anchor's tile, facing its way`);
|
||||
assert.ok(!a.alive && !a.removed && a.mem.docked, `${label(f)}: the anchor withdrawn (its piece kept)`);
|
||||
assert.equal(u.mem.summonStock[ANCHOR], 0, `${label(f)}: stock emptied`);
|
||||
assert.ok(u.skill.active, label(f));
|
||||
approx(u.skill.timeLeft, tl, `${label(f)}: the remaining time kept`, 0.1);
|
||||
assert.equal(sil.findBuff('talent:kalts2:regen')?.mods.hpRegen, 50, `${label(f)}: 赫默, 2.0 from her flight, entered T2`);
|
||||
h.runUntil(() => !u.skill.active, 40);
|
||||
h.run(5);
|
||||
assert.ok(!a.alive, `${label(f)}: no anchor back after the skill (stock 0)`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('the anchor link: she leaves the field before her S3 ⇒ the anchor on the field no longer moves her (kalts2_s_3[on_token_born] is lost with her); with S1 / S2 the anchor piece never takes the field', () => {
|
||||
const { h, u, a } = field({ tier: 6, elite: true, skill: 2, anchor: { row: 10, col: 9 }, others: [{ uid: 3, chessId: YAK, row: 10, col: 5 }] });
|
||||
const yak = h.unit(3);
|
||||
assert.equal(u.mem.k2link, a);
|
||||
h.b.kill(u, null);
|
||||
h.step();
|
||||
assert.equal(u.mem.k2link, null, 'link lost');
|
||||
assert.ok(h.runUntil(() => u.alive, 90), 'she redeploys');
|
||||
assert.ok(a.alive, 'the anchor still stands');
|
||||
u.skill.gainSp(999);
|
||||
yak.hp = yak.s.maxHp * 0.3;
|
||||
assert.ok(h.runUntil(() => u.skill.active, 4));
|
||||
h.run(3);
|
||||
assert.deepEqual([u.tileR, u.tileC, u.hidden], [10, 4, false], 'no transport');
|
||||
done(h);
|
||||
for (const skill of [0, 1]) {
|
||||
const r = field({ skill, anchor: { row: 10, col: 9 } });
|
||||
r.h.run(2);
|
||||
assert.ok(!r.a.alive && r.a.deferDeploy, `S${skill + 1}: the anchor stays off the field`);
|
||||
done(r.h);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,377 @@
|
||||
// test/content/op_monstr.test.js — the 自选 operator kit of Mon3tr (char_4179_monstr, 6★ 链愈师; kit
|
||||
// server/sim/content/kits/ops/op-monstr.js) and her summon 重构体 (token_10050_monstr_prosts), fielded the production way (a
|
||||
// DIY slot + its `diy` pick; the 重构体 as a token piece of hers) in every form: tiers 5 / 6, normal (E2 Lv1, skill rank 4, no
|
||||
// module) and elite (E2 Lv60, rank 7) with no module or XAH-X 记忆存档 at stage 1 (tier 5) / 3 (tier 6). Every number is read
|
||||
// back from data/backups.json (the form of that slot status, the token's variant); the fidelity checklist of kits/README.md
|
||||
// item by item.
|
||||
// Run: node --test test/content/op_monstr.test.js
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
import { KITTED_CHARS, OPERATOR_KITS, KITS } from '../../server/sim/content/kits/index.js';
|
||||
import { diyPool, validateDiyPicks } from '../../shared/diy.js';
|
||||
|
||||
const load = (f) => JSON.parse(readFileSync(new URL(`../../data/${f}.json`, import.meta.url), 'utf8'));
|
||||
const CHESS = load('chess');
|
||||
const BACKUPS = load('backups');
|
||||
const MONSTR = 'char_4179_monstr';
|
||||
const PROSTS = 'token_10050_monstr_prosts';
|
||||
const FORMS = BACKUPS.units[MONSTR].forms;
|
||||
const TOKEN = BACKUPS.tokens[PROSTS];
|
||||
const SLOT = { 5: 'chess_char_5_diy1_a', 6: 'chess_char_6_diy1_a' };
|
||||
const XAHX = 'uniequip_002_monstr';
|
||||
const S1 = 'skchr_monstr_1', S2 = 'skchr_monstr_2', S3 = 'skchr_monstr_3';
|
||||
const statusKey = (tier, elite) => (elite ? (tier === 5 ? '2/60/7/1' : '2/60/7/3') : '2/1/4/0');
|
||||
const formOf = (tier, elite) => FORMS[statusKey(tier, elite)];
|
||||
const skillOf = (tier, elite, id) => formOf(tier, elite).skills.find((s) => s.skillId === id);
|
||||
const modOf = (tier, mod) => (mod ? formOf(tier, true).modules.find((m) => m.uniEquipId === mod) : null);
|
||||
const variantOf = (tier, elite) => TOKEN.variants[`${MONSTR}@${statusKey(tier, elite)}`];
|
||||
/** The trait / T1 blackboards a form fights with (module changes applied). */
|
||||
const traitOf = (tier, elite, mod) => (elite && mod ? modOf(tier, mod).traitOverride.bb : formOf(tier, elite).trait.bb);
|
||||
const t0Of = (tier, elite, mod) => (elite && mod && modOf(tier, mod).talentChanges.find((t) => t.talentIndex === 0)?.bb) || formOf(tier, elite).talents.find((t) => t.index === 0).bb;
|
||||
const approx = (a, b, msg, eps = 1e-6) => assert.ok(Math.abs(a - b) <= eps * Math.max(1, Math.abs(b)), `${msg}: ${a} vs ${b}`);
|
||||
const dummy = (key, o = {}) => enemyRec({ key, hp: 1e9, speed: 0, mass: 0, ...o });
|
||||
const ENEMIES = {
|
||||
enemy_dummy: dummy('enemy_dummy'), enemy_fly: dummy('enemy_fly', { motion: 'FLY' }),
|
||||
enemy_biter: dummy('enemy_biter', { atk: 300, range: 1.2, bat: 1 }),
|
||||
};
|
||||
/** Every 自选 form: [tier, elite, module]. */
|
||||
const FORMS_ALL = [[5, false, null], [6, false, null], ...[5, 6].flatMap((t) => [null, XAHX].map((m) => [t, true, m]))];
|
||||
const label = ([tier, elite, mod]) => `T${tier} ${elite ? 'elite' : 'normal'} ${mod ?? 'none'}`;
|
||||
const YAK = 'chess_char_1_02_a', ESTELLE = 'chess_char_1_12_a', SILENCE = 'chess_char_2_02_a', PERFUMER = 'chess_char_2_14_a';
|
||||
|
||||
/**
|
||||
* A battle with Mon3tr as uid 1 at (10, 4) facing RIGHT (`prosts`: her piece as uid 2), plus `others`; `log` = her heals /
|
||||
* damage in the order they happen.
|
||||
*/
|
||||
function field({ tier = 5, elite = false, mod = null, skill = 0, prosts = null, others = [], seed = 5, dp = 99 } = {}) {
|
||||
const units = [{ uid: 1, diy: { slot: SLOT[tier], charId: MONSTR, skillIndex: skill, uniEquipId: mod }, elite, row: 10, col: 4 }];
|
||||
if (prosts) units.push({ uid: 2, kind: 'token', tokenId: PROSTS, ownerUid: 1, row: prosts.row, col: prosts.col });
|
||||
const log = [];
|
||||
const h = makeBattle({
|
||||
defs: { enemies: ENEMIES }, timeLimit: 900, autoFinish: false, seed,
|
||||
flags: { dpInit: dp, dpPerSec: 0, dpMax: 999 },
|
||||
hooks: ['damaged', 'heal', 'skillStart', 'skillEnd', 'death', 'deploy', 'fatal'], captureNoisy: true,
|
||||
units: [...units, ...others],
|
||||
setup: (b) => {
|
||||
b.on('heal', (c) => { if (!c.opts?.regen) log.push({ name: 'heal', ...c, t: b.time }); }, { priority: -1000 });
|
||||
b.on('damaged', (c) => log.push({ name: 'damaged', ...c, t: b.time }), { priority: -1000 });
|
||||
},
|
||||
});
|
||||
h.step();
|
||||
return { h, u: h.unit(1), p: prosts ? h.unit(2) : null, log };
|
||||
}
|
||||
const healsOf = (log, u, from = 0) => log.slice(from).filter((c) => c.name === 'heal' && c.source === u);
|
||||
const setHp = (x, r) => { x.hp = x.s.maxHp * r; };
|
||||
function done(h) {
|
||||
checkInvariants(h.b);
|
||||
assert.equal(h.b.errors.length, 0, JSON.stringify(h.b.errors[0]));
|
||||
}
|
||||
|
||||
test('Mon3tr in every 自选 form: her operator kit (all three skills authored), stats + module attributes, y-2, one main heal per attack (her own chain), blocks 1, ground-targetable, 罗德岛, no 特质; her 重构体: the form\'s token stats, 禁疗, block 0, taunt −1', () => {
|
||||
assert.equal(OPERATOR_KITS[MONSTR], KITS[MONSTR]);
|
||||
for (const f of FORMS_ALL) {
|
||||
const [tier, elite, mod] = f;
|
||||
for (const skill of [0, 1, 2]) {
|
||||
const { h, u, p } = field({ tier, elite, mod, skill, prosts: { row: 10, col: 6 } });
|
||||
const form = formOf(tier, elite), m = elite ? modOf(tier, mod) : null;
|
||||
assert.deepEqual([u.def.charId, u.def.diyFor, u.skill.id, !!u.kit.generic, u.kit.skillSource], [MONSTR, SLOT[tier], form.skills[skill].skillId, false, 'skills'], label(f));
|
||||
assert.deepEqual([u.base.maxHp, u.base.atk, u.base.def, u.base.bat, u.base.cost], [form.stats.maxHp, form.stats.atk + (m?.attr.atk ?? 0), form.stats.def + (m?.attr.def ?? 0), 2.85, 18], `${label(f)}: stats`);
|
||||
assert.deepEqual([u.profile.dmgType, u.profile.heal, u.profile.attack, u.def.subProf, u.s.blockCnt], ['heal', { mode: 'single' }, 'ranged', 'chainhealer', 1], `${label(f)}: 链愈师`);
|
||||
assert.deepEqual(u.def.traitBb, traitOf(tier, elite, mod), `${label(f)}: the trait (XAH-X 0.85)`);
|
||||
assert.deepEqual(u.liveRangeGrid, form.rangeGrid, `${label(f)}: y-2`);
|
||||
assert.deepEqual([u.def.bonds, u.def.raw.garrisonIds, u.def.tokens, u.def.raw.nationId], [BACKUPS.diy.operators[MONSTR].bonds, [], [PROSTS], 'rhodes'], `${label(f)}: bonds / 特质 / summon`);
|
||||
assert.ok(!u.s.flags.liftoff && !u.s.flags.camou && !u.s.flags.stealth, `${label(f)}: enemies target her`);
|
||||
const ts = ((elite && mod && variantOf(tier, elite).byModule?.[mod]) || variantOf(tier, elite)).stats;
|
||||
assert.deepEqual([p.base.maxHp, p.base.def, p.base.atk, p.base.cost, p.base.respawnTime, p.s.blockCnt, p.s.taunt], [ts.maxHp, ts.def, 0, 3, 15, 0, -1], `${label(f)}: 重构体 stats`);
|
||||
assert.deepEqual([p.s.flags.noHeal, p.s.flags.noBlock, p.profile.noAttack, !!p.kit.generic, p.alive], [true, true, true, false, true], `${label(f)}: 禁疗, blocks nothing, no attack, on the field`);
|
||||
done(h);
|
||||
}
|
||||
}
|
||||
// E2 Lv1 1492 / 422 / 176, E2 Lv60 1653 / 492 / 206; XAH-X +40 / +30 → +65 / +40 ATK / DEF; the 重构体 4292 / 181 → 5048 / 208
|
||||
assert.deepEqual([FORMS['2/1/4/0'].stats.maxHp, FORMS['2/1/4/0'].stats.atk, FORMS['2/60/7/1'].stats.maxHp, FORMS['2/60/7/1'].stats.atk], [1492, 422, 1653, 492]);
|
||||
assert.deepEqual([modOf(5, XAHX).attr, modOf(6, XAHX).attr], [{ atk: 40, def: 30 }, { atk: 65, def: 40 }]);
|
||||
assert.deepEqual([variantOf(5, false).stats.maxHp, variantOf(5, false).stats.def, variantOf(6, true).stats.maxHp], [4292, 181, 5048]);
|
||||
assert.deepEqual([t0Of(5, true, XAHX).atk, t0Of(6, true, XAHX).atk, t0Of(6, true, XAHX)['attack@chain.extra_cnt'], t0Of(6, false, null).atk], [0.15, 0.25, 1, 0.15]);
|
||||
});
|
||||
|
||||
test('a 自选 pick: Mon3tr is offered at tiers 5 and 6 (she has a kit) and a roster with her and XAH-X passes validateDiyPicks', () => {
|
||||
const data = { chess: CHESS, backups: BACKUPS };
|
||||
assert.ok(KITTED_CHARS.includes(MONSTR));
|
||||
for (const t of [5, 6]) assert.ok(diyPool(t, { data, kitted: KITTED_CHARS }).includes(MONSTR), `tier ${t}`);
|
||||
assert.deepEqual(validateDiyPicks({ [SLOT[6]]: { charId: MONSTR, skillIndex: 2, uniEquipId: XAHX } }, { data, kitted: KITTED_CHARS }),
|
||||
{ ok: true, picks: { [SLOT[6]]: { charId: MONSTR, skillIndex: 2, uniEquipId: XAHX } } });
|
||||
});
|
||||
|
||||
test('trait 链愈师: the heal jumps to the lowest-HP-ratio injured ally of the 3×3 around the last one healed, ×0.75 per jump (XAH-X ×0.85), 3 units healed; a full-HP unit and one two tiles away are skipped', () => {
|
||||
for (const f of [[5, false, null], [6, true, null], [5, true, XAHX], [6, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const step = traitOf(tier, elite, mod)['attack@chain.atk_scale'];
|
||||
assert.equal(step, mod ? 0.85 : 0.75, label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }, { uid: 4, chessId: ESTELLE, row: 11, col: 6 }, { uid: 5, chessId: SILENCE, row: 12, col: 7 },
|
||||
{ uid: 6, chessId: PERFUMER, row: 9, col: 7 }, { uid: 7, chessId: 'chess_char_1_11_a', row: 10, col: 6 }];
|
||||
const { h, u, log } = field({ tier, elite, mod, skill: 2, others });
|
||||
const [A, B, C, F, G] = [3, 4, 5, 6, 7].map((i) => h.unit(i));
|
||||
setHp(A, 0.2); setHp(B, 0.5); setHp(C, 0.6); setHp(F, 0.3);
|
||||
const n0 = log.length;
|
||||
assert.ok(h.runUntil(() => healsOf(log, u, n0).length > 0, 4), label(f));
|
||||
h.step();
|
||||
const hs = healsOf(log, u, n0).filter((c) => c.t === healsOf(log, u, n0)[0].t);
|
||||
assert.deepEqual(hs.map((c) => c.target), [A, B, C], `${label(f)}: A → B → C (G full, F two tiles off)`);
|
||||
const atk = u.s.atk;
|
||||
approx(hs[0].amount, atk, `${label(f)}: the main heal`);
|
||||
approx(hs[1].amount, atk * step, `${label(f)}: ×${step}`);
|
||||
approx(hs[2].amount, atk * step * step, `${label(f)}: ×${step}²`);
|
||||
assert.ok(!healsOf(log, u, n0).some((c) => c.target === F || c.target === G), `${label(f)}: F / G not healed`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('T1 自我修复 free link: her 重构体 in a chain uses no target of the count and the next jump keeps the amount; XAH-X stage 3: one more jump without decay', () => {
|
||||
for (const f of [[5, false, null], [5, true, XAHX], [6, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const step = traitOf(tier, elite, mod)['attack@chain.atk_scale'];
|
||||
const extra = t0Of(tier, elite, mod)['attack@chain.extra_cnt'] ?? 0;
|
||||
assert.equal(extra, tier === 6 && mod ? 1 : 0, label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }, { uid: 4, chessId: ESTELLE, row: 11, col: 6 }, { uid: 5, chessId: SILENCE, row: 12, col: 7 }];
|
||||
const { h, u, p, log } = field({ tier, elite, mod, skill: 2, prosts: { row: 11, col: 5 }, others });
|
||||
const [A, B, C] = [3, 4, 5].map((i) => h.unit(i));
|
||||
setHp(A, 0.2); setHp(p, 0.3); setHp(B, 0.5); setHp(C, 0.6);
|
||||
const n0 = log.length;
|
||||
assert.ok(h.runUntil(() => healsOf(log, u, n0).length > 0, 4), label(f));
|
||||
const hs = healsOf(log, u, n0).filter((c) => c.t === healsOf(log, u, n0)[0].t);
|
||||
assert.deepEqual(hs.map((c) => c.target), [A, p, B, C], `${label(f)}: A → 重构体 → B → C (4 healed: the 重构体 is not counted)`);
|
||||
const atk = u.s.atk;
|
||||
approx(hs[1].amount, atk * step, `${label(f)}: the jump onto it decays`);
|
||||
approx(hs[2].amount, atk * step, `${label(f)}: the next one does not`);
|
||||
approx(hs[3].amount, atk * step * (extra ? 1 : step), `${label(f)}: then ${extra ? 'still no decay (下次跳跃也不衰减)' : 'it decays again'}`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('T1 自我修复 — the 重构体: only her heals reach it (禁疗), it loses 8 HP every 0.1 s from 1 s after it lands, every ally of its 3×3 ATK +15 % (XAH-X stage 3 +25 %; not 2 tiles away); gone ⇒ back 15 s later for 3 DP while she stands; withdrawn when she leaves, her redeployment readies it', () => {
|
||||
for (const f of [[5, false, null], [6, true, XAHX], [5, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const atkUp = t0Of(tier, elite, mod).atk;
|
||||
const others = [{ uid: 3, chessId: YAK, row: 11, col: 6 }, { uid: 4, chessId: ESTELLE, row: 12, col: 6 }, { uid: 5, chessId: SILENCE, row: 9, col: 3 }];
|
||||
const { h, u, p } = field({ tier, elite, mod, skill: 0, prosts: { row: 10, col: 6 }, others, dp: 50 });
|
||||
const yak = h.unit(3), tx = h.unit(4), sil = h.unit(5);
|
||||
h.run(0.3);
|
||||
approx(yak.s.atk, yak.base.atk * (1 + atkUp), `${label(f)}: 角峰 next to it ATK +${atkUp * 100} %`);
|
||||
approx(u.s.atk, u.base.atk, `${label(f)}: Mon3tr (two tiles off) nothing`);
|
||||
approx(tx.s.atk, tx.base.atk, `${label(f)}: 艾丝黛尔 (two rows off) nothing`);
|
||||
assert.equal(p.findBuff('talent:monstr:prostsAtk'), null, `${label(f)}: not itself`);
|
||||
// the 流失: none in the first second, then 80 per second
|
||||
const hp0 = p.hp;
|
||||
assert.equal(hp0, p.s.maxHp);
|
||||
h.run(0.6);
|
||||
assert.equal(p.hp, hp0, `${label(f)}: nothing before 1 s`);
|
||||
h.run(1.0);
|
||||
const lost = hp0 - p.hp;
|
||||
assert.ok(lost > 60 && lost < 100, `${label(f)}: ≈80 HP in the next second (${lost})`);
|
||||
assert.ok(h.hooksOf('damaged').some((c) => c.target === p && c.dmg?.tags?.includes('hpLoss')), `${label(f)}: a 流失`);
|
||||
// 禁疗: 赫默 never heals it; Mon3tr does
|
||||
setHp(p, 0.3);
|
||||
h.run(6);
|
||||
assert.equal(h.hooksOf('heal').filter((c) => c.source === sil && c.target === p).length, 0, `${label(f)}: no heal from 赫默`);
|
||||
assert.ok(h.hooksOf('heal').some((c) => c.source === u && c.target === p), `${label(f)}: Mon3tr heals it`);
|
||||
// destroyed: back 15 s later for 3 DP
|
||||
const dp0 = h.b.players[0].dp;
|
||||
h.b.kill(p, null);
|
||||
h.run(14.5);
|
||||
assert.ok(!p.alive, `${label(f)}: not before 15 s`);
|
||||
h.run(0.7);
|
||||
assert.ok(p.alive, `${label(f)}: back`);
|
||||
assert.deepEqual([p.tileR, p.tileC], [10, 6], `${label(f)}: on its tile`);
|
||||
approx(h.b.players[0].dp, dp0 - 3, `${label(f)}: 3 DP`);
|
||||
// she leaves: withdrawn; it comes with her redeployment at once
|
||||
h.b.kill(u, null);
|
||||
h.step();
|
||||
assert.ok(!p.alive && !p.removed, `${label(f)}: withdrawn with her`);
|
||||
assert.ok(h.runUntil(() => u.alive, 90), `${label(f)}: she redeploys`);
|
||||
assert.ok(h.runUntil(() => p.alive, 0.3), `${label(f)}: at once`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('T2 战术协同: each heal of hers gives its target and her ASPD +20 for 10 s (no stacking: refreshed); under S2 ×1.8 / ×2.3 — the strongest effect stays', () => {
|
||||
for (const f of [[5, false, null], [6, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const t1 = formOf(tier, elite).talents.find((t) => t.index === 1).bb;
|
||||
const s2 = skillOf(tier, elite, S2);
|
||||
assert.deepEqual([t1.attack_speed, t1.buff_duration, s2.bb.talent_scale], [20, 10, elite ? 2.3 : 1.8], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }];
|
||||
const { h, u, log } = field({ tier, elite, mod, skill: 1, others });
|
||||
const yak = h.unit(3);
|
||||
setHp(yak, 0.3);
|
||||
const n0 = log.length;
|
||||
assert.ok(h.runUntil(() => healsOf(log, u, n0).length > 0, 4), label(f));
|
||||
assert.deepEqual([yak.findBuff('talent:monstr:aspd')?.mods, u.findBuff('talent:monstr:aspd')?.mods], [{ aspd: 20 }, { aspd: 20 }], `${label(f)}: target and herself`);
|
||||
approx(yak.findBuff('talent:monstr:aspd').timeLeft, 10, `${label(f)}: 10 s`, 0.05);
|
||||
assert.equal(yak.s.aspd, yak.base.aspd + 20, label(f));
|
||||
// S2 (an enemy in her y-2): ×talent_scale
|
||||
h.spawn('enemy_dummy', { pos: [11, 6] });
|
||||
u.skill.gainSp(999);
|
||||
assert.ok(h.runUntil(() => u.skill.active, 2), `${label(f)}: S2`);
|
||||
setHp(yak, 0.3);
|
||||
const n1 = log.length;
|
||||
assert.ok(h.runUntil(() => healsOf(log, u, n1).some((c) => c.target === yak), 6), label(f));
|
||||
approx(yak.findBuff('talent:monstr:aspd').mods.aspd, 20 * s2.bb.talent_scale, `${label(f)}: ×${s2.bb.talent_scale}`);
|
||||
approx(u.findBuff('talent:monstr:aspd').mods.aspd, 20 * s2.bb.talent_scale, `${label(f)}: herself too`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S1 策略:超压链接 (AUTO, attack SP 5 / 3, data DEFAULT — the next heal): that heal is 135 % / 170 % ATK and heals 4 units', () => {
|
||||
for (const f of [[5, false, null], [6, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const sk = skillOf(tier, elite, S1);
|
||||
const step = traitOf(tier, elite, mod)['attack@chain.atk_scale'];
|
||||
assert.deepEqual([sk.skillType, sk.spType, sk.spCost, sk.initSp, sk.bb.heal_scale, sk.bb['chain.max_target'], sk.trigger.rule],
|
||||
['AUTO', 'INCREASE_WHEN_ATTACK', elite ? 3 : 5, 0, elite ? 1.7 : 1.35, 4, 'DEFAULT'], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 10, col: 5 }, { uid: 4, chessId: ESTELLE, row: 11, col: 6 }, { uid: 5, chessId: SILENCE, row: 12, col: 7 }, { uid: 6, chessId: PERFUMER, row: 12, col: 8 }];
|
||||
const { h, u, log } = field({ tier, elite, mod, skill: 0, others });
|
||||
const [A, B, C, D] = [3, 4, 5, 6].map((i) => h.unit(i));
|
||||
assert.deepEqual([u.skill.rule, u.skill.spType, u.skill.spCost], ['DEFAULT', 'attack', sk.spCost], label(f));
|
||||
u.skill.gainSp(999);
|
||||
setHp(A, 0.2); setHp(B, 0.4); setHp(C, 0.5); setHp(D, 0.6);
|
||||
const n0 = log.length;
|
||||
assert.ok(h.runUntil(() => u.skill.activations === 1, 4), `${label(f)}: cast as she heals`);
|
||||
const hs = healsOf(log, u, n0);
|
||||
assert.deepEqual(hs.slice(0, 4).map((c) => c.target), [A, B, C, D], `${label(f)}: 4 units`);
|
||||
const base = u.s.atk * sk.bb.heal_scale;
|
||||
hs.slice(0, 4).forEach((c, i) => approx(c.amount, base * step ** i, `${label(f)}: link ${i}`));
|
||||
assert.ok(!u.skill.active, `${label(f)}: spent on that heal`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S2 策略:超负荷 (MANUAL, data SKILL_RANGE on y-2: an enemy there; 30 s): her 重构体 in range is her heal target, even at full HP with nobody injured; 0.6 s after her attack healed it a second chain starts on it', () => {
|
||||
for (const f of [[5, false, null], [6, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const sk = skillOf(tier, elite, S2);
|
||||
assert.deepEqual([sk.duration, sk.trigger.rule, sk.rangeId, sk.spCost, sk.initSp], [30, 'SKILL_RANGE', 'y-2', 15, elite ? 8 : 7], label(f));
|
||||
const others = [{ uid: 3, chessId: YAK, row: 11, col: 5 }, { uid: 4, chessId: ESTELLE, row: 12, col: 6 }];
|
||||
const { h, u, p, log } = field({ tier, elite, mod, skill: 1, prosts: { row: 10, col: 5 }, others });
|
||||
const yak = h.unit(3), tx = h.unit(4);
|
||||
assert.equal(u.skill.rule, 'SKILL_RANGE');
|
||||
u.skill.gainSp(999);
|
||||
setHp(yak, 0.3);
|
||||
h.run(2);
|
||||
assert.equal(u.skill.activations, 0, `${label(f)}: an injured ally, no enemy: no cast`);
|
||||
h.spawn('enemy_dummy', { pos: [9, 6] });
|
||||
assert.ok(h.runUntil(() => u.skill.active, 0.5), `${label(f)}: an enemy in her y-2`);
|
||||
approx(u.skill.timeLeft, 30, `${label(f)}: 30 s`, 0.05);
|
||||
// 角峰 is lower, the 重构体 is still the target
|
||||
setHp(p, 0.9); setHp(yak, 0.2);
|
||||
let n0 = log.length;
|
||||
assert.ok(h.runUntil(() => healsOf(log, u, n0).length > 0, 4), label(f));
|
||||
const first = healsOf(log, u, n0);
|
||||
assert.equal(first[0].target, p, `${label(f)}: the 重构体 first`);
|
||||
const t0 = first[0].t;
|
||||
// the second chain 0.6 s later, on it again
|
||||
h.run(0.7);
|
||||
const again = healsOf(log, u, n0).filter((c) => c.t > t0 + 0.5);
|
||||
assert.ok(again.length > 0, `${label(f)}: a second chain`);
|
||||
approx(again[0].t - t0, 0.6, `${label(f)}: 0.6 s later`, 0.06);
|
||||
assert.equal(again[0].target, p, `${label(f)}: starting on the 重构体`);
|
||||
approx(again[0].amount, u.s.atk, `${label(f)}: her ATK`);
|
||||
// nobody injured: she heals the full-HP 重构体 anyway
|
||||
setHp(yak, 1); setHp(tx, 1); setHp(u, 1); p.hp = p.s.maxHp;
|
||||
n0 = log.length;
|
||||
h.run(4);
|
||||
assert.ok(healsOf(log, u, n0).some((c) => c.target === p), `${label(f)}: the full-HP 重构体 healed`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S3 策略:熔毁 (MANUAL, data DEFAULT, 25 s): refused without her 重构体; with it: it is withdrawn and she moves onto its tile — x-4, ATK +, interval 1.35 s, block 3, max HP +5000 (ratio kept), 流失 80/s; true damage on every ground enemy she blocks, a chain heal on herself (0.5 ATK, a free link) each attack; back at the end with SP 0, the 重构体 returns', () => {
|
||||
for (const f of [[5, false, null], [6, true, XAHX]]) {
|
||||
const [tier, elite, mod] = f;
|
||||
const sk = skillOf(tier, elite, S3);
|
||||
assert.deepEqual([sk.duration, sk.bb.atk, sk.bb.base_attack_time, sk.bb.block_cnt, sk.bb.max_hp, sk.bb.damage_per_second, sk.bb['attack@heal_scale'], sk.rangeId, sk.trigger.rule],
|
||||
[25, elite ? 2.8 : 2.5, -1.5, 2, 5000, 80, 0.5, 'x-4', 'DEFAULT'], label(f));
|
||||
// refused without the 重构体 (destroyed: its piece waits)
|
||||
const r0 = field({ tier, elite, mod, skill: 2, prosts: { row: 10, col: 6 }, others: [{ uid: 3, chessId: YAK, row: 11, col: 4 }] });
|
||||
r0.h.b.kill(r0.p, null);
|
||||
r0.u.skill.gainSp(999);
|
||||
setHp(r0.h.unit(3), 0.3);
|
||||
r0.h.run(3);
|
||||
assert.equal(r0.u.skill.activations, 0, `${label(f)}: no cast without the 重构体`);
|
||||
assert.ok(r0.u.skill.ready, `${label(f)}: still ready`);
|
||||
done(r0.h);
|
||||
const others = [{ uid: 3, chessId: YAK, row: 11, col: 4 }, { uid: 4, chessId: ESTELLE, row: 9, col: 7 }];
|
||||
const { h, u, p, log } = field({ tier, elite, mod, skill: 2, prosts: { row: 10, col: 6 }, others, dp: 50 });
|
||||
const yak = h.unit(3), tx = h.unit(4);
|
||||
const hpRatio = 0.8;
|
||||
setHp(u, hpRatio);
|
||||
u.skill.gainSp(999);
|
||||
setHp(yak, 0.3);
|
||||
assert.ok(h.runUntil(() => u.skill.active, 4), label(f));
|
||||
assert.ok(!p.alive && !p.removed, `${label(f)}: the 重构体 withdrawn (its piece waits)`);
|
||||
assert.deepEqual([u.tileR, u.tileC, u.ground], [10, 6, true], `${label(f)}: on its tile (a ground tile)`);
|
||||
assert.deepEqual(u.liveRangeGrid, sk.rangeGrid, `${label(f)}: x-4`);
|
||||
approx(u.s.atk, u.base.atk * (1 + sk.bb.atk), `${label(f)}: ATK`);
|
||||
approx(u.s.interval, 2.85 - 1.5, `${label(f)}: 1.35 s`);
|
||||
assert.equal(u.s.blockCnt, 3, `${label(f)}: block 3`);
|
||||
approx(u.s.maxHp, u.base.maxHp + 5000, `${label(f)}: max HP +5000`);
|
||||
approx(u.hpRatio, hpRatio, `${label(f)}: the ratio kept`, 1e-3);
|
||||
// the 流失
|
||||
const hpA = u.hp;
|
||||
h.run(1);
|
||||
approx(hpA - u.hp, 80, `${label(f)}: 80 per second`, 0.15);
|
||||
// the blocked: two ground enemies on her tile, a flyer beside her
|
||||
const g1 = h.spawn('enemy_dummy', { pos: [10, 6] });
|
||||
const g2 = h.spawn('enemy_dummy', { pos: [10, 6] });
|
||||
const fly = h.spawn('enemy_fly', { pos: [10, 7] });
|
||||
h.step();
|
||||
assert.deepEqual([g1.blockedBy, g2.blockedBy], [u, u], `${label(f)}: blocked`);
|
||||
const n0 = log.length;
|
||||
assert.ok(h.runUntil(() => log.slice(n0).some((c) => c.name === 'damaged' && c.source === u && c.target.side === 'enemy'), 3), label(f));
|
||||
h.step();
|
||||
const atkHits = log.slice(n0).filter((c) => c.name === 'damaged' && c.source === u && c.dmg?.isAttack);
|
||||
const id = atkHits[0].dmg.attackId;
|
||||
const one = atkHits.filter((c) => c.dmg.attackId === id);
|
||||
assert.deepEqual(one.map((c) => c.target).sort((a, b) => a.id - b.id), [g1, g2].sort((a, b) => a.id - b.id), `${label(f)}: both blocked enemies, not the flyer`);
|
||||
for (const c of one) { assert.equal(c.type, 'true', `${label(f)}: true damage`); approx(c.amount, u.s.atk, `${label(f)}: 100 % ATK`); }
|
||||
assert.ok(!log.slice(n0).some((c) => c.target === fly), `${label(f)}: ground only`);
|
||||
const self = log.slice(n0).find((c) => c.name === 'heal' && c.source === u && c.target === u);
|
||||
assert.ok(self, `${label(f)}: the attack heals herself`);
|
||||
approx(self.amount, u.s.atk * 0.5, `${label(f)}: 50 % ATK`);
|
||||
// the end: back on her tile, SP 0, the 重构体 returns (its 15 s are long past) for 3 DP
|
||||
const dp0 = h.b.players[0].dp;
|
||||
h.runUntil(() => !u.skill.active, 30);
|
||||
assert.deepEqual([u.tileR, u.tileC, u.skill.sp], [10, 4, 0], `${label(f)}: 【返回】, SP emptied`);
|
||||
assert.deepEqual(u.liveRangeGrid, formOf(tier, elite).rangeGrid, `${label(f)}: y-2 again`);
|
||||
approx(u.s.maxHp, u.base.maxHp, `${label(f)}: max HP back`);
|
||||
assert.ok(h.runUntil(() => p.alive, 0.5), `${label(f)}: the 重构体 back`);
|
||||
assert.deepEqual([p.tileR, p.tileC], [10, 6], label(f));
|
||||
approx(h.b.players[0].dp, dp0 - 3, `${label(f)}: 3 DP`);
|
||||
assert.ok(tx.alive, label(f));
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
test('S3 不死: a fatal blow leaves her at 1 HP (untargetable, blocking nothing), the skill ends on the next tick and she returns at ≤ 1 HP with 1 s of 不死; afterwards a fatal blow knocks her out', () => {
|
||||
const { h, u, p } = field({ tier: 6, elite: true, mod: XAHX, skill: 2, prosts: { row: 10, col: 6 }, others: [{ uid: 3, chessId: YAK, row: 11, col: 4 }] });
|
||||
u.skill.gainSp(999);
|
||||
setHp(h.unit(3), 0.3);
|
||||
assert.ok(h.runUntil(() => u.skill.active, 4));
|
||||
assert.deepEqual([u.tileR, u.tileC], [10, 6]);
|
||||
h.b.dealDamage(null, u, { amount: 1e7, type: 'true', sourceless: true });
|
||||
assert.deepEqual([u.alive, u.hp, u.s.flags.untargetable, u.s.flags.noBlock, u.skill.active], [true, 1, true, true, true], 'held at 1 HP');
|
||||
assert.ok(h.hooksOf('fatal').some((c) => c.unit === u && c.prevented));
|
||||
h.step();
|
||||
assert.ok(!u.skill.active, 'the skill ended');
|
||||
assert.deepEqual([u.tileR, u.tileC], [10, 4], 'returned');
|
||||
assert.ok(!u.s.flags.untargetable, 'targetable again');
|
||||
h.b.dealDamage(null, u, { amount: 1e7, type: 'true', sourceless: true });
|
||||
assert.ok(u.alive && u.hp <= 1, '1 s 不死 after the return');
|
||||
h.run(1.1);
|
||||
h.b.dealDamage(null, u, { amount: 1e7, type: 'true', sourceless: true });
|
||||
assert.ok(!u.alive, 'then a knock-out');
|
||||
assert.ok(!p.alive, 'her 重构体 off the field');
|
||||
done(h);
|
||||
});
|
||||
+242
-170
@@ -5699,250 +5699,322 @@
|
||||
"snaps": "e06bb91d 3d9603df 235a870d 610af988 e5adbf49 c087e51c 9580ba01 de2151cd 8ec08976 09a31624 b5a17549 01ba521e afdab2ca 7c0620a6 e7ef14a1 498df6e0 62fd682d cc89ff94 12c6a7a5"
|
||||
},
|
||||
"diy-132": {
|
||||
"about": "5_diy1_a=cdfend 5_diy2_b=cdfend 5_diy1_a=cguard 5_diy2_b=cguard 6_diy1_b=necras/S1 6_diy2_b=necras/S2 6_diy1_b=necras/S3 6_diy2_b=necras/S1/002_necras 6_diy1_b=necras/S2/002_necras 6_diy2_b=necras/S3/002_necras 5_diy1_a=csnipe 5_diy2_b=csnipe",
|
||||
"fast": true,
|
||||
"about": "6_diy1_b=necras/S1 6_diy2_b=necras/S2 6_diy1_b=necras/S3 6_diy2_b=necras/S1/002_necras 6_diy1_b=necras/S2/002_necras 6_diy2_b=necras/S3/002_necras 5_diy1_a=kalts2/S1 5_diy2_a=kalts2/S2 5_diy1_a=kalts2/S3 5_diy2_b=kalts2/S1 5_diy1_b=kalts2/S2 5_diy2_b=kalts2/S3",
|
||||
"fast": false,
|
||||
"kind": "normal",
|
||||
"stageId": "act2autochess_m04",
|
||||
"modeId": "mode_single_hard",
|
||||
"round": 13,
|
||||
"seed": 326579774,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 198.8,
|
||||
"ticks": 5964,
|
||||
"time": 198.97,
|
||||
"ticks": 5969,
|
||||
"reason": "cleared",
|
||||
"killed": 123,
|
||||
"total": 123,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 26,
|
||||
"rngDraws": 27,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":123,"total":123,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1100899,"bossDmg":0,"heal":80557,"deaths":2,"layerGains":{}}},
|
||||
"players": {"p1":{"killed":123,"total":123,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1103259,"bossDmg":0,"heal":59011,"deaths":0,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_5_diy1_a",2,null,213.74,1,7,0,380,1914,0,4,1],
|
||||
["p1",2,"op","chess_char_5_diy2_b",2,"none",2908,1,8,0,0,0,0,0,0],
|
||||
["p1",3,"op","chess_char_5_diy1_a",2,null,2564,1,6,0,0,0,0,0,0],
|
||||
["p1",4,"op","chess_char_5_diy2_b",2,"none",3334,1,9,0,0,0,0,0,0],
|
||||
["p1",5,"op","chess_char_6_diy1_b",0,"none",1762,1,3,1,13338,0,0,6,2],
|
||||
["p1",6,"op","chess_char_6_diy2_b",1,"none",1762,1,4,1,23910,0,0,13,1],
|
||||
["p1",7,"op","chess_char_6_diy1_b",2,"none",1762,1,11,6,59742,0,0,38,5],
|
||||
["p1",8,"op","chess_char_6_diy2_b",0,"uniequip_002_necras",57.23,1,13,9,71000,1905,0,61,6],
|
||||
["p1",9,"op","chess_char_6_diy1_b",1,"uniequip_002_necras",1962,1,5,2,13327,0,0,16,1],
|
||||
["p1",10,"op","chess_char_6_diy2_b",2,"uniequip_002_necras",1962,1,10,39,323654,0,0,108,8],
|
||||
["p1",11,"op","chess_char_5_diy1_a",2,null,1271,1,12,8,47570,0,0,178,3],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"none",0,0,97,3,30621,3020,0,84,1],
|
||||
["p1",1,"op","chess_char_6_diy1_b",0,"none",1762,1,3,0,3163,0,0,6,1],
|
||||
["p1",2,"op","chess_char_6_diy2_b",1,"none",1762,1,4,1,18528,0,0,3,1],
|
||||
["p1",3,"op","chess_char_6_diy1_b",2,"none",1762,1,11,3,39051,0,0,44,5],
|
||||
["p1",4,"op","chess_char_6_diy2_b",0,"uniequip_002_necras",1962,1,13,9,53451,2143,22,46,6],
|
||||
["p1",5,"op","chess_char_6_diy1_b",1,"uniequip_002_necras",1962,1,5,1,2988,0,0,4,1],
|
||||
["p1",6,"op","chess_char_6_diy2_b",2,"uniequip_002_necras",1962,1,8,37,328672,0,0,110,8],
|
||||
["p1",7,"op","chess_char_5_diy1_a",0,null,1741.25,1,12,0,0,0,939,4,1],
|
||||
["p1",8,"op","chess_char_5_diy2_a",1,null,1741.25,1,14,5,82935,0,473,12,1],
|
||||
["p1",9,"op","chess_char_5_diy1_a",2,null,1741.25,1,9,0,0,0,0,0,0],
|
||||
["p1",10,"op","chess_char_5_diy2_b",0,"none",1928.75,1,6,0,0,0,0,0,0],
|
||||
["p1",11,"op","chess_char_5_diy1_b",1,"none",1928.75,1,7,4,32361,0,0,7,1],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"none",1928.75,1,10,0,0,0,709,3,0],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
["p1","~1","token","token_10043_necras_skeltn",null,null,2651.32,0,24,2,3785,4349,4008,10,null],
|
||||
["p1","~2","token","token_10043_necras_skeltn",null,null,1744,0,29,0,566,0,0,2,null],
|
||||
["p1","~3","token","token_10043_necras_skeltn",null,null,9417.6,1,30,1,3565,1716,2080,3,4],
|
||||
["p1","~4","token","token_10043_necras_skeltn",null,null,0,0,31,0,4439,5232,0,6,0],
|
||||
["p1","~5","token","token_10043_necras_skeltn",null,null,3488,0,32,0,0,0,0,0,null],
|
||||
["p1","~6","token","token_10043_necras_skeltn",null,null,3488,1,39,0,6254,8534,8534,12,null],
|
||||
["p1","~7","token","token_10043_necras_skeltn",null,null,3459.5,0,40,0,3191,1182,2022,9,0],
|
||||
["p1","~8","token","token_10043_necras_skeltn",null,null,3488,1,41,0,9098,57,57,19,null],
|
||||
["p1","~9","token","token_10043_necras_skeltn",null,null,13603.2,1,45,43,416415,11542,14835,44,7],
|
||||
["p1","~10","token","token_10043_necras_skeltn",null,null,0,0,46,0,3745,3488,0,7,null],
|
||||
["p1","~11","token","token_10043_necras_skeltn",null,null,3488,1,49,1,554,0,0,3,null],
|
||||
["p1","~12","token","token_10043_necras_skeltn",null,null,3488,1,50,0,0,0,0,0,null],
|
||||
["p1","~13","token","token_10043_necras_skeltn",null,null,3459.5,0,52,0,231,28,0,1,0],
|
||||
["p1","~14","token","token_10043_necras_skeltn",null,null,0,0,53,0,231,1744,0,1,0],
|
||||
["p1","~15","token","token_10043_necras_skeltn",null,null,1417.62,0,54,1,6384,5341,3270,13,0],
|
||||
["p1","~16","token","token_10043_necras_skeltn",null,null,2683.81,0,58,0,5063,804,0,10,0],
|
||||
["p1","~17","token","token_10043_necras_skeltn",null,null,1371.71,0,67,0,173,372,0,1,0],
|
||||
["p1","~18","token","token_10043_necras_skeltn",null,null,2423.35,0,69,1,6119,10888,9824,12,0],
|
||||
["p1","~19","token","token_10043_necras_skeltn",null,null,1744,0,93,0,243,0,0,1,null],
|
||||
["p1","~20","token","token_10043_necras_skeltn",null,null,0,0,94,0,243,1744,0,1,null],
|
||||
["p1","~21","token","token_10043_necras_skeltn",null,null,0,0,95,0,694,1744,0,3,null],
|
||||
["p1","~22","token","token_10043_necras_skeltn",null,null,3488,0,96,0,231,0,0,1,0],
|
||||
["p1","~23","token","token_10043_necras_skeltn",null,null,3459.5,0,98,1,6252,8879,8851,14,0],
|
||||
["p1","~24","token","token_10043_necras_skeltn",null,null,1451.54,0,100,0,5318,5860,3824,10,0],
|
||||
["p1","~25","token","token_10043_necras_skeltn",null,null,1247.61,0,105,0,462,496,0,2,0],
|
||||
["p1","~26","token","token_10043_necras_skeltn",null,null,3431.01,0,112,0,5306,6183,7477,12,0],
|
||||
["p1","~27","token","token_10043_necras_skeltn",null,null,1083.02,0,113,0,3029,2729,619,9,0],
|
||||
["p1","~28","token","token_10043_necras_skeltn",null,null,1451.54,0,130,0,3387,2407,541,7,null],
|
||||
["p1","~29","token","token_10043_necras_skeltn",null,null,0,0,136,2,4024,6576,4020,10,0],
|
||||
["p1","~30","token","token_10043_necras_skeltn",null,null,27.29,0,139,0,6115,11686,8692,12,0],
|
||||
["p1","~31","token","token_10043_necras_skeltn",null,null,0,0,147,0,3006,3488,0,6,null],
|
||||
["p1","~32","token","token_10043_necras_skeltn",null,null,1495.81,0,154,0,231,248,0,1,0],
|
||||
["p1","~33","token","token_10043_necras_skeltn",null,null,1744,1,155,0,0,0,0,0,0],
|
||||
["p1","~34","token","token_10043_necras_skeltn",null,null,0,0,156,0,647,1744,0,2,0],
|
||||
["p1","~35","token","token_10043_necras_skeltn",null,null,0,0,160,0,2774,3716,352,6,0],
|
||||
["p1","~36","token","token_10043_necras_skeltn",null,null,1619.9,0,165,0,231,124,0,1,0],
|
||||
["p1","~37","token","token_10043_necras_skeltn",null,null,1619.9,0,167,0,231,124,0,1,0],
|
||||
["p1","~38","token","token_10043_necras_skeltn",null,null,3431.01,1,168,2,4656,749,940,11,0],
|
||||
["p1","~39","token","token_10043_necras_skeltn",null,null,3459.5,1,170,0,462,391,610,1,0]
|
||||
["p1","~1","token","token_10043_necras_skeltn",null,null,1814.64,0,24,1,3834,4292,3115,9,null],
|
||||
["p1","~2","token","token_10043_necras_skeltn",null,null,0,0,29,0,462,1744,0,2,null],
|
||||
["p1","~3","token","token_10043_necras_skeltn",null,null,0,0,32,0,231,1744,0,1,null],
|
||||
["p1","~4","token","token_10043_necras_skeltn",null,null,1744,1,34,0,879,0,0,3,null],
|
||||
["p1","~5","token","token_10043_necras_skeltn",null,null,0,0,35,3,7649,5232,0,6,1],
|
||||
["p1","~6","token","token_10043_necras_skeltn",null,null,1744,1,36,0,0,0,0,0,null],
|
||||
["p1","~7","token","token_10043_necras_skeltn",null,null,643.36,0,37,1,5198,11243,8399,11,0],
|
||||
["p1","~8","token","token_10043_necras_skeltn",null,null,3488,1,38,0,0,0,0,0,null],
|
||||
["p1","~9","token","token_10043_necras_skeltn",null,null,0,0,45,0,324,1744,0,1,null],
|
||||
["p1","~10","token","token_10043_necras_skeltn",null,null,3488,0,46,0,971,0,0,2,0],
|
||||
["p1","~11","token","token_10043_necras_skeltn",null,null,3431.01,0,47,0,2173,2567,2510,4,null],
|
||||
["p1","~12","token","token_10043_necras_skeltn",null,null,1744,1,48,0,0,0,0,0,null],
|
||||
["p1","~13","token","token_10043_necras_skeltn",null,null,13582.7,1,51,52,477248,23721,24624,43,7],
|
||||
["p1","~14","token","token_10043_necras_skeltn",null,null,2594.33,0,53,1,7004,3981,3211,15,0],
|
||||
["p1","~15","token","token_10043_necras_skeltn",null,null,3488,0,55,0,324,0,0,1,0],
|
||||
["p1","~16","token","token_10043_necras_skeltn",null,null,3384.16,0,69,1,4897,719,615,10,null],
|
||||
["p1","~17","token","token_10043_necras_skeltn",null,null,0,0,70,0,879,1744,0,3,0],
|
||||
["p1","~18","token","token_10043_necras_skeltn",null,null,3384.16,0,83,0,2451,464,484,6,0],
|
||||
["p1","~19","token","token_10043_necras_skeltn",null,null,3488,0,84,0,0,0,0,0,0],
|
||||
["p1","~20","token","token_10043_necras_skeltn",null,null,3431.01,0,94,0,1283,2902,2845,3,null],
|
||||
["p1","~21","token","token_10043_necras_skeltn",null,null,0,0,98,0,231,3488,0,1,0],
|
||||
["p1","~22","token","token_10043_necras_skeltn",null,null,3488,0,99,0,0,0,0,0,0],
|
||||
["p1","~23","token","token_10043_necras_skeltn",null,null,3459.5,0,100,0,0,85,57,0,0],
|
||||
["p1","~24","token","token_10043_necras_skeltn",null,null,3184.68,0,110,0,5445,4970,4838,13,0],
|
||||
["p1","~25","token","token_10043_necras_skeltn",null,null,3488,0,112,0,0,0,0,0,0],
|
||||
["p1","~26","token","token_10043_necras_skeltn",null,null,3384.16,0,120,1,4897,719,615,10,null],
|
||||
["p1","~27","token","token_10043_necras_skeltn",null,null,0,0,136,2,2938,4282,794,8,0],
|
||||
["p1","~28","token","token_10043_necras_skeltn",null,null,3459.5,0,138,1,1165,57,28,3,0],
|
||||
["p1","~29","token","token_10043_necras_skeltn",null,null,3488,1,144,0,1283,2845,2845,3,null],
|
||||
["p1","~30","token","token_10043_necras_skeltn",null,null,1744,0,148,0,0,0,0,0,0],
|
||||
["p1","~31","token","token_10043_necras_skeltn",null,null,3241.67,0,153,0,7838,1201,955,17,0],
|
||||
["p1","~32","token","token_10043_necras_skeltn",null,null,3488,0,154,0,0,0,0,0,0],
|
||||
["p1","~33","token","token_10043_necras_skeltn",null,null,3431.01,1,167,0,2509,619,934,6,0],
|
||||
["p1","~34","token","token_10043_necras_skeltn",null,null,3488,1,170,0,0,0,0,0,0]
|
||||
],
|
||||
"enemies": {"enemy_1019_jshoot_2":[57,57,0,13712,255302],"enemy_1299_ymkilr_2":[27,27,0,103761,492687],"enemy_1422_lrsldr_2":[39,39,0,3532,355830]},
|
||||
"hooks": [1,177,5964,1154,1154,1519,1519,139,134,155,39,39,0,31791,2,137,123,0,1,134,0,0,0],
|
||||
"events": {"atk":1154,"deploy":52,"die":155,"dmg":1519,"engage":45,"fx":323,"heal":88,"skill":78,"spawn":176,"status":3},
|
||||
"fx": {"aoe":143,"grow":139,"sleep":2,"summon":39},
|
||||
"snaps": "fca07158 7e491823 1379df7d 43eca66a 1670212d 34467249 ce2fd1dd 106168b3 2159db6f 66183b01 3f2c0eb3 117ea2a7 1cd2cb58 bec7ece2 6863bf40 ef5528a5 10c16399 2b2413f1 c9fee135 a483f71b"
|
||||
"enemies": {"enemy_1019_jshoot_2":[57,57,0,8746,255302],"enemy_1299_ymkilr_2":[27,27,0,70163,492687],"enemy_1422_lrsldr_2":[39,39,0,3595,355830]},
|
||||
"hooks": [1,171,5969,793,793,1146,1146,187,130,149,33,32,17,24902,40,130,123,0,1,130,0,0,0],
|
||||
"events": {"atk":793,"deploy":46,"die":149,"dmg":1145,"engage":32,"fx":337,"heal":85,"skill":65,"spawn":171,"status":25},
|
||||
"fx": {"aoe":122,"grow":133,"healAoe":17,"shield":23,"sleep":2,"summon":34,"takeoff":6},
|
||||
"snaps": "4f2919b7 5c182264 5bf39cab e2a0ece8 080eb0d4 bc7de66d c8fcb067 50b279d1 84890128 1a5055e8 a19a801b 8dbbbcfd bc88a159 d86c8768 ae16bbe2 ffed3a5a 672f95ca 7f7d9779 0dcbf1c6 7e1daa07"
|
||||
},
|
||||
"diy-133": {
|
||||
"about": "5_diy1_a=acfend 5_diy2_b=acfend 5_diy1_a=acpion 5_diy2_b=acpion 5_diy1_a=acguad 5_diy2_b=acguad 5_diy1_a=ccast 5_diy2_b=ccast 5_diy1_a=cmedic 5_diy2_b=cmedic 5_diy1_a=csuppo 5_diy2_b=csuppo",
|
||||
"about": "6_diy1_a=kalts2/S1 6_diy2_a=kalts2/S2 6_diy1_a=kalts2/S3 6_diy2_b=kalts2/S1 6_diy1_b=kalts2/S2 6_diy2_b=kalts2/S3 5_diy1_a=monstr/S1 5_diy2_a=monstr/S2 5_diy1_a=monstr/S3 5_diy2_b=monstr/S1 5_diy1_b=monstr/S2 5_diy2_b=monstr/S3",
|
||||
"fast": false,
|
||||
"kind": "normal",
|
||||
"stageId": "act1autochess_m01",
|
||||
"modeId": "mode_multi_normal",
|
||||
"round": 12,
|
||||
"round": 13,
|
||||
"seed": 953190138,
|
||||
"waveId": "act1autochess_h05",
|
||||
"time": 218.27,
|
||||
"ticks": 6548,
|
||||
"reason": "cleared",
|
||||
"killed": 173,
|
||||
"total": 243,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 230,
|
||||
"ticks": 6900,
|
||||
"reason": "timeout",
|
||||
"killed": 70,
|
||||
"total": 123,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 117,
|
||||
"rngDraws": 0,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":173,"total":243,"perfect":false,"leaks":70,"leaksUncounted":0,"coins":0,"dmg":509327,"bossDmg":0,"heal":35627,"deaths":7,"layerGains":{}}},
|
||||
"players": {"p1":{"killed":70,"total":123,"perfect":false,"leaks":53,"leaksUncounted":0,"coins":0,"dmg":1875047,"bossDmg":0,"heal":434706,"deaths":1,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_5_diy1_a",2,null,0,0,198,1,9062,5208,0,41,1],
|
||||
["p1",2,"op","chess_char_5_diy2_b",2,"uniequip_002_acfend",3121.56,1,252,5,24943,3620,0,81,1],
|
||||
["p1",3,"op","chess_char_5_diy1_a",2,null,1529,1,261,6,18839,7756,0,40,2],
|
||||
["p1",4,"op","chess_char_5_diy2_b",2,"uniequip_002_acpion",1904,1,151,100,195205,11634,0,107,6],
|
||||
["p1",5,"op","chess_char_5_diy1_a",2,null,2638,1,15,14,93664,21198,0,140,3],
|
||||
["p1",6,"op","chess_char_5_diy2_b",2,"uniequip_002_acguad",3045.96,1,233,10,25732,3776,0,42,2],
|
||||
["p1",7,"op","chess_char_5_diy1_a",2,null,1133,1,4,8,15364,0,0,83,3],
|
||||
["p1",8,"op","chess_char_5_diy2_b",2,"none",1360,1,5,16,47409,0,0,148,4],
|
||||
["p1",9,"op","chess_char_5_diy1_a",2,null,1218,1,11,0,0,0,19362,59,3],
|
||||
["p1",10,"op","chess_char_5_diy2_b",2,"none",1374,1,13,0,0,0,16264,58,3],
|
||||
["p1",11,"op","chess_char_5_diy1_a",2,null,932,1,6,12,18431,0,0,107,3],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"none",1131,1,14,1,60680,0,0,133,3],
|
||||
["p1",1,"op","chess_char_6_diy1_a",0,null,1741.25,1,4,0,0,0,37709,78,3],
|
||||
["p1",2,"op","chess_char_6_diy2_a",1,null,1741.25,1,5,48,1100372,0,16381,73,4],
|
||||
["p1",3,"op","chess_char_6_diy1_a",2,null,1741.25,1,11,0,0,0,53796,100,2],
|
||||
["p1",4,"op","chess_char_6_diy2_b",0,"none",1928.75,1,13,0,0,0,38862,81,3],
|
||||
["p1",5,"op","chess_char_6_diy1_b",1,"none",1928.75,1,6,22,774676,0,16942,61,3],
|
||||
["p1",6,"op","chess_char_6_diy2_b",2,"none",1928.75,1,14,0,0,0,30210,85,3],
|
||||
["p1",7,"op","chess_char_5_diy1_a",0,null,1291.59,1,139,0,0,45164,32152,57,8],
|
||||
["p1",8,"op","chess_char_5_diy2_a",1,null,531.03,1,9,0,0,110638,50299,95,4],
|
||||
["p1",9,"op","chess_char_5_diy1_a",2,null,1492,1,10,0,0,104050,43027,89,0],
|
||||
["p1",10,"op","chess_char_5_diy2_b",0,"none",1653,1,12,0,0,22233,51356,78,19],
|
||||
["p1",11,"op","chess_char_5_diy1_b",1,"none",1653,1,15,0,0,64626,22360,57,3],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"none",1653,1,8,0,0,90649,41613,86,0],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {
|
||||
"enemy_1195_sfyin": [45,26,19,13482,144786],
|
||||
"enemy_1196_msfyin": [52,45,7,0,91],
|
||||
"enemy_1197_sfshu": [39,28,11,13145,181727],
|
||||
"enemy_1198_msfshu": [84,61,23,0,195],
|
||||
"enemy_1199_sfjin": [12,11,1,26565,182424],
|
||||
"enemy_1200_msfjin": [11,2,9,0,104]
|
||||
},
|
||||
"hooks": [1,264,6548,1492,1492,1952,1915,117,180,250,34,33,0,39609,222,69,243,70,1,180,0,0,37],
|
||||
"events": {"atk":1492,"deploy":18,"die":180,"dmg":1915,"engage":10,"fx":323,"heal":117,"leak":70,"skill":67,"spawn":258,"status":456},
|
||||
"fx": {"aoe":52,"dodge":37,"dp":114,"slash":55,"summon":65},
|
||||
"snaps": "bc6da2c9 ceebca03 cdedd66c 4125c5f9 0142c8a8 b8d589a4 0abad4db 48f74225 831cf5aa c20fb442 0f7f866d 1b522268 513dbd00 272f78e4 f8c39620 6413c2bd 7ee6e8b4 83bfc984 c53e60c8 bee2518e 35f20f3b 28db8fee"
|
||||
"enemies": {"enemy_1006_shield_3":[21,0,18,340735,587877],"enemy_1043_zomsbr":[66,61,3,26508,478625],"enemy_1402_tgshd_2":[36,9,25,70117,808545]},
|
||||
"hooks": [1,139,6900,1419,1419,1144,1144,12782,71,117,52,48,68,23516,593,11,123,46,1,71,0,0,0],
|
||||
"events": {"atk":1688,"deploy":13,"die":71,"dmg":1133,"engage":2,"fx":466,"heal":1153,"leak":46,"skill":100,"spawn":138,"status":207},
|
||||
"fx": {"aoe":66,"expose":285,"healAoe":66,"shield":43,"takeoff":6},
|
||||
"snaps": "0dc07611 2d775bde 1fc01304 f7fccabc ac9092fe 6b03ce6b 05b51726 12e75ee8 cebb88e2 ffd2c168 1292c3ef dc98d628 be587ff3 4fffef3a 83b81feb 3e1c3aa5 0546fdbd c932bd72 0ace38b0 0210eaa1 091b8de5 53995786 47763ff2"
|
||||
},
|
||||
"diy-134": {
|
||||
"about": "5_diy1_a=sharp2 5_diy2_b=sharp2 5_diy1_a=acspec 5_diy2_b=acspec 5_diy1_a=acnipe 5_diy2_b=acnipe 5_diy1_a=accast 5_diy2_b=accast 5_diy1_a=acmedc 5_diy2_b=acmedc 5_diy1_a=acsupo 5_diy2_b=acsupo",
|
||||
"about": "5_diy1_b=monstr/S1/002_monstr 5_diy2_b=monstr/S2/002_monstr 5_diy1_b=monstr/S3/002_monstr 6_diy1_a=monstr/S1 6_diy2_a=monstr/S2 6_diy1_a=monstr/S3 6_diy2_b=monstr/S1 6_diy1_b=monstr/S2 6_diy2_b=monstr/S3 6_diy1_b=monstr/S1/002_monstr 6_diy2_b=monstr/S2/002_monstr 6_diy1_b=monstr/S3/002_monstr",
|
||||
"fast": false,
|
||||
"kind": "normal",
|
||||
"stageId": "act1autochess_m02",
|
||||
"modeId": "mode_multi_hard",
|
||||
"round": 12,
|
||||
"round": 13,
|
||||
"seed": 3608019556,
|
||||
"waveId": "act1autochess_h05",
|
||||
"time": 208.03,
|
||||
"ticks": 6241,
|
||||
"reason": "cleared",
|
||||
"killed": 2,
|
||||
"total": 60,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 230,
|
||||
"ticks": 6900,
|
||||
"reason": "timeout",
|
||||
"killed": 0,
|
||||
"total": 87,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 1049,
|
||||
"rngDraws": 0,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":2,"total":60,"perfect":false,"leaks":58,"leaksUncounted":0,"coins":0,"dmg":558618,"bossDmg":0,"heal":29304,"deaths":21,"layerGains":{}}},
|
||||
"players": {"p1":{"killed":0,"total":87,"perfect":false,"leaks":87,"leaksUncounted":0,"coins":0,"dmg":0,"bossDmg":0,"heal":114453,"deaths":14,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_5_diy1_a",0,null,0,0,61,0,13900,3730,0,16,1],
|
||||
["p1",2,"op","chess_char_5_diy2_b",0,"uniequip_002_sharp2",0,0,68,1,55591,4748,0,37,2],
|
||||
["p1",3,"op","chess_char_5_diy1_a",2,null,1154,1,87,0,942,5770,0,24,0],
|
||||
["p1",4,"op","chess_char_5_diy2_b",2,"uniequip_002_acspec",1448,1,85,0,4428,7240,0,24,0],
|
||||
["p1",5,"op","chess_char_5_diy1_a",2,null,1272,1,5,0,14792,0,0,181,2],
|
||||
["p1",6,"op","chess_char_5_diy2_b",2,"uniequip_002_acnipe",1587,1,8,0,30614,0,0,177,2],
|
||||
["p1",7,"op","chess_char_5_diy1_a",2,null,1372,1,6,1,137220,0,0,51,2],
|
||||
["p1",8,"op","chess_char_5_diy2_b",2,"uniequip_002_accast",1749,1,9,0,261767,0,0,77,2],
|
||||
["p1",9,"op","chess_char_5_diy1_a",2,null,1224,1,88,0,0,3201,3296,8,1],
|
||||
["p1",10,"op","chess_char_5_diy2_b",2,"uniequip_002_acmedc",0,0,11,0,0,15092,26008,21,1],
|
||||
["p1",11,"op","chess_char_5_diy1_a",2,null,0,0,71,0,21082,17194,0,40,1],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"uniequip_002_acsupo",1430,1,89,0,18281,2860,0,22,1]
|
||||
["p1",1,"op","chess_char_5_diy1_b",0,"uniequip_002_monstr",1653,1,4,0,0,1231,28656,62,15],
|
||||
["p1",2,"op","chess_char_5_diy2_b",1,"uniequip_002_monstr",1653,1,109,0,0,4970,13758,46,3],
|
||||
["p1",3,"op","chess_char_5_diy1_b",2,"uniequip_002_monstr",1653,1,5,0,0,0,11440,23,0],
|
||||
["p1",4,"op","chess_char_6_diy1_a",0,null,1492,1,8,0,0,2305,3149,4,0],
|
||||
["p1",5,"op","chess_char_6_diy2_a",1,null,0,0,90,0,0,12009,9102,20,2],
|
||||
["p1",6,"op","chess_char_6_diy1_a",2,null,0,0,87,0,0,12009,5260,16,0],
|
||||
["p1",7,"op","chess_char_6_diy2_b",0,"none",1653,1,68,0,0,21567,9022,26,6],
|
||||
["p1",8,"op","chess_char_6_diy1_b",1,"none",1653,1,110,0,0,11160,7905,29,2],
|
||||
["p1",9,"op","chess_char_6_diy2_b",2,"none",1653,1,103,0,0,18821,11079,42,0],
|
||||
["p1",10,"op","chess_char_6_diy1_b",0,"uniequip_002_monstr",1653,1,61,0,0,23049,10188,42,10],
|
||||
["p1",11,"op","chess_char_6_diy2_b",1,"uniequip_002_monstr",0,0,97,0,0,12660,3142,11,1],
|
||||
["p1",12,"op","chess_char_6_diy1_b",2,"uniequip_002_monstr",1653,1,108,0,0,17168,1752,7,0]
|
||||
],
|
||||
"enemies": {"enemy_1045_hammer_2":[12,0,12,4838,121840],"enemy_1061_zomshd":[48,2,46,54996,436778]},
|
||||
"hooks": [1,89,6241,759,759,1939,1993,26777,23,81,15,25,0,22089,2415,24,60,58,1,23,0,0,0],
|
||||
"events": {"atk":1095,"deploy":29,"die":23,"dmg":1979,"engage":10,"fx":13,"heal":53,"leak":58,"skill":54,"spawn":72,"status":266},
|
||||
"fx": {"aoe":8,"dodge":4,"spGain":1},
|
||||
"snaps": "eb37fe41 2feae3f0 5c31d8ff f72ff3aa 32f606ba 526ce9c0 49700653 5006595f c5c6846e 6d2b9b8e 4b024976 e92d04be 471cd084 7f606005 f119fdc0 5677942e 4fe9bcbf 9d0af4bf 4077bae2 9168ce11 ab027285"
|
||||
"enemies": {"enemy_10065_ftzlc":[51,0,48,51257,0],"enemy_10067_ftsjc":[36,0,36,85694,0]},
|
||||
"hooks": [1,110,6900,609,609,312,399,368,14,98,39,38,0,225,0,16,87,84,1,14,0,14,0],
|
||||
"events": {"atk":649,"deploy":23,"die":14,"dmg":399,"fx":46,"heal":368,"leak":84,"skill":77,"spawn":99,"status":28},
|
||||
"fx": {"burst":14,"explode":6,"ignite":26},
|
||||
"snaps": "5c7b8a79 01547e8a 90ab5c33 6e74c1c0 586848ce 4f19fce8 17b8d4e5 0a916bf2 ac2ca570 3ce8dfbe 64bd6f35 333142a2 f63439de a966c63b 573934d5 850c592d 0e478159 94f44eaf a3a69e69 6352b8d4 a04ce541 e0c1c7a9 7367d229"
|
||||
},
|
||||
"diy-135": {
|
||||
"about": "6_diy1_a=acfend 6_diy2_b=acfend 6_diy1_a=acpion 6_diy2_b=acpion 6_diy1_a=acguad 6_diy2_b=acguad 6_diy1_a=acnipe 6_diy2_b=acnipe 6_diy1_a=accast 6_diy2_b=accast 6_diy1_a=acmedc 6_diy2_b=acmedc",
|
||||
"fast": false,
|
||||
"about": "5_diy1_a=cdfend 5_diy2_b=cdfend 5_diy1_a=cguard 5_diy2_b=cguard 5_diy1_a=csnipe 5_diy2_b=csnipe 5_diy1_a=ccast 5_diy2_b=ccast 5_diy1_a=cmedic 5_diy2_b=cmedic 5_diy1_a=csuppo 5_diy2_b=csuppo",
|
||||
"fast": true,
|
||||
"kind": "normal",
|
||||
"stageId": "act1autochess_m03",
|
||||
"modeId": "mode_single_normal",
|
||||
"round": 13,
|
||||
"round": 12,
|
||||
"seed": 3994643046,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 216.23,
|
||||
"ticks": 6487,
|
||||
"reason": "cleared",
|
||||
"killed": 139,
|
||||
"total": 150,
|
||||
"waveId": "act1autochess_h05",
|
||||
"time": 230,
|
||||
"ticks": 6900,
|
||||
"reason": "timeout",
|
||||
"killed": 1,
|
||||
"total": 60,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 391,
|
||||
"rngDraws": 0,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":139,"total":150,"perfect":false,"leaks":11,"leaksUncounted":0,"coins":0,"dmg":1550587,"bossDmg":0,"heal":109719,"deaths":4,"layerGains":{}}},
|
||||
"players": {"p1":{"killed":1,"total":60,"perfect":false,"leaks":59,"leaksUncounted":0,"coins":0,"dmg":282380,"bossDmg":0,"heal":50396,"deaths":8,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_diy1_a",2,null,2604,1,167,0,3442,2604,0,30,1],
|
||||
["p1",2,"op","chess_char_6_diy2_b",2,"uniequip_002_acfend",2646.22,1,7,1,22576,4540,0,130,2],
|
||||
["p1",3,"op","chess_char_6_diy1_a",2,null,1529,1,169,26,218591,23279,0,102,10],
|
||||
["p1",4,"op","chess_char_6_diy2_b",2,"uniequip_002_acpion",1494.95,1,11,63,603310,32568,0,159,15],
|
||||
["p1",5,"op","chess_char_6_diy1_a",2,null,1912.7,1,152,2,28838,15194,0,91,3],
|
||||
["p1",6,"op","chess_char_6_diy2_b",2,"uniequip_002_acguad",2661.84,1,15,10,107218,19493,0,155,5],
|
||||
["p1",7,"op","chess_char_6_diy1_a",2,null,1272,1,8,0,9284,0,0,49,2],
|
||||
["p1",8,"op","chess_char_6_diy2_b",2,"uniequip_002_acnipe",1643,1,12,2,55255,0,0,111,2],
|
||||
["p1",9,"op","chess_char_6_diy1_a",2,null,1372,1,14,0,30553,0,0,40,2],
|
||||
["p1",10,"op","chess_char_6_diy2_b",2,"uniequip_002_accast",1819,1,4,35,471521,0,0,80,3],
|
||||
["p1",11,"op","chess_char_6_diy1_a",2,null,1224,1,6,0,0,0,52763,71,2],
|
||||
["p1",12,"op","chess_char_6_diy2_b",2,"uniequip_002_acmedc",1464,1,168,0,0,23065,56956,50,2],
|
||||
["p1",1,"op","chess_char_5_diy1_a",2,null,0,0,79,0,373,6384,0,27,1],
|
||||
["p1",2,"op","chess_char_5_diy2_b",2,"none",2815.76,1,7,0,2719,7225,0,171,3],
|
||||
["p1",3,"op","chess_char_5_diy1_a",2,null,0,0,64,0,11489,20263,0,63,2],
|
||||
["p1",4,"op","chess_char_5_diy2_b",2,"none",3334,1,81,0,14126,12445,0,43,2],
|
||||
["p1",5,"op","chess_char_5_diy1_a",2,null,1271,1,8,0,2487,0,0,114,2],
|
||||
["p1",6,"op","chess_char_5_diy2_b",2,"none",1510,1,12,0,2916,0,0,108,3],
|
||||
["p1",7,"op","chess_char_5_diy1_a",2,null,1133,1,14,0,35959,0,0,77,2],
|
||||
["p1",8,"op","chess_char_5_diy2_b",2,"none",1360,1,4,0,99918,0,0,182,4],
|
||||
["p1",9,"op","chess_char_5_diy1_a",2,null,1218,1,6,0,0,0,27340,74,4],
|
||||
["p1",10,"op","chess_char_5_diy2_b",2,"none",1374,1,80,0,0,23724,23055,44,2],
|
||||
["p1",11,"op","chess_char_5_diy1_a",2,null,932,1,13,0,49770,0,0,126,3],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"none",1131,1,15,1,62624,0,0,135,3],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {"enemy_1249_lysdb_2":[60,57,3,19813,650353],"enemy_1251_lysyta":[51,49,2,2808,252854],"enemy_1329_cbshld":[39,33,6,98122,647381]},
|
||||
"hooks": [1,169,6487,1589,1589,4054,3954,233,143,154,49,47,0,47498,0,146,150,11,1,143,0,0,40],
|
||||
"events": {"atk":1708,"deploy":16,"die":143,"dmg":3954,"engage":10,"fx":494,"heal":232,"leak":11,"skill":96,"spawn":165,"status":50},
|
||||
"fx": {"aoe":79,"dodge":40,"dp":114,"shieldBreak":60,"slash":197,"spGain":4},
|
||||
"snaps": "3a143536 45205b96 e8d7d1d8 00f7e32c ce3c78b4 2407bf14 13b01dbe 3fec0a84 ee21dba1 0cc379c6 b04c03a9 9eee7fbb b11dc910 c1766ce2 0f77c84f 9fe87e87 75e19b69 4a45b886 895b892d 6b459f6c cfac3759 f5653f35"
|
||||
"enemies": {"enemy_1045_hammer_2":[12,0,12,7564,38430],"enemy_1061_zomshd":[48,1,44,62478,243951]},
|
||||
"hooks": [1,81,6900,1538,1538,1420,1420,26919,9,65,31,29,0,36483,261,17,60,56,1,9,0,0,0],
|
||||
"events": {"atk":1538,"deploy":18,"die":9,"dmg":1420,"engage":10,"heal":118,"leak":56,"skill":60,"spawn":75,"status":508},
|
||||
"fx": {},
|
||||
"snaps": "3d47827a f438ec09 4750a3f1 67a2ef63 bc8c524d cc232240 f8a81b0b 67199d32 37bf514c 390c7a46 49463832 571ec11b 7c76454f 4f9d6f04 937aa413 88154103 fac6145b 67b1e3b0 c42bccca b4078766 5160b359 435f2091 4734a9e2"
|
||||
},
|
||||
"diy-136": {
|
||||
"about": "6_diy1_a=sharp2 6_diy2_b=sharp2 6_diy1_a=acspec 6_diy2_b=acspec 6_diy1_a=acsupo 6_diy2_b=acsupo",
|
||||
"about": "5_diy1_a=acfend 5_diy2_b=acfend 5_diy1_a=acpion 5_diy2_b=acpion 5_diy1_a=acguad 5_diy2_b=acguad 5_diy1_a=acnipe 5_diy2_b=acnipe 5_diy1_a=accast 5_diy2_b=accast 5_diy1_a=acmedc 5_diy2_b=acmedc",
|
||||
"fast": false,
|
||||
"kind": "normal",
|
||||
"stageId": "act1autochess_m04",
|
||||
"modeId": "mode_multi_abyss",
|
||||
"round": 13,
|
||||
"round": 12,
|
||||
"seed": 2354181867,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 225.6,
|
||||
"ticks": 6768,
|
||||
"waveId": "act1autochess_h05",
|
||||
"time": 197.6,
|
||||
"ticks": 5928,
|
||||
"reason": "cleared",
|
||||
"killed": 1,
|
||||
"total": 123,
|
||||
"total": 111,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 130,
|
||||
"rngDraws": 194,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":1,"total":123,"perfect":false,"leaks":122,"leaksUncounted":0,"coins":0,"dmg":293038,"bossDmg":0,"heal":0,"deaths":24,"layerGains":{}}},
|
||||
"players": {"p1":{"killed":1,"total":111,"perfect":false,"leaks":110,"leaksUncounted":0,"coins":0,"dmg":372575,"bossDmg":0,"heal":35598,"deaths":21,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_diy1_a",0,null,0,0,149,0,281,5595,0,10,0],
|
||||
["p1",2,"op","chess_char_6_diy2_b",0,"uniequip_002_sharp2",0,0,89,1,41351,4988,0,15,1],
|
||||
["p1",3,"op","chess_char_6_diy1_a",2,null,0,0,153,0,27405,12694,0,18,0],
|
||||
["p1",4,"op","chess_char_6_diy2_b",2,"uniequip_002_acspec",1484,1,152,0,64271,11872,0,52,0],
|
||||
["p1",5,"op","chess_char_6_diy1_a",2,null,1160,1,4,0,59475,0,0,141,3],
|
||||
["p1",6,"op","chess_char_6_diy2_b",2,"uniequip_002_acsupo",1450,1,5,0,100254,0,0,162,3],
|
||||
["p1",1,"op","chess_char_5_diy1_a",2,null,0,0,84,0,1295,5208,0,8,0],
|
||||
["p1",2,"op","chess_char_5_diy2_b",2,"uniequip_002_acfend",0,0,92,0,492,6742,0,7,0],
|
||||
["p1",3,"op","chess_char_5_diy1_a",2,null,0,0,133,0,19015,4587,0,5,1],
|
||||
["p1",4,"op","chess_char_5_diy2_b",2,"uniequip_002_acpion",0,0,127,0,9727,5712,0,6,1],
|
||||
["p1",5,"op","chess_char_5_diy1_a",2,null,0,0,13,1,41644,15919,0,36,1],
|
||||
["p1",6,"op","chess_char_5_diy2_b",2,"uniequip_002_acguad",0,0,14,0,21548,11545,0,31,1],
|
||||
["p1",7,"op","chess_char_5_diy1_a",2,null,0,0,4,0,26438,1272,0,57,2],
|
||||
["p1",8,"op","chess_char_5_diy2_b",2,"uniequip_002_acnipe",0,0,104,0,24016,3174,0,62,1],
|
||||
["p1",9,"op","chess_char_5_diy1_a",2,null,1372,1,15,0,197621,0,0,72,2],
|
||||
["p1",10,"op","chess_char_5_diy2_b",2,"uniequip_002_accast",0,0,79,0,30779,3702,0,6,1],
|
||||
["p1",11,"op","chess_char_5_diy1_a",2,null,1224,1,137,0,0,6465,6476,12,1],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"uniequip_002_acmedc",0,0,116,0,0,12790,29122,22,1],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {"enemy_1006_shield_3":[21,0,21,9388,58708],"enemy_1043_zomsbr":[66,1,65,8698,238677],"enemy_1402_tgshd_2":[36,0,36,17063,131985]},
|
||||
"hooks": [1,153,6768,459,459,2469,2495,24678,25,147,7,24,0,10141,11244,28,123,122,1,25,0,0,0],
|
||||
"events": {"atk":470,"deploy":27,"die":25,"dmg":2389,"engage":6,"fx":257,"leak":122,"skill":44,"spawn":132,"status":875},
|
||||
"fx": {"displace":30,"dodge":6,"expose":221},
|
||||
"snaps": "3b9227e1 1631291e 032dd42b e8c97fce 06b2c450 146801be a9ba58c8 42440c2a ee76d4c2 8ac7769c f06d9109 be306e36 b9697bbb 7631a029 e3f80b10 e1a55253 af28ac55 b4ecedd5 6565411e 93bc5900 9a306169 33d6050f e195f073"
|
||||
"enemies": {"enemy_1010_demon":[42,0,42,20414,146344],"enemy_1422_lrsldr_2":[45,1,44,16488,213185],"enemy_1425_lrcmra_2":[24,0,24,32934,113917]},
|
||||
"hooks": [1,137,5928,414,414,2605,2597,68,22,132,12,12,0,16478,0,31,111,110,1,22,0,0,8],
|
||||
"events": {"atk":475,"deploy":23,"die":22,"dmg":2597,"engage":10,"fx":70,"heal":68,"leak":110,"skill":24,"spawn":126,"status":86},
|
||||
"fx": {"aoe":19,"dodge":8,"dp":2,"slash":16,"spGain":1,"telegraph":24},
|
||||
"snaps": "9b55e6b0 4b84e6c9 a31196f7 65bbf7e6 005f7244 d545b0aa d6dda7d1 4eb2a689 eb73849b 9fa2f84e 44e19af8 b33de929 b2b82608 6d75148b d12443b1 69fce227 9fae0dac cac0fed8 eb1fd014 2d8d16d9"
|
||||
},
|
||||
"diy-137": {
|
||||
"about": "6_diy1_a=acfend 6_diy2_b=acfend 5_diy1_a=sharp2 5_diy2_b=sharp2 6_diy1_a=acpion 6_diy2_b=acpion 5_diy1_a=acspec 5_diy2_b=acspec 6_diy1_a=acguad 6_diy2_b=acguad 5_diy1_a=acsupo 5_diy2_b=acsupo",
|
||||
"fast": false,
|
||||
"kind": "normal",
|
||||
"stageId": "act1autochess_m05",
|
||||
"modeId": "mode_multi_funny",
|
||||
"round": 13,
|
||||
"seed": 2980751949,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 230,
|
||||
"ticks": 6900,
|
||||
"reason": "timeout",
|
||||
"killed": 72,
|
||||
"total": 147,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 167,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":72,"total":147,"perfect":false,"leaks":75,"leaksUncounted":0,"coins":0,"dmg":442745,"bossDmg":0,"heal":0,"deaths":14,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_diy1_a",2,null,471.32,1,2,3,12047,2133,0,125,2],
|
||||
["p1",2,"op","chess_char_6_diy2_b",2,"uniequip_002_acfend",1874.72,1,4,3,15501,1556,0,135,2],
|
||||
["p1",3,"op","chess_char_5_diy1_a",0,null,0,0,154,4,26068,3730,0,94,2],
|
||||
["p1",4,"op","chess_char_5_diy2_b",0,"uniequip_002_sharp2",0,0,159,17,105338,4748,0,101,3],
|
||||
["p1",5,"op","chess_char_6_diy1_a",2,null,1529,1,170,15,83718,3058,0,48,3],
|
||||
["p1",6,"op","chess_char_6_diy2_b",2,"uniequip_002_acpion",1904,1,171,20,148088,3808,0,71,4],
|
||||
["p1",7,"op","chess_char_5_diy1_a",2,null,418.4,1,166,1,6993,7660,0,118,0],
|
||||
["p1",8,"op","chess_char_5_diy2_b",2,"uniequip_002_acspec",1448,1,1,0,0,0,0,0,0],
|
||||
["p1",9,"op","chess_char_6_diy1_a",2,null,2638,1,3,0,0,0,0,0,0],
|
||||
["p1",10,"op","chess_char_6_diy2_b",2,"uniequip_002_acguad",3531,1,5,0,0,0,0,0,0],
|
||||
["p1",11,"op","chess_char_5_diy1_a",2,null,1160,1,9,0,17741,0,0,110,2],
|
||||
["p1",12,"op","chess_char_5_diy2_b",2,"uniequip_002_acsupo",1430,1,10,3,27250,0,0,134,3]
|
||||
],
|
||||
"enemies": {"enemy_1166_dusbr_2":[39,23,15,833,110746],"enemy_1169_duphlx_2":[57,29,26,4837,204334],"enemy_1170_dushld_2":[51,20,29,21023,251761]},
|
||||
"hooks": [1,171,6900,1228,1228,5344,5469,0,86,156,21,26,0,28148,6290,80,147,70,1,86,0,0,0],
|
||||
"events": {"atk":1304,"deploy":24,"die":86,"dmg":5439,"engage":9,"fx":232,"leak":70,"skill":57,"spawn":159,"status":822},
|
||||
"fx": {"aoe":120,"displace":8,"dodge":6,"dp":42,"slash":56},
|
||||
"snaps": "07acf070 45a4a681 36321b07 c1ed5954 e01eadef 1123aeb3 663fd869 dc9ceb93 b6fd2a6f f9266265 d4f4edf4 8b69fcf3 3c07e569 761e9246 979d0a5a 2531a80a a4512719 c0b965f6 a660da32 5eefd2c0 40fd4609 bd9011f1 ceee71ef"
|
||||
},
|
||||
"diy-138": {
|
||||
"about": "6_diy1_a=sharp2 6_diy2_b=sharp2 6_diy1_a=acspec 6_diy2_b=acspec 6_diy1_a=acnipe 6_diy2_b=acnipe 6_diy1_a=accast 6_diy2_b=accast 6_diy1_a=acmedc 6_diy2_b=acmedc 6_diy1_a=acsupo 6_diy2_b=acsupo",
|
||||
"fast": false,
|
||||
"kind": "normal",
|
||||
"stageId": "act1autochess_m06",
|
||||
"modeId": "mode_single_hard",
|
||||
"round": 13,
|
||||
"seed": 1340399568,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 230,
|
||||
"ticks": 6900,
|
||||
"reason": "timeout",
|
||||
"killed": 12,
|
||||
"total": 135,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 1103,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {"p1":{"killed":12,"total":135,"perfect":false,"leaks":123,"leaksUncounted":0,"coins":0,"dmg":874043,"bossDmg":0,"heal":89389,"deaths":10,"layerGains":{}}},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_diy1_a",0,null,0,0,124,2,25509,3730,0,33,1],
|
||||
["p1",2,"op","chess_char_6_diy2_b",0,"uniequip_002_sharp2",2494,1,6,1,474445,0,0,188,3],
|
||||
["p1",3,"op","chess_char_6_diy1_a",2,null,1154,1,9,0,0,0,0,0,0],
|
||||
["p1",4,"op","chess_char_6_diy2_b",2,"uniequip_002_acspec",1115.05,1,12,0,14047,22383,0,245,0],
|
||||
["p1",5,"op","chess_char_6_diy1_a",2,null,0,0,131,2,31907,2544,0,75,1],
|
||||
["p1",6,"op","chess_char_6_diy2_b",2,"uniequip_002_acnipe",0,0,129,0,71814,3286,0,76,1],
|
||||
["p1",7,"op","chess_char_6_diy1_a",2,null,1372,1,154,1,70869,3863,0,28,1],
|
||||
["p1",8,"op","chess_char_6_diy2_b",2,"uniequip_002_accast",469.69,1,153,5,105691,4987,0,31,1],
|
||||
["p1",9,"op","chess_char_6_diy1_a",2,null,1224,1,8,0,0,19566,51961,95,3],
|
||||
["p1",10,"op","chess_char_6_diy2_b",2,"uniequip_002_acmedc",1464,1,10,0,0,15232,37429,85,3],
|
||||
["p1",11,"op","chess_char_6_diy1_a",2,null,1160,1,11,0,33045,15061,0,145,4],
|
||||
["p1",12,"op","chess_char_6_diy2_b",2,"uniequip_002_acsupo",1450,1,7,1,46716,16396,0,158,4]
|
||||
],
|
||||
"enemies": {"enemy_1288_duskls":[27,3,23,18896,105117],"enemy_1288_duskls_2":[57,7,49,9711,311592],"enemy_1292_duskld":[51,2,45,78443,457334]},
|
||||
"hooks": [1,154,6900,1612,1612,2128,2402,342,22,139,22,18,0,26300,21429,41,135,117,1,65,0,0,1],
|
||||
"events": {"atk":1936,"deploy":19,"die":22,"dmg":2402,"engage":9,"fx":109,"heal":283,"leak":117,"skill":44,"spawn":147,"status":1367},
|
||||
"fx": {"aoe":6,"dodge":35,"ember":43,"revive":25},
|
||||
"snaps": "0f96a83c adacdeed 01e7aaaa 0845acac 365160dd 4fb8f73e 8b18200d abbdd9f9 50a849a6 84d7394f a1abe90e c542c234 5d100327 4ad910fc ec6a0b25 2dc48918 dbd4ba26 76770d66 27358121 1aed3b22 66238e0c fcf111a7 1f31984f"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user