feedback5 WE1: Misery's S1 / S2 run as deploy-timed duration skills (activateOnDeploy), not a passive holding a timed buff (follow-up 12)
The 补位 stand-in Misery (char_615_acspec): S1 物理的服从 and S2 战争的恭顺 are PASSIVE / ON_DEPLOY with a 10 s duration; PR #109's deploy-timed contract runs such skills as duration skills started by every deployment, with their own skillStart / skillEnd (as 宴 S2, 斯卡蒂 S2, 缄默德克萨斯 …). - standin-acspec.js: S1 / S2 = kind 'duration', activateOnDeploy, no SP, trigger NEVER, their mods for the duration; 二象命末 reads "S1 running". S3 空间的归依 stays a passive with its deploy hook. Numbers unchanged. Golden: standin-01, standin-06 (Misery for 缄默德克萨斯, S2): her skill casts 0 → 14 / 12, skillStart / skillEnd counts, 'skill' events and snapshot hashes; every damage, kill and HP unchanged. Others unchanged. Tests: test/content/standin_acspec.test.js updated on purpose (the S1 / S2 lifecycle); golden-standins, backups pass.
This commit is contained in:
1 parent
d870103ce8
commit
19b63f2a02
3 files changed
+36
-23
No files matched your search
@@ -12,8 +12,10 @@ import { bodyInKeys } from '../../../body.js';
|
||||
const S1 = 'skchr_acspec_1';
|
||||
const S2 = 'skchr_acspec_2';
|
||||
const S3 = 'skchr_acspec_3';
|
||||
const S1_KEY = 'acspec:s1';
|
||||
const PULL_TAG = 'acspec:pull';
|
||||
/** A 被动 ON_DEPLOY skill with a duration: a duration skill every deployment starts (no SP, never cast otherwise). */
|
||||
const ON_DEPLOY = Object.freeze({ kind: 'duration', activateOnDeploy: true, spCost: 0, spType: 'none', trigger: 'NEVER' });
|
||||
const s1On = (unit) => !!(unit.skill && unit.skill.active && unit.skill.id === S1);
|
||||
/**
|
||||
* 四维分离's "周围四格": her tile and the four tiles beside it [ASSUMED: her own tile counts — an enemy she blocks stands
|
||||
* on it, and the talent is about her being alone with one enemy].
|
||||
@@ -63,9 +65,12 @@ export default {
|
||||
// attack@prob (PRTS 备注: it is her normal attack's basic logic; EXE-X levels 2 / 3 make it +atk ATK and a higher
|
||||
// chance). Talent 2 四维分离: ATK +atk while exactly `cnt` enemy stands on her tile or the four beside it.
|
||||
// All three skills are 被动, ON_DEPLOY — they start at every deployment (the battle-start one included, when no enemy
|
||||
// is on the field yet: S3 then hits nothing, as in the official mode; it matters on a redeploy):
|
||||
// S1 物理的服从: for the skill's duration (10 s) the talent-1 chance is attack@prob and physical dodge prob.
|
||||
// S2 战争的恭顺: for the skill's duration (10 s) ATK +atk, ASPD +attack_speed.
|
||||
// is on the field yet: S3 then hits nothing, as in the official mode; it matters on a redeploy). S1 / S2 last the
|
||||
// skill's duration (10 s): duration skills started by the deployment (`activateOnDeploy`, the deploy-timed skill
|
||||
// contract of PR #109 — their own skillStart / skillEnd and the client's skill on / off), not a passive holding a timed
|
||||
// buff:
|
||||
// S1 物理的服从: while it runs the talent-1 chance is attack@prob and physical dodge prob.
|
||||
// S2 战争的恭顺: while it runs ATK +atk, ASPD +attack_speed.
|
||||
// S3 空间的归依: spaceBurst above (ground only), right after each deployment (install).
|
||||
// Module EXE-X (trait atk): ATK +atk while no ally stands on the four tiles beside her (PRTS: unlike the usual EXE-X;
|
||||
// shared/tier4.js lonely, as 缄默德克萨斯's module of the same text).
|
||||
@@ -81,11 +86,11 @@ export default {
|
||||
const cnt = Math.max(1, Math.floor(num(t1.cnt, 1)));
|
||||
return {
|
||||
skills: {
|
||||
[S1]: { kind: 'passive', onStart({ battle, unit }) { battle.addBuff(unit, { key: S1_KEY, duration: num(r1?.duration, 10), mods: { dodgePhys: num(b1.prob) }, tags: ['skill'] }); } },
|
||||
[S2]: { kind: 'passive', onStart({ battle, unit }) { battle.addBuff(unit, { key: 'acspec:s2', duration: num(r2?.duration, 10), mods: { atkPct: num(b2.atk), aspd: num(b2.attack_speed) }, tags: ['skill'] }); } },
|
||||
[S1]: { ...ON_DEPLOY, duration: num(r1?.duration, 10), mods: { dodgePhys: num(b1.prob) } },
|
||||
[S2]: { ...ON_DEPLOY, duration: num(r2?.duration, 10), mods: { atkPct: num(b2.atk), aspd: num(b2.attack_speed) } },
|
||||
[S3]: { kind: 'passive' },
|
||||
},
|
||||
trait: { hitsFn: (battle, unit) => (battle.rng.chance(unit.findBuff(S1_KEY) ? s1Prob : talentProb) ? 2 : 1) },
|
||||
trait: { hitsFn: (battle, unit) => (battle.rng.chance(s1On(unit) ? s1Prob : talentProb) ? 2 : 1) },
|
||||
talents: [
|
||||
{ install(battle, unit) { statBuff(battle, unit, 'acspec:t1', { atkPct: num(t0.atk) }); } },
|
||||
{ install(battle, unit) { toggleBuff(battle, unit, 'acspec:t2', () => enemiesAround4(battle, unit) === cnt, { atkPct: num(t1.atk) }); } },
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// server/sim/content/kits/ops/standin-acspec.js) on every chess she replaces: 缄默德克萨斯 (tier 4, S2), 山 (tier 5, S3 +
|
||||
// EXE-X), 新约能天使 / 锏 (tier 6, S3 + EXE-X), normal (E2 Lv1, skill 4) and elite (E2 Lv60, skill 7). Her three skills
|
||||
// are ON_DEPLOY passives: they start at every deployment — at the battle start no enemy is on the field yet, so S3 hits
|
||||
// nothing there and matters on a redeploy (再部署时间大幅度减少: 18 s). Numbers are read back from data/backups.json.
|
||||
// nothing there and matters on a redeploy (再部署时间大幅度减少: 18 s). S1 / S2 last 10 s: since 0.2.0 they are duration skills
|
||||
// started by every deployment (activateOnDeploy — PR #109's deploy-timed contract, their own skillStart / skillEnd), no
|
||||
// longer a passive holding a 10 s buff. Numbers are read back from data/backups.json.
|
||||
// Run: node --test test/content/standin_acspec.test.js
|
||||
|
||||
import { test } from 'node:test';
|
||||
@@ -26,7 +28,7 @@ const ALLY = 'chess_char_1_02_a'; // 角峰, a melee blocker
|
||||
function battle(units, o = {}) {
|
||||
return makeBattle({
|
||||
defs: { enemies: ENEMIES }, units, timeLimit: o.timeLimit ?? 400, autoFinish: false, seed: o.seed ?? 5,
|
||||
flags: { dpPerSec: 0 }, hooks: ['damaged', 'deploy', 'death'], captureNoisy: true,
|
||||
flags: { dpPerSec: 0 }, hooks: ['damaged', 'deploy', 'death', 'skillStart', 'skillEnd'], captureNoisy: true,
|
||||
});
|
||||
}
|
||||
const MISERY = (id, standIn = true) => ({ chessId: id, row: 10, col: 4, standIn });
|
||||
@@ -38,7 +40,7 @@ function done(h) {
|
||||
assert.equal(h.b.errors.length, 0, JSON.stringify(h.b.errors[0]));
|
||||
}
|
||||
|
||||
test('Misery on every chess she replaces (normal + elite): her body (再部署 18 s), the backup skill as a passive, this kit; EXE-X on the tier-5 / 6 elites only', () => {
|
||||
test('Misery on every chess she replaces (normal + elite): her body (再部署 18 s), the backup skill (S2 a deploy-timed duration skill, S3 a passive), this kit; EXE-X on the tier-5 / 6 elites only', () => {
|
||||
const ids = UNIT.standsIn.flatMap((a) => [a, a.replace(/_a$/, '_b')]);
|
||||
assert.equal(ids.length, 8);
|
||||
const withModule = [];
|
||||
@@ -48,7 +50,8 @@ test('Misery on every chess she replaces (normal + elite): her body (再部署 1
|
||||
h.step();
|
||||
const u = h.unit(1);
|
||||
const sk = form.skills.find((s) => s.index === c.backup.skillIndex);
|
||||
assert.deepEqual([u.def.charId, u.def.standInFor, u.skill.id, u.skill.kind, !!u.kit.generic, u.kit.skillSource], [CHAR, c.charId, sk.skillId, 'passive', false, 'skills'], id);
|
||||
const kind = sk.skillId === 'skchr_acspec_3' ? 'passive' : 'duration';
|
||||
assert.deepEqual([u.def.charId, u.def.standInFor, u.skill.id, u.skill.kind, !!u.kit.generic, u.kit.skillSource], [CHAR, c.charId, sk.skillId, kind, false, 'skills'], id);
|
||||
assert.deepEqual([sk.skillType, sk.spType], ['PASSIVE', 'ON_DEPLOY'], id);
|
||||
const mod = c.backup.uniEquipId && c.status.equipLevel > 0 ? form.modules.find((m) => m.uniEquipId === c.backup.uniEquipId) : null;
|
||||
assert.equal(!!u.def.raw.module?.active, !!mod, `${id}: module`);
|
||||
@@ -137,7 +140,7 @@ test('Misery S1 物理的服从 (no chess names it — a 自选 slot may): for 1
|
||||
const h = battle([MISERY(id, { skillIndex: 0 })]);
|
||||
h.step();
|
||||
const u = h.unit(1);
|
||||
assert.deepEqual([u.skill.id, u.skill.kind, sk.duration], ['skchr_acspec_1', 'passive', 10]);
|
||||
assert.deepEqual([u.skill.id, u.skill.kind, u.skill.duration, sk.duration], ['skchr_acspec_1', 'duration', 10, 10]);
|
||||
const prob = () => { let p = null; u.profile.hitsFn({ rng: { chance: (x) => { p = x; return false; } } }, u); return p; };
|
||||
const on = () => { approx(u.s.dodgePhys, bb.prob, `${id}: dodge ${bb.prob}`); assert.equal(prob(), bb['attack@prob'], `${id}: chance ${bb['attack@prob']}`); };
|
||||
const off = () => { assert.equal(u.s.dodgePhys, 0, `${id}: no dodge`); assert.equal(prob(), talentBb(u, 0)['attack@prob'], `${id}: the talent's chance`); };
|
||||
@@ -162,9 +165,13 @@ test('Misery S2 战争的恭顺 (缄默德克萨斯): for 10 s from every deploy
|
||||
const h = battle([MISERY(id)]);
|
||||
h.step();
|
||||
const u = h.unit(1);
|
||||
assert.deepEqual([u.skill.id, sk.duration], ['skchr_acspec_2', 10]);
|
||||
const on = () => { approx(u.s.atk, u.base.atk * (1 + bb.atk), `${id}: ATK +${bb.atk}`); assert.equal(u.s.aspd, 100 + bb.attack_speed, `${id}: ASPD`); };
|
||||
const off = () => { approx(u.s.atk, u.base.atk, `${id}: ATK back`); assert.equal(u.s.aspd, 100, `${id}: ASPD back`); };
|
||||
assert.deepEqual([u.skill.id, u.skill.kind, u.skill.duration, sk.duration], ['skchr_acspec_2', 'duration', 10, 10]);
|
||||
// the deploy-timed contract (PR #109): the skill runs — skillStart now, skillEnd 10 s later — and then is off
|
||||
const on = () => { approx(u.s.atk, u.base.atk * (1 + bb.atk), `${id}: ATK +${bb.atk}`); assert.equal(u.s.aspd, 100 + bb.attack_speed, `${id}: ASPD`); assert.ok(u.skill.active, `${id}: running`); };
|
||||
const off = () => { approx(u.s.atk, u.base.atk, `${id}: ATK back`); assert.equal(u.s.aspd, 100, `${id}: ASPD back`); assert.ok(!u.skill.active, `${id}: over`); };
|
||||
const starts = () => h.hooksOf('skillStart').filter((c) => c.unit === u).length;
|
||||
const ends = () => h.hooksOf('skillEnd').filter((c) => c.unit === u).length;
|
||||
assert.deepEqual([starts(), ends()], [1, 0], `${id}: started by the deployment`);
|
||||
on();
|
||||
h.run(9.8);
|
||||
on();
|
||||
@@ -176,6 +183,7 @@ test('Misery S2 战争的恭顺 (缄默德克萨斯): for 10 s from every deploy
|
||||
on();
|
||||
h.run(10.1);
|
||||
off();
|
||||
assert.deepEqual([starts(), ends()], [2, 2], `${id}: one window per deployment (the retreat ends none — it had ended)`);
|
||||
done(h);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
["p1",4,"op","chess_char_3_18_a",2,null,2638,1,7,2,32526,8770,0,75,2],
|
||||
["p1",5,"op","chess_char_4_04_a",1,null,1264.1,1,91,2,17806,8706,0,100,9],
|
||||
["p1",6,"op","chess_char_4_07_a",1,null,0,0,74,1,16008,10455,0,37,1],
|
||||
["p1",7,"op","chess_char_4_16_a",1,null,807.8,1,92,1,9136,11490,0,98,0],
|
||||
["p1",7,"op","chess_char_4_16_a",1,null,807.8,1,92,1,9136,11490,0,98,14],
|
||||
["p1",8,"op","chess_char_3_01_a",2,null,1271,1,5,0,15937,0,0,131,2],
|
||||
["p1",9,"op","chess_char_3_04_a",2,null,1218,1,8,0,0,0,26765,59,3],
|
||||
["p1",10,"op","chess_char_3_21_a",2,null,161.7,1,6,1,42388,1110,0,121,2],
|
||||
@@ -68,10 +68,10 @@
|
||||
"enemy_1119_vofsd": [12,6,6,19166,93491],
|
||||
"enemy_1121_lifbos_2": [9,1,7,9882,35401]
|
||||
},
|
||||
"hooks": [1,93,5100,939,939,1515,1509,3842,41,82,27,38,0,22264,26,41,50,27,1,41,5,0,6],
|
||||
"events": {"atk":1088,"bounty":2,"deploy":43,"die":55,"dmg":1509,"engage":11,"fx":61,"heal":59,"layer":5,"leak":27,"skill":52,"spawn":62,"status":123},
|
||||
"hooks": [1,93,5100,939,939,1515,1509,3842,41,82,41,39,0,22264,26,41,50,27,1,41,5,0,6],
|
||||
"events": {"atk":1088,"bounty":2,"deploy":43,"die":55,"dmg":1509,"engage":11,"fx":61,"heal":59,"layer":5,"leak":27,"skill":80,"spawn":62,"status":123},
|
||||
"fx": {"blink":14,"dodge":7,"dp":5,"garrison":5,"liberate":13,"slash":16,"telegraph":1},
|
||||
"snaps": "f6b212e6 305ae509 ec53440d 68686487 36b691ce 6a3a0f75 3bf73b20 56836b06 21e8ab42 092dab80 ab8cb21f 0d51c9c6 b1e5b425 ac801d55 57850eec ee889f70 5b8b495b"
|
||||
"snaps": "8b8be66d 3d41101a 548df15a 1009f8b0 08d13c52 9fe4b099 64799759 70e168de 0b87fdee 880a5dbc 4750bf5e 71d1cbac a6d3a940 eda8aa0d 5dec048f 8b7b727d 0b96370a"
|
||||
},
|
||||
"standin-02": {
|
||||
"about": "normal band 5 as stand-ins: chess_char_4_17_a→char_610_acfend chess_char_4_18_a→char_602_cdfend chess_char_5_05_a→char_602_cdfend chess_char_4_19_a→char_608_acpion chess_char_5_01_a→char_600_cpione chess_char_4_22_a→char_609_acguad chess_char_4_23_a→char_609_acguad chess_char_5_06_a→char_609_acguad chess_char_4_20_a→char_603_csnipe chess_char_4_24_a→char_604_ccast chess_char_5_02_a→char_604_ccast chess_char_5_03_a→char_612_accast",
|
||||
@@ -334,7 +334,7 @@
|
||||
["p1",4,"op","chess_char_3_18_b",2,"none",0,0,93,3,28745,9190,2808,27,2],
|
||||
["p1",5,"op","chess_char_4_04_b",1,"none",0,0,137,0,25476,10224,0,40,9],
|
||||
["p1",6,"op","chess_char_4_07_b",1,"none",0,0,101,3,28031,10981,0,27,1],
|
||||
["p1",7,"op","chess_char_4_16_b",1,"none",0,0,139,7,42175,16104,0,70,0],
|
||||
["p1",7,"op","chess_char_4_16_b",1,"none",0,0,139,7,42175,16104,0,70,12],
|
||||
["p1",8,"op","chess_char_3_01_b",2,"none",0,0,122,1,16716,3020,0,24,1],
|
||||
["p1",9,"op","chess_char_3_04_b",2,"none",0,0,4,0,0,15932,26703,25,2],
|
||||
["p1",10,"op","chess_char_3_21_b",2,"none",0,0,118,12,175716,7911,0,55,1],
|
||||
@@ -354,10 +354,10 @@
|
||||
"enemy_1252_lysytb_2": [21,8,13,29953,226804],
|
||||
"enemy_1329_cbshld_2": [12,1,11,40556,69057]
|
||||
},
|
||||
"hooks": [1,140,5511,596,596,875,906,94,81,139,25,37,0,7322,36,60,98,48,1,89,7,0,4],
|
||||
"events": {"atk":684,"bounty":4,"deploy":42,"die":91,"dmg":906,"engage":11,"fx":62,"heal":67,"layer":7,"leak":48,"skill":50,"spawn":110,"status":117},
|
||||
"hooks": [1,140,5511,596,596,875,906,94,81,139,37,37,0,7322,36,60,98,48,1,89,7,0,4],
|
||||
"events": {"atk":684,"bounty":4,"deploy":42,"die":91,"dmg":906,"engage":11,"fx":62,"heal":67,"layer":7,"leak":48,"skill":74,"spawn":110,"status":117},
|
||||
"fx": {"blink":10,"camouflage":1,"dodge":6,"dp":13,"explode":1,"garrison":7,"slash":24},
|
||||
"snaps": "accd1698 74126f20 63d105c2 6f46af20 41b2aaf8 62757331 5ee97ef6 6331a731 2b18e7ef 8540aa5c 5e961254 ad125465 3ac7012a 04cc07f3 968df17c 2499c755 43a6f5dc 312679f4 5b9c22ce"
|
||||
"snaps": "be6d1821 fa43a30d 52099f77 a860ccb6 ea311a6e 44affa90 9dabf4d8 83adbc77 77dfae35 f02fd736 470fba6a a9871a60 b7e054aa ae8c4b73 55df9303 1c374baa 0d780d78 e03a2b60 b7879d42"
|
||||
},
|
||||
"standin-07": {
|
||||
"about": "elite band 6 as stand-ins: chess_char_4_17_b→char_610_acfend chess_char_4_18_b→char_602_cdfend chess_char_5_05_b→char_602_cdfend chess_char_4_19_b→char_608_acpion chess_char_5_01_b→char_600_cpione chess_char_4_22_b→char_609_acguad chess_char_4_23_b→char_609_acguad chess_char_5_06_b→char_609_acguad chess_char_4_20_b→char_603_csnipe chess_char_4_24_b→char_604_ccast chess_char_5_02_b→char_604_ccast chess_char_5_03_b→char_612_accast",
|
||||
|
||||
Reference in new issue
Block a user