Files
ark-stronghold-protocol/test/sim/feedback5-groundbind.test.js
T
sganggs 83e879a335 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.
2026-10-06 05:20:28 +08:00

87 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// test/sim/feedback5-groundbind.test.js — the 缚地 status (0.2.0, O20: 予愿安洁莉娜 S2, kits/ops/op-aglna2.js): gamedata_const
// ba.groundbind "目标变为地面单位,无法移动;使部分近地悬浮敌人掉落;对重量大于3的单位持续时间减半" — server/sim/buffs.js STATUS
// `groundbind` (flag `groundbind` + `noMove`), units.js Unit.isFlying (a 缚地 enemy is a ground unit unless a 浮空 lifts it),
// battle/status.js applyStatus (浮空 lands on a 缚地 data flyer — PRTS 异常效果 "若单位数据上为飞行单位且不持有缚地异常…则Buff
// 取消"; the weight halving), the 抵抗 list, and the two hovering enemies whose PRTS text names it (content/enemies:
// 掠海漂移体 爬行模式, 吉兆飞鳞 晕眩模式).
// Run: node --test test/sim/feedback5-groundbind.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
import { STATUS, RESIST_STATUSES, FLAG_KEYS } from '../../server/sim/buffs.js';
const approx = (a, b, msg, eps = 1e-6) => assert.ok(Math.abs(a - b) <= eps * Math.max(1, Math.abs(b)), `${msg}: ${a} vs ${b}`);
const ENEMIES = {
enemy_fly: enemyRec({ key: 'enemy_fly', hp: 1e9, speed: 0, mass: 0, motion: 'FLY' }),
enemy_flywalk: enemyRec({ key: 'enemy_flywalk', hp: 1e9, speed: 0.6, mass: 0, motion: 'FLY' }),
enemy_flyheavy: enemyRec({ key: 'enemy_flyheavy', hp: 1e9, speed: 0, mass: 4, motion: 'FLY' }),
};
function arena(units = []) {
const h = makeBattle({ defs: { enemies: ENEMIES }, timeLimit: 600, autoFinish: false, seed: 3, flags: { dpPerSec: 0 }, units });
h.step();
return h;
}
test('缚地 is a catalogue status: flag groundbind + noMove, move ×0, one of 抵抗\'s statuses', () => {
assert.deepEqual(STATUS.groundbind, { flags: { groundbind: true, noMove: true }, mods: { moveMul: 0 } });
assert.ok(FLAG_KEYS.includes('groundbind'));
assert.ok(RESIST_STATUSES.has('groundbind'));
});
test('缚地: an air unit is a ground unit (melee operators hit it) and cannot move; half as long on weight > 3; 抵抗 shortens it; 浮空 lands on a 缚地 flyer; back in the air when it ends', () => {
const h = arena([{ uid: 1, chessId: 'chess_char_1_02_a', row: 9, col: 4 }]);
const yak = h.unit(1);
const fl = h.spawn('enemy_flywalk', { routeIndex: 2 });
h.run(0.5);
assert.equal(fl.isFlying, true);
assert.equal(h.b.applyStatus(fl, 'levitate', { duration: 3 }), false, 'a data flyer takes no 浮空');
assert.ok(h.b.applyStatus(fl, 'groundbind', { duration: 5 }));
assert.deepEqual([fl.isFlying, !!fl.s.flags.noMove, fl.s.moveSpeed], [false, true, 0], 'ground unit, rooted');
const x0 = fl.x;
h.run(1);
approx(fl.x, x0, 'it does not move');
assert.ok(h.b.applyStatus(fl, 'levitate', { duration: 2 }), '浮空 lands on a 缚地 flyer');
assert.equal(fl.isFlying, true, 'levitated: an air unit again');
// a melee operator (ground-only, range: his own tile) hits a 缚地 flyer on his tile — and blocks it, a ground unit now
const near = h.spawn('enemy_fly', { pos: [9, 4] });
h.run(1);
assert.equal(yak.stats.dmg, 0, '角峰 cannot hit the flyer');
h.b.applyStatus(near, 'groundbind', { duration: 10 });
assert.ok(h.runUntil(() => yak.stats.dmg > 0, 5), '缚地: a ground unit he can hit');
assert.equal(near.blockedBy, yak, 'and block');
// halving and 抵抗
const heavy = h.spawn('enemy_flyheavy', { pos: [10, 9] });
h.b.applyStatus(heavy, 'groundbind', { duration: 10 });
approx(heavy.findBuff('groundbind').timeLeft, 5, 'weight 4: half');
const res = h.spawn('enemy_fly', { pos: [11, 9] });
h.b.applyStatus(res, 'resist', { duration: 99, value: 0.5 });
h.b.applyStatus(res, 'groundbind', { duration: 10 });
approx(res.findBuff('groundbind').timeLeft, 5, '抵抗 halves it');
h.run(6);
assert.equal(res.isFlying, true, 'back in the air when it ends');
checkInvariants(h.b);
assert.equal(h.b.errors.length, 0);
});
test('the hovering enemies (PRTS "受…缚地影响后"): 掠海漂移体 drops to its 爬行模式 for good; 吉兆飞鳞 is down while the 缚地 holds', () => {
const h = arena();
const sy = h.spawn('enemy_2025_syufo', { pos: [12, 8], routeIndex: 0, mods: { speedMul: 0 } });
const pa = h.spawn('enemy_10045_parrot', { pos: [12, 6], routeIndex: 0, mods: { speedMul: 0 } });
h.step();
assert.ok(sy.isFlying && pa.isFlying, 'both hover');
h.b.applyStatus(sy, 'groundbind', { duration: 4 });
h.b.applyStatus(pa, 'groundbind', { duration: 20 });
h.step();
assert.equal(sy.form, 'crawl', '掠海漂移体: 爬行模式');
assert.equal(pa.isFlying, false, '吉兆飞鳞: down');
h.run(10);
assert.equal(sy.isFlying, false, '掠海漂移体 stays down for good');
assert.equal(pa.isFlying, false, '吉兆飞鳞 down while the 缚地 holds (its 8 s stun over)');
h.run(12);
assert.equal(pa.isFlying, true, '吉兆飞鳞 floats again once nothing holds it');
checkInvariants(h.b);
assert.equal(h.b.errors.length, 0);
});