feedback5 O20: the 缚地 status (groundbind) — an enemy air unit counts as a ground unit and cannot move

ba.groundbind "目标变为地面单位,无法移动;使部分近地悬浮敌人掉落;对重量大于3的单位持续时间减半" (予愿安洁莉娜 S2 needs it):
- buffs.js: STATUS groundbind (flag groundbind + noMove, move x0), FLAG_KEYS, RESIST_STATUSES (as 束缚, [ASSUMED]);
- units.js isFlying: an enemy under 缚地 is a ground unit unless a 浮空 lifts it (same result as before without the flag);
- battle/status.js: 浮空 lands on a 缚地 data flyer (PRTS 异常效果 "…且不持有缚地异常…则Buff取消"), 缚地 halved on weight > 3;
- content/enemies: 掠海漂移体 / 吉兆飞鳞 drop on 缚地 as their PRTS text says (吉兆飞鳞 stays down while it holds);
- render/style.js: the 束缚 icon for it; docs/SIM.md (§2 isFlying, §3 flags / status table / 抵抗).
Nothing applies it yet: every golden family is unchanged. Test: test/sim/feedback5-groundbind.test.js.
This commit is contained in:
sganggs committed 2026-10-06 05:20:28 +08:00
1 parent 996f5d20fe
commit 83e879a335
8 files changed
+126 -25

No files matched your search

