feedback5 WE1: 「同时攻击阻挡的所有敌人」 takes up to the block count of targets from the range, blocked first (the queued centurion question; Mon3tr S3)
PRTS 分支特性信息 强攻手 / 重剑手 / 推击手 "普通攻击最大目标数等于阻挡数(不会低于1)"; PRTS 作战机制 §AOE "锁定人数的无弹道AOE攻击 (例如近卫分支“强攻手”)…在抬手时选取范围内的全体目标(不超过其攻击目标上限)"; PRTS 忍冬 S3 备注 "可对空". The client's selectors of the branch traits and of every kit skill marked hitAllBlocked carry _limitedMaxTargetNumToBlockedCnt (_allowZeroBlockCntLimit off) — charpacks extracted 2026-10-06. - ai.js: hitAllBlocked no longer returns just the blocked enemies; targetCount = max(1, block count) (+ maxTargets mods) of targets from the range and the blocked enemies, blocked first. Mon3tr S3 drops maxTargets 1; 凯尔希's Mon3tr under S2 picks its targets the same way. [ASSUMED] maxTargets mods add; 忍冬 S3's range-only note not modelled. Golden: 52 scenarios, each with a hitAllBlocked attacker (probe-checked) — roster-001, -003, -004, -007, -009, -011, -013, -014, -015, -016, -020, -022, -023, -024, -030, -032, -038, -040, -041, -042, -043, -049; bond-sargonShip-high, bond-soloShip-low, bond-soloShip-high, bond-yanShip-low; boss-boss_4-solo, boss-boss_6-solo, boss-boss_7-solo, hidden-boss_9-solo, hidden-boss_10-solo; matches solo-NORMAL-1, solo-ABYSS-1, coop3-ABYSS-5, coop4-FUNNY-6, coop4-ABYSS-7, coop2-NORMAL-70-diy; diy-013, -014, -033, -034, -035, -037, -038, -039, -051, -053, -076, -078, -109, -127, -130. standins unchanged. Tests: test/sim/feedback5-block-targets.test.js (new, 2). Updated on purpose: feedback1-transform.test.js (its real round relied on 百炼嘉维尔 — a 强攻手 — falling; the ember-walks-on check is now a deterministic sub-test). test/content and test/sim pass. Docs: SIM.md centurion row, professions.js header.
This commit is contained in:
1 parent
b2d3964478
commit
97454d8cda
12 files changed
+860
-776
No files matched your search
+15
-6
@@ -140,10 +140,6 @@ export function acquireTargets(b, u, prof) {
|
||||
}
|
||||
prof._fortressMelee = false;
|
||||
}
|
||||
if (prof.hitAllBlocked && u.blocking.length) {
|
||||
const t = u.blocking.filter((e) => canTargetEnemy(u, e, prof));
|
||||
if (t.length) return t;
|
||||
}
|
||||
const cands = b.enemiesInKeys(u.rangeKeys, u, prof);
|
||||
// "可以选择且优先选择阻挡单位" (PRTS 选择器): the enemies a unit blocks are always selectable by it — the block radius
|
||||
// (0.7071) reaches past its own tile, so a blocked enemy may stand outside a short range or behind its facing (user
|
||||
@@ -155,16 +151,29 @@ export function acquireTargets(b, u, prof) {
|
||||
if (prof.allInRange) return cands.concat(extra);
|
||||
sortEnemyTargets(b, u, cands, prof.priority);
|
||||
const all = cands.concat(extra);
|
||||
const n = Math.max(1, Math.floor((prof.maxTargets || 1) + u.s.maxTargets));
|
||||
const n = targetCount(u, prof);
|
||||
return n >= all.length ? all : all.slice(0, n);
|
||||
}
|
||||
if (!cands.length) return cands;
|
||||
if (prof.allInRange) return cands;
|
||||
const n = Math.max(1, Math.floor((prof.maxTargets || 1) + u.s.maxTargets));
|
||||
const n = targetCount(u, prof);
|
||||
sortEnemyTargets(b, u, cands, prof.priority);
|
||||
return n >= cands.length ? cands : cands.slice(0, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* How many targets one attack of `u` takes. `hitAllBlocked` — "同时攻击阻挡的所有敌人": the 强攻手 / 重剑手 / 推击手 traits and every
|
||||
* skill worded so, whose client selectors carry `_limitedMaxTargetNumToBlockedCnt` (with `_allowZeroBlockCntLimit` off) —
|
||||
* takes up to its block count, never fewer than 1, from its range and the enemies it blocks, the blocked ones first
|
||||
* (acquireTargets' order): PRTS 分支特性信息 强攻手 / 重剑手 / 推击手 "普通攻击最大目标数等于阻挡数(不会低于1)", PRTS 作战机制 §AOE伤害判定
|
||||
* "锁定人数的无弹道AOE攻击(例如近卫分支“强攻手”)…在抬手时选取范围内的全体目标(不超过其攻击目标上限)", PRTS 忍冬 S3 备注 "可对空" (a
|
||||
* flyer she cannot block). Until 0.2.0 it struck the blocked enemies only (one in range when it blocked none).
|
||||
*/
|
||||
function targetCount(u, prof) {
|
||||
const base = prof.hitAllBlocked ? Math.max(1, Math.floor(u.s.blockCnt)) : (prof.maxTargets || 1);
|
||||
return Math.max(1, Math.floor(base + u.s.maxTargets));
|
||||
}
|
||||
|
||||
/** Perform an attack/heal with profile `prof` against `targets`. opts: { noAmmo } (Battle.forceAttack). */
|
||||
export function performAttack(b, u, prof, targets, opts = null) {
|
||||
const isSkill = !!prof.isSkill;
|
||||
|
||||
@@ -43,8 +43,9 @@
|
||||
// - S1 指令:结构加固 (MANUAL, data DEFAULT — a heal skill: cast as she is about to heal): her DEF +def and Mon3tr's DEF
|
||||
// +attack@def while it runs; 物理格挡 prob (damage_block[phy]: a physical damage instance she takes is blocked whole —
|
||||
// not a dodge, nothing ignores it; a 流失 is no damage).
|
||||
// - S2 指令:战术协同 (MANUAL, data DEFAULT, 绑定 Mon3tr): her ASPD +attack_speed; Mon3tr ATK +attack@atk and each of its attacks
|
||||
// strikes every enemy it blocks (one target when it blocks none [ASSUMED]).
|
||||
// - S2 指令:战术协同 (MANUAL, data DEFAULT, 绑定 Mon3tr): her ASPD +attack_speed; Mon3tr ATK +attack@atk and "可以攻击阻挡的所有
|
||||
// 敌人": each of its attacks takes up to its block count (3) of targets, the blocked ones first (the 强攻手 rule — PRTS 分支特性信息
|
||||
// "普通攻击最大目标数等于阻挡数(不会低于1)"; its mode's _limitedMaxTargetNumToBlockedCnt).
|
||||
// - S3 指令:熔毁 (MANUAL, data DEFAULT, 绑定 Mon3tr, 18 s): Mon3tr DEF +attack@def, its attacks deal true damage, its ATK
|
||||
// +attack@atk × the remaining share of the skill, updated every second from the cast (+190 % … +10.6 %); unless it killed
|
||||
// an enemy meanwhile, at the skill's normal end it loses attack@hp_ratio × its max HP (a 流失 that may knock it out).
|
||||
@@ -54,6 +55,7 @@
|
||||
import { num, talentBb, traitBb, moduleOn, skillRec, up } from '../shared/tier1.js';
|
||||
import { absoluteRangeKeys } from '../../../targeting.js';
|
||||
import { COLS } from '../../../constants.js';
|
||||
import { acquireTargets } from '../../../ai.js';
|
||||
|
||||
const S1 = 'skchr_kalts_1';
|
||||
const S2 = 'skchr_kalts_2';
|
||||
@@ -132,11 +134,12 @@ function mon3trKit(owner, { t0, b1, b2, b3 }) {
|
||||
}
|
||||
holdBuff(battle, m, 'kalts:s3:mon3tr', skillOn(owner, S3), { atkPct: s3Atk, defPct: num(b3['attack@def']) });
|
||||
}, { owner: m });
|
||||
// S2: each attack strikes every enemy it blocks
|
||||
// S2: "Mon3tr可以攻击阻挡的所有敌人" — the 强攻手 rule (its mode's _limitedMaxTargetNumToBlockedCnt): up to its block count
|
||||
// of targets, the blocked ones first (ai.js acquireTargets with `hitAllBlocked`)
|
||||
battle.on('beforeAttack', (c) => {
|
||||
if (c.attacker !== m || !skillOn(owner, S2)) return;
|
||||
const blocked = battle.blockedTargets(m, c.profile);
|
||||
if (blocked.length) c.targets = blocked;
|
||||
if (c.attacker !== m || !skillOn(owner, S2) || !c.profile) return;
|
||||
const t = acquireTargets(battle, m, { ...c.profile, hitAllBlocked: true });
|
||||
if (t.length) c.targets = t;
|
||||
}, { owner: m });
|
||||
// S3: true damage; a kill keeps its HP
|
||||
battle.on('hit', (c) => {
|
||||
|
||||
@@ -55,8 +55,9 @@
|
||||
// - S3 策略:熔毁 (MANUAL, data DEFAULT, 25 s; only while her 重构体 stands — the cast is refused otherwise): the 重构体 is withdrawn
|
||||
// (its piece waits: back once its tile is free again) and she 【移动】s onto its tile; meanwhile range x-4, ATK +atk, base attack
|
||||
// time base_attack_time (a flat −1.5 s on 2.85 s), block +block_cnt, max HP +max_hp (the HP ratio kept), 流失 of
|
||||
// damage_per_second HP/s (8 every 0.1 s); her attacks hit every ground enemy she blocks (one in range when she blocks none —
|
||||
// [ASSUMED] as 凯尔希's Mon3tr S2) for ATK true damage, and each attack starts a chain heal on herself at attack@heal_scale × ATK
|
||||
// damage_per_second HP/s (8 every 0.1 s); "同时攻击阻挡的所有敌人" (her selector's _limitedMaxTargetNumToBlockedCnt): each attack
|
||||
// takes up to her block count of ground enemies of her range, the blocked ones first (ai.js `hitAllBlocked`, the 强攻手 rule
|
||||
// of PRTS 分支特性信息) for ATK true damage, and each attack starts a chain heal on herself at attack@heal_scale × ATK
|
||||
// (she is a free link). 不死: a hit or 流失 that would knock her out leaves her at 1 HP, untargetable and blocking nothing, and
|
||||
// the skill ends on the next tick. At the end she 【返回】s to the tile she left (her SP emptied; the max HP drop keeps the ratio,
|
||||
// so after a fatal blow she stands at ≤ 1 HP) with 1 s of 不死. The 路标形态 marker on her tile is not modelled: should another
|
||||
@@ -199,7 +200,7 @@ export default {
|
||||
kind: 'duration',
|
||||
mods: { atkPct: num(b3.atk), batPct: batMod(b3.base_attack_time, chess), blockCnt: num(b3.block_cnt), hpFlat: num(b3.max_hp) },
|
||||
targeting: s3?.rangeGrid ? { rangeGrid: s3.rangeGrid } : undefined,
|
||||
attack: { dmgType: 'true', attack: 'melee', projectile: 'none', canHitFly: false, groundOnly: true, hitAllBlocked: true, maxTargets: 1 },
|
||||
attack: { dmgType: 'true', attack: 'melee', projectile: 'none', canHitFly: false, groundOnly: true, hitAllBlocked: true },
|
||||
onStart({ battle, unit, skill }) {
|
||||
const r = prostsOf(battle, unit).find(up);
|
||||
if (!r) { skill.end('noToken'); skill.addCharge(1); return; } // (refused before: the activate guard)
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
// projectile 'none'|'beam'|'arrow'|'bolt'|'bomb'|'lob'|'orb'|'drone'|'boomerang' ('beam': an instant hit drawn as a
|
||||
// line; 'boomerang': out to the target and back to the thrower, ai.js throwBoomerang)
|
||||
// boomerang bool (回环射手: keeps 'boomerang')
|
||||
// canHitFly bool maxTargets n (≥1) hitAllBlocked bool (attack every blocked enemy)
|
||||
// canHitFly bool maxTargets n (≥1) hitAllBlocked bool ("同时攻击阻挡的所有敌人": up to the block count of
|
||||
// targets, blocked ones first — ai.js targetCount)
|
||||
// allInRange bool (every enemy on the range at once) splashRadius tiles (around the struck target)
|
||||
// rangeAoe bool (a 锁定攻击范围 AoE without a projectile — SUB table or kit trait, applied by resolveProfile after
|
||||
// every override: allInRange + instant 'beam' hits on a ranged profile; only selectable enemies are
|
||||
|
||||
Reference in new issue
Block a user