feedback1 final: six 重装 skills cast with an enemy in range, a deliberate deviation from the official TAKE_DAMAGE row (GitHub issue #4, PR #12)

The owner's decision (2026-10-03, after community feedback): 深巡 S2 行动能力剥夺, 雷蛇 S2 反击电弧, 号角 S2 暴风号令
and S3 终极防线, 灰毫 S1 攻击力强化·γ型 and S2 专注轰击 take the basic strategy (DEFAULT) instead of the 下半 TANK class
row's TAKE_DAMAGE. Every other MANUAL 重装 skill, 深巡 S1 included, still waits for a hit.

- tools/build-data.mjs TRIGGER_DEVIATIONS: a small table keyed by the normal chess id and the skill id (the elite
  follows through baseId), never by the skill id alone - 灰毫 S1 is the generic skcom_atk_up[3]. resolveTrigger applies
  it last; rawRule keeps the official TAKE_DAMAGE; validateAll fails the build when an entry no longer meets a
  TAKE_DAMAGE row. data/chess.json rebuilt with --offline: exactly the 12 records' rule changes.
- PR #12's kit lines (trigger 'DEFAULT' on 深巡 / 雷蛇 S2) stay and agree with the data; their comments and tests now
  give this reason instead of the community summary.
- Tests: test/sim/feedback1-tank-triggers.test.js (the data, kit = data, a real battle on 战场#01(下半) in which each of
  the six casts with an enemy in range before any hit and each of the other 46 MANUAL 重装 records casts on the first
  hit); data.test.js trigger expectations; the 灰毫 / 号角 kit tests follow; docs-consistency.
- Docs: DESIGN §21.29 (new), §5.6, §19.2, §20.2, §21.20 (settled row), §21.23; DATA, SIM, PLAYING, CHANGELOG 0.1.1;
  research 03 §1.4 drops the arknights.wiki.gg "offensive / defensive" summary and records the activity_table history
  (上半 had no TANK row, 下半 added it) and this deviation; research 00-INDEX.
This commit is contained in:
sganggs committed 2026-10-03 19:13:35 +08:00
1 parent e6ab4e1ff8
commit 2dba6da598
20 files changed
+325 -58

No files matched your search

+11 -6
View File
@@ -132,7 +132,8 @@ test('1_04 深巡: fin darts pierce attack@max_target enemies on the line + 1 s
});
const u = h.unit(id);
h.step();
// 技能策略 (issue #4): an enemy inside her initial 2-2 range is enough — the basic strategy casts it, no hit required
// 技能策略 (issue #4; the deliberate deviation of DESIGN §21.29): an enemy inside her initial 2-2 range is enough — the
// basic strategy casts it, no hit required
assert.equal(u.skill.rule, 'DEFAULT', '行动能力剥夺 is an offensive ranged skill, not a 重装 TAKE_DAMAGE one');
h.run(3.5);
assert.ok(u.skill.active);
@@ -150,9 +151,9 @@ test('1_04 深巡: fin darts pierce attack@max_target enemies on the line + 1 s
});
test('1_04 深巡 / 1_20 雷蛇 S2 技能策略: both 哨戒铁卫 S2s cast with an enemy in range, no hit needed (issue #4)', () => {
// both S2s are offensive (深巡: range up + ATK/ASPD + piercing darts; 雷蛇: ATK +125 % + arts on 3 + stun), and the
// mode's rule is "offensive skills activate when an enemy is in their skill range". The 重装 TAKE_DAMAGE row is
// documented for skillIndex 0 only — reading it as a wildcard made both wait for a hit.
// both S2s are offensive (深巡: range up + ATK/ASPD + piercing darts; 雷蛇: ATK +125 % + arts on 3 + stun); the official
// 下半 重装 row (TAKE_DAMAGE for every MANUAL 重装 skill) made both wait for a hit, and the owner's deliberate deviation
// from it (DESIGN §21.29, after community feedback) gives them the basic strategy — the data and the kit say DEFAULT.
for (const id of ['chess_char_1_04_a', 'chess_char_1_04_b', 'chess_char_1_20_a', 'chess_char_1_20_b']) {
const h = run({ units: [{ chessId: id, row: 9, col: 4, carryState: READY }], enemies: [{ key: 'e', pos: [9, 6] }] });
const u = h.unit(id);
@@ -1116,7 +1117,7 @@ test('2_17 折桠: 生存决心 trembles ground enemies around, ATK/DEF up, hits
done(h2);
});
test('2_18 灰毫: 炮术研习 ATK +atk (ground surroundings: ashlok_t_1.atk); TAKE_DAMAGE skill ATK +atk; elite ×1.1 vs blocked', () => {
test('2_18 灰毫: 炮术研习 ATK +atk (ground surroundings: ashlok_t_1.atk); skill ATK +atk cast with an enemy in range (DEFAULT, DESIGN §21.29); elite ×1.1 vs blocked', () => {
const id = 'chess_char_2_18_a', bb = bbOf(id), t = tal(id);
const h = run({ units: [{ chessId: id, row: 10, col: 5 }, { chessId: 'chess_char_2_18_b', row: 10, col: 3 }] });
h.step();
@@ -1126,7 +1127,11 @@ test('2_18 灰毫: 炮术研习 ATK +atk (ground surroundings: ashlok_t_1.atk);
const h2 = run({ defs: { enemies: { e: dummy('e', { atk: 400 }) } }, units: [{ chessId: id, row: 10, col: 5, carryState: READY }], enemies: [{ key: 'e', pos: [10, 5] }] });
const u = h2.unit(id);
h2.runUntil(() => u.skill.active, 5);
assert.equal(h2.hooksOf('skillStart').find((c) => c.unit === u).reason, 'TAKE_DAMAGE');
// the owner's deliberate deviation from the 重装 TAKE_DAMAGE row: the basic strategy, no hit needed — she casts before the
// enemy she holds has hit her
assert.equal(u.skill.rule, 'DEFAULT');
assert.equal(h2.hooksOf('skillStart').find((c) => c.unit === u).reason, 'DEFAULT');
assert.ok(!h2.hooksOf('damaged').some((c) => c.target === u), 'not hit before the cast');
approx(u.s.atk, u.base.atk * (1 + t['ashlok_t_1.atk'] + bb.atk));
done(h2);
const idb = 'chess_char_2_18_b', tb = tbOf(idb);