+7 -6
View File
@@ -135,8 +135,8 @@ export class BattleStatus {
* handlers may cancel it or change `duration` / `value`. Official rules (buffs.js STATUS): 抵抗 (the `resist` status)
* shortens the RESIST_STATUSES by its value (default half; applied after `beforeStatus`; `resistApplied` skips that
* pass — the cold-on-cold 冻结 below already used post-抵抗 lengths). A second 寒冷 while 寒冷 remains applies 冻结 for
* max(remaining, this cold after 抵抗) (PRTS 术语释义 寒冷 「持续时间取双方之中最高」). 浮空 lasts half as long on units heavier
* than LEVITATE_HALF_WEIGHT (current massLevel); 冻结's RES cut hits enemies only; 麻痹 adds stacks; "同名效果取最高"
* max(remaining, this cold after 抵抗) (PRTS 术语释义 寒冷 「持续时间取双方之中最高」). 浮空 and 缚地 last half as long on units
* heavier than LEVITATE_HALF_WEIGHT (current massLevel); 冻结's RES cut hits enemies only; 麻痹 adds stacks; "同名效果取最高"
* statuses (`valued`) keep the strongest value — a weaker application only extends past the stronger one's end (it
* then resumes); `stackAs` = the value such an application competes with instead of its own (its effect stays
* `value`): an effect the game stacks as another strength — Raidian S3's 虚弱, PRTS 备注 "在叠加时视为90%(1级~6级)/80%
@@ -162,9 +162,9 @@ export class BattleStatus {
const immune = target.def && target.def.immune;
if (!opts.force && tpl.immune && immune && immune.has(tpl.immune)) return false;
// 浮空 Buff (PRTS 异常效果: "若单位数据上为飞行单位且不持有缚地异常或是持有浮空异常则Buff取消"; 行动方式 "行动类型(数据)为
// 飞行的单位、以及已持有浮空异常的单位无法被施加浮空Buff"): refused on data flyers (`motion` FLY) and units already
// levitated — a hovering 近地悬浮 enemy is WALK in its data, so it can be levitated (no 缚地 / 浮空强化 in this mode)
if (key === 'levitate' && (target.motion === 'FLY' || target.s.flags.levitate)) return false;
// 飞行的单位、以及已持有浮空异常的单位无法被施加浮空Buff"): refused on data flyers (`motion` FLY) that hold no 缚地 and on
// units already levitated — a hovering 近地悬浮 enemy is WALK in its data, so it can be levitated (no 浮空强化 in this mode)
if (key === 'levitate' && ((target.motion === 'FLY' && !target.s.flags.groundbind) || target.s.flags.levitate)) return false;
if (this._hooks.beforeStatus) {
const c = this.emit('beforeStatus', { source: opts.source ?? null, target, status: key, duration, value, cancel: false });
if (c.cancel || !target.alive) return false;
@@ -177,7 +177,8 @@ export class BattleStatus {
const rv = this.resistOf(target);
if (rv > 0) duration *= 1 - rv;
}
if (key === 'levitate' && target.s.massLevel > LEVITATE_HALF_WEIGHT) duration /= 2;
// 浮空 / 缚地 (ba.levitate, ba.groundbind): "对重量大于3的单位持续时间减半"
if ((key === 'levitate' || key === 'groundbind') && target.s.massLevel > LEVITATE_HALF_WEIGHT) duration /= 2;
if (!(duration > 0)) return false;
if (key === 'cold' && target.findBuff('cold') && !(immune && immune.has('frozen'))) {
// PRTS 术语释义 寒冷: 友方寒冷 pairs into 冻结, 「持续时间取双方之中最高」. `duration` is this cold after 抵抗;
+9 -2
View File
@@ -42,6 +42,8 @@ export const FLAG_KEYS = Object.freeze([
// 自缚 (the unit's own immobility: 守墓石像's 转换模式, the 自缚 leaders) beside its `noMove` — 束缚 sets noMove too, and
// only 自缚 makes a unit "不视为可达目标" for 余 S2's teleport (PRTS 余 S2 备注)
'selfBound', 'healFree', 'stealthOff',
// 缚地 (status `groundbind`): an enemy air unit counts as a ground unit meanwhile (Unit.isFlying)
'groundbind',
]);
/**
@@ -90,6 +92,11 @@ export const STATUS = Object.freeze({
// 浮空: 变为空中单位 (Unit.isFlying),无法移动、攻击及使用技能; 对重量大于3的单位持续时间减半; the state holds 不可阻挡 +
// 失衡免疫 (noDisplace: "不会被位移影响", PRTS 异常效果)
levitate: { flags: { levitate: true, stun: true, unblockable: true, noDisplace: true }, immune: 'levitate' },
// 缚地 (gamedata_const ba.groundbind "目标变为地面单位,无法移动;使部分近地悬浮敌人掉落;对重量大于3的单位持续时间减半"):
// an air unit (data flyer, 近地悬浮) counts as a ground unit (Unit.isFlying: melee operators hit it, ground blockers
// block it) and cannot move; half duration on units with massLevel > 3 (Battle.applyStatus, as 浮空); a 浮空 still
// lands on it (PRTS 异常效果 "若单位数据上为飞行单位且不持有缚地异常…则Buff取消"). 予愿安洁莉娜 S2 (kits/ops/op-aglna2.js)
groundbind: { flags: { groundbind: true, noMove: true }, mods: { moveMul: 0 } },
// 麻痹: value = stacks (default 1, max 3); each stack cancels one enemy normal attack; lasts until consumed; honours
// 麻痹免疫 (enemy_database palsyImmune, e.g. 假想敌:铳 — PRTS 元素: a 神经 burst gives 麻痹 only "若单位不具有麻痹免疫")
palsy: { palsy: true, immune: 'palsy' },
@@ -120,12 +127,12 @@ export const STATUS = Object.freeze({
});
/**
* Control statuses (异常状态) shortened by 抵抗: 晕眩 冻结 寒冷 沉睡 恐惧 战栗 诱导 浮空 束缚 沉默 缴械 停顿 减速. The
* Control statuses (异常状态) shortened by 抵抗: 晕眩 冻结 寒冷 沉睡 恐惧 战栗 诱导 浮空 束缚 沉默 缴械 停顿 减速 缚地. The
* official term lists 晕眩/寒冷/冻结/恐惧/诱导…; the rest follow the operator kits that grant 抵抗 [ASSUMED]. 麻痹 is
* stack-based: it decays instead (RESIST_PALSY_DECAY).
*/
export const RESIST_STATUSES = Object.freeze(new Set(['stun', 'freeze', 'cold', 'sleep', 'fear', 'tremble', 'attract', 'levitate',
'bind', 'silence', 'disarm', 'sluggish', 'slow']));
'bind', 'silence', 'disarm', 'sluggish', 'slow', 'groundbind']));
/** Statuses that should be reported to clients as `['status', id, key, on]`. */
export const VISIBLE_STATUS = new Set(Object.keys(STATUS));
+2 -2
View File
@@ -58,13 +58,13 @@ const SYUFO_CRAWL_STUN = 0.5;
/**
* 掠海漂移体 (PRTS): 初始模式 近地悬浮 + 失衡免疫, 不会攻击飞行单位; 受晕眩/无法行动/沉睡/冻结/缚地影响后进入爬行模式 for
* good and is stunned 0.5 s — a ground unit (blockable, melee operators hit it) that "仅进行阻挡攻击" (only its blocker).
* The engine has 晕眩 / 冻结 / 沉睡 (no operator here applies 无法行动 or 缚地). Erosion on every attack.
* The engine has 晕眩 / 冻结 / 沉睡 / 缚地 (予愿安洁莉娜 S2; no operator here applies 无法行动). Erosion on every attack.
*/
function kitSyufo(ab) {
return [float(), ep('erosion', T(ab, 'EpDamage.attack@ep_damage_ratio') ?? 0), {
spawn(b, e) { e.profile.canTarget = (u) => !u.isFlying; },
status(c, b, e, a) {
if (a.crawl || !(c.status === 'stun' || c.status === 'freeze' || c.status === 'sleep')) return;
if (a.crawl || !(c.status === 'stun' || c.status === 'freeze' || c.status === 'sleep' || c.status === 'groundbind')) return;
a.crawl = true;
setFloat(b, e, false);
e.profile.melee = true;
+3 -3
View File
@@ -103,7 +103,7 @@ function kitParrot(ab) {
},
status(c, b, e, a) {
if (c.status === 'freeze') { a.chill = true; return; }
if (a.down || !(c.status === 'stun' || c.status === 'sleep')) return;
if (a.down || !(c.status === 'stun' || c.status === 'sleep' || c.status === 'groundbind')) return;
a.down = true;
setFloat(b, e, false);
b.removeBuff(e, 'ab:parrotRun');
@@ -114,8 +114,8 @@ function kitParrot(ab) {
tick(b, e, a) {
if (a.chill && !e.s.flags.freeze) { a.chill = false; b.applyStatus(e, 'stun', { duration: PARROT_CHILL_STUN, source: null }); }
// "离开上述异常效果影响后进入初始模式": s.flags.stun also holds while asleep / frozen (units.js), so a sleep that
// outlasts the mode stun keeps it down
if (a.down && !e.s.flags.stun) {
// outlasts the mode stun keeps it down; so does a 缚地 (flag `groundbind`)
if (a.down && !e.s.flags.stun && !e.s.flags.groundbind) {
a.down = false;
b.removeBuff(e, 'ab:parrotDown');
b.removeBuff(e, 'ab:parrotRun');
+10 -5
View File
@@ -179,13 +179,18 @@ export class Unit {
/**
* Air unit (空中单位) for every targeting / ground-only rule: FLY movers, and enemies that hover (近地悬浮, buff flag
* `float` — PRTS 术语释义 ba.float "算作空中单位"; they keep walking the ground path) or are levitated (浮空, gamedata_const
* ba.levitate "变为空中单位"). Movement and pathing read `motion`, never this.
* ba.levitate "变为空中单位"). A 缚地 enemy (status `groundbind`, ba.groundbind "目标变为地面单位") is a ground unit
* meanwhile — unless a 浮空 lifts it again (浮空 lands on a 缚地 flyer: Battle.applyStatus). Movement and pathing read
* `motion`, never this.
*/
get isFlying() {
if (this.motion === 'FLY') return true;
if (this.side !== 'enemy') return false;
const f = this.s.flags;
return !!(f.float || f.levitate);
if (this.side === 'enemy') {
const f = this.s.flags;
if (f.levitate) return true;
if (f.groundbind) return false;
return this.motion === 'FLY' || !!f.float;
}
return this.motion === 'FLY';
}
get hpRatio() { const mh = this.s.maxHp; return mh > 0 ? this.hp / mh : 0; }