Official: PRTS 乌尔比安 S3 备注 ③ — the 【移动】 takes the anchor tile if it can be deployed on, else the tile one beyond, else
his own tile. Cause: "deployable" was any standable tile of the battle rect; UNITE_RECT holds both halves of the 联防 map, and
on escaped_single (the lone helper on the left half, the enemies out of the middle gate at col 10) an anchor that met no
enemy flew on to the right half, where no enemy walks, and he stood there for the whole skill. Now the landing must also
lie on his player's board: Battle.onOwnBoard(ps, r, c), the board region GEO.FIELD mapped onto the field (the hunk is
byte-identical to CB4's 219ab31, which uses it for the 突袭 landing, so the two merge cleanly).
Golden: 1 scenario — fields hidden-boss_9-solo: his first cast from (2,5) landed on (2,11), the solo boss field's right
half (checked instrumented); he now stays and the fight differs from there. Tests: test/sim/feedback5-ulpia-own-board.test.js
(4; the one-helper and the two-helper 联防 cases fail on the parent); test/sim/facing-invariance.test.js — its open field
has no boards, so its setup counts every tile as the player's own (the own-board rule is a board-position rule, like the
deployment order the scenario already avoids).
64 lines
4.1 KiB
JavaScript
64 lines
4.1 KiB
JavaScript
// test/sim/feedback5-ulpia-own-board.test.js — community report of 2026-10-06 (item 27) 「乌尔比安使用3技能会在联防阶段跳到红门
|
|
// 后」. 乌尔比安 S3 必须开辟的通路 throws the anchor up to 6 tiles ahead (the nearest enemy of the line, else the farthest tile) and
|
|
// moves there when the tile "可以部署" (PRTS 备注 ③: the landing tile > the tile one beyond > his own tile). On the one-helper 联防
|
|
// map (act1autochess_escaped_single: the helper's pieces on the left half, the enemies out of the middle gate at col 10) the
|
|
// right half is no tile his player deploys on, yet an anchor that met no enemy flew there and he moved behind the red gate for
|
|
// the whole skill. Now the landing must lie on his own board (Battle.onOwnBoard, the board region mapped onto the field).
|
|
// Run: node --test test/sim/feedback5-ulpia-own-board.test.js
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
|
|
|
const ULPIA = 'chess_char_5_05_a';
|
|
const dummy = enemyRec({ key: 'enemy_anchor_dummy', hp: 1e9, speed: 0, mass: 0, atk: 0 });
|
|
|
|
/** 乌尔比安 cast S3 on `tile` of `kind`'s field; `enemy` = a parked enemy's tile or null. Returns where he stands after the cast. */
|
|
function cast({ kind = 'unite', stageId = 'act1autochess_escaped_single', tile = [10, 7], enemy = null, colOffset = 0 } = {}) {
|
|
const h = makeBattle({
|
|
kind, stageId, seed: 5, autoFinish: false, timeLimit: 120, defs: { enemies: { enemy_anchor_dummy: dummy } },
|
|
players: [{ playerId: 'p1', seat: 0, side: 'L', colOffset, units: [{ uid: 1, kind: 'chess', chessId: ULPIA, row: tile[0], col: tile[1] }], bonds: {} }],
|
|
});
|
|
h.step();
|
|
const u = h.unit(ULPIA);
|
|
const home = [u.tileR, u.tileC];
|
|
if (enemy) { h.spawn('enemy_anchor_dummy', { pos: enemy }); h.step(); } // (one tick: the enemy joins the tile index)
|
|
u.skill.gainSp(999, 'test');
|
|
// (its CUSTOM_RANGE trigger waits for an enemy on the line: with none the cast is made by hand, as a player's would be)
|
|
if (!u.skill.active) assert.ok(u.skill.activate('test'), 'S3 cast');
|
|
h.run(0.2);
|
|
const at = [u.tileR, u.tileC];
|
|
checkInvariants(h.b);
|
|
return { h, u, home, at };
|
|
}
|
|
|
|
test('one-helper 联防: an anchor that meets no enemy flies on past the middle gate — he stays on his own half (until 0.2.0 he moved behind the red gate)', () => {
|
|
const { h, u, home, at } = cast({ tile: [10, 7] });
|
|
assert.deepEqual(home, [10, 7]);
|
|
assert.ok(h.b.rect.c1 >= 13, 'the 联防 field spans both halves');
|
|
assert.deepEqual(at, home, `stays on (10,7) — not ${at}`);
|
|
assert.ok(h.b.onOwnBoard(u.player, 10, 7) && !h.b.onOwnBoard(u.player, 10, 13), 'cols 11+ are not his board');
|
|
assert.equal(h.b.allyUnits.filter((a) => a.kind === 'token' && a.alive).length, 0, 'no 从不混淆的方向: nothing to return from');
|
|
});
|
|
|
|
test('one-helper 联防: an enemy on his half still draws the anchor and he moves next to it (own board)', () => {
|
|
const { at } = cast({ tile: [10, 4], enemy: [10, 8] });
|
|
assert.deepEqual(at, [10, 8], 'he moves onto the anchor tile on his half');
|
|
});
|
|
|
|
test('two-helper 联防: the left helper never lands on the right helper\'s half; the right helper moves on its own half', () => {
|
|
const left = cast({ stageId: 'act1autochess_escaped_multi', tile: [10, 7] });
|
|
assert.deepEqual(left.at, left.home, `left helper stays (${left.at})`);
|
|
// the right-hand helper (colOffset 8): board col 3 → field col 11, enemy 3 tiles ahead on its own half
|
|
const right = cast({ stageId: 'act1autochess_escaped_multi', tile: [10, 3], colOffset: 8, enemy: [10, 14] });
|
|
assert.deepEqual(right.home, [10, 11]);
|
|
assert.deepEqual(right.at, [10, 14], 'moves on its own half');
|
|
assert.ok(right.h.b.onOwnBoard(right.u.player, 10, 14) && !right.h.b.onOwnBoard(right.u.player, 10, 7));
|
|
});
|
|
|
|
test('a normal battle is unchanged: the anchor stops at the field edge and he moves onto the farthest tile of his board', () => {
|
|
const { at, h, u } = cast({ kind: 'normal', stageId: 'flat', tile: [10, 3] });
|
|
assert.deepEqual(at, [10, 9], `6 tiles ahead, the last road tile before the gate column (${at})`);
|
|
assert.ok(h.b.onOwnBoard(u.player, 10, 9));
|
|
});
|