feedback1 integrate: no status on a 无敌 + 无法选中 unit; 余 S2 skips 自缚 units; 信仰搅拌机 S2 blocks with too few bullets

- Battle.applyStatus: a unit both invulnerable and untargetable takes no status from the
  other side (PRTS 无敌 "无法被不同阵营选中"), so the status carried by the hit that knocks an
  enemy out (妮芙 S2's fear, a 叙拉古 proc) no longer lands after its 重生 cleanse.
- Flag selfBound (自缚: 守墓石像's 转换模式, the 自缚 leaders, the 胄 parts' anchor): 余 S2
  never teleports such a unit (PRTS 余 S2 备注 "持有自缚的单位不视为可达目标").
- 信仰搅拌机 S2: with fewer than ammo_cost bullets the lethal hit is still blocked, every
  bullet goes and the skill ends; it steps aside while 坚固维式重锤's 不死 window holds her
  (PRTS 备注), replacing the [ASSUMED] no-guard rule.
This commit is contained in:
sganggs committed 2026-10-03 11:52:52 +08:00
1 parent f142012033
commit 94a99a3b1a
11 files changed
+153 -13

No files matched your search

+2 -2
View File
@@ -240,7 +240,7 @@ export function registerMeta() {}
function onSpawn(b, e) {
const kit = BOSS_KITS[e.defId];
if (typeof kit !== 'function') return;
if (SELF_BOUND.includes(e.defId)) b.addBuff(e, { key: 'boss:selfBound', persist: true, flags: { noMove: true, unblockable: true } });
if (SELF_BOUND.includes(e.defId)) b.addBuff(e, { key: 'boss:selfBound', persist: true, flags: { noMove: true, selfBound: true, unblockable: true } });
const tpl = templateOf(b);
const ab = abOf(b, e);
let list = [];
@@ -484,7 +484,7 @@ function kitBlade(ab, e, b, tpl) {
spawn(b2, e2) {
hover(b2, true);
// 自缚 (moved by hand) · 不可阻挡 · 失衡免疫 (PRTS 天赋 "{{特殊机制|静态刚体}},不可阻挡、失衡免疫…" — data `staticBody` too)
b2.addBuff(e2, { key: 'boss:anchor', persist: true, flags: { noMove: true, unblockable: true, noDisplace: true } });
b2.addBuff(e2, { key: 'boss:anchor', persist: true, flags: { noMove: true, selfBound: true, unblockable: true, noDisplace: true } });
if (BLADE_ATK_SCALE[e2.defId]) e2.profile.atkScale = BLADE_ATK_SCALE[e2.defId];
},
before(c, b2, e2) { const l = targetsNear(b2, e2, e2.base.rangeRadius || 1.6, { ranged: false }); if (l.length) c.targets = l; }, // 范围物理伤害
+1 -1
View File
@@ -944,7 +944,7 @@ function statue(ab) {
ab2.immune = new Set([...(ab2.immune || []), 'levitate']);
setForm(b, e, 'stone', 'stone', { dur });
b.addBuff(e, {
key: 'ab:stone', duration: dur, visible: true, flags: { noMove: true, unblockable: true, noDisplace: true },
key: 'ab:stone', duration: dur, visible: true, flags: { noMove: true, selfBound: true, unblockable: true, noDisplace: true },
mods: { defFlat: T(ab, 'stone.def') ?? 0, resFlat: T(ab, 'stone.magic_resistance') ?? 0 },
onExpire: ({ battle }) => {
if (!e.alive) return;
+1
View File
@@ -279,6 +279,7 @@ function hammerAcquire(battle, rt, u) {
hs.undyingUsed = true;
const dur = num(p.undeadable_duration, 8) * m;
hs.undyingUntil = battle.time + dur;
u.mem.undyingUntil = hs.undyingUntil; // the carrier holds 不死 (信仰搅拌机 S2 steps aside: kits/tier4.js)
c.prevented = true;
fxOn(battle, 'undying', u, 'item:hammer', 'chess_item_3_09_e', { duration: dur });
}, PRIO_REVIVE);
+9 -4
View File
@@ -281,17 +281,22 @@ const kits = {
install(battle, unit) {
if (S2) {
// S2 "技能期间若受到致命伤害,立即消耗N发弹药抵挡这次伤害": the lethal hit is negated (HP back to its value
// before the hit) while at least N bullets are left ([ASSUMED]: fewer bullets ⇒ no guard); 0 left ends the skill
// before the hit) and N bullets go; with fewer left it still blocks, spends them all and the skill ends (PRTS 备注
// "弹药量不足时仍可抵挡致命伤害,此时将消耗所有剩余弹药并退出技能状态"). "自身持有不死时此效果不会生效": a 不死
// that runs before it (骑士戒律, priority 20) has already prevented the hit; 坚固维式重锤's held window
// (unit.mem.undyingUntil, items/battle.js) runs last, so the guard steps aside while it lasts
const cost = Math.max(1, Math.floor(num(bb.ammo_cost, 30)));
battle.on('hit', (c) => { if (c.target === unit) unit.mem.rmixerPre = { dmg: c.dmg, hp: unit.hp }; }, { owner: unit, priority: -100 });
battle.on('fatal', (c) => {
const sk = unit.skill;
if (c.unit !== unit || c.prevented || !sk || !sk.active || sk.kind !== 'ammo' || sk.ammoLeft < cost) return;
if (c.unit !== unit || c.prevented || !sk || !sk.active || sk.kind !== 'ammo' || !(sk.ammoLeft > 0)) return;
if (battle.time < (unit.mem.undyingUntil ?? -Infinity)) return;
c.prevented = true;
const pre = unit.mem.rmixerPre;
unit.hp = pre && pre.dmg === c.dmg ? Math.max(1, Math.min(unit.s.maxHp, pre.hp)) : 1;
sk.ammoLeft -= cost;
battle.fx('shield', { x: unit.x, y: unit.y, id: unit.id, n: cost });
const spent = Math.min(cost, sk.ammoLeft);
sk.ammoLeft -= spent;
battle.fx('shield', { x: unit.x, y: unit.y, id: unit.id, n: spent });
if (sk.ammoLeft <= 0) sk.end('ammo');
}, { owner: unit });
}
+4 -2
View File
@@ -199,10 +199,12 @@ function cleanseAbnormal(battle, u) {
}
/**
* "传送至自身位置": a ground enemy that can reach `unit`'s tile on the ground grid is moved onto it (unblocked, its
* route re-planned from there — the engine then blocks it on that tile when capacity allows). Flyers / bosses stay.
* route re-planned from there — the engine then blocks it on that tile when capacity allows). Flyers / bosses stay, and
* so does a 自缚 unit (flag `selfBound`, e.g. 守墓石像's 转换模式): PRTS 余 S2 备注 "处于消失状态的/持有自缚的单位不视为可达
* 目标" (束缚 alone does not exempt it, so not `noMove`).
*/
function teleportEnemy(battle, unit, e) {
if (!e || !e.alive || e.isFlying || e.isBoss) return false;
if (!e || !e.alive || e.isFlying || e.isBoss || e.s.flags.selfBound) return false;
const r = unit.tileR, c = unit.tileC;
if (!battle.grid.groundPassable(r, c)) return false;
const er = Math.round(e.y), ec = Math.round(e.x);