feedback1 integrate: WH 维娜·维多利亚 S3 lions on every free melee tile; 玛恩纳 S3 hits air units
This commit is contained in:
commit
6e1cf914d1
6 files changed
+447
-44
No files matched your search
@@ -2168,21 +2168,43 @@ const KITS = {
|
||||
},
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 玛恩纳 — librator (ramp to +200 % ATK while idle). S3 未照耀的荣光 (26 s, CUSTOM_RANGE): skill range, trait ×2 (−10 %
|
||||
// per kill), 5 targets at 125/150 % ATK phys; enemies in range take +10/11 % of his ATK true damage from every Kazimierz
|
||||
// attack. T1 游侠: ×1.1 ATK on attacks (×1.15 and −15 % damage taken with ≥3 enemies around). T2 无动于衷: taunt +1,
|
||||
// Kazimierz ops reflect 15 % of his ATK as true damage when attacked.
|
||||
// 玛恩纳 — librator (ramp to +200 % ATK while idle). S3 未照耀的荣光 (26 s, CUSTOM_RANGE): skill range, trait ×2, 5
|
||||
// targets at 125/150 % ATK phys, air units too (PRTS 备注 "※可对空"; player report B4 after 0.1.0: on a flying wave the
|
||||
// cast hit nothing for 26 s); enemies in range take +10/11 % of his ATK true damage from every Kazimierz attack. Per
|
||||
// PRTS 备注 the trait bonus drops by 10 % (per_kill_reduce, absolute: +400 % → +390 %) for each enemy knocked out by
|
||||
// his own attack (or the damage it carries — not 无动于衷's reflection, not a mark another operator's attack set off),
|
||||
// settled after that attack, never below +0 %. T1 游侠: ×1.1 ATK on attacks (×1.15 and −15 % damage taken with ≥3
|
||||
// enemies around). T2 无动于衷: taunt +1, Kazimierz ops reflect 15 % of his ATK as true damage when attacked.
|
||||
// S1 未声张的怒火 (duration, SEARCH): attacks attack@atk_scale × ATK, DEF +. S2 未宽解的悲哀 (duration): skill range,
|
||||
// BAT +0.3 s, attacks attack@atk_scale × ATK twice; a kill during the skill keeps the trait ramp when it ends.
|
||||
// BAT +0.3 s, attacks attack@atk_scale × ATK twice; a kill of his own attacks during the skill keeps the trait ramp
|
||||
// when it ends. S1 / S2 have no 对空 note: ground only, like his trait.
|
||||
chess_char_5_19_a: (bb, chess, def) => {
|
||||
const t0 = talent(chess, 0), t1 = talent(chess, 1);
|
||||
const sid = selectedId(chess, def);
|
||||
const up = num(bb.trait_up, 1), perKill = num(bb.per_kill_reduce);
|
||||
// S3: the trait bonus = ramp × trait_up + per_kill_reduce × kills, ≥ 0 — the trait's own ramp buff stays, the
|
||||
// difference goes into `mlynar:traitUp` (negative once the kills take the bonus below the ramp)
|
||||
const applyUp = (battle, unit) => {
|
||||
const extra = num(unit.trait.ramp) * Math.max(0, num(unit.mem.mlyMult, 1) - 1);
|
||||
if (extra > 0) battle.addBuff(unit, { key: 'mlynar:traitUp', mods: { atkPct: extra } });
|
||||
const ramp = num(unit.trait.ramp);
|
||||
const extra = unit.mem.mlyUp ? Math.max(0, ramp * up + perKill * num(unit.mem.mlyKills)) - ramp : 0;
|
||||
if (extra) battle.addBuff(unit, { key: 'mlynar:traitUp', mods: { atkPct: extra } });
|
||||
else battle.removeBuff(unit, 'mlynar:traitUp');
|
||||
};
|
||||
/**
|
||||
* "仅自身普通攻击(与该次攻击附带的伤害)击倒非角色类单位" (PRTS S2 / S3 备注): `onKill(victim)` for each enemy he
|
||||
* knocks out, while his skill runs, that his current attack hit (or the mark it set off, tagged 'mlynarOwn') — so a
|
||||
* kill by the damage that attack carries (天马之枪, the 卡西米尔 bond's true damage, the mark) counts too, in any
|
||||
* hook order; `onAttack()` after each of his attacks ("加成降低于当次攻击后统一结算"), which also closes the set.
|
||||
* 无动于衷's reflection and a mark another operator's attack set off happen outside his attack: never counted.
|
||||
*/
|
||||
const ownKills = (battle, unit, onKill, onAttack = null) => {
|
||||
const hit = new Set();
|
||||
battle.on('damaged', (c) => {
|
||||
if (c.source === unit && (c.dmg?.isAttack || (c.dmg?.tags || []).includes('mlynarOwn'))) hit.add(c.target);
|
||||
}, { owner: unit, priority: 1000 });
|
||||
battle.on('kill', (c) => { if (c.killer === unit && hit.has(c.victim) && c.victim.side === 'enemy' && unit.skill?.active) onKill(c.victim); }, { owner: unit });
|
||||
battle.on('attack', (c) => { if (c.attacker !== unit) return; hit.clear(); if (onAttack) onAttack(); }, { owner: unit });
|
||||
};
|
||||
return {
|
||||
skills: lazySkills({
|
||||
skchr_mlynar_1: () => ({ kind: 'duration', mods: mods({ defPct: num(bb.def) }), attack: { atkScale: num(bb['attack@atk_scale'], 1) } }),
|
||||
@@ -2194,10 +2216,14 @@ const KITS = {
|
||||
}),
|
||||
skill: {
|
||||
kind: 'duration',
|
||||
targeting: skillGrid(chess, def) ? { rangeGrid: skillGrid(chess, def) } : undefined,
|
||||
targeting: { ...(skillGrid(chess, def) ? { rangeGrid: skillGrid(chess, def) } : {}), canHitFly: true },
|
||||
attack: { atkScale: num(bb['attack@atk_scale'], 1), maxTargets: Math.max(1, num(bb['attack@max_target'], 1)) },
|
||||
onStart({ battle, unit }) { unit.mem.mlyMult = up; applyUp(battle, unit); battle.fx('aoe', { x: unit.x, y: unit.y, id: unit.id, r: 2, skill: 'mlynar' }); },
|
||||
onEnd({ battle, unit }) { unit.mem.mlyMult = 1; battle.removeBuff(unit, 'mlynar:traitUp'); },
|
||||
onStart({ battle, unit }) {
|
||||
unit.mem.mlyUp = true; unit.mem.mlyKills = 0; unit.mem.mlyPending = 0;
|
||||
applyUp(battle, unit);
|
||||
battle.fx('aoe', { x: unit.x, y: unit.y, id: unit.id, r: 2, skill: 'mlynar' });
|
||||
},
|
||||
onEnd({ battle, unit }) { unit.mem.mlyUp = false; unit.mem.mlyPending = 0; battle.removeBuff(unit, 'mlynar:traitUp'); },
|
||||
},
|
||||
trait: { dmgMul: (b, u) => (num(u.mem.mlyNear) >= num(t0.cnt, 3) ? num(t0.atk_scale_up, 1) : num(t0.atk_scale_base, 1)) },
|
||||
talents: [
|
||||
@@ -2225,7 +2251,7 @@ const KITS = {
|
||||
// 技能期间若击倒敌人,技能结束时特性效果不重置: the librator trait resets the ramp on skillEnd (priority 0) —
|
||||
// remember it before and put it back after
|
||||
battle.on('skillStart', (c) => { if (c.unit === unit) unit.mem.mlyKeep = false; }, { owner: unit });
|
||||
battle.on('kill', (c) => { if (c.killer === unit && c.victim.side === 'enemy' && unit.skill?.active) unit.mem.mlyKeep = true; }, { owner: unit });
|
||||
ownKills(battle, unit, () => { unit.mem.mlyKeep = true; });
|
||||
battle.on('skillEnd', (c) => {
|
||||
if (c.unit === unit) unit.mem.mlyRamp = unit.mem.mlyKeep && c.reason !== 'death' ? num(unit.trait.ramp) : null;
|
||||
}, { owner: unit, priority: 100 });
|
||||
@@ -2237,18 +2263,20 @@ const KITS = {
|
||||
}, { owner: unit, priority: -100 });
|
||||
}
|
||||
if (sid && sid !== 'skchr_mlynar_3') return;
|
||||
battle.on('kill', (c) => {
|
||||
if (c.killer !== unit || c.victim.side !== 'enemy' || !unit.skill?.active) return;
|
||||
unit.mem.mlyMult = Math.max(1, num(unit.mem.mlyMult, 1) + perKill);
|
||||
ownKills(battle, unit, () => { unit.mem.mlyPending = num(unit.mem.mlyPending) + 1; }, () => {
|
||||
if (!unit.mem.mlyPending || !unit.mem.mlyUp) return;
|
||||
unit.mem.mlyKills = num(unit.mem.mlyKills) + unit.mem.mlyPending;
|
||||
unit.mem.mlyPending = 0;
|
||||
applyUp(battle, unit);
|
||||
}, { owner: unit });
|
||||
});
|
||||
const extra = num(bb.atk_scale);
|
||||
if (!(extra > 0)) return;
|
||||
battle.on('damaged', (c) => {
|
||||
const src = c.source, e = c.target;
|
||||
if (!src || src.side !== 'ally' || !isOp(src) || !isKazimierz(src) || !c.dmg?.isAttack || c.type === 'element') return;
|
||||
if (e.side !== 'enemy' || !e.alive || !unit.skill?.active || !on(unit) || !inRange(unit, e)) return;
|
||||
battle.dealDamage(unit, e, { amount: unit.s.atk * extra, type: 'true', canDodge: false, isSkill: true, tags: ['skill', 'mlynarMark'] });
|
||||
const tags = src === unit ? ['skill', 'mlynarMark', 'mlynarOwn'] : ['skill', 'mlynarMark'];
|
||||
battle.dealDamage(unit, e, { amount: unit.s.atk * extra, type: 'true', canDodge: false, isSkill: true, tags });
|
||||
}, { owner: unit });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
// the storm stops when she leaves the field.
|
||||
// 6_06 佩佩 splash stun via `damaged` (isSplash) hook; module ×1.15 when ≥3 enemies in the splash area.
|
||||
// 6_07 维娜 "attack enemies blocked by allies in talent range" = those enemies' tiles are added to her range;
|
||||
// 黄金盟誓 lasts the skill duration on the tile nearest to an enemy.
|
||||
// S3 puts a 黄金盟誓 on every free deployable melee tile of her talent-1 area (fences too), each for
|
||||
// the skill duration (the token's maxDeployCount 1 is its hand limit — tokens.js).
|
||||
// 6_08 焰影苇草 灼痕 = marker (ATK −20 %) + the 法术脆弱 status (同名效果取最高); applied during S3 it lasts until the
|
||||
// skill ends (duration = remaining skill time).
|
||||
// 6_09 塑心 cannot normal attack; each skill charge is one attack; 精神逆构 multiplies every apoptosis gauge fill on
|
||||
@@ -1337,21 +1338,24 @@ function siege2(bb, chess, def) {
|
||||
mods: { atkPct: num(bb.atk), batPct: batOf(bb.base_attack_time, def) },
|
||||
targeting: { maxTargets: Math.max(1, Math.floor(num(bb['attack@max_target'], 1))) },
|
||||
attack: { dmgType: 'true' },
|
||||
// "立即在天赋一生效范围内可部署地面召唤“黄金盟誓”" (EN client "Summons Golden Vows on deployable tiles within
|
||||
// Talent 1's range"): one on EVERY free tile of the talent-1 area a melee piece could be deployed on — fences
|
||||
// (low, deployable, not walkable) included [ASSUMED: the EN wiki's "open low ground tiles"]; player report B3
|
||||
onStart({ battle, unit, skill }) {
|
||||
const tile = bestTile(battle, freeTiles(battle, unit, tGrid));
|
||||
if (tile) {
|
||||
const lions = [];
|
||||
for (const tile of freeTiles(battle, unit, tGrid, { ground: false })) {
|
||||
const lion = battle.spawnToken(unit, tokId, tile[0], tile[1], { duration: skill.timeLeft });
|
||||
unit.mem.vlion = lion;
|
||||
if (lion) {
|
||||
if (!lion.kit?.fromTokens && lion.profile) lion.profile.dmgType = 'true'; // "攻击造成真实伤害" without a token kit
|
||||
battle.fx('summon', { x: lion.x, y: lion.y, id: lion.id, src: unit.id });
|
||||
}
|
||||
if (!lion) continue;
|
||||
lions.push(lion);
|
||||
if (!lion.kit?.fromTokens && lion.profile) lion.profile.dmgType = 'true'; // "攻击造成真实伤害" without a token kit
|
||||
battle.fx('summon', { x: lion.x, y: lion.y, id: lion.id, src: unit.id });
|
||||
}
|
||||
unit.mem.vlions = lions;
|
||||
},
|
||||
onEnd({ battle, unit }) {
|
||||
const lion = unit.mem.vlion;
|
||||
unit.mem.vlion = null;
|
||||
if (lion && lion.alive) battle.retreat(lion, { reason: 'expired', permanent: true });
|
||||
const lions = unit.mem.vlions || [];
|
||||
unit.mem.vlions = null;
|
||||
for (const lion of lions) if (lion.alive) battle.retreat(lion, { reason: 'expired', permanent: true });
|
||||
battle.setExtraRange(unit, null);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
// after duration_switch s on the field) and sluggish; the bomb is used up ('expired', never a knock-out;
|
||||
// its blast fx carries `consumed: true` so clients play the explosion, not a death sound)
|
||||
// 从不混淆的方向 untargetable marker; when the owner's skill ends it vanishes and the owner returns to its tile
|
||||
// 黄金盟誓 attacks deal true damage (trait); lasts while the owner's skill runs
|
||||
// 黄金盟誓 attacks deal true damage (trait); lasts while the owner's skill runs; 维娜 S3 places one on every
|
||||
// free deployable tile around her (kits/tier6.js) — no per-owner deploy limit (SKILL_SUMMON_UNCAPPED)
|
||||
// 防护单元 untargetable, invulnerable device placed by the player (a hand piece, user playtest #6): shield =
|
||||
// 凯瑟琳 max HP × max_shield_ratio on the operator in its range (range 1-1: the tile it faces; effects do
|
||||
// not stack — `cathy:shield`, read by 凯瑟琳 S1 岁月锻打), in full whenever it takes a new operator,
|
||||
@@ -41,7 +42,8 @@
|
||||
// 炎佑 (enemy_9012_acloon) flying ally: flies after the highest-aggro enemy of the field and hovers over it, stays
|
||||
// put when there is none; 3-target arts + burn on every hit, 元素脆弱 aura, 祛恶之焰 channel (yanyouKit)
|
||||
// 预备干员-医疗 / Touch (band map characters) generic kit / 恳切福音 kit
|
||||
// Common rules: deploy limit per owner for tokens running these kits (data `deployLimit`; the oldest is withdrawn),
|
||||
// Common rules: deploy limit per owner for tokens running these kits (data `deployLimit`; the oldest is withdrawn —
|
||||
// except SKILL_SUMMON_UNCAPPED, whose data limit is the hand count the skill does not obey),
|
||||
// `summonKill` hook ({ token, owner, victim }) whenever a token kills, lifetimes never outlive a shorter
|
||||
// `opts.duration`; summon tiles skip `battle.isReservedTile` (home tiles of pieces not deployed yet).
|
||||
// Managed mode: when the summoner runs a hand-authored kit (content/kits), the owner-coupled parts of its summon
|
||||
@@ -1395,8 +1397,17 @@ const SKILL_SUMMONS = Object.freeze({
|
||||
[TOKEN_IDS.radiantSword]: 'plus',
|
||||
[TOKEN_IDS.rosmonGear]: 'melee',
|
||||
});
|
||||
/** Pieces per cast of the skill summons placed several at a time (skill description; default 1). */
|
||||
const SKILL_SUMMON_PER_CAST = Object.freeze({ [TOKEN_IDS.rosmonGear]: 2 });
|
||||
/**
|
||||
* Pieces per cast of the skill summons placed several at a time (skill description; default 1). 黄金盟誓: "立即在天赋
|
||||
* 一生效范围内可部署地面召唤" — every free tile of the 8 around 维娜 (EN client "Summons Golden Vows on deployable tiles
|
||||
* within Talent 1's range"; player report B3 after 0.1.0).
|
||||
*/
|
||||
const SKILL_SUMMON_PER_CAST = Object.freeze({ [TOKEN_IDS.rosmonGear]: 2, [TOKEN_IDS.goldenOath]: 8 });
|
||||
/**
|
||||
* Skill summons the per-owner deploy limit does not apply to: the data `deployLimit` (character_table phase maxDeployCount 1)
|
||||
* of 黄金盟誓 is a hand count, while 维娜 S3 summons one on every free deployable tile of her talent-1 area at once.
|
||||
*/
|
||||
const SKILL_SUMMON_UNCAPPED = new Set([TOKEN_IDS.goldenOath]);
|
||||
/** Tactician talent tokens that replace the engine's generic 援军. */
|
||||
const TACTICIAN_TOKENS = new Set([TOKEN_IDS.wolfPack, TOKEN_IDS.manifold]);
|
||||
|
||||
@@ -1462,7 +1473,7 @@ export function install(battle) {
|
||||
// deploy limit per owner (data deployLimit): a new summon withdraws the oldest one of the same kind
|
||||
battle.on('deploy', (ctx) => {
|
||||
const u = ctx.unit;
|
||||
if (!u || u.kind !== 'token' || !u.ownerUnit || u.mem.isClone || !u.kit?.fromTokens) return;
|
||||
if (!u || u.kind !== 'token' || !u.ownerUnit || u.mem.isClone || !u.kit?.fromTokens || SKILL_SUMMON_UNCAPPED.has(u.defId)) return;
|
||||
const lim = deployLimitOf(u);
|
||||
if (!(lim >= 1) || !Number.isFinite(lim)) return;
|
||||
const same = battle.allyUnits.filter((t) => t.alive && t.kind === 'token' && t.defId === u.defId && t.ownerUnit === u.ownerUnit && !t.mem.isClone && t.kit?.fromTokens);
|
||||
|
||||
Reference in new issue
Block a